├── Aurage ├── __init__.py ├── asgi.py ├── settings.py ├── urls.py └── wsgi.py ├── Client.py ├── Monitor ├── __init__.py ├── admin.py ├── apps.py ├── authentication.py ├── consumers │ ├── __init__.py │ └── index.py ├── models │ ├── Device │ │ ├── Device.py │ │ └── __init__.py │ ├── Mode │ │ ├── Mode.py │ │ └── __init__.py │ ├── ModeDevice │ │ ├── ModeDevice.py │ │ └── __init__.py │ ├── Plan │ │ ├── Plan.py │ │ └── __init__.py │ ├── PlanMode │ │ ├── PlanMode.py │ │ └── __init__.py │ ├── PlanTime │ │ ├── PlanTime.py │ │ └── __init__.py │ ├── User │ │ ├── User.py │ │ └── __init__.py │ ├── UserPlan │ │ ├── UserPlan.py │ │ └── __init__.py │ └── __init__.py ├── routing.py ├── templates │ └── web.html ├── tests.py ├── urls │ ├── __init__.py │ ├── index.py │ ├── web │ │ ├── __init__.py │ │ └── index.py │ └── wx │ │ ├── Login │ │ ├── __init__.py │ │ └── index.py │ │ ├── Plan │ │ ├── __init__.py │ │ └── index.py │ │ ├── __init__.py │ │ └── index.py ├── utils.py └── views │ ├── __init__.py │ ├── index.py │ ├── web │ └── __init__.py │ └── wx │ ├── Login │ ├── UserProfileView.py │ ├── __init__.py │ ├── getPhoneNumber.py │ ├── getUid.py │ └── savePhoneNumber.py │ ├── Mode │ └── __init__.py │ ├── Plan │ ├── AddPlanInfoWithID.py │ ├── CancelAct.py │ ├── PlanAct.py │ ├── SaveAutoRecords.py │ ├── __init__.py │ ├── getAutoTimeWithPlanID.py │ ├── getModelInfoWithPlanID.py │ └── getPlanInfoWithID.py │ └── __init__.py ├── README.md ├── db.sqlite3 ├── django.docx ├── img ├── 1.png ├── 2.1.png ├── 2.png ├── 3.png ├── 电气柜.jpg └── 示意图.jpg ├── manage.py ├── requirements.txt ├── stringPLC.smart ├── 微信小程序部分 ├── .vscode │ └── launch.json ├── miniprogram │ ├── app.json │ ├── app.ts │ ├── app.wxss │ ├── images │ │ ├── default.zip │ │ ├── tab │ │ │ ├── device.png │ │ │ ├── device_active.png │ │ │ ├── home.png │ │ │ ├── home_active.png │ │ │ ├── user.png │ │ │ └── user_active.png │ │ ├── w │ │ │ ├── default.png │ │ │ └── gg.png │ │ └── 二维.jpg │ ├── pages │ │ ├── device │ │ │ ├── device.json │ │ │ ├── device.ts │ │ │ ├── device.wxml │ │ │ ├── device.wxss │ │ │ └── devicexijie │ │ │ │ ├── auto │ │ │ │ ├── auto.json │ │ │ │ ├── auto.ts │ │ │ │ ├── auto.wxml │ │ │ │ └── auto.wxss │ │ │ │ ├── personality │ │ │ │ ├── personality.json │ │ │ │ ├── personality.ts │ │ │ │ ├── personality.wxml │ │ │ │ └── personality.wxss │ │ │ │ ├── xijie.json │ │ │ │ ├── xijie.ts │ │ │ │ ├── xijie.wxml │ │ │ │ └── xijie.wxss │ │ ├── index │ │ │ ├── index.json │ │ │ ├── index.ts │ │ │ ├── index.wxml │ │ │ └── index.wxss │ │ ├── login │ │ │ ├── login.json │ │ │ ├── login.ts │ │ │ ├── login.wxml │ │ │ └── login.wxss │ │ └── user │ │ │ ├── user.json │ │ │ ├── user.ts │ │ │ ├── user.wxml │ │ │ └── user.wxss │ └── utils │ │ └── util.ts ├── node_modules │ ├── .package-lock.json │ └── miniprogram-api-typings │ │ ├── CHANGELOG.md │ │ ├── LICENSE │ │ ├── README-en.md │ │ ├── README.md │ │ ├── VERSIONS.md │ │ ├── index.d.ts │ │ ├── package.json │ │ ├── types │ │ ├── index.d.ts │ │ └── wx │ │ │ ├── index.d.ts │ │ │ ├── lib.wx.api.d.ts │ │ │ ├── lib.wx.app.d.ts │ │ │ ├── lib.wx.behavior.d.ts │ │ │ ├── lib.wx.cloud.d.ts │ │ │ ├── lib.wx.component.d.ts │ │ │ ├── lib.wx.event.d.ts │ │ │ └── lib.wx.page.d.ts │ │ └── typings.json ├── package-lock.json ├── package.json ├── project.config.json ├── project.private.config.json ├── tsconfig.json └── typings │ ├── index.d.ts │ └── types │ ├── index.d.ts │ └── wx │ ├── index.d.ts │ ├── lib.wx.api.d.ts │ ├── lib.wx.app.d.ts │ ├── lib.wx.behavior.d.ts │ ├── lib.wx.cloud.d.ts │ ├── lib.wx.component.d.ts │ ├── lib.wx.event.d.ts │ └── lib.wx.page.d.ts ├── 数据接口.xls └── 水泵阀门远程控制柜.pdf /Aurage/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringzc/RemoteControl/4c37ab43627223f755d76674f30fce91f6ec120b/Aurage/__init__.py -------------------------------------------------------------------------------- /Aurage/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for Aurage project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/5.1/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | import django 13 | from channels.auth import AuthMiddlewareStack 14 | from channels.layers import get_channel_layer 15 | from channels.routing import ProtocolTypeRouter, URLRouter 16 | from django.core.asgi import get_asgi_application 17 | 18 | from Monitor.routing import websocket_urlpatterns 19 | 20 | django.setup() 21 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "Aurage.settings") 22 | channel_layer = get_channel_layer() 23 | 24 | application = ProtocolTypeRouter({ 25 | "http": get_asgi_application(), 26 | "websocket": AuthMiddlewareStack( 27 | URLRouter( 28 | websocket_urlpatterns 29 | ) 30 | ), 31 | }) 32 | -------------------------------------------------------------------------------- /Aurage/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for Aurage project. 3 | 4 | Generated by 'django-admin startproject' using Django 5.1.6. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/5.1/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/5.1/ref/settings/ 11 | """ 12 | import os.path 13 | from pathlib import Path 14 | 15 | # Build paths inside the project like this: BASE_DIR / 'subdir'. 16 | BASE_DIR = Path(__file__).resolve().parent.parent 17 | 18 | # Quick-start development settings - unsuitable for production 19 | # See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/ 20 | 21 | # SECURITY WARNING: keep the secret key used in production secret! 22 | SECRET_KEY = "django-insecure-z5*wir+!bc22o*5))ai%y#*2bnb&0$2*kss@-!og38i$fkf2g7" 23 | 24 | # SECURITY WARNING: don't run with debug turned on in production! 25 | DEBUG = True 26 | 27 | ALLOWED_HOSTS = ['0.0.0.0', 'localhost'] 28 | 29 | # Application definition 30 | 31 | INSTALLED_APPS = [ 32 | "rest_framework", 33 | "Monitor.apps.MonitorConfig", 34 | 'channels', 35 | "django.contrib.admin", 36 | "django.contrib.auth", 37 | "django.contrib.contenttypes", 38 | "django.contrib.sessions", 39 | "django.contrib.messages", 40 | "django.contrib.staticfiles", 41 | ] 42 | 43 | MIDDLEWARE = [ 44 | "django.middleware.security.SecurityMiddleware", 45 | "django.contrib.sessions.middleware.SessionMiddleware", 46 | "django.middleware.common.CommonMiddleware", 47 | # "django.middleware.csrf.CsrfViewMiddleware", 48 | "django.contrib.auth.middleware.AuthenticationMiddleware", 49 | "django.contrib.messages.middleware.MessageMiddleware", 50 | "django.middleware.clickjacking.XFrameOptionsMiddleware", 51 | ] 52 | 53 | ROOT_URLCONF = "Aurage.urls" 54 | 55 | TEMPLATES = [ 56 | { 57 | "BACKEND": "django.template.backends.django.DjangoTemplates", 58 | "DIRS": [BASE_DIR / 'templates'] 59 | , 60 | "APP_DIRS": True, 61 | "OPTIONS": { 62 | "context_processors": [ 63 | "django.template.context_processors.debug", 64 | "django.template.context_processors.request", 65 | "django.contrib.auth.context_processors.auth", 66 | "django.contrib.messages.context_processors.messages", 67 | ], 68 | }, 69 | }, 70 | ] 71 | 72 | WSGI_APPLICATION = "Aurage.wsgi.application" 73 | ASGI_APPLICATION = "Aurage.asgi.application" 74 | 75 | # Database 76 | # https://docs.djangoproject.com/en/5.1/ref/settings/#databases 77 | 78 | DATABASES = { 79 | "default": { 80 | "ENGINE": "django.db.backends.sqlite3", 81 | "NAME": BASE_DIR / "db.sqlite3", 82 | } 83 | } 84 | 85 | # Password validation 86 | # https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators 87 | 88 | AUTH_PASSWORD_VALIDATORS = [ 89 | { 90 | "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", 91 | }, 92 | {"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", }, 93 | {"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", }, 94 | {"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", }, 95 | ] 96 | 97 | # Internationalization 98 | # https://docs.djangoproject.com/en/5.1/topics/i18n/ 99 | 100 | LANGUAGE_CODE = "en-us" 101 | 102 | TIME_ZONE = "Asia/Shanghai" 103 | 104 | USE_I18N = True 105 | 106 | USE_TZ = True 107 | 108 | # Static files (CSS, JavaScript, Images) 109 | # https://docs.djangoproject.com/en/5.1/howto/static-files/ 110 | 111 | STATIC_ROOT = os.path.join(BASE_DIR, 'Monitor/static') 112 | STATIC_URL = "/static/" 113 | # Default primary key field type 114 | # https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field 115 | 116 | MEDIA_ROOT = os.path.join(BASE_DIR, 'media') 117 | MEDIA_URL = "/media/" 118 | 119 | DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" 120 | 121 | # 自定义Token配置 122 | TOKEN_EXPIRE_DAYS = 2 123 | WX_APPID = 'wx1e4cca245d369e87' 124 | WX_SECRET = '484ac4e9d41d2a4a7ebee02438f6e027' 125 | # # wx1e4cca245d369e87 126 | # # 484ac4e9d41d2a4a7ebee02438f6e027 127 | REST_FRAMEWORK = { 128 | 'DEFAULT_AUTHENTICATION_CLASSES': [ 129 | 'Monitor.authentication.TokenAuth' 130 | ] 131 | } 132 | 133 | # settings.py 134 | FERNET_KEY = 'Rxt7ML7JriiCXEd2qKKekqUt8YSztX-F5knPXAOyQrg=' # 粘贴到这里(确保安全) 135 | 136 | CHANNEL_LAYERS = { 137 | "default": { 138 | "BACKEND": "channels_redis.core.RedisChannelLayer", 139 | "CONFIG": { 140 | "hosts": [("127.0.0.1", 6379)], 141 | }, 142 | }, 143 | } 144 | CACHES = { 145 | 'default': { 146 | 'BACKEND': 'django_redis.cache.RedisCache', 147 | 'LOCATION': 'redis://127.0.0.1:6379/1', 148 | "OPTIONS": { 149 | "CLIENT_CLASS": "django_redis.client.DefaultClient", 150 | }, 151 | }, 152 | } 153 | USER_AGENTS_CACHE = 'default' 154 | -------------------------------------------------------------------------------- /Aurage/urls.py: -------------------------------------------------------------------------------- 1 | """ 2 | URL configuration for Aurage project. 3 | 4 | The `urlpatterns` list routes URLs to views. For more information please see: 5 | https://docs.djangoproject.com/en/5.1/topics/http/urls/ 6 | Examples: 7 | Function views 8 | 1. Add an import: from my_app import views 9 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 10 | Class-based views 11 | 1. Add an import: from other_app.views import Home 12 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 13 | Including another URLconf 14 | 1. Import the include() function: from django.urls import include, path 15 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 16 | """ 17 | from django.contrib import admin 18 | from django.urls import path, include 19 | 20 | urlpatterns = [ 21 | path("", include("Monitor.urls.index")), 22 | path("admin/", admin.site.urls) 23 | ] 24 | -------------------------------------------------------------------------------- /Aurage/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for Aurage project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/5.1/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "Aurage.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /Client.py: -------------------------------------------------------------------------------- 1 | 2 | import asyncio 3 | import json 4 | import logging 5 | import traceback 6 | import websockets 7 | from datetime import datetime 8 | from backoff import expo, on_exception 9 | 10 | # 配置参数 11 | DJANGO_WS_URL = "wss://www.aurage.cn/wss/ControlConsumer/raspberry_001/" 12 | RECONNECT_INTERVAL = 5 13 | MAX_RECONNECT_ATTEMPTS = 10 14 | PING_INTERVAL = 10 15 | PING_TIMEOUT = 15 16 | 17 | # 初始化日志 18 | logging.basicConfig( 19 | level=logging.INFO, 20 | format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' 21 | ) 22 | logger = logging.getLogger("RaspberryPi") 23 | 24 | 25 | class ConnectionManager: 26 | """异步安全连接管理器""" 27 | 28 | def __init__(self): 29 | self.websocket = None 30 | self._connected = asyncio.Event() 31 | self._lock = asyncio.Lock() 32 | self._is_manually_closed = False 33 | self.receive_queue = asyncio.Queue() 34 | self.send_queue = asyncio.Queue() 35 | 36 | @property 37 | def connected(self): 38 | return self._connected.is_set() 39 | 40 | async def maintain_connection(self): 41 | """连接保活主循环""" 42 | while not self._is_manually_closed: 43 | try: 44 | async with self._lock: 45 | if self.websocket: 46 | await self.websocket.close() 47 | 48 | self.websocket = await websockets.connect( 49 | DJANGO_WS_URL, 50 | ping_interval=PING_INTERVAL, 51 | ping_timeout=PING_TIMEOUT, 52 | close_timeout=1 53 | ) 54 | self._connected.set() 55 | logger.info("WebSocket连接成功") 56 | 57 | # 启动收发任务 58 | await asyncio.gather( 59 | self._keep_receiving(), 60 | self._keep_sending(), 61 | self._heartbeat() 62 | ) 63 | 64 | except Exception as e: 65 | logger.error(f"连接异常: {str(e)}") 66 | await self._safe_close() 67 | await asyncio.sleep(RECONNECT_INTERVAL) 68 | 69 | async def _keep_receiving(self): 70 | """持续接收消息""" 71 | try: 72 | while self.connected: 73 | message = await self.websocket.recv() 74 | await self.receive_queue.put(message) 75 | logger.debug(f"接收消息: {message}") 76 | except websockets.ConnectionClosed: 77 | logger.warning("连接已关闭") 78 | await self._safe_close() 79 | except Exception as e: 80 | logger.error(f"接收错误: {str(e)}") 81 | await self._safe_close() 82 | 83 | async def _keep_sending(self): 84 | """持续发送消息""" 85 | try: 86 | while self.connected: 87 | data = await self.send_queue.get() 88 | if self.connected: 89 | await self.websocket.send(json.dumps(data)) 90 | logger.info(f"发送成功: {data}") 91 | except websockets.ConnectionClosed: 92 | await self._safe_close() 93 | except Exception as e: 94 | logger.error(f"发送错误: {str(e)}") 95 | await self._safe_close() 96 | 97 | async def _heartbeat(self): 98 | """心跳检测""" 99 | try: 100 | while self.connected: 101 | await asyncio.sleep(PING_INTERVAL) 102 | if self.connected: 103 | await self.websocket.ping() 104 | logger.debug("心跳检测正常") 105 | except Exception as e: 106 | logger.error(f"心跳异常: {str(e)}") 107 | await self._safe_close() 108 | 109 | async def _safe_close(self): 110 | """安全关闭连接""" 111 | async with self._lock: 112 | if self.connected: 113 | self._connected.clear() 114 | try: 115 | await self.websocket.close() 116 | except: 117 | pass 118 | finally: 119 | self.websocket = None 120 | logger.warning("连接已安全关闭") 121 | 122 | 123 | async def data_generator(manager): 124 | """模拟数据生成器""" 125 | while True: 126 | try: 127 | data = { 128 | "status": "1", 129 | "updateTime": datetime.now().strftime("%Y/%m/%d %H-%M-%S") 130 | } 131 | await manager.send_queue.put(data) 132 | await asyncio.sleep(10) 133 | except Exception as e: 134 | logger.error(f"数据生成异常: {str(e)}") 135 | 136 | 137 | async def data_processor(manager): 138 | """数据处理器""" 139 | while True: 140 | try: 141 | message = await manager.receive_queue.get() 142 | logger.info(f"开始处理消息: {message}") 143 | 144 | # 添加具体业务逻辑 145 | try: 146 | data = json.loads(message) 147 | logger.info(f"解析成功: {data}") 148 | # 示例响应处理 149 | if data.get("command"): 150 | response = {"status": "received", "command": data["command"]} 151 | await manager.send_queue.put(response) 152 | except json.JSONDecodeError: 153 | logger.warning("收到非JSON格式数据") 154 | 155 | except Exception as e: 156 | logger.error(f"处理异常: {str(e)}") 157 | 158 | 159 | async def main(): 160 | manager = ConnectionManager() 161 | 162 | # 启动核心任务 163 | main_tasks = asyncio.gather( 164 | manager.maintain_connection(), 165 | data_generator(manager), 166 | data_processor(manager) 167 | ) 168 | 169 | try: 170 | await main_tasks 171 | except asyncio.CancelledError: 172 | logger.info("正在关闭服务...") 173 | manager._is_manually_closed = True 174 | await manager._safe_close() 175 | logger.info("服务已安全退出") 176 | 177 | 178 | if __name__ == "__main__": 179 | try: 180 | asyncio.run(main()) 181 | except KeyboardInterrupt: 182 | logger.info("用户中断服务") 183 | -------------------------------------------------------------------------------- /Monitor/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringzc/RemoteControl/4c37ab43627223f755d76674f30fce91f6ec120b/Monitor/__init__.py -------------------------------------------------------------------------------- /Monitor/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models.User.User import User 3 | from .models.ModeDevice.ModeDevice import ModeDevice 4 | from .models.Plan.Plan import Plan 5 | from .models.UserPlan.UserPlan import UserPlan 6 | from .models.Device.Device import Device 7 | from .models.Mode.Mode import Mode 8 | from .models.PlanMode.PlanMode import PlanMode 9 | 10 | 11 | admin.site.register(User) 12 | 13 | 14 | admin.site.register(Plan) 15 | 16 | 17 | admin.site.register(UserPlan) 18 | 19 | 20 | admin.site.register(Device) 21 | 22 | 23 | admin.site.register(Mode) 24 | 25 | 26 | admin.site.register(PlanMode) 27 | 28 | 29 | admin.site.register(ModeDevice) 30 | -------------------------------------------------------------------------------- /Monitor/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class MonitorConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "Monitor" 7 | -------------------------------------------------------------------------------- /Monitor/authentication.py: -------------------------------------------------------------------------------- 1 | from rest_framework.authentication import BaseAuthentication 2 | from rest_framework.exceptions import AuthenticationFailed 3 | from .models.User.User import User 4 | from django.utils import timezone 5 | 6 | 7 | class TokenAuth(BaseAuthentication): 8 | def authenticate(self, request): 9 | token = request.META.get('HTTP_AUTHORIZATION', '').split('Bearer ')[-1] 10 | if not token: 11 | return None 12 | 13 | try: 14 | user = User.objects.get(token=token) 15 | if timezone.now() > user.token_expire: 16 | raise AuthenticationFailed('Token已过期') 17 | 18 | # 自动续期:距离过期不足1天时刷新 19 | if (user.token_expire - timezone.now()).days < 1: 20 | user.refresh_token() 21 | 22 | return user, None 23 | except User.DoesNotExist: 24 | raise AuthenticationFailed('无效Token') -------------------------------------------------------------------------------- /Monitor/consumers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringzc/RemoteControl/4c37ab43627223f755d76674f30fce91f6ec120b/Monitor/consumers/__init__.py -------------------------------------------------------------------------------- /Monitor/consumers/index.py: -------------------------------------------------------------------------------- 1 | from channels.generic.websocket import AsyncWebsocketConsumer 2 | from channels.db import database_sync_to_async 3 | import json 4 | from Monitor.models.Plan.Plan import Plan 5 | 6 | 7 | class ControlConsumer(AsyncWebsocketConsumer): 8 | def __init__(self, *args, **kwargs): 9 | super().__init__(*args, **kwargs) 10 | self.device_id = None 11 | 12 | async def connect(self): 13 | # 获取设备号并作为房间名的一部分 14 | self.device_id = self.scope['url_route']['kwargs']['device_id'] 15 | room_group_name = self.device_id 16 | 17 | # 加入房间 18 | await self.channel_layer.group_add( 19 | room_group_name, 20 | self.channel_name 21 | ) 22 | 23 | # 接受 WebSocket 连接 24 | await self.accept() 25 | 26 | async def disconnect(self, close_code): 27 | # 离开房间 28 | await self.channel_layer.group_discard( 29 | self.device_id, 30 | self.channel_name 31 | ) 32 | 33 | @database_sync_to_async 34 | def db_get_player(self): 35 | try: 36 | return Plan.objects.filter(plan_device=self.device_id).first() 37 | except Plan.DoesNotExist: 38 | return None 39 | 40 | @database_sync_to_async 41 | def update_plan_status(self, plan, status, updateTime): 42 | """更新Plan状态""" 43 | plan.active = status # 假设字段名为plan_device_status 44 | plan.updateTime = updateTime 45 | plan.save() # 仅更新必要字段 46 | 47 | async def receive(self, text_data): 48 | # 可选:接收来自设备的消息 49 | try: 50 | data = json.loads(text_data) 51 | status = data.get("status") 52 | if status: 53 | 54 | plan = await self.db_get_player() 55 | if not plan: 56 | await self.send(text_data="Error: Plan not found for this device") 57 | return 58 | await self.update_plan_status(plan, status) 59 | # await self.send(text_data="Status updated successfully") 60 | except json.JSONDecodeError: 61 | await self.send(text_data="Error: Invalid JSON format") 62 | except Exception as e: 63 | await self.send(text_data=f"Error: {str(e)}") 64 | # message = '123' 65 | # await self.send(text_data=message) 66 | 67 | # 向房间组内发送消息 68 | async def send_command(self, event): 69 | # 从事件中获取消息并发送给设备 70 | message = event['message'] 71 | await self.send(text_data=message) 72 | -------------------------------------------------------------------------------- /Monitor/models/Device/Device.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | 4 | class Device(models.Model): 5 | Device_id = models.CharField(max_length=255, primary_key=True) # 设备ID,字符串类型 6 | name = models.CharField(max_length=255) # 设备名称 7 | active = models.IntegerField(default=0) # 默认值为0 8 | 9 | def __str__(self): 10 | return self.name 11 | -------------------------------------------------------------------------------- /Monitor/models/Device/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringzc/RemoteControl/4c37ab43627223f755d76674f30fce91f6ec120b/Monitor/models/Device/__init__.py -------------------------------------------------------------------------------- /Monitor/models/Mode/Mode.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | 4 | class Mode(models.Model): 5 | Mode_id = models.CharField(max_length=255, primary_key=True) # 模式ID,字符串类型 6 | name = models.CharField(max_length=255) # 模式名称 7 | description = models.TextField() # 模式描述 8 | url = models.URLField() # 模式的URL链接 9 | 10 | def __str__(self): 11 | return self.name 12 | -------------------------------------------------------------------------------- /Monitor/models/Mode/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringzc/RemoteControl/4c37ab43627223f755d76674f30fce91f6ec120b/Monitor/models/Mode/__init__.py -------------------------------------------------------------------------------- /Monitor/models/ModeDevice/ModeDevice.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | from Monitor.models.Device.Device import Device 4 | from Monitor.models.Mode.Mode import Mode 5 | 6 | 7 | class ModeDevice(models.Model): 8 | mode_id = models.ForeignKey(Mode, on_delete=models.CASCADE) 9 | Device_id = models.ForeignKey(Device, on_delete=models.CASCADE) 10 | 11 | class Meta: 12 | unique_together = ('mode_id', 'Device_id') # 确保每个模式与设备的组合唯一 13 | -------------------------------------------------------------------------------- /Monitor/models/ModeDevice/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringzc/RemoteControl/4c37ab43627223f755d76674f30fce91f6ec120b/Monitor/models/ModeDevice/__init__.py -------------------------------------------------------------------------------- /Monitor/models/Plan/Plan.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | 4 | class Plan(models.Model): 5 | planid = models.CharField(max_length=255, primary_key=True, unique=True) # 方案ID,字符串类型 6 | name = models.CharField(max_length=255, null=True) # 方案名称 7 | description = models.TextField(null=True) # 方案描述 8 | updateTime = models.CharField(max_length=255, null=True) # 更新时间 9 | active = models.IntegerField(default=0) # 默认值为0 10 | icon = models.CharField(max_length=255, null=True) 11 | useTime = models.IntegerField(default=4) # 默认值为0 12 | plan_device = models.CharField(max_length=255, null=True) # 方案名称 13 | 14 | def __str__(self): 15 | return self.planid 16 | -------------------------------------------------------------------------------- /Monitor/models/Plan/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringzc/RemoteControl/4c37ab43627223f755d76674f30fce91f6ec120b/Monitor/models/Plan/__init__.py -------------------------------------------------------------------------------- /Monitor/models/PlanMode/PlanMode.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | from Monitor.models.Mode.Mode import Mode 4 | from Monitor.models.Plan.Plan import Plan 5 | 6 | 7 | class PlanMode(models.Model): 8 | plan = models.ForeignKey(Plan, on_delete=models.CASCADE) 9 | mode = models.ForeignKey(Mode, on_delete=models.CASCADE) 10 | 11 | class Meta: 12 | unique_together = ('plan', 'mode') # 确保每个方案与模式的组合唯一 13 | -------------------------------------------------------------------------------- /Monitor/models/PlanMode/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringzc/RemoteControl/4c37ab43627223f755d76674f30fce91f6ec120b/Monitor/models/PlanMode/__init__.py -------------------------------------------------------------------------------- /Monitor/models/PlanTime/PlanTime.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | from Monitor.models.Plan.Plan import Plan 4 | 5 | 6 | class PlanTime(models.Model): 7 | Plan_id = models.ForeignKey(Plan, on_delete=models.CASCADE) 8 | StartTime = models.CharField(max_length=128, unique=True) 9 | EndTime = models.CharField(max_length=128, unique=True) 10 | 11 | class Meta: 12 | unique_together = ('Plan_id', 'StartTime', 'EndTime') # 确保每个方案与模式的组合唯一 13 | -------------------------------------------------------------------------------- /Monitor/models/PlanTime/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringzc/RemoteControl/4c37ab43627223f755d76674f30fce91f6ec120b/Monitor/models/PlanTime/__init__.py -------------------------------------------------------------------------------- /Monitor/models/User/User.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.utils import timezone 3 | import secrets 4 | 5 | from Aurage import settings 6 | 7 | 8 | class User(models.Model): 9 | phone_number = models.CharField(max_length=15, unique=True, null=True, # 允许数据库存储 NULL 10 | blank=True) # 用户手机号,确保唯一 11 | openid = models.CharField(max_length=128, unique=True) 12 | token = models.CharField(max_length=64, unique=True, null=True) 13 | token_expire = models.DateTimeField(null=True) 14 | last_login = models.DateTimeField(auto_now=True, null=True) # 记录最后活跃时间 15 | @classmethod 16 | def generate_token(cls): 17 | return secrets.token_urlsafe(32) # 生成高强度随机Token 18 | 19 | def refresh_token(self): 20 | self.token = self.generate_token() 21 | self.token_expire = timezone.now() + timezone.timedelta(days=settings.TOKEN_EXPIRE_DAYS) 22 | self.save() 23 | # 添加认证系统所需属性 24 | 25 | def refresh_time(self): 26 | now = timezone.now() 27 | # 检查是否存在token_expire且未过期 28 | if self.token_expire and now < self.token_expire: 29 | self.token_expire = now + timezone.timedelta(days=settings.TOKEN_EXPIRE_DAYS) 30 | self.save() # auto_now=True会自动更新last_login 31 | return True 32 | else: 33 | return False 34 | 35 | @property 36 | def is_authenticated(self): 37 | return True 38 | 39 | @property 40 | def is_anonymous(self): 41 | return False 42 | 43 | 44 | -------------------------------------------------------------------------------- /Monitor/models/User/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringzc/RemoteControl/4c37ab43627223f755d76674f30fce91f6ec120b/Monitor/models/User/__init__.py -------------------------------------------------------------------------------- /Monitor/models/UserPlan/UserPlan.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | from Monitor.models.Plan.Plan import Plan 4 | from Monitor.models.User.User import User 5 | 6 | 7 | class UserPlan(models.Model): 8 | openid = models.CharField(max_length=128, unique=True) 9 | plan_id = models.CharField(max_length=128, unique=True) 10 | 11 | class Meta: 12 | unique_together = ('openid', 'plan_id') # 确保每个用户与方案的组合唯一 13 | -------------------------------------------------------------------------------- /Monitor/models/UserPlan/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringzc/RemoteControl/4c37ab43627223f755d76674f30fce91f6ec120b/Monitor/models/UserPlan/__init__.py -------------------------------------------------------------------------------- /Monitor/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringzc/RemoteControl/4c37ab43627223f755d76674f30fce91f6ec120b/Monitor/models/__init__.py -------------------------------------------------------------------------------- /Monitor/routing.py: -------------------------------------------------------------------------------- 1 | from django.urls import re_path 2 | from Monitor.consumers.index import ControlConsumer 3 | 4 | websocket_urlpatterns = [ 5 | re_path(r'ws/ControlConsumer/(?P\w+)/$', ControlConsumer.as_asgi()), 6 | ] -------------------------------------------------------------------------------- /Monitor/templates/web.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 我的第一个网页 7 | 33 | 34 | 35 |
36 |

