見出し画像

Webixライブラリを使ってWebアプリを開発する手順を複数回にわたって説明します。(その7)作業日報で写真利用No.048

前回までで、作業日報にGPS情報(緯度、経度)を付与する機能追加を紹介しましたが、今回は、スマホでカメラを利用して作業日報に関連する映像を撮影して作業日報に取り込む機能を紹介します。パソコンなどでは、事前に保存した映像を取り込む操作が可能です。(スマホでは、カメラ撮影または、事前に保存した映像を選択する操作も可能です)
作業日報に映像を付与できると、文章の入力操作をしなくても、作業内容を把握しやすく、日報登録の作業時間短縮になります。
今回は、最大2個の映像を取り込むように機能(データベース構造も)を変更しました。
Webアプリ(Javascrit)でカメラ操作が可能ですが、いくつかの条件が必要です。
Webアプリ公開サイトがhttps化されていること
Webアプリの操作の中でカメラ操作をするとき(初めての操作)では、ユーザがOKとボタン押下が必要
上記2つの条件を満たせば、Webアプリ(Javascript)でカメラ撮影をしてそのまま、取り込む動作が可能となります。
尚、最近のスマホのカメラは、解像度が高く、映像サイズが大きいので、そのままのサイズ映像をWebアプリで使用するには、通信料(パケット量)の増加やレスポンス時間が長くなる要因となり、解像度を下げて、映像を保存することが望ましいです。
また、撮影した写真で不要な部分のカットや拡大操作なども同時にやりたく、クリッピング機能を使う中で、解像度も抑える実装としました。
(そのままの映像を使うと、レスポンスが遅くなり実用的ではありません)
Javascriptでクリッピング操作をするには、ライブラリとしてCroppie-2.6.4を使っています。事前にWebサーバにインストール操作が必要です。
今回のサーバでは、以下の場所に事前にライブラリを格納して参照しています。
jsとcssの指定が必要です。

<script src="/webix02/commonlib/Croppie-2.6.4/croppie.min.js"></script>
<link rel="stylesheet" href="/webix02/commonlib/Croppie-2.6.4/croppie.css" type="text/css"   charset="utf-8">

ライブラリCroppie-2.6.4のインストール操作は、Web上でいくつも公開されています。ダウンロードして、Webサーバに格納する操作で可能です。
Croppie-2.6.4にもいくつも機能があるので、詳しいことは、マニュアルを見てください。今回は、最小限に指定をしてクリッピング機能とwebixライブラリを連携しています。
Webixライブラリ上からCroppie-2.6.4を使うには、カメラで撮影した映像や事前に保存した映像を選択して一度、プレビュー画面で表示させ、その画面でクリッピング操作をします。このプレビュー画面表示のときに、映像の解像度を下げる動作も同時に実施しています。
パソコンだけの機能であれば、少し解像度が低いように見えますが、スマホ画面での映像表示であれば、十分な解像度になっています。
作成した画面のイメージです。作業日報を新規登録するときに、画像選択ボタンが2つ実装されているので、画像選択(撮影)をクリックすると、

スマホの場合、カメラ撮影か写真ライブラリか、ファイル選択の画面が表示されるので、今回は、写真を撮るをクリックします。カメラが起動し、撮影を行います。撮影後、画面の下に再撮影か写真を使用と表示されるので、写真を使用をクリックすると、プレビュー画面に遷移します。

プレビュー画面では、クリッピングする枠が表示されているので、そのサイズを変更する操作や指で映像を移動させたり、拡大させたりして、思った映像になったら、保存をクリックすると、最初の画面に戻り、画像も表示されます。
クリッピング枠は、白い線で表示されています

保存をクリックすると、元の画面に戻り、映像が取り込まれています。

2つ目の映像も同様にして取り込むことが可能です。
この状態では、作業日報の登録操作がまだ完了していませんので、他の情報を選択や入力して再度に登録をクリックします。
もし、キャンセルをクリックした場合、作業日報の情報は取り消しとしますので、映像情報の後処理(キャンセル時は、削除する処理)が必要になります。
映像を保存する操作をしたタイミングで画像データは、サーバ側に送信され、指定フォルダに保存しています。その情報を作業日報の登録操作でデータベースに格納する動作となりますが、途中でキャンセルした場合、保存した映像は不要ですので、削除操作を実装する必要があります。
映像情報が多く保存されると、レンタルサーバの両方にも影響が出てくるので、不要なファイルをきちんと削除するロジックを実装しておく必要があります。
このあたりの実装が少し、複雑になりますが、ソースコードには記載していますので、参考にしてください。
画面の映像をクリックすると、拡大表示します。

カメラで撮影してそのまま、映像を取り込む操作をスマホで実施した場合、対象映像は、スマホ内には保存されません。同じ画像を利用したい場合は、事前にカメラで撮影し、写真ライブラリから取り込む操作をすることになります。(作業日報で保存した映像をダウンロードする機能は、実装できます)
作業日報として保存した映像は、作業日報の一覧から対象行をクリックして詳細画面にしたときに、確認できますし、その状態で写真の変更や追加操作も可能です。
作業日報にカメラ機能が使えるのは、いろいろな場面で効果が期待できますので、ぜひ実装を検討してみてください。
webixライブラリで写真を格納する操作は、uploaderコンポーネントを利用します。onBeforeFileAddイベントの中でプレビュー操作など実装します。
以下、詳細画面のソースコードです。

//======================================================================
//File Name       : DR0021_dailyreport_winform.php
//Encoding        : UTF-8
//Creation Date   : 2024-06-28
// 
//Copyright © 2024 sunsunfarm. All rights reserved.
// 
//This source code or any portion thereof must not be  
//reproduced or used in any manner whatsoever.
//======================================================================
webix.i18n.setLocale("ja-JP");
    
var DR0021_win_job_info = "DR0021";
var DR0021_win_resize_height=590;
var DR0021_win_resize_width=350;
var DR0021_win_resize_top=5;
var DR0021_win_resize_left=5;
var DR0021_form_minwidth = 350;

var DR0021_path_info = "";
var DR0021_image_info = "";
var upload_filename = "";

function DR0021_create_data_win_show(permission_info,userid){
    $$("DR0021_dailyreport_win").config.modal = true;
    DR0021_dailyreport_win.show();
    webix.fullscreen.set(DR0021_dailyreport_win);

    DR0021_dailyreport_form_clear();
    $$("DR0021_user_id").setValue(userid);
    
    $$("DR0021_dailyreport_udate_btn").enable();
    $$("DR0021_dailyreport_udate_btn").define("label","登録");
    $$("DR0021_dailyreport_udate_btn").refresh();
    $$("DR0021_dailyreport_cancel_btn").define("label","キャンセル");
    $$("DR0021_dailyreport_cancel_btn").refresh();
    $$("DR0021_dailyreport_delete_btn").hide();
    
    $$("DR0021_dailyreport_editmode").setValue("3");  //1:閲覧 2:編集 3:新規登録
    return true;
}

