Merge branch 'dev' into dev-hub

This commit is contained in:
jackhoo_98 2023-03-17 09:56:09 +08:00
commit 343d88209e
15 changed files with 293 additions and 184 deletions

View File

@ -94,8 +94,8 @@ export const useSceneStore = defineStore('scene', () => {
const branchesLength = branches.length; const branchesLength = branches.length;
if ( if (
triggerType === 'device' && triggerType === 'device' &&
((branchesLength === 1 && !!branches[0]?.when?.length) || // 有一组数据并且when有值 ((branchesLength === 1 && branches[0]?.when?.length) || // 有一组数据并且when有值
(branchesLength > 1 && !branches[branchesLength - 1]?.when?.length)) // 有多组否则数据并且最后一组when有值 (branchesLength > 1 && branches[branchesLength - 1]?.when?.length)) // 有多组否则数据并且最后一组when有值
) { ) {
branches.push(null); branches.push(null);
} }

View File

@ -76,6 +76,9 @@
:rules="[ :rules="[
{ required: true, message: '请输入视频地址' }, { required: true, message: '请输入视频地址' },
{ max: 128, message: '最多可输入128个字符' }, { max: 128, message: '最多可输入128个字符' },
{
validator: validateUrl,
},
]" ]"
> >
<template #label> <template #label>
@ -237,7 +240,7 @@ watch(
* @param _rule * @param _rule
* @param value * @param value
*/ */
let validateChannelId = async (_rule: Rule, value: string) => { const validateChannelId = async (_rule: Rule, value: string) => {
// ID, ID, ID // ID, ID, ID
if (!value) return; if (!value) return;
const { result } = await ChannelApi.validateField({ const { result } = await ChannelApi.validateField({
@ -252,6 +255,21 @@ let validateChannelId = async (_rule: Rule, value: string) => {
} }
}; };
/**
* 校验视频地址
* @param _rule
* @param value
*/
const validateUrl = async (_rule: Rule, value: string) => {
console.log('value: ', value);
const reg = /(http|https|rtsp|rtmp):\/\/([\w.]+\/?)\S*/;
return new Promise((resolve, reject) => {
reg.test(value) || !value
? resolve('')
: reject('请输入正确的视频地址');
});
};
/** /**
* 提交 * 提交
*/ */

View File

@ -13,7 +13,13 @@
<j-form-item <j-form-item
label="产品名称" label="产品名称"
name="name" name="name"
:rules="{ required: true, message: '请输入产品名称' }" :rules="[
{ required: true, message: '请输入产品名称' },
{
max: 64,
message: '最多输入64个字符',
},
]"
> >
<j-input <j-input
v-model:value="formData.name" v-model:value="formData.name"
@ -25,11 +31,17 @@
<j-form-item <j-form-item
:name="item.name" :name="item.name"
:label="item.label" :label="item.label"
:rules="{ :rules="[
{
required: item.required, required: item.required,
message: item.message, message: item.message,
trigger: 'change', trigger: 'change',
}" },
{
max: 64,
message: '最多输入64个字符',
},
]"
> >
<j-select <j-select
v-if="item.type === 'enum'" v-if="item.type === 'enum'"
@ -260,7 +272,7 @@ const handleOk = () => {
res.result.id, res.result.id,
); );
if (deployResp.success) { if (deployResp.success) {
emit('save', {...res.result}) emit('save', { ...res.result });
message.success('操作成功'); message.success('操作成功');
handleCancel(); handleCancel();
} }

View File

@ -4,10 +4,14 @@
<j-card> <j-card>
<j-row :gutter="24"> <j-row :gutter="24">
<j-col :span="12"> <j-col :span="12">
<j-form layout="vertical"> <j-form ref="formRef" :model="formData" layout="vertical">
<j-form-item <j-form-item
label="接入方式" label="接入方式"
v-bind="validateInfos.channel" name="channel"
:rules="{
required: true,
message: '请选择接入方式',
}"
> >
<RadioCard <RadioCard
layout="horizontal" layout="horizontal"
@ -15,7 +19,7 @@
:checkStyle="true" :checkStyle="true"
:disabled="!!route.query.id" :disabled="!!route.query.id"
v-model="formData.channel" v-model="formData.channel"
@change="formData.productId = undefined" @change="handleChannelChange"
/> />
</j-form-item> </j-form-item>
<j-row :gutter="24"> <j-row :gutter="24">
@ -28,7 +32,24 @@
<j-col :span="16"> <j-col :span="16">
<j-form-item <j-form-item
label="ID" label="ID"
v-bind="validateInfos.id" name="id"
:rules="[
{
required:
formData.channel ===
'gb28181-2016',
message: '请输入ID',
},
{
max: 64,
message: '最多输入64个字符',
},
{
pattern: /^[a-zA-Z0-9_\-]+$/,
message:
'请输入英文或者数字或者-或者_',
},
]"
> >
<j-input <j-input
v-model:value="formData.id" v-model:value="formData.id"
@ -38,18 +59,32 @@
</j-form-item> </j-form-item>
<j-form-item <j-form-item
label="设备名称" label="设备名称"
v-bind="validateInfos.name" name="name"
:rules="[
{
required: true,
message: '请输入设备名称',
},
{
max: 64,
message: '最多可输入64个字符',
},
]"
> >
<j-input <j-input
v-model:value="formData.name" v-model:value="formData.name"
placeholder="请输入名称" placeholder="请输入设备名称"
/> />
</j-form-item> </j-form-item>
</j-col> </j-col>
</j-row> </j-row>
<j-form-item <j-form-item
label="所属产品" label="所属产品"
v-bind="validateInfos.productId" name="productId"
:rules="{
required: true,
message: '请选择所属产品',
}"
> >
<j-row :gutter="[0, 10]"> <j-row :gutter="[0, 10]">
<j-col :span="!!route.query.id ? 24 : 22"> <j-col :span="!!route.query.id ? 24 : 22">
@ -79,7 +114,17 @@
</j-form-item> </j-form-item>
<j-form-item <j-form-item
label="接入密码" label="接入密码"
v-bind="validateInfos['others.access_pwd']" :name="['others', 'access_pwd']"
:rules="[
{
required: true,
message: '请输入接入密码',
},
{
max: 64,
message: '最多可输入64个字符',
},
]"
v-if="formData.channel === 'gb28181-2016'" v-if="formData.channel === 'gb28181-2016'"
> >
<j-input-password <j-input-password
@ -90,7 +135,11 @@
<template v-if="!!route.query.id"> <template v-if="!!route.query.id">
<j-form-item <j-form-item
label="流传输模式" label="流传输模式"
v-bind="validateInfos.streamMode" name="streamMode"
:rules="{
required: true,
message: '请选择流传输模式',
}"
> >
<j-radio-group <j-radio-group
button-style="solid" button-style="solid"
@ -246,18 +295,14 @@
<script setup lang="ts"> <script setup lang="ts">
import { getImage } from '@/utils/comm'; import { getImage } from '@/utils/comm';
import { Form } from 'ant-design-vue';
import { message } from 'ant-design-vue'; import { message } from 'ant-design-vue';
import DeviceApi from '@/api/media/device'; import DeviceApi from '@/api/media/device';
import { PROVIDER_OPTIONS } from '@/views/media/Device/const'; import { PROVIDER_OPTIONS } from '@/views/media/Device/const';
import type { ProductType } from '@/views/media/Device/typings'; import type { ProductType } from '@/views/media/Device/typings';
import SaveProduct from './SaveProduct.vue'; import SaveProduct from './SaveProduct.vue';
const router = useRouter(); const router = useRouter();
const route = useRoute(); const route = useRoute();
const useForm = Form.useForm;
// //
const formData = ref({ const formData = ref({
@ -266,10 +311,10 @@ const formData = ref({
channel: 'gb28181-2016', channel: 'gb28181-2016',
photoUrl: getImage('/device-media.png'), photoUrl: getImage('/device-media.png'),
productId: undefined, productId: undefined,
description: '',
others: { others: {
access_pwd: '', access_pwd: '',
}, },
description: '',
// //
streamMode: 'UDP', streamMode: 'UDP',
manufacturer: '', manufacturer: '',
@ -277,50 +322,9 @@ const formData = ref({
firmware: '', firmware: '',
}); });
// const handleChannelChange = () => {
const formRules = ref({ formData.value.productId = undefined;
id: [
{
required: true,
message: '请输入ID',
},
{ max: 64, message: '最多输入64个字符' },
{
pattern: /^[j-zA-Z0-9_\-]+$/,
message: '请输入英文或者数字或者-或者_',
},
],
name: [
{ required: true, message: '请输入名称' },
{ max: 64, message: '最多可输入64个字符' },
],
productId: [{ required: true, message: '请选择所属产品' }],
channel: [{ required: true, message: '请选择接入方式' }],
'others.access_pwd': [{ required: true, message: '请输入接入密码' }],
description: [{ max: 200, message: '最多可输入200个字符' }],
streamMode: [{ required: true, message: '请选择流传输模式' }],
});
watch(
() => formData.value.channel,
(val) => {
formRules.value['id'][0].required = val === 'gb28181-2016';
formRules.value['others.access_pwd'][0].required =
val === 'gb28181-2016';
validate();
getProductList(); getProductList();
},
);
const { resetFields, validate, validateInfos, clearValidate } = useForm(
formData.value,
formRules.value,
);
const clearValid = () => {
setTimeout(() => {
clearValidate();
}, 200);
}; };
/** /**
@ -328,7 +332,6 @@ const clearValid = () => {
*/ */
const productList = ref<ProductType[]>([]); const productList = ref<ProductType[]>([]);
const getProductList = async () => { const getProductList = async () => {
// console.log('formData.productId: ', formData.value.productId);
const params = { const params = {
paging: false, paging: false,
sorts: [{ name: 'createTime', order: 'desc' }], sorts: [{ name: 'createTime', order: 'desc' }],
@ -352,12 +355,8 @@ const saveProductVis = ref(false);
*/ */
const getDetail = async () => { const getDetail = async () => {
const res = await DeviceApi.detail(route.query.id as string); const res = await DeviceApi.detail(route.query.id as string);
// console.log('res: ', res);
// formData.value = res.result;
Object.assign(formData.value, res.result); Object.assign(formData.value, res.result);
formData.value.channel = res.result.provider; formData.value.channel = res.result.provider;
console.log('formData.value: ', formData.value);
}; };
onMounted(() => { onMounted(() => {
@ -368,23 +367,51 @@ onMounted(() => {
* 表单提交 * 表单提交
*/ */
const btnLoading = ref<boolean>(false); const btnLoading = ref<boolean>(false);
const formRef = ref();
const handleSubmit = () => { const handleSubmit = () => {
// console.log('formData.value: ', formData.value); const {
validate() others,
id,
streamMode,
manufacturer,
model,
firmware,
...extraParams
} = formData.value;
let params: any;
if (formData.value.channel === 'fixed-media') {
//
params = !id
? extraParams
: { id, streamMode, manufacturer, model, firmware, ...extraParams };
} else {
//
params = !id
? { others, id, ...extraParams }
: {
others,
id,
streamMode,
manufacturer,
model,
firmware,
...extraParams,
};
}
formRef.value
?.validate()
.then(async () => { .then(async () => {
btnLoading.value = true; btnLoading.value = true;
let res; const res = !route.query.id
if (!route.query.id) { ? await DeviceApi.save(params)
res = await DeviceApi.save(formData.value); : await DeviceApi.update(params);
} else {
res = await DeviceApi.update(formData.value);
}
if (res?.success) { if (res?.success) {
message.success('保存成功'); message.success('保存成功');
router.back(); router.back();
} }
}) })
.catch((err) => { .catch((err: any) => {
console.log('err: ', err); console.log('err: ', err);
}) })
.finally(() => { .finally(() => {

View File

@ -328,7 +328,7 @@ const getActions = (
? '设备已离线' ? '设备已离线'
: data.state.value === 'notActive' : data.state.value === 'notActive'
? '设备已禁用' ? '设备已禁用'
: '', : '更新通道',
}, },
disabled: disabled:
data.state.value === 'offline' || data.state.value === 'offline' ||

View File

@ -82,6 +82,7 @@ import ParamsDropdown, { DoubleParamsDropdown } from '../../components/ParamsDro
import { inject } from 'vue' import { inject } from 'vue'
import { useSceneStore } from 'store/scene' import { useSceneStore } from 'store/scene'
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { flattenDeep, set } from 'lodash-es'
const sceneStore = useSceneStore() const sceneStore = useSceneStore()
const { data: formModel } = storeToRefs(sceneStore) const { data: formModel } = storeToRefs(sceneStore)
@ -207,12 +208,32 @@ const mouseout = () => {
} }
} }
const columnSelect = () => { const handleOptionsColumnsValue = (termsColumns: any[], _options: any) => {
formModel.value.branches![props.branchName].then[props.thenName].actions[props.name].options!.termsColumns = termsColumns
const flatten = new Set(flattenDeep(termsColumns))
let newColumns = [...flatten.values()]
if (_options?.otherColumns) {
newColumns = [..._options?.otherColumns, ...newColumns]
}
formModel.value.branches![props.branchName].then[props.thenName].actions[props.name].options!.columns = newColumns
}
const columnSelect = (e: any) => {
paramsValue.termType = 'eq' paramsValue.termType = 'eq'
paramsValue.value = { paramsValue.value = {
source: tabsOptions.value[0].key, source: tabsOptions.value[0].key,
value: undefined value: undefined
} }
const columns = e.metadata === true ? [e.column] : []
const _options = formModel.value.branches![props.branchName].then[props.thenName].actions[props.actionName].options
const termsColumns = _options?.termsColumns || []
set(
termsColumns,
[props.termsName, props.name],
columns
)
handleOptionsColumnsValue(termsColumns, _options)
emit('update:value', { ...paramsValue }) emit('update:value', { ...paramsValue })
} }
@ -245,6 +266,14 @@ const termAdd = () => {
const onDelete = () => { const onDelete = () => {
formModel.value.branches?.[props.branchName]?.then?.[props.thenName]?.actions?.[props.actionName].terms?.[props.termsName].terms?.splice(props.name, 1) formModel.value.branches?.[props.branchName]?.then?.[props.thenName]?.actions?.[props.actionName].terms?.[props.termsName].terms?.splice(props.name, 1)
const _options = formModel.value.branches![props.branchName].then[props.thenName].actions[props.name].options
const termsColumns = _options?.termsColumns || []
set(
termsColumns,
[props.termsName, props.name],
[]
)
handleOptionsColumnsValue(termsColumns, _options)
} }
nextTick(() => { nextTick(() => {

View File

@ -58,7 +58,7 @@ import { storeToRefs } from 'pinia'
import { useSceneStore } from 'store/scene' import { useSceneStore } from 'store/scene'
import DropdownButton from '../../components/DropdownButton' import DropdownButton from '../../components/DropdownButton'
import FilterItem from './FilterCondition.vue' import FilterItem from './FilterCondition.vue'
import { isArray } from 'lodash-es' import { flattenDeep, isArray, set } from 'lodash-es'
import { provide } from 'vue' import { provide } from 'vue'
import { randomString } from '@/utils/utils' import { randomString } from '@/utils/utils'
import { useParams } from '@/views/rule-engine/Scene/Save/util' import { useParams } from '@/views/rule-engine/Scene/Save/util'
@ -66,8 +66,6 @@ import { useParams } from '@/views/rule-engine/Scene/Save/util'
const sceneStore = useSceneStore() const sceneStore = useSceneStore()
const { data: formModel } = storeToRefs(sceneStore) const { data: formModel } = storeToRefs(sceneStore)
const props = defineProps({ const props = defineProps({
isFirst: { isFirst: {
type: Boolean, type: Boolean,
@ -103,9 +101,7 @@ const { columnOptions } = useParams({
branch: props.branchName, branch: props.branchName,
branchGroup: props.thenName, branchGroup: props.thenName,
action: props.actionName action: props.actionName
}, [ })
formModel.value.branches![props.branchName].then[props.thenName].actions[props.actionName]
])
provide('filter-params', columnOptions) provide('filter-params', columnOptions)
@ -123,6 +119,16 @@ const mouseout = () => {
showDelete.value = false showDelete.value = false
} }
const handleOptionsColumnsValue = (termsColumns: any[], _options: any) => {
formModel.value.branches![props.branchName].then[props.thenName].actions[props.name].options!.termsColumns = termsColumns
const flatten = new Set(flattenDeep(termsColumns))
let newColumns = [...flatten.values()]
if (_options?.otherColumns) {
newColumns = [..._options?.otherColumns, ...newColumns]
}
formModel.value.branches![props.branchName].then[props.thenName].actions[props.name].options!.columns = newColumns
}
const addTerms = () => { const addTerms = () => {
const item: any = { const item: any = {
type: 'and', type: 'and',
@ -154,6 +160,13 @@ const onDelete = () => {
.then[props.thenName] .then[props.thenName]
.actions[props.actionName] .actions[props.actionName]
.terms?.splice(props.name, 1) .terms?.splice(props.name, 1)
const _options = formModel.value.branches![props.branchName].then[props.thenName].actions[props.actionName].options
const termsColumns = _options?.termsColumns || []
if (_options?.termsColumns) {
termsColumns.splice(props.name, 1)
handleOptionsColumnsValue(termsColumns, _options)
}
} }
const rules = [ const rules = [

View File

@ -446,6 +446,11 @@ const termsOptions = computed(() => {
}); });
const onDelete = () => { const onDelete = () => {
if (props.name !== 0 && !props.parallel) { // options.termsColumnsterms
_data.value.branches![props.branchesName].then[props.thenName].actions[props.name - 1].options!.termsColumns = []
_data.value.branches![props.branchesName].then[props.thenName].actions[props.name - 1].options!.terms = []
_data.value.branches![props.branchesName].then[props.thenName].actions[props.name - 1].terms = []
}
emit('delete'); emit('delete');
}; };
@ -453,11 +458,16 @@ const onClose = () => {
visible.value = false; visible.value = false;
}; };
const onSave = (data: ActionsType, options?: any) => { const onSave = (data: ActionsType, options: any) => {
emit('update', data, options); const { key, terms } = _data.value.branches![props.branchesName].then?.[props.thenName].actions?.[props.name]
// setTimeout(() => { const actionItem: ActionsType = {
// getParams(); ...data,
// }, 10); options,
key,
terms
}
_data.value.branches![props.branchesName].then[props.thenName].actions.splice(props.name, 1, actionItem)
visible.value = false; visible.value = false;
}; };

View File

@ -11,7 +11,6 @@
:isLast="index === actions.length - 1" :isLast="index === actions.length - 1"
:options="item.options" :options="item.options"
@delete="_delete(item.key || '')" @delete="_delete(item.key || '')"
@update="(data, options) => _update(data, options, item)"
/> />
</template> </template>
<div class="actions-add-list" :class="{ 'border': props.actions.length }"> <div class="actions-add-list" :class="{ 'border': props.actions.length }">
@ -37,7 +36,6 @@ import { PropType } from 'vue';
import { ActionsType, ParallelType } from '../../../typings'; import { ActionsType, ParallelType } from '../../../typings';
import Modal from '../Modal/index.vue'; import Modal from '../Modal/index.vue';
import Item from './Item.vue'; import Item from './Item.vue';
import { pick } from 'lodash';
import { useSceneStore } from '@/store/scene'; import { useSceneStore } from '@/store/scene';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
@ -98,9 +96,7 @@ const _delete = (_key: string) => {
emit('delete', _key) emit('delete', _key)
} }
const _update = (data: ActionsType, options: any, item: any) => { const _update = () => {
const olData = pick(item, ['terms']);
emit('add', {...olData, ...data, options})
visible.value = false visible.value = false
} }
</script> </script>

View File

@ -131,7 +131,8 @@ const onOk = () => {
emit( emit(
'save', 'save',
{ {
...props.data, // ...props.data,
key: props.data.key,
executor: 'alarm', executor: 'alarm',
alarm: { mode: values.type }, alarm: { mode: values.type },
}, },

View File

@ -130,13 +130,14 @@ const onDelete = (_key: string, _parallel: boolean) => {
const onAdd = (actionItem: any, _parallel: boolean) => { const onAdd = (actionItem: any, _parallel: boolean) => {
const thenName = props.thenOptions.findIndex(item => item.parallel === _parallel) const thenName = props.thenOptions.findIndex(item => item.parallel === _parallel)
if (thenName !== -1) { // if (thenName !== -1) { //
const cacheAction = props.thenOptions[thenName].actions // const cacheAction = props.thenOptions[thenName].actions
const indexOf = cacheAction?.findIndex(item => item.key === actionItem.key) || -1 // const indexOf = cacheAction?.findIndex(item => item.key === actionItem.key) || -1
if (indexOf !== -1) { // if (indexOf !== -1) {
FormModel.value.branches?.[props.name].then?.[thenName].actions.splice(indexOf, 1, actionItem) // FormModel.value.branches?.[props.name].then?.[thenName].actions.splice(indexOf, 1, actionItem)
} else { // } else {
// FormModel.value.branches?.[props.name].then?.[thenName].actions.push(actionItem)
// }
FormModel.value.branches?.[props.name].then?.[thenName].actions.push(actionItem) FormModel.value.branches?.[props.name].then?.[thenName].actions.push(actionItem)
}
} else { // } else { //
const newThenItem = { const newThenItem = {
parallel: _parallel, parallel: _parallel,

View File

@ -1,5 +1,5 @@
<template> <template>
<div :class='["actions-terms-warp", props.class]'> <div :class='["actions-terms-warp", isFirst ? "first-children" : ""]'>
<div class='actions-terms-title'> <div class='actions-terms-title'>
{{ isFirst ? '当' : '否则' }} {{ isFirst ? '当' : '否则' }}
</div> </div>

View File

@ -32,13 +32,6 @@
</div> </div>
</template> </template>
</template> </template>
<!-- <j-form-item-->
<!-- v-else-->
<!-- :name='["branches", 0, "then"]'-->
<!-- :rules='rules'-->
<!-- >-->
<!-- -->
<!-- </j-form-item>-->
</div> </div>
</template> </template>

View File

@ -49,16 +49,8 @@
.actions-terms { .actions-terms {
.actions-terms-warp { .actions-terms-warp {
display: flex; display: flex;
//width: 66.66%;
margin-bottom: 24px; margin-bottom: 24px;
&.first-children {
width: 100%;
.actions-branches {
width: 66.66%;
}
}
&.first-children, &.first-children,
&:last-child { &:last-child {
margin-bottom: 0; margin-bottom: 0;
@ -212,4 +204,21 @@
} }
} }
@minWidth: 75%;
@media (min-width: 1600px) {
.actions-terms {
.actions-terms-warp {
width: @minWidth;
&.first-children {
width: 100%;
.actions-branches {
width: calc(@minWidth - 12px);
}
}
}
}
}

View File

@ -27,7 +27,7 @@ export const getParams = (params: Params, sceneModel: FormModelType): Promise<an
/** /**
* @param params * @param params
*/ */
export const useParams = (params: Params, effect: any[] = []) => { export const useParams = (params: Params) => {
const sceneStore = useSceneStore() const sceneStore = useSceneStore()
const { data: formModel } = storeToRefs(sceneStore) const { data: formModel } = storeToRefs(sceneStore)
const columnOptions = ref<any[]>([]) const columnOptions = ref<any[]>([])
@ -38,7 +38,7 @@ export const useParams = (params: Params, effect: any[] = []) => {
} }
watchEffect(() => { watchEffect(() => {
if (effect[0]) { if (formModel.value.branches![params.branch].then[params.branchGroup].actions[params.action]) {
handleParams() handleParams()
} }
}) })