committed by
GitHub
23 changed files with 940 additions and 40 deletions
@ -0,0 +1 @@ |
|||||
|
export { default as groupBy } from 'lodash.groupby'; |
||||
@ -1,3 +1,5 @@ |
|||||
export { default as FeatureDefinitionTable } from './definitions/features/FeatureDefinitionTable.vue'; |
export { default as FeatureDefinitionTable } from './definitions/features/FeatureDefinitionTable.vue'; |
||||
export { default as FeatureGroupDefinitionTable } from './definitions/groups/FeatureGroupDefinitionTable.vue'; |
export { default as FeatureGroupDefinitionTable } from './definitions/groups/FeatureGroupDefinitionTable.vue'; |
||||
export { default as FeatureModal } from './features/FeatureModal.vue'; |
export { default as FeatureModal } from './features/FeatureModal.vue'; |
||||
|
export { default as FeatureStateCheck } from './state-check/FeatureStateCheck.vue'; |
||||
|
export { default as GlobalFeatureStateCheck } from './state-check/GlobalFeatureStateCheck.vue'; |
||||
|
|||||
@ -0,0 +1,135 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import type { |
||||
|
FeatureDefinitionDto, |
||||
|
FeatureGroupDefinitionDto, |
||||
|
} from '../../types'; |
||||
|
|
||||
|
import { computed, onMounted, ref } from 'vue'; |
||||
|
|
||||
|
import { |
||||
|
listToTree, |
||||
|
useLocalization, |
||||
|
useLocalizationSerializer, |
||||
|
} from '@abp/core'; |
||||
|
import { Checkbox, FormItemRest, TreeSelect } from 'ant-design-vue'; |
||||
|
|
||||
|
import { useFeatureDefinitionsApi } from '../../api/useFeatureDefinitionsApi'; |
||||
|
import { useFeatureGroupDefinitionsApi } from '../../api/useFeatureGroupDefinitionsApi'; |
||||
|
|
||||
|
interface FeatureTreeData { |
||||
|
checkable?: boolean; |
||||
|
children: FeatureTreeData[]; |
||||
|
displayName: string; |
||||
|
groupName: string; |
||||
|
name: string; |
||||
|
} |
||||
|
|
||||
|
const emits = defineEmits(['blur', 'change']); |
||||
|
|
||||
|
const { getListApi: getFeaturesApi } = useFeatureDefinitionsApi(); |
||||
|
const { getListApi: getFeatureGroupsApi } = useFeatureGroupDefinitionsApi(); |
||||
|
const { deserialize } = useLocalizationSerializer(); |
||||
|
const { Lr } = useLocalization(); |
||||
|
|
||||
|
const features = ref<FeatureDefinitionDto[]>([]); |
||||
|
const featureGroups = ref<FeatureGroupDefinitionDto[]>([]); |
||||
|
const modelValue = defineModel<{ |
||||
|
featureNames: string[]; |
||||
|
requiresAll: boolean; |
||||
|
}>({ |
||||
|
default: { |
||||
|
featureNames: [], |
||||
|
requiresAll: false, |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
const getFeatureOptions = computed(() => { |
||||
|
const featureOptions: FeatureTreeData[] = []; |
||||
|
featureGroups.value.forEach((group) => { |
||||
|
featureOptions.push({ |
||||
|
checkable: false, |
||||
|
displayName: group.displayName, |
||||
|
groupName: group.name, |
||||
|
name: group.name, |
||||
|
children: listToTree( |
||||
|
features.value.filter((x) => x.groupName === group.name), |
||||
|
{ |
||||
|
id: 'name', |
||||
|
pid: 'parentName', |
||||
|
}, |
||||
|
), |
||||
|
}); |
||||
|
}); |
||||
|
return featureOptions; |
||||
|
}); |
||||
|
const getRequiredFeatures = computed(() => { |
||||
|
const featureNames = modelValue.value?.featureNames ?? []; |
||||
|
const requiredFeatures = features.value |
||||
|
.filter((feature) => featureNames.includes(feature.name)) |
||||
|
.map((feature) => { |
||||
|
return { |
||||
|
label: feature.displayName, |
||||
|
value: feature.name, |
||||
|
}; |
||||
|
}); |
||||
|
return requiredFeatures; |
||||
|
}); |
||||
|
|
||||
|
function onChange() { |
||||
|
emits('change', modelValue.value); |
||||
|
} |
||||
|
|
||||
|
function onFeaturesChange(value: any[]) { |
||||
|
modelValue.value.featureNames = value.map((item) => item.value); |
||||
|
emits('change', modelValue.value); |
||||
|
} |
||||
|
|
||||
|
async function onInit() { |
||||
|
const [featureGroupRes, featuresRes] = await Promise.all([ |
||||
|
getFeatureGroupsApi(), |
||||
|
getFeaturesApi(), |
||||
|
]); |
||||
|
featureGroups.value = featureGroupRes.items.map((item) => { |
||||
|
const displayName = deserialize(item.displayName); |
||||
|
return { |
||||
|
...item, |
||||
|
displayName: Lr(displayName.resourceName, displayName.name), |
||||
|
}; |
||||
|
}); |
||||
|
features.value = featuresRes.items.map((item) => { |
||||
|
const displayName = deserialize(item.displayName); |
||||
|
return { |
||||
|
...item, |
||||
|
displayName: Lr(displayName.resourceName, displayName.name), |
||||
|
}; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
onMounted(onInit); |
||||
|
</script> |
||||
|
|
||||
|
<template> |
||||
|
<div class="flex w-full flex-col gap-5"> |
||||
|
<FormItemRest> |
||||
|
<Checkbox |
||||
|
v-model:checked="modelValue.requiresAll" |
||||
|
@blur="emits('blur')" |
||||
|
@change="onChange" |
||||
|
> |
||||
|
{{ $t('component.simple_state_checking.requireFeatures.requiresAll') }} |
||||
|
</Checkbox> |
||||
|
<TreeSelect |
||||
|
:tree-data="getFeatureOptions" |
||||
|
allow-clear |
||||
|
tree-checkable |
||||
|
tree-check-strictly |
||||
|
:field-names="{ label: 'displayName', value: 'name' }" |
||||
|
:value="getRequiredFeatures" |
||||
|
@blur="emits('blur')" |
||||
|
@change="onFeaturesChange" |
||||
|
/> |
||||
|
</FormItemRest> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<style scoped></style> |
||||
@ -0,0 +1,65 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import { computed } from 'vue'; |
||||
|
|
||||
|
import { useAbpStore } from '@abp/core'; |
||||
|
import { Checkbox, FormItemRest, Select } from 'ant-design-vue'; |
||||
|
|
||||
|
const emits = defineEmits(['blur', 'change']); |
||||
|
|
||||
|
const modelValue = defineModel<{ |
||||
|
globalFeatureNames: string[]; |
||||
|
requiresAll: boolean; |
||||
|
}>({ |
||||
|
default: { |
||||
|
globalFeatureNames: [], |
||||
|
requiresAll: false, |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
const abpStore = useAbpStore(); |
||||
|
|
||||
|
const getFeatureOptions = computed(() => { |
||||
|
if (!abpStore.application?.globalFeatures) { |
||||
|
return []; |
||||
|
} |
||||
|
return abpStore.application.globalFeatures.enabledFeatures.map((feature) => { |
||||
|
return { |
||||
|
label: feature, |
||||
|
value: feature, |
||||
|
}; |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
function onChange() { |
||||
|
emits('change', modelValue.value); |
||||
|
} |
||||
|
|
||||
|
function onFeaturesChange(value: any) { |
||||
|
modelValue.value.globalFeatureNames = value; |
||||
|
emits('change', modelValue.value); |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<template> |
||||
|
<div class="flex w-full flex-col gap-5"> |
||||
|
<FormItemRest> |
||||
|
<Checkbox |
||||
|
v-model:checked="modelValue.requiresAll" |
||||
|
@blur="emits('blur')" |
||||
|
@change="onChange" |
||||
|
> |
||||
|
{{ $t('component.simple_state_checking.requireFeatures.requiresAll') }} |
||||
|
</Checkbox> |
||||
|
<Select |
||||
|
:value="modelValue.globalFeatureNames" |
||||
|
:options="getFeatureOptions" |
||||
|
show-search |
||||
|
mode="tags" |
||||
|
@blur="emits('blur')" |
||||
|
@change="onFeaturesChange" |
||||
|
/> |
||||
|
</FormItemRest> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<style scoped></style> |
||||
@ -1,3 +1,4 @@ |
|||||
export { default as PermissionGroupDefinitionTable } from './definitions/groups/PermissionGroupDefinitionTable.vue'; |
export { default as PermissionGroupDefinitionTable } from './definitions/groups/PermissionGroupDefinitionTable.vue'; |
||||
export { default as PermissionDefinitionTable } from './definitions/permissions/PermissionDefinitionTable.vue'; |
export { default as PermissionDefinitionTable } from './definitions/permissions/PermissionDefinitionTable.vue'; |
||||
export { default as PermissionModal } from './permissions/PermissionModal.vue'; |
export { default as PermissionModal } from './permissions/PermissionModal.vue'; |
||||
|
export { default as PermissionStateCheck } from './state-check/PermissionStateCheck.vue'; |
||||
|
|||||
@ -0,0 +1,134 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import type { PermissionDefinitionDto } from '../../types/definitions'; |
||||
|
import type { PermissionGroupDefinitionDto } from '../../types/groups'; |
||||
|
|
||||
|
import { computed, onMounted, ref } from 'vue'; |
||||
|
|
||||
|
import { |
||||
|
listToTree, |
||||
|
useLocalization, |
||||
|
useLocalizationSerializer, |
||||
|
} from '@abp/core'; |
||||
|
import { Checkbox, FormItemRest, TreeSelect } from 'ant-design-vue'; |
||||
|
|
||||
|
import { usePermissionDefinitionsApi } from '../../api/usePermissionDefinitionsApi'; |
||||
|
import { usePermissionGroupDefinitionsApi } from '../../api/usePermissionGroupDefinitionsApi'; |
||||
|
|
||||
|
interface TreeData { |
||||
|
checkable?: boolean; |
||||
|
children: TreeData[]; |
||||
|
displayName: string; |
||||
|
groupName: string; |
||||
|
name: string; |
||||
|
} |
||||
|
|
||||
|
const emits = defineEmits(['blur', 'change']); |
||||
|
|
||||
|
const { getListApi: getPermissionsApi } = usePermissionDefinitionsApi(); |
||||
|
const { getListApi: getPermissionGroupsApi } = |
||||
|
usePermissionGroupDefinitionsApi(); |
||||
|
const { deserialize } = useLocalizationSerializer(); |
||||
|
const { Lr } = useLocalization(); |
||||
|
|
||||
|
const permissions = ref<PermissionDefinitionDto[]>([]); |
||||
|
const permissionGroups = ref<PermissionGroupDefinitionDto[]>([]); |
||||
|
const modelValue = defineModel<{ |
||||
|
permissions: string[]; |
||||
|
requiresAll: boolean; |
||||
|
}>({ |
||||
|
default: { |
||||
|
permissions: [], |
||||
|
requiresAll: false, |
||||
|
}, |
||||
|
}); |
||||
|
const getPermissionOptions = computed(() => { |
||||
|
const permissionOptions: TreeData[] = []; |
||||
|
permissionGroups.value.forEach((group) => { |
||||
|
permissionOptions.push({ |
||||
|
checkable: false, |
||||
|
displayName: group.displayName, |
||||
|
groupName: group.name, |
||||
|
name: group.name, |
||||
|
children: listToTree( |
||||
|
permissions.value.filter((x) => x.groupName === group.name), |
||||
|
{ |
||||
|
id: 'name', |
||||
|
pid: 'parentName', |
||||
|
}, |
||||
|
), |
||||
|
}); |
||||
|
}); |
||||
|
return permissionOptions; |
||||
|
}); |
||||
|
const getRequiredPermissions = computed(() => { |
||||
|
const permissionNames = modelValue.value?.permissions ?? []; |
||||
|
return permissions.value |
||||
|
.filter((permission) => permissionNames.includes(permission.name)) |
||||
|
.map((permission) => { |
||||
|
return { |
||||
|
label: permission.displayName, |
||||
|
value: permission.name, |
||||
|
}; |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
function onChange() { |
||||
|
emits('change', modelValue.value); |
||||
|
} |
||||
|
|
||||
|
function onPermissionsChange(value: any[]) { |
||||
|
modelValue.value.permissions = value.map((item) => item.value); |
||||
|
emits('change', modelValue.value); |
||||
|
} |
||||
|
|
||||
|
async function onInit() { |
||||
|
const [permissionGroupRes, permissionsRes] = await Promise.all([ |
||||
|
getPermissionGroupsApi(), |
||||
|
getPermissionsApi(), |
||||
|
]); |
||||
|
permissionGroups.value = permissionGroupRes.items.map((item) => { |
||||
|
const displayName = deserialize(item.displayName); |
||||
|
return { |
||||
|
...item, |
||||
|
displayName: Lr(displayName.resourceName, displayName.name), |
||||
|
}; |
||||
|
}); |
||||
|
permissions.value = permissionsRes.items.map((item) => { |
||||
|
const displayName = deserialize(item.displayName); |
||||
|
return { |
||||
|
...item, |
||||
|
displayName: Lr(displayName.resourceName, displayName.name), |
||||
|
}; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
onMounted(onInit); |
||||
|
</script> |
||||
|
|
||||
|
<template> |
||||
|
<div class="flex w-full flex-col gap-5"> |
||||
|
<FormItemRest> |
||||
|
<Checkbox |
||||
|
v-model:checked="modelValue.requiresAll" |
||||
|
@blur="emits('blur')" |
||||
|
@change="onChange" |
||||
|
> |
||||
|
{{ |
||||
|
$t('component.simple_state_checking.requirePermissions.requiresAll') |
||||
|
}} |
||||
|
</Checkbox> |
||||
|
<TreeSelect |
||||
|
:tree-data="getPermissionOptions" |
||||
|
allow-clear |
||||
|
tree-checkable |
||||
|
tree-check-strictly |
||||
|
:field-names="{ label: 'displayName', value: 'name' }" |
||||
|
:value="getRequiredPermissions" |
||||
|
@blur="emits('blur')" |
||||
|
@change="onPermissionsChange" |
||||
|
/> |
||||
|
</FormItemRest> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<style scoped></style> |
||||
@ -1,5 +1,6 @@ |
|||||
export { default as LocalizableInput } from './localizable-input/LocalizableInput.vue'; |
export { default as LocalizableInput } from './localizable-input/LocalizableInput.vue'; |
||||
export { default as PropertyTable } from './properties/PropertyTable.vue'; |
export { default as PropertyTable } from './properties/PropertyTable.vue'; |
||||
export * from './properties/types'; |
export * from './properties/types'; |
||||
|
export * from './simple-state-checking'; |
||||
export * from './string-value-type'; |
export * from './string-value-type'; |
||||
export type * from './vxe-table'; |
export type * from './vxe-table'; |
||||
|
|||||
@ -0,0 +1,265 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import type { ColumnsType } from 'ant-design-vue/lib/table'; |
||||
|
|
||||
|
import type { PropType } from 'vue'; |
||||
|
|
||||
|
import type { SimplaCheckStateBase } from './interface'; |
||||
|
|
||||
|
import { computed, reactive, unref } from 'vue'; |
||||
|
|
||||
|
import { useVbenModal } from '@vben/common-ui'; |
||||
|
import { createIconifyIcon } from '@vben/icons'; |
||||
|
import { $t } from '@vben/locales'; |
||||
|
|
||||
|
import { isNullOrUnDef, useSimpleStateCheck } from '@abp/core'; |
||||
|
import { Button, Card, Col, Row, Table, Tag } from 'ant-design-vue'; |
||||
|
|
||||
|
import SimpleStateCheckingModal from './SimpleStateCheckingModal.vue'; |
||||
|
|
||||
|
const props = defineProps({ |
||||
|
allowDelete: { |
||||
|
default: false, |
||||
|
type: Boolean, |
||||
|
}, |
||||
|
allowEdit: { |
||||
|
default: false, |
||||
|
type: Boolean, |
||||
|
}, |
||||
|
disabled: { |
||||
|
default: false, |
||||
|
type: Boolean, |
||||
|
}, |
||||
|
state: { |
||||
|
required: true, |
||||
|
type: Object as PropType<SimplaCheckStateBase>, |
||||
|
}, |
||||
|
value: { |
||||
|
default: '', |
||||
|
type: String, |
||||
|
}, |
||||
|
}); |
||||
|
const emits = defineEmits(['change', 'update:value']); |
||||
|
const DeleteOutlined = createIconifyIcon('ant-design:delete-outlined'); |
||||
|
const EditOutlined = createIconifyIcon('ant-design:edit-outlined'); |
||||
|
|
||||
|
const simpleCheckerMap = reactive<{ [key: string]: string }>({ |
||||
|
A: $t('component.simple_state_checking.requireAuthenticated.title'), |
||||
|
F: $t('component.simple_state_checking.requireFeatures.title'), |
||||
|
G: $t('component.simple_state_checking.requireGlobalFeatures.title'), |
||||
|
P: $t('component.simple_state_checking.requirePermissions.title'), |
||||
|
}); |
||||
|
|
||||
|
const { deserialize, deserializeArray, serializeArray } = useSimpleStateCheck(); |
||||
|
|
||||
|
const [Modal, modalApi] = useVbenModal({ |
||||
|
connectedComponent: SimpleStateCheckingModal, |
||||
|
}); |
||||
|
|
||||
|
const getTableColumns = computed(() => { |
||||
|
const columns: ColumnsType = [ |
||||
|
{ |
||||
|
align: 'left', |
||||
|
dataIndex: 'name', |
||||
|
fixed: 'left', |
||||
|
key: 'name', |
||||
|
maxWidth: 150, |
||||
|
title: $t('component.simple_state_checking.table.name'), |
||||
|
}, |
||||
|
{ |
||||
|
align: 'left', |
||||
|
dataIndex: 'properties', |
||||
|
fixed: 'left', |
||||
|
key: 'properties', |
||||
|
title: $t('component.simple_state_checking.table.properties'), |
||||
|
}, |
||||
|
]; |
||||
|
return [ |
||||
|
...columns, |
||||
|
...(props.disabled |
||||
|
? [] |
||||
|
: [ |
||||
|
{ |
||||
|
align: 'center', |
||||
|
dataIndex: 'action', |
||||
|
fixed: 'right', |
||||
|
key: 'action', |
||||
|
title: $t('component.simple_state_checking.table.actions'), |
||||
|
width: 180, |
||||
|
}, |
||||
|
]), |
||||
|
]; |
||||
|
}); |
||||
|
const getSimpleCheckers = computed(() => { |
||||
|
if (isNullOrUnDef(props.value) || props.value.length === 0) { |
||||
|
return []; |
||||
|
} |
||||
|
const simpleCheckers = deserializeArray(props.value, props.state); |
||||
|
return simpleCheckers; |
||||
|
}); |
||||
|
const getStateCheckerOptions = computed(() => { |
||||
|
const stateCheckers = unref(getSimpleCheckers); |
||||
|
return Object.keys(simpleCheckerMap).map((key) => { |
||||
|
return { |
||||
|
disabled: stateCheckers.some((x) => (x as any).name === key), |
||||
|
label: simpleCheckerMap[key], |
||||
|
value: key, |
||||
|
}; |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
function handleAddNew() { |
||||
|
modalApi.setData({ |
||||
|
options: getStateCheckerOptions.value, |
||||
|
}); |
||||
|
modalApi.open(); |
||||
|
} |
||||
|
|
||||
|
function handleEdit(record: any) { |
||||
|
modalApi.setData({ |
||||
|
options: getStateCheckerOptions.value, |
||||
|
record, |
||||
|
}); |
||||
|
modalApi.open(); |
||||
|
} |
||||
|
|
||||
|
function onChange(record: any) { |
||||
|
const stateChecker = deserialize(record, props.state)!; |
||||
|
const stateCheckers = unref(getSimpleCheckers); |
||||
|
const inputIndex = stateCheckers.findIndex( |
||||
|
(x) => (x as any).name === record.name, |
||||
|
); |
||||
|
if (inputIndex === -1) { |
||||
|
stateCheckers.push(stateChecker); |
||||
|
} else { |
||||
|
stateCheckers[inputIndex] = stateChecker; |
||||
|
} |
||||
|
const updateValue = serializeArray(stateCheckers); |
||||
|
|
||||
|
emits('change', updateValue); |
||||
|
emits('update:value', updateValue); |
||||
|
} |
||||
|
|
||||
|
function handleDelete(record: any) { |
||||
|
const stateCheckers = unref(getSimpleCheckers); |
||||
|
const filtedStateCheckers = stateCheckers.filter( |
||||
|
(x) => (x as any).name !== record.name, |
||||
|
); |
||||
|
if (filtedStateCheckers.length === 0) { |
||||
|
handleClean(); |
||||
|
return; |
||||
|
} |
||||
|
const serializedCheckers = serializeArray(filtedStateCheckers); |
||||
|
emits('change', serializedCheckers); |
||||
|
emits('update:value', serializedCheckers); |
||||
|
} |
||||
|
|
||||
|
function handleClean() { |
||||
|
emits('change', undefined); |
||||
|
emits('update:value', undefined); |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<template> |
||||
|
<div class="w-full"> |
||||
|
<div class="card"> |
||||
|
<Card> |
||||
|
<template #title> |
||||
|
<Row> |
||||
|
<Col :span="12"> |
||||
|
<span>{{ $t('component.simple_state_checking.title') }}</span> |
||||
|
</Col> |
||||
|
<Col :span="12"> |
||||
|
<div class="toolbar" v-if="!props.disabled"> |
||||
|
<Button type="primary" @click="handleAddNew"> |
||||
|
{{ $t('component.simple_state_checking.actions.create') }} |
||||
|
</Button> |
||||
|
<Button danger @click="handleClean"> |
||||
|
{{ $t('component.simple_state_checking.actions.clean') }} |
||||
|
</Button> |
||||
|
</div> |
||||
|
</Col> |
||||
|
</Row> |
||||
|
</template> |
||||
|
<Table :columns="getTableColumns" :data-source="getSimpleCheckers"> |
||||
|
<template #bodyCell="{ column, record }"> |
||||
|
<template v-if="column.key === 'name'"> |
||||
|
<span>{{ simpleCheckerMap[record.name] }}</span> |
||||
|
</template> |
||||
|
<template v-else-if="column.key === 'properties'"> |
||||
|
<div v-if="record.name === 'F'"> |
||||
|
<Tag v-for="feature in record.featureNames" :key="feature"> |
||||
|
{{ feature }} |
||||
|
</Tag> |
||||
|
</div> |
||||
|
<div v-else-if="record.name === 'G'"> |
||||
|
<Tag |
||||
|
v-for="feature in record.globalFeatureNames" |
||||
|
:key="feature" |
||||
|
> |
||||
|
{{ feature }} |
||||
|
</Tag> |
||||
|
</div> |
||||
|
<div v-else-if="record.name === 'P'"> |
||||
|
<Tag |
||||
|
v-for="permission in record.model.permissions" |
||||
|
:key="permission" |
||||
|
> |
||||
|
{{ permission }} |
||||
|
</Tag> |
||||
|
</div> |
||||
|
<div v-else-if="record.name === 'A'"> |
||||
|
<span>{{ |
||||
|
$t( |
||||
|
'component.simple_state_checking.requireAuthenticated.title', |
||||
|
) |
||||
|
}}</span> |
||||
|
</div> |
||||
|
</template> |
||||
|
<template v-else-if="column.key === 'action'"> |
||||
|
<div class="flex flex-row"> |
||||
|
<Button |
||||
|
v-if="props.allowEdit" |
||||
|
type="link" |
||||
|
@click="() => handleEdit(record)" |
||||
|
> |
||||
|
<template #icon> |
||||
|
<EditOutlined /> |
||||
|
</template> |
||||
|
{{ $t('component.simple_state_checking.actions.update') }} |
||||
|
</Button> |
||||
|
<Button |
||||
|
v-if="props.allowDelete" |
||||
|
type="link" |
||||
|
@click="() => handleDelete(record)" |
||||
|
danger |
||||
|
> |
||||
|
<template #icon> |
||||
|
<DeleteOutlined /> |
||||
|
</template> |
||||
|
{{ $t('component.simple_state_checking.actions.delete') }} |
||||
|
</Button> |
||||
|
</div> |
||||
|
</template> |
||||
|
</template> |
||||
|
</Table> |
||||
|
</Card> |
||||
|
</div> |
||||
|
</div> |
||||
|
<Modal @change="onChange" /> |
||||
|
</template> |
||||
|
|
||||
|
<style lang="less" scoped> |
||||
|
.card { |
||||
|
.toolbar { |
||||
|
flex: 1; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: flex-end; |
||||
|
margin-bottom: 8px; |
||||
|
|
||||
|
> * { |
||||
|
margin-right: 8px; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,231 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import { toRaw } from 'vue'; |
||||
|
|
||||
|
import { useVbenForm, useVbenModal, z } from '@vben/common-ui'; |
||||
|
import { $t } from '@vben/locales'; |
||||
|
|
||||
|
interface ModalState { |
||||
|
options: { disabled?: boolean; label: string; value: string }[]; |
||||
|
record: any; |
||||
|
} |
||||
|
|
||||
|
const emits = defineEmits(['change']); |
||||
|
const [Modal, modalApi] = useVbenModal({ |
||||
|
closeOnClickModal: false, |
||||
|
closeOnPressEscape: false, |
||||
|
onConfirm: onSubmit, |
||||
|
onOpenChange(isOpen) { |
||||
|
if (isOpen) { |
||||
|
onInit(); |
||||
|
} |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
const [Form, formApi] = useVbenForm({ |
||||
|
schema: [ |
||||
|
{ |
||||
|
component: 'Select', |
||||
|
fieldName: 'name', |
||||
|
label: $t('component.simple_state_checking.form.name'), |
||||
|
rules: 'selectRequired', |
||||
|
}, |
||||
|
{ |
||||
|
component: 'Empty', |
||||
|
disabledOnChangeListener: false, |
||||
|
fieldName: 'value', |
||||
|
label: '', |
||||
|
}, |
||||
|
], |
||||
|
showDefaultActions: false, |
||||
|
}); |
||||
|
|
||||
|
async function onInit() { |
||||
|
const state = modalApi.getData<ModalState>(); |
||||
|
formApi.updateSchema([ |
||||
|
{ |
||||
|
componentProps: { |
||||
|
disabled: !!state.record, |
||||
|
onChange, |
||||
|
options: state.options, |
||||
|
}, |
||||
|
fieldName: 'name', |
||||
|
}, |
||||
|
{ |
||||
|
component: 'Empty', |
||||
|
fieldName: 'value', |
||||
|
label: '', |
||||
|
rules: z.object({}).optional(), |
||||
|
}, |
||||
|
]); |
||||
|
formApi.setValues({ |
||||
|
name: state.record?.name, |
||||
|
value: {}, |
||||
|
}); |
||||
|
if (state.record) { |
||||
|
onRecordChange(state.record); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
async function onRecordChange(record: any) { |
||||
|
await formApi.setFieldValue('name', record.name); |
||||
|
onChange(record.name); |
||||
|
switch (record.name) { |
||||
|
case 'F': { |
||||
|
await formApi.setFieldValue('value', { |
||||
|
featureNames: record.featureNames, |
||||
|
requiresAll: record.requiresAll, |
||||
|
}); |
||||
|
break; |
||||
|
} |
||||
|
case 'G': { |
||||
|
await formApi.setFieldValue('value', { |
||||
|
globalFeatureNames: record.globalFeatureNames, |
||||
|
requiresAll: record.requiresAll, |
||||
|
}); |
||||
|
break; |
||||
|
} |
||||
|
case 'P': { |
||||
|
await formApi.setFieldValue('value', { |
||||
|
permissions: record.permissions, |
||||
|
requiresAll: record.requiresAll, |
||||
|
}); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function onChange(value: string) { |
||||
|
formApi.updateSchema([ |
||||
|
{ |
||||
|
component: 'Empty', |
||||
|
controlClass: '', |
||||
|
fieldName: 'value', |
||||
|
label: '', |
||||
|
rules: z.object({}).optional(), |
||||
|
}, |
||||
|
]); |
||||
|
formApi.setFieldValue('value', {}); |
||||
|
switch (value) { |
||||
|
case 'A': { |
||||
|
formApi.updateSchema([ |
||||
|
{ |
||||
|
controlClass: 'invisible', |
||||
|
fieldName: 'value', |
||||
|
}, |
||||
|
]); |
||||
|
break; |
||||
|
} |
||||
|
case 'F': { |
||||
|
formApi.updateSchema([ |
||||
|
{ |
||||
|
component: 'FeatureStateCheck', |
||||
|
fieldName: 'value', |
||||
|
label: $t( |
||||
|
'component.simple_state_checking.requireFeatures.featureNames', |
||||
|
), |
||||
|
// TODO: vben应该做到用户能自定义验证器, 在组件的适配器中而不是单独使用时 |
||||
|
rules: z |
||||
|
.object({ |
||||
|
featureNames: z.array(z.string()), |
||||
|
requiresAll: z.boolean().optional(), |
||||
|
}) |
||||
|
.refine((v) => v.featureNames.length > 0, { |
||||
|
message: `${$t('ui.placeholder.select')}${$t('component.simple_state_checking.requireFeatures.featureNames')}`, |
||||
|
}), |
||||
|
}, |
||||
|
]); |
||||
|
formApi.setFieldValue('value', { |
||||
|
featureNames: [], |
||||
|
requiresAll: false, |
||||
|
}); |
||||
|
break; |
||||
|
} |
||||
|
case 'G': { |
||||
|
formApi.updateSchema([ |
||||
|
{ |
||||
|
component: 'GlobalFeatureStateCheck', |
||||
|
fieldName: 'value', |
||||
|
label: $t( |
||||
|
'component.simple_state_checking.requireFeatures.featureNames', |
||||
|
), |
||||
|
// TODO: vben应该做到用户能自定义验证器, 在组件的适配器中而不是单独使用时 |
||||
|
rules: z |
||||
|
.object({ |
||||
|
globalFeatureNames: z.array(z.string()), |
||||
|
requiresAll: z.boolean().optional(), |
||||
|
}) |
||||
|
.refine((v) => v.globalFeatureNames.length > 0, { |
||||
|
message: `${$t('ui.placeholder.select')}${$t('component.simple_state_checking.requireFeatures.featureNames')}`, |
||||
|
}), |
||||
|
}, |
||||
|
]); |
||||
|
formApi.setFieldValue('value', { |
||||
|
globalFeatureNames: [], |
||||
|
requiresAll: false, |
||||
|
}); |
||||
|
break; |
||||
|
} |
||||
|
case 'P': { |
||||
|
formApi.updateSchema([ |
||||
|
{ |
||||
|
component: 'PermissionStateCheck', |
||||
|
fieldName: 'value', |
||||
|
label: $t( |
||||
|
'component.simple_state_checking.requirePermissions.permissions', |
||||
|
), |
||||
|
// TODO: vben应该做到用户能自定义验证器, 在组件的适配器中而不是单独使用时 |
||||
|
rules: z |
||||
|
.object({ |
||||
|
permissions: z.array(z.string()), |
||||
|
requiresAll: z.boolean().optional(), |
||||
|
}) |
||||
|
.refine((v) => v.permissions.length > 0, { |
||||
|
message: `${$t('ui.placeholder.select')}${$t('component.simple_state_checking.requirePermissions.permissions')}`, |
||||
|
}), |
||||
|
}, |
||||
|
]); |
||||
|
formApi.setFieldValue('value', { |
||||
|
permissions: [], |
||||
|
requiresAll: false, |
||||
|
}); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
async function onSubmit() { |
||||
|
const result = await formApi.validate(); |
||||
|
if (result.valid) { |
||||
|
const values = await formApi.getValues(); |
||||
|
const value = toRaw(values.value); |
||||
|
const stateChecker: { [key: string]: any } = { |
||||
|
A: value.requiresAll, |
||||
|
T: values.name, |
||||
|
}; |
||||
|
switch (values.name) { |
||||
|
case 'F': { |
||||
|
stateChecker.N = value.featureNames; |
||||
|
break; |
||||
|
} |
||||
|
case 'G': { |
||||
|
stateChecker.N = value.globalFeatureNames; |
||||
|
break; |
||||
|
} |
||||
|
case 'P': { |
||||
|
stateChecker.N = value.permissions; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
emits('change', stateChecker); |
||||
|
modalApi.close(); |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<template> |
||||
|
<Modal :title="$t('component.simple_state_checking.title')"> |
||||
|
<Form /> |
||||
|
</Modal> |
||||
|
</template> |
||||
|
|
||||
|
<style scoped></style> |
||||
@ -0,0 +1,2 @@ |
|||||
|
export * from './interface'; |
||||
|
export { default as SimpleStateChecking } from './SimpleStateChecking.vue'; |
||||
@ -0,0 +1,6 @@ |
|||||
|
import type { IHasSimpleStateCheckers, ISimpleStateChecker } from '@abp/core'; |
||||
|
|
||||
|
export interface SimplaCheckStateBase |
||||
|
extends IHasSimpleStateCheckers<SimplaCheckStateBase> { |
||||
|
stateCheckers: ISimpleStateChecker<SimplaCheckStateBase>[]; |
||||
|
} |
||||
Loading…
Reference in new issue