committed by
GitHub
40 changed files with 999 additions and 270 deletions
@ -0,0 +1,67 @@ |
|||||
|
<template> |
||||
|
<BasicModal |
||||
|
:title="L('ManageSecret')" |
||||
|
@register="registerModal" |
||||
|
@ok="handleSubmit" |
||||
|
> |
||||
|
<BasicForm @register="registerForm" /> |
||||
|
</BasicModal> |
||||
|
</template> |
||||
|
|
||||
|
<script setup lang="ts"> |
||||
|
import { reactive } from 'vue'; |
||||
|
import { BasicForm, useForm } from '/@/components/Form'; |
||||
|
import { BasicModal, useModalInner } from '/@/components/Modal'; |
||||
|
import { useLocalization } from '/@/hooks/abp/useLocalization'; |
||||
|
import { useMessage } from '/@/hooks/web/useMessage'; |
||||
|
import { UpdateAsyncByIdAndInput } from '/@/api/openiddict/open-iddict-application'; |
||||
|
import { OpenIddictApplicationUpdateDto } from '/@/api/openiddict/open-iddict-application/model'; |
||||
|
|
||||
|
const state = reactive<{ |
||||
|
application: Recordable |
||||
|
}>({ |
||||
|
application: {}, |
||||
|
}); |
||||
|
const emits = defineEmits(['change', 'register']); |
||||
|
const { createMessage } = useMessage(); |
||||
|
const { L } = useLocalization(['AbpOpenIddict', 'AbpUi']); |
||||
|
const [registerForm, { validate, resetFields }] = useForm({ |
||||
|
layout: 'vertical', |
||||
|
showActionButtonGroup: false, |
||||
|
schemas: [ |
||||
|
{ |
||||
|
field: 'clientSecret', |
||||
|
component: 'Input', |
||||
|
label: L('DisplayName:ClientSecret'), |
||||
|
colProps: { span: 24 }, |
||||
|
required: true, |
||||
|
}, |
||||
|
], |
||||
|
}); |
||||
|
const [registerModal, { changeOkLoading, closeModal }] = useModalInner((data) => { |
||||
|
state.application = data; |
||||
|
}); |
||||
|
|
||||
|
function handleSubmit() { |
||||
|
validate().then((input) => { |
||||
|
changeOkLoading(true); |
||||
|
UpdateAsyncByIdAndInput( |
||||
|
state.application.id, |
||||
|
{ |
||||
|
...state.application as OpenIddictApplicationUpdateDto, |
||||
|
clientSecret: input.clientSecret |
||||
|
}).then((dto) => { |
||||
|
createMessage.success(L('Successful')); |
||||
|
resetFields(); |
||||
|
emits('change', dto); |
||||
|
closeModal(); |
||||
|
}).finally(() => { |
||||
|
changeOkLoading(false); |
||||
|
}); |
||||
|
}); |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
|
||||
|
</style> |
||||
@ -1,10 +1,272 @@ |
|||||
<template> |
<template> |
||||
<div> |
<BasicModal |
||||
</div> |
@register="registerModal" |
||||
|
:title="L('Scopes')" |
||||
|
:can-fullscreen="false" |
||||
|
:width="800" |
||||
|
:height="500" |
||||
|
:close-func="handleBeforeClose" |
||||
|
@ok="handleSubmit" |
||||
|
> |
||||
|
<Form |
||||
|
ref="formRef" |
||||
|
:model="state.formModel" |
||||
|
:rules="state.formRules" |
||||
|
:label-col="{ span: 6 }" |
||||
|
:wrapper-col="{ span: 16 }" |
||||
|
> |
||||
|
<Tabs v-model:active-key="state.activeTab"> |
||||
|
<!-- Basic --> |
||||
|
<TabPane key="basic" :tab="L('BasicInfo')"> |
||||
|
<FormItem name="name" :label="L('DisplayName:Name')"> |
||||
|
<Input v-model:value="state.formModel.name" /> |
||||
|
</FormItem> |
||||
|
</TabPane> |
||||
|
<!-- DisplayName --> |
||||
|
<TabPane key="description" :tab="L('Descriptions')"> |
||||
|
<FormItem name="description" :label="L('DisplayName:DefaultDescription')"> |
||||
|
<Input v-model:value="state.formModel.description" /> |
||||
|
</FormItem> |
||||
|
<DisplayNameForm |
||||
|
:displayNames="state.formModel.descriptions" |
||||
|
@create="handleNewDescription" |
||||
|
@delete="handleDelDescription" |
||||
|
/> |
||||
|
</TabPane> |
||||
|
<!-- DisplayName --> |
||||
|
<TabPane key="displayName" :tab="L('DisplayNames')"> |
||||
|
<FormItem name="displayName" :label="L('DisplayName:DefaultDisplayName')"> |
||||
|
<Input v-model:value="state.formModel.displayName" /> |
||||
|
</FormItem> |
||||
|
<DisplayNameForm |
||||
|
:displayNames="state.formModel.displayNames" |
||||
|
@create="handleNewDisplayName" |
||||
|
@delete="handleDelDisplayName" |
||||
|
/> |
||||
|
</TabPane> |
||||
|
<!-- Resources --> |
||||
|
<TabPane key="resources" :tab="L('Resources')"> |
||||
|
<PermissionForm |
||||
|
:resources="getSupportResources" |
||||
|
:targetResources="targetResources" |
||||
|
@change="handleResourceChange" |
||||
|
/> |
||||
|
</TabPane> |
||||
|
<!-- Propertites --> |
||||
|
<TabPane key="propertites" :tab="L('Propertites')"> |
||||
|
<PropertyForm |
||||
|
:properties="state.formModel.properties" |
||||
|
@create="handleNewProperty" |
||||
|
@delete="handleDelProperty" |
||||
|
/> |
||||
|
</TabPane> |
||||
|
</Tabs> |
||||
|
</Form> |
||||
|
</BasicModal> |
||||
</template> |
</template> |
||||
|
|
||||
<script lang="ts" setup> |
<script lang="ts" setup> |
||||
</script> |
import type { FormInstance } from 'ant-design-vue/lib/form'; |
||||
|
import { cloneDeep } from 'lodash-es'; |
||||
|
import { computed, nextTick, ref, unref, reactive, watch, onMounted } from 'vue'; |
||||
|
import { Form, Input, Tabs } from 'ant-design-vue'; |
||||
|
import { BasicModal, useModalInner } from '/@/components/Modal'; |
||||
|
import { useMessage } from '/@/hooks/web/useMessage'; |
||||
|
import { useValidation } from '/@/hooks/abp/useValidation'; |
||||
|
import { useLocalization } from '/@/hooks/abp/useLocalization'; |
||||
|
import { ScopeState } from '../types/props'; |
||||
|
import { GetAsyncById, CreateAsyncByInput, UpdateAsyncByIdAndInput } from '/@/api/openiddict/open-iddict-scope'; |
||||
|
import { OpenIddictScopeDto } from '/@/api/openiddict/open-iddict-scope/model'; |
||||
|
import { discovery } from '/@/api/identity-server/identityServer'; |
||||
|
import DisplayNameForm from '../../components/DisplayNames/DisplayNameForm.vue'; |
||||
|
import PropertyForm from '../../components/Properties/PropertyForm.vue'; |
||||
|
import PermissionForm from '../../components/Permissions/PermissionForm.vue'; |
||||
|
|
||||
|
const FormItem = Form.Item; |
||||
|
const TabPane = Tabs.TabPane; |
||||
|
|
||||
|
const emits = defineEmits(['register', 'change']); |
||||
|
const { L } = useLocalization(['AbpOpenIddict', 'AbpUi']); |
||||
|
const { ruleCreator } = useValidation(); |
||||
|
const { createMessage, createConfirm } = useMessage(); |
||||
|
const formRef = ref<FormInstance>(); |
||||
|
const state = reactive<ScopeState>({ |
||||
|
activeTab: 'basic', |
||||
|
isEdit: false, |
||||
|
entityChanged: false, |
||||
|
formModel: {} as OpenIddictScopeDto, |
||||
|
formRules: { |
||||
|
name: ruleCreator.fieldRequired({ |
||||
|
name: 'Name', |
||||
|
prefix: 'DisplayName', |
||||
|
resourceName: 'AbpOpenIddict', |
||||
|
trigger: 'blur', |
||||
|
}), |
||||
|
}, |
||||
|
}); |
||||
|
watch( |
||||
|
() => state.formModel, |
||||
|
() => { |
||||
|
state.entityChanged = true; |
||||
|
}, |
||||
|
{ |
||||
|
deep: true, |
||||
|
}, |
||||
|
); |
||||
|
const targetResources = computed(() => { |
||||
|
if (!state.formModel.resources) return []; |
||||
|
const targetScopes = getSupportResources.value.filter((item) => |
||||
|
state.formModel.resources?.some((scope) => scope === item.key), |
||||
|
); |
||||
|
return targetScopes.map((item) => item.key); |
||||
|
}); |
||||
|
const getSupportResources = computed(() => { |
||||
|
if (state.openIdConfiguration?.claims_supported) { |
||||
|
const supportResources = state.openIdConfiguration?.claims_supported.map(claim => { |
||||
|
return { |
||||
|
key: claim, |
||||
|
title: claim, |
||||
|
} |
||||
|
}); |
||||
|
return supportResources; |
||||
|
} |
||||
|
return []; |
||||
|
}); |
||||
|
|
||||
|
const [registerModal, { changeLoading, changeOkLoading, closeModal }] = useModalInner((data) => { |
||||
|
nextTick(() => { |
||||
|
fetch(data?.id); |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
onMounted(initOpenidDiscovery); |
||||
|
|
||||
|
function initOpenidDiscovery() { |
||||
|
discovery().then((openIdConfiuration) => { |
||||
|
state.openIdConfiguration = openIdConfiuration; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function fetch(id?: string) { |
||||
|
state.activeTab = 'basic'; |
||||
|
state.isEdit = false; |
||||
|
state.entityChanged = false; |
||||
|
state.formModel = {} as OpenIddictScopeDto; |
||||
|
const form = unref(formRef); |
||||
|
form?.resetFields(); |
||||
|
if (!id) { |
||||
|
nextTick(() => { |
||||
|
state.isEdit = false; |
||||
|
state.entityChanged = false; |
||||
|
}); |
||||
|
return; |
||||
|
} |
||||
|
changeLoading(true); |
||||
|
GetAsyncById(id).then((scope) => { |
||||
|
state.formModel = scope; |
||||
|
nextTick(() => { |
||||
|
state.isEdit = true; |
||||
|
state.entityChanged = false; |
||||
|
}); |
||||
|
}).finally(() => { |
||||
|
changeLoading(false); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function handleResourceChange(_, direction, moveKeys: string[]) { |
||||
|
switch (direction) { |
||||
|
case 'left': |
||||
|
moveKeys.forEach((key) => { |
||||
|
const index = state.formModel.resources?.findIndex(r => r === key); |
||||
|
index && state.formModel.resources?.splice(index); |
||||
|
}); |
||||
|
break; |
||||
|
case 'right': |
||||
|
state.formModel.resources ??= []; |
||||
|
state.formModel.resources.push(...moveKeys); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
<style lang="less" scoped> |
function handleNewDisplayName(record) { |
||||
</style> |
if (!state.formModel) { |
||||
|
return; |
||||
|
} |
||||
|
state.formModel.displayNames ??= {}; |
||||
|
state.formModel.displayNames[record.culture] = record.displayName; |
||||
|
} |
||||
|
|
||||
|
function handleDelDisplayName(record) { |
||||
|
if (!state.formModel || !state.formModel.displayNames) { |
||||
|
return; |
||||
|
} |
||||
|
delete state.formModel.displayNames[record.culture]; |
||||
|
} |
||||
|
|
||||
|
function handleNewDescription(record) { |
||||
|
if (!state.formModel) { |
||||
|
return; |
||||
|
} |
||||
|
state.formModel.descriptions ??= {}; |
||||
|
state.formModel.descriptions[record.culture] = record.displayName; |
||||
|
} |
||||
|
|
||||
|
function handleDelDescription(record) { |
||||
|
if (!state.formModel || !state.formModel.descriptions) { |
||||
|
return; |
||||
|
} |
||||
|
delete state.formModel.descriptions[record.culture]; |
||||
|
} |
||||
|
|
||||
|
function handleNewProperty(record) { |
||||
|
if (!state.formModel) { |
||||
|
return; |
||||
|
} |
||||
|
state.formModel.properties ??= {}; |
||||
|
state.formModel.properties[record.key] = record.value; |
||||
|
} |
||||
|
|
||||
|
function handleDelProperty(record) { |
||||
|
if (!state.formModel || !state.formModel.properties) { |
||||
|
return; |
||||
|
} |
||||
|
delete state.formModel.properties[record.key]; |
||||
|
} |
||||
|
|
||||
|
function handleBeforeClose(): Promise<boolean> { |
||||
|
return new Promise((resolve) => { |
||||
|
if (!state.entityChanged) { |
||||
|
return resolve(true); |
||||
|
} |
||||
|
createConfirm({ |
||||
|
iconType: 'warning', |
||||
|
title: L('AreYouSure'), |
||||
|
content: L('AreYouSureYouWantToCancelEditingWarningMessage'), |
||||
|
onOk: () => { |
||||
|
resolve(true); |
||||
|
}, |
||||
|
onCancel: () => { |
||||
|
resolve(false); |
||||
|
} |
||||
|
}); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function handleSubmit() { |
||||
|
const form = unref(formRef); |
||||
|
form?.validate().then(() => { |
||||
|
changeOkLoading(true); |
||||
|
console.log(state.formModel); |
||||
|
const api = state.formModel.id |
||||
|
? UpdateAsyncByIdAndInput(state.formModel.id, cloneDeep(state.formModel)) |
||||
|
: CreateAsyncByInput(cloneDeep(state.formModel)); |
||||
|
api.then((res) => { |
||||
|
createMessage.success(L('Successful')); |
||||
|
emits('change', res); |
||||
|
closeModal(); |
||||
|
}).finally(() => { |
||||
|
changeOkLoading(false); |
||||
|
}) |
||||
|
}); |
||||
|
} |
||||
|
</script> |
||||
|
|||||
@ -1,10 +1,107 @@ |
|||||
<template> |
<template> |
||||
<div> |
<div> |
||||
|
<BasicTable @register="registerTable"> |
||||
|
<template #toolbar> |
||||
|
<Button |
||||
|
v-auth="['AbpOpenIddict.Scopes.Create']" |
||||
|
type="primary" |
||||
|
@click="handleAddNew" |
||||
|
> |
||||
|
{{ L('Scopes:AddNew') }} |
||||
|
</Button> |
||||
|
</template> |
||||
|
<template #bodyCell="{ column, record }"> |
||||
|
<template v-if="column.key === 'action'"> |
||||
|
<TableAction |
||||
|
:stop-button-propagation="true" |
||||
|
:actions="[ |
||||
|
{ |
||||
|
auth: 'AbpOpenIddict.Scopes.Update', |
||||
|
label: L('Edit'), |
||||
|
icon: 'ant-design:edit-outlined', |
||||
|
onClick: handleEdit.bind(null, record), |
||||
|
}, |
||||
|
{ |
||||
|
auth: 'AbpOpenIddict.Scopes.Delete', |
||||
|
label: L('Delete'), |
||||
|
color: 'error', |
||||
|
icon: 'ant-design:delete-outlined', |
||||
|
onClick: handleDelete.bind(null, record), |
||||
|
}, |
||||
|
]" |
||||
|
/> |
||||
|
</template> |
||||
|
</template> |
||||
|
</BasicTable> |
||||
|
<ScopeModal @register="registerModal" @change="reload" /> |
||||
</div> |
</div> |
||||
</template> |
</template> |
||||
|
|
||||
<script lang="ts" setup> |
<script lang="ts" setup> |
||||
</script> |
import { Button } from 'ant-design-vue'; |
||||
|
import { BasicTable, TableAction, useTable } from '/@/components/Table'; |
||||
|
import { getDataColumns } from '../datas/TableData'; |
||||
|
import { useModal } from '/@/components/Modal'; |
||||
|
import { useMessage } from '/@/hooks/web/useMessage'; |
||||
|
import { useLocalization } from '/@/hooks/abp/useLocalization'; |
||||
|
import { GetListAsyncByInput, DeleteAsyncById } from '/@/api/openiddict/open-iddict-scope'; |
||||
|
import { formatPagedRequest } from '/@/utils/http/abp/helper'; |
||||
|
import ScopeModal from './ScopeModal.vue'; |
||||
|
|
||||
|
const { L } = useLocalization(['AbpOpenIddict', 'AbpUi']); |
||||
|
const { createConfirm, createMessage } = useMessage(); |
||||
|
const [registerModal, { openModal }] = useModal(); |
||||
|
const [registerTable, { reload }] = useTable({ |
||||
|
rowKey: 'id', |
||||
|
title: L('Scopes'), |
||||
|
api: GetListAsyncByInput, |
||||
|
columns: getDataColumns(), |
||||
|
beforeFetch: formatPagedRequest, |
||||
|
pagination: true, |
||||
|
striped: false, |
||||
|
useSearchForm: true, |
||||
|
showIndexColumn: false, |
||||
|
showTableSetting: true, |
||||
|
formConfig: { |
||||
|
labelWidth: 100, |
||||
|
schemas: [ |
||||
|
{ |
||||
|
field: 'filter', |
||||
|
component: 'Input', |
||||
|
label: L('Search'), |
||||
|
colProps: { span: 24 }, |
||||
|
}, |
||||
|
], |
||||
|
}, |
||||
|
bordered: true, |
||||
|
canResize: true, |
||||
|
immediate: true, |
||||
|
actionColumn: { |
||||
|
width: 150, |
||||
|
title: L('Actions'), |
||||
|
dataIndex: 'action', |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
function handleAddNew() { |
||||
|
openModal(true, {}); |
||||
|
} |
||||
|
|
||||
<style lang="less" scoped> |
function handleEdit(record) { |
||||
</style> |
openModal(true, record); |
||||
|
} |
||||
|
|
||||
|
function handleDelete(record) { |
||||
|
createConfirm({ |
||||
|
iconType: 'warning', |
||||
|
title: L('AreYouSure'), |
||||
|
content: L('ItemWillBeDeletedMessage'), |
||||
|
onOk: () => { |
||||
|
return DeleteAsyncById(record.id).then(() => { |
||||
|
createMessage.success(L('SuccessfullyDeleted')); |
||||
|
reload(); |
||||
|
}); |
||||
|
}, |
||||
|
}); |
||||
|
} |
||||
|
</script> |
||||
|
|||||
@ -0,0 +1,18 @@ |
|||||
|
import { useLocalization } from '/@/hooks/abp/useLocalization'; |
||||
|
import { FormProps } from '/@/components/Form'; |
||||
|
|
||||
|
const { L } = useLocalization(['AbpOpenIddict', 'AbpUi']); |
||||
|
|
||||
|
export function getSearchFormProps(): Partial<FormProps> { |
||||
|
return { |
||||
|
labelWidth: 100, |
||||
|
schemas: [ |
||||
|
{ |
||||
|
field: 'filter', |
||||
|
component: 'Input', |
||||
|
label: L('Search'), |
||||
|
colProps: { span: 24 }, |
||||
|
}, |
||||
|
], |
||||
|
}; |
||||
|
} |
||||
@ -0,0 +1,51 @@ |
|||||
|
import { useLocalization } from '/@/hooks/abp/useLocalization'; |
||||
|
import { BasicColumn } from '/@/components/Table'; |
||||
|
import { formatToDateTime } from '/@/utils/dateUtil'; |
||||
|
|
||||
|
const { L } = useLocalization(['AbpOpenIddict', 'AbpUi']); |
||||
|
|
||||
|
export function getDataColumns(): BasicColumn[] { |
||||
|
return [ |
||||
|
{ |
||||
|
title: L('DisplayName:ConcurrencyStamp'), |
||||
|
dataIndex: 'concurrencyStamp', |
||||
|
align: 'left', |
||||
|
width: 1, |
||||
|
ifShow: false, |
||||
|
}, |
||||
|
{ |
||||
|
title: L('DisplayName:Name'), |
||||
|
dataIndex: 'name', |
||||
|
align: 'left', |
||||
|
width: 150, |
||||
|
sorter: true, |
||||
|
resizable: true, |
||||
|
}, |
||||
|
{ |
||||
|
title: L('DisplayName:DisplayName'), |
||||
|
dataIndex: 'displayName', |
||||
|
align: 'left', |
||||
|
width: 180, |
||||
|
sorter: true, |
||||
|
}, |
||||
|
{ |
||||
|
title: L('DisplayName:Description'), |
||||
|
dataIndex: 'description', |
||||
|
align: 'left', |
||||
|
width: 200, |
||||
|
sorter: true, |
||||
|
resizable: true, |
||||
|
}, |
||||
|
{ |
||||
|
title: L('DisplayName:CreationDate'), |
||||
|
dataIndex: 'creationTime', |
||||
|
align: 'left', |
||||
|
width: 200, |
||||
|
sorter: true, |
||||
|
resizable: true, |
||||
|
format: (text?: string) => { |
||||
|
return text ? formatToDateTime(text) : ''; |
||||
|
}, |
||||
|
}, |
||||
|
]; |
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
import { Rule } from 'ant-design-vue/lib/form'; |
||||
|
import { OpenIdConfiguration } from '/@/api/identity-server/model/basicModel'; |
||||
|
import { OpenIddictScopeDto } from '/@/api/openiddict/open-iddict-scope/model'; |
||||
|
|
||||
|
export interface ScopeState { |
||||
|
activeTab: string; |
||||
|
formRules?: Dictionary<string, Rule>, |
||||
|
formModel: OpenIddictScopeDto; |
||||
|
openIdConfiguration?: OpenIdConfiguration, |
||||
|
entityChanged: boolean; |
||||
|
isEdit: boolean; |
||||
|
} |
||||
Loading…
Reference in new issue