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 { queryOwnThree } from '@/api/system/menu'
|
||||||
import { filterAsnycRouter, MenuItem } from '@/utils/menu'
|
import { filterAsnycRouter, MenuItem } from '@/utils/menu'
|
||||||
import { isArray } from 'lodash-es'
|
import { isArray } from 'lodash-es'
|
||||||
|
import { usePermissionStore } from './permission'
|
||||||
import router from '@/router'
|
import router from '@/router'
|
||||||
|
|
||||||
const defaultOwnParams = [
|
const defaultOwnParams = [
|
||||||
|
|
@ -45,26 +46,15 @@ export const useMenuStore = defineStore({
|
||||||
}),
|
}),
|
||||||
getters: {
|
getters: {
|
||||||
hasPermission(state) {
|
hasPermission(state) {
|
||||||
return (code: string | string[]) => {
|
return (menuCode: string | string[]) => {
|
||||||
if (!code) {
|
if (!menuCode) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if (!!Object.keys(state.menus).length) {
|
if (!!Object.keys(state.menus).length) {
|
||||||
let codes: string[] = []
|
if (typeof menuCode === 'string') {
|
||||||
|
return !!this.menus[menuCode]
|
||||||
if (typeof code === 'string') {
|
|
||||||
codes.push(code)
|
|
||||||
} else {
|
|
||||||
codes = code
|
|
||||||
}
|
}
|
||||||
|
return menuCode.some(code => !!this.menus[code])
|
||||||
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 false
|
return false
|
||||||
}
|
}
|
||||||
|
|
@ -95,6 +85,8 @@ export const useMenuStore = defineStore({
|
||||||
//过滤非集成的菜单
|
//过滤非集成的菜单
|
||||||
const resp = await queryOwnThree({ paging: false, terms: defaultOwnParams })
|
const resp = await queryOwnThree({ paging: false, terms: defaultOwnParams })
|
||||||
if (resp.success) {
|
if (resp.success) {
|
||||||
|
const permission = usePermissionStore()
|
||||||
|
permission.permissions = {}
|
||||||
const { menusData, silderMenus } = filterAsnycRouter(resp.result)
|
const { menusData, silderMenus } = filterAsnycRouter(resp.result)
|
||||||
this.menus = {}
|
this.menus = {}
|
||||||
const handleMenuItem = (menu: any) => {
|
const handleMenuItem = (menu: any) => {
|
||||||
|
|
@ -104,6 +96,7 @@ export const useMenuStore = defineStore({
|
||||||
path: menuItem.path,
|
path: menuItem.path,
|
||||||
buttons: menuItem.meta.buttons
|
buttons: menuItem.meta.buttons
|
||||||
}
|
}
|
||||||
|
permission.permissions[menuItem.name] = menuItem.meta.buttons
|
||||||
if (menuItem.children && menuItem.children.length) {
|
if (menuItem.children && menuItem.children.length) {
|
||||||
handleMenuItem(menuItem.children)
|
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>
|
<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>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang='ts' name='Scene'>
|
||||||
export default {
|
import { useSceneStore } from '@/store/scene'
|
||||||
name: 'index'
|
|
||||||
}
|
const { getDetail, data } = useSceneStore()
|
||||||
|
const route = useRoute();
|
||||||
|
const loading = ref(false)
|
||||||
|
|
||||||
|
getDetail(route.query.id as string)
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped lang='less'>
|
||||||
|
.scene-warp {
|
||||||
|
padding: 24px;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
</style>
|
</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
|
<j-table
|
||||||
:columns='columns'
|
: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>
|
</page-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang='ts'>
|
<script setup lang='ts'>
|
||||||
|
import SaveModal from './Save/save.vue'
|
||||||
import type { SceneItem } from './typings'
|
import type { SceneItem } from './typings'
|
||||||
|
import { useMenuStore } from 'store/menu'
|
||||||
|
|
||||||
|
const menuStory = useMenuStore()
|
||||||
|
const visible = ref<boolean>(false)
|
||||||
|
|
||||||
const columns = [
|
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>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue