123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261 |
- <script setup lang="ts">
- import { computed, onMounted, PropType, ref, toRefs, watch } from 'vue';
- import { ElMessage } from 'element-plus';
- import { Plus, Delete } from '@element-plus/icons-vue';
- import { useI18n } from 'vue-i18n';
- import _ from 'lodash';
- import { perm } from '@/store/useCurrentUser';
- import { actionType, ACTION } from '@/constant/common';
- const CONTINUOUS_SETTINGS = 'cms_continuous_settings';
- function fetchContinuous(): Record<string, boolean> {
- const settings = localStorage.getItem(CONTINUOUS_SETTINGS);
- return settings ? JSON.parse(settings) : {};
- }
- function storeContinuous(settings: Record<string, boolean>) {
- localStorage.setItem(CONTINUOUS_SETTINGS, JSON.stringify(settings));
- }
- function getContinuous(name: string) {
- const settings = fetchContinuous();
- return settings[name] ?? false;
- }
- function setContinuous(name: string, continuous: boolean) {
- const settings = fetchContinuous();
- settings[name] = continuous;
- storeContinuous(settings);
- }
- const props = defineProps({
- modelValue: { type: Boolean, required: true },
- name: { type: String, required: true },
- beanId: { type: [Number, String], default: null },
- beanIds: { type: Array as PropType<string[] | number[]>, required: true },
- values: { type: Object, required: true },
- initValues: { type: Function as PropType<(bean?: any) => any>, required: true },
- toValues: { type: Function as PropType<(bean: any) => any>, required: true },
- queryBean: { type: Function as PropType<(id: any) => Promise<any>>, required: true },
- createBean: { type: Function as PropType<(bean: any) => Promise<any>>, required: true },
- updateBean: { type: Function as PropType<(bean: any) => Promise<any>>, required: true },
- disableDelete: { type: Function as PropType<(bean: any) => boolean>, default: null },
- disableEdit: { type: Function as PropType<(bean: any) => boolean>, default: null },
- addable: { type: Boolean, default: true },
- action: { type: String as PropType<actionType>, default: ACTION.EDIT },
- showId: { type: Boolean, default: true },
- perms: { type: String, default: null },
- focus: { type: Object, default: null },
- large: { type: Boolean, default: false },
- labelPosition: { type: String as PropType<'top' | 'right' | 'left'>, default: 'right' },
- labelWidth: { type: String, default: '150px' },
- });
- const emit = defineEmits({
- 'update:modelValue': null,
- 'update:values': null,
- finished: null,
- beanChange: null,
- beforeSubmit: null,
- });
- const { name, beanId, beanIds, focus, values, action, modelValue: visible } = toRefs(props);
- const { t } = useI18n();
- const loading = ref<boolean>(false);
- const buttonLoading = ref<boolean>(false);
- const continuous = ref<boolean>(getContinuous(name.value));
- const form = ref();
- const bean = ref(props.initValues());
- const origValues = ref();
- const id = ref();
- const ids = ref<Array<any>>([]);
- const isEdit = computed(() => id.value != null && action.value === ACTION.EDIT);
- const unsaved = computed(() => {
- // 调试 未保存
- // if (!_.isEqual(origValues.value, values.value)) {
- // console.log(JSON.stringify(origValues.value));
- // console.log(JSON.stringify(values.value));
- // }
- return !loading.value && !_.isEqual(origValues.value, values.value);
- });
- const disabled = computed(() => props.disableEdit?.(bean.value) ?? false);
- const title = computed(() => `${name.value} - ${isEdit.value ? `${t(disabled.value ? ACTION.DETAIL : ACTION.EDIT)} (ID: ${id.value})` : `${t('add')}`}`);
- const loadBean = async () => {
- loading.value = true;
- try {
- bean.value = id.value != null ? await props.queryBean(id.value) : props.initValues(values.value);
- origValues.value = id.value != null ? props.toValues(bean.value) : bean.value;
- emit('update:values', _.cloneDeep(origValues.value));
- emit('beanChange', bean.value);
- form.value?.resetFields();
- } finally {
- loading.value = false;
- }
- };
- onMounted(() => emit('update:values', props.initValues()));
- watch(visible, () => {
- if (visible.value) {
- ids.value = beanIds.value;
- if (id.value !== beanId.value) {
- id.value = beanId.value;
- } else {
- loadBean();
- }
- }
- });
- watch(id, () => {
- loadBean();
- });
- watch(continuous, () => setContinuous(name.value, continuous.value));
- const index = computed(() => ids.value.indexOf(id.value));
- const hasPrev = computed(() => index.value > 0);
- const hasNext = computed(() => index.value < ids.value.length - 1);
- const handlePrev = () => {
- if (hasPrev.value) {
- id.value = ids.value[index.value - 1];
- }
- };
- const handleNext = () => {
- if (hasNext.value) {
- id.value = ids.value[index.value + 1];
- }
- };
- // const handleAdd = () => {
- // focus.value?.focus?.();
- // id.value = undefined;
- // };
- // const handleCancel = () => {
- // emit('update:modelValue', false);
- // };
- const handleSubmit = () => {
- form.value.validate(async (valid: boolean) => {
- if (!valid) return;
- buttonLoading.value = true;
- try {
- emit('beforeSubmit', values.value);
- if (isEdit.value) {
- await props.updateBean(values.value);
- } else {
- await props.createBean(values.value);
- // eslint-disable-next-line no-unused-expressions
- focus.value?.focus?.();
- emit('update:values', props.initValues(values.value));
- form.value.resetFields();
- }
- ElMessage.success(t('success'));
- if (!continuous.value) emit('update:modelValue', false);
- emit('finished', bean.value);
- } finally {
- buttonLoading.value = false;
- }
- });
- };
- // const handleDelete = async () => {
- // buttonLoading.value = true;
- // try {
- // await props.deleteBean([id.value]);
- // if (!continuous.value) emit('update:modelValue', false);
- // if (hasNext.value) {
- // handleNext();
- // ids.value.splice(index.value - 1, 1);
- // } else if (hasPrev.value) {
- // handlePrev();
- // ids.value.splice(index.value + 1, 1);
- // } else {
- // emit('update:modelValue', false);
- // }
- // ElMessage.success(t('success'));
- // emit('finished');
- // } finally {
- // buttonLoading.value = false;
- // }
- // };
- const submit = (
- executor: (values: any, payload: { isEdit: boolean; continuous: boolean; form: any; props: any; focus: any; loadBean: () => Promise<any>; emit: any }) => Promise<any>,
- ) => {
- form.value.validate(async (valid: boolean) => {
- if (!valid) return;
- buttonLoading.value = true;
- try {
- emit('beforeSubmit', values.value);
- await executor(values.value, { isEdit: isEdit.value, continuous: continuous.value, form: form.value, props, focus: focus.value, loadBean, emit });
- if (!continuous.value) emit('update:modelValue', false);
- emit('finished', bean.value);
- } finally {
- buttonLoading.value = false;
- }
- });
- };
- const remove = async (
- executor: (values: any, payload: { isEdit: boolean; continuous: boolean; form: any; props: any; focus: any; loadBean: () => Promise<any>; emit: any }) => Promise<any>,
- ) => {
- buttonLoading.value = true;
- try {
- await executor(values.value, { isEdit: isEdit.value, continuous: continuous.value, form: form.value, props, focus: focus.value, loadBean, emit });
- if (!continuous.value) emit('update:modelValue', false);
- if (hasNext.value) {
- handleNext();
- ids.value.splice(index.value - 1, 1);
- } else if (hasPrev.value) {
- handlePrev();
- ids.value.splice(index.value + 1, 1);
- } else {
- emit('update:modelValue', false);
- }
- ElMessage.success(t('success'));
- emit('finished');
- } finally {
- buttonLoading.value = false;
- }
- };
- defineExpose({ form, submit, remove });
- </script>
- <template>
- <el-dialog
- :title="title"
- :close-on-click-modal="!unsaved"
- :model-value="modelValue"
- :width="large ? '98%' : '768px'"
- :top="large ? '16px' : '8vh'"
- @update:model-value="(event) => $emit('update:modelValue', event)"
- @opened="() => !isEdit && focus?.focus()"
- >
- <template #header>
- {{ name }} -
- <span v-if="isEdit">
- {{ $t(disabled ? 'detail' : 'edit') }}
- <span v-if="showId">(ID: {{ id }})</span>
- </span>
- <span v-else>{{ $t('add') }}</span>
- </template>
- <!-- <div v-loading="loading || buttonLoading" class="space-x-2">
- <el-button v-if="isEdit && addable" :disabled="perm(`${perms}:create`)" type="primary" :icon="Plus" @click="handleAdd">{{ $t('add') }}</el-button>
- <slot name="header-action" :bean="bean" :is-edit="isEdit" :disabled="disabled" :unsaved="unsaved" :disable-delete="disableDelete" :handle-delete="handleDelete">
- <el-popconfirm v-if="isEdit" :title="$t('confirmDelete')" @confirm="() => handleDelete()">
- <template #reference>
- <el-button :disabled="disableDelete?.(bean) || perm(`${perms}:delete`)" :icon="Delete">{{ $t('delete') }}</el-button>
- </template>
- </el-popconfirm>
- </slot>
- <el-button-group v-if="isEdit">
- <el-button :disabled="!hasPrev" @click="handlePrev">{{ $t('form.prev') }}</el-button>
- <el-button :disabled="!hasNext" @click="handleNext">{{ $t('form.next') }}</el-button>
- </el-button-group>
- <el-button type="primary" @click="handleCancel">{{ $t('back') }}</el-button>
- <el-tooltip :content="$t('form.continuous')" placement="top">
- <el-switch v-model="continuous" size="small"></el-switch>
- </el-tooltip>
- <el-tag v-if="unsaved" type="danger">{{ $t('form.unsaved') }}</el-tag>
- <slot name="header-status" :bean="bean" :is-edit="isEdit" :disabled="disabled"></slot>
- </div> -->
- <el-form ref="form" :class="['mt-5', 'pr-5']" :model="values" :disabled="disabled" :label-width="labelWidth" :label-position="labelPosition" scroll-to-error>
- <slot :bean="bean" :is-edit="isEdit" :disabled="disabled"></slot>
- <div v-if="!disabled" v-loading="buttonLoading" class="w-full text-center footer-action">
- <slot name="footer-action" :bean="bean" :is-edit="isEdit" :disabled="disabled" :handle-submit="handleSubmit">
- <el-button :disabled="perm(isEdit ? `${perms}:update` : `${perms}:create`)" type="primary" native-type="submit" @click.prevent="() => handleSubmit()">
- {{ $t('save') }}
- </el-button>
- </slot>
- </div>
- </el-form>
- </el-dialog>
- </template>
|