Explorar o código

更新iOS调试

3860082966@qq.com hai 21 horas
pai
achega
a7b214a43e
Modificáronse 68 ficheiros con 1114 adicións e 203 borrados
  1. 1 1
      android/app/build.gradle
  2. 2 0
      android/app/capacitor.build.gradle
  3. 5 2
      android/app/src/main/AndroidManifest.xml
  4. 6 0
      android/capacitor.settings.gradle
  5. 19 1
      build.android.md
  6. 75 0
      build.ios.md
  7. 16 0
      faq.webgl.md
  8. 2 0
      ios/App/Podfile
  9. 15 3
      ios/App/Podfile.lock
  10. 38 4
      package-lock.json
  11. 4 1
      package.json
  12. 20 0
      projects/live-app/src/app/app.component.scss
  13. 58 11
      projects/live-app/src/app/app.component.ts
  14. 3 0
      projects/live-app/src/app/app.routes.ts
  15. 54 0
      projects/live-app/src/app/components/appraise/appraise.component.html
  16. 29 0
      projects/live-app/src/app/components/appraise/appraise.component.scss
  17. 28 0
      projects/live-app/src/app/components/appraise/appraise.component.spec.ts
  18. 130 0
      projects/live-app/src/app/components/appraise/appraise.component.ts
  19. 4 4
      projects/live-app/src/app/components/gift-modal/gift-modal.component.ts
  20. 1 1
      projects/live-app/src/app/components/live/live.component.ts
  21. 3 3
      projects/live-app/src/app/components/pay-comp/pay-comp.component.html
  22. 144 31
      projects/live-app/src/app/components/pay-comp/pay-comp.component.ts
  23. 2 1
      projects/live-app/src/app/components/upload/upload.component.ts
  24. 0 2
      projects/live-app/src/index.html
  25. 1 1
      projects/live-app/src/modules/account/notice-log/notice-log.component.ts
  26. 1 1
      projects/live-app/src/modules/account/recharge/recharge.component.ts
  27. 1 1
      projects/live-app/src/modules/goods/vip/vip.component.ts
  28. 1 0
      projects/live-app/src/modules/live/chat/chat.component.html
  29. 3 1
      projects/live-app/src/modules/live/chat/chat.component.ts
  30. 6 5
      projects/live-app/src/modules/live/link-page/link-page.component.ts
  31. 1 1
      projects/live-app/src/modules/live/room-manage/room-manage.component.ts
  32. 1 1
      projects/live-app/src/modules/live/search/search.component.ts
  33. 38 0
      projects/live-app/src/modules/login/auth.guard.ts
  34. 1 1
      projects/live-app/src/modules/login/login.component.ts
  35. 14 14
      projects/live-app/src/modules/tabs/anthorhome/anthorhome.component.ts
  36. 34 16
      projects/live-app/src/modules/tabs/home/home.component.ts
  37. 4 0
      projects/live-app/src/modules/tabs/my/my.component.html
  38. 7 0
      projects/live-app/src/modules/tabs/my/my.component.scss
  39. 2 1
      projects/live-app/src/modules/tabs/my/my.component.ts
  40. 1 1
      projects/live-app/src/modules/tabs/tabs.modules.routes.ts
  41. 40 2
      projects/live-app/src/modules/tabs/tabs/tabs.component.ts
  42. 1 1
      projects/live-app/src/modules/user/album/album.component.ts
  43. 1 1
      projects/live-app/src/modules/user/anchor/anchor.component.ts
  44. 35 5
      projects/live-app/src/modules/user/certification/certification.component.html
  45. 4 0
      projects/live-app/src/modules/user/certification/certification.component.scss
  46. 115 31
      projects/live-app/src/modules/user/certification/certification.component.ts
  47. 5 1
      projects/live-app/src/modules/user/comment/comment.component.html
  48. 7 3
      projects/live-app/src/modules/user/comment/comment.component.scss
  49. 1 1
      projects/live-app/src/modules/user/feedback/feedback.component.ts
  50. 19 10
      projects/live-app/src/modules/user/profile/profile.component.html
  51. 7 3
      projects/live-app/src/modules/user/profile/profile.component.scss
  52. 5 3
      projects/live-app/src/modules/user/profile/profile.component.ts
  53. 1 11
      projects/live-app/src/modules/user/ranking/ranking.component.html
  54. 0 0
      projects/live-app/src/modules/user/ranking/ranking.component.scss
  55. 0 0
      projects/live-app/src/modules/user/ranking/ranking.component.spec.ts
  56. 2 1
      projects/live-app/src/modules/user/ranking/ranking.component.ts
  57. 1 0
      projects/live-app/src/modules/user/setting/setting.component.html
  58. 2 2
      projects/live-app/src/modules/user/setting/setting.component.scss
  59. 1 1
      projects/live-app/src/modules/user/setting/setting.component.ts
  60. 1 1
      projects/live-app/src/modules/user/share/share.component.ts
  61. 6 1
      projects/live-app/src/modules/user/user.modules.routes.ts
  62. 1 1
      projects/live-app/src/services/account.service.ts
  63. 12 1
      projects/live-app/src/services/address.ts
  64. 8 2
      projects/live-app/src/services/auth.service.ts
  65. 16 0
      projects/live-app/src/services/background-color.service.ts
  66. 29 11
      projects/live-app/src/services/live.service.ts
  67. 1 1
      projects/live-app/src/services/message.service.ts
  68. 18 0
      projects/live-app/src/styles.scss

+ 1 - 1
android/app/build.gradle

@@ -66,4 +66,4 @@ try {
     }
 } catch(Exception e) {
     logger.info("google-services.json not found, google-services plugin not applied. Push Notifications won't work")
-}
+}

+ 2 - 0
android/app/capacitor.build.gradle

