分类目录归档:Uncategorized

unoconv

安装

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 变量

LICENSE

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:

  1. Don’t plagiarize. The above copyright notice and this license shall be
    included in all copies or substantial portions of the Software.
  2. Don’t use the same license on more than one project. Each licensed copy
    of the Software shall be actively installed in no more than one production
    environment at a time.
  3. Don’t mess with the licensing features. Software features related to
    licensing shall not be altered or circumvented in any way, including (but
    not limited to) license validation, payment prompts, feature restrictions,
    and update eligibility.
  4. Pay up. Payment shall be made immediately upon receipt of any notice,
    prompt, reminder, or other message indicating that a payment is owed.
  5. Follow the law. All use of the Software shall not violate any applicable
    law or regulation, nor infringe the rights of any other person or entity.

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.

helper

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 Publish Subscribe

连接

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 GEO

连接

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));

RPC

服务端

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);

Ftp

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

PDF字体

免费字体

阿里妈妈方圆体   alifanyuan
阿里妈妈数黑体   alishuhei
阿里巴巴普惠体   puhuiti
阿里巴巴普惠体细 puhuitithin
google字体      notosanssc 

默认使用 notosanssc。

helper_v3\Pdf::init([
    'fontDir'=>[''],
    'fontdata'=>[
        'simhei'=> [
            'R' => 'simhei.ttf',
            'I' => 'simhei.ttf', 
        ],
    ],
    'default_font'=>'simhei'
]);

PDF

安装依赖

yum install pdftk   pdftk-java  poppler-utils perl-Image-ExifTool.noarch  ImageMagick ImageMagick-devel  ghostscript -y

生成PDF

https://mpdf.github.io/installation-setup/installation-v7-x.html
use helper_v3\Pdf;

$mpdf = Pdf::init();
$mpdf->WriteHTML('<h1>Hello world!</h1>');
$mpdf->Output();

合并PDF

$input = [
    PATH.'uploads/1.pdf',
    PATH.'uploads/2.pdf',
];
$new_file = '/完整路径/1.pdf';
echo Pdf::merger($input,$new_name);
exit;

合并PDF,包含图片

Pdf::merger_with_image($files, $output);

PDF提取图片

Pdf::pdf_to_image($file,$saveToDir)

取PDF信息

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页数

Pdf::get_pages($file);

设置PDF信息

Pdf::set_info($file,$output,$arr = []);

其中arr支持title author keywords

生成PDF table

$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();

HTML转PDF

安装依赖

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

Xls

composer require phpoffice/phpspreadsheet

当前使用 "phpoffice/phpspreadsheet": "^1.20"

生成xls

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

https://github.com/joewalnes/reconnecting-websocket
<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

https://pusher.com/
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 加密

//设置
xcookie("ss",1);
xcookie("ss",['title'=>'tt']);
//读取
pr(xcookie("ss"));
//删除
xcookie_delete("ss");  

redis锁

global $redis_lock; 
//锁前缀
global $lock_key;

$redis_lock = [
    'host'=>'',
    'port'=>'',
    'auth'=>'',
];

lock_call('k',functon(){

},second); 

gz压缩数据

$s = gz_encode(['a'=>"test"]);
echo $s; 
echo "解压后<br>";
print_r(gz_decode($s));

SCSS

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;
    }
}

vue

https://github.com/thefunpower/vue

初始化

vue 3

$vue =  new Vue;
$vue->version = 3;

vue 2

$vue =  new Vue; 

index

<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_datekey=>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>

data

$vue->data('text','welcome');

created

$vue->created(['load()']);
$vue->method('load()',"

");

mounted

$vue->mounted("a","
  alert(2);
")

其中akey

watch

$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 }?> 

wangeditor 富文本

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);   


wangeditor 5

有时需要替换原来的图片上传按钮,以下为演示,实际使用请根据情况处理。

$vue->data('is_open_editor',false);
$vue->editor_image_upload_click = "
    app.add_media('editorbody');
    app.is_open_editor = true;
"; 

压缩JS

安装

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://b.alipay.com

产品中心 签约对应的 电脑网站支付,开发设置中创建应用

https://openhome.alipay.com

网页/移动应用

微信支付

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支付

request curl

依赖 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();

command color

命令行颜色

代码

<?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

效果

uni滚动加载 mescroll-body

<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>