ConverScript.vue 9.81 KB
<template>
  <div>
    <a-form
      ref="formRef"
      :model="scriptForm"
      name="basic"
      :label-col="{ span: 4 }"
      :wrapper-col="{ span: 16 }"
      autocomplete="off"
    >
      <a-form-item
        :label="ifAdd ? '名称' : '输入参数(params)'"
        :name="ifAdd ? 'name' : 'params'"
        :rules="[{ required: true, message: ifAdd ? '请输入脚本名称' : '请输入参数' }]"
      >
        <a-input-group compact>
          <a-input
            style="width: calc(100% - 200px)"
            v-if="ifAdd"
            :maxlength="36"
            @change="handleInputChange"
            v-model:value="scriptForm.name"
            placeholder="请输入脚本名称"
          />
          <a-input
            @change="handleInputChange"
            v-else
            v-model:value="scriptForm.params"
            placeholder="请输入参数"
          />
          <a-button @click="onHandleClick(text)" v-show="ifAdd && !view" type="primary">
            测试
          </a-button>
        </a-input-group>
      </a-form-item>
      <a-form-item
        label="脚本类型"
        name="scriptType"
        :rules="[{ required: true, message: '请选择脚本类型' }]"
      >
        <a-space direction="vertical">
          <a-radio-group
            @change="handleScriptType"
            v-model:value="scriptForm.scriptType"
            :options="scriptTypeOptions"
          />
        </a-space>
      </a-form-item>
      <a-form-item label="脚本内容" :name="ifAdd ? 'convertJs' : 'script'">
        <Card title="脚本内容" :bodyStyle="{ padding: 0, height: '280px' }">
          <template #extra>
            <Button
              v-show="ifAdd && !view"
              @click="onHandleTestExample(scriptForm.scriptType)"
              style="position: relative; top: -4rem; right: -6.6rem"
              type="primary"
            >
              测试用例</Button
            >
            <a-button @click="handleFormat" size="small">格式化</a-button>
            <Tooltip
              v-if="scriptForm.scriptType !== 'TRANSPORT_TCP_UP'"
              :title="defaultAuthTitle"
              class="ml-2"
            >
              <QuestionCircleOutlined style="font-size: 1rem" />
            </Tooltip>
            <Tooltip v-else :title="defaultUpTitle" class="ml-2">
              <QuestionCircleOutlined style="font-size: 1rem" />
            </Tooltip>
          </template>
          <div ref="aceRef" class="overflow-hidden"></div>
        </Card>
        <Button @click="handleCopy" class="mt-4">
          <template #icon>
            <CopyOutlined />
          </template>
          copy
        </Button>
      </a-form-item>
      <a-form-item
        :label="ifAdd ? '备注' : '输出参数(output)'"
        :name="ifAdd ? 'description' : 'output'"
      >
        <a-textarea
          :rows="3"
          v-if="ifAdd"
          v-model:value="scriptForm.description"
          placeholder="请输入备注"
          :maxlength="255"
        />
        <a-textarea
          :rows="5"
          v-else
          v-model:value="scriptForm.output"
          placeholder="输出参数为服务端返回的内容"
          :maxlength="255"
        />
      </a-form-item>
    </a-form>
    <TestScriptModal @register="registerModal" />
  </div>