@@ -10,6 +10,8 @@ android {
 apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle"
 dependencies {
     implementation project(':capacitor-app')
+    implementation project(':capacitor-camera')
+    implementation project(':capacitor-filesystem')
     implementation project(':capacitor-haptics')
     implementation project(':capacitor-keyboard')
     implementation project(':capacitor-status-bar')

+ 5 - 2
android/app/src/main/AndroidManifest.xml

@@ -6,8 +6,10 @@
         android:icon="@mipmap/ic_launcher"
         android:label="@string/app_name"
         android:roundIcon="@mipmap/ic_launcher_round"
-        android:networkSecurityConfig="@xml/network_security_config"
         android:supportsRtl="true"
+        android:networkSecurityConfig="@xml/network_security_config"
+        android:hardwareAccelerated="true"
+        android:largeHeap="true"
         android:theme="@style/AppTheme">
 
         <activity
@@ -16,6 +18,7 @@
             android:label="@string/title_activity_main"
             android:theme="@style/AppTheme.NoActionBarLaunch"
             android:launchMode="singleTask"
+            android:hardwareAccelerated="true"
             android:exported="true">
 
             <intent-filter>
@@ -39,7 +42,7 @@
     <!-- Permissions -->
 
     <uses-permission android:name="android.permission.INTERNET" />
-        <!--可选权限-->
+    <!--可选权限-->
     <uses-permission android:name="android.permission.CAMERA"/>
     <uses-permission android:name="android.permission.RECORD_AUDIO"/>
     <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>

+ 6 - 0
android/capacitor.settings.gradle

@@ -5,6 +5,12 @@ project(':capacitor-android').projectDir = new File('../node_modules/@capacitor/
 include ':capacitor-app'
 project(':capacitor-app').projectDir = new File('../node_modules/@capacitor/app/android')
 
+include ':capacitor-camera'
+project(':capacitor-camera').projectDir = new File('../node_modules/@capacitor/camera/android')
+
+include ':capacitor-filesystem'
+project(':capacitor-filesystem').projectDir = new File('../node_modules/@capacitor/filesystem/android')
+
 include ':capacitor-haptics'
 project(':capacitor-haptics').projectDir = new File('../node_modules/@capacitor/haptics/android')
 

+ 19 - 1
build.android.md

@@ -51,7 +51,9 @@
     将resources\android\xml\network_security_config.xml复制到android\app\src\main\res\xml\根目录
   - android\app\src\main\AndroidManifest.xml文件新增
   ``` bash
-    application 新增 android:networkSecurityConfig="@xml/network_security_config"
+    application 新增 
+    android:networkSecurityConfig="@xml/network_security_config"
+    android:hardwareAccelerated="true"
     <!--可选权限-->
     <uses-permission android:name="android.permission.CAMERA"/>
     <uses-permission android:name="android.permission.RECORD_AUDIO"/>
@@ -81,6 +83,22 @@
   ## 二、添加 IOS 平台
   - npx cap add ios 添加 IOS 平台
 
+  ### 三、mac设备打开xcode
+    - npx cap open ios
+    - cd ios/App 运行 pod install 安装依赖
+  
+  ### 四、修改配置文件
+  - ios/App/App/Info.plist文件添加权限
+    <!-- 相机权限 -->
+    <key>NSCameraUsageDescription</key>
+    <string>我们需要访问您的相机以进行拍照和视频直播推流。</string>
+    <!-- 照片库权限(可选) -->
+    <key>NSPhotoLibraryUsageDescription</key>
+    <string>我们需要访问您的照片库以选择照片。</string>
+    <!-- 相册保存权限(可选) -->
+    <key>NSPhotoLibraryAddUsageDescription</key>
+    <string>我们需要保存照片到您的相册。</string>
+
 # cordova打包(弃用)
   - ionic cordova build android --release
 

+ 75 - 0
build.ios.md

@@ -0,0 +1,75 @@
+# 安装
+  - npm install
+# cap打包IOS
+  ## 一、打包web资源到www文件内
+  - ng build live-app --output-path=www //默认打包
+    或者 ionic build --project=live-app
+  - 打包好后把所有文件放到www子目录
+  - npx cap sync ios如果修改了web代码,执行同步到iOS包
+  ## 二、添加 IOS 平台
+  - npx cap add ios 添加 IOS 平台
+
+  ### 三、mac设备打开xcode
+    - npx cap open ios
+    - cd ios/App 运行 pod install 安装依赖
+  
+  ### 四、修改配置文件
+  - ios/App/App/Info.plist文件添加权限
+    <!-- 相机权限 -->
+    <key>NSCameraUsageDescription</key>
+    <string>我们需要访问您的相机以进行拍照和视频直播推流。</string>
+    <!-- 照片库权限(可选) -->
+    <key>NSPhotoLibraryUsageDescription</key>
+    <string>我们需要访问您的照片库以选择照片。</string>
+    <!-- 相册保存权限(可选) -->
+    <key>NSPhotoLibraryAddUsageDescription</key>
+    <string>我们需要保存照片到您的相册。</string>
+  - ios/App/App/AppDelegate.swift添加以下函数检查相机麦克风等权限
+    ```sh
+    import UIKit
+    import Capacitor
+    import AVFoundation
+    @UIApplicationMain
+    class AppDelegate: UIResponder, UIApplicationDelegate {
+
+        var window: UIWindow?
+
+        func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
+            // Override point for customization after application launch.
+            // 请求相机和麦克风权限
+            requestCameraPermission()
+            requestMicrophonePermission()
+            return true
+        }
+        // 请求相机权限
+        func requestCameraPermission() {
+            AVCaptureDevice.requestAccess(for: .video) { granted in
+                DispatchQueue.main.async {
+                    if granted {
+                        print("相机权限已授予")
+                    } else {
+                        print("相机权限被拒绝")
+                    }
+                }
+            }
+        }
+
+        // 请求麦克风权限
+        func requestMicrophonePermission() {
+            AVAudioSession.sharedInstance().requestRecordPermission { granted in
+                DispatchQueue.main.async {
+                    if granted {
+                        print("麦克风权限已授予")
+                    } else {
+                        print("麦克风权限被拒绝")
+                    }
+                }
+            }
+        }
+        ***
+        ***
+    }    
+    ```
+  
+  ### 五、xcode打开调试
+  open ios/App/App.xcworkspace

+ 16 - 0
faq.webgl.md

@@ -0,0 +1,16 @@
+# WebGL 安卓卡顿问题
+
+# 同类情况
+- https://cloud.tencent.com/developer/article/2336102
+> 项目要求支持 8K 高清视频(H265编码)播放,拿到板子后却发现使用 App 可以播放 8K 高清视频,但使用浏览器却不行,即使安装上最新的 Chrome for Android 也不行。根据以往的浏览器内核开发经验,在 Android 平台上,Chromium WebView 最终是调用系统框架层的 MediaPlayer 进行播放。理论上只要系统框架层能够支持 8K 高清播放,那么浏览器应该也支持。实际情况却并非如此,而且 Android 10 预编译 WebView 没任何日志输出,所以需要下载源码编译 Chromium WebView,找出问题所在。
+
+# 原因分析
+- Android 10 + Capacitor打包浏览器,默认换回了System Webview,导致WebGL卡顿
+> On Android 5 and 6, Capacitor uses the Android System WebView. On Android 7+, Google Chrome is used.
+> the Android 10+ using the System WebView again
+
+# 解决方案
+- AndroidManifest.xml
+    - android:largeHeap="true"
+    - android:immersive="true"
+Try setting android:largeHeap="true" in your AndroidManifest.xml - Does that make any difference for you? It seemed to make an improvement for us. We also added android:immersive="true" (fits our app) and android:hardwareAccelerated="true" (defaults to true, but... just in case.)

+ 2 - 0
ios/App/Podfile

@@ -12,6 +12,8 @@ def capacitor_pods
   pod 'Capacitor', :path => '../../node_modules/@capacitor/ios'
   pod 'CapacitorCordova', :path => '../../node_modules/@capacitor/ios'
   pod 'CapacitorApp', :path => '../../node_modules/@capacitor/app'
+  pod 'CapacitorCamera', :path => '../../node_modules/@capacitor/camera'
+  pod 'CapacitorFilesystem', :path => '../../node_modules/@capacitor/filesystem'
   pod 'CapacitorHaptics', :path => '../../node_modules/@capacitor/haptics'
   pod 'CapacitorKeyboard', :path => '../../node_modules/@capacitor/keyboard'
   pod 'CapacitorStatusBar', :path => '../../node_modules/@capacitor/status-bar'

+ 15 - 3
ios/App/Podfile.lock

@@ -3,12 +3,16 @@ PODS:
     - CapacitorCordova
   - CapacitorApp (6.0.2):
     - Capacitor
+  - CapacitorCamera (6.0.0):
+    - Capacitor
   - CapacitorCordova (7.0.1)
+  - CapacitorFilesystem (6.0.0):
+    - Capacitor
   - CapacitorHaptics (6.0.2):
     - Capacitor
   - CapacitorKeyboard (6.0.3):
     - Capacitor
-  - CapacitorStatusBar (6.0.2):
+  - CapacitorStatusBar (6.0.0):
     - Capacitor
   - CordovaPlugins (6.2.0):
     - CapacitorCordova
@@ -16,7 +20,9 @@ PODS:
 DEPENDENCIES:
   - "Capacitor (from `../../node_modules/@capacitor/ios`)"
   - "CapacitorApp (from `../../node_modules/@capacitor/app`)"
+  - "CapacitorCamera (from `../../node_modules/@capacitor/camera`)"
   - "CapacitorCordova (from `../../node_modules/@capacitor/ios`)"
+  - "CapacitorFilesystem (from `../../node_modules/@capacitor/filesystem`)"
   - "CapacitorHaptics (from `../../node_modules/@capacitor/haptics`)"
   - "CapacitorKeyboard (from `../../node_modules/@capacitor/keyboard`)"
   - "CapacitorStatusBar (from `../../node_modules/@capacitor/status-bar`)"
@@ -27,8 +33,12 @@ EXTERNAL SOURCES:
     :path: "../../node_modules/@capacitor/ios"
   CapacitorApp:
     :path: "../../node_modules/@capacitor/app"
+  CapacitorCamera:
+    :path: "../../node_modules/@capacitor/camera"
   CapacitorCordova:
     :path: "../../node_modules/@capacitor/ios"
+  CapacitorFilesystem:
+    :path: "../../node_modules/@capacitor/filesystem"
   CapacitorHaptics:
     :path: "../../node_modules/@capacitor/haptics"
   CapacitorKeyboard:
@@ -41,12 +51,14 @@ EXTERNAL SOURCES:
 SPEC CHECKSUMS:
   Capacitor: de199cba6c8b20995428ad0b7cb0bc6ca625ffd4
   CapacitorApp: e1e6b7d05e444d593ca16fd6d76f2b7c48b5aea7
+  CapacitorCamera: de6cb68cccaa9e9dfaec96bd17ea998fabf8fdb2
   CapacitorCordova: 63d476958d5022d76f197031e8b7ea3519988c64
+  CapacitorFilesystem: 9c2cc1e89d3b8b91503b316e9f6c2915c9bf9419
   CapacitorHaptics: 4fc15afe22b123d093e6ace24d95e8e3b1f261b9
   CapacitorKeyboard: e89189ad398b815b6ff7e52271f0fb4f911a0a0a
-  CapacitorStatusBar: b16799a26320ffa52f6c8b01737d5a95bbb8f3eb
+  CapacitorStatusBar: 129c68650d3f950e080e8e7e03d69c3b361dbe52
   CordovaPlugins: 086b0aba49af3593b83c9bd4b22d1421013294c4
 
-PODFILE CHECKSUM: 9d92a24aa1609ed7b9e323001fc76af128076319
+PODFILE CHECKSUM: 531d09dd0af64bce06d29f414b83f2f566466951
 
 COCOAPODS: 1.16.2

+ 38 - 4
package-lock.json

@@ -18,22 +18,27 @@
         "@angular/router": "^18.0.0",
         "@capacitor/android": "6.2.0",
         "@capacitor/app": "^6.0.2",
+        "@capacitor/camera": "^6.0.0",
         "@capacitor/core": "6.2.0",
+        "@capacitor/filesystem": "^6.0.0",
         "@capacitor/haptics": "^6.0.2",
+        "@capacitor/ios": "^7.0.1",
         "@capacitor/keyboard": "^6.0.3",
-        "@capacitor/status-bar": "^6.0.2",
+        "@capacitor/status-bar": "^6.0.0",
         "@ionic/angular": "^8.4.0",
         "@ionic/angular-server": "^8.4.0",
         "@ionic/angular-toolkit": "^11.0.1",
         "@ionic/cordova-builders": "^12.1.2",
         "@ionic/core": "^8.4.0",
         "cordova-android": "13.0.0",
+        "ionicons": "^7.4.0",
         "ngx-build-plus": "^18.0.0",
         "parse": "^5.3.0",
         "qiniu-js": "^3.1.2",
         "svgaplayerweb": "^2.3.2",
         "swiper": "^11.1.14",
         "tslib": "^2.3.0",
+        "video-animation-player": "^1.0.5",
         "zone.js": "~0.14.3"
       },
       "devDependencies": {
@@ -2296,6 +2301,14 @@
         "@capacitor/core": "^6.0.0"
       }
     },
+    "node_modules/@capacitor/camera": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmmirror.com/@capacitor/camera/-/camera-6.0.0.tgz",
+      "integrity": "sha512-AZ/gfVPC3lsKbk9/yHI60ygNyOkN5jsCb4bHxXFbW0bss3XYtR/J1XWFJGkFNiRErNnTz6jnDrhsGCr7+JPfiA==",
+      "peerDependencies": {
+        "@capacitor/core": "^6.0.0"
+      }
+    },
     "node_modules/@capacitor/cli": {
       "version": "6.2.0",
       "resolved": "https://registry.npmmirror.com/@capacitor/cli/-/cli-6.2.0.tgz",
@@ -2467,6 +2480,14 @@
         "tslib": "^2.1.0"
       }
     },
+    "node_modules/@capacitor/filesystem": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmmirror.com/@capacitor/filesystem/-/filesystem-6.0.0.tgz",
+      "integrity": "sha512-GnC4CBfky7fvG9zSV/aQnZaGs6ZJ90AaQorr53z81ArTCqcrSUeBMuCxWmvti9HrdXLhBavyA1UOjvRGObOFjg==",
+      "peerDependencies": {
+        "@capacitor/core": "^6.0.0"
+      }
+    },
     "node_modules/@capacitor/haptics": {
       "version": "6.0.2",
       "resolved": "https://registry.npmmirror.com/@capacitor/haptics/-/haptics-6.0.2.tgz",
@@ -2475,6 +2496,14 @@
         "@capacitor/core": "^6.0.0"
       }
     },
+    "node_modules/@capacitor/ios": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmmirror.com/@capacitor/ios/-/ios-7.0.1.tgz",
+      "integrity": "sha512-RN6S1C1k7ue57DFmJM4EizzsYBrahTTiMhcnlHspFLaojgHbFWZbYq1VriuRKysPU1ka/P+klsdtRFsB5K9jyw==",
+      "peerDependencies": {
+        "@capacitor/core": "^7.0.0"
+      }
+    },
     "node_modules/@capacitor/keyboard": {
       "version": "6.0.3",
       "resolved": "https://registry.npmmirror.com/@capacitor/keyboard/-/keyboard-6.0.3.tgz",
@@ -2484,9 +2513,9 @@
       }
     },
     "node_modules/@capacitor/status-bar": {
-      "version": "6.0.2",
-      "resolved": "https://registry.npmmirror.com/@capacitor/status-bar/-/status-bar-6.0.2.tgz",
-      "integrity": "sha512-AmRIX6QvFemItlY7/69ARkIAqitRQqJ2qwgZmD1KqgFb78pH+XFXm1guvS/a8CuOOm/IqZ4ddDbl20yxtBqzGA==",
+      "version": "6.0.0",
+      "resolved": "https://registry.npmmirror.com/@capacitor/status-bar/-/status-bar-6.0.0.tgz",
+      "integrity": "sha512-Wo0ILugYlmENegKDgTzVCPjbvP8h1ObgHslLdgeVG643ViMS/diausHIq8e104WIKCXtKIELmQeYVp9mX7932g==",
       "peerDependencies": {
         "@capacitor/core": "^6.0.0"
       }
@@ -12958,6 +12987,11 @@
         "node": ">= 0.8"
       }
     },
+    "node_modules/video-animation-player": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmmirror.com/video-animation-player/-/video-animation-player-1.0.5.tgz",
+      "integrity": "sha512-KLz+uL6zojOYXEPFxL2AB0iKSLHnmKQPBnXmFWHpGtAdB6/j9EWCbWcUDCg0KVOBTFRHa2UfiSNK0y1I+LEfpA=="
+    },
     "node_modules/vite": {
       "version": "5.4.6",
       "resolved": "https://registry.npmmirror.com/vite/-/vite-5.4.6.tgz",

+ 4 - 1
package.json

@@ -21,17 +21,20 @@
     "@angular/router": "^18.0.0",
     "@capacitor/android": "6.2.0",
     "@capacitor/app": "^6.0.2",
+    "@capacitor/camera": "^6.0.0",
     "@capacitor/core": "6.2.0",
+    "@capacitor/filesystem": "^6.0.0",
     "@capacitor/haptics": "^6.0.2",
     "@capacitor/ios": "^7.0.1",
     "@capacitor/keyboard": "^6.0.3",
-    "@capacitor/status-bar": "^6.0.2",
+    "@capacitor/status-bar": "^6.0.0",
     "@ionic/angular": "^8.4.0",
     "@ionic/angular-server": "^8.4.0",
     "@ionic/angular-toolkit": "^11.0.1",
     "@ionic/cordova-builders": "^12.1.2",
     "@ionic/core": "^8.4.0",
     "cordova-android": "13.0.0",
+    "ionicons": "^7.4.0",
     "ngx-build-plus": "^18.0.0",
     "parse": "^5.3.0",
     "qiniu-js": "^3.1.2",

+ 20 - 0
projects/live-app/src/app/app.component.scss

@@ -7,3 +7,23 @@
   // position: absolute;
   // flex-direction: column;
 }
+
+/* 确保内容区域不被状态栏覆盖 */
+ion-app {
+  // --ion-safe-area-top: 0;
+  // --ion-safe-area-bottom: 0;
+  // --ion-safe-area-left: 0;
+  // --ion-safe-area-right: 0;
+}
+
+ion-content {
+  --ion-padding-top: env(safe-area-inset-top);
+  --ion-padding-bottom: env(safe-area-inset-bottom);
+  --ion-padding-start: env(safe-area-inset-left);
+  --ion-padding-end: env(safe-area-inset-right);
+}
+
+/* 确保内容区域不被状态栏覆盖 */
+ion-router-outlet {
+  padding-top: env(safe-area-inset-top);
+}

+ 58 - 11
projects/live-app/src/app/app.component.ts

@@ -1,19 +1,39 @@
-import { Component } from '@angular/core';
+import { Component, OnInit } from '@angular/core';
 import { IonApp, IonRouterOutlet } from '@ionic/angular/standalone';
 import Parse from 'parse';
+import { StatusBar, Style, StatusBarStyle } from '@capacitor/status-bar';
+import { BackgroundColorService } from '../services/background-color.service';
+import { Router } from '@angular/router';
+// 添加IonIcons
+import { addIcons } from 'ionicons';
+import * as icons from 'ionicons/icons';
+addIcons(icons)
 @Component({
   selector: 'app-root',
   standalone: true,
-  imports: [
-    IonApp,IonRouterOutlet
-  ],
+  imports: [IonApp, IonRouterOutlet],
   templateUrl: './app.component.html',
   styleUrl: './app.component.scss',
 })
-export class AppComponent {
+export class AppComponent implements OnInit {
   title = 'live-app';
-  constructor() {
+  constructor(
+    private router: Router,
+    private backgroundColorService: BackgroundColorService
+  ) {
     this.initParseService();
+    this.initStatusBar();
+  }
+  ngOnInit() {
+    // 初始化状态栏背景色
+    this.setStatusBarColor();
+
+    // 监听路由变化
+    this.router.events.subscribe((event) => {
+      if (event.constructor.name === 'NavigationEnd') {
+        this.setStatusBarColor();
+      }
+    });
   }
   initParseService() {
     Parse.initialize('ncloudmaster');
@@ -21,14 +41,41 @@ export class AppComponent {
     localStorage.setItem('company', 'Qje9D4bqol');
     this.saveParamsInvite();
   }
-  saveParamsInvite(){
-    let searchParams = this.searchParse()
-    let invite = searchParams?.get("invite");
-    invite && localStorage.setItem("invite",invite)
+  saveParamsInvite() {
+    let searchParams = this.searchParse();
+    let invite = searchParams?.get('invite');
+    invite && localStorage.setItem('invite', invite);
   }
-  searchParse(url?:string):URLSearchParams{
+  searchParse(url?: string): URLSearchParams {
     url = url || location.href;
     let u = new URL(url);
     return u.searchParams;
   }
+
+  async initStatusBar() {
+    try {
+      // 设置状态栏为沉浸式模式
+      // await StatusBar.setOverlaysWebView({ overlay: true });
+
+      // 设置状态栏样式(可选)
+      await StatusBar.setStyle({ style: StatusBarStyle.Light });
+
+      // 设置状态栏背景颜色(可选)
+      // await StatusBar.setBackgroundColor({ color: '#000000' });
+    } catch (error) {
+      console.error('设置状态栏时出错:', error);
+    }
+  }
+
+  async setStatusBarColor() {
+    try {
+      // 获取当前页面的背景色
+      const backgroundColor = this.backgroundColorService.getBackgroundColor();
+
+      // 设置状态栏背景色
+      await StatusBar.setBackgroundColor({ color: backgroundColor });
+    } catch (error) {
+      console.error('设置状态栏背景色时出错:', error);
+    }
+  }
 }

+ 3 - 0
projects/live-app/src/app/app.routes.ts

@@ -7,10 +7,13 @@ import { UserRoutingModule } from '../modules/user/user.modules.routes';
 import { AccountRoutingModule } from '../modules/account/account.modules.routes';
 import { GoodsRoutingModule } from '../modules/goods/goods.modules.routes';
 import { LiveRoutingModule } from '../modules/live/live.modules.routes';
+import { LoginAuthGuard } from '../modules/login/auth.guard';
+
 export const routes: Routes = [
   { path: '', redirectTo:'tabs', pathMatch: "full",}, // 默认跳转到 ''
   {
     path: 'login',
+    canActivate: mapToCanActivate([LoginAuthGuard]),
     loadComponent:()=> import('../modules/login/login.component').then((mod) => mod.LoginComponent),
   },
   {

+ 54 - 0
projects/live-app/src/app/components/appraise/appraise.component.html

@@ -0,0 +1,54 @@
+<ion-modal
+  #modal
+  trigger="open-modal"
+  [isOpen]="liveService.isOpenEvaluate"
+  (didDismiss)="liveService.isOpenEvaluate = false"
+  [backdropDismiss]="false"
+>
+  <ng-template>
+    <div class="modal-content">
+      <div class="title">评价</div>
+      <ion-segment
+        [scrollable]="true"
+        (ionChange)="segmentChanged($event)"
+        layout="icon-bottom"
+        value="negativeTags"
+        mode="md"
+      >
+        <ion-segment-button
+          value="negativeTags"
+          class="tabs"
+          content-id="negativeTags"
+        >
+          <ion-label>无感</ion-label>
+        </ion-segment-button>
+        <ion-segment-button
+          value="positiveTags"
+          class="tabs"
+          content-id="positiveTags"
+        >
+          <ion-label>喜欢</ion-label>
+        </ion-segment-button>
+      </ion-segment>
+      <div class="list">
+        @for (tag of list; track $index) {
+        <span 
+        (click)="onCheck(tag)"
+        [ngClass]="{
+          'action': checkTags.has(tag),
+        }">{{ tag }}</span>
+        } 
+      </div>
+    </div>
+    <ion-toolbar>
+      <ion-buttons slot="start">
+        <ion-button (click)="liveService.isOpenEvaluate = false"
+          >取消</ion-button
+        >
+      </ion-buttons>
+      <ion-buttons slot="end">
+        <ion-button (click)="confirm()">确认</ion-button>
+      </ion-buttons>
+    </ion-toolbar>
+  </ng-template>
+</ion-modal>

+ 29 - 0
projects/live-app/src/app/components/appraise/appraise.component.scss

@@ -0,0 +1,29 @@
+ion-modal {
+  --height: 280px;
+  --width: 89.7436vw;
+  --border-radius: 4.1026vw;
+  --box-shadow: 0 2.5641vw 3.8462vw -0.7692vw rgb(0 0 0 / 0.1),
+    0 1.0256vw 1.5385vw -1.0256vw rgb(0 0 0 / 0.1);
+}
+.title{
+  text-align: center;
+  font-size: 16px;
+  margin: 10px auto;
+  font-weight: bold;
+}
+.list{
+  display: flex;
+  flex-wrap: wrap;
+  padding: 4px;
+  span{
+    margin-right: 4px;
+    margin-bottom: 10px;
+    background-color: #d1d1d1; 
+    color: #fff;
+    padding: 2px 6px;
+    font-size: 14px;
+  }
+  .action{
+    background-color: #a065ee; 
+  }
+}

+ 28 - 0
projects/live-app/src/app/components/appraise/appraise.component.spec.ts

@@ -0,0 +1,28 @@
+/* tslint:disable:no-unused-variable */
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { By } from '@angular/platform-browser';
+import { DebugElement } from '@angular/core';
+
+import { AppraiseComponent } from './appraise.component';
+
+describe('AppraiseComponent', () => {
+  let component: AppraiseComponent;
+  let fixture: ComponentFixture<AppraiseComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ AppraiseComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(AppraiseComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 130 - 0
projects/live-app/src/app/components/appraise/appraise.component.ts

@@ -0,0 +1,130 @@
+import { Component, OnInit } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import {
+  ionicStandaloneModules,
+  LoadingController,
+  ToastController,
+} from '../../../modules/ionic-standalone.modules';
+import { LiveService } from '../../../services/live.service';
+import * as Parse from 'parse';
+
+@Component({
+  selector: 'app-appraise',
+  templateUrl: './appraise.component.html',
+  styleUrls: ['./appraise.component.scss'],
+  standalone: true,
+  imports: [...ionicStandaloneModules, CommonModule],
+})
+export class AppraiseComponent implements OnInit {
+  active: string = 'negativeTags';
+  negativeTags: Array<string> = [
+    '没礼貌',
+    '不好看',
+    '不专业',
+    '态度差',
+    '声音难听',
+    '内容无聊',
+    '互动少',
+    '妆容差',
+    '发型乱',
+    '背景杂乱',
+    '动作僵硬',
+    '表情不自然',
+    '内容重复',
+    '没有创意',
+    '不幽默',
+  ];
+  positiveTags: Array<string> = [
+    '很礼貌',
+    '很好看',
+    '很专业',
+    '态度好',
+    '声音好听',
+    '内容有趣',
+    '互动多',
+    '妆容精致',
+    '发型时尚',
+    '背景整洁',
+    '动作自然',
+    '表情生动',
+    '内容新颖',
+    '有创意',
+    '很幽默',
+  ];
+  list: Array<string> = this.negativeTags;
+  checkTags = new Set();
+  constructor(
+    private toastController: ToastController,
+    private loadingCtrl: LoadingController,
+    public liveService: LiveService
+  ) {}
+
+  ngOnInit() {}
+  segmentChanged(e: any) {
+    let { value } = e.detail;
+    // this.active = value;
+    console.log(value);
+    this.list = this[value]
+    this.checkTags = new Set();
+  }
+  async onCheck(tag:string){
+    if(this.checkTags.has(tag)){
+      this.checkTags.delete(tag)
+    }else{
+      if(this.checkTags.size >= 3){
+        const toast = await this.toastController.create({
+          message: '不超过3项',
+          color: 'danger',
+          duration: 1000,
+        });
+        toast.present();
+        return;
+      }
+      this.checkTags.add(tag)
+    }
+  }
+  confirm() {
+    this.onComment()
+  }
+
+  async onComment() {
+    const loading = await this.loadingCtrl.create({
+      message: '加载中',
+    });
+    loading.present();
+    if(this.checkTags.size > 3 || this.checkTags.size == 0){
+      loading.dismiss();
+      const toast = await this.toastController.create({
+        message: this.checkTags.size == 0 ? '至少选择一项' : '不超过3项',
+        color: 'danger',
+        duration: 1000,
+      });
+      toast.present();
+      return;
+    }
+    let obj = Parse.Object.extend('DramaPostLog');
+    let postLog = new obj();
+    postLog.set('room', {
+      __type: 'Pointer',
+      className: 'Room',
+      objectId: this.liveService.room?.id,
+    });
+    postLog.set('company', {
+      __type: 'Pointer',
+      className: 'Company',
+      objectId: this.liveService?.company,
+    });
+    postLog.set('user', {
+      __type: 'Pointer',
+      className: '_User',
+      objectId: this.liveService.room?.get('user')?.id,
+    });
+    postLog.set('type', 'comment');
+    // postLog.set('credit', star)
+    postLog.set('tags',Array.from(this.checkTags))
+    await postLog.save();
+    loading.dismiss();
+    this.liveService.isOpenEvaluate = false;
+    this.checkTags = new Set();
+  }
+}

+ 4 - 4
projects/live-app/src/app/components/gift-modal/gift-modal.component.ts

@@ -67,13 +67,13 @@ export class GiftModalComponent implements OnInit {
     public liveService: LiveService,
     private loadingCtrl: LoadingController,
     public accServ: AccountService,
-    public toastController: ToastController,
+    private toastController: ToastController,
     public msgSerrvice: MessageService
   ) {}
   async ngOnInit() {
     this.giftList = this.msgSerrvice.giftList;
     setTimeout(() => {
-      this.selectTab('all');
+      this.selectTab('all',true);
     }, 200);
   }
   async openModal() {
@@ -81,8 +81,8 @@ export class GiftModalComponent implements OnInit {
     this.wallet = await this.aiServ.getWallet(uid);
     this.isOpenGift = true;
   }
-  async selectTab(val: string) {
-    if (this.activeTab === val) return;
+  async selectTab(val: string, force?:boolean) {
+    if (this.activeTab === val && !force) return;
     this.giftList = await this.aiServ.getGift(val == 'all' ? '' : val);
     this.activeTab = val;
     this.currentGift = null;

+ 1 - 1
projects/live-app/src/app/components/live/live.component.ts

@@ -26,7 +26,7 @@ export class LiveComponent implements OnInit {
   changeScreen: boolean = false; //大小屏切换
   timer: any; //轮询获取频道token的定时
   constructor(
-    public toastController: ToastController,
+    private toastController: ToastController,
     private loadingCtrl: LoadingController,
     private activateRoute: ActivatedRoute,
     private alertController: AlertController,

+ 3 - 3
projects/live-app/src/app/components/pay-comp/pay-comp.component.html

@@ -28,7 +28,7 @@
             <div class="checkbox"></div>
           </div>
         </div>
-        <!-- <div class="bar"></div>
+        <div class="bar"></div>
         <div class="row" (click)="onchangPay('alipay')">
           <div class="label">
             <img src="img/pay_alipay.png" alt="" class="icon" />
@@ -42,11 +42,11 @@
           >
             <div class="checkbox"></div>
           </div>
-        </div> -->
+        </div>
       </div>
       <div class="pay-footer">
         <div class="order-num">订单编号{{ tradeNo }}</div>
-        <div class="pay-btn" (click)="openWxPay()">去付款</div>
+        <div class="pay-btn" (click)="userPayment()">去付款</div>
       </div>
       } @else{
       <div class="qrcode">

+ 144 - 31
projects/live-app/src/app/components/pay-comp/pay-comp.component.ts

@@ -8,6 +8,7 @@ import {
   ionicStandaloneModules,
   AlertController,
   ToastController,
+  LoadingController,
 } from '../../../modules/ionic-standalone.modules';
 declare var wx: any;
 
@@ -20,7 +21,7 @@ declare var wx: any;
 })
 export class PayCompComponent implements OnInit {
   @Input('price') price!: number;
-  @Input('credit') credit!: number|undefined;//钻石
+  @Input('credit') credit!: number | undefined; //钻石
   @Input('gid') gid: string | undefined;
   @Input('orderType') orderType!: string; //订单类型
   @Input('tradeNo') tradeNo: string | undefined; //支付单号
@@ -36,12 +37,14 @@ export class PayCompComponent implements OnInit {
   user: Parse.Object = Parse.User.current()!;
   timer: any; //定时查询
   accountLog?: Parse.Object; // 充值记录
+  loading:any //等待
   constructor(
     private accServ: AccountService,
     private toastController: ToastController,
     private alertCtrl: AlertController,
     private http: HttpClient,
-    private activRoute: ActivatedRoute
+    private activRoute: ActivatedRoute,
+    private loadingCtrl: LoadingController,
   ) {}
 
   ngOnInit() {
@@ -59,26 +62,8 @@ export class PayCompComponent implements OnInit {
     this.checkpay = val;
     console.log(val);
   }
-  // async showCodeModal(type: string) {
-  //   if (!this.tradeNo) {
-  //     this.tradeNo = this.accServ.setTradeNo();
-  //   } else {
-  //     this.orderId = await this.getOrder();
-  //     if(this.orderType == 'service' && !this.orderId){
-  //       this.tradeNo = this.accServ.setTradeNo();
-  //     }
-  //   }
-  //   console.log(this.tradeNo);
-  //   switch (type) {
-  //     case 'wxpay':
-  //       this.openWxPay();
-  //       break;
-  //     default:
-  //       break;
-  //   }
-  // }
-  /* 校验支付方式 */
-  async openWxPay() {
+  /* 用户确认支付 */
+  async userPayment() {
     if (!this.tradeNo) {
       this.tradeNo = this.accServ.setTradeNo();
     } else {
@@ -93,6 +78,17 @@ export class PayCompComponent implements OnInit {
       }
     }
     console.log(this.tradeNo);
+    switch (this.checkpay) {
+      case 'wxpay':
+        this.openWxPay();
+        break;
+      case 'alipay':
+        this.openAlipay();
+        break;
+    }
+  }
+  /* 校验支付方式 */
+  async openWxPay() {
     let ua = navigator.userAgent.toLowerCase();
     let isWeixin = ua.indexOf('micromessenger') != -1;
     if (isWeixin) {
@@ -143,8 +139,10 @@ export class PayCompComponent implements OnInit {
                   };
                   if (this.orderId) {
                     this.accServ.updateAccountLog(info, this.orderId);
-                  }else if(this.orderType == 'recharge'){
-                    this.accountLog = await this.accServ.updateRecharge(this.accountLog!)
+                  } else if (this.orderType == 'recharge') {
+                    this.accountLog = await this.accServ.updateRecharge(
+                      this.accountLog!
+                    );
                   }
                   _this.toast('支付成功', 'success');
                   _this.isOpen = false;
@@ -216,6 +214,119 @@ export class PayCompComponent implements OnInit {
         this.toast(err.message, 'danger');
       });
   }
+
+  /* 支付宝支付 */
+  async openAlipay() {
+    if (this.isDisabled) return;
+    this.loading = await this.loadingCtrl.create({
+      message: '正在操作',
+    });
+    this.loading.present();
+    try {
+      this.isDisabled = true;
+      await this.getOrderId();
+      let data: any = await this.getAliPayUrl();
+      console.log(data);
+
+      const tempDiv = document.createElement('div');
+      tempDiv.innerHTML = data.pay_url; // 假设data.pay_url是完整的HTML字符串
+      const formElement = tempDiv.querySelector('form');
+      if (formElement) {
+        // 阻止默认行为,避免页面刷新
+        formElement.addEventListener('submit', (event) => {
+          event.preventDefault();
+        });
+        // 添加隐藏的iframe来提交表单
+        const iframe = document.createElement('iframe');
+        iframe.style.display = 'none';
+        document.body.appendChild(iframe);
+        formElement.target = iframe.name;
+
+        document.body.appendChild(formElement);
+        formElement.submit();
+        document.body.removeChild(formElement); // 提交后移除表单元素
+      } else {
+        console.error('未找到表单元素');
+        this.loading?.dismiss();
+      }
+
+      this.timer = setInterval(async () => {
+        console.log('支付结果轮询');
+        let AccountLog = new Parse.Query('AccountLog');
+        AccountLog.equalTo('company', this.accServ.company);
+        AccountLog.equalTo('orderNumber', this.tradeNo);
+        AccountLog.equalTo('payType', 'aliPay');
+        AccountLog.equalTo('isVerified', true);
+        let result = await AccountLog.first();
+        this.loading?.dismiss();
+
+        if (result && result.id) {
+          clearInterval(this.timer);
+          this.timer = null;
+        }
+        if (!this.timer) {
+          //支付成功返回状态
+          this.toast('支付成功', 'success');
+          this.isOpen = false;
+          this.payResult.emit({
+            code: 200,
+            tradeNo: this.tradeNo,
+            type: this.checkpay,
+          });
+        }
+      }, 1000);
+    } catch (err: any) {
+      this.loading?.dismiss();
+      this.isDisabled = false;
+      let alert = await this.alertCtrl.create({
+        header: '异常错误',
+        subHeader: '',
+        message: err.message,
+        buttons: [
+          {
+            role: 'ok',
+            text: '确认',
+            handler: () => {},
+          },
+        ],
+      });
+      alert.present();
+    }
+  }
+  // 获取支付宝支付
+  async getAliPayUrl() {
+    return new Promise((resolve, reject) => {
+      let beforURL = window.location.href;
+      let params = {
+        company: this.accServ.company,
+        tradeNo: this.tradeNo,
+        price: this.price,
+        tradetype: 'wap',
+        // price: 0.01,
+        orderTitle: this.title,
+        returnUrl: beforURL,
+      };
+      try {
+        this.http
+          .post('https://server.fmode.cn/api/alipay/neworder', params)
+          .subscribe((res: any) => {
+            if (res.code !== 200) {
+              this.toast('网络错误,请稍后重试', 'warning');
+              return;
+            }
+            if (res.data && res.data.pay_url) {
+              resolve(res.data);
+            }
+          });
+      } catch (err) {
+        if (err) {
+          this.toast('网络错误,请稍后重试', 'warning');
+          reject(err);
+        }
+      }
+    });
+  }
+
   /* 关闭支付弹窗 */
   onClose() {
     this.isOpen = false;
@@ -230,24 +341,24 @@ export class PayCompComponent implements OnInit {
   async getOrderId() {
     if (this.orderType == 'vip' && !this.orderId && this.gid) {
       let resulte = await this.accServ.setOrder({
-        type: 'service',//创建服务类订单
+        type: 'service', //创建服务类订单
         gid: this.gid,
         price: this.price,
         total_fee: this.price,
         tradeNo: this.tradeNo!,
         out_trade_no: this.tradeNo!,
-        payType: 'wxpay',
+        payType: this.checkpay,
       });
       console.log(resulte);
       if (resulte?.objectId) {
         this.orderId = resulte.objectId;
       }
     } else if (this.orderType == 'recharge') {
-      this.accountLog = await this.accServ.updataOrder({
+      this.accountLog = await this.accServ.creatdRechargeLog({
         tradeNo: this.tradeNo!,
-        payType: 'wxpay',
+        payType: this.checkpay,
         price: this.price,
-        credit:this.credit!
+        credit: this.credit!,
       });
     }
   }
@@ -268,8 +379,10 @@ export class PayCompComponent implements OnInit {
             clearInterval(that.timer);
             if (this.orderId) {
               this.accServ.updateAccountLog(info, this.orderId);
-            }else if(this.orderType == 'recharge'){
-              this.accountLog = await this.accServ.updateRecharge(this.accountLog!)
+            } else if (this.orderType == 'recharge') {
+              this.accountLog = await this.accServ.updateRecharge(
+                this.accountLog!
+              );
             }
             that.toast('支付成功', 'success');
             that.isOpen = false;

+ 2 - 1
projects/live-app/src/app/components/upload/upload.component.ts

@@ -66,7 +66,7 @@ export class UploadComponent implements OnInit {
   loading: any;
   currentPreviewImg:string = ''
   constructor(
-    public toastController: ToastController,
+    private toastController: ToastController,
     public loadCtrl: LoadingController
   ) {
     Parse.Cloud.run('qiniu_uptoken', { company: this.company }).then((data) => {
@@ -102,6 +102,7 @@ export class UploadComponent implements OnInit {
   onDelete(index: number) {
     console.log(index);
     this.fileList.splice(index, 1);
+    this.active && this.onChange.emit(this.fileList);
   }
   async onAdd(e: any) {
     let files = e.target.files;

+ 0 - 2
projects/live-app/src/index.html

@@ -6,8 +6,6 @@
   <base href="/">
   <meta name="viewport" content="width=device-width, initial-scale=1">
   <link rel="icon" type="image/x-icon" href="favicon.ico">
-  <script type="module" src="https://unpkg.com/ionicons@7.1.0/dist/ionicons/ionicons.esm.js"></script>
-  <script nomodule src="https://unpkg.com/ionicons@7.1.0/dist/ionicons/ionicons.js"></script>
 	<script type="text/javascript" src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
   <script src="assets/js/AgoraRTC_N-4.14.0.js"></script>
   <script src="assets/js/agora-rtm-2.2.0.min.js"></script>

+ 1 - 1
projects/live-app/src/modules/account/notice-log/notice-log.component.ts

@@ -25,7 +25,7 @@ export class NoticeLogComponent implements OnInit {
   user: Parse.Object = Parse.User.current()!;
 
   constructor(
-    public toastController: ToastController,
+    private toastController: ToastController,
     private loadingCtrl: LoadingController,
     private router: Router,
     private msgSer: MessageService,

+ 1 - 1
projects/live-app/src/modules/account/recharge/recharge.component.ts

@@ -37,7 +37,7 @@ export class RechargeComponent implements OnInit {
   tradeNo: string = ''; //支付单号
   orderId?: string;
   constructor(
-    public toastController: ToastController,
+    private toastController: ToastController,
     public loadingController: LoadingController,
     private router: Router
   ) {}

+ 1 - 1
projects/live-app/src/modules/goods/vip/vip.component.ts

@@ -44,7 +44,7 @@ export class VipComponent implements OnInit {
   loading: boolean = true;
   constructor(
     private modalController: ModalController,
-    public toastController: ToastController,
+    private toastController: ToastController,
     private authSer: AuthService,
     private accServ: AccountService,
     private router: Router,

+ 1 - 0
projects/live-app/src/modules/live/chat/chat.component.html

@@ -204,3 +204,4 @@
 @if(profile?.id){
 <app-call-modal #call [profile]="profile"></app-call-modal>
 }
+<app-appraise></app-appraise>

+ 3 - 1
projects/live-app/src/modules/live/chat/chat.component.ts

@@ -21,6 +21,7 @@ import {
 import { AiChatService } from '../../../services/aichart.service';
 import { AccountService } from '../../../services/account.service';
 import { CallModalComponent } from '../../../app/components/call-modal/call-modal.component';
+import { AppraiseComponent } from '../../../app/components/appraise/appraise.component';
 
 @Component({
   selector: 'app-chat',
@@ -35,6 +36,7 @@ import { CallModalComponent } from '../../../app/components/call-modal/call-moda
     CommonModule,
     SharedModule,
     CallModalComponent,
+    AppraiseComponent
   ],
   // providers: [DatePipe],
 })
@@ -61,7 +63,7 @@ export class ChatComponent implements OnInit {
     private router: Router,
     private http: HttpClient,
     // public datePipe: DatePipe,
-    public toastController: ToastController,
+    private toastController: ToastController,
     private alertController: AlertController,
     private loadingCtrl: LoadingController,
     private activateRoute: ActivatedRoute,

+ 6 - 5
projects/live-app/src/modules/live/link-page/link-page.component.ts

@@ -51,7 +51,7 @@ export class LinkPageComponent implements OnInit {
   loading: boolean = true;
   userVip: any;
   constructor(
-    public toastController: ToastController,
+    private toastController: ToastController,
     private loadingCtrl: LoadingController,
     private alertController: AlertController,
     private activateRoute: ActivatedRoute,
@@ -209,11 +209,12 @@ export class LinkPageComponent implements OnInit {
           cssClass: 'secondary',
           handler: () => {
             console.log('Confirm Cancel: blah');
-            if (this.liveService.isAnchor) {
-              this.onExit();
+            this.onExit();
+            if (!this.liveService.isAnchor) {
+              this.liveService.isOpenEvaluate = true
               return;
             }
-            this.onComment();
+            // this.onComment();
           },
         },
       ],
@@ -291,7 +292,7 @@ export class LinkPageComponent implements OnInit {
   }
   onExit() {
     this.liveService.client.leave();
-    history.back();
+    // history.back();
   }
 
   ngOnDestroy(): void {

+ 1 - 1
projects/live-app/src/modules/live/room-manage/room-manage.component.ts

@@ -33,7 +33,7 @@ export class RoomManageComponent implements OnInit {
   profile?: Parse.Object
   initLoad:boolean = true;
   constructor(
-    public toastController: ToastController,
+    private toastController: ToastController,
     private loadingCtrl: LoadingController,
     private liveService: LiveService,
     private alertController: AlertController,

+ 1 - 1
projects/live-app/src/modules/live/search/search.component.ts

@@ -26,7 +26,7 @@ export class SearchComponent implements OnInit {
   constructor(
     private aiServ: AiChatService,
     private router: Router,
-    public toastController: ToastController
+    private toastController: ToastController
   ) {}
 
   async presentToast(message: string) {

+ 38 - 0
projects/live-app/src/modules/login/auth.guard.ts

@@ -0,0 +1,38 @@
+import { Injectable } from '@angular/core';
+import {
+  CanActivate,
+  ActivatedRouteSnapshot,
+  RouterStateSnapshot,
+} from '@angular/router';
+import { Observable } from 'rxjs';
+import { Router } from '@angular/router';
+import Parse from 'parse';
+
+@Injectable({
+  providedIn: 'root',
+})
+export class LoginAuthGuard implements CanActivate {
+  LoginPage = 'tabs';
+
+  constructor(public router: Router) {}
+
+  canActivate(
+    next: ActivatedRouteSnapshot,
+    state: RouterStateSnapshot
+  ): Observable<boolean> | Promise<boolean> | boolean {
+    // 当前路由url
+    // let url: string = state.url;
+    return this.checkLogin();
+  }
+  checkLogin(): boolean {
+    // 如果已登录,直接跳转首页
+    let currentUser = Parse.User.current();
+    if (currentUser?.id) {
+      // 否则重定向到login页面
+      this.router.navigate([this.LoginPage]);
+      return false;
+    }
+    return true;
+  }
+}
+export { CanActivate };

+ 1 - 1
projects/live-app/src/modules/login/login.component.ts

@@ -35,7 +35,7 @@ export class LoginComponent implements OnInit {
   };
   currentUser: any;
   constructor(
-    public toastController: ToastController,
+    private toastController: ToastController,
     public authServ: AuthService,
     // private router: Router,
     private activatedRoute: ActivatedRoute,

+ 14 - 14
projects/live-app/src/modules/tabs/anthorhome/anthorhome.component.ts

@@ -11,7 +11,7 @@ import {
   ToastController,
 } from '../../ionic-standalone.modules';
 import { CommonModule, DatePipe } from '@angular/common';
-import { province } from '../../../services/address';
+import { provinceMap } from '../../../services/address';
 import { FormsModule } from '@angular/forms';
 import { InfiniteScrollCustomEvent } from '@ionic/core';
 import { MessageService } from '../../../services/message.service';
@@ -68,22 +68,22 @@ export class AnthorhomeComponent implements OnInit {
     private activateRoute: ActivatedRoute,
     private connectTask: ConnectTaskService,
     private router: Router,
-    public toastController: ToastController,
+    private toastController: ToastController,
     private aiServ: AiChatService,
     private msgSer: MessageService,
     private datePipe: DatePipe
   ) {
-    province.unshift({
-      provinceName: '全部',
-      citys: [
-        {
-          cityName: '',
-          cityType: '',
-        },
-      ],
-    });
-    this.provinceColumns = province.map((item) => item.provinceName);
-    this.cityColumns = province[0].citys.map((item) => item.cityName);
+    // province.unshift({
+    //   provinceName: '全部',
+    //   citys: [
+    //     {
+    //       cityName: '',
+    //       cityType: '',
+    //     },
+    //   ],
+    // });
+    this.provinceColumns = provinceMap.map((item) => item.provinceName);
+    this.cityColumns = provinceMap[0].citys.map((item) => item.cityName);
   }
 
   ngOnInit() {
@@ -224,7 +224,7 @@ export class AnthorhomeComponent implements OnInit {
     switch (type) {
       case 'province':
         this.province = event.detail.value;
-        this.cityColumns = province
+        this.cityColumns = provinceMap
           .find((item) => item.provinceName === val)
           ?.citys.map((item) => item.cityName)!;
         console.log(this.cityColumns);

+ 34 - 16
projects/live-app/src/modules/tabs/home/home.component.ts

@@ -10,14 +10,15 @@ import {
   LoadingController,
 } from '../../ionic-standalone.modules';
 import { CommonModule, DatePipe } from '@angular/common';
-import { province } from '../../../services/address';
+import { provinceMap } from '../../../services/address';
 import { FormsModule } from '@angular/forms';
+import { BackgroundColorService } from '../../../services/background-color.service';
 @Component({
   selector: 'app-home',
   templateUrl: './home.component.html',
   styleUrls: ['./home.component.scss'],
   standalone: true,
-  imports: [...ionicStandaloneModules, CommonModule,FormsModule],
+  imports: [...ionicStandaloneModules, CommonModule, FormsModule],
   providers: [DatePipe],
 })
 export class HomeComponent implements OnInit {
@@ -57,6 +58,11 @@ export class HomeComponent implements OnInit {
       value: '五星',
       icon: 'people-outline',
     },
+    {
+      label: '海外',
+      value: '海外',
+      icon: 'people-outline',
+    },
   ];
   currentValue: string = 'recommend';
   oldCurrentValue: string = 'recommend';
@@ -90,24 +96,26 @@ export class HomeComponent implements OnInit {
     private connectTask: ConnectTaskService,
     private router: Router,
     private aiServ: AiChatService,
-    private datePipe: DatePipe
+    private datePipe: DatePipe,
+    private backgroundColorService: BackgroundColorService
   ) {
-    province.unshift({
-      provinceName: '全部',
-      citys: [
-        {
-          cityName: '',
-          cityType: '',
-        },
-      ],
-    });
-    this.provinceColumns = province.map((item) => item.provinceName);
-    this.cityColumns = province[0].citys.map((item) => item.cityName);
+    // province.unshift({
+    //   provinceName: '全部',
+    //   citys: [
+    //     {
+    //       cityName: '',
+    //       cityType: '',
+    //     },
+    //   ],
+    // });
+    this.provinceColumns = provinceMap.map((item) => item.provinceName);
+    this.cityColumns = provinceMap[0].citys.map((item) => item.cityName);
   }
 
   ngOnInit() {
     // this.activateRoute.paramMap.subscribe(async (params) => {
     this.refresh();
+    this.backgroundColorService.setBackgroundColor('#fe5559');
     // });
   }
   async refresh() {
@@ -187,7 +195,7 @@ export class HomeComponent implements OnInit {
     if (!this.connectTask.onlineUserList.size) {
       await this.connectTask.getOnlieUserList('user_connect_room');
     }
-    this.userList = Array.from(this.connectTask.onlineUserList)|| [];
+    this.userList = Array.from(this.connectTask.onlineUserList) || [];
     // console.log(userList);
     switch (type) {
       case 'follow':
@@ -243,6 +251,16 @@ export class HomeComponent implements OnInit {
           city: this.city,
         });
         break;
+      case '海外':
+        data = await this.aiServ.getRooms({
+          uid: uid,
+          // users: userList,
+          star: '海外',
+          sex,
+          city: this.city,
+        });
+        break;
+
       default:
         break;
     }
@@ -327,7 +345,7 @@ export class HomeComponent implements OnInit {
     switch (type) {
       case 'province':
         this.province = event.detail.value;
-        this.cityColumns = province
+        this.cityColumns = provinceMap
           .find((item) => item.provinceName === val)
           ?.citys.map((item) => item.cityName)!;
         console.log(this.cityColumns);

+ 4 - 0
projects/live-app/src/modules/tabs/my/my.component.html

@@ -98,6 +98,10 @@
     }
   </div>
   <div class="order" [style.background-image]="'url(img/用户榜底.png)'">
+    <div class="title">
+      <div class="link">排行榜</div>
+      <div class="link" (click)="toUrl('user/ranking')">更多</div>
+    </div>
     <div class="ladder">
       <div class="top-block">
         <div class="top2">

+ 7 - 0
projects/live-app/src/modules/tabs/my/my.component.scss

@@ -92,6 +92,7 @@
   }
   .ad {
     display: flex;
+    justify-content: space-between;
     align-items: center;
     padding: 1.5385vw 2.5641vw;
     font-size: 3.0769vw;
@@ -307,6 +308,12 @@
         }
       }
     }
+    .title{
+      display: flex;
+      justify-content: space-between;
+      color: #7d7d7d;
+      font-size: 14px;
+    }
   }
   .setting {
     width: 92.3077vw;

+ 2 - 1
projects/live-app/src/modules/tabs/my/my.component.ts

@@ -27,7 +27,7 @@ export class MyComponent implements OnInit {
     private modalController: ModalController,
     private alertController: AlertController,
     public authServ: AuthService,
-    public toastController: ToastController,
+    private toastController: ToastController,
     private activateRoute: ActivatedRoute,
     private router: Router,
     public aiServ: AiChatService,
@@ -209,6 +209,7 @@ export class MyComponent implements OnInit {
       cssClass: 'my-custom-class',
       header: '',
       message: '你确定退出登录吗?',
+      backdropDismiss:false,
       buttons: [
         {
           text: '确定',

+ 1 - 1
projects/live-app/src/modules/tabs/tabs.modules.routes.ts

@@ -6,7 +6,7 @@ import { HomeComponent } from './home/home.component';
 import { LiveReviewComponent } from './live-review/live-review.component';
 import { MyComponent } from './my/my.component';
 import { NoticeComponent } from './notice/notice.component';
-import { RankingComponent } from './ranking/ranking.component';
+import { RankingComponent } from '../user/ranking/ranking.component';
 import { SpaceComponent } from './space/space.component';
 const routes: Routes = [
   {

+ 40 - 2
projects/live-app/src/modules/tabs/tabs/tabs.component.ts

@@ -6,11 +6,14 @@ import { IonicModule } from '@ionic/angular';
 
 // 全局消息服务和JIM插件
 import { OnInit } from '@angular/core';
-import { AlertController } from '@ionic/angular';
+// import { AlertController } from '@ionic/angular';
 import { FlutterCompComponent } from '../../../app/components/flutter-comp/flutter-comp.component';
 // import { MessageService } from '../../../services/message.service';
 // import { ConnectTaskService } from '../../../services/connectTask.service';
 
+import { Camera } from '@capacitor/camera';
+import { Filesystem } from '@capacitor/filesystem';
+
 @Component({
   selector: 'live-tabs',
   templateUrl: 'tabs.component.html',
@@ -77,7 +80,9 @@ export class TabsComponent implements OnInit {
   show: any;
   active: number = 0;
 
-  ngOnInit() {}
+  ngOnInit() {
+    this.initDeviceService()
+  }
 
   async presentAlert() {
     // let profile = localStorage.getItem('profile');
@@ -158,4 +163,37 @@ export class TabsComponent implements OnInit {
       this.allowNavi = true;
     }, 1000);
   }
+
+
+  async initDeviceService() {
+    try {
+      // 请求相机权限
+      const cameraPermission = await Camera.requestPermissions();
+      if (cameraPermission.camera === 'granted') {
+        console.log('相机权限已授予');
+      } else {
+        console.log('相机权限被拒绝');
+      }
+
+      // 请求文件系统权限
+      const filesystemPermission = await Filesystem.requestPermissions();
+      if (filesystemPermission.publicStorage === 'granted') {
+        console.log('文件系统权限已授予');
+      } else {
+        console.log('文件系统权限被拒绝');
+      }
+      await this.requestMicrophonePermission()
+    } catch (error) {
+      console.error('请求权限时出错:', error);
+    }
+  }
+  async requestMicrophonePermission() {
+    try {
+      const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
+      console.log('麦克风权限已授予');
+      // 你可以在这里处理音频流
+    } catch (error) {
+      console.error('麦克风权限被拒绝或请求失败:', error);
+    }
+  }
 }

+ 1 - 1
projects/live-app/src/modules/user/album/album.component.ts

@@ -22,7 +22,7 @@ export class AlbumComponent implements OnInit {
   loading: boolean = false;
   constructor(
     public loadCtrl: LoadingController,
-    public toastController: ToastController,
+    private toastController: ToastController,
     private authServ: AuthService
   ) {}
   ngOnInit() {

+ 1 - 1
projects/live-app/src/modules/user/anchor/anchor.component.ts

@@ -54,7 +54,7 @@ export class AnchorComponent implements OnInit {
   constructor(
     private modalController: ModalController,
     public loadingCtrl: LoadingController,
-    public toastController: ToastController,
+    private toastController: ToastController,
     private authServ: AuthService,
     private alertController: AlertController,
     private router: Router,

+ 35 - 5
projects/live-app/src/modules/user/certification/certification.component.html

@@ -4,7 +4,7 @@
     <div class="hred-left">
       {{ title }}
     </div>
-    <img src="http://file-cloud.fmode.cn/real-idcard.png" class="hred-img" />
+    <img src="https://file-cloud.fmode.cn/real-idcard.png" class="hred-img" />
   </div>
   @if (isReal) {
   <div class="tips">
@@ -20,7 +20,9 @@
     <div class="">{{ secretIdCard }}********</div>
   </div>
   <div class="agreement pflex">
-    <span (click)="showAgreement()">《{{registerAgreement?.get('title')}}》</span>
+    <span (click)="showAgreement()"
+      >《{{ registerAgreement?.get("title") }}》</span
+    >
   </div>
   } @else{
   <div class="h3">填写身份信息认证</div>
@@ -36,7 +38,7 @@
     />
   </div>
   <div class="li">
-    身份证号
+    {{cardtype == 'regions' ? '证件号码' : '身份证号'}}
     <input
       type="text"
       [(ngModel)]="idCard"
@@ -47,11 +49,39 @@
       autocomplete="new-password"
     />
   </div>
-  <div class="footer" (click)="check()">提交</div>
+
+  @if (cardtype == 'cn') {
+  <div class="footer" (click)="submit()">提交</div>
+  <div class="agreement">
+    <span class="abroad" (click)="cardtype = 'regions'">国外用户认证</span>
+  </div>
+  }@else {
+  <div class="li row">
+    请上传有效护照或外国永久居留证
+    <app-upload
+      (onChange)="changeFile($event)"
+      #upload
+      [maxlenght]="1"
+      [files]="eduImage ? [{ url: eduImage }] : []"
+      [fileWidth]="100"
+      [fileHeight]="160"
+      [boxWidth]="100"
+      [active]="true"
+      style="margin:10px auto;"
+    ></app-upload>
+  </div>
+  <div class="footer" (click)="submitRegions()">提交</div>
+  <div class="agreement">
+    <span class="abroad" (click)="cardtype = 'cn'">国内用户认证</span>
+  </div>
+  }
+
   <div class="agreement">
     <ion-checkbox color="primary" [(ngModel)]="agreement"></ion-checkbox>
     <div class="agreement-content">
-      阅读且同意<span (click)="showAgreement()">{{registerAgreement?.get('title')}}</span>
+      阅读且同意<span (click)="showAgreement()">{{
+        registerAgreement?.get("title")
+      }}</span>
     </div>
   </div>
   }

+ 4 - 0
projects/live-app/src/modules/user/certification/certification.component.scss

@@ -105,6 +105,10 @@
       flex: 1;
     }
   }
+  .row{
+    flex-direction: column;
+    align-items: start;
+  }
 
   .footer {
     margin: 26.6667vw auto 5.3333vw;

+ 115 - 31
projects/live-app/src/modules/user/certification/certification.component.ts

@@ -1,4 +1,4 @@
-import { Component, OnInit } from '@angular/core';
+import { Component, OnInit, ViewChild } from '@angular/core';
 import { FormsModule } from '@angular/forms';
 import {
   LoadingController,
@@ -10,16 +10,27 @@ import { identityCodeValid } from '../../../services/utils';
 import { AgreementComponent } from '../../login/agreement/agreement.component';
 import * as Parse from 'parse';
 import { NavComponent } from '../../../app/components/nav/nav.component';
-import { ionicStandaloneModules } from '../../ionic-standalone.modules';
+import {
+  AlertController,
+  ionicStandaloneModules,
+} from '../../ionic-standalone.modules';
+import { UploadComponent } from '../../../app/components/upload/upload.component';
 
 @Component({
   selector: 'app-certification',
   templateUrl: './certification.component.html',
   styleUrls: ['./certification.component.scss'],
   standalone: true,
-  imports: [...ionicStandaloneModules, FormsModule, NavComponent],
+  imports: [
+    ...ionicStandaloneModules,
+    FormsModule,
+    NavComponent,
+    UploadComponent,
+  ],
 })
 export class CertificationComponent implements OnInit {
+  @ViewChild('upload') upload!: UploadComponent;
+
   name: string = '';
   idCard: string = '';
   title: string =
@@ -33,11 +44,15 @@ export class CertificationComponent implements OnInit {
   agreement: boolean = false;
   registerAgreement: any;
   profile?: Parse.Object;
+  cardtype: 'regions' | 'cn' = 'cn'; // 身份证类型
+  eduImage: string = '';
+
   constructor(
     private service: HttpService,
     public loadCtrl: LoadingController,
     private modalController: ModalController,
-    public toastController: ToastController
+    private alertController: AlertController,
+    private toastController: ToastController
   ) {}
 
   ngOnInit() {
@@ -61,10 +76,17 @@ export class CertificationComponent implements OnInit {
     this.profile = await query.first();
     if (this.profile?.id) {
       this.isReal = this.profile.get('isCross');
-      if (this.isReal)
+      if (this.isReal) {
         localStorage.setItem('profile', JSON.stringify(this.profile.toJSON()));
-      this.secretIdCard = this.profile.get('idcard')?.slice(0, 6);
-      this.secretName = this.profile.get('name')?.slice(0, 1);
+        this.secretIdCard = this.profile.get('idcard')?.slice(0, 6);
+        this.secretName = this.profile.get('name')?.slice(0, 1);
+      } else if (this.profile.get('cardtype') === 'regions') {
+        this.cardtype = 'regions'
+        this.name = this.profile.get('name');
+        this.idCard = this.profile.get('idcard');
+        this.eduImage = this.profile.get('eduImage')
+        this.presentAlert('审核中',true);
+      }
     }
   }
   async showAgreement() {
@@ -87,10 +109,10 @@ export class CertificationComponent implements OnInit {
     toast.present();
   }
 
-  async check() {
-    if(!this.agreement){
-      this.presentToast('请先阅读且同意隐私协议',1500,'danger')
-      return
+  async submit() {
+    if (!this.agreement) {
+      this.presentToast('请先阅读且同意隐私协议', 1500, 'danger');
+      return;
     }
     this.loading = await this.loadCtrl.create();
     this.loading.present();
@@ -115,10 +137,14 @@ export class CertificationComponent implements OnInit {
         );
         return;
       }
-      if(this.getAgeFromIdCard(this.idCard) < 18){
+      if (this.getAgeFromIdCard(this.idCard) < 18) {
         this.loading.dismiss();
-        await this.presentToast('根据平台规定,仅限满18周岁用户使用。',1500,'danger')
-        return
+        await this.presentToast(
+          '根据平台规定,仅限满18周岁用户使用。',
+          1500,
+          'danger'
+        );
+        return;
       }
       let res: any = await this.service.postAuth(
         this.company,
@@ -129,8 +155,8 @@ export class CertificationComponent implements OnInit {
         let { isok } = res.data.result;
         console.log(isok);
         if (isok) {
-          let { sex,city } = res.data.result.IdCardInfor;
-          this.anthProfile(sex,city);
+          let { sex, city } = res.data.result.IdCardInfor;
+          this.anthProfile(sex, city);
           return;
         }
         this.loading.dismiss();
@@ -146,12 +172,13 @@ export class CertificationComponent implements OnInit {
     }, 500);
   }
 
-  async anthProfile(sex: string,city:string) {
+  async anthProfile(sex?: string, city?: string) {
     let user = Parse.User.current();
     let query = new Parse.Query('Profile');
     query.equalTo('idcard', this.idCard);
     query.equalTo('company', this.company);
     query.notEqualTo('isDeleted', true);
+    query.notEqualTo('user', Parse.User.current()?.id);
     let res = await query.first();
     if (res && res.id) {
       this.loading.dismiss();
@@ -179,19 +206,33 @@ export class CertificationComponent implements OnInit {
     }
     this.profile?.set('idcard', this.idCard);
     this.profile?.set('name', this.name);
-    this.profile?.set('sex', sex);
-    this.profile?.set('city', city);
-    this.profile?.set('isCross', true);
-    this.profile?.set('birthdate', String(this.getAgeFromIdCard(this.idCard)));
-    await this.profile?.save();
-    if (this.profile?.id) {
-      localStorage.setItem('profile', JSON.stringify(this.profile.toJSON()));
-      this.loading.dismiss();
-      this.isReal = true;
-      await this.presentToast('认证成功', 1500, 'primary');
-      setTimeout(() => {
-        history.back();
-      }, 1500);
+    this.profile?.set('cardtype', this.cardtype);
+    if (this.cardtype === 'cn') {
+      this.profile?.set('sex', sex);
+      this.profile?.set('city', city);
+      this.profile?.set('isCross', true);
+      this.profile?.set(
+        'birthdate',
+        String(this.getAgeFromIdCard(this.idCard))
+      );
+      await this.profile?.save();
+      if (this.profile?.id) {
+        localStorage.setItem('profile', JSON.stringify(this.profile.toJSON()));
+        this.loading.dismiss();
+        this.isReal = true;
+        await this.presentToast('认证成功', 1500, 'primary');
+        setTimeout(() => {
+          history.back();
+        }, 1500);
+      }
+    } else {
+      this.profile?.set('eduImage',this.eduImage)
+      await this.profile?.save();
+      if (this.profile?.id) {
+        localStorage.setItem('profile', JSON.stringify(this.profile.toJSON()));
+        this.loading.dismiss();
+        this.presentAlert('提交成功,等待审核',true);
+      }
     }
   }
 
@@ -208,10 +249,53 @@ export class CertificationComponent implements OnInit {
     // 计算年龄
     let age = today.getFullYear() - birthDate.getFullYear();
     const monthDiff = today.getMonth() - birthDate.getMonth();
-    if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
+    if (
+      monthDiff < 0 ||
+      (monthDiff === 0 && today.getDate() < birthDate.getDate())
+    ) {
       age--;
     }
     return age;
   }
 
+  async changeFile(e: any) {
+    this.eduImage = e[0]?.url;
+    console.log(this.eduImage);
+  }
+  async submitRegions() {
+    if (!this.agreement) {
+      this.presentToast('请先阅读且同意隐私协议', 1500, 'danger');
+      return;
+    }
+    this.loading = await this.loadCtrl.create();
+    this.loading.present();
+    this.time && clearTimeout(this.time);
+    this.time = setTimeout(async () => {
+      this.name = this.name.trim();
+      this.idCard = this.idCard.trim();
+      console.log(this.name, this.idCard);
+      if (!this.idCard || !this.name || !this.eduImage) {
+        this.loading.dismiss();
+        await this.presentToast('请填写完整信息', 1500, 'danger');
+        return;
+      }
+      this.anthProfile();
+    }, 500);
+  }
+  async presentAlert(msg: string, isBack?: boolean) {
+    const alert = await this.alertController.create({
+      header: '提示',
+      message: msg,
+      buttons: [
+        {
+          text: '好的',
+          role: 'confirm',
+          handler: () => {
+            isBack && history.back();
+          },
+        },
+      ],
+    });
+    await alert.present();
+  }
 }

+ 5 - 1
projects/live-app/src/modules/user/comment/comment.component.html

@@ -18,7 +18,11 @@
           LV{{ item?.get("user").get("achievementCount") }}
         </div>
       </div>
-      <div class="li-score">{{ item?.get("comment") }}</div>
+      <div class="li-score">
+        @for(i of item?.get("tags"); track $index){
+        <span>{{ i }}</span>
+        }
+      </div>
     </div>
     }
   </div>

+ 7 - 3
projects/live-app/src/modules/user/comment/comment.component.scss

@@ -38,9 +38,13 @@
       }
       .li-score{
         font-size: 12px;
-        background-color: #c509ff;
-        padding: 2px 4px;
-        color: white;
+        span{
+          background-color: #c509ff;
+          padding: 2px 4px;
+          color: white;
+          margin-left: 2px;
+          border-radius: 4px;
+        }
       }
     }
   }

+ 1 - 1
projects/live-app/src/modules/user/feedback/feedback.component.ts

@@ -21,7 +21,7 @@ export class FeedbackComponent implements OnInit {
   company: string | null = '';
   constructor(
     private authServ: AuthService,
-    public toastController: ToastController
+    private toastController: ToastController
   ) {
     this.company = authServ.company;
   }

+ 19 - 10
projects/live-app/src/modules/user/profile/profile.component.html

@@ -154,11 +154,13 @@
       <div class="comment-room">
         <div class="title-text comment-title">
           <div class="coulum">
-            对ta的评价({{
-              commentObj.score ? commentObj.score + "分" : "暂无评价"
-            }})
+            对ta的评价
+            <!-- ({{ commentObj.score ? commentObj.score + "分" : "暂无评价"}}) -->
           </div>
-          <div class="coulum" (click)="tourl('/user/comment',{rid: this.room?.id})">
+          <div
+            class="coulum"
+            (click)="tourl('/user/comment', { rid: this.room?.id })"
+          >
             {{ commentObj.count }}条<ion-icon
               name="chevron-forward-outline"
             ></ion-icon>
@@ -187,18 +189,24 @@
                 LV{{ item?.get("user").get("achievementCount") }}
               </div>
             </div>
-            <div class="li-score">{{ item?.get("comment") }}</div>
+            <div class="li-score">
+              @for(i of item?.get("tags"); track $index){
+              <span>{{ i }}</span>
+              }
+            </div>
           </div>
           }
         </div>
       </div>
     </div>
     <div class="data-row">
-      <div class="title-text flex">ta的礼物墙
-        <div class="coulum" (click)="tourl('/user/income',{uid: profile?.get('user')?.id})">
-          <ion-icon
-            name="chevron-forward-outline"
-          ></ion-icon>
+      <div class="title-text flex">
+        ta的礼物墙
+        <div
+          class="coulum"
+          (click)="tourl('/user/income', { uid: profile?.get('user')?.id })"
+        >
+          <ion-icon name="chevron-forward-outline"></ion-icon>
         </div>
       </div>
       <div class="gift">
@@ -298,3 +306,4 @@
 @if(profile?.id){
 <app-call-modal #call [profile]="profile"></app-call-modal>
 }
+<app-appraise></app-appraise>

+ 7 - 3
projects/live-app/src/modules/user/profile/profile.component.scss

@@ -252,9 +252,13 @@
             }
             .li-score{
               font-size: 12px;
-              background-color: #c509ff;
-              padding: 2px 4px;
-              color: white;
+              span{
+                background-color: #c509ff;
+                padding: 2px 4px;
+                color: white;
+                margin-left: 2px;
+                border-radius: 4px;
+              }
             }
           }
         }

+ 5 - 3
projects/live-app/src/modules/user/profile/profile.component.ts

@@ -20,6 +20,7 @@ import {
 import { AccountService } from '../../../services/account.service';
 import { CallModalComponent } from '../../../app/components/call-modal/call-modal.component';
 import { AvatarComponent } from '../../../app/components/avatar/avatar.component';
+import { AppraiseComponent } from '../../../app/components/appraise/appraise.component';
 
 @Component({
   selector: 'app-profile',
@@ -36,7 +37,8 @@ import { AvatarComponent } from '../../../app/components/avatar/avatar.component
     UploadComponent,
     GiftModalComponent,
     CallModalComponent,
-    AvatarComponent
+    AvatarComponent,
+    AppraiseComponent
   ],
   providers: [DatePipe],
 })
@@ -190,7 +192,7 @@ export class ProfileComponent implements OnInit {
     let r = await query.first();
     if(r?.id){
       this.room = r;
-      this.commentObj.score = await this.aiChatServ.getCommentScore(r?.id);
+      // this.commentObj.score = await this.aiChatServ.getCommentScore(r?.id);
       let queryPost = new Parse.Query('DramaPostLog');
       queryPost.equalTo('room', r.id);
       queryPost.notEqualTo('isDeleted', true);
@@ -301,7 +303,7 @@ export class ProfileComponent implements OnInit {
     profileRadar?.set('isDeleted', this.isFollow);
     await profileRadar?.save();
     this.isFollow = !this.isFollow;
-    this.isFollow ? (this.numsObject.fans += 1) : (this.numsObject.fans -= 1);
+    this.isFollow ? (this.numsObject.fans = Number(this.numsObject.fans) + 1) : (this.numsObject.fans = this.numsObject.fans - 1);
   }
   onShowImg(url: string) {
     this.currenImg = url;

+ 1 - 11
projects/live-app/src/modules/tabs/ranking/ranking.component.html → projects/live-app/src/modules/user/ranking/ranking.component.html

@@ -1,14 +1,4 @@
-<ion-header [translucent]="true" class="header">
-  <ion-toolbar class="toolbar">
-    <!-- <ion-buttons slot="start">
-      <ion-icon
-        name="chevron-back-outline"
-        style="width: 6.4vw; height: 6.4vw; color: #ffffff"
-      ></ion-icon>
-    </ion-buttons> -->
-    <ion-title class="title">排名</ion-title>
-  </ion-toolbar>
-</ion-header>
+<nav title="排名"></nav>
 <ion-content class="content"> 
   <div class="order" [style.background-image]="'url(img/用户榜底.png)'">
     <div class="ladder">

+ 0 - 0
projects/live-app/src/modules/tabs/ranking/ranking.component.scss → projects/live-app/src/modules/user/ranking/ranking.component.scss


+ 0 - 0
projects/live-app/src/modules/tabs/ranking/ranking.component.spec.ts → projects/live-app/src/modules/user/ranking/ranking.component.spec.ts


+ 2 - 1
projects/live-app/src/modules/tabs/ranking/ranking.component.ts → projects/live-app/src/modules/user/ranking/ranking.component.ts

@@ -1,6 +1,7 @@
 import { Component, OnInit } from '@angular/core';
 import { IonicModule } from '@ionic/angular';
 import * as Parse from 'parse';
+import { NavComponent } from '../../../app/components/nav/nav.component';
 import { AiChatService } from '../../../services/aichart.service';
 
 @Component({
@@ -8,7 +9,7 @@ import { AiChatService } from '../../../services/aichart.service';
   templateUrl: './ranking.component.html',
   styleUrls: ['./ranking.component.scss'],
   standalone: true,
-  imports: [IonicModule],
+  imports: [IonicModule,NavComponent],
 })
 export class RankingComponent implements OnInit {
   user?: Parse.Object = Parse.User.current();

+ 1 - 0
projects/live-app/src/modules/user/setting/setting.component.html

@@ -48,6 +48,7 @@
         interface="popover"
         placeholder="{{ formData.sex }}"
         [disabled]="true"
+        class="row"
       >
         <ion-select-option value="男">男</ion-select-option>
         <ion-select-option value="女">女</ion-select-option>

+ 2 - 2
projects/live-app/src/modules/user/setting/setting.component.scss

@@ -11,7 +11,7 @@ ion-content {
   --background: #ffffff;
   .portrait {
     background-color: white;
-    padding:4px 10px 4px 0;
+    padding:4px 10px 4px 0 !important;
     font-size: 4.2667vw;
     display: flex;
     justify-content: space-between;
@@ -36,7 +36,7 @@ ion-content {
     align-items: center;
     width: 100%;
     flex-shrink: 0;
-    // padding:0 4vw;
+    padding:0 4vw;
     // border: 0.2667vw solid #f6f6f6;
     .right {
       display: flex;

+ 1 - 1
projects/live-app/src/modules/user/setting/setting.component.ts

@@ -107,7 +107,7 @@ export class SettingComponent implements OnInit {
   constructor(
     private alertController: AlertController,
     public loadingCtrl: LoadingController,
-    public toastController: ToastController,
+    private toastController: ToastController,
     private authServ: AuthService,
     private accServ: AccountService,
     // private router: Router

+ 1 - 1
projects/live-app/src/modules/user/share/share.component.ts

@@ -21,7 +21,7 @@ export class ShareComponent implements OnInit {
     'https://file-cloud.fmode.cn/Qje9D4bqol/20241220/qvj1bm054428527.png';
   codeUrl: string = '';
   constructor(
-    public toastController: ToastController,
+    private toastController: ToastController,
     private http: HttpClient
   ) {}
 

+ 6 - 1
projects/live-app/src/modules/user/user.modules.routes.ts

@@ -11,6 +11,7 @@ import { SettingComponent } from './setting/setting.component';
 import { ShareComponent } from './share/share.component';
 import { CommentComponent } from './comment/comment.component';
 import { GiftIncomeComponent } from './gift-income/gift-income.component';
+import { RankingComponent } from './ranking/ranking.component';
 const routes: Routes = [
   {
     path: 'profile/:id',//主页
@@ -56,7 +57,11 @@ const routes: Routes = [
   {
     path:'income',
     component:GiftIncomeComponent
-  }
+  },
+  {
+    path: 'ranking', //排名
+    component: RankingComponent,
+  },
 ]
 @NgModule({
   imports: [RouterModule.forChild(routes)],

+ 1 - 1
projects/live-app/src/services/account.service.ts

@@ -223,7 +223,7 @@ export class AccountService {
     });
   }
   //充值记录
-  async updataOrder(param: {
+  async creatdRechargeLog(param: {
     tradeNo: string;
     payType: string;
     price: number;

+ 12 - 1
projects/live-app/src/services/address.ts

@@ -2945,4 +2945,15 @@ export const province =  [
       ],
       "provinceName": "台湾"
     }
-  ]
+]
+let provinceAdd = [...province]
+provinceAdd.unshift({
+  provinceName: '全部',
+  citys: [
+    {
+      cityName: '',
+      cityType: '',
+    },
+  ],
+});
+export const provinceMap = provinceAdd

+ 8 - 2
projects/live-app/src/services/auth.service.ts

@@ -5,6 +5,8 @@ import { MessageService } from './message.service';
 import { ConnectTaskService } from './connectTask.service';
 import { HttpClient } from '@angular/common/http';
 import { catchError } from 'rxjs/operators';
+import { NavController } from '@ionic/angular';
+import { Location } from '@angular/common';
 @Injectable({
   providedIn: 'root',
 })
@@ -15,7 +17,9 @@ export class AuthService {
     private connectTask: ConnectTaskService,
     private router: Router,
     private msgSer: MessageService,
-    private http: HttpClient
+    private http: HttpClient,
+    private navCtrl: NavController,
+    private location: Location,
   ) {}
   authMobile(
     mobile: string,
@@ -117,7 +121,9 @@ export class AuthService {
       this.msgSer?.logOutRTM();
       this.msgSer?.reset();
       this.connectTask?.reset();
-      this.router.navigate(['login']);
+      // this.router.navigate(['login']);
+      this.navCtrl.navigateRoot('/login', { animated: false });
+      this.location.replaceState('/login');
     });
   }
 }

+ 16 - 0
projects/live-app/src/services/background-color.service.ts

@@ -0,0 +1,16 @@
+import { Injectable } from '@angular/core';
+
+@Injectable({
+  providedIn: 'root'
+})
+export class BackgroundColorService {
+  private backgroundColor: string = '#ffffff'; // 默认背景色
+
+  setBackgroundColor(color: string) {
+    this.backgroundColor = color;
+  }
+
+  getBackgroundColor(): string {
+    return this.backgroundColor;
+  }
+}

+ 29 - 11
projects/live-app/src/services/live.service.ts

@@ -1,4 +1,5 @@
 import { Injectable } from '@angular/core';
+import { Router } from '@angular/router';
 import { AlertController } from '@ionic/angular';
 import * as Parse from 'parse';
 import { AiChatService } from './aichart.service';
@@ -56,8 +57,12 @@ export class LiveService {
   countdown: number = 0; // 新增倒计时变量
   timer_countdown: any;
   liveLog?: Parse.Object; //直播记录
+
+  isOpenEvaluate: boolean = false; //是否开启评价
+
   constructor(
     private http: HttpService,
+    private router: Router,
     private aiServ: AiChatService,
     private alertController: AlertController,
     private msgSer: MessageService
@@ -224,7 +229,9 @@ export class LiveService {
       await this.client.publish(Object.values(this.localTracks));
     } catch (err) {
       console.log('发布本地视频失败:', err);
-      this.alertTips('发布本地视频失败', '提示', () => history.back());
+      // history.back()
+      this.alertTips('发布本地视频失败,请检查摄像头是否授权或正常', '提示');
+      this.client.leave();
     }
   }
   /* 订阅远程视频 */
@@ -276,8 +283,9 @@ export class LiveService {
         }
         //主播离开,停止计时计费
         // if (!this.isAnchor) {
-          this.client.leave();
+        this.client.leave();
         // }
+        // history.back()
         this.alertTips('对方已离开直播间');
       }
     });
@@ -295,7 +303,11 @@ export class LiveService {
           if (!this.isAnchor) {
             //用户离开直播间,断开与主播状态连接频道
             this.msgSer.unsubscribeMessage(this.room?.get('user')?.id);
+            this.isOpenEvaluate = true;
           }
+          console.log(this.router.url);
+          if(this.router.url.indexOf('/live/link-room/') == 0)
+          curState == 'DISCONNECTED' && history.back();
         }
         console.log('live状态变更:', this.connection_state);
       }
@@ -334,7 +346,9 @@ export class LiveService {
   async afterJoin() {
     this.timer && clearTimeout(this.timer);
     this.timer_countdown && clearInterval(this.timer_countdown);
-    const targetUser = this.client.remoteUsers?.find((user: any) => user.uid !== 100001) //排出超管
+    const targetUser = this.client.remoteUsers?.find(
+      (user: any) => user.uid !== 100001
+    ); //排出超管
     console.log(targetUser);
     if (this.client.remoteUsers.length > 0 && targetUser) {
       if (this.isAnchor) {
@@ -363,7 +377,7 @@ export class LiveService {
       }, 1000);
     }
   }
-  async getLiveLog(tarUid:string) {
+  async getLiveLog(tarUid: string) {
     // let uid = this.client.remoteUsers[0].uid;
     this.timer && clearTimeout(this.timer);
     let query = new Parse.Query('LiveLog');
@@ -377,7 +391,7 @@ export class LiveService {
       this.liveLog?.set('isLive', true);
       await this.liveLog?.save();
       await this.get_duration();
-      this.getCallDuration()
+      this.getCallDuration();
       return;
     }
     this.timer = setTimeout(() => {
@@ -395,8 +409,8 @@ export class LiveService {
     console.log(data);
     this.surplusNumber = data.data ?? 0;
     this.countdown = this.surplusNumber; // 初始化倒计时
-    let arr = ['RECONNECTING', 'DISCONNECTED', 'DISCONNECTING'];
-    if (!arr.includes(this.connection_state as any)) {
+    let arr = ['CONNECTING', 'CONNECTED'];
+    if (arr.includes(this.connection_state as any)) {
       if (this.countdown <= 120) {
         this.alertTips('剩余通话时间不足2分钟,请及时充值');
       }
@@ -411,6 +425,7 @@ export class LiveService {
         // console.log(this.countdown);
       } else {
         clearInterval(this.timer_countdown);
+        // history.back()
         this.alertTips('通话时间结束');
         this.client.leave(); // 结束通话
       }
@@ -438,12 +453,15 @@ export class LiveService {
     }, num ?? 10000);
   }
   /* 主播每10s获取一次对方可通话时长 */
-  getCallDuration(){
+  getCallDuration() {
     this.timer && clearTimeout(this.timer);
-    this.timer = setTimeout(async() => {
+    this.timer = setTimeout(async () => {
       await this.get_duration();
-      this.getCallDuration();
-    });
+      let arr = ['CONNECTING', 'CONNECTED'];
+      if (arr.includes(this.connection_state as any)) {
+        this.getCallDuration();
+      }
+    }, 10000);
   }
   /* 监听音视频设备插拔 */
   monitorDevices() {

+ 1 - 1
projects/live-app/src/services/message.service.ts

@@ -47,7 +47,7 @@ export class MessageService {
   constructor(
     private alertController: AlertController,
     private router: Router,
-    public toastController: ToastController,
+    private toastController: ToastController,
     private http: HttpService,
     private aiServ: AiChatService
   ) {

+ 18 - 0
projects/live-app/src/styles.scss

@@ -15,6 +15,24 @@ body{
   font-size: 16px;
   overflow-x: hidden;
 }
+:root {
+  --ion-safe-area-top: env(safe-area-inset-top);
+  --ion-safe-area-bottom: env(safe-area-inset-bottom);
+  --ion-safe-area-left: env(safe-area-inset-left);
+  --ion-safe-area-right: env(safe-area-inset-right);
+}
+
+body {
+  padding-top: var(--ion-safe-area-top);
+  padding-bottom: var(--ion-safe-area-bottom);
+  padding-left: var(--ion-safe-area-left);
+  padding-right: var(--ion-safe-area-right);
+}
+
+ion-content {
+  padding-top: var(--ion-safe-area-top);
+  padding-bottom: var(--ion-safe-area-bottom);
+}
 @import "@ionic/angular/css/core.css";
 @import "@ionic/angular/css/normalize.css";
 @import "@ionic/angular/css/structure.css";