Browse Source

添加跳转微信小程序功能

一只小菜杨 2 months ago
parent
commit
6fe5c5b2ed

+ 2 - 0
components.d.ts

@@ -19,6 +19,7 @@ declare module 'vue' {
     VanField: typeof import('vant/es')['Field']
     VanForm: typeof import('vant/es')['Form']
     VanIcon: typeof import('vant/es')['Icon']
+    VanLoading: typeof import('vant/es')['Loading']
     VanNumberKeyboard: typeof import('vant/es')['NumberKeyboard']
     VanPicker: typeof import('vant/es')['Picker']
     VanPopup: typeof import('vant/es')['Popup']
@@ -28,6 +29,7 @@ declare module 'vue' {
     VanSwipeItem: typeof import('vant/es')['SwipeItem']
     VanTabbar: typeof import('vant/es')['Tabbar']
     VanTabbarItem: typeof import('vant/es')['TabbarItem']
+    VanTag: typeof import('vant/es')['Tag']
     VanTimePicker: typeof import('vant/es')['TimePicker']
   }
 }

+ 36 - 0
src/router/index.ts

@@ -30,4 +30,40 @@ const router = createRouter({
   ]
 })
 
+const getWxConfig = (url: string) => {
+  fetch('https://plceditor.worldflying.cn/api/build/getjsconfig', {
+    method: 'POST',
+    body: JSON.stringify({ url })
+  })
+    .then((res) => res.json())
+    .then((res) => {
+      if (res.errcode === 0) {
+        localStorage.setItem(url, JSON.stringify(res.data))
+        // wx.config(res.data)
+        // wx.ready(function () {
+        //   console.log('微信初始化成功')
+        // })
+        // wx.error(function (res: any) {
+        //   console.log('微信初始化失败', res)
+        // })
+      } else {
+        showFailToast('获取配置信息失败!')
+      }
+    })
+}
+
+router.beforeEach((to, from, next) => {
+  const url = location.origin + location.pathname + location.search
+  // console.log(url)
+  const wxConfigStr = localStorage.getItem(url)
+  if (!wxConfigStr) getWxConfig(url)
+  else {
+    const wxConfig = JSON.parse(wxConfigStr)
+    const crtTimestamp = Date.now() / 1000 - 10 * 60
+    if (crtTimestamp > wxConfig.timestamp) getWxConfig(url)
+  }
+
+  next()
+})
+
 export default router

+ 3 - 1
src/stores/global.ts

@@ -83,7 +83,9 @@ export const useGlobalStore = defineStore(
               }
             ]
           }
-        ]
+        ],
+        isCompiled: false,
+        CompileTime: ''
       }
     ])
 

+ 2 - 0
src/types/global.d.ts

@@ -33,5 +33,7 @@ export type ProjectType = {
   name: string
   id: string
   time: string
+  isCompiled: boolean
+  CompileTime: string
   commandList: CommandType[]
 }

+ 31 - 0
src/utils/tools.ts

@@ -14,6 +14,37 @@ export const deepClone = <T>(obj: T): T => {
   return clonedObj
 }
 
+// 深度比较两个对象的值是否相等
+export const deepEqual = (a: any, b: any) => {
+  if (!a || !b) return false
+  if (a === b) return true
+  if (a !== a && b !== b) return true // 处理 NaN 的特殊情况
+  if (typeof a !== 'object' || a === null || typeof b !== 'object' || b === null) {
+    return false
+  }
+
+  const isArrayA = Array.isArray(a)
+  const isArrayB = Array.isArray(b)
+  if (isArrayA !== isArrayB) return false
+  if (isArrayA) {
+    console.log(a.length, b.length, a, b)
+
+    if (a.length !== b.length) return false
+    for (let i = 0; i < a.length; i++) {
+      if (!deepEqual(a[i], b[i])) return false
+    }
+    return true
+  }
+  const keysA = Object.keys(a)
+  const keysB = Object.keys(b)
+  if (keysA.length !== keysB.length) return false
+  for (const key of keysA) {
+    if (!b.hasOwnProperty(key)) return false // 确保存在该属性
+    if (!deepEqual(a[key], b[key])) return false // 递归比较值
+  }
+  return true
+}
+
 export const formatDateTime = (date: string | Date | number, joinStr = ' ') => {
   if (!date) return
   const dateObj = new Date(date)

+ 4 - 1
src/views/Automate.vue

@@ -47,6 +47,7 @@
             v-model="newProjectName"
             name="newProjectName"
             placeholder="工程名称"
+            maxlength="20"
             :rules="[{ required: true, message: '请填写工程名称' }]"
           />
         </van-cell-group>
@@ -115,7 +116,9 @@ const onSubmit = (values: Record<string, string>) => {
       name: values.newProjectName,
       id: Date.now() + '',
       time: new Date().toLocaleString(),
-      commandList: []
+      commandList: [],
+      isCompiled: false,
+      CompileTime: ''
     })
     handleCancel()
   }

