Quellcode durchsuchen

新增会议模板切换/会议配置密码/会议功能完善

fanghuisheng vor 6 Tagen
Ursprung
Commit
6efb9f5f08

+ 1 - 0
.gitignore

@@ -2,6 +2,7 @@
 .history/
 .hbuilderx/
 package-lock.json
+unpackage/resources/
 node_modules/
 dist/
 npm-debug.log*

+ 1 - 8
src/App.vue

@@ -24,6 +24,7 @@ function stteingInit() {
   plus.screen.unlockOrientation(); //解除屏幕方向的锁定,但是不一定是竖屏;
   // 智能会议
   if (config.appInfo.appid === "__UNI__F3963F8") {
+    // plus.screen.lockOrientation("portrait-primary"); 
     plus.screen.lockOrientation("landscape-primary"); //设置屏幕方向(1.竖屏正方向:portrait-primary 2.竖屏反方向:portrait-secondary 3.横屏正方向:landscape-primary 4.横屏反方向:landscape-secondary 5.自然方向:default)
     proxy.$keyListen.startListen({
       needStopSystem: true,
@@ -41,14 +42,6 @@ function stteingInit() {
   }
 }
 
-watchEffect(() => {
-  //#ifdef APP-PLUS || MP-WEIXIN
-  if (uni.getStorageSync("serveUrl")) {
-    config.baseUrl = "http://" + uni.getStorageSync("serveUrl") + "/prod-api";
-  }
-  //#endif
-});
-
 onLaunch(() => {
   console.log("App Launch");
   initApp();

+ 9 - 0
src/api/business/meeting.js

@@ -286,4 +286,13 @@ export function signOnOut(data) {
         method: 'POST',
         data
     })
+}
+
+//设备心跳接口
+export function escalation(data) {
+    return request({
+        url: '/service-meeting/meetingDeviceHeartbeat/escalation',
+        method: 'POST',
+        data
+    })
 }

+ 8 - 6
src/pages/door/index.vue

@@ -42,12 +42,10 @@ const { version, webviewStyles } = toRefs(state);
 
 // 初始化
 function init() {
-  setTimeout(() => {
-    controlStore.initCamera(); //初始化摄像头
-    // controlStore.initNfc();//初始化NFC
-    controlStore.initData(); //初始化数据
-    controlStore.openInterval("door"); //开启门禁数据定时任务
-  }, 500);
+  controlStore.initCamera(); //初始化摄像头
+  // controlStore.initNfc();//初始化NFC
+  controlStore.initData(); //初始化数据
+  controlStore.openInterval("door"); //开启门禁数据定时任务
 }
 
 // 密码开门组件确认事件
@@ -112,6 +110,10 @@ iframe {
     &__title {
       font-size: 18px !important;
     }
+    &__selector-item {
+      color: black;
+    }
+
     .slot-content {
       font-size: 16px;
       width: 100%;

+ 13 - 0
src/pages/door/setting/index.scss

@@ -42,6 +42,13 @@
     }
 
     :deep() {
+        .uni-select {
+            .uni-icons {
+                color: white !important;
+                font-size: 18px !important;
+            }
+        }
+
 
         .u-cell__value,
         .u-cell__title-text {
@@ -94,6 +101,12 @@
         }
 
         :deep() {
+            .uni-select {
+                .uni-icons {
+                    font-size: 0.8rem !important;
+                }
+            }
+
             .u-cell {
                 .u-cell__title-text {
                     font-size: 0.8rem !important;

+ 4 - 0
src/pages/door/setting/index.vue

@@ -92,6 +92,10 @@ function startActivity() {
 onLoad((options) => {
   controlStore.initData(); //初始化数据
 });
+
+onShow(() => {
+  controlStore.getDoorList();
+});
 </script>
 <style lang="scss">
 @import "./index.scss";

+ 30 - 25
src/pages/door/setting/other/index.vue

@@ -19,38 +19,22 @@
       <text class="iconfont oaIcon-left" @click="handleExit()"></text>
 
       <view class="mb10">绑定门禁</view>
-      <view>
-        <u-input
-          v-model="controlStore.form.door.name"
+      <view class="flex">
+        <uni-data-select
+          v-model="controlStore.form.door.id"
+          :localdata="controlStore.doorList"
           placeholder="门禁(必选)"
-          suffixIcon="arrow-right"
-          suffixIconStyle="color: white"
-          border="none"
-          color="white"
-          disabledColor="transparent"
-          disabled
-          @click="controlStore.handlePicker('绑定门禁')"
-        />
+          mode="none"
+          :clear="false"
+          @change="(e) => controlStore.handleSelectChange({ value: e, type: '绑定门禁' })"
+        >
+        </uni-data-select>
       </view>
     </template>
   </oa-scroll>
-
-  <u-picker
-    :show="controlStore.picker.show"
-    :columns="controlStore.picker.list"
-    :title="'请选择' + controlStore.picker.title"
-    keyName="name"
-    visibleItemCount="6"
-    :defaultIndex="[controlStore.picker.defaultIndex]"
-    :closeOnClickOverlay="true"
-    @close="controlStore.picker.show = false"
-    @cancel="controlStore.picker.show = false"
-    @confirm="controlStore.pickerConfirm"
-  ></u-picker>
 </template>
 <script setup>
 /*----------------------------------依赖引入-----------------------------------*/
-import config from "@/config";
 import { onLoad, onShow, onReady, onHide, onLaunch, onUnload, onNavigationBarButtonTap, onPageScroll } from "@dcloudio/uni-app";
 import { ref, reactive, computed, getCurrentInstance, toRefs, inject, nextTick, watch } from "vue";
 /*----------------------------------接口引入-----------------------------------*/
@@ -88,4 +72,25 @@ onShow(() => {
 </script>
 <style lang="scss">
 @import "../index.scss";
+
+:deep() {
+  .uni-stat-box {
+    background-color: transparent !important;
+    color: black !important;
+
+    .uni-select {
+      padding-left: 0px !important;
+      padding-right: 0px !important;
+
+      &__input-text {
+        color: #ffffff !important;
+      }
+
+      &__input-placeholder {
+        font-size: 15px !important;
+        color: #c0c4cc !important;
+      }
+    }
+  }
+}
 </style>

+ 0 - 255
src/pages/face/index.vue

@@ -1,255 +0,0 @@
-<template>
-  <web-view
-    v-show="!controlStore.modal.show"
-    ref="faceView"
-    id="faceView"
-    class="faceView"
-    src="/static/face/meeting.html"
-    bindmessage="receiveMessage"
-    :webview-styles="webviewStyles"
-    @message="onMessage"
-  >
-  </web-view>
-
-  <u-modal
-    :show="controlStore.modal.show"
-    title=""
-    :cancelText="'退出应用'"
-    :zoom="false"
-    :showConfirmButton="true"
-    :showCancelButton="true"
-    :closeOnClickOverlay="true"
-    @confirm="controlStore.handleModal('Confirm')"
-    @cancel="controlStore.handleModal('Cancel')"
-    @close="controlStore.handleModal('Close')"
-  >
-    <view class="slot-content">
-      <!-- <view @click="startActivity()">测试startActivity</view> -->
-
-      <u-subsection class="mb20" :list="controlStore.subsection.list" :current="controlStore.subsection.value" @change="controlStore.sectionChange"></u-subsection>
-
-      <view v-if="controlStore.subsection.value == 0">
-        <view class="mb10 required">绑定门禁</view>
-        <view>
-          <u-input
-            v-model="controlStore.form.door.name"
-            placeholder="门禁(必选)"
-            suffixIcon="arrow-right"
-            suffixIconStyle="color: #909399"
-            border="none"
-            disabledColor="transparent"
-            disabled
-            @click="controlStore.handlePicker('绑定门禁')"
-          />
-        </view>
-      </view>
-
-      <u-cell-group v-if="controlStore.subsection.value == 1">
-        <u-cell title="导航栏显示">
-          <template #value>
-            <u-switch v-model="setting.navBarNew" size="15" @change="navBarNewChange"></u-switch>
-          </template>
-        </u-cell>
-        <u-cell title="软件版本号" :value="version"></u-cell>
-        <u-cell title="IP" :value="setting.ipAddress || '-'"></u-cell>
-        <u-cell title="设备型号" :value="sysPlugins.getDeviceInfo().model || '-'"></u-cell>
-        <u-cell title="设备序列号" :value="sysPlugins.getDeviceInfo().serial || '-'"></u-cell>
-        <u-cell title="检查更新" @click="handleToUpgrade()">
-          <template #value> <view class="iconfont oaIcon-jianchagengxin menu-item-icon mr2" style="color: #2979ff"></view> </template>
-        </u-cell>
-        <u-cell title="设备重启" @click="rebootNow()">
-          <template #value> <u-icon name="reload" color="#2979ff" size="20"></u-icon> </template>
-        </u-cell>
-      </u-cell-group>
-    </view>
-  </u-modal>
-
-  <u-picker
-    :show="controlStore.picker.show"
-    :columns="controlStore.picker.list"
-    :title="'请选择' + controlStore.picker.title"
-    keyName="name"
-    visibleItemCount="6"
-    :defaultIndex="[controlStore.picker.defaultIndex]"
-    :closeOnClickOverlay="true"
-    @close="controlStore.picker.show = false"
-    @cancel="controlStore.picker.show = false"
-    @confirm="controlStore.pickerConfirm"
-  ></u-picker>
-
-  <oa-upgrade ref="oaUpgradeRef" :themesColor="proxy.$settingStore.themeColor.color" />
-</template>
-<script setup>
-/*----------------------------------依赖引入-----------------------------------*/
-import config from "@/config";
-import { onLoad, onShow, onReady, onHide, onLaunch, onUnload, onNavigationBarButtonTap, onPageScroll } from "@dcloudio/uni-app";
-import { ref, reactive, computed, getCurrentInstance, toRefs, inject, nextTick, watch } from "vue";
-/*----------------------------------接口引入-----------------------------------*/
-/*----------------------------------组件引入-----------------------------------*/
-/*----------------------------------store引入-----------------------------------*/
-import { controlStores } from "@/store/modules/index";
-/*----------------------------------公共方法引入-----------------------------------*/
-import sysPlugins from "@/plugins/device/sys.plugins";
-/*----------------------------------公共变量-----------------------------------*/
-const { proxy } = getCurrentInstance();
-const controlStore = controlStores();
-/*----------------------------------公共变量-----------------------------------*/
-const state = reactive({
-  version: computed(() => {
-    return config.appInfo.version;
-  }),
-  webviewStyles: {
-    width: "100%",
-    height: "100%",
-  },
-  inter: {
-    meeting: null,
-    rebootNow: null,
-  },
-
-  setting: {
-    navBarNew: true,
-    ipAddress: "",
-  },
-});
-const { version, webviewStyles, inter, setting } = toRefs(state);
-
-// 初始化
-function init() {
-  setInterval(() => {
-    sysPlugins.getIpAddress({
-      success: (res) => {
-        state.setting.ipAddress = res;
-      },
-      error: (res) => {
-        state.setting.ipAddress = "";
-      },
-    });
-  }, 2000);
-
-  controlStore.pageFunction = ["门禁", "会议"];
-  controlStore.initCamera();
-  // controlStore.initNfc();
-  controlStore.initData();
-  controlStore.openInterval("meeting");
-
-}
-
-/**
- * @接收子页面传过来的值
- */
-function onMessage(e) {
-  controlStore.analysisData(e.detail.data[0]);
-}
-// #ifdef H5
-window.onmessage = function (event) {
-  controlStore.analysisData(event.data);
-};
-// #endif
-
-// 隐藏导航栏
-function navBarNewChange(value) {
-  //#ifdef APP-PLUS
-  const yxPlugin = uni.requireNativePlugin("yxPlugin");
-  yxPlugin.setNavBarNew(value);
-  //#endif
-}
-
-// 设备重启
-function rebootNow() {
-  //#ifdef APP-PLUS
-  const yxPlugin = uni.requireNativePlugin("yxPlugin");
-  yxPlugin.rebootNow();
-  //#endif
-}
-
-// 定时重启
-function timingRebootNow() {
-  if (!inter.rebootNow) {
-    checkMorningSix();
-    inter.rebootNow = setInterval(() => {
-      checkMorningSix();
-    }, 1000);
-  }
-
-  // 判断是否是早上6点钟
-  function checkMorningSix() {
-    const now = new Date();
-    // 设置时间为早上6点,忽略分钟和秒
-    const targetTime = new Date();
-    targetTime.setHours(6, 0, 0, 0);
-
-    // 比较当前时间是否等于早上6点
-    if (now.getTime() === targetTime.getTime()) {
-      rebootNow();
-    }
-  }
-}
-
-/**
- * @检查更新
- */
-function handleToUpgrade() {
-  proxy.$settingStore.handleToUpgrade({
-    success: (res) => {
-      proxy.$refs["oaUpgradeRef"].openUpgrade({
-        modalArray: res.data,
-      });
-    },
-  });
-}
-
-function startActivity() {
-  const yxPlugin = uni.requireNativePlugin("yxPlugin");
-  yxPlugin.startActivity();
-}
-
-onLoad((options) => {
-  setTimeout(() => {
-    init();
-  }, 500);
-});
-
-onShow(() => {});
-
-onUnload(() => {
-  clearInterval(inter.meeting); //销毁之前定时器
-  clearInterval(inter.rebootNow); //销毁之前定时器
-});
-</script>
-<style>
-.faceView {
-  width: 100% !important;
-  height: 100% !important;
-}
-
-iframe {
-  width: 100% !important;
-  height: 100% !important;
-  border-width: 0;
-}
-</style>
-<style lang="scss" scoped>
-:deep() {
-  .u-modal {
-    width: 30rem !important;
-
-    &__title {
-      font-size: 18px !important;
-    }
-    .slot-content {
-      font-size: 16px;
-      width: 100%;
-    }
-  }
-
-  .u-cell__body {
-    padding-left: 0 !important;
-    padding-right: 0 !important;
-  }
-
-  .u-line {
-    border-bottom: 1px solid #dadbde !important;
-  }
-}
-</style>

+ 70 - 0
src/pages/meeting/index.vue

@@ -0,0 +1,70 @@
+<template>
+  <web-view ref="faceView" id="faceView" class="faceView" :src="controlStore.meetingTemplateSrc" bindmessage="receiveMessage" :webview-styles="webviewStyles" @message="onMessage"> </web-view>
+</template>
+<script setup>
+/*----------------------------------依赖引入-----------------------------------*/
+import config from "@/config";
+import { onLoad, onShow, onReady, onHide, onLaunch, onUnload, onNavigationBarButtonTap, onPageScroll } from "@dcloudio/uni-app";
+import { ref, reactive, computed, getCurrentInstance, toRefs, inject, nextTick, watch } from "vue";
+/*----------------------------------接口引入-----------------------------------*/
+/*----------------------------------组件引入-----------------------------------*/
+/*----------------------------------store引入-----------------------------------*/
+import { controlStores } from "@/store/modules/index";
+/*----------------------------------公共变量-----------------------------------*/
+const { proxy } = getCurrentInstance();
+const controlStore = controlStores();
+/*----------------------------------公共变量-----------------------------------*/
+const state = reactive({
+  webviewStyles: {
+    width: "100%",
+    height: "100%",
+  },
+});
+const { webviewStyles } = toRefs(state);
+
+// 初始化
+function init() {
+  controlStore.openInterval("ipAddress"); //定时获取IP地址
+  controlStore.pageFunction = ["会议"];
+  controlStore.initCamera(); //初始化摄像头
+  // controlStore.initNfc();
+  controlStore.initData(); //初始化数据
+  controlStore.openInterval("meeting"); //定时获取会议信息
+
+  controlStore.doorControl(false); //关闭门禁
+}
+
+/**
+ * @接收子页面传过来的值
+ */
+function onMessage(e) {
+  controlStore.analysisData(e.detail.data[0]);
+}
+// #ifdef H5
+window.onmessage = function (event) {
+  controlStore.analysisData(event.data);
+};
+// #endif
+
+onLoad((options) => {
+  init();
+});
+
+onShow(() => {});
+
+onUnload(() => {
+  controlStore.clearInterval("meeting");
+});
+</script>
+<style>
+.faceView {
+  width: 100% !important;
+  height: 100% !important;
+}
+
+iframe {
+  width: 100% !important;
+  height: 100% !important;
+  border-width: 0;
+}
+</style>

+ 158 - 0
src/pages/meeting/setting/index.vue

@@ -0,0 +1,158 @@
+<template>
+  <oa-scroll
+    customClass="meetingSetting-container scroll-height"
+    :customStyle="{
+      //#ifdef APP-PLUS || MP-WEIXIN
+      height: `calc(100vh - (0px))`,
+      //#endif
+      //#ifdef H5
+      height: `calc(100vh - (0px))`,
+      //#endif
+    }"
+    :refresherLoad="false"
+    :refresherEnabled="false"
+    :refresherDefaultStyle="'none'"
+    :refresherBackground="'#f5f6f7'"
+    :data-theme="'theme-' + proxy.$settingStore.themeColor.name"
+  >
+    <template #default>
+      <view class="setting-header">
+        <text class="iconfont oaIcon-exit" @click="handleExit()"></text>
+        <view class="setting-title">会议配置</view>
+      </view>
+
+      <view class="setting-content">
+        <u-cell-group>
+          <u-cell title="门禁开关">
+            <template #value>
+              <u-switch v-model="setting.doorStatus" size="15" @change="controlStore.doorControl"></u-switch>
+            </template>
+          </u-cell>
+          <u-cell title="导航栏显示">
+            <template #value>
+              <u-switch v-model="setting.navBarNew" size="15" @change="navBarNewChange"></u-switch>
+            </template>
+          </u-cell>
+          <u-cell title="软件版本号" :value="version"></u-cell>
+          <u-cell title="IP" :value="controlStore.ipAddress || '-'"></u-cell>
+          <u-cell title="设备型号" :value="sysPlugins.getDeviceInfo().model || '-'"></u-cell>
+          <u-cell title="设备序列号" :value="sysPlugins.getDeviceInfo().serial || '-'"></u-cell>
+          <u-cell title="检查更新" @click="handleToUpgrade()">
+            <template #value>
+              <view class="iconfont oaIcon-jianchagengxin menu-item-icon mr2" style="color: #2979ff"></view>
+            </template>
+          </u-cell>
+          <u-cell title="设备重启" @click="proxy.$yx.rebootNow()">
+            <template #value>
+              <u-icon name="reload" color="#2979ff" size="20"></u-icon>
+            </template>
+          </u-cell>
+        </u-cell-group>
+      </view>
+    </template>
+  </oa-scroll>
+
+  <oa-upgrade ref="oaUpgradeRef" :themesColor="proxy.$settingStore.themeColor.color" />
+</template>
+
+<script setup>
+/*----------------------------------依赖引入-----------------------------------*/
+import config from "@/config";
+import { onLoad, onShow, onUnload } from "@dcloudio/uni-app";
+import { ref, reactive, computed, getCurrentInstance, toRefs } from "vue";
+/*----------------------------------组件引入-----------------------------------*/
+/*----------------------------------store引入-----------------------------------*/
+import { controlStores } from "@/store/modules/index";
+/*----------------------------------公共方法引入-----------------------------------*/
+import sysPlugins from "@/plugins/device/sys.plugins";
+/*----------------------------------公共变量-----------------------------------*/
+const { proxy } = getCurrentInstance();
+const controlStore = controlStores();
+const oaUpgradeRef = ref(null);
+/*----------------------------------公共变量-----------------------------------*/
+const state = reactive({
+  version: computed(() => {
+    return config.appInfo.version;
+  }),
+  setting: {
+    doorStatus: false,
+    navBarNew: true,
+    ipAddress: "",
+  },
+});
+const { version, setting } = toRefs(state);
+
+/**
+ * @退出
+ */
+function handleExit() {
+  proxy.$tab.navigateBack(2);
+  controlStore.initCamera(); //初始化摄像头
+}
+
+// 隐藏导航栏
+function navBarNewChange(value) {
+  proxy.$yx.navBarNewChange(value);
+}
+
+/**
+ * @检查更新
+ */
+function handleToUpgrade() {
+  proxy.$settingStore.handleToUpgrade({
+    success: (res) => {
+      proxy.$refs["oaUpgradeRef"].openUpgrade({
+        modalArray: res.data,
+      });
+    },
+  });
+}
+
+onLoad((options) => {});
+
+onShow(() => {});
+
+onUnload(() => {});
+</script>
+
+<style lang="scss" scoped>
+.meetingSetting-container {
+  background: white;
+}
+
+.setting-header {
+  display: flex;
+  align-items: center;
+  padding: 20px;
+  background: #fff;
+  border-bottom: 1px solid #e5e5e5;
+
+  .iconfont {
+    font-size: 24px;
+    color: #333;
+    margin-right: 15px;
+    cursor: pointer;
+  }
+
+  .setting-title {
+    font-size: 20px;
+    font-weight: 600;
+    color: #333;
+  }
+}
+
+.setting-content {
+  padding: 20px;
+}
+
+:deep() {
+  .u-cell__body {
+    padding-left: 0 !important;
+    padding-right: 0 !important;
+  }
+
+  .u-line {
+    border-bottom: 1px solid #dadbde !important;
+  }
+}
+</style>

+ 117 - 0
src/pages/meeting/setting/password.vue

@@ -0,0 +1,117 @@
+<template>
+  <view class="password-container">
+    <view class="password-box">
+      <view class="password-title">请输入配置密码</view>
+      <view class="password-input-box">
+        <u-input v-model="password" type="password" :placeholder="'请输入密码'" border="none" :clearable="true" :maxlength="20" @confirm="handleConfirm"></u-input>
+      </view>
+      <view class="password-tip" v-if="errorTip">{{ errorTip }}</view>
+      <view class="password-buttons">
+        <u-button type="primary" :loading="loading" @click="handleConfirm" class="password-button"> 确认 </u-button>
+        <u-button type="default" @click="handleCancel" class="password-button"> 取消 </u-button>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script setup>
+/*----------------------------------依赖引入-----------------------------------*/
+import { ref, reactive } from "vue";
+import { onLoad } from "@dcloudio/uni-app";
+import { getCurrentInstance } from "vue";
+import { controlStores } from "@/store/modules/index";
+/*----------------------------------公共变量-----------------------------------*/
+const { proxy } = getCurrentInstance();
+const controlStore = controlStores();
+const password = ref("");
+const errorTip = ref("");
+const loading = ref(false);
+
+// 默认密码,可以从配置或存储中读取
+const defaultPassword = "123456";
+
+/**
+ * @确认密码
+ */
+function handleConfirm() {
+  if (!password.value) {
+    errorTip.value = "请输入密码";
+    return;
+  }
+
+  loading.value = true;
+  errorTip.value = "";
+
+  // 模拟密码验证(实际可以从存储或服务器验证)
+  setTimeout(() => {
+    loading.value = false;
+
+    if (password.value === defaultPassword) {
+      // 密码正确,跳转到配置页面
+      proxy.$tab.navigateTo("/pages/meeting/setting/index");
+    } else {
+      errorTip.value = "密码错误,请重新输入";
+      password.value = "";
+    }
+  }, 300);
+}
+
+/**
+ * @取消
+ */
+function handleCancel() {
+  proxy.$tab.navigateBack(1);
+  controlStore.initCamera(); //初始化摄像头
+}
+
+onLoad(() => {});
+</script>
+
+<style lang="scss" scoped>
+.password-container {
+  width: 100%;
+  height: 100vh;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+}
+
+.password-box {
+  width: 90%;
+  max-width: 400px;
+  background: #fff;
+  border-radius: 16px;
+  padding: 40px 30px;
+  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1);
+}
+
+.password-title {
+  font-size: 24px;
+  font-weight: 600;
+  color: #333;
+  text-align: center;
+  margin-bottom: 30px;
+}
+
+.password-input-box {
+  margin-bottom: 20px;
+}
+
+.password-tip {
+  color: #fa3534;
+  font-size: 14px;
+  text-align: center;
+  margin-bottom: 20px;
+  min-height: 20px;
+}
+
+.password-buttons {
+  display: flex;
+  gap: 15px;
+}
+
+.password-button {
+  flex: 1;
+}
+</style>

+ 2 - 21
src/permission.js

@@ -1,27 +1,11 @@
 import { getToken } from "@/utils/auth";
-import setting from "@/plugins/setting.plugins";
 
 // 登录页面
 const loginPage = "/pages/login";
 
 // 页面白名单
 const whiteList = [
-  "/pages/login",//登录
-  "/pages/register",//注册
-  "/pages/serveConfig",//服务器配置
-  "/pages/serveConfigSelect",//服务器配置
-  "/pages/common/textview/index",//浏览文本
-  "/pages/common/webview/index",
-  "/pages/business/mhxf/unitInfoCollection/index",//单位信息采集
-  "/pages/common/invoicing/index",//开票管理
-  "/pages/common/success/index",//成功
-  "/pages/common/phoneVerify/index",//手机号验证
-  "/pages/business/fireIot/repairReport/index",//报修申请
-  "/pages/business/fireIot/repairReport/record",//报修历史
-  "/pages/common/evaluate/record",//服务评价
-  "/pages/common/NFC/index",//NFC读取
-  "/pages/common/appMessage/details",//消息详情
-  "/pages/face/index",//人脸识别
+  "/pages/meeting/index",//会议系统
   "/pages/door/index",//门禁识别
   "/pages/door/setting/index",//门禁配置
 ];
@@ -37,13 +21,10 @@ let list = ["navigateTo", "redirectTo", "reLaunch", "switchTab"];
 list.forEach((item) => {
   uni.addInterceptor(item, {
     invoke(to) {
-      // #ifdef APP-PLUS
-      setting.formatSize(); //获取缓存大小
-      // #endif
 
       if (getToken()) {
         if (to.url === loginPage) {
-          uni.reLaunch({ url: "/pages/index" });
+          uni.reLaunch({ url: "/pages/meeting/index" });
         }
         return true;
       } else {

+ 23 - 5
src/plugins/device/sys.plugins.js

@@ -44,16 +44,22 @@ export default {
         }
     },
     /**
-     * @获取有线网的IP地址
+     * @获取IP地址
      */
-    getEthernetIpAddress() {
+    getIpAddress({ success, error }) {
         //#ifdef APP-PLUS
         const sysPlugin = uni.requireNativePlugin("sysPlugin");
         sysPlugin.initWithContext((code) => {
             if (code == 'SUCCESS') {
-                console.log(sysPlugin.getEthernetIpAddress())
-
-                return sysPlugin.getEthernetIpAddress();
+                if (sysPlugin.getEthernetIpAddress()) {
+                    // console.log(sysPlugin.getEthernetIpAddress())
+                    success(sysPlugin.getEthernetIpAddress())
+                } else if (sysPlugin.getWifiIpAddress()) {
+                    // console.log(sysPlugin.getWifiIpAddress())
+                    success(sysPlugin.getWifiIpAddress())
+                } else {
+                    error('设备无网络连接!')
+                }
             } else {
                 modal.msg(code)
             }
@@ -104,5 +110,17 @@ export default {
         const sysPlugin = uni.requireNativePlugin("sysPlugin");
         return sysPlugin.getSdk();
         //#endif
+    },
+    /**
+     * @获取设备信息集合
+     */
+    getDeviceInfo() {
+        return {
+            serial: this.getSerial(),//获取设备序列号
+            model: this.getModel(),//获取设备型号
+            manufacturer: this.getManufacturer(),//获取厂商
+            version: this.getVersion(),//获取系统版本
+            Sdk: this.getSdk(),//获取SDK版本
+        }
     }
 };

+ 18 - 17
src/plugins/device/yx.plugins.js

@@ -4,23 +4,6 @@ import modal from "../modal.plugins";
  * @武汉智联硬件api
  */
 export default {
-    /**
-     * 离线开门
-     * @isOpen  开关门(1开、2常闭、3常开)
-     */
-    openDoor(isOpen) {
-        const yxPlugin = uni.requireNativePlugin("yxPlugin");
-        if (isOpen == '1') {
-            yxPlugin.setDoor("开");
-            setTimeout(() => {
-                yxPlugin.setDoor("关");
-            }, 2000);
-        } else if (isOpen == '2') {
-            yxPlugin.setDoor("关");
-        } else if (isOpen == '3') {
-            yxPlugin.setDoor("开");
-        }
-    },
     /**
      * @设置灯光
      * @param {        
@@ -37,5 +20,23 @@ export default {
         const yxPlugin = uni.requireNativePlugin("yxPlugin");
         yxPlugin.setLed(value);
         //#endif
+    },
+    /**
+     * @设备重启 
+    */
+    rebootNow() {
+        //#ifdef APP-PLUS
+        const yxPlugin = uni.requireNativePlugin("yxPlugin");
+        yxPlugin.rebootNow();
+        //#endif
+    },
+    /**
+     * @隐藏导航栏
+     */
+    navBarNewChange(value) {
+        //#ifdef APP-PLUS
+        const yxPlugin = uni.requireNativePlugin("yxPlugin");
+        yxPlugin.setNavBarNew(value);
+        //#endif
     }
 };

+ 1 - 1
src/plugins/permission.plugins.js

@@ -90,7 +90,7 @@ function judgeIosPermissionRecord(ifRequest) {
 function judgeIosPermissionCamera(ifRequest) {
 	return new Promise(resolve => {
 		var AVCaptureDevice = plus.ios.import("AVCaptureDevice");
-		var authStatus = AVCaptureDevice.authorizationStatusForMediaType('vide');
+		var authStatus = AVCaptureDevice.authorizationStatusForMediaType('video');
 		if (authStatus == 3) {
 			resolve(true)
 		} else {

+ 7 - 0
src/static/face/door.html

@@ -115,6 +115,13 @@
             }
         }
     </style>
+    <script>
+        // 提前占位,防止宿主在页面初始化前调用 receiveData 导致未定义
+        window.__pendingReceiveData = [];
+        window.receiveData = function (msg) {
+            window.__pendingReceiveData.push(msg);
+        };
+    </script>
 </head>
 
 <body>

BIN
src/static/face/img/meeting/meeting_admin.png


BIN
src/static/face/img/meeting/meeting_bg.jpg


BIN
src/static/face/img/meeting/meeting_loading.gif


BIN
src/static/face/img/meeting/meeting_unbound.png


Datei-Diff unterdrückt, da er zu groß ist
+ 0 - 0
src/static/face/js/dayjs.min.js


+ 713 - 0
src/static/face/js/meeting-page.js

@@ -0,0 +1,713 @@
+// 会议人脸识别页面逻辑封装,可在多个 HTML 中复用
+// 依赖:vue.global.prod.js、tracking-min.js、face-min.js、jquery-2.2.1.min.js、uni.webview.1.5.4.js、dayjs.min.js
+
+; (function (global) {
+    function initMeetingPage(options) {
+        options = options || {};
+
+        var app = Vue.createApp({
+            components: {},
+            emits: [],
+            props: {},
+            data() {
+                return {
+                    flag: true,
+                    time: 2000,
+                    tracker: null,
+                    trackerTask: null,
+                    state: {
+                        dataAll: {},
+                        thisVenueData: [],
+                        thisVenueTime: {},
+                        nextSceneData: [],
+                        nextSceneTime: {},
+                        timeList: [],
+                        faceImgState: true,
+                        meetingTemplate: {},
+                    },
+                    timeOutEvent: 0,
+
+                    mettingTypes: ['date', 'week', 'meeting-name', 'meeting-when-title', 'meeting-when-date', 'meeting-exit-title', "meeting-exit-date"],//文字类型元素
+                };
+            },
+            computed: {},
+            methods: {
+                // 初始化数据
+                initData(event) {
+                    this.state.dataAll = event.dataAll;
+                    this.state.thisVenueData = event.thisVenueData;
+                    this.state.thisVenueTime = event.thisVenueTime;
+                    this.state.nextSceneData = event.nextSceneData;
+                    this.state.nextSceneTime = event.nextSceneTime;
+                    this.state.timeList = event.timeList;
+
+                    this.initTemplateData();
+                },
+                // 初始化模板数据
+                initTemplateData() {
+                    var that = this;
+                    this.mettingTypes.forEach((type) => {
+                        // 使用 jQuery 的 .each() 方法遍历元素
+                        $(`.${type}`).each(function (index, element) {
+                            var $dom = $(element); // 将原生DOM元素转换为jQuery对象
+
+                            //date:日期、week:周
+                            //meeting-name:会议室名称、meeting-when-title:当场会议、meeting-when-date:当场会议时间
+                            //meeting-exit-title:下场会议、meeting-exit-date:下场会议时间
+
+                            if (type == "date") {
+                                $dom.text(dayjs().format('YYYY年MM月DD日'));
+                            } else if (type == "week") {
+                                const weekChinese = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];  // 定义映射数组(索引对应 0~6)
+                                $dom.text(weekChinese[dayjs().day()]);
+                            } else if (type == "meeting-name") {
+                                $dom.text(that.state.dataAll?.roomName || "请配置会议室");
+                            } else if (type == "meeting-when-title") {
+                                $dom.text(that.state.thisVenueData?.meetingName || "会议空闲中");
+                            } else if (type == "meeting-when-date") {
+                                $dom.text(JSON.stringify(that.state.thisVenueTime) != "{}" ? that.state.thisVenueTime.startTime + "-" + that.state.thisVenueTime.endTime : "00:00-00:00");
+                            } else if (type == "meeting-exit-title") {
+                                $dom.text(that.state.nextSceneData?.meetingName || "会议空闲中");
+                            } else if (type == "meeting-exit-date") {
+                                $dom.text(JSON.stringify(that.state.nextSceneTime) != "{}" ? that.state.nextSceneTime.startTime + "-" + that.state.nextSceneTime.endTime : "00:00-00:00");
+                            }
+                        });
+                    });
+
+                    // 更新二维码图片
+                    if ($('#qrCode').length > 0) {
+                        // 获取二维码URL(可以从配置项或会议信息中获取)
+                        var qrCodeUrl = '';
+                        var meetingId = that.state.thisVenueData?.meetingId || '';
+
+                        // 使用会议ID生成二维码
+                        qrCodeUrl = 'https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=' + encodeURIComponent(meetingId ? meetingId : "会议空闲中");
+
+                        $('#qrCode').attr('src', qrCodeUrl);
+                    }
+                },
+                // 初始化事件
+                initHandle() {
+                    var that = this;
+                    $('#openConfig').on({
+                        touchstart: function (e) {
+                            that.timeOutEvent = setTimeout(() => {
+                                that.parentMessage('打开会议配置');
+                                that.timeOutEvent = 0;
+                            }, 1000);
+                            e.preventDefault();
+                        },
+                        touchmove: function () {
+                            clearTimeout(that.timeOutEvent);
+                            that.timeOutEvent = 0;
+                        },
+                        touchend: function () {
+                            clearTimeout(that.timeOutEvent);
+                            if (that.timeOutEvent != 0) {
+                                console.log('你这是点击,不是长按');
+                            }
+                            return false;
+                        },
+                    });
+                },
+                // 初始化摄像头
+                initVido() {
+                    var that = this;
+
+                    var video = document.getElementById('video'); // 视频dom
+                    if (!video) return;
+                    video.style.transform = 'scaleX(-1)'; // 水平翻转
+                    var canvas = document.getElementById('canvas'); // 画布dom
+                    if (!canvas) return;
+                    canvas.style.transform = 'scaleX(-1)'; // 水平翻转
+                    var context = canvas.getContext('2d');
+                    that.tracker = new tracking.ObjectTracker('face');
+                    that.tracker.setInitialScale(4); // 识别放大比例
+                    that.tracker.setStepSize(2); // 步长
+                    that.tracker.setEdgesDensity(0.1); // 边缘密度
+                    // 启动摄像头,并且识别视频内容
+                    that.trackerTask = tracking.track('#video', that.tracker, {
+                        camera: true,
+                    });
+
+                    that.tracker.on('track', function (event) {
+                        if (event.data.length === 0) {
+                            // 未检测到人脸
+                            context.clearRect(0, 0, canvas.width, canvas.height);
+                        } else if (event.data.length > 1) {
+                            // 检测到多张人脸
+                        } else {
+                            context.clearRect(0, 0, canvas.width, canvas.height);
+                            event.data.forEach(function (rect) {
+                                context.strokeStyle = '#409eff';
+                                context.strokeRect(rect.x, rect.y, rect.width, rect.height);
+                                context.fillStyle = '#409eff';
+                                context.lineWidth = 1.5;
+                            });
+                            if (that.flag) {
+                                console.log('拍照');
+                                that.state.faceImgState = false;
+                                context.drawImage(video, 0, 0, video.width, video.height);
+                                that.saveAsLocalImage(); // 调用获取图片
+                                context.clearRect(0, 0, canvas.width, canvas.height);
+                                that.flag = false;
+                            } else {
+                                // 冷却中
+                            }
+                        }
+                    });
+                },
+                // 向父页面推送数据
+                parentMessage(type, data) {
+                    var message = {
+                        funcName: type,
+                        data: data,
+                    };
+
+                    // APP-PLUS
+                    if (typeof uni !== 'undefined' && uni.postMessage) {
+                        uni.postMessage({
+                            data: message,
+                        });
+                    }
+
+                    // H5
+                    if (window.parent) {
+                        window.parent.postMessage(message, '*');
+                    }
+                },
+                // 获取图片
+                saveAsLocalImage() {
+                    var myCanvas = document.getElementById('canvas');
+                    if (!myCanvas) return;
+                    var image = myCanvas
+                        .toDataURL('image/png')
+                        .replace('image/png', 'image/octet-stream');
+                    this.parentMessage('人脸识别', { imageBase: image });
+                },
+                // 人脸冷却
+                faceCooling() {
+                    var that = this;
+                    setTimeout(() => {
+                        that.flag = true;
+                        that.state.faceImgState = false;
+                    }, that.time);
+                },
+                // 解析数据
+                analysisData(event) {
+                    if (!event) return;
+                    console.log(event.funcName);
+                    if ('funcName' in event) {
+                        if (event.funcName == '初始化数据') {
+                            this.initData(JSON.parse(event.data));
+                        } else if (event.funcName == '初始化模板') {
+                            this.initTemplate(JSON.parse(event.data))
+                        } else if (
+                            event.funcName == '开启摄像头' &&
+                            document.getElementById('video')
+                        ) {
+                            this.initVido(); // 调用初始化摄像头
+                        } else if (event.funcName == '关闭摄像头') {
+                            this.closeVideo();
+                        } else if (event.funcName == '人脸冷却') {
+                            this.faceCooling();
+                        }
+                    }
+                },
+                // 监听页面是否隐藏
+                handleVisibilityChange() {
+                    if (document.visibilityState === 'visible') {
+                        console.log('页面变为可见');
+                        this.tracker == null && document.getElementById('video')
+                            ? this.initVido()
+                            : undefined;
+                    } else if (document.visibilityState === 'hidden') {
+                        console.log('页面变为不可见');
+                        this.tracker != null && document.getElementById('video')
+                            ? this.closeVideo()
+                            : undefined;
+                    }
+                },
+                // 关闭摄像头
+                closeVideo() {
+                    try {
+                        this.tracker = null;
+                        // 关闭摄像头
+                        var video = document.getElementById('video');
+                        if (video && video.srcObject && video.srcObject.getTracks) {
+                            var tracks = video.srcObject.getTracks();
+                            tracks && tracks[0] && tracks[0].stop();
+                        }
+                        // 停止侦测
+                        this.trackerTask && this.trackerTask.stop && this.trackerTask.stop();
+                    } catch (error) { }
+                },
+                // 初始化模板
+                initTemplate(event) {
+                    var _this = this
+
+                    if (JSON.stringify(event) == "{}") {
+                        createUnbound();
+                        return;
+                    }
+
+                    if (this.state.meetingTemplate && event.id == this.state.meetingTemplate.id) {
+                        this.state.meetingTemplate = event
+                        return;
+                    } else {
+                        this.state.meetingTemplate = event
+                    }
+
+                    var s1Con = this.state.meetingTemplate.s1Con ? JSON.parse(this.state.meetingTemplate.s1Con) : []
+                    if (!s1Con) return
+
+                    var stage = document.getElementById(options.containerId);
+                    if (!stage) return
+
+                    const [w, h] = (this.state.meetingTemplate.fbl || window.innerWidth + 'x' + window.innerHeight).split('x');
+                    stage.style.width = w + 'px'
+                    stage.style.height = h + 'px'
+                    stage.innerHTML = ''; // 清空原有内容
+
+                    function createText(item) {
+                        var $el = $('<div>', {
+                            class: item.type,
+                            text: item.content || ''
+                        }).css({
+                            'position': 'absolute',
+                            'left': item.x + 'px',
+                            'top': item.y + 'px',
+                            'width': item.width + 'px',
+                            'height': item.height + 'px',
+                            'font-size': item.fontSize + 'px',
+                            'color': item.color || '#000',
+                            'font-family': (item.fontFamily || 'Microsoft YaHei') + ', sans-serif',
+                            'line-height': item.lineHeight || 1.4,
+                            'font-weight': item.fontWeight || 'normal',
+                            'font-style': item.fontStyle || 'normal',
+                            'text-decoration': item.textDecoration || 'none',
+                            'letter-spacing': (item.letterSpacing || 0) + 'px',
+                            'text-align': item.textAlign || 'left',
+                            'display': 'flex',
+                            'align-items': item.textAlign || 'left',
+                            'justify-content': item.textAlign || 'left',
+                            'word-break': 'break-all',
+                            'background-color': item.backgroundColor || 'transparent'
+                        });
+
+                        // 返回原生DOM元素(保持与原有代码兼容)
+                        return $el[0];
+                    }
+
+                    function createImage(item) {
+                        var $wrapper = $('<div>', {
+                            id: 'home-card-left-image',
+                            class: 'image-item'
+                        }).css({
+                            'position': 'absolute',
+                            'left': item.x + 'px',
+                            'top': item.y + 'px',
+                            'width': item.width + 'px',
+                            'height': item.height + 'px',
+                            'overflow': 'hidden',
+                            'display': 'flex',
+                            'align-items': 'center',
+                            'justify-content': 'center'
+                        });
+
+                        var $img = $('<img>', {
+                            src: item.src,
+                            alt: item.alt || item.name || '图片'
+                        }).css({
+                            'width': '100%',
+                            'height': '100%',
+                            'object-fit': 'fill'
+                        });
+
+                        $wrapper.append($img);
+
+                        // 返回原生DOM元素(保持与原有代码兼容)
+                        return $wrapper[0];
+                    }
+
+                    function createVideo(item) {
+                        var wrapper = document.createElement('div');
+                        wrapper.className = 'video-item';
+                        wrapper.style.position = 'absolute';
+                        wrapper.style.left = item.x + 'px';
+                        wrapper.style.top = item.y + 'px';
+                        wrapper.style.width = item.width + 'px';
+                        wrapper.style.height = item.height + 'px';
+                        wrapper.style.overflow = 'hidden';
+                        wrapper.style.display = 'flex';
+                        wrapper.style.alignItems = 'center';
+                        wrapper.style.justifyContent = 'center';
+                        wrapper.style.backgroundColor = item.backgroundColor || '#000';
+                        // wrapper.style.border = "2px red solid"
+
+                        var video = document.createElement('video');
+                        video.style.width = '100%';
+                        video.style.height = '100%';
+                        video.style.objectFit = item.objectFit || 'cover';
+                        video.autoplay = item.autoplay !== undefined ? item.autoplay : true; // 自动播放
+                        video.loop = false; // 统一关闭循环,改用 ended 事件手动重播
+                        video.muted = false; // 不静音
+                        video.controls = true;//允许用户控制视频的播放,包括音量,跨帧,暂停/恢复播放。
+                        if (item.poster) video.poster = item.poster;
+                        video.preload = item.preload || 'auto';
+                        video.src = item.src;
+
+                        video.addEventListener('ended', function () {
+                            video.load();//重新加载视频文件
+                            video.play();//播放视频
+                        });
+
+                        wrapper.appendChild(video);
+                        return wrapper;
+                    }
+
+                    function createCarousel(item) {
+                        var wrapper = document.createElement('div');
+                        wrapper.className = 'carousel';
+                        wrapper.style.position = 'absolute';
+                        wrapper.style.left = item.x + 'px';
+                        wrapper.style.top = item.y + 'px';
+                        wrapper.style.width = item.width + 'px';
+                        wrapper.style.height = item.height + 'px';
+                        wrapper.style.overflow = 'hidden';
+                        wrapper.style.backgroundColor = item.backgroundColor || '#f6f6f6';
+                        wrapper.style.display = 'flex';
+                        wrapper.style.alignItems = 'center';
+                        wrapper.style.justifyContent = 'center';
+                        wrapper.style.color = '#8f959f';
+
+                        if (!item.images || !item.images.length) {
+                            wrapper.textContent = '轮播图';
+                            return wrapper;
+                        }
+
+                        var img = document.createElement('img');
+                        img.style.width = '100%';
+                        img.style.height = '100%';
+                        img.style.objectFit = 'cover';
+                        wrapper.appendChild(img);
+
+                        var index = 0;
+                        var interval = item.interval || 3000;
+                        var timer = null;
+
+                        function showImage(nextIndex) {
+                            if (typeof nextIndex === 'number') {
+                                index = (nextIndex + item.images.length) % item.images.length;
+                            }
+                            img.src = item.images[index].url;
+                            index = (index + 1) % item.images.length;
+                        }
+
+                        function restartTimer() {
+                            if (timer) clearInterval(timer);
+                            timer = setInterval(showImage, interval);
+                        }
+
+                        function goPrev() {
+                            var prevIndex = index - 2; // index 已指向下一张,回退需 -2
+                            showImage(prevIndex);
+                            restartTimer();
+                        }
+
+                        function goNext() {
+                            showImage(index);
+                            restartTimer();
+                        }
+
+                        showImage(index);
+                        restartTimer();
+
+                        var startX = 0;
+                        var threshold = 30; // 滑动触发距离
+                        wrapper.addEventListener('touchstart', function (e) {
+                            if (!e.touches || !e.touches.length) return;
+                            startX = e.touches[0].clientX;
+                        });
+                        wrapper.addEventListener('touchend', function (e) {
+                            if (!e.changedTouches || !e.changedTouches.length) return;
+                            var deltaX = e.changedTouches[0].clientX - startX;
+                            if (Math.abs(deltaX) < threshold) return;
+                            if (deltaX > 0) {
+                                goPrev();
+                            } else {
+                                goNext();
+                            }
+                        });
+
+                        return wrapper;
+                    }
+
+                    function createAI(item) {
+                        var wrapper = document.createElement('div');
+                        wrapper.className = 'image-item';
+                        wrapper.style.position = 'absolute';
+                        wrapper.style.left = item.x + 'px';
+                        wrapper.style.top = item.y + 'px';
+                        wrapper.style.width = item.width + 'px';
+                        wrapper.style.height = item.height + 'px';
+                        wrapper.style.overflow = 'hidden';
+                        wrapper.style.display = 'flex';
+                        wrapper.style.alignItems = 'center';
+                        wrapper.style.justifyContent = 'center';
+                        wrapper.style.background = 'transparent';
+
+                        var iframe = document.createElement('iframe');
+                        iframe.src = item.src;
+                        iframe.style.width = '100%';
+                        iframe.style.height = '100%';
+                        iframe.style.border = 'none';
+                        iframe.style.frameborder = 0;
+                        iframe.style.allowfullscreen = true;
+                        iframe.style.background = 'transparent';
+
+                        wrapper.appendChild(iframe);
+                        return wrapper;
+                    }
+
+                    function createSignIn(item) {
+                        // 创建主容器
+                        var $wrapper = $('<div>', {
+                            class: 'SignIn-item'
+                        }).css({
+                            'position': 'absolute',
+                            'left': item.x + 'px',
+                            'top': item.y + 'px',
+                            'width': item.width + 'px',
+                            'height': item.height + 'px',
+                            'overflow': 'hidden',
+                            'display': 'flex',
+                            'flex-direction': 'column',
+                            'align-items': 'center',
+                            'justify-content': 'center',
+                            'background': 'transparent',
+                            'box-sizing': 'border-box',
+                        });
+
+                        // 创建二维码图片
+                        var $qrCodeImg = $('<img>', {
+                            src: "",
+                            id: "qrCode"
+                        }).css({
+                            'width': '100%',
+                            'height': '70%',
+                            'object-fit': 'contain'
+                        });
+
+
+                        var $face = $('<div>', {
+                            id: "face"
+                        }).css({
+                            'width': '100%',
+                            'height': '80%',
+                            'position': 'relative',
+                            'top': '0px',
+                            'left': '0px',
+                            'display': 'none',
+                        });
+                        // 创建视频元素
+                        var $video = $('<video>', {
+                            id: "video",
+                            preload: true,
+                            autoplay: true,
+                            loop: true,
+                            muted: true
+                        }).css({
+                            'width': '100%',
+                            'height': '100%',
+                            'position': 'absolute',
+                            'top': '0px',
+                        });
+                        // 创建画布元素
+                        var $canvas = $('<canvas>', {
+                            id: "canvas",
+                        }).css({
+                            'width': '100%',
+                            'height': '100%',
+                            'position': 'absolute',
+                            'top': '0px',
+                        });
+
+                        // 创建状态文本
+                        var $statusText = $('<div>', {
+                            text: '请扫描二维码签到'
+                        }).css({
+                            'font-size': '18px',
+                            'color': 'red',
+                            'margin-top': '10px',
+                            'text-align': 'center',
+                            'line-height': '1.5'
+                        });
+
+                        // 创建按钮容器
+                        var $buttonContainer = $('<div>').css({
+                            'display': 'flex',
+                            'gap': '15px',
+                            'margin-top': '10px',
+                            'width': '100%',
+                            'justify-content': 'center'
+                        });
+
+                        // 创建人脸按钮
+                        var $faceButton = $('<button>', {
+                            text: '人脸'
+                        }).css({
+                            'flex': '1',
+                            'max-width': '120px',
+                            'height': '40px',
+                            'background-color': '#c41e3a',
+                            'color': '#fff',
+                            'border': 'none',
+                            'border-radius': '8px',
+                            'font-size': '16px',
+                            'cursor': 'pointer',
+                            'font-weight': '500',
+                            'transition': 'background-color 0.3s'
+                        }).on('click', function () {
+                            $qrCodeImg.hide();
+                            $statusText.hide();
+                            $face.show();
+
+                            _this.initVido();
+                        });
+
+                        // 创建二维码按钮
+                        var $qrButton = $('<button>', {
+                            text: '二维码'
+                        }).css({
+                            'flex': '1',
+                            'max-width': '120px',
+                            'height': '40px',
+                            'background-color': '#c41e3a',
+                            'color': '#fff',
+                            'border': 'none',
+                            'border-radius': '8px',
+                            'font-size': '16px',
+                            'cursor': 'pointer',
+                            'font-weight': '500',
+                            'transition': 'background-color 0.3s'
+                        }).on('click', function () {
+                            _this.closeVideo();
+                            $qrCodeImg.show();
+                            $statusText.show();
+                            $face.hide();
+                        });
+
+                        // 组装所有元素
+                        $wrapper
+                            .append($qrCodeImg)
+                            .append($face.append($video).append($canvas))
+                            .append($statusText)
+                            .append($buttonContainer.append($faceButton).append($qrButton));
+
+                        // 返回原生DOM元素(保持与原有代码兼容)
+                        return $wrapper[0];
+                    }
+
+                    function createUnbound() {
+                        // 显示未绑定图片
+                        var stage = document.getElementById(options.containerId);
+                        if (stage) {
+                            stage.style.width = window.innerWidth + 'px';
+                            stage.style.height = window.innerHeight + 'px';
+                            stage.innerHTML = '';
+
+                            var $wrapper = $('<div>', {
+                                class: 'image-item'
+                            }).css({
+                                'position': 'absolute',
+                                'left': '0',
+                                'top': '0',
+                                'width': '100%',
+                                'height': '100%',
+                                'overflow': 'hidden',
+                                'display': 'flex',
+                                'align-items': 'center',
+                                'justify-content': 'center'
+                            });
+
+                            var $img = $('<img>', {
+                                src: './img/meeting/meeting_unbound.png',
+                                alt: '未绑定'
+                            }).css({
+                                'width': '500px',
+                                'object-fit': 'contain'
+                            });
+
+                            $wrapper.append($img);
+                            stage.appendChild($wrapper[0]);
+                        }
+                    }
+
+                    s1Con.forEach(function (item) {
+                        var el = null;
+                        if (item.type === "meeting-signIn") {
+                            el = createSignIn(item);
+                        } else if (item.type === 'text' || item.type == "date" || item.type == "week" || item.type.indexOf('meeting') === 0) {
+                            el = createText(item);
+                        } else if (item.type === 'image') {
+                            el = createImage(item);
+                        } else if (item.type === 'video') {
+                            el = createVideo(item);
+                        } else if (item.type === 'carousel') {
+                            el = createCarousel(item);
+                        } else if (item.type === 'AI') {
+                            el = createAI(item);
+                        }
+
+                        if (el) {
+                            stage.appendChild(el);
+                        }
+                    });
+                }
+            },
+            created() {
+                var that = this;
+                // APP-PLUS || H5(接收父页面传过来的值)
+                window.receiveData = function (msg) {
+                    that.analysisData(msg);
+                };
+                window.addEventListener('message', function (event) {
+                    that.analysisData(event.data);
+                });
+            },
+            mounted() {
+                this.initHandle();
+                document.addEventListener(
+                    'visibilitychange',
+                    this.handleVisibilityChange
+                );
+            },
+            beforeUnmount() {
+                // 不要移除 receiveData,因为可能还有其他地方需要调用
+                // 恢复为占位函数,防止未定义错误
+                window.receiveData = function (msg) {
+                    if (window.__pendingReceiveData) {
+                        window.__pendingReceiveData.push(msg);
+                    }
+                };
+            },
+            watch: {},
+        });
+
+        app.mount('#' + (options.containerId || 'mbContainer'));
+
+        // 如果在 Vue 挂载前已有宿主推送数据,初始化后立即回放
+        if (window.__pendingReceiveData && window.__pendingReceiveData.length) {
+            window.__pendingReceiveData.forEach(function (event) {
+                window.receiveData && window.receiveData(event);
+            });
+            window.__pendingReceiveData = [];
+        }
+    }
+
+    // 导出到全局
+    global.initMeetingPage = initMeetingPage;
+})(window);
+
+

+ 46 - 200
src/static/face/meeting.html

@@ -11,6 +11,17 @@
     <script type="text/javascript" src="./js/vue.global.prod.js"></script>
     <!-- uni 的 SDK -->
     <script type="text/javascript" src="./js/uni.webview.1.5.4.js"></script>
+    <!-- 会议页面封装逻辑 -->
+    <script type="text/javascript" src="./js/meeting-page.js"></script>
+    <!-- dayjs -->
+    <script type="text/javascript" src="./js/dayjs.min.js"></script>
+    <script>
+        // 提前占位,防止宿主在页面初始化前调用 receiveData 导致未定义
+        window.__pendingReceiveData = [];
+        window.receiveData = function (msg) {
+            window.__pendingReceiveData.push(msg);
+        };
+    </script>
     <style>
         html,
         body {
@@ -31,6 +42,13 @@
             }
         }
 
+        .openConfig {
+            position: absolute;
+            width: 100px;
+            height: 100px;
+            z-index: 10000;
+        }
+
         video,
         canvas {
             position: absolute;
@@ -194,21 +212,22 @@
 </head>
 
 <body>
-    <div id="face-container" class="face-container home-card-two">
+    <div id="openConfig" class="openConfig"></div>
+    <div id="mbContainer" class="mbContainer home-card-two">
         <div id="home-card-left" class="home-card-left">
             <image id="home-card-left-image" class="home-card-left-image" src="./img/logo.png" width="125px"
                 height="60px" mode="aspectFill"></image>
             <div class="home-card-left-header">{{state.dataAll?.roomName || "请配置会议室"}}</div>
             <div class="home-card-left-content" style="margin-bottom: 20px;">
                 <div>本场会议</div>
-                <div>{{state.thisVenueData[0]?.meetingName || "空闲中"}}</div>
+                <div>{{state.thisVenueData?.meetingName || "空闲中"}}</div>
                 <div>{{state.thisVenueTime.startTime || "00:00"}} — {{state.thisVenueTime.endTime || "00:00"}}
                 </div>
             </div>
 
             <div class="home-card-left-content">
                 <div>下场会议</div>
-                <div>{{state.nextSceneData[0]?.meetingName || "空闲中"}}</div>
+                <div>{{state.nextSceneData?.meetingName || "空闲中"}}</div>
                 <div>{{state.nextSceneTime.startTime || "00:00"}} — {{state.nextSceneTime.endTime || "00:00"}}
                 </div>
             </div>
@@ -221,11 +240,11 @@
                     <span style="padding: 0px 5px; border-right: 1px #fff solid">PM2.5:10μg/m3</span>
                     <span style="padding: 0 0 0 5px">甲醛:0ppm</span>
                 </div>
-                <div class="home-card-right-header-title">{{state.thisVenueData[0] ? "&nbsp;&nbsp;会议进行中" :
+                <div class="home-card-right-header-title">{{state.thisVenueData ? "&nbsp;&nbsp;会议进行中" :
                     "&nbsp;"}}</div>
             </div>
             <div class="home-card-right-content">
-                <div style="font-size: 20px; margin-bottom: 15px;">{{state.thisVenueData[0]?.meetingName || "空闲中"}}
+                <div style="font-size: 20px; margin-bottom: 15px;">{{state.thisVenueData?.meetingName || "空闲中"}}
                 </div>
                 <div style="font-size: 18px; margin-bottom: 15px;">
                     {{state.thisVenueTime.startTime || "00:00"}} — {{state.thisVenueTime.endTime || "00:00"}}
@@ -250,201 +269,28 @@
         </div>
     </div>
     <script>
-        // 创建Vue实例
-        Vue.createApp({
-            components: {},
-            emits: [],
-            props: {},
-            data() {
-                return {
-                    flag: true,
-                    time: 2000,
-                    tracker: null,
-                    trackerTask: null,
-                    state: {
-                        dataAll: {},
-                        thisVenueData: [],
-                        thisVenueTime: {},
-                        nextSceneData: [],
-                        nextSceneTime: {},
-                        timeList: [],
-                        faceImgState: true,
-                    },
-                    timeOutEvent: 0
-                };
-            },
-            computed: {},
-            methods: {
-                // 初始化数据
-                initData(event) {
-                    this.state.dataAll = event.dataAll
-                    this.state.thisVenueData = event.thisVenueData
-                    this.state.thisVenueTime = event.thisVenueTime
-                    this.state.nextSceneData = event.nextSceneData
-                    this.state.nextSceneTime = event.nextSceneTime
-                    this.state.timeList = event.timeList
-                },
-                // 初始化事件
-                initHandle() {
-                    var that = this;
-                    $("#home-card-left-image").on({
-                        touchstart: function (e) {
-                            that.timeOutEvent = setTimeout(() => {
-                                that.parentMessage('打开会议配置')
-                                that.timeOutEvent = 0
-                            }, 1000);
-                            e.preventDefault();
-                        },
-                        touchmove: function () {
-                            clearTimeout(that.timeOutEvent);
-                            that.timeOutEvent = 0;
-                        },
-                        touchend: function () {
-                            clearTimeout(that.timeOutEvent);
-                            if (that.timeOutEvent != 0) {
-                                console.log("你这是点击,不是长按");
-                            }
-                            return false;
-                        }
-                    })
-                },
-                // 初始化摄像头
-                initVido() {
-                    var that = this;
-
-                    var video = document.getElementById("video");//视频dom
-                    video.style.transform = 'scaleX(-1)';//视频翻转(1.水平翻转-scaleX(-1) 2.垂直翻转-scaleY(-1))
-                    var canvas = document.getElementById('canvas');//画布dom
-                    canvas.style.transform = 'scaleX(-1)';//画布翻转(1.水平翻转-scaleX(-1) 2.垂直翻转-scaleY(-1))
-                    var context = canvas.getContext('2d');
-                    that.tracker = new tracking.ObjectTracker('face');
-                    that.tracker.setInitialScale(4); //设置识别的放大比例
-                    that.tracker.setStepSize(2);//设置步长
-                    that.tracker.setEdgesDensity(0.1);//边缘密度
-                    //启动摄像头,并且识别视频内容
-                    that.trackerTask = tracking.track('#video', that.tracker, {
-                        camera: true
-                    });
-
-                    that.tracker.on('track', function (event) {
-                        if (event.data.length === 0) {
-                            // console.log('未检测到人脸')
-                            context.clearRect(0, 0, canvas.width, canvas.height);
-                        } else if (event.data.length > 1) {
-                            // console.log('检测到多张人脸')
-                        } else {
-                            context.clearRect(0, 0, canvas.width, canvas.height);
-                            event.data.forEach(function (rect) {
-                                context.strokeStyle = '#409eff';
-                                context.strokeRect(rect.x, rect.y, rect.width, rect.height);
-                                context.fillStyle = "#409eff";
-                                context.lineWidth = 1.5;
-                            });
-                            if (that.flag) {
-                                console.log("拍照");
-                                that.state.faceImgState = false;
-                                context.drawImage(video, 0, 0, video.width, video.height);
-                                that.saveAsLocalImage();//调用获取图片bold
-                                context.clearRect(0, 0, canvas.width, canvas.height);
-                                flag = false;
-                            } else {
-                                //console.log("冷却中");
-                            }
-                        }
-                    });
-
-
-                },
-                // 向父页面推送数据
-                parentMessage(type, data) {
-                    var message = {
-                        funcName: type,
-                        data: data,
-                    };
-
-                    //APP-PLUS
-                    uni.postMessage({
-                        data: message
-                    });
-
-                    //H5
-                    if (window.parent) {
-                        window.parent.postMessage(message, '*');
-                    }
-                },
-                // 获取图片bold
-                saveAsLocalImage() {
-                    var myCanvas = document.getElementById("canvas");
-                    var image = myCanvas.toDataURL("image/png").replace("image/png", "image/octet-stream");
-                    this.parentMessage('人脸识别', { imageBase: image })
-                },
-                // 人脸冷却
-                faceCooling() {
-                    var that = this
-                    setTimeout(() => {
-                        that.flag = true
-                        that.state.faceImgState = false;
-                    }, that.time);
-                },
-                // 解析数据
-                analysisData(event) {
-                    console.log(event.funcName)
-                    if ("funcName" in event) {
-                        if (event.funcName == "初始化数据") {
-                            this.initData(JSON.parse(event.data));
-                        } else if (event.funcName == "开启摄像头" && document.getElementById("video")) {
-                            this.initVido();//调用初始化摄像头
-                        } else if (event.funcName == "关闭摄像头") {
-                            this.closeFace();
-                        } else if (event.funcName == "人脸冷却") {
-                            this.faceCooling();
-                        }
-                    }
-                },
-                // 监听页面是否隐藏
-                handleVisibilityChange() {
-                    if (document.visibilityState === 'visible') {
-                        // 页面变为可见时的处理逻辑
-                        console.log('页面变为可见');
-                        this.tracker == null && document.getElementById("video") ? this.initVido() : undefined
-                    } else if (document.visibilityState === 'hidden') {
-                        // 页面变为不可见时的处理逻辑
-                        console.log('页面变为不可见');
-                        this.tracker != null && document.getElementById("video") ? this.closeFace() : undefined
-                    }
-                },
-                closeFace() {
-                    try {
-                        this.tracker = null
-                        // 关闭摄像头
-                        let video = document.getElementById('video')
-                        video.srcObject.getTracks()[0].stop()
-                        // 停止侦测
-                        this.trackerTask.stop()
-                    } catch (error) { }
-                }
-            },
-            created() {
-                var that = this
-                // APP-PLUS || H5(接收父页面传过来的值)
-                window.receiveData = (msg) => {
-                    that.analysisData(msg)
-                }
-                window.addEventListener("message", function (event) {
-                    that.analysisData(event.data)
-                });
-            },
-            mounted() {
-                this.initHandle();
-
-                document.addEventListener('visibilitychange', this.handleVisibilityChange);
-            },
-            beforeDestroy() {
-                // 移除window方法
-                window.receiveData = null;
-            },
-            watch: {},
-        }).mount('#face-container');
+        // 为 video 动态生成透明背景的 PNG 占位图
+        (function createPoster() {
+            var video = document.getElementById("video");
+            if (!video) return;
+            var w = 320, h = 240;
+            var canvas = document.createElement("canvas");
+            canvas.width = w;
+            canvas.height = h;
+            var ctx = canvas.getContext("2d");
+            ctx.clearRect(0, 0, w, h); // 透明背景
+            ctx.fillStyle = "#777";
+            ctx.font = "24px Arial";
+            ctx.textAlign = "center";
+            ctx.textBaseline = "middle";
+            ctx.fillText("模块加载中...", w / 2, h / 2);
+            video.poster = canvas.toDataURL("image/png");
+        })();
+
+        // 使用封装好的入口初始化当前页面
+        initMeetingPage({
+            containerId: 'mbContainer'
+        });
     </script>
 </body>
 

+ 0 - 208
src/static/face/meeting1.html

@@ -1,208 +0,0 @@
-<!doctype html>
-<html lang="en">
-
-<head>
-    <!-- Required meta tags -->
-    <meta charset="utf-8">
-    <title>人脸识别</title>
-    <!-- <script type="text/javascript" src="./js/opencv4.5.0.js"></script> -->
-    <script type="text/javascript" src="./js/jquery-2.2.1.min.js"></script>
-    <!-- VUE3 的 SDK -->
-    <script type="text/javascript" src="./js/vue.global.prod.js"></script>
-    <!-- uni 的 SDK -->
-    <script type="text/javascript" src="./js/uni.webview.1.5.4.js"></script>
-    <script type="text/javascript" src="./js/opencv4.5.0.js"></script>
-
-    <style>
-        #container {
-            min-height: 300px;
-        }
-
-        #canvasOutput,
-        #imageSrc {
-            background: #ccc;
-            min-width: 300px;
-            min-height: 300px;
-            display: block;
-            float: left;
-            margin-left: 20px;
-        }
-    </style>
-</head>
-
-<body>
-
-    <div id="face-container" class="face-container home-card-two">
-        <video id="video" width="320" height="240" @click="detectFaces()" preload autoplay loop muted></video>
-        <img id="imageSrc" alt="No Image" />
-        <canvas id="canvas" width="320" height="240"></canvas>
-    </div>
-
-
-    <script>
-        // 创建Vue实例
-        Vue.createApp({
-            components: {},
-            emits: [],
-            props: {},
-            data() {
-                return {
-                };
-            },
-            computed: {},
-            methods: {
-                // 初始化数据
-                initData() { },
-                // 初始化摄像头
-                initVido() {
-
-                },
-                detectFaces() {
-                    // let frame = new cv.Mat(video.videoHeight, video.videoWidth, cv.CV_8UC4);
-                    // cv.cvtColor(frame, frame, cv.COLOR_RGBA2GRAY, 0);
-                    // // 假设faceCascade是预先加载的Haar或DNN人脸检测器
-                    // let faces = new cv.RectVector();
-                    // console.log(faces)
-                    // faceCascade.detectMultiScale(frame, faces);
-                    // // 处理faces...
-
-                    let imgElement = document.getElementById('imageSrc');
-                    let mat = cv.imread(imgElement);
-                    cv.imshow('canvas', mat);
-                    mat.delete();
-                },
-
-
-            },
-            created() {
-
-            },
-            mounted() {
-
-
-
-                // let video = document.getElementById('video');
-                // if (navigator.mediaDevices.getUserMedia) {
-                //     navigator.mediaDevices.getUserMedia({ video: true })
-                //         .then(function (stream) {
-                //             video.srcObject = stream;
-                //         })
-                //         .catch(function (err0r) {
-                //             console.log("Something went wrong!");
-                //         });
-
-
-                // }
-                // 当DOM完全加载后,获取视频流并进行活体检测
-                document.addEventListener('DOMContentLoaded', () => {
-                    let video = document.getElementById('video'); // 获取视频标签
-                    let canvas = document.getElementById('canvas'); // 获取画布标签
-                    let context = canvas.getContext('2d'); // 获取画布上下文
-
-                    // 设置视频源
-                    navigator.mediaDevices.getUserMedia({ video: true })
-                        .then((stream) => {
-                            video.srcObject = stream;
-                        })
-                        .catch((error) => {
-                            console.error('获取视频流出错:', error);
-                        });
-
-                    // 视频帧更新时执行检测
-                    video.addEventListener('play', () => {
-                        const width = video.videoWidth / 2;
-                        const height = video.videoHeight / 2;
-                        const size = { width: width, height: height };
-
-                        setInterval(() => {
-                            context.drawImage(video, 0, 0, size.width, size.height); // 绘制画布
-                            let imageData = context.getImageData(0, 0, size.width, size.height); // 获取画布数据
-
-                            // 加载CascadeClassifier(Haar级联分类器)
-
-
-                            var myCanvas = document.getElementById("canvas");
-                            var image = myCanvas.toDataURL("image/png").replace("image/png", "image/octet-stream");
-
-
-
-
-                            // 将base64字符串转换为Mat对象
-                            // const img = cv.imread(myCanvas);
-
-
-                            // // 检测图像中的人脸
-                            // const minFeatures = 1;
-                            // const objects = new cv.RectVector();
-
-                            // // 绘制检测到的人脸
-                            // const rects = objects.toArray();
-                            // for (let i = 0; i < rects.length; ++i) {
-                            //     const rect = rects[i];
-                            //     cv.rectangle(img, rect.x, rect.y, rect.x + rect.width, rect.y + rect.height, [255, 0, 0, 255]);
-                            // }
-
-                            // // 展示结果
-                            // cv.imshow('canvas', img);
-
-                            // // 释放资源
-                            // objects.delete();
-                            // img.delete();
-
-
-                            // const hsv = new cv.Mat();
-                            // // cv.cvtColor(image, hsv, cv.COLOR_BGR2HSV)
-
-                            // const lower_white = [0, 0, 0, 0];
-                            // const upper_white = [0, 0, 0, 255];
-
-                            // const low = new cv.Mat(hsv.rows, hsv.cols, hsv.type(), lower_white);
-                            // const high = new cv.Mat(hsv.rows, hsv.cols, hsv.type(), upper_white);
-
-                            // const dst = new cv.Mat();
-                            // cv.inRange(hsv, low, high, dst);
-
-
-                            // let grayMat = new cv.Mat(size.height, size.width, cv.CV_8U, lower_white);
-                            // let grayImage = new cv.Mat(size.height, size.width, cv.CV_8UC3, upper_white);
-                            // let faces = new cv.RectVector();
-
-                            // console.log(grayMat, grayImage, faces)
-                            // 将图像转换为灰度
-                            // cv.cvtColor(imageData.data, grayMat, cv.COLOR_RGBA2GRAY, 0);
-                            // // 使用分类器检测脸部
-                            // classifier.detectMultiScale(grayMat, faces, 1.1, 3, 0);
-
-                            // for (let i = 0; i < faces.size(); ++i) {
-                            //     let face = faces.get(i);
-                            //     // 在画布上绘制脸部标记
-                            //     cv.rectangle(imageData.data, face, { x: 0, y: 255, width: 1 }, 2);
-                            // }
-
-                            // // 清理资源
-                            // grayMat.delete();
-                            // grayImage.delete();
-                            // faces.delete();
-                            // classifier.delete();
-
-                            // // 活体检测的逻辑放这里
-                            // // ...
-
-                            // // 显示处理后的图像
-                            // let imgData = new ImageData(new Uint8ClampedArray(imageData.data), size.width, size.height);
-                            // context.putImageData(imgData, 0, 0);
-
-                        }, 2000);
-                    });
-                });
-
-            },
-            beforeDestroy() {
-
-            },
-            watch: {},
-        }).mount('#face-container');
-    </script>
-</body>
-
-</html>

+ 122 - 0
src/static/face/meeting_template_preview.html

@@ -0,0 +1,122 @@
+<!doctype html>
+<html>
+
+<head>
+    <meta charset="utf-8" />
+    <title>会议模板预览</title>
+    <script type="text/javascript" src="./js/tracking-min.js"></script>
+    <script type="text/javascript" src="./js/face_data/face-min.js"></script>
+    <script type="text/javascript" src="./js/jquery-2.2.1.min.js"></script>
+    <!-- VUE3 的 SDK -->
+    <script type="text/javascript" src="./js/vue.global.prod.js"></script>
+    <!-- uni 的 SDK -->
+    <script type="text/javascript" src="./js/uni.webview.1.5.4.js"></script>
+    <!-- 会议页面封装逻辑 -->
+    <script type="text/javascript" src="./js/meeting-page.js"></script>
+    <!-- dayjs -->
+    <script type="text/javascript" src="./js/dayjs.min.js"></script>
+
+    <script>
+        // 提前占位,防止宿主在页面初始化前调用 receiveData 导致未定义
+        window.__pendingReceiveData = [];
+        window.receiveData = function (msg) {
+            window.__pendingReceiveData.push(msg);
+        };
+    </script>
+    <style>
+        * {
+            margin: 0;
+            padding: 0;
+            box-sizing: border-box;
+            -webkit-user-select: none;
+            -moz-user-select: none;
+            -ms-user-select: none;
+            user-select: none;
+        }
+
+        body {
+            width: 100vw;
+            height: 100vh;
+            background: #f5f5f5;
+            font-family: "Microsoft YaHei", "PingFang SC", sans-serif;
+            /* display: flex;
+            align-items: center;
+            justify-content: center; */
+        }
+
+        .openConfig {
+            position: absolute;
+            width: 100px;
+            height: 100px;
+            z-index: 10000;
+        }
+
+        .mbContainer {
+            position: relative;
+            width: 100%;
+            height: 100%;
+            background: #ffffff;
+            overflow: hidden;
+            box-shadow: 0 20px 60px rgba(0, 0, 0, 0.12);
+        }
+
+        .text-item {
+            position: absolute;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            text-align: center;
+            word-break: break-all;
+        }
+
+        .image-item {
+            position: absolute;
+            overflow: hidden;
+            border-radius: 12px;
+            /* border: 2px dotted #c7c7c7; */
+            /* background: #f7f8fa; */
+            display: flex;
+            align-items: center;
+            justify-content: center;
+        }
+
+        .image-item img {
+            width: 100%;
+            height: 100%;
+            object-fit: fill;
+        }
+
+        .carousel {
+            position: absolute;
+            /* border: 2px dotted #c7c7c7; */
+            /* border-radius: 12px; */
+            overflow: hidden;
+            background: #f6f6f6;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            color: #8f959f;
+        }
+
+        .carousel img {
+            width: 100%;
+            height: 100%;
+            object-fit: cover;
+        }
+    </style>
+</head>
+
+<body>
+    <div id="openConfig" class="openConfig"></div>
+    <div id="mbContainer" class="mbContainer"></div>
+
+    <script>
+        // 使用封装好的入口初始化当前页面
+        initMeetingPage({
+            containerId: 'mbContainer'
+        });
+
+    </script>
+</body>
+
+</html>

+ 0 - 28
src/store/modules/common.js

@@ -46,34 +46,6 @@ const commonStore = defineStore("common", {
   }),
   unistorage: true,
   actions: {
-    /**
-     * @公共添加服务器列表
-     */
-    setServeList(linkUrl, content) {
-      let serveList = uni.getStorageSync("serveList");
-      if (serveList.length <= 0) {
-        uni.setStorageSync("serveList", [
-          {
-            radiolist: [
-              {
-                id: 1,
-                linkUrl: linkUrl,
-                content: content,
-              },
-            ],
-            radiovalue: 1,
-          },
-        ]);
-      } else {
-        serveList[0].radiolist.push({
-          id: serveList[0].radiolist[serveList[0].radiolist.length - 1].id + 1,
-          linkUrl: linkUrl,
-          content: content,
-        });
-
-        uni.setStorageSync("serveList", serveList);
-      }
-    },
   },
 });
 

+ 378 - 209
src/store/modules/control.js

@@ -1,7 +1,7 @@
 import { defineStore } from "pinia";
 import { doorApi } from "@/api/business/door.js";
 import { faceApi } from "@/api/business/face.js";
-import { meetingApi, signOnOut } from "@/api/business/meeting.js";
+import { deviceApi, meetingApi, signOnOut, escalation } from "@/api/business/meeting.js";
 import { getToken, setToken, removeToken } from "@/utils/auth";
 import dayjs from 'dayjs'
 import config from "@/config";
@@ -10,11 +10,14 @@ import nfc from "@/plugins/nfc.plugins.js";
 import modal from "@/plugins/modal.plugins.js";
 import keyListen from "@/plugins/keyListen.plugins.js";
 import permission from "@/plugins/permission.plugins.js";
+import common from "@/plugins/common.plugins.js";
 import time from "@/plugins/time.plugins.js";
 import sysPlugins from "@/plugins/device/sys.plugins";
+import yxPlugins from "@/plugins/device/yx.plugins";
 
 const controlStore = defineStore("control", {
     state: () => ({
+        ipAddress: "",//设备IP地址
         pageFunction: [], //被包含的功能
         isClicked: false, //按钮是否被点击
         isDataChange: false,//数据是否改变
@@ -33,32 +36,25 @@ const controlStore = defineStore("control", {
         modal: {
             show: false,
         },
-        picker: {
-            show: false,
-            title: "",
-            list: [[]],
-            defaultIndex: 0,
-        },
-        subsection: {
-            list: ["服务器配置", "其它配置", "系统设置"],
-            value: 0,
-        },
-        doooList: [],
-        meetingDoorList: [],
-        meetingRoomList: [],
+        doorList: [],
+        meetingDoorList: [],//会议门禁列表数据
+        meetingRoomList: [],//会议室列表数据
         meetingTimeList: [],
+        meetingTemplateSrc: "", // 会议模板src
+        meetingTemplateList: {},//会议模板数据
         meetingReservaList: {
             dataAll: {},
-            thisVenueData: [],
+            thisVenueData: {},
             thisVenueTime: {},
-            nextSceneData: [],
+            nextSceneData: {},
             nextSceneTime: {},
             timeList: [],
         },
-
         inter: {
             doorDom: null,
             meeting: null,
+            meetingEscalation: null,
+            rebootNow: null
         },
     }),
     actions: {
@@ -68,13 +64,19 @@ const controlStore = defineStore("control", {
         initData() {
             var that = this
             var storage = uni.getStorageSync("storage_face");
-            if (storage) {
+            if (!storage) return;
+
+            // 智能会议
+            if (config.appInfo.appid === "__UNI__F3963F8") {
+                that.form.meetingId = storage.meetingId || undefined;
+                that.form.meetingName = storage.meetingName || undefined;
+            }
+            // 智能门禁
+            else if (config.appInfo.appid === "__UNI__8D6E9FD") {
                 config.baseUrl = "http://" + storage.domain + "/prod-api";
                 that.form.domain = storage.domain;
                 that.form.linkUrl = storage.linkUrl.indexOf(":") != -1 ? storage.linkUrl.split(":")[0] : storage.linkUrl;
                 that.form.port = storage.port ? storage.port : "";
-                that.form.meetingId = storage.meetingId || undefined;
-                that.form.meetingName = storage.meetingName || undefined;
                 that.form.door = {
                     id: storage.door.id || undefined,
                     name: storage.door.name || undefined,
@@ -87,9 +89,11 @@ const controlStore = defineStore("control", {
         initCamera() {
             var that = this
             //#ifdef APP-PLUS
-            permission.getPermisson("camera").then((res) => {
-                res ? that.handleChildren({ funcName: "开启摄像头", data: {} }) : "";
-            });
+            setTimeout(() => {
+                permission.getPermisson("camera").then((res) => {
+                    res ? that.handleChildren({ funcName: "开启摄像头", data: {} }) : "";
+                });
+            }, 1000);
             //#endif
         },
         /**
@@ -132,17 +136,38 @@ const controlStore = defineStore("control", {
          * @开启定时任务
          */
         openInterval(type) {
-            if (type == "door") {
+            var _this = this
+            //IP地址
+            if (type == "ipAddress") {
+                setInterval(() => {
+                    sysPlugins.getIpAddress({
+                        success: (res) => {
+                            _this.ipAddress = res;
+                        },
+                        error: (res) => {
+                            _this.ipAddress = "";
+                        },
+                    });
+                }, 1000);
+            }
+            //门禁
+            else if (type == "door") {
                 if (this.inter.doorDom) return;
                 this.getDoorList("updateData");
                 this.inter.doorDom = setInterval(() => {
                     this.getDoorList("updateData");
                 }, 1000 * 3);
-            } else if (type == "meeting") {
-                if (this.inter.meeting) return;
-                getMeetingRoomReservationList();
+            }
+            // 会议
+            else if (type == "meeting") {
+                this.initEscalation();//调用初始化会议心跳方法
+                this.inter.meetingEscalation = setInterval(() => {
+                    this.initEscalation();//调用初始化会议心跳方法
+                }, 1000 * 10)
+
+                this.getMeetingDeviceList()
                 this.inter.meeting = setInterval(() => {
-                    getMeetingRoomReservationList();
+                    this.getMeetingDeviceList()
                 }, 1000 * 3);
             }
         },
@@ -159,148 +184,295 @@ const controlStore = defineStore("control", {
             }
         },
         /**
-         * @弹窗确定按钮事件
+         * @弹窗事件
+         * @Confirm 确定
+         * @Cancel 退出应用
+         * @Close 关闭
          */
-        modalConfirm(rules) {
-            if (!this.form.linkUrl) {
-                modal.msg("请输入链接地址");
-                return;
-            }
-
-            if (!/^[a-zA-Z0-9][a-zA-Z0-9-]{1,61}(?:\.[a-zA-Z0-9]{2,})+$/.test(this.form.linkUrl)) {
-                modal.msg("请输入正确的链接地址");
-                return;
-            }
+        handleModal(type) {
+            if (type == "Confirm") {
+                uni.setStorageSync("storage_face", this.form);
+            } else if (type == 'Cancel') {
+                //#ifdef APP-PLUS
+                keyListen.quitApp();
+                //#endif
+            } else if (type == "Close") {
 
-            if (!this.form.meetingName && this.pageFunction.includes('会议')) {
-                modal.msg("请选择绑定会议室");
-                return;
             }
 
-            if (!this.form.door.name && this.pageFunction.includes('门禁')) {
-                modal.msg("请选择绑定门禁");
-                return;
-            }
+            this.handleChildren({ funcName: "开启摄像头", data: {} });
+            this.modal.show = false;
 
-            uni.setStorageSync("storage_face", this.form);
-            this.modalClose();
         },
         /**
-         * @应用退出按钮事件
+         * @Select下拉框回调事件
          */
-        modalCancel() {
-            this.modal.show = false;
-            //#ifdef APP-PLUS
-            keyListen.quitApp();
-            //#endif
+        handleSelectChange(e) {
+            if (e.type === "绑定门禁") {
+                const match = this.doorList.find(item => item.value === e.value);
+                if (match) {
+                    this.form.door.id = match.value;
+                    this.form.door.name = match.name;
+                }
+            }
+            this.isDataChange = true;
         },
         /**
-         * @弹窗关闭事件
+         * @初始化设备心跳数据
          */
-        modalClose() {
-            this.handleChildren({ funcName: "开启摄像头", data: {} });
-            this.modal.show = false;
+        initEscalation() {
+            if (!sysPlugins.getDeviceInfo().serial) return;
+            sysPlugins.getIpAddress({
+                success: (res) => {
+                    var param = {
+                        "deviceCode": sysPlugins.getDeviceInfo().serial,
+                        "ipAddr": res,
+                        "deviceType": 1,//设备类型(1.会议屏 2.信息发布屏 3.综合屏)
+                        "model": sysPlugins.getDeviceInfo().model,
+                        "manuFacturer": sysPlugins.getDeviceInfo().manufacturer,
+                        "version": sysPlugins.getDeviceInfo().version,
+                        "sdk": sysPlugins.getDeviceInfo().Sdk
+                    }
+
+                    escalation(param)
+                }
+            });
         },
         /**
-         * @section回调事件
+         * @获取会议设备列表
          */
-        sectionChange(e) {
-            if (e == 1) {
-                if (!this.form.linkUrl) {
-                    modal.msg("请输入链接地址");
-                    return;
-                }
+        getMeetingDeviceList() {
+            this.meetingTimeList = [];
+            this.meetingReservaList.thisVenueData = [];
+            this.meetingReservaList.thisVenueTime = {};
+            this.meetingReservaList.nextSceneData = [];
+            this.meetingReservaList.nextSceneTime = {};
 
-                if (!/^[a-zA-Z0-9][a-zA-Z0-9-]{1,61}(?:\.[a-zA-Z0-9]{2,})+$/.test(this.form.linkUrl)) {
-                    modal.msg("请输入正确的链接地址");
-                    return;
-                }
+            // 生成 00:00-23:30 的半小时刻度
+            this.meetingTimeList = Array.from({ length: 48 }, (_, idx) => {
+                const hour = Math.floor(idx / 2);
+                const minute = idx % 2 ? "30" : "00";
+                const time = `${String(hour).padStart(2, "0")}:${minute}`;
 
-                var domain = "";
-                if (this.form.linkUrl) {
-                    domain = this.form.linkUrl;
-                    if (this.form.port) {
-                        domain += ":" + this.form.port;
-                    }
-                }
+                return {
+                    startTime: time,
+                    endTime: time,
+                    isEnd: 0,
+                    isHave: 0,
+                    isReservation: 0,
+                };
+            });
 
-                this.form.domain = domain;
-                config.baseUrl = "http://" + this.form.domain + "/prod-api";
-                if (this.pageFunction.includes('会议')) {
-                    this.getMeetingRoomList();
-                }
-                if (this.pageFunction.includes('门禁')) {
-                    this.getDoorList();
-                }
-            }
+            deviceApi()
+                .Select({
+                    deviceCode: sysPlugins.getDeviceInfo().serial || "NTECVSG3PL",
+                    current: 1,
+                    size: 10
+                })
+                .then((requset) => {
+                    if (requset.data.records.length > 0) {
+                        this.form.meetingId = requset.data.records[0].meetingRoom.roomId
+                        this.form.meetingName = requset.data.records[0].meetingRoom.roomName
+                        this.meetingTemplateList = requset.data.records[0].meetingTemplate
+
+                        if (this.meetingTemplateList && this.meetingTemplateList?.type == 1) {
+                            this.meetingTemplateSrc = `/static/face/${this.meetingTemplateList.s1Con}`
+                        } else {
+                            this.meetingTemplateSrc = "/static/face/meeting_template_preview.html"
+                            this.handleChildren({
+                                funcName: "初始化模板",
+                                data: JSON.stringify(this.meetingTemplateList || {}),
+                            });
+                        }
 
-            this.subsection.value = e;
+                        this.getMeetingRoomReservationList();
+                    } else {
+                        this.form.meetingId = undefined
+                        this.form.meetingName = undefined
+                        this.meetingReservaList.timeList = this.meetingTimeList;
+                        this.handleChildren({
+                            funcName: "初始化数据",
+                            data: JSON.stringify(this.meetingReservaList),
+                        });
+                    }
+                })
+                .catch((err) => {
+
+                });
         },
+        /**
+         * @会议室详情列表
+         */
+        getMeetingRoomReservationList() {
+            var _this = this
+
+            meetingApi()
+                .GetMeetingRoomReservationList({
+                    deviceCode: sysPlugins.getDeviceInfo().serial || "NTECVSG3PL",
+                    meetingRoomId: this.form.meetingId,
+                    date: dayjs().format("YYYY-MM-DD") + " 00:00:00",
+                })
+                .then((requset) => {
+                    if (requset.data.length > 0) {
+                        _this.meetingReservaList.dataAll = requset.data[0];
+                        _this.meetingReservaList.dataAll.dmMeetingList.forEach((e, index) => {
+                            //判断开始时间和结束时间是否包含当前时间
+                            if (dayjs().isBetween(e.startDate, e.endDate, null, "[]")) {
+                                _this.meetingReservaList.thisVenueData = e;
+                                _this.meetingReservaList.thisVenueTime = time.timeRestructuring(_this.meetingReservaList.dataAll.meetingRoomUsage[index]);
+                            }
+                            //判断当前时间是否相同或在其之前
+                            if (dayjs().isSameOrBefore(e.startDate) && _this.meetingReservaList.nextSceneData.length < 1) {
+                                _this.meetingReservaList.nextSceneData = e;
+                                _this.meetingReservaList.nextSceneTime = time.timeRestructuring(_this.meetingReservaList.dataAll.meetingRoomUsage[index]);
+                            }
+                        });
 
+                        _this.meetingReservaList.timeList = _this.showTimeSegments(_this.meetingTimeList);
+                        _this.handleChildren({
+                            funcName: "初始化数据",
+                            data: JSON.stringify(_this.meetingReservaList),
+                        });
+                    } else {
+                        _this.meetingReservaList.timeList = _this.meetingTimeList;
+                        _this.handleChildren({
+                            funcName: "初始化数据",
+                            data: JSON.stringify(_this.meetingReservaList),
+                        });
+                    }
+                })
+                .catch((err) => { });
+        },
         /**
-         * @action弹出框点击事件
+         * @显示时间段的函数
          */
-        handlePicker(value, index, ind) {
-            if (value == "绑定会议室") {
-                this.picker.title = "绑定会议室";
-                this.picker.list = [this.meetingRoomList];
-                this.picker.defaultIndex = 0;
-            } else if (value == "绑定门禁") {
-                this.picker.title = "绑定门禁";
-                this.picker.list = [this.doooList];
-                this.picker.defaultIndex = 0;
+        showTimeSegments(times) {
+            const timesXleList = JSON.parse(JSON.stringify(times));
+
+            for (var i = 0; i < timesXleList.length; i++) {
+                const timeValue = new Date(`${dayjs().format("YYYY-MM-DD")}T${timesXleList[i].startTime}`);
+
+                this.meetingReservaList.dataAll.meetingRoomUsage.forEach((item) => {
+                    const timeList = time.timeRestructuring(item);
+                    const startValue = new Date(`${dayjs().format("YYYY-MM-DD")}T${timeList.startTime}`);
+                    const endValue = new Date(`${dayjs().format("YYYY-MM-DD")}T${timeList.endTime}`);
+
+                    if (timeValue.getTime() >= startValue.getTime() && timeValue.getTime() < endValue.getTime()) {
+                        timesXleList.splice(i--, 1);
+                    }
+                });
             }
-            this.picker.show = true;
-        },
 
+            this.meetingReservaList.dataAll.meetingRoomUsage.forEach((item) => {
+                const timeList = time.timeRestructuring(item);
+                const startValue = new Date(`${dayjs().format("YYYY-MM-DD")}T${timeList.startTime}`);
+                const endValue = new Date(`${dayjs().format("YYYY-MM-DD")}T${timeList.endTime}`);
+
+                if (dayjs().isBetween(startValue, endValue, null, "[]")) {
+                    timesXleList.push({
+                        ...timeList,
+                        isEnd: 0,
+                        isHave: 1,
+                        isReservation: 0,
+                    });
+                } else {
+                    if (dayjs().isSameOrAfter(startValue)) {
+                        timesXleList.push({
+                            ...timeList,
+                            isEnd: 1,
+                            isHave: 0,
+                            isReservation: 0,
+                        });
+                    }
+                    if (dayjs().isSameOrBefore(startValue)) {
+                        timesXleList.push({
+                            ...timeList,
+                            isEnd: 0,
+                            isHave: 0,
+                            isReservation: 1,
+                        });
+                    }
+                }
+            });
+
+            var newTimesXleList = common
+                .uniq(timesXleList, "startTime")
+                .sort((a, b) => new Date(`${dayjs().format("YYYY-MM-DD")}T${a.startTime}`) - new Date(`${dayjs().format("YYYY-MM-DD")}T${b.startTime}`));
+            return newTimesXleList;
+        },
         /**
-         * @action弹出框选择事件
+         * @人脸验证
          */
-        pickerConfirm(e) {
-            if (this.picker.title == "绑定会议室") {
-                this.form.meetingId = e.value[0].value;
-                this.form.meetingName = e.value[0].name;
-            } else if (this.picker.title == "绑定门禁") {
-                this.form.door.id = e.value[0].value;
-                this.form.door.name = e.value[0].name;
-            }
-            this.picker.show = false;
-            this.isDataChange = true
+        faceVerify(imageBase) {
+            var that = this
+            faceApi()
+                .faceVef({
+                    deviceCode: sysPlugins.getDeviceInfo().serial,
+                    imageBase: imageBase,
+                })
+                .then((item) => {
+                    if (item.data.code === 200 || item.data.code === 201) {
+                        item.data.userName = item.data.faceName;
+                        if (that.pageFunction.includes('会议')) {
+                            that.meetingVerify(item.data);
+                        } else {
+                            that.openDoor(item.data);
+                        }
+                    }
+
+                    if (item.data.msg != "人脸验证接口返回异常") {
+                        modal.msg(item.data.msg);
+                    }
+
+                    that.handleChildren({ funcName: "人脸冷却", data: {} });
+                })
+                .catch((err) => {
+                    that.handleChildren({ funcName: "人脸冷却", data: {} });
+                });
         },
         /**
-         * @会议室下拉列表
+         * @会议验证
          */
-        getMeetingRoomList() {
+        meetingVerify(event) {
             var that = this
-            that.meetingRoomList = [];
+            if (that.meetingReservaList.thisVenueData.length <= 0) return;
+
             meetingApi()
-                .GetMeetingRoomList({
-                    domain: that.form.domain,
+                .Attendee({
+                    deviceCode: sysPlugins.getDeviceInfo().serial,
+                    meetingId: this.meetingReservaList.thisVenueData.meetingId,
+                    userId: event.userId,
+                    userName: event.userName,
                 })
-                .then((requset) => {
-                    if (requset.data.length > 0) {
-                        requset.data.forEach((e) => {
-                            that.meetingRoomList.push({
-                                value: e.roomId,
-                                name: e.roomName,
-                            });
-                        });
+                .then((item) => {
+                    if (item.data.status == "1") {
+                        that.openDoor(event);
+                        that.meetingSign(event);
                     }
+                    modal.msg(item.data.msg);
                 });
         },
+        /**
+         * @会议签到
+         */
+        meetingSign(event) {
+            signOnOut({
+                deviceCode: sysPlugins.getDeviceInfo().serial,
+                meetingId: this.meetingReservaList.thisVenueData.meetingId,
+                userId: event.userId, //参会人Id
+                mothodType: 0, //签到签退类别(0.签到 1.签退)
+                signType: 1, //签到签退方式(0.人工 1.人脸)
+            }).then((item) => { });
+        },
         /**
          * @门禁下拉列表
          */
         getDoorList(type) {
-            this.doooList = [];
+            this.doorList = [];
 
-            if (!this.form.domain) {
-                return;
-            }
+            if (!this.form.domain) return;
+            if (type == 'updateData' && !this.form.door.id) return;
 
-            if (type == 'updateData' && !this.form.door.id) {
-                return;
-            }
 
             doorApi()
                 .Select({
@@ -310,9 +482,7 @@ const controlStore = defineStore("control", {
                     deviceUuid: type == 'updateData' ? this.form.door.id : undefined
                 })
                 .then((requset) => {
-                    if (requset.data.records.length <= 0) {
-                        return;
-                    }
+                    if (requset.data.records.length <= 0) return;
 
                     if (type == "updateData") {
                         let data = requset.data.records[0]
@@ -330,9 +500,10 @@ const controlStore = defineStore("control", {
                         uni.setStorageSync("storage_face", this.form);
                     } else {
                         requset.data.records.forEach((e) => {
-                            this.doooList.push({
+                            this.doorList.push({
                                 value: e.deviceUuid,
                                 name: e.deviceName,
+                                text: e.deviceName,
                             });
                         });
                     }
@@ -344,7 +515,7 @@ const controlStore = defineStore("control", {
         openDoor(event) {
             var that = this
 
-            if (!this.form.door.name) {
+            if (!this.form.door.id) {
                 modal.msg("请先绑定门禁!");
                 return;
             }
@@ -400,74 +571,6 @@ const controlStore = defineStore("control", {
                     console.log(err);
                 });
         },
-        /**
-         * @人脸验证
-         */
-        faceVerify(imageBase) {
-            if (!this.form.domain) {
-                return;
-            }
-
-            var that = this
-            faceApi()
-                .faceVef({
-                    domain: that.form.domain,
-                    imageBase: imageBase,
-                })
-                .then((item) => {
-                    if (item.data.code === 200 || item.data.code === 201) {
-                        item.data.userName = item.data.faceName;
-                        if (that.pageFunction.includes('会议')) {
-                            that.meetingVerify(item.data);
-                        } else {
-                            that.openDoor(item.data);
-                        }
-                    }
-
-                    if (item.data.msg != "人脸验证接口返回异常") {
-                        modal.msg(item.data.msg);
-                    }
-
-                    that.handleChildren({ funcName: "人脸冷却", data: {} });
-                })
-                .catch((err) => {
-                    that.handleChildren({ funcName: "人脸冷却", data: {} });
-                });
-        },
-        /**
-         * @会议验证
-         */
-        meetingVerify(event) {
-            var that = this
-            if (that.meetingReservaList.thisVenueData.length > 0) {
-                meetingApi()
-                    .Attendee({
-                        domain: this.form.domain,
-                        meetingId: this.meetingReservaList.thisVenueData[0].meetingId,
-                        userId: event.userId,
-                        userName: event.userName,
-                    })
-                    .then((item) => {
-                        if (item.data.status == "1") {
-                            that.openDoor(event);
-                            that.meetingSign(event);
-                        }
-                        modal.msg(item.data.msg);
-                    });
-            }
-        },
-        /**
-         * @会议签到
-         */
-        meetingSign(event) {
-            signOnOut({
-                domain: this.form.domain,
-                meetingId: this.meetingReservaList.thisVenueData[0].meetingId,
-                userId: event.userId, //参会人Id
-                mothodType: 0, //签到签退类别(0.签到 1.签退)
-                signType: 1, //签到签退方式(0.人工 1.人脸)
-            }).then((item) => { });
-        },
         /**
          * @解析父页面传回的数据
          */
@@ -478,7 +581,8 @@ const controlStore = defineStore("control", {
                     tab.navigateTo("/pages/door/setting/index")
                 } else if (event.funcName == "打开会议配置") {
                     this.handleChildren({ funcName: "关闭摄像头", data: {} });
-                    this.modal.show = true;
+                    // 跳转到密码验证页面
+                    tab.navigateTo("/pages/meeting/setting/password");
                 } else if (event.funcName == "人脸识别") {
                     this.faceVerify(event.data.imageBase);
                 } else if (event.funcName == "点击开门") {
@@ -497,15 +601,44 @@ const controlStore = defineStore("control", {
          */
         handleChildren(data) {
             // #ifdef APP-PLUS
-            var pages = getCurrentPages();
-            var currentWebview = pages[pages.length - 1].$getAppWebview();
-            var wv = currentWebview.children()[0];
-            wv.evalJS(`receiveData(${JSON.stringify(data)})`);
+            try {
+                var pages = getCurrentPages();
+                if (!pages || pages.length === 0) return;
+
+                var currentWebview = pages[pages.length - 1].$getAppWebview();
+                if (!currentWebview) return;
+
+                var children = currentWebview.children();
+                if (!children || children.length === 0) return;
+
+                var wv = children[0];
+                if (!wv || !wv.evalJS) return;
+
+                // 确保 receiveData 函数存在,如果不存在则先定义它
+                var jsCode = `
+                    if (typeof receiveData === 'function') {
+                        receiveData(${JSON.stringify(data)});
+                    } else if (window.receiveData && typeof window.receiveData === 'function') {
+                        window.receiveData(${JSON.stringify(data)});
+                    } else {
+                        console.warn('receiveData is not defined');
+                    }
+                `;
+                wv.evalJS(jsCode);
+            } catch (error) {
+                console.warn('handleChildren error:', error);
+            }
             // #endif
 
             // #ifdef H5
-            var iframe = document.getElementById("faceView");
-            iframe.contentWindow.postMessage(data, "*");
+            try {
+                var iframe = document.getElementById("faceView");
+                if (iframe && iframe.contentWindow) {
+                    iframe.contentWindow.postMessage(data, "*");
+                }
+            } catch (error) {
+                console.warn('handleChildren H5 error:', error);
+            }
             // #endif
         },
         /**
@@ -519,6 +652,42 @@ const controlStore = defineStore("control", {
                 return originalStr.replace(regex, '');
             }
             return originalStr;
+        },
+        /**
+         * @定时重启
+         */
+        timingRebootNow() {
+            var _this = this;
+
+            if (!_this.inter.rebootNow) {
+                checkMorningSix();
+                _this.inter.rebootNow = setInterval(() => {
+                    checkMorningSix();
+                }, 1000);
+            }
+
+            // 判断是否是早上6点钟
+            function checkMorningSix() {
+                const now = new Date();
+                // 设置时间为早上6点,忽略分钟和秒
+                const targetTime = new Date();
+                targetTime.setHours(6, 0, 0, 0);
+
+                // 比较当前时间是否等于早上6点
+                if (now.getTime() === targetTime.getTime()) {
+                    yxPlugins.rebootNow();
+                }
+            }
+        },
+        /**
+         * @门禁开关
+         */
+        doorControl(value) {
+            if (value) {
+                sysPlugins.openDoor(2, 0);
+            } else {
+                sysPlugins.openDoor(3, 0);
+            }
         }
     },
 });

+ 1 - 4
src/utils/hideHead.js

@@ -1,8 +1,5 @@
 // 页面白名单
-const whiteList = [
-	"pages/index",//登录
-	"pages/info",//消息
-];
+const whiteList = [];
 
 export default {
 	mounted() {

+ 4 - 4
unpackage/config/setting.js

@@ -18,8 +18,8 @@ filesToModify.forEach((file) => {
                 state.name = '智能会议'
                 state.appid = '__UNI__F3963F8'
                 state.description = '智能会议APP,是一款集成了现代信息技术和智能化管理功能的移动应用程序,旨在提升会议体验和管理效率。'
-                state.versionName = "1.0.3"
-                state.versionCode = 3
+                state.versionName = "1.0.5"
+                state.versionCode = 5
                 state.h5.title = '智能会议'
 
                 maps.amap.name = "amapBOujshtbA"
@@ -122,12 +122,12 @@ filesToModify.forEach((file) => {
         } else if ('./src/pages.json') {
             state.pages.forEach((e, index) => {
                 if (args === 'huiYi') {
-                    if (e.style.navigationBarTitleText === "人脸识别") {
+                    if (e.style.navigationBarTitleText === "智能会议") {
                         if (index != 0) {
                             state.pages.splice(0, 1, ...state.pages.splice(index, 1, state.pages[0]))
                         }
                     }
-                    state.condition.list[0].path = "pages/face/index"
+                    state.condition.list[0].path = "pages/meeting/index"
                 } else if (args === 'menJin') {
                     if (e.style.navigationBarTitleText === "门禁识别") {
                         if (index != 0) {

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.