这是基于vue-vben-admin 模板适用于abp Vnext的前端管理项目
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

435 lines
15 KiB

<template>
<BasicModal
@register="registerModal"
:title="L('FeatureDefinitions')"
:can-fullscreen="false"
:width="800"
:height="500"
:close-func="handleBeforeClose"
@ok="handleSubmit"
>
<Form
ref="formRef"
:model="state.entity"
:rules="state.entityRules"
:label-col="{ span: 6 }"
:wrapper-col="{ span: 18 }"
>
<Tabs v-model:active-key="state.activeTab">
<TabPane key="basic" :tab="L('BasicInfo')">
<FormItem name="groupName" :label="L('DisplayName:GroupName')">
<Select
:disabled="!state.allowedChange"
:allow-clear="true"
v-model:value="state.entity.groupName"
:options="getGroupOptions"
@change="handleGroupChange"
/>
</FormItem>
<FormItem name="parentName" :label="L('DisplayName:ParentName')">
<TreeSelect
:disabled="!state.allowedChange"
:allow-clear="true"
:tree-data="state.availableFeatures"
v-model:value="state.entity.parentName"
:field-names="{
label: 'displayName',
value: 'name',
children: 'children',
}"
@change="handleParentChange"
/>
</FormItem>
<FormItem name="name" :label="L('DisplayName:Name')">
<Input :disabled="state.entityEditFlag && !state.allowedChange" :allow-clear="true" v-model:value="state.entity.name" />
</FormItem>
<FormItem name="displayName" :label="L('DisplayName:DisplayName')">
<LocalizableInput :disabled="!state.allowedChange" :allow-clear="true" v-model:value="state.entity.displayName" />
</FormItem>
<FormItem name="description" :label="L('DisplayName:Description')">
<LocalizableInput :disabled="!state.allowedChange" :allow-clear="true" v-model:value="state.entity.description" />
</FormItem>
<FormItem name="defaultValue" :label="L('DisplayName:DefaultValue')">
<Select
v-if="valueTypeNameRef === 'SelectionStringValueType'"
:disabled="!state.allowedChange"
:allow-clear="true"
v-model:value="state.entity.defaultValue"
:options="state.selectionDataSource"
/>
<TextArea
v-else-if="valueTypeNameRef === 'FreeTextStringValueType' && (validatorNameRef === 'NULL' || validatorNameRef === 'STRING')"
:disabled="state.entityEditFlag && !state.allowedChange"
:allow-clear="true"
:auto-size="{ minRows: 3 }"
v-model:value="state.entity.defaultValue"
/>
<InputNumber
v-else-if="valueTypeNameRef === 'FreeTextStringValueType' && validatorNameRef === 'NUMERIC'"
style="width: 100%;"
:disabled="state.entityEditFlag && !state.allowedChange"
v-model:value="state.entity.defaultValue"
/>
<Checkbox
v-else-if="valueTypeNameRef === 'ToggleStringValueType' && validatorNameRef === 'BOOLEAN'"
:disabled="state.entityEditFlag && !state.allowedChange"
:checked="state.entity.defaultValue === 'true'"
@change="(e) => state.entity.defaultValue = String(e.target.checked).toLowerCase()"
>
{{ L('DisplayName:DefaultValue') }}
</Checkbox>
</FormItem>
<FormItem name="IsVisibleToClients" :label="L('DisplayName:IsVisibleToClients')" :extra="L('Description:IsVisibleToClients')">
<Checkbox
:disabled="!state.allowedChange"
v-model:checked="state.entity.isVisibleToClients"
>{{ L('DisplayName:IsVisibleToClients') }}
</Checkbox>
</FormItem>
<FormItem name="IsAvailableToHost" :label="L('DisplayName:IsAvailableToHost')" :extra="L('Description:IsAvailableToHost')">
<Checkbox
:disabled="!state.allowedChange"
v-model:checked="state.entity.isAvailableToHost"
>{{ L('DisplayName:IsAvailableToHost') }}
</Checkbox>
</FormItem>
</TabPane>
<TabPane key="valueType" :tab="L('ValueValidator')" force-render>
<FormItem name="valueType" label="" :label-col="{ span: 0 }" :wrapper-col="{ span: 24 }">
<StringValueTypeInput
ref="valueTypeRef"
:disabled="!state.allowedChange"
:allow-delete="true"
:allow-edit="true"
v-model:value="state.entity.valueType"
@change:value-type="handleValueTypeNameChange"
@change:validator="handleValidatorNameChange"
@change:selection="handleSelectionChange"
/>
</FormItem>
</TabPane>
<TabPane key="propertites" :tab="L('Properties')" force-render>
<FormItem name="extraProperties" label="" :label-col="{ span: 0 }" :wrapper-col="{ span: 24 }">
<ExtraPropertyDictionary :disabled="!state.allowedChange" :allow-delete="true" :allow-edit="true" v-model:value="state.entity.extraProperties" />
</FormItem>
</TabPane>
</Tabs>
</Form>
</BasicModal>
</template>
<script setup lang="ts">
import type { Rule } from 'ant-design-vue/lib/form';
import { cloneDeep } from 'lodash-es';
import { computed, ref, reactive, unref, nextTick, watch } from 'vue';
import { Checkbox, Form, Input, InputNumber, Select, Tabs, TreeSelect } from 'ant-design-vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { LocalizableInput, ExtraPropertyDictionary, StringValueTypeInput } from '/@/components/Abp';
import { FreeTextStringValueType, SelectionStringValueItem, valueTypeSerializer } from '/@/components/Abp/StringValueType/valueType';
import { useMessage } from '/@/hooks/web/useMessage';
import { ValidationEnum, useValidation } from '/@/hooks/abp/useValidation';
import { useLocalization } from '/@/hooks/abp/useLocalization';
import { useLocalizationSerializer } from '/@/hooks/abp/useLocalizationSerializer';
import {
GetAsyncByName,
CreateAsyncByInput,
UpdateAsyncByNameAndInput
} from '/@/api/feature-management/definitions/features';
import {
FeatureDefinitionUpdateDto,
FeatureDefinitionCreateDto
} from '/@/api/feature-management/definitions/features/model';
import { GetListAsyncByInput } from '/@/api/feature-management/definitions/features';
import { FeatureGroupDefinitionDto } from '/@/api/feature-management/definitions/groups/model';
import { listToTree } from '/@/utils/helper/treeHelper';
import { isBoolean, isNullOrUnDef } from '/@/utils/is';
import { groupBy } from '/@/utils/array';
const FormItem = Form.Item;
const TabPane = Tabs.TabPane;
const TextArea = Input.TextArea;
interface FeatureSelectData {
label: string;
value: string;
}
interface FeatureTreeData {
name: string;
groupName: string;
displayName: string;
children: FeatureTreeData[];
}
interface State {
activeTab: string,
allowedChange: boolean,
entity: Recordable,
entityRules?: Dictionary<string, Rule>,
entityChanged: boolean,
entityEditFlag: boolean,
defaultGroup?: string,
availableFeatures: FeatureTreeData[],
availableGroups: FeatureGroupDefinitionDto[],
selectionDataSource: FeatureSelectData[],
}
const emits = defineEmits(['register', 'change']);
const { ruleCreator } = useValidation();
const { deserialize, validate } = useLocalizationSerializer();
const { createConfirm, createMessage } = useMessage();
const { L, Lr } = useLocalization(['AbpFeatureManagement', 'AbpUi']);
const formRef = ref<any>();
const valueTypeRef = ref<any>();
const validatorNameRef = ref<string>('NULL');
const valueTypeNameRef = ref<string>('FreeTextStringValueType');
const state = reactive<State>({
activeTab: 'basic',
entity: {},
allowedChange: false,
entityChanged: false,
entityEditFlag: false,
availableFeatures: [],
availableGroups: [],
selectionDataSource: [],
entityRules: {
groupName: ruleCreator.fieldRequired({
name: 'GroupName',
prefix: 'DisplayName',
resourceName: 'WebhooksManagement',
trigger: 'blur',
}),
name: ruleCreator.fieldRequired({
name: 'Name',
prefix: 'DisplayName',
resourceName: 'WebhooksManagement',
trigger: 'blur',
}),
displayName: ruleCreator.defineValidator({
required: true,
trigger: 'blur',
validator(_rule, value) {
if (!validate(value)) {
return Promise.reject(L(ValidationEnum.FieldRequired, [L('DisplayName:DisplayName')]));
}
return Promise.resolve();
},
}),
description: ruleCreator.defineValidator({
trigger: 'blur',
validator(_rule, value) {
if (!validate(value, { required: false })) {
return Promise.reject(L(ValidationEnum.FieldRequired, [L('DisplayName:Description')]));
}
return Promise.resolve();
},
}),
defaultValue: ruleCreator.defineValidator({
trigger: 'change',
validator(_rule, value) {
const valueType = unref(valueTypeRef);
if (valueType) {
return valueType.validate(value);
}
return Promise.resolve();
},
}),
},
});
const getGroupOptions = computed(() => {
return state.availableGroups
.filter((group) => !group.isStatic)
.map((group) => {
const info = deserialize(group.displayName);
return {
label: Lr(info.resourceName, info.name),
value: group.name,
};
});
});
watch(
() => state.entity,
() => {
state.entityChanged = true;
},
{
deep: true,
},
);
const [registerModal, { closeModal, changeLoading, changeOkLoading }] = useModalInner((data) => {
state.defaultGroup = data.groupName;
state.availableGroups = data.groups;
nextTick(() => {
fetchFeatures(state.defaultGroup);
fetch(data.record?.name);
});
});
function fetch(name?: string) {
state.activeTab = 'basic';
state.entityEditFlag = false;
if (!name) {
state.entity = {
isVisibleToClients: true,
isAvailableToHost: true,
groupName: state.defaultGroup,
requiredFeatures: [],
valueType: valueTypeSerializer.serialize(new FreeTextStringValueType())
};
state.allowedChange = true;
nextTick(() => state.entityChanged = false);
return;
}
changeLoading(true);
changeOkLoading(true);
GetAsyncByName(name).then((record) => {
state.entity = record;
state.entityEditFlag = true;
state.allowedChange = !record.isStatic;
}).finally(() => {
changeLoading(false);
changeOkLoading(false);
nextTick(() => state.entityChanged = false);
});
}
function fetchFeatures(groupName?: string) {
GetListAsyncByInput({
groupName: groupName,
}).then((res) => {
const featureGroup = groupBy(res.items.filter((def) => !def.isStatic), 'groupName');
const featureTreeData: FeatureTreeData[] = [];
Object.keys(featureGroup).forEach((gk) => {
const featureTree = listToTree(featureGroup[gk], {
id: 'name',
pid: 'parentName',
});
formatDisplayName(featureTree);
featureTreeData.push(...featureTree);
});
state.availableFeatures = featureTreeData;
});
}
function formatDisplayName(list: any[]) {
if (list && Array.isArray(list)) {
list.forEach((item) => {
if (Reflect.has(item, 'displayName')) {
const info = deserialize(item.displayName);
item.displayName = Lr(info.resourceName, info.name);
}
if (Reflect.has(item, 'children')) {
formatDisplayName(item.children);
}
});
}
}
function handleGroupChange(value?: string) {
state.entity.parentName = undefined;
fetchFeatures(value);
}
function handleParentChange(value?: string) {
if (!value) return;
GetAsyncByName(value).then((res) => {
state.entity.groupName = res.groupName;
const form = unref(formRef);
form?.clearValidate(['groupName']);
});
}
function handleValueTypeNameChange(valueTypeName: string) {
valueTypeNameRef.value = valueTypeName;
switch (valueTypeName) {
case 'ToggleStringValueType':
if (!isBoolean(state.entity.defaultValue)) {
state.entity.defaultValue = false;
}
break;
default:
if (isBoolean(state.entity.defaultValue)) {
state.entity.defaultValue = undefined;
}
break;
}
}
function handleValidatorNameChange(validatorName: string) {
validatorNameRef.value = validatorName;
}
function handleSelectionChange(items: SelectionStringValueItem[]) {
if (items.length === 0) {
state.entity.defaultValue = undefined;
state.selectionDataSource = [];
return;
}
state.selectionDataSource = items.map((item) => {
return {
label: Lr(item.displayText.resourceName, item.displayText.name),
value: item.value,
};
});
if (!items.find(item => item.value === state.entity.defaultValue)) {
state.entity.defaultValue = undefined;
}
}
function handleBeforeClose(): Promise<boolean> {
return new Promise((resolve) => {
if (!state.entityChanged) {
const form = unref(formRef);
form?.resetFields();
return resolve(true);
}
createConfirm({
iconType: 'warning',
title: L('AreYouSure'),
content: L('AreYouSureYouWantToCancelEditingWarningMessage'),
onOk: () => {
const form = unref(formRef);
form?.resetFields();
state.allowedChange = false;
resolve(true);
},
onCancel: () => {
resolve(false);
},
afterClose: () => {
state.defaultGroup = undefined;
},
});
});
}
function handleSubmit() {
if (!state.allowedChange) {
closeModal();
return;
}
const form = unref(formRef);
form?.validate().then(() => {
changeOkLoading(true);
const input = cloneDeep(state.entity);
if (!isNullOrUnDef(input.defaultValue)) {
input.defaultValue = String(input.defaultValue);
}
const api = state.entityEditFlag
? UpdateAsyncByNameAndInput(state.entity.name, input as FeatureDefinitionUpdateDto)
: CreateAsyncByInput(input as FeatureDefinitionCreateDto);
api.then((res) => {
createMessage.success(L('Successful'));
emits('change', res);
form.resetFields();
closeModal();
}).finally(() => {
changeOkLoading(false);
})
});
}
</script>
<style scoped>
</style>