作者 刘朕
1 个管道 的构建 通过 耗费 0 秒

合并分支 'liuzhen' 到 'master'

订单支付调试



查看合并请求 !12
正在显示 72 个修改的文件 包含 4417 行增加0 行删除

要显示太多修改。

为保证性能只显示 72 of 72+ 个文件。

<?php
namespace addons\wechat;
use app\common\library\Menu;
use think\Addons;
/**
* 微信插件
*/
class Wechat extends Addons
{
/**
* 插件安装方法
* @return bool
*/
public function install()
{
$menu = [
[
'name' => 'wechat',
'title' => '微信管理',
'icon' => 'fa fa-wechat',
'sublist' => [
[
'name' => 'wechat/autoreply',
'title' => '自动回复管理',
'icon' => 'fa fa-reply-all',
'sublist' => [
['name' => 'wechat/autoreply/index', 'title' => '查看'],
['name' => 'wechat/autoreply/add', 'title' => '添加'],
['name' => 'wechat/autoreply/edit', 'title' => '修改'],
['name' => 'wechat/autoreply/del', 'title' => '删除'],
['name' => 'wechat/autoreply/multi', 'title' => '批量更新'],
]
],
[
'name' => 'wechat/config',
'title' => '配置管理',
'icon' => 'fa fa-cog',
'sublist' => [
['name' => 'wechat/config/index', 'title' => '查看'],
['name' => 'wechat/config/add', 'title' => '添加'],
['name' => 'wechat/config/edit', 'title' => '修改'],
['name' => 'wechat/config/del', 'title' => '删除'],
['name' => 'wechat/config/multi', 'title' => '批量更新'],
]
],
[
'name' => 'wechat/menu',
'title' => '菜单管理',
'icon' => 'fa fa-list',
'sublist' => [
['name' => 'wechat/menu/index', 'title' => '查看'],
['name' => 'wechat/menu/add', 'title' => '添加'],
['name' => 'wechat/menu/edit', 'title' => '修改'],
['name' => 'wechat/menu/del', 'title' => '删除'],
['name' => 'wechat/menu/sync', 'title' => '同步'],
['name' => 'wechat/menu/multi', 'title' => '批量更新'],
]
],
[
'name' => 'wechat/response',
'title' => '资源管理',
'icon' => 'fa fa-list-alt',
'sublist' => [
['name' => 'wechat/response/index', 'title' => '查看'],
['name' => 'wechat/response/add', 'title' => '添加'],
['name' => 'wechat/response/edit', 'title' => '修改'],
['name' => 'wechat/response/del', 'title' => '删除'],
['name' => 'wechat/response/select', 'title' => '选择'],
['name' => 'wechat/response/multi', 'title' => '批量更新'],
]
]
]
]
];
Menu::create($menu);
return true;
}
/**
* 插件卸载方法
* @return bool
*/
public function uninstall()
{
Menu::delete('wechat');
return true;
}
/**
* 插件启用方法
*/
public function enable()
{
Menu::enable('wechat');
}
/**
* 插件禁用方法
*/
public function disable()
{
Menu::disable('wechat');
}
}
\ No newline at end of file
... ...
<?php
namespace app\admin\controller\wechat;
use app\common\controller\Backend;
use app\admin\model\WechatResponse;
/**
* 微信自动回复管理
*
* @icon fa fa-circle-o
*/
class Autoreply extends Backend
{
protected $model = null;
protected $noNeedRight = ['check_text_unique'];
public function _initialize()
{
parent::_initialize();
$this->model = model('WechatAutoreply');
}
/**
* 编辑
*/
public function edit($ids = null)
{
$row = $this->model->get(['id' => $ids]);
if (!$row) {
$this->error(__('No Results were found'));
}
if ($this->request->isPost()) {
$params = $this->request->post("row/a");
if ($params) {
$row->save($params);
$this->success();
}
$this->error();
}
$response = WechatResponse::get(['eventkey' => $row['eventkey']]);
$this->view->assign("response", $response);
$this->view->assign("row", $row);
return $this->view->fetch();
}
/**
* 判断文本是否唯一
* @internal
*/
public function check_text_unique()
{
$row = $this->request->post("row/a");
$except = $this->request->post("except");
$text = isset($row['text']) ? $row['text'] : '';
if ($this->model->where('text', $text)->where(function ($query) use ($except) {
if ($except) {
$query->where('text', '<>', $except);
}
})->count() == 0) {
$this->success();
} else {
$this->error(__('Text already exists'));
}
}
}
... ...
<?php
namespace app\admin\controller\wechat;
use app\common\controller\Backend;
use think\Controller;
use think\Request;
/**
* 微信配置管理
*
* @icon fa fa-circle-o
*/
class Config extends Backend
{
protected $model = null;
public function _initialize()
{
parent::_initialize();
$this->model = model('WechatConfig');
}
/**
* 添加
*/
public function add()
{
if ($this->request->isPost()) {
$params = $this->request->post("row/a");
if ($params) {
foreach ($params as $k => &$v) {
$v = is_array($v) ? implode(',', $v) : $v;
}
if ($params['mode'] == 'json') {
//JSON字段
$fieldarr = $valuearr = [];
$field = $this->request->post('field/a');
$value = $this->request->post('value/a');
foreach ($field as $k => $v) {
if ($v != '') {
$fieldarr[] = $field[$k];
$valuearr[] = $value[$k];
}
}
$params['value'] = json_encode(array_combine($fieldarr, $valuearr), JSON_UNESCAPED_UNICODE);
}
unset($params['mode']);
try {
//是否采用模型验证
if ($this->modelValidate) {
$name = basename(str_replace('\\', '/', get_class($this->model)));
$validate = is_bool($this->modelValidate) ? ($this->modelSceneValidate ? $name . '.add' : true) : $this->modelValidate;
$this->model->validate($validate);
}
$result = $this->model->save($params);
if ($result !== false) {
$this->success();
} else {
$this->error($this->model->getError());
}
} catch (\think\exception\PDOException $e) {
$this->error($e->getMessage());
}
}
$this->error(__('Parameter %s can not be empty', ''));
}
return $this->view->fetch();
}
/**
* 编辑
*/
public function edit($ids = NULL)
{
$row = $this->model->get($ids);
if (!$row)
$this->error(__('No Results were found'));
if ($this->request->isPost()) {
$params = $this->request->post("row/a");
if ($params) {
foreach ($params as $k => &$v) {
$v = is_array($v) ? implode(',', $v) : $v;
}
if ($params['mode'] == 'json') {
//JSON字段
$fieldarr = $valuearr = [];
$field = $this->request->post('field/a');
$value = $this->request->post('value/a');
foreach ($field as $k => $v) {
if ($v != '') {
$fieldarr[] = $field[$k];
$valuearr[] = $value[$k];
}
}
$params['value'] = json_encode(array_combine($fieldarr, $valuearr), JSON_UNESCAPED_UNICODE);
}
unset($params['mode']);
try {
//是否采用模型验证
if ($this->modelValidate) {
$name = basename(str_replace('\\', '/', get_class($this->model)));
$validate = is_bool($this->modelValidate) ? ($this->modelSceneValidate ? $name . '.add' : true) : $this->modelValidate;
$row->validate($validate);
}
$result = $row->save($params);
if ($result !== false) {
$this->success();
} else {
$this->error($row->getError());
}
} catch (\think\exception\PDOException $e) {
$this->error($e->getMessage());
}
}
$this->error(__('Parameter %s can not be empty', ''));
}
$this->view->assign("row", $row);
$this->view->assign("value", (array)json_decode($row->value, true));
return $this->view->fetch();
}
}
... ...
<?php
namespace app\admin\controller\wechat;
use app\common\controller\Backend;
use app\admin\model\WechatResponse;
use EasyWeChat\Foundation\Application;
use think\Exception;
/**
* 菜单管理
*
* @icon fa fa-list-alt
*/
class Menu extends Backend
{
protected $wechatcfg = null;
public function _initialize()
{
parent::_initialize();
$this->wechatcfg = \app\admin\model\WechatConfig::get(['name' => 'menu']);
}
/**
* 查看
*/
public function index()
{
$responselist = array();
$all = WechatResponse::all();
foreach ($all as $k => $v) {
$responselist[$v['eventkey']] = $v['title'];
}
$this->view->assign('responselist', $responselist);
$this->view->assign('menu', (array)json_decode($this->wechatcfg->value, true));
return $this->view->fetch();
}
/**
* 修改
*/
public function edit($ids = null)
{
$menu = $this->request->post("menu");
$menu = (array)json_decode($menu, true);
foreach ($menu as $index => &$item) {
if (isset($item['sub_button'])) {
foreach ($item['sub_button'] as &$subitem) {
if ($subitem['type'] == 'view') {
$allowFields = ['type', 'name', 'url'];
$subitem = ['type' => $subitem['type'], 'name' => $subitem['name'], 'url' => $subitem['url']];
} else {
if ($subitem['type'] == 'miniprogram') {
$allowFields = ['type', 'name', 'url', 'appid', 'pagepath'];
$subitem = ['type' => $subitem['type'], 'name' => $subitem['name'], 'url' => $subitem['url'], 'appid' => $subitem['appid'], 'pagepath' => $subitem['pagepath']];
} else {
$allowFields = ['type', 'name', 'key'];
$subitem = ['type' => $subitem['type'], 'name' => $subitem['name'], 'key' => $subitem['key']];
}
}
$subitem = array_intersect_key($subitem, array_flip($allowFields));
}
} else {
if ($item['type'] == 'view') {
$allowFields = ['type', 'name', 'url'];
} else {
if ($item['type'] == 'miniprogram') {
$allowFields = ['type', 'name', 'url', 'appid', 'pagepath'];
} else {
$allowFields = ['type', 'name', 'key'];
}
}
$item = array_intersect_key($item, array_flip($allowFields));
}
}
$this->wechatcfg->value = json_encode($menu, JSON_UNESCAPED_UNICODE);
$this->wechatcfg->save();
$this->success();
}
/**
* 同步
*/
public function sync($ids = null)
{
$app = new Application(get_addon_config('wechat'));
try {
$hasError = false;
$menu = json_decode($this->wechatcfg->value, true);
foreach ($menu as $k => $v) {
if (isset($v['sub_button'])) {
foreach ($v['sub_button'] as $m => $n) {
if ($n['type'] == 'click' && isset($n['key']) && !$n['key']) {
$hasError = true;
break 2;
}
}
} else {
if ($v['type'] == 'click' && isset($v['key']) && !$v['key']) {
$hasError = true;
break;
}
}
}
if (!$hasError) {
try {
$ret = $app->menu->add($menu);
} catch (\EasyWeChat\Core\Exceptions\HttpException $e) {
$this->error($e->getMessage());
}
if ($ret->errcode == 0) {
$this->success();
} else {
$this->error($ret->errmsg);
}
} else {
$this->error(__('Invalid parameters'));
}
} catch (Exception $e) {
$this->error($e->getMessage());
}
}
}
... ...
<?php
namespace app\admin\controller\wechat;
use app\common\controller\Backend;
use addons\wechat\library\Wechat;
/**
* 资源管理
*
* @icon fa fa-list-alt
*/
class Response extends Backend
{
protected $model = null;
protected $searchFields = 'id,title';
public function _initialize()
{
parent::_initialize();
$this->model = model('WechatResponse');
}
/**
* 选择素材
*/
public function select()
{
return $this->view->fetch();
}
/**
* 添加
*/
public function add()
{
if ($this->request->isPost()) {
$params = $this->request->post("row/a");
$params['eventkey'] = isset($params['eventkey']) && $params['eventkey'] ? $params['eventkey'] : uniqid();
$params['content'] = json_encode($params['content']);
$params['createtime'] = time();
if ($params) {
$this->model->save($params);
$this->success();
$this->content = $params;
}
$this->error();
}
$appConfig = Wechat::appConfig();
$this->view->applist = $appConfig;
return $this->view->fetch();
}
/**
* 编辑
*/
public function edit($ids = NULL)
{
$row = $this->model->get($ids);
if (!$row)
$this->error(__('No Results were found'));
if ($this->request->isPost()) {
$params = $this->request->post("row/a");
$params['eventkey'] = isset($params['eventkey']) && $params['eventkey'] ? $params['eventkey'] : uniqid();
$params['content'] = json_encode($params['content']);
if ($params) {
$row->save($params);
$this->success();
}
$this->error();
}
$this->view->assign("row", $row);
$appConfig = Wechat::appConfig();
$this->view->applist = $appConfig;
return $this->view->fetch();
}
}
... ...
<?php
return [
'Text' => '文本',
'Event key' => '响应标识',
'Remark' => '备注',
'Text already exists' => '文本已经存在',
];
... ...
<?php
return [
'name' => '配置名称',
'value' => '配置值',
'Json key' => '键',
'Json value' => '值',
'Json editor' => 'JSON编辑器',
'Insert link' => '插入链接',
'createtime' => '创建时间',
'updatetime' => '更新时间'
];
... ...
<?php
return [
'Resource title' => '资源标题',
'Event key' => '事件标识',
'Event' => '事件标识',
'Text' => '文本',
'App' => '应用',
];
... ...
<?php
namespace app\admin\model;
use think\Model;
class WechatAutoreply extends Model
{
// 自动写入时间戳字段
protected $autoWriteTimestamp = 'int';
// 定义时间戳字段名
protected $createTime = 'createtime';
protected $updateTime = 'updatetime';
}
... ...
<?php
namespace app\admin\model;
use fast\Random;
use think\Model;
class WechatCaptcha extends Model
{
// 表名
protected $name = 'wechat_captcha';
// 自动写入时间戳字段
protected $autoWriteTimestamp = 'int';
// 定义时间戳字段名
protected $createTime = 'createtime';
protected $updateTime = '';
// 追加属性
protected $append = [
];
/**
* 发送验证码
* @param $openid string 用户OpenID
* @param $event string 事件
* @param $ip string IP地址
* @return string
*/
public static function send($openid, $event, $ip)
{
$captcha = self::where(['openid' => $openid, 'event' => $event])->whereTime('createtime', '-2 minutes')->find();
if ($captcha) {
return "验证码发送速度过快,请稍后重试";
}
$code = Random::alnum(4);
$data = [
'event' => $event,
'openid' => $openid,
'code' => $code,
'ip' => $ip,
];
self::create($data);
return "你的验证码是:{$code},2分钟内输入有效";
}
/**
* 检测验证码
* @param $code string 验证码
* @param $event string 事件
* @param $ip string IP
* @return bool
*/
public static function check($code, $event, $ip = null)
{
$ip = is_null($ip) ? request()->ip() : $ip;
$captcha = self::where(['ip' => $ip, 'event' => $event])->whereTime('createtime', '-2 minutes')->find();
if ($captcha && $captcha->code == $code && $captcha->times < 10) {
$captcha->setInc("times");
return true;
}
//验证大于10次或超时
if ($captcha && ($captcha->times >= 10 || time() - $captcha->createtime > 120)) {
$captcha->delete();
}
return false;
}
}
... ...
<?php
namespace app\admin\model;
use think\Model;
class WechatConfig extends Model
{
// 表名,不含前缀
public $name = 'wechat_config';
// 自动写入时间戳字段
protected $autoWriteTimestamp = 'int';
// 定义时间戳字段名
protected $createTime = 'createtime';
protected $updateTime = 'updatetime';
// 追加属性
protected $append = [
];
/**
* 读取指定配置名称的值
* @param string $name
* @return string
*/
public static function value($name)
{
$item = self::get(['name' => $name]);
return $item ? $item->value : '';
}
}
... ...
<?php
namespace app\admin\model;
use think\Model;
class WechatContext extends Model
{
// 自动写入时间戳字段
protected $autoWriteTimestamp = 'int';
// 定义时间戳字段名
protected $createTime = 'createtime';
protected $updateTime = 'updatetime';
}
... ...
<?php
namespace app\admin\model;
use think\Model;
class WechatResponse extends Model
{
// 自动写入时间戳字段
protected $autoWriteTimestamp = 'int';
// 定义时间戳字段名
protected $createTime = 'createtime';
protected $updateTime = 'updatetime';
}
... ...
<link href="__CDN__/assets/addons/wechat/css/menu.css?v={$site.version}" rel="stylesheet">
<style>
.clickbox {margin:0;text-align: left;}
.create-click {
margin-left:0;
}
</style>
<form id="add-form" class="form-horizontal form-ajax" role="form" data-toggle="validator" method="POST" action="">
<div class="form-group">
<label for="c-title" class="control-label col-xs-12 col-sm-2">{:__('Title')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" name="row[title]" value="" id="c-title" class="form-control" data-rule="required" />
</div>
</div>
<div class="form-group">
<label for="c-text" class="control-label col-xs-12 col-sm-2">{:__('Text')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" name="row[text]" value="" id="c-text" class="form-control" data-rule="required; remote(wechat/autoreply/check_text_unique)" />
</div>
</div>
<div class="form-group">
<label for="c-eventkey" class="control-label col-xs-12 col-sm-2">{:__('Event key')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="hidden" name="row[eventkey]" id="c-eventkey" class="form-control" value="" data-rule="required" readonly />
<div class="clickbox">
<span class="create-click"><a href="{:url('wechat.response/select')}" id="select-resources"><i class="weixin-icon big-add-gray"></i><strong>选择现有资源</strong></a></span>
<span class="create-click"><a href="{:url('wechat.response/add')}" id="add-resources"><i class="weixin-icon big-add-gray"></i><strong>添加新资源</strong></a></span>
</div>
</div>
</div>
<div class="form-group">
<label for="c-remark" class="control-label col-xs-12 col-sm-2">{:__('Remark')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" name="row[remark]" value="" id="c-remark" class="form-control" />
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Status')}:</label>
<div class="col-xs-12 col-sm-8">
{:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')])}
</div>
</div>
<div class="form-group hide layer-footer">
<label class="control-label col-xs-12 col-sm-2"></label>
<div class="col-xs-12 col-sm-8">
<button type="submit" class="btn btn-success btn-embossed disabled">{:__('OK')}</button>
<button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
</div>
</div>
</form>
... ...
<link href="__CDN__/assets/addons/wechat/css/menu.css?v={$site.version}" rel="stylesheet">
<style>
.clickbox {margin:0;text-align: left;}
.create-click {
margin-left:0;
}
</style>
<form id="edit-form" class="form-horizontal form-ajax" role="form" data-toggle="validator" method="POST" action="">
<div class="form-group">
<label for="c-title" class="control-label col-xs-12 col-sm-2">{:__('Title')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" name="row[title]" value="{$row.title}" id="c-title" class="form-control" data-rule="required" />
</div>
</div>
<div class="form-group">
<label for="c-text" class="control-label col-xs-12 col-sm-2">{:__('Text')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" name="row[text]" value="{$row.text}" id="c-text" class="form-control" data-rule="required; remote(wechat/autoreply/check_text_unique, except={$row.text})" />
</div>
</div>
<div class="form-group">
<label for="c-eventkey" class="control-label col-xs-12 col-sm-2">{:__('Content')}:</label>
<div class="col-xs-12 col-sm-8">
<div class="clickbox">
<input type="hidden" name="row[eventkey]" id="c-eventkey" class="form-control" value="{$row.eventkey}" data-rule="required" readonly />
<span class="create-click"><a href="wechat/response/select" id="select-resources"><i class="weixin-icon big-add-gray"></i><strong>选择现有资源</strong></a><div class="keytitle">资源名:{:$response['title']}</div></span>
<span class="create-click"><a href="wechat/response/add" id="add-resources"><i class="weixin-icon big-add-gray"></i><strong>添加新资源</strong></a></span>
</div>
</div>
</div>
<div class="form-group">
<label for="c-remark" class="control-label col-xs-12 col-sm-2">{:__('Remark')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" name="row[remark]" value="{$row.remark}" id="c-remark" class="form-control" />
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Status')}:</label>
<div class="col-xs-12 col-sm-8">
{:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')], $row['status'])}
</div>
</div>
<div class="form-group hide layer-footer">
<label class="control-label col-xs-12 col-sm-2"></label>
<div class="col-xs-12 col-sm-8">
<button type="submit" class="btn btn-success btn-embossed disabled">{:__('OK')}</button>
<button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
</div>
</div>
</form>
... ...
<div class="panel panel-default panel-intro">
{:build_heading()}
<div class="panel-body">
<div id="myTabContent" class="tab-content">
<div class="tab-pane fade active in" id="one">
<div class="widget-body no-padding">
<div id="toolbar" class="toolbar">
{:build_toolbar()}
<div class="dropdown btn-group {:$auth->check('wechat/autoreply/multi')?'':'hide'}">
<a class="btn btn-primary btn-more dropdown-toggle btn-disabled disabled" data-toggle="dropdown"><i class="fa fa-cog"></i> <?= __('More') ?></a>
<ul class="dropdown-menu text-left" role="menu">
<li><a class="btn btn-link btn-multi btn-disabled disabled" href="javascript:;" data-params="status=normal"><i class="fa fa-eye"></i> {:__('Set to normal')}</a></li>
<li><a class="btn btn-link btn-multi btn-disabled disabled" href="javascript:;" data-params="status=hidden"><i class="fa fa-eye-slash"></i> {:__('Set to hidden')}</a></li>
</ul>
</div>
</div>
<table id="table" class="table table-striped table-bordered table-hover"
data-operate-edit="{:$auth->check('wechat/autoreply/edit')}"
data-operate-del="{:$auth->check('wechat/autoreply/del')}"
width="100%">
</table>
</div>
</div>
</div>
</div>
</div>
... ...
<form id="add-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
<input type="hidden" name="row[mode]" value="textarea" />
<div class="form-group">
<label for="c-name" class="control-label col-xs-12 col-sm-2">{:__('Name')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-name" data-rule="required" class="form-control" name="row[name]" type="text" value="">
</div>
</div>
<div class="form-group">
<label for="c-title" class="control-label col-xs-12 col-sm-2">{:__('Title')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-title" data-rule="required" class="form-control" name="row[title]" type="text" value="">
</div>
</div>
<div class="form-group">
<label for="c-value" class="control-label col-xs-12 col-sm-2">{:__('Value')}:</label>
<div class="col-xs-12 col-sm-8">
<p>
<a href="javascript:;" class="btn btn-info btn-jsoneditor"><i class="fa fa-pencil"></i> {:__('Json editor')}</a>
<a href="javascript:;" class="btn btn-primary btn-insertlink"><i class="fa fa-link"></i> {:__('Insert link')}</a>
</p>
<textarea id="c-value" class="form-control " rows="15" name="row[value]"></textarea>
<dl class="fieldlist hide" rel="1">
<dd>
<ins>{:__('Json key')}</ins>
<ins>{:__('Json value')}</ins>
</dd>
<dd>
<input type="text" name="field[0]" class="form-control" id="field-0" value="" size="10" required />
<input type="text" name="value[0]" class="form-control" id="value-0" value="" size="40" required />
<span class="btn btn-sm btn-danger btn-remove"><i class="fa fa-times"></i></span>
<span class="btn btn-sm btn-primary btn-dragsort"><i class="fa fa-arrows"></i></span>
</dd>
<dd><a href="javascript:;" class="append btn btn-sm btn-success"><i class="fa fa-plus"></i> {:__('Append')}</a></dd>
</dl>
</div>
</div>
<div class="form-group hide layer-footer">
<label class="control-label col-xs-12 col-sm-2"></label>
<div class="col-xs-12 col-sm-8">
<button type="submit" class="btn btn-success btn-embossed disabled">{:__('OK')}</button>
<button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
</div>
</div>
</form>
... ...
<form id="edit-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
<input type="hidden" name="row[mode]" value="textarea" />
<div class="form-group">
<label for="c-name" class="control-label col-xs-12 col-sm-2">{:__('Name')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-name" data-rule="required" class="form-control" name="row[name]" type="text" value="{$row.name}">
</div>
</div>
<div class="form-group">
<label for="c-title" class="control-label col-xs-12 col-sm-2">{:__('Title')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-title" data-rule="required" class="form-control" name="row[title]" type="text" value="{$row.title}">
</div>
</div>
<div class="form-group">
<label for="c-value" class="control-label col-xs-12 col-sm-2">{:__('Value')}:</label>
<div class="col-xs-12 col-sm-8">
<textarea id="c-value" class="form-control " rows="15" name="row[value]">{$row.value}</textarea>
</div>
</div>
<div class="form-group hide layer-footer">
<label class="control-label col-xs-12 col-sm-2"></label>
<div class="col-xs-12 col-sm-8">
<button type="submit" class="btn btn-success btn-embossed disabled">{:__('OK')}</button>
<button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
</div>
</div>
</form>
... ...
<div class="panel panel-default panel-intro">
{:build_heading()}
<div class="panel-body">
<div id="myTabContent" class="tab-content">
<div class="tab-pane fade active in" id="one">
<div class="widget-body no-padding">
<div id="toolbar" class="toolbar">
{:build_toolbar()}
<div class="dropdown btn-group {:$auth->check('wechat/config/multi')?'':'hide'}">
<a class="btn btn-primary btn-more dropdown-toggle btn-disabled disabled" data-toggle="dropdown"><i class="fa fa-cog"></i> {:__('More')}</a>
<ul class="dropdown-menu text-left" role="menu">
<li><a class="btn btn-link btn-multi btn-disabled disabled" href="javascript:;" data-params="status=normal"><i class="fa fa-eye"></i> {:__('Set to normal')}</a></li>
<li><a class="btn btn-link btn-multi btn-disabled disabled" href="javascript:;" data-params="status=hidden"><i class="fa fa-eye-slash"></i> {:__('Set to hidden')}</a></li>
</ul>
</div>
</div>
<table id="table" class="table table-striped table-bordered table-hover"
data-operate-edit="{:$auth->check('wechat/config/edit')}"
data-operate-del="{:$auth->check('wechat/config/del')}"
width="100%">
</table>
</div>
</div>
</div>
</div>
</div>
... ...
<link href="__CDN__/assets/addons/wechat/css/menu.css?v={$site['version']}" rel="stylesheet"/>
<style>
.form-item dl dt {
width: 120px;
}
.form-item dl dd {
margin-left: 120px;
}
.form-item dl dd input {
font-size: 12px;
}
</style>
<div class="panel panel-default panel-intro">
{:build_heading()}
<div class="panel-body">
<div id="myTabContent" class="tab-content">
<div class="tab-pane fade active in" id="one">
<div class="widget-body no-padding">
<div class="weixin-menu-setting clearfix">
<div class="mobile-menu-preview">
<div class="mobile-head-title">{$site.name}</div>
<ul class="menu-list" id="menu-list">
</ul>
</div>
<div class="weixin-body">
<div class="weixin-content" style="display:none">
<div class="item-info">
<form id="form-item" class="form-item" data-value="">
<div class="item-head">
<h4 id="current-item-name">添加子菜单</h4>
<div class="item-delete"><a href="javascript:;" id="item_delete">删除菜单</a></div>
</div>
<div style="margin-top: 20px;" id="item-body">
</div>
</form>
</div>
</div>
<div class="no-weixin-content">
点击左侧菜单进行编辑操作
</div>
</div>
</div>
<div class="text-center" style="position:relative;">
<div class="text-danger" style="width:317px;position:absolute;left:0;top:0;">
<i class="fa fa-lightbulb-o"></i> <small>可直接拖动菜单排序</small>
</div>
<div style="padding-left:337px;"><a href="javascript:;" id="menuSyn" class="btn btn-danger">保存并发布</a></div>
</div>
</div>
</div>
</div>
</div>
</div>
<script type="text/html" id="menutpl">
<%for(var i=0; i< menu.length; i++){%>
<%var first=menu[i];%>
<li id="menu-<%=i%>" class="menu-item" data-type="<%=first['type']%>" data-key="<%=first['key']%>" data-name="<%=first['name']%>" data-url="<%=first['url']%>" data-appid="<%=first['appid']%>" data-pagepath="<%=first['pagepath']%>">
<a href="javascript:;" class="menu-link">
<i class="icon-menu-dot"></i> <i class="weixin-icon sort-gray"></i> <span class="title"><%=first['name']%></span>
</a>
<div class="sub-menu-box" style="display:none;">
<ul class="sub-menu-list">
<%if(typeof first['sub_button']!='undefined'){%>
<%for(var j=0; j< first['sub_button'].length; j++){%>
<%var second=first['sub_button'][j];%>
<li id="sub-menu-<%=j%>" class="sub-menu-item" data-type="<%=second['type']%>" data-key="<%=second['key']%>" data-name="<%=second['name']%>" data-url="<%=second['url']%>" data-appid="<%=second['appid']%>" data-pagepath="<%=second['pagepath']%>"><a href="javascript:;"> <i class="weixin-icon sort-gray"></i><span class="sub-title"><%=second['name']%></span></a></li>
<%}%>
<%}%>
<li class="add-sub-item <%if(typeof first['sub_button']!='undefined' && first['sub_button'].length>=5){%>hidden<%}%>"><a href="javascript:;" title="添加子菜单"><span class=" "><i class="weixin-icon add-gray"></i></span></a></li>
</ul>
<i class="arrow arrow-out"></i> <i class="arrow arrow-in"></i>
</div>
</li>
<%}%>
<li class="add-item extra" id="add-item">
<a href="javascript:;" class="menu-link" title="添加菜单"><i class="weixin-icon add-gray"></i></a>
</li>
</script>
<script type="text/html" id="itemtpl">
<dl>
<dt id="current-item-option"><span class="is-sub-item <%=first?'hidden':''%>"></span>菜单标题:</dt>
<dd>
<div class="input-box"><input id="item_title" name="name" type="text" value="<%=name%>"></div>
</dd>
</dl>
<%if(!hasChild){%>
<dl class="is-item">
<dt id="current-item-type"><span class="is-sub-item <%=first?'hidden':''%>"></span>菜单内容:</dt>
<dd>
<%for(var i=0;i< typeList.length; i++){%>
<input id="type<%=i%>" type="radio" name="type" value="<%=typeList[i]['name']%>" <%=typeList[i]['name']==type?'checked':''%> /><label for="type<%=i%>"><span class="lbl_content"><%=typeList[i]['title']%></span></label>
<%}%>
</dd>
</dl>
<div id="menu-content" class="is-item">
<%if(type=='view'){%>
<div class="viewbox is-view">
<p class="menu-content-tips">点击该<span class="is-sub-item <%=first?'hidden':''%>"></span>菜单会跳到以下链接</p>
<dl>
<dt>页面地址:</dt>
<dd>
<div class="input-box"><input type="text" name="url" value="<%=url%>"></div>
</dd>
</dl>
</div>
<%}%>
<%if(type!='view'&&type!='miniprogram'){%>
<div class="clickbox is-click">
<input type="hidden" name="key" id="key" value="<%=key%>"/>
<span class="create-click">
<%if(keytitle){%>
<div class="keytitle">资源名:<%=keytitle%></div>
<%}%>
<a href="{:url('wechat.response/select')}" id="select-resources"><i class="weixin-icon big-add-gray"></i><strong>选择现有资源</strong></a>
</span>
<span class="create-click">
<a href="{:url('wechat.response/add')}" id="add-resources"><i class="weixin-icon big-add-gray"></i><strong>添加新资源</strong></a>
</span>
</div>
<%}%>
<%if(type=='miniprogram'){%>
<div class="viewbox is-miniprogram">
<p class="menu-content-tips">点击该<span class="is-sub-item <%=first?'':'hidden'%>"></span>菜单会跳到以下小程序</p>
<dl>
<dt>小程序ID</dt>
<dd>
<div class="input-box"><input type="text" id="appid" name="appid" placeholder="在小程序后台获取" value="<%=appid%>"></div>
</dd>
</dl>
<dl>
<dt>小程序页面路径:</dt>
<dd>
<div class="input-box"><input type="text" id="pagepath" name="pagepath" placeholder="小程序页面路径" value="<%=pagepath%>"></div>
</dd>
</dl>
<dl>
<dt>页面地址:</dt>
<dd>
<div class="input-box"><input type="text" name="url" placeholder="页面地址,当不支持小程序时会跳转此页面" value="<%=url%>"></div>
</dd>
</dl>
</div>
<%}%>
</div>
<%}%>
</script>
<!--@formatter:off-->
<script type="text/javascript">
var menu = {:json_encode($menu, JSON_UNESCAPED_UNICODE)};
var responselist = {:json_encode($responselist, JSON_UNESCAPED_UNICODE)};
</script>
<!--@formatter:on-->
... ...
<form id="add-form" class="form-horizontal form-ajax" role="form" data-toggle="validator" method="POST" action="">
<div class="form-group">
<label for="module" class="control-label col-xs-12 col-sm-2">{:__('Resource title')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" class="form-control" id="title" name="row[title]" value="" data-rule="required" />
</div>
</div>
<div class="form-group">
<label for="remark" class="control-label col-xs-12 col-sm-2">{:__('Memo')}:</label>
<div class="col-xs-12 col-sm-8">
<textarea class="form-control" id="remark" name="row[remark]"></textarea>
</div>
</div>
<div class="form-group">
<label for="content" class="control-label col-xs-12 col-sm-2">{:__('Type')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="radio" name="row[type]" value="text" id="type-text" checked />
<label for="type-text">{:__('Text')}</label>
<input type="radio" name="row[type]" value="app" id="type-app" />
<label for="type-app">{:__('App')}</label>
</div>
</div>
<div id="expand">
</div>
<div class="form-group">
<label for="status" class="control-label col-xs-12 col-sm-2">{:__('Status')}:</label>
<div class="col-xs-12 col-sm-8">
{:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')])}
</div>
</div>
<div class="form-group {:input('get.callback')?'':'hidden layer-footer'}">
<div class="col-xs-2"></div>
<div class="col-xs-12 col-sm-8">
<button type="submit" class="btn btn-success btn-embossed disabled">{:__('OK')}</button>
<button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
</div>
</div>
<select name="applist" disabled="true" class="hidden">
{foreach $applist as $k => $v}
<option value="{$k}">{$v.name}</option>
{/foreach}
</select>
</form>
<script>
var apps = {:json_encode($applist)};
var datas = {};
</script>
\ No newline at end of file
... ...
<form id="edit-form" class="form-horizontal form-ajax" role="form" data-toggle="validator" method="POST" action="">
<div class="form-group">
<label for="module" class="control-label col-xs-12 col-sm-2">{:__('Resource title')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" class="form-control" id="title" name="row[title]" value="{$row.title}" data-rule="required" />
</div>
</div>
<div class="form-group">
<label for="controller" class="control-label col-xs-12 col-sm-2">{:__('Event key')}:</label>
<div class="col-xs-12 col-sm-8">
<input type='text' class="form-control" id="eventkey" name="row[eventkey]" value="{$row.eventkey}" data-rule="required" readonly />
</div>
</div>
<div class="form-group">
<label for="remark" class="control-label col-xs-12 col-sm-2">{:__('Memo')}:</label>
<div class="col-xs-12 col-sm-8">
<textarea class="form-control" id="remark" name="row[remark]">{$row.remark}</textarea>
</div>
</div>
<div class="form-group">
<label for="content" class="control-label col-xs-12 col-sm-2">{:__('Type')}:</label>
<div class="col-xs-12 col-sm-8">
{:build_radios('row[type]', ['text' => __('Text'), 'app' => __('App')], $row['type'])}
</div>
</div>
<div id="expand">
</div>
<div class="form-group">
<div class="col-xs-2"></div>
<div class="col-xs-12 col-sm-8">
{:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')], $row['status'])}
</div>
</div>
<div class="form-group hidden layer-footer">
<div class="col-xs-2"></div>
<div class="col-xs-12 col-sm-8">
<button type="submit" class="btn btn-success btn-embossed disabled">{:__('OK')}</button>
<button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
</div>
</div>
<select name="applist" disabled="true" class="hidden">
{foreach $applist as $k => $v}
<option value="{$k}">{$v.name}</option>
{/foreach}
</select>
</form>
<script>
var apps = {:json_encode($applist)};
var datas = {$row.content};
</script>
\ No newline at end of file
... ...
<div class="panel panel-default panel-intro">
{:build_heading()}
<div class="panel-body">
<div id="myTabContent" class="tab-content">
<div class="tab-pane fade active in" id="one">
<div class="widget-body no-padding">
<div id="toolbar" class="toolbar">
{:build_toolbar();}
</div>
<table id="table" class="table table-bordered table-hover"
data-operate-edit="{:$auth->check('wechat/response/edit')}"
data-operate-del="{:$auth->check('wechat/response/del')}"
width="100%">
</table>
</div>
</div>
</div>
</div>
</div>
... ...
<div class="panel panel-default panel-intro">
{:build_heading()}
<div class="panel-body">
<div id="myTabContent" class="tab-content">
<div class="tab-pane fade active in" id="one">
<div class="widget-body no-padding">
<table id="table" class="table table-bordered table-hover" width="100%">
</table>
</div>
</div>
</div>
</div>
</div>
... ...
.weixin-menu-setting {
margin: 0;
margin-bottom: 10px;
width: 100%;
}
.mobile-head-title {
color: #fff;
text-align: center;
padding-top: 33px;
font-size: 15px;
width: auto;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
word-wrap: normal;
margin: 0 40px 0 70px;
}
.weixin-body {
padding: 0;
margin: 0;
margin-left: 337px;
}
.weixin-content, .no-weixin-content {
background-color: #f4f5f9;
border: 1px solid #e7e7eb;
padding: 15px;
}
.no-weixin-content {
border: #fff;
background-color: #fff;
vertical-align: middle;
padding-top: 200px;
text-align: center;
}
@media (max-width: 720px) {
.weixin-body {
margin-left: 0;
margin-top: 560px;
}
}
.weixin-menu-title {
border-bottom: 1px solid #e7e7eb;
font-size: 16px;
padding: 0 20px;
line-height: 55px;
margin-bottom: 20px;
}
.mobile-menu-preview {
display: block;
float: left;
position: relative;
width: 317px;
height: 550px;
background: transparent url(../images/mobile_header_bg.png) no-repeat 0 0;
background-position: 0 0;
border: 1px solid #e7e7eb;
}
.mobile-menu-preview .menu-list {
position: absolute;
height: 50px;
bottom: 0;
left: 0;
right: 0;
border-top: 1px solid #e7e7eb;
background: transparent url(../images/mobile_footer_bg.png) no-repeat 0 0;
background-position: 0 0;
background-repeat: no-repeat;
padding-left: 43px;
margin: 0;
}
.menu-list .menu-item, .menu-list .add-item {
line-height: 50px;
position: relative;
float: left;
text-align: center;
width: 33.33%;
list-style: none;
}
.ui-sortable-placeholder {
background-color: #fff;
}
.menu-item a, .add-item a {
display: block;
width: auto;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
word-wrap: normal;
color: #616161;
text-decoration: none;
}
.menu-item.current a.menu-link {
border: 1px solid #44b549;
line-height: 48px;
background-color: #fff;
color: #44b549;
}
.menu-item .icon-menu-dot {
background: url(../images/mobile_index.png) 0 0 no-repeat;
width: 7px;
height: 7px;
vertical-align: middle;
display: inline-block;
margin-right: 2px;
margin-top: -2px;
bottom: 60px;
background-color: #fafafa;
border-top-width: 0;
}
.menu-item .menu-link, .add-item .menu-link {
border-left-width: 0;
border-left: 1px solid #e7e7eb;
text-align: center;
}
.sub-menu-item a, .add-sub-item a {
border: 1px solid #d0d0d0;
position: relative;
padding: 0 0.5em;
}
.sub-menu-item.current a {
border: 1px solid #44b549;
background-color: #fff;
color: #44b549;
z-index: 1;
}
.sub-menu-list li a:hover {
background: #f1f1f1;
}
.menu-item.current .menu-link {
border: 1px solid #44b549;
line-height: 48px;
background-color: #fff;
color: #44b549;
}
.sub-menu-box {
position: absolute;
bottom: 60px;
left: 0;
width: 100%;
background-color: #fff;
border-top: none;
}
.sub-menu-list {
line-height: 50px;
margin: 0;
padding: 0;
}
.sub-menu-list li {
line-height: 44px;
margin: -1px -1px 0;
list-style: none;
}
.sub-menu-box .arrow {
position: absolute;
left: 50%;
margin-left: -6px;
}
.sub-menu-box .arrow-in {
bottom: -5px;
display: inline-block;
width: 0;
height: 0;
border-width: 6px;
border-style: dashed;
border-color: transparent;
border-bottom-width: 0;
border-top-color: #fafafa;
border-top-style: solid;
}
.sub-menu-box .arrow-out {
bottom: -6px;
display: inline-block;
width: 0;
height: 0;
border-width: 6px;
border-style: dashed;
border-color: transparent;
border-bottom-width: 0;
border-top-color: #d0d0d0;
border-top-style: solid;
}
.sub-menu-item.current {
}
.sub-menu-inner-add {
display: block;
border-top: 1px solid #e7e7eb;
width: auto;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
word-wrap: normal;
cursor: pointer;
}
.weixin-icon {
background: url(../images/weixin_icon.png) 0 -4418px no-repeat;
width: 16px;
height: 16px;
vertical-align: middle;
display: inline-block;
line-height: 100px;
overflow: hidden;
}
.weixin-icon.add-gray {
background-position: 0 0;
}
.weixin-icon.sort-gray {
background: url(../images/weixin_icon.png) 0 -32px no-repeat;
background-position: 0 -32px;
margin-top: -1px;
display: none;
width: 20px;
}
.weixin-icon.big-add-gray {
background-position: -36px 0;
width: 36px;
height: 36px;
vertical-align: middle;
}
.menu-item a.menu-link:hover {
}
.add-item.extra, .add-item.extra {
float: none;
width: auto;
overflow: hidden;
}
table.btn-bar {
width: 100%;
}
table.btn-bar td {
text-align: center;
}
.item-info .item-head {
position: relative;
padding: 0;
border-bottom: 1px solid #e7e7eb;
}
.item-info .item-delete {
position: absolute;
top: 0;
right: 0;
}
table.weixin-form td {
vertical-align: middle;
height: 24px;
line-height: 24px;
padding: 8px 0;
}
#menu-content {
background-color: #fff;
padding: 16px 20px;
border: 1px solid #e7e7eb;
}
.menu-content-tips {
color: #8d8d8d;
padding-bottom: 10px;
}
.form-item dl {
position: relative;
margin: 10px 0;
}
.form-item dl dt {
width: 90px;
height: 30px;
line-height: 30px;
text-align: right;
position: absolute;
vertical-align: middle;
top: 0;
left: 0;
bottom: 0;
display: block;
}
.form-item dl dd {
position: relative;
display: block;
margin-left: 90px;
line-height: 30px;
}
.form-item .input-box {
display: inline-block;
position: relative;
height: 30px;
line-height: 30px;
vertical-align: middle;
width: 278px;
font-size: 14px;
padding: 0 10px;
border: 1px solid #e7e7eb;
box-shadow: none;
-moz-box-shadow: none;
-webkit-box-shadow: none;
border-radius: 0;
-moz-border-radius: 0;
-webkit-border-radius: 0;
background-color: #fff;
}
.form-item .input-box input {
width: 100%;
background-color: transparent;
border: 0;
outline: 0;
height: 30px;
}
.clickbox {
text-align: center;
margin: 40px 0;
}
.create-click {
display: inline-block;
padding-top: 30px;
position: relative;
width: 240px;
height: 120px;
border: 2px dotted #d9dadc;
text-align: center;
margin-bottom: 20px;
margin-left: 50px;
}
.create-click a {
display: block;
}
.create-click a strong {
display: block;
}
.keytitle {
position: absolute;
width: 100%;
text-align: center;
top: 0px;
height: 35px;
line-height: 35px;
background: #f4f5f9;
}
dl.is-item dd > label {
margin-left: 5px;
}
\ No newline at end of file
... ...
不能预览此文件类型
-----BEGIN CERTIFICATE-----
MIID9jCCAt6gAwIBAgIUMzR7dgg/jSSD5nVz7fbLpCb8+uMwDQYJKoZIhvcNAQEL
BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT
FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg
Q0EwHhcNMjAwODI0MDcxMDM5WhcNMjUwODIzMDcxMDM5WjCBhzETMBEGA1UEAwwK
MTYwMTk4NDEwNjEbMBkGA1UECgwS5b6u5L+h5ZWG5oi357O757ufMTMwMQYDVQQL
DCrljJfkuqzmlrDmoaXkupLogZTkv6Hmga/np5HmioDmnInpmZDlhazlj7gxCzAJ
BgNVBAYMAkNOMREwDwYDVQQHDAhTaGVuWmhlbjCCASIwDQYJKoZIhvcNAQEBBQAD
ggEPADCCAQoCggEBAMJvFDVhXDfW4Klj8f/iYN1SFXGG7l34E0Gf6OZepZygy0UG
8M3c06mK9avv0/KFeXbBq1js/Dp/1g8j+5MTIZUCZmo2XZut3Wq3XST1s+LwufUc
lpy6uLmQcJ6nbQF3eq4dM2ZVuo+nPxVG5GYV+swj5IBGdP8J5MMLxo9+VwSlKGM5
n+7Nrq7DBxKfKLpg3TRWTz46EYqd2psUbvZ9csuH1TYbgNQZa47sica9Hs1AGzl1
FWov/gW2VbKrcJyR3EytCTi2iLUeXsWAMaHViwdt4jk9hCJ7lkeeYwIeWj5FXLss
yWXSZcOipj1X2DVgTrOqLAJtWjXUxmnr9kpySDUCAwEAAaOBgTB/MAkGA1UdEwQC
MAAwCwYDVR0PBAQDAgTwMGUGA1UdHwReMFwwWqBYoFaGVGh0dHA6Ly9ldmNhLml0
cnVzLmNvbS5jbi9wdWJsaWMvaXRydXNjcmw/Q0E9MUJENDIyMEU1MERCQzA0QjA2
QUQzOTc1NDk4NDZDMDFDM0U4RUJEMjANBgkqhkiG9w0BAQsFAAOCAQEAu3eXDumy
gVBLw0Qo19LWiBeDTMA22Od8e43GnJPJDrwC7EEwyWOPPGMt/Y+lY/sN6Uz5XvwP
qOGtE4Y08S2u3w21zZ6oCcm5+8KYqc7+F+7PePDfXv1y1b+uBQpDuKPjvWcM9NJ6
bB8ATJdDdrWTK8vGCh5XNMtIda/dSXUX0SEkfX52mKZA1w0if75gBqCwz2c+SdBh
h7Ww7dm7QAVvRL76i+9MWPAQzK2Kk2YFNYhbAVVobmuBl9m3Ym5+P+swsqjBdHRP
Ulh541WjNx3+v/OnRuoqFmTT8ZjuHZkdQENh5ILOlXnb0ZK/AMkuY+0a/KZnpdIc
oeiaELfpUxj10A==
-----END CERTIFICATE-----
... ...
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDCbxQ1YVw31uCp
Y/H/4mDdUhVxhu5d+BNBn+jmXqWcoMtFBvDN3NOpivWr79PyhXl2watY7Pw6f9YP
I/uTEyGVAmZqNl2brd1qt10k9bPi8Ln1HJacuri5kHCep20Bd3quHTNmVbqPpz8V
RuRmFfrMI+SARnT/CeTDC8aPflcEpShjOZ/uza6uwwcSnyi6YN00Vk8+OhGKndqb
FG72fXLLh9U2G4DUGWuO7InGvR7NQBs5dRVqL/4FtlWyq3CckdxMrQk4toi1Hl7F
gDGh1YsHbeI5PYQie5ZHnmMCHlo+RVy7LMll0mXDoqY9V9g1YE6zqiwCbVo11MZp
6/ZKckg1AgMBAAECggEALwtTV2scNgKOz014c1Jh34sJ1zMvn5rzhdBcOMkzxwb8
x9HbF+RTblVKBfT8UlTLQ9muTNDiYoEl2Pn61+YsHPK8tvw6f/18cmK1bcjpYoKe
a9kj8/4VzghNx4V5EX7INzKnih46CqIL2GkTwEOXMCeLFSJ3EnJhYHQL8hI+mce0
JcSvZEzvPdmOczmkz0w1AojjK4Mzs3Gef0YWuWOsTRXsIIXAy4Pgm36K5NvmzaPo
S9aUle8KHNHm/tC1GZB4E9Ybcq5v/ZHHE7V+N9I/Kq3vdlPPrfomncqfWcIqjLkd
bx/Xbs7B9OL19mDvUqEyK5HLgULEyNIyuUf6nBIGDQKBgQDtkJsext4LA7fvKIxo
j4XHXiTAYBCqa2RPJbvy6opmTmDe6G+i+AJIYhuNSyZlxUSdq5oju+idf25cO9ac
8G1XJXzca/QsQI4WBtPW5u6msXCPiwSkaNE5swGagtftU89uV++NFSyh9wOFe0LM
06QwgDvxugPkSfWRVdPVJ3W0AwKBgQDRhaVY3C6LfMsPcS7sAAZUtOv/QgY4kUuQ
D6zzlmFaAGKxS6e+pgMmF1ePDN/gxQlkRrm6eaNqNp//AZiokBMfU3wxoWGhPvwF
TWEr8FkhDkZjxerlmu3yvU7u4dPgcjkxn+/xoyAok+RTJ7ZQiOidZ9HBie/s+dpn
WBUpIJZJZwKBgQDTuCd241cqS9MWMnYIDbj1OELarALOTs1j+EYrtLlJehq9Ljun
6w3lwoG5h7sdwhRnzHKf3CzwiIwraSzYSzG3E/LWYuOGuLWojccn5EHWj5a4ZvXp
LH0ixhTsxiV4HlRki7wzaSaQ5hreUD71Wr0eeHpz8Qrc3skbEHmsovE0TwKBgEOQ
w4dqSaA1uA3cw/La9y+twETqd7MaN7HXPEC5B9/MLAdcjFWQl4dbE6NP1YpYP/UU
UaRuwWHpB6NmUmJZQ7FgbLZcgm1LMF5NW76dnUBpqA1Yr60nTwBuR9wE+xxv+T2l
g+z6W856ErE2JzbFT64i4qN05egXknStFPXUSmKVAoGAXxwyS7upnIupcJdq21EU
t8Ay6BYf75aanncBi/TabGV67ZUVN1p2sli5a0OcQWpktBGxApC5O38GCkLMbSIX
tEiutWLXXUjmM+m+xq5wgCw6TFe7yg4r/QBa4QEh9UPIBjC8hDknCjeEhWPU2Htd
6aFPkcssGWh2mF6t3kZhAuU=
-----END PRIVATE KEY-----
... ...
<?php
return array (
0 =>
array (
'name' => 'app_id',
'title' => 'app_id',
'type' => 'string',
'content' =>
array (
),
'value' => 'wxb345558fbe7123f4',
'rule' => 'required',
'msg' => '',
'tip' => '你的微信公众号appid',
'ok' => '',
'extend' => '',
),
1 =>
array (
'name' => 'secret',
'title' => 'secret',
'type' => 'string',
'content' =>
array (
),
'value' => '7f04ffc23a080d581542e2be5c71486e',
'rule' => 'required',
'msg' => '',
'tip' => '你的微信公众号appsecret',
'ok' => '',
'extend' => '',
),
2 =>
array (
'name' => 'token',
'title' => 'token',
'type' => 'string',
'content' =>
array (
),
'value' => 'your token',
'rule' => 'required',
'msg' => '',
'tip' => '通信token',
'ok' => '',
'extend' => '',
),
3 =>
array (
'name' => 'aes_key',
'title' => 'aes_key',
'type' => 'string',
'content' =>
array (
),
'value' => 'your aes_key',
'rule' => '',
'msg' => '',
'tip' => '',
'ok' => '',
'extend' => '',
),
4 =>
array (
'name' => 'debug',
'title' => '调试模式',
'type' => 'radio',
'content' =>
array (
0 => '否',
1 => '是',
),
'value' => '1',
'rule' => 'required',
'msg' => '',
'tip' => '',
'ok' => '',
'extend' => '',
),
5 =>
array (
'name' => 'log_level',
'title' => '日志记录等级',
'type' => 'select',
'content' =>
array (
'debug' => 'debug',
'info' => 'info',
'notice' => 'notice',
'warning' => 'warning',
'error' => 'error',
'critical' => 'critical',
'alert' => 'alert',
'emergency' => 'emergency',
),
'value' => 'debug',
'rule' => 'required',
'msg' => '',
'tip' => '',
'ok' => '',
'extend' => '',
),
6 =>
array (
'name' => 'oauth_callback',
'title' => '登录回调',
'type' => 'string',
'content' =>
array (
),
'value' => 'http://www.yoursite.com/addons/wechat/index/callback',
'rule' => 'required',
'msg' => '',
'tip' => '',
'ok' => '',
'extend' => '',
),
);
... ...
<?php
namespace addons\wechat\controller;
use addons\wechat\library\Wechat;
use addons\wechat\model\WechatCaptcha;
use fast\Http;
/**
* 微信验证码验证接口
*/
class Captcha extends \think\addons\Controller
{
public function _initialize()
{
parent::_initialize();
}
/**
* 验证码检测接口
*/
public function check()
{
$captcha = $this->request->post("captcha");
$event = $this->request->post("event");
$result = WechatCaptcha::check($captcha, $event);
if ($result) {
$this->success("验证码正确");
} else {
$this->error("验证码错误");
}
}
/**
* 验证码发送接口
*/
public function send()
{
$ip = $this->request->ip();
$event = $this->request->post("event");
if (!$event) {
$this->error("参数错误");
}
$captch = WechatCaptcha::where('ip', $ip)
->where('event', $event)
->whereTime('createtime', '-2 minutes')
->find();
if ($captch) {
$this->error("获取频繁,请稍后重试");
}
$token = Wechat::getAccessToken();
if (!$token) {
$this->error("发送失败,请稍后重试");
}
$url = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token={$token}";
$params = [
'expire_seconds' => 120,
'action_name' => 'QR_STR_SCENE',
'action_info' => [
'scene' => [
'scene_str' => "captcha_" . $event . "_" . $ip,
]
],
];
$result = Http::sendRequest($url, json_encode($params));
if ($result['ret']) {
$msg = (array)json_decode($result['msg'], true);
if (isset($msg['ticket']) && isset($msg['url'])) {
$this->success("", null, ['image' => "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=" . urlencode($msg['ticket']), 'url' => $msg['url']]);
}
}
$this->error("获取失败!请稍后重试");
}
}
\ No newline at end of file
... ...
<?php
namespace addons\wechat\controller;
use addons\wechat\model\WechatAutoreply;
use addons\wechat\model\WechatCaptcha;
use addons\wechat\model\WechatContext;
use addons\wechat\model\WechatResponse;
use addons\wechat\model\WechatConfig;
use EasyWeChat\Foundation\Application;
use EasyWeChat\Payment\Order;
use addons\wechat\library\Wechat as WechatService;
use addons\wechat\library\Config as ConfigService;
use think\Log;
/**
* 微信接口
*/
class Index extends \think\addons\Controller
{
public $app = null;
public function _initialize()
{
parent::_initialize();
$this->app = new Application(ConfigService::load());
}
/**
*
*/
public function index()
{
$this->error("当前插件暂无前台页面");
}
/**
* 微信API对接接口
*/
public function api()
{
$this->app->server->setMessageHandler(function ($message) {
$wechatService = new WechatService;
$matches = null;
$openid = $message->FromUserName;
$to_openid = $message->ToUserName;
$event = $message->Event;
$eventkey = $message->EventKey ? $message->EventKey : $message->Event;
$unknownMessage = WechatConfig::value('default.unknown.message');
$unknownMessage = $unknownMessage ? $unknownMessage : "";
switch ($message->MsgType) {
case 'event': //事件消息
//验证码消息
if (in_array($event, ['subscribe', 'SCAN']) && preg_match("/^captcha_([a-zA-Z0-9]+)_([0-9\.]+)/", $eventkey, $matches)) {
return WechatCaptcha::send($openid, $matches[1], $matches[2]);
}
switch ($event) {
case 'subscribe'://添加关注
$subscribeMessage = WechatConfig::value('default.subscribe.message');
$subscribeMessage = $subscribeMessage ? $subscribeMessage : "欢迎关注我们!";
return $subscribeMessage;
case 'unsubscribe'://取消关注
return '';
case 'LOCATION'://获取地理位置
return '';
case 'VIEW': //跳转链接,eventkey为链接
return '';
case 'SCAN': //扫码
return '';
default:
break;
}
$wechatResponse = WechatResponse::where(["eventkey" => $eventkey, 'status' => 'normal'])->find();
if ($wechatResponse) {
$responseContent = (array)json_decode($wechatResponse['content'], true);
$wechatContext = WechatContext::where(['openid' => $openid])->order('id', 'desc')->find();
$data = ['eventkey' => $eventkey, 'command' => '', 'refreshtime' => time(), 'openid' => $openid];
if ($wechatContext) {
$wechatContext->save($data);
} else {
$wechatContext = WechatContext::create($data, true);
}
$result = $wechatService->response($this, $openid, '', $responseContent, $wechatContext);
if ($result) {
return $result;
}
}
return $unknownMessage;
case 'text': //文字消息
case 'image': //图片消息
case 'voice': //语音消息
case 'video': //视频消息
case 'location': //坐标消息
case 'link': //链接消息
default: //其它消息
//自动回复处理
if ($message->MsgType == 'text') {
$autoreply = null;
$autoreplyList = WechatAutoreply::where('status', 'normal')->cache(true)->order('weigh DESC,id DESC')->select();
foreach ($autoreplyList as $index => $item) {
//完全匹配和正则匹配
if ($item['text'] == $message->Content || (in_array(mb_substr($item['text'], 0, 1), ['#', '~', '/']) && preg_match($item['text'], $message->Content, $matches))) {
$autoreply = $item;
break;
}
}
if ($autoreply) {
$wechatResponse = WechatResponse::where(["eventkey" => $autoreply['eventkey'], 'status' => 'normal'])->find();
if ($wechatResponse) {
$responseContent = (array)json_decode($wechatResponse['content'], true);
$wechatContext = WechatContext::where(['openid' => $openid])->order('id', 'desc')->find();
$result = $wechatService->response($this, $openid, $message->Content, $responseContent, $wechatContext, $matches);
if ($result) {
return $result;
}
}
}
}
return $unknownMessage;
}
return ""; //SUCCESS
});
$response = $this->app->server->serve();
// 将响应输出
$response->send();
}
/**
* 登录回调
*/
public function callback()
{
}
/**
* 支付回调
*/
public function notify()
{
Log::record(file_get_contents('php://input'), "notify");
$response = $this->app->payment->handleNotify(function ($notify, $successful) {
// 使用通知里的 "微信支付订单号" 或者 "商户订单号" 去自己的数据库找到订单
$orderinfo = Order::getByTransactionId($notify->transaction_id);
if ($orderinfo) {
//订单已处理
return true;
}
$orderinfo = Order::get($notify->out_trade_no);
if (!$orderinfo) { // 如果订单不存在
return 'Order not exist.'; // 告诉微信,我已经处理完了,订单没找到,别再通知我了
}
// 如果订单存在
// 检查订单是否已经更新过支付状态,已经支付成功了就不再更新了
if ($orderinfo['paytime']) {
return true;
}
// 用户是否支付成功
if ($successful) {
// 请在这里编写处理成功的处理逻辑
return true; // 返回处理完成
} else { // 用户支付失败
return true;
}
});
$response->send();
}
}
\ No newline at end of file
... ...
name = wechat
title = 微信管理
intro = 微信管理插件
author = Karson
website = https://www.fastadmin.net
version = 1.1.0
state = 1
url = /addons/wechat.html
... ...
CREATE TABLE IF NOT EXISTS `__PREFIX__wechat_autoreply` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`title` varchar(100) NOT NULL DEFAULT '' COMMENT '标题',
`text` varchar(100) NOT NULL DEFAULT '' COMMENT '触发文本',
`eventkey` varchar(50) NOT NULL DEFAULT '' COMMENT '响应事件',
`remark` varchar(255) NOT NULL DEFAULT '' COMMENT '备注',
`weigh` int(10) NOT NULL DEFAULT '0' COMMENT '权重',
`createtime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '添加时间',
`updatetime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间',
`status` varchar(30) NOT NULL DEFAULT '' COMMENT '状态',
PRIMARY KEY (`id`),
KEY `eventkey` (`eventkey`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COMMENT='微信自动回复表';
BEGIN;
INSERT INTO `__PREFIX__wechat_autoreply`(`id`, `title`, `text`, `eventkey`, `remark`, `weigh`, `createtime`, `updatetime`, `status`) VALUES (1, '输入hello', 'hello', '58c7d908c4570', '', 1, 1493366855, 1493366855, 'normal');
INSERT INTO `__PREFIX__wechat_autoreply`(`id`, `title`, `text`, `eventkey`, `remark`, `weigh`, `createtime`, `updatetime`, `status`) VALUES (2, '输入你好', '你好', '58fdfaa9e1965', '', 2, 1493704976, 1493704976, 'normal');
COMMIT;
CREATE TABLE IF NOT EXISTS `__PREFIX__wechat_captcha` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`event` varchar(100) DEFAULT '' COMMENT '事件',
`openid` varchar(255) DEFAULT NULL COMMENT '用户openid',
`context` varchar(20) DEFAULT NULL COMMENT '上下文',
`code` varchar(30) DEFAULT NULL COMMENT '验证码',
`times` int(10) unsigned DEFAULT '0' COMMENT '验证次数',
`ip` varchar(50) DEFAULT '' COMMENT 'IP',
`createtime` int(10) DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`),
KEY `ip` (`ip`,`event`) USING BTREE,
KEY `openid` (`openid`(191),`event`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='微信公众号验证码';
CREATE TABLE IF NOT EXISTS `__PREFIX__wechat_config` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL DEFAULT '' COMMENT '配置名称',
`title` varchar(50) NOT NULL DEFAULT '' COMMENT '配置标题',
`value` text NOT NULL COMMENT '配置值',
`createtime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
`updatetime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COMMENT='微信配置表';
BEGIN;
INSERT INTO `__PREFIX__wechat_config` VALUES ('1', 'menu', '微信菜单', '[{\"name\":\"FastAdmin\",\"sub_button\":[{\"name\":\"官网\",\"type\":\"view\",\"url\":\"http:\\/\\/www.fastadmin.net\"},{\"name\":\"在线演示\",\"type\":\"click\",\"key\":\"\"},{\"name\":\"文档\",\"type\":\"view\",\"url\":\"http:\\/\\/doc.fastadmin.net\"}]},{\"name\":\"在线客服\",\"type\":\"click\",\"key\":\"58cb852984970\"},{\"name\":\"关于我们\",\"type\":\"click\",\"key\":\"58bf944aa0777\"}]', '1497398820', '1500538185'), ('2', 'service', '客服配置', '{\"onlinetime\":\"09:00-18:00\",\"offlinemsg\":\"请在工作时间联系客服!\",\"nosessionmsg\":\"当前没有客服在线!请稍后重试!\",\"waitformsg\":\"请问有什么可以帮到您?\"}', '1497429674', '1497429674'), ('3', 'signin', '连续登录配置', '{\"s1\":\"100\",\"s2\":\"200\",\"s3\":\"300\",\"sn\":\"500\"}', '1497429711', '1497429711');
COMMIT;
CREATE TABLE IF NOT EXISTS `__PREFIX__wechat_context` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`openid` varchar(64) NOT NULL DEFAULT '',
`type` varchar(30) NOT NULL DEFAULT '' COMMENT '类型',
`eventkey` varchar(64) NOT NULL DEFAULT '',
`command` varchar(64) NOT NULL DEFAULT '',
`message` varchar(255) NOT NULL DEFAULT '' COMMENT '内容',
`refreshtime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最后刷新时间',
`createtime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
`updatetime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `openid` (`openid`,`eventkey`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='微信上下文表';
CREATE TABLE IF NOT EXISTS `__PREFIX__wechat_response` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`title` varchar(100) NOT NULL DEFAULT '' COMMENT '资源名',
`eventkey` varchar(128) NOT NULL DEFAULT '' COMMENT '事件',
`type` enum('text','image','news','voice','video','music','link','app') NOT NULL DEFAULT 'text' COMMENT '类型',
`content` text NOT NULL COMMENT '内容',
`remark` varchar(255) NOT NULL DEFAULT '' COMMENT '备注',
`createtime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
`updatetime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间',
`status` varchar(30) NOT NULL DEFAULT '' COMMENT '状态',
PRIMARY KEY (`id`),
UNIQUE KEY `eventkey` (`eventkey`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COMMENT='微信资源表';
BEGIN;
INSERT INTO `__PREFIX__wechat_response` VALUES ('1', '签到送积分', '58adaf7876aab', 'app', '{\"app\":\"signin\"}', '', '1487777656', '1487777656', 'normal'), ('2', '关于我们', '58bf944aa0777', 'app', '{\"app\":\"page\",\"id\":\"1\"}', '', '1488950346', '1488950346', 'normal'), ('3', '自动回复1', '58c7d908c4570', 'text', '{\"content\":\"world\"}', '', '1489492232', '1489492232', 'normal'), ('5', '自动回复2', '58fdfaa9e1965', 'text', '{\"content\":\"我是FastAdmin!\"}', '', '1493039785', '1493039785', 'normal');
COMMIT;
... ...
<?php
namespace addons\wechat\library;
/**
* 微信配置类
*/
class Config
{
public function __construct()
{
}
public static function load()
{
$config = get_addon_config('wechat');
return [
/**
* Debug 模式,bool 值:true/false
*
* 当值为 false 时,所有的日志都不会记录
*/
'debug' => !!$config['debug'],
/**
* 账号基本信息,请从微信公众平台/开放平台获取
*/
'app_id' => $config['app_id'], // AppID
'secret' => $config['secret'], // AppSecret
'token' => $config['token'], // Token
'aes_key' => $config['aes_key'], // EncodingAESKey,安全模式下请一定要填写!!!
/**
* 日志配置
*
* level: 日志级别, 可选为:
* debug/info/notice/warning/error/critical/alert/emergency
* permission:日志文件权限(可选),默认为null(若为null值,monolog会取0644)
* file:日志文件位置(绝对路径!!!),要求可写权限
*/
'log' => [
'level' => $config['log_level'],
'permission' => 0777,
'file' => ROOT_PATH . '/runtime/log/easywechat.log',
],
/**
* OAuth 配置
*
* scopes:公众平台(snsapi_userinfo / snsapi_base),开放平台:snsapi_login
* callback:OAuth授权完成后的回调页地址
*/
'oauth' => [
'scopes' => ['snsapi_userinfo'],
'callback' => $config['oauth_callback'],
],
/**
* 微信支付
*/
'payment' => [
'merchant_id' => '1601984106',
'key' => 'b6iGEk2ZPhj8ElHuWX0slbxojCVSn8oN',
'cert_path' => ROOT_PATH.'/addons/wechat/cert/apiclient_cert.pem', // XXX: 绝对路径!!!!
'key_path' => ROOT_PATH.'/addons/wechat/cert/apiclient_key.pem', // XXX: 绝对路径!!!!
// 'device_info' => '013467007045764',
// 'sub_app_id' => '',
// 'sub_merchant_id' => '',
// ...
],
/**
* Guzzle 全局设置
*
* 更多请参考: http://docs.guzzlephp.org/en/latest/request-options.html
*/
'guzzle' => [
'timeout' => 3.0, // 超时时间(秒)
//'verify' => false, // 关掉 SSL 认证(强烈不建议!!!)
],
];
}
}
... ...
<?php
namespace addons\wechat\library;
use addons\signin\model\Signin;
use addons\third\model\Third;
use app\common\model\User;
use EasyWeChat\Message\News;
use fast\Date;
use fast\Http;
use fast\Random;
use think\Cache;
use think\Config;
/**
* 微信服务类
*/
class Wechat
{
public static function appConfig()
{
return array(
'signin' => array(
'name' => '签到送积分',
'config' => array()
),
'blog' => array(
'name' => '关联博客',
'config' => array(
array(
'type' => 'text',
'caption' => '日志ID',
'field' => 'post_id',
'rule' => '',
'extend' => 'class="form-control selectpage" data-source="blog/post/index" data-field="title"',
'options' => '',
),
array(
'type' => 'radio',
'caption' => '开启搜索日志',
'field' => 'searchpost',
'rule' => '',
'extend' => '',
'options' => [
'1' => '是',
'0' => '否',
],
),
array(
'type' => 'text',
'caption' => '正则搜索匹配索引',
'field' => 'searchregexindex',
'rule' => '',
'defaultvalue' => '1',
'extend' => '',
'options' => [],
)
)
),
'cms' => array(
'name' => '关联CMS',
'config' => array(
array(
'type' => 'text',
'caption' => '文章ID',
'field' => 'archives_id',
'rule' => '',
'extend' => 'class="form-control selectpage" data-source="cms/archives/index" data-field="title"',
'options' => ''
),
array(
'type' => 'text',
'caption' => '单页ID',
'field' => 'page_id',
'rule' => '',
'extend' => 'class="form-control selectpage" data-source="cms/page/index" data-field="title"',
'options' => ''
),
array(
'type' => 'text',
'caption' => '专题ID',
'field' => 'special_id',
'rule' => '',
'extend' => 'class="form-control selectpage" data-source="cms/special/index" data-field="title"',
'options' => ''
),
array(
'type' => 'radio',
'caption' => '开启搜索文章',
'field' => 'searcharchives',
'rule' => '',
'extend' => '',
'options' => [
'1' => '是',
'0' => '否',
],
),
array(
'type' => 'radio',
'caption' => '开启搜索单页',
'field' => 'searchpage',
'rule' => '',
'extend' => '',
'options' => [
'1' => '是',
'0' => '否',
],
),
array(
'type' => 'radio',
'caption' => '开启搜索专题',
'field' => 'searchspecial',
'rule' => '',
'extend' => '',
'options' => [
'1' => '是',
'0' => '否',
],
),
array(
'type' => 'text',
'caption' => '正则搜索匹配索引',
'field' => 'searchregexindex',
'rule' => '',
'defaultvalue' => '1',
'extend' => '',
'options' => [],
)
)
),
'ask' => array(
'name' => '关联问答',
'config' => array(
array(
'type' => 'text',
'caption' => '问题ID',
'field' => 'question_id',
'extend' => 'class="form-control selectpage" data-source="ask/question/index" data-field="title"',
'options' => ''
),
array(
'type' => 'text',
'caption' => '文章ID',
'field' => 'article_id',
'extend' => 'class="form-control selectpage" data-source="ask/article/index" data-field="title"',
'options' => ''
),
array(
'type' => 'radio',
'caption' => '开启搜索问题',
'field' => 'searchquestion',
'rule' => '',
'extend' => '',
'options' => [
'1' => '是',
'0' => '否',
],
),
array(
'type' => 'radio',
'caption' => '开启搜索文章',
'field' => 'searcharticle',
'rule' => '',
'extend' => '',
'options' => [
'1' => '是',
'0' => '否',
],
),
array(
'type' => 'text',
'caption' => '正则搜索匹配索引',
'field' => 'searchregexindex',
'rule' => '',
'defaultvalue' => '1',
'extend' => '',
'options' => [],
)
)
),
'vote' => array(
'name' => '关联投票',
'config' => array(
array(
'type' => 'text',
'caption' => '投票主题ID',
'field' => 'subject_id',
'extend' => 'class="form-control selectpage" data-source="vote/subject/index" data-field="title"',
'rule' => '',
'options' => ''
),
array(
'type' => 'text',
'caption' => '参赛人员ID',
'field' => 'player_id',
'rule' => '',
'extend' => 'class="form-control selectpage" data-source="vote/player/index" data-field="nickname"',
'options' => ''
),
array(
'type' => 'radio',
'caption' => '开启搜索主题',
'field' => 'searchsubject',
'rule' => '',
'extend' => '',
'options' => [
'1' => '是',
'0' => '否',
],
),
array(
'type' => 'radio',
'caption' => '开启搜索参赛人员',
'field' => 'searchplayer',
'rule' => '',
'extend' => '',
'options' => [
'1' => '是',
'0' => '否',
],
),
array(
'type' => 'text',
'caption' => '正则搜索匹配索引',
'field' => 'searchregexindex',
'rule' => '',
'defaultvalue' => '1',
'extend' => '',
'options' => [],
)
)
),
);
}
/**
* 应用交互
* @return array|bool|mixed|string
*/
public function response($obj, $openid, $message, $content, $context, $matches = null)
{
$response = false;
if (isset($content['app'])) {
$entry = null;
$keyword = isset($content['searchregexindex']) && $content['searchregexindex'] > -1 && $matches && isset($matches[$content['searchregexindex']])
? $matches[$content['searchregexindex']] : $message;
switch ($content['app']) {
case 'signin':
$signinInfo = get_addon_info('signin');
if (!$signinInfo || !$signinInfo['state']) {
return "请先在后台管理安装并启用《会员签到》插件";
}
$thirdInfo = get_addon_info('third');
if (!$thirdInfo || !$thirdInfo['state']) {
return "请先在后台管理安装并启用《第三方登录》插件";
}
$user = self::getUserByOpenid($openid);
if (!$user) {
return "请先在会员中心绑定微信登录,<a href='" . addon_url('third/index/connect', [':platform' => 'wechat'], true, true) . "'>点击这里绑定</a>";
}
$config = get_addon_config('signin');
$signdata = $config['signinscore'];
$lastdata = Signin::where('user_id', $user->id)->order('id', 'desc')->find();
$successions = $lastdata && $lastdata['createtime'] > Date::unixtime('day', -1) ? $lastdata['successions'] : 0;
$signin = Signin::where('user_id', $user->id)->whereTime('createtime', 'today')->find();
if ($signin) {
return '今天已签到,请明天再来!';
} else {
$successions++;
Signin::create(['user_id' => $user->id, 'successions' => $successions, 'createtime' => time()]);
$score = isset($signdata['s' . $successions]) ? $signdata['s' . $successions] : $signdata['sn'];
$user->setInc('score', $score);
User::score($score, $user->id, "连续签到{$successions}天");
return '签到成功!连续签到' . $successions . '天!获得' . $score . '积分,';
}
break;
case 'blog':
$blogInfo = get_addon_info('blog');
if (!$blogInfo || !$blogInfo['state']) {
return "请先在后台管理安装并启用《简单博客》插件";
}
$entry = \addons\blog\model\Post::get($content['post_id']);
if ($entry) {
$entry['image'] = $entry['thumb'];
}
if (!$entry && $content['searchpost']) {
$entry = \addons\blog\model\Post::where("title|description", 'like', "%{$keyword}%")->where('status', 'normal')->find();
}
if (!$entry) {
return "未搜索到任何匹配信息$keyword" . json_encode($matches);
}
break;
case 'cms':
$cmsInfo = get_addon_info('cms');
if (!$cmsInfo || !$cmsInfo['state']) {
return "请先在后台管理安装并启用《CMS内容管理系统》插件";
}
if (isset($content['archives_id']) && $content['archives_id']) {
$entry = \addons\cms\model\Archives::get($content['archives_id']);
} elseif (isset($content['page_id']) && $content['page_id']) {
$entry = \addons\cms\model\Page::get($content['page_id']);
} elseif (isset($content['special_id']) && $content['special_id']) {
$entry = \addons\cms\model\Special::get($content['special_id']);
}
if (!$entry && $content['searcharchives']) {
$entry = \addons\cms\model\Archives::where("title|description", 'like', "%{$keyword}%")->where('status', 'normal')->find();
}
if (!$entry && $content['searchpage']) {
$entry = \addons\cms\model\Page::where("title|description", 'like', "%{$keyword}%")->where('status', 'normal')->find();
}
if (!$entry && $content['searchspecial']) {
$entry = \addons\cms\model\Special::where("title|description", 'like', "%{$keyword}%")->where('status', 'normal')->find();
}
if (!$entry) {
return "未搜索到任何匹配信息";
}
break;
case 'ask':
$blogInfo = get_addon_info('ask');
if (!$blogInfo || !$blogInfo['state']) {
return "请先在后台管理安装并启用《知识付费问答》插件";
}
if (isset($content['question_id']) && $content['question_id']) {
$entry = \addons\ask\model\Question::get($content['question_id']);
} elseif (isset($content['article_id']) && $content['article_id']) {
$entry = \addons\ask\model\Article::get($content['article_id']);
}
if (!$entry && $content['searchquestion']) {
$entry = \addons\ask\model\Question::where("title", 'like', "%{$keyword}%")->where('status', 'normal')->find();
}
if (!$entry && $content['searcharticle']) {
$entry = \addons\ask\model\Article::where("title|description", 'like', "%{$keyword}%")->where('status', 'normal')->find();
}
if (!$entry) {
return "未搜索到任何匹配信息";
}
break;
case 'vote':
$blogInfo = get_addon_info('vote');
if (!$blogInfo || !$blogInfo['state']) {
return "请先在后台管理安装并启用《在线投票系统》插件";
}
if (isset($content['subject_id']) && $content['subject_id']) {
$entry = \addons\vote\model\Subject::all($content['subject_id']);
} elseif (isset($content['player_id']) && $content['player_id']) {
$entry = \addons\vote\model\Player::all($content['player_id']);
}
if (!$entry && $content['searchsubject']) {
$entry = \addons\vote\model\Subject::where("title|description", 'like', "%{$keyword}%")->where('status', 'normal')->find();
}
if (!$entry && $content['searchplayer']) {
$entry = \addons\vote\model\Player::where("nickname", 'like', "%{$keyword}%")->where('status', 'normal')->find();
}
if (!$entry) {
return "未搜索到任何匹配信息";
}
break;
default:
break;
}
if (isset($entry) && $entry) {
$news = new News();
$news->title = isset($entry['title']) ? $entry['title'] : (isset($entry['nickname']) ? $entry['nickname'] : '');
$news->url = $entry['fullurl'];
$news->image = cdnurl($entry['image'], true);
$news->description = isset($entry['description']) ? $entry['description'] : '';
$response[] = $news;
}
} else {
$response = isset($content['content']) ? $content['content'] : $response;
}
return $response;
}
/**
* 获取Token
*/
public static function getAccessToken()
{
$token = Cache::get('wechat_access_token');
if (!$token) {
$config = get_addon_config('wechat');
$params = [
'grant_type' => 'client_credential',
'appid' => $config['app_id'],
'secret' => $config['secret'],
];
$url = "https://api.weixin.qq.com/cgi-bin/token";
$result = Http::sendRequest($url, $params, 'GET');
if ($result['ret']) {
$msg = (array)json_decode($result['msg'], true);
if (isset($msg['access_token'])) {
$token = $msg['access_token'];
Cache::set('wechat_access_token', $token, $msg['expires_in'] - 1);
}
}
}
return $token;
}
/**
* 根据Openid获取用户信息
* @param string $openid 微信OpenID
* @return User|null
*/
public static function getUserByOpenid($openid)
{
$third = Third::where('platform', 'wechat')->where('openid', $openid)->find();
if ($third && $third->user_id) {
return User::get($third->user_id);
}
return null;
}
}
... ...
<?php
namespace addons\wechat\model;
use think\Model;
class WechatAutoreply extends Model
{
// 自动写入时间戳字段
protected $autoWriteTimestamp = 'int';
// 定义时间戳字段名
protected $createTime = 'createtime';
protected $updateTime = 'updatetime';
}
... ...
<?php
namespace addons\wechat\model;
use fast\Random;
use think\Model;
class WechatCaptcha extends Model
{
// 表名
protected $name = 'wechat_captcha';
// 自动写入时间戳字段
protected $autoWriteTimestamp = 'int';
// 定义时间戳字段名
protected $createTime = 'createtime';
protected $updateTime = '';
// 追加属性
protected $append = [
];
/**
* 发送验证码
* @param $openid string 用户OpenID
* @param $event string 事件
* @param $ip string IP地址
* @return string
*/
public static function send($openid, $event, $ip)
{
$captcha = self::where(['openid' => $openid, 'event' => $event])->whereTime('createtime', '-2 minutes')->find();
if ($captcha) {
return "验证码发送速度过快,请稍后重试";
}
$code = Random::alnum(4);
$data = [
'event' => $event,
'openid' => $openid,
'code' => $code,
'ip' => $ip,
];
self::create($data);
return "你的验证码是:{$code},2分钟内输入有效";
}
/**
* 检测验证码
* @param $code string 验证码
* @param $event string 事件
* @param $ip string IP
* @return bool
*/
public static function check($code, $event, $ip = null)
{
$ip = is_null($ip) ? request()->ip() : $ip;
$captcha = self::where(['ip' => $ip, 'event' => $event])->whereTime('createtime', '-2 minutes')->find();
if ($captcha && $captcha->code == $code && $captcha->times < 10) {
$captcha->setInc("times");
return true;
}
//验证大于10次或超时
if ($captcha && ($captcha->times >= 10 || time() - $captcha->createtime > 120)) {
$captcha->delete();
}
return false;
}
}
... ...
<?php
namespace addons\wechat\model;
use think\Model;
class WechatConfig extends Model
{
// 表名,不含前缀
public $name = 'wechat_config';
// 自动写入时间戳字段
protected $autoWriteTimestamp = 'int';
// 定义时间戳字段名
protected $createTime = 'createtime';
protected $updateTime = 'updatetime';
// 追加属性
protected $append = [
];
/**
* 读取指定配置名称的值
* @param string $name
* @return string
*/
public static function value($name)
{
$item = self::get(['name' => $name]);
return $item ? $item->value : '';
}
}
... ...
<?php
namespace addons\wechat\model;
use think\Model;
class WechatContext extends Model
{
// 自动写入时间戳字段
protected $autoWriteTimestamp = 'int';
// 定义时间戳字段名
protected $createTime = 'createtime';
protected $updateTime = 'updatetime';
}
... ...
<?php
namespace addons\wechat\model;
use think\Model;
class WechatResponse extends Model
{
// 自动写入时间戳字段
protected $autoWriteTimestamp = 'int';
// 定义时间戳字段名
protected $createTime = 'createtime';
protected $updateTime = 'updatetime';
}
... ...
define(['jquery', 'bootstrap', 'backend', 'form', 'table'], function ($, undefined, Backend, Form, Table) {
var Controller = {
index: function () {
// 初始化表格参数配置
Table.api.init({
extend: {
index_url: 'wechat/autoreply/index',
add_url: 'wechat/autoreply/add',
edit_url: 'wechat/autoreply/edit',
del_url: 'wechat/autoreply/del',
multi_url: 'wechat/autoreply/multi',
}
});
var table = $("#table");
// 初始化表格
table.bootstrapTable({
url: $.fn.bootstrapTable.defaults.extend.index_url,
sortName: 'id',
columns: [
[
{field: 'state', checkbox: true, },
{field: 'id', title: __('Id')},
{field: 'title', title: __('Title')},
{field: 'text', title: __('Text')},
{field: 'eventkey', title: __('Event key')},
{field: 'remark', title: __('Remark')},
{field: 'createtime', title: __('Create time'), formatter: Table.api.formatter.datetime, operate: 'RANGE', addclass: 'datetimerange', sortable: true},
{field: 'updatetime', title: __('Update time'), formatter: Table.api.formatter.datetime, operate: 'RANGE', addclass: 'datetimerange', sortable: true},
{field: 'status', title: __('Status'), formatter: Table.api.formatter.status},
{field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, formatter: Table.api.formatter.operate}
]
]
});
// 为表格绑定事件
Table.api.bindevent(table);
},
add: function () {
Controller.api.bindevent();
},
edit: function () {
Controller.api.bindevent();
},
api: {
bindevent: function () {
Form.api.bindevent($("form[role=form]"));
var refreshkey = function (data) {
$("input[name='row[eventkey]']").val(data.eventkey).trigger("change");
Layer.closeAll();
var keytitle = data.title;
var cont = $(".clickbox .create-click:first");
$(".keytitle", cont).remove();
if (keytitle) {
cont.append('<div class="keytitle">' + __('Event key') + ':' + keytitle + '</div>');
}
};
$(document).on('click', "#select-resources", function () {
var key = $("input[name='row[eventkey]']").val();
parent.Backend.api.open($(this).attr("href") + "?key=" + key, __('Select'), {callback: refreshkey});
return false;
});
$(document).on('click', "#add-resources", function () {
parent.Backend.api.open($(this).attr("href") + "?key=", __('Add'), {callback: refreshkey});
return false;
});
}
}
};
return Controller;
});
\ No newline at end of file
... ...
define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefined, Backend, Table, Form) {
var Controller = {
index: function () {
// 初始化表格参数配置
Table.api.init({
extend: {
index_url: 'wechat/config/index',
add_url: 'wechat/config/add',
edit_url: 'wechat/config/edit',
del_url: 'wechat/config/del',
multi_url: 'wechat/config/multi',
table: 'wechat_config',
}
});
var table = $("#table");
// 初始化表格
table.bootstrapTable({
url: $.fn.bootstrapTable.defaults.extend.index_url,
pk: 'id',
sortName: 'id',
columns: [
[
{field: 'state', checkbox: true},
{field: 'id', title: __('Id')},
{field: 'name', title: __('Name')},
{field: 'title', title: __('Title')},
{field: 'createtime', title: __('Createtime'), formatter: Table.api.formatter.datetime, operate: 'RANGE', addclass: 'datetimerange', sortable: true},
{field: 'updatetime', title: __('Updatetime'), formatter: Table.api.formatter.datetime, operate: 'RANGE', addclass: 'datetimerange', sortable: true},
{field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, formatter: Table.api.formatter.operate}
]
]
});
// 为表格绑定事件
Table.api.bindevent(table);
},
add: function () {
Controller.api.bindevent();
},
edit: function () {
Controller.api.bindevent();
},
api: {
bindevent: function () {
Form.api.bindevent($("form[role=form]"));
$("input[name='row[type]']:checked").trigger("click");
}
}
};
return Controller;
});
\ No newline at end of file
... ...
define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'sortable'], function ($, undefined, Backend, Table, Form, Sortable) {
var Controller = {
index: function () {
var typeList = [
{name: "click", title: "发送消息"},
{name: "view", title: "跳转网页"},
{name: "miniprogram", title: "跳转小程序"},
{name: "scancode_push", title: "扫码推"},
{name: "scancode_waitmsg", title: "扫码推提示框"},
{name: "pic_sysphoto", title: "拍照发图"},
{name: "pic_photo_or_album", title: "拍照相册发图"},
{name: "pic_weixin", title: "相册发图"},
{name: "location_select", title: "地理位置选择"},
];
String.prototype.subByte = function (start, bytes) {
for (var i = start; bytes > 0; i++) {
var code = this.charCodeAt(i);
bytes -= code < 256 ? 1 : 2;
}
return this.slice(start, i + bytes)
};
//初始化菜单
$("#menu-list").prepend(Template("menutpl", {menu: menu}));
//拖动排序
new Sortable($("#menu-list")[0], {
draggable: 'li.menu-item',
onEnd: function () {
updateChangeMenu();
}
});
//子菜单拖动排序
$(".sub-menu-list").each(function () {
new Sortable(this, {
draggable: 'li.sub-menu-item',
onEnd: function () {
updateChangeMenu();
}
});
});
//添加主菜单
$(document).on('click', '#add-item', function () {
var menu_item_total = $(".menu-item").size();
if (menu_item_total < 3) {
var item = '<li class="menu-item" data-type="click" data-key="" data-name="添加菜单" > <a href="javascript:;" class="menu-link"> <i class="icon-menu-dot"></i> <i class="weixin-icon sort-gray"></i> <span class="title">添加菜单</span> </a> <div class="sub-menu-box" style=""> <ul class="sub-menu-list"><li class=" add-sub-item"><a href="javascript:;" title="添加子菜单"><span class=" "><i class="weixin-icon add-gray"></i></span></a></li> </ul> <i class="arrow arrow-out"></i> <i class="arrow arrow-in"></i> </div></li>';
var itemDom = $(item);
itemDom.insertBefore(this);
itemDom.trigger("click");
$("#item_title").focus().select();
$(".sub-menu-box", itemDom).show();
updateChangeMenu();
new Sortable($(".sub-menu-list", itemDom)[0], {draggable: 'li.sub-menu-item'});
}
});
//切换类型
$(document).on('change', 'input[name=type]', function () {
$(".sub-menu-item.current,.menu-item.current").data("type", $(this).val()).trigger("click");
});
//删除菜单
$(document).on('click', '#item_delete', function () {
var current = $("#menu-list li.current");
var prev = current.prev("li[data-type]");
var next = current.next("li[data-type]");
if (prev.size() == 0 && next.size() == 0 && $(".sub-menu-box", current).size() == 0) {
last = current.closest(".menu-item");
} else if (prev.size() > 0 || next.size() > 0) {
last = prev.size() > 0 ? prev : next;
} else {
last = null;
$(".weixin-content").hide();
$(".no-weixin-content").show();
}
if (current.hasClass("sub-menu-item")) {
$(".add-sub-item", current.parent()).removeClass("hidden");
}
current.remove();
if (last) {
last.trigger('click');
} else {
$("input[name='name']").val('');
}
updateChangeMenu();
});
//更新修改与变动
var updateChangeMenu = function () {
var item = $("#menu-list li.current");
var values = $("#form-item").serializeArray();
$.each(values, function (i, j) {
if (j.name == 'name') {
$(">a", item).html(j.value);
}
item.data(j.name, j.value);
});
menuUpdate();
};
//更新菜单数据
var menuUpdate = function () {
$.post("wechat/menu/edit", {menu: JSON.stringify(getMenuList())}, function (data) {
if (data['code'] == 1) {
} else {
Toastr.error(__('Operation failed'));
}
}, 'json');
};
//获取菜单数据
var getMenuList = function () {
var menus = [];
var sub_button = [];
var menu_i = 0;
var sub_menu_i = 0;
var item;
$("#menu-list li").each(function (i) {
item = $(this);
var name = item.data('name');
if (name != null) {
if (item.hasClass('menu-item')) {
sub_menu_i = 0;
if (item.find('.sub-menu-item').size() > 0) {
menus[menu_i] = {"name": name, "sub_button": "sub_button"}
} else {
menus[menu_i] = $(this).data();
}
if (menu_i > 0) {
menus[menu_i - 1]['sub_button'] = menus[menu_i - 1]['sub_button'] == "sub_button" ? sub_button : menus[menu_i - 1]['sub_button'];
}
sub_button = [];
menu_i++;
} else {
sub_button[sub_menu_i++] = $(this).data();
}
}
});
if (sub_button.length > 0) {
var len = menus.length;
menus[len - 1]['sub_button'] = sub_button;
}
return menus;
};
//添加子菜单
$(document).on('click', ".add-sub-item", function () {
var sub_menu_item_total = $(this).parent().find(".sub-menu-item").size();
if (sub_menu_item_total < 5) {
var item = '<li class="sub-menu-item" data-type="click" data-key="" data-name="添加子菜单"><a href="javascript:;"><span class=" "><i class="weixin-icon sort-gray"></i><span class="sub-title">添加子菜单</span></span></a></li>';
var itemDom = $(item);
itemDom.insertBefore(this);
itemDom.trigger("click");
$("#item_title").focus().select();
updateChangeMenu();
if (sub_menu_item_total == 4) {
$(this).addClass("hidden");
}
}
return false;
});
//主菜单子菜单点击事件
$(document).on('click', ".menu-item, .sub-menu-item", function () {
var hasChild = $(".sub-menu-item", this).size() > 0 ? true : false;
if ($(this).hasClass("sub-menu-item")) {
$("#menu-list li").removeClass('current');
} else {
$("#menu-list li").removeClass('current');
$("#menu-list > li").not(this).find(".sub-menu-box").hide();
$(".sub-menu-box", this).toggle();
}
$(this).addClass('current');
var data = $.extend({}, $(this).data());
data.keytitle = data.key && typeof responselist[data.key] != 'undefined' ? responselist[data.key] : '';
data.typeList = typeList;
data.hasChild = hasChild;
data.first = $(this).hasClass("menu-item") ? true : false;
$(".weixin-content").show();
$(".no-weixin-content").hide();
$("#item-body").html(Template("itemtpl", data));
return false;
});
//触发保存
$("form#form-item").on('change', "input,textarea", function () {
updateChangeMenu();
});
//点击同步
$(document).on('click', "#menuSyn", function () {
$.post("wechat/menu/sync", {}, function (ret) {
var msg = ret.hasOwnProperty("msg") && ret.msg != "" ? ret.msg : "";
if (ret.code == 1) {
Backend.api.toastr.success('菜单同步更新成功,生效时间看微信官网说明,或者你重新关注微信号!');
} else {
Backend.api.toastr.error(msg ? msg : __('Operation failed'));
}
}, 'json');
});
//刷新资源
var refreshkey = function (data) {
responselist[data.eventkey] = data.title;
$("input[name=key]").val(data.eventkey).trigger("change");
$("#menu-list li.current").trigger("click");
Layer.closeAll();
};
//选择资源
$(document).on('click', "#select-resources", function () {
var key = $("#key").val();
Backend.api.open($(this).attr("href") + "?key=" + key, __('Select'), {
callback: refreshkey
});
return false;
});
//添加资源
$(document).on('click', "#add-resources", function () {
Backend.api.open($(this).attr("href") + "?key=" + key, __('Add'), {
callback: refreshkey
});
return false;
});
$("#menu-list li.menu-item:first").trigger("click");
},
add: function () {
Form.api.bindevent($("form[role=form]"));
},
edit: function () {
Form.api.bindevent($("form[role=form]"));
}
};
return Controller;
});
... ...
define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'adminlte'], function ($, undefined, Backend, Table, Form, Adminlte) {
var Controller = {
index: function () {
// 初始化表格参数配置
Table.api.init({
extend: {
index_url: 'wechat/response/index',
add_url: 'wechat/response/add',
edit_url: 'wechat/response/edit',
del_url: 'wechat/response/del',
multi_url: 'wechat/response/multi',
}
});
var table = $("#table");
// 初始化表格
table.bootstrapTable({
url: $.fn.bootstrapTable.defaults.extend.index_url,
sortName: 'id',
columns: [
[
{field: 'state', checkbox: true,},
{field: 'id', title: 'ID'},
{field: 'type', title: __('Type')},
{field: 'title', title: __('Resource title')},
{field: 'eventkey', title: __('Event key')},
{field: 'status', title: __('Status'), formatter: Table.api.formatter.status, operate: false},
{field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, formatter: Table.api.formatter.operate}
]
]
});
// 为表格绑定事件
Table.api.bindevent(table);
},
select: function () {
// 初始化表格参数配置
Table.api.init({
extend: {
index_url: 'wechat/response/index',
}
});
var table = $("#table");
// 初始化表格
table.bootstrapTable({
url: $.fn.bootstrapTable.defaults.extend.index_url,
sortName: 'id',
columns: [
[
{field: 'state', checkbox: true,},
{field: 'id', title: 'ID'},
{field: 'type', title: __('Type')},
{field: 'title', title: __('Title')},
{field: 'eventkey', title: __('Event key')},
{field: 'status', title: __('Status'), formatter: Table.api.formatter.status, operate: false},
{
field: 'operate', title: __('Operate'), events: {
'click .btn-chooseone': function (e, value, row, index) {
Fast.api.close(row);
},
}, formatter: function () {
return '<a href="javascript:;" class="btn btn-danger btn-chooseone btn-xs"><i class="fa fa-check"></i> ' + __('Choose') + '</a>';
}
}
]
]
});
// 为表格绑定事件
Table.api.bindevent(table);
},
add: function () {
Form.api.bindevent($("form[role=form]"), function (data) {
Fast.api.close(data);
});
Controller.api.bindevent();
},
edit: function () {
Form.api.bindevent($("form[role=form]"));
Controller.api.bindevent();
},
api: {
bindevent: function () {
var getAppFileds = function (id) {
var app = apps[id];
var appConfig = app['config'];
var str = '';
for (var i in appConfig) {
var type = appConfig[i]['type'];
var field = appConfig[i]['field'];
var caption = appConfig[i]['caption'];
var defaultvalue = typeof appConfig[i]['defaultvalue'] != 'undefined' ? appConfig[i]['defaultvalue'] : '';
if (type == 'text' || type == 'textarea') {
if (type == 'textarea') {
str += '<div class="form-group"><label for="content" class="control-label col-xs-12 col-sm-2">' + caption + ':</label><div class="col-xs-12 col-sm-8"><textarea ' + appConfig[i]['extend'] + ' class="form-control" name="row[content][' + field + ']" data-rule="' + appConfig[i]['rule'] + '">' + defaultvalue + '</textarea> </div> </div>';
} else {
str += '<div class="form-group"><label for="content" class="control-label col-xs-12 col-sm-2">' + caption + ':</label><div class="col-xs-12 col-sm-8"><input ' + appConfig[i]['extend'] + ' class="form-control" name="row[content][' + field + ']" type="text" value="' + defaultvalue + '" data-rule="' + appConfig[i]['rule'] + '"> </div> </div>';
}
} else {
var options = appConfig[i]['options'];
var html = '';
if (type == 'select') {
for (var j in options) {
html += '<option value="' + j + '">' + options[j] + '</option>';
}
html = '<select ' + appConfig[i]['extend'] + ' class="form-control" name="row[content][' + field + ']">' + html + '</select>';
} else if (type == 'checkbox') {
for (var j in options) {
html += '<input type="checkbox" name="row[content][' + field + '][]" value="' + j + '"> <span>' + options[j] + '</span> ';
}
} else if (type == 'radio') {
var index = 0;
for (var j in options) {
html += '<input type="radio" name="row[content][' + field + ']" value="' + j + '" ' + (index == 0 ? 'checked' : '') + '> <span>' + options[j] + '</span> ';
index++;
}
}
str += '<div class="form-group"><label for="content" class="control-label col-xs-12 col-sm-2">' + caption + ':</label><div class="col-xs-12 col-sm-8">' + html + ' </div> </div>';
}
}
return str;
};
$(document).on('change', "#app", function () {
var app = $(this).val();
$("#appfields").html(getAppFileds(app));
if (datas.app == app) {
delete (datas.app);
var form = $("form.form-ajax");
$.each(datas, function (i, j) {
console.log(i, j);
form.field("row[content][" + i + "]" + ($("input[name='row[content][" + i + "][]']", form).size() > 0 ? '[]' : ''), j);
});
}
Form.api.bindevent("#appfields");
});
$(document).on('click', "input[name='row[type]']", function () {
var type = $(this).val();
if (type == 'text') {
$("#expand").html('<div class="form-group"><label for="content" class="control-label col-xs-12 col-sm-2">文本内容:</label><div class="col-xs-12 col-sm-8"><textarea class="form-control" name="row[content][content]" data-rule="required"></textarea></div></div>');
$("form[role='form']").field("row[content][content]", datas.content);
} else if (type == 'app') {
$("#expand").html('<div class="form-group"><label for="content" class="control-label col-xs-12 col-sm-2">应用:</label><div class="col-xs-12 col-sm-8"><select class="form-control" name="row[content][app]" id="app">' + $("select[name=applist]").html() + '</select></div></div><div id="appfields"><div>');
$("form[role='form']").field("row[content][app]", datas.app);
$("#app").trigger('change');
}
});
$("input[name='row[type]']:checked").trigger("click");
}
}
};
return Controller;
});
\ No newline at end of file
... ...
<?php
namespace app\admin\controller\wechat;
use app\common\controller\Backend;
use app\admin\model\WechatResponse;
/**
* 微信自动回复管理
*
* @icon fa fa-circle-o
*/
class Autoreply extends Backend
{
protected $model = null;
protected $noNeedRight = ['check_text_unique'];
public function _initialize()
{
parent::_initialize();
$this->model = model('WechatAutoreply');
}
/**
* 编辑
*/
public function edit($ids = null)
{
$row = $this->model->get(['id' => $ids]);
if (!$row) {
$this->error(__('No Results were found'));
}
if ($this->request->isPost()) {
$params = $this->request->post("row/a");
if ($params) {
$row->save($params);
$this->success();
}
$this->error();
}
$response = WechatResponse::get(['eventkey' => $row['eventkey']]);
$this->view->assign("response", $response);
$this->view->assign("row", $row);
return $this->view->fetch();
}
/**
* 判断文本是否唯一
* @internal
*/
public function check_text_unique()
{
$row = $this->request->post("row/a");
$except = $this->request->post("except");
$text = isset($row['text']) ? $row['text'] : '';
if ($this->model->where('text', $text)->where(function ($query) use ($except) {
if ($except) {
$query->where('text', '<>', $except);
}
})->count() == 0) {
$this->success();
} else {
$this->error(__('Text already exists'));
}
}
}
... ...
<?php
namespace app\admin\controller\wechat;
use app\common\controller\Backend;
use think\Controller;
use think\Request;
/**
* 微信配置管理
*
* @icon fa fa-circle-o
*/
class Config extends Backend
{
protected $model = null;
public function _initialize()
{
parent::_initialize();
$this->model = model('WechatConfig');
}
/**
* 添加
*/
public function add()
{
if ($this->request->isPost()) {
$params = $this->request->post("row/a");
if ($params) {
foreach ($params as $k => &$v) {
$v = is_array($v) ? implode(',', $v) : $v;
}
if ($params['mode'] == 'json') {
//JSON字段
$fieldarr = $valuearr = [];
$field = $this->request->post('field/a');
$value = $this->request->post('value/a');
foreach ($field as $k => $v) {
if ($v != '') {
$fieldarr[] = $field[$k];
$valuearr[] = $value[$k];
}
}
$params['value'] = json_encode(array_combine($fieldarr, $valuearr), JSON_UNESCAPED_UNICODE);
}
unset($params['mode']);
try {
//是否采用模型验证
if ($this->modelValidate) {
$name = basename(str_replace('\\', '/', get_class($this->model)));
$validate = is_bool($this->modelValidate) ? ($this->modelSceneValidate ? $name . '.add' : true) : $this->modelValidate;
$this->model->validate($validate);
}
$result = $this->model->save($params);
if ($result !== false) {
$this->success();
} else {
$this->error($this->model->getError());
}
} catch (\think\exception\PDOException $e) {
$this->error($e->getMessage());
}
}
$this->error(__('Parameter %s can not be empty', ''));
}
return $this->view->fetch();
}
/**
* 编辑
*/
public function edit($ids = NULL)
{
$row = $this->model->get($ids);
if (!$row)
$this->error(__('No Results were found'));
if ($this->request->isPost()) {
$params = $this->request->post("row/a");
if ($params) {
foreach ($params as $k => &$v) {
$v = is_array($v) ? implode(',', $v) : $v;
}
if ($params['mode'] == 'json') {
//JSON字段
$fieldarr = $valuearr = [];
$field = $this->request->post('field/a');
$value = $this->request->post('value/a');
foreach ($field as $k => $v) {
if ($v != '') {
$fieldarr[] = $field[$k];
$valuearr[] = $value[$k];
}
}
$params['value'] = json_encode(array_combine($fieldarr, $valuearr), JSON_UNESCAPED_UNICODE);
}
unset($params['mode']);
try {
//是否采用模型验证
if ($this->modelValidate) {
$name = basename(str_replace('\\', '/', get_class($this->model)));
$validate = is_bool($this->modelValidate) ? ($this->modelSceneValidate ? $name . '.add' : true) : $this->modelValidate;
$row->validate($validate);
}
$result = $row->save($params);
if ($result !== false) {
$this->success();
} else {
$this->error($row->getError());
}
} catch (\think\exception\PDOException $e) {
$this->error($e->getMessage());
}
}
$this->error(__('Parameter %s can not be empty', ''));
}
$this->view->assign("row", $row);
$this->view->assign("value", (array)json_decode($row->value, true));
return $this->view->fetch();
}
}
... ...
<?php
namespace app\admin\controller\wechat;
use app\common\controller\Backend;
use app\admin\model\WechatResponse;
use EasyWeChat\Foundation\Application;
use think\Exception;
/**
* 菜单管理
*
* @icon fa fa-list-alt
*/
class Menu extends Backend
{
protected $wechatcfg = null;
public function _initialize()
{
parent::_initialize();
$this->wechatcfg = \app\admin\model\WechatConfig::get(['name' => 'menu']);
}
/**
* 查看
*/
public function index()
{
$responselist = array();
$all = WechatResponse::all();
foreach ($all as $k => $v) {
$responselist[$v['eventkey']] = $v['title'];
}
$this->view->assign('responselist', $responselist);
$this->view->assign('menu', (array)json_decode($this->wechatcfg->value, true));
return $this->view->fetch();
}
/**
* 修改
*/
public function edit($ids = null)
{
$menu = $this->request->post("menu");
$menu = (array)json_decode($menu, true);
foreach ($menu as $index => &$item) {
if (isset($item['sub_button'])) {
foreach ($item['sub_button'] as &$subitem) {
if ($subitem['type'] == 'view') {
$allowFields = ['type', 'name', 'url'];
$subitem = ['type' => $subitem['type'], 'name' => $subitem['name'], 'url' => $subitem['url']];
} else {
if ($subitem['type'] == 'miniprogram') {
$allowFields = ['type', 'name', 'url', 'appid', 'pagepath'];
$subitem = ['type' => $subitem['type'], 'name' => $subitem['name'], 'url' => $subitem['url'], 'appid' => $subitem['appid'], 'pagepath' => $subitem['pagepath']];
} else {
$allowFields = ['type', 'name', 'key'];
$subitem = ['type' => $subitem['type'], 'name' => $subitem['name'], 'key' => $subitem['key']];
}
}
$subitem = array_intersect_key($subitem, array_flip($allowFields));
}
} else {
if ($item['type'] == 'view') {
$allowFields = ['type', 'name', 'url'];
} else {
if ($item['type'] == 'miniprogram') {
$allowFields = ['type', 'name', 'url', 'appid', 'pagepath'];
} else {
$allowFields = ['type', 'name', 'key'];
}
}
$item = array_intersect_key($item, array_flip($allowFields));
}
}
$this->wechatcfg->value = json_encode($menu, JSON_UNESCAPED_UNICODE);
$this->wechatcfg->save();
$this->success();
}
/**
* 同步
*/
public function sync($ids = null)
{
$app = new Application(get_addon_config('wechat'));
try {
$hasError = false;
$menu = json_decode($this->wechatcfg->value, true);
foreach ($menu as $k => $v) {
if (isset($v['sub_button'])) {
foreach ($v['sub_button'] as $m => $n) {
if ($n['type'] == 'click' && isset($n['key']) && !$n['key']) {
$hasError = true;
break 2;
}
}
} else {
if ($v['type'] == 'click' && isset($v['key']) && !$v['key']) {
$hasError = true;
break;
}
}
}
if (!$hasError) {
try {
$ret = $app->menu->add($menu);
} catch (\EasyWeChat\Core\Exceptions\HttpException $e) {
$this->error($e->getMessage());
}
if ($ret->errcode == 0) {
$this->success();
} else {
$this->error($ret->errmsg);
}
} else {
$this->error(__('Invalid parameters'));
}
} catch (Exception $e) {
$this->error($e->getMessage());
}
}
}
... ...
<?php
namespace app\admin\controller\wechat;
use app\common\controller\Backend;
use addons\wechat\library\Wechat;
/**
* 资源管理
*
* @icon fa fa-list-alt
*/
class Response extends Backend
{
protected $model = null;
protected $searchFields = 'id,title';
public function _initialize()
{
parent::_initialize();
$this->model = model('WechatResponse');
}
/**
* 选择素材
*/
public function select()
{
return $this->view->fetch();
}
/**
* 添加
*/
public function add()
{
if ($this->request->isPost()) {
$params = $this->request->post("row/a");
$params['eventkey'] = isset($params['eventkey']) && $params['eventkey'] ? $params['eventkey'] : uniqid();
$params['content'] = json_encode($params['content']);
$params['createtime'] = time();
if ($params) {
$this->model->save($params);
$this->success();
$this->content = $params;
}
$this->error();
}
$appConfig = Wechat::appConfig();
$this->view->applist = $appConfig;
return $this->view->fetch();
}
/**
* 编辑
*/
public function edit($ids = NULL)
{
$row = $this->model->get($ids);
if (!$row)
$this->error(__('No Results were found'));
if ($this->request->isPost()) {
$params = $this->request->post("row/a");
$params['eventkey'] = isset($params['eventkey']) && $params['eventkey'] ? $params['eventkey'] : uniqid();
$params['content'] = json_encode($params['content']);
if ($params) {
$row->save($params);
$this->success();
}
$this->error();
}
$this->view->assign("row", $row);
$appConfig = Wechat::appConfig();
$this->view->applist = $appConfig;
return $this->view->fetch();
}
}
... ...
<?php
return [
'Text' => '文本',
'Event key' => '响应标识',
'Remark' => '备注',
'Text already exists' => '文本已经存在',
];
... ...
<?php
return [
'name' => '配置名称',
'value' => '配置值',
'Json key' => '键',
'Json value' => '值',
'Json editor' => 'JSON编辑器',
'Insert link' => '插入链接',
'createtime' => '创建时间',
'updatetime' => '更新时间'
];
... ...
<?php
return [
'Resource title' => '资源标题',
'Event key' => '事件标识',
'Event' => '事件标识',
'Text' => '文本',
'App' => '应用',
];
... ...
<?php
namespace app\admin\model;
use think\Model;
class WechatAutoreply extends Model
{
// 自动写入时间戳字段
protected $autoWriteTimestamp = 'int';
// 定义时间戳字段名
protected $createTime = 'createtime';
protected $updateTime = 'updatetime';
}
... ...
<?php
namespace app\admin\model;
use fast\Random;
use think\Model;
class WechatCaptcha extends Model
{
// 表名
protected $name = 'wechat_captcha';
// 自动写入时间戳字段
protected $autoWriteTimestamp = 'int';
// 定义时间戳字段名
protected $createTime = 'createtime';
protected $updateTime = '';
// 追加属性
protected $append = [
];
/**
* 发送验证码
* @param $openid string 用户OpenID
* @param $event string 事件
* @param $ip string IP地址
* @return string
*/
public static function send($openid, $event, $ip)
{
$captcha = self::where(['openid' => $openid, 'event' => $event])->whereTime('createtime', '-2 minutes')->find();
if ($captcha) {
return "验证码发送速度过快,请稍后重试";
}
$code = Random::alnum(4);
$data = [
'event' => $event,
'openid' => $openid,
'code' => $code,
'ip' => $ip,
];
self::create($data);
return "你的验证码是:{$code},2分钟内输入有效";
}
/**
* 检测验证码
* @param $code string 验证码
* @param $event string 事件
* @param $ip string IP
* @return bool
*/
public static function check($code, $event, $ip = null)
{
$ip = is_null($ip) ? request()->ip() : $ip;
$captcha = self::where(['ip' => $ip, 'event' => $event])->whereTime('createtime', '-2 minutes')->find();
if ($captcha && $captcha->code == $code && $captcha->times < 10) {
$captcha->setInc("times");
return true;
}
//验证大于10次或超时
if ($captcha && ($captcha->times >= 10 || time() - $captcha->createtime > 120)) {
$captcha->delete();
}
return false;
}
}
... ...
<?php
namespace app\admin\model;
use think\Model;
class WechatConfig extends Model
{
// 表名,不含前缀
public $name = 'wechat_config';
// 自动写入时间戳字段
protected $autoWriteTimestamp = 'int';
// 定义时间戳字段名
protected $createTime = 'createtime';
protected $updateTime = 'updatetime';
// 追加属性
protected $append = [
];
/**
* 读取指定配置名称的值
* @param string $name
* @return string
*/
public static function value($name)
{
$item = self::get(['name' => $name]);
return $item ? $item->value : '';
}
}
... ...
<?php
namespace app\admin\model;
use think\Model;
class WechatContext extends Model
{
// 自动写入时间戳字段
protected $autoWriteTimestamp = 'int';
// 定义时间戳字段名
protected $createTime = 'createtime';
protected $updateTime = 'updatetime';
}
... ...
<?php
namespace app\admin\model;
use think\Model;
class WechatResponse extends Model
{
// 自动写入时间戳字段
protected $autoWriteTimestamp = 'int';
// 定义时间戳字段名
protected $createTime = 'createtime';
protected $updateTime = 'updatetime';
}
... ...
<link href="__CDN__/assets/addons/wechat/css/menu.css?v={$site.version}" rel="stylesheet">
<style>
.clickbox {margin:0;text-align: left;}
.create-click {
margin-left:0;
}
</style>
<form id="add-form" class="form-horizontal form-ajax" role="form" data-toggle="validator" method="POST" action="">
<div class="form-group">
<label for="c-title" class="control-label col-xs-12 col-sm-2">{:__('Title')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" name="row[title]" value="" id="c-title" class="form-control" data-rule="required" />
</div>
</div>
<div class="form-group">
<label for="c-text" class="control-label col-xs-12 col-sm-2">{:__('Text')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" name="row[text]" value="" id="c-text" class="form-control" data-rule="required; remote(wechat/autoreply/check_text_unique)" />
</div>
</div>
<div class="form-group">
<label for="c-eventkey" class="control-label col-xs-12 col-sm-2">{:__('Event key')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="hidden" name="row[eventkey]" id="c-eventkey" class="form-control" value="" data-rule="required" readonly />
<div class="clickbox">
<span class="create-click"><a href="{:url('wechat.response/select')}" id="select-resources"><i class="weixin-icon big-add-gray"></i><strong>选择现有资源</strong></a></span>
<span class="create-click"><a href="{:url('wechat.response/add')}" id="add-resources"><i class="weixin-icon big-add-gray"></i><strong>添加新资源</strong></a></span>
</div>
</div>
</div>
<div class="form-group">
<label for="c-remark" class="control-label col-xs-12 col-sm-2">{:__('Remark')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" name="row[remark]" value="" id="c-remark" class="form-control" />
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Status')}:</label>
<div class="col-xs-12 col-sm-8">
{:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')])}
</div>
</div>
<div class="form-group hide layer-footer">
<label class="control-label col-xs-12 col-sm-2"></label>
<div class="col-xs-12 col-sm-8">
<button type="submit" class="btn btn-success btn-embossed disabled">{:__('OK')}</button>
<button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
</div>
</div>
</form>
... ...
<link href="__CDN__/assets/addons/wechat/css/menu.css?v={$site.version}" rel="stylesheet">
<style>
.clickbox {margin:0;text-align: left;}
.create-click {
margin-left:0;
}
</style>
<form id="edit-form" class="form-horizontal form-ajax" role="form" data-toggle="validator" method="POST" action="">
<div class="form-group">
<label for="c-title" class="control-label col-xs-12 col-sm-2">{:__('Title')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" name="row[title]" value="{$row.title}" id="c-title" class="form-control" data-rule="required" />
</div>
</div>
<div class="form-group">
<label for="c-text" class="control-label col-xs-12 col-sm-2">{:__('Text')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" name="row[text]" value="{$row.text}" id="c-text" class="form-control" data-rule="required; remote(wechat/autoreply/check_text_unique, except={$row.text})" />
</div>
</div>
<div class="form-group">
<label for="c-eventkey" class="control-label col-xs-12 col-sm-2">{:__('Content')}:</label>
<div class="col-xs-12 col-sm-8">
<div class="clickbox">
<input type="hidden" name="row[eventkey]" id="c-eventkey" class="form-control" value="{$row.eventkey}" data-rule="required" readonly />
<span class="create-click"><a href="wechat/response/select" id="select-resources"><i class="weixin-icon big-add-gray"></i><strong>选择现有资源</strong></a><div class="keytitle">资源名:{:$response['title']}</div></span>
<span class="create-click"><a href="wechat/response/add" id="add-resources"><i class="weixin-icon big-add-gray"></i><strong>添加新资源</strong></a></span>
</div>
</div>
</div>
<div class="form-group">
<label for="c-remark" class="control-label col-xs-12 col-sm-2">{:__('Remark')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" name="row[remark]" value="{$row.remark}" id="c-remark" class="form-control" />
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Status')}:</label>
<div class="col-xs-12 col-sm-8">
{:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')], $row['status'])}
</div>
</div>
<div class="form-group hide layer-footer">
<label class="control-label col-xs-12 col-sm-2"></label>
<div class="col-xs-12 col-sm-8">
<button type="submit" class="btn btn-success btn-embossed disabled">{:__('OK')}</button>
<button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
</div>
</div>
</form>
... ...
<div class="panel panel-default panel-intro">
{:build_heading()}
<div class="panel-body">
<div id="myTabContent" class="tab-content">
<div class="tab-pane fade active in" id="one">
<div class="widget-body no-padding">
<div id="toolbar" class="toolbar">
{:build_toolbar()}
<div class="dropdown btn-group {:$auth->check('wechat/autoreply/multi')?'':'hide'}">
<a class="btn btn-primary btn-more dropdown-toggle btn-disabled disabled" data-toggle="dropdown"><i class="fa fa-cog"></i> <?= __('More') ?></a>
<ul class="dropdown-menu text-left" role="menu">
<li><a class="btn btn-link btn-multi btn-disabled disabled" href="javascript:;" data-params="status=normal"><i class="fa fa-eye"></i> {:__('Set to normal')}</a></li>
<li><a class="btn btn-link btn-multi btn-disabled disabled" href="javascript:;" data-params="status=hidden"><i class="fa fa-eye-slash"></i> {:__('Set to hidden')}</a></li>
</ul>
</div>
</div>
<table id="table" class="table table-striped table-bordered table-hover"
data-operate-edit="{:$auth->check('wechat/autoreply/edit')}"
data-operate-del="{:$auth->check('wechat/autoreply/del')}"
width="100%">
</table>
</div>
</div>
</div>
</div>
</div>
... ...
<form id="add-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
<input type="hidden" name="row[mode]" value="textarea" />
<div class="form-group">
<label for="c-name" class="control-label col-xs-12 col-sm-2">{:__('Name')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-name" data-rule="required" class="form-control" name="row[name]" type="text" value="">
</div>
</div>
<div class="form-group">
<label for="c-title" class="control-label col-xs-12 col-sm-2">{:__('Title')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-title" data-rule="required" class="form-control" name="row[title]" type="text" value="">
</div>
</div>
<div class="form-group">
<label for="c-value" class="control-label col-xs-12 col-sm-2">{:__('Value')}:</label>
<div class="col-xs-12 col-sm-8">
<p>
<a href="javascript:;" class="btn btn-info btn-jsoneditor"><i class="fa fa-pencil"></i> {:__('Json editor')}</a>
<a href="javascript:;" class="btn btn-primary btn-insertlink"><i class="fa fa-link"></i> {:__('Insert link')}</a>
</p>
<textarea id="c-value" class="form-control " rows="15" name="row[value]"></textarea>
<dl class="fieldlist hide" rel="1">
<dd>
<ins>{:__('Json key')}</ins>
<ins>{:__('Json value')}</ins>
</dd>
<dd>
<input type="text" name="field[0]" class="form-control" id="field-0" value="" size="10" required />
<input type="text" name="value[0]" class="form-control" id="value-0" value="" size="40" required />
<span class="btn btn-sm btn-danger btn-remove"><i class="fa fa-times"></i></span>
<span class="btn btn-sm btn-primary btn-dragsort"><i class="fa fa-arrows"></i></span>
</dd>
<dd><a href="javascript:;" class="append btn btn-sm btn-success"><i class="fa fa-plus"></i> {:__('Append')}</a></dd>
</dl>
</div>
</div>
<div class="form-group hide layer-footer">
<label class="control-label col-xs-12 col-sm-2"></label>
<div class="col-xs-12 col-sm-8">
<button type="submit" class="btn btn-success btn-embossed disabled">{:__('OK')}</button>
<button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
</div>
</div>
</form>
... ...
<form id="edit-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
<input type="hidden" name="row[mode]" value="textarea" />
<div class="form-group">
<label for="c-name" class="control-label col-xs-12 col-sm-2">{:__('Name')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-name" data-rule="required" class="form-control" name="row[name]" type="text" value="{$row.name}">
</div>
</div>
<div class="form-group">
<label for="c-title" class="control-label col-xs-12 col-sm-2">{:__('Title')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-title" data-rule="required" class="form-control" name="row[title]" type="text" value="{$row.title}">
</div>
</div>
<div class="form-group">
<label for="c-value" class="control-label col-xs-12 col-sm-2">{:__('Value')}:</label>
<div class="col-xs-12 col-sm-8">
<textarea id="c-value" class="form-control " rows="15" name="row[value]">{$row.value}</textarea>
</div>
</div>
<div class="form-group hide layer-footer">
<label class="control-label col-xs-12 col-sm-2"></label>
<div class="col-xs-12 col-sm-8">
<button type="submit" class="btn btn-success btn-embossed disabled">{:__('OK')}</button>
<button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
</div>
</div>
</form>
... ...
<div class="panel panel-default panel-intro">
{:build_heading()}
<div class="panel-body">
<div id="myTabContent" class="tab-content">
<div class="tab-pane fade active in" id="one">
<div class="widget-body no-padding">
<div id="toolbar" class="toolbar">
{:build_toolbar()}
<div class="dropdown btn-group {:$auth->check('wechat/config/multi')?'':'hide'}">
<a class="btn btn-primary btn-more dropdown-toggle btn-disabled disabled" data-toggle="dropdown"><i class="fa fa-cog"></i> {:__('More')}</a>
<ul class="dropdown-menu text-left" role="menu">
<li><a class="btn btn-link btn-multi btn-disabled disabled" href="javascript:;" data-params="status=normal"><i class="fa fa-eye"></i> {:__('Set to normal')}</a></li>
<li><a class="btn btn-link btn-multi btn-disabled disabled" href="javascript:;" data-params="status=hidden"><i class="fa fa-eye-slash"></i> {:__('Set to hidden')}</a></li>
</ul>
</div>
</div>
<table id="table" class="table table-striped table-bordered table-hover"
data-operate-edit="{:$auth->check('wechat/config/edit')}"
data-operate-del="{:$auth->check('wechat/config/del')}"
width="100%">
</table>
</div>
</div>
</div>
</div>
</div>
... ...
<link href="__CDN__/assets/addons/wechat/css/menu.css?v={$site['version']}" rel="stylesheet"/>
<style>
.form-item dl dt {
width: 120px;
}
.form-item dl dd {
margin-left: 120px;
}
.form-item dl dd input {
font-size: 12px;
}
</style>
<div class="panel panel-default panel-intro">
{:build_heading()}
<div class="panel-body">
<div id="myTabContent" class="tab-content">
<div class="tab-pane fade active in" id="one">
<div class="widget-body no-padding">
<div class="weixin-menu-setting clearfix">
<div class="mobile-menu-preview">
<div class="mobile-head-title">{$site.name}</div>
<ul class="menu-list" id="menu-list">
</ul>
</div>
<div class="weixin-body">
<div class="weixin-content" style="display:none">
<div class="item-info">
<form id="form-item" class="form-item" data-value="">
<div class="item-head">
<h4 id="current-item-name">添加子菜单</h4>
<div class="item-delete"><a href="javascript:;" id="item_delete">删除菜单</a></div>
</div>
<div style="margin-top: 20px;" id="item-body">
</div>
</form>
</div>
</div>
<div class="no-weixin-content">
点击左侧菜单进行编辑操作
</div>
</div>
</div>
<div class="text-center" style="position:relative;">
<div class="text-danger" style="width:317px;position:absolute;left:0;top:0;">
<i class="fa fa-lightbulb-o"></i> <small>可直接拖动菜单排序</small>
</div>
<div style="padding-left:337px;"><a href="javascript:;" id="menuSyn" class="btn btn-danger">保存并发布</a></div>
</div>
</div>
</div>
</div>
</div>
</div>
<script type="text/html" id="menutpl">
<%for(var i=0; i< menu.length; i++){%>
<%var first=menu[i];%>
<li id="menu-<%=i%>" class="menu-item" data-type="<%=first['type']%>" data-key="<%=first['key']%>" data-name="<%=first['name']%>" data-url="<%=first['url']%>" data-appid="<%=first['appid']%>" data-pagepath="<%=first['pagepath']%>">
<a href="javascript:;" class="menu-link">
<i class="icon-menu-dot"></i> <i class="weixin-icon sort-gray"></i> <span class="title"><%=first['name']%></span>
</a>
<div class="sub-menu-box" style="display:none;">
<ul class="sub-menu-list">
<%if(typeof first['sub_button']!='undefined'){%>
<%for(var j=0; j< first['sub_button'].length; j++){%>
<%var second=first['sub_button'][j];%>
<li id="sub-menu-<%=j%>" class="sub-menu-item" data-type="<%=second['type']%>" data-key="<%=second['key']%>" data-name="<%=second['name']%>" data-url="<%=second['url']%>" data-appid="<%=second['appid']%>" data-pagepath="<%=second['pagepath']%>"><a href="javascript:;"> <i class="weixin-icon sort-gray"></i><span class="sub-title"><%=second['name']%></span></a></li>
<%}%>
<%}%>
<li class="add-sub-item <%if(typeof first['sub_button']!='undefined' && first['sub_button'].length>=5){%>hidden<%}%>"><a href="javascript:;" title="添加子菜单"><span class=" "><i class="weixin-icon add-gray"></i></span></a></li>
</ul>
<i class="arrow arrow-out"></i> <i class="arrow arrow-in"></i>
</div>
</li>
<%}%>
<li class="add-item extra" id="add-item">
<a href="javascript:;" class="menu-link" title="添加菜单"><i class="weixin-icon add-gray"></i></a>
</li>
</script>
<script type="text/html" id="itemtpl">
<dl>
<dt id="current-item-option"><span class="is-sub-item <%=first?'hidden':''%>"></span>菜单标题:</dt>
<dd>
<div class="input-box"><input id="item_title" name="name" type="text" value="<%=name%>"></div>
</dd>
</dl>
<%if(!hasChild){%>
<dl class="is-item">
<dt id="current-item-type"><span class="is-sub-item <%=first?'hidden':''%>"></span>菜单内容:</dt>
<dd>
<%for(var i=0;i< typeList.length; i++){%>
<input id="type<%=i%>" type="radio" name="type" value="<%=typeList[i]['name']%>" <%=typeList[i]['name']==type?'checked':''%> /><label for="type<%=i%>"><span class="lbl_content"><%=typeList[i]['title']%></span></label>
<%}%>
</dd>
</dl>
<div id="menu-content" class="is-item">
<%if(type=='view'){%>
<div class="viewbox is-view">
<p class="menu-content-tips">点击该<span class="is-sub-item <%=first?'hidden':''%>"></span>菜单会跳到以下链接</p>
<dl>
<dt>页面地址:</dt>
<dd>
<div class="input-box"><input type="text" name="url" value="<%=url%>"></div>
</dd>
</dl>
</div>
<%}%>
<%if(type!='view'&&type!='miniprogram'){%>
<div class="clickbox is-click">
<input type="hidden" name="key" id="key" value="<%=key%>"/>
<span class="create-click">
<%if(keytitle){%>
<div class="keytitle">资源名:<%=keytitle%></div>
<%}%>
<a href="{:url('wechat.response/select')}" id="select-resources"><i class="weixin-icon big-add-gray"></i><strong>选择现有资源</strong></a>
</span>
<span class="create-click">
<a href="{:url('wechat.response/add')}" id="add-resources"><i class="weixin-icon big-add-gray"></i><strong>添加新资源</strong></a>
</span>
</div>
<%}%>
<%if(type=='miniprogram'){%>
<div class="viewbox is-miniprogram">
<p class="menu-content-tips">点击该<span class="is-sub-item <%=first?'':'hidden'%>"></span>菜单会跳到以下小程序</p>
<dl>
<dt>小程序ID</dt>
<dd>
<div class="input-box"><input type="text" id="appid" name="appid" placeholder="在小程序后台获取" value="<%=appid%>"></div>
</dd>
</dl>
<dl>
<dt>小程序页面路径:</dt>
<dd>
<div class="input-box"><input type="text" id="pagepath" name="pagepath" placeholder="小程序页面路径" value="<%=pagepath%>"></div>
</dd>
</dl>
<dl>
<dt>页面地址:</dt>
<dd>
<div class="input-box"><input type="text" name="url" placeholder="页面地址,当不支持小程序时会跳转此页面" value="<%=url%>"></div>
</dd>
</dl>
</div>
<%}%>
</div>
<%}%>
</script>
<!--@formatter:off-->
<script type="text/javascript">
var menu = {:json_encode($menu, JSON_UNESCAPED_UNICODE)};
var responselist = {:json_encode($responselist, JSON_UNESCAPED_UNICODE)};
</script>
<!--@formatter:on-->
... ...
<form id="add-form" class="form-horizontal form-ajax" role="form" data-toggle="validator" method="POST" action="">
<div class="form-group">
<label for="module" class="control-label col-xs-12 col-sm-2">{:__('Resource title')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" class="form-control" id="title" name="row[title]" value="" data-rule="required" />
</div>
</div>
<div class="form-group">
<label for="remark" class="control-label col-xs-12 col-sm-2">{:__('Memo')}:</label>
<div class="col-xs-12 col-sm-8">
<textarea class="form-control" id="remark" name="row[remark]"></textarea>
</div>
</div>
<div class="form-group">
<label for="content" class="control-label col-xs-12 col-sm-2">{:__('Type')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="radio" name="row[type]" value="text" id="type-text" checked />
<label for="type-text">{:__('Text')}</label>
<input type="radio" name="row[type]" value="app" id="type-app" />
<label for="type-app">{:__('App')}</label>
</div>
</div>
<div id="expand">
</div>
<div class="form-group">
<label for="status" class="control-label col-xs-12 col-sm-2">{:__('Status')}:</label>
<div class="col-xs-12 col-sm-8">
{:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')])}
</div>
</div>
<div class="form-group {:input('get.callback')?'':'hidden layer-footer'}">
<div class="col-xs-2"></div>
<div class="col-xs-12 col-sm-8">
<button type="submit" class="btn btn-success btn-embossed disabled">{:__('OK')}</button>
<button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
</div>
</div>
<select name="applist" disabled="true" class="hidden">
{foreach $applist as $k => $v}
<option value="{$k}">{$v.name}</option>
{/foreach}
</select>
</form>
<script>
var apps = {:json_encode($applist)};
var datas = {};
</script>
\ No newline at end of file
... ...
<form id="edit-form" class="form-horizontal form-ajax" role="form" data-toggle="validator" method="POST" action="">
<div class="form-group">
<label for="module" class="control-label col-xs-12 col-sm-2">{:__('Resource title')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" class="form-control" id="title" name="row[title]" value="{$row.title}" data-rule="required" />
</div>
</div>
<div class="form-group">
<label for="controller" class="control-label col-xs-12 col-sm-2">{:__('Event key')}:</label>
<div class="col-xs-12 col-sm-8">
<input type='text' class="form-control" id="eventkey" name="row[eventkey]" value="{$row.eventkey}" data-rule="required" readonly />
</div>
</div>
<div class="form-group">
<label for="remark" class="control-label col-xs-12 col-sm-2">{:__('Memo')}:</label>
<div class="col-xs-12 col-sm-8">
<textarea class="form-control" id="remark" name="row[remark]">{$row.remark}</textarea>
</div>
</div>
<div class="form-group">
<label for="content" class="control-label col-xs-12 col-sm-2">{:__('Type')}:</label>
<div class="col-xs-12 col-sm-8">
{:build_radios('row[type]', ['text' => __('Text'), 'app' => __('App')], $row['type'])}
</div>
</div>
<div id="expand">
</div>
<div class="form-group">
<div class="col-xs-2"></div>
<div class="col-xs-12 col-sm-8">
{:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')], $row['status'])}
</div>
</div>
<div class="form-group hidden layer-footer">
<div class="col-xs-2"></div>
<div class="col-xs-12 col-sm-8">
<button type="submit" class="btn btn-success btn-embossed disabled">{:__('OK')}</button>
<button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
</div>
</div>
<select name="applist" disabled="true" class="hidden">
{foreach $applist as $k => $v}
<option value="{$k}">{$v.name}</option>
{/foreach}
</select>
</form>
<script>
var apps = {:json_encode($applist)};
var datas = {$row.content};
</script>
\ No newline at end of file
... ...
<div class="panel panel-default panel-intro">
{:build_heading()}
<div class="panel-body">
<div id="myTabContent" class="tab-content">
<div class="tab-pane fade active in" id="one">
<div class="widget-body no-padding">
<div id="toolbar" class="toolbar">
{:build_toolbar();}
</div>
<table id="table" class="table table-bordered table-hover"
data-operate-edit="{:$auth->check('wechat/response/edit')}"
data-operate-del="{:$auth->check('wechat/response/del')}"
width="100%">
</table>
</div>
</div>
</div>
</div>
</div>
... ...
<div class="panel panel-default panel-intro">
{:build_heading()}
<div class="panel-body">
<div id="myTabContent" class="tab-content">
<div class="tab-pane fade active in" id="one">
<div class="widget-body no-padding">
<table id="table" class="table table-bordered table-hover" width="100%">
</table>
</div>
</div>
</div>
</div>
</div>
... ...
... ... @@ -697,6 +697,40 @@ class Goods extends Api
}
}
private function init_wx_pay_for_gzh($Ischeck=false){
//这里首先判断 此用户是否绑定了微信公众号
if($Ischeck){
$third = Third::where(['user_id' => $this->user_id, 'platform' => 'wechat'])->find();
if(!$third){
//从这里自动绑定微信公众号的账户
$this->error('您未绑定微信号',null,1008);
}
}
$config = get_addon_config('litestore');
$third_config = get_addon_config('third');
$third_options = array_intersect_key($third_config, array_flip(['wechat']));
$third_options = $third_options['wechat'];
$options = [
'debug' => true,
'log' => [
'level' => 'debug',
'file' => '/tmp/easywechat.log',
],
'app_id' => $third_options['app_id'],
'secret' => $third_options['app_secret'],
'payment' => [
'merchant_id' => $config['MCHIDGZH'],
'key' => $config['APIKEYGZH'],
'notify_url' => \think\Request::instance()->domain().addon_url('litestore/api.order/callback_for_wxgzh'),
],
];
$this->wxapp = new WXPAY_APP($options);
}
private function init_wx_pay_for_wxapp(){
$config = get_addon_config('litestore');
$options = [
... ...