Merge branch 'dev' of github.com:jetlinks/jetlinks-ui-vue into dev

This commit is contained in:
JiangQiming 2023-03-10 11:29:33 +08:00
commit fbbeb1df6d
18 changed files with 539 additions and 338 deletions

View File

@ -45,4 +45,5 @@ export const SystemConst = {
GET_METADATA: 'get_metadata',
REFRESH_DEVICE: 'refresh_device',
VERSION_CODE: 'version_code',
AMAP_KEY : 'amap_key',
}

View File

@ -1,19 +1,46 @@
<template>
<div style="width: 100%; height: 400px">
<el-amap
>
</el-amap>
<AmapComponent>
<el-amap-label-marker
v-for="i in point"
:key="i"
:position="i.geometry.coordinates"
:text="{
content: i.properties.deviceName,
direction: 'right',
style: {
fontSize: 15,
fillColor: '#fff',
strokeColor: 'rgba(255,0,0,0.5)',
strokeWidth: 2,
padding: [3, 10],
backgroundColor: 'yellow',
borderColor: '#ccc',
borderWidth: 3,
},
}"
:icon="{
image: 'https://a.amap.com/jsapi_demos/static/images/poi-marker.png',
anchor: 'bottom-center',
size: [25, 34],
clipOrigin: [459, 92],
clipSize: [50, 68],
}"
>123</el-amap-label-marker
>
</AmapComponent>
</div>
</template>
<script lang="ts" setup>
import { initAMapApiLoader } from '@vuemap/vue-amap';
import '@vuemap/vue-amap/dist/style.css';
initAMapApiLoader({
// key: '95fa72137f4263f8e64ae01f766ad09c',
key: 'a0415acfc35af15f10221bfa5a6850b4',
securityJsCode: 'cae6108ec3dd222f946d1a7237c78be0',
});
import AmapComponent from '@/components/AMapComponent/index.vue';
import { getGo } from '@/api/device/dashboard';
let point = ref();
const getMapData = async () => {
const res = await getGo({});
point.value = res.result?.features;
};
getMapData();
</script>
<style scoped>
</style>

View File

