feat: 新增字典管理;新增s7;新增默认流程引擎菜单权限
* fix: 优化初始化流程引擎菜单数据权限 * feat: 新增S7 * feat(s7): 数据采集增加S7协议 * fix: s7点位死区校验 * fix: bug#19996 * fix: bug#20211 * fix: bug#20211 * fix: s7点位配置 * fix: s7点位死区校验 * fix: s7点位配置 * fix: 优化流程引擎字段权限 * fix: 优化流程引擎字段权限 * fix: s7按钮权限 * fix: 修复s7点位bug * fix: 修复s7点位bug * fix: s7点位bug修复 * fix: s7点位bug修复 * fix: bug#20382 * fix: bug#19996 * fix: bug#19649 * feat: 导入通道校验 * feat(s7): 数据采集增加S7协议 * fix: s7点位死区校验 * fix: s7点位配置 * fix: 修复s7点位bug * fix: s7点位bug修复 * feat: 新增点位导入功能 * feat: 新增导入功能 * feat: 导入通道校验 * fix: 优化内部应用页面集成 * * fix: 修改导入图标 * fix: 修改导入图标 * bug#20101 * fix: bug#20449 * fix: bug#20488 * fix: bug#20495 * fix: bug#20101 * feat: 集成菜单新增菜单功能 * feat: api新增菜单 * feat: 集成菜单新增菜单功能 * feat: 集成菜单应用新增菜单 * fix: bug#20293 * fix: bug#20430 * fix: s7修改导入图标 * fix: 修改导入图标 * fix: 优化集成页面路径
This commit is contained in:
parent
345be95d1d
commit
feb8dcddb9
|
@ -1,2 +1,3 @@
|
||||||
ENV=develop
|
ENV=develop
|
||||||
VITE_APP_BASE_API=/api
|
VITE_APP_BASE_API=/api
|
||||||
|
VITE_TOKEN_KEY = X-Access-Token
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
ENV=production
|
ENV=production
|
||||||
VITE_APP_BASE_API=/api
|
VITE_APP_BASE_API=/api
|
||||||
|
VITE_TOKEN_KEY = X-Access-Token
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
"event-source-polyfill": "^1.0.31",
|
"event-source-polyfill": "^1.0.31",
|
||||||
"global": "^4.4.0",
|
"global": "^4.4.0",
|
||||||
"jetlinks-store": "^0.0.3",
|
"jetlinks-store": "^0.0.3",
|
||||||
"jetlinks-ui-components": "^1.0.34-4",
|
"jetlinks-ui-components": "^1.0.34-7",
|
||||||
"js-cookie": "^3.0.1",
|
"js-cookie": "^3.0.1",
|
||||||
"jsencrypt": "^3.3.2",
|
"jsencrypt": "^3.3.2",
|
||||||
"less": "^4.1.3",
|
"less": "^4.1.3",
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 4.0 KiB |
Binary file not shown.
After Width: | Height: | Size: 5.8 KiB |
|
@ -8,7 +8,8 @@ export const getList_api = (data: any): any => server.post(`/notifications/_quer
|
||||||
export const changeStatus_api = (type: '_read' | '_unread', data: string[]): any => server.post(`/notifications/${type}`, data)
|
export const changeStatus_api = (type: '_read' | '_unread', data: string[]): any => server.post(`/notifications/${type}`, data)
|
||||||
|
|
||||||
export const changeAllStatus = (type: '_read' | '_unread', data: string[]): any => server.post(`/notifications/${type}/provider`, data)
|
export const changeAllStatus = (type: '_read' | '_unread', data: string[]): any => server.post(`/notifications/${type}/provider`, data)
|
||||||
|
//查看工作流通知详情
|
||||||
|
export const getWorkflowNotice = (data:any) => server.post('/process/runtime/processes/_query/no-paging',data)
|
||||||
|
|
||||||
// 查询告警记录详情
|
// 查询告警记录详情
|
||||||
export const getDetail = (id: string): any => server.get(`/alarm/record/${id}`)
|
export const getDetail = (id: string): any => server.get(`/alarm/record/${id}`)
|
||||||
|
|
|
@ -37,3 +37,5 @@ export const systemVersion = () => server.get<{edition?: string}>('/system/versi
|
||||||
export const queryDashboard = (data: Record<string, any>) => server.post(`/dashboard/_multi`, data)
|
export const queryDashboard = (data: Record<string, any>) => server.post(`/dashboard/_multi`, data)
|
||||||
|
|
||||||
export const fileUpload = (data: any) => server.post('/file/static', data)
|
export const fileUpload = (data: any) => server.post('/file/static', data)
|
||||||
|
|
||||||
|
export const lowCodeUrl = () => server.get('/system/config/low-code')
|
|
@ -54,8 +54,7 @@ export const queryPointNoPaging = (data: any) =>
|
||||||
|
|
||||||
export const scanOpcUAList = (data: any) =>
|
export const scanOpcUAList = (data: any) =>
|
||||||
server.get(
|
server.get(
|
||||||
`/data-collect/opc/channel/${data.id}/nodes?nodeId=${
|
`/data-collect/opc/channel/${data.id}/nodes?nodeId=${data?.nodeId || ''
|
||||||
data?.nodeId || ''
|
|
||||||
}`,
|
}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -64,3 +63,9 @@ export const queryTypeList = () => server.get(`/data-collect/opc/data-types`);
|
||||||
export const getProviders = () => server.get('/data-collect/channel/gateway/codec/providers')
|
export const getProviders = () => server.get('/data-collect/channel/gateway/codec/providers')
|
||||||
|
|
||||||
export const getStates = () => server.get('/dictionary/running-state/items')
|
export const getStates = () => server.get('/dictionary/running-state/items')
|
||||||
|
|
||||||
|
export const getSnapTypes = () => server.get('/s7/client/s7codecs/list')
|
||||||
|
|
||||||
|
export const getArea = () => server.get('/s7/client/s7area/list')
|
||||||
|
|
||||||
|
export const exportTemplate = (provider: string, format: string) =>server.get(`/data-collect/point/${provider}/template.${format}`, {}, {responseType: 'blob'})
|
|
@ -4,7 +4,8 @@ import server from '@/utils/request';
|
||||||
export const updateMenus = (data: any) => server.patch(`/menu/iot/_all`, data)
|
export const updateMenus = (data: any) => server.patch(`/menu/iot/_all`, data)
|
||||||
// 添加角色
|
// 添加角色
|
||||||
export const addRole = (data: any) => server.post(`/role`, data)
|
export const addRole = (data: any) => server.post(`/role`, data)
|
||||||
|
//添加角色分组
|
||||||
|
export const addRoleGroup = (data:any) => server.patch('/role/group',data)
|
||||||
//更新权限菜单
|
//更新权限菜单
|
||||||
export const getRoleMenu = (id: string) => server.get(`/menu/role/${id}/_grant/tree`)
|
export const getRoleMenu = (id: string) => server.get(`/menu/role/${id}/_grant/tree`)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
import server, { request } from '@/utils/request';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询字典列表
|
||||||
|
*/
|
||||||
|
export const getDicList = (data:any) => request.post('/dictionary/_query/no-paging',data)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询字典分页
|
||||||
|
*/
|
||||||
|
export const getDic_page = (data:any) => request.post('/dictionary/_query',data)
|
||||||
|
/**
|
||||||
|
* 查询字典ID是否重复
|
||||||
|
*/
|
||||||
|
export const verifyId = (id:string) => request.post(`/dictionary/_exists`,{where:`id is ${id}`})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存字典
|
||||||
|
*/
|
||||||
|
export const addDictionary = (data:any) => request.patch('/dictionary',data)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除字典
|
||||||
|
*/
|
||||||
|
export const deleteDictionary =(id:string) => request.delete(`/dictionary/${id}`)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询字典项
|
||||||
|
*/
|
||||||
|
export const queryDicItem = (data:any)=>request.post('/dictionary-item/_query',data)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询字典项不分页
|
||||||
|
*/
|
||||||
|
export const queryDicItemNoPage = (data:any) => request.post('/dictionary-item/_query/no-paging',data)
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存字典项
|
||||||
|
*/
|
||||||
|
export const saveDicItem = (data:any) => request.patch('/dictionary-item',data)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除字典项
|
||||||
|
*/
|
||||||
|
export const deleteDicItem = (id:string) => request.delete(`/dictionary-item/${id}`)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验字典项value唯一
|
||||||
|
*/
|
||||||
|
export const verifyValue = (data:any) => request.post('/dictionary-item/_exists',data)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载字典
|
||||||
|
*/
|
||||||
|
export const downDic = (data:any) => request.post('/dictionary/detail/_query',data)
|
|
@ -21,3 +21,5 @@ export const saveMenuInfo_api = (data: object) => server.patch(`/menu`, data);
|
||||||
export const addMenuInfo_api = (data: object) => server.post(`/menu`, data);
|
export const addMenuInfo_api = (data: object) => server.post(`/menu`, data);
|
||||||
// 删除菜单信息
|
// 删除菜单信息
|
||||||
export const delMenuInfo_api = (id: string) => server.remove(`/menu/${id}`);
|
export const delMenuInfo_api = (id: string) => server.remove(`/menu/${id}`);
|
||||||
|
//查询集成菜单
|
||||||
|
export const queryApp = (data:any) => server.post('/application/_query/no-paging',data)
|
|
@ -0,0 +1,22 @@
|
||||||
|
import server from '@/utils/request';
|
||||||
|
|
||||||
|
// 获取全部地区(树结构)
|
||||||
|
export const getRegionTree = (): Promise<any> => server.post(`/area/_all/tree`);
|
||||||
|
|
||||||
|
// 校验名称是否存在
|
||||||
|
export const validateName = (name: string, id?: string): Promise<any> => server.get(`/area/name/_validate?name=${name}${id ? `&id=${id}` : ''}`);
|
||||||
|
|
||||||
|
//校验行政区划代码是否存在
|
||||||
|
export const validateCode = (code: string, id?: string): Promise<any> => server.get(`/area/code/_validate?code=${code}${id ? `&id=${id}` : ''}`);
|
||||||
|
|
||||||
|
// 删除
|
||||||
|
export const delRegion = (id: string): Promise<any> => server.remove(`/area/${id}`);
|
||||||
|
|
||||||
|
// 保存
|
||||||
|
export const saveRegion = (data: any): Promise<any> => server.post(`/area`, data);
|
||||||
|
|
||||||
|
// 更新
|
||||||
|
export const updateRegion = (data: any): Promise<any> => server.patch(`/area`, data);
|
||||||
|
|
||||||
|
// 获取全部内置地区(树结构)
|
||||||
|
export const getBuiltinRegionTree = (data: any): Promise<any> => server.post(`/area/builtin/_all/tree`, data);
|
|
@ -1,3 +1,4 @@
|
||||||
|
import request from '@/utils/request';
|
||||||
import server from '@/utils/request';
|
import server from '@/utils/request';
|
||||||
|
|
||||||
// 获取角色列表
|
// 获取角色列表
|
||||||
|
@ -26,3 +27,9 @@ export const getUserByRole_api = (data: any): Promise<any> => server.post(`/user
|
||||||
export const bindUser_api = (roleId:string, data: string[]): Promise<any> => server.post(`/role/${roleId}/users/_bind`, data);
|
export const bindUser_api = (roleId:string, data: string[]): Promise<any> => server.post(`/role/${roleId}/users/_bind`, data);
|
||||||
// 将用户与角色解绑
|
// 将用户与角色解绑
|
||||||
export const unbindUser_api = (roleId:string, data: string[]): Promise<any> => server.post(`/role/${roleId}/users/_unbind`, data);
|
export const unbindUser_api = (roleId:string, data: string[]): Promise<any> => server.post(`/role/${roleId}/users/_unbind`, data);
|
||||||
|
//查询分组
|
||||||
|
export const queryRoleGroup = (data:any) => request.post('/role/group/_query/no-paging',data)
|
||||||
|
//保存分组
|
||||||
|
export const saveRoleGroup = (data:any) => request.patch('/role/group',data)
|
||||||
|
//删除分组
|
||||||
|
export const deleteRoleGroup = (id:string) => request.remove(`/role/group/${id}`)
|
|
@ -15,8 +15,11 @@ export const validateField_api = (type: 'username' | 'password', name: string) =
|
||||||
|
|
||||||
// 获取角色列表
|
// 获取角色列表
|
||||||
export const getRoleList_api = () => server.get(`/role/_query/no-paging?paging=false`);
|
export const getRoleList_api = () => server.get(`/role/_query/no-paging?paging=false`);
|
||||||
|
|
||||||
|
//获取角色列表
|
||||||
|
export const getRoleList = (data:any) => server.post('/role/group/detail/_query/tree',data)
|
||||||
// 获取组织列表
|
// 获取组织列表
|
||||||
export const getDepartmentList_api = () => server.get(`/organization/_all/tree?paging=false`);
|
export const getDepartmentList_api = (data:any) => server.post(`/organization/_all/tree`,data);
|
||||||
|
|
||||||
// 获取用户信息
|
// 获取用户信息
|
||||||
export const getUser_api = (id: string) => server.get(`/user/detail/${id}`);
|
export const getUser_api = (id: string) => server.get(`/user/detail/${id}`);
|
||||||
|
@ -34,3 +37,5 @@ export const updatePassword_api = (data: { id: string, password: string }) => se
|
||||||
export const changeUserStatus_api = (data: object) => server.patch(`/user`,data);
|
export const changeUserStatus_api = (data: object) => server.patch(`/user`,data);
|
||||||
// 删除用户
|
// 删除用户
|
||||||
export const deleteUser_api = (id: string) => server.remove(`/user/${id}`);
|
export const deleteUser_api = (id: string) => server.remove(`/user/${id}`);
|
||||||
|
// 查询角色不分页
|
||||||
|
export const queryRole_api = (data: any): Promise<any> => server.post(`/role/_query/no-paging`, data)
|
|
@ -22,6 +22,8 @@ import '@vuemap/vue-amap/dist/style.css';
|
||||||
import { getAMapUiPromise } from './utils';
|
import { getAMapUiPromise } from './utils';
|
||||||
import { useSystem } from '@/store/system';
|
import { useSystem } from '@/store/system';
|
||||||
|
|
||||||
|
const emit = defineEmits('init')
|
||||||
|
|
||||||
const system = useSystem();
|
const system = useSystem();
|
||||||
interface AMapProps {
|
interface AMapProps {
|
||||||
style?: CSSProperties;
|
style?: CSSProperties;
|
||||||
|
@ -65,6 +67,7 @@ const initMap = (e: any) => {
|
||||||
if (isOpenUi.value) {
|
if (isOpenUi.value) {
|
||||||
getAMapUI();
|
getAMapUI();
|
||||||
}
|
}
|
||||||
|
emit('init', e)
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,129 @@
|
||||||
|
<template>
|
||||||
|
<j-pro-layout
|
||||||
|
v-model:collapsed="basicLayout.collapsed"
|
||||||
|
v-model:openKeys="basicLayout.openKeys"
|
||||||
|
:breadcrumb="{ routes: breadcrumbs }"
|
||||||
|
:headerHeight='layout.headerHeight'
|
||||||
|
:pure="basicLayout.pure"
|
||||||
|
:selectedKeys="basicLayout.selectedKeys"
|
||||||
|
v-bind="layoutConf"
|
||||||
|
@backClick='routerBack'
|
||||||
|
>
|
||||||
|
<template #breadcrumbRender="slotProps">
|
||||||
|
<a
|
||||||
|
v-if="slotProps.route.index !== 0 && !slotProps.route.isLast"
|
||||||
|
@click='jump(slotProps.route)'
|
||||||
|
>
|
||||||
|
{{ slotProps.route.breadcrumbName }}
|
||||||
|
</a>
|
||||||
|
<span v-else style='cursor: default' >{{ slotProps.route.breadcrumbName }}</span>
|
||||||
|
</template>
|
||||||
|
<template #rightContentRender>
|
||||||
|
<div class="right-content">
|
||||||
|
<AIcon type="QuestionCircleOutlined" @click="toDoc" />
|
||||||
|
<Notice style="margin: 0 24px" />
|
||||||
|
<UserInfo />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<Iframe :key="route.path" />
|
||||||
|
</j-pro-layout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" name="SinglePage" setup>
|
||||||
|
import UserInfo from './components/UserInfo.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';
|
||||||
|
import { AccountMenu } from '@/router/menu'
|
||||||
|
import { useSystem } from '@/store/system';
|
||||||
|
import { storeToRefs } from 'pinia';
|
||||||
|
import Iframe from '@/views/iframe/index.vue'
|
||||||
|
|
||||||
|
type StateType = {
|
||||||
|
collapsed: boolean;
|
||||||
|
openKeys: string[];
|
||||||
|
selectedKeys: string[];
|
||||||
|
pure: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
const menu = useMenuStore();
|
||||||
|
|
||||||
|
const system = useSystem();
|
||||||
|
const {configInfo,layout, basicLayout} = storeToRefs(system);
|
||||||
|
|
||||||
|
const layoutConf = reactive({
|
||||||
|
theme: DefaultSetting.layout.theme,
|
||||||
|
siderWidth: layout.value.siderWidth,
|
||||||
|
logo: DefaultSetting.layout.logo,
|
||||||
|
title: DefaultSetting.layout.title,
|
||||||
|
menuData: menu.siderMenus,
|
||||||
|
// menuData: menu.siderMenus,
|
||||||
|
splitMenus: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
layoutConf.theme = configInfo.value.front?.headerTheme || DefaultSetting.layout.theme;
|
||||||
|
layoutConf.title = configInfo.value.front?.title || DefaultSetting.layout.title;
|
||||||
|
layoutConf.logo = configInfo.value.front?.logo || DefaultSetting.layout.logo;
|
||||||
|
})
|
||||||
|
|
||||||
|
const components = computed(() => {
|
||||||
|
const componentName = route.matched[route.matched.length - 1]?.components?.default?.name
|
||||||
|
if (componentName !== 'BasicLayoutPage') {
|
||||||
|
return route.matched[route.matched.length - 1]?.components?.default
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 面包屑
|
||||||
|
*/
|
||||||
|
const breadcrumbs = computed(() =>
|
||||||
|
{
|
||||||
|
const paths = router.currentRoute.value.matched
|
||||||
|
|
||||||
|
return paths.map((item, index) => {
|
||||||
|
return {
|
||||||
|
index,
|
||||||
|
isLast: index === (paths.length -1),
|
||||||
|
path: item.path,
|
||||||
|
breadcrumbName: (item.meta as any).title || '',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const routerBack = () => {
|
||||||
|
router.go(-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const jump = (item: any) => {
|
||||||
|
router.push(item.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
if (router.currentRoute) {
|
||||||
|
const paths = router.currentRoute.value.matched
|
||||||
|
basicLayout.value.selectedKeys = paths.map(item => item.path)
|
||||||
|
basicLayout.value.openKeys = paths.map(item => item.path)
|
||||||
|
console.log(paths) //
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const toDoc = () => window.open('http://doc.v2.jetlinks.cn/');
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.right-content {
|
||||||
|
margin-right: 24px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -88,8 +88,9 @@ const subscribeNotice = () => {
|
||||||
const read = (type: string, data: any) => {
|
const read = (type: string, data: any) => {
|
||||||
changeStatus_api('_read', [data.payload.id]).then((resp: any) => {
|
changeStatus_api('_read', [data.payload.id]).then((resp: any) => {
|
||||||
if (resp.status !== 200) return;
|
if (resp.status !== 200) return;
|
||||||
notification.close(data.payload.id);
|
// notification.close(data.payload.id);
|
||||||
getList();
|
getList();
|
||||||
|
console.log(data,type)
|
||||||
if (type !== '_read') {
|
if (type !== '_read') {
|
||||||
menuStory.routerPush('account/center', {
|
menuStory.routerPush('account/center', {
|
||||||
tabKey: 'StationMessage',
|
tabKey: 'StationMessage',
|
||||||
|
@ -175,11 +176,11 @@ watch(updateCount, () => getList());
|
||||||
|
|
||||||
const tabs = ref<any>([]);
|
const tabs = ref<any>([]);
|
||||||
|
|
||||||
const queryTypeList = async () => {
|
const queryTypeList = async (_tab: any[]) => {
|
||||||
const resp: any = await getAllNotice();
|
const resp: any = await getAllNotice();
|
||||||
if (resp.status === 200) {
|
if (resp.status === 200) {
|
||||||
const provider = resp.result.map((i: any) => i.provider) || [];
|
const provider = resp.result.map((i: any) => i.provider) || [];
|
||||||
const arr = tab.filter((item: any) => {
|
const arr = _tab.filter((item: any) => {
|
||||||
return item.type.some((i: any) => provider.includes(i))
|
return item.type.some((i: any) => provider.includes(i))
|
||||||
});
|
});
|
||||||
tabs.value = arr;
|
tabs.value = arr;
|
||||||
|
@ -191,7 +192,15 @@ const queryTypeList = async () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
queryTypeList()
|
const _list: any[] = [...tab]
|
||||||
|
if(menuStory.hasMenu('process')){
|
||||||
|
_list.push({
|
||||||
|
key: 'workflow-notification',
|
||||||
|
tab: '工作流通知',
|
||||||
|
type: ['workflow-task-todo', 'workflow-task-reject', 'workflow-task-cc', 'workflow-process-finish', 'workflow-process-repealed'],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
queryTypeList(_list)
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -64,12 +64,13 @@ import NoticeTab from './NoticeTab.vue';
|
||||||
|
|
||||||
const emits = defineEmits(['action']);
|
const emits = defineEmits(['action']);
|
||||||
|
|
||||||
type DataType = 'alarm' | 'system-monitor' | 'system-business';
|
type DataType = 'alarm' | 'system-monitor' | 'system-business' | 'workflow-notification';
|
||||||
|
|
||||||
const refreshObj = ref({
|
const refreshObj = ref({
|
||||||
'alarm': true,
|
'alarm': true,
|
||||||
'system-monitor': true,
|
'system-monitor': true,
|
||||||
'system-business': true,
|
'system-business': true,
|
||||||
|
'workflow-notification': true
|
||||||
});
|
});
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
export { default as BasicLayoutPage } from './BasicLayoutPage.vue'
|
export { default as BasicLayoutPage } from './BasicLayoutPage.vue'
|
||||||
export { default as BlankLayoutPage } from './BlankLayoutPage.vue'
|
export { default as BlankLayoutPage } from './BlankLayoutPage.vue'
|
||||||
export { default as FullPage } from './FullPage.vue'
|
export { default as FullPage } from './FullPage.vue'
|
||||||
|
export { default as SinglePage } from './SinglePage.vue'
|
|
@ -91,5 +91,5 @@ export default [
|
||||||
path: VideoSharePath,
|
path: VideoSharePath,
|
||||||
component: () => import('@/views/media/Device/Channel/Share/index.vue')
|
component: () => import('@/views/media/Device/Channel/Share/index.vue')
|
||||||
},
|
},
|
||||||
AccountMenu
|
AccountMenu,
|
||||||
]
|
]
|
|
@ -24,13 +24,23 @@ const defaultOwnParams = [
|
||||||
termType: 'eq',
|
termType: 'eq',
|
||||||
value: 'iot'
|
value: 'iot'
|
||||||
},
|
},
|
||||||
// {
|
{
|
||||||
// column: 'owner',
|
column: 'owner',
|
||||||
// termType: 'isnull',
|
termType: 'isnull',
|
||||||
// value: '1',
|
value: '1',
|
||||||
// type: 'or'
|
type: 'or'
|
||||||
// }
|
},
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
terms: [
|
||||||
|
{
|
||||||
|
value: "%show\":true%",
|
||||||
|
termType: "like",
|
||||||
|
column: "options"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
type:'or'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -79,7 +89,7 @@ export const useMenuStore = defineStore({
|
||||||
jumpPage(name: string, params?: Record<string, any>, query?: Record<string, any>) {
|
jumpPage(name: string, params?: Record<string, any>, query?: Record<string, any>) {
|
||||||
const path = this.hasMenu(name)
|
const path = this.hasMenu(name)
|
||||||
if (path) {
|
if (path) {
|
||||||
this.params = { [name]: params || {}}
|
this.params = { [name]: params || {} }
|
||||||
router.push({
|
router.push({
|
||||||
name, params, query, state: { params }
|
name, params, query, state: { params }
|
||||||
})
|
})
|
||||||
|
@ -89,14 +99,16 @@ export const useMenuStore = defineStore({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
routerPush(name: string, params?: Record<string, any>, query?: Record<string, any>) {
|
routerPush(name: string, params?: Record<string, any>, query?: Record<string, any>) {
|
||||||
this.params = { [name]: params || {}}
|
this.params = { [name]: params || {} }
|
||||||
router.push({
|
router.push({
|
||||||
name, params, query, state: { params }
|
name, params, query, state: { params }
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
handleMenusMapById(item: { code: string, path: string}) {
|
handleMenusMapById(item: { name: string, path: string }) {
|
||||||
const { name, path } = item
|
const { name, path } = item
|
||||||
this.menus[name] = {path}
|
if (name) {
|
||||||
|
this.menus[name] = { path }
|
||||||
|
}
|
||||||
},
|
},
|
||||||
queryMenuTree(isCommunity = false): Promise<any[]> {
|
queryMenuTree(isCommunity = false): Promise<any[]> {
|
||||||
return new Promise(async (res) => {
|
return new Promise(async (res) => {
|
||||||
|
@ -105,9 +117,6 @@ export const useMenuStore = defineStore({
|
||||||
if (resp.success) {
|
if (resp.success) {
|
||||||
const permission = usePermissionStore()
|
const permission = usePermissionStore()
|
||||||
let resultData = resp.result
|
let resultData = resp.result
|
||||||
// if (!isNoCommunity) {
|
|
||||||
// resultData = filterCommunityMenus(resultData)
|
|
||||||
// }
|
|
||||||
const components = getAsyncRoutesMap()
|
const components = getAsyncRoutesMap()
|
||||||
const menusData = handleMenus(cloneDeep(resultData), components)
|
const menusData = handleMenus(cloneDeep(resultData), components)
|
||||||
permission.handlePermission(resultData)
|
permission.handlePermission(resultData)
|
||||||
|
|
|
@ -57,4 +57,5 @@ export const protocolList = [
|
||||||
{ label: 'OPC_UA', value: 'OPC_UA', alias: 'opc-ua' },
|
{ label: 'OPC_UA', value: 'OPC_UA', alias: 'opc-ua' },
|
||||||
{ label: 'MODBUS_TCP', value: 'MODBUS_TCP', alias: 'Modbus/TCP' },
|
{ label: 'MODBUS_TCP', value: 'MODBUS_TCP', alias: 'Modbus/TCP' },
|
||||||
{ label: 'COLLECTOR_GATEWAY', value: 'COLLECTOR_GATEWAY', alias: 'GATEWAY' },
|
{ label: 'COLLECTOR_GATEWAY', value: 'COLLECTOR_GATEWAY', alias: 'GATEWAY' },
|
||||||
|
{ label: 'S7', value: 'snap7', alias: 'snap7' },
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { BlankLayoutPage, BasicLayoutPage } from 'components/Layout'
|
import { BlankLayoutPage, BasicLayoutPage, SinglePage } from 'components/Layout'
|
||||||
import { isNoCommunity } from '@/utils/utils'
|
import { isNoCommunity } from '@/utils/utils'
|
||||||
import Iframe from '../views/iframe/index.vue'
|
import Iframe from '../views/iframe/index.vue'
|
||||||
import { shallowRef, defineAsyncComponent } from 'vue'
|
import { shallowRef, defineAsyncComponent, h } from 'vue'
|
||||||
|
|
||||||
const pagesComponent = import.meta.glob('../views/**/*.vue');
|
const pagesComponent = import.meta.glob('../views/**/*.vue');
|
||||||
|
|
||||||
|
@ -158,10 +158,12 @@ const extraRouteObj = {
|
||||||
|
|
||||||
type Buttons = Array<{ id: string }>
|
type Buttons = Array<{ id: string }>
|
||||||
|
|
||||||
const hasAppID = (item: { appId?: string, url?: string }): { isApp: boolean, appUrl: string } => {
|
const hasAppID = (item: any): { isApp: boolean, appUrl: string } => {
|
||||||
|
const isApp = !!item.appId
|
||||||
|
const isLowCode = !!item.options?.LowCode
|
||||||
return {
|
return {
|
||||||
isApp: !!item.appId,
|
isApp: isApp || isLowCode,
|
||||||
appUrl: `/${item.appId}${item.url}`
|
appUrl: isApp ? `/${item.appId}${item.url}` : item.url
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,17 +172,25 @@ const handleButtons = (buttons?: Buttons) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleMeta = (item: MenuItem, isApp: boolean) => {
|
const handleMeta = (item: MenuItem, isApp: boolean) => {
|
||||||
|
const meta = item.meta
|
||||||
return {
|
return {
|
||||||
icon: item.icon,
|
icon: item.icon,
|
||||||
title: item.name,
|
title: meta?.title || item.name,
|
||||||
hideInMenu: item.isShow === false,
|
hideInMenu: meta?.hideInMenu ?? item.isShow === false,
|
||||||
buttons: handleButtons(item.buttons),
|
buttons: handleButtons(item.buttons),
|
||||||
isApp
|
isApp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const findComponents = (code: string, level: number, isApp: boolean, components: any, mate: any, hasChildren: boolean) => {
|
const findComponents = (code: string, level: number, isApp: boolean, components: any, mate: any, hasChildren: boolean, extraModules = {}) => {
|
||||||
const myComponents = components[code]
|
|
||||||
|
const allComponents = {
|
||||||
|
...components,
|
||||||
|
...extraModules
|
||||||
|
}
|
||||||
|
|
||||||
|
const myComponents = allComponents[code]
|
||||||
|
|
||||||
if (level === 1) { // BasicLayoutPage
|
if (level === 1) { // BasicLayoutPage
|
||||||
if (myComponents && !hasChildren) {
|
if (myComponents && !hasChildren) {
|
||||||
return mate?.hasLayout === false ? () => myComponents() : h(BasicLayoutPage, {}, () => [h(defineAsyncComponent(() => myComponents()), {})])
|
return mate?.hasLayout === false ? () => myComponents() : h(BasicLayoutPage, {}, () => [h(defineAsyncComponent(() => myComponents()), {})])
|
||||||
|
@ -195,6 +205,8 @@ const findComponents = (code: string, level: number, isApp: boolean, components:
|
||||||
return myComponents ? () => myComponents() : BlankLayoutPage
|
return myComponents ? () => myComponents() : BlankLayoutPage
|
||||||
} else if(myComponents) { // components
|
} else if(myComponents) { // components
|
||||||
return () => myComponents()
|
return () => myComponents()
|
||||||
|
} else if(myComponents) { // components
|
||||||
|
return () => myComponents()
|
||||||
}
|
}
|
||||||
// return components['demo'] // 开发测试用
|
// return components['demo'] // 开发测试用
|
||||||
return undefined
|
return undefined
|
||||||
|
@ -253,7 +265,7 @@ const findSaveRouteItem = (item: any, components: any) => {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
export const handleMenus = (menuData: any[], components: any, level: number = 1) => {
|
export const handleMenus = (menuData: any[], components: any, level: number = 1, extraModules = {}) => {
|
||||||
if (menuData && menuData.length) {
|
if (menuData && menuData.length) {
|
||||||
return menuData.map(item => {
|
return menuData.map(item => {
|
||||||
const { isApp, appUrl } = hasAppID(item) // 是否为第三方程序
|
const { isApp, appUrl } = hasAppID(item) // 是否为第三方程序
|
||||||
|
@ -266,7 +278,7 @@ export const handleMenus = (menuData: any[], components: any, level: number = 1)
|
||||||
children: item.children
|
children: item.children
|
||||||
}
|
}
|
||||||
|
|
||||||
route.component = findComponents(item.code, level, isApp, components, item.meta, !!item.chidlren?.length)
|
route.component = findComponents(item.code, level, isApp, components, item.meta, !!item.chidlren?.length, extraModules)
|
||||||
const extraRoute = hasExtraChildren(item, extraRouteObj)
|
const extraRoute = hasExtraChildren(item, extraRouteObj)
|
||||||
const detail_components = findDetailRouteItem(item, components)
|
const detail_components = findDetailRouteItem(item, components)
|
||||||
|
|
||||||
|
@ -274,12 +286,16 @@ export const handleMenus = (menuData: any[], components: any, level: number = 1)
|
||||||
route.children = route.children ? [...route.children, ...extraRoute] : extraRoute
|
route.children = route.children ? [...route.children, ...extraRoute] : extraRoute
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (item.options?.LowCode && level === 1) {
|
||||||
|
route.component = SinglePage
|
||||||
|
}
|
||||||
|
|
||||||
if (detail_components.length) {
|
if (detail_components.length) {
|
||||||
route.children = route.children ? route.children.concat(detail_components) : detail_components
|
route.children = route.children ? route.children.concat(detail_components) : detail_components
|
||||||
}
|
}
|
||||||
|
|
||||||
if (route.children && route.children.length) {
|
if (route.children && route.children.length) {
|
||||||
route.children = handleMenus(route.children, components, level + 1)
|
route.children = handleMenus(route.children, components, level + 1, extraModules)
|
||||||
}
|
}
|
||||||
|
|
||||||
const showChildren = route.children?.filter(r => !r.meta?.hideInMenu) || []
|
const showChildren = route.children?.filter(r => !r.meta?.hideInMenu) || []
|
||||||
|
@ -312,8 +328,17 @@ const hideInMenu = (code: string) => {
|
||||||
|
|
||||||
export const handleSiderMenu = (menuData: any[]) => {
|
export const handleSiderMenu = (menuData: any[]) => {
|
||||||
if (menuData && menuData.length) {
|
if (menuData && menuData.length) {
|
||||||
return menuData.map(item => {
|
return menuData.filter(item => {
|
||||||
|
|
||||||
|
if ((item.options?.isShow === false) || item.meta?.hideInMenu === true) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}).map(item => {
|
||||||
const { isApp, appUrl } = hasAppID(item) // 是否为第三方程序
|
const { isApp, appUrl } = hasAppID(item) // 是否为第三方程序
|
||||||
|
if ( item.options?.isShow !== undefined && item.isShow === undefined) {
|
||||||
|
item.isShow = item.options.isShow
|
||||||
|
}
|
||||||
const meta = handleMeta(item, isApp)
|
const meta = handleMeta(item, isApp)
|
||||||
const route: any = {
|
const route: any = {
|
||||||
path: isApp ? appUrl : `${item.url}`,
|
path: isApp ? appUrl : `${item.url}`,
|
||||||
|
@ -327,7 +352,7 @@ export const handleSiderMenu = (menuData: any[]) => {
|
||||||
route.children = handleSiderMenu(route.children)
|
route.children = handleSiderMenu(route.children)
|
||||||
}
|
}
|
||||||
|
|
||||||
route.meta.hideInMenu = hideInMenu(item.code)
|
// route.meta.hideInMenu = hideInMenu(item.code)
|
||||||
|
|
||||||
return route
|
return route
|
||||||
})
|
})
|
||||||
|
|
|
@ -131,7 +131,7 @@ export const postStream = function(url: string, data = {}, params = {}) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const showNotification = (message: string, description: string, key?: string, show: boolean = true) => {
|
export const showNotification = (message: string, description: string, key?: string, show: boolean = true) => {
|
||||||
if (show) {
|
if (show) {
|
||||||
Notification.error({
|
Notification.error({
|
||||||
style: {
|
style: {
|
||||||
|
@ -144,6 +144,10 @@ const showNotification = (message: string, description: string, key?: string, sh
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const TokenLose = () => {
|
||||||
|
showNotification('Unauthorized', '用户未登录', '401')
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 异常拦截处理器
|
* 异常拦截处理器
|
||||||
* @param {Object} error
|
* @param {Object} error
|
||||||
|
@ -182,7 +186,7 @@ const errorHandler = (error: any) => {
|
||||||
} else if (status === 400) {
|
} else if (status === 400) {
|
||||||
showNotification('Request Error', (data.message + '').substr(0, 90), '400')
|
showNotification('Request Error', (data.message + '').substr(0, 90), '400')
|
||||||
} else if (status === 401) {
|
} else if (status === 401) {
|
||||||
showNotification('Unauthorized', '用户未登录', '401')
|
TokenLose()
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
cleanToken()
|
cleanToken()
|
||||||
router.replace({
|
router.replace({
|
||||||
|
|
|
@ -171,6 +171,12 @@
|
||||||
v-model:value="formData.configuration.password"
|
v-model:value="formData.configuration.password"
|
||||||
/>
|
/>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
|
<!-- <j-form-item
|
||||||
|
v-if="formData.provider === 'snap7'"
|
||||||
|
:name="['configuration', 'connect']"
|
||||||
|
>
|
||||||
|
<j-input v-model:value="formData.configuration.connect"/>
|
||||||
|
</j-form-item> -->
|
||||||
<j-form-item label="说明" name="description">
|
<j-form-item label="说明" name="description">
|
||||||
<j-textarea
|
<j-textarea
|
||||||
placeholder="请输入说明"
|
placeholder="请输入说明"
|
||||||
|
@ -239,6 +245,12 @@ const handleOk = async () => {
|
||||||
params.configuration.deviceName = formData.value.configuration.deviceName
|
params.configuration.deviceName = formData.value.configuration.deviceName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(params?.provider === 'snap7'){
|
||||||
|
params.configuration={
|
||||||
|
connect : false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
params.circuitBreaker = {
|
params.circuitBreaker = {
|
||||||
type: 'Ignore'
|
type: 'Ignore'
|
||||||
}
|
}
|
||||||
|
@ -304,7 +316,7 @@ const getProvidersList = async () => {
|
||||||
if (resp.status === 200) {
|
if (resp.status === 200) {
|
||||||
const arr = resp.result
|
const arr = resp.result
|
||||||
.filter(
|
.filter(
|
||||||
(item: any) => ['GATEWAY', 'Modbus/TCP', 'opc-ua'].includes(item.name),
|
(item: any) => ['GATEWAY', 'Modbus/TCP', 'opc-ua','snap7'].includes(item.name),
|
||||||
)
|
)
|
||||||
.map((it: any) => it.name);
|
.map((it: any) => it.name);
|
||||||
const providers: any = protocolList.filter((item: any) =>
|
const providers: any = protocolList.filter((item: any) =>
|
||||||
|
|
|
@ -17,6 +17,7 @@ export const FormState: FormDataType = {
|
||||||
password: '',
|
password: '',
|
||||||
deviceId: undefined,
|
deviceId: undefined,
|
||||||
deviceName: undefined,
|
deviceName: undefined,
|
||||||
|
connect:false,
|
||||||
},
|
},
|
||||||
description: '',
|
description: '',
|
||||||
};
|
};
|
||||||
|
|
|
@ -43,7 +43,7 @@
|
||||||
>
|
>
|
||||||
<template #img>
|
<template #img>
|
||||||
<slot name="img">
|
<slot name="img">
|
||||||
<img :src="getImage('/channel.png')" />
|
<img :src="ImageMap.get(slotProps.provider)" />
|
||||||
</slot>
|
</slot>
|
||||||
</template>
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
|
@ -157,6 +157,17 @@ const params = ref<Record<string, any>>({});
|
||||||
const visible = ref(false);
|
const visible = ref(false);
|
||||||
const current = ref({});
|
const current = ref({});
|
||||||
|
|
||||||
|
const opcImage = getImage('/DataCollect/device-opcua.png');
|
||||||
|
const modbusImage = getImage('/DataCollect/device-modbus.png');
|
||||||
|
const s7Image = getImage('/DataCollect/s7.png')
|
||||||
|
const gatewayImage = getImage('/DataCollect/gateway.png')
|
||||||
|
const ImageMap = new Map()
|
||||||
|
ImageMap.set('OPC_UA',opcImage)
|
||||||
|
ImageMap.set('MODBUS_TCP',modbusImage)
|
||||||
|
ImageMap.set('snap7',s7Image)
|
||||||
|
ImageMap.set('COLLECTOR_GATEWAY',gatewayImage)
|
||||||
|
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
title: '通道名称',
|
title: '通道名称',
|
||||||
|
|
|
@ -10,6 +10,7 @@ export interface ConfigurationType {
|
||||||
authType: string | undefined,
|
authType: string | undefined,
|
||||||
deviceId: string | undefined,
|
deviceId: string | undefined,
|
||||||
deviceName: string | undefined,
|
deviceName: string | undefined,
|
||||||
|
connect:boolean | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FormDataType {
|
export interface FormDataType {
|
||||||
|
|
|
@ -0,0 +1,260 @@
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="top"><j-switch v-model:checked="deathArea" @change="handleDeathArea"></j-switch></div>
|
||||||
|
<div v-if="deathArea">
|
||||||
|
<div class="content">
|
||||||
|
<j-radio-group v-model:value="tag" @change="handleTag">
|
||||||
|
<j-space>
|
||||||
|
<j-radio-button value="currentValue">固定值</j-radio-button>
|
||||||
|
<j-radio-button value="this['currentValue'] - this['lastValue']">百分比</j-radio-button>
|
||||||
|
</j-space>
|
||||||
|
</j-radio-group>
|
||||||
|
<j-tooltip v-if="tag !== 'currentValue'" title="最近一次采集到的值与上一次采集值比对,数值浮动在百分比以内时将被过滤">
|
||||||
|
<AIcon type="QuestionCircleOutlined" style="margin-left: 10px;font-size: 18px;color: rgb(153, 153, 153)" />
|
||||||
|
</j-tooltip>
|
||||||
|
</div>
|
||||||
|
<j-form-item-rest>
|
||||||
|
<div v-if="tag === 'currentValue'" class="fixed">
|
||||||
|
<j-row :gutter="5" align="middle">
|
||||||
|
<j-col>
|
||||||
|
<j-input-number v-model:value="_value[0].value" style="width: 100%" placeholder="请输入值"
|
||||||
|
:max="_value[1] ? _value[1].value : 999999" :min="1" @change="handleChange" />
|
||||||
|
</j-col>
|
||||||
|
<j-col>
|
||||||
|
<j-select v-model:value="_value[0].termType" :showArrow="false"
|
||||||
|
:options="_value.length !== 2 ? termTypeOptions : leftOptions" placeholder="符号"
|
||||||
|
@change="handleChange" />
|
||||||
|
</j-col>
|
||||||
|
<template v-if="swap === 'range'">
|
||||||
|
<j-col>点位值</j-col>
|
||||||
|
<j-col>
|
||||||
|
<j-select :showArrow="false" v-model:value="_value[1].termType" :options="termTypeOptions"
|
||||||
|
placeholder="符号" @change="handleChange" />
|
||||||
|
</j-col>
|
||||||
|
<j-col>
|
||||||
|
<j-input-number v-model:value="_value[1].value" style="width: 100%" placeholder="请输入值"
|
||||||
|
:min="_value[0].value" @change="handleChange" />
|
||||||
|
</j-col>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
<j-button @click="handleSwap">
|
||||||
|
<AIcon type="SwapOutlined" />
|
||||||
|
</j-button>
|
||||||
|
</j-row>
|
||||||
|
</div>
|
||||||
|
<div v-else class="percent">
|
||||||
|
<div class="percent-title">点位值</div>
|
||||||
|
<j-input-number v-model:value="percentValue" style="width: 200px" addon-after="%" placeholder="请输入值"
|
||||||
|
:min="1" @change="handlePercent" :max="65535" />
|
||||||
|
</div>
|
||||||
|
</j-form-item-rest>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang='ts'>
|
||||||
|
import { Form } from 'ant-design-vue';
|
||||||
|
|
||||||
|
const formItemContext = Form.useInjectFormItemContext()
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
value: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const emits = defineEmits(['update:value', 'change']);
|
||||||
|
const _value = ref<any>(props.value)
|
||||||
|
const deathArea = ref(false);
|
||||||
|
const tag = ref<string>('currentValue')
|
||||||
|
const swap = ref<string>('fix')
|
||||||
|
const percentValue = ref()
|
||||||
|
|
||||||
|
|
||||||
|
const termTypeOptions = computed(() => {
|
||||||
|
if (_value.value?.length === 1) {
|
||||||
|
return [
|
||||||
|
{ label: '=', value: 'neq' },
|
||||||
|
{ label: '>', value: 'lte' },
|
||||||
|
{ label: '<', value: 'gte' },
|
||||||
|
{ label: '≥', value: 'lt' },
|
||||||
|
{ label: '≤', value: 'gt' },
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
return [
|
||||||
|
{ label: '<', value: 'gte' },
|
||||||
|
{ label: '≤', value: 'gt' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const leftOptions = [
|
||||||
|
{ label: '<', value: 'lte' },
|
||||||
|
{ label: '≤', value: 'lt' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const handleDeathArea = (e: any) => {
|
||||||
|
if (e) {
|
||||||
|
_value.value = [{
|
||||||
|
column: 'currentValue',
|
||||||
|
value: undefined,
|
||||||
|
termType: undefined,
|
||||||
|
type: 'and',
|
||||||
|
}]
|
||||||
|
} else {
|
||||||
|
_value.value = []
|
||||||
|
}
|
||||||
|
handleChange()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSwap = () => {
|
||||||
|
if (swap.value === 'fix') {
|
||||||
|
swap.value = 'range'
|
||||||
|
_value.value = [
|
||||||
|
{
|
||||||
|
column: 'currentValue',
|
||||||
|
value: '',
|
||||||
|
termType: undefined,
|
||||||
|
type: 'or',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
column: 'currentValue',
|
||||||
|
value: '',
|
||||||
|
termType: undefined,
|
||||||
|
type: 'or',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
swap.value = 'fix'
|
||||||
|
_value.value = [{
|
||||||
|
column: 'currentValue',
|
||||||
|
value: undefined,
|
||||||
|
termType: undefined,
|
||||||
|
type: 'and',
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
handleChange()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleTag = (e: any) => {
|
||||||
|
if (e.target.value === 'currentValue') {
|
||||||
|
swap.value = 'fix'
|
||||||
|
_value.value = [{
|
||||||
|
column: 'currentValue',
|
||||||
|
value: undefined,
|
||||||
|
termType: undefined,
|
||||||
|
type: 'and',
|
||||||
|
}]
|
||||||
|
} else {
|
||||||
|
_value.value = [
|
||||||
|
{
|
||||||
|
column: `this['currentValue'] - this['lastValue']*init/100`,
|
||||||
|
value: 0,
|
||||||
|
termType: 'lt',
|
||||||
|
type: 'or',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
column: `this['currentValue'] - this['lastValue']*0/100`,
|
||||||
|
value: 0,
|
||||||
|
termType: 'gt',
|
||||||
|
type: 'or',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
handleChange()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlePercent = (e: any) => {
|
||||||
|
if (e) {
|
||||||
|
_value.value = [
|
||||||
|
{
|
||||||
|
column: `this['currentValue'] - this['lastValue'] * ${e}/100`,
|
||||||
|
value: 0,
|
||||||
|
termType: 'lt',
|
||||||
|
type: 'or',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
column: `this['currentValue'] - this['lastValue'] * ${e + 100}/100`,
|
||||||
|
value: 0,
|
||||||
|
termType: 'gt',
|
||||||
|
type: 'or',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
_value.value = [
|
||||||
|
{
|
||||||
|
column: `this['currentValue'] - this['lastValue'] * 1/100`,
|
||||||
|
value: 0,
|
||||||
|
termType: 'lt',
|
||||||
|
type: 'or',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
column: `this['currentValue'] - this['lastValue'] * 1/100`,
|
||||||
|
value: 0,
|
||||||
|
termType: 'gt',
|
||||||
|
type: 'or',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
handleChange()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlePercentProps = (arr: any) => {
|
||||||
|
const obj = arr.find((item: any) => item.termType === 'lt')
|
||||||
|
const val = obj.column.split('*')[1].split('/')[0]
|
||||||
|
percentValue.value = val !== 'init' ? val : undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleChange = () => {
|
||||||
|
emits('update:value', _value.value)
|
||||||
|
emits('change', _value.value)
|
||||||
|
formItemContext.onFieldChange()
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.value,
|
||||||
|
(val: any) => {
|
||||||
|
if (val && val.length !== 0) {
|
||||||
|
deathArea.value = true
|
||||||
|
if (val && val[0]?.column === 'currentValue') {
|
||||||
|
tag.value = 'currentValue'
|
||||||
|
_value.value = val
|
||||||
|
if (val.length === 2) {
|
||||||
|
swap.value = 'range'
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
handlePercentProps(val)
|
||||||
|
tag.value = `this['currentValue'] - this['lastValue']`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang='less'>
|
||||||
|
.top {
|
||||||
|
padding: 12px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.fixed {
|
||||||
|
padding: 12px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.percent {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 12px;
|
||||||
|
|
||||||
|
.percent-title {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,377 @@
|
||||||
|
<template >
|
||||||
|
<j-modal :title="data.id ? '编辑' : '新增'" :visible="true" width="700px" @cancel="handleCancel">
|
||||||
|
<j-form :model="form" layout="vertical" ref="formRef">
|
||||||
|
<j-form-item label="点位名称" name="name">
|
||||||
|
<j-input placeholder="请输入点位名称" v-model:value="form.name" />
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item label="地址区域" :name="['configuration', 'daveArea']" :rules="{
|
||||||
|
required: true,
|
||||||
|
message: '请选择地址区域',
|
||||||
|
trigger: 'change',
|
||||||
|
}">
|
||||||
|
<j-select v-model:value="form.configuration.daveArea" show-search placeholder="请选择地址区域"
|
||||||
|
@change="daveAreaChange">
|
||||||
|
<j-select-option v-for="item in dataAreaFilterList" :key="item.id" :value="item.id">{{
|
||||||
|
item.name }}</j-select-option>
|
||||||
|
</j-select>
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item label="地址编号" :name="['configuration', 'areaNumber']" v-show="form.configuration.daveArea == 'DB'"
|
||||||
|
:rules="{
|
||||||
|
required: true,
|
||||||
|
message: '请输入地址编号',
|
||||||
|
trigger: 'blur',
|
||||||
|
}">
|
||||||
|
<j-input-number v-model:value="form.configuration.areaNumber" :maxlength="64" style="width: 100%"
|
||||||
|
:max="65535" autocomplete="off" :disabled="form.configuration.daveArea == 'DB' && deviceType == 'S200'"
|
||||||
|
placeholder="请输入地址编号" />
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item label="数据类型" :name="['configuration', 'type']" :rules="{
|
||||||
|
required: true,
|
||||||
|
message: '请选择数据类型',
|
||||||
|
trigger: 'change',
|
||||||
|
}">
|
||||||
|
<j-select v-model:value="form.configuration.type" show-search placeholder="请选择数据类型"
|
||||||
|
@change="chooseS7DataType">
|
||||||
|
<j-select-option v-for="item in dataTypesList" :key="item.id" :value="item.id">{{
|
||||||
|
item.name }}</j-select-option>
|
||||||
|
</j-select>
|
||||||
|
</j-form-item>
|
||||||
|
|
||||||
|
<j-form-item v-if="!disabled" label="字符串长度(byte)" :name="['configuration', 'bytes']" :rules="{
|
||||||
|
required: true,
|
||||||
|
message: '请输入0~65535之间的正整数',
|
||||||
|
trigger: 'blur',
|
||||||
|
}">
|
||||||
|
<j-input-number type="number" style="width: 100%" addon-after="字节" v-model:value="form.configuration.bytes"
|
||||||
|
placeholder="请输入字符串长度" :precision="0" :controls="false" :maxlength="64" :disabled="disabled" />
|
||||||
|
</j-form-item>
|
||||||
|
|
||||||
|
<j-form-item v-if="form.configuration.type == 'Bool'" label="位偏移量(bit)" :name="['configuration', 'bits']"
|
||||||
|
:rules="{
|
||||||
|
required: true,
|
||||||
|
message: '请输入0~7之间的正整数',
|
||||||
|
trigger: 'blur',
|
||||||
|
}">
|
||||||
|
<j-input-number type="number" style="width: 100%" addon-after="位" v-model:value="form.configuration.bits"
|
||||||
|
placeholder="请输入位偏移量" :precision="0" :min="0" :max="7" :controls="false" :maxlength="2" />
|
||||||
|
</j-form-item>
|
||||||
|
|
||||||
|
<j-form-item label="偏移量" :name="['configuration', 'offset']" :rules="{
|
||||||
|
required: true,
|
||||||
|
message: '请输入0~65535之间的正整数',
|
||||||
|
trigger: 'blur',
|
||||||
|
}">
|
||||||
|
<j-input-number type="number" style="width: 100%" v-model:value="form.configuration.offset"
|
||||||
|
placeholder="请输入偏移量" :precision="0" :min="0" :max="65535" :controls="false" :maxlength="64" />
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item label="缩放因子" :name="['configuration', 'scaleFactor']">
|
||||||
|
<j-input-number type="number" style="width: 100%" v-model:value="form.configuration.scaleFactor"
|
||||||
|
placeholder="缩放因子" :min="0" :max="65535" :controls="false" :maxlength="64" />
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item label="小数保留位数" :name="['configuration', 'scale']">
|
||||||
|
<j-input-number type="number" style="width: 100%" v-model:value="form.configuration.scale"
|
||||||
|
placeholder="缩放因子" :precision="0" :min="1" :max="65535" :controls="false" :maxlength="64" />
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item label="访问类型" name="accessModes" :rules="{
|
||||||
|
required: true,
|
||||||
|
message: '请选择访问类型',
|
||||||
|
}">
|
||||||
|
<j-card-select multiple :showImage="false" v-model:value="form.accessModes" :options="[
|
||||||
|
{ label: '读', value: 'read' },
|
||||||
|
{ label: '写', value: 'write' },
|
||||||
|
]
|
||||||
|
" :column="2" />
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item :name="['configuration', 'terms']" :rules="[{
|
||||||
|
validator: Area,
|
||||||
|
trigger: 'change',
|
||||||
|
}]">
|
||||||
|
<template #label>
|
||||||
|
<j-space>
|
||||||
|
<span>点位死区</span><span class="explain">点位死区范围内的异常数据将被过滤(请勿配置非数值类型)</span>
|
||||||
|
</j-space>
|
||||||
|
</template>
|
||||||
|
<DeathArea v-model:value="form.configuration.terms" />
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item label="轮询任务" :name="['configuration', 'interval']">
|
||||||
|
<p>
|
||||||
|
采集频率<span style="margin-left: 5px; color: #9d9ea1; font-size: 12px">采集频率为0时不执行轮询任务</span>
|
||||||
|
</p>
|
||||||
|
<j-radio-group v-model:value="form.configuration.interval">
|
||||||
|
<j-space>
|
||||||
|
<j-radio-button :value="3000">3000ms</j-radio-button>
|
||||||
|
<j-radio-button :value="6000">6000ms</j-radio-button>
|
||||||
|
<j-radio-button :value="9000">9000ms</j-radio-button>
|
||||||
|
<j-radio-button :value="Number(form.configuration.interval)" @click="intervalRef.visible = true">
|
||||||
|
{{
|
||||||
|
![3000, 6000, 9000].includes(form.configuration.interval)
|
||||||
|
? form.configuration.interval + 'ms'
|
||||||
|
: '自定义'
|
||||||
|
}}
|
||||||
|
</j-radio-button>
|
||||||
|
</j-space>
|
||||||
|
</j-radio-group>
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item name="features">
|
||||||
|
<j-checkbox-group v-model:value="form.features">
|
||||||
|
<j-checkbox value="changedOnly">只推送变化的数据</j-checkbox>
|
||||||
|
</j-checkbox-group>
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item label="说明" name="description">
|
||||||
|
<j-textarea placeholder="请输入说明" v-model:value="form.description" :maxlength="200" :rows="3" showCount />
|
||||||
|
</j-form-item>
|
||||||
|
</j-form>
|
||||||
|
<j-modal title="采集频率" :visible="intervalRef.visible" @cancel="handleCancelInterval" @ok="handleInterval">
|
||||||
|
<j-form ref="formRef2" name="virtual-form" layout="vertical" :model="intervalRef">
|
||||||
|
<j-form-item label="采集频率" name="interval" :rules="[
|
||||||
|
{ required: true, message: '请输入采集频率' },
|
||||||
|
]">
|
||||||
|
<j-input-number type="tel" addonAfter="ms" v-model:value="intervalRef.interval" placeholder="请输入采集频率"
|
||||||
|
:controls="false" :precision="0" :maxlength="64" />
|
||||||
|
</j-form-item>
|
||||||
|
</j-form>
|
||||||
|
</j-modal>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<j-button key="back" @click="handleCancel">取消</j-button>
|
||||||
|
<PermissionButton key="submit" type="primary" :loading="loading" @click="handleOk" style="margin-left: 8px"
|
||||||
|
:hasPermission="`DataCollect/Collector:${data.id ? 'update' : 'add'}`">
|
||||||
|
确认
|
||||||
|
</PermissionButton>
|
||||||
|
</template>
|
||||||
|
</j-modal>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import {
|
||||||
|
savePoint,
|
||||||
|
updatePoint,
|
||||||
|
_validateField,
|
||||||
|
getArea,
|
||||||
|
getSnapTypes
|
||||||
|
} from '@/api/data-collect/collector';
|
||||||
|
import type { FormInstance } from 'ant-design-vue';
|
||||||
|
import DeathArea from './DeathArea.vue';
|
||||||
|
import { randomString } from '@/utils/utils';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
default: () => { },
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const dataAreaFilter = {
|
||||||
|
S200: [
|
||||||
|
'RELAY',
|
||||||
|
'HIGH_SPEED',
|
||||||
|
'SYSTEM_FLAGS',
|
||||||
|
'ANALOG_INPUTS',
|
||||||
|
'ANALOG_OUTPUTS',
|
||||||
|
'I',
|
||||||
|
'Q',
|
||||||
|
'M',
|
||||||
|
'IEC_COUNTERS',
|
||||||
|
'IEC_TIMERS',
|
||||||
|
],
|
||||||
|
S1200: ['I', 'Q', 'M', 'DB'],
|
||||||
|
S1500: ['I', 'Q', 'M', 'DB'],
|
||||||
|
S300: ['I', 'Q', 'M', 'DB', 'C', 'T'],
|
||||||
|
S400: ['I', 'Q', 'M', 'DB', 'C', 'T'],
|
||||||
|
};
|
||||||
|
const emit = defineEmits(['change']);
|
||||||
|
const loading = ref(false);
|
||||||
|
const formRef = ref<FormInstance>();
|
||||||
|
const formRef2 = ref<FormInstance>();
|
||||||
|
const deviceType = ref<string>(props.data.deviceType);
|
||||||
|
const dataTypesList = ref<any[]>([]);
|
||||||
|
const daveAreaList = ref<any>([]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const form = ref<any>({
|
||||||
|
name: props.data.name || '',
|
||||||
|
configuration: props.data.configuration || {
|
||||||
|
type: undefined,
|
||||||
|
interval: 3000,
|
||||||
|
areaNumber: undefined,
|
||||||
|
bytes: undefined,
|
||||||
|
terms: []
|
||||||
|
},
|
||||||
|
accessModes: [],
|
||||||
|
features: [],
|
||||||
|
description: props.data.description || '',
|
||||||
|
// inheritBreaker: true,
|
||||||
|
// pointKey: randomString(9)
|
||||||
|
});
|
||||||
|
const intervalRef = reactive({
|
||||||
|
visible: false,
|
||||||
|
interval: 3000,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
/**选择S7点位数据类型 */
|
||||||
|
const disabled = ref(true);
|
||||||
|
const chooseS7DataType = (val: string) => {
|
||||||
|
const result: any = dataTypesList.value.find((item: any) => item.id == val);
|
||||||
|
form.value.configuration.bytes = result.length;
|
||||||
|
disabled.value = result.length != 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
const daveAreaChange = (val: string) => {
|
||||||
|
if (val === 'DB') {
|
||||||
|
form.value.configuration.areaNumber = 1;
|
||||||
|
} else {
|
||||||
|
form.value.configuration.areaNumber = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取地区信息
|
||||||
|
*/
|
||||||
|
const getAreaList = async () => {
|
||||||
|
const res = await getArea();
|
||||||
|
if (res.success) {
|
||||||
|
daveAreaList.value = res.result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
getAreaList();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取数据类型
|
||||||
|
*/
|
||||||
|
const getTypes = async () => {
|
||||||
|
const res: any = await getSnapTypes();
|
||||||
|
dataTypesList.value = res.result;
|
||||||
|
};
|
||||||
|
getTypes();
|
||||||
|
|
||||||
|
const dataAreaFilterList = computed(() => {
|
||||||
|
let result = daveAreaList.value.filter((item: any) =>
|
||||||
|
dataAreaFilter[deviceType.value]?.includes(item.id),
|
||||||
|
);
|
||||||
|
if (deviceType.value == 'S200') {
|
||||||
|
result.push({
|
||||||
|
id: 'DB',
|
||||||
|
name: '变量存储区(V)',
|
||||||
|
address: '',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleInterval = async () => {
|
||||||
|
const res = await formRef2.value?.validate()
|
||||||
|
if (res) {
|
||||||
|
form.value.configuration.interval = res.interval
|
||||||
|
intervalRef.visible = false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const handleCancelInterval = () => {
|
||||||
|
intervalRef.visible = false
|
||||||
|
intervalRef.interval = 3000
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const handleOk = async () => {
|
||||||
|
const res: any = await formRef.value?.validate();
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
...res,
|
||||||
|
configuration: {
|
||||||
|
...res.configuration,
|
||||||
|
bytes: res.configuration.bytes || form.value.configuration.bytes
|
||||||
|
},
|
||||||
|
inheritBreaker: true,
|
||||||
|
pointKey: props.data.pointKey || randomString(9),
|
||||||
|
provider: props.data.provider,
|
||||||
|
collectorId: props.data.collectorId,
|
||||||
|
accessModes: res?.accessModes.filter((item: any) => item)
|
||||||
|
}
|
||||||
|
loading.value = true;
|
||||||
|
const response = !props.data.id
|
||||||
|
? await savePoint(params).catch(() => { })
|
||||||
|
: await updatePoint(props.data.id, { ...props.data, ...params }).catch(() => { });
|
||||||
|
emit('change', response?.status === 200);
|
||||||
|
loading.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
emit('change', false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Area = (_: any, value: any): Promise<any> =>
|
||||||
|
new Promise(async (resolve, reject) => {
|
||||||
|
if (!value) {
|
||||||
|
return resolve('')
|
||||||
|
}
|
||||||
|
if (value?.length === 0) {
|
||||||
|
return resolve('')
|
||||||
|
} else if (value?.length === 1) {
|
||||||
|
return value[0].value && value[0].termType ? resolve('') : reject('请配置点位死区');
|
||||||
|
} else {
|
||||||
|
if (value?.[0].column === 'currentValue') {
|
||||||
|
// value.forEach((item:any) => {
|
||||||
|
// if(item.termType && item.value){
|
||||||
|
// return resolve('')
|
||||||
|
// }else{
|
||||||
|
// return reject('请配置点位死区')
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
const pass = value.every((item: any) => item.termType && item.value)
|
||||||
|
return pass ? resolve('') : reject('请配置点位死区')
|
||||||
|
} else {
|
||||||
|
value.forEach((item: any) => {
|
||||||
|
if (item.column === `this['currentValue'] - this['lastValue']*init/100`) {
|
||||||
|
return reject('请配置点位死区')
|
||||||
|
} else {
|
||||||
|
return resolve('')
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
form.value.features = props.data.features?.map((item: any) => item.value)
|
||||||
|
form.value.configuration.bytes = props.data.configuration?.bytes
|
||||||
|
if (props.data.accessModes?.length !== 0) {
|
||||||
|
form.value.accessModes = props.data.accessModes?.map((item: any) => item.value)
|
||||||
|
}
|
||||||
|
console.log(props.data.configuration, 123)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => dataTypesList.value,
|
||||||
|
(val: any[]) => {
|
||||||
|
if (val) {
|
||||||
|
const result: any = dataTypesList.value.find(
|
||||||
|
(item: any) => item.id == form.value.configuration.type,
|
||||||
|
);
|
||||||
|
if (result) {
|
||||||
|
// console.log('result',result)
|
||||||
|
disabled.value = (result && result.length !== 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => form.value.configuration.type,
|
||||||
|
(val) => {
|
||||||
|
if (val !== 'Bool') {
|
||||||
|
form.value.configuration.bits = 0;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.explain {
|
||||||
|
color: #adadad;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,206 @@
|
||||||
|
<template>
|
||||||
|
<j-modal visible title="导入" @cancel="emit('closeImport')" @ok="emit('closeImport')" :width="800" maskClosable="false">
|
||||||
|
<div class="import-content">
|
||||||
|
<div class="column">
|
||||||
|
<p>上传文件</p>
|
||||||
|
<div class="import">
|
||||||
|
<a-upload-dragger v-model:fileList="fileList" name="file" :action="`${FILE_UPLOAD}?options=tempFile`"
|
||||||
|
:headers="{
|
||||||
|
'X-Access-Token': LocalStore.get(TOKEN_KEY),
|
||||||
|
}" :limit="1" :showUploadList="false" @change="uploadChange" :accept="['xlsx', 'xls', 'csv']"
|
||||||
|
:before-upload="beforeUpload">
|
||||||
|
<div class="dragger-box">
|
||||||
|
<AIcon class="icon" type="PlusCircleFilled" />
|
||||||
|
<span style="margin: 16px 0 8px 0">点击或拖拽上传文件</span>
|
||||||
|
<span>格式:.xlsx, .csv</span>
|
||||||
|
</div>
|
||||||
|
</a-upload-dragger>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="importing-status" v-if="importStatus == 'importing'">
|
||||||
|
<AIcon type="LoadingOutlined" />
|
||||||
|
正在导入
|
||||||
|
</div>
|
||||||
|
<div class="column" v-if="importStatus != 'wait'">
|
||||||
|
<p>
|
||||||
|
<AIcon style="color: #00a4ff" type="CheckOutlined"/>导入成功 总数量
|
||||||
|
{{ successNumber }}
|
||||||
|
</p>
|
||||||
|
<span v-if="failNumber">
|
||||||
|
<AIcon style="color: #e50012" type="CloseOutlined"/>导入失败 总数量
|
||||||
|
{{ failNumber }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="column">
|
||||||
|
<p>Excel导入模板</p>
|
||||||
|
<div class="file-download">
|
||||||
|
<j-button @click="downTemplate('xlsx')" class="btn">
|
||||||
|
下载模板
|
||||||
|
</j-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</j-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { FILE_UPLOAD } from '@/api/comm';
|
||||||
|
import { TOKEN_KEY, BASE_API_PATH } from '@/utils/variable';
|
||||||
|
import { LocalStore, onlyMessage } from '@/utils/comm';
|
||||||
|
import {
|
||||||
|
exportTemplate
|
||||||
|
} from '@/api/data-collect/collector';
|
||||||
|
import { downloadFileByUrl } from '@/utils/utils';
|
||||||
|
import { getToken } from '@/utils/comm';
|
||||||
|
const props = defineProps({
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
default: {}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const emit = defineEmits(['closeImport'])
|
||||||
|
const fileList = ref()
|
||||||
|
type ImportStatus = 'wait' | 'importing' | 'done';
|
||||||
|
const importStatus = ref<ImportStatus>('wait'); //导入进度
|
||||||
|
const successNumber = ref(0); //导入成功数量
|
||||||
|
const failNumber = ref(0); //导入失败数量
|
||||||
|
const errorMessage = ref();
|
||||||
|
const detailFile = ref('')
|
||||||
|
const uploadChange = async (info: Record<string, any>) => {
|
||||||
|
if (info.file.status === 'done') {
|
||||||
|
const resp: any = info.file.response || { result: '' };
|
||||||
|
handleImport(resp)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const beforeUpload = (_file: any) => {
|
||||||
|
const isCsv = _file.type === 'text/csv';
|
||||||
|
const isXlsx =
|
||||||
|
_file.type ===
|
||||||
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
|
||||||
|
if (!isCsv && !isXlsx) {
|
||||||
|
onlyMessage('请上传.csv或.xlsx格式文件', 'warning');
|
||||||
|
}
|
||||||
|
return isCsv || isXlsx;
|
||||||
|
};
|
||||||
|
const downTemplate = async (type: string) => {
|
||||||
|
const res: any = await exportTemplate(<string>props.data?.provider, type);
|
||||||
|
if (res) {
|
||||||
|
const blob = new Blob([res], { type: type });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
downloadFileByUrl(url, `${props.data?.provider}导入模版`, type);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const handleImport = async (file: any) => {
|
||||||
|
let message: any = []
|
||||||
|
importStatus.value = 'importing';
|
||||||
|
let event: EventSource
|
||||||
|
event = new EventSource(
|
||||||
|
`${BASE_API_PATH}/data-collect/point/${props.data?.collectorId
|
||||||
|
}/${props.data?.provider}/import?:X_Access_Token=${getToken()
|
||||||
|
}&fileUrl=${file.result}`,
|
||||||
|
{ withCredentials: true },
|
||||||
|
);
|
||||||
|
event.onopen = (e) => {
|
||||||
|
// pushMessage.value = []
|
||||||
|
console.log('open');
|
||||||
|
};
|
||||||
|
event.onmessage = (e) => {
|
||||||
|
console.log(e, '123')
|
||||||
|
const result = JSON.parse(e.data);
|
||||||
|
if (result.success) {
|
||||||
|
successNumber.value++;
|
||||||
|
} else {
|
||||||
|
if (result.rowNumber !== -1) {
|
||||||
|
failNumber.value++;
|
||||||
|
message.push({
|
||||||
|
rowNumber: `第${result.rowNumber}行`,
|
||||||
|
message: result.message,
|
||||||
|
name: result.name
|
||||||
|
})
|
||||||
|
errorMessage.value = JSON.stringify(message)
|
||||||
|
} else {
|
||||||
|
detailFile.value = result.detailFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
event.onerror = (err) => {
|
||||||
|
importStatus.value = 'done';
|
||||||
|
event.close();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.import-content {
|
||||||
|
width: 100%;
|
||||||
|
flex-direction: column;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: flex-start;
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
.column {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
span {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
.importing-status {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
&.last {
|
||||||
|
margin-top: 135px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-upload-select) {
|
||||||
|
width: 100% !important;
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-row {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-btn) {
|
||||||
|
width: 100%;
|
||||||
|
background-color: transparent;
|
||||||
|
|
||||||
|
&.ant-btn-link {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dragger-box {
|
||||||
|
margin: 46px 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
color: #666666;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
font-size: 30px;
|
||||||
|
color: @primary-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-download {
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
border: none;
|
||||||
|
background-color: #ECECF0;
|
||||||
|
width: 152px;
|
||||||
|
color: #666666;
|
||||||
|
}
|
||||||
|
}</style>
|
|
@ -81,6 +81,48 @@
|
||||||
v-model:value="formData.value"
|
v-model:value="formData.value"
|
||||||
/>
|
/>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
|
<j-form-item
|
||||||
|
:label="data.name"
|
||||||
|
name="value"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: `请输入${data.name}`,
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
v-else-if="data.provider === 'snap7'"
|
||||||
|
>
|
||||||
|
<j-input-number
|
||||||
|
v-if="s7Type.includes(data.configuration?.type)"
|
||||||
|
style="width: 100%"
|
||||||
|
placeholder="请输入"
|
||||||
|
v-model:value="formData.value"
|
||||||
|
/>
|
||||||
|
<j-select
|
||||||
|
v-else-if="['Bool'].includes(data.configuration?.type)"
|
||||||
|
style="width: 100%"
|
||||||
|
v-model:value="formData.value"
|
||||||
|
:options="[
|
||||||
|
{
|
||||||
|
label: '是',
|
||||||
|
value: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '否',
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
placeholder="请选择"
|
||||||
|
allowClear
|
||||||
|
show-search
|
||||||
|
:filter-option="filterOption"
|
||||||
|
/>
|
||||||
|
<j-input
|
||||||
|
v-else
|
||||||
|
placeholder="请输入"
|
||||||
|
v-model:value="formData.value"
|
||||||
|
/>
|
||||||
|
</j-form-item>
|
||||||
<j-form-item
|
<j-form-item
|
||||||
:label="data.name"
|
:label="data.name"
|
||||||
name="value"
|
name="value"
|
||||||
|
@ -176,6 +218,19 @@ const valueTypeArray = [
|
||||||
'number',
|
'number',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const s7Type =[
|
||||||
|
'Byte',
|
||||||
|
'Word',
|
||||||
|
'DWord',
|
||||||
|
'USInt',
|
||||||
|
'SInt',
|
||||||
|
'Int',
|
||||||
|
'UDInt',
|
||||||
|
'DInt',
|
||||||
|
'Real',
|
||||||
|
'LReal'
|
||||||
|
]
|
||||||
|
|
||||||
const emit = defineEmits(['change']);
|
const emit = defineEmits(['change']);
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const formRef = ref<FormInstance>();
|
const formRef = ref<FormInstance>();
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
<template #headerTitle>
|
<template #headerTitle>
|
||||||
<j-space>
|
<j-space>
|
||||||
<PermissionButton
|
<PermissionButton
|
||||||
v-if="['MODBUS_TCP', 'COLLECTOR_GATEWAY'].includes(data?.provider)"
|
v-if="['MODBUS_TCP', 'COLLECTOR_GATEWAY','snap7'].includes(data?.provider)"
|
||||||
type="primary"
|
type="primary"
|
||||||
@click="handlAdd"
|
@click="handlAdd"
|
||||||
hasPermission="DataCollect/Collector:add"
|
hasPermission="DataCollect/Collector:add"
|
||||||
|
@ -30,7 +30,17 @@
|
||||||
/></template>
|
/></template>
|
||||||
新增点位
|
新增点位
|
||||||
</PermissionButton>
|
</PermissionButton>
|
||||||
|
<PermissionButton
|
||||||
|
v-if="['MODBUS_TCP', 'COLLECTOR_GATEWAY','snap7'].includes(data?.provider)"
|
||||||
|
type="primary"
|
||||||
|
@click="handleImport"
|
||||||
|
hasPermission="DataCollect/Collector:add"
|
||||||
|
>
|
||||||
|
<!-- <template #icon
|
||||||
|
><AIcon type="PlusOutlined"
|
||||||
|
/></template> -->
|
||||||
|
批量导入
|
||||||
|
</PermissionButton>
|
||||||
<PermissionButton
|
<PermissionButton
|
||||||
v-if="data?.provider === 'OPC_UA'"
|
v-if="data?.provider === 'OPC_UA'"
|
||||||
type="primary"
|
type="primary"
|
||||||
|
@ -150,13 +160,7 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #img>
|
<template #img>
|
||||||
<img
|
<img :src="ImageMap.get(slotProps.provider)"/>
|
||||||
:src="
|
|
||||||
slotProps.provider === 'OPC_UA'
|
|
||||||
? opcImage
|
|
||||||
: modbusImage
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
<div class="card-box-content">
|
<div class="card-box-content">
|
||||||
|
@ -319,7 +323,9 @@
|
||||||
:data="current"
|
:data="current"
|
||||||
@change="saveChange"
|
@change="saveChange"
|
||||||
/>
|
/>
|
||||||
|
<SaveS7 v-if="visible.saveS7" :data="current" @change="saveChange"/>
|
||||||
<Scan v-if="visible.scan" :data="current" @change="saveChange" />
|
<Scan v-if="visible.scan" :data="current" @change="saveChange" />
|
||||||
|
<Import v-if="visible.import" :data="current" @close-import="closeImport"/>
|
||||||
</j-spin>
|
</j-spin>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup name="PointPage">
|
<script lang="ts" setup name="PointPage">
|
||||||
|
@ -340,12 +346,13 @@ import SaveModBus from './Save/SaveModBus.vue';
|
||||||
import SaveOPCUA from './Save/SaveOPCUA.vue';
|
import SaveOPCUA from './Save/SaveOPCUA.vue';
|
||||||
import Scan from './Scan/index.vue';
|
import Scan from './Scan/index.vue';
|
||||||
import { colorMap } from '../data';
|
import { colorMap } from '../data';
|
||||||
import { cloneDeep, isNumber, throttle } from 'lodash-es';
|
import { cloneDeep, isBoolean, isNumber, throttle } from 'lodash-es';
|
||||||
import { getWebSocket } from '@/utils/websocket';
|
import { getWebSocket } from '@/utils/websocket';
|
||||||
import { map } from 'rxjs/operators';
|
import { map } from 'rxjs/operators';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { responsiveArray } from 'ant-design-vue/lib/_util/responsiveObserve';
|
import { responsiveArray } from 'ant-design-vue/lib/_util/responsiveObserve';
|
||||||
|
import SaveS7 from './Save/SaveS7.vue';
|
||||||
|
import Import from './components/Import/index.vue'
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
data: {
|
data: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -357,12 +364,23 @@ const tableRef = ref<Record<string, any>>({});
|
||||||
const params = ref<Record<string, any>>({});
|
const params = ref<Record<string, any>>({});
|
||||||
const opcImage = getImage('/DataCollect/device-opcua.png');
|
const opcImage = getImage('/DataCollect/device-opcua.png');
|
||||||
const modbusImage = getImage('/DataCollect/device-modbus.png');
|
const modbusImage = getImage('/DataCollect/device-modbus.png');
|
||||||
|
const s7Image = getImage('/DataCollect/s7.png')
|
||||||
|
const gatewayImage = getImage('/DataCollect/gateway.png')
|
||||||
|
const ImageMap = new Map()
|
||||||
|
ImageMap.set('OPC_UA',opcImage)
|
||||||
|
ImageMap.set('MODBUS_TCP',modbusImage)
|
||||||
|
ImageMap.set('snap7',s7Image)
|
||||||
|
ImageMap.set('COLLECTOR_GATEWAY',gatewayImage)
|
||||||
|
|
||||||
|
|
||||||
const visible = reactive({
|
const visible = reactive({
|
||||||
saveModBus: false,
|
saveModBus: false,
|
||||||
saveOPCUA: false,
|
saveOPCUA: false,
|
||||||
writePoint: false,
|
writePoint: false,
|
||||||
batchUpdate: false,
|
batchUpdate: false,
|
||||||
scan: false,
|
scan: false,
|
||||||
|
saveS7:false,
|
||||||
|
import:false
|
||||||
});
|
});
|
||||||
const current: any = ref({});
|
const current: any = ref({});
|
||||||
const accessModesOption = ref();
|
const accessModesOption = ref();
|
||||||
|
@ -474,19 +492,35 @@ const clickBatch = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlAdd = () => {
|
const handlAdd = () => {
|
||||||
visible.saveModBus = true;
|
if( props.data?.provider === 'snap7'){
|
||||||
current.value = {
|
console.log(props.data)
|
||||||
collectorId: props.data?.id,
|
visible.saveS7 = true
|
||||||
provider: props.data?.provider || 'MODBUS_TCP',
|
current.value = {
|
||||||
};
|
collectorId: props.data?.id,
|
||||||
|
provider: props.data?.provider,
|
||||||
|
deviceType:props.data?.configuration.type,
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
visible.saveModBus = true;
|
||||||
|
current.value = {
|
||||||
|
collectorId: props.data?.id,
|
||||||
|
provider: props.data?.provider || 'MODBUS_TCP',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
const handlEdit = (data: any) => {
|
const handlEdit = (data: any) => {
|
||||||
if (data?.provider === 'OPC_UA') {
|
if (data?.provider === 'OPC_UA') {
|
||||||
visible.saveOPCUA = true;
|
visible.saveOPCUA = true;
|
||||||
} else {
|
} else if(data?.provider === 'snap7'){
|
||||||
|
visible.saveS7 = true
|
||||||
|
}else{
|
||||||
visible.saveModBus = true;
|
visible.saveModBus = true;
|
||||||
}
|
}
|
||||||
current.value = cloneDeep(data);
|
current.value = cloneDeep({
|
||||||
|
...data,
|
||||||
|
deviceType:props.data?.configuration.type,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlDelete = async (id: string | undefined = undefined) => {
|
const handlDelete = async (id: string | undefined = undefined) => {
|
||||||
|
@ -519,6 +553,10 @@ const handlScan = () => {
|
||||||
visible.scan = true;
|
visible.scan = true;
|
||||||
current.value = cloneDeep(props.data);
|
current.value = cloneDeep(props.data);
|
||||||
};
|
};
|
||||||
|
const handleImport = () =>{
|
||||||
|
visible.import = true
|
||||||
|
current.value = cloneDeep(props.data)
|
||||||
|
}
|
||||||
const clickEdit = async (data: object) => {
|
const clickEdit = async (data: object) => {
|
||||||
visible.writePoint = true;
|
visible.writePoint = true;
|
||||||
current.value = cloneDeep(data);
|
current.value = cloneDeep(data);
|
||||||
|
@ -526,11 +564,13 @@ const clickEdit = async (data: object) => {
|
||||||
|
|
||||||
// ReadIdMap
|
// ReadIdMap
|
||||||
const clickRead = async (data: any) => {
|
const clickRead = async (data: any) => {
|
||||||
|
console.log('------')
|
||||||
const res: any = await readPoint(data?.collectorId, [data?.id]);
|
const res: any = await readPoint(data?.collectorId, [data?.id]);
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
const readData: any = res.result[0];
|
const readData: any = res.result[0];
|
||||||
const _data = ReadIdMap.get(data?.id);
|
const _data = ReadIdMap.get(data?.id);
|
||||||
ReadIdMap.set(data?.id, { ..._data, ...readData });
|
ReadIdMap.set(data?.id, { ..._data, ...readData });
|
||||||
|
console.log('====',ReadIdMap.get(data.id))
|
||||||
cancelSelect();
|
cancelSelect();
|
||||||
tableRef.value?.reload();
|
tableRef.value?.reload();
|
||||||
onlyMessage('操作成功', 'success');
|
onlyMessage('操作成功', 'success');
|
||||||
|
@ -574,7 +614,12 @@ const getReadParseData = (item: any) => {
|
||||||
let _data = '--';
|
let _data = '--';
|
||||||
if (ReadIdMap.has(item.id)) {
|
if (ReadIdMap.has(item.id)) {
|
||||||
const { parseData, dataType } = ReadIdMap.get(item.id);
|
const { parseData, dataType } = ReadIdMap.get(item.id);
|
||||||
_data = !!parseData ? `${parseData}(${dataType || '-'}) ` : '--';
|
if(isBoolean(parseData)){
|
||||||
|
_data = `${parseData}(${dataType || '-'}) `;
|
||||||
|
}else{
|
||||||
|
_data = !!parseData ? `${parseData}(${dataType || '-'}) ` : '--';
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return _data;
|
return _data;
|
||||||
};
|
};
|
||||||
|
@ -647,6 +692,11 @@ const onCheckAllChange = (e: any) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const closeImport = () =>{
|
||||||
|
visible.import = false
|
||||||
|
tableRef.value.reload()
|
||||||
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => tableRef?.value?._dataSource,
|
() => tableRef?.value?._dataSource,
|
||||||
(value) => {
|
(value) => {
|
||||||
|
|
|
@ -39,6 +39,33 @@
|
||||||
placeholder="请输入采集器名称"
|
placeholder="请输入采集器名称"
|
||||||
v-model:value="formData.name"
|
v-model:value="formData.name"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item v-if="provider === 'snap7'" label="IP" :name="['configuration', 'host']" :rules="LeftTreeRules.host" >
|
||||||
|
<j-input v-model:value="formData.configuration.host" autocomplete="off" placeholder="请输入通道IP" :disabled="false"/>
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item v-if="provider === 'snap7'" label="端口" :name="['configuration', 'port']" :rules="LeftTreeRules.port">
|
||||||
|
<j-input-number style="width: 100%" v-model:value="formData.configuration.port" :precision="0" autocomplete="off" placeholder="请输入通道端口"/>
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item v-if="provider === 'snap7'" label="机架号" :name="['configuration', 'rack']" :rules="LeftTreeRules.rack">
|
||||||
|
<j-input-number style="width: 100%" v-model:value="formData.configuration.rack" autocomplete="off" placeholder="请输入机架号" :maxlength="64" />
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item v-if="provider === 'snap7'" label="型号" :name="['configuration', 'type']" :rules="LeftTreeRules.type">
|
||||||
|
<j-select v-model:value="formData.configuration.type" placeholder="请选择型号" @change="typeChange">
|
||||||
|
<j-select-option v-for="item in typeOptions" :key="item.value" :value="item.value">{{ item.label }}</j-select-option>
|
||||||
|
</j-select>
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item v-if="provider === 'snap7'" label="槽位" :name="['configuration', 'slot']" :rules="LeftTreeRules.slot">
|
||||||
|
<j-input-number style="width: 100%" v-model:value="formData.configuration.slot" autocomplete="off" placeholder="请输入槽位" :maxlength="64" :disabled="formData.configuration.type == 'S200' || formData.configuration.type == 'S1200'"/>
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item v-if="provider === 'snap7'" label="超时时间(毫秒)" :name="['configuration', 'timeout']" :rules="LeftTreeRules.timeout">
|
||||||
|
<j-input-number style="width: 100%" v-model:value="formData.configuration.timeout" autocomplete="off" placeholder="请输入超时时间" :maxlength="64" />
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item v-if="provider === 'snap7'" label="数据读取方式" :name="['configuration', 'serializable']">
|
||||||
|
<j-radio-group v-model:value="formData.configuration.serializable">
|
||||||
|
<j-radio-button :value="false">并行</j-radio-button>
|
||||||
|
<j-radio-button :value="true">串行</j-radio-button>
|
||||||
|
</j-radio-group>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
<j-form-item
|
<j-form-item
|
||||||
v-if="provider === 'COLLECTOR_GATEWAY'"
|
v-if="provider === 'COLLECTOR_GATEWAY'"
|
||||||
|
@ -133,6 +160,7 @@
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<j-form-item
|
<j-form-item
|
||||||
|
v-if="provider !== 'snap7'"
|
||||||
:name="['configuration', 'requestTimeout']"
|
:name="['configuration', 'requestTimeout']"
|
||||||
:rules="LeftTreeRules.requestTimeout"
|
:rules="LeftTreeRules.requestTimeout"
|
||||||
>
|
>
|
||||||
|
@ -196,6 +224,14 @@ const props = defineProps({
|
||||||
|
|
||||||
const emit = defineEmits(['change']);
|
const emit = defineEmits(['change']);
|
||||||
|
|
||||||
|
const typeOptions = ref([
|
||||||
|
{value: 'S200', label: 'S7-200'},
|
||||||
|
{value: 'S300', label: 'S7-300'},
|
||||||
|
{value: 'S400', label: 'S7-400'},
|
||||||
|
{value: 'S1200', label: 'S7-1200'},
|
||||||
|
{value: 'S1500', label: 'S7-1500'},
|
||||||
|
])
|
||||||
|
|
||||||
const id = props.data.id;
|
const id = props.data.id;
|
||||||
const formRef = ref<FormInstance>();
|
const formRef = ref<FormInstance>();
|
||||||
const provider = ref()
|
const provider = ref()
|
||||||
|
@ -248,16 +284,17 @@ const endianData = computed(() => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const formData = ref({
|
const formData = ref<any>({
|
||||||
channelId: undefined,
|
channelId: undefined,
|
||||||
name: '',
|
name: '',
|
||||||
collectorProvider: undefined,
|
collectorProvider: undefined,
|
||||||
configuration: {
|
configuration: {
|
||||||
unitId: '',
|
unitId: '',
|
||||||
type: 'LowerFrequency',
|
type: undefined,
|
||||||
endian: 'BIG',
|
endian: 'BIG',
|
||||||
endianIn: 'BIG',
|
endianIn: 'BIG',
|
||||||
requestTimeout: 2000,
|
requestTimeout: 2000,
|
||||||
|
serializable:false,
|
||||||
inheritBreakerSpec: {
|
inheritBreakerSpec: {
|
||||||
type: 'LowerFrequency',
|
type: 'LowerFrequency',
|
||||||
}
|
}
|
||||||
|
@ -347,10 +384,16 @@ const filterOption = (input: string, option: any) => {
|
||||||
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
|
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const typeChange = (val:any)=>{
|
||||||
|
if(val == 'S200' || val == 'S1200') {
|
||||||
|
formData.value.configuration.slot = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => formData.value.channelId,
|
() => formData.value.channelId,
|
||||||
(value) => {
|
(value) => {
|
||||||
const dt = _channelListAll.value.find((item) => item.id === value);
|
const dt:any = _channelListAll.value.find((item:any) => item.id === value);
|
||||||
visibleUnitId.value = visibleEndian.value =
|
visibleUnitId.value = visibleEndian.value =
|
||||||
dt?.provider && ['MODBUS_TCP', 'COLLECTOR_GATEWAY'].includes(dt?.provider);
|
dt?.provider && ['MODBUS_TCP', 'COLLECTOR_GATEWAY'].includes(dt?.provider);
|
||||||
},
|
},
|
||||||
|
|
|
@ -22,6 +22,28 @@ export const getState = (record: any) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const regOnlyNumber = new RegExp(/^\d+$/);
|
export const regOnlyNumber = new RegExp(/^\d+$/);
|
||||||
|
export const regular = {
|
||||||
|
// IPV4 IPV6 域名
|
||||||
|
IP_Domain: /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$|^(?:(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){6})(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:::(?:(?:(?:[0-9a-fA-F]{1,4})):){5})(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})))?::(?:(?:(?:[0-9a-fA-F]{1,4})):){4})(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){0,1}(?:(?:[0-9a-fA-F]{1,4})))?::(?:(?:(?:[0-9a-fA-F]{1,4})):){3})(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){0,2}(?:(?:[0-9a-fA-F]{1,4})))?::(?:(?:(?:[0-9a-fA-F]{1,4})):){2})(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){0,3}(?:(?:[0-9a-fA-F]{1,4})))?::(?:(?:[0-9a-fA-F]{1,4})):)(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){0,4}(?:(?:[0-9a-fA-F]{1,4})))?::)(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){0,5}(?:(?:[0-9a-fA-F]{1,4})))?::)(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){0,6}(?:(?:[0-9a-fA-F]{1,4})))?::))))$/,
|
||||||
|
// IP地址
|
||||||
|
IP_URL: /^((2((5[0-5])|([0-4]\d)))|([0-1]?\d{1,2}))(\.((2((5[0-5])|([0-4]\d)))|([0-1]?\d{1,2}))){3}$/,
|
||||||
|
// 英文或者数字或者-或者_
|
||||||
|
EN_NUMBER: /^[A-Za-z0-9_-]+$/,
|
||||||
|
// 服务器地址
|
||||||
|
HTTP_URL: /^http:|https:\/\/([\w-]+\.)+[\w-]+(\/[\w-./?%&=]*)?$/,
|
||||||
|
// 域名
|
||||||
|
DOMAIN_NAME: /^(?:(?:(?:[a-zA-z\-]+)\:\/{1,3})?(?:[a-zA-Z0-9])(?:[a-zA-Z0-9-\.]){1,61}[a-zA-Z0-9](?:\.[a-zA-Z]{2,})+|\[(?:(?:(?:[a-fA-F0-9]){1,4})(?::(?:[a-fA-F0-9]){1,4}){7}|::1|::)\]|(?:(?:[0-9]{1,3})(?:\.[0-9]{1,3}){3}))(?:\:[0-9]{1,5})?$/,
|
||||||
|
// 数字
|
||||||
|
// NUMBER: /^[0-9]*[1-9][0-9]*$/,
|
||||||
|
NUMBER: /^([1-9]\d*|[0]{1,1})$/,
|
||||||
|
// 正数、负数、小数
|
||||||
|
ASSIGNMENT: /^(\-)?\d+(\.\d+)?$/,
|
||||||
|
//密码验证
|
||||||
|
PASSWORD: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[^]{8,}$/,
|
||||||
|
//IP子网掩码
|
||||||
|
IP_MASK: /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(\/\d{1,2})?$/,
|
||||||
|
CronRegEx: new RegExp('^\\s*($|#|\\w+\\s*=|(\\?|\\*|(?:[0-5]?\\d)(?:(?:-|\\/|\\,)(?:[0-5]?\\d))?(?:,(?:[0-5]?\\d)(?:(?:-|\\/|\\,)(?:[0-5]?\\d))?)*)\\s+(\\?|\\*|(?:[0-5]?\\d)(?:(?:-|\\/|\\,)(?:[0-5]?\\d))?(?:,(?:[0-5]?\\d)(?:(?:-|\\/|\\,)(?:[0-5]?\\d))?)*)\\s+(\\?|\\*|(?:[01]?\\d|2[0-3])(?:(?:-|\\/|\\,)(?:[01]?\\d|2[0-3]))?(?:,(?:[01]?\\d|2[0-3])(?:(?:-|\\/|\\,)(?:[01]?\\d|2[0-3]))?)*)\\s+(\\?|\\*|(?:0?[1-9]|[12]\\d|3[01])(?:(?:-|\\/|\\,)(?:0?[1-9]|[12]\\d|3[01]))?(?:,(?:0?[1-9]|[12]\\d|3[01])(?:(?:-|\\/|\\,)(?:0?[1-9]|[12]\\d|3[01]))?)*)\\s+(\\?|\\*|(?:[1-9]|1[012])(?:(?:-|\\/|\\,)(?:[1-9]|1[012]))?(?:L|W)?(?:,(?:[1-9]|1[012])(?:(?:-|\\/|\\,)(?:[1-9]|1[012]))?(?:L|W)?)*|\\?|\\*|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(?:(?:-)(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))?(?:,(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(?:(?:-)(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))?)*)\\s+(\\?|\\*|(?:[0-6])(?:(?:-|\\/|\\,|#)(?:[0-6]))?(?:L)?(?:,(?:[0-6])(?:(?:-|\\/|\\,|#)(?:[0-6]))?(?:L)?)*|\\?|\\*|(?:MON|TUE|WED|THU|FRI|SAT|SUN)(?:(?:-)(?:MON|TUE|WED|THU|FRI|SAT|SUN))?(?:,(?:MON|TUE|WED|THU|FRI|SAT|SUN)(?:(?:-)(?:MON|TUE|WED|THU|FRI|SAT|SUN))?)*)(|\\s)+(\\?|\\*|(?:|\\d{4})(?:(?:-|\\/|\\,)(?:|\\d{4}))?(?:,(?:|\\d{4})(?:(?:-|\\/|\\,)(?:|\\d{4}))?)*))$',)
|
||||||
|
}
|
||||||
|
|
||||||
export const checkProviderData = {
|
export const checkProviderData = {
|
||||||
int8: 1,
|
int8: 1,
|
||||||
|
@ -150,6 +172,73 @@ export const OPCUARules = {
|
||||||
description: [{ max: 200, message: '最多可输入200个字符' }],
|
description: [{ max: 200, message: '最多可输入200个字符' }],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const validatorUrl = (rule:any, value:any, callback:any) => {
|
||||||
|
const reg = regular.DOMAIN_NAME
|
||||||
|
const reg1 = regular.IP_URL
|
||||||
|
if(value === undefined || value === '' || value === null) {
|
||||||
|
return Promise.reject("请输入通道Ip")
|
||||||
|
} else {
|
||||||
|
if(reg.test(value) === false && reg1.test(value) === false) {
|
||||||
|
return Promise.reject("请输入正确的域名或ip地址")
|
||||||
|
}
|
||||||
|
return Promise.resolve()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 校验通道端口
|
||||||
|
*/
|
||||||
|
const validator1 = (rule:any, value:any, callback:any) => {
|
||||||
|
if(value === undefined || value === '' || value === null) {
|
||||||
|
return Promise.reject("请输入通道端口")
|
||||||
|
} else {
|
||||||
|
if(value < 0 || value > 65535) {
|
||||||
|
return Promise.reject("请输入0~65535的整数")
|
||||||
|
}
|
||||||
|
return Promise.resolve()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验机架号
|
||||||
|
*/
|
||||||
|
const validator2 = (rule:any, value:any, callback:any) => {
|
||||||
|
if(value === undefined || value === '' || value === null) {
|
||||||
|
return Promise.reject("请输入机架号")
|
||||||
|
} else {
|
||||||
|
if(value < 0 || value > 65535) {
|
||||||
|
return Promise.reject("请输入0~65535的整数")
|
||||||
|
}
|
||||||
|
return Promise.resolve()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验槽位
|
||||||
|
*/
|
||||||
|
const validator3 = (rule:any, value:any, callback:any) => {
|
||||||
|
if(value === undefined || value === '' || value === null) {
|
||||||
|
return Promise.reject("请输入槽位")
|
||||||
|
} else {
|
||||||
|
if(value < 0 || value > 65535) {
|
||||||
|
return Promise.reject("请输入0~65535的整数")
|
||||||
|
}
|
||||||
|
return Promise.resolve()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验超时时间
|
||||||
|
*/
|
||||||
|
const validator4 = (rule:any, value:any, callback:any) => {
|
||||||
|
if(value === undefined || value === '' || value === null) {
|
||||||
|
return Promise.reject("请输入超时时间")
|
||||||
|
} else {
|
||||||
|
if(value < 0 || value > 65535) {
|
||||||
|
return Promise.reject("请输入0~65535的整数")
|
||||||
|
}
|
||||||
|
return Promise.resolve()
|
||||||
|
}
|
||||||
|
}
|
||||||
export const LeftTreeRules = {
|
export const LeftTreeRules = {
|
||||||
channelId: [{ required: true, message: '请选择所属通道', trigger: 'blur' }],
|
channelId: [{ required: true, message: '请选择所属通道', trigger: 'blur' }],
|
||||||
name: [
|
name: [
|
||||||
|
@ -163,7 +252,7 @@ export const LeftTreeRules = {
|
||||||
message: '请输入0-255之间的正整数',
|
message: '请输入0-255之间的正整数',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
type: [{ required: true, message: '请选择处理方式', trigger: 'blur' }],
|
// type: [{ required: true, message: '请选择处理方式', trigger: 'blur' }],
|
||||||
endian: [
|
endian: [
|
||||||
{ required: true, message: '请选择双字高低位切换', trigger: 'blur' },
|
{ required: true, message: '请选择双字高低位切换', trigger: 'blur' },
|
||||||
],
|
],
|
||||||
|
@ -172,7 +261,16 @@ export const LeftTreeRules = {
|
||||||
],
|
],
|
||||||
requestTimeout:[
|
requestTimeout:[
|
||||||
{ pattern: /^\d+$/, message:'请输入2000-60000的正整数',trigger: 'change'}
|
{ pattern: /^\d+$/, message:'请输入2000-60000的正整数',trigger: 'change'}
|
||||||
]
|
],
|
||||||
|
host: [
|
||||||
|
{ required: true, trigger: 'blur', validator: validatorUrl, },
|
||||||
|
],
|
||||||
|
port: [{ required: true, trigger: 'blur' , validator: validator1}],
|
||||||
|
rack: [{ required: true, trigger: 'blur', validator: validator2 }],
|
||||||
|
slot: [{ required: true, trigger: 'blur', validator: validator3 }],
|
||||||
|
timeout: [{ required: true, trigger: 'blur', validator: validator4 }],
|
||||||
|
type: [{required: true, trigger: 'change', message: '请选择型号'}],
|
||||||
|
serializable: [{required: true, trigger: 'change', message: '请选择型号'}],
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FormTableColumns = [
|
export const FormTableColumns = [
|
||||||
|
@ -216,3 +314,5 @@ export const FormTableColumns = [
|
||||||
width: 50,
|
width: 50,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<j-modal
|
<j-modal visible title="详情" width="754px" @cancel="emits('update:visible', false)" class="view-dialog-container">
|
||||||
visible
|
|
||||||
title="详情"
|
|
||||||
width="754px"
|
|
||||||
@cancel="emits('update:visible', false)"
|
|
||||||
class="view-dialog-container"
|
|
||||||
>
|
|
||||||
<template v-if="['device-transparent-codec', 'system-event'].includes(data?.topicProvider)">
|
<template v-if="['device-transparent-codec', 'system-event'].includes(data?.topicProvider)">
|
||||||
<div>
|
<div>
|
||||||
<div class="label">通知流水:</div>
|
<div class="label">通知流水:</div>
|
||||||
|
@ -16,20 +10,50 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
<template
|
||||||
|
v-else-if="['workflow-task-cc', 'workflow-task-todo', 'workflow-task-reject', 'workflow-process-finish', 'workflow-process-repealed','workflow-task-transfer-todo'].includes(data?.topicProvider)">
|
||||||
|
<j-descriptions :column="2" :contentStyle="{
|
||||||
|
color: '#333333',
|
||||||
|
}" :labelStyle="{
|
||||||
|
color: 'rgba(0, 0, 0, 0.6)',
|
||||||
|
width: '72px',
|
||||||
|
}">
|
||||||
|
<j-descriptions-item label="发起人">
|
||||||
|
<j-ellipsis>{{ workFlowData?.creatorName }}</j-ellipsis>
|
||||||
|
</j-descriptions-item>
|
||||||
|
<j-descriptions-item label="发起时间">
|
||||||
|
<j-ellipsis>
|
||||||
|
{{ dayjs(workFlowData?.createTime).format('YYYY-MM-DD HH:mm:ss') }}
|
||||||
|
</j-ellipsis>
|
||||||
|
</j-descriptions-item>
|
||||||
|
<j-descriptions-item label="流程分类">
|
||||||
|
<j-ellipsis>
|
||||||
|
{{ workFlowData?.classifiedName }}
|
||||||
|
</j-ellipsis>
|
||||||
|
</j-descriptions-item>
|
||||||
|
<j-descriptions-item label="流程名称"><j-ellipsis>
|
||||||
|
{{ workFlowData?.modelName }}
|
||||||
|
</j-ellipsis></j-descriptions-item>
|
||||||
|
<j-descriptions-item label="标题"><j-ellipsis>
|
||||||
|
{{ workFlowData?.name }}
|
||||||
|
</j-ellipsis></j-descriptions-item>
|
||||||
|
<j-descriptions-item label="摘要">
|
||||||
|
<j-ellipsis>
|
||||||
|
{{ workFlowData?.summary }}
|
||||||
|
</j-ellipsis>
|
||||||
|
</j-descriptions-item>
|
||||||
|
</j-descriptions>
|
||||||
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<j-descriptions
|
<j-descriptions :column="2" :contentStyle="{
|
||||||
:column="2"
|
color: '#333333',
|
||||||
:contentStyle="{
|
}" :labelStyle="{
|
||||||
color: '#333333',
|
color: 'rgba(0, 0, 0, 0.6)',
|
||||||
}"
|
width: '72px',
|
||||||
:labelStyle="{
|
}">
|
||||||
color: 'rgba(0, 0, 0, 0.6)',
|
|
||||||
width: '72px',
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<template v-if="data?.topicProvider === 'alarm-device'">
|
<template v-if="data?.topicProvider === 'alarm-device'">
|
||||||
<j-descriptions-item label="告警设备">
|
<j-descriptions-item label="告警设备">
|
||||||
<j-ellipsis>{{ _data?.targetName || ''}}</j-ellipsis>
|
<j-ellipsis>{{ _data?.targetName || '' }}</j-ellipsis>
|
||||||
</j-descriptions-item>
|
</j-descriptions-item>
|
||||||
<j-descriptions-item label="设备ID">
|
<j-descriptions-item label="设备ID">
|
||||||
<j-ellipsis>
|
<j-ellipsis>
|
||||||
|
@ -58,10 +82,7 @@
|
||||||
<div class="label">告警流水:</div>
|
<div class="label">告警流水:</div>
|
||||||
<div style="padding: 10px; background-color: #fafafa">
|
<div style="padding: 10px; background-color: #fafafa">
|
||||||
<j-scrollbar height="200px">
|
<j-scrollbar height="200px">
|
||||||
<JsonViewer
|
<JsonViewer style="background-color: #fafafa" :value="JSON.parse(_data?.alarmInfo || '{}')" />
|
||||||
style="background-color: #fafafa"
|
|
||||||
:value="JSON.parse(_data?.alarmInfo || '{}')"
|
|
||||||
/>
|
|
||||||
</j-scrollbar>
|
</j-scrollbar>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -77,6 +98,7 @@ import { JsonViewer } from 'vue3-json-viewer';
|
||||||
import 'vue3-json-viewer/dist/index.css';
|
import 'vue3-json-viewer/dist/index.css';
|
||||||
import { queryLevel as queryLevel_api } from '@/api/rule-engine/config';
|
import { queryLevel as queryLevel_api } from '@/api/rule-engine/config';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
import { getWorkflowNotice } from '@/api/account/notificationRecord';
|
||||||
|
|
||||||
const emits = defineEmits(['update:visible']);
|
const emits = defineEmits(['update:visible']);
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
@ -86,6 +108,8 @@ const props = defineProps<{
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const levelList = ref<any[]>([]);
|
const levelList = ref<any[]>([]);
|
||||||
|
//工作流详情的值
|
||||||
|
const workFlowData = ref()
|
||||||
|
|
||||||
const _data = computed(() => {
|
const _data = computed(() => {
|
||||||
if (props.data.detailJson) return JSON.parse(props.data.detailJson);
|
if (props.data.detailJson) return JSON.parse(props.data.detailJson);
|
||||||
|
@ -103,9 +127,22 @@ const getLevelLabel = (id: number) => {
|
||||||
return obj?.title;
|
return obj?.title;
|
||||||
};
|
};
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if(!['device-transparent-codec', 'system-event'].includes(props?.data?.topicProvider)){
|
if (!['device-transparent-codec', 'system-event'].includes(props?.data?.topicProvider)) {
|
||||||
getLevel();
|
getLevel();
|
||||||
}
|
}
|
||||||
|
if (['workflow-task-cc', 'workflow-task-todo', 'workflow-task-reject', 'workflow-process-finish', 'workflow-process-repealed','workflow-task-transfer-todo'].includes(props?.data?.topicProvider)) {
|
||||||
|
const params = {
|
||||||
|
terms: [{
|
||||||
|
type: "or",
|
||||||
|
value: ['workflow-process-finish', 'workflow-process-repealed'].includes(props?.data?.topicProvider) ? props?.data?.dataId : props?.data?.detailJson ? JSON.parse(props?.data?.detailJson)?.processId : props?.data?.detail?.processId,
|
||||||
|
termType: "eq",
|
||||||
|
column: "id"
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
getWorkflowNotice(params).then((res) => {
|
||||||
|
workFlowData.value = { 'topicProvider': props?.data?.topicProvider, ...res?.result?.[0] }
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -107,7 +107,10 @@ const getType = computed(() => {
|
||||||
return ['device-transparent-codec'];
|
return ['device-transparent-codec'];
|
||||||
} else if (props.type === 'system-monitor') {
|
} else if (props.type === 'system-monitor') {
|
||||||
return ['system-event'];
|
return ['system-event'];
|
||||||
} else {
|
} else if(props.type === 'workflow-notification'){
|
||||||
|
return ['workflow-task-cc','workflow-task-todo','workflow-task-reject', 'workflow-process-finish', 'workflow-process-repealed','workflow-task-transfer-todo']
|
||||||
|
}
|
||||||
|
else {
|
||||||
return [
|
return [
|
||||||
'alarm',
|
'alarm',
|
||||||
'alarm-product',
|
'alarm-product',
|
||||||
|
@ -125,6 +128,7 @@ const columns = [
|
||||||
key: 'topicProvider',
|
key: 'topicProvider',
|
||||||
search: {
|
search: {
|
||||||
type: 'select',
|
type: 'select',
|
||||||
|
termFilter: ['in', 'nin'],
|
||||||
options: () =>
|
options: () =>
|
||||||
getTypeList_api().then((resp: any) => {
|
getTypeList_api().then((resp: any) => {
|
||||||
return resp.result
|
return resp.result
|
||||||
|
@ -168,6 +172,7 @@ const columns = [
|
||||||
key: 'state',
|
key: 'state',
|
||||||
search: {
|
search: {
|
||||||
type: 'select',
|
type: 'select',
|
||||||
|
termFilter: ['in', 'nin'],
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
label: '未读',
|
label: '未读',
|
||||||
|
@ -217,8 +222,8 @@ const queryParams = ref({});
|
||||||
const tableRef = ref();
|
const tableRef = ref();
|
||||||
|
|
||||||
const view = (row: any) => {
|
const view = (row: any) => {
|
||||||
viewItem.value = row;
|
viewItem.value = row;
|
||||||
viewVisible.value = true;
|
viewVisible.value = true;
|
||||||
};
|
};
|
||||||
const refresh = () => {
|
const refresh = () => {
|
||||||
tableRef.value && tableRef.value.reload();
|
tableRef.value && tableRef.value.reload();
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import NotificationRecord from './components/NotificationRecord/index.vue';
|
import NotificationRecord from './components/NotificationRecord/index.vue';
|
||||||
import { initData } from '../data';
|
import { getInitData } from '../data';
|
||||||
import { getAllNotice } from '@/api/account/center';
|
import { getAllNotice } from '@/api/account/center';
|
||||||
import { useRouterParams } from '@/utils/hooks/useParams';
|
import { useRouterParams } from '@/utils/hooks/useParams';
|
||||||
import { useUserInfo } from '@/store/userInfo';
|
import { useUserInfo } from '@/store/userInfo';
|
||||||
|
@ -28,7 +28,7 @@ import { useUserInfo } from '@/store/userInfo';
|
||||||
const tabs = ref<any[]>([]);
|
const tabs = ref<any[]>([]);
|
||||||
const router = useRouterParams();
|
const router = useRouterParams();
|
||||||
const user = useUserInfo();
|
const user = useUserInfo();
|
||||||
|
let initData:any[]
|
||||||
const queryTypeList = () => {
|
const queryTypeList = () => {
|
||||||
getAllNotice().then((resp: any) => {
|
getAllNotice().then((resp: any) => {
|
||||||
if (resp.status === 200) {
|
if (resp.status === 200) {
|
||||||
|
@ -73,6 +73,7 @@ watchEffect(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
initData = getInitData()
|
||||||
queryTypeList();
|
queryTypeList();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
|
@ -55,14 +55,15 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { getAllNotice } from '@/api/account/center';
|
import { getAllNotice } from '@/api/account/center';
|
||||||
import { getNoticeList_api } from '@/api/account/notificationSubscription';
|
import { getNoticeList_api } from '@/api/account/notificationSubscription';
|
||||||
import { initData } from '../data';
|
import { getInitData } from '../data';
|
||||||
import Item from './components/Item.vue';
|
import Item from './components/Item.vue';
|
||||||
|
import { useMenuStore } from '@/store/menu';
|
||||||
|
const menuStore = useMenuStore();
|
||||||
const subscribe = ref<any[]>([]);
|
const subscribe = ref<any[]>([]);
|
||||||
const dataSource = ref<any[]>([]);
|
const dataSource = ref<any[]>([]);
|
||||||
const activeKey = ref<string[]>(['alarm', 'system-monitor', 'system-business']);
|
const activeKey = ref<string[]>();
|
||||||
const loading = ref<boolean>(false)
|
const loading = ref<boolean>(false)
|
||||||
|
let initData:any[]
|
||||||
const handleSearch = () => {
|
const handleSearch = () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
getAllNotice().then((resp: any) => {
|
getAllNotice().then((resp: any) => {
|
||||||
|
@ -105,6 +106,12 @@ const handleSearch = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
const keys = ['alarm', 'system-monitor', 'system-business']
|
||||||
|
if (menuStore.hasMenu('process')) {
|
||||||
|
keys.push('workflow-notification')
|
||||||
|
}
|
||||||
|
activeKey.value = keys
|
||||||
|
initData = getInitData()
|
||||||
handleSearch();
|
handleSearch();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
const initData: any[] = [
|
import { useMenuStore } from '@/store/menu';
|
||||||
|
const menuStore = useMenuStore();
|
||||||
|
const systemNotice = [
|
||||||
{
|
{
|
||||||
provider: 'alarm',
|
provider: 'alarm',
|
||||||
name: '告警',
|
name: '告警',
|
||||||
|
@ -50,5 +52,38 @@ const initData: any[] = [
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
const workflowNotice = [
|
||||||
export { initData };
|
{
|
||||||
|
provider: 'workflow-notification',
|
||||||
|
name: '工作流通知',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
provider: 'workflow-task-todo',
|
||||||
|
name: '待办通知',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provider: 'workflow-task-reject',
|
||||||
|
name: '驳回通知',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provider: 'workflow-task-cc',
|
||||||
|
name: '抄送通知',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provider: 'workflow-process-finish',
|
||||||
|
name: '办结通知',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provider: 'workflow-process-repealed',
|
||||||
|
name: '关闭通知',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provider: 'workflow-task-transfer-todo',
|
||||||
|
name: '转办通知'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
export const getInitData = () =>{
|
||||||
|
return menuStore.hasMenu('process') ? [...systemNotice,...workflowNotice] : [...systemNotice]
|
||||||
|
}
|
||||||
|
|
|
@ -251,7 +251,7 @@ onUnmounted(() => {
|
||||||
.person-content-item {
|
.person-content-item {
|
||||||
padding: 10px 20px;
|
padding: 10px 20px;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
overflow: hidden;
|
// overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.person-content {
|
.person-content {
|
||||||
|
@ -261,7 +261,7 @@ onUnmounted(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
.person-content-item-content {
|
.person-content-item-content {
|
||||||
height: calc(100vh - 251px);
|
// height: calc(100vh - 251px);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 10px 0;
|
padding: 10px 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -155,7 +155,7 @@ const handleOk = () => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
btnLoading.value = true;
|
btnLoading.value = true;
|
||||||
bindDevice(_selectedRowKeys.value[0], [props.data.id] )
|
bindDevice(_selectedRowKeys.value[0], [props.data.id])
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
if(resp.status === 200){
|
if(resp.status === 200){
|
||||||
emit('ok', _selectedRowKeys.value[0]);
|
emit('ok', _selectedRowKeys.value[0]);
|
||||||
|
|
|
@ -1,28 +1,38 @@
|
||||||
<template>
|
<template>
|
||||||
<page-container>
|
<page-container>
|
||||||
|
<full-page>
|
||||||
<iframe
|
<iframe
|
||||||
|
v-if="loading"
|
||||||
:src="iframeUrl"
|
:src="iframeUrl"
|
||||||
|
scrolling="no"
|
||||||
frameBorder="0"
|
frameBorder="0"
|
||||||
style="width: 100%; height: calc(100vh - 140px)"
|
style="width: 100%; height: 100%"
|
||||||
></iframe>
|
></iframe>
|
||||||
|
</full-page>
|
||||||
</page-container>
|
</page-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script lang="ts" name="IframgePage" setup>
|
||||||
import { TOKEN_KEY } from '@/utils/variable';
|
import { TOKEN_KEY } from '@/utils/variable';
|
||||||
import { LocalStore } from '@/utils/comm';
|
import {LocalStore, getToken, cleanToken} from '@/utils/comm';
|
||||||
import { getAppInfo_api } from '@/api/system/apply';
|
import { getAppInfo_api } from '@/api/system/apply';
|
||||||
|
import { lowCodeUrl } from '@/api/comm'
|
||||||
|
import FullPage from "components/Layout/FullPage.vue";
|
||||||
|
import {onUnmounted} from "vue";
|
||||||
|
import router from "@/router";
|
||||||
|
import {LoginPath} from "@/router/menu";
|
||||||
|
import { TokenLose} from "@/utils/request";
|
||||||
|
|
||||||
const iframeUrl = ref<string>('');
|
const iframeUrl = ref<string>('');
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
const loading = ref(false)
|
||||||
const handle = async (appId: string, url: string) => {
|
const handle = async (appId: string, url: string) => {
|
||||||
const res = await getAppInfo_api(appId);
|
const res = await getAppInfo_api(appId);
|
||||||
let menuUrl: any = url;
|
let menuUrl: any = url;
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
const result = res.result
|
const result = res.result
|
||||||
if (result.page.routeType === 'hash') {
|
if (result.page.routeType === 'hash') {
|
||||||
menuUrl = url.startsWith('/') ? `#${url}` : `#/${url}`;
|
menuUrl = url.startsWith('/') ? `/#${url}` : `/#/${url}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.page.parameters) {
|
if (result.page.parameters) {
|
||||||
|
@ -42,9 +52,10 @@ const handle = async (appId: string, url: string) => {
|
||||||
const urlStandalone = `${result.page.baseUrl}/api/application/sso/${appId}/login?redirect=${menuUrl}?layout=false`;
|
const urlStandalone = `${result.page.baseUrl}/api/application/sso/${appId}/login?redirect=${menuUrl}?layout=false`;
|
||||||
iframeUrl.value = urlStandalone;
|
iframeUrl.value = urlStandalone;
|
||||||
} else if (result.provider === 'internal-integrated') {
|
} else if (result.provider === 'internal-integrated') {
|
||||||
|
const _url = menuUrl.startsWith('/') ? menuUrl : `/${menuUrl}`;
|
||||||
const tokenUrl = `${
|
const tokenUrl = `${
|
||||||
result.page.baseUrl
|
result.page.baseUrl
|
||||||
}/${menuUrl}?layout=false&X-Access-Token=${LocalStore.get(TOKEN_KEY)}`;
|
}${_url}?layout=false&X-Access-Token=${LocalStore.get(TOKEN_KEY)}`;
|
||||||
iframeUrl.value = tokenUrl;
|
iframeUrl.value = tokenUrl;
|
||||||
} else {
|
} else {
|
||||||
const urlOther = `${result.page.baseUrl}/${menuUrl}`;
|
const urlOther = `${result.page.baseUrl}/${menuUrl}`;
|
||||||
|
@ -53,13 +64,50 @@ const handle = async (appId: string, url: string) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const lowCode = () => {
|
||||||
|
lowCodeUrl().then(res => {
|
||||||
|
if (res.success && res.result) {
|
||||||
|
const url = res.result['ui-addr']
|
||||||
|
// const url = 'http://localhost:8080'
|
||||||
|
iframeUrl.value = url + '/#' + route.path + '?&token=' + getToken()
|
||||||
|
console.log(iframeUrl.value)
|
||||||
|
loading.value = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const onMessage = (msg: any) => {
|
||||||
|
console.log('onMessage',msg)
|
||||||
|
if (msg?.data?.token === 'LOSE') {
|
||||||
|
TokenLose()
|
||||||
|
setTimeout(() => {
|
||||||
|
cleanToken()
|
||||||
|
router.replace({
|
||||||
|
path: LoginPath
|
||||||
|
})
|
||||||
|
}, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
window.addEventListener('message', onMessage)
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('message', onMessage)
|
||||||
|
})
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
const matchedItem: any = route.matched?.[0]
|
const matchedItem: any = route.matched?.[0]
|
||||||
if (matchedItem?.meta?.isApp) {
|
if (route.meta?.isApp) {
|
||||||
const params = route.path.split('/')?.[1];
|
const params = route.path.split('/')?.[1];
|
||||||
const url = route.path.split('/').slice(2).join('/');
|
if (params === 'preview') {
|
||||||
handle(params, url);
|
lowCode()
|
||||||
|
} else {
|
||||||
|
loading.value = true
|
||||||
|
const url = route.path.split('/').slice(2).join('/');
|
||||||
|
handle(params, url);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -96,12 +96,26 @@ const menuCount = (menus: any[]) => {
|
||||||
return pre + _count;
|
return pre + _count;
|
||||||
}, 0);
|
}, 0);
|
||||||
};
|
};
|
||||||
|
/**
|
||||||
|
* 添加options show用于控制菜单是否显示函数
|
||||||
|
*/
|
||||||
|
const dealMenu = (data:any) =>{
|
||||||
|
data.forEach((item:any)=>{
|
||||||
|
item.options = Object.assign({
|
||||||
|
show: true
|
||||||
|
}, item?.options || {})
|
||||||
|
if(item.children){
|
||||||
|
dealMenu(item.children)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* 初始化菜单
|
* 初始化菜单
|
||||||
*/
|
*/
|
||||||
const initMenu = async () => {
|
const initMenu = async () => {
|
||||||
return new Promise(async (resolve) => {
|
return new Promise(async (resolve) => {
|
||||||
// 用户中心
|
// 用户中心
|
||||||
|
dealMenu(menuDatas.current)
|
||||||
console.log([...menuDatas.current!, USER_CENTER_MENU_DATA]);
|
console.log([...menuDatas.current!, USER_CENTER_MENU_DATA]);
|
||||||
const res = await updateMenus([...menuDatas.current!, USER_CENTER_MENU_DATA]);
|
const res = await updateMenus([...menuDatas.current!, USER_CENTER_MENU_DATA]);
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
|
|
|
@ -1,15 +1,31 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="init-home-role">
|
<div class="init-home-role">
|
||||||
|
<div class="built_in_group">
|
||||||
|
<div>平台角色内置分组</div>
|
||||||
|
<div class="group">
|
||||||
|
<div v-for="(item, index) in group" class="group_item" @mouseover="() => showButton(item)"
|
||||||
|
@mouseleave="() => hiddenButton(item)">
|
||||||
|
<div class="group_name" :class="{ group_selected: item.selected }"
|
||||||
|
:type="item.selected ? 'primary' : 'default'">
|
||||||
|
<j-ellipsis style="max-width: 100%;"><span>{{ item.name }}</span>
|
||||||
|
<AIcon type="CloseOutlined" class="closeIcon" v-if="item.closeIcon"
|
||||||
|
@click="group.splice(index, 1)"></AIcon>
|
||||||
|
</j-ellipsis>
|
||||||
|
</div>
|
||||||
|
<div v-if="item.show">
|
||||||
|
<j-button block @click="() => selectGroup(item)">{{ item.selected ? '取消选中' : '选中' }}</j-button>
|
||||||
|
<j-button block @click="() => showEditGroup(item, index)">编辑</j-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<j-button type="text" @click="showAddGroup" :disabled="group.length >= 10">+ 自定义分组</j-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<j-checkbox-group @change="getCheckValue">
|
<j-checkbox-group @change="getCheckValue">
|
||||||
<div class="init-home-role-content">
|
<div class="init-home-role-content">
|
||||||
<div
|
<div class="role-item role-item-1" :style="keys.includes('device')
|
||||||
class="role-item role-item-1"
|
? 'background-color: #f5f5f5;'
|
||||||
:style="
|
: ''
|
||||||
keys.includes('device')
|
">
|
||||||
? 'background-color: #f5f5f5;'
|
|
||||||
: ''
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<div class="role-item-title">
|
<div class="role-item-title">
|
||||||
<j-checkbox :value="ROLEKEYS.device"></j-checkbox>
|
<j-checkbox :value="ROLEKEYS.device"></j-checkbox>
|
||||||
<div class="role-title">设备接入岗</div>
|
<div class="role-title">设备接入岗</div>
|
||||||
|
@ -19,14 +35,10 @@
|
||||||
该角色负责设备接入模块的维护管理
|
该角色负责设备接入模块的维护管理
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div class="role-item role-item-2" :style="keys.includes('link')
|
||||||
class="role-item role-item-2"
|
? 'background-color: #f5f5f5;'
|
||||||
:style="
|
: ''
|
||||||
keys.includes('link')
|
">
|
||||||
? 'background-color: #f5f5f5;'
|
|
||||||
: ''
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<div class="role-item-title">
|
<div class="role-item-title">
|
||||||
<j-checkbox :value="ROLEKEYS.link"></j-checkbox>
|
<j-checkbox :value="ROLEKEYS.link"></j-checkbox>
|
||||||
<div class="role-title">运维管理岗</div>
|
<div class="role-title">运维管理岗</div>
|
||||||
|
@ -36,14 +48,10 @@
|
||||||
该角色负责系统运维模块的维护管理
|
该角色负责系统运维模块的维护管理
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div class="role-item role-item-3" :style="keys.includes('complex')
|
||||||
class="role-item role-item-3"
|
? 'background-color: #f5f5f5;'
|
||||||
:style="
|
: ''
|
||||||
keys.includes('complex')
|
">
|
||||||
? 'background-color: #f5f5f5;'
|
|
||||||
: ''
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<div class="role-item-title">
|
<div class="role-item-title">
|
||||||
<j-checkbox :value="ROLEKEYS.complex"></j-checkbox>
|
<j-checkbox :value="ROLEKEYS.complex"></j-checkbox>
|
||||||
<div class="role-title">综合管理岗</div>
|
<div class="role-title">综合管理岗</div>
|
||||||
|
@ -56,11 +64,28 @@
|
||||||
</div>
|
</div>
|
||||||
</j-checkbox-group>
|
</j-checkbox-group>
|
||||||
</div>
|
</div>
|
||||||
|
<j-modal :visible="showAdd" title="自定义分组" @cancel="showAdd = false" @ok="addGroup">
|
||||||
|
<j-form layout="vertical" ref="formRef" :model="formData">
|
||||||
|
<j-form-item name="name" label="名称" :rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入名称',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
max: 64,
|
||||||
|
message: '最多可输入64个字符',
|
||||||
|
},
|
||||||
|
]">
|
||||||
|
<j-input v-model:value="formData.name"></j-input>
|
||||||
|
</j-form-item>
|
||||||
|
</j-form>
|
||||||
|
</j-modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import RoleMenuData, { ROLEKEYS, RoleData } from '../data/RoleData';
|
import RoleMenuData, { ROLEKEYS, RoleData } from '../data/RoleData';
|
||||||
import { updateRoleMenu, addRole, getRoleMenu } from '@/api/initHome';
|
import { updateRoleMenu, addRole, getRoleMenu, addRoleGroup } from '@/api/initHome';
|
||||||
|
import { randomString } from '@/utils/utils';
|
||||||
/**
|
/**
|
||||||
* 角色勾选数据
|
* 角色勾选数据
|
||||||
*/
|
*/
|
||||||
|
@ -71,6 +96,34 @@ const keys = ref([]);
|
||||||
const getCheckValue = (val: any) => {
|
const getCheckValue = (val: any) => {
|
||||||
keys.value = val;
|
keys.value = val;
|
||||||
};
|
};
|
||||||
|
/**
|
||||||
|
* 获取分组数据
|
||||||
|
*/
|
||||||
|
const group = ref<any[]>([{
|
||||||
|
name: '默认',
|
||||||
|
show: false,
|
||||||
|
selected: true,
|
||||||
|
id: 'default_group'
|
||||||
|
}, {
|
||||||
|
name: '岗位',
|
||||||
|
show: false,
|
||||||
|
selected: false,
|
||||||
|
id: randomString()
|
||||||
|
}, {
|
||||||
|
name: '职位',
|
||||||
|
show: false,
|
||||||
|
selected: false,
|
||||||
|
id: randomString()
|
||||||
|
}])
|
||||||
|
|
||||||
|
const showAdd = ref(false)
|
||||||
|
const groupStatue = ref('add')
|
||||||
|
const selectedGroup = ref()
|
||||||
|
const formData = ref({
|
||||||
|
name: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const formRef = ref()
|
||||||
/**
|
/**
|
||||||
* 根据菜单找角色
|
* 根据菜单找角色
|
||||||
*/
|
*/
|
||||||
|
@ -137,45 +190,153 @@ const saveRoleData = (item: any) => {
|
||||||
* 保存角色
|
* 保存角色
|
||||||
*/
|
*/
|
||||||
const addRoleData = async () => {
|
const addRoleData = async () => {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
if (!keys.value.length) {
|
if (!keys.value.length) {
|
||||||
return resolve(true);
|
return resolve(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
const allPromise = keys.value.map(async (item) => {
|
const allPromise = keys.value.map(async (item) => {
|
||||||
return await saveRoleData(item);
|
return await saveRoleData(item);
|
||||||
});
|
});
|
||||||
|
|
||||||
Promise.all(allPromise).then((item) => {
|
Promise.all(allPromise).then((item) => {
|
||||||
resolve(
|
resolve(
|
||||||
item.every((i) => {
|
item.every((i) => {
|
||||||
return i;
|
return i;
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const showButton = (item: any) => {
|
||||||
|
if (item.name != '默认') {
|
||||||
|
item.show = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const hiddenButton = (item: any) => {
|
||||||
|
item.show = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectGroup = (item: any) => {
|
||||||
|
item.selected = !item.selected
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 新增分组
|
||||||
|
*/
|
||||||
|
const showAddGroup = () => {
|
||||||
|
formData.value.name = ''
|
||||||
|
showAdd.value = true
|
||||||
|
groupStatue.value = 'add'
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 分组编辑
|
||||||
|
*/
|
||||||
|
const showEditGroup = (item: any, index: number) => {
|
||||||
|
formData.value.name = item.name
|
||||||
|
showAdd.value = true
|
||||||
|
groupStatue.value = 'edit'
|
||||||
|
selectedGroup.value = index
|
||||||
|
}
|
||||||
|
const addGroup = () => {
|
||||||
|
formRef.value?.validate().then(() => {
|
||||||
|
groupStatue.value === 'add' ? group.value.push({
|
||||||
|
name: formData.value.name,
|
||||||
|
selected: true,
|
||||||
|
show: false,
|
||||||
|
closeIcon: true,
|
||||||
|
id: randomString()
|
||||||
|
}) : group.value[selectedGroup.value].name = formData.value.name
|
||||||
|
showAdd.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 保存角色分组
|
||||||
|
*/
|
||||||
|
const saveGroup = async () => {
|
||||||
|
const roleGroup = group.value.filter((item: any) => {
|
||||||
|
return item.selected
|
||||||
|
})
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const allPromise = roleGroup.map(async (item) => {
|
||||||
|
return await addRoleGroup({ id: item.id, name: item.name });
|
||||||
|
});
|
||||||
|
Promise.all(allPromise).then((item) => {
|
||||||
|
resolve(
|
||||||
|
item.every((i) => {
|
||||||
|
return i;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
defineExpose({
|
defineExpose({
|
||||||
submitRole: addRoleData,
|
submitRole: addRoleData,
|
||||||
|
submitRoleGroup: saveGroup
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.init-home-role {
|
.init-home-role {
|
||||||
|
.built_in_group {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.group {
|
||||||
|
display: flex;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 999;
|
||||||
|
|
||||||
|
.group_item {
|
||||||
|
width: 100px;
|
||||||
|
margin-right: 20px;
|
||||||
|
|
||||||
|
.button {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group_name {
|
||||||
|
border: .2px solid rgb(217, 217, 217);
|
||||||
|
text-align: center;
|
||||||
|
height: 32px;
|
||||||
|
line-height: 32px;
|
||||||
|
border-radius: 12%;
|
||||||
|
padding: 0 10px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.closeIcon {
|
||||||
|
font-size: 12px;
|
||||||
|
position: absolute;
|
||||||
|
top: 4px;
|
||||||
|
right: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.group_selected {
|
||||||
|
background-color: rgb(190, 232, 251);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.init-home-role-content {
|
.init-home-role-content {
|
||||||
display: flex;
|
display: flex;
|
||||||
grid-gap: 24px;
|
grid-gap: 24px;
|
||||||
gap: 24px;
|
gap: 24px;
|
||||||
|
margin-top: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.role-item-1 {
|
.role-item-1 {
|
||||||
background-image: url(/images/init-home/role1.png);
|
background-image: url(/images/init-home/role1.png);
|
||||||
}
|
}
|
||||||
|
|
||||||
.role-item-2 {
|
.role-item-2 {
|
||||||
background-image: url(/images/init-home/role2.png);
|
background-image: url(/images/init-home/role2.png);
|
||||||
}
|
}
|
||||||
|
|
||||||
.role-item-3 {
|
.role-item-3 {
|
||||||
background-image: url(/images/init-home/role3.png);
|
background-image: url(/images/init-home/role3.png);
|
||||||
}
|
}
|
||||||
|
|
||||||
.role-item {
|
.role-item {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -187,8 +348,10 @@ defineExpose({
|
||||||
background-position: 50%;
|
background-position: 50%;
|
||||||
background-size: 370px;
|
background-size: 370px;
|
||||||
border: 1px solid #f5f5f5;
|
border: 1px solid #f5f5f5;
|
||||||
|
|
||||||
.role-item-title {
|
.role-item-title {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
.role-title {
|
.role-title {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
|
@ -196,11 +359,13 @@ defineExpose({
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.role-item-content {
|
.role-item-content {
|
||||||
width: 250px;
|
width: 250px;
|
||||||
height: 260px;
|
height: 260px;
|
||||||
margin-top: 24px;
|
margin-top: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.role-item-footer {
|
.role-item-footer {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: -30px;
|
bottom: -30px;
|
||||||
|
@ -211,5 +376,4 @@ defineExpose({
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}</style>
|
||||||
</style>
|
|
|
@ -13,15 +13,18 @@ export const RoleData = {
|
||||||
[ROLEKEYS.device]: {
|
[ROLEKEYS.device]: {
|
||||||
name: '设备接入岗',
|
name: '设备接入岗',
|
||||||
description: '该角色负责设备接入模块的维护管理',
|
description: '该角色负责设备接入模块的维护管理',
|
||||||
|
groupId:'default_group',
|
||||||
state: { text: '正常', value: 'enabled' },
|
state: { text: '正常', value: 'enabled' },
|
||||||
},
|
},
|
||||||
[ROLEKEYS.link]: {
|
[ROLEKEYS.link]: {
|
||||||
name: '运维管理岗',
|
name: '运维管理岗',
|
||||||
|
groupId:'default_group',
|
||||||
description: '该角色负责系统运维模块的维护管理',
|
description: '该角色负责系统运维模块的维护管理',
|
||||||
state: { text: '正常', value: 'enabled' },
|
state: { text: '正常', value: 'enabled' },
|
||||||
},
|
},
|
||||||
[ROLEKEYS.complex]: {
|
[ROLEKEYS.complex]: {
|
||||||
name: '综合管理岗',
|
name: '综合管理岗',
|
||||||
|
groupId:'default_group',
|
||||||
description: '该角色负责系统运维和设备接入模块的维护管理',
|
description: '该角色负责系统运维和设备接入模块的维护管理',
|
||||||
state: { text: '正常', value: 'enabled' },
|
state: { text: '正常', value: 'enabled' },
|
||||||
},
|
},
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -112,6 +112,11 @@ const submitData = async () => {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const roleGroupRes = await roleRef.value.submitRoleGroup();
|
||||||
|
if (!roleGroupRes) {
|
||||||
|
loading.value = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
const initDataRes = await initDataRef.value.save();
|
const initDataRes = await initDataRef.value.save();
|
||||||
if (!initDataRes) {
|
if (!initDataRes) {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
|
@ -119,7 +124,7 @@ const submitData = async () => {
|
||||||
}
|
}
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
// 当前数据是否成功提交
|
// 当前数据是否成功提交
|
||||||
if (basicRes && menuRes && roleRes && initDataRes) {
|
if (basicRes && menuRes && roleRes && roleGroupRes && initDataRes) {
|
||||||
onlyMessage('保存成功');
|
onlyMessage('保存成功');
|
||||||
// // 记录初始化数据,跳转首页
|
// // 记录初始化数据,跳转首页
|
||||||
const res = await saveInit();
|
const res = await saveInit();
|
||||||
|
|
|
@ -203,6 +203,17 @@ const handOptionByColumn = (option: any) => {
|
||||||
valueOptions.value = []
|
valueOptions.value = []
|
||||||
valueColumnOptions.value = []
|
valueColumnOptions.value = []
|
||||||
}
|
}
|
||||||
|
if(paramsValue.termType){
|
||||||
|
if (columnType.value ==='date') {
|
||||||
|
if (timeTypeKeys.includes(paramsValue.termType)) {
|
||||||
|
if (tabsOptions.value[0].component !== 'int') {
|
||||||
|
}
|
||||||
|
tabsOptions.value[0].component = 'int'
|
||||||
|
} else if (!timeTypeKeys.includes(paramsValue.termType) && tabsOptions.value[0].component == 'int') {
|
||||||
|
tabsOptions.value[0].component = 'date'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(() => [columnOptions.value, paramsValue.column], () => {
|
watch(() => [columnOptions.value, paramsValue.column], () => {
|
||||||
|
|
|
@ -128,6 +128,15 @@ function getMenus(id: string) {
|
||||||
column: 'appId',
|
column: 'appId',
|
||||||
value: id,
|
value: id,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
terms:[
|
||||||
|
{
|
||||||
|
value:"%show\":true%",
|
||||||
|
termType:"like",
|
||||||
|
column:"options"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
getMenuTree_api(params).then((resp: any) => {
|
getMenuTree_api(params).then((resp: any) => {
|
||||||
|
|
|
@ -221,6 +221,7 @@ const permission = 'system/Apply';
|
||||||
|
|
||||||
const typeOptions = ref<any[]>([])
|
const typeOptions = ref<any[]>([])
|
||||||
const visible = ref<boolean>(false)
|
const visible = ref<boolean>(false)
|
||||||
|
const addMenuVisible = ref<boolean>(false)
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
queryType().then((resp: any) => {
|
queryType().then((resp: any) => {
|
||||||
|
|
|
@ -332,7 +332,6 @@ import { save_api } from '@/api/system/basis';
|
||||||
import { usePermissionStore } from '@/store/permission';
|
import { usePermissionStore } from '@/store/permission';
|
||||||
import { useSystem } from '@/store/system';
|
import { useSystem } from '@/store/system';
|
||||||
import { settingDetail } from '@/api/login';
|
import { settingDetail } from '@/api/login';
|
||||||
|
|
||||||
const action = `${BASE_API_PATH}/file/static`;
|
const action = `${BASE_API_PATH}/file/static`;
|
||||||
const headers = { [TOKEN_KEY]: LocalStore.get(TOKEN_KEY) };
|
const headers = { [TOKEN_KEY]: LocalStore.get(TOKEN_KEY) };
|
||||||
const formRef = ref();
|
const formRef = ref();
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
<template>
|
||||||
|
<j-modal visible title="下载" @cancel="close" @ok="downLoad" :maskClosable="false"
|
||||||
|
:confirmLoading="loading" :width="900">
|
||||||
|
<JProTable
|
||||||
|
:columns="columns"
|
||||||
|
model="CARD"
|
||||||
|
:request="getDic_page"
|
||||||
|
:gridColumn="2"
|
||||||
|
:defaultParams="{sorts: [{ name: 'createTime', order: 'desc' }]}"
|
||||||
|
>
|
||||||
|
<template #headerTitle>
|
||||||
|
请选择需要下载的字典
|
||||||
|
</template>
|
||||||
|
<template #card="slotProps">
|
||||||
|
<CardBox :value="slotProps" :showStatus="false" :active="_selectedRowKeys.includes(slotProps.id)" @click="onSelectChange">
|
||||||
|
<template #content>
|
||||||
|
<j-row>
|
||||||
|
<j-col :span="12">
|
||||||
|
<Ellipsis style="width: 100%">
|
||||||
|
<div>字典名称:{{ slotProps.name }}</div>
|
||||||
|
</Ellipsis>
|
||||||
|
</j-col>
|
||||||
|
<j-col :span="12">
|
||||||
|
<Ellipsis style="width: 100%">
|
||||||
|
字典ID:{{ slotProps.id }}
|
||||||
|
</Ellipsis>
|
||||||
|
</j-col>
|
||||||
|
</j-row>
|
||||||
|
</template>
|
||||||
|
</CardBox>
|
||||||
|
</template>
|
||||||
|
</JProTable>
|
||||||
|
</j-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { getDic_page , downDic} from '@/api/system/dictionary';
|
||||||
|
import { onlyMessage } from '@/utils/comm';
|
||||||
|
import { downloadFileByUrl } from '@/utils/utils';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
const emit = defineEmits(['closeDown'])
|
||||||
|
const loading = ref(false)
|
||||||
|
const columns = [ {
|
||||||
|
title:'name',
|
||||||
|
dataIndex:'name',
|
||||||
|
key:'name',
|
||||||
|
ellipsis: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'name',
|
||||||
|
dataIndex: 'name',
|
||||||
|
key: 'name',
|
||||||
|
ellipsis: true,
|
||||||
|
}]
|
||||||
|
const _selectedRowKeys:any = ref([])
|
||||||
|
const onSelectChange = (dt: any) => {
|
||||||
|
if (_selectedRowKeys.value.includes(dt.id)) {
|
||||||
|
const _index = _selectedRowKeys.value.findIndex((i:any) => i === dt.id);
|
||||||
|
_selectedRowKeys.value.splice(_index, 1);
|
||||||
|
} else {
|
||||||
|
_selectedRowKeys.value = [..._selectedRowKeys.value, dt.id];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const close = () =>{
|
||||||
|
emit('closeDown')
|
||||||
|
}
|
||||||
|
const downLoad = async() =>{
|
||||||
|
if(_selectedRowKeys.value.length){
|
||||||
|
const res:any = await downDic({
|
||||||
|
terms:[
|
||||||
|
{
|
||||||
|
terms:[
|
||||||
|
{
|
||||||
|
value:_selectedRowKeys.value,
|
||||||
|
termType:'in',
|
||||||
|
column:'id'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
if(res.status === 200 && res.result){
|
||||||
|
const json = JSON.stringify(res.result)
|
||||||
|
if (json) {
|
||||||
|
const blob = new Blob([json], { type: 'application/json' });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
downloadFileByUrl(url, `数据字典${dayjs().format('YYYY-MM-DD HH:mm:ss')}`, 'json');
|
||||||
|
emit('closeDown');
|
||||||
|
}
|
||||||
|
emit('closeDown')
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
onlyMessage('至少选择一条数据!')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
</style>
|
|
@ -0,0 +1,218 @@
|
||||||
|
<template>
|
||||||
|
<div class="left-contain">
|
||||||
|
<j-input placeholder="字典名称" v-model:value="searchValue" @pressEnter="search" @change="searchChange">
|
||||||
|
<template #suffix>
|
||||||
|
<AIcon type="SearchOutlined" @click="search" />
|
||||||
|
</template>
|
||||||
|
</j-input>
|
||||||
|
<div class="controls">
|
||||||
|
<PermissionButton type="primary" hasPermission="system/Dictionary:add" @click="showSave" style="width: 160px">
|
||||||
|
新增字典
|
||||||
|
</PermissionButton>
|
||||||
|
<PermissionButton type="text" hasPermission="system/Dictionary:down" @click="downVisible = true">
|
||||||
|
下载
|
||||||
|
</PermissionButton>
|
||||||
|
<j-upload :before-upload="beforeUpload" accept=".json" :show-upload-list="false"
|
||||||
|
:disabled="!hasPermission('system/Dictionary:import')">
|
||||||
|
<PermissionButton type="text" hasPermission="system/Dictionary:import">
|
||||||
|
导入
|
||||||
|
</PermissionButton>
|
||||||
|
</j-upload>
|
||||||
|
</div>
|
||||||
|
<div class="tree">
|
||||||
|
<j-tree :tree-data="listData" v-if="listData.length" :fieldNames="{ title: 'name', key: 'id' }" blockNode
|
||||||
|
:selectedKeys="selectedKeys">
|
||||||
|
<template #title="item">
|
||||||
|
<div class="treeItem" @click="() => selectDic(item.data)">
|
||||||
|
<div class="itemText">
|
||||||
|
<Ellipsis style="width: calc(100%-100px)">{{ item.name }}</Ellipsis>
|
||||||
|
</div>
|
||||||
|
<div @click="(e) => e.stopPropagation()">
|
||||||
|
<j-popconfirm v-if="hasPermission('system/Dictionary:action')"
|
||||||
|
:title="item.data.status === 1 ? '确定禁用?' : '确定启用?'" @confirm="() => updateDic(item.data)">
|
||||||
|
<j-switch :checked="item.status" :disabled="!hasPermission('system/Dictionary:action')"
|
||||||
|
:checkedValue="1" :unCheckedValue="0"></j-switch>
|
||||||
|
</j-popconfirm>
|
||||||
|
<j-tooltip v-else placement="top" title="暂无权限,请联系管理员">
|
||||||
|
<j-switch :checked="item.status" :disabled="!hasPermission('system/Dictionary:action')"
|
||||||
|
:checkedValue="1" :unCheckedValue="0"></j-switch>
|
||||||
|
</j-tooltip>
|
||||||
|
<PermissionButton type="text" hasPermission="system/Dictionary:delete" :popConfirm="{
|
||||||
|
title: `确定要删除?`,
|
||||||
|
onConfirm: () => deleteDic(item.id),
|
||||||
|
}">
|
||||||
|
删除
|
||||||
|
</PermissionButton>
|
||||||
|
<PermissionButton type="text" hasPermission="system/Dictionary:update"
|
||||||
|
@click="showEdit(item.data)">
|
||||||
|
编辑
|
||||||
|
</PermissionButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</j-tree>
|
||||||
|
<j-empty v-else style="margin-top: 100px;" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Save v-if="saveShow" :type="addType" @close-save="saveShow = false" @success="saveSuccess" :data="editData" />
|
||||||
|
<Export v-if="downVisible" @closeDown="closeDown" />
|
||||||
|
<Import />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { getDicList, deleteDictionary, addDictionary } from '@/api/system/dictionary';
|
||||||
|
import Save from './save/index.vue'
|
||||||
|
import { onlyMessage } from '@/utils/comm';
|
||||||
|
import Export from './Export/index.vue'
|
||||||
|
import { usePermissionStore } from '@/store/permission';
|
||||||
|
const emit = defineEmits(['selectData'])
|
||||||
|
const hasPermission = usePermissionStore().hasPermission;
|
||||||
|
const saveShow = ref(false)
|
||||||
|
const addType = ref('add')
|
||||||
|
const listData = ref<any[]>([])
|
||||||
|
const editData = ref()
|
||||||
|
const selectedKeys: any = ref([])
|
||||||
|
const showSave = () => {
|
||||||
|
saveShow.value = true
|
||||||
|
addType.value = 'add'
|
||||||
|
}
|
||||||
|
const downVisible = ref(false)
|
||||||
|
const searchValue = ref()
|
||||||
|
const queryData = (first?: Boolean, searchName?: any) => {
|
||||||
|
const params = searchName ? { paging:false ,sorts: [{ name: 'createTime', order: 'desc' }, { name: 'name', order: 'desc' }], terms: [{ terms: [{ value: '%' + searchName + '%', termType: 'like', column: 'name' }] }] } : { sorts: [{ name: 'createTime', order: 'desc' }, { name: 'name', order: 'desc' }], paging:false }
|
||||||
|
getDicList(params).then((res: any) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
listData.value = res.result
|
||||||
|
if (first && res.result.length) {
|
||||||
|
selectDic(res.result[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const search = () => {
|
||||||
|
queryData(true, searchValue.value)
|
||||||
|
}
|
||||||
|
const searchChange = () => {
|
||||||
|
console.log(searchValue.value === '')
|
||||||
|
if (searchValue.value === '') {
|
||||||
|
queryData(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const showEdit = (data: any) => {
|
||||||
|
saveShow.value = true
|
||||||
|
addType.value = 'edit'
|
||||||
|
editData.value = data
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 重新请求数据
|
||||||
|
*/
|
||||||
|
const reload = () => {
|
||||||
|
queryData()
|
||||||
|
}
|
||||||
|
const saveSuccess = () => {
|
||||||
|
saveShow.value = false
|
||||||
|
reload()
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param id 字典id
|
||||||
|
* 删除字典
|
||||||
|
*/
|
||||||
|
const deleteDic = (id: string) => {
|
||||||
|
deleteDictionary(id).then((res: any) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
onlyMessage('操作成功!')
|
||||||
|
queryData(true)
|
||||||
|
} else {
|
||||||
|
onlyMessage('操作失败!', 'error')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 更新字典
|
||||||
|
*/
|
||||||
|
const updateDic = (data: any) => {
|
||||||
|
data.status = data.status === 1 ? 0 : 1
|
||||||
|
addDictionary(data).then((res: any) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
onlyMessage('操作成功!')
|
||||||
|
reload()
|
||||||
|
} else {
|
||||||
|
onlyMessage('操作失败!', 'error')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 切换选中字典
|
||||||
|
*/
|
||||||
|
const selectDic = (selectKeys: any) => {
|
||||||
|
selectedKeys.value = [selectKeys.id]
|
||||||
|
emit('selectData', selectKeys)
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 导入字典
|
||||||
|
*/
|
||||||
|
const beforeUpload = (file: any) => {
|
||||||
|
if (file.type === 'application/json') {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.readAsText(file);
|
||||||
|
reader.onload = async (json: any) => {
|
||||||
|
if (json?.target?.result) {
|
||||||
|
const text = json.target.result
|
||||||
|
let data
|
||||||
|
try {
|
||||||
|
data = JSON.parse(text);
|
||||||
|
} catch {
|
||||||
|
onlyMessage('请上传json格式的文件', 'error')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
const res = await addDictionary(data)
|
||||||
|
if (res.status === 200) {
|
||||||
|
reload()
|
||||||
|
onlyMessage('操作成功!')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
onlyMessage('文件内容不能为空', 'error')
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
onlyMessage('请上传json格式的文件', 'error')
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeDown = () => {
|
||||||
|
downVisible.value = false
|
||||||
|
}
|
||||||
|
onMounted(() => {
|
||||||
|
queryData(true)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.left-contain {
|
||||||
|
width: 300px;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-tree-switcher) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree {
|
||||||
|
height: calc(100% - 110px);
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls {
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.treeItem {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.itemText {
|
||||||
|
line-height: 32px;
|
||||||
|
max-width: 40%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,125 @@
|
||||||
|
<template>
|
||||||
|
<j-modal
|
||||||
|
:title="type==='add'?'新增字典':'编辑字典'"
|
||||||
|
visible
|
||||||
|
@cancel="closeModal"
|
||||||
|
@ok="submitData"
|
||||||
|
width="650px"
|
||||||
|
:maskClosable="false"
|
||||||
|
:confirmLoading="loading"
|
||||||
|
>
|
||||||
|
<j-form layout="vertical" :rules="rules" ref="formRef" :model="form">
|
||||||
|
<j-form-item label="字典ID" name="id">
|
||||||
|
<j-input v-model:value="form.id" :disabled="type ==='edit'"></j-input>
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item label="字典名称" name="name">
|
||||||
|
<j-input v-model:value="form.name"></j-input>
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item label="状态" name="status">
|
||||||
|
<j-radio-group v-model:value="form.status">
|
||||||
|
<j-radio-button :value="1">启用</j-radio-button>
|
||||||
|
<j-radio-button :value="0">禁用</j-radio-button>
|
||||||
|
</j-radio-group>
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item label="说明" name="describe">
|
||||||
|
<j-textarea :rows="4" :maxlength="200" v-model:value="form.describe"></j-textarea>
|
||||||
|
</j-form-item>
|
||||||
|
</j-form>
|
||||||
|
</j-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { isInput } from '@/utils/regular';
|
||||||
|
import type { Rule } from 'ant-design-vue/es/form';
|
||||||
|
import { verifyId,addDictionary } from '@/api/system/dictionary'
|
||||||
|
import { onlyMessage } from '@/utils/comm';
|
||||||
|
const props = defineProps({
|
||||||
|
type:{
|
||||||
|
type:String,
|
||||||
|
default:'add'
|
||||||
|
},
|
||||||
|
data:{
|
||||||
|
type:Object,
|
||||||
|
default:{}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const emit = defineEmits(['closeSave','success'])
|
||||||
|
const loading = ref(false)
|
||||||
|
const formRef = ref()
|
||||||
|
const form = reactive({
|
||||||
|
id:'',
|
||||||
|
name:'',
|
||||||
|
describe:'',
|
||||||
|
status:1,
|
||||||
|
})
|
||||||
|
/**
|
||||||
|
* 校验id
|
||||||
|
*/
|
||||||
|
const validateInput = async (_rule: Rule, value: string) => {
|
||||||
|
if (value) {
|
||||||
|
if (!isInput(value)) {
|
||||||
|
return Promise.reject('请输入英文或者数字或者-或者_');
|
||||||
|
} else {
|
||||||
|
if (props.type === 'add') {
|
||||||
|
const res:any = await verifyId(value);
|
||||||
|
if (res.status === 200 && res.result) {
|
||||||
|
return Promise.reject('该字典ID已存在');
|
||||||
|
} else {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const rules = {
|
||||||
|
id: [
|
||||||
|
{ required:true,message:'请输入ID'},
|
||||||
|
{ validator: validateInput, trigger: 'blur' },
|
||||||
|
{ max: 64, message: '最多可输入64位字符', trigger: 'change' },
|
||||||
|
],
|
||||||
|
name: [
|
||||||
|
{ required: true, message: '请输入名称', trigger: 'blur' },
|
||||||
|
{ max: 64, message: '最多可输入64位字符', trigger: 'change' },
|
||||||
|
],
|
||||||
|
status: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message:'请选择状态',
|
||||||
|
trigger: 'blur',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
description: [
|
||||||
|
{ max: 200, message: '最多可输入200位字符', trigger: 'blur' },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const submitData = () =>{
|
||||||
|
formRef.value.validate().then(async()=>{
|
||||||
|
loading.value = true
|
||||||
|
const res = await addDictionary(form)
|
||||||
|
if(res.status === 200){
|
||||||
|
onlyMessage('保存成功!')
|
||||||
|
emit('success')
|
||||||
|
}else{
|
||||||
|
onlyMessage('操作失败!','error')
|
||||||
|
}
|
||||||
|
loading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const closeModal = ()=>{
|
||||||
|
emit('closeSave')
|
||||||
|
}
|
||||||
|
onMounted(()=>{
|
||||||
|
if(props.type==='edit' && props.data){
|
||||||
|
form.describe = props.data.describe
|
||||||
|
form.id = props.data.id
|
||||||
|
form.name = props.data.name
|
||||||
|
form.status = props.data.status
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
</style>
|
|
@ -0,0 +1,143 @@
|
||||||
|
<template>
|
||||||
|
<j-modal visible :title="type === 'add' ? '新增' : '编辑'" @cancel="close" @ok="submitData" :maskClosable="false"
|
||||||
|
:confirmLoading="loading">
|
||||||
|
<j-form :model="form" layout="vertical" :rules="rules" ref="formRef">
|
||||||
|
<j-form-item label="name" name="name">
|
||||||
|
<j-input placeholder="con_type" v-model:value="form.name"></j-input>
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item label="value" name="value">
|
||||||
|
<j-input placeholder="con_type" v-model:value="form.value"></j-input>
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item label="text" name="text">
|
||||||
|
<j-input placeholder="连接失败" v-model:value="form.text"></j-input>
|
||||||
|
</j-form-item>
|
||||||
|
</j-form>
|
||||||
|
</j-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { isInput } from '@/utils/regular';
|
||||||
|
import type { Rule } from 'ant-design-vue/es/form';
|
||||||
|
import { saveDicItem, verifyValue } from '@/api/system/dictionary';
|
||||||
|
import { onlyMessage } from '@/utils/comm';
|
||||||
|
import { validateValueType } from '@/views/device/components/Metadata/Base/Edit/validator';
|
||||||
|
import { cloneDeep } from 'lodash-es';
|
||||||
|
const props = defineProps({
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
default: 'add'
|
||||||
|
},
|
||||||
|
dicId: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
sort: {
|
||||||
|
type: Number,
|
||||||
|
default: 1
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
default: {}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const emit = defineEmits(['closeModal', 'refresh'])
|
||||||
|
const form: any = ref({
|
||||||
|
dictId: '',
|
||||||
|
name: '',
|
||||||
|
value: '',
|
||||||
|
text: '',
|
||||||
|
ordinal: 0,
|
||||||
|
searchCode:''
|
||||||
|
})
|
||||||
|
const lastValue = ref()
|
||||||
|
const loading = ref(false)
|
||||||
|
const formRef = ref()
|
||||||
|
/*
|
||||||
|
* 校验name
|
||||||
|
*/
|
||||||
|
const validateInput = async (_rule: Rule, value: string) => {
|
||||||
|
if (value) {
|
||||||
|
if (!isInput(value)) {
|
||||||
|
return Promise.reject('请输入英文或者数字或者-或者_');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* 校验value唯一
|
||||||
|
*/
|
||||||
|
const validateValue = async (_rule: Rule, value: string) => {
|
||||||
|
if (value && lastValue.value !== value) {
|
||||||
|
const res:any = await verifyValue({
|
||||||
|
terms: [
|
||||||
|
{
|
||||||
|
terms: [
|
||||||
|
{
|
||||||
|
value: value,
|
||||||
|
termType: "eq",
|
||||||
|
column: "value"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: form.value.dictId,
|
||||||
|
termType: "eq",
|
||||||
|
column: "dictId"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
if (res.status === 200 && res.result) {
|
||||||
|
return Promise.reject('value重复');
|
||||||
|
} else {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const rules = {
|
||||||
|
name: [
|
||||||
|
{ required: true, message: '请输入name' },
|
||||||
|
{ validator: validateInput, trigger: 'change' },
|
||||||
|
{ max: 64, message: '最多可输入64位字符', trigger: 'change' },
|
||||||
|
],
|
||||||
|
value: [
|
||||||
|
{ required: true, message: '请输入value', trigger: 'blur' },
|
||||||
|
{ max: 64, message: '最多可输入64位字符', trigger: 'change' },
|
||||||
|
{ validator: validateValue, trigger: 'blur' }
|
||||||
|
],
|
||||||
|
text: [
|
||||||
|
{ required: true, message: '请输入text', trigger: 'blur' },
|
||||||
|
{ max: 64, message: '最多可输入64位字符', trigger: 'change' },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
const submitData = () => {
|
||||||
|
formRef.value.validate().then(async () => {
|
||||||
|
loading.value = true
|
||||||
|
form.value.searchCode = form.value.name + ':' + form.value.value + ':' + form.value.text
|
||||||
|
const res = await saveDicItem(form.value)
|
||||||
|
if (res.status === 200) {
|
||||||
|
onlyMessage('操作成功!')
|
||||||
|
emit('refresh')
|
||||||
|
} else {
|
||||||
|
onlyMessage('操作失败!', 'error')
|
||||||
|
}
|
||||||
|
loading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const close = () => {
|
||||||
|
emit('closeModal')
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (props.type === 'add') {
|
||||||
|
form.value.dictId = props.dicId
|
||||||
|
form.value.ordinal = props.sort
|
||||||
|
} else {
|
||||||
|
form.value = cloneDeep(props.data)
|
||||||
|
lastValue.value = props.data.value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped></style>
|
|
@ -0,0 +1,229 @@
|
||||||
|
<template>
|
||||||
|
<div class="des">
|
||||||
|
<div class="des_head">
|
||||||
|
<div>字典ID:<span>{{ data.id }}</span></div>
|
||||||
|
<div>说明:<span>{{ data.describe }}</span></div>
|
||||||
|
<div>创建日期:<span> {{
|
||||||
|
dayjs(
|
||||||
|
data?.createTime,
|
||||||
|
).format(
|
||||||
|
'YYYY-MM-DD HH:mm:ss',
|
||||||
|
)
|
||||||
|
}}</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="contain">
|
||||||
|
<pro-search :columns="columns" @search="handleSearch" target="system_dictionary" />
|
||||||
|
<JProTable :columns="columns" model="TABLE" :request="queryItem" :params="params" ref="tableRef">
|
||||||
|
<template #headerTitle>
|
||||||
|
<PermissionButton type="primary" @click="add" hasPermission="system/Dictionary:add">
|
||||||
|
新增
|
||||||
|
</PermissionButton>
|
||||||
|
</template>
|
||||||
|
<template #action="slotProps">
|
||||||
|
<j-space>
|
||||||
|
<template v-for="i in getActions(slotProps, 'table')" :key="i.key">
|
||||||
|
<PermissionButton :disabled="i.disabled" :popConfirm="i.popConfirm" :tooltip="{
|
||||||
|
...i.tooltip,
|
||||||
|
}" @click="i.onClick" type="link" style="padding: 0 5px" :danger="i.key === 'delete'"
|
||||||
|
:hasPermission="'system/Dictionary:' + i.key
|
||||||
|
">
|
||||||
|
<template #icon>
|
||||||
|
<AIcon :type="i.icon" />
|
||||||
|
</template>
|
||||||
|
</PermissionButton>
|
||||||
|
</template>
|
||||||
|
</j-space>
|
||||||
|
</template>
|
||||||
|
</JProTable>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Save v-if="saveVisible" :dicId='data.id' :type="modalType" :data="current" :sort=sort @closeModal="closeModal"
|
||||||
|
@refresh="refresh" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { queryDicItem, deleteDicItem, queryDicItemNoPage } from '@/api/system/dictionary'
|
||||||
|
import Save from './Save/index.vue'
|
||||||
|
import type { ActionsType } from './typings';
|
||||||
|
import { onlyMessage } from '@/utils/comm';
|
||||||
|
import { cloneDeep } from 'lodash-es';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
const props = defineProps({
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
default: {}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const params = ref()
|
||||||
|
const tableRef = ref()
|
||||||
|
const saveVisible = ref(false)
|
||||||
|
const sort = ref(0)
|
||||||
|
const modalType = ref('add')
|
||||||
|
const current = ref()
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: '检索码',
|
||||||
|
dataIndex: 'searchCode',
|
||||||
|
hideInTable: true,
|
||||||
|
search: {
|
||||||
|
type: 'string'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'name',
|
||||||
|
dataIndex: 'name',
|
||||||
|
key: 'name',
|
||||||
|
ellipsis: true,
|
||||||
|
search: {
|
||||||
|
type: 'string'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'value',
|
||||||
|
dataIndex: 'value',
|
||||||
|
key: 'value',
|
||||||
|
ellipsis: true,
|
||||||
|
search: {
|
||||||
|
type: 'string'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'text',
|
||||||
|
dataIndex: 'text',
|
||||||
|
key: 'text',
|
||||||
|
ellipsis: true,
|
||||||
|
search: {
|
||||||
|
type: 'string'
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
title: '操作',
|
||||||
|
dataIndex: 'action',
|
||||||
|
key: 'action',
|
||||||
|
scopedSlots: true,
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const getActions = (
|
||||||
|
data: Partial<Record<string, any>>,
|
||||||
|
type: 'card' | 'table',
|
||||||
|
): ActionsType[] => {
|
||||||
|
if (!data) return [];
|
||||||
|
const actions = [
|
||||||
|
{
|
||||||
|
key: 'update',
|
||||||
|
text: '编辑',
|
||||||
|
tooltip: {
|
||||||
|
title: '编辑',
|
||||||
|
},
|
||||||
|
icon: 'EditOutlined',
|
||||||
|
onClick: () => {
|
||||||
|
saveVisible.value = true;
|
||||||
|
modalType.value = 'edit';
|
||||||
|
current.value = data
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'delete',
|
||||||
|
text: '删除',
|
||||||
|
tooltip: {
|
||||||
|
title: '删除',
|
||||||
|
},
|
||||||
|
popConfirm: {
|
||||||
|
title: '确认删除?',
|
||||||
|
onConfirm: async () => {
|
||||||
|
const res = await deleteDicItem(data.id)
|
||||||
|
if (res.status === 200) {
|
||||||
|
onlyMessage('操作成功!')
|
||||||
|
tableRef.value.reload()
|
||||||
|
} else {
|
||||||
|
onlyMessage('操作失败!', 'error')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
icon: 'DeleteOutlined',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
if (type === 'card')
|
||||||
|
return actions.filter((i: ActionsType) => i.key !== 'view');
|
||||||
|
return actions;
|
||||||
|
};
|
||||||
|
const add = () => {
|
||||||
|
modalType.value = 'add'
|
||||||
|
current.value = {}
|
||||||
|
queryDicItemNoPage({
|
||||||
|
paging: false,
|
||||||
|
sorts: [{ name: 'ordinal', order: 'desc' }],
|
||||||
|
terms: [{
|
||||||
|
column: 'dictId',
|
||||||
|
termType: 'eq',
|
||||||
|
value: props.data?.id
|
||||||
|
}]
|
||||||
|
}).then((res:any)=>{
|
||||||
|
sort.value = res.result?.length ? res.result[0].ordinal + 1 : 1
|
||||||
|
saveVisible.value = true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeModal = () => {
|
||||||
|
saveVisible.value = false
|
||||||
|
}
|
||||||
|
const handleSearch = (i: any) => {
|
||||||
|
params.value = i
|
||||||
|
}
|
||||||
|
const refresh = () => {
|
||||||
|
saveVisible.value = false
|
||||||
|
tableRef.value.reload()
|
||||||
|
}
|
||||||
|
const queryItem = async (_params: any) => {
|
||||||
|
if (props.data?.id) {
|
||||||
|
const params = {
|
||||||
|
..._params,
|
||||||
|
sorts: [{ name: 'ordinal', order: 'desc' }],
|
||||||
|
terms: [
|
||||||
|
..._params.terms,
|
||||||
|
{
|
||||||
|
column: 'dictId',
|
||||||
|
termType: 'eq',
|
||||||
|
value: props.data?.id
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
const resp: any = await queryDicItem(params);
|
||||||
|
if (resp.status === 200) {
|
||||||
|
const arr = cloneDeep(resp.result.data)
|
||||||
|
arr?.sort((a: any, b: any) => {
|
||||||
|
return b.ordinal - a.ordinal
|
||||||
|
})
|
||||||
|
return {
|
||||||
|
code: resp.status,
|
||||||
|
result: resp.result,
|
||||||
|
status: resp.status,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
code: 200,
|
||||||
|
result: {
|
||||||
|
data: [],
|
||||||
|
pageIndex: 0,
|
||||||
|
pageSize: 0,
|
||||||
|
total: 0,
|
||||||
|
},
|
||||||
|
status: 200,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
watch(() => props?.data?.id, () => {
|
||||||
|
tableRef.value.reload()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.des_head {
|
||||||
|
padding: 10px 20px;
|
||||||
|
background-color: rgb(242, 242, 242);
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: rgb(127, 127, 127)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,45 @@
|
||||||
|
<template>
|
||||||
|
<page-container>
|
||||||
|
<FullPage>
|
||||||
|
<div class="dictionary_contain">
|
||||||
|
<div class="dictionary_left">
|
||||||
|
<Left @selectData="selectData" />
|
||||||
|
</div>
|
||||||
|
<div class="dictionary_right">
|
||||||
|
<Right :data="data" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</FullPage>
|
||||||
|
</page-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import Left from './components/Left/index.vue'
|
||||||
|
import Right from './components/Right/index.vue'
|
||||||
|
const data = ref()
|
||||||
|
const selectData = (i: any) => {
|
||||||
|
data.value = i
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.dictionary_contain {
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 24px;
|
||||||
|
padding-bottom: 0;
|
||||||
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dictionary_left {
|
||||||
|
position: absolute;
|
||||||
|
border-right: 1px solid #f0f0f0;
|
||||||
|
padding-right: 24px;
|
||||||
|
width: 310px;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dictionary_right {
|
||||||
|
margin-left: 317px;
|
||||||
|
width: calc(100% - 317px);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -4,209 +4,125 @@
|
||||||
<h3>基本信息</h3>
|
<h3>基本信息</h3>
|
||||||
<j-form ref="basicFormRef" :model="form.data" class="basic-form">
|
<j-form ref="basicFormRef" :model="form.data" class="basic-form">
|
||||||
<div class="row" style="display: flex">
|
<div class="row" style="display: flex">
|
||||||
<j-form-item
|
<j-form-item ref="uploadIcon" label="菜单图标" name="icon" :rules="[
|
||||||
ref="uploadIcon"
|
{
|
||||||
label="菜单图标"
|
required: true,
|
||||||
name="icon"
|
message: '请上传图标',
|
||||||
:rules="[
|
trigger: 'change',
|
||||||
{
|
},
|
||||||
required: true,
|
]" style="flex: 0 0 186px">
|
||||||
message: '请上传图标',
|
|
||||||
trigger: 'change',
|
|
||||||
},
|
|
||||||
]"
|
|
||||||
style="flex: 0 0 186px"
|
|
||||||
>
|
|
||||||
<div class="icon-upload has-icon" v-if="form.data.icon">
|
<div class="icon-upload has-icon" v-if="form.data.icon">
|
||||||
<AIcon
|
<AIcon :type="form.data.icon" style="font-size: 90px" />
|
||||||
:type="form.data.icon"
|
<span class="mark" @click="dialogVisible = true">点击修改</span>
|
||||||
style="font-size: 90px"
|
|
||||||
/>
|
|
||||||
<span class="mark" @click="dialogVisible = true"
|
|
||||||
>点击修改</span
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div v-else @click="dialogVisible = true" class="icon-upload no-icon">
|
||||||
v-else
|
|
||||||
@click="dialogVisible = true"
|
|
||||||
class="icon-upload no-icon"
|
|
||||||
>
|
|
||||||
<span>
|
<span>
|
||||||
<AIcon
|
<AIcon type="PlusOutlined" style="font-size: 30px" />
|
||||||
type="PlusOutlined"
|
|
||||||
style="font-size: 30px"
|
|
||||||
/>
|
|
||||||
<p>点击选择图标</p>
|
<p>点击选择图标</p>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
<j-row :gutter="24" style="flex: 1 1 auto">
|
<j-row :gutter="24" style="flex: 1 1 auto">
|
||||||
<j-col :span="12">
|
<j-col :span="12">
|
||||||
<j-form-item
|
<j-form-item label="名称" name="name" :rules="[
|
||||||
label="名称"
|
{
|
||||||
name="name"
|
required: true,
|
||||||
:rules="[
|
message: '请输入名称',
|
||||||
{
|
trigger: 'change',
|
||||||
required: true,
|
},
|
||||||
message: '请输入名称',
|
{
|
||||||
trigger: 'change',
|
max: 64,
|
||||||
},
|
message: '最多可输入64个字符',
|
||||||
{
|
trigger: 'change',
|
||||||
max: 64,
|
},
|
||||||
message: '最多可输入64个字符',
|
]">
|
||||||
trigger: 'change',
|
<j-input v-model:value="form.data.name" placeholder="请输入名称" />
|
||||||
},
|
|
||||||
]"
|
|
||||||
>
|
|
||||||
<j-input
|
|
||||||
v-model:value="form.data.name"
|
|
||||||
placeholder="请输入名称"
|
|
||||||
/>
|
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
</j-col>
|
</j-col>
|
||||||
<j-col :span="12">
|
<j-col :span="12">
|
||||||
<j-form-item
|
<j-form-item label="编码" name="code" :rules="[
|
||||||
label="编码"
|
{
|
||||||
name="code"
|
required: true,
|
||||||
:rules="[
|
message: '请输入编码',
|
||||||
{
|
trigger: 'change',
|
||||||
required: true,
|
},
|
||||||
message: '请输入编码',
|
{
|
||||||
trigger: 'change',
|
max: 64,
|
||||||
},
|
message: '最多可输入64个字符',
|
||||||
{
|
trigger: 'change',
|
||||||
max: 64,
|
},
|
||||||
message: '最多可输入64个字符',
|
{
|
||||||
trigger: 'change',
|
validator: form.checkCode,
|
||||||
},
|
trigger: 'blur',
|
||||||
{
|
},
|
||||||
validator: form.checkCode,
|
]">
|
||||||
trigger: 'blur',
|
<j-input v-model:value="form.data.code" placeholder="请输入编码" />
|
||||||
},
|
|
||||||
]"
|
|
||||||
>
|
|
||||||
<j-input
|
|
||||||
v-model:value="form.data.code"
|
|
||||||
placeholder="请输入编码"
|
|
||||||
/>
|
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
</j-col>
|
</j-col>
|
||||||
<j-col :span="12">
|
<j-col :span="12">
|
||||||
<j-form-item
|
<j-form-item label="页面地址" name="url" :rules="[
|
||||||
label="页面地址"
|
{
|
||||||
name="url"
|
required: true,
|
||||||
:rules="[
|
message: '请输入页面地址',
|
||||||
{
|
},
|
||||||
required: true,
|
{ max: 128, message: '最多可输入128字符' },
|
||||||
message: '请输入页面地址',
|
{ pattern: /^\//, message: '请正确填写地址,以/开头' },
|
||||||
},
|
]">
|
||||||
{ max: 128, message: '最多可输入128字符' },
|
<j-input v-model:value="form.data.url" placeholder="请输入页面地址" />
|
||||||
{ pattern: /^\// ,message:'请正确填写地址,以/开头'},
|
|
||||||
]"
|
|
||||||
>
|
|
||||||
<j-input
|
|
||||||
v-model:value="form.data.url"
|
|
||||||
placeholder="请输入页面地址"
|
|
||||||
/>
|
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
</j-col>
|
</j-col>
|
||||||
<j-col :span="12">
|
<j-col :span="12">
|
||||||
<j-form-item
|
<j-form-item label="排序" name="sortIndex" :rules="[
|
||||||
label="排序"
|
{
|
||||||
name="sortIndex"
|
pattern: /^[0-9]*[1-9][0-9]*$/,
|
||||||
:rules="[
|
message: '请输入大于0的整数',
|
||||||
{
|
},
|
||||||
pattern: /^[0-9]*[1-9][0-9]*$/,
|
]">
|
||||||
message: '请输入大于0的整数',
|
<j-input-number v-model:value="form.data.sortIndex" placeholder="请输入排序"
|
||||||
},
|
style="width: 100%" />
|
||||||
]"
|
</j-form-item>
|
||||||
>
|
</j-col>
|
||||||
<j-input-number
|
<j-col :span="12" v-if="!isChildren">
|
||||||
v-model:value="form.data.sortIndex"
|
<j-form-item label="所属应用" name="appId">
|
||||||
placeholder="请输入排序"
|
<j-select v-model:value="form.data.appId" :options="appOptions" :allowClear="!routeParams.id"
|
||||||
style="width: 100%"
|
placeholder="请选择所属应用" style="width: 100%" @change="selectApp"/>
|
||||||
/>
|
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
</j-col>
|
</j-col>
|
||||||
<j-col :span="12">
|
|
||||||
<j-form-item
|
|
||||||
label="所属应用"
|
|
||||||
name="owner"
|
|
||||||
>
|
|
||||||
<j-select
|
|
||||||
v-model:value="form.data.owner"
|
|
||||||
:options="[{ label: 'Iot', value: 'iot' }]"
|
|
||||||
allowClear
|
|
||||||
placeholder="请选择所属应用"
|
|
||||||
style="width: 100%"
|
|
||||||
/>
|
|
||||||
</j-form-item>
|
|
||||||
</j-col>
|
|
||||||
</j-row>
|
</j-row>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<j-form-item label="说明" name="describe">
|
<j-form-item label="说明" name="describe">
|
||||||
<j-textarea
|
<j-textarea v-model:value="form.data.describe" :rows="4" show-count :maxlength="200"
|
||||||
v-model:value="form.data.describe"
|
placeholder="请输入说明" />
|
||||||
:rows="4"
|
|
||||||
show-count
|
|
||||||
:maxlength="200"
|
|
||||||
placeholder="请输入说明"
|
|
||||||
/>
|
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
</j-form>
|
</j-form>
|
||||||
</div>
|
</div>
|
||||||
<div class="card">
|
<div class="card" v-if="!form.data.appId && !isChildren">
|
||||||
<h3>权限配置</h3>
|
<h3>权限配置</h3>
|
||||||
<j-form
|
<j-form ref="permissFormRef" :model="form.data" class="basic-form permiss-form">
|
||||||
ref="permissFormRef"
|
|
||||||
:model="form.data"
|
|
||||||
class="basic-form permiss-form"
|
|
||||||
>
|
|
||||||
<j-form-item name="accessSupport" required v-if="isNoCommunity">
|
<j-form-item name="accessSupport" required v-if="isNoCommunity">
|
||||||
<template #label>
|
<template #label>
|
||||||
<span style="margin-right: 3px">数据权限控制</span>
|
<span style="margin-right: 3px">数据权限控制</span>
|
||||||
<j-tooltip title="此菜单页面数据所对应的资产类型">
|
<j-tooltip title="此菜单页面数据所对应的资产类型">
|
||||||
<AIcon
|
<AIcon type="QuestionCircleOutlined" class="img-style" style="color: #a6a6a6" />
|
||||||
type="QuestionCircleOutlined"
|
|
||||||
class="img-style"
|
|
||||||
style="color: #a6a6a6"
|
|
||||||
/>
|
|
||||||
</j-tooltip>
|
</j-tooltip>
|
||||||
</template>
|
</template>
|
||||||
<j-radio-group
|
<j-radio-group v-model:value="form.data.accessSupport" name="radioGroup">
|
||||||
v-model:value="form.data.accessSupport"
|
|
||||||
name="radioGroup"
|
|
||||||
>
|
|
||||||
<j-radio value="unsupported">不支持</j-radio>
|
<j-radio value="unsupported">不支持</j-radio>
|
||||||
<j-radio value="support">支持</j-radio>
|
<j-radio value="support">支持</j-radio>
|
||||||
<j-radio value="indirect">
|
<j-radio value="indirect">
|
||||||
<span style="margin-right: 3px">间接控制</span>
|
<span style="margin-right: 3px">间接控制</span>
|
||||||
<j-tooltip
|
<j-tooltip title="此菜单内的数据基于其他菜单的数据权限控制">
|
||||||
title="此菜单内的数据基于其他菜单的数据权限控制"
|
<AIcon type="QuestionCircleFilled" class="img-style" />
|
||||||
>
|
|
||||||
<AIcon
|
|
||||||
type="QuestionCircleFilled"
|
|
||||||
class="img-style"
|
|
||||||
/>
|
|
||||||
</j-tooltip>
|
</j-tooltip>
|
||||||
</j-radio>
|
</j-radio>
|
||||||
</j-radio-group>
|
</j-radio-group>
|
||||||
|
|
||||||
<j-form-item
|
<j-form-item name="assetType" v-if="form.data.accessSupport === 'support'"
|
||||||
name="assetType"
|
:rules="[{ required: true, message: '请选择资产类型' }]" style="margin-top: 24px; margin-bottom: 0">
|
||||||
v-if="form.data.accessSupport === 'support'"
|
<j-select v-model:value="form.data.assetType" style="width: 500px" placeholder="请选择资产类型" show-search
|
||||||
:rules="[{ required: true, message: '请选择资产类型' }]"
|
:options="form.assetsType">
|
||||||
style="margin-top: 24px; margin-bottom: 0"
|
|
||||||
>
|
|
||||||
<j-select
|
|
||||||
v-model:value="form.data.assetType"
|
|
||||||
style="width: 500px"
|
|
||||||
placeholder="请选择资产类型"
|
|
||||||
show-search
|
|
||||||
:options="form.assetsType"
|
|
||||||
>
|
|
||||||
<!-- <j-select-option
|
<!-- <j-select-option
|
||||||
v-for="item in form.assetsType"
|
v-for="item in form.assetsType"
|
||||||
:value="item.value"
|
:value="item.value"
|
||||||
|
@ -215,61 +131,32 @@
|
||||||
</j-select>
|
</j-select>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
|
|
||||||
<j-form-item
|
<j-form-item name="indirectMenus" v-if="form.data.accessSupport === 'indirect'"
|
||||||
name="indirectMenus"
|
:rules="[{ required: true, message: '请选择关联菜单' }]" style="margin-top: 24px; margin-bottom: 0">
|
||||||
v-if="form.data.accessSupport === 'indirect'"
|
<j-tree-select v-model:value="form.data.indirectMenus" style="width: 400px" :dropdown-style="{
|
||||||
:rules="[{ required: true, message: '请选择关联菜单' }]"
|
maxHeight: '400px',
|
||||||
style="margin-top: 24px; margin-bottom: 0"
|
overflow: 'auto',
|
||||||
>
|
}" placeholder="请选择关联菜单" multiple show-search :tree-data="form.treeData" :field-names="{
|
||||||
<j-tree-select
|
children: 'children',
|
||||||
v-model:value="form.data.indirectMenus"
|
label: 'name',
|
||||||
style="width: 400px"
|
value: 'id',
|
||||||
:dropdown-style="{
|
}">
|
||||||
maxHeight: '400px',
|
|
||||||
overflow: 'auto',
|
|
||||||
}"
|
|
||||||
placeholder="请选择关联菜单"
|
|
||||||
multiple
|
|
||||||
show-search
|
|
||||||
:tree-data="form.treeData"
|
|
||||||
:field-names="{
|
|
||||||
children: 'children',
|
|
||||||
label: 'name',
|
|
||||||
value: 'id',
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
</j-tree-select>
|
</j-tree-select>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
<j-form-item label="权限">
|
<j-form-item label="权限">
|
||||||
<PermissChoose
|
<PermissChoose :first-width="3" max-height="350px" v-model:value="form.data.permissions"
|
||||||
:first-width="3"
|
:key="form.data.id || ''" />
|
||||||
max-height="350px"
|
|
||||||
v-model:value="form.data.permissions"
|
|
||||||
:key="form.data.id || ''"
|
|
||||||
/>
|
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
</j-form>
|
</j-form>
|
||||||
|
|
||||||
<PermissionButton
|
|
||||||
type="primary"
|
|
||||||
:hasPermission="`${permission}:${
|
|
||||||
route.params.id === ':id' ? 'add' : 'update'
|
|
||||||
}`"
|
|
||||||
@click="form.clickSave"
|
|
||||||
:loading='form.saveLoading'
|
|
||||||
>
|
|
||||||
保存
|
|
||||||
</PermissionButton>
|
|
||||||
</div>
|
</div>
|
||||||
|
<PermissionButton type="primary" :hasPermission="`${permission}:${route.params.id === ':id' ? 'add' : 'update'
|
||||||
|
}`" @click="form.clickSave" :loading='form.saveLoading' class="saveBtn">
|
||||||
|
保存
|
||||||
|
</PermissionButton>
|
||||||
<!-- 弹窗 -->
|
<!-- 弹窗 -->
|
||||||
<ChooseIconDialog
|
<ChooseIconDialog v-if="dialogVisible" v-model:visible="dialogVisible" :icon="form.data.icon"
|
||||||
v-if="dialogVisible"
|
@confirm="(typeStr: string) => choseIcon(typeStr)" />
|
||||||
v-model:visible="dialogVisible"
|
|
||||||
:icon="form.data.icon"
|
|
||||||
@confirm="(typeStr:string)=>choseIcon(typeStr)"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -285,10 +172,12 @@ import {
|
||||||
saveMenuInfo_api,
|
saveMenuInfo_api,
|
||||||
addMenuInfo_api,
|
addMenuInfo_api,
|
||||||
validCode_api,
|
validCode_api,
|
||||||
|
queryApp
|
||||||
} from '@/api/system/menu';
|
} from '@/api/system/menu';
|
||||||
import { Rule } from 'ant-design-vue/lib/form';
|
import { Rule } from 'ant-design-vue/lib/form';
|
||||||
import { isNoCommunity } from '@/utils/utils';
|
import { isNoCommunity } from '@/utils/utils';
|
||||||
import { onlyMessage } from '@/utils/comm';
|
import { onlyMessage } from '@/utils/comm';
|
||||||
|
import { applicationInfo } from '@/api/bind';
|
||||||
|
|
||||||
const permission = 'system/Menu';
|
const permission = 'system/Menu';
|
||||||
// 路由
|
// 路由
|
||||||
|
@ -300,11 +189,13 @@ const routeParams = {
|
||||||
url: route.query.basePath,
|
url: route.query.basePath,
|
||||||
parentId: route.query.pid,
|
parentId: route.query.pid,
|
||||||
};
|
};
|
||||||
|
const isChildren = route.query?.isChildren
|
||||||
// 表单
|
// 表单
|
||||||
const basicFormRef = ref<FormInstance>();
|
const basicFormRef = ref<FormInstance>();
|
||||||
const permissFormRef = ref<FormInstance>();
|
const permissFormRef = ref<FormInstance>();
|
||||||
const uploadIcon = ref<FormInstance>();
|
const uploadIcon = ref<FormInstance>();
|
||||||
|
//菜单应用选项
|
||||||
|
const appOptions = ref<any>([])
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
data: {
|
data: {
|
||||||
name: '',
|
name: '',
|
||||||
|
@ -316,6 +207,8 @@ const form = reactive({
|
||||||
accessSupport: 'unsupported',
|
accessSupport: 'unsupported',
|
||||||
assetType: undefined,
|
assetType: undefined,
|
||||||
indirectMenus: [],
|
indirectMenus: [],
|
||||||
|
appId: '',
|
||||||
|
application:'',
|
||||||
...routeParams,
|
...routeParams,
|
||||||
} as formType,
|
} as formType,
|
||||||
treeData: [], // 关联菜单
|
treeData: [], // 关联菜单
|
||||||
|
@ -329,6 +222,7 @@ const form = reactive({
|
||||||
getMenuInfo_api(routeParams.id).then((resp: any) => {
|
getMenuInfo_api(routeParams.id).then((resp: any) => {
|
||||||
form.data = {
|
form.data = {
|
||||||
...(resp.result as formType),
|
...(resp.result as formType),
|
||||||
|
permissions: resp.result?.permissions ? resp.result.permissions : [],
|
||||||
accessSupport:
|
accessSupport:
|
||||||
resp.result?.accessSupport?.value || 'unsupported',
|
resp.result?.accessSupport?.value || 'unsupported',
|
||||||
};
|
};
|
||||||
|
@ -337,7 +231,15 @@ const form = reactive({
|
||||||
|
|
||||||
if (isNoCommunity) {
|
if (isNoCommunity) {
|
||||||
// 获取关联菜单
|
// 获取关联菜单
|
||||||
getMenuTree_api({ paging: false }).then((resp: any) => {
|
getMenuTree_api({ paging: false,terms:[{terms:[{
|
||||||
|
terms:[
|
||||||
|
{
|
||||||
|
value:"%show\":true%",
|
||||||
|
termType:"like",
|
||||||
|
column:"options"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}]}]}).then((resp: any) => {
|
||||||
form.treeData = resp.result;
|
form.treeData = resp.result;
|
||||||
});
|
});
|
||||||
// 获取资产类型
|
// 获取资产类型
|
||||||
|
@ -374,9 +276,9 @@ const form = reactive({
|
||||||
const api = routeParams.id ? saveMenuInfo_api : addMenuInfo_api;
|
const api = routeParams.id ? saveMenuInfo_api : addMenuInfo_api;
|
||||||
form.saveLoading = true;
|
form.saveLoading = true;
|
||||||
const accessSupportValue = form.data.accessSupport;
|
const accessSupportValue = form.data.accessSupport;
|
||||||
const params = {
|
const params:any = {
|
||||||
...form.data,
|
...form.data,
|
||||||
owner: form.data?.owner ?? null,
|
owner: 'iot',
|
||||||
options: { show: true },
|
options: { show: true },
|
||||||
accessSupport: {
|
accessSupport: {
|
||||||
value: accessSupportValue,
|
value: accessSupportValue,
|
||||||
|
@ -384,10 +286,13 @@ const form = reactive({
|
||||||
accessSupportValue === 'unsupported'
|
accessSupportValue === 'unsupported'
|
||||||
? '不支持'
|
? '不支持'
|
||||||
: accessSupportValue === 'support'
|
: accessSupportValue === 'support'
|
||||||
? '支持'
|
? '支持'
|
||||||
: '间接控制',
|
: '间接控制',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
if(params?.isChildren){
|
||||||
|
delete params.isChildren
|
||||||
|
}
|
||||||
api(params)
|
api(params)
|
||||||
.then((resp: any) => {
|
.then((resp: any) => {
|
||||||
if (resp.status === 200) {
|
if (resp.status === 200) {
|
||||||
|
@ -406,18 +311,41 @@ const form = reactive({
|
||||||
})
|
})
|
||||||
.finally(() => (form.saveLoading = false));
|
.finally(() => (form.saveLoading = false));
|
||||||
})
|
})
|
||||||
.catch((err) => {});
|
.catch((err) => { });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
form.init();
|
form.init();
|
||||||
|
|
||||||
const choseIcon = (typeStr:string) =>{
|
const choseIcon = (typeStr: string) => {
|
||||||
form.data.icon = typeStr;
|
form.data.icon = typeStr;
|
||||||
uploadIcon.value?.clearValidate();
|
uploadIcon.value?.clearValidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const selectApp = (value:string,options:any) =>{
|
||||||
|
form.data.application = options?.label
|
||||||
|
}
|
||||||
// 弹窗
|
// 弹窗
|
||||||
const dialogVisible = ref(false);
|
const dialogVisible = ref(false);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
queryApp({
|
||||||
|
terms: [
|
||||||
|
{
|
||||||
|
"column": "integrationModes",
|
||||||
|
"termType": "in$any",
|
||||||
|
"value": "page"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
paging: false
|
||||||
|
}).then((res:any)=>{
|
||||||
|
appOptions.value = res.result?.map((i:any)=>{
|
||||||
|
return {
|
||||||
|
label:i.name,
|
||||||
|
value:i.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
type formType = {
|
type formType = {
|
||||||
id?: string;
|
id?: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -431,6 +359,8 @@ type formType = {
|
||||||
assetType: string | undefined;
|
assetType: string | undefined;
|
||||||
indirectMenus: any[];
|
indirectMenus: any[];
|
||||||
parentId?: string;
|
parentId?: string;
|
||||||
|
appId:string,
|
||||||
|
application:string
|
||||||
};
|
};
|
||||||
|
|
||||||
type assetType = {
|
type assetType = {
|
||||||
|
@ -441,10 +371,10 @@ type assetType = {
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.basic-info-container {
|
.basic-info-container {
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 24px;
|
||||||
.card {
|
.card {
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
padding: 24px;
|
|
||||||
background-color: #fff;
|
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -470,15 +400,19 @@ type assetType = {
|
||||||
.basic-form {
|
.basic-form {
|
||||||
.ant-form-item {
|
.ant-form-item {
|
||||||
display: block;
|
display: block;
|
||||||
|
|
||||||
:deep(.ant-form-item-label) {
|
:deep(.ant-form-item-label) {
|
||||||
overflow: inherit;
|
overflow: inherit;
|
||||||
|
|
||||||
.img-style {
|
.img-style {
|
||||||
cursor: help;
|
cursor: help;
|
||||||
}
|
}
|
||||||
|
|
||||||
label::after {
|
label::after {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.ant-form-item-control-input-content) {
|
:deep(.ant-form-item-control-input-content) {
|
||||||
.icon-upload {
|
.icon-upload {
|
||||||
width: 160px;
|
width: 160px;
|
||||||
|
@ -491,10 +425,12 @@ type assetType = {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: 0.5s;
|
transition: 0.5s;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
border-color: #415ed1;
|
border-color: #415ed1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.has-icon {
|
.has-icon {
|
||||||
position: relative;
|
position: relative;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
@ -512,10 +448,12 @@ type assetType = {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover .mark {
|
&:hover .mark {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-icon {
|
.no-icon {
|
||||||
background-color: rgba(0, 0, 0, 0.06);
|
background-color: rgba(0, 0, 0, 0.06);
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,6 +65,7 @@ import {
|
||||||
import {
|
import {
|
||||||
filterMenu,
|
filterMenu,
|
||||||
initData,
|
initData,
|
||||||
|
inItSelected,
|
||||||
drop,
|
drop,
|
||||||
select,
|
select,
|
||||||
getMaxDepth,
|
getMaxDepth,
|
||||||
|
@ -128,31 +129,70 @@ const getProvidersFn = async () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
getProvidersFn();
|
getProvidersFn();
|
||||||
function filterTree(nodes: Array<any>, selectedKeys: Array<any>) {
|
/**
|
||||||
|
* 作用:过滤掉非选中菜单重新组成新的数组
|
||||||
|
*/
|
||||||
|
// function filterTree(nodes: Array<any>, selectedKeys: Array<any>,parentId?:string) {
|
||||||
|
// const filtered = [];
|
||||||
|
// for (let i = 0; i < nodes.length; i++) {
|
||||||
|
// const node = nodes[i];
|
||||||
|
// if (!node.code) {
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
// node.parentId = parentId ? undefined : parentId
|
||||||
|
// if (selectedKeys.indexOf(node.code) !== -1) {
|
||||||
|
// filtered.push(node);
|
||||||
|
// if (node.children) {
|
||||||
|
// node.children = filterTree(node.children, selectedKeys,node.id);
|
||||||
|
// }
|
||||||
|
// } else if (node.children) {
|
||||||
|
// node.children = filterTree(node.children, selectedKeys,node.id);
|
||||||
|
// if (node.children.length > 0) {
|
||||||
|
// filtered.push(node);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return filtered;
|
||||||
|
// }
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param nodes 菜单数据
|
||||||
|
* @param selectedKeys 选中的菜单
|
||||||
|
* 选中和非选中改变show的值
|
||||||
|
*/
|
||||||
|
const dealTree = (nodes: Array<any>, selectedKeys: Array<any>,parentId?:string) =>{
|
||||||
const filtered = [];
|
const filtered = [];
|
||||||
for (let i = 0; i < nodes.length; i++) {
|
for (let i = 0; i < nodes.length; i++) {
|
||||||
const node = nodes[i];
|
const node = nodes[i];
|
||||||
if (!node.code) {
|
if (!node.code) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
node.parentId = parentId ? undefined : parentId
|
||||||
|
node?.options ? node.options.show = false : node.options = { show : false }
|
||||||
|
|
||||||
if (selectedKeys.indexOf(node.code) !== -1) {
|
if (selectedKeys.indexOf(node.code) !== -1) {
|
||||||
filtered.push(node);
|
node.options.show = true
|
||||||
if (node.children) {
|
if (node.children) {
|
||||||
node.children = filterTree(node.children, selectedKeys);
|
node.children = dealTree(node.children, selectedKeys,node.id);
|
||||||
}
|
}
|
||||||
} else if (node.children) {
|
} else if (node.children) {
|
||||||
node.children = filterTree(node.children, selectedKeys);
|
node.children = dealTree(node.children, selectedKeys,node.id);
|
||||||
if (node.children.length > 0) {
|
const children =node.children.filter((item:any)=>{
|
||||||
filtered.push(node);
|
item.options.show === true
|
||||||
|
})
|
||||||
|
if (children.length > 0) {
|
||||||
|
node.options.show = true
|
||||||
}
|
}
|
||||||
|
}else{
|
||||||
|
node.options.show = false
|
||||||
}
|
}
|
||||||
|
filtered.push(node)
|
||||||
}
|
}
|
||||||
return filtered;
|
return filtered;
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleOk = async () => {
|
const handleOk = async () => {
|
||||||
const _dataArr = filterTree(cloneDeep(treeData.value), selectedKeys.value);
|
// const _dataArr = filterTree(cloneDeep(treeData.value), selectedKeys.value);
|
||||||
|
const _dataArr = dealTree(cloneDeep(treeData.value),selectedKeys.value)
|
||||||
const _dataSorts = handleSorts(_dataArr)
|
const _dataSorts = handleSorts(_dataArr)
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
const res = await updateMenus(_dataSorts).catch(() => {});
|
const res = await updateMenus(_dataSorts).catch(() => {});
|
||||||
|
@ -203,13 +243,13 @@ const onDragend = (info: AntTreeNodeDropEvent) => {
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getSystemPermission_api().then((resp: any) => {
|
getSystemPermission_api().then((resp: any) => {
|
||||||
const filterBaseMenu = BaseMenu.filter(item => ![
|
// const filterBaseMenu = BaseMenu.filter(item => ![
|
||||||
USER_CENTER_MENU_CODE,messageSubscribe
|
// USER_CENTER_MENU_CODE,messageSubscribe
|
||||||
].includes(item.code))
|
// ].includes(item.code))
|
||||||
baseMenu.value = filterMenu(
|
// baseMenu.value = filterMenu(
|
||||||
resp.result.map((item: any) => JSON.parse(item).id),
|
// resp.result.map((item: any) => JSON.parse(item).id),
|
||||||
filterBaseMenu,
|
// filterBaseMenu,
|
||||||
);
|
// );
|
||||||
getMenuTree_api(params).then((resp: any) => {
|
getMenuTree_api(params).then((resp: any) => {
|
||||||
if (resp.status == 200) {
|
if (resp.status == 200) {
|
||||||
systemMenu.value = resp.result?.filter(
|
systemMenu.value = resp.result?.filter(
|
||||||
|
@ -219,17 +259,16 @@ onMounted(() => {
|
||||||
].includes(item.code),
|
].includes(item.code),
|
||||||
);
|
);
|
||||||
//初始化菜单
|
//初始化菜单
|
||||||
initData(baseMenu.value); // 不要克隆,通过引用 处理key和name
|
// initData(baseMenu.value); // 不要克隆,通过引用 处理key和name
|
||||||
const systemMenuData = initData(systemMenu.value);
|
const systemMenuData = inItSelected(systemMenu.value);
|
||||||
selectedKeys.value = systemMenuData.checkedKeys;
|
selectedKeys.value = systemMenuData.checkedKeys;
|
||||||
|
// const AllMenu = filterMenus(mergeArr(
|
||||||
const AllMenu = filterMenus(mergeArr(
|
// cloneDeep(baseMenu.value),
|
||||||
cloneDeep(baseMenu.value),
|
// cloneDeep(systemMenu.value),
|
||||||
cloneDeep(systemMenu.value),
|
// ))
|
||||||
))
|
// console.log(AllMenu);
|
||||||
console.log(AllMenu);
|
|
||||||
// 处理排序
|
// 处理排序
|
||||||
treeData.value = handleSortsArr(AllMenu);
|
treeData.value = handleSortsArr(systemMenu.value);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -115,9 +115,7 @@ export const initData = (Menu: any) => {
|
||||||
arr.forEach((item: any) => {
|
arr.forEach((item: any) => {
|
||||||
item.title = item.code;
|
item.title = item.code;
|
||||||
item.key = item.code; // treeData需要唯一key
|
item.key = item.code; // treeData需要唯一key
|
||||||
|
|
||||||
checkedKeys.push(item.code);
|
checkedKeys.push(item.code);
|
||||||
|
|
||||||
if (item?.children) {
|
if (item?.children) {
|
||||||
getMap(item?.children);
|
getMap(item?.children);
|
||||||
}
|
}
|
||||||
|
@ -127,6 +125,23 @@ export const initData = (Menu: any) => {
|
||||||
return { checkedKeys };
|
return { checkedKeys };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**通过options判断选中菜单 */
|
||||||
|
export const inItSelected = (Menu:any) =>{
|
||||||
|
const checkedKeys: any = [];
|
||||||
|
const getMap = (arr: any) => {
|
||||||
|
arr.forEach((item: any) => {
|
||||||
|
item.title = item.code;
|
||||||
|
item.key = item.code; // treeData需要唯一key
|
||||||
|
item?.options?.show ? checkedKeys.push(item.code) : '';
|
||||||
|
if (item?.children) {
|
||||||
|
getMap(item?.children);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
getMap(Menu);
|
||||||
|
return { checkedKeys };
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查找父级、子级code
|
* 查找父级、子级code
|
||||||
* @param data 当前完整的菜单
|
* @param data 当前完整的菜单
|
||||||
|
|
|
@ -62,6 +62,7 @@ const direction = [
|
||||||
'RadiusUpright',
|
'RadiusUpright',
|
||||||
'Fullscreen',
|
'Fullscreen',
|
||||||
'FullscreenExit',
|
'FullscreenExit',
|
||||||
|
'EnvironmentOutlined',
|
||||||
];
|
];
|
||||||
|
|
||||||
const suggestion = [
|
const suggestion = [
|
||||||
|
|
|
@ -41,11 +41,12 @@
|
||||||
<template #action="slotProps">
|
<template #action="slotProps">
|
||||||
<j-space :size="16">
|
<j-space :size="16">
|
||||||
<j-tooltip>
|
<j-tooltip>
|
||||||
<template #title>编辑</template>
|
<template #title>{{ slotProps?.options?.LowCode ? '低码创建的菜单不支持编辑' : '编辑' }}</template>
|
||||||
<j-button
|
<j-button
|
||||||
style="padding: 0"
|
style="padding: 0"
|
||||||
type="link"
|
type="link"
|
||||||
@click="table.toDetails(slotProps)"
|
@click="table.toDetails(slotProps)"
|
||||||
|
:disabled="slotProps?.options?.LowCode"
|
||||||
>
|
>
|
||||||
<AIcon type="EditOutlined" />
|
<AIcon type="EditOutlined" />
|
||||||
</j-button>
|
</j-button>
|
||||||
|
@ -53,8 +54,8 @@
|
||||||
<PermissionButton
|
<PermissionButton
|
||||||
type="link"
|
type="link"
|
||||||
:hasPermission="`${permission}:add`"
|
:hasPermission="`${permission}:add`"
|
||||||
:tooltip="{ title: '新增子菜单' }"
|
:tooltip="{ title: slotProps.level >= 3 ? '仅支持3级菜单' : '新增子菜单' }"
|
||||||
:disabled="slotProps.level >= 3"
|
:disabled="slotProps.level >= 3 || slotProps?.options?.LowCode"
|
||||||
@click="table.addChildren(slotProps)"
|
@click="table.addChildren(slotProps)"
|
||||||
>
|
>
|
||||||
<AIcon type="PlusCircleOutlined" />
|
<AIcon type="PlusCircleOutlined" />
|
||||||
|
@ -179,17 +180,34 @@ const table = reactive({
|
||||||
//过滤非集成的菜单
|
//过滤非集成的菜单
|
||||||
const item = {
|
const item = {
|
||||||
terms: [
|
terms: [
|
||||||
{
|
{
|
||||||
column: 'owner',
|
terms: [
|
||||||
termType: 'eq',
|
{
|
||||||
value: 'iot',
|
column: 'owner',
|
||||||
},
|
termType: 'eq',
|
||||||
{
|
value: 'iot',
|
||||||
column: 'owner',
|
},
|
||||||
termType: 'isnull',
|
{
|
||||||
value: '1',
|
column: 'owner',
|
||||||
type: 'or',
|
termType: 'isnull',
|
||||||
},
|
value: '1',
|
||||||
|
type: 'or',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
terms:[
|
||||||
|
{
|
||||||
|
terms:[
|
||||||
|
{
|
||||||
|
value:"%show\":true%",
|
||||||
|
termType:"like",
|
||||||
|
column:"options"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
const params = {
|
const params = {
|
||||||
|
@ -218,12 +236,12 @@ const table = reactive({
|
||||||
},
|
},
|
||||||
addChildren: (row: any) => {
|
addChildren: (row: any) => {
|
||||||
const sortIndex = row?.children?.length || 0;
|
const sortIndex = row?.children?.length || 0;
|
||||||
|
|
||||||
router.push(
|
router.push(
|
||||||
`/system/Menu/detail/:id?pid=${row.id}&basePath=${
|
`/system/Menu/detail/:id?pid=${row.id}&basePath=${
|
||||||
row.url || ''
|
row.url || ''
|
||||||
}&sortIndex=${sortIndex + 1}`,
|
}&sortIndex=${sortIndex + 1}&isChildren=${true}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
},
|
},
|
||||||
// 跳转至详情页
|
// 跳转至详情页
|
||||||
toDetails: (row: any) => {
|
toDetails: (row: any) => {
|
||||||
|
|
|
@ -54,8 +54,10 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { queryChannelConfig } from '@/api/system/noticeRule';
|
import { queryChannelConfig } from '@/api/system/noticeRule';
|
||||||
import Item from './components/Item/index.vue';
|
import Item from './components/Item/index.vue';
|
||||||
|
import { useMenuStore } from '@/store/menu';
|
||||||
const dataSource = [
|
const menuStore = useMenuStore();
|
||||||
|
let dataSource:any[] =[]
|
||||||
|
const systemNotice = [
|
||||||
{
|
{
|
||||||
provider: 'alarm',
|
provider: 'alarm',
|
||||||
name: '告警',
|
name: '告警',
|
||||||
|
@ -99,7 +101,40 @@ const dataSource = [
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const activeKey = ref<string[]>(['alarm', 'system-monitor', 'system-business']);
|
const lowCodeNotice = [
|
||||||
|
{
|
||||||
|
provider: 'workflow-notification',
|
||||||
|
name: '工作流通知',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
provider: 'workflow-task-todo',
|
||||||
|
name: '待办通知',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provider: 'workflow-task-reject',
|
||||||
|
name: '驳回通知',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provider: 'workflow-task-cc',
|
||||||
|
name: '抄送通知',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provider: 'workflow-process-finish',
|
||||||
|
name: '办结通知',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provider: 'workflow-process-repealed',
|
||||||
|
name: '关闭通知',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provider: 'workflow-task-transfer-todo',
|
||||||
|
name: '转办通知'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const activeKey = ref<string[]>();
|
||||||
|
|
||||||
const dataMap = new Map();
|
const dataMap = new Map();
|
||||||
|
|
||||||
|
@ -159,6 +194,13 @@ onMounted(() => {
|
||||||
// data.value = Array.from(dataMap).map((item) => {
|
// data.value = Array.from(dataMap).map((item) => {
|
||||||
// return item?.[1];
|
// return item?.[1];
|
||||||
// });
|
// });
|
||||||
|
if(menuStore.hasMenu('process')){
|
||||||
|
dataSource = [...systemNotice,...lowCodeNotice]
|
||||||
|
activeKey.value = ['alarm', 'system-monitor', 'system-business','workflow-notification']
|
||||||
|
}else{
|
||||||
|
dataSource = [...systemNotice]
|
||||||
|
activeKey.value = ['alarm', 'system-monitor', 'system-business']
|
||||||
|
}
|
||||||
handleSearch();
|
handleSearch();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -225,17 +225,17 @@ const _send = () => {
|
||||||
|
|
||||||
let url = props.selectApi?.url;
|
let url = props.selectApi?.url;
|
||||||
let params
|
let params
|
||||||
if (methodName === 'get'){
|
const urlParams = {};
|
||||||
const urlParams = {};
|
requestBody.params.paramsTable.forEach((item) => {
|
||||||
requestBody.params.paramsTable.forEach((item) => {
|
urlParams[item.name] = item.value;
|
||||||
urlParams[item.name] = item.value;
|
if (url.includes(`{${item.name}}`))
|
||||||
if (url.includes(`{${item.name}}`))
|
url = url.replace(`{${item.name}}`, item.value);
|
||||||
url = url.replace(`{${item.name}}`, item.value);
|
|
||||||
});
|
});
|
||||||
params = {
|
if (methodName === 'get'){
|
||||||
...JSON.parse(requestBody.code || '{}'),
|
params = {
|
||||||
...urlParams,
|
...JSON.parse(requestBody.code || '{}'),
|
||||||
};
|
...urlParams,
|
||||||
|
};
|
||||||
}else{
|
}else{
|
||||||
params = JSON.parse(requestBody.code || '{}')
|
params = JSON.parse(requestBody.code || '{}')
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,240 @@
|
||||||
|
<template>
|
||||||
|
<j-input
|
||||||
|
placeholder="请输入区域名称或行政区划代码"
|
||||||
|
class="search-input"
|
||||||
|
v-model:value="searchValue"
|
||||||
|
@change="(e) => onSearch(e.target.value)"
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<AIcon type="SearchOutlined" style="color: rgba(0, 0, 0, 0.45)" />
|
||||||
|
</template>
|
||||||
|
</j-input>
|
||||||
|
<j-button @click="onAdd" type="primary" class="btn">新增区域</j-button>
|
||||||
|
<j-tree
|
||||||
|
class="draggable-tree"
|
||||||
|
draggable
|
||||||
|
block-node
|
||||||
|
v-if="treeData.length"
|
||||||
|
:tree-data="_treeData"
|
||||||
|
@drop="onDrop"
|
||||||
|
:defaultExpandAll="true"
|
||||||
|
:height="700"
|
||||||
|
:show-line="{ showLeafIcon: false }"
|
||||||
|
:show-icon="true"
|
||||||
|
>
|
||||||
|
<template #title="_data">
|
||||||
|
<div class="tree-box">
|
||||||
|
<div class="name">
|
||||||
|
<j-ellipsis>{{ _data?.name }}</j-ellipsis>
|
||||||
|
</div>
|
||||||
|
<div class="actions">
|
||||||
|
<j-space :size="8">
|
||||||
|
<j-tooltip title="重命名">
|
||||||
|
<j-button
|
||||||
|
@click.stop="onEdit(_data?.data)"
|
||||||
|
class="actions-btn"
|
||||||
|
type="link"
|
||||||
|
><AIcon type="EditOutlined"
|
||||||
|
/></j-button>
|
||||||
|
</j-tooltip>
|
||||||
|
<j-tooltip title="新增子区域">
|
||||||
|
<j-button
|
||||||
|
@click.stop="onAdd(_data?.data)"
|
||||||
|
class="actions-btn"
|
||||||
|
type="link"
|
||||||
|
><AIcon type="PlusCircleOutlined"
|
||||||
|
/></j-button>
|
||||||
|
</j-tooltip>
|
||||||
|
<j-tooltip title="删除">
|
||||||
|
<j-popconfirm @confirm="onRemove(_data?.id)">
|
||||||
|
<j-button
|
||||||
|
@click.stop
|
||||||
|
class="actions-btn"
|
||||||
|
type="link"
|
||||||
|
danger
|
||||||
|
><AIcon type="DeleteOutlined"
|
||||||
|
/></j-button>
|
||||||
|
</j-popconfirm>
|
||||||
|
</j-tooltip>
|
||||||
|
</j-space>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</j-tree>
|
||||||
|
<j-empty v-else style="margin-top: 150px" />
|
||||||
|
<Save
|
||||||
|
:mode="mode"
|
||||||
|
v-if="visible"
|
||||||
|
:data="current"
|
||||||
|
@save="onSave"
|
||||||
|
@close="onClose"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { onlyMessage } from '@/utils/comm';
|
||||||
|
import { debounce } from 'lodash-es';
|
||||||
|
import { onMounted, ref, watch } from 'vue';
|
||||||
|
import Save from '../Save/index.vue';
|
||||||
|
import { getRegionTree, delRegion } from '@/api/system/region';
|
||||||
|
|
||||||
|
const treeData = ref<any[]>([]);
|
||||||
|
const _treeData = ref<any[]>([]);
|
||||||
|
const visible = ref<boolean>(false);
|
||||||
|
const current = ref<any>({});
|
||||||
|
const mode = ref<'add' | 'edit'>('add');
|
||||||
|
const searchValue = ref<string>();
|
||||||
|
|
||||||
|
const filterTreeNodes = (tree: any[], condition: string) => {
|
||||||
|
return tree.filter((item) => {
|
||||||
|
if (item?.title && item.title.includes(condition)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (item?.code && item.code.includes(condition)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.children) {
|
||||||
|
item.children = filterTreeNodes(item.children, condition);
|
||||||
|
return !!item.children.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSearch = debounce((v: string) => {
|
||||||
|
_treeData.value = filterTreeNodes(treeData.value, v);
|
||||||
|
});
|
||||||
|
|
||||||
|
const onSave = () => {
|
||||||
|
visible.value = false;
|
||||||
|
handleSearch()
|
||||||
|
};
|
||||||
|
|
||||||
|
const onClose = () => {
|
||||||
|
visible.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onEdit = (_data: any) => {
|
||||||
|
mode.value = 'edit';
|
||||||
|
current.value = _data;
|
||||||
|
visible.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onRemove = async (id: string) => {
|
||||||
|
const resp = await delRegion(id);
|
||||||
|
if (resp.success) {
|
||||||
|
onlyMessage('操作成功!');
|
||||||
|
handleSearch();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onAdd = (_data?: any) => {
|
||||||
|
mode.value = 'add';
|
||||||
|
current.value = _data ? _data : {};
|
||||||
|
visible.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDrop = (info: any) => {
|
||||||
|
const dropKey = info.node.key;
|
||||||
|
const dragKey = info.dragNode.key;
|
||||||
|
const dropPos = info.node.pos.split('-');
|
||||||
|
const dropPosition =
|
||||||
|
info.dropPosition - Number(dropPos[dropPos.length - 1]);
|
||||||
|
const loop = (data: any, key: string | number, callback: any) => {
|
||||||
|
data.forEach((item: any, index: number) => {
|
||||||
|
if (item.key === key) {
|
||||||
|
return callback(item, index, data);
|
||||||
|
}
|
||||||
|
if (item.children) {
|
||||||
|
return loop(item.children, key, callback);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const data = [...treeData.value];
|
||||||
|
// // Find dragObject
|
||||||
|
let dragObj: any;
|
||||||
|
loop(data, dragKey, (item: any, index: number, arr: any[]) => {
|
||||||
|
arr.splice(index, 1);
|
||||||
|
dragObj = item;
|
||||||
|
});
|
||||||
|
if (!info.dropToGap) {
|
||||||
|
// Drop on the content
|
||||||
|
loop(data, dropKey, (item: any) => {
|
||||||
|
item.children = item.children || [];
|
||||||
|
/// where to insert 示例添加到头部,可以是随意位置
|
||||||
|
item.children.unshift(dragObj);
|
||||||
|
});
|
||||||
|
} else if (
|
||||||
|
(info.node.children || []).length > 0 && // Has children
|
||||||
|
info.node.expanded && // Is expanded
|
||||||
|
dropPosition === 1 // On the bottom gap
|
||||||
|
) {
|
||||||
|
loop(data, dropKey, (item: any) => {
|
||||||
|
item.children = item.children || [];
|
||||||
|
// where to insert 示例添加到头部,可以是随意位置
|
||||||
|
item.children.unshift(dragObj);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
let ar: any[] = [];
|
||||||
|
let i = 0;
|
||||||
|
loop(data, dropKey, (_item: any, index: number, arr: any[]) => {
|
||||||
|
ar = arr;
|
||||||
|
i = index;
|
||||||
|
});
|
||||||
|
if (dropPosition === -1) {
|
||||||
|
ar.splice(i, 0, dragObj);
|
||||||
|
} else {
|
||||||
|
ar.splice(i + 1, 0, dragObj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
treeData.value = data;
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => treeData.value,
|
||||||
|
() => {
|
||||||
|
if (searchValue.value) {
|
||||||
|
onSearch(searchValue.value);
|
||||||
|
} else {
|
||||||
|
_treeData.value = treeData.value;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deep: true,
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleSearch = async () => {
|
||||||
|
const resp = await getRegionTree();
|
||||||
|
if (resp.success) {
|
||||||
|
treeData.value = resp?.result || [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
handleSearch();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.btn {
|
||||||
|
width: 100%;
|
||||||
|
margin: 18px 0;
|
||||||
|
}
|
||||||
|
.tree-box {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
.actions-btn {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,304 @@
|
||||||
|
<template>
|
||||||
|
<div class="region-map">
|
||||||
|
<AMapComponent
|
||||||
|
@init="initMap"
|
||||||
|
>
|
||||||
|
<el-amap-polygon
|
||||||
|
v-if="overlay.type === 'polygon'"
|
||||||
|
:key="JSON.stringify(overlay.path || [])"
|
||||||
|
:editable="overlay.editable"
|
||||||
|
:path="overlay.path"
|
||||||
|
:visible="visible"
|
||||||
|
@addnode="polygonDraw"
|
||||||
|
@adjust="polygonDraw"
|
||||||
|
@init="overlaysInit"
|
||||||
|
@removenode="polygonDraw"
|
||||||
|
/>
|
||||||
|
<el-amap-circle
|
||||||
|
v-else-if="overlay.type === 'circle'"
|
||||||
|
:key="`${overlay.radius}_${JSON.stringify(overlay.path || [])}`"
|
||||||
|
:center="overlay.path"
|
||||||
|
:editable="overlay.editable"
|
||||||
|
:radius="overlay.radius"
|
||||||
|
:visible="visible"
|
||||||
|
@adjust="circleDraw"
|
||||||
|
@init="overlaysInit"
|
||||||
|
/>
|
||||||
|
<el-amap-rectangle
|
||||||
|
v-else-if="overlay.type === 'rectangle'"
|
||||||
|
:key="JSON.stringify(overlay.path || [])"
|
||||||
|
:bounds="overlay.path"
|
||||||
|
:editable="overlay.editable"
|
||||||
|
:visible="visible"
|
||||||
|
@adjust="rectangleDraw"
|
||||||
|
@init="overlaysInit"
|
||||||
|
/>
|
||||||
|
<el-amap-mouse-tool
|
||||||
|
v-else-if="overlay.type === 'create' && toolType"
|
||||||
|
:type="toolType"
|
||||||
|
@draw="toolDraw"
|
||||||
|
/>
|
||||||
|
</AMapComponent>
|
||||||
|
<div v-show="overlay.editable || overlay.type === 'create'" class="map-tool">
|
||||||
|
<div class="map-tool-content">
|
||||||
|
<div class="tool-item-group">
|
||||||
|
<div class="tool-item">
|
||||||
|
<j-tooltip title="双击保存描点" >
|
||||||
|
<AIcon type="QuestionCircleOutlined" />
|
||||||
|
</j-tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="tool-item-group">
|
||||||
|
<div :class="{'tool-item': true, 'active': toolType === 'rectangle'}" @click="() => { drawOverlays('rectangle') }">
|
||||||
|
<j-tooltip title="矩形" >
|
||||||
|
<AIcon type="icon-huajuxing" />
|
||||||
|
</j-tooltip>
|
||||||
|
</div>
|
||||||
|
<div :class="{'tool-item': true, 'active': toolType === 'circle'}" @click="() => { drawOverlays('circle') }">
|
||||||
|
<j-tooltip title="圆" >
|
||||||
|
<AIcon type="icon-draw-circle" />
|
||||||
|
</j-tooltip>
|
||||||
|
</div>
|
||||||
|
<div :class="{'tool-item': true, 'active': toolType === 'polygon'}" @click="() => { drawOverlays('polygon') }">
|
||||||
|
<j-tooltip title="多边形" >
|
||||||
|
<AIcon type="icon-huaduobianxing" />
|
||||||
|
</j-tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- <div class="tool-item-group">-->
|
||||||
|
<!-- <div class="tool-item">-->
|
||||||
|
<!-- <j-tooltip title="缩放" >-->
|
||||||
|
<!-- <AIcon type="GetwayOutlined" />-->
|
||||||
|
<!-- </j-tooltip>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- <div class="tool-item">-->
|
||||||
|
<!-- <j-tooltip title="旋转" >-->
|
||||||
|
<!-- <AIcon type="GetwayOutlined" />-->
|
||||||
|
<!-- </j-tooltip>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<div class="tool-item-group">
|
||||||
|
<div :class="{'tool-item': true, 'disabled': historyList.length <= 1 }" @click="cancel">
|
||||||
|
<j-tooltip title="撤销" >
|
||||||
|
<AIcon type="RollbackOutlined" />
|
||||||
|
</j-tooltip>
|
||||||
|
</div>
|
||||||
|
<div class="tool-item">
|
||||||
|
<j-tooltip title="删除" @click="deleteOverlays">
|
||||||
|
<AIcon type="DeleteOutlined" />
|
||||||
|
</j-tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script name="RegionMap" setup>
|
||||||
|
const props = defineProps({
|
||||||
|
path: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
radius: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
default: 'polygon'
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits('dragend')
|
||||||
|
const MapRef = ref()
|
||||||
|
const toolType = ref('')
|
||||||
|
const historyList = ref([])
|
||||||
|
|
||||||
|
const overlay = reactive({
|
||||||
|
type: props.type,
|
||||||
|
path: props.path,
|
||||||
|
center: [],
|
||||||
|
editable: false,
|
||||||
|
status: 0 // 0 预览;1 编辑
|
||||||
|
})
|
||||||
|
|
||||||
|
const visible = computed(() => {
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
const setHistory = (data) => {
|
||||||
|
if (historyList.value.length > 10) {
|
||||||
|
historyList.value.shift()
|
||||||
|
}
|
||||||
|
historyList.value.push(data)
|
||||||
|
}
|
||||||
|
const initMap = (e) => {
|
||||||
|
MapRef.value = e
|
||||||
|
}
|
||||||
|
|
||||||
|
const polygonDraw = (e) => {
|
||||||
|
|
||||||
|
const target = e.target
|
||||||
|
const path = target.getPath()
|
||||||
|
setHistory({
|
||||||
|
type: 'polygon',
|
||||||
|
toolType: toolType.value,
|
||||||
|
editable: true,
|
||||||
|
path: path.map(item => [item.lng, item.lat]),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const circleDraw = (e) => {
|
||||||
|
setHistory({
|
||||||
|
type: 'circle',
|
||||||
|
toolType: toolType.value,
|
||||||
|
editable: true,
|
||||||
|
path: [e.lnglat.lng, e.lnglat.lat],
|
||||||
|
radius: e.radius
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const rectangleDraw = (e) => {
|
||||||
|
const northEast = e.bounds.getNorthEast()
|
||||||
|
const southWest = e.bounds.getSouthWest()
|
||||||
|
const path = [
|
||||||
|
[southWest.lng, southWest.lat],
|
||||||
|
[northEast.lng, northEast.lat]
|
||||||
|
]
|
||||||
|
|
||||||
|
setHistory({
|
||||||
|
type: 'rectangle',
|
||||||
|
toolType: toolType.value,
|
||||||
|
editable: true,
|
||||||
|
path: path,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const overlaysInit = (e) => {
|
||||||
|
if (MapRef.value) {
|
||||||
|
MapRef.value.setFitView(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const drawOverlays = (e) => {
|
||||||
|
overlay.type = 'create'
|
||||||
|
toolType.value = e
|
||||||
|
}
|
||||||
|
|
||||||
|
const toolDraw = (e) => {
|
||||||
|
if (toolType.value === 'circle') {
|
||||||
|
overlay.path = e.center
|
||||||
|
overlay.radius = e.radius
|
||||||
|
} else {
|
||||||
|
overlay.path = e
|
||||||
|
}
|
||||||
|
overlay.editable = true
|
||||||
|
overlay.type = toolType.value
|
||||||
|
setHistory({
|
||||||
|
type: toolType.value,
|
||||||
|
toolType: toolType.value,
|
||||||
|
editable: overlay.editable,
|
||||||
|
path: overlay.path,
|
||||||
|
radius: overlay.radius
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteOverlays = () => {
|
||||||
|
overlay.type = 'create'
|
||||||
|
overlay.editable = false
|
||||||
|
toolType.value = ''
|
||||||
|
|
||||||
|
setHistory({
|
||||||
|
type: 'create',
|
||||||
|
toolType: '',
|
||||||
|
editable: false,
|
||||||
|
path: [],
|
||||||
|
radius: 0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const cancel = () => {
|
||||||
|
if (historyList.value.length > 1) {
|
||||||
|
historyList.value.pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
const oldData = historyList.value[historyList.value.length - 1]
|
||||||
|
|
||||||
|
if (oldData) {
|
||||||
|
overlay.type = oldData.type
|
||||||
|
overlay.editable = oldData.editable
|
||||||
|
overlay.path = oldData.path
|
||||||
|
overlay.radius = oldData.radius
|
||||||
|
toolType.value = oldData.toolType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.region-map {
|
||||||
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.map-tool{
|
||||||
|
position: absolute;
|
||||||
|
top: 20%;
|
||||||
|
right: 20px;
|
||||||
|
|
||||||
|
|
||||||
|
.map-tool-content {
|
||||||
|
display: flex;
|
||||||
|
gap: 24px;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.tool-item-group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
border: 1px solid #e3e3e3;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 0 0 16px rgba(#000, .15);
|
||||||
|
|
||||||
|
.tool-item {
|
||||||
|
padding: 4px 6px;
|
||||||
|
color: #333;
|
||||||
|
font-size: 16px;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
border-top-left-radius: 4px;
|
||||||
|
border-top-right-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom-left-radius: 4px;
|
||||||
|
border-bottom-right-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(:first-child) {
|
||||||
|
border-top: 1px solid #e3e3e3;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: var(--ant-primary-color);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
cursor: not-allowed !important;
|
||||||
|
background-color: #efefef;
|
||||||
|
|
||||||
|
> span {
|
||||||
|
cursor: not-allowed !important;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,124 @@
|
||||||
|
<template>
|
||||||
|
<j-tree-select
|
||||||
|
showSearch
|
||||||
|
placeholder="1级区域不需要选择"
|
||||||
|
:tree-data="builtInAreaList"
|
||||||
|
:value="_value"
|
||||||
|
:field-names="{
|
||||||
|
children: 'children',
|
||||||
|
label: 'name',
|
||||||
|
value: 'code',
|
||||||
|
}"
|
||||||
|
tree-node-filter-prop="name"
|
||||||
|
@select="onSelect"
|
||||||
|
>
|
||||||
|
<template #title="{ name, code }">
|
||||||
|
<span v-if="code">{{ name }} | {{ code }}</span>
|
||||||
|
</template>
|
||||||
|
</j-tree-select>
|
||||||
|
<j-checkbox
|
||||||
|
@change="onCheckChange"
|
||||||
|
v-model:checked="_checked"
|
||||||
|
style="margin-top: 5px"
|
||||||
|
>同步添加下一级区域</j-checkbox
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { getBuiltinRegionTree } from '@/api/system/region';
|
||||||
|
import { onMounted, ref, watch } from 'vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
value: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
children: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const emits = defineEmits(['update:value', 'update:name', 'update:children']);
|
||||||
|
|
||||||
|
const features = ref<any>({});
|
||||||
|
const _value = ref<string>();
|
||||||
|
const builtInAreaList = ref<Record<string, any>[]>([]);
|
||||||
|
const _checked = ref<boolean>(false);
|
||||||
|
|
||||||
|
const queryBuiltinRegionTree = async () => {
|
||||||
|
const resp = await getBuiltinRegionTree({
|
||||||
|
paging: false,
|
||||||
|
sorts: [{ name: 'sortIndex', order: 'asc' }],
|
||||||
|
});
|
||||||
|
if (resp.success) {
|
||||||
|
builtInAreaList.value = resp?.result || [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onCheckChange = (e: any) => {
|
||||||
|
if (e.target.checked) {
|
||||||
|
emits('update:children', features.value?.children || []);
|
||||||
|
} else {
|
||||||
|
emits('update:children', []);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getObj = (node: any): any => {
|
||||||
|
const _children = (node?.children || []).map((item: any) => {
|
||||||
|
return {
|
||||||
|
code: item.code,
|
||||||
|
name: item.name,
|
||||||
|
parentId: item.parentId,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
code: node.code,
|
||||||
|
name: node.name,
|
||||||
|
parentId: node.parentId,
|
||||||
|
children: _children,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSelect = (val: string, node: any) => {
|
||||||
|
features.value = getObj(node);
|
||||||
|
_value.value = val;
|
||||||
|
emits('update:name', features.value?.name);
|
||||||
|
emits('update:value', features.value?.code);
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
queryBuiltinRegionTree();
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.value,
|
||||||
|
() => {
|
||||||
|
if (props.value) {
|
||||||
|
_value.value = props.value
|
||||||
|
} else {
|
||||||
|
emits('update:name', '中国');
|
||||||
|
emits('update:value', 100000);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deep: true,
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.children,
|
||||||
|
() => {
|
||||||
|
_checked.value = !!props.children?.length
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deep: true,
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
</script>
|
|
@ -0,0 +1,45 @@
|
||||||
|
<template>
|
||||||
|
<j-button @click="onClick" v-if="!_value.length" type="link"
|
||||||
|
>请在地图上描点</j-button
|
||||||
|
>
|
||||||
|
<div v-else>已完成描点<j-button type="link" @click="onEdit">编辑</j-button></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, watch, inject } from 'vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
value: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const emits = defineEmits(['update:value', 'close']);
|
||||||
|
|
||||||
|
const _value = ref<any[]>([]);
|
||||||
|
|
||||||
|
const __data: any = inject('system-region')
|
||||||
|
|
||||||
|
const onClick = () => {
|
||||||
|
console.log(__data)
|
||||||
|
__data.type.value = 'edit'
|
||||||
|
emits('close')
|
||||||
|
};
|
||||||
|
|
||||||
|
const onEdit = () => {
|
||||||
|
// __data.type.value = 'edit'
|
||||||
|
// emits('close')
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.value,
|
||||||
|
() => {
|
||||||
|
console.log(props.value)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deep: true,
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
</script>
|
|
@ -0,0 +1,234 @@
|
||||||
|
<template>
|
||||||
|
<j-modal
|
||||||
|
:maskClosable="false"
|
||||||
|
width="650px"
|
||||||
|
:visible="true"
|
||||||
|
:title="mode === 'edit' ? '编辑区域' : '新增区域'"
|
||||||
|
@ok="handleSave"
|
||||||
|
@cancel="handleCancel"
|
||||||
|
:confirmLoading="loading"
|
||||||
|
>
|
||||||
|
<div style="margin-top: 10px">
|
||||||
|
<j-form :layout="'vertical'" ref="formRef" :model="modelRef">
|
||||||
|
<j-form-item name="parentId" label="上级区域">
|
||||||
|
<j-tree-select
|
||||||
|
showSearch
|
||||||
|
v-model:value="modelRef.parentId"
|
||||||
|
placeholder="1级区域不需要选择"
|
||||||
|
:tree-data="areaList"
|
||||||
|
allowClear
|
||||||
|
:field-names="{
|
||||||
|
children: 'children',
|
||||||
|
label: 'name',
|
||||||
|
value: 'id',
|
||||||
|
}"
|
||||||
|
tree-node-filter-prop="name"
|
||||||
|
/>
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item :name="['properties', 'type']" label="添加方式">
|
||||||
|
<j-radio-group
|
||||||
|
v-model:value="modelRef.properties.type"
|
||||||
|
button-style="solid"
|
||||||
|
@change="onChange"
|
||||||
|
>
|
||||||
|
<j-radio-button value="builtin"
|
||||||
|
>内置行政区</j-radio-button
|
||||||
|
>
|
||||||
|
<j-radio-button value="Custom"
|
||||||
|
>自定义数据</j-radio-button
|
||||||
|
>
|
||||||
|
</j-radio-group>
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item v-if="modelRef.properties.type === 'builtin'">
|
||||||
|
<BuildIn
|
||||||
|
v-model:value="modelRef.code"
|
||||||
|
v-model:children="modelRef.children"
|
||||||
|
v-model:name="modelRef.name"
|
||||||
|
/>
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item
|
||||||
|
label="区域名称"
|
||||||
|
name="name"
|
||||||
|
required
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入区域名称',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
max: 64,
|
||||||
|
message: '最多输入64个字符',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
validator: vailName,
|
||||||
|
trigger: 'blur',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<j-input
|
||||||
|
v-model:value="modelRef.name"
|
||||||
|
placeholder="请输入区域名称"
|
||||||
|
/>
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item
|
||||||
|
label="行政区划代码"
|
||||||
|
name="code"
|
||||||
|
required
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入行政区划代码',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
validator: vailCode,
|
||||||
|
trigger: 'blur',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<j-input-number
|
||||||
|
v-model:value="modelRef.code"
|
||||||
|
style="width: 100%"
|
||||||
|
placeholder="请输入行政区划代码"
|
||||||
|
/>
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item
|
||||||
|
v-if="modelRef.properties.type !== 'builtin'"
|
||||||
|
label="区划划分"
|
||||||
|
required
|
||||||
|
name="features"
|
||||||
|
>
|
||||||
|
<TracePoint @close="emit('close')" v-model:value="modelRef.features" />
|
||||||
|
</j-form-item>
|
||||||
|
</j-form>
|
||||||
|
</div>
|
||||||
|
</j-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, watch, reactive, PropType, onMounted } from 'vue';
|
||||||
|
import TracePoint from './TracePoint.vue';
|
||||||
|
import BuildIn from './BuildIn.vue';
|
||||||
|
import {
|
||||||
|
validateName,
|
||||||
|
getRegionTree,
|
||||||
|
validateCode,
|
||||||
|
updateRegion,
|
||||||
|
} from '@/api/system/region';
|
||||||
|
import { onlyMessage } from '@/utils/comm';
|
||||||
|
|
||||||
|
const emit = defineEmits(['close', 'save']);
|
||||||
|
const props = defineProps({
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
mode: {
|
||||||
|
type: String as PropType<'add' | 'edit'>,
|
||||||
|
default: 'add',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const areaList = ref<Record<string, any>[]>([]);
|
||||||
|
const loading = ref<boolean>(false);
|
||||||
|
|
||||||
|
const formRef = ref();
|
||||||
|
|
||||||
|
const init = {
|
||||||
|
parentId: undefined,
|
||||||
|
id: undefined,
|
||||||
|
name: undefined,
|
||||||
|
code: undefined,
|
||||||
|
features: undefined,
|
||||||
|
children: [],
|
||||||
|
properties: {
|
||||||
|
type: 'builtin',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const modelRef = reactive(init);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.data,
|
||||||
|
() => {
|
||||||
|
Object.assign(modelRef, {});
|
||||||
|
if (props.mode === 'add' && props.data?.id) {
|
||||||
|
// 添加子
|
||||||
|
Object.assign(modelRef, {
|
||||||
|
...init,
|
||||||
|
parentId: props.data.id,
|
||||||
|
});
|
||||||
|
} else if (props.mode === 'edit') {
|
||||||
|
// 编辑
|
||||||
|
Object.assign(modelRef, props.data);
|
||||||
|
} else {
|
||||||
|
Object.assign(modelRef, init);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true, deep: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
emit('close');
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSave = () => {
|
||||||
|
formRef.value
|
||||||
|
.validate()
|
||||||
|
.then(async (_data: any) => {
|
||||||
|
loading.value = true;
|
||||||
|
const resp = await updateRegion({
|
||||||
|
...props.data,
|
||||||
|
...modelRef,
|
||||||
|
}).finally(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
if (resp.status === 200) {
|
||||||
|
onlyMessage('操作成功!');
|
||||||
|
emit('save');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err: any) => {
|
||||||
|
console.log('error', err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const vailName = async (_: Record<string, any>, value: string) => {
|
||||||
|
if (!props?.data?.id && value) {
|
||||||
|
const resp = await validateName(value, props.data.id);
|
||||||
|
if (resp.status === 200 && !resp.result?.passed) {
|
||||||
|
return Promise.reject(resp.result?.reason || '区域名称重复');
|
||||||
|
} else {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const vailCode = async (_: Record<string, any>, value: string) => {
|
||||||
|
if (!props?.data?.id && value) {
|
||||||
|
const resp = await validateCode(value, props.data.id);
|
||||||
|
if (resp.status === 200 && !resp.result?.passed) {
|
||||||
|
return Promise.reject(resp.result?.reason || '行政区域代码重复');
|
||||||
|
} else {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onChange = () => {
|
||||||
|
modelRef.features = undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSearch = async () => {
|
||||||
|
const resp = await getRegionTree();
|
||||||
|
if (resp.success) {
|
||||||
|
areaList.value = resp?.result || [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
handleSearch();
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,65 @@
|
||||||
|
<template>
|
||||||
|
<page-container>
|
||||||
|
<full-page>
|
||||||
|
<div class="region">
|
||||||
|
<div class="left">
|
||||||
|
<LeftTree />
|
||||||
|
<div class="mask" v-if="type === 'edit'"></div>
|
||||||
|
</div>
|
||||||
|
<div class="right">
|
||||||
|
<Map :path="path" :type="mapType" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</full-page>
|
||||||
|
</page-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts" name="RegionMange">
|
||||||
|
import LeftTree from './LeftTree/index.vue'
|
||||||
|
import Map from './MapTool/map.vue'
|
||||||
|
import FullPage from "components/Layout/FullPage.vue";
|
||||||
|
import { provide } from 'vue'
|
||||||
|
|
||||||
|
const path = ref([[121.5273285, 31.21515044], [121.5293285, 31.21515044], [121.5293285, 31.21915044], [121.5273285, 31.21515044]])
|
||||||
|
const type = ref<'view' | 'edit'>('view')
|
||||||
|
const mapType = ref<string>('create')
|
||||||
|
|
||||||
|
provide('system-region', {
|
||||||
|
type,
|
||||||
|
mapType: '',
|
||||||
|
path
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.region {
|
||||||
|
display: flex;
|
||||||
|
gap: 24px;
|
||||||
|
height: 100%;
|
||||||
|
padding: 16px;
|
||||||
|
|
||||||
|
.left {
|
||||||
|
width: 300px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
width: 100%;
|
||||||
|
margin: 18px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mask {
|
||||||
|
width: 100%;
|
||||||
|
position: absolute;
|
||||||
|
height: 100%;
|
||||||
|
background-color: lightgray;
|
||||||
|
opacity: .5;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.right {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -10,22 +10,9 @@
|
||||||
class="edit-dialog-container"
|
class="edit-dialog-container"
|
||||||
>
|
>
|
||||||
<j-form ref="formRef" :model="form.data" layout="vertical">
|
<j-form ref="formRef" :model="form.data" layout="vertical">
|
||||||
<j-form-item
|
|
||||||
label="名称"
|
|
||||||
name="name"
|
|
||||||
:rules="[
|
|
||||||
{ required: true, message: '请输入名称' },
|
|
||||||
{ max: 64, message: '最多可输入64个字符' },
|
|
||||||
]"
|
|
||||||
>
|
|
||||||
<j-input
|
|
||||||
v-model:value="form.data.name"
|
|
||||||
placeholder="请输入名称"
|
|
||||||
/>
|
|
||||||
</j-form-item>
|
|
||||||
<j-form-item
|
<j-form-item
|
||||||
name="relation"
|
name="relation"
|
||||||
label="标识"
|
label="关系标识"
|
||||||
:rules="[
|
:rules="[
|
||||||
{ required: true, message: '请输入标识' },
|
{ required: true, message: '请输入标识' },
|
||||||
{ max: 64, message: '最多可输入64个字符' },
|
{ max: 64, message: '最多可输入64个字符' },
|
||||||
|
@ -83,6 +70,37 @@
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
</j-col>
|
</j-col>
|
||||||
</j-row>
|
</j-row>
|
||||||
|
<j-form-item
|
||||||
|
label="正向关系名称"
|
||||||
|
name="name"
|
||||||
|
:rules="[
|
||||||
|
{ required: true, message: '请输入名称' },
|
||||||
|
{ max: 64, message: '最多可输入64个字符' },
|
||||||
|
{ required: true , validator:validateName, trigger: 'blur',}
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<j-input
|
||||||
|
v-model:value="form.data.name"
|
||||||
|
placeholder="请输入名称"
|
||||||
|
/>
|
||||||
|
<span class="example">正向关系示例:用户张三是001号视频设备的管理员</span>
|
||||||
|
</j-form-item>
|
||||||
|
|
||||||
|
<j-form-item
|
||||||
|
label="反向关系名称"
|
||||||
|
name="reverseName"
|
||||||
|
:rules="[
|
||||||
|
{ required: true, message: '请输入名称' },
|
||||||
|
{ max: 64, message: '最多可输入64个字符' },
|
||||||
|
{ required: true , validator:validateName, trigger: 'blur',}
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<j-input
|
||||||
|
v-model:value="form.data.reverseName"
|
||||||
|
placeholder="请输入名称"
|
||||||
|
/>
|
||||||
|
<span class="example">反向关系示例:001号视频设备是用户张三的管辖设备</span>
|
||||||
|
</j-form-item>
|
||||||
<j-form-item name="description" label="说明">
|
<j-form-item name="description" label="说明">
|
||||||
<j-textarea
|
<j-textarea
|
||||||
v-model:value="form.data.description"
|
v-model:value="form.data.description"
|
||||||
|
@ -115,6 +133,7 @@ const props = defineProps<{
|
||||||
}>();
|
}>();
|
||||||
// 弹窗相关
|
// 弹窗相关
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
|
const targetList = ref([])
|
||||||
const dialogTitle = computed(() => (props.data.id ? '编辑' : '新增'));
|
const dialogTitle = computed(() => (props.data.id ? '编辑' : '新增'));
|
||||||
const confirm = () => {
|
const confirm = () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
|
@ -180,6 +199,7 @@ const form = reactive({
|
||||||
getObjectList: () => {
|
getObjectList: () => {
|
||||||
getObjectList_api().then((resp: any) => {
|
getObjectList_api().then((resp: any) => {
|
||||||
form.objectList = resp.result;
|
form.objectList = resp.result;
|
||||||
|
targetList.value = resp.result
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
submit: () => {
|
submit: () => {
|
||||||
|
@ -187,7 +207,7 @@ const form = reactive({
|
||||||
...form.data,
|
...form.data,
|
||||||
objectTypeName: form.objectList.find(
|
objectTypeName: form.objectList.find(
|
||||||
(item) => item.id === form.data.objectType,
|
(item) => item.id === form.data.objectType,
|
||||||
).name,
|
)?.name,
|
||||||
targetTypeName: targetList.value.find(
|
targetTypeName: targetList.value.find(
|
||||||
(item: dictItemType) => item.id === form.data.targetType,
|
(item: dictItemType) => item.id === form.data.targetType,
|
||||||
)?.name,
|
)?.name,
|
||||||
|
@ -196,13 +216,17 @@ const form = reactive({
|
||||||
return api(params);
|
return api(params);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const targetList = computed(() =>
|
const validateName = async(_:any,value:any)=>{
|
||||||
form.data.objectType === 'device' ? [{ id: 'user', name: '用户' }] : [],
|
if(!value){
|
||||||
);
|
return Promise.resolve()
|
||||||
|
}
|
||||||
|
return form.data.reverseName === form.data.name ? Promise.reject('不能使用相同的关系名称') : Promise.resolve()
|
||||||
|
}
|
||||||
form.getObjectList();
|
form.getObjectList();
|
||||||
|
|
||||||
type formType = {
|
type formType = {
|
||||||
name: string;
|
name: string;
|
||||||
|
reverseName: string;
|
||||||
relation: string;
|
relation: string;
|
||||||
objectType: string | undefined;
|
objectType: string | undefined;
|
||||||
targetType: string | undefined;
|
targetType: string | undefined;
|
||||||
|
@ -210,3 +234,9 @@ type formType = {
|
||||||
id?: string;
|
id?: string;
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
<style scoped lang="less">
|
||||||
|
.example {
|
||||||
|
color: rgb(192, 192, 192);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -72,6 +72,7 @@ import PermissionButton from '@/components/PermissionButton/index.vue';
|
||||||
import {
|
import {
|
||||||
getRelationshipList_api,
|
getRelationshipList_api,
|
||||||
delRelation_api,
|
delRelation_api,
|
||||||
|
getObjectList_api
|
||||||
} from '@/api/system/relationship';
|
} from '@/api/system/relationship';
|
||||||
import EditDialog from './components/EditDialog.vue';
|
import EditDialog from './components/EditDialog.vue';
|
||||||
import { onlyMessage } from '@/utils/comm';
|
import { onlyMessage } from '@/utils/comm';
|
||||||
|
@ -80,7 +81,7 @@ const permission = 'system/Relationship';
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
title: '名称',
|
title: '正向关系名称',
|
||||||
dataIndex: 'name',
|
dataIndex: 'name',
|
||||||
key: 'name',
|
key: 'name',
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
|
@ -89,6 +90,16 @@ const columns = [
|
||||||
type: 'string',
|
type: 'string',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: '反向关系名称',
|
||||||
|
dataIndex: 'reverseName',
|
||||||
|
key: 'reverseName',
|
||||||
|
ellipsis: true,
|
||||||
|
fixed: 'left',
|
||||||
|
search: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: '关联方',
|
title: '关联方',
|
||||||
dataIndex: 'objectTypeName',
|
dataIndex: 'objectTypeName',
|
||||||
|
@ -97,17 +108,16 @@ const columns = [
|
||||||
fixed: 'left',
|
fixed: 'left',
|
||||||
search: {
|
search: {
|
||||||
type: 'select',
|
type: 'select',
|
||||||
options: [
|
options: async () =>{
|
||||||
{
|
const res:any = await getObjectList_api()
|
||||||
label: '用户',
|
return res.result?.map((i:any)=>{
|
||||||
value: '用户',
|
return {
|
||||||
},
|
label:i.name,
|
||||||
{
|
value:i.id
|
||||||
label: '设备',
|
}
|
||||||
value: '设备',
|
})
|
||||||
},
|
}
|
||||||
],
|
}
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '被关联方',
|
title: '被关联方',
|
||||||
|
@ -118,12 +128,15 @@ const columns = [
|
||||||
search: {
|
search: {
|
||||||
rename: 'targetType',
|
rename: 'targetType',
|
||||||
type: 'select',
|
type: 'select',
|
||||||
options: [
|
options: async () =>{
|
||||||
{
|
const res:any = await getObjectList_api()
|
||||||
label: '用户',
|
return res.result?.map((i:any)=>{
|
||||||
value: 'user',
|
return {
|
||||||
},
|
label:i.name,
|
||||||
],
|
value:i.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -174,6 +187,7 @@ const dialog = reactive({
|
||||||
selectRow: {} as any,
|
selectRow: {} as any,
|
||||||
visible: false,
|
visible: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="role-permiss-container">
|
<div class="role-permiss-container">
|
||||||
<section class="card">
|
<!-- <section class="card">
|
||||||
<h5>基本信息</h5>
|
<h5>基本信息</h5>
|
||||||
<j-form
|
<j-form
|
||||||
ref="formRef"
|
ref="formRef"
|
||||||
|
@ -30,7 +30,7 @@
|
||||||
/>
|
/>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
</j-form>
|
</j-form>
|
||||||
</section>
|
</section> -->
|
||||||
|
|
||||||
<section class="card">
|
<section class="card">
|
||||||
<h5>权限分配</h5>
|
<h5>权限分配</h5>
|
||||||
|
@ -38,7 +38,7 @@
|
||||||
|
|
||||||
<j-button
|
<j-button
|
||||||
type="primary"
|
type="primary"
|
||||||
:disabled="form.loading"
|
:confirm-loading="form.loading"
|
||||||
@click="form.clickSave"
|
@click="form.clickSave"
|
||||||
style="margin-top: 24px"
|
style="margin-top: 24px"
|
||||||
>保存</j-button
|
>保存</j-button
|
||||||
|
@ -70,33 +70,33 @@ const roleId = route.params.id as string;
|
||||||
const formRef = ref<FormInstance>();
|
const formRef = ref<FormInstance>();
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
loading: false,
|
loading: false,
|
||||||
data: {
|
// data: {
|
||||||
name: '',
|
// name: '',
|
||||||
description: '',
|
// description: '',
|
||||||
},
|
// },
|
||||||
menus: [], // USER_CENTER_MENU_DATA
|
menus: [], // USER_CENTER_MENU_DATA
|
||||||
getForm: () => {
|
// getForm: () => {
|
||||||
getRoleDetails_api(roleId).then((resp) => {
|
// getRoleDetails_api(roleId).then((resp) => {
|
||||||
if (resp.status) {
|
// if (resp.status) {
|
||||||
form.data = resp.result;
|
// form.data = resp.result;
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
},
|
// },
|
||||||
clickSave: () => {
|
clickSave: () => {
|
||||||
formRef.value?.validate().then(() => {
|
// formRef.value?.validate().then(() => {
|
||||||
const updateRole = editRole_api(roleId, form.data);
|
// const updateRole = editRole_api(roleId, form.data);
|
||||||
const updateTree = updatePrimissTree_api(roleId, {
|
const updateTree = updatePrimissTree_api(roleId, {
|
||||||
menus: form.menus,
|
menus: form.menus,
|
||||||
});
|
});
|
||||||
Promise.all([updateRole, updateTree]).then((resp) => {
|
Promise.all([ updateTree]).then((resp) => {
|
||||||
onlyMessage('操作成功');
|
onlyMessage('操作成功');
|
||||||
// jumpPage(`system/Role`);
|
// jumpPage(`system/Role`);
|
||||||
});
|
})
|
||||||
});
|
// });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
form.getForm();
|
// form.getForm();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
|
|
@ -0,0 +1,175 @@
|
||||||
|
<template>
|
||||||
|
<div class="left-contain">
|
||||||
|
<j-input placeholder="分组名称" v-model:value="searchValue" @pressEnter="search" @change="searchChange">
|
||||||
|
<template #suffix>
|
||||||
|
<AIcon type="SearchOutlined" @click="search" />
|
||||||
|
</template>
|
||||||
|
</j-input>
|
||||||
|
<div class="controls" v-if="admin">
|
||||||
|
<j-button type="primary" @click="addGroup" style="width: 100%">
|
||||||
|
新增分组
|
||||||
|
</j-button>
|
||||||
|
</div>
|
||||||
|
<div class="listBox">
|
||||||
|
<j-tree :tree-data="listData" v-if="listData.length" :fieldNames="{ title: 'name', key: 'id', children: 'children' }"
|
||||||
|
blockNode :selectedKeys="selectedKeys" :default-expanded-keys="['global_role']"
|
||||||
|
:showLine="{ showLeafIcon: false }">
|
||||||
|
<template #title="item">
|
||||||
|
<div v-if="selectId === item.data.id">
|
||||||
|
<j-input v-model:value="addName" @blur="() => saveGroup(item.data)" ref="inputRef"
|
||||||
|
:maxlength="64"></j-input>
|
||||||
|
</div>
|
||||||
|
<div class="treeItem" @click="() => selectGroup(item.data.id)" v-else>
|
||||||
|
<template v-if="!item?.children">
|
||||||
|
<div class="itemText">
|
||||||
|
<Ellipsis style="width: calc(100%-100px)">{{ item.name }}</Ellipsis>
|
||||||
|
</div>
|
||||||
|
<div @click="(e) => e.stopPropagation()" v-if="item.id !== 'default_group'">
|
||||||
|
<PermissionButton type="text" hasPermission="system/Role:groupDelete" :popConfirm="{
|
||||||
|
title: `确定要删除?`,
|
||||||
|
onConfirm: () => deleteGroup(item.id),
|
||||||
|
}" :disabled="item.id === 'default_group'">
|
||||||
|
删除
|
||||||
|
</PermissionButton>
|
||||||
|
<PermissionButton type="text" hasPermission="system/Role:groupUpdate"
|
||||||
|
@click="editGroup(item.data)" :disabled="item.id === 'default_group'">
|
||||||
|
编辑
|
||||||
|
</PermissionButton>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<Ellipsis style="width: calc(100%-100px)">{{ item.name }}</Ellipsis>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</j-tree>
|
||||||
|
<j-empty v-else style="margin-top: 100px;" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { onlyMessage } from '@/utils/comm';
|
||||||
|
import { queryRoleGroup, saveRoleGroup, deleteRoleGroup } from '@/api/system/role';
|
||||||
|
import { randomString } from '@/utils/utils';
|
||||||
|
import { useUserInfo } from '@/store/userInfo';
|
||||||
|
import { storeToRefs } from 'pinia';
|
||||||
|
const emit = defineEmits(['selectData'])
|
||||||
|
const userInfoStore = useUserInfo()
|
||||||
|
const { userInfos } = storeToRefs(userInfoStore)
|
||||||
|
const admin = computed(() => {
|
||||||
|
return userInfos.value?.username === 'admin';
|
||||||
|
})
|
||||||
|
const listData: any = ref([{
|
||||||
|
name: '全局角色',
|
||||||
|
id: 'global_role',
|
||||||
|
children: []
|
||||||
|
}])
|
||||||
|
const selectedKeys = ref<string[]>(['global_role'])
|
||||||
|
const searchValue = ref()
|
||||||
|
const inputRef = ref()
|
||||||
|
const addName = ref()
|
||||||
|
const selectId = ref()
|
||||||
|
const addStatus = ref(false) // 新增分组状态
|
||||||
|
const queryGroup = async (select?: Boolean, searchName?: string) => {
|
||||||
|
const params = searchName ? { sorts: [{ name: 'createTime', order: 'desc' }], terms: [{ terms: [{ value: '%' + searchName + '%', termType: 'like', column: 'name' }] }] } : { sorts: [{ name: 'createTime', order: 'desc' }] }
|
||||||
|
const req: any = await queryRoleGroup(params)
|
||||||
|
if (req.status === 200) {
|
||||||
|
listData.value[0].children = req.result
|
||||||
|
// if(req.result.length && select){
|
||||||
|
// selectGroup(req.result[0].id)
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const addGroup = () => {
|
||||||
|
addName.value = ''
|
||||||
|
const newId = randomString()
|
||||||
|
listData.value[0].children.unshift({
|
||||||
|
name: '',
|
||||||
|
id: newId
|
||||||
|
})
|
||||||
|
selectId.value = newId
|
||||||
|
nextTick(() => {
|
||||||
|
inputRef.value.focus()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const saveGroup = async (data: any) => {
|
||||||
|
if (addName.value === '') {
|
||||||
|
listData.value[0].children.shift()
|
||||||
|
} else {
|
||||||
|
const saveData = {
|
||||||
|
name: addName.value,
|
||||||
|
id: data.id
|
||||||
|
}
|
||||||
|
const res = await saveRoleGroup(saveData)
|
||||||
|
if (res.status === 200) {
|
||||||
|
onlyMessage('操作成功!')
|
||||||
|
queryGroup()
|
||||||
|
} else {
|
||||||
|
onlyMessage('操作失败!')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setTimeout(()=>{
|
||||||
|
selectId.value = ''
|
||||||
|
},300)
|
||||||
|
}
|
||||||
|
const search = () => {
|
||||||
|
queryGroup(true, searchValue.value)
|
||||||
|
}
|
||||||
|
const searchChange = () => {
|
||||||
|
if (searchValue.value === '') {
|
||||||
|
queryGroup()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const selectGroup = (id: string) => {
|
||||||
|
selectedKeys.value = [id]
|
||||||
|
id === 'global_role' ? emit('selectData', '') : emit('selectData', selectedKeys.value)
|
||||||
|
|
||||||
|
}
|
||||||
|
const deleteGroup = async (id: string) => {
|
||||||
|
const res: any = await deleteRoleGroup(id)
|
||||||
|
if (res.status === 200) {
|
||||||
|
onlyMessage('操作成功!')
|
||||||
|
queryGroup(true)
|
||||||
|
} else {
|
||||||
|
onlyMessage('操作失败!')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const editGroup = (data: any) => {
|
||||||
|
if(!selectId.value){
|
||||||
|
selectId.value = data.id
|
||||||
|
addName.value = data.name
|
||||||
|
listData.value[0].children.forEach((item: any) => {
|
||||||
|
if (item.id === data.id) {
|
||||||
|
item.edit = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
nextTick(() => {
|
||||||
|
inputRef.value.focus()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
onMounted(() => {
|
||||||
|
queryGroup(true)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.controls {
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.treeItem {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.itemText {
|
||||||
|
line-height: 32px;
|
||||||
|
max-width: 40%
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.listBox {
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,139 @@
|
||||||
|
<template>
|
||||||
|
<j-modal
|
||||||
|
visible
|
||||||
|
:title="modalType ==='add' ? '新增' : '编辑'"
|
||||||
|
width="670px"
|
||||||
|
@cancel="emits('update:visible', false)"
|
||||||
|
@ok="confirm"
|
||||||
|
:confirm-loading="loading"
|
||||||
|
>
|
||||||
|
<j-form ref="formRef" :model="form" layout="vertical">
|
||||||
|
<j-form-item
|
||||||
|
name="name"
|
||||||
|
label="名称"
|
||||||
|
:rules="[
|
||||||
|
{ required: true, message: '请输入名称' },
|
||||||
|
{ max: 64, message: '最多可输入64个字符' },
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<j-input
|
||||||
|
v-model:value="form.name"
|
||||||
|
placeholder="请输入角色名称"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item
|
||||||
|
name="groupId"
|
||||||
|
label="分组"
|
||||||
|
:rules="[
|
||||||
|
{ required: true, message: '请选择分组' },
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<j-select
|
||||||
|
v-model:value="form.groupId"
|
||||||
|
placeholder="请选择分组"
|
||||||
|
:options="groupOptions"
|
||||||
|
/>
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item name="name" label="说明">
|
||||||
|
<j-textarea
|
||||||
|
v-model:value="form.description"
|
||||||
|
placeholder="请输入说明"
|
||||||
|
allow-clear
|
||||||
|
:maxlength="200"
|
||||||
|
show-count
|
||||||
|
/>
|
||||||
|
</j-form-item>
|
||||||
|
</j-form>
|
||||||
|
</j-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { FormInstance } from 'ant-design-vue';
|
||||||
|
import { saveRole_api , queryRoleGroup , updateRole_api} from '@/api/system/role';
|
||||||
|
import { useMenuStore } from '@/store/menu';
|
||||||
|
import { onlyMessage } from '@/utils/comm';
|
||||||
|
const route = useRoute();
|
||||||
|
const { jumpPage } = useMenuStore();
|
||||||
|
|
||||||
|
const emits = defineEmits(['update:visible']);
|
||||||
|
const props = defineProps({
|
||||||
|
visible: {
|
||||||
|
type:Boolean,
|
||||||
|
default:false
|
||||||
|
},
|
||||||
|
groupId:{
|
||||||
|
type:String,
|
||||||
|
default:""
|
||||||
|
},
|
||||||
|
modalType:{
|
||||||
|
type:String,
|
||||||
|
default:'add'
|
||||||
|
},
|
||||||
|
current:{
|
||||||
|
type:Object,
|
||||||
|
default:{}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// 弹窗相关
|
||||||
|
const loading = ref(false);
|
||||||
|
const form = ref<any>({
|
||||||
|
name:'',
|
||||||
|
groupId:'',
|
||||||
|
description:''
|
||||||
|
});
|
||||||
|
const formRef = ref<FormInstance>();
|
||||||
|
const groupOptions = ref<any>([])
|
||||||
|
const confirm = async() => {
|
||||||
|
loading.value = true;
|
||||||
|
formRef.value
|
||||||
|
?.validate()
|
||||||
|
.then(() => {
|
||||||
|
if(props.modalType === 'add'){
|
||||||
|
saveRole_api(form.value).then((resp:any)=>{
|
||||||
|
if (resp.status === 200) {
|
||||||
|
onlyMessage('操作成功');
|
||||||
|
emits('update:visible', false);
|
||||||
|
if (route.query.save) {
|
||||||
|
// @ts-ignore
|
||||||
|
if((window as any).onTabSaveSuccess){
|
||||||
|
(window as any).onTabSaveSuccess(resp.result.id);
|
||||||
|
setTimeout(() => window.close(), 300);
|
||||||
|
}
|
||||||
|
} else jumpPage(`system/Role/Detail`, { id: resp.result.id });
|
||||||
|
}
|
||||||
|
}).catch(() => (loading.value = false));
|
||||||
|
}else{
|
||||||
|
updateRole_api(form.value).then((resp:any)=>{
|
||||||
|
if (resp.status === 200) {
|
||||||
|
onlyMessage('操作成功');
|
||||||
|
emits('update:visible', false);
|
||||||
|
}
|
||||||
|
}).catch(() => (loading.value = false));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => (loading.value = false));
|
||||||
|
};
|
||||||
|
// 表单相关
|
||||||
|
const getGroupOptions = ()=>{
|
||||||
|
queryRoleGroup({sorts: [{ name: 'createTime', order: 'desc' }]}).then((res:any)=>{
|
||||||
|
if(res.status ===200){
|
||||||
|
groupOptions.value = res.result.map((item:any)=>{
|
||||||
|
return {
|
||||||
|
label:item.name,
|
||||||
|
value:item.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
onMounted(()=>{
|
||||||
|
getGroupOptions()
|
||||||
|
form.value.groupId = props.groupId
|
||||||
|
if(props.modalType === 'edit'){
|
||||||
|
form.value = props.current
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
|
@ -0,0 +1,193 @@
|
||||||
|
<template>
|
||||||
|
<page-container>
|
||||||
|
<div class="role-container">
|
||||||
|
<pro-search :columns="columns" target="system-role" @search="handelSearch" />
|
||||||
|
<FullPage>
|
||||||
|
<j-pro-table ref="tableRef" :columns="columns" :request="getRoleList_api" model="TABLE"
|
||||||
|
:params="queryParams" :defaultParams="{
|
||||||
|
sorts: [
|
||||||
|
{ name: 'createTime', order: 'desc' },
|
||||||
|
{ name: 'id', order: 'desc' },
|
||||||
|
]
|
||||||
|
}">
|
||||||
|
<template #headerTitle>
|
||||||
|
<PermissionButton type="primary" :hasPermission="`${permission}:add`" @click="addRole">
|
||||||
|
<AIcon type="PlusOutlined" />新增
|
||||||
|
</PermissionButton>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #action="slotProps">
|
||||||
|
<j-space>
|
||||||
|
<template v-for="i in getActions(slotProps, 'table')" :key="i.key">
|
||||||
|
<PermissionButton :disabled="i.disabled" :popConfirm="i.popConfirm" :tooltip="{
|
||||||
|
...i.tooltip,
|
||||||
|
}" @click="i.onClick" type="link" style="padding: 0 5px" :danger="i.key === 'delete'"
|
||||||
|
:hasPermission="'system/Role:' + i.key
|
||||||
|
">
|
||||||
|
<template #icon>
|
||||||
|
<AIcon :type="i.icon" />
|
||||||
|
</template>
|
||||||
|
</PermissionButton>
|
||||||
|
</template>
|
||||||
|
</j-space>
|
||||||
|
</template>
|
||||||
|
</j-pro-table>
|
||||||
|
</FullPage>
|
||||||
|
|
||||||
|
<AddDialog v-if="dialogVisible" v-model:visible="dialogVisible" :groupId="groupId" :modalType="modalType"
|
||||||
|
:current="current" />
|
||||||
|
</div>
|
||||||
|
</page-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts" name="Role">
|
||||||
|
import PermissionButton from '@/components/PermissionButton/index.vue';
|
||||||
|
import AddDialog from './components/AddDialog.vue';
|
||||||
|
import { getRoleList_api, delRole_api } from '@/api/system/role';
|
||||||
|
import type { ActionsType } from './typings';
|
||||||
|
import { useMenuStore } from '@/store/menu';
|
||||||
|
import { onlyMessage } from '@/utils/comm';
|
||||||
|
const props = defineProps({
|
||||||
|
groupId: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const permission = 'system/Role';
|
||||||
|
const { jumpPage } = useMenuStore();
|
||||||
|
const modalType = ref('add')
|
||||||
|
const current = ref()
|
||||||
|
const isSave = !!useRoute().query.save;
|
||||||
|
const queryParams = ref<any>({terms:[]});
|
||||||
|
// 表格
|
||||||
|
const tableRef = ref<Record<string, any>>();
|
||||||
|
const dialogVisible = ref(isSave);
|
||||||
|
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: '说明',
|
||||||
|
key: 'description',
|
||||||
|
ellipsis: true,
|
||||||
|
dataIndex: 'description',
|
||||||
|
search: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
dataIndex: 'action',
|
||||||
|
key: 'action',
|
||||||
|
width: 120,
|
||||||
|
fixed: 'right',
|
||||||
|
scopedSlots: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const getActions = (
|
||||||
|
data: Partial<Record<string, any>>,
|
||||||
|
type: 'card' | 'table',
|
||||||
|
): ActionsType[] => {
|
||||||
|
if (!data) return [];
|
||||||
|
const actions = [
|
||||||
|
{
|
||||||
|
key: 'update',
|
||||||
|
text: '编辑',
|
||||||
|
tooltip: {
|
||||||
|
title: '编辑',
|
||||||
|
},
|
||||||
|
icon: 'EditOutlined',
|
||||||
|
onClick: () => {
|
||||||
|
dialogVisible.value = true;
|
||||||
|
modalType.value = 'edit';
|
||||||
|
current.value = data
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'update',
|
||||||
|
text: '权限配置',
|
||||||
|
tooltip: {
|
||||||
|
title: '权限配置'
|
||||||
|
},
|
||||||
|
onClick: () => {
|
||||||
|
jumpPage(`system/Role/Detail`, {
|
||||||
|
id: data.id,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
icon: 'FormOutlined'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'delete',
|
||||||
|
text: '删除',
|
||||||
|
tooltip: {
|
||||||
|
title: '删除',
|
||||||
|
},
|
||||||
|
popConfirm: {
|
||||||
|
title: '确认删除?',
|
||||||
|
onConfirm: async () => {
|
||||||
|
const res = await delRole_api(data.id)
|
||||||
|
if (res.status === 200) {
|
||||||
|
onlyMessage('操作成功!')
|
||||||
|
tableRef.value?.reload()
|
||||||
|
} else {
|
||||||
|
onlyMessage('操作失败!', 'error')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
icon: 'DeleteOutlined',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
if (type === 'card')
|
||||||
|
return actions.filter((i: ActionsType) => i.key !== 'view');
|
||||||
|
return actions;
|
||||||
|
};
|
||||||
|
|
||||||
|
const addRole = () => {
|
||||||
|
dialogVisible.value = true
|
||||||
|
modalType.value = 'add'
|
||||||
|
}
|
||||||
|
const handelSearch = (search: any) => {
|
||||||
|
queryParams.value.terms =props.groupId ? [{
|
||||||
|
value: props.groupId,
|
||||||
|
termType: 'eq',
|
||||||
|
column: 'groupId'
|
||||||
|
}, ...search.terms] : [...search.terms]
|
||||||
|
}
|
||||||
|
watch(() => props.groupId, (value) => {
|
||||||
|
queryParams.value = value ? {
|
||||||
|
terms: [{
|
||||||
|
value: props.groupId,
|
||||||
|
termType: 'eq',
|
||||||
|
column: 'groupId'
|
||||||
|
}]
|
||||||
|
} : {
|
||||||
|
terms: []
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.role-container {
|
||||||
|
:deep(.ant-table-cell) {
|
||||||
|
.ant-btn-link {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,79 +0,0 @@
|
||||||
<template>
|
|
||||||
<j-modal
|
|
||||||
visible
|
|
||||||
title="新增"
|
|
||||||
width="670px"
|
|
||||||
@cancel="emits('update:visible', false)"
|
|
||||||
@ok="confirm"
|
|
||||||
:confirm-loading="loading"
|
|
||||||
>
|
|
||||||
<j-form ref="formRef" :model="form" layout="vertical">
|
|
||||||
<j-form-item
|
|
||||||
name="name"
|
|
||||||
label="名称"
|
|
||||||
:rules="[
|
|
||||||
{ required: true, message: '请输入名称' },
|
|
||||||
{ max: 64, message: '最多可输入64个字符' },
|
|
||||||
]"
|
|
||||||
>
|
|
||||||
<j-input
|
|
||||||
v-model:value="form.name"
|
|
||||||
placeholder="请输入角色名称"
|
|
||||||
allow-clear
|
|
||||||
/>
|
|
||||||
</j-form-item>
|
|
||||||
<j-form-item name="name" label="说明">
|
|
||||||
<j-textarea
|
|
||||||
v-model:value="form.description"
|
|
||||||
placeholder="请输入说明"
|
|
||||||
allow-clear
|
|
||||||
:maxlength="200"
|
|
||||||
show-count
|
|
||||||
/>
|
|
||||||
</j-form-item>
|
|
||||||
</j-form>
|
|
||||||
</j-modal>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { FormInstance } from 'ant-design-vue';
|
|
||||||
import { saveRole_api } from '@/api/system/role';
|
|
||||||
import { useMenuStore } from '@/store/menu';
|
|
||||||
import { onlyMessage } from '@/utils/comm';
|
|
||||||
const route = useRoute();
|
|
||||||
const { jumpPage } = useMenuStore();
|
|
||||||
|
|
||||||
const emits = defineEmits(['update:visible']);
|
|
||||||
const props = defineProps<{
|
|
||||||
visible: boolean;
|
|
||||||
}>();
|
|
||||||
// 弹窗相关
|
|
||||||
const loading = ref(false);
|
|
||||||
const form = ref<any>({});
|
|
||||||
const formRef = ref<FormInstance>();
|
|
||||||
|
|
||||||
const confirm = () => {
|
|
||||||
loading.value = true;
|
|
||||||
formRef.value
|
|
||||||
?.validate()
|
|
||||||
.then(() => saveRole_api(form.value))
|
|
||||||
.then((resp) => {
|
|
||||||
if (resp.status === 200) {
|
|
||||||
onlyMessage('操作成功');
|
|
||||||
emits('update:visible', false);
|
|
||||||
|
|
||||||
if (route.query.save) {
|
|
||||||
// @ts-ignore
|
|
||||||
if((window as any).onTabSaveSuccess){
|
|
||||||
(window as any).onTabSaveSuccess(resp.result.id);
|
|
||||||
setTimeout(() => window.close(), 300);
|
|
||||||
}
|
|
||||||
} else jumpPage(`system/Role/Detail`, { id: resp.result.id });
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.finally(() => (loading.value = false));
|
|
||||||
};
|
|
||||||
// 表单相关
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped></style>
|
|
|
@ -1,142 +1,40 @@
|
||||||
<template>
|
<template>
|
||||||
<page-container>
|
<page-container>
|
||||||
<div class="role-container">
|
<FullPage>
|
||||||
<pro-search
|
<div class="dictionary_contain">
|
||||||
:columns="columns"
|
<div class="dictionary_left">
|
||||||
target="system-role"
|
<Left @select-data="selectData"/>
|
||||||
@search="(params:any)=>queryParams = {...params}"
|
</div>
|
||||||
/>
|
<div class="dictionary_right">
|
||||||
<FullPage>
|
<Right :groupId="groupId"/>
|
||||||
<j-pro-table
|
</div>
|
||||||
ref="tableRef"
|
</div>
|
||||||
:columns="columns"
|
</FullPage>
|
||||||
:request="getRoleList_api"
|
|
||||||
model="TABLE"
|
|
||||||
:params="queryParams"
|
|
||||||
:defaultParams="{
|
|
||||||
sorts: [
|
|
||||||
{ name: 'createTime', order: 'desc' },
|
|
||||||
{ name: 'id', order: 'desc' },
|
|
||||||
],
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<template #headerTitle>
|
|
||||||
<PermissionButton
|
|
||||||
type="primary"
|
|
||||||
:hasPermission="`${permission}:add`"
|
|
||||||
@click="dialogVisible = true"
|
|
||||||
>
|
|
||||||
<AIcon type="PlusOutlined" />新增
|
|
||||||
</PermissionButton>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #action="slotProps">
|
|
||||||
<j-space :size="16">
|
|
||||||
<PermissionButton
|
|
||||||
:hasPermission="`${permission}:update`"
|
|
||||||
type="link"
|
|
||||||
:tooltip="{
|
|
||||||
title: '编辑',
|
|
||||||
}"
|
|
||||||
@click="
|
|
||||||
jumpPage(`system/Role/Detail`, {
|
|
||||||
id: slotProps.id,
|
|
||||||
})
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<AIcon type="EditOutlined" />
|
|
||||||
</PermissionButton>
|
|
||||||
<PermissionButton
|
|
||||||
type="link"
|
|
||||||
:hasPermission="`${permission}:delete`"
|
|
||||||
:tooltip="{ title: '删除' }"
|
|
||||||
:popConfirm="{
|
|
||||||
title: `确定要删除吗`,
|
|
||||||
onConfirm: () => clickDel(slotProps),
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<AIcon type="DeleteOutlined" />
|
|
||||||
</PermissionButton>
|
|
||||||
</j-space>
|
|
||||||
</template>
|
|
||||||
</j-pro-table>
|
|
||||||
</FullPage>
|
|
||||||
|
|
||||||
<AddDialog v-if="dialogVisible" v-model:visible="dialogVisible" />
|
|
||||||
</div>
|
|
||||||
</page-container>
|
</page-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="Role">
|
<script lang="ts" setup>
|
||||||
import PermissionButton from '@/components/PermissionButton/index.vue';
|
import Left from './RoleLeft/index.vue'
|
||||||
import AddDialog from './components/AddDialog.vue';
|
import Right from './RoleRight/index.vue'
|
||||||
import { getRoleList_api, delRole_api } from '@/api/system/role';
|
const groupId = ref()
|
||||||
import { useMenuStore } from '@/store/menu';
|
const selectData = (data:any)=>{
|
||||||
import { onlyMessage } from '@/utils/comm';
|
groupId.value = data[0]
|
||||||
|
}
|
||||||
const permission = 'system/Role';
|
</script>
|
||||||
const { jumpPage } = useMenuStore();
|
<style lang="less" scoped>
|
||||||
|
.dictionary_contain{
|
||||||
const isSave = !!useRoute().query.save;
|
display: flex;
|
||||||
|
background-color: #fff;
|
||||||
const columns = [
|
padding: 24px;
|
||||||
{
|
height: 100%;
|
||||||
title: '标识',
|
}
|
||||||
dataIndex: 'id',
|
.dictionary_left{
|
||||||
key: 'id',
|
border-right: 1px solid #f0f0f0;
|
||||||
ellipsis: true,
|
padding-right: 24px;
|
||||||
fixed: 'left',
|
flex:1;
|
||||||
search: {
|
height:100%
|
||||||
type: 'string',
|
}
|
||||||
},
|
.dictionary_right{
|
||||||
},
|
flex:4
|
||||||
{
|
}
|
||||||
title: '名称',
|
</style>
|
||||||
dataIndex: 'name',
|
|
||||||
key: 'name',
|
|
||||||
ellipsis: true,
|
|
||||||
search: {
|
|
||||||
type: 'string',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '说明',
|
|
||||||
key: 'description',
|
|
||||||
ellipsis: true,
|
|
||||||
dataIndex: 'description',
|
|
||||||
search: {
|
|
||||||
type: 'string',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '操作',
|
|
||||||
dataIndex: 'action',
|
|
||||||
key: 'action',
|
|
||||||
width: 120,
|
|
||||||
fixed: 'right',
|
|
||||||
scopedSlots: true,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
const queryParams = ref({});
|
|
||||||
// 表格
|
|
||||||
const tableRef = ref<Record<string, any>>();
|
|
||||||
const clickDel = (row: any) => {
|
|
||||||
delRole_api(row.id).then((resp: any) => {
|
|
||||||
if (resp.status === 200) {
|
|
||||||
tableRef.value?.reload();
|
|
||||||
onlyMessage('操作成功!');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
const dialogVisible = ref(isSave);
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="less" scoped>
|
|
||||||
.role-container {
|
|
||||||
:deep(.ant-table-cell) {
|
|
||||||
.ant-btn-link {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -12,8 +12,9 @@
|
||||||
okText="确定"
|
okText="确定"
|
||||||
>
|
>
|
||||||
<j-form ref="formRef" :model="form.data" layout="vertical">
|
<j-form ref="formRef" :model="form.data" layout="vertical">
|
||||||
|
<div class="formName" v-if="form.IsShow('add', 'edit')">基础信息</div>
|
||||||
<j-row :gutter="24" v-if="form.IsShow('add', 'edit')">
|
<j-row :gutter="24" v-if="form.IsShow('add', 'edit')">
|
||||||
<j-col :span="12">
|
<j-col :span="24">
|
||||||
<j-form-item
|
<j-form-item
|
||||||
name="name"
|
name="name"
|
||||||
label="姓名"
|
label="姓名"
|
||||||
|
@ -31,7 +32,113 @@
|
||||||
/>
|
/>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
</j-col>
|
</j-col>
|
||||||
|
</j-row>
|
||||||
|
<j-row :gutter="24" v-if="form.IsShow('add', 'edit')">
|
||||||
<j-col :span="12">
|
<j-col :span="12">
|
||||||
|
<j-form-item
|
||||||
|
name="telephone"
|
||||||
|
label="手机号"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
pattern: /^1[3456789]\d{9}$/,
|
||||||
|
message: '请输入正确的手机号',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<j-input
|
||||||
|
v-model:value="form.data.telephone"
|
||||||
|
placeholder="请输入手机号"
|
||||||
|
:maxlength="64"
|
||||||
|
/>
|
||||||
|
</j-form-item>
|
||||||
|
</j-col>
|
||||||
|
<j-col :span="12">
|
||||||
|
<j-form-item
|
||||||
|
name="email"
|
||||||
|
label="邮箱"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
pattern:
|
||||||
|
/^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/,
|
||||||
|
message: '请输入正确的邮箱',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<j-input
|
||||||
|
v-model:value="form.data.email"
|
||||||
|
placeholder="请输入邮箱"
|
||||||
|
:maxlength="64"
|
||||||
|
/>
|
||||||
|
</j-form-item>
|
||||||
|
</j-col>
|
||||||
|
</j-row>
|
||||||
|
<j-row :gutter="24" v-if="form.IsShow('add', 'edit')">
|
||||||
|
<j-col :span="12">
|
||||||
|
<j-form-item name="roleIdList" label="角色" class="flex"
|
||||||
|
:rules="[
|
||||||
|
{ required: form.data.username !== 'admin', message: '请选择角色' },
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<j-tree-select
|
||||||
|
v-model:value="form.data.roleIdList"
|
||||||
|
multiple
|
||||||
|
show-search
|
||||||
|
style="width: calc(100% - 40px)"
|
||||||
|
placeholder="请选择角色"
|
||||||
|
:tree-data="form.roleOptions"
|
||||||
|
:fieldNames="{ label: 'name', value: 'id', children:'children' }"
|
||||||
|
:disabled="form.data.username === 'admin'"
|
||||||
|
:filterTreeNode="
|
||||||
|
(v: string, node: any) => filterSelectNode(v, node, 'name')
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<template #title="{ name }">
|
||||||
|
<div style="width: calc(100% - 10px) ">
|
||||||
|
<Ellipsis>{{ name }}</Ellipsis>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</j-tree-select>
|
||||||
|
<PermissionButton
|
||||||
|
:hasPermission="`${rolePermission}:add`"
|
||||||
|
@click="form.clickAddItem('roleIdList', 'Role')"
|
||||||
|
v-if="form.data.username !== 'admin'"
|
||||||
|
>
|
||||||
|
<AIcon type="PlusOutlined" />
|
||||||
|
</PermissionButton>
|
||||||
|
</j-form-item>
|
||||||
|
</j-col>
|
||||||
|
<j-col :span="12">
|
||||||
|
<j-form-item name="orgIdList" label="组织" class="flex">
|
||||||
|
<j-tree-select
|
||||||
|
v-model:value="form.data.orgIdList"
|
||||||
|
show-search
|
||||||
|
style="width: calc(100% - 40px)"
|
||||||
|
placeholder="请选择组织"
|
||||||
|
:tree-data="form.departmentOptions"
|
||||||
|
:fieldNames="{ label: 'name', value: 'id' }"
|
||||||
|
multiple
|
||||||
|
:filterTreeNode="
|
||||||
|
(v: string, node: any) => filterSelectNode(v, node, 'name')
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<template #title="{ name }">
|
||||||
|
{{ name }}
|
||||||
|
</template>
|
||||||
|
</j-tree-select>
|
||||||
|
<PermissionButton
|
||||||
|
:hasPermission="`${deptPermission}:add`"
|
||||||
|
@click="
|
||||||
|
form.clickAddItem('orgIdList', 'Department')
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<AIcon type="PlusOutlined" />
|
||||||
|
</PermissionButton>
|
||||||
|
</j-form-item>
|
||||||
|
</j-col>
|
||||||
|
</j-row>
|
||||||
|
<div class="formName" v-if="form.IsShow('add', 'edit')">账号信息</div>
|
||||||
|
<j-row :gutter="24" v-if="form.IsShow('add', 'edit')">
|
||||||
|
<j-col :span="24">
|
||||||
<j-form-item
|
<j-form-item
|
||||||
name="username"
|
name="username"
|
||||||
label="用户名"
|
label="用户名"
|
||||||
|
@ -92,99 +199,6 @@
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
</j-col>
|
</j-col>
|
||||||
</j-row>
|
</j-row>
|
||||||
<j-row :gutter="24" v-if="form.IsShow('add', 'edit')">
|
|
||||||
<j-col :span="12">
|
|
||||||
<j-form-item name="roleIdList" label="角色" class="flex"
|
|
||||||
:rules="[
|
|
||||||
{ required: form.data.username !== 'admin', message: '请选择角色' },
|
|
||||||
]"
|
|
||||||
>
|
|
||||||
<j-select
|
|
||||||
v-model:value="form.data.roleIdList"
|
|
||||||
mode="multiple"
|
|
||||||
style="width: calc(100% - 40px)"
|
|
||||||
placeholder="请选择角色"
|
|
||||||
:options="_roleOptions"
|
|
||||||
:disabled="form.data.username === 'admin'"
|
|
||||||
></j-select>
|
|
||||||
|
|
||||||
<PermissionButton
|
|
||||||
:hasPermission="`${rolePermission}:add`"
|
|
||||||
@click="form.clickAddItem('roleIdList', 'Role')"
|
|
||||||
v-if="form.data.username !== 'admin'"
|
|
||||||
>
|
|
||||||
<AIcon type="PlusOutlined" />
|
|
||||||
</PermissionButton>
|
|
||||||
</j-form-item>
|
|
||||||
</j-col>
|
|
||||||
<j-col :span="12">
|
|
||||||
<j-form-item name="orgIdList" label="组织" class="flex">
|
|
||||||
<j-tree-select
|
|
||||||
v-model:value="form.data.orgIdList"
|
|
||||||
show-search
|
|
||||||
style="width: calc(100% - 40px)"
|
|
||||||
placeholder="请选择组织"
|
|
||||||
:tree-data="_departmentOptions"
|
|
||||||
:fieldNames="{ label: 'name', value: 'id' }"
|
|
||||||
multiple
|
|
||||||
:filterTreeNode="
|
|
||||||
(v: string, node: any) => filterSelectNode(v, node, 'name')
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<template #title="{ name }">
|
|
||||||
{{ name }}
|
|
||||||
</template>
|
|
||||||
</j-tree-select>
|
|
||||||
<PermissionButton
|
|
||||||
:hasPermission="`${deptPermission}:add`"
|
|
||||||
@click="
|
|
||||||
form.clickAddItem('orgIdList', 'Department')
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<AIcon type="PlusOutlined" />
|
|
||||||
</PermissionButton>
|
|
||||||
</j-form-item>
|
|
||||||
</j-col>
|
|
||||||
</j-row>
|
|
||||||
<j-row :gutter="24" v-if="form.IsShow('add', 'edit')">
|
|
||||||
<j-col :span="12">
|
|
||||||
<j-form-item
|
|
||||||
name="telephone"
|
|
||||||
label="手机号"
|
|
||||||
:rules="[
|
|
||||||
{
|
|
||||||
pattern: /^1[3456789]\d{9}$/,
|
|
||||||
message: '请输入正确的手机号',
|
|
||||||
},
|
|
||||||
]"
|
|
||||||
>
|
|
||||||
<j-input
|
|
||||||
v-model:value="form.data.telephone"
|
|
||||||
placeholder="请输入手机号"
|
|
||||||
:maxlength="64"
|
|
||||||
/>
|
|
||||||
</j-form-item>
|
|
||||||
</j-col>
|
|
||||||
<j-col :span="12">
|
|
||||||
<j-form-item
|
|
||||||
name="email"
|
|
||||||
label="邮箱"
|
|
||||||
:rules="[
|
|
||||||
{
|
|
||||||
pattern:
|
|
||||||
/^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/,
|
|
||||||
message: '请输入正确的邮箱',
|
|
||||||
},
|
|
||||||
]"
|
|
||||||
>
|
|
||||||
<j-input
|
|
||||||
v-model:value="form.data.email"
|
|
||||||
placeholder="请输入邮箱"
|
|
||||||
:maxlength="64"
|
|
||||||
/>
|
|
||||||
</j-form-item>
|
|
||||||
</j-col>
|
|
||||||
</j-row>
|
|
||||||
</j-form>
|
</j-form>
|
||||||
</j-modal>
|
</j-modal>
|
||||||
</template>
|
</template>
|
||||||
|
@ -200,6 +214,7 @@ import {
|
||||||
updateUser_api,
|
updateUser_api,
|
||||||
updatePassword_api,
|
updatePassword_api,
|
||||||
getUser_api,
|
getUser_api,
|
||||||
|
getRoleList
|
||||||
} from '@/api/system/user';
|
} from '@/api/system/user';
|
||||||
import { Rule } from 'ant-design-vue/es/form';
|
import { Rule } from 'ant-design-vue/es/form';
|
||||||
import { DefaultOptionType } from 'ant-design-vue/es/vc-tree-select/TreeSelect';
|
import { DefaultOptionType } from 'ant-design-vue/es/vc-tree-select/TreeSelect';
|
||||||
|
@ -207,10 +222,7 @@ import { AxiosResponse } from 'axios';
|
||||||
import { passwordRegEx } from '@/utils/validate';
|
import { passwordRegEx } from '@/utils/validate';
|
||||||
import { filterSelectNode, onlyMessage } from '@/utils/comm';
|
import { filterSelectNode, onlyMessage } from '@/utils/comm';
|
||||||
import { uniqBy } from 'lodash-es';
|
import { uniqBy } from 'lodash-es';
|
||||||
|
import { storeToRefs } from 'pinia';
|
||||||
const admin = computed(() => {
|
|
||||||
return userInfos.value?.username === 'admin';
|
|
||||||
})
|
|
||||||
|
|
||||||
const deptPermission = 'system/Department';
|
const deptPermission = 'system/Department';
|
||||||
const rolePermission = 'system/Role';
|
const rolePermission = 'system/Role';
|
||||||
|
@ -282,10 +294,9 @@ const form = reactive({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
roleOptions: [] as optionType[],
|
roleOptions: [],
|
||||||
departmentOptions: [] as DefaultOptionType[],
|
departmentOptions: [] as DefaultOptionType[],
|
||||||
|
|
||||||
_roleOptions: [] as optionType[],
|
|
||||||
_departmentOptions: [] as DefaultOptionType[],
|
_departmentOptions: [] as DefaultOptionType[],
|
||||||
|
|
||||||
init: () => {
|
init: () => {
|
||||||
|
@ -309,8 +320,8 @@ const form = reactive({
|
||||||
(item: dictType) => item.id,
|
(item: dictType) => item.id,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
form._roleOptions = resp.result?.roleList?.map((i: any) => {
|
form.data.roleIdList = resp.result?.roleList?.map((i: any) => {
|
||||||
return {label: i.name, value: i.id}
|
return i.id
|
||||||
});
|
});
|
||||||
form._departmentOptions = resp.result?.orgList
|
form._departmentOptions = resp.result?.orgList
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
|
@ -348,16 +359,22 @@ const form = reactive({
|
||||||
return api(params);
|
return api(params);
|
||||||
},
|
},
|
||||||
getRoleList: () => {
|
getRoleList: () => {
|
||||||
getRoleList_api().then((resp: any) => {
|
getRoleList({ sorts: [{ name: 'createTime', order: 'desc' }] }).then((resp: any) => {
|
||||||
form.roleOptions = resp.result.map((item: dictType) => ({
|
if(resp.status === 200){
|
||||||
label: item.name,
|
form.roleOptions = dealRoleList(resp.result)
|
||||||
value: item.id,
|
}
|
||||||
}));
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getDepartmentList: () => {
|
getDepartmentList: () => {
|
||||||
getDepartmentList_api().then((resp: any) => {
|
getDepartmentList_api({
|
||||||
form.departmentOptions = resp.result;
|
paging: false,
|
||||||
|
sorts: [{ name: 'sortIndex', order: 'asc' }],
|
||||||
|
}).then((resp: any) => {
|
||||||
|
form.departmentOptions = resp.result.sort((a: any, b: any) =>
|
||||||
|
a.sortIndex === b.sortIndex
|
||||||
|
? b.createTime - a.createTime
|
||||||
|
: a.sortIndex - b.sortIndex,
|
||||||
|
); // 报存源数据;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
IsShow: (...typeList: modalType[]) => typeList.includes(props.type),
|
IsShow: (...typeList: modalType[]) => typeList.includes(props.type),
|
||||||
|
@ -371,13 +388,25 @@ const form = reactive({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const _roleOptions = computed(() => {
|
const dealRoleList = (data:any) =>{
|
||||||
return uniqBy([...form.roleOptions, ...form._roleOptions], 'value')
|
return data.map((item:any)=>{
|
||||||
})
|
return {
|
||||||
|
name: item.groupName,
|
||||||
const _departmentOptions = computed(() => {
|
id: item.groupId,
|
||||||
return uniqBy([...form.departmentOptions, ...form._departmentOptions], 'id')
|
disabled: true,
|
||||||
})
|
children: item?.roles ? item.roles.map((i:any)=>{
|
||||||
|
return {
|
||||||
|
name:i.name,
|
||||||
|
id:i.id
|
||||||
|
}
|
||||||
|
}) : []
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 组织已删除在仍显示在列表中
|
||||||
|
// const _departmentOptions = computed(() => {
|
||||||
|
// return uniqBy([...form.departmentOptions, ...form._departmentOptions], 'id')
|
||||||
|
// })
|
||||||
|
|
||||||
form.init();
|
form.init();
|
||||||
|
|
||||||
|
@ -442,4 +471,16 @@ type optionType = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.formName{
|
||||||
|
margin-bottom: 10px;
|
||||||
|
font-size: 16px;
|
||||||
|
&::before{
|
||||||
|
width: 2px;
|
||||||
|
background-color: rgb(184, 184, 184);
|
||||||
|
display: inline-block;
|
||||||
|
height: 13px;
|
||||||
|
margin-right: 3px;
|
||||||
|
content:''
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -29,6 +29,13 @@
|
||||||
<template #type="slotProps">
|
<template #type="slotProps">
|
||||||
{{ slotProps.type?.name }}
|
{{ slotProps.type?.name }}
|
||||||
</template>
|
</template>
|
||||||
|
<template #roleList="slotProps">
|
||||||
|
<j-ellipsis>
|
||||||
|
{{ slotProps?.roleList?.map((item)=>{
|
||||||
|
return item.name
|
||||||
|
}).join(',') }}
|
||||||
|
</j-ellipsis>
|
||||||
|
</template>
|
||||||
<template #status="slotProps">
|
<template #status="slotProps">
|
||||||
<BadgeStatus
|
<BadgeStatus
|
||||||
:status="slotProps.status"
|
:status="slotProps.status"
|
||||||
|
@ -94,10 +101,11 @@
|
||||||
: '删除',
|
: '删除',
|
||||||
}"
|
}"
|
||||||
:popConfirm="{
|
:popConfirm="{
|
||||||
title: `确认删除`,
|
title:'确认删除?',
|
||||||
onConfirm: () =>
|
onConfirm: () =>
|
||||||
table.clickDel(slotProps.id),
|
table.clickDel(slotProps.id),
|
||||||
}"
|
}"
|
||||||
|
|
||||||
:disabled="slotProps.status"
|
:disabled="slotProps.status"
|
||||||
>
|
>
|
||||||
<AIcon type="DeleteOutlined" />
|
<AIcon type="DeleteOutlined" />
|
||||||
|
@ -126,6 +134,7 @@ import {
|
||||||
getUserList_api,
|
getUserList_api,
|
||||||
changeUserStatus_api,
|
changeUserStatus_api,
|
||||||
deleteUser_api,
|
deleteUser_api,
|
||||||
|
queryRole_api
|
||||||
} from '@/api/system/user';
|
} from '@/api/system/user';
|
||||||
import { onlyMessage } from '@/utils/comm';
|
import { onlyMessage } from '@/utils/comm';
|
||||||
|
|
||||||
|
@ -171,6 +180,35 @@ const columns = [
|
||||||
},
|
},
|
||||||
scopedSlots: true,
|
scopedSlots: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: '角色',
|
||||||
|
dataIndex: 'roleList',
|
||||||
|
key: 'roleList',
|
||||||
|
search:{
|
||||||
|
type:'select',
|
||||||
|
rename:'id$in-dimension$role',
|
||||||
|
options:() =>
|
||||||
|
new Promise((resolve)=>{
|
||||||
|
queryRole_api(
|
||||||
|
{
|
||||||
|
paging:false,
|
||||||
|
sorts: [
|
||||||
|
{ name: 'createTime', order: 'desc' },
|
||||||
|
{ name: 'id', order: 'desc' },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
).then((resp:any)=>{
|
||||||
|
resolve(
|
||||||
|
resp.result.map((item: dictType) => ({
|
||||||
|
label: item.name,
|
||||||
|
value: item.id,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
scopedSlots: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: '状态',
|
title: '状态',
|
||||||
dataIndex: 'status',
|
dataIndex: 'status',
|
||||||
|
@ -215,7 +253,7 @@ const columns = [
|
||||||
dataIndex: 'action',
|
dataIndex: 'action',
|
||||||
key: 'action',
|
key: 'action',
|
||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
width: 150,
|
width: 200,
|
||||||
scopedSlots: true,
|
scopedSlots: true,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
|
@ -96,9 +96,10 @@ export default defineConfig(({ mode}) => {
|
||||||
// target: 'http://192.168.32.244:8881',
|
// target: 'http://192.168.32.244:8881',
|
||||||
// target: 'http://192.168.32.163:8844', //张季本地
|
// target: 'http://192.168.32.163:8844', //张季本地
|
||||||
// target: 'http://120.77.179.54:8844', // 120测试
|
// target: 'http://120.77.179.54:8844', // 120测试
|
||||||
// target: 'http://192.168.33.46:8844', // 本地开发环境
|
target: 'http://192.168.33.46:8844', // 本地开发环境
|
||||||
target: 'http://192.168.33.1:8845', // 社区版开发环境
|
// target: 'http://192.168.33.1:8845', // 社区版开发环境
|
||||||
// target: 'http://192.168.32.5:8848', // 刘本地
|
// target: 'http://192.168.32.5:8848', // 刘本地
|
||||||
|
// target: 'http://192.168.32.187:8844', // 谭本地
|
||||||
ws: 'ws://192.168.33.46:8844',
|
ws: 'ws://192.168.33.46:8844',
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
rewrite: (path) => path.replace(/^\/api/, '')
|
rewrite: (path) => path.replace(/^\/api/, '')
|
||||||
|
|
|
@ -3738,10 +3738,10 @@ jetlinks-store@^0.0.3:
|
||||||
resolved "https://registry.npmjs.org/jetlinks-store/-/jetlinks-store-0.0.3.tgz"
|
resolved "https://registry.npmjs.org/jetlinks-store/-/jetlinks-store-0.0.3.tgz"
|
||||||
integrity sha512-AZf/soh1hmmwjBZ00fr1emuMEydeReaI6IBTGByQYhTmK1Zd5pQAxC7WLek2snRAn/HHDgJfVz2hjditKThl6Q==
|
integrity sha512-AZf/soh1hmmwjBZ00fr1emuMEydeReaI6IBTGByQYhTmK1Zd5pQAxC7WLek2snRAn/HHDgJfVz2hjditKThl6Q==
|
||||||
|
|
||||||
jetlinks-ui-components@^1.0.34-4:
|
jetlinks-ui-components@^1.0.34-7:
|
||||||
version "1.0.34-4"
|
version "1.0.34-7"
|
||||||
resolved "https://registry.npmjs.org/jetlinks-ui-components/-/jetlinks-ui-components-1.0.34-4.tgz#92cfc6be685988385a489f3e924383c4831f3ed5"
|
resolved "https://registry.npmjs.org/jetlinks-ui-components/-/jetlinks-ui-components-1.0.34-7.tgz#3a14e85edb4c5d11427d30f3925dc5f498478940"
|
||||||
integrity sha512-td+RgaBC5lQxRuDsHkCg9UEzCcSy4XikufnabVGz5IxU+UmXu+PJUjz2wo9vDe8sPSctyk/5jQU+N6GBPlp8JA==
|
integrity sha512-Rgbjig3QYP8CDVHLbco20Cf7sArYralO8yWtH5E5zylYAN2lINLUsgOlIVf9aweszZR/Ps+z/NLP0CoRQf1Xtw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@vueuse/core" "^9.12.0"
|
"@vueuse/core" "^9.12.0"
|
||||||
"@vueuse/router" "^9.13.0"
|
"@vueuse/router" "^9.13.0"
|
||||||
|
|
Loading…
Reference in New Issue