fix: 优化物联卡详情仪表盘显示异常;修改菜单拖拽排序

* fix: 修改菜单拖拽排序

* fix: 优化物联卡详情仪表盘显示异常
This commit is contained in:
XieYongHong 2024-05-10 10:43:01 +08:00 committed by GitHub
parent acf2c006db
commit bc0c4fa55b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 381 additions and 886 deletions

3
package-lock.json generated
View File

@ -8848,11 +8848,8 @@
"version": "2.0.5",
"resolved": "http://registry.jetlinks.cn/init-package-json/-/init-package-json-2.0.5.tgz",
"integrity": "sha512-u1uGAtEFu3VA6HNl/yUWw57jmKEMx8SKOxHhxjGnOFUiIlFnohKDFg4ZrPpv9wWqk44nDxGJAtqjdQFm+9XXQA==",
<<<<<<< HEAD
=======
"resolved": "http://registry.jetlinks.cn/init-package-json/-/init-package-json-2.0.5.tgz",
"integrity": "sha512-u1uGAtEFu3VA6HNl/yUWw57jmKEMx8SKOxHhxjGnOFUiIlFnohKDFg4ZrPpv9wWqk44nDxGJAtqjdQFm+9XXQA==",
>>>>>>> dev-bug
"inBundle": true,
"license": "ISC",
"dependencies": {

File diff suppressed because one or more lines are too long

View File

@ -1,75 +0,0 @@
<template>
<j-modal :mask-closable="false" visible width="70vw" title="设置属性规则" @cancel="handleCancel" @ok="handleOk">
<div class="advance-box">
<div class="left">
<Editor
ref="editor"
mode="advance"
key="advance"
v-model:value="_value"
/>
<Debug
:virtualRule="{
...virtualRule,
script: _value,
}"
:id="id"
/>
</div>
<div class="right">
<Operator :id="id" @add-operator-value="addOperatorValue"/>
</div>
</div>
</j-modal>
</template>
<script setup lang="ts" name="Advance">
import Editor from '../Editor/index.vue'
import Debug from '../Debug/index.vue'
import Operator from '../Operator/index.vue'
interface Emits {
(e: 'update:value', data: string | undefined): void;
(e: 'change', data: string): void;
}
const emit = defineEmits<Emits>();
const props = defineProps({
value: String,
id: String,
virtualRule: Object
})
const _value = ref<string | undefined>(props.value)
const handleCancel = () => {
emit('change', 'simple')
}
const handleOk = () => {
emit('update:value', _value.value)
emit('change', 'simple')
}
const editor = ref()
const addOperatorValue = (val: string) => {
editor.value.addOperatorValue(val)
}
</script>
<style lang="less" scoped>
.advance-box {
display: flex;
justify-content: flex-start;
width: 100%;
.left {
width: 70%;
}
.right {
width: 30%;
margin-left: 10px;
padding-left: 10px;
border-left: 1px solid lightgray;
}
}
</style>

View File

