Merge branch 'dev' of github.com:jetlinks/jetlinks-ui-vue into dev
This commit is contained in:
commit
c932dc015f
|
@ -8,213 +8,214 @@ export interface IMatcher {
|
|||
const matchComponents: IMatcher[] = [
|
||||
{
|
||||
pattern: /^Avatar/,
|
||||
styleDir: 'Avatar',
|
||||
styleDir: 'Avatar'
|
||||
},
|
||||
{
|
||||
pattern: /^AutoComplete/,
|
||||
styleDir: 'AutoComplete',
|
||||
styleDir: 'AutoComplete'
|
||||
},
|
||||
{
|
||||
pattern: /^Anchor/,
|
||||
styleDir: 'Anchor',
|
||||
styleDir: 'Anchor'
|
||||
},
|
||||
|
||||
{
|
||||
pattern: /^Badge/,
|
||||
styleDir: 'Badge',
|
||||
styleDir: 'Badge'
|
||||
},
|
||||
{
|
||||
pattern: /^Breadcrumb/,
|
||||
styleDir: 'Breadcrumb',
|
||||
styleDir: 'Breadcrumb'
|
||||
},
|
||||
{
|
||||
pattern: /^Button/,
|
||||
styleDir: 'Button',
|
||||
styleDir: 'Button'
|
||||
},
|
||||
{
|
||||
pattern: /^Checkbox/,
|
||||
styleDir: 'Checkbox',
|
||||
styleDir: 'Checkbox'
|
||||
},
|
||||
{
|
||||
pattern: /^CardSelect/,
|
||||
styleDir: 'CardSelect'
|
||||
},
|
||||
{
|
||||
pattern: /^Card/,
|
||||
styleDir: 'Card',
|
||||
styleDir: 'Card'
|
||||
},
|
||||
{
|
||||
pattern: /^Collapse/,
|
||||
styleDir: 'Collapse',
|
||||
styleDir: 'Collapse'
|
||||
},
|
||||
{
|
||||
pattern: /^Descriptions/,
|
||||
styleDir: 'Descriptions',
|
||||
styleDir: 'Descriptions'
|
||||
},
|
||||
{
|
||||
pattern: /^RangePicker|^WeekPicker|^MonthPicker/,
|
||||
styleDir: 'DatePicker',
|
||||
styleDir: 'DatePicker'
|
||||
},
|
||||
{
|
||||
pattern: /^Dropdown/,
|
||||
styleDir: 'Dropdown',
|
||||
styleDir: 'Dropdown'
|
||||
},
|
||||
|
||||
{
|
||||
pattern: /^Form/,
|
||||
styleDir: 'Form',
|
||||
styleDir: 'Form'
|
||||
},
|
||||
{
|
||||
pattern: /^InputNumber/,
|
||||
styleDir: 'InputNumber',
|
||||
styleDir: 'InputNumber'
|
||||
},
|
||||
|
||||
{
|
||||
pattern: /^Input|^Textarea/,
|
||||
styleDir: 'Input',
|
||||
styleDir: 'Input'
|
||||
},
|
||||
{
|
||||
pattern: /^Statistic/,
|
||||
styleDir: 'Statistic',
|
||||
styleDir: 'Statistic'
|
||||
},
|
||||
{
|
||||
pattern: /^CheckableTag/,
|
||||
styleDir: 'Tag',
|
||||
styleDir: 'Tag'
|
||||
},
|
||||
{
|
||||
pattern: /^TimeRangePicker/,
|
||||
styleDir: 'TimePicker',
|
||||
styleDir: 'TimePicker'
|
||||
},
|
||||
{
|
||||
pattern: /^Layout/,
|
||||
styleDir: 'Layout',
|
||||
styleDir: 'Layout'
|
||||
},
|
||||
{
|
||||
pattern: /^Menu|^SubMenu/,
|
||||
styleDir: 'Menu',
|
||||
styleDir: 'Menu'
|
||||
},
|
||||
|
||||
{
|
||||
pattern: /^Table/,
|
||||
styleDir: 'Table',
|
||||
styleDir: 'Table'
|
||||
},
|
||||
{
|
||||
pattern: /^TimePicker|^TimeRangePicker/,
|
||||
styleDir: 'TimeTicker',
|
||||
styleDir: 'TimeTicker'
|
||||
},
|
||||
{
|
||||
pattern: /^Radio/,
|
||||
styleDir: 'Radio',
|
||||
styleDir: 'Radio'
|
||||
},
|
||||
|
||||
{
|
||||
pattern: /^Image/,
|
||||
styleDir: 'Image',
|
||||
styleDir: 'Image'
|
||||
},
|
||||
|
||||
{
|
||||
pattern: /^List/,
|
||||
styleDir: 'List',
|
||||
styleDir: 'List'
|
||||
},
|
||||
|
||||
{
|
||||
pattern: /^Tab/,
|
||||
styleDir: 'Tabs',
|
||||
styleDir: 'Tabs'
|
||||
},
|
||||
{
|
||||
pattern: /^Mentions/,
|
||||
styleDir: 'Mentions',
|
||||
styleDir: 'Mentions'
|
||||
},
|
||||
|
||||
{
|
||||
pattern: /^Step/,
|
||||
styleDir: 'Steps',
|
||||
styleDir: 'Steps'
|
||||
},
|
||||
{
|
||||
pattern: /^Skeleton/,
|
||||
styleDir: 'Skeleton',
|
||||
styleDir: 'Skeleton'
|
||||
},
|
||||
|
||||
{
|
||||
pattern: /^Select/,
|
||||
styleDir: 'Select',
|
||||
styleDir: 'Select'
|
||||
},
|
||||
{
|
||||
pattern: /^TreeSelect/,
|
||||
styleDir: 'TreeSelect',
|
||||
styleDir: 'TreeSelect'
|
||||
},
|
||||
{
|
||||
pattern: /^Tree|^DirectoryTree/,
|
||||
styleDir: 'Tree',
|
||||
styleDir: 'Tree'
|
||||
},
|
||||
{
|
||||
pattern: /^Typography/,
|
||||
styleDir: 'Typography',
|
||||
styleDir: 'Typography'
|
||||
},
|
||||
{
|
||||
pattern: /^Timeline/,
|
||||
styleDir: 'Timeline',
|
||||
styleDir: 'Timeline'
|
||||
},
|
||||
{
|
||||
pattern: /^Upload/,
|
||||
styleDir: 'Upload',
|
||||
styleDir: 'Upload'
|
||||
},
|
||||
{
|
||||
pattern: /^ProTable/,
|
||||
styleDir: 'ProTable',
|
||||
styleDir: 'ProTable'
|
||||
},
|
||||
{
|
||||
pattern: /^Search|^AdvancedSearch/,
|
||||
styleDir: 'Search',
|
||||
styleDir: 'Search'
|
||||
},
|
||||
{
|
||||
pattern: /^Ellipsis/,
|
||||
styleDir: 'Ellipsis',
|
||||
styleDir: 'Ellipsis'
|
||||
},
|
||||
{
|
||||
pattern: /^MonacoEditor/,
|
||||
styleDir: 'MonacoEditor',
|
||||
styleDir: 'MonacoEditor'
|
||||
},
|
||||
{
|
||||
pattern: /^ProLayout/,
|
||||
styleDir: 'ProLayout',
|
||||
styleDir: 'ProLayout'
|
||||
},
|
||||
{
|
||||
pattern: /^ScrollTable/,
|
||||
styleDir: 'ScrollTable',
|
||||
styleDir: 'ScrollTable'
|
||||
},
|
||||
{
|
||||
pattern: /^TableCard/,
|
||||
styleDir: 'TableCard',
|
||||
styleDir: 'TableCard'
|
||||
},
|
||||
{
|
||||
pattern: /^Scrollbar/,
|
||||
styleDir: 'Scrollbar',
|
||||
styleDir: 'Scrollbar'
|
||||
},
|
||||
{
|
||||
pattern: /^AIcon/,
|
||||
styleDir: 'AIcon',
|
||||
},
|
||||
{
|
||||
pattern: /^CardSelect/,
|
||||
styleDir: 'CardSelect',
|
||||
styleDir: 'AIcon'
|
||||
},
|
||||
|
||||
{
|
||||
pattern: /^Tooltip/,
|
||||
styleDir: 'Tooltip',
|
||||
styleDir: 'Tooltip'
|
||||
},
|
||||
{
|
||||
pattern: /^Empty/,
|
||||
styleDir: 'Empty',
|
||||
styleDir: 'Empty'
|
||||
},
|
||||
{
|
||||
pattern: /^Popconfirm/,
|
||||
styleDir: 'Popconfirm',
|
||||
styleDir: 'Popconfirm'
|
||||
},
|
||||
{
|
||||
pattern: /^message/,
|
||||
styleDir: 'Message',
|
||||
styleDir: 'Message'
|
||||
},
|
||||
{
|
||||
pattern: /^Notification/,
|
||||
styleDir: 'Notification',
|
||||
},
|
||||
styleDir: 'Notification'
|
||||
}
|
||||
]
|
||||
|
||||
export interface JetlinksVueResolverOptions {
|
||||
|
@ -259,7 +260,6 @@ function getStyleDir(compName: string, _isAntd = false): string {
|
|||
let styleDir
|
||||
const components = _isAntd ? AntdMatchComponents : matchComponents
|
||||
const total = components.length
|
||||
console.log('getStyleDir', compName)
|
||||
for (let i = 0; i < total; i++) {
|
||||
const matcher = components[i]
|
||||
if (compName.match(matcher.pattern)) {
|
||||
|
@ -273,10 +273,10 @@ function getStyleDir(compName: string, _isAntd = false): string {
|
|||
return styleDir
|
||||
}
|
||||
|
||||
function getSideEffects(compName: string, options: JetlinksVueResolverOptions, _isAntd= false): any {
|
||||
function getSideEffects(compName: string, options: JetlinksVueResolverOptions, _isAntd = false): any {
|
||||
const {
|
||||
importStyle = true,
|
||||
importLess = false,
|
||||
importLess = false
|
||||
} = options
|
||||
|
||||
if (!importStyle)
|
||||
|
@ -286,23 +286,21 @@ function getSideEffects(compName: string, options: JetlinksVueResolverOptions, _
|
|||
|
||||
if (importStyle === 'less' || importLess) {
|
||||
const styleDir = getStyleDir(compName, _isAntd)
|
||||
console.log('getSideEffects-style-path', `${packageName}/${lib}/${styleDir}/style`)
|
||||
return `${packageName}/${lib}/${styleDir}/style`
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
const styleDir = getStyleDir(compName, _isAntd)
|
||||
return `${packageName}/${lib}/${styleDir}/style/css`
|
||||
}
|
||||
}
|
||||
|
||||
const filterName = [ 'message', 'Notification', 'AIcon']
|
||||
const filterName = ['message', 'Notification', 'AIcon']
|
||||
const primitiveNames = ['Affix', 'Anchor', 'AnchorLink', 'message', 'Notification', 'AutoComplete', 'AutoCompleteOptGroup', 'AutoCompleteOption', 'Alert', 'Avatar', 'AvatarGroup', 'BackTop', 'Badge', 'BadgeRibbon', 'Breadcrumb', 'BreadcrumbItem', 'BreadcrumbSeparator', 'Button', 'ButtonGroup', 'Calendar', 'Card', 'CardGrid', 'CardMeta', 'Collapse', 'CollapsePanel', 'Carousel', 'Cascader', 'Checkbox', 'CheckboxGroup', 'Col', 'Comment', 'ConfigProvider', 'DatePicker', 'MonthPicker', 'WeekPicker', 'RangePicker', 'QuarterPicker', 'Descriptions', 'DescriptionsItem', 'Divider', 'Dropdown', 'DropdownButton', 'Drawer', 'Empty', 'Form', 'FormItem', 'FormItemRest', 'Grid', 'Input', 'InputGroup', 'InputPassword', 'InputSearch', 'Textarea', 'Image', 'ImagePreviewGroup', 'InputNumber', 'Layout', 'LayoutHeader', 'LayoutSider', 'LayoutFooter', 'LayoutContent', 'List', 'ListItem', 'ListItemMeta', 'Menu', 'MenuDivider', 'MenuItem', 'MenuItemGroup', 'SubMenu', 'Mentions', 'MentionsOption', 'Modal', 'Statistic', 'StatisticCountdown', 'PageHeader', 'Pagination', 'Popconfirm', 'Popover', 'Progress', 'Radio', 'RadioButton', 'RadioGroup', 'Rate', 'Result', 'Row', 'Select', 'SelectOptGroup', 'SelectOption', 'Skeleton', 'SkeletonButton', 'SkeletonAvatar', 'SkeletonInput', 'SkeletonImage', 'Slider', 'Space', 'Spin', 'Steps', 'Step', 'Switch', 'Table', 'TableColumn', 'TableColumnGroup', 'TableSummary', 'TableSummaryRow', 'TableSummaryCell', 'Transfer', 'Tree', 'TreeNode', 'DirectoryTree', 'TreeSelect', 'TreeSelectNode', 'Tabs', 'TabPane', 'Tag', 'CheckableTag', 'TimePicker', 'TimeRangePicker', 'Timeline', 'TimelineItem', 'Tooltip', 'Typography', 'TypographyLink', 'TypographyParagraph', 'TypographyText', 'TypographyTitle', 'Upload', 'UploadDragger', 'LocaleProvider', 'ProTable', 'Search', 'AdvancedSearch', 'Ellipsis', 'MonacoEditor', 'ProLayout', 'ScrollTable', 'TableCard', 'Scrollbar', 'CardSelect', 'ColorPicker']
|
||||
const prefix = 'J'
|
||||
|
||||
let jetlinksNames: Set<string>
|
||||
|
||||
function genJetlinksNames(primitiveNames: string[]): void {
|
||||
jetlinksNames = new Set(primitiveNames.map(name => filterName.includes(name) ? name : `${prefix}${name}`))
|
||||
jetlinksNames = new Set(primitiveNames.map(name => filterName.includes(name) ? name : `${prefix}${name}`))
|
||||
}
|
||||
|
||||
let antdvNames: Set<string>
|
||||
|
@ -322,16 +320,14 @@ function isAntdv(compName: string): boolean {
|
|||
return antdvNames.has(compName)
|
||||
}
|
||||
|
||||
export function JetlinksVueResolver(options: JetlinksVueResolverOptions = {
|
||||
|
||||
}): any {
|
||||
export function JetlinksVueResolver(options: JetlinksVueResolverOptions = {}): any {
|
||||
return {
|
||||
type: 'component',
|
||||
resolve: (name: string) => {
|
||||
if (options.resolveIcons && name.match(/(Outlined|Filled|TwoTone)$/)) {
|
||||
return {
|
||||
name,
|
||||
from: '@ant-design/icons-vue',
|
||||
from: '@ant-design/icons-vue'
|
||||
}
|
||||
}
|
||||
const _isJetlinks = isJetlinks(name)
|
||||
|
@ -340,13 +336,16 @@ export function JetlinksVueResolver(options: JetlinksVueResolverOptions = {
|
|||
const importName = filterName.includes(name) ? name : name.slice(1)
|
||||
options.packageName = _isJetlinks ? 'jetlinks-ui-components' : 'ant-design-vue'
|
||||
const path = `${options.packageName}/${options.cjs ? 'lib' : 'es'}`
|
||||
|
||||
const stylePath = getSideEffects(importName, options, _isAntd)
|
||||
if (_isJetlinks) {
|
||||
console.log(name, importName, stylePath)
|
||||
}
|
||||
return {
|
||||
name: importName,
|
||||
from: path,
|
||||
sideEffects: getSideEffects(importName, options, _isAntd),
|
||||
sideEffects: stylePath
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,4 +21,6 @@ export const _action = (id: string, type: '_disable' | '_enable') => server.put(
|
|||
export const _execute = (id: string) => server.post(`/scene/${id}/_execute`);
|
||||
|
||||
// 内置参数
|
||||
export const queryBuiltInParams = (data: any, params?: any) => server.post(`/scene/parse-variables`, data, params);
|
||||
export const queryBuiltInParams = (data: any, params?: any) => server.post(`/scene/parse-variables`, data, params);
|
||||
|
||||
export const getParseTerm = (data: Record<string, any>) => server.post(`/scene/parse-term-column`, data)
|
|
@ -13,12 +13,12 @@
|
|||
}}</a>
|
||||
<span v-else>{{ slotProps.route.breadcrumbName }}</span>
|
||||
</template>
|
||||
<template #rightContentRender>
|
||||
<div style="margin-right: 24px;display: flex;align-items: center;">
|
||||
<AIcon type="QuestionCircleOutlined" @click="toDoc" />
|
||||
<Notice style="margin: 0 24px;" />
|
||||
<UserInfo />
|
||||
</div>
|
||||
<template #rightContentRender>
|
||||
<div class="right-content">
|
||||
<AIcon type="QuestionCircleOutlined" @click="toDoc" />
|
||||
<Notice style="margin: 0 24px" />
|
||||
<UserInfo />
|
||||
</div>
|
||||
</template>
|
||||
<router-view v-slot="{ Component }">
|
||||
<component :is="Component" />
|
||||
|
@ -28,7 +28,7 @@
|
|||
|
||||
<script setup lang="ts" name="BasicLayoutPage">
|
||||
import UserInfo from './components/UserInfo.vue';
|
||||
import Notice from './components/Notice.vue'
|
||||
import Notice from './components/Notice.vue';
|
||||
import DefaultSetting from '../../../config/config';
|
||||
import { useMenuStore } from '@/store/menu';
|
||||
import { clearMenuItem } from 'jetlinks-ui-components/es/ProLayout/util';
|
||||
|
@ -95,8 +95,13 @@ watchEffect(() => {
|
|||
}
|
||||
});
|
||||
|
||||
|
||||
const toDoc = ()=>window.open('http://doc.v2.jetlinks.cn/')
|
||||
const toDoc = () => window.open('http://doc.v2.jetlinks.cn/');
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
<style scoped>
|
||||
.right-content {
|
||||
margin-right: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -10,7 +10,7 @@ import NormalUpload from './NormalUpload/index.vue'
|
|||
import FileFormat from './FileFormat/index.vue'
|
||||
import JProUpload from './JUpload/index.vue'
|
||||
import { BasicLayoutPage, BlankLayoutPage } from './Layout'
|
||||
import { PageContainer } from 'jetlinks-ui-components/es/components'
|
||||
import { PageContainer } from 'jetlinks-ui-components'
|
||||
import Ellipsis from './Ellipsis/index.vue'
|
||||
import JEmpty from './Empty/index.vue'
|
||||
import AMapComponent from './AMapComponent/index.vue'
|
||||
|
|
|
@ -76,7 +76,7 @@ export const useMenuStore = defineStore({
|
|||
const path = this.hasMenu(name)
|
||||
if (path) {
|
||||
router.push({
|
||||
name, params, query
|
||||
name, params, query, state: { params }
|
||||
})
|
||||
} else {
|
||||
onlyMessage('暂无权限,请联系管理员', 'error')
|
||||
|
|
|
@ -92,7 +92,8 @@ export type MenuItem = {
|
|||
icon?: string
|
||||
[key: string]: any
|
||||
},
|
||||
component?: any
|
||||
component?: any,
|
||||
props?: boolean
|
||||
};
|
||||
|
||||
// 额外子级路由
|
||||
|
@ -213,7 +214,7 @@ export function filterAsnycRouter(asyncRouterMap: any, parentCode = '', level =
|
|||
title: route.name,
|
||||
hideInMenu: route.isShow === false,
|
||||
buttons: route.buttons?.map((b: any) => b.id) || []
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
const silder = {..._route}
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
<template>
|
||||
<a-card class="boot-card-container" :bordered="false">
|
||||
<template #title>
|
||||
<h5 class="title">{{ cardTitle }}</h5>
|
||||
</template>
|
||||
<div class="boot-card-container" :bordered="false">
|
||||
<h5 class="title">{{ cardTitle }}</h5>
|
||||
|
||||
<div class="box">
|
||||
<div
|
||||
class="box-item"
|
||||
v-for="(item, index) in cardData"
|
||||
@click="jumpPage(item)"
|
||||
@click="jumpPage(item.link,item.params)"
|
||||
>
|
||||
<div class="item-english">{{ item.english }}</div>
|
||||
<div class="item-title">{{ item.label }}</div>
|
||||
|
@ -18,46 +17,27 @@
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
</a-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { message } from 'ant-design-vue';
|
||||
import { bootConfig } from "../index";
|
||||
import { bootConfig } from "../typing";
|
||||
import { useMenuStore } from '@/store/menu';
|
||||
|
||||
const { jumpPage } = useMenuStore();
|
||||
|
||||
const router = useRouter();
|
||||
const props = defineProps({
|
||||
cardData: Array<bootConfig>,
|
||||
cardTitle: String,
|
||||
});
|
||||
const { cardData, cardTitle } = toRefs(props);
|
||||
|
||||
const jumpPage = (row: bootConfig): void => {
|
||||
if (row.auth && row.link) {
|
||||
router.push(`${row.link}${objToParams(row.params || {})}`);
|
||||
} else {
|
||||
message.warning('暂无权限,请联系管理员');
|
||||
}
|
||||
};
|
||||
|
||||
const objToParams = (source: object): string => {
|
||||
if (Object.prototype.toString.call(source) === '[object Object]') {
|
||||
const paramsArr = <any>[];
|
||||
Object.entries(source).forEach(([prop, value]) => {
|
||||
if (typeof value === 'object') value = JSON.stringify(value);
|
||||
paramsArr.push(`${prop}=${value}`);
|
||||
});
|
||||
if (paramsArr.length > 0) return '?' + paramsArr.join('&');
|
||||
}
|
||||
return '';
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.boot-card-container {
|
||||
:deep(.ant-card-body) {
|
||||
padding-top: 0;
|
||||
}
|
||||
background-color: #fff;
|
||||
padding: 24px 14px;
|
||||
.title {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import { message } from 'ant-design-vue';
|
||||
import { bootConfig } from "../index";
|
||||
import { bootConfig } from "../typing";
|
||||
|
||||
const router = useRouter();
|
||||
const props = defineProps({
|
||||
|
|
|
@ -43,7 +43,7 @@ import StepCard from '../StepCard.vue';
|
|||
|
||||
import { useMenuStore } from '@/store/menu';
|
||||
import { usePermissionStore } from '@/store/permission';
|
||||
import { recommendList, bootConfig } from '../../index';
|
||||
import { recommendList, bootConfig } from '../../typing';
|
||||
|
||||
// 按钮权限控制
|
||||
const hasPermission = usePermissionStore().hasPermission;
|
||||
|
|
|
@ -28,7 +28,7 @@ import PlatformPicCard from '../PlatformPicCard.vue';
|
|||
import StepCard from '../StepCard.vue';
|
||||
import { useMenuStore } from "@/store/menu";
|
||||
|
||||
import { bootConfig, recommendList } from '../../index';
|
||||
import { bootConfig, recommendList } from '../../typing';
|
||||
// 页面权限控制
|
||||
const menuPermission = useMenuStore().hasPermission;
|
||||
|
||||
|
|
|
@ -1,13 +1,7 @@
|
|||
<template>
|
||||
<a-card class="device-count-container">
|
||||
<template #title>
|
||||
<h5 class="title">设备统计</h5>
|
||||
</template>
|
||||
<template #extra>
|
||||
<span style="color: #1d39c4; cursor: pointer" @click="jumpPage"
|
||||
>详情</span
|
||||
>
|
||||
</template>
|
||||
<div class="device-count-container">
|
||||
<h5 class="title">设备统计</h5>
|
||||
<span class="detail" @click="jumpPage('device/DashBoard')"> 详情 </span>
|
||||
|
||||
<div class="box-list">
|
||||
<div class="box-item">
|
||||
|
@ -21,34 +15,33 @@
|
|||
<img src="/images/home/top-2.png" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
</a-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getDeviceCount_api, getProductCount_api } from '@/api/home';
|
||||
import { useMenuStore } from '@/store/menu';
|
||||
|
||||
const { jumpPage } = useMenuStore();
|
||||
const projectNum = ref(0);
|
||||
const deviceNum = ref(0);
|
||||
|
||||
onMounted(() => {
|
||||
getData();
|
||||
});
|
||||
|
||||
const getData = () => {
|
||||
getDeviceCount_api().then((resp) => {
|
||||
getDeviceCount_api().then((resp: any) => {
|
||||
deviceNum.value = resp.result;
|
||||
});
|
||||
getProductCount_api().then((resp) => {
|
||||
getProductCount_api({}).then((resp: any) => {
|
||||
projectNum.value = resp.result;
|
||||
});
|
||||
};
|
||||
const jumpPage = () => {};
|
||||
getData();
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.device-count-container {
|
||||
:deep(.ant-card-body) {
|
||||
padding-top: 0;
|
||||
}
|
||||
background-color: #fff;
|
||||
padding: 24px 14px;
|
||||
position: relative;
|
||||
.title {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
|
@ -71,6 +64,14 @@ const jumpPage = () => {};
|
|||
content: ' ';
|
||||
}
|
||||
}
|
||||
.detail {
|
||||
color: #1d39c4;
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
top: 24px;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.box-list {
|
||||
display: grid;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="device-home-container">
|
||||
<a-row :gutter="10">
|
||||
<a-row :gutter="24">
|
||||
<a-col :span="14">
|
||||
<BootCard :cardData="deviceBootConfig" cardTitle="物联网引导" />
|
||||
</a-col>
|
||||
|
@ -18,7 +18,7 @@
|
|||
:dataList="deviceStepDetails"
|
||||
/>
|
||||
</a-row>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="deviceHome">
|
||||
|
@ -28,7 +28,7 @@ import PlatformPicCard from '../PlatformPicCard.vue';
|
|||
import StepCard from '../StepCard.vue';
|
||||
|
||||
import { usePermissionStore } from '@/store/permission';
|
||||
import { bootConfig, recommendList } from '../../index';
|
||||
import { bootConfig, recommendList } from '../../typing';
|
||||
|
||||
// 按钮权限控制
|
||||
const hasPermission = usePermissionStore().hasPermission;
|
||||
|
@ -43,28 +43,28 @@ const deviceBootConfig: bootConfig[] = [
|
|||
{
|
||||
english: 'STEP1',
|
||||
label: '创建产品',
|
||||
link: '/iot/device/Product',
|
||||
link: 'device/Product',
|
||||
auth: productPermission('add'),
|
||||
params: {
|
||||
save: true,
|
||||
type: 'add',
|
||||
},
|
||||
},
|
||||
{
|
||||
english: 'STEP2',
|
||||
label: '创建设备',
|
||||
link: '/iot/device/Instance',
|
||||
link: 'device/Instance',
|
||||
auth: devicePermission('add'),
|
||||
params: {
|
||||
save: true,
|
||||
type: 'add',
|
||||
},
|
||||
},
|
||||
{
|
||||
english: 'STEP3',
|
||||
label: '规则引擎',
|
||||
link: '/iot/rule-engine/Instance',
|
||||
link: 'rule-engine/Instance',
|
||||
auth: rulePermission('add'),
|
||||
params: {
|
||||
save: true,
|
||||
type: 'add',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
@ -74,10 +74,10 @@ const deviceStepDetails: recommendList[] = [
|
|||
details:
|
||||
'产品是设备的集合,通常指一组具有相同功能的设备。物联设备必须通过产品进行接入方式配置。',
|
||||
iconUrl: '/images/home/bottom-4.png',
|
||||
linkUrl: '/iot/device/Product',
|
||||
linkUrl: 'device/Product',
|
||||
auth: productPermission('add'),
|
||||
params: {
|
||||
save: true,
|
||||
type: 'add',
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -85,7 +85,7 @@ const deviceStepDetails: recommendList[] = [
|
|||
details:
|
||||
'通过产品对同一类型的设备进行统一的接入方式配置。请参照设备铭牌说明选择匹配的接入方式。',
|
||||
iconUrl: '/images/home/bottom-1.png',
|
||||
linkUrl: '/iot/device/Product/detail',
|
||||
linkUrl: 'device/Product/Detail',
|
||||
auth: productPermission('update'),
|
||||
dialogTag: 'accessMethod',
|
||||
},
|
||||
|
@ -93,10 +93,10 @@ const deviceStepDetails: recommendList[] = [
|
|||
title: '添加测试设备',
|
||||
details: '添加单个设备,用于验证产品模型是否配置正确。',
|
||||
iconUrl: '/images/home/bottom-5.png',
|
||||
linkUrl: '/iot/device/Instance',
|
||||
linkUrl: 'device/Instance',
|
||||
auth: devicePermission('add'),
|
||||
params: {
|
||||
save: true,
|
||||
type: 'add',
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -104,8 +104,7 @@ const deviceStepDetails: recommendList[] = [
|
|||
details:
|
||||
'对添加的测试设备进行功能调试,验证能否连接到平台,设备功能是否配置正确。',
|
||||
iconUrl: '/images/home/bottom-2.png',
|
||||
linkUrl: '/iot/device/Instance/detail',
|
||||
// auth: devicePermission('update'),
|
||||
linkUrl: 'device/Instance/Detail',
|
||||
auth: true,
|
||||
dialogTag: 'funcTest',
|
||||
},
|
||||
|
@ -113,7 +112,7 @@ const deviceStepDetails: recommendList[] = [
|
|||
title: '批量添加设备',
|
||||
details: '批量添加同一产品下的设备',
|
||||
iconUrl: '/images/home/bottom-3.png',
|
||||
linkUrl: '/iot/device/Instance',
|
||||
linkUrl: 'device/Instance',
|
||||
auth: devicePermission('import'),
|
||||
params: {
|
||||
import: true,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<a-card class="platform-pic-container">
|
||||
<div class="platform-pic-container">
|
||||
<div class="title">
|
||||
<span>平台架构图</span>
|
||||
<p>PLATFORM ARCHITECTURE DIAGRAM</p>
|
||||
|
@ -9,7 +9,7 @@
|
|||
class="bj"
|
||||
alt=""
|
||||
/>
|
||||
</a-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
@ -19,13 +19,11 @@ const props = defineProps({
|
|||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
:deep(.ant-card-body) {
|
||||
padding: 0;
|
||||
}
|
||||
.platform-pic-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
background-color: #fff;
|
||||
border-bottom: 1px solid #2f54eb;
|
||||
|
||||
.bj {
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
<template>
|
||||
<a-card class="step-container">
|
||||
<dic class="step-container">
|
||||
<h5 class="title">
|
||||
<span style="margin-right: 12px">{{ cardTitle }}</span>
|
||||
<a-tooltip placement="top">
|
||||
<span style="margin-right: 12px">{{ props.cardTitle }}</span>
|
||||
<j-tooltip placement="top">
|
||||
<template #title>
|
||||
<span>{{ tooltip }}</span>
|
||||
<span>{{ props.tooltip }}</span>
|
||||
</template>
|
||||
<question-circle-outlined style="padding-top: 5px" />
|
||||
</a-tooltip>
|
||||
<AIcon type="QuestionCircleOutlined" style="padding-top: 5px" />
|
||||
</j-tooltip>
|
||||
</h5>
|
||||
|
||||
<div class="box-list">
|
||||
<div class="list-item" v-for="item in dataList">
|
||||
<div class="list-item" v-for="item in props.dataList">
|
||||
<div class="box-top" @click="jumpPage(item)">
|
||||
<span class="top-title">{{ item.title }}</span>
|
||||
<img :src="item.iconUrl" alt="" />
|
||||
|
@ -22,42 +22,35 @@
|
|||
|
||||
<div class="dialogs">
|
||||
<ProductChooseDialog
|
||||
:open-number="openAccess"
|
||||
v-if="productDialogVisible"
|
||||
v-model:visible="productDialogVisible"
|
||||
@confirm="againJumpPage"
|
||||
/>
|
||||
<DeviceChooseDialog
|
||||
:open-number="openFunc"
|
||||
v-if="deviceDialogVisible"
|
||||
v-model:visible="deviceDialogVisible"
|
||||
@confirm="againJumpPage"
|
||||
/>
|
||||
</div>
|
||||
</a-card>
|
||||
</dic>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { PropType } from 'vue';
|
||||
import { QuestionCircleOutlined } from '@ant-design/icons-vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import ProductChooseDialog from './dialogs/ProductChooseDialog.vue';
|
||||
import DeviceChooseDialog from './dialogs/DeviceChooseDialog.vue';
|
||||
import { recommendList } from '../typing';
|
||||
import { useMenuStore } from '@/store/menu';
|
||||
|
||||
import { recommendList } from '../index';
|
||||
|
||||
type rowType = {
|
||||
params: object;
|
||||
linkUrl: string;
|
||||
};
|
||||
const { jumpPage: _jumpPage } = useMenuStore();
|
||||
|
||||
const props = defineProps({
|
||||
cardTitle: String,
|
||||
tooltip: String,
|
||||
dataList: Array as PropType<recommendList[]>,
|
||||
});
|
||||
const router = useRouter();
|
||||
|
||||
const { cardTitle, tooltip, dataList } = toRefs(props);
|
||||
const openAccess = ref<number>(0);
|
||||
const openFunc = ref<number>(0);
|
||||
let selectRow: recommendList | rowType = {
|
||||
params: {},
|
||||
linkUrl: '',
|
||||
|
@ -66,34 +59,33 @@ let selectRow: recommendList | rowType = {
|
|||
const jumpPage = (row: recommendList) => {
|
||||
if (!row.auth) return message.warning('暂无权限,请联系管理员');
|
||||
selectRow = row; // 二次跳转需要使用
|
||||
if (row.dialogTag == 'accessMethod') return (openAccess.value += 1);
|
||||
else if (row.dialogTag === 'funcTest') return (openFunc.value += 1);
|
||||
if (row.dialogTag == 'accessMethod')
|
||||
return (productDialogVisible.value = true);
|
||||
else if (row.dialogTag === 'funcTest')
|
||||
return (deviceDialogVisible.value = true);
|
||||
else if (row.linkUrl) {
|
||||
router.push(`${row.linkUrl}${objToParams(row.params || {})}`);
|
||||
_jumpPage(row.linkUrl, row.params);
|
||||
}
|
||||
};
|
||||
// 弹窗返回后的二次跳转
|
||||
const againJumpPage = (params: string) => {
|
||||
router.push(`${selectRow.linkUrl}/${params}`);
|
||||
const againJumpPage = (id: string) => {
|
||||
_jumpPage(selectRow.linkUrl, { id });
|
||||
};
|
||||
|
||||
const objToParams = (source: object): string => {
|
||||
if (Object.prototype.toString.call(source) === '[object Object]') {
|
||||
const paramsArr = <any>[];
|
||||
// 直接使用for in遍历对象ts会报错
|
||||
Object.entries(source).forEach(([prop, value]) => {
|
||||
if (typeof value === 'object') value = JSON.stringify(value);
|
||||
paramsArr.push(`${prop}=${value}`);
|
||||
});
|
||||
if (paramsArr.length > 0) return '?' + paramsArr.join('&');
|
||||
}
|
||||
return '';
|
||||
const productDialogVisible = ref(false);
|
||||
const deviceDialogVisible = ref(false);
|
||||
|
||||
type rowType = {
|
||||
params: object;
|
||||
linkUrl: string;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.step-container {
|
||||
width: 100%;
|
||||
padding: 24px 14px;
|
||||
background-color: #fff;
|
||||
.title {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
|
|
|
@ -1,23 +1,26 @@
|
|||
<template>
|
||||
<div ref="modal" class="func-test-dialog-container"></div>
|
||||
<a-modal
|
||||
v-model:visible="visible"
|
||||
title="选择产品"
|
||||
<j-modal
|
||||
visible
|
||||
title="选择设备"
|
||||
style="width: 1000px"
|
||||
@ok="handleOk"
|
||||
:getContainer="getContainer"
|
||||
@ok="confirm"
|
||||
@cancel="emits('update:visible', false)"
|
||||
:maskClosable="false"
|
||||
>
|
||||
<Search type="simple" :columns="query.columns" @search="query.search" />
|
||||
<JTable
|
||||
<j-advanced-search
|
||||
type="simple"
|
||||
:columns="columns"
|
||||
@search="(params:any)=>queryParams = {...params}"
|
||||
/>
|
||||
<j-pro-table
|
||||
model="TABLE"
|
||||
:request="getDeviceList_api"
|
||||
:columns="table.columns"
|
||||
:params="query.params"
|
||||
:columns="columns"
|
||||
:params="queryParams"
|
||||
:defaultParams="{ sorts: [{ name: 'createTime', order: 'desc' }] }"
|
||||
:rowSelection="{
|
||||
selectedRowKeys: table.selectedKeys,
|
||||
onChange: table.onSelect,
|
||||
selectedRowKeys: selectedKeys,
|
||||
onChange: (keys:string[])=>selectedKeys = keys,
|
||||
type: 'radio',
|
||||
}"
|
||||
>
|
||||
|
@ -32,152 +35,90 @@
|
|||
:status-label="slotProps.state.text"
|
||||
/>
|
||||
</template>
|
||||
</JTable>
|
||||
|
||||
<template #footer>
|
||||
<a-button key="back" @click="visible = false">取消</a-button>
|
||||
<a-button key="submit" type="primary" @click="handleOk"
|
||||
>确认</a-button
|
||||
>
|
||||
</template>
|
||||
</a-modal>
|
||||
</j-pro-table>
|
||||
</j-modal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import StatusLabel from '../StatusLabel.vue';
|
||||
|
||||
import { ComponentInternalInstance } from 'vue';
|
||||
import { getDeviceList_api } from '@/api/home';
|
||||
import { message } from 'ant-design-vue';
|
||||
import moment from 'moment';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const emits = defineEmits(['confirm']);
|
||||
const props = defineProps({
|
||||
openNumber: Number,
|
||||
});
|
||||
const emits = defineEmits(['confirm', 'update:visible']);
|
||||
const props = defineProps<{
|
||||
visible: boolean;
|
||||
}>();
|
||||
|
||||
// 弹窗控制
|
||||
const visible = ref<boolean>(false);
|
||||
const getContainer = () => proxy?.$refs.modal as HTMLElement;
|
||||
const handleOk = () => {
|
||||
if (table.selectedKeys.length < 1) return message.warn('请选择设备');
|
||||
emits('confirm', table.selectedKeys[0]);
|
||||
visible.value = false;
|
||||
const confirm = () => {
|
||||
if (selectedKeys.value.length < 1) return message.warn('请选择设备');
|
||||
emits('confirm', selectedKeys.value[0]);
|
||||
emits('update:visible', false);
|
||||
};
|
||||
watch(
|
||||
() => props.openNumber,
|
||||
() => {
|
||||
visible.value = true;
|
||||
},
|
||||
);
|
||||
|
||||
const query = reactive({
|
||||
params: {},
|
||||
columns: [
|
||||
{
|
||||
title: '设备ID',
|
||||
dataIndex: 'id',
|
||||
key: 'id',
|
||||
ellipsis: true,
|
||||
search: {
|
||||
type: 'string',
|
||||
},
|
||||
const columns = [
|
||||
{
|
||||
title: '设备ID',
|
||||
dataIndex: 'id',
|
||||
key: 'id',
|
||||
ellipsis: true,
|
||||
search: {
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
title: '设备名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
ellipsis: true,
|
||||
search: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '产品名称',
|
||||
dataIndex: 'productName',
|
||||
key: 'productName',
|
||||
ellipsis: true,
|
||||
search: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '注册时间',
|
||||
dataIndex: 'modifyTime',
|
||||
key: 'modifyTime',
|
||||
ellipsis: true,
|
||||
search: {
|
||||
type: 'date',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'state',
|
||||
key: 'state',
|
||||
ellipsis: true,
|
||||
search: {
|
||||
rename: 'state',
|
||||
type: 'select',
|
||||
options: [
|
||||
{
|
||||
label: '在线',
|
||||
value: 'online',
|
||||
},
|
||||
{
|
||||
label: '离线',
|
||||
value: 'offline',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
search: (params: object) => {
|
||||
query.params = params;
|
||||
},
|
||||
});
|
||||
{
|
||||
title: '设备名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
ellipsis: true,
|
||||
search: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '产品名称',
|
||||
dataIndex: 'productName',
|
||||
key: 'productName',
|
||||
ellipsis: true,
|
||||
search: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '注册时间',
|
||||
dataIndex: 'modifyTime',
|
||||
key: 'modifyTime',
|
||||
ellipsis: true,
|
||||
search: {
|
||||
type: 'date',
|
||||
},
|
||||
scopedSlots: true,
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'state',
|
||||
key: 'state',
|
||||
ellipsis: true,
|
||||
search: {
|
||||
rename: 'state',
|
||||
type: 'select',
|
||||
options: [
|
||||
{
|
||||
label: '在线',
|
||||
value: 'online',
|
||||
},
|
||||
{
|
||||
label: '离线',
|
||||
value: 'offline',
|
||||
},
|
||||
],
|
||||
},
|
||||
scopedSlots: true,
|
||||
},
|
||||
];
|
||||
|
||||
const table = reactive({
|
||||
columns: [
|
||||
{
|
||||
title: '设备Id',
|
||||
dataIndex: 'id',
|
||||
key: 'id',
|
||||
},
|
||||
{
|
||||
title: '设备名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
},
|
||||
{
|
||||
title: '产品名称',
|
||||
dataIndex: 'productName',
|
||||
key: 'productName',
|
||||
},
|
||||
{
|
||||
title: '注册时间',
|
||||
dataIndex: 'modifyTime',
|
||||
key: 'modifyTime',
|
||||
scopedSlots: true,
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'state',
|
||||
key: 'state',
|
||||
scopedSlots: true,
|
||||
},
|
||||
],
|
||||
selectedKeys: [] as string[],
|
||||
onSelect: (keys: string[]) => {
|
||||
table.selectedKeys = [...keys];
|
||||
},
|
||||
});
|
||||
const queryParams = ref({});
|
||||
|
||||
const selectedKeys = ref<string[]>([]);
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.func-test-dialog-container {
|
||||
.search {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,59 +1,52 @@
|
|||
<template>
|
||||
<div ref="modal" class="access-method-dialog-container"></div>
|
||||
<a-modal
|
||||
v-model:visible="visible"
|
||||
<j-modal
|
||||
visible
|
||||
title="选择产品"
|
||||
style="width: 700px"
|
||||
@ok="handleOk"
|
||||
show-search
|
||||
width="700px"
|
||||
@ok="confirm"
|
||||
@cancel="emits('update:visible', false)"
|
||||
:filter-option="filterOption"
|
||||
:getContainer="getContainer"
|
||||
:maskClosable="false"
|
||||
class="access-method-dialog-container"
|
||||
>
|
||||
<a-form :model="form" name="basic" autocomplete="off" layout="vertical">
|
||||
<a-form-item
|
||||
<j-form :model="form" name="basic" autocomplete="off" layout="vertical">
|
||||
<j-form-item
|
||||
label="产品"
|
||||
name="productId"
|
||||
:rules="[{ required: true, message: '该字段是必填字段' }]"
|
||||
>
|
||||
<a-select
|
||||
<j-select
|
||||
v-model:value="form.productId"
|
||||
style="width: 100%"
|
||||
:options="productList"
|
||||
>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<template #footer>
|
||||
<a-button key="back" @click="visible = false">取消</a-button>
|
||||
<a-button key="submit" type="primary" @click="handleOk"
|
||||
>确认</a-button
|
||||
>
|
||||
</template>
|
||||
</a-modal>
|
||||
</j-select>
|
||||
</j-form-item>
|
||||
</j-form>
|
||||
</j-modal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ComponentInternalInstance } from 'vue';
|
||||
|
||||
import { getProductList_api } from '@/api/home';
|
||||
import { productItem } from '../../index';
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const props = defineProps({
|
||||
openNumber: Number,
|
||||
});
|
||||
const emits = defineEmits(['confirm']);
|
||||
const visible = ref<boolean>(false);
|
||||
import { productItem } from '../../typing';
|
||||
|
||||
const emits = defineEmits(['confirm', 'update:visible']);
|
||||
const props = defineProps<{
|
||||
visible: boolean;
|
||||
}>();
|
||||
|
||||
const confirm = () => {
|
||||
emits('confirm', form.value.productId);
|
||||
emits('update:visible', false);
|
||||
};
|
||||
|
||||
const form = ref({
|
||||
productId: '',
|
||||
});
|
||||
|
||||
const productList = ref<[productItem] | []>([]);
|
||||
|
||||
const getContainer = () => proxy?.$refs.modal as HTMLElement;
|
||||
const getOptions = () => {
|
||||
getProductList_api().then((resp:any) => {
|
||||
getProductList_api().then((resp: any) => {
|
||||
productList.value = resp.result
|
||||
.filter((i: any) => !i?.accessId)
|
||||
.map((item: { name: any; id: any }) => ({
|
||||
|
@ -62,21 +55,11 @@ const getOptions = () => {
|
|||
})) as [productItem];
|
||||
});
|
||||
};
|
||||
const handleOk = () => {
|
||||
emits('confirm', form.value.productId);
|
||||
visible.value = false;
|
||||
};
|
||||
|
||||
const filterOption = (input: string, option: any) => {
|
||||
return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0;
|
||||
};
|
||||
watch(
|
||||
() => props.openNumber,
|
||||
() => {
|
||||
visible.value = true;
|
||||
form.value.productId = '';
|
||||
getOptions();
|
||||
},
|
||||
);
|
||||
getOptions();
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
|
|
@ -17,7 +17,13 @@
|
|||
:gridColumn="2"
|
||||
>
|
||||
<template #headerTitle>
|
||||
<a-button type="primary" @click="handleAdd"> 新增 </a-button>
|
||||
<PermissionButton
|
||||
type="primary"
|
||||
@click="handleAdd"
|
||||
hasPermission="media/Cascade:add"
|
||||
>
|
||||
<template #icon><AIcon type="PlusOutlined" />新增</template>
|
||||
</PermissionButton>
|
||||
</template>
|
||||
<template #card="slotProps">
|
||||
<CardBox
|
||||
|
@ -58,48 +64,14 @@
|
|||
</Ellipsis>
|
||||
</template>
|
||||
<template #actions="item">
|
||||
<a-tooltip
|
||||
v-bind="item.tooltip"
|
||||
:title="item.disabled && item.tooltip.title"
|
||||
>
|
||||
<a-popconfirm
|
||||
v-if="item.popConfirm"
|
||||
v-bind="item.popConfirm"
|
||||
:disabled="item.disabled"
|
||||
>
|
||||
<a-button
|
||||
:disabled="item.disabled"
|
||||
v-if="item.key === 'delete'"
|
||||
>
|
||||
<AIcon type="DeleteOutlined" />
|
||||
</a-button>
|
||||
<a-button
|
||||
:disabled="item.disabled"
|
||||
@click="item.onClick"
|
||||
v-else
|
||||
>
|
||||
<AIcon :type="item.icon" />
|
||||
<span>{{ item.text }}</span>
|
||||
</a-button>
|
||||
</a-popconfirm>
|
||||
<template v-else>
|
||||
<a-button
|
||||
:disabled="item.disabled"
|
||||
@click="item.onClick"
|
||||
>
|
||||
<AIcon :type="item.icon" />
|
||||
<span>{{ item.text }}</span>
|
||||
</a-button>
|
||||
</template>
|
||||
</a-tooltip>
|
||||
<!-- <PermissionButton
|
||||
<PermissionButton
|
||||
:disabled="item.disabled"
|
||||
:popConfirm="item.popConfirm"
|
||||
:tooltip="{
|
||||
...item.tooltip,
|
||||
}"
|
||||
@click="item.onClick"
|
||||
:hasPermission="`media/Cascade:${item.key}`"
|
||||
:hasPermission="'media/Cascade:' + item.key"
|
||||
>
|
||||
<AIcon
|
||||
type="DeleteOutlined"
|
||||
|
@ -109,7 +81,7 @@
|
|||
<AIcon :type="item.icon" />
|
||||
<span>{{ item?.text }}</span>
|
||||
</template>
|
||||
</PermissionButton> -->
|
||||
</PermissionButton>
|
||||
</template>
|
||||
</CardBox>
|
||||
</template>
|
||||
|
@ -141,38 +113,7 @@
|
|||
</template>
|
||||
<template #action="slotProps">
|
||||
<a-space :size="16">
|
||||
<a-tooltip
|
||||
v-for="i in getActions(slotProps, 'table')"
|
||||
:key="i.key"
|
||||
v-bind="i.tooltip"
|
||||
>
|
||||
<a-popconfirm
|
||||
v-if="i.popConfirm"
|
||||
v-bind="i.popConfirm"
|
||||
:disabled="i.disabled"
|
||||
>
|
||||
<a-button
|
||||
:disabled="i.disabled"
|
||||
style="padding: 0"
|
||||
type="link"
|
||||
><AIcon :type="i.icon"
|
||||
/></a-button>
|
||||
</a-popconfirm>
|
||||
<a-button
|
||||
style="padding: 0"
|
||||
type="link"
|
||||
v-else
|
||||
@click="i.onClick && i.onClick(slotProps)"
|
||||
>
|
||||
<a-button
|
||||
:disabled="i.disabled"
|
||||
style="padding: 0"
|
||||
type="link"
|
||||
><AIcon :type="i.icon"
|
||||
/></a-button>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
<!-- <template
|
||||
<template
|
||||
v-for="i in getActions(slotProps, 'table')"
|
||||
:key="i.key"
|
||||
>
|
||||
|
@ -185,11 +126,11 @@
|
|||
@click="i.onClick"
|
||||
type="link"
|
||||
style="padding: 0px"
|
||||
:hasPermission="`device/Instance:${i.key}`"
|
||||
:hasPermission="'media/Cascade:' + i.key"
|
||||
>
|
||||
<template #icon><AIcon :type="i.icon" /></template>
|
||||
</PermissionButton>
|
||||
</template> -->
|
||||
</template>
|
||||
</a-space>
|
||||
</template>
|
||||
</JProTable>
|
||||
|
@ -331,7 +272,7 @@ const getActions = (
|
|||
if (!data) return [];
|
||||
const actions = [
|
||||
{
|
||||
key: 'edit',
|
||||
key: 'update',
|
||||
text: '编辑',
|
||||
tooltip: {
|
||||
title: '编辑',
|
||||
|
@ -348,7 +289,7 @@ const getActions = (
|
|||
},
|
||||
},
|
||||
{
|
||||
key: 'view',
|
||||
key: 'channel',
|
||||
text: '选择通道',
|
||||
tooltip: {
|
||||
title: '选择通道',
|
||||
|
@ -365,7 +306,7 @@ const getActions = (
|
|||
},
|
||||
},
|
||||
{
|
||||
key: 'debug',
|
||||
key: 'push',
|
||||
text: '推送',
|
||||
tooltip: {
|
||||
title:
|
||||
|
|
|
@ -214,6 +214,10 @@ const channelId = computed(() => route.query.channelId as string);
|
|||
|
||||
const deviceType = ref('');
|
||||
|
||||
/**
|
||||
* 查询本地视频
|
||||
* @param date
|
||||
*/
|
||||
const queryLocalRecords = async (date: Dayjs) => {
|
||||
playStatus.value = 0;
|
||||
url.value = '';
|
||||
|
@ -388,17 +392,11 @@ const handlePanelChange = (date: any) => {
|
|||
*/
|
||||
const handlePlay = (_startTime: any) => {
|
||||
if (playStatus.value === 0 || _startTime !== playNowTime.value) {
|
||||
if (playTimeNode.value) {
|
||||
playTimeNode.value.playByStartTime(_startTime);
|
||||
}
|
||||
playTimeNode.value?.playByStartTime(_startTime);
|
||||
} else if (playStatus.value == 1 && _startTime === playNowTime.value) {
|
||||
if (player.value) {
|
||||
player.value.pause();
|
||||
}
|
||||
player.value?.pause();
|
||||
} else if (playStatus.value == 2 && _startTime === playNowTime.value) {
|
||||
if (player.value) {
|
||||
player.value.play();
|
||||
}
|
||||
player.value?.play();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -15,7 +15,13 @@
|
|||
:params="params"
|
||||
>
|
||||
<template #headerTitle>
|
||||
<a-button type="primary" @click="handleAdd"> 新增 </a-button>
|
||||
<PermissionButton
|
||||
type="primary"
|
||||
@click="handleAdd"
|
||||
hasPermission="media/Device:add"
|
||||
>
|
||||
<template #icon><AIcon type="PlusOutlined" />新增</template>
|
||||
</PermissionButton>
|
||||
</template>
|
||||
<template #card="slotProps">
|
||||
<CardBox
|
||||
|
@ -64,65 +70,47 @@
|
|||
</a-row>
|
||||
</template>
|
||||
<template #actions="item">
|
||||
<a-tooltip
|
||||
v-bind="item.tooltip"
|
||||
:title="item.disabled && item.tooltip.title"
|
||||
<PermissionButton
|
||||
:disabled="item.disabled"
|
||||
:popConfirm="item.popConfirm"
|
||||
:tooltip="{
|
||||
...item.tooltip,
|
||||
}"
|
||||
@click="item.onClick"
|
||||
:hasPermission="'media/Device:' + item.key"
|
||||
>
|
||||
<a-popconfirm
|
||||
v-if="item.popConfirm"
|
||||
v-bind="item.popConfirm"
|
||||
:disabled="item.disabled"
|
||||
>
|
||||
<a-button :disabled="item.disabled">
|
||||
<AIcon type="DeleteOutlined" />
|
||||
</a-button>
|
||||
</a-popconfirm>
|
||||
<AIcon
|
||||
type="DeleteOutlined"
|
||||
v-if="item.key === 'delete'"
|
||||
/>
|
||||
<template v-else>
|
||||
<a-button
|
||||
:disabled="item.disabled"
|
||||
@click="item.onClick"
|
||||
>
|
||||
<AIcon :type="item.icon" />
|
||||
<span>{{ item.text }}</span>
|
||||
</a-button>
|
||||
<AIcon :type="item.icon" />
|
||||
<span>{{ item?.text }}</span>
|
||||
</template>
|
||||
</a-tooltip>
|
||||
</PermissionButton>
|
||||
</template>
|
||||
</CardBox>
|
||||
</template>
|
||||
<template #action="slotProps">
|
||||
<a-space :size="16">
|
||||
<a-tooltip
|
||||
<template
|
||||
v-for="i in getActions(slotProps, 'table')"
|
||||
:key="i.key"
|
||||
v-bind="i.tooltip"
|
||||
>
|
||||
<a-popconfirm
|
||||
v-if="i.popConfirm"
|
||||
v-bind="i.popConfirm"
|
||||
<PermissionButton
|
||||
:disabled="i.disabled"
|
||||
>
|
||||
<a-button
|
||||
:disabled="i.disabled"
|
||||
style="padding: 0"
|
||||
type="link"
|
||||
><AIcon :type="i.icon"
|
||||
/></a-button>
|
||||
</a-popconfirm>
|
||||
<a-button
|
||||
style="padding: 0"
|
||||
:popConfirm="i.popConfirm"
|
||||
:tooltip="{
|
||||
...i.tooltip,
|
||||
}"
|
||||
@click="i.onClick"
|
||||
type="link"
|
||||
v-else
|
||||
@click="i.onClick && i.onClick(slotProps)"
|
||||
style="padding: 0px"
|
||||
:hasPermission="'media/Device:' + i.key"
|
||||
>
|
||||
<a-button
|
||||
:disabled="i.disabled"
|
||||
style="padding: 0"
|
||||
type="link"
|
||||
><AIcon :type="i.icon"
|
||||
/></a-button>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
<template #icon><AIcon :type="i.icon" /></template>
|
||||
</PermissionButton>
|
||||
</template>
|
||||
</a-space>
|
||||
</template>
|
||||
</JProTable>
|
||||
|
@ -254,7 +242,7 @@ const getActions = (
|
|||
if (!data) return [];
|
||||
const actions = [
|
||||
{
|
||||
key: 'edit',
|
||||
key: 'update',
|
||||
text: '编辑',
|
||||
tooltip: {
|
||||
title: '编辑',
|
||||
|
@ -278,9 +266,6 @@ const getActions = (
|
|||
},
|
||||
icon: 'PartitionOutlined',
|
||||
onClick: () => {
|
||||
// router.push(
|
||||
// `/media/device/Channel?id=${data.id}&type=${data.provider}`,
|
||||
// );
|
||||
menuStory.jumpPage(
|
||||
'media/Device/Channel',
|
||||
{},
|
||||
|
@ -292,7 +277,7 @@ const getActions = (
|
|||
},
|
||||
},
|
||||
{
|
||||
key: 'debug',
|
||||
key: 'view', // updateChannel
|
||||
text: '更新通道',
|
||||
tooltip: {
|
||||
title:
|
||||
|
@ -311,6 +296,7 @@ const getActions = (
|
|||
icon: 'SyncOutlined',
|
||||
onClick: () => {
|
||||
// updateChannel()
|
||||
console.log('updateChannel: ', data);
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
@ -31,7 +31,7 @@ import StepCard from '@/views/home/components/StepCard.vue';
|
|||
import BasicCountCard from '@/views/media/Home/components/BasicCountCard.vue';
|
||||
|
||||
import { usePermissionStore } from '@/store/permission';
|
||||
import type { bootConfig, recommendList } from '@/views/home/index';
|
||||
import type { bootConfig, recommendList } from '@/views/home/typing';
|
||||
|
||||
// 权限控制
|
||||
const hasPermission = usePermissionStore().hasPermission;
|
||||
|
|
|
@ -408,6 +408,7 @@ const { resetFields, validate, validateInfos, clearValidate } = useForm(
|
|||
);
|
||||
|
||||
const getDetail = async () => {
|
||||
console.log('getDetail', route)
|
||||
if (route.params.id === ':id') return;
|
||||
const res = await configApi.detail(route.params.id as string);
|
||||
// formData.value = res.result;
|
||||
|
|
|
@ -17,16 +17,22 @@
|
|||
>
|
||||
<template #headerTitle>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="handleAdd">
|
||||
<PermissionButton
|
||||
type="primary"
|
||||
@click="handleAdd"
|
||||
hasPermission="notice/Config:add"
|
||||
>
|
||||
新增
|
||||
</a-button>
|
||||
</PermissionButton>
|
||||
<a-upload
|
||||
name="file"
|
||||
accept="json"
|
||||
:showUploadList="false"
|
||||
:before-upload="beforeUpload"
|
||||
>
|
||||
<a-button>导入</a-button>
|
||||
<PermissionButton hasPermission="notice/Config:import">
|
||||
导入
|
||||
</PermissionButton>
|
||||
</a-upload>
|
||||
<a-popconfirm
|
||||
title="确认导出?"
|
||||
|
@ -34,7 +40,9 @@
|
|||
cancel-text="取消"
|
||||
@confirm="handleExport"
|
||||
>
|
||||
<a-button>导出</a-button>
|
||||
<PermissionButton hasPermission="notice/Config:export">
|
||||
导出
|
||||
</PermissionButton>
|
||||
</a-popconfirm>
|
||||
</a-space>
|
||||
</template>
|
||||
|
@ -95,34 +103,45 @@
|
|||
v-for="(o, i) in item.children"
|
||||
:key="i"
|
||||
>
|
||||
<a-button
|
||||
<PermissionButton
|
||||
type="link"
|
||||
@click="o.onClick"
|
||||
:hasPermission="`notice/Config:${o.key}`"
|
||||
>
|
||||
<AIcon :type="o.icon" />
|
||||
<template #icon>
|
||||
<AIcon :type="o.icon" />
|
||||
</template>
|
||||
<span>{{ o.text }}</span>
|
||||
</a-button>
|
||||
</PermissionButton>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
<a-popconfirm
|
||||
<j-popconfirm
|
||||
v-else-if="item.key === 'delete'"
|
||||
v-bind="item.popConfirm"
|
||||
:disabled="item.disabled"
|
||||
>
|
||||
<a-button :disabled="item.disabled">
|
||||
<AIcon type="DeleteOutlined" />
|
||||
</a-button>
|
||||
</a-popconfirm>
|
||||
<PermissionButton
|
||||
:disabled="item.disabled"
|
||||
:hasPermission="`notice/Config:${item.key}`"
|
||||
>
|
||||
<template #icon>
|
||||
<AIcon type="DeleteOutlined" />
|
||||
</template>
|
||||
</PermissionButton>
|
||||
</j-popconfirm>
|
||||
<template v-else>
|
||||
<a-button
|
||||
<PermissionButton
|
||||
:disabled="item.disabled"
|
||||
@click="item.onClick"
|
||||
:hasPermission="`notice/Config:${item.key}`"
|
||||
>
|
||||
<AIcon :type="item.icon" />
|
||||
<template #icon>
|
||||
<AIcon :type="item.icon" />
|
||||
</template>
|
||||
<span>{{ item.text }}</span>
|
||||
</a-button>
|
||||
</PermissionButton>
|
||||
</template>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
|
@ -130,37 +149,24 @@
|
|||
</template>
|
||||
<template #action="slotProps">
|
||||
<a-space :size="16">
|
||||
<a-tooltip
|
||||
<template
|
||||
v-for="i in getActions(slotProps, 'table')"
|
||||
:key="i.key"
|
||||
v-bind="i.tooltip"
|
||||
>
|
||||
<a-popconfirm
|
||||
v-if="i.popConfirm"
|
||||
v-bind="i.popConfirm"
|
||||
<PermissionButton
|
||||
:disabled="i.disabled"
|
||||
>
|
||||
<a-button
|
||||
:disabled="i.disabled"
|
||||
style="padding: 0"
|
||||
type="link"
|
||||
><AIcon :type="i.icon"
|
||||
/></a-button>
|
||||
</a-popconfirm>
|
||||
<a-button
|
||||
style="padding: 0"
|
||||
:popConfirm="i.popConfirm"
|
||||
:tooltip="{
|
||||
...i.tooltip,
|
||||
}"
|
||||
@click="i.onClick"
|
||||
type="link"
|
||||
v-else
|
||||
@click="i.onClick && i.onClick(slotProps)"
|
||||
style="padding: 0px"
|
||||
:hasPermission="'notice/Config:' + i.key"
|
||||
>
|
||||
<a-button
|
||||
:disabled="i.disabled"
|
||||
style="padding: 0"
|
||||
type="link"
|
||||
><AIcon :type="i.icon"
|
||||
/></a-button>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
<template #icon><AIcon :type="i.icon" /></template>
|
||||
</PermissionButton>
|
||||
</template>
|
||||
</a-space>
|
||||
</template>
|
||||
</JProTable>
|
||||
|
@ -324,7 +330,7 @@ const getActions = (
|
|||
if (!data) return [];
|
||||
const actions = [
|
||||
{
|
||||
key: 'edit',
|
||||
key: 'update',
|
||||
text: '编辑',
|
||||
tooltip: {
|
||||
title: '编辑',
|
||||
|
@ -349,7 +355,7 @@ const getActions = (
|
|||
},
|
||||
},
|
||||
{
|
||||
key: 'debug',
|
||||
key: 'log',
|
||||
text: '通知记录',
|
||||
tooltip: {
|
||||
title: '通知记录',
|
||||
|
@ -385,7 +391,7 @@ const getActions = (
|
|||
icon: 'EllipsisOutlined',
|
||||
children: [
|
||||
{
|
||||
key: 'debug',
|
||||
key: 'export',
|
||||
text: '导出',
|
||||
tooltip: {
|
||||
title: '导出',
|
||||
|
@ -396,7 +402,7 @@ const getActions = (
|
|||
},
|
||||
},
|
||||
{
|
||||
key: 'sync',
|
||||
key: 'bind',
|
||||
text: '同步用户',
|
||||
tooltip: {
|
||||
title: '同步用户',
|
||||
|
|
|
@ -17,16 +17,24 @@
|
|||
>
|
||||
<template #headerTitle>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="handleAdd">
|
||||
<PermissionButton
|
||||
type="primary"
|
||||
@click="handleAdd"
|
||||
hasPermission="notice/Template:add"
|
||||
>
|
||||
新增
|
||||
</a-button>
|
||||
</PermissionButton>
|
||||
<a-upload
|
||||
name="file"
|
||||
accept="json"
|
||||
:showUploadList="false"
|
||||
:before-upload="beforeUpload"
|
||||
>
|
||||
<a-button>导入</a-button>
|
||||
<PermissionButton
|
||||
hasPermission="notice/Template:import"
|
||||
>
|
||||
导入
|
||||
</PermissionButton>
|
||||
</a-upload>
|
||||
<a-popconfirm
|
||||
title="确认导出?"
|
||||
|
@ -34,7 +42,11 @@
|
|||
cancel-text="取消"
|
||||
@confirm="handleExport"
|
||||
>
|
||||
<a-button>导出</a-button>
|
||||
<PermissionButton
|
||||
hasPermission="notice/Template:export"
|
||||
>
|
||||
导出
|
||||
</PermissionButton>
|
||||
</a-popconfirm>
|
||||
</a-space>
|
||||
</template>
|
||||
|
@ -77,42 +89,24 @@
|
|||
</a-row>
|
||||
</template>
|
||||
<template #actions="item">
|
||||
<a-tooltip
|
||||
v-bind="item.tooltip"
|
||||
:title="item.disabled && item.tooltip.title"
|
||||
<PermissionButton
|
||||
:disabled="item.disabled"
|
||||
:popConfirm="item.popConfirm"
|
||||
:tooltip="{
|
||||
...item.tooltip,
|
||||
}"
|
||||
@click="item.onClick"
|
||||
:hasPermission="'notice/Template:' + item.key"
|
||||
>
|
||||
<a-popconfirm
|
||||
v-if="item.popConfirm"
|
||||
v-bind="item.popConfirm"
|
||||
:disabled="item.disabled"
|
||||
>
|
||||
<a-button :disabled="item.disabled">
|
||||
<AIcon
|
||||
type="DeleteOutlined"
|
||||
v-if="item.key === 'delete'"
|
||||
/>
|
||||
<template v-else>
|
||||
<AIcon :type="item.icon" />
|
||||
<span>{{ item.text }}</span>
|
||||
</template>
|
||||
</a-button>
|
||||
</a-popconfirm>
|
||||
<AIcon
|
||||
type="DeleteOutlined"
|
||||
v-if="item.key === 'delete'"
|
||||
/>
|
||||
<template v-else>
|
||||
<a-button
|
||||
:disabled="item.disabled"
|
||||
@click="item.onClick"
|
||||
>
|
||||
<AIcon
|
||||
type="DeleteOutlined"
|
||||
v-if="item.key === 'delete'"
|
||||
/>
|
||||
<template v-else>
|
||||
<AIcon :type="item.icon" />
|
||||
<span>{{ item.text }}</span>
|
||||
</template>
|
||||
</a-button>
|
||||
<AIcon :type="item.icon" />
|
||||
<span>{{ item?.text }}</span>
|
||||
</template>
|
||||
</a-tooltip>
|
||||
</PermissionButton>
|
||||
</template>
|
||||
</CardBox>
|
||||
</template>
|
||||
|
@ -126,37 +120,24 @@
|
|||
</template>
|
||||
<template #action="slotProps">
|
||||
<a-space :size="16">
|
||||
<a-tooltip
|
||||
<template
|
||||
v-for="i in getActions(slotProps, 'table')"
|
||||
:key="i.key"
|
||||
v-bind="i.tooltip"
|
||||
>
|
||||
<a-popconfirm
|
||||
v-if="i.popConfirm"
|
||||
v-bind="i.popConfirm"
|
||||
<PermissionButton
|
||||
:disabled="i.disabled"
|
||||
>
|
||||
<a-button
|
||||
:disabled="i.disabled"
|
||||
style="padding: 0"
|
||||
type="link"
|
||||
><AIcon :type="i.icon"
|
||||
/></a-button>
|
||||
</a-popconfirm>
|
||||
<a-button
|
||||
style="padding: 0"
|
||||
:popConfirm="i.popConfirm"
|
||||
:tooltip="{
|
||||
...i.tooltip,
|
||||
}"
|
||||
@click="i.onClick"
|
||||
type="link"
|
||||
v-else
|
||||
@click="i.onClick && i.onClick(slotProps)"
|
||||
style="padding: 0px"
|
||||
:hasPermission="'notice/Template:' + i.key"
|
||||
>
|
||||
<a-button
|
||||
:disabled="i.disabled"
|
||||
style="padding: 0"
|
||||
type="link"
|
||||
><AIcon :type="i.icon"
|
||||
/></a-button>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
<template #icon><AIcon :type="i.icon" /></template>
|
||||
</PermissionButton>
|
||||
</template>
|
||||
</a-space>
|
||||
</template>
|
||||
</JProTable>
|
||||
|
@ -286,7 +267,7 @@ const handleAdd = () => {
|
|||
* 导入
|
||||
*/
|
||||
const beforeUpload = (file: any) => {
|
||||
console.log('file: ', file);
|
||||
// console.log('file: ', file);
|
||||
const reader = new FileReader();
|
||||
reader.readAsText(file);
|
||||
reader.onload = async (result) => {
|
||||
|
@ -330,7 +311,7 @@ const getActions = (
|
|||
if (!data) return [];
|
||||
const actions = [
|
||||
{
|
||||
key: 'edit',
|
||||
key: 'update',
|
||||
text: '编辑',
|
||||
tooltip: {
|
||||
title: '编辑',
|
||||
|
@ -355,7 +336,7 @@ const getActions = (
|
|||
},
|
||||
},
|
||||
{
|
||||
key: 'debug',
|
||||
key: 'export',
|
||||
text: '导出',
|
||||
tooltip: {
|
||||
title: '导出',
|
||||
|
@ -366,7 +347,7 @@ const getActions = (
|
|||
},
|
||||
},
|
||||
{
|
||||
key: 'debug',
|
||||
key: 'log',
|
||||
text: '通知记录',
|
||||
tooltip: {
|
||||
title: '通知记录',
|
||||
|
|
|
@ -53,9 +53,9 @@
|
|||
|
||||
<script setup lang='ts' name='AddModel'>
|
||||
import type { PropType } from 'vue'
|
||||
import type { metadataType, TriggerDevice, TriggerDeviceOptions } from '@/views/rule-engine/Scene/typings'
|
||||
import type { metadataType, TriggerDevice, TriggerDeviceOptions, SelectorValuesItem } from '@/views/rule-engine/Scene/typings'
|
||||
import { onlyMessage } from '@/utils/comm'
|
||||
import { detail as deviceDetail } from '@/api/device/instance'
|
||||
import { detail as deviceDetail } from '@/api/device/instance'
|
||||
import Product from './Product.vue'
|
||||
import DeviceSelect from './DeviceSelect.vue'
|
||||
import Type from './Type.vue'
|
||||
|
@ -66,12 +66,13 @@ type Emit = {
|
|||
(e: 'save', data: TriggerDevice, options: Record<string, any>): void
|
||||
}
|
||||
|
||||
|
||||
interface AddModelType extends Omit<TriggerDevice, 'selectorValues'> {
|
||||
stepNumber: number
|
||||
deviceKeys: Array<{ label: string, value: string }>
|
||||
orgId: Array<{ label: string, value: string }>
|
||||
deviceKeys: SelectorValuesItem[]
|
||||
orgId: SelectorValuesItem[]
|
||||
productDetail: any
|
||||
selectorValues: Array<Record<string, any>>
|
||||
selectorValues: SelectorValuesItem[]
|
||||
metadata: metadataType,
|
||||
operator: TriggerDeviceOptions
|
||||
}
|
||||
|
@ -95,23 +96,21 @@ const props = defineProps({
|
|||
})
|
||||
|
||||
const addModel = reactive<AddModelType>({
|
||||
productId: '',
|
||||
selector: 'fixed',
|
||||
selectorValues: [],
|
||||
productId: props.value.productId || '',
|
||||
selector: props.value.selector || 'fixed',
|
||||
selectorValues: props.value.selectorValues || [],
|
||||
stepNumber: 0,
|
||||
deviceKeys: [],
|
||||
orgId: [],
|
||||
deviceKeys: props.value.selectorValues || [],
|
||||
orgId: props.value.selectorValues || [],
|
||||
productDetail: {},
|
||||
metadata: {},
|
||||
operator: {
|
||||
operator: props.value.operation || {
|
||||
operator: 'online'
|
||||
}
|
||||
})
|
||||
|
||||
const optionsCache = ref(props.options)
|
||||
|
||||
Object.assign(addModel, props.value)
|
||||
|
||||
const handleOptions = (data: TriggerDeviceOptions) => {
|
||||
const typeIconMap = {
|
||||
writeProperty: 'icon-bianji1',
|
||||
|
@ -230,6 +229,11 @@ const productChange = () => {
|
|||
addModel.selectorValues = []
|
||||
}
|
||||
|
||||
const getDeviceDetailByMetadata = async (deviceId: string) => {
|
||||
const resp = await deviceDetail(deviceId)
|
||||
return resp.result?.metadata
|
||||
}
|
||||
|
||||
const save = async (step?: number) => {
|
||||
let _step = step !== undefined ? step : addModel.stepNumber
|
||||
if (_step === 0) {
|
||||
|
@ -240,12 +244,8 @@ const save = async (step?: number) => {
|
|||
return onlyMessage(isFixed ? '请选择设备' : '请选择部门', 'error')
|
||||
}
|
||||
// 选择方式为设备且仅选中一个设备时,物模型取该设备
|
||||
if (isFixed && addModel.selectorValues?.length === 1) {
|
||||
const resp = await deviceDetail(addModel.selectorValues[0].value)
|
||||
handleMetadata(resp.result.metadata)
|
||||
} else {
|
||||
handleMetadata(addModel.productDetail?.metadata)
|
||||
}
|
||||
const onlyOneDevice = isFixed && addModel.selectorValues?.length === 1
|
||||
handleMetadata( onlyOneDevice ? await getDeviceDetailByMetadata(addModel.selectorValues[0].value) : addModel.productDetail?.metadata)
|
||||
addModel.stepNumber = 2
|
||||
} else {
|
||||
const typeData = await typeRef.value.vail()
|
||||
|
@ -273,6 +273,16 @@ const stepChange = (step: number) => {
|
|||
}
|
||||
}
|
||||
|
||||
const initQuery = async () => {
|
||||
if (props.value.selector === 'fixed' && props.value.selectorValues?.length) {
|
||||
handleMetadata(await getDeviceDetailByMetadata(props.value.selectorValues[0].value))
|
||||
}
|
||||
}
|
||||
|
||||
nextTick(() => {
|
||||
initQuery()
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<Search
|
||||
<j-advanced-search
|
||||
:columns="columns"
|
||||
type='simple'
|
||||
@search="handleSearch"
|
||||
|
@ -7,7 +7,7 @@
|
|||
target="scene-triggrt-device-device"
|
||||
/>
|
||||
<a-divider style='margin: 0' />
|
||||
<j-table
|
||||
<j-pro-table
|
||||
ref='actionRef'
|
||||
model='CARD'
|
||||
:columns='columns'
|
||||
|
@ -60,7 +60,7 @@
|
|||
</template>
|
||||
</CardBox>
|
||||
</template>
|
||||
</j-table>
|
||||
</j-pro-table>
|
||||
</template>
|
||||
|
||||
<script setup lang='ts' name='DeviceSelectList'>
|
||||
|
@ -68,17 +68,17 @@ import type { PropType } from 'vue'
|
|||
import { getImage } from '@/utils/comm'
|
||||
import { query } from '@/api/device/instance'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import type { SelectorValuesItem } from '@/views/rule-engine/Scene/typings'
|
||||
|
||||
type Emit = {
|
||||
(e: 'update', data: Array<{ name: string, value: string}>): void
|
||||
(e: 'update', data: SelectorValuesItem[]): void
|
||||
}
|
||||
|
||||
const actionRef = ref()
|
||||
const params = ref({})
|
||||
const context = inject('SceneDeviceAddModel')
|
||||
const props = defineProps({
|
||||
rowKeys: {
|
||||
type: Array as PropType<Array<{ name: string, value: string}>>,
|
||||
type: Array as PropType<SelectorValuesItem[]>,
|
||||
default: () => ([])
|
||||
},
|
||||
productId: {
|
||||
|
|
|
@ -12,17 +12,13 @@ import DeviceList from './DeviceList.vue'
|
|||
import OrgList from './OrgList.vue'
|
||||
import { getImage } from '@/utils/comm'
|
||||
import type { PropType } from 'vue'
|
||||
|
||||
type ItemType = {
|
||||
name: string,
|
||||
value: string
|
||||
}
|
||||
import { SelectorValuesItem } from '@/views/rule-engine/Scene/typings'
|
||||
|
||||
type Emit = {
|
||||
(e: 'update:selector', data: string): void
|
||||
(e: 'update:selectorValues', data: ItemType[]): void
|
||||
(e: 'update:deviceKeys', data: ItemType[]): void
|
||||
(e: 'update:orgId', data: ItemType[]): void
|
||||
(e: 'update:selectorValues', data: SelectorValuesItem[]): void
|
||||
(e: 'update:deviceKeys', data: SelectorValuesItem[]): void
|
||||
(e: 'update:orgId', data: SelectorValuesItem[]): void
|
||||
}
|
||||
|
||||
const emit = defineEmits<Emit>()
|
||||
|
@ -36,18 +32,22 @@ const props = defineProps({
|
|||
type: String,
|
||||
default: ''
|
||||
},
|
||||
device: {
|
||||
type: Array as PropType<ItemType[]>,
|
||||
selectorValues: {
|
||||
type: Array as PropType<SelectorValuesItem[]>,
|
||||
default: () => []
|
||||
},
|
||||
deviceKeys: {
|
||||
type: Array as PropType<SelectorValuesItem[]>,
|
||||
default: () => []
|
||||
},
|
||||
orgId: {
|
||||
type: Array as PropType<ItemType[]>,
|
||||
type: Array as PropType<SelectorValuesItem[]>,
|
||||
default: () => []
|
||||
}
|
||||
})
|
||||
|
||||
const selectorModel = ref(props.selector)
|
||||
const devices = ref(props.device)
|
||||
const devices = ref(props.deviceKeys)
|
||||
const orgIds = ref(props.orgId)
|
||||
|
||||
const typeList = [
|
||||
|
@ -69,6 +69,7 @@ const updateDevice = (d: any[]) => {
|
|||
}
|
||||
|
||||
const updateOrg = (d: any[]) => {
|
||||
console.log('updateOrg', d)
|
||||
orgIds.value = d
|
||||
emit('update:orgId', d)
|
||||
emit('update:selectorValues', d)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<Search
|
||||
<j-advanced-search
|
||||
:columns="columns"
|
||||
type='simple'
|
||||
@search="handleSearch"
|
||||
|
@ -7,7 +7,7 @@
|
|||
target="scene-triggrt-device-category"
|
||||
/>
|
||||
<a-divider style='margin: 0' />
|
||||
<JTable
|
||||
<j-pro-table
|
||||
ref="instanceRef"
|
||||
model='TABLE'
|
||||
type='TREE'
|
||||
|
@ -26,9 +26,10 @@
|
|||
onChange: selectedRowChange
|
||||
}'
|
||||
:onChange='tableChange'
|
||||
@selectCancel='cancelAll'
|
||||
>
|
||||
|
||||
</JTable>
|
||||
</j-pro-table>
|
||||
|
||||
</template>
|
||||
|
||||
|
@ -36,14 +37,15 @@
|
|||
import type { PropType } from 'vue'
|
||||
import { getExpandedRowById } from './util'
|
||||
import { getTreeData_api } from '@/api/system/department'
|
||||
import { SelectorValuesItem } from '@/views/rule-engine/Scene/typings'
|
||||
|
||||
type Emit = {
|
||||
(e: 'update', data: Array<{ name: string, value: string}>): void
|
||||
(e: 'update', data: SelectorValuesItem[]): void
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
rowKeys: {
|
||||
type: Array as PropType<Array<{ name: string, value: string}>>,
|
||||
type: Array as PropType<SelectorValuesItem[]>,
|
||||
default: () => ([])
|
||||
},
|
||||
productId: {
|
||||
|
@ -69,6 +71,9 @@ const columns = [
|
|||
width: 300,
|
||||
ellipsis: true,
|
||||
dataIndex: 'name',
|
||||
search: {
|
||||
type: 'string'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '排序',
|
||||
|
@ -109,10 +114,14 @@ const query = async (p: any) => {
|
|||
return resp
|
||||
}
|
||||
|
||||
const selectedRowChange = (_: any, selectedRows: any[]) => {
|
||||
const selectedRowChange = (values: any, selectedRows: any[]) => {
|
||||
const item = selectedRows[0]
|
||||
console.log(selectedRows)
|
||||
emit('update', item ? [{ name: item.name, value: item.id }] : [])
|
||||
console.log(values, selectedRows)
|
||||
emit('update', [{ name: item.name, value: item.id }])
|
||||
}
|
||||
|
||||
const cancelAll = () => {
|
||||
emit('update', [])
|
||||
}
|
||||
|
||||
const expandedRowChange = (keys: string[]) => {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<Search
|
||||
<j-advanced-search
|
||||
:columns="columns"
|
||||
type='simple'
|
||||
@search="handleSearch"
|
||||
|
@ -7,7 +7,7 @@
|
|||
target="scene-triggrt-device-device"
|
||||
/>
|
||||
<a-divider style='margin: 0' />
|
||||
<j-table
|
||||
<j-pro-table
|
||||
ref='actionRef'
|
||||
model='CARD'
|
||||
:columns='columns'
|
||||
|
@ -53,7 +53,7 @@
|
|||
</template>
|
||||
</CardBox>
|
||||
</template>
|
||||
</j-table>
|
||||
</j-pro-table>
|
||||
</template>
|
||||
|
||||
<script setup lang='ts' name='Product'>
|
||||
|
@ -83,6 +83,7 @@ const props = defineProps({
|
|||
})
|
||||
|
||||
const emit = defineEmits<Emit>()
|
||||
const firstFind = ref(true)
|
||||
|
||||
const columns = [
|
||||
{
|
||||
|
@ -230,7 +231,7 @@ const handleSearch = (p: any) => {
|
|||
params.value = p
|
||||
}
|
||||
|
||||
const productQuery = (p: any) => {
|
||||
const productQuery = async (p: any) => {
|
||||
const sorts: any = [];
|
||||
|
||||
if (props.rowKey) {
|
||||
|
@ -241,7 +242,15 @@ const productQuery = (p: any) => {
|
|||
}
|
||||
sorts.push({ name: 'createTime', order: 'desc' });
|
||||
p.sorts = sorts
|
||||
return queryProductList(p)
|
||||
const resp = await queryProductList(p)
|
||||
if (resp.success && props.rowKey && firstFind.value) {
|
||||
const productItem = (resp.result as { data: any[]}).data.find((item: any) => item.id === props.rowKey)
|
||||
emit('update:detail', productItem)
|
||||
firstFind.value = false
|
||||
}
|
||||
return {
|
||||
...resp
|
||||
}
|
||||
}
|
||||
|
||||
const handleClick = (detail: any) => {
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
<Title :options='data.options.trigger' />
|
||||
</AddButton>
|
||||
</a-form-item>
|
||||
<Terms />
|
||||
<Action />
|
||||
<AddModel v-if='visible' @cancel='visible = false' @save='save' :value='data.trigger.device' :options='data.options.trigger' />
|
||||
</div>
|
||||
|
@ -26,6 +27,7 @@ import AddModel from './AddModal.vue'
|
|||
import AddButton from '../components/AddButton.vue'
|
||||
import Title from '../components/Title.vue'
|
||||
import Action from '../action/index.vue'
|
||||
import Terms from '../components/Terms'
|
||||
import type { TriggerDevice } from '@/views/rule-engine/Scene/typings'
|
||||
|
||||
const sceneStore = useSceneStore()
|
||||
|
|
|
@ -0,0 +1,153 @@
|
|||
<template>
|
||||
<j-dropdown class='scene-select' trigger='click'>
|
||||
<div :class='dropdownButtonClass'>
|
||||
<span :style='LabelStyle'>
|
||||
{{ label }}
|
||||
</span>
|
||||
</div>
|
||||
<template #overlay>
|
||||
<template v-if='options.length'>
|
||||
<j-menu v-if='component === "select"' @click='menuSelect'>
|
||||
<j-menu-item v-for='item in options' :key='item.value'>{{ item.label }}</j-menu-item>
|
||||
</j-menu>
|
||||
<j-tree
|
||||
:selectedKeys='selectValue ? [selectValue] : []'
|
||||
:treeData='options'
|
||||
@select='treeSelect'
|
||||
/>
|
||||
</template>
|
||||
<div class='scene-select-empty' v-else>
|
||||
<j-empty />
|
||||
</div>
|
||||
</template>
|
||||
</j-dropdown>
|
||||
</template>
|
||||
|
||||
<script lang='ts' setup name='DropdownButton'>
|
||||
import type { PropType } from 'vue'
|
||||
|
||||
type LabelType = string | number | undefined
|
||||
|
||||
type DropdownButtonOptions = {
|
||||
label: string;
|
||||
value: string;
|
||||
children?: DropdownButtonOptions[];
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
type Emit = {
|
||||
(e: 'update:value', data: string | number): void
|
||||
(e: 'select', data: DropdownButtonOptions | undefined ): void
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: undefined
|
||||
},
|
||||
value: {
|
||||
type: [String, Number],
|
||||
default: undefined
|
||||
},
|
||||
options: {
|
||||
type: Array as PropType<Array<DropdownButtonOptions>>,
|
||||
default: () => []
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'column' // 'column' | 'termType' | 'value' | 'type'
|
||||
},
|
||||
component: {
|
||||
type: String,
|
||||
default: 'select' // 'select' | 'treeSelect'
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits<Emit>()
|
||||
|
||||
const label = ref<LabelType>(props.placeholder)
|
||||
const selectValue = ref(props.value)
|
||||
const flatMapTree = new Map()
|
||||
|
||||
const LabelStyle = computed(() => {
|
||||
return {
|
||||
color: selectValue.value ? '#' : '#'
|
||||
}
|
||||
})
|
||||
|
||||
const dropdownButtonClass = computed(() => ({
|
||||
'dropdown-button': true,
|
||||
'column': props.type === 'column',
|
||||
'termType': props.type === 'termType',
|
||||
'value': props.type === 'value',
|
||||
'type': props.type === 'type',
|
||||
}))
|
||||
|
||||
const getOption = (key?: string | number): DropdownButtonOptions | undefined => {
|
||||
let option
|
||||
for(let i = props.options.length - 1; i >= 0; i --) {
|
||||
const cacheOption = props.options[i]
|
||||
if (cacheOption.value === key) {
|
||||
option = {
|
||||
...cacheOption
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
return option
|
||||
}
|
||||
|
||||
const treeSelect = () => {
|
||||
|
||||
}
|
||||
|
||||
const menuSelect = (v: any) => {
|
||||
const option = getOption(props.value)
|
||||
emit('update:value', v.key)
|
||||
emit('select', option)
|
||||
}
|
||||
|
||||
watch([props.options, props.value], () => {
|
||||
const option = getOption(props.value)
|
||||
console.log(props.value)
|
||||
if (option) {
|
||||
label.value = option.label
|
||||
} else {
|
||||
label.value = props.value || props.placeholder
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang='less'>
|
||||
.dropdown-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 6px 8px;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.column {
|
||||
color: #00a4fe;
|
||||
background-color: rgba(154, 219, 255, 0.3);
|
||||
border-color: rgba(0, 164, 254, 0.3);
|
||||
}
|
||||
|
||||
.termType {
|
||||
color: #2f54eb;
|
||||
background-color: rgba(163, 202, 255, 0.3);
|
||||
border-color: rgba(47, 84, 235, 0.3);
|
||||
}
|
||||
|
||||
.value {
|
||||
color: #692ca7;
|
||||
background-color: rgba(188, 125, 238, 0.1);
|
||||
border-color: rgba(188, 125, 238, 0.5);
|
||||
}
|
||||
|
||||
.type {
|
||||
padding: 5px 10px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,13 @@
|
|||
<template>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ParamsDropdown'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -0,0 +1,135 @@
|
|||
<template>
|
||||
<div :class='["actions-terms-warp", props.class]'>
|
||||
<div class='actions-terms-title'>
|
||||
{{ isFirst ? '当' : '否则' }}
|
||||
</div>
|
||||
<div :class='optionsClass'>
|
||||
<j-popconfirm
|
||||
title='确认删除?'
|
||||
@confirm='onDelete'
|
||||
>
|
||||
<div v-if='!isFirst' class='terms-params-delete danger show'>
|
||||
<AIcon type='DeleteOutlined' />
|
||||
</div>
|
||||
</j-popconfirm>
|
||||
<div
|
||||
class='actions-terms-list'
|
||||
@mouseover='mouseover'
|
||||
@mouseout='mouseout'
|
||||
>
|
||||
<j-popconfirm
|
||||
title='该操作将清空其它所有否则条件,确认删除?'
|
||||
placement='topRight'
|
||||
@confirm='onDeleteAll'
|
||||
>
|
||||
<AIcon type='CloseOutlined' v-show='showDelete' />
|
||||
</j-popconfirm>
|
||||
|
||||
<div class='actions-terms-list-content'>
|
||||
<template v-if='showWhen'>
|
||||
<TermsItem
|
||||
v-for='(item, index) in data.when'
|
||||
:key='item.key'
|
||||
:isFirst='index === 0'
|
||||
:isLast='index === data.when.length -1'
|
||||
:branchName='name'
|
||||
:whenName='index'
|
||||
:data='item'
|
||||
:isFrist='index === 0'
|
||||
/>
|
||||
</template>
|
||||
<span v-else class='when-add' @click='addWhen' :style='{ padding: isFirst ? "16px 0" : 0 }'>
|
||||
<AIcon type='PlusCircleOutlined' style='padding: 4px' />
|
||||
添加过滤条件
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class='actions-branches'>
|
||||
<j-form-item></j-form-item>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang='ts' setup name='Branchs'>
|
||||
import type { PropType } from 'vue'
|
||||
import type { ActionBranchesProps } from '@/views/rule-engine/Scene/typings'
|
||||
import TermsItem from './TermsItem.vue'
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useSceneStore } from 'store/scene'
|
||||
|
||||
const sceneStore = useSceneStore()
|
||||
const { data: FormModel } = storeToRefs(sceneStore)
|
||||
|
||||
const props = defineProps({
|
||||
isFirst: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
data: {
|
||||
type: Object as PropType<ActionBranchesProps>,
|
||||
default: () => ({
|
||||
when: [],
|
||||
shakeLimit: {},
|
||||
then: []
|
||||
})
|
||||
},
|
||||
class: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
name: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
})
|
||||
|
||||
const showDelete = ref(false)
|
||||
const error = ref(false)
|
||||
|
||||
const showWhen = computed(() => {
|
||||
return props.data.when.length
|
||||
})
|
||||
|
||||
const onDelete = () => {
|
||||
|
||||
}
|
||||
|
||||
const onDeleteAll = () => {
|
||||
|
||||
}
|
||||
|
||||
const mouseover = () => {
|
||||
if (props.isFirst && props.data.when.length){
|
||||
showDelete.value = true
|
||||
}
|
||||
}
|
||||
|
||||
const mouseout = () => {
|
||||
if (props.isFirst && props.data.when.length){
|
||||
showDelete.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const addWhen = () => {
|
||||
|
||||
}
|
||||
|
||||
const optionsClass = computed(() => {
|
||||
return {
|
||||
'actions-terms-options': true,
|
||||
border: !props.isFirst,
|
||||
error: error
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang='less'>
|
||||
.when-add {
|
||||
font-size: 14px;
|
||||
color: #2F54EB;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,142 @@
|
|||
<template>
|
||||
<div class='terms-params-item'>
|
||||
<div v-if='!isFirst' class='term-type-warp'>
|
||||
<DropdownButton
|
||||
:options='[
|
||||
{ label: "并且", value: "and" },
|
||||
{ label: "或者", value: "or" },
|
||||
]'
|
||||
type='type'
|
||||
v-model:value='paramsValue.type'
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class='params-item_button'
|
||||
@mouseover='mouseover'
|
||||
@mouseout='mouseout'
|
||||
>
|
||||
<DropdownButton
|
||||
:options='options'
|
||||
type='column'
|
||||
placeholder='请选择参数'
|
||||
v-model:value='paramsValue.column'
|
||||
component='treeSelect'
|
||||
@select='columnSelect'
|
||||
/>
|
||||
<DropdownButton
|
||||
:options='termTypeOptions'
|
||||
type="termType"
|
||||
placeholder="操作符"
|
||||
v-model:value='paramsValue.termsType'
|
||||
@select='termsTypeSelect'
|
||||
/>
|
||||
<termplate v-if='showDouble'>
|
||||
|
||||
</termplate>
|
||||
<j-popconfirm title='确认删除?' @confirm='onDelete'>
|
||||
<div v-show='showDelete' class='button-delete'> <AIcon type='CloseOutlined' /></div>
|
||||
</j-popconfirm>
|
||||
</div>
|
||||
<div class='term-add' @click.stop='termAdd'>
|
||||
<div class='terms-content'>
|
||||
<AIcon type='PlusOutlined' style='font-size: 12px' />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang='ts' name='ParamsItem'>
|
||||
import type { PropType } from 'vue'
|
||||
import type { TermsType } from '@/views/rule-engine/Scene/typings'
|
||||
import DropdownButton from '../DropdownButton.vue'
|
||||
import { inject } from 'vue'
|
||||
import { ContextKey } from './util'
|
||||
|
||||
const props = defineProps({
|
||||
isFirst: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
isLast: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
name: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
value: {
|
||||
type: Object as PropType<TermsType>,
|
||||
default: () => ({
|
||||
column: '',
|
||||
type: '',
|
||||
termsType: undefined,
|
||||
value: undefined
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
const paramsValue = reactive<TermsType>({
|
||||
column: '',
|
||||
type: '',
|
||||
termType: undefined,
|
||||
value: undefined
|
||||
})
|
||||
|
||||
const showDelete = ref(false)
|
||||
const columnOptions = inject(ContextKey)
|
||||
|
||||
const options = computed(() => {
|
||||
function handleOptions() {
|
||||
|
||||
}
|
||||
|
||||
return []
|
||||
})
|
||||
|
||||
const termTypeOptions = computed(() => {
|
||||
|
||||
return []
|
||||
})
|
||||
|
||||
const showDouble = computed(() => {
|
||||
return paramsValue.termType ? ['nbtw', 'btw', 'in', 'nin'].includes(paramsValue.termType) : false
|
||||
})
|
||||
|
||||
const mouseover = () => {
|
||||
if (props.name !== 0){
|
||||
showDelete.value = true
|
||||
}
|
||||
}
|
||||
|
||||
const mouseout = () => {
|
||||
if (props.name !== 0){
|
||||
showDelete.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const columnSelect = () => {
|
||||
|
||||
}
|
||||
|
||||
const termsTypeSelect = () => {
|
||||
|
||||
}
|
||||
|
||||
const termAdd = () => {
|
||||
|
||||
}
|
||||
|
||||
const onDelete = () => {
|
||||
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
Object.assign(paramsValue, props.value)
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -0,0 +1,121 @@
|
|||
<template>
|
||||
<div class='actions-terms'>
|
||||
<TitleComponent data='触发条件' style='font-size: 14px;' >
|
||||
<template #extra>
|
||||
<j-switch
|
||||
:checked='open'
|
||||
@change='change'
|
||||
checkedChildren='开'
|
||||
unCheckedChildren='关'
|
||||
style='margin-left: 4px;'
|
||||
/>
|
||||
</template>
|
||||
</TitleComponent>
|
||||
<template v-if='open'>
|
||||
<template v-for='(item, index) in data.branches'>
|
||||
<Branches
|
||||
v-if='!!item'
|
||||
:data='item'
|
||||
:isFirst='index === 0'
|
||||
:name='index'
|
||||
:key='item.key'
|
||||
@delete='branchesDelete'
|
||||
@deleteAll='branchesDeleteAll'
|
||||
/>
|
||||
<div v-else class='actions-terms-warp' :style='{ marginTop: data.branches.length === 2 ? 0 : 24 }'>
|
||||
<div class='actions-terms-title' style='padding: 0'>
|
||||
否则
|
||||
</div>
|
||||
<div class='actions-terms-options no-when'>
|
||||
<AIcon type='PlusOutlined' class='when-add-button' @click='addBranches' />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
<j-form-item
|
||||
v-else
|
||||
:name='["branches", 0, "then"]'
|
||||
:rules='rules'
|
||||
>
|
||||
|
||||
</j-form-item>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang='ts' name='Terms'>
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useSceneStore } from 'store/scene'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { provide } from 'vue'
|
||||
import { ContextKey } from './util'
|
||||
import { getParseTerm } from '@/api/rule-engine/scene'
|
||||
import type { FormModelType } from '@/views/rule-engine/Scene/typings'
|
||||
import Branches from './Branchs.vue'
|
||||
|
||||
const sceneStore = useSceneStore()
|
||||
const { data } = storeToRefs(sceneStore)
|
||||
|
||||
const open = ref(false)
|
||||
const columnOptions = ref<any[]>([])
|
||||
|
||||
provide(ContextKey, columnOptions)
|
||||
|
||||
const change = (e: boolean) => {
|
||||
open.value = e
|
||||
}
|
||||
|
||||
const rules = [{
|
||||
validator(_: string, value: any) {
|
||||
if (!value || (value && !value.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();
|
||||
}
|
||||
}]
|
||||
|
||||
const queryColumn = (dataModel: FormModelType) => {
|
||||
const cloneDevice = cloneDeep(dataModel)
|
||||
cloneDevice.branches = cloneDevice.branches?.filter(item => !!item)
|
||||
getParseTerm(cloneDevice).then(res => {
|
||||
columnOptions.value = res as any
|
||||
})
|
||||
}
|
||||
|
||||
const addBranches = () => {
|
||||
|
||||
}
|
||||
|
||||
const branchesDelete = (index: number) => {
|
||||
if ((data as FormModelType).branches?.length === 2) {
|
||||
(data as FormModelType).branches?.splice(index, 1, null as any)
|
||||
} else {
|
||||
(data as FormModelType).branches?.splice(index, 1)
|
||||
}
|
||||
(data as FormModelType).options?.when?.splice(index, 1)
|
||||
}
|
||||
|
||||
const branchesDeleteAll = () => {
|
||||
|
||||
}
|
||||
|
||||
watchEffect(() => {
|
||||
if ((data as FormModelType).trigger?.device) {
|
||||
queryColumn((data as FormModelType))
|
||||
}
|
||||
})
|
||||
|
||||
watchEffect(() => {
|
||||
open.value = !(
|
||||
(data as FormModelType).branches &&
|
||||
(data as FormModelType).branches?.length === 1
|
||||
)
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang='less'>
|
||||
|
||||
</style>
|
|
@ -0,0 +1,126 @@
|
|||
<template>
|
||||
<div class='terms-params'>
|
||||
<div class='terms-params-warp'>
|
||||
<div v-if='!isFirst' class='term-type-warp'>
|
||||
<DropdownButton
|
||||
:options='[
|
||||
{ label: "并且", value: "and" },
|
||||
{ label: "或者", value: "or" },
|
||||
]'
|
||||
type='type'
|
||||
v-model:value='formModel.branches[branchName].when[whenName].type'
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class='terms-params-content'
|
||||
@mouseover='mouseover'
|
||||
@mouseout='mouseout'
|
||||
>
|
||||
<j-popconfirm
|
||||
title='确认删除?'
|
||||
@confirm='onDelete'
|
||||
>
|
||||
<div v-show='showDelete' class='terms-params-delete'>
|
||||
<AIcon type='CloseOutlined' />
|
||||
</div>
|
||||
</j-popconfirm>
|
||||
|
||||
<j-form-item
|
||||
v-for='(item, index) in data.terms'
|
||||
:key='item.key'
|
||||
:name='["branches", branchName, "when", whenName, "terms", index]'
|
||||
>
|
||||
<ParamsItem
|
||||
v-model:value='formModel.branches[branchName].when[whenName].terms[index]'
|
||||
:isFirst='index === 0'
|
||||
:isLast='index === data.terms.length - 1'
|
||||
:name='index'
|
||||
@change='paramsChange'
|
||||
@delete='paramsDelete'
|
||||
@add='paramsAdd'
|
||||
/>
|
||||
</j-form-item>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang='ts' name='TermsItem'>
|
||||
import type { PropType } from 'vue'
|
||||
import type { TermsType } from '@/views/rule-engine/Scene/typings'
|
||||
import DropdownButton from '../DropdownButton.vue'
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useSceneStore } from 'store/scene'
|
||||
import ParamsItem from './ParamsItem.vue'
|
||||
|
||||
const sceneStore = useSceneStore()
|
||||
const { data: formModel } = storeToRefs(sceneStore)
|
||||
|
||||
const props = defineProps({
|
||||
isFirst: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
data: {
|
||||
type: Object as PropType<TermsType>,
|
||||
default: () => ({
|
||||
when: [],
|
||||
shakeLimit: {},
|
||||
then: []
|
||||
})
|
||||
},
|
||||
class: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
branchName: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
whenName: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
})
|
||||
|
||||
const showDelete = ref(false)
|
||||
|
||||
const mouseover = () => {
|
||||
console.log(props.whenName)
|
||||
if (props.whenName !== 0){
|
||||
showDelete.value = true
|
||||
}
|
||||
}
|
||||
|
||||
const mouseout = () => {
|
||||
console.log(props.whenName)
|
||||
if (props.whenName !== 0){
|
||||
showDelete.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const onDelete = () => {
|
||||
|
||||
}
|
||||
|
||||
const onDeleteAll = () => {
|
||||
|
||||
}
|
||||
|
||||
const paramsChange = () => {
|
||||
|
||||
}
|
||||
|
||||
const paramsDelete = () => {
|
||||
|
||||
}
|
||||
|
||||
const paramsAdd = () => {
|
||||
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -0,0 +1,204 @@
|
|||
.add-button() {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
color: rgba(0, 0, 0, 0.3);
|
||||
background-color: #fff;
|
||||
border: 1px dashed rgba(0, 0, 0, 0.3);
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.deleteBtn() {
|
||||
position: absolute;
|
||||
top: -10px;
|
||||
right: -10px;
|
||||
display: none;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
color: #999;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
background-color: #f1f1f1;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
|
||||
&.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
}
|
||||
|
||||
.actions-terms {
|
||||
.actions-terms-warp {
|
||||
display: flex;
|
||||
width: 66.66%;
|
||||
margin-bottom: 24px;
|
||||
|
||||
&.first-children {
|
||||
width: 100%;
|
||||
.actions-branches {
|
||||
width: 66.66%;
|
||||
}
|
||||
}
|
||||
|
||||
&.first-children,
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.when-add-button {
|
||||
.add-button();
|
||||
}
|
||||
|
||||
.actions-terms-title {
|
||||
width: 40px;
|
||||
padding-top: 16px;
|
||||
color: #6968be;
|
||||
font-weight: 800;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.actions-terms-options {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
width: 0;
|
||||
|
||||
&.border {
|
||||
padding: 10px 18px 0 18px;
|
||||
border: 1px dashed #999;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
&.no-when {
|
||||
flex: none;
|
||||
}
|
||||
|
||||
.actions-terms-list {
|
||||
position: relative;
|
||||
margin-bottom: 16px;
|
||||
|
||||
.ant-form-item-has-error {
|
||||
.params-item_button {
|
||||
border-color: @error-color;
|
||||
}
|
||||
}
|
||||
|
||||
.actions-terms-list-content {
|
||||
display: flex;
|
||||
padding-top: 10px;
|
||||
overflow-x: auto;
|
||||
overflow-y: visible;
|
||||
row-gap: 16px;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.terms-params {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
|
||||
.terms-params-warp {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.terms-params-content {
|
||||
position: relative;
|
||||
display: flex;
|
||||
// flex-wrap: wrap;
|
||||
padding: 8px;
|
||||
padding-bottom: 0;
|
||||
border: 1px dashed #e0e0e0;
|
||||
//background-color: #fafafa;
|
||||
border-radius: 6px;
|
||||
row-gap: 16px;
|
||||
.terms-params-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.ant-form-item {
|
||||
margin-bottom: 8px;
|
||||
&:not(:first-child) {
|
||||
.ant-form-item-explain-error {
|
||||
padding-left: 80px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.terms-group-add {
|
||||
width: 66px;
|
||||
margin-left: 16px;
|
||||
padding: 2px 8px;
|
||||
color: rgba(0, 0, 0, 0.3);
|
||||
background: #fff;
|
||||
border: 1px dashed rgba(0, 0, 0, 0.3);
|
||||
border-radius: 30px;
|
||||
cursor: pointer;
|
||||
|
||||
.terms-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.term-type-warp {
|
||||
width: 50px;
|
||||
margin: 0 16px;
|
||||
.term-type {
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.terms-params-item {
|
||||
.params-button {
|
||||
padding: 6px 8px;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.params-item_button {
|
||||
position: relative;
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
|
||||
|
||||
.button-delete {
|
||||
.deleteBtn();
|
||||
}
|
||||
}
|
||||
|
||||
.term-add {
|
||||
margin-left: 16px;
|
||||
.add-button();
|
||||
}
|
||||
}
|
||||
|
||||
.terms-params-delete {
|
||||
.deleteBtn();
|
||||
|
||||
&.danger {
|
||||
color: #e50012;
|
||||
background-color: rgba(229, 0, 18, 0.1);
|
||||
}
|
||||
|
||||
&.filter-terms-params-delete {
|
||||
transform: translateY(6px);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
import Terms from './Terms.vue'
|
||||
import './index.less'
|
||||
|
||||
export default Terms
|
|
@ -0,0 +1,2 @@
|
|||
export const ContextKey = 'columnOptions'
|
||||
|
|
@ -122,13 +122,18 @@ export interface TriggerDeviceOptions {
|
|||
functionParameters?: Record<string, any>[];
|
||||
}
|
||||
|
||||
export type SelectorValuesItem = {
|
||||
name: string
|
||||
value: any
|
||||
}
|
||||
|
||||
/**
|
||||
* 设备触发配置
|
||||
*/
|
||||
export interface TriggerDevice {
|
||||
productId: string;
|
||||
selector: string;
|
||||
selectorValues?: Record<string, any>[];
|
||||
selectorValues?: SelectorValuesItem[];
|
||||
operation?: TriggerDeviceOptions;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,288 +1,291 @@
|
|||
<template>
|
||||
<page-container>
|
||||
<a-card class="basis-container">
|
||||
<a-form
|
||||
layout="vertical"
|
||||
ref="formBasicRef"
|
||||
:rules="rulesFrom"
|
||||
:model="formValue"
|
||||
>
|
||||
<a-row :span="24" :gutter="24">
|
||||
<a-col :span="10">
|
||||
<a-form-item label="系统名称" name="title">
|
||||
<a-input
|
||||
v-model:value="formValue.title"
|
||||
:maxlength="64"
|
||||
placeholder="请输入系统名称"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="主题色" name="headerTheme">
|
||||
<a-select v-model:value="formValue.headerTheme">
|
||||
<a-select-option value="light"
|
||||
>白色</a-select-option
|
||||
>
|
||||
<a-select-option value="dark">黑色</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<template #label>
|
||||
<span>高德API Key</span>
|
||||
<a-tooltip title="配置后平台可调用高德地图GIS服务">
|
||||
<img
|
||||
class="img-style"
|
||||
:src="getImage('/init-home/mark.png')"
|
||||
/>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input
|
||||
v-model:value="formValue.apiKey"
|
||||
placeholder="请输入高德API Key"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item name="'base-path'">
|
||||
<template #label>
|
||||
<span>base-path</span>
|
||||
<a-tooltip title="系统后台访问的url">
|
||||
<img
|
||||
class="img-style"
|
||||
:src="getImage('/init-home/mark.png')"
|
||||
/>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input
|
||||
v-model:value="formValue['base-path']"
|
||||
placeholder="请输入高德API Key"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-row :gutter="24" :span="24">
|
||||
<a-col>
|
||||
<a-form-item label="系统logo">
|
||||
<div class="upload-image-warp-logo">
|
||||
<div class="upload-image-border-logo">
|
||||
<a-upload
|
||||
name="file"
|
||||
:action="action"
|
||||
:headers="headers"
|
||||
:showUploadList="false"
|
||||
:beforeUpload="
|
||||
uploader.beforeLogoUpload
|
||||
"
|
||||
@change="uploader.handleChangeLogo"
|
||||
:accept="
|
||||
uploader.imageTypes.toString()
|
||||
"
|
||||
>
|
||||
<div
|
||||
class="upload-image-content-logo"
|
||||
<page-container>
|
||||
<div class="basis-container">
|
||||
<j-form
|
||||
layout="vertical"
|
||||
ref="formBasicRef"
|
||||
:rules="rulesFrom"
|
||||
:model="formValue"
|
||||
>
|
||||
<j-row :span="24" :gutter="24">
|
||||
<j-col :span="10">
|
||||
<j-form-item label="系统名称" name="title">
|
||||
<j-input
|
||||
v-model:value="formValue.title"
|
||||
:maxlength="64"
|
||||
placeholder="请输入系统名称"
|
||||
/>
|
||||
</j-form-item>
|
||||
<j-form-item label="主题色" name="headerTheme">
|
||||
<j-select v-model:value="formValue.headerTheme">
|
||||
<j-select-option value="light">
|
||||
白色
|
||||
</j-select-option>
|
||||
<j-select-option value="dark">
|
||||
黑色
|
||||
</j-select-option>
|
||||
</j-select>
|
||||
</j-form-item>
|
||||
<j-form-item>
|
||||
<template #label>
|
||||
<span>高德API Key</span>
|
||||
<j-tooltip
|
||||
title="配置后平台可调用高德地图GIS服务"
|
||||
>
|
||||
<img
|
||||
class="img-style"
|
||||
:src="getImage('/init-home/mark.png')"
|
||||
/>
|
||||
</j-tooltip>
|
||||
</template>
|
||||
<j-input
|
||||
v-model:value="formValue.apiKey"
|
||||
placeholder="请输入高德API Key"
|
||||
/>
|
||||
</j-form-item>
|
||||
<j-form-item name="'base-path'">
|
||||
<template #label>
|
||||
<span>base-path</span>
|
||||
<j-tooltip title="系统后台访问的url">
|
||||
<img
|
||||
class="img-style"
|
||||
:src="getImage('/init-home/mark.png')"
|
||||
/>
|
||||
</j-tooltip>
|
||||
</template>
|
||||
<j-input
|
||||
v-model:value="formValue['base-path']"
|
||||
placeholder="请输入高德API Key"
|
||||
/>
|
||||
</j-form-item>
|
||||
<j-row :gutter="24" :span="24">
|
||||
<j-col>
|
||||
<j-form-item label="系统logo">
|
||||
<div class="upload-image-warp-logo">
|
||||
<div class="upload-image-border-logo">
|
||||
<a-upload
|
||||
name="file"
|
||||
:action="action"
|
||||
:headers="headers"
|
||||
:showUploadList="false"
|
||||
:beforeUpload="
|
||||
uploader.beforeLogoUpload
|
||||
"
|
||||
@change="
|
||||
uploader.handleChangeLogo
|
||||
"
|
||||
:accept="
|
||||
uploader.imageTypes.toString()
|
||||
"
|
||||
>
|
||||
<div
|
||||
class="loading-logo"
|
||||
v-if="form.logoLoading"
|
||||
class="upload-image-content-logo"
|
||||
>
|
||||
<LoadingOutlined
|
||||
style="font-size: 28px"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="upload-image"
|
||||
style="height: 100%"
|
||||
v-if="formValue.logo"
|
||||
:style="
|
||||
formValue.logo
|
||||
? `background-image: url(${formValue.logo});`
|
||||
: ''
|
||||
"
|
||||
></div>
|
||||
<div
|
||||
v-if="formValue.logo"
|
||||
class="upload-image-mask"
|
||||
>
|
||||
点击修改
|
||||
</div>
|
||||
<div v-else>
|
||||
<div
|
||||
class="loading-logo"
|
||||
v-if="form.logoLoading"
|
||||
>
|
||||
<LoadingOutlined
|
||||
style="
|
||||
font-size: 28px;
|
||||
"
|
||||
<AIcon
|
||||
type="LoadingOutlined"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="upload-image"
|
||||
style="height: 100%"
|
||||
v-if="formValue.logo"
|
||||
:style="
|
||||
formValue.logo
|
||||
? `background-image: url(${formValue.logo});`
|
||||
: ''
|
||||
"
|
||||
></div>
|
||||
<div
|
||||
v-if="formValue.logo"
|
||||
class="upload-image-mask"
|
||||
>
|
||||
点击修改
|
||||
</div>
|
||||
<div v-else>
|
||||
<PlusOutlined
|
||||
style="
|
||||
font-size: 28px;
|
||||
<AIcon
|
||||
:type="
|
||||
form.logoLoading
|
||||
? 'LoadingOutlined'
|
||||
: 'PlusOutlined'
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-upload>
|
||||
<div v-if="form.logoLoading">
|
||||
<div class="upload-loading-mask">
|
||||
<LoadingOutlined
|
||||
style="font-size: 28px"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="upload-tips">推荐尺寸200*200</div>
|
||||
<div class="upload-tips">支持jpg,png</div>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col>
|
||||
<a-form-item>
|
||||
<template #label>
|
||||
<span>浏览器页签</span>
|
||||
<a-tooltip
|
||||
title="浏览器tab页中显示的图片元素"
|
||||
>
|
||||
<img
|
||||
class="img-style"
|
||||
:src="
|
||||
getImage('/init-home/mark.png')
|
||||
"
|
||||
/>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<div class="upload-image-warp-logo">
|
||||
<div class="upload-image-border-logo">
|
||||
<a-upload
|
||||
name="file"
|
||||
:action="action"
|
||||
:headers="headers"
|
||||
:showUploadList="false"
|
||||
:beforeUpload="
|
||||
uploader.beforeIconUpload
|
||||
"
|
||||
@change="uploader.changeIconUpload"
|
||||
:accept="
|
||||
uploader.imageTypes.toString()
|
||||
"
|
||||
>
|
||||
<div
|
||||
class="upload-image-content-logo"
|
||||
>
|
||||
</a-upload>
|
||||
<div v-if="form.logoLoading">
|
||||
<div
|
||||
v-if="form.iconLoading"
|
||||
class="loading-icon"
|
||||
class="upload-loading-mask"
|
||||
>
|
||||
<LoadingOutlined
|
||||
style="font-size: 28px"
|
||||
<AIcon
|
||||
type="LoadingOutlined"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="upload-tips">
|
||||
推荐尺寸200*200
|
||||
</div>
|
||||
<div class="upload-tips">支持jpg,png</div>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col>
|
||||
<j-form-item>
|
||||
<template #label>
|
||||
<span>浏览器页签</span>
|
||||
<j-tooltip
|
||||
title="浏览器tab页中显示的图片元素"
|
||||
>
|
||||
<img
|
||||
class="img-style"
|
||||
:src="
|
||||
getImage(
|
||||
'/init-home/mark.png',
|
||||
)
|
||||
"
|
||||
/>
|
||||
</j-tooltip>
|
||||
</template>
|
||||
<div class="upload-image-warp-logo">
|
||||
<div class="upload-image-border-logo">
|
||||
<a-upload
|
||||
name="file"
|
||||
:action="action"
|
||||
:headers="headers"
|
||||
:showUploadList="false"
|
||||
:beforeUpload="
|
||||
uploader.beforeIconUpload
|
||||
"
|
||||
@change="
|
||||
uploader.changeIconUpload
|
||||
"
|
||||
:accept="
|
||||
uploader.imageTypes.toString()
|
||||
"
|
||||
>
|
||||
<div
|
||||
class="upload-image-icon"
|
||||
v-if="formValue.ico"
|
||||
:style="
|
||||
formValue.ico
|
||||
? `background-image: url(${formValue.ico});`
|
||||
: ''
|
||||
"
|
||||
></div>
|
||||
<div
|
||||
v-if="formValue.ico"
|
||||
class="upload-image-mask"
|
||||
class="upload-image-content-logo"
|
||||
>
|
||||
点击修改
|
||||
</div>
|
||||
<div v-else>
|
||||
<div>
|
||||
<PlusOutlined
|
||||
style="
|
||||
font-size: 28px;
|
||||
"
|
||||
<div
|
||||
v-if="form.iconLoading"
|
||||
class="loading-icon"
|
||||
>
|
||||
<AIcon
|
||||
type="LoadingOutlined"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="upload-image-icon"
|
||||
v-if="formValue.ico"
|
||||
:style="
|
||||
formValue.ico
|
||||
? `background-image: url(${formValue.ico});`
|
||||
: ''
|
||||
"
|
||||
></div>
|
||||
<div
|
||||
v-if="formValue.ico"
|
||||
class="upload-image-mask"
|
||||
>
|
||||
点击修改
|
||||
</div>
|
||||
<div v-else>
|
||||
<div>
|
||||
<AIcon
|
||||
type="PlusOutlined"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-upload>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="upload-tips">推荐尺寸64*64</div>
|
||||
<div class="upload-tips">支持icon格式</div>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</j-col>
|
||||
<j-col :span="14">
|
||||
<j-form-item label="登录背景图">
|
||||
<div class="upload-image-warp-back">
|
||||
<div class="upload-image-border-back">
|
||||
<a-upload
|
||||
name="file"
|
||||
:action="action"
|
||||
:headers="headers"
|
||||
:beforeUpload="
|
||||
uploader.beforeBackUpload
|
||||
"
|
||||
:showUploadList="false"
|
||||
@change="uploader.changeBackUpload"
|
||||
:accept="uploader.imageTypes.toString()"
|
||||
>
|
||||
<div class="upload-image-content-back">
|
||||
<div
|
||||
v-if="form.backLoading"
|
||||
class="loading-back"
|
||||
>
|
||||
<AIcon type="LoadingOutlined" />
|
||||
</div>
|
||||
<div
|
||||
class="upload-image"
|
||||
v-if="formValue.backgroud"
|
||||
:style="
|
||||
formValue.backgroud
|
||||
? `background-image: url(${formValue.backgroud});`
|
||||
: ''
|
||||
"
|
||||
></div>
|
||||
<div
|
||||
v-if="formValue.backgroud"
|
||||
class="upload-image-mask"
|
||||
>
|
||||
点击修改
|
||||
</div>
|
||||
<div v-else>
|
||||
<div>
|
||||
<AIcon
|
||||
type="PlusOutlined"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</a-upload>
|
||||
</div>
|
||||
</div>
|
||||
</a-upload>
|
||||
</div>
|
||||
|
||||
<div class="upload-tips">推荐尺寸64*64</div>
|
||||
<div class="upload-tips">支持icon格式</div>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-col>
|
||||
<a-col :span="14">
|
||||
<a-form-item label="登录背景图">
|
||||
<div class="upload-image-warp-back">
|
||||
<div class="upload-image-border-back">
|
||||
<a-upload
|
||||
name="file"
|
||||
:action="action"
|
||||
:headers="headers"
|
||||
:beforeUpload="uploader.beforeBackUpload"
|
||||
:showUploadList="false"
|
||||
@change="uploader.changeBackUpload"
|
||||
:accept="uploader.imageTypes.toString()"
|
||||
>
|
||||
<div class="upload-image-content-back">
|
||||
<div
|
||||
v-if="form.backLoading"
|
||||
class="loading-back"
|
||||
>
|
||||
<LoadingOutlined
|
||||
style="font-size: 28px"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="upload-image"
|
||||
v-if="formValue.backgroud"
|
||||
:style="
|
||||
formValue.backgroud
|
||||
? `background-image: url(${formValue.backgroud});`
|
||||
: ''
|
||||
"
|
||||
></div>
|
||||
<div
|
||||
v-if="formValue.backgroud"
|
||||
class="upload-image-mask"
|
||||
>
|
||||
点击修改
|
||||
</div>
|
||||
<div v-else>
|
||||
<div>
|
||||
<PlusOutlined
|
||||
style="font-size: 28px"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-upload>
|
||||
</div>
|
||||
</div>
|
||||
<div class="upload-tips">
|
||||
支持4M以内的图片:支持jpg、png
|
||||
</div>
|
||||
<div class="upload-tips">建议尺寸1400x1080</div>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
<div class="upload-tips">
|
||||
支持4M以内的图片:支持jpg、png
|
||||
</div>
|
||||
<div class="upload-tips">建议尺寸1400x1080</div>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</j-form>
|
||||
|
||||
<a-button
|
||||
type="primary"
|
||||
@click="form.clickSave"
|
||||
:disabled="
|
||||
form.saveLoading ||
|
||||
form.logoLoading ||
|
||||
form.iconLoading ||
|
||||
form.backLoading
|
||||
"
|
||||
>保存</a-button
|
||||
>
|
||||
</a-card>
|
||||
</page-container>
|
||||
<j-button
|
||||
type="primary"
|
||||
@click="form.clickSave"
|
||||
:disabled="
|
||||
form.saveLoading ||
|
||||
form.logoLoading ||
|
||||
form.iconLoading ||
|
||||
form.backLoading
|
||||
"
|
||||
>
|
||||
保存
|
||||
</j-button>
|
||||
</div>
|
||||
</page-container>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="Basis">
|
||||
import { PlusOutlined, LoadingOutlined } from '@ant-design/icons-vue';
|
||||
import { formType, uploaderType } from './index';
|
||||
import { getImage } from '@/utils/comm.ts';
|
||||
import { message } from 'ant-design-vue';
|
||||
|
@ -365,8 +368,8 @@ const form = reactive<formType>({
|
|||
},
|
||||
clickSave: () => {
|
||||
const hasPermission = usePermissionStore().hasPermission;
|
||||
if(hasPermission(`system/Basis:update`) ){
|
||||
formBasicRef.value.validate().then(() => {
|
||||
if (hasPermission(`system/Basis:update`)) {
|
||||
formBasicRef.value.validate().then(() => {
|
||||
form.saveLoading = true;
|
||||
const params = [
|
||||
{
|
||||
|
@ -400,11 +403,9 @@ const form = reactive<formType>({
|
|||
})
|
||||
.finally(() => (form.saveLoading = false));
|
||||
});
|
||||
}else {
|
||||
message.warning('暂无权限,请联系管理员');
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
message.warning('暂无权限,请联系管理员');
|
||||
}
|
||||
},
|
||||
});
|
||||
const { formValue, rulesFrom } = toRefs(form);
|
||||
|
@ -502,6 +503,8 @@ form.getDetails();
|
|||
|
||||
<style lang="less" scoped>
|
||||
.basis-container {
|
||||
padding: 24px;
|
||||
background-color: #fff;
|
||||
.img-style {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
|
@ -625,5 +628,9 @@ form.getDetails();
|
|||
font-size: 14px;
|
||||
line-height: 1.5715;
|
||||
}
|
||||
|
||||
.anticon {
|
||||
font-size: 28px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
<template>
|
||||
<a-card class="mangement-container">
|
||||
<div class="mangement-container">
|
||||
<div class="left">
|
||||
<a-input-search
|
||||
<j-input-search
|
||||
v-model:value="leftData.searchValue"
|
||||
placeholder="请输入"
|
||||
style="margin-bottom: 24px"
|
||||
/>
|
||||
<!-- 使用v-if用于解决异步加载数据后不展开的问题 -->
|
||||
<a-tree
|
||||
<j-tree
|
||||
v-if="leftData.treeData.length > 0"
|
||||
showLine
|
||||
defaultExpandAll
|
||||
|
@ -37,12 +37,12 @@
|
|||
{{ dataRef.title }}
|
||||
</span>
|
||||
</template>
|
||||
</a-tree>
|
||||
</j-tree>
|
||||
</div>
|
||||
<div class="right">
|
||||
<div class="btns">
|
||||
<a-button type="primary" @click="table.clickSave"
|
||||
>保存</a-button
|
||||
<j-button type="primary" @click="table.clickSave"
|
||||
>保存</j-button
|
||||
>
|
||||
</div>
|
||||
<j-pro-table
|
||||
|
@ -52,7 +52,7 @@
|
|||
:dataSource="table.data"
|
||||
>
|
||||
<template #name="slotProps">
|
||||
<a-input
|
||||
<j-input
|
||||
:disabled="slotProps.scale !== undefined"
|
||||
v-model:value="slotProps.name"
|
||||
placeholder="请输入名称"
|
||||
|
@ -60,37 +60,37 @@
|
|||
/>
|
||||
</template>
|
||||
<template #type="slotProps">
|
||||
<a-input
|
||||
<j-input
|
||||
v-model:value="slotProps.type"
|
||||
placeholder="请输入类型"
|
||||
:maxlength="64"
|
||||
/>
|
||||
</template>
|
||||
<template #length="slotProps">
|
||||
<a-input-number
|
||||
<j-input-number
|
||||
v-model:value="slotProps.length"
|
||||
:min="0"
|
||||
:max="99999"
|
||||
/>
|
||||
</template>
|
||||
<template #precision="slotProps">
|
||||
<a-input-number
|
||||
<j-input-number
|
||||
v-model:value="slotProps.precision"
|
||||
:min="0"
|
||||
:max="99999"
|
||||
/>
|
||||
</template>
|
||||
<template #notnull="slotProps">
|
||||
<a-radio-group
|
||||
<j-radio-group
|
||||
v-model:value="slotProps.notnull"
|
||||
button-style="solid"
|
||||
>
|
||||
<a-radio-button :value="true">是</a-radio-button>
|
||||
<a-radio-button :value="false">否</a-radio-button>
|
||||
</a-radio-group>
|
||||
<j-radio-button :value="true">是</j-radio-button>
|
||||
<j-radio-button :value="false">否</j-radio-button>
|
||||
</j-radio-group>
|
||||
</template>
|
||||
<template #comment="slotProps">
|
||||
<a-input
|
||||
<j-input
|
||||
v-model:value="slotProps.comment"
|
||||
placeholder="请输入说明"
|
||||
/>
|
||||
|
@ -110,19 +110,19 @@
|
|||
</PermissionButton>
|
||||
</template>
|
||||
</j-pro-table>
|
||||
<a-botton class="add-row" @click="table.addRow">
|
||||
<j-botton class="add-row" @click="table.addRow">
|
||||
<AIcon type="PlusOutlined" /> 新增行
|
||||
</a-botton>
|
||||
</j-botton>
|
||||
</div>
|
||||
</a-card>
|
||||
</div>
|
||||
<div class="dialogs">
|
||||
<a-modal
|
||||
<j-modal
|
||||
v-model:visible="dialog.visible"
|
||||
title="新增"
|
||||
@ok="dialog.handleOk"
|
||||
>
|
||||
<a-form :model="dialog.form" ref="addFormRef">
|
||||
<a-form-item
|
||||
<j-form :model="dialog.form" ref="addFormRef">
|
||||
<j-form-item
|
||||
label="名称"
|
||||
name="name"
|
||||
:rules="[
|
||||
|
@ -148,13 +148,13 @@
|
|||
},
|
||||
]"
|
||||
>
|
||||
<a-input
|
||||
<j-input
|
||||
v-model:value="dialog.form.name"
|
||||
placeholder="请输入名称"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</j-form-item>
|
||||
</j-form>
|
||||
</j-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -311,8 +311,11 @@ const table = reactive({
|
|||
name: leftData.selectedKeys[0],
|
||||
columns: table.data,
|
||||
};
|
||||
saveTable_api(id, params).then(() => {
|
||||
table.getTabelData(params.name);
|
||||
saveTable_api(id, params).then((resp) => {
|
||||
if (resp.status === 200) {
|
||||
message.success('操作成功');
|
||||
table.getTabelData(params.name);
|
||||
}
|
||||
});
|
||||
},
|
||||
clickDel: (row: any) => {},
|
||||
|
@ -348,50 +351,48 @@ function init() {
|
|||
|
||||
<style lang="less" scoped>
|
||||
.mangement-container {
|
||||
margin: 24px;
|
||||
padding: 24px;
|
||||
background-color: transparent;
|
||||
:deep(.ant-card-body) {
|
||||
display: flex;
|
||||
background-color: #fff;
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
|
||||
.left {
|
||||
flex-basis: 280px;
|
||||
padding-right: 24px;
|
||||
box-sizing: border-box;
|
||||
.left {
|
||||
flex-basis: 280px;
|
||||
padding-right: 24px;
|
||||
box-sizing: border-box;
|
||||
|
||||
.ant-tree-treenode {
|
||||
:deep(.ant-tree-treenode) {
|
||||
width: 100%;
|
||||
.ant-tree-switcher-noop {
|
||||
display: none;
|
||||
}
|
||||
.ant-tree-node-content-wrapper {
|
||||
width: 100%;
|
||||
.ant-tree-switcher-noop {
|
||||
display: none;
|
||||
}
|
||||
.ant-tree-node-content-wrapper {
|
||||
.ant-tree-title {
|
||||
width: 100%;
|
||||
.ant-tree-title {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
&:first-child .ant-tree-node-selected {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
&:first-child .ant-tree-node-selected {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
.right {
|
||||
width: calc(100% - 280px);
|
||||
box-sizing: border-box;
|
||||
border-left: 1px solid #f0f0f0;
|
||||
}
|
||||
.right {
|
||||
width: calc(100% - 280px);
|
||||
box-sizing: border-box;
|
||||
border-left: 1px solid #f0f0f0;
|
||||
|
||||
.btns {
|
||||
display: flex;
|
||||
justify-content: right;
|
||||
padding: 0px 24px;
|
||||
}
|
||||
.btns {
|
||||
display: flex;
|
||||
justify-content: right;
|
||||
padding: 0px 24px;
|
||||
}
|
||||
|
||||
.add-row {
|
||||
display: block;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
}
|
||||
.add-row {
|
||||
display: block;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,18 +1,17 @@
|
|||
<template>
|
||||
<a-modal
|
||||
<j-modal
|
||||
class="edit-dialog-container"
|
||||
:title="dialog.title"
|
||||
width="1050px"
|
||||
@ok="dialog.handleOk"
|
||||
:confirmLoading="dialog.loading.value"
|
||||
cancelText="取消"
|
||||
okText="确定"
|
||||
v-model:visible="dialog.visible.value"
|
||||
visible
|
||||
:title="dialogTitle"
|
||||
:confirmLoading="loading"
|
||||
@ok="confirm"
|
||||
@cancel="emits('update:visible', false)"
|
||||
>
|
||||
<a-form ref="formRef" :model="form.data" layout="vertical">
|
||||
<a-row :gutter="24">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
<j-form ref="formRef" :model="form.data" layout="vertical">
|
||||
<j-row :gutter="24">
|
||||
<j-col :span="12">
|
||||
<j-form-item
|
||||
name="name"
|
||||
label="名称"
|
||||
:rules="[
|
||||
|
@ -20,72 +19,72 @@
|
|||
{ max: 64, message: '最多可输入64个字符' },
|
||||
]"
|
||||
>
|
||||
<a-input
|
||||
<j-input
|
||||
v-model:value="form.data.name"
|
||||
placeholder="请输入名称"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col :span="12">
|
||||
<j-form-item
|
||||
name="typeId"
|
||||
label="类型"
|
||||
:rules="[{ required: true, message: '请选择类型' }]"
|
||||
>
|
||||
<a-select
|
||||
<j-select
|
||||
v-model:value="form.data.typeId"
|
||||
:options="form.typeOptions"
|
||||
placeholder="请选择类型"
|
||||
:disabled="!!form.data.id"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="24" v-if="form.data.typeId === 'rdb'">
|
||||
<a-col :span="24">
|
||||
<a-form-item
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
</j-row>
|
||||
<j-row :gutter="24" v-if="form.data.typeId === 'rdb'">
|
||||
<j-col :span="24">
|
||||
<j-form-item
|
||||
:name="['shareConfig', 'url']"
|
||||
label="URL"
|
||||
:rules="[{ required: true, message: '请输入URL' }]"
|
||||
>
|
||||
<a-input
|
||||
<j-input
|
||||
v-model:value="form.data.shareConfig.url"
|
||||
placeholder="请输入r2bdc或者jdbc连接地址,示例:r2dbc:mysql://127.0.0.1:3306/test"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="24" v-if="form.data.typeId === 'rabbitmq'">
|
||||
<a-col :span="24">
|
||||
<a-form-item
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
</j-row>
|
||||
<j-row :gutter="24" v-if="form.data.typeId === 'rabbitmq'">
|
||||
<j-col :span="24">
|
||||
<j-form-item
|
||||
:name="['shareConfig', 'adminUrl']"
|
||||
label="管理地址"
|
||||
:rules="[{ required: true, message: '请输入管理地址' }]"
|
||||
>
|
||||
<a-input
|
||||
<j-input
|
||||
v-model:value="form.data.shareConfig.adminUrl"
|
||||
placeholder="请输入管理地址,示例:http://localhost:15672"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="24" v-if="form.data.typeId === 'rabbitmq'">
|
||||
<a-col :span="24">
|
||||
<a-form-item
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
</j-row>
|
||||
<j-row :gutter="24" v-if="form.data.typeId === 'rabbitmq'">
|
||||
<j-col :span="24">
|
||||
<j-form-item
|
||||
:name="['shareConfig', 'addresses']"
|
||||
label="链接地址"
|
||||
:rules="[{ required: true, message: '请输入链接地址' }]"
|
||||
>
|
||||
<a-input
|
||||
<j-input
|
||||
v-model:value="form.data.shareConfig.addresses"
|
||||
placeholder="请输入链接地址,示例:localhost:5672"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="24" v-show="form.data.typeId">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
</j-row>
|
||||
<j-row :gutter="24" v-show="form.data.typeId">
|
||||
<j-col :span="12">
|
||||
<j-form-item
|
||||
:name="['shareConfig', 'username']"
|
||||
label="用户名"
|
||||
:rules="[
|
||||
|
@ -96,14 +95,14 @@
|
|||
},
|
||||
]"
|
||||
>
|
||||
<a-input
|
||||
<j-input
|
||||
v-model:value="form.data.shareConfig.username"
|
||||
placeholder="请输入用户名"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col :span="12">
|
||||
<j-form-item
|
||||
:name="['shareConfig', 'password']"
|
||||
label="密码"
|
||||
:rules="[
|
||||
|
@ -114,16 +113,16 @@
|
|||
},
|
||||
]"
|
||||
>
|
||||
<a-input-password
|
||||
<j-input-password
|
||||
v-model:value="form.data.shareConfig.password"
|
||||
placeholder="请输入密码"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="24" v-if="form.data.typeId === 'rabbitmq'">
|
||||
<a-col :span="24">
|
||||
<a-form-item
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
</j-row>
|
||||
<j-row :gutter="24" v-if="form.data.typeId === 'rabbitmq'">
|
||||
<j-col :span="24">
|
||||
<j-form-item
|
||||
:name="['shareConfig', 'virtualHost']"
|
||||
label="虚拟域"
|
||||
:rules="[
|
||||
|
@ -134,16 +133,16 @@
|
|||
},
|
||||
]"
|
||||
>
|
||||
<a-input
|
||||
<j-input
|
||||
v-model:value="form.data.shareConfig.virtualHost"
|
||||
placeholder="请输入虚拟域"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="24" v-if="form.data.typeId === 'rdb'">
|
||||
<a-col :span="24">
|
||||
<a-form-item
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
</j-row>
|
||||
<j-row :gutter="24" v-if="form.data.typeId === 'rdb'">
|
||||
<j-col :span="24">
|
||||
<j-form-item
|
||||
:name="['shareConfig', 'schema']"
|
||||
label="schema"
|
||||
:rules="[
|
||||
|
@ -154,28 +153,28 @@
|
|||
},
|
||||
]"
|
||||
>
|
||||
<a-input
|
||||
<j-input
|
||||
v-model:value="form.data.shareConfig.schema"
|
||||
placeholder="请输入schema"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="24">
|
||||
<a-col :span="24">
|
||||
<a-form-item name="description" label="说明">
|
||||
<a-textarea
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
</j-row>
|
||||
<j-row :gutter="24">
|
||||
<j-col :span="24">
|
||||
<j-form-item name="description" label="说明">
|
||||
<j-textarea
|
||||
v-model:value="form.data.description"
|
||||
placeholder="请输入说明"
|
||||
:rows="3"
|
||||
showCount
|
||||
:maxlength="200"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</j-form>
|
||||
</j-modal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
@ -186,38 +185,35 @@ import {
|
|||
import { FormInstance, message } from 'ant-design-vue';
|
||||
import type { dictItemType, optionItemType, sourceItemType } from '../typing';
|
||||
|
||||
const emits = defineEmits(['confirm']);
|
||||
|
||||
const emits = defineEmits(['confirm', 'update:visible']);
|
||||
const props = defineProps<{
|
||||
visible: boolean;
|
||||
data: sourceItemType;
|
||||
}>();
|
||||
// 弹窗相关
|
||||
const dialog = {
|
||||
title: '',
|
||||
loading: ref<boolean>(false),
|
||||
visible: ref<boolean>(false),
|
||||
handleOk: () => {
|
||||
formRef.value?.validate().then(() => {
|
||||
form.submit();
|
||||
});
|
||||
},
|
||||
// 打开弹窗
|
||||
openDialog: (row: sourceItemType) => {
|
||||
if (row.id) dialog.title = '编辑数据源';
|
||||
else dialog.title = '新增数据源';
|
||||
form.data = { ...row };
|
||||
nextTick(() => {
|
||||
formRef.value?.clearValidate();
|
||||
dialog.visible.value = true;
|
||||
});
|
||||
},
|
||||
const dialogTitle = computed(() =>
|
||||
props.data.id ? '编辑数据源' : '新增数据源',
|
||||
);
|
||||
const loading = ref(false);
|
||||
const confirm = () => {
|
||||
loading.value = true;
|
||||
formRef.value
|
||||
?.validate()
|
||||
.then(() => form.submit())
|
||||
.then((resp: any) => {
|
||||
if (resp.status === 200) {
|
||||
message.success('操作成功');
|
||||
emits('confirm');
|
||||
emits('update:visible', false);
|
||||
}
|
||||
})
|
||||
.finally(() => (loading.value = false));
|
||||
};
|
||||
// 将打开弹窗的操作暴露给父组件
|
||||
defineExpose({
|
||||
openDialog: dialog.openDialog,
|
||||
});
|
||||
|
||||
const formRef = ref<FormInstance>();
|
||||
const form = reactive({
|
||||
data: {
|
||||
shareConfig: {},
|
||||
...props.data,
|
||||
} as sourceItemType,
|
||||
|
||||
typeOptions: [] as optionItemType[],
|
||||
|
@ -232,14 +228,7 @@ const form = reactive({
|
|||
});
|
||||
},
|
||||
submit: () => {
|
||||
dialog.loading.value = true;
|
||||
saveDataSource_api(form.data)
|
||||
.then(() => {
|
||||
message.success('操作成功');
|
||||
emits('confirm');
|
||||
dialog.visible.value = false;
|
||||
})
|
||||
.finally(() => (dialog.loading.value = false));
|
||||
return saveDataSource_api(form.data);
|
||||
},
|
||||
});
|
||||
form.getTypeOption();
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
<template>
|
||||
<page-container>
|
||||
<div class="data-source-container">
|
||||
<Search :columns="query.columns" @search="query.search" />
|
||||
<j-advanced-search
|
||||
:columns="columns"
|
||||
@search="(params:any)=>queryParams = {...params}"
|
||||
/>
|
||||
|
||||
<j-pro-table
|
||||
ref="tableRef"
|
||||
:columns="table.columns"
|
||||
:columns="columns"
|
||||
:request="getDataSourceList_api"
|
||||
model="TABLE"
|
||||
:params="query.params.value"
|
||||
:params="queryParams"
|
||||
:defaultParams="{
|
||||
sorts: [{ name: 'createTime', order: 'desc' }],
|
||||
}"
|
||||
|
@ -41,7 +44,7 @@
|
|||
}}
|
||||
</template>
|
||||
<template #action="slotProps">
|
||||
<a-space :size="16">
|
||||
<j-space :size="16">
|
||||
<PermissionButton
|
||||
:uhasPermission="`${permission}:update`"
|
||||
type="link"
|
||||
|
@ -120,13 +123,16 @@
|
|||
>
|
||||
<AIcon type="DeleteOutlined" />
|
||||
</PermissionButton>
|
||||
</a-space>
|
||||
</j-space>
|
||||
</template>
|
||||
</j-pro-table>
|
||||
|
||||
<div class="dialogs">
|
||||
<EditDialog ref="editDialogRef" @confirm="table.refresh" />
|
||||
</div>
|
||||
<EditDialog
|
||||
v-if="dialog.visible"
|
||||
v-model:visible="dialog.visible"
|
||||
:data="dialog.selectItem"
|
||||
@confirm="table.refresh"
|
||||
/>
|
||||
</div>
|
||||
</page-container>
|
||||
</template>
|
||||
|
@ -150,113 +156,81 @@ const permission = 'system/DataSource';
|
|||
|
||||
const router = useRouter();
|
||||
|
||||
const query = {
|
||||
columns: [
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
search: {
|
||||
type: 'string',
|
||||
},
|
||||
const columns = [
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
search: {
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
title: '类型',
|
||||
dataIndex: 'typeId',
|
||||
key: 'typeId',
|
||||
search: {
|
||||
type: 'select',
|
||||
options: () =>
|
||||
new Promise((resolve) => {
|
||||
if (table.typeOptions.value.length > 0)
|
||||
return resolve(table.typeOptions.value);
|
||||
getDataTypeDict_api().then((resp: any) => {
|
||||
const result = resp.result as dictItemType[];
|
||||
resolve(
|
||||
result.map((item) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
})),
|
||||
);
|
||||
});
|
||||
}),
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '说明',
|
||||
dataIndex: 'description',
|
||||
key: 'description',
|
||||
search: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'state',
|
||||
key: 'state',
|
||||
ellipsis: true,
|
||||
fixed: 'left',
|
||||
search: {
|
||||
type: 'select',
|
||||
options: [
|
||||
{
|
||||
label: '正常',
|
||||
value: 'enabled',
|
||||
},
|
||||
{
|
||||
label: '已禁用',
|
||||
value: 'disabled',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
params: ref({}),
|
||||
search: (params: object) => {
|
||||
query.params.value = params;
|
||||
width: '250px',
|
||||
},
|
||||
};
|
||||
{
|
||||
title: '类型',
|
||||
dataIndex: 'typeId',
|
||||
key: 'typeId',
|
||||
search: {
|
||||
type: 'select',
|
||||
options: () =>
|
||||
new Promise((resolve) => {
|
||||
if (table.typeOptions.value.length > 0)
|
||||
return resolve(table.typeOptions.value);
|
||||
getDataTypeDict_api().then((resp: any) => {
|
||||
const result = resp.result as dictItemType[];
|
||||
resolve(
|
||||
result.map((item) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
})),
|
||||
);
|
||||
});
|
||||
}),
|
||||
},
|
||||
scopedSlots: true,
|
||||
},
|
||||
{
|
||||
title: '说明',
|
||||
dataIndex: 'description',
|
||||
key: 'description',
|
||||
search: {
|
||||
type: 'string',
|
||||
},
|
||||
ellipsis: true,
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'state',
|
||||
key: 'state',
|
||||
search: {
|
||||
type: 'select',
|
||||
options: [
|
||||
{
|
||||
label: '正常',
|
||||
value: 'enabled',
|
||||
},
|
||||
{
|
||||
label: '已禁用',
|
||||
value: 'disabled',
|
||||
},
|
||||
],
|
||||
},
|
||||
scopedSlots: true,
|
||||
width: '120px',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
key: 'action',
|
||||
scopedSlots: true,
|
||||
width: '200px',
|
||||
fixed: 'right',
|
||||
},
|
||||
];
|
||||
const queryParams = ref({});
|
||||
|
||||
const editDialogRef = ref(); // 新增弹窗实例
|
||||
const tableRef = ref<Record<string, any>>({}); // 表格实例
|
||||
const table = {
|
||||
columns: [
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
width: '250px',
|
||||
},
|
||||
{
|
||||
title: '类型',
|
||||
dataIndex: 'typeId',
|
||||
key: 'typeId',
|
||||
scopedSlots: true,
|
||||
},
|
||||
|
||||
{
|
||||
title: '说明',
|
||||
dataIndex: 'description',
|
||||
key: 'description',
|
||||
ellipsis: true,
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'state',
|
||||
key: 'state',
|
||||
scopedSlots: true,
|
||||
width: '120px',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
key: 'action',
|
||||
scopedSlots: true,
|
||||
width: '200px',
|
||||
fixed: 'right',
|
||||
},
|
||||
],
|
||||
|
||||
typeOptions: ref<optionItemType[]>([]),
|
||||
|
||||
getTypeOption: () => {
|
||||
|
@ -279,7 +253,8 @@ const table = {
|
|||
},
|
||||
// 打开编辑弹窗
|
||||
openDialog: (row: sourceItemType | {}) => {
|
||||
editDialogRef.value.openDialog({ shareConfig: {}, ...row });
|
||||
dialog.selectItem = { shareConfig: {}, ...row };
|
||||
dialog.visible = true;
|
||||
},
|
||||
// 删除
|
||||
clickDel: (row: sourceItemType) => {
|
||||
|
@ -304,6 +279,11 @@ const table = {
|
|||
},
|
||||
};
|
||||
table.getTypeOption();
|
||||
|
||||
const dialog = reactive({
|
||||
visible: false,
|
||||
selectItem: {} as any,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
|
|
@ -83,6 +83,7 @@ const selectItem = ref<any>({});
|
|||
const dialogVisible = ref(false);
|
||||
const dialogTitle = ref<'查看' | '新增' | '编辑'>('新增');
|
||||
const openDialog = (mode: '查看' | '新增' | '编辑', row: object) => {
|
||||
if(!routeParams.id) return message.warning('请先新增菜单基本信息')
|
||||
selectItem.value = { ...row };
|
||||
dialogTitle.value = mode;
|
||||
dialogVisible.value = true;
|
||||
|
|
|
@ -1,132 +1,132 @@
|
|||
<template>
|
||||
<div class="setting-container">
|
||||
<a-card>
|
||||
<h5 class="top">
|
||||
<exclamation-circle-outlined />
|
||||
<span style="padding-left: 12px"
|
||||
>基于系统源代码中的菜单数据,配置系统菜单。</span
|
||||
>
|
||||
</h5>
|
||||
<h5 class="top">
|
||||
<exclamation-circle-outlined />
|
||||
<span style="padding-left: 12px"
|
||||
>基于系统源代码中的菜单数据,配置系统菜单。</span
|
||||
>
|
||||
</h5>
|
||||
|
||||
<div class="transfer">
|
||||
<!-- 左侧树 -->
|
||||
<div class="basic-tree left">
|
||||
<div class="title">
|
||||
<div class="title-label">
|
||||
<span>源菜单</span>
|
||||
<a-tooltip>
|
||||
<template #title
|
||||
>根据系统代码自动读取的菜单数据</template
|
||||
>
|
||||
<question-circle-outlined />
|
||||
</a-tooltip>
|
||||
</div>
|
||||
<div class="title-func">
|
||||
<a-button
|
||||
type="primary"
|
||||
@click="dialogShow = true"
|
||||
ghost
|
||||
>一键拷贝</a-button
|
||||
<div class="transfer">
|
||||
<!-- 左侧树 -->
|
||||
<div class="basic-tree left">
|
||||
<div class="title">
|
||||
<div class="title-label">
|
||||
<span>源菜单</span>
|
||||
<j-tooltip>
|
||||
<template #title
|
||||
>根据系统代码自动读取的菜单数据</template
|
||||
>
|
||||
</div>
|
||||
<question-circle-outlined />
|
||||
</j-tooltip>
|
||||
</div>
|
||||
<div class="content">
|
||||
<a-input
|
||||
v-model:value="transfer.data.leftSearchValue"
|
||||
style="margin-bottom: 8px"
|
||||
placeholder="请输入菜单名称"
|
||||
<div class="title-func">
|
||||
<j-button
|
||||
type="primary"
|
||||
@click="dialogShow = true"
|
||||
ghost
|
||||
>一键拷贝</j-button
|
||||
>
|
||||
<template #prefix>
|
||||
<search-outlined style="color: #b3b3b3" />
|
||||
</template>
|
||||
</a-input>
|
||||
<a-tree
|
||||
autoExpandParent
|
||||
:tree-data="transfer.data.leftTreeData"
|
||||
draggable
|
||||
>
|
||||
<template #title="row">
|
||||
<div>{{ row.name }}</div>
|
||||
</template>
|
||||
</a-tree>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="center">
|
||||
<a-button>请拖动至右侧</a-button>
|
||||
</div>
|
||||
<!-- 右侧树 -->
|
||||
<div class="basic-tree right">
|
||||
<div class="title">
|
||||
<div class="title-label">
|
||||
<span>系统菜单</span>
|
||||
<a-tooltip>
|
||||
<template #title
|
||||
>菜单管理页面配置的菜单数据</template
|
||||
>
|
||||
<question-circle-outlined />
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content">
|
||||
<a-input
|
||||
v-model:value="transfer.data.rightSearchValue"
|
||||
style="margin-bottom: 8px"
|
||||
placeholder="请输入菜单名称"
|
||||
>
|
||||
<template #prefix>
|
||||
<search-outlined style="color: #b3b3b3" />
|
||||
</template>
|
||||
</a-input>
|
||||
<a-tree
|
||||
draggable
|
||||
autoExpandParent
|
||||
:tree-data="transfer.data.rightTreeData"
|
||||
@drop="transfer.onRightDrop"
|
||||
>
|
||||
<template #title="row">
|
||||
<div
|
||||
style="
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
"
|
||||
>
|
||||
<span>{{ row.name }}</span>
|
||||
<a-popconfirm
|
||||
title="确认删除?"
|
||||
ok-text="确定"
|
||||
cancel-text="取消"
|
||||
@confirm="transfer.removeItem(row)"
|
||||
>
|
||||
<a-tooltip>
|
||||
<template #title>删除</template>
|
||||
<a-button
|
||||
style="padding: 0"
|
||||
type="link"
|
||||
>
|
||||
<close-outlined />
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
</a-popconfirm>
|
||||
</div>
|
||||
</template>
|
||||
</a-tree>
|
||||
</div>
|
||||
<div class="content">
|
||||
<j-input
|
||||
v-model:value="transfer.data.leftSearchValue"
|
||||
style="margin-bottom: 8px"
|
||||
placeholder="请输入菜单名称"
|
||||
>
|
||||
<template #prefix>
|
||||
<search-outlined style="color: #b3b3b3" />
|
||||
</template>
|
||||
</j-input>
|
||||
<j-tree
|
||||
autoExpandParent
|
||||
:tree-data="transfer.data.leftTreeData"
|
||||
draggable
|
||||
>
|
||||
<template #title="row">
|
||||
<div>{{ row.name }}</div>
|
||||
</template>
|
||||
</j-tree>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dialogs">
|
||||
<a-modal
|
||||
v-model:visible="dialogShow"
|
||||
title="一键拷贝"
|
||||
@ok="transfer.copy"
|
||||
cancelText="取消"
|
||||
okText="确认"
|
||||
>
|
||||
<p>源数据将会覆盖当前的系统菜单数据,确定要一键拷贝吗?</p>
|
||||
</a-modal>
|
||||
<div class="center">
|
||||
<j-button>请拖动至右侧</j-button>
|
||||
</div>
|
||||
</a-card>
|
||||
<!-- 右侧树 -->
|
||||
<div class="basic-tree right">
|
||||
<div class="title">
|
||||
<div class="title-label">
|
||||
<span>系统菜单</span>
|
||||
<j-tooltip>
|
||||
<template #title
|
||||
>菜单管理页面配置的菜单数据</template
|
||||
>
|
||||
<question-circle-outlined />
|
||||
</j-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content">
|
||||
<j-input
|
||||
v-model:value="transfer.data.rightSearchValue"
|
||||
style="margin-bottom: 8px"
|
||||
placeholder="请输入菜单名称"
|
||||
>
|
||||
<template #prefix>
|
||||
<search-outlined style="color: #b3b3b3" />
|
||||
</template>
|
||||
</j-input>
|
||||
<j-tree
|
||||
draggable
|
||||
autoExpandParent
|
||||
:tree-data="transfer.data.rightTreeData"
|
||||
@drop="transfer.onRightDrop"
|
||||
>
|
||||
<template #title="row">
|
||||
<div
|
||||
style="
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
"
|
||||
>
|
||||
<span>{{ row.name }}</span>
|
||||
<j-popconfirm
|
||||
title="确认删除?"
|
||||
ok-text="确定"
|
||||
cancel-text="取消"
|
||||
@confirm="transfer.removeItem(row)"
|
||||
>
|
||||
<j-tooltip>
|
||||
<template #title>删除</template>
|
||||
<j-button
|
||||
style="padding: 0"
|
||||
type="link"
|
||||
>
|
||||
<close-outlined />
|
||||
</j-button>
|
||||
</j-tooltip>
|
||||
</j-popconfirm>
|
||||
</div>
|
||||
</template>
|
||||
</j-tree>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<j-button type="primary" style="margin-top: 24px;">保存</j-button>
|
||||
|
||||
<div class="dialogs">
|
||||
<j-modal
|
||||
v-model:visible="dialogShow"
|
||||
title="一键拷贝"
|
||||
@ok="transfer.copy"
|
||||
cancelText="取消"
|
||||
okText="确认"
|
||||
>
|
||||
<p>源数据将会覆盖当前的系统菜单数据,确定要一键拷贝吗?</p>
|
||||
</j-modal>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -282,6 +282,9 @@ const dialogShow = ref<boolean>(false);
|
|||
<style lang="less" scoped>
|
||||
.setting-container {
|
||||
padding: 24px;
|
||||
margin: 24px;
|
||||
background-color: #fff;
|
||||
|
||||
.top {
|
||||
font-size: inherit;
|
||||
margin-bottom: 24px;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<a-modal
|
||||
<j-modal
|
||||
visible
|
||||
:title="props.mode"
|
||||
width="660px"
|
||||
|
@ -8,8 +8,8 @@
|
|||
:maskClosable="false"
|
||||
:confirmLoading="loading"
|
||||
>
|
||||
<a-form :model="form.data" class="basic-form" ref="formRef">
|
||||
<a-form-item
|
||||
<j-form :model="form.data" class="basic-form" ref="formRef">
|
||||
<j-form-item
|
||||
label="编码"
|
||||
name="id"
|
||||
:rules="[
|
||||
|
@ -17,12 +17,12 @@
|
|||
{ max: 64, message: '最多可输入64个字符' },
|
||||
]"
|
||||
>
|
||||
<a-input
|
||||
<j-input
|
||||
v-model:value="form.data.id"
|
||||
:disabled="props.mode !== '新增'"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
</j-form-item>
|
||||
<j-form-item
|
||||
label="名称"
|
||||
name="name"
|
||||
:rules="[
|
||||
|
@ -30,12 +30,12 @@
|
|||
{ max: 64, message: '最多可输入64个字符' },
|
||||
]"
|
||||
>
|
||||
<a-input
|
||||
<j-input
|
||||
v-model:value="form.data.name"
|
||||
:disabled="props.mode === '查看'"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
</j-form-item>
|
||||
<j-form-item
|
||||
label="权限"
|
||||
name="permissions"
|
||||
:rules="[
|
||||
|
@ -53,17 +53,17 @@
|
|||
:disabled="props.mode === '查看'"
|
||||
:key="form.data.id || ''"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="说明" name="describe">
|
||||
<a-textarea
|
||||
</j-form-item>
|
||||
<j-form-item label="说明" name="describe">
|
||||
<j-textarea
|
||||
v-model:value="form.data.describe"
|
||||
:rows="4"
|
||||
placeholder="请输入说明"
|
||||
:disabled="props.mode === '查看'"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</j-form-item>
|
||||
</j-form>
|
||||
</j-modal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="permission-choose-container">
|
||||
<a-input
|
||||
<j-input
|
||||
v-model:value="searchValue"
|
||||
style="width: 300px"
|
||||
allowClear
|
||||
|
@ -10,35 +10,35 @@
|
|||
/>
|
||||
|
||||
<div class="permission-table">
|
||||
<a-row :gutter="24" class="table-head">
|
||||
<a-col :span="props.firstWidth">权限名称</a-col
|
||||
><a-col :span="24 - props.firstWidth">权限操作</a-col>
|
||||
</a-row>
|
||||
<j-row :gutter="24" class="table-head">
|
||||
<j-col :span="props.firstWidth">权限名称</j-col
|
||||
><j-col :span="24 - props.firstWidth">权限操作</j-col>
|
||||
</j-row>
|
||||
<div class="table-body" :style="{ 'max-height': props.maxHeight }">
|
||||
<a-row
|
||||
<j-row
|
||||
:gutter="24"
|
||||
class="row"
|
||||
v-for="rowItem in permission.list"
|
||||
>
|
||||
<a-col :span="props.firstWidth" class="item-name">
|
||||
<a-checkbox
|
||||
<j-col :span="props.firstWidth" class="item-name">
|
||||
<j-checkbox
|
||||
v-model:checked="rowItem.checkAll"
|
||||
:indeterminate="rowItem.indeterminate"
|
||||
@change="() => permission.selectAllOpions(rowItem)"
|
||||
:disabled="props.disabled"
|
||||
>
|
||||
{{ rowItem.name }}
|
||||
</a-checkbox>
|
||||
</a-col>
|
||||
<a-col :span="24 - props.firstWidth">
|
||||
<a-checkbox-group
|
||||
</j-checkbox>
|
||||
</j-col>
|
||||
<j-col :span="24 - props.firstWidth">
|
||||
<j-checkbox-group
|
||||
v-model:value="rowItem.checkedList"
|
||||
:options="rowItem.options"
|
||||
@change="((checkValue:string[])=>permission.selectOption(rowItem, checkValue))"
|
||||
:disabled="props.disabled"
|
||||
/>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
<template>
|
||||
<a-modal
|
||||
v-model:visible="dialog.visible"
|
||||
:title="dialog.title"
|
||||
<j-modal
|
||||
visible
|
||||
:title="dialogTitle"
|
||||
width="1000px"
|
||||
@ok="dialog.handleOk"
|
||||
@ok="confirm"
|
||||
@cancel="emits('update:visible', false)"
|
||||
:confirmLoading="loading"
|
||||
class="edit-dialog-container"
|
||||
>
|
||||
<a-form ref="formRef" :model="form.data" layout="vertical">
|
||||
<a-form-item
|
||||
<j-form ref="formRef" :model="form.data" layout="vertical">
|
||||
<j-form-item
|
||||
name="id"
|
||||
:rules="[
|
||||
{ required: true, message: '请输入标识(ID)' },
|
||||
|
@ -18,34 +20,37 @@
|
|||
<template #label>
|
||||
<span>标识</span>
|
||||
<span class="required-icon">*</span>
|
||||
<a-tooltip placement="top">
|
||||
<j-tooltip placement="top">
|
||||
<template #title>
|
||||
<span>标识ID需与代码中的标识ID一致</span>
|
||||
</template>
|
||||
<question-circle-outlined style="color: #00000073" />
|
||||
</a-tooltip>
|
||||
<AIcon
|
||||
type="QuestionCircleOutlined"
|
||||
style="color: #00000073"
|
||||
/>
|
||||
</j-tooltip>
|
||||
</template>
|
||||
<a-input
|
||||
<j-input
|
||||
v-model:value="form.data.id"
|
||||
placeholder="请输入标识(ID)"
|
||||
:maxlength="64"
|
||||
:disabled="dialog.title === '编辑'"
|
||||
:disabled="dialogTitle === '编辑'"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
</j-form-item>
|
||||
<j-form-item
|
||||
name="name"
|
||||
label="名称"
|
||||
:rules="[{ required: true, message: '请输入名称' }]"
|
||||
>
|
||||
<a-input
|
||||
<j-input
|
||||
v-model:value="form.data.name"
|
||||
placeholder="请输入名称"
|
||||
:maxlength="64"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</j-form-item>
|
||||
</j-form>
|
||||
|
||||
<a-table
|
||||
<j-table
|
||||
:columns="table.columns"
|
||||
:data-source="actionTableData"
|
||||
:pagination="false"
|
||||
|
@ -61,54 +66,41 @@
|
|||
<template
|
||||
v-else-if="column.key !== 'index' && column.key !== 'act'"
|
||||
>
|
||||
<a-input v-model:value="record[column.key]" />
|
||||
<j-input v-model:value="record[column.key]" />
|
||||
</template>
|
||||
<template v-else-if="column.key === 'act'">
|
||||
<a-button
|
||||
<j-button
|
||||
class="delete-btn"
|
||||
style="padding: 0"
|
||||
type="link"
|
||||
@click="table.clickRemove(index)"
|
||||
>
|
||||
<delete-outlined />
|
||||
</a-button>
|
||||
<AIcon type="DeleteOutlined" />
|
||||
</j-button>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</j-table>
|
||||
<div class="pager" v-show="pager.total > pager.pageSize">
|
||||
<a-select v-model:value="pager.current" style="width: 60px">
|
||||
<a-select-option v-for="(val, i) in pageArr" :value="i + 1">{{
|
||||
<j-select v-model:value="pager.current" style="width: 60px">
|
||||
<j-select-option v-for="(val, i) in pageArr" :value="i + 1">{{
|
||||
i + 1
|
||||
}}</a-select-option>
|
||||
</a-select>
|
||||
<a-pagination
|
||||
}}</j-select-option>
|
||||
</j-select>
|
||||
<j-pagination
|
||||
v-model:current="pager.current"
|
||||
:page-size="pager.pageSize"
|
||||
:total="pager.total"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<a-button type="dashed" style="width: 100%" @click="table.clickAdd">
|
||||
<plus-outlined /> 添加
|
||||
</a-button>
|
||||
|
||||
<template #footer>
|
||||
<a-button key="back" @click="dialog.visible = false">取消</a-button>
|
||||
<a-button
|
||||
key="submit"
|
||||
type="primary"
|
||||
:loading="form.loading"
|
||||
@click="dialog.handleOk"
|
||||
>确定</a-button
|
||||
>
|
||||
</template>
|
||||
</a-modal>
|
||||
<j-button type="dashed" style="width: 100%" @click="table.clickAdd">
|
||||
<AIcon type="PlusOutlined" /> 添加
|
||||
</j-button>
|
||||
</j-modal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { FormInstance, message } from 'ant-design-vue';
|
||||
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons-vue';
|
||||
import { QuestionCircleOutlined } from '@ant-design/icons-vue';
|
||||
import { Rule } from 'ant-design-vue/es/form';
|
||||
|
||||
import {
|
||||
|
@ -122,42 +114,41 @@ const defaultAction = [
|
|||
{ action: 'save', name: '保存', describe: '保存' },
|
||||
{ action: 'delete', name: '删除', describe: '删除' },
|
||||
];
|
||||
const emits = defineEmits(['refresh']);
|
||||
// 弹窗相关
|
||||
const dialog = reactive({
|
||||
title: '',
|
||||
visible: false,
|
||||
handleOk: () => {
|
||||
formRef.value?.validate().then(() => {
|
||||
form.submit();
|
||||
});
|
||||
},
|
||||
// 控制弹窗的打开与关闭
|
||||
changeVisible: (status: boolean, defaultForm: any = {}) => {
|
||||
dialog.title = defaultForm.id ? '编辑' : '新增';
|
||||
form.data = { name: '', ...defaultForm };
|
||||
table.data = defaultForm.id ? defaultForm.actions : [...defaultAction];
|
||||
pager.total = table.data.length;
|
||||
pager.current = 1;
|
||||
dialog.visible = status;
|
||||
nextTick(() => {
|
||||
formRef.value?.clearValidate();
|
||||
});
|
||||
},
|
||||
});
|
||||
const emits = defineEmits(['refresh', 'update:visible']);
|
||||
const props = defineProps<{
|
||||
data: any;
|
||||
visible: boolean;
|
||||
}>();
|
||||
|
||||
const loading = ref(false);
|
||||
const dialogTitle = computed(() => (props.data.id ? '编辑' : '新增'));
|
||||
const confirm = () => {
|
||||
loading.value = true;
|
||||
formRef.value
|
||||
?.validate()
|
||||
.then(() => form.submit())
|
||||
.then((resp) => {
|
||||
if (resp.status === 200) {
|
||||
message.success('操作成功');
|
||||
emits('refresh');
|
||||
emits('update:visible', false);
|
||||
}
|
||||
})
|
||||
.finally(() => (loading.value = false));
|
||||
};
|
||||
// 表单相关
|
||||
const formRef = ref<FormInstance>();
|
||||
const form = reactive({
|
||||
loading: false,
|
||||
data: {
|
||||
name: '',
|
||||
id: '',
|
||||
...props.data,
|
||||
},
|
||||
rules: {
|
||||
// 校验标识是否可用
|
||||
idCheck: (_rule: Rule, id: string, cb: Function) => {
|
||||
if (!id) return cb('请输入标识(ID)');
|
||||
if (dialog.title === '编辑') return cb();
|
||||
if (props.data.id) return cb();
|
||||
else if (!id) return cb('请输入标识(ID)');
|
||||
checkId_api({ id })
|
||||
.then((resp: any) => {
|
||||
if (resp.status === 200 && !resp.result.passed)
|
||||
|
@ -165,16 +156,6 @@ const form = reactive({
|
|||
else cb();
|
||||
})
|
||||
.catch(() => cb('验证失败'));
|
||||
|
||||
// return new Promise((resolve) => {
|
||||
// checkId_api({ id })
|
||||
// .then((resp: any) => {
|
||||
// if (resp.status === 200 && !resp.result.passed)
|
||||
// resolve(resp.result.reason);
|
||||
// else resolve('');
|
||||
// })
|
||||
// .catch(() => resolve('验证失败'));
|
||||
// });
|
||||
},
|
||||
},
|
||||
submit: () => {
|
||||
|
@ -182,16 +163,9 @@ const form = reactive({
|
|||
...form.data,
|
||||
actions: table.data.filter((item: any) => item.action && item.name),
|
||||
};
|
||||
const api =
|
||||
dialog.title === '编辑' ? editPermission_api : addPermission_api;
|
||||
const api = props.data.id ? editPermission_api : addPermission_api;
|
||||
|
||||
api(params).then((resp) => {
|
||||
if (resp.status === 200) {
|
||||
message.error('操作成功');
|
||||
emits('refresh');
|
||||
dialog.visible = false;
|
||||
}
|
||||
});
|
||||
return api(params);
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -201,26 +175,26 @@ const table = reactive({
|
|||
title: '-',
|
||||
dataIndex: 'index',
|
||||
key: 'index',
|
||||
width:80,
|
||||
align:'center'
|
||||
width: 80,
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '操作类型',
|
||||
dataIndex: 'action',
|
||||
key: 'action',
|
||||
width: 220
|
||||
width: 220,
|
||||
},
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
width: 220
|
||||
width: 220,
|
||||
},
|
||||
{
|
||||
title: '说明',
|
||||
dataIndex: 'describe',
|
||||
key: 'describe',
|
||||
width: 220
|
||||
width: 220,
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
|
@ -228,7 +202,7 @@ const table = reactive({
|
|||
key: 'act',
|
||||
},
|
||||
],
|
||||
data: <any>[],
|
||||
data: props.data.id ? props.data.actions : [...defaultAction],
|
||||
clickRemove: (index: number) => {
|
||||
pager.total -= 1;
|
||||
table.data.splice(index, 1);
|
||||
|
@ -251,7 +225,7 @@ const table = reactive({
|
|||
const pager = reactive({
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
total: table.data.length,
|
||||
});
|
||||
const pageArr = computed(() => {
|
||||
const maxPageNum = Math.ceil(pager.total / pager.pageSize);
|
||||
|
@ -267,11 +241,6 @@ const actionTableData = computed(() => {
|
|||
|
||||
return table.data.slice(startIndex, endIndex);
|
||||
});
|
||||
|
||||
// 将打开弹窗的操作暴露给父组件
|
||||
defineExpose({
|
||||
openDialog: dialog.changeVisible,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
@ -302,7 +271,7 @@ defineExpose({
|
|||
}
|
||||
.delete-btn {
|
||||
color: #000000d9;
|
||||
&:hover{
|
||||
&:hover {
|
||||
color: #415ed1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
<template>
|
||||
<span class="status-label-container">
|
||||
<i
|
||||
class="circle"
|
||||
:style="{ background: props.statusValue ? '#52c41a' : '#ff4d4f' }"
|
||||
></i>
|
||||
<span>{{ props.statusValue ? '启用' : '禁用' }}</span>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const props = defineProps<{
|
||||
statusValue: number;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.status-label-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.circle {
|
||||
display: inline-block;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,14 +1,17 @@
|
|||
<template>
|
||||
<page-container>
|
||||
<div class="permission-container">
|
||||
<Search :columns="query.columns" @search="query.search" />
|
||||
<j-advanced-search
|
||||
:columns="columns"
|
||||
@search="(params:any) => (queryParams = params)"
|
||||
/>
|
||||
|
||||
<j-pro-table
|
||||
ref="tableRef"
|
||||
:columns="table.columns"
|
||||
:columns="columns"
|
||||
:request="getPermission_api"
|
||||
model="TABLE"
|
||||
:params="query.params"
|
||||
:params="queryParams"
|
||||
:defaultParams="{ sorts: [{ name: 'id', order: 'asc' }] }"
|
||||
>
|
||||
<template #headerTitle>
|
||||
|
@ -19,12 +22,12 @@
|
|||
>
|
||||
<AIcon type="PlusOutlined" />新增
|
||||
</PermissionButton>
|
||||
<a-dropdown trigger="hover">
|
||||
<a-button>批量操作</a-button>
|
||||
<j-dropdown trigger="hover">
|
||||
<j-button>批量操作</j-button>
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item>
|
||||
<a-upload
|
||||
<j-menu>
|
||||
<j-menu-item>
|
||||
<j-upload
|
||||
name="file"
|
||||
action="#"
|
||||
accept=".json"
|
||||
|
@ -41,9 +44,9 @@
|
|||
>
|
||||
导入
|
||||
</PermissionButton>
|
||||
</a-upload>
|
||||
</a-menu-item>
|
||||
<a-menu-item>
|
||||
</j-upload>
|
||||
</j-menu-item>
|
||||
<j-menu-item>
|
||||
<PermissionButton
|
||||
:uhasPermission="`${permission}:export`"
|
||||
:popConfirm="{
|
||||
|
@ -54,16 +57,23 @@
|
|||
>
|
||||
导出
|
||||
</PermissionButton>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</j-menu-item>
|
||||
</j-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
</j-dropdown>
|
||||
</template>
|
||||
<template #status="slotProps">
|
||||
<StatusLabel :status-value="slotProps.status" />
|
||||
<BadgeStatus
|
||||
:status="slotProps.status"
|
||||
:text="slotProps.status ? '启用' : '禁用'"
|
||||
:statusNames="{
|
||||
1: 'success',
|
||||
0: 'error',
|
||||
}"
|
||||
></BadgeStatus>
|
||||
</template>
|
||||
<template #action="slotProps">
|
||||
<a-space :size="16">
|
||||
<j-space :size="16">
|
||||
<PermissionButton
|
||||
:uhasPermission="`${permission}:update`"
|
||||
type="link"
|
||||
|
@ -113,13 +123,16 @@
|
|||
>
|
||||
<AIcon type="DeleteOutlined" />
|
||||
</PermissionButton>
|
||||
</a-space>
|
||||
</j-space>
|
||||
</template>
|
||||
</j-pro-table>
|
||||
|
||||
<div class="dialogs">
|
||||
<EditDialog ref="editDialogRef" @refresh="table.refresh" />
|
||||
</div>
|
||||
<EditDialog
|
||||
v-if="dialog.visible"
|
||||
v-model:visible="dialog.visible"
|
||||
:data="dialog.selectItem"
|
||||
@refresh="table.refresh"
|
||||
/>
|
||||
</div>
|
||||
</page-container>
|
||||
</template>
|
||||
|
@ -127,7 +140,6 @@
|
|||
<script setup lang="ts">
|
||||
import PermissionButton from '@/components/PermissionButton/index.vue';
|
||||
import EditDialog from './components/EditDialog.vue';
|
||||
import StatusLabel from './components/StatusLabel.vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import {
|
||||
getPermission_api,
|
||||
|
@ -141,87 +153,63 @@ import { usePermissionStore } from '@/store/permission';
|
|||
const permission = 'system/Permission';
|
||||
const hasPermission = usePermissionStore().hasPermission;
|
||||
|
||||
const editDialogRef = ref(); // 新增弹窗实例
|
||||
const tableRef = ref<Record<string, any>>({}); // 表格实例
|
||||
// 筛选
|
||||
const query = reactive({
|
||||
columns: [
|
||||
{
|
||||
title: '标识',
|
||||
dataIndex: 'id',
|
||||
key: 'id',
|
||||
ellipsis: true,
|
||||
fixed: 'left',
|
||||
search: {
|
||||
type: 'string',
|
||||
},
|
||||
const columns = [
|
||||
{
|
||||
title: '标识',
|
||||
dataIndex: 'id',
|
||||
key: 'id',
|
||||
ellipsis: true,
|
||||
fixed: 'left',
|
||||
search: {
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
ellipsis: true,
|
||||
search: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
ellipsis: true,
|
||||
search: {
|
||||
rename: 'status',
|
||||
type: 'select',
|
||||
options: [
|
||||
{
|
||||
label: '启用',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: '禁用',
|
||||
value: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
params: {},
|
||||
search: (params: object) => {
|
||||
query.params = params;
|
||||
},
|
||||
});
|
||||
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
ellipsis: true,
|
||||
search: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
ellipsis: true,
|
||||
search: {
|
||||
type: 'select',
|
||||
options: [
|
||||
{
|
||||
label: '启用',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: '禁用',
|
||||
value: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
scopedSlots: true,
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
key: 'action',
|
||||
width: '200px',
|
||||
fixed: 'right',
|
||||
scopedSlots: true,
|
||||
},
|
||||
];
|
||||
const queryParams = ref({});
|
||||
// 表格
|
||||
const table = reactive({
|
||||
columns: [
|
||||
{
|
||||
title: '标识',
|
||||
dataIndex: 'id',
|
||||
key: 'id',
|
||||
},
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
scopedSlots: true,
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
key: 'action',
|
||||
scopedSlots: true,
|
||||
},
|
||||
],
|
||||
tableData: [],
|
||||
const tableRef = ref<Record<string, any>>({}); // 表格实例
|
||||
const table = {
|
||||
// 打开编辑弹窗
|
||||
openDialog: (row: object | undefined = {}) => {
|
||||
editDialogRef.value.openDialog(true, row);
|
||||
dialog.selectItem = { ...row };
|
||||
dialog.visible = true;
|
||||
},
|
||||
// 导入数据
|
||||
clickImport: (file: File) => {
|
||||
|
@ -248,7 +236,7 @@ const table = reactive({
|
|||
clickExport: () => {
|
||||
const params = {
|
||||
paging: false,
|
||||
...query.params,
|
||||
...queryParams,
|
||||
};
|
||||
exportPermission_api(params).then((resp) => {
|
||||
if (resp.status === 200) {
|
||||
|
@ -283,12 +271,16 @@ const table = reactive({
|
|||
refresh: () => {
|
||||
tableRef.value.reload();
|
||||
},
|
||||
};
|
||||
|
||||
const dialog = reactive({
|
||||
selectItem: {},
|
||||
visible: false,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.permission-container {
|
||||
|
||||
.ant-dropdown-trigger {
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
<template>
|
||||
<a-modal
|
||||
v-model:visible="dialog.visible"
|
||||
:title="dialog.title"
|
||||
<j-modal
|
||||
visible
|
||||
:title="dialogTitle"
|
||||
:maskClosable="false"
|
||||
width="675px"
|
||||
@ok="dialog.handleOk"
|
||||
@ok="confirm"
|
||||
@cancel="emits('update:visible', false)"
|
||||
:confirmLoading="loading"
|
||||
class="edit-dialog-container"
|
||||
>
|
||||
<a-form ref="formRef" :model="form.data" layout="vertical">
|
||||
<a-form-item
|
||||
<j-form ref="formRef" :model="form.data" layout="vertical">
|
||||
<j-form-item
|
||||
label="名称"
|
||||
name="name"
|
||||
:rules="[
|
||||
|
@ -16,13 +18,13 @@
|
|||
{ max: 64, message: '最多可输入64个字符' },
|
||||
]"
|
||||
>
|
||||
<a-input
|
||||
<j-input
|
||||
v-model:value="form.data.name"
|
||||
placeholder="请输入名称"
|
||||
:maxlength="64"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
</j-form-item>
|
||||
<j-form-item
|
||||
name="relation"
|
||||
label="标识"
|
||||
:rules="[
|
||||
|
@ -31,76 +33,66 @@
|
|||
{ validator: form.rules.checkRelation, trigger: 'change' },
|
||||
]"
|
||||
>
|
||||
<a-input
|
||||
<j-input
|
||||
v-model:value="form.data.relation"
|
||||
placeholder="请输入标识"
|
||||
:maxlength="64"
|
||||
:disabled="!!form.data.id"
|
||||
/>
|
||||
</a-form-item>
|
||||
</j-form-item>
|
||||
|
||||
<a-row :gutter="24">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
<j-row :gutter="24">
|
||||
<j-col :span="12">
|
||||
<j-form-item
|
||||
name="objectType"
|
||||
label="关联方"
|
||||
:rules="[{ required: true, message: '请选择关联方' }]"
|
||||
>
|
||||
<a-select
|
||||
<j-select
|
||||
v-model:value="form.data.objectType"
|
||||
:disabled="!!form.data.id"
|
||||
>
|
||||
<a-select-option
|
||||
<j-select-option
|
||||
v-for="item in form.objectList"
|
||||
:value="item.id"
|
||||
>{{ item.name }}</a-select-option
|
||||
>{{ item.name }}</j-select-option
|
||||
>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
</j-select>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col :span="12">
|
||||
<j-form-item
|
||||
name="targetType"
|
||||
label="标识"
|
||||
label="被关联方"
|
||||
:rules="[{ required: true, message: '请选择被关联方' }]"
|
||||
>
|
||||
<a-select
|
||||
<j-select
|
||||
v-model:value="form.data.targetType"
|
||||
:disabled="!!form.data.id"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="item in form.targetList"
|
||||
<j-select-option
|
||||
v-for="item in targetList"
|
||||
:value="item.id"
|
||||
>{{ item.name }}</a-select-option
|
||||
>{{ item.name }}</j-select-option
|
||||
>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-form-item
|
||||
</j-select>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
</j-row>
|
||||
<j-form-item
|
||||
name="description"
|
||||
label="说明"
|
||||
:rules="[{ max: 200, message: '最多可输入200个字符' }]"
|
||||
>
|
||||
<a-textarea
|
||||
<j-textarea
|
||||
v-model:value="form.data.description"
|
||||
placeholder="请输入说明"
|
||||
show-count
|
||||
:maxlength="200"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<template #footer>
|
||||
<a-button key="back" @click="dialog.visible = false">取消</a-button>
|
||||
<a-button
|
||||
key="submit"
|
||||
type="primary"
|
||||
:loading="form.loading"
|
||||
@click="dialog.handleOk"
|
||||
>确定</a-button
|
||||
>
|
||||
</template>
|
||||
</a-modal>
|
||||
</j-form-item>
|
||||
</j-form>
|
||||
</j-modal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
@ -112,53 +104,45 @@ import {
|
|||
addRelation_api,
|
||||
editRelation_api,
|
||||
} from '@/api/system/relationship';
|
||||
import { dictItemType } from '../../DataSource/typing';
|
||||
|
||||
const emits = defineEmits(['refresh']);
|
||||
const emits = defineEmits(['refresh', 'update:visible']);
|
||||
const props = defineProps<{
|
||||
visible: boolean;
|
||||
data: formType;
|
||||
}>();
|
||||
// 弹窗相关
|
||||
const dialog = reactive({
|
||||
title: '',
|
||||
visible: false,
|
||||
handleOk: () => {
|
||||
formRef.value?.validate().then(() => {
|
||||
form.submit();
|
||||
});
|
||||
},
|
||||
// 控制弹窗的打开与关闭
|
||||
changeVisible: (status: boolean, defaultForm: formType) => {
|
||||
dialog.title = defaultForm.id ? '编辑' : '新增';
|
||||
form.data = { ...defaultForm };
|
||||
dialog.visible = status;
|
||||
nextTick(() => {
|
||||
formRef.value?.clearValidate();
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
// 表单相关
|
||||
const initForm: formType = {
|
||||
name: '',
|
||||
relation: '',
|
||||
objectType: '',
|
||||
targetType: '',
|
||||
description: '',
|
||||
const loading = ref(false);
|
||||
const dialogTitle = computed(() => (props.data.id ? '编辑' : '新增'));
|
||||
const confirm = () => {
|
||||
loading.value = true;
|
||||
formRef.value
|
||||
?.validate()
|
||||
.then(() => form.submit())
|
||||
.then((resp: any) => {
|
||||
if (resp.status === 200) {
|
||||
message.success('操作成功');
|
||||
emits('refresh');
|
||||
emits('update:visible', false);
|
||||
}
|
||||
})
|
||||
.finally(() => (loading.value = false));
|
||||
};
|
||||
|
||||
const formRef = ref<FormInstance>();
|
||||
const form = reactive({
|
||||
loading: false,
|
||||
data: {} as formType,
|
||||
data: props.data,
|
||||
rules: {
|
||||
checkRelation: (_rule: Rule, value: string): any => {
|
||||
if (!value) return '';
|
||||
const reg = new RegExp('^[0-9a-zA-Z_\\\\-]+$');
|
||||
if (reg.test(value)) return Promise.resolve();
|
||||
else
|
||||
return Promise.reject(
|
||||
'标识只能由数字、字母、下划线、中划线组成',
|
||||
);
|
||||
|
||||
return reg.test(value)
|
||||
? Promise.resolve()
|
||||
: Promise.reject('标识只能由数字、字母、下划线、中划线组成');
|
||||
},
|
||||
},
|
||||
objectList: [] as any[],
|
||||
targetList: [] as any[],
|
||||
|
||||
getObjectList: () => {
|
||||
getObjectList_api().then((resp: any) => {
|
||||
|
@ -166,43 +150,23 @@ const form = reactive({
|
|||
});
|
||||
},
|
||||
submit: () => {
|
||||
formRef.value?.validate().then(() => {
|
||||
const params = {
|
||||
...form.data,
|
||||
objectTypeName: form.objectList.find(
|
||||
(item) => item.id === form.data.objectType,
|
||||
).name,
|
||||
targetTypeName: form.targetList.find(
|
||||
(item) => item.id === form.data.targetType,
|
||||
).name,
|
||||
};
|
||||
const api =
|
||||
dialog.title === '编辑'
|
||||
? editRelation_api
|
||||
: addRelation_api;
|
||||
api(params).then((resp: any) => {
|
||||
if (resp.status === 200) {
|
||||
message.success('操作成功');
|
||||
emits('refresh');
|
||||
dialog.visible = false;
|
||||
}
|
||||
});
|
||||
});
|
||||
const params = {
|
||||
...form.data,
|
||||
objectTypeName: form.objectList.find(
|
||||
(item) => item.id === form.data.objectType,
|
||||
).name,
|
||||
targetTypeName: targetList.value.find(
|
||||
(item: dictItemType) => item.id === form.data.targetType,
|
||||
)?.name,
|
||||
};
|
||||
const api = props.data.id ? editRelation_api : addRelation_api;
|
||||
return api(params);
|
||||
},
|
||||
});
|
||||
form.getObjectList();
|
||||
|
||||
watch(
|
||||
() => form.data.objectType,
|
||||
(n) => {
|
||||
form.targetList = n === 'device' ? [{ id: 'user', name: '用户' }] : [];
|
||||
},
|
||||
const targetList = computed(() =>
|
||||
form.data.objectType === 'device' ? [{ id: 'user', name: '用户' }] : [],
|
||||
);
|
||||
|
||||
// 将打开弹窗的操作暴露给父组件
|
||||
defineExpose({
|
||||
openDialog: dialog.changeVisible,
|
||||
});
|
||||
form.getObjectList();
|
||||
|
||||
type formType = {
|
||||
name: string;
|
||||
|
@ -213,5 +177,3 @@ type formType = {
|
|||
id?: string;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
<template>
|
||||
<page-container>
|
||||
<div class="relationship-container">
|
||||
<Search :columns="query.columns" @search="query.search" />
|
||||
<j-advanced-search
|
||||
:columns="columns"
|
||||
@search="(params:any)=>queryParams = {...params}"
|
||||
/>
|
||||
|
||||
<j-pro-table
|
||||
ref="tableRef"
|
||||
:columns="table.columns"
|
||||
:columns="columns"
|
||||
:request="getRelationshipList_api"
|
||||
model="TABLE"
|
||||
:params="query.params.value"
|
||||
:params="queryParams"
|
||||
:defaultParams="{
|
||||
sorts: [{ name: 'createTime', order: 'desc' }],
|
||||
}"
|
||||
|
@ -23,7 +26,7 @@
|
|||
</PermissionButton>
|
||||
</template>
|
||||
<template #action="slotProps">
|
||||
<a-space :size="16">
|
||||
<j-space :size="16">
|
||||
<PermissionButton
|
||||
:uhasPermission="`${permission}:update`"
|
||||
type="link"
|
||||
|
@ -47,11 +50,16 @@
|
|||
>
|
||||
<AIcon type="DeleteOutlined" />
|
||||
</PermissionButton>
|
||||
</a-space>
|
||||
</j-space>
|
||||
</template>
|
||||
</j-pro-table>
|
||||
|
||||
<EditDialog ref="editDialogRef" @refresh="table.refresh" />
|
||||
<EditDialog
|
||||
v-if="dialog.visible"
|
||||
v-model:visible="dialog.visible"
|
||||
:data="dialog.selectRow"
|
||||
@refresh="table.refresh"
|
||||
/>
|
||||
</div>
|
||||
</page-container>
|
||||
</template>
|
||||
|
@ -67,105 +75,79 @@ import EditDialog from './components/EditDialog.vue';
|
|||
|
||||
const permission = 'system/Relationship';
|
||||
|
||||
const query = {
|
||||
columns: [
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
ellipsis: true,
|
||||
fixed: 'left',
|
||||
search: {
|
||||
type: 'string',
|
||||
},
|
||||
const columns = [
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
ellipsis: true,
|
||||
fixed: 'left',
|
||||
search: {
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
title: '关联方',
|
||||
dataIndex: 'objectTypeName',
|
||||
key: 'objectTypeName',
|
||||
ellipsis: true,
|
||||
fixed: 'left',
|
||||
search: {
|
||||
type: 'select',
|
||||
options: [
|
||||
{
|
||||
label: '启用',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: '禁用',
|
||||
value: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '被关联方',
|
||||
dataIndex: 'targetType',
|
||||
key: 'targetType',
|
||||
ellipsis: true,
|
||||
fixed: 'left',
|
||||
search: {
|
||||
type: 'select',
|
||||
options: [
|
||||
{
|
||||
label: '用户',
|
||||
value: 'user',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '说明',
|
||||
dataIndex: 'description',
|
||||
key: 'description',
|
||||
ellipsis: true,
|
||||
fixed: 'left',
|
||||
search: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
],
|
||||
params: ref({}),
|
||||
search: (params: object) => {
|
||||
query.params.value = params;
|
||||
},
|
||||
};
|
||||
{
|
||||
title: '关联方',
|
||||
dataIndex: 'objectTypeName',
|
||||
key: 'objectTypeName',
|
||||
ellipsis: true,
|
||||
fixed: 'left',
|
||||
search: {
|
||||
type: 'select',
|
||||
options: [
|
||||
{
|
||||
label: '用户',
|
||||
value: '用户',
|
||||
},
|
||||
{
|
||||
label: '设备',
|
||||
value: '设备',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '被关联方',
|
||||
dataIndex: 'targetTypeName',
|
||||
key: 'targetTypeName',
|
||||
ellipsis: true,
|
||||
fixed: 'left',
|
||||
search: {
|
||||
rename: 'targetType',
|
||||
type: 'select',
|
||||
options: [
|
||||
{
|
||||
label: '用户',
|
||||
value: 'user',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '说明',
|
||||
dataIndex: 'description',
|
||||
key: 'description',
|
||||
ellipsis: true,
|
||||
fixed: 'left',
|
||||
search: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
key: 'action',
|
||||
scopedSlots: true,
|
||||
},
|
||||
];
|
||||
const queryParams = ref({});
|
||||
|
||||
const editDialogRef = ref(); // 新增弹窗实例
|
||||
const tableRef = ref<Record<string, any>>({}); // 表格实例
|
||||
const table = {
|
||||
columns: [
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
},
|
||||
{
|
||||
title: '关联方',
|
||||
dataIndex: 'objectTypeName',
|
||||
key: 'objectTypeName',
|
||||
},
|
||||
{
|
||||
title: '被关联方',
|
||||
dataIndex: 'targetTypeName',
|
||||
key: 'targetTypeName',
|
||||
},
|
||||
{
|
||||
title: '说明',
|
||||
dataIndex: 'description',
|
||||
key: 'description',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
key: 'action',
|
||||
scopedSlots: true,
|
||||
},
|
||||
],
|
||||
// 打开编辑弹窗
|
||||
openDialog: (row: object | undefined = {}) => {
|
||||
editDialogRef.value.openDialog(true, row);
|
||||
dialog.selectRow = { ...row };
|
||||
dialog.visible = true;
|
||||
},
|
||||
// 删除
|
||||
clickDel: (row: any) => {
|
||||
|
@ -181,6 +163,11 @@ const table = {
|
|||
tableRef.value.reload();
|
||||
},
|
||||
};
|
||||
|
||||
const dialog = reactive({
|
||||
selectRow: {} as any,
|
||||
visible: false,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
|
|
@ -10,6 +10,7 @@ import VueSetupExtend from 'vite-plugin-vue-setup-extend'
|
|||
import { createStyleImportPlugin, AndDesignVueResolve } from 'vite-plugin-style-import'
|
||||
import * as path from 'path'
|
||||
import monacoEditorPlugin from 'vite-plugin-monaco-editor';
|
||||
// import { JetlinksVueResolver } from 'jetlinks-ui-components/lib/plugin/resolve'
|
||||
import { JetlinksVueResolver } from './plugin/jetlinks'
|
||||
import copy from 'rollup-plugin-copy';
|
||||
|
||||
|
|
Loading…
Reference in New Issue