Browse Source

feat(vben5): add gdpr module

pull/1116/head
colin 12 months ago
parent
commit
bb8a83485c
  1. 3
      apps/vben5/apps/app-antd/src/locales/langs/en-US/abp.json
  2. 3
      apps/vben5/apps/app-antd/src/locales/langs/zh-CN/abp.json
  3. 9
      apps/vben5/apps/app-antd/src/store/auth.ts
  4. 1
      apps/vben5/apps/app-antd/src/views/_core/authentication/login.vue
  5. 1
      apps/vben5/packages/@abp/account/package.json
  6. 10
      apps/vben5/packages/@abp/account/src/components/MySetting.vue
  7. 18
      apps/vben5/packages/@abp/account/src/components/components/PersonalDataSettings.vue
  8. 36
      apps/vben5/packages/@abp/gdpr/package.json
  9. 1
      apps/vben5/packages/@abp/gdpr/src/api/index.ts
  10. 100
      apps/vben5/packages/@abp/gdpr/src/api/useGdprRequestsApi.ts
  11. 60
      apps/vben5/packages/@abp/gdpr/src/components/GdprCard.vue
  12. 218
      apps/vben5/packages/@abp/gdpr/src/components/GdprTable.vue
  13. 2
      apps/vben5/packages/@abp/gdpr/src/components/index.ts
  14. 3
      apps/vben5/packages/@abp/gdpr/src/index.ts
  15. 1
      apps/vben5/packages/@abp/gdpr/src/types/index.ts
  16. 17
      apps/vben5/packages/@abp/gdpr/src/types/requests.ts
  17. 6
      apps/vben5/packages/@abp/gdpr/tsconfig.json

3
apps/vben5/apps/app-antd/src/locales/langs/en-US/abp.json

@ -65,7 +65,8 @@
"noticeSettings": "Notice Settings",
"authenticatorSettings": "Authenticator Settings",
"changeAvatar": "Change Avatar",
"sessionSettings": "Session Settings"
"sessionSettings": "Session Settings",
"personalDataSettings": "Personal Data Settings"
},
"profile": "My Profile"
},

3
apps/vben5/apps/app-antd/src/locales/langs/zh-CN/abp.json

@ -65,7 +65,8 @@
"noticeSettings": "新消息通知",
"authenticatorSettings": "身份验证程序",
"changeAvatar": "更改头像",
"sessionSettings": "会话管理"
"sessionSettings": "会话管理",
"personalDataSettings": "个人信息管理"
},
"profile": "个人中心"
},

9
apps/vben5/apps/app-antd/src/store/auth.ts