@ -28,8 +28,7 @@
:chartXData="barChartXData"
:chartYData="barChartYData"
></BarChart> -->
<Charts :options="onlineOptions"></Charts>
</TopCard
<Charts :options="onlineOptions"></Charts> </TopCard
></a-col>
<a-col :span="6"
><TopCard
@ -54,7 +53,7 @@
</template>
</Guide>
<div class="message-chart">
<Charts :options="devMegOptions"></Charts>
<Charts :options="devMegOptions"></Charts>
</div>
</div>
</a-col>
@ -74,7 +73,7 @@
</template>
<script lang="ts" setup>
import TimeSelect from './components/TimeSelect.vue';
import Charts from './components/Charts.vue'
import Charts from './components/Charts.vue';
import Guide from './components/Guide.vue';
import {
productCount,
@ -86,7 +85,8 @@ import encodeQuery from '@/utils/encodeQuery';
import { getImage } from '@/utils/comm';
import type { Footer } from '@/views/device/DashBoard/typings';
import TopCard from '@/views/device/DashBoard/components/TopCard.vue';
import Amap from './components/Amap.vue'
import { useMenuStore } from '@/store/menu';
import Amap from './components/Amap.vue';
let productTotal = ref(0);
let productFooter = ref<Footer[]>([
{
@ -133,6 +133,7 @@ let messageMaxChartYData = ref<number>();
let onlineOptions = ref<any>({});
let TodayDevOptions = ref<any>({});
let devMegOptions = ref<any>({});
const menuStore = useMenuStore();
const quickBtnList = [
{ label: '昨日', value: 'yesterday' },
{ label: '近一周', value: 'week' },
@ -140,54 +141,60 @@ const quickBtnList = [
{ label: '近一年', value: 'year' },
];
const getProductData = () => {
productCount().then((res) => {
if (res.status == 200) {
productTotal.value = res.result;
}
});
productCount({
terms: [
{
column: 'state',
value: '1',
},
],
}).then((res) => {
if (res.status == 200) {
productFooter.value[0].value = res.result;
}
});
productCount({
terms: [
{
column: 'state',
value: '0',
},
],
}).then((res) => {
if (res.status == 200) {
productFooter.value[1].value = res.result;
}
});
if (menuStore.hasMenu('device/Product')) {
productCount().then((res) => {
if (res.status == 200) {
productTotal.value = res.result;
}
});
productCount({
terms: [
{
column: 'state',
value: '1',
},
],
}).then((res) => {
if (res.status == 200) {
productFooter.value[0].value = res.result;
}
});
productCount({
terms: [
{
column: 'state',
value: '0',
},
],
}).then((res) => {
if (res.status == 200) {
productFooter.value[1].value = res.result;
}
});
}
};
getProductData();
const getDeviceData = () => {
deviceCount().then((res) => {
if (res.status == 200) {
deviceTotal.value = res.result;
}
});
deviceCount(encodeQuery({ terms: { state: 'online' } })).then((res) => {
if (res.status == 200) {
deviceFooter.value[0].value = res.result;
deviceOnline.value = res.result;
}
});
deviceCount(encodeQuery({ terms: { state: 'offline' } })).then((res) => {
if (res.status == 200) {
deviceFooter.value[1].value = res.result;
}
});
if (menuStore.hasMenu('device/Instance')) {
deviceCount().then((res) => {
if (res.status == 200) {
deviceTotal.value = res.result;
}
});
deviceCount(encodeQuery({ terms: { state: 'online' } })).then((res) => {
if (res.status == 200) {
deviceFooter.value[0].value = res.result;
deviceOnline.value = res.result;
}
});
deviceCount(encodeQuery({ terms: { state: 'offline' } })).then(
(res) => {
if (res.status == 200) {
deviceFooter.value[1].value = res.result;
}
},
);
}
};
getDeviceData();
const getOnline = () => {
@ -213,163 +220,167 @@ const getOnline = () => {
.reverse();
const y = res.result.map((item: any) => item.data.value);
const onlineYdata = y;
onlineYdata.reverse()
setOnlineChartOpition(x,onlineYdata);
onlineYdata.reverse();
setOnlineChartOpition(x, onlineYdata);
deviceFooter.value[0].value = y?.[1];
}
});
};
const setOnlineChartOpition = (x:Array<any>,y:Array<number>):void=>{
const setOnlineChartOpition = (x: Array<any>, y: Array<number>): void => {
onlineOptions.value = {
xAxis: {
type: 'category',
data: x,
show: false,
},
yAxis: {
type: 'value',
show: false,
},
grid: {
top: '5%',
bottom: 0,
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow',
},
},
series: [
{
name: '在线数',
data: y,
type: 'bar',
showBackground: true,
itemStyle: {
color: '#D3ADF7',
},
},
],
};
}
const setTodayDevChartOption = (x:Array<any>,y:Array<number>):void =>{
TodayDevOptions = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow',
},
},
xAxis: {
type: 'category',
boundaryGap: false,
show: false,
data:x
},
yAxis: {
type: 'value',
show: false,
},
grid: {
top: '2%',
bottom: 0,
},
series: [
{
name: '消息量',
data: y,
type: 'line',
smooth: true, // 线
symbolSize: 0, //
color: '#F29B55',
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 0,
color: '#FBBB87', // 100%
},
{
offset: 1,
color: '#FFFFFF', // 0%
},
],
global: false, // false
},
},
},
],
};
}
const setDevMesChartOption = (x:Array<any>,y:Array<number>,maxY:number):void =>{
devMegOptions.value = {
xAxis: {
type: 'category',
boundaryGap: false,
data: x,
type: 'category',
data: x,
show: false,
},
yAxis: {
type: 'value',
},
tooltip: {
trigger: 'axis',
formatter: '{b0}<br />{a0}: {c0}',
// formatter: '{b0}<br />{a0}: {c0}<br />{a1}: {c1}%'
type: 'value',
show: false,
},
grid: {
top: '2%',
bottom: '5%',
left: maxY > 100000 ? '90px' : '50px',
right: '50px',
top: '5%',
bottom: 0,
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow',
},
},
series: [
{
name: '消息量',
data: y,
type: 'bar',
// type: 'line',
// smooth: true,
color: '#597EF7',
barWidth: '30%',
// areaStyle: {
// color: {
// type: 'linear',
// x: 0,
// y: 0,
// x2: 0,
// y2: 1,
// colorStops: [
// {
// offset: 0,
// color: '#685DEB', // 100%
// },
// {
// offset: 1,
// color: '#FFFFFF', // 0%
// },
// ],
// global: false, // false
// },
// },
},
{
name: '占比',
data: y,
// data: percentageY,
type: 'line',
smooth: true,
symbolSize: 0, //
color: '#96ECE3',
},
{
name: '在线数',
data: y,
type: 'bar',
showBackground: true,
itemStyle: {
color: '#D3ADF7',
},
},
],
}
}
};
};
const setTodayDevChartOption = (x: Array<any>, y: Array<number>): void => {
TodayDevOptions = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow',
},
},
xAxis: {
type: 'category',
boundaryGap: false,
show: false,
data: x,
},
yAxis: {
type: 'value',
show: false,
},
grid: {
top: '2%',
bottom: 0,
},
series: [
{
name: '消息量',
data: y,
type: 'line',
smooth: true, // 线
symbolSize: 0, //
color: '#F29B55',
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 0,
color: '#FBBB87', // 100%
},
{
offset: 1,
color: '#FFFFFF', // 0%
},
],
global: false, // false
},
},
},
],
};
};
const setDevMesChartOption = (
x: Array<any>,
y: Array<number>,
maxY: number,
): void => {
devMegOptions.value = {
xAxis: {
type: 'category',
boundaryGap: false,
data: x,
},
yAxis: {
type: 'value',
},
tooltip: {
trigger: 'axis',
formatter: '{b0}<br />{a0}: {c0}',
// formatter: '{b0}<br />{a0}: {c0}<br />{a1}: {c1}%'
},
grid: {
top: '2%',
bottom: '5%',
left: maxY > 100000 ? '90px' : '50px',
right: '50px',
},
series: [
{
name: '消息量',
data: y,
type: 'bar',
// type: 'line',
// smooth: true,
color: '#597EF7',
barWidth: '30%',
// areaStyle: {
// color: {
// type: 'linear',
// x: 0,
// y: 0,
// x2: 0,
// y2: 1,
// colorStops: [
// {
// offset: 0,
// color: '#685DEB', // 100%
// },
// {
// offset: 1,
// color: '#FFFFFF', // 0%
// },
// ],
// global: false, // false
// },
// },
},
{
name: '占比',
data: y,
// data: percentageY,
type: 'line',
smooth: true,
symbolSize: 0, //
color: '#96ECE3',
},
],
};
};
getOnline();
//
const getDevice = () => {
@ -427,7 +438,7 @@ const getDevice = () => {
);
const x = today.map((item: any) => item.data.timeString).reverse();
const y = today.map((item: any) => item.data.value).reverse();
setTodayDevChartOption(x,y);
setTodayDevChartOption(x, y);
}
});
};
@ -452,7 +463,7 @@ const getEcharts = (data: any) => {
_time = '1M';
format = 'yyyy年-M月';
}
dashboard([
{
dashboard: 'device',
@ -468,7 +479,7 @@ const getEcharts = (data: any) => {
to: data.end,
},
},
]).then((res:any) => {
]).then((res: any) => {
if (res.status === 200) {
const x = res.result
.map((item: any) =>
@ -478,23 +489,27 @@ const getEcharts = (data: any) => {
)
.reverse();
const y = res.result.map((item: any) => item.data.value).reverse();
const maxY = Math.max.apply(null, messageChartYData.value.length ? messageChartYData.value : [0]);
setDevMesChartOption(x,y,maxY);
const maxY = Math.max.apply(
null,
messageChartYData.value.length ? messageChartYData.value : [0],
);
setDevMesChartOption(x, y, maxY);
}
});
};
</script>
<style lang="less" scoped>
.message-card,.device-position{
.message-card,
.device-position {
margin-top: 24px;
padding: 24px;
background-color: white;
}
.message-chart {
width: 100%;
height: 400px;
height: 400px;
}
.amap-box{
.amap-box {
height: 500px;
width: 100%;
}

View File

@ -174,7 +174,7 @@ const form = ref();
const filterOption = (input: string, option: any) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
};
const { productList } = defineProps(['productList']);
const props = defineProps(['productList']);
const _emit = defineEmits(['close']);
const instanceStore = useInstanceStore();
let _metadata = ref();
@ -279,7 +279,7 @@ const onSave = async () => {
});
const formData = {
...form.value,
productName: productList.find(
productName: props.productList.find(
(item: any) => item.id === form.value?.productId,
).name,
parentId: instanceStore.current.id,
@ -319,7 +319,7 @@ const showModal = async () => {
if (form.value) {
const formData = {
...form.value,
productName: productList.find(
productName: props.productList.find(
(item: any) => item.id === form.value?.productId,
).name,
parentId: instanceStore.current.id,

View File

@ -52,6 +52,7 @@ import EdgeMap from '../EdgeMap/index.vue';
import { useInstanceStore } from '@/store/instance';
import { storeToRefs } from 'pinia';
import { provide } from 'vue';
import { getEdgeMap, removeEdgeMap } from '@/api/device/instance';
const instanceStore = useInstanceStore();
const { current } = storeToRefs(instanceStore);
const props = defineProps(['childData']);
@ -89,18 +90,71 @@ const selectChange = (e: any) => {
};
watchEffect(() => {
if (props.childData?.id) {
current.value.parentId = props.childData.id;
form.name = props.childData?.name;
form.productId = props.childData?.productId;
if (props.childData.deriveMetadata) {
const metadata = JSON.parse(
props.childData?.deriveMetadata || {},
)?.properties?.map((item: any) => ({
metadataId: item.id,
metadataName: `${item.name}(${item.id})`,
metadataType: 'property',
name: item.name,
}));
if (metadata && metadata.length !== 0) {
getEdgeMap(current.value.id, {
deviceId: props.childData.id,
query: {},
}).then((res) => {
if (res.status === 200) {
// console.log(res.result)
//
const array = res.result[0]?.reduce(
(x: any, y: any) => {
const metadataId = metadata.find(
(item: any) =>
item.metadataId === y.metadataId,
);
if (metadataId) {
Object.assign(metadataId, y);
} else {
x.push(y);
}
return x;
},
metadata,
);
//
const items = array.filter(
(item: any) => item.metadataName,
);
current.value.metadata = items;
const delList = array
.filter((a: any) => !a.metadataName)
.map((b: any) => b.id);
//
if (delList && delList.length !== 0) {
removeEdgeMap(current.value.id, {
deviceId: props.childData.id,
idList: [...delList],
});
}
}
});
}
}
visible.value = true;
}
});
watchEffect(() => {});
const validate = async () => {
return formRef.value.validateFields();
return formRef.value.validateFields();
};
provide('validate',validate);
const comeBack = () =>{
provide('validate', validate);
const comeBack = () => {
emit('closeChildSave');
}
};
</script>
<style lang="less" scoped>
</style>

View File

@ -123,7 +123,7 @@ import { usePermissionStore } from '@/store/permission';
import SaveChild from './SaveChild/index.vue';
const instanceStore = useInstanceStore();
const { detail } = storeToRefs(instanceStore);
const { detail } = storeToRefs(instanceStore);
const router = useRouter();
const childVisible = ref(false);
const permissionStore = usePermissionStore();

View File

@ -1,8 +1,17 @@
<template>
<j-dropdown class='scene-select' trigger='click'>
<div :class='dropdownButtonClass'>
<AIcon v-if='!!icon' :type='icon' />
{{ label }}
<j-dropdown
class='scene-select'
trigger='click'
v-model:visible='visible'
@visibleChange='visibleChange'
>
<div @click.prevent='visible = true'>
<slot :label='label'>
<div :class='dropdownButtonClass' >
<AIcon v-if='!!icon' :type='icon' />
{{ label }}
</div>
</slot>
</div>
<template #overlay>
<div class='scene-select-content'>
@ -18,7 +27,7 @@
:type='component'
@change='timeSelect'
/>
<div v-else>
<div style='min-width: 400px' v-else>
<j-tree
:selectedKeys='selectValue ? [selectValue] : []'
:treeData='options'
@ -89,20 +98,18 @@ const props = defineProps({
component: {
type: String,
default: 'select' // 'select' | 'treeSelect'
}
},
})
const emit = defineEmits<Emit>()
const label = ref<LabelType>(props.placeholder)
const selectValue = ref(props.value)
const flatMapTree = new Map()
const visible = ref(false)
const LabelStyle = computed(() => {
return {
color: selectValue.value ? '#' : '#'
}
})
const visibleChange = (v: boolean) => {
visible.value = v
}
const dropdownButtonClass = computed(() => ({
'dropdown-button': true,
@ -112,24 +119,33 @@ const dropdownButtonClass = computed(() => ({
'type': props.type === 'type',
}))
const treeSelect = (v: any) => {
const treeSelect = (v: any, option: any) => {
const node = option.node
visible.value = false
label.value = node.fullname || node.name
selectValue.value = v[0]
emit('update:value', node[props.valueName])
emit('select', node)
}
const timeSelect = (v: string) => {
selectValue.value = v
visible.value = false
emit('update:value', v)
emit('select', v)
}
const menuSelect = (v: any) => {
const option = getOption(props.options, props.value, props.valueName)
emit('update:value', v.key)
const menuSelect = (v: string, option: any) => {
selectValue.value = v
visible.value = false
emit('update:value', v)
emit('select', option)
}
watchEffect(() => {
const option = getOption(props.options, props.value, props.valueName)
if (option && Object.keys(option).length) {
selectValue.value = props.value
if (option) {
label.value = option[props.labelName] || option.name
} else {
label.value = props.value || props.placeholder

View File

@ -7,13 +7,13 @@
</template>
<script lang='ts' setup name='DropdownMenus'>
import { isBoolean } from 'lodash-es'
import { isBoolean, isUndefined } from 'lodash-es'
import { getOption } from '../DropdownButton/util'
type ValueType = string| number | boolean
type Emits = {
(e: 'update:value', value: ValueType): void
(e: 'click', data: any): void
(e: 'click', value: string, data: any): void
}
const props = defineProps({
@ -32,29 +32,39 @@ const emit = defineEmits<Emits>()
const myOptions = computed(() => {
return props.options.map((item: any) => {
let _label = item.label || item.name
if (isBoolean(item.value)) {
_label = item.value === true ? '是' : '否'
let _value = isUndefined(item.value) ? item.id : item.value
if (isBoolean(_value)) {
_label = _value === true ? '是' : '否'
_value = String(_value)
}
return {
...item,
label: _label,
value: item.value || item.id,
value: _value
}
})
})
const myValue = ref(props.value)
const handleBoolean = (key: string) => {
return key === 'false' ? false : true
}
const click = (e: any) => {
const option = getOption(myOptions.value, e.key)
myValue.value = e.key
emit('update:value', e.key)
emit('click', {
key: e.key,
const _key = ['true', 'false'].includes(e.key) ? handleBoolean(e.key) : e.key
const option = getOption(myOptions.value, _key)
myValue.value = _key
emit('update:value', _key)
emit('click', _key, {
key: _key,
...option
})
}
watch(() => props.value, () => {
myValue.value = props.value
}, { immediate: true})
</script>
<style scoped lang='less'>

View File

@ -62,7 +62,7 @@ const change = (e: string) => {
}
</script>
<style lang='less'>
<style lang='less' scoped>
.dropdown-time-picker {
>div{
position: relative !important;
@ -86,4 +86,8 @@ const change = (e: string) => {
box-shadow: unset;
}
}
.wrapper{
display: none;
}
</style>

View File

@ -26,8 +26,8 @@ export const getComponent = (type: string): string => {
}
export const getOption = (data: any[], value?: string | number | boolean, key: string = 'name'): DropdownButtonOptions | any => {
let option = {}
if (!value) return option
let option
if (value === undefined && value === null) return option
for (let i = 0; i < data.length; i++) {
const item = data[i]
if (item[key] === value) {
@ -35,7 +35,9 @@ export const getOption = (data: any[], value?: string | number | boolean, key: s
break
} else if (item.children && item.children.length){
option = getOption(item.children, value, key)
if (option) break
if (option) {
break
}
}
}
return option

View File

@ -5,9 +5,13 @@
v-model:visible='visible'
@visibleChange='visibleChange'
>
<div class='dropdown-button value' @click.prevent='visible = true'>
<AIcon v-if='!!icon' :type='icon' />
{{ label }}
<div @click.prevent='visible = true'>
<slot :label='label'>
<div class='dropdown-button value'>
<AIcon v-if='!!icon' :type='icon' />
{{ label }}
</div>
</slot>
</div>
<template #overlay>
<div class='scene-select-content'>
@ -24,32 +28,36 @@
@change='timeChange'
/>
<DropdownMenus
v-if='["metric","enum", "boolean"].includes(item.component)'
:options='options'
@change='onSelect'
v-if='["select","enum", "boolean"].includes(item.component)'
:options='["metric", "upper"].includes(item.key) ? metricOption : options'
@click='onSelect'
/>
<div
v-else-if='item.component === "tree"'
style='min-width: 400px'
>
<j-tree
:selectedKeys='myValue ? [myValue] : []'
:treeData='item.key === "upper" ? metricOption : options'
@select='treeSelect'
:height='450'
:virtual='true'
>
<template #title="{ name, description }">
<j-space>
{{ name }}
<span v-if='description' class='tree-title-description'>{{ description }}</span>
</j-space>
</template>
</j-tree>
</div>
<ValueItem
v-else-if='valueItemKey.includes(item.component)'
v-else
v-model:modelValue='myValue'
:itemType='getComponent(item.component)'
:options='options'
:options='item.key === "upper" ? metricOption : options'
@change='valueItemChange'
/>
<j-tree
v-else
:selectedKeys='myValue ? [myValue] : []'
:treeData='options'
@select='treeSelect'
:height='450'
:virtual='true'
>
<template #title="{ name, description }">
<j-space>
{{ name }}
<span v-if='description' class='tree-title-description'>{{ description }}</span>
</j-space>
</template>
</j-tree>
</div>
</j-tab-pane>
</j-tabs>
@ -101,10 +109,9 @@ const updateValue = () => {
}
const treeSelect = (e: any) => {
console.log('treeSelect', e)
visible.value = false
label.value = e.fullname || e.name
emit('update:value', e.id)
emit('update:value', e[props.valueName])
emit('select', e)
}
@ -115,7 +122,8 @@ const valueItemChange = (e: string) => {
emit('select', e)
}
const sonSelect = (e: string, option: any) => {
const onSelect = (e: string, option: any) => {
console.log(e, option)
visible.value = false
label.value = option.label
emit('update:value', e)
@ -133,14 +141,16 @@ const visibleChange = (v: boolean) => {
visible.value = v
}
watch([props.options, props.value], () => {
watchEffect(() => {
const option = getOption(props.options, props.value as string, props.valueName) // label
if (option && Object.keys(option).length) {
myValue.value = props.value
mySource.value = props.source
if (option) {
label.value = option[props.labelName] || option.name
} else {
label.value = props.value || props.placeholder
}
}, { immediate: true })
})
</script>

View File

@ -12,8 +12,7 @@ export type DropdownButtonOptions = {
export type TabsOption = {
label: string;
key: string;
component: string,
options: DropdownButtonOptions[]
component: string
}
type ValueArrayType = [string, number]
export type ValueType = string | number | undefined | ValueArrayType
@ -47,6 +46,10 @@ export const defaultSetting = {
type: Array as PropType<Array<DropdownButtonOptions>>,
default: () => []
},
metricOption: {
type: Array as PropType<Array<DropdownButtonOptions>>,
default: () => []
},
metricOptions: { // 指标值
type: Array as PropType<Array<DropdownButtonOptions>>,
default: () => []

View File

@ -40,10 +40,8 @@
icon='icon-canshu'
placeholder='参数值'
:options='valueOptions'
:tabsOptions='[
{ label: "手动输入", component: "input", key: "fixed" },
{ label: "指标值", component: "time", key: "manual" }
]'
:metricOption='metricOption'
:tabsOptions='tabsOptions'
v-model:value='paramsValue.value.value'
v-model:source='paramsValue.value.source'
/>
@ -52,10 +50,8 @@
icon='icon-canshu'
placeholder='参数值'
:options='valueOptions'
:tabsOptions='[
{ label: "手动输入", component: "time", key: "fixed" },
{ label: "指标值", component: "input", key: "manual" },
]'
:metricOption='metricOption'
:tabsOptions='tabsOptions'
v-model:value='paramsValue.value.value'
v-model:source='paramsValue.value.source'
/>
@ -80,6 +76,16 @@ import ParamsDropdown, { DoubleParamsDropdown } from '../ParamsDropdown'
import { inject } from 'vue'
import { ContextKey } from './util'
type Emit = {
(e: 'update:value', data: TermsType): void
}
type TabsOption = {
label: string;
key: string;
component: string
}
const props = defineProps({
isFirst: {
type: Boolean,
@ -98,7 +104,7 @@ const props = defineProps({
default: () => ({
column: '',
type: '',
termType: undefined,
termType: 'eq',
value: {
source: 'fixed',
value: undefined
@ -107,6 +113,8 @@ const props = defineProps({
}
})
const emit = defineEmits<Emit>()
const paramsValue = reactive<TermsType>({
column: props.value.column,
type: props.value.type,
@ -115,21 +123,56 @@ const paramsValue = reactive<TermsType>({
})
const showDelete = ref(false)
const columnOptions: any = inject(ContextKey)
const options = ref<any>([])
const columnOptions: any = inject(ContextKey) //
const termTypeOptions = ref<Array<{ id: string, name: string}>>([]) //
const valueOptions = ref<any[]>([]) //
const metricOption = ref<any[]>([]) // termType
const tabsOptions = ref<Array<TabsOption>>([{ label: '手动输入', key: 'manual', component: 'string' }])
let metricsCacheOption: any[] = [] //
const termTypeOptions = computed(() => {
const handOptionByColumn = (option: any) => {
if (option) {
termTypeOptions.value = option.termTypes || []
metricsCacheOption = option.metrics || []
tabsOptions.value.length = 1
tabsOptions.value[0].component = option.dataType
if (option.metrics && option.metrics.length) {
tabsOptions.value.push(
{ label: '指标值', key: 'metric', component: 'select' }
)
}
if (option.dataType === 'boolean') {
valueOptions.value = [
{ label: '是', value: true },
{ label: '否', value: false },
]
} else if(option.dataType === 'enum') {
valueOptions.value = option.options?.map((item: any) => ({ ...item, label: item.name, value: item.id})) || []
} else{
valueOptions.value = option.options || []
}
} else {
termTypeOptions.value = []
metricsCacheOption = []
valueOptions.value = []
}
}
watchEffect(() => {
const option = getOption(columnOptions.value, paramsValue.column, 'column')
return option && Object.keys(option).length ? option.termTypes : []
})
const tabsOptions = computed(() => {
// valueoption
return []
handOptionByColumn(option)
})
const showDouble = computed(() => {
return paramsValue.termType ? ['nbtw', 'btw', 'in', 'nin'].includes(paramsValue.termType) : false
const isRange = paramsValue.termType ? ['nbtw', 'btw', 'in', 'nin'].includes(paramsValue.termType) : false
if (metricsCacheOption.length) {
metricOption.value = metricsCacheOption.filter(item => isRange ? item.range : !item.range)
} else {
metricOption.value = []
}
return isRange
})
const mouseover = () => {
@ -145,11 +188,20 @@ const mouseout = () => {
}
const columnSelect = () => {
paramsValue.termType = 'eq'
paramsValue.value = {
source: tabsOptions.value[0].key,
value: undefined
}
emit('update:value', { ...paramsValue })
}
const termsTypeSelect = () => {
paramsValue.value = {
source: tabsOptions.value[0].key,
value: undefined
}
emit('update:value', { ...paramsValue })
}
const termAdd = () => {
@ -160,10 +212,6 @@ const onDelete = () => {
}
const valueOptions = computed(() => {
return []
})
nextTick(() => {
Object.assign(paramsValue, props.value)
})

View File

@ -77,11 +77,22 @@ const rules = [{
}
}]
const handleParamsData = (data: any[]): any[] => {
return data?.map(item => {
return {
...item,
key: item.column,
disabled: !!item.children,
children: handleParamsData(item.children)
}
}) || []
}
const queryColumn = (dataModel: FormModelType) => {
const cloneDevice = cloneDeep(dataModel)
cloneDevice.branches = cloneDevice.branches?.filter(item => !!item)
getParseTerm(cloneDevice).then(res => {
columnOptions.value = res.result
columnOptions.value = handleParamsData(res.result as any[])
})
}

View File

@ -121,7 +121,7 @@
</page-container>
</template>
<script setup lang='ts'>
<script setup lang='ts' name='Scene'>
import SaveModal from './Save/save.vue';
import type { SceneItem } from './typings';
import { useMenuStore } from 'store/menu';

View File

@ -187,7 +187,7 @@ export type TriggerType = {
};
export interface TermsVale {
source: keyof typeof Source;
source: string;
/** 手动输入值,source为 manual 时不能为空 */
value?: Record<string, any> | any[];
/** 指标值,source为 metric 时不能为空 */

View File

@ -1,6 +1,6 @@
<template>
<div>
<a-spin :spinning="loading" :delay="500">
<j-spin :spinning="loading" :delay="500">
<div class="container">
<div class="left">
<img
@ -32,13 +32,13 @@
{{ basis.title || SystemConst.SYSTEM_NAME }}
</div>
<div class="main">
<a-form
<j-form
layout="vertical"
:model="form"
class="login-form"
@finish="onFinish"
>
<a-form-item
<j-form-item
label="账号"
name="username"
:rules="[
@ -48,13 +48,13 @@
},
]"
>
<a-input
<j-input
v-model:value="form.username"
placeholder="请输入账号"
:maxlength="64"
></a-input>
</a-form-item>
<a-form-item
></j-input>
</j-form-item>
<j-form-item
label="密码"
name="password"
:rules="[
@ -64,13 +64,13 @@
},
]"
>
<a-input-password
<j-input-password
v-model:value="form.password"
placeholder="请输入密码"
:maxlength="64"
></a-input-password>
</a-form-item>
<a-form-item
></j-input-password>
</j-form-item>
<j-form-item
v-if="codeConfig"
class="verifyCode"
label="验证码"
@ -82,7 +82,7 @@
},
]"
>
<a-input
<j-input
v-model:value="form.verifyCode"
autocomplete="off"
:maxlength="64"
@ -96,13 +96,13 @@
/>
</div>
</template>
</a-input>
</a-form-item>
<a-form-item
</j-input>
</j-form-item>
<j-form-item
name="remember"
style="text-align: left"
>
<a-checkbox
<j-checkbox
v-model:checked="form.remember"
@change="
() =>
@ -111,10 +111,10 @@
? -1
: 3600000)
"
>记住我</a-checkbox
>记住我</j-checkbox
>
</a-form-item>
<a-form-item>
</j-form-item>
<j-form-item>
<j-button
:loading="loading"
type="primary"
@ -124,14 +124,14 @@
>
登录
</j-button>
</a-form-item>
</a-form>
</j-form-item>
</j-form>
<div class="other">
<a-divider plain>
<j-divider plain>
<div class="other-text">
其他方式登录
</div>
</a-divider>
</j-divider>
<div class="other-button">
<j-button
v-for="(item, index) in bindings"
@ -173,7 +173,7 @@
</div>
</div>
</div>
</a-spin>
</j-spin>
</div>
</template>

View File

@ -3695,8 +3695,8 @@ jetlinks-store@^0.0.3:
jetlinks-ui-components@^1.0.4:
version "1.0.4"
resolved "https://registry.jetlinks.cn/jetlinks-ui-components/-/jetlinks-ui-components-1.0.4.tgz#34b70f75dcc4ea679f0da6674a3f372dfc4acab4"
integrity sha512-ffDi9NyD51hPzu6iRBhibxlI36In5igUciMugm+Lui2LxnYdGbXGvB0i4y6xKaGg4jRDJ/lDX+b6AvuIrlb1lg==
resolved "https://registry.jetlinks.cn/jetlinks-ui-components/-/jetlinks-ui-components-1.0.4.tgz#41d52892f0f4d38adc6df02a87290a3042eb5645"
integrity sha512-aX+XiGigzxZnrG52xqipxd+WuFwBeZ6+dvLkcvOfLLBqSu8sgfvr/8NJ5hFgv5Eo2QFnUJq3Qf4HXLw9Ogv/yw==
dependencies:
"@vueuse/core" "^9.12.0"
ant-design-vue "^3.2.15"