Merge branch 'dev' of github.com:jetlinks/jetlinks-ui-vue into dev

This commit is contained in:
JiangQiming 2023-03-13 18:06:11 +08:00
commit 977b68473a
19 changed files with 247 additions and 89 deletions

View File

@ -22,3 +22,7 @@ export const rdbTree_api = (id: string) => server.get(`/datasource/rdb/${id}/tab
export const rdbTables_api = (id: string,key:string) => server.get(`/datasource/rdb/${id}/table/${key}`);
// 保存表格
export const saveTable_api = (id: string,data:object) => server.patch(`/datasource/rdb/${id}/table`,data);
/**
*
*/
export const delSaveRow_api = (datasourceId: string, table: string, data: any) => server.post(`/datasource/rdb/${datasourceId}/table/${table}/drop-column`,data);

View File

@ -4,7 +4,11 @@ import server from '@/utils/request';
export const getMenuTree_api = (data: object) => server.post(`/menu/_all/tree`, data);
export const queryOwnThree = (data: any) => server.post<any>('/menu/user-own/tree', data)
/**
*
* @param data
*/
export const validCode_api = (data:object) => server.get(`/menu/code/_validate`,data);
// 获取资产类型

View File

@ -18,11 +18,11 @@
</div>
</template>
<script setup lang="ts">
<script setup lang="tsx">
import { getList_api } from '@/api/account/notificationRecord';
import NoticeInfo from './NoticeInfo.vue';
import { getWebSocket } from '@/utils/websocket';
import { notification } from 'ant-design-vue';
import { notification, Button } from 'ant-design-vue';
import { changeStatus_api } from '@/api/account/notificationRecord';
import { useUserInfo } from '@/store/userInfo';
@ -38,12 +38,40 @@ const subscribeNotice = () => {
.subscribe((resp: any) => {
total.value += 1;
notification.open({
message: 'Notification Title',
description:
'This is the content of the notification. This is the content of the notification. This is the content of the notification.',
message: resp?.payload?.topicName,
description: () => (
<div
class="ellipsis"
style={{ cursor: 'pointer' }}
onClick={() => {
changeStatus_api('_read', [resp.id]);
}}
>
{resp?.payload?.message}
</div>
),
onClick: () => {
changeStatus_api('_read', [resp.id]);
},
key: resp.payload.id,
btn: (
<Button
type="primary"
size="small"
onClick={() => {
changeStatus_api('_read', [resp.id]).then(
(resp: any) => {
if (resp.status === 200) {
notification.close(resp.payload.id);
getList();
}
},
);
}}
>
标记已读
</Button>
),
});
});
};

View File

@ -164,9 +164,11 @@ import {
rdbTree_api,
rdbTables_api,
saveTable_api,
delSaveRow_api,
} from '@/api/system/dataSource';
import { FormInstance, message } from 'ant-design-vue';
import { DataNode } from 'ant-design-vue/lib/tree';
import { cloneDeep } from 'lodash';
import type { dbColumnType, dictItemType, sourceItemType } from '../typing';
const id = useRoute().query.id as string;
@ -292,7 +294,9 @@ const table = reactive({
getTabelData: (key: string) => {
rdbTables_api(id, key).then((resp: any) => {
table.data = resp.result.columns;
table.data = resp.result.columns.map(
(item: object, index: number) => ({ ...item, index }),
);
});
},
addRow: () => {
@ -307,9 +311,11 @@ const table = reactive({
table.data.push(initData);
},
clickSave: () => {
const columns = cloneDeep(table.data);
columns.forEach((item) => delete item.index);
const params = {
name: leftData.selectedKeys[0],
columns: table.data,
columns,
};
saveTable_api(id, params).then((resp) => {
if (resp.status === 200) {
@ -318,7 +324,15 @@ const table = reactive({
}
});
},
clickDel: (row: any) => {},
clickDel: (row: any) => {
if (row.scale !== undefined) {
delSaveRow_api(id, leftData.selectedKeys[0], [row.name]).then(
(resp: any) => {
if (resp.status === 200) table.data.splice(row.index, 1);
},
);
} else table.data.splice(row.index, 1);
},
});
const addFormRef = ref<FormInstance>();

View File

@ -45,7 +45,14 @@
<j-form-item
:name="['shareConfig', 'url']"
label="URL"
:rules="[{ required: true, message: '请输入URL' }]"
:rules="[
{
required: true,
message: '请输入URL',
trigger: 'change',
},
{ validator: form.checkUrl, trigger: 'blur' },
]"
>
<j-input
v-model:value="form.data.shareConfig.url"
@ -183,6 +190,7 @@ import {
saveDataSource_api,
} from '@/api/system/dataSource';
import { FormInstance, message } from 'ant-design-vue';
import { Rule } from 'ant-design-vue/lib/form';
import type { dictItemType, optionItemType, sourceItemType } from '../typing';
const emits = defineEmits(['confirm', 'update:visible']);
@ -218,6 +226,15 @@ const form = reactive({
typeOptions: [] as optionItemType[],
checkUrl: (_rule: Rule, value: string): Promise<any> => {
if (!value) return Promise.reject('请输入URL');
const arr = value.split(':');
if (arr?.[0] === 'jdbc' || arr?.[0] === 'r2dbc') {
return Promise.resolve();
} else {
return Promise.reject('请输入正确的URL');
}
},
getTypeOption: () => {
getDataTypeDict_api().then((resp: any) => {
const result = resp.result as dictItemType[];

View File

@ -33,5 +33,6 @@ export type dbColumnType = {
notnull: boolean,
comment: string,
name: string,
scale?:number
scale?:number,
index?:number
}

View File

@ -32,13 +32,13 @@
ref="tableRef"
:request="table.requestFun"
:gridColumn="2"
model="CARD"
:params="query.params.value"
:rowSelection="{
selectedRowKeys: table._selectedRowKeys.value,
onChange: pageChange
onChange: selectRow,
}"
@cancelSelect="table.cancelSelect"
:columns="columns"
>
<template #card="slotProps">
<CardBox
@ -100,6 +100,30 @@
</template>
</CardBox>
</template>
<template #permission="slotProps">
<div
style="cursor: pointer"
class="card-item-content-value"
@click="(e) => e.stopPropagation()"
>
<a-checkbox-group
v-model:value="slotProps.selectPermissions"
:options="slotProps.permissionList"
/>
</div>
</template>
<template #state="slotProps">
<BadgeStatus
:status="slotProps.state.value"
:text="slotProps.state.text"
:statusNames="{
online: 'success',
offline: 'error',
notActive: 'warning',
}"
></BadgeStatus>
</template>
</j-pro-table>
</a-modal>
</template>
@ -162,6 +186,9 @@ const options = computed(() =>
})),
);
const columns = props.queryColumns.filter(
(item) => item.dataIndex !== 'action',
);
const query = {
columns: [
{
@ -369,8 +396,6 @@ const table: any = {
}),
//
requestFun: async (oParams: any) => {
table._selectedRowKeys.value = [];
table.selectedRows = [];
if (props.parentId) {
const terms = [
{
@ -421,9 +446,11 @@ const table: any = {
},
};
table.init();
const pageChange = ()=>{
console.log(1111,table._selectedRowKeys.value);
}
const selectRow = (keys:string[], rows:any[]) => {
const okRows = rows.filter(item=>!!item.permissionList.find((permiss:any)=>permiss.value === 'share'));
table.selectedRows = okRows;
table._selectedRowKeys.value = okRows.map(item=>item.id)
};
</script>
<style lang="less" scoped>

View File

@ -11,8 +11,9 @@
:params="queryParams"
:rowSelection="{
selectedRowKeys: table._selectedRowKeys.value,
onChange:(keys:string[])=>table._selectedRowKeys.value = [...keys],
onSelectNone: table.cancelSelect
}"
@cancelSelect="table.cancelSelect"
:columns="columns"
>
<template #headerTitle>
@ -425,8 +426,6 @@ const table = {
}),
//
requestFun: async (oParams: any) => {
table._selectedRowKeys.value = [];
table.selectedRows = [];
if (props.parentId) {
const params = {
...oParams,

View File

@ -286,6 +286,7 @@ const columns = [
dataIndex: 'state',
key: 'state',
ellipsis: true,
width: '80px',
search: {
type: 'select',
options: [

View File

@ -45,8 +45,8 @@
label="名称"
name="name"
:rules="[
{ required: true, message: '请输入名称' },
{ max: 64, message: '最多可输入64个字符' },
{ required: true, message: '请输入名称',trigger: 'change', },
{ max: 64, message: '最多可输入64个字符', trigger: 'change', },
]"
>
<j-input v-model:value="form.data.name" />
@ -57,8 +57,12 @@
label="编码"
name="code"
:rules="[
{ required: true, message: '请输入编码' },
{ max: 64, message: '最多可输入64个字符' },
{ required: true, message: '请输入编码', trigger: 'change', },
{ max: 64, message: '最多可输入64个字符', trigger: 'change', },
{
validator: form.checkCode,
trigger: 'blur',
},
]"
>
<j-input v-model:value="form.data.code" />
@ -228,7 +232,9 @@ import {
getMenuInfo_api,
saveMenuInfo_api,
addMenuInfo_api,
validCode_api,
} from '@/api/system/menu';
import { Rule } from 'ant-design-vue/lib/form';
const permission = 'system/Menu';
//
@ -260,18 +266,18 @@ const form = reactive({
treeData: [], //
assetsType: [] as assetType[], //
saveLoading: false,
sourceCode: '', // code
init: () => {
//
routeParams.id &&
getMenuInfo_api(routeParams.id).then((resp: any) => {
console.log(1111);
form.data = {
...(resp.result as formType),
accessSupport:
resp.result?.accessSupport?.value || 'unsupported',
};
form.sourceCode = resp.result.code
});
//
getMenuTree_api({ paging: false }).then((resp: any) => {
@ -285,6 +291,20 @@ const form = reactive({
}));
});
},
checkCode: async (_rule: Rule, value: string): Promise<any> => {
if (!value) return Promise.reject('请输入编码');
else if (value.length > 64) return Promise.reject('最多可输入64个字符');
//
else if (routeParams.id && value === form.sourceCode) return Promise.resolve('');
else {
const resp: any = await validCode_api({
code: value,
owner: 'iot',
});
if (resp.result.passed) return Promise.resolve();
else return Promise.reject('该编码重复');
}
},
clickSave: () => {
if (!basicFormRef || !permissFormRef) return;
Promise.all([

View File

@ -20,7 +20,7 @@
<j-space :size="16">
<PermissionButton
type="link"
:uhasPermission="`${permission}:update`"
:hasPermission="`${permission}:update`"
:tooltip="{ title: '编辑' }"
@click="openDialog('编辑', slotProps)"
>
@ -28,7 +28,7 @@
</PermissionButton>
<PermissionButton
type="link"
:uhasPermission="true"
:hasPermission="`${permission}:view`"
:tooltip="{ title: '查看' }"
@click="openDialog('查看', slotProps)"
>
@ -36,7 +36,6 @@
</PermissionButton>
<PermissionButton
type="link"
:uhasPermission="`${permission}:update`"
:tooltip="{ title: '删除' }"
:popConfirm="{
title: `确认删除`,
@ -83,7 +82,7 @@ const selectItem = ref<any>({});
const dialogVisible = ref(false);
const dialogTitle = ref<'查看' | '新增' | '编辑'>('新增');
const openDialog = (mode: '查看' | '新增' | '编辑', row: object) => {
if(!routeParams.id) return message.warning('请先新增菜单基本信息')
if (!routeParams.id) return message.warning('请先新增菜单基本信息');
selectItem.value = { ...row };
dialogTitle.value = mode;
dialogVisible.value = true;
@ -149,4 +148,12 @@ type tableDataItem = {
};
</script>
<style scoped></style>
<style lang="less" scoped>
.button-mange-container {
:deep(.ant-table-cell) {
.ant-btn-link {
padding: 0;
}
}
}
</style>

View File

@ -74,7 +74,7 @@
</page-container>
</template>
<script setup lang="ts">
<script setup lang="ts" name="Menu">
import PermissionButton from '@/components/PermissionButton/index.vue';
import { getMenuTree_api, delMenuInfo_api } from '@/api/system/menu';
@ -201,10 +201,12 @@ const table = reactive({
};
},
addChildren: (row: any) => {
const sortIndex = row?.children?.length || 0;
router.push(
`/system/Menu/detail/:id?pid=${row.id}&basePath=${
row.url || ''
}&sortIndex=${row.children.length + 1}`,
}&sortIndex=${sortIndex + 1}`,
);
},
//