function DR0021_dailyreport_form_clear(){
    $$("DR0021_id").setValue("");
    $$("DR0021_report_id").setValue("");
    $$("DR0021_imagefolder").setValue("");
    $$("DR0021_org_imagefolder").setValue("");
    $$("DR0021_org_imagefile").setValue("");
    $$("DR0021_imagefile").setValue("");
    $$("DR0021_image").setValue("");
    $$("DR0021_imagefolder2").setValue("");
    $$("DR0021_org_imagefolder2").setValue("");
    $$("DR0021_org_imagefile2").setValue("");
    $$("DR0021_imagefile2").setValue("");
    $$("DR0021_image2").setValue("");

	$$("DR0021_report_date").setValue(moment().format("YYYY/MM/DD"));
    var now_time_h = moment().format("HH");
    var now_time_m = moment().format("mm");
   
    var now_time_m2 = Math.round(now_time_m/ 15) * 15;
    var duration_hour = "2.0";
	$$("DR0021_duration_hour").setValue(duration_hour);
	$$("DR0021_start_time").setValue(moment(now_time_h+":"+now_time_m2,"HH:mm").subtract(Number(duration_hour),'h').format("HH:mm"));
	$$("DR0021_end_time").setValue(now_time_h+":"+now_time_m2);
}




//行から読み出して、report_idをキーにDBを検索してフォームにセットする
function DR0021_disp_win_show(permission_info,session_info,report_id,id){
    DR0021_dailyreport_form_clear();
    
    $$("DR0021_mess").setValue("");
    var access_key = Get_AccessKey();
    var send_prm = Prepare_send_prm(session_info,access_key);
    send_prm.report_id = report_id;
    send_prm.id = id;
    send_prm.form_id = "DR0021";
    
	var xhr =webix.ajax().sync().get("<?php  echo SUB_FOLDER; ?>/rest_api/DR0010/DR0013_dailyreport_select.php",send_prm);
	var resp =  JSON.parse(xhr.responseText);
	if(resp.resp =="ok"){
		$$("DR0021_dailyreport_win_form1").setValues(resp.form_datas);
		$$("DR0021_org_imagefolder" ).setValue(resp.form_datas.DR0021_imagefolder);
		$$("DR0021_org_imagefile" ).setValue(resp.form_datas.DR0021_imagefile);
		$$("DR0021_org_imagefolder2" ).setValue(resp.form_datas.DR0021_imagefolder2);
		$$("DR0021_org_imagefile2" ).setValue(resp.form_datas.DR0021_imagefile2);

        $$("DR0021_dailyreport_udate_btn").enable();
        $$("DR0021_dailyreport_udate_btn").define("label","更新");
        $$("DR0021_dailyreport_udate_btn").refresh();
        $$("DR0021_dailyreport_cancel_btn").define("label","キャンセル");
        $$("DR0021_dailyreport_cancel_btn").refresh();
        $$("DR0021_dailyreport_editmode").setValue("2");  //1:閲覧 2:編集 3:新規登録

        if(CM0010_permission_check(permission_info,CM0080_PERMISSION_B5)){  //削除可能者
            $$("DR0021_dailyreport_delete_btn").enable();
            $$("DR0021_dailyreport_delete_btn").show();
        }
        else{
            $$("DR0021_dailyreport_delete_btn").disable();
            $$("DR0021_dailyreport_delete_btn").hide();
        }
		DR0021_dailyreport_win.show();
    	webix.fullscreen.set(DR0021_dailyreport_win);
    }
	else{
		webix.message({type:"error", text:"検索でエラーが発生しました。code="+resp.error_code});
	}
	
}


function gps_infos(position) {
    var latitude = (position.coords.latitude).toString();
    var longitude = (position.coords.longitude).toString() ;
	$$("DR0021_latitude").setValue(latitude);
	$$("DR0021_longitude").setValue(longitude);
	if(latitude == ""){
		webix.alert("GPS情報が取得できませんでした。");
	}
	else{
		webix.message({type:"success", text:"GPS情報を取得しました。"});
	}

}

var geoOptions = {
  maximumAge : 2000 ,
  timeout : 8000 ,
  enableHighAccuracy : false
};

function get_gps_info(){
	$$("DR0021_latitude").setValue("");
	$$("DR0021_longitude").setValue("");
	navigator.geolocation.getCurrentPosition(gps_infos, onErr , geoOptions );
}


function onErr( error ){
  // エラー処理
	 // エラーコード(error.code)の番号
	 // 0:UNKNOWN_ERROR				原因不明のエラー
	 // 1:PERMISSION_DENIED			利用者が位置情報の取得を許可しなかった
	 // 2:POSITION_UNAVAILABLE		電波状況などで位置情報が取得できなかった
	 // 3:TIMEOUT					位置情報の取得に時間がかかり過ぎた…
	 // エラー番号に対応したメッセージ
	 //webix.message({type:"debug", text:"error="+error.code});
	 DR0020_errorNo = error.code ;

	 var errorInfo = [
	 	"原因不明のエラーが発生しました。" ,
	 	"位置情報の取得が許可されませんでした。" ,
	 	"電波状況などで位置情報が取得できませんでした。" ,
	 	"位置情報の取得に時間がかかり過ぎてタイムアウトしました。"
	 ] ;
	 // エラー番号
	 // エラーメッセージ
	 var errorMessage = "[エラー番号: " + DR0020_errorNo + "]<br>" + errorInfo[ DR0020_errorNo ] ;
	 // アラート表示
	 webix.alert( errorMessage ) ;
}

