dataParse.vue 7.3 KB

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