fix: 合并冲突
This commit is contained in:
commit
ae959f9dcd
|
@ -5,6 +5,7 @@
|
|||
<%- favicon %>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title><%- title %></title>
|
||||
<script src="/js/liveplayer-lib.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -13,6 +13,7 @@
|
|||
"prepare": "husky install"
|
||||
},
|
||||
"dependencies": {
|
||||
"@liveqing/liveplayer-v3": "^3.7.10",
|
||||
"@types/marked": "^4.0.8",
|
||||
"@vitejs/plugin-vue-jsx": "^3.0.0",
|
||||
"@vuemap/vue-amap": "^1.1.20",
|
||||
|
@ -34,6 +35,7 @@
|
|||
"monaco-editor": "^0.36.0",
|
||||
"nrm": "^1.2.5",
|
||||
"pinia": "^2.0.28",
|
||||
"rollup-plugin-copy": "^3.4.0",
|
||||
"unplugin-auto-import": "^0.12.1",
|
||||
"unplugin-vue-components": "^0.22.12",
|
||||
"v-clipboard3": "^0.1.4",
|
||||
|
@ -43,8 +45,7 @@
|
|||
"vue-router": "^4.1.6",
|
||||
"vue3-json-viewer": "^2.2.2",
|
||||
"vue3-markdown-it": "^1.0.10",
|
||||
"vue3-ts-jsoneditor": "^2.7.1",
|
||||
"vue3-video-play": "^1.3.1-beta.6"
|
||||
"vue3-ts-jsoneditor": "^2.7.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^17.4.1",
|
||||
|
|
|
@ -8,213 +8,214 @@ export interface IMatcher {
|
|||
const matchComponents: IMatcher[] = [
|
||||
{
|
||||
pattern: /^Avatar/,
|
||||
styleDir: 'Avatar',
|
||||
styleDir: 'Avatar'
|
||||
},
|
||||
{
|
||||
pattern: /^AutoComplete/,
|
||||
styleDir: 'AutoComplete',
|
||||
styleDir: 'AutoComplete'
|
||||
},
|
||||
{
|
||||
pattern: /^Anchor/,
|
||||
styleDir: 'Anchor',
|
||||
styleDir: 'Anchor'
|
||||
},
|
||||
|
||||
{
|
||||
pattern: /^Badge/,
|
||||
styleDir: 'Badge',
|
||||
styleDir: 'Badge'
|
||||
},
|
||||
{
|
||||
pattern: /^Breadcrumb/,
|
||||
styleDir: 'Breadcrumb',
|
||||
styleDir: 'Breadcrumb'
|
||||
},
|
||||
{
|
||||
pattern: /^Button/,
|
||||
styleDir: 'Button',
|
||||
styleDir: 'Button'
|
||||
},
|
||||
{
|
||||
pattern: /^Checkbox/,
|
||||
styleDir: 'Checkbox',
|
||||
styleDir: 'Checkbox'
|
||||
},
|
||||
{
|
||||
pattern: /^CardSelect/,
|
||||
styleDir: 'CardSelect'
|
||||
},
|
||||
{
|
||||
pattern: /^Card/,
|
||||
styleDir: 'Card',
|
||||
styleDir: 'Card'
|
||||
},
|
||||
{
|
||||
pattern: /^Collapse/,
|
||||
styleDir: 'Collapse',
|
||||
styleDir: 'Collapse'
|
||||
},
|
||||
{
|
||||
pattern: /^Descriptions/,
|
||||
styleDir: 'Descriptions',
|
||||
styleDir: 'Descriptions'
|
||||
},
|
||||
{
|
||||
pattern: /^RangePicker|^WeekPicker|^MonthPicker/,
|
||||
styleDir: 'DatePicker',
|
||||
styleDir: 'DatePicker'
|
||||
},
|
||||
{
|
||||
pattern: /^Dropdown/,
|
||||
styleDir: 'Dropdown',
|
||||
styleDir: 'Dropdown'
|
||||
},
|
||||
|
||||
{
|
||||
pattern: /^Form/,
|
||||
styleDir: 'Form',
|
||||
styleDir: 'Form'
|
||||
},
|
||||
{
|
||||
pattern: /^InputNumber/,
|
||||
styleDir: 'InputNumber',
|
||||
styleDir: 'InputNumber'
|
||||
},
|
||||
|
||||
{
|
||||
pattern: /^Input|^Textarea/,
|
||||
styleDir: 'Input',
|
||||
styleDir: 'Input'
|
||||
},
|
||||
{
|
||||
pattern: /^Statistic/,
|
||||
styleDir: 'Statistic',
|
||||
styleDir: 'Statistic'
|
||||
},
|
||||
{
|
||||
pattern: /^CheckableTag/,
|
||||
styleDir: 'Tag',
|
||||
styleDir: 'Tag'
|
||||
},
|
||||
{
|
||||
pattern: /^TimeRangePicker/,
|
||||
styleDir: 'TimePicker',
|
||||
styleDir: 'TimePicker'
|
||||
},
|
||||
{
|
||||
pattern: /^Layout/,
|
||||
styleDir: 'Layout',
|
||||
styleDir: 'Layout'
|
||||
},
|
||||
{
|
||||
pattern: /^Menu|^SubMenu/,
|
||||
styleDir: 'Menu',
|
||||
styleDir: 'Menu'
|
||||
},
|
||||
|
||||
{
|
||||
pattern: /^Table/,
|
||||
styleDir: 'Table',
|
||||
styleDir: 'Table'
|
||||
},
|
||||
{
|
||||
pattern: /^TimePicker|^TimeRangePicker/,
|
||||
styleDir: 'TimeTicker',
|
||||
styleDir: 'TimeTicker'
|
||||
},
|
||||
{
|
||||
pattern: /^Radio/,
|
||||
styleDir: 'Radio',
|
||||
styleDir: 'Radio'
|
||||
},
|
||||
|
||||
{
|
||||
pattern: /^Image/,
|
||||
styleDir: 'Image',
|
||||
styleDir: 'Image'
|
||||
},
|
||||
|
||||
{
|
||||
pattern: /^List/,
|
||||
styleDir: 'List',
|
||||
styleDir: 'List'
|
||||
},
|
||||
|
||||
{
|
||||
pattern: /^Tab/,
|
||||
styleDir: 'Tabs',
|
||||
styleDir: 'Tabs'
|
||||
},
|
||||
{
|
||||
pattern: /^Mentions/,
|
||||
styleDir: 'Mentions',
|
||||
styleDir: 'Mentions'
|
||||
},
|
||||
|
||||
{
|
||||
pattern: /^Step/,
|
||||
styleDir: 'Steps',
|
||||
styleDir: 'Steps'
|
||||
},
|
||||
{
|
||||
pattern: /^Skeleton/,
|
||||
styleDir: 'Skeleton',
|
||||
styleDir: 'Skeleton'
|
||||
},
|
||||
|
||||
{
|
||||
pattern: /^Select/,
|
||||
styleDir: 'Select',
|
||||
styleDir: 'Select'
|
||||
},
|
||||
{
|
||||
pattern: /^TreeSelect/,
|
||||
styleDir: 'TreeSelect',
|
||||
styleDir: 'TreeSelect'
|
||||
},
|
||||
{
|
||||
pattern: /^Tree|^DirectoryTree/,
|
||||
styleDir: 'Tree',
|
||||
styleDir: 'Tree'
|
||||
},
|
||||
{
|
||||
pattern: /^Typography/,
|
||||
styleDir: 'Typography',
|
||||
styleDir: 'Typography'
|
||||
},
|
||||
{
|
||||
pattern: /^Timeline/,
|
||||
styleDir: 'Timeline',
|
||||
styleDir: 'Timeline'
|
||||
},
|
||||
{
|
||||
pattern: /^Upload/,
|
||||
styleDir: 'Upload',
|
||||
styleDir: 'Upload'
|
||||
},
|
||||
{
|
||||
pattern: /^ProTable/,
|
||||
styleDir: 'ProTable',
|
||||
styleDir: 'ProTable'
|
||||
},
|
||||
{
|
||||
pattern: /^Search|^AdvancedSearch/,
|
||||
styleDir: 'Search',
|
||||
styleDir: 'Search'
|
||||
},
|
||||
{
|
||||
pattern: /^Ellipsis/,
|
||||
styleDir: 'Ellipsis',
|
||||
styleDir: 'Ellipsis'
|
||||
},
|
||||
{
|
||||
pattern: /^MonacoEditor/,
|
||||
styleDir: 'MonacoEditor',
|
||||
styleDir: 'MonacoEditor'
|
||||
},
|
||||
{
|
||||
pattern: /^ProLayout/,
|
||||
styleDir: 'ProLayout',
|
||||
styleDir: 'ProLayout'
|
||||
},
|
||||
{
|
||||
pattern: /^ScrollTable/,
|
||||
styleDir: 'ScrollTable',
|
||||
styleDir: 'ScrollTable'
|
||||
},
|
||||
{
|
||||
pattern: /^TableCard/,
|
||||
styleDir: 'TableCard',
|
||||
styleDir: 'TableCard'
|
||||
},
|
||||
{
|
||||
pattern: /^Scrollbar/,
|
||||
styleDir: 'Scrollbar',
|
||||
styleDir: 'Scrollbar'
|
||||
},
|
||||
{
|
||||
pattern: /^AIcon/,
|
||||
styleDir: 'AIcon',
|
||||
},
|
||||
{
|
||||
pattern: /^CardSelect/,
|
||||
styleDir: 'CardSelect',
|
||||
styleDir: 'AIcon'
|
||||
},
|
||||
|
||||
{
|
||||
pattern: /^Tooltip/,
|
||||
styleDir: 'Tooltip',
|
||||
styleDir: 'Tooltip'
|
||||
},
|
||||
{
|
||||
pattern: /^Empty/,
|
||||
styleDir: 'Empty',
|
||||
styleDir: 'Empty'
|
||||
},
|
||||
{
|
||||
pattern: /^Popconfirm/,
|
||||
styleDir: 'Popconfirm',
|
||||
styleDir: 'Popconfirm'
|
||||
},
|
||||
{
|
||||
pattern: /^message/,
|
||||
styleDir: 'Message',
|
||||
styleDir: 'Message'
|
||||
},
|
||||
{
|
||||
pattern: /^Notification/,
|
||||
styleDir: 'Notification',
|
||||
},
|
||||
styleDir: 'Notification'
|
||||
}
|
||||
]
|
||||
|
||||
export interface JetlinksVueResolverOptions {
|
||||
|
@ -259,7 +260,6 @@ function getStyleDir(compName: string, _isAntd = false): string {
|
|||
let styleDir
|
||||
const components = _isAntd ? AntdMatchComponents : matchComponents
|
||||
const total = components.length
|
||||
console.log('getStyleDir', compName)
|
||||
for (let i = 0; i < total; i++) {
|
||||
const matcher = components[i]
|
||||
if (compName.match(matcher.pattern)) {
|
||||
|
@ -273,10 +273,10 @@ function getStyleDir(compName: string, _isAntd = false): string {
|
|||
return styleDir
|
||||
}
|
||||
|
||||
function getSideEffects(compName: string, options: JetlinksVueResolverOptions, _isAntd= false): any {
|
||||
function getSideEffects(compName: string, options: JetlinksVueResolverOptions, _isAntd = false): any {
|
||||
const {
|
||||
importStyle = true,
|
||||
importLess = false,
|
||||
importLess = false
|
||||
} = options
|
||||
|
||||
if (!importStyle)
|
||||
|
@ -286,23 +286,21 @@ function getSideEffects(compName: string, options: JetlinksVueResolverOptions, _
|
|||
|
||||
if (importStyle === 'less' || importLess) {
|
||||
const styleDir = getStyleDir(compName, _isAntd)
|
||||
console.log('getSideEffects-style-path', `${packageName}/${lib}/${styleDir}/style`)
|
||||
return `${packageName}/${lib}/${styleDir}/style`
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
const styleDir = getStyleDir(compName, _isAntd)
|
||||
return `${packageName}/${lib}/${styleDir}/style/css`
|
||||
}
|
||||
}
|
||||
|
||||
const filterName = [ 'message', 'Notification', 'AIcon']
|
||||
const filterName = ['message', 'Notification', 'AIcon']
|
||||
const primitiveNames = ['Affix', 'Anchor', 'AnchorLink', 'message', 'Notification', 'AutoComplete', 'AutoCompleteOptGroup', 'AutoCompleteOption', 'Alert', 'Avatar', 'AvatarGroup', 'BackTop', 'Badge', 'BadgeRibbon', 'Breadcrumb', 'BreadcrumbItem', 'BreadcrumbSeparator', 'Button', 'ButtonGroup', 'Calendar', 'Card', 'CardGrid', 'CardMeta', 'Collapse', 'CollapsePanel', 'Carousel', 'Cascader', 'Checkbox', 'CheckboxGroup', 'Col', 'Comment', 'ConfigProvider', 'DatePicker', 'MonthPicker', 'WeekPicker', 'RangePicker', 'QuarterPicker', 'Descriptions', 'DescriptionsItem', 'Divider', 'Dropdown', 'DropdownButton', 'Drawer', 'Empty', 'Form', 'FormItem', 'FormItemRest', 'Grid', 'Input', 'InputGroup', 'InputPassword', 'InputSearch', 'Textarea', 'Image', 'ImagePreviewGroup', 'InputNumber', 'Layout', 'LayoutHeader', 'LayoutSider', 'LayoutFooter', 'LayoutContent', 'List', 'ListItem', 'ListItemMeta', 'Menu', 'MenuDivider', 'MenuItem', 'MenuItemGroup', 'SubMenu', 'Mentions', 'MentionsOption', 'Modal', 'Statistic', 'StatisticCountdown', 'PageHeader', 'Pagination', 'Popconfirm', 'Popover', 'Progress', 'Radio', 'RadioButton', 'RadioGroup', 'Rate', 'Result', 'Row', 'Select', 'SelectOptGroup', 'SelectOption', 'Skeleton', 'SkeletonButton', 'SkeletonAvatar', 'SkeletonInput', 'SkeletonImage', 'Slider', 'Space', 'Spin', 'Steps', 'Step', 'Switch', 'Table', 'TableColumn', 'TableColumnGroup', 'TableSummary', 'TableSummaryRow', 'TableSummaryCell', 'Transfer', 'Tree', 'TreeNode', 'DirectoryTree', 'TreeSelect', 'TreeSelectNode', 'Tabs', 'TabPane', 'Tag', 'CheckableTag', 'TimePicker', 'TimeRangePicker', 'Timeline', 'TimelineItem', 'Tooltip', 'Typography', 'TypographyLink', 'TypographyParagraph', 'TypographyText', 'TypographyTitle', 'Upload', 'UploadDragger', 'LocaleProvider', 'ProTable', 'Search', 'AdvancedSearch', 'Ellipsis', 'MonacoEditor', 'ProLayout', 'ScrollTable', 'TableCard', 'Scrollbar', 'CardSelect', 'ColorPicker']
|
||||
const prefix = 'J'
|
||||
|
||||
let jetlinksNames: Set<string>
|
||||
|
||||
function genJetlinksNames(primitiveNames: string[]): void {
|
||||
jetlinksNames = new Set(primitiveNames.map(name => filterName.includes(name) ? name : `${prefix}${name}`))
|
||||
jetlinksNames = new Set(primitiveNames.map(name => filterName.includes(name) ? name : `${prefix}${name}`))
|
||||
}
|
||||
|
||||
let antdvNames: Set<string>
|
||||
|
@ -322,16 +320,14 @@ function isAntdv(compName: string): boolean {
|
|||
return antdvNames.has(compName)
|
||||
}
|
||||
|
||||
export function JetlinksVueResolver(options: JetlinksVueResolverOptions = {
|
||||
|
||||
}): any {
|
||||
export function JetlinksVueResolver(options: JetlinksVueResolverOptions = {}): any {
|
||||
return {
|
||||
type: 'component',
|
||||
resolve: (name: string) => {
|
||||
if (options.resolveIcons && name.match(/(Outlined|Filled|TwoTone)$/)) {
|
||||
return {
|
||||
name,
|
||||
from: '@ant-design/icons-vue',
|
||||
from: '@ant-design/icons-vue'
|
||||
}
|
||||
}
|
||||
const _isJetlinks = isJetlinks(name)
|
||||
|
@ -340,13 +336,16 @@ export function JetlinksVueResolver(options: JetlinksVueResolverOptions = {
|
|||
const importName = filterName.includes(name) ? name : name.slice(1)
|
||||
options.packageName = _isJetlinks ? 'jetlinks-ui-components' : 'ant-design-vue'
|
||||
const path = `${options.packageName}/${options.cjs ? 'lib' : 'es'}`
|
||||
|
||||
const stylePath = getSideEffects(importName, options, _isAntd)
|
||||
if (_isJetlinks) {
|
||||
console.log(name, importName, stylePath)
|
||||
}
|
||||
return {
|
||||
name: importName,
|
||||
from: path,
|
||||
sideEffects: getSideEffects(importName, options, _isAntd),
|
||||
sideEffects: stylePath
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,49 @@
|
|||
import server from '@/utils/request';
|
||||
import { LocalStore } from '@/utils/comm';
|
||||
import { BASE_API_PATH, TOKEN_KEY } from '@/utils/variable';
|
||||
import { recordsItemType } from '@/views/media/Device/Playback/typings';
|
||||
|
||||
export default {
|
||||
// 开始直播
|
||||
ptzStart: (deviceId: string, channelId: string, type: string) =>
|
||||
`${BASE_API_PATH}/media/device/${deviceId}/${channelId}/live.${type}?:X_Access_Token=${LocalStore.get(TOKEN_KEY)}`,
|
||||
|
||||
// 查询设备通道详情
|
||||
queryDetail: (deviceId: string, data: any) => server.post(`/media/device/${deviceId}/channel/_query`, data),
|
||||
|
||||
// 查询本地回放记录
|
||||
queryRecordLocal: (deviceId: string, channelId: string, data?: any) =>
|
||||
server.post<any>(`/media/device/${deviceId}/${channelId}/records/in-local`, data),
|
||||
|
||||
// 下载到云端
|
||||
downloadRecord: (deviceId: string, channelId: string, data: any) =>
|
||||
server.post(`/media/device/${deviceId}/${channelId}/_record`, data),
|
||||
|
||||
// 播放本地回放
|
||||
playbackLocal: (
|
||||
deviceId: string,
|
||||
channelId: string,
|
||||
suffix: string,
|
||||
startTime: string,
|
||||
endTime: string,
|
||||
speed: number = 1
|
||||
) =>
|
||||
`${BASE_API_PATH}/media/device/${deviceId}/${channelId}/playback.${suffix}?:X_Access_Token=${LocalStore.get(TOKEN_KEY)}&startTime=${startTime}&endTime=${endTime}&speed=${speed}`,
|
||||
|
||||
// 本地录像播放控制
|
||||
playbackControl: (deviceId: string, channelId: string) =>
|
||||
server.post(`/media/device/${deviceId}/${channelId}/stream-control`),
|
||||
|
||||
// 查询云端回放记录
|
||||
recordsInServer: (deviceId: string, channelId: string, data: any) =>
|
||||
server.post<recordsItemType[]>(`/media/device/${deviceId}/${channelId}/records/in-server`, data),
|
||||
|
||||
// 查询云端回放文件信息
|
||||
recordsInServerFiles: (deviceId: string, channelId: string, data: any) =>
|
||||
server.post<recordsItemType[]>(`/media/device/${deviceId}/${channelId}/records/in-server/files`, data),
|
||||
|
||||
// 播放云端回放
|
||||
playbackStart: (recordId: string) => `${BASE_API_PATH}/record/${recordId}.mp4?:X_Access_Token=${LocalStore.get(TOKEN_KEY)}`,
|
||||
|
||||
downLoadFile: (recordId: string) => `${BASE_API_PATH}/record/${recordId}.mp4?download=true&:X_Access_Token=${LocalStore.get(TOKEN_KEY)}`
|
||||
}
|
|
@ -21,4 +21,6 @@ export const _action = (id: string, type: '_disable' | '_enable') => server.put(
|
|||
export const _execute = (id: string) => server.post(`/scene/${id}/_execute`);
|
||||
|
||||
// 内置参数
|
||||
export const queryBuiltInParams = (data: any, params?: any) => server.post(`/scene/parse-variables`, data, params);
|
||||
export const queryBuiltInParams = (data: any, params?: any) => server.post(`/scene/parse-variables`, data, params);
|
||||
|
||||
export const getParseTerm = (data: Record<string, any>) => server.post(`/scene/parse-term-column`, data)
|
|
@ -74,6 +74,8 @@ const iconKeys = [
|
|||
'UserOutlined',
|
||||
'LogoutOutlined',
|
||||
'ReadIconOutlined',
|
||||
'CloudDownloadOutlined',
|
||||
'PauseCircleOutlined',,
|
||||
'FormOutlined',
|
||||
]
|
||||
|
||||
|
|
|
@ -13,12 +13,12 @@
|
|||
}}</a>
|
||||
<span v-else>{{ slotProps.route.breadcrumbName }}</span>
|
||||
</template>
|
||||
<template #rightContentRender>
|
||||
<div style="margin-right: 24px;display: flex;align-items: center;">
|
||||
<AIcon type="QuestionCircleOutlined" @click="toDoc" />
|
||||
<Notice style="margin: 0 24px;" />
|
||||
<UserInfo />
|
||||
</div>
|
||||
<template #rightContentRender>
|
||||
<div class="right-content">
|
||||
<AIcon type="QuestionCircleOutlined" @click="toDoc" />
|
||||
<Notice style="margin: 0 24px" />
|
||||
<UserInfo />
|
||||
</div>
|
||||
</template>
|
||||
<router-view v-slot="{ Component }">
|
||||
<component :is="Component" />
|
||||
|
@ -28,7 +28,7 @@
|
|||
|
||||
<script setup lang="ts" name="BasicLayoutPage">
|
||||
import UserInfo from './components/UserInfo.vue';
|
||||
import Notice from './components/Notice.vue'
|
||||
import Notice from './components/Notice.vue';
|
||||
import DefaultSetting from '../../../config/config';
|
||||
import { useMenuStore } from '@/store/menu';
|
||||
import { clearMenuItem } from 'jetlinks-ui-components/es/ProLayout/util';
|
||||
|
@ -95,8 +95,13 @@ watchEffect(() => {
|
|||
}
|
||||
});
|
||||
|
||||
|
||||
const toDoc = ()=>window.open('http://doc.v2.jetlinks.cn/')
|
||||
const toDoc = () => window.open('http://doc.v2.jetlinks.cn/');
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
<style scoped>
|
||||
.right-content {
|
||||
margin-right: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -127,11 +127,7 @@
|
|||
>
|
||||
刷新
|
||||
</div>
|
||||
<LivePlayer
|
||||
:src="item.url"
|
||||
:width="screenWidth"
|
||||
:height="screenHeight"
|
||||
/>
|
||||
<LivePlayer :url="item.url" autoplay />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
@ -192,9 +188,6 @@ const screen = ref(1);
|
|||
const players = ref<Player[]>([]);
|
||||
// 当前选中的窗口
|
||||
const playerActive = ref(0);
|
||||
// 单个播放窗口宽高
|
||||
const screenWidth = ref('');
|
||||
const screenHeight = ref('');
|
||||
// 历史记录
|
||||
const historyList = ref<any[]>([]);
|
||||
// 展示保存浮窗
|
||||
|
|
|
@ -1,50 +1,88 @@
|
|||
<!-- 视频播放 -->
|
||||
<template>
|
||||
<vue3videoPlay v-bind="options" />
|
||||
<LivePlayer
|
||||
ref="player"
|
||||
fluent
|
||||
:protocol="props.protocol || 'mp4'"
|
||||
:class="props.className"
|
||||
:loading="props.loading"
|
||||
:live="'live' in props ? props.live !== false : true"
|
||||
:autoplay="'autoplay' in props ? props.autoplay !== false : true"
|
||||
:muted="'muted' in props ? props.muted !== false : true"
|
||||
:hide-big-play-button="true"
|
||||
:poster="props.poster || ''"
|
||||
:timeout="props.timeout || 20"
|
||||
:video-url="url || ''"
|
||||
@play="props.onPlay"
|
||||
@pause="props.onPause"
|
||||
@ended="props.onEnded"
|
||||
@error="props.onError"
|
||||
@timeupdate="props.onTimeUpdate"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import 'vue3-video-play/dist/style.css';
|
||||
import vue3videoPlay from 'vue3-video-play';
|
||||
import LivePlayer from '@liveqing/liveplayer-v3';
|
||||
|
||||
const props = defineProps({
|
||||
src: { type: String, default: '' },
|
||||
type: { type: String, default: 'mp4' },
|
||||
width: { type: String, default: '500px' },
|
||||
height: { type: String, default: '280px' },
|
||||
type PlayerProps = {
|
||||
url?: string;
|
||||
live?: boolean;
|
||||
autoplay?: boolean;
|
||||
muted?: boolean;
|
||||
poster?: string;
|
||||
timeout?: number;
|
||||
className?: string;
|
||||
updateTime?: number;
|
||||
key?: string | number;
|
||||
loading?: boolean;
|
||||
protocol?: 'mp4' | 'flv' | 'hls';
|
||||
onDestroy?: (e?: any) => void;
|
||||
onMessage?: (msg: any) => void;
|
||||
onError?: (err: any) => void;
|
||||
onTimeUpdate?: (time: any) => void;
|
||||
onPause?: (e?: any) => void;
|
||||
onPlay?: (e?: any) => void;
|
||||
onFullscreen?: () => void;
|
||||
onSnapOutside?: (base64: any) => void;
|
||||
onSnapInside?: (base64: any) => void;
|
||||
onCustomButtons?: (name: any) => void;
|
||||
onEnded?: (e?: any) => void;
|
||||
onClick?: () => void;
|
||||
};
|
||||
|
||||
const props = defineProps<PlayerProps>();
|
||||
|
||||
const player = ref<HTMLVideoElement>();
|
||||
const url = ref(props.url);
|
||||
|
||||
watchEffect(() => {
|
||||
url.value = props.url;
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.src,
|
||||
(val: string) => {
|
||||
options.src = val;
|
||||
},
|
||||
);
|
||||
/**
|
||||
* 播放
|
||||
*/
|
||||
const play = () => {
|
||||
player.value?.play();
|
||||
};
|
||||
|
||||
const options = reactive({
|
||||
...props,
|
||||
color: '#409eff', //主题色
|
||||
title: '', //视频名称
|
||||
// src: props.src,
|
||||
// type: props.type,
|
||||
muted: false, //静音
|
||||
webFullScreen: false,
|
||||
speedRate: ['0.75', '1.0', '1.25', '1.5', '2.0'], //播放倍速
|
||||
autoPlay: false, //自动播放
|
||||
loop: false, //循环播放
|
||||
mirror: false, //镜像画面
|
||||
ligthOff: false, //关灯模式
|
||||
volume: 0.3, //默认音量大小
|
||||
control: true, //是否显示控制
|
||||
controlBtns: [
|
||||
'audioTrack',
|
||||
'quality',
|
||||
'speedRate',
|
||||
'volume',
|
||||
'setting',
|
||||
'pip',
|
||||
'pageFullScreen',
|
||||
'fullScreen',
|
||||
], //显示所有按钮,
|
||||
/**
|
||||
* 暂停
|
||||
*/
|
||||
const pause = () => {
|
||||
player.value?.pause();
|
||||
};
|
||||
|
||||
/**
|
||||
* 暂停状态
|
||||
*/
|
||||
const paused = () => {
|
||||
return player.value?.paused;
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
play,
|
||||
pause,
|
||||
paused,
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
<div
|
||||
:class="[
|
||||
'checked-icon',
|
||||
disabled && myValue === item.value
|
||||
(disabled && myValue === item.value) || item.disabled
|
||||
? 'checked-icon-disabled'
|
||||
: '',
|
||||
]"
|
||||
|
@ -45,6 +45,7 @@ interface IOption {
|
|||
label: string;
|
||||
value: string;
|
||||
logo: string;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
type Emits = {
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
<!-- 参数类型输入组件 -->
|
||||
<template>
|
||||
<div class="wrapper">
|
||||
<a-select
|
||||
<j-select
|
||||
v-if="typeMap.get(itemType) === 'select'"
|
||||
v-model:value="myValue"
|
||||
:options="options"
|
||||
allowClear
|
||||
style="width: 100%"
|
||||
/>
|
||||
<a-date-picker
|
||||
<j-date-picker
|
||||
v-else-if="typeMap.get(itemType) === 'date'"
|
||||
v-model:value="myValue"
|
||||
allowClear
|
||||
|
@ -17,13 +17,13 @@
|
|||
format="YYYY-MM-DD HH:mm:ss"
|
||||
style="width: 100%"
|
||||
/>
|
||||
<a-input-number
|
||||
<j-input-number
|
||||
v-else-if="typeMap.get(itemType) === 'inputNumber'"
|
||||
v-model:value="myValue"
|
||||
allowClear
|
||||
style="width: 100%"
|
||||
/>
|
||||
<a-input
|
||||
<j-input
|
||||
allowClear
|
||||
v-else-if="typeMap.get(itemType) === 'object'"
|
||||
v-model:value="myValue"
|
||||
|
@ -31,19 +31,19 @@
|
|||
<template #addonAfter>
|
||||
<form-outlined @click="modalVis = true" />
|
||||
</template>
|
||||
</a-input>
|
||||
</j-input>
|
||||
<GeoComponent
|
||||
v-else-if="typeMap.get(itemType) === 'geoPoint'"
|
||||
v-model:point="myValue"
|
||||
/>
|
||||
<a-input
|
||||
<j-input
|
||||
v-else-if="typeMap.get(itemType) === 'file'"
|
||||
v-model:value="myValue"
|
||||
placeholder="请输入图片链接"
|
||||
allowClear
|
||||
>
|
||||
<template #addonAfter>
|
||||
<a-upload
|
||||
<j-upload
|
||||
name="file"
|
||||
:action="FILE_UPLOAD"
|
||||
:headers="headers"
|
||||
|
@ -51,10 +51,10 @@
|
|||
@change="handleFileChange"
|
||||
>
|
||||
<cloud-upload-outlined />
|
||||
</a-upload>
|
||||
</j-upload>
|
||||
</template>
|
||||
</a-input>
|
||||
<a-input
|
||||
</j-input>
|
||||
<j-input
|
||||
v-else
|
||||
allowClear
|
||||
type="text"
|
||||
|
@ -63,7 +63,7 @@
|
|||
/>
|
||||
|
||||
<!-- 代码编辑器弹窗 -->
|
||||
<a-modal
|
||||
<j-modal
|
||||
title="编辑"
|
||||
ok-text="确认"
|
||||
cancel-text="取消"
|
||||
|
@ -75,13 +75,12 @@
|
|||
<div style="width: 100%; height: 400px">
|
||||
<MonacoEditor v-model:modelValue="objectValue" />
|
||||
</div>
|
||||
</a-modal>
|
||||
</j-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { PropType } from 'vue';
|
||||
import { FormOutlined, CloudUploadOutlined } from '@ant-design/icons-vue';
|
||||
import { UploadChangeParam, UploadFile } from 'ant-design-vue';
|
||||
import { DefaultOptionType } from 'ant-design-vue/lib/select';
|
||||
import MonacoEditor from '@/components/MonacoEditor/index.vue';
|
||||
|
|
|
@ -10,7 +10,7 @@ import NormalUpload from './NormalUpload/index.vue'
|
|||
import FileFormat from './FileFormat/index.vue'
|
||||
import JProUpload from './JUpload/index.vue'
|
||||
import { BasicLayoutPage, BlankLayoutPage } from './Layout'
|
||||
import { PageContainer } from 'jetlinks-ui-components/es/components'
|
||||
import { PageContainer } from 'jetlinks-ui-components'
|
||||
import Ellipsis from './Ellipsis/index.vue'
|
||||
import JEmpty from './Empty/index.vue'
|
||||
import AMapComponent from './AMapComponent/index.vue'
|
||||
|
|
|
@ -76,7 +76,7 @@ export const useMenuStore = defineStore({
|
|||
const path = this.hasMenu(name)
|
||||
if (path) {
|
||||
router.push({
|
||||
name, params, query
|
||||
name, params, query, state: { params }
|
||||
})
|
||||
} else {
|
||||
onlyMessage('暂无权限,请联系管理员', 'error')
|
||||
|
|
|
@ -92,7 +92,8 @@ export type MenuItem = {
|
|||
icon?: string
|
||||
[key: string]: any
|
||||
},
|
||||
component?: any
|
||||
component?: any,
|
||||
props?: boolean
|
||||
};
|
||||
|
||||
// 额外子级路由
|
||||
|
@ -213,7 +214,7 @@ export function filterAsnycRouter(asyncRouterMap: any, parentCode = '', level =
|
|||
title: route.name,
|
||||
hideInMenu: route.isShow === false,
|
||||
buttons: route.buttons?.map((b: any) => b.id) || []
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
const silder = {..._route}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<a-table
|
||||
<j-table
|
||||
rowKey="id"
|
||||
:columns="columns"
|
||||
:data-source="dataSource"
|
||||
|
@ -36,7 +36,7 @@
|
|||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</a-table>
|
||||
</j-table>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
|
|
@ -1,60 +1,60 @@
|
|||
<template>
|
||||
<a-form
|
||||
<j-form
|
||||
:layout="'vertical'"
|
||||
ref="formRef"
|
||||
:model="modelRef"
|
||||
>
|
||||
<a-row :gutter="24">
|
||||
<a-col :span="24" v-if="actionType === 'command'">
|
||||
<a-form-item name="messageType" label="指令类型" :rules="{
|
||||
<j-row :gutter="24">
|
||||
<j-col :span="24" v-if="actionType === 'command'">
|
||||
<j-form-item name="messageType" label="指令类型" :rules="{
|
||||
required: true,
|
||||
message: '请选择指令类型',
|
||||
}">
|
||||
<a-select placeholder="请选择指令类型" v-model:value="modelRef.messageType" show-search :filter-option="filterOption">
|
||||
<a-select-option value="READ_PROPERTY">读取属性</a-select-option>
|
||||
<a-select-option value="WRITE_PROPERTY">修改属性</a-select-option>
|
||||
<a-select-option value="INVOKE_FUNCTION">调用功能</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="(modelRef.messageType === 'READ_PROPERTY' || actionType === 'latestData') ? 24 : 12" v-if="(actionType === 'command' && ['READ_PROPERTY','WRITE_PROPERTY'].includes(modelRef.messageType)) || actionType === 'latestData'">
|
||||
<a-form-item :name="['message', 'properties']" label="属性" :rules="{
|
||||
<j-select placeholder="请选择指令类型" v-model:value="modelRef.messageType" show-search :filter-option="filterOption">
|
||||
<j-select-option value="READ_PROPERTY">读取属性</j-select-option>
|
||||
<j-select-option value="WRITE_PROPERTY">修改属性</j-select-option>
|
||||
<j-select-option value="INVOKE_FUNCTION">调用功能</j-select-option>
|
||||
</j-select>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col :span="(modelRef.messageType === 'READ_PROPERTY' || actionType === 'latestData') ? 24 : 12" v-if="(actionType === 'command' && ['READ_PROPERTY','WRITE_PROPERTY'].includes(modelRef.messageType)) || actionType === 'latestData'">
|
||||
<j-form-item :name="['message', 'properties']" label="属性" :rules="{
|
||||
required: true,
|
||||
message: '请选择属性',
|
||||
}">
|
||||
<a-select placeholder="请选择属性" v-model:value="modelRef.message.properties" show-search :filter-option="filterOption">
|
||||
<a-select-option v-for="i in (metadata?.properties) || []" :key="i.id" :value="i.id" :label="i.name">{{i.name}}</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12" v-if="modelRef.messageType === 'WRITE_PROPERTY' && actionType === 'command'">
|
||||
<a-form-item :name="['message', 'value']" label="值" :rules="{
|
||||
<j-select placeholder="请选择属性" v-model:value="modelRef.message.properties" show-search :filter-option="filterOption">
|
||||
<j-select-option v-for="i in (metadata?.properties) || []" :key="i.id" :value="i.id" :label="i.name">{{i.name}}</j-select-option>
|
||||
</j-select>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col :span="12" v-if="modelRef.messageType === 'WRITE_PROPERTY' && actionType === 'command'">
|
||||
<j-form-item :name="['message', 'value']" label="值" :rules="{
|
||||
required: true,
|
||||
message: '请输入值',
|
||||
}">
|
||||
<a-input />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24" v-if="modelRef.messageType === 'INVOKE_FUNCTION'">
|
||||
<a-form-item :name="['message', 'functionId']" label="功能" :rules="{
|
||||
<j-input />
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col :span="24" v-if="modelRef.messageType === 'INVOKE_FUNCTION'">
|
||||
<j-form-item :name="['message', 'functionId']" label="功能" :rules="{
|
||||
required: true,
|
||||
message: '请选择功能',
|
||||
}">
|
||||
<a-select placeholder="请选择功能" v-model:value="modelRef.message.functionId" show-search :filter-option="filterOption" @change="funcChange">
|
||||
<a-select-option v-for="i in (metadata?.functions) || []" :key="i.id" :value="i.id" :label="i.name">{{i.name}}</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24" v-if="modelRef.messageType === 'INVOKE_FUNCTION' && modelRef.message.functionId">
|
||||
<a-form-item :name="['message', 'inputs']" label="参数列表" :rules="{
|
||||
<j-select placeholder="请选择功能" v-model:value="modelRef.message.functionId" show-search :filter-option="filterOption" @change="funcChange">
|
||||
<j-select-option v-for="i in (metadata?.functions) || []" :key="i.id" :value="i.id" :label="i.name">{{i.name}}</j-select-option>
|
||||
</j-select>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col :span="24" v-if="modelRef.messageType === 'INVOKE_FUNCTION' && modelRef.message.functionId">
|
||||
<j-form-item :name="['message', 'inputs']" label="参数列表" :rules="{
|
||||
required: true,
|
||||
message: '请输入参数列表',
|
||||
}">
|
||||
<EditTable v-model="modelRef.message.inputs"/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</j-form>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
@ -62,8 +62,6 @@ import EditTable from './EditTable.vue'
|
|||
|
||||
const formRef = ref();
|
||||
|
||||
const funcList = ref<Record<string, any>[]>([])
|
||||
|
||||
const filterOption = (input: string, option: any) => {
|
||||
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
|
||||
};
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
<div>
|
||||
<h2>1、在百度小度技能平台创建技能,并授权。完成物联网平台与DuerOS的关联。</h2>
|
||||
<div class="image">
|
||||
<a-image width="100%" :src="getImage('/cloud/dueros-doc.jpg')" />
|
||||
<j-image width="100%" :src="getImage('/cloud/dueros-doc.jpg')" />
|
||||
</div>
|
||||
<h1>授权地址</h1>
|
||||
<div>物联网平台的登录地址。注意需要为https。</div>
|
||||
|
@ -26,19 +26,19 @@
|
|||
<h1>Client_Id</h1>
|
||||
<div>请填写系统管理-应用管理中的clientId。</div>
|
||||
<div class="image">
|
||||
<a-image width="100%" :src="getImage('/cloud/dueros-doc1.png')" />
|
||||
<j-image width="100%" :src="getImage('/cloud/dueros-doc1.png')" />
|
||||
</div>
|
||||
<h1>回调地址</h1>
|
||||
<div>请复制DuerOS平台中的值,填写到系统管理-应用管理中-redirectUrl中。</div>
|
||||
<div class="image">
|
||||
<a-image width="100%" :src="getImage('/cloud/dueros-doc2.png')" />
|
||||
<j-image width="100%" :src="getImage('/cloud/dueros-doc2.png')" />
|
||||
</div>
|
||||
<h1>Token地址</h1>
|
||||
<div>请复制并填写:HTTPS://{location.host}/api/v1/token</div>
|
||||
<h1>ClientSecret</h1>
|
||||
<div>请复制系统管理-应用管理中的secureKey,填写到DuerOS平台。</div>
|
||||
<div class="image">
|
||||
<a-image width="100%" :src="getImage('/cloud/dueros-doc3.png')" />
|
||||
<j-image width="100%" :src="getImage('/cloud/dueros-doc3.png')" />
|
||||
</div>
|
||||
<div></div>
|
||||
<h1>WebService</h1>
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
<template>
|
||||
<page-container>
|
||||
<a-card>
|
||||
<a-row :gutter="24">
|
||||
<a-col :span="16">
|
||||
<j-card>
|
||||
<j-row :gutter="24">
|
||||
<j-col :span="16">
|
||||
<TitleComponent data="基本信息" />
|
||||
<a-form
|
||||
<j-form
|
||||
:layout="'vertical'"
|
||||
ref="formRef"
|
||||
:model="modelRef"
|
||||
>
|
||||
<a-row :gutter="24">
|
||||
<a-col :span="24">
|
||||
<a-form-item
|
||||
<j-row :gutter="24">
|
||||
<j-col :span="24">
|
||||
<j-form-item
|
||||
label="名称"
|
||||
name="name"
|
||||
:rules="[
|
||||
|
@ -25,14 +25,14 @@
|
|||
},
|
||||
]"
|
||||
>
|
||||
<a-input
|
||||
<j-input
|
||||
placeholder="请输入名称"
|
||||
v-model:value="modelRef.name"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col :span="12">
|
||||
<j-form-item
|
||||
label="产品"
|
||||
name="id"
|
||||
:rules="[
|
||||
|
@ -42,7 +42,7 @@
|
|||
},
|
||||
]"
|
||||
>
|
||||
<a-select
|
||||
<j-select
|
||||
:disabled="
|
||||
type !== 'edit' &&
|
||||
modelRef.id &&
|
||||
|
@ -54,18 +54,18 @@
|
|||
:filter-option="filterOption"
|
||||
@change="productChange"
|
||||
>
|
||||
<a-select-option
|
||||
<j-select-option
|
||||
v-for="item in productList"
|
||||
:key="item.id"
|
||||
:value="item.id"
|
||||
:label="item.name"
|
||||
>{{ item.name }}</a-select-option
|
||||
>{{ item.name }}</j-select-option
|
||||
>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
</j-select>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col :span="12">
|
||||
<j-form-item
|
||||
name="applianceType"
|
||||
:rules="{
|
||||
required: true,
|
||||
|
@ -75,50 +75,50 @@
|
|||
<template #label>
|
||||
<span>
|
||||
设备类型
|
||||
<a-tooltip
|
||||
<j-tooltip
|
||||
title="DuerOS平台拟定的规范"
|
||||
>
|
||||
<AIcon
|
||||
type="QuestionCircleOutlined"
|
||||
style="margin-left: 2px"
|
||||
/>
|
||||
</a-tooltip>
|
||||
</j-tooltip>
|
||||
</span>
|
||||
</template>
|
||||
<a-select
|
||||
<j-select
|
||||
placeholder="请选择设备类型"
|
||||
v-model:value="modelRef.applianceType"
|
||||
show-search
|
||||
:filter-option="filterOption"
|
||||
@change="typeChange"
|
||||
>
|
||||
<a-select-option
|
||||
<j-select-option
|
||||
v-for="item in typeList"
|
||||
:key="item.id"
|
||||
:value="item.id"
|
||||
:label="item.name"
|
||||
>{{ item.name }}</a-select-option
|
||||
>{{ item.name }}</j-select-option
|
||||
>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
</j-select>
|
||||
</j-form-item>
|
||||
<j-form-item
|
||||
name="productName"
|
||||
v-show="false"
|
||||
label="产品名称"
|
||||
>
|
||||
<a-input
|
||||
<j-input
|
||||
v-model:value="modelRef.productName"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col :span="24">
|
||||
<p>动作映射</p>
|
||||
<a-collapse
|
||||
<j-collapse
|
||||
v-if="modelRef.actionMappings.length"
|
||||
:activeKey="actionActiveKey"
|
||||
@change="onActionCollChange"
|
||||
>
|
||||
<a-collapse-panel
|
||||
<j-collapse-panel
|
||||
v-for="(
|
||||
item, index
|
||||
) in modelRef.actionMappings"
|
||||
|
@ -139,9 +139,9 @@
|
|||
type="DeleteOutlined"
|
||||
@click="delItem(index)"
|
||||
/></template>
|
||||
<a-row :gutter="24">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
<j-row :gutter="24">
|
||||
<j-col :span="12">
|
||||
<j-form-item
|
||||
:name="[
|
||||
'actionMappings',
|
||||
index,
|
||||
|
@ -155,16 +155,16 @@
|
|||
<template #label>
|
||||
<span>
|
||||
动作
|
||||
<a-tooltip
|
||||
<j-tooltip
|
||||
title="DuerOS平台拟定的设备类型具有的相关动作"
|
||||
>
|
||||
<AIcon
|
||||
type="QuestionCircleOutlined"
|
||||
/>
|
||||
</a-tooltip>
|
||||
</j-tooltip>
|
||||
</span>
|
||||
</template>
|
||||
<a-select
|
||||
<j-select
|
||||
placeholder="请选择动作"
|
||||
v-model:value="
|
||||
item.action
|
||||
|
@ -174,7 +174,7 @@
|
|||
filterOption
|
||||
"
|
||||
>
|
||||
<a-select-option
|
||||
<j-select-option
|
||||
v-for="i in getTypesActions(
|
||||
item.action,
|
||||
)"
|
||||
|
@ -183,13 +183,13 @@
|
|||
:label="i.name"
|
||||
>{{
|
||||
i.name
|
||||
}}</a-select-option
|
||||
}}</j-select-option
|
||||
>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
</j-select>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col :span="12">
|
||||
<j-form-item
|
||||
:name="[
|
||||
'actionMappings',
|
||||
index,
|
||||
|
@ -203,16 +203,16 @@
|
|||
<template #label>
|
||||
<span>
|
||||
操作
|
||||
<a-tooltip
|
||||
<j-tooltip
|
||||
title="映射物联网平台中所选产品具备的动作"
|
||||
>
|
||||
<AIcon
|
||||
type="QuestionCircleOutlined"
|
||||
/>
|
||||
</a-tooltip>
|
||||
</j-tooltip>
|
||||
</span>
|
||||
</template>
|
||||
<a-select
|
||||
<j-select
|
||||
placeholder="请选择操作"
|
||||
v-model:value="
|
||||
item.actionType
|
||||
|
@ -222,22 +222,22 @@
|
|||
filterOption
|
||||
"
|
||||
>
|
||||
<a-select-option
|
||||
<j-select-option
|
||||
value="command"
|
||||
>下发指令</a-select-option
|
||||
>下发指令</j-select-option
|
||||
>
|
||||
<a-select-option
|
||||
<j-select-option
|
||||
value="latestData"
|
||||
>获取历史数据</a-select-option
|
||||
>获取历史数据</j-select-option
|
||||
>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col
|
||||
</j-select>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col
|
||||
:span="24"
|
||||
v-if="item.actionType"
|
||||
>
|
||||
<a-form-item
|
||||
<j-form-item
|
||||
:name="[
|
||||
'actionMappings',
|
||||
index,
|
||||
|
@ -256,14 +256,14 @@
|
|||
item.actionType
|
||||
"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-collapse-panel>
|
||||
</a-collapse>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-button
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</j-collapse-panel>
|
||||
</j-collapse>
|
||||
</j-col>
|
||||
<j-col :span="24">
|
||||
<j-button
|
||||
type="dashed"
|
||||
style="width: 100%; margin-top: 10px"
|
||||
@click="addItem"
|
||||
|
@ -272,16 +272,16 @@
|
|||
type="PlusOutlined"
|
||||
style="margin-left: 2px"
|
||||
/>新增动作
|
||||
</a-button>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
</j-button>
|
||||
</j-col>
|
||||
<j-col :span="24">
|
||||
<p style="margin-top: 20px">属性映射</p>
|
||||
<a-collapse
|
||||
<j-collapse
|
||||
v-if="modelRef.propertyMappings.length"
|
||||
:activeKey="propertyActiveKey"
|
||||
@change="onPropertyCollChange"
|
||||
>
|
||||
<a-collapse-panel
|
||||
<j-collapse-panel
|
||||
v-for="(
|
||||
item, index
|
||||
) in modelRef.propertyMappings"
|
||||
|
@ -302,9 +302,9 @@
|
|||
type="DeleteOutlined"
|
||||
@click="delPropertyItem(index)"
|
||||
/></template>
|
||||
<a-row :gutter="24">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
<j-row :gutter="24">
|
||||
<j-col :span="12">
|
||||
<j-form-item
|
||||
label="DuerOS属性"
|
||||
:name="[
|
||||
'propertyMappings',
|
||||
|
@ -317,7 +317,7 @@
|
|||
'请选择DuerOS属性',
|
||||
}"
|
||||
>
|
||||
<a-select
|
||||
<j-select
|
||||
placeholder="请选择DuerOS属性"
|
||||
v-model:value="
|
||||
item.source
|
||||
|
@ -327,7 +327,7 @@
|
|||
filterOption
|
||||
"
|
||||
>
|
||||
<a-select-option
|
||||
<j-select-option
|
||||
v-for="i in getDuerOSProperties(
|
||||
item.source,
|
||||
)"
|
||||
|
@ -335,13 +335,13 @@
|
|||
:value="i.id"
|
||||
>{{
|
||||
i.name
|
||||
}}</a-select-option
|
||||
}}</j-select-option
|
||||
>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
</j-select>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col :span="12">
|
||||
<j-form-item
|
||||
label="平台属性"
|
||||
:name="[
|
||||
'propertyMappings',
|
||||
|
@ -354,7 +354,7 @@
|
|||
'请选择平台属性',
|
||||
}"
|
||||
>
|
||||
<a-select
|
||||
<j-select
|
||||
placeholder="请选择平台属性"
|
||||
v-model:value="
|
||||
item.target
|
||||
|
@ -365,7 +365,7 @@
|
|||
filterOption
|
||||
"
|
||||
>
|
||||
<a-select-option
|
||||
<j-select-option
|
||||
v-for="i in getProductProperties(
|
||||
item.target,
|
||||
)"
|
||||
|
@ -373,17 +373,17 @@
|
|||
:value="item.id"
|
||||
>{{
|
||||
i.name
|
||||
}}</a-select-option
|
||||
}}</j-select-option
|
||||
>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-collapse-panel>
|
||||
</a-collapse>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-button
|
||||
</j-select>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</j-collapse-panel>
|
||||
</j-collapse>
|
||||
</j-col>
|
||||
<j-col :span="24">
|
||||
<j-button
|
||||
type="dashed"
|
||||
style="width: 100%; margin-top: 10px"
|
||||
@click="addPropertyItem"
|
||||
|
@ -392,10 +392,10 @@
|
|||
type="PlusOutlined"
|
||||
style="margin-left: 2px"
|
||||
/>新增属性
|
||||
</a-button>
|
||||
</a-col>
|
||||
<a-col :span="24" style="margin-top: 20px">
|
||||
<a-form-item
|
||||
</j-button>
|
||||
</j-col>
|
||||
<j-col :span="24" style="margin-top: 20px">
|
||||
<j-form-item
|
||||
label="说明"
|
||||
name="description"
|
||||
:rules="{
|
||||
|
@ -403,16 +403,16 @@
|
|||
message: '最多输入200个字符',
|
||||
}"
|
||||
>
|
||||
<a-textarea
|
||||
<j-textarea
|
||||
v-model:value="modelRef.description"
|
||||
placeholder="请输入说明"
|
||||
showCount
|
||||
:maxlength="200"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</j-form>
|
||||
<div v-if="type === 'edit'">
|
||||
<PermissionButton
|
||||
type="primary"
|
||||
|
@ -423,12 +423,12 @@
|
|||
保存
|
||||
</PermissionButton>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
</j-col>
|
||||
<j-col :span="8">
|
||||
<Doc />
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-card>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</j-card>
|
||||
</page-container>
|
||||
</template>
|
||||
|
||||
|
@ -442,9 +442,10 @@ import {
|
|||
detail,
|
||||
} from '@/api/northbound/dueros';
|
||||
import _ from 'lodash';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { message } from 'jetlinks-ui-components';
|
||||
import { useMenuStore } from '@/store/menu';
|
||||
|
||||
const router = useRouter();
|
||||
const menuStory = useMenuStore();
|
||||
const route = useRoute();
|
||||
|
||||
const formRef = ref();
|
||||
|
@ -640,7 +641,8 @@ const saveBtn = async () => {
|
|||
if (resp.status === 200) {
|
||||
message.success('操作成功!');
|
||||
formRef.value.resetFields();
|
||||
router.push('/iot/northbound/DuerOS/');
|
||||
menuStory.jumpPage('Northbound/DuerOS');
|
||||
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
<template>
|
||||
<page-container>
|
||||
<Search
|
||||
<j-advanced-search
|
||||
:columns="columns"
|
||||
target="northbound-dueros"
|
||||
@search="handleSearch"
|
||||
/>
|
||||
<JTable
|
||||
<JProTable
|
||||
ref="instanceRef"
|
||||
:columns="columns"
|
||||
:request="query"
|
||||
|
@ -13,7 +13,7 @@
|
|||
:params="params"
|
||||
>
|
||||
<template #headerTitle>
|
||||
<a-space>
|
||||
<j-space>
|
||||
<PermissionButton
|
||||
type="primary"
|
||||
@click="handleAdd"
|
||||
|
@ -22,7 +22,7 @@
|
|||
<template #icon><AIcon type="PlusOutlined" /></template>
|
||||
新增
|
||||
</PermissionButton>
|
||||
</a-space>
|
||||
</j-space>
|
||||
</template>
|
||||
<template #card="slotProps">
|
||||
<CardBox
|
||||
|
@ -45,18 +45,18 @@
|
|||
>
|
||||
{{ slotProps.name }}
|
||||
</h3>
|
||||
<a-row>
|
||||
<a-col :span="12">
|
||||
<j-row>
|
||||
<j-col :span="12">
|
||||
<div class="card-item-content-text">产品</div>
|
||||
<div>{{ slotProps?.productName }}</div>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
</j-col>
|
||||
<j-col :span="12">
|
||||
<div class="card-item-content-text">
|
||||
设备类型
|
||||
</div>
|
||||
<div>{{ slotProps?.applianceType?.text }}</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</template>
|
||||
<template #actions="item">
|
||||
<PermissionButton
|
||||
|
@ -81,7 +81,7 @@
|
|||
</CardBox>
|
||||
</template>
|
||||
<template #state="slotProps">
|
||||
<a-badge
|
||||
<j-badge
|
||||
:text="slotProps.state?.text"
|
||||
:status="statusMap.get(slotProps.state?.value)"
|
||||
/>
|
||||
|
@ -90,7 +90,7 @@
|
|||
{{ slotProps.applianceType.text }}
|
||||
</template>
|
||||
<template #action="slotProps">
|
||||
<a-space>
|
||||
<j-space>
|
||||
<template
|
||||
v-for="i in getActions(slotProps, 'table')"
|
||||
:key="i.key"
|
||||
|
@ -109,9 +109,9 @@
|
|||
<template #icon><AIcon :type="i.icon" /></template>
|
||||
</PermissionButton>
|
||||
</template>
|
||||
</a-space>
|
||||
</j-space>
|
||||
</template>
|
||||
</JTable>
|
||||
</JProTable>
|
||||
</page-container>
|
||||
</template>
|
||||
|
||||
|
@ -124,15 +124,13 @@ import {
|
|||
queryProductList,
|
||||
queryTypes,
|
||||
} from '@/api/northbound/dueros';
|
||||
import type { ActionsType } from '@/components/Table/index.vue';
|
||||
import type { ActionsType } from '@/views/device/Instance/typings';
|
||||
import { getImage } from '@/utils/comm';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { message } from 'jetlinks-ui-components';
|
||||
import { useMenuStore } from 'store/menu';
|
||||
|
||||
const router = useRouter();
|
||||
const instanceRef = ref<Record<string, any>>({});
|
||||
const params = ref<Record<string, any>>({});
|
||||
const current = ref<Record<string, any>>({});
|
||||
const menuStory = useMenuStore();
|
||||
|
||||
const statusMap = new Map();
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
<div class="dialog-box">
|
||||
<div class="dialog-header">
|
||||
<div class="dialog-title">
|
||||
<a-badge
|
||||
<j-badge
|
||||
:color="
|
||||
statusColor.get(
|
||||
item.error ? 'error' : 'success',
|
||||
|
@ -41,7 +41,7 @@
|
|||
class="dialog-editor"
|
||||
v-if="visible.includes(item.key)"
|
||||
>
|
||||
<a-textarea autoSize :bordered="false" :value="item?.detail" />
|
||||
<j-textarea autoSize :bordered="false" :value="item?.detail" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -50,8 +50,9 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const operationMap = new Map();
|
||||
import moment from 'moment';
|
||||
|
||||
const operationMap = new Map();
|
||||
operationMap.set('connection', '连接');
|
||||
operationMap.set('auth', '权限验证');
|
||||
operationMap.set('decode', '解码');
|
||||
|
@ -80,19 +81,9 @@ const getDetail = (item: any) => {
|
|||
visible.value.splice(index, 1);
|
||||
}
|
||||
};
|
||||
|
||||
watchEffect(() => {
|
||||
console.log(props.data)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
// @import 'ant-design-vue/es/style/themes/default.less';
|
||||
|
||||
// :root {
|
||||
// --dialog-primary-color: @primary-color;
|
||||
// }
|
||||
|
||||
.dialog-item {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<a-table
|
||||
<j-table
|
||||
rowKey="id"
|
||||
:columns="columns"
|
||||
:data-source="dataSource"
|
||||
|
@ -36,12 +36,11 @@
|
|||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</a-table>
|
||||
</j-table>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { PropType } from "vue-demi";
|
||||
|
||||
import { PropType } from "vue";
|
||||
|
||||
type Emits = {
|
||||
(e: 'update:modelValue', data: Record<string, any>[]): void;
|
||||
|
|
|
@ -1,110 +1,110 @@
|
|||
<template>
|
||||
<div class="function">
|
||||
<a-form :layout="'vertical'" ref="formRef" :model="modelRef">
|
||||
<a-row :gutter="24">
|
||||
<a-col :span="6">
|
||||
<a-form-item
|
||||
<j-form :layout="'vertical'" ref="formRef" :model="modelRef">
|
||||
<j-row :gutter="24">
|
||||
<j-col :span="6">
|
||||
<j-form-item
|
||||
name="type"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '请选择',
|
||||
}"
|
||||
>
|
||||
<a-select
|
||||
<j-select
|
||||
placeholder="请选择"
|
||||
v-model:value="modelRef.type"
|
||||
show-search
|
||||
:filter-option="filterOption"
|
||||
>
|
||||
<a-select-option value="READ_PROPERTY"
|
||||
>读取属性</a-select-option
|
||||
<j-select-option value="READ_PROPERTY"
|
||||
>读取属性</j-select-option
|
||||
>
|
||||
<a-select-option value="WRITE_PROPERTY"
|
||||
>修改属性</a-select-option
|
||||
<j-select-option value="WRITE_PROPERTY"
|
||||
>修改属性</j-select-option
|
||||
>
|
||||
<a-select-option value="INVOKE_FUNCTION"
|
||||
>调用功能</a-select-option
|
||||
<j-select-option value="INVOKE_FUNCTION"
|
||||
>调用功能</j-select-option
|
||||
>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col
|
||||
</j-select>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col
|
||||
:span="6"
|
||||
v-if="
|
||||
['READ_PROPERTY', 'WRITE_PROPERTY'].includes(
|
||||
modelRef.type,
|
||||
modelRef?.type || '',
|
||||
)
|
||||
"
|
||||
>
|
||||
<a-form-item
|
||||
<j-form-item
|
||||
name="properties"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '请选择属性',
|
||||
}"
|
||||
>
|
||||
<a-select
|
||||
<j-select
|
||||
placeholder="请选择属性"
|
||||
v-model:value="modelRef.properties"
|
||||
show-search
|
||||
:filter-option="filterOption"
|
||||
>
|
||||
<a-select-option
|
||||
<j-select-option
|
||||
v-for="i in metadata?.properties || []"
|
||||
:key="i.id"
|
||||
:value="i.id"
|
||||
:label="i.name"
|
||||
>{{ i.name }}</a-select-option
|
||||
>{{ i.name }}</j-select-option
|
||||
>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="6" v-if="modelRef.type === 'WRITE_PROPERTY'">
|
||||
<a-form-item
|
||||
</j-select>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col :span="6" v-if="modelRef.type === 'WRITE_PROPERTY'">
|
||||
<j-form-item
|
||||
name="propertyValue"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '请输入值',
|
||||
}"
|
||||
>
|
||||
<a-input v-model:value="modelRef.propertyValue" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="6" v-if="modelRef.type === 'INVOKE_FUNCTION'">
|
||||
<a-form-item
|
||||
<j-input v-model:value="modelRef.propertyValue" />
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col :span="6" v-if="modelRef.type === 'INVOKE_FUNCTION'">
|
||||
<j-form-item
|
||||
name="function"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '请选择功能',
|
||||
}"
|
||||
>
|
||||
<a-select
|
||||
<j-select
|
||||
placeholder="请选择功能"
|
||||
v-model:value="modelRef.function"
|
||||
show-search
|
||||
:filter-option="filterOption"
|
||||
@change="funcChange"
|
||||
>
|
||||
<a-select-option
|
||||
<j-select-option
|
||||
v-for="i in metadata?.functions || []"
|
||||
:key="i.id"
|
||||
:value="i.id"
|
||||
:label="i.name"
|
||||
>{{ i.name }}</a-select-option
|
||||
>{{ i.name }}</j-select-option
|
||||
>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="4">
|
||||
<a-button type="primary" @click="saveBtn">发送</a-button>
|
||||
</a-col>
|
||||
<a-col
|
||||
</j-select>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col :span="4">
|
||||
<j-button type="primary" @click="saveBtn">发送</j-button>
|
||||
</j-col>
|
||||
<j-col
|
||||
:span="24"
|
||||
v-if="
|
||||
modelRef.type === 'INVOKE_FUNCTION' && modelRef.function && modelRef.inputs.length
|
||||
"
|
||||
>
|
||||
<a-form-item
|
||||
<j-form-item
|
||||
name="inputs"
|
||||
label="参数列表"
|
||||
:rules="{
|
||||
|
@ -113,10 +113,10 @@
|
|||
}"
|
||||
>
|
||||
<EditTable v-model="modelRef.inputs" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</j-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -1,3 +1,106 @@
|
|||
<template>
|
||||
log
|
||||
</template>
|
||||
<div class="log-item" :key="data.id">
|
||||
<div class="log-card">
|
||||
<div class="log-icon" @click="visible = !visible">
|
||||
<AIcon :type="visible ? 'DownOutlined' : 'RightOutlined'" />
|
||||
</div>
|
||||
<div class="log-box">
|
||||
<div class="log-header">
|
||||
<div class="log-title">
|
||||
<j-tag color="error">ERROR</j-tag>
|
||||
{{ operationMap.get(data.operation) }}
|
||||
</div>
|
||||
<div class="log-time">
|
||||
{{ dayjs(data.endTime).format('YYYY-MM-DD HH:mm:ss') }}
|
||||
</div>
|
||||
</div>
|
||||
<div className="log-editor" v-if="visible">
|
||||
<j-textarea
|
||||
autoSize
|
||||
:bordered="false"
|
||||
:value="data?.detail"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
const operationMap = new Map();
|
||||
operationMap.set('connection', '连接');
|
||||
operationMap.set('auth', '权限验证');
|
||||
operationMap.set('decode', '解码');
|
||||
operationMap.set('encode', '编码');
|
||||
operationMap.set('request', '请求');
|
||||
operationMap.set('response', '响应');
|
||||
operationMap.set('downstream', '下行消息');
|
||||
operationMap.set('upstream', '上行消息');
|
||||
|
||||
const visible = ref<boolean>(false);
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.log-item {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
width: 100%;
|
||||
padding-bottom: 12px;
|
||||
|
||||
.log-card {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
background-color: #fff;
|
||||
|
||||
.log-icon {
|
||||
margin-right: 10px;
|
||||
color: rgba(0, 0, 0, 0.75);
|
||||
font-weight: 500;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.log-box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
|
||||
.log-header {
|
||||
.log-title {
|
||||
color: rgba(0, 0, 0, 0.75);
|
||||
font-weight: 700;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.log-time {
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.log-editor {
|
||||
width: 100%;
|
||||
margin-top: 10px;
|
||||
color: rgba(0, 0, 0, 0.75);
|
||||
|
||||
textarea {
|
||||
color: black !important;
|
||||
background-color: #fafafa !important;
|
||||
}
|
||||
|
||||
textarea::-webkit-scrollbar {
|
||||
width: 5px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,20 +1,20 @@
|
|||
<template>
|
||||
<a-row :gutter="24">
|
||||
<a-col :span="16">
|
||||
<a-row :gutter="24" style="margin-bottom: 20px">
|
||||
<a-col :span="12" v-for="item in messageArr" :key="item">
|
||||
<j-row :gutter="24">
|
||||
<j-col :span="16">
|
||||
<j-row :gutter="24" style="margin-bottom: 20px">
|
||||
<j-col :span="12" v-for="item in messageArr" :key="item">
|
||||
<div
|
||||
:style="messageStyleMap.get(item.status)"
|
||||
class="message-status"
|
||||
>
|
||||
<a-badge
|
||||
<j-badge
|
||||
:status="messageStatusMap.get(item.status)"
|
||||
style="margin-right: 5px"
|
||||
/>
|
||||
<span>{{ item.text }}</span>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</j-col>
|
||||
</j-row>
|
||||
<div>
|
||||
<TitleComponent data="调试" />
|
||||
<div class="content">
|
||||
|
@ -26,8 +26,8 @@
|
|||
</div>
|
||||
<div><Function /></div>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
</j-col>
|
||||
<j-col :span="8">
|
||||
<div class="right-log">
|
||||
<TitleComponent data="日志" />
|
||||
<div :style="{ marginTop: '10px' }">
|
||||
|
@ -38,11 +38,11 @@
|
|||
:key="item.key"
|
||||
/>
|
||||
</template>
|
||||
<a-empty v-else />
|
||||
<j-empty v-else />
|
||||
</div>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { Badge, Descriptions, Modal, Tooltip } from "ant-design-vue"
|
||||
import { Badge, Descriptions, Modal, Tooltip, AIcon, DescriptionsItem } from "jetlinks-ui-components"
|
||||
import TitleComponent from '@/components/TitleComponent/index.vue'
|
||||
import styles from './index.module.less'
|
||||
import AIcon from "@/components/AIcon";
|
||||
import _ from "lodash";
|
||||
|
||||
const DiagnosticAdvice = defineComponent({
|
||||
|
@ -42,11 +41,11 @@ const DiagnosticAdvice = defineComponent({
|
|||
<div style={{ marginTop: 15 }}>
|
||||
<TitleComponent data="连接信息" />
|
||||
<Descriptions column={2}>
|
||||
<Descriptions.Item span={1} label="设备ID">
|
||||
<DescriptionsItem span={1} label="设备ID">
|
||||
{data?.info?.id || ''}
|
||||
</Descriptions.Item>
|
||||
</DescriptionsItem>
|
||||
{data?.info?.address?.length > 0 && (
|
||||
<Descriptions.Item span={1} label="连接地址">
|
||||
<DescriptionsItem span={1} label="连接地址">
|
||||
<Tooltip
|
||||
placement="topLeft"
|
||||
title={
|
||||
|
@ -69,11 +68,11 @@ const DiagnosticAdvice = defineComponent({
|
|||
))}
|
||||
</div>
|
||||
</Tooltip>
|
||||
</Descriptions.Item>
|
||||
</DescriptionsItem>
|
||||
)}
|
||||
|
||||
{(_.flatten(_.map(data?.info?.config, 'properties')) || []).map((item: any, index: number) => (
|
||||
<Descriptions.Item
|
||||
<DescriptionsItem
|
||||
key={index}
|
||||
span={1}
|
||||
label={
|
||||
|
@ -90,7 +89,7 @@ const DiagnosticAdvice = defineComponent({
|
|||
}
|
||||
>
|
||||
{data?.info?.configValue[item?.property] || ''}
|
||||
</Descriptions.Item>
|
||||
</DescriptionsItem>
|
||||
))}
|
||||
</Descriptions>
|
||||
</div>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import AIcon from "@/components/AIcon";
|
||||
import { useInstanceStore } from "@/store/instance";
|
||||
import { useMenuStore } from "@/store/menu";
|
||||
import { Button, Descriptions, Modal } from "ant-design-vue"
|
||||
import { AIcon, Button, Modal, Descriptions, DescriptionsItem, Space } from "jetlinks-ui-components"
|
||||
import styles from './index.module.less'
|
||||
|
||||
const ManualInspection = defineComponent({
|
||||
|
@ -39,12 +38,12 @@ const ManualInspection = defineComponent({
|
|||
<div style={{ marginTop: '10px' }}>
|
||||
<Descriptions title={data?.data?.name} layout="vertical" bordered>
|
||||
{(data?.data?.properties || []).map((item: any) => (
|
||||
<Descriptions.Item
|
||||
<DescriptionsItem
|
||||
key={item.property}
|
||||
label={`${item.name}${item?.description ? `(${item.description})` : ''}`}
|
||||
>
|
||||
{data?.configuration[item.property] || ''}
|
||||
</Descriptions.Item>
|
||||
</DescriptionsItem>
|
||||
))}
|
||||
</Descriptions>
|
||||
</div>
|
||||
|
@ -80,30 +79,30 @@ const ManualInspection = defineComponent({
|
|||
<Descriptions title={data?.data?.name} layout="vertical" bordered>
|
||||
{data.configuration?.provider === 'OneNet' ? (
|
||||
<>
|
||||
<Descriptions.Item label={'接口地址'}>
|
||||
<DescriptionsItem label={'接口地址'}>
|
||||
{data?.configuration?.configuration?.apiAddress || ''}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={'apiKey'}>
|
||||
</DescriptionsItem>
|
||||
<DescriptionsItem label={'apiKey'}>
|
||||
{data?.configuration?.configuration?.apiKey || ''}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={'通知Token'}>
|
||||
</DescriptionsItem>
|
||||
<DescriptionsItem label={'通知Token'}>
|
||||
{data?.configuration?.configuration?.validateToken || ''}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={'aesKey'}>
|
||||
</DescriptionsItem>
|
||||
<DescriptionsItem label={'aesKey'}>
|
||||
{data?.configuration?.configuration?.aesKey || ''}
|
||||
</Descriptions.Item>
|
||||
</DescriptionsItem>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Descriptions.Item label={'接口地址'}>
|
||||
<DescriptionsItem label={'接口地址'}>
|
||||
{data?.configuration?.configuration?.apiAddress || ''}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={'appKey'}>
|
||||
</DescriptionsItem>
|
||||
<DescriptionsItem label={'appKey'}>
|
||||
{data?.configuration?.configuration?.appKey || ''}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={'appSecret'}>
|
||||
</DescriptionsItem>
|
||||
<DescriptionsItem label={'appSecret'}>
|
||||
{data?.configuration?.configuration?.appSecret || ''}
|
||||
</Descriptions.Item>
|
||||
</DescriptionsItem>
|
||||
</>
|
||||
)}
|
||||
</Descriptions>
|
||||
|
@ -140,45 +139,45 @@ const ManualInspection = defineComponent({
|
|||
<Descriptions title={data?.data?.name} layout="vertical" bordered>
|
||||
{data?.configuration?.configuration?.shareCluster ? (
|
||||
<>
|
||||
<Descriptions.Item label={'SIP 域'}>
|
||||
<DescriptionsItem label={'SIP 域'}>
|
||||
{data?.configuration?.configuration?.domain || ''}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={'SIP ID'}>
|
||||
</DescriptionsItem>
|
||||
<DescriptionsItem label={'SIP ID'}>
|
||||
{data?.configuration?.configuration?.sipId || ''}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={'集群'}>
|
||||
</DescriptionsItem>
|
||||
<DescriptionsItem label={'集群'}>
|
||||
{data?.configuration?.configuration?.shareCluster ? '共享配置' : '独立配置'}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={'SIP 地址'}>
|
||||
</DescriptionsItem>
|
||||
<DescriptionsItem label={'SIP 地址'}>
|
||||
{`${data?.configuration?.configuration?.hostPort?.host}:${data?.configuration?.configuration?.hostPort?.port}`}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={'公网 Host'}>
|
||||
</DescriptionsItem>
|
||||
<DescriptionsItem label={'公网 Host'}>
|
||||
{`${data?.configuration?.configuration?.hostPort?.publicHost}:${data?.configuration?.configuration?.hostPort?.publicPort}`}
|
||||
</Descriptions.Item>
|
||||
</DescriptionsItem>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Descriptions.Item label={'SIP 域'}>
|
||||
<DescriptionsItem label={'SIP 域'}>
|
||||
{data?.configuration?.configuration?.domain || ''}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={'SIP ID'}>
|
||||
</DescriptionsItem>
|
||||
<DescriptionsItem label={'SIP ID'}>
|
||||
{data?.configuration?.configuration?.sipId || ''}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={'集群'}>
|
||||
</DescriptionsItem>
|
||||
<DescriptionsItem label={'集群'}>
|
||||
{data?.configuration?.configuration?.shareCluster ? '共享配置' : '独立配置'}
|
||||
</Descriptions.Item>
|
||||
</DescriptionsItem>
|
||||
{data?.configuration?.configuration?.cluster.map((i: any, it: number) => (
|
||||
<div key={it}>
|
||||
<div>节点{it + 1}</div>
|
||||
<Descriptions.Item label={'节点名称'}>
|
||||
<DescriptionsItem label={'节点名称'}>
|
||||
{i?.clusterNodeId || ''}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={'SIP 地址'}>
|
||||
</DescriptionsItem>
|
||||
<DescriptionsItem label={'SIP 地址'}>
|
||||
{`${i.host}:${i?.port}`}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={'公网 Host'}>
|
||||
</DescriptionsItem>
|
||||
<DescriptionsItem label={'公网 Host'}>
|
||||
{`${i?.publicHost}:${i?.publicPort}`}
|
||||
</Descriptions.Item>
|
||||
</DescriptionsItem>
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
|
@ -207,20 +206,29 @@ const ManualInspection = defineComponent({
|
|||
title="人工检查"
|
||||
visible
|
||||
width={1000}
|
||||
cancelText="去修改"
|
||||
okText="确认无误"
|
||||
onOk={() => {
|
||||
emit('save', data)
|
||||
}}
|
||||
onCancel={() => {
|
||||
if (data.type === 'device') {
|
||||
instanceStore.tabActiveKey = 'Info'
|
||||
} else if (data.type === 'product') {
|
||||
menuStory.jumpPage('device/Product/Detail', { id: data.productId, tab: 'access' });
|
||||
} else {
|
||||
menuStory.jumpPage('link/AccessConfig/Detail', { id: data.configuration?.id });
|
||||
}
|
||||
}}>
|
||||
emit('close')
|
||||
}}
|
||||
v-slots={{
|
||||
footer: <Space>
|
||||
<Button onClick={() => {
|
||||
if (data.type === 'device') {
|
||||
instanceStore.tabActiveKey = 'Info'
|
||||
} else if (data.type === 'product') {
|
||||
menuStory.jumpPage('device/Product/Detail', { id: data.productId, tab: 'access' });
|
||||
} else {
|
||||
menuStory.jumpPage('link/AccessConfig/Detail', { id: data.configuration?.id });
|
||||
}
|
||||
}}>去修改</Button>
|
||||
<Button onClick={() => {
|
||||
emit('save', data)
|
||||
}}>确认无误</Button>
|
||||
</Space>
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex' }}>{dataRender()}</div>
|
||||
</Modal>
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Badge, Button, message, Popconfirm, Space } from "ant-design-vue"
|
||||
import { Badge, Button, message, Popconfirm, Space } from "jetlinks-ui-components"
|
||||
import TitleComponent from '@/components/TitleComponent/index.vue'
|
||||
import styles from './index.module.less'
|
||||
import type { ListProps } from './util'
|
||||
|
@ -13,6 +13,7 @@ import ManualInspection from './ManualInspection'
|
|||
import { deployDevice } from "@/api/initHome"
|
||||
import PermissionButton from '@/components/PermissionButton/index.vue'
|
||||
import { useMenuStore } from "@/store/menu"
|
||||
import BindParentDevice from '../../components/BindParentDevice/index.vue'
|
||||
|
||||
type TypeProps = 'network' | 'child-device' | 'media' | 'cloud' | 'channel'
|
||||
|
||||
|
@ -1691,6 +1692,102 @@ const Status = defineComponent({
|
|||
}}
|
||||
/>
|
||||
)}
|
||||
{
|
||||
bindParentVisible && (
|
||||
<BindParentDevice
|
||||
data={device.value}
|
||||
onCancel={() => {
|
||||
bindParentVisible.value = false
|
||||
}}
|
||||
onOk={async (parentId: string) => {
|
||||
let item: ListProps | undefined = undefined;
|
||||
const response = await detail(parentId);
|
||||
if (response.status === 200) {
|
||||
if (response?.result?.state?.value === 'notActive') {
|
||||
item = {
|
||||
key: 'parent-device',
|
||||
name: '网关父设备',
|
||||
desc: '诊断网关父设备状态是否正常,禁用或离线将导致连接失败',
|
||||
status: 'error',
|
||||
text: '异常',
|
||||
info: (
|
||||
<div>
|
||||
<div class={styles.infoItem}>
|
||||
<Badge
|
||||
status="default"
|
||||
text={
|
||||
<span>
|
||||
网关父设备已禁用,请先
|
||||
<PermissionButton
|
||||
hasPermission="device/Product:action"
|
||||
popConfirm={{
|
||||
title: '确认启用',
|
||||
onConfirm: async () => {
|
||||
const resp = await _deploy(response?.result?.id || '');
|
||||
if (resp.status === 200) {
|
||||
message.success('操作成功!');
|
||||
list.value = modifyArrayList(
|
||||
list.value,
|
||||
{
|
||||
key: 'parent-device',
|
||||
name: '网关父设备',
|
||||
desc: '诊断网关父设备状态是否正常,禁用或离线将导致连接失败',
|
||||
status: 'success',
|
||||
text: '正常',
|
||||
info: null,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
启用
|
||||
</PermissionButton>
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
} else if (response?.state?.value === 'online') {
|
||||
item = {
|
||||
key: 'parent-device',
|
||||
name: '网关父设备',
|
||||
desc: '诊断网关父设备状态是否正常,禁用或离线将导致连接失败',
|
||||
status: 'success',
|
||||
text: '正常',
|
||||
info: null,
|
||||
};
|
||||
} else {
|
||||
item = {
|
||||
key: 'parent-device',
|
||||
name: '网关父设备',
|
||||
desc: '诊断网关父设备状态是否正常,禁用或离线将导致连接失败',
|
||||
status: 'error',
|
||||
text: '异常',
|
||||
info: (
|
||||
<div>
|
||||
<div class={styles.infoItem}>
|
||||
<Badge
|
||||
status="default"
|
||||
text={<span>网关父设备已离线,请先排查网关设备故障</span>}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
}
|
||||
if (item) {
|
||||
list.value = modifyArrayList(unref(list), item);
|
||||
}
|
||||
instanceStore.current.parentId = parentId;
|
||||
bindParentVisible.value = false
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
},
|
||||
})
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<a-card>
|
||||
<j-card>
|
||||
<div class="diagnose">
|
||||
<div class="diagnose-header" :style="{background: headerColorMap.get(topState)}">
|
||||
<div class="diagnose-top">
|
||||
|
@ -19,7 +19,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="diagnose-progress">
|
||||
<a-progress
|
||||
<j-progress
|
||||
:percent="percent"
|
||||
:showInfo="false"
|
||||
size="small"
|
||||
|
@ -38,7 +38,7 @@
|
|||
<Status v-show="activeKey !== 'message'" :providerType="providerType" @countChange="countChange" @percentChange="percentChange" @stateChange="stateChange" />
|
||||
</div>
|
||||
</div>
|
||||
</a-card>
|
||||
</j-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
</div>
|
||||
</template>
|
||||
<j-form layout="vertical" ref="formRef" :model="modelRef">
|
||||
<template v-for="(item, index) in props.config" :key="index">
|
||||
<template v-for="(item, index) in (props.config || [])" :key="index">
|
||||
<j-form-item
|
||||
:name="item.property"
|
||||
v-for="i in item.properties"
|
||||
|
@ -54,7 +54,7 @@
|
|||
<script lang="ts" setup>
|
||||
import { modify } from '@/api/device/instance';
|
||||
import { useInstanceStore } from '@/store/instance';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { message } from 'jetlinks-ui-components';
|
||||
|
||||
const emit = defineEmits(['close', 'save']);
|
||||
|
||||
|
|
|
@ -97,7 +97,7 @@ import {
|
|||
_deploy,
|
||||
configurationReset,
|
||||
} from '@/api/device/instance';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { message } from 'jetlinks-ui-components';
|
||||
import Save from './Save.vue';
|
||||
|
||||
const instanceStore = useInstanceStore();
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
<script lang="ts" setup>
|
||||
import { queryUserListNoPaging, saveRelations } from '@/api/device/instance';
|
||||
import { useInstanceStore } from '@/store/instance';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { message } from 'jetlinks-ui-components';
|
||||
|
||||
const emit = defineEmits(['close', 'save']);
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<j-table
|
||||
rowKey="id"
|
||||
:columns="columns"
|
||||
:datj-source="dataSource"
|
||||
:data-source="dataSource"
|
||||
bordered
|
||||
:pagination="false"
|
||||
>
|
||||
|
@ -49,7 +49,7 @@
|
|||
|
||||
<script lang="ts" setup>
|
||||
import { useInstanceStore } from '@/store/instance';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { message } from 'jetlinks-ui-components';
|
||||
import _ from 'lodash';
|
||||
import { saveTags, delTags } from '@/api/device/instance'
|
||||
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
<template>
|
||||
<j-card>
|
||||
<Search
|
||||
<j-advanced-search
|
||||
:columns="columns"
|
||||
target="device-instance-log"
|
||||
@search="handleSearch"
|
||||
type="simple"
|
||||
class="search"
|
||||
/>
|
||||
<JProTable
|
||||
ref="instanceRefLog"
|
||||
|
@ -12,6 +14,7 @@
|
|||
model="TABLE"
|
||||
:defaultParams="{ sorts: [{ name: 'timestamp', order: 'desc' }] }"
|
||||
:params="params"
|
||||
:bodyStyle="{ padding: 0 }"
|
||||
>
|
||||
<template #type="slotProps">
|
||||
{{ slotProps?.type?.text }}
|
||||
|
@ -150,4 +153,7 @@ const handleSearch = (_params: any) => {
|
|||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.search {
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
|
@ -1,28 +1,30 @@
|
|||
<template>
|
||||
<Search :columns="columns" target="device-instance-running-events" />
|
||||
<JTable
|
||||
<j-advanced-search class="search" type="simple" :columns="columns" target="device-instance-running-events" @search="handleSearch" />
|
||||
<JProTable
|
||||
ref="eventsRef"
|
||||
:columns="columns"
|
||||
:request="_getEventList"
|
||||
model="TABLE"
|
||||
:bodyStyle="{ padding: '0 24px' }"
|
||||
:params="params"
|
||||
:bodyStyle="{ padding: '0 0 0 24px' }"
|
||||
>
|
||||
<template #timestamp="slotProps">
|
||||
{{ moment(slotProps.timestamp).format('YYYY-MM-DD HH:mm:ss') }}
|
||||
{{ dayjs(slotProps.timestamp).format('YYYY-MM-DD HH:mm:ss') }}
|
||||
</template>
|
||||
<template #action="slotProps">
|
||||
<a-button type="link" @click="detail(slotProps)">
|
||||
<j-button type="link" @click="detail(slotProps)">
|
||||
<AIcon type="SearchOutlined" />
|
||||
</a-button>
|
||||
</j-button>
|
||||
</template>
|
||||
</JTable>
|
||||
</JProTable>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import moment from 'moment';
|
||||
import dayjs from 'dayjs';
|
||||
import { getEventList } from '@/api/device/instance';
|
||||
import { useInstanceStore } from '@/store/instance';
|
||||
import { Modal } from 'ant-design-vue';
|
||||
import { Modal } from 'jetlinks-ui-components';
|
||||
import JsonViewer from 'vue-json-viewer';
|
||||
|
||||
const events = defineProps({
|
||||
data: {
|
||||
|
@ -51,11 +53,11 @@ const columns = ref<Record<string, any>>([
|
|||
]);
|
||||
const params = ref<Record<string, any>>({});
|
||||
|
||||
const _getEventList = () =>
|
||||
const _getEventList = (_params: any) =>
|
||||
getEventList(
|
||||
instanceStore.current.id || '',
|
||||
events.data.id || '',
|
||||
params.value,
|
||||
_params
|
||||
);
|
||||
|
||||
watchEffect(() => {
|
||||
|
@ -78,12 +80,25 @@ watchEffect(() => {
|
|||
}
|
||||
});
|
||||
|
||||
const detail = () => {
|
||||
const handleSearch = (_params: any) => {
|
||||
params.value = _params;
|
||||
};
|
||||
|
||||
const detail = (_info: any) => {
|
||||
Modal.info({
|
||||
title: () => '详情',
|
||||
width: 850,
|
||||
content: () => h('div', {}, [h('p', '暂未开发')]),
|
||||
content: () => h('JsonViewer', {
|
||||
'expand-depth': 5,
|
||||
value: _info
|
||||
}),
|
||||
okText: '关闭',
|
||||
});
|
||||
};
|
||||
</script>
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.search {
|
||||
padding: 0 0 0 24px;
|
||||
}
|
||||
</style>
|
|
@ -1,36 +1,36 @@
|
|||
<template>
|
||||
<a-spin :spinning="loading">
|
||||
<j-spin :spinning="loading">
|
||||
<div>
|
||||
<a-space>
|
||||
<j-space>
|
||||
<div>
|
||||
统计周期:
|
||||
<a-select v-model:value="cycle" style="width: 120px">
|
||||
<a-select-option value="*" v-if="_type"
|
||||
>实际值</a-select-option
|
||||
<j-select v-model:value="cycle" style="width: 120px">
|
||||
<j-select-option value="*" v-if="_type"
|
||||
>实际值</j-select-option
|
||||
>
|
||||
<a-select-option value="1m">按分钟统计</a-select-option>
|
||||
<a-select-option value="1h">按小时统计</a-select-option>
|
||||
<a-select-option value="1d">按天统计</a-select-option>
|
||||
<a-select-option value="1w">按周统计</a-select-option>
|
||||
<a-select-option value="1M">按月统计</a-select-option>
|
||||
</a-select>
|
||||
<j-select-option value="1m">按分钟统计</j-select-option>
|
||||
<j-select-option value="1h">按小时统计</j-select-option>
|
||||
<j-select-option value="1d">按天统计</j-select-option>
|
||||
<j-select-option value="1w">按周统计</j-select-option>
|
||||
<j-select-option value="1M">按月统计</j-select-option>
|
||||
</j-select>
|
||||
</div>
|
||||
<div v-if="cycle !== '*' && _type">
|
||||
统计规则:
|
||||
<a-select v-model:value="agg" style="width: 120px">
|
||||
<a-select-option value="AVG">平均值</a-select-option>
|
||||
<a-select-option value="MAX">最大值</a-select-option>
|
||||
<a-select-option value="MIN">最小值</a-select-option>
|
||||
<a-select-option value="COUNT">总数</a-select-option>
|
||||
</a-select>
|
||||
<j-select v-model:value="agg" style="width: 120px">
|
||||
<j-select-option value="AVG">平均值</j-select-option>
|
||||
<j-select-option value="MAX">最大值</j-select-option>
|
||||
<j-select-option value="MIN">最小值</j-select-option>
|
||||
<j-select-option value="COUNT">总数</j-select-option>
|
||||
</j-select>
|
||||
</div>
|
||||
</a-space>
|
||||
</j-space>
|
||||
</div>
|
||||
<div style="width: 100%; height: 500px">
|
||||
<Chart :options="options" v-if="chartsList.length" />
|
||||
<JEmpty v-else />
|
||||
</div>
|
||||
</a-spin>
|
||||
</j-spin>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
<template>
|
||||
<a-spin :spinning="loading">
|
||||
<j-spin :spinning="loading">
|
||||
<div style="position: relative">
|
||||
<div style="position: absolute; right: 0; top: 5px; z-index: 999">
|
||||
<a-space>
|
||||
<a-button type="primary" @click="onStart">开始动画</a-button>
|
||||
<a-button type="primary" @click="onStop">停止动画</a-button>
|
||||
</a-space>
|
||||
<j-space>
|
||||
<j-button type="primary" @click="onStart">开始动画</j-button>
|
||||
<j-button type="primary" @click="onStop">停止动画</j-button>
|
||||
</j-space>
|
||||
</div>
|
||||
</div>
|
||||
<AMapComponent style="height: 500px">
|
||||
<PathSimplifier :pathData="geoList" ref="amapPath"></PathSimplifier>
|
||||
</AMapComponent>
|
||||
</a-spin>
|
||||
</j-spin>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div>
|
||||
<a-table
|
||||
<j-table
|
||||
:columns="columns"
|
||||
size="small"
|
||||
rowKey="id"
|
||||
|
@ -11,7 +11,7 @@
|
|||
pageSize: dataSource?.pageSize || 10,
|
||||
showSizeChanger: true,
|
||||
total: dataSource?.total || 0,
|
||||
pageSizeOptions: [5, 10, 20, 50],
|
||||
pageSizeOptions: ['8', '12', '24', '60', '100']
|
||||
}"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
|
@ -26,8 +26,8 @@
|
|||
/>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'action'">
|
||||
<a-space>
|
||||
<a-button
|
||||
<j-space>
|
||||
<j-button
|
||||
v-if="
|
||||
showLoad ||
|
||||
(!getType(record?.value) &&
|
||||
|
@ -36,16 +36,16 @@
|
|||
type="link"
|
||||
@click="_download(record)"
|
||||
><AIcon type="DownloadOutlined"
|
||||
/></a-button>
|
||||
<a-button type="link" @click="showDetail(record)"
|
||||
/></j-button>
|
||||
<j-button type="link" @click="showDetail(record)"
|
||||
><AIcon type="SearchOutlined"
|
||||
/></a-button>
|
||||
</a-space>
|
||||
/></j-button>
|
||||
</j-space>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</j-table>
|
||||
</div>
|
||||
<a-modal
|
||||
<j-modal
|
||||
title="详情"
|
||||
:visible="visible"
|
||||
@ok="visible = false"
|
||||
|
@ -60,13 +60,13 @@
|
|||
:expand-depth="5"
|
||||
:value="current.formatValue"
|
||||
/>
|
||||
<a-textarea
|
||||
<j-textarea
|
||||
v-else-if="data?.valueType?.type === 'file'"
|
||||
:value="current.formatValue"
|
||||
:row="3"
|
||||
/>
|
||||
<a-input v-else disabled :value="current.formatValue" />
|
||||
</a-modal>
|
||||
<j-input v-else disabled :value="current.formatValue" />
|
||||
</j-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
@ -90,7 +90,12 @@ const _props = defineProps({
|
|||
});
|
||||
|
||||
const instanceStore = useInstanceStore();
|
||||
const dataSource = ref({});
|
||||
const dataSource = ref({
|
||||
pageIndex: 0,
|
||||
pageSize: 10,
|
||||
data: [],
|
||||
total: 0
|
||||
});
|
||||
const current = ref<any>({});
|
||||
const visible = ref<boolean>(false);
|
||||
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
<template>
|
||||
<a-space>
|
||||
<a-radio-group
|
||||
<j-space>
|
||||
<j-radio-group
|
||||
:value="radioValue"
|
||||
button-style="solid"
|
||||
@change="onRadioChange"
|
||||
>
|
||||
<a-radio-button value="today">今日</a-radio-button>
|
||||
<a-radio-button value="week">近一周</a-radio-button>
|
||||
<a-radio-button value="month">近一月</a-radio-button>
|
||||
</a-radio-group>
|
||||
<a-range-picker
|
||||
<j-radio-button value="today">今日</j-radio-button>
|
||||
<j-radio-button value="week">近一周</j-radio-button>
|
||||
<j-radio-button value="month">近一月</j-radio-button>
|
||||
</j-radio-group>
|
||||
<j-range-picker
|
||||
show-time
|
||||
v-model:value="dateValue"
|
||||
:placeholder="['开始时间', '结束时间']"
|
||||
@change="onRangeChange"
|
||||
:allowClear="false"
|
||||
/>
|
||||
</a-space>
|
||||
</j-space>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
<template>
|
||||
<a-modal title="详情" visible width="50vw" @ok="onCancel" @cancel="onCancel">
|
||||
<j-modal title="详情" visible width="50vw" @ok="onCancel" @cancel="onCancel">
|
||||
<div style="margin-bottom: 10px"><TimeComponent v-model="dateValue" /></div>
|
||||
<div>
|
||||
<a-tabs :destroyInactiveTabPane="true" v-model:activeKey="activeKey" style="max-height: 600px; overflow-y: auto">
|
||||
<a-tab-pane key="table" tab="列表">
|
||||
<j-tabs :destroyInactiveTabPane="true" v-model:activeKey="activeKey" style="max-height: 600px; overflow-y: auto">
|
||||
<j-tab-pane key="table" tab="列表">
|
||||
<Table :data="props.data" :time="_getTimes" />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="charts" tab="图表">
|
||||
</j-tab-pane>
|
||||
<j-tab-pane key="charts" tab="图表">
|
||||
<Charts :data="props.data" :time="_getTimes" />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="geo" tab="轨迹" v-if="data?.valueType?.type === 'geoPoint'">
|
||||
</j-tab-pane>
|
||||
<j-tab-pane key="geo" tab="轨迹" v-if="data?.valueType?.type === 'geoPoint'">
|
||||
<PropertyAMap :data="props.data" :time="_getTimes" />
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</j-tab-pane>
|
||||
</j-tabs>
|
||||
</div>
|
||||
</a-modal>
|
||||
</j-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<a-modal
|
||||
<j-modal
|
||||
:maskClosable="false"
|
||||
:visible="true"
|
||||
title="编辑指标"
|
||||
|
@ -7,17 +7,32 @@
|
|||
@cancel="handleCancel"
|
||||
:confirmLoading="loading"
|
||||
>
|
||||
<a-alert message="场景联动页面可引用指标配置触发条件" type="warning" showIcon />
|
||||
<a-form layout="vertical" ref="formRef" :model="modelRef" style="margin-top: 20px">
|
||||
<j-alert
|
||||
message="场景联动页面可引用指标配置触发条件"
|
||||
type="warning"
|
||||
showIcon
|
||||
/>
|
||||
<j-form
|
||||
layout="vertical"
|
||||
ref="formRef"
|
||||
:model="modelRef"
|
||||
style="margin-top: 20px"
|
||||
>
|
||||
<template v-for="(item, index) in modelRef.metrics" :key="index">
|
||||
<a-row type="flex" justify="space-between" align="bottom">
|
||||
<a-col :span="11">
|
||||
<a-form-item
|
||||
<j-row type="flex" justify="space-between" :align="'bottom'">
|
||||
<j-col :span="item.range ? 11 : 24">
|
||||
<j-form-item
|
||||
:rules="{
|
||||
required: true,
|
||||
message: `请${['date', 'boolean'].includes(data?.valueType?.type)? '选择': '输入'}指标值`,
|
||||
}"
|
||||
:name="['metrics', index, 'value', 0]"
|
||||
message: `请${
|
||||
['date', 'boolean'].includes(
|
||||
data?.valueType?.type,
|
||||
)
|
||||
? '选择'
|
||||
: '输入'
|
||||
}指标值`,
|
||||
}"
|
||||
:name="['metrics', index, 'value', 0]"
|
||||
:label="item?.name || '指标值'"
|
||||
>
|
||||
<ValueItem
|
||||
|
@ -26,100 +41,131 @@
|
|||
:options="
|
||||
data.valueType?.type === 'boolean'
|
||||
? [
|
||||
{
|
||||
label: data.valueType?.trueText,
|
||||
value: String(data.valueType?.trueValue),
|
||||
},
|
||||
{
|
||||
label: data.valueType?.falseText,
|
||||
value: String(data.valueType?.falseValue),
|
||||
},
|
||||
]
|
||||
{
|
||||
label: data.valueType
|
||||
?.trueText,
|
||||
value: String(
|
||||
data.valueType?.trueValue,
|
||||
),
|
||||
},
|
||||
{
|
||||
label: data.valueType
|
||||
?.falseText,
|
||||
value: String(
|
||||
data.valueType
|
||||
?.falseValue,
|
||||
),
|
||||
},
|
||||
]
|
||||
: undefined
|
||||
"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<template v-if="item.range">
|
||||
<a-col><div class="center-icon">~</div></a-col>
|
||||
<a-col :span="11">
|
||||
<a-form-item
|
||||
:name="['metrics', index, 'value', 1]"
|
||||
<j-col><div class="center-icon">~</div></j-col>
|
||||
<j-col :span="11">
|
||||
<j-form-item
|
||||
:name="['metrics', index, 'value', 1]"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: `请${['date', 'boolean'].includes(data?.valueType?.type)? '选择': '输入'}指标值`,
|
||||
}"
|
||||
message: `请${
|
||||
['date', 'boolean'].includes(
|
||||
data?.valueType?.type,
|
||||
)
|
||||
? '选择'
|
||||
: '输入'
|
||||
}指标值`,
|
||||
}"
|
||||
>
|
||||
<ValueItem
|
||||
v-model:modelValue="item.value[1]"
|
||||
:itemType="data.valueType?.type"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
</template>
|
||||
</a-row>
|
||||
</j-row>
|
||||
</template>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</j-form>
|
||||
</j-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { queryMetric, saveMetric } from '@/api/device/instance'
|
||||
const emit = defineEmits(['close']);
|
||||
import { useInstanceStore } from "@/store/instance"
|
||||
import { message } from 'ant-design-vue';
|
||||
import { queryMetric, saveMetric } from '@/api/device/instance';
|
||||
import { useInstanceStore } from '@/store/instance';
|
||||
import { message } from 'jetlinks-ui-components';
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
})
|
||||
default: () => {},
|
||||
},
|
||||
});
|
||||
|
||||
const loading = ref<boolean>(false)
|
||||
const instanceStore = useInstanceStore()
|
||||
const emit = defineEmits(['close']);
|
||||
|
||||
const loading = ref<boolean>(false);
|
||||
const instanceStore = useInstanceStore();
|
||||
const formRef = ref();
|
||||
|
||||
const modelRef = reactive({
|
||||
metrics: []
|
||||
metrics: [],
|
||||
});
|
||||
|
||||
const handleCancel = () => {
|
||||
emit('close')
|
||||
}
|
||||
emit('close');
|
||||
};
|
||||
|
||||
watch(() => props.data.id, (newVal) => {
|
||||
if(newVal && instanceStore.current.id){
|
||||
queryMetric(instanceStore.current.id, props.data.id).then(resp => {
|
||||
if (resp.status === 200) {
|
||||
if (Array.isArray(resp?.result) && resp?.result.length) {
|
||||
const list = resp?.result.map((item: any) => {
|
||||
const val = Array.isArray(item?.value) ? [item?.value] : item?.value?.split(',')
|
||||
return {
|
||||
...item,
|
||||
value: val
|
||||
};
|
||||
});
|
||||
modelRef.metrics = list as any
|
||||
} else {
|
||||
const type = props.data.valueType?.type;
|
||||
if (type === 'boolean') {
|
||||
const list = props.data.expands?.metrics.map((item: any) => {
|
||||
const value = (item?.value || {}).map((i: any) => String(i)) || {};
|
||||
return {
|
||||
...item,
|
||||
value,
|
||||
};
|
||||
});
|
||||
modelRef.metrics = list || []
|
||||
} else {
|
||||
modelRef.metrics = props.data.expands?.metrics || []
|
||||
}
|
||||
}
|
||||
watch(
|
||||
() => props.data.id,
|
||||
(newVal) => {
|
||||
if (newVal && instanceStore.current.id) {
|
||||
queryMetric(instanceStore.current.id, props.data.id).then(
|
||||
(resp) => {
|
||||
if (resp.status === 200) {
|
||||
if (
|
||||
Array.isArray(resp?.result) &&
|
||||
resp?.result.length
|
||||
) {
|
||||
const list = resp?.result.map((item: any) => {
|
||||
const val = Array.isArray(item?.value)
|
||||
? [item?.value]
|
||||
: item?.value?.split(',');
|
||||
return {
|
||||
...item,
|
||||
value: val,
|
||||
};
|
||||
});
|
||||
modelRef.metrics = list as any;
|
||||
} else {
|
||||
const type = props.data.valueType?.type;
|
||||
if (type === 'boolean') {
|
||||
const list = props.data.expands?.metrics.map(
|
||||
(item: any) => {
|
||||
const value =
|
||||
(item?.value || {}).map((i: any) =>
|
||||
String(i),
|
||||
) || {};
|
||||
return {
|
||||
...item,
|
||||
value,
|
||||
};
|
||||
},
|
||||
);
|
||||
modelRef.metrics = list || [];
|
||||
} else {
|
||||
modelRef.metrics =
|
||||
props.data.expands?.metrics || [];
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
})
|
||||
}
|
||||
}, {immediate: true, deep: true})
|
||||
},
|
||||
{ immediate: true, deep: true },
|
||||
);
|
||||
|
||||
const handleSave = () => {
|
||||
formRef.value
|
||||
|
@ -132,19 +178,23 @@ const handleSave = () => {
|
|||
value: item.value.join(','),
|
||||
};
|
||||
});
|
||||
const resp = await saveMetric(instanceStore.current.id || '', props.data.id || '', list).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
const resp = await saveMetric(
|
||||
instanceStore.current.id || '',
|
||||
props.data.id || '',
|
||||
list,
|
||||
).finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
if (resp.status === 200) {
|
||||
message.success('操作成功!');
|
||||
emit('close')
|
||||
emit('close');
|
||||
formRef.value.resetFields();
|
||||
}
|
||||
})
|
||||
.catch((err: any) => {
|
||||
console.log('error', err);
|
||||
});
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
<template>
|
||||
<a-card :hoverable="true" class="card-box">
|
||||
<!-- <a-spin :spinning="loading"> -->
|
||||
<j-card :hoverable="true" class="card-box">
|
||||
<!-- <j-spin :spinning="loading"> -->
|
||||
<div class="card-container">
|
||||
<div class="header">
|
||||
<div class="title">{{ _props.data.name }}</div>
|
||||
<div class="extra">
|
||||
<a-space :size="16">
|
||||
<j-space :size="16">
|
||||
<template v-for="i in actions" :key="i.key">
|
||||
<a-tooltip
|
||||
<j-tooltip
|
||||
v-bind="i.tooltip"
|
||||
v-if="i.key !== 'edit'"
|
||||
>
|
||||
<a-button
|
||||
<j-button
|
||||
style="padding: 0; margin: 0"
|
||||
type="link"
|
||||
:disabled="i.disabled"
|
||||
|
@ -21,8 +21,8 @@
|
|||
:type="i.icon"
|
||||
style="color: #323130; font-size: 12px"
|
||||
/>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
</j-button>
|
||||
</j-tooltip>
|
||||
<PermissionButton
|
||||
:disabled="i.disabled"
|
||||
v-else
|
||||
|
@ -38,7 +38,7 @@
|
|||
/></template>
|
||||
</PermissionButton>
|
||||
</template>
|
||||
</a-space>
|
||||
</j-space>
|
||||
</div>
|
||||
</div>
|
||||
<div class="value">
|
||||
|
@ -53,8 +53,8 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- </a-spin> -->
|
||||
</a-card>
|
||||
<!-- </j-spin> -->
|
||||
</j-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<a-modal
|
||||
<j-modal
|
||||
:maskClosable="false"
|
||||
:visible="true"
|
||||
title="编辑"
|
||||
|
@ -7,9 +7,9 @@
|
|||
@cancel="handleCancel"
|
||||
:confirmLoading="loading"
|
||||
>
|
||||
<a-alert message="当数据来源为设备时,填写的值将下发到设备" type="warning" showIcon />
|
||||
<a-form :rules="rules" layout="vertical" ref="formRef" :model="modelRef" style="margin-top: 20px">
|
||||
<a-form-item name="propertyValue" :label="data?.name || '自定义属性'">
|
||||
<j-alert message="当数据来源为设备时,填写的值将下发到设备" type="warning" showIcon />
|
||||
<j-form :rules="rules" layout="vertical" ref="formRef" :model="modelRef" style="margin-top: 20px">
|
||||
<j-form-item name="propertyValue" :label="data?.name || '自定义属性'">
|
||||
<ValueItem
|
||||
v-model:modelValue="modelRef.propertyValue"
|
||||
:itemType="data?.valueType?.type || data?.dataType"
|
||||
|
@ -24,16 +24,15 @@
|
|||
: undefined
|
||||
"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</j-form-item>
|
||||
</j-form>
|
||||
</j-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { setProperty } from '@/api/device/instance'
|
||||
const emit = defineEmits(['close']);
|
||||
import { useInstanceStore } from "@/store/instance"
|
||||
import { message } from 'ant-design-vue';
|
||||
import { message } from 'jetlinks-ui-components';
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
|
@ -42,6 +41,8 @@ const props = defineProps({
|
|||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['close']);
|
||||
|
||||
const loading = ref<boolean>(false)
|
||||
const instanceStore = useInstanceStore()
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<a-modal
|
||||
<j-modal
|
||||
:maskClosable="false"
|
||||
width="600px"
|
||||
:visible="true"
|
||||
|
@ -10,7 +10,7 @@
|
|||
@cancel="handleCancel"
|
||||
>
|
||||
<template v-if="['.jpg', '.png'].includes(type)">
|
||||
<a-image :src="value?.formatValue" />
|
||||
<j-image :src="value?.formatValue" />
|
||||
</template>
|
||||
<template v-else-if="['.flv', '.m3u8', '.mp4'].includes(type)">
|
||||
</template>
|
||||
|
@ -20,7 +20,7 @@
|
|||
:value="value?.formatValue"
|
||||
/>
|
||||
</template>
|
||||
</a-modal>
|
||||
</j-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
|
|
@ -1,59 +1,110 @@
|
|||
<template>
|
||||
<div class="value">
|
||||
<div v-if="value?.formatValue !== 0 && !value?.formatValue" :class="valueClass">--</div>
|
||||
<div
|
||||
v-if="value?.formatValue !== 0 && !value?.formatValue"
|
||||
:class="valueClass"
|
||||
>
|
||||
--
|
||||
</div>
|
||||
<div v-else-if="_data.data?.valueType?.type === 'file'">
|
||||
<template v-if="data?.valueType?.fileType === 'base64'">
|
||||
<div :class="valueClass" v-if="!!getType(value?.formatValue)">
|
||||
<img :src="imgMap.get(_type)" @error="onError" />
|
||||
</div>
|
||||
<div v-else :class="valueClass">
|
||||
<img :src="imgMap.get('other')" />
|
||||
</div>
|
||||
</template>
|
||||
<div v-else-if="data?.valueType?.fileType === 'Binary(二进制)'" :class="valueClass">
|
||||
<img :src="imgMap.get('other')" />
|
||||
</div>
|
||||
<template v-else>
|
||||
<template v-if="imgList.some((item) => value?.formatValue.includes(item))">
|
||||
<div :class="valueClass" @click="getDetail('img')">
|
||||
<img :src="value?.formatValue" @error="imgError" />
|
||||
</div>
|
||||
<template v-if="data?.valueType?.fileType === 'base64'">
|
||||
<div :class="valueClass" v-if="!!getType(value?.formatValue)">
|
||||
<img :src="imgMap.get(_type)" @error="onError" />
|
||||
</div>
|
||||
<div v-else :class="valueClass">
|
||||
<img :src="imgMap.get('other')" />
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="videoList.some((item) => value?.formatValue.includes(item))">
|
||||
<div :class="valueClass" @click="getDetail('video')">
|
||||
<img :src="imgMap.get('video')" />
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="fileList.some((item) => value?.formatValue.includes(item))">
|
||||
<div :class="valueClass">
|
||||
<img :src="imgMap.get(fileList.find((item) => value?.formatValue.includes(item)).slice(1))" />
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div :class="valueClass">
|
||||
<div
|
||||
v-else-if="data?.valueType?.fileType === 'Binary(二进制)'"
|
||||
:class="valueClass"
|
||||
>
|
||||
<img :src="imgMap.get('other')" />
|
||||
</div>
|
||||
</div>
|
||||
<template v-else>
|
||||
<template
|
||||
v-if="
|
||||
imgList.some((item) =>
|
||||
value?.formatValue.includes(item),
|
||||
)
|
||||
"
|
||||
>
|
||||
<div :class="valueClass" @click="getDetail('img')">
|
||||
<img :src="value?.formatValue" @error="imgError" />
|
||||
</div>
|
||||
</template>
|
||||
<template
|
||||
v-else-if="
|
||||
videoList.some((item) =>
|
||||
value?.formatValue.includes(item),
|
||||
)
|
||||
"
|
||||
>
|
||||
<div :class="valueClass" @click="getDetail('video')">
|
||||
<img :src="imgMap.get('video')" />
|
||||
</div>
|
||||
</template>
|
||||
<template
|
||||
v-else-if="
|
||||
fileList.some((item) =>
|
||||
value?.formatValue.includes(item),
|
||||
)
|
||||
"
|
||||
>
|
||||
<div :class="valueClass">
|
||||
<img
|
||||
:src="
|
||||
imgMap.get(
|
||||
fileList
|
||||
.find((item) =>
|
||||
value?.formatValue.includes(item),
|
||||
)
|
||||
.slice(1),
|
||||
)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div :class="valueClass">
|
||||
<img :src="imgMap.get('other')" />
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
<div v-else-if="_data.data?.valueType?.type === 'object'" @click="getDetail('obj')" :class="valueClass">
|
||||
<img :src="imgMap.get('obj')" />
|
||||
<div
|
||||
v-else-if="_data.data?.valueType?.type === 'object'"
|
||||
@click="getDetail('obj')"
|
||||
:class="valueClass"
|
||||
>
|
||||
<img :src="imgMap.get('obj')" />
|
||||
</div>
|
||||
<div v-else-if="_data.data?.valueType?.type === 'geoPoint' || _data.data?.valueType?.type === 'array'" :class="valueClass">
|
||||
{{JSON.stringify(value?.formatValue)}}
|
||||
<div
|
||||
v-else-if="
|
||||
_data.data?.valueType?.type === 'geoPoint' ||
|
||||
_data.data?.valueType?.type === 'array'
|
||||
"
|
||||
:class="valueClass"
|
||||
>
|
||||
{{ JSON.stringify(value?.formatValue) }}
|
||||
</div>
|
||||
<div v-else :class="valueClass">
|
||||
{{String(value?.formatValue)}}
|
||||
{{ String(value?.formatValue) }}
|
||||
</div>
|
||||
<ValueDetail v-if="visible" :type="_types" :value="value" @close="visible = false" />
|
||||
<ValueDetail
|
||||
v-if="visible"
|
||||
:type="_types"
|
||||
:value="value"
|
||||
@close="visible = false"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { getImage } from "@/utils/comm";
|
||||
import { message } from "ant-design-vue";
|
||||
import ValueDetail from './ValueDetail.vue'
|
||||
import {getType, imgMap, imgList, videoList, fileList} from './index'
|
||||
import { getImage } from '@/utils/comm';
|
||||
import { message } from 'jetlinks-ui-components';
|
||||
import ValueDetail from './ValueDetail.vue';
|
||||
import { getType, imgMap, imgList, videoList, fileList } from './index';
|
||||
|
||||
const _data = defineProps({
|
||||
data: {
|
||||
|
@ -62,88 +113,99 @@ const _data = defineProps({
|
|||
},
|
||||
value: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
default: () => {},
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'card'
|
||||
}
|
||||
default: 'card',
|
||||
},
|
||||
});
|
||||
|
||||
const valueClass = computed(() => {
|
||||
return _data.type === 'card' ? 'cardValue' : 'otherValue'
|
||||
})
|
||||
return _data.type === 'card' ? 'cardValue' : 'otherValue';
|
||||
});
|
||||
|
||||
const isHttps = document.location.protocol === 'https:';
|
||||
|
||||
const _types = ref<string>('')
|
||||
const visible = ref<boolean>(false)
|
||||
const temp = ref<boolean>(false)
|
||||
|
||||
const _types = ref<string>('');
|
||||
const visible = ref<boolean>(false);
|
||||
const temp = ref<boolean>(false);
|
||||
|
||||
const onError = (e: any) => {
|
||||
e.target.src = imgMap.get('other')
|
||||
}
|
||||
e.target.src = imgMap.get('other');
|
||||
};
|
||||
|
||||
const imgError = (e: any) => {
|
||||
e.target.src = imgMap.get('error')
|
||||
temp.value = true
|
||||
}
|
||||
e.target.src = imgMap.get('error');
|
||||
temp.value = true;
|
||||
};
|
||||
|
||||
const getDetail = (_type: string) => {
|
||||
const value = _data.value
|
||||
let flag: string = ''
|
||||
if(_type === 'img'){
|
||||
if (isHttps && value?.formatValue.indexOf('http:') !== -1) {
|
||||
message.error('域名为https时,不支持访问http地址');
|
||||
} else if (temp.value) {
|
||||
message.error('该图片无法访问');
|
||||
} else {
|
||||
flag = ['.jpg', '.png'].find((item) => value?.formatValue.includes(item)) || '--';
|
||||
const value = _data.value;
|
||||
let flag: string = '';
|
||||
if (_type === 'img') {
|
||||
if (isHttps && value?.formatValue.indexOf('http:') !== -1) {
|
||||
message.error('域名为https时,不支持访问http地址');
|
||||
} else if (temp.value) {
|
||||
message.error('该图片无法访问');
|
||||
} else {
|
||||
flag =
|
||||
['.jpg', '.png'].find((item) =>
|
||||
value?.formatValue.includes(item),
|
||||
) || '--';
|
||||
_types.value = flag;
|
||||
visible.value = true;
|
||||
}
|
||||
} else if (_type === 'video') {
|
||||
if (isHttps && value?.formatValue.indexOf('http:') !== -1) {
|
||||
message.error('域名为https时,不支持访问http地址');
|
||||
} else if (
|
||||
['.rmvb', '.mvb'].some((item) => value?.formatValue.includes(item))
|
||||
) {
|
||||
message.error('当前仅支持播放.mp4,.flv,.m3u8格式的视频');
|
||||
} else {
|
||||
flag =
|
||||
['.m3u8', '.flv', '.mp4'].find((item) =>
|
||||
value?.formatValue.includes(item),
|
||||
) || '--';
|
||||
_types.value = flag;
|
||||
visible.value = true;
|
||||
}
|
||||
} else if (_type === 'obj') {
|
||||
flag = 'obj';
|
||||
_types.value = flag;
|
||||
visible.value = true;
|
||||
}
|
||||
} else if(_type === 'video'){
|
||||
if (isHttps && value?.formatValue.indexOf('http:') !== -1) {
|
||||
message.error('域名为https时,不支持访问http地址');
|
||||
} else if (['.rmvb', '.mvb'].some((item) => value?.formatValue.includes(item))) {
|
||||
message.error('当前仅支持播放.mp4,.flv,.m3u8格式的视频');
|
||||
} else {
|
||||
flag = ['.m3u8', '.flv', '.mp4'].find((item) => value?.formatValue.includes(item)) || '--';
|
||||
}
|
||||
}else if(_type === 'obj'){
|
||||
flag = 'obj'
|
||||
}
|
||||
_types.value = flag
|
||||
visible.value = true
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.value {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
|
||||
.cardValue {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
overflow: hidden;
|
||||
color: #323130;
|
||||
font-weight: 700;
|
||||
font-size: 24px;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
img {
|
||||
width: 60px;
|
||||
}
|
||||
}
|
||||
.cardValue {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
overflow: hidden;
|
||||
color: #323130;
|
||||
font-weight: 700;
|
||||
font-size: 24px;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
.otherValue {
|
||||
img {
|
||||
width: 40px;
|
||||
img {
|
||||
width: 60px;
|
||||
}
|
||||
}
|
||||
|
||||
.otherValue {
|
||||
img {
|
||||
width: 40px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -2,7 +2,8 @@
|
|||
<j-spin :spinning="loading">
|
||||
<JProTable
|
||||
:columns="columns"
|
||||
:dataSource="dataSource"
|
||||
:request="query"
|
||||
:params="_params"
|
||||
:bodyStyle="{ padding: '0 0 0 20px' }"
|
||||
>
|
||||
<template #headerTitle>
|
||||
|
@ -58,26 +59,6 @@
|
|||
</template>
|
||||
</j-space>
|
||||
</template>
|
||||
<template #paginationRender>
|
||||
<j-pagination
|
||||
size="small"
|
||||
:total="total"
|
||||
:showQuickJumper="false"
|
||||
:showSizeChanger="true"
|
||||
:current="pageIndex + 1"
|
||||
:pageSize="pageSize"
|
||||
:pageSizeOptions="['8', '12', '24', '60', '100']"
|
||||
:show-total="
|
||||
(num) =>
|
||||
`第 ${pageIndex * pageSize + 1} - ${
|
||||
(pageIndex + 1) * pageSize > num
|
||||
? num
|
||||
: (pageIndex + 1) * pageSize
|
||||
} 条/总共 ${num} 条`
|
||||
"
|
||||
@change="pageChange"
|
||||
/>
|
||||
</template>
|
||||
</JProTable>
|
||||
</j-spin>
|
||||
<Save v-if="editVisible" @close="editVisible = false" :data="currentInfo" />
|
||||
|
@ -107,6 +88,7 @@ import { message } from 'ant-design-vue';
|
|||
import { getWebSocket } from '@/utils/websocket';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { queryDashboard } from '@/api/comm';
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '名称',
|
||||
|
@ -142,9 +124,6 @@ const _data = defineProps({
|
|||
const value = ref<string>('');
|
||||
const dataSource = ref<PropertyData[]>([]);
|
||||
const _dataSource = ref<PropertyData[]>([]);
|
||||
const pageIndex = ref<number>(0);
|
||||
const pageSize = ref<number>(8);
|
||||
const total = ref<number>(0);
|
||||
const editVisible = ref<boolean>(false); // 编辑
|
||||
const detailVisible = ref<boolean>(false); // 详情
|
||||
const currentInfo = ref<Record<string, any>>({});
|
||||
|
@ -152,6 +131,9 @@ const instanceStore = useInstanceStore();
|
|||
const indicatorVisible = ref<boolean>(false); // 指标
|
||||
const loading = ref<boolean>(false);
|
||||
const propertyValue = ref<Record<string, any>>({});
|
||||
const _params = reactive({
|
||||
name: '',
|
||||
});
|
||||
|
||||
const subRef = ref();
|
||||
|
||||
|
@ -309,39 +291,35 @@ const getDashboard = async () => {
|
|||
loading.value = false;
|
||||
};
|
||||
|
||||
const query = (page: number, size: number, value: string) => {
|
||||
pageIndex.value = page || 0;
|
||||
pageSize.value = size || 8;
|
||||
const _from = pageIndex.value * pageSize.value;
|
||||
const _to = (pageIndex.value + 1) * pageSize.value;
|
||||
const arr = _.cloneDeep(_dataSource.value);
|
||||
if (unref(value)) {
|
||||
const li = arr.filter((i: any) => {
|
||||
return i?.name.indexOf(unref(value)) !== -1;
|
||||
const query = (params: Record<string, any>) =>
|
||||
new Promise((resolve) => {
|
||||
const _from = params.pageIndex * params.pageSize;
|
||||
const _to = (params.pageIndex + 1) * params.pageSize;
|
||||
let arr = _.cloneDeep(_dataSource.value);
|
||||
if (params?.name) {
|
||||
const li = _dataSource.value.filter((i: any) => {
|
||||
return i?.name.indexOf(params.name) !== -1;
|
||||
});
|
||||
arr = _.cloneDeep(li);
|
||||
}
|
||||
resolve({
|
||||
result: {
|
||||
data: arr.slice(_from, _to),
|
||||
pageIndex: params.pageIndex || 0,
|
||||
pageSize: params.pageSize || 12,
|
||||
total: arr.length,
|
||||
},
|
||||
status: 200,
|
||||
});
|
||||
dataSource.value = li.slice(_from, _to);
|
||||
total.value = li.length;
|
||||
} else {
|
||||
dataSource.value = arr.slice(_from, _to);
|
||||
total.value = arr.length;
|
||||
}
|
||||
getDashboard();
|
||||
};
|
||||
|
||||
const pageChange = (page: number, size: number) => {
|
||||
if (size === pageSize.value) {
|
||||
query(page - 1, size, value.value);
|
||||
} else {
|
||||
query(0, size, value.value);
|
||||
}
|
||||
};
|
||||
getDashboard();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => _data.data,
|
||||
(newVal) => {
|
||||
if (newVal.length) {
|
||||
_dataSource.value = newVal as PropertyData[];
|
||||
query(0, 8, value.value);
|
||||
_params.name = '';
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -351,7 +329,7 @@ watch(
|
|||
);
|
||||
|
||||
const onSearch = () => {
|
||||
query(0, 8, value.value);
|
||||
_params.name = value.value;
|
||||
};
|
||||
|
||||
onUnmounted(() => {
|
||||
|
|
|
@ -0,0 +1,175 @@
|
|||
<!-- 绑定设备 -->
|
||||
<template>
|
||||
<j-modal
|
||||
:maskClosable="false"
|
||||
width="1000px"
|
||||
:visible="true"
|
||||
title="绑定父设备"
|
||||
okText="确定"
|
||||
cancelText="取消"
|
||||
@ok="handleOk"
|
||||
@cancel="handleCancel"
|
||||
:confirmLoading="btnLoading"
|
||||
>
|
||||
<j-advanced-search
|
||||
:columns="columns"
|
||||
target="child-device-bind"
|
||||
@search="handleSearch"
|
||||
type="simple"
|
||||
/>
|
||||
<JProTable
|
||||
ref="bindDeviceRef"
|
||||
:columns="columns"
|
||||
:request="query"
|
||||
model="TABLE"
|
||||
:bodyStyle="{ padding: '0 0 0 24px' }"
|
||||
:defaultParams="defaultParams"
|
||||
:rowSelection="{
|
||||
selectedRowKeys: _selectedRowKeys,
|
||||
onChange: onSelectChange,
|
||||
}"
|
||||
:params="params"
|
||||
>
|
||||
<template #registryTime="slotProps">
|
||||
{{
|
||||
slotProps.registryTime
|
||||
? dayjs(slotProps.registryTime).format(
|
||||
'YYYY-MM-DD HH:mm:ss',
|
||||
)
|
||||
: ''
|
||||
}}
|
||||
</template>
|
||||
<template #state="slotProps">
|
||||
<j-badge
|
||||
:text="slotProps.state.text"
|
||||
:status="statusMap.get(slotProps.state.value)"
|
||||
/>
|
||||
</template>
|
||||
</JProTable>
|
||||
</j-modal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { query, bindDevice } from '@/api/device/instance';
|
||||
import dayjs from 'dayjs';
|
||||
import { message } from 'jetlinks-ui-components';
|
||||
|
||||
const emit = defineEmits(['cancel', 'ok']);
|
||||
|
||||
const bindDeviceRef = ref<Record<string, any>>({});
|
||||
const params = ref<Record<string, any>>({});
|
||||
const _selectedRowKeys = ref<string[]>([]);
|
||||
const btnLoading = ref<boolean>(false);
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
})
|
||||
|
||||
const statusMap = new Map();
|
||||
statusMap.set('online', 'success');
|
||||
statusMap.set('offline', 'error');
|
||||
statusMap.set('notActive', 'warning');
|
||||
|
||||
const defaultParams = {
|
||||
terms: [
|
||||
{
|
||||
column: 'productId$product-info',
|
||||
value: [
|
||||
{
|
||||
column: 'deviceType',
|
||||
termType: 'eq',
|
||||
value: 'gateway',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'id',
|
||||
key: 'id',
|
||||
ellipsis: true,
|
||||
fixed: 'left',
|
||||
search: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '设备名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
ellipsis: true,
|
||||
search: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '所属产品',
|
||||
dataIndex: 'productName',
|
||||
key: 'productName',
|
||||
search: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '注册时间',
|
||||
dataIndex: 'registryTime',
|
||||
key: 'registryTime',
|
||||
scopedSlots: true,
|
||||
search: {
|
||||
type: 'date',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'state',
|
||||
key: 'state',
|
||||
scopedSlots: true,
|
||||
search: {
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: '禁用', value: 'notActive' },
|
||||
{ label: '离线', value: 'offline' },
|
||||
{ label: '在线', value: 'online' },
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const handleSearch = (e: any) => {
|
||||
params.value = e;
|
||||
};
|
||||
|
||||
const onSelectChange = (keys: string[]) => {
|
||||
_selectedRowKeys.value = [...keys];
|
||||
};
|
||||
|
||||
const handleOk = () => {
|
||||
if (_selectedRowKeys.value.length === 0) {
|
||||
message.warning('请选择需要绑定的设备');
|
||||
return;
|
||||
}
|
||||
btnLoading.value = true;
|
||||
bindDevice(_selectedRowKeys.value[0], props.data.id, )
|
||||
.then((resp) => {
|
||||
if(resp.status === 200){
|
||||
emit('ok', _selectedRowKeys.value[0]);
|
||||
message.success('操作成功');
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
btnLoading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
emit('cancel', false);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="less"></style>
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<a-modal
|
||||
<j-modal
|
||||
width="900px"
|
||||
title="批量映射"
|
||||
visible
|
||||
|
@ -10,49 +10,49 @@
|
|||
<div class="map-tree-top">
|
||||
采集器的点位名称与属性名称一致时将自动映射绑定;有多个采集器点位名称与属性名称一致时以第1个采集器的点位数据进行绑定
|
||||
</div>
|
||||
<a-spin :spinning="loading">
|
||||
<j-spin :spinning="loading">
|
||||
<div class="map-tree-content">
|
||||
<a-card class="map-tree-content-card" title="源数据">
|
||||
<a-tree
|
||||
<j-card class="map-tree-content-card" title="源数据">
|
||||
<j-tree
|
||||
checkable
|
||||
:height="300"
|
||||
:tree-data="dataSource"
|
||||
:checkedKeys="checkedKeys"
|
||||
@check="onCheck"
|
||||
/>
|
||||
</a-card>
|
||||
</j-card>
|
||||
<div style="width: 100px">
|
||||
<a-button
|
||||
<j-button
|
||||
:disabled="rightList.length >= leftList.length"
|
||||
@click="onRight"
|
||||
>加入右侧</a-button
|
||||
>加入右侧</j-button
|
||||
>
|
||||
</div>
|
||||
<a-card class="map-tree-content-card" title="采集器">
|
||||
<a-list
|
||||
<j-card class="map-tree-content-card" title="采集器">
|
||||
<j-list
|
||||
size="small"
|
||||
:data-source="rightList"
|
||||
class="map-tree-content-card-list"
|
||||
>
|
||||
<template #renderItem="{ item }">
|
||||
<a-list-item>
|
||||
<j-list-item>
|
||||
{{ item.title }}
|
||||
<template #actions>
|
||||
<a-popconfirm
|
||||
<j-popconfirm
|
||||
title="确定删除?"
|
||||
@confirm="_delete(item.key)"
|
||||
>
|
||||
<AIcon type="DeleteOutlined" />
|
||||
</a-popconfirm>
|
||||
</j-popconfirm>
|
||||
</template>
|
||||
</a-list-item>
|
||||
</j-list-item>
|
||||
</template>
|
||||
</a-list>
|
||||
</a-card>
|
||||
</j-list>
|
||||
</j-card>
|
||||
</div>
|
||||
</a-spin>
|
||||
</j-spin>
|
||||
</div>
|
||||
</a-modal>
|
||||
</j-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
|
|
@ -1,46 +1,46 @@
|
|||
<template>
|
||||
<a-spin :spinning="loading" v-if="metadata.properties.length">
|
||||
<a-card>
|
||||
<j-spin :spinning="loading" v-if="metadata.properties.length">
|
||||
<j-card>
|
||||
<template #extra>
|
||||
<a-space>
|
||||
<a-button @click="visible = true">批量映射</a-button>
|
||||
<a-button type="primary" @click="onSave">保存</a-button>
|
||||
</a-space>
|
||||
<j-space>
|
||||
<j-button @click="visible = true">批量映射</j-button>
|
||||
<j-button type="primary" @click="onSave">保存</j-button>
|
||||
</j-space>
|
||||
</template>
|
||||
<a-form ref="formRef" :model="modelRef">
|
||||
<a-table :dataSource="modelRef.dataSource" :columns="columns">
|
||||
<j-form ref="formRef" :model="modelRef">
|
||||
<j-table :dataSource="modelRef.dataSource" :columns="columns">
|
||||
<template #headerCell="{ column }">
|
||||
<template v-if="column.key === 'collectorId'">
|
||||
采集器
|
||||
<a-tooltip title="数据采集中配置的真实物理设备">
|
||||
<j-tooltip title="数据采集中配置的真实物理设备">
|
||||
<AIcon type="QuestionCircleOutlined" />
|
||||
</a-tooltip>
|
||||
</j-tooltip>
|
||||
</template>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record, index }">
|
||||
<template v-if="column.dataIndex === 'channelId'">
|
||||
<a-form-item
|
||||
<j-form-item
|
||||
:name="['dataSource', index, 'channelId']"
|
||||
>
|
||||
<a-select
|
||||
<j-select
|
||||
style="width: 100%"
|
||||
v-model:value="record[column.dataIndex]"
|
||||
placeholder="请选择"
|
||||
allowClear
|
||||
:filter-option="filterOption"
|
||||
>
|
||||
<a-select-option
|
||||
<j-select-option
|
||||
v-for="item in channelList"
|
||||
:key="item.value"
|
||||
:value="item.value"
|
||||
:label="item.label"
|
||||
>{{ item.label }}</a-select-option
|
||||
>{{ item.label }}</j-select-option
|
||||
>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</j-select>
|
||||
</j-form-item>
|
||||
</template>
|
||||
<template v-if="column.dataIndex === 'collectorId'">
|
||||
<a-form-item
|
||||
<j-form-item
|
||||
:name="['dataSource', index, 'collectorId']"
|
||||
:rules="[
|
||||
{
|
||||
|
@ -54,10 +54,10 @@
|
|||
:id="record.channelId"
|
||||
type="COLLECTOR"
|
||||
/>
|
||||
</a-form-item>
|
||||
</j-form-item>
|
||||
</template>
|
||||
<template v-if="column.dataIndex === 'pointId'">
|
||||
<a-form-item
|
||||
<j-form-item
|
||||
:name="['dataSource', index, 'pointId']"
|
||||
:rules="[
|
||||
{
|
||||
|
@ -71,33 +71,33 @@
|
|||
:id="record.collectorId"
|
||||
type="POINT"
|
||||
/>
|
||||
</a-form-item>
|
||||
</j-form-item>
|
||||
</template>
|
||||
<template v-if="column.dataIndex === 'id'">
|
||||
<a-badge
|
||||
<j-badge
|
||||
v-if="record[column.dataIndex]"
|
||||
status="success"
|
||||
text="已绑定"
|
||||
/>
|
||||
<a-badge v-else status="error" text="未绑定" />
|
||||
<j-badge v-else status="error" text="未绑定" />
|
||||
</template>
|
||||
<template v-if="column.key === 'action'">
|
||||
<a-tooltip title="解绑">
|
||||
<a-popconfirm
|
||||
<j-tooltip title="解绑">
|
||||
<j-popconfirm
|
||||
title="确认解绑"
|
||||
:disabled="!record.id"
|
||||
@confirm="unbind(record.id)"
|
||||
>
|
||||
<a-button type="link" :disabled="!record.id"
|
||||
<j-button type="link" :disabled="!record.id"
|
||||
><AIcon type="icon-jiebang"
|
||||
/></a-button>
|
||||
</a-popconfirm>
|
||||
</a-tooltip>
|
||||
/></j-button>
|
||||
</j-popconfirm>
|
||||
</j-tooltip>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-form>
|
||||
</a-card>
|
||||
</j-table>
|
||||
</j-form>
|
||||
</j-card>
|
||||
<PatchMapping
|
||||
:deviceId="instanceStore.current.id"
|
||||
v-if="visible"
|
||||
|
@ -106,10 +106,10 @@
|
|||
:type="provider"
|
||||
:metaData="modelRef.dataSource"
|
||||
/>
|
||||
</a-spin>
|
||||
<a-card v-else>
|
||||
</j-spin>
|
||||
<j-card v-else>
|
||||
<JEmpty description='暂无数据,请配置物模型' style="margin: 10% 0" />
|
||||
</a-card>
|
||||
</j-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
<template>
|
||||
<a-select allowClear v-model:value="_value" @change="onChange" placeholder="请选择" style="width: 100%">
|
||||
<a-select-option
|
||||
<j-select allowClear v-model:value="_value" @change="onChange" placeholder="请选择" style="width: 100%">
|
||||
<j-select-option
|
||||
v-for="item in list"
|
||||
:key="item.id"
|
||||
:value="item.id"
|
||||
:label="item.name"
|
||||
:filter-option="filterOption"
|
||||
>{{ item.name }}</a-select-option
|
||||
>{{ item.name }}</j-select-option
|
||||
>
|
||||
</a-select>
|
||||
</j-select>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
|
|
@ -10,9 +10,9 @@
|
|||
<div style="display: flex; align-items: center">
|
||||
<AIcon type="ArrowLeftOutlined" @click="onBack" />
|
||||
<div style="margin-left: 20px">{{ instanceStore.current.name }}</div>
|
||||
<a-divider type="vertical" />
|
||||
<a-space>
|
||||
<a-badge
|
||||
<j-divider type="vertical" />
|
||||
<j-space>
|
||||
<j-badge
|
||||
:text="instanceStore.current.state?.text"
|
||||
:status="
|
||||
statusMap.get(
|
||||
|
@ -49,7 +49,7 @@
|
|||
>
|
||||
断开连接
|
||||
</PermissionButton>
|
||||
<a-tooltip
|
||||
<j-tooltip
|
||||
v-if="
|
||||
instanceStore.current?.accessProvider ===
|
||||
'child-device' &&
|
||||
|
@ -68,15 +68,15 @@
|
|||
type="QuestionCircleOutlined"
|
||||
style="font-size: 14px"
|
||||
/>
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
</j-tooltip>
|
||||
</j-space>
|
||||
</div>
|
||||
<div style="padding-top: 10px">
|
||||
<a-descriptions size="small" :column="4">
|
||||
<a-descriptions-item label="ID">{{
|
||||
<j-descriptions size="small" :column="4">
|
||||
<j-descriptions-item label="ID">{{
|
||||
instanceStore.current.id
|
||||
}}</a-descriptions-item>
|
||||
<a-descriptions-item label="所属产品">
|
||||
}}</j-descriptions-item>
|
||||
<j-descriptions-item label="所属产品">
|
||||
<PermissionButton
|
||||
type="link"
|
||||
style="margin-top: -5px; padding: 0"
|
||||
|
@ -85,8 +85,8 @@
|
|||
>
|
||||
{{ instanceStore.current.productName }}
|
||||
</PermissionButton>
|
||||
</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
</j-descriptions-item>
|
||||
</j-descriptions>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -119,7 +119,7 @@ import EdgeMap from './EdgeMap/index.vue';
|
|||
import Parsing from './Parsing/index.vue'
|
||||
import Log from './Log/index.vue'
|
||||
import { _deploy, _disconnect } from '@/api/device/instance';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { message } from 'jetlinks-ui-components';
|
||||
import { getImage } from '@/utils/comm';
|
||||
import { getWebSocket } from '@/utils/websocket';
|
||||
import { useMenuStore } from '@/store/menu';
|
||||
|
|
|
@ -64,6 +64,6 @@ const handleCancel = () => {
|
|||
}
|
||||
|
||||
const handleSave = () => {
|
||||
emit('close')
|
||||
emit('save')
|
||||
}
|
||||
</script>
|
|
@ -1,12 +1,12 @@
|
|||
<template>
|
||||
<a-modal :maskClosable="false" width="800px" :visible="true" title="当前进度" @ok="handleCancel" @cancel="handleCancel">
|
||||
<j-modal :maskClosable="false" width="800px" :visible="true" title="当前进度" @ok="handleCancel" @cancel="handleCancel">
|
||||
<div>
|
||||
<a-badge v-if="flag" status="processing" text="进行中" />
|
||||
<a-badge v-else status="success" text="已完成" />
|
||||
<j-badge v-if="flag" status="processing" text="进行中" />
|
||||
<j-badge v-else status="success" text="已完成" />
|
||||
</div>
|
||||
<p>总数量:{{count}}</p>
|
||||
<a style="color: red">{{errMessage}}</a>
|
||||
</a-modal>
|
||||
</j-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
|
|
@ -88,7 +88,7 @@
|
|||
import { queryNoPagingPost } from '@/api/device/product';
|
||||
import { isExists, update } from '@/api/device/instance';
|
||||
import { getImage } from '@/utils/comm';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { message } from 'jetlinks-ui-components';
|
||||
|
||||
const emit = defineEmits(['close', 'save']);
|
||||
const props = defineProps({
|
||||
|
|
|
@ -156,7 +156,10 @@
|
|||
</template>
|
||||
<template #content>
|
||||
<Ellipsis style="width: calc(100% - 100px)">
|
||||
<span style="font-size: 16px; font-weight: 600" @click.stop="handleView(slotProps.id)">
|
||||
<span
|
||||
style="font-size: 16px; font-weight: 600"
|
||||
@click.stop="handleView(slotProps.id)"
|
||||
>
|
||||
{{ slotProps.name }}
|
||||
</span>
|
||||
</Ellipsis>
|
||||
|
@ -205,6 +208,11 @@
|
|||
:status="statusMap.get(slotProps.state?.value)"
|
||||
/>
|
||||
</template>
|
||||
<template #createTime="slotProps">
|
||||
<span>{{
|
||||
dayjs(slotProps.createTime).format('YYYY-MM-DD HH:mm:ss')
|
||||
}}</span>
|
||||
</template>
|
||||
<template #action="slotProps">
|
||||
<j-space>
|
||||
<template
|
||||
|
@ -229,11 +237,16 @@
|
|||
</template>
|
||||
</JProTable>
|
||||
</page-container>
|
||||
<Import v-if="importVisible" @close="importVisible = false" @save="onRefresh" />
|
||||
<Import
|
||||
v-if="importVisible"
|
||||
@close="importVisible = false"
|
||||
@save="onRefresh"
|
||||
/>
|
||||
<Export
|
||||
v-if="exportVisible"
|
||||
@close="exportVisible = false"
|
||||
:data="params"
|
||||
@save="onRefresh"
|
||||
/>
|
||||
<Process
|
||||
v-if="operationVisible"
|
||||
|
@ -260,7 +273,7 @@ import {
|
|||
batchDeleteDevice,
|
||||
} from '@/api/device/instance';
|
||||
import { getImage, LocalStore } from '@/utils/comm';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { message } from 'jetlinks-ui-components';
|
||||
import Import from './Import/index.vue';
|
||||
import Export from './Export/index.vue';
|
||||
import Process from './Process/index.vue';
|
||||
|
@ -274,9 +287,9 @@ import {
|
|||
} from '@/api/device/product';
|
||||
import { queryTree } from '@/api/device/category';
|
||||
import { useMenuStore } from '@/store/menu';
|
||||
import { ActionsType } from './typings';
|
||||
import type { ActionsType } from './typings';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
const router = useRouter();
|
||||
const instanceRef = ref<Record<string, any>>({});
|
||||
const params = ref<Record<string, any>>({});
|
||||
const _selectedRowKeys = ref<string[]>([]);
|
||||
|
@ -699,5 +712,5 @@ const handleSearch = (_params: any) => {
|
|||
|
||||
const onRefresh = () => {
|
||||
instanceRef.value?.reload();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -4,11 +4,12 @@
|
|||
<div v-if="productStore.current.accessId === undefined || null">
|
||||
<a-empty :image="simpleImage">
|
||||
<template #description>
|
||||
<span>
|
||||
<span v-if="permissionStore.hasPermission('device/Product:update')">
|
||||
请先<a-button type="link" @click="showModal"
|
||||
>选择</a-button
|
||||
>设备接入网关,用以提供设备接入能力
|
||||
</span>
|
||||
<span v-else>暂无权限,请联系管理员</span>
|
||||
</template>
|
||||
</a-empty>
|
||||
</div>
|
||||
|
@ -173,9 +174,7 @@
|
|||
</a-select>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<a-button type="primary" @click="submitDevice"
|
||||
>保存</a-button
|
||||
>
|
||||
<PermissionButton type="primary" @click="submitDevice" hasPermission="device/Instance:update">保存</PermissionButton>
|
||||
</a-col>
|
||||
<a-col
|
||||
:span="12"
|
||||
|
@ -271,16 +270,11 @@
|
|||
target="deviceModal"
|
||||
@search="search"
|
||||
/>
|
||||
<JTable
|
||||
<JProTable
|
||||
:columns="columns"
|
||||
:request="queryList"
|
||||
ref="tableRef"
|
||||
modal="card"
|
||||
:rowSelection="{
|
||||
selectedRowKeys: _selectedRowKeys,
|
||||
onChange: onSelectChange,
|
||||
type: 'radio',
|
||||
}"
|
||||
:defaultParams="{
|
||||
...temp,
|
||||
sorts: [
|
||||
|
@ -343,7 +337,7 @@
|
|||
<template #id="slotProps">
|
||||
<a>{{ slotProps.id }}</a>
|
||||
</template>
|
||||
</JTable>
|
||||
</JProTable>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
|
@ -353,6 +347,7 @@ import { ConfigMetadata } from '@/views/device/Product/typings';
|
|||
import { Empty, message } from 'ant-design-vue';
|
||||
import { getImage } from '@/utils/comm';
|
||||
import Title from '../Title/index.vue';
|
||||
import { usePermissionStore } from '@/store/permission';
|
||||
import './index.less';
|
||||
import {
|
||||
getProviders,
|
||||
|
@ -375,6 +370,7 @@ import Driver from 'driver.js';
|
|||
import 'driver.js/dist/driver.min.css';
|
||||
import { marked } from 'marked';
|
||||
import type { FormInstance, TableColumnType } from 'ant-design-vue';
|
||||
const permissionStore = usePermissionStore();
|
||||
const render = new marked.Renderer();
|
||||
marked.setOptions({
|
||||
renderer: render,
|
||||
|
@ -548,10 +544,6 @@ const temp = {
|
|||
const _selectedRowKeys = ref<string[]>([]);
|
||||
const currentForm = ref({});
|
||||
|
||||
const onSelectChange = (keys: string[]) => {
|
||||
_selectedRowKeys.value = [...keys];
|
||||
};
|
||||
|
||||
const cancelSelect = () => {
|
||||
_selectedRowKeys.value = [];
|
||||
};
|
||||
|
|
|
@ -15,19 +15,26 @@
|
|||
:params="params"
|
||||
>
|
||||
<template #headerTitle>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="add"
|
||||
><plus-outlined />新增</a-button
|
||||
<j-space>
|
||||
<PermissionButton
|
||||
type="primary"
|
||||
@click="add"
|
||||
hasPermission="device/Product:add"
|
||||
>
|
||||
<template #icon><AIcon type="PlusOutlined" /></template>
|
||||
新增
|
||||
</PermissionButton>
|
||||
<a-upload
|
||||
name="file"
|
||||
accept=".json"
|
||||
:showUploadList="false"
|
||||
:before-upload="beforeUpload"
|
||||
>
|
||||
<a-button>导入</a-button>
|
||||
<PermissionButton hasPermission="device/Product:import"
|
||||
>导入</PermissionButton
|
||||
>
|
||||
</a-upload>
|
||||
</a-space>
|
||||
</j-space>
|
||||
</template>
|
||||
<template #deviceType="slotProps">
|
||||
<div>{{ slotProps.deviceType.text }}</div>
|
||||
|
@ -130,6 +137,7 @@
|
|||
...item.tooltip,
|
||||
}"
|
||||
@click="item.onClick"
|
||||
:hasPermission="'device/Product:' + item.key"
|
||||
>
|
||||
<AIcon
|
||||
type="DeleteOutlined"
|
||||
|
@ -193,6 +201,7 @@
|
|||
<PermissionButton
|
||||
:disabled="i.disabled"
|
||||
:popConfirm="i.popConfirm"
|
||||
:hasPermission="'device/Product:' + i.key"
|
||||
:tooltip="{
|
||||
...i.tooltip,
|
||||
}"
|
||||
|
@ -334,12 +343,11 @@ const getActions = (
|
|||
},
|
||||
},
|
||||
{
|
||||
key: 'edit',
|
||||
key: 'update',
|
||||
text: '编辑',
|
||||
tooltip: {
|
||||
title: '编辑',
|
||||
},
|
||||
|
||||
icon: 'EditOutlined',
|
||||
onClick: () => {
|
||||
title.value = '编辑';
|
||||
|
@ -350,7 +358,7 @@ const getActions = (
|
|||
},
|
||||
},
|
||||
{
|
||||
key: 'download',
|
||||
key: 'export',
|
||||
text: '导出',
|
||||
tooltip: {
|
||||
title: '导出',
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
<template>
|
||||
<a-card class="boot-card-container" :bordered="false">
|
||||
<template #title>
|
||||
<h5 class="title">{{ cardTitle }}</h5>
|
||||
</template>
|
||||
<div class="boot-card-container" :bordered="false">
|
||||
<h5 class="title">{{ cardTitle }}</h5>
|
||||
|
||||
<div class="box">
|
||||
<div
|
||||
class="box-item"
|
||||
v-for="(item, index) in cardData"
|
||||
@click="jumpPage(item)"
|
||||
@click="jumpPage(item.link,item.params)"
|
||||
>
|
||||
<div class="item-english">{{ item.english }}</div>
|
||||
<div class="item-title">{{ item.label }}</div>
|
||||
|
@ -18,46 +17,27 @@
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
</a-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { message } from 'ant-design-vue';
|
||||
import { bootConfig } from "../index";
|
||||
import { bootConfig } from "../typing";
|
||||
import { useMenuStore } from '@/store/menu';
|
||||
|
||||
const { jumpPage } = useMenuStore();
|
||||
|
||||
const router = useRouter();
|
||||
const props = defineProps({
|
||||
cardData: Array<bootConfig>,
|
||||
cardTitle: String,
|
||||
});
|
||||
const { cardData, cardTitle } = toRefs(props);
|
||||
|
||||
const jumpPage = (row: bootConfig): void => {
|
||||
if (row.auth && row.link) {
|
||||
router.push(`${row.link}${objToParams(row.params || {})}`);
|
||||
} else {
|
||||
message.warning('暂无权限,请联系管理员');
|
||||
}
|
||||
};
|
||||
|
||||
const objToParams = (source: object): string => {
|
||||
if (Object.prototype.toString.call(source) === '[object Object]') {
|
||||
const paramsArr = <any>[];
|
||||
Object.entries(source).forEach(([prop, value]) => {
|
||||
if (typeof value === 'object') value = JSON.stringify(value);
|
||||
paramsArr.push(`${prop}=${value}`);
|
||||
});
|
||||
if (paramsArr.length > 0) return '?' + paramsArr.join('&');
|
||||
}
|
||||
return '';
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.boot-card-container {
|
||||
:deep(.ant-card-body) {
|
||||
padding-top: 0;
|
||||
}
|
||||
background-color: #fff;
|
||||
padding: 24px 14px;
|
||||
.title {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import { message } from 'ant-design-vue';
|
||||
import { bootConfig } from "../index";
|
||||
import { bootConfig } from "../typing";
|
||||
|
||||
const router = useRouter();
|
||||
const props = defineProps({
|
||||
|
|
|
@ -43,7 +43,7 @@ import StepCard from '../StepCard.vue';
|
|||
|
||||
import { useMenuStore } from '@/store/menu';
|
||||
import { usePermissionStore } from '@/store/permission';
|
||||
import { recommendList, bootConfig } from '../../index';
|
||||
import { recommendList, bootConfig } from '../../typing';
|
||||
|
||||
// 按钮权限控制
|
||||
const hasPermission = usePermissionStore().hasPermission;
|
||||
|
|
|
@ -28,7 +28,7 @@ import PlatformPicCard from '../PlatformPicCard.vue';
|
|||
import StepCard from '../StepCard.vue';
|
||||
import { useMenuStore } from "@/store/menu";
|
||||
|
||||
import { bootConfig, recommendList } from '../../index';
|
||||
import { bootConfig, recommendList } from '../../typing';
|
||||
// 页面权限控制
|
||||
const menuPermission = useMenuStore().hasPermission;
|
||||
|
||||
|
|
|
@ -1,13 +1,7 @@
|
|||
<template>
|
||||
<a-card class="device-count-container">
|
||||
<template #title>
|
||||
<h5 class="title">设备统计</h5>
|
||||
</template>
|
||||
<template #extra>
|
||||
<span style="color: #1d39c4; cursor: pointer" @click="jumpPage"
|
||||
>详情</span
|
||||
>
|
||||
</template>
|
||||
<div class="device-count-container">
|
||||
<h5 class="title">设备统计</h5>
|
||||
<span class="detail" @click="jumpPage('device/DashBoard')"> 详情 </span>
|
||||
|
||||
<div class="box-list">
|
||||
<div class="box-item">
|
||||
|
@ -21,34 +15,33 @@
|
|||
<img src="/images/home/top-2.png" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
</a-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getDeviceCount_api, getProductCount_api } from '@/api/home';
|
||||
import { useMenuStore } from '@/store/menu';
|
||||
|
||||
const { jumpPage } = useMenuStore();
|
||||
const projectNum = ref(0);
|
||||
const deviceNum = ref(0);
|
||||
|
||||
onMounted(() => {
|
||||
getData();
|
||||
});
|
||||
|
||||
const getData = () => {
|
||||
getDeviceCount_api().then((resp) => {
|
||||
getDeviceCount_api().then((resp: any) => {
|
||||
deviceNum.value = resp.result;
|
||||
});
|
||||
getProductCount_api().then((resp) => {
|
||||
getProductCount_api({}).then((resp: any) => {
|
||||
projectNum.value = resp.result;
|
||||
});
|
||||
};
|
||||
const jumpPage = () => {};
|
||||
getData();
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.device-count-container {
|
||||
:deep(.ant-card-body) {
|
||||
padding-top: 0;
|
||||
}
|
||||
background-color: #fff;
|
||||
padding: 24px 14px;
|
||||
position: relative;
|
||||
.title {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
|
@ -71,6 +64,14 @@ const jumpPage = () => {};
|
|||
content: ' ';
|
||||
}
|
||||
}
|
||||
.detail {
|
||||
color: #1d39c4;
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
top: 24px;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.box-list {
|
||||
display: grid;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="device-home-container">
|
||||
<a-row :gutter="10">
|
||||
<a-row :gutter="24">
|
||||
<a-col :span="14">
|
||||
<BootCard :cardData="deviceBootConfig" cardTitle="物联网引导" />
|
||||
</a-col>
|
||||
|
@ -18,7 +18,7 @@
|
|||
:dataList="deviceStepDetails"
|
||||
/>
|
||||
</a-row>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="deviceHome">
|
||||
|
@ -28,7 +28,7 @@ import PlatformPicCard from '../PlatformPicCard.vue';
|
|||
import StepCard from '../StepCard.vue';
|
||||
|
||||
import { usePermissionStore } from '@/store/permission';
|
||||
import { bootConfig, recommendList } from '../../index';
|
||||
import { bootConfig, recommendList } from '../../typing';
|
||||
|
||||
// 按钮权限控制
|
||||
const hasPermission = usePermissionStore().hasPermission;
|
||||
|
@ -43,28 +43,28 @@ const deviceBootConfig: bootConfig[] = [
|
|||
{
|
||||
english: 'STEP1',
|
||||
label: '创建产品',
|
||||
link: '/iot/device/Product',
|
||||
link: 'device/Product',
|
||||
auth: productPermission('add'),
|
||||
params: {
|
||||
save: true,
|
||||
type: 'add',
|
||||
},
|
||||
},
|
||||
{
|
||||
english: 'STEP2',
|
||||
label: '创建设备',
|
||||
link: '/iot/device/Instance',
|
||||
link: 'device/Instance',
|
||||
auth: devicePermission('add'),
|
||||
params: {
|
||||
save: true,
|
||||
type: 'add',
|
||||
},
|
||||
},
|
||||
{
|
||||
english: 'STEP3',
|
||||
label: '规则引擎',
|
||||
link: '/iot/rule-engine/Instance',
|
||||
link: 'rule-engine/Instance',
|
||||
auth: rulePermission('add'),
|
||||
params: {
|
||||
save: true,
|
||||
type: 'add',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
@ -74,10 +74,10 @@ const deviceStepDetails: recommendList[] = [
|
|||
details:
|
||||
'产品是设备的集合,通常指一组具有相同功能的设备。物联设备必须通过产品进行接入方式配置。',
|
||||
iconUrl: '/images/home/bottom-4.png',
|
||||
linkUrl: '/iot/device/Product',
|
||||
linkUrl: 'device/Product',
|
||||
auth: productPermission('add'),
|
||||
params: {
|
||||
save: true,
|
||||
type: 'add',
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -85,7 +85,7 @@ const deviceStepDetails: recommendList[] = [
|
|||
details:
|
||||
'通过产品对同一类型的设备进行统一的接入方式配置。请参照设备铭牌说明选择匹配的接入方式。',
|
||||
iconUrl: '/images/home/bottom-1.png',
|
||||
linkUrl: '/iot/device/Product/detail',
|
||||
linkUrl: 'device/Product/Detail',
|
||||
auth: productPermission('update'),
|
||||
dialogTag: 'accessMethod',
|
||||
},
|
||||
|
@ -93,10 +93,10 @@ const deviceStepDetails: recommendList[] = [
|
|||
title: '添加测试设备',
|
||||
details: '添加单个设备,用于验证产品模型是否配置正确。',
|
||||
iconUrl: '/images/home/bottom-5.png',
|
||||
linkUrl: '/iot/device/Instance',
|
||||
linkUrl: 'device/Instance',
|
||||
auth: devicePermission('add'),
|
||||
params: {
|
||||
save: true,
|
||||
type: 'add',
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -104,8 +104,7 @@ const deviceStepDetails: recommendList[] = [
|
|||
details:
|
||||
'对添加的测试设备进行功能调试,验证能否连接到平台,设备功能是否配置正确。',
|
||||
iconUrl: '/images/home/bottom-2.png',
|
||||
linkUrl: '/iot/device/Instance/detail',
|
||||
// auth: devicePermission('update'),
|
||||
linkUrl: 'device/Instance/Detail',
|
||||
auth: true,
|
||||
dialogTag: 'funcTest',
|
||||
},
|
||||
|
@ -113,7 +112,7 @@ const deviceStepDetails: recommendList[] = [
|
|||
title: '批量添加设备',
|
||||
details: '批量添加同一产品下的设备',
|
||||
iconUrl: '/images/home/bottom-3.png',
|
||||
linkUrl: '/iot/device/Instance',
|
||||
linkUrl: 'device/Instance',
|
||||
auth: devicePermission('import'),
|
||||
params: {
|
||||
import: true,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<a-card class="platform-pic-container">
|
||||
<div class="platform-pic-container">
|
||||
<div class="title">
|
||||
<span>平台架构图</span>
|
||||
<p>PLATFORM ARCHITECTURE DIAGRAM</p>
|
||||
|
@ -9,7 +9,7 @@
|
|||
class="bj"
|
||||
alt=""
|
||||
/>
|
||||
</a-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
@ -19,13 +19,11 @@ const props = defineProps({
|
|||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
:deep(.ant-card-body) {
|
||||
padding: 0;
|
||||
}
|
||||
.platform-pic-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
background-color: #fff;
|
||||
border-bottom: 1px solid #2f54eb;
|
||||
|
||||
.bj {
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
<template>
|
||||
<a-card class="step-container">
|
||||
<dic class="step-container">
|
||||
<h5 class="title">
|
||||
<span style="margin-right: 12px">{{ cardTitle }}</span>
|
||||
<a-tooltip placement="top">
|
||||
<span style="margin-right: 12px">{{ props.cardTitle }}</span>
|
||||
<j-tooltip placement="top">
|
||||
<template #title>
|
||||
<span>{{ tooltip }}</span>
|
||||
<span>{{ props.tooltip }}</span>
|
||||
</template>
|
||||
<question-circle-outlined style="padding-top: 5px" />
|
||||
</a-tooltip>
|
||||
<AIcon type="QuestionCircleOutlined" style="padding-top: 5px" />
|
||||
</j-tooltip>
|
||||
</h5>
|
||||
|
||||
<div class="box-list">
|
||||
<div class="list-item" v-for="item in dataList">
|
||||
<div class="list-item" v-for="item in props.dataList">
|
||||
<div class="box-top" @click="jumpPage(item)">
|
||||
<span class="top-title">{{ item.title }}</span>
|
||||
<img :src="item.iconUrl" alt="" />
|
||||
|
@ -22,42 +22,35 @@
|
|||
|
||||
<div class="dialogs">
|
||||
<ProductChooseDialog
|
||||
:open-number="openAccess"
|
||||
v-if="productDialogVisible"
|
||||
v-model:visible="productDialogVisible"
|
||||
@confirm="againJumpPage"
|
||||
/>
|
||||
<DeviceChooseDialog
|
||||
:open-number="openFunc"
|
||||
v-if="deviceDialogVisible"
|
||||
v-model:visible="deviceDialogVisible"
|
||||
@confirm="againJumpPage"
|
||||
/>
|
||||
</div>
|
||||
</a-card>
|
||||
</dic>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { PropType } from 'vue';
|
||||
import { QuestionCircleOutlined } from '@ant-design/icons-vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import ProductChooseDialog from './dialogs/ProductChooseDialog.vue';
|
||||
import DeviceChooseDialog from './dialogs/DeviceChooseDialog.vue';
|
||||
import { recommendList } from '../typing';
|
||||
import { useMenuStore } from '@/store/menu';
|
||||
|
||||
import { recommendList } from '../index';
|
||||
|
||||
type rowType = {
|
||||
params: object;
|
||||
linkUrl: string;
|
||||
};
|
||||
const { jumpPage: _jumpPage } = useMenuStore();
|
||||
|
||||
const props = defineProps({
|
||||
cardTitle: String,
|
||||
tooltip: String,
|
||||
dataList: Array as PropType<recommendList[]>,
|
||||
});
|
||||
const router = useRouter();
|
||||
|
||||
const { cardTitle, tooltip, dataList } = toRefs(props);
|
||||
const openAccess = ref<number>(0);
|
||||
const openFunc = ref<number>(0);
|
||||
let selectRow: recommendList | rowType = {
|
||||
params: {},
|
||||
linkUrl: '',
|
||||
|
@ -66,34 +59,33 @@ let selectRow: recommendList | rowType = {
|
|||
const jumpPage = (row: recommendList) => {
|
||||
if (!row.auth) return message.warning('暂无权限,请联系管理员');
|
||||
selectRow = row; // 二次跳转需要使用
|
||||
if (row.dialogTag == 'accessMethod') return (openAccess.value += 1);
|
||||
else if (row.dialogTag === 'funcTest') return (openFunc.value += 1);
|
||||
if (row.dialogTag == 'accessMethod')
|
||||
return (productDialogVisible.value = true);
|
||||
else if (row.dialogTag === 'funcTest')
|
||||
return (deviceDialogVisible.value = true);
|
||||
else if (row.linkUrl) {
|
||||
router.push(`${row.linkUrl}${objToParams(row.params || {})}`);
|
||||
_jumpPage(row.linkUrl, row.params);
|
||||
}
|
||||
};
|
||||
// 弹窗返回后的二次跳转
|
||||
const againJumpPage = (params: string) => {
|
||||
router.push(`${selectRow.linkUrl}/${params}`);
|
||||
const againJumpPage = (id: string) => {
|
||||
_jumpPage(selectRow.linkUrl, { id });
|
||||
};
|
||||
|
||||
const objToParams = (source: object): string => {
|
||||
if (Object.prototype.toString.call(source) === '[object Object]') {
|
||||
const paramsArr = <any>[];
|
||||
// 直接使用for in遍历对象ts会报错
|
||||
Object.entries(source).forEach(([prop, value]) => {
|
||||
if (typeof value === 'object') value = JSON.stringify(value);
|
||||
paramsArr.push(`${prop}=${value}`);
|
||||
});
|
||||
if (paramsArr.length > 0) return '?' + paramsArr.join('&');
|
||||
}
|
||||
return '';
|
||||
const productDialogVisible = ref(false);
|
||||
const deviceDialogVisible = ref(false);
|
||||
|
||||
type rowType = {
|
||||
params: object;
|
||||
linkUrl: string;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.step-container {
|
||||
width: 100%;
|
||||
padding: 24px 14px;
|
||||
background-color: #fff;
|
||||
.title {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
|
|
|
@ -1,23 +1,26 @@
|
|||
<template>
|
||||
<div ref="modal" class="func-test-dialog-container"></div>
|
||||
<a-modal
|
||||
v-model:visible="visible"
|
||||
title="选择产品"
|
||||
<j-modal
|
||||
visible
|
||||
title="选择设备"
|
||||
style="width: 1000px"
|
||||
@ok="handleOk"
|
||||
:getContainer="getContainer"
|
||||
@ok="confirm"
|
||||
@cancel="emits('update:visible', false)"
|
||||
:maskClosable="false"
|
||||
>
|
||||
<Search type="simple" :columns="query.columns" @search="query.search" />
|
||||
<JTable
|
||||
<j-advanced-search
|
||||
type="simple"
|
||||
:columns="columns"
|
||||
@search="(params:any)=>queryParams = {...params}"
|
||||
/>
|
||||
<j-pro-table
|
||||
model="TABLE"
|
||||
:request="getDeviceList_api"
|
||||
:columns="table.columns"
|
||||
:params="query.params"
|
||||
:columns="columns"
|
||||
:params="queryParams"
|
||||
:defaultParams="{ sorts: [{ name: 'createTime', order: 'desc' }] }"
|
||||
:rowSelection="{
|
||||
selectedRowKeys: table.selectedKeys,
|
||||
onChange: table.onSelect,
|
||||
selectedRowKeys: selectedKeys,
|
||||
onChange: (keys:string[])=>selectedKeys = keys,
|
||||
type: 'radio',
|
||||
}"
|
||||
>
|
||||
|
@ -32,152 +35,90 @@
|
|||
: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>
|
||||
</j-pro-table>
|
||||
</j-modal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import StatusLabel from '../StatusLabel.vue';
|
||||
|
||||
import { ComponentInternalInstance } from 'vue';
|
||||
import { getDeviceList_api } from '@/api/home';
|
||||
import { message } from 'ant-design-vue';
|
||||
import moment from 'moment';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const emits = defineEmits(['confirm']);
|
||||
const props = defineProps({
|
||||
openNumber: Number,
|
||||
});
|
||||
const emits = defineEmits(['confirm', 'update:visible']);
|
||||
const props = defineProps<{
|
||||
visible: boolean;
|
||||
}>();
|
||||
|
||||
// 弹窗控制
|
||||
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;
|
||||
const confirm = () => {
|
||||
if (selectedKeys.value.length < 1) return message.warn('请选择设备');
|
||||
emits('confirm', selectedKeys.value[0]);
|
||||
emits('update:visible', false);
|
||||
};
|
||||
watch(
|
||||
() => props.openNumber,
|
||||
() => {
|
||||
visible.value = true;
|
||||
},
|
||||
);
|
||||
|
||||
const query = reactive({
|
||||
params: {},
|
||||
columns: [
|
||||
{
|
||||
title: '设备ID',
|
||||
dataIndex: 'id',
|
||||
key: 'id',
|
||||
ellipsis: true,
|
||||
search: {
|
||||
type: 'string',
|
||||
},
|
||||
const 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;
|
||||
},
|
||||
});
|
||||
{
|
||||
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',
|
||||
},
|
||||
scopedSlots: true,
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'state',
|
||||
key: 'state',
|
||||
ellipsis: true,
|
||||
search: {
|
||||
rename: 'state',
|
||||
type: 'select',
|
||||
options: [
|
||||
{
|
||||
label: '在线',
|
||||
value: 'online',
|
||||
},
|
||||
{
|
||||
label: '离线',
|
||||
value: 'offline',
|
||||
},
|
||||
],
|
||||
},
|
||||
scopedSlots: true,
|
||||
},
|
||||
];
|
||||
|
||||
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];
|
||||
},
|
||||
});
|
||||
const queryParams = ref({});
|
||||
|
||||
const selectedKeys = ref<string[]>([]);
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.func-test-dialog-container {
|
||||
.search {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,59 +1,52 @@
|
|||
<template>
|
||||
<div ref="modal" class="access-method-dialog-container"></div>
|
||||
<a-modal
|
||||
v-model:visible="visible"
|
||||
<j-modal
|
||||
visible
|
||||
title="选择产品"
|
||||
style="width: 700px"
|
||||
@ok="handleOk"
|
||||
show-search
|
||||
width="700px"
|
||||
@ok="confirm"
|
||||
@cancel="emits('update:visible', false)"
|
||||
:filter-option="filterOption"
|
||||
:getContainer="getContainer"
|
||||
:maskClosable="false"
|
||||
class="access-method-dialog-container"
|
||||
>
|
||||
<a-form :model="form" name="basic" autocomplete="off" layout="vertical">
|
||||
<a-form-item
|
||||
<j-form :model="form" name="basic" autocomplete="off" layout="vertical">
|
||||
<j-form-item
|
||||
label="产品"
|
||||
name="productId"
|
||||
:rules="[{ required: true, message: '该字段是必填字段' }]"
|
||||
>
|
||||
<a-select
|
||||
<j-select
|
||||
v-model:value="form.productId"
|
||||
style="width: 100%"
|
||||
:options="productList"
|
||||
>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<template #footer>
|
||||
<a-button key="back" @click="visible = false">取消</a-button>
|
||||
<a-button key="submit" type="primary" @click="handleOk"
|
||||
>确认</a-button
|
||||
>
|
||||
</template>
|
||||
</a-modal>
|
||||
</j-select>
|
||||
</j-form-item>
|
||||
</j-form>
|
||||
</j-modal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ComponentInternalInstance } from 'vue';
|
||||
|
||||
import { getProductList_api } from '@/api/home';
|
||||
import { productItem } from '../../index';
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const props = defineProps({
|
||||
openNumber: Number,
|
||||
});
|
||||
const emits = defineEmits(['confirm']);
|
||||
const visible = ref<boolean>(false);
|
||||
import { productItem } from '../../typing';
|
||||
|
||||
const emits = defineEmits(['confirm', 'update:visible']);
|
||||
const props = defineProps<{
|
||||
visible: boolean;
|
||||
}>();
|
||||
|
||||
const confirm = () => {
|
||||
emits('confirm', form.value.productId);
|
||||
emits('update:visible', false);
|
||||
};
|
||||
|
||||
const form = ref({
|
||||
productId: '',
|
||||
});
|
||||
|
||||
const productList = ref<[productItem] | []>([]);
|
||||
|
||||
const getContainer = () => proxy?.$refs.modal as HTMLElement;
|
||||
const getOptions = () => {
|
||||
getProductList_api().then((resp:any) => {
|
||||
getProductList_api().then((resp: any) => {
|
||||
productList.value = resp.result
|
||||
.filter((i: any) => !i?.accessId)
|
||||
.map((item: { name: any; id: any }) => ({
|
||||
|
@ -62,21 +55,11 @@ const getOptions = () => {
|
|||
})) as [productItem];
|
||||
});
|
||||
};
|
||||
const handleOk = () => {
|
||||
emits('confirm', form.value.productId);
|
||||
visible.value = false;
|
||||
};
|
||||
|
||||
const filterOption = (input: string, option: any) => {
|
||||
return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0;
|
||||
};
|
||||
watch(
|
||||
() => props.openNumber,
|
||||
() => {
|
||||
visible.value = true;
|
||||
form.value.productId = '';
|
||||
getOptions();
|
||||
},
|
||||
);
|
||||
getOptions();
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
|
|
@ -1,62 +1,62 @@
|
|||
<!-- 基础内容 -->
|
||||
<template>
|
||||
<a-form layout="vertical" :model="form" ref="formBasicRef">
|
||||
<a-row :span="24" :gutter="24">
|
||||
<a-col :span="10">
|
||||
<a-form-item
|
||||
<j-form layout="vertical" :model="form" ref="formBasicRef">
|
||||
<j-row :span="24" :gutter="24">
|
||||
<j-col :span="10">
|
||||
<j-form-item
|
||||
label="系统名称"
|
||||
name="title"
|
||||
v-bind="validateInfos.title"
|
||||
>
|
||||
<a-input
|
||||
<j-input
|
||||
v-model:value="form.title"
|
||||
:maxlength="64"
|
||||
placeholder="请输入系统名称"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
</j-form-item>
|
||||
<j-form-item
|
||||
label="主题色"
|
||||
name="headerTheme"
|
||||
v-bind="validateInfos.headerTheme"
|
||||
>
|
||||
<a-select v-model:value="form.headerTheme">
|
||||
<a-select-option value="light">白色</a-select-option>
|
||||
<a-select-option value="dark">黑色</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<j-select v-model:value="form.headerTheme">
|
||||
<j-select-option value="light">白色</j-select-option>
|
||||
<j-select-option value="dark">黑色</j-select-option>
|
||||
</j-select>
|
||||
</j-form-item>
|
||||
<j-form-item>
|
||||
<template #label>
|
||||
<span>高德API Key</span>
|
||||
<a-tooltip title="配置后平台可调用高德地图GIS服务">
|
||||
<j-tooltip title="配置后平台可调用高德地图GIS服务">
|
||||
<img
|
||||
class="img-style"
|
||||
:src="getImage('/init-home/mark.png')"
|
||||
/>
|
||||
</a-tooltip>
|
||||
</j-tooltip>
|
||||
</template>
|
||||
<a-input
|
||||
<j-input
|
||||
v-model:value="form.apikey"
|
||||
placeholder="请输入高德API Key"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item name="basePath" v-bind="validateInfos.basePath">
|
||||
</j-form-item>
|
||||
<j-form-item name="basePath" v-bind="validateInfos.basePath">
|
||||
<template #label>
|
||||
<span>base-path</span>
|
||||
<a-tooltip title="系统后台访问的url">
|
||||
<j-tooltip title="系统后台访问的url">
|
||||
<img
|
||||
class="img-style"
|
||||
:src="getImage('/init-home/mark.png')"
|
||||
/>
|
||||
</a-tooltip>
|
||||
</j-tooltip>
|
||||
</template>
|
||||
<a-input
|
||||
<j-input
|
||||
v-model:value="form.basePath"
|
||||
placeholder="请输入高德API Key"
|
||||
placeholder="请输入base-path"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-row :gutter="24" :span="24">
|
||||
<a-col>
|
||||
<a-form-item label="系统logo">
|
||||
</j-form-item>
|
||||
<j-row :gutter="24" :span="24">
|
||||
<j-col>
|
||||
<j-form-item label="系统logo">
|
||||
<div class="upload-image-warp-logo">
|
||||
<div class="upload-image-border-logo">
|
||||
<a-upload
|
||||
|
@ -123,18 +123,18 @@
|
|||
|
||||
<div class="upload-tips">推荐尺寸200*200</div>
|
||||
<div class="upload-tips">支持jpg,png</div>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col>
|
||||
<a-form-item>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col>
|
||||
<j-form-item>
|
||||
<template #label>
|
||||
<span>浏览器页签</span>
|
||||
<a-tooltip title="浏览器tab页中显示的图片元素">
|
||||
<j-tooltip title="浏览器tab页中显示的图片元素">
|
||||
<img
|
||||
class="img-style"
|
||||
:src="getImage('/init-home/mark.png')"
|
||||
/>
|
||||
</a-tooltip>
|
||||
</j-tooltip>
|
||||
</template>
|
||||
<div class="upload-image-warp-logo">
|
||||
<div class="upload-image-border-logo">
|
||||
|
@ -189,12 +189,12 @@
|
|||
|
||||
<div class="upload-tips">推荐尺寸64*64</div>
|
||||
<div class="upload-tips">支持icon格式</div>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-col>
|
||||
<a-col :span="14">
|
||||
<a-form-item label="登录背景图">
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</j-col>
|
||||
<j-col :span="14">
|
||||
<j-form-item label="登录背景图">
|
||||
<div class="upload-image-warp-back">
|
||||
<div class="upload-image-border-back">
|
||||
<a-upload
|
||||
|
@ -247,10 +247,10 @@
|
|||
</div>
|
||||
<div class="upload-tips">支持4M以内的图片:支持jpg、png</div>
|
||||
<div class="upload-tips">建议尺寸1400x1080</div>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</j-form>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { modalState, formState, logoState } from '../data/interface';
|
||||
|
|
|
@ -11,13 +11,13 @@
|
|||
/>
|
||||
</div>
|
||||
<!-- 初始数据提交表单 -->
|
||||
<a-modal
|
||||
<j-modal
|
||||
v-model:visible="visible"
|
||||
title="初始数据"
|
||||
width="52vw"
|
||||
:maskClosable="false"
|
||||
@cancel="cancel"
|
||||
@ok="save"
|
||||
@ok="handelSave"
|
||||
okText="确定"
|
||||
cancelText="取消"
|
||||
class="modal-style"
|
||||
|
@ -31,88 +31,88 @@
|
|||
</p>
|
||||
</div>
|
||||
<div style="margin-top: 20px">
|
||||
<a-form
|
||||
<j-form
|
||||
layout="vertical"
|
||||
:model="modalForm"
|
||||
ref="formRef"
|
||||
:rules="rulesModle"
|
||||
>
|
||||
<a-row :span="24" :gutter="24">
|
||||
<a-col :span="12">
|
||||
<a-form-item name="host">
|
||||
<j-row :span="24" :gutter="24">
|
||||
<j-col :span="12">
|
||||
<j-form-item name="host">
|
||||
<template #label>
|
||||
<span>本地地址 </span>
|
||||
<a-tooltip
|
||||
<j-tooltip
|
||||
title="绑定到服务器上的网卡地址,绑定到所有网卡:0.0.0.0"
|
||||
>
|
||||
<img
|
||||
class="img-style"
|
||||
:src="getImage('/init-home/mark.png')"
|
||||
/>
|
||||
</a-tooltip>
|
||||
</j-tooltip>
|
||||
</template>
|
||||
<a-input
|
||||
<j-input
|
||||
v-model:value="modalForm.host"
|
||||
:disabled="true"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item name="publicHost">
|
||||
</j-form-item>
|
||||
<j-form-item name="publicHost">
|
||||
<template #label>
|
||||
<span>公网地址 </span>
|
||||
<a-tooltip
|
||||
<j-tooltip
|
||||
title="对外提供访问的地址内网环境时填写服务器的内网IP地址"
|
||||
>
|
||||
<img
|
||||
class="img-style"
|
||||
:src="getImage('/init-home/mark.png')"
|
||||
/>
|
||||
</a-tooltip>
|
||||
</j-tooltip>
|
||||
</template>
|
||||
<a-input v-model:value="modalForm.publicHost">
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item name="port">
|
||||
<j-input v-model:value="modalForm.publicHost">
|
||||
</j-input>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col :span="12">
|
||||
<j-form-item name="port">
|
||||
<template #label>
|
||||
<span>本地端口 </span>
|
||||
<a-tooltip title="监听指定端口的请求">
|
||||
<j-tooltip title="监听指定端口的请求">
|
||||
<img
|
||||
class="img-style"
|
||||
:src="getImage('/init-home/mark.png')"
|
||||
/>
|
||||
</a-tooltip>
|
||||
</j-tooltip>
|
||||
</template>
|
||||
<a-select v-model:value="modalForm.port">
|
||||
<a-select-option
|
||||
<j-select v-model:value="modalForm.port">
|
||||
<j-select-option
|
||||
v-for="item in optionPorts"
|
||||
:key="item"
|
||||
:value="item.value"
|
||||
:label="item.label"
|
||||
>{{ item.label }}</a-select-option
|
||||
>{{ item.label }}</j-select-option
|
||||
>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item name="publicPort">
|
||||
</j-select>
|
||||
</j-form-item>
|
||||
<j-form-item name="publicPort">
|
||||
<template #label>
|
||||
<span>公网端口 </span>
|
||||
<a-tooltip title="对外提供访问的端口">
|
||||
<j-tooltip title="对外提供访问的端口">
|
||||
<img
|
||||
class="img-style"
|
||||
:src="getImage('/init-home/mark.png')"
|
||||
/>
|
||||
</a-tooltip>
|
||||
</j-tooltip>
|
||||
</template>
|
||||
<a-input-number
|
||||
<j-input-number
|
||||
v-model:value="modalForm.publicPort"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</j-form>
|
||||
</div>
|
||||
</a-modal>
|
||||
</j-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
@ -321,10 +321,12 @@ const saveCurrentData = () => {
|
|||
});
|
||||
};
|
||||
const { optionPorts, isSucessInit } = toRefs(initialization);
|
||||
const save = () => {
|
||||
message.success('保存成功');
|
||||
flag.value = true;
|
||||
visible.value = false;
|
||||
const handelSave = () => {
|
||||
formRef.value.validate().then(() => {
|
||||
message.success('保存成功');
|
||||
flag.value = true;
|
||||
visible.value = false;
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 初始化
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="init-home-role">
|
||||
<a-checkbox-group @change="getCheckValue">
|
||||
<j-checkbox-group @change="getCheckValue">
|
||||
<div class="init-home-role-content">
|
||||
<div
|
||||
class="role-item role-item-1"
|
||||
|
@ -11,7 +11,7 @@
|
|||
"
|
||||
>
|
||||
<div class="role-item-title">
|
||||
<a-checkbox :value="ROLEKEYS.device"></a-checkbox>
|
||||
<j-checkbox :value="ROLEKEYS.device"></j-checkbox>
|
||||
<div class="role-title">设备接入岗</div>
|
||||
</div>
|
||||
<div class="role-item-content"></div>
|
||||
|
@ -28,7 +28,7 @@
|
|||
"
|
||||
>
|
||||
<div class="role-item-title">
|
||||
<a-checkbox :value="ROLEKEYS.link"></a-checkbox>
|
||||
<j-checkbox :value="ROLEKEYS.link"></j-checkbox>
|
||||
<div class="role-title">运维管理岗</div>
|
||||
</div>
|
||||
<div class="role-item-content"></div>
|
||||
|
@ -45,7 +45,7 @@
|
|||
"
|
||||
>
|
||||
<div class="role-item-title">
|
||||
<a-checkbox :value="ROLEKEYS.complex"></a-checkbox>
|
||||
<j-checkbox :value="ROLEKEYS.complex"></j-checkbox>
|
||||
<div class="role-title">综合管理岗</div>
|
||||
</div>
|
||||
<div class="role-item-content"></div>
|
||||
|
@ -54,7 +54,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-checkbox-group>
|
||||
</j-checkbox-group>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -7,9 +7,9 @@
|
|||
<div class="container-box">
|
||||
<div class="container-main">
|
||||
<div class="container-right">
|
||||
<a-spin :spinning="spinning">
|
||||
<a-collapse v-model:activeKey="activeKey" accordion>
|
||||
<a-collapse-panel key="1">
|
||||
<j-spin :spinning="spinning">
|
||||
<j-collapse v-model:activeKey="activeKey" accordion>
|
||||
<j-collapse-panel key="1">
|
||||
<template #header>
|
||||
<span class="title">基本信息</span>
|
||||
<span class="sub-title"
|
||||
|
@ -17,8 +17,8 @@
|
|||
>
|
||||
</template>
|
||||
<Basic ref="basicRef" />
|
||||
</a-collapse-panel>
|
||||
<a-collapse-panel key="2" >
|
||||
</j-collapse-panel>
|
||||
<j-collapse-panel key="2" >
|
||||
<template #header>
|
||||
<span class="title">菜单初始化</span>
|
||||
<span class="sub-title"
|
||||
|
@ -26,8 +26,8 @@
|
|||
>
|
||||
</template>
|
||||
<Menu></Menu>
|
||||
</a-collapse-panel>
|
||||
<a-collapse-panel key="3" forceRender>
|
||||
</j-collapse-panel>
|
||||
<j-collapse-panel key="3" forceRender>
|
||||
<template #header>
|
||||
<span class="title">角色初始化</span>
|
||||
<span class="sub-title"
|
||||
|
@ -35,8 +35,8 @@
|
|||
>
|
||||
</template>
|
||||
<Role ref="roleRef"></Role>
|
||||
</a-collapse-panel>
|
||||
<a-collapse-panel key="4" forceRender>
|
||||
</j-collapse-panel>
|
||||
<j-collapse-panel key="4" forceRender>
|
||||
<template #header>
|
||||
<span class="title">初始化数据</span>
|
||||
<span class="sub-title"
|
||||
|
@ -44,15 +44,15 @@
|
|||
>
|
||||
</template>
|
||||
<InitData ref="initDataRef"></InitData>
|
||||
</a-collapse-panel>
|
||||
</a-collapse>
|
||||
</a-spin>
|
||||
<a-button
|
||||
</j-collapse-panel>
|
||||
</j-collapse>
|
||||
</j-spin>
|
||||
<j-button
|
||||
type="primary"
|
||||
class="btn-style"
|
||||
@click="submitData"
|
||||
:loading="loading"
|
||||
>确定</a-button
|
||||
>确定</j-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -17,7 +17,13 @@
|
|||
:gridColumn="2"
|
||||
>
|
||||
<template #headerTitle>
|
||||
<a-button type="primary" @click="handleAdd"> 新增 </a-button>
|
||||
<PermissionButton
|
||||
type="primary"
|
||||
@click="handleAdd"
|
||||
hasPermission="media/Cascade:add"
|
||||
>
|
||||
<template #icon><AIcon type="PlusOutlined" />新增</template>
|
||||
</PermissionButton>
|
||||
</template>
|
||||
<template #card="slotProps">
|
||||
<CardBox
|
||||
|
@ -58,48 +64,14 @@
|
|||
</Ellipsis>
|
||||
</template>
|
||||
<template #actions="item">
|
||||
<a-tooltip
|
||||
v-bind="item.tooltip"
|
||||
:title="item.disabled && item.tooltip.title"
|
||||
>
|
||||
<a-popconfirm
|
||||
v-if="item.popConfirm"
|
||||
v-bind="item.popConfirm"
|
||||
:disabled="item.disabled"
|
||||
>
|
||||
<a-button
|
||||
:disabled="item.disabled"
|
||||
v-if="item.key === 'delete'"
|
||||
>
|
||||
<AIcon type="DeleteOutlined" />
|
||||
</a-button>
|
||||
<a-button
|
||||
:disabled="item.disabled"
|
||||
@click="item.onClick"
|
||||
v-else
|
||||
>
|
||||
<AIcon :type="item.icon" />
|
||||
<span>{{ item.text }}</span>
|
||||
</a-button>
|
||||
</a-popconfirm>
|
||||
<template v-else>
|
||||
<a-button
|
||||
:disabled="item.disabled"
|
||||
@click="item.onClick"
|
||||
>
|
||||
<AIcon :type="item.icon" />
|
||||
<span>{{ item.text }}</span>
|
||||
</a-button>
|
||||
</template>
|
||||
</a-tooltip>
|
||||
<!-- <PermissionButton
|
||||
<PermissionButton
|
||||
:disabled="item.disabled"
|
||||
:popConfirm="item.popConfirm"
|
||||
:tooltip="{
|
||||
...item.tooltip,
|
||||
}"
|
||||
@click="item.onClick"
|
||||
:hasPermission="`media/Cascade:${item.key}`"
|
||||
:hasPermission="'media/Cascade:' + item.key"
|
||||
>
|
||||
<AIcon
|
||||
type="DeleteOutlined"
|
||||
|
@ -109,7 +81,7 @@
|
|||
<AIcon :type="item.icon" />
|
||||
<span>{{ item?.text }}</span>
|
||||
</template>
|
||||
</PermissionButton> -->
|
||||
</PermissionButton>
|
||||
</template>
|
||||
</CardBox>
|
||||
</template>
|
||||
|
@ -141,38 +113,7 @@
|
|||
</template>
|
||||
<template #action="slotProps">
|
||||
<a-space :size="16">
|
||||
<a-tooltip
|
||||
v-for="i in getActions(slotProps, 'table')"
|
||||
:key="i.key"
|
||||
v-bind="i.tooltip"
|
||||
>
|
||||
<a-popconfirm
|
||||
v-if="i.popConfirm"
|
||||
v-bind="i.popConfirm"
|
||||
:disabled="i.disabled"
|
||||
>
|
||||
<a-button
|
||||
:disabled="i.disabled"
|
||||
style="padding: 0"
|
||||
type="link"
|
||||
><AIcon :type="i.icon"
|
||||
/></a-button>
|
||||
</a-popconfirm>
|
||||
<a-button
|
||||
style="padding: 0"
|
||||
type="link"
|
||||
v-else
|
||||
@click="i.onClick && i.onClick(slotProps)"
|
||||
>
|
||||
<a-button
|
||||
:disabled="i.disabled"
|
||||
style="padding: 0"
|
||||
type="link"
|
||||
><AIcon :type="i.icon"
|
||||
/></a-button>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
<!-- <template
|
||||
<template
|
||||
v-for="i in getActions(slotProps, 'table')"
|
||||
:key="i.key"
|
||||
>
|
||||
|
@ -185,11 +126,11 @@
|
|||
@click="i.onClick"
|
||||
type="link"
|
||||
style="padding: 0px"
|
||||
:hasPermission="`device/Instance:${i.key}`"
|
||||
:hasPermission="'media/Cascade:' + i.key"
|
||||
>
|
||||
<template #icon><AIcon :type="i.icon" /></template>
|
||||
</PermissionButton>
|
||||
</template> -->
|
||||
</template>
|
||||
</a-space>
|
||||
</template>
|
||||
</JProTable>
|
||||
|
@ -331,7 +272,7 @@ const getActions = (
|
|||
if (!data) return [];
|
||||
const actions = [
|
||||
{
|
||||
key: 'edit',
|
||||
key: 'update',
|
||||
text: '编辑',
|
||||
tooltip: {
|
||||
title: '编辑',
|
||||
|
@ -348,7 +289,7 @@ const getActions = (
|
|||
},
|
||||
},
|
||||
{
|
||||
key: 'view',
|
||||
key: 'channel',
|
||||
text: '选择通道',
|
||||
tooltip: {
|
||||
title: '选择通道',
|
||||
|
@ -365,7 +306,7 @@ const getActions = (
|
|||
},
|
||||
},
|
||||
{
|
||||
key: 'debug',
|
||||
key: 'push',
|
||||
text: '推送',
|
||||
tooltip: {
|
||||
title:
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
<div class="tool-item">刷新</div>
|
||||
<div class="tool-item" @click.stop="handleReset">重置</div>
|
||||
</div>
|
||||
<LivePlayer :src="src" :type="mediaType" />
|
||||
<LivePlayer :url="url" :protocol="mediaType" autoplay />
|
||||
</div>
|
||||
<MediaTool
|
||||
@onMouseDown="handleMouseDown"
|
||||
|
@ -71,14 +71,14 @@ const _vis = computed({
|
|||
});
|
||||
|
||||
// 视频地址
|
||||
const src = ref('');
|
||||
const url = ref('');
|
||||
// 视频类型
|
||||
const mediaType = ref('mp4');
|
||||
const mediaType = ref<'mp4' | 'flv' | 'hls'>('mp4');
|
||||
/**
|
||||
* 媒体开始播放
|
||||
*/
|
||||
const mediaStart = () => {
|
||||
src.value = channelApi.ptzStart(
|
||||
url.value = channelApi.ptzStart(
|
||||
props.data.deviceId,
|
||||
props.data.channelId,
|
||||
mediaType.value,
|
||||
|
@ -150,6 +150,9 @@ watch(
|
|||
if (val) {
|
||||
mediaStart();
|
||||
getIsRecord();
|
||||
} else {
|
||||
// url置空, 即销毁播放器
|
||||
url.value = '';
|
||||
}
|
||||
},
|
||||
);
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
<!-- 视频图标组件 -->
|
||||
<template>
|
||||
<a @click="handleClick">
|
||||
<AIcon :type="iconType" />
|
||||
</a>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { recordsItemType } from './typings';
|
||||
import playBackApi from '@/api/media/playback';
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
interface Props {
|
||||
type: string;
|
||||
item: recordsItemType;
|
||||
onCloudView: (startTime: number, endTime: number) => void;
|
||||
onDownLoad: () => void;
|
||||
}
|
||||
const props = defineProps<Props>();
|
||||
|
||||
// type 为local时有效,0:未下载; 1:下载中:2:已下载
|
||||
const status = ref(props.item?.isServer ? 2 : 0);
|
||||
|
||||
const getLocalIcon = (s: number) => {
|
||||
if (s === 0) {
|
||||
return 'CloudDownloadOutlined';
|
||||
} else if (s === 2) {
|
||||
return 'EyeOutlined';
|
||||
} else {
|
||||
return 'LoadingOutlined';
|
||||
}
|
||||
};
|
||||
|
||||
const iconType = computed(() =>
|
||||
props.type === 'local' ? getLocalIcon(status.value) : 'DownloadOutlined',
|
||||
);
|
||||
|
||||
const downLoadCloud = (item: recordsItemType) => {
|
||||
status.value = 1;
|
||||
playBackApi
|
||||
.downloadRecord(item.deviceId, item.channelId, {
|
||||
local: false,
|
||||
downloadSpeed: 4,
|
||||
startTime: item.startTime,
|
||||
endTime: item.endTime,
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.status === 200) {
|
||||
message.success(
|
||||
'操作成功。上传云端需要一定时间,请稍后查看云端数据',
|
||||
);
|
||||
}
|
||||
status.value = res.status === 200 ? 2 : 0;
|
||||
})
|
||||
.catch(() => {
|
||||
status.value = 0;
|
||||
});
|
||||
};
|
||||
|
||||
const handleClick = () => {
|
||||
if (props.type === 'local') {
|
||||
if (status.value === 2) {
|
||||
// 已下载,进行跳转
|
||||
props.onCloudView(props.item.startTime, props.item.endTime);
|
||||
} else if (status.value === 0) {
|
||||
// 未下载 进行下载
|
||||
downLoadCloud(props.item);
|
||||
}
|
||||
} else {
|
||||
props.onDownLoad();
|
||||
}
|
||||
};
|
||||
</script>
|
|
@ -0,0 +1,119 @@
|
|||
@borderColor: #d9d9d9;
|
||||
|
||||
.playback-warp {
|
||||
display: flex;
|
||||
padding: 24px;
|
||||
background-color: #fff;
|
||||
|
||||
.playback-left {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
width: 0;
|
||||
|
||||
.playback-media {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.playback-right {
|
||||
width: 300px;
|
||||
margin-left: 24px;
|
||||
|
||||
.playback-calendar {
|
||||
margin-top: 16px;
|
||||
border: 1px solid @borderColor;
|
||||
border-radius: 2px;
|
||||
|
||||
:deep(.ant-picker-calendar-header) {
|
||||
justify-content: space-between;
|
||||
& > div:nth-child(3) {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.playback-list {
|
||||
display: flex;
|
||||
height: 300px;
|
||||
margin-top: 16px;
|
||||
overflow-y: auto;
|
||||
border: 1px solid @borderColor;
|
||||
|
||||
&.no-list {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.playback-list-items {
|
||||
width: 100%;
|
||||
|
||||
.ant-list-item {
|
||||
padding-left: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.time-line-warp {
|
||||
padding: 10px 0;
|
||||
|
||||
.time-line-clock {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
|
||||
> div {
|
||||
color: #666;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.time-line-content {
|
||||
position: relative;
|
||||
padding-bottom: 20px;
|
||||
|
||||
.time-line-progress {
|
||||
position: relative;
|
||||
height: 16px;
|
||||
overflow: hidden;
|
||||
background-color: #d9d9d9;
|
||||
border-radius: 2px;
|
||||
|
||||
> div {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
background-color: #52c41a;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.time-line-btn {
|
||||
position: absolute;
|
||||
top: -2px;
|
||||
left: 0;
|
||||
width: 3px;
|
||||
height: 19px;
|
||||
background-color: @primary-color;
|
||||
border-radius: 2px;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.time-line {
|
||||
position: absolute;
|
||||
bottom: -8px;
|
||||
left: -30px;
|
||||
width: 60px;
|
||||
padding: 2px 0;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
background-color: #d9d9d9;
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 0 12px rgba(#000, 0.15);
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,406 @@
|
|||
<!-- 回放 -->
|
||||
<template>
|
||||
<page-container> 回放 </page-container>
|
||||
<page-container>
|
||||
<div class="playback-warp">
|
||||
<!-- 播放器/进度条 -->
|
||||
<div class="playback-left">
|
||||
<LivePlayer
|
||||
ref="player"
|
||||
autoplay
|
||||
:url="url"
|
||||
className="playback-media"
|
||||
:live="type === 'local'"
|
||||
:on-play="
|
||||
() => {
|
||||
isEnded = false;
|
||||
playStatus = 1;
|
||||
}
|
||||
"
|
||||
:on-pause="
|
||||
() => {
|
||||
playStatus = 2;
|
||||
}
|
||||
"
|
||||
:on-ended="
|
||||
() => {
|
||||
playStatus = 0;
|
||||
if (playTimeNode && isEnded) {
|
||||
isEnded = true;
|
||||
playTimeNode.onNextPlay();
|
||||
}
|
||||
}
|
||||
"
|
||||
:on-error="
|
||||
() => {
|
||||
playStatus = 0;
|
||||
}
|
||||
"
|
||||
:on-time-update="
|
||||
(e: any) => {
|
||||
playTime = e;
|
||||
}
|
||||
"
|
||||
/>
|
||||
<TimeLine
|
||||
ref="playTimeNode"
|
||||
:type="type"
|
||||
:data="historyList"
|
||||
:date-time="time"
|
||||
:on-change="handleTimeLineChange"
|
||||
:play-status="playStatus"
|
||||
:play-time="playNowTime + playTime * 1000"
|
||||
:local-to-server="cloudTime"
|
||||
/>
|
||||
</div>
|
||||
<div class="playback-right">
|
||||
<a-spin :spinning="loading">
|
||||
<a-tooltip placement="topLeft">
|
||||
<template #title>
|
||||
<div>云端:存储在服务器中</div>
|
||||
<div>本地:存储在设备本地</div>
|
||||
</template>
|
||||
<div>类型: <AIcon type="QuestionCircleOutlined" /></div>
|
||||
</a-tooltip>
|
||||
<RadioCard
|
||||
layout="horizontal"
|
||||
:options="[
|
||||
{
|
||||
label: '云端',
|
||||
value: 'cloud',
|
||||
logo: getImage('/media/cloud.png'),
|
||||
},
|
||||
{
|
||||
label: '本地',
|
||||
value: 'local',
|
||||
logo: getImage('/local.png'),
|
||||
disabled: deviceType === 'fixed-media',
|
||||
},
|
||||
]"
|
||||
:checkStyle="true"
|
||||
v-model="type"
|
||||
/>
|
||||
<div class="playback-calendar">
|
||||
<a-calendar
|
||||
v-model:value="time"
|
||||
:fullscreen="false"
|
||||
:disabledDate="
|
||||
(currentDate) => currentDate > dayjs(new Date())
|
||||
"
|
||||
@change="handlePanelChange"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="playback-list"
|
||||
:class="{ 'no-list': !historyList.length }"
|
||||
>
|
||||
<a-empty
|
||||
v-if="!historyList.length"
|
||||
description="暂无数据"
|
||||
/>
|
||||
<a-list
|
||||
v-else
|
||||
class="playback-list-items"
|
||||
itemLayout="horizontal"
|
||||
:dataSource="historyList"
|
||||
>
|
||||
<template #renderItem="{ item }">
|
||||
<a-list-item>
|
||||
<template #actions>
|
||||
<a-tooltip
|
||||
key="play-btn"
|
||||
:title="
|
||||
(item.startTime ||
|
||||
item.mediaStartTime) ===
|
||||
playNowTime &&
|
||||
playStatus === 1
|
||||
? '暂停'
|
||||
: '播放'
|
||||
"
|
||||
>
|
||||
<a
|
||||
@click="
|
||||
handlePlay(
|
||||
item.startTime ||
|
||||
item.mediaStartTime,
|
||||
)
|
||||
"
|
||||
>
|
||||
<AIcon
|
||||
:type="
|
||||
(item.startTime ||
|
||||
item.mediaStartTime) ===
|
||||
playNowTime &&
|
||||
playStatus === 1
|
||||
? 'PauseCircleOutlined'
|
||||
: 'PlayCircleOutlined'
|
||||
"
|
||||
/>
|
||||
</a>
|
||||
</a-tooltip>
|
||||
<a-tooltip
|
||||
key="download"
|
||||
:title="
|
||||
type !== 'local'
|
||||
? '下载录像文件'
|
||||
: item.isServer
|
||||
? '查看'
|
||||
: '下载到云端'
|
||||
"
|
||||
>
|
||||
<IconNode
|
||||
:type="type"
|
||||
:item="item"
|
||||
:on-cloud-view="cloudView"
|
||||
:on-down-load="
|
||||
() => downloadClick(item)
|
||||
"
|
||||
/>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
|
||||
<div>
|
||||
{{
|
||||
dayjs(
|
||||
item.startTime ||
|
||||
item.mediaStartTime,
|
||||
).format('HH:mm:ss')
|
||||
}}
|
||||
~
|
||||
{{
|
||||
dayjs(
|
||||
item.endTime ||
|
||||
item.mediaEndTime,
|
||||
).format('HH:mm:ss')
|
||||
}}
|
||||
</div>
|
||||
</a-list-item>
|
||||
</template>
|
||||
<div></div>
|
||||
</a-list>
|
||||
</div>
|
||||
</a-spin>
|
||||
</div>
|
||||
</div>
|
||||
</page-container>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts"></script>
|
||||
<script setup lang="ts">
|
||||
import playBackApi from '@/api/media/playback';
|
||||
import TimeLine from './timeLine.vue';
|
||||
import IconNode from './iconNode.vue';
|
||||
import type { recordsItemType } from './typings';
|
||||
import LivePlayer from '@/components/Player/index.vue';
|
||||
import { getImage } from '@/utils/comm';
|
||||
import dayjs from 'dayjs';
|
||||
import type { Dayjs } from 'dayjs';
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
const route = useRoute();
|
||||
|
||||
const url = ref('');
|
||||
const type = ref('local');
|
||||
const historyList = ref<recordsItemType[]>([]);
|
||||
const time = ref<Dayjs | undefined>(undefined);
|
||||
const loading = ref(false);
|
||||
const cloudTime = ref<any>();
|
||||
const player = ref<any>();
|
||||
const playStatus = ref(0); // 播放状态, 0 停止, 1 播放, 2 暂停, 3 播放完成
|
||||
const playTime = ref(0);
|
||||
|
||||
const playNowTime = ref(0); // 当前播放视频标识
|
||||
const playTimeNode = ref<any>(null);
|
||||
const isEnded = ref(false); // 是否结束播放
|
||||
const deviceId = computed(() => route.query.id as string);
|
||||
const channelId = computed(() => route.query.channelId as string);
|
||||
|
||||
const deviceType = ref('');
|
||||
|
||||
/**
|
||||
* 查询本地视频
|
||||
* @param date
|
||||
*/
|
||||
const queryLocalRecords = async (date: Dayjs) => {
|
||||
playStatus.value = 0;
|
||||
url.value = '';
|
||||
|
||||
if (deviceId.value && channelId.value && date) {
|
||||
loading.value = true;
|
||||
const params = {
|
||||
startTime: date.format('YYYY-MM-DD 00:00:00'),
|
||||
endTime: date.format('YYYY-MM-DD 23:59:59'),
|
||||
};
|
||||
const localResp = await playBackApi.queryRecordLocal(
|
||||
deviceId.value,
|
||||
channelId.value,
|
||||
params,
|
||||
);
|
||||
if (localResp.status === 200 && localResp.result.length) {
|
||||
const serviceResp = await playBackApi.recordsInServer(
|
||||
deviceId.value,
|
||||
channelId.value,
|
||||
{
|
||||
...params,
|
||||
includeFiles: false,
|
||||
},
|
||||
);
|
||||
loading.value = false;
|
||||
let newList: recordsItemType[] = serviceResp.result;
|
||||
// console.log(newList)
|
||||
|
||||
if (serviceResp.status === 200 && serviceResp.result) {
|
||||
// 判断是否已下载云端视频
|
||||
newList = localResp.result.map((item: recordsItemType) => {
|
||||
return {
|
||||
...item,
|
||||
isServer: serviceResp.result.length
|
||||
? serviceResp.result.some(
|
||||
(serverFile: any) =>
|
||||
item.startTime <=
|
||||
serverFile.streamStartTime &&
|
||||
serverFile.streamEndTime <= item.endTime,
|
||||
)
|
||||
: false,
|
||||
};
|
||||
});
|
||||
|
||||
historyList.value = newList;
|
||||
} else {
|
||||
historyList.value = newList;
|
||||
}
|
||||
} else {
|
||||
loading.value = false;
|
||||
historyList.value = [];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询云端视频
|
||||
* @param date
|
||||
*/
|
||||
const queryServiceRecords = async (date: Dayjs) => {
|
||||
playStatus.value = 0;
|
||||
url.value = '';
|
||||
if (deviceId.value && channelId.value && date) {
|
||||
loading.value = true;
|
||||
const params = {
|
||||
startTime: date.format('YYYY-MM-DD 00:00:00'),
|
||||
endTime: date.format('YYYY-MM-DD 23:59:59'),
|
||||
includeFiles: true,
|
||||
};
|
||||
|
||||
const resp = await playBackApi.recordsInServerFiles(
|
||||
deviceId.value,
|
||||
channelId.value,
|
||||
params,
|
||||
);
|
||||
loading.value = false;
|
||||
if (resp.status === 200) {
|
||||
historyList.value = resp.result;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const cloudView = (startTime: number, endTime: number) => {
|
||||
type.value = 'cloud';
|
||||
cloudTime.value = { startTime, endTime };
|
||||
queryServiceRecords(time.value!);
|
||||
};
|
||||
|
||||
/**
|
||||
* 下载到云端
|
||||
* @param item
|
||||
*/
|
||||
const downloadClick = async (item: recordsItemType) => {
|
||||
const downloadUrl = playBackApi.downLoadFile(item.id);
|
||||
const downNode = document.createElement('a');
|
||||
downNode.href = downloadUrl;
|
||||
downNode.download = `${channelId}-${dayjs(item.startTime).format(
|
||||
'YYYY-MM-DD-HH-mm-ss',
|
||||
)}.mp4`;
|
||||
downNode.style.display = 'none';
|
||||
document.body.appendChild(downNode);
|
||||
downNode.click();
|
||||
document.body.removeChild(downNode);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
const _type = route.query.type as string;
|
||||
if (_type) {
|
||||
deviceType.value = _type;
|
||||
const _timeStr = dayjs(new Date());
|
||||
time.value = _timeStr;
|
||||
if (_type === 'fixed-media') {
|
||||
type.value = 'cloud';
|
||||
queryServiceRecords(_timeStr);
|
||||
} else {
|
||||
queryLocalRecords(_timeStr);
|
||||
type.value = 'local';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 播放进度条点击
|
||||
* @param times
|
||||
*/
|
||||
const handleTimeLineChange = (times: any) => {
|
||||
if (times) {
|
||||
playNowTime.value = Number(times.startTime.valueOf());
|
||||
playTime.value = 0;
|
||||
url.value =
|
||||
type.value === 'local'
|
||||
? playBackApi.playbackLocal(
|
||||
times.deviceId,
|
||||
times.channelId,
|
||||
'mp4',
|
||||
dayjs(times.startTime).format('YYYY-MM-DD HH:mm:ss'),
|
||||
dayjs(times.endTime).format('YYYY-MM-DD HH:mm:ss'),
|
||||
)
|
||||
: playBackApi.playbackStart(times.deviceId);
|
||||
} else {
|
||||
url.value = '';
|
||||
}
|
||||
};
|
||||
|
||||
watch(
|
||||
() => type.value,
|
||||
(val: string) => {
|
||||
if (val === 'cloud') {
|
||||
queryServiceRecords(time.value!);
|
||||
} else {
|
||||
queryLocalRecords(time.value!);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* 日历操作
|
||||
* @param date
|
||||
*/
|
||||
const handlePanelChange = (date: any) => {
|
||||
time.value = date;
|
||||
if (type.value === 'cloud') {
|
||||
queryServiceRecords(date);
|
||||
} else {
|
||||
queryLocalRecords(date);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 播放/暂停
|
||||
* @param _startTime
|
||||
*/
|
||||
const handlePlay = (_startTime: any) => {
|
||||
if (playStatus.value === 0 || _startTime !== playNowTime.value) {
|
||||
playTimeNode.value?.playByStartTime(_startTime);
|
||||
} else if (playStatus.value == 1 && _startTime === playNowTime.value) {
|
||||
player.value?.pause();
|
||||
} else if (playStatus.value == 2 && _startTime === playNowTime.value) {
|
||||
player.value?.play();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@import './index.less';
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,264 @@
|
|||
<!-- 播放器时间刻度 -->
|
||||
<template>
|
||||
<div class="time-line-warp">
|
||||
<div class="time-line-clock">
|
||||
<div
|
||||
v-for="item in Array.from(Array(25), (v, k) => k)"
|
||||
:key="item"
|
||||
style="width: 12px"
|
||||
>
|
||||
{{ item }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="time-line-content" ref="LineContent">
|
||||
<div class="time-line-progress">
|
||||
<div
|
||||
v-for="(item, index) in list"
|
||||
:key="`time_${index}`"
|
||||
@click="handleProgress($event, item)"
|
||||
:style="
|
||||
getLineItemStyle(
|
||||
item.startTime || item.mediaStartTime,
|
||||
item.endTime || item.mediaEndTime,
|
||||
)
|
||||
"
|
||||
></div>
|
||||
</div>
|
||||
<div id="btn" class="time-line-btn"></div>
|
||||
<div id="time" class="time-line">
|
||||
{{ dayjs(playTime || 0).format('HH:mm:ss') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { message } from 'ant-design-vue';
|
||||
import type { recordsItemType } from './typings';
|
||||
import type { Dayjs } from 'dayjs';
|
||||
import dayjs from 'dayjs';
|
||||
import { useElementSize } from '@vueuse/core';
|
||||
|
||||
export type TimeChangeType = {
|
||||
endTime: Dayjs;
|
||||
startTime: Dayjs;
|
||||
deviceId: string;
|
||||
channelId: string;
|
||||
};
|
||||
|
||||
interface Props {
|
||||
onChange: (times: TimeChangeType | undefined) => void;
|
||||
data: recordsItemType[];
|
||||
dateTime?: Dayjs;
|
||||
type: string;
|
||||
playStatus: number;
|
||||
playTime: number;
|
||||
server?: any;
|
||||
localToServer?: {
|
||||
endTime: number;
|
||||
startTime: number;
|
||||
};
|
||||
getPlayList?: (data: any) => void;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
// 获取选中当天开始时间戳
|
||||
const startT = ref<number>(
|
||||
new Date(
|
||||
dayjs(props.dateTime).startOf('day').format('YYYY-MM-DD HH:mm:ss'),
|
||||
).getTime(),
|
||||
);
|
||||
// 获取选中当天结束时间戳
|
||||
const endT = ref<number>(
|
||||
new Date(
|
||||
dayjs(props.dateTime).endOf('day').format('YYYY-MM-DD HH:mm:ss'),
|
||||
).getTime(),
|
||||
);
|
||||
const list = ref<any[]>([]);
|
||||
const playTime = ref<number>(0);
|
||||
const LineContent = ref<HTMLDivElement>();
|
||||
const LineContentSize = useElementSize(LineContent);
|
||||
|
||||
const setTimeAndPosition = (ob: number) => {
|
||||
const oBtn = document.getElementById('btn');
|
||||
const oTime = document.getElementById('time');
|
||||
|
||||
if (oBtn && oTime && LineContentSize.width) {
|
||||
oBtn.style.visibility = 'visible';
|
||||
oBtn.style.left = `${ob * LineContentSize.width.value}px`;
|
||||
oTime.style.visibility = 'visible';
|
||||
oTime.style.left = `${ob * LineContentSize.width.value - 15}px`;
|
||||
}
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.dateTime,
|
||||
(val: any) => {
|
||||
startT.value = new Date(
|
||||
dayjs(val).startOf('day').format('YYYY-MM-DD HH:mm:ss'),
|
||||
).getTime();
|
||||
},
|
||||
);
|
||||
|
||||
const onChange = (
|
||||
startTime: number,
|
||||
endTime: number,
|
||||
deviceId: string,
|
||||
channelId: string,
|
||||
) => {
|
||||
playTime.value = startTime;
|
||||
props.onChange({
|
||||
startTime: dayjs(startTime),
|
||||
endTime: dayjs(endTime),
|
||||
deviceId,
|
||||
channelId,
|
||||
});
|
||||
};
|
||||
|
||||
const playByStartTime = (time: any) => {
|
||||
const playNow = props.data.find((item) => {
|
||||
const startTime = item.startTime || item.mediaStartTime;
|
||||
return startTime === time;
|
||||
});
|
||||
|
||||
if (playNow) {
|
||||
const startTime = playNow.startTime || playNow.mediaStartTime;
|
||||
const endTime = playNow.endTime || playNow.mediaEndTime;
|
||||
const deviceId = props.type === 'local' ? playNow.deviceId : playNow.id;
|
||||
onChange(startTime, endTime, deviceId, playNow.channelId);
|
||||
}
|
||||
};
|
||||
playByStartTime(0);
|
||||
|
||||
const onNextPlay = () => {
|
||||
if (playTime.value) {
|
||||
// 查找下一个视频
|
||||
const nowIndex = props.data.findIndex((item) => {
|
||||
const startTime = item.startTime || item.mediaStartTime;
|
||||
return startTime === playTime.value;
|
||||
});
|
||||
// 是否为最后一个
|
||||
if (nowIndex !== props.data.length - 1) {
|
||||
const nextPlay = props.data[nowIndex + 1];
|
||||
const startTime = nextPlay.startTime || nextPlay.mediaStartTime;
|
||||
const endTime = nextPlay.endTime || nextPlay.mediaEndTime;
|
||||
const deviceId =
|
||||
props.type === 'local' ? nextPlay.deviceId : nextPlay.id;
|
||||
onChange(startTime, endTime, deviceId, nextPlay.channelId);
|
||||
}
|
||||
}
|
||||
};
|
||||
onNextPlay();
|
||||
|
||||
watch(
|
||||
() => props.data,
|
||||
(val: any) => {
|
||||
const { data, localToServer, type } = props;
|
||||
if (data && Array.isArray(data) && data.length > 0) {
|
||||
list.value = [...data];
|
||||
if (type === 'local') {
|
||||
// 播放第一个
|
||||
onChange(
|
||||
data[0].startTime,
|
||||
data[0].endTime,
|
||||
data[0].deviceId,
|
||||
data[0].channelId,
|
||||
);
|
||||
} else if (type === 'cloud') {
|
||||
// 是否从本地跳转到云端播放
|
||||
if (localToServer && Object.keys(localToServer).length > 0) {
|
||||
// 获取跳转播放段
|
||||
const playItem = data.find((item) => {
|
||||
return (
|
||||
item.mediaEndTime <= localToServer.endTime &&
|
||||
item.mediaStartTime >= localToServer.startTime
|
||||
);
|
||||
});
|
||||
if (playItem) {
|
||||
//播放片段
|
||||
onChange(
|
||||
playItem.mediaStartTime,
|
||||
playItem.mediaEndTime,
|
||||
playItem.id,
|
||||
playItem.channelId,
|
||||
);
|
||||
} else {
|
||||
props.onChange(undefined);
|
||||
message.error('没有可播放的视频资源');
|
||||
}
|
||||
} else {
|
||||
onChange(
|
||||
data[0].mediaStartTime,
|
||||
data[0].mediaEndTime,
|
||||
data[0].id,
|
||||
data[0].channelId,
|
||||
);
|
||||
}
|
||||
}
|
||||
} else if (localToServer && localToServer.startTime) {
|
||||
// 本地跳转云端但是无资源
|
||||
props.onChange(undefined);
|
||||
message.error('没有可播放的视频资源');
|
||||
list.value = [];
|
||||
} else {
|
||||
// 啥都没有
|
||||
list.value = [];
|
||||
props.onChange(undefined);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const getLineItemStyle = (
|
||||
startTime: number,
|
||||
endTime: number,
|
||||
): { left: string; width: string } => {
|
||||
const start = startTime - startT.value > 0 ? startTime - startT.value : 0;
|
||||
const _width = LineContentSize.width.value!;
|
||||
const itemWidth = ((endTime - startTime) / (24 * 3600000)) * _width;
|
||||
return {
|
||||
left: `${(start / (24 * 3600000)) * _width}px`,
|
||||
width: `${itemWidth < 1 ? 1 : itemWidth}px`,
|
||||
};
|
||||
};
|
||||
|
||||
const playTimeChange = () => {
|
||||
if (
|
||||
props.playTime &&
|
||||
props.playTime >= startT.value &&
|
||||
props.playTime <= endT.value &&
|
||||
props.data &&
|
||||
props.data.length
|
||||
) {
|
||||
setTimeAndPosition((props.playTime - startT.value) / 3600000 / 24);
|
||||
}
|
||||
};
|
||||
watch(
|
||||
() => props.playTime,
|
||||
() => {
|
||||
playTimeChange();
|
||||
},
|
||||
);
|
||||
watch(
|
||||
() => startT.value,
|
||||
() => {
|
||||
playTimeChange();
|
||||
},
|
||||
);
|
||||
|
||||
const handleProgress = (event: any, item: any) => {
|
||||
const pos = LineContent.value?.getBoundingClientRect();
|
||||
if (pos && item.endTime) {
|
||||
const dt = event.clientX - pos.x;
|
||||
const start = (dt / pos.width) * 24 * 3600000 + startT.value;
|
||||
const _start = start < item.startTime ? item.startTime : start;
|
||||
onChange(_start, item.endTime, item.deviceId, item.channelId);
|
||||
}
|
||||
};
|
||||
|
||||
defineExpose({ playByStartTime, onNextPlay });
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@import './index.less';
|
||||
</style>
|
|
@ -15,7 +15,13 @@
|
|||
:params="params"
|
||||
>
|
||||
<template #headerTitle>
|
||||
<a-button type="primary" @click="handleAdd"> 新增 </a-button>
|
||||
<PermissionButton
|
||||
type="primary"
|
||||
@click="handleAdd"
|
||||
hasPermission="media/Device:add"
|
||||
>
|
||||
<template #icon><AIcon type="PlusOutlined" />新增</template>
|
||||
</PermissionButton>
|
||||
</template>
|
||||
<template #card="slotProps">
|
||||
<CardBox
|
||||
|
@ -64,65 +70,47 @@
|
|||
</a-row>
|
||||
</template>
|
||||
<template #actions="item">
|
||||
<a-tooltip
|
||||
v-bind="item.tooltip"
|
||||
:title="item.disabled && item.tooltip.title"
|
||||
<PermissionButton
|
||||
:disabled="item.disabled"
|
||||
:popConfirm="item.popConfirm"
|
||||
:tooltip="{
|
||||
...item.tooltip,
|
||||
}"
|
||||
@click="item.onClick"
|
||||
:hasPermission="'media/Device:' + item.key"
|
||||
>
|
||||
<a-popconfirm
|
||||
v-if="item.popConfirm"
|
||||
v-bind="item.popConfirm"
|
||||
:disabled="item.disabled"
|
||||
>
|
||||
<a-button :disabled="item.disabled">
|
||||
<AIcon type="DeleteOutlined" />
|
||||
</a-button>
|
||||
</a-popconfirm>
|
||||
<AIcon
|
||||
type="DeleteOutlined"
|
||||
v-if="item.key === 'delete'"
|
||||
/>
|
||||
<template v-else>
|
||||
<a-button
|
||||
:disabled="item.disabled"
|
||||
@click="item.onClick"
|
||||
>
|
||||
<AIcon :type="item.icon" />
|
||||
<span>{{ item.text }}</span>
|
||||
</a-button>
|
||||
<AIcon :type="item.icon" />
|
||||
<span>{{ item?.text }}</span>
|
||||
</template>
|
||||
</a-tooltip>
|
||||
</PermissionButton>
|
||||
</template>
|
||||
</CardBox>
|
||||
</template>
|
||||
<template #action="slotProps">
|
||||
<a-space :size="16">
|
||||
<a-tooltip
|
||||
<template
|
||||
v-for="i in getActions(slotProps, 'table')"
|
||||
:key="i.key"
|
||||
v-bind="i.tooltip"
|
||||
>
|
||||
<a-popconfirm
|
||||
v-if="i.popConfirm"
|
||||
v-bind="i.popConfirm"
|
||||
<PermissionButton
|
||||
:disabled="i.disabled"
|
||||
>
|
||||
<a-button
|
||||
:disabled="i.disabled"
|
||||
style="padding: 0"
|
||||
type="link"
|
||||
><AIcon :type="i.icon"
|
||||
/></a-button>
|
||||
</a-popconfirm>
|
||||
<a-button
|
||||
style="padding: 0"
|
||||
:popConfirm="i.popConfirm"
|
||||
:tooltip="{
|
||||
...i.tooltip,
|
||||
}"
|
||||
@click="i.onClick"
|
||||
type="link"
|
||||
v-else
|
||||
@click="i.onClick && i.onClick(slotProps)"
|
||||
style="padding: 0px"
|
||||
:hasPermission="'media/Device:' + i.key"
|
||||
>
|
||||
<a-button
|
||||
:disabled="i.disabled"
|
||||
style="padding: 0"
|
||||
type="link"
|
||||
><AIcon :type="i.icon"
|
||||
/></a-button>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
<template #icon><AIcon :type="i.icon" /></template>
|
||||
</PermissionButton>
|
||||
</template>
|
||||
</a-space>
|
||||
</template>
|
||||
</JProTable>
|
||||
|
@ -254,7 +242,7 @@ const getActions = (
|
|||
if (!data) return [];
|
||||
const actions = [
|
||||
{
|
||||
key: 'edit',
|
||||
key: 'update',
|
||||
text: '编辑',
|
||||
tooltip: {
|
||||
title: '编辑',
|
||||
|
@ -278,9 +266,6 @@ const getActions = (
|
|||
},
|
||||
icon: 'PartitionOutlined',
|
||||
onClick: () => {
|
||||
// router.push(
|
||||
// `/media/device/Channel?id=${data.id}&type=${data.provider}`,
|
||||
// );
|
||||
menuStory.jumpPage(
|
||||
'media/Device/Channel',
|
||||
{},
|
||||
|
@ -292,7 +277,7 @@ const getActions = (
|
|||
},
|
||||
},
|
||||
{
|
||||
key: 'debug',
|
||||
key: 'view', // updateChannel
|
||||
text: '更新通道',
|
||||
tooltip: {
|
||||
title:
|
||||
|
@ -311,6 +296,7 @@ const getActions = (
|
|||
icon: 'SyncOutlined',
|
||||
onClick: () => {
|
||||
// updateChannel()
|
||||
console.log('updateChannel: ', data);
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
@ -31,7 +31,7 @@ import StepCard from '@/views/home/components/StepCard.vue';
|
|||
import BasicCountCard from '@/views/media/Home/components/BasicCountCard.vue';
|
||||
|
||||
import { usePermissionStore } from '@/store/permission';
|
||||
import type { bootConfig, recommendList } from '@/views/home/index';
|
||||
import type { bootConfig, recommendList } from '@/views/home/typing';
|
||||
|
||||
// 权限控制
|
||||
const hasPermission = usePermissionStore().hasPermission;
|
||||
|
|
|
@ -408,6 +408,7 @@ const { resetFields, validate, validateInfos, clearValidate } = useForm(
|
|||
);
|
||||
|
||||
const getDetail = async () => {
|
||||
console.log('getDetail', route)
|
||||
if (route.params.id === ':id') return;
|
||||
const res = await configApi.detail(route.params.id as string);
|
||||
// formData.value = res.result;
|
||||
|
|
|
@ -17,16 +17,22 @@
|
|||
>
|
||||
<template #headerTitle>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="handleAdd">
|
||||
<PermissionButton
|
||||
type="primary"
|
||||
@click="handleAdd"
|
||||
hasPermission="notice/Config:add"
|
||||
>
|
||||
新增
|
||||
</a-button>
|
||||
</PermissionButton>
|
||||
<a-upload
|
||||
name="file"
|
||||
accept="json"
|
||||
:showUploadList="false"
|
||||
:before-upload="beforeUpload"
|
||||
>
|
||||
<a-button>导入</a-button>
|
||||
<PermissionButton hasPermission="notice/Config:import">
|
||||
导入
|
||||
</PermissionButton>
|
||||
</a-upload>
|
||||
<a-popconfirm
|
||||
title="确认导出?"
|
||||
|
@ -34,7 +40,9 @@
|
|||
cancel-text="取消"
|
||||
@confirm="handleExport"
|
||||
>
|
||||
<a-button>导出</a-button>
|
||||
<PermissionButton hasPermission="notice/Config:export">
|
||||
导出
|
||||
</PermissionButton>
|
||||
</a-popconfirm>
|
||||
</a-space>
|
||||
</template>
|
||||
|
@ -95,34 +103,45 @@
|
|||
v-for="(o, i) in item.children"
|
||||
:key="i"
|
||||
>
|
||||
<a-button
|
||||
<PermissionButton
|
||||
type="link"
|
||||
@click="o.onClick"
|
||||
:hasPermission="`notice/Config:${o.key}`"
|
||||
>
|
||||
<AIcon :type="o.icon" />
|
||||
<template #icon>
|
||||
<AIcon :type="o.icon" />
|
||||
</template>
|
||||
<span>{{ o.text }}</span>
|
||||
</a-button>
|
||||
</PermissionButton>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
<a-popconfirm
|
||||
<j-popconfirm
|
||||
v-else-if="item.key === 'delete'"
|
||||
v-bind="item.popConfirm"
|
||||
:disabled="item.disabled"
|
||||
>
|
||||
<a-button :disabled="item.disabled">
|
||||
<AIcon type="DeleteOutlined" />
|
||||
</a-button>
|
||||
</a-popconfirm>
|
||||
<PermissionButton
|
||||
:disabled="item.disabled"
|
||||
:hasPermission="`notice/Config:${item.key}`"
|
||||
>
|
||||
<template #icon>
|
||||
<AIcon type="DeleteOutlined" />
|
||||
</template>
|
||||
</PermissionButton>
|
||||
</j-popconfirm>
|
||||
<template v-else>
|
||||
<a-button
|
||||
<PermissionButton
|
||||
:disabled="item.disabled"
|
||||
@click="item.onClick"
|
||||
:hasPermission="`notice/Config:${item.key}`"
|
||||
>
|
||||
<AIcon :type="item.icon" />
|
||||
<template #icon>
|
||||
<AIcon :type="item.icon" />
|
||||
</template>
|
||||
<span>{{ item.text }}</span>
|
||||
</a-button>
|
||||
</PermissionButton>
|
||||
</template>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
|
@ -130,37 +149,24 @@
|
|||
</template>
|
||||
<template #action="slotProps">
|
||||
<a-space :size="16">
|
||||
<a-tooltip
|
||||
<template
|
||||
v-for="i in getActions(slotProps, 'table')"
|
||||
:key="i.key"
|
||||
v-bind="i.tooltip"
|
||||
>
|
||||
<a-popconfirm
|
||||
v-if="i.popConfirm"
|
||||
v-bind="i.popConfirm"
|
||||
<PermissionButton
|
||||
:disabled="i.disabled"
|
||||
>
|
||||
<a-button
|
||||
:disabled="i.disabled"
|
||||
style="padding: 0"
|
||||
type="link"
|
||||
><AIcon :type="i.icon"
|
||||
/></a-button>
|
||||
</a-popconfirm>
|
||||
<a-button
|
||||
style="padding: 0"
|
||||
:popConfirm="i.popConfirm"
|
||||
:tooltip="{
|
||||
...i.tooltip,
|
||||
}"
|
||||
@click="i.onClick"
|
||||
type="link"
|
||||
v-else
|
||||
@click="i.onClick && i.onClick(slotProps)"
|
||||
style="padding: 0px"
|
||||
:hasPermission="'notice/Config:' + i.key"
|
||||
>
|
||||
<a-button
|
||||
:disabled="i.disabled"
|
||||
style="padding: 0"
|
||||
type="link"
|
||||
><AIcon :type="i.icon"
|
||||
/></a-button>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
<template #icon><AIcon :type="i.icon" /></template>
|
||||
</PermissionButton>
|
||||
</template>
|
||||
</a-space>
|
||||
</template>
|
||||
</JProTable>
|
||||
|
@ -324,7 +330,7 @@ const getActions = (
|
|||
if (!data) return [];
|
||||
const actions = [
|
||||
{
|
||||
key: 'edit',
|
||||
key: 'update',
|
||||
text: '编辑',
|
||||
tooltip: {
|
||||
title: '编辑',
|
||||
|
@ -349,7 +355,7 @@ const getActions = (
|
|||
},
|
||||
},
|
||||
{
|
||||
key: 'debug',
|
||||
key: 'log',
|
||||
text: '通知记录',
|
||||
tooltip: {
|
||||
title: '通知记录',
|
||||
|
@ -385,7 +391,7 @@ const getActions = (
|
|||
icon: 'EllipsisOutlined',
|
||||
children: [
|
||||
{
|
||||
key: 'debug',
|
||||
key: 'export',
|
||||
text: '导出',
|
||||
tooltip: {
|
||||
title: '导出',
|
||||
|
@ -396,7 +402,7 @@ const getActions = (
|
|||
},
|
||||
},
|
||||
{
|
||||
key: 'sync',
|
||||
key: 'bind',
|
||||
text: '同步用户',
|
||||
tooltip: {
|
||||
title: '同步用户',
|
||||
|
|
|
@ -17,16 +17,24 @@
|
|||
>
|
||||
<template #headerTitle>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="handleAdd">
|
||||
<PermissionButton
|
||||
type="primary"
|
||||
@click="handleAdd"
|
||||
hasPermission="notice/Template:add"
|
||||
>
|
||||
新增
|
||||
</a-button>
|
||||
</PermissionButton>
|
||||
<a-upload
|
||||
name="file"
|
||||
accept="json"
|
||||
:showUploadList="false"
|
||||
:before-upload="beforeUpload"
|
||||
>
|
||||
<a-button>导入</a-button>
|
||||
<PermissionButton
|
||||
hasPermission="notice/Template:import"
|
||||
>
|
||||
导入
|
||||
</PermissionButton>
|
||||
</a-upload>
|
||||
<a-popconfirm
|
||||
title="确认导出?"
|
||||
|
@ -34,7 +42,11 @@
|
|||
cancel-text="取消"
|
||||
@confirm="handleExport"
|
||||
>
|
||||
<a-button>导出</a-button>
|
||||
<PermissionButton
|
||||
hasPermission="notice/Template:export"
|
||||
>
|
||||
导出
|
||||
</PermissionButton>
|
||||
</a-popconfirm>
|
||||
</a-space>
|
||||
</template>
|
||||
|
@ -77,42 +89,24 @@
|
|||
</a-row>
|
||||
</template>
|
||||
<template #actions="item">
|
||||
<a-tooltip
|
||||
v-bind="item.tooltip"
|
||||
:title="item.disabled && item.tooltip.title"
|
||||
<PermissionButton
|
||||
:disabled="item.disabled"
|
||||
:popConfirm="item.popConfirm"
|
||||
:tooltip="{
|
||||
...item.tooltip,
|
||||
}"
|
||||
@click="item.onClick"
|
||||
:hasPermission="'notice/Template:' + item.key"
|
||||
>
|
||||
<a-popconfirm
|
||||
v-if="item.popConfirm"
|
||||
v-bind="item.popConfirm"
|
||||
:disabled="item.disabled"
|
||||
>
|
||||
<a-button :disabled="item.disabled">
|
||||
<AIcon
|
||||
type="DeleteOutlined"
|
||||
v-if="item.key === 'delete'"
|
||||
/>
|
||||
<template v-else>
|
||||
<AIcon :type="item.icon" />
|
||||
<span>{{ item.text }}</span>
|
||||
</template>
|
||||
</a-button>
|
||||
</a-popconfirm>
|
||||
<AIcon
|
||||
type="DeleteOutlined"
|
||||
v-if="item.key === 'delete'"
|
||||
/>
|
||||
<template v-else>
|
||||
<a-button
|
||||
:disabled="item.disabled"
|
||||
@click="item.onClick"
|
||||
>
|
||||
<AIcon
|
||||
type="DeleteOutlined"
|
||||
v-if="item.key === 'delete'"
|
||||
/>
|
||||
<template v-else>
|
||||
<AIcon :type="item.icon" />
|
||||
<span>{{ item.text }}</span>
|
||||
</template>
|
||||
</a-button>
|
||||
<AIcon :type="item.icon" />
|
||||
<span>{{ item?.text }}</span>
|
||||
</template>
|
||||
</a-tooltip>
|
||||
</PermissionButton>
|
||||
</template>
|
||||
</CardBox>
|
||||
</template>
|
||||
|
@ -126,37 +120,24 @@
|
|||
</template>
|
||||
<template #action="slotProps">
|
||||
<a-space :size="16">
|
||||
<a-tooltip
|
||||
<template
|
||||
v-for="i in getActions(slotProps, 'table')"
|
||||
:key="i.key"
|
||||
v-bind="i.tooltip"
|
||||
>
|
||||
<a-popconfirm
|
||||
v-if="i.popConfirm"
|
||||
v-bind="i.popConfirm"
|
||||
<PermissionButton
|
||||
:disabled="i.disabled"
|
||||
>
|
||||
<a-button
|
||||
:disabled="i.disabled"
|
||||
style="padding: 0"
|
||||
type="link"
|
||||
><AIcon :type="i.icon"
|
||||
/></a-button>
|
||||
</a-popconfirm>
|
||||
<a-button
|
||||
style="padding: 0"
|
||||
:popConfirm="i.popConfirm"
|
||||
:tooltip="{
|
||||
...i.tooltip,
|
||||
}"
|
||||
@click="i.onClick"
|
||||
type="link"
|
||||
v-else
|
||||
@click="i.onClick && i.onClick(slotProps)"
|
||||
style="padding: 0px"
|
||||
:hasPermission="'notice/Template:' + i.key"
|
||||
>
|
||||
<a-button
|
||||
:disabled="i.disabled"
|
||||
style="padding: 0"
|
||||
type="link"
|
||||
><AIcon :type="i.icon"
|
||||
/></a-button>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
<template #icon><AIcon :type="i.icon" /></template>
|
||||
</PermissionButton>
|
||||
</template>
|
||||
</a-space>
|
||||
</template>
|
||||
</JProTable>
|
||||
|
@ -286,7 +267,7 @@ const handleAdd = () => {
|
|||
* 导入
|
||||
*/
|
||||
const beforeUpload = (file: any) => {
|
||||
console.log('file: ', file);
|
||||
// console.log('file: ', file);
|
||||
const reader = new FileReader();
|
||||
reader.readAsText(file);
|
||||
reader.onload = async (result) => {
|
||||
|
@ -330,7 +311,7 @@ const getActions = (
|
|||
if (!data) return [];
|
||||
const actions = [
|
||||
{
|
||||
key: 'edit',
|
||||
key: 'update',
|
||||
text: '编辑',
|
||||
tooltip: {
|
||||
title: '编辑',
|
||||
|
@ -355,7 +336,7 @@ const getActions = (
|
|||
},
|
||||
},
|
||||
{
|
||||
key: 'debug',
|
||||
key: 'export',
|
||||
text: '导出',
|
||||
tooltip: {
|
||||
title: '导出',
|
||||
|
@ -366,7 +347,7 @@ const getActions = (
|
|||
},
|
||||
},
|
||||
{
|
||||
key: 'debug',
|
||||
key: 'log',
|
||||
text: '通知记录',
|
||||
tooltip: {
|
||||
title: '通知记录',
|
||||
|
|
|
@ -21,15 +21,16 @@
|
|||
"
|
||||
/>
|
||||
</a-tooltip>
|
||||
<div
|
||||
style="
|
||||
margin: 0 0px 0 4px;
|
||||
color: #1d39c4;
|
||||
cursor: pointer;
|
||||
"
|
||||
|
||||
<PermissionButton
|
||||
type="link"
|
||||
@click="showOutput"
|
||||
hasPermission="device/Instance:update"
|
||||
>
|
||||
<edit-outlined @click="showOutput"/>
|
||||
</div>
|
||||
<template #icon
|
||||
><AIcon type="EditOutlined"
|
||||
/></template>
|
||||
</PermissionButton>
|
||||
</div>
|
||||
</template>
|
||||
<a-descriptions
|
||||
|
@ -82,15 +83,14 @@
|
|||
"
|
||||
/>
|
||||
</a-tooltip>
|
||||
<div
|
||||
style="
|
||||
margin: 0 0px 0 4px;
|
||||
color: #1d39c4;
|
||||
cursor: pointer;
|
||||
"
|
||||
<PermissionButton
|
||||
type="link"
|
||||
@click="showInput"
|
||||
hasPermission="device/Instance:update"
|
||||
>
|
||||
<edit-outlined @click="showInput"/>
|
||||
</div>
|
||||
<template #icon
|
||||
><AIcon type="EditOutlined" /></template
|
||||
></PermissionButton>
|
||||
</div>
|
||||
</template>
|
||||
<a-descriptions
|
||||
|
@ -168,14 +168,24 @@
|
|||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<InputSave :data="input" v-if="inputVisible" @closeModel="closeInput" @saveSuc="saveInput"/>
|
||||
<OutputSave :data="output" v-if="outputVisible" @closeModel="closeOutput" @saveSuc="saveOutput"/>
|
||||
<InputSave
|
||||
:data="input"
|
||||
v-if="inputVisible"
|
||||
@closeModel="closeInput"
|
||||
@saveSuc="saveInput"
|
||||
/>
|
||||
<OutputSave
|
||||
:data="output"
|
||||
v-if="outputVisible"
|
||||
@closeModel="closeOutput"
|
||||
@saveSuc="saveOutput"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import InputSave from './Save/input.vue'
|
||||
import OutputSave from './save/output.vue'
|
||||
import InputSave from './Save/input.vue';
|
||||
import OutputSave from './save/output.vue';
|
||||
import {
|
||||
EditOutlined,
|
||||
DeleteOutlined,
|
||||
|
@ -439,24 +449,24 @@ handleInputSearch();
|
|||
handleOutputSearch();
|
||||
const showInput = () => {
|
||||
inputVisible.value = true;
|
||||
}
|
||||
const closeInput = () =>{
|
||||
};
|
||||
const closeInput = () => {
|
||||
inputVisible.value = false;
|
||||
}
|
||||
const saveInput = () =>{
|
||||
};
|
||||
const saveInput = () => {
|
||||
inputVisible.value = false;
|
||||
handleInputSearch();
|
||||
}
|
||||
const showOutput = () =>{
|
||||
};
|
||||
const showOutput = () => {
|
||||
outputVisible.value = true;
|
||||
}
|
||||
const closeOutput = () =>{
|
||||
};
|
||||
const closeOutput = () => {
|
||||
outputVisible.value = false;
|
||||
}
|
||||
const saveOutput = () =>{
|
||||
};
|
||||
const saveOutput = () => {
|
||||
outputVisible.value = false;
|
||||
handleOutputSearch();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.alarmTitle {
|
||||
|
|
|
@ -35,12 +35,13 @@
|
|||
</div>
|
||||
</div>
|
||||
</a-card>
|
||||
<a-button
|
||||
<!-- <a-button
|
||||
type="primary"
|
||||
size="middle"
|
||||
@click="handleSaveLevel"
|
||||
>保存</a-button
|
||||
>
|
||||
> -->
|
||||
<PermissionButton type="primary" size="middle" @click="handleSaveLevel" hasPermission="rule-engine/Alarm/Config:update">保存</PermissionButton>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="10">
|
||||
|
|
|
@ -46,9 +46,7 @@
|
|||
<a-form-item label="说明" name="description">
|
||||
<a-textarea v-model:value="form.description"></a-textarea>
|
||||
</a-form-item>
|
||||
<a-button type="primary" @click="handleSave" :loading="loading"
|
||||
>保存</a-button
|
||||
>
|
||||
<PermissionButton type="primary" @click="handleSave" :hasPermission="['rule-engine/Alarm/Configuration:add','rule-engine/Alarm/Configuration:update']">保存</PermissionButton>
|
||||
</a-form>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -63,6 +61,7 @@ import { useMenuStore } from '@/store/menu';
|
|||
import { useRoute } from 'vue-router';
|
||||
import { useAlarmConfigurationStore } from '@/store/alarm';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { usePermissionStore } from '@/store/permission';
|
||||
const route = useRoute();
|
||||
const id = route.query?.id;
|
||||
let selectDisable = ref(false);
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
>
|
||||
<template #headerTitle>
|
||||
<a-space>
|
||||
<PermissionButton type="primary" @click="showModal">
|
||||
<PermissionButton type="primary" @click="showModal" hasPermission="rule-engine/Alarm/Configuration:add">
|
||||
<template #icon><AIcon type="PlusOutlined" /></template>
|
||||
新增
|
||||
</PermissionButton>
|
||||
|
@ -64,6 +64,7 @@
|
|||
...item.tooltip,
|
||||
}"
|
||||
@click="item.onClick"
|
||||
:hasPermission="'rule-engine/Alarm/Configuration:'+item.key"
|
||||
>
|
||||
<AIcon :type="item.icon" />
|
||||
<span>{{ item?.text }}</span>
|
||||
|
@ -137,7 +138,7 @@ const getActions = (
|
|||
if (!data) return [];
|
||||
const actions: ActionsType[] = [
|
||||
{
|
||||
key: 'unbind',
|
||||
key: 'action',
|
||||
text: '解绑',
|
||||
icon: 'DisconnectOutlined',
|
||||
popConfirm: {
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
:columns="columns"
|
||||
:request="queryList"
|
||||
:gridColumn="3"
|
||||
:gridColumns="[1,2,3]"
|
||||
:gridColumns="[1, 2, 3]"
|
||||
ref="tableRef"
|
||||
:defaultParams="{
|
||||
sorts: [{ name: 'createTime', order: 'desc' }],
|
||||
|
@ -18,11 +18,18 @@
|
|||
:params="params"
|
||||
>
|
||||
<template #headerTitle>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="add"
|
||||
><plus-outlined />新增</a-button
|
||||
<j-space>
|
||||
<PermissionButton
|
||||
type="primary"
|
||||
@click="add"
|
||||
hasPermission="device/Instance:add"
|
||||
>
|
||||
</a-space>
|
||||
<template #icon
|
||||
><AIcon type="PlusOutlined"
|
||||
/></template>
|
||||
新增
|
||||
</PermissionButton>
|
||||
</j-space>
|
||||
</template>
|
||||
<template #card="slotProps">
|
||||
<CardBox
|
||||
|
@ -74,13 +81,17 @@
|
|||
<template #actions="item">
|
||||
<PermissionButton
|
||||
v-if="
|
||||
item.key != 'trigger' ||
|
||||
item.key != 'tigger' ||
|
||||
slotProps.sceneTriggerType == 'manual'
|
||||
"
|
||||
:disabled="item.disabled"
|
||||
:popConfirm="item.popConfirm"
|
||||
:tooltip="{ ...item.tootip }"
|
||||
@click="item.onClick"
|
||||
:hasPermission="
|
||||
'rule-engine/Alarm/Configuration:' +
|
||||
item.key
|
||||
"
|
||||
>
|
||||
<AIcon
|
||||
type="DeleteOutlined"
|
||||
|
@ -136,7 +147,7 @@
|
|||
>
|
||||
<PermissionButton
|
||||
v-if="
|
||||
i.key != 'trigger' ||
|
||||
i.key != 'tigger' ||
|
||||
slotProps.sceneTriggerType == 'manual'
|
||||
"
|
||||
:disabled="i.disabled"
|
||||
|
@ -147,6 +158,10 @@
|
|||
@click="i.onClick"
|
||||
type="link"
|
||||
style="padding: 0px"
|
||||
:hasPermission="
|
||||
'rule-engine/Alarm/Configuration:' +
|
||||
item.key
|
||||
"
|
||||
>
|
||||
<template #icon
|
||||
><AIcon :type="i.icon"
|
||||
|
@ -161,7 +176,6 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import JTable from '@/components/Table';
|
||||
import {
|
||||
queryList,
|
||||
_enable,
|
||||
|
@ -323,7 +337,7 @@ const getActions = (
|
|||
}
|
||||
const actions = [
|
||||
{
|
||||
key: 'trigger',
|
||||
key: 'tigger',
|
||||
text: '手动触发',
|
||||
disabled: data?.state?.value === 'disabled',
|
||||
tooltip: {
|
||||
|
@ -353,7 +367,7 @@ const getActions = (
|
|||
icon: 'LikeOutlined',
|
||||
},
|
||||
{
|
||||
key: 'edit',
|
||||
key: 'update',
|
||||
text: '编辑',
|
||||
tooltip: {
|
||||
title: '编辑',
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
target="alarm-log-detail"
|
||||
@search="handleSearch"
|
||||
></Search>
|
||||
<JTable
|
||||
<JProTable
|
||||
:columns="columns"
|
||||
model="TABLE"
|
||||
:request="queryList"
|
||||
|
@ -39,7 +39,7 @@
|
|||
</template>
|
||||
</a-space>
|
||||
</template>
|
||||
</JTable>
|
||||
</JProTable>
|
||||
<Info v-if="visiable" :data="current" @close="close"/>
|
||||
</page-container>
|
||||
</template>
|
||||
|
@ -128,7 +128,7 @@ const queryList = async (params: any) => {
|
|||
// sorts: [{ name: 'alarmTime', order: 'desc' }],
|
||||
});
|
||||
if (res.status === 200) {
|
||||
|
||||
details.value = res.result.data[0];
|
||||
return {
|
||||
code: res.message,
|
||||
result: {
|
||||
|
@ -178,6 +178,13 @@ const handleSearch = (_params: any) => {
|
|||
const close = () => {
|
||||
visiable.value = false
|
||||
}
|
||||
|
||||
watchEffect(()=>{
|
||||
current.value = details.value;
|
||||
if(route.query.detail && details.value){
|
||||
visiable.value = true;
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
</style>
|
|
@ -13,7 +13,7 @@
|
|||
target="bind-channel"
|
||||
@search="handleSearch"
|
||||
></Search>
|
||||
<JTable
|
||||
<JProTable
|
||||
model="TABLE"
|
||||
:columns="columns"
|
||||
:defaultParams="{
|
||||
|
@ -47,7 +47,7 @@
|
|||
}}
|
||||
</span>
|
||||
</template>
|
||||
</JTable>
|
||||
</JProTable>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
:columns="columns"
|
||||
:request="handleSearch"
|
||||
:params="params"
|
||||
:gridColumns="[1,1,2]"
|
||||
:gridColumns="[1, 1, 2]"
|
||||
:gridColumn="2"
|
||||
model="CARD"
|
||||
>
|
||||
|
@ -98,16 +98,23 @@
|
|||
</span>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
</template>
|
||||
<template #actions="item">
|
||||
<PermissionButton
|
||||
:disabled="item.key === 'solve' && slotProps.state.value ==='normal'"
|
||||
:disabled="
|
||||
item.key === 'solve' &&
|
||||
slotProps.state.value === 'normal'
|
||||
"
|
||||
:popConfirm="item.popConfirm"
|
||||
:tooltip="{
|
||||
...item.tooltip,
|
||||
}"
|
||||
@click="item.onClick"
|
||||
:hasPermission="
|
||||
item.key == 'solve'
|
||||
? 'rule-engine/Alarm/Log:action'
|
||||
: 'rule-engine/Alarm/Log:view'
|
||||
"
|
||||
>
|
||||
<AIcon :type="item.icon" />
|
||||
<span>{{ item?.text }}</span>
|
||||
|
@ -116,8 +123,16 @@
|
|||
</CardBox>
|
||||
</template>
|
||||
</JProTable>
|
||||
<SolveComponent :data="data" v-if="data.solveVisible" @closeSolve="closeSolve"/>
|
||||
<SolveLog :data="data.current" v-if="data.logVisible" @closeLog="closeLog"/>
|
||||
<SolveComponent
|
||||
:data="data"
|
||||
v-if="data.solveVisible"
|
||||
@closeSolve="closeSolve"
|
||||
/>
|
||||
<SolveLog
|
||||
:data="data.current"
|
||||
v-if="data.logVisible"
|
||||
@closeLog="closeLog"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -139,6 +154,7 @@ import type { ActionsType } from '@/components/Table';
|
|||
import SolveComponent from '../SolveComponent/index.vue';
|
||||
import SolveLog from '../SolveLog/index.vue'
|
||||
import { useMenuStore } from '@/store/menu';
|
||||
import { usePermissionStore } from '@/store/permission';
|
||||
const menuStory = useMenuStore();
|
||||
|
||||
const alarmStore = useAlarmStore();
|
||||
|
@ -368,6 +384,13 @@ const getActions = (
|
|||
onClick: () =>{
|
||||
data.value.current = currentData;
|
||||
data.value.solveVisible = true;
|
||||
},
|
||||
popConfirm:{
|
||||
title: !usePermissionStore().hasPermission('rule-engine/Alarm/Log:action')
|
||||
? '暂无权限,请联系管理员'
|
||||
: data.state?.value === 'normal'
|
||||
? '无告警'
|
||||
: ''
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
:title="item.alarmName"
|
||||
placement="topLeft"
|
||||
>
|
||||
<a>{{ item.alarmName }}</a>
|
||||
<a @click="()=>{return jumpDetail(item)}">{{ item.alarmName }}</a>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
<div class="new-alarm-item-state">
|
||||
|
@ -63,6 +63,7 @@
|
|||
<script lang="ts" setup>
|
||||
import { Empty } from 'ant-design-vue';
|
||||
import { getImage } from '@/utils/comm';
|
||||
import { useMenuStore } from '@/store/menu';
|
||||
import moment from 'moment';
|
||||
const props = defineProps({
|
||||
alarmList: {
|
||||
|
@ -70,6 +71,10 @@ const props = defineProps({
|
|||
default: [],
|
||||
},
|
||||
});
|
||||
const menuStore = useMenuStore();
|
||||
const jumpDetail = (item:any) =>{
|
||||
menuStore.jumpPage(`rule-engine/Alarm/Log/Detail`,{id:item.id},{detail:true});
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
.new-alarm {
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
:maskClosable="false"
|
||||
width="650px"
|
||||
destroyOnClose
|
||||
v-model:visible="visible"
|
||||
:title="props.title"
|
||||
visible
|
||||
:title="props.data?.id ? '编辑' : '新增'"
|
||||
@ok="handleSave"
|
||||
@cancel="handleCancel"
|
||||
okText="确定"
|
||||
|
@ -39,31 +39,23 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { saveRule , modify } from '@/api/rule-engine/instance';
|
||||
import { saveRule, modify } from '@/api/rule-engine/instance';
|
||||
import { getImage } from '@/utils/comm';
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
const emit = defineEmits(['success', 'closeSave']);
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: '',
|
||||
data: {
|
||||
type: Object,
|
||||
},
|
||||
isAdd: {
|
||||
type: Number,
|
||||
default: '',
|
||||
},
|
||||
});
|
||||
const productList = ref<Record<string, any>[]>([]);
|
||||
const loading = ref<boolean>(false);
|
||||
const visible = ref<boolean>(false);
|
||||
const formRef = ref();
|
||||
let id = ref<string>();
|
||||
const modelRef = reactive({
|
||||
name: '',
|
||||
description: '',
|
||||
});
|
||||
|
||||
const productList = ref<Record<string, any>[]>([]);
|
||||
const loading = ref<boolean>(false);
|
||||
const formRef = ref();
|
||||
let id = ref<string>();
|
||||
const modelRef = ref();
|
||||
modelRef.value = props.data
|
||||
const rules = {
|
||||
name: [
|
||||
{
|
||||
|
@ -76,15 +68,8 @@ const rules = {
|
|||
},
|
||||
],
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.isAdd,
|
||||
() => {},
|
||||
{ immediate: true, deep: true },
|
||||
);
|
||||
|
||||
const handleCancel = () => {
|
||||
visible.value = false;
|
||||
emit('closeSave');
|
||||
};
|
||||
|
||||
const handleSave = () => {
|
||||
|
@ -92,26 +77,24 @@ const handleSave = () => {
|
|||
.validate()
|
||||
.then(async () => {
|
||||
loading.value = true;
|
||||
if (props.isAdd === 1) {
|
||||
let resp = await saveRule(modelRef);
|
||||
if (!modelRef.value?.id) {
|
||||
let resp = await saveRule(modelRef.value);
|
||||
loading.value = false;
|
||||
if (resp.status === 200) {
|
||||
message.success('操作成功!');
|
||||
emit('closeSave');
|
||||
emit('success');
|
||||
formRef.value.resetFields();
|
||||
visible.value = false;
|
||||
}else{
|
||||
message.error('操作失败')
|
||||
} else {
|
||||
message.error('操作失败');
|
||||
}
|
||||
}else if(props.isAdd === 2) {
|
||||
let resp = await modify(id,modelRef);
|
||||
} else {
|
||||
let resp = await modify(modelRef.value?.id, modelRef.value);
|
||||
loading.value = false;
|
||||
if (resp.status === 200) {
|
||||
message.success('操作成功!');
|
||||
emit('closeSave');
|
||||
emit('success');
|
||||
formRef.value.resetFields();
|
||||
visible.value = false;
|
||||
}else{
|
||||
} else {
|
||||
message.error('操作失败!');
|
||||
}
|
||||
}
|
||||
|
@ -120,19 +103,4 @@ const handleSave = () => {
|
|||
console.log('error', err);
|
||||
});
|
||||
};
|
||||
|
||||
const show = (data: any) => {
|
||||
if (props.isAdd === 1) {
|
||||
modelRef.name = '';
|
||||
modelRef.description = '';
|
||||
} else if (props.isAdd === 2) {
|
||||
modelRef.name = data?.name;
|
||||
modelRef.description = data?.description;
|
||||
id = data.id
|
||||
}
|
||||
visible.value = true;
|
||||
};
|
||||
defineExpose({
|
||||
show: show,
|
||||
});
|
||||
</script>
|
|
@ -6,7 +6,7 @@
|
|||
target="device-instance"
|
||||
@search="handleSearch"
|
||||
></Search>
|
||||
<JTable
|
||||
<JProTable
|
||||
:columns="columns"
|
||||
:request="queryList"
|
||||
ref="tableRef"
|
||||
|
@ -16,11 +16,18 @@
|
|||
:params="params"
|
||||
>
|
||||
<template #headerTitle>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="add"
|
||||
><plus-outlined />新增</a-button
|
||||
<j-space>
|
||||
<PermissionButton
|
||||
type="primary"
|
||||
@click="add"
|
||||
hasPermission="rule-engine/Instance:add"
|
||||
>
|
||||
</a-space>
|
||||
<template #icon
|
||||
><AIcon type="PlusOutlined"
|
||||
/></template>
|
||||
新增
|
||||
</PermissionButton>
|
||||
</j-space>
|
||||
</template>
|
||||
<template #card="slotProps">
|
||||
<CardBox
|
||||
|
@ -29,6 +36,7 @@
|
|||
v-bind="slotProps"
|
||||
:status="slotProps.state?.value"
|
||||
:statusText="slotProps.state?.text"
|
||||
@click="openRuleEditor"
|
||||
:statusNames="{
|
||||
started: 'success',
|
||||
disable: 'error',
|
||||
|
@ -62,6 +70,9 @@
|
|||
:tooltip="{
|
||||
...item.tooltip,
|
||||
}"
|
||||
:hasPermission="
|
||||
'rule-engine/Instance:' + item.key
|
||||
"
|
||||
@click="item.onClick"
|
||||
>
|
||||
<AIcon
|
||||
|
@ -113,20 +124,19 @@
|
|||
</template>
|
||||
</a-space>
|
||||
</template>
|
||||
</JTable>
|
||||
</JProTable>
|
||||
<!-- 新增、编辑 -->
|
||||
<Save
|
||||
ref="saveRef"
|
||||
:isAdd="isAdd"
|
||||
:title="title"
|
||||
:data="current"
|
||||
@success="refresh"
|
||||
v-if="visiable"
|
||||
@close-save="closeSave"
|
||||
/>
|
||||
</div>
|
||||
</page-container>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import JTable from '@/components/Table';
|
||||
import type { InstanceItem } from './typings';
|
||||
import {
|
||||
queryList,
|
||||
|
@ -138,11 +148,9 @@ import type { ActionsType } from '@/components/Table/index.vue';
|
|||
import { getImage } from '@/utils/comm';
|
||||
import { message } from 'ant-design-vue';
|
||||
import Save from './Save/index.vue';
|
||||
import { SystemConst } from '@/utils/consts';
|
||||
const params = ref<Record<string, any>>({});
|
||||
let isAdd = ref<number>(0);
|
||||
let title = ref<string>('');
|
||||
let saveRef = ref();
|
||||
let currentForm = ref();
|
||||
let visiable = ref(false);
|
||||
const tableRef = ref<Record<string, any>>({});
|
||||
const query = {
|
||||
columns: [
|
||||
|
@ -207,6 +215,7 @@ const columns = [
|
|||
scopedSlots: true,
|
||||
},
|
||||
];
|
||||
const current = ref();
|
||||
const getActions = (
|
||||
data: Partial<Record<string, any>>,
|
||||
type?: 'card' | 'table',
|
||||
|
@ -216,7 +225,7 @@ const getActions = (
|
|||
}
|
||||
const actions = [
|
||||
{
|
||||
key: 'edit',
|
||||
key: 'update',
|
||||
text: '编辑',
|
||||
tooltip: {
|
||||
title: '编辑',
|
||||
|
@ -224,11 +233,8 @@ const getActions = (
|
|||
|
||||
icon: 'EditOutlined',
|
||||
onClick: () => {
|
||||
title.value = '编辑';
|
||||
isAdd.value = 2;
|
||||
nextTick(() => {
|
||||
saveRef.value.show(data);
|
||||
});
|
||||
current.value = data;
|
||||
visiable.value = true;
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -297,11 +303,11 @@ const getActions = (
|
|||
return actions;
|
||||
};
|
||||
const add = () => {
|
||||
isAdd.value = 1;
|
||||
title.value = '新增';
|
||||
nextTick(() => {
|
||||
saveRef.value.show(currentForm.value);
|
||||
});
|
||||
current.value = {
|
||||
name:'',
|
||||
description:''
|
||||
},
|
||||
visiable.value = true
|
||||
};
|
||||
/**
|
||||
* 刷新数据
|
||||
|
@ -312,6 +318,14 @@ const refresh = () => {
|
|||
const handleSearch = (e: any) => {
|
||||
params.value = e;
|
||||
};
|
||||
const openRuleEditor = (item: any) => {
|
||||
window.open(
|
||||
`/${SystemConst.API_BASE}/rule-editor/index.html#flow/${item.id}`,
|
||||
);
|
||||
};
|
||||
const closeSave = () =>{
|
||||
visiable.value = false;
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
</style>
|
|
@ -53,9 +53,9 @@
|
|||
|
||||
<script setup lang='ts' name='AddModel'>
|
||||
import type { PropType } from 'vue'
|
||||
import type { metadataType, TriggerDevice, TriggerDeviceOptions } from '@/views/rule-engine/Scene/typings'
|
||||
import type { metadataType, TriggerDevice, TriggerDeviceOptions, SelectorValuesItem } from '@/views/rule-engine/Scene/typings'
|
||||
import { onlyMessage } from '@/utils/comm'
|
||||
import { detail as deviceDetail } from '@/api/device/instance'
|
||||
import { detail as deviceDetail } from '@/api/device/instance'
|
||||
import Product from './Product.vue'
|
||||
import DeviceSelect from './DeviceSelect.vue'
|
||||
import Type from './Type.vue'
|
||||
|
@ -66,12 +66,13 @@ type Emit = {
|
|||
(e: 'save', data: TriggerDevice, options: Record<string, any>): void
|
||||
}
|
||||
|
||||
|
||||
interface AddModelType extends Omit<TriggerDevice, 'selectorValues'> {
|
||||
stepNumber: number
|
||||
deviceKeys: Array<{ label: string, value: string }>
|
||||
orgId: Array<{ label: string, value: string }>
|
||||
deviceKeys: SelectorValuesItem[]
|
||||
orgId: SelectorValuesItem[]
|
||||
productDetail: any
|
||||
selectorValues: Array<Record<string, any>>
|
||||
selectorValues: SelectorValuesItem[]
|
||||
metadata: metadataType,
|
||||
operator: TriggerDeviceOptions
|
||||
}
|
||||
|
@ -95,23 +96,21 @@ const props = defineProps({
|
|||
})
|
||||
|
||||
const addModel = reactive<AddModelType>({
|
||||
productId: '',
|
||||
selector: 'fixed',
|
||||
selectorValues: [],
|
||||
productId: props.value.productId || '',
|
||||
selector: props.value.selector || 'fixed',
|
||||
selectorValues: props.value.selectorValues || [],
|
||||
stepNumber: 0,
|
||||
deviceKeys: [],
|
||||
orgId: [],
|
||||
deviceKeys: props.value.selectorValues || [],
|
||||
orgId: props.value.selectorValues || [],
|
||||
productDetail: {},
|
||||
metadata: {},
|
||||
operator: {
|
||||
operator: props.value.operation || {
|
||||
operator: 'online'
|
||||
}
|
||||
})
|
||||
|
||||
const optionsCache = ref(props.options)
|
||||
|
||||
Object.assign(addModel, props.value)
|
||||
|
||||
const handleOptions = (data: TriggerDeviceOptions) => {
|
||||
const typeIconMap = {
|
||||
writeProperty: 'icon-bianji1',
|
||||
|
@ -230,6 +229,11 @@ const productChange = () => {
|
|||
addModel.selectorValues = []
|
||||
}
|
||||
|
||||
const getDeviceDetailByMetadata = async (deviceId: string) => {
|
||||
const resp = await deviceDetail(deviceId)
|
||||
return resp.result?.metadata
|
||||
}
|
||||
|
||||
const save = async (step?: number) => {
|
||||
let _step = step !== undefined ? step : addModel.stepNumber
|
||||
if (_step === 0) {
|
||||
|
@ -240,12 +244,8 @@ const save = async (step?: number) => {
|
|||
return onlyMessage(isFixed ? '请选择设备' : '请选择部门', 'error')
|
||||
}
|
||||
// 选择方式为设备且仅选中一个设备时,物模型取该设备
|
||||
if (isFixed && addModel.selectorValues?.length === 1) {
|
||||
const resp = await deviceDetail(addModel.selectorValues[0].value)
|
||||
handleMetadata(resp.result.metadata)
|
||||
} else {
|
||||
handleMetadata(addModel.productDetail?.metadata)
|
||||
}
|
||||
const onlyOneDevice = isFixed && addModel.selectorValues?.length === 1
|
||||
handleMetadata( onlyOneDevice ? await getDeviceDetailByMetadata(addModel.selectorValues[0].value) : addModel.productDetail?.metadata)
|
||||
addModel.stepNumber = 2
|
||||
} else {
|
||||
const typeData = await typeRef.value.vail()
|
||||
|
@ -273,6 +273,16 @@ const stepChange = (step: number) => {
|
|||
}
|
||||
}
|
||||
|
||||
const initQuery = async () => {
|
||||
if (props.value.selector === 'fixed' && props.value.selectorValues?.length) {
|
||||
handleMetadata(await getDeviceDetailByMetadata(props.value.selectorValues[0].value))
|
||||
}
|
||||
}
|
||||
|
||||
nextTick(() => {
|
||||
initQuery()
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<Search
|
||||
<j-advanced-search
|
||||
:columns="columns"
|
||||
type='simple'
|
||||
@search="handleSearch"
|
||||
|
@ -7,7 +7,7 @@
|
|||
target="scene-triggrt-device-device"
|
||||
/>
|
||||
<a-divider style='margin: 0' />
|
||||
<j-table
|
||||
<j-pro-table
|
||||
ref='actionRef'
|
||||
model='CARD'
|
||||
:columns='columns'
|
||||
|
@ -60,7 +60,7 @@
|
|||
</template>
|
||||
</CardBox>
|
||||
</template>
|
||||
</j-table>
|
||||
</j-pro-table>
|
||||
</template>
|
||||
|
||||
<script setup lang='ts' name='DeviceSelectList'>
|
||||
|
@ -68,17 +68,17 @@ import type { PropType } from 'vue'
|
|||
import { getImage } from '@/utils/comm'
|
||||
import { query } from '@/api/device/instance'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import type { SelectorValuesItem } from '@/views/rule-engine/Scene/typings'
|
||||
|
||||
type Emit = {
|
||||
(e: 'update', data: Array<{ name: string, value: string}>): void
|
||||
(e: 'update', data: SelectorValuesItem[]): void
|
||||
}
|
||||
|
||||
const actionRef = ref()
|
||||
const params = ref({})
|
||||
const context = inject('SceneDeviceAddModel')
|
||||
const props = defineProps({
|
||||
rowKeys: {
|
||||
type: Array as PropType<Array<{ name: string, value: string}>>,
|
||||
type: Array as PropType<SelectorValuesItem[]>,
|
||||
default: () => ([])
|
||||
},
|
||||
productId: {
|
||||
|
|
|
@ -12,17 +12,13 @@ import DeviceList from './DeviceList.vue'
|
|||
import OrgList from './OrgList.vue'
|
||||
import { getImage } from '@/utils/comm'
|
||||
import type { PropType } from 'vue'
|
||||
|
||||
type ItemType = {
|
||||
name: string,
|
||||
value: string
|
||||
}
|
||||
import { SelectorValuesItem } from '@/views/rule-engine/Scene/typings'
|
||||
|
||||
type Emit = {
|
||||
(e: 'update:selector', data: string): void
|
||||
(e: 'update:selectorValues', data: ItemType[]): void
|
||||
(e: 'update:deviceKeys', data: ItemType[]): void
|
||||
(e: 'update:orgId', data: ItemType[]): void
|
||||
(e: 'update:selectorValues', data: SelectorValuesItem[]): void
|
||||
(e: 'update:deviceKeys', data: SelectorValuesItem[]): void
|
||||
(e: 'update:orgId', data: SelectorValuesItem[]): void
|
||||
}
|
||||
|
||||
const emit = defineEmits<Emit>()
|
||||
|
@ -36,18 +32,22 @@ const props = defineProps({
|
|||
type: String,
|
||||
default: ''
|
||||
},
|
||||
device: {
|
||||
type: Array as PropType<ItemType[]>,
|
||||
selectorValues: {
|
||||
type: Array as PropType<SelectorValuesItem[]>,
|
||||
default: () => []
|
||||
},
|
||||
deviceKeys: {
|
||||
type: Array as PropType<SelectorValuesItem[]>,
|
||||
default: () => []
|
||||
},
|
||||
orgId: {
|
||||
type: Array as PropType<ItemType[]>,
|
||||
type: Array as PropType<SelectorValuesItem[]>,
|
||||
default: () => []
|
||||
}
|
||||
})
|
||||
|
||||
const selectorModel = ref(props.selector)
|
||||
const devices = ref(props.device)
|
||||
const devices = ref(props.deviceKeys)
|
||||
const orgIds = ref(props.orgId)
|
||||
|
||||
const typeList = [
|
||||
|
@ -69,6 +69,7 @@ const updateDevice = (d: any[]) => {
|
|||
}
|
||||
|
||||
const updateOrg = (d: any[]) => {
|
||||
console.log('updateOrg', d)
|
||||
orgIds.value = d
|
||||
emit('update:orgId', d)
|
||||
emit('update:selectorValues', d)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<Search
|
||||
<j-advanced-search
|
||||
:columns="columns"
|
||||
type='simple'
|
||||
@search="handleSearch"
|
||||
|
@ -7,7 +7,7 @@
|
|||
target="scene-triggrt-device-category"
|
||||
/>
|
||||
<a-divider style='margin: 0' />
|
||||
<JTable
|
||||
<j-pro-table
|
||||
ref="instanceRef"
|
||||
model='TABLE'
|
||||
type='TREE'
|
||||
|
@ -26,9 +26,10 @@
|
|||
onChange: selectedRowChange
|
||||
}'
|
||||
:onChange='tableChange'
|
||||
@selectCancel='cancelAll'
|
||||
>
|
||||
|
||||
</JTable>
|
||||
</j-pro-table>
|
||||
|
||||
</template>
|
||||
|
||||
|
@ -36,14 +37,15 @@
|
|||
import type { PropType } from 'vue'
|
||||
import { getExpandedRowById } from './util'
|
||||
import { getTreeData_api } from '@/api/system/department'
|
||||
import { SelectorValuesItem } from '@/views/rule-engine/Scene/typings'
|
||||
|
||||
type Emit = {
|
||||
(e: 'update', data: Array<{ name: string, value: string}>): void
|
||||
(e: 'update', data: SelectorValuesItem[]): void
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
rowKeys: {
|
||||
type: Array as PropType<Array<{ name: string, value: string}>>,
|
||||
type: Array as PropType<SelectorValuesItem[]>,
|
||||
default: () => ([])
|
||||
},
|
||||
productId: {
|
||||
|
@ -69,6 +71,9 @@ const columns = [
|
|||
width: 300,
|
||||
ellipsis: true,
|
||||
dataIndex: 'name',
|
||||
search: {
|
||||
type: 'string'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '排序',
|
||||
|
@ -109,10 +114,14 @@ const query = async (p: any) => {
|
|||
return resp
|
||||
}
|
||||
|
||||
const selectedRowChange = (_: any, selectedRows: any[]) => {
|
||||
const selectedRowChange = (values: any, selectedRows: any[]) => {
|
||||
const item = selectedRows[0]
|
||||
console.log(selectedRows)
|
||||
emit('update', item ? [{ name: item.name, value: item.id }] : [])
|
||||
console.log(values, selectedRows)
|
||||
emit('update', [{ name: item.name, value: item.id }])
|
||||
}
|
||||
|
||||
const cancelAll = () => {
|
||||
emit('update', [])
|
||||
}
|
||||
|
||||
const expandedRowChange = (keys: string[]) => {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<Search
|
||||
<j-advanced-search
|
||||
:columns="columns"
|
||||
type='simple'
|
||||
@search="handleSearch"
|
||||
|
@ -7,7 +7,7 @@
|
|||
target="scene-triggrt-device-device"
|
||||
/>
|
||||
<a-divider style='margin: 0' />
|
||||
<j-table
|
||||
<j-pro-table
|
||||
ref='actionRef'
|
||||
model='CARD'
|
||||
:columns='columns'
|
||||
|
@ -53,7 +53,7 @@
|
|||
</template>
|
||||
</CardBox>
|
||||
</template>
|
||||
</j-table>
|
||||
</j-pro-table>
|
||||
</template>
|
||||
|
||||
<script setup lang='ts' name='Product'>
|
||||
|
@ -83,6 +83,7 @@ const props = defineProps({
|
|||
})
|
||||
|
||||
const emit = defineEmits<Emit>()
|
||||
const firstFind = ref(true)
|
||||
|
||||
const columns = [
|
||||
{
|
||||
|
@ -230,7 +231,7 @@ const handleSearch = (p: any) => {
|
|||
params.value = p
|
||||
}
|
||||
|
||||
const productQuery = (p: any) => {
|
||||
const productQuery = async (p: any) => {
|
||||
const sorts: any = [];
|
||||
|
||||
if (props.rowKey) {
|
||||
|
@ -241,7 +242,15 @@ const productQuery = (p: any) => {
|
|||
}
|
||||
sorts.push({ name: 'createTime', order: 'desc' });
|
||||
p.sorts = sorts
|
||||
return queryProductList(p)
|
||||
const resp = await queryProductList(p)
|
||||
if (resp.success && props.rowKey && firstFind.value) {
|
||||
const productItem = (resp.result as { data: any[]}).data.find((item: any) => item.id === props.rowKey)
|
||||
emit('update:detail', productItem)
|
||||
firstFind.value = false
|
||||
}
|
||||
return {
|
||||
...resp
|
||||
}
|
||||
}
|
||||
|
||||
const handleClick = (detail: any) => {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue