This commit is contained in:
JayJiaJun 2025-02-17 18:44:53 +08:00
parent 0a6cf2ef99
commit 55833cbdc9
96 changed files with 2898 additions and 206 deletions

101
android/.gitignore vendored Normal file
View File

@ -0,0 +1,101 @@
# Using Android gitignore template: https://github.com/github/gitignore/blob/HEAD/Android.gitignore
# Built application files
*.apk
*.aar
*.ap_
*.aab
# Files for the ART/Dalvik VM
*.dex
# Java class files
*.class
# Generated files
bin/
gen/
out/
# Uncomment the following line in case you need and you don't have the release build type files in your app
# release/
# Gradle files
.gradle/
build/
# Local configuration file (sdk path, etc)
local.properties
# Proguard folder generated by Eclipse
proguard/
# Log Files
*.log
# Android Studio Navigation editor temp files
.navigation/
# Android Studio captures folder
captures/
# IntelliJ
*.iml
.idea/workspace.xml
.idea/tasks.xml
.idea/gradle.xml
.idea/assetWizardSettings.xml
.idea/dictionaries
.idea/libraries
# Android Studio 3 in .gitignore file.
.idea/caches
.idea/modules.xml
# Comment next line if keeping position of elements in Navigation Editor is relevant for you
.idea/navEditor.xml
# Keystore files
# Uncomment the following lines if you do not want to check your keystore files in.
#*.jks
#*.keystore
# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild
.cxx/
# Google Services (e.g. APIs or Firebase)
# google-services.json
# Freeline
freeline.py
freeline/
freeline_project_description.json
# fastlane
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
fastlane/readme.md
# Version control
vcs.xml
# lint
lint/intermediates/
lint/generated/
lint/outputs/
lint/tmp/
# lint/reports/
# Android Profiling
*.hprof
# Cordova plugins for Capacitor
capacitor-cordova-android-plugins
# Copied web assets
app/src/main/assets/public
# Generated Config files
app/src/main/assets/capacitor.config.json
app/src/main/assets/capacitor.plugins.json
app/src/main/res/xml/config.xml