function DR0021_geoShow(DR0021_lat,DR0021_lng ) {
if(!$$("DR0021_mapwin")){
  var f = webix.ui({
    view: "window",
    id: "DR0021_mapwin",
    move: true,
    resize: true,
    body: {key:google_key,view: "google-map", id: "DR0021_map", zoom: 15, center: [DR0021_lat,DR0021_lng], data:[]},
    head: {
      view: "toolbar",
      elements: [
        {view: "label", label: "Google Map", align: 'left',id:"DR0021_mapwin_label"}, {
          view: 'button',
          label: 'close',
          width: 120,
          click: function() {
            $$("DR0021_mapwin").hide();
          }
        }
      ]
    },
    top: 50,
    left: 1,
    width: 360,
    height: 400
  });
  }
  var DR0021_map_data = [{ id:1, lat:DR0021_lat, lng:DR0021_lng, title:"Google Map" }];
  $$("DR0021_map").define({center: [DR0021_lat,DR0021_lng], zoom:15,data:DR0021_map_data});
  $$("DR0021_map").refresh();
  $$("DR0021_mapwin_label").setValue("Google Map");
  $$("DR0021_mapwin").show();
}



    
var DR0021_dailyreport_form1 = [
            { margin:20,cols:[
                {view:"text",id:"DR0021_id"           ,name:"DR0021_id"           ,label:"id"    ,width: 30,labelWidth:100,labelAlign:"right",inputAlign:"right",readonly:true,hidden:true},
                {view:"text",id:"DR0021_report_id"    ,name:"DR0021_report_id"    ,label:"作業日報id",width: 200,labelWidth:100,labelAlign:"right",inputAlign:"right",readonly:true},
                {view:"text",id:"DR0021_user_id"      ,name:"DR0021_user_id"      ,label:"user_id",width: 200,labelWidth:100,labelAlign:"right",inputAlign:"right",hidden:true},
            ]},
            { margin:20,cols:[
    	        {view: "datepicker",id:"DR0021_report_date",name:"DR0021_report_date",  label:"作業日", format:webix.Date.dateToStr("%Y/%m/%d"),width: 220,invalidMessage:"未指定です",labelWidth:100,labelAlign:"right"},
           ]},
            { margin:20,cols:[
                {view:"datepicker",type:"time",stringResult:true  ,label:"作業開始時刻",width: 220,labelWidth:100,labelAlign:"right",id:"DR0021_start_time",name:"DR0021_start_time" },
            ]},
            { margin:20,cols:[
                {view:"datepicker",type:"time",stringResult:true  ,label:"作業終了時刻",width: 220,labelWidth:100,labelAlign:"right",id:"DR0021_end_time",name:"DR0021_end_time" },
             ]},
            { margin:20,cols:[
                {view:"select", label:"経過時間",  value:"1.0",width: 160,labelWidth:100,labelAlign:"right", inputAlign:"right",id:"DR0021_duration_hour",name:"DR0021_duration_hour" ,
                 options:["1.0","1.5","2.0","2.5","3.0","3.5","4.0","4.5","5.0","5.5","6.0" ],
                }
             ]},
            { margin:20,cols:[
                {view:"select", label:"作業人数",  value:"2",width: 160,labelWidth:100,labelAlign:"right", inputAlign:"right",id:"DR0021_workers",name:"DR0021_workers",
                 options:["1","2"]
                }
             ]},
            { margin:10,cols:[
                {view:"select", label:"作業場所",  value:"畑",width: 200,labelWidth:100,labelAlign:"right", inputAlign:"right",id:"DR0021_work_place",name:"DR0021_work_place",
                 options:["自宅","畑","倉庫畑","柑橘畑","田んぼ畑","その他"]
                },
            ]},
            { margin:10,cols:[
                {view:"select", label:"作業内容",  value:"畑",width: 200,labelWidth:100,labelAlign:"right", inputAlign:"right",id:"DR0021_work_details",name:"DR0021_work_details",
                 options:["収穫","水やり","肥料","監視","耕作","水やり","出荷","その他"]
                },
           ]},
            { margin:20,cols:[
                {view:"text",id:"DR0021_memo"  ,name:"DR0021_memo" ,label:"メモ",width: 320, labelWidth: "auto",labelAlign:"right"},
            ]},
            {view:"text",id:"DR0021_imagefolder"  ,name:"DR0021_imagefolder"  ,label:"imagefolder",width: 600,labelWidth: 100,labelAlign:"right",hidden:true},
            {view:"text",id:"DR0021_imagefolder2"  ,name:"DR0021_imagefolder2"  ,label:"imagefolder2",width: 600,labelWidth: 100,labelAlign:"right",hidden:true},
            {view:"text",id:"DR0021_org_imagefolder"  ,name:"DR0021_org_imagefolder"  ,label:"org_imagefolder",width: 600,labelWidth: 100,labelAlign:"right",hidden:true},
            {view:"text",id:"DR0021_org_imagefile"    ,name:"DR0021_org_imagefile"    ,label:"org_画像",width: 600,labelWidth: 100,labelAlign:"right",hidden:true},
            {view:"text",id:"DR0021_org_imagefolder2" ,name:"DR0021_org_imagefolder2" ,label:"org_imagefolder2",width: 600,labelWidth: 100,labelAlign:"right",hidden:true},
            {view:"text",id:"DR0021_org_imagefile2"   ,name:"DR0021_org_imagefile2"   ,label:"org_画像2",width: 600,labelWidth: 100,labelAlign:"right",hidden:true},
            { margin:20,cols:[
                {view:"text",id:"DR0021_imagefile"    ,name:"DR0021_imagefile"    ,label:"画像",width: 400,labelWidth: 100,labelAlign:"right",hidden:true},
                {view:"text",id:"DR0021_imagefile2"   ,name:"DR0021_imagefile2"  ,label:"画像2",width: 400,labelWidth: 100,labelAlign:"right",hidden:true},

            ]},
            { margin:20,cols:[
                { view:"label",label:"", width: 200,height:150, id:"DR0021_image", name:"DR0021_image",
                    click:function(id,event){
                            DR0021_path_info = $$("DR0021_org_imagefolder").getValue();
                            DR0021_image_info = $$("DR0021_org_imagefile").getValue();
                            $$("template1").setValues({
                                   "path":DR0021_path_info+"/",
                                   "img":DR0021_image_info,
                                   "width":"380",
                                   "height":"380"
                                 });
  							$$('DR0021_imageWin').show();
                    },
                    onFocus:function(id,event){
                    }
                },
                { view:"label",label:"", width: 200,height:150, id:"DR0021_image2", name:"DR0021_image2",
                    click:function(id,event){
                            DR0021_path_info2 = $$("DR0021_org_imagefolder2").getValue();
                            DR0021_image_info2 = $$("DR0021_org_imagefile2").getValue();
                            $$("template1").setValues({
                                   "path":DR0021_path_info2+"/",
                                   "img":DR0021_image_info2,
                                   "width":"380",
                                   "height":"380"
                                 });
  							$$('DR0021_imageWin').show();
                    },
                    onFocus:function(id,event){
                    }
                },
            ]},
            { margin:20,cols:[
                { view:"uploader", 
                  value:"画像選択(撮影)",
                  accept:"image/jpeg, image/png",        
                  autosend:false, 
                  multiple:false,
                  fullscreen:true,
                  width: 120,
                  upload:"<?php  echo SUB_FOLDER; ?>/rest_api/DR0020/DR0027_upload_action.php",
                  id:"DR0021_image_btn",
                  on:{        
                    onBeforeFileAdd: function(item){
                       var type = item.type.toLowerCase();
                       if (type != "jpg" && type != "jpeg" && type != "png"){
                           webix.message("PNG または JPG フォーマットのみサポートします。");
                           return false;
                       }
                       var file = item.file;
                       upload_filename =file.name;
                       var reader = new FileReader();  
                       reader.onload = function(event) {
                            dailyreport_body = $$("dailyreport_imageWin").getBody();
                            if (!dailyreport_body.croppie) {
                                dailyreport_body.croppie = new Croppie(dailyreport_body.$view, {
                                    url:event.target.result,
                                    enableOrientation:true,
                                    enableResize:true,
                                    viewport: { width: 380, height: 380 }
                               });
                            }
                            else{
                               dailyreport_body.croppie.bind({
                                   url:event.target.result
                               });
                            }
                            $$("image_id").setValue("DR0021_imagefile");
                            $$("dailyreport_imageWin").show();
                       };           
                       reader.readAsDataURL(file)
                       return false;
                    },
                  }
                },
                { width: 50},
                { view:"uploader", 
                  value:"画像選択(撮影)2",
                  accept:"image/jpeg, image/png",        
                  autosend:false, 
                  multiple:false,
                  fullscreen:true,
                  width: 120,
                  upload:"<?php  echo SUB_FOLDER; ?>/rest_api/DR0020/DR0027_upload_action.php",
                  id:"DR0021_image_btn2",
                  on:{        
                    onBeforeFileAdd: function(item){
                       var type = item.type.toLowerCase();
                       if (type != "jpg" && type != "jpeg" && type != "png"){
                           webix.message("PNG または JPG フォーマットのみサポートします。");
                           return false;
                       }
                       var file = item.file;
                       upload_filename =file.name;
                       var reader = new FileReader();  
                       reader.onload = function(event) {
                            dailyreport_body = $$("dailyreport_imageWin").getBody();
                            if (!dailyreport_body.croppie) {
                                dailyreport_body.croppie = new Croppie(dailyreport_body.$view, {
                                    url:event.target.result,
                                    enableOrientation:true,
                                    enableResize:true,
                                    viewport: { width: 380, height: 380 }
                               });
                            }
                            else{
                               dailyreport_body.croppie.bind({
                                   url:event.target.result
                               });
                            }
                            $$("image_id").setValue("DR0021_imagefile2");
                            $$("dailyreport_imageWin").show();
                       };           
                       reader.readAsDataURL(file)
                       return false;
                    },
                  }
                },
            ]},
             { margin:20,cols:[
				{view:"label",label: "<hr style='border:0;border-top:1px solid blue;'>",id:"LINE01",width: 320},
            ]},
           { margin:20,cols:[
              {width:20},
              { view:"button", id:"DR0021_gps_get_btn",name:"DR0021_gps_get_btn", css:"webix_primary", value: "GPS取得", align:"center", width: 80,
                    click:function(){
                    	if(DR0020_errorNo != -1){
                    		webix.alert("一度、GPS取得で<br>エラー発生のため、<br>本メニューを閉じて、<br>再度開き直してください。");
                    		return;
                    	}
                    	else{
                    		get_gps_info();
                    	}
                    }
              },
              { view:"button", id:"DR0021_map_disp_btn",name:"DR0021_map_disp_btn", css:"webix_primary", value: "map表示", align:"center", width: 80,
                    click:function(){
                    	var lat = $$("DR0021_latitude").getValue();
                    	var lng = $$("DR0021_longitude").getValue();
                    	if(lat == "" || lng == ""){
                    		webix.alert("GPS情報が未取得です。");
                    		return;
                    	}
                    	DR0021_geoShow(lat,lng);
                    }
               },
            ]},
            { margin:20,cols:[
                {view:"text",label:"緯度",width: 150,labelWidth:40,labelAlign:"right",inputAlign:"right",value:"",id:"DR0021_latitude",name:"DR0021_latitude"},
            ]},
            { margin:20,cols:[
                {view:"text",label:"経度",width: 150,labelWidth:40,labelAlign:"right",inputAlign:"right",value:"",id:"DR0021_longitude",name:"DR0021_longitude"},
            ]},
            { margin:20,cols:[
				{view:"label",label: "<hr style='border:0;border-top:1px solid blue;'>",id:"LINE02",width: 320},
            ]},
            { margin:20,cols:[
                {view:"label", width:450,label: "",id:"DR0021_mess",css:"red"},
                {view:"text",id:"DR0021_dailyreport_editmode" ,name:"DR0021_dailyreport_editmode" ,label:"editmode",width: 200,labelWidth: 100,labelAlign:"right",hidden:true},
            ]},  
     ];




