Procházet zdrojové kódy

Merge branch 'refs/heads/20240604-dev' into 20240904-zyy

周玉佂 před 2 týdny
rodič
revize
5f56414fd8

+ 4 - 1
.env.development

@@ -16,7 +16,10 @@ VITE_APP_BASE_API = '/dev-api'
 VITE_APP_API_URL = 'http://192.168.1.4:8107'
 
 # 家里本地
-# VITE_APP_API_URL = 'http://192.168.1.3:8107'
+# VITE_APP_API_URL = 'http://192.168.1.11:8107'
 
 # 是否启用 Mock 服务
 VITE_MOCK_DEV_SERVER = false
+
+# WebSocket 服务地址
+VITE_WEBSOCKET_SERVER = 'ws://localhost:52025'

+ 3 - 0
.env.production

@@ -4,3 +4,6 @@ VITE_NODE_ENV = 'production'
 # 代理前缀
 VITE_APP_BASE_API = '/prod-api'
 
+# WebSocket 服务地址
+VITE_WEBSOCKET_SERVER = 'ws://localhost:52025'
+

+ 7 - 8
index.html

@@ -35,20 +35,19 @@
     }
 
     .loader {
-      --d: 22px;
-
-      width: 4px;
-      height: 4px;
+      --d: 440px;
+      width: 80px;
+      height: 80px;
       color: #25b09b;
       border-radius: 50%;
       box-shadow:
         calc(1 * var(--d)) calc(0 * var(--d)) 0 0,
-        calc(0.707 * var(--d)) calc(0.707 * var(--d)) 0 1px,
+        calc(0.707 * var(--d)) calc(0.707 * var(--d)) 0 20px,
         calc(0 * var(--d)) calc(1 * var(--d)) 0 2px,
-        calc(-0.707 * var(--d)) calc(0.707 * var(--d)) 0 3px,
+        calc(-0.707 * var(--d)) calc(0.707 * var(--d)) 0 60px,
         calc(-1 * var(--d)) calc(0 * var(--d)) 0 4px,
-        calc(-0.707 * var(--d)) calc(-0.707 * var(--d)) 0 5px,
-        calc(0 * var(--d)) calc(-1 * var(--d)) 0 6px;
+        calc(-0.707 * var(--d)) calc(-0.707 * var(--d)) 0 100px,
+        calc(0 * var(--d)) calc(-1 * var(--d)) 0 120px;
       animation: l27 1s infinite steps(8);
     }
 

binární
public/static/image/game/avatar/icon-31.png


binární
public/static/image/game/avatar/icon-32.png


+ 33 - 0
src/App.vue

@@ -13,6 +13,13 @@
     <!-- 关闭水印 -->
     <router-view v-else />
   </el-config-provider>
+
+  <div
+    v-if="winFlag && winFlag.length > 0"
+    class="fixed z-99999 text-85px bottom-0 left-18px text-[#d3d3d34c] select-none"
+  >
+    {{ winFlag }}
+  </div>
 </template>
 
 <script setup lang="ts">
@@ -21,6 +28,9 @@ import defaultSettings from '@/settings'
 import { ThemeEnum } from '@/enums/ThemeEnum'
 import { SizeEnum } from '@/enums/SizeEnum'
 
+import { getCurrentInstance } from 'vue'
+const instance = getCurrentInstance()
+
 const appStore = useAppStore()
 const settingsStore = useSettingsStore()
 
@@ -32,4 +42,27 @@ const watermarkEnabled = computed(() => settingsStore.watermarkEnabled)
 const fontColor = computed(() => {
   return settingsStore.theme === ThemeEnum.DARK ? 'rgba(255, 255, 255, .15)' : 'rgba(0, 0, 0, .15)'
 })
+
+const winFlag = ref('')
+onMounted(() => {
+  instance?.proxy?.$Bus.on('tow-win-show-is-main-win', (val: any) => {
+    if (val === 'YES' && (window.location.href.includes(':5588') || window.location.href.includes(':5589'))) {
+      winFlag.value = window.location.href.includes('win=main') ? '主' : '副'
+    } else {
+      winFlag.value = ''
+    }
+  })
+  let tempWinFlag = localStorage.getItem('tow-win-show-is-main-win')
+  if (tempWinFlag && tempWinFlag === 'YES') {
+    winFlag.value = window.location.href.includes('win=main') ? '主' : '副'
+  } else {
+    winFlag.value = ''
+  }
+})
+
+onUnmounted(() => {
+  winFlag.value = ''
+  localStorage.removeItem('tow-win-show-is-main-win')
+  instance?.proxy?.$Bus.off('tow-win-show-is-main-win')
+})
 </script>

+ 18 - 0
src/layout/components/NavBar/components/NavbarRight.vue

@@ -42,7 +42,9 @@
 import { useAppStore, useTagsViewStore, useUserStore, useSettingsStore } from '@/store'
 import defaultSettings from '@/settings'
 import { DeviceEnum } from '@/enums/DeviceEnum'
+import { useSocket } from '@/utils/websoket'
 
+const { socket } = useSocket()
 const appStore = useAppStore()
 const tagsViewStore = useTagsViewStore()
 const userStore = useUserStore()
@@ -75,6 +77,22 @@ function logout() {
       })
   })
 }
+
+onMounted(() => {
+  socket.on('message', (data: any) => {
+    // 若Electron关闭窗口,则注销并退出系统
+    if (data === 'close-win-after') {
+      userStore
+        .logout()
+        .then(() => {
+          tagsViewStore.delAllViews()
+        })
+        .then(() => {
+          router.push(`/login?redirect=${route.fullPath}`)
+        })
+    }
+  })
+})
 </script>
 <style lang="scss" scoped>
 .setting-item {

+ 7 - 0
src/styles/index.scss

@@ -115,6 +115,13 @@
         backdrop-filter: blur(4px); /* 背景模糊 */
         -webkit-backdrop-filter: blur(4px); /* 兼容老版本 Safari */
       }