2
android/app/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/build/*
!/build/.npmkeep

54
android/app/build.gradle Normal file
View File

@ -0,0 +1,54 @@
apply plugin: 'com.android.application'
android {
namespace "com.gateway.com"
compileSdk rootProject.ext.compileSdkVersion
defaultConfig {
applicationId "com.gateway.com"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
aaptOptions {
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
// Default: https://android.googlesource.com/platform/frameworks/base/+/282e181b58cf72b6ca770dc7ca5f91f135444502/tools/aapt/AaptAssets.cpp#61
ignoreAssetsPattern '!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~'
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
repositories {
flatDir{
dirs '../capacitor-cordova-android-plugins/src/main/libs', 'libs'
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
implementation "androidx.coordinatorlayout:coordinatorlayout:$androidxCoordinatorLayoutVersion"
implementation "androidx.core:core-splashscreen:$coreSplashScreenVersion"
implementation project(':capacitor-android')
testImplementation "junit:junit:$junitVersion"
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
implementation project(':capacitor-cordova-android-plugins')
}
apply from: 'capacitor.build.gradle'
try {
def servicesJSON = file('google-services.json')
if (servicesJSON.text) {
apply plugin: 'com.google.gms.google-services'
}
} catch(Exception e) {
logger.info("google-services.json not found, google-services plugin not applied. Push Notifications won't work")
}

View File

@ -0,0 +1,19 @@
// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN
android {
compileOptions {
sourceCompatibility JavaVersion.VERSION_21
targetCompatibility JavaVersion.VERSION_21
}
}
apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle"
dependencies {
}
if (hasProperty('postBuildExtras')) {
postBuildExtras()
}

21
android/app/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@ -0,0 +1,26 @@
package com.getcapacitor.myapp;
import static org.junit.Assert.*;
import android.content.Context;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() throws Exception {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
assertEquals("com.getcapacitor.app", appContext.getPackageName());
}
}

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode|navigation"
android:name=".MainActivity"
android:label="@string/title_activity_main"
android:theme="@style/AppTheme.NoActionBarLaunch"
android:launchMode="singleTask"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"></meta-data>
</provider>
</application>
<!-- Permissions -->
<uses-permission android:name="android.permission.INTERNET" />
</manifest>

View File

@ -0,0 +1,5 @@
package com.gateway.com;
import com.getcapacitor.BridgeActivity;
public class MainActivity extends BridgeActivity {}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -0,0 +1,34 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportHeight="108"
android:viewportWidth="108">
<path
android:fillType="evenOdd"
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
android:strokeColor="#00000000"
android:strokeWidth="1">
<aapt:attr name="android:fillColor">
<gradient
android:endX="78.5885"
android:endY="90.9159"
android:startX="48.7653"
android:startY="61.0927"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
</vector>

View File

@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportHeight="108"
android:viewportWidth="108">
<path
android:fillColor="#26A69A"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<WebView
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#FFFFFF</color>
</resources>

View File

@ -0,0 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<string name="app_name">set-vue</string>
<string name="title_activity_main">set-vue</string>
<string name="package_name">com.gateway.com</string>
<string name="custom_url_scheme">com.gateway.com</string>
</resources>

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="AppTheme.NoActionBar" parent="Theme.AppCompat.DayNight.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="android:background">@null</item>
</style>
<style name="AppTheme.NoActionBarLaunch" parent="Theme.SplashScreen">
<item name="android:background">@drawable/splash</item>
</style>
</resources>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="my_images" path="." />
<cache-path name="my_cache_images" path="." />
</paths>

View File

@ -0,0 +1,18 @@
package com.getcapacitor.myapp;
import static org.junit.Assert.*;
import org.junit.Test;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}

29
android/build.gradle Normal file
View File

@ -0,0 +1,29 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:8.7.2'
classpath 'com.google.gms:google-services:4.4.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
apply from: "variables.gradle"
allprojects {
repositories {
google()
mavenCentral()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}

View File

@ -0,0 +1,3 @@
// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN
include ':capacitor-android'
project(':capacitor-android').projectDir = new File('../node_modules/@capacitor/android/capacitor')

22
android/gradle.properties Normal file
View File

@ -0,0 +1,22 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app's APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true

Binary file not shown.

View File

@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

252
android/gradlew vendored Normal file
View File

@ -0,0 +1,252 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

94
android/gradlew.bat vendored Normal file
View File

@ -0,0 +1,94 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

5
android/settings.gradle Normal file
View File

@ -0,0 +1,5 @@
include ':app'
include ':capacitor-cordova-android-plugins'
project(':capacitor-cordova-android-plugins').projectDir = new File('./capacitor-cordova-android-plugins/')
apply from: 'capacitor.settings.gradle'

16
android/variables.gradle Normal file
View File

@ -0,0 +1,16 @@
ext {
minSdkVersion = 23
compileSdkVersion = 35
targetSdkVersion = 35
androidxActivityVersion = '1.9.2'
androidxAppCompatVersion = '1.7.0'
androidxCoordinatorLayoutVersion = '1.2.0'
androidxCoreVersion = '1.15.0'
androidxFragmentVersion = '1.8.4'
coreSplashScreenVersion = '1.0.1'
androidxWebkitVersion = '1.12.1'
junitVersion = '4.13.2'
androidxJunitVersion = '1.2.1'
androidxEspressoCoreVersion = '3.6.1'
cordovaAndroidVersion = '10.1.1'
}

5
capacitor.config.json Normal file
View File

@ -0,0 +1,5 @@
{
"appId": "com.gateway.com",
"appName": "set-vue",
"webDir": "dist"
}

1291
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
{ {
"name": "set-vue", "name": "GateWay",
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"scripts": { "scripts": {
@ -8,12 +8,19 @@
"lint": "vue-cli-service lint" "lint": "vue-cli-service lint"
}, },
"dependencies": { "dependencies": {
"@capacitor/android": "^7.0.1",
"@capacitor/cli": "^7.0.1",
"@capacitor/core": "^7.0.1",
"@vant/touch-emulator": "^1.4.0", "@vant/touch-emulator": "^1.4.0",
"amfe-flexible": "^2.2.1", "amfe-flexible": "^2.2.1",
"autoprefixer": "^10.4.19", "autoprefixer": "^10.4.19",
"axios": "^1.7.2", "axios": "^1.7.2",
"core-js": "^3.8.3", "core-js": "^3.8.3",
"element-plus": "^2.9.4",
"express": "^4.21.2",
"lamejs": "^1.2.1",
"mqtt": "^2.18.8", "mqtt": "^2.18.8",
"nipplejs": "^0.10.2",
"vant": "^4.9.5", "vant": "^4.9.5",
"vconsole": "^3.15.1", "vconsole": "^3.15.1",
"vue": "^3.2.13", "vue": "^3.2.13",
@ -30,7 +37,7 @@
"@vue/cli-plugin-router": "~5.0.0", "@vue/cli-plugin-router": "~5.0.0",
"@vue/cli-plugin-vuex": "~5.0.0", "@vue/cli-plugin-vuex": "~5.0.0",
"@vue/cli-service": "~5.0.0", "@vue/cli-service": "~5.0.0",
"code-inspector-plugin": "^0.17.3", "code-inspector-plugin": "^0.17.9",
"compression-webpack-plugin": "^11.1.0", "compression-webpack-plugin": "^11.1.0",
"eslint": "^7.32.0", "eslint": "^7.32.0",
"eslint-plugin-vue": "^8.0.3", "eslint-plugin-vue": "^8.0.3",
@ -39,6 +46,6 @@
"less-loader": "^12.2.0", "less-loader": "^12.2.0",
"postcss-pxtorem": "^5.1.1", "postcss-pxtorem": "^5.1.1",
"unplugin-auto-import": "^0.17.8", "unplugin-auto-import": "^0.17.8",
"unplugin-vue-components": "^0.27.4" "unplugin-vue-components": "^0.27.5"
} }
} }

View File

@ -0,0 +1,24 @@
// audio-worklet-processor.js
class AudioProcessor extends AudioWorkletProcessor {
constructor() {
super();
}
process(inputs, outputs, parameters) {
const input = inputs[0]; // 获取音频输入数据
const output = outputs[0]; // 音频输出数据
// 获取第一个声道的音频数据
const inputData = input[0];
// 发送音频数据到主线程
this.port.postMessage(inputData);
// 直接把音频数据传递到输出
output[0].set(inputData);
return true; // 保持音频流
}
}
registerProcessor('audio-processor', AudioProcessor);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 463 B

12
server.js Normal file
View File

@ -0,0 +1,12 @@
const express = require('express');
const path = require('path');
const app = express();
const PORT = 3000; // 您可以根据需要更改端口号
// 设置静态文件目录
app.use('/files', express.static(path.join(__dirname, 'files')));
// 启动服务器
app.listen(PORT, () => {
console.log(`文件服务正在运行在 http://localhost:${PORT}`);
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
src/assets/img/bat_full.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

BIN
src/assets/img/bat_high.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

BIN
src/assets/img/bat_low.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

BIN
src/assets/img/boom.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
src/assets/img/car.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

BIN
src/assets/img/connect.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
src/assets/img/down.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
src/assets/img/fire.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
src/assets/img/mp3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
src/assets/img/obstacle.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

BIN
src/assets/img/police.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

BIN
src/assets/img/rc_car.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
src/assets/img/refresh.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

BIN
src/assets/img/rescue.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
src/assets/img/sense.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

BIN
src/assets/img/shout.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
src/assets/img/stop.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
src/assets/img/up.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

BIN
src/assets/img/voice.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

BIN
src/assets/img/welcome.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
src/assets/img/wifi.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,84 @@
class GamepadController {
constructor(config = {}) {
this.config = {
buttonMapping: {},
axisMapping: {},
...config
};
this.gamepad = null;
this.listeners = {};
// 初始化默认按键映射
Object.assign(this.config.buttonMapping, {
0: 'A',
1: 'B',
2: 'X',
3: 'Y',
// 其他默认映射
});
// 初始化默认摇杆映射
Object.assign(this.config.axisMapping, {
0: 'leftX',
1: 'leftY',
2: 'rightX',
3: 'rightY',
// 其他默认映射
});
}
connect() {
window.addEventListener('gamepadconnected', (e) => {
this.gamepad = navigator.getGamepads()[e.gamepad.index];
this.triggerEvent('connected');
});
}
disconnect() {
window.removeEventListener('gamepadconnected', this.handleConnect);
this.gamepad = null;
this.triggerEvent('disconnected');
}
// 手动获取手柄数据的方法
pollGamepad() {
if (!this.gamepad) return;
// 处理摇杆数据
const axesData = this.gamepad.axes;
const axisEvents = {};
Object.entries(this.config.axisMapping).forEach(([index, name]) => {
axisEvents[name] = axesData[index];
});
this.triggerEvent('axisChange', axisEvents);
// 处理按键事件
const buttons = this.gamepad.buttons;
buttons.forEach((btn, index) => {
if (btn.value === 1 && this.config.buttonMapping[index]) {
const keyName = this.config.buttonMapping[index];
this.triggerEvent('buttonPress', { keyName, index });
}
});
}
addEventListener(type, callback) {
if (!this.listeners[type]) this.listeners[type] = [];
this.listeners[type].push(callback);
}
triggerEvent(type, data = {}) {
if (this.listeners[type]) {
this.listeners[type].forEach(callback => callback(data));
}
}
}
// 导出模块
if (typeof module !== 'undefined' && module.exports) {
module.exports = GamepadController;
} else if (typeof define === 'function' && define.amd) {
define([], () => GamepadController);
} else {
window.GamepadController = GamepadController;
}

View File

@ -2,7 +2,7 @@
<div class="footer"> <div class="footer">
<router-view></router-view> <router-view></router-view>
<van-tabbar v-model="active" @change="onChange"> <van-tabbar v-model="active" @change="onChange">
<van-tabbar-item icon="home-o" name="home">系统设置</van-tabbar-item> <van-tabbar-item icon="home-o" name="Home">系统设置</van-tabbar-item>
<van-tabbar-item icon="setting-o" name="info">系统信息</van-tabbar-item> <van-tabbar-item icon="setting-o" name="info">系统信息</van-tabbar-item>
</van-tabbar> </van-tabbar>
</div> </div>

104
src/components/rocker.vue Normal file
View File

@ -0,0 +1,104 @@
<template>
<div>
<div class="out_circle" ref="outCircleRef" :style="{ width: r * 2 + 'px', height: r * 2 + 'px' }">
<div class="inner_circle" ref="innerCircleRef" :style="{ width: inR * 2 + 'px', height: inR * 2 + 'px' }">
Press
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
const { r } = withDefaults(defineProps<{
r: number,
inR: number
}>(), {
r: 100,
inR: 30
})
let outCircleRef = ref<HTMLElement>()
let innerCircleRef = ref<HTMLElement>()
let mouseAngle = ref<number>(0)
let mouseAngleComp = computed(() => {
return mouseAngle.value.toFixed(2) + '°'
})
defineExpose({ mouseAngleComp })
function mouseDown(e: MouseEvent) {
document.body.addEventListener('mousemove', mouseMove)
}
function mouseMove(e: MouseEvent) {
if (outCircleRef.value && innerCircleRef.value) {
let centerX = outCircleRef.value.offsetLeft + r
let centerY = outCircleRef.value.offsetTop + r
let disToCenter = distance(centerX, centerY, e.clientX, e.clientY)
let ratioX = 0
let ratioY = 0
if (disToCenter <= r) {
ratioX = (e.clientX - outCircleRef.value.offsetLeft) / (r * 2) * 100
ratioY = (e.clientY - outCircleRef.value.offsetTop) / (r * 2) * 100
} else {
let copyX = e.clientX
let copyY = e.clientY
while (disToCenter > r) {
copyX += copyX > centerX ? -2 : 2
copyY += copyY > centerY ? -2 : 2
disToCenter = distance(centerX, centerY, copyX, copyY)
}
ratioX = (copyX - outCircleRef.value.offsetLeft) / (r * 2) * 100
ratioY = (copyY - outCircleRef.value.offsetTop) / (r * 2) * 100
}
innerCircleRef.value.style.left = ratioX + '%'
innerCircleRef.value.style.top = ratioY + '%'
mouseAngle.value = (Math.atan2(ratioX, ratioY) * 180) % 360
}
}
function mouseUp(e: MouseEvent) {
document.body.removeEventListener('mousemove', mouseMove)
innerCircleRef.value!.style.left = '50%'
innerCircleRef.value!.style.top = '50%'
mouseAngle.value = 0
}
function distance(x0: number, y0: number, x1: number, y1: number) {
return Math.sqrt((x1 - x0) ** 2 + (y1 - y0) ** 2)
}
onMounted(() => {
innerCircleRef.value?.addEventListener('mousedown', mouseDown)
document.body.addEventListener('mouseup', mouseUp)
})
</script>
<style scoped>
.out_circle {
border-radius: 50%;
background-color: #ddd;
position: relative;
margin-top: 8px;
.inner_circle {
border-radius: 50%;
background-color: #999;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
font-weight: bold;
user-select: none;
&:active {
background: #666;
}
}
}
</style>

View File

@ -1,5 +1,8 @@
import { createRouter, createWebHashHistory } from 'vue-router'; import { createRouter, createWebHashHistory } from 'vue-router';
import { useStore } from 'vuex'; import { useStore } from 'vuex';
import Home from '../views/home/home.vue';
import CarControl from '../views/CarControl.vue'; // 导入小车控制页面
const routes = [ const routes = [
{ {
path: '/login', path: '/login',
@ -15,9 +18,10 @@ const routes = [
}, },
{ {
path: '/', path: '/',
name: 'home', name: 'Home',
component: () => import('../views/home/home.vue') component: Home,
}, },
{ {
path: '/recognition', path: '/recognition',
name: 'recognition', name: 'recognition',
@ -43,6 +47,21 @@ const routes = [
name: 'voiceset', name: 'voiceset',
component: () => import('../views/voice/voiceset.vue') component: () => import('../views/voice/voiceset.vue')
}, },
{
path: '/car_control', // 添加小车控制的路由
name: 'CarControl',
component: () => import('../views/CarControl.vue')
},
{
path: '/audio-play',
name: 'AudioPlay',
component: () => import('../views/AudioPlay.vue'),
},
{
path: '/TEST',
name: 'TEST',
component: () => import('../views/TEST.vue'),
},
]; ];
const router = createRouter({ const router = createRouter({

92
src/views/AudioPlay.vue Normal file
View File

@ -0,0 +1,92 @@
<template>
<div>
<h1>音频实时传输</h1>
<button @click="toggleRecording">
{{ isRecording ? '停止传输音频' : '开始传输音频' }}
</button>
<p>{{ status }}</p>
</div>
</template>
<script>
export default {
data() {
return {
ws: null, // WebSocket
mediaRecorder: null, // MediaRecorder
audioChunks: [], //
status: '点击按钮开始传输音频',
isRecording: false //
};
},
mounted() {
// WebSocket
this.ws = new WebSocket('ws://192.168.1.60:81'); // ESP32 IP
this.ws.onopen = () => {
console.log('WebSocket连接已打开');
};
this.ws.onmessage = (event) => {
console.log('接收到来自ESP32的消息:', event.data);
};
this.ws.onclose = () => {
console.log('WebSocket连接已关闭');
};
},
methods: {
toggleRecording() {
if (this.isRecording) {
//
this.stopRecording();
} else {
//
this.startRecording();
}
},
startRecording() {
this.status = '开始录音并传输音频...';
this.audioChunks = [];
this.isRecording = true;
navigator.mediaDevices.getUserMedia({ audio: true })
.then((stream) => {
this.mediaRecorder = new MediaRecorder(stream);
this.mediaRecorder.ondataavailable = (event) => {
this.audioChunks.push(event.data);
//
const audioBlob = new Blob([event.data], { type: 'audio/wav' });
audioBlob.arrayBuffer().then((arrayBuffer) => {
//
console.log('实时音频数据:', new Uint8Array(arrayBuffer));
// WebSocket
if (this.ws.readyState === WebSocket.OPEN) {
this.ws.send(arrayBuffer);
}
});
};
this.mediaRecorder.start(100); // 100ms
})
.catch((err) => {
console.error('无法访问麦克风:', err);
});
},
stopRecording() {
this.status = '停止录音';
this.isRecording = false;
if (this.mediaRecorder && this.mediaRecorder.state !== 'inactive') {
this.mediaRecorder.stop(); //
}
}
}
};
</script>
<style scoped>
button {
padding: 10px 20px;
font-size: 16px;
cursor: pointer;
}
</style>

240
src/views/CarControl.vue Normal file
View File

@ -0,0 +1,240 @@
<template>
<div class="control-panel">
<!-- 顶部状态栏 -->
<div class="status-bar">
<div class="placeholder-div"></div>
<div class="title-container">
<div class="title-box">
遥控车当前状态
<img src="../assets/img/shout.png" alt="voice" class="voice-icon-img" style="width: 20px; height: 20px;margin-left: 20px;">
</div>
</div>
<div class="top-right-icons">
<img src="../assets/img/connect.png" alt="link" class="top-icon">
<img src="../assets/img/bat_empty.png" alt="mp3" class="top-icon">
</div>
</div>
<!-- 主要内容区域 -->
<div class="main-content">
<!-- 左侧控制开关 -->
<div class="left-controls">
<div class="switch-item">
<span>屏幕</span>
<el-switch v-model="screenStatus" />
</div>
<div class="switch-item">
<span>警灯</span>
<el-switch v-model="warningLightStatus" />
</div>
<div class="switch-item">
<span>跟随</span>
<el-switch v-model="followStatus" />
</div>
<div class="switch-item">
<span>避障</span>
<el-switch v-model="obstacleStatus" />
</div>
</div>
<!-- 中间车辆状态显示区域 -->
<div class="center-display">
<div class="status-text">云台状态</div>
<div class="car-display">
<img src="../assets/img/car.png" alt="car" class="car-image" />
</div>
</div>
<!-- 右侧状态图标 -->
<div class="right-status">
<div class="status-icons">
<div class="icons-row">
<div class="icon-item">
<img src="../assets/img/refresh.png" alt="refresh" style="width: 40px; height: 40px;">
</div>
<div class="icon-item">
<img src="../assets/img/mp3.png" alt="play" style="width: 40px; height: 40px;">
</div>
</div>
</div>
<div class="control-button">
<img src="../assets/img/stop.png" alt="control" class="control-button-img" style="width: 80px; height: 80px;">
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'CarControl',
data() {
return {
screenStatus: false,
warningLightStatus: false,
followStatus: false,
obstacleStatus: false
}
}
}
</script>
<style scoped>
.control-panel {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: #000033;
color: #fff;
overflow: hidden;
display: flex;
flex-direction: column;
}
.status-bar {
height: 60px;
background-color: #000033;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 20px;
position: relative;
}
.placeholder-div {
width: 63px;
}
.title-container {
position: absolute;
left: 50%;
transform: translateX(-50%);
font-size: 24px;
display: flex;
align-items: center;
gap: 10px;
}
.title-box {
border: 2px solid #00ffff;
padding: 5px 15px;
border-radius: 8px;
}
.main-content {
flex: 1;
display: flex;
padding: 20px;
gap: 20px;
}
.left-controls {
width: 120px;
display: flex;
flex-direction: column;
gap: 15px;
}
.switch-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px;
border: 1px solid #00ffff;
border-radius: 8px;
font-size: 20px;
}
.center-display {
flex: 1;
border: 1px solid #00ffff;
border-radius: 8px;
padding: 20px;
display: flex;
flex-direction: column;
align-items: flex-start;
}
.status-text {
font-size: 20px;
color: #00ffff;
align-self: flex-start;
margin-bottom: 10px;
}
.car-display {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
width: 100%;
}
.car-image {
width: 160px;
height: auto;
}
.right-status {
width: 150px;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
}
.status-icons {
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
margin-top: 50px;
}
.icons-row {
display: flex;
flex-direction: row;
gap: 15px;
justify-content: center;
}
.icon-item {
width: 40px;
height: 40px;
/* border: 1px solid #00ffff; */
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
color: #00ffff;
}
.battery {
background-color: #00ffff;
color: #000033;
}
:deep(.el-switch__core) {
border-color: #00ffff;
background-color: transparent;
}
:deep(.el-switch.is-checked .el-switch__core) {
background-color: #00ffff;
}
.top-right-icons {
display: flex;
gap: 15px;
align-items: center;
width: 63px;
}
.top-icon {
width: 24px;
height: 24px;
cursor: pointer;
}
</style>

