Browse Source

feat(openiddict): 完善授权令牌页面.

pull/1056/head
colin 1 year ago
parent
commit
706def222e
  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/router/routes/modules/abp.ts
  4. 15
      apps/vben5/apps/app-antd/src/views/openiddict/tokens/index.vue
  5. 1
      apps/vben5/packages/@abp/openiddict/src/components/index.ts
  6. 165
      apps/vben5/packages/@abp/openiddict/src/components/tokens/TokenModal.vue
  7. 297
      apps/vben5/packages/@abp/openiddict/src/components/tokens/TokenTable.vue

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

@ -16,6 +16,7 @@
"title": "OpenIddict",
"applications": "Applications",
"authorizations": "Authorizations",
"scopes": "Scopes"
"scopes": "Scopes",
"tokens": "Tokens"
}
}

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

@ -16,6 +16,7 @@
"title": "OpenIddict",
"applications": "应用管理",
"authorizations": "授权管理",
"scopes": "范围管理"
"scopes": "范围管理",
"tokens": "授权令牌"
}
}

9
apps/vben5/apps/app-antd/src/router/routes/modules/abp.ts

@ -129,6 +129,15 @@ const routes: RouteRecordRaw[] = [
path: '/openiddict/scopes',
component: () => import('#/views/openiddict/scopes/index.vue'),
},
{
meta: {
title: $t('abp.openiddict.tokens'),
icon: 'oui:token-key',
},
name: 'OpenIddictTokens',
path: '/openiddict/tokens',
component: () => import('#/views/openiddict/tokens/index.vue'),
},
],
},
],

15
apps/vben5/apps/app-antd/src/views/openiddict/tokens/index.vue

@ -0,0 +1,15 @@
<script lang="ts" setup>
import { Page } from '@vben/common-ui';
import { TokenTable } from '@abp/openiddict';
defineOptions({
name: 'OpenIddictTokens',
});
</script>
<template>
<Page>
<TokenTable />
</Page>
</template>

1
apps/vben5/packages/@abp/openiddict/src/components/index.ts

@ -1,3 +1,4 @@
export { default as ApplicationTable } from './applications/ApplicationTable.vue';
export { default as AuthorizationTable } from './authorizations/AuthorizationTable.vue';
export { default as ScopeTable } from './scopes/ScopeTable.vue';
export { default as TokenTable } from './tokens/TokenTable.vue';

165
apps/vben5/packages/@abp/openiddict/src/components/tokens/TokenModal.vue

@ -0,0 +1,165 @@
<script setup lang="ts">
import type { OpenIddictTokenDto } from '../../types/tokens';
import { useAccess } from '@vben/access';
import { useVbenForm, useVbenModal } from '@vben/common-ui';
import { $t } from '@vben/locales';
import {
type IdentityUserDto,
userLookupApi,
UserLookupPermissions,
} from '@abp/identity';
import { CodeEditor } from '@abp/ui';
import { getApi as getApplication } from '../../api/applications';
import { getApi as getAuthorization } from '../../api/tokens';
defineOptions({
name: 'TokenModal',
});
const { hasAccessByCodes } = useAccess();
const [Form, formApi] = useVbenForm({
commonConfig: {
//
componentProps: {
class: 'w-full',
},
},
schema: [
{
component: 'Input',
componentProps: {
readonly: true,
},
fieldName: 'applicationId',
label: $t('AbpOpenIddict.DisplayName:ApplicationId'),
},
{
component: 'Input',
componentProps: {
readonly: true,
},
fieldName: 'authorizationId',
label: $t('AbpOpenIddict.DisplayName:AuthorizationId'),
},
{
component: 'Input',
componentProps: {
readonly: true,
},
fieldName: 'subject',
label: $t('AbpOpenIddict.DisplayName:Subject'),
},
{
component: 'Input',
componentProps: {
readonly: true,
},
fieldName: 'type',
label: $t('AbpOpenIddict.DisplayName:Type'),
},
{
component: 'Input',
componentProps: {
readonly: true,
},
fieldName: 'status',
label: $t('AbpOpenIddict.DisplayName:Status'),
},
{
component: 'Input',
componentProps: {
readonly: true,
},
fieldName: 'creationDate',
label: $t('AbpOpenIddict.DisplayName:CreationDate'),
},
{
component: 'Input',
componentProps: {
readonly: true,
},
fieldName: 'expirationDate',
label: $t('AbpOpenIddict.DisplayName:ExpirationDate'),
},
{
component: 'Input',
componentProps: {
readonly: true,
},
fieldName: 'redemptionDate',
label: $t('AbpOpenIddict.DisplayName:RedemptionDate'),
},
{
component: 'Input',
componentProps: {
readonly: true,
},
fieldName: 'referenceId',
label: $t('AbpOpenIddict.DisplayName:ReferenceId'),
},
{
component: 'Input',
componentProps: {
readonly: true,
},
fieldName: 'payload',
label: $t('AbpOpenIddict.DisplayName:Payload'),
},
],
showDefaultActions: false,
});
const [Modal, modalApi] = useVbenModal({
class: 'w-1/2',
draggable: true,
fullscreenButton: false,
onCancel() {
modalApi.close();
},
async onOpenChange(isOpen) {
if (isOpen) {
try {
modalApi.setState({ loading: true });
const { id } = modalApi.getData<OpenIddictTokenDto>();
await onGet(id);
} finally {
modalApi.setState({ loading: false });
}
}
},
showConfirmButton: false,
title: $t('AbpOpenIddict.Tokens'),
});
async function onGet(id: string) {
const authorization = await getAuthorization(id);
const application = await getApplication(authorization.applicationId!);
let subjectInfo: IdentityUserDto | undefined;
if (hasAccessByCodes([UserLookupPermissions.Default])) {
subjectInfo = await userLookupApi.findByIdApi(authorization.subject!);
}
formApi.setValues({
...authorization,
applicationId: `${application.clientId}(${authorization.applicationId})`,
subject: subjectInfo?.userName
? `${subjectInfo.userName}(${authorization.subject})`
: authorization.subject,
});
}
</script>
<template>
<Modal>
<Form>
<template #payload="{ modelValue }">
<CodeEditor :value="modelValue" readonly />
</template>
</Form>
</Modal>
</template>
<style scoped></style>

