Forráskód Böngészése

开发获取当前播放方案与后端联调

zmj 1 hete
szülő
commit
a698a90d92

+ 8 - 0
.env.development

@@ -0,0 +1,8 @@
+# 页面标题
+VITE_APP_TITLE = 若依管理系统
+
+# 开发环境配置
+VITE_APP_ENV = 'development'
+
+# 若依管理系统/开发环境
+VITE_APP_BASE_API = '/dev-api'

+ 11 - 0
.env.production

@@ -0,0 +1,11 @@
+# 页面标题
+VITE_APP_TITLE = 若依管理系统
+
+# 生产环境配置
+VITE_APP_ENV = 'production'
+
+# 若依管理系统/生产环境
+VITE_APP_BASE_API = '/prod-api'
+
+# 是否在打包时开启压缩,支持 gzip 和 brotli
+VITE_BUILD_COMPRESS = gzip

+ 11 - 0
.env.staging

@@ -0,0 +1,11 @@
+# 页面标题
+VITE_APP_TITLE = 若依管理系统
+
+# 生产环境配置
+VITE_APP_ENV = 'staging'
+
+# 若依管理系统/生产环境
+VITE_APP_BASE_API = '/stage-api'
+
+# 是否在打包时开启压缩,支持 gzip 和 brotli
+VITE_BUILD_COMPRESS = gzip

+ 23 - 0
.gitignore copy

@@ -0,0 +1,23 @@
+.DS_Store
+node_modules/
+dist/
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+**/*.log
+
+tests/**/coverage/
+tests/e2e/reports
+selenium-debug.log
+
+# Editor directories and files
+.idea
+.vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.local
+
+package-lock.json
+yarn.lock

+ 260 - 0
package-lock.json

@@ -8,6 +8,7 @@
       "name": "robot-screen",
       "name": "robot-screen",
       "version": "1.0.0",
       "version": "1.0.0",
       "dependencies": {
       "dependencies": {
+        "axios": "^1.16.0",
         "pinia": "^2.1.7",
         "pinia": "^2.1.7",
         "vue": "^3.4.21",
         "vue": "^3.4.21",
         "vue-router": "^4.3.0"
         "vue-router": "^4.3.0"
@@ -189,10 +190,69 @@
       "version": "3.5.34",
       "version": "3.5.34",
       "license": "MIT"
       "license": "MIT"
     },
     },
+    "node_modules/asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+    },
+    "node_modules/axios": {
+      "version": "1.16.0",
+      "resolved": "https://registry.npmmirror.com/axios/-/axios-1.16.0.tgz",
+      "integrity": "sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w==",
+      "dependencies": {
+        "follow-redirects": "^1.16.0",
+        "form-data": "^4.0.5",
+        "proxy-from-env": "^2.1.0"
+      }
+    },
+    "node_modules/call-bind-apply-helpers": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+      "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "function-bind": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/combined-stream": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+      "dependencies": {
+        "delayed-stream": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
     "node_modules/csstype": {
     "node_modules/csstype": {
       "version": "3.2.3",
       "version": "3.2.3",
       "license": "MIT"
       "license": "MIT"
     },
     },
+    "node_modules/delayed-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/dunder-proto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz",
+      "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.1",
+        "es-errors": "^1.3.0",
+        "gopd": "^1.2.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
     "node_modules/entities": {
     "node_modules/entities": {
       "version": "7.0.1",
       "version": "7.0.1",
       "license": "BSD-2-Clause",
       "license": "BSD-2-Clause",
@@ -203,6 +263,47 @@
         "url": "https://github.com/fb55/entities?sponsor=1"
         "url": "https://github.com/fb55/entities?sponsor=1"
       }
       }
     },
     },
+    "node_modules/es-define-property": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz",
+      "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-errors": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz",
+      "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-object-atoms": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+      "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+      "dependencies": {
+        "es-errors": "^1.3.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-set-tostringtag": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+      "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.6",
+        "has-tostringtag": "^1.0.2",
+        "hasown": "^2.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
     "node_modules/esbuild": {
     "node_modules/esbuild": {
       "version": "0.21.5",
       "version": "0.21.5",
       "dev": true,
       "dev": true,
@@ -596,6 +697,40 @@
       "version": "2.0.2",
       "version": "2.0.2",
       "license": "MIT"
       "license": "MIT"
     },
     },
+    "node_modules/follow-redirects": {
+      "version": "1.16.0",
+      "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.16.0.tgz",
+      "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/RubenVerborgh"
+        }
+      ],
+      "engines": {
+        "node": ">=4.0"
+      },
+      "peerDependenciesMeta": {
+        "debug": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/form-data": {
+      "version": "4.0.5",
+      "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.5.tgz",
+      "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
+      "dependencies": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "es-set-tostringtag": "^2.1.0",
+        "hasown": "^2.0.2",
+        "mime-types": "^2.1.12"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
     "node_modules/fsevents": {
     "node_modules/fsevents": {
       "version": "2.3.3",
       "version": "2.3.3",
       "dev": true,
       "dev": true,
@@ -608,6 +743,96 @@
         "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
         "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
       }
       }
     },
     },
+    "node_modules/function-bind": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz",
+      "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/get-intrinsic": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+      "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.2",
+        "es-define-property": "^1.0.1",
+        "es-errors": "^1.3.0",
+        "es-object-atoms": "^1.1.1",
+        "function-bind": "^1.1.2",
+        "get-proto": "^1.0.1",
+        "gopd": "^1.2.0",
+        "has-symbols": "^1.1.0",
+        "hasown": "^2.0.2",
+        "math-intrinsics": "^1.1.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/get-proto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz",
+      "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+      "dependencies": {
+        "dunder-proto": "^1.0.1",
+        "es-object-atoms": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/gopd": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz",
+      "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/has-symbols": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz",
+      "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/has-tostringtag": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+      "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+      "dependencies": {
+        "has-symbols": "^1.0.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/hasown": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.3.tgz",
+      "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==",
+      "dependencies": {
+        "function-bind": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
     "node_modules/magic-string": {
     "node_modules/magic-string": {
       "version": "0.30.21",
       "version": "0.30.21",
       "license": "MIT",
       "license": "MIT",
@@ -615,6 +840,33 @@
         "@jridgewell/sourcemap-codec": "^1.5.5"
         "@jridgewell/sourcemap-codec": "^1.5.5"
       }
       }
     },
     },