欢迎来到我的网页

37 |
38 | 39 |
40 |

关于我

41 |

这是一个简单的HTML页面示例。你可以在这里添加更多内容。

42 | 47 |
48 | 49 | 52 | 53 | -------------------------------------------------------------------------------- /Monitor/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /Monitor/urls/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringzc/RemoteControl/4c37ab43627223f755d76674f30fce91f6ec120b/Monitor/urls/__init__.py -------------------------------------------------------------------------------- /Monitor/urls/index.py: -------------------------------------------------------------------------------- 1 | from django.urls import path, include 2 | 3 | from Monitor.views.index import index 4 | 5 | urlpatterns = [ 6 | path("", index, name="index"), 7 | path("wx/", include("Monitor.urls.wx.index")), 8 | path("web/", include("Monitor.urls.web.index")) 9 | ] 10 | -------------------------------------------------------------------------------- /Monitor/urls/web/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringzc/RemoteControl/4c37ab43627223f755d76674f30fce91f6ec120b/Monitor/urls/web/__init__.py -------------------------------------------------------------------------------- /Monitor/urls/web/index.py: -------------------------------------------------------------------------------- 1 | from django.urls import path, include 2 | 3 | 4 | urlpatterns = [ 5 | ] 6 | -------------------------------------------------------------------------------- /Monitor/urls/wx/Login/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringzc/RemoteControl/4c37ab43627223f755d76674f30fce91f6ec120b/Monitor/urls/wx/Login/__init__.py -------------------------------------------------------------------------------- /Monitor/urls/wx/Login/index.py: -------------------------------------------------------------------------------- 1 | from django.urls import path, include 2 | 3 | from Monitor.views.wx.Login.UserProfileView import UserProfileView 4 | from Monitor.views.wx.Login.getPhoneNumber import getPhoneNumber 5 | from Monitor.views.wx.Login.getUid import getUid 6 | from Monitor.views.wx.Login.savePhoneNumber import savePhoneNumber 7 | 8 | urlpatterns = [ 9 | path("getUid/", getUid.as_view()), # getPhoneNumber 10 | path("getPhoneNumber/", getPhoneNumber.as_view()), # savePhoneNumber 11 | path("savePhoneNumber/", savePhoneNumber.as_view()), 12 | path("UserProfileView/", UserProfileView.as_view()) 13 | # path("web/", include("Monitor.urls.web.index")) 14 | ] 15 | -------------------------------------------------------------------------------- /Monitor/urls/wx/Plan/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringzc/RemoteControl/4c37ab43627223f755d76674f30fce91f6ec120b/Monitor/urls/wx/Plan/__init__.py -------------------------------------------------------------------------------- /Monitor/urls/wx/Plan/index.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from Monitor.views.wx.Plan.AddPlanInfoWithID import AddPlanInfoWithID 4 | from Monitor.views.wx.Plan.CancelAct import CancelAct 5 | from Monitor.views.wx.Plan.PlanAct import PlanAct 6 | from Monitor.views.wx.Plan.SaveAutoRecords import SaveAutoRecords 7 | from Monitor.views.wx.Plan.getAutoTimeWithPlanID import getAutoTimeWithPlanID 8 | from Monitor.views.wx.Plan.getModelInfoWithPlanID import getModelInfoWithPlanID 9 | from Monitor.views.wx.Plan.getPlanInfoWithID import getPlanInfoWithID 10 | 11 | urlpatterns = [ 12 | path("getPlanInfoWithID/", getPlanInfoWithID.as_view()), # getPhoneNumber 13 | path("getModelInfoWithPlanID/", getModelInfoWithPlanID.as_view()), # savePhoneNumber 14 | path("getAutoTimeWithPlanID/", getAutoTimeWithPlanID.as_view()), 15 | path("AddPlanInfoWithID/", AddPlanInfoWithID.as_view()), # CancelAct 16 | path("CancelAct/", CancelAct.as_view()), 17 | path("PlanAct/", PlanAct.as_view()), # saveAutoRecords 18 | path("SaveAutoRecords/", SaveAutoRecords.as_view()) 19 | ] 20 | -------------------------------------------------------------------------------- /Monitor/urls/wx/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringzc/RemoteControl/4c37ab43627223f755d76674f30fce91f6ec120b/Monitor/urls/wx/__init__.py -------------------------------------------------------------------------------- /Monitor/urls/wx/index.py: -------------------------------------------------------------------------------- 1 | from django.urls import path, include 2 | 3 | urlpatterns = [ 4 | path("Login/", include("Monitor.urls.wx.Login.index")), 5 | path("Plan/", include("Monitor.urls.wx.Plan.index")), 6 | # path("web/", include("Monitor.urls.web.index")) 7 | ] 8 | -------------------------------------------------------------------------------- /Monitor/utils.py: -------------------------------------------------------------------------------- 1 | from cryptography.fernet import Fernet 2 | from django.conf import settings 3 | import secrets 4 | import string 5 | 6 | # 加密函数 7 | def encrypt_data(data): 8 | cipher_suite = Fernet(settings.FERNET_KEY.encode()) 9 | return cipher_suite.encrypt(data.encode()).decode() 10 | 11 | # 解密函数 12 | def decrypt_data(encrypted_data): 13 | cipher_suite = Fernet(settings.FERNET_KEY.encode()) 14 | return cipher_suite.decrypt(encrypted_data.encode()).decode() 15 | 16 | # 生成45位随机ID 17 | def generate_planid(): 18 | chars = string.ascii_letters + string.digits 19 | return ''.join(secrets.choice(chars) for _ in range(45)) -------------------------------------------------------------------------------- /Monitor/views/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringzc/RemoteControl/4c37ab43627223f755d76674f30fce91f6ec120b/Monitor/views/__init__.py -------------------------------------------------------------------------------- /Monitor/views/index.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | from ..models.Plan.Plan import Plan 3 | from ..utils import generate_planid, encrypt_data, decrypt_data 4 | 5 | 6 | # game/templates/multiends 7 | def index(request): 8 | # raw_id = generate_planid() 9 | # encrypted_id = encrypt_data(raw_id) 10 | # 11 | # # 创建数据对象 12 | # new_plan = Plan( 13 | # planid=encrypted_id, 14 | # name="智能浇灌设备", 15 | # description="自动化浇水方案", 16 | # updateTime="2023-10-26 09:30:00", 17 | # active=1, 18 | # icon="GG", 19 | # useTime=4, 20 | # plan_device='raspberry_001' 21 | # ) 22 | # new_plan.save() 23 | return render(request, "web.html") 24 | -------------------------------------------------------------------------------- /Monitor/views/web/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringzc/RemoteControl/4c37ab43627223f755d76674f30fce91f6ec120b/Monitor/views/web/__init__.py -------------------------------------------------------------------------------- /Monitor/views/wx/Login/UserProfileView.py: -------------------------------------------------------------------------------- 1 | from rest_framework.views import APIView 2 | from rest_framework.response import Response 3 | from rest_framework.permissions import IsAuthenticated 4 | 5 | from Monitor.authentication import TokenAuth 6 | 7 | 8 | class UserProfileView(APIView): 9 | authentication_classes = [TokenAuth] 10 | permission_classes = [IsAuthenticated] 11 | 12 | def get(self, request): 13 | user = request.user 14 | if user.refresh_time(): 15 | return Response({ 16 | "openid": user.openid, 17 | "last_login": user.last_login 18 | }) 19 | else: 20 | return Response({"error": "Token expired"}, status=401) 21 | -------------------------------------------------------------------------------- /Monitor/views/wx/Login/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringzc/RemoteControl/4c37ab43627223f755d76674f30fce91f6ec120b/Monitor/views/wx/Login/__init__.py -------------------------------------------------------------------------------- /Monitor/views/wx/Login/getPhoneNumber.py: -------------------------------------------------------------------------------- 1 | from django.http import JsonResponse 2 | from rest_framework.permissions import IsAuthenticated 3 | from rest_framework.views import APIView 4 | 5 | from Monitor.authentication import TokenAuth 6 | 7 | 8 | class getPhoneNumber(APIView): 9 | authentication_classes = [TokenAuth] 10 | permission_classes = [IsAuthenticated] 11 | 12 | def get(self, request): 13 | return JsonResponse({ 14 | 'result': "success", 15 | 'phoneNumber': request.user.phone_number, 16 | }) 17 | -------------------------------------------------------------------------------- /Monitor/views/wx/Login/getUid.py: -------------------------------------------------------------------------------- 1 | from django.http import JsonResponse 2 | from rest_framework.views import APIView 3 | from rest_framework.response import Response 4 | from django.conf import settings 5 | import requests 6 | from Monitor.models.User.User import User 7 | from django.utils import timezone 8 | 9 | 10 | class getUid(APIView): 11 | def get(self, request): 12 | code = request.query_params.get('code') # 从GET参数获取code 13 | 14 | if not code: 15 | return Response({"error": "Missing code"}, status=400) 16 | 17 | # 向微信服务器验证code 18 | wx_url = f"https://api.weixin.qq.com/sns/jscode2session?appid={settings.WX_APPID}&secret={settings.WX_SECRET}&js_code={code}&grant_type=authorization_code" 19 | wx_res = requests.get(wx_url).json() 20 | if 'errcode' in wx_res: 21 | return Response({"error": "微信登录失败"}, status=401) 22 | 23 | openid = wx_res['openid'] 24 | # 获取或创建用户 25 | user, created = User.objects.get_or_create(openid=openid) 26 | if created or (timezone.now() > user.token_expire): 27 | user.refresh_token() 28 | if user.phone_number: 29 | return Response({ 30 | "statues": True, 31 | "token": user.token, 32 | "expire": user.token_expire.timestamp() 33 | }) 34 | else: 35 | return Response({ 36 | "statues": False, 37 | "token": user.token, 38 | "expire": user.token_expire.timestamp() 39 | }) -------------------------------------------------------------------------------- /Monitor/views/wx/Login/savePhoneNumber.py: -------------------------------------------------------------------------------- 1 | from django.http import JsonResponse 2 | from rest_framework.permissions import IsAuthenticated 3 | from rest_framework.views import APIView 4 | 5 | from Monitor.authentication import TokenAuth 6 | from Monitor.models.User.User import User 7 | 8 | 9 | class savePhoneNumber(APIView): 10 | authentication_classes = [TokenAuth] 11 | permission_classes = [IsAuthenticated] 12 | 13 | def post(self, request): 14 | phone = request.data['phoneNumber'] 15 | user = User.objects.get(openid=request.user.openid) 16 | if user: 17 | user.phone_number = phone 18 | user.save() 19 | return JsonResponse({ 20 | 'result': "success", 21 | }) 22 | else: 23 | return JsonResponse({ 24 | 'result': "fail", 25 | }) 26 | -------------------------------------------------------------------------------- /Monitor/views/wx/Mode/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringzc/RemoteControl/4c37ab43627223f755d76674f30fce91f6ec120b/Monitor/views/wx/Mode/__init__.py -------------------------------------------------------------------------------- /Monitor/views/wx/Plan/AddPlanInfoWithID.py: -------------------------------------------------------------------------------- 1 | from django.http import JsonResponse 2 | from rest_framework.permissions import IsAuthenticated 3 | from rest_framework.views import APIView 4 | 5 | from Monitor.authentication import TokenAuth 6 | from Monitor.models.Plan.Plan import Plan 7 | from Monitor.models.UserPlan.UserPlan import UserPlan 8 | from Monitor.utils import decrypt_data 9 | 10 | 11 | class AddPlanInfoWithID(APIView): 12 | authentication_classes = [TokenAuth] 13 | permission_classes = [IsAuthenticated] 14 | 15 | def post(self, request): 16 | plan_id = request.data['deviceCode'][45:-45] 17 | openid = request.user.openid 18 | plan = Plan.objects.filter(planid=plan_id).first() 19 | # up, created = UserPlan.objects.get_or_create(openid=openid, plan_id=plan_id) 20 | if plan: 21 | if plan.useTime > 0: 22 | plan.useTime -= 1 23 | plan.save() 24 | up, created = UserPlan.objects.get_or_create(openid=openid, plan_id=plan_id) 25 | if created: 26 | return JsonResponse({ 27 | # 'data': { 28 | # 'devices': [ 29 | # {'result': "success", 30 | # 'id': 'savePhoneNumber', 31 | # 'name': '智能灌溉系统', 32 | # "updateTime": '1717398223000', 33 | # 'active': 1, 34 | # 'icon': 'GG'}], 35 | # } 36 | 'device': [ 37 | { 38 | 'id': plan.planid, 39 | 'name': plan.name, 40 | "updateTime": plan.updateTime, 41 | 'active': plan.active, 42 | 'icon': plan.icon}, ], 43 | 'result': "success", 44 | }) 45 | else: # 设备全部添加完成 46 | return JsonResponse({ 47 | 'result': "have", 48 | }) 49 | else: 50 | return JsonResponse({ 51 | 'result': "no", 52 | }) 53 | else: 54 | return JsonResponse({ 55 | 'result': "fail", 56 | }) 57 | 58 | # def get_plan(request, encrypted_id): 59 | # # 查询并解密 60 | # plan = Plan.objects.get(planid=encrypted_id) 61 | # decrypted_id = decrypt_data(plan.planid) 62 | # 63 | # return JsonResponse({ 64 | # "id": decrypted_id, 65 | # "name": plan.name, 66 | # "use_time": plan.useTime 67 | # }) 68 | -------------------------------------------------------------------------------- /Monitor/views/wx/Plan/CancelAct.py: -------------------------------------------------------------------------------- 1 | from asgiref.sync import async_to_sync 2 | from channels.layers import get_channel_layer 3 | from django.core.cache import cache 4 | from django.http import JsonResponse 5 | from rest_framework.permissions import IsAuthenticated 6 | from rest_framework.views import APIView 7 | 8 | from Monitor.authentication import TokenAuth 9 | from Monitor.models.Plan.Plan import Plan 10 | 11 | 12 | class CancelAct(APIView): 13 | authentication_classes = [TokenAuth] 14 | permission_classes = [IsAuthenticated] 15 | 16 | def post(self, request): 17 | planid = request.data['PlanId'] 18 | plan = Plan.objects.filter(planid=planid).first() 19 | if plan: 20 | command = '11000' 21 | cache_key = 'ACT' 22 | data = cache.get(cache_key) 23 | if data is None: 24 | return JsonResponse({ 25 | 'status': 1 26 | }) 27 | else: 28 | cache.delete(cache_key) 29 | device_id = plan.plan_device 30 | # 通过WebSocket发送指令 31 | channel_layer = get_channel_layer() 32 | async_to_sync(channel_layer.group_send)( 33 | device_id, 34 | { 35 | "type": "send_command", 36 | "message": command 37 | } 38 | ) 39 | return JsonResponse({ 40 | 'status': 0 41 | }) 42 | else: 43 | return JsonResponse({ 44 | 'status': 999 45 | }) 46 | -------------------------------------------------------------------------------- /Monitor/views/wx/Plan/PlanAct.py: -------------------------------------------------------------------------------- 1 | from asgiref.sync import async_to_sync 2 | from channels.layers import get_channel_layer 3 | from django.http import JsonResponse 4 | from django.core.cache import cache 5 | from rest_framework.permissions import IsAuthenticated 6 | from rest_framework.views import APIView 7 | 8 | from Monitor.authentication import TokenAuth 9 | from Monitor.models.Plan.Plan import Plan 10 | 11 | class PlanAct(APIView): 12 | authentication_classes = [TokenAuth] 13 | permission_classes = [IsAuthenticated] 14 | 15 | def post(self, request): 16 | planid = request.data['PlanId'] 17 | plan = Plan.objects.filter(planid=planid).first() 18 | if plan: 19 | command = '10000' 20 | cache_key = 'ACT' 21 | data = cache.get(cache_key) 22 | if data: 23 | return JsonResponse({ 24 | 'status': 1 25 | }) 26 | else: 27 | cache.set(cache_key, 'ACT', timeout=90) 28 | device_id = plan.plan_device 29 | # 通过WebSocket发送指令 30 | channel_layer = get_channel_layer() 31 | async_to_sync(channel_layer.group_send)( 32 | device_id, 33 | { 34 | "type": "send_command", 35 | "message": command 36 | } 37 | ) 38 | return JsonResponse({ 39 | 'status': 0 40 | }) 41 | else: 42 | return JsonResponse({ 43 | 'status': 999 44 | }) 45 | -------------------------------------------------------------------------------- /Monitor/views/wx/Plan/SaveAutoRecords.py: -------------------------------------------------------------------------------- 1 | from django.http import JsonResponse 2 | from rest_framework.permissions import IsAuthenticated 3 | from rest_framework.views import APIView 4 | 5 | from Monitor.authentication import TokenAuth 6 | from Monitor.models.PlanTime.PlanTime import PlanTime 7 | from Monitor.models.Plan.Plan import Plan 8 | 9 | class SaveAutoRecords(APIView): 10 | authentication_classes = [TokenAuth] 11 | permission_classes = [IsAuthenticated] 12 | 13 | def post(self, r): 14 | records = r.data['records'] 15 | plan = Plan.objects.filter(planid=r.data['PlanId']).first() 16 | if plan: 17 | for _ in records: 18 | pt = PlanTime.objects.create(Plan_id=plan, StartTime=_['startTime'], EndTime=_['endTime']) 19 | return JsonResponse({"result": 'success'}) 20 | else: 21 | return JsonResponse({"result": 'fail'}) 22 | -------------------------------------------------------------------------------- /Monitor/views/wx/Plan/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringzc/RemoteControl/4c37ab43627223f755d76674f30fce91f6ec120b/Monitor/views/wx/Plan/__init__.py -------------------------------------------------------------------------------- /Monitor/views/wx/Plan/getAutoTimeWithPlanID.py: -------------------------------------------------------------------------------- 1 | from django.http import JsonResponse 2 | from rest_framework.permissions import IsAuthenticated 3 | from rest_framework.views import APIView 4 | 5 | from Monitor.authentication import TokenAuth 6 | from Monitor.models.PlanTime.PlanTime import PlanTime 7 | 8 | 9 | class getAutoTimeWithPlanID(APIView): 10 | authentication_classes = [TokenAuth] 11 | permission_classes = [IsAuthenticated] 12 | 13 | def post(self, r): 14 | PlanId = r.data['PlanId'] 15 | AuToTime = PlanTime.objects.filter(Plan_id=PlanId) 16 | if AuToTime: 17 | records = [] 18 | for _ in AuToTime: 19 | records.append({ 20 | 'start_time': _.StartTime, 21 | 'end_time': _.EndTime 22 | }) 23 | return JsonResponse({"result": 'success', "data": records}) 24 | else: 25 | return JsonResponse({"result": 'fail'}) 26 | -------------------------------------------------------------------------------- /Monitor/views/wx/Plan/getModelInfoWithPlanID.py: -------------------------------------------------------------------------------- 1 | from django.http import JsonResponse 2 | from rest_framework.permissions import IsAuthenticated 3 | from rest_framework.views import APIView 4 | 5 | from Monitor.authentication import TokenAuth 6 | 7 | 8 | class getModelInfoWithPlanID(APIView): 9 | authentication_classes = [TokenAuth] 10 | permission_classes = [IsAuthenticated] 11 | 12 | def post(self, r): 13 | Model = [ 14 | {"id": "1", "name": "1", 'description': "打开将给一号区域土地供水", 15 | 'imageUrl': 'https://img95.699pic.com/photo/40242/7819.jpg_wh860.jpg'}, 16 | {"id": "2", "name": "2", 'description': "打开将给二号区域土地供水", 17 | 'imageUrl': 'https://img95.699pic.com/photo/40242/7819.jpg_wh860.jpg'}, 18 | ] 19 | return JsonResponse({"data": Model}) 20 | -------------------------------------------------------------------------------- /Monitor/views/wx/Plan/getPlanInfoWithID.py: -------------------------------------------------------------------------------- 1 | from django.http import JsonResponse 2 | from rest_framework.permissions import IsAuthenticated 3 | from rest_framework.views import APIView 4 | from Monitor.models.UserPlan.UserPlan import UserPlan 5 | from Monitor.models.Plan.Plan import Plan 6 | 7 | from Monitor.authentication import TokenAuth 8 | 9 | 10 | class getPlanInfoWithID(APIView): 11 | authentication_classes = [TokenAuth] 12 | permission_classes = [IsAuthenticated] 13 | 14 | def get(self, request): 15 | openid = request.user.openid 16 | plan = UserPlan.objects.filter(openid=openid) 17 | devices = [] 18 | if plan: 19 | for i in plan: 20 | j = Plan.objects.get(planid=i.plan_id) 21 | if j: 22 | devices.append( 23 | { 24 | 'id': j.planid, 25 | 'name': j.name, 26 | "updateTime": j.updateTime, 27 | 'active': j.active, 28 | 'icon': j.icon 29 | } 30 | ) 31 | return JsonResponse({ 32 | 'devices': devices, 33 | 'result': "success" 34 | }, ) 35 | else: 36 | return JsonResponse({ 37 | 'devices': devices, 38 | 'result': "fail" 39 | }) 40 | -------------------------------------------------------------------------------- /Monitor/views/wx/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringzc/RemoteControl/4c37ab43627223f755d76674f30fce91f6ec120b/Monitor/views/wx/__init__.py -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringzc/RemoteControl/4c37ab43627223f755d76674f30fce91f6ec120b/README.md -------------------------------------------------------------------------------- /db.sqlite3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringzc/RemoteControl/4c37ab43627223f755d76674f30fce91f6ec120b/db.sqlite3 -------------------------------------------------------------------------------- /django.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringzc/RemoteControl/4c37ab43627223f755d76674f30fce91f6ec120b/django.docx -------------------------------------------------------------------------------- /img/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringzc/RemoteControl/4c37ab43627223f755d76674f30fce91f6ec120b/img/1.png -------------------------------------------------------------------------------- /img/2.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringzc/RemoteControl/4c37ab43627223f755d76674f30fce91f6ec120b/img/2.1.png -------------------------------------------------------------------------------- /img/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringzc/RemoteControl/4c37ab43627223f755d76674f30fce91f6ec120b/img/2.png -------------------------------------------------------------------------------- /img/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringzc/RemoteControl/4c37ab43627223f755d76674f30fce91f6ec120b/img/3.png -------------------------------------------------------------------------------- /img/电气柜.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringzc/RemoteControl/4c37ab43627223f755d76674f30fce91f6ec120b/img/电气柜.jpg -------------------------------------------------------------------------------- /img/示意图.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringzc/RemoteControl/4c37ab43627223f755d76674f30fce91f6ec120b/img/示意图.jpg -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | """Run administrative tasks.""" 9 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "Aurage.settings") 10 | try: 11 | from django.core.management import execute_from_command_line 12 | except ImportError as exc: 13 | raise ImportError( 14 | "Couldn't import Django. Are you sure it's installed and " 15 | "available on your PYTHONPATH environment variable? Did you " 16 | "forget to activate a virtual environment?" 17 | ) from exc 18 | execute_from_command_line(sys.argv) 19 | 20 | 21 | if __name__ == "__main__": 22 | main() 23 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringzc/RemoteControl/4c37ab43627223f755d76674f30fce91f6ec120b/requirements.txt -------------------------------------------------------------------------------- /stringPLC.smart: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringzc/RemoteControl/4c37ab43627223f755d76674f30fce91f6ec120b/stringPLC.smart -------------------------------------------------------------------------------- /微信小程序部分/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // 使用 IntelliSense 了解相关属性。 3 | // 悬停以查看现有属性的描述。 4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "pwa-chrome", 9 | "request": "launch", 10 | "name": "Launch Chrome against localhost", 11 | "url": "http://localhost:8080", 12 | "webRoot": "${workspaceFolder}" 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /微信小程序部分/miniprogram/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "pages": [ 3 | "pages/index/index", 4 | "pages/login/login", 5 | "pages/device/device", 6 | "pages/user/user", 7 | "pages/device/devicexijie/xijie", 8 | "pages/device/devicexijie/auto/auto", 9 | "pages/device/devicexijie/personality/personality" 10 | ], 11 | "tabBar": { 12 | "color": "#999999", 13 | "selectedColor": "#1296db", 14 | "backgroundColor": "#ffffff", 15 | "list": [ 16 | { 17 | "pagePath": "pages/index/index", 18 | "text": "主页", 19 | "iconPath": "images/tab/home.png", 20 | "selectedIconPath": "images/tab/home_active.png" 21 | }, 22 | { 23 | "pagePath": "pages/device/device", 24 | "text": "设备", 25 | "iconPath": "images/tab/device.png", 26 | "selectedIconPath": "images/tab/device_active.png" 27 | }, 28 | { 29 | "pagePath": "pages/user/user", 30 | "text": "我的", 31 | "iconPath": "images/tab/user.png", 32 | "selectedIconPath": "images/tab/user_active.png" 33 | } 34 | ] 35 | }, 36 | "window": { 37 | "navigationBarTextStyle": "black", 38 | "navigationBarTitleText": "斗城农业", 39 | "navigationBarBackgroundColor": "#ffffff" 40 | }, 41 | "style": "v2", 42 | "componentFramework": "glass-easel", 43 | "lazyCodeLoading": "requiredComponents" 44 | } -------------------------------------------------------------------------------- /微信小程序部分/miniprogram/app.ts: -------------------------------------------------------------------------------- 1 | // app.ts 2 | type TokenType = string; 3 | App({ 4 | globalData: { 5 | phoneNumber: '', // 存储用户手机号 6 | uid: '', // 存储用户唯一标识 7 | baseurl:`https://www.aurage.cn/wx/`, 8 | token: '', 9 | }, 10 | 11 | onLaunch() { 12 | // 登录并获取用户 uid 13 | const token: TokenType = wx.getStorageSync('token'); 14 | if (token) { 15 | wx.request({ 16 | url: this.globalData.baseurl + 'Login/UserProfileView', 17 | header: { 18 | 'Authorization': `Bearer ${token}` // 将 Token 放入请求头 19 | }, 20 | success: (res) => { 21 | // 这里获取用户资料(如昵称、头像) 22 | if (res.statusCode >= 200 && res.statusCode < 300) 23 | { 24 | this.globalData.uid = res.data.openid; 25 | this.checkPhoneNumber(); 26 | }else{ 27 | wx.showToast({ 28 | title: 'token过期', 29 | icon: 'none' 30 | }); 31 | this.getToken() 32 | } 33 | }, 34 | fail: (err) => { 35 | wx.showToast({ 36 | title: '网络错误', 37 | icon: 'error' 38 | }); 39 | } 40 | }); 41 | } else { 42 | this.getToken() 43 | } 44 | }, 45 | /** 46 | * 获取token 47 | */ 48 | getToken(){ 49 | wx.login({ 50 | success: res => { 51 | // 假设从后台获取 uid 52 | wx.request({ 53 | url: this.globalData.baseurl + `Login/getUid`, // 替换为实际的服务器 URL 54 | method: 'GET', 55 | data: { code: res.code }, 56 | success: (response:any) => { 57 | const token = response.data.token; 58 | wx.setStorageSync('token', token); // 存储Token 59 | this.checkPhoneNumber(); 60 | } 61 | }); 62 | } 63 | }); 64 | }, 65 | /** 66 | * 检查是否已存储手机号 67 | */ 68 | checkPhoneNumber() { 69 | this.queryPhoneNumberFromServer(); 70 | }, 71 | 72 | /** 73 | * 从服务器查询手机号 74 | */ 75 | queryPhoneNumberFromServer() { 76 | const token: TokenType = wx.getStorageSync('token'); 77 | wx.request({ 78 | url: this.globalData.baseurl + 'Login/getPhoneNumber', // 替换为实际的服务器 URL 79 | method: 'GET', 80 | header: { 81 | 'Authorization': `Bearer ${token}` // 将 Token 放入请求头 82 | }, 83 | success: (response) => { 84 | const data = response.data as { phoneNumber: string }; 85 | if (data.phoneNumber) { 86 | this.globalData.phoneNumber = data.phoneNumber; 87 | this.navigateToHome(); 88 | } else { 89 | this.requestPhoneNumberAuthorization(); 90 | } 91 | }, 92 | fail: (err) => { 93 | wx.showToast({ 94 | title: '网络错误', 95 | icon: 'error' 96 | }); 97 | } 98 | }); 99 | }, 100 | 101 | /** 102 | * 请求用户授权手机号 103 | */ 104 | requestPhoneNumberAuthorization() { 105 | wx.redirectTo({ 106 | url: '/pages/login/login', // 跳转到获取手机号的页面 107 | }); 108 | }, 109 | 110 | /** 111 | * 跳转到主页面 112 | */ 113 | navigateToHome() { 114 | wx.reLaunch({ 115 | url: '/pages/index/index', // 跳转到主页面 116 | }); 117 | }, 118 | 119 | }) -------------------------------------------------------------------------------- /微信小程序部分/miniprogram/app.wxss: -------------------------------------------------------------------------------- 1 | /**app.wxss**/ 2 | .container { 3 | height: 100%; 4 | display: flex; 5 | flex-direction: column; 6 | align-items: center; 7 | justify-content: space-between; 8 | padding: 200rpx 0; 9 | box-sizing: border-box; 10 | } 11 | -------------------------------------------------------------------------------- /微信小程序部分/miniprogram/images/default.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringzc/RemoteControl/4c37ab43627223f755d76674f30fce91f6ec120b/微信小程序部分/miniprogram/images/default.zip -------------------------------------------------------------------------------- /微信小程序部分/miniprogram/images/tab/device.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringzc/RemoteControl/4c37ab43627223f755d76674f30fce91f6ec120b/微信小程序部分/miniprogram/images/tab/device.png -------------------------------------------------------------------------------- /微信小程序部分/miniprogram/images/tab/device_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringzc/RemoteControl/4c37ab43627223f755d76674f30fce91f6ec120b/微信小程序部分/miniprogram/images/tab/device_active.png -------------------------------------------------------------------------------- /微信小程序部分/miniprogram/images/tab/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringzc/RemoteControl/4c37ab43627223f755d76674f30fce91f6ec120b/微信小程序部分/miniprogram/images/tab/home.png -------------------------------------------------------------------------------- /微信小程序部分/miniprogram/images/tab/home_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringzc/RemoteControl/4c37ab43627223f755d76674f30fce91f6ec120b/微信小程序部分/miniprogram/images/tab/home_active.png -------------------------------------------------------------------------------- /微信小程序部分/miniprogram/images/tab/user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringzc/RemoteControl/4c37ab43627223f755d76674f30fce91f6ec120b/微信小程序部分/miniprogram/images/tab/user.png -------------------------------------------------------------------------------- /微信小程序部分/miniprogram/images/tab/user_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringzc/RemoteControl/4c37ab43627223f755d76674f30fce91f6ec120b/微信小程序部分/miniprogram/images/tab/user_active.png -------------------------------------------------------------------------------- /微信小程序部分/miniprogram/images/w/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringzc/RemoteControl/4c37ab43627223f755d76674f30fce91f6ec120b/微信小程序部分/miniprogram/images/w/default.png -------------------------------------------------------------------------------- /微信小程序部分/miniprogram/images/w/gg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringzc/RemoteControl/4c37ab43627223f755d76674f30fce91f6ec120b/微信小程序部分/miniprogram/images/w/gg.png -------------------------------------------------------------------------------- /微信小程序部分/miniprogram/images/二维.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringzc/RemoteControl/4c37ab43627223f755d76674f30fce91f6ec120b/微信小程序部分/miniprogram/images/二维.jpg -------------------------------------------------------------------------------- /微信小程序部分/miniprogram/pages/device/device.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": {} 3 | } -------------------------------------------------------------------------------- /微信小程序部分/miniprogram/pages/device/device.ts: -------------------------------------------------------------------------------- 1 | // pages/device/device.ts 2 | interface Device { 3 | id: number; 4 | name: string; 5 | status: '已连接' | '未响应' | '离线'; 6 | updateTime: string; 7 | active: boolean; 8 | icon: string; 9 | } 10 | 11 | Page({ 12 | 13 | data: { 14 | deviceList: [] as Device[] 15 | }, 16 | 17 | /** 18 | * 生命周期函数--监听页面加载 19 | */ 20 | onLoad() { 21 | this.loadRealDeviceData(); 22 | }, 23 | /** 24 | * 从后端API获取真实设备数据 25 | */ 26 | loadRealDeviceData() { 27 | const token: TokenType = wx.getStorageSync('token'); 28 | wx.showLoading({ title: '加载设备...' }); 29 | wx.request({ 30 | url: getApp().globalData.baseurl + 'Plan/getPlanInfoWithID/', 31 | method: 'GET', 32 | header: { 33 | 'Authorization': `Bearer ${token}` // 将 Token 放入请求头 34 | }, 35 | success: (res) => { 36 | if (res.statusCode === 200) { 37 | const data = res.data 38 | if (data.result === 'success'){ 39 | this.processApiData(data.devices); 40 | }else if(data.result === 'fail'){ 41 | this.handleDataError('错误') 42 | }else{ 43 | this.handleDataError('错误') 44 | } 45 | 46 | } else { 47 | this.handleDataError('数据格式异常'); 48 | } 49 | }, 50 | fail: (err) => { 51 | this.handleDataError('网络连接异常'); 52 | }, 53 | complete: () => wx.hideLoading() 54 | }); 55 | }, 56 | 57 | /** 58 | * 处理API返回的真实数据 59 | */ 60 | processApiData(apiData: any[]) { 61 | const validatedData = apiData 62 | .filter(item => this.validateDeviceData(item)) 63 | .map(item => ({ 64 | id: item.id, 65 | name: item.name, 66 | status: this.mapDeviceStatus(item.active), 67 | updateTime: this.formatTimestamp(item.updateTime), 68 | active: Boolean(item.active), 69 | icon: this.getDeviceIcon(item.icon) 70 | })) 71 | .sort((a, b) => (a.active === b.active ? 0 : a.active ? -1 : 1)); 72 | this.setData({ 73 | deviceList: [...this.data.deviceList, ...validatedData] 74 | }); 75 | }, 76 | 77 | /** 78 | * 数据验证(示例) 79 | */ 80 | validateDeviceData(item: any): boolean { 81 | const requiredFields = ['id', 'name', 'active', 'updateTime']; 82 | return requiredFields.every(field => item[field] !== undefined); 83 | }, 84 | 85 | /** 86 | * 状态码映射(根据实际API调整) 87 | */ 88 | mapDeviceStatus(statusCode: number): Device['status'] { 89 | const statusMap: Record = { 90 | 0: '离线', 91 | 1: '已连接', 92 | 2: '未响应' 93 | }; 94 | return statusMap[statusCode] || '离线'; 95 | }, 96 | 97 | /** 98 | * 时间戳格式化 99 | */ 100 | formatTimestamp(timestamp: number): string { 101 | const date = new Date(timestamp); 102 | return `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`; 103 | }, 104 | 105 | /** 106 | * 获取设备图标(根据实际类型映射) 107 | */ 108 | getDeviceIcon(deviceType: string): string { 109 | const iconMap: Record = { 110 | 'GG': '/images/w/default.png', 111 | }; 112 | return iconMap[deviceType] || '/images/w/default.png'; 113 | }, 114 | 115 | /** 116 | * 错误统一处理 117 | */ 118 | handleDataError(message: string) { 119 | wx.showToast({ 120 | title: `设备加载失败: ${message}`, 121 | icon: 'none', 122 | duration: 3000 123 | }); 124 | 125 | // 失败时降级显示空状态 126 | this.setData({ deviceList: [] }); 127 | }, 128 | /** 129 | * 生命周期函数--监听页面初次渲染完成 130 | */ 131 | onReady() { 132 | 133 | }, 134 | 135 | /** 136 | * 生命周期函数--监听页面显示 137 | */ 138 | onShow() { 139 | 140 | }, 141 | 142 | /** 143 | * 生命周期函数--监听页面隐藏 144 | */ 145 | onHide() { 146 | 147 | }, 148 | 149 | /** 150 | * 生命周期函数--监听页面卸载 151 | */ 152 | onUnload() { 153 | 154 | }, 155 | 156 | /** 157 | * 页面相关事件处理函数--监听用户下拉动作 158 | */ 159 | onPullDownRefresh() { 160 | 161 | }, 162 | 163 | /** 164 | * 页面上拉触底事件的处理函数 165 | */ 166 | onReachBottom() { 167 | 168 | }, 169 | 170 | /** 171 | * 用户点击右上角分享 172 | */ 173 | onShareAppMessage() { 174 | 175 | }, 176 | handleScan() { 177 | wx.scanCode({ 178 | success: res => this.handleScanSuccess(res.result), 179 | fail: err => this.handleScanError(err) 180 | }); 181 | }, 182 | /** 183 | * 扫码成功处理 184 | */ 185 | handleScanSuccess(code: string) { 186 | this.fetchDeviceInfo(code); 187 | }, 188 | /** 189 | * 扫码错误处理 190 | */ 191 | handleScanError(err: WechatMiniprogram.GeneralCallbackResult) { 192 | 193 | wx.showToast({ 194 | title: '扫码失败', 195 | icon: 'none' 196 | }); 197 | }, 198 | /** 199 | * 获取设备信息 200 | * 通过设备编码访问服务器,获取设备的详细信息 201 | */ 202 | fetchDeviceInfo(code: string) { 203 | wx.showLoading({ title: '加载设备信息...' }); 204 | const token: TokenType = wx.getStorageSync('token'); 205 | wx.request({ 206 | url: getApp().globalData.baseurl + 'Plan/AddPlanInfoWithID/', // 替换为实际的服务器 URL 207 | method: 'POST', 208 | header: { 209 | 'Authorization': `Bearer ${token}` // 将 Token 放入请求头 210 | }, 211 | data: { deviceCode: code }, 212 | success: res => { 213 | // 处理服务器返回的数据 214 | const data = res.data 215 | if (data.result === 'success') { 216 | this.handleDeviceData(res.data.device); 217 | } else if (data.result === 'fail') { 218 | wx.showToast({ 219 | title: '不存在该设备', 220 | icon: 'none' 221 | }); 222 | }else if(data.result === 'have') 223 | { 224 | wx.showToast({ 225 | title: '已经添加该设备', 226 | icon: 'none' 227 | }); 228 | }else if(data.result === 'no'){ 229 | wx.showToast({ 230 | title: '已经添加该设备', 231 | icon: 'none' 232 | }); 233 | }else{ 234 | wx.showToast({ 235 | title: '错误', 236 | icon: 'error' 237 | }); 238 | } 239 | }, 240 | fail: err => { 241 | wx.showToast({ 242 | title: '设备信息获取失败', 243 | icon: 'none' 244 | }); 245 | }, 246 | complete: () => { 247 | wx.hideLoading(); 248 | } 249 | }); 250 | }, 251 | /** 252 | * 处理设备数据 253 | * 获取设备信息后,创建新的设备对象并添加到设备列表 254 | */ 255 | handleDeviceData(deviceData:any) { 256 | this.processApiData(deviceData) 257 | wx.showToast({ 258 | title: '设备添加成功', 259 | icon: 'success' 260 | }); 261 | }, 262 | 263 | /** 264 | * 设备点击事件(需要在WXML中绑定) 265 | */ 266 | onDeviceTap(e: WechatMiniprogram.TouchEvent) { 267 | const deviceId = e.currentTarget.dataset.id as number; 268 | const device = this.data.deviceList.find(d => d.id === deviceId); 269 | const NewDate = {"id": deviceId, "url" : device.icon} 270 | const ND = encodeURIComponent(JSON.stringify(NewDate)) 271 | if (device) { 272 | wx.navigateTo({ 273 | url: `/pages/device/devicexijie/xijie?data=${ND}` 274 | }); 275 | } 276 | } 277 | 278 | }) -------------------------------------------------------------------------------- /微信小程序部分/miniprogram/pages/device/device.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | 12 | 13 | 18 | 19 | 20 | {{item.name}} 21 | {{item.status}} 22 | 最后更新:{{item.updateTime}} 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /微信小程序部分/miniprogram/pages/device/device.wxss: -------------------------------------------------------------------------------- 1 | /* 页面整体容器 */ 2 | .container { 3 | height: 100vh; 4 | display: flex; 5 | flex-direction: column; 6 | background-color: #f8f8f8; /* 背景色 */ 7 | box-sizing: border-box; 8 | } 9 | 10 | /* 添加设备区域 */ 11 | .add-device-area { 12 | height: 20vh; 13 | background: #f8f8f8; 14 | display: flex; 15 | justify-content: center; 16 | align-items: center; 17 | } 18 | 19 | /* 扫描按钮 */ 20 | .scan-button { 21 | background: none; 22 | border: none; 23 | display: flex; 24 | flex-direction: column; 25 | align-items: center; 26 | line-height: 1.5; 27 | padding: 10rpx; 28 | transition: transform 0.3s ease-in-out, box-shadow 0.3s ease; /* 添加动画效果 */ 29 | } 30 | 31 | /* 扫描按钮图标 */ 32 | .scan-icon { 33 | width: 80rpx; 34 | height: 80rpx; 35 | margin-bottom: 20rpx; 36 | } 37 | 38 | /* 设备列表区域 */ 39 | .device-list { 40 | flex: 1; 41 | padding: 20rpx 30rpx; 42 | } 43 | 44 | /* 设备项 */ 45 | .device-item { 46 | background: white; 47 | border-radius: 16rpx; 48 | padding: 30rpx; 49 | margin-bottom: 20rpx; 50 | box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08); 51 | display: flex; 52 | align-items: center; 53 | position: relative; 54 | transition: transform 0.3s ease-in-out, box-shadow 0.3s ease; /* 添加动画效果 */ 55 | } 56 | 57 | /* 设备图标 */ 58 | .device-icon { 59 | width: 100rpx; 60 | height: 100rpx; 61 | margin-right: 30rpx; 62 | border-radius: 12rpx; /* 圆角效果 */ 63 | box-shadow: 0 4rpx 8rpx rgba(0, 0, 0, 0.12); /* 图标阴影效果 */ 64 | transition: transform 0.3s ease-in-out; /* 动画效果 */ 65 | } 66 | 67 | /* 设备信息区域 */ 68 | .device-info { 69 | flex: 1; 70 | display: flex; 71 | flex-direction: column; 72 | } 73 | 74 | /* 设备名称 */ 75 | .device-name { 76 | font-size: 34rpx; 77 | color: #333; 78 | margin-bottom: 10rpx; 79 | font-weight: bold; /* 更加突出设备名称 */ 80 | } 81 | 82 | /* 设备状态 */ 83 | .device-status { 84 | font-size: 28rpx; 85 | color: #666; 86 | } 87 | 88 | /* 更新时间 */ 89 | .update-time { 90 | font-size: 24rpx; 91 | color: #999; 92 | margin-top: 8rpx; 93 | } 94 | 95 | /* 状态指示器 */ 96 | .status-indicator { 97 | width: 16rpx; 98 | height: 16rpx; 99 | border-radius: 50%; 100 | background: #ccc; 101 | margin-left: 20rpx; 102 | transition: background 0.3s ease, transform 0.3s ease; /* 平滑过渡 */ 103 | } 104 | 105 | /* 激活状态指示器 */ 106 | .status-indicator.active { 107 | background: #09bb07; 108 | transform: scale(1.2); /* 增加激活状态的缩放效果 */ 109 | } 110 | 111 | /* 点击状态效果 */ 112 | .device-item:active { 113 | transform: scale(0.98); /* 点击时稍微缩小 */ 114 | box-shadow: 0 8rpx 16rpx rgba(0, 0, 0, 0.15); /* 更强的阴影效果 */ 115 | } 116 | 117 | .scan-button:active { 118 | transform: scale(0.98); /* 扫描按钮点击效果 */ 119 | box-shadow: 0 8rpx 16rpx rgba(0, 0, 0, 0.15); /* 点击时更强的阴影效果 */ 120 | } 121 | -------------------------------------------------------------------------------- /微信小程序部分/miniprogram/pages/device/devicexijie/auto/auto.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": {} 3 | } -------------------------------------------------------------------------------- /微信小程序部分/miniprogram/pages/device/devicexijie/auto/auto.ts: -------------------------------------------------------------------------------- 1 | // 获取应用实例 2 | const app = getApp() 3 | 4 | interface RecordItem { 5 | startTime: string; 6 | endTime: string; 7 | } 8 | 9 | Page({ 10 | data: { 11 | startTime: '', // 起始时间 12 | endTime: '', // 结束时间 13 | records: [] as RecordItem[], // 记录列表,指定为 RecordItem 类型数组 14 | maxRecords: 4, // 最大记录数 15 | errorMessage: '', // 错误提示 16 | token: String, 17 | PlanId: String 18 | }, 19 | 20 | // 页面加载时加载已有记录 21 | onLoad(PlanId: any) { 22 | this.setData( 23 | { 24 | token:wx.getStorageSync('token'), 25 | PlanId:PlanId.id 26 | } 27 | ) 28 | this.loadRecords(PlanId); 29 | }, 30 | 31 | // 加载已有记录(从本地存储或后台获取) 32 | loadRecords(PlanId: any) { 33 | // 从云端获取 34 | wx.request({ 35 | url: getApp().globalData.baseurl + 'Plan/getAutoTimeWithPlanID/', // 36 | method: 'POST', 37 | header: { 38 | 'Authorization': `Bearer ${this.data.token}` // 将 Token 放入请求头 39 | }, 40 | data: { PlanId: PlanId.id }, 41 | success: res => { 42 | // 处理服务器返回的数据 43 | if (res.data.result === 'success') { 44 | // 转换字段名:start_time → startTime,end_time → endTime 45 | const records = res.data.data.map(item => ({ 46 | startTime: item.start_time, 47 | endTime: item.end_time 48 | })); 49 | this.setData({ records }); 50 | wx.setStorageSync('records', records); // 可选缓存 51 | } else if( res.data.result ==='fail'){ 52 | wx.showToast({ title: '当前没有设置自动启停时间', icon: 'none' }); 53 | }else{ 54 | wx.showToast({ title: '错误', icon: 'error' }); 55 | } 56 | }, 57 | fail: err => { 58 | console.error('获取设备信息失败:', err); 59 | wx.showToast({ 60 | title: '设备信息获取失败', 61 | icon: 'none' 62 | }); 63 | }, 64 | complete: () => { 65 | wx.hideLoading(); 66 | } 67 | }); 68 | // const records = wx.getStorageSync('records') || []; 69 | // this.setData({ 70 | // records: records 71 | // }); 72 | }, 73 | 74 | // 设置起始时间 75 | setStartTime(e: any) { 76 | this.setData({ 77 | startTime: e.detail.value 78 | }); 79 | }, 80 | 81 | // 设置结束时间 82 | setEndTime(e: any) { 83 | this.setData({ 84 | endTime: e.detail.value 85 | }); 86 | }, 87 | 88 | // 创建新记录 89 | createRecord() { 90 | if (this.data.records.length >= this.data.maxRecords) { 91 | this.setData({ 92 | errorMessage: '最多只能创建四条记录!' 93 | }); 94 | return; 95 | } 96 | 97 | if (!this.data.startTime || !this.data.endTime) { 98 | this.setData({ 99 | errorMessage: '起始时间和结束时间不能为空!' 100 | }); 101 | return; 102 | } 103 | 104 | if (this.data.startTime >= this.data.endTime) { 105 | this.setData({ 106 | errorMessage: '起始时间不能晚于结束时间!' 107 | }); 108 | return; 109 | } 110 | 111 | const newRecord: RecordItem = { 112 | startTime: this.data.startTime, 113 | endTime: this.data.endTime 114 | }; 115 | 116 | const updatedRecords = [...this.data.records, newRecord]; 117 | 118 | // 根据起始时间排序记录 119 | updatedRecords.sort((a, b) => a.startTime.localeCompare(b.startTime)); 120 | 121 | this.setData({ 122 | records: updatedRecords, 123 | startTime: '', 124 | endTime: '', 125 | errorMessage: '' 126 | }); 127 | 128 | wx.setStorageSync('records', updatedRecords); // 更新本地存储 129 | }, 130 | 131 | // 删除记录 132 | // deleteRecord(e: any) { 133 | // const index = e.currentTarget.dataset.index; 134 | // const records = this.data.records; 135 | // records.splice(index, 1); // 删除记录 136 | // this.setData({ 137 | // records: records 138 | // }); 139 | // wx.setStorageSync('records', records); // 更新本地存储 140 | // }, 141 | // 删除记录 142 | deleteRecord(e: any) { 143 | const index = e.currentTarget.dataset.index; 144 | 145 | wx.showModal({ 146 | title: "确认删除", 147 | content: "确定要删除这条记录吗?", 148 | success: (res) => { 149 | if (res.confirm) { 150 | // 用户点击确定后执行删除 151 | const records = [...this.data.records]; // 创建新数组避免污染原数据 152 | records.splice(index, 1); 153 | 154 | this.setData({ 155 | records: records 156 | }); 157 | 158 | } 159 | } 160 | }); 161 | }, 162 | 163 | // 保存记录 164 | saveRecords() { 165 | // wx.setStorageSync('records', this.data.records); 166 | wx.request({ 167 | url: getApp().globalData.baseurl + 'Plan/SaveAutoRecords/', // 168 | method: 'POST', 169 | header: { 170 | 'Authorization': `Bearer ${this.data.token}` // 将 Token 放入请求头 171 | }, 172 | data: { records: this.data.records , 173 | PlanId: this.data.PlanId 174 | }, 175 | success: res => { 176 | // 处理服务器返回的数据 177 | if (res.data.result === 'success') { 178 | console.log(res) 179 | } else { 180 | wx.showToast({ 181 | title: '设备信息获取失败', 182 | icon: 'none' 183 | }); 184 | } 185 | }, 186 | fail: err => { 187 | console.error('获取设备信息失败:', err); 188 | wx.showToast({ 189 | title: '设备信息获取失败', 190 | icon: 'none' 191 | }); 192 | }, 193 | complete: () => { 194 | wx.hideLoading(); 195 | } 196 | }); 197 | wx.showToast({ 198 | title: '记录已保存', 199 | icon: 'success' 200 | }); 201 | }, 202 | 203 | // 退出页面 204 | exitPage() { 205 | wx.showModal({ 206 | title: "确认退出", 207 | content: "确定要退出吗?", 208 | success: (res) => { 209 | if (res.confirm) { 210 | wx.navigateBack(); // 返回上一页 211 | } 212 | } 213 | }); 214 | } 215 | }); 216 | -------------------------------------------------------------------------------- /微信小程序部分/miniprogram/pages/device/devicexijie/auto/auto.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{errorMessage}} 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | {{startTime || '请选择起始时间'}} 13 | 14 | 15 | 16 | 17 | 18 | 19 | {{endTime || '请选择结束时间'}} 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 起始时间: {{item.startTime}} 结束时间: {{item.endTime}} 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /微信小程序部分/miniprogram/pages/device/devicexijie/auto/auto.wxss: -------------------------------------------------------------------------------- 1 | /* 页面整体容器 */ 2 | .container { 3 | display: flex; 4 | flex-direction: column; 5 | padding: 20px; 6 | background-color: #f5f5f5; 7 | height: 100vh; 8 | } 9 | 10 | /* 固定起始时间模块 */ 11 | .fixed-start-time-section { 12 | background-color: #ffffff; 13 | border-radius: 10px; 14 | padding: 20px; 15 | box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1); 16 | margin-bottom: 30px; 17 | flex-shrink: 0; /* 防止此区域收缩 */ 18 | position: sticky; 19 | top: 0; 20 | z-index: 10; 21 | width: 100%; 22 | } 23 | 24 | /* 输入框与选择框 */ 25 | .input-group { 26 | margin-bottom: 20px; 27 | } 28 | 29 | .input-group label { 30 | font-size: 16px; 31 | color: #333; 32 | } 33 | 34 | .picker { 35 | padding: 10px; 36 | background-color: #e9ecef; 37 | border-radius: 5px; 38 | text-align: center; 39 | } 40 | 41 | .create-button { 42 | width: 100%; 43 | padding: 12px; 44 | background-color: #007bff; 45 | color: white; 46 | border: none; 47 | border-radius: 5px; 48 | font-size: 16px; 49 | margin-top: 20px; 50 | } 51 | 52 | /* 错误提示 */ 53 | .error-message { 54 | color: red; 55 | text-align: center; 56 | margin-bottom: 10px; 57 | } 58 | 59 | /* 记录列表 */ 60 | .record-list { 61 | flex-grow: 1; /* 让记录区域占满剩余空间 */ 62 | overflow-y: auto; /* 添加滚动条 */ 63 | margin-bottom: 30px; 64 | } 65 | 66 | .record-item { 67 | display: flex; 68 | justify-content: space-between; 69 | align-items: center; 70 | background-color: #ffffff; 71 | padding: 15px; 72 | margin-bottom: 15px; /* 间隔设置 */ 73 | border-radius: 5px; 74 | box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.1); 75 | } 76 | 77 | /* 删除按钮 */ 78 | .delete-button { 79 | background-color: red; 80 | color: white; 81 | padding: 5px 10px; 82 | border: none; 83 | border-radius: 5px; 84 | } 85 | 86 | /* 固定底部按钮 */ 87 | .button-group { 88 | display: flex; 89 | justify-content: space-between; 90 | position: fixed; 91 | bottom: 0; 92 | width: 100%; 93 | padding: 10px 20px; 94 | background-color: #fff; 95 | box-shadow: 0px -2px 5px rgba(0, 0, 0, 0.1); 96 | } 97 | 98 | .save-button, .exit-button { 99 | width: 48%; 100 | padding: 12px; 101 | border: none; 102 | border-radius: 5px; 103 | font-size: 16px; 104 | } 105 | 106 | .save-button { 107 | background-color: #28a745; 108 | color: white; 109 | } 110 | 111 | .exit-button { 112 | background-color: #dc3545; 113 | color: white; 114 | } 115 | -------------------------------------------------------------------------------- /微信小程序部分/miniprogram/pages/device/devicexijie/personality/personality.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": {} 3 | } -------------------------------------------------------------------------------- /微信小程序部分/miniprogram/pages/device/devicexijie/personality/personality.ts: -------------------------------------------------------------------------------- 1 | // pages/device/devicexijie/personality/personality.ts 2 | Page({ 3 | 4 | /** 5 | * 页面的初始数据 6 | */ 7 | data: { 8 | list: [], 9 | activeId: null, // 当前激活的ID 10 | selectedItem: null, // 当前选中的项 11 | token: String 12 | }, 13 | /** 14 | * 生命周期函数--监听页面加载 15 | */ 16 | onLoad(PlanId: any) { 17 | this.setData( 18 | { 19 | token:wx.getStorageSync('token') 20 | } 21 | ) 22 | // 从云端获取每个模式的内容 获取模式内容 23 | wx.request({ 24 | url: getApp().globalData.baseurl + 'Plan/getModelInfoWithPlanID/', // 25 | method: 'POST', 26 | header: { 27 | 'Authorization': `Bearer ${this.data.token}` // 将 Token 放入请求头 28 | }, 29 | data: { PlanId: PlanId }, 30 | success: res => { 31 | // 处理服务器返回的数据 32 | if (res) { 33 | console.log(res) 34 | this.setData({ 35 | list:res.data.data 36 | }) 37 | 38 | } else { 39 | wx.showToast({ 40 | title: '模式信息获取失败', 41 | icon: 'none' 42 | }); 43 | } 44 | }, 45 | fail: err => { 46 | console.error('模式信息获取失败:', err); 47 | wx.showToast({ 48 | title: '模式信息获取失败', 49 | icon: 'error' 50 | }); 51 | }, 52 | }); 53 | }, 54 | 55 | /** 56 | * 生命周期函数--监听页面初次渲染完成 57 | */ 58 | onReady() { 59 | 60 | }, 61 | 62 | /** 63 | * 生命周期函数--监听页面显示 64 | */ 65 | onShow() { 66 | 67 | }, 68 | 69 | /** 70 | * 生命周期函数--监听页面隐藏 71 | */ 72 | onHide() { 73 | 74 | }, 75 | 76 | /** 77 | * 生命周期函数--监听页面卸载 78 | */ 79 | onUnload() { 80 | 81 | }, 82 | 83 | /** 84 | * 页面相关事件处理函数--监听用户下拉动作 85 | */ 86 | onPullDownRefresh() { 87 | 88 | }, 89 | 90 | /** 91 | * 页面上拉触底事件的处理函数 92 | */ 93 | onReachBottom() { 94 | 95 | }, 96 | 97 | /** 98 | * 用户点击右上角分享 99 | */ 100 | onShareAppMessage() { 101 | 102 | }, 103 | 104 | // 处理按钮点击事件 105 | onButtonClick(e: any) { 106 | wx.showModal({ 107 | title: "点击确认", 108 | content: "确定点击该按钮吗", 109 | success: (res) => { 110 | if (res.confirm) { 111 | const clickedId = e.currentTarget.dataset.id; // 获取当前点击行的ID 112 | this.setData({ 113 | activeId: this.data.activeId === clickedId ? null : clickedId // 切换激活状态 114 | }); 115 | 116 | } 117 | } 118 | }); 119 | }, 120 | 121 | // 处理点击行显示详情 122 | onRowClick(e: any) { 123 | const selectedItem = this.data.list.find(item => item.id === e.currentTarget.dataset.id); 124 | this.setData({ 125 | selectedItem: selectedItem || null // 设置选中的项数据,确保为null时不会报错 126 | }); 127 | } 128 | }) -------------------------------------------------------------------------------- /微信小程序部分/miniprogram/pages/device/devicexijie/personality/personality.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {{item.name}} 9 | {{item.description}} 10 | 11 | 12 | 13 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 详细信息 27 | 28 | 名称:{{selectedItem.name}} 描述:{{selectedItem.description}} 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /微信小程序部分/miniprogram/pages/device/devicexijie/personality/personality.wxss: -------------------------------------------------------------------------------- 1 | /* 页面整体容器 */ 2 | .container { 3 | padding: 10px; 4 | background-color: #f5f5f5; 5 | display: flex; 6 | flex-direction: column; 7 | height: 100vh; /* 全屏显示 */ 8 | box-sizing: border-box; /* 防止溢出 */ 9 | } 10 | 11 | /* 列表区域,占大部分屏幕 */ 12 | .list-area { 13 | flex: 2; /* 控制列表占2/3的空间 */ 14 | overflow-y: auto; /* 当内容过多时允许滚动 */ 15 | } 16 | 17 | /* 每一行项 */ 18 | .list-item { 19 | display: flex; 20 | justify-content: space-between; 21 | align-items: center; 22 | padding: 10px; 23 | background-color: #ffffff; 24 | border-radius: 5px; 25 | box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); 26 | margin-bottom: 10px; 27 | height: 80px; /* 行高设置 */ 28 | } 29 | 30 | /* 显示每一行的名称和描述 */ 31 | .item-content { 32 | flex: 2; /* 名称和描述占2/3空间 */ 33 | padding-right: 10px; 34 | } 35 | 36 | .item-name { 37 | font-size: 16px; 38 | font-weight: bold; 39 | } 40 | 41 | .item-description { 42 | font-size: 14px; 43 | color: #888; 44 | } 45 | 46 | /* 按钮样式 */ 47 | .action-button { 48 | padding: 6px 14px; /* 调整按钮大小 */ 49 | font-size: 14px; /* 按钮文本大小 */ 50 | color: white; 51 | border: none; 52 | border-radius: 5px; 53 | margin-left: 10px; 54 | flex-shrink: 0; 55 | font-weight: bold; 56 | } 57 | 58 | /* 下方显示详情区域 */ 59 | .details-area { 60 | flex: 1; /* 详细信息占1/3的空间 */ 61 | background-color: #ffffff; 62 | padding: 20px; 63 | margin-top: 20px; 64 | border-radius: 10px; 65 | box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.1); 66 | overflow: hidden; /* 避免溢出 */ 67 | } 68 | 69 | .details-title { 70 | font-size: 18px; 71 | font-weight: bold; 72 | margin-bottom: 0px; 73 | } 74 | 75 | .details-content text { 76 | display: block; 77 | font-size: 14px; 78 | margin-bottom: 0px; 79 | } 80 | 81 | .detail-image { 82 | max-width: 100%; 83 | height: auto; 84 | margin-top: 0px; 85 | } 86 | 87 | /* 防止内容溢出,保证页面自适应 */ 88 | body { 89 | margin: 0; 90 | padding: 0; 91 | box-sizing: border-box; 92 | } 93 | -------------------------------------------------------------------------------- /微信小程序部分/miniprogram/pages/device/devicexijie/xijie.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": {} 3 | } -------------------------------------------------------------------------------- /微信小程序部分/miniprogram/pages/device/devicexijie/xijie.ts: -------------------------------------------------------------------------------- 1 | // pages/device/devicexijie/xijie.ts 2 | Page({ 3 | 4 | /** 5 | * 页面的初始数据 6 | */ 7 | data: { 8 | id: String, 9 | url: String, 10 | token: String 11 | }, 12 | 13 | /** 14 | * 生命周期函数--监听页面加载 15 | */ 16 | onLoad(options: { data?: string }) { 17 | this.setData( 18 | { 19 | token:wx.getStorageSync('token') 20 | } 21 | ) 22 | if (options.data) { 23 | const decodedData = JSON.parse(decodeURIComponent(options.data)); 24 | this.setData({ 25 | url: decodedData['url'], 26 | id: decodedData['id'] 27 | }); 28 | } 29 | 30 | }, 31 | 32 | /** 33 | * 生命周期函数--监听页面初次渲染完成 34 | */ 35 | onReady() { 36 | 37 | }, 38 | 39 | /** 40 | * 生命周期函数--监听页面显示 41 | */ 42 | onShow() { 43 | 44 | }, 45 | 46 | /** 47 | * 生命周期函数--监听页面隐藏 48 | */ 49 | onHide() { 50 | 51 | }, 52 | 53 | /** 54 | * 生命周期函数--监听页面卸载 55 | */ 56 | onUnload() { 57 | 58 | }, 59 | 60 | /** 61 | * 页面相关事件处理函数--监听用户下拉动作 62 | */ 63 | onPullDownRefresh() { 64 | 65 | }, 66 | 67 | /** 68 | * 页面上拉触底事件的处理函数 69 | */ 70 | onReachBottom() { 71 | 72 | }, 73 | 74 | /** 75 | * 用户点击右上角分享 76 | */ 77 | onShareAppMessage() { 78 | 79 | }, 80 | handleAutomation(){ 81 | console.log(1) 82 | wx.navigateTo({ 83 | url: `/pages/device/devicexijie/auto/auto?id=${this.data.id}` 84 | }); 85 | }, 86 | handleCustom(){ 87 | console.log(2) 88 | wx.navigateTo({ 89 | url: `/pages/device/devicexijie/personality/personality?id=${this.data.id}` 90 | }); 91 | }, 92 | 93 | 94 | handleActmation(){ 95 | wx.request({ 96 | url: getApp().globalData.baseurl + 'Plan/PlanAct/', // 替换为实际的服务器 URL 97 | method: 'POST', 98 | header: { 99 | 'Authorization': `Bearer ${this.data.token}` // 将 Token 放入请求头 100 | }, 101 | data: { PlanId: this.data.id}, 102 | success: res => { 103 | // 处理服务器返回的数据 104 | const status = res.data.status; 105 | if (status == 0){ 106 | wx.showToast({ 107 | title: '启动成功', 108 | icon: 'success' 109 | }); 110 | }else if( status == 1){ 111 | wx.showToast({ 112 | title: '正在执行请勿重复', 113 | icon: 'loading' 114 | }); 115 | }else if(status == 2){ 116 | wx.showToast({ 117 | title: '请一分钟后重试', 118 | icon: 'loading' 119 | }); 120 | }else{ 121 | wx.showToast({ 122 | title: '404', 123 | icon: 'error' 124 | }); 125 | } 126 | }, 127 | fail: err => { 128 | wx.showToast({ 129 | title: '启动失败', 130 | icon: 'error' 131 | }); 132 | }, 133 | }); 134 | }, 135 | handleCancelmation(){ 136 | wx.request({ 137 | url: getApp().globalData.baseurl + 'Plan/CancelAct/', // 替换为实际的服务器 URL 138 | method: 'POST', 139 | header: { 140 | 'Authorization': `Bearer ${this.data.token}` // 将 Token 放入请求头 141 | }, 142 | data: { PlanId: this.data.id, 143 | }, 144 | success: res => { 145 | // 处理服务器返回的数据 146 | const status = res.data.status; 147 | if (status == 0){ 148 | wx.showToast({ 149 | title: '取消成功', 150 | icon: 'success' 151 | }); 152 | }else if( status == 1){ 153 | wx.showToast({ 154 | title: '无任务在执行', 155 | icon: 'error' 156 | }); 157 | }else if (status == 2){ 158 | wx.showToast({ 159 | title: '点击过于频繁', 160 | icon: 'loading' 161 | }); 162 | }else{ 163 | wx.showToast({ 164 | title: '错误', 165 | icon: 'error' 166 | }); 167 | } 168 | }, 169 | fail: err => { 170 | wx.showToast({ 171 | title: '取消失败', 172 | icon: 'error' 173 | }); 174 | }, 175 | }); 176 | } 177 | }) -------------------------------------------------------------------------------- /微信小程序部分/miniprogram/pages/device/devicexijie/xijie.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 智能电气控制柜\n当前状态:运行中 6 | 7 | 8 | 9 | 10 | 11 | 15 | 18 | 19 | 20 | 23 | 24 | 25 | 28 | 29 | 30 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /微信小程序部分/miniprogram/pages/device/devicexijie/xijie.wxss: -------------------------------------------------------------------------------- 1 | /* 页面整体容器 */ 2 | .container { 3 | height: 100vh; 4 | display: flex; 5 | flex-direction: column; 6 | background-color: #f8f9fa; /* 淡灰色背景,显得更加高级 */ 7 | padding: 20px; 8 | box-sizing: border-box; 9 | font-family: 'Arial', sans-serif; /* 更加现代的字体 */ 10 | } 11 | 12 | /* 顶部设备展示区域 */ 13 | .top-section { 14 | height: 30vh; /* 增加顶部区域的高度 */ 15 | width: 55%; 16 | display: flex; 17 | flex-direction: column; 18 | align-items: center; 19 | justify-content: center; 20 | background-color: #ffffff; 21 | border-radius: 16px; 22 | padding: 20px; 23 | box-shadow: 0px 8px 16px rgba(0, 0, 0, 0.15); /* 更强的阴影效果,增加层次感 */ 24 | transition: all 0.3s ease; /* 添加平滑过渡效果 */ 25 | } 26 | 27 | /* 顶部图像 */ 28 | .device-image { 29 | width: 200rpx; /* 图像大小调整 */ 30 | height: 200rpx; 31 | margin-bottom: 20rpx; 32 | border-radius: 50%; 33 | box-shadow: 0px 6px 12px rgba(0, 0, 0, 0.1); /* 圆形图像的阴影 */ 34 | transition: transform 0.3s ease; /* 添加动画效果 */ 35 | } 36 | 37 | .device-desc { 38 | text-align: center; 39 | font-size: 26rpx; 40 | color: #495057; /* 更深的灰色,显得更优雅 */ 41 | line-height: 1.5; 42 | font-weight: bold; 43 | } 44 | 45 | /* 按钮控制区域 */ 46 | .button-container { 47 | flex: 1; 48 | display: flex; 49 | flex-direction: column; 50 | justify-content: space-between; 51 | padding: 20px; 52 | width: 100%; 53 | margin-top: 30rpx; 54 | } 55 | 56 | /* 按钮样式 */ 57 | .control-btn { 58 | padding: 18rpx; 59 | font-size: 30rpx; 60 | border-radius: 16rpx; 61 | display: flex; 62 | align-items: center; 63 | justify-content: center; 64 | margin: 15rpx 0; 65 | transition: all 0.3s ease; /* 平滑的过渡效果 */ 66 | box-shadow: 0 6px 12px rgba(0, 0, 0, 0.1); /* 增强阴影效果 */ 67 | font-weight: bold; 68 | width: 100%; 69 | background-color: #eeeeee; /* 按钮背景色更柔和 */ 70 | border: none; 71 | outline: none; 72 | } 73 | .act-btn{ 74 | background-color: brown; 75 | color: white; 76 | } 77 | .cancel-btn{ 78 | background-color: rgb(25, 53, 70); 79 | color: white; 80 | } 81 | .auto-btn { 82 | background-color: #4CAF50; 83 | color: white; 84 | } 85 | 86 | .custom-btn { 87 | background-color: #2196F3; 88 | color: white; 89 | } 90 | 91 | .status-btn { 92 | background-color: #9E9E9E; 93 | color: white; 94 | } 95 | 96 | /* 按钮点击效果 */ 97 | .control-btn:active { 98 | opacity: 0.8; 99 | transform: scale(0.98); /* 按钮点击时的缩放效果 */ 100 | } 101 | 102 | /* 按钮悬停效果 */ 103 | .control-btn:hover { 104 | transform: scale(1.05); /* 悬停时放大 */ 105 | box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2); /* 鼠标悬停时增加阴影 */ 106 | } 107 | 108 | /* 防止页面溢出,保证页面自适应 */ 109 | body { 110 | margin: 0; 111 | padding: 0; 112 | box-sizing: border-box; 113 | } 114 | -------------------------------------------------------------------------------- /微信小程序部分/miniprogram/pages/index/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": { 3 | } 4 | } -------------------------------------------------------------------------------- /微信小程序部分/miniprogram/pages/index/index.ts: -------------------------------------------------------------------------------- 1 | // pages/index/index.ts 2 | Page({ 3 | data: { 4 | currentSwiper: 0, 5 | bannerList: [ 6 | { 7 | id: 1, 8 | imageUrl: "https://i2.3conline.com/images/piclib/201203/22/batch/1/130539/1332349654033obqessjgay.jpg", 9 | link: "/pages/detail/detail?id=1", 10 | title: "智能家居新时代", 11 | description: "体验全屋智能联动" 12 | }, 13 | { 14 | id: 2, 15 | imageUrl: "https://ts1.cn.mm.bing.net/th/id/R-C.51136df0b742cdadabac92f88ba989a1?rik=Gq5BvhSCaT0NhQ&riu=http%3a%2f%2fimg.pconline.com.cn%2fimages%2fupload%2fupc%2ftx%2fwallpaper%2f1211%2f23%2fc2%2f16013920_1353654565612.jpg&ehk=QlYeWMk3KcFlzHLEp0xY8yOi0Uc1S03XwjlVmU7Nk5o%3d&risl=&pid=ImgRaw&r=0", 16 | link: "/pages/detail/detail?id=2", 17 | title: "安全防护系统", 18 | description: "24小时智能安防监控" 19 | }, 20 | { 21 | id: 3, 22 | imageUrl: "https://pic1.zhimg.com/v2-202ef5254b6dcbd45053b42f81d12f2a_r.jpg?source=1940ef5c", 23 | link: "/pages/about/about", 24 | title: "环境智能调节", 25 | description: "温湿度自动控制系统" 26 | } 27 | ] 28 | }, 29 | onSwiperChange(e: { detail: { current: number } }) { 30 | this.setData({ 31 | currentSwiper: e.detail.current 32 | }) 33 | }, 34 | onBannerTap(e: { currentTarget: { dataset: { id: number } } }) { 35 | const bannerId = e.currentTarget.dataset.id; 36 | const banner = this.data.bannerList.find(item => item.id === bannerId); 37 | if (banner) { 38 | wx.navigateTo({ 39 | url: banner.link 40 | }); 41 | } 42 | } 43 | }); -------------------------------------------------------------------------------- /微信小程序部分/miniprogram/pages/index/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 13 | 14 | 19 | 20 | 21 | {{item.title}} 22 | {{item.description}} 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /微信小程序部分/miniprogram/pages/index/index.wxss: -------------------------------------------------------------------------------- 1 | /* pages/index/index.wxss */ 2 | .swiper-container { 3 | width: 100%; 4 | height: 75vh; 5 | position: relative; 6 | padding: 20rpx 0; 7 | } 8 | 9 | .enhanced-swiper { 10 | width: 92%; 11 | height: 100%; 12 | margin: 0 auto; 13 | } 14 | 15 | .slide-wrapper { 16 | width: 100%; 17 | height: 100%; 18 | position: relative; 19 | border-radius: 24rpx; 20 | overflow: hidden; 21 | box-shadow: 0 12rpx 24rpx rgba(0,0,0,0.15); 22 | transform: scale(0.96); 23 | transition: transform 0.3s ease; 24 | } 25 | 26 | .swiper-item-active .slide-wrapper { 27 | transform: scale(1); 28 | box-shadow: 0 16rpx 32rpx rgba(0,0,0,0.2); 29 | } 30 | 31 | .bordered-image { 32 | width: 100%; 33 | height: 100%; 34 | position: relative; 35 | border: 6rpx solid #fff; 36 | border-radius: 20rpx; 37 | box-sizing: border-box; 38 | } 39 | 40 | .gradient-overlay { 41 | position: absolute; 42 | bottom: 0; 43 | left: 0; 44 | right: 0; 45 | padding: 40rpx; 46 | background: linear-gradient(transparent, rgba(0,0,0,0.7)); 47 | } 48 | 49 | .slide-title { 50 | display: block; 51 | color: #fff; 52 | font-size: 40rpx; 53 | font-weight: 600; 54 | margin-bottom: 12rpx; 55 | text-shadow: 0 2rpx 4rpx rgba(0,0,0,0.3); 56 | } 57 | 58 | .slide-desc { 59 | color: rgba(255,255,255,0.9); 60 | font-size: 28rpx; 61 | display: block; 62 | line-height: 1.4; 63 | } 64 | 65 | /* 自定义指示器 */ 66 | .custom-indicator { 67 | position: absolute; 68 | bottom: 40rpx; 69 | left: 50%; 70 | transform: translateX(-50%); 71 | display: flex; 72 | gap: 16rpx; 73 | } 74 | 75 | .dot { 76 | width: 24rpx; 77 | height: 24rpx; 78 | border-radius: 50%; 79 | background: rgba(255,255,255,0.5); 80 | transition: all 0.3s ease; 81 | } 82 | 83 | .dot.active { 84 | width: 48rpx; 85 | border-radius: 12rpx; 86 | background: #fff; 87 | } -------------------------------------------------------------------------------- /微信小程序部分/miniprogram/pages/login/login.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": {} 3 | } -------------------------------------------------------------------------------- /微信小程序部分/miniprogram/pages/login/login.ts: -------------------------------------------------------------------------------- 1 | Page({ 2 | data: { 3 | phoneNumber: '', // 用于保存用户输入的手机号码 4 | }, 5 | 6 | /** 7 | * 处理用户输入的手机号码 8 | */ 9 | onInputPhoneNumber(e: WechatMiniprogram.BaseEvent): void { 10 | this.setData({ 11 | phoneNumber: e.detail.value, // 更新 phoneNumber 数据 12 | }); 13 | }, 14 | 15 | /** 16 | * 提交手机号码 17 | */ 18 | submitPhoneNumber(): void { 19 | const phoneNumber = this.data.phoneNumber; // 获取用户输入的手机号码 20 | // 校验手机号码是否为空 21 | if (!phoneNumber) { 22 | wx.showToast({ 23 | title: '请输入手机号码', 24 | icon: 'none', 25 | }); 26 | return; 27 | } 28 | 29 | // 校验手机号码格式(简单示例,您可以根据需要增加更严格的验证) 30 | const phoneRegex = /^[1][3-9][0-9]{9}$/; 31 | if (!phoneRegex.test(phoneNumber)) { 32 | wx.showToast({ 33 | title: '请输入有效的手机号码', 34 | icon: 'none', 35 | }); 36 | return; 37 | } 38 | 39 | // 将手机号和 uid 发送到后端 40 | const token: TokenType = wx.getStorageSync('token'); 41 | wx.request({ 42 | url: getApp().globalData.baseurl + 'Login/savePhoneNumber/', // 替换为实际的服务器 URL 43 | method: 'POST', 44 | header: { 45 | 'Authorization': `Bearer ${token}` // 将 Token 放入请求头 46 | }, 47 | data: { 48 | phoneNumber:phoneNumber, 49 | }, 50 | success: (response) => { 51 | if (response.data && response.data.result === "success") { 52 | // 手机号保存成功 53 | getApp().globalData.phoneNumber = phoneNumber 54 | wx.reLaunch({ 55 | url: '/pages/index/index', // 跳转到主页 56 | }); 57 | } else { 58 | wx.showToast({ 59 | title: '保存手机号失败,请重试', 60 | icon: 'none', 61 | }); 62 | } 63 | }, 64 | fail: (err) => { 65 | wx.showToast({ 66 | title: '请求失败,请稍后再试', 67 | icon: 'none', 68 | }); 69 | console.error('保存手机号失败:', err); 70 | } 71 | }); 72 | } 73 | }); 74 | -------------------------------------------------------------------------------- /微信小程序部分/miniprogram/pages/login/login.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 请输入您的手机号 5 | 我们需要获取您的手机号以便为您提供更好的服务。 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 我们将严格保护您的隐私,手机号仅用于身份验证。 17 | 18 | 19 | -------------------------------------------------------------------------------- /微信小程序部分/miniprogram/pages/login/login.wxss: -------------------------------------------------------------------------------- 1 | /* 页面容器 */ 2 | .container { 3 | display: flex; 4 | flex-direction: column; 5 | justify-content: center; 6 | align-items: center; 7 | height: 100vh; 8 | background-color: #f4f6f9; 9 | padding: 20rpx; 10 | } 11 | 12 | /* 页面标题 */ 13 | .header { 14 | text-align: center; 15 | margin-bottom: 40rpx; 16 | } 17 | 18 | .title { 19 | font-size: 34rpx; 20 | font-weight: bold; 21 | color: #333; 22 | } 23 | 24 | .subtitle { 25 | font-size: 24rpx; 26 | color: #666; 27 | margin-top: 10rpx; 28 | } 29 | 30 | /* 输入框样式 */ 31 | .phone-input { 32 | width: 90%; /* 输入框宽度调整为 90% */ 33 | height: 8%; 34 | padding: 30rpx; /* 增加上下内边距,确保 placeholder 不被遮挡 */ 35 | margin-top: 40rpx; 36 | font-size: 36rpx; /* 增大字体大小 */ 37 | border-radius: 30rpx; /* 圆角效果 */ 38 | border: 1px solid #007bff; /* 边框颜色调整为蓝色 */ 39 | background-color: #ffffff; /* 背景色 */ 40 | box-sizing: border-box; /* 确保 padding 和 border 包含在宽度和高度内 */ 41 | line-height: 60rpx; /* 增加行高,确保文本不拥挤 */ 42 | color: #333; /* 字体颜色 */ 43 | } 44 | 45 | /* 输入框中的 placeholder 样式 */ 46 | .phone-input::placeholder { 47 | color: #ccc; /* 提示文字颜色 */ 48 | font-size: 36rpx; /* 提示文字大小与输入字体一致 */ 49 | opacity: 1; /* 确保 placeholder 可见 */ 50 | } 51 | 52 | /* 提交按钮 */ 53 | .submit-button { 54 | background-color: #007bff; 55 | color: white; 56 | padding: 20rpx 40rpx; 57 | border-radius: 30rpx; 58 | font-size: 32rpx; 59 | text-align: center; 60 | margin-top: 40rpx; 61 | width: 80%; 62 | box-shadow: 0 4rpx 8rpx rgba(0, 0, 0, 0.1); 63 | } 64 | 65 | /* 提示信息 */ 66 | .tip { 67 | text-align: center; 68 | margin-top: 30rpx; 69 | font-size: 20rpx; 70 | color: #888; 71 | } 72 | -------------------------------------------------------------------------------- /微信小程序部分/miniprogram/pages/user/user.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": {} 3 | } -------------------------------------------------------------------------------- /微信小程序部分/miniprogram/pages/user/user.ts: -------------------------------------------------------------------------------- 1 | // pages/user/user.ts 2 | interface UserInfo { 3 | avatar: string; 4 | nickname: string; 5 | bio?: string; 6 | } 7 | 8 | Page({ 9 | data: { 10 | userInfo: { 11 | avatar: 'https://www.aurage.cn/static/userhead.png', 12 | nickname: '智能农业用户', 13 | bio: '🏠 智能农业爱好者' 14 | } as UserInfo 15 | }, 16 | 17 | // 更换头像 18 | changeAvatar() { 19 | wx.chooseMedia({ 20 | count: 1, 21 | mediaType: ['image'], 22 | success: (res) => { 23 | const tempPath = res.tempFiles[0].tempFilePath 24 | this.setData({ 25 | 'userInfo.avatar': tempPath 26 | }) 27 | // 实际开发中需上传至服务器 28 | } 29 | }) 30 | }, 31 | 32 | // 跳转编辑资料 33 | navigateToEditProfile() { 34 | wx.navigateTo({ 35 | url: '/pages/edit-profile/edit-profile' 36 | }) 37 | }, 38 | 39 | // 显示添加设备面板 40 | showAddDevicePanel() { 41 | wx.showActionSheet({ 42 | itemList: ['扫码添加设备', '手动输入设备码'], 43 | success: (res) => { 44 | if (res.tapIndex === 0) { 45 | this.scanToAddDevice() 46 | } else { 47 | this.inputDeviceCode() 48 | } 49 | } 50 | }) 51 | }, 52 | 53 | scanToAddDevice() { 54 | wx.scanCode({ 55 | success: (res) => { 56 | console.log('扫描结果:', res.result) 57 | wx.showToast({ title: '设备添加成功' }) 58 | } 59 | }) 60 | }, 61 | 62 | inputDeviceCode() { 63 | wx.showModal({ 64 | title: '输入设备码', 65 | content: '', 66 | editable: true, 67 | success: (res) => { 68 | if (res.confirm && res.content) { 69 | console.log('输入设备码:', res.content) 70 | } 71 | } 72 | }) 73 | } 74 | }) -------------------------------------------------------------------------------- /微信小程序部分/miniprogram/pages/user/user.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 编辑资料 19 | 20 | 21 | 22 | 23 | 24 | 添加设备 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /微信小程序部分/miniprogram/pages/user/user.wxss: -------------------------------------------------------------------------------- 1 | /* 页面整体容器 */ 2 | .container { 3 | background: #f5f5f5; 4 | min-height: 100vh; 5 | display: flex; 6 | flex-direction: column; 7 | padding: 20rpx; 8 | box-sizing: border-box; 9 | } 10 | 11 | /* 个人信息区 */ 12 | .profile-section { 13 | height: 33vh; 14 | background: #fff; 15 | padding: 40rpx; 16 | display: flex; 17 | align-items: center; 18 | border-radius: 16rpx; 19 | box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1); /* 增加阴影效果 */ 20 | margin-bottom: 20rpx; 21 | } 22 | 23 | /* 头像容器 */ 24 | .avatar-box { 25 | position: relative; 26 | margin-right: 40rpx; 27 | } 28 | 29 | /* 头像 */ 30 | .avatar { 31 | width: 160rpx; 32 | height: 160rpx; 33 | border-radius: 50%; 34 | background: #eee; 35 | box-shadow: 0 6rpx 16rpx rgba(0, 0, 0, 0.1); /* 头像增加阴影效果 */ 36 | } 37 | 38 | /* 编辑图标 */ 39 | .edit-icon { 40 | width: 40rpx; 41 | height: 40rpx; 42 | position: absolute; 43 | right: 0; 44 | bottom: 0; 45 | background: #fff; 46 | border-radius: 50%; 47 | box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.15); /* 提升编辑图标的质感 */ 48 | display: flex; 49 | justify-content: center; 50 | align-items: center; 51 | } 52 | 53 | /* 用户信息区域 */ 54 | .user-info { 55 | flex: 1; 56 | } 57 | 58 | /* 用户名 */ 59 | .username { 60 | font-size: 36rpx; 61 | color: #333; 62 | margin-bottom: 16rpx; 63 | font-weight: 500; 64 | line-height: 1.4; 65 | } 66 | 67 | /* 用户简介 */ 68 | .bio { 69 | font-size: 28rpx; 70 | color: #999; 71 | line-height: 1.6; 72 | } 73 | 74 | /* 功能操作区 */ 75 | .action-section { 76 | padding: 30rpx; 77 | flex: 1; 78 | } 79 | 80 | /* 每个操作卡片 */ 81 | .action-card { 82 | background: #fff; 83 | border-radius: 16rpx; 84 | padding: 32rpx; 85 | margin-bottom: 24rpx; 86 | display: flex; 87 | align-items: center; 88 | box-shadow: 0 6rpx 18rpx rgba(0, 0, 0, 0.12); /* 提升操作卡片的阴影效果 */ 89 | transition: all 0.3s ease; /* 添加平滑过渡效果 */ 90 | } 91 | 92 | /* 操作图标 */ 93 | .action-icon { 94 | width: 48rpx; 95 | height: 48rpx; 96 | margin-right: 24rpx; 97 | background: #2196F3; /* 操作图标背景色更醒目 */ 98 | border-radius: 50%; 99 | display: flex; 100 | justify-content: center; 101 | align-items: center; 102 | box-shadow: 0 4rpx 8rpx rgba(0, 0, 0, 0.1); /* 图标阴影 */ 103 | } 104 | 105 | /* 操作文本 */ 106 | .action-text { 107 | flex: 1; 108 | font-size: 32rpx; 109 | color: #333; 110 | font-weight: 500; 111 | letter-spacing: 1px; /* 增加字母间距,提高可读性 */ 112 | } 113 | 114 | /* 向右箭头 */ 115 | .arrow { 116 | width: 32rpx; 117 | height: 32rpx; 118 | margin-left: 12rpx; 119 | /* background: url('/miniprogram/images/arrow-right.png') no-repeat center center; */ 120 | background-size: contain; 121 | } 122 | 123 | /* 卡片点击时效果 */ 124 | .action-card:active { 125 | transform: scale(0.98); /* 点击时缩小 */ 126 | box-shadow: 0 8rpx 18rpx rgba(0, 0, 0, 0.2); /* 加强点击时的阴影 */ 127 | } 128 | -------------------------------------------------------------------------------- /微信小程序部分/miniprogram/utils/util.ts: -------------------------------------------------------------------------------- 1 | export const formatTime = (date: Date) => { 2 | const year = date.getFullYear() 3 | const month = date.getMonth() + 1 4 | const day = date.getDate() 5 | const hour = date.getHours() 6 | const minute = date.getMinutes() 7 | const second = date.getSeconds() 8 | 9 | return ( 10 | [year, month, day].map(formatNumber).join('/') + 11 | ' ' + 12 | [hour, minute, second].map(formatNumber).join(':') 13 | ) 14 | } 15 | 16 | const formatNumber = (n: number) => { 17 | const s = n.toString() 18 | return s[1] ? s : '0' + s 19 | } 20 | -------------------------------------------------------------------------------- /微信小程序部分/node_modules/.package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "miniprogram-ts-quickstart", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "node_modules/miniprogram-api-typings": { 8 | "version": "2.12.0", 9 | "resolved": "https://registry.npmmirror.com/miniprogram-api-typings/-/miniprogram-api-typings-2.12.0.tgz", 10 | "integrity": "sha512-ibvbqeslVFur0IAvTxLMvsbtvVcMo6gwvOnj0YZHV7aeDLu091VQRrETT2QuiG9P6aZWRcxeNGJChRKVPCp9VQ==", 11 | "dev": true, 12 | "license": "MIT" 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /微信小程序部分/node_modules/miniprogram-api-typings/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 2020-06-15 v2.11.0-1 2 | - 该版本继续合并了一部分完全相同的 interface / callback,是一个 **破坏性改动**,原本字面上引用了这些 interface / callback 的代码可能会报错。 3 | - 为 `Component` 构造器增加第四个泛型,以允许在自定义组件上挂载自定义的字段 ([#133](https://github.com/wechat-miniprogram/api-typings/issues/133)) 4 | - 修复一些接口错误 ([#134](https://github.com/wechat-miniprogram/api-typings/issues/134)) 5 | - 补齐 `App` 的 `onThemeChange` ([#135](https://github.com/wechat-miniprogram/api-typings/issues/135)) 6 | - 补齐 `Page` 的 `onAddToFavorites` ([#136](https://github.com/wechat-miniprogram/api-typings/issues/136)) 7 | 8 | ## 2020-05-20 v2.11.0 9 | - 同步 API 定义到基础库 2.11.0 10 | - 该版本继续合并了一部分完全相同的 interface / callback,是一个 **破坏性改动**,原本字面上引用了这些 interface / callback 的代码可能会报错。 11 | - 修复接口错误 ([#126](https://github.com/wechat-miniprogram/api-typings/issues/126)) 12 | 13 | ## 2020-04-20 v2.10.4 14 | - 同步 API 定义到基础库 2.10.4 15 | - 在之前的版本中,分属于不同接口的两个 interface / callback 即使完全相同,也会拥有不同的名字。在这次更新中,他们将合并为同一个(如 `FileSystemManagerGetFileInfoCompleteCallback` 和 `WxGetFileInfoCompleteCallback` 都变成了 `GetFileInfoCompleteCallback`)。这是一个 **破坏性改动**,原本字面上引用了这些 interface / callback 的代码可能会报错。 16 | - 修复了一些取消监听接口(off callback)的参数错误 ([#120](https://github.com/wechat-miniprogram/api-typings/issues/120)) 17 | 18 | ## 2020-04-03 v2.10.3-1 19 | - 补齐 `Component` 的 `getOpenerEventChannel` ([#112](https://github.com/wechat-miniprogram/api-typings/issues/113) by [@baranwang](https://github.com/baranwang)) 20 | - 加入了部分事件的定义 ([#115](https://github.com/wechat-miniprogram/api-typings/issues/115) by [@zenml](https://github.com/zenml)) 21 | - 更新了小程序·云开发的 API 定义 ([#92](https://github.com/wechat-miniprogram/api-typings/issues/92)) 22 | 23 | ## 2020-03-26 v2.10.3 24 | - 同步 API 定义到基础库 2.10.3 25 | 26 | ## 2020-03-18 v2.10.2-1 27 | - 支持 API Promise 化调用 ([#105](https://github.com/wechat-miniprogram/api-typings/issues/105)) 28 | 29 | ## 2020-03-06 v2.10.2 30 | - 同步 API 定义到基础库 2.10.2 31 | 32 | ## 2020-02-10 v2.10.1-1 33 | - 允许重写部分全局变量 (由 `const` 改为 `let`) ([#102](https://github.com/wechat-miniprogram/api-typings/issues/102)) 34 | - 补齐 `Page` 上的 `options` 字段 ([#101](https://github.com/wechat-miniprogram/api-typings/issues/101) by [@baranwang](https://github.com/baranwang)) 35 | 36 | ## 2020-01-19 v2.10.1 37 | - 同步 API 定义到基础库 2.10.1 38 | - 补齐 `Component` `selectOwnerComponent`, `animate`, `clearAnimation` ([#96](https://github.com/wechat-miniprogram/api-typings/issues/96)) 39 | - 补齐 `App` `onUnhandledRejection` ([#99](https://github.com/wechat-miniprogram/api-typings/issues/99)) 40 | 41 | ## 2020-01-07 v2.10.0-1 42 | - 修复接口错误 ([#95](https://github.com/wechat-miniprogram/api-typings/issues/95)) 43 | 44 | ## 2020-01-07 v2.10.0 45 | - 同步 API 定义到基础库 2.10.0 46 | 47 | ## 2019-12-20 v2.9.4 48 | - 同步 API 定义到基础库 2.9.4 49 | - 修正一些接口错误 ([#88](https://github.com/wechat-miniprogram/api-typings/issues/88),[#89](https://github.com/wechat-miniprogram/api-typings/issues/89),[#91](https://github.com/wechat-miniprogram/api-typings/issues/91)) 50 | 51 | ## 2019-12-06 v2.9.3 52 | - 同步 API 定义到基础库 2.9.3 53 | - 补齐 `Component` 纯数据字段 (`pureDataPattern`) 54 | - 支持 `Component` 的属性监听器使用 `string` 类型 55 | 56 | ## 2019-11-14 v2.9.2 57 | - 同步 API 定义到基础库 2.9.2 58 | - 补齐 `Behaviors` 中缺少的一些选项 59 | 60 | ## 2019-11-06 v2.9.1 61 | - 同步 API 定义到基础库 2.9.1 62 | 63 | ## 2019-10-23 v2.9.0 64 | - 同步 API 定义到基础库 2.9.0 65 | 66 | ## 2019-10-10 v2.8.3-1 67 | - 修复注释文档中不可用的链接 68 | - 组件实例类型支持 `Partial` 的自定义方法 ([用例](https://github.com/wechat-miniprogram/api-typings/blob/master/test/issue.test.ts#L170-L185)) 69 | 70 | ## 2019-09-19 v2.8.3 71 | - 同步 API 定义到基础库 2.8.3 72 | - `getApp` 支持范型 ([#77](https://github.com/wechat-miniprogram/api-typings/issues/77)) 73 | - 修正一些接口错误 ([#73](https://github.com/wechat-miniprogram/api-typings/issues/73), [#75](https://github.com/wechat-miniprogram/api-typings/issues/75), [#79](https://github.com/wechat-miniprogram/api-typings/issues/79)) 74 | - 补齐 `require`, `exports`, `module.exports` 定义,以支持在没有 `@types/node` 下编译 75 | 76 | ## 2019-09-10 v2.8.2 77 | - 同步 API 定义到基础库 2.8.2 78 | - 加强了参数为可选值的方法参数类型定义和注释 (如 `FileSystemManager.appendFileSync` 的 `encoding`) 79 | 80 | ## 2019-08-30 v2.8.1 81 | - 同步 API 定义到基础库 2.8.1 82 | - 修复了部分最低基础库显示为 `[object Object]` 的问题 83 | 84 | ## 2019-08-20 v2.8.0-2 85 | 86 | - 将 `object` 改为 `Record`,以允许任意属性和方法 87 | - 自定义组件属性构造器为 `ObjectConstructor` 时,类型推导为 `Record` 而不是 `object` 88 | - 修正 `component` 参数的类型为页面或自定义组件实例 89 | - 补齐 `console: WechatMiniprogram.Console` 全局变量 90 | - 修正一些其他的接口类型错误 91 | 92 | ## 2019-08-14 v2.8.0-1 93 | 94 | - 补齐 `styleIsolation` 到 `ComponentOption` 95 | 96 | ## 2019-08-14 v2.8.0 97 | 98 | - 同步 API 定义到基础库 2.8.0 99 | - 不再向全局暴露 `IAnyObject`,收回到命名空间 `WechatMiniprogram` 内 100 | - 对齐代码规范,使用 4 空格缩进,不再使用分号等 101 | - 小幅改动 behavior, component 和 page 的定义,使其对 data 和 properties 等的类型推断更准确 102 | - 修复了一些其他问题 ([#60](https://github.com/wechat-miniprogram/api-typings/issues/60), [#59](https://github.com/wechat-miniprogram/api-typings/issues/59), [#48](https://github.com/wechat-miniprogram/api-typings/issues/48), [#47](https://github.com/wechat-miniprogram/api-typings/issues/47), [#45](https://github.com/wechat-miniprogram/api-typings/issues/45), [#33](https://github.com/wechat-miniprogram/api-typings/issues/33), [#13](https://github.com/wechat-miniprogram/api-typings/issues/13)) 103 | 104 | ## 2019-08-08 v2.7.7-2 105 | 106 | - 补齐了部分接口 fail 回调的错误码 ([#51](https://github.com/wechat-miniprogram/api-typings/issues/51)) 107 | 108 | ## 2019-08-06 v2.7.7-1 109 | 110 | - 重写了 page, component 和 behavior 的定义,替换原来不完整的定义,使其更全面,更准确 ([#46](https://github.com/wechat-miniprogram/api-typings/issues/46), [#40](https://github.com/wechat-miniprogram/api-typings/issues/40), [#30](https://github.com/wechat-miniprogram/api-typings/issues/30), [#28](https://github.com/wechat-miniprogram/api-typings/issues/28), [#27](https://github.com/wechat-miniprogram/api-typings/issues/27)) 111 | 112 | ## 2019-07-31 v2.7.7 113 | 114 | - 同步 API 定义到基础库 2.7.7 115 | - 将命名空间从 `Wx` 更改为更正式的 `WechatMiniprogram`,这是一个 **破坏性改动**,原本字面上引用了 `Wx` 命名空间的代码可能失效 116 | - 修复了云开发的定义无法使用的问题 ([#25](https://github.com/wechat-miniprogram/api-typings/issues/25), [#32](https://github.com/wechat-miniprogram/api-typings/issues/32), [#42](https://github.com/wechat-miniprogram/api-typings/issues/42)) 117 | - 修复了一些其它问题 ([#11](https://github.com/wechat-miniprogram/api-typings/issues/11), [#35](https://github.com/wechat-miniprogram/api-typings/issues/35), [#43](https://github.com/wechat-miniprogram/api-typings/issues/43)) -------------------------------------------------------------------------------- /微信小程序部分/node_modules/miniprogram-api-typings/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /微信小程序部分/node_modules/miniprogram-api-typings/README-en.md: -------------------------------------------------------------------------------- 1 | # Wechat Mini Program API Typings 2 | 3 | > [中文版本](./README.md) 4 | 5 | [![Published on NPM](https://img.shields.io/npm/v/miniprogram-api-typings.svg?style=flat)](https://www.npmjs.com/package/miniprogram-api-typings) 6 | [![MIT License](https://img.shields.io/github/license/wechat-miniprogram/api-typings.svg)](https://github.com/wechat-miniprogram/api-typings) 7 | [![Travis CI Test Status](https://travis-ci.org/wechat-miniprogram/api-typings.svg?branch=master)](https://travis-ci.org/wechat-miniprogram/api-typings) 8 | 9 | Type definitions for APIs of Wechat Mini Program in TypeScript 10 | 11 | ## Install 12 | 13 | Install by NPM: 14 | ```bash 15 | # install definitions for latest base library 16 | npm install miniprogram-api-typings 17 | ``` 18 | 19 | or specify a base library version: 20 | 21 | ```bash 22 | # install definitions for base library version 2.4.1 23 | npm install miniprogram-api-typings@2.4.1 24 | ``` 25 | 26 | ## Versions 27 | 28 | Check out all available versions corresponding to base library version in [VERSIONS.md](https://github.com/wechat-miniprogram/api-typings/blob/master/VERSIONS.md) 29 | 30 | ## Changelog 31 | 32 | See [CHANGELOG.md](https://github.com/wechat-miniprogram/api-typings/blob/master/CHANGELOG.md) (Chinese only) 33 | 34 | ## Contribution 35 | 36 | Definitions of Wechat APIs (`lib.wx.api.d.ts`) are auto-generated together with our [documentations](https://developers.weixin.qq.com/miniprogram/dev/index.html), therefore PRs including that file will __not__ be merged. If you found some APIs defined wrongly, create an issue instead. 37 | 38 | Both PR and issue are welcomed for definitions of pages (`Page`), custom components (`Component`) and other else, since they are written manually. Help us improve this definition if you have any bug reports or suggestions! Thanks for contributing! 39 | 40 | ### Contributors 41 | 42 | - [Baran](https://github.com/baranwang) 43 | - [MinLiang Zeng](https://github.com/zenml/) 44 | 45 | ### Automated tests 46 | 47 | We use [`tsd`](https://github.com/SamVerschueren/tsd) to check if this definition is working properly. All test cases are under folder `test`. 48 | 49 | To perform an automated test, clone this repo, `npm install --save-dev` and `npm test`. 50 | 51 | If you have test case that fails the test, an issue or PR will be great. Strong test case that passes are also welcomed. 52 | -------------------------------------------------------------------------------- /微信小程序部分/node_modules/miniprogram-api-typings/README.md: -------------------------------------------------------------------------------- 1 | # 微信小程序定义文件 2 | 3 | > [English version](./README-en.md) 4 | 5 | [![已在 NPM 发布](https://img.shields.io/npm/v/miniprogram-api-typings.svg?style=flat)](https://www.npmjs.com/package/miniprogram-api-typings) 6 | [![MIT 协议](https://img.shields.io/github/license/wechat-miniprogram/api-typings.svg)](https://github.com/wechat-miniprogram/api-typings) 7 | [![Travis CI 测试状况](https://travis-ci.org/wechat-miniprogram/api-typings.svg?branch=master)](https://travis-ci.org/wechat-miniprogram/api-typings) 8 | 9 | 微信小程序 API 的 TypeScript 类型定义文件 10 | 11 | ## 安装 12 | 13 | 通过 npm 安装: 14 | ```bash 15 | # 安装对应最新基础库的定义文件 16 | npm install miniprogram-api-typings 17 | ``` 18 | 19 | 或者通过版本号指定一个基础库版本: 20 | ```bash 21 | # 安装对应基础库版本 2.4.1 的定义文件 22 | npm install miniprogram-api-typings@2.4.1 23 | ``` 24 | 25 | ## 版本 26 | 27 | 所有可用的版本和对应的基础库版本,参考 [VERSIONS.md](https://github.com/wechat-miniprogram/api-typings/blob/master/VERSIONS.md) 28 | 29 | ## 更新日志 30 | 31 | 参考 [CHANGELOG.md](https://github.com/wechat-miniprogram/api-typings/blob/master/CHANGELOG.md) 32 | 33 | ## 贡献 34 | 35 | API 的定义文件(`lib.wx.api.d.ts`)是随 [文档](https://developers.weixin.qq.com/miniprogram/dev/index.html) 一起自动生成的,如果发现了 API 接口的定义错误,请提一个 issue 给我们,关于 API 的 PR 将 __不会__ 被接受。 36 | 37 | 如果有针对页面(`Page`)、自定义组件(`Component`)等接口的 bug 和建议,欢迎 PR 或提一个 issue 给我们。非常感谢! 38 | 39 | ### 贡献者 40 | 41 | - [Baran](https://github.com/baranwang) 42 | - [MinLiang Zeng](https://github.com/zenml/) 43 | 44 | ### 测试 45 | 46 | 本定义文件使用 [`tsd`](https://github.com/SamVerschueren/tsd) 进行测试,所有的测试样例放在 `test` 目录下。 47 | 48 | 想执行测试的话,克隆本项目并完成 `npm install --save-dev` 后执行 `npm test` 即可。 49 | 50 | 如果您发现了不能通过自动化测试的测试样例,可以提交 PR 或者提一个 issue。当然,能通过自动化测试的强有力的测试样例也是欢迎的。 51 | -------------------------------------------------------------------------------- /微信小程序部分/node_modules/miniprogram-api-typings/VERSIONS.md: -------------------------------------------------------------------------------- 1 | ## 所有可用版本 2 | 3 | 基础库版本|npm 版本|命令 4 | -|-|- 5 | [v2.11.3](https://developers.weixin.qq.com/miniprogram/dev/framework/release/#v2-11-2-2020-06-08) | [2.11.3-beta](https://www.npmjs.com/package/miniprogram-api-typings/v/2.11.3-beta) | `npm install miniprogram-api-typings@2.11.3-beta` 6 | [v2.11.2](https://developers.weixin.qq.com/miniprogram/dev/framework/release/#v2-11-2-2020-06-08) | [2.11.2-beta](https://www.npmjs.com/package/miniprogram-api-typings/v/2.11.2-beta) | `npm install miniprogram-api-typings@2.11.2-beta` 7 | [v2.11.0](https://developers.weixin.qq.com/miniprogram/dev/framework/release/#v2-11-0-2020-04-24) | [2.11.0-1](https://www.npmjs.com/package/miniprogram-api-typings/v/2.11.0-1) | `npm install miniprogram-api-typings@2.11.0-1` 8 | [v2.10.4](https://developers.weixin.qq.com/miniprogram/dev/framework/release/#v2-10-4-2020-03-24) | [2.10.4](https://www.npmjs.com/package/miniprogram-api-typings/v/2.10.4) | `npm install miniprogram-api-typings@2.10.4` 9 | [v2.10.3](https://developers.weixin.qq.com/miniprogram/dev/framework/release/#v2-10-3-2020-03-06) | [2.10.3-1](https://www.npmjs.com/package/miniprogram-api-typings/v/2.10.3-1) | `npm install miniprogram-api-typings@2.10.3-1` 10 | [v2.10.2](https://developers.weixin.qq.com/miniprogram/dev/framework/release/#v2-10-2-2020-02-20) | [2.10.2-1](https://www.npmjs.com/package/miniprogram-api-typings/v/2.10.2-1) | `npm install miniprogram-api-typings@2.10.2-1` 11 | [v2.10.1](https://developers.weixin.qq.com/miniprogram/dev/framework/release/#v2-10-1-2020-01-14) | [2.10.1-1](https://www.npmjs.com/package/miniprogram-api-typings/v/2.10.1-1) | `npm install miniprogram-api-typings@2.10.1-1` 12 | [v2.10.0](https://developers.weixin.qq.com/miniprogram/dev/framework/release/#v2-10-0-2019-12-24) | [2.10.0-1](https://www.npmjs.com/package/miniprogram-api-typings/v/2.10.0-1) | `npm install miniprogram-api-typings@2.10.0-1` 13 | [v2.9.4](https://developers.weixin.qq.com/miniprogram/dev/framework/release/#v2-9-4-2019-11-28) | [2.9.4](https://www.npmjs.com/package/miniprogram-api-typings/v/2.9.4) | `npm install miniprogram-api-typings@2.9.4` 14 | [v2.9.3](https://developers.weixin.qq.com/miniprogram/dev/framework/release/) | [2.9.3](https://www.npmjs.com/package/miniprogram-api-typings/v/2.9.3) | `npm install miniprogram-api-typings@2.9.3` 15 | [v2.9.2](https://developers.weixin.qq.com/miniprogram/dev/framework/release/#v2-9-2-2019-11-04) | [2.9.2](https://www.npmjs.com/package/miniprogram-api-typings/v/2.9.2) | `npm install miniprogram-api-typings@2.9.2` 16 | [v2.9.1](https://developers.weixin.qq.com/miniprogram/dev/framework/release/#v2-9-1-2019-10-29) | [2.9.1](https://www.npmjs.com/package/miniprogram-api-typings/v/2.9.1) | `npm install miniprogram-api-typings@2.9.1` 17 | [v2.9.0](https://developers.weixin.qq.com/miniprogram/dev/framework/release/#v2-9-0-2019-10-09) | [2.9.0](https://www.npmjs.com/package/miniprogram-api-typings/v/2.9.0) | `npm install miniprogram-api-typings@2.9.0` 18 | [v2.8.3](https://developers.weixin.qq.com/miniprogram/dev/framework/release/#v2-8-3-2019-09-17) | [2.8.3-1](https://www.npmjs.com/package/miniprogram-api-typings/v/2.8.3-1) | `npm install miniprogram-api-typings@2.8.3-1` 19 | [v2.8.2](https://developers.weixin.qq.com/miniprogram/dev/framework/release/#v2-8-2-2019-08-30) | [2.8.2](https://www.npmjs.com/package/miniprogram-api-typings/v/2.8.2) | `npm install miniprogram-api-typings@2.8.2` 20 | [v2.8.1](https://developers.weixin.qq.com/miniprogram/dev/framework/release/#v2-8-1-2019-08-22) | [2.8.1](https://www.npmjs.com/package/miniprogram-api-typings/v/2.8.1) | `npm install miniprogram-api-typings@2.8.1` 21 | [v2.8.0](https://developers.weixin.qq.com/miniprogram/dev/framework/release/#v2-8-0-2019-07-30) | [2.8.0-2](https://www.npmjs.com/package/miniprogram-api-typings/v/2.8.0-2) | `npm install miniprogram-api-typings@2.8.0-2` 22 | [v2.7.7](https://developers.weixin.qq.com/miniprogram/dev/framework/release/) | [2.7.7-2](https://www.npmjs.com/package/miniprogram-api-typings/v/2.7.7-2) | `npm install miniprogram-api-typings@2.7.7-2` 23 | [v2.6.5](https://developers.weixin.qq.com/miniprogram/dev/framework/release/#v2-6-5-2019-04-02) | [2.6.5-2](https://www.npmjs.com/package/miniprogram-api-typings/v/2.6.5-2) | `npm install miniprogram-api-typings@2.6.5-2` 24 | [v2.4.2](https://developers.weixin.qq.com/miniprogram/dev/framework/release/v2.html#v2-4-2-2018-12-04)|[2.4.2-2](https://www.npmjs.com/package/miniprogram-api-typings/v/2.4.2-2)|`npm install miniprogram-api-typings@2.4.2-2` 25 | [v2.4.1](https://developers.weixin.qq.com/miniprogram/dev/framework/release/v2.html#v2-4-1-2018-11-21)|[2.4.1](https://www.npmjs.com/package/miniprogram-api-typings/v/2.4.1)|`npm install miniprogram-api-typings@2.4.1` 26 | [v2.4.0](https://developers.weixin.qq.com/miniprogram/dev/framework/release/v2.html#v2-4-0-2018-11-05)|[2.4.0-1](https://www.npmjs.com/package/miniprogram-api-typings/v/2.4.0-1)|`npm install miniprogram-api-typings@2.4.0.1` -------------------------------------------------------------------------------- /微信小程序部分/node_modules/miniprogram-api-typings/index.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /微信小程序部分/node_modules/miniprogram-api-typings/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "miniprogram-api-typings", 3 | "version": "2.12.0", 4 | "beta": "true", 5 | "description": "Type definitions for APIs of Wechat Mini Program in TypeScript", 6 | "main": "./index.d.ts", 7 | "types": "./index.d.ts", 8 | "scripts": { 9 | "test": "npm run tsd && npm run tslint", 10 | "tsd": "tsd", 11 | "tslint": "tslint --project ." 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/wechat-miniprogram/api-typings.git" 16 | }, 17 | "author": "Wechat Miniprogram ", 18 | "license": "MIT", 19 | "bugs": { 20 | "url": "https://github.com/wechat-miniprogram/api-typings/issues" 21 | }, 22 | "homepage": "https://github.com/wechat-miniprogram/api-typings#readme", 23 | "dependencies": {}, 24 | "devDependencies": { 25 | "tsd": "^0.11.0", 26 | "tslint": "^5.20.0", 27 | "typescript": "^3.5.3" 28 | }, 29 | "tsd": { 30 | "directory": "test" 31 | }, 32 | "files": [ 33 | "LICENSE", 34 | "CHANGELOG.md", 35 | "VERSIONS.md", 36 | "README.md", 37 | "README-en.md", 38 | "index.d.ts", 39 | "typings.json", 40 | "types/" 41 | ] 42 | } -------------------------------------------------------------------------------- /微信小程序部分/node_modules/miniprogram-api-typings/types/index.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /微信小程序部分/node_modules/miniprogram-api-typings/types/wx/index.d.ts: -------------------------------------------------------------------------------- 1 | /*! ***************************************************************************** 2 | Copyright (c) 2020 Tencent, Inc. All rights reserved. 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 | of the Software, and to permit persons to whom the Software is furnished to do 9 | so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | ***************************************************************************** */ 22 | 23 | /// 24 | /// 25 | /// 26 | /// 27 | /// 28 | /// 29 | /// 30 | 31 | declare namespace WechatMiniprogram { 32 | type IAnyObject = Record 33 | type Optional = F extends (arg: infer P) => infer R ? (arg?: P) => R : F 34 | type OptionalInterface = { [K in keyof T]: Optional } 35 | interface AsyncMethodOptionLike { 36 | success?: (...args: any[]) => void 37 | } 38 | type PromisifySuccessResult< 39 | P, 40 | T extends AsyncMethodOptionLike 41 | > = P extends { success: any } 42 | ? void 43 | : P extends { fail: any } 44 | ? void 45 | : P extends { complete: any } 46 | ? void 47 | : Promise>[0]> 48 | } 49 | -------------------------------------------------------------------------------- /微信小程序部分/node_modules/miniprogram-api-typings/types/wx/lib.wx.app.d.ts: -------------------------------------------------------------------------------- 1 | /*! ***************************************************************************** 2 | Copyright (c) 2020 Tencent, Inc. All rights reserved. 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 | of the Software, and to permit persons to whom the Software is furnished to do 9 | so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | ***************************************************************************** */ 22 | 23 | declare namespace WechatMiniprogram { 24 | namespace App { 25 | interface ReferrerInfo { 26 | /** 来源小程序或公众号或App的 appId 27 | * 28 | * 以下场景支持返回 referrerInfo.appId: 29 | * - 1020(公众号 profile 页相关小程序列表): appId 30 | * - 1035(公众号自定义菜单):来源公众号 appId 31 | * - 1036(App 分享消息卡片):来源应用 appId 32 | * - 1037(小程序打开小程序):来源小程序 appId 33 | * - 1038(从另一个小程序返回):来源小程序 appId 34 | * - 1043(公众号模板消息):来源公众号 appId 35 | */ 36 | appId: string 37 | /** 来源小程序传过来的数据,scene=1037或1038时支持 */ 38 | extraData?: any 39 | } 40 | 41 | type SceneValues = 42 | | 1001 43 | | 1005 44 | | 1006 45 | | 1007 46 | | 1008 47 | | 1011 48 | | 1012 49 | | 1013 50 | | 1014 51 | | 1017 52 | | 1019 53 | | 1020 54 | | 1023 55 | | 1024 56 | | 1025 57 | | 1026 58 | | 1027 59 | | 1028 60 | | 1029 61 | | 1030 62 | | 1031 63 | | 1032 64 | | 1034 65 | | 1035 66 | | 1036 67 | | 1037 68 | | 1038 69 | | 1039 70 | | 1042 71 | | 1043 72 | | 1044 73 | | 1045 74 | | 1046 75 | | 1047 76 | | 1048 77 | | 1049 78 | | 1052 79 | | 1053 80 | | 1056 81 | | 1057 82 | | 1058 83 | | 1059 84 | | 1064 85 | | 1067 86 | | 1069 87 | | 1071 88 | | 1072 89 | | 1073 90 | | 1074 91 | | 1077 92 | | 1078 93 | | 1079 94 | | 1081 95 | | 1082 96 | | 1084 97 | | 1089 98 | | 1090 99 | | 1091 100 | | 1092 101 | | 1095 102 | | 1096 103 | | 1097 104 | | 1099 105 | | 1102 106 | | 1124 107 | | 1125 108 | | 1126 109 | | 1129 110 | 111 | interface LaunchShowOption { 112 | /** 打开小程序的路径 */ 113 | path: string 114 | /** 打开小程序的query */ 115 | query: IAnyObject 116 | /** 打开小程序的场景值 117 | * - 1001:发现栏小程序主入口,「最近使用」列表(基础库2.2.4版本起包含「我的小程序」列表) 118 | * - 1005:微信首页顶部搜索框的搜索结果页 119 | * - 1006:发现栏小程序主入口搜索框的搜索结果页 120 | * - 1007:单人聊天会话中的小程序消息卡片 121 | * - 1008:群聊会话中的小程序消息卡片 122 | * - 1011:扫描二维码 123 | * - 1012:长按图片识别二维码 124 | * - 1013:扫描手机相册中选取的二维码 125 | * - 1014:小程序模板消息 126 | * - 1017:前往小程序体验版的入口页 127 | * - 1019:微信钱包(微信客户端7.0.0版本改为支付入口) 128 | * - 1020:公众号 profile 页相关小程序列表 129 | * - 1023:安卓系统桌面图标 130 | * - 1024:小程序 profile 页 131 | * - 1025:扫描一维码 132 | * - 1026:发现栏小程序主入口,「附近的小程序」列表 133 | * - 1027:微信首页顶部搜索框搜索结果页「使用过的小程序」列表 134 | * - 1028:我的卡包 135 | * - 1029:小程序中的卡券详情页 136 | * - 1030:自动化测试下打开小程序 137 | * - 1031:长按图片识别一维码 138 | * - 1032:扫描手机相册中选取的一维码 139 | * - 1034:微信支付完成页 140 | * - 1035:公众号自定义菜单 141 | * - 1036:App 分享消息卡片 142 | * - 1037:小程序打开小程序 143 | * - 1038:从另一个小程序返回 144 | * - 1039:摇电视 145 | * - 1042:添加好友搜索框的搜索结果页 146 | * - 1043:公众号模板消息 147 | * - 1044:带 shareTicket 的小程序消息卡片 [详情](https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/share.html) 148 | * - 1045:朋友圈广告 149 | * - 1046:朋友圈广告详情页 150 | * - 1047:扫描小程序码 151 | * - 1048:长按图片识别小程序码 152 | * - 1049:扫描手机相册中选取的小程序码 153 | * - 1052:卡券的适用门店列表 154 | * - 1053:搜一搜的结果页 155 | * - 1056:聊天顶部音乐播放器右上角菜单 156 | * - 1057:钱包中的银行卡详情页 157 | * - 1058:公众号文章 158 | * - 1059:体验版小程序绑定邀请页 159 | * - 1064:微信首页连Wi-Fi状态栏 160 | * - 1067:公众号文章广告 161 | * - 1069:移动应用 162 | * - 1071:钱包中的银行卡列表页 163 | * - 1072:二维码收款页面 164 | * - 1073:客服消息列表下发的小程序消息卡片 165 | * - 1074:公众号会话下发的小程序消息卡片 166 | * - 1077:摇周边 167 | * - 1078:微信连Wi-Fi成功提示页 168 | * - 1079:微信游戏中心 169 | * - 1081:客服消息下发的文字链 170 | * - 1082:公众号会话下发的文字链 171 | * - 1084:朋友圈广告原生页 172 | * - 1089:微信聊天主界面下拉,「最近使用」栏(基础库2.2.4版本起包含「我的小程序」栏) 173 | * - 1090:长按小程序右上角菜单唤出最近使用历史 174 | * - 1091:公众号文章商品卡片 175 | * - 1092:城市服务入口 176 | * - 1095:小程序广告组件 177 | * - 1096:聊天记录 178 | * - 1097:微信支付签约页 179 | * - 1099:页面内嵌插件 180 | * - 1102:公众号 profile 页服务预览 181 | * - 1124:扫“一物一码”打开小程序 182 | * - 1125:长按图片识别“一物一码” 183 | * - 1126:扫描手机相册中选取的“一物一码” 184 | * - 1129:微信爬虫访问 [详情](https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/sitemap.html) 185 | */ 186 | scene: SceneValues 187 | /** shareTicket,详见 [获取更多转发信息]((转发#获取更多转发信息)) */ 188 | shareTicket: string 189 | /** 当场景为由从另一个小程序或公众号或App打开时,返回此字段 */ 190 | referrerInfo?: ReferrerInfo 191 | } 192 | 193 | interface PageNotFoundOption { 194 | /** 不存在页面的路径 */ 195 | path: string 196 | /** 打开不存在页面的 query */ 197 | query: IAnyObject 198 | /** 是否本次启动的首个页面(例如从分享等入口进来,首个页面是开发者配置的分享页面) */ 199 | isEntryPage: boolean 200 | } 201 | 202 | interface Option { 203 | /** 生命周期回调—监听小程序初始化 204 | * 205 | * 小程序初始化完成时触发,全局只触发一次。 206 | */ 207 | onLaunch(options: LaunchShowOption): void 208 | /** 生命周期回调—监听小程序显示 209 | * 210 | * 小程序启动,或从后台进入前台显示时 211 | */ 212 | onShow(options: LaunchShowOption): void 213 | /** 生命周期回调—监听小程序隐藏 214 | * 215 | * 小程序从前台进入后台时 216 | */ 217 | onHide(): void 218 | /** 错误监听函数 219 | * 220 | * 小程序发生脚本错误,或者 api 221 | */ 222 | onError(/** 错误信息,包含堆栈 */ error: string): void 223 | /** 页面不存在监听函数 224 | * 225 | * 小程序要打开的页面不存在时触发,会带上页面信息回调该函数 226 | * 227 | * **注意:** 228 | * 1. 如果开发者没有添加 `onPageNotFound` 监听,当跳转页面不存在时,将推入微信客户端原生的页面不存在提示页面。 229 | * 2. 如果 `onPageNotFound` 回调中又重定向到另一个不存在的页面,将推入微信客户端原生的页面不存在提示页面,并且不再回调 `onPageNotFound`。 230 | * 231 | * 最低基础库: 1.9.90 232 | */ 233 | onPageNotFound(options: PageNotFoundOption): void 234 | /** 235 | * 小程序有未处理的 Promise 拒绝时触发。也可以使用 [wx.onUnhandledRejection](https://developers.weixin.qq.com/miniprogram/dev/api/base/app/app-event/wx.onUnhandledRejection.html) 绑定监听。注意事项请参考 [wx.onUnhandledRejection](https://developers.weixin.qq.com/miniprogram/dev/api/base/app/app-event/wx.onUnhandledRejection.html)。 236 | * **参数**:与 [wx.onUnhandledRejection](https://developers.weixin.qq.com/miniprogram/dev/api/base/app/app-event/wx.onUnhandledRejection.html) 一致 237 | */ 238 | onUnhandledRejection: OnUnhandledRejectionCallback 239 | /** 240 | * 系统切换主题时触发。也可以使用 wx.onThemeChange 绑定监听。 241 | * 242 | * 最低基础库: 2.11.0 243 | */ 244 | onThemeChange: OnThemeChangeCallback 245 | } 246 | 247 | type Instance = Option & T 248 | type Options = Partial