aceEditor.vue 4.25 KB
<template>
  <div class="flex flex-col justify-between">
    <Tag v-if="scriptType === OnlineEditorTypeEnum.JAVASCRIPT" color="blue" class="tag-text-top">
      <span>function</span>&nbsp;&nbsp;filter(res)&nbsp;&nbsp;{
    </Tag>
    <div class="mt-2 mb-2" ref="aceRef"></div>
    <Tag
      v-if="scriptType === OnlineEditorTypeEnum.JAVASCRIPT"
      color="blue"
      class="mt-2 tag-text-bottom"
      >}</Tag
    >
    <div v-if="scriptType === OnlineEditorTypeEnum.JAVASCRIPT" class="ml-2 -mt-1.5">
      <Button @click="onHandleFormatter" class="mt-8 -ml-2">
        <template #icon>
          <CopyOutlined />
        </template>
        格式化
      </Button>
    </div>
  </div>
</template>

<script lang="ts" setup name="aceEditor">
  import { ref, computed, onMounted, nextTick, watch } from 'vue';
  import ace from 'ace-builds';
  import 'ace-builds/src-noconflict/theme-chrome'; // 默认设置的主题
  import 'ace-builds/src-noconflict/theme-terminal'; // 默认设置的主题
  import 'ace-builds/src-noconflict/mode-javascript'; // 默认设置的语言模式javascript
  import 'ace-builds/src-noconflict/snippets/xml'; // 设置xmL
  import { beautify } from 'ace-builds/src-noconflict/ext-beautify.js';
  import { useAppStore } from '/@/store/modules/app';
  import { CopyOutlined } from '@ant-design/icons-vue';
  import { Button, Tag } from 'ant-design-vue';
  import { OnlineEditorTypeEnum } from '../../../config/enum';

  const emits = defineEmits(['changeAceContent']);

  const props = defineProps({
    restData: {
      type: Object,
    },
    scriptType: {
      type: String,
      default: 'javascript',
    },
  });

  const aceEditor = ref();

  const aceRef = ref();

  const userStore = useAppStore();

  const getAceClass = computed((): string => userStore.getDarkMode);

  const getRestData: any = ref(null);

  watch(
    () => props.restData,
    (newVal) => {
      getRestData.value = newVal;
      onHandleFormatter();
    },
    { deep: true }
  );

  // 初始化编辑器
  const initEditor = () => {
    aceEditor.value = ace.edit(aceRef.value, {
      maxLines: 50, // 最大行数,超过会自动出现滚动条
      minLines: props.scriptType === OnlineEditorTypeEnum.JAVASCRIPT ? 36 : 14, // 最小行数,还未到最大行数时,编辑器会自动伸缩大小
      fontSize: 14, // 编辑器内字体大小
      theme: 'ace/theme/chrome', // 默认设置的主题
      mode:
        props.scriptType === OnlineEditorTypeEnum.JAVASCRIPT
          ? 'ace/mode/javascript'
          : 'ace/mode/xml', // 默认设置的语言模式
      tabSize: 2, // 制表符设置为 4 个空格大小
    });

    aceEditor.value.setOptions({
      enableBasicAutocompletion: true,
      enableLiveAutocompletion: true,
      enableSnippets: true,
      enableEmmet: true,
      theme: getAceClass.value === 'dark' ? 'ace/theme/terminal' : 'ace/theme/chrome',
    });
    doFilter();
    beautify(aceEditor.value.session);
  };

  const doFilter = () => {
    aceEditor.value.getSession().on('change', async () => {
      try {
        await nextTick();
        const jsCode = aceEditor.value.getValue();
        const res = getRestData.value ?? props.restData;
        const fn = new Function('res', jsCode)(res);
        emits('changeAceContent', fn);
      } catch (error) {
        return `过滤函数错误,日志:${error}`;
      }
    });
  };

  const onHandleFormatter = () => {
    let oldValue = aceEditor.value?.getValue() || '';
    oldValue = oldValue.replaceAll(/;(\n+)?/g, ';\n');
    aceEditor.value?.setValue(oldValue);
    beautify(aceEditor.value.session);
  };

  const getValue = () => {
    return aceEditor?.value?.getValue();
  };

  const setValue = async (data) => {
    await nextTick();
    return aceEditor?.value?.setValue(data);
  };

  onMounted(() => {
    initEditor();
  });

  defineExpose({
    getValue,
    setValue,
  });
</script>

<style lang="less" scoped>
  .jsoneditor {
    border: none;
  }

  .tag-text {
    white-space: normal;
    height: auto;
  }

  .tag-text-top {
    width: 8vw;
    &:extend(.tag-text);
  }

  .tag-text-bottom {
    width: 1vw;
    &:extend(.tag-text);
  }
</style>