@ -1,302 +0,0 @@
<template>
<div class="debug-container">
<div class="left">
<div class="header">
<div>
<div class="title">
属性赋值
<div class="description">请对上方规则使用的属性进行赋值</div>
</div>
<div v-if="!isBeginning && virtualRule?.type === 'window'" class="action" @click="runScriptAgain">
<a style="margin-left: 75px;">发送数据</a>
</div>
</div>
</div>
<j-table :columns="columns" :data-source="property" :pagination="false" bordered size="small">
<template #bodyCell="{ column, record, index }">
<template v-if="column.key === 'id'">
<j-auto-complete :options="options" v-model:value="record.id" size="small" style="width: 130px" />
</template>
<template v-if="column.key === 'current'">
<j-input v-model:value="record.current" size="small"></j-input>
</template>
<template v-if="column.key === 'last'">
<j-input v-model:value="record.last" size="small"></j-input>
</template>
<template v-if="column.key === 'action'">
<AIcon type="DeleteOutlined" @click="deleteItem(index)" />
</template>
</template>
</j-table>
<j-button type="dashed" block style="margin-top: 5px" @click="addItem">
<template #icon>
<AIcon type="PlusOutlined" />
</template>
添加条目
</j-button>
</div>
<div class="right">
<div class="header">
<div class="title">
<div>运行结果</div>
</div>
<div class="action">
<div>
<a v-if="isBeginning" @click="beginAction">
开始运行
</a>
<a v-else @click="stopAction">
停止运行
</a>
</div>
<div>
<a @click="clearAction">
清空
</a>
</div>
</div>
</div>
<div class="log">
<j-descriptions>
<j-descriptions-item v-for="item in ruleEditorStore.state.log" :label="moment(item.time).format('HH:mm:ss')"
:key="item.time" :span="3">
<j-tooltip placement="top" :title="item.content">
{{ item.content }}
</j-tooltip>
</j-descriptions-item>
</j-descriptions>
</div>
</div>
</div>
</template>
<script setup lang="ts" name="Debug">
import { PropType } from 'vue';
import { useProductStore } from '@/store/product';
import { useRuleEditorStore } from '@/store/ruleEditor';
import moment from 'moment';
import { getWebSocket } from '@/utils/websocket';
import { PropertyMetadata } from '@/views/device/Product/typings';
import { onlyMessage } from '@/utils/comm';
const props = defineProps({
virtualRule: Object as PropType<Record<any, any>>,
id: String,
})
const isBeginning = ref(true)
type propertyType = {
id?: string,
current?: string,
last?: string
}
const property = ref<propertyType[]>([])
const columns = [{
title: '属性ID',
dataIndex: 'id',
key: 'id'
}, {
title: '当前值',
dataIndex: 'current',
key: 'current'
}, {
title: '上一值',
dataIndex: 'last',
key: 'last'
}, {
title: '',
key: 'action'
}]
const addItem = () => {
property.value.push({})
}
const deleteItem = (index: number) => {
property.value.splice(index, 1)
}
const ws = ref()
const virtualIdRef = ref(new Date().getTime());
const productStore = useProductStore()
const ruleEditorStore = useRuleEditorStore()
const runScript = () => {
const metadata = productStore.current.metadata || '{}';
const propertiesList = JSON.parse(metadata).properties || [];
const _properties = property.value.map((item: any) => {
const _item = propertiesList.find((i: any) => i.id === item.id);
return { ...item, type: _item?.valueType?.type };
});
if (ws.value) {
ws.value.unsubscribe?.();
}
if (!props.virtualRule?.script) {
isBeginning.value = true;
onlyMessage('请编辑规则', 'warning');
return;
}
ws.value = getWebSocket(`virtual-property-debug-${props.id}-${new Date().getTime()}`,
'/virtual-property-debug',
{
virtualId: `${virtualIdRef.value}-virtual-id`,
property: props.id,
virtualRule: {
...props.virtualRule,
},
properties: _properties || [],
}).subscribe((data: any) => {
ruleEditorStore.state.log.push({ time: new Date().getTime(), content: JSON.stringify(data.payload) });
if (props.virtualRule?.type !== 'window') {
stopAction()
}
})
}
const wsAgain = ref<any>();
const runScriptAgain = async () => {
if (wsAgain.value) {
wsAgain.value.unsubscribe?.();
}
const metadata = productStore.current.metadata || '{}';
const propertiesList = JSON.parse(metadata).properties || [];
const _properties = property.value.map((item: any) => {
const _item = propertiesList.find((i: any) => i.id === item.id);
return { ...item, type: _item?.valueType?.type };
});
wsAgain.value = getWebSocket(`virtual-property-debug-${props.id}-${new Date().getTime()}`,
'/virtual-property-debug',
{
virtualId: `${virtualIdRef.value}-virtual-id`,
property: props.id,
virtualRule: {
...props.virtualRule,
},
properties: _properties || [],
}).subscribe((data: any) => { })
}
const beginAction = () => {
isBeginning.value = false;
runScript();
}
const stopAction = () => {
isBeginning.value = true;
if (ws.value) {
ws.value.unsubscribe?.();
}
}
const clearAction = () => {
ruleEditorStore.set('log', []);
}
onUnmounted(() => {
if (ws.value) {
ws.value.unsubscribe?.();
}
clearAction()
})
const options = ref<{ label: string, value: string }[]>()
const getProperty = () => {
const metadata = productStore.current.metadata || '{}';
const _p: PropertyMetadata[] = JSON.parse(metadata).properties || [];
options.value = _p.filter((p) => p.id !== props.id).map((item) => ({
label: item.name,
value: item.id,
}));
}
getProperty()
</script>
<style lang="less" scoped>
.debug-container {
display: flex;
width: 100%;
height: 340px;
margin-top: 20px;
.left {
flex: 1;
min-width: 0;
max-width: 550px;
overflow-y: auto;
border: 1px solid lightgray;
.header {
display: flex;
align-items: center;
width: 100%;
height: 40px;
border-bottom: 1px solid lightgray;
//justify-content: space-around;
div {
display: flex;
//width: 100%;
align-items: center;
justify-content: flex-start;
height: 100%;
.title {
margin: 0 10px;
font-weight: 600;
font-size: 16px;
}
.description {
margin-left: 10px;
color: lightgray;
font-size: 12px;
}
}
.action {
width: 150px;
font-size: 14px;
}
}
}
.right {
flex: 1;
min-width: 0;
border: 1px solid lightgray;
border-left: none;
.header {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
height: 40px;
border-bottom: 1px solid lightgray;
.title {
display: flex;
div {
margin: 0 10px;
}
}
.action {
display: flex;
div {
margin: 0 10px;
}
}
}
.log {
height: 290px;
padding: 5px;
overflow: auto;
}
}
}
</style>

