JTBC是一款全栈开发的原生框架,遵循AGPL协议,这里要感谢原作者jetiben,多年以来玩过许多框架,这个是目前为止个人感觉最好玩的一版,这里需要注意千万不要申请著作权,否则原作者保留一切诉讼权利。本框架牵扯的知识点有PHP、HTML、CSS包括(CSS3)、XML、JAVASCRIPT以及MySQL数据库,另外本框架附函数说明,二开时遵循其函数语法就能正常使用,这写基本都是废话,能看到这里你也挺不容易。
JTBC5要求最低PHP8.0+以及MYSQL8.0+版本。
推荐环境
操作系统:Ubuntu 20.04 LTS 或更高的长期支持版
HTTP服务器:Nginx 最新稳定版
PHP:8.0+
MySQL:8.0+
其他可运行环境
操作系统:Windows、CentOS、Debian 等
HTTP服务器:IIS、Apache 等
PHP:8.0+
MySQL:8.0+
硬件要求不高,但存储空间一定要根据未来使用量,作提前规划。
宝塔面板安装科一键部署,
在Nginx下配置地址重写
只需在Nginx站点配置文件的server配置项下增加如下:
if (!-f $request_filename) {
rewrite ^(.*)$ /index.php$1 last;
break;
}
请注意,使用Nginx运行PHP需要支持PATH_INFO模式,如果您不了解具体设置,可以按照对应的版本在网上搜索一下具体的设置方法。
在Apache下配置地址重写
只需在Public目录下新增一个.htaccess配置文件,内容如下:
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ /index.php/$1 [QSA,PT,L]
</IfModule>
在IIS下配置地址重写
需要安装地址重写模块,下载地址:https://www.iis.net/downloads/microsoft/url-rewrite,安装完成之后,在Public目录下设置web.config文件,内容为:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="index">
<match url="(.*)" />
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
</conditions>
<action type="Rewrite" url="index.php/{R:1}" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
小皮面板安装:
首先安装NGINX1.15.11和MySQL8.0.12然后点击首页一键启动里面的WNMP的小红点,启动NGINX1.15.11和MySQL8.0.12,然后确定,点击WNMP的启动。
创建网站,把提前下载好的文件解压到小皮的根目录d:/phpstudy_pro/www/,点击伪静态
if (!-f $request_filename) {
rewrite ^(.*)$ /index.php$1 last;
break;
}
创建数据库,导入安装文件里面的SQL
*重点提示:网站根目录到PUBLIC,数据库需要修改root账户密码
在地址栏输入地址/_install/开始安装框架,数据库信息保持一致
以下为JTBC5的根目录结构
目录名 用途
App 存放应用的共用类库
Bootstrap 存放启动文件
Config 存放配置文件
Jtbc 存放JTBC5基础类库
Pem 存放证书文件
Phar 存放第三方的Phar代码包
Public 站点的对外主目录
Runtime 存放运行中的临时文件
(此目录在任何情况下均应该设置为可读写)
Vendor 存放第三方的类库
以下为JTBC5中Public目录下的结构
目录名 用途
Public/common 公共目录
Public/common/assets 公共的静态资源目录
Public/common/diplomat 首页的执行文件目录
Public/common/language 公共的语言文件目录(主要用于首页)
Public/common/template 公共的模板文件目录(主要用于首页)
Public/console 系统管理的模块目录(该模块固定)
Public/universal 公共管理的模块目录(该模块固定)
Public/… 各模块自己的目录
在JTBC5中,除了common目录是一个特殊的目录以外,其他的目录均为某一个模块的目录,其中console和universal两个目录是基础目录,一般情况不移除。
目录规范
对内的目录名遵循大驼峰命名规范
对外的目录名统一小写(主要是Public目录下的子目录)
公共资源目录,固定命名为common
公共资源中的素材,固定命名为common/assets
公共资源中的代码,固定命名为common/diplomat
公共资源中的语言,固定命名为common/language
公共资源中的模板,固定命名为common/template
这里值得夸赞的就是J5的两两分离,前后端分离,所有前后端模块都在public里面,而后台管理在console里面,前段是index.jtbc,后端是manage.jtbc,二次开发时互不干涉。
根目录/public/common/template/index.jtbc里面的class=“home_section”>为首页栏目模块.后面的数字是分为几栏。
下面是首页滚动条,由于滚动内容有限,只默认字长来滚动,因此需要加入不等的汉字空格符(这个知识点白嫖同事的)
<div class="box">
<p class="animate">
<b>                                XXXX医院信息科管理系统 </b>
</p>
</div>
根目录/public/common/template/communal.jtbc里面的mainmenu是首页的菜单栏
<li genre="模块名称英文"><a href="{$=$getActualRoute('模块名称英文/')}">{$=$take('global.模块名称英文:index.title', 'lng')}</a></li>
语言太过简单自行修改不做赘述。
根目录/public/common/template/render.jtbc,第13行改为
<p type="title"> <b> <a href="product/?type=detail&id={$id}">{$title}</a> </b></p>
因为原文标记了占位符并显示为一列,影响页面美观,所以自定义一个P标签,title为标题,product是模块名称。
以product为例在第76行后面添加一个按钮
<input id="button" type="button" value="导出到Excel">
然后在后面天EXCEL导出功能
<script type="text/javascript">
var tableToExcel = (function() {
var uri = 'data:application/vnd.ms-excel;base64,',
template = '<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40"><head><!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets><x:ExcelWorksheet><x:Name>{worksheet}</x:Name><x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions></x:ExcelWorksheet></x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]--></head><body><table>{table}</table></body></html>',
base64 = function(s) { return window.btoa(unescape(encodeURIComponent(s))) },
format = function(s, c) { return s.replace(/{(\w+)}/g, function(m, p) { return c[p]; }) };
return function(table, name, filename) {
if (!table.nodeType) table = document.getElementById(table);
console.log(table.innerHTML)
var ctx = { worksheet: name || 'Worksheet', table: table.innerHTML }//此时的innerHTML数据可以自己自定义 比如json转化 只要值要数据符合即可
document.getElementById("dlink").href = uri + base64(format(template, ctx));
document.getElementById("dlink").download = filename;
document.getElementById("dlink").click();
}
});
var id = "tables",
worksheetName = 'sheet',
workName = "demo.xls";
document.getElementById('button').onclick = function() {
var download = tableToExcel();
download(id, worksheetName, workName)
};
</script>
<table id="tables">
这里一定要注意,如果ID定义为tables,以上代码中的ID一定要定义为tables否则无法实现,var id =tables与“table id =tables”中的ID命名不能与其它模块冲突且同一模块中需保持一致。
这里以news模块为例(news模块我已经修改成资料管理模块,在实际使用中可以将一些需要部署的插件及软件整理发布到此模块,也可以自行在模块管理中克隆或者新建一个有分类或无分类的模块)这里我只添加了一句,为了不影响其代码完整性,我把不需要的代码经过注释合理规避,胆大的可以直接删除,这句可以添加在根目录/public/news/common/template/index.jtbc第58行
<a href='{$=$htmlEncode($getValueFromJSON(#upfile, '0->filepath'))}'>附件下载</a>
“附件下载”为占位符也可自行修改,此句适合只下载第一条附件的场景,从实际使用中发现,这个就够用了,多个附件我们可以打包为一个附件或以添加多个标签来承载附件,还有更为复杂的就是在后台管理里面的摸版管理—公共摸版—添加一个att节点,这里牵扯两个函数说明, h t m l E n c o d e 将给定的字符串按照不同模式进行 H T M L 编码,意思就是你要让这个标签来干点什么。干点什么那? htmlEncode将给定的字符串按照不同模式进行HTML编码,意思就是你要让这个标签来干点什么。干点什么那? htmlEncode将给定的字符串按照不同模式进行HTML编码,意思就是你要让这个标签来干点什么。干点什么那?getValueFromJSON,从JSON字符串中获取指定名称的值。也就是我们上传的附件在upfile里面,然后给出一个地址,地址附在标签里面,说的已经很明白了。
div class="image"><p type="image"><img src="{$filepath}" alt="{$filename}" /></p></div>
基本没什么卵用,把简单的事情复杂化了,所以可以忽略上面这句。
动态时钟显示当前时间,我放在banner里面主要因为醒目且从功能布局上较为合理,这个比较简单,给根目录/public/common/template/index.jtbc里面的banner–box里面添加一个A标签定义为time,然后加入以下代码,需要注意的是代码里面的queryselector(“.a标签的class”)否则无法获取元素,另外在home.css里面同样需要添加一个banner box a.time的渲染效果,也可添加动画效果。
<script>
var t = null;
t = setTimeout(time, 1000); //開始运行
function time() {
clearTimeout(t); //清除定时器
dt = new Date();
var y = dt.getFullYear();
var mt = dt.getMonth() + 1;
var day = dt.getDate();
var h = dt.getHours(); //获取时
var m = dt.getMinutes(); //获取分
var s = dt.getSeconds(); //获取秒
document.querySelector(".time").innerHTML =
"" +
y +
"年" +
mt +
"月" +
day +
"-" +
h +
"时" +
m +
"分" +
s +
"秒";
t = setTimeout(time, 1000); //设定定时器,循环运行
}
</script>
CSS作为框架及页面的渲染语言可自行学习一点基础,否则使用起来还是有一定的压力,也可以变学习变练习,从功能上讲并不复杂,如果不修改大框架,只作菊部渲染处一些基础知识外只需要掌握几个过渡动画函数便可以随便改,还是那句话,在你不是很了解的情况下最好使用注释,否则报错后很难发现问题,CSS注释“/* 内容 */”,
header box mainmenu ul li a {
display: inline-block; font-size: 2.2rem; font-weight: bold; height: 30px; line-height: 30px; color: #666; position: relative; transition: color .2s ease;text-shadow:0 0 1.5px #005f9b
}
header box mainmenu ul li a::before,
header box mainmenu ul li a::after {
content: ''; position: absolute; width: 0%; bottom: 0px; height: 2px; background: #333333; transition: width .2s ease, background .2s ease
}
header box mainmenu ul li a::before {
left: 50%
}
header box mainmenu ul li a::after {
right: 50%
}
header box mainmenu ul li a:hover {
color: #43c1bf !important
}
header box mainmenu ul li a:hover::before,
header box mainmenu ul li a:hover::after {
width: 50%; background: #43c1bf !important
}
banner box slogan {
display: block; font-size: 4.8rem ; text-decoration:blink ; color: #ffffff; text-align: center;text-shadow:0 0 10px #ffffff; -webkit-animation: shine 1.2s infinite;/*设置动画*/
}
@-webkit-keyframes shine{/*创建动画*/
0%,100%{ color:#fff;text-shadow:0 0 10px #FAFAFA,0 0 10px #FAFAFA; }
50%{ text-shadow:0 0 10px #FAFAFA,0 0 40px #FAFAFA; }
}
banner box a.more {
display: block; width: 160px; margin: auto; margin-top: 10px; padding: 10px; border: #ffffff 2px solid; color: #ffffff; font-size: 1.4rem; text-align: center; border-radius: 10px; transition: border-radius .3s ease; box-shadow:0 0 10px #ffffff; ;animation: movei 800ms ease-out infinite alternate
}
@keyframes movei {
0% {
border-color: #fff;
box-shadow: 0 0 5px rgba(255,255,255.1), inset 0 0 5px rgba(255,255,255.1), 0 1px 0 #fff;
}
100% {
border-color: #fff;
box-shadow: 0 0 20px rgba(255,255,255.1), inset 0 0 10px rgba(255,255,255.1), 0 1px 0 #fff;
}
}
.box {
width: 100%;
margin: 0 auto;
border: 2px solid #43c1bf;
overflow: hidden;
}
.animate {
padding-left: 200px;
font-size: 20px;
color: #43c1bf;
display: inline-block;
white-space: nowrap;
animation: 15s wordsLoop linear infinite normal;
}
@keyframes wordsLoop {
0% {
transform: translateX(100%);
-webkit-transform: translateX(100%);
}
100% {
transform: translateX(-100%);
-webkit-transform: translateX(-100%);
}
}
@-webkit-keyframes wordsLoop {
0% {
transform: translateX(100%);
-webkit-transform: translateX(100%);
}
100% {
transform: translateX(-100%);
-webkit-transform: translateX(-100%);
}
}
.home_section div.box {
width: 100%; box-sizing: border-box; border-style: outset; border-width:1px; border-color:#43c1bf; border-radius:10px;
max-width: var(--page-max-width); margin: auto; padding: calc(var(--page-gap) * 2) 0px; box-shadow:0 0 20px #43c1bf;animation: glow 800ms ease-out infinite alternate
}
@keyframes glow {
0% {
border-color: #43c1bf;
box-shadow: 0 0 5px rgba(67,193,191,.1), inset 0 0 5px rgba(67,193,191.1), 0 1px 0 #43c1bf;
}
100% {
border-color: #43c1bf;
box-shadow: 0 0 20px rgba(67,193,191.1), inset 0 0 10px rgba(67,193,191.1), 0 1px 0 #43c1bf;
}
}
以上为主页CSS效果的全部代码,具体元素及动画函数自行脑补,基础渲染可根据属性多做尝试,动画效果主要由animation后面附的值引入,比如上图animation附的glow,动画函数keyframes就引入glow,这些属于基础知识。
<jtbc-qr-code content='{$=$htmlEncode(#content, 0)}'></jtbc-qr-code>
二维码<jtbc-qr-code content='内容'></jtbc-qr-code>
需要注意的是内容需要英文单引号,并且是全字段,单纯字段不识别或输出为字段名称的二维码。
打印功能的实现主要依赖JS,html部分只需要添加打印按钮,打印内容需要用JS包围
打印功能JavaScript主体
<script language="javascript">
function preview(oper){
if (oper < 10){
bdhtml=window.document.body.innerHTML;//获取当前页的html代码
sprnstr="<!--startprint"+oper+"-->";//设置打印开始区域
eprnstr="<!--endprint"+oper+"-->";//设置打印结束区域
prnhtml=bdhtml.substring(bdhtml.indexOf(sprnstr)+18); //从开始代码向后取html
prnhtml=prnhtml.substring(0,prnhtml.indexOf(eprnstr));//从结束代码向前取html
window.document.body.innerHTML=prnhtml;
window.print();
window.document.body.innerHTML=bdhtml;
}
else {
window.print();
}
}
</script>
按钮部分
<input type=button name='button_export' value='打印' onclick=preview(1) >
打印内容,下方示例为SBDA的index示例,只打印二维码,正常来说只需要用start和end把打印范围包围起来。
<!--startprint1--><jtbc-qr-code content='{$=$htmlEncode(#IPDZ, 0)}'></jtbc-qr-code><!--endprint1-->
在main适当位置添加,相对来说还是比较简单的。需要注意的是不能直接带字段,需要使用htmlencode函数,否侧输出内容有误。
<table border="1" cellspacing="0">
<tr>
<th>IP地址</th>
<th>所在科室</th>
<th>责任人</th>
<th>记录时间</th>
<th>维护记录</th>
</tr>
<tr>
<td>{$=$htmlEncode(#IPDZ, 0)}</td>
<td>{$=$htmlEncode(#SZKS, 0)}</td>
<td>{$=$htmlEncode(#syr, 0)}</td>
<td>{$=$htmlEncode(#time, 2)}</td>
<td>{$=$htmlEncode(#WHJL, 0)}</td>
</table>
以table包围,th为表头,td为表内容
数据库连接配置文件在根目录/config/DB/MYSQL.PHP文件里面
<?php
//******************************//
// JTBC Powered by jtbc.cn //
//******************************//
namespace Config\DB;
class MySQL
{
public const SERVER = ['default' => ['HOST' => 'localhost','USERNAME' => 'jtbcserver','PASSWORD' => 'admin123','DATABASE' => 'jtbcserver','CHARSET' => 'utf8mb4',],];
}
HOST是数据库的网络地址,如果是本机则为localhost
USERNAME是链接数据库的用户名
PASSWORD是链接数据库的密码
DATABASE是数据库的名称
CHARSET是链接数据库时采用的字符集
后续将测试oracle数据库的连接,主要因为建表太多,工作量较大,待测试后将更新文档。
1.添加调用节点
文件位置:
/common/template/render.jtbc
最后1个
下面添加
<![CDATA[product-id]]>
<![CDATA[{$id}]]>
2.数据调用方法
调用最后一个ID,此ID可能是已发布,未发布或已删除的数据
{
=
=
=render(‘global.render.product-id’, $fetch([‘genre’ => ‘product’, ‘orderBy’ => [[‘id’, ‘desc’]], ‘limit’ => 1]))}
调用最后一个ID,此ID是已发布的数据
{ = = =render(‘global.render.product-id’, $fetch([‘genre’ => ‘product’, ‘where’ => [‘published’ => 1], ‘orderBy’ => [[‘id’, ‘desc’]], ‘limit’ => 1]))}@TOC
未完待续