function check_form_data(){
	var report_date = $$("DR0021_report_date").getValue();
	if(report_date == null || report_date == ""){
		$$("DR0021_mess").setValue("作業日が未指定です。");
		webix.alert("作業日が未指定です。");
		return false;
	}
	var start_time = $$("DR0021_start_time").getValue();
	var end_time   = $$("DR0021_end_time").getValue();
	var duration_hour = $$("DR0021_duration_hour").getValue();
	if(start_time == null || start_time == ""){
		$$("DR0021_mess").setValue("作業開始時刻が未指定です。");
		webix.alert("作業開始時刻が未指定です。");
		return false;
	}
	if(end_time == null || end_time == ""){
		$$("DR0021_mess").setValue("作業終了時刻が未指定です。");
		webix.alert("作業終了時刻が未指定です。");
		return false;
	}
	if(duration_hour == ""){
		$$("DR0021_mess").setValue("経過時間が未指定です。");
		webix.alert("経過時間が未指定です。");
		return false;
	}
	return true;
}


webix.ui({
  view:"window",
  id:"dailyreport_imageWin", 
  position:"center",
  head:{
    view:"toolbar", cols:[
        { view:"label", label: "プレビュー" },
        { view:"text",id:"image_id" ,name:"image_id" ,label:"image_id",width: 200,labelWidth: 100,labelAlign:"right",hidden:true},
        { view:"button", label: 'キャンセル',
            click:function(){
                $$("image_id").setValue("");
                $$('dailyreport_imageWin').hide();
            }
        },
        { view:"button", label: '保存',
            click:function(){
            	var report_id = $$("DR0021_report_id").getValue();
                if (dailyreport_body.croppie) {
                    dailyreport_body.croppie.result('blob').then(function(blob) {
                        var edit_mode = $$("DR0021_dailyreport_editmode").getValue(); ///1:閲覧 2:更新3:新規登録
						var image_id = $$("image_id").getValue();

                    	var save_filename = "report_"+moment().format("YYYYMMDDHHmmss")+".jpg";
                    	var formData = new FormData();
                        var access_key = Get_AccessKey();
                        formData.append("accesskey",access_key);
                        formData.append("userid","admin");
                        formData.append('upload', blob, upload_filename);
                        formData.append("report_id",report_id);
                        formData.append("save_filename",save_filename);
                        var xhr =webix.ajax().sync().post("<?php  echo SUB_FOLDER; ?>/rest_api/DR0020/DR0027_upload_action.php",formData);
                       	var resp =  JSON.parse(xhr.responseText);
  						if(resp.status == "server"){
  							$$("dailyreport_imageWin").hide();
  							if(edit_mode == 2){
  								if(image_id == "DR0021_imagefile"){
  									$$("DR0021_org_imagefolder").setValue(resp.folder);
  									$$("DR0021_org_imagefile").setValue(resp.upload_filename);
  								}
  								else{
  									$$("DR0021_org_imagefolder2").setValue(resp.folder);
  									$$("DR0021_org_imagefile2").setValue(resp.upload_filename);
  								}
  							}
							if(image_id == "DR0021_imagefile"){
  								$$("DR0021_imagefolder").setValue(resp.folder);
  								$$("DR0021_imagefile").setValue(resp.upload_filename);
                            	var image = "<img src='"+resp.folder+"/"+resp.upload_filename+"' style='height:150px;cursor: hand; cursor:pointer;'>";
                            	$$("DR0021_image").setValue(image);
                            	$$("DR0021_image").refresh();
  							}
  							else{
  								$$("DR0021_imagefolder2").setValue(resp.folder);
  								$$("DR0021_imagefile2").setValue(resp.upload_filename);
                            	var image = "<img src='"+resp.folder+"/"+resp.upload_filename+"' style='height:150px;cursor: hand; cursor:pointer;'>";
                            	$$("DR0021_image2").setValue(image);
                            	$$("DR0021_image2").refresh();
  							}
							webix.message("UPLOAD操作が完了しました。<br>file="+resp.folder+"/<br>"+resp.upload_filename);
						}
  						else{
							webix.alert("のUPLOAD操作に失敗しました。"+resp.error_code);
  						}
                    });
                }
            }
        }
    ]
}, 
  close:true,
  body:{ 
    id:"tmp", 
    width:400,
    height:400,
    top:1,
    left:1,
  }
});