View File

@ -1,213 +0,0 @@
<template>
<div class="editor-box">
<div class="top">
<div class="left">
<span v-for="item in symbolList.filter((t: SymbolType, i: number) => i <= 3)" :key="item.key"
@click="addOperatorValue(item.value)">
{{ item.value }}
</span>
<span>
<j-dropdown>
<AIcon type="MoreOutlined" />
<template #overlay>
<j-menu>
<j-menu-item v-for="item in symbolList.filter((t: SymbolType, i: number) => i > 6)" :key="item.key"
@click="addOperatorValue(item.value)">
{{ item.value }}
</j-menu-item>
</j-menu>
</template>
</j-dropdown>
</span>
</div>
<div class="right">
<span v-if="mode !== 'advance'">
<j-tooltip :title="!id ? '请先输入标识' : '设置属性规则'">
<AIcon type="FullscreenOutlined" :class="!id ? 'disabled' : ''" @click="fullscreenClick" />
</j-tooltip>
</span>
</div>
</div>
<div class="editor">
<j-monaco-editor v-if="loading" v-model:model-value="_value" theme="vs" ref="editor" language="javascript"/>
</div>
</div>
</template>
<script setup lang="ts" name="Editor">
interface Props {
mode?: 'advance' | 'simple';
id?: string;
value?: string;
}
const props = defineProps<Props>()
interface Emits {
(e: 'change', data: string): void;
(e: 'update:value', data: string): void;
}
const emit = defineEmits<Emits>()
type editorType = {
insert(val: string): void
}
const editor = ref<editorType>()
type SymbolType = {
key: string,
value: string
}
const symbolList = [
{
key: 'add',
value: '+',
},
{
key: 'subtract',
value: '-',
},
{
key: 'multiply',
value: '*',
},
{
key: 'divide',
value: '/',
},
{
key: 'parentheses',
value: '()',
},
{
key: 'cubic',
value: '^',
},
{
key: 'dayu',
value: '>',
},
{
key: 'dayudengyu',
value: '>=',
},
{
key: 'dengyudengyu',
value: '==',
},
{
key: 'xiaoyudengyu',
value: '<=',
},
{
key: 'xiaoyu',
value: '<',
},
{
key: 'jiankuohao',
value: '<>',
},
{
key: 'andand',
value: '&&',
},
{
key: 'huohuo',
value: '||',
},
{
key: 'fei',
value: '!',
},
{
key: 'and',
value: '&',
},
{
key: 'huo',
value: '|',
},
{
key: 'bolang',
value: '~',
},
] as SymbolType[];
const _value = computed({
get: () => props.value || '',
set: (data: string) => {
emit('update:value', data);
}
})
const loading = ref(false)
onMounted(() => {
setTimeout(() => {
loading.value = true;
}, 100);
})
const addOperatorValue = (val: string) => {
editor.value?.insert(val)
}
const fullscreenClick = () => {
if (props.id) {
emit('change', 'advance');
}
}
defineExpose({
addOperatorValue
})
</script>
<style lang="less" scoped>
.editor-box {
margin-bottom: 10px;
border: 1px solid lightgray;
.top {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
width: 100%;
border-bottom: 1px solid lightgray;
.left {
display: flex;
align-items: center;
width: 60%;
margin: 0 5px;
span {
display: inline-block;
height: 40px;
margin: 0 10px;
line-height: 40px;
cursor: pointer;
}
}
.right {
display: flex;
align-items: center;
width: 10%;
margin: 0 5px;
span {
margin: 0 5px;
}
}
.disabled {
color: rgba(#000, 0.5);
cursor: not-allowed;
}
}
.editor {
height: 300px;
}
}
</style>

View File

@ -1,10 +0,0 @@
import type { TreeNode } from '@/utils/tree';
interface OperatorItem extends TreeNode {
id: string;
name: string;
key: string;
description: string;
code: string;
children: OperatorItem[];
}

View File

@ -1,44 +0,0 @@
<template>
<Editor key="simple" @change="change" v-model:value="_value" :id="id" />
<Advance v-if="ruleEditorStore.state.model === 'advance'" v-model:value="_value" :model="ruleEditorStore.state.model"
:virtualRule="virtualRule" :id="id" @change="change" />
</template>
<script setup lang="ts">
import { useRuleEditorStore } from '@/store/ruleEditor'
import Editor from './Editor/index.vue'
import Advance from './Advance/index.vue'
interface Props {
value: string;
property?: string;
virtualRule?: any;
id?: string;
}
const props = defineProps<Props>()
interface Emits {
(e: 'update:value', data: string): void;
}
const emit = defineEmits<Emits>()
const _value = computed({
get: () => props.value,
set: (val: string) => {
emit('update:value', val)
}
})
const ruleEditorStore = useRuleEditorStore()
const change = (v: string) => {
ruleEditorStore.set('model', v);
}
onMounted(() => {
ruleEditorStore.set('property', props.property)
ruleEditorStore.set('code', props.value);
})
</script>
<style lang="less" scoped></style>

View File

@ -360,6 +360,13 @@ const runScript = () => {
if (props.virtualRule?.type !== 'window') {
stopAction();
}
}, () => {}, () => {
ruleEditorStore.state.log.push({
time: new Date().getTime(),
content: '运行结束',
_time: unref(time.value),
});
stopAction()
});
};