297
apps/vben5/packages/@abp/openiddict/src/components/tokens/TokenTable.vue

@ -0,0 +1,297 @@
<script setup lang="ts">
import type { VbenFormProps, VxeGridProps } from '@abp/ui';
import type { SelectValue } from 'ant-design-vue/es/select';
import type { OpenIddictApplicationDto } from '../../types';
import type { OpenIddictTokenDto } from '../../types/tokens';
import { defineAsyncComponent, h, onMounted, ref } from 'vue';
import { useAccess } from '@vben/access';
import { useVbenModal } from '@vben/common-ui';
import { createIconifyIcon } from '@vben/icons';
import { $t } from '@vben/locales';
import { formatToDateTime } from '@abp/core';
import { useVbenVxeGrid } from '@abp/ui';
import { DeleteOutlined, EditOutlined } from '@ant-design/icons-vue';
import { Button, message, Modal, Select } from 'ant-design-vue';
import debounce from 'lodash.debounce';
import { getPagedListApi as getApplications } from '../../api/applications';
import { deleteApi, getPagedListApi } from '../../api/tokens';
import { TokensPermissions } from '../../constants/permissions';
defineOptions({
name: 'TokenTable',
});
const CheckIcon = createIconifyIcon('ant-design:check-outlined');
const CloseIcon = createIconifyIcon('ant-design:close-outlined');
const { hasAccessByCodes } = useAccess();
const applications = ref<OpenIddictApplicationDto[]>([]);
const formOptions: VbenFormProps = {
//
collapsed: true,
collapsedRows: 2,
//
commonConfig: {
// label
colon: true,
//
componentProps: {
class: 'w-full',
},
},
fieldMappingTime: [
['creationTime', ['beginCreationTime', 'endCreationTime'], 'YYYY-MM-DD'],
[
'expirationDate',
['beginExpirationDate', 'endExpirationDate'],
'YYYY-MM-DD',
],
],
handleReset: onFormReset,
schema: [
{
component: 'Select',
fieldName: 'clientId',
formItemClass: 'col-span-1/3 items-baseline',
label: $t('AbpOpenIddict.DisplayName:ClientId'),
},
{
component: 'RangePicker',
fieldName: 'creationTime',
formItemClass: 'col-span-2 items-baseline',
label: $t('AbpOpenIddict.DisplayName:CreationTime'),
},
{
component: 'RangePicker',
fieldName: 'expirationDate',
formItemClass: 'col-span-2 items-baseline',
label: $t('AbpOpenIddict.DisplayName:ExpirationDate'),
},
{
component: 'Input',
fieldName: 'subject',
formItemClass: 'col-span-1/3 items-baseline',
label: $t('AbpOpenIddict.DisplayName:Subject'),
},
{
component: 'Input',
fieldName: 'status',
formItemClass: 'col-span-1/3 items-baseline',
label: $t('AbpOpenIddict.DisplayName:Status'),
},
{
component: 'Input',
fieldName: 'type',
formItemClass: 'col-span-1/3 items-baseline',
label: $t('AbpOpenIddict.DisplayName:Type'),
},
{
component: 'Input',
fieldName: 'referenceId',
formItemClass: 'col-span-1/3 items-baseline',
label: $t('AbpOpenIddict.DisplayName:ReferenceId'),
},
{
component: 'Input',
fieldName: 'filter',
formItemClass: 'col-span-2 items-baseline',
label: $t('AbpUi.Search'),
},
],
//
showCollapseButton: true,
//
submitOnEnter: true,
};
const gridOptions: VxeGridProps<OpenIddictTokenDto> = {
columns: [
{
align: 'left',
field: 'applicationId',
minWidth: 300,
title: $t('AbpOpenIddict.DisplayName:ApplicationId'),
},
{
align: 'left',
field: 'subject',
minWidth: 300,
title: $t('AbpOpenIddict.DisplayName:Subject'),
},
{
align: 'left',
field: 'type',
minWidth: 150,
title: $t('AbpOpenIddict.DisplayName:Type'),
},
{
align: 'left',
field: 'status',
minWidth: 150,
title: $t('AbpOpenIddict.DisplayName:Status'),
},
{
align: 'left',
field: 'creationDate',
formatter: ({ cellValue }) => {
return cellValue ? formatToDateTime(cellValue) : cellValue;
},
minWidth: 200,
title: $t('AbpOpenIddict.DisplayName:CreationDate'),
},
{
align: 'left',
field: 'expirationDate',
formatter: ({ cellValue }) => {
return cellValue ? formatToDateTime(cellValue) : cellValue;
},
minWidth: 200,
title: $t('AbpOpenIddict.DisplayName:ExpirationDate'),
},
{
field: 'action',
fixed: 'right',
slots: { default: 'action' },
title: $t('AbpUi.Actions'),
visible: hasAccessByCodes([TokensPermissions.Delete]),
width: 220,
},
],
exportConfig: {},
keepSource: true,
proxyConfig: {
ajax: {
query: async ({ page }, formValues) => {
return await getPagedListApi({
maxResultCount: page.pageSize,
skipCount: (page.currentPage - 1) * page.pageSize,
...formValues,
});
},
},
response: {
total: 'totalCount',
list: 'items',
},
},
toolbarConfig: {
custom: true,
export: true,
// import: true,
refresh: true,
zoom: true,
},
};
const [TokenModal, modalApi] = useVbenModal({
connectedComponent: defineAsyncComponent(() => import('./TokenModal.vue')),
});
const [Grid, gridApi] = useVbenVxeGrid({
formOptions,
gridOptions,
});
const onSearchClient = debounce(async (filter?: string) => {
const { items } = await getApplications({
filter,
maxResultCount: 25,
});
applications.value = items;
}, 500);
function onChangeClient(value?: SelectValue) {
gridApi.formApi.setFieldValue('clientId', value);
}
function onUpdate(row: OpenIddictTokenDto) {
modalApi.setData(row);
modalApi.open();
}
function onDelete(row: OpenIddictTokenDto) {
Modal.confirm({
centered: true,
content: `${$t('AbpUi.ItemWillBeDeletedMessage')}`,
onOk: () => {
return deleteApi(row.id).then(() => {
message.success($t('AbpUi.SuccessfullyDeleted'));
gridApi.query();
});
},
title: $t('AbpUi.AreYouSure'),
});
}
function onFormReset() {
gridApi.formApi.resetForm();
gridApi.formApi.submitForm();
}
onMounted(onSearchClient);
</script>
<template>
<Grid :table-title="$t('AbpOpenIddict.Tokens')">
<template #form-clientId="{ modelValue }">
<Select
:default-active-first-option="false"
:field-names="{ label: 'clientId', value: 'id' }"
:filter-option="false"
:options="applications"
:placeholder="$t('ui.placeholder.select')"
:value="modelValue"
allow-clear
class="w-full"
show-search
@change="onChangeClient"
@search="onSearchClient"
/>
</template>
<template #required="{ row }">
<div class="flex flex-row justify-center">
<CheckIcon v-if="row.required" class="text-green-500" />
<CloseIcon v-else class="text-red-500" />
</div>
</template>
<template #static="{ row }">
<div class="flex flex-row justify-center">
<CheckIcon v-if="row.isStatic" class="text-green-500" />
<CloseIcon v-else class="text-red-500" />
</div>
</template>
<template #action="{ row }">
<div class="flex flex-row">
<div class="basis-1/2">
<Button
:icon="h(EditOutlined)"
block
type="link"
@click="onUpdate(row)"
>
{{ $t('AbpUi.Edit') }}
</Button>
</div>
<div class="basis-1/2">
<Button
:icon="h(DeleteOutlined)"
block
danger
type="link"
v-access:code="[TokensPermissions.Delete]"
@click="onDelete(row)"
>
{{ $t('AbpUi.Delete') }}
</Button>
</div>
</div>
</template>
</Grid>
<TokenModal @change="() => gridApi.query()" />
</template>
<style lang="scss" scoped></style>
Loading…
Cancel
Save