DR0021_dailyreport_win = webix.ui({
    view:"window", 
    id: "DR0021_dailyreport_win",
    move:true,
    resize:true,
    height : DR0021_win_resize_height,
    width : DR0021_win_resize_width,
    top : DR0021_win_resize_top,
    left : DR0021_win_resize_left,
head:{
    cols:[
        {template:"作業日報情報(DR0021)", type:"header", borderless:true},
        {view:"icon", icon:"wxi-close", tooltip:"画面を閉じます", click: function(){
            //閲覧モードの場合は、すぐに閉じる
            var form_mode = $$("DR0021_dailyreport_editmode").getValue(); //1:閲覧 2:編集 3:新規登録
            if(form_mode == "1")
            {
                DR0021_dailyreport_win.hide();
                $$("DR0021_dailyreport_editmode").setValue("1");  //1:閲覧 2:編集 3:新規登録
                return;
            }
            webix.confirm({
                title:"確認",
                ok:"はい", 
                cancel:"いいえ",
                text: "操作を中断して画面を閉じますか?",
            })
            .then(function(result){
                //「はい」の場合
                DR0021_dailyreport_win.hide();
                $$("DR0021_dailyreport_editmode").setValue("1");  //1:閲覧 2:編集 3:新規登録
            })
            .fail(function(){
                //「いいえ」の場合(2枚目)   
                webix.message({type:"debug",text:"操作を継続できます。"});
            });
        }}  
    ]
},
    body:{
        rows:[
            {
                margin:5,
                view:"form",
                id: "DR0021_dailyreport_win_form1",
                name: "DR0021_dailyreport_win_form1",
                elements:DR0021_dailyreport_form1,
                elementsConfig:{
                    bottomPadding:14
                },
                width:DR0021_win_resize_width,
     			scroll:true,
                margin:3
            },
            {height:40,
             cols:[
                {width:20},
                { view:"button",  css:"webix_danger", id:"DR0021_dailyreport_delete_btn",name:"DR0021_dailyreport_delete_btn",label:"削除", align:"center", width: 100,
                    click:function(){
                        $$("DR0021_mess").setValue("");
                        var DR0021_report_id = $$("DR0021_report_id").getValue();
                        
                        webix.confirm({
                            title:"確認",
                            ok:"はい", 
                            cancel:"いいえ",
                            text:"作業日報:"+DR0021_report_id+"を<br>削除しますか?"
                        })
                        .then(function(result){
                        	var DR0021_imagefile = $$("DR0021_imagefile").getValue();
                            var DR0021_imagefolder = $$("DR0021_imagefolder").getValue();
                        	if(DR0021_imagefile != ""){
                        		//登録画像の削除
                                DR0021_image_delete(DR0021_imagefolder,DR0021_imagefile);
                        	}
                        	var DR0021_imagefile2 = $$("DR0021_imagefile2").getValue();
                            var DR0021_imagefolder2 = $$("DR0021_imagefolder2").getValue();
                        	if(DR0021_imagefile2 != ""){
                        		//登録画像の削除
                                DR0021_image_delete(DR0021_imagefolder2,DR0021_imagefile2);
                        	}

                            $$("DR0021_dailyreport_editmode").setValue("4");  //1:閲覧 2:更新 3:新規 4:削除
                            var report_id = $$("DR0021_report_id").getValue();
                            var formData = new FormData();
        					var access_key = Get_AccessKey();
        					formData.append("accesskey",access_key);
        					formData.append("userid",  my_local_session.userid);
                            formData.append("report_id",report_id );
                            formData.append("edit_mode",4);
                            formData.append("form_id","DR0021");
         					var xhr =webix.ajax().sync().post("<?php  echo SUB_FOLDER; ?>/rest_api/DR0010/DR0016_delete_dailyreport.php",formData);
        					var resp =  JSON.parse(xhr.responseText);
        					if(resp.resp =="ok"){
        						DR0021_dailyreport_win.hide();
								var fl_user_id = my_local_session.userid;
								DR0020_select_dailyreport_lists(fl_user_id);
            					webix.message({type:"success",text:"作業日報を削除しました。"});
            					return;
        					}
        					else{
            					webix.alert("作業日報の削除でエラーしました。 code="+resp.error_code);
            					return;
        					}
                        })
                        .fail(function(){
                            webix.message("操作をキャンセルしました。");
                        });
                    }
                },
                {width:20},
                // 更新,登録
                { view:"button",  css:"webix_primary", id:"DR0021_dailyreport_udate_btn",name:"DR0021_dailyreport_udate_btn",label: "更新", align:"center", width: 100,
                    click:function(){
                        $$("DR0021_mess").setValue("");
                        if(check_form_data()){
                        }
                        else{
                        	return;
                        }
                        var edit_mode = $$("DR0021_dailyreport_editmode").getValue(); ///1:閲覧 2:更新3:新規登録
                        if(edit_mode == "2"){   //2:更新
                            //更新処理
                            var upate_datas = $$("DR0021_dailyreport_win_form1").getValues();
                            webix.confirm({
                            title:"確認",
                            ok:"はい", 
                            cancel:"いいえ",
                            text:"作業日報を更新しますか?"
                        	})
                            .then(function(result){
                            	start_time = moment($$("DR0021_start_time").getValue(),"HH:mm").format("HH:mm");
                            	end_time = moment($$("DR0021_end_time").getValue(),"HH:mm").format("HH:mm");
                                var formData = new FormData();
        						var access_key = Get_AccessKey();
        						formData.append("accesskey",access_key);
        						formData.append("userid",  my_local_session.userid);
                                formData.append("upate_datas",JSON.stringify(upate_datas));
                                formData.append("report_date",moment($$("DR0021_report_date").getValue()).format("YYYY-MM-DD"));
                                formData.append("start_time",start_time);
                                formData.append("end_time",end_time);
                                formData.append("edit_mode",edit_mode);
                            	formData.append("form_id","DR0021");
         						var xhr =webix.ajax().sync().post("<?php  echo SUB_FOLDER; ?>/rest_api/DR0010/DR0015_update_dailyreport.php",formData);
        						var resp =  JSON.parse(xhr.responseText);
        						if(resp.resp =="ok"){
        							$$("DR0021_report_id").setValue(resp.report_id);
        							DR0021_dailyreport_win.hide();
									var fl_user_id = my_local_session.userid;
									DR0020_select_dailyreport_lists(fl_user_id);
        							
            						webix.message({type:"success",text:"作業日報を更新しました。"});
            						
            						return;
        						}
        						else{
            						webix.alert("作業日報の更新でエラーしました。 code="+resp.error_code);
            						return;
        						}
                            })
                            .fail(function(){
                                webix.message("操作をキャンセルしました。");
                            });
                        }
                        else if(edit_mode == "3"){  //3:新規登録
                            var entry_datas = $$("DR0021_dailyreport_win_form1").getValues();
                            webix.confirm({
                            title:"確認",
                            ok:"はい", 
                            cancel:"いいえ",
                            text:"作業日報を新規登録しますか?"
                        	})
                            .then(function(result){
                            	start_time = moment($$("DR0021_start_time").getValue(),"HH:mm").format("HH:mm");
                            	end_time = moment($$("DR0021_end_time").getValue(),"HH:mm").format("HH:mm");
                                var formData = new FormData();
        						var access_key = Get_AccessKey();
        						formData.append("accesskey",access_key);
        						formData.append("userid",  my_local_session.userid);
                                formData.append("entry_datas",JSON.stringify(entry_datas));
                                formData.append("report_date",moment($$("DR0021_report_date").getValue()).format("YYYY-MM-DD"));
                                formData.append("start_time",start_time);
                                formData.append("end_time",end_time);
                                formData.append("edit_mode",edit_mode);
	                            formData.append("form_id","DR0021");
         						var xhr =webix.ajax().sync().post("<?php  echo SUB_FOLDER; ?>/rest_api/DR0010/DR0014_entry_dailyreport.php",formData);
        						var resp =  JSON.parse(xhr.responseText);
        						if(resp.resp =="ok"){
        							$$("DR0021_report_id").setValue(resp.report_id);
        							DR0021_dailyreport_win.hide();
									var fl_user_id = my_local_session.userid;
									DR0020_select_dailyreport_lists(fl_user_id);
        							
            						webix.message({type:"success",text:"作業日報を登録しました。"});
            						return;
        						}
        						else{
            						webix.alert("作業日報の登録でエラーしました。 code="+resp.error_code);
            						return;
        						}
                            })
                            .fail(function(){
                                webix.message("操作をキャンセルしました。");
                            });
                        }
                    }
                },
                { view:"button", id:"DR0021_dailyreport_cancel_btn",name:"DR0021_dailyreport_cancel_btn", css:"webix_primary", value: "キャンセル", align:"center", width: 100,css:"menu",
                    click:function(){
                         form_mode = $$("DR0021_dailyreport_editmode").getValue();    //1:閲覧 2:更新 3:新規登録
                        if(form_mode == "1")
                        {
                            DR0021_dailyreport_win.hide();
                            $$("DR0021_dailyreport_editmode").setValue("1");  //1:閲覧 2:更新 3:新規登録
                            return;
                        }
                        webix.confirm({
                            title:"確認",
                            ok:"はい", 
                            cancel:"いいえ",
                            text: "操作を中断して画面を閉じますか?",
                        })
                        .then(function(result){
                            //「はい」の場合
                            //画像ファイルの更新をチェックし、不要時はサーバから削除する
                            var DR0021_org_imagefile = $$("DR0021_org_imagefile").getValue();
                            var DR0021_imagefile = $$("DR0021_imagefile").getValue();
                            var DR0021_imagefolder = $$("DR0021_imagefolder").getValue();
                            if(DR0021_org_imagefile != DR0021_imagefile){
                                DR0021_image_delete(DR0021_imagefolder,DR0021_imagefile);
                            }
                            var DR0021_org_imagefile2 = $$("DR0021_org_imagefile2").getValue();
                            var DR0021_imagefile2 = $$("DR0021_imagefile2").getValue();
                            var DR0021_imagefolder2 = $$("DR0021_imagefolder2").getValue();
                            if(DR0021_org_imagefile2 != DR0021_imagefile2){
                                DR0021_image_delete(DR0021_imagefolder2,DR0021_imagefile2);
                            }

                            DR0021_dailyreport_win.hide();
                            $$("DR0021_dailyreport_editmode").setValue("1");  //1:閲覧 2:編集 3:新規登録
                        })
                        .fail(function(){
                            //「いいえ」の場合(2枚目)   
                            webix.message({type:"debug",text:"操作を継続できます。"});
                        });
                    }
                }
            ]
            }
        ]}
});