View File

@ -12,7 +12,16 @@
<j-form-item
name="id"
:rules="[
{ required: true, message: '请输入标识(ID)' },
{
required: true,
message: '请输入标识(ID)',
trigger: 'change',
},
{
max: 64,
message: '最多可输入64个字符',
trigger: 'change',
},
{ validator: form.rules.idCheck, trigger: 'blur' },
]"
class="question-item"
@ -33,19 +42,23 @@
<j-input
v-model:value="form.data.id"
placeholder="请输入标识(ID)"
:maxlength="64"
:disabled="dialogTitle === '编辑'"
/>
</j-form-item>
<j-form-item
name="name"
label="名称"
:rules="[{ required: true, message: '请输入名称' }]"
:rules="[
{ required: true, message: '请输入名称' },
{
max: 64,
message: '最多可输入64个字符',
},
]"
>
<j-input
v-model:value="form.data.name"
placeholder="请输入名称"
:maxlength="64"
/>
</j-form-item>
</j-form>
@ -146,16 +159,17 @@ const form = reactive({
},
rules: {
//
idCheck: (_rule: Rule, id: string, cb: Function) => {
if (props.data.id) return cb();
else if (!id) return cb('请输入标识(ID)');
checkId_api({ id })
.then((resp: any) => {
if (resp.status === 200 && !resp.result.passed)
cb(resp.result.reason);
else cb();
})
.catch(() => cb('验证失败'));
idCheck: async (_rule: Rule, id: string): Promise<any> => {
if (!id) return Promise.reject('请输入标识(ID)');
else if (id.length > 64)
return Promise.reject('最多可输入64个字符');
else if (props.data.id && props.data.id === form.data.id)
return Promise.resolve();
else {
const resp: any = await checkId_api({ id });
if (resp.result.passed) return Promise.resolve();
else return Promise.reject(resp.result.reason);
}
},
},
submit: () => {

View File

@ -17,7 +17,7 @@
<template #headerTitle>
<PermissionButton
type="primary"
:uhasPermission="`${permission}:add`"
:hasPermission="`${permission}:add`"
@click="table.openDialog(undefined)"
>
<AIcon type="PlusOutlined" />新增
@ -27,7 +27,7 @@
<template #overlay>
<j-menu>
<j-menu-item>
<j-upload
<a-upload
name="file"
action="#"
accept=".json"
@ -44,11 +44,11 @@
>
导入
</PermissionButton>
</j-upload>
</a-upload>
</j-menu-item>
<j-menu-item>
<PermissionButton
:uhasPermission="`${permission}:export`"
:hasPermission="`${permission}:export`"
:popConfirm="{
title: `确认导出?`,
onConfirm: () =>
@ -86,7 +86,7 @@
</PermissionButton>
<PermissionButton
:uhasPermission="`${permission}:action`"
:hasPermission="`${permission}:action`"
type="link"
:popConfirm="{
title: `确定要${
@ -108,7 +108,7 @@
</PermissionButton>
<PermissionButton
:uhasPermission="`${permission}:delete`"
:hasPermission="`${permission}:delete`"
type="link"
:tooltip="{
title: slotProps.status

View File

@ -21,7 +21,6 @@
<j-input
v-model:value="form.data.name"
placeholder="请输入名称"
:maxlength="64"
/>
</j-form-item>
<j-form-item
@ -36,7 +35,6 @@
<j-input
v-model:value="form.data.relation"
placeholder="请输入标识"
:maxlength="64"
:disabled="!!form.data.id"
/>
</j-form-item>
@ -51,12 +49,14 @@
<j-select
v-model:value="form.data.objectType"
:disabled="!!form.data.id"
@change="() => (form.data.targetType = undefined)"
>
<j-select-option
v-for="item in form.objectList"
:value="item.id"
>{{ item.name }}</j-select-option
>
{{ item.name }}.
</j-select-option>
</j-select>
</j-form-item>
</j-col>
@ -73,17 +73,14 @@
<j-select-option
v-for="item in targetList"
:value="item.id"
>{{ item.name }}</j-select-option
>
{{ item.name }}
</j-select-option>
</j-select>
</j-form-item>
</j-col>
</j-row>
<j-form-item
name="description"
label="说明"
:rules="[{ max: 200, message: '最多可输入200个字符' }]"
>
<j-form-item name="description" label="说明">
<j-textarea
v-model:value="form.data.description"
placeholder="请输入说明"
@ -134,7 +131,8 @@ const form = reactive({
data: props.data,
rules: {
checkRelation: (_rule: Rule, value: string): any => {
if (!value) return '';
if (!value) return Promise.reject('');
else if (value.length > 64) return Promise.reject('');
const reg = new RegExp('^[0-9a-zA-Z_\\\\-]+$');
return reg.test(value)
@ -172,7 +170,7 @@ type formType = {
name: string;
relation: string;
objectType: string;
targetType: string;
targetType: string | undefined;
description: string;
id?: string;
};

View File

@ -19,7 +19,7 @@
<template #headerTitle>
<PermissionButton
type="primary"
:uhasPermission="`${permission}:add`"
:hasPermission="`${permission}:add`"
@click="table.openDialog(undefined)"
>
<AIcon type="PlusOutlined" />新增
@ -28,7 +28,7 @@
<template #action="slotProps">
<j-space :size="16">
<PermissionButton
:uhasPermission="`${permission}:update`"
:hasPermission="`${permission}:update`"
type="link"
:tooltip="{
title: '编辑',
@ -39,7 +39,7 @@
</PermissionButton>
<PermissionButton
:uhasPermission="`${permission}:delete`"
:hasPermission="`${permission}:delete`"
type="link"
:tooltip="{ title: '删除' }"
:popConfirm="{

View File

@ -2,19 +2,26 @@
<div class="role-permiss-container">
<section class="card">
<h5>基本信息</h5>
<j-form ref="formRef" class="basic-form" :model="form.data" layout="vertical">
<j-form
ref="formRef"
class="basic-form"
:model="form.data"
layout="vertical"
>
<j-form-item
name="name"
label="名称"
:rules="[{ required: true, message: '请输入名称' }]"
:rules="[
{ required: true, message: '请输入名称' },
{ max: 64, message: '最多可输入64个字符' },
]"
>
<j-input
v-model:value="form.data.name"
placeholder="请输入角色名称"
:maxlength="64"
/>
</j-form-item>
<j-form-item name="name" label="说明">
<j-form-item label="说明">
<j-textarea
v-model:value="form.data.description"
placeholder="请输入说明"
@ -33,7 +40,7 @@
type="primary"
:disabled="form.loading"
@click="form.clickSave"
style="margin-top: 24px;"
style="margin-top: 24px"
>保存</j-button
>
</section>
@ -113,6 +120,13 @@ form.getForm();
}
.basic-form {
:deep(.ant-form-item-required) {
padding-right: 12px;
&::before{
right: 0;
}
}
.ant-form-item {
display: block;
width: 60%;

View File

@ -1,5 +1,5 @@
<template>
<a-modal
<j-modal
visible
title="新增"
width="670px"
@ -7,29 +7,31 @@
@ok="confirm"
:confirm-loading="loading"
>
<a-form ref="formRef" :model="form" layout="vertical">
<a-form-item
<j-form ref="formRef" :model="form" layout="vertical">
<j-form-item
name="name"
label="名称"
:rules="[{ required: true, message: '请输入名称' }]"
:rules="[
{ required: true, message: '请输入名称' },
{ max: 64, message: '最多可输入64个字符' },
]"
>
<a-input
<j-input
v-model:value="form.name"
placeholder="请输入角色名称"
allow-clear
:maxlength="64"
/>
</a-form-item>
<a-form-item name="name" label="说明">
<a-textarea
</j-form-item>
<j-form-item name="name" label="说明">
<j-textarea
v-model:value="form.description"
placeholder="请输入说明"
allow-clear
:maxlength="200"
/>
</a-form-item>
</a-form>
</a-modal>
</j-form-item>
</j-form>
</j-modal>
</template>
<script setup lang="ts">

View File

@ -1,6 +1,6 @@
<template>
<page-container>
<a-card class="role-container">
<div class="role-container">
<j-advanced-search
:columns="columns"
@search="(params:any)=>queryParams = params"
@ -12,6 +12,12 @@
:request="getRoleList_api"
model="TABLE"
:params="queryParams"
:defaultParams="{
sorts: [
{ name: 'createTime', order: 'desc' },
{ name: 'id', order: 'desc' },
],
}"
>
<template #headerTitle>
<PermissionButton
@ -24,7 +30,7 @@
</template>
<template #action="slotProps">
<a-space :size="16">
<j-space :size="16">
<PermissionButton
:uhasPermission="`${permission}:update`"
type="link"
@ -46,12 +52,12 @@
>
<AIcon type="DeleteOutlined" />
</PermissionButton>
</a-space>
</j-space>
</template>
</j-pro-table>
<AddDialog v-if="dialogVisible" v-model:visible="dialogVisible" />
</a-card>
</div>
</page-container>
</template>