La idea era poder entender el funcionamiento del webview2, para poder aplicarlo a mediano plazo en nuestras aplicaciones, esta creado en html5, css y javascript puro, para los maestros, es posible que se pueda simplificar mucho el código usado, sobre todo del lado de javascript; ya teniendo otros recursos como un servidor, podemos enlazar librerías como jquery y boostrap.
Como digo fue montado para entender el funcionamiento webview2, lo compartimos por si alguien algún día llega a necesitarlo, le interesa y quizás le sirva de guía.
Con esto lo que pretendemos es poder mejorar en MUCHO la parte visual o interfase de usuario de nuestras apps, apoyándonos en las maravillas que permite el html y sus amigos jejejeje.
Esperamos les sirva.
Pdta. Apenas nos quede un tiempito vamos a generar un reporte tipo html para ser enviado a la impresora.
webview_custo.prg
- Code: Select all Expand view RUN
#include "FiveWin.ch"
static oWebView
static d_custo
function Main()
Local oWnd,cHtml
USE "customer.dbf" ALIAS d_custo NEW EXCLUSIVE VIA "DBFCDX"
INDEX ON d_custo->id TAG "id"
d_custo->(OrdSetFocus("id"))
cHtml := buildHtml()
DEFINE WINDOW oWnd TITLE "Information maintenance" SIZE 1200, 800
oWebView = TWebView2():New( oWnd )
oWebView:SetHtml( cHtml )
oWebView:InjectJavascript( cJavascript() )
oWebView:bOnBind = { | cJson, cCalls | whatscoming( cJson ) }
//oWebView:OpenDevToolsWindow()
ACTIVATE WINDOW oWnd CENTER ON RESIZE oWebView:SetSize( nWidth, nHeight ) VALID (d_custo->(DbCloseArea()),.T.)
oWebView:End()
return nil
function whatscoming( cJson )
Local oDlg,oGId,oGNm,oGAp,oGDi,oGEd,oGCi,oGSa
Local cName, cLast,cStreet,cCity,cAge,cSalary,cIdBack
Local nGId := 0
Local nGEd := 0
Local nGSa := 0
Local nIdDbf := 0
Local cGNm := space(0)
Local cGAp := space(0)
Local cGDi := space(0)
Local cGCi := space(0)
Local cIdRow := space(0)
Local cInsRow := space(0)
Local nAction := cJson[1]["action"]
if nAction==1
nGId := cJson[1]["datos"][1]
cGNm := cJson[1]["datos"][2]
cGAp := cJson[1]["datos"][3]
cGDi := cJson[1]["datos"][4]
cGCi := cJson[1]["datos"][5]
nGEd := val(cJson[1]["datos"][6])
nGSa := val(strtran(cJson[1]["datos"][7],",",""))
DEFINE DIALOG oDlg SIZE 350,250 PIXEL TRUEPIXEL TITLE "Edit Row"
@ 14, 15 SAY "Id :" SIZE 100,24 PIXEL OF oDlg
@ 10, 88 GET oGId VAR nGId SIZE 50,24 PIXEL OF oDlg PICTURE "999" RIGHT
@ 40, 15 SAY "Name :" SIZE 100,24 PIXEL OF oDlg
@ 36, 88 GET oGNm VAR cGNm SIZE 140,24 PIXEL OF oDlg PICTURE "@N"
@ 66, 15 SAY "Last :" SIZE 100,24 PIXEL OF oDlg
@ 62, 88 GET oGAp VAR cGAp SIZE 140,24 PIXEL OF oDlg PICTURE "@N"
@ 92, 15 SAY "Adress :" SIZE 100,24 PIXEL OF oDlg
@ 88, 88 GET oGDi VAR cGDi SIZE 200,24 PIXEL OF oDlg PICTURE "@N"
@ 118, 15 SAY "City :" SIZE 100,24 PIXEL OF oDlg
@ 114, 88 GET oGCi VAR cGCi SIZE 140,24 PIXEL OF oDlg PICTURE "@N"
@ 144, 15 SAY "Age :" SIZE 100,24 PIXEL OF oDlg
@ 140, 88 GET oGEd VAR nGEd SIZE 40,24 PIXEL OF oDlg PICTURE "999" SPINNER
@ 170, 15 SAY "Salary :" SIZE 100,24 PIXEL OF oDlg
@ 166, 88 GET oGSa VAR nGSa SIZE 100,24 PIXEL OF oDlg PICTURE "999,999.99" RIGHT
@ 220,15 BUTTONBMP OF oDlg PIXEL size 95, 24 PROMPT "Close" ACTION oDlg:End()
@ 220,120 BUTTONBMP OF oDlg PIXEL size 120, 24 PROMPT "Update" //ACTION oDlg:End()
oGId:disable()
oDlg:lHelpIcon := .F.
ACTIVATE DIALOG oDlg CENTERED
elseif nAction==2
if msgnoyes("Confirm delete row ?","Confirm")
nIdDbf := val(cJson[1]["datos"][1])
SELECT d_custo
DELETE FOR d_custo->id = nIdDbf
PACK
cIdRow := "row_"+alltrim(cJson[1]["datos"][1])
oWebView:Eval( "deleterowhtmltable( '"+cIdRow+"' )" )
endif
elseif nAction==0
nIdDbf := cJson[1]["datos"][1]
cName := cJson[1]["datos"][2]
cLast := cJson[1]["datos"][3]
cStreet := cJson[1]["datos"][4]
cCity := cJson[1]["datos"][5]
cAge := val(cJson[1]["datos"][6])
cSalary := val(cJson[1]["datos"][7])
SELECT d_custo
Dbgotop()
if !dbseek(nIdDbf)
dbappend()
//d_custo->id := nIdDbf //Incremental
d_custo->first := cName
d_custo->last := cLast
d_custo->street := cStreet
d_custo->city := cCity
d_custo->age := cAge
d_custo->salary := cSalary
cIdBack := cValtochar(d_custo->id)
oWebView:Eval( "insertrowhtmltable('"+cIdBack+"','"+transform(cSalary,"999,999.99")+"');")
else
//With any of these options you can inform the user that the id already exists
//msginfo("The id you are trying to enter already exists","Error")
//oWebView:Eval( "document.getElementById('spn_id').innerHTML='<br>Exists'")
oWebView:Eval( "alert('The id you are trying to enter already exists');")
endif
endif
return nil
function buildHtml()
Local cHead,cFoot
Local cHtml := space(0)
Local cTable := space(0)
TEXT INTO cHead
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<style>
body {
background-color: #e4f2ff;
display: flex;
flex-direction: column;
min-height: 100vh;
}
.main-content {
padding: 20px;
flex-grow: 1;
overflow-y: auto;
}
.table-container {
overflow-x: auto;
}
.table {
background-color: #fff;
min-width: 600px; /* Asegura que la tabla no se comprima demasiado en móviles */
}
h2 {
color: #0064a5;
}
footer {
background-color: #0064a5;
color: #fff;
text-align: center;
padding: 2rem 0;
margin-top: auto;
}
.btntbl {
border: none;
color: white;
padding: 5px 10px;
cursor: pointer;
border-radius: 5px;
}
.primary {background-color: #007bff;}
.primary:hover {background: #0b7dda;}
.secondary {background-color: #e7e7e7; color: black;}
.secondary:hover {background: #ddd;}
.success {background-color: #04AA6D;}
.success:hover {background-color: #46a049;}
.warning {background-color: #ff9800;}
.warning:hover {background: #e68a00;}
.danger {background-color: #f44336;}
.danger:hover {background: #da190b;}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: flex;
font-size: 16px;
font-weight: bold;
margin-bottom: 2px;
color: #0693e3;
}
.form-group input {
width: 90%;
padding: 5px;
font-size: 14px;
border-radius: 5px;
border: 1px solid #0693e3;
}
.msg{
color: #f44336;
}
/* The Modal (background) */
.modal {
display: none; /* Hidden by default */
position: fixed; /* Stay in place */
z-index: 1; /* Sit on top */
padding-top: 20px; /* Location of the box */
left: 0;
top: 0;
width: 100%; /* Full width */
height: 100%; /* Full height */
overflow: auto; /* Enable scroll if needed */
background-color: rgb(0,0,0); /* Fallback color */
background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
}
/* Modal Content */
.modal-content {
background-color: #fefefe;
margin: auto;
padding: 20px;
border: 1px solid #888;
width: 40%;
}
/* The Close Button */
.close {
color: #aaaaaa;
float: right;
font-size: 28px;
font-weight: bold;
}
.close:hover,
.close:focus {
color: #000;
text-decoration: none;
cursor: pointer;
}
table.tableSection {
display: table;
width: 100%;
}
table.tableSection thead,
table.tableSection tbody {
width: 100%;
}
table.tableSection thead {
overflow-y: scroll;
display: table;
table-layout: fixed;
width: calc(100% - 16px); /* assuming scrollbar width as 16px */
}
table.tableSection tbody {
overflow: auto;
height: 500px;
display: block;
}
table.tableSection tr {
width: 100%;
text-align: left;
display: table;
table-layout: fixed;
}
table.tableSection tr:hover {
background-color: #f8f9fa;
}
</style>
</head>
<body>
<div class="container-fluid">
<div class="row">
<!-- Contenido principal -->
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4 main-content">
<!-- Trigger/Open The Modal -->
<button id="myBtn" onclick="openmodal();" class="btntbl success">New</button>
<h2 class="mb-4">Customer Table </h2>
<div class="table-container">
<table class="tableSection" id="tbl_cstm">
<thead>
<tr>
<th style="width:25px">ID</th>
<th style="width:100px">Name</th>
<th style="width:100px">Last</th>
<th style="width:220px">Adress</th>
<th>City</th>
<th>Age</th>
<th>Salary</th>
<th colspan="2">Actions</th>
</tr>
</thead>
<tbody id="tableBody">
ENDTEXT
TEXT INTO cFoot
</tbody>
</table>
</div>
</main>
</div>
</div>
<footer>
<p>© 2024 My App. All rights reserved.</p>
</footer>
<!-- The Modal -->
<div id="myModal" class="modal">
<!-- Modal content -->
<div class="modal-content">
<span class="close" role="button" onclick="closemodal();" title="Close Modal (Cancel)">×</span>
<h2>Customer data</h2>
<div id="form_data">
<div class="form-group">
<label for="id">Id:</label>
<input type="text" id="id" name="id" class="myinput" value="0" disabled></input>
<span id="spn_id" class="msg"></span>
</div>
<div class="form-group">
<label for="name">Name:</label>
<input type="text" id="name" name="name" class="myinput" >
<span id="spn_name" class="msg"></span>
</div>
<div class="form-group">
<label for="last">Last:</label>
<input type="text" id="last" name="last" class="myinput" >
<span id="spn_last" class="msg"></span>
</div>
<div class="form-group">
<label for="adress">Adress:</label>
<input type="text" id="adress" name="adress" class="myinput" >
<span id="spn_adress" class="msg"></span>
</div>
<div class="form-group">
<label for="city">City:</label>
<input type="text" id="city" name="city" class="myinput" >
<span id="spn_city" class="msg"></span>
</div>
<div class="form-group">
<label for="age">Age:</label>
<input type="number" id="age" name="age" class="myinput" >
<span id="spn_age" class="msg"></span>
</div>
<div class="form-group">
<label for="salary">Salary:</label>
<input type="number" id="salary" name="salary" class="myinput" >
<span id="spn_salary" class="msg"></span>
</div>
<div class="form-group" >
<button type="submit" class="btntbl success" onclick="newdata();">Acept</button>
</div>
</div>
</div>
</div>
</body>
</html>
ENDTEXT
SELECT d_custo
Dbgotop()
Do While !EOF()
cTable += "<tr id='row_"+cValtochar(d_custo->id)+"'>"
cTable += "<td style='width:25px'>"+transform(d_custo->id,"999")+"</td>"
cTable += "<td style='width:100px'>"+alltrim(d_custo->first)+"</td>"
cTable += "<td style='width:100px'>"+alltrim(d_custo->last)+"</td>"
cTable += "<td style='width:220px'>"+alltrim(d_custo->street)+"</td>"
cTable += "<td>"+alltrim(d_custo->city)+"</td>"
cTable += "<td>"+transform(d_custo->age,"999")+"</td>"
cTable += "<td align='right'>"+transform(d_custo->salary,"999,999.99")+"</td>"
cTable += "<td>"
cTable += "<button onclick='how("+cValtochar(d_custo->id)+",1)' title='Edit' class='btntbl primary'>Edit</button>"
cTable += "</td>"
cTable += "<td>"
cTable += "<button onclick='how("+cValtochar(d_custo->id)+",2)' title='Delete' class='btntbl danger'>Delete</button>"
cTable += "</td>"
cTable += "</tr>"
d_custo->(DBSKIP())
EndDo
cHtml := cHead+cTable+cFoot
return cHtml
function cJavascript()
Local cInyec
TEXT INTO cInyec
function how(id,acc){
var id_row = "row_"+id;
var therow = document.getElementById(id_row);
var aDatos = new Array();
var objeto = { action : 0 ,id : "", datos : []};
var elementsrow = therow.getElementsByTagName("td");
for(i=0;i<7;i++){
aDatos.push(elementsrow[i].innerHTML);
}
objeto.action = acc;
objeto.id = id;
objeto.datos = aDatos;
SendToFWH(objeto);
}
function openmodal(){
var lamodal = document.getElementById("myModal");
lamodal.style.display = "block";
}
function closemodal(){
var lamodal = document.getElementById("myModal");
lamodal.style.display = "none";
}
function newdata(){
var id_row = "row_0";
var aDatos = new Array();
var objeto = { action : 0 ,id : 0, datos : []};
var aErrores = new Array();
var theform = document.getElementById("form_data");
var theinputs = theform.getElementsByTagName("input");
for(i=0;i<theinputs.length;i++){
var idname = theinputs[i].id;
var id_span = "spn_"+idname;
var oSpan = document.getElementById(id_span);
if(theinputs[i].value==""){
var cErro = "Please input "+idname;
aErrores.push(cErro);
oSpan.innerHTML = cErro;
}else{
oSpan.innerHTML = "";
}
}
if (aErrores.length==0){
aDatos.push(document.getElementById("id").value);
aDatos.push(document.getElementById("name").value);
aDatos.push(document.getElementById("last").value);
aDatos.push(document.getElementById("adress").value);
aDatos.push(document.getElementById("city").value);
aDatos.push(document.getElementById("age").value);
aDatos.push(document.getElementById("salary").value);
objeto.datos = aDatos;
SendToFWH(objeto);
}
}
function deleterowhtmltable(id){
var idrowdel = document.getElementById(id);
idrowdel.remove();
}
function insertrowhtmltable(idDbf,cSal){
var cIdRow = "row_"+idDbf;
var tbldata = document.getElementById("tbl_cstm").insertRow(-1);
tbldata.setAttribute("id", cIdRow );
var oCol1 = tbldata.insertCell(0);
var oCol2 = tbldata.insertCell(1);
var oCol3 = tbldata.insertCell(2);
var oCol4 = tbldata.insertCell(3);
var oCol5 = tbldata.insertCell(4);
var oCol6 = tbldata.insertCell(5);
var oCol7 = tbldata.insertCell(6);
var oCol8 = tbldata.insertCell(7);
var oCol9 = tbldata.insertCell(8);
oCol1.innerHTML = idDbf;
oCol1.style.width = "25px";
oCol2.innerHTML = document.getElementById("name").value;
oCol2.style.width = "100px";
oCol3.innerHTML = document.getElementById("last").value;
oCol3.style.width = "100px";
oCol4.innerHTML = document.getElementById("adress").value;
oCol4.style.width = "220px";
oCol5.innerHTML = document.getElementById("city").value;
oCol6.innerHTML = document.getElementById("age").value;
oCol7.innerHTML = cSal;
oCol7.setAttribute("align", "right");
oCol8.innerHTML = "<button onclick='how("+idDbf+",1)' title='Edit' class='btntbl primary'>Edit</button>";
oCol9.innerHTML = "<button onclick='how("+idDbf+",2)' title='Delete' class='btntbl danger'>Delete</button>";
location.hash = "#"+cIdRow;
closemodal();
}
ENDTEXT
Return cInyec