var dailyreport_body = $$("dailyreport_imageWin").getBody();

function DR0021_image_delete(imagefolder,imagefile){
    var formData = new FormData();
    var access_key = Get_AccessKey();
    formData.append("accesskey",access_key);
    formData.append("userid",my_local_session.userid);
    formData.append("job_info","DR0021");
    formData.append("imagefolder",imagefolder);
    formData.append("imagefile",imagefile);
    var xhr =webix.ajax().sync().post("<?php  echo SUB_FOLDER; ?>/rest_api/DR0020/DR0028_image_delete.php",formData);
    var resp =  JSON.parse(xhr.responseText);
    
}

webix.ui({
  id:"uploadAPI2",
  view:"uploader",
  autosend:true,
  formData:function(){
        var report_id = $$("DR0021_report_id").getValue();
        return {
            report_id:report_id,
        };
    },
  upload:"<?php  echo SUB_FOLDER; ?>/rest_api/DR0020/DR0027_upload_action.php",
  on:{
    onBeforeFileAdd:function(item){
      var type = item.type.toLowerCase();
      if (type != "jpg" && type != "jpeg" && type != "png"){
        webix.message("PNG または JPG フォーマットのみサポートします。");
        return false;
      }
    },
    onFileUpload:function(item){
      var upd_status = item.status;
      if(upd_status == "server"){
        var imagefile = item.upload_filename;
        var folder = item.folder;
		var image_id = $$("image_id").getValue();
        var image = "<img src='"+folder+"/"+imagefile+"' style='height:150px;cursor: hand; cursor:pointer;'>";
		if(image_id == "DR0021_imagefile"){
        	$$("DR0021_imagefile").setValue(imagefile);
        	$$("DR0021_imagefolder").setValue(folder);
        	$$("DR0021_image").setValue(image);
        	$$("DR0021_image").refresh();
        }
        else{
        	$$("DR0021_imagefile2").setValue(imagefile);
        	$$("DR0021_imagefolder2").setValue(folder);
        	$$("DR0021_image2").setValue(image);
        	$$("DR0021_image2").refresh();
        }
      }
      else{
        webix.alert("画像保存に失敗しました。");
      }
    },
    onFileUploadError:function(item){
      webix.alert("Error during file upload");
    }
  },
  apiOnly:true
});


webix.ui({
  view:"window",
  id:'DR0021_imageWin',

<?php
    if($userclient != "pc"){
?>
  fullscreen:true,
<?php
}
?>


  body:{"view":"template",
         id:"template1",
         "template":"<img src='#path##img#'style='height:380px;'/>"
       },
  head:{ view:"toolbar", cols:[
         { view:"label", label: "登録画像" },
         { view:"button", label: '閉じる',
            click:function(){
                $$('DR0021_imageWin').hide();
            }
         },
    ]
  },
  top:50,
  left:1,
  width:380,
  height:380
});




$$("DR0021_start_time").attachEvent("onChange", function(newValue, oldValue, config){
	var new_start_time = newValue;
	var duration_hour = $$("DR0021_duration_hour").getValue();
	var new_end_time = moment(new_start_time).add(duration_hour,'h').format("HH:mm");
    $$("DR0021_end_time").setValue(moment(new_start_time).add(duration_hour,'h').format("HH:mm"));
    
});

$$("DR0021_duration_hour").attachEvent("onChange", function(newValue, oldValue, config){
	var start_time = $$("DR0021_start_time").getValue();
	if(start_time!= ""){
		var duration_hour = newValue;
		var v1= moment(start_time,"HH:mm").add(duration_hour ,'h').format("HH:mm");
    	$$("DR0021_end_time").setValue(v1);
    }
});
$$("DR0021_end_time").attachEvent("onChange", function(newValue, oldValue, config){
	var start_time = $$("DR0021_start_time").getValue();
	var end_time = newValue;
	if(start_time!= "" && end_time != null){
		var duration_hour = $$("DR0021_duration_hour").getValue();
		var v1= moment(end_time,"HH:mm").subtract(duration_hour ,'h').format("HH:mm");
    	$$("DR0021_start_time").setValue(v1);
    }
});

写真をサーバに保存するREST_APIは、以下のとおりです。
DR0027_upload_action.php

 <?php
//======================================================================
//File Name       : DR0027_upload_action.php
//Encoding        : UTF-8
//Creation Date   : 2024-06-29
// 
//Copyright © 2024 sunsunfarm. All rights reserved.
// 
//This source code or any portion thereof must not be  
//reproduced or used in any manner whatsoever.
//====================================================================== 
    header("Content-Type: text/javascript; charset=utf-8");
    $myfilename = basename(__FILE__);   //自分自身のファイル名取得
    $logheader = 'log:'.$myfilename.':';//ログ出力時のヘッダー情報(自ファイル名を付与)
    include('../../commonlib/svr_common_lib_v2.php');   //
    if($_SERVER["REQUEST_METHOD"] != "POST"){
      error_log($logheader."REQUEST_METHOD NOT POST");
      header("HTTP/1.0 404 Not Found");
      return;
    }
    $userid = "";
    if(isset($_POST['userid'])){
        $userid = $_POST['userid'];
        $error_flag = 1;
    }

    $ph_status = 0; //0 init 1:temp_folder copy 2:target folder move
    $image_folder = '/webix02/image/dailyreport';
    $base_folder = '/home/sunsun/www'.$image_folder;
    $destination = $base_folder; //my folder
    error_log($logheader."destination =".$destination);

	$save_filename = "";
	if(isset($_POST['save_filename'])){
		$save_filename = $_POST['save_filename'];
	}



    $upload_filename = "";
    
