安装
yum -y install unoconv
yum install -y ImageMagick ImageMagick-devel
PPT转 PDF
unoconv -f pdf 1.ppt
指定路径
unoconv -o demo.pptx -f pptx 1.ppt
pdf转图片
convert 1.pdf %d.jpg
安装
yum -y install unoconv
yum install -y ImageMagick ImageMagick-devel
PPT转 PDF
unoconv -f pdf 1.ppt
指定路径
unoconv -o demo.pptx -f pptx 1.ppt
pdf转图片
convert 1.pdf %d.jpg
支持打开图片资源管理器,选择图片插入指定元素
HTML
<el-form-item label="封面" required>
<?php think_vue_media_button('image')?>
</el-form-item>
VUE
<?php
think_vue_media($vue,"
if(!this.form.image){
this.form.image= [];
}
for(let i in dd){
if(dd[i] && dd[i].url){
this.form.image.push(dd[i].url);
}
}
","
this.selected_media_use_muit = true;
");
$vue->method("remove_images(index)","
this.form.image.splice(index,1);
this.\$forceUpdate();
");
?>
注意其中 image 变量
Permission is hereby granted to any person obtaining a copy of this software
(the “Software”) to use, copy, modify, merge, publish and/or distribute copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
Failure to comply with the foregoing conditions will automatically and
immediately result in termination of the permission granted hereby. This
license does not include any right to receive updates to the Software or
technical support. Licensees bear all risk related to the quality and
performance of the Software and any modifications made or obtained to it,
including liability for actual and consequential harm, such as loss or
corruption of data, and any necessary service, repair, or correction.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER
LIABILITY, INCLUDING SPECIAL, INCIDENTAL AND CONSEQUENTIAL DAMAGES, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
https://github.com/thefunpower/helper
在composer.json中添加
"thefunpower/helper": "dev-main"
需要定义PATH
目录,项目的根目录
define("PATH",__DIR__.'/');
需要定义WWW_PATH
目录,网站访问的目录,有时PATH与WWW_PATH是一样的
define("WWW_PATH",__DIR__.'/');
确保有data
uploads
两个目录且可写.
data在根目录 uploads在网站访问的目录
连接
predis($host,$port,$auth);
发布消息
redis_pub("demo","welcome man");
redis_pub("demo",['title'=>'yourname']);
取订阅消息
redis_sub("demo",function($channel,$message){
echo "channel ".$channel."\n";
print_r($message);
});
连接
predis($host,$port,$auth);
获取
$s = predis_geo_pos('places',[
'上海外滩','北京天安门'
]);
pr($s) ;
添加
predis_add_geo('places',[
[
'lat'=>'116.397128',
'lng'=>'39.916527',
'title'=>'北京天安门'
],
[
'lat'=>'121.473701',
'lng'=>'31.230416',
'title'=>'上海外滩'
],
[
'lat'=>'121.45668',
'lng'=>'31.21706',
'title'=>'襄阳公园'
],
]);
附近分页
pr(predis_get_pager('places', 121.45668, 31.21706));
服务端
class ServerGetUser{
public function getInfo($name = 'abc'){
return ['welcome'=>$name,'token'=>rpc_token()];
}
}
rpc_server("ServerGetUser");
客户端
$client = rpc_client("http://127.0.0.1:5000/rpc.php");
$info = $client->getInfo("test");
print_r($info);
php.ini中开启ftp
扩展
把本地文件同步到FTP上。
如果FTP上目录文件已存在,将会被替换。
use helper_v3\Ftp;
$ftp = Ftp::start([
'host' =>'IP地址',
'user' =>'帐号',
'pwd' =>'密码',
'port' =>'端口,默认21',
]);
//上传到根目录
Ftp::put_all(__DIR__.'/uploads');
//或上传到指定目录
//Ftp::put_all(__DIR__.'/uploads','uploads');
Ftp::end();
更多方法 https://github.com/Nicolab/php-ftp-client
免费字体
阿里妈妈方圆体 alifanyuan
阿里妈妈数黑体 alishuhei
阿里巴巴普惠体 puhuiti
阿里巴巴普惠体细 puhuitithin
google字体 notosanssc
默认使用 notosanssc。
helper_v3\Pdf::init([
'fontDir'=>[''],
'fontdata'=>[
'simhei'=> [
'R' => 'simhei.ttf',
'I' => 'simhei.ttf',
],
],
'default_font'=>'simhei'
]);
安装依赖
yum install pdftk pdftk-java poppler-utils perl-Image-ExifTool.noarch ImageMagick ImageMagick-devel ghostscript -y
use helper_v3\Pdf;
$mpdf = Pdf::init();
$mpdf->WriteHTML('<h1>Hello world!</h1>');
$mpdf->Output();
$input = [
PATH.'uploads/1.pdf',
PATH.'uploads/2.pdf',
];
$new_file = '/完整路径/1.pdf';
echo Pdf::merger($input,$new_name);
exit;
Pdf::merger_with_image($files, $output);
Pdf::pdf_to_image($file,$saveToDir)
Pdf::get_info($file);
返回
Array
(
[header] => Array
(
[ModDate] => D
[Creator] => Microsoft® PowerPoint® 2019
[CreationDate] => D
[Producer] => Microsoft® PowerPoint® 2019
[Author] => Microsoft Office User
[Title] => PowerPoint 演示文稿
)
文档长宽
[dimensions] => Array
(
[0] => 960
[1] => 540
)
2是横版,1是竖版
[dimensions_type] => 2
)
Pdf::get_pages($file);
Pdf::set_info($file,$output,$arr = []);
其中arr
支持title
author
keywords
$html = '
<style>
table{
width: 100%;
text-align:left;
margin: 0 auto;
border: 1px solid #000000;
border-collapse: collapse;
}
th,td {
border: 1px solid #000000;
text-align: center;
}
</style>
<table cellspacing="0" cellpadding="0" border="0" >
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">First</th>
<th scope="col">Last</th>
<th scope="col">Handle</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">1</th>
<td>Mark</td>
<td>Otto</td>
<td>@mdo</td>
</tr>
<tr>
<th scope="row">2</th>
<td>Jacob</td>
<td>Thornton</td>
<td>@fat</td>
</tr>
<tr>
<th scope="row">3</th>
<td colspan="2">Larry the Bird</td>
<td>@twitter</td>
</tr>
</tbody>
</table>';
$mpdf = Pdf::init();
$mpdf->shrink_tables_to_fit = 1;
$mpdf->WriteHTML($html);
$mpdf->Output();
安装依赖
yum install xorg-x11-server-Xvfb wkhtmltopdf fontconfig freetype wqy-zenhei-fonts wqy-microhei-fonts
PHP中调用
html_to_pdf($input_html_file,$output_pdf_file,$return_cmd = false,$exec = false)
如遇条形码可用 php-barcode-generator
composer require picqer/php-barcode-generator
composer require phpoffice/phpspreadsheet
当前使用 "phpoffice/phpspreadsheet": "^1.20"
use helper_v3\Xls;
$all = db_get("catalog_product",'*');
foreach($all as $v){
$title = $v['title'];
$desc = $v['desc'];
$values[] = [
'title'=>$title,
'desc'=>$desc,
];
}
Xls::create([
'title'=>'编号',
'desc'=>'规格',
], $values, 'product', FALSE);
第一个worksheet
Xls::$label = $txt_month.'专票';
Xls::$sheet_width = [
'A' => "15",
'B' => "36",
'C' => "30",
'D' => "10",
'E' => "10",
'F' => "10",
];
更多worksheet
Xls::$works = [
[
'title' => $title,
'label' => $txt_month.'普票',
'data' => $new_data,
'width' => Xls::$sheet_width,
]
];
合并
Xls::$merge = [
'A18:E22'
];
Xls::create($title, $values, $name, FALSE);
依赖
yarn add ioredis
yarn add ws
1.生成server.js
echo create_node_ws_server($ws_port=3006,$topic=['demo'],$redis_host='127.0.0.1',$port='6379',$auth='');
复制代码至server.js
中
启动server
node server.js
2.HTML添加监听
依赖 reconnecting-websocket.js
<script>
<?php
$func = "
data = JSON.parse(data);
console.log(data);
";
echo get_ws_js($func,'ws://127.0.0.1:3006');
?>
</script>
其中ws://127.0.0.1:3006
如果是 wss 则wss://yourdomain/wss
3.php发送消息
redis_pub("demo",['title'=>'yourname']);
如使用wss则需配置Nginx转发
location /wss {
proxy_pass http://127.0.0.1:3006;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
rewrite /wss/(.*) /$1 break;
proxy_redirect off;
}
测试
redis_sub("demo",function($channel,$message){
echo "channel ".$channel."\n";
print_r($message);
});
PUSHER_APP_KEY =
PUSHER_APP_SECRET =
PUSHER_APP_ID =
PUSHER_APP_CLUSTER =
前端需要加载JS
<script src="https://js.pusher.com/8.0.1/pusher.min.js"></script>
<script type="text/javascript">
var pusher = new Pusher("<?=get_config("PUSHER_APP_KEY")?>", {
cluster: "<?=get_config("PUSHER_APP_CLUSTER")?>",
});
var channel = pusher.subscribe("netteadmin");
channel.bind("notice", (data) => {
console.log(data);
});
</script>
发送消息
helper_v3\Pusher::sender($channel,$event,$data = []);
或使用
send_pusher($data = [],$channel='netteadmin',$event='notice');
//设置
xcookie("ss",1);
xcookie("ss",['title'=>'tt']);
//读取
pr(xcookie("ss"));
//删除
xcookie_delete("ss");
global $redis_lock;
//锁前缀
global $lock_key;
$redis_lock = [
'host'=>'',
'port'=>'',
'auth'=>'',
];
lock_call('k',functon(){
},second);
$s = gz_encode(['a'=>"test"]);
echo $s;
echo "解压后<br>";
print_r(gz_decode($s));
scss链接
<link rel="stylesheet" href="<?=scss("app.scss",true)?>" />
也可以直接调用
<style>
<?php
echo scss("
\$color: #abc;
div { color: lighten(\$color, 20%); }
");
<?php }?>
</style>
scss文件语法,参考 http://www.uinio.com/Web/Scss/
$color: red;
.navigation {
ul {
line-height: 20px;
color: blue;
a {
color: $color;
}
}
}
.footer {
.copyright {
color: silver;
}
}
https://github.com/thefunpower/vue
vue 3
$vue = new Vue;
$vue->version = 3;
vue 2
$vue = new Vue;
<el-table-column type="index" label="序号" :index="indexMethod" width="80">
</el-table-column>
$vue->search_date = [
'今天',
'昨天',
'本周',
'上周',
'上上周',
'本月',
'上月',
'上上月',
'本年'=>'今年',
'上年'=>'去年',
'上上年',
'最近一个月',
'最近两个月',
'最近三个月',
'第一季度',
'第二季度',
'第三季度',
'第四季度',
];
//限制在这个时间之前的不无法选择
$vue->start_date = '2023-11-01';
$vue->add_date();
search_date
以 key
=>value
形式存在,key
是显示的时间,value
是显示的标题
<el-date-picker v-model="where.date" value-format="yyyy-MM-dd" :picker-options="pickerOptions" size="medium" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期">
</el-date-picker>
$vue->data('text','welcome');
$vue->created(['load()']);
$vue->method('load()',"
");
$vue->mounted("a","
alert(2);
")
其中a
是key
$vue->watch("page(new_val,old_val)","
console.log('watch');
console.log(old_val);
console.log(new_val);
")
$vue->watch("where.per_page","
handler(new_val,old_val){
console.log('watch');
console.log(old_val);
console.log(new_val);
},
");
$vue->watch("where","
handler(new_val,old_val){
console.log('watch');
console.log(old_val.per_page);
console.log(new_val.per_page);
},
deep: true
");
底部加入
<?php
if($vue){
?>
<script type="text/javascript">
<?=$vue->run();?>
</script>
<?php }?>
如body
字段
在html中
<?=$vue->editor()?>
页面
<el-dialog @opened="on_open_form"
vue代码
$vue->editor_method();
$vue->method("on_open_form()","
this.weditor();
");
添加时
setTimeout(function(){
editorbody.setHtml('');
},600);
编辑时
setTimeout(function(){
editorbody.setHtml(d.body);
},600);
有时需要替换原来的图片上传按钮,以下为演示,实际使用请根据情况处理。
$vue->data('is_open_editor',false);
$vue->editor_image_upload_click = "
app.add_media('editorbody');
app.is_open_editor = true;
";
安装
yarn add --dev javascript-obfuscator
配置
$config['vue_encodejs'] = true;
$config['vue_encodejs_ignore'] = ['/plugins/config/config.php'];
每个季度开始、结束时间
vue_get_jidu_array($year)
某月的最后一天
vue_get_last_day($month = '2023-07')
<?php
global $vue;
admin_header();
$vue->upload_url = '/admin/media/upload';
?>
<?php admin_footer();?>
.evn
show_alipay=1
沙盒
https://openhome.alipay.com/develop/sandbox/account
PC支付,将跳转显示支付宝二维码
/payment/alipay/do_pay?total_fee=0.01&method=pc&order_num=test1234560021&is_json=1
composer依赖
https://pay.yansongda.cn/docs/v3/wechat/pay.html
"yansongda/pay": "~3.5.0",
"hyperf/pimple": "~2.2.0"
需要在
产品中心 签约对应的 电脑网站支付,开发设置中创建应用
网页/移动应用
https://pay.weixin.qq.com/
查看证书序列号,cert.pem 对应证书CERT
注意:查看序列号是cert.pem,生成平台证书是app.pem(对应证书KEY)
openssl x509 -in cert.pem -noout -serial
生成平台
下载 CertificateDownloader.jar
https://github.com/wechatpay-apiv3/CertificateDownloader/releases
输出平台证书
java -jar CertificateDownloader.jar -k ${apiV3key} -m ${mchId} -f ${mchPrivateKeyFilePath} -s ${mchSerialNo} -o ${outputFilePath}
必需参数有:
-f <privateKeyFilePath>
,商户API私钥文件路径 对应是的 证书KEY,也就是app.key-k <apiV3Key>
,证书解密的密钥-m <merchantId>
,商户号-o <outputFilePath>
,保存证书的路径-s <merchantSerialNo>
,商户API证书的序列号系统使用
native 是生成二维码用户使用微信扫一扫
此模式需要在 https://pay.weixin.qq.com/ 产品中心开通 Native支付
此处显示未开通时,需要点击开通,按步骤开通就可以了。开通成功后显示
此时再看产品中心 Native支付显示如下
测试如:
/payment/weixin/do_pay?total_fee=0.01&method=native&order_num=test123456&is_json=1
is_json有值时返回json数组,无值返回二维码图片
小程序使用的是 jsapi 对应产品中心是 JSAPI支付
依赖 Guzzle 7.x
GET
$client = guzzle_http();
$res = $client->request('GET', $url);
return (string)$res->getBody();
POST
$res = $client->request('POST', $url,['body'=>$body]);
return (string)$res->getBody();
POST JSON
$res = $client->request('POST', '/json.php', [
'json' => ['foo' => 'bar']
]);
发送application/x-www-form-urlencoded POST请求需要你传入form_params
$res = $client->request('POST', $url, [
'form_params' => [
'field_name' => 'abc',
'other_field' => '123',
'nested_field' => [
'nested' => 'hello'
]
]
]);
PUT
$body = file_get_contents($local_file);
$request = new \GuzzleHttp\Psr7\Request('PUT', $upload_url, $headers=[], $body);
$response = $client->send($request, ['timeout' => 30]);
if($response->getStatusCode() == 200){
return true;
}
随机IP
$opt = guzzle_http_fake_option();
$opt['referer'] = '';
$opt['form_params'] = [
'kw' => $kw,
'page'=> $page,
];
guzzle_http()->request('POST', $url, $opt);
$res = (string)$res->getBody();
命令行颜色
代码
<?php
declare (strict_types = 1);
namespace app\admin\command;
use think\console\Command;
use think\console\Input;
use think\console\input\Argument;
use think\console\input\Option;
use think\console\output\formatter\Style;
use think\console\Output;
class Hello extends Command
{
protected function configure()
{
// 指令配置
$this->setName('hello')
->setDescription('php think hello --ansi 演示命令行颜色');
}
protected function execute(Input $input, Output $output)
{
$output->info("info");
$output->error("error");
$output->warning("warning");
$output->highlight("highlight");
$output->question("question");
/*$question = $output->confirm($input, 'Continue with this action?', false);
if (!$question) {
return;
}*/
}
}
添加至php think
add_action("console",function(&$console){
if(!is_cli()){return;}
// php think hello
$console['hello'] = "app\admin\command\Hello";
});
运行
php think hello --ansi
效果
<template>
<view>
<mescroll-body top="0" :up="upOption" :down="downOption" @init="mescrollInit" @down="downCallback"
@up="upCallback">
<view class="" style="font-size: 28rpx;">
<view class="card t-center" v-for="v in list" @click="nav('/active_bm/index/detail?id='+v.id)">
<image mode="aspectFit" :src="v.image_http" style="width: 100%;"></image>
<view>{{v.title}}</view>
</view>
</view>
</mescroll-body>
<cl-toast ref="toast"></cl-toast>
<cl-message ref="message"></cl-message>
<t-login-phone @logined="logined" :visible='show_login_phone'></t-login-phone>
</view>
</template>
<script>
var _this
import MescrollMixin from "@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js";
export default {
mixins: [MescrollMixin], // 使用mixin
data() {
return {
where: {
page: 1,
per_page: 10
},
upOption: {
page: {
size: 10 // 每页数据的数量,默认10
},
noMoreSize: 5, // 配置列表的总数量要大于等于5条才显示'-- END --'的提示
empty: {
tip: '暂无相关数据'
},
textNoMore: '-- 到底了 --'
},
downOption: {
auto: false, //是否在初始化后,自动执行downCallback; 默认true
},
list: [],
is_load: false,
vip_card: {},
row: {},
visible: false,
}
},
onLoad() {
_this = this
},
onShow() {
},
methods: {
view_detail(v) {
this.nav('/active_bm/index/detail?id='+v.id)
},
swiper_change() {
let index = this.active_swiper
this.yue = this.vip_card[index].amount
},
logined() {
this.reload()
},
/*下拉刷新的回调 */
downCallback() {
this.where.page = 1
this.load()
this.mescroll.resetUpScroll();
},
upCallback(page) {
this.where.page = page.num
this.load()
},
reload() {
this.where.page = 1
this.load()
},
load() {
_this.ajax(_this.config.active_bm.index, _this.where).then(res => {
_this.is_load = true
if(res.code != 0){
_this.mescroll.endBySize(0, 0);
return;
}
if (res.current_page == 1) {
_this.list = []
}
for (let i in res.data) {
_this.list.push(res.data[i])
}
_this.mescroll.endBySize(res.total_cur, res.total);
})
}
}
};
</script>
<style lang="scss">
.fuwu {
.space-between {
margin-bottom: 10rpx;
}
}
page {
background-color: #F5F7FA;
}
.page-community {
/deep/.cl-tabs__bar-item {
color: #848484;
}
.tabBar {
/deep/.cl-tabs__bar {
background-color: rgba(0, 0, 0, 0) !important;
.cl-tabs__bar-item.is-active {
font-size: 34rpx;
}
.cl-tabs__line {
height: 6rpx;
border-radius: 4rpx;
}
}
}
.list {
.item {
/deep/.cl-button.cl-button--primary {
background-color: #F5F7FA;
width: 136rpx;
height: 58rpx;
.cl-button__text {
color: #28B5B5;
}
}
}
}
}
</style>