215
src/views/TEST.vue Normal file
View File

@ -0,0 +1,215 @@
<template>
<div class="home">
<div class="gamepad-container">
<div class="gamepad-info">
<div class="data-card axes-card">
<h3>遥感数据</h3>
<div class="data-display">
<span class="axis-label">X :</span>
<span class="axis-value">{{ directionAxis0_1 }}</span>
</div>
<div class="data-display">
<span class="axis-label">Y :</span>
<span class="axis-value">{{ directionAxis0_1 }}</span>
</div>
</div>
<div class="data-card buttons-card">
<h3>按键状态</h3>
<div class="button-group">
<div
v-for="(button, index) in buttons"
:key="index"
class="button-item"
:class="{ 'pressed': button.pressed }"
>
{{ button.name }}
</div>
</div>
</div>
<div class="data-card axis9-card">
<h3>按钮数据Axis9</h3>
<div class="data-display">
<span class="axis-label">方向:</span>
<span class="axis-value">{{ directionAxis9 }}</span>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "Home",
data() {
return {
interval: null,
gamepad: null,
buttons: [
{ name: "Left2", pressed: false },
{ name: "Back", pressed: false },
{ name: "Right Joystick Press", pressed: false }
],
directionAxis0_1: "",
directionAxis9: ""
};
},
created() {
window.addEventListener("gamepadconnected", this.onGamepadConnected);
window.addEventListener("gamepaddisconnected", this.onGamepadDisconnected);
},
beforeDestroy() {
window.removeEventListener("gamepadconnected", this.onGamepadConnected);
window.removeEventListener("gamepaddisconnected", this.onGamepadDisconnected);
},
methods: {
onGamepadConnected(e) {
console.log("Gamepad connected:", e.gamepad);
this.gamepad = navigator.getGamepads()[e.gamepad.index];
this.startGamepad();
},
onGamepadDisconnected() {
clearInterval(this.interval);
this.gamepad = null;
},
startGamepad() {
this.interval = setInterval(() => {
const gamepad = navigator.getGamepads()[0];
if (gamepad) {
this.updateDirection(gamepad.axes);
this.updateDirectionAxis9(gamepad.axes);
this.pressKey(gamepad.buttons);
}
}, 50);
},
updateDirection(axes) {
const axis0 = axes[0];
const axis1 = axes[1];
if (axis0 >= -0.3 && axis0 <= 0.3 && axis1 <= -0.5) {
this.directionAxis0_1 = "上";
} else if (axis0 >= -0.3 && axis0 <= 0.3 && axis1 >= 0.5) {
this.directionAxis0_1 = "下";
} else if (axis1 >= -0.3 && axis1 <= 0.3 && axis0 <= -0.3) {
this.directionAxis0_1 = "左";
} else if (axis1 >= -0.3 && axis1 <= 0.3 && axis0 >= 0.3) {
this.directionAxis0_1 = "右";
} else {
this.directionAxis0_1 = "未定义";
}
},
updateDirectionAxis9(axes) {
const axis9 = axes[9];
const roundedAxis9 = Math.round(axis9 * 100) / 100;
if (roundedAxis9 <= -0.9) {
this.directionAxis9 = "上";
} else if (roundedAxis9 >= 0.0 && roundedAxis9 <= 0.2) {
this.directionAxis9 = "下";
} else if (roundedAxis9 >= 0.6 && roundedAxis9 <= 0.8) {
this.directionAxis9 = "左";
} else if (roundedAxis9 >= -0.5 && roundedAxis9 <= -0.4) {
this.directionAxis9 = "右";
} else {
this.directionAxis9 = "未定义";
}
},
pressKey(buttons) {
this.buttons = [
{ name: "Left2", pressed: buttons[6].value === 1 },
{ name: "Back", pressed: buttons[8].value === 1 },
{ name: "Right Joystick Press", pressed: buttons[11].value === 1 }
];
}
}
};
</script>
<style scoped>
.home {
position: fixed;
width: 100%;
height: 100%;
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
display: flex;
justify-content: center;
align-items: center;
}
.gamepad-container {
width: 450px;
padding: 2rem;
border-radius: 15px;
background: rgba(255, 255, 255, 0.9);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
}
.gamepad-info {
display: flex;
flex-direction: column;
gap: 2rem;
}
.data-card {
padding: 1.5rem;
border-radius: 12px;
background: rgba(255, 255, 255, 0.8);
}
h3 {
font-size: 1.4rem;
color: #2c3e50;
margin-bottom: 1rem;
}
.data-display {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.8rem;
}
.axis-label {
color: #666;
font-size: 1rem;
}
.axis-value {
color: #34495e;
font-size: 1.1rem;
font-weight: bold;
}
.buttons-card {
padding: 1rem;
}
.button-group {
display: flex;
flex-direction: column;
gap: 0.8rem;
}
.button-item {
padding: 0.8rem;
border-radius: 8px;
background: rgba(255, 255, 255, 0.9);
color: #666;
font-size: 1rem;
cursor: pointer;
transition: all 0.2s ease;
}
.button-item:hover {
background: rgba(46, 204, 113, 0.1);
}
.pressed {
background: #2ecc71;
color: white !important;
}
.pressed:hover {
background: #27ae60;
}
</style>