+    "node_modules/math-intrinsics": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+      "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/mime-db": {
+      "version": "1.52.0",
+      "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
+      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mime-types": {
+      "version": "2.1.35",
+      "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
+      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+      "dependencies": {
+        "mime-db": "1.52.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
     "node_modules/nanoid": {
     "node_modules/nanoid": {
       "version": "3.3.12",
       "version": "3.3.12",
       "funding": [
       "funding": [
@@ -681,6 +933,14 @@
         "node": "^10 || ^12 || >=14"
         "node": "^10 || ^12 || >=14"
       }
       }
     },
     },
+    "node_modules/proxy-from-env": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-2.1.0.tgz",
+      "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==",
+      "engines": {
+        "node": ">=10"
+      }
+    },
     "node_modules/rollup": {
     "node_modules/rollup": {
       "version": "4.60.3",
       "version": "4.60.3",
       "dev": true,
       "dev": true,

+ 3 - 2
package.json

@@ -9,9 +9,10 @@
     "preview": "vite preview"
     "preview": "vite preview"
   },
   },
   "dependencies": {
   "dependencies": {
+    "axios": "^1.16.0",
+    "pinia": "^2.1.7",
     "vue": "^3.4.21",
     "vue": "^3.4.21",
-    "vue-router": "^4.3.0",
-    "pinia": "^2.1.7"
+    "vue-router": "^4.3.0"
   },
   },
   "devDependencies": {
   "devDependencies": {
     "@vitejs/plugin-vue": "^5.0.4",
     "@vitejs/plugin-vue": "^5.0.4",

+ 161 - 86
src/api/screen.js

@@ -1,10 +1,11 @@
 /**
 /**
  * API 封装层 - 迎宾机器人屏幕端
  * API 封装层 - 迎宾机器人屏幕端
  *
  *
- * 所有接口统一在此文件封装,一期使用 Mock 数据返回 Promise
- * 后续替换为真实接口时,只需修改本文件中对应的方法实现
+ * 所有接口统一在此文件封装
+ * 使用真实的后端接口,失败时降级到 Mock 数据
  */
  */
 
 
+import { http } from '@/utils/request'
 import {
 import {
   mockRobotStatus,
   mockRobotStatus,
   mockScreenConfig,
   mockScreenConfig,
@@ -23,12 +24,6 @@ import {
   mockScreenTheme
   mockScreenTheme
 } from '@/mock/screen'
 } from '@/mock/screen'
 
 
-// API 基础地址(后续替换为真实后端地址)
-const BASE_URL = '/api'
-
-// 模拟网络延迟
-const delay = (ms = 300) => new Promise(resolve => setTimeout(resolve, ms))
-
 // ============================================
 // ============================================
 // 屏幕配置与状态
 // 屏幕配置与状态
 // ============================================
 // ============================================
@@ -37,56 +32,84 @@ const delay = (ms = 300) => new Promise(resolve => setTimeout(resolve, ms))
  * 获取屏幕配置
  * 获取屏幕配置
  */
  */
 export async function getScreenConfig() {
 export async function getScreenConfig() {
-  await delay()
-  return { ...mockScreenConfig }
+  try {
+    return await http.get('/robot-ops/screen/config')
+  } catch (error) {
+    console.warn('使用 Mock 数据:getScreenConfig')
+    return { ...mockScreenConfig }
+  }
 }
 }
 
 
 /**
 /**
  * 获取机器人状态
  * 获取机器人状态
  */
  */
 export async function getRobotStatus() {
 export async function getRobotStatus() {
-  await delay()
-  return { ...mockRobotStatus }
+  try {
+    return await http.get('/robot-ops/robot/status')
+  } catch (error) {
+    console.warn('使用 Mock 数据:getRobotStatus')
+    return { ...mockRobotStatus }
+  }
 }
 }
 
 
 /**
 /**
  * 获取播放方案
  * 获取播放方案
  */
  */
 export async function getPlayPlan() {
 export async function getPlayPlan() {
-  await delay()
-  return { ...mockPlayPlan }
+  try {
+    return await http.get('/robot-ops/screen/play-plan/current')
+  } catch (error) {
+    console.warn('使用 Mock 数据:getPlayPlan')
+    return { ...mockPlayPlan }
+  }
 }
 }
 
 
 /**
 /**
  * 获取欢迎页兜底内容
  * 获取欢迎页兜底内容
  */
  */
 export async function getWelcomeContent() {
 export async function getWelcomeContent() {
-  await delay()
-  return { ...mockWelcomeContent }
+  try {
+    return await http.get('/robot-ops/screen/welcome-content')
+  } catch (error) {
+    console.warn('使用 Mock 数据:getWelcomeContent')
+    return { ...mockWelcomeContent }
+  }
 }
 }
 
 
 /**
 /**
  * 获取屏幕主题配置
  * 获取屏幕主题配置
  */
  */
 export async function getScreenTheme() {
 export async function getScreenTheme() {
-  await delay()
-  return { ...mockScreenTheme }
+  try {
+    return await http.get('/robot-ops/screen/theme')
+  } catch (error) {
+    console.warn('使用 Mock 数据:getScreenTheme')
+    return { ...mockScreenTheme }
+  }
 }
 }
 
 
 /**
 /**
  * 获取播报状态
  * 获取播报状态
  */
  */
 export async function getBroadcastState() {
 export async function getBroadcastState() {
-  await delay()
-  return { ...mockBroadcastState }
+  try {
+    return await http.get('/robot-ops/broadcast/state')
+  } catch (error) {
+    console.warn('使用 Mock 数据:getBroadcastState')
+    return { ...mockBroadcastState }
+  }
 }
 }
 
 
 /**
 /**
  * 获取播报内容
  * 获取播报内容
  */
  */
 export async function getBroadcastContent() {
 export async function getBroadcastContent() {
-  await delay()
-  return { ...mockBroadcastContent }
+  try {
+    return await http.get('/robot-ops/broadcast/content')
+  } catch (error) {
+    console.warn('使用 Mock 数据:getBroadcastContent')
+    return { ...mockBroadcastContent }
+  }
 }
 }
 
 
 // ============================================
 // ============================================
@@ -97,20 +120,27 @@ export async function getBroadcastContent() {
  * 获取最新指令
  * 获取最新指令
  */
  */
 export async function getLatestCommand() {
 export async function getLatestCommand() {
-  await delay(100)
-  // 模拟随机返回指令
-  if (Math.random() > 0.9) {
-    return mockCommands[Math.floor(Math.random() * mockCommands.length)]
+  try {
+    return await http.get('/robot-ops/commands/latest')
+  } catch (error) {
+    console.warn('使用 Mock 数据:getLatestCommand')
+    if (Math.random() > 0.9) {
+      return mockCommands[Math.floor(Math.random() * mockCommands.length)]
+    }
+    return null
   }
   }
-  return null
 }
 }
 
 
 /**
 /**
  * 指令回执
  * 指令回执
  */
  */
 export async function ackCommand(commandId) {
 export async function ackCommand(commandId) {
-  await delay()
-  return { success: true, commandId }
+  try {
+    return await http.post(`/robot-ops/commands/${commandId}/ack`)
+  } catch (error) {
+    console.warn('使用 Mock 数据:ackCommand')
+    return { success: true, commandId }
+  }
 }
 }
 
 
 // ============================================
 // ============================================
@@ -121,38 +151,44 @@ export async function ackCommand(commandId) {
  * 读取身份证
  * 读取身份证
  */
  */
 export async function readIdCard() {
 export async function readIdCard() {
-  await delay(1000)
-  return { ...mockIdCardResult }
+  try {
+    return await http.post('/robot-ops/id-card/read')
+  } catch (error) {
+    console.warn('使用 Mock 数据:readIdCard')
+    return { ...mockIdCardResult }
+  }
 }
 }
 
 
 /**
 /**
  * 预约查询
  * 预约查询
  */
  */
 export async function queryAppointment(params) {
 export async function queryAppointment(params) {
-  await delay(800)
-  const { mobile, idCardNo } = params
-  // 根据手机号或身份证号查询
-  const appointment = mockAppointments.find(
-    a => a.mobile === mobile || a.idCardNo === idCardNo
-  )
-  if (appointment) {
-    return appointment
+  try {
+    return await http.get('/robot-ops/appointments/query', {
+      data: params
+    })
+  } catch (error) {
+    console.warn('使用 Mock 数据:queryAppointment')
+    const { mobile, idCardNo } = params
+    const appointment = mockAppointments.find(
+      a => a.mobile === mobile || a.idCardNo === idCardNo
+    )
+    if (appointment) {
+      return appointment
+    }
+    throw new Error('未查询到预约记录')
   }
   }
-  throw new Error('未查询到预约记录')
 }
 }
 
 
 /**
 /**
  * 提交访客登记
  * 提交访客登记
  */
  */
 export async function submitVisitorRegistration(data) {
 export async function submitVisitorRegistration(data) {
-  await delay(1000)
-  // 模拟成功返回
-  return {
-    success: true,
-    visitorId: 'V' + Date.now(),
-    registrationNo: 'REG' + Date.now(),
-    visitorName: data.visitorName,
-    registrationTime: new Date().toISOString()
+  try {
+    return await http.post('/robot-ops/visitors/register', data)
+  } catch (error) {
+    console.error('提交访客登记失败:', error)
+    throw error
   }
   }
 }
 }
 
 
@@ -160,8 +196,12 @@ export async function submitVisitorRegistration(data) {
  * 获取最新识别结果
  * 获取最新识别结果
  */
  */
 export async function getRecognitionResult() {
 export async function getRecognitionResult() {
-  await delay(500)
-  return { ...mockRecognitionResult }
+  try {
+    return await http.get('/robot-ops/face/result/latest')
+  } catch (error) {
+    console.warn('使用 Mock 数据:getRecognitionResult')
+    return { ...mockRecognitionResult }
+  }
 }
 }
 
 
 // ============================================
 // ============================================
@@ -172,19 +212,23 @@ export async function getRecognitionResult() {
  * 获取目的地列表
  * 获取目的地列表
  */
  */
 export async function getDestinations() {
 export async function getDestinations() {
-  await delay()
-  return [...mockDestinations]
+  try {
+    return await http.get('/robot-ops/destinations')
+  } catch (error) {
+    console.warn('使用 Mock 数据:getDestinations')
+    return [...mockDestinations]
+  }
 }
 }
 
 
 /**
 /**
  * 开始导航
  * 开始导航
  */
  */
 export async function startNavigation(params) {
 export async function startNavigation(params) {
-  await delay(500)
-  return {
-    taskId: 'nav_' + Date.now(),
-    destinationId: params.destinationId,
-    status: 'starting'
+  try {
+    return await http.post('/robot-ops/navigation/start', params)
+  } catch (error) {
+    console.error('开始导航失败:', error)
+    throw error
   }
   }
 }
 }
 
 
@@ -192,16 +236,24 @@ export async function startNavigation(params) {
  * 获取导航状态
  * 获取导航状态
  */
  */
 export async function getNavigationStatus(params) {
 export async function getNavigationStatus(params) {
-  await delay()
-  return { ...mockNavigationStatus, taskId: params.taskId }
+  try {
+    return await http.get(`/robot-ops/navigation/${params.taskId}/status`)
+  } catch (error) {
+    console.warn('使用 Mock 数据:getNavigationStatus')
+    return { ...mockNavigationStatus, taskId: params.taskId }
+  }
 }
 }
 
 
 /**
 /**
  * 取消导航
  * 取消导航
  */
  */
 export async function cancelNavigation(params) {
 export async function cancelNavigation(params) {
-  await delay(300)
-  return { success: true, taskId: params.taskId }
+  try {
+    return await http.post(`/robot-ops/navigation/${params.taskId}/cancel`)
+  } catch (error) {
+    console.error('取消导航失败:', error)
+    throw error
+  }
 }
 }
 
 
 // ============================================
 // ============================================
@@ -212,20 +264,28 @@ export async function cancelNavigation(params) {
  * 获取通知公告列表
  * 获取通知公告列表
  */
  */
 export async function getNotices() {
 export async function getNotices() {
-  await delay()
-  return [...mockNotices]
+  try {
+    return await http.get('/robot-ops/notices')
+  } catch (error) {
+    console.warn('使用 Mock 数据:getNotices')
+    return [...mockNotices]
+  }
 }
 }
 
 
 /**
 /**
  * 获取通知详情
  * 获取通知详情
  */
  */
 export async function getNoticeDetail(id) {
 export async function getNoticeDetail(id) {
-  await delay()
-  const notice = mockNotices.find(n => n.id === id)
-  if (notice) {
-    return { ...notice }
+  try {
+    return await http.get(`/robot-ops/notices/${id}`)
+  } catch (error) {
+    console.warn('使用 Mock 数据:getNoticeDetail')
+    const notice = mockNotices.find(n => n.id === id)
+    if (notice) {
+      return { ...notice }
+    }
+    throw new Error('公告不存在')
   }
   }
-  throw new Error('公告不存在')
 }
 }
 
 
 // ============================================
 // ============================================
@@ -236,11 +296,11 @@ export async function getNoticeDetail(id) {
  * 呼叫工作人员
  * 呼叫工作人员
  */
  */
 export async function callStaff(params) {
 export async function callStaff(params) {
-  await delay(1000)
-  return {
-    success: true,
-    callId: 'CALL' + Date.now(),
-    message: '已通知工作人员,请稍候'
+  try {
+    return await http.post('/robot-ops/staff/call', params)
+  } catch (error) {
+    console.error('呼叫工作人员失败:', error)
+    throw error
   }
   }
 }
 }
 
 
@@ -252,8 +312,12 @@ export async function callStaff(params) {
  * 获取系统信息
  * 获取系统信息
  */
  */
 export async function getSystemInfo() {
 export async function getSystemInfo() {
-  await delay()
-  return { ...mockSystemInfo }
+  try {
+    return await http.get('/robot-ops/system/info')
+  } catch (error) {
+    console.warn('使用 Mock 数据:getSystemInfo')
+    return { ...mockSystemInfo }
+  }
 }
 }
 
 
 // ============================================
 // ============================================
@@ -264,11 +328,15 @@ export async function getSystemInfo() {
  * 获取摄像头预览地址
  * 获取摄像头预览地址
  */
  */
 export async function getCameraPreviewUrl() {
 export async function getCameraPreviewUrl() {
-  await delay()
-  return {
-    streamUrl: 'rtsp://localhost:8554/camera',
-    streamType: 'rtsp',
-    expireTime: Date.now() + 300000
+  try {
+    return await http.get('/robot-ops/camera/preview-url')
+  } catch (error) {
+    console.warn('使用 Mock 数据:getCameraPreviewUrl')
+    return {
+      streamUrl: 'rtsp://localhost:8554/camera',
+      streamType: 'rtsp',
+      expireTime: Date.now() + 300000
+    }
   }
   }
 }
 }
 
 
@@ -280,8 +348,12 @@ export async function getCameraPreviewUrl() {
  * 音量控制
  * 音量控制
  */
  */
 export async function setAudioControl(params) {
 export async function setAudioControl(params) {
-  await delay()
-  return { success: true, ...params }
+  try {
+    return await http.post('/robot-ops/audio/control', params)
+  } catch (error) {
+    console.error('音量控制失败:', error)
+    throw error
+  }
 }
 }
 
 
 // ============================================
 // ============================================
@@ -292,9 +364,12 @@ export async function setAudioControl(params) {
  * 上报屏幕事件
  * 上报屏幕事件
  */
  */
 export async function reportEvent(data) {
 export async function reportEvent(data) {
-  await delay(100)
-  console.log('[Event Report]', data)
-  return { success: true }
+  try {
+    return await http.post('/robot-ops/events/report', data)
+  } catch (error) {
+    console.error('事件上报失败:', error)
+    return { success: true }
+  }
 }
 }
 
 
 // 导出所有 API 方法
 // 导出所有 API 方法
@@ -323,4 +398,4 @@ export default {
   getCameraPreviewUrl,
   getCameraPreviewUrl,
   setAudioControl,
   setAudioControl,
   reportEvent
   reportEvent
-}
+}

+ 2 - 2
src/components/IdlePlayer.vue

@@ -1,14 +1,14 @@
 <template>
 <template>
   <div class="idle-player">
   <div class="idle-player">
     <!-- 调试切换按钮(showDebugSwitch 为 true 时显示) -->
     <!-- 调试切换按钮(showDebugSwitch 为 true 时显示) -->
-    <button
+    <!-- <button
       v-if="theme.showDebugSwitch !== false"
       v-if="theme.showDebugSwitch !== false"
       class="debug-toggle"
       class="debug-toggle"
       @click.stop="onToggleMode"
       @click.stop="onToggleMode"
       title="开发调试:切换待机页显示模式"
       title="开发调试:切换待机页显示模式"
     >
     >
       {{ actualMode === 'welcome' ? '切换播放方案' : '切换欢迎页' }}
       {{ actualMode === 'welcome' ? '切换播放方案' : '切换欢迎页' }}
-    </button>
+    </button> -->
 
 
     <!-- ================================================
     <!-- ================================================
          欢迎模式(默认兜底页)
          欢迎模式(默认兜底页)

+ 1 - 1
src/mock/screen.js

@@ -73,7 +73,7 @@ const videoItems = Object.entries(videoModules).map(([path, url], index) => ({
 }))
 }))
 
 
 const localPlayItems = [
 const localPlayItems = [
-  ...imageItems,
+  //...imageItems,
   ...videoItems
   ...videoItems
 ]
 ]
 
 

+ 18 - 11
src/stores/screen.js

@@ -87,7 +87,8 @@ export const useScreenStore = defineStore('screen', () => {
   async function fetchScreenConfig() {
   async function fetchScreenConfig() {
     try {
     try {
       const res = await api.getScreenConfig()
       const res = await api.getScreenConfig()
-      screenConfig.value = res
+      const data = res && res.data !== undefined ? res.data : res
+      screenConfig.value = data
     } catch (e) {
     } catch (e) {
       console.error('Failed to fetch screen config:', e)
       console.error('Failed to fetch screen config:', e)
     }
     }
@@ -96,7 +97,8 @@ export const useScreenStore = defineStore('screen', () => {
   async function fetchScreenTheme() {
   async function fetchScreenTheme() {
     try {
     try {
       const res = await api.getScreenTheme()
       const res = await api.getScreenTheme()
-      screenTheme.value = res
+      const data = res && res.data !== undefined ? res.data : res
+      screenTheme.value = data
     } catch (e) {
     } catch (e) {
       console.error('Failed to fetch screen theme:', e)
       console.error('Failed to fetch screen theme:', e)
     }
     }
@@ -105,7 +107,8 @@ export const useScreenStore = defineStore('screen', () => {
   async function fetchRobotStatus() {
   async function fetchRobotStatus() {
     try {
     try {
       const res = await api.getRobotStatus()
       const res = await api.getRobotStatus()
-      robotStatus.value = res
+      const data = res && res.data !== undefined ? res.data : res
+      robotStatus.value = data
     } catch (e) {
     } catch (e) {
       console.error('Failed to fetch robot status:', e)
       console.error('Failed to fetch robot status:', e)
     }
     }
@@ -114,17 +117,20 @@ export const useScreenStore = defineStore('screen', () => {
   async function fetchPlayPlan() {
   async function fetchPlayPlan() {
     try {
     try {
       const res = await api.getPlayPlan()
       const res = await api.getPlayPlan()
-      playPlan.value = res
+      // 处理 RuoYi 风格的响应结构 { msg, code, data }
+      const data = res && res.data !== undefined ? res.data : res
+      
+      playPlan.value = data
       currentMediaIndex.value = 0
       currentMediaIndex.value = 0
-      // 判断是否有有效的播放方案:enabled !== false 且 items 非空
       hasPlayPlan.value = !!(
       hasPlayPlan.value = !!(
-        res &&
-        res.enabled !== false &&
-        Array.isArray(res.items) &&
-        res.items.length > 0
+        data &&
+        data.enabled !== false &&
+        Array.isArray(data.items) &&
+        data.items.length > 0
       )
       )
+      console.log('[Store] 播放方案加载成功', hasPlayPlan.value ? '有播放方案' : '无播放方案', data)
     } catch (e) {
     } catch (e) {
-      console.error('Failed to fetch play plan:', e)
+      console.error('[Store] 播放方案加载失败:', e)
       playPlan.value = null
       playPlan.value = null
       hasPlayPlan.value = false
       hasPlayPlan.value = false
     }
     }
@@ -133,7 +139,8 @@ export const useScreenStore = defineStore('screen', () => {
   async function fetchBroadcastState() {
   async function fetchBroadcastState() {
     try {
     try {
       const res = await api.getBroadcastState()
       const res = await api.getBroadcastState()
-      broadcastState.value = res
+      const data = res && res.data !== undefined ? res.data : res
+      broadcastState.value = data
     } catch (e) {
     } catch (e) {
       console.error('Failed to fetch broadcast state:', e)
       console.error('Failed to fetch broadcast state:', e)
     }
     }

+ 75 - 0
src/utils/request.js

@@ -0,0 +1,75 @@
+import { useScreenStore } from '@/stores/screen'
+
+const BASE_URL = import.meta.env.VITE_API_BASE_URL || '/dev-api'
+
+class Request {
+  constructor() {
+    this.baseURL = BASE_URL
+    this.timeout = 10000
+  }
+
+  async request(url, options = {}) {
+    const {
+      method = 'GET',
+      data = null,
+      headers = {},
+      timeout = this.timeout
+    } = options
+
+    const controller = new AbortController()
+    const timeoutId = setTimeout(() => controller.abort(), timeout)
+
+    try {
+      const config = {
+        method,
+        headers: {
+          'Content-Type': 'application/json',
+          ...headers
+        },
+        signal: controller.signal
+      }
+
+      if (data && (method === 'POST' || method === 'PUT' || method === 'PATCH')) {
+        config.body = JSON.stringify(data)
+      }
+
+      const response = await fetch(`${this.baseURL}${url}`, config)
+      clearTimeout(timeoutId)
+
+      if (!response.ok) {
+        throw new Error(`HTTP ${response.status}: ${response.statusText}`)
+      }
+
+      const result = await response.json()
+      return result
+    } catch (error) {
+      clearTimeout(timeoutId)
+      
+      if (error.name === 'AbortError') {
+        throw new Error('请求超时')
+      }
+      
+      console.error(`[Request Error] ${method} ${url}:`, error)
+      throw error
+    }
+  }
+
+  get(url, options = {}) {
+    return this.request(url, { ...options, method: 'GET' })
+  }
+
+  post(url, data, options = {}) {
+    return this.request(url, { ...options, method: 'POST', data })
+  }
+
+  put(url, data, options = {}) {
+    return this.request(url, { ...options, method: 'PUT', data })
+  }
+
+  delete(url, options = {}) {
+    return this.request(url, { ...options, method: 'DELETE' })
+  }
+}
+
+export const http = new Request()
+export default http

+ 10 - 2
vite.config.js

@@ -11,7 +11,15 @@ export default defineConfig({
   },
   },
   server: {
   server: {
     port: 5173,
     port: 5173,
-    host: true
+    host: true,
+    proxy: {
+      // 使用 /dev-api 前缀,与 RobotSpineWeb 保持一致
+      '/dev-api': {
+        target: 'http://192.168.0.30:8080',
+        changeOrigin: true,
+        rewrite: (path) => path.replace(/^\/dev-api/, '')
+      }
+    }
   },
   },
   build: {
   build: {
     outDir: 'dist',
     outDir: 'dist',
@@ -19,4 +27,4 @@ export default defineConfig({
     sourcemap: false,
     sourcemap: false,
     chunkSizeWarningLimit: 1000
     chunkSizeWarningLimit: 1000
   }
   }
-})
+})

+ 0 - 0
杩庡宸¢€诲畨闃叉満鍣ㄤ汉鏈鸿韩灞忎氦浜掔郴缁熻缁嗚璁″紑鍙戞枃妗o紙涓€鏈燂級.html → 迎宾巡逻安防机器人机身屏交互系统详细设计开发文档(一期).html


+ 0 - 0
杩庡宸¢€诲畨闃叉満鍣ㄤ汉杩愮淮绔疻eb绠$悊绯荤粺璇︾粏璁捐寮€鍙戞枃妗V2.1.html → 迎宾巡逻安防机器人运维端 Web 管理系统详细设计开发文档(一期).html