+
+      .el-dialog {
+        .el-dialog__body{
+          width: auto;
+          height: auto;
+        }
+      }
     }
   }
 

+ 3 - 2
src/utils/websoket.ts

@@ -50,7 +50,7 @@ class Socket {
   }
   //收到的WebSocket消息
   onMessage(event: MessageEvent) {
-    console.log('WebSocket message received:', event.data);
+    console.log('WebSocket message received:', event.data)
     this.emit('message', event.data)
   }
   //错误
@@ -116,7 +116,8 @@ class Socket {
   }
 }
 
-export function useSocket(url: string = `ws://192.168.1.5:54378`, opts?: SocketOptions) {
+export function useSocket(url: string = import.meta.env.VITE_WEBSOCKET_SERVER, opts?: SocketOptions) {
+  console.log('websocket-url: ', url)
   const socket = new Socket(url, opts)
 
   onUnmounted(() => {

+ 4 - 5
src/views/gameCenter/components/Introduction/index.vue

@@ -80,7 +80,7 @@
       <!-- 工作记忆 WorkingMemory -->
       <working-memory v-if="gameFlag.WorkingMemory" @game-over="gameOver" />
 
-      <cocos-dialog v-if="gameFlag.CocosDialog" :code="cocosType" @game-over="gameOver" />
+      <cocos-dialog v-if="gameFlag.CocosDialog" :code="cocosType" :url="cocosUrl" @game-over="gameOver" />
 
       <voice-imp ref="VoiceImpRef" />
     </div>
@@ -133,6 +133,7 @@ const gameFlag = ref<StrKeyObj>({
   SetTransferTest: false // 定式转移测验
 })
 const cocosType = ref('')
+const cocosUrl = ref('')
 const VoiceImpRef = ref()
 
 const beginGame = () => {
@@ -148,11 +149,9 @@ const beginGame = () => {
         tempPlanId = JSON.parse(currentPlanInfo).id
       }
     }
+    cocosUrl.value = `${window.location.origin}/cocos/${cocosType.value}/?userID=${userStore.user.id}&planID=${tempPlanId}`
     VoiceImpRef.value.pauseIntro()
-    window.open(
-      `${window.location.origin}/cocos/${cocosType.value}/?userID=${userStore.user.id}&planID=${tempPlanId}`,
-      '_blank'
-    )
+    gameFlag.value.CocosDialog = true
   } else {
     VoiceImpRef.value.pauseIntro()
     gameFlag.value[props.gameInfo.code] = true

+ 13 - 8
src/views/gameCenter/components/games/CocosDialog/index.vue

@@ -1,9 +1,8 @@
 <template>
-  <my-full-screen-dialog ref="openDialogRef" close-color="#134FA4" @close-dialog="handleClose">
+  <my-full-screen-dialog ref="openDialogRef" close-color="#134FA4" :show-close="false" @close-dialog="handleClose">
     <div class="cocos-container w-full h-full flex flex-col items-center text-[#134FA4]">
-      <iframe :src="`/src/views/gameCenter/cocos/${props.code}/build/pack/index.html`" frameborder="0"></iframe>
-<!--      <iframe :src="`/public/cocos/${props.code}/build/pack/index.html`" frameborder="0"></iframe>-->
-<!--      <iframe src="http://192.168.0.39:7457/web-mobile/pack/index.html" frameborder="0"></iframe>-->
+      <iframe :src="props.url" frameborder="0"></iframe>
+      <!--<iframe src="http://192.168.1.10:7456/web-mobile/ADL/index.html" frameborder="0"></iframe>-->
     </div>
   </my-full-screen-dialog>
 </template>
@@ -22,18 +21,25 @@ const props = defineProps({
     type: String,
     required: true,
     default: ''
+  },
+  url: {
+    type: String,
+    required: true,
+    default: ''
   }
 })
 const $emits = defineEmits(['gameOver'])
 const openDialogRef = ref()
 const handleClose = (done: () => void) => {
-  $emits('gameOver', 'COCOSDialog')
+  $emits('gameOver', 'CocosDialog')
 }
 
 async function exec() {
   openDialogRef.value.openDialog()
-  window.addEventListener('message',(e) => {
-    console.log(e)
+  window.addEventListener('message', (e: MessageEvent) => {
+    if (e.data === 'close') {
+      handleClose(() => {})
+    }
   })
 }
 
@@ -44,7 +50,6 @@ onMounted(() => {
 
 <style scoped lang="scss">
 .cocos-container {
-
   iframe {
     width: 100%;
     height: 100%;

+ 13 - 9
src/views/gameCenter/manage/index.vue

@@ -281,15 +281,19 @@ function resetForm() {
 
 const handlePlay = (row: any) => {
   if (row.status === '1') {
-    if (row.code.includes('Cocos')) {
-      let tempCode = row.code.replace('Cocos-', '')
-      const userStore = useUserStore()
-      window.open(`${window.location.origin}/cocos/${tempCode}/?userID=${userStore.user.id}&planID=`, '_blank')
-    } else {
-      gameInfo.value = row
-      showCurrentCode.value = row.code
-      showIntroFlag.value = true
-    }
+    gameInfo.value = row
+    showCurrentCode.value = row.code
+    showIntroFlag.value = true
+
+    // if (row.code.includes('Cocos')) {
+    //   let tempCode = row.code.replace('Cocos-', '')
+    //   const userStore = useUserStore()
+    //   window.open(`${window.location.origin}/cocos/${tempCode}/?userID=${userStore.user.id}&planID=`, '_blank')
+    // } else {
+    //   gameInfo.value = row
+    //   showCurrentCode.value = row.code
+    //   showIntroFlag.value = true
+    // }
   } else {
     ElMessage.warning('该游戏暂未完成,敬请期待!')
   }

+ 168 - 69
src/views/tester/components/RehabilitationEvaluation/CognitiveAbilityTask/CognitiveAbilityTaskDrawClock/index.vue

@@ -20,7 +20,8 @@
         @mouseup="stopDrawing"
       ></canvas>
 
-      <div>
+      <div v-if="isMainWin" class="absolute wh-full top-0 left-0 z-3000"></div>
+      <div v-else>
         <div class="font-600 text-[20px] mb-[12px]">画笔颜色:</div>
         <div class="w-[270px] flex flex-row flex-wrap">
           <div
@@ -44,111 +45,146 @@
     <!-- 选择文化程度 -->
     <el-dialog
       v-model="dialogVisible"
-      width="30%"
+      width="24%"
       :show-close="false"
       :modal-append-to-body="false"
       :close-on-click-modal="false"
-      class="h-[800px]"
     >
-      <div class="font-600 text-[18px] mb-[20px]">请评分:</div>
+      <div class="font-600 text-[18px] mb-[20px]">{{ isMainWin ? '' : '医生' }}评分:</div>
       <div class="h-[690px] overflow-y-auto">
         <h3>1. 锚定“12.3.6.9”四个点</h3>
-        <el-radio-group v-model="radios[0]">
+        <el-radio-group v-if="isMainWin" v-model="radios[0]">
           <el-radio value="0">0分</el-radio>
           <el-radio value="1">1分</el-radio>
           <el-radio value="2">2分</el-radio>
           <el-radio value="3">3分</el-radio>
           <el-radio value="4">4分</el-radio>
         </el-radio-group>
+        <div v-else class="h-[18px] mt-4px text-[#089BAB] text-18px w-[50%] text-right">
+          <span v-if="radios[0]">{{ radios[0] }} 分</span>
+        </div>
         <h3 style="margin-top: 5px">2. 写出所有数字</h3>
-        <el-radio-group v-model="radios[1]">
+        <el-radio-group v-if="isMainWin" v-model="radios[1]">
           <el-radio value="0">0分</el-radio>
           <el-radio value="1">1分</el-radio>
           <el-radio value="2">2分</el-radio>
           <el-radio value="3">3分</el-radio>
           <el-radio value="4">4分</el-radio>
         </el-radio-group>
+        <div v-else class="h-[18px] mt-4px text-[#089BAB] text-18px w-[50%] text-right">
+          <span v-if="radios[1]">{{ radios[1] }} 分</span>
+        </div>
         <h3 style="margin-top: 5px">3. 所有数字在钟面圆圈内</h3>
-        <el-radio-group v-model="radios[2]">
+        <el-radio-group v-if="isMainWin" v-model="radios[2]">
           <el-radio value="0">0分</el-radio>
           <el-radio value="1">1分</el-radio>
           <el-radio value="2">2分</el-radio>
           <el-radio value="3">3分</el-radio>
         </el-radio-group>
+        <div v-else class="h-[18px] mt-4px text-[#089BAB] text-18px w-[50%] text-right">
+          <span v-if="radios[2]">{{ radios[2] }} 分</span>
+        </div>
         <h3 style="margin-top: 5px">4. 顺时针排列</h3>
-        <el-radio-group v-model="radios[3]">
+        <el-radio-group v-if="isMainWin" v-model="radios[3]">
           <el-radio value="0">0分</el-radio>
           <el-radio value="1">1分</el-radio>
         </el-radio-group>
+        <div v-else class="h-[18px] mt-4px text-[#089BAB] text-18px w-[50%] text-right">
+          <span v-if="radios[3]">{{ radios[3] }} 分</span>
+        </div>
         <h3 style="margin-top: 5px">5. 1-12数字次序</h3>
-        <el-radio-group v-model="radios[4]">
+        <el-radio-group v-if="isMainWin" v-model="radios[4]">
           <el-radio value="0">0分</el-radio>
           <el-radio value="1">1分</el-radio>
         </el-radio-group>
+        <div v-else class="h-[18px] mt-4px text-[#089BAB] text-18px w-[50%] text-right">
+          <span v-if="radios[4]">{{ radios[4] }} 分</span>
+        </div>
         <h3 style="margin-top: 5px">6.“12,3,6,9”分布对称</h3>
-        <el-radio-group v-model="radios[5]">
+        <el-radio-group v-if="isMainWin" v-model="radios[5]">
           <el-radio value="0">0分</el-radio>
           <el-radio value="1">1分</el-radio>
           <el-radio value="2">2分</el-radio>
         </el-radio-group>
+        <div v-else class="h-[18px] mt-4px text-[#089BAB] text-18px w-[50%] text-right">
+          <span v-if="radios[5]">{{ radios[5] }} 分</span>
+        </div>
         <h3 style="margin-top: 5px">7. 其他8个数字位置</h3>
-        <el-radio-group v-model="radios[6]">
+        <el-radio-group v-if="isMainWin" v-model="radios[6]">
           <el-radio value="0">0分</el-radio>
           <el-radio value="1">1分</el-radio>
           <el-radio value="2">2分</el-radio>
           <el-radio value="3">3分</el-radio>
         </el-radio-group>
+        <div v-else class="h-[18px] mt-4px text-[#089BAB] text-18px w-[50%] text-right">
+          <span v-if="radios[6]">{{ radios[6] }} 分</span>
+        </div>
         <h3 style="margin-top: 5px">8. 中央点位置</h3>
-        <el-radio-group v-model="radios[7]">
+        <el-radio-group v-if="isMainWin" v-model="radios[7]">
           <el-radio value="0">0分</el-radio>
           <el-radio value="1">1分</el-radio>
         </el-radio-group>
+        <div v-else class="h-[18px] mt-4px text-[#089BAB] text-18px w-[50%] text-right">
+          <span v-if="radios[7]">{{ radios[7] }} 分</span>
+        </div>
         <h3 style="margin-top: 5px">9. 钟面完整</h3>
-        <el-radio-group v-model="radios[8]">
+        <el-radio-group v-if="isMainWin" v-model="radios[8]">
           <el-radio value="0">0分</el-radio>
           <el-radio value="1">1分</el-radio>
         </el-radio-group>
+        <div v-else class="h-[18px] mt-4px text-[#089BAB] text-18px w-[50%] text-right">
+          <span v-if="radios[8]">{{ radios[8] }} 分</span>
+        </div>
         <h3 style="margin-top: 5px">10. 有时针和分针</h3>
-        <el-radio-group v-model="radios[9]">
+        <el-radio-group v-if="isMainWin" v-model="radios[9]">
           <el-radio value="0">0分</el-radio>
           <el-radio value="1">1分</el-radio>
           <el-radio value="2">2分</el-radio>
         </el-radio-group>
+        <div v-else class="h-[18px] mt-4px text-[#089BAB] text-18px w-[50%] text-right">
+          <span v-if="radios[9]">{{ radios[9] }} 分</span>
+        </div>
         <h3 style="margin-top: 5px">11. 时针指向正确</h3>
-        <el-radio-group v-model="radios[10]">
+        <el-radio-group v-if="isMainWin" v-model="radios[10]">
           <el-radio value="0">0分</el-radio>
           <el-radio value="1">1分</el-radio>
           <el-radio value="2">2分</el-radio>
         </el-radio-group>
+        <div v-else class="h-[18px] mt-4px text-[#089BAB] text-18px w-[50%] text-right">
+          <span v-if="radios[10]">{{ radios[10] }} 分</span>
+        </div>
         <h3 style="margin-top: 5px">12. 分针指向正确</h3>
-        <el-radio-group v-model="radios[11]">
+        <el-radio-group v-if="isMainWin" v-model="radios[11]">
           <el-radio value="0">0分</el-radio>
           <el-radio value="1">1分</el-radio>
           <el-radio value="2">2分</el-radio>
         </el-radio-group>
+        <div v-else class="h-[18px] mt-4px text-[#089BAB] text-18px w-[50%] text-right">
+          <span v-if="radios[11]">{{ radios[11] }} 分</span>
+        </div>
         <h3 style="margin-top: 5px">13. 分针比时针长</h3>
-        <el-radio-group v-model="radios[12]">
+        <el-radio-group v-if="isMainWin" v-model="radios[12]">
           <el-radio value="0">0分</el-radio>
           <el-radio value="1">1分</el-radio>
           <el-radio value="2">2分</el-radio>
         </el-radio-group>
+        <div v-else class="h-[18px] mt-4px text-[#089BAB] text-18px w-[50%] text-right">
+          <span v-if="radios[12]">{{ radios[12] }} 分</span>
+        </div>
         <h3 style="margin-top: 5px">14. 时针和分针都有箭头</h3>
-        <el-radio-group v-model="radios[13]">
+        <el-radio-group v-if="isMainWin" v-model="radios[13]">
           <el-radio value="0">0分</el-radio>
           <el-radio value="1">1分</el-radio>
           <el-radio value="2">2分</el-radio>
         </el-radio-group>
+        <div v-else class="h-[18px] mt-4px text-[#089BAB] text-18px w-[50%] text-right">
+          <span v-if="radios[13]">{{ radios[13] }} 分</span>
+        </div>
       </div>
-      <div class="absolute right-[20px] bottom-[20px]">
-        <el-button type="primary" plain @click="dialogVisible = false">关 闭</el-button>
-        <el-button :loading="loading" type="primary" @click="handleSubmit">确 定</el-button>
+      <div v-if="isMainWin" class="flex-center mt-10px">
+        <el-button :disabled="loading" type="primary" plain class="mr-24px" @click="closeDialog">关 闭</el-button>
+        <el-button :loading="loading" :disabled="loading" type="primary" @click="handleSubmit">确 定</el-button>
       </div>
-
-      <!--      <template #footer>-->
-      <!--        <div class="dialog-footer text-right">-->
-      <!--          <el-button :loading="loading" type="primary" @click="handleSubmit">确 定</el-button>-->
-      <!--        </div>-->
-      <!--      </template>-->
     </el-dialog>
   </section>
 </template>
@@ -167,7 +203,7 @@ import { useSocket } from '@/utils/websoket'
 import { isJSON } from '@/utils'
 const { socket, wsSend, wsOn, wsOff } = useSocket()
 
-const isPatient = ref(false)
+const isMainWin = ref(false)
 
 defineOptions({
   name: 'CognitiveAbilityTaskDrawClock',
@@ -192,11 +228,6 @@ const dialogVisible = ref(false) // 学历弹窗
 const btnActive = ref(-1)
 const colors = ref(['#fef4ac', '#0018ba', '#ffc200', '#f32f15', '#cccccc', '#5ab639'])
 const imgUrl = ref('')
-const preDrawAry = ref<ImageData[]>([])
-const middleAry = ref<ImageData[]>([])
-const nextDrawAry = ref<ImageData[]>([])
-
-const radios = ref([])
 
 const changeColor = (color: string, index: number) => {
   btnActive.value = index
@@ -211,6 +242,9 @@ let isPainting = false
 let lastX = 0
 let lastY = 0
 let ctx: CanvasRenderingContext2D | null = null
+const preDrawAry = ref<ImageData[]>([])
+const middleAry = ref<ImageData[]>([])
+const nextDrawAry = ref<ImageData[]>([])
 
 const startDrawing = (event: MouseEvent) => {
   if (!isPainting && ctx) {
@@ -231,6 +265,7 @@ const draw = (event: MouseEvent) => {
     ctx.lineTo(event.offsetX, event.offsetY)
     ctx.stroke()
     ;[lastX, lastY] = [event.offsetX, event.offsetY]
+    canvasToBase64ToStorage(0)
   }
 }
 
@@ -288,20 +323,36 @@ function controlCanvas(action: string) {
       radios.value = []
       break
   }
+  canvasToBase64ToStorage(1)
 }
 
 const getImage = () => {
   imgUrl.value = paintCanvas.value?.toDataURL('image/png') ?? ''
   dialogVisible.value = true
-  wsSend(
-    JSON.stringify({
-      dataSource: 'drawClock',
-      msg: 'ping-fen-dialog-show',
-      currentPatId: userStore.user.id
-    })
-  )
+
+  localStorage.setItem('tow-win-draw-clock-result-arr-reset', 'reset')
+  localStorage.setItem('tow-win-draw-clock-show-dialog', 'YES')
+}
+
+const closeDialog = () => {
+  dialogVisible.value = false
+  localStorage.setItem('tow-win-draw-clock-show-dialog', 'NO')
 }
 
+const radios = ref([])
+watch(
+  () => radios.value,
+  (newVal) => {
+    localStorage.setItem('tow-win-draw-clock-result-arr', JSON.stringify(newVal))
+  },
+  {
+    deep: true
+  }
+)
+
+/**
+ * 提交操作
+ */
 const handleSubmit = () => {
   if (
     radios.value[0] == null ||
@@ -330,8 +381,7 @@ const handleSubmit = () => {
     grade += parseInt(radios.value[i])
   }
 
-  loading.value = false
-  let tempObj = {}
+  loading.value = true
   EvaluationSTRecordInfoAPI.addCognition({
     parentId: props.parentId,
     patId: userStore.user.id,
@@ -360,8 +410,11 @@ const handleSubmit = () => {
       loading.value = false
       ElMessage.success('提交成功!')
       emits('showResult', data)
+
+      localStorage.setItem('tow-win-draw-clock-show-result', JSON.stringify(data))
     })
     .catch(() => {
+      ElMessage.error('提交失败!')
       loading.value = false
     })
 }
@@ -375,46 +428,92 @@ const countDownEnd = () => {
 }
 
 async function exec() {
-  isPatient.value = ['5588', '5589'].includes(window.location.port)
-  if (isPatient.value) {
+  isMainWin.value = window.location.href.includes('win=main')
+  if (isMainWin.value) {
+    countDownEnd()
+  } else {
     setTimeout(() => {
       countDownBegin.value = true
     }, 2300)
-  } else {
-    countDownEnd()
   }
 }
 
 onMounted(() => {
   exec()
-})
 
-// ----------------------------------- WebSocket -----------------------------------
-wsOn('close', () => console.log('Socket closed!'))
-//webSocket连接上服务器时
-wsOn('open', (event: Event) => {
-  console.log('webSocket连接上服务器时')
-})
-socket.on('message', (data: any) => {
-  console.log('Received data:', data)
-  if (data && isJSON(data)) {
-    let message = JSON.parse(data)
-    if (isPatient.value) {
-      console.log('------')
+  window.addEventListener('storage', (val) => {
+    if (isMainWin.value) {
+      if (val.key === 'tow-win-draw-clock-img-data') {
+        let tempVal = JSON.parse(val.newValue!)
+        drawBase64ToCanvas(tempVal.optFlag, tempVal.url)
+      }
+
+      if (val.key === 'tow-win-draw-clock-show-dialog' && val.newValue === 'YES') {
+        dialogVisible.value = true
+        localStorage.removeItem('tow-win-draw-clock-show-dialog')
+      }
+
+      if (val.key === 'tow-win-draw-clock-result-arr-reset') {
+        radios.value = []
+        localStorage.removeItem('tow-win-draw-clock-result-arr-reset')
+      }
     } else {
-      // 当 医生端 接到 患者端 从 认画钟测验 页面发出来的 开始评分 请求时
-      // 根据患者的ID作为判断依据
-      // 医生端弹出评分框
-      if (message.dataSource === 'drawClock') {
-        // TODO  医生端打开患者记录前 在缓存里面存储下该患者的真实ID 然后在这个地方调取
-        // if (message.msg === 'ping-fen-dialog-show' && message.currentPatId === ) {
-        //   dialogVisible.value = true
-        // }
+      if (val.key === 'tow-win-draw-clock-result-arr') {
+        radios.value = JSON.parse(val.newValue!)
+        localStorage.removeItem('tow-win-draw-clock-result-arr')
+      }
+
+      if (val.key === 'tow-win-draw-clock-show-dialog' && val.newValue === 'NO') {
+        dialogVisible.value = false
+        localStorage.removeItem('tow-win-draw-clock-show-dialog')
+      }
+
+      if (val.key === 'tow-win-draw-clock-show-result') {
+        ElMessage.success('提交成功!')
+        emits('showResult', JSON.parse(val.newValue!))
+        localStorage.removeItem('tow-win-draw-clock-show-result')
       }
     }
-  }
+  })
+})
+
+onUnmounted(() => {
+  localStorage.removeItem('tow-win-draw-clock-img-data')
+  window.removeEventListener('storage', () => {})
 })
-// ----------------------------------- WebSocket -----------------------------------
+
+/**
+ * 画布数据转base64并保存到localStorage
+ * @param optFlag 0:不清空画布  1:清空画布
+ */
+function canvasToBase64ToStorage(optFlag: number) {
+  console.log(paintCanvas.value?.toDataURL('image/png') ?? '')
+  localStorage.setItem(
+    'tow-win-draw-clock-img-data',
+    JSON.stringify({
+      optFlag: optFlag,
+      url: paintCanvas.value?.toDataURL('image/png') ?? ''
+    })
+  )
+}
+
+/**
+ * base64转图片并绘制到画布
+ * @param optFlag 0:不清空画布  1:清空画布
+ * @param base64Data
+ */
+function drawBase64ToCanvas(optFlag: number, base64Data: string) {
+  if (optFlag === 1 && ctx) {
+    // 清空整个 Canvas
+    ctx.clearRect(0, 0, paintCanvas.value!.width!, paintCanvas.value!.height!)
+  }
+  const img = new Image()
+  img.onload = () => {
+    ctx?.drawImage(img, 0, 0)
+  }
+  img.src = base64Data
+  localStorage.removeItem('tow-win-draw-clock-img-data')
+}
 </script>
 
 <style scoped lang="scss">

+ 35 - 36
src/views/tester/components/RehabilitationEvaluation/CognitiveAbilityTask/index.vue

@@ -1,5 +1,10 @@
 <template>
-  <my-full-screen-dialog ref="openDialogRef" close-color="#089BAB" @close-dialog="handleClose">
+  <my-full-screen-dialog
+    ref="openDialogRef"
+    close-color="#089BAB"
+    :show-close="showCloseFlag"
+    @close-dialog="handleClose"
+  >
     <!-- 画钟测试 -->
     <cognitive-ability-task-draw-clock
       v-if="props.cognitionId === '33' && taskBegin"
@@ -78,11 +83,11 @@
  * 编写者: JutarryWu
  */
 import { getCurrentInstance } from 'vue'
+import { useUserStore } from '@/store'
 
 import { useSocket } from '@/utils/websoket'
 import { isJSON } from '@/utils'
-import { useUserStore } from '@/store'
-const { socket, wsSend, wsOn, wsOff } = useSocket()
+const { wsSend } = useSocket()
 
 const props = defineProps({
   cognitionId: {
@@ -99,16 +104,38 @@ const props = defineProps({
 })
 const userStore = useUserStore()
 const instance = getCurrentInstance()
-const emits = defineEmits(['close'])
+const emits = defineEmits(['close', 'closeAll'])
 const openDialogRef = ref()
 const taskBegin = ref(false)
 
+const showCloseFlag = ref(true)
 const detailVisible = ref(false)
 const resultId = ref()
 
-const handleClose = (done: () => void) => {
+/**
+ * 关闭窗口
+ * @param closeFlag 0: 关闭窗口,1: 关闭窗口,返回列表
+ * @returns
+ */
+const handleClose = (closeFlag: number = 0) => {
   taskBegin.value = false
-  emits('close')
+
+  // 移除主副窗口标识
+  instance?.proxy?.$Bus.emit('tow-win-show-is-main-win')
+  localStorage.removeItem('tow-win-show-is-main-win')
+
+  wsSend(
+    JSON.stringify({
+      dataSource: 'RehabilitationEvaluationLogIntro',
+      msg: 'close-other-win'
+    })
+  )
+
+  if (closeFlag === 0) {
+    emits('close')
+  } else {
+    emits('closeAll')
+  }
 }
 
 const showResult = (val: string) => {
@@ -120,7 +147,7 @@ const closeDrawer = () => {
   detailVisible.value = false
   instance?.proxy?.$Bus.emit('RehabilitationEvaluationLogDetail-refresh')
   setTimeout(() => {
-    handleClose(() => {})
+    handleClose(1)
   }, 500)
 }
 
@@ -131,37 +158,9 @@ async function exec() {
 }
 
 onMounted(() => {
+  showCloseFlag.value = !window.location.href.includes('win=main')
   exec()
 })
-
-// ----------------------------------- WebSocket -----------------------------------
-wsOn('close', () => console.log('Socket closed!'))
-//webSocket连接上服务器时
-wsOn('open', (event: Event) => {
-  console.log('webSocket连接上服务器时')
-})
-socket.on('message', (data: any) => {
-  console.log('Received data:', data)
-  if (data && isJSON(data)) {
-    let message = JSON.parse(data)
-    if (message.dataSource === 'cognitionMgrList') {
-      // 当 患者端 接到 医生端 从 认知任务管理列表页面发出来的查询当前患者正在进行的评估任务 请求时
-      // 发送当前任务的真实ID
-      // 根据患者的ID作为判断依据
-      if (message.msg === 'current-cognition-id-req' && message.currentPatId === userStore.user.id) {
-        wsSend(
-          JSON.stringify({
-            dataSource: 'cognitionMgrList',
-            msg: 'current-cognition-id-res',
-            currentPatId: userStore.user.id,
-            currentCognitionId: props.cognitionId
-          })
-        )
-      }
-    }
-  }
-})
-// ----------------------------------- WebSocket -----------------------------------
 </script>
 
 <style scoped lang="scss"></style>

+ 28 - 2
src/views/tester/components/RehabilitationEvaluation/RehabilitationEvaluationLogDetail/index.vue

@@ -10,7 +10,7 @@
           :key="`scale-${index}`"
           class="d-list-item m-[12px] rounded-[8px] flex flex-row"
         >
-          <el-image :src="`static/image/cover/icon-item-${parseInt(<string>item.id) % 9}.png`" fit="fill" />
+          <el-image :src="`static/image/cover/icon-item-${parseInt(<string>item.id) % 9}.png`" fit="fill" class="w-[190px] h-full" />
           <div class="flex-1 p-[16px] pos-relative">
             <div class="text-[20px] font-600 line-height-[24px]">{{ item.sname }}</div>
             <div class="text-[15px] text-[#666] mt-[8px] text-2-ellipsis">{{ item.info?.intro }}</div>
@@ -44,7 +44,11 @@
           :key="`scale-${index}`"
           class="d-list-item m-[12px] rounded-[8px] flex flex-row"
         >
-          <el-image :src="`static/image/cover/icon-item-${8 - (parseInt(<string>item.id) % 9)}.png`" fit="fill" />
+          <el-image
+            :src="`static/image/cover/icon-item-${8 - (parseInt(<string>item.id) % 9)}.png`"
+            fit="fill"
+            class="w-[190px] h-full"
+          />
           <div class="flex-1 p-[16px] pos-relative">
             <div class="text-[20px] font-600 line-height-[24px]">{{ item.tname }}</div>
             <div class="text-[15px] text-[#666] mt-[8px] text-2-ellipsis">{{ item.info?.intro }}</div>
@@ -76,6 +80,7 @@
         :parent-id="targetItem.parentId"
         :id="targetItem.id"
         :name="targetItem.name"
+        @close="showInfoDetail = false"
       />
     </right-to-left-div>
 
@@ -117,6 +122,11 @@ const targetItem = ref({
   name: ''
 })
 
+/**
+ * 开始评定任务
+ * @param item 评定任务主体信息
+ * @param type 0:量表 1:认知任务
+ */
 const beginTest = (item: EvaluationScaleSubjectVO | EvaluationCognitionSubjectVO, type: number) => {
   if (item.finish === '1') {
     EvaluationSTRecordInfoAPI.getList({
@@ -134,6 +144,11 @@ const beginTest = (item: EvaluationScaleSubjectVO | EvaluationCognitionSubjectVO
       name: type === 0 ? item.sname! : item.tname!
     }
     showInfoDetail.value = true
+
+    // 如果是副窗口且评定任务认知任务 存储当前选中评定记录中认知评定任务
+    if (!window.location.href.includes('win=main') && type === 1) {
+      localStorage.setItem('tow-win-target-evaluation-log-cognition', JSON.stringify(item))
+    }
   }
 }
 
@@ -147,12 +162,21 @@ async function exec() {
     evaluateId: props.evaluateId
   }).then((data) => {
     cognitionList.value = data
+
+    // 如果是主窗口 获取存储的评定记录的认知评定任务信息 自动跳转到认知任务介绍页
+    if (window.location.href.includes('win=main')) {
+      let tempJSON = localStorage.getItem('tow-win-target-evaluation-log-cognition')
+      if (tempJSON) {
+        beginTest(JSON.parse(tempJSON) as EvaluationCognitionSubjectVO, 1)
+      }
+    }
   })
 }
 
 onMounted(() => {
   exec()
 
+  // 监听刷新事件 当患者完成某一项评定任务时 刷新当前页面
   instance?.proxy?.$Bus.on('RehabilitationEvaluationLogDetail-refresh', () => {
     exec()
   })
@@ -160,6 +184,8 @@ onMounted(() => {
 
 onBeforeUnmount(() => {
   instance?.proxy?.$Bus.off('RehabilitationEvaluationLogDetail-refresh')
+  // 移除当前选中的评定记录信息
+  localStorage.removeItem('tow-win-target-evaluation-log')
 })
 </script>
 

+ 48 - 5
src/views/tester/components/RehabilitationEvaluation/RehabilitationEvaluationLogIntro/index.vue

@@ -2,7 +2,7 @@
   <div class="evaluation-log-intro-container w-full mt-[16px] relative">
     <svg-icon icon-class="bg-evaluation-info" class="absolute z-119 left-0 top-0" style="width: 100%; height: 100%" />
     <div
-      class="w-[51%] h-[73%] absolute top-[45%] left-[50%] translate-[-50%] z-249 bg-white flex flex-col overflow-y-auto"
+      class="w-[51%] h-[70%] absolute top-[45%] left-[50%] translate-[-50%] z-249 bg-white flex flex-col overflow-y-auto"
     >
       <div class="flex flex-row">
         <el-image
@@ -34,7 +34,7 @@
       </div>
     </div>
     <div
-      class="absolute left-[50%] bottom-[30px] w-[260px] h-[90px] z-249 text-[32px] translate-x-[-50%] cursor-pointer hover:scale-104"
+      class="absolute left-[50%] bottom-[40px] w-[260px] h-[90px] z-249 text-[32px] translate-x-[-50%] cursor-pointer hover:scale-104"
       @click="optTestPage"
     >
       <el-image :src="BeginUrl" fit="contain" style="width: 100%; height: 100%" />
@@ -44,14 +44,14 @@
       :subject-id="props.id"
       :parent-id="props.parentId"
       :title="props.name"
-      @close="optTestPage"
+      @close="closeIntro"
     />
     <cognitive-ability-task
       v-if="cognitionVisible"
       :cognition-id="props.id"
       :parent-id="props.parentId"
       :title="props.name"
-      @close="optTestPage"
+      @close-all="closeIntro"
     />
   </div>
 </template>
@@ -61,6 +61,12 @@ import EvaluationScaleAPI, { EvaluationScaleVO } from '@/api/tester/evaluation/s
 import EvaluationCognitionAPI, { EvaluationCognitionVO } from '@/api/tester/evaluation/cognition'
 import BeginUrl from '@/assets/images/icon-task-begin.png'
 
+import { getCurrentInstance } from 'vue'
+const instance = getCurrentInstance()
+
+import { useSocket } from '@/utils/websoket'
+const { wsSend } = useSocket()
+
 const props = defineProps({
   type: {
     type: Number,
@@ -82,6 +88,7 @@ const props = defineProps({
   }
 })
 
+const emits = defineEmits(['close'])
 const scaleInfo = ref<EvaluationScaleVO>()
 const cognitionInfo = ref<EvaluationCognitionVO>()
 const references = ref<string[]>()
@@ -100,15 +107,46 @@ const getScaleInfo = () => {
 const getCognitionInfo = () => {
   EvaluationCognitionAPI.findById(props.id).then((res) => {
     cognitionInfo.value = res
+
+    if (window.location.href.includes('win=main')) {
+      optTestPage()
+    }
   })
 }
 
-const optTestPage = () => {
+let isClick = false // 开始按钮 防止重复点击
+function optTestPage() {
+  if (isClick) return
+  isClick = true
   if (props.type === 0) {
     testPageVisible.value = !testPageVisible.value
   } else {
     cognitionVisible.value = !cognitionVisible.value
   }
+
+  // 如果是副窗口 则通知Electron打开住窗口 并启动显示主副标识程序
+  if (!window.location.href.includes('win=main')) {
+    // 设置主副窗口标识: instance用来通知当前窗口显示主副标识  localStorage用来通知其他窗口显示主副标识
+    instance?.proxy?.$Bus.emit('tow-win-show-is-main-win', 'YES')
+    localStorage.setItem('tow-win-show-is-main-win', 'YES')
+
+    // 使用websocket与electron通信 打开新的窗口
+    wsSend(
+      JSON.stringify({
+        dataSource: 'RehabilitationEvaluationLogIntro',
+        msg: 'open-other-win',
+        url: window.location.href
+      })
+    )
+  }
+
+  setTimeout(() => {
+    isClick = false
+  }, 2000)
+}
+
+const closeIntro = () => {
+  emits('close')
 }
 
 onMounted(() => {
@@ -118,6 +156,11 @@ onMounted(() => {
     getCognitionInfo()
   }
 })
+
+onBeforeUnmount(() => {
+  // 移除当前选中的评定记录中认知任务信息
+  localStorage.removeItem('tow-win-target-evaluation-log-cognition')
+})
 </script>
 
 <style scoped lang="scss">

+ 0 - 44
src/views/tester/components/RehabilitationEvaluation/RehabilitationEvaluationMgr/components/RehabilitationEvaluationMgrCognition/index.vue

@@ -215,50 +215,6 @@ onMounted(() => {
   handleTypeQuery()
   handleAddedQuery()
 })
-
-// ----------------------------------- WebSocket -----------------------------------
-let reqCognitionIdTimer: NodeJS.Timeout | null = null // 每隔4s查询患者当前打开了那个评定认知任务的请求
-const reqCognitionIdFn = () => {
-  reqCognitionIdTimer = setInterval(() => {
-    // 医生端 发出 被选中的患者当前打开了那个评定认知任务 的请求
-    // 根据患者的ID作为判断依据
-    wsSend(
-      JSON.stringify({
-        dataSource: 'cognitionMgrList',
-        msg: 'current-cognition-id-req',
-        currentPatId: userInfo.value?.id
-      })
-    )
-  }, 4000)
-}
-const clearCognitionIdTimer = () => {
-  clearInterval(reqCognitionIdTimer)
-  reqCognitionIdTimer = null
-}
-
-wsOn('close', () => console.log('Socket closed!'))
-//webSocket连接上服务器时
-wsOn('open', (event: Event) => {
-  console.log('webSocket连接上服务器时')
-
-  reqCognitionIdFn()
-})
-socket.on('message', (data: any) => {
-  console.log('Received data:', data)
-  if (data && isJSON(data)) {
-    let message = JSON.parse(data)
-    // if (message.dataSource === 'drawClock') {
-    //   if (message.msg === 'ping-fen-dialog-show') {
-    //     dialogVisible.value = true
-    //   }
-    // }
-  }
-})
-// ----------------------------------- WebSocket -----------------------------------
-
-onUnmounted(() => {
-  clearCognitionIdTimer()
-})
 </script>
 
 <style scoped lang="scss">

+ 19 - 2
src/views/tester/evaluation/rehabilitation/index.vue

@@ -26,7 +26,7 @@
 
     <div class="center-list mt-[28px] mb-[10px] overflow-y-auto flex flex-row flex-wrap">
       <div v-for="(item, index) in pageData" :key="index" class="item m-[12px] flex flex-row rounded-[8px]">
-        <el-image :src="`static/image/cover/icon-item-${index}.png`" fit="fill" />
+        <el-image :src="`static/image/cover/icon-item-${index}.png`" fit="fill" class="w-[240px] h-full" />
         <div class="flex-1 ml-[24px] flex flex-col pos-relative h-full">
           <div class="text-[22px] mt-[12px]">
             <span>{{ item.edate }}</span>
@@ -113,6 +113,14 @@ function handleQuery() {
     .then((data) => {
       pageData.value = data.records
       total.value = data.total
+
+      // 如果是主窗口 获取存储的评定记录信息 自动跳转到评定详情页
+      if (window.location.href.includes('win=main')) {
+        let tempJSON = localStorage.getItem('tow-win-target-evaluation-log')
+        if (tempJSON) {
+          goToList(JSON.parse(tempJSON) as EvaluationLogVO)
+        }
+      }
     })
     .finally(() => {
       loading.value = false
@@ -131,9 +139,14 @@ function resetQuery() {
   handleQuery()
 }
 
-const goToList = (item: EvaluationLogVO) => {
+function goToList(item: EvaluationLogVO) {
   targetLog.value = item
   rehabilitationListVisible.value = true
+
+  // 如果是副窗口 存储当前选中的评定记录
+  if (!window.location.href.includes('win=main')) {
+    localStorage.setItem('tow-win-target-evaluation-log', JSON.stringify(item))
+  }
 }
 
 const pageHeaderGoBack = () => {
@@ -144,6 +157,10 @@ onMounted(() => {
   queryParams.patId = userStore.user.id
   handleQuery()
 })
+
+onUnmounted(() => {
+  localStorage.removeItem('tow-win-show-is-main-win') // 移除主副窗口标识
+})
 </script>
 
 <style scoped lang="scss">