+ 14 - 3
src/views/CommandInfo.vue

@@ -165,7 +165,7 @@
 <script setup lang="ts">
 import { useGlobalStore } from '@/stores/global'
 import type { CommandType, ConditionType, ProjectType } from '@/types/global'
-import { deepClone } from '@/utils/tools'
+import { deepClone, deepEqual } from '@/utils/tools'
 import { storeToRefs } from 'pinia'
 import { v4 as uuid4 } from 'uuid'
 import { showConfirmDialog } from 'vant'
@@ -177,7 +177,6 @@ const { projectList } = storeToRefs(useGlobalStore())
 
 const crtCommand = ref<CommandType>()
 const crtProject = ref<ProjectType>()
-
 const textMap: Record<string, string> = {
   condition: '条件',
   delay: '延时',
@@ -188,7 +187,7 @@ const isExistCnt = ref(false)
 watch(
   () => route,
   () => {
-    crtProject.value= projectList.value.find((item) => item.id === route.params.pid)
+    crtProject.value = projectList.value.find((item) => item.id === route.params.pid)
     if (crtProject.value) {
       const command = crtProject.value.commandList!.find((item) => item.id === route.params.id)!
       if (command.stepList.length) {
@@ -449,6 +448,18 @@ const handleSaveCommand = () => {
   const project = projectList.value.find((item) => item.id === route.params.pid)
   if (project) {
     const command = project.commandList!.find((item) => item.id === route.params.id)
+    console.log(crtProject.value?.isCompiled)
+
+    if (project.isCompiled) {
+      const isCompiled = deepEqual(
+        project.commandList.find((item) => item.id === crtCommand.value?.id),
+        crtCommand.value
+      )
+      if (!isCompiled) {
+        localStorage.removeItem('imgobj' + crtProject.value!.id)
+      }
+      project!.isCompiled = isCompiled
+    }
     if (command) {
       command.stepList = crtCommand.value!.stepList
       showSuccessToast('保存成功')

+ 104 - 44
src/views/ProjectInfo.vue

@@ -4,7 +4,13 @@
       <div class="go-back" @click="router.replace({ name: 'Automate' })">
         <van-button size="small"><van-icon name="arrow-left" />返回</van-button>
       </div>
-      <div class="project-name">{{ crtProject!.name }}</div>
+      <div class="project-name">
+        <div class="name">
+          {{ crtProject!.name }}
+        </div>
+        <van-tag type="primary" v-show="crtProject?.isCompiled" size="medium">已编译</van-tag>
+        <van-tag type="warning" v-show="!crtProject?.isCompiled" size="medium">未编译</van-tag>
+      </div>
       <div class="more"><van-button type="primary" size="small" @click="showMorePopup = true">更多</van-button></div>
     </div>
 
@@ -27,17 +33,19 @@
       </van-swipe-cell>
     </div>
     <div class="add-btn">
-      <div style="text-align: center;">{{project_status}}</div>
-      <div v-html="wxtag"></div>
       <van-button type="primary" block @click="handleAddCommond">添加任务</van-button>
     </div>
 
     <van-popup v-model:show="showMorePopup" position="right" :style="{ width: '50%', height: '100%' }">
       <van-cell-group :style="{ marginTop: '16px' }">
-        <van-cell title="编辑工程名称" clickable @click="handleEditName" />
-        <van-cell title="编译文件" @click="getXMLFile" clickable />
-        <van-cell title="设备烧录" @click="burnToDevice" clickable />
-        <van-cell title="删除工程" @click="delProject" clickable />
+        <van-cell title="编辑工程名称" clickable @click="handleEditName" :title-style="{ color: '#07c160' }" />
+        <van-cell title="编译文件" @click="getXMLFile" clickable :title-style="{ color: '#1989fa' }" />
+        <van-cell clickable v-show="crtProject?.isCompiled">
+          <template #title>
+            <div v-html="wxtag"></div>
+          </template>
+        </van-cell>
+        <van-cell title="删除工程" @click="delProject" clickable :title-style="{ color: '#fd5632' }" />
       </van-cell-group>
     </van-popup>
 
@@ -50,6 +58,7 @@
             v-model="newProjectName"
             name="newProjectName"
             placeholder="工程名称"
+            maxlength="20"
             :rules="[{ required: true, message: '请填写工程名称' }]"
           />
         </van-cell-group>
@@ -65,10 +74,10 @@
 <script setup lang="ts">
 import { useGlobalStore } from '@/stores/global'
 import type { CommandType, ProjectType } from '@/types/global'
-import { formatDateTime } from '@/utils/tools'
+import { formatDateTime, deepEqual, deepClone } from '@/utils/tools'
 import { storeToRefs } from 'pinia'
 import { v4 as uuid4 } from 'uuid'
-import { showConfirmDialog } from 'vant'
+import { showConfirmDialog, showLoadingToast, showFailToast } from 'vant'
 
 const route = useRoute()
 const router = useRouter()
@@ -76,38 +85,55 @@ const router = useRouter()
 const { projectList } = storeToRefs(useGlobalStore())
 const crtProject = ref<ProjectType>()
 
-const project_status = ref('未编译')
 const wxtag = ref('')
 
 let wx = window.wx
-console.log('wx', wx)
-
-fetch('https://plceditor.worldflying.cn/api/build/getjsconfig', {
-  method: 'POST',
-  body: JSON.stringify({url: location.origin + location.pathname + location.search})
-}).then((res) => res.json()).then((res) => {
-  wx.config(res.data)
-  wx.ready(function () {
-    console.log('微信初始化成功')
-  })
-  wx.error(function(res:any){
-    console.log('微信初始化失败', res)
-  })
-})
+// fetch('https://plceditor.worldflying.cn/api/build/getjsconfig', {
+//   method: 'POST',
+//   body: JSON.stringify({ url: location.origin + location.pathname + location.search })
+// })
+//   .then((res) => res.json())
+//   .then((res) => {
+//     wx.config(res.data)
+//     wx.ready(function () {
+//       console.log('微信初始化成功')
+//       showWxAppBtn.value = true
+//     })
+//     wx.error(function (res: any) {
+//       console.log('微信初始化失败', res)
+//     })
+//   })
+const initWxEnv = () => {
+  const url = location.origin + location.pathname + location.search
+  const wxConfigStr = localStorage.getItem(url)
+  if (!wxConfigStr) showFailToast('初始化失败')
+  else {
+    const wxConfig = JSON.parse(wxConfigStr)
+    wx.config(wxConfig)
+    wx.ready(function () {
+      console.log('微信初始化成功')
+    })
+    wx.error(function (res: any) {
+      console.log('微信初始化失败', res)
+    })
+  }
+}
+initWxEnv()
 
 watch(
   () => route,
   () => {
     crtProject.value = projectList.value.find((item) => item.id === route.params.id)
-    // commandList.value = crtProject.value!.commandList
     let imgobjStr = localStorage.getItem('imgobj' + crtProject.value!.id)
     if (imgobjStr) {
       try {
         let imgobj = JSON.parse(imgobjStr)
         let now = new Date().getTime() // 当前时间戳
         if (now < imgobj.deadline) {
-          project_status.value = '已编译'
-          wxtag.value = '<wx-open-launch-weapp appid="wx4c5a777c71f2981c" path="pages/index/rj45cfgbybt/plc/index?imgurl=' + imgobj.url + '"><template><button>烧录设备</button></template></wx-open-launch-weapp>'
+          crtProject.value!.isCompiled = true
+          wxtag.value = `<wx-open-launch-weapp appid="wx4c5a777c71f2981c" path="pages/index/rj45cfgbybt/plc/index?imgurl=${imgobj.url}"><template><text style="color: #7232dd">烧录设备</text></template></wx-open-launch-weapp>`
+        } else {
+          crtProject.value!.isCompiled = false
         }
       } catch (e) {
         console.log(e)
@@ -172,6 +198,10 @@ const handleDelete = (val: string) => {
     const project = projectList.value.find((item) => (item.id = crtProject.value!.id))
     if (project) {
       project.commandList = project.commandList.filter((item) => item.id !== val)
+      if (crtProject.value?.isCompiled) {
+        crtProject.value.isCompiled = false
+        localStorage.removeItem('imgobj' + crtProject.value!.id)
+      }
     }
   }
 }
@@ -192,17 +222,31 @@ const handleAddCommond = () => {
     y: 100 + crtProject.value!.commandList.length * 2000,
     parentId: crtProject.value!.id
   })
+  if (crtProject.value?.isCompiled) {
+    crtProject.value.isCompiled = false
+    localStorage.removeItem('imgobj' + crtProject.value!.id)
+  }
 }
 
 // 文件编译
 const getXMLFile = () => {
   if (!crtProject.value) return
-  if (crtProject.value.commandList.length === 0) return showFailToast('请先添加任务')
+  if (crtProject.value.commandList.length === 0) return showFailToast('请先添加任务!')
   if (crtProject.value.commandList.length === 1 && crtProject.value.commandList[0].stepList.length === 0) {
-    return showFailToast('任务内容为空')
+    return showFailToast('任务内容为空!')
+  }
+  if (crtProject.value.isCompiled) {
+    return showFailToast('工程已编译!')
   }
-  const isExistExec = crtProject.value.commandList.some((item) => item.stepList.some((cItem) => cItem.type === 'exec' && cItem.list?.length !== 0))
-  if (!isExistExec) return showFailToast('请先添加执行任务')
+  const isExistExec = crtProject.value.commandList.some((item) =>
+    item.stepList.some((cItem) => cItem.type === 'exec' && cItem.list?.length !== 0)
+  )
+  if (!isExistExec) return showFailToast('请先添加执行任务!')
+  const loading = showLoadingToast({
+    message: '正在编译中...',
+    forbidClick: true,
+    duration: 0
+  })
   let xml = ''
   // 头部
   xml += `<?xml version="1.0" encoding="UTF-8"?>
@@ -278,7 +322,7 @@ const getXMLFile = () => {
                       </variable>`
   })
   crtProject.value.commandList.forEach((item, index) => {
-    if (!item.stepList.some(cItem => cItem.type === 'exec' && cItem.list?.length !== 0)) return
+    if (!item.stepList.some((cItem) => cItem.type === 'exec' && cItem.list?.length !== 0)) return
     instanceXml += `<pouInstance name="instance${index}" typeName="program${index}"/>`
     xml += `<pou name="program${index}" pouType="program">
               <interface>
@@ -1413,18 +1457,23 @@ const getXMLFile = () => {
     .then((res) => res.json())
     .then((res) => {
       console.log(res)
+      // 关闭loading
+      loading.close()
       if (res.errcode === 3000) {
-        console.log(res.errmsg)
+        showFailToast('编译失败!')
       } else if (res.errcode === 0) {
-        localStorage.setItem('imgobj' + crtProject.value!.id, JSON.stringify({
-          url: res.url,
-          deadline: new Date().getTime() + 30*60*1000
-        }))
-        project_status.value = '已编译'
-        let w = '<wx-open-launch-weapp appid="wx4c5a777c71f2981c" path="pages/index/rj45cfgbybt/plc/index?imgurl=' + res.url + '"><template><button>烧录设备</button></template></wx-open-launch-weapp>'
-        console.log(w)
-        wxtag.value = w
+        localStorage.setItem(
+          'imgobj' + crtProject.value!.id,
+          JSON.stringify({
+            url: res.url,
+            deadline: new Date().getTime() + 30 * 60 * 1000
+          })
+        )
+        crtProject.value!.isCompiled = true
+        crtProject.value!.CompileTime = formatDateTime(new Date())!
+        wxtag.value = `<wx-open-launch-weapp appid="wx4c5a777c71f2981c" path="pages/index/rj45cfgbybt/plc/index?imgurl=${res.url}"><template><text style="color: #7232dd">烧录设备</text></template></wx-open-launch-weapp>`
         console.log('本地存储成功')
+        showSuccessToast('编译成功!')
       }
     })
 
@@ -1436,9 +1485,6 @@ const getXMLFile = () => {
   // link.remove()
 }
 
-// 设备烧录
-const burnToDevice = () => {}
-
 // 删除工程
 const delProject = () => {
   if (crtProject.value) {
@@ -1500,6 +1546,20 @@ const getSTFile = () => {
   justify-content: space-between;
   align-items: center;
 }
+.header .project-name {
+  height: 100%;
+  max-width: 70%;
+  display: flex;
+  gap: 4px;
+  align-items: center;
+  justify-content: center;
+}
+.header .project-name .name {
+  overflow: hidden;
+  white-space: nowrap;
+  text-overflow: ellipsis;
+  max-width: 70%;
+}
 .command-container {
   height: calc(100% - 65px);
   padding-bottom: 16px;