Merge remote-tracking branch 'origin/dev' into dev-hub
This commit is contained in:
commit
05fa3208b7
|
@ -24,7 +24,7 @@
|
|||
"event-source-polyfill": "^1.0.31",
|
||||
"global": "^4.4.0",
|
||||
"jetlinks-store": "^0.0.3",
|
||||
"jetlinks-ui-components": "^1.0.3",
|
||||
"jetlinks-ui-components": "^1.0.4",
|
||||
"js-cookie": "^3.0.1",
|
||||
"less": "^4.1.3",
|
||||
"less-loader": "^11.1.0",
|
||||
|
|
|
@ -0,0 +1,159 @@
|
|||
import type { IMatcher } from './jetlinks'
|
||||
|
||||
|
||||
export function kebabCase(key: string) {
|
||||
const result = key.replace(/([A-Z])/g, ' $1').trim()
|
||||
return result.split(' ').join('-').toLowerCase()
|
||||
}
|
||||
|
||||
export const AntdMatchComponents: IMatcher[] = [
|
||||
{
|
||||
pattern: /^Avatar/,
|
||||
styleDir: 'avatar',
|
||||
},
|
||||
{
|
||||
pattern: /^AutoComplete/,
|
||||
styleDir: 'auto-complete',
|
||||
},
|
||||
{
|
||||
pattern: /^Anchor/,
|
||||
styleDir: 'anchor',
|
||||
},
|
||||
|
||||
{
|
||||
pattern: /^Badge/,
|
||||
styleDir: 'badge',
|
||||
},
|
||||
{
|
||||
pattern: /^Breadcrumb/,
|
||||
styleDir: 'breadcrumb',
|
||||
},
|
||||
{
|
||||
pattern: /^Button/,
|
||||
styleDir: 'button',
|
||||
},
|
||||
{
|
||||
pattern: /^Checkbox/,
|
||||
styleDir: 'checkbox',
|
||||
},
|
||||
{
|
||||
pattern: /^Card/,
|
||||
styleDir: 'card',
|
||||
},
|
||||
{
|
||||
pattern: /^Collapse/,
|
||||
styleDir: 'collapse',
|
||||
},
|
||||
{
|
||||
pattern: /^Descriptions/,
|
||||
styleDir: 'descriptions',
|
||||
},
|
||||
{
|
||||
pattern: /^RangePicker|^WeekPicker|^MonthPicker/,
|
||||
styleDir: 'date-picker',
|
||||
},
|
||||
{
|
||||
pattern: /^Dropdown/,
|
||||
styleDir: 'dropdown',
|
||||
},
|
||||
|
||||
{
|
||||
pattern: /^Form/,
|
||||
styleDir: 'form',
|
||||
},
|
||||
{
|
||||
pattern: /^InputNumber/,
|
||||
styleDir: 'input-number',
|
||||
},
|
||||
|
||||
{
|
||||
pattern: /^Input|^Textarea/,
|
||||
styleDir: 'input',
|
||||
},
|
||||
{
|
||||
pattern: /^Statistic/,
|
||||
styleDir: 'statistic',
|
||||
},
|
||||
{
|
||||
pattern: /^CheckableTag/,
|
||||
styleDir: 'tag',
|
||||
},
|
||||
{
|
||||
pattern: /^TimeRangePicker/,
|
||||
styleDir: 'time-picker',
|
||||
},
|
||||
{
|
||||
pattern: /^Layout/,
|
||||
styleDir: 'layout',
|
||||
},
|
||||
{
|
||||
pattern: /^Menu|^SubMenu/,
|
||||
styleDir: 'menu',
|
||||
},
|
||||
|
||||
{
|
||||
pattern: /^Table/,
|
||||
styleDir: 'table',
|
||||
},
|
||||
{
|
||||
pattern: /^TimePicker|^TimeRangePicker/,
|
||||
styleDir: 'time-picker',
|
||||
},
|
||||
{
|
||||
pattern: /^Radio/,
|
||||
styleDir: 'radio',
|
||||
},
|
||||
|
||||
{
|
||||
pattern: /^Image/,
|
||||
styleDir: 'image',
|
||||
},
|
||||
|
||||
{
|
||||
pattern: /^List/,
|
||||
styleDir: 'list',
|
||||
},
|
||||
|
||||
{
|
||||
pattern: /^Tab/,
|
||||
styleDir: 'tabs',
|
||||
},
|
||||
{
|
||||
pattern: /^Mentions/,
|
||||
styleDir: 'mentions',
|
||||
},
|
||||
|
||||
{
|
||||
pattern: /^Step/,
|
||||
styleDir: 'steps',
|
||||
},
|
||||
{
|
||||
pattern: /^Skeleton/,
|
||||
styleDir: 'skeleton',
|
||||
},
|
||||
|
||||
{
|
||||
pattern: /^Select/,
|
||||
styleDir: 'select',
|
||||
},
|
||||
{
|
||||
pattern: /^TreeSelect/,
|
||||
styleDir: 'tree-select',
|
||||
},
|
||||
{
|
||||
pattern: /^Tree|^DirectoryTree/,
|
||||
styleDir: 'tree',
|
||||
},
|
||||
{
|
||||
pattern: /^Typography/,
|
||||
styleDir: 'typography',
|
||||
},
|
||||
{
|
||||
pattern: /^Timeline/,
|
||||
styleDir: 'timeline',
|
||||
},
|
||||
{
|
||||
pattern: /^Upload/,
|
||||
styleDir: 'upload',
|
||||
},
|
||||
]
|
|
@ -0,0 +1,352 @@
|
|||
import { AntdMatchComponents, kebabCase } from './antdv'
|
||||
|
||||
export interface IMatcher {
|
||||
pattern: RegExp
|
||||
styleDir: string
|
||||
}
|
||||
|
||||
const matchComponents: IMatcher[] = [
|
||||
{
|
||||
pattern: /^Avatar/,
|
||||
styleDir: 'Avatar',
|
||||
},
|
||||
{
|
||||
pattern: /^AutoComplete/,
|
||||
styleDir: 'AutoComplete',
|
||||
},
|
||||
{
|
||||
pattern: /^Anchor/,
|
||||
styleDir: 'Anchor',
|
||||
},
|
||||
|
||||
{
|
||||
pattern: /^Badge/,
|
||||
styleDir: 'Badge',
|
||||
},
|
||||
{
|
||||
pattern: /^Breadcrumb/,
|
||||
styleDir: 'Breadcrumb',
|
||||
},
|
||||
{
|
||||
pattern: /^Button/,
|
||||
styleDir: 'Button',
|
||||
},
|
||||
{
|
||||
pattern: /^Checkbox/,
|
||||
styleDir: 'Checkbox',
|
||||
},
|
||||
{
|
||||
pattern: /^Card/,
|
||||
styleDir: 'Card',
|
||||
},
|
||||
{
|
||||
pattern: /^Collapse/,
|
||||
styleDir: 'Collapse',
|
||||
},
|
||||
{
|
||||
pattern: /^Descriptions/,
|
||||
styleDir: 'Descriptions',
|
||||
},
|
||||
{
|
||||
pattern: /^RangePicker|^WeekPicker|^MonthPicker/,
|
||||
styleDir: 'DatePicker',
|
||||
},
|
||||
{
|
||||
pattern: /^Dropdown/,
|
||||
styleDir: 'Dropdown',
|
||||
},
|
||||
|
||||
{
|
||||
pattern: /^Form/,
|
||||
styleDir: 'Form',
|
||||
},
|
||||
{
|
||||
pattern: /^InputNumber/,
|
||||
styleDir: 'InputNumber',
|
||||
},
|
||||
|
||||
{
|
||||
pattern: /^Input|^Textarea/,
|
||||
styleDir: 'Input',
|
||||
},
|
||||
{
|
||||
pattern: /^Statistic/,
|
||||
styleDir: 'Statistic',
|
||||
},
|
||||
{
|
||||
pattern: /^CheckableTag/,
|
||||
styleDir: 'Tag',
|
||||
},
|
||||
{
|
||||
pattern: /^TimeRangePicker/,
|
||||
styleDir: 'TimePicker',
|
||||
},
|
||||
{
|
||||
pattern: /^Layout/,
|
||||
styleDir: 'Layout',
|
||||
},
|
||||
{
|
||||
pattern: /^Menu|^SubMenu/,
|
||||
styleDir: 'Menu',
|
||||
},
|
||||
|
||||
{
|
||||
pattern: /^Table/,
|
||||
styleDir: 'Table',
|
||||
},
|
||||
{
|
||||
pattern: /^TimePicker|^TimeRangePicker/,
|
||||
styleDir: 'TimeTicker',
|
||||
},
|
||||
{
|
||||
pattern: /^Radio/,
|
||||
styleDir: 'Radio',
|
||||
},
|
||||
|
||||
{
|
||||
pattern: /^Image/,
|
||||
styleDir: 'Image',
|
||||
},
|
||||
|
||||
{
|
||||
pattern: /^List/,
|
||||
styleDir: 'List',
|
||||
},
|
||||
|
||||
{
|
||||
pattern: /^Tab/,
|
||||
styleDir: 'Tabs',
|
||||
},
|
||||
{
|
||||
pattern: /^Mentions/,
|
||||
styleDir: 'Mentions',
|
||||
},
|
||||
|
||||
{
|
||||
pattern: /^Step/,
|
||||
styleDir: 'Steps',
|
||||
},
|
||||
{
|
||||
pattern: /^Skeleton/,
|
||||
styleDir: 'Skeleton',
|
||||
},
|
||||
|
||||
{
|
||||
pattern: /^Select/,
|
||||
styleDir: 'Select',
|
||||
},
|
||||
{
|
||||
pattern: /^TreeSelect/,
|
||||
styleDir: 'TreeSelect',
|
||||
},
|
||||
{
|
||||
pattern: /^Tree|^DirectoryTree/,
|
||||
styleDir: 'Tree',
|
||||
},
|
||||
{
|
||||
pattern: /^Typography/,
|
||||
styleDir: 'Typography',
|
||||
},
|
||||
{
|
||||
pattern: /^Timeline/,
|
||||
styleDir: 'Timeline',
|
||||
},
|
||||
{
|
||||
pattern: /^Upload/,
|
||||
styleDir: 'Upload',
|
||||
},
|
||||
{
|
||||
pattern: /^ProTable/,
|
||||
styleDir: 'ProTable',
|
||||
},
|
||||
{
|
||||
pattern: /^Search|^AdvancedSearch/,
|
||||
styleDir: 'Search',
|
||||
},
|
||||
{
|
||||
pattern: /^Ellipsis/,
|
||||
styleDir: 'Ellipsis',
|
||||
},
|
||||
{
|
||||
pattern: /^MonacoEditor/,
|
||||
styleDir: 'MonacoEditor',
|
||||
},
|
||||
{
|
||||
pattern: /^ProLayout/,
|
||||
styleDir: 'ProLayout',
|
||||
},
|
||||
{
|
||||
pattern: /^ScrollTable/,
|
||||
styleDir: 'ScrollTable',
|
||||
},
|
||||
{
|
||||
pattern: /^TableCard/,
|
||||
styleDir: 'TableCard',
|
||||
},
|
||||
{
|
||||
pattern: /^Scrollbar/,
|
||||
styleDir: 'Scrollbar',
|
||||
},
|
||||
{
|
||||
pattern: /^AIcon/,
|
||||
styleDir: 'AIcon',
|
||||
},
|
||||
{
|
||||
pattern: /^CardSelect/,
|
||||
styleDir: 'CardSelect',
|
||||
},
|
||||
{
|
||||
pattern: /^Tooltip/,
|
||||
styleDir: 'Tooltip',
|
||||
},
|
||||
{
|
||||
pattern: /^Empty/,
|
||||
styleDir: 'Empty',
|
||||
},
|
||||
{
|
||||
pattern: /^Popconfirm/,
|
||||
styleDir: 'Popconfirm',
|
||||
},
|
||||
{
|
||||
pattern: /^message/,
|
||||
styleDir: 'Message',
|
||||
},
|
||||
{
|
||||
pattern: /^Notification/,
|
||||
styleDir: 'Notification',
|
||||
},
|
||||
]
|
||||
|
||||
export interface JetlinksVueResolverOptions {
|
||||
/**
|
||||
* exclude components that do not require automatic import
|
||||
*
|
||||
* @default []
|
||||
*/
|
||||
exclude?: string[]
|
||||
/**
|
||||
* import style along with components
|
||||
*
|
||||
* @default 'css'
|
||||
*/
|
||||
importStyle?: boolean | 'css' | 'less'
|
||||
/**
|
||||
* resolve `ant-design-vue' icons
|
||||
*
|
||||
* requires package `@ant-design/icons-vue`
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
resolveIcons?: boolean
|
||||
|
||||
/**
|
||||
* @deprecated use `importStyle: 'css'` instead
|
||||
*/
|
||||
importCss?: boolean
|
||||
/**
|
||||
* @deprecated use `importStyle: 'less'` instead
|
||||
*/
|
||||
importLess?: boolean
|
||||
|
||||
/**
|
||||
* use commonjs build default false
|
||||
*/
|
||||
cjs?: boolean
|
||||
packageName?: string
|
||||
}
|
||||
|
||||
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)) {
|
||||
styleDir = matcher.styleDir
|
||||
break
|
||||
}
|
||||
}
|
||||
if (!styleDir)
|
||||
styleDir = _isAntd ? kebabCase(compName) : compName
|
||||
|
||||
return styleDir
|
||||
}
|
||||
|
||||
function getSideEffects(compName: string, options: JetlinksVueResolverOptions, _isAntd= false): any {
|
||||
const {
|
||||
importStyle = true,
|
||||
importLess = false,
|
||||
} = options
|
||||
|
||||
if (!importStyle)
|
||||
return
|
||||
const lib = options.cjs ? 'lib' : 'es'
|
||||
const packageName = options?.packageName || 'jetlinks-ui-components'
|
||||
|
||||
if (importStyle === 'less' || importLess) {
|
||||
const styleDir = getStyleDir(compName, _isAntd)
|
||||
console.log('getSideEffects-style-path', `${packageName}/${lib}/${styleDir}/style`)
|
||||
return `${packageName}/${lib}/${styleDir}/style`
|
||||
}
|
||||
else {
|
||||
const styleDir = getStyleDir(compName, _isAntd)
|
||||
return `${packageName}/${lib}/${styleDir}/style/css`
|
||||
}
|
||||
}
|
||||
|
||||
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}`))
|
||||
}
|
||||
|
||||
let antdvNames: Set<string>
|
||||
|
||||
function genAntdNames(primitiveNames: string[]): void {
|
||||
antdvNames = new Set(primitiveNames.map(name => `A${name}`))
|
||||
}
|
||||
|
||||
genAntdNames(primitiveNames.filter(key => !filterName.includes(key)))
|
||||
genJetlinksNames(primitiveNames)
|
||||
|
||||
function isJetlinks(compName: string): boolean {
|
||||
return jetlinksNames.has(compName)
|
||||
}
|
||||
|
||||
function isAntdv(compName: string): boolean {
|
||||
return antdvNames.has(compName)
|
||||
}
|
||||
|
||||
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',
|
||||
}
|
||||
}
|
||||
const _isJetlinks = isJetlinks(name)
|
||||
const _isAntd = isAntdv(name)
|
||||
if ((_isJetlinks || _isAntd) && !options?.exclude?.includes(name)) {
|
||||
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'}`
|
||||
|
||||
return {
|
||||
name: importName,
|
||||
from: path,
|
||||
sideEffects: getSideEffects(importName, options, _isAntd),
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
|
@ -17,6 +17,8 @@ export default {
|
|||
getHistory: (data: any, id: string) => post(`/notify/history/config/${id}/_query`, data),
|
||||
// 获取所有平台用户
|
||||
getPlatformUsers: (data: any) => post<any>(`/user/_query/no-paging`, data),
|
||||
// 获取所有关系
|
||||
getRelationUsers: (data: any) => post<any>(`/relation/_query/no-paging`, data),
|
||||
// 钉钉部门
|
||||
dingTalkDept: (id: string) => get<any>(`/notifier/dingtalk/corp/${id}/departments/tree`),
|
||||
// 钉钉部门人员
|
||||
|
@ -38,5 +40,9 @@ export default {
|
|||
//通知类型
|
||||
queryMessageType: () => get(`/notifier/config/types`),
|
||||
// 不分页-列表
|
||||
queryListNoPaging: (data: any) => post(`/notifier/config/_query/no-paging?paging=false`, data)
|
||||
queryListNoPaging: (data: any) => post(`/notifier/config/_query/no-paging?paging=false`, data),
|
||||
//
|
||||
queryDingTalkUsers: (id: string) => get<any>(`/notifier/dingtalk/corp/${id}/users?sorts[0].name='name'&sorts[0].order=asc`),
|
||||
//
|
||||
queryWechatUsers: (id: string) => get<any>(`/notifier/wechat/corp/${id}/users?sorts[0].name='name'&sorts[0].order=asc`),
|
||||
}
|
|
@ -18,4 +18,7 @@ export const _action = (id: string, type: '_disable' | '_enable') => server.put(
|
|||
* @param id
|
||||
* @returns
|
||||
*/
|
||||
export const _execute = (id: string) => server.post(`/scene/${id}/_execute`);
|
||||
export const _execute = (id: string) => server.post(`/scene/${id}/_execute`);
|
||||
|
||||
// 内置参数
|
||||
export const queryBuiltInParams = (data: any, params?: any) => server.post(`/scene/parse-variables`, data, params);
|
|
@ -61,7 +61,10 @@
|
|||
保存
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-empty v-if="!historyList.length" />
|
||||
<a-empty
|
||||
v-if="!historyList.length"
|
||||
description="暂无数据"
|
||||
/>
|
||||
<a-menu-item
|
||||
v-for="(item, index) in historyList"
|
||||
:key="`his${index}`"
|
||||
|
@ -124,7 +127,11 @@
|
|||
>
|
||||
刷新
|
||||
</div>
|
||||
<LivePlayer :src="item.url" />
|
||||
<LivePlayer
|
||||
:src="item.url"
|
||||
:width="screenWidth"
|
||||
:height="screenHeight"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
@ -179,20 +186,39 @@ const props = defineProps<ScreenProps>();
|
|||
|
||||
const DEFAULT_SAVE_CODE = 'screen-save';
|
||||
|
||||
// 分屏数量 1/4/9/0
|
||||
const screen = ref(1);
|
||||
// 视频窗口
|
||||
const players = ref<Player[]>([]);
|
||||
// 当前选中的窗口
|
||||
const playerActive = ref(0);
|
||||
// 单个播放窗口宽高
|
||||
const screenWidth = ref('');
|
||||
const screenHeight = ref('');
|
||||
// 历史记录
|
||||
const historyList = ref<any[]>([]);
|
||||
// 展示保存浮窗
|
||||
const visible = ref(false);
|
||||
const loading = ref(false);
|
||||
const fullscreenRef = ref(null);
|
||||
const { isFullscreen, enter, exit, toggle } = useFullscreen();
|
||||
|
||||
// 保存表单
|
||||
const formRef = ref();
|
||||
const formData = ref({
|
||||
name: '',
|
||||
});
|
||||
|
||||
// 全屏元素
|
||||
const fullscreenRef = ref(null);
|
||||
const { isFullscreen, enter, exit, toggle } = useFullscreen(
|
||||
fullscreenRef.value,
|
||||
);
|
||||
|
||||
/**
|
||||
* 刷新视频
|
||||
* @param id
|
||||
* @param channelId
|
||||
* @param url
|
||||
* @param index
|
||||
*/
|
||||
const reloadPlayer = (
|
||||
id: string,
|
||||
channelId: string,
|
||||
|
@ -221,6 +247,12 @@ const reloadPlayer = (
|
|||
}, 1000);
|
||||
};
|
||||
|
||||
/**
|
||||
* 视频链接变化, 更新播放内容
|
||||
* @param id
|
||||
* @param channelId
|
||||
* @param url
|
||||
*/
|
||||
const replaceVideo = (id: string, channelId: string, url: string) => {
|
||||
const olPlayers = [...players.value];
|
||||
const newPlayer = {
|
||||
|
@ -246,6 +278,10 @@ const replaceVideo = (id: string, channelId: string, url: string) => {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 点击分屏历史记录
|
||||
* @param item
|
||||
*/
|
||||
const handleHistory = (item: any) => {
|
||||
if (props.historyHandle) {
|
||||
const log = JSON.parse(item.content || '{}');
|
||||
|
@ -271,12 +307,20 @@ const handleHistory = (item: any) => {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取历史分屏
|
||||
*/
|
||||
const getHistory = async () => {
|
||||
const res = await getSearchHistory(DEFAULT_SAVE_CODE);
|
||||
if (res.success) {
|
||||
historyList.value = res.result;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除历史分屏
|
||||
* @param id
|
||||
*/
|
||||
const deleteHistory = async (id: string) => {
|
||||
const res = await deleteSearchHistory(DEFAULT_SAVE_CODE, id);
|
||||
if (res.success) {
|
||||
|
@ -285,6 +329,9 @@ const deleteHistory = async (id: string) => {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 保存分屏
|
||||
*/
|
||||
const saveHistory = async () => {
|
||||
formRef.value
|
||||
.validate()
|
||||
|
@ -292,7 +339,7 @@ const saveHistory = async () => {
|
|||
const param = {
|
||||
name: formData.value.name,
|
||||
content: JSON.stringify({
|
||||
screen: screen,
|
||||
screen: screen.value,
|
||||
players: players.value.map((item: any) => ({
|
||||
deviceId: item.id,
|
||||
channelId: item.channelId,
|
||||
|
@ -306,6 +353,7 @@ const saveHistory = async () => {
|
|||
visible.value = false;
|
||||
getHistory();
|
||||
message.success('保存成功');
|
||||
formRef.value.resetFields();
|
||||
} else {
|
||||
message.error('保存失败');
|
||||
}
|
||||
|
@ -315,6 +363,9 @@ const saveHistory = async () => {
|
|||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*/
|
||||
const mediaInit = () => {
|
||||
const newArr = [];
|
||||
for (let i = 0; i < 9; i++) {
|
||||
|
@ -329,6 +380,10 @@ const mediaInit = () => {
|
|||
players.value = newArr;
|
||||
};
|
||||
|
||||
/**
|
||||
* 改变分屏数量
|
||||
* @param e
|
||||
*/
|
||||
const handleScreenChange = (e: any) => {
|
||||
if (e.target.value) {
|
||||
screenChange(e.target.value);
|
||||
|
@ -348,8 +403,19 @@ const screenChange = (index: number) => {
|
|||
}));
|
||||
playerActive.value = 0;
|
||||
screen.value = index;
|
||||
|
||||
// if (screen.value === 4) {
|
||||
// screenWidth.value = '350px';
|
||||
// screenHeight.value = '2000px';
|
||||
// }
|
||||
};
|
||||
|
||||
/**
|
||||
* 刷新
|
||||
* @param e
|
||||
* @param item
|
||||
* @param index
|
||||
*/
|
||||
const handleRefresh = (e: any, item: any, index: number) => {
|
||||
e.stopPropagation();
|
||||
if (item.url) {
|
||||
|
@ -363,10 +429,6 @@ const handleRefresh = (e: any, item: any, index: number) => {
|
|||
*/
|
||||
const handleMouseDown = (type: string) => {
|
||||
const { id, channelId } = players.value[playerActive.value];
|
||||
console.log('players.value: ', players.value);
|
||||
console.log('playerActive.value: ', playerActive.value);
|
||||
console.log('id: ', id);
|
||||
console.log('channelId: ', channelId);
|
||||
if (id && channelId && props.onMouseDown) {
|
||||
props.onMouseDown(id, channelId, type);
|
||||
}
|
||||
|
@ -393,6 +455,10 @@ watchEffect(() => {
|
|||
}
|
||||
mediaInit();
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
replaceVideo,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
|
|
@ -5,10 +5,10 @@ import JTable from './Table/index'
|
|||
import TitleComponent from "./TitleComponent/index.vue";
|
||||
import Form from './Form';
|
||||
import CardBox from './CardBox/index.vue';
|
||||
import Search from './Search'
|
||||
// import Search from './Search'
|
||||
import NormalUpload from './NormalUpload/index.vue'
|
||||
import FileFormat from './FileFormat/index.vue'
|
||||
import JUpload from './JUpload/index.vue'
|
||||
// import JUpload from './JUpload/index.vue'
|
||||
import { BasicLayoutPage, BlankLayoutPage } from './Layout'
|
||||
import { PageContainer } from 'jetlinks-ui-components/es/components'
|
||||
import Ellipsis from './Ellipsis/index.vue'
|
||||
|
@ -24,10 +24,10 @@ export default {
|
|||
.component('TitleComponent', TitleComponent)
|
||||
.component('Form', Form)
|
||||
.component('CardBox', CardBox)
|
||||
.component('Search', Search)
|
||||
// .component('Search', Search)
|
||||
.component('NormalUpload', NormalUpload)
|
||||
.component('FileFormat', FileFormat)
|
||||
.component('JUpload', JUpload)
|
||||
// .component('JUpload', JUpload)
|
||||
.component('BasicLayoutPage', BasicLayoutPage)
|
||||
.component('BlankLayoutPage', BlankLayoutPage)
|
||||
.component('PageContainer', PageContainer)
|
||||
|
|
|
@ -4,13 +4,13 @@ import store from './store'
|
|||
import components from './components'
|
||||
import router from './router'
|
||||
import './style.less'
|
||||
import jComponents from 'jetlinks-ui-components'
|
||||
import 'jetlinks-ui-components/es/style.js'
|
||||
// import jComponents from 'jetlinks-ui-components'
|
||||
// import 'jetlinks-ui-components/es/style.js'
|
||||
|
||||
const app = createApp(App)
|
||||
|
||||
app.use(store)
|
||||
.use(router)
|
||||
.use(components)
|
||||
.use(jComponents)
|
||||
// .use(jComponents)
|
||||
.mount('#app')
|
||||
|
|
|
@ -1,5 +1,49 @@
|
|||
export const LoginPath = '/login'
|
||||
|
||||
export const AccountMenu = {
|
||||
path: '/account',
|
||||
component: () => import('@/components/Layout/BasicLayoutPage.vue'),
|
||||
redirect: '/account/center',
|
||||
name: 'account',
|
||||
meta: {
|
||||
title: '个人中心',
|
||||
icon: '',
|
||||
hideInMenu: true
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '/account/center',
|
||||
name: 'account/center',
|
||||
meta: {
|
||||
title: '基本设置',
|
||||
icon: '',
|
||||
hideInMenu: false
|
||||
},
|
||||
component: () => import('@/views/account/Center/index.vue')
|
||||
},
|
||||
{
|
||||
path: '/account/NotificationSubscription',
|
||||
name: 'account/NotificationSubscription',
|
||||
meta: {
|
||||
title: '通知订阅',
|
||||
icon: '',
|
||||
hideInMenu: false
|
||||
},
|
||||
component: () => import('@/views/account/NotificationSubscription/index.vue')
|
||||
},
|
||||
{
|
||||
path: '/account/NotificationRecord',
|
||||
name: 'account/NotificationRecord',
|
||||
meta: {
|
||||
title: '通知记录',
|
||||
icon: '',
|
||||
hideInMenu: false
|
||||
},
|
||||
component: () => import('@/views/account/NotificationRecord/index.vue')
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
export default [
|
||||
{ path: '/*', redirect: '/'},
|
||||
// start: 测试用, 可删除
|
||||
|
@ -27,18 +71,6 @@ export default [
|
|||
path: '/system/Api',
|
||||
component: () => import('@/views/system/Platforms/index.vue')
|
||||
},
|
||||
{
|
||||
path: '/account/center',
|
||||
component: () => import('@/views/account/Center/index.vue')
|
||||
},
|
||||
{
|
||||
path: '/account/NotificationSubscription',
|
||||
component: () => import('@/views/account/NotificationSubscription/index.vue')
|
||||
},
|
||||
{
|
||||
path: '/account/NotificationRecord',
|
||||
component: () => import('@/views/account/NotificationRecord/index.vue')
|
||||
},
|
||||
// end: 测试用, 可删除
|
||||
|
||||
// 初始化
|
||||
|
|
|
@ -4,8 +4,8 @@ import { filterAsnycRouter, MenuItem } from '@/utils/menu'
|
|||
import { isArray } from 'lodash-es'
|
||||
import { usePermissionStore } from './permission'
|
||||
import router from '@/router'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { onlyMessage } from '@/utils/comm'
|
||||
import { AccountMenu } from '@/router/menu'
|
||||
|
||||
const defaultOwnParams = [
|
||||
{
|
||||
|
@ -115,8 +115,11 @@ export const useMenuStore = defineStore({
|
|||
hideInMenu: true
|
||||
}
|
||||
})
|
||||
menusData.push(AccountMenu)
|
||||
silderMenus.push(AccountMenu)
|
||||
this.siderMenus = silderMenus
|
||||
console.log('menusData', menusData)
|
||||
console.log('silderMenus', silderMenus)
|
||||
res(menusData)
|
||||
}
|
||||
})
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import type { Slots } from 'vue'
|
||||
import { TOKEN_KEY } from '@/utils/variable'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { message } from 'jetlinks-ui-components';
|
||||
|
||||
/**
|
||||
* 静态图片资源处理
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import { isObject, isArray } from 'lodash-es'
|
||||
|
||||
const encodeParams = (params: Record<string, any>) => {
|
||||
export const encodeParams = (params: Record<string, any>) => {
|
||||
const _params = new URLSearchParams()
|
||||
for (const key in params) {
|
||||
const _value = params[key]
|
||||
const isArrOrObj = isObject(_value) || isArray(_value)
|
||||
_params.set(key, isArrOrObj ? encodeParams(_value) : _value)
|
||||
_params.set(key, isArrOrObj ? JSON.stringify(_value) : _value)
|
||||
}
|
||||
return _params.toString()
|
||||
}
|
||||
|
|
|
@ -103,7 +103,7 @@ import {
|
|||
saveDeviceCode,
|
||||
delDeviceCode
|
||||
} from '@/api/device/instance'
|
||||
import { message } from 'ant-design-vue';
|
||||
import { message } from 'jetlinks-ui-components';
|
||||
import { isBoolean } from 'lodash';
|
||||
|
||||
const defaultValue =
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<page-container>
|
||||
<JSearch
|
||||
<j-advanced-search
|
||||
:columns="columns"
|
||||
target="device-instance"
|
||||
@search="handleSearch"
|
||||
|
@ -259,7 +259,7 @@ import {
|
|||
batchDeployDevice,
|
||||
batchDeleteDevice,
|
||||
} from '@/api/device/instance';
|
||||
import type { ActionsType } from '@/components/Table/index.vue';
|
||||
// import type { ActionsType } from '@/components/Table/index.vue';
|
||||
import { getImage, LocalStore } from '@/utils/comm';
|
||||
import { message } from 'ant-design-vue';
|
||||
import Import from './Import/index.vue';
|
||||
|
|
|
@ -72,7 +72,7 @@ import {
|
|||
testCode,
|
||||
saveProductCode,
|
||||
} from '@/api/device/instance'
|
||||
import { message } from 'ant-design-vue';
|
||||
import { message } from 'jetlinks-ui-components';
|
||||
import { isBoolean } from 'lodash';
|
||||
|
||||
const defaultValue =
|
||||
|
|
|
@ -374,6 +374,7 @@ const productStore = useProductStore();
|
|||
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 render = new marked.Renderer();
|
||||
marked.setOptions({
|
||||
renderer: render,
|
||||
|
@ -690,31 +691,53 @@ const driver1 = new Driver({
|
|||
/**
|
||||
* 表格列表
|
||||
*/
|
||||
const columnsMQTT: any[] = [
|
||||
{
|
||||
title: '分组',
|
||||
dataIndex: 'group',
|
||||
key: 'group',
|
||||
ellipsis: true,
|
||||
width: 100,
|
||||
// customCell: (record: any, index: number) => {
|
||||
// const list =
|
||||
// (config?.routes || []).sort((a: any, b: any) => a - b) || [];
|
||||
// const arr = list.filter((res: any) => {
|
||||
// // 这里gpsNumber是我需要判断的字段名(相同就合并)
|
||||
// return res?.group == record?.group;
|
||||
// });
|
||||
// if (index == 0 || list[index - 1]?.group != record?.group) {
|
||||
// return { rowSpan: arr.length };
|
||||
// } else {
|
||||
// return { rowSpan: 0 };
|
||||
// }
|
||||
// },
|
||||
},
|
||||
// const columnsMQTT: any[] = [
|
||||
// {
|
||||
// title: '分组',
|
||||
// dataIndex: 'group',
|
||||
// key: 'group',
|
||||
// ellipsis: true,
|
||||
// width: 100,
|
||||
// // customCell: (record: any, index: number) => {
|
||||
// // const list =
|
||||
// // (config?.routes || []).sort((a: any, b: any) => a - b) || [];
|
||||
// // const arr = list.filter((res: any) => {
|
||||
// // // 这里gpsNumber是我需要判断的字段名(相同就合并)
|
||||
// // return res?.group == record?.group;
|
||||
// // });
|
||||
// // if (index == 0 || list[index - 1]?.group != record?.group) {
|
||||
// // return { rowSpan: arr.length };
|
||||
// // } else {
|
||||
// // return { rowSpan: 0 };
|
||||
// // }
|
||||
// // },
|
||||
// },
|
||||
// {
|
||||
// title: 'topic',
|
||||
// dataIndex: 'topic',
|
||||
// key: 'topic',
|
||||
// },
|
||||
// {
|
||||
// title: '上下行',
|
||||
// dataIndex: 'stream',
|
||||
// key: 'stream',
|
||||
// ellipsis: true,
|
||||
// align: 'center',
|
||||
// width: 100,
|
||||
// },
|
||||
// {
|
||||
// title: '说明',
|
||||
// dataIndex: 'description',
|
||||
// key: 'description',
|
||||
// },
|
||||
// ];
|
||||
let columnsMQTT = ref(<TableColumnType>[]);
|
||||
const ColumnsMQTT = [
|
||||
{
|
||||
title: 'topic',
|
||||
dataIndex: 'topic',
|
||||
key: 'topic',
|
||||
ellipsis: true,
|
||||
},
|
||||
{
|
||||
title: '上下行',
|
||||
|
@ -723,46 +746,71 @@ const columnsMQTT: any[] = [
|
|||
ellipsis: true,
|
||||
align: 'center',
|
||||
width: 100,
|
||||
scopedSlots: { customRender: 'stream' },
|
||||
},
|
||||
{
|
||||
title: '说明',
|
||||
dataIndex: 'description',
|
||||
key: 'description',
|
||||
ellipsis: true,
|
||||
},
|
||||
];
|
||||
|
||||
const columnsHTTP: any[] = [
|
||||
const columnsHTTP = ref(<TableColumnType>[]);
|
||||
const ColumnsHTTP = [
|
||||
{
|
||||
title: '分组',
|
||||
dataIndex: 'group',
|
||||
key: 'group',
|
||||
title: '地址',
|
||||
dataIndex: 'address',
|
||||
key: 'address',
|
||||
ellipsis: true,
|
||||
width: 100,
|
||||
// customCell: (record: any, index: number) => {
|
||||
// const list =
|
||||
// (config?.routes || []).sort((a: any, b: any) => a - b) || [];
|
||||
// const arr = list.filter((res: any) => {
|
||||
// // 这里gpsNumber是我需要判断的字段名(相同就合并)
|
||||
// return res?.group == record?.group;
|
||||
// });
|
||||
// if (index == 0 || list[index - 1]?.group != record?.group) {
|
||||
// return { rowSpan: arr.length };
|
||||
// } else {
|
||||
// return { rowSpan: 0 };
|
||||
// }
|
||||
// },
|
||||
// scopedSlots: { customRender: 'address' },
|
||||
},
|
||||
{
|
||||
title: '示例',
|
||||
dataIndex: 'example',
|
||||
key: 'example',
|
||||
ellipsis: true,
|
||||
// scopedSlots: { customRender: 'example' },
|
||||
},
|
||||
{
|
||||
title: '说明',
|
||||
dataIndex: 'description',
|
||||
key: 'description',
|
||||
ellipsis: true,
|
||||
// scopedSlots: { customRender: 'description' },
|
||||
},
|
||||
];
|
||||
// const columnsHTTP: any[] = [
|
||||
// {
|
||||
// title: '分组',
|
||||
// dataIndex: 'group',
|
||||
// key: 'group',
|
||||
// ellipsis: true,
|
||||
// width: 100,
|
||||
// // customCell: (record: any, index: number) => {
|
||||
// // const list =
|
||||
// // (config?.routes || []).sort((a: any, b: any) => a - b) || [];
|
||||
// // const arr = list.filter((res: any) => {
|
||||
// // // 这里gpsNumber是我需要判断的字段名(相同就合并)
|
||||
// // return res?.group == record?.group;
|
||||
// // });
|
||||
// // if (index == 0 || list[index - 1]?.group != record?.group) {
|
||||
// // return { rowSpan: arr.length };
|
||||
// // } else {
|
||||
// // return { rowSpan: 0 };
|
||||
// // }
|
||||
// // },
|
||||
// },
|
||||
// {
|
||||
// title: '示例',
|
||||
// dataIndex: 'example',
|
||||
// key: 'example',
|
||||
// },
|
||||
// {
|
||||
// title: '说明',
|
||||
// dataIndex: 'description',
|
||||
// key: 'description',
|
||||
// },
|
||||
// ];
|
||||
/**
|
||||
* 获取上下行数据
|
||||
*/
|
||||
|
@ -806,6 +854,34 @@ const getConfigDetail = async (
|
|||
(resp) => {
|
||||
if (resp.status === 200) {
|
||||
config.value = resp.result;
|
||||
const Group = {
|
||||
title: '分组',
|
||||
dataIndex: 'group',
|
||||
key: 'group',
|
||||
ellipsis: true,
|
||||
align: 'center',
|
||||
width: 100,
|
||||
customCell: (record: any, rowIndex: number) => {
|
||||
const obj = {
|
||||
children: record,
|
||||
rowSpan: 0,
|
||||
};
|
||||
const list = config.value?.routes || [];
|
||||
|
||||
const arr = list.filter(
|
||||
(res: any) => res.group === record.group,
|
||||
);
|
||||
|
||||
const isRowIndex =
|
||||
rowIndex === 0 ||
|
||||
list[rowIndex - 1].group !== record.group;
|
||||
isRowIndex && (obj.rowSpan = arr.length);
|
||||
|
||||
return obj;
|
||||
},
|
||||
};
|
||||
columnsMQTT.value = [Group, ...ColumnsMQTT];
|
||||
columnsHTTP.value = [Group, ...ColumnsHTTP];
|
||||
if (config.value?.document) {
|
||||
markdownToHtml.value = marked(config.value.document);
|
||||
}
|
||||
|
@ -989,10 +1065,12 @@ watchEffect(() => {
|
|||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
:deep(._jtable-body_1eyxz_1
|
||||
._jtable-body-header_1eyxz_6
|
||||
._jtable-body-header-right_1eyxz_12
|
||||
._jtable-body-header-right-button_1eyxz_17) {
|
||||
:deep(
|
||||
._jtable-body_1eyxz_1
|
||||
._jtable-body-header_1eyxz_6
|
||||
._jtable-body-header-right_1eyxz_12
|
||||
._jtable-body-header-right-button_1eyxz_17
|
||||
) {
|
||||
display: none;
|
||||
margin-left: 10px;
|
||||
gap: 8px;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
target="product-manage"
|
||||
@search="handleSearch"
|
||||
/>
|
||||
<JTable
|
||||
<JProTable
|
||||
:columns="columns"
|
||||
:request="queryProductList"
|
||||
ref="tableRef"
|
||||
|
@ -85,7 +85,7 @@
|
|||
</a-row>
|
||||
</template>
|
||||
<template #actions="item">
|
||||
<a-tooltip
|
||||
<!-- <a-tooltip
|
||||
v-bind="item.tooltip"
|
||||
:title="item.disabled && item.tooltip.title"
|
||||
>
|
||||
|
@ -122,7 +122,24 @@
|
|||
</template>
|
||||
</a-button>
|
||||
</template>
|
||||
</a-tooltip>
|
||||
</a-tooltip> -->
|
||||
<PermissionButton
|
||||
:disabled="item.disabled"
|
||||
:popConfirm="item.popConfirm"
|
||||
:tooltip="{
|
||||
...item.tooltip,
|
||||
}"
|
||||
@click="item.onClick"
|
||||
>
|
||||
<AIcon
|
||||
type="DeleteOutlined"
|
||||
v-if="item.key === 'delete'"
|
||||
/>
|
||||
<template v-else>
|
||||
<AIcon :type="item.icon" />
|
||||
<span>{{ item?.text }}</span>
|
||||
</template>
|
||||
</PermissionButton>
|
||||
</template>
|
||||
</CardBox>
|
||||
</template>
|
||||
|
@ -137,7 +154,7 @@
|
|||
</template>
|
||||
<template #action="slotProps">
|
||||
<a-space :size="16">
|
||||
<a-tooltip
|
||||
<!-- <a-tooltip
|
||||
v-for="i in getActions(slotProps)"
|
||||
:key="i.key"
|
||||
v-bind="i.tooltip"
|
||||
|
@ -168,10 +185,27 @@
|
|||
><AIcon :type="i.icon"
|
||||
/></a-button>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
</a-tooltip> -->
|
||||
<template
|
||||
v-for="i in getActions(slotProps, 'table')"
|
||||
:key="i.key"
|
||||
>
|
||||
<PermissionButton
|
||||
:disabled="i.disabled"
|
||||
:popConfirm="i.popConfirm"
|
||||
:tooltip="{
|
||||
...i.tooltip,
|
||||
}"
|
||||
@click="i.onClick"
|
||||
type="link"
|
||||
style="padding: 0px"
|
||||
>
|
||||
<template #icon><AIcon :type="i.icon" /></template>
|
||||
</PermissionButton>
|
||||
</template>
|
||||
</a-space>
|
||||
</template>
|
||||
</JTable>
|
||||
</JProTable>
|
||||
<!-- 新增、编辑 -->
|
||||
<Save ref="saveRef" :isAdd="isAdd" :title="title" @success="refresh" />
|
||||
</page-container>
|
||||
|
|
|
@ -1,53 +1,27 @@
|
|||
<!-- 绑定设备 -->
|
||||
<template>
|
||||
<j-modal
|
||||
:maskClosable="false"
|
||||
width="1100px"
|
||||
:visible="true"
|
||||
title="选择设备"
|
||||
okText="确定"
|
||||
cancelText="取消"
|
||||
@ok="handleOk"
|
||||
@cancel="handleCancel"
|
||||
:confirmLoading="btnLoading"
|
||||
>
|
||||
<j-modal :maskClosable="false" width="1100px" :visible="true" title="选择设备" okText="确定" cancelText="取消" @ok="handleOk"
|
||||
@cancel="handleCancel" :confirmLoading="btnLoading">
|
||||
<div style="margin-top: 10px">
|
||||
<Search
|
||||
:columns="columns"
|
||||
target="iot-card-bind-device"
|
||||
@search="handleSearch"
|
||||
type="simple"
|
||||
/>
|
||||
<j-pro-table
|
||||
ref="bindDeviceRef"
|
||||
:columns="columns"
|
||||
:request="queryUnbounded"
|
||||
model="TABLE"
|
||||
:defaultParams="{
|
||||
sorts: [{ name: 'createTime', order: 'desc' }],
|
||||
}"
|
||||
:rowSelection="{
|
||||
type: 'radio',
|
||||
selectedRowKeys: _selectedRowKeys,
|
||||
onSelect: onSelectChange,
|
||||
}"
|
||||
@cancelSelect="cancelSelect"
|
||||
:params="params"
|
||||
>
|
||||
<Search :columns="columns" target="iot-card-bind-device" @search="handleSearch" type="simple" />
|
||||
<j-pro-table ref="bindDeviceRef" :columns="columns" :request="queryUnbounded" model="TABLE" :defaultParams="{
|
||||
sorts: [{ name: 'createTime', order: 'desc' }],
|
||||
}" :rowSelection="{
|
||||
type: 'radio',
|
||||
selectedRowKeys: _selectedRowKeys,
|
||||
onSelect: onSelectChange,
|
||||
}" @cancelSelect="cancelSelect" :params="params">
|
||||
<template #registryTime="slotProps">
|
||||
{{
|
||||
slotProps.registryTime
|
||||
? moment(slotProps.registryTime).format(
|
||||
'YYYY-MM-DD HH:mm:ss',
|
||||
)
|
||||
: ''
|
||||
? moment(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)"
|
||||
/>
|
||||
<j-badge :text="slotProps.state.text" :status="statusMap.get(slotProps.state.value)" />
|
||||
</template>
|
||||
</j-pro-table>
|
||||
</div>
|
||||
|
@ -57,7 +31,7 @@
|
|||
<script setup lang="ts">
|
||||
import { queryUnbounded, bind } from '@/api/iot-card/cardManagement';
|
||||
import moment from 'moment';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { message } from 'jetlinks-ui-components';
|
||||
|
||||
const emit = defineEmits(['change']);
|
||||
|
||||
|
@ -141,7 +115,7 @@ const handleOk = () => {
|
|||
bind(props.cardId, _selectedRowKeys.value[0])
|
||||
.then((resp: any) => {
|
||||
if (resp.status === 200) {
|
||||
message.success('操作成功');
|
||||
message.success('操作成功')
|
||||
emit('change', true);
|
||||
}
|
||||
})
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<!-- 导入 -->
|
||||
<a-modal
|
||||
<j-modal
|
||||
:maskClosable="false"
|
||||
:visible="true"
|
||||
title="导出"
|
||||
|
@ -10,19 +10,19 @@
|
|||
@cancel="handleCancel"
|
||||
>
|
||||
<div style="margin-top: 10px">
|
||||
<a-space>
|
||||
<j-space>
|
||||
<span>文件格式:</span>
|
||||
<a-radio-group
|
||||
<j-radio-group
|
||||
v-model:value="type"
|
||||
placeholder="请选择文件格式"
|
||||
button-style="solid"
|
||||
>
|
||||
<a-radio-button value="xlsx">xlsx</a-radio-button>
|
||||
<a-radio-button value="csv">csv</a-radio-button>
|
||||
</a-radio-group>
|
||||
</a-space>
|
||||
<j-radio-button value="xlsx">xlsx</j-radio-button>
|
||||
<j-radio-button value="csv">csv</j-radio-button>
|
||||
</j-radio-group>
|
||||
</j-space>
|
||||
</div>
|
||||
</a-modal>
|
||||
</j-modal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
|
|
@ -82,7 +82,9 @@ import { BASE_API_PATH, TOKEN_KEY } from '@/utils/variable';
|
|||
import { LocalStore } from '@/utils/comm';
|
||||
import { downloadFile, downloadFileByUrl } from '@/utils/utils';
|
||||
import { queryPlatformNoPage, _import ,exportCard} from '@/api/iot-card/cardManagement';
|
||||
import { message } from 'ant-design-vue';
|
||||
// import { message } from 'ant-design-vue';
|
||||
import { message } from 'jetlinks-ui-components';
|
||||
|
||||
|
||||
const emit = defineEmits(['close']);
|
||||
|
||||
|
@ -125,10 +127,10 @@ const fileChange = (info: any) => {
|
|||
_import(modelRef.configId, { fileUrl: r.result })
|
||||
.then((resp: any) => {
|
||||
totalCount.value = resp.result.total;
|
||||
message.success('导入成功');
|
||||
message.success('导入成功')
|
||||
})
|
||||
.catch((err) => {
|
||||
message.error(err.response.data.message || '导入失败');
|
||||
message.error(err.response.data.message || '导入失败')
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
|
|
|
@ -98,7 +98,7 @@ import {
|
|||
add,
|
||||
edit,
|
||||
} from '@/api/iot-card/cardManagement';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { message } from 'jetlinks-ui-components';
|
||||
import { OperatorList, TypeList } from '@/views/iot-card/data';
|
||||
|
||||
const emit = defineEmits(['change']);
|
||||
|
@ -236,7 +236,7 @@ const handleOk = () => {
|
|||
: await edit(toRaw(modelRef));
|
||||
btnLoading.value = false;
|
||||
if (resp.status === 200) {
|
||||
message.success('操作成功!');
|
||||
message.success('操作成功')
|
||||
emit('change', true);
|
||||
formRef.value.resetFields();
|
||||
}
|
||||
|
|
|
@ -9,9 +9,9 @@
|
|||
}" @cancelSelect="cancelSelect" :params="params" :gridColumn="3">
|
||||
<template #headerTitle>
|
||||
<j-space>
|
||||
<!-- <a-button type="primary" @click="handleAdd">
|
||||
<!-- <a-button type="primary" @click="handleAdd">
|
||||
<AIcon type="PlusOutlined" />新增
|
||||
</a-button> -->
|
||||
</a-button> -->
|
||||
<PermissionButton @click="handleAdd" :hasPermission="'iot-card/CardManagement:add'" type="primary">
|
||||
<AIcon type="PlusOutlined" />新增
|
||||
</PermissionButton>
|
||||
|
@ -50,7 +50,7 @@
|
|||
onConfirm: handleStop,
|
||||
}" ghost type="primary" :hasPermission="'iot-card/CardManagement:action'">
|
||||
<AIcon type="StopOutlined" />
|
||||
批量停用
|
||||
批量停用
|
||||
</PermissionButton>
|
||||
</j-menu-item>
|
||||
<j-menu-item>
|
||||
|
@ -58,8 +58,8 @@
|
|||
title: '确认复机吗?',
|
||||
onConfirm: handleResumption,
|
||||
}" ghost type="primary" :hasPermission="'iot-card/CardManagement:action'">
|
||||
<AIcon type="PoweroffOutlined" />
|
||||
批量复机
|
||||
<AIcon type="PoweroffOutlined" />
|
||||
批量复机
|
||||
</PermissionButton>
|
||||
</j-menu-item>
|
||||
<j-menu-item>
|
||||
|
@ -67,17 +67,17 @@
|
|||
title: '确认同步状态吗?',
|
||||
onConfirm: handleSync,
|
||||
}" ghost type="primary" :hasPermission="'iot-card/CardManagement:sync'">
|
||||
<AIcon type="SwapOutlined" />
|
||||
同步状态
|
||||
<AIcon type="SwapOutlined" />
|
||||
同步状态
|
||||
</PermissionButton>
|
||||
</j-menu-item>
|
||||
<j-menu-item v-if="_selectedRowKeys.length > 0">
|
||||
<PermissionButton :popConfirm="{
|
||||
title: '确认删除吗?',
|
||||
onConfirm: handelRemove,
|
||||
}" ghost type="primary" :hasPermission="'iot-card/CardManagement:delete'">
|
||||
<AIcon type="SwapOutlined" />
|
||||
批量删除
|
||||
}" ghost type="primary" :hasPermission="'iot-card/CardManagement:delete'">
|
||||
<AIcon type="SwapOutlined" />
|
||||
批量删除
|
||||
</PermissionButton>
|
||||
</j-menu-item>
|
||||
</j-menu>
|
||||
|
@ -188,7 +188,7 @@
|
|||
</template>
|
||||
</a-button>
|
||||
</template>
|
||||
</j-tooltip> -->
|
||||
</j-tooltip> -->
|
||||
</template>
|
||||
</CardBox>
|
||||
</template>
|
||||
|
@ -248,22 +248,14 @@
|
|||
</template>
|
||||
<template #action="slotProps">
|
||||
<j-space :size="16">
|
||||
<template
|
||||
v-for="i in getActions(slotProps,'table')"
|
||||
:key="i.key"
|
||||
>
|
||||
<PermissionButton
|
||||
:disabled="i.disabled"
|
||||
:popConfirm="i.popConfirm"
|
||||
:tooltip="{
|
||||
...i.tooltip,
|
||||
}"
|
||||
@click="i.onClick"
|
||||
type="link"
|
||||
style="padding: 0px"
|
||||
:hasPermission="'iot-card/CardManagement:' + i.key"
|
||||
>
|
||||
<template #icon><AIcon :type="i.icon" /></template>
|
||||
<template v-for="i in getActions(slotProps, 'table')" :key="i.key">
|
||||
<PermissionButton :disabled="i.disabled" :popConfirm="i.popConfirm" :tooltip="{
|
||||
...i.tooltip,
|
||||
}" @click="i.onClick" type="link" style="padding: 0px"
|
||||
:hasPermission="'iot-card/CardManagement:' + i.key">
|
||||
<template #icon>
|
||||
<AIcon :type="i.icon" />
|
||||
</template>
|
||||
</PermissionButton>
|
||||
</template>
|
||||
</j-space>
|
||||
|
@ -297,7 +289,7 @@ import {
|
|||
removeCards,
|
||||
unbind,
|
||||
} from '@/api/iot-card/cardManagement';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { message } from 'jetlinks-ui-components';
|
||||
import type { CardManagement } from './typing';
|
||||
import { getImage } from '@/utils/comm';
|
||||
import BindDevice from './BindDevice.vue';
|
||||
|
@ -513,7 +505,7 @@ const getActions = (
|
|||
onConfirm: async () => {
|
||||
unbind(data.id).then((resp: any) => {
|
||||
if (resp.status === 200) {
|
||||
message.success('操作成功');
|
||||
message.success('操作成功')
|
||||
cardManageRef.value?.reload();
|
||||
}
|
||||
});
|
||||
|
@ -562,21 +554,21 @@ const getActions = (
|
|||
if (data.cardStateType?.value === 'toBeActivated') {
|
||||
changeDeploy(data.id).then((resp) => {
|
||||
if (resp.status === 200) {
|
||||
message.success('操作成功');
|
||||
message.success('操作成功')
|
||||
cardManageRef.value?.reload();
|
||||
}
|
||||
});
|
||||
} else if (data.cardStateType?.value === 'deactivate') {
|
||||
resumption(data.id).then((resp) => {
|
||||
if (resp.status === 200) {
|
||||
message.success('操作成功');
|
||||
message.success('操作成功')
|
||||
cardManageRef.value?.reload();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
unDeploy(data.id).then((resp) => {
|
||||
if (resp.status === 200) {
|
||||
message.success('操作成功');
|
||||
message.success('操作成功')
|
||||
cardManageRef.value?.reload();
|
||||
}
|
||||
});
|
||||
|
@ -597,7 +589,7 @@ const getActions = (
|
|||
onConfirm: async () => {
|
||||
const resp: any = await del(data.id);
|
||||
if (resp.status === 200) {
|
||||
message.success('操作成功!');
|
||||
message.success('操作成功')
|
||||
cardManageRef.value?.reload();
|
||||
} else {
|
||||
message.error('操作失败!');
|
||||
|
@ -692,7 +684,7 @@ const handleStop = () => {
|
|||
) {
|
||||
unDeployBatch(_selectedRowKeys.value).then((res: any) => {
|
||||
if (res.status === 200) {
|
||||
message.success('操作成功');
|
||||
message.success('操作成功')
|
||||
}
|
||||
});
|
||||
} else {
|
||||
|
@ -710,7 +702,7 @@ const handleResumption = () => {
|
|||
) {
|
||||
resumptionBatch(_selectedRowKeys.value).then((res: any) => {
|
||||
if (res.status === 200) {
|
||||
message.success('操作成功');
|
||||
message.success('操作成功')
|
||||
}
|
||||
});
|
||||
} else {
|
||||
|
@ -736,7 +728,7 @@ const handleSync = () => {
|
|||
const handelRemove = async () => {
|
||||
const resp = await removeCards(_selectedRow.value);
|
||||
if (resp.status === 200) {
|
||||
message.success('操作成功!');
|
||||
message.success('操作成功')
|
||||
_selectedRowKeys.value = [];
|
||||
_selectedRow.value = [];
|
||||
cardManageRef.value?.reload();
|
||||
|
|
|
@ -133,7 +133,7 @@
|
|||
import { getImage } from '@/utils/comm';
|
||||
import PlatformType from '@/views/iot-card/components/PlatformType.vue';
|
||||
import { queryById, save, update } from '@/api/iot-card/platform';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { message } from 'jetlinks-ui-components';
|
||||
import Doc from '../doc/index.vue';
|
||||
|
||||
const router = useRouter();
|
||||
|
|
|
@ -1,41 +1,25 @@
|
|||
<!-- 平台对接 -->
|
||||
<template>
|
||||
<page-container>
|
||||
<Search
|
||||
:columns="columns"
|
||||
target="platform-search"
|
||||
@search="handleSearch"
|
||||
/>
|
||||
<j-pro-table
|
||||
ref="platformRef"
|
||||
:columns="columns"
|
||||
:request="queryList"
|
||||
:defaultParams="{ sorts: [{ name: 'createTime', order: 'desc' }] }"
|
||||
:params="params"
|
||||
:gridColumn="3"
|
||||
>
|
||||
<Search :columns="columns" target="platform-search" @search="handleSearch" />
|
||||
<j-pro-table ref="platformRef" :columns="columns" :request="queryList"
|
||||
:defaultParams="{ sorts: [{ name: 'createTime', order: 'desc' }] }" :params="params" :gridColumn="3">
|
||||
<template #headerTitle>
|
||||
<j-space>
|
||||
<!-- <j-button type="primary" @click="handleAdd">
|
||||
<!-- <j-button type="primary" @click="handleAdd">
|
||||
<AIcon type="PlusOutlined" />新增
|
||||
</j-button> -->
|
||||
</j-button> -->
|
||||
<PermissionButton @click="handleAdd" :hasPermission="'iot-card/Platform:add'" type="primary">
|
||||
<AIcon type="PlusOutlined" />新增
|
||||
</PermissionButton>
|
||||
</j-space>
|
||||
</template>
|
||||
<template #card="slotProps">
|
||||
<CardBox
|
||||
:value="slotProps"
|
||||
:actions="getActions(slotProps, 'card')"
|
||||
v-bind="slotProps"
|
||||
:status="slotProps.state.value"
|
||||
:statusText="slotProps.state.text"
|
||||
:statusNames="{
|
||||
<CardBox :value="slotProps" :actions="getActions(slotProps, 'card')" v-bind="slotProps"
|
||||
:status="slotProps.state.value" :statusText="slotProps.state.text" :statusNames="{
|
||||
enabled: 'success',
|
||||
disabled: 'error',
|
||||
}"
|
||||
>
|
||||
}">
|
||||
<template #img>
|
||||
<slot name="img">
|
||||
<img :src="getImage('/iot-card/iot-card-bg.png')" />
|
||||
|
@ -72,33 +56,22 @@
|
|||
</CardBox>
|
||||
</template>
|
||||
<template #state="slotProps">
|
||||
<j-badge
|
||||
:text="slotProps.state.text"
|
||||
:status="
|
||||
slotProps.state.value === 'disabled'
|
||||
? 'error'
|
||||
: 'success'
|
||||
"
|
||||
/>
|
||||
<j-badge :text="slotProps.state.text" :status="
|
||||
slotProps.state.value === 'disabled'
|
||||
? 'error'
|
||||
: 'success'
|
||||
" />
|
||||
</template>
|
||||
<template #action="slotProps">
|
||||
<j-space :size="16">
|
||||
<template
|
||||
v-for="i in getActions(slotProps,'table')"
|
||||
:key="i.key"
|
||||
>
|
||||
<PermissionButton
|
||||
:disabled="i.disabled"
|
||||
:popConfirm="i.popConfirm"
|
||||
:tooltip="{
|
||||
...i.tooltip,
|
||||
}"
|
||||
@click="i.onClick"
|
||||
type="link"
|
||||
style="padding: 0px"
|
||||
:hasPermission="'iot-card/Platform:' + i.key"
|
||||
>
|
||||
<template #icon><AIcon :type="i.icon" /></template>
|
||||
<template v-for="i in getActions(slotProps, 'table')" :key="i.key">
|
||||
<PermissionButton :disabled="i.disabled" :popConfirm="i.popConfirm" :tooltip="{
|
||||
...i.tooltip,
|
||||
}" @click="i.onClick" type="link" style="padding: 0px"
|
||||
:hasPermission="'iot-card/Platform:' + i.key">
|
||||
<template #icon>
|
||||
<AIcon :type="i.icon" />
|
||||
</template>
|
||||
</PermissionButton>
|
||||
</template>
|
||||
</j-space>
|
||||
|
@ -110,7 +83,7 @@
|
|||
<script setup lang="ts">
|
||||
import { getImage } from '@/utils/comm';
|
||||
import type { ActionsType } from '@/components/Table';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { message } from 'jetlinks-ui-components';
|
||||
import { queryList, update, del } from '@/api/iot-card/platform';
|
||||
import { useMenuStore } from 'store/menu'
|
||||
const menuStory = useMenuStore()
|
||||
|
@ -192,8 +165,8 @@ const getActions = (
|
|||
},
|
||||
icon: 'EditOutlined',
|
||||
onClick: () => {
|
||||
// router.push(`/iot-card/Platform/detail/${data.id}`);
|
||||
menuStory.jumpPage('iot-card/Platform/Detail',{id:data.id});
|
||||
// router.push(`/iot-card/Platform/detail/${data.id}`);
|
||||
menuStory.jumpPage('iot-card/Platform/Detail', { id: data.id });
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -207,9 +180,8 @@ const getActions = (
|
|||
? 'StopOutlined'
|
||||
: 'PlayCircleOutlined',
|
||||
popConfirm: {
|
||||
title: `确认${
|
||||
data.state.value === 'enabled' ? '禁用' : '启用'
|
||||
}?`,
|
||||
title: `确认${data.state.value === 'enabled' ? '禁用' : '启用'
|
||||
}?`,
|
||||
okText: ' 确定',
|
||||
cancelText: '取消',
|
||||
onConfirm: () => {
|
||||
|
@ -266,7 +238,7 @@ const handleSearch = (e: any) => {
|
|||
* 新增
|
||||
*/
|
||||
const handleAdd = () => {
|
||||
menuStory.jumpPage('iot-card/Platform/Detail',{id:'add'})
|
||||
menuStory.jumpPage('iot-card/Platform/Detail', { id: 'add' })
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@
|
|||
|
||||
<script lang="ts" setup>
|
||||
import { queryPlatformNoPage, recharge } from '@/api/iot-card/cardManagement';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { message } from 'jetlinks-ui-components';
|
||||
import { PaymentMethod } from '@/views/iot-card/data';
|
||||
|
||||
const emit = defineEmits(['change']);
|
||||
|
@ -167,7 +167,7 @@ const handleOk = () => {
|
|||
btnLoading.value = false;
|
||||
if (resp.status === 200) {
|
||||
if (resp.result === '失败') {
|
||||
message.error('缴费失败');
|
||||
message.error('缴费失败')
|
||||
} else {
|
||||
window.open(resp.result);
|
||||
}
|
||||
|
@ -176,6 +176,7 @@ const handleOk = () => {
|
|||
}
|
||||
})
|
||||
.catch((err: any) => {
|
||||
btnLoading.value=false
|
||||
console.log('error', err);
|
||||
});
|
||||
};
|
||||
|
|
|
@ -30,10 +30,19 @@ const deviceId = ref('');
|
|||
const channelId = ref('');
|
||||
const player = ref();
|
||||
|
||||
/**
|
||||
* 获取视频链接
|
||||
* @param dId
|
||||
* @param cId
|
||||
*/
|
||||
const getMediaUrl = (dId: string, cId: string): string => {
|
||||
return channelApi.ptzStart(dId, cId, 'mp4');
|
||||
};
|
||||
|
||||
/**
|
||||
* 点击左侧摄像头, 播放对应视频
|
||||
* @param e
|
||||
*/
|
||||
const mediaStart = (e: { cId: string; dId: string }) => {
|
||||
channelId.value = e.cId;
|
||||
deviceId.value = e.dId;
|
||||
|
|
|
@ -41,8 +41,12 @@ interface DataNode {
|
|||
children?: DataNode[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 点击节点
|
||||
* @param _
|
||||
* @param param1
|
||||
*/
|
||||
const onSelect = (_: any, { node }: any) => {
|
||||
console.log('node: ', node);
|
||||
emit('onSelect', { dId: node.deviceId, cId: node.channelId });
|
||||
};
|
||||
|
||||
|
@ -76,6 +80,12 @@ const getDeviceList = async () => {
|
|||
};
|
||||
getDeviceList();
|
||||
|
||||
/**
|
||||
* 更新数据
|
||||
* @param list
|
||||
* @param key
|
||||
* @param children
|
||||
*/
|
||||
const updateTreeData = (
|
||||
list: DataNode[],
|
||||
key: any,
|
||||
|
@ -129,12 +139,15 @@ const getChildren = (key: any, params: any): Promise<any> => {
|
|||
});
|
||||
}, 50);
|
||||
}
|
||||
console.log('treeData.value: ', treeData.value);
|
||||
resolve(res.result);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 异步加载子节点数据
|
||||
* @param param0
|
||||
*/
|
||||
const onLoadData = ({ key, children }: any): Promise<void> => {
|
||||
return new Promise(async (resolve) => {
|
||||
if (children) {
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
>
|
||||
<Search :columns="columns" @search="handleSearch"></Search>
|
||||
<div style="height: 500px; overflow-y: auto">
|
||||
<JTable
|
||||
<JProTable
|
||||
model="CARD"
|
||||
:request="query"
|
||||
:rowSelection="{
|
||||
|
@ -76,7 +76,7 @@
|
|||
</template>
|
||||
</SceneCard>
|
||||
</template>
|
||||
</JTable>
|
||||
</JProTable>
|
||||
</div>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<JTable
|
||||
<JProTable
|
||||
model="CARD"
|
||||
:request="query"
|
||||
:defaultParams="{
|
||||
|
@ -71,7 +71,7 @@
|
|||
</template>
|
||||
</SceneCard>
|
||||
</template>
|
||||
</JTable>
|
||||
</JProTable>
|
||||
<Save
|
||||
:id="id"
|
||||
:type="configurationData.current?.targetType"
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
target="device-instance"
|
||||
@search="handleSearch"
|
||||
></Search>
|
||||
<JTable
|
||||
<JProTable
|
||||
:columns="columns"
|
||||
:request="queryList"
|
||||
:gridColumn="3"
|
||||
|
@ -155,7 +155,7 @@
|
|||
</template>
|
||||
</a-space>
|
||||
</template>
|
||||
</JTable>
|
||||
</JProTable>
|
||||
</div>
|
||||
</page-container>
|
||||
</template>
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
v-if="props.type === 'org'"
|
||||
@search="search"
|
||||
></Search>
|
||||
<JTable
|
||||
<JProTable
|
||||
:columns="columns"
|
||||
:request="handleSearch"
|
||||
:params="params"
|
||||
|
@ -115,7 +115,7 @@
|
|||
</template>
|
||||
</CardBox>
|
||||
</template>
|
||||
</JTable>
|
||||
</JProTable>
|
||||
<SolveComponent :data="data" v-if="data.solveVisible" @closeSolve="closeSolve"/>
|
||||
<SolveLog :data="data.current" v-if="data.logVisible" @closeLog="closeLog"/>
|
||||
</div>
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
<template>
|
||||
|
||||
<div>
|
||||
<Action :thenOptions="data.branches ? data?.branches[0].then : []" :name="0" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'inex'
|
||||
}
|
||||
<script lang="ts" setup>
|
||||
import { useSceneStore } from '@/store/scene'
|
||||
import Action from '../action/index.vue'
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
const sceneStore = useSceneStore()
|
||||
const { data } = storeToRefs(sceneStore)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<a-input-number
|
||||
style="max-width: 220px"
|
||||
placeholder="请输入时间"
|
||||
v-model:value="value"
|
||||
v-model:value="_value"
|
||||
:precision="3"
|
||||
:min="0"
|
||||
:max="6535"
|
||||
|
@ -32,6 +32,18 @@
|
|||
<script lang="ts" setup>
|
||||
import { onlyMessage } from '@/utils/comm';
|
||||
|
||||
const props = defineProps({
|
||||
value: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {
|
||||
time: 0,
|
||||
unit: 'seconds',
|
||||
};
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const timeUnitEnum = {
|
||||
seconds: '秒',
|
||||
minutes: '分',
|
||||
|
@ -40,24 +52,38 @@ const timeUnitEnum = {
|
|||
|
||||
const emit = defineEmits(['cancel', 'save']);
|
||||
|
||||
const value = ref<number>(0);
|
||||
const unit = ref<'seconds' | 'minutes' | 'hours'>('seconds');
|
||||
const _value = ref<number>(props.value.time);
|
||||
const unit = ref<'seconds' | 'minutes' | 'hours'>(
|
||||
props.value?.unit || 'seconds',
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.value,
|
||||
(newVal) => {
|
||||
_value.value = newVal?.time || 0
|
||||
unit.value = newVal?.unit || 'seconds'
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
deep: true,
|
||||
},
|
||||
);
|
||||
|
||||
const onCancel = () => {
|
||||
emit('cancel');
|
||||
};
|
||||
const onOk = () => {
|
||||
if (unref(value) || unref(value) === 0) {
|
||||
if (unref(_value) || unref(_value) === 0) {
|
||||
} else {
|
||||
onlyMessage('请输入时间', 'error');
|
||||
}
|
||||
emit(
|
||||
'save',
|
||||
{
|
||||
time: value.value,
|
||||
time: _value.value,
|
||||
unit: unit.value,
|
||||
},
|
||||
{ name: `${value.value} ${timeUnitEnum[unit.value]}后,执行后续动作` },
|
||||
{ name: `${_value.value} ${timeUnitEnum[unit.value]}后,执行后续动作` },
|
||||
);
|
||||
};
|
||||
</script>
|
|
@ -0,0 +1,261 @@
|
|||
<template>
|
||||
<Search
|
||||
:columns="columns"
|
||||
type='simple'
|
||||
@search="handleSearch"
|
||||
class='search'
|
||||
target="scene-triggrt-device-device"
|
||||
/>
|
||||
<a-divider style='margin: 0' />
|
||||
<j-pro-table
|
||||
ref='actionRef'
|
||||
model='CARD'
|
||||
:columns='columns'
|
||||
:params='params'
|
||||
:request='productQuery'
|
||||
:gridColumn='2'
|
||||
:gridColumns='[2,2,2]'
|
||||
:bodyStyle='{
|
||||
paddingRight: 0,
|
||||
paddingLeft: 0
|
||||
}'
|
||||
>
|
||||
<template #card="slotProps">
|
||||
<CardBox
|
||||
:value='slotProps'
|
||||
:active="rowKey === slotProps.id"
|
||||
:status="slotProps.state"
|
||||
:statusText="slotProps.state === 1 ? '正常' : '禁用'"
|
||||
:statusNames="{ 1: 'success', 0: 'error', }"
|
||||
@click="handleClick"
|
||||
>
|
||||
<template #img>
|
||||
<slot name="img">
|
||||
<img width='88' height='88' :src="slotProps.photoUrl || getImage('/device-product.png')" />
|
||||
</slot>
|
||||
</template>
|
||||
<template #content>
|
||||
<div style='width: calc(100% - 100px)'>
|
||||
<Ellipsis>
|
||||
<span style="font-size: 16px;font-weight: 600" >
|
||||
{{ slotProps.name }}
|
||||
</span>
|
||||
</Ellipsis>
|
||||
</div>
|
||||
<a-row>
|
||||
<a-col :span="12">
|
||||
<div class="card-item-content-text">
|
||||
设备类型
|
||||
</div>
|
||||
<div>直连设备</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</template>
|
||||
</CardBox>
|
||||
</template>
|
||||
</j-pro-table>
|
||||
</template>
|
||||
|
||||
<script setup lang='ts' name='Product'>
|
||||
import { getProviders, queryGatewayList, queryProductList } from '@/api/device/product'
|
||||
import { queryTree } from '@/api/device/category'
|
||||
import { getTreeData_api } from '@/api/system/department'
|
||||
import { isNoCommunity } from '@/utils/utils'
|
||||
import { getImage } from '@/utils/comm'
|
||||
|
||||
type Emit = {
|
||||
(e: 'update:rowKey', data: string): void
|
||||
(e: 'update:detail', data: string): void
|
||||
(e: 'change', data: string): void
|
||||
}
|
||||
|
||||
const actionRef = ref()
|
||||
const params = ref({})
|
||||
const props = defineProps({
|
||||
rowKey: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
detail: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits<Emit>()
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'id',
|
||||
width: 300,
|
||||
ellipsis: true,
|
||||
fixed: 'left',
|
||||
search: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
width: 200,
|
||||
ellipsis: true,
|
||||
search: {
|
||||
type: 'string',
|
||||
first: true
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '网关类型',
|
||||
dataIndex: 'accessProvider',
|
||||
width: 150,
|
||||
ellipsis: true,
|
||||
hideInTable: true,
|
||||
search: {
|
||||
type: 'select',
|
||||
options: () => getProviders().then((resp: any) => {
|
||||
if (isNoCommunity) {
|
||||
return (resp?.result || []).map((item: any) => ({
|
||||
label: item.name,
|
||||
value: item.id
|
||||
}))
|
||||
} else {
|
||||
return (resp?.result || []).filter((item: any) => [
|
||||
'mqtt-server-gateway',
|
||||
'http-server-gateway',
|
||||
'mqtt-client-gateway',
|
||||
'tcp-server-gateway',
|
||||
].includes(item.id))
|
||||
.map((item: any) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}))
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '接入方式',
|
||||
dataIndex: 'accessName',
|
||||
width: 150,
|
||||
ellipsis: true,
|
||||
search: {
|
||||
type: 'select',
|
||||
options: () => queryGatewayList().then((resp: any) =>
|
||||
resp.result.map((item: any) => ({
|
||||
label: item.name, value: item.id
|
||||
}))
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '设备类型',
|
||||
dataIndex: 'deviceType',
|
||||
width: 150,
|
||||
search: {
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: '直连设备', value: 'device' },
|
||||
{ label: '网关子设备', value: 'childrenDevice' },
|
||||
{ label: '网关设备', value: 'gateway' },
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'state',
|
||||
width: '90px',
|
||||
search: {
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: '禁用', value: 0 },
|
||||
{ label: '正常', value: 1 },
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '说明',
|
||||
dataIndex: 'describe',
|
||||
ellipsis: true,
|
||||
width: 300,
|
||||
},
|
||||
{
|
||||
dataIndex: 'classifiedId',
|
||||
title: '分类',
|
||||
hideInTable: true,
|
||||
search: {
|
||||
type: 'treeSelect',
|
||||
options: queryTree({ paging: false }).then(resp => resp.result),
|
||||
componentProps: {
|
||||
fieldNames: {
|
||||
label: 'name',
|
||||
value: 'id',
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
dataIndex: 'id$dim-assets',
|
||||
title: '所属组织',
|
||||
hideInTable: true,
|
||||
search: {
|
||||
type: 'treeSelect',
|
||||
options: getTreeData_api({ paging: false }).then((resp: any) => {
|
||||
const formatValue = (list: any[]) => {
|
||||
return list.map((item: any) => {
|
||||
if (item.children) {
|
||||
item.children = formatValue(item.children);
|
||||
}
|
||||
return {
|
||||
...item,
|
||||
value: JSON.stringify({
|
||||
assetType: 'product',
|
||||
targets: [
|
||||
{
|
||||
type: 'org',
|
||||
id: item.id,
|
||||
},
|
||||
],
|
||||
}),
|
||||
}
|
||||
})
|
||||
}
|
||||
return formatValue(resp.result)
|
||||
}),
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
const handleSearch = (p: any) => {
|
||||
params.value = p
|
||||
}
|
||||
|
||||
const productQuery = (p: any) => {
|
||||
const sorts: any = [];
|
||||
|
||||
if (props.rowKey) {
|
||||
sorts.push({
|
||||
name: 'id',
|
||||
value: props.rowKey,
|
||||
});
|
||||
}
|
||||
sorts.push({ name: 'createTime', order: 'desc' });
|
||||
p.sorts = sorts
|
||||
return queryProductList(p)
|
||||
}
|
||||
|
||||
const handleClick = (detail: any) => {
|
||||
emit('update:rowKey', detail.id)
|
||||
emit('update:detail', detail)
|
||||
emit('change', detail)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang='less'>
|
||||
.search {
|
||||
margin-bottom: 0;
|
||||
padding-right: 0px;
|
||||
padding-left: 0px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,14 @@
|
|||
|
||||
<template>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang='ts' name="Device">
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang='less'>
|
||||
|
||||
</style>
|
|
@ -1,22 +1,104 @@
|
|||
<template>
|
||||
<j-modal
|
||||
title="执行动作"
|
||||
visible
|
||||
:width="860"
|
||||
@cancel="onCancel"
|
||||
@ok="onOk"
|
||||
:maskClosable="false"
|
||||
>
|
||||
device
|
||||
<j-modal title="执行动作" visible :width="860" @cancel="onCancel" @ok="save" :maskClosable="false">
|
||||
<j-steps :current='DeviceModel.current' @change='stepChange'>
|
||||
<j-step>
|
||||
<template #title>选择产品</template>
|
||||
</j-step>
|
||||
<j-step>
|
||||
<template #title>选择设备</template>
|
||||
</j-step>
|
||||
<j-step>
|
||||
<template #title>执行动作</template>
|
||||
</j-step>
|
||||
</j-steps>
|
||||
<j-divider style='margin-bottom: 10px;' />
|
||||
<div class='steps-content'>
|
||||
<Product v-if='DeviceModel.current === 0' v-model:rowKey='DeviceModel.productId'
|
||||
v-model:detail='DeviceModel.productDetail' />
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class='steps-action'>
|
||||
<j-button v-if='DeviceModel.current === 0' @click='cancel'>取消</j-button>
|
||||
<j-button v-else @click='prev'>上一步</j-button>
|
||||
<j-button type='primary' v-if='DeviceModel.current < 2' @click='saveClick'>下一步</j-button>
|
||||
<j-button type='primary' v-else @click='saveClick'>确定</j-button>
|
||||
</div>
|
||||
</template>
|
||||
</j-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const emit = defineEmits(['cancel', 'save']);
|
||||
const onCancel = () => {
|
||||
import { DeviceModelType } from './typings';
|
||||
import Product from './Product.vue';
|
||||
import { onlyMessage } from '@/utils/comm';
|
||||
|
||||
type Emit = {
|
||||
(e: 'cancel'): void
|
||||
(e: 'save', data: any, options: Record<string, any>): void
|
||||
}
|
||||
|
||||
|
||||
const DeviceModel = reactive<DeviceModelType>({
|
||||
steps: [],
|
||||
current: 0,
|
||||
productId: '',
|
||||
deviceId: '',
|
||||
productDetail: {},
|
||||
device: {},
|
||||
deviceDetail: {},
|
||||
options: {},
|
||||
selector: 'fixed',
|
||||
selectorValues: [],
|
||||
upperKey: '',
|
||||
source: 'fixed',
|
||||
relationName: '',
|
||||
message: {},
|
||||
propertiesName: '',
|
||||
propertiesValue: '',
|
||||
columns: [],
|
||||
actionName: '',
|
||||
tagList: [],
|
||||
})
|
||||
|
||||
const emit = defineEmits<Emit>()
|
||||
const cancel = () => {
|
||||
emit('cancel');
|
||||
};
|
||||
const onOk = () => {
|
||||
emit('save');
|
||||
const save = async(step?: number) => {
|
||||
let _step = step !== undefined ? step : DeviceModel.current
|
||||
if (_step === 0) {
|
||||
DeviceModel.productId ? DeviceModel.current = 1 : onlyMessage('请选择产品', 'error')
|
||||
} else if (_step === 1) {
|
||||
|
||||
} else {
|
||||
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
const stepChange = (step: number) => {
|
||||
if (step !== 0) {
|
||||
save(step - 1)
|
||||
} else {
|
||||
DeviceModel.current = 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const prev = () => {
|
||||
DeviceModel.current = DeviceModel.current - 1
|
||||
}
|
||||
const saveClick = () => save()
|
||||
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.steps-steps {
|
||||
width: 100%;
|
||||
margin-bottom: 17px;
|
||||
padding-bottom: 17px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.steps-content {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,28 @@
|
|||
import { ProductItem } from '@/views/device/Product/typings';
|
||||
import { ActionsDeviceProps } from '../../../typings';
|
||||
|
||||
type DeviceModelType = {
|
||||
steps: {
|
||||
key: string;
|
||||
title: string;
|
||||
content: React.ReactNode;
|
||||
}[];
|
||||
current: number;
|
||||
productId: string;
|
||||
deviceId: string;
|
||||
productDetail: ProductItem | any;
|
||||
device: Partial<ActionsDeviceProps>;
|
||||
deviceDetail: any;
|
||||
options: any;
|
||||
selector: string;
|
||||
selectorValues: any;
|
||||
upperKey: string;
|
||||
source: string;
|
||||
relationName: string;
|
||||
message: any;
|
||||
propertiesName: string;
|
||||
propertiesValue: string | any;
|
||||
columns: string[];
|
||||
actionName: string;
|
||||
tagList: any[];
|
||||
}
|
|
@ -1,3 +1,631 @@
|
|||
<template>
|
||||
<div>ITEM</div>
|
||||
</template>
|
||||
<div class="actions-item-warp">
|
||||
<div class="actions-item">
|
||||
<div class="item-options-warp">
|
||||
<div class="item-options-type" @click="onAdd">
|
||||
<img
|
||||
style="width: 18px"
|
||||
:src="
|
||||
iconMap.get(
|
||||
data?.executor === 'alarm'
|
||||
? data?.alarm?.mode
|
||||
: data?.executor,
|
||||
)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="item-options-content"
|
||||
v-if="data?.executor === 'alarm'"
|
||||
>
|
||||
<template v-if="data?.alarm?.mode === 'trigger'">
|
||||
满足条件后将触发<a-button
|
||||
type="link"
|
||||
@click.stop="triggerVisible = true"
|
||||
>关联此场景的告警</a-button
|
||||
>
|
||||
</template>
|
||||
<template v-else>
|
||||
满足条件后将解除<a-button
|
||||
type="link"
|
||||
@click.stop="triggerVisible = true"
|
||||
>关联此场景的告警</a-button
|
||||
>
|
||||
</template>
|
||||
</div>
|
||||
<div
|
||||
class="item-options-content"
|
||||
v-else-if="data?.executor === 'notify'"
|
||||
@click="onType('notify')"
|
||||
>
|
||||
<template v-if="data?.notify?.notifyType === 'dingTalk'">
|
||||
<template
|
||||
v-if="options?.provider === 'dingTalkRobotWebHook'"
|
||||
>
|
||||
<div>
|
||||
通过<span class="notify-text-highlight"
|
||||
>群机器人消息</span
|
||||
>
|
||||
发送
|
||||
<span class="notify-text-highlight">
|
||||
{{
|
||||
options?.templateName ||
|
||||
data?.notify?.templateId
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div>
|
||||
通过
|
||||
<span class="notify-text-highlight">
|
||||
<img
|
||||
style="width: 18px"
|
||||
:src="
|
||||
itemNotifyIconMap.get(
|
||||
data?.notify?.notifyType,
|
||||
)
|
||||
"
|
||||
/>
|
||||
钉钉
|
||||
</span>
|
||||
向<span class="notify-text-highlight">{{
|
||||
options?.orgName || ''
|
||||
}}</span>
|
||||
<span class="notify-text-highlight">{{
|
||||
options?.sendTo || ''
|
||||
}}</span>
|
||||
发送
|
||||
<span class="notify-text-highlight">
|
||||
{{
|
||||
options?.templateName ||
|
||||
data?.notify?.templateId
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
<template v-else-if="data?.notify?.notifyType === 'weixin'">
|
||||
<div>
|
||||
通过
|
||||
<span class="notify-text-highlight">
|
||||
<img
|
||||
style="width: 18px"
|
||||
:src="
|
||||
itemNotifyIconMap.get(
|
||||
data?.notify?.notifyType,
|
||||
)
|
||||
"
|
||||
/>
|
||||
微信
|
||||
</span>
|
||||
向<span class="notify-text-highlight">{{
|
||||
options?.orgName || ''
|
||||
}}</span>
|
||||
<span class="notify-text-highlight">{{
|
||||
options?.sendTo || ''
|
||||
}}</span>
|
||||
<span class="notify-text-highlight">{{
|
||||
options?.tagName || ''
|
||||
}}</span>
|
||||
发送
|
||||
<span class="notify-text-highlight">
|
||||
{{
|
||||
options?.templateName ||
|
||||
data?.notify?.templateId
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="data?.notify?.notifyType === 'email'">
|
||||
<div>
|
||||
通过
|
||||
<span class="notify-text-highlight">
|
||||
<img
|
||||
style="width: 18px"
|
||||
:src="
|
||||
itemNotifyIconMap.get(
|
||||
data?.notify?.notifyType,
|
||||
)
|
||||
"
|
||||
/>
|
||||
邮件
|
||||
</span>
|
||||
向<span class="notify-text-highlight">{{
|
||||
options?.sendTo || ''
|
||||
}}</span>
|
||||
发送
|
||||
<span class="notify-text-highlight">
|
||||
{{
|
||||
options?.templateName ||
|
||||
data?.notify?.templateId
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="data?.notify?.notifyType === 'voice'">
|
||||
<div>
|
||||
通过
|
||||
<span class="notify-text-highlight">
|
||||
<img
|
||||
style="width: 18px"
|
||||
:src="
|
||||
itemNotifyIconMap.get(
|
||||
data?.notify?.notifyType,
|
||||
)
|
||||
"
|
||||
/>
|
||||
语音
|
||||
</span>
|
||||
向<span class="notify-text-highlight">{{
|
||||
options?.sendTo || ''
|
||||
}}</span>
|
||||
发送
|
||||
<span class="notify-text-highlight">
|
||||
{{
|
||||
options?.templateName ||
|
||||
data?.notify?.templateId
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="data?.notify?.notifyType === 'sms'">
|
||||
<div>
|
||||
通过
|
||||
<span class="notify-text-highlight">
|
||||
<img
|
||||
style="width: 18px"
|
||||
:src="
|
||||
itemNotifyIconMap.get(
|
||||
data?.notify?.notifyType,
|
||||
)
|
||||
"
|
||||
/>
|
||||
短信
|
||||
</span>
|
||||
向<span class="notify-text-highlight">{{
|
||||
options?.sendTo || ''
|
||||
}}</span>
|
||||
发送
|
||||
<span class="notify-text-highlight">
|
||||
{{
|
||||
options?.templateName ||
|
||||
data?.notify?.templateId
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<template
|
||||
v-else-if="data?.notify?.notifyType === 'webhook'"
|
||||
>
|
||||
<div>
|
||||
通过
|
||||
<span class="notify-text-highlight">
|
||||
<img
|
||||
style="width: 18px"
|
||||
:src="
|
||||
itemNotifyIconMap.get(
|
||||
data?.notify?.notifyType,
|
||||
)
|
||||
"
|
||||
/>
|
||||
webhook
|
||||
</span>
|
||||
发送
|
||||
<span>{{
|
||||
options?.templateName ||
|
||||
data?.notify?.templateId
|
||||
}}</span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div
|
||||
class="item-options-content"
|
||||
v-else-if="data?.executor === 'delay'"
|
||||
@click="onType('delay')"
|
||||
>
|
||||
{{ options?.name }}
|
||||
</div>
|
||||
<div
|
||||
class="item-options-content"
|
||||
v-else-if="data?.executor === 'device'"
|
||||
@click="onType('device')"
|
||||
>
|
||||
<template v-if="data?.device?.selector === 'fixed'">
|
||||
<div>
|
||||
<AIcon
|
||||
:type="
|
||||
typeIconMap[
|
||||
data?.device?.message?.messageType ||
|
||||
'INVOKE_FUNCTION'
|
||||
]
|
||||
"
|
||||
/>
|
||||
<span style="padding-left: 4px">{{
|
||||
data?.options?.type
|
||||
}}</span>
|
||||
<AIcon
|
||||
type="icon-mubiao"
|
||||
style="padding-right: 2px"
|
||||
/>
|
||||
{{
|
||||
`${data?.options?.name} ${
|
||||
data?.options?.properties
|
||||
}
|
||||
${
|
||||
(
|
||||
isBoolean(
|
||||
data?.options?.propertiesValue,
|
||||
)
|
||||
? true
|
||||
: data?.options?.propertiesValue
|
||||
)
|
||||
? `为 ${data?.options?.propertiesValue}`
|
||||
: ''
|
||||
}`
|
||||
}}
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="data?.device?.selector === 'tag'">
|
||||
<div>
|
||||
<AIcon
|
||||
:type="
|
||||
typeIconMap[
|
||||
data?.device?.message?.messageType ||
|
||||
'INVOKE_FUNCTION'
|
||||
]
|
||||
"
|
||||
/>
|
||||
{{ data?.options?.type }}
|
||||
<span
|
||||
v-for="i in data?.options?.taglist || []"
|
||||
:key="i.value"
|
||||
>
|
||||
{{ i.type }}
|
||||
{{ i.name }}为{{ i.value }}
|
||||
</span>
|
||||
的{{ data?.options?.productName }}
|
||||
{{ data?.options?.properties }}
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="data?.device?.selector === 'relation'">
|
||||
<div>
|
||||
<AIcon
|
||||
:type="
|
||||
typeIconMap[
|
||||
data?.device?.message?.messageType ||
|
||||
'INVOKE_FUNCTION'
|
||||
]
|
||||
"
|
||||
/>
|
||||
{{ data?.options?.type }}与<span>{{
|
||||
data?.options?.triggerName
|
||||
}}</span
|
||||
>具有相同 {{ data?.options?.relationName }}的{{
|
||||
data?.options?.productName
|
||||
}}设备的
|
||||
{{ data?.options?.properties }}
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<a-button v-else @click="onAdd">点击配置执行动作</a-button>
|
||||
</div>
|
||||
<div class="item-number">{{ name + 1 }}</div>
|
||||
<a-popconfirm title="确认删除?" @confirm="onDelete">
|
||||
<div class="item-delete">
|
||||
<AIcon type="DeleteOutlined" />
|
||||
</div>
|
||||
</a-popconfirm>
|
||||
</div>
|
||||
<template v-if="!isLast && type === 'serial'">
|
||||
<div class="actions-item-filter-warp">
|
||||
<!-- filter-border -->
|
||||
满足此条件后执行后续动作
|
||||
</div>
|
||||
</template>
|
||||
<!-- 编辑 -->
|
||||
<template v-if="visible">
|
||||
<Modal
|
||||
:name="name"
|
||||
:branchGroup="branchGroup"
|
||||
:branchesName="branchesName"
|
||||
:data="data"
|
||||
@cancel="onClose"
|
||||
@save="onSave"
|
||||
/>
|
||||
</template>
|
||||
<template>
|
||||
<ActionTypeComponent
|
||||
v-bind="props"
|
||||
v-if="!!actionType"
|
||||
:actionType="actionType"
|
||||
@save="onPropsOk"
|
||||
@cancel="onPropsCancel"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { getImage } from '@/utils/comm';
|
||||
import { isBoolean } from 'lodash-es';
|
||||
import { PropType } from 'vue';
|
||||
import { ActionsType, ParallelType } from '../../../typings';
|
||||
import Modal from '../Modal/index.vue';
|
||||
import ActionTypeComponent from '../Modal/ActionTypeComponent.vue';
|
||||
|
||||
const props = defineProps({
|
||||
branchesName: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
branchGroup: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
name: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
data: {
|
||||
type: Object as PropType<ActionsType>,
|
||||
},
|
||||
type: {
|
||||
type: Object as PropType<ParallelType>,
|
||||
},
|
||||
parallel: {
|
||||
type: Boolean,
|
||||
},
|
||||
options: {
|
||||
type: Object,
|
||||
},
|
||||
isLast: {
|
||||
type: Boolean,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['delete', 'update']);
|
||||
|
||||
const iconMap = new Map();
|
||||
iconMap.set('trigger', getImage('/scene/action-bind-icon.png'));
|
||||
iconMap.set('notify', getImage('/scene/action-notify-icon.png'));
|
||||
iconMap.set('device', getImage('/scene/action-device-icon.png'));
|
||||
iconMap.set('relieve', getImage('/scene/action-unbind-icon.png'));
|
||||
iconMap.set('delay', getImage('/scene/action-delay-icon.png'));
|
||||
|
||||
const itemNotifyIconMap = new Map();
|
||||
itemNotifyIconMap.set(
|
||||
'dingTalk',
|
||||
getImage('/scene/notify-item-img/dingtalk.png'),
|
||||
);
|
||||
itemNotifyIconMap.set('weixin', getImage('/scene/notify-item-img/weixin.png'));
|
||||
itemNotifyIconMap.set('email', getImage('/scene/notify-item-img/email.png'));
|
||||
itemNotifyIconMap.set('voice', getImage('/scene/notify-item-img/voice.png'));
|
||||
itemNotifyIconMap.set('sms', getImage('/scene/notify-item-img/sms.png'));
|
||||
itemNotifyIconMap.set(
|
||||
'webhook',
|
||||
getImage('/scene/notify-item-img/webhook.png'),
|
||||
);
|
||||
|
||||
const typeIconMap = {
|
||||
READ_PROPERTY: 'icon-zhihangdongzuodu',
|
||||
INVOKE_FUNCTION: 'icon-zhihangdongzuoxie-1',
|
||||
WRITE_PROPERTY: 'icon-zhihangdongzuoxie',
|
||||
};
|
||||
|
||||
const visible = ref<boolean>(false);
|
||||
const triggerVisible = ref<boolean>(false);
|
||||
const actionType = ref('');
|
||||
|
||||
const onDelete = () => {
|
||||
emit('delete');
|
||||
};
|
||||
|
||||
const onClose = () => {
|
||||
visible.value = false;
|
||||
};
|
||||
|
||||
const onSave = (data: ActionsType, options?: any) => {
|
||||
emit('update', data, options);
|
||||
// setTimeout(() => {
|
||||
// getParams();
|
||||
// }, 10);
|
||||
visible.value = false;
|
||||
};
|
||||
|
||||
const onAdd = () => {
|
||||
visible.value = true;
|
||||
};
|
||||
|
||||
const onType = (_type: string) => {
|
||||
actionType.value = _type
|
||||
}
|
||||
|
||||
const onPropsOk = (data: ActionsType, options?: any) => {
|
||||
emit('update', data, options);
|
||||
// setTimeout(() => {
|
||||
// getParams();
|
||||
// }, 10);
|
||||
actionType.value = '';
|
||||
};
|
||||
|
||||
const onPropsCancel = () => {
|
||||
actionType.value = '';
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.deleteBtn() {
|
||||
position: absolute;
|
||||
top: -10px;
|
||||
right: -10px;
|
||||
display: none;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
color: #999;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
background-color: #f1f1f1;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
|
||||
&.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
}
|
||||
|
||||
.actions-item {
|
||||
position: relative;
|
||||
margin-bottom: 24px;
|
||||
padding: 16px;
|
||||
border: 1px dashed #999;
|
||||
border-radius: 2px;
|
||||
|
||||
.item-options-warp {
|
||||
display: inline-flex;
|
||||
height: 48px;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 6px;
|
||||
|
||||
.item-options-type {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 48px;
|
||||
background-color: #f0f0f0;
|
||||
border-radius: 6px 0 0 6px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.item-options-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 8px;
|
||||
background: #fafafa;
|
||||
border-radius: 0 6px 6px 0;
|
||||
cursor: pointer;
|
||||
|
||||
div {
|
||||
padding: 6px 10px;
|
||||
color: #333;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
background-color: #fff;
|
||||
border-radius: 22px;
|
||||
|
||||
.notify-text-highlight {
|
||||
margin-left: 5px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.notify-img-highlight {
|
||||
margin: 0 10px;
|
||||
color: rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.item-number {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 16px;
|
||||
font-weight: 800;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.item-delete {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
color: #e50012;
|
||||
background-color: rgba(229, 0, 18, 0.1);
|
||||
border-radius: 50%;
|
||||
transform: translate(50%, -50%);
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(#e50012, 0.2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.actions-item-filter-warp {
|
||||
position: relative;
|
||||
margin-bottom: 24px;
|
||||
padding: 2px 0;
|
||||
border: 1px dashed #999;
|
||||
border-radius: 30px;
|
||||
|
||||
&.filter-border {
|
||||
padding: 2px 16px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.actions-item-filter-overflow {
|
||||
display: flex;
|
||||
padding-top: 4px;
|
||||
overflow-x: auto;
|
||||
overflow-y: visible;
|
||||
row-gap: 16px;
|
||||
}
|
||||
|
||||
.terms-params {
|
||||
// display: inline-block;
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
|
||||
// &:not(:first-child) {
|
||||
// margin-bottom: 16px;
|
||||
// }
|
||||
|
||||
.terms-params-warp {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.terms-params-content {
|
||||
position: relative;
|
||||
display: flex;
|
||||
background-color: #fafafa;
|
||||
border: unset;
|
||||
row-gap: 16px;
|
||||
|
||||
.terms-params-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.ant-form-item {
|
||||
margin-bottom: 8px;
|
||||
&:not(:first-child) {
|
||||
.ant-form-item-explain-error {
|
||||
padding-left: 80px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.term-type-warp {
|
||||
// display: inline-block;
|
||||
width: 50px;
|
||||
margin: 0 16px;
|
||||
|
||||
.term-type {
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,19 +1,43 @@
|
|||
<template>
|
||||
<div class="action-list-content">
|
||||
<div class="actions-add-list" :class="{'border': props.actions.length}">
|
||||
<template v-for="(item, index) in actions" :key="item.key">
|
||||
<Item
|
||||
:parallel="parallel"
|
||||
:data="item"
|
||||
:branchesName="branchesName"
|
||||
:branchGroup="parallel ? 1 : 0"
|
||||
:name="index"
|
||||
:type="type"
|
||||
:isLast="index === actions.length - 1"
|
||||
:options="item.options"
|
||||
@delete="_delete(item.key || '')"
|
||||
@update="(data, options) => _update(data, options, item)"
|
||||
/>
|
||||
</template>
|
||||
<div class="actions-add-list" :class="{ 'border': props.actions.length }">
|
||||
<j-button type="primary" ghost style="width: 100%" @click="onAdd">
|
||||
<template #icon><AIcon type="PlusOutlined" /></template>
|
||||
添加执行动作
|
||||
</j-button>
|
||||
</div>
|
||||
<Modal v-if="visible" @cancel="visible = false" />
|
||||
<Modal
|
||||
v-if="visible"
|
||||
@cancel="onCancel"
|
||||
:parallel="parallel"
|
||||
:name="actions.length"
|
||||
:branchGroup="parallel ? 1 : 0"
|
||||
@save="onSave"
|
||||
:branchesName="branchesName"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { PropType } from 'vue';
|
||||
import { ActionsType, ParallelType } from '../../../typings';
|
||||
import Modal from '../Modal/index.vue'
|
||||
import Modal from '../Modal/index.vue';
|
||||
import Item from './Item.vue';
|
||||
import { pick } from 'lodash';
|
||||
|
||||
interface ListProps {
|
||||
branchesName: number;
|
||||
|
@ -35,40 +59,66 @@ const props = defineProps({
|
|||
parallel: Boolean,
|
||||
});
|
||||
|
||||
const visible = ref<boolean>(false)
|
||||
const emit = defineEmits(['delete', 'add']);
|
||||
|
||||
const visible = ref<boolean>(false);
|
||||
|
||||
const onAdd = () => {
|
||||
visible.value = true
|
||||
visible.value = true;
|
||||
};
|
||||
|
||||
const onSave = (data: any, options?: any) => {
|
||||
const { type, ...extra } = data;
|
||||
const item: ActionsType = {
|
||||
...extra,
|
||||
key: data.key,
|
||||
options,
|
||||
};
|
||||
emit('add', item)
|
||||
visible.value = false
|
||||
};
|
||||
|
||||
const onCancel = () => {
|
||||
visible.value = false
|
||||
}
|
||||
|
||||
const _delete = (_key: string) => {
|
||||
emit('delete', _key)
|
||||
}
|
||||
|
||||
const _update = (data: ActionsType, options: any, item: any) => {
|
||||
const olData = pick(item, ['terms']);
|
||||
emit('add', {...olData, ...data, options})
|
||||
visible.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.action-list-content {
|
||||
padding: 8px;
|
||||
padding: 8px;
|
||||
|
||||
.actions-list_form {
|
||||
.action-list-add {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 16px;
|
||||
padding-top: 16px;
|
||||
border-top: 1px solid @primary-color;
|
||||
.actions-list_form {
|
||||
.action-list-add {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 16px;
|
||||
padding-top: 16px;
|
||||
border-top: 1px solid @primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.filter-add-button {
|
||||
width: 100%;
|
||||
color: rgba(0, 0, 0, 0.3);
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.actions-add-list {
|
||||
&.border {
|
||||
padding-top: 16px;
|
||||
border-top: 1px solid @primary-color;
|
||||
.filter-add-button {
|
||||
width: 100%;
|
||||
color: rgba(0, 0, 0, 0.3);
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.actions-add-list {
|
||||
&.border {
|
||||
padding-top: 16px;
|
||||
border-top: 1px solid @primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,64 @@
|
|||
<template>
|
||||
<div>
|
||||
<template v-if="actionType === 'device'">
|
||||
<Device v-bind="props" :value="data?.device" @cancel="onCancel" @save="onPropsOk" />
|
||||
</template>
|
||||
<template v-else-if="actionType === 'notify'">
|
||||
<Notify v-bind="props" :value="data?.notify" @cancel="onCancel" @save="onPropsOk" />
|
||||
</template>
|
||||
<template v-else-if="actionType === 'delay'">
|
||||
<Delay :value="data?.delay" @cancel="onCancel" @save="onPropsOk" />
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { PropType } from 'vue';
|
||||
import { ActionsType } from '../../../typings';
|
||||
import Delay from '../Delay/index.vue';
|
||||
import Notify from '../Notify/index.vue';
|
||||
import Device from '../Device/index.vue';
|
||||
|
||||
const props = defineProps({
|
||||
branchesName: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
branchGroup: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
name: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
data: {
|
||||
type: Object as PropType<ActionsType>,
|
||||
},
|
||||
parallel: {
|
||||
type: Boolean,
|
||||
},
|
||||
actionType: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['cancel', 'save']);
|
||||
|
||||
const onCancel = () => {
|
||||
emit('cancel');
|
||||
};
|
||||
|
||||
const onPropsOk = (data: any, options: any) => {
|
||||
const _data = {
|
||||
type: props.actionType,
|
||||
executor: props.actionType,
|
||||
key: props?.data?.key || `${props.actionType}_${new Date().getTime()}`,
|
||||
device: {
|
||||
...data,
|
||||
},
|
||||
};
|
||||
emit('save', { ..._data }, options);
|
||||
};
|
||||
</script>
|
|
@ -25,24 +25,51 @@
|
|||
float="right"
|
||||
/>
|
||||
</a-form-item>
|
||||
<template v-if="actionType === 'device'">
|
||||
<Device @cancel="onCancel" @save="onPropsOk" />
|
||||
</template>
|
||||
<template v-else-if="actionType === 'notify'">
|
||||
<Notify @cancel="onCancel" @save="onPropsOk" />
|
||||
</template>
|
||||
<template v-else-if="actionType === 'delay'">
|
||||
<Delay @cancel="onCancel" @save="onPropsOk" />
|
||||
</template>
|
||||
<ActionTypeComponent
|
||||
v-bind="props"
|
||||
v-if="!!actionType"
|
||||
:actionType="actionType"
|
||||
@save="onPropsOk"
|
||||
@cancel="onPropsCancel"
|
||||
/>
|
||||
</a-form>
|
||||
</j-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { getImage } from '@/utils/comm';
|
||||
import Delay from '../Delay/index.vue'
|
||||
import Notify from '../Notify/index.vue'
|
||||
import Device from '../Device/index.vue'
|
||||
import Delay from '../Delay/index.vue';
|
||||
import Notify from '../Notify/index.vue';
|
||||
import Device from '../Device/index.vue';
|
||||
import { PropType } from 'vue';
|
||||
import { ActionsType } from '../../../typings';
|
||||
import ActionTypeComponent from './ActionTypeComponent.vue'
|
||||
import { randomString } from '@/utils/utils';
|
||||
|
||||
|
||||
const props = defineProps({
|
||||
branchesName: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
branchGroup: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
name: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
data: {
|
||||
type: Object as PropType<ActionsType>,
|
||||
default: () => ({
|
||||
key: randomString()
|
||||
})
|
||||
},
|
||||
parallel: {
|
||||
type: Boolean,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['cancel', 'save']);
|
||||
|
||||
|
@ -81,25 +108,42 @@ const options = [
|
|||
|
||||
const actionForm = ref();
|
||||
const formModel = reactive({
|
||||
type: '',
|
||||
type: 'device',
|
||||
});
|
||||
|
||||
const actionType = ref<string>('')
|
||||
const actionType = ref<string>('');
|
||||
|
||||
const onCancel = () => {
|
||||
emit('cancel');
|
||||
};
|
||||
watch(
|
||||
() => props.data,
|
||||
(newVal) => {
|
||||
if (newVal?.executor) {
|
||||
formModel.type = (newVal?.executor === 'alarm' ? newVal?.alarm?.mode : newVal?.executor) as string
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
deep: true,
|
||||
},
|
||||
);
|
||||
const onOk = () => {
|
||||
actionForm.value.validate().then((values: any) => {
|
||||
actionType.value = values?.type
|
||||
actionType.value = values?.type;
|
||||
if (values?.type === 'relieve' || values?.type === 'trigger') {
|
||||
// emit('save');
|
||||
// props.save({ ...props.data, executor: 'alarm', alarm: { mode: values.type } }, {});
|
||||
emit('save', { ...props.data, executor: 'alarm', alarm: { mode: values.type } }, {});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const onPropsOk = (data: any, option: any) => {
|
||||
console.log(data, option)
|
||||
const onCancel = () => {
|
||||
emit('cancel');
|
||||
};
|
||||
|
||||
const onPropsOk = (data: any, options?: any) => {
|
||||
emit('save', { ...data, executor: data.type }, options);
|
||||
actionType.value = ''
|
||||
};
|
||||
|
||||
const onPropsCancel = () => {
|
||||
actionType.value = ''
|
||||
}
|
||||
</script>
|
|
@ -7,16 +7,12 @@
|
|||
class="search"
|
||||
/>
|
||||
<div style="height: 400px; overflow-y: auto">
|
||||
<JTable
|
||||
<JProTable
|
||||
:columns="columns"
|
||||
:request="ConfigApi.list"
|
||||
model="CARD"
|
||||
:bodyStyle="{
|
||||
paddingRight: 0,
|
||||
paddingLeft: 0,
|
||||
}"
|
||||
:defaultParams="{
|
||||
:request="(e) => ConfigApi.list({
|
||||
...e,
|
||||
terms: [
|
||||
...e?.terms,
|
||||
{
|
||||
terms: [
|
||||
{
|
||||
|
@ -27,11 +23,15 @@
|
|||
],
|
||||
},
|
||||
],
|
||||
sorts: [{ name: 'createTime', order: 'desc' }],
|
||||
sorts: [{ name: 'id', value: props.value }, { name: 'createTime', order: 'desc' }],
|
||||
})"
|
||||
model="CARD"
|
||||
:bodyStyle="{
|
||||
paddingRight: 0,
|
||||
paddingLeft: 0,
|
||||
}"
|
||||
:params="params"
|
||||
:gridColumn="2"
|
||||
:gridColumns="[2, 2, 2]"
|
||||
:rowSelection="{
|
||||
selectedRowKeys: _selectedRowKeys,
|
||||
}"
|
||||
|
@ -82,7 +82,7 @@
|
|||
</template>
|
||||
</CardBox>
|
||||
</template>
|
||||
</JTable>
|
||||
</JProTable>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -175,4 +175,9 @@ watch(
|
|||
padding-right: 0px;
|
||||
padding-left: 0px;
|
||||
}
|
||||
</style>
|
||||
|
||||
.logo{
|
||||
width: 88px;
|
||||
height: 88px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -7,20 +7,16 @@
|
|||
class="search"
|
||||
/>
|
||||
<div style="height: 400px; overflow-y: auto">
|
||||
<JTable
|
||||
<JProTable
|
||||
:columns="columns"
|
||||
:request="(e) => TemplateApi.getListByConfigId(props.notifierId, e)"
|
||||
:request="(e) => handleData(e)"
|
||||
model="CARD"
|
||||
:bodyStyle="{
|
||||
paddingRight: 0,
|
||||
paddingLeft: 0,
|
||||
}"
|
||||
:defaultParams="{
|
||||
sorts: [{ name: 'createTime', order: 'desc' }],
|
||||
}"
|
||||
:params="params"
|
||||
:gridColumn="2"
|
||||
:gridColumns="[2, 2, 2]"
|
||||
:rowSelection="{
|
||||
selectedRowKeys: _selectedRowKeys,
|
||||
}"
|
||||
|
@ -71,7 +67,7 @@
|
|||
</template>
|
||||
</CardBox>
|
||||
</template>
|
||||
</JTable>
|
||||
</JProTable>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -142,6 +138,27 @@ const handleClick = (dt: any) => {
|
|||
emit('update:value', dt.id);
|
||||
};
|
||||
|
||||
const handleData = async (e: any) => {
|
||||
const sorts = [
|
||||
{ name: 'id', value: props.value },
|
||||
{ name: 'createTime', order: 'desc' },
|
||||
];
|
||||
const resp = await TemplateApi.getListByConfigId(props.notifierId, {
|
||||
...e,
|
||||
sorts: sorts,
|
||||
});
|
||||
return {
|
||||
code: resp.message,
|
||||
result: {
|
||||
data: resp.result ? resp.result : [],
|
||||
pageIndex: 0,
|
||||
pageSize: resp.result.length,
|
||||
total: resp.result.length,
|
||||
},
|
||||
status: resp.status,
|
||||
};
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.value,
|
||||
(newValue) => {
|
||||
|
@ -164,4 +181,9 @@ watch(
|
|||
padding-right: 0px;
|
||||
padding-left: 0px;
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 88px;
|
||||
height: 88px;
|
||||
}
|
||||
</style>
|
|
@ -42,6 +42,7 @@ watch(
|
|||
);
|
||||
|
||||
onMounted(() => {
|
||||
loading.value = true
|
||||
notice.queryMessageType().then((resp) => {
|
||||
if (resp.status === 200) {
|
||||
options.value = (resp.result as any[]).map((item) => {
|
||||
|
@ -52,6 +53,7 @@ onMounted(() => {
|
|||
};
|
||||
});
|
||||
}
|
||||
loading.value = false
|
||||
});
|
||||
notifyType.value = props.value
|
||||
});
|
||||
|
|
|
@ -5,38 +5,44 @@
|
|||
ref="formRef"
|
||||
:model="modelRef"
|
||||
>
|
||||
<template v-for="item in variableDefinitions" :key="item.id">
|
||||
<a-form-item
|
||||
:name="item.id"
|
||||
:label="item.name"
|
||||
:rules="[{ required: true, message: `请输入${item.name}` }]"
|
||||
>
|
||||
<User
|
||||
v-if="getType(item) === 'user'"
|
||||
v-model="modelRef[`${item.id}`]"
|
||||
/>
|
||||
<Org
|
||||
v-else-if="getType(item) === 'org'"
|
||||
v-model="modelRef[`${item.id}`]"
|
||||
/>
|
||||
<Tag
|
||||
v-else-if="getType(item) === 'tag'"
|
||||
v-model="modelRef[`${item.id}`]"
|
||||
/>
|
||||
<InputFile
|
||||
v-else-if="getType(item) === 'file'"
|
||||
v-model="modelRef[`${item.id}`]"
|
||||
/>
|
||||
<a-input
|
||||
v-else-if="getType(item) === 'link'"
|
||||
v-model="modelRef[`${item.id}`]"
|
||||
/>
|
||||
<BuildIn
|
||||
v-else
|
||||
v-model="modelRef[`${item.id}`]"
|
||||
/>
|
||||
</a-form-item>
|
||||
</template>
|
||||
<a-form-item
|
||||
:name="`${item?.id}`"
|
||||
:label="item?.name"
|
||||
v-for="item in variableDefinitions"
|
||||
:key="item.id"
|
||||
:required="getType(item) !== 'file' ? true : false"
|
||||
:rules="[
|
||||
{
|
||||
validator: (_rule, value) => checkValue(_rule, value, item),
|
||||
trigger: 'change',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<User
|
||||
:notify="notify"
|
||||
v-if="getType(item) === 'user'"
|
||||
v-model:value="modelRef[item.id]"
|
||||
/>
|
||||
<Org
|
||||
:notify="notify"
|
||||
v-else-if="getType(item) === 'org'"
|
||||
v-model:value="modelRef[item.id]"
|
||||
/>
|
||||
<Tag
|
||||
:notify="notify"
|
||||
v-else-if="getType(item) === 'tag'"
|
||||
v-model:value="modelRef[item.id]"
|
||||
/>
|
||||
<InputFile
|
||||
v-else-if="getType(item) === 'file'"
|
||||
v-model:value="modelRef[item.id]"
|
||||
/>
|
||||
<a-input
|
||||
v-else-if="getType(item) === 'link'"
|
||||
v-model:value="modelRef[item.id]"
|
||||
/>
|
||||
<BuildIn v-else :item="item" v-model:value="modelRef[item.id]" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</template>
|
||||
|
||||
|
@ -46,25 +52,103 @@ import Org from './variableItem/Org.vue';
|
|||
import Tag from './variableItem/Tag.vue';
|
||||
import InputFile from './variableItem/InputFile.vue';
|
||||
import User from './variableItem/User.vue';
|
||||
import { PropType } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
variableDefinitions: {
|
||||
type: Array,
|
||||
type: Array as PropType<any>,
|
||||
default: () => [],
|
||||
},
|
||||
value: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
notify: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
});
|
||||
|
||||
const formRef = ref();
|
||||
|
||||
const modelRef = reactive({});
|
||||
|
||||
Object.assign(formRef, props.value);
|
||||
watchEffect(() => {
|
||||
Object.assign(modelRef, props.value);
|
||||
});
|
||||
|
||||
const getType = (item: any) => {
|
||||
return item.expands?.businessType || item.type;
|
||||
};
|
||||
|
||||
const checkValue = (_rule: any, value: any, item: any) => {
|
||||
const type = item.expands?.businessType || item?.type;
|
||||
if (type === 'file') {
|
||||
return Promise.resolve();
|
||||
} else if (type === 'link') {
|
||||
if (!value) {
|
||||
return Promise.reject(new Error('请输入' + item.name));
|
||||
} else if (value.length > 64) {
|
||||
return Promise.reject(new Error('最多64个字符'));
|
||||
}
|
||||
} else if (type === 'tag' && !value) {
|
||||
return Promise.reject(new Error('请选择' + item.name));
|
||||
} else if (['date', 'org'].includes(type)) {
|
||||
if (!value) {
|
||||
return Promise.reject(new Error('请选择' + item.name));
|
||||
} else {
|
||||
if (value?.source === 'upper') {
|
||||
if (!value.upperKey) {
|
||||
return Promise.reject(new Error('请选择' + item.name));
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
} else {
|
||||
if (!value.value) {
|
||||
return Promise.reject(new Error('请选择' + item.name));
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (value.source === 'fixed' && !value.value) {
|
||||
return Promise.reject(new Error('请输入' + item.name));
|
||||
} else if (value.source === 'relation' && !value.value && !value.relation) {
|
||||
return Promise.reject(new Error('请选择' + item.name));
|
||||
} else if (value.source === 'upper' && !value.upperKey) {
|
||||
return Promise.reject(new Error('请选择' + item.name));
|
||||
} else if (type === 'user') {
|
||||
if (props.notify.notifyType === 'email') {
|
||||
if (Array.isArray(value.value)) {
|
||||
if (!value.value.length) {
|
||||
return Promise.reject(new Error('请输入收件人'));
|
||||
}
|
||||
const reg =
|
||||
/^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/;
|
||||
const flag = value.value.every((it: string) => {
|
||||
return reg.test(it);
|
||||
});
|
||||
if (!flag) {
|
||||
return Promise.reject(new Error('请输入正确的邮箱地址'));
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
if (
|
||||
props.notify.notifyType &&
|
||||
['sms', 'voice'].includes(props?.notify?.notifyType)
|
||||
) {
|
||||
const reg = /^[1][3-9]\d{9}$/;
|
||||
if (!reg.test(value.value)) {
|
||||
return Promise.reject(new Error('请输入正确的手机号码'));
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
}
|
||||
return Promise.resolve();
|
||||
};
|
||||
</script>
|
|
@ -33,12 +33,18 @@
|
|||
</template>
|
||||
<template v-if="current === 1">
|
||||
<a-form-item name="notifierId">
|
||||
<NotifyConfig v-model:value="formModel.notifierId" :notifyType="formModel.notifyType" />
|
||||
<NotifyConfig
|
||||
v-model:value="formModel.notifierId"
|
||||
:notifyType="formModel.notifyType"
|
||||
/>
|
||||
</a-form-item>
|
||||
</template>
|
||||
<template v-if="current === 2">
|
||||
<a-form-item name="templateId">
|
||||
<NotifyTemplate v-model:value="formModel.templateId" :notifierId="formModel.notifierId" />
|
||||
<NotifyTemplate
|
||||
v-model:value="formModel.templateId"
|
||||
:notifierId="formModel.notifierId"
|
||||
/>
|
||||
</a-form-item>
|
||||
</template>
|
||||
<template v-if="current === 3">
|
||||
|
@ -46,6 +52,7 @@
|
|||
<VariableDefinitions
|
||||
:variableDefinitions="variable"
|
||||
v-model:value="formModel.variables"
|
||||
:notify="formModel"
|
||||
/>
|
||||
</a-form-item>
|
||||
</template>
|
||||
|
@ -55,8 +62,12 @@
|
|||
<a-space>
|
||||
<j-button v-if="current === 0" @click="onCancel">取消</j-button>
|
||||
<j-button v-if="current > 0" @click="prev">上一步</j-button>
|
||||
<j-button v-if="current < 3" type="primary" @click="next">下一步</j-button>
|
||||
<j-button v-if="current === 3" type="primary" @click="onOk">确定</j-button>
|
||||
<j-button v-if="current < 3" type="primary" @click="next"
|
||||
>下一步</j-button
|
||||
>
|
||||
<j-button v-if="current === 3" type="primary" @click="onOk"
|
||||
>确定</j-button
|
||||
>
|
||||
</a-space>
|
||||
</template>
|
||||
</j-modal>
|
||||
|
@ -69,6 +80,22 @@ import NotifyTemplate from './NotifyTemplate.vue';
|
|||
import VariableDefinitions from './VariableDefinitions.vue';
|
||||
import { onlyMessage } from '@/utils/comm';
|
||||
import Template from '@/api/notice/template';
|
||||
import { PropType } from 'vue';
|
||||
import { NotifyProps } from '../../../typings';
|
||||
|
||||
const props = defineProps({
|
||||
value: {
|
||||
type: Object as PropType<Partial<NotifyProps>>,
|
||||
default: () => undefined,
|
||||
},
|
||||
options: {
|
||||
type: Object,
|
||||
},
|
||||
name: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['cancel', 'save']);
|
||||
|
||||
|
@ -78,33 +105,42 @@ const formModel = reactive({
|
|||
notifyType: '',
|
||||
notifierId: '',
|
||||
templateId: '',
|
||||
variables: [],
|
||||
variables: {},
|
||||
});
|
||||
|
||||
const variable = ref([]);
|
||||
|
||||
watch(
|
||||
() => props.value,
|
||||
(newVal) => {
|
||||
Object.assign(formModel, newVal);
|
||||
},
|
||||
{ deep: true, immediate: true },
|
||||
);
|
||||
|
||||
const jumpStep = async (val: number) => {
|
||||
if (val === 0) {
|
||||
current.value = val;
|
||||
} else if (val === 1) {
|
||||
console.log(formModel)
|
||||
if (formModel.notifyType) {
|
||||
current.value = val
|
||||
current.value = val;
|
||||
} else {
|
||||
onlyMessage('请选择通知方式', 'error');
|
||||
}
|
||||
} else if (val === 2) {
|
||||
if (formModel.notifierId) {
|
||||
current.value = val
|
||||
current.value = val;
|
||||
} else {
|
||||
onlyMessage('请选择通知配置', 'error');
|
||||
}
|
||||
} else if (val === 3) {
|
||||
formModel.templateId = '1628943618904956928'
|
||||
formModel.templateId = '1628943618904956928';
|
||||
if (formModel.templateId) {
|
||||
const resp = await Template.getTemplateDetail(formModel.templateId);
|
||||
if (resp.status === 200) {
|
||||
variable.value = resp.result?.variableDefinitions || [];
|
||||
current.value = val
|
||||
current.value = val;
|
||||
}
|
||||
} else {
|
||||
onlyMessage('请选择通知模板', 'error');
|
||||
|
@ -113,7 +149,7 @@ const jumpStep = async (val: number) => {
|
|||
};
|
||||
|
||||
const onChange = (cur: number) => {
|
||||
jumpStep(cur)
|
||||
jumpStep(cur);
|
||||
};
|
||||
|
||||
const prev = () => {
|
||||
|
@ -121,7 +157,7 @@ const prev = () => {
|
|||
};
|
||||
|
||||
const next = async () => {
|
||||
jumpStep(current.value + 1)
|
||||
jumpStep(current.value + 1);
|
||||
};
|
||||
|
||||
const onCancel = () => {
|
||||
|
|
|
@ -1,3 +1,163 @@
|
|||
<template>
|
||||
build-in
|
||||
</template>
|
||||
<a-input-group compact>
|
||||
<a-select
|
||||
:options="[
|
||||
{ label: '手动输入', value: 'fixed' },
|
||||
{ label: '内置参数', value: 'upper' },
|
||||
]"
|
||||
style="width: 120px"
|
||||
:value="value?.source"
|
||||
@change="sourceChange"
|
||||
/>
|
||||
<template v-if="source === 'upper'">
|
||||
<a-tree-select
|
||||
v-model:value="upperKey"
|
||||
:treeData="builtInList"
|
||||
placeholder="请选择参数"
|
||||
style="width: calc(100% - 120px)"
|
||||
>
|
||||
<template #title="{ name, description }">
|
||||
<a-space>
|
||||
{{ name }}
|
||||
<spn style="color: grey; margin-left: 5px">{{ description }}</spn>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-tree-select>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-date-picker
|
||||
:value="value.value"
|
||||
allowClear
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
style="width: calc(100% - 120px)"
|
||||
v-if="item.type === 'date'"
|
||||
@change="(_, dateString) => itemOnChange(dateString)"
|
||||
/>
|
||||
<a-input-number
|
||||
:value="value.value"
|
||||
allowClear
|
||||
style="width: calc(100% - 120px)"
|
||||
v-else-if="item.type === 'number'"
|
||||
:placeholder="`请输入${item.name}`"
|
||||
@change="itemOnChange"
|
||||
/>
|
||||
<a-input
|
||||
:value="value.value"
|
||||
allowClear
|
||||
style="width: calc(100% - 120px)"
|
||||
v-else
|
||||
:placeholder="`请输入${item.name}`"
|
||||
@change="(e) => itemOnChange(e.target.value)"
|
||||
/>
|
||||
</template>
|
||||
</a-input-group>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { queryBuiltInParams } from '@/api/rule-engine/scene';
|
||||
import { useSceneStore } from '@/store/scene';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
const sceneStore = useSceneStore();
|
||||
const { data } = storeToRefs(sceneStore);
|
||||
|
||||
const props = defineProps({
|
||||
value: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {
|
||||
source: 'fixed',
|
||||
value: undefined,
|
||||
upperKey: undefined,
|
||||
};
|
||||
},
|
||||
},
|
||||
item: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
name: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:value']);
|
||||
|
||||
const source = computed(() => {
|
||||
return props.value?.source || 'fixed';
|
||||
});
|
||||
|
||||
const builtInList = ref<any[]>([]);
|
||||
const upperKey = ref();
|
||||
|
||||
const sourceChange = (val: any) => {
|
||||
emit('update:value', {
|
||||
...props.value,
|
||||
source: val,
|
||||
value: undefined,
|
||||
});
|
||||
};
|
||||
|
||||
const itemOnChange = (val: any) => {
|
||||
emit('update:value', {
|
||||
...props.value,
|
||||
value: val,
|
||||
});
|
||||
};
|
||||
|
||||
const treeDataFilter = (arr: any[], type: string) => {
|
||||
if (Array.isArray(arr) && arr.length) {
|
||||
const list: any[] = [];
|
||||
arr.map((item: any) => {
|
||||
if (item.children) {
|
||||
const children = treeDataFilter(item.children, type);
|
||||
if (children.length) {
|
||||
list.push({
|
||||
...item,
|
||||
title: item.name,
|
||||
value: item.id,
|
||||
disabled: true,
|
||||
children,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (
|
||||
item.type === type ||
|
||||
(type === 'double' &&
|
||||
['int', 'float', 'double', 'long'].includes(item.type))
|
||||
) {
|
||||
list.push(item);
|
||||
}
|
||||
}
|
||||
});
|
||||
return list;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
watch(
|
||||
() => source.value,
|
||||
(newVal) => {
|
||||
const v = newVal;
|
||||
if (v === 'upper') {
|
||||
const params =
|
||||
props.name - 1 >= 0 ? { action: props.name - 1 } : undefined;
|
||||
queryBuiltInParams(unref(data), params).then((resp) => {
|
||||
if (resp.status === 200) {
|
||||
const arr = treeDataFilter(
|
||||
resp.result as any[],
|
||||
props.item.expands?.businessType || props.item?.type,
|
||||
);
|
||||
builtInList.value = arr;
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
{ deep: true, immediate: true },
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
</style>
|
|
@ -1,3 +1,77 @@
|
|||
<template>
|
||||
input-file
|
||||
</template>
|
||||
<a-input
|
||||
allowClear
|
||||
placeholder="请上传文件"
|
||||
v-model:value="url"
|
||||
@change="onChange"
|
||||
>
|
||||
<template #addonAfter>
|
||||
<a-upload
|
||||
name="file"
|
||||
:showUploadList="false"
|
||||
:accept="'image/jpeg,image/png'"
|
||||
:disabled="loading"
|
||||
:headers="{
|
||||
[TOKEN_KEY]: LocalStore.get(TOKEN_KEY),
|
||||
}"
|
||||
:action="`${BASE_API_PATH}/file/static`"
|
||||
@change="handleChange"
|
||||
@beforeUpload="handleBeforeUpload"
|
||||
>
|
||||
<j-button type="link" style="height: 30px">
|
||||
<AIcon type="LoadingOutlined" v-if="loading" />
|
||||
<AIcon type="PlusOutlined" v-else />
|
||||
上传附件
|
||||
</j-button>
|
||||
</a-upload>
|
||||
</template>
|
||||
</a-input>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { BASE_API_PATH, TOKEN_KEY } from '@/utils/variable';
|
||||
import { LocalStore, onlyMessage } from '@/utils/comm';
|
||||
|
||||
const props = defineProps({
|
||||
id: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:value']);
|
||||
const url = ref(props.value || undefined);
|
||||
const loading = ref<boolean>(false);
|
||||
|
||||
const handleChange = (info: any) => {
|
||||
if (info.file.status === 'uploading') {
|
||||
loading.value = true;
|
||||
}
|
||||
if (info.file.status === 'done') {
|
||||
info.file.url = info.file.response?.result;
|
||||
loading.value = false;
|
||||
const result = info.file.response?.result;
|
||||
emit('update:value', result);
|
||||
}
|
||||
};
|
||||
|
||||
const handleBeforeUpload = (file: any) => {
|
||||
const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';
|
||||
if (!isJpgOrPng) {
|
||||
onlyMessage('请上传正确格式图片', 'error');
|
||||
}
|
||||
const isSize = file.size / 1024 / 1024 < 4;
|
||||
if (!isSize) {
|
||||
onlyMessage(`图片大小必须小于4M`, 'error');
|
||||
}
|
||||
return isJpgOrPng && isSize;
|
||||
};
|
||||
|
||||
const onChange = (e: any) => {
|
||||
emit('update:value', e.target.value);
|
||||
};
|
||||
</script>
|
|
@ -1,3 +1,75 @@
|
|||
<template>
|
||||
org
|
||||
</template>
|
||||
<j-tree-select
|
||||
:listHeight="200"
|
||||
v-model:value="keys"
|
||||
placeholder="请选择组织"
|
||||
:tree-data="departmentTree"
|
||||
@change="onChange"
|
||||
:fieldNames="{
|
||||
label: 'name',
|
||||
value: 'id',
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import ConfigApi from '@/api/notice/config';
|
||||
|
||||
const props = defineProps({
|
||||
notify: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
value: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:value']);
|
||||
|
||||
const departmentTree = ref<any[]>([]);
|
||||
const keys = ref<any[]>([]);
|
||||
|
||||
const getDepartment = async (id: string) => {
|
||||
if (props.notify.notifyType === 'dingTalk') {
|
||||
const resp = await ConfigApi.dingTalkDept(id);
|
||||
if (resp.status === 200) {
|
||||
departmentTree.value = resp.result;
|
||||
}
|
||||
} else {
|
||||
const resp = await ConfigApi.weChatDept(id);
|
||||
if (resp.status === 200) {
|
||||
departmentTree.value = resp.result;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.value,
|
||||
() => {
|
||||
keys.value = props?.value || [];
|
||||
},
|
||||
{ deep: true, immediate: true },
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.notify.notifierId,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
getDepartment(newVal);
|
||||
}
|
||||
},
|
||||
{ deep: true, immediate: true },
|
||||
);
|
||||
|
||||
const onChange = (key: string[], label: string[]) => {
|
||||
emit('update:value', {
|
||||
source: 'fixed',
|
||||
value: key,
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
</style>
|
|
@ -1,3 +1,65 @@
|
|||
<template>
|
||||
tag
|
||||
</template>
|
||||
<j-select
|
||||
style="width: 100%"
|
||||
v-model:value="keys"
|
||||
placeholder="请选择标签"
|
||||
:options="tagsList"
|
||||
@change="onChange"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import TemplateApi from '@/api/notice/template';
|
||||
|
||||
const props = defineProps({
|
||||
notify: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:value']);
|
||||
|
||||
const tagsList = ref<any[]>([]);
|
||||
const keys = ref<string>('');
|
||||
|
||||
const getDepartment = async (id: string) => {
|
||||
const resp = await TemplateApi.getTags(id);
|
||||
if (resp.status === 200) {
|
||||
tagsList.value = resp.result.map((item: any) => ({value: item.id, label: item.name}))
|
||||
}
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.value,
|
||||
(newVal) => {
|
||||
keys.value = newVal || ''
|
||||
},
|
||||
{ deep: true, immediate: true },
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.notify.notifierId,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
getDepartment(newVal);
|
||||
}
|
||||
},
|
||||
{ deep: true, immediate: true },
|
||||
);
|
||||
|
||||
const onChange = (key: string, label: string[]) => {
|
||||
// TODO 回显label的问题
|
||||
emit('update:value', {
|
||||
source: 'fixed',
|
||||
value: key,
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
</style>
|
|
@ -1,3 +1,274 @@
|
|||
<template>
|
||||
user
|
||||
</template>
|
||||
<a-input-group compact>
|
||||
<a-select
|
||||
style="width: 120px"
|
||||
v-model:value="mySource"
|
||||
@change="sourceChange"
|
||||
>
|
||||
<a-select-option key="5" value="relation">
|
||||
平台用户
|
||||
</a-select-option>
|
||||
<a-select-option
|
||||
v-if="notifyType === 'dingTalk'"
|
||||
key="1"
|
||||
value="fixed"
|
||||
>
|
||||
钉钉用户
|
||||
</a-select-option>
|
||||
<a-select-option
|
||||
v-else-if="notifyType === 'weixin'"
|
||||
key="2"
|
||||
value="fixed"
|
||||
>
|
||||
微信用户
|
||||
</a-select-option>
|
||||
<a-select-option
|
||||
v-else-if="notifyType === 'email'"
|
||||
key="3"
|
||||
value="fixed"
|
||||
>
|
||||
固定邮箱
|
||||
</a-select-option>
|
||||
<a-select-option v-else key="4" value="fixed">
|
||||
固定号码
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
<a-tree-select
|
||||
v-if="source === 'relation'"
|
||||
style="width: calc(100% - 120px)"
|
||||
placeholder="请选择收信人"
|
||||
@select="treeSelect"
|
||||
:tree-data="treeData"
|
||||
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
|
||||
:value="relationData"
|
||||
/>
|
||||
<a-select
|
||||
style="width: calc(100% - 120px)"
|
||||
v-else-if="['dingTalk', 'weixin'].includes(notifyType)"
|
||||
placeholder="请选择收信人"
|
||||
:value="value?.value"
|
||||
@change="(val) => onChange(val)"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="item in relationList"
|
||||
:key="item.id"
|
||||
:value="item.id"
|
||||
>{{ item.name }}</a-select-option
|
||||
>
|
||||
</a-select>
|
||||
<a-input
|
||||
style="width: calc(100% - 120px)"
|
||||
v-else-if="['email'].includes(notifyType)"
|
||||
placeholder="请输入固定邮箱"
|
||||
:value="value?.value"
|
||||
:multiple="true"
|
||||
@change="(e) => onChange(e.target.value)"
|
||||
></a-input>
|
||||
<a-input
|
||||
style="width: calc(100% - 120px)"
|
||||
v-else-if="['sms', 'voice'].includes(notifyType)"
|
||||
placeholder="请输入固定号码"
|
||||
:value="value?.value"
|
||||
@change="(e) => onChange(e.target.value)"
|
||||
></a-input>
|
||||
</a-input-group>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useSceneStore } from '@/store/scene';
|
||||
import NoticeApi from '@/api/notice/config';
|
||||
|
||||
const sceneStore = useSceneStore();
|
||||
const { data } = storeToRefs(sceneStore);
|
||||
|
||||
const props = defineProps({
|
||||
notify: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
value: {
|
||||
type: [Object],
|
||||
default: () => {},
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:value']);
|
||||
|
||||
const notifyType = computed(() => {
|
||||
return props.notify?.notifyType;
|
||||
});
|
||||
|
||||
const notifierId = computed(() => {
|
||||
return props.notify?.notifierId;
|
||||
});
|
||||
|
||||
const source = computed(() => {
|
||||
if (props.value) {
|
||||
return props.value?.source || 'relation';
|
||||
} else {
|
||||
return 'relation';
|
||||
}
|
||||
});
|
||||
|
||||
const triggerType = computed(() => {
|
||||
return data?.trigger || data?.trigger?.type;
|
||||
});
|
||||
|
||||
const relationData = computed(() => {
|
||||
const item = props.value;
|
||||
if (item?.source === 'relation') {
|
||||
const relation = item?.relation;
|
||||
if (relation) {
|
||||
if (relation.objectId) {
|
||||
// 平台用户
|
||||
return relation.objectId;
|
||||
} else {
|
||||
// 关系用户
|
||||
return relation.related?.relation;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const relationList = ref<any[]>([]);
|
||||
const treeData = ref<any[]>([
|
||||
{
|
||||
title: '平台用户',
|
||||
value: 'p1',
|
||||
key: 'p1',
|
||||
selectable: false,
|
||||
children: [],
|
||||
},
|
||||
]);
|
||||
const mySource = ref<string>('relation');
|
||||
|
||||
const getRelationUsers = async (notifyType: string, notifierId: string) => {
|
||||
let resp = undefined;
|
||||
if (notifyType === 'dingTalk') {
|
||||
resp = await NoticeApi.queryDingTalkUsers(notifierId);
|
||||
} else {
|
||||
resp = await NoticeApi.queryWechatUsers(notifierId);
|
||||
}
|
||||
if (resp && resp.status === 200) {
|
||||
relationList.value = resp.result;
|
||||
}
|
||||
};
|
||||
|
||||
const getUser = async (_source: string, triggerType: string) => {
|
||||
const newTree = [
|
||||
{
|
||||
title: '平台用户',
|
||||
value: 'p1',
|
||||
key: 'p1',
|
||||
selectable: false,
|
||||
children: [],
|
||||
},
|
||||
];
|
||||
let relationResp = undefined;
|
||||
const platformResp = await NoticeApi.getPlatformUsers({
|
||||
paging: false,
|
||||
sorts: [{ name: 'name', order: 'asc' }],
|
||||
});
|
||||
if (triggerType && triggerType === 'device' && _source === 'relation') {
|
||||
relationResp = await NoticeApi.getRelationUsers({
|
||||
paging: false,
|
||||
sorts: [{ name: 'name', order: 'asc' }],
|
||||
});
|
||||
}
|
||||
if (platformResp.status === 200) {
|
||||
newTree[0].children = platformResp.result.map((item: any) => {
|
||||
return {
|
||||
...item,
|
||||
value: item.id,
|
||||
key: item.id,
|
||||
title: item.name,
|
||||
};
|
||||
});
|
||||
}
|
||||
if (relationResp && relationResp.success) {
|
||||
newTree.push({
|
||||
title: '关系用户',
|
||||
value: 'p2',
|
||||
key: 'p2',
|
||||
selectable: false,
|
||||
children: relationResp.result.map((item: any) => {
|
||||
return {
|
||||
...item,
|
||||
value: item.id,
|
||||
key: item.id,
|
||||
title: item.name,
|
||||
isRelation: true,
|
||||
};
|
||||
}),
|
||||
});
|
||||
}
|
||||
treeData.value = newTree;
|
||||
};
|
||||
|
||||
const treeSelect = (val: string, _data: any) => {
|
||||
const obj = {
|
||||
source: source.value,
|
||||
relation: {}
|
||||
};
|
||||
if (_data?.isRelation) {
|
||||
obj.relation = {
|
||||
objectType: 'device',
|
||||
objectSource: {
|
||||
source: 'upper',
|
||||
upperKey: 'scene.deviceId',
|
||||
},
|
||||
related: {
|
||||
objectType: 'user',
|
||||
relation: val,
|
||||
},
|
||||
};
|
||||
} else {
|
||||
obj.relation = {
|
||||
objectType: 'user',
|
||||
objectId: val,
|
||||
};
|
||||
}
|
||||
emit('update:value', obj);
|
||||
};
|
||||
|
||||
const sourceChange = (v: any) => {
|
||||
emit('update:value', {
|
||||
source: v,
|
||||
});
|
||||
};
|
||||
|
||||
const onChange = (val: any) => {
|
||||
emit('update:value', {
|
||||
source: source.value,
|
||||
value: val,
|
||||
});
|
||||
};
|
||||
|
||||
watch(
|
||||
() => triggerType.value,
|
||||
(newVal) => {
|
||||
if (newVal && source.value === 'relation') {
|
||||
getUser(source.value, newVal);
|
||||
}
|
||||
},
|
||||
{ deep: true, immediate: true },
|
||||
);
|
||||
|
||||
watch(
|
||||
() => source.value,
|
||||
(newVal) => {
|
||||
const v = newVal;
|
||||
mySource.value = v as string;
|
||||
if (
|
||||
v === 'fixed' &&
|
||||
['dingTalk', 'weixin'].includes(notifyType.value)
|
||||
) {
|
||||
getRelationUsers(notifyType.value, notifierId.value);
|
||||
} else {
|
||||
getUser(v, triggerType.value);
|
||||
}
|
||||
},
|
||||
{ deep: true, immediate: true },
|
||||
);
|
||||
</script>
|
|
@ -42,6 +42,8 @@
|
|||
:branchesName="props.name"
|
||||
:parallel="true"
|
||||
:actions="parallelArray.length ? parallelArray[0].actions : []"
|
||||
@add="onAdd"
|
||||
@delete="onDelete"
|
||||
/>
|
||||
</div>
|
||||
</a-collapse-panel>
|
||||
|
@ -53,8 +55,6 @@
|
|||
<script lang="ts" setup>
|
||||
import ShakeLimit from '../components/ShakeLimit/index.vue';
|
||||
import { List } from './ListItem';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useSceneStore } from '@/store/scene';
|
||||
import { BranchesThen } from '../../typings';
|
||||
import { PropType } from 'vue';
|
||||
|
||||
|
@ -73,7 +73,9 @@ const props = defineProps({
|
|||
openShakeLimit: Boolean,
|
||||
});
|
||||
|
||||
const shakeLimit = ref({});
|
||||
const shakeLimit = ref({
|
||||
enabled: false
|
||||
});
|
||||
const activeKeys = ref<string[]>(['1']);
|
||||
const parallelArray = ref<BranchesThen[]>([]);
|
||||
const serialArray = ref<BranchesThen[]>([]);
|
||||
|
@ -105,6 +107,13 @@ watch(
|
|||
immediate: true,
|
||||
},
|
||||
);
|
||||
|
||||
const onDelete = (_key: string) => {
|
||||
console.log(_key)
|
||||
}
|
||||
const onAdd = (data: any) => {
|
||||
console.log(data)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
|
|
@ -5,5 +5,5 @@
|
|||
"moduleResolution": "Node",
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["vite.config.ts", "src/vite-env.d.ts", "config/**/*.ts"]
|
||||
"include": ["vite.config.ts", "src/vite-env.d.ts", "config/**/*.ts", "plugin/**/*.ts"]
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ import {defineConfig, loadEnv} from 'vite'
|
|||
import vue from '@vitejs/plugin-vue'
|
||||
import vueJsx from '@vitejs/plugin-vue-jsx'
|
||||
import Components from 'unplugin-vue-components/vite'
|
||||
import {AntDesignVueResolver} from 'unplugin-vue-components/resolvers'
|
||||
import AutoImport from 'unplugin-auto-import/vite'
|
||||
import { createHtmlPlugin } from 'vite-plugin-html'
|
||||
import Config from './config/config'
|
||||
|
@ -11,6 +10,7 @@ import VueSetupExtend from 'vite-plugin-vue-setup-extend'
|
|||
import { createStyleImportPlugin, AndDesignVueResolve } from 'vite-plugin-style-import'
|
||||
import * as path from 'path'
|
||||
import monacoEditorPlugin from 'vite-plugin-monaco-editor';
|
||||
import { JetlinksVueResolver } from './plugin/jetlinks'
|
||||
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
|
@ -53,7 +53,7 @@ export default defineConfig(({ mode}) => {
|
|||
monacoEditorPlugin({}),
|
||||
vueJsx(),
|
||||
Components({
|
||||
resolvers: [AntDesignVueResolver({ importStyle: 'less' }), VueAmapResolver()],
|
||||
resolvers: [JetlinksVueResolver({ importStyle: 'less' }), VueAmapResolver()],
|
||||
directoryAsNamespace: true
|
||||
}),
|
||||
AutoImport({
|
||||
|
|
|
@ -3901,10 +3901,10 @@ jetlinks-store@^0.0.3:
|
|||
resolved "https://registry.npmjs.org/jetlinks-store/-/jetlinks-store-0.0.3.tgz"
|
||||
integrity sha512-AZf/soh1hmmwjBZ00fr1emuMEydeReaI6IBTGByQYhTmK1Zd5pQAxC7WLek2snRAn/HHDgJfVz2hjditKThl6Q==
|
||||
|
||||
jetlinks-ui-components@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.jetlinks.cn/jetlinks-ui-components/-/jetlinks-ui-components-1.0.3.tgz#08a35ebfa016574affcfd11204359cdccf8e139f"
|
||||
integrity sha512-kA/AxzdfNy+Sl8En8raMwIh3stofgElkUuJ+oRJMpQVTGbrOk29DifRsHJJFNvtEvclmLdKZkOkthOuEdG2mnw==
|
||||
jetlinks-ui-components@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.jetlinks.cn/jetlinks-ui-components/-/jetlinks-ui-components-1.0.4.tgz#38f2abbb6c686ce1e1d7c08a318a6c2f50267adc"
|
||||
integrity sha512-bpmSoKVpdgZ2FUxIBaqBpewt2T9g1tTf4tECam9uTdww6SDxyvLzWmeKIUfVIwp2Ts7uKpHPFsCtI8fPmZuRjw==
|
||||
dependencies:
|
||||
"@vueuse/core" "^9.12.0"
|
||||
ant-design-vue "^3.2.15"
|
||||
|
|
Loading…
Reference in New Issue