View File

@ -83,7 +83,6 @@ export default {
.use(MarkdownItAbbr)
.use(MarkdownItAnchor, props.anchor)
.use(MarkdownItDeflist)
.use(emoji, props.emoji)
.use(MarkdownItFootnote)
.use(MarkdownItHighlightjs, props.highlight)
.use(MarkdownItIns)

View File

@ -84,40 +84,48 @@ watch(
});
},
{ immediate: true, deep: true },
);
const productName = computed(() => {
console.log(modelRef.product);
return productList.value.find(item => item.id === modelRef.product)?.name || ''
// console.log(item.id);
})
const handleOk = async () => {
const params = paramsEncodeQuery(props.data);
const _params = paramsEncodeQuery(props.data);
// downloadFile(
// deviceExport(modelRef.product || '', modelRef.fileType),
// params,
// );
const res: any = await deviceExport(
modelRef.product || '',
modelRef.fileType,
params
);
if (res) {
// const blob = new Blob([res], { type: modelRef.fileType });
// const url = URL.createObjectURL(blob);
// downloadFileByUrl(url, `${productName.value ? (productName.value + '') : ''}`, modelRef.fileType);
window.open(`${origin}/api/device-instance/${modelRef.product}/export.xlsx?:X_Access_Token=${LocalStore.get(TOKEN_KEY)}`)
emit('close');
}
// const res: any = await deviceExport(
// modelRef.product || '',
// modelRef.fileType,
// params
//
//
// );
// if (res) {
// // const blob = new Blob([res], { type: modelRef.fileType });
// // const url = URL.createObjectURL(blob);
// // downloadFileByUrl(url, `${productName.value ? (productName.value + '') : ''}`, modelRef.fileType);
//
// }
const urlParams = new URLSearchParams()
Object.keys(_params).forEach(key => {
if (_params[key]) {
urlParams.append(key, _params[key])
}
})
window.open(`${origin}/api/device-instance/${modelRef.product}/export.xlsx?:X_Access_Token=${LocalStore.get(TOKEN_KEY)}&${urlParams}`)
emit('close');
};
const handleCancel = () => {

View File

@ -327,7 +327,7 @@ const handleClick = (data: any) => {
const add = () => {
const url = menuStore.hasMenu('link/AccessConfig/Detail');
if (url) {
const tab: any = window.open(`${origin}/#${url}?view=false&save=true`);
const tab: any = window.open(`${window.location.origin + window.location.pathname}#${url}?view=false`);
tab.onTabSaveSuccess = (value: any) => {
if (value.status === 200) {
tableRef.value.reload();

View File

@ -180,8 +180,8 @@ export const useColumns = (type?: MetadataType, target?: 'device' | 'product', n
},
{ max: 64, message: '最多可输入64个字符' },
{
pattern: /^[a-zA-Z0-9_\-]+$/,
message: '标识只能由数字、字母、下划线、中划线组成',
pattern: /^[a-zA-Z0-9_]+$/,
message: '标识只能由数字、字母、下划线组成',
},
]
},

View File

@ -1,238 +1,295 @@
<template>
<j-popconfirm-modal
body-style="padding-top:4px;width:600px;"
placement="bottomRight"
:disabled="disabled"
:get-popup-container="(node) => fullRef || node"
@confirm="confirm"
@cancel="cancel"
@visibleChange="visibleChange"
>
<template v-if="visible" #content>
<j-scrollbar height="350" v-if="showMetrics || config.length > 0">
<j-collapse v-model:activeKey="activeKey">
<j-collapse-panel v-for="(item, index) in config" :key="'store_'+index" :header="item.name">
<j-table
:columns="columns"
:data-source="item.properties"
:pagination="false"
rowKey="id"
>
<template #bodyCell="{ column, record, index }">
<template v-if="column.dataIndex === 'type'">{{ record.type?.name }}</template>
<value-item
v-else-if="column.dataIndex === 'value'"
v-model:modelValue="configValue[record.property]"
:itemType="item.properties[index].type?.type"
:options="(item.properties[index].type?.elements || []).map((a:any) => ({
<j-popconfirm-modal
v-if="!disabled"
body-style="padding-top:4px;width:600px;"
placement="bottomRight"
:disabled="disabled"
:get-popup-container="(node) => fullRef || node"
@confirm="confirm"
@cancel="cancel"
@visibleChange="visibleChange"
>
<template v-if="visible" #content>
<j-scrollbar height="350" v-if="showMetrics || config.length > 0">
<j-collapse v-model:activeKey="activeKey">
<j-collapse-panel
v-for="(item, index) in config"
:key="'store_' + index"
:header="item.name"
>
<j-table
:columns="columns"
:data-source="item.properties"
:pagination="false"
rowKey="id"
>
<template #bodyCell="{ column, record, index }">
<template v-if="column.dataIndex === 'type'">{{
record.type?.name
}}</template>
<value-item
v-else-if="column.dataIndex === 'value'"
v-model:modelValue="
configValue[record.property]
"
:itemType="
item.properties[index].type?.type
"
:options="(item.properties[index].type?.elements || []).map((a:any) => ({
label: a.text,
value: a.value,
}))"
:get-popup-container="(node) => fullRef || node"
/>
</template>
</j-table>
</j-collapse-panel>
<j-collapse-panel key="metrics" v-if="showMetrics">
<template #header>
指标配置
<j-tooltip title="场景联动页面可引用指标配置作为触发条件">
<AIcon type="ExclamationCircleOutlined" style="padding-left: 12px;padding-top: 4px;" />
</j-tooltip>
</template>
<Metrics ref="metricsRef" :options="booleanOptions" :type="props.type" :value="myValue.metrics"/>
</j-collapse-panel>
</j-collapse>
</j-scrollbar>
<div v-else style="padding-top: 24px">
<j-empty
description="没有动态配置项"
/>
</div>
</template>
:get-popup-container="
(node) => fullRef || node
"
/>
</template>
</j-table>
</j-collapse-panel>
<j-collapse-panel key="metrics" v-if="showMetrics">
<template #header>
指标配置
<j-tooltip
title="场景联动页面可引用指标配置作为触发条件"
>
<AIcon
type="ExclamationCircleOutlined"
style="padding-left: 12px; padding-top: 4px"
/>
</j-tooltip>
</template>
<Metrics
ref="metricsRef"
:options="booleanOptions"
:type="props.type"
:value="myValue.metrics"
/>
</j-collapse-panel>
</j-collapse>
</j-scrollbar>
<div v-else style="padding-top: 24px">
<j-empty description="没有动态配置项" />
</div>
</template>
<PermissionButton
key="setting"
:disabled="disabled"
:has-permission="hasPermission"
:tooltip="tooltip"
style="padding-left: 0"
type="link"
>
<AIcon type="SettingOutlined" />
配置
</PermissionButton>
</j-popconfirm-modal>
<PermissionButton
v-else
key="setting"
:disabled="disabled"
:has-permission="hasPermission"
:tooltip="tooltip"
style="padding-left: 0;"
style="padding-left: 0"
type="link"
>
<AIcon type="SettingOutlined" />
配置
<AIcon type="SettingOutlined" />
配置
</PermissionButton>
</j-popconfirm-modal>
</template>
<script setup lang="ts" name="OtherSetting">
import Metrics from './Metrics/Metrics.vue'
import {watch} from "vue";
import {useProductStore} from "store/product";
import {useInstanceStore} from "store/instance";
import {getMetadataConfig, getMetadataDeviceConfig} from "@/api/device/product";
import ModelButton from '@/views/device/components/Metadata/Base/components/ModelButton.vue'
import { omit , cloneDeep} from "lodash-es";
import { FULL_CODE } from 'jetlinks-ui-components/es/DataTable'
import Metrics from './Metrics/Metrics.vue';
import { watch } from 'vue';
import { useProductStore } from 'store/product';
import { useInstanceStore } from 'store/instance';
import {
getMetadataConfig,
getMetadataDeviceConfig,
} from '@/api/device/product';
import ModelButton from '@/views/device/components/Metadata/Base/components/ModelButton.vue';
import { omit, cloneDeep } from 'lodash-es';
import { FULL_CODE } from 'jetlinks-ui-components/es/DataTable';
const props = defineProps({
value: {
type: Object,
default: () => ({})
},
type: {
type: String,
default: undefined
},
disabled: {
type: Boolean,
default: false
},
id: {
type: String,
default: undefined
},
record: {
type: Object,
default: () => ({})
},
hasPermission: String,
tooltip: Object
})
value: {
type: Object,
default: () => ({}),
},
type: {
type: String,
default: undefined,
},
disabled: {
type: Boolean,
default: false,
},
id: {
type: String,
default: undefined,
},
record: {
type: Object,
default: () => ({}),
},
hasPermission: String,
tooltip: Object,
});
const fullRef = inject(FULL_CODE);
const type = inject('_metadataType')
const type = inject('_metadataType');
const productStore = useProductStore()
const deviceStore = useInstanceStore()
const productStore = useProductStore();
const deviceStore = useInstanceStore();
const emit = defineEmits(['update:value'])
const emit = defineEmits(['update:value']);
const activeKey = ref()
const storageRef = ref()
const metricsRef = ref()
const activeKey = ref();
const storageRef = ref();
const metricsRef = ref();
const myValue = ref(props.value)
const visible = ref(false)
const myValue = ref(props.value);
const visible = ref(false);
const config = ref<any>([])
const configValue = ref(props.value?.expands)
const config = ref<any>([]);
const configValue = ref(props.value?.expands);
const showMetrics = computed(() => {
return ['int', 'long', 'float', 'double', 'string', 'boolean', 'date'].includes(props.type as any)
})
return [
'int',
'long',
'float',
'double',
'string',
'boolean',
'date',
].includes(props.type as any);
});
const booleanOptions = ref([
{ label: '否', value: 'false'},
{ label: '是', value: 'true'},
])
{ label: '否', value: 'false' },
{ label: '是', value: 'true' },
]);
const columns = ref([
{
title: '参数名称',
dataIndex: 'name',
width: 150,
ellipsis: true,
},
{
title: '输入类型',
dataIndex: 'type',
width: 150,
},
{
title: '值',
dataIndex: 'value',
},
{
title: '参数名称',
dataIndex: 'name',
width: 150,
ellipsis: true,
},
{
title: '输入类型',
dataIndex: 'type',
width: 150,
},
{
title: '值',
dataIndex: 'value',
},
]);
const getConfig = async () => {
const id = type === 'product' ? productStore.current?.id : deviceStore.current.id
console.log(props.id, id, props)
const id =
type === 'product' ? productStore.current?.id : deviceStore.current.id;
console.log(props.id, id, props);
if(!props.id || !id || !props.type) return
if (!props.id || !id || !props.type) return;
if (props.type === 'boolean') {
const booleanValue = props.record.valueType
booleanOptions.value[0] = { label: booleanValue.falseText || '否', value: booleanValue.falseValue || 'false'}
booleanOptions.value[1] = { label: booleanValue.trueText || '是', value: booleanValue.trueValue || 'true'}
}
const params: any = {
deviceId: id,
metadata: {
id: props.id,
type: 'property',
dataType: props.type,
},
}
const resp = type === 'product' ? await getMetadataConfig(params) : await getMetadataDeviceConfig(params)
if (resp.status === 200) {
config.value = resp.result
if (resp.result.length) {
activeKey.value = ['store_0']
} else if (showMetrics.value) {
activeKey.value = ['metrics']
if (props.type === 'boolean') {
const booleanValue = props.record.valueType;
booleanOptions.value[0] = {
label: booleanValue.falseText || '否',
value: booleanValue.falseValue || 'false',
};
booleanOptions.value[1] = {
label: booleanValue.trueText || '是',
value: booleanValue.trueValue || 'true',
};
}
if (resp.result.length && !configValue.value) {
resp.result.forEach(a => {
if (a.properties) {
a.properties.forEach(b => {
configValue.value[b.property] = undefined
})
const params: any = {
deviceId: id,
metadata: {
id: props.id,
type: 'property',
dataType: props.type,
},
};
const resp =
type === 'product'
? await getMetadataConfig(params)
: await getMetadataDeviceConfig(params);
if (resp.status === 200) {
config.value = resp.result;
if (resp.result.length) {
activeKey.value = ['store_0'];
} else if (showMetrics.value) {
activeKey.value = ['metrics'];
}
if (resp.result.length && !configValue.value) {
resp.result.forEach((a) => {
if (a.properties) {
a.properties.forEach((b) => {
configValue.value[b.property] = undefined;
});
}
});
}
})
}
}
}
};
const confirm = () => {
return new Promise(async (resolve, reject) => {
try {
let metrics: any;
metrics = await metricsRef.value?.getData();
return new Promise(async (resolve, reject) => {
try {
let metrics: any
metrics = await metricsRef.value?.getData()
const expands = {
...(configValue.value || {}),
};
const expands = {
...(configValue.value || {}),
}
if (metrics) {
expands.metrics = metrics
}
emit('update:value', {
...props.value,
...expands
})
resolve(true)
} catch (err) {
reject(false)
}
})
}
if (metrics) {
expands.metrics = metrics;
}
emit('update:value', {
...props.value,
...expands,
});
resolve(true);
} catch (err) {
reject(false);
}
});
};
const visibleChange = (e: boolean) => {
visible.value = e
if (e) {
configValue.value = omit(props.value, ['source', 'type', 'metrics', 'required'])
getConfig()
}
}
visible.value = e;
if (e) {
configValue.value = omit(props.value, [
'source',
'type',
'metrics',
'required',
]);
getConfig();
}
};
const cancel = () => {
myValue.value = cloneDeep(props.value)
}
watch(() => props.value, () => {
console.log(props.value)
myValue.value = cloneDeep(props.value)
}, {immediate: true, deep: true})
myValue.value = cloneDeep(props.value);
};
watch(
() => props.value,
() => {
console.log(props.value);
myValue.value = cloneDeep(props.value);
},
{ immediate: true, deep: true },
);
</script>
<style scoped>
</style>
<style scoped></style>

