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

This commit is contained in:
JiangQiming 2023-03-30 15:51:41 +08:00
commit 2bccd21378
24 changed files with 306 additions and 166 deletions

View File

@ -176,8 +176,8 @@ const objectValue = ref<string>('');
const handleItemModalSubmit = () => {
myValue.value = objectValue.value.replace(/[\r\n]\s*/g, '');
modalVis.value = false;
emit('update:modelValue', objectValue.value);
emit('change', objectValue.value)
emit('update:modelValue', myValue.value);
};
//
@ -192,23 +192,23 @@ const handleFileChange = (info: UploadChangeParam<UploadFile<any>>) => {
};
const selectChange = (e: string, option: any) => {
emit('change', e, option)
emit('update:modelValue', myValue.value);
emit('change', e, option)
}
const timeChange = (e: any) => {
emit('change', e)
emit('update:modelValue', myValue.value);
emit('change', e)
}
const inputChange = (e: any) => {
emit('change', e && e.target ? e.target.value : e)
emit('update:modelValue', myValue.value);
emit('change', e && e.target ? e.target.value : e)
}
const dateChange = (e: any) => {
emit('change', e)
emit('update:modelValue', myValue.value);
emit('change', e)
}
myValue.value = props.modelValue

View File

@ -30,6 +30,7 @@
v-model:value="formData.configuration.function"
:options="[
{ label: '01线圈寄存器', value: 'Coils' },
{ label: '02离散输入寄存器', value: 'DiscreteInputs' },
{ label: '03保存寄存器', value: 'HoldingRegisters' },
{ label: '04输入寄存器', value: 'InputRegisters' },
]"
@ -122,6 +123,21 @@
"
/>
</j-form-item>
<j-form-item
label="小数保留位数"
:name="['configuration', 'codec', 'configuration', 'scale']"
>
<j-input-number
style="width: 100%"
placeholder="请输入小数保留位数"
:min="0"
:max="255"
:precision="0"
v-model:value="
formData.configuration.codec.configuration.scale
"
/>
</j-form-item>
<j-form-item
v-if="formData.configuration.function"
label="访问类型"
@ -132,7 +148,8 @@
:showImage="false"
v-model:value="formData.accessModes"
:options="
formData.configuration.function === 'InputRegisters'
formData.configuration.function === 'InputRegisters' ||
formData.configuration.function === 'DiscreteInputs'
? [{ label: '读', value: 'read' }]
: [
{ label: '读', value: 'read' },
@ -274,6 +291,7 @@ const oldPointKey = props.data.pointKey;
const InitAddress = {
Coils: 1,
DiscreteInputs: 10001,
HoldingRegisters: 40001,
InputRegisters: 30001,
};
@ -293,6 +311,7 @@ const formData = ref({
provider: undefined,
configuration: {
scaleFactor: 1,
scale: undefined,
},
},
},

View File

@ -238,6 +238,9 @@ const filterOption = (input: string, option: any) => {
const clickDelete = (value: string) => {
emits('change', value);
// todo
};
const getTargetData = (index: number, type: string) => {

View File

@ -169,18 +169,21 @@
<j-ellipsis
style="max-width: 150px"
>
{{
getParseData(
slotProps.id,
)[0]
}}({{
getParseData(
slotProps.id,
)[1]
}})
{{ getParseData(slotProps) }}
</j-ellipsis>
</div>
<div
class="ard-box-content-left-1-title"
v-else
>
<j-ellipsis
style="max-width: 150px"
>
{{
getReadParseData(slotProps)
}}
</j-ellipsis>
</div>
<span v-else>--</span>
<a
v-if="
getAccessModes(
@ -196,7 +199,7 @@
slotProps,
).includes('read')
"
@click.stop="clickRedo(slotProps)"
@click.stop="clickRead(slotProps)"
><AIcon type="RedoOutlined"
/></a>
</div>
@ -333,6 +336,7 @@ const accessModesOption = ref();
const _selectedRowKeys = ref<string[]>([]);
const checkAll = ref(false);
const spinning = ref(false);
const ReadIdMap = new Map();
const defaultParams = ref({
sorts: [{ name: 'id', order: 'desc' }],
@ -495,9 +499,14 @@ const clickEdit = async (data: object) => {
visible.writePoint = true;
current.value = cloneDeep(data);
};
const clickRedo = async (data: any) => {
const res = await readPoint(data?.collectorId, [data?.id]);
// ReadIdMap
const clickRead = async (data: any) => {
const res: any = await readPoint(data?.collectorId, [data?.id]);
if (res.status === 200) {
const readData: any = res.result[0];
const _data = ReadIdMap.get(data?.id);
ReadIdMap.set(data?.id, { ..._data, ...readData });
cancelSelect();
tableRef.value?.reload();
onlyMessage('操作成功', 'success');
@ -526,14 +535,24 @@ const getInterval = (item: Partial<Record<string, any>>) => {
const { interval } = item.configuration || '';
return !!interval ? '采集频率' + interval + 'ms' : '';
};
const getAccessModes = (item: Partial<Record<string, any>>) => {
return item?.accessModes?.map((i: any) => i?.value);
};
const getParseData = (id: string) => {
const { parseData, dataType } = propertyValue.value.get(id);
const getParseData = (item: any) => {
const { parseData, dataType } = propertyValue.value.get(item.id);
const data = isNumber(parseData) ? parseData || 0 : parseData;
return [data, dataType];
const _data = `${data}(${dataType}) `;
return _data;
};
const getReadParseData = (item: any) => {
let _data = '--';
if (ReadIdMap.has(item.id)) {
const { parseData, dataType } = ReadIdMap.get(item.id);
_data = !!parseData ? `${parseData}(${dataType || '-'}) ` : '--';
}
return _data;
};
const saveChange = (value: object) => {
@ -618,6 +637,13 @@ watch(
(value) => {
if (value.length !== 0) {
subscribeProperty(value);
value.forEach((item: any) => {
item?.accessModes?.forEach((i: any) => {
if (i?.value === 'read') {
ReadIdMap.set(item.id, item);
}
});
});
}
cancelSelect();
checkAll.value = false;

View File

@ -73,21 +73,11 @@
{{ getTypeTooltip(formData.circuitBreaker.type) }}
</p>
<j-form-item
label="双字高低位切换"
:name="['configuration', 'endian']"
v-if="visibleEndian"
:rules="LeftTreeRules.endian"
>
<template #label>
<span>
高低位切换
<j-tooltip title="统一配置所有点位的高低位切换">
<AIcon
type="QuestionCircleOutlined"
style="margin-left: 2px"
/>
</j-tooltip>
</span>
</template>
<j-card-select
:showImage="false"
v-model:value="formData.configuration.endian"
@ -99,6 +89,41 @@
:column="2"
/>
</j-form-item>
<j-form-item
label="单字高低位切换"
:name="['configuration', 'endianIn']"
v-if="visibleEndian"
:rules="LeftTreeRules.endianIn"
>
<j-card-select
:showImage="false"
v-model:value="formData.configuration.endianIn"
:options="[
{ label: 'AB', value: 'BIG' },
{ label: 'BA', value: 'LITTLE' },
]"
@change="changeCardSelectEndianIn"
:column="2"
/>
</j-form-item>
<div
style="color: #616161"
v-if="
formData.configuration.endian ||
formData.configuration.endianIn
"
>
<p>
当前内存布局:{{
endianMap.get(formData.configuration.endian)
}}{{ endianMap.get(formData.configuration.endianIn) }}
</p>
<p>
只有4字节数据类型(int32ieee754 float)
具有4种内存布局其它只有ABCDDCBA两种内存布局(以双字配置为准)
</p>
</div>
<j-form-item label="说明" name="description">
<j-textarea
placeholder="请输入说明"
@ -150,6 +175,11 @@ const emit = defineEmits(['change']);
const id = props.data.id;
const formRef = ref<FormInstance>();
const endianMap = new Map([
['BIG', 'AB'],
['LITTLE', 'BA'],
]);
const formData = ref({
channelId: undefined,
name: '',
@ -157,6 +187,7 @@ const formData = ref({
unitId: '',
type: 'LowerFrequency',
endian: 'BIG',
endianIn: 'BIG',
},
circuitBreaker: {
type: 'LowerFrequency',
@ -203,6 +234,9 @@ const changeCardSelectType = (value: Array<string>) => {
const changeCardSelectEndian = (value: Array<string>) => {
formData.value.configuration.endian = value[0];
};
const changeCardSelectEndianIn = (value: Array<string>) => {
formData.value.configuration.endianIn = value[0];
};
const getChannelNoPaging = async () => {
channelListAll.value = Store.get('channelListAll');
channelList.value = channelListAll.value.map((item) => ({

View File

@ -162,7 +162,12 @@ export const LeftTreeRules = {
},
],
type: [{ required: true, message: '请选择处理方式', trigger: 'blur' }],
endian: [{ required: true, message: '请选择高低位切换', trigger: 'blur' }],
endian: [
{ required: true, message: '请选择双字高低位切换', trigger: 'blur' },
],
endianIn: [
{ required: true, message: '请选择单字高低位切换', trigger: 'blur' },
],
};
export const FormTableColumns = [

View File

@ -66,11 +66,12 @@ import Role from './Role/index.vue';
import Menu from './Menu/index.vue';
import InitData from './InitData/index.vue';
import { modalState, formState, logoState } from './data/interface';
import { saveInit } from '@/api/initHome';
import { getInit, saveInit } from '@/api/initHome';
import { BASE_API_PATH, TOKEN_KEY } from '@/utils/variable';
import { FILE_UPLOAD } from '@/api/comm';
import { LocalStore } from '@/utils/comm';
import { message } from 'jetlinks-ui-components';
import { useUserInfo } from '@/store/userInfo';
const basicRef = ref();
const roleRef = ref();
const initDataRef = ref();
@ -81,6 +82,7 @@ const loading = ref(false);
*/
const activeKey = ref<string>('1');
const spinning = ref<boolean>(false);
const userInfo = useUserInfo();
// const action = ref<string>(`${BASE_API_PATH}/file/static`);
// const headers = ref({ [TOKEN_KEY]: LocalStore.get(TOKEN_KEY) });
/**
@ -128,6 +130,22 @@ const submitData = async () => {
}
}
};
/**
* 判断是否已有配置
*/
const judgeInitSet = async () => {
if (userInfo.$state.userInfos.username === 'admin') {
const resp: any = await getInit();
if (resp.status === 200 && resp.result.length) {
window.location.href = '/';
}
} else {
window.location.href = '/';
}
};
onMounted(() => {
judgeInitSet();
});
</script>
<style scoped lang="less">
.page-container {

View File

@ -65,10 +65,12 @@ const handleOptions = computed(() => {
{
label: _item.trueText || true,
value: _item.trueValue || true,
id: _item.trueValue || true,
},
{
label: _item.falseText || false,
value: _item.falseValue || false,
id: _item.falseValue || false,
},
];
}
@ -77,6 +79,7 @@ const handleOptions = computed(() => {
return {
label: i.text,
value: i.value,
id: i.value,
};
});
}

View File

@ -136,10 +136,12 @@ const handleOptions = computed(() => {
{
label: _item.trueText || true,
value: _item.trueValue || true,
id: _item.trueValue || true,
},
{
label: _item.falseText || false,
value: _item.falseValue || false,
id: _item.falseValue || false,
},
];
}
@ -148,6 +150,7 @@ const handleOptions = computed(() => {
return {
label: i.text,
value: i.value,
id: i.value,
};
});
}

View File

@ -326,13 +326,13 @@ const onFormSave = () => {
..._data.message,
},
});
emit('change', {
propertiesName:
deviceMessageType.value === 'INVOKE_FUNCTION'
? _function.value?.name
: _property.value?.name,
propertiesValue: modelRef.propertiesValue,
});
// emit('change', {
// propertiesName:
// deviceMessageType.value === 'INVOKE_FUNCTION'
// ? _function.value?.name
// : _property.value?.name,
// propertiesValue: modelRef.propertiesValue,
// });
})
.catch((err: any) => {
reject(err);

View File

@ -120,17 +120,25 @@ const onTypeSelect = (key: any, _index: number) => {
};
const onTagSelect = (_data: any, _index: number) => {
const newList = [...unref(tagList)];
const indexType = newList[_index].type;
newList.splice(
_index,
1,
handleItem({ ..._data, value: undefined, type: indexType }),
);
tagList.value = newList;
const indexType = tagList.value[_index].type;
const _item = handleItem({ ..._data, value: undefined, type: indexType })
tagList.value[_index] = _item
onValueChange()
};
const onValueChange = () => {
// const _data = tagList.value.filter((item) => item?.value !== undefined);
const newValue = tagList.value.map((item: any) => {
return {
column: item.id,
type: item?.type,
value: item?.value,
};
});
emits('update:value', [{ value: newValue, name: '标签' }]);
emits('change', [{ value: newValue, name: '标签' }], tagList.value);
};
watch(
() => [props.tagData, props.value],
([newTag, newVal]) => {
@ -170,19 +178,6 @@ watch(
},
);
const onValueChange = () => {
const _data = tagList.value.filter((item) => !!item?.value);
const newValue = _data.map((item: any) => {
return {
column: item.id,
type: item?.type,
value: item?.value,
};
});
emits('update:value', [{ value: newValue, name: '标签' }]);
emits('change', [{ value: newValue, name: '标签' }], _data);
};
onMounted(() => {
if(props.value?.[0]?.value){
const arr: any[] = []

View File

@ -145,10 +145,10 @@ const onSave = (_data: any) => {
item.upperKey = 'scene.deviceId';
}
const _options: any = {
name: '-', //
type: '', //
properties: '', //
propertiesValue: '', //
// name: '-', //
// type: '', //
// properties: '', //
// propertiesValue: '', //
selector: DeviceModel.selector, //
triggerName: data.value.options?.trigger?.name || '触发设备',
...DeviceOptions.value,
@ -165,15 +165,18 @@ const onSave = (_data: any) => {
_options.propertiesValue =
(typeof _options?.propertiesValue === 'object'
? JSON.stringify(_options?.propertiesValue)
: `${_options?.propertiesValue}`) ||
DeviceModel?.selectorValues?.[0]?.value;
: _options?.propertiesValue)
}
emit('save', item, _options);
emit('save', item, JSON.parse(JSON.stringify(_options)));
};
const onProductChange = (_val: any, bol: boolean) => {
if (!bol) {
DeviceModel.selectorValues = undefined;
DeviceModel.deviceId = ''
DeviceModel.selector = 'fixed',
DeviceModel.upperKey = ''
DeviceModel.source = 'fixed'
const flag = isActionChange(
JSON.parse(_val.metadata || '{}'),
DeviceModel?.message,

View File

@ -519,9 +519,10 @@ const onType = (_type: string) => {
*/
const onSave = (data: ActionsType, options: any) => {
const { key, terms } = _data.value.branches![props.branchesName].then?.[props.thenName].actions?.[props.name]
console.log({...props.options, ...options})
const actionItem: ActionsType = {
...data,
options,
options: {...props.options, ...options},
key,
terms
}

View File

@ -62,7 +62,7 @@ const handleBoolean = (key: string) => {
const click = (e: any) => {
const _key = ['true', 'false'].includes(e.key) ? handleBoolean(e.key) : e.key
const option = getOption(myOptions.value, _key, props.valueName)
myValue.value = _key
myValue.value = e.key
emit('update:value', _key)
emit('click', _key, {
key: _key,
@ -71,7 +71,7 @@ const click = (e: any) => {
}
watch(() => props.value, () => {
myValue.value = props.value
myValue.value = isBoolean(props.value) ? String(props.value) : props.value
}, { immediate: true})
</script>

View File

@ -99,14 +99,11 @@ const handleOptions = (record: any) => {
}
const valueChange = () => {
console.log('valueChange', dataSource.value)
const _value = dataSource.value.map(item => {
console.log(item.value)
return {
name: item.id, value: item.value
}
})
console.log('valueChange2', _value)
emit('update:value', _value)
emit('change', _value)
}

View File

@ -162,6 +162,7 @@ watchEffect(() => {
const option = getOption(_options, props.value as string, props.valueName) // label
myValue.value = props.value
mySource.value = props.source
console.log(option, _options, props.valueName)
if (option) {
label.value = option[props.labelName] || option.name
treeOpenKeys.value = openKeysByTree(_options, props.value, props.valueName)

View File

@ -177,8 +177,8 @@ const handOptionByColumn = (option: any) => {
if (option.dataType === 'boolean') {
valueOptions.value = option.options?.map((item: any) => ({ ...item, label: item.name, value: item.id})) || [
{ label: '是', value: true },
{ label: '否', value: false },
{ label: '是', value: true, id: true },
{ label: '否', value: false, id: false },
]
} else if(option.dataType === 'enum') {
valueOptions.value = option.options?.map((item: any) => ({ ...item, label: item.name, value: item.id})) || []

View File

@ -215,9 +215,20 @@ defineExpose({
resolve(data)
})
})
Object.assign(formModel, props.value)
formModel.when = props.value.when || []
watchEffect(() => {
if(props.value?.period.unit === 'hours') {
unitMax.
value = 99999
} else {
unitMax.value = 99
}
})
</script>
<style scoped lang='less'>

View File

@ -73,7 +73,7 @@ export const handleTimerOptions = (timer: OperationTimer):TimerOption => {
const whenStrArr = continuousValue(timer.when! || [], timer!.trigger);
const whenStrArr3 = whenStrArr.splice(0, 3);
when += whenStrArr3.join('、');
when += `${timer.when!.length}`;
when += `...${timer.when!.length}`;
}
if (timer.once) {

View File

@ -23,58 +23,59 @@
</div>
<div class="tree">
<jTree
v-if="treeData.length > 0"
:tree-data="treeData"
v-model:selected-keys="selectedKeys"
v-model:expandedKeys="expandedKeys"
: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="暂无数据" />
<j-spin :spinning='loading'>
<jTree
v-if="treeData.length > 0"
:tree-data="treeData"
v-model:selected-keys="selectedKeys"
v-model:expandedKeys="expandedKeys"
: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>
<j-empty v-else description="暂无数据" />
</j-spin>
</div>
<!-- 编辑弹窗 -->
<EditDepartmentDialog
@ -227,6 +228,9 @@ function init() {
.left-tree-container {
padding-right: 24px;
border-right: 1px solid #f0f0f0;
display: flex;
height: 100%;
flex-direction: column;
.add-btn {
margin: 24px 0;
@ -262,10 +266,17 @@ function init() {
}
}
.loading {
.tree {
overflow-y: auto;
overflow-x: hidden;
.loading {
display: flex;
width: 100%;
justify-content: center;
margin-top: 20px;
}
}
}
</style>

View File

@ -1,28 +1,30 @@
<template>
<page-container>
<div class="department-container">
<div class="department-warp">
<div class='department-container'>
<div class="left">
<LeftTree @change="(id) => (departmentId = id)" />
<LeftTree @change="(id) => (departmentId = id)" />
</div>
<div class="right">
<j-tabs v-model:activeKey="activeKey" destroyInactiveTabPane>
<j-tab-pane key="product" tab="产品">
<Product
:parentId="departmentId"
@open-device-bind="openDeviceBind"
/>
</j-tab-pane>
<j-tab-pane key="device" tab="设备">
<Device
:parentId="departmentId"
v-model:bindBool="bindBool"
/>
</j-tab-pane>
<j-tab-pane key="user" tab="用户">
<User :parentId="departmentId" />
</j-tab-pane>
</j-tabs>
<j-tabs v-model:activeKey="activeKey" destroyInactiveTabPane>
<j-tab-pane key="product" tab="产品">
<Product
:parentId="departmentId"
@open-device-bind="openDeviceBind"
/>
</j-tab-pane>
<j-tab-pane key="device" tab="设备">
<Device
:parentId="departmentId"
v-model:bindBool="bindBool"
/>
</j-tab-pane>
<j-tab-pane key="user" tab="用户">
<User :parentId="departmentId" />
</j-tab-pane>
</j-tabs>
</div>
</div>
</div>
</page-container>
</template>
@ -45,19 +47,25 @@ const openDeviceBind = () => {
</script>
<style lang="less" scoped>
.department-container {
display: flex;
background-color: #fff;
padding: 24px;
.department-warp {
background-color: #fff;
padding: 24px;
.department-container {
position: relative;
.left {
flex-basis: 300px;
}
.right {
width: calc(100% - 300px);
:deep(.ant-tabs-nav-wrap) {
padding-left: 24px;
}
}
.left {
position: absolute;
height: 100%;
width: 300px;
}
.right {
width: calc(100% - 316px);
margin-left: 316px;
:deep(.ant-tabs-nav-wrap) {
padding-left: 24px;
}
}
}
}
</style>

View File

@ -155,6 +155,7 @@ const handleParams = (params: any) => {
//
const tableRef = ref<Record<string, any>>({}); //
const table = reactive({
_selectedRowKeys: [] as string[],

View File

@ -101,6 +101,7 @@
message: '请输入页面地址',
},
{ max: 128, message: '最多可输入128字符' },
{ pattern: /^\// ,message:'请正确填写地址,以/开头'},
]"
>
<j-input

View File

@ -92,8 +92,8 @@ export default defineConfig(({ mode}) => {
[env.VITE_APP_BASE_API]: {
// target: 'http://192.168.33.22:8800',
// target: 'http://192.168.32.244:8881',
target: 'http://120.77.179.54:8844', // 120测试
// target: 'http://192.168.33.46:8844', // 本地开发环境
// target: 'http://120.77.179.54:8844', // 120测试
target: 'http://192.168.33.46:8844', // 本地开发环境
ws: 'ws://192.168.33.46:8844',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')