fix: 视频中心仪表盘兼容数据处理;
* fix: bug#28509【设备】设备物模型属性类型为地理位置时,运行状态查看详情,选择图标展示筛选条件错误 * fix: bug#28513 【设备】设备详情界面,物联网卡和远程升级需做权限控制,当没有权限时,隐藏当前tab * fix: 视频中心仪表盘兼容数据处理 * chore: add dayjs * update: docker tag
This commit is contained in:
parent
984fd9c115
commit
3da54bb59b
4
build.sh
4
build.sh
|
@ -1,3 +1,3 @@
|
|||
#!/usr/bin/env bash
|
||||
docker build -t registry.cn-shenzhen.aliyuncs.com/jetlinks/jetlinks-ui-vue:2.2.0-TEST .
|
||||
docker push registry.cn-shenzhen.aliyuncs.com/jetlinks/jetlinks-ui-vue:2.2.0-TEST
|
||||
docker build -t registry.cn-shenzhen.aliyuncs.com/jetlinks/jetlinks-ui-vue:2.3.0-SNAPSHOT .
|
||||
docker push registry.cn-shenzhen.aliyuncs.com/jetlinks/jetlinks-ui-vue:2.3.0-SNAPSHOT
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "jetlinks-vue",
|
||||
"private": true,
|
||||
"version": "2.2.0",
|
||||
"version": "2.2.1",
|
||||
"scripts": {
|
||||
"dev": "vite --mode develop",
|
||||
"dev:force": "vite --force --mode develop",
|
||||
|
@ -29,6 +29,7 @@
|
|||
"axios": "^1.2.1",
|
||||
"colorpicker-v3": "^2.10.2",
|
||||
"cronstrue": "^2.50.0",
|
||||
"dayjs": "^1.11.12",
|
||||
"driver.js": "^0.9.8",
|
||||
"echarts": "^5.4.1",
|
||||
"event-source-polyfill": "^1.0.31",
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<j-pro-layout
|
||||
v-bind="layoutConf"
|
||||
v-model:collapsed="basicLayout.collapsed"
|
||||
v-model:openKeys="basicLayout.openKeys"
|
||||
:openKeys="basicLayout.collapsed ? [] : basicLayout.openKeys"
|
||||
:selectedKeys="basicLayout.selectedKeys"
|
||||
:breadcrumb="basicLayout.pure ? undefined : { routes: breadcrumbs }"
|
||||
:headerHeight='basicLayout.pure ? 1 : layout.headerHeight'
|
||||
|
|
|
@ -257,7 +257,10 @@ defineExpose({
|
|||
//position: absolute;
|
||||
transition: top .2s, height .2s, background-color .1s;
|
||||
border-bottom: 1px solid #ebebeb;
|
||||
|
||||
.metadata-edit-table-cell{
|
||||
position:absolute;
|
||||
min-width: 0;;
|
||||
}
|
||||
&:hover {
|
||||
background-color: rgb(248, 248, 248);
|
||||
}
|
||||
|
|
|
@ -233,7 +233,7 @@ watch(
|
|||
(val) => {
|
||||
const diffInSeconds = dayjs(val[1]).diff(dayjs(val[0]), 'minute');
|
||||
if (diffInSeconds < 60) {
|
||||
periodOptions.value = [
|
||||
periodOptions.value = _type.value ? [
|
||||
{
|
||||
label: '实际值',
|
||||
value: '*',
|
||||
|
@ -242,10 +242,15 @@ watch(
|
|||
label: '按分钟统计',
|
||||
value: '1m',
|
||||
},
|
||||
];
|
||||
cycle.value = '*';
|
||||
] : [
|
||||
{
|
||||
label: '按分钟统计',
|
||||
value: '1m',
|
||||
},
|
||||
]
|
||||
cycle.value = _type.value ? '*' : '1m';
|
||||
} else if (diffInSeconds < 1440) {
|
||||
periodOptions.value = [
|
||||
periodOptions.value = _type.value ? [
|
||||
{
|
||||
label: '实际值',
|
||||
value: '*',
|
||||
|
@ -258,8 +263,17 @@ watch(
|
|||
label: '按小时统计',
|
||||
value: '1h',
|
||||
},
|
||||
];
|
||||
cycle.value = '*';
|
||||
] : [
|
||||
{
|
||||
label: '按分钟统计',
|
||||
value: '1m',
|
||||
},
|
||||
{
|
||||
label: '按小时统计',
|
||||
value: '1h',
|
||||
},
|
||||
]
|
||||
cycle.value = _type.value ? '*' : '1m';
|
||||
} else if (diffInSeconds < 43200) {
|
||||
periodOptions.value = [
|
||||
{
|
||||
|
|
|
@ -170,14 +170,6 @@ const initList = [
|
|||
key: 'Log',
|
||||
tab: '日志管理',
|
||||
},
|
||||
{
|
||||
key: 'CardManagement',
|
||||
tab: '物联网卡',
|
||||
},
|
||||
{
|
||||
key: 'Firmware',
|
||||
tab: '远程升级',
|
||||
},
|
||||
];
|
||||
|
||||
const list = ref([...initList]);
|
||||
|
@ -226,6 +218,18 @@ const getDetail = () => {
|
|||
tab: '预处理数据',
|
||||
});
|
||||
}
|
||||
if (permissionStore.hasPermission('iot-card/CardManagement:view')) {
|
||||
list.value.push({
|
||||
key: 'CardManagement',
|
||||
tab: '物联网卡',
|
||||
});
|
||||
}
|
||||
if (permissionStore.hasPermission('device/Firmware:view')) {
|
||||
list.value.push({
|
||||
key: 'Firmware',
|
||||
tab: '远程升级',
|
||||
});
|
||||
}
|
||||
if (
|
||||
instanceStore.current?.protocol &&
|
||||
!['modbus-tcp', 'opc-ua'].includes(instanceStore.current?.protocol) &&
|
||||
|
@ -330,16 +334,11 @@ const getDetailFn = async () => {
|
|||
list.value = [...initList];
|
||||
getDetail();
|
||||
instanceStore.tabActiveKey = routerParams.params.value.tab || 'Info';
|
||||
}else{
|
||||
} else {
|
||||
instanceStore.tabActiveKey = routerParams.params.value.tab || 'Info';
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getDetailFn();
|
||||
});
|
||||
|
||||
const onTabChange = (e: string) => {
|
||||
if (instanceStore.tabActiveKey === 'Metadata') {
|
||||
EventEmitter.emit('MetadataTabs', () => {
|
||||
|
@ -389,6 +388,10 @@ const jumpProduct = () => {
|
|||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getDetailFn();
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
instanceStore.current = {} as any;
|
||||
statusRef.value && statusRef.value.unsubscribe();
|
||||
|
|
|
@ -193,10 +193,6 @@ const list = ref([
|
|||
key: 'Device',
|
||||
tab: '设备接入',
|
||||
},
|
||||
{
|
||||
key: 'Firmware',
|
||||
tab: '远程升级',
|
||||
},
|
||||
]);
|
||||
|
||||
const tabs = {
|
||||
|
@ -300,10 +296,6 @@ const getProtocol = async () => {
|
|||
key: 'DataAnalysis',
|
||||
tab: '数据解析',
|
||||
},
|
||||
{
|
||||
key: 'Firmware',
|
||||
tab: '远程升级',
|
||||
},
|
||||
];
|
||||
} else {
|
||||
list.value = [
|
||||
|
@ -320,10 +312,6 @@ const getProtocol = async () => {
|
|||
key: 'Device',
|
||||
tab: '设备接入',
|
||||
},
|
||||
{
|
||||
key: 'Firmware',
|
||||
tab: '远程升级',
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -348,6 +336,12 @@ const getProtocol = async () => {
|
|||
tab: '预处理数据',
|
||||
});
|
||||
}
|
||||
if (permissionStore.hasPermission('device/Firmware:view')) {
|
||||
list.value.push({
|
||||
key: 'Firmware',
|
||||
tab: '远程升级',
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
/**
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
<!-- 国标级联-绑定通道 -->
|
||||
<template>
|
||||
<j-modal
|
||||
v-model:visible="_vis"
|
||||
visible
|
||||
title="绑定通道"
|
||||
cancelText="取消"
|
||||
okText="确定"
|
||||
width="80%"
|
||||
@ok="handleSave"
|
||||
@cancel="_vis = false"
|
||||
@cancel="$emit('cancel')"
|
||||
:confirmLoading="loading"
|
||||
>
|
||||
<pro-search
|
||||
:columns="columns"
|
||||
target="media-bind"
|
||||
@search="handleSearch"
|
||||
type="simple"
|
||||
/>
|
||||
|
||||
<JProTable
|
||||
|
@ -68,37 +68,15 @@
|
|||
<script setup lang="ts">
|
||||
import CascadeApi from '@/api/media/cascade';
|
||||
import { onlyMessage } from '@/utils/comm';
|
||||
import { PropType } from 'vue';
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
type Emits = {
|
||||
(e: 'update:visible', data: boolean): void;
|
||||
(e: 'cancel'): void;
|
||||
(e: 'submit'): void;
|
||||
};
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
const props = defineProps({
|
||||
visible: { type: Boolean, default: false },
|
||||
data: {
|
||||
type: Object as PropType<Partial<Record<string, any>>>,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
|
||||
const _vis = computed({
|
||||
get: () => props.visible,
|
||||
set: (val) => emit('update:visible', val),
|
||||
});
|
||||
|
||||
watch(
|
||||
() => _vis.value,
|
||||
(val) => {
|
||||
if (val) handleSearch({ terms: [] });
|
||||
else _selectedRowKeys.value = [];
|
||||
},
|
||||
);
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '设备名称',
|
||||
|
@ -211,10 +189,13 @@ const handleSave = async () => {
|
|||
})
|
||||
if (resp.success) {
|
||||
onlyMessage('操作成功!');
|
||||
_vis.value = false;
|
||||
emit('submit');
|
||||
} else {
|
||||
onlyMessage('操作失败!', 'error');
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(()=>{
|
||||
handleSearch({ terms: [] })
|
||||
})
|
||||
</script>
|
||||
|
|
|
@ -138,7 +138,7 @@
|
|||
</JProTable>
|
||||
</FullPage>
|
||||
|
||||
<BindChannel v-model:visible="bindVis" @submit="listRef.reload()" />
|
||||
<BindChannel v-if="bindVis" @submit="submitData" @cancel="cancel"/>
|
||||
</page-container>
|
||||
</template>
|
||||
|
||||
|
@ -420,6 +420,14 @@ const handleClose = (data: any) => {
|
|||
valid.value = undefined;
|
||||
gbID.value = '';
|
||||
};
|
||||
|
||||
const cancel = () =>{
|
||||
bindVis.value = false
|
||||
}
|
||||
const submitData = () =>{
|
||||
bindVis.value = false
|
||||
listRef.value?.reload()
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.header {
|
||||
|
|
|
@ -617,7 +617,7 @@ const getClustersList = async () => {
|
|||
value: m.id,
|
||||
}));
|
||||
};
|
||||
getClustersList();
|
||||
|
||||
/**
|
||||
* SIP本地地址
|
||||
*/
|
||||
|
@ -631,7 +631,7 @@ const getAllList = async () => {
|
|||
}));
|
||||
setPorts();
|
||||
};
|
||||
getAllList();
|
||||
|
||||
|
||||
const handleTransportChange = () => {
|
||||
formData.value.host = undefined;
|
||||
|
@ -671,9 +671,6 @@ const getDetail = async () => {
|
|||
// console.log('formData.value: ', formData.value);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getDetail();
|
||||
});
|
||||
|
||||
const regDomain = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(?:\.|$)){4}$/
|
||||
// /[j-zA-Z0-9][-j-zA-Z0-9]{0,62}(\.[j-zA-Z0-9][-j-zA-Z0-9]{0,62})+\.?/;
|
||||
|
@ -778,6 +775,12 @@ const handleSubmit = () => {
|
|||
console.log('err: ', err);
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(async() => {
|
||||
await getDetail();
|
||||
getClustersList();
|
||||
getAllList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
|
|
@ -21,9 +21,9 @@
|
|||
</div>
|
||||
<div class="top-card-footer">
|
||||
<template v-for="(item, index) in footer" :key="index">
|
||||
<span v-if="!item.status">{{ item.title }}</span>
|
||||
<span v-if="!item?.status">{{ item?.title }}</span>
|
||||
<j-badge v-else :text="item.title" :status="item.status" />
|
||||
<div class="footer-item-value">{{ item.value }}</div>
|
||||
<div class="footer-item-value">{{ item?.value }}</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
.media-live-video {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
|
||||
min-height: 490px;
|
||||
.media-tool {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<div class="actions-item" v-if="item.executor === 'alarm'">
|
||||
<div class="item-options-warp">
|
||||
<div class="item-options-type">
|
||||
<img
|
||||
<!-- <img
|
||||
style="width: 18px"
|
||||
:src="
|
||||
iconMap.get(
|
||||
|
@ -14,7 +14,8 @@
|
|||
: item.executor,
|
||||
)
|
||||
"
|
||||
/>
|
||||
/> -->
|
||||
<AIcon :type="iconMap.get(item.executor === 'alarm' ? item.alarm.mode : item.executor)"/>
|
||||
</div>
|
||||
<div class="item-options-content">
|
||||
<template v-if="item.executor === 'alarm'">
|
||||
|
@ -75,6 +76,7 @@ const props = defineProps({
|
|||
background-color: #f0f0f0;
|
||||
border-radius: 6px 0 0 6px;
|
||||
cursor: pointer;
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.item-options-content {
|
||||
|
|
|
@ -257,11 +257,12 @@ const columns = [
|
|||
},
|
||||
{
|
||||
title: '关联场景联动',
|
||||
dataIndex: 'scene',
|
||||
dataIndex: 'id',
|
||||
hideInTable:true,
|
||||
key: 'scene',
|
||||
key: 'id',
|
||||
search: {
|
||||
type: 'select',
|
||||
termOptions: ['in'],
|
||||
options: async () => {
|
||||
const allData = await queryList({
|
||||
paging: false,
|
||||
|
@ -355,6 +356,14 @@ const map = {
|
|||
other: '其他',
|
||||
};
|
||||
const handleSearch = (e: any) => {
|
||||
e.terms.map((i:any)=>{
|
||||
i.terms.forEach((item:any)=>{
|
||||
if(item.column === 'id'){
|
||||
item.termType = 'rule-bind-alarm'
|
||||
}
|
||||
})
|
||||
})
|
||||
console.log(e,'e')
|
||||
params.value = e;
|
||||
};
|
||||
|
||||
|
|
|
@ -287,7 +287,7 @@
|
|||
.clientId
|
||||
"
|
||||
placeholder="请输入appId"
|
||||
:disabled="!!form.data.id"
|
||||
:disabled="!!form.data.id && !!form.data.apiClient.authConfig.oauth2.clientId"
|
||||
/>
|
||||
</j-form-item>
|
||||
<j-form-item
|
||||
|
|
|
@ -241,11 +241,13 @@
|
|||
</j-form-item>
|
||||
<j-form-item label="权限">
|
||||
<PermissChoose
|
||||
v-if="showPermissionChoose"
|
||||
:first-width="3"
|
||||
max-height="350px"
|
||||
v-model:value="form.data.permissions"
|
||||
:key="form.data.id || ''"
|
||||
/>
|
||||
<a-spin v-else/>
|
||||
</j-form-item>
|
||||
</j-form>
|
||||
</div>
|
||||
|
@ -291,6 +293,7 @@ const permission = 'system/Menu';
|
|||
// 路由
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const showPermissionChoose = ref(false)
|
||||
const routeParams = {
|
||||
id: route.params.id === ':id' ? undefined : (route.params.id as string),
|
||||
...route.query,
|
||||
|
@ -334,6 +337,7 @@ const form = reactive({
|
|||
resp.result?.accessSupport?.value || 'unsupported',
|
||||
};
|
||||
form.sourceCode = resp.result.code;
|
||||
showPermissionChoose.value = true
|
||||
});
|
||||
|
||||
if (isNoCommunity) {
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
<h5>请求示例</h5>
|
||||
<JsonViewer :value="requestCard.codeText" copyable />
|
||||
</div>
|
||||
<div class="api-card">
|
||||
<div class="api-card" v-if="requestCard.tableData.length">
|
||||
<h5>请求参数</h5>
|
||||
<div class="content">
|
||||
<j-pro-table
|
||||
|
@ -91,7 +91,7 @@ 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';
|
||||
import { findData, getCodeText, dealNoRef } from '../utils';
|
||||
|
||||
type cardType = {
|
||||
columns: object[];
|
||||
|
@ -101,7 +101,6 @@ type cardType = {
|
|||
getData: Function;
|
||||
};
|
||||
|
||||
|
||||
const props = defineProps({
|
||||
selectApi: {
|
||||
type: Object as PropType<apiDetailsType>,
|
||||
|
@ -114,7 +113,6 @@ const props = defineProps({
|
|||
});
|
||||
const { selectApi } = toRefs(props);
|
||||
|
||||
|
||||
const requestCard = reactive<cardType>({
|
||||
columns: [
|
||||
{
|
||||
|
@ -153,12 +151,14 @@ const requestCard = reactive<cardType>({
|
|||
const schema =
|
||||
props.selectApi.requestBody.content['application/json'].schema;
|
||||
const _ref = schema.$ref || schema?.items?.$ref;
|
||||
if (!_ref) return; // schema不是Java中的类的话则不进行解析,直接结束
|
||||
|
||||
// schema不是Java中的类的话则不进行解析,直接结束
|
||||
if (!_ref) {
|
||||
const type = schema.type || '';
|
||||
requestCard.codeText = dealNoRef(type, schema);
|
||||
} else {
|
||||
const schemaName = _ref?.split('/').pop();
|
||||
const type = schema.type || '';
|
||||
const tableData = findData(props.schemas, schemaName);
|
||||
|
||||
requestCard.codeText =
|
||||
type === 'array'
|
||||
? [getCodeText(props.schemas, tableData, 3)]
|
||||
|
@ -178,6 +178,7 @@ const requestCard = reactive<cardType>({
|
|||
})),
|
||||
},
|
||||
];
|
||||
}
|
||||
},
|
||||
});
|
||||
const responseStatusCard = reactive<cardType>({
|
||||
|
|
|
@ -108,7 +108,7 @@
|
|||
</j-button>
|
||||
</div>
|
||||
<j-monaco-editor
|
||||
v-if=" method !=='get' && method !=='patch'"
|
||||
v-if="showRequestBody"
|
||||
ref="editorRef"
|
||||
language="json"
|
||||
style="height: 100% ; min-height: 200px;"
|
||||
|
@ -144,6 +144,7 @@ const responsesContent = ref({});
|
|||
const editorRef = ref();
|
||||
const formRef = ref<FormInstance>();
|
||||
const method = ref()
|
||||
const showRequestBody = ref(!!props.selectApi?.requestBody)
|
||||
const requestBody = reactive({
|
||||
tableColumns: [
|
||||
{
|
||||
|
|
|
@ -7,7 +7,7 @@ import { schemaObjType } from "./typing";
|
|||
* @param schemaName 实体类名称
|
||||
*/
|
||||
export function findData(schemas: object, schemaName: string) {
|
||||
const basicType = ['string', 'integer', 'boolean'];
|
||||
const basicType = ['string', 'integer', 'boolean','number'];
|
||||
|
||||
if (!schemaName || !schemas[schemaName]) {
|
||||
return [];
|
||||
|
@ -62,6 +62,9 @@ export function getCodeText(
|
|||
case 'object':
|
||||
result[item.paramsName] = '';
|
||||
break;
|
||||
case 'number':
|
||||
result[item.paramsName] = 0;
|
||||
break;
|
||||
default: {
|
||||
const properties = schemas[item.paramsType]?.properties as object || {};
|
||||
const newArr = Object.entries(properties).map(
|
||||
|
@ -87,3 +90,32 @@ export function getCodeText(
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理数据中没有$ref的情况
|
||||
*/
|
||||
export function dealNoRef(type:string,schema?:any):any{
|
||||
let result = undefined;
|
||||
switch (type) {
|
||||
case 'string':
|
||||
result = '';
|
||||
break;
|
||||
case 'integer':
|
||||
result = 0;
|
||||
break;
|
||||
case 'boolean':
|
||||
result = true;
|
||||
break;
|
||||
case 'array':
|
||||
const itemType = schema?.items?.type
|
||||
const item = dealNoRef(itemType)
|
||||
result = [item];
|
||||
break;
|
||||
case 'number':
|
||||
result = 0;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
return result
|
||||
}
|
|
@ -2293,6 +2293,11 @@ dayjs@^1.10.5:
|
|||
resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.11.11.tgz#dfe0e9d54c5f8b68ccf8ca5f72ac603e7e5ed59e"
|
||||
integrity sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg==
|
||||
|
||||
dayjs@^1.11.12:
|
||||
version "1.11.12"
|
||||
resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.11.12.tgz#5245226cc7f40a15bf52e0b99fd2a04669ccac1d"
|
||||
integrity sha512-Rt2g+nTbLlDWZTwwrIXjy9MeiZmSDI375FvZs72ngxx8PDC6YXOeR3q5LAuPzjZQxhiWdRKac7RKV+YyQYfYIg==
|
||||
|
||||
de-indent@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d"
|
||||
|
|
Loading…
Reference in New Issue