View File

@ -313,7 +313,7 @@ const getDataTotal = () => {
yearTotal.value = resp.sortArray
.reduce((r, n) => r + Number(n.value), 0)
.toFixed(2);
yearOptions.value = monthOptions.value;
yearOptions.value = resp.sortArray;
});
};

View File

@ -164,8 +164,13 @@ const onDelete = () => {
const onDeleteAll = () => {
if (FormModel.value.branches) {
// FormModel.value.branches.length = props.name
// FormModel.value.branches.push(null as any)
FormModel.value.branches[props.name].when = []
FormModel.value.options.when[props.branches_Index].terms = []
if(FormModel.value.branches[props.name + 1] === null){
FormModel.value.branches.splice(props.name + 1 , 1)
}
}
}
@ -199,7 +204,9 @@ const addWhen = () => {
key: `terms_${randomString()}`
}
FormModel.value.branches?.[props.name].when?.push(terms)
FormModel.value.branches?.splice(props.groupLen, 0, null)
if(FormModel.value.branches?.length <= props.name + 1){
FormModel.value.branches?.splice(props.groupLen, 0, null)
}
FormModel.value.options!.when[props.branches_Index]?.terms.push({ termType: '并且', terms: [['','eq','','and']]})
}

View File

@ -1,6 +1,6 @@
<template>
<div class="button-mange-container">
<j-pro-table
<j-pro-table
ref="tableRef"
:columns="table.columns"
model="TABLE"
@ -46,7 +46,7 @@
</PermissionButton>
</j-space>
</template>
</j-pro-table>
</j-pro-table>
<div class="dialog">
<ButtonAddDialog
@ -110,6 +110,7 @@ const table = reactive({
title: '说明',
dataIndex: 'description',
key: 'description',
ellipsis: true,
},
{
title: '操作',

View File

@ -328,7 +328,7 @@ export const handleSorts = (node: any[]) => {
if (!node) return []
return node.map((item, index) => {
if (item.index !== index) {
item.sortIndex = index
item.sortIndex = index + 1
if (item.children) {
item.children = handleSorts(item.children).sort((a, b) => a.sortIndex - b.sortIndex)
}

View File

@ -63,7 +63,10 @@
:btnId="form.data.id"
/>
</j-form-item>
<j-form-item label="说明" name="description">
<j-form-item label="说明" name="description"
:rules="[
{ max: 200, message: '最多可输入200个字符' },
]">
<j-textarea
v-model:value="form.data.description"
:rows="4"
@ -147,7 +150,7 @@ const codeOptions = [
{ label: 'update', value: 'update', message: '更新' },
];
const validateIdRepeat = (rule: any, val: any) => {
if (props.mode === '编辑') {
if (props.mode === '编辑'|| props.mode === '查看') {
return Promise.resolve('');
}
const isRepeat = props.menuData.find((i: any) => {

View File

@ -14,7 +14,9 @@
:params="queryParams"
:rowSelection="{
selectedRowKeys: selectedRowKeys,
onChange: (keys:string[])=>selectedRowKeys = keys,
onSelect: onSelectChange,
onSelectAll: selectAll,
onSelectNone: () => (selectedRowKeys = []),
}"
size="small"
>
@ -26,6 +28,7 @@
<PermissionButton
:popConfirm="{
title: `是否批量解除绑定`,
placement: 'topRight',
onConfirm: () => table.unbind(),
}"
>
@ -181,9 +184,37 @@ const table = {
},
};
const onSelectChange = (item: any, state: boolean) => {
const arr = new Set(selectedRowKeys.value);
if (state) {
arr.add(item.id);
} else {
arr.delete(item.id);
}
selectedRowKeys.value = [...arr.values()];
};
const selectAll = (selected: Boolean, selectedRows: any, changeRows: any) => {
if (selected) {
changeRows.map((i: any) => {
if (!selectedRowKeys.value.includes(i.id)) {
selectedRowKeys.value.push(i.id);
}
});
} else {
const arr = changeRows.map((item: any) => item.id);
const _ids: string[] = [];
selectedRowKeys.value.map((i: any) => {
if (!arr.includes(i)) {
_ids.push(i);
}
});
selectedRowKeys.value = _ids;
}
};
//
const dialogVisible = ref(false);
</script>
<style lang="less" scoped>

View File

@ -53,10 +53,11 @@ import { FormInstance } from 'ant-design-vue';
import { saveRole_api , queryRoleGroup , updateRole_api} from '@/api/system/role';
import { useMenuStore } from '@/store/menu';
import { onlyMessage } from '@/utils/comm';
import { cloneDeep } from 'lodash-es'
const route = useRoute();
const { jumpPage } = useMenuStore();
const emits = defineEmits(['update:visible']);
const emits = defineEmits(['update:visible','refresh']);
const props = defineProps({
visible: {
type:Boolean,
@ -107,6 +108,7 @@ const confirm = async() => {
updateRole_api(form.value).then((resp:any)=>{
if (resp.status === 200) {
onlyMessage('操作成功');
emits('refresh');
emits('update:visible', false);
}
}).catch(() => (loading.value = false));
@ -131,7 +133,8 @@ onMounted(()=>{
getGroupOptions()
form.value.groupId = props.groupId
if(props.modalType === 'edit'){
form.value = props.current
// Object.assign(form.value,props.current)
form.value=cloneDeep(props.current)
}
})
</script>

View File

@ -34,8 +34,10 @@
</j-pro-table>
</FullPage>
<AddDialog v-if="dialogVisible" v-model:visible="dialogVisible" :groupId="groupId" :modalType="modalType"
:current="current" />
<AddDialog v-if="dialogVisible" v-model:visible="dialogVisible" :groupId="groupId" :modalType="modalType"
:current="current"
@refresh="tableRef?.reload()"
/>
</div>
</template>
@ -140,6 +142,7 @@ const getActions = (
},
popConfirm: {
title: '确认删除?',
placement:'topRight',
onConfirm: async () => {
const res = await delRole_api(data.id)
if (res.status === 200) {

View File

@ -186,11 +186,11 @@ const columns = [
key: 'roleList',
search:{
type:'select',
rename:'id$in-dimension$role',
// rename:'id$in-dimension$role',
options:() =>
new Promise((resolve)=>{
queryRole_api(
{
{
paging:false,
sorts: [
{ name: 'createTime', order: 'desc' },
@ -303,37 +303,60 @@ type dictType = {
type modalType = '' | 'add' | 'edit' | 'reset';
const handleParams = (params: any) => {
const newParams = (params?.terms as any[])?.map((item1) => {
const newParams = (params?.terms as any[])?.map((termsGroupA) => {
let arr: any[] = []
item1.terms = item1.terms.map((item2: any) => {
if (['telephone', 'email'].includes(item2.column)) {
termsGroupA.terms = termsGroupA.terms.map((termsItem: any) => {
if (termsItem.column === 'id$in-dimension$role') {
let _termType = termsItem.termType === 'nin' ? 'not$in' : termsItem.termType
termsItem.column = `${termsItem.column}$${_termType}`
delete termsItem.termType
}
if (['telephone', 'email'].includes(termsItem.column)) {
return {
column: 'id$user-detail',
value: [item2],
value: [termsItem],
};
}
if (['type'].includes(item2.column) && item2.value === 'other') {
if (['type'].includes(termsItem.column) && termsItem.value === 'other') {
arr = [
{
...item2,
...termsItem,
type: 'or',
termType: 'isnull',
value: 1,
},
{
...item2,
...termsItem,
type: 'or',
termType: 'empty',
value: 1,
}
]
}
return item2;
if(termsItem.column === 'roleList'){
if(termsItem.termType === 'eq' || termsItem.termType === 'in'){
return {
column: 'id$in-dimension$role',
type: termsItem.type,
value: termsItem.value
}
}else{
return {
column: 'id$in-dimension$role$not',
type: termsItem.type,
value: termsItem.value
}
}
}
return termsItem;
});
if(arr.length){
item1.terms = [...item1.terms, ...arr]
termsGroupA.terms = [...termsGroupA.terms, ...arr]
}
return item1;
return termsGroupA;
});
queryParams.value = { terms: newParams || [] };
};

View File

@ -96,7 +96,7 @@ export default defineConfig(({ mode}) => {
// target: 'http://192.168.32.244:8881',
// target: 'http://192.168.32.163:8844', //张本地
// target: 'http://120.77.179.54:8844', // 120测试
target: 'http://192.168.32.66:8800', // 本地开发环境
target: 'http://192.168.33.46:8844', // 本地开发环境
// target: 'http://192.168.32.167:8844', // 本地开发环境1
// target: 'http://192.168.33.1:8848', // 社区版开发环境
// target: 'http://192.168.32.207:8844', // 刘本地