</template>
<script setup lang="ts">
  import { ref, unref, reactive, onMounted, toRefs, computed } from 'vue';
  import ace from 'ace-builds';
  import { Card, Button, Tooltip } from 'ant-design-vue';
  import 'ace-builds/src-noconflict/theme-chrome'; // 默认设置的主题
  import 'ace-builds/src-noconflict/theme-terminal'; // 默认设置的主题
  import 'ace-builds/src-noconflict/mode-javascript'; // 默认设置的语言模式
  import { beautify } from 'ace-builds/src-noconflict/ext-beautify.js';
  import { CopyOutlined } from '@ant-design/icons-vue';
  import { useCopyToClipboard } from '/@/hooks/web/useCopyToClipboard';
  import { useMessage } from '/@/hooks/web/useMessage';
  import { findDictItemByCode } from '/@/api/system/dict';
  import { QuestionCircleOutlined } from '@ant-design/icons-vue';
  import {
    defaultAuthTitle,
    defaultUpTitle,
    defaultScriptTypeContent,
    defaultTestUpExample,
    defaultTestAuthExample,
  } from './config.data';
  import { useAppStore } from '/@/store/modules/app';
  import TestScriptModal from './TestScriptModal.vue';
  import { useModal } from '/@/components/Modal';

  defineEmits(['register']);
  const props = defineProps({
    ifAdd: { type: Boolean, default: true },
    text: { type: String, default: '' },
    view: { type: Boolean, default: false },
  });

  const scriptForm = reactive({
    name: '',
    description: '',
    convertJs: '',
    script: '',
    params: '',
    output: '',
    scriptType: 'TRANSPORT_TCP_UP',
  });

  const reportTypeOptions = reactive({
    scriptTypeOptions: [],
  });

  const { scriptTypeOptions } = toRefs(reportTypeOptions);

  const { createMessage } = useMessage();

  const { clipboardRef, copiedRef } = useCopyToClipboard();

  const aceEditor = ref();

  const aceRef = ref();

  const userStore = useAppStore();

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

  const setDefaultRadio = (p3) => {
    scriptForm.scriptType = p3;
  };

  const getDictValue = async (dict_type) => {
    const res = await findDictItemByCode({
      dictCode: dict_type,
    });
    return res.map((m) => {
      return { label: m.itemText, value: m.itemValue };
    });
  };

  onMounted(async () => {
    reportTypeOptions.scriptTypeOptions = (await getDictValue('script_type')) as never as any;
  });

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

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

  const handleScriptType = ({ target }) => {
    const { value } = target;
    scriptForm.scriptType = value;
    switchScriptTypeGetContent(value);
  };

  const switchScriptTypeGetContent = (type) => {
    aceEditor.value.setValue(defaultScriptTypeContent[type]);
  };

  const onHandleTestExample = (example) => {
    if (example === 'TRANSPORT_TCP_UP') {
      aceEditor.value?.setValue(defaultTestUpExample);
    } else {
      aceEditor.value?.setValue(defaultTestAuthExample);
    }
    handleFormat();
  };

  const handleCopy = () => {
    const valueRef = aceEditor.value.getValue();
    const value = unref(valueRef);
    if (!value) {
      createMessage.warning('请输入要拷贝的内容!');
      return;
    }
    clipboardRef.value = value;
    if (unref(copiedRef)) {
      createMessage.success('复制成功!');
    }
  };

  const formRef = ref();

  const getFormData = async () => {
    const value = await formRef.value.validateFields();
    if (props.ifAdd) {
      scriptForm.convertJs = aceEditor.value.getValue();
      if (scriptForm.convertJs == '') {
        createMessage.error('请编写脚本内容');
        throw '请编写脚本内容';
      }
    } else {
      scriptForm.script = aceEditor.value.getValue();
      if (scriptForm.script == '') {
        createMessage.error('请编写脚本内容');
        throw '请编写脚本内容';
      }
    }
    if (!value) return;
    if (scriptForm.params) {
      const trimParams = scriptForm.params.replace(/\s*/g, '');
      Reflect.set(value, 'params', trimParams);
    }
    if (scriptForm.convertJs.length > 1000) {
      createMessage.error('脚本内容长度不能大于1000');
      throw '脚本内容长度不能大于1000';
    }
    return {
      ...value,
      ...{ convertJs: props.ifAdd ? scriptForm.convertJs : null },
      ...{ script: !props.ifAdd ? scriptForm.script : null },
    };
  };

  const handleInputChange = (e) => {
    const trimParams = e.target.value.replace(/\s*/g, '');
    Reflect.set(scriptForm, 'params', trimParams);
    if (scriptForm.scriptType === 'TRANSPORT_TCP_DOWN') {
      aceEditor.value.setValue(`out.datas = "${scriptForm.params}";out.deviceName = "sensor";`);
    }
  };

  const getRecordId = ref('');

  const setFormData = (v) => {
    if (v) {
      getRecordId.value = v?.id;
      for (let i in scriptForm) {
        Reflect.set(scriptForm, i, v[i]);
      }
      aceEditor.value.setValue(v.convertJs);
      handleFormat();
    }
  };

  const setScriptContentData = (v) => {
    aceEditor.value.setValue(v);
    handleFormat();
  };

  const resetFormData = () => {
    for (let i in scriptForm) {
      Reflect.set(scriptForm, i, '');
    }
  };

  const setScriptOutputData = (v) => {
    scriptForm.output = v;
  };

  const handleFormat = () => {
    beautify(aceEditor.value.session);
    aceEditor.value.getSession().setUseWrapMode(true);
  };

  const [registerModal, { openModal }] = useModal();

  const onHandleClick = (o) => {
    const getTestContent = aceEditor.value?.getValue();
    openModal(true, {
      isAuth: '',
      isUpdate: o,
      record: o === 'add' ? null : getRecordId.value,
      testContent: getTestContent,
      isTest: true,
      isText: 'test',
      isTitle: 'test',
    });
  };

  defineExpose({
    initEditor,
    getFormData,
    resetFormData,
    setFormData,
    setScriptContentData,
    setScriptOutputData,
    setDefaultRadio,
  });
</script>
<style lang="less" scoped>
  @import url('./ConverScriptModal.less');
</style>