dataParse.vue 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. <template>
  2. <div class="data-parse-wrapper" style="position: relative" ref="wrapperRef">
  3. <div class="full-screen-btn">
  4. <!-- fullScreen -->
  5. <el-icon v-if="!isFullScreen" @click="toggleFullScreen" :title="$t('message.device.fullScreen')"><FullScreen /></el-icon>
  6. <!-- exitFullScreen -->
  7. <el-icon v-else @click="toggleFullScreen" :title="$t('message.device.exitFullScreen')"><Close /></el-icon>
  8. </div>
  9. <div class="flex" style="align-items: stretch">
  10. <codeEditor class="params flex1" ref="mirrorRef" style="height: calc(100vh - 310px)" mode="javascript" :content="script || emptyFunction"></codeEditor>
  11. <div class="mock" style="width: 300px; margin-left: 20px">
  12. <el-radio-group v-model="functionName">
  13. <el-radio-button label="parse">parse</el-radio-button>
  14. <el-radio-button label="send">send</el-radio-button>
  15. </el-radio-group>
  16. <!-- 请输入入参,以字符串的方式,如果是对象字符串会在执行时自动转换为对象再执行 -->
  17. <el-input
  18. class="input"
  19. v-model="inputData"
  20. type="textarea"
  21. :placeholder="$t('message.device.tip1')"
  22. ></el-input>
  23. <!-- 此处显示执行结果 -->
  24. <el-input class="input" v-model="outputData" type="textarea" readonly :placeholder="$t('message.device.tip2')"></el-input>
  25. </div>
  26. </div>
  27. <!-- 保存脚本 -->
  28. <el-button type="primary" style="margin-top: 20px" v-auth="'save'" @click="saveCode">{{ $t('message.device.tip3') }}</el-button>
  29. <!-- 调试 -->
  30. <el-button type="primary" style="margin-top: 20px" v-auth="'debug'" :loading="runing" @click="mock">{{ $t('message.device.tip4') }}</el-button>
  31. </div>
  32. </template>
  33. <script lang="ts" setup>
  34. import { onMounted, ref, onUnmounted, computed } from 'vue'
  35. import codeEditor from '/@/components/codeEditor/index.vue'
  36. import { ElMessage } from 'element-plus'
  37. import api from '/@/api/device'
  38. import { useRoute } from 'vue-router'
  39. import { FullScreen, Close } from '@element-plus/icons-vue'
  40. import { useI18n } from 'vue-i18n'
  41. const { t } = useI18n()
  42. // tip5: "下面是预制的空的方法,请不要修改函数名称,直接在内部编写函数即可",
  43. // tip6: "此处是设备功能调用应答数据解析",
  44. // tip7: "此处编写对数据的处理",
  45. // tip8: "此处是设备功能调用发送数据解析",
  46. // tip9: "此处编写对数据的处理",
  47. // tip10: "请先输入可执行脚本",
  48. const emptyFunction = computed(() =>
  49. `
  50. // ${t('message.device.tip5')}
  51. // ${t('message.device.tip6')}
  52. function parse (data) {
  53. // ${t('message.device.tip7')}
  54. return data
  55. }
  56. // ${t('message.device.tip8')}
  57. function send (data) {
  58. // ${t('message.device.tip9')}
  59. return data
  60. }
  61. `
  62. );
  63. const route = useRoute()
  64. const emit = defineEmits(['updateScript'])
  65. defineProps({
  66. script: String,
  67. })
  68. const inputData = ref('')
  69. const outputData = ref('')
  70. const functionName = ref('parse')
  71. const runing = ref(false)
  72. const mirrorRef = ref()
  73. const isFullScreen = ref(false)
  74. const wrapperRef = ref()
  75. onMounted(() => {
  76. // 添加全屏变化事件监听
  77. document.addEventListener('fullscreenchange', handleFullscreenChange)
  78. document.addEventListener('webkitfullscreenchange', handleFullscreenChange)
  79. document.addEventListener('mozfullscreenchange', handleFullscreenChange)
  80. document.addEventListener('MSFullscreenChange', handleFullscreenChange)
  81. })
  82. onUnmounted(() => {
  83. document.removeEventListener('fullscreenchange', handleFullscreenChange)
  84. document.removeEventListener('webkitfullscreenchange', handleFullscreenChange)
  85. document.removeEventListener('mozfullscreenchange', handleFullscreenChange)
  86. document.removeEventListener('MSFullscreenChange', handleFullscreenChange)
  87. })
  88. // 切换全屏状态
  89. const toggleFullScreen = () => {
  90. if (!isFullScreen.value) {
  91. // 进入全屏
  92. const element = wrapperRef.value! as HTMLElement
  93. if (element) {
  94. if (element.requestFullscreen) {
  95. element.requestFullscreen()
  96. } else if ((element as any).webkitRequestFullscreen) {
  97. // Safari
  98. (element as any).webkitRequestFullscreen()
  99. } else if ((element as any).msRequestFullscreen) {
  100. // IE11
  101. (element as any).msRequestFullscreen()
  102. }
  103. }
  104. } else {
  105. // 退出全屏
  106. if (document.exitFullscreen) {
  107. document.exitFullscreen()
  108. } else if ((document as any).webkitExitFullscreen) {
  109. // Safari
  110. (document as any).webkitExitFullscreen()
  111. } else if ((document as any).msExitFullscreen) {
  112. // IE11
  113. (document as any).msExitFullscreen()
  114. }
  115. }
  116. isFullScreen.value = !isFullScreen.value
  117. }
  118. // 处理全屏状态变化
  119. const handleFullscreenChange = () => {
  120. const isDocFullscreen =
  121. document.fullscreenElement ||
  122. (document as any).webkitFullscreenElement ||
  123. (document as any).mozFullScreenElement ||
  124. (document as any).msFullscreenElement
  125. // 如果文档不在全屏状态,但我们的状态是全屏,则更新状态
  126. if (!isDocFullscreen && isFullScreen.value) {
  127. isFullScreen.value = false
  128. }
  129. }
  130. async function saveCode() {
  131. const funStr = mirrorRef.value.getValue()
  132. if (funStr === '') {
  133. return toSave(funStr)
  134. }
  135. await validate()
  136. toSave(funStr)
  137. }
  138. function toSave(data: string) {
  139. api.product
  140. .script({
  141. key: route.params.id,
  142. scriptInfo: data,
  143. })
  144. .then(() => {
  145. ElMessage.success(t('message.tableI18nConfirm.saveSuccess'))
  146. emit('updateScript', data)
  147. })
  148. }
  149. function validate() {
  150. return new Promise((resolve, reject) => {
  151. const funStr = mirrorRef.value.getValue()
  152. const worker = new Worker('/worker.js')
  153. // 向 Worker 发送消息
  154. worker.postMessage({ type: 'validateFunctionString', functionString: funStr })
  155. // 监听 Worker 返回的消息
  156. worker.addEventListener('message', ({ data }) => {
  157. worker.terminate()
  158. if (data.isOk) return resolve(true)
  159. ElMessage.error(data.message)
  160. reject()
  161. })
  162. })
  163. }
  164. async function mock() {
  165. outputData.value = ''
  166. await validate()
  167. // 请输入参数
  168. if (!inputData.value) return ElMessage.error(t('message.device.formI18nPlaceholder.inputParameter'))
  169. const funStr = mirrorRef.value.getValue()
  170. if (funStr === '') {
  171. // 请先输入可执行脚本
  172. return ElMessage.error(t('message.device.tip10'))
  173. }
  174. // 远程调试
  175. runing.value = true
  176. api.product
  177. .scriptRun({
  178. functionName: functionName.value,
  179. scriptInfo: funStr,
  180. jsonData: inputData.value,
  181. })
  182. .then((res: any) => {
  183. // outputData.value = typeof res.data === 'object' ? JSON.stringify(res.data, null, 2) : res.data
  184. outputData.value = res.data
  185. })
  186. .finally(() => {
  187. runing.value = false
  188. })
  189. // 本地调试
  190. // const worker = new Worker('./worker.js');
  191. // // 向 Worker 发送消息
  192. // worker.postMessage({ type: 'runFunction', functionString: funStr, functionName: functionName.value, params: inputData.value });
  193. // // 监听 Worker 返回的消息
  194. // worker.addEventListener('message', ({ data }) => {
  195. // worker.terminate()
  196. // if (data.isOk) {
  197. // return outputData.value = typeof data.data === 'object' ? JSON.stringify(data.data, null, 2) : data.data
  198. // }
  199. // ElMessage.error(data.message)
  200. // });
  201. }
  202. </script>
  203. <style scoped lang="scss">
  204. .flex {
  205. display: flex;
  206. }
  207. .flex1 {
  208. flex: 1;
  209. }
  210. .mock {
  211. .input {
  212. margin-top: 20px;
  213. height: calc(50vh - 200px);
  214. }
  215. &.hidden {
  216. display: none;
  217. }
  218. }
  219. .full-screen-btn {
  220. position: absolute;
  221. top: 2px;
  222. right: 2px; /* 考虑到右侧 mock 区域的宽度 */
  223. z-index: 100;
  224. cursor: pointer;
  225. padding: 4px;
  226. background-color: rgba(255, 255, 255, 0.9);
  227. border-radius: 4px;
  228. box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
  229. transition: all 0.3s;
  230. &:hover {
  231. background-color: #f2f6fc;
  232. }
  233. .el-icon {
  234. font-size: 18px;
  235. color: #409eff;
  236. display: block;
  237. }
  238. }
  239. .is-fullscreen {
  240. position: fixed !important;
  241. top: 0;
  242. left: 0;
  243. right: 0;
  244. bottom: 0;
  245. z-index: 9999;
  246. height: 100vh !important;
  247. width: 100vw;
  248. background-color: #fff;
  249. padding: 20px;
  250. box-sizing: border-box;
  251. }
  252. .data-parse-wrapper {
  253. &:fullscreen {
  254. padding: 20px;
  255. background-color: white;
  256. box-sizing: border-box;
  257. .full-screen-btn {
  258. right: 20px;
  259. top: 20px;
  260. }
  261. .mock {
  262. display: block;
  263. }
  264. }
  265. &::-webkit-full-screen {
  266. padding: 20px;
  267. background-color: white;
  268. box-sizing: border-box;
  269. .full-screen-btn {
  270. right: 20px;
  271. top: 20px;
  272. }
  273. .mock {
  274. display: block;
  275. }
  276. }
  277. }
  278. </style>