mirror of
				https://github.com/Tencent/tmagic-editor.git
				synced 2025-11-04 18:52:18 +08:00 
			
		
		
		
	feat(form): groupList支持复制单条记录,支持移动单条记得到指定位置,table支持复制单条记录
This commit is contained in:
		
							parent
							
								
									833dbcd8b4
								
							
						
					
					
						commit
						a831413151
					
				@ -20,6 +20,7 @@
 | 
			
		||||
      :disabled="disabled"
 | 
			
		||||
      :group-model="model[name]"
 | 
			
		||||
      @remove-item="removeHandler"
 | 
			
		||||
      @copy-item="copyHandler"
 | 
			
		||||
      @swap-item="swapHandler"
 | 
			
		||||
      @change="changeHandler"
 | 
			
		||||
      @addDiffCount="onAddDiffCount()"
 | 
			
		||||
@ -41,6 +42,7 @@
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { computed, inject } from 'vue';
 | 
			
		||||
import { Grid } from '@element-plus/icons-vue';
 | 
			
		||||
import { cloneDeep } from 'lodash-es';
 | 
			
		||||
 | 
			
		||||
import { TMagicButton } from '@tmagic/design';
 | 
			
		||||
 | 
			
		||||
@ -131,11 +133,17 @@ const removeHandler = (index: number) => {
 | 
			
		||||
  emit('change', props.model[props.name]);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const copyHandler = (index: number) => {
 | 
			
		||||
  props.model[props.name].push(cloneDeep(props.model[props.name][index]));
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const swapHandler = (idx1: number, idx2: number) => {
 | 
			
		||||
  if (!props.name) return false;
 | 
			
		||||
 | 
			
		||||
  const { length } = props.model[props.name];
 | 
			
		||||
 | 
			
		||||
  const [currRow] = props.model[props.name].splice(idx1, 1);
 | 
			
		||||
  props.model[props.name].splice(idx2, 0, currRow);
 | 
			
		||||
  props.model[props.name].splice(Math.min(Math.max(idx2, 0), length - 1), 0, currRow);
 | 
			
		||||
  emit('change', props.model[props.name]);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -6,23 +6,83 @@
 | 
			
		||||
      </TMagicButton>
 | 
			
		||||
 | 
			
		||||
      <TMagicButton
 | 
			
		||||
        v-show="showDelete(parseInt(String(index)))"
 | 
			
		||||
        style="color: #f56c6c"
 | 
			
		||||
        v-show="showDelete"
 | 
			
		||||
        type="danger"
 | 
			
		||||
        size="small"
 | 
			
		||||
        link
 | 
			
		||||
        :icon="Delete"
 | 
			
		||||
        :disabled="disabled"
 | 
			
		||||
        @click="removeHandler"
 | 
			
		||||
      ></TMagicButton>
 | 
			
		||||
 | 
			
		||||
      <template v-if="movable()">
 | 
			
		||||
        <TMagicButton v-show="index !== 0" link :disabled="disabled" size="small" @click="changeOrder(-1)"
 | 
			
		||||
          >上移<TMagicIcon><CaretTop /></TMagicIcon
 | 
			
		||||
        ></TMagicButton>
 | 
			
		||||
        <TMagicButton v-show="index !== length - 1" :disabled="disabled" link size="small" @click="changeOrder(1)"
 | 
			
		||||
          >下移<TMagicIcon><CaretBottom /></TMagicIcon
 | 
			
		||||
        ></TMagicButton>
 | 
			
		||||
      <TMagicButton
 | 
			
		||||
        v-if="copyable"
 | 
			
		||||
        link
 | 
			
		||||
        size="small"
 | 
			
		||||
        type="primary"
 | 
			
		||||
        :icon="DocumentCopy"
 | 
			
		||||
        :disabled="disabled"
 | 
			
		||||
        @click="copyHandler"
 | 
			
		||||
        >复制</TMagicButton
 | 
			
		||||
      >
 | 
			
		||||
 | 
			
		||||
      <template v-if="movable">
 | 
			
		||||
        <TMagicButton
 | 
			
		||||
          v-show="index !== 0"
 | 
			
		||||
          link
 | 
			
		||||
          size="small"
 | 
			
		||||
          :disabled="disabled"
 | 
			
		||||
          :icon="CaretTop"
 | 
			
		||||
          @click="changeOrder(-1)"
 | 
			
		||||
          >上移</TMagicButton
 | 
			
		||||
        >
 | 
			
		||||
        <TMagicButton
 | 
			
		||||
          v-show="index !== length - 1"
 | 
			
		||||
          link
 | 
			
		||||
          size="small"
 | 
			
		||||
          :disabled="disabled"
 | 
			
		||||
          :icon="CaretBottom"
 | 
			
		||||
          @click="changeOrder(1)"
 | 
			
		||||
          >下移</TMagicButton
 | 
			
		||||
        >
 | 
			
		||||
      </template>
 | 
			
		||||
 | 
			
		||||
      <TMagicPopover
 | 
			
		||||
        v-if="config.moveSpecifyLocation"
 | 
			
		||||
        trigger="click"
 | 
			
		||||
        placement="top"
 | 
			
		||||
        width="200"
 | 
			
		||||
        :visible="moveSpecifyLocationVisible"
 | 
			
		||||
      >
 | 
			
		||||
        <template #reference>
 | 
			
		||||
          <TMagicButton
 | 
			
		||||
            link
 | 
			
		||||
            size="small"
 | 
			
		||||
            type="primary"
 | 
			
		||||
            :icon="Position"
 | 
			
		||||
            :disabled="disabled"
 | 
			
		||||
            @click="moveSpecifyLocationVisible = true"
 | 
			
		||||
            >移动至</TMagicButton
 | 
			
		||||
          >
 | 
			
		||||
        </template>
 | 
			
		||||
        <div>
 | 
			
		||||
          <div>
 | 
			
		||||
            第<TMagicInputNumber
 | 
			
		||||
              style="margin: 0 5px"
 | 
			
		||||
              v-model="moveSpecifyLocationIndex"
 | 
			
		||||
              size="small"
 | 
			
		||||
              :min="1"
 | 
			
		||||
              :disabled="disabled"
 | 
			
		||||
            ></TMagicInputNumber
 | 
			
		||||
            >行
 | 
			
		||||
          </div>
 | 
			
		||||
          <div style="text-align: right; margin-top: 20px">
 | 
			
		||||
            <TMagicButton size="small" text @click="moveSpecifyLocationVisible = false">取消</TMagicButton>
 | 
			
		||||
            <TMagicButton size="small" type="primary" @click="moveSpecifyLocationHandler">确认</TMagicButton>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </TMagicPopover>
 | 
			
		||||
 | 
			
		||||
      <span v-if="itemExtra" v-html="itemExtra" class="m-form-tip"></span>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
@ -44,9 +104,9 @@
 | 
			
		||||
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { computed, inject, ref } from 'vue';
 | 
			
		||||
import { CaretBottom, CaretRight, CaretTop, Delete } from '@element-plus/icons-vue';
 | 
			
		||||
import { CaretBottom, CaretRight, CaretTop, Delete, DocumentCopy, Position } from '@element-plus/icons-vue';
 | 
			
		||||
 | 
			
		||||
import { TMagicButton, TMagicIcon } from '@tmagic/design';
 | 
			
		||||
import { TMagicButton, TMagicIcon, TMagicInputNumber, TMagicPopover } from '@tmagic/design';
 | 
			
		||||
 | 
			
		||||
import type { ContainerChangeEventData, FormState, GroupListConfig } from '../schema';
 | 
			
		||||
import { filterFunction } from '../utils/form';
 | 
			
		||||
@ -70,7 +130,7 @@ const props = defineProps<{
 | 
			
		||||
  disabled?: boolean;
 | 
			
		||||
}>();
 | 
			
		||||
 | 
			
		||||
const emit = defineEmits(['swap-item', 'remove-item', 'change', 'addDiffCount']);
 | 
			
		||||
const emit = defineEmits(['swap-item', 'remove-item', 'change', 'addDiffCount', 'copy-item']);
 | 
			
		||||
 | 
			
		||||
const mForm = inject<FormState | undefined>('mForm');
 | 
			
		||||
const expand = ref(props.config.expandAll || !props.index);
 | 
			
		||||
@ -112,18 +172,18 @@ const expandHandler = () => {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// 希望支持单行可控制是否显示删除按钮,不会影响现有逻辑
 | 
			
		||||
const showDelete = (index: number) => {
 | 
			
		||||
const showDelete = computed(() => {
 | 
			
		||||
  const deleteFunc = props.config.delete;
 | 
			
		||||
  if (deleteFunc && typeof deleteFunc === 'function') {
 | 
			
		||||
    return deleteFunc(props.model, index, mForm?.values);
 | 
			
		||||
    return deleteFunc(props.model, props.index, mForm?.values);
 | 
			
		||||
  }
 | 
			
		||||
  return true;
 | 
			
		||||
};
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// 调换顺序
 | 
			
		||||
const changeOrder = (offset = 0) => emit('swap-item', props.index, props.index + offset);
 | 
			
		||||
 | 
			
		||||
const movable = () => {
 | 
			
		||||
const movable = computed(() => {
 | 
			
		||||
  const { movable } = props.config;
 | 
			
		||||
 | 
			
		||||
  // 没有设置时,默认可移动
 | 
			
		||||
@ -132,6 +192,20 @@ const movable = () => {
 | 
			
		||||
    return movable(mForm, props.index || 0, props.model, props.groupModel);
 | 
			
		||||
  }
 | 
			
		||||
  return movable;
 | 
			
		||||
};
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const copyable = computed(() => filterFunction<boolean>(mForm, props.config.copyable, props));
 | 
			
		||||
const onAddDiffCount = () => emit('addDiffCount');
 | 
			
		||||
 | 
			
		||||
const copyHandler = () => {
 | 
			
		||||
  emit('copy-item', props.index);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const moveSpecifyLocationVisible = ref(false);
 | 
			
		||||
const moveSpecifyLocationIndex = ref(1);
 | 
			
		||||
 | 
			
		||||
const moveSpecifyLocationHandler = () => {
 | 
			
		||||
  moveSpecifyLocationVisible.value = false;
 | 
			
		||||
  emit('swap-item', props.index, moveSpecifyLocationIndex.value - 1);
 | 
			
		||||
};
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
@ -26,18 +26,32 @@
 | 
			
		||||
 | 
			
		||||
            <TMagicTableColumn
 | 
			
		||||
              label="操作"
 | 
			
		||||
              :width="config.operateColWidth || 55"
 | 
			
		||||
              :width="config.operateColWidth || 100"
 | 
			
		||||
              align="center"
 | 
			
		||||
              :fixed="config.fixed === false ? undefined : 'left'"
 | 
			
		||||
            >
 | 
			
		||||
              <template v-slot="scope">
 | 
			
		||||
                <slot name="operateCol" :scope="scope"></slot>
 | 
			
		||||
                <TMagicIcon
 | 
			
		||||
                <TMagicButton
 | 
			
		||||
                  v-show="showDelete(scope.$index + 1 + pagecontext * pagesize - 1)"
 | 
			
		||||
                  class="m-table-delete-icon"
 | 
			
		||||
                  size="small"
 | 
			
		||||
                  type="danger"
 | 
			
		||||
                  link
 | 
			
		||||
                  title="删除"
 | 
			
		||||
                  :icon="Delete"
 | 
			
		||||
                  @click="removeHandler(scope.$index + 1 + pagecontext * pagesize - 1)"
 | 
			
		||||
                  ><Delete
 | 
			
		||||
                /></TMagicIcon>
 | 
			
		||||
                ></TMagicButton>
 | 
			
		||||
 | 
			
		||||
                <TMagicButton
 | 
			
		||||
                  v-if="copyable(scope.$index + 1 + pagecontext * pagesize - 1)"
 | 
			
		||||
                  link
 | 
			
		||||
                  size="small"
 | 
			
		||||
                  type="primary"
 | 
			
		||||
                  title="复制"
 | 
			
		||||
                  :icon="DocumentCopy"
 | 
			
		||||
                  :disabled="disabled"
 | 
			
		||||
                  @click="copyHandler(scope.$index + 1 + pagecontext * pagesize - 1)"
 | 
			
		||||
                ></TMagicButton>
 | 
			
		||||
              </template>
 | 
			
		||||
            </TMagicTableColumn>
 | 
			
		||||
 | 
			
		||||
@ -189,13 +203,12 @@
 | 
			
		||||
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { computed, inject, onMounted, ref, toRefs, watchEffect } from 'vue';
 | 
			
		||||
import { ArrowDown, ArrowUp, Delete, FullScreen, Grid } from '@element-plus/icons-vue';
 | 
			
		||||
import { ArrowDown, ArrowUp, Delete, DocumentCopy, FullScreen, Grid } from '@element-plus/icons-vue';
 | 
			
		||||
import { cloneDeep } from 'lodash-es';
 | 
			
		||||
import Sortable, { SortableEvent } from 'sortablejs';
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
  TMagicButton,
 | 
			
		||||
  TMagicIcon,
 | 
			
		||||
  tMagicMessage,
 | 
			
		||||
  TMagicPagination,
 | 
			
		||||
  TMagicTable,
 | 
			
		||||
@ -550,6 +563,22 @@ const showDelete = (index: number) => {
 | 
			
		||||
  return true;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const copyable = (index: number) => {
 | 
			
		||||
  const copyableFunc = props.config.copyable;
 | 
			
		||||
  if (copyableFunc && typeof copyableFunc === 'function') {
 | 
			
		||||
    return copyableFunc(mForm, {
 | 
			
		||||
      values: mForm?.initValues || {},
 | 
			
		||||
      model: props.model,
 | 
			
		||||
      parent: mForm?.parentValues || {},
 | 
			
		||||
      formValue: mForm?.values || props.model,
 | 
			
		||||
      prop: props.prop,
 | 
			
		||||
      config: props.config,
 | 
			
		||||
      index,
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
  return true;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const clearHandler = () => {
 | 
			
		||||
  const len = props.model[modelName.value].length;
 | 
			
		||||
  props.model[modelName.value].splice(0, len);
 | 
			
		||||
@ -594,6 +623,10 @@ const handleCurrentChange = (val: number) => {
 | 
			
		||||
  pagecontext.value = val - 1;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const copyHandler = (index: number) => {
 | 
			
		||||
  props.model[modelName.value].push(cloneDeep(props.model[modelName.value][index]));
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const toggleMode = () => {
 | 
			
		||||
  const calcLabelWidth = (label: string) => {
 | 
			
		||||
    if (!label) return '0px';
 | 
			
		||||
 | 
			
		||||
@ -684,6 +684,7 @@ export interface TableConfig extends FormItem {
 | 
			
		||||
  addable?: (mForm: FormState | undefined, data: any) => boolean | 'undefined' | boolean;
 | 
			
		||||
  /** 是否显示删除按钮 */
 | 
			
		||||
  delete?: (model: any, index: number, values: any) => boolean | boolean;
 | 
			
		||||
  copyable?: (model: any, data: any) => boolean | boolean;
 | 
			
		||||
  /** 是否显示导入按钮 */
 | 
			
		||||
  importable?: (mForm: FormState | undefined, data: any) => boolean | 'undefined' | boolean;
 | 
			
		||||
  /** 是否显示checkbox */
 | 
			
		||||
@ -718,12 +719,14 @@ export interface GroupListConfig extends FormItem {
 | 
			
		||||
  addable?: (mForm: FormState | undefined, data: any) => boolean | 'undefined' | boolean;
 | 
			
		||||
  defaultAdd?: (mForm: FormState | undefined, data: any) => any;
 | 
			
		||||
  delete?: (model: any, index: number | string | symbol, values: any) => boolean | boolean;
 | 
			
		||||
  copyable?: FilterFunction<boolean>;
 | 
			
		||||
  movable?: (
 | 
			
		||||
    mForm: FormState | undefined,
 | 
			
		||||
    index: number | string | symbol,
 | 
			
		||||
    model: any,
 | 
			
		||||
    groupModel: any,
 | 
			
		||||
  ) => boolean | boolean;
 | 
			
		||||
  moveSpecifyLocation?: boolean;
 | 
			
		||||
  [key: string]: any;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -62,9 +62,4 @@
 | 
			
		||||
  .el-form-item {
 | 
			
		||||
    margin-bottom: 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .m-table-delete-icon {
 | 
			
		||||
    color: #f56c6c;
 | 
			
		||||
    cursor: pointer;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -211,6 +211,7 @@ export default createForm([
 | 
			
		||||
  {
 | 
			
		||||
    type: 'table',
 | 
			
		||||
    name: 'table',
 | 
			
		||||
    copyable: true,
 | 
			
		||||
    defautSort: { prop: 'name', order: 'descending' },
 | 
			
		||||
    extra: 'extra',
 | 
			
		||||
    itemExtra: (vm: any, { model }: any): any => `${model.text}itemExtra`,
 | 
			
		||||
@ -232,6 +233,8 @@ export default createForm([
 | 
			
		||||
    type: 'groupList',
 | 
			
		||||
    name: 'groupList',
 | 
			
		||||
    extra: '分组xxxxxxxxxxxx',
 | 
			
		||||
    copyable: true,
 | 
			
		||||
    moveSpecifyLocation: true,
 | 
			
		||||
    itemExtra: (vm: any, { model }: any) => `${model.name}extra`,
 | 
			
		||||
    items: [
 | 
			
		||||
      {
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user