//同一ファイル名の場合、ファイル名を変更して返す    
function unique_filename($org_path, $num=0){
    if( $num > 0){
        $info = pathinfo($org_path);
        $path = $info['dirname'] . "/" . $info['filename'] . "_" . $num;
        if(isset($info['extension'])) $path .= "." . $info['extension'];
    } else {
        $path = $org_path;
    }
     
    if(file_exists($path)){
        $num++;
        return unique_filename($org_path, $num);
    } else {
        return $path;
    }
}   
    
//Main
    try {
        $config_obj = get_config_obj();

        $folder_name =  $destination;
        error_log($logheader."folder_name =".$folder_name);
        if (!file_exists($folder_name)){
            error_log($logheader." create folder:".$folder_name);
            mkdir($folder_name, 0777);
        }
    }catch (Exception $e) {
        error_log($logheader."folder conv error ");
        $res = array("status" => "error","error_code" => -2,"folder" => $image_folder);
        echo json_encode($res);
        exit();
    }

    //ファイルの格納処理
    if(isset($_FILES['upload'])){
        $file = $_FILES['upload'];
        $filename_tmp = $file["name"];
        if($filename_tmp == "image.jpg"){
            error_log($logheader."filename rename to".$upload_filename);
        }
        $upload_filename = $filename_tmp;
        error_log($logheader."filename =".$upload_filename.' save_filename='.$save_filename);
        if($save_filename == ""){
        	$moved_filepath_name = $folder_name."/".$upload_filename;
        }
        else{
        	$moved_filepath_name = $folder_name."/".$save_filename;
        }
        
        //移動先にファイルがあるかチェック
        $moved_filepath_name = unique_filename($moved_filepath_name);
        $file_infos = pathinfo($moved_filepath_name);
        $path_info = $file_infos['dirname'];
        $upload_filename = $file_infos['basename'];
        
        error_log($logheader."moved_filepath_name=".$moved_filepath_name.' upload_filename ='.$upload_filename);
        if (file_exists($moved_filepath_name )){
            //ファイルがあったら、削除して上書き
            error_log($logheader."same file exist: ".$moved_filepath_name);
            
            $result = unlink($moved_filepath_name);
            error_log($logheader."delete temp file:".$moved_filepath_name);
        }

        if($upload_filename != ""){
            $filename = $destination."/".preg_replace("|[\\\/]|", "",$upload_filename);
        }
        else{
            $filename = $destination."/".preg_replace("|[\\\/]|", "",$filename_tmp);
        }
        error_log($logheader."filename =".$filename);
        if ($filename !== "" && file_exists($filename)){
            //先にtempfileを削除する
            $result = unlink($filename);
            error_log($logheader."delete temp file:".$filename);
        }

        if ($filename !== "" && !file_exists($filename)){
            error_log($logheader."move_uploaded_file:".$file["tmp_name"] );
            error_log($logheader."move_size:".$file["size"] );

            move_uploaded_file($file["tmp_name"], $filename);
            
            $ph_status = 1; //0 init 1:temp_folder copy 2:target folder move
            
            //指定フォルダに移動させる
            try {
                $resp = rename($filename, $moved_filepath_name);
                if($resp != true){
                    error_log($logheader."file move error from:".$filename." to:".$moved_filepath_name);
                    $res = array("status" => "error","error_code" => -4,"folder" => $image_folder,"upload_filename" => $upload_filename);
                    echo json_encode($res);
                    exit();
                }
            
            }catch (Exception $e) {
                error_log($logheader."file move error ");
                $res = array("status" => "error","error_code" => -5,"folder" => $image_folder,"upload_filename" => $upload_filename);
                echo json_encode($res);
                exit();
            }
            
            $ph_status = 2; //0 init 1:temp_folder copy 2:target folder move

            error_log($logheader.'$filename='.$filename.' status=server');
            $res = array("status" => "server", "error_code" => 0,"folder" => $image_folder,"upload_filename" => $upload_filename);
            
        }
        else {
            if(file_exists($filename)){
                if($ph_status == 1){
                    //delete temp file
                    $result = unlink($filename);
                    error_log($logheader."delete temp file:".$filename);
                }
                error_log($logheader."filename =".$filename." is exists error_code:-6");
                $res = array("status" => "error","error_code" => -6,"folder" => $image_folder,"upload_filename" => $upload_filename);
            }
            else if($filename == ""){
                if($ph_status == 1){
                    //delete temp file
                    $result = unlink($filename);
                    error_log($logheader."delete temp file:".$filename);
                }
                error_log($logheader."filename is blank error_code:-7");
                $res = array("status" => "error","error_code" => -7,"folder" => $image_folder,"upload_filename" => $upload_filename);
            }
            else{
                if($ph_status == 1){
                    //delete temp file
                    $result = unlink($filename);
                    error_log($logheader."delete temp file:".$filename);
                }
                error_log($logheader."external error  error_code:-8");
                $res = array("status" => "error","error_code" => -8,"folder" => $image_folder,"upload_filename" => $upload_filename);
            }
        }
        echo json_encode($res);
        exit;
    }
    else{
        error_log($logheader." not foud upload file error_code:-1");
        $res = array("status" => "error","error_code" => -39,"folder" => $image_folder,"upload_filename" => $upload_filename);
        echo json_encode($res);
    }

?>

管理用のデータベースは、2つの画像情報を管理できるようにフィールドを追加しました。

CREATE TABLE `dailyreports` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id:自動連番',
`user_id` varchar(255) NOT NULL DEFAULT '' COMMENT 'ユーザID',
`report_date` date DEFAULT NULL COMMENT '日付',
`work_place` varchar(255) NOT NULL DEFAULT '' COMMENT '作業場所',
`work_details` varchar(255) NOT NULL DEFAULT '' COMMENT '作業内容',
`start_time` time DEFAULT NULL COMMENT '開始時刻',
`end_time` time DEFAULT NULL COMMENT '終了時刻',
`duration_hour` varchar(16) NOT NULL DEFAULT '0.0' COMMENT '経過時間',
`photo_name` varchar(255) NOT NULL DEFAULT '' COMMENT '写真ファイル名',
`photo_name2` varchar(255) NOT NULL DEFAULT '' COMMENT '写真ファイル名2',
`photo_folder` varchar(255) NOT NULL DEFAULT '' COMMENT '写真フォルダ名',
`photo_folder2` varchar(255) NOT NULL DEFAULT '' COMMENT '写真フォルダ名2',
`workers` int(4) NOT NULL DEFAULT 0 COMMENT '作業人数',
`memo` varchar(255) NOT NULL DEFAULT '' COMMENT 'メモ',
`latitude` varchar(255) NOT NULL DEFAULT '' COMMENT '緯度',
`longitude` varchar(255) NOT NULL DEFAULT '' COMMENT '経度',
`created_userid` varchar(16) NOT NULL DEFAULT 'admin' COMMENT '作成ユーザ',
`updated_userid` varchar(16) NOT NULL DEFAULT 'admin' COMMENT '更新ユーザ',
`created_on` datetime DEFAULT NULL COMMENT '作成日時',
`updated_on` datetime DEFAULT NULL COMMENT '更新日時',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='作業日報';