@ -100,14 +100,17 @@ export const useAuthStore = defineStore('auth', () => {
}
async function fetchUserInfo() {
let userInfo: ({ [key: string]: any } & UserInfo) | null = null;
let userInfo: null | (UserInfo & { [key: string]: any }) = null;
const userInfoRes = await getUserInfoApi();
const abpConfig = await getConfigApi();
userInfo = {
userId: userInfoRes.sub ?? abpConfig.currentUser.id,
username: userInfoRes.uniqueName ?? abpConfig.currentUser.userName,
realName: userInfoRes.name ?? abpConfig.currentUser.name,
avatar: userInfoRes.avatarUrl ?? userInfoRes.picture,
realName:
userInfoRes.name ??
abpConfig.currentUser.name ??
abpConfig.currentUser.userName,
avatar: userInfoRes.avatarUrl ?? userInfoRes.picture ?? '',
desc: userInfoRes.uniqueName ?? userInfoRes.name,
email: userInfoRes.email ?? userInfoRes.email,
emailVerified:

1
apps/vben5/apps/app-antd/src/views/_core/authentication/login.vue

@ -1,5 +1,6 @@
<script lang="ts" setup>
import type { TwoFactorError } from '@abp/account';
import type { VbenFormSchema } from '@vben/common-ui';
import type { Recordable } from '@vben/types';

1
apps/vben5/packages/@abp/account/package.json

@ -21,6 +21,7 @@
},
"dependencies": {
"@abp/core": "workspace:*",
"@abp/gdpr": "workspace:*",
"@abp/identity": "workspace:*",
"@abp/request": "workspace:*",
"@abp/ui": "workspace:*",

10
apps/vben5/packages/@abp/account/src/components/MySetting.vue

@ -31,6 +31,9 @@ const SecuritySettings = defineAsyncComponent(
const SessionSettings = defineAsyncComponent(
() => import('./components/SessionSettings.vue'),
);
const PersonalDataSettings = defineAsyncComponent(
() => import('./components/PersonalDataSettings.vue'),
);
const { getApi, updateApi } = useProfileApi();
const userStore = useUserStore();
const { query } = useRoute();
@ -62,6 +65,10 @@ const menuItems = reactive([
key: 'authenticator',
label: $t('abp.account.settings.authenticatorSettings'),
},
{
key: 'personal-data',
label: $t('abp.account.settings.personalDataSettings'),
},
]);
const getUserInfo = computed((): null | UserInfo => {
if (!userStore.userInfo) {
@ -155,6 +162,9 @@ onMounted(async () => {
v-else-if="selectedMenuKeys[0] === 'authenticator'"
/>
<SessionSettings v-else-if="selectedMenuKeys[0] === 'session'" />
<PersonalDataSettings
v-else-if="selectedMenuKeys[0] === 'personal-data'"
/>
</div>
</div>
</Card>

18
apps/vben5/packages/@abp/account/src/components/components/PersonalDataSettings.vue

@ -0,0 +1,18 @@
<script setup lang="ts">
import { useUserStore } from '@vben/stores';
import { GdprCard } from '@abp/gdpr';
const userStore = useUserStore();
const onAccountDeleted = () => {
userStore.$reset();
window.location.reload();
};
</script>
<template>
<GdprCard @account-delete="onAccountDeleted" />
</template>
<style scoped></style>

36
apps/vben5/packages/@abp/gdpr/package.json

@ -0,0 +1,36 @@
{
"name": "@abp/gdpr",
"version": "9.0.4",
"homepage": "https://github.com/colinin/abp-next-admin",
"bugs": "https://github.com/colinin/abp-next-admin/issues",
"repository": {
"type": "git",
"url": "git+https://github.com/colinin/abp-next-admin.git",
"directory": "packages/@abp/gdpr"
},
"license": "MIT",
"type": "module",
"sideEffects": [
"**/*.css"
],
"exports": {
".": {
"types": "./src/index.ts",
"default": "./src/index.ts"
}
},
"dependencies": {
"@abp/core": "workspace:*",
"@abp/request": "workspace:*",
"@abp/ui": "workspace:*",
"@ant-design/icons-vue": "catalog:",
"@vben/common-ui": "workspace:*",
"@vben/hooks": "workspace:*",
"@vben/icons": "workspace:*",
"@vben/layouts": "workspace:*",
"@vben/locales": "workspace:*",
"ant-design-vue": "catalog:",
"dayjs": "catalog:",
"vue": "catalog:*"
}
}

1
apps/vben5/packages/@abp/gdpr/src/api/index.ts

@ -0,0 +1 @@
export * from './useGdprRequestsApi';

100
apps/vben5/packages/@abp/gdpr/src/api/useGdprRequestsApi.ts

@ -0,0 +1,100 @@
import type { PagedResultDto } from '@abp/core';
import type { GdprRequestDto, GdprRequestGetListInput } from '../types';
import { useRequest } from '@abp/request';
export function useGdprRequestsApi() {
const { cancel, request } = useRequest();
/**
*
* @param {string} id Id
* @returns {void}
*/
function deleteApi(id: string): Promise<void> {
return request(`/api/gdpr/requests/${id}`, {
method: 'DELETE',
});
}
/**
*
* @returns {Promise<void>}
*/
function deletePersonalAccountApi(): Promise<void> {
return request(`/api/gdpr/requests/personal-account`, {
method: 'DELETE',
});
}
/**
*
* @returns {Promise<void>}
*/
function deletePersonalDataApi(): Promise<void> {
return request(`/api/gdpr/requests/personal-data`, {
method: 'DELETE',
});
}
/**
*
* @returns {Promise<void>}
*/
function preparePersonalDataApi(): Promise<void> {
return request(`/api/gdpr/requests/personal-data/prepare`, {
method: 'POST',
});
}
/**
*
* @returns {Promise<Blob>}
*/
function downloadPersonalDataApi(requestId: string): Promise<Blob> {
return request<Blob>(
`/api/gdpr/requests/personal-data/download/${requestId}`,
{
method: 'GET',
responseType: 'blob',
},
);
}
/**
*
* @param {string} id Id
* @returns {Promise<GdprRequestDto>}
*/
function getApi(id: string): Promise<GdprRequestDto> {
return request<GdprRequestDto>(`/api/gdpr/requests/${id}`, {
method: 'GET',
});
}
/**
*
* @param {GdprRequestGetListInput} input
* @returns {Promise<PagedResultDto<GdprRequestDto>>}
*/
function getPagedListApi(
input?: GdprRequestGetListInput,
): Promise<PagedResultDto<GdprRequestDto>> {
return request<PagedResultDto<GdprRequestDto>>(`/api/gdpr/requests`, {
method: 'GET',
params: input,
});
}
return {
cancel,
deleteApi,
deletePersonalAccountApi,
deletePersonalDataApi,
downloadPersonalDataApi,
getApi,
getPagedListApi,
preparePersonalDataApi,
};
}

60
apps/vben5/packages/@abp/gdpr/src/components/GdprCard.vue

@ -0,0 +1,60 @@
<script setup lang="ts">
import { ref } from 'vue';
import { $t } from '@vben/locales';
import { Button, Card, Modal } from 'ant-design-vue';
import { useGdprRequestsApi } from '../api/useGdprRequestsApi';
import GdprTable from './GdprTable.vue';
const emits = defineEmits<{
(event: 'accountDelete'): void;
}>();
const { cancel, deletePersonalAccountApi } = useGdprRequestsApi();
const submiting = ref(false);
const onDelete = async () => {
Modal.confirm({
centered: true,
content: $t('AbpGdpr.DeletePersonalAccountWarning'),
onCancel: () => {
cancel();
},
onOk: async () => {
submiting.value = true;
try {
await deletePersonalAccountApi();
Modal.success({
centered: true,
content: $t('AbpGdpr.PersonalAccountDeleteRequestReceived'),
onOk: () => {
emits('accountDelete');
},
title: $t('AbpGdpr.RequestedSuccessfully'),
});
} finally {
submiting.value = false;
}
},
title: $t('AbpUi.AreYouSure'),
});
};
</script>
<template>
<Card
:bordered="false"
:title="$t('abp.account.settings.personalDataSettings')"
>
<template #extra>
<Button block danger type="dashed" @click="onDelete">
{{ $t('AbpGdpr.DeletePersonalAccount') }}
</Button>
</template>
<GdprTable />
</Card>
</template>
<style scoped></style>

218
apps/vben5/packages/@abp/gdpr/src/components/GdprTable.vue

@ -0,0 +1,218 @@
<script setup lang="ts">
import type { VxeGridListeners, VxeGridProps } from '@abp/ui';
import type { GdprRequestDto } from '../types/requests';
import { h } from 'vue';
import { $t } from '@vben/locales';
import { formatToDateTime, useAbpStore } from '@abp/core';
import { useVbenVxeGrid } from '@abp/ui';
import { DeleteOutlined, DownloadOutlined } from '@ant-design/icons-vue';
import { Button, message, Modal, Tag } from 'ant-design-vue';
import dayJs from 'dayjs';
import { useGdprRequestsApi } from '../api/useGdprRequestsApi';
defineOptions({
name: 'GdprTable',
});
const {
cancel,
deleteApi,
deletePersonalDataApi,
downloadPersonalDataApi,
getPagedListApi,
preparePersonalDataApi,
} = useGdprRequestsApi();
const abpStore = useAbpStore();
const gridOptions: VxeGridProps<GdprRequestDto> = {
columns: [
{
align: 'left',
field: 'readyTime',
slots: { default: 'readly' },
title: $t('AbpGdpr.DisplayName:ReadyTime'),
},
{
align: 'left',
field: 'creationTime',
title: $t('AbpGdpr.DisplayName:CreationTime'),
},
{
field: 'action',
fixed: 'right',
slots: { default: 'action' },
title: $t('AbpUi.Actions'),
width: 180,
},
],
exportConfig: {},
keepSource: true,
proxyConfig: {
ajax: {
query: async ({ page }, formValues) => {
const dto = await getPagedListApi({
maxResultCount: page.pageSize,
skipCount: (page.currentPage - 1) * page.pageSize,
...formValues,
});
const now = dayJs();
return {
totalCount: dto.totalCount,
items: dto.items.map((item) => {
return {
creationTime: formatToDateTime(item.creationTime),
id: item.id,
isReadly: now.diff(dayJs(item.readyTime)) >= 0,
readyTime: formatToDateTime(item.readyTime),
};
}),
};
},
},
response: {
total: 'totalCount',
list: 'items',
},
},
toolbarConfig: {
refresh: true,
},
};
const gridEvents: VxeGridListeners<GdprRequestDto> = {
cellClick: () => {},
};
const [Grid, gridApi] = useVbenVxeGrid({
gridEvents,
gridOptions,
});
const onRequestData = async () => {
gridApi.setLoading(true);
try {
await preparePersonalDataApi();
Modal.success({
centered: true,
content: $t('AbpGdpr.PersonalDataPrepareRequestReceived'),
title: $t('AbpGdpr.RequestedSuccessfully'),
});
await gridApi.query();
} finally {
gridApi.setLoading(false);
}
};
const onDeleteData = () => {
Modal.confirm({
centered: true,
content: $t('AbpGdpr.DeletePersonalDataWarning'),
onCancel: () => {
cancel();
},
onOk: async () => {
gridApi.setLoading(true);
try {
await deletePersonalDataApi();
Modal.success({
centered: true,
content: $t('AbpGdpr.PersonalDataDeleteRequestReceived'),
onOk: () => {
//
window.location.reload();
},
title: $t('AbpGdpr.RequestedSuccessfully'),
});
await gridApi.query();
} finally {
gridApi.setLoading(false);
}
},
title: $t('AbpUi.AreYouSure'),
});
};
const onDownload = async (row: GdprRequestDto) => {
const blob = await downloadPersonalDataApi(row.id);
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
const { userName } = abpStore.application!.currentUser;
const fileName = `${$t('AbpGdpr.PersonalData')}_${userName}_${formatToDateTime(row.readyTime)}.xlsx`;
link.setAttribute('download', decodeURIComponent(fileName));
document.body.append(link);
link.click();
window.URL.revokeObjectURL(url);
link.remove();
};
const onDelete = (row: GdprRequestDto) => {
Modal.confirm({
centered: true,
content: $t('AbpUi.ItemWillBeDeletedMessage'),
onCancel: () => {
cancel();
},
onOk: async () => {
gridApi.setLoading(true);
try {
await deleteApi(row.id);
message.success($t('AbpUi.SuccessfullyDeleted'));
await gridApi.query();
} finally {
gridApi.setLoading(false);
}
},
title: $t('AbpUi.AreYouSure'),
});
};
</script>
<template>
<Grid :table-title="$t('AbpGdpr.PersonalData')">
<template #toolbar-tools>
<div class="flex flex-row gap-2">
<Button type="primary" @click="onRequestData">
{{ $t('AbpGdpr.RequestPersonalData') }}
</Button>
<Button type="primary" danger @click="onDeleteData">
{{ $t('AbpGdpr.DeletePersonalData') }}
</Button>
</div>
</template>
<template #readly="{ row }">
{{ row.readyTime }}
<Tag v-if="!row.isReadly" color="warning">
{{ $t('AbpGdpr.Preparing') }}
</Tag>
</template>
<template #action="{ row }">
<div class="flex flex-row">
<Button
v-if="row.isReadly"
:icon="h(DownloadOutlined)"
block
type="link"
@click="onDownload(row)"
>
{{ $t('AbpGdpr.Download') }}
</Button>
<Button
:icon="h(DeleteOutlined)"
block
danger
type="link"
@click="onDelete(row)"
>
{{ $t('AbpUi.Delete') }}
</Button>
</div>
</template>
</Grid>
</template>
<style lang="scss" scoped></style>

2
apps/vben5/packages/@abp/gdpr/src/components/index.ts

@ -0,0 +1,2 @@
export { default as GdprCard } from './GdprCard.vue';
export { default as GdprTable } from './GdprTable.vue';

3
apps/vben5/packages/@abp/gdpr/src/index.ts

@ -0,0 +1,3 @@
export * from './api';
export * from './components';
export * from './types';

1
apps/vben5/packages/@abp/gdpr/src/types/index.ts

@ -0,0 +1 @@
export * from './requests';

17
apps/vben5/packages/@abp/gdpr/src/types/requests.ts

@ -0,0 +1,17 @@
import type { EntityDto, PagedAndSortedResultRequestDto } from '@abp/core';
interface GdprRequestDto extends EntityDto<string> {
/** 创建时间 */
creationTime: string;
/** 就绪时间 */
readyTime: string;
}
interface GdprRequestGetListInput extends PagedAndSortedResultRequestDto {
/** 创建时间 */
creationTime?: string;
/** 就绪时间 */
readyTime?: string;
}
export type { GdprRequestDto, GdprRequestGetListInput };

6
apps/vben5/packages/@abp/gdpr/tsconfig.json

@ -0,0 +1,6 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "@vben/tsconfig/web.json",
"include": ["src"],
"exclude": ["node_modules"]
}
Loading…
Cancel
Save