Merge branch 'dev' into dev-hub

This commit is contained in:
jackhoo_98 2023-03-23 18:02:33 +08:00
commit ef9cfc902c
68 changed files with 765 additions and 498 deletions

View File

@ -1,5 +1,10 @@
<template> <template>
<ConfigProvider :locale='zhCN'> <ConfigProvider :locale='zhCN'>
<!-- <router-view v-slot="{ Component }">-->
<!-- <keep-alive>-->
<!-- <component :is="Component" />-->
<!-- </keep-alive>-->
<!-- </router-view>-->
<router-view /> <router-view />
</ConfigProvider> </ConfigProvider>
</template> </template>

View File

@ -75,11 +75,9 @@ const handleMenuClick = (e: any) => {
if(!(val?.popConfirm || val?.onClick)){ if(!(val?.popConfirm || val?.onClick)){
emits('update:isCheck', true); emits('update:isCheck', true);
emits('change', true); emits('change', true);
}
if (val?.popConfirm) {
visible.value = false;
} else {
visible.value = true; visible.value = true;
} else {
visible.value = false;
} }
_item.value = (val || {}) as any; _item.value = (val || {}) as any;
}; };

View File

@ -6,6 +6,7 @@
v-model:selectedKeys="state.selectedKeys" v-model:selectedKeys="state.selectedKeys"
:pure="state.pure" :pure="state.pure"
:breadcrumb="{ routes: breadcrumb }" :breadcrumb="{ routes: breadcrumb }"
@backClick='routerBack'
> >
<template #breadcrumbRender="slotProps"> <template #breadcrumbRender="slotProps">
<a <a
@ -14,7 +15,7 @@
> >
{{ slotProps.route.breadcrumbName }} {{ slotProps.route.breadcrumbName }}
</a> </a>
<span v-else style='cursor: pointer' >{{ slotProps.route.breadcrumbName }}</span> <span v-else style='cursor: default' >{{ slotProps.route.breadcrumbName }}</span>
</template> </template>
<template #rightContentRender> <template #rightContentRender>
<div class="right-content"> <div class="right-content">
@ -70,6 +71,10 @@ const state = reactive<StateType>({
selectedKeys: [], selectedKeys: [],
}); });
const routerBack = () => {
router.go(-1)
}
const findRouteMeta = (code: string) => { const findRouteMeta = (code: string) => {
let meta = [] let meta = []
let menuItem: any = menu.menus[code] let menuItem: any = menu.menus[code]

View File

@ -194,7 +194,7 @@ const timeChange = (e: any) => {
} }
const inputChange = (e: any) => { const inputChange = (e: any) => {
emit('change', e.target ? e.target.value : e) emit('change', e && e.target ? e.target.value : e)
} }
const dateChange = (e: any) => { const dateChange = (e: any) => {

View File

@ -51,7 +51,7 @@ export const AccountMenu = {
export default [ export default [
{ path: '/*', redirect: '/'}, { path: '/*', redirect: '/'},
{ {
path: '/login', path: LoginPath,
component: () => import('@/views/user/Login/index.vue') component: () => import('@/views/user/Login/index.vue')
}, },
{ {

26
src/store/department.ts Normal file
View File

@ -0,0 +1,26 @@
import { defineStore } from "pinia";
type DepartmentStateType = {
productId: string;
optType: string | undefined;
}
export const useDepartmentStore = defineStore({
id: 'department',
state: (): DepartmentStateType => ({
productId: '',
// 设备资产分配弹窗操作类型:
// 1. optType === 'handle': 手动点击资产分配按钮;
// 2. optType === ': 产品资产分配后, 自动弹出设备资产分配
optType: ''
}),
actions: {
setProductId(value: string) {
this.productId = value
},
setType(value: string | undefined) {
this.optType = value
}
}
})

View File

@ -109,7 +109,7 @@ export const useSceneStore = defineStore('scene', () => {
...result, ...result,
trigger: result.trigger || {}, trigger: result.trigger || {},
branches: cloneDeep(assignmentKey(branches)), branches: cloneDeep(assignmentKey(branches)),
options: result.options ? {...defaultOptions, ...result.options } : defaultOptions, options: result.options ? {...cloneDeep(defaultOptions), ...result.options } : cloneDeep(defaultOptions),
} }
} }
} }

View File

@ -148,6 +148,9 @@ const extraRouteObj = {
'edge/Device': { 'edge/Device': {
children: [{ code: 'Remote', name: '远程控制' }], children: [{ code: 'Remote', name: '远程控制' }],
}, },
'rule-engine/Alarm/Log': {
children: [{ code: 'Record', name: '处理记录' }]
}
}; };
@ -206,7 +209,7 @@ const findDetailRoutes = (routes: any[]): any[] => {
export const findCodeRoute = (asyncRouterMap: any[]) => { export const findCodeRoute = (asyncRouterMap: any[]) => {
const routeMeta = {} const routeMeta = {}
function getDetail( code: string, url: string) { function getDetail(code: string, url: string) {
const detail = findDetailRouteItem(code, url) const detail = findDetailRouteItem(code, url)
if (!detail) return if (!detail) return
routeMeta[(detail as MenuItem).code] = { routeMeta[(detail as MenuItem).code] = {
@ -217,7 +220,7 @@ export const findCodeRoute = (asyncRouterMap: any[]) => {
} }
} }
function findChildren (data: any[], code: string = '') { function findChildren(data: any[], code: string = '') {
data.forEach(route => { data.forEach(route => {
routeMeta[route.code] = { routeMeta[route.code] = {
path: route.url || route.path, path: route.url || route.path,
@ -254,7 +257,7 @@ export const findCodeRoute = (asyncRouterMap: any[]) => {
return routeMeta return routeMeta
} }
export function filterAsyncRouter(asyncRouterMap: any, parentCode = '', level = 1): { menusData: any, silderMenus: any} { export function filterAsyncRouter(asyncRouterMap: any, parentCode = '', level = 1): { menusData: any, silderMenus: any } {
const _asyncRouterMap = cloneDeep(asyncRouterMap) const _asyncRouterMap = cloneDeep(asyncRouterMap)
const menusData: any[] = [] const menusData: any[] = []
const silderMenus: any[] = [] const silderMenus: any[] = []
@ -270,7 +273,7 @@ export function filterAsyncRouter(asyncRouterMap: any, parentCode = '', level =
}, },
} }
const silder = {..._route} const silder = { ..._route }
// 查看是否有隐藏子路由 // 查看是否有隐藏子路由
route.children = findChildrenRoute(route.code, route.url, route.children) route.children = findChildrenRoute(route.code, route.url, route.children)

View File

@ -155,12 +155,10 @@ const errorHandler = (error: any) => {
Notification.error({ Notification.error({
key: '401', key: '401',
message: 'Unauthorized', message: 'Unauthorized',
description: 'Authorization verification failed' description: '用户未登录'
}) })
setTimeout(() => { setTimeout(() => {
router.replace({ location.href = `/#${LoginPath}`
path: LoginPath
})
}, 0) }, 0)
} }
} else if (error.response === undefined) { } else if (error.response === undefined) {
@ -179,10 +177,13 @@ request.interceptors.request.use(config => {
const token = LocalStore.get(TOKEN_KEY) const token = LocalStore.get(TOKEN_KEY)
// const token = store.$state.tokenAlias // const token = store.$state.tokenAlias
if (!token) { if (!token) {
// setTimeout(() => {
// router.replace({
// path: LoginPath
// })
// }, 0)
setTimeout(() => { setTimeout(() => {
router.replace({ location.href = `/#${LoginPath}`
path: LoginPath
})
}, 0) }, 0)
return config return config
} }

View File

@ -19,6 +19,7 @@
format="YYYY-MM-DD HH:mm:ss" format="YYYY-MM-DD HH:mm:ss"
valueFormat="YYYY-MM-DD HH:mm:ss" valueFormat="YYYY-MM-DD HH:mm:ss"
style="margin-left: 12px" style="margin-left: 12px"
:show-time="{ format: 'HH:mm:ss' }"
@change="rangeChange" @change="rangeChange"
v-model:value="rangeVal" v-model:value="rangeVal"
:allowClear="false" :allowClear="false"
@ -110,5 +111,5 @@ const handleBtnChange = (val: string) => {
}); });
}; };
handleBtnChange(radioValue.value); handleBtnChange(radioValue.value);
watch(() => radioValue.value, { deep: true, immediate: true }); // watch(() => radioValue.value, { deep: true, immediate: true });
</script> </script>

View File

@ -364,7 +364,7 @@ const setDevMesChartOption = (
grid: { grid: {
top: '2%', top: '2%',
bottom: '5%', bottom: '5%',
left: maxY > 100000 ? '90px' : '60px', left: maxY > 100000 ? '50px' : '70px',
right: '50px', right: '50px',
}, },
series: [ series: [

View File

@ -147,6 +147,10 @@ const columns = [
dataIndex: 'id', dataIndex: 'id',
key: 'id', key: 'id',
ellipsis: true, ellipsis: true,
search:{
type:'string',
defaultTermType: 'eq'
}
}, },
{ {
title: '设备名称', title: '设备名称',

View File

@ -162,7 +162,7 @@ import {
} from '@/api/device/instance'; } from '@/api/device/instance';
import MSelect from './MSelect.vue'; import MSelect from './MSelect.vue';
import PatchMapping from './PatchMapping.vue'; import PatchMapping from './PatchMapping.vue';
import { message } from 'ant-design-vue/es'; import { onlyMessage } from '@/utils/comm';
const columns = [ const columns = [
{ {
@ -280,7 +280,7 @@ const unbind = async (id: string) => {
}, },
); );
if (resp.status === 200) { if (resp.status === 200) {
message.success('操作成功!'); onlyMessage('操作成功!', 'success');
handleSearch(); handleSearch();
} }
} }
@ -313,7 +313,7 @@ const onSave = () => {
submitData, submitData,
); );
if (resp.status === 200) { if (resp.status === 200) {
message.success('操作成功!'); onlyMessage('操作成功!', 'success');
handleSearch(); handleSearch();
} }
} }
@ -342,7 +342,7 @@ const onAction = async (record: any) => {
submitData, submitData,
); );
if (resp.status === 200) { if (resp.status === 200) {
message.success('操作成功!'); onlyMessage('操作成功!', 'success');
handleSearch(); handleSearch();
} }
}; };

View File

@ -13,6 +13,7 @@
<j-image :src="value?.formatValue" /> <j-image :src="value?.formatValue" />
</template> </template>
<template v-else-if="['.flv', '.m3u8', '.mp4'].includes(type)"> <template v-else-if="['.flv', '.m3u8', '.mp4'].includes(type)">
<LivePlayer :url="value?.formatValue" autoplay />
</template> </template>
<template v-else> <template v-else>
<JsonViewer <JsonViewer
@ -25,6 +26,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import JsonViewer from 'vue-json-viewer'; import JsonViewer from 'vue-json-viewer';
import LivePlayer from '@/components/Player/index.vue';
const _data = defineProps({ const _data = defineProps({
type: { type: {

View File

@ -1,21 +1,16 @@
<template> <template>
<page-container <page-container
:tabList="list" :tabList="list"
:showBack="true"
:tabActiveKey="instanceStore.tabActiveKey" :tabActiveKey="instanceStore.tabActiveKey"
@tabChange="onTabChange" @tabChange="onTabChange"
> >
<template #title> <template #title>
<div>
<div style="display: flex; align-items: center"> <div style="display: flex; align-items: center">
<!-- <j-button @click="onBack" size="small">返回</j-button> -->
<div style="font-size: 24px">
{{ instanceStore.current?.name }} {{ instanceStore.current?.name }}
</div>
<j-divider type="vertical" /> <j-divider type="vertical" />
<j-space> <j-space>
<span <span style="font-size: 14px; color: rgba(0, 0, 0, 0.85)">
style="font-size: 14px; color: rgba(0, 0, 0, 0.85)"
>
状态 状态
<j-badge <j-badge
:status=" :status="
@ -28,8 +23,7 @@
</span> </span>
<PermissionButton <PermissionButton
v-if=" v-if="
instanceStore.current?.state?.value === instanceStore.current?.state?.value === 'notActive'
'notActive'
" "
type="link" type="link"
style="margin-top: -5px; padding: 0 20px" style="margin-top: -5px; padding: 0 20px"
@ -42,9 +36,7 @@
启用设备 启用设备
</PermissionButton> </PermissionButton>
<PermissionButton <PermissionButton
v-if=" v-if="instanceStore.current?.state?.value === 'online'"
instanceStore.current?.state?.value === 'online'
"
type="link" type="link"
style="margin-top: -5px; padding: 0 20px" style="margin-top: -5px; padding: 0 20px"
:popConfirm="{ :popConfirm="{
@ -59,8 +51,7 @@
v-if=" v-if="
instanceStore.current?.accessProvider === instanceStore.current?.accessProvider ===
'child-device' && 'child-device' &&
instanceStore.current?.state?.value === instanceStore.current?.state?.value === 'offline'
'offline'
" "
:title=" :title="
instanceStore.current?.features?.find( instanceStore.current?.features?.find(
@ -77,7 +68,8 @@
</j-tooltip> </j-tooltip>
</j-space> </j-space>
</div> </div>
<div style="padding-top: 24px"> </template>
<template #content>
<j-descriptions size="small" :column="4"> <j-descriptions size="small" :column="4">
<j-descriptions-item label="ID">{{ <j-descriptions-item label="ID">{{
instanceStore.current?.id instanceStore.current?.id
@ -93,8 +85,6 @@
</PermissionButton> </PermissionButton>
</j-descriptions-item> </j-descriptions-item>
</j-descriptions> </j-descriptions>
</div>
</div>
</template> </template>
<template #extra> <template #extra>
<img <img
@ -142,7 +132,7 @@ statusMap.set('notActive', 'warning');
const statusRef = ref(); const statusRef = ref();
const list = ref([ const initList = [
{ {
key: 'Info', key: 'Info',
tab: '实例信息', tab: '实例信息',
@ -163,7 +153,9 @@ const list = ref([
key: 'Log', key: 'Log',
tab: '日志管理', tab: '日志管理',
}, },
]); ];
const list = ref([...initList]);
const tabs = { const tabs = {
Info, Info,
@ -191,64 +183,7 @@ const getStatus = (id: string) => {
}); });
}; };
watch( const getDetail = () => {
() => route.params?.id,
(newId) => {
if (newId) {
instanceStore.refresh(String(newId));
getStatus(String(newId));
instanceStore.tabActiveKey = 'Info'
}
},
{ immediate: true, deep: true },
);
onMounted(() => {
instanceStore.tabActiveKey = history.state?.params?.tab || 'Info';
});
// const onBack = () => {
// menuStory.jumpPage('device/Instance');
// };
const onTabChange = (e: string) => {
instanceStore.tabActiveKey = e;
};
const handleAction = async () => {
if (instanceStore.current?.id) {
const resp = await _deploy(instanceStore.current?.id);
if (resp.status === 200) {
message.success('操作成功!');
instanceStore.refresh(instanceStore.current?.id);
}
}
};
const handleDisconnect = async () => {
if (instanceStore.current?.id) {
const resp = await _disconnect(instanceStore.current?.id);
if (resp.status === 200) {
message.success('操作成功!');
instanceStore.refresh(instanceStore.current?.id);
}
}
};
const handleRefresh = async () => {
if (instanceStore.current?.id) {
await instanceStore.refresh(instanceStore.current?.id);
message.success('操作成功');
}
};
const jumpProduct = () => {
menuStory.jumpPage('device/Product/Detail', {
id: instanceStore.current?.productId,
});
};
watchEffect(() => {
const keys = list.value.map((i) => i.key); const keys = list.value.map((i) => i.key);
if ( if (
instanceStore.current?.protocol && instanceStore.current?.protocol &&
@ -309,8 +244,67 @@ watchEffect(() => {
tab: '边缘端映射', tab: '边缘端映射',
}); });
} }
};
watch(
() => route.params?.id,
async (newId) => {
if (newId) {
await instanceStore.refresh(String(newId));
getStatus(String(newId));
list.value = [...initList];
getDetail();
instanceStore.tabActiveKey = 'Info';
}
},
{ immediate: true, deep: true },
);
onMounted(() => {
instanceStore.tabActiveKey = history.state?.params?.tab || 'Info';
}); });
const onBack = () => {
menuStory.jumpPage('device/Instance');
};
const onTabChange = (e: string) => {
instanceStore.tabActiveKey = e;
};
const handleAction = async () => {
if (instanceStore.current?.id) {
const resp = await _deploy(instanceStore.current?.id);
if (resp.status === 200) {
message.success('操作成功!');
instanceStore.refresh(instanceStore.current?.id);
}
}
};
const handleDisconnect = async () => {
if (instanceStore.current?.id) {
const resp = await _disconnect(instanceStore.current?.id);
if (resp.status === 200) {
message.success('操作成功!');
instanceStore.refresh(instanceStore.current?.id);
}
}
};
const handleRefresh = async () => {
if (instanceStore.current?.id) {
await instanceStore.refresh(instanceStore.current?.id);
message.success('操作成功');
}
};
const jumpProduct = () => {
menuStory.jumpPage('device/Product/Detail', {
id: instanceStore.current?.productId,
});
};
onUnmounted(() => { onUnmounted(() => {
statusRef.value && statusRef.value.unsubscribe(); statusRef.value && statusRef.value.unsubscribe();
}); });

View File

@ -13,7 +13,7 @@
</div> </div>
<div v-else> <div v-else>
<p>{{ type === 'active' ? '启用' : '同步' }}成功{{ count }}</p> <p>{{ type === 'active' ? '启用' : '同步' }}成功{{ count }}</p>
<p v-if="type === 'active'">启用失败:{{ errCount }}条<j-tooltip title="实例信息页面中的配置项未完善"><AIcon type="QuestionCircleOutlined" /></j-tooltip></p> <p v-if="type === 'active'">启用失败:{{ errCount }}条<j-tooltip title="实例信息页面中的配置项未完善"><AIcon style="margin-left: 5px" type="QuestionCircleOutlined" /></j-tooltip></p>
</div> </div>
</div> </div>
<template #footer> <template #footer>
@ -56,13 +56,13 @@ const getData = (api: string) => {
source.value = _source; source.value = _source;
_source.onmessage = (e: any) => { _source.onmessage = (e: any) => {
const res = JSON.parse(e.data); const res = JSON.parse(e.data);
// console.log(res)
switch (props.type) { switch (props.type) {
case 'active': case 'active':
if (res.success) { if (res.success) {
_source.close(); _source.close();
dt += res.total; dt += res.total;
count.value = dt; count.value = dt;
flag.value = false;
} else { } else {
if (res.source) { if (res.source) {
errCount.value = 1 errCount.value = 1
@ -76,6 +76,7 @@ const getData = (api: string) => {
case 'sync': case 'sync':
dt += res; dt += res;
count.value = dt; count.value = dt;
flag.value = false;
break; break;
default: default:
break; break;

View File

@ -328,6 +328,7 @@ const columns = [
title: 'ID', title: 'ID',
dataIndex: 'id', dataIndex: 'id',
key: 'id', key: 'id',
ellipsis: true,
search: { search: {
type: 'string', type: 'string',
defaultTermType: 'eq', defaultTermType: 'eq',
@ -337,6 +338,7 @@ const columns = [
title: '设备名称', title: '设备名称',
dataIndex: 'name', dataIndex: 'name',
key: 'name', key: 'name',
ellipsis: true,
search: { search: {
type: 'string', type: 'string',
first: true, first: true,
@ -346,6 +348,7 @@ const columns = [
title: '产品名称', title: '产品名称',
dataIndex: 'productName', dataIndex: 'productName',
key: 'productName', key: 'productName',
ellipsis: true,
search: { search: {
type: 'select', type: 'select',
rename: 'productId', rename: 'productId',
@ -367,6 +370,7 @@ const columns = [
dataIndex: 'createTime', dataIndex: 'createTime',
key: 'createTime', key: 'createTime',
scopedSlots: true, scopedSlots: true,
width: 200,
search: { search: {
type: 'date', type: 'date',
}, },

View File

@ -33,7 +33,7 @@
" "
> >
<j-button <j-button
style="margin: 0 0 0 20px" class="changeBtn"
size="small" size="small"
:disabled=" :disabled="
productStore.current?.count && productStore.current?.count &&
@ -568,7 +568,17 @@ const query = reactive({
}); });
const param = ref<Record<string, any>>({ const param = ref<Record<string, any>>({
pageSize: 4, pageSize: 4,
terms: [], terms: [
{
terms: [
{
column: 'channel',
termType: 'nin',
value: 'plugin',
},
],
},
],
}); });
const queryParams = ref<Record<string, any>>({}); const queryParams = ref<Record<string, any>>({});
/** /**
@ -669,49 +679,6 @@ const driver1 = new Driver({
}, },
}); });
/**
* 表格列表
*/
// const columnsMQTT: any[] = [
// {
// title: '',
// dataIndex: 'group',
// key: 'group',
// ellipsis: true,
// width: 100,
// // customCell: (record: any, index: number) => {
// // const list =
// // (config?.routes || []).sort((a: any, b: any) => a - b) || [];
// // const arr = list.filter((res: any) => {
// // // gpsNumber
// // return res?.group == record?.group;
// // });
// // if (index == 0 || list[index - 1]?.group != record?.group) {
// // return { rowSpan: arr.length };
// // } else {
// // return { rowSpan: 0 };
// // }
// // },
// },
// {
// title: 'topic',
// dataIndex: 'topic',
// key: 'topic',
// },
// {
// title: '',
// dataIndex: 'stream',
// key: 'stream',
// ellipsis: true,
// align: 'center',
// width: 100,
// },
// {
// title: '',
// dataIndex: 'description',
// key: 'description',
// },
// ];
let columnsMQTT = ref(<TableColumnType>[]); let columnsMQTT = ref(<TableColumnType>[]);
const ColumnsMQTT = [ const ColumnsMQTT = [
{ {
@ -760,38 +727,6 @@ const ColumnsHTTP = [
// scopedSlots: { customRender: 'description' }, // scopedSlots: { customRender: 'description' },
}, },
]; ];
// const columnsHTTP: any[] = [
// {
// title: '',
// dataIndex: 'group',
// key: 'group',
// ellipsis: true,
// width: 100,
// // customCell: (record: any, index: number) => {
// // const list =
// // (config?.routes || []).sort((a: any, b: any) => a - b) || [];
// // const arr = list.filter((res: any) => {
// // // gpsNumber
// // return res?.group == record?.group;
// // });
// // if (index == 0 || list[index - 1]?.group != record?.group) {
// // return { rowSpan: arr.length };
// // } else {
// // return { rowSpan: 0 };
// // }
// // },
// },
// {
// title: '',
// dataIndex: 'example',
// key: 'example',
// },
// {
// title: '',
// dataIndex: 'description',
// key: 'description',
// },
// ];
/** /**
* 获取上下行数据 * 获取上下行数据
*/ */
@ -892,6 +827,28 @@ const submitData = async () => {
accessProvider: current.value?.provider, accessProvider: current.value?.provider,
messageProtocol: current.value?.protocol, messageProtocol: current.value?.protocol,
}; };
// getConfigView(current.value?.protocol, current.value?.transport).then(
// (resp: any) => {
// metadata.value = (resp?.result[0] as ConfigMetadata) || {
// properties: [],
// };
// // udp
// if (metadata.value?.properties) {
// metadata.value?.properties.forEach((item) => {
// if (
// item.name === '' &&
// (!productStore.current?.configuration ||
// !productStore.current?.configuration.hasOwnProperty(
// item.name,
// ))
// ) {
// formData.data[item.name] =
// item.type.expands?.defaultValue;
// }
// });
// }
// },
// );
const metatdata = JSON.parse(productStore.current?.metadata || '{}'); const metatdata = JSON.parse(productStore.current?.metadata || '{}');
if (!productStore.current?.metadata) { if (!productStore.current?.metadata) {
const response = await getConfigView( const response = await getConfigView(
@ -1128,4 +1085,10 @@ nextTick(() => {
font-weight: 400; font-weight: 400;
font-size: 12px; font-size: 12px;
} }
.changeBtn{
margin: 0 0 0 20px;
color: #315EFB;
background: #ffffff;
border: 1px solid #315EFB
}
</style> </style>

View File

@ -1,7 +1,6 @@
<template> <template>
<page-container <page-container
:tabList="list" :tabList="list"
@back="onBack"
:tabActiveKey="productStore.tabActiveKey" :tabActiveKey="productStore.tabActiveKey"
@tabChange="onTabChange" @tabChange="onTabChange"
showBack="true" showBack="true"
@ -49,9 +48,21 @@
</div> </div>
</div> </div>
</div> </div>
</template>
<template #content>
<div style="padding-top: 10px"> <div style="padding-top: 10px">
<j-descriptions size="small" :column="4"> <j-descriptions size="small" :column="4">
<j-descriptions-item label="设备数量" <j-descriptions-item
label="设备数量"
:labelStyle="{
fontSize: '14px',
opacity: 0.55,
}"
:contentStyle="{
fontSize: '14px',
color: '#092EE7',
cursor: 'pointer',
}"
><span @click="jumpDevice">{{ ><span @click="jumpDevice">{{
productStore.current?.count productStore.current?.count
? productStore.current?.count ? productStore.current?.count
@ -62,18 +73,6 @@
</div> </div>
</template> </template>
<template #extra> <template #extra>
<!-- <j-popconfirm
title="确认应用配置"
@confirm="handleCofig"
okText="确定"
cancelText="取消"
>
<j-button
:disabled="productStore.current.state === 0"
type="primary"
>应用配置</j-button
>
</j-popconfirm> -->
<PermissionButton <PermissionButton
type="primary" type="primary"
:popConfirm="{ :popConfirm="{
@ -172,7 +171,9 @@ watch(
getProtocol(); getProtocol();
}, },
); );
const onBack = () => {}; const onBack = () => {
history.back();
};
const onTabChange = (e: string) => { const onTabChange = (e: string) => {
productStore.tabActiveKey = e; productStore.tabActiveKey = e;
@ -250,7 +251,7 @@ const getProtocol = async () => {
tab: '数据解析', tab: '数据解析',
}, },
]; ];
}else{ } else {
list.value = [ list.value = [
{ {
key: 'Info', key: 'Info',
@ -264,7 +265,8 @@ const getProtocol = async () => {
{ {
key: 'Device', key: 'Device',
tab: '设备接入', tab: '设备接入',
},] },
];
} }
} }
} }

View File

@ -84,7 +84,7 @@
> >
<template #title="item"> <template #title="item">
<span>{{ item.title }}</span> <span>{{ item.title }}</span>
<a-tooltip :title="item" <a-tooltip :title="item.option.tooltip"
><AIcon ><AIcon
type="QuestionCircleOutlined" type="QuestionCircleOutlined"
style="margin-left: 2px" style="margin-left: 2px"

View File

@ -563,7 +563,7 @@ const query = reactive({
}, },
}, },
{ {
title: '所属部门', title: '所属组织',
key: 'id$dim-assets', key: 'id$dim-assets',
dataIndex: 'id$dim-assets', dataIndex: 'id$dim-assets',
search: { search: {

View File

@ -196,11 +196,12 @@ const resetMetadata = async () => {
// } // }
const { id } = route.params const { id } = route.params
if (target === 'device') { if (target === 'device') {
instanceStore.refresh(id as string) await instanceStore.refresh(id as string)
} else { } else {
productStore.refresh(id as string) await productStore.getDetail(id as string)
} }
metadataStore.set('importMetadata', true) metadataStore.set('importMetadata', true)
}; };
const removeItem = async (record: MetadataItem) => { const removeItem = async (record: MetadataItem) => {

View File

@ -61,12 +61,15 @@ const close = () => {
const instanceStore = useInstanceStore() const instanceStore = useInstanceStore()
const productStore = useProductStore() const productStore = useProductStore()
const metadataMap = { const metadata = computed(() => {
const metadataMap = {
product: productStore.current?.metadata as string, product: productStore.current?.metadata as string,
device: instanceStore.current?.metadata as string, device: instanceStore.current?.metadata as string,
}; };
const metadata = metadataMap[props.type]; return metadataMap[props.type];
const value = ref(metadata) })
// const metadata = metadataMap[props.type];
const value = ref(metadata.value)
const handleExport = async () => { const handleExport = async () => {
try { try {
downloadObject( downloadObject(
@ -86,14 +89,14 @@ const handleConvertMetadata = (key: Key) => {
if (key === 'alink') { if (key === 'alink') {
value.value = ''; value.value = '';
if (metadata) { if (metadata) {
convertMetadata('to', 'alink', JSON.parse(metadata)).then(res => { convertMetadata('to', 'alink', JSON.parse(metadata.value)).then(res => {
if (res.status === 200) { if (res.status === 200) {
value.value = JSON.stringify(res.result) value.value = JSON.stringify(res.result)
} }
}); });
} }
} else { } else {
value.value = metadata; value.value = metadata.value;
} }
}; };

View File

@ -267,26 +267,26 @@ const columns = [
}), }),
}, },
}, },
{ // {
key: 'productId$product-info', // key: 'productId$product-info',
dataIndex: 'productId$product-info', // dataIndex: 'productId$product-info',
title: '接入方式', // title: '',
hideInTable: true, // hideInTable: true,
search: { // search: {
type: 'select', // type: 'select',
options: () => // options: () =>
new Promise((resolve) => { // new Promise((resolve) => {
queryGatewayList({}).then((resp: any) => { // queryGatewayList({}).then((resp: any) => {
resolve( // resolve(
resp.result.map((item: any) => ({ // resp.result.map((item: any) => ({
label: item.name, // label: item.name,
value: `accessId is ${item.id}`, // value: `accessId is ${item.id}`,
})), // })),
); // );
}); // });
}), // }),
}, // },
}, // },
{ {
dataIndex: 'deviceType', dataIndex: 'deviceType',
title: '设备类型', title: '设备类型',

View File

@ -20,7 +20,7 @@
<j-col :span="8">已下发数量{{ countErr + count }}</j-col> <j-col :span="8">已下发数量{{ countErr + count }}</j-col>
</j-row> </j-row>
<div v-if="!flag"> <div v-if="!flag">
<j-textarea :rows="20" :value="JSON.stringify(errMessage)" /> <j-textarea :rows="10" :value="JSON.stringify(errMessage)" />
</div> </div>
</j-modal> </j-modal>
</template> </template>
@ -86,7 +86,7 @@ const getData = () => {
et += 1; et += 1;
countErr.value = et; countErr.value = et;
flag.value = false; flag.value = false;
if (errMessages.length <= 5) { if (errMessages.length < 5) {
errMessages.push({ ...res }); errMessages.push({ ...res });
errMessage.value = [...errMessages]; errMessage.value = [...errMessages];
} }

View File

@ -169,7 +169,8 @@ const onCancel = () => {
<style lang="less" scoped> <style lang="less" scoped>
.search { .search {
padding: 0 0 0 24px; padding: 0px;
margin: 0px;
} }
.alert { .alert {
height: 40px; height: 40px;

View File

@ -2,7 +2,7 @@
<template> <template>
<j-form layout="vertical" :model="form" ref="formBasicRef"> <j-form layout="vertical" :model="form" ref="formBasicRef">
<j-row :span="24" :gutter="24"> <j-row :span="24" :gutter="24">
<j-col :span="10"> <j-col :span="12">
<j-form-item <j-form-item
label="系统名称" label="系统名称"
name="title" name="title"
@ -193,7 +193,7 @@
</j-col> </j-col>
</j-row> </j-row>
</j-col> </j-col>
<j-col :span="14"> <j-col :span="12">
<j-form-item label="登录背景图"> <j-form-item label="登录背景图">
<div class="upload-image-warp-back"> <div class="upload-image-warp-back">
<div class="upload-image-border-back"> <div class="upload-image-border-back">

View File

@ -183,6 +183,7 @@ defineExpose({
flex: 1 1 auto; flex: 1 1 auto;
font-weight: 700; font-weight: 700;
font-size: 16px; font-size: 16px;
text-align: center;
} }
} }
.role-item-content { .role-item-content {

View File

@ -237,6 +237,11 @@ const getData = (
return new Promise((resolve) => { return new Promise((resolve) => {
queryFlow(start, end, { queryFlow(start, end, {
orderBy: 'date', orderBy: 'date',
terms: [{
column : "cardId",
termType: "eq",
value: route.params.id
}]
}).then((resp: any) => { }).then((resp: any) => {
if (resp.status === 200) { if (resp.status === 200) {
const sortArray = resp.result.sort( const sortArray = resp.result.sort(

View File

@ -232,8 +232,8 @@ const handleOk = () => {
btnLoading.value = true; btnLoading.value = true;
const resp = const resp =
props.type === 'add' props.type === 'add'
? await add(toRaw(modelRef)) ? await add(toRaw(modelRef)).catch(err => err)
: await edit(toRaw(modelRef)); : await edit(toRaw(modelRef)).catch(err => err);
btnLoading.value = false; btnLoading.value = false;
if (resp.status === 200) { if (resp.status === 200) {
message.success('操作成功') message.success('操作成功')

View File

@ -457,6 +457,7 @@ const columns = [
width: 200, width: 200,
search: { search: {
type: 'string', type: 'string',
defaultTermType: 'eq'
}, },
}, },
{ {
@ -466,6 +467,9 @@ const columns = [
ellipsis: true, ellipsis: true,
scopedSlots: true, scopedSlots: true,
width: 200, width: 200,
search: {
type: 'string'
}
}, },
{ {
title: '平台对接', title: '平台对接',
@ -568,10 +572,11 @@ const columns = [
search: { search: {
type: 'select', type: 'select',
options: [ options: [
{ label: '正常', value: 'using' }, { label: '激活', value: 'using' },
{ label: '未激活', value: 'toBeActivated' }, { label: '未激活', value: 'toBeActivated' },
{ label: '停机', value: 'deactivate' }, { label: '停机', value: 'deactivate' },
], { label: '其它', value: 'using,toBeActivated,deactivate' },
]
}, },
}, },
{ {
@ -737,7 +742,16 @@ const getActions = (
}; };
const handleSearch = (e: any) => { const handleSearch = (e: any) => {
params.value = e; const newParams = (e?.terms as any[])?.map(item1 => {
item1.terms = item1.terms.map((item2: any) => {
if (['cardStateType'].includes(item2.column)) {
item2.termType = 'nin'
}
return item2
})
return item1
})
params.value = { terms: newParams || [] };
}; };
const onSelectChange = (keys: string[], rows: []) => { const onSelectChange = (keys: string[], rows: []) => {

View File

@ -87,7 +87,7 @@
</page-container> </page-container>
</template> </template>
<script setup lang="ts"> <script setup lang="ts" name='IotCardHome'>
import { getImage } from '@/utils/comm'; import { getImage } from '@/utils/comm';
import Guide from '../components/Guide.vue'; import Guide from '../components/Guide.vue';
import moment from 'moment'; import moment from 'moment';
@ -113,14 +113,14 @@ const menuHasPermission = useMenuStore().hasPermission;
const btnHasPermission = usePermissionStore().hasPermission; const btnHasPermission = usePermissionStore().hasPermission;
// //
const dashBoardUrl = menuHasPermission('/iot-card/Dashboard'); const dashBoardUrl = menuHasPermission('iot-card/Dashboard');
const platformUrl = menuHasPermission('/iot-card/Platform/Detail'); const platformUrl = menuHasPermission('iot-card/Platform/Detail');
const recordUrl = menuHasPermission('/iot-card/Record'); const recordUrl = menuHasPermission('iot-card/Record');
const cardUrl = menuHasPermission('/iot-card/CardManagement'); const cardUrl = menuHasPermission('iot-card/CardManagement');
// //
const paltformPermission = btnHasPermission(`/iot-card/Platform:add`); const paltformPermission = btnHasPermission(`iot-card/Platform:add`);
const cardPermission = btnHasPermission(`/iot-card/CardManagement:add`); const cardPermission = btnHasPermission(`iot-card/CardManagement:add`);
const Image = { const Image = {
1: getImage('/home/1.png'), 1: getImage('/home/1.png'),
@ -179,23 +179,19 @@ const pieChartData = ref<any[]>([
]); ]);
const jumpPage = (data: GuideItemProps) => { const jumpPage = (data: GuideItemProps) => {
console.log(data.auth)
if (!data.auth){ if (!data.auth){
message.warning('暂无权限,请联系管理员'); message.warning('暂无权限,请联系管理员');
return return
} }
if (data.key === 'EQUIPMENT') { if (data.key === 'EQUIPMENT') {
menuStory.jumpPage(data.url, { id: 'add' }); menuStory.jumpPage(data.url, { id: ':id'});
} else { } else {
menuStory.jumpPage(data.url); menuStory.jumpPage(data.url);
} }
}; };
const jumpDashboard = () => { const jumpDashboard = () => {
// if (dashBoardUrl) {
// router.push(`${dashBoardUrl}`);
// } else {
// message.warning('');
// }
menuStory.jumpPage('iot-card/Dashboard'); menuStory.jumpPage('iot-card/Dashboard');
}; };

View File

@ -204,6 +204,7 @@ const rules = {
}; };
const getDetail = async () => { const getDetail = async () => {
console.log(route.params)
if (route.params.id === ':id') return; if (route.params.id === ':id') return;
const resp: any = await queryById(route.params.id); const resp: any = await queryById(route.params.id);
if (resp.status === 200) { if (resp.status === 200) {

View File

@ -238,7 +238,7 @@ const handleSearch = (e: any) => {
* 新增 * 新增
*/ */
const handleAdd = () => { const handleAdd = () => {
menuStory.jumpPage('iot-card/Platform/Detail', { id: 'add' }) menuStory.jumpPage('iot-card/Platform/Detail', { id: ':id' })
}; };
</script> </script>

View File

@ -16,8 +16,9 @@
</j-radio-button> </j-radio-button>
</j-radio-group> </j-radio-group>
<j-range-picker <j-range-picker
format="YYYY-MM-DD" format="YYYY-MM-DD HH:mm:ss"
valueFormat="YYYY-MM-DD" valueFormat="YYYY-MM-DD HH:mm:ss"
:show-time="{ format: 'HH:mm:ss' }"
style="margin-left: 12px" style="margin-left: 12px"
@change="rangeChange" @change="rangeChange"
v-model:value="rangeVal" v-model:value="rangeVal"
@ -28,7 +29,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import moment from 'moment'; import dayjs from 'dayjs';
import { PropType } from 'vue'; import { PropType } from 'vue';
interface BtnOptions { interface BtnOptions {
@ -70,38 +71,37 @@ const rangeVal = ref<[string, string]>();
const rangeChange = (val: any) => { const rangeChange = (val: any) => {
radioValue.value = undefined; radioValue.value = undefined;
emit('change', { emit('change', {
start: moment(val[0]).valueOf(), start: dayjs(val[0]).valueOf(),
end: moment(val[1]).valueOf(), end: dayjs(val[1]).valueOf(),
type: undefined, type: undefined,
}); });
}; };
const getTimeByType = (type: string) => { const getTimeByType = (type?: string) => {
switch (type) { switch (type) {
case 'hour': case 'hour':
return moment().subtract(1, 'hours').valueOf(); return dayjs().subtract(1, 'hours').valueOf();
case 'week': case 'week':
return moment().subtract(6, 'days').valueOf(); return dayjs().subtract(6, 'days').valueOf();
case 'month': case 'month':
return moment().subtract(29, 'days').valueOf(); return dayjs().subtract(29, 'days').valueOf();
case 'year': case 'year':
return moment().subtract(365, 'days').valueOf(); return dayjs().subtract(365, 'days').valueOf();
default: default:
return moment().startOf('day').valueOf(); return dayjs().startOf('day').valueOf();
} }
}; };
const handleBtnChange = (val: string) => { const handleBtnChange = (val?: string) => {
radioValue.value = val; let endTime = dayjs(new Date()).valueOf();
let endTime = moment(new Date()).valueOf();
let startTime = getTimeByType(val); let startTime = getTimeByType(val);
if (val === 'yesterday') { if (val === 'yesterday') {
startTime = moment().subtract(1, 'days').startOf('day').valueOf(); startTime = dayjs().subtract(1, 'days').startOf('day').valueOf();
endTime = moment().subtract(1, 'days').endOf('day').valueOf(); endTime = dayjs().subtract(1, 'days').endOf('day').valueOf();
} }
rangeVal.value = [ rangeVal.value = [
moment(startTime).format('YYYY-MM-DD'), dayjs(startTime).format('YYYY-MM-DD HH:mm:ss'),
moment(endTime).format('YYYY-MM-DD'), dayjs(endTime).format('YYYY-MM-DD HH:mm:ss'),
]; ];
emit('change', { emit('change', {
start: startTime, start: startTime,
@ -110,11 +110,8 @@ const handleBtnChange = (val: string) => {
}); });
}; };
watch( nextTick(() => {
() => radioValue.value, handleBtnChange(radioValue.value)
(val) => { })
handleBtnChange(val);
},
{ deep: true, immediate: true },
);
</script> </script>

View File

@ -20,7 +20,7 @@
</j-col> </j-col>
</j-row> </j-row>
<j-form-item label="级别" name="level"> <j-form-item label="级别" name="level">
<j-radio-group v-model:value="form.level"> <j-radio-group v-model:value="form.level" class="levelSelect">
<j-radio-button <j-radio-button
v-for="(item, index) in levelOption" v-for="(item, index) in levelOption"
:key="index" :key="index"
@ -199,7 +199,11 @@ queryData();
<style lang="less" scoped> <style lang="less" scoped>
.ant-radio-button-wrapper { .ant-radio-button-wrapper {
margin: 10px 15px 0 0; margin: 10px 15px 0 0;
width: 125px; width: 20%;
height: 100%; height: 100%;
} }
.levelSelect{
display: flex;
width: 100%;
}
</style> </style>

View File

@ -1,13 +1,5 @@
<template> <template>
<j-modal <page-container>
visible
title="处理记录"
:width="1200"
cancelText="取消"
okText="确定"
@ok="clsoeModal"
@cancel="clsoeModal"
>
<pro-search <pro-search
:columns="columns" :columns="columns"
target="bind-channel" target="bind-channel"
@ -30,7 +22,7 @@
<span> <span>
{{ {{
dayjs(slotsProps.handleTime).format( dayjs(slotsProps.handleTime).format(
'YYYY-MM-DD HH:mm:ss' 'YYYY-MM-DD HH:mm:ss',
) )
}} }}
</span> </span>
@ -41,29 +33,25 @@
<template #alarmTime="slotProps"> <template #alarmTime="slotProps">
<span> <span>
{{ {{
dayjs(slotProps.alarmTime).format( dayjs(slotProps.alarmTime).format('YYYY-MM-DD HH:mm:ss')
'YYYY-MM-DD HH:mm:ss',
)
}} }}
</span> </span>
</template> </template>
</JProTable> </JProTable>
</j-modal> </page-container>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { queryHandleHistory } from '@/api/rule-engine/log'; import { queryHandleHistory } from '@/api/rule-engine/log';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
const props = defineProps({ import { useRoute } from 'vue-router';
data: { const route = useRoute();
type: Object, const id = route.query?.id;
},
});
const terms = [ const terms = [
{ {
column: 'alarmRecordId', column: 'alarmRecordId',
termType: 'eq', termType: 'eq',
value: props.data.id, value: id,
type: 'and', type: 'and',
}, },
]; ];
@ -119,9 +107,6 @@ const emit = defineEmits(['closeLog']);
/** /**
* 关闭弹窗 * 关闭弹窗
*/ */
const clsoeModal = () => {
emit('closeLog');
};
const handleSearch = (e: any) => { const handleSearch = (e: any) => {
params.value = e; params.value = e;

View File

@ -62,8 +62,8 @@
</span> </span>
</Ellipsis> </Ellipsis>
<j-row :gutter="24"> <j-row :gutter="24">
<j-col :span="8"> <j-col :span="8" class="content-left">
<div class="content-des-title"> <div class="content-left-title">
{{ titleMap.get(slotProps.targetType) }} {{ titleMap.get(slotProps.targetType) }}
</div> </div>
<Ellipsis <Ellipsis
@ -73,7 +73,7 @@
> >
</j-col> </j-col>
<j-col :span="8"> <j-col :span="8">
<div class="content-des-title"> <div class="content-right-title">
最近告警时间 最近告警时间
</div> </div>
<Ellipsis <Ellipsis
@ -87,7 +87,7 @@
> >
</j-col> </j-col>
<j-col :span="8"> <j-col :span="8">
<div class="content-des-title">状态</div> <div class="content-right-title">状态</div>
<BadgeStatus <BadgeStatus
:status="slotProps.state.value" :status="slotProps.state.value"
:statusName="{ :statusName="{
@ -136,11 +136,6 @@
v-if="data.solveVisible" v-if="data.solveVisible"
@closeSolve="closeSolve" @closeSolve="closeSolve"
/> />
<SolveLog
:data="data.current"
v-if="data.logVisible"
@closeLog="closeLog"
/>
</div> </div>
</template> </template>
@ -292,7 +287,7 @@ const orgCol = [
}, },
]; ];
let params = ref({ let params:any = ref({
sorts: [{ name: 'alarmTime', order: 'desc' }], sorts: [{ name: 'alarmTime', order: 'desc' }],
terms: [], terms: [],
}); });
@ -412,8 +407,11 @@ const getActions = (
}, },
icon: 'FileTextOutlined', icon: 'FileTextOutlined',
onClick: () => { onClick: () => {
data.value.current = currentData; menuStory.jumpPage(
data.value.logVisible = true; 'rule-engine/Alarm/Log/Record',
{},
{ id: currentData.id },
);
}, },
}, },
]; ];
@ -434,4 +432,14 @@ const closeLog = () => {
}; };
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.content-left{
border-right: .2px solid rgba(0,0,0,0.2);
}
.content-right-title{
color: #666;
font-size: 12px
}
.content-left-title{
font-size: 18px
}
</style> </style>

View File

@ -38,7 +38,7 @@
:statusText="slotProps.state?.text" :statusText="slotProps.state?.text"
@click="openRuleEditor" @click="openRuleEditor"
:statusNames="{ :statusNames="{
started: 'success', started: 'processing',
disable: 'error', disable: 'error',
}" }"
> >
@ -88,17 +88,17 @@
</CardBox> </CardBox>
</template> </template>
<template #state="slotProps"> <template #state="slotProps">
<j-badge <BadgeStatus
:text=" :text="
slotProps.state?.value === 'started' slotProps.state?.value === 'started'
? '正常' ? '正常'
: '禁用' : '禁用'
" "
:status=" :status="slotProps.state?.value"
slotProps.state?.value === 'started' :statusNames="{
? 'success' started: 'processing',
: 'error' disable: 'error',
" }"
/> />
</template> </template>
<template #action="slotProps"> <template #action="slotProps">

View File

@ -3,7 +3,7 @@
title='触发规则' title='触发规则'
visible visible
:width='820' :width='820'
@click='save' @ok='save'
@cancel='cancel' @cancel='cancel'
:maskClosable="false" :maskClosable="false"
> >

View File

@ -3,7 +3,7 @@
:columns="columns" :columns="columns"
type='simple' type='simple'
@search="handleSearch" @search="handleSearch"
class='search' class="scene-search"
target="scene-triggrt-device-device" target="scene-triggrt-device-device"
/> />
<j-divider style='margin: 0' /> <j-divider style='margin: 0' />

View File

@ -22,11 +22,16 @@
<j-form-item>定时调用所选功能</j-form-item> <j-form-item>定时调用所选功能</j-form-item>
</j-col> </j-col>
<j-col :span='24'> <j-col :span='24'>
<j-form-item
name='functionData'
:rules="rules"
>
<FunctionCall <FunctionCall
:value='_value' v-model:value='formModel.functionData'
:data='functionData' :data='functionData'
@change='callDataChange' @change='callDataChange'
/> />
</j-form-item>
</j-col> </j-col>
</j-row> </j-row>
</j-form> </j-form>
@ -66,9 +71,9 @@ const props = defineProps({
const emit = defineEmits<Emit>() const emit = defineEmits<Emit>()
const invokeForm = ref() const invokeForm = ref()
const formModel = reactive({ const formModel = reactive({
functionId: props.functionId functionId: props.functionId,
functionData: props.functionParameters
}) })
const _value = ref<any[]>(props.functionParameters)
/** /**
* 获取当前选择功能属性 * 获取当前选择功能属性
@ -94,13 +99,29 @@ const functionData = computed(() => {
return arrCache return arrCache
}) })
const rules = [{
validator(_: string, value: any) {
if (!value?.length && functionData.value.length) {
return Promise.reject('请输入功能值')
} else {
let hasValue = value.find((item: { name: string, value: any}) => !item.value)
if (hasValue) {
const functionItem = functionData.value.find((item: any) => item.id === hasValue.name)
return Promise.reject(functionItem?.name ? `请输入${functionItem?.name}` : '请输入功能值')
}
}
return Promise.resolve();
}
}]
const onSelect = (v: string, item: any) => { const onSelect = (v: string, item: any) => {
formModel.functionData = []
emit('update:action', `执行${item.name}`) emit('update:action', `执行${item.name}`)
emit('update:functionId', v) emit('update:functionId', v)
emit('update:functionParameters', [])
} }
const callDataChange = (v: any[]) => { const callDataChange = (v: any[]) => {
_value.value = v
emit('update:functionParameters', v) emit('update:functionParameters', v)
} }

View File

@ -3,7 +3,7 @@
:columns="columns" :columns="columns"
type='simple' type='simple'
@search="handleSearch" @search="handleSearch"
class='search' class="scene-search"
target="scene-triggrt-device-device" target="scene-triggrt-device-device"
/> />
<j-divider style='margin: 0' /> <j-divider style='margin: 0' />

View File

@ -1,5 +1,5 @@
<template> <template>
<div class='actions-branches-item'> <div class='manual actions-branches-item'>
<j-form-item <j-form-item
:rules="actionRules" :rules="actionRules"
:name="['branches', 0, 'then']" :name="['branches', 0, 'then']"
@ -32,6 +32,21 @@ const actionRules = [{
</script> </script>
<style scoped> <style scoped lang='less'>
@minWidth: 75%;
.manual {
&.actions-branches-item {
width: 100%;
}
}
@media (min-width: 1600px) {
.manual {
&.actions-branches-item {
width: @minWidth;
}
}
}
</style> </style>

View File

@ -3,7 +3,7 @@
title='触发规则' title='触发规则'
visible visible
:width='820' :width='820'
@click='save' @ok='save'
@cancel='cancel' @cancel='cancel'
> >
<Timer <Timer
@ -15,17 +15,16 @@
<script setup lang="ts" name="timerAddModel"> <script setup lang="ts" name="timerAddModel">
import Timer from '../components/Timer' import Timer from '../components/Timer'
import type { OperationTimer } from '@/views/rule-engine/Scene/typings' import type { OperationTimer } from '@/views/rule-engine/Scene/typings'
import {nextTick, PropType} from "vue"; import { PropType} from "vue";
import {TriggerDevice} from "@/views/rule-engine/Scene/typings";
import {handleTimerOptions} from "@/views/rule-engine/Scene/Save/components/Timer/util"; import {handleTimerOptions} from "@/views/rule-engine/Scene/Save/components/Timer/util";
type Emit = { type Emit = {
(e: 'cancel'): void (e: 'cancel'): void
(e: 'save', data: TriggerDevice, options: Record<string, any>): void (e: 'save', data: OperationTimer, options: Record<string, any>): void
} }
const props = defineProps({ const props = defineProps({
timer: { value: {
type: Object as PropType<OperationTimer>, type: Object as PropType<OperationTimer>,
default: () => ({}) default: () => ({})
} }
@ -39,14 +38,14 @@ interface AddModelType {
} }
const addModel = reactive<AddModelType>({ const addModel = reactive<AddModelType>({
timer: props.timer timer: props.value
}) })
const save = async () => { const save = async () => {
const timerData = await timerRef.value?.validateFields() const timerData = await timerRef.value?.validateFields()
if (timerData) { if (timerData) {
const options = handleTimerOptions(timerData) const options = handleTimerOptions(addModel.timer)
emit("save", timerData, options) emit("save", addModel.timer, options)
} }
} }
@ -54,9 +53,9 @@ const cancel = () => {
emit("cancel") emit("cancel")
} }
nextTick(() => { // watchEffect(() => {
Object.assign(addModel, props.timer) // addModel.timer = props.value
}) // })
</script> </script>

View File

@ -0,0 +1,58 @@
<template>
<div :class='["trigger-options-content", isAdd ? "is-add" : ""]'>
<span v-if='!isAdd'> 点击配置定时触发 </span>
<template v-else>
<div class='center-item'>
<AIcon v-if='options.selectorIcon' :type='options.selectorIcon' class='icon-padding-right' />
<span class='trigger-options-name'>
<Ellipsis style='max-width: 310px'>
{{ options.name }}
</Ellipsis>
</span>
<span v-if='options.extraName'>{{ options.extraName }}</span>
</div>
<div v-if='options.when'>
<span className='trigger-options-when'>{{ options.when }}</span>
</div>
<div v-if='options.time'>
<span className='trigger-options-time'>{{ options.time }}</span>
</div>
<div v-if='options.extraTime'>
<span className='trigger-options-extraTime'>{{ options.extraTime }}</span>
</div>
</template>
</div>
</template>
<script setup lang='ts' name='TimerTitle'>
const props = defineProps({
options: {
type: Object,
default: () => ({})
}
})
const isAdd = computed(() => {
console.log(props.options, Object.keys(props.options).length)
return !!Object.keys(props.options).length
})
</script>
<style scoped lang='less'>
.trigger-options-content {
display: inline-flex;
gap: 16px;
.center-item {
display: flex;
align-items: center;
}
.icon-padding-right {
padding-right: 4px;
}
}
</style>

View File

@ -2,7 +2,7 @@
<div class='timer'> <div class='timer'>
<j-form-item <j-form-item
:rules="rules" :rules="rules"
name="timer" :name="['trigger', 'timer']"
> >
<template #label> <template #label>
<TitleComponent data='触发规则' style='font-size: 14px;' /> <TitleComponent data='触发规则' style='font-size: 14px;' />
@ -29,8 +29,7 @@
v-if="visible" v-if="visible"
@cancel='visible = false' @cancel='visible = false'
@save="save" @save="save"
:value="data.trigger.device" :value="data.trigger.timer"
:options="data.options.trigger"
/> />
</div> </div>
</template> </template>
@ -41,6 +40,7 @@ import { storeToRefs } from 'pinia';
import Action from '../action/index.vue'; import Action from '../action/index.vue';
import AddModel from './AddModal.vue' import AddModel from './AddModal.vue'
import AddButton from '../components/AddButton.vue' import AddButton from '../components/AddButton.vue'
import Title from './Title.vue'
import type { OperationTimer, BranchesThen } from '@/views/rule-engine/Scene/typings' import type { OperationTimer, BranchesThen } from '@/views/rule-engine/Scene/typings'
const sceneStore = useSceneStore(); const sceneStore = useSceneStore();
@ -49,6 +49,7 @@ const visible = ref(false)
const rules = [{ const rules = [{
validator(_: any, v: any) { validator(_: any, v: any) {
console.log(v)
if (!v) { if (!v) {
return Promise.reject(new Error('请配置定时触发规则')); return Promise.reject(new Error('请配置定时触发规则'));
} }
@ -85,5 +86,21 @@ const save = (_data: OperationTimer, options: Record<string, any>) => {
} }
</script> </script>
<style scoped> <style scoped lang='less'>
@minWidth: 75%;
.timer {
.actions-branches-item {
width: 100%;
}
}
@media (min-width: 1600px) {
.timer {
.actions-branches-item {
width: @minWidth;
}
}
}
</style> </style>

View File

@ -3,7 +3,7 @@
:columns="columns" :columns="columns"
type="simple" type="simple"
@search="handleSearch" @search="handleSearch"
class="search" class="scene-search"
target="scene-trigger-device-product" target="scene-trigger-device-product"
/> />
<j-divider style="margin: 0" /> <j-divider style="margin: 0" />

View File

@ -22,6 +22,7 @@
showSearch showSearch
placeholder="请选择功能" placeholder="请选择功能"
v-model:value="modelRef.message.functionId" v-model:value="modelRef.message.functionId"
@select='functionSelect'
> >
<j-select-option <j-select-option
v-for="item in metadata?.functions || []" v-for="item in metadata?.functions || []"
@ -34,7 +35,7 @@
<j-form-item <j-form-item
v-if="modelRef.message.functionId" v-if="modelRef.message.functionId"
:name="['message', 'inputs']" :name="['message', 'inputs']"
:rules="[{ required: true, message: '请输入功能值' }]" :rules="functionRules"
> >
<EditTable <EditTable
:functions="functions" :functions="functions"
@ -138,6 +139,25 @@ const modelRef = reactive({
}, },
}); });
const functionSelect = () => {
modelRef.message.inputs = []
}
const functionRules = [{
validator(_: string, value: any) {
if (!value?.length && functions.value.length) {
return Promise.reject('请输入功能值')
} else {
const hasValue = value.find((item: { name: string, value: any}) => !item.value)
if (hasValue) {
const functionItem = functions.value.find((item: any) => item.id === hasValue.name)
return Promise.reject(functionItem?.name ? `请输入${functionItem.name}` : '请输入功能值')
}
}
return Promise.resolve();
}
}]
const metadata = ref<{ const metadata = ref<{
functions: any[]; functions: any[];
properties: any[]; properties: any[];

View File

@ -3,7 +3,7 @@
:columns="columns" :columns="columns"
type="simple" type="simple"
@search="handleSearch" @search="handleSearch"
class="search" class="scene-search"
target="scene-trigger-device-device" target="scene-trigger-device-device"
/> />
<j-divider style="margin: 0" /> <j-divider style="margin: 0" />

View File

@ -101,6 +101,7 @@ const valueChange = debounce(() => {
const _value = dataSource.value.map(item => ({ const _value = dataSource.value.map(item => ({
name: item.id, value: item.value name: item.id, value: item.value
})) }))
console.log(_value)
emit('change', _value) emit('change', _value)
emit('update:value', _value) emit('update:value', _value)
}, 500) }, 500)

View File

@ -17,6 +17,7 @@ import { numberToString } from './util'
type Emit = { type Emit = {
(e: 'update:value', data: Array<number>):void (e: 'update:value', data: Array<number>):void
(e: 'change', data: Array<number>):void
} }
const props = defineProps({ const props = defineProps({
@ -48,6 +49,7 @@ const change = (number: number) => {
} }
rowKeys.value = [..._keys.values()] rowKeys.value = [..._keys.values()]
emit('update:value', rowKeys.value) emit('update:value', rowKeys.value)
emit('change', rowKeys.value)
} }
const allActive = computed(() => { const allActive = computed(() => {
@ -57,7 +59,9 @@ const allActive = computed(() => {
watch(() => props.type, () => { watch(() => props.type, () => {
const isMonth = props.type === 'month' const isMonth = props.type === 'month'
const day = isMonth ? 31 : 7 const day = isMonth ? 31 : 7
if (!props.value.length) {
change(0) change(0)
}
timeOptions.value = new Array(day) timeOptions.value = new Array(day)
.fill(1) .fill(1)
.map((_, index) => { .map((_, index) => {

View File

@ -15,6 +15,7 @@
]' ]'
option-type='button' option-type='button'
button-style='solid' button-style='solid'
@change='updateValue'
/> />
</j-form-item> </j-form-item>
<j-form-item v-if='showCron' name='cron' :rules="[ <j-form-item v-if='showCron' name='cron' :rules="[
@ -32,11 +33,11 @@
} }
} }
]"> ]">
<j-input placeholder='corn表达式' v-model:value='formModel.cron' /> <j-input placeholder='corn表达式' v-model:value='formModel.cron' @change='updateValue' />
</j-form-item> </j-form-item>
<template v-else> <template v-else>
<j-form-item name='when'> <j-form-item name='when'>
<WhenOption v-model:value='formModel.when' :type='formModel.trigger' /> <WhenOption v-model:value='formModel.when' :type='formModel.trigger' @change='updateValue' />
</j-form-item> </j-form-item>
<j-form-item name='mod'> <j-form-item name='mod'>
<j-radio-group <j-radio-group
@ -47,13 +48,19 @@
]' ]'
option-type='button' option-type='button'
button-style='solid' button-style='solid'
@change='updateValue'
/> />
</j-form-item> </j-form-item>
</template> </template>
<j-space v-if='showOnce' style='display: flex;gap: 24px'> <j-space v-if='showOnce' style='display: flex;gap: 24px'>
<j-form-item :name="['once', 'time']"> <j-form-item :name="['once', 'time']">
<j-time-picker valueFormat='HH:mm:ss' v-model:value='formModel.once.time' style='width: 100%' <j-time-picker
format='HH:mm:ss' /> valueFormat='HH:mm:ss'
v-model:value='formModel.once.time'
style='width: 100%'
format='HH:mm:ss'
@change='updateValue'
/>
</j-form-item> </j-form-item>
<j-form-item> 执行一次</j-form-item> <j-form-item> 执行一次</j-form-item>
</j-space> </j-space>
@ -68,6 +75,7 @@
@change='(v) => { @change='(v) => {
formModel.period.from = v[0] formModel.period.from = v[0]
formModel.period.to = v[1] formModel.period.to = v[1]
updateValue()
}' }'
/> />
</j-form-item> </j-form-item>
@ -83,6 +91,7 @@
:min='1' :min='1'
:max='59' :max='59'
v-model:value='formModel.period.every' v-model:value='formModel.period.every'
@change='updateValue'
> >
<template #addonAfter> <template #addonAfter>
<j-select <j-select
@ -92,6 +101,7 @@
{ label: "分", value: "minutes" }, { label: "分", value: "minutes" },
{ label: "小时", value: "hours" }, { label: "小时", value: "hours" },
]' ]'
@select='updateValue'
/> />
</template> </template>
</j-input-number> </j-input-number>
@ -103,7 +113,7 @@
<script setup lang='ts' name='Timer'> <script setup lang='ts' name='Timer'>
import type { PropType } from 'vue' import type { PropType } from 'vue'
import moment from 'moment' import dayjs from 'dayjs'
import WhenOption from './WhenOption.vue' import WhenOption from './WhenOption.vue'
import { cloneDeep } from 'lodash-es' import { cloneDeep } from 'lodash-es'
import type { OperationTimer } from '../../../typings' import type { OperationTimer } from '../../../typings'
@ -131,23 +141,21 @@ const emit = defineEmits<Emit>()
const formModel = reactive<OperationTimer>({ const formModel = reactive<OperationTimer>({
trigger: 'week', trigger: 'week',
when: [], when: props.value.when || [],
mod: 'period', mod: 'period',
cron: undefined, cron: undefined,
once: { once: {
time: moment(new Date()).format('HH:mm:ss') time: dayjs(new Date()).format('HH:mm:ss')
}, },
period: { period: {
from: moment(new Date()).startOf('day').format('HH:mm:ss'), from: dayjs(new Date()).startOf('day').format('HH:mm:ss'),
to: moment(new Date()).endOf('day').format('HH:mm:ss'), to: dayjs(new Date()).endOf('day').format('HH:mm:ss'),
every: 1, every: 1,
unit: 'seconds' unit: 'seconds'
} }
}) })
const timerForm = ref() const timerForm = ref()
Object.assign(formModel, props.value)
const showCron = computed(() => { const showCron = computed(() => {
return formModel.trigger === 'cron' return formModel.trigger === 'cron'
}) })
@ -160,7 +168,7 @@ const showPeriod = computed(() => {
return formModel.trigger !== 'cron' && formModel.mod === 'period' return formModel.trigger !== 'cron' && formModel.mod === 'period'
}) })
watch(() => formModel, () => { const updateValue = () => {
const cloneValue = cloneDeep(formModel) const cloneValue = cloneDeep(formModel)
if (cloneValue.trigger === 'cron') { if (cloneValue.trigger === 'cron') {
delete cloneValue.when delete cloneValue.when
@ -174,7 +182,7 @@ watch(() => formModel, () => {
delete cloneValue.period delete cloneValue.period
} }
emit('update:value', cloneValue) emit('update:value', cloneValue)
}, { deep: true }) }
defineExpose({ defineExpose({
validateFields: () => new Promise(async (resolve) => { validateFields: () => new Promise(async (resolve) => {
@ -182,6 +190,8 @@ defineExpose({
resolve(data) resolve(data)
}) })
}) })
Object.assign(formModel, props.value)
formModel.when = props.value.when || []
</script> </script>

View File

@ -57,15 +57,17 @@ const classNames = computed(() => {
}) })
const handleClick = (type: string) => { const handleClick = (type: string) => {
if (!props.disabled) {
emit('update:modelValue', type) emit('update:modelValue', type)
}
} }
</script> </script>
<style scoped lang='less'> <style scoped lang='less'>
// @import 'ant-design-vue/es/style/themes/default.less';
.scene-trigger-way-warp {display: flex; .scene-trigger-way-warp {
display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: 16px 24px; gap: 16px 24px;
width: 100%; width: 100%;
@ -115,5 +117,16 @@ const handleClick = (type: string) => {
} }
} }
} }
&.disabled {
color: rgba(#000, .8);
.way-item-image {
opacity: 0.6;
}
.trigger-way-item {
cursor: not-allowed;
}
}
} }
</style> </style>

View File

@ -115,3 +115,6 @@ onUnmounted(() => {
} }
} }
</style> </style>
<style lang='less'>
@import "./style.less";
</style>

View File

@ -0,0 +1,3 @@
.scene-search {
padding: 24px 0 0 0;
}

View File

@ -300,7 +300,7 @@ const getActions = (
]; ];
if (data.triggerType === 'manual') { if (data.triggerType === 'manual') {
const _item: ActionsType = { const _item: ActionsType = {
key: 'trigger', key: 'tigger',
text: '手动触发', text: '手动触发',
disabled: data.state?.value === 'disable', disabled: data.state?.value === 'disable',
tooltip: { tooltip: {

View File

@ -143,6 +143,9 @@ import {
} from '@/api/system/department'; } from '@/api/system/department';
import { message } from 'jetlinks-ui-components'; import { message } from 'jetlinks-ui-components';
import { dictType } from '../typing'; import { dictType } from '../typing';
import { useDepartmentStore } from '@/store/department';
const departmentStore = useDepartmentStore();
const emits = defineEmits(['confirm', 'update:visible']); const emits = defineEmits(['confirm', 'update:visible']);
const props = defineProps<{ const props = defineProps<{
@ -154,6 +157,9 @@ const props = defineProps<{
}>(); }>();
// //
const loading = ref(false); const loading = ref(false);
// , , id
const queryCount = ref(0);
const confirm = () => { const confirm = () => {
if (table.selectedRows.length < 1) { if (table.selectedRows.length < 1) {
return message.warning('请先勾选数据'); return message.warning('请先勾选数据');
@ -167,6 +173,11 @@ const confirm = () => {
permission: item.selectPermissions, permission: item.selectPermissions,
})); }));
if (params.length === 1) {
// , ,
departmentStore.setProductId(params[0].assetIdList[0]);
}
loading.value = true; loading.value = true;
bindDeviceOrProductList_api(props.assetType, params) bindDeviceOrProductList_api(props.assetType, params)
.then(() => { .then(() => {
@ -259,8 +270,10 @@ const table: any = {
// //
onSelectChange: (row: any) => { onSelectChange: (row: any) => {
// //
if (!row.permissionList.find((item: any) => item.value === 'share')) if (!row.permissionList.find((item: any) => item.value === 'share')) {
message.warning('该资产不支持共享');
return; return;
}
const selectedRowKeys = table._selectedRowKeys.value; const selectedRowKeys = table._selectedRowKeys.value;
const index = selectedRowKeys.indexOf(row.id); const index = selectedRowKeys.indexOf(row.id);
@ -330,7 +343,9 @@ const table: any = {
resolve({ resolve({
code: 200, code: 200,
result: { result: {
data: data.sort((a, b) => a.createTime - b.createTime), data: data.sort(
(a, b) => a.createTime - b.createTime,
),
pageIndex, pageIndex,
pageSize, pageSize,
total, total,
@ -394,6 +409,7 @@ const table: any = {
}), }),
// //
requestFun: async (oParams: any) => { requestFun: async (oParams: any) => {
queryCount.value += 1;
if (props.parentId) { if (props.parentId) {
const terms = [ const terms = [
{ {
@ -411,9 +427,24 @@ const table: any = {
], ],
}, },
}, },
{
column: 'productId$product-info',
type: 'and',
value: `id is ${departmentStore.productId}`,
},
], ],
}, },
]; ];
if (
props.assetType !== 'device' ||
!departmentStore.productId ||
queryCount.value > 1 ||
departmentStore.optType === 'handle'
) {
// |id|(queryCount+1)|, id
terms[0].terms.pop();
}
if (oParams.terms && oParams.terms.length > 0) if (oParams.terms && oParams.terms.length > 0)
terms.unshift({ terms: oParams.terms }); terms.unshift({ terms: oParams.terms });
const params = { const params = {

View File

@ -18,6 +18,7 @@
placeholder="请选择上级组织" placeholder="请选择上级组织"
:tree-data="treeData" :tree-data="treeData"
:field-names="{ value: 'id' }" :field-names="{ value: 'id' }"
@change="handleTreeSelectChange"
> >
<template #title="{ name }"> {{ name }} </template> <template #title="{ name }"> {{ name }} </template>
</j-tree-select> </j-tree-select>
@ -60,6 +61,21 @@ import {
updateDepartment_api, updateDepartment_api,
} from '@/api/system/department'; } from '@/api/system/department';
type treeType = {
id: string;
parentId?: string;
name: string;
sortIndex: string | number;
children?: treeType[];
disabled?: boolean;
};
type formType = {
id?: string;
parentId?: string;
name: string;
sortIndex: string | number;
};
const emits = defineEmits(['refresh', 'update:visible']); const emits = defineEmits(['refresh', 'update:visible']);
const props = defineProps<{ const props = defineProps<{
treeData: any[]; treeData: any[];
@ -160,18 +176,15 @@ const form = reactive({
}); });
form.init(); form.init();
type treeType = { /**
id: string; * 上级组织选择改变
parentId?: string; */
name: string; const handleTreeSelectChange = () => {
sortIndex: string | number; //
children?: treeType[]; const parent = treeData.value.find((f: any) => f.id === form.data.parentId);
disabled?: boolean; // , +1, , 1
}; form.data.sortIndex = parent?.children
type formType = { ? parent.children[parent.children.length - 1].sortIndex + 1
id?: string; : 1;
parentId?: string;
name: string;
sortIndex: string | number;
}; };
</script> </script>

View File

@ -22,7 +22,7 @@
<PermissionButton <PermissionButton
:hasPermission="`${permission}:assert`" :hasPermission="`${permission}:assert`"
type="primary" type="primary"
@click="table.clickAdd" @click="table.clickAdd('handle')"
> >
<AIcon type="PlusOutlined" />资产分配 <AIcon type="PlusOutlined" />资产分配
</PermissionButton> </PermissionButton>
@ -70,7 +70,7 @@
:status="slotProps.state?.value" :status="slotProps.state?.value"
:statusText="slotProps.state?.text" :statusText="slotProps.state?.text"
:statusNames="{ :statusNames="{
online: 'success', online: 'processing',
offline: 'error', offline: 'error',
notActive: 'warning', notActive: 'warning',
}" }"
@ -147,7 +147,7 @@
:status="slotProps.state.value" :status="slotProps.state.value"
:text="slotProps.state.text" :text="slotProps.state.text"
:statusNames="{ :statusNames="{
online: 'success', online: 'processing',
offline: 'error', offline: 'error',
notActive: 'warning', notActive: 'warning',
}" }"
@ -211,6 +211,9 @@ import { intersection } from 'lodash-es';
import type { dictType, optionsType } from '../typing'; import type { dictType, optionsType } from '../typing';
import { message } from 'jetlinks-ui-components'; import { message } from 'jetlinks-ui-components';
import { useDepartmentStore } from '@/store/department';
const departmentStore = useDepartmentStore();
const permission = 'system/Department'; const permission = 'system/Department';
@ -247,6 +250,9 @@ const columns = [
search: { search: {
rename: 'productId$product-info', rename: 'productId$product-info',
type: 'select', type: 'select',
handleValue(value: string) {
return `id is ${value}`;
},
options: () => options: () =>
new Promise((resolve) => { new Promise((resolve) => {
const params = { const params = {
@ -288,14 +294,9 @@ const columns = [
search: { search: {
type: 'select', type: 'select',
options: [ options: [
{ { label: '禁用', value: 'notActive' },
label: '正常', { label: '离线', value: 'offline' },
value: 1, { label: '在线', value: 'online' },
},
{
label: '禁用',
value: 0,
},
], ],
}, },
scopedSlots: true, scopedSlots: true,
@ -467,7 +468,9 @@ const table = {
}; };
} }
}, },
clickAdd: () => { clickAdd: (type?: string) => {
// : type = 'handle': , !type,
departmentStore.setType(type)
dialogs.addShow = true; dialogs.addShow = true;
}, },
clickEdit: (row?: any) => { clickEdit: (row?: any) => {
@ -520,7 +523,7 @@ const dialogs = reactive({
}); });
table.init(); table.init();
nextTick(() => { watchEffect(() => {
props.bindBool && table.clickAdd(); props.bindBool && table.clickAdd();
emits('update:bindBool', false); emits('update:bindBool', false);
}); });

View File

@ -68,9 +68,8 @@
:status="slotProps.state?.value" :status="slotProps.state?.value"
:statusText="slotProps.state?.text" :statusText="slotProps.state?.text"
:statusNames="{ :statusNames="{
online: 'success', 1: 'processing',
offline: 'error', 0: 'error',
notActive: 'warning',
}" }"
> >
<template #img> <template #img>
@ -172,9 +171,8 @@
:status="slotProps.state.value" :status="slotProps.state.value"
:text="slotProps.state.text" :text="slotProps.state.text"
:statusNames="{ :statusNames="{
online: 'success', 1: 'processing',
offline: 'error', 0: 'error',
notActive: 'warning',
}" }"
></BadgeStatus> ></BadgeStatus>
</template> </template>

View File

@ -19,6 +19,7 @@
:dataSource="paramsTable" :dataSource="paramsTable"
:pagination="false" :pagination="false"
size="small" size="small"
bordered
> >
<template #bodyCell="{ column, record, index }"> <template #bodyCell="{ column, record, index }">
<template v-if="column.key === 'name'"> <template v-if="column.key === 'name'">
@ -65,8 +66,7 @@
</template> </template>
<template v-else-if="column.key === 'action'"> <template v-else-if="column.key === 'action'">
<PermissionButton <PermissionButton
type="link" type="text"
:hasPermission="`{permission}:delete`"
:popConfirm="{ :popConfirm="{
title: `确定删除`, title: `确定删除`,
onConfirm: () => onConfirm: () =>
@ -88,8 +88,9 @@
style="text-align: center" style="text-align: center"
/> />
<j-button <j-button
type="dashed"
@click="requestBody.addRow" @click="requestBody.addRow"
style="width: 100%; text-align: center" style="width: 100%; text-align: center; margin-top: 5px;"
> >
<AIcon type="PlusOutlined" />新增 <AIcon type="PlusOutlined" />新增
</j-button> </j-button>
@ -305,11 +306,15 @@ type requestObj = {
} }
} }
.table { .table {
margin-bottom: 22px;
:deep(.ant-table-cell) { :deep(.ant-table-cell) {
padding: 0 8px; padding: 0 8px;
height: 56px; height: 56px;
} }
} }
:deep(.ant-form-item) {
margin-bottom: 0;
}
} }
} }
} }

View File

@ -79,7 +79,7 @@ const rowSelection = {
}, },
selectedRowKeys: ref<string[]>([]), selectedRowKeys: ref<string[]>([]),
}; };
const save = () => { const save = async () => {
const keys = props.selectedRowKeys; const keys = props.selectedRowKeys;
const removeKeys = props.sourceKeys.filter((key) => !keys.includes(key)); const removeKeys = props.sourceKeys.filter((key) => !keys.includes(key));
@ -87,13 +87,20 @@ const save = () => {
if (props.mode === 'api') { if (props.mode === 'api') {
// api // api
removeKeys.length && // removeKeys.length &&
delOperations_api(removeKeys) // delOperations_api(removeKeys)
.finally(() => addOperations_api(addKeys)) // .finally(() => addOperations_api(addKeys))
.then(() => { // .then(() => {
// message.success('');
// emits('refresh');
// });
// fix: bug#10829
removeKeys.length && (await delOperations_api(removeKeys));
const res = await addOperations_api(addKeys);
if (res.success) {
message.success('操作成功'); message.success('操作成功');
emits('refresh') emits('refresh');
}); }
} else if (props.mode === 'appManger') { } else if (props.mode === 'appManger') {
const removeItems = removeKeys.map((key) => ({ const removeItems = removeKeys.map((key) => ({
id: key, id: key,

View File

@ -78,13 +78,17 @@ const form = reactive({
}); });
}, },
clickSave: () => { clickSave: () => {
formRef.value?.validate().then(() => {
const updateRole = updateRole_api(form.data); const updateRole = updateRole_api(form.data);
const updateTree = updatePrimissTree_api(roleId, { menus: form.menus }); const updateTree = updatePrimissTree_api(roleId, {
menus: form.menus,
});
Promise.all([updateRole, updateTree]).then((resp) => { Promise.all([updateRole, updateTree]).then((resp) => {
message.success('操作成功'); message.success('操作成功');
router.push('/system/Role'); router.push('/system/Role');
}); });
});
}, },
}); });
@ -123,7 +127,7 @@ form.getForm();
:deep(.ant-form-item-required) { :deep(.ant-form-item-required) {
padding-right: 12px; padding-right: 12px;
&::before{ &::before {
right: 0; right: 0;
} }
} }

View File

@ -4,7 +4,7 @@
<pro-search <pro-search
:columns="columns" :columns="columns"
target="category" target="category"
@search="(params:any)=>queryParams = {...params}" @search="handleParams"
/> />
<j-pro-table <j-pro-table
@ -262,6 +262,23 @@ type dictType = {
name: string; name: string;
}; };
type modalType = '' | 'add' | 'edit' | 'reset'; type modalType = '' | 'add' | 'edit' | 'reset';
const handleParams = (params: any)=> {
const newParams = (params?.terms as any[])?.map(item1 => {
item1.terms = item1.terms.map((item2: any) => {
if (['telephone', 'email'].includes(item2.column)) {
return {
column: 'id$user-detail',
value: [item2]
}
}
return item2
})
return item1
})
queryParams.value = { terms: newParams || [] }
}
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>

View File

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

View File

@ -3700,8 +3700,8 @@ jetlinks-store@^0.0.3:
jetlinks-ui-components@^1.0.5: jetlinks-ui-components@^1.0.5:
version "1.0.5" version "1.0.5"
resolved "http://47.108.170.157:9013/jetlinks-ui-components/-/jetlinks-ui-components-1.0.5.tgz#682711e0f69c141fff2c256db61a060c82539611" resolved "http://47.108.170.157:9013/jetlinks-ui-components/-/jetlinks-ui-components-1.0.5.tgz#f2e62df4054e513df39e3355592d95e356438328"
integrity sha512-rQxD/YlE+XSAG7BWIcFTtKrCQJXk5o+TUgejyuUT/baBThJB6xYt1k2dQEdXyiwpukYen5FzaoLpelSD9SUegw== integrity sha512-vfQ8g8nEJueDkHCVcg9WeWDDjnPklyigbobB1CQArfHLd5O4x6vLFWvF69k0yqytCvIOXB13CzhLtMnv68pC5w==
dependencies: dependencies:
"@vueuse/core" "^9.12.0" "@vueuse/core" "^9.12.0"
ant-design-vue "^3.2.15" ant-design-vue "^3.2.15"