Merge branch 'dev' of github.com:jetlinks/jetlinks-ui-vue into dev
This commit is contained in:
commit
1afe27ca91
|
@ -293,8 +293,8 @@ function getSideEffects(compName: string, options: JetlinksVueResolverOptions, _
|
|||
}
|
||||
}
|
||||
|
||||
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 filterName = ['message', 'Notification']
|
||||
const primitiveNames = ['AIcon','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>
|
||||
|
|
|
@ -11,7 +11,7 @@ export const delApply_api = (id: string) => server.remove(`/application/${id}`)
|
|||
|
||||
|
||||
// 获取组织列表
|
||||
export const getDepartmentList_api = () => server.get(`/organization/_all/tree`);
|
||||
export const getDepartmentList_api = (params: any) => server.get(`/organization/_all/tree`, params);
|
||||
// 获取应用详情
|
||||
export const getAppInfo_api = (id: string) => server.get(`/application/${id}`);
|
||||
// 新增应用
|
||||
|
|
|
@ -1,88 +0,0 @@
|
|||
import { createFromIconfontCN } from '@ant-design/icons-vue';
|
||||
import * as $Icon from '@ant-design/icons-vue';
|
||||
import { createVNode } from 'vue';
|
||||
|
||||
const AliIcon = createFromIconfontCN({
|
||||
scriptUrl: '/icons/iconfont.js', // 在 iconfont.cn 上生成
|
||||
});
|
||||
|
||||
const AntdIcon = (props: {type: string}) => {
|
||||
const {type} = props;
|
||||
let antIcon: {[key: string]: any} = $Icon
|
||||
return createVNode(antIcon[type])
|
||||
}
|
||||
|
||||
const iconKeys = [
|
||||
'EyeOutlined',
|
||||
'EditOutlined',
|
||||
'PlusOutlined',
|
||||
'DeleteOutlined',
|
||||
'CheckCircleOutlined',
|
||||
'StopOutlined',
|
||||
'CheckOutlined',
|
||||
'CloseOutlined',
|
||||
'DownOutlined',
|
||||
'ImportOutlined',
|
||||
'ExportOutlined',
|
||||
'SyncOutlined',
|
||||
'ExclamationCircleOutlined',
|
||||
'UploadOutlined',
|
||||
'LoadingOutlined',
|
||||
'PlusCircleOutlined',
|
||||
'QuestionCircleOutlined',
|
||||
'DisconnectOutlined',
|
||||
'LinkOutlined',
|
||||
'PoweroffOutlined',
|
||||
'SwapOutlined',
|
||||
'BugOutlined',
|
||||
'BarsOutlined',
|
||||
'ArrowDownOutlined',
|
||||
'SmallDashOutlined',
|
||||
'TeamOutlined',
|
||||
'MenuUnfoldOutlined',
|
||||
'MenuFoldOutlined',
|
||||
'QuestionCircleOutlined',
|
||||
'InfoCircleOutlined',
|
||||
'SearchOutlined',
|
||||
'EllipsisOutlined',
|
||||
'ClockCircleOutlined',
|
||||
'PartitionOutlined',
|
||||
'ShareAltOutlined',
|
||||
'PlayCircleOutlined',
|
||||
'RightOutlined',
|
||||
'FileTextOutlined',
|
||||
'UploadOutlined',
|
||||
'LikeOutlined',
|
||||
'ArrowLeftOutlined',
|
||||
'DownloadOutlined',
|
||||
'PauseOutlined',
|
||||
'ControlOutlined',
|
||||
'RedoOutlined',
|
||||
'ExpandOutlined',
|
||||
'VideoCameraOutlined',
|
||||
'HistoryOutlined',
|
||||
'ToolOutlined',
|
||||
'FileOutlined',
|
||||
'LikeOutlined',
|
||||
'CaretUpOutlined',
|
||||
'CaretRightOutlined',
|
||||
'CaretLeftOutlined',
|
||||
'CaretDownOutlined',
|
||||
'MinusOutlined',
|
||||
'AudioOutlined',
|
||||
'BellOutlined',
|
||||
'UserOutlined',
|
||||
'LogoutOutlined',
|
||||
'ReadIconOutlined',
|
||||
'CloudDownloadOutlined',
|
||||
'PauseCircleOutlined',,
|
||||
'FormOutlined',
|
||||
'EyeInvisibleOutlined',
|
||||
]
|
||||
|
||||
const Icon = (props: {type: string}) => {
|
||||
if(iconKeys.includes(props.type)) return <AntdIcon {...props} />
|
||||
return <AliIcon {...props} />
|
||||
}
|
||||
|
||||
export default Icon
|
|
@ -13,7 +13,7 @@ const props = defineProps({
|
|||
type: String,
|
||||
},
|
||||
status: {
|
||||
type: String || Number,
|
||||
type: [String, Number],
|
||||
default: 'default',
|
||||
// validator: (value) => {
|
||||
// // 这个值必须匹配下列字符串中的一个
|
||||
|
|
|
@ -121,11 +121,11 @@ const props = defineProps({
|
|||
default: '正常',
|
||||
},
|
||||
status: {
|
||||
type: [String, Number],
|
||||
type: [String, Number] as PropType<string | number>,
|
||||
default: 'default',
|
||||
},
|
||||
statusNames: {
|
||||
type: Object,
|
||||
type: Object as PropType<Record<any, any>>,
|
||||
default:()=>({'default':'default'})
|
||||
},
|
||||
actions: {
|
||||
|
@ -142,7 +142,7 @@ const props = defineProps({
|
|||
},
|
||||
});
|
||||
|
||||
const getBackgroundColor = (code: string) => {
|
||||
const getBackgroundColor = (code: string | number) => {
|
||||
const _color = color[code] || color.default;
|
||||
return `linear-gradient(
|
||||
188.4deg,
|
||||
|
|
|
@ -165,5 +165,6 @@ function syncTriggerClass(
|
|||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
word-break: break-all;
|
||||
white-space: normal;
|
||||
}
|
||||
</style>
|
|
@ -11,7 +11,7 @@
|
|||
<template #breadcrumbRender="slotProps">
|
||||
<a
|
||||
v-if="slotProps.route.index !== 0 && !slotProps.route.isLast"
|
||||
@click='() => jumpPage(slotProps.route.path)'
|
||||
@click='jump(slotProps.route)'
|
||||
>
|
||||
{{ slotProps.route.breadcrumbName }}
|
||||
</a>
|
||||
|
@ -89,14 +89,31 @@ const findRouteMeta = (code: string) => {
|
|||
return meta
|
||||
}
|
||||
|
||||
const jumpPage = (path: string) => {
|
||||
const jump = (item: any) => {
|
||||
let path = history.state.back
|
||||
if (path) {
|
||||
// 包含query参数,清除?参数
|
||||
if (path.includes('?')) {
|
||||
const _path = path.split('?')[0]
|
||||
path = _path === item.path ? path : item.path
|
||||
} else if (path !== item.path) {
|
||||
path = item.path
|
||||
}
|
||||
} else {
|
||||
path = item.path
|
||||
}
|
||||
|
||||
console.log(item, history.state)
|
||||
console.log(path)
|
||||
// jumpPage(slotProps.route.path)
|
||||
router.push(path)
|
||||
}
|
||||
|
||||
const breadcrumb = computed(() =>
|
||||
{
|
||||
const paths = router.currentRoute.value.name as string
|
||||
console.log(router.currentRoute)
|
||||
console.log(route)
|
||||
const metas = findRouteMeta(paths)
|
||||
return metas.map((item, index) => {
|
||||
return {
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
<!-- 表格行新增 - 简单分页组件 -->
|
||||
<template>
|
||||
<div class="pager">
|
||||
<j-select v-model:value="myCurrent" style="width: 60px">
|
||||
<j-select-option v-for="(val, i) in pageArr" :value="i + 1">
|
||||
{{ i + 1 }}
|
||||
</j-select-option>
|
||||
</j-select>
|
||||
<j-pagination
|
||||
:pageSize="pageSize"
|
||||
v-model:current="myCurrent"
|
||||
:total="total"
|
||||
style="text-align: center"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="RowPagination">
|
||||
type PageEmits = {
|
||||
(e: 'update:pageNum', data: number | string): void;
|
||||
(e: 'update:pageSize', data: number | string): void;
|
||||
};
|
||||
|
||||
type PageProps = {
|
||||
pageNum: number;
|
||||
pageSize: number;
|
||||
total: number;
|
||||
};
|
||||
|
||||
const emit = defineEmits<PageEmits>();
|
||||
const props = defineProps<PageProps>();
|
||||
|
||||
const myCurrent = computed({
|
||||
get: () => props.pageNum,
|
||||
set: (val: number) => {
|
||||
emit('update:pageNum', val);
|
||||
},
|
||||
});
|
||||
|
||||
const pageArr = computed(() => {
|
||||
const maxPageNum = Math.ceil(props.total / props.pageSize);
|
||||
return new Array(maxPageNum).fill(1);
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.pager {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: 8px 0;
|
||||
.ant-pagination {
|
||||
margin-left: 8px;
|
||||
:deep(.ant-pagination-item) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,5 +1,4 @@
|
|||
import type { App } from 'vue'
|
||||
// import AIcon from './AIcon'
|
||||
import PermissionButton from './PermissionButton/index.vue'
|
||||
import JTable from './Table/index'
|
||||
import TitleComponent from "./TitleComponent/index.vue";
|
||||
|
@ -16,6 +15,7 @@ import JEmpty from './Empty/index.vue'
|
|||
import AMapComponent from './AMapComponent/index.vue'
|
||||
import PathSimplifier from './AMapComponent/PathSimplifier.vue'
|
||||
import ValueItem from './ValueItem/index.vue'
|
||||
import RowPagination from './RowPagination/index.vue'
|
||||
|
||||
export default {
|
||||
install(app: App) {
|
||||
|
@ -37,5 +37,6 @@ export default {
|
|||
.component('AMapComponent', AMapComponent)
|
||||
.component('PathSimplifier', PathSimplifier)
|
||||
.component('ValueItem', ValueItem)
|
||||
.component('RowPagination', RowPagination)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,10 +39,7 @@ type MenuStateType = {
|
|||
}
|
||||
}
|
||||
siderMenus: MenuItem[]
|
||||
params: {
|
||||
key: string
|
||||
params: Record<string, any>
|
||||
}
|
||||
params: Record<string, any>
|
||||
}
|
||||
|
||||
|
||||
|
@ -50,10 +47,7 @@ export const useMenuStore = defineStore({
|
|||
id: 'menu',
|
||||
state: (): MenuStateType => ({
|
||||
menus: {},
|
||||
params: {
|
||||
key: '',
|
||||
params: {}
|
||||
},
|
||||
params: {},
|
||||
siderMenus: []
|
||||
}),
|
||||
getters: {
|
||||
|
@ -85,6 +79,7 @@ export const useMenuStore = defineStore({
|
|||
jumpPage(name: string, params?: Record<string, any>, query?: Record<string, any>) {
|
||||
const path = this.hasMenu(name)
|
||||
if (path) {
|
||||
this.params = { [name]: params || {}}
|
||||
router.push({
|
||||
name, params, query, state: { params }
|
||||
})
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import type { Slots } from 'vue'
|
||||
import { TOKEN_KEY } from '@/utils/variable'
|
||||
import { message } from 'jetlinks-ui-components';
|
||||
import {cloneDeep, isArray} from "lodash-es";
|
||||
|
||||
/**
|
||||
* 静态图片资源处理
|
||||
|
@ -133,4 +134,44 @@ export const handleParamsToString = (terms:SearchItemData[] = []) => {
|
|||
})
|
||||
|
||||
return JSON.stringify({ terms: _terms})
|
||||
}
|
||||
|
||||
export const treeFilter = (data: any[], value: any, key: string = 'name'): any[] => {
|
||||
if (!data) return []
|
||||
|
||||
return data.filter(item => {
|
||||
if (item.children && item.children.length) {
|
||||
item.children = treeFilter(item.children || [], value, key)
|
||||
return !!item.children.length
|
||||
} else {
|
||||
return item[key] === value
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过子节点获取上级相应数据
|
||||
* @param data 树形数据
|
||||
* @param search 搜索值
|
||||
* @param searchKey 搜索key
|
||||
* @param returnKey 返回key
|
||||
*/
|
||||
export const openKeysByTree = (data: any[], search: any, searchKey: string = 'id', returnKey: string = 'id'): any[] => {
|
||||
if (!data || (data && !isArray(data))) return []
|
||||
const cloneData = cloneDeep(data)
|
||||
const filterTree = treeFilter(cloneData, search, searchKey)
|
||||
const openKeys: any[] = []
|
||||
|
||||
const findKey = (treeData: any[]) => {
|
||||
for (let i = 0; i < treeData.length; i++) {
|
||||
const item = treeData[i]
|
||||
openKeys.push(item[returnKey])
|
||||
if (item.children && item.children.length) {
|
||||
findKey(item.children)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
findKey(filterTree)
|
||||
return openKeys
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
import {useMenuStore} from "store/menu";
|
||||
import { onBeforeUnmount } from 'vue'
|
||||
|
||||
export const useRouterParams = () => {
|
||||
const params = ref<Record<string, any>>({})
|
||||
const menu = useMenuStore();
|
||||
|
||||
const router = useRouter();
|
||||
const routeName = router.currentRoute.value.name as string
|
||||
|
||||
params.value = routeName && menu.params[routeName] ? menu.params[routeName] : {}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (routeName && menu.params[routeName]) { // 如果当前路由params参数,离开页面清除掉
|
||||
menu.params = {}
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
params
|
||||
}
|
||||
}
|
|
@ -96,9 +96,9 @@ const handleBtnChange = (val: string) => {
|
|||
radioValue.value = val;
|
||||
let endTime = dayjs(new Date()).valueOf();
|
||||
let startTime = getTimeByType(val);
|
||||
if (val === 'yesterday') {
|
||||
startTime = dayjs().subtract(1, 'days').startOf('day').valueOf();
|
||||
endTime = dayjs().subtract(1, 'days').endOf('day').valueOf();
|
||||
if (val === 'today') {
|
||||
startTime = dayjs().subtract(0, 'days').startOf('day').valueOf();
|
||||
endTime = dayjs().subtract(0, 'days').endOf('day').valueOf();
|
||||
}
|
||||
rangeVal.value = [
|
||||
dayjs(startTime).format('YYYY-MM-DD HH:mm:ss'),
|
||||
|
|
|
@ -43,7 +43,6 @@
|
|||
<TimeSelect
|
||||
key="flow-static"
|
||||
:type="'week'"
|
||||
:quickBtnList="quickBtnList"
|
||||
@change="getEcharts"
|
||||
/>
|
||||
</template>
|
||||
|
@ -134,12 +133,12 @@ let onlineOptions = ref<any>({});
|
|||
let TodayDevOptions = ref<any>({});
|
||||
let devMegOptions = ref<any>({});
|
||||
const menuStore = useMenuStore();
|
||||
const quickBtnList = [
|
||||
{ label: '昨日', value: 'yesterday' },
|
||||
{ label: '近一周', value: 'week' },
|
||||
{ label: '近一月', value: 'month' },
|
||||
{ label: '近一年', value: 'year' },
|
||||
];
|
||||
// const quickBtnList = [
|
||||
// { label: '昨日', value: 'yesterday' },
|
||||
// { label: '近一周', value: 'week' },
|
||||
// { label: '近一月', value: 'month' },
|
||||
// { label: '近一年', value: 'year' },
|
||||
// ];
|
||||
/**
|
||||
* 获取产品数量
|
||||
*/
|
||||
|
@ -242,10 +241,40 @@ const getOnline = () => {
|
|||
const onlineYdata = y;
|
||||
onlineYdata.reverse();
|
||||
setOnlineChartOption(x, onlineYdata);
|
||||
onlineFooter.value[0].value = y?.[1];
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 昨日在线
|
||||
*/
|
||||
const getYesterdayOnline = () => {
|
||||
const startTime = dayjs().subtract(1, 'days').startOf('day').format('YYYY-MM-DD HH:mm:ss');
|
||||
const endTime = dayjs().subtract(1, 'days').endOf('day').format('YYYY-MM-DD HH:mm:ss');
|
||||
|
||||
dashboard([
|
||||
{
|
||||
dashboard: 'device',
|
||||
object: 'session',
|
||||
measurement: 'online',
|
||||
dimension: 'agg',
|
||||
group: 'aggOnline',
|
||||
params: {
|
||||
state: 'online',
|
||||
limit: 24,
|
||||
from: startTime,
|
||||
to: endTime,
|
||||
time: '1d',
|
||||
format: 'yyyy-MM-dd HH:mm:ss',
|
||||
},
|
||||
},
|
||||
]).then((res) => {
|
||||
if (res.status == 200) {
|
||||
onlineFooter.value[0].value = res.result?.[0]?.data.value || 0
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const setOnlineChartOption = (x: Array<any>, y: Array<number>): void => {
|
||||
onlineOptions.value = {
|
||||
xAxis: {
|
||||
|
@ -441,9 +470,12 @@ const setDevMesChartOption = (
|
|||
],
|
||||
};
|
||||
};
|
||||
getOnline();
|
||||
|
||||
//今日设备消息量
|
||||
const getDevice = () => {
|
||||
const startTime = dayjs().subtract(0, 'days').startOf('day').format('YYYY-MM-DD HH:mm:ss');
|
||||
const endTime = dayjs().subtract(0, 'days').endOf('day').format('YYYY-MM-DD HH:mm:ss');
|
||||
|
||||
dashboard([
|
||||
{
|
||||
dashboard: 'device',
|
||||
|
@ -455,7 +487,8 @@ const getDevice = () => {
|
|||
time: '1h',
|
||||
format: 'yyyy-MM-dd HH:mm:ss',
|
||||
limit: 24,
|
||||
from: 'now-1d',
|
||||
from: startTime,
|
||||
to: endTime
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -502,7 +535,7 @@ const getDevice = () => {
|
|||
}
|
||||
});
|
||||
};
|
||||
getDevice();
|
||||
|
||||
const getEcharts = (data: any) => {
|
||||
let _time = '1h';
|
||||
let format = 'HH';
|
||||
|
@ -557,6 +590,11 @@ const getEcharts = (data: any) => {
|
|||
}
|
||||
});
|
||||
};
|
||||
|
||||
getOnline();
|
||||
getYesterdayOnline()
|
||||
getDevice();
|
||||
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.message-card,
|
||||
|
|
|
@ -237,7 +237,7 @@ const columns = [
|
|||
scopedSlots: true,
|
||||
width: 200,
|
||||
search: {
|
||||
type: 'string',
|
||||
type: 'number',
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
@ -90,7 +90,6 @@
|
|||
</template>
|
||||
|
||||
<script setup lang='ts' name="Parsing">
|
||||
import AIcon from '@/components/AIcon'
|
||||
import PermissionButton from '@/components/PermissionButton/index.vue'
|
||||
// import MonacoEditor from '@/components/MonacoEditor/index.vue';
|
||||
import { useFullscreen } from '@vueuse/core'
|
||||
|
|
|
@ -119,10 +119,12 @@ import { message } from 'jetlinks-ui-components';
|
|||
import { getImage } from '@/utils/comm';
|
||||
import { getWebSocket } from '@/utils/websocket';
|
||||
import { useMenuStore } from '@/store/menu';
|
||||
import {useRouterParams} from "@/utils/hooks/useParams";
|
||||
|
||||
const menuStory = useMenuStore();
|
||||
|
||||
const route = useRoute();
|
||||
const routerParams = useRouterParams()
|
||||
const instanceStore = useInstanceStore();
|
||||
|
||||
const statusMap = new Map();
|
||||
|
@ -246,22 +248,35 @@ const getDetail = () => {
|
|||
}
|
||||
};
|
||||
|
||||
watch(
|
||||
() => route.params?.id,
|
||||
async (newId) => {
|
||||
if (newId) {
|
||||
await instanceStore.refresh(String(newId));
|
||||
getStatus(String(newId));
|
||||
list.value = [...initList];
|
||||
getDetail();
|
||||
instanceStore.tabActiveKey = 'Info';
|
||||
}
|
||||
},
|
||||
{ immediate: true, deep: true },
|
||||
);
|
||||
// watch(
|
||||
// () => route.params?.id,
|
||||
// async (newId) => {
|
||||
// if (newId) {
|
||||
// await instanceStore.refresh(String(newId));
|
||||
// getStatus(String(newId));
|
||||
// list.value = [...initList];
|
||||
// console.log('watch', route.params?.id)
|
||||
// getDetail();
|
||||
// instanceStore.tabActiveKey = 'Info';
|
||||
// }
|
||||
// },
|
||||
// { immediate: true, deep: true },
|
||||
// );
|
||||
|
||||
const getDetailFn = async () => {
|
||||
const _id = route.params?.id
|
||||
if (_id) {
|
||||
await instanceStore.refresh(String(_id));
|
||||
getStatus(String(_id));
|
||||
list.value = [...initList];
|
||||
getDetail();
|
||||
instanceStore.tabActiveKey = routerParams.params.value.tab || 'Info';
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
instanceStore.tabActiveKey = history.state?.params?.tab || 'Info';
|
||||
getDetailFn()
|
||||
instanceStore.tabActiveKey = routerParams.params.value.tab || 'Info';
|
||||
});
|
||||
|
||||
const onBack = () => {
|
||||
|
|
|
@ -308,6 +308,7 @@ import dayjs from 'dayjs';
|
|||
import BadgeStatus from '@/components/BadgeStatus/index.vue';
|
||||
import BatchDropdown from '@/components/BatchDropdown/index.vue';
|
||||
import { BatchActionsType } from '@/components/BatchDropdown/types';
|
||||
import {useRouterParams} from "@/utils/hooks/useParams";
|
||||
|
||||
const instanceRef = ref<Record<string, any>>({});
|
||||
const params = ref<Record<string, any>>({});
|
||||
|
@ -320,7 +321,7 @@ const operationVisible = ref<boolean>(false);
|
|||
const api = ref<string>('');
|
||||
const type = ref<string>('');
|
||||
const isCheck = ref<boolean>(false);
|
||||
|
||||
const routerParams = useRouterParams()
|
||||
const menuStory = useMenuStore();
|
||||
|
||||
const columns = [
|
||||
|
@ -546,7 +547,7 @@ const paramsFormat = (
|
|||
};
|
||||
|
||||
onMounted(() => {
|
||||
if (history.state?.params?.type === 'add') {
|
||||
if (routerParams.params.value.type === 'add') {
|
||||
handleAdd();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -61,7 +61,6 @@
|
|||
</template>
|
||||
|
||||
<script setup lang='ts' name="DataAnalysis">
|
||||
import AIcon from '@/components/AIcon'
|
||||
import PermissionButton from '@/components/PermissionButton/index.vue'
|
||||
// import MonacoEditor from '@/components/MonacoEditor/index.vue';
|
||||
import { useFullscreen } from '@vueuse/core'
|
||||
|
|
|
@ -114,11 +114,13 @@ import { message } from 'jetlinks-ui-components';
|
|||
import { getImage, handleParamsToString } from '@/utils/comm'
|
||||
import encodeQuery from '@/utils/encodeQuery';
|
||||
import { useMenuStore } from '@/store/menu';
|
||||
import {useRouterParams} from "@/utils/hooks/useParams";
|
||||
|
||||
const menuStory = useMenuStore();
|
||||
const route = useRoute();
|
||||
const checked = ref<boolean>(true);
|
||||
const productStore = useProductStore();
|
||||
const routerParams = useRouterParams()
|
||||
const searchParams = ref({
|
||||
terms1: [
|
||||
{
|
||||
|
@ -291,8 +293,8 @@ const jumpDevice = () => {
|
|||
);
|
||||
};
|
||||
onMounted(() => {
|
||||
if (history.state?.params?.tab) {
|
||||
productStore.tabActiveKey = history.state?.params?.tab;
|
||||
if (routerParams.params?.value.tab) {
|
||||
productStore.tabActiveKey = routerParams.params?.value.tab;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -181,6 +181,7 @@ import { typeOptions } from '@/components/Search/util';
|
|||
import Save from './Save/index.vue';
|
||||
import { useMenuStore } from 'store/menu';
|
||||
import { useRoute } from 'vue-router';
|
||||
import {useRouterParams} from "@/utils/hooks/useParams";
|
||||
/**
|
||||
* 表格数据
|
||||
*/
|
||||
|
@ -616,9 +617,9 @@ const saveRef = ref();
|
|||
const handleSearch = (e: any) => {
|
||||
params.value = e;
|
||||
};
|
||||
const route = useRoute();
|
||||
const routerParams = useRouterParams()
|
||||
onMounted(() => {
|
||||
if(history.state?.params?.save){
|
||||
if(routerParams.params?.value.save){
|
||||
add();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -8,12 +8,13 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import { _control, _stopControl } from '@/api/edge/device';
|
||||
import {useRouterParams} from "@/utils/hooks/useParams";
|
||||
|
||||
const url = ref<string>('');
|
||||
const deviceId = ref<string>('');
|
||||
|
||||
const { params } = useRouterParams()
|
||||
watch(
|
||||
() => history.state?.params?.id,
|
||||
() => params.value.id,
|
||||
(newId) => {
|
||||
if (newId) {
|
||||
deviceId.value = newId as string;
|
||||
|
|
|
@ -76,18 +76,15 @@ const jumpPage = (item: bootConfig) => {
|
|||
.box-item {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
border-width: 1px 1px 1px 2px;
|
||||
border-style: solid;
|
||||
border-color: rgb(238, 238, 238) rgb(238, 238, 238)
|
||||
rgb(238, 238, 238) rgb(133, 165, 255);
|
||||
//border-width: 1px 1px 1px 2px;
|
||||
//border-style: solid;
|
||||
//border-color: rgb(238, 238, 238) rgb(238, 238, 238)
|
||||
// rgb(238, 238, 238) rgb(133, 165, 255);
|
||||
border: 1px solid #e6e6e6;
|
||||
padding: 11px;
|
||||
background: linear-gradient(
|
||||
135.62deg,
|
||||
#f6f7fd 22.27%,
|
||||
rgba(255, 255, 255, 0.86) 91.82%
|
||||
);
|
||||
background: linear-gradient(0deg, #FFFFFF, #FFFFFF), linear-gradient(135.62deg, rgba(47, 84, 235, 0.07) 22.27%, rgba(47, 84, 235, 0.01) 91.82%);
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 4px 18px #efefef;
|
||||
box-shadow: -2px 0 #85A5FF;
|
||||
&:not(:first-child) {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
|
|
@ -12,11 +12,13 @@
|
|||
|
||||
<div class="box-list">
|
||||
<div class="list-item" v-for="item in props.dataList">
|
||||
<div class="item-content">
|
||||
<div class="box-top" @click="jumpPage(item)">
|
||||
<span class="top-title">{{ item.title }}</span>
|
||||
<img :src="item.iconUrl" alt="" />
|
||||
</div>
|
||||
<div class="box-details">{{ item.details }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -82,6 +84,14 @@ const jumpPage = (row: recommendList) => {
|
|||
.list-item {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
.item-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
&:hover {
|
||||
box-shadow: @shadow-1-down;
|
||||
}
|
||||
}
|
||||
.box-top {
|
||||
position: relative;
|
||||
padding: 16px 24px;
|
||||
|
@ -103,6 +113,7 @@ const jumpPage = (row: recommendList) => {
|
|||
padding: 24px;
|
||||
border: 1px solid #e5edf4;
|
||||
border-top: none;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
&:not(:last-child)::after {
|
||||
|
|
|
@ -6,12 +6,12 @@
|
|||
title="导入"
|
||||
okText="确定"
|
||||
cancelText="取消"
|
||||
@ok="handleCancel"
|
||||
@ok="handleOk"
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
<div style="margin-top: 10px">
|
||||
<j-form :layout="'vertical'">
|
||||
<j-form-item label="平台对接" required>
|
||||
<j-form :layout="'vertical'" :model="modelRef" ref="formRef" :rules="rules">
|
||||
<j-form-item label="平台对接" required name="configId">
|
||||
<j-select
|
||||
showSearch
|
||||
v-model:value="modelRef.configId"
|
||||
|
@ -86,19 +86,24 @@ import { queryPlatformNoPage, _import ,exportCard} from '@/api/iot-card/cardMana
|
|||
import { message } from 'jetlinks-ui-components';
|
||||
|
||||
|
||||
const emit = defineEmits(['close']);
|
||||
const emit = defineEmits(['close', 'save']);
|
||||
|
||||
const configList = ref<Record<string, any>[]>([]);
|
||||
const loading = ref<boolean>(false);
|
||||
const totalCount = ref<number>(0);
|
||||
const errCount = ref<number>(0);
|
||||
|
||||
const formRef = ref(null)
|
||||
const importStatus = ref(false)
|
||||
const modelRef = reactive({
|
||||
configId: undefined,
|
||||
upload: [],
|
||||
fileType: 'xlsx',
|
||||
});
|
||||
|
||||
const rules = {
|
||||
configId: [{ required: true, message: '请选择平台对接'}]
|
||||
}
|
||||
|
||||
const getConfig = async () => {
|
||||
const resp: any = await queryPlatformNoPage({
|
||||
paging: false,
|
||||
|
@ -127,6 +132,7 @@ const fileChange = (info: any) => {
|
|||
_import(modelRef.configId, { fileUrl: r.result })
|
||||
.then((resp: any) => {
|
||||
totalCount.value = resp.result.total;
|
||||
importStatus.value = true
|
||||
message.success('导入成功')
|
||||
})
|
||||
.catch((err) => {
|
||||
|
@ -159,9 +165,20 @@ const handleCancel = () => {
|
|||
totalCount.value = 0;
|
||||
errCount.value = 0;
|
||||
modelRef.configId = undefined;
|
||||
|
||||
emit('close', true);
|
||||
if (importStatus.value) {
|
||||
emit('save', true)
|
||||
}
|
||||
importStatus.value = false
|
||||
};
|
||||
|
||||
const handleOk = () => {
|
||||
formRef.value.validate().then(res => {
|
||||
handleCancel()
|
||||
})
|
||||
}
|
||||
|
||||
getConfig();
|
||||
</script>
|
||||
|
||||
|
|
|
@ -370,7 +370,7 @@
|
|||
</template>
|
||||
</j-pro-table>
|
||||
<!-- 批量导入 -->
|
||||
<Import v-if="importVisible" @close="importVisible = false" />
|
||||
<Import v-if="importVisible" @close="importVisible = false" @save="importSave"/>
|
||||
<!-- 批量导出 -->
|
||||
<Export
|
||||
v-if="exportVisible"
|
||||
|
@ -421,13 +421,15 @@ import { useMenuStore } from 'store/menu';
|
|||
import BadgeStatus from '@/components/BadgeStatus/index.vue';
|
||||
import BatchDropdown from '@/components/BatchDropdown/index.vue';
|
||||
import { BatchActionsType } from '@/components/BatchDropdown/types';
|
||||
import {usePermissionStore} from "store/permission";
|
||||
import {useRouterParams} from "@/utils/hooks/useParams";
|
||||
|
||||
const router = useRouter();
|
||||
const menuStory = useMenuStore();
|
||||
const cardManageRef = ref<Record<string, any>>({});
|
||||
const params = ref<Record<string, any>>({});
|
||||
const _selectedRowKeys = ref<string[]>([]);
|
||||
const _selectedRow = ref<any[]>([]);
|
||||
// const _selectedRow = ref<any[]>([]);
|
||||
const bindDeviceVisible = ref<boolean>(false);
|
||||
const visible = ref<boolean>(false);
|
||||
const exportVisible = ref<boolean>(false);
|
||||
|
@ -468,7 +470,8 @@ const columns = [
|
|||
scopedSlots: true,
|
||||
width: 200,
|
||||
search: {
|
||||
type: 'string'
|
||||
type: 'string',
|
||||
rename: 'deviceName'
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -587,6 +590,14 @@ const columns = [
|
|||
scopedSlots: true,
|
||||
},
|
||||
];
|
||||
const btnHasPermission = usePermissionStore().hasPermission;
|
||||
const paltformPermission = btnHasPermission(`iot-card/Platform:add`);
|
||||
const importSave = () => {
|
||||
cardManageRef.value?.reload()
|
||||
importVisible.value = false
|
||||
}
|
||||
|
||||
const routerParams = useRouterParams()
|
||||
|
||||
const getActions = (
|
||||
data: Partial<Record<string, any>>,
|
||||
|
@ -756,7 +767,7 @@ const handleSearch = (e: any) => {
|
|||
|
||||
const onSelectChange = (keys: string[], rows: []) => {
|
||||
_selectedRowKeys.value = [...keys];
|
||||
_selectedRow.value = [...rows];
|
||||
// _selectedRow.value = [...rows];
|
||||
};
|
||||
|
||||
const cancelSelect = () => {
|
||||
|
@ -819,6 +830,9 @@ const bindDevice = (val: boolean) => {
|
|||
* 批量激活
|
||||
*/
|
||||
const handleActive = () => {
|
||||
if (!_selectedRowKeys.value.length) {
|
||||
return message.warn('请选择数据');
|
||||
}
|
||||
if (
|
||||
_selectedRowKeys.value.length >= 10 &&
|
||||
_selectedRowKeys.value.length <= 100
|
||||
|
@ -885,15 +899,15 @@ const handleSync = () => {
|
|||
* 批量删除
|
||||
*/
|
||||
const handelRemove = async () => {
|
||||
if (!_selectedRow.value.length) {
|
||||
if (!_selectedRowKeys.value.length) {
|
||||
message.error('请选择数据');
|
||||
return;
|
||||
}
|
||||
const resp = await removeCards(_selectedRow.value);
|
||||
const resp = await removeCards(_selectedRowKeys.value);
|
||||
if (resp.status === 200) {
|
||||
message.success('操作成功');
|
||||
_selectedRowKeys.value = [];
|
||||
_selectedRow.value = [];
|
||||
// _selectedRow.value = [];
|
||||
cardManageRef.value?.reload();
|
||||
}
|
||||
};
|
||||
|
@ -980,6 +994,12 @@ const batchActions: BatchActionsType[] = [
|
|||
},
|
||||
},
|
||||
];
|
||||
|
||||
onMounted(() => {
|
||||
if (routerParams.params.value.type === 'add' && paltformPermission) {
|
||||
handleAdd()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
|
|
|
@ -148,7 +148,7 @@
|
|||
<script setup lang="ts">
|
||||
import Guide from '../components/Guide.vue';
|
||||
import LineChart from '../components/LineChart.vue';
|
||||
import moment from 'moment';
|
||||
import dayjs from 'dayjs';
|
||||
import { queryFlow } from '@/api/iot-card/home';
|
||||
import TimeSelect from '@/views/iot-card/components/TimeSelect.vue';
|
||||
import { Empty } from 'ant-design-vue';
|
||||
|
@ -200,16 +200,16 @@ const getData = (
|
|||
*/
|
||||
const getDataTotal = () => {
|
||||
const dTime = [
|
||||
moment(new Date()).startOf('day').valueOf(),
|
||||
moment(new Date()).endOf('day').valueOf(),
|
||||
dayjs(new Date()).startOf('day').valueOf(),
|
||||
dayjs(new Date()).endOf('day').valueOf(),
|
||||
];
|
||||
const mTime = [
|
||||
moment().startOf('month').valueOf(),
|
||||
moment().endOf('month').valueOf(),
|
||||
dayjs().startOf('month').valueOf(),
|
||||
dayjs().endOf('month').valueOf(),
|
||||
];
|
||||
const yTime = [
|
||||
moment().startOf('year').valueOf(),
|
||||
moment().endOf('year').valueOf(),
|
||||
dayjs().startOf('year').valueOf(),
|
||||
dayjs().endOf('year').valueOf(),
|
||||
];
|
||||
getData(dTime[0], dTime[1]).then((resp) => {
|
||||
dayTotal.value = resp.data
|
||||
|
@ -238,9 +238,10 @@ const getDataTotal = () => {
|
|||
const getEcharts = (data: any) => {
|
||||
let startTime = data.start;
|
||||
let endTime = data.end;
|
||||
if (data.type === 'week' || data.type === 'month') {
|
||||
startTime = moment(data.start).startOf('days').valueOf();
|
||||
endTime = moment(data.end).startOf('days').valueOf();
|
||||
|
||||
if (data.type !== 'day') {
|
||||
startTime = dayjs(data.start).startOf('days').valueOf();
|
||||
endTime = dayjs(data.end).startOf('days').valueOf();
|
||||
}
|
||||
getData(startTime, endTime).then((resp) => {
|
||||
flowData.value = resp.sortArray;
|
||||
|
|
|
@ -179,7 +179,6 @@ const pieChartData = ref<any[]>([
|
|||
]);
|
||||
|
||||
const jumpPage = (data: GuideItemProps) => {
|
||||
console.log(data.auth)
|
||||
if (!data.auth){
|
||||
message.warning('暂无权限,请联系管理员');
|
||||
return
|
||||
|
@ -187,7 +186,11 @@ const jumpPage = (data: GuideItemProps) => {
|
|||
if (data.key === 'EQUIPMENT') {
|
||||
menuStory.jumpPage(data.url, { id: ':id'});
|
||||
} else {
|
||||
menuStory.jumpPage(data.url);
|
||||
let params: any = undefined
|
||||
if (data.key === 'SCREEN') {
|
||||
params = { type: 'add'}
|
||||
}
|
||||
menuStory.jumpPage(data.url, params);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -70,10 +70,11 @@ const rangeVal = ref<[string, string]>();
|
|||
|
||||
const rangeChange = (val: any) => {
|
||||
radioValue.value = undefined;
|
||||
const differenceTime = dayjs(val[1]).valueOf() - dayjs(val[0]).valueOf()
|
||||
emit('change', {
|
||||
start: dayjs(val[0]).valueOf(),
|
||||
end: dayjs(val[1]).valueOf(),
|
||||
type: undefined,
|
||||
type: differenceTime > (24 * 60 * 60 * 1000) ? undefined : 'day',
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -103,6 +104,7 @@ const handleBtnChange = (val?: string) => {
|
|||
dayjs(startTime).format('YYYY-MM-DD HH:mm:ss'),
|
||||
dayjs(endTime).format('YYYY-MM-DD HH:mm:ss'),
|
||||
];
|
||||
console.log(startTime, endTime)
|
||||
emit('change', {
|
||||
start: startTime,
|
||||
end: endTime,
|
||||
|
|
|
@ -53,8 +53,10 @@ import { message } from 'jetlinks-ui-components';
|
|||
import { useAlarmStore } from '@/store/alarm';
|
||||
import Info from './info.vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import {useRouterParams} from "@/utils/hooks/useParams";
|
||||
const route = useRoute();
|
||||
const id = route.params?.id;
|
||||
const { params: routerParams } = useRouterParams()
|
||||
let visiable = ref(false);
|
||||
const columns = [
|
||||
{
|
||||
|
@ -181,7 +183,7 @@ const close = () => {
|
|||
|
||||
watchEffect(()=>{
|
||||
current.value = details.value;
|
||||
if(history.state?.params.detail && details.value){
|
||||
if(routerParams.value.detail && details.value){
|
||||
visiable.value = true;
|
||||
}
|
||||
})
|
||||
|
|
|
@ -151,9 +151,11 @@ import { getImage } from '@/utils/comm';
|
|||
import { message } from 'jetlinks-ui-components';
|
||||
import Save from './Save/index.vue';
|
||||
import { SystemConst } from '@/utils/consts';
|
||||
import {useRouterParams} from "@/utils/hooks/useParams";
|
||||
const params = ref<Record<string, any>>({});
|
||||
let visiable = ref(false);
|
||||
const tableRef = ref<Record<string, any>>({});
|
||||
const { params: routeParams } = useRouterParams()
|
||||
const query = {
|
||||
columns: [
|
||||
{
|
||||
|
|
|
@ -23,11 +23,16 @@
|
|||
</span>
|
||||
</j-col>
|
||||
<j-col :span='24' v-if='showTable'>
|
||||
<FunctionCall
|
||||
:value='_value'
|
||||
:data='callDataOptions'
|
||||
@change='callDataChange'
|
||||
/>
|
||||
<j-form-item
|
||||
name='data'
|
||||
:rules="rules"
|
||||
>
|
||||
<FunctionCall
|
||||
v-model:value='formModel.data'
|
||||
:data='callDataOptions'
|
||||
@change='callDataChange'
|
||||
/>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</j-form>
|
||||
|
@ -61,8 +66,9 @@ const props = defineProps({
|
|||
|
||||
const emit = defineEmits<Emit>()
|
||||
|
||||
const formModel = reactive<{ reportKey: string | undefined }>({
|
||||
reportKey: undefined
|
||||
const formModel = reactive<{ reportKey: string | undefined, data: any[] }>({
|
||||
reportKey: undefined,
|
||||
data: Object.keys(props.value).map(key => ({ name: key, value: props.value[key] })) || []
|
||||
})
|
||||
|
||||
const callData = ref<Array<{ id: string, value: string | undefined }>>()
|
||||
|
@ -97,6 +103,10 @@ const callDataOptions = computed(() => {
|
|||
return []
|
||||
})
|
||||
|
||||
nextTick(() => {
|
||||
formModel.reportKey = Object.keys(props.value)[0]
|
||||
})
|
||||
|
||||
const showTable = computed(() => {
|
||||
return !!formModel.reportKey
|
||||
})
|
||||
|
@ -116,6 +126,23 @@ const callDataChange = (v: any[]) => {
|
|||
})
|
||||
}
|
||||
|
||||
const rules = [{
|
||||
validator(_: string, value: any) {
|
||||
console.log(value, callDataOptions.value)
|
||||
if (!value?.length && callDataOptions.value.length) {
|
||||
return Promise.reject('请选择属性值')
|
||||
} else {
|
||||
let hasValue = value.find((item: { name: string, value: any}) => !item.value)
|
||||
if (hasValue) {
|
||||
const item = callDataOptions.value.find((item: any) => item.id === hasValue.name)
|
||||
console.log()
|
||||
return Promise.reject(item?.name ? `请输入${item?.name}值` : '请输入属性值')
|
||||
}
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
}]
|
||||
|
||||
const initRowKey = () => {
|
||||
if (props.value.length) {
|
||||
const keys = Object.keys(props.value)
|
||||
|
|
|
@ -23,7 +23,8 @@ const { data } = storeToRefs(sceneStore);
|
|||
|
||||
const actionRules = [{
|
||||
validator(_: any, v?: BranchesThen[]) {
|
||||
if (!v || (v && !v.length) || (v && v.length && !v[0].actions.length)) {
|
||||
|
||||
if (!v || (v && !v.length) || !v.some(item => item.actions && item.actions.length)) {
|
||||
return Promise.reject('至少配置一个执行动作');
|
||||
}
|
||||
return Promise.resolve();
|
||||
|
|
|
@ -59,7 +59,7 @@ const rules = [{
|
|||
|
||||
const actionRules = [{
|
||||
validator(_: any, v?: BranchesThen[]) {
|
||||
if (!v || (v && !v.length)) {
|
||||
if (!v || (v && !v.length) || !v.some(item => item.actions && item.actions.length)) {
|
||||
return Promise.reject('至少配置一个执行动作');
|
||||
}
|
||||
return Promise.resolve();
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
<template>
|
||||
<slot />
|
||||
</template>
|
||||
|
||||
<script setup lang='ts' name='ActionCheckItem'>
|
||||
import { ActionsType } from '@/views/rule-engine/Scene/typings'
|
||||
import { useSceneStore } from '@/store/scene';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { queryProductList } from '@/api/device/product'
|
||||
import { query as deviceQuery } from '@/api/device/instance'
|
||||
import noticeConfig from '@/api/notice/config'
|
||||
import noticeTemplate from '@/api/notice/template'
|
||||
import { Form } from 'jetlinks-ui-components'
|
||||
const sceneStore = useSceneStore();
|
||||
const { data: _data } = storeToRefs(sceneStore);
|
||||
|
||||
const formItemContext = Form.useInjectFormItemContext()
|
||||
|
||||
const props = defineProps({
|
||||
branchesName: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
thenName: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
name: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
});
|
||||
|
||||
const rules = [{
|
||||
validator(_: any, v?: ActionsType) {
|
||||
console.log('validator',v)
|
||||
if (v?.executor === 'device') {
|
||||
if(!v.device?.productId || !v.device?.selectorValues) {
|
||||
return Promise.reject(new Error('该数据已发生变更,请重新配置'))
|
||||
}
|
||||
}
|
||||
return Promise.resolve()
|
||||
}
|
||||
}]
|
||||
|
||||
const formTouchOff = () => {
|
||||
formItemContext.onFieldChange()
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验当前执行动作的设备或者产品是否删除
|
||||
*/
|
||||
const checkDeviceDelete = async () => {
|
||||
const item = _data.value.branches![props.branchesName].then[props.thenName].actions[props.name].device
|
||||
const proResp = await queryProductList({ terms: [{ terms: [{ column: 'id', termType: 'eq', value: item!.productId }]}]})
|
||||
if (proResp.success && (proResp.result as any)?.total === 0) { // 产品已删除
|
||||
_data.value.branches![props.branchesName].then[props.thenName].actions[props.name].device!.productId = undefined
|
||||
formTouchOff()
|
||||
return
|
||||
}
|
||||
const deviceList = item!.selectorValues?.map(item => item.value) || []
|
||||
const deviceResp = await deviceQuery({ terms: [{ terms: [{ column: 'id', termType: 'in', value: deviceList.toString() }]}]})
|
||||
if (deviceResp.success && (deviceResp.result as any)?.total < (item!.selectorValues?.length || 0)) { // 某一个设备被删除
|
||||
_data.value.branches![props.branchesName].then[props.thenName].actions[props.name].device!.selectorValues = undefined
|
||||
formTouchOff()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验当前执行动作的通知配置、消息模板是否删除
|
||||
*/
|
||||
const checkNoticeDelete = async () => {
|
||||
const item = _data.value.branches![props.branchesName].then[props.thenName].actions[props.name].notify
|
||||
const configResp = await noticeConfig.list({ terms: [{ terms: [{ column: 'id', termType: 'eq', value: item!.notifierId }]}]})
|
||||
if (configResp.success && (configResp.result as any)?.total === 0) {
|
||||
_data.value.branches![props.branchesName].then[props.thenName].actions[props.name].notify!.notifierId = ''
|
||||
formTouchOff()
|
||||
return
|
||||
}
|
||||
const templateResp = await noticeTemplate.list({ terms: [{ terms: [{ column: 'id', termType: 'eq', value: item!.templateId }]}]})
|
||||
if (templateResp.success && (templateResp.result as any)?.total === 0) {
|
||||
_data.value.branches![props.branchesName].then[props.thenName].actions[props.name].notify!.templateId = ''
|
||||
formTouchOff()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
nextTick(() => {
|
||||
const _executor = _data.value.branches![props.branchesName].then[props.thenName].actions[props.name]?.executor
|
||||
if (_executor === 'device' && _data.value.branches![props.branchesName].then[props.thenName].actions[props.name]?.device) {
|
||||
checkDeviceDelete()
|
||||
} else if (_executor === 'notify' && _data.value.branches![props.branchesName].then[props.thenName].actions[props.name]?.notify) {
|
||||
checkNoticeDelete
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -43,7 +43,7 @@
|
|||
value-name='id'
|
||||
label-name='name'
|
||||
:options='valueOptions'
|
||||
:metricOptions='columnOptions'
|
||||
:metricOptions='valueColumnOptions'
|
||||
:tabsOptions='tabsOptions'
|
||||
v-model:value='paramsValue.value.value'
|
||||
v-model:source='paramsValue.value.source'
|
||||
|
@ -56,7 +56,7 @@
|
|||
value-name='id'
|
||||
label-name='name'
|
||||
:options='valueOptions'
|
||||
:metricOptions='columnOptions'
|
||||
:metricOptions='valueColumnOptions'
|
||||
:tabsOptions='tabsOptions'
|
||||
v-model:value='paramsValue.value.value'
|
||||
v-model:source='paramsValue.value.source'
|
||||
|
@ -83,8 +83,9 @@ import ParamsDropdown, { DoubleParamsDropdown } from '../../components/ParamsDro
|
|||
import { inject } from 'vue'
|
||||
import { useSceneStore } from 'store/scene'
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { flattenDeep, set } from 'lodash-es'
|
||||
import {cloneDeep, flattenDeep, isArray, set} from 'lodash-es'
|
||||
import { Form } from 'jetlinks-ui-components'
|
||||
import {treeFilter} from "@/utils/comm";
|
||||
|
||||
const sceneStore = useSceneStore()
|
||||
const { data: formModel } = storeToRefs(sceneStore)
|
||||
|
@ -160,6 +161,7 @@ const columnOptions: any = inject('filter-params') //
|
|||
const termTypeOptions = ref<Array<{ id: string, name: string}>>([]) // 条件值
|
||||
const valueOptions = ref<any[]>([]) // 默认手动输入下拉
|
||||
const arrayParamsKey = ['nbtw', 'btw', 'in', 'nin']
|
||||
const valueColumnOptions = ref<any[]>([])
|
||||
|
||||
const tabsOptions = ref<Array<TabsOption>>(
|
||||
[
|
||||
|
@ -182,9 +184,11 @@ const handOptionByColumn = (option: any) => {
|
|||
} else{
|
||||
valueOptions.value = option.options || []
|
||||
}
|
||||
valueColumnOptions.value = treeFilter(cloneDeep(columnOptions.value), option.type, 'type')
|
||||
} else {
|
||||
termTypeOptions.value = []
|
||||
valueOptions.value = []
|
||||
valueColumnOptions.value = []
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -259,7 +263,8 @@ const columnSelect = (e: any) => {
|
|||
}
|
||||
|
||||
const termsTypeSelect = (e: { key: string, name: string }) => {
|
||||
const value = arrayParamsKey.includes(e.key) ? [ undefined, undefined ] : undefined
|
||||
const oldValue = isArray(paramsValue.value!.value) ? paramsValue.value!.value[0] : paramsValue.value!.value
|
||||
const value = arrayParamsKey.includes(e.key) ? [ oldValue, undefined ] : oldValue
|
||||
paramsValue.value = {
|
||||
source: tabsOptions.value[0].key,
|
||||
value: value
|
||||
|
@ -276,6 +281,8 @@ const valueSelect = (_: any, label: string, labelObj: Record<number, any>) => {
|
|||
}
|
||||
|
||||
const typeChange = (e: any) => {
|
||||
paramsValue.type = e.value
|
||||
emit('update:value', { ...paramsValue })
|
||||
formModel.value.branches![props.branchName].then[props.thenName].actions[props.actionName].options!.terms[props.termsName].terms[props.name][3] = e.label
|
||||
}
|
||||
|
||||
|
|
|
@ -63,6 +63,7 @@ import { flattenDeep, isArray } from 'lodash-es'
|
|||
import { provide } from 'vue'
|
||||
import { randomString } from '@/utils/utils'
|
||||
import { getParams, EventEmitter, EventSubscribeKeys } from '@/views/rule-engine/Scene/Save/util'
|
||||
import { handleParamsData } from '@/views/rule-engine/Scene/Save/components/Terms/util'
|
||||
|
||||
const sceneStore = useSceneStore()
|
||||
const { data: formModel } = storeToRefs(sceneStore)
|
||||
|
@ -119,7 +120,7 @@ const columnRequest = () => {
|
|||
action: props.actionName
|
||||
}
|
||||
getParams(param, formModel.value).then(res => {
|
||||
columnOptions.value = res
|
||||
columnOptions.value = handleParamsData(res, 'id')
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -226,6 +227,9 @@ const rules = [
|
|||
}
|
||||
}
|
||||
} else {
|
||||
if (v?.error) { // 数据发生变化
|
||||
return Promise.reject(new Error('该数据已发生变更,请重新配置'))
|
||||
}
|
||||
return Promise.reject(new Error('请选择参数'))
|
||||
}
|
||||
return Promise.resolve()
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
<template>
|
||||
<div class="actions-item-warp">
|
||||
<j-form-item
|
||||
:name='["branches", branchesName, "then", thenName, "actions", name]'
|
||||
:rules='rules'
|
||||
>
|
||||
<div class="actions-item">
|
||||
<CheckItem v-bind='props'>
|
||||
<div class="item-options-warp">
|
||||
<div class="item-options-type" @click="onAdd">
|
||||
<img
|
||||
|
@ -120,7 +125,7 @@
|
|||
</div>
|
||||
</template>
|
||||
<template v-else-if="data?.notify?.notifyType === 'email'">
|
||||
<div>
|
||||
<div style="display: flex;">
|
||||
通过
|
||||
<span class="notify-text-highlight">
|
||||
<img
|
||||
|
@ -133,9 +138,13 @@
|
|||
/>
|
||||
邮件
|
||||
</span>
|
||||
向<span class="notify-text-highlight">{{
|
||||
options?.sendTo || ''
|
||||
}}</span>
|
||||
向<span class="notify-text-highlight">
|
||||
<Ellipsis style='max-width: 400px;'>
|
||||
{{
|
||||
options?.sendTo || ''
|
||||
}}
|
||||
</Ellipsis>
|
||||
</span>
|
||||
发送
|
||||
<span class="notify-text-highlight">
|
||||
{{
|
||||
|
@ -256,19 +265,23 @@
|
|||
<Ellipsis style='max-width: 400px;'>
|
||||
{{data?.options?.properties}}
|
||||
</Ellipsis>
|
||||
|
||||
<Ellipsis style='max-width: 200px;'>
|
||||
{{
|
||||
`${
|
||||
(
|
||||
isBoolean(
|
||||
data?.options?.propertiesValue,
|
||||
)
|
||||
? true
|
||||
: data?.options?.propertiesValue
|
||||
)
|
||||
? `为 ${data?.options?.propertiesValue}`
|
||||
: ''
|
||||
}`
|
||||
`${
|
||||
(
|
||||
isBoolean(
|
||||
data?.options?.propertiesValue,
|
||||
)
|
||||
? true
|
||||
: data?.options?.propertiesValue
|
||||
)
|
||||
? `为 ${data?.options?.propertiesValue}`
|
||||
: ''
|
||||
}`
|
||||
}}
|
||||
</Ellipsis>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="data?.device?.selector === 'tag'">
|
||||
|
@ -322,12 +335,14 @@
|
|||
<j-button v-else @click="onAdd">点击配置执行动作</j-button>
|
||||
</div>
|
||||
<div class="item-number">{{ name + 1 }}</div>
|
||||
<j-popconfirm title="确认删除?" @confirm="onDelete">
|
||||
<j-popconfirm title="确认删除?" @confirm="onDelete" :overlayStyle='{minWidth: "180px"}'>
|
||||
<div class="item-delete">
|
||||
<AIcon type="DeleteOutlined" />
|
||||
</div>
|
||||
</j-popconfirm>
|
||||
</CheckItem>
|
||||
</div>
|
||||
</j-form-item>
|
||||
<template v-if="!isLast && type === 'serial'">
|
||||
<div
|
||||
:class="[
|
||||
|
@ -399,6 +414,8 @@ import { iconMap, itemNotifyIconMap, typeIconMap } from './util';
|
|||
import FilterGroup from './FilterGroup.vue';
|
||||
import { randomString } from '@/utils/utils'
|
||||
import { EventEmitter, EventEmitterKeys } from '@/views/rule-engine/Scene/Save/util'
|
||||
import CheckItem from './CheckItem.vue'
|
||||
import { Form } from 'jetlinks-ui-components'
|
||||
|
||||
const sceneStore = useSceneStore();
|
||||
const { data: _data } = storeToRefs(sceneStore);
|
||||
|
@ -443,7 +460,7 @@ const eventEmitterKey = EventEmitterKeys({
|
|||
branchGroup: props.thenName,
|
||||
action: props.name
|
||||
})
|
||||
|
||||
const formItemContext = Form.useInjectFormItemContext()
|
||||
const termsOptions = computed(() => {
|
||||
if (!props.parallel) {
|
||||
// 串行
|
||||
|
@ -543,6 +560,56 @@ const onPropsCancel = () => {
|
|||
actionType.value = '';
|
||||
};
|
||||
|
||||
const rules = [{
|
||||
validator(_: any, v?: ActionsType) {
|
||||
console.log('validator',v)
|
||||
if (v?.executor === 'device') {
|
||||
if(!v.device?.productId || !v.device?.selectorValues) {
|
||||
return Promise.reject(new Error('该数据已发生变更,请重新配置'))
|
||||
}
|
||||
}
|
||||
return Promise.resolve()
|
||||
}
|
||||
}]
|
||||
|
||||
const formTouchOff = () => {
|
||||
console.log('formTouchOff')
|
||||
formItemContext.onFieldChange()
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验当前执行动作的设备或者产品是否删除
|
||||
*/
|
||||
const checkDeviceDelete = async () => {
|
||||
const item = _data.value.branches![props.branchesName].then[props.thenName].actions[props.name].device
|
||||
const proResp = await queryProductList({ terms: [{ terms: [{ column: 'id', termType: 'eq', value: item!.productId }]}]})
|
||||
if (proResp.success && (proResp.result as any)?.total === 0) { // 产品已删除
|
||||
_data.value.branches![props.branchesName].then[props.thenName].actions[props.name].device!.productId = undefined
|
||||
formTouchOff()
|
||||
return
|
||||
}
|
||||
const deviceList = item!.selectorValues?.map(item => item.value) || []
|
||||
const deviceResp = await deviceQuery({ terms: [{ terms: [{ column: 'id', termType: 'in', value: deviceList.toString() }]}]})
|
||||
if (deviceResp.success && (deviceResp.result as any)?.total < (item!.selectorValues?.length || 0)) { // 某一个设备被删除
|
||||
_data.value.branches![props.branchesName].then[props.thenName].actions[props.name].device!.selectorValues = undefined
|
||||
formTouchOff()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验当前执行动作的通知配置、消息模板是否删除
|
||||
*/
|
||||
const checkNoticeDelete = () => {
|
||||
|
||||
}
|
||||
|
||||
nextTick(() => {
|
||||
if (_data.value.branches![props.branchesName].then[props.thenName].actions[props.name]?.executor === 'device') {
|
||||
checkDeviceDelete()
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
@ -571,7 +638,7 @@ const onPropsCancel = () => {
|
|||
|
||||
.actions-item {
|
||||
position: relative;
|
||||
margin-bottom: 24px;
|
||||
//margin-bottom: 24px;
|
||||
padding: 16px;
|
||||
border: 1px dashed #999;
|
||||
border-radius: 2px;
|
||||
|
|
|
@ -73,6 +73,19 @@ const thenName = computed(() => {
|
|||
return _data.value.branches![props.branchesName].then.findIndex(item => item.parallel === props.parallel)
|
||||
})
|
||||
|
||||
|
||||
const rules = [{
|
||||
validator(_: any, v?: ActionsType) {
|
||||
console.log('validator',v)
|
||||
if (v?.executor === 'device') {
|
||||
if(!v.device?.productId || !v.device?.selectorValues) {
|
||||
return Promise.reject(new Error('该数据已发生变更,请重新配置'))
|
||||
}
|
||||
}
|
||||
return Promise.resolve()
|
||||
}
|
||||
}]
|
||||
|
||||
const onAdd = () => {
|
||||
visible.value = true;
|
||||
};
|
||||
|
|
|
@ -15,10 +15,12 @@
|
|||
:treeData="builtInList"
|
||||
placeholder="请选择参数"
|
||||
style="width: calc(100% - 120px)"
|
||||
:fieldNames="{ label: 'name', value: 'id' }"
|
||||
@change="(val) => itemOnChange(undefined, val)"
|
||||
>
|
||||
<template #title="{ name, description }">
|
||||
<template #title="{ fullName, description }">
|
||||
<j-space>
|
||||
{{ name }}
|
||||
{{ fullName }}
|
||||
<span style="color: grey; margin-left: 5px">{{ description }}</span>
|
||||
</j-space>
|
||||
</template>
|
||||
|
@ -95,14 +97,15 @@ const sourceChange = (val: any) => {
|
|||
emit('update:value', {
|
||||
...props.value,
|
||||
source: val,
|
||||
value: undefined,
|
||||
value: undefined
|
||||
});
|
||||
};
|
||||
|
||||
const itemOnChange = (val: any) => {
|
||||
const itemOnChange = (val: any, _upperKey?: string) => {
|
||||
emit('update:value', {
|
||||
...props.value,
|
||||
value: val,
|
||||
upperKey: _upperKey
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -151,6 +151,7 @@ const onAdd = (actionItem: any, _parallel: boolean) => {
|
|||
}
|
||||
formItemContext.onFieldChange()
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped>
|
||||
|
|
|
@ -27,15 +27,17 @@
|
|||
v-if='component === "select"'
|
||||
:value='selectValue'
|
||||
:options='options'
|
||||
:valueName='valueName'
|
||||
@click='menuSelect'
|
||||
/>
|
||||
<div style='min-width: 400px' v-else>
|
||||
<j-tree
|
||||
v-model:expandedKeys="treeOpenKeys"
|
||||
:selectedKeys='selectValue ? [selectValue] : []'
|
||||
:treeData='options'
|
||||
@select='treeSelect'
|
||||
:height='450'
|
||||
:virtual='true'
|
||||
@select='treeSelect'
|
||||
>
|
||||
<template #title="{ name, description }">
|
||||
<j-space>
|
||||
|
@ -60,6 +62,7 @@ import DropMenus from './Menus.vue'
|
|||
import DropdownTimePicker from './Time.vue'
|
||||
import { getOption } from './util'
|
||||
import type { DropdownButtonOptions } from './util'
|
||||
import {openKeysByTree} from "@/utils/comm";
|
||||
|
||||
type LabelType = string | number | boolean | undefined
|
||||
|
||||
|
@ -108,7 +111,7 @@ const emit = defineEmits<Emit>()
|
|||
const label = ref<LabelType>(props.placeholder)
|
||||
const selectValue = ref(props.value)
|
||||
const visible = ref(false)
|
||||
|
||||
const treeOpenKeys = ref<(string|number)[]>([])
|
||||
const visibleChange = (v: boolean) => {
|
||||
visible.value = v
|
||||
}
|
||||
|
@ -147,10 +150,11 @@ const menuSelect = (v: string, option: any) => {
|
|||
watchEffect(() => {
|
||||
const option = getOption(props.options, props.value, props.valueName)
|
||||
selectValue.value = props.value
|
||||
if (option) {
|
||||
if (option) { // 数据回显
|
||||
label.value = option[props.labelName] || option.name
|
||||
treeOpenKeys.value = openKeysByTree(props.options, props.value, props.valueName)
|
||||
} else {
|
||||
label.value = props.value || props.placeholder
|
||||
label.value = props.value !== undefined ? props.value : props.placeholder
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
<template>
|
||||
<j-menu class='scene-dropdown-menus' @click='click' :selectedKeys='[myValue]'>
|
||||
<j-menu-item v-for='item in myOptions' :key='item.value' :title='item.label'>
|
||||
{{ item.label }}
|
||||
<Ellipsis >
|
||||
{{ item.label }}
|
||||
</Ellipsis>
|
||||
</j-menu-item>
|
||||
</j-menu>
|
||||
</template>
|
||||
|
@ -24,6 +26,10 @@ const props = defineProps({
|
|||
options: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
valueName: {
|
||||
type: String,
|
||||
default: 'value'
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -53,7 +59,7 @@ const handleBoolean = (key: string) => {
|
|||
|
||||
const click = (e: any) => {
|
||||
const _key = ['true', 'false'].includes(e.key) ? handleBoolean(e.key) : e.key
|
||||
const option = getOption(myOptions.value, _key)
|
||||
const option = getOption(myOptions.value, _key, props.valueName)
|
||||
myValue.value = _key
|
||||
emit('update:value', _key)
|
||||
emit('click', _key, {
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
v-model:value='myValue'
|
||||
class='manual-time-picker'
|
||||
:format='myFormat'
|
||||
:valueFormat='myFormat'
|
||||
:getPopupContainer='getPopupContainer'
|
||||
popupClassName='manual-time-picker-popup'
|
||||
@change='change'
|
||||
|
@ -16,6 +17,7 @@
|
|||
class='manual-time-picker'
|
||||
v-model:value='myValue'
|
||||
:format='myFormat'
|
||||
:valueFormat='myFormat'
|
||||
:getPopupContainer='getPopupContainer'
|
||||
popupClassName='manual-time-picker-popup'
|
||||
@change='change'
|
||||
|
@ -48,15 +50,17 @@ const props = defineProps({
|
|||
|
||||
const emit = defineEmits<Emit>()
|
||||
const myFormat = props.format || ( props.type === 'time' ? 'HH:mm:ss' : 'YYYY-MM-DD HH:mm:ss')
|
||||
const myValue = ref<Dayjs>(dayjs(props.value || new Date(), myFormat))
|
||||
// const myValue = ref<Dayjs>(dayjs(props.value || new Date(), myFormat))
|
||||
const myValue = ref<string>(props.value || dayjs(new Date()).format(myFormat))
|
||||
|
||||
const getPopupContainer = (trigger: HTMLElement) => {
|
||||
return trigger?.parentNode || document.body
|
||||
}
|
||||
|
||||
const change = (e: Dayjs) => {
|
||||
emit('update:value', e.format(myFormat))
|
||||
emit('change', e.format(myFormat))
|
||||
const change = (e: string) => {
|
||||
myValue.value = e
|
||||
emit('update:value', e)
|
||||
emit('change', e)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { isEqual } from 'lodash-es'
|
||||
|
||||
export type DropdownButtonOptions = {
|
||||
label: string;
|
||||
value: string;
|
||||
|
@ -34,7 +36,7 @@ export const getOption = (data: any[], value?: string | number | boolean, key: s
|
|||
if (value === undefined && value === null) return option
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const item = data[i]
|
||||
if (item[key] === value) {
|
||||
if (isEqual(item[key], value)) {
|
||||
option = data[i]
|
||||
break
|
||||
} else if (item.children && item.children.length) {
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
class='scene-select-value'
|
||||
trigger='click'
|
||||
v-model:visible='visible'
|
||||
:overlayStyle='{
|
||||
maxWidth: "300px"
|
||||
}'
|
||||
@visibleChange='visibleChange'
|
||||
>
|
||||
<div @click.prevent='visible = true'>
|
||||
|
@ -35,6 +38,7 @@
|
|||
<DropdownMenus
|
||||
v-if='(["metric", "upper"].includes(item.key) ? metricOptions : options).length'
|
||||
:options='["metric", "upper"].includes(item.key) ? metricOptions : options'
|
||||
:valueName='valueName'
|
||||
@click='onSelect'
|
||||
/>
|
||||
<div class='scene-select-empty' v-else>
|
||||
|
@ -44,11 +48,12 @@
|
|||
<template v-else-if='item.component === "tree"'>
|
||||
<div style='min-width: 400px' v-if='(item.key === "upper" ? metricOptions : options).length'>
|
||||
<j-tree
|
||||
v-model:expandedKeys="treeOpenKeys"
|
||||
:selectedKeys='myValue ? [myValue] : []'
|
||||
:treeData='item.key === "upper" ? metricOptions : options'
|
||||
@select='treeSelect'
|
||||
:height='450'
|
||||
:virtual='true'
|
||||
@select='treeSelect'
|
||||
>
|
||||
<template #title="{ name, description }">
|
||||
<j-space>
|
||||
|
@ -84,6 +89,8 @@ import type { ValueType } from './typings'
|
|||
import { defaultSetting } from './typings'
|
||||
import { DropdownMenus, DropdownTimePicker} from '../DropdownButton'
|
||||
import { getOption } from '../DropdownButton/util'
|
||||
import { isArray } from 'lodash-es'
|
||||
import {openKeysByTree} from "@/utils/comm";
|
||||
|
||||
type Emit = {
|
||||
(e: 'update:value', data: ValueType): void
|
||||
|
@ -101,6 +108,7 @@ const emit = defineEmits<Emit>()
|
|||
const myValue = ref<ValueType>(props.value)
|
||||
const mySource = ref<string>(props.source)
|
||||
const label = ref<any>(props.placeholder)
|
||||
const treeOpenKeys = ref<(string|number)[]>([])
|
||||
const visible = ref(false)
|
||||
|
||||
nextTick(() => {
|
||||
|
@ -120,7 +128,7 @@ const tabsChange = (e: string) => {
|
|||
const treeSelect = (v: any, option: any) => {
|
||||
const node = option.node
|
||||
visible.value = false
|
||||
label.value = node.fullname || node.name
|
||||
label.value = node[props.labelName] || node.name
|
||||
emit('update:value', node[props.valueName])
|
||||
emit('select', node, label.value, { 0: label.value })
|
||||
}
|
||||
|
@ -133,7 +141,7 @@ const valueItemChange = (e: string) => {
|
|||
|
||||
const onSelect = (e: string, option: any) => {
|
||||
visible.value = false
|
||||
label.value = option.label
|
||||
label.value = option[props.labelName]
|
||||
emit('update:value', e)
|
||||
emit('select', e, label.value, { 0: label.value })
|
||||
}
|
||||
|
@ -150,14 +158,19 @@ const visibleChange = (v: boolean) => {
|
|||
}
|
||||
|
||||
watchEffect(() => {
|
||||
const _options = props.source === 'upper' ? props.metricOptions : props.options
|
||||
const _options = ['metric', 'upper'].includes(props.source) ? props.metricOptions : props.options
|
||||
const option = getOption(_options, props.value as string, props.valueName) // 回显label值
|
||||
myValue.value = props.value
|
||||
mySource.value = props.source
|
||||
if (option) {
|
||||
label.value = option[props.labelName] || option.name
|
||||
treeOpenKeys.value = openKeysByTree(_options, props.value, props.valueName)
|
||||
} else {
|
||||
label.value = props.value || props.placeholder
|
||||
let doubleNull = false
|
||||
if (isArray(props.value)) {
|
||||
doubleNull = !!props.value.filter(item => !!item).length
|
||||
}
|
||||
label.value = props.value !== undefined && !doubleNull ? props.value : props.placeholder
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
<j-popconfirm
|
||||
title='确认删除?'
|
||||
@confirm='onDelete'
|
||||
:overlayStyle='{minWidth: "180px"}'
|
||||
>
|
||||
<div v-if='!isFirst' class='terms-params-delete danger show'>
|
||||
<AIcon type='DeleteOutlined' />
|
||||
|
|
|
@ -58,7 +58,7 @@
|
|||
v-model:source='paramsValue.value.source'
|
||||
@select='valueSelect'
|
||||
/>
|
||||
<j-popconfirm title='确认删除?' @confirm='onDelete'>
|
||||
<j-popconfirm title='确认删除?' @confirm='onDelete' :overlayStyle='{minWidth: "180px"}'>
|
||||
<div v-show='showDelete' class='button-delete'> <AIcon type='CloseOutlined' /></div>
|
||||
</j-popconfirm>
|
||||
</div>
|
||||
|
@ -81,7 +81,7 @@ import { ContextKey } from './util'
|
|||
import { useSceneStore } from 'store/scene'
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { Form } from 'jetlinks-ui-components'
|
||||
import { pick } from 'lodash-es'
|
||||
import {isArray, pick} from 'lodash-es'
|
||||
|
||||
const sceneStore = useSceneStore()
|
||||
const { data: formModel } = storeToRefs(sceneStore)
|
||||
|
@ -154,14 +154,15 @@ const columnOptions: any = inject(ContextKey) //
|
|||
const termTypeOptions = ref<Array<{ id: string, name: string}>>([]) // 条件值
|
||||
const valueOptions = ref<any[]>([]) // 默认手动输入下拉
|
||||
const metricOption = ref<any[]>([]) // 根据termType获取对应指标值
|
||||
const isMetric = ref<boolean>(false) // 是否为指标值
|
||||
const tabsOptions = ref<Array<TabsOption>>([{ label: '手动输入', key: 'manual', component: 'string' }])
|
||||
const arrayParamsKey = ['nbtw', 'btw', 'in', 'nin']
|
||||
let metricsCacheOption: any[] = [] // 缓存指标值
|
||||
const metricsCacheOption = ref<any[]>([]) // 缓存指标值
|
||||
|
||||
const handOptionByColumn = (option: any) => {
|
||||
if (option) {
|
||||
termTypeOptions.value = option.termTypes || []
|
||||
metricsCacheOption = option.metrics || []
|
||||
metricsCacheOption.value = option.metrics?.map((item: any) => ({...item, label: item.name})) || []
|
||||
tabsOptions.value.length = 1
|
||||
tabsOptions.value[0].component = option.dataType
|
||||
|
||||
|
@ -169,6 +170,9 @@ const handOptionByColumn = (option: any) => {
|
|||
tabsOptions.value.push(
|
||||
{ label: '指标值', key: 'metric', component: 'select' }
|
||||
)
|
||||
isMetric.value = true
|
||||
} else {
|
||||
isMetric.value = false
|
||||
}
|
||||
|
||||
if (option.dataType === 'boolean') {
|
||||
|
@ -183,7 +187,7 @@ const handOptionByColumn = (option: any) => {
|
|||
}
|
||||
} else {
|
||||
termTypeOptions.value = []
|
||||
metricsCacheOption = []
|
||||
metricsCacheOption.value = []
|
||||
valueOptions.value = []
|
||||
}
|
||||
}
|
||||
|
@ -212,12 +216,12 @@ watch(() => [columnOptions.value, paramsValue.column], () => {
|
|||
|
||||
const showDouble = computed(() => {
|
||||
const isRange = paramsValue.termType ? arrayParamsKey.includes(paramsValue.termType) : false
|
||||
if (metricsCacheOption.length) {
|
||||
metricOption.value = metricsCacheOption.filter(item => isRange ? item.range : !item.range)
|
||||
if (metricsCacheOption.value.length) {
|
||||
metricOption.value = metricsCacheOption.value.filter(item => isRange ? item.range : !item.range)
|
||||
} else {
|
||||
metricOption.value = []
|
||||
}
|
||||
return isRange
|
||||
return isRange && !isMetric
|
||||
})
|
||||
|
||||
const mouseover = () => {
|
||||
|
@ -244,11 +248,11 @@ const columnSelect = (option: any) => {
|
|||
formItemContext.onFieldChange()
|
||||
formModel.value.options!.when[props.branchName].terms[props.whenName].terms[props.name][0] = option.name
|
||||
formModel.value.options!.when[props.branchName].terms[props.whenName].terms[props.name][1] = paramsValue.termType
|
||||
|
||||
}
|
||||
|
||||
const termsTypeSelect = (e: { key: string, name: string }) => {
|
||||
const value = arrayParamsKey.includes(e.key) ? [ undefined, undefined ] : undefined
|
||||
const oldValue = isArray(paramsValue.value!.value) ? paramsValue.value!.value[0] : paramsValue.value!.value
|
||||
const value = arrayParamsKey.includes(e.key) ? [ oldValue, undefined ] : oldValue
|
||||
paramsValue.value = {
|
||||
source: tabsOptions.value[0].key,
|
||||
value: value
|
||||
|
@ -266,6 +270,7 @@ const valueSelect = (_: any, label: string, labelObj: Record<number, any>) => {
|
|||
}
|
||||
|
||||
const typeSelect = (e: any) => {
|
||||
emit('update:value', { ...paramsValue })
|
||||
formModel.value.options!.when[props.branchName].terms[props.whenName].terms[props.name][3] = e.label
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
>
|
||||
<j-popconfirm
|
||||
title='确认删除?'
|
||||
:overlayStyle='{minWidth: "180px"}'
|
||||
@confirm='onDelete'
|
||||
>
|
||||
<div v-show='showDelete' class='terms-params-delete'>
|
||||
|
@ -97,7 +98,7 @@ const rules = [
|
|||
if (!v.termType) {
|
||||
return Promise.reject(new Error('请选择操作符'));
|
||||
}
|
||||
if (!v.value?.value) {
|
||||
if (v.value?.value === undefined) {
|
||||
return Promise.reject(new Error('请选择或输入参数值'));
|
||||
}
|
||||
if (
|
||||
|
@ -107,6 +108,9 @@ const rules = [
|
|||
return Promise.reject(new Error('请选择或输入参数值'));
|
||||
}
|
||||
} else {
|
||||
if (v?.error) { // 数据发生变化
|
||||
return Promise.reject(new Error('该数据已发生变更,请重新配置'))
|
||||
}
|
||||
return Promise.reject(new Error('请选择参数'));
|
||||
}
|
||||
return Promise.resolve();
|
||||
|
|
|
@ -13,11 +13,8 @@ export const handleParamsData = (data: any[], key: string = 'column'): any[] =>
|
|||
|
||||
export const thenRules = [{
|
||||
validator(_: string, value: any) {
|
||||
if (!value || (value && !value.length)) {
|
||||
if (!value || (value && !value.length) || !value.some(item => item.actions && item.actions.length)) {
|
||||
return Promise.reject('至少配置一个执行动作')
|
||||
} else {
|
||||
const isActions = value.some((item: any) => item.actions && item.actions.length)
|
||||
return isActions ? Promise.resolve() : Promise.reject('至少配置一个执行动作');
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ import { handleParamsData } from './components/Terms/util'
|
|||
import { useSceneStore } from 'store/scene'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import type { FormModelType } from '@/views/rule-engine/Scene/typings'
|
||||
import { isArray } from 'lodash-es'
|
||||
|
||||
interface Params {
|
||||
branch: number
|
||||
|
|
|
@ -52,14 +52,14 @@
|
|||
{{ slotProps.name }}
|
||||
</span>
|
||||
</Ellipsis>
|
||||
<div class="subTitle">
|
||||
<Ellipsis :lineClamp="2">
|
||||
<div class="subTitle">
|
||||
说明:{{
|
||||
slotProps?.description ||
|
||||
typeMap.get(slotProps.triggerType)?.tip
|
||||
}}
|
||||
</div>
|
||||
</Ellipsis>
|
||||
</div>
|
||||
</template>
|
||||
<template #actions="item">
|
||||
<PermissionButton
|
||||
|
|
|
@ -103,11 +103,6 @@
|
|||
<j-checkbox-group
|
||||
v-model:value="form.data.integrationModes"
|
||||
:options="joinOptions"
|
||||
@change="
|
||||
form.integrationModesISO = [
|
||||
...form.data.integrationModes,
|
||||
]
|
||||
"
|
||||
/>
|
||||
</j-form-item>
|
||||
|
||||
|
@ -333,6 +328,7 @@
|
|||
.clientId
|
||||
"
|
||||
placeholder="请输入appId"
|
||||
:disabled="!!form.data.id"
|
||||
/>
|
||||
</j-form-item>
|
||||
<j-form-item
|
||||
|
@ -408,7 +404,7 @@
|
|||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '该字段是必填字段',
|
||||
message: '请输入授权地址',
|
||||
},
|
||||
]"
|
||||
>
|
||||
|
@ -439,7 +435,7 @@
|
|||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '请选择认证方式',
|
||||
message: '请选择请求方式',
|
||||
},
|
||||
]"
|
||||
>
|
||||
|
@ -448,6 +444,7 @@
|
|||
form.data.apiClient.authConfig.oauth2
|
||||
.tokenRequestType
|
||||
"
|
||||
placeholder="请选择请求方式"
|
||||
>
|
||||
<j-select-option value="POST_BODY">
|
||||
请求体
|
||||
|
@ -469,7 +466,11 @@
|
|||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '该字段是必填字段',
|
||||
message: '请输入client_id',
|
||||
},
|
||||
{
|
||||
max: 64,
|
||||
message: '最多可输入64个字符',
|
||||
},
|
||||
]"
|
||||
>
|
||||
|
@ -499,7 +500,11 @@
|
|||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '该字段是必填字段',
|
||||
message: '请输入client_secret',
|
||||
},
|
||||
{
|
||||
max: 64,
|
||||
message: '最多可输入64个字符',
|
||||
},
|
||||
]"
|
||||
>
|
||||
|
@ -537,6 +542,10 @@
|
|||
required: true,
|
||||
message: '该字段是必填字段',
|
||||
},
|
||||
{
|
||||
max: 64,
|
||||
message: '最多可输入64个字符',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<j-input
|
||||
|
@ -560,6 +569,10 @@
|
|||
required: true,
|
||||
message: '该字段是必填字段',
|
||||
},
|
||||
{
|
||||
max: 64,
|
||||
message: '最多可输入64个字符',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<j-input
|
||||
|
@ -576,7 +589,12 @@
|
|||
form.data.apiClient.authConfig.type === 'bearer'
|
||||
"
|
||||
label="token"
|
||||
:name="['apiClient', 'authConfig', 'token']"
|
||||
:name="[
|
||||
'apiClient',
|
||||
'authConfig',
|
||||
'bearer',
|
||||
'token',
|
||||
]"
|
||||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
|
@ -586,7 +604,7 @@
|
|||
>
|
||||
<j-input
|
||||
v-model:value="
|
||||
form.data.apiClient.authConfig.token
|
||||
form.data.apiClient.authConfig.bearer.token
|
||||
"
|
||||
placeholder="请输入token"
|
||||
/>
|
||||
|
@ -594,7 +612,18 @@
|
|||
</div>
|
||||
|
||||
<div v-if="form.data.provider !== 'internal-integrated'">
|
||||
<j-form-item>
|
||||
<j-form-item
|
||||
:name="['apiClient', 'headers']"
|
||||
:rules="[
|
||||
{
|
||||
required: !headerValid,
|
||||
message: '请输入请求头',
|
||||
},
|
||||
{
|
||||
validator: headerValidator,
|
||||
},
|
||||
]"
|
||||
>
|
||||
<template #label>
|
||||
<FormLabel
|
||||
text="请求头"
|
||||
|
@ -604,11 +633,25 @@
|
|||
|
||||
<RequestTable
|
||||
v-model:value="form.data.apiClient.headers"
|
||||
v-model:valid="headerValid"
|
||||
/>
|
||||
</j-form-item>
|
||||
<j-form-item label="参数">
|
||||
<j-form-item
|
||||
label="参数"
|
||||
:name="['apiClient', 'parameters']"
|
||||
:rules="[
|
||||
{
|
||||
required: !paramsValid,
|
||||
message: '请输入参数',
|
||||
},
|
||||
{
|
||||
validator: paramsValidator,
|
||||
},
|
||||
]"
|
||||
>
|
||||
<RequestTable
|
||||
v-model:value="form.data.apiClient.parameters"
|
||||
v-model:valid="paramsValid"
|
||||
/>
|
||||
</j-form-item>
|
||||
</div>
|
||||
|
@ -657,10 +700,6 @@
|
|||
required: true,
|
||||
message: '请输入secureKey',
|
||||
},
|
||||
{
|
||||
max: 64,
|
||||
message: '最多可输入64个字符',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<template #label>
|
||||
|
@ -696,7 +735,7 @@
|
|||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '请选中角色',
|
||||
message: '请选择角色',
|
||||
},
|
||||
]"
|
||||
>
|
||||
|
@ -711,7 +750,7 @@
|
|||
v-model:value="form.data.apiServer.roleIdList"
|
||||
:options="form.roleIdList"
|
||||
mode="multiple"
|
||||
placeholder="请选中角色"
|
||||
placeholder="请选择角色"
|
||||
></j-select>
|
||||
<PermissionButton
|
||||
:hasPermission="`${rolePermission}:update`"
|
||||
|
@ -749,6 +788,9 @@
|
|||
multiple
|
||||
:tree-data="form.orgIdList"
|
||||
placeholder="请选择组织"
|
||||
:filterTreeNode="
|
||||
(v: string, node: any) => filterSelectNode(v, node, 'name')
|
||||
"
|
||||
>
|
||||
<template #title="{ name }">
|
||||
{{ name }}
|
||||
|
@ -760,7 +802,7 @@
|
|||
@click="
|
||||
clickAddItem(
|
||||
form.data.apiServer.orgIdList,
|
||||
'Role',
|
||||
'Department',
|
||||
)
|
||||
"
|
||||
class="add-item"
|
||||
|
@ -821,7 +863,7 @@
|
|||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '该字段是必填字段',
|
||||
message: '请选择认证方式',
|
||||
},
|
||||
]"
|
||||
>
|
||||
|
@ -948,7 +990,7 @@
|
|||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '该字段是必填字段',
|
||||
message: '请输入授权地址',
|
||||
},
|
||||
]"
|
||||
>
|
||||
|
@ -981,7 +1023,7 @@
|
|||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '该字段是必填字段',
|
||||
message: '请输入token地址',
|
||||
},
|
||||
]"
|
||||
>
|
||||
|
@ -1002,7 +1044,7 @@
|
|||
<j-form-item label="logo">
|
||||
<j-upload
|
||||
v-model:file-list="form.fileList"
|
||||
accept=".jpg,.png,.jfif,.pjp,.pjpeg,.jpeg"
|
||||
accept=".jpg,.png"
|
||||
:maxCount="1"
|
||||
list-type="picture-card"
|
||||
:show-upload-list="false"
|
||||
|
@ -1010,6 +1052,7 @@
|
|||
[TOKEN_KEY]: LocalStore.get(TOKEN_KEY),
|
||||
}"
|
||||
:action="`${BASE_API_PATH}/file/static`"
|
||||
:beforeUpload="beforeLogoUpload"
|
||||
@change="changeBackUpload"
|
||||
>
|
||||
<img
|
||||
|
@ -1050,7 +1093,7 @@
|
|||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '该字段是必填字段',
|
||||
message: '请输入用户信息地址',
|
||||
},
|
||||
]"
|
||||
>
|
||||
|
@ -1074,7 +1117,7 @@
|
|||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '请输入该字段是必填字段用户ID',
|
||||
message: '请输入用户ID',
|
||||
},
|
||||
]"
|
||||
>
|
||||
|
@ -1105,7 +1148,7 @@
|
|||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '该字段是必填字段',
|
||||
message: '请输入用户名',
|
||||
},
|
||||
]"
|
||||
>
|
||||
|
@ -1165,6 +1208,10 @@
|
|||
required: true,
|
||||
message: '请输入appId',
|
||||
},
|
||||
{
|
||||
max: 64,
|
||||
message: '最多可输入64个字符',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<template #label>
|
||||
|
@ -1196,6 +1243,10 @@
|
|||
required: true,
|
||||
message: '请输入appKey',
|
||||
},
|
||||
{
|
||||
max: 64,
|
||||
message: '最多可输入64个字符',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<template #label>
|
||||
|
@ -1227,6 +1278,10 @@
|
|||
required: true,
|
||||
message: '请输入appSecret',
|
||||
},
|
||||
{
|
||||
max: 64,
|
||||
message: '最多可输入64个字符',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<template #label>
|
||||
|
@ -1281,6 +1336,10 @@
|
|||
required: true,
|
||||
message: '请输入默认密码',
|
||||
},
|
||||
{
|
||||
max: 64,
|
||||
message: '最多可输入64个字符',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<j-input
|
||||
|
@ -1294,7 +1353,7 @@
|
|||
v-model:value="form.data.sso.roleIdList"
|
||||
mode="multiple"
|
||||
:options="form.roleIdList"
|
||||
placeholder="请选中角色"
|
||||
placeholder="请选择角色"
|
||||
></j-select>
|
||||
<PermissionButton
|
||||
:hasPermission="`${rolePermission}:update`"
|
||||
|
@ -1383,7 +1442,7 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import { BASE_API_PATH, TOKEN_KEY } from '@/utils/variable';
|
||||
import { LocalStore } from '@/utils/comm';
|
||||
import { LocalStore, filterSelectNode } from '@/utils/comm';
|
||||
|
||||
import {
|
||||
getDepartmentList_api,
|
||||
|
@ -1436,7 +1495,7 @@ const initForm: formType = {
|
|||
type: 'oauth2', // 类型, 可选值:none, bearer, oauth2, basic, other
|
||||
bearer: { token: '' }, // 授权信息
|
||||
basic: { username: '', password: '' }, // 基本信息
|
||||
token: '',
|
||||
// token: '',
|
||||
oauth2: {
|
||||
// OAuth2信息
|
||||
authorizationUrl: '', // 授权地址
|
||||
|
@ -1446,7 +1505,7 @@ const initForm: formType = {
|
|||
clientSecret: '', // 客户端密钥
|
||||
grantType: '', // 类型
|
||||
accessTokenProperty: '', // token属性名
|
||||
tokenRequestType: '', // token请求方式, 可选值:POST_URI,POST_BODY
|
||||
tokenRequestType: undefined, // token请求方式, 可选值:POST_URI,POST_BODY
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1515,6 +1574,21 @@ const form = reactive({
|
|||
fileList: [] as any[],
|
||||
uploadLoading: false,
|
||||
});
|
||||
|
||||
// 请求头和参数必填验证
|
||||
const headerValid = ref(true);
|
||||
const paramsValid = ref(true);
|
||||
const headerValidator = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
headerValid.value ? resolve('') : reject('请输入完整的请求头');
|
||||
});
|
||||
};
|
||||
const paramsValidator = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
paramsValid.value ? resolve('') : reject('请输入完整的请求参数');
|
||||
});
|
||||
};
|
||||
|
||||
// 接入方式的选项
|
||||
const joinOptions = computed(() => {
|
||||
if (form.data.provider === 'internal-standalone')
|
||||
|
@ -1614,12 +1688,27 @@ function init() {
|
|||
o.forEach((key) => {
|
||||
if (!n.includes(key)) form.errorNumInfo[key].clear();
|
||||
});
|
||||
|
||||
form.integrationModesISO = [...n];
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
function getInfo(id: string) {
|
||||
getAppInfo_api(id).then((resp: any) => {
|
||||
// 后端返回的headers和parameters中, key转为label
|
||||
resp.result.apiClient.headers = resp.result.apiClient.headers.map(
|
||||
(m: any) => ({
|
||||
...m,
|
||||
label: m.key,
|
||||
}),
|
||||
);
|
||||
resp.result.apiClient.parameters = resp.result.apiClient.parameters.map(
|
||||
(m: any) => ({
|
||||
...m,
|
||||
label: m.key,
|
||||
}),
|
||||
);
|
||||
form.data = {
|
||||
...resp.result,
|
||||
integrationModes: resp.result.integrationModes.map(
|
||||
|
@ -1643,7 +1732,7 @@ function getRoleIdList() {
|
|||
}
|
||||
// 获取组织列表
|
||||
function getOrgIdList() {
|
||||
getDepartmentList_api().then((resp) => {
|
||||
getDepartmentList_api({ paging: false }).then((resp) => {
|
||||
if (resp.status === 200) {
|
||||
form.orgIdList = resp.result as dictType;
|
||||
}
|
||||
|
@ -1660,6 +1749,7 @@ function clickAddItem(data: string[], target: string) {
|
|||
}
|
||||
// 保存
|
||||
function clickSave() {
|
||||
console.log('headers: ', form.data.apiClient.headers);
|
||||
formRef.value?.validate().then(() => {
|
||||
const params = cloneDeep(form.data);
|
||||
// 删除多余的参数
|
||||
|
@ -1699,6 +1789,23 @@ function clickSave() {
|
|||
params.id = params.apiServer.appId;
|
||||
}
|
||||
}
|
||||
|
||||
// headers和params参数label需改为key传给后端
|
||||
if (params.integrationModes.includes('apiClient')) {
|
||||
params.apiClient.headers = params.apiClient.headers.map(
|
||||
(m: any) => ({
|
||||
...m,
|
||||
key: m.label,
|
||||
}),
|
||||
);
|
||||
params.apiClient.parameters = params.apiClient.parameters.map(
|
||||
(m: any) => ({
|
||||
...m,
|
||||
key: m.label,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
const request = routeQuery.id
|
||||
? updateApp_api(routeQuery.id as string, params)
|
||||
: addApp_api(params);
|
||||
|
@ -1734,11 +1841,24 @@ function getErrorNum(
|
|||
} else if (!set.has(key)) set.add(key);
|
||||
}
|
||||
}
|
||||
|
||||
const imageTypes = ref(['image/jpg', 'image/png']);
|
||||
const beforeLogoUpload = (file: any) => {
|
||||
const isType: any = imageTypes.value.includes(file.type);
|
||||
if (!isType) {
|
||||
message.error(`请上传.jpg.png格式的图片`);
|
||||
return false;
|
||||
}
|
||||
const isSize = file.size / 1024 / 1024 < 4;
|
||||
if (!isSize) {
|
||||
message.error(`图片大小必须小于${4}M`);
|
||||
}
|
||||
return isType && isSize;
|
||||
};
|
||||
function changeBackUpload(info: UploadChangeParam<UploadFile<any>>) {
|
||||
if (info.file.status === 'uploading') {
|
||||
form.uploadLoading = true;
|
||||
} else if (info.file.status === 'done') {
|
||||
|
||||
info.file.url = info.file.response?.result;
|
||||
form.uploadLoading = false;
|
||||
form.data.sso.configuration.oauth2.logoUrl = info.file.response?.result;
|
||||
|
@ -1798,7 +1918,7 @@ function clearNullProp(obj: object) {
|
|||
color: #000;
|
||||
|
||||
&.ant-radio-button-wrapper-disabled {
|
||||
opacity: .5;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
&.ant-radio-button-wrapper-checked {
|
||||
|
|
|
@ -1,49 +1,89 @@
|
|||
<template>
|
||||
<div class="request-table-container">
|
||||
<j-table
|
||||
:columns="columns"
|
||||
:data-source="tableData"
|
||||
:pagination="false"
|
||||
size="small"
|
||||
bordered
|
||||
>
|
||||
<template #bodyCell="{ column, record, index }">
|
||||
<template v-if="column.dataIndex === 'key'">
|
||||
<j-input v-model:value="record.label" />
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'value'">
|
||||
<j-input
|
||||
v-model:value="record.value"
|
||||
v-if="props.valueType === 'input'"
|
||||
/>
|
||||
<j-select
|
||||
v-else-if="props.valueType === 'select'"
|
||||
v-model:value="record.value"
|
||||
>
|
||||
<j-select-option
|
||||
v-for="item in props.valueOptions"
|
||||
:value="item.value"
|
||||
>{{ item.label }}</j-select-option
|
||||
<j-form ref="formRef" :model="formData" layout="vertical">
|
||||
<j-table
|
||||
:columns="columns"
|
||||
:data-source="formData.tableData"
|
||||
:pagination="false"
|
||||
size="small"
|
||||
bordered
|
||||
>
|
||||
<template #bodyCell="{ column, record, index }">
|
||||
<template v-if="column.dataIndex === 'label'">
|
||||
<j-form-item
|
||||
:name="[
|
||||
'tableData',
|
||||
index + (current - 1) * 10,
|
||||
'label',
|
||||
]"
|
||||
:rules="[
|
||||
{
|
||||
required: !!record.label && !!record.value,
|
||||
message: '该字段为必填字段',
|
||||
trigger: 'change',
|
||||
},
|
||||
]"
|
||||
>
|
||||
</j-select>
|
||||
<j-input v-model:value="record.label" />
|
||||
</j-form-item>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'value'">
|
||||
<j-form-item
|
||||
:name="[
|
||||
'tableData',
|
||||
index + (current - 1) * 10,
|
||||
'value',
|
||||
]"
|
||||
:rules="[
|
||||
{
|
||||
required: !!record.value && !!record.label,
|
||||
message: '该字段为必填字段',
|
||||
trigger: 'change',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<j-input
|
||||
v-model:value="record.value"
|
||||
v-if="props.valueType === 'input'"
|
||||
/>
|
||||
<j-select
|
||||
v-else-if="props.valueType === 'select'"
|
||||
v-model:value="record.value"
|
||||
>
|
||||
<j-select-option
|
||||
v-for="item in props.valueOptions"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</j-select-option>
|
||||
</j-select>
|
||||
</j-form-item>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'action'">
|
||||
<j-button
|
||||
type="link"
|
||||
@click="removeRow((current - 1) * 10 + index)"
|
||||
>
|
||||
<AIcon type="DeleteOutlined" />
|
||||
</j-button>
|
||||
</template>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'action'">
|
||||
<j-button
|
||||
type="link"
|
||||
@click="removeRow((current - 1) * 10 + index)"
|
||||
>
|
||||
<AIcon type="DeleteOutlined" />
|
||||
</j-button>
|
||||
</template>
|
||||
</template>
|
||||
</j-table>
|
||||
<j-pagination
|
||||
</j-table>
|
||||
</j-form>
|
||||
<!-- <j-pagination
|
||||
v-show="props.value.length > 10"
|
||||
v-model:current="current"
|
||||
:page-size="10"
|
||||
:total="props.value.length"
|
||||
show-less-items
|
||||
/> -->
|
||||
<RowPagination
|
||||
v-if="props.value.length > 10"
|
||||
v-model:pageNum="current"
|
||||
:pageSize="10"
|
||||
:total="props.value.length"
|
||||
/>
|
||||
|
||||
<j-button type="dashed" @click="addRow" class="add-btn">
|
||||
<AIcon type="PlusOutlined" />新增
|
||||
</j-button>
|
||||
|
@ -53,12 +93,13 @@
|
|||
<script setup lang="ts">
|
||||
import type { optionsType } from '../typing';
|
||||
|
||||
const emits = defineEmits(['update:value']);
|
||||
const emits = defineEmits(['update:value', 'update:valid']);
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
value: optionsType;
|
||||
valueType?: 'input' | 'select';
|
||||
valueOptions?: optionsType;
|
||||
valid?: boolean;
|
||||
}>(),
|
||||
{
|
||||
valueType: 'input',
|
||||
|
@ -67,28 +108,50 @@ const props = withDefaults(
|
|||
const columns = [
|
||||
{
|
||||
title: 'KEY',
|
||||
dataIndex: 'key',
|
||||
width: '40%'
|
||||
dataIndex: 'label', // 此处表格需要验证, key为字段验证无法通过, 改为label
|
||||
key: 'label',
|
||||
width: '40%',
|
||||
},
|
||||
{
|
||||
title: 'VALUE',
|
||||
dataIndex: 'value',
|
||||
width: '40%'
|
||||
key: 'value',
|
||||
width: '40%',
|
||||
},
|
||||
{
|
||||
title: ' ',
|
||||
dataIndex: 'action',
|
||||
width: '20%'
|
||||
key: 'action',
|
||||
width: '20%',
|
||||
},
|
||||
];
|
||||
|
||||
const current = ref<number>(1);
|
||||
|
||||
const tableData = computed(() => {
|
||||
return props.value.slice((current.value - 1) * 10, current.value * 10);
|
||||
// const tableData = computed(() => {
|
||||
// return props.value.slice((current.value - 1) * 10, current.value * 10);
|
||||
// });
|
||||
const formData = ref({
|
||||
tableData: computed(() => {
|
||||
return props.value.slice((current.value - 1) * 10, current.value * 10);
|
||||
}),
|
||||
});
|
||||
|
||||
if(props.value.length < 1) addRow()
|
||||
const formRef = ref();
|
||||
watch(
|
||||
() => formData.value,
|
||||
(val) => {
|
||||
formRef.value?.validate();
|
||||
// 每一项都填了, 或者没项都没有填, 均为验证通过, 否则均为未通过
|
||||
const valid = formData.value.tableData.every(
|
||||
(e: any) => (e.label && e.value) || (!e.label && !e.value),
|
||||
);
|
||||
emits('update:valid', valid);
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
|
||||
if (props.value.length < 1) addRow();
|
||||
watch(
|
||||
() => props.value,
|
||||
(n, o) => {
|
||||
|
@ -140,5 +203,8 @@ function addRow() {
|
|||
display: block;
|
||||
margin-top: 10px;
|
||||
}
|
||||
:deep(.ant-form-item) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -10,9 +10,11 @@ export type dictType = {
|
|||
}[];
|
||||
|
||||
export type optionsType = {
|
||||
label: string,
|
||||
label?: string;
|
||||
key?: string;
|
||||
value: string;
|
||||
disabled?: boolean
|
||||
disabled?: boolean;
|
||||
required?: boolean;
|
||||
}[]
|
||||
export type formType = {
|
||||
id?:string,
|
||||
|
@ -34,7 +36,7 @@ export type formType = {
|
|||
type: 'none' | 'bearer' | 'oauth2' | 'basic' | 'other', // 类型, 可选值:none, bearer, oauth2, basic, other
|
||||
bearer: { token: string }, // 授权信息
|
||||
basic: { username: string, password: string }, // 基本信息
|
||||
token: string,
|
||||
token?: string,
|
||||
oauth2: { // OAuth2信息
|
||||
authorizationUrl: string, // 授权地址
|
||||
tokenUrl: string, // token地址
|
||||
|
@ -43,7 +45,7 @@ export type formType = {
|
|||
clientSecret: string, // 客户端密钥
|
||||
grantType: 'authorization_code' | 'client_credentials' | '', // 类型
|
||||
accessTokenProperty: string, // token属性名
|
||||
tokenRequestType: 'POST_URI' | 'POST_BODY' | '' // token请求方式, 可选值:POST_URI,POST_BODY
|
||||
tokenRequestType: 'POST_URI' | 'POST_BODY' | '' | undefined // token请求方式, 可选值:POST_URI,POST_BODY
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -46,7 +46,6 @@
|
|||
enabled: 'success',
|
||||
disabled: 'error',
|
||||
}"
|
||||
hasMark
|
||||
>
|
||||
<template #img>
|
||||
<slot name="img">
|
||||
|
@ -55,7 +54,9 @@
|
|||
</template>
|
||||
<template #content>
|
||||
<h3 class="card-item-content-title">
|
||||
{{ slotProps.name }}
|
||||
<Ellipsis>
|
||||
{{ slotProps.name }}
|
||||
</Ellipsis>
|
||||
</h3>
|
||||
<j-row>
|
||||
<j-col :span="12">
|
||||
|
@ -74,7 +75,9 @@
|
|||
<div class="card-item-content-text">
|
||||
说明
|
||||
</div>
|
||||
<div>{{ slotProps.description }}</div>
|
||||
<Ellipsis>
|
||||
{{ slotProps.description }}
|
||||
</Ellipsis>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</template>
|
||||
|
@ -242,7 +245,7 @@ const columns = [
|
|||
key: 'status',
|
||||
ellipsis: true,
|
||||
search: {
|
||||
rename: 'status',
|
||||
rename: 'state',
|
||||
type: 'select',
|
||||
options: [
|
||||
{
|
||||
|
@ -271,8 +274,8 @@ const columns = [
|
|||
dataIndex: 'action',
|
||||
key: 'action',
|
||||
scopedSlots: true,
|
||||
width:'200px',
|
||||
fixed:'right'
|
||||
width: '200px',
|
||||
fixed: 'right',
|
||||
},
|
||||
];
|
||||
const queryParams = ref({});
|
||||
|
@ -361,7 +364,7 @@ const table = {
|
|||
// 有集成菜单权限
|
||||
if (otherServers.includes('page'))
|
||||
others.children?.push({
|
||||
permission: [`${permission}:add`,`${permission}:update`],
|
||||
permission: [`${permission}:add`, `${permission}:update`],
|
||||
key: 'page',
|
||||
text: '集成菜单',
|
||||
tooltip: {
|
||||
|
@ -378,7 +381,7 @@ const table = {
|
|||
if (otherServers.includes('apiServer'))
|
||||
others.children?.push(
|
||||
{
|
||||
permission: [`${permission}:add`,`${permission}:update`],
|
||||
permission: [`${permission}:add`, `${permission}:update`],
|
||||
key: 'empowerment',
|
||||
text: '赋权',
|
||||
tooltip: {
|
||||
|
@ -394,7 +397,7 @@ const table = {
|
|||
},
|
||||
},
|
||||
{
|
||||
permission: [`${permission}:add`,`${permission}:update`],
|
||||
permission: [`${permission}:add`, `${permission}:update`],
|
||||
key: 'viewApi',
|
||||
text: '查看API',
|
||||
tooltip: {
|
||||
|
|
|
@ -23,12 +23,16 @@
|
|||
style="margin-bottom: 12px"
|
||||
/>
|
||||
<j-tree
|
||||
v-if="treeData.length !== 0"
|
||||
show-line
|
||||
defaultExpandAll
|
||||
multiple
|
||||
draggable
|
||||
:tree-data="treeData"
|
||||
:height="500"
|
||||
@select="onSelect"
|
||||
:selectedKeys="selectedKeys"
|
||||
@drop="onDrop"
|
||||
>
|
||||
<template #title="row">
|
||||
<div class="tree-content">
|
||||
|
@ -38,24 +42,6 @@
|
|||
{{ row.name }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="tree-content-action">
|
||||
<span @click="(e) => e.stopPropagation()">
|
||||
<PermissionButton
|
||||
type="text"
|
||||
:tooltip="{
|
||||
title: '删除',
|
||||
}"
|
||||
hasPermission="DataCollect/Collector:delete"
|
||||
:popConfirm="{
|
||||
title: `确定删除?`,
|
||||
onConfirm: () =>
|
||||
handlDelete(row.id),
|
||||
}"
|
||||
>
|
||||
<AIcon type="CloseOutlined" />
|
||||
</PermissionButton>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</j-tree>
|
||||
|
@ -69,28 +55,32 @@
|
|||
<script setup lang="ts" name="MenuSetting">
|
||||
import { getMenuTree_api } from '@/api/system/menu';
|
||||
import { getSystemPermission as getSystemPermission_api } from '@/api/initHome';
|
||||
import { filterMenu, getKeys, loop } from './utils';
|
||||
import {
|
||||
filterMenu,
|
||||
mergeMapToArr,
|
||||
developArrToMap,
|
||||
drop,
|
||||
select,
|
||||
} from './utils';
|
||||
import BaseMenu from './baseMenu';
|
||||
import type {
|
||||
AntTreeNodeDropEvent,
|
||||
TreeDataItem,
|
||||
TreeProps,
|
||||
} from 'ant-design-vue/es/tree';
|
||||
import type { AntTreeNodeDropEvent } from 'ant-design-vue/es/tree';
|
||||
import { treeFilter } from '@/utils/tree';
|
||||
import { cloneDeep } from 'lodash';
|
||||
|
||||
const treeData = ref<any>();
|
||||
const selectedKeys: any = ref([]);
|
||||
const treeData = ref<any>([]);
|
||||
const filterText = ref('');
|
||||
treeData.value = [...BaseMenu];
|
||||
let systemMenu: any = reactive([]);
|
||||
const baseMenu = cloneDeep(BaseMenu);
|
||||
const systemMenu: any = ref([]);
|
||||
const baseMenu: any = ref([]);
|
||||
const AllMenu = ref([]);
|
||||
|
||||
const BaseMenuMap = new Map();
|
||||
BaseMenu.forEach((item) => {
|
||||
BaseMenuMap.set(item.code, item);
|
||||
});
|
||||
const onSelect = (selecteds: Array<string>, e: any) => {
|
||||
selectedKeys.value = select(selecteds, e);
|
||||
};
|
||||
|
||||
console.log(11, BaseMenuMap);
|
||||
const onDrop = (info: AntTreeNodeDropEvent) => {
|
||||
treeData.value = drop(info, treeData.value);
|
||||
};
|
||||
|
||||
const params = {
|
||||
paging: false,
|
||||
|
@ -114,20 +104,25 @@ const params = {
|
|||
};
|
||||
|
||||
const change = (val: any) => {
|
||||
treeData.value = treeFilter(baseMenu, val.target.value, 'name');
|
||||
};
|
||||
|
||||
const handlDelete = (value) => {
|
||||
console.log(22, value);
|
||||
treeData.value = treeFilter(AllMenu.value, val.target.value, 'name');
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getMenuTree_api(params).then((resp: any) => {
|
||||
if (resp.status == 200) {
|
||||
systemMenu = resp.result;
|
||||
console.log(2, systemMenu);
|
||||
}
|
||||
// transfer.data.rightTreeData = resp.result;
|
||||
getSystemPermission_api().then((resp: any) => {
|
||||
baseMenu.value = filterMenu(
|
||||
resp.result.map((item: any) => JSON.parse(item).id),
|
||||
BaseMenu,
|
||||
);
|
||||
getMenuTree_api(params).then((resp: any) => {
|
||||
if (resp.status == 200) {
|
||||
systemMenu.value = resp.result;
|
||||
const baseMenuData = developArrToMap(baseMenu.value);
|
||||
const systemMenuData = developArrToMap(systemMenu.value, true);
|
||||
selectedKeys.value = systemMenuData.checkedKeys;
|
||||
AllMenu.value = mergeMapToArr(baseMenuData, systemMenuData);
|
||||
treeData.value = cloneDeep(AllMenu.value);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
@ -183,15 +178,13 @@ onMounted(() => {
|
|||
&-content {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
&-title {
|
||||
flex: 1;
|
||||
font-weight: 800;
|
||||
font-size: 12px;
|
||||
line-height: 17px;
|
||||
line-height: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
color: #333333;
|
||||
}
|
||||
&-action {
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
import { TreeProps } from "ant-design-vue";
|
||||
import type {
|
||||
AntTreeNodeDropEvent,
|
||||
TreeProps,
|
||||
TreeDataItem,
|
||||
} from 'ant-design-vue/es/tree';
|
||||
|
||||
/**
|
||||
* 根据权限过滤菜单
|
||||
|
@ -18,21 +22,167 @@ export const filterMenu = (permissions: string[], menus: any[]) => {
|
|||
});
|
||||
};
|
||||
|
||||
// 在树形结构中对id进行匹配,通过callback对匹配id的对象进行操作
|
||||
export const loop= (data: TreeProps['treeData'], id: string | number, callback: any) => {
|
||||
data?.forEach((item, index) => {
|
||||
if (item.id === id) {
|
||||
return callback(item, index, data);
|
||||
}
|
||||
if (item.children) {
|
||||
return loop(item.children, id, callback);
|
||||
/**
|
||||
* 合并Map菜单转成Arr菜单
|
||||
* @param baseMenuData baseMenu developArrToMap平铺后的数据
|
||||
* @param systemMenuData systemMenu developArrToMap平铺后的数据
|
||||
* @returns 合并后的菜单
|
||||
*/
|
||||
export const mergeMapToArr = (baseMenuData: any, systemMenuData: any) => {
|
||||
const updataArr = (r: any) => {
|
||||
for (let i = 0; i < r.length; i++) {
|
||||
const child = r[i].children;
|
||||
if (child) {
|
||||
updataArr(child);
|
||||
}
|
||||
r[i] = newMap.get(r[i].code);
|
||||
delete r[i].parentCode;
|
||||
}
|
||||
};
|
||||
const root: any = [];
|
||||
const newMap = new Map([...baseMenuData?.arrMap, ...systemMenuData.arrMap]);
|
||||
const newRootArr = [
|
||||
...new Set([...baseMenuData?.rootSet, ...systemMenuData.rootSet]),
|
||||
];
|
||||
newRootArr.forEach((item: any) => {
|
||||
root.push(newMap.get(item));
|
||||
});
|
||||
}
|
||||
//
|
||||
export const getKeys = (data: any[]): (string | number)[] => {
|
||||
return data.reduce((pre: (string | number)[], next: any) => {
|
||||
const childrenKeys = next.children ? getKeys(next.children) : [];
|
||||
return [...pre, next.code, ...childrenKeys];
|
||||
}, []);
|
||||
}
|
||||
updataArr(root);
|
||||
return root;
|
||||
};
|
||||
|
||||
/**
|
||||
* 平铺菜单
|
||||
* @param value 默认菜单以及查询系统菜单 baseMenu systemMenu
|
||||
* @param checked 查询系统菜单传true
|
||||
* @returns 平铺菜单Map、根菜单名、系统选中的keys
|
||||
*/
|
||||
export const developArrToMap = (Menu: any, checked = false) => {
|
||||
const rootSet = new Set();
|
||||
const arrMap = new Map();
|
||||
const checkedKeys: any = [];
|
||||
const getMap = (arr: any, parentCode = 'root', preKey = '0') => {
|
||||
arr.forEach((item: any, index: number) => {
|
||||
const key = preKey + `-${index}`; //初始化key
|
||||
item.title = item.code;
|
||||
item.key = key;
|
||||
if (checked) {
|
||||
checkedKeys.push(key);
|
||||
}
|
||||
arrMap.set(item.code, item);
|
||||
if (parentCode === 'root') {
|
||||
rootSet.add(item.code); //处理根菜单
|
||||
}
|
||||
if (item?.children) {
|
||||
getMap(item?.children, item.code, key);
|
||||
}
|
||||
});
|
||||
};
|
||||
getMap(Menu);
|
||||
return { arrMap, rootSet, checkedKeys };
|
||||
};
|
||||
|
||||
/**
|
||||
* 选择功能
|
||||
* @param selecteds onSelect事件默认参数
|
||||
* @param e onSelect事件默认参数
|
||||
* @returns 处理后的keys
|
||||
*/
|
||||
export const select = (selecteds: Array<string>, e: any) => {
|
||||
const { node } = e;
|
||||
const childKeys: Array<string> = [];
|
||||
const getChildKeys = (data: any, preKey = '0') => {
|
||||
data.forEach((item: any, index: number) => {
|
||||
const checkedKey = preKey + `-${index}`;
|
||||
childKeys.push(checkedKey);
|
||||
if (item?.children) {
|
||||
getChildKeys(item?.children, checkedKey);
|
||||
}
|
||||
});
|
||||
};
|
||||
if (node?.children) {
|
||||
getChildKeys(node.children, node.key);
|
||||
}
|
||||
|
||||
const Keys = new Set(selecteds);
|
||||
const selectedAllKeys = [...[node.key, ...childKeys]];
|
||||
selectedAllKeys.forEach((item: string) => {
|
||||
Keys[e.selected ? 'add' : 'delete'](item);
|
||||
});
|
||||
return [...Keys];
|
||||
};
|
||||
|
||||
/**
|
||||
* 拖拽功能
|
||||
* @param info drop事件默认参数
|
||||
* @param treeData 当前treeData值
|
||||
* @returns 拖拽后treeData值
|
||||
*/
|
||||
export const drop = (info: AntTreeNodeDropEvent, treeData: any) => {
|
||||
const dropKey = info.node.key;
|
||||
const dragKey = info.dragNode.key;
|
||||
const dropPos = info.node.pos.split('-');
|
||||
const dropPosition =
|
||||
info.dropPosition - Number(dropPos[dropPos.length - 1]);
|
||||
const loop = (
|
||||
data: TreeProps['treeData'],
|
||||
key: string | number,
|
||||
callback: any,
|
||||
) => {
|
||||
data.forEach((item, index) => {
|
||||
if (item.key === key) {
|
||||
return callback(item, index, data);
|
||||
}
|
||||
if (item.children) {
|
||||
return loop(item.children, key, callback);
|
||||
}
|
||||
});
|
||||
};
|
||||
const data = [...treeData];
|
||||
|
||||
let dragObj: TreeDataItem;
|
||||
loop(
|
||||
data,
|
||||
dragKey,
|
||||
(item: TreeDataItem, index: number, arr: TreeProps['treeData']) => {
|
||||
arr.splice(index, 1);
|
||||
dragObj = item;
|
||||
},
|
||||
);
|
||||
if (!info.dropToGap) {
|
||||
loop(data, dropKey, (item: TreeDataItem) => {
|
||||
item.children = item.children || [];
|
||||
item.children.unshift(dragObj);
|
||||
});
|
||||
} else if (
|
||||
(info.node.children || []).length > 0 && // Has children
|
||||
info.node.expanded && // Is expanded
|
||||
dropPosition === 1 // On the bottom gap
|
||||
) {
|
||||
loop(data, dropKey, (item: TreeDataItem) => {
|
||||
item.children = item.children || [];
|
||||
item.children.unshift(dragObj);
|
||||
});
|
||||
} else {
|
||||
let ar: TreeProps['treeData'] = [];
|
||||
let i = 0;
|
||||
loop(
|
||||
data,
|
||||
dropKey,
|
||||
(
|
||||
_item: TreeDataItem,
|
||||
index: number,
|
||||
arr: TreeProps['treeData'],
|
||||
) => {
|
||||
ar = arr;
|
||||
i = index;
|
||||
},
|
||||
);
|
||||
if (dropPosition === -1) {
|
||||
ar.splice(i, 0, dragObj);
|
||||
} else {
|
||||
ar.splice(i + 1, 0, dragObj);
|
||||
}
|
||||
}
|
||||
return data;
|
||||
};
|
|
@ -3700,8 +3700,8 @@ jetlinks-store@^0.0.3:
|
|||
|
||||
jetlinks-ui-components@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "http://47.108.170.157:9013/jetlinks-ui-components/-/jetlinks-ui-components-1.0.5.tgz#6dc396bc8a1b6f5a08accf5f46aec2099c15f481"
|
||||
integrity sha512-mnVN6MfHfyZf82miEoZV8+ud6RBH29x0A8PfpcraFQUl9Wat6XcjGppr8FOkmFbGw8laCGK5jlbiX3cYJUwaxw==
|
||||
resolved "http://47.108.170.157:9013/jetlinks-ui-components/-/jetlinks-ui-components-1.0.5.tgz#593185f6313895485b59e9a79bc920c63374d84d"
|
||||
integrity sha512-dkSOmatSPLHlV91YdTcHWO2wfwriUIZKEuLd5bJF2GsO9SvDMyJ2YJ4n/3fkklOoL5albhY37iX2Ot3A+7QYwA==
|
||||
dependencies:
|
||||
"@vueuse/core" "^9.12.0"
|
||||
ant-design-vue "^3.2.15"
|
||||
|
|
Loading…
Reference in New Issue