Merge branch 'dev' of github.com:jetlinks/jetlinks-ui-vue into dev
This commit is contained in:
commit
4961bb5bb2
|
@ -0,0 +1,8 @@
|
|||
import server from '@/utils/request';
|
||||
|
||||
|
||||
export const modify = (id: string, data: any) => server.put(`/scene/${id}`, data)
|
||||
|
||||
export const save = (data: any) => server.post(`/scene`, data)
|
||||
|
||||
export const detail = (id: string) => server.get(`/scene/${id}`)
|
|
@ -2,6 +2,7 @@ import { defineStore } from 'pinia'
|
|||
import { queryOwnThree } from '@/api/system/menu'
|
||||
import { filterAsnycRouter, MenuItem } from '@/utils/menu'
|
||||
import { isArray } from 'lodash-es'
|
||||
import { usePermissionStore } from './permission'
|
||||
import router from '@/router'
|
||||
|
||||
const defaultOwnParams = [
|
||||
|
@ -45,26 +46,15 @@ export const useMenuStore = defineStore({
|
|||
}),
|
||||
getters: {
|
||||
hasPermission(state) {
|
||||
return (code: string | string[]) => {
|
||||
if (!code) {
|
||||
return (menuCode: string | string[]) => {
|
||||
if (!menuCode) {
|
||||
return true
|
||||
}
|
||||
if (!!Object.keys(state.menus).length) {
|
||||
let codes: string[] = []
|
||||
|
||||
if (typeof code === 'string') {
|
||||
codes.push(code)
|
||||
} else {
|
||||
codes = code
|
||||
if (typeof menuCode === 'string') {
|
||||
return !!this.menus[menuCode]
|
||||
}
|
||||
|
||||
return codes.some(_c => {
|
||||
const menu_code = _c.split(':')
|
||||
if (menu_code.length > 1) {
|
||||
return !!this.menus[menu_code[0]]?.buttons?.includes(menu_code[1])
|
||||
}
|
||||
return false
|
||||
})
|
||||
return menuCode.some(code => !!this.menus[code])
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
@ -95,6 +85,8 @@ export const useMenuStore = defineStore({
|
|||
//过滤非集成的菜单
|
||||
const resp = await queryOwnThree({ paging: false, terms: defaultOwnParams })
|
||||
if (resp.success) {
|
||||
const permission = usePermissionStore()
|
||||
permission.permissions = {}
|
||||
const { menusData, silderMenus } = filterAsnycRouter(resp.result)
|
||||
this.menus = {}
|
||||
const handleMenuItem = (menu: any) => {
|
||||
|
@ -104,6 +96,7 @@ export const useMenuStore = defineStore({
|
|||
path: menuItem.path,
|
||||
buttons: menuItem.meta.buttons
|
||||
}
|
||||
permission.permissions[menuItem.name] = menuItem.meta.buttons
|
||||
if (menuItem.children && menuItem.children.length) {
|
||||
handleMenuItem(menuItem.children)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
import { defineStore } from 'pinia'
|
||||
import type { BranchesType, FormModelType, SceneItem } from '@/views/rule-engine/Scene/typings'
|
||||
import { detail } from '@/api/rule-engine/scene'
|
||||
import { cloneDeep, isArray } from 'lodash-es'
|
||||
import { randomString } from '@/utils/utils'
|
||||
|
||||
type DataType = {
|
||||
data: FormModelType | any
|
||||
productCache: any
|
||||
}
|
||||
|
||||
const assignmentKey = (data: any[]): any[] => {
|
||||
const onlyKey = ['when', 'then', 'terms', 'actions'];
|
||||
if (!data) return [];
|
||||
|
||||
return data.map((item: any) => {
|
||||
if (item) {
|
||||
item.key = randomString();
|
||||
Object.keys(item).some((key) => {
|
||||
if (onlyKey.includes(key) && isArray(item[key])) {
|
||||
item[key] = assignmentKey(item[key]);
|
||||
}
|
||||
});
|
||||
}
|
||||
return item;
|
||||
});
|
||||
};
|
||||
|
||||
export const defaultBranches = [
|
||||
{
|
||||
when: [
|
||||
{
|
||||
terms: [
|
||||
{
|
||||
column: undefined,
|
||||
value: undefined,
|
||||
termType: undefined,
|
||||
key: 'params_1',
|
||||
type: 'and',
|
||||
},
|
||||
],
|
||||
type: 'and',
|
||||
key: 'terms_1',
|
||||
},
|
||||
],
|
||||
key: 'branches_1',
|
||||
shakeLimit: {
|
||||
enabled: false,
|
||||
time: 1,
|
||||
threshold: 1,
|
||||
alarmFirst: false,
|
||||
},
|
||||
then: [],
|
||||
},
|
||||
];
|
||||
|
||||
const defaultOptions = {
|
||||
trigger: {},
|
||||
when: [
|
||||
{
|
||||
terms: [
|
||||
{
|
||||
terms: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const useSceneStore = defineStore({
|
||||
id: 'scene',
|
||||
state: (): DataType => {
|
||||
return {
|
||||
data: {
|
||||
trigger: { type: ''},
|
||||
options: defaultOptions,
|
||||
branches: defaultBranches,
|
||||
description: ''
|
||||
},
|
||||
productCache: {}
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
/**
|
||||
* 初始化数据
|
||||
*/
|
||||
initData() {
|
||||
|
||||
},
|
||||
/**
|
||||
* 获取详情
|
||||
* @param id
|
||||
*/
|
||||
async getDetail(id: string) {
|
||||
const resp = await detail(id)
|
||||
if (resp.success) {
|
||||
const result = resp.result as SceneItem
|
||||
const triggerType = result.triggerType
|
||||
let branches: any[] = result.branches
|
||||
|
||||
if (!branches) {
|
||||
branches = cloneDeep(defaultBranches)
|
||||
if (triggerType === 'device') {
|
||||
branches.push(null)
|
||||
}
|
||||
} else {
|
||||
const branchesLength = branches.length;
|
||||
if (
|
||||
triggerType === 'device' &&
|
||||
((branchesLength === 1 && !!branches[0]?.when?.length) || // 有一组数据并且when有值
|
||||
(branchesLength > 1 && !branches[branchesLength - 1]?.when?.length)) // 有多组否则数据,并且最后一组when有值
|
||||
) {
|
||||
branches.push(null);
|
||||
}
|
||||
}
|
||||
|
||||
this.data = {
|
||||
...result,
|
||||
trigger: result.trigger || {},
|
||||
branches: cloneDeep(assignmentKey(branches)),
|
||||
options: {...defaultOptions, ...result.options },
|
||||
}
|
||||
}
|
||||
},
|
||||
getProduct() {
|
||||
|
||||
}
|
||||
},
|
||||
getters: {
|
||||
|
||||
}
|
||||
})
|
|
@ -0,0 +1,119 @@
|
|||
<template>
|
||||
<div :class='classNames'>
|
||||
<div
|
||||
v-for='item in options'
|
||||
:key='item.value'
|
||||
:class='["trigger-way-item", modelValue === item.value ? "active" : "" ]'
|
||||
@click='handleClick(item.value)'
|
||||
>
|
||||
<div class='way-item-title'>
|
||||
<p>{{ item.label }}</p>
|
||||
<span>{{ item.tip}}</span>
|
||||
</div>
|
||||
<div class='way-item-image'>
|
||||
<img width='40' :src='item.image' />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang='ts' setup name='TriggerWay'>
|
||||
|
||||
import { getImage } from '@/utils/comm'
|
||||
|
||||
type Emit = {
|
||||
(e: 'update:modelValue', data: string): void
|
||||
}
|
||||
|
||||
const options = [
|
||||
{ value: 'device', label: '设备触发', tip: '适用于设备数据或行为满足触发条件时,执行指定的动作', image: getImage('/device-trigger.png') },
|
||||
{ value: 'manual', label: '手动触发', tip: '适用于第三方平台向物联网平台下发指令控制设备', image: getImage('/manual-trigger.png') },
|
||||
{ value: 'timing', label: '定时触发', tip: '适用于定期执行固定任务', image: getImage('/timing-trigger.png') },
|
||||
]
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
className: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits<Emit>()
|
||||
|
||||
const classNames = computed(() => {
|
||||
return {
|
||||
[props.className]: true,
|
||||
'scene-trigger-way-warp': true,
|
||||
disabled: props.disabled
|
||||
}
|
||||
})
|
||||
|
||||
const handleClick = (type: string) => {
|
||||
emit('update:modelValue', type)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang='less'>
|
||||
@import 'ant-design-vue/es/style/themes/default.less';
|
||||
|
||||
.scene-trigger-way-warp {display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16px 24px;
|
||||
width: 100%;
|
||||
|
||||
.trigger-way-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 16px;
|
||||
border: 1px solid #e0e4e8;
|
||||
border-radius: 2px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
width: 204px;
|
||||
|
||||
.way-item-title {
|
||||
p {
|
||||
margin-bottom: 8px;
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: rgba(#000, 0.35);
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.way-item-image {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
margin: 0 !important;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: @primary-color-hover;
|
||||
.way-item-image {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
border-color: @primary-color-active;
|
||||
.way-item-image {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,13 +1,35 @@
|
|||
<template>
|
||||
<page-container>
|
||||
<div class='scene-warp'>
|
||||
<div></div>
|
||||
<a-form ref='sceneForm' :model='data'>
|
||||
|
||||
</a-form>
|
||||
<PermissionButton
|
||||
type='primary'
|
||||
hasPermission='rule-engine/Scene:update'
|
||||
>
|
||||
保存
|
||||
</PermissionButton>
|
||||
<!-- <a-button type='primary' :loading='loading'>保存</a-button>-->
|
||||
</div>
|
||||
</page-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'index'
|
||||
}
|
||||
<script setup lang='ts' name='Scene'>
|
||||
import { useSceneStore } from '@/store/scene'
|
||||
|
||||
const { getDetail, data } = useSceneStore()
|
||||
const route = useRoute();
|
||||
const loading = ref(false)
|
||||
|
||||
getDetail(route.query.id as string)
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
<style scoped lang='less'>
|
||||
.scene-warp {
|
||||
padding: 24px;
|
||||
background-color: #fff;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,104 @@
|
|||
<template>
|
||||
<a-modal
|
||||
visible
|
||||
:title='title'
|
||||
:width='750'
|
||||
:confirm-loading='loading'
|
||||
:maskClosable='false'
|
||||
@cancel='emit("close")'
|
||||
@ok='handleOk'
|
||||
>
|
||||
<a-form
|
||||
layout='vertical'
|
||||
name='scene-save'
|
||||
ref="formRef"
|
||||
:model='formModel'
|
||||
>
|
||||
<a-form-item
|
||||
name='name'
|
||||
label='名称'
|
||||
:rules="[
|
||||
{ required: true, message: '请输入名称' },
|
||||
{ max: 64, message: '最多输入64个字符' }
|
||||
]"
|
||||
>
|
||||
<a-input v-model:value='formModel.name' placeholder='请输入名称' />
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
:name='["trigger", "type"]'
|
||||
label='触发方式'
|
||||
:rules="[{ required: true, message: '请选择触发方式' }]"
|
||||
>
|
||||
<TriggerWay v-model:modelValue='formModel.trigger.type' :disabled='disabled' />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script setup lang='ts'>
|
||||
|
||||
import { SceneItem } from '@/views/rule-engine/Scene/typings'
|
||||
import TriggerWay from './components/TriggerWay.vue'
|
||||
import type { PropType } from 'vue'
|
||||
import type { FormInstance } from 'ant-design-vue';
|
||||
import { save, modify } from '@/api/rule-engine/scene'
|
||||
import { useMenuStore } from 'store/menu'
|
||||
|
||||
type Emit = {
|
||||
(e: 'close'): void
|
||||
}
|
||||
|
||||
const loading = ref(false)
|
||||
const menuStory = useMenuStore()
|
||||
const formModel = reactive({
|
||||
name: '',
|
||||
trigger: {
|
||||
type: 'device'
|
||||
}
|
||||
})
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object as PropType<Partial<SceneItem>>,
|
||||
default: () => ({})
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits<Emit>()
|
||||
|
||||
const title = computed(() => {
|
||||
return props.data?.id ? '编辑' : '新增'
|
||||
})
|
||||
|
||||
const disabled = computed(() => {
|
||||
return !!props.data?.id
|
||||
})
|
||||
|
||||
const handleOk = async () => {
|
||||
if (formRef.value) {
|
||||
const values = await formRef.value.validateFields()
|
||||
let modelObj = { ...values }
|
||||
if (props.data.id) {
|
||||
modelObj = {
|
||||
...props.data,
|
||||
name: values.name
|
||||
}
|
||||
}
|
||||
loading.value = true
|
||||
const resp = props.data.id ? await modify(props.data.id, modelObj) : await save(modelObj)
|
||||
loading.value = false
|
||||
if (resp.success) {
|
||||
emit('close')
|
||||
const _id = props.data?.id || (resp.result as any).id
|
||||
menuStory.jumpPage('rule-engine/Scene/Save', {}, { triggerType: values.trigger.type, id: _id })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -5,13 +5,24 @@
|
|||
/>
|
||||
<j-table
|
||||
:columns='columns'
|
||||
/>
|
||||
>
|
||||
<template #headerTitle>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="visible = true">新增</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</j-table>
|
||||
<SaveModal v-if='visible' @close='visible = false'/>
|
||||
</page-container>
|
||||
</template>
|
||||
|
||||
<script setup lang='ts'>
|
||||
|
||||
import SaveModal from './Save/save.vue'
|
||||
import type { SceneItem } from './typings'
|
||||
import { useMenuStore } from 'store/menu'
|
||||
|
||||
const menuStory = useMenuStore()
|
||||
const visible = ref<boolean>(false)
|
||||
|
||||
const columns = [
|
||||
{
|
||||
|
@ -52,6 +63,24 @@ const columns = [
|
|||
}
|
||||
}
|
||||
]
|
||||
|
||||
/**
|
||||
* 编辑
|
||||
* @param id
|
||||
* @param triggerType 触发类型
|
||||
*/
|
||||
const handleEdit = (id: string, triggerType: string) => {
|
||||
menuStory.jumpPage('Scene/Save', { }, { triggerType: triggerType, id, type: 'edit' })
|
||||
}
|
||||
|
||||
/**
|
||||
* 查看
|
||||
* @param id
|
||||
* @param triggerType 触发类型
|
||||
*/
|
||||
const handleView = (id: string, triggerType: string) => {
|
||||
menuStory.jumpPage('Scene/Save', { }, { triggerType: triggerType, id, type: 'view' })
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
Loading…
Reference in New Issue