update: 首页优化及完善权限

This commit is contained in:
easy 2023-01-29 16:08:40 +08:00
parent dc6ad3b081
commit 4badeb8d2a
9 changed files with 294 additions and 143 deletions

View File

@ -12,4 +12,6 @@ export const getDeviceCount_api = () => server.get(`/device/instance/_count`);
// 产品数量 // 产品数量
export const getProductCount_api = (data:object) => server.post(`/device-product/_count`, data); export const getProductCount_api = (data:object) => server.post(`/device-product/_count`, data);
// 查询产品列表 // 查询产品列表
export const getProductList_api = (data:object) => server.get(`/device/product/_query/no-paging?paging=false`, data); export const getProductList_api = (data:object={}) => server.get(`/device/product/_query/no-paging?paging=false`, data);
// 查询设备列表
export const getDeviceList_api = (data:object) => server.post(`/device-instance/_query/`, data);

View File

@ -6,7 +6,7 @@ import { Terms } from 'components/Search/types'
* @param path {String} * @param path {String}
*/ */
export const getImage = (path: string) => { export const getImage = (path: string) => {
return new URL('/images'+path, import.meta.url).href return new URL('/images' + path, import.meta.url).href
} }
export const LocalStore = { export const LocalStore = {
@ -57,3 +57,30 @@ export const filterTreeSelectNode = (value: string, treeNode: any, key: string =
export const filterSelectNode = (value: string, option: any, key: string = 'label'): boolean => { export const filterSelectNode = (value: string, option: any, key: string = 'label'): boolean => {
return option[key]?.includes(value) return option[key]?.includes(value)
} }
/**
* '2022-01-02 14:03:05'
* @param date
* @returns
*/
export const dateFormat = (dateSouce:any):string|Error => {
let date = null
try {
date = new Date(dateSouce)
} catch (error) {
return new Error('请传入日期格式数据')
}
let year = date.getFullYear();
let month: number | string = date.getMonth() + 1;
let day: number | string = date.getDate();
let hour: number | string = date.getHours();
let minutes: number | string = date.getMinutes();
let seconds: number | string = date.getSeconds();
month = (month < 10) ? '0' + month : month;
day = (day < 10) ? '0' + day : day;
hour = (hour < 10) ? '0' + hour : hour;
minutes = (minutes < 10) ? '0' + minutes : minutes;
seconds = (seconds < 10) ? '0' + seconds : seconds;
return year + "-" + month + "-" + day
+ " " + hour + ":" + minutes + ":" + seconds;
}

View File

@ -0,0 +1,38 @@
<template>
<span class="status-label-container">
<i class="circle" :style="{ background: bjColor }"></i>
<span>{{ props.statusLabel }}</span>
</span>
</template>
<script setup lang="ts">
const props = defineProps<{
statusValue: string;
statusLabel: string;
}>();
const bjColor = computed(() => {
switch (props.statusValue) {
case 'online':
return '#52c41a';
case 'offline':
return '#ff4d4f';
case 'notActive':
return '#1890ff';
}
});
</script>
<style lang="less" scoped>
.status-label-container {
display: flex;
align-items: center;
.circle {
display: inline-block;
width: 6px;
height: 6px;
border-radius: 50%;
margin-right: 8px;
}
}
</style>

View File

@ -21,11 +21,11 @@
</div> </div>
<div class="dialogs"> <div class="dialogs">
<AccessMethodDialog <ProductChooseDialog
:open-number="openAccess" :open-number="openAccess"
@confirm="againJumpPage" @confirm="againJumpPage"
/> />
<FuncTestDialog <DeviceChooseDialog
:open-number="openFunc" :open-number="openFunc"
@confirm="againJumpPage" @confirm="againJumpPage"
/> />
@ -38,8 +38,8 @@ import { PropType } from 'vue';
import { QuestionCircleOutlined } from '@ant-design/icons-vue'; import { QuestionCircleOutlined } from '@ant-design/icons-vue';
import { message } from 'ant-design-vue'; import { message } from 'ant-design-vue';
import AccessMethodDialog from './dialogs/AccessMethodDialog.vue'; import ProductChooseDialog from './dialogs/ProductChooseDialog.vue';
import FuncTestDialog from './dialogs/FuncTestDialog.vue'; import DeviceChooseDialog from './dialogs/DeviceChooseDialog.vue';
import { recommendList } from '../index'; import { recommendList } from '../index';
@ -73,9 +73,8 @@ const jumpPage = (row: recommendList) => {
} }
}; };
// //
const againJumpPage = (paramsSource: object) => { const againJumpPage = (params: string) => {
const params = { ...(selectRow.params || {}), ...paramsSource }; router.push(`${selectRow.linkUrl}/${params}`);
router.push(`${selectRow.linkUrl}${objToParams(params || {})}`);
}; };
const objToParams = (source: object): string => { const objToParams = (source: object): string => {

View File

@ -0,0 +1,181 @@
<template>
<div ref="modal" class="func-test-dialog-container"></div>
<a-modal
v-model:visible="visible"
title="选择产品"
style="width: 1000px"
@ok="handleOk"
:getContainer="getContainer"
:maskClosable="false"
>
<Search type="simple" :columns="query.columns" @search="query.search" />
<JTable
model="TABLE"
:request="getDeviceList_api"
:columns="table.columns"
:params="query.params"
:defaultParams="{ sorts: [{ name: 'createTime', order: 'desc' }] }"
:rowSelection="{
selectedRowKeys: table.selectedKeys,
onChange: table.onSelect,
type: 'radio',
}"
>
<template #modifyTime="slotProps">
<span>{{ dateFormat(slotProps.modifyTime) }}</span>
</template>
<template #state="slotProps">
<StatusLabel
:status-value="slotProps.state.value"
:status-label="slotProps.state.text"
/>
</template>
</JTable>
<template #footer>
<a-button key="back" @click="visible = false">取消</a-button>
<a-button key="submit" type="primary" @click="handleOk"
>确认</a-button
>
</template>
</a-modal>
</template>
<script setup lang="ts">
import StatusLabel from '../StatusLabel.vue';
import { ComponentInternalInstance } from 'vue';
import { getDeviceList_api } from '@/api/home';
import { dateFormat } from '@/utils/comm';
import { message } from 'ant-design-vue';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const emits = defineEmits(['confirm']);
const props = defineProps({
openNumber: Number,
});
//
const visible = ref<boolean>(false);
const getContainer = () => proxy?.$refs.modal as HTMLElement;
const handleOk = () => {
if (table.selectedKeys.length < 1) return message.warn('请选择设备');
emits('confirm', table.selectedKeys[0]);
visible.value = false;
};
watch(
() => props.openNumber,
() => {
visible.value = true;
},
);
const query = reactive({
params: {},
columns: [
{
title: '设备ID',
dataIndex: 'id',
key: 'id',
ellipsis: true,
search: {
type: 'string',
},
},
{
title: '设备名称',
dataIndex: 'name',
key: 'name',
ellipsis: true,
search: {
type: 'string',
},
},
{
title: '产品名称',
dataIndex: 'productName',
key: 'productName',
ellipsis: true,
search: {
type: 'string',
},
},
{
title: '注册时间',
dataIndex: 'modifyTime',
key: 'modifyTime',
ellipsis: true,
search: {
type: 'date',
},
},
{
title: '状态',
dataIndex: 'state',
key: 'state',
ellipsis: true,
search: {
rename: 'state',
type: 'select',
options: [
{
label: '在线',
value: 'online',
},
{
label: '离线',
value: 'offline',
},
],
},
},
],
search: (params: object) => {
query.params = params;
},
});
const table = reactive({
columns: [
{
title: '设备Id',
dataIndex: 'id',
key: 'id',
},
{
title: '设备名称',
dataIndex: 'name',
key: 'name',
},
{
title: '产品名称',
dataIndex: 'productName',
key: 'productName',
},
{
title: '注册时间',
dataIndex: 'modifyTime',
key: 'modifyTime',
scopedSlots: true,
},
{
title: '状态',
dataIndex: 'state',
key: 'state',
scopedSlots: true,
},
],
selectedKeys: [] as string[],
onSelect: (keys: string[]) => {
table.selectedKeys = [...keys];
},
});
</script>
<style lang="less" scoped>
.func-test-dialog-container {
.search {
display: flex;
}
}
</style>

View File

@ -1,98 +0,0 @@
<template>
<div ref="modal" class="func-test-dialog-container"></div>
<a-modal
v-model:visible="visible"
title="选择产品"
style="width: 1000px"
@ok="handleOk"
:getContainer="getContainer"
:maskClosable="false"
>
<Search />
<JTable :columns="columns" model="TABLE"> </JTable>
<template #footer>
<a-button key="back" @click="visible = false">取消</a-button>
<a-button key="submit" type="primary" @click="handleOk"
>确认</a-button
>
</template>
</a-modal>
</template>
<script setup lang="ts">
import { ComponentInternalInstance } from 'vue';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const emits = defineEmits(['confirm']);
const props = defineProps({
openNumber: Number,
});
//
const visible = ref<boolean>(false);
const getContainer = () => proxy?.$refs.modal as HTMLElement;
const handleOk = () => {
emits('confirm', form.value);
visible.value = false;
};
watch(
() => props.openNumber,
() => {
visible.value = true;
clickReset();
clickSearch();
},
);
//
const form = ref({
key: '',
relation: '',
keyValue: '',
});
const clickSearch = () => {};
const clickReset = () => {
Object.entries(form.value).forEach(([prop]) => {
form.value[prop] = '';
});
};
//
const columns = [
{
title: '设备Id',
dataIndex: 'deviceId',
key: 'deviceId',
},
{
title: '设备名称',
dataIndex: 'deviceName',
key: 'deviceName',
},
{
title: '产品名称',
dataIndex: 'productName',
key: 'productName',
},
{
title: '注册时间',
dataIndex: 'createTime',
key: 'createTime',
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
},
];
</script>
<style lang="less" scoped>
.func-test-dialog-container {
.search {
display: flex;
}
}
</style>

View File

@ -53,7 +53,7 @@ const productList = ref<[productItem] | []>([]);
const getContainer = () => proxy?.$refs.modal as HTMLElement; const getContainer = () => proxy?.$refs.modal as HTMLElement;
const getOptions = () => { const getOptions = () => {
getProductList_api().then((resp) => { getProductList_api().then((resp:any) => {
productList.value = resp.result productList.value = resp.result
.filter((i: any) => !i?.accessId) .filter((i: any) => !i?.accessId)
.map((item: { name: any; id: any }) => ({ .map((item: { name: any; id: any }) => ({
@ -63,7 +63,7 @@ const getOptions = () => {
}); });
}; };
const handleOk = () => { const handleOk = () => {
emits('confirm', form.value); emits('confirm', form.value.productId);
visible.value = false; visible.value = false;
}; };
const filterOption = (input: string, option: any) => { const filterOption = (input: string, option: any) => {

View File

@ -8,7 +8,11 @@ export interface recommendList {
auth: boolean; auth: boolean;
dialogTag?: 'accessMethod' | 'funcTest'; dialogTag?: 'accessMethod' | 'funcTest';
} }
// 产品列表里的每项
export interface productItem {
label: string;
value: string
}
export interface deviceInfo { export interface deviceInfo {
deviceId: string, deviceId: string,
deviceName: string, deviceName: string,

View File

@ -1,9 +1,10 @@
// import {getImage} from '@/utils/comm' // import {getImage} from '@/utils/comm'
import { useMenuStore } from "@/store/menu";
import { usePermissionStore } from "@/store/permission"; import { usePermissionStore } from "@/store/permission";
import { recommendList, bootConfig } from "../index"; import { recommendList, bootConfig } from "../index";
// 权限控制 // 按钮权限控制
const hasPermission = usePermissionStore().hasPermission; const hasPermission = usePermissionStore().hasPermission;
const productPermission = (action: string) => const productPermission = (action: string) =>
hasPermission(`device/Product:${action}`); hasPermission(`device/Product:${action}`);
@ -11,6 +12,8 @@ const devicePermission = (action: string) =>
hasPermission(`device/Instance:${action}`); hasPermission(`device/Instance:${action}`);
const rulePermission = (action: string) => const rulePermission = (action: string) =>
hasPermission(`rule-engine/Instance:${action}`); hasPermission(`rule-engine/Instance:${action}`);
// 页面权限控制
const menuPermission = useMenuStore().hasPermission
// 物联网引导-数据 // 物联网引导-数据
@ -18,7 +21,7 @@ export const deviceBootConfig: bootConfig[] = [
{ {
english: 'STEP1', english: 'STEP1',
label: '创建产品', label: '创建产品',
link: '/a', link: '/iot/device/Product',
auth: productPermission('add'), auth: productPermission('add'),
params: { params: {
save: true, save: true,
@ -27,7 +30,7 @@ export const deviceBootConfig: bootConfig[] = [
{ {
english: 'STEP2', english: 'STEP2',
label: '创建设备', label: '创建设备',
link: '/b', link: '/iot/device/Instance',
auth: devicePermission('add'), auth: devicePermission('add'),
params: { params: {
save: true, save: true,
@ -36,7 +39,7 @@ export const deviceBootConfig: bootConfig[] = [
{ {
english: 'STEP3', english: 'STEP3',
label: '规则引擎', label: '规则引擎',
link: '/c', link: '/iot/rule-engine/Instance',
auth: rulePermission('add'), auth: rulePermission('add'),
params: { params: {
save: true, save: true,
@ -50,7 +53,7 @@ export const deviceStepDetails: recommendList[] = [
details: details:
'产品是设备的集合,通常指一组具有相同功能的设备。物联设备必须通过产品进行接入方式配置。', '产品是设备的集合,通常指一组具有相同功能的设备。物联设备必须通过产品进行接入方式配置。',
iconUrl: '/images/home/bottom-4.png', iconUrl: '/images/home/bottom-4.png',
linkUrl: '/a', linkUrl: '/iot/device/Product',
auth: productPermission('add'), auth: productPermission('add'),
params: { params: {
save: true, save: true,
@ -61,7 +64,7 @@ export const deviceStepDetails: recommendList[] = [
details: details:
'通过产品对同一类型的设备进行统一的接入方式配置。请参照设备铭牌说明选择匹配的接入方式。', '通过产品对同一类型的设备进行统一的接入方式配置。请参照设备铭牌说明选择匹配的接入方式。',
iconUrl: '/images/home/bottom-1.png', iconUrl: '/images/home/bottom-1.png',
linkUrl: '/a', linkUrl: '/iot/device/Product/detail',
auth: productPermission('update'), auth: productPermission('update'),
dialogTag: 'accessMethod', dialogTag: 'accessMethod',
}, },
@ -69,7 +72,7 @@ export const deviceStepDetails: recommendList[] = [
title: '添加测试设备', title: '添加测试设备',
details: '添加单个设备,用于验证产品模型是否配置正确。', details: '添加单个设备,用于验证产品模型是否配置正确。',
iconUrl: '/images/home/bottom-5.png', iconUrl: '/images/home/bottom-5.png',
linkUrl: '/a', linkUrl: '/iot/device/Instance',
auth: devicePermission('add'), auth: devicePermission('add'),
params: { params: {
save: true, save: true,
@ -80,15 +83,16 @@ export const deviceStepDetails: recommendList[] = [
details: details:
'对添加的测试设备进行功能调试,验证能否连接到平台,设备功能是否配置正确。', '对添加的测试设备进行功能调试,验证能否连接到平台,设备功能是否配置正确。',
iconUrl: '/images/home/bottom-2.png', iconUrl: '/images/home/bottom-2.png',
linkUrl: '/a', linkUrl: '/iot/device/Instance/detail',
auth: devicePermission('update'), auth: devicePermission('update'),
// auth: true,
dialogTag: 'funcTest', dialogTag: 'funcTest',
}, },
{ {
title: '批量添加设备', title: '批量添加设备',
details: '批量添加同一产品下的设备', details: '批量添加同一产品下的设备',
iconUrl: '/images/home/bottom-3.png', iconUrl: '/images/home/bottom-3.png',
linkUrl: '/a', linkUrl: '/iot/device/Instance',
auth: devicePermission('import'), auth: devicePermission('import'),
params: { params: {
import: true, import: true,
@ -102,14 +106,14 @@ export const opsBootConfig: bootConfig[] = [
{ {
english: 'STEP1', english: 'STEP1',
label: '设备接入配置', label: '设备接入配置',
link: '/a', link: '/iot/link/accessConfig',
auth: true, auth: menuPermission('link/accessConfig'),
}, },
{ {
english: 'STEP2', english: 'STEP2',
label: '日志排查', label: '日志排查',
link: '/b', link: '/iot/link/Log',
auth: true, auth: menuPermission('link/Log'),
params: { params: {
key: 'system', key: 'system',
}, },
@ -117,8 +121,8 @@ export const opsBootConfig: bootConfig[] = [
{ {
english: 'STEP3', english: 'STEP3',
label: '实时监控', label: '实时监控',
link: '/c', link: '/iot/link/dashboard',
auth: false, auth: menuPermission('link/dashboard'),
params: { params: {
save: true, save: true,
}, },
@ -131,44 +135,38 @@ export const opsStepDetails: recommendList[] = [
details: details:
'根据业务需求自定义开发对应的产品(设备模型)接入协议,并上传到平台。', '根据业务需求自定义开发对应的产品(设备模型)接入协议,并上传到平台。',
iconUrl: '/images/home/bottom-1.png', iconUrl: '/images/home/bottom-1.png',
linkUrl: '/a', linkUrl: '/iot/link/protocol',
auth: true, auth: menuPermission('link/Protocol'),
params: {
a: 1,
save: true,
},
}, },
{ {
title: '证书管理', title: '证书管理',
details: '统一维护平台内的证书,用于数据通信加密。', details: '统一维护平台内的证书,用于数据通信加密。',
iconUrl: '/images/home/bottom-6.png', iconUrl: '/images/home/bottom-6.png',
linkUrl: '/a', linkUrl: '/iot/link/Certificate',
auth: true, auth: menuPermission('link/Certificate'),
params: {
a: 1,
save: false,
},
}, },
{ {
title: '网络组件', title: '网络组件',
details: '根据不同的传输类型配置平台底层网络组件相关参数。', details: '根据不同的传输类型配置平台底层网络组件相关参数。',
iconUrl: '/images/home/bottom-3.png', iconUrl: '/images/home/bottom-3.png',
linkUrl: '/a', linkUrl: '/iot/link/type',
auth: true, auth: menuPermission('link/Type'),
}, },
{ {
title: '设备接入网关', title: '设备接入网关',
details: '根据不同的传输类型,关联消息协议,配置设备接入网关相关参数。', details: '根据不同的传输类型,关联消息协议,配置设备接入网关相关参数。',
iconUrl: '/images/home/bottom-4.png', iconUrl: '/images/home/bottom-4.png',
linkUrl: '/a', linkUrl: '/iot/link/accessConfig',
auth: true, auth: menuPermission('link/AccessConfig'),
}, },
{ {
title: '日志管理', title: '日志管理',
details: '监控系统日志,及时处理系统异常。', details: '监控系统日志,及时处理系统异常。',
iconUrl: '/images/home/bottom-5.png', iconUrl: '/images/home/bottom-5.png',
linkUrl: '/a', linkUrl: '/iot/link/Log',
auth: false, auth: menuPermission('Log'),
params: { params: {
key: 'system', key: 'system',
} }