新規登録時のREST_APIソースです。

 <?php
 	//DR0014_entry_dailyreport.php
	//header("Content-Type: text/javascript; charset=utf-8");
	$myfilename = basename(__FILE__);	//自分自身のファイル名取得
	$userid = '';
	$logheader = 'userid='.$userid.', '.$myfilename.':';//ログ出力時のヘッダー情報(自ファイル名,ログインIDを付与)
	if($_SERVER["REQUEST_METHOD"] != "POST"){
	  //POST以外ははじく
	  error_log($logheader.' not POST method');
	  header("HTTP/1.0 404 Not Found");
	  return;
	}
	//指定パラメータで作業日報新規追加(INSERT)する
    //POST情報からパラメータ取得
	$accesskey = 0;
	if(isset($_POST['accesskey'])){
		 if(is_numeric($_POST['accesskey'])){
			$accesskey = $_POST['accesskey'];
			$error_flag = 1;
		}
	}
	else{
		$error_flag = -1;
	  	error_log($logheader.'  accesskey not found');
	  	header("HTTP/1.0 404 Not Found");
	  	exit;
	}
	if(isset($_POST['userid'])){
		$userid = $_POST['userid'];
		$error_flag = 1;
	}
	else{
		$error_flag = -1;
	  	error_log($logheader.'  userid not found');
	  	header("HTTP/1.0 404 Not Found");
	  	exit;
	}
	$job_info = "DR0014";
	if(isset($_POST['job_info'])){
		$job_info = $_POST['job_info'];
	}

	$report_date = $_POST['report_date'];
	$start_time = $_POST['start_time'];
	$end_time = $_POST['end_time'];
	
    
	$logheader = 'userid='.$userid.', '.$myfilename.':';//ログ出力時のヘッダー情報(自ファイル名,ログインIDを付与)

	include('../../commonlib/svr_common_lib_v2.php');	//
	$config_obj = get_config_obj();
	//アクセスキーチェック
	if(Chk_AccessKey($accesskey)){
	}
	else{
		error_log($logheader.'  accesskey check error');
	  	header("HTTP/1.0 404 Not Found");
	  	exit;

	}

	$edit_mode = "1";
	if(isset($_POST['edit_mode'])){
		$edit_mode = $_POST['edit_mode'];	//1:閲覧 2:更新 3:新規 4:削除
	}

    $form_id ='DR0011';
	if(isset($_POST['form_id'])){
		$form_id = $_POST['form_id'];
	}


	if( $edit_mode != "3"){
		error_log($logheader.'prm error  userid='.$userid.' edit_mode='.$edit_mode);
 		$resp = "ng";
		$error_code = -1;
		$json_data = json_encode(compact("resp","error_code"),JSON_UNESCAPED_UNICODE);
		echo $json_data;
   		return;
	}
	//Form data
	if(isset($_POST['entry_datas'])){
		$entry_datas_str = $_POST['entry_datas'];
 		$entry_datas = json_decode($entry_datas_str ,true);
	}
    //
    //メインルーチン
    //
   	$config_obj = get_config_obj();
    //データベース接続する(MySQLDB)
    $dbh = mariadb_connect($config_obj,'app01','webix_webix');
    if($dbh == false){
    	//エラー応答
    	$resp = "ng";
		$error_code = -2;
 		$json_data = json_encode(compact("resp","error_code"),JSON_UNESCAPED_UNICODE);
		echo $json_data;
		exit;
    }

  	try{
	
		$insert_sql1 = "INSERT INTO dailyreports(user_id,report_date,work_place,work_details,start_time,end_time,duration_hour,workers,memo,latitude,longitude,photo_name,photo_folder,photo_name2,photo_folder2,created_userid,updated_userid,created_on,updated_on)  VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
		$insert_stmt =  $dbh->prepare($insert_sql1);
		// 静的プレースホルダを指定
   		$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
   		// エラー発生時に例外を投げる
   		$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
	
    	//トランザクション処理を開始
    	$dbh->beginTransaction();
		$resp = "ok";
		$error_code = 0;
		//dailyreportsに追加する情報
		$insert_stmt->bindValue(1,$userid);
		$insert_stmt->bindValue(2,$report_date);
		$insert_stmt->bindValue(3,$entry_datas[$form_id."_work_place"]);
		$insert_stmt->bindValue(4,$entry_datas[$form_id."_work_details"]);
		$insert_stmt->bindValue(5,$start_time);
		$insert_stmt->bindValue(6,$end_time);
		$insert_stmt->bindValue(7,$entry_datas[$form_id."_duration_hour"]);
		$insert_stmt->bindValue(8,$entry_datas[$form_id."_workers"]);
		$insert_stmt->bindValue(9,$entry_datas[$form_id."_memo"]);
		$insert_stmt->bindValue(10,$entry_datas[$form_id."_latitude"]);
		$insert_stmt->bindValue(11,$entry_datas[$form_id."_longitude"]);
		$insert_stmt->bindValue(12,$entry_datas[$form_id."_imagefile"]);
		$insert_stmt->bindValue(13,$entry_datas[$form_id."_imagefolder"]);
		$insert_stmt->bindValue(14,$entry_datas[$form_id."_imagefile2"]);
		$insert_stmt->bindValue(15,$entry_datas[$form_id."_imagefolder2"]);
		$insert_stmt->bindValue(16,$userid);	//created_userid
		$insert_stmt->bindValue(17,$userid);	//updated_userid
		$insert_stmt->bindValue(18,date("Y-m-d H:i:s"));
		$insert_stmt->bindValue(19,date("Y-m-d H:i:s"));
		$insert_stmt->execute();
		error_log($logheader.' insert dailyreports report_date='.$report_date.' start_time='.$start_time.' end_time='.$end_time.' duration_hour='.$entry_datas[$form_id."_duration_hour"]);
		$select_stmt1 = $dbh->prepare("SELECT LAST_INSERT_ID() as report_id");
		$select_stmt1->execute();
		$insert_code = $select_stmt1->fetchAll(PDO::FETCH_ASSOC); //フィールド名だけで保存(index情報なし)
		$report_id = $insert_code[0]["report_id"];
		error_log($logheader.' insert report_id:'.$report_id);
		$dbh->commit();
		error_log($logheader.' commit');
  	} catch(PDOException $e){
		error_log($logheader.'  捕捉した例外: '.$e->getMessage());	//例外発生時の処理(エラー情報をログに格納してエラー応答)
		$resp = "ng";
		$error_code = -1;
		$report_id = "0";
  	}
	$dbh = null;
	$json_data = json_encode(compact("resp","error_code","report_id"),JSON_UNESCAPED_UNICODE);
	echo $json_data;
?>

作業日報の事例としてカメラ連携機能を追加した内容ですが、ほぼ、実用的に使える機能にしています。参考にして、実装してみてください。
必要であれば、有料にはなりますが、開発支援などを受けることも可能です。
コメントなどで要望や質問があれば、遠慮なく、投稿してください。
(補足)前回の投稿で、GoogpeMapの実装を紹介していますが、現在、該当ソースでは、エラーが発生しています。原因が判明しだい、記事を修正し、ソースコードも修正します。

この記事が気に入ったらサポートをしてみませんか?