↓↓クリックして頂けると励みになります。
【55 | 配達人のマップ表示】 << 【ホーム】 >> 【57 | 配達依頼を受けた時の連動】
「core/courier/apis.py」ファイルを編集します。
記述編集 【Desktop/crowdsource/core/courier/apis.py】
from asgiref.sync import async_to_sync from channels.layers import get_channel_layer from django.http import JsonResponse from django.contrib.auth.decorators import login_required from django.views.decorators.csrf import csrf_exempt from django.utils import timezone from core.models import * @csrf_exempt @login_required(login_url="/courier/sign-in/") def available_jobs_api(request): jobs = list(Job.objects.filter(status=Job.PROCESSING_STATUS).values()) return JsonResponse({ "success": True, "jobs": jobs }) @csrf_exempt @login_required(login_url="/courier/sign-in/") def current_job_update_api(request, id): job = Job.objects.filter( id=id, courier=request.user.courier, status__in=[ Job.PICKING_STATUS, Job.DELIVERING_STATUS ] ).last() if job.status == Job.PICKING_STATUS: job.pickup_photo = request.FILES['pickup_photo'] job.pickedup_at = timezone.now() job.status = Job.DELIVERING_STATUS job.save() print(job.pickup_photo.url) try: layer = get_channel_layer() async_to_sync(layer.group_send)("job_" + str(job.id), { 'type': 'job_update', 'job': { 'status': job.get_status_display(), 'pickup_photo': job.pickup_photo.url, } }) except: pass elif job.status == Job.DELIVERING_STATUS: job.delivery_photo = request.FILES['delivery_photo'] job.delivered_at = timezone.now() job.status = Job.COMPLETED_STATUS job.save() try: layer = get_channel_layer() async_to_sync(layer.group_send)("job_" + str(job.id), { 'type': 'job_update', 'job': { 'status': job.get_status_display(), 'delivery_photo': job.delivery_photo.url, } }) except: pass return JsonResponse({ "success": True }) @csrf_exempt @login_required(login_url="/courier/sign-in/") def fcm_token_update_api(request): request.user.courier.fcm_token = request.GET.get('fcm_token') request.user.courier.save() return JsonResponse({ "success": True })
「core/templates/customer/job.html」ファイルを編集します。
記述編集 【core/templates/customer/job.html】
{% extends 'customer/base.html' %} {% load static %} {% block head %} <style> .photo { object-fit: cover; } .photo-blank { border: 2px dashed #DFDFDF; height: 130px; width: 130px; border-radius: 5px; align-items: center; display: flex; justify-content: center; text-align: center; padding: 10px; } </style> <script src="https://maps.googleapis.com/maps/api/js?key={{ GOOGLE_MAP_API_KEY }}&callback=initMap&libraries=places&v=weekly" defer></script> <script> var pickupLat = parseFloat(" {{ job.pickup_lat }} "); var pickupLng = parseFloat(" {{ job.pickup_lng }} "); var deliveryLat = parseFloat(" {{ job.delivery_lat }} "); var deliveryLng = parseFloat(" {{ job.delivery_lng }} "); var courierLat = parseFloat(" {{ job.courier.lat }} "); var courierLng = parseFloat(" {{ job.courier.lng }} "); function initMap() { const directionsService = new google.maps.DirectionsService(); const directionsRenderer = new google.maps.DirectionsRenderer(); const map = new google.maps.Map(document.getElementById("map"), { zoom: 7, center: { lat: 43.062087, lng: 141.354404 }, }); directionsRenderer.setMap(map); calculateAndDisplayRoute(map, directionsService, directionsRenderer); } function calculateAndDisplayRoute(map, directionsService, directionsRenderer) { directionsService.route( { origin: new google.maps.LatLng(pickupLat, pickupLng), destination: new google.maps.LatLng(deliveryLat, deliveryLng), travelMode: google.maps.TravelMode.DRIVING, }, (response, status) => { if (status === "OK") { new google.maps.DirectionsRenderer({ map: map, directions: response, suppressMarkers: true, polylineOptions: { strokeColor: "#000", strokeWeight: 5, strokeOpacity: 0.8 } }); var leg = response.routes[0].legs[0]; new google.maps.Marker({ position: leg.start_location, map: map, icon: "{% static 'img/start.png' %}" }); new google.maps.Marker({ position: leg.end_location, map: map, icon: "{% static 'img/end.png' %}" }); window.courierMarker = new google.maps.Marker({ position: new google.maps.LatLng(courierLat, courierLng), map, icon: '/static/img/courier.png', }) } else { window.alert("リクエストは次の理由で失敗しました:" + status); } } ); } const jobSocket = new WebSocket( "ws://" + window.location.host + "/ws/jobs/{{ job.id }}/" ); // このページが WebSocket 経由でイベントを受信するたびにこの関数を実行します jobSocket.onmessage = function (e) { var data = JSON.parse(e.data); var job = data.job; console.log(job); if (job.courier_lat && job.courier_lng) { var courierPosition = new google.maps.LatLng(job.courier_lat, job.courier_lng); window.courierMarker.setPosition(courierPosition); } if (job.status) { $("#job_status").html(job.status); $("form").css("display", "none"); } if (job.pickup_photo) { $("#pickup_photo").html('<img src="' + job.pickup_photo + '" class="rounded-lg photo" width="130" height="130">'); } if (job.delivery_photo) { $("#delivery_photo").html('<img src="' + job.delivery_photo + '" class="rounded-lg photo" width="130" height="130">'); } } </script> {% endblock %} {% block main %} <!-- JOB DESCRIPTION --> <div class="media mb-4"> <img src="{{ job.photo.url }}" class="rounded-lg mr-3" width="150" height="150"> <div class="media-body"> {% if job.status == 'processing' %} <form method="POST" class="float-right"> {% csrf_token %} <button type="submit" class="btn btn-warning">配達依頼をキャンセル</button> </form> {% endif %} <h4>{{ job.name }}</h4> <p class="text-secondary">{{ job.description }}</p> <div class="row"> <div class="col-lg-3"> <small class="text-secondary">カテゴリー</small><br /> <span><b>{{ job.category.name }}</b></span> </div> <div class="col-lg-3"> <small class="text-secondary">サイズ</small><br /> <span><b>{{ job.get_size_display }}</b></span> </div> <div class="col-lg-3"> <small class="text-secondary">料金</small><br /> <span><b>{{ job.price }}円</b></span> </div> <div class="col-lg-3"> <small class="text-secondary">数量</small><br /> <span><b>{{ job.quantity }}</b></span> </div> </div> </div> </div> <!-- DELIVERY INFORMATION --> <b class="text-secondary">配達依頼情報</b><br /> <div class="card bg-white mt-2 mb-5"> <div class="card-body p-4"> <h4 class="mb-3"> 荷物受取先 </h4> <div class="row"> <div class="col-lg-4"> <b>荷物受取先住所</b><br /> <span>{{ job.pickup_address }}</span> </div> <div class="col-lg-4"> <b>{{ job.pickup_name }}</b><br /> <span>{{ job.pickup_phone }}</span> </div> <div id="pickup_photo" class="col-lg-4"> {% if job.pickup_photo %} <img src="{{ job.pickup_photo.url }}" class="rounded-lg photo" width="130" height="130"> {% else %} <div class="photo-blank">写真があればここに表示されます。</div> {% endif %} </div> </div> <hr class="my-4" /> <h4 class="mb-3"> 配達先 </h4> <div class="row"> <div class="col-lg-4"> <b>配達先住所</b><br /> <span>{{ job.delivery_address }}</span> </div> <div class="col-lg-4"> <b>{{ job.delivery_name }}</b><br /> <span>{{ job.delivery_phone }}</span> </div> <div id="delivery_photo" class="col-lg-4"> {% if job.delivery_photo %} <img src="{{ job.delivery_photo.url }}" class="rounded-lg photo" width="130" height="130"> {% else %} <div class="photo-blank">写真があればここに表示されます。</div> {% endif %} </div> </div> </div> </div> <!-- マップ --> <div class="d-flex justify-content-between"> <b class="text-secondary">配達の追跡</b> <div> <span id="job_status" class="badge badge-warning"> {% if job.get_status_display == 'Processing' %} 進行中 {{ job.get_status_display }} {% else %} 完了 {{ job.get_status_display }} {% endif %} </span> </div> </div> <div class="card bg-white mt-2"> <div class="card-body p-0"> <div id="map" style="height: 500px;"></div> </div> </div> {% endblock %}
写真を撮影すると、リアルタイムで更新されるようになりました。
ステータスもリアルタイムで更新されるようになってます。
↓↓クリックして頂けると励みになります。
【55 | 配達人のマップ表示】 << 【ホーム】 >> 【57 | 配達依頼を受けた時の連動】