Merge branch 'dev' into dev-hub
This commit is contained in:
commit
865c9e989c
|
@ -25,7 +25,7 @@
|
|||
"event-source-polyfill": "^1.0.31",
|
||||
"global": "^4.4.0",
|
||||
"jetlinks-store": "^0.0.3",
|
||||
"jetlinks-ui-components": "^1.0.4",
|
||||
"jetlinks-ui-components": "^1.0.5",
|
||||
"js-cookie": "^3.0.1",
|
||||
"less": "^4.1.3",
|
||||
"less-loader": "^11.1.0",
|
||||
|
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |
|
@ -131,7 +131,7 @@ export const saveProductMetadata = (data: Record<string, unknown>) => server.pat
|
|||
* @param data 查询条件
|
||||
* @returns
|
||||
*/
|
||||
export const getDeviceNumber = (params:any) => server.get('/device-instance/_count', params)
|
||||
export const getDeviceNumber = (params:any) => server.get<number>('/device-instance/_count', params)
|
||||
|
||||
/**
|
||||
* 获取协议详情
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
|
||||
const color = {
|
||||
'processing': '64, 169, 255',
|
||||
'error': '247, 79, 70',
|
||||
'success': '74, 234, 220',
|
||||
'warning': '250, 178, 71',
|
||||
'default': '63, 73, 96'
|
||||
'processing': '9, 46, 231',
|
||||
'error': '229, 0, 18',
|
||||
'success': '36, 178, 118',
|
||||
'warning': '255, 144, 0',
|
||||
'default': '102, 102, 102'
|
||||
}
|
||||
export const getHexColor = (code: string, pe: number = 0.3) => {
|
||||
export const getHexColor = (code: string, pe: number = 0.1) => {
|
||||
const _color = color[code] || color.default
|
||||
if (code === 'default') {
|
||||
pe = 0.1
|
||||
|
|
|
@ -6,6 +6,18 @@
|
|||
@click="handleClick"
|
||||
>
|
||||
<div class="card-content">
|
||||
<div
|
||||
class="card-content-bg1"
|
||||
:style="{
|
||||
background: getBackgroundColor(statusNames[status]),
|
||||
}"
|
||||
></div>
|
||||
<div
|
||||
class="card-content-bg2"
|
||||
:style="{
|
||||
background: getBackgroundColor(statusNames[status]),
|
||||
}"
|
||||
></div>
|
||||
<div style="display: flex">
|
||||
<!-- 图片 -->
|
||||
<div class="card-item-avatar">
|
||||
|
@ -17,21 +29,19 @@
|
|||
<slot name="content"></slot>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 勾选 -->
|
||||
<div v-if="active" class="checked-icon">
|
||||
<div>
|
||||
<AIcon type="CheckOutlined" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 状态 -->
|
||||
<div
|
||||
v-if="showStatus"
|
||||
class="card-state"
|
||||
:style='{
|
||||
backgroundColor: getHexColor(statusNames[status])
|
||||
}'
|
||||
:style="{
|
||||
backgroundColor: getHexColor(statusNames[status]),
|
||||
}"
|
||||
>
|
||||
<div class="card-state-content">
|
||||
<BadgeStatus
|
||||
|
@ -72,7 +82,7 @@
|
|||
|
||||
<script setup lang="ts" name='CardBox'>
|
||||
import BadgeStatus from '@/components/BadgeStatus/index.vue';
|
||||
import { getHexColor } from '../BadgeStatus/color'
|
||||
import color, { getHexColor } from '../BadgeStatus/color';
|
||||
import type { ActionsType } from '@/components/Table';
|
||||
import { PropType } from 'vue';
|
||||
|
||||
|
@ -123,6 +133,15 @@ const props = defineProps({
|
|||
},
|
||||
});
|
||||
|
||||
const getBackgroundColor = (code: string) => {
|
||||
const _color = color[code] || color.default;
|
||||
return `linear-gradient(
|
||||
188.4deg,
|
||||
rgba(${_color}, 0.03) 22.94%,
|
||||
rgba(${_color}, 0) 94.62%
|
||||
)`;
|
||||
};
|
||||
|
||||
const handleClick = () => {
|
||||
emit('click', props.value);
|
||||
};
|
||||
|
@ -257,6 +276,33 @@ const handleClick = () => {
|
|||
}
|
||||
}
|
||||
|
||||
.card-content-bg1 {
|
||||
position: absolute;
|
||||
right: -5%;
|
||||
height: 100%;
|
||||
width: 44.65%;
|
||||
top: 0;
|
||||
background: linear-gradient(
|
||||
188.4deg,
|
||||
rgba(229, 0, 18, 0.03) 22.94%,
|
||||
rgba(229, 0, 18, 0) 94.62%
|
||||
);
|
||||
transform: skewX(-15deg);
|
||||
}
|
||||
|
||||
.card-content-bg2 {
|
||||
position: absolute;
|
||||
right: -5%;
|
||||
height: 100%;
|
||||
width: calc(44.65% + 34px);
|
||||
top: 0;
|
||||
background: linear-gradient(
|
||||
188.4deg,
|
||||
rgba(229, 0, 18, 0.03) 22.94%,
|
||||
rgba(229, 0, 18, 0) 94.62%
|
||||
);
|
||||
transform: skewX(-15deg);
|
||||
}
|
||||
.card-mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
|
@ -268,7 +314,7 @@ const handleClick = () => {
|
|||
width: 100%;
|
||||
height: 100%;
|
||||
color: #fff;
|
||||
background-color: rgba(#000, .5);
|
||||
background-color: rgba(#000, 0.5);
|
||||
visibility: hidden;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<AIcon type="MenuOutlined" class="item-drag item-icon" />
|
||||
</div>
|
||||
<div class="item-middle item-editable">
|
||||
<j-popover :visible="editIndex === index" placement="top">
|
||||
<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>
|
||||
|
|
|
@ -83,7 +83,7 @@ const props = defineProps({
|
|||
type: Object as PropType<PopconfirmProps>,
|
||||
},
|
||||
hasPermission: {
|
||||
type: String || Array,
|
||||
type: String || Array || Boolean,
|
||||
},
|
||||
style: {
|
||||
type: Object as PropType<CSSProperties>
|
||||
|
@ -96,7 +96,7 @@ const props = defineProps({
|
|||
const permissionStore = usePermissionStore()
|
||||
|
||||
const isPermission = computed(() => {
|
||||
if (!props.hasPermission) {
|
||||
if (!props.hasPermission || props.hasPermission === true) {
|
||||
return true
|
||||
}
|
||||
return permissionStore.hasPermission(props.hasPermission)
|
||||
|
|
|
@ -50,36 +50,16 @@ export const AccountMenu = {
|
|||
|
||||
export default [
|
||||
{ path: '/*', redirect: '/'},
|
||||
// start: 测试用, 可删除
|
||||
{
|
||||
path: '/login',
|
||||
component: () => import('@/views/user/Login/index.vue')
|
||||
},
|
||||
{
|
||||
path: '/demo',
|
||||
component: () => import('@/views/demo/index.vue')
|
||||
},
|
||||
{
|
||||
path: '/account/center/bind',
|
||||
component: () => import('@/views/account/Center/bind/index.vue')
|
||||
},
|
||||
{
|
||||
path: '/table',
|
||||
component: () => import('@/views/demo/table/index.vue')
|
||||
},
|
||||
{
|
||||
path: '/form',
|
||||
component: () => import('@/views/demo/Form.vue')
|
||||
},
|
||||
// {
|
||||
// path: '/system/Api',
|
||||
// component: () => import('@/views/system/Platforms/index.vue')
|
||||
// },
|
||||
// end: 测试用, 可删除
|
||||
|
||||
// 初始化
|
||||
{
|
||||
path: '/init-home',
|
||||
path: '/init-home', // 初始化
|
||||
component: () => import('@/views/init-home/index.vue')
|
||||
},
|
||||
|
||||
|
|
|
@ -15,19 +15,26 @@ export const useProductStore = defineStore({
|
|||
this.current = current
|
||||
this.detail = current
|
||||
},
|
||||
async refresh(id: string) {
|
||||
async getDetail(id: string) {
|
||||
const resp = await detail(id)
|
||||
const res = await getDeviceNumber(encodeQuery({ terms: { productId: id } }))
|
||||
if(resp.status === 200){
|
||||
this.current = resp.result
|
||||
this.detail = resp.result
|
||||
if(res.status === 200){
|
||||
this.current.count = res.result
|
||||
}
|
||||
}
|
||||
},
|
||||
async refresh(id: string) {
|
||||
this.getDetail(id)
|
||||
const res = await getDeviceNumber(encodeQuery({ terms: { productId: id } }))
|
||||
if(res.status === 200){
|
||||
this.current.count = res.result
|
||||
}
|
||||
},
|
||||
setTabActiveKey(key: string) {
|
||||
this.tabActiveKey = key
|
||||
},
|
||||
reSet(){
|
||||
this.current = {} as ProductItem
|
||||
this.detail = {} as ProductItem
|
||||
}
|
||||
}
|
||||
})
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<a-result status="404" title="404" sub-title="Sorry, the page you visited does not exist.">
|
||||
</a-result>
|
||||
<j-result status="404" title="404">
|
||||
</j-result>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
|
|
@ -27,11 +27,12 @@
|
|||
<template #card="slotProps">
|
||||
<CardBox
|
||||
:value="slotProps"
|
||||
@click="handleView(slotProps.id)"
|
||||
:actions="getActions(slotProps, 'card')"
|
||||
:status="slotProps.state?.value"
|
||||
:statusText="slotProps.state?.text"
|
||||
:statusNames="{
|
||||
enabled: 'success',
|
||||
enabled: 'processing',
|
||||
disabled: 'error',
|
||||
}"
|
||||
>
|
||||
|
@ -39,19 +40,20 @@
|
|||
<img :src="getImage('/northbound/aliyun.png')" />
|
||||
</template>
|
||||
<template #content>
|
||||
<h3
|
||||
class="card-item-content-title"
|
||||
@click.stop="handleView(slotProps.id)"
|
||||
>
|
||||
{{ slotProps.name }}
|
||||
</h3>
|
||||
<j-row>
|
||||
<Ellipsis style="width: calc(100% - 100px)">
|
||||
<span style="font-size: 16px; font-weight: 600">
|
||||
{{ slotProps.name }}
|
||||
</span>
|
||||
</Ellipsis>
|
||||
<j-row style="margin-top: 15px">
|
||||
<j-col :span="12">
|
||||
<div class="card-item-content-text">
|
||||
网桥产品
|
||||
</div>
|
||||
<Ellipsis>
|
||||
<div>{{ slotProps?.bridgeProductName }}</div>
|
||||
<div>
|
||||
{{ slotProps?.bridgeProductName }}
|
||||
</div>
|
||||
</Ellipsis>
|
||||
</j-col>
|
||||
<j-col :span="12">
|
||||
|
@ -85,9 +87,13 @@
|
|||
</CardBox>
|
||||
</template>
|
||||
<template #state="slotProps">
|
||||
<j-badge
|
||||
<BadgeStatus
|
||||
:status="slotProps.state?.value"
|
||||
:text="slotProps.state?.text"
|
||||
:status="statusMap.get(slotProps.state?.value)"
|
||||
:statusNames="{
|
||||
enabled: 'processing',
|
||||
disabled: 'error',
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
<template #action="slotProps">
|
||||
|
@ -100,7 +106,7 @@
|
|||
:disabled="i.disabled"
|
||||
:popConfirm="i.popConfirm"
|
||||
:tooltip="i.tooltip"
|
||||
style="padding: 0px"
|
||||
style="padding: 0 5px"
|
||||
@click="i.onClick"
|
||||
type="link"
|
||||
:hasPermission="'Northbound/AliCloud:' + i.key"
|
||||
|
@ -116,20 +122,17 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import { query, _undeploy, _deploy, _delete } from '@/api/northbound/alicloud';
|
||||
import type { ActionsType } from '@/views/device/Instance/typings'
|
||||
import type { ActionsType } from '@/views/device/Instance/typings';
|
||||
import { getImage } from '@/utils/comm';
|
||||
import { message } from 'jetlinks-ui-components';
|
||||
import { useMenuStore } from 'store/menu';
|
||||
import BadgeStatus from '@/components/BadgeStatus/index.vue';
|
||||
|
||||
const instanceRef = ref<Record<string, any>>({});
|
||||
const params = ref<Record<string, any>>({});
|
||||
|
||||
const menuStory = useMenuStore();
|
||||
|
||||
const statusMap = new Map();
|
||||
statusMap.set('enabled', 'success');
|
||||
statusMap.set('disabled', 'error');
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '名称',
|
||||
|
@ -151,6 +154,7 @@ const columns = [
|
|||
title: '说明',
|
||||
dataIndex: 'description',
|
||||
key: 'description',
|
||||
ellipsis: true,
|
||||
search: {
|
||||
type: 'string',
|
||||
},
|
||||
|
@ -172,7 +176,7 @@ const columns = [
|
|||
title: '操作',
|
||||
key: 'action',
|
||||
fixed: 'right',
|
||||
width: 250,
|
||||
width: 200,
|
||||
scopedSlots: true,
|
||||
},
|
||||
];
|
||||
|
|
|
@ -1,47 +1,71 @@
|
|||
<template>
|
||||
<j-table
|
||||
rowKey="id"
|
||||
:columns="columns"
|
||||
:data-source="dataSource"
|
||||
bordered
|
||||
:pagination="false"
|
||||
>
|
||||
<template #bodyCell="{ column, text, record }">
|
||||
<div style="width: 280px">
|
||||
<template v-if="['valueType', 'name'].includes(column.dataIndex)">
|
||||
<span>{{ text }}</span>
|
||||
<div class="inputs">
|
||||
<j-form ref="formRef" :model="modelRef">
|
||||
<j-table
|
||||
rowKey="id"
|
||||
:columns="columns"
|
||||
:data-source="modelRef.dataSource"
|
||||
bordered
|
||||
:pagination="false"
|
||||
>
|
||||
<template #bodyCell="{ column, record, index }">
|
||||
<template v-if="column.dataIndex === 'value'">
|
||||
<j-form-item
|
||||
:name="['dataSource', index, 'value']"
|
||||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message:
|
||||
record.type === 'enum' ||
|
||||
record.type === 'boolean'
|
||||
? '请选择'
|
||||
: '请输入',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<ValueItem
|
||||
v-model:modelValue="record.value"
|
||||
:itemType="record.type"
|
||||
:options="
|
||||
record.type === 'enum'
|
||||
? (
|
||||
record?.dataType?.elements || []
|
||||
).map((item) => {
|
||||
return {
|
||||
label: item.text,
|
||||
value: item.value,
|
||||
};
|
||||
})
|
||||
: record.type === 'boolean'
|
||||
? [
|
||||
{ label: '是', value: true },
|
||||
{ label: '否', value: false },
|
||||
]
|
||||
: undefined
|
||||
"
|
||||
@change="onChange"
|
||||
/>
|
||||
</j-form-item>
|
||||
</template>
|
||||
<template v-else>
|
||||
<j-form-item
|
||||
:name="['dataSource', index, column.dataIndex]"
|
||||
>
|
||||
<j-input
|
||||
readonly
|
||||
:bordered="false"
|
||||
v-model:value="record[column.dataIndex]"
|
||||
/>
|
||||
</j-form-item>
|
||||
</template>
|
||||
</template>
|
||||
<template v-else>
|
||||
<ValueItem
|
||||
v-model:modelValue="record.value"
|
||||
:itemType="record.type"
|
||||
:options="
|
||||
record.type === 'enum'
|
||||
? (record?.dataType?.elements || []).map(
|
||||
(item) => {
|
||||
return {
|
||||
label: item.text,
|
||||
value: item.value,
|
||||
};
|
||||
},
|
||||
)
|
||||
: record.type === 'boolean'
|
||||
? [
|
||||
{ label: '是', value: true },
|
||||
{ label: '否', value: false },
|
||||
]
|
||||
: undefined
|
||||
"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</j-table>
|
||||
</j-table>
|
||||
</j-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { PropType } from "vue";
|
||||
|
||||
import { PropType } from 'vue';
|
||||
|
||||
type Emits = {
|
||||
(e: 'update:modelValue', data: Record<string, any>[]): void;
|
||||
|
@ -52,9 +76,14 @@ const _props = defineProps({
|
|||
modelValue: {
|
||||
type: Array as PropType<Record<string, any>[]>,
|
||||
default: '',
|
||||
}
|
||||
},
|
||||
});
|
||||
const columns = [
|
||||
// {
|
||||
// title: 'ID',
|
||||
// dataIndex: 'id',
|
||||
// with: '33%',
|
||||
// },
|
||||
{
|
||||
title: '参数名称',
|
||||
dataIndex: 'name',
|
||||
|
@ -72,22 +101,35 @@ const columns = [
|
|||
},
|
||||
];
|
||||
|
||||
// const dataSource = ref<Record<any, any>[]>(_props.modelValue || []);
|
||||
const modelRef = reactive<{
|
||||
dataSource: any[];
|
||||
}>({
|
||||
dataSource: [],
|
||||
});
|
||||
const formRef = ref();
|
||||
|
||||
const dataSource = computed({
|
||||
get: () => {
|
||||
return _props.modelValue || {
|
||||
messageType: undefined,
|
||||
message: {
|
||||
properties: undefined,
|
||||
functionId: undefined,
|
||||
inputs: []
|
||||
}
|
||||
}
|
||||
},
|
||||
set: (val: any) => {
|
||||
_emit('update:modelValue', val);
|
||||
}
|
||||
})
|
||||
watchEffect(() => {
|
||||
modelRef.dataSource = _props.modelValue || [];
|
||||
});
|
||||
|
||||
</script>
|
||||
const onChange = () => {
|
||||
_emit('update:modelValue', modelRef.dataSource);
|
||||
};
|
||||
|
||||
const onSave = () =>
|
||||
new Promise((resolve, reject) => {
|
||||
formRef.value
|
||||
.validate()
|
||||
.then(() => {
|
||||
resolve([...modelRef.dataSource]);
|
||||
})
|
||||
.catch(() => {
|
||||
reject(false);
|
||||
});
|
||||
});
|
||||
|
||||
defineExpose({ onSave });
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
</style>
|
|
@ -1,76 +1,156 @@
|
|||
<template>
|
||||
<j-form
|
||||
:layout="'vertical'"
|
||||
ref="formRef"
|
||||
:model="modelRef"
|
||||
>
|
||||
<j-form :layout="'vertical'" ref="formRef" :model="modelRef">
|
||||
<j-row :gutter="24">
|
||||
<j-col :span="24" v-if="actionType === 'command'">
|
||||
<j-form-item name="messageType" label="指令类型" :rules="{
|
||||
required: true,
|
||||
message: '请选择指令类型',
|
||||
}" 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>
|
||||
<j-form-item
|
||||
name="messageType"
|
||||
label="指令类型"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '请选择指令类型',
|
||||
}"
|
||||
>
|
||||
<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
|
||||
>
|
||||
</j-select>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col :span="(modelRef.messageType === 'READ_PROPERTY' || actionType === 'latestData') ? 24 : 12" v-if="(actionType === 'command' && ['READ_PROPERTY','WRITE_PROPERTY'].includes(modelRef.messageType)) || actionType === 'latestData'">
|
||||
<j-form-item :name="['message', 'properties']" label="属性" :rules="{
|
||||
required: true,
|
||||
message: '请选择属性',
|
||||
}">
|
||||
<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-col
|
||||
:span="
|
||||
modelRef.messageType === 'READ_PROPERTY' ||
|
||||
actionType === 'latestData'
|
||||
? 24
|
||||
: 12
|
||||
"
|
||||
v-if="
|
||||
(actionType === 'command' &&
|
||||
['READ_PROPERTY', 'WRITE_PROPERTY'].includes(
|
||||
modelRef.messageType,
|
||||
)) ||
|
||||
actionType === 'latestData'
|
||||
"
|
||||
>
|
||||
<j-form-item
|
||||
:name="['message', 'properties']"
|
||||
label="属性"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '请选择属性',
|
||||
}"
|
||||
>
|
||||
<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>
|
||||
</j-col>
|
||||
<j-col :span="12" v-if="modelRef.messageType === 'WRITE_PROPERTY' && actionType === 'command'">
|
||||
<j-form-item :name="['message', 'value']" label="值" :rules="{
|
||||
required: true,
|
||||
message: '请输入值',
|
||||
}">
|
||||
<j-col
|
||||
:span="12"
|
||||
v-if="
|
||||
modelRef.messageType === 'WRITE_PROPERTY' &&
|
||||
actionType === 'command'
|
||||
"
|
||||
>
|
||||
<j-form-item
|
||||
:name="['message', 'value']"
|
||||
label="值"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '请输入值',
|
||||
}"
|
||||
>
|
||||
<ValueItem
|
||||
v-model:modelValue="modelRef.message.value"
|
||||
:itemType="property.type || property.valueType?.type || 'int'"
|
||||
:itemType="
|
||||
property.type || property.valueType?.type || 'int'
|
||||
"
|
||||
:options="
|
||||
property.valueType?.type === 'enum'
|
||||
? (property?.dataType?.elements || []).map(
|
||||
(item) => {
|
||||
return {
|
||||
label: item?.text,
|
||||
value: item?.value,
|
||||
};
|
||||
},
|
||||
)
|
||||
(item) => {
|
||||
return {
|
||||
label: item?.text,
|
||||
value: item?.value,
|
||||
};
|
||||
},
|
||||
)
|
||||
: property.valueType?.type === 'boolean'
|
||||
? [
|
||||
{ label: '是', value: true },
|
||||
{ label: '否', value: false },
|
||||
]
|
||||
{ label: '是', value: true },
|
||||
{ label: '否', value: false },
|
||||
]
|
||||
: undefined
|
||||
"
|
||||
/>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col :span="24" v-if="modelRef.messageType === 'INVOKE_FUNCTION'">
|
||||
<j-form-item :name="['message', 'functionId']" label="功能" :rules="{
|
||||
required: true,
|
||||
message: '请选择功能',
|
||||
}">
|
||||
<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-form-item
|
||||
:name="['message', 'functionId']"
|
||||
label="功能"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '请选择功能',
|
||||
}"
|
||||
>
|
||||
<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>
|
||||
</j-col>
|
||||
<j-col :span="24" v-if="modelRef.messageType === 'INVOKE_FUNCTION' && modelRef.message.functionId">
|
||||
<j-form-item :name="['message', 'inputs']" label="参数列表" :rules="{
|
||||
required: true,
|
||||
message: '请输入参数列表',
|
||||
}">
|
||||
<EditTable v-model="modelRef.message.inputs"/>
|
||||
<j-col
|
||||
:span="24"
|
||||
v-if="
|
||||
modelRef.messageType === 'INVOKE_FUNCTION' &&
|
||||
modelRef.message.functionId
|
||||
"
|
||||
class="inputs"
|
||||
>
|
||||
<j-form-item
|
||||
:name="['message', 'inputs']"
|
||||
label="参数列表"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '请输入参数列表',
|
||||
}"
|
||||
>
|
||||
<EditTable
|
||||
ref="editRef"
|
||||
v-model="modelRef.message.inputs"
|
||||
/>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
</j-row>
|
||||
|
@ -78,101 +158,106 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import EditTable from './EditTable.vue'
|
||||
import EditTable from './EditTable.vue';
|
||||
|
||||
const formRef = ref();
|
||||
|
||||
const props = defineProps({
|
||||
actionType: {
|
||||
type: String,
|
||||
default: ''
|
||||
default: '',
|
||||
},
|
||||
modelValue: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
default: () => {},
|
||||
},
|
||||
metadata: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {
|
||||
properties: [],
|
||||
functions: []
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
functions: [],
|
||||
};
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
type Emits = {
|
||||
(e: 'update:modelValue', data: any): void;
|
||||
};
|
||||
const editRef = ref();
|
||||
|
||||
const emit = defineEmits<Emits>();
|
||||
const modelRef = reactive({
|
||||
messageType: 'READ_PROPERTY',
|
||||
message: {
|
||||
properties: undefined,
|
||||
functionId: undefined,
|
||||
inputs: [],
|
||||
},
|
||||
});
|
||||
|
||||
const modelRef = computed({
|
||||
get: () => {
|
||||
onPropertyChange(props.modelValue?.message?.properties)
|
||||
return props.modelValue || {
|
||||
messageType: undefined,
|
||||
message: {
|
||||
properties: undefined,
|
||||
functionId: undefined,
|
||||
inputs: []
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
Object.assign(modelRef, newVal);
|
||||
if(newVal?.message?.properties){
|
||||
onPropertyChange(newVal?.message?.properties);
|
||||
}
|
||||
}
|
||||
},
|
||||
set: (val: any) => {
|
||||
emit('update:modelValue', val);
|
||||
}
|
||||
})
|
||||
{
|
||||
immediate: true,
|
||||
},
|
||||
);
|
||||
|
||||
const property = ref<any>({})
|
||||
const property = ref<any>({});
|
||||
|
||||
const funcChange = (val: string) => {
|
||||
if(val){
|
||||
const arr = props.metadata?.functions.find((item: any) => item.id === val)?.inputs || []
|
||||
if (val) {
|
||||
const arr =
|
||||
props.metadata?.functions.find((item: any) => item.id === val)
|
||||
?.inputs || [];
|
||||
const list = arr.map((item: any) => {
|
||||
return {
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
value: undefined,
|
||||
valueType: item?.valueType?.type,
|
||||
}
|
||||
})
|
||||
modelRef.value.message.inputs = list
|
||||
};
|
||||
});
|
||||
modelRef.message.inputs = list;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onPropertyChange = (val: string) => {
|
||||
if(val){
|
||||
const _item = props.metadata?.properties.find((item: any) => item.id === val)
|
||||
property.value = _item?.[0] || {}
|
||||
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(() => {
|
||||
const _arr = toRaw(modelRef).value?.message?.inputs || []
|
||||
if(_arr.length && !_arr.every((_a: any) => _a.value)){
|
||||
resolve(false)
|
||||
} else {
|
||||
resolve(toRaw(modelRef))
|
||||
}
|
||||
})
|
||||
.catch((err: any) => {
|
||||
resolve(err)
|
||||
const saveBtn = () =>
|
||||
new Promise((resolve) => {
|
||||
formRef.value
|
||||
.validate()
|
||||
.then(async (_data: any) => {
|
||||
await editRef.value.onSave().catch(() => {
|
||||
resolve(false)
|
||||
})
|
||||
resolve(_data)
|
||||
})
|
||||
.catch((err: any) => {
|
||||
resolve(err);
|
||||
});
|
||||
});
|
||||
})
|
||||
|
||||
defineExpose({ saveBtn })
|
||||
|
||||
defineExpose({ saveBtn });
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
:deep(.ant-form-item){
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.other {
|
||||
margin-bottom: 24px;
|
||||
.inputs {
|
||||
.ant-form-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -171,7 +171,8 @@
|
|||
>
|
||||
<j-select-option
|
||||
v-for="i in getTypesActions(
|
||||
item.action || ''
|
||||
item.action ||
|
||||
'',
|
||||
)"
|
||||
:key="i.id"
|
||||
:value="i.id"
|
||||
|
@ -321,7 +322,8 @@
|
|||
>
|
||||
<j-select-option
|
||||
v-for="i in getDuerOSProperties(
|
||||
item.source || '',
|
||||
item.source ||
|
||||
'',
|
||||
)"
|
||||
:key="i.id"
|
||||
:value="i.id"
|
||||
|
@ -410,7 +412,10 @@
|
|||
type="primary"
|
||||
:loading="loading"
|
||||
@click="saveBtn"
|
||||
:hasPermission="['Northbound/DuerOS:add', 'Northbound/DuerOS:update']"
|
||||
:hasPermission="[
|
||||
'Northbound/DuerOS:add',
|
||||
'Northbound/DuerOS:update',
|
||||
]"
|
||||
>
|
||||
保存
|
||||
</PermissionButton>
|
||||
|
@ -626,12 +631,11 @@ const saveBtn = async () => {
|
|||
loading.value = true;
|
||||
const resp = await savePatch(data).finally(() => {
|
||||
loading.value = false;
|
||||
})
|
||||
});
|
||||
if (resp.status === 200) {
|
||||
message.success('操作成功!');
|
||||
formRef.value.resetFields();
|
||||
menuStory.jumpPage('Northbound/DuerOS');
|
||||
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -27,11 +27,12 @@
|
|||
<template #card="slotProps">
|
||||
<CardBox
|
||||
:value="slotProps"
|
||||
@click="handleView(slotProps.id)"
|
||||
:actions="getActions(slotProps, 'card')"
|
||||
:status="slotProps.state?.value"
|
||||
:statusText="slotProps.state?.text"
|
||||
:statusNames="{
|
||||
enabled: 'success',
|
||||
enabled: 'processing',
|
||||
disabled: 'error',
|
||||
}"
|
||||
>
|
||||
|
@ -39,13 +40,12 @@
|
|||
<img :src="getImage('/cloud/dueros.png')" />
|
||||
</template>
|
||||
<template #content>
|
||||
<h3
|
||||
class="card-item-content-title"
|
||||
@click.stop="handleView(slotProps.id)"
|
||||
>
|
||||
{{ slotProps.name }}
|
||||
</h3>
|
||||
<j-row>
|
||||
<Ellipsis style="width: calc(100% - 100px)">
|
||||
<span style="font-size: 16px; font-weight: 600">
|
||||
{{ slotProps.name }}
|
||||
</span>
|
||||
</Ellipsis>
|
||||
<j-row style="margin-top: 15px">
|
||||
<j-col :span="12">
|
||||
<div class="card-item-content-text">产品</div>
|
||||
<Ellipsis>
|
||||
|
@ -85,9 +85,13 @@
|
|||
</CardBox>
|
||||
</template>
|
||||
<template #state="slotProps">
|
||||
<j-badge
|
||||
<BadgeStatus
|
||||
:status="slotProps.state?.value"
|
||||
:text="slotProps.state?.text"
|
||||
:status="statusMap.get(slotProps.state?.value)"
|
||||
:statusNames="{
|
||||
enabled: 'processing',
|
||||
disabled: 'error',
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
<template #applianceType="slotProps">
|
||||
|
@ -105,7 +109,7 @@
|
|||
:tooltip="{
|
||||
...i.tooltip,
|
||||
}"
|
||||
style="padding: 0px"
|
||||
style="padding: 0 5px"
|
||||
@click="i.onClick"
|
||||
type="link"
|
||||
:hasPermission="'Northbound/DuerOS:' + i.key"
|
||||
|
@ -132,15 +136,12 @@ import type { ActionsType } from '@/views/device/Instance/typings';
|
|||
import { getImage } from '@/utils/comm';
|
||||
import { message } from 'jetlinks-ui-components';
|
||||
import { useMenuStore } from 'store/menu';
|
||||
import BadgeStatus from '@/components/BadgeStatus/index.vue';
|
||||
|
||||
const instanceRef = ref<Record<string, any>>({});
|
||||
const params = ref<Record<string, any>>({});
|
||||
const menuStory = useMenuStore();
|
||||
|
||||
const statusMap = new Map();
|
||||
statusMap.set('enabled', 'success');
|
||||
statusMap.set('disabled', 'error');
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '名称',
|
||||
|
@ -193,6 +194,7 @@ const columns = [
|
|||
title: '说明',
|
||||
dataIndex: 'description',
|
||||
key: 'description',
|
||||
ellipsis: true,
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
|
@ -211,7 +213,7 @@ const columns = [
|
|||
title: '操作',
|
||||
key: 'action',
|
||||
fixed: 'right',
|
||||
width: 250,
|
||||
width: 200,
|
||||
scopedSlots: true,
|
||||
},
|
||||
];
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
<template>
|
||||
<Form
|
||||
ref='form'
|
||||
:options='options'
|
||||
:initValue='initValue'
|
||||
/>
|
||||
<a-button @click='submit'>提交</a-button>
|
||||
<a-button @click='reset'>重置</a-button>
|
||||
<a-button @click='setValue'>修改name</a-button>
|
||||
</template>
|
||||
|
||||
<script setup name='FormDemo'>
|
||||
import { componentType } from 'components/Form'
|
||||
const form = ref()
|
||||
const initValue = reactive({})
|
||||
|
||||
const submit = () => {
|
||||
form.value.formValidate().then(res => {
|
||||
console.log(res)
|
||||
})
|
||||
}
|
||||
|
||||
const reset = () => {
|
||||
form.value.reset()
|
||||
}
|
||||
|
||||
const setValue =() => {
|
||||
initValue.name = '111111'
|
||||
}
|
||||
|
||||
const options = reactive({
|
||||
name: {
|
||||
component: componentType.input,
|
||||
componentProps: {
|
||||
style: {
|
||||
width: '200px'
|
||||
}
|
||||
},
|
||||
title: '测试',
|
||||
required: true
|
||||
},
|
||||
sex: {
|
||||
component: componentType.select,
|
||||
title: '性别',
|
||||
options: [
|
||||
{ label: '111', value: 1 },
|
||||
{ label: '222', value: 2 },
|
||||
],
|
||||
required: true,
|
||||
rules: [
|
||||
{ required: true, message: '请选择性别'}
|
||||
],
|
||||
tooltip: '性别',
|
||||
default: 1
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -1,83 +0,0 @@
|
|||
<!-- test demo -->
|
||||
<template>
|
||||
<div class="page-container">
|
||||
父级: {{ testValue }}
|
||||
<ValueItem v-model="testValue" />
|
||||
<!-- 卡片 -->
|
||||
<br />卡片组件:
|
||||
<a-row :gutter="20">
|
||||
<a-col :span="6">
|
||||
<CardBox
|
||||
status="disable"
|
||||
:statusNames="{ disable: StatusColorEnum.error }"
|
||||
statusText="正常"
|
||||
:actions="actions"
|
||||
v-model="data"
|
||||
>
|
||||
<template #img>
|
||||
<img :src="getImage('/device-product.png')" />
|
||||
</template>
|
||||
<template #content>
|
||||
<div class="card-item-heard-name">设备名称</div>
|
||||
<a-row>
|
||||
<a-col :span="12">
|
||||
<div class="card-item-content-text">
|
||||
设备类型
|
||||
</div>
|
||||
<div>直连设备</div>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<div class="card-item-content-text">
|
||||
产品名称
|
||||
</div>
|
||||
<div>测试固定地址</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</template>
|
||||
</CardBox>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import CardBox from '@/components/CardBox/index.vue';
|
||||
import { StatusColorEnum } from '@/utils/consts';
|
||||
import { getImage } from '@/utils/comm';
|
||||
|
||||
const testValue = ref('');
|
||||
// 卡片按钮
|
||||
const actions = ref([
|
||||
{
|
||||
key: 'check',
|
||||
label: '查看',
|
||||
},
|
||||
{
|
||||
key: 'edit',
|
||||
label: '编辑',
|
||||
disabled: true,
|
||||
message: '暂无权限,请联系管理员',
|
||||
},
|
||||
{
|
||||
key: 'delete',
|
||||
label: '删除',
|
||||
},
|
||||
]);
|
||||
const data = ref({
|
||||
id: 123
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.card-item-heard-name {
|
||||
font-weight: 700;
|
||||
font-size: 16px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.card-item-content-text {
|
||||
color: rgba(0, 0, 0, 0.75);
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
|
@ -1,187 +0,0 @@
|
|||
<template>
|
||||
<div class="box">
|
||||
<JTable
|
||||
:columns="columns"
|
||||
:request="request"
|
||||
:rowSelection="{
|
||||
selectedRowKeys: _selectedRowKeys,
|
||||
onChange: onSelectChange
|
||||
}"
|
||||
@cancelSelect="cancelSelect"
|
||||
>
|
||||
<template #headerTitle>
|
||||
<a-button type="primary" @click="add">新增</a-button>
|
||||
</template>
|
||||
<template #card="slotProps">
|
||||
<CardBox
|
||||
:value="slotProps"
|
||||
@click="handleClick"
|
||||
:actions="getActions(slotProps)"
|
||||
v-bind="slotProps"
|
||||
:active="_selectedRowKeys.includes(slotProps.id)"
|
||||
:status="slotProps.state ? 'success' : 'error'"
|
||||
>
|
||||
<template #img>
|
||||
<slot name="img">
|
||||
<img :src="getImage('/device-product.png')" />
|
||||
</slot>
|
||||
</template>
|
||||
<template #content>
|
||||
<h3>{{slotProps.name}}</h3>
|
||||
<a-row>
|
||||
<a-col :span="12">
|
||||
<div class="card-item-content-text">
|
||||
设备类型
|
||||
</div>
|
||||
<div>直连设备</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</template>
|
||||
<template #actions="item">
|
||||
<a-popconfirm v-if="item.popConfirm" v-bind="item.popConfirm">
|
||||
<a-button :disabled="item.disabled">
|
||||
<DeleteOutlined v-if="item.key === 'delete'" />
|
||||
<template v-else>
|
||||
<AIcon :type="item.icon" />
|
||||
<span>{{ item.text }}</span>
|
||||
</template>
|
||||
</a-button>
|
||||
</a-popconfirm>
|
||||
<template v-else>
|
||||
<a-button :disabled="item.disabled">
|
||||
<DeleteOutlined v-if="item.key === 'delete'" />
|
||||
<template v-else>
|
||||
<AIcon :type="item.icon" />
|
||||
<span>{{ item.text }}</span>
|
||||
</template>
|
||||
</a-button>
|
||||
</template>
|
||||
</template>
|
||||
</CardBox>
|
||||
</template>
|
||||
<template #id="slotProps">
|
||||
<a>{{slotProps.id}}</a>
|
||||
</template>
|
||||
<template #action="slotProps">
|
||||
<a-space :size="16">
|
||||
<a-tooltip v-for="i in getActions(slotProps)" :key="i.key" v-bind="i.tooltip">
|
||||
<a-popconfirm v-if="i.popConfirm" v-bind="i.popConfirm">
|
||||
<a-button :disabled="i.disabled" style="padding: 0" type="link"><AIcon :type="i.icon" /></a-button>
|
||||
</a-popconfirm>
|
||||
<a-button style="padding: 0" type="link" v-else @click="i.onClick && i.onClick(slotProps)">
|
||||
<a-button :disabled="i.disabled" style="padding: 0" type="link"><AIcon :type="i.icon" /></a-button>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
</template>
|
||||
</JTable>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import server from "@/utils/request";
|
||||
import type { ActionsType } from '@/components/Table/index.vue'
|
||||
import { getImage } from '@/utils/comm';
|
||||
import { DeleteOutlined } from '@ant-design/icons-vue'
|
||||
import { message } from "ant-design-vue";
|
||||
|
||||
const request = (data: any) => server.post(`/device-product/_query`, data)
|
||||
// const request = (data: any) => server.post(`/device/category/_tree`, {paging: false})
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
},
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'id',
|
||||
key: 'id',
|
||||
scopedSlots: true
|
||||
},
|
||||
{
|
||||
title: '分类',
|
||||
dataIndex: 'classifiedName',
|
||||
key: 'classifiedName',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
fixed: 'right',
|
||||
width: 250,
|
||||
scopedSlots: true
|
||||
}
|
||||
]
|
||||
|
||||
const _selectedRowKeys = ref<string[]>([])
|
||||
|
||||
const onSelectChange = (keys: string[]) => {
|
||||
_selectedRowKeys.value = [...keys]
|
||||
}
|
||||
|
||||
const cancelSelect = () => {
|
||||
_selectedRowKeys.value = []
|
||||
}
|
||||
|
||||
const handleClick = (dt: any) => {
|
||||
// _selectedRowKeys.value = [dt.id] // 单选
|
||||
// _selectedRowKeys.value = [..._selectedRowKeys.value, dt.id] // 多选
|
||||
if(_selectedRowKeys.value.includes(dt.id)) {
|
||||
const _index = _selectedRowKeys.value.findIndex(i => i === dt.id)
|
||||
_selectedRowKeys.value.splice(_index, 1)
|
||||
} else {
|
||||
_selectedRowKeys.value = [..._selectedRowKeys.value, dt.id]
|
||||
}
|
||||
}
|
||||
|
||||
const getActions = (data: Partial<Record<string, any>>): ActionsType[] => {
|
||||
if(!data){
|
||||
return []
|
||||
}
|
||||
return [
|
||||
{
|
||||
key: 'edit',
|
||||
text: "编辑",
|
||||
tooltip: {
|
||||
title: '编辑'
|
||||
},
|
||||
icon: 'icon-rizhifuwu'
|
||||
},
|
||||
{
|
||||
key: 'import',
|
||||
text: "导入",
|
||||
tooltip: {
|
||||
title: '导入'
|
||||
},
|
||||
disabled: true,
|
||||
icon: 'icon-xiazai'
|
||||
},
|
||||
{
|
||||
key: 'delete',
|
||||
text: "删除",
|
||||
tooltip: {
|
||||
title: !!data?.state ? '正常的产品不能删除' : '删除'
|
||||
},
|
||||
popConfirm: {
|
||||
title: '确认删除?'
|
||||
},
|
||||
icon: 'icon-huishouzhan'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const add = () => {
|
||||
message.warn('123')
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style lang="less" scoped>
|
||||
.box {
|
||||
padding: 20px;
|
||||
background: #f0f2f5;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
import { Button } from 'ant-design-vue'
|
||||
|
||||
export default defineComponent({
|
||||
setup(){
|
||||
return () => <div>
|
||||
<Button type='primary'>新增</Button>
|
||||
</div>
|
||||
}
|
||||
})
|
|
@ -76,6 +76,7 @@ import type { ActionsType } from '@/components/Table/index.vue';
|
|||
import ModifyModal from './components/modifyModal/index.vue';
|
||||
import type { TableColumnType, TableProps } from 'ant-design-vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
const expandedRowKeys = ref<any>([]);
|
||||
const tableRef = ref<Record<string, any>>({});
|
||||
const modifyRef = ref();
|
||||
const dataSource = ref<any>([]);
|
||||
|
@ -126,7 +127,7 @@ let params = ref();
|
|||
* 搜索
|
||||
*/
|
||||
const handleSearch = (e: any) => {
|
||||
params.value = e
|
||||
params.value = e;
|
||||
};
|
||||
/**
|
||||
* 操作栏按钮
|
||||
|
|
|
@ -8,20 +8,25 @@
|
|||
<template #title>
|
||||
<div>
|
||||
<div style="display: flex; align-items: center">
|
||||
<AIcon type="ArrowLeftOutlined" @click="onBack" />
|
||||
<div style="margin-left: 20px">
|
||||
<j-button @click="onBack" size="small">返回</j-button>
|
||||
<div style="margin-left: 20px; font-size: 24px">
|
||||
{{ instanceStore.current.name }}
|
||||
</div>
|
||||
<j-divider type="vertical" />
|
||||
<j-space>
|
||||
<j-badge
|
||||
:text="instanceStore.current.state?.text"
|
||||
:status="
|
||||
statusMap.get(
|
||||
instanceStore.current.state?.value,
|
||||
)
|
||||
"
|
||||
/>
|
||||
<span
|
||||
style="font-size: 14px; color: rgba(0, 0, 0, 0.85)"
|
||||
>
|
||||
状态:
|
||||
<j-badge
|
||||
:status="
|
||||
statusMap.get(
|
||||
instanceStore.current.state?.value,
|
||||
)
|
||||
"
|
||||
/>
|
||||
{{ instanceStore.current.state?.text }}
|
||||
</span>
|
||||
<PermissionButton
|
||||
v-if="
|
||||
instanceStore.current.state?.value ===
|
||||
|
@ -73,7 +78,7 @@
|
|||
</j-tooltip>
|
||||
</j-space>
|
||||
</div>
|
||||
<div style="padding-top: 10px">
|
||||
<div style="padding-top: 24px">
|
||||
<j-descriptions size="small" :column="4">
|
||||
<j-descriptions-item label="ID">{{
|
||||
instanceStore.current.id
|
||||
|
@ -199,8 +204,8 @@ watch(
|
|||
);
|
||||
|
||||
onMounted(() => {
|
||||
instanceStore.tabActiveKey = history.state?.params?.tab || 'Info'
|
||||
})
|
||||
instanceStore.tabActiveKey = history.state?.params?.tab || 'Info';
|
||||
});
|
||||
|
||||
const onBack = () => {
|
||||
menuStory.jumpPage('device/Instance');
|
||||
|
@ -284,7 +289,10 @@ watchEffect(() => {
|
|||
tab: 'OPC UA',
|
||||
});
|
||||
}
|
||||
if (instanceStore.current.deviceType?.value === 'gateway' && !keys.includes('ChildDevice')) {
|
||||
if (
|
||||
instanceStore.current.deviceType?.value === 'gateway' &&
|
||||
!keys.includes('ChildDevice')
|
||||
) {
|
||||
// 产品类型为网关的情况下才显示此模块
|
||||
list.value.push({
|
||||
key: 'ChildDevice',
|
||||
|
|
|
@ -143,7 +143,7 @@
|
|||
:status="slotProps.state?.value"
|
||||
:statusText="slotProps.state?.text"
|
||||
:statusNames="{
|
||||
online: 'success',
|
||||
online: 'processing',
|
||||
offline: 'error',
|
||||
notActive: 'warning',
|
||||
}"
|
||||
|
@ -202,9 +202,14 @@
|
|||
</CardBox>
|
||||
</template>
|
||||
<template #state="slotProps">
|
||||
<j-badge
|
||||
<BadgeStatus
|
||||
:status="slotProps.state?.value"
|
||||
:text="slotProps.state?.text"
|
||||
:status="statusMap.get(slotProps.state?.value)"
|
||||
:statusNames="{
|
||||
online: 'processing',
|
||||
offline: 'error',
|
||||
notActive: 'warning',
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
<template #createTime="slotProps">
|
||||
|
@ -226,7 +231,7 @@
|
|||
}"
|
||||
@click="i.onClick"
|
||||
type="link"
|
||||
style="padding: 0px"
|
||||
style="padding: 0 5px"
|
||||
:hasPermission="'device/Instance:' + i.key"
|
||||
>
|
||||
<template #icon><AIcon :type="i.icon" /></template>
|
||||
|
@ -289,6 +294,7 @@ import { queryTree } from '@/api/device/category';
|
|||
import { useMenuStore } from '@/store/menu';
|
||||
import type { ActionsType } from './typings';
|
||||
import dayjs from 'dayjs';
|
||||
import BadgeStatus from '@/components/BadgeStatus/index.vue';
|
||||
|
||||
const instanceRef = ref<Record<string, any>>({});
|
||||
const params = ref<Record<string, any>>({});
|
||||
|
@ -303,11 +309,6 @@ const type = ref<string>('');
|
|||
|
||||
const menuStory = useMenuStore();
|
||||
|
||||
const statusMap = new Map();
|
||||
statusMap.set('online', 'success');
|
||||
statusMap.set('offline', 'error');
|
||||
statusMap.set('notActive', 'warning');
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: 'ID',
|
||||
|
@ -479,6 +480,7 @@ const columns = [
|
|||
title: '说明',
|
||||
dataIndex: 'describe',
|
||||
key: 'describe',
|
||||
ellipsis: true,
|
||||
search: {
|
||||
type: 'string',
|
||||
},
|
||||
|
@ -487,7 +489,7 @@ const columns = [
|
|||
title: '操作',
|
||||
key: 'action',
|
||||
fixed: 'right',
|
||||
width: 250,
|
||||
width: 200,
|
||||
scopedSlots: true,
|
||||
},
|
||||
];
|
||||
|
@ -525,10 +527,10 @@ const paramsFormat = (
|
|||
};
|
||||
|
||||
onMounted(() => {
|
||||
if(history.state?.params?.type === 'add'){
|
||||
handleAdd()
|
||||
if (history.state?.params?.type === 'add') {
|
||||
handleAdd();
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
const handleParams = (config: Record<string, any>) => {
|
||||
const _terms: Record<string, any> = {};
|
||||
|
|
|
@ -374,6 +374,7 @@ import { Empty, FormItem, message } from 'ant-design-vue';
|
|||
import { getImage } from '@/utils/comm';
|
||||
import Title from '../Title/index.vue';
|
||||
import { usePermissionStore } from '@/store/permission';
|
||||
import { steps, steps1 } from './util'
|
||||
import './index.less';
|
||||
import {
|
||||
getProviders,
|
||||
|
@ -412,7 +413,7 @@ const visible = ref<boolean>(false);
|
|||
const listData = ref<string[]>([]);
|
||||
const access = ref({});
|
||||
const config = ref<any>({});
|
||||
const metadata = ref<ConfigMetadata[]>([]);
|
||||
const metadata = ref<ConfigMetadata>({ properties: [] });
|
||||
const dataSource = ref<string[]>([]);
|
||||
const storageList = ref<any[]>([]);
|
||||
const markdownToHtml = shallowRef('');
|
||||
|
@ -589,74 +590,8 @@ const search = (e: any) => {
|
|||
};
|
||||
};
|
||||
|
||||
//引导页数据
|
||||
const steps = [
|
||||
{
|
||||
element: '#rc-tabs-0-tab-Metadata',
|
||||
popover: {
|
||||
id: 'driver',
|
||||
title: `<div id='title'>配置物模型</div><div id='guide'>1/3</div>`,
|
||||
description: `配置产品物模型,实现设备在云端的功能描述。`,
|
||||
position: 'bottom',
|
||||
},
|
||||
},
|
||||
{
|
||||
element: '.ant-switch',
|
||||
popover: {
|
||||
className: 'driver',
|
||||
title: `<div id='title'>启用产品</div><div id='guide'>2/3</div>`,
|
||||
description: '启用产品后,可在产品下新增设备。',
|
||||
position: 'bottom',
|
||||
},
|
||||
},
|
||||
{
|
||||
element: '.ant-descriptions-item-label',
|
||||
popover: {
|
||||
className: 'driver',
|
||||
title: `<div id='title'>添加设备</div><div id='guide'>3/3</div>`,
|
||||
description: '添加设备,并连接到平台。',
|
||||
position: 'bottom',
|
||||
},
|
||||
},
|
||||
];
|
||||
const steps1 = [
|
||||
{
|
||||
element: '.config',
|
||||
popover: {
|
||||
className: 'driver',
|
||||
title: `<div id='title'>填写配置</div><div id='guide'>1/4</div>`,
|
||||
description: `填写设备接入所需的配置参数。`,
|
||||
position: 'right',
|
||||
},
|
||||
},
|
||||
{
|
||||
element: '#rc-tabs-0-tab-Metadata',
|
||||
popover: {
|
||||
className: 'driver',
|
||||
title: `<div id='title'>配置物模型</div><div id='guide'>2/4</div>`,
|
||||
description: `配置产品物模型,实现设备在云端的功能描述。`,
|
||||
position: 'bottom',
|
||||
},
|
||||
},
|
||||
{
|
||||
element: '.ant-switch',
|
||||
popover: {
|
||||
className: 'driver',
|
||||
title: `<div id='title'>启用产品</div><div id='guide'>3/4</div>`,
|
||||
description: '启用产品后,可在产品下新增设备。',
|
||||
position: 'bottom',
|
||||
},
|
||||
},
|
||||
{
|
||||
element: '.ant-descriptions-item-label',
|
||||
popover: {
|
||||
className: 'driver',
|
||||
title: `<div id='title'>添加设备</div><div id='guide'>4/4</div>`,
|
||||
description: '添加设备,并连接到平台。',
|
||||
position: 'bottom',
|
||||
},
|
||||
},
|
||||
];
|
||||
const stepsRef = reactive({current:0})
|
||||
|
||||
/**
|
||||
* 保存引导页数据
|
||||
*/
|
||||
|
@ -673,19 +608,19 @@ const driver = new Driver({
|
|||
nextBtnText: '下一步',
|
||||
prevBtnText: '上一步',
|
||||
onNext: () => {
|
||||
// ref.current = ref.current + 1;
|
||||
stepsRef.current = stepsRef.current + 1;
|
||||
},
|
||||
onPrevious: () => {
|
||||
// ref.current = ref.current - 1;
|
||||
stepsRef.current = stepsRef.current - 1;
|
||||
},
|
||||
onReset: () => {
|
||||
// if (ref.current !== 3) {
|
||||
// guide({
|
||||
// name: 'guide',
|
||||
// content: 'skip',
|
||||
// });
|
||||
// }
|
||||
// ref.current = 0;
|
||||
if (stepsRef.current !== 3) {
|
||||
guide({
|
||||
name: 'guide',
|
||||
content: 'skip',
|
||||
});
|
||||
}
|
||||
stepsRef.current = 0;
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -695,16 +630,20 @@ const driver1 = new Driver({
|
|||
closeBtnText: '不再提示',
|
||||
nextBtnText: '下一步',
|
||||
prevBtnText: '上一步',
|
||||
onNext: () => {},
|
||||
onPrevious: () => {},
|
||||
onNext: () => {
|
||||
stepsRef.current = stepsRef.current + 1;
|
||||
},
|
||||
onPrevious: () => {
|
||||
stepsRef.current = stepsRef.current - 1;
|
||||
},
|
||||
onReset: () => {
|
||||
// if (ref.current !== 4) {
|
||||
// // guide({
|
||||
// // name: 'guide',
|
||||
// // content: 'skip',
|
||||
// // });
|
||||
// }
|
||||
// ref.current = 0;
|
||||
if (stepsRef.current !== 4) {
|
||||
guide({
|
||||
name: 'guide',
|
||||
content: 'skip',
|
||||
});
|
||||
}
|
||||
stepsRef.current = 0;
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -865,11 +804,11 @@ const queryAccessDetail = async (id: string) => {
|
|||
/**
|
||||
* 查询协议信息
|
||||
*/
|
||||
const getConfigDetail = async (
|
||||
const getConfigDetail = (
|
||||
messageProtocol: string,
|
||||
transportProtocol: string,
|
||||
) => {
|
||||
const res = await getConfigView(messageProtocol, transportProtocol).then(
|
||||
getConfigView(messageProtocol, transportProtocol).then(
|
||||
(resp) => {
|
||||
if (resp.status === 200) {
|
||||
config.value = resp.result;
|
||||
|
@ -960,10 +899,12 @@ const submitData = async () => {
|
|||
obj.metadata = JSON.stringify(mdata);
|
||||
}
|
||||
}
|
||||
// 保存或者更新设备接入
|
||||
const resp: any = obj.id
|
||||
? await updateDevice(obj)
|
||||
: await saveDevice(obj);
|
||||
if (resp.status === 200) {
|
||||
|
||||
detail(productStore.current?.id || '').then((res) => {
|
||||
if (res.status === 200) {
|
||||
productStore.current = { ...res.result };
|
||||
|
@ -972,7 +913,9 @@ const submitData = async () => {
|
|||
}
|
||||
visible.value = false;
|
||||
queryParams.value = {};
|
||||
|
||||
});
|
||||
getData(obj.accessId);
|
||||
}
|
||||
} else {
|
||||
message.error('请选择接入方式');
|
||||
|
@ -988,48 +931,59 @@ const modifyArray = (oldData: any[], newData: any[]) => {
|
|||
return { ...item, sortsIndex: index };
|
||||
});
|
||||
};
|
||||
/**
|
||||
*
|
||||
*/
|
||||
const getGuide = async (isDriver1: boolean = false) => {
|
||||
const res: any = await productGuide();
|
||||
if (res.result && res.result?.content === 'skip') {
|
||||
return;
|
||||
} else {
|
||||
if (isDriver1) {
|
||||
driver1.defineSteps(steps1);
|
||||
driver1.start();
|
||||
} else {
|
||||
driver.defineSteps(steps);
|
||||
driver.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 查询保存数据信息
|
||||
*/
|
||||
const getData = async () => {
|
||||
if (productStore.current?.accessId) {
|
||||
if (productStore.current?.id) {
|
||||
getConfigMetadata(productStore.current?.id).then(
|
||||
async (resp: any) => {
|
||||
metadata.value =
|
||||
(resp?.result[0] as ConfigMetadata[]) || [];
|
||||
const res: any = await productGuide();
|
||||
if (res.result && res.result?.content === 'skip') {
|
||||
return;
|
||||
} else {
|
||||
if (resp.result && resp.result.length > 0) {
|
||||
driver1.defineSteps(steps1);
|
||||
driver1.start();
|
||||
} else {
|
||||
driver.defineSteps(steps);
|
||||
driver.start();
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
queryAccessDetail(productStore.current?.accessId);
|
||||
getConfigDetail(
|
||||
productStore.current?.messageProtocol || '',
|
||||
productStore.current?.transportProtocol || '',
|
||||
);
|
||||
getProviders().then((resp) => {
|
||||
if (resp.status === 200) {
|
||||
dataSource.value = resp.result;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (productStore.current?.id) {
|
||||
getConfigMetadata(productStore.current?.id).then((resp: any) => {
|
||||
metadata.value = resp?.result[0] as ConfigMetadata[];
|
||||
});
|
||||
const getData = async (accessId?: string) => {
|
||||
const _accessId = accessId || productStore.current?.accessId
|
||||
if (productStore.current?.id) {
|
||||
getConfigMetadata(productStore.current?.id).then((resp: any) => {
|
||||
metadata.value = resp?.result[0] as ConfigMetadata || { properties: [] };
|
||||
if (accessId) { // 切换接入方式之后获取是否显示引导
|
||||
getGuide(!resp?.result.length) //
|
||||
}
|
||||
});
|
||||
}
|
||||
if (_accessId) { // 有设备接入
|
||||
// const metadataResp = await getConfigMetadata(productStore.current!.id)
|
||||
// if (metadataResp.success) {
|
||||
// metadata.value = (metadataResp.result?.[0] as ConfigMetadata[]) || [];
|
||||
// }
|
||||
queryAccessDetail(_accessId);
|
||||
getConfigDetail(
|
||||
productStore.current?.messageProtocol || '',
|
||||
productStore.current?.transportProtocol || '',
|
||||
);
|
||||
getProviders().then((resp) => {
|
||||
if (resp.status === 200) {
|
||||
dataSource.value = resp.result;
|
||||
}
|
||||
});
|
||||
}
|
||||
// else {
|
||||
// if (productStore.current?.id) {
|
||||
// getConfigMetadata(productStore.current?.id).then((resp: any) => {
|
||||
// metadata.value = resp?.result[0] as ConfigMetadata[];
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
getStoragList().then((resp: any) => {
|
||||
if (resp.status === 200) {
|
||||
storageList.value = resp.result;
|
||||
|
@ -1086,10 +1040,14 @@ const add = () => {
|
|||
* 初始化
|
||||
*/
|
||||
watchEffect(() => {
|
||||
if (productStore.current?.accessId) {
|
||||
getData();
|
||||
}
|
||||
});
|
||||
if (productStore.current?.storePolicy) {
|
||||
form.storePolicy = productStore.current!.storePolicy
|
||||
}
|
||||
})
|
||||
|
||||
nextTick(() => {
|
||||
getData();
|
||||
})
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
:deep(
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
//引导页数据
|
||||
export const steps = [
|
||||
{
|
||||
element: '#rc-tabs-0-tab-Metadata',
|
||||
popover: {
|
||||
id: 'driver',
|
||||
title: `<div id='title'>配置物模型</div><div id='guide'>1/3</div>`,
|
||||
description: `配置产品物模型,实现设备在云端的功能描述。`,
|
||||
position: 'bottom',
|
||||
},
|
||||
},
|
||||
{
|
||||
element: '.ant-switch',
|
||||
popover: {
|
||||
className: 'driver',
|
||||
title: `<div id='title'>启用产品</div><div id='guide'>2/3</div>`,
|
||||
description: '启用产品后,可在产品下新增设备。',
|
||||
position: 'bottom',
|
||||
},
|
||||
},
|
||||
{
|
||||
element: '.ant-descriptions-item-label',
|
||||
popover: {
|
||||
className: 'driver',
|
||||
title: `<div id='title'>添加设备</div><div id='guide'>3/3</div>`,
|
||||
description: '添加设备,并连接到平台。',
|
||||
position: 'bottom',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const steps1 = [
|
||||
{
|
||||
element: '.config',
|
||||
popover: {
|
||||
className: 'driver',
|
||||
title: `<div id='title'>填写配置</div><div id='guide'>1/4</div>`,
|
||||
description: `填写设备接入所需的配置参数。`,
|
||||
position: 'right',
|
||||
},
|
||||
},
|
||||
{
|
||||
element: '#rc-tabs-0-tab-Metadata',
|
||||
popover: {
|
||||
className: 'driver',
|
||||
title: `<div id='title'>配置物模型</div><div id='guide'>2/4</div>`,
|
||||
description: `配置产品物模型,实现设备在云端的功能描述。`,
|
||||
position: 'bottom',
|
||||
},
|
||||
},
|
||||
{
|
||||
element: '.ant-switch',
|
||||
popover: {
|
||||
className: 'driver',
|
||||
title: `<div id='title'>启用产品</div><div id='guide'>3/4</div>`,
|
||||
description: '启用产品后,可在产品下新增设备。',
|
||||
position: 'bottom',
|
||||
},
|
||||
},
|
||||
{
|
||||
element: '.ant-descriptions-item-label',
|
||||
popover: {
|
||||
className: 'driver',
|
||||
title: `<div id='title'>添加设备</div><div id='guide'>4/4</div>`,
|
||||
description: '添加设备,并连接到平台。',
|
||||
position: 'bottom',
|
||||
},
|
||||
},
|
||||
];
|
|
@ -87,6 +87,7 @@
|
|||
<component
|
||||
:is="tabs[productStore.tabActiveKey]"
|
||||
:class="productStore.tabActiveKey === 'Metadata' ? 'metedata' : ''"
|
||||
v-bind="{ type: 'product' }"
|
||||
/>
|
||||
</page-container>
|
||||
</template>
|
||||
|
@ -151,6 +152,7 @@ watch(
|
|||
() => route.params.id,
|
||||
(newId) => {
|
||||
if (newId) {
|
||||
productStore.reSet();
|
||||
productStore.tabActiveKey = 'Info';
|
||||
productStore.refresh(newId as string);
|
||||
}
|
||||
|
|
|
@ -132,7 +132,7 @@
|
|||
<PermissionButton
|
||||
:disabled="i.disabled"
|
||||
:popConfirm="i.popConfirm"
|
||||
:hasPermission="'device/Product:' + i.key"
|
||||
:hasPermission="i.key === 'view' ? true : 'device/Product:' + i.key"
|
||||
:tooltip="{
|
||||
...i.tooltip,
|
||||
}"
|
||||
|
|
|
@ -47,9 +47,9 @@ export type ConfigProperty = {
|
|||
};
|
||||
|
||||
export type ConfigMetadata = {
|
||||
name: string;
|
||||
description: string;
|
||||
scopes: any[];
|
||||
name?: string;
|
||||
description?: string;
|
||||
scopes?: any[];
|
||||
properties: ConfigProperty[];
|
||||
};
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
</j-radio-group>
|
||||
</j-form-item>
|
||||
<j-form-item label="输入参数" name="inputs" :rules="[
|
||||
{ required: true, validator: (_rule: Rule, val: Record<any, any>[]) => validateJson(_rule, val, '输入参数') },
|
||||
{ validator: (_rule: Rule, val: Record<any, any>[]) => validateJson(_rule, val, '输入参数', false) },
|
||||
]">
|
||||
<JsonParam v-model:value="value.inputs" :name="['inputs']"></JsonParam>
|
||||
</j-form-item>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<j-form-item :label="title" :name="name.concat(['type'])" :rules="[
|
||||
required ? { required: true, message: `请选择${title}` } : {},
|
||||
]">
|
||||
<j-select v-model:value="_value.type"
|
||||
<j-select v-model:value="_value.type" :disabled="onlyObject"
|
||||
:options="onlyObject ? eventDataTypeList : _dataTypeList" size="small"
|
||||
@change="changeType"></j-select>
|
||||
</j-form-item>
|
||||
|
@ -124,7 +124,7 @@ onMounted(() => {
|
|||
}
|
||||
})
|
||||
|
||||
const unit = {
|
||||
const unit = reactive({
|
||||
unitOptions: [] as DefaultOptionType[],
|
||||
getUnit: () => {
|
||||
getUnit().then(resp => {
|
||||
|
@ -137,7 +137,7 @@ const unit = {
|
|||
unit.unitOptions = _data;
|
||||
})
|
||||
},
|
||||
}
|
||||
})
|
||||
unit.getUnit()
|
||||
|
||||
const _dataTypeList = computed(() => props.isSub ? DataTypeList.filter(item => item.value !== 'array' && item.value !== 'object') : DataTypeList)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<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>
|
||||
<j-button :loading="save.loading" type="primary" @click="save.saveMetadata">保存</j-button>
|
||||
<j-button :loading="save.loading" type="primary" @click="() => save.saveMetadata()">保存</j-button>
|
||||
</template>
|
||||
<j-form ref="formRef" :model="form.model" layout="vertical">
|
||||
<BaseForm :model-type="metadataStore.model.type" :type="type" v-model:value="form.model"></BaseForm>
|
||||
|
@ -98,7 +98,7 @@ const save = reactive({
|
|||
}
|
||||
const _data = updateMetadata(type, [formValue], _detail, updateStore)
|
||||
const result = await asyncUpdateMetadata(props.type, _data)
|
||||
if (result.status === 200) {
|
||||
if (result.success) {
|
||||
if ((window as any).onTabSaveSuccess) {
|
||||
if (result) {
|
||||
(window as any).onTabSaveSuccess(result);
|
||||
|
@ -113,7 +113,16 @@ const save = reactive({
|
|||
}
|
||||
// Store.set(SystemConst.REFRESH_METADATA_TABLE, true);
|
||||
if (deploy) {
|
||||
_deploy(id as string)
|
||||
const res = await _deploy(id as string)
|
||||
if (res.success) {
|
||||
save.resetMetadata();
|
||||
message.success({
|
||||
key: 'metadata',
|
||||
content: '操作成功!',
|
||||
});
|
||||
} else {
|
||||
message.error('操作失败!');
|
||||
}
|
||||
// Store.set('product-deploy', deploy);
|
||||
} else {
|
||||
save.resetMetadata();
|
||||
|
|
|
@ -17,8 +17,8 @@ export const validateArray = async (_rule: Rule, val: Record<any, any>) => {
|
|||
return Promise.resolve();
|
||||
}
|
||||
|
||||
export const validateJson = async (_rule: Rule, val: Record<any, any>[], title = '配置参数') => {
|
||||
if (!val || val.length === 0) {
|
||||
export const validateJson = async (_rule: Rule, val: Record<any, any>[], title = '配置参数', required = true) => {
|
||||
if (required && (!val || val.length === 0)) {
|
||||
return Promise.reject(new Error(`请输入${title}`));
|
||||
}
|
||||
for (let item of val) {
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
<template v-if="column.dataIndex === 'action'">
|
||||
<j-space>
|
||||
<PermissionButton :has-permission="`${permission}:update`" type="link" key="edit" style="padding: 0"
|
||||
:udisabled="operateLimits('updata', type)" @click="handleEditClick(record)" :tooltip="{
|
||||
:disabled="operateLimits('updata', type)" @click="handleEditClick(record)" :tooltip="{
|
||||
title: operateLimits('updata', type) ? '当前的存储方式不支持编辑' : '编辑',
|
||||
}">
|
||||
<AIcon type="EditOutlined" />
|
||||
|
@ -68,7 +68,6 @@ import { useMetadataStore } from '@/store/metadata'
|
|||
import PermissionButton from '@/components/PermissionButton/index.vue'
|
||||
import { TablePaginationConfig, message } from 'ant-design-vue/es'
|
||||
import { asyncUpdateMetadata, removeMetadata } from '../metadata'
|
||||
import { detail } from '@/api/device/instance'
|
||||
import Edit from './Edit/index.vue'
|
||||
interface Props {
|
||||
type: MetadataType;
|
||||
|
@ -153,6 +152,9 @@ const handleAddClick = () => {
|
|||
metadataStore.set('item', undefined)
|
||||
metadataStore.set('type', type)
|
||||
metadataStore.set('action', 'add')
|
||||
if (props.target === 'device' && !instanceStore.detail?.independentMetadata) {
|
||||
message.warning('修改物模型后会脱离产品物模型')
|
||||
}
|
||||
}
|
||||
|
||||
const limitsMap = new Map<string, any>();
|
||||
|
@ -172,7 +174,7 @@ const handleEditClick = (record: MetadataItem) => {
|
|||
metadataStore.model.item = record;
|
||||
metadataStore.model.type = type;
|
||||
metadataStore.model.action = 'edit';
|
||||
if (!instanceStore.detail?.independentMetadata && props.target === 'device') {
|
||||
if (props.target === 'device' && !instanceStore.detail?.independentMetadata) {
|
||||
message.warning('修改物模型后会脱离产品物模型');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<j-card>
|
||||
<div class='device-detail-metadata' style="position: relative;">
|
||||
<div class="tips">
|
||||
<j-tooltip :title="instanceStore.detail?.independentMetadata && type === 'device'
|
||||
<j-tooltip v-if="type === 'device'" :title="instanceStore.detail?.independentMetadata && type === 'device'
|
||||
? '该设备已脱离产品物模型,修改产品物模型对该设备无影响'
|
||||
: '设备会默认继承产品的物模型,修改设备物模型后将脱离产品物模型'">
|
||||
<div class="ellipsis">
|
||||
|
@ -41,7 +41,7 @@
|
|||
<BaseMetadata :target="type" type="tags" :permission="permission" />
|
||||
</j-tab-pane>
|
||||
</j-tabs>
|
||||
<Import v-model:visible="visible" :type="type" @close="visible = false" />
|
||||
<Import v-if="visible" v-model:visible="visible" :type="type" @close="visible = false" />
|
||||
<Cat v-model:visible="cat" @close="cat = false" :type="type" />
|
||||
</div>
|
||||
</j-card>
|
||||
|
|
|
@ -51,7 +51,7 @@
|
|||
<h3 class="card-item-content-title">
|
||||
{{ slotProps.name }}
|
||||
</h3>
|
||||
<p>通道数量:{{ slotProps.count }}</p>
|
||||
<p>通道数量:{{ slotProps.count || 0 }}</p>
|
||||
<Ellipsis>
|
||||
<j-badge
|
||||
:text="`sip:${slotProps.sipConfigs[0]?.sipId}@${slotProps.sipConfigs[0]?.hostAndPort}`"
|
||||
|
@ -91,6 +91,9 @@
|
|||
<template #publicHost="slotProps">
|
||||
{{ slotProps.sipConfigs[0]?.publicHost }}
|
||||
</template>
|
||||
<template #count="slotProps">
|
||||
{{ slotProps.count || 0 }}
|
||||
</template>
|
||||
<template #status="slotProps">
|
||||
<j-badge
|
||||
:text="slotProps.status?.text"
|
||||
|
@ -180,6 +183,7 @@ const columns = [
|
|||
title: '通道数量',
|
||||
dataIndex: 'count',
|
||||
key: 'count',
|
||||
scopedSlots: true,
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
|
@ -237,13 +241,17 @@ const handleSearch = (e: any) => {
|
|||
* 处理表格数据
|
||||
* @param params
|
||||
*/
|
||||
const lastValueFrom = async (params: any) => {
|
||||
const res = await CascadeApi.list(params);
|
||||
res.result.data.forEach(async (item: any) => {
|
||||
const resp = await CascadeApi.queryBindChannel(item.id, {});
|
||||
item.count = resp.result.total;
|
||||
const lastValueFrom = (params: any) => {
|
||||
return new Promise(async (resolve) => {
|
||||
const res = await CascadeApi.list(params);
|
||||
res.result.data.forEach(async (item: any) => {
|
||||
const resp = await CascadeApi.queryBindChannel(item.id, {});
|
||||
item.count = resp.result.total;
|
||||
});
|
||||
setTimeout(() => {
|
||||
resolve(res);
|
||||
}, 1000);
|
||||
});
|
||||
return res;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -22,10 +22,18 @@
|
|||
: '停止录像'
|
||||
}}
|
||||
</div>
|
||||
<div class="tool-item">刷新</div>
|
||||
<div class="tool-item" @click.stop="handleRefresh">
|
||||
刷新
|
||||
</div>
|
||||
<div class="tool-item" @click.stop="handleReset">重置</div>
|
||||
</div>
|
||||
<LivePlayer :live="true" :url="url" :protocol="mediaType" autoplay />
|
||||
<LivePlayer
|
||||
ref="player"
|
||||
:live="true"
|
||||
:url="url"
|
||||
:protocol="mediaType"
|
||||
autoplay
|
||||
/>
|
||||
</div>
|
||||
<MediaTool
|
||||
@onMouseDown="handleMouseDown"
|
||||
|
@ -70,6 +78,8 @@ const _vis = computed({
|
|||
set: (val) => emit('update:visible', val),
|
||||
});
|
||||
|
||||
// 播放器
|
||||
const player = ref();
|
||||
// 视频地址
|
||||
const url = ref('');
|
||||
// 视频类型
|
||||
|
@ -126,6 +136,13 @@ const handleRecord = async () => {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 刷新
|
||||
*/
|
||||
const handleRefresh = () => {
|
||||
player.value.play();
|
||||
};
|
||||
|
||||
/**
|
||||
* 重置
|
||||
*/
|
||||
|
|
|
@ -24,9 +24,8 @@
|
|||
</j-form-item>
|
||||
<j-row :gutter="24">
|
||||
<j-col :span="8">
|
||||
<JUpload
|
||||
<JProUpload
|
||||
v-model:modelValue="formData.photoUrl"
|
||||
:bgImage="formData.photoUrl"
|
||||
/>
|
||||
</j-col>
|
||||
<j-col :span="16">
|
||||
|
@ -92,11 +91,14 @@
|
|||
v-model:value="formData.productId"
|
||||
placeholder="请选择所属产品"
|
||||
:disabled="!!route.query.id"
|
||||
showSearch
|
||||
@change="handleProductChange"
|
||||
>
|
||||
<j-select-option
|
||||
v-for="(item, index) in productList"
|
||||
:key="index"
|
||||
:value="item.id"
|
||||
:label="item.name"
|
||||
>
|
||||
{{ item.name }}
|
||||
</j-select-option>
|
||||
|
@ -345,6 +347,12 @@ const getProductList = async () => {
|
|||
};
|
||||
getProductList();
|
||||
|
||||
const handleProductChange = () => {
|
||||
formData.value.others.access_pwd =
|
||||
productList.value.find((f: any) => f.id === formData.value.productId)
|
||||
?.configuration.access_pwd || '';
|
||||
};
|
||||
|
||||
/**
|
||||
* 新增产品
|
||||
*/
|
||||
|
@ -408,7 +416,7 @@ const handleSubmit = () => {
|
|||
: await DeviceApi.update(params);
|
||||
if (res?.success) {
|
||||
message.success('保存成功');
|
||||
router.back();
|
||||
history.back();
|
||||
}
|
||||
})
|
||||
.catch((err: any) => {
|
||||
|
|
|
@ -104,6 +104,9 @@
|
|||
<template #provider="slotProps">
|
||||
{{ providerType[slotProps.provider] }}
|
||||
</template>
|
||||
<template #productId="slotProps">
|
||||
{{ getProductName(slotProps.productId) }}
|
||||
</template>
|
||||
<template #state="slotProps">
|
||||
<j-badge
|
||||
:text="slotProps.state?.text"
|
||||
|
@ -131,7 +134,10 @@
|
|||
style="padding: 0px"
|
||||
:hasPermission="
|
||||
'media/Device:' +
|
||||
(i.key !== 'updateChannel' ? i.key : 'update')
|
||||
(i.key !== 'updateChannel' &&
|
||||
i.key !== 'viewDevice'
|
||||
? i.key
|
||||
: 'update')
|
||||
"
|
||||
>
|
||||
<template #icon><AIcon :type="i.icon" /></template>
|
||||
|
@ -211,7 +217,7 @@ const columns = [
|
|||
title: '产品名称',
|
||||
dataIndex: 'productId',
|
||||
key: 'productId',
|
||||
// scopedSlots: true,
|
||||
scopedSlots: true,
|
||||
search: {
|
||||
type: 'select',
|
||||
options: () =>
|
||||
|
@ -325,6 +331,17 @@ const getActions = (
|
|||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'viewDevice',
|
||||
text: '查看',
|
||||
tooltip: {
|
||||
title: '查看',
|
||||
},
|
||||
icon: 'EyeOutlined',
|
||||
onClick: () => {
|
||||
menuStory.jumpPage('device/Instance/Detail', { id: data.id });
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'updateChannel',
|
||||
text: '更新通道',
|
||||
|
@ -374,8 +391,42 @@ const getActions = (
|
|||
icon: 'DeleteOutlined',
|
||||
},
|
||||
];
|
||||
return data.provider === 'fixed-media'
|
||||
? actions.filter((f: any) => f.key !== 'updateChannel')
|
||||
: actions;
|
||||
|
||||
let acts: any = [];
|
||||
if (type === 'card') {
|
||||
// 卡片不展示查看按钮
|
||||
const tempActs = actions.filter((f: any) => f.key !== 'viewDevice');
|
||||
acts =
|
||||
data.provider === 'fixed-media'
|
||||
? tempActs.filter((f: any) => f.key !== 'updateChannel')
|
||||
: tempActs;
|
||||
} else {
|
||||
acts =
|
||||
data.provider === 'fixed-media'
|
||||
? actions.filter((f: any) => f.key !== 'updateChannel')
|
||||
: actions;
|
||||
}
|
||||
return acts;
|
||||
};
|
||||
|
||||
const productList = ref<any[]>([]);
|
||||
const getProductList = () => {
|
||||
DeviceApi.getProductList(
|
||||
encodeQuery({
|
||||
terms: {
|
||||
messageProtocol$in: ['gb28181-2016', 'fixed-media'],
|
||||
},
|
||||
}),
|
||||
).then((resp: any) => {
|
||||
productList.value = resp.result.map((pItem: any) => ({
|
||||
label: pItem.name,
|
||||
value: pItem.id,
|
||||
}));
|
||||
});
|
||||
};
|
||||
getProductList();
|
||||
|
||||
const getProductName = (pid: string) => {
|
||||
return productList.value.find((f: any) => f.value === pid)?.label;
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -23,6 +23,11 @@ export type DeviceItem = {
|
|||
transport: string;
|
||||
} & BaseItem;
|
||||
|
||||
type configuration = {
|
||||
access_pwd: string;
|
||||
stream_mode: string;
|
||||
}
|
||||
|
||||
export type ProductType = {
|
||||
accessId: string;
|
||||
accessName: string;
|
||||
|
@ -42,6 +47,7 @@ export type ProductType = {
|
|||
protocolName: string;
|
||||
state: number;
|
||||
transportProtocol: string;
|
||||
configuration: configuration;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -50,9 +50,9 @@
|
|||
<template #card="slotProps">
|
||||
<CardBox
|
||||
:showStatus="false"
|
||||
:statusNames="{}"
|
||||
:value="slotProps"
|
||||
:actions="getActions(slotProps, 'card')"
|
||||
v-bind="slotProps"
|
||||
>
|
||||
<template #img>
|
||||
<slot name="img">
|
||||
|
|
|
@ -56,7 +56,7 @@
|
|||
:showStatus="false"
|
||||
:value="slotProps"
|
||||
:actions="getActions(slotProps, 'card')"
|
||||
v-bind="slotProps"
|
||||
:statusNames="{}"
|
||||
>
|
||||
<template #img>
|
||||
<slot name="img">
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
<template>
|
||||
<a-modal
|
||||
<j-modal
|
||||
title='触发规则'
|
||||
visible
|
||||
:width='820'
|
||||
@click='save'
|
||||
@cancel='cancel'
|
||||
>
|
||||
<a-steps :current='addModel.stepNumber' @change='stepChange'>
|
||||
<a-step>
|
||||
<j-steps :current='addModel.stepNumber' @change='stepChange'>
|
||||
<j-step>
|
||||
<template #title>选择产品</template>
|
||||
</a-step>
|
||||
<a-step>
|
||||
</j-step>
|
||||
<j-step>
|
||||
<template #title>选择设备</template>
|
||||
</a-step>
|
||||
<a-step>
|
||||
</j-step>
|
||||
<j-step>
|
||||
<template #title>触发类型</template>
|
||||
</a-step>
|
||||
</a-steps>
|
||||
<a-divider style='margin-bottom: 0px' />
|
||||
</j-step>
|
||||
</j-steps>
|
||||
<j-divider style='margin-bottom: 0px' />
|
||||
<div class='steps-content'>
|
||||
<Product
|
||||
v-if='addModel.stepNumber === 0'
|
||||
|
@ -42,13 +42,13 @@
|
|||
</div>
|
||||
<template #footer>
|
||||
<div class='steps-action'>
|
||||
<a-button v-if='addModel.stepNumber === 0' @click='cancel'>取消</a-button>
|
||||
<a-button v-else @click='prev'>上一步</a-button>
|
||||
<a-button type='primary' v-if='addModel.stepNumber < 2' @click='saveClick'>下一步</a-button>
|
||||
<a-button type='primary' v-else @click='saveClick'>确定</a-button>
|
||||
<j-button v-if='addModel.stepNumber === 0' @click='cancel'>取消</j-button>
|
||||
<j-button v-else @click='prev'>上一步</j-button>
|
||||
<j-button type='primary' v-if='addModel.stepNumber < 2' @click='saveClick'>下一步</j-button>
|
||||
<j-button type='primary' v-else @click='saveClick'>确定</j-button>
|
||||
</div>
|
||||
</template>
|
||||
</a-modal>
|
||||
</j-modal>
|
||||
</template>
|
||||
|
||||
<script setup lang='ts' name='AddModel'>
|
||||
|
@ -59,7 +59,7 @@ import { detail as deviceDetail } from '@/api/device/instance'
|
|||
import Product from './Product.vue'
|
||||
import DeviceSelect from './DeviceSelect.vue'
|
||||
import Type from './Type.vue'
|
||||
import { continuousValue, timeUnitEnum } from '@/views/rule-engine/Scene/Save/components/Timer/util'
|
||||
import {continuousValue, handleTimerOptions, timeUnitEnum} from '@/views/rule-engine/Scene/Save/components/Timer/util'
|
||||
|
||||
type Emit = {
|
||||
(e: 'cancel'): void
|
||||
|
@ -161,46 +161,50 @@ const handleOptions = (data: TriggerDeviceOptions) => {
|
|||
|
||||
if (data.timer) {
|
||||
const _timer = data.timer;
|
||||
if (_timer.trigger === 'cron') {
|
||||
_options.time = _timer.cron;
|
||||
} else {
|
||||
// console.log('continuousValue', continuousValue(_timer.when! || [], _timer!.trigger))
|
||||
let whenStr = '每天';
|
||||
if (_timer.when!.length) {
|
||||
whenStr = _timer!.trigger === 'week' ? '每周' : '每月';
|
||||
const whenStrArr = continuousValue(_timer.when! || [], _timer!.trigger);
|
||||
const whenStrArr3 = whenStrArr.splice(0, 3);
|
||||
whenStr += whenStrArr3.join('、');
|
||||
whenStr += `等${_timer.when!.length}天`;
|
||||
}
|
||||
_options.when = whenStr;
|
||||
if (_timer.once) {
|
||||
_options.time = _timer.once.time + ' 执行1次';
|
||||
} else if (_timer.period) {
|
||||
_options.time = _timer.period.from + '-' + _timer.period.to;
|
||||
_options.extraTime = `每${_timer.period.every}${timeUnitEnum[_timer.period.unit]}执行1次`;
|
||||
}
|
||||
}
|
||||
|
||||
if (data.operator === 'online') {
|
||||
_options.type = '上线';
|
||||
_options.action = '';
|
||||
_options.typeIcon = 'icon-a-Group4713';
|
||||
}
|
||||
|
||||
if (data.operator === 'offline') {
|
||||
_options.type = '离线';
|
||||
_options.action = '';
|
||||
_options.typeIcon = 'icon-a-Group4892';
|
||||
}
|
||||
|
||||
if (data.operator === 'reportProperty') {
|
||||
_options.type = '属性上报';
|
||||
_options.action = '';
|
||||
_options.typeIcon = 'icon-file-upload-outline';
|
||||
}
|
||||
return _options;
|
||||
const { time, extraTime, when } = handleTimerOptions(_timer)
|
||||
_options.when = when;
|
||||
_options.time = time;
|
||||
_options.extraTime = extraTime;
|
||||
// if (_timer.trigger === 'cron') {
|
||||
// _options.time = _timer.cron;
|
||||
// } else {
|
||||
// // console.log('continuousValue', continuousValue(_timer.when! || [], _timer!.trigger))
|
||||
// let whenStr = '每天';
|
||||
// if (_timer.when!.length) {
|
||||
// whenStr = _timer!.trigger === 'week' ? '每周' : '每月';
|
||||
// const whenStrArr = continuousValue(_timer.when! || [], _timer!.trigger);
|
||||
// const whenStrArr3 = whenStrArr.splice(0, 3);
|
||||
// whenStr += whenStrArr3.join('、');
|
||||
// whenStr += `等${_timer.when!.length}天`;
|
||||
// }
|
||||
// _options.when = whenStr;
|
||||
// if (_timer.once) {
|
||||
// _options.time = _timer.once.time + ' 执行1次';
|
||||
// } else if (_timer.period) {
|
||||
// _options.time = _timer.period.from + '-' + _timer.period.to;
|
||||
// _options.extraTime = `每${_timer.period.every}${timeUnitEnum[_timer.period.unit]}执行1次`;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
if (data.operator === 'online') {
|
||||
_options.type = '上线';
|
||||
_options.action = '';
|
||||
_options.typeIcon = 'icon-a-Group4713';
|
||||
}
|
||||
|
||||
if (data.operator === 'offline') {
|
||||
_options.type = '离线';
|
||||
_options.action = '';
|
||||
_options.typeIcon = 'icon-a-Group4892';
|
||||
}
|
||||
|
||||
if (data.operator === 'reportProperty') {
|
||||
_options.type = '属性上报';
|
||||
_options.action = '';
|
||||
_options.typeIcon = 'icon-file-upload-outline';
|
||||
}
|
||||
return _options;
|
||||
}
|
||||
|
||||
const prev = () => {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
class='search'
|
||||
target="scene-triggrt-device-device"
|
||||
/>
|
||||
<a-divider style='margin: 0' />
|
||||
<j-divider style='margin: 0' />
|
||||
<j-pro-table
|
||||
ref='actionRef'
|
||||
model='CARD'
|
||||
|
@ -43,20 +43,20 @@
|
|||
{{ slotProps.name }}
|
||||
</span>
|
||||
</Ellipsis>
|
||||
<a-row>
|
||||
<a-col :span="12">
|
||||
<j-row>
|
||||
<j-col :span="12">
|
||||
<div class="card-item-content-text">
|
||||
设备类型
|
||||
</div>
|
||||
<div>{{ slotProps.deviceType?.text }}</div>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
</j-col>
|
||||
<j-col :span="12">
|
||||
<div class="card-item-content-text">
|
||||
产品名称
|
||||
</div>
|
||||
<div>{{ slotProps.productName }}</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</template>
|
||||
</CardBox>
|
||||
</template>
|
||||
|
@ -172,7 +172,6 @@ const handleClick = (detail: any) => {
|
|||
value: detail.id
|
||||
})
|
||||
}
|
||||
console.log('cloneRowKeys', cloneRowKeys)
|
||||
emit('update', cloneRowKeys)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
<template>
|
||||
<a-form ref='invokeForm' :model='formModel' layout='vertical' :colon='false'>
|
||||
<a-row :gutter='24'>
|
||||
<a-col :span='10'>
|
||||
<a-form-item
|
||||
<j-form ref='invokeForm' :model='formModel' layout='vertical' :colon='false'>
|
||||
<j-row :gutter='24'>
|
||||
<j-col :span='10'>
|
||||
<j-form-item
|
||||
name='functionId'
|
||||
:rules="[{ required: true, message: '请选择功能' }]"
|
||||
>
|
||||
<a-select
|
||||
<j-select
|
||||
showSearch
|
||||
allowClear
|
||||
v-model:value='formModel.functionId'
|
||||
|
@ -16,20 +16,20 @@
|
|||
:filterOption='filterSelectNode'
|
||||
@select='onSelect'
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span='14'>
|
||||
<a-form-item>定时调用所选功能</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span='24'>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col :span='14'>
|
||||
<j-form-item>定时调用所选功能</j-form-item>
|
||||
</j-col>
|
||||
<j-col :span='24'>
|
||||
<FunctionCall
|
||||
:value='_value'
|
||||
:data='functionData'
|
||||
@change='callDataChange'
|
||||
/>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</j-form>
|
||||
</template>
|
||||
|
||||
<script setup lang='ts' name='InvokeFunction'>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
class='search'
|
||||
target="scene-triggrt-device-category"
|
||||
/>
|
||||
<a-divider style='margin: 0' />
|
||||
<j-divider style='margin: 0' />
|
||||
<j-pro-table
|
||||
ref="instanceRef"
|
||||
model='TABLE'
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
class='search'
|
||||
target="scene-triggrt-device-device"
|
||||
/>
|
||||
<a-divider style='margin: 0' />
|
||||
<j-divider style='margin: 0' />
|
||||
<j-pro-table
|
||||
ref='actionRef'
|
||||
model='CARD'
|
||||
|
@ -42,14 +42,14 @@
|
|||
</span>
|
||||
</Ellipsis>
|
||||
</div>
|
||||
<a-row>
|
||||
<a-col :span="12">
|
||||
<j-row>
|
||||
<j-col :span="12">
|
||||
<div class="card-item-content-text">
|
||||
设备类型
|
||||
</div>
|
||||
<div>直连设备</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</template>
|
||||
</CardBox>
|
||||
</template>
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
<template>
|
||||
<a-row :gutter='[24]'>
|
||||
<a-col :span='10'>
|
||||
<a-form-item
|
||||
<j-row :gutter='[24]'>
|
||||
<j-col :span='10'>
|
||||
<j-form-item
|
||||
name='readProperties'
|
||||
:rules="[{ required: true, message: '请选择属性' }]"
|
||||
>
|
||||
<a-select
|
||||
<j-select
|
||||
show-search
|
||||
mode='multiple'
|
||||
max-tag-count='responsive'
|
||||
|
@ -16,12 +16,12 @@
|
|||
:filter-option='filterSelectNode'
|
||||
@change='change'
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span='14'>
|
||||
<a-form-item>定时读取所选属性值</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col :span='14'>
|
||||
<j-form-item>定时读取所选属性值</j-form-item>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</template>
|
||||
|
||||
<script setup lang='ts' name='ReadProperties'>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div class='type'>
|
||||
<a-form ref='typeForm' :model='formModel' layout='vertical' :colon='false'>
|
||||
<a-form-item
|
||||
<j-form ref='typeForm' :model='formModel' layout='vertical' :colon='false'>
|
||||
<j-form-item
|
||||
required
|
||||
label='触发类型'
|
||||
>
|
||||
|
@ -10,7 +10,7 @@
|
|||
:options='topOptions'
|
||||
v-model:value='formModel.operator'
|
||||
/>
|
||||
</a-form-item>
|
||||
</j-form-item>
|
||||
<template v-if='showTimer'>
|
||||
<Timer ref='timerRef' v-model:value='formModel.timer' />
|
||||
</template>
|
||||
|
@ -27,12 +27,12 @@
|
|||
v-model:action='optionCache.action'
|
||||
:properties='writeProperties'
|
||||
/>
|
||||
<a-form-item
|
||||
<j-form-item
|
||||
v-if='showReportEvent'
|
||||
name='eventId'
|
||||
:rules="[{ required: true, message: '请选择事件' }]"
|
||||
>
|
||||
<a-select
|
||||
<j-select
|
||||
v-model:value='formModel.eventId'
|
||||
:filter-option='filterSelectNode'
|
||||
:options='eventOptions'
|
||||
|
@ -40,7 +40,7 @@
|
|||
style='width: 100%'
|
||||
@select='eventSelect'
|
||||
/>
|
||||
</a-form-item>
|
||||
</j-form-item>
|
||||
<template v-if='showInvokeFunction'>
|
||||
<InvokeFunction
|
||||
ref='invokeRef'
|
||||
|
@ -49,7 +49,7 @@
|
|||
:functions='functionOptions'
|
||||
/>
|
||||
</template>
|
||||
</a-form>
|
||||
</j-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
<template>
|
||||
<a-form ref='writeForm' :model='formModel' layout='vertical' :colon='false'>
|
||||
<a-row :futter='[24, 24]'>
|
||||
<a-col :span='10'>
|
||||
<a-form-item
|
||||
<j-form ref='writeForm' :model='formModel' layout='vertical' :colon='false'>
|
||||
<j-row :futter='[24, 24]'>
|
||||
<j-col :span='10'>
|
||||
<j-form-item
|
||||
name='reportKey'
|
||||
:rules="[{ required: true, message: '请输入修改值' }]"
|
||||
>
|
||||
<a-select
|
||||
<j-select
|
||||
showSearch
|
||||
style='width: 100%'
|
||||
placeholder='请选择属性'
|
||||
|
@ -15,22 +15,22 @@
|
|||
:filter-option='filterSelectNode'
|
||||
@change='change'
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span='14'>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col :span='14'>
|
||||
<span style='line-height: 32px;padding-left: 24px'>
|
||||
定时调用所选属性
|
||||
</span>
|
||||
</a-col>
|
||||
<a-col :span='24' v-if='showTable'>
|
||||
</j-col>
|
||||
<j-col :span='24' v-if='showTable'>
|
||||
<FunctionCall
|
||||
:value='_value'
|
||||
:data='callDataOptions'
|
||||
@change='callDataChange'
|
||||
/>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</j-form>
|
||||
</template>
|
||||
|
||||
<script setup lang='ts' name='WriteProperties'>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class='device'>
|
||||
<a-form-item
|
||||
<j-form-item
|
||||
:rules='rules'
|
||||
name='device'
|
||||
>
|
||||
|
@ -13,7 +13,7 @@
|
|||
>
|
||||
<Title :options='data.options.trigger' />
|
||||
</AddButton>
|
||||
</a-form-item>
|
||||
</j-form-item>
|
||||
<Terms />
|
||||
<AddModel v-if='visible' @cancel='visible = false' @save='save' :value='data.trigger.device' :options='data.options.trigger' />
|
||||
</div>
|
||||
|
@ -25,7 +25,6 @@ import { useSceneStore } from '@/store/scene'
|
|||
import AddModel from './AddModal.vue'
|
||||
import AddButton from '../components/AddButton.vue'
|
||||
import Title from '../components/Title.vue'
|
||||
import Action from '../action/index.vue'
|
||||
import Terms from '../components/Terms'
|
||||
import type { TriggerDevice } from '@/views/rule-engine/Scene/typings'
|
||||
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
<template>
|
||||
<j-modal
|
||||
title='触发规则'
|
||||
visible
|
||||
:width='820'
|
||||
@click='save'
|
||||
@cancel='cancel'
|
||||
>
|
||||
<Timer
|
||||
ref='timerRef' v-model:value='addModel.timer'
|
||||
/>
|
||||
</j-modal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="timerAddModel">
|
||||
import Timer from '../components/Timer'
|
||||
import type { OperationTimer } from '@/views/rule-engine/Scene/typings'
|
||||
import {nextTick, PropType} from "vue";
|
||||
import {TriggerDevice} from "@/views/rule-engine/Scene/typings";
|
||||
import {handleTimerOptions} from "@/views/rule-engine/Scene/Save/components/Timer/util";
|
||||
|
||||
type Emit = {
|
||||
(e: 'cancel'): void
|
||||
(e: 'save', data: TriggerDevice, options: Record<string, any>): void
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
timer: {
|
||||
type: Object as PropType<OperationTimer>,
|
||||
default: () => ({})
|
||||
}
|
||||
})
|
||||
const emit = defineEmits<Emit>()
|
||||
|
||||
const timerRef = ref()
|
||||
|
||||
interface AddModelType {
|
||||
timer: OperationTimer
|
||||
}
|
||||
|
||||
const addModel = reactive<AddModelType>({
|
||||
timer: props.timer
|
||||
})
|
||||
|
||||
const save = async () => {
|
||||
const timerData = await timerRef.value?.validateFields()
|
||||
if (timerData) {
|
||||
const options = handleTimerOptions(timerData)
|
||||
emit("save", timerData, options)
|
||||
}
|
||||
}
|
||||
|
||||
const cancel = () => {
|
||||
emit("cancel")
|
||||
}
|
||||
|
||||
nextTick(() => {
|
||||
Object.assign(addModel, props.timer)
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -1,28 +1,67 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class='timer'>
|
||||
<j-form-item
|
||||
:rules="rules"
|
||||
name="timer"
|
||||
>
|
||||
<template #label>
|
||||
<TitleComponent data='触发规则' style='font-size: 14px;' />
|
||||
</template>
|
||||
<AddButton
|
||||
style='width: 100%'
|
||||
@click='visible = true'
|
||||
>
|
||||
<Title :options='data.options.trigger' />
|
||||
</AddButton>
|
||||
</j-form-item>
|
||||
<j-form-item
|
||||
:rules="actionRules"
|
||||
:name="['branches', 0, 'then']"
|
||||
>
|
||||
<Action
|
||||
:thenOptions="data.branches ? data?.branches[0].then : []"
|
||||
:name="0"
|
||||
@add="onActionAdd"
|
||||
@update="onActionUpdate"
|
||||
/>
|
||||
</j-form-item>
|
||||
<AddModel
|
||||
v-if="visible"
|
||||
@cancel='visible = false'
|
||||
@save="save"
|
||||
:value="data.trigger.device"
|
||||
:options="data.options.trigger"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useSceneStore } from '@/store/scene';
|
||||
import Action from '../action/index.vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import Action from '../action/index.vue';
|
||||
import AddModel from './AddModal.vue'
|
||||
import AddButton from '../components/AddButton.vue'
|
||||
import type { OperationTimer } from '@/views/rule-engine/Scene/typings'
|
||||
|
||||
const sceneStore = useSceneStore();
|
||||
const { data } = storeToRefs(sceneStore);
|
||||
const visible = ref(false)
|
||||
|
||||
const onActionAdd = (_data: any) => {
|
||||
if (data.value?.branches && _data) {
|
||||
const newThen = [...data.value.branches[0].then, _data];
|
||||
data.value.branches![0].then = newThen
|
||||
const rules = [{
|
||||
validator(_: any, v: any) {
|
||||
if (!v) {
|
||||
return Promise.reject(new Error('请配置定时触发规则'));
|
||||
}
|
||||
};
|
||||
return Promise.resolve();
|
||||
},
|
||||
}]
|
||||
|
||||
const actionRules = [{
|
||||
validator(_, v) {
|
||||
if (!v || (v && !v.length)) {
|
||||
return Promise.reject('至少配置一个执行动作');
|
||||
}
|
||||
return Promise.resolve();
|
||||
},
|
||||
}]
|
||||
|
||||
const onActionUpdate = (_data: any, type: boolean) => {
|
||||
const indexOf = data.value.branches![0].then.findIndex(
|
||||
|
@ -36,6 +75,11 @@ const onActionUpdate = (_data: any, type: boolean) => {
|
|||
}
|
||||
}
|
||||
};
|
||||
|
||||
const save = (data: OperationTimer, options: Record<string, any>) => {
|
||||
data.value.trigger!.timer = data
|
||||
data.value.options!.trigger = options
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
</template>
|
||||
<template v-if='column.dataIndex === "type"'>
|
||||
{{ record.type }}
|
||||
<a-tooltip
|
||||
<j-tooltip
|
||||
v-if="record.type === 'object'"
|
||||
>
|
||||
<template slot="title">
|
||||
|
@ -26,7 +26,7 @@
|
|||
cursor: 'help',
|
||||
}"
|
||||
/>
|
||||
</a-tooltip>
|
||||
</j-tooltip>
|
||||
</template>
|
||||
<template v-if='column.dataIndex === "value"'>
|
||||
<ValueItem
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
<template>
|
||||
<a-form
|
||||
<j-form
|
||||
ref='timerForm'
|
||||
:model='formModel'
|
||||
layout='vertical'
|
||||
:colon='false'
|
||||
>
|
||||
<a-form-item name='trigger'>
|
||||
<a-radio-group
|
||||
<j-form-item name='trigger'>
|
||||
<j-radio-group
|
||||
v-model:value='formModel.trigger'
|
||||
:options='[
|
||||
{ label: "按周", value: "week" },
|
||||
|
@ -16,8 +16,8 @@
|
|||
option-type='button'
|
||||
button-style='solid'
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item v-if='showCron' name='cron' :rules="[
|
||||
</j-form-item>
|
||||
<j-form-item v-if='showCron' name='cron' :rules="[
|
||||
{ max: 64, message: '最多可输入64个字符' },
|
||||
{
|
||||
validator: async (_, v) => {
|
||||
|
@ -32,14 +32,14 @@
|
|||
}
|
||||
}
|
||||
]">
|
||||
<a-input placeholder='corn表达式' v-model:value='formModel.cron' />
|
||||
</a-form-item>
|
||||
<j-input placeholder='corn表达式' v-model:value='formModel.cron' />
|
||||
</j-form-item>
|
||||
<template v-else>
|
||||
<a-form-item name='when'>
|
||||
<j-form-item name='when'>
|
||||
<WhenOption v-model:value='formModel.when' :type='formModel.trigger' />
|
||||
</a-form-item>
|
||||
<a-form-item name='mod'>
|
||||
<a-radio-group
|
||||
</j-form-item>
|
||||
<j-form-item name='mod'>
|
||||
<j-radio-group
|
||||
v-model:value='formModel.mod'
|
||||
:options='[
|
||||
{ label: "周期执行", value: "period" },
|
||||
|
@ -48,18 +48,18 @@
|
|||
option-type='button'
|
||||
button-style='solid'
|
||||
/>
|
||||
</a-form-item>
|
||||
</j-form-item>
|
||||
</template>
|
||||
<a-space v-if='showOnce' style='display: flex;gap: 24px'>
|
||||
<a-form-item :name="['once', 'time']">
|
||||
<a-time-picker valueFormat='HH:mm:ss' v-model:value='formModel.once.time' style='width: 100%'
|
||||
<j-space v-if='showOnce' style='display: flex;gap: 24px'>
|
||||
<j-form-item :name="['once', 'time']">
|
||||
<j-time-picker valueFormat='HH:mm:ss' v-model:value='formModel.once.time' style='width: 100%'
|
||||
format='HH:mm:ss' />
|
||||
</a-form-item>
|
||||
<a-form-item> 执行一次</a-form-item>
|
||||
</a-space>
|
||||
<a-space v-if='showPeriod' style='display: flex;gap: 24px'>
|
||||
<a-form-item>
|
||||
<a-time-range-picker
|
||||
</j-form-item>
|
||||
<j-form-item> 执行一次</j-form-item>
|
||||
</j-space>
|
||||
<j-space v-if='showPeriod' style='display: flex;gap: 24px'>
|
||||
<j-form-item>
|
||||
<j-time-range-picker
|
||||
valueFormat='HH:mm:ss'
|
||||
:value='[
|
||||
formModel.period.from,
|
||||
|
@ -70,13 +70,13 @@
|
|||
formModel.period.to = v[1]
|
||||
}'
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item>每</a-form-item>
|
||||
<a-form-item
|
||||
</j-form-item>
|
||||
<j-form-item>每</j-form-item>
|
||||
<j-form-item
|
||||
:name='["period", "every"]'
|
||||
:rules='[{ required: true, message: "请输入时间" }]'
|
||||
>
|
||||
<a-input-number
|
||||
<j-input-number
|
||||
placeholder='请输入时间'
|
||||
style='max-width: 170px'
|
||||
:precision='0'
|
||||
|
@ -85,7 +85,7 @@
|
|||
v-model:value='formModel.period.every'
|
||||
>
|
||||
<template #addonAfter>
|
||||
<a-select
|
||||
<j-select
|
||||
v-model:value='formModel.period.unit'
|
||||
:options='[
|
||||
{ label: "秒", value: "seconds" },
|
||||
|
@ -94,11 +94,11 @@
|
|||
]'
|
||||
/>
|
||||
</template>
|
||||
</a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item>执行一次</a-form-item>
|
||||
</a-space>
|
||||
</a-form>
|
||||
</j-input-number>
|
||||
</j-form-item>
|
||||
<j-form-item>执行一次</j-form-item>
|
||||
</j-space>
|
||||
</j-form>
|
||||
</template>
|
||||
|
||||
<script setup lang='ts' name='Timer'>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { isArray } from 'lodash-es'
|
||||
import type { OperationTimer } from "@/views/rule-engine/Scene/typings";
|
||||
export const numberToString = {
|
||||
1: '星期一',
|
||||
2: '星期二',
|
||||
|
@ -49,4 +50,42 @@ export const continuousValue: continuousValueFn = (data, type) => {
|
|||
});
|
||||
}
|
||||
return newArray;
|
||||
};
|
||||
};
|
||||
|
||||
type TimerOption = {
|
||||
when?: string
|
||||
time?: string
|
||||
extraTime?: string
|
||||
}
|
||||
|
||||
export const handleTimerOptions = (timer: OperationTimer):TimerOption => {
|
||||
let when = '每天'
|
||||
let time = undefined
|
||||
let extraTime = undefined
|
||||
|
||||
if (timer.trigger === 'cron') {
|
||||
time = timer.cron
|
||||
return { time }
|
||||
}
|
||||
|
||||
if (timer.when?.length) {
|
||||
when = timer!.trigger === 'week' ? '每周' : '每月';
|
||||
const whenStrArr = continuousValue(timer.when! || [], timer!.trigger);
|
||||
const whenStrArr3 = whenStrArr.splice(0, 3);
|
||||
when += whenStrArr3.join('、');
|
||||
when += `等${timer.when!.length}天`;
|
||||
}
|
||||
|
||||
if (timer.once) {
|
||||
time = timer.once.time + ' 执行1次';
|
||||
} else if (timer.period) {
|
||||
time = timer.period.from + '-' + timer.period.to;
|
||||
extraTime = `每${timer.period.every}${timeUnitEnum[timer.period.unit]}执行1次`;
|
||||
}
|
||||
|
||||
return {
|
||||
when,
|
||||
time,
|
||||
extraTime
|
||||
}
|
||||
}
|
|
@ -12,9 +12,9 @@
|
|||
>
|
||||
<div class='way-item-title'>
|
||||
<span class='label'>{{ item.label }}</span>
|
||||
<a-popover v-if='item.tip' :content='item.tip'>
|
||||
<j-popover v-if='item.tip' :content='item.tip'>
|
||||
<AIcon type='QuestionCircleOutlined' class='icon' />
|
||||
</a-popover>
|
||||
</j-popover>
|
||||
</div>
|
||||
<div class='way-item-image'>
|
||||
<img
|
||||
|
|
|
@ -10,11 +10,20 @@
|
|||
{{ keyByLabel[data.triggerType] }}
|
||||
</div>
|
||||
</div>
|
||||
<a-form ref='sceneForm' :model='data' :colon='false' layout='vertical'>
|
||||
<j-form ref='sceneForm' :model='data' :colon='false' layout='vertical'>
|
||||
<Device v-if='data.triggerType === "device"' />
|
||||
<Manual v-else-if='data.triggerType === "manual"' />
|
||||
<Timer v-else-if='data.triggerType === "timer"' />
|
||||
</a-form>
|
||||
<j-form-item>
|
||||
<j-textarea
|
||||
v-model:value="data.description"
|
||||
placeholder=''
|
||||
:rows="4"
|
||||
show-count
|
||||
:maxLength="200"
|
||||
/>
|
||||
</j-form-item>
|
||||
</j-form>
|
||||
<PermissionButton
|
||||
type='primary'
|
||||
hasPermission='rule-engine/Scene:update'
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<a-modal
|
||||
<j-modal
|
||||
visible
|
||||
:title='title'
|
||||
:width='750'
|
||||
|
@ -8,13 +8,13 @@
|
|||
@cancel='emit("close")'
|
||||
@ok='handleOk'
|
||||
>
|
||||
<a-form
|
||||
<j-form
|
||||
layout='vertical'
|
||||
name='scene-save'
|
||||
ref="formRef"
|
||||
:model='formModel'
|
||||
>
|
||||
<a-form-item
|
||||
<j-form-item
|
||||
name='name'
|
||||
label='名称'
|
||||
:rules="[
|
||||
|
@ -22,17 +22,17 @@
|
|||
{ max: 64, message: '最多输入64个字符' }
|
||||
]"
|
||||
>
|
||||
<a-input v-model:value='formModel.name' placeholder='请输入名称' />
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
<j-input v-model:value='formModel.name' placeholder='请输入名称' />
|
||||
</j-form-item>
|
||||
<j-form-item
|
||||
:name='["trigger", "type"]'
|
||||
label='触发方式'
|
||||
:rules="[{ required: true, message: '请选择触发方式' }]"
|
||||
>
|
||||
<TriggerWay v-model:modelValue='formModel.trigger.type' :disabled='disabled' />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</j-form-item>
|
||||
</j-form>
|
||||
</j-modal>
|
||||
</template>
|
||||
|
||||
<script setup lang='ts'>
|
||||
|
|
|
@ -46,4 +46,4 @@ export const useParams = (params: Params) => {
|
|||
return {
|
||||
columnOptions
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -290,7 +290,7 @@ import { message } from 'ant-design-vue';
|
|||
import { BASE_API_PATH, TOKEN_KEY } from '@/utils/variable';
|
||||
import { LocalStore } from '@/utils/comm';
|
||||
|
||||
import { save_api, getDetails_api } from '@/api/system/basis';
|
||||
import { save_api } from '@/api/system/basis';
|
||||
import { usePermissionStore } from '@/store/permission';
|
||||
import { useSystem } from '@/store/system';
|
||||
|
||||
|
|
|
@ -21,56 +21,59 @@
|
|||
</PermissionButton>
|
||||
</div>
|
||||
|
||||
<jTree
|
||||
:tree-data="treeData"
|
||||
v-model:selected-keys="selectedKeys"
|
||||
:fieldNames="{ key: 'id' }"
|
||||
v-loading="loading"
|
||||
>
|
||||
<template #title="{ name, data }">
|
||||
<span>{{ name }}</span>
|
||||
<span class="func-btns" @click="(e) => e.stopPropagation()">
|
||||
<PermissionButton
|
||||
:hasPermission="`${permission}:update`"
|
||||
type="link"
|
||||
:tooltip="{
|
||||
title: '编辑',
|
||||
}"
|
||||
@click="openDialog(data)"
|
||||
>
|
||||
<AIcon type="EditOutlined" />
|
||||
</PermissionButton>
|
||||
<PermissionButton
|
||||
:hasPermission="`${permission}:add`"
|
||||
type="link"
|
||||
:tooltip="{
|
||||
title: '新增子组织',
|
||||
}"
|
||||
@click="
|
||||
openDialog({
|
||||
...data,
|
||||
id: '',
|
||||
parentId: data.id,
|
||||
})
|
||||
"
|
||||
>
|
||||
<AIcon type="PlusCircleOutlined" />
|
||||
</PermissionButton>
|
||||
<PermissionButton
|
||||
type="link"
|
||||
:hasPermission="`${permission}:delete`"
|
||||
:tooltip="{ title: '删除' }"
|
||||
:popConfirm="{
|
||||
title: `确定要删除吗`,
|
||||
onConfirm: () => delDepartment(data.id),
|
||||
}"
|
||||
>
|
||||
<AIcon type="DeleteOutlined" />
|
||||
</PermissionButton>
|
||||
</span>
|
||||
</template>
|
||||
</jTree>
|
||||
|
||||
<div class="tree">
|
||||
<jTree
|
||||
v-if="treeData.length > 0"
|
||||
:tree-data="treeData"
|
||||
v-model:selected-keys="selectedKeys"
|
||||
:fieldNames="{ key: 'id' }"
|
||||
>
|
||||
<template #title="{ name, data }">
|
||||
<span>{{ name }}</span>
|
||||
<span class="func-btns" @click="(e) => e.stopPropagation()">
|
||||
<PermissionButton
|
||||
:hasPermission="`${permission}:update`"
|
||||
type="link"
|
||||
:tooltip="{
|
||||
title: '编辑',
|
||||
}"
|
||||
@click="openDialog(data)"
|
||||
>
|
||||
<AIcon type="EditOutlined" />
|
||||
</PermissionButton>
|
||||
<PermissionButton
|
||||
:hasPermission="`${permission}:add`"
|
||||
type="link"
|
||||
:tooltip="{
|
||||
title: '新增子组织',
|
||||
}"
|
||||
@click="
|
||||
openDialog({
|
||||
...data,
|
||||
id: '',
|
||||
parentId: data.id,
|
||||
})
|
||||
"
|
||||
>
|
||||
<AIcon type="PlusCircleOutlined" />
|
||||
</PermissionButton>
|
||||
<PermissionButton
|
||||
type="link"
|
||||
:hasPermission="`${permission}:delete`"
|
||||
:tooltip="{ title: '删除' }"
|
||||
:popConfirm="{
|
||||
title: `确定要删除吗`,
|
||||
onConfirm: () => delDepartment(data.id),
|
||||
}"
|
||||
>
|
||||
<AIcon type="DeleteOutlined" />
|
||||
</PermissionButton>
|
||||
</span>
|
||||
</template>
|
||||
</jTree>
|
||||
<div class="loading" v-else-if="loading"><j-spin /></div>
|
||||
<j-empty v-else description="暂无数据" />
|
||||
</div>
|
||||
<!-- 编辑弹窗 -->
|
||||
<EditDepartmentDialog
|
||||
v-if="dialog.visible"
|
||||
|
@ -193,9 +196,11 @@ const openDialog = (row: any = {}) => {
|
|||
if (row.parentId) {
|
||||
childrens = row.children;
|
||||
} else childrens = treeData.value;
|
||||
const indexs = childrens.length > 0 ? childrens?.map((item) => item.sortIndex) :[0]
|
||||
sortIndex =
|
||||
Math.max(...indexs) + 1;
|
||||
const indexs =
|
||||
childrens.length > 0
|
||||
? childrens?.map((item) => item.sortIndex)
|
||||
: [0];
|
||||
sortIndex = Math.max(...indexs) + 1;
|
||||
}
|
||||
|
||||
dialog.selectItem = { ...row, sortIndex };
|
||||
|
@ -248,5 +253,11 @@ function init() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.loading {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -91,6 +91,16 @@ import 'vue3-json-viewer/dist/index.css';
|
|||
import type { apiDetailsType } from '../typing';
|
||||
import InputCard from './InputCard.vue';
|
||||
import { PropType } from 'vue';
|
||||
import { findData, getCodeText } from '../utils';
|
||||
|
||||
type cardType = {
|
||||
columns: object[];
|
||||
tableData: any[];
|
||||
codeText?: any;
|
||||
activeKey?: string;
|
||||
getData: Function;
|
||||
};
|
||||
|
||||
|
||||
const props = defineProps({
|
||||
selectApi: {
|
||||
|
@ -104,14 +114,8 @@ const props = defineProps({
|
|||
});
|
||||
const { selectApi } = toRefs(props);
|
||||
|
||||
type tableCardType = {
|
||||
columns: object[];
|
||||
tableData: any[];
|
||||
codeText?: any;
|
||||
activeKey?: any;
|
||||
getData?: any;
|
||||
};
|
||||
const requestCard = reactive<tableCardType>({
|
||||
|
||||
const requestCard = reactive<cardType>({
|
||||
columns: [
|
||||
{
|
||||
title: '参数名',
|
||||
|
@ -149,16 +153,16 @@ const requestCard = reactive<tableCardType>({
|
|||
const schema =
|
||||
props.selectApi.requestBody.content['application/json'].schema;
|
||||
const _ref = schema.$ref || schema?.items?.$ref;
|
||||
if(!_ref) return; // schema不是Java中的类的话则不进行解析,直接结束
|
||||
if (!_ref) return; // schema不是Java中的类的话则不进行解析,直接结束
|
||||
|
||||
const schemaName = _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);
|
||||
const tableData = findData(props.schemas, schemaName);
|
||||
|
||||
requestCard.codeText =
|
||||
type === 'array'
|
||||
? [getCodeText(props.schemas, tableData, 3)]
|
||||
: getCodeText(props.schemas, tableData, 3);
|
||||
requestCard.tableData = [
|
||||
{
|
||||
name: schemaName[0].toLowerCase() + schemaName.substring(1),
|
||||
|
@ -176,7 +180,7 @@ const requestCard = reactive<tableCardType>({
|
|||
];
|
||||
},
|
||||
});
|
||||
const responseStatusCard = reactive<tableCardType>({
|
||||
const responseStatusCard = reactive<cardType>({
|
||||
activeKey: '',
|
||||
columns: [
|
||||
{
|
||||
|
@ -220,7 +224,7 @@ const tabs = computed(() =>
|
|||
.map((item: any) => item.code + '')
|
||||
.filter((code: string) => code !== '400'),
|
||||
);
|
||||
const respParamsCard = reactive<tableCardType>({
|
||||
const respParamsCard = reactive<cardType>({
|
||||
columns: [
|
||||
{
|
||||
title: '参数名称',
|
||||
|
@ -242,9 +246,8 @@ const respParamsCard = reactive<tableCardType>({
|
|||
(item: any) => item.code === code,
|
||||
)?.schema;
|
||||
|
||||
const tableData = findData(schemaName);
|
||||
respParamsCard.codeText = getCodeText(tableData, 3);
|
||||
|
||||
const tableData = findData(props.schemas, schemaName);
|
||||
respParamsCard.codeText = getCodeText(props.schemas, tableData, 3);
|
||||
respParamsCard.tableData = tableData;
|
||||
},
|
||||
});
|
||||
|
@ -258,95 +261,18 @@ const getContent = (data: any) => {
|
|||
onMounted(() => {
|
||||
requestCard.getData();
|
||||
responseStatusCard.getData();
|
||||
});
|
||||
watch(
|
||||
() => props.selectApi,
|
||||
() => {
|
||||
requestCard.getData();
|
||||
responseStatusCard.getData();
|
||||
},
|
||||
);
|
||||
watch(
|
||||
() => props.selectApi,
|
||||
() => {
|
||||
requestCard.getData();
|
||||
responseStatusCard.getData();
|
||||
},
|
||||
);
|
||||
|
||||
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);
|
||||
watch([() => responseStatusCard.activeKey, () => props.selectApi], (n) => {
|
||||
n[0] && respParamsCard.getData(n[0]);
|
||||
});
|
||||
|
||||
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>
|
||||
|
|
|
@ -121,6 +121,7 @@ import InputCard from './InputCard.vue';
|
|||
import { cloneDeep, toLower } from 'lodash';
|
||||
import { FormInstance } from 'ant-design-vue';
|
||||
import server from '@/utils/request';
|
||||
import { findData, getCodeText } from '../utils';
|
||||
|
||||
const props = defineProps<{
|
||||
selectApi: apiDetailsType;
|
||||
|
@ -238,92 +239,17 @@ function getDefaultParams() {
|
|||
if (!refStr.value) return ''; // schema不是Java中的类的话则不进行解析,直接结束
|
||||
const schemaName = refStr.value?.split('/').pop() as string;
|
||||
const type = schema.type || '';
|
||||
const tableData = findData(schemaName);
|
||||
if (type === 'array') {
|
||||
return [getCodeText(tableData, 3)];
|
||||
} else return getCodeText(tableData, 3);
|
||||
}
|
||||
const tableData = findData(props.schemas, schemaName);
|
||||
|
||||
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;
|
||||
return type === 'array'
|
||||
? [getCodeText(props.schemas, tableData, 3)]
|
||||
: getCodeText(props.schemas, tableData, 3);
|
||||
}
|
||||
|
||||
type requestObj = {
|
||||
name: string;
|
||||
value: string;
|
||||
};
|
||||
type schemaObjType = {
|
||||
paramsName: string;
|
||||
paramsType: string;
|
||||
desc?: string;
|
||||
children?: schemaObjType[];
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
|
|
@ -29,4 +29,11 @@ export type apiDetailsType = {
|
|||
* appManger: 应用管理-赋权
|
||||
* home:应用管理-查看菜单,第三方首页
|
||||
*/
|
||||
export type modeType = 'api'| 'appManger' | 'home'
|
||||
export type modeType = 'api'| 'appManger' | 'home'
|
||||
|
||||
export type schemaObjType = {
|
||||
paramsName: string;
|
||||
paramsType: string;
|
||||
desc?: string;
|
||||
children?: schemaObjType[];
|
||||
};
|
|
@ -0,0 +1,90 @@
|
|||
import { schemaObjType } from "./typing";
|
||||
|
||||
|
||||
/**
|
||||
* 将对应实体转换为表格数据返回
|
||||
* @param schemas 实体类map
|
||||
* @param schemaName 实体类名称
|
||||
*/
|
||||
export function findData(schemas: object, schemaName: string) {
|
||||
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(schemas, paramsType);
|
||||
result.push(schemaObj);
|
||||
});
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
/**
|
||||
* 将字段信息数组转换为JSON代码
|
||||
* @param schemas 对应的所有实体类
|
||||
* @param arr 字段信息数组
|
||||
* @param level 转换深度
|
||||
*/
|
||||
export function getCodeText(
|
||||
schemas: object,
|
||||
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(
|
||||
schemas,
|
||||
newArr,
|
||||
level - 1,
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
|
@ -92,7 +92,7 @@
|
|||
}"
|
||||
:popConfirm="{
|
||||
title: `确认删除`,
|
||||
onConfirm: () => table.clickDel(slotProps),
|
||||
onConfirm: () => table.clickDel(slotProps.id),
|
||||
}"
|
||||
:disabled="slotProps.status"
|
||||
>
|
||||
|
@ -223,10 +223,10 @@ const table = {
|
|||
dialog.type = type;
|
||||
dialog.visible = true;
|
||||
},
|
||||
changeStatus: (row: any) => {
|
||||
changeStatus: ({ id, status }: any) => {
|
||||
const params = {
|
||||
id: row.id,
|
||||
status: row.status === 0 ? 1 : 0,
|
||||
status: status === 0 ? 1 : 0,
|
||||
id,
|
||||
};
|
||||
changeUserStatus_api(params).then(() => {
|
||||
message.success('操作成功');
|
||||
|
@ -234,8 +234,8 @@ const table = {
|
|||
});
|
||||
},
|
||||
// 删除
|
||||
clickDel: (row: any) => {
|
||||
deleteUser_api(row.id).then(() => {
|
||||
clickDel: (id: string) => {
|
||||
deleteUser_api(id).then(() => {
|
||||
message.success('操作成功');
|
||||
table.refresh();
|
||||
});
|
||||
|
|
12
yarn.lock
12
yarn.lock
|
@ -3709,6 +3709,18 @@ jetlinks-ui-components@^1.0.4:
|
|||
lodash-es "^4.17.21"
|
||||
monaco-editor "^0.35.0"
|
||||
|
||||
jetlinks-ui-components@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "http://47.108.170.157:9013/jetlinks-ui-components/-/jetlinks-ui-components-1.0.5.tgz#0c4999d28c96c11ce266c5c9706cc895010450dc"
|
||||
integrity sha512-buCf4mWJ8cUyn+12nRRLIr25MwG60nxqWH4pZidKy/npNKt5WQXLV8PmHmf04z0xpJUnW5yY3C7QBkYoAkSgVw==
|
||||
dependencies:
|
||||
"@vueuse/core" "^9.12.0"
|
||||
ant-design-vue "^3.2.15"
|
||||
colorpicker-v3 "^2.10.2"
|
||||
jetlinks-ui-components "^1.0.4"
|
||||
lodash-es "^4.17.21"
|
||||
monaco-editor "^0.35.0"
|
||||
|
||||
js-cookie@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.1.tgz"
|
||||
|
|
Loading…
Reference in New Issue