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.
255 lines
7.3 KiB
255 lines
7.3 KiB
<template>
|
|
<div style="width: 100%">
|
|
<div :class="`${prefixCls}__toolbar`" v-if="!props.disabled">
|
|
<Button type="primary" @click="handleAddNew">{{ t('component.extra_property_dictionary.actions.create') }}</Button>
|
|
<Button v-if="props.allowDelete" danger @click="handleClean">{{ t('component.extra_property_dictionary.actions.clean') }}</Button>
|
|
</div>
|
|
<Card :title="t('component.extra_property_dictionary.title')">
|
|
<Table sticky rowKey="key" :columns="getTableColumns" :data-source="state.table.dataSource" :scroll="{ x: 1500 }">
|
|
<template v-if="!props.disabled" #bodyCell="{ column, record }">
|
|
<template v-if="column.key === 'action'">
|
|
<div :class="`${prefixCls}__action`">
|
|
<Button v-if="props.allowEdit" type="link" @click="() => handleEdit(record)">
|
|
<template #icon>
|
|
<EditOutlined />
|
|
</template>
|
|
{{ t('component.extra_property_dictionary.actions.update') }}
|
|
</Button>
|
|
<Divider v-if="props.allowEdit && props.allowDelete" type="vertical" />
|
|
<Button v-if="props.allowDelete" type="link" @click="() => handleDelete(record)" class="ant-btn-error">
|
|
<template #icon>
|
|
<DeleteOutlined />
|
|
</template>
|
|
{{ t('component.extra_property_dictionary.actions.delete') }}
|
|
</Button>
|
|
</div>
|
|
</template>
|
|
</template>
|
|
</Table>
|
|
</Card>
|
|
<Modal v-bind="state.modal">
|
|
<Form
|
|
ref="formRef"
|
|
:class="`${prefixCls}__form`"
|
|
v-bind="state.form"
|
|
:label-col="{ span: 6 }"
|
|
:wrapper-col="{ span: 18}"
|
|
>
|
|
<FormItem name="key" required :label="t('component.extra_property_dictionary.key')">
|
|
<Input :disabled="state.editFlag" v-model:value="state.form.model.key" />
|
|
</FormItem>
|
|
<FormItem name="value" required :label="t('component.extra_property_dictionary.value')">
|
|
<Input v-model:value="state.form.model.value" />
|
|
</FormItem>
|
|
</Form>
|
|
</Modal>
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts" setup>
|
|
import type { RuleObject } from 'ant-design-vue/lib/form';
|
|
import type { ColumnsType } from 'ant-design-vue/lib/table/interface';
|
|
import { cloneDeep } from 'lodash-es';
|
|
import { computed, reactive, ref, unref, watch } from 'vue';
|
|
import { DeleteOutlined, EditOutlined } from '@ant-design/icons-vue';
|
|
import { Button, Card, Divider, Form, Input, Table, Modal } from 'ant-design-vue';
|
|
import { useDesign } from '/@/hooks/web/useDesign';
|
|
import { useI18n } from '/@/hooks/web/useI18n';
|
|
import { propTypes } from '/@/utils/propTypes';
|
|
|
|
const FormItem = Form.Item;
|
|
|
|
interface DataSource {
|
|
key: string;
|
|
value: string;
|
|
}
|
|
interface State {
|
|
editFlag: boolean,
|
|
modal: {
|
|
title?: string,
|
|
visible?: boolean,
|
|
maskClosable?: boolean,
|
|
width?: number,
|
|
minHeight?: number,
|
|
onOk?: (e: MouseEvent) => void,
|
|
onCancel?: (e: MouseEvent) => void,
|
|
},
|
|
form: {
|
|
model: any,
|
|
rules?: Dictionary<string, RuleObject>,
|
|
},
|
|
table: {
|
|
dataSource: DataSource[],
|
|
},
|
|
}
|
|
|
|
const emits = defineEmits(['change', 'update:value']);
|
|
const props = defineProps({
|
|
value: {
|
|
type: Object as PropType<Dictionary<string, string>>,
|
|
},
|
|
allowEdit: propTypes.bool.def(false),
|
|
allowDelete: propTypes.bool.def(false),
|
|
disabled: propTypes.bool.def(false),
|
|
});
|
|
|
|
const { prefixCls } = useDesign('extra-property-dictionary');
|
|
const { t } = useI18n();
|
|
const formRef = ref<any>();
|
|
const getTableColumns = computed(() => {
|
|
const columns: ColumnsType = [{
|
|
title: t('component.extra_property_dictionary.key'),
|
|
dataIndex: 'key',
|
|
align: 'left',
|
|
fixed: 'left',
|
|
width: 180,
|
|
},
|
|
{
|
|
title: t('component.extra_property_dictionary.value'),
|
|
dataIndex: 'value',
|
|
align: 'left',
|
|
fixed: 'left',
|
|
width: 'auto',
|
|
}];
|
|
return columns.concat(props.disabled
|
|
? []
|
|
: [{
|
|
width: 220,
|
|
title: t('component.extra_property_dictionary.actions.title'),
|
|
align: 'center',
|
|
dataIndex: 'action',
|
|
key: 'action',
|
|
fixed: 'right',
|
|
}]);
|
|
});
|
|
const state = reactive<State>({
|
|
editFlag: false,
|
|
modal: {
|
|
width: 600,
|
|
minHeight: 400,
|
|
visible: false,
|
|
maskClosable: false,
|
|
onOk: handleSubmit,
|
|
onCancel: handleCancel,
|
|
},
|
|
form: {
|
|
model: {},
|
|
rules: {
|
|
key: {
|
|
validator: (_rule, value) => {
|
|
if (!state.editFlag && state.table.dataSource && state.table.dataSource.findIndex(x => x.key === value) >= 0) {
|
|
return Promise.reject(t('component.extra_property_dictionary.validator.duplicateKey'));
|
|
}
|
|
return Promise.resolve();
|
|
}
|
|
},
|
|
},
|
|
},
|
|
table: {
|
|
dataSource: [],
|
|
},
|
|
});
|
|
watch(
|
|
() => props.value,
|
|
(dataSource) => {
|
|
if (!dataSource) {
|
|
state.table.dataSource = [];
|
|
return;
|
|
}
|
|
state.table.dataSource = Object.keys(dataSource).map((key) => {
|
|
return {
|
|
key: key,
|
|
value: dataSource[key],
|
|
};
|
|
});
|
|
},
|
|
{
|
|
immediate: true,
|
|
}
|
|
);
|
|
|
|
function handleAddNew() {
|
|
state.form.model = {};
|
|
state.editFlag = false;
|
|
state.modal.title = t('component.extra_property_dictionary.actions.create');
|
|
state.modal.visible = true;
|
|
}
|
|
|
|
function handleEdit(record) {
|
|
state.editFlag = true;
|
|
state.form.model = cloneDeep(record);
|
|
state.modal.title = t('component.extra_property_dictionary.actions.update');
|
|
state.modal.visible = true;
|
|
}
|
|
|
|
function handleClean() {
|
|
emits('change', {});
|
|
emits('update:value', {});
|
|
}
|
|
|
|
function handleDelete(record) {
|
|
const dataSource = state.table.dataSource ?? [];
|
|
const findIndex = dataSource.findIndex(x => x.key === record.key);
|
|
const changeData: Dictionary<string, string> = {};
|
|
dataSource.splice(findIndex, 1);
|
|
dataSource.forEach((item) => {
|
|
changeData[item.key] = item.value;
|
|
});
|
|
emits('change', changeData);
|
|
emits('update:value', changeData);
|
|
}
|
|
|
|
function handleCancel() {
|
|
const form = unref(formRef);
|
|
form?.resetFields();
|
|
state.modal.visible = false;
|
|
}
|
|
|
|
function handleSubmit() {
|
|
const form = unref(formRef);
|
|
form?.validate().then(() => {
|
|
const dataSource = state.table.dataSource ?? [];
|
|
const changeData: Dictionary<string, string> = {};
|
|
if (!state.editFlag) {
|
|
dataSource.push(state.form.model);
|
|
} else {
|
|
const findIndex = dataSource.findIndex(x => x.key === state.form.model.key);
|
|
dataSource[findIndex] = state.form.model;
|
|
}
|
|
dataSource.forEach((item) => {
|
|
changeData[item.key] = item.value;
|
|
});
|
|
emits('change', changeData);
|
|
emits('update:value', changeData);
|
|
form.resetFields();
|
|
state.modal.visible = false;
|
|
});
|
|
}
|
|
</script>
|
|
|
|
<style lang="less" scoped>
|
|
@prefix-cls: ~'@{namespace}-extra-property-dictionary';
|
|
|
|
.@{prefix-cls} {
|
|
&__toolbar {
|
|
flex: 1;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: flex-end;
|
|
margin-bottom: 8px;
|
|
|
|
> * {
|
|
margin-right: 8px;
|
|
}
|
|
}
|
|
|
|
&__action {
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
&__form {
|
|
margin: 10px;
|
|
}
|
|
}
|
|
</style>
|
|
|