View File

@ -87,7 +87,7 @@
<script> <script>
import { onMounted, ref } from 'vue'; import { onMounted, ref } from 'vue';
import { showToast } from 'vant'; // import { showToast } from 'vant';
import axios from 'axios'; import axios from 'axios';
import { connectMQTT, client } from "../../components/MQTT/mqttclient"; import { connectMQTT, client } from "../../components/MQTT/mqttclient";

View File

@ -16,7 +16,7 @@
<van-cell size="large" class="custom-cell" title="车牌识别" icon="search" is-link value="识别" @click="goto('recognition')" /> <van-cell size="large" class="custom-cell" title="车牌识别" icon="search" is-link value="识别" @click="goto('recognition')" />
<van-cell size="large" class="custom-cell" title="预警设置" icon="warning-o" is-link value="预警" @click="goto('warning')" /> <van-cell size="large" class="custom-cell" title="预警设置" icon="warning-o" is-link value="预警" @click="goto('warning')" />
<van-cell size="large" class="custom-cell" title="远程喊话" icon="bullhorn-o" is-link value="喊话" @click="navigateTo('web/voice_copy.html')" /> <van-cell size="large" class="custom-cell" title="远程喊话" icon="bullhorn-o" is-link value="喊话" @click="navigateTo('web/voice_copy.html')" />
<van-cell size="large" class="custom-cell" title="小车控制" icon="car" is-link value="控制" @click="goto('AudioPlay')" />
</van-cell-group> </van-cell-group>
</div> </div>
</div> </div>

View File

@ -2,6 +2,9 @@ const { defineConfig } = require('@vue/cli-service');
const { VantResolver } = require('@vant/auto-import-resolver'); const { VantResolver } = require('@vant/auto-import-resolver');
const AutoImport = require('unplugin-auto-import/webpack'); const AutoImport = require('unplugin-auto-import/webpack');
const Components = require('unplugin-vue-components/webpack'); const Components = require('unplugin-vue-components/webpack');
const { codeInspectorPlugin } = require('code-inspector-plugin');
const { ElementPlusResolver } = require('unplugin-vue-components/resolvers')
module.exports = defineConfig({ module.exports = defineConfig({
lintOnSave: false, lintOnSave: false,
productionSourceMap: false, productionSourceMap: false,
@ -23,7 +26,13 @@ module.exports = defineConfig({
open: true, open: true,
hot: true,//自动保存 hot: true,//自动保存
}, },
chainWebpack: (config) => {
config.plugin('code-inspector-plugin').use(
codeInspectorPlugin({
bundler: 'webpack',
})
);
},
configureWebpack: { configureWebpack: {
plugins: [ plugins: [
// 当 unplugin-vue-components 版本小于 0.26.0 时,使用以下写法 // 当 unplugin-vue-components 版本小于 0.26.0 时,使用以下写法
@ -31,9 +40,10 @@ module.exports = defineConfig({
// Components({ resolvers: [VantResolver()] }), // Components({ resolvers: [VantResolver()] }),
//当大于等于 0.26.0 时,使用以下写法 //当大于等于 0.26.0 时,使用以下写法
AutoImport.default({ AutoImport.default({
resolvers: [VantResolver()], resolvers: [VantResolver(),ElementPlusResolver()],
}), }),
Components.default({ resolvers: [VantResolver()] }), Components.default({ resolvers: [VantResolver(),ElementPlusResolver()] }),
// new CompressionPlugin({ // new CompressionPlugin({
// algorithm: 'gzip', // 使用gzip压缩 // algorithm: 'gzip', // 使用gzip压缩
// test: /\.js$|\.html$|\.css$/, // 匹配文件名 // test: /\.js$|\.html$|\.css$/, // 匹配文件名