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

合并分支 'liuzhen' 到 'master'

订单支付调试



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

要显示太多修改。

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

  1 +<?php
  2 +
  3 +namespace addons\wechat;
  4 +
  5 +use app\common\library\Menu;
  6 +use think\Addons;
  7 +
  8 +/**
  9 + * 微信插件
  10 + */
  11 +class Wechat extends Addons
  12 +{
  13 +
  14 + /**
  15 + * 插件安装方法
  16 + * @return bool
  17 + */
  18 + public function install()
  19 + {
  20 + $menu = [
  21 + [
  22 + 'name' => 'wechat',
  23 + 'title' => '微信管理',
  24 + 'icon' => 'fa fa-wechat',
  25 + 'sublist' => [
  26 + [
  27 + 'name' => 'wechat/autoreply',
  28 + 'title' => '自动回复管理',
  29 + 'icon' => 'fa fa-reply-all',
  30 + 'sublist' => [
  31 + ['name' => 'wechat/autoreply/index', 'title' => '查看'],
  32 + ['name' => 'wechat/autoreply/add', 'title' => '添加'],
  33 + ['name' => 'wechat/autoreply/edit', 'title' => '修改'],
  34 + ['name' => 'wechat/autoreply/del', 'title' => '删除'],
  35 + ['name' => 'wechat/autoreply/multi', 'title' => '批量更新'],
  36 + ]
  37 + ],
  38 + [
  39 + 'name' => 'wechat/config',
  40 + 'title' => '配置管理',
  41 + 'icon' => 'fa fa-cog',
  42 + 'sublist' => [
  43 + ['name' => 'wechat/config/index', 'title' => '查看'],
  44 + ['name' => 'wechat/config/add', 'title' => '添加'],
  45 + ['name' => 'wechat/config/edit', 'title' => '修改'],
  46 + ['name' => 'wechat/config/del', 'title' => '删除'],
  47 + ['name' => 'wechat/config/multi', 'title' => '批量更新'],
  48 + ]
  49 + ],
  50 + [
  51 + 'name' => 'wechat/menu',
  52 + 'title' => '菜单管理',
  53 + 'icon' => 'fa fa-list',
  54 + 'sublist' => [
  55 + ['name' => 'wechat/menu/index', 'title' => '查看'],
  56 + ['name' => 'wechat/menu/add', 'title' => '添加'],
  57 + ['name' => 'wechat/menu/edit', 'title' => '修改'],
  58 + ['name' => 'wechat/menu/del', 'title' => '删除'],
  59 + ['name' => 'wechat/menu/sync', 'title' => '同步'],
  60 + ['name' => 'wechat/menu/multi', 'title' => '批量更新'],
  61 + ]
  62 + ],
  63 + [
  64 + 'name' => 'wechat/response',
  65 + 'title' => '资源管理',
  66 + 'icon' => 'fa fa-list-alt',
  67 + 'sublist' => [
  68 + ['name' => 'wechat/response/index', 'title' => '查看'],
  69 + ['name' => 'wechat/response/add', 'title' => '添加'],
  70 + ['name' => 'wechat/response/edit', 'title' => '修改'],
  71 + ['name' => 'wechat/response/del', 'title' => '删除'],
  72 + ['name' => 'wechat/response/select', 'title' => '选择'],
  73 + ['name' => 'wechat/response/multi', 'title' => '批量更新'],
  74 + ]
  75 + ]
  76 + ]
  77 + ]
  78 + ];
  79 + Menu::create($menu);
  80 + return true;
  81 + }
  82 +
  83 + /**
  84 + * 插件卸载方法
  85 + * @return bool
  86 + */
  87 + public function uninstall()
  88 + {
  89 + Menu::delete('wechat');
  90 + return true;
  91 + }
  92 +
  93 + /**
  94 + * 插件启用方法
  95 + */
  96 + public function enable()
  97 + {
  98 + Menu::enable('wechat');
  99 + }
  100 +
  101 + /**
  102 + * 插件禁用方法
  103 + */
  104 + public function disable()
  105 + {
  106 + Menu::disable('wechat');
  107 + }
  108 +
  109 +}
  1 +<?php
  2 +
  3 +namespace app\admin\controller\wechat;
  4 +
  5 +use app\common\controller\Backend;
  6 +use app\admin\model\WechatResponse;
  7 +
  8 +/**
  9 + * 微信自动回复管理
  10 + *
  11 + * @icon fa fa-circle-o
  12 + */
  13 +class Autoreply extends Backend
  14 +{
  15 +
  16 + protected $model = null;
  17 + protected $noNeedRight = ['check_text_unique'];
  18 +
  19 + public function _initialize()
  20 + {
  21 + parent::_initialize();
  22 + $this->model = model('WechatAutoreply');
  23 + }
  24 +
  25 + /**
  26 + * 编辑
  27 + */
  28 + public function edit($ids = null)
  29 + {
  30 + $row = $this->model->get(['id' => $ids]);
  31 + if (!$row) {
  32 + $this->error(__('No Results were found'));
  33 + }
  34 + if ($this->request->isPost()) {
  35 + $params = $this->request->post("row/a");
  36 + if ($params) {
  37 + $row->save($params);
  38 + $this->success();
  39 + }
  40 + $this->error();
  41 + }
  42 + $response = WechatResponse::get(['eventkey' => $row['eventkey']]);
  43 + $this->view->assign("response", $response);
  44 + $this->view->assign("row", $row);
  45 + return $this->view->fetch();
  46 + }
  47 +
  48 + /**
  49 + * 判断文本是否唯一
  50 + * @internal
  51 + */
  52 + public function check_text_unique()
  53 + {
  54 + $row = $this->request->post("row/a");
  55 + $except = $this->request->post("except");
  56 + $text = isset($row['text']) ? $row['text'] : '';
  57 + if ($this->model->where('text', $text)->where(function ($query) use ($except) {
  58 + if ($except) {
  59 + $query->where('text', '<>', $except);
  60 + }
  61 + })->count() == 0) {
  62 + $this->success();
  63 + } else {
  64 + $this->error(__('Text already exists'));
  65 + }
  66 + }
  67 +
  68 +}
  1 +<?php
  2 +
  3 +namespace app\admin\controller\wechat;
  4 +
  5 +use app\common\controller\Backend;
  6 +use think\Controller;
  7 +use think\Request;
  8 +
  9 +/**
  10 + * 微信配置管理
  11 + *
  12 + * @icon fa fa-circle-o
  13 + */
  14 +class Config extends Backend
  15 +{
  16 +
  17 + protected $model = null;
  18 +
  19 + public function _initialize()
  20 + {
  21 + parent::_initialize();
  22 + $this->model = model('WechatConfig');
  23 + }
  24 +
  25 + /**
  26 + * 添加
  27 + */
  28 + public function add()
  29 + {
  30 + if ($this->request->isPost()) {
  31 + $params = $this->request->post("row/a");
  32 + if ($params) {
  33 + foreach ($params as $k => &$v) {
  34 + $v = is_array($v) ? implode(',', $v) : $v;
  35 + }
  36 +
  37 + if ($params['mode'] == 'json') {
  38 + //JSON字段
  39 + $fieldarr = $valuearr = [];
  40 + $field = $this->request->post('field/a');
  41 + $value = $this->request->post('value/a');
  42 + foreach ($field as $k => $v) {
  43 + if ($v != '') {
  44 + $fieldarr[] = $field[$k];
  45 + $valuearr[] = $value[$k];
  46 + }
  47 + }
  48 + $params['value'] = json_encode(array_combine($fieldarr, $valuearr), JSON_UNESCAPED_UNICODE);
  49 + }
  50 + unset($params['mode']);
  51 + try {
  52 + //是否采用模型验证
  53 + if ($this->modelValidate) {
  54 + $name = basename(str_replace('\\', '/', get_class($this->model)));
  55 + $validate = is_bool($this->modelValidate) ? ($this->modelSceneValidate ? $name . '.add' : true) : $this->modelValidate;
  56 + $this->model->validate($validate);
  57 + }
  58 + $result = $this->model->save($params);
  59 + if ($result !== false) {
  60 + $this->success();
  61 + } else {
  62 + $this->error($this->model->getError());
  63 + }
  64 + } catch (\think\exception\PDOException $e) {
  65 + $this->error($e->getMessage());
  66 + }
  67 + }
  68 + $this->error(__('Parameter %s can not be empty', ''));
  69 + }
  70 + return $this->view->fetch();
  71 + }
  72 +
  73 + /**
  74 + * 编辑
  75 + */
  76 + public function edit($ids = NULL)
  77 + {
  78 + $row = $this->model->get($ids);
  79 + if (!$row)
  80 + $this->error(__('No Results were found'));
  81 + if ($this->request->isPost()) {
  82 + $params = $this->request->post("row/a");
  83 + if ($params) {
  84 + foreach ($params as $k => &$v) {
  85 + $v = is_array($v) ? implode(',', $v) : $v;
  86 + }
  87 +
  88 + if ($params['mode'] == 'json') {
  89 + //JSON字段
  90 + $fieldarr = $valuearr = [];
  91 + $field = $this->request->post('field/a');
  92 + $value = $this->request->post('value/a');
  93 + foreach ($field as $k => $v) {
  94 + if ($v != '') {
  95 + $fieldarr[] = $field[$k];
  96 + $valuearr[] = $value[$k];
  97 + }
  98 + }
  99 + $params['value'] = json_encode(array_combine($fieldarr, $valuearr), JSON_UNESCAPED_UNICODE);
  100 + }
  101 + unset($params['mode']);
  102 + try {
  103 + //是否采用模型验证
  104 + if ($this->modelValidate) {
  105 + $name = basename(str_replace('\\', '/', get_class($this->model)));
  106 + $validate = is_bool($this->modelValidate) ? ($this->modelSceneValidate ? $name . '.add' : true) : $this->modelValidate;
  107 + $row->validate($validate);
  108 + }
  109 + $result = $row->save($params);
  110 + if ($result !== false) {
  111 + $this->success();
  112 + } else {
  113 + $this->error($row->getError());
  114 + }
  115 + } catch (\think\exception\PDOException $e) {
  116 + $this->error($e->getMessage());
  117 + }
  118 + }
  119 + $this->error(__('Parameter %s can not be empty', ''));
  120 + }
  121 + $this->view->assign("row", $row);
  122 + $this->view->assign("value", (array)json_decode($row->value, true));
  123 + return $this->view->fetch();
  124 + }
  125 +
  126 +}
  1 +<?php
  2 +
  3 +namespace app\admin\controller\wechat;
  4 +
  5 +use app\common\controller\Backend;
  6 +use app\admin\model\WechatResponse;
  7 +use EasyWeChat\Foundation\Application;
  8 +use think\Exception;
  9 +
  10 +/**
  11 + * 菜单管理
  12 + *
  13 + * @icon fa fa-list-alt
  14 + */
  15 +class Menu extends Backend
  16 +{
  17 + protected $wechatcfg = null;
  18 +
  19 + public function _initialize()
  20 + {
  21 + parent::_initialize();
  22 + $this->wechatcfg = \app\admin\model\WechatConfig::get(['name' => 'menu']);
  23 + }
  24 +
  25 + /**
  26 + * 查看
  27 + */
  28 + public function index()
  29 + {
  30 + $responselist = array();
  31 + $all = WechatResponse::all();
  32 + foreach ($all as $k => $v) {
  33 + $responselist[$v['eventkey']] = $v['title'];
  34 + }
  35 + $this->view->assign('responselist', $responselist);
  36 + $this->view->assign('menu', (array)json_decode($this->wechatcfg->value, true));
  37 + return $this->view->fetch();
  38 + }
  39 +
  40 + /**
  41 + * 修改
  42 + */
  43 + public function edit($ids = null)
  44 + {
  45 + $menu = $this->request->post("menu");
  46 + $menu = (array)json_decode($menu, true);
  47 + foreach ($menu as $index => &$item) {
  48 + if (isset($item['sub_button'])) {
  49 + foreach ($item['sub_button'] as &$subitem) {
  50 + if ($subitem['type'] == 'view') {
  51 + $allowFields = ['type', 'name', 'url'];
  52 + $subitem = ['type' => $subitem['type'], 'name' => $subitem['name'], 'url' => $subitem['url']];
  53 + } else {
  54 + if ($subitem['type'] == 'miniprogram') {
  55 + $allowFields = ['type', 'name', 'url', 'appid', 'pagepath'];
  56 + $subitem = ['type' => $subitem['type'], 'name' => $subitem['name'], 'url' => $subitem['url'], 'appid' => $subitem['appid'], 'pagepath' => $subitem['pagepath']];
  57 + } else {
  58 + $allowFields = ['type', 'name', 'key'];
  59 + $subitem = ['type' => $subitem['type'], 'name' => $subitem['name'], 'key' => $subitem['key']];
  60 + }
  61 + }
  62 + $subitem = array_intersect_key($subitem, array_flip($allowFields));
  63 + }
  64 + } else {
  65 + if ($item['type'] == 'view') {
  66 + $allowFields = ['type', 'name', 'url'];
  67 + } else {
  68 + if ($item['type'] == 'miniprogram') {
  69 + $allowFields = ['type', 'name', 'url', 'appid', 'pagepath'];
  70 + } else {
  71 + $allowFields = ['type', 'name', 'key'];
  72 + }
  73 + }
  74 + $item = array_intersect_key($item, array_flip($allowFields));
  75 + }
  76 + }
  77 + $this->wechatcfg->value = json_encode($menu, JSON_UNESCAPED_UNICODE);
  78 + $this->wechatcfg->save();
  79 + $this->success();
  80 + }
  81 +
  82 + /**
  83 + * 同步
  84 + */
  85 + public function sync($ids = null)
  86 + {
  87 + $app = new Application(get_addon_config('wechat'));
  88 + try {
  89 + $hasError = false;
  90 + $menu = json_decode($this->wechatcfg->value, true);
  91 + foreach ($menu as $k => $v) {
  92 + if (isset($v['sub_button'])) {
  93 + foreach ($v['sub_button'] as $m => $n) {
  94 + if ($n['type'] == 'click' && isset($n['key']) && !$n['key']) {
  95 + $hasError = true;
  96 + break 2;
  97 + }
  98 + }
  99 + } else {
  100 + if ($v['type'] == 'click' && isset($v['key']) && !$v['key']) {
  101 + $hasError = true;
  102 + break;
  103 + }
  104 + }
  105 + }
  106 + if (!$hasError) {
  107 + try {
  108 + $ret = $app->menu->add($menu);
  109 + } catch (\EasyWeChat\Core\Exceptions\HttpException $e) {
  110 + $this->error($e->getMessage());
  111 + }
  112 + if ($ret->errcode == 0) {
  113 + $this->success();
  114 + } else {
  115 + $this->error($ret->errmsg);
  116 + }
  117 + } else {
  118 + $this->error(__('Invalid parameters'));
  119 + }
  120 + } catch (Exception $e) {
  121 + $this->error($e->getMessage());
  122 + }
  123 + }
  124 +}
  1 +<?php
  2 +
  3 +namespace app\admin\controller\wechat;
  4 +
  5 +use app\common\controller\Backend;
  6 +use addons\wechat\library\Wechat;
  7 +
  8 +/**
  9 + * 资源管理
  10 + *
  11 + * @icon fa fa-list-alt
  12 + */
  13 +class Response extends Backend
  14 +{
  15 +
  16 + protected $model = null;
  17 + protected $searchFields = 'id,title';
  18 +
  19 + public function _initialize()
  20 + {
  21 + parent::_initialize();
  22 + $this->model = model('WechatResponse');
  23 + }
  24 +
  25 + /**
  26 + * 选择素材
  27 + */
  28 + public function select()
  29 + {
  30 + return $this->view->fetch();
  31 + }
  32 +
  33 + /**
  34 + * 添加
  35 + */
  36 + public function add()
  37 + {
  38 + if ($this->request->isPost()) {
  39 + $params = $this->request->post("row/a");
  40 + $params['eventkey'] = isset($params['eventkey']) && $params['eventkey'] ? $params['eventkey'] : uniqid();
  41 + $params['content'] = json_encode($params['content']);
  42 + $params['createtime'] = time();
  43 + if ($params) {
  44 + $this->model->save($params);
  45 + $this->success();
  46 + $this->content = $params;
  47 + }
  48 + $this->error();
  49 + }
  50 + $appConfig = Wechat::appConfig();
  51 + $this->view->applist = $appConfig;
  52 + return $this->view->fetch();
  53 + }
  54 +
  55 + /**
  56 + * 编辑
  57 + */
  58 + public function edit($ids = NULL)
  59 + {
  60 + $row = $this->model->get($ids);
  61 + if (!$row)
  62 + $this->error(__('No Results were found'));
  63 + if ($this->request->isPost()) {
  64 + $params = $this->request->post("row/a");
  65 + $params['eventkey'] = isset($params['eventkey']) && $params['eventkey'] ? $params['eventkey'] : uniqid();
  66 + $params['content'] = json_encode($params['content']);
  67 + if ($params) {
  68 + $row->save($params);
  69 + $this->success();
  70 + }
  71 + $this->error();
  72 + }
  73 + $this->view->assign("row", $row);
  74 + $appConfig = Wechat::appConfig();
  75 + $this->view->applist = $appConfig;
  76 + return $this->view->fetch();
  77 + }
  78 +
  79 +}
  1 +<?php
  2 +
  3 +return [
  4 + 'Text' => '文本',
  5 + 'Event key' => '响应标识',
  6 + 'Remark' => '备注',
  7 + 'Text already exists' => '文本已经存在',
  8 +];
  1 +<?php
  2 +
  3 +return [
  4 + 'name' => '配置名称',
  5 + 'value' => '配置值',
  6 + 'Json key' => '键',
  7 + 'Json value' => '值',
  8 + 'Json editor' => 'JSON编辑器',
  9 + 'Insert link' => '插入链接',
  10 + 'createtime' => '创建时间',
  11 + 'updatetime' => '更新时间'
  12 +];
  1 +<?php
  2 +
  3 +return [
  4 + 'Resource title' => '资源标题',
  5 + 'Event key' => '事件标识',
  6 + 'Event' => '事件标识',
  7 + 'Text' => '文本',
  8 + 'App' => '应用',
  9 +];
  1 +<?php
  2 +
  3 +namespace app\admin\model;
  4 +
  5 +use think\Model;
  6 +
  7 +class WechatAutoreply extends Model
  8 +{
  9 +
  10 + // 自动写入时间戳字段
  11 + protected $autoWriteTimestamp = 'int';
  12 + // 定义时间戳字段名
  13 + protected $createTime = 'createtime';
  14 + protected $updateTime = 'updatetime';
  15 +
  16 +}
  1 +<?php
  2 +
  3 +namespace app\admin\model;
  4 +
  5 +use fast\Random;
  6 +use think\Model;
  7 +
  8 +class WechatCaptcha extends Model
  9 +{
  10 +
  11 + // 表名
  12 + protected $name = 'wechat_captcha';
  13 + // 自动写入时间戳字段
  14 + protected $autoWriteTimestamp = 'int';
  15 + // 定义时间戳字段名
  16 + protected $createTime = 'createtime';
  17 + protected $updateTime = '';
  18 + // 追加属性
  19 + protected $append = [
  20 + ];
  21 +
  22 + /**
  23 + * 发送验证码
  24 + * @param $openid string 用户OpenID
  25 + * @param $event string 事件
  26 + * @param $ip string IP地址
  27 + * @return string
  28 + */
  29 + public static function send($openid, $event, $ip)
  30 + {
  31 + $captcha = self::where(['openid' => $openid, 'event' => $event])->whereTime('createtime', '-2 minutes')->find();
  32 + if ($captcha) {
  33 + return "验证码发送速度过快,请稍后重试";
  34 + }
  35 + $code = Random::alnum(4);
  36 + $data = [
  37 + 'event' => $event,
  38 + 'openid' => $openid,
  39 + 'code' => $code,
  40 + 'ip' => $ip,
  41 + ];
  42 + self::create($data);
  43 + return "你的验证码是:{$code},2分钟内输入有效";
  44 + }
  45 +
  46 + /**
  47 + * 检测验证码
  48 + * @param $code string 验证码
  49 + * @param $event string 事件
  50 + * @param $ip string IP
  51 + * @return bool
  52 + */
  53 + public static function check($code, $event, $ip = null)
  54 + {
  55 + $ip = is_null($ip) ? request()->ip() : $ip;
  56 + $captcha = self::where(['ip' => $ip, 'event' => $event])->whereTime('createtime', '-2 minutes')->find();
  57 + if ($captcha && $captcha->code == $code && $captcha->times < 10) {
  58 + $captcha->setInc("times");
  59 + return true;
  60 + }
  61 + //验证大于10次或超时
  62 + if ($captcha && ($captcha->times >= 10 || time() - $captcha->createtime > 120)) {
  63 + $captcha->delete();
  64 + }
  65 +
  66 + return false;
  67 + }
  68 +
  69 +}
  1 +<?php
  2 +
  3 +namespace app\admin\model;
  4 +
  5 +use think\Model;
  6 +
  7 +class WechatConfig extends Model
  8 +{
  9 +
  10 + // 表名,不含前缀
  11 + public $name = 'wechat_config';
  12 + // 自动写入时间戳字段
  13 + protected $autoWriteTimestamp = 'int';
  14 + // 定义时间戳字段名
  15 + protected $createTime = 'createtime';
  16 + protected $updateTime = 'updatetime';
  17 + // 追加属性
  18 + protected $append = [
  19 + ];
  20 +
  21 + /**
  22 + * 读取指定配置名称的值
  23 + * @param string $name
  24 + * @return string
  25 + */
  26 + public static function value($name)
  27 + {
  28 + $item = self::get(['name' => $name]);
  29 + return $item ? $item->value : '';
  30 + }
  31 +
  32 +}
  1 +<?php
  2 +
  3 +namespace app\admin\model;
  4 +
  5 +use think\Model;
  6 +
  7 +class WechatContext extends Model
  8 +{
  9 +
  10 + // 自动写入时间戳字段
  11 + protected $autoWriteTimestamp = 'int';
  12 + // 定义时间戳字段名
  13 + protected $createTime = 'createtime';
  14 + protected $updateTime = 'updatetime';
  15 +
  16 +}
  1 +<?php
  2 +
  3 +namespace app\admin\model;
  4 +
  5 +use think\Model;
  6 +
  7 +class WechatResponse extends Model
  8 +{
  9 +
  10 + // 自动写入时间戳字段
  11 + protected $autoWriteTimestamp = 'int';
  12 + // 定义时间戳字段名
  13 + protected $createTime = 'createtime';
  14 + protected $updateTime = 'updatetime';
  15 +
  16 +}
  1 +<link href="__CDN__/assets/addons/wechat/css/menu.css?v={$site.version}" rel="stylesheet">
  2 +<style>
  3 + .clickbox {margin:0;text-align: left;}
  4 + .create-click {
  5 + margin-left:0;
  6 + }
  7 +</style>
  8 +<form id="add-form" class="form-horizontal form-ajax" role="form" data-toggle="validator" method="POST" action="">
  9 + <div class="form-group">
  10 + <label for="c-title" class="control-label col-xs-12 col-sm-2">{:__('Title')}:</label>
  11 + <div class="col-xs-12 col-sm-8">
  12 + <input type="text" name="row[title]" value="" id="c-title" class="form-control" data-rule="required" />
  13 + </div>
  14 + </div>
  15 + <div class="form-group">
  16 + <label for="c-text" class="control-label col-xs-12 col-sm-2">{:__('Text')}:</label>
  17 + <div class="col-xs-12 col-sm-8">
  18 + <input type="text" name="row[text]" value="" id="c-text" class="form-control" data-rule="required; remote(wechat/autoreply/check_text_unique)" />
  19 + </div>
  20 + </div>
  21 + <div class="form-group">
  22 + <label for="c-eventkey" class="control-label col-xs-12 col-sm-2">{:__('Event key')}:</label>
  23 + <div class="col-xs-12 col-sm-8">
  24 + <input type="hidden" name="row[eventkey]" id="c-eventkey" class="form-control" value="" data-rule="required" readonly />
  25 + <div class="clickbox">
  26 + <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>
  27 + <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>
  28 + </div>
  29 + </div>
  30 + </div>
  31 + <div class="form-group">
  32 + <label for="c-remark" class="control-label col-xs-12 col-sm-2">{:__('Remark')}:</label>
  33 + <div class="col-xs-12 col-sm-8">
  34 + <input type="text" name="row[remark]" value="" id="c-remark" class="form-control" />
  35 + </div>
  36 + </div>
  37 + <div class="form-group">
  38 + <label class="control-label col-xs-12 col-sm-2">{:__('Status')}:</label>
  39 + <div class="col-xs-12 col-sm-8">
  40 + {:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')])}
  41 + </div>
  42 + </div>
  43 + <div class="form-group hide layer-footer">
  44 + <label class="control-label col-xs-12 col-sm-2"></label>
  45 + <div class="col-xs-12 col-sm-8">
  46 + <button type="submit" class="btn btn-success btn-embossed disabled">{:__('OK')}</button>
  47 + <button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
  48 + </div>
  49 + </div>
  50 +
  51 +</form>
  1 +<link href="__CDN__/assets/addons/wechat/css/menu.css?v={$site.version}" rel="stylesheet">
  2 +<style>
  3 + .clickbox {margin:0;text-align: left;}
  4 + .create-click {
  5 + margin-left:0;
  6 + }
  7 +</style>
  8 +<form id="edit-form" class="form-horizontal form-ajax" role="form" data-toggle="validator" method="POST" action="">
  9 +
  10 + <div class="form-group">
  11 + <label for="c-title" class="control-label col-xs-12 col-sm-2">{:__('Title')}:</label>
  12 + <div class="col-xs-12 col-sm-8">
  13 + <input type="text" name="row[title]" value="{$row.title}" id="c-title" class="form-control" data-rule="required" />
  14 + </div>
  15 + </div>
  16 + <div class="form-group">
  17 + <label for="c-text" class="control-label col-xs-12 col-sm-2">{:__('Text')}:</label>
  18 + <div class="col-xs-12 col-sm-8">
  19 + <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})" />
  20 + </div>
  21 + </div>
  22 + <div class="form-group">
  23 + <label for="c-eventkey" class="control-label col-xs-12 col-sm-2">{:__('Content')}:</label>
  24 + <div class="col-xs-12 col-sm-8">
  25 + <div class="clickbox">
  26 + <input type="hidden" name="row[eventkey]" id="c-eventkey" class="form-control" value="{$row.eventkey}" data-rule="required" readonly />
  27 + <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>
  28 + <span class="create-click"><a href="wechat/response/add" id="add-resources"><i class="weixin-icon big-add-gray"></i><strong>添加新资源</strong></a></span>
  29 + </div>
  30 + </div>
  31 + </div>
  32 + <div class="form-group">
  33 + <label for="c-remark" class="control-label col-xs-12 col-sm-2">{:__('Remark')}:</label>
  34 + <div class="col-xs-12 col-sm-8">
  35 + <input type="text" name="row[remark]" value="{$row.remark}" id="c-remark" class="form-control" />
  36 + </div>
  37 + </div>
  38 + <div class="form-group">
  39 + <label class="control-label col-xs-12 col-sm-2">{:__('Status')}:</label>
  40 + <div class="col-xs-12 col-sm-8">
  41 + {:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')], $row['status'])}
  42 + </div>
  43 + </div>
  44 + <div class="form-group hide layer-footer">
  45 + <label class="control-label col-xs-12 col-sm-2"></label>
  46 + <div class="col-xs-12 col-sm-8">
  47 + <button type="submit" class="btn btn-success btn-embossed disabled">{:__('OK')}</button>
  48 + <button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
  49 + </div>
  50 + </div>
  51 +</form>
  1 +<div class="panel panel-default panel-intro">
  2 + {:build_heading()}
  3 +
  4 + <div class="panel-body">
  5 + <div id="myTabContent" class="tab-content">
  6 + <div class="tab-pane fade active in" id="one">
  7 + <div class="widget-body no-padding">
  8 + <div id="toolbar" class="toolbar">
  9 + {:build_toolbar()}
  10 + <div class="dropdown btn-group {:$auth->check('wechat/autoreply/multi')?'':'hide'}">
  11 + <a class="btn btn-primary btn-more dropdown-toggle btn-disabled disabled" data-toggle="dropdown"><i class="fa fa-cog"></i> <?= __('More') ?></a>
  12 + <ul class="dropdown-menu text-left" role="menu">
  13 + <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>
  14 + <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>
  15 + </ul>
  16 + </div>
  17 + </div>
  18 + <table id="table" class="table table-striped table-bordered table-hover"
  19 + data-operate-edit="{:$auth->check('wechat/autoreply/edit')}"
  20 + data-operate-del="{:$auth->check('wechat/autoreply/del')}"
  21 + width="100%">
  22 + </table>
  23 + </div>
  24 + </div>
  25 +
  26 + </div>
  27 + </div>
  28 +</div>
  1 +<form id="add-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
  2 + <input type="hidden" name="row[mode]" value="textarea" />
  3 + <div class="form-group">
  4 + <label for="c-name" class="control-label col-xs-12 col-sm-2">{:__('Name')}:</label>
  5 + <div class="col-xs-12 col-sm-8">
  6 + <input id="c-name" data-rule="required" class="form-control" name="row[name]" type="text" value="">
  7 + </div>
  8 + </div>
  9 + <div class="form-group">
  10 + <label for="c-title" class="control-label col-xs-12 col-sm-2">{:__('Title')}:</label>
  11 + <div class="col-xs-12 col-sm-8">
  12 + <input id="c-title" data-rule="required" class="form-control" name="row[title]" type="text" value="">
  13 + </div>
  14 + </div>
  15 + <div class="form-group">
  16 + <label for="c-value" class="control-label col-xs-12 col-sm-2">{:__('Value')}:</label>
  17 + <div class="col-xs-12 col-sm-8">
  18 + <p>
  19 + <a href="javascript:;" class="btn btn-info btn-jsoneditor"><i class="fa fa-pencil"></i> {:__('Json editor')}</a>
  20 + <a href="javascript:;" class="btn btn-primary btn-insertlink"><i class="fa fa-link"></i> {:__('Insert link')}</a>
  21 + </p>
  22 + <textarea id="c-value" class="form-control " rows="15" name="row[value]"></textarea>
  23 + <dl class="fieldlist hide" rel="1">
  24 + <dd>
  25 + <ins>{:__('Json key')}</ins>
  26 + <ins>{:__('Json value')}</ins>
  27 + </dd>
  28 + <dd>
  29 + <input type="text" name="field[0]" class="form-control" id="field-0" value="" size="10" required />
  30 + <input type="text" name="value[0]" class="form-control" id="value-0" value="" size="40" required />
  31 + <span class="btn btn-sm btn-danger btn-remove"><i class="fa fa-times"></i></span>
  32 + <span class="btn btn-sm btn-primary btn-dragsort"><i class="fa fa-arrows"></i></span>
  33 + </dd>
  34 + <dd><a href="javascript:;" class="append btn btn-sm btn-success"><i class="fa fa-plus"></i> {:__('Append')}</a></dd>
  35 + </dl>
  36 + </div>
  37 + </div>
  38 + <div class="form-group hide layer-footer">
  39 + <label class="control-label col-xs-12 col-sm-2"></label>
  40 + <div class="col-xs-12 col-sm-8">
  41 + <button type="submit" class="btn btn-success btn-embossed disabled">{:__('OK')}</button>
  42 + <button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
  43 + </div>
  44 + </div>
  45 +</form>
  1 +<form id="edit-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
  2 + <input type="hidden" name="row[mode]" value="textarea" />
  3 + <div class="form-group">
  4 + <label for="c-name" class="control-label col-xs-12 col-sm-2">{:__('Name')}:</label>
  5 + <div class="col-xs-12 col-sm-8">
  6 + <input id="c-name" data-rule="required" class="form-control" name="row[name]" type="text" value="{$row.name}">
  7 + </div>
  8 + </div>
  9 + <div class="form-group">
  10 + <label for="c-title" class="control-label col-xs-12 col-sm-2">{:__('Title')}:</label>
  11 + <div class="col-xs-12 col-sm-8">
  12 + <input id="c-title" data-rule="required" class="form-control" name="row[title]" type="text" value="{$row.title}">
  13 + </div>
  14 + </div>
  15 + <div class="form-group">
  16 + <label for="c-value" class="control-label col-xs-12 col-sm-2">{:__('Value')}:</label>
  17 + <div class="col-xs-12 col-sm-8">
  18 + <textarea id="c-value" class="form-control " rows="15" name="row[value]">{$row.value}</textarea>
  19 + </div>
  20 + </div>
  21 + <div class="form-group hide layer-footer">
  22 + <label class="control-label col-xs-12 col-sm-2"></label>
  23 + <div class="col-xs-12 col-sm-8">
  24 + <button type="submit" class="btn btn-success btn-embossed disabled">{:__('OK')}</button>
  25 + <button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
  26 + </div>
  27 + </div>
  28 +</form>
  1 +<div class="panel panel-default panel-intro">
  2 + {:build_heading()}
  3 +
  4 + <div class="panel-body">
  5 + <div id="myTabContent" class="tab-content">
  6 + <div class="tab-pane fade active in" id="one">
  7 + <div class="widget-body no-padding">
  8 + <div id="toolbar" class="toolbar">
  9 + {:build_toolbar()}
  10 + <div class="dropdown btn-group {:$auth->check('wechat/config/multi')?'':'hide'}">
  11 + <a class="btn btn-primary btn-more dropdown-toggle btn-disabled disabled" data-toggle="dropdown"><i class="fa fa-cog"></i> {:__('More')}</a>
  12 + <ul class="dropdown-menu text-left" role="menu">
  13 + <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>
  14 + <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>
  15 + </ul>
  16 + </div>
  17 + </div>
  18 + <table id="table" class="table table-striped table-bordered table-hover"
  19 + data-operate-edit="{:$auth->check('wechat/config/edit')}"
  20 + data-operate-del="{:$auth->check('wechat/config/del')}"
  21 + width="100%">
  22 + </table>
  23 + </div>
  24 + </div>
  25 +
  26 + </div>
  27 + </div>
  28 +</div>
  1 +<link href="__CDN__/assets/addons/wechat/css/menu.css?v={$site['version']}" rel="stylesheet"/>
  2 +<style>
  3 + .form-item dl dt {
  4 + width: 120px;
  5 + }
  6 +
  7 + .form-item dl dd {
  8 + margin-left: 120px;
  9 + }
  10 +
  11 + .form-item dl dd input {
  12 + font-size: 12px;
  13 + }
  14 +</style>
  15 +<div class="panel panel-default panel-intro">
  16 + {:build_heading()}
  17 +
  18 + <div class="panel-body">
  19 + <div id="myTabContent" class="tab-content">
  20 + <div class="tab-pane fade active in" id="one">
  21 + <div class="widget-body no-padding">
  22 + <div class="weixin-menu-setting clearfix">
  23 + <div class="mobile-menu-preview">
  24 + <div class="mobile-head-title">{$site.name}</div>
  25 + <ul class="menu-list" id="menu-list">
  26 + </ul>
  27 + </div>
  28 + <div class="weixin-body">
  29 + <div class="weixin-content" style="display:none">
  30 + <div class="item-info">
  31 + <form id="form-item" class="form-item" data-value="">
  32 + <div class="item-head">
  33 + <h4 id="current-item-name">添加子菜单</h4>
  34 + <div class="item-delete"><a href="javascript:;" id="item_delete">删除菜单</a></div>
  35 + </div>
  36 + <div style="margin-top: 20px;" id="item-body">
  37 +
  38 + </div>
  39 + </form>
  40 + </div>
  41 + </div>
  42 + <div class="no-weixin-content">
  43 + 点击左侧菜单进行编辑操作
  44 + </div>
  45 + </div>
  46 + </div>
  47 +
  48 + <div class="text-center" style="position:relative;">
  49 + <div class="text-danger" style="width:317px;position:absolute;left:0;top:0;">
  50 + <i class="fa fa-lightbulb-o"></i> <small>可直接拖动菜单排序</small>
  51 + </div>
  52 + <div style="padding-left:337px;"><a href="javascript:;" id="menuSyn" class="btn btn-danger">保存并发布</a></div>
  53 + </div>
  54 + </div>
  55 + </div>
  56 +
  57 + </div>
  58 + </div>
  59 +</div>
  60 +<script type="text/html" id="menutpl">
  61 + <%for(var i=0; i< menu.length; i++){%>
  62 + <%var first=menu[i];%>
  63 + <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']%>">
  64 + <a href="javascript:;" class="menu-link">
  65 + <i class="icon-menu-dot"></i> <i class="weixin-icon sort-gray"></i> <span class="title"><%=first['name']%></span>
  66 + </a>
  67 +
  68 + <div class="sub-menu-box" style="display:none;">
  69 + <ul class="sub-menu-list">
  70 + <%if(typeof first['sub_button']!='undefined'){%>
  71 + <%for(var j=0; j< first['sub_button'].length; j++){%>
  72 + <%var second=first['sub_button'][j];%>
  73 + <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>
  74 + <%}%>
  75 + <%}%>
  76 + <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>
  77 + </ul>
  78 + <i class="arrow arrow-out"></i> <i class="arrow arrow-in"></i>
  79 + </div>
  80 + </li>
  81 + <%}%>
  82 + <li class="add-item extra" id="add-item">
  83 + <a href="javascript:;" class="menu-link" title="添加菜单"><i class="weixin-icon add-gray"></i></a>
  84 + </li>
  85 +</script>
  86 +<script type="text/html" id="itemtpl">
  87 + <dl>
  88 + <dt id="current-item-option"><span class="is-sub-item <%=first?'hidden':''%>"></span>菜单标题:</dt>
  89 + <dd>
  90 + <div class="input-box"><input id="item_title" name="name" type="text" value="<%=name%>"></div>
  91 + </dd>
  92 + </dl>
  93 + <%if(!hasChild){%>
  94 + <dl class="is-item">
  95 + <dt id="current-item-type"><span class="is-sub-item <%=first?'hidden':''%>"></span>菜单内容:</dt>
  96 + <dd>
  97 + <%for(var i=0;i< typeList.length; i++){%>
  98 + <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>
  99 + <%}%>
  100 + </dd>
  101 + </dl>
  102 + <div id="menu-content" class="is-item">
  103 + <%if(type=='view'){%>
  104 + <div class="viewbox is-view">
  105 + <p class="menu-content-tips">点击该<span class="is-sub-item <%=first?'hidden':''%>"></span>菜单会跳到以下链接</p>
  106 + <dl>
  107 + <dt>页面地址:</dt>
  108 + <dd>
  109 + <div class="input-box"><input type="text" name="url" value="<%=url%>"></div>
  110 + </dd>
  111 + </dl>
  112 + </div>
  113 + <%}%>
  114 + <%if(type!='view'&&type!='miniprogram'){%>
  115 + <div class="clickbox is-click">
  116 + <input type="hidden" name="key" id="key" value="<%=key%>"/>
  117 + <span class="create-click">
  118 + <%if(keytitle){%>
  119 + <div class="keytitle">资源名:<%=keytitle%></div>
  120 + <%}%>
  121 + <a href="{:url('wechat.response/select')}" id="select-resources"><i class="weixin-icon big-add-gray"></i><strong>选择现有资源</strong></a>
  122 + </span>
  123 + <span class="create-click">
  124 + <a href="{:url('wechat.response/add')}" id="add-resources"><i class="weixin-icon big-add-gray"></i><strong>添加新资源</strong></a>
  125 + </span>
  126 + </div>
  127 + <%}%>
  128 + <%if(type=='miniprogram'){%>
  129 + <div class="viewbox is-miniprogram">
  130 + <p class="menu-content-tips">点击该<span class="is-sub-item <%=first?'':'hidden'%>"></span>菜单会跳到以下小程序</p>
  131 + <dl>
  132 + <dt>小程序ID</dt>
  133 + <dd>
  134 + <div class="input-box"><input type="text" id="appid" name="appid" placeholder="在小程序后台获取" value="<%=appid%>"></div>
  135 + </dd>
  136 + </dl>
  137 + <dl>
  138 + <dt>小程序页面路径:</dt>
  139 + <dd>
  140 + <div class="input-box"><input type="text" id="pagepath" name="pagepath" placeholder="小程序页面路径" value="<%=pagepath%>"></div>
  141 + </dd>
  142 + </dl>
  143 + <dl>
  144 + <dt>页面地址:</dt>
  145 + <dd>
  146 + <div class="input-box"><input type="text" name="url" placeholder="页面地址,当不支持小程序时会跳转此页面" value="<%=url%>"></div>
  147 + </dd>
  148 + </dl>
  149 + </div>
  150 + <%}%>
  151 + </div>
  152 + <%}%>
  153 +</script>
  154 +<!--@formatter:off-->
  155 +<script type="text/javascript">
  156 + var menu = {:json_encode($menu, JSON_UNESCAPED_UNICODE)};
  157 + var responselist = {:json_encode($responselist, JSON_UNESCAPED_UNICODE)};
  158 +</script>
  159 +<!--@formatter:on-->
  1 +<form id="add-form" class="form-horizontal form-ajax" role="form" data-toggle="validator" method="POST" action="">
  2 + <div class="form-group">
  3 + <label for="module" class="control-label col-xs-12 col-sm-2">{:__('Resource title')}:</label>
  4 + <div class="col-xs-12 col-sm-8">
  5 + <input type="text" class="form-control" id="title" name="row[title]" value="" data-rule="required" />
  6 + </div>
  7 + </div>
  8 + <div class="form-group">
  9 + <label for="remark" class="control-label col-xs-12 col-sm-2">{:__('Memo')}:</label>
  10 + <div class="col-xs-12 col-sm-8">
  11 + <textarea class="form-control" id="remark" name="row[remark]"></textarea>
  12 + </div>
  13 + </div>
  14 + <div class="form-group">
  15 + <label for="content" class="control-label col-xs-12 col-sm-2">{:__('Type')}:</label>
  16 + <div class="col-xs-12 col-sm-8">
  17 + <input type="radio" name="row[type]" value="text" id="type-text" checked />
  18 + <label for="type-text">{:__('Text')}</label>
  19 + <input type="radio" name="row[type]" value="app" id="type-app" />
  20 + <label for="type-app">{:__('App')}</label>
  21 + </div>
  22 + </div>
  23 + <div id="expand">
  24 +
  25 + </div>
  26 + <div class="form-group">
  27 + <label for="status" class="control-label col-xs-12 col-sm-2">{:__('Status')}:</label>
  28 + <div class="col-xs-12 col-sm-8">
  29 + {:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')])}
  30 + </div>
  31 + </div>
  32 + <div class="form-group {:input('get.callback')?'':'hidden layer-footer'}">
  33 + <div class="col-xs-2"></div>
  34 + <div class="col-xs-12 col-sm-8">
  35 + <button type="submit" class="btn btn-success btn-embossed disabled">{:__('OK')}</button>
  36 + <button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
  37 + </div>
  38 + </div>
  39 + <select name="applist" disabled="true" class="hidden">
  40 + {foreach $applist as $k => $v}
  41 + <option value="{$k}">{$v.name}</option>
  42 + {/foreach}
  43 + </select>
  44 +</form>
  45 +<script>
  46 + var apps = {:json_encode($applist)};
  47 + var datas = {};
  48 +</script>
  1 +<form id="edit-form" class="form-horizontal form-ajax" role="form" data-toggle="validator" method="POST" action="">
  2 +
  3 + <div class="form-group">
  4 + <label for="module" class="control-label col-xs-12 col-sm-2">{:__('Resource title')}:</label>
  5 + <div class="col-xs-12 col-sm-8">
  6 + <input type="text" class="form-control" id="title" name="row[title]" value="{$row.title}" data-rule="required" />
  7 + </div>
  8 + </div>
  9 + <div class="form-group">
  10 + <label for="controller" class="control-label col-xs-12 col-sm-2">{:__('Event key')}:</label>
  11 + <div class="col-xs-12 col-sm-8">
  12 + <input type='text' class="form-control" id="eventkey" name="row[eventkey]" value="{$row.eventkey}" data-rule="required" readonly />
  13 + </div>
  14 + </div>
  15 + <div class="form-group">
  16 + <label for="remark" class="control-label col-xs-12 col-sm-2">{:__('Memo')}:</label>
  17 + <div class="col-xs-12 col-sm-8">
  18 + <textarea class="form-control" id="remark" name="row[remark]">{$row.remark}</textarea>
  19 + </div>
  20 + </div>
  21 + <div class="form-group">
  22 + <label for="content" class="control-label col-xs-12 col-sm-2">{:__('Type')}:</label>
  23 + <div class="col-xs-12 col-sm-8">
  24 + {:build_radios('row[type]', ['text' => __('Text'), 'app' => __('App')], $row['type'])}
  25 + </div>
  26 + </div>
  27 + <div id="expand">
  28 +
  29 + </div>
  30 + <div class="form-group">
  31 + <div class="col-xs-2"></div>
  32 + <div class="col-xs-12 col-sm-8">
  33 + {:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')], $row['status'])}
  34 + </div>
  35 + </div>
  36 + <div class="form-group hidden layer-footer">
  37 + <div class="col-xs-2"></div>
  38 + <div class="col-xs-12 col-sm-8">
  39 + <button type="submit" class="btn btn-success btn-embossed disabled">{:__('OK')}</button>
  40 + <button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
  41 + </div>
  42 + </div>
  43 + <select name="applist" disabled="true" class="hidden">
  44 + {foreach $applist as $k => $v}
  45 + <option value="{$k}">{$v.name}</option>
  46 + {/foreach}
  47 + </select>
  48 +</form>
  49 +<script>
  50 + var apps = {:json_encode($applist)};
  51 + var datas = {$row.content};
  52 +</script>
  1 +<div class="panel panel-default panel-intro">
  2 + {:build_heading()}
  3 +
  4 + <div class="panel-body">
  5 + <div id="myTabContent" class="tab-content">
  6 + <div class="tab-pane fade active in" id="one">
  7 + <div class="widget-body no-padding">
  8 + <div id="toolbar" class="toolbar">
  9 + {:build_toolbar();}
  10 + </div>
  11 + <table id="table" class="table table-bordered table-hover"
  12 + data-operate-edit="{:$auth->check('wechat/response/edit')}"
  13 + data-operate-del="{:$auth->check('wechat/response/del')}"
  14 + width="100%">
  15 + </table>
  16 + </div>
  17 + </div>
  18 +
  19 + </div>
  20 + </div>
  21 +</div>
  1 +<div class="panel panel-default panel-intro">
  2 + {:build_heading()}
  3 +
  4 + <div class="panel-body">
  5 + <div id="myTabContent" class="tab-content">
  6 + <div class="tab-pane fade active in" id="one">
  7 + <div class="widget-body no-padding">
  8 + <table id="table" class="table table-bordered table-hover" width="100%">
  9 +
  10 + </table>
  11 + </div>
  12 + </div>
  13 +
  14 + </div>
  15 + </div>
  16 +</div>
  1 +.weixin-menu-setting {
  2 + margin: 0;
  3 + margin-bottom: 10px;
  4 + width: 100%;
  5 +}
  6 +
  7 +.mobile-head-title {
  8 + color: #fff;
  9 + text-align: center;
  10 + padding-top: 33px;
  11 + font-size: 15px;
  12 + width: auto;
  13 + overflow: hidden;
  14 + text-overflow: ellipsis;
  15 + white-space: nowrap;
  16 + word-wrap: normal;
  17 + margin: 0 40px 0 70px;
  18 +}
  19 +
  20 +.weixin-body {
  21 + padding: 0;
  22 + margin: 0;
  23 + margin-left: 337px;
  24 +}
  25 +
  26 +.weixin-content, .no-weixin-content {
  27 + background-color: #f4f5f9;
  28 + border: 1px solid #e7e7eb;
  29 + padding: 15px;
  30 +}
  31 +
  32 +.no-weixin-content {
  33 + border: #fff;
  34 + background-color: #fff;
  35 + vertical-align: middle;
  36 + padding-top: 200px;
  37 + text-align: center;
  38 +}
  39 +
  40 +@media (max-width: 720px) {
  41 + .weixin-body {
  42 + margin-left: 0;
  43 + margin-top: 560px;
  44 + }
  45 +}
  46 +
  47 +.weixin-menu-title {
  48 + border-bottom: 1px solid #e7e7eb;
  49 + font-size: 16px;
  50 + padding: 0 20px;
  51 + line-height: 55px;
  52 + margin-bottom: 20px;
  53 +}
  54 +
  55 +.mobile-menu-preview {
  56 + display: block;
  57 + float: left;
  58 + position: relative;
  59 + width: 317px;
  60 + height: 550px;
  61 + background: transparent url(../images/mobile_header_bg.png) no-repeat 0 0;
  62 + background-position: 0 0;
  63 + border: 1px solid #e7e7eb;
  64 +}
  65 +
  66 +.mobile-menu-preview .menu-list {
  67 + position: absolute;
  68 + height: 50px;
  69 + bottom: 0;
  70 + left: 0;
  71 + right: 0;
  72 + border-top: 1px solid #e7e7eb;
  73 + background: transparent url(../images/mobile_footer_bg.png) no-repeat 0 0;
  74 + background-position: 0 0;
  75 + background-repeat: no-repeat;
  76 + padding-left: 43px;
  77 + margin: 0;
  78 +}
  79 +
  80 +.menu-list .menu-item, .menu-list .add-item {
  81 + line-height: 50px;
  82 + position: relative;
  83 + float: left;
  84 + text-align: center;
  85 + width: 33.33%;
  86 + list-style: none;
  87 +}
  88 +
  89 +.ui-sortable-placeholder {
  90 + background-color: #fff;
  91 +}
  92 +
  93 +.menu-item a, .add-item a {
  94 + display: block;
  95 + width: auto;
  96 + overflow: hidden;
  97 + text-overflow: ellipsis;
  98 + white-space: nowrap;
  99 + word-wrap: normal;
  100 + color: #616161;
  101 + text-decoration: none;
  102 +}
  103 +
  104 +.menu-item.current a.menu-link {
  105 + border: 1px solid #44b549;
  106 + line-height: 48px;
  107 + background-color: #fff;
  108 + color: #44b549;
  109 +}
  110 +
  111 +.menu-item .icon-menu-dot {
  112 + background: url(../images/mobile_index.png) 0 0 no-repeat;
  113 + width: 7px;
  114 + height: 7px;
  115 + vertical-align: middle;
  116 + display: inline-block;
  117 + margin-right: 2px;
  118 + margin-top: -2px;
  119 + bottom: 60px;
  120 + background-color: #fafafa;
  121 + border-top-width: 0;
  122 +}
  123 +
  124 +.menu-item .menu-link, .add-item .menu-link {
  125 + border-left-width: 0;
  126 + border-left: 1px solid #e7e7eb;
  127 + text-align: center;
  128 +}
  129 +
  130 +.sub-menu-item a, .add-sub-item a {
  131 + border: 1px solid #d0d0d0;
  132 + position: relative;
  133 + padding: 0 0.5em;
  134 +}
  135 +
  136 +.sub-menu-item.current a {
  137 + border: 1px solid #44b549;
  138 + background-color: #fff;
  139 + color: #44b549;
  140 + z-index: 1;
  141 +}
  142 +
  143 +.sub-menu-list li a:hover {
  144 + background: #f1f1f1;
  145 +}
  146 +
  147 +.menu-item.current .menu-link {
  148 + border: 1px solid #44b549;
  149 + line-height: 48px;
  150 + background-color: #fff;
  151 + color: #44b549;
  152 +}
  153 +
  154 +.sub-menu-box {
  155 + position: absolute;
  156 + bottom: 60px;
  157 + left: 0;
  158 + width: 100%;
  159 + background-color: #fff;
  160 + border-top: none;
  161 +}
  162 +
  163 +.sub-menu-list {
  164 + line-height: 50px;
  165 + margin: 0;
  166 + padding: 0;
  167 +}
  168 +
  169 +.sub-menu-list li {
  170 + line-height: 44px;
  171 + margin: -1px -1px 0;
  172 + list-style: none;
  173 +}
  174 +
  175 +.sub-menu-box .arrow {
  176 + position: absolute;
  177 + left: 50%;
  178 + margin-left: -6px;
  179 +}
  180 +
  181 +.sub-menu-box .arrow-in {
  182 + bottom: -5px;
  183 + display: inline-block;
  184 + width: 0;
  185 + height: 0;
  186 + border-width: 6px;
  187 + border-style: dashed;
  188 + border-color: transparent;
  189 + border-bottom-width: 0;
  190 + border-top-color: #fafafa;
  191 + border-top-style: solid;
  192 +}
  193 +
  194 +.sub-menu-box .arrow-out {
  195 + bottom: -6px;
  196 + display: inline-block;
  197 + width: 0;
  198 + height: 0;
  199 + border-width: 6px;
  200 + border-style: dashed;
  201 + border-color: transparent;
  202 + border-bottom-width: 0;
  203 + border-top-color: #d0d0d0;
  204 + border-top-style: solid;
  205 +}
  206 +
  207 +.sub-menu-item.current {
  208 +
  209 +}
  210 +
  211 +.sub-menu-inner-add {
  212 + display: block;
  213 + border-top: 1px solid #e7e7eb;
  214 + width: auto;
  215 + overflow: hidden;
  216 + text-overflow: ellipsis;
  217 + white-space: nowrap;
  218 + word-wrap: normal;
  219 + cursor: pointer;
  220 +}
  221 +
  222 +.weixin-icon {
  223 + background: url(../images/weixin_icon.png) 0 -4418px no-repeat;
  224 + width: 16px;
  225 + height: 16px;
  226 + vertical-align: middle;
  227 + display: inline-block;
  228 + line-height: 100px;
  229 + overflow: hidden;
  230 +}
  231 +
  232 +.weixin-icon.add-gray {
  233 + background-position: 0 0;
  234 +}
  235 +
  236 +.weixin-icon.sort-gray {
  237 + background: url(../images/weixin_icon.png) 0 -32px no-repeat;
  238 + background-position: 0 -32px;
  239 + margin-top: -1px;
  240 + display: none;
  241 + width: 20px;
  242 +}
  243 +
  244 +.weixin-icon.big-add-gray {
  245 + background-position: -36px 0;
  246 + width: 36px;
  247 + height: 36px;
  248 + vertical-align: middle;
  249 +}
  250 +
  251 +.menu-item a.menu-link:hover {
  252 +
  253 +}
  254 +
  255 +.add-item.extra, .add-item.extra {
  256 + float: none;
  257 + width: auto;
  258 + overflow: hidden;
  259 +}
  260 +
  261 +table.btn-bar {
  262 + width: 100%;
  263 +}
  264 +
  265 +table.btn-bar td {
  266 + text-align: center;
  267 +}
  268 +
  269 +.item-info .item-head {
  270 + position: relative;
  271 + padding: 0;
  272 + border-bottom: 1px solid #e7e7eb;
  273 +}
  274 +
  275 +.item-info .item-delete {
  276 + position: absolute;
  277 + top: 0;
  278 + right: 0;
  279 +}
  280 +
  281 +table.weixin-form td {
  282 + vertical-align: middle;
  283 + height: 24px;
  284 + line-height: 24px;
  285 + padding: 8px 0;
  286 +}
  287 +
  288 +#menu-content {
  289 + background-color: #fff;
  290 + padding: 16px 20px;
  291 + border: 1px solid #e7e7eb;
  292 +}
  293 +
  294 +.menu-content-tips {
  295 + color: #8d8d8d;
  296 + padding-bottom: 10px;
  297 +}
  298 +
  299 +.form-item dl {
  300 + position: relative;
  301 + margin: 10px 0;
  302 +}
  303 +
  304 +.form-item dl dt {
  305 + width: 90px;
  306 + height: 30px;
  307 + line-height: 30px;
  308 + text-align: right;
  309 + position: absolute;
  310 + vertical-align: middle;
  311 + top: 0;
  312 + left: 0;
  313 + bottom: 0;
  314 + display: block;
  315 +}
  316 +
  317 +.form-item dl dd {
  318 + position: relative;
  319 + display: block;
  320 + margin-left: 90px;
  321 + line-height: 30px;
  322 +}
  323 +
  324 +.form-item .input-box {
  325 + display: inline-block;
  326 + position: relative;
  327 + height: 30px;
  328 + line-height: 30px;
  329 + vertical-align: middle;
  330 + width: 278px;
  331 + font-size: 14px;
  332 + padding: 0 10px;
  333 + border: 1px solid #e7e7eb;
  334 + box-shadow: none;
  335 + -moz-box-shadow: none;
  336 + -webkit-box-shadow: none;
  337 + border-radius: 0;
  338 + -moz-border-radius: 0;
  339 + -webkit-border-radius: 0;
  340 + background-color: #fff;
  341 +}
  342 +
  343 +.form-item .input-box input {
  344 + width: 100%;
  345 + background-color: transparent;
  346 + border: 0;
  347 + outline: 0;
  348 + height: 30px;
  349 +}
  350 +
  351 +.clickbox {
  352 + text-align: center;
  353 + margin: 40px 0;
  354 +}
  355 +
  356 +.create-click {
  357 + display: inline-block;
  358 + padding-top: 30px;
  359 + position: relative;
  360 + width: 240px;
  361 + height: 120px;
  362 + border: 2px dotted #d9dadc;
  363 + text-align: center;
  364 + margin-bottom: 20px;
  365 + margin-left: 50px;
  366 +}
  367 +
  368 +.create-click a {
  369 + display: block;
  370 +}
  371 +
  372 +.create-click a strong {
  373 + display: block;
  374 +}
  375 +
  376 +.keytitle {
  377 + position: absolute;
  378 + width: 100%;
  379 + text-align: center;
  380 + top: 0px;
  381 + height: 35px;
  382 + line-height: 35px;
  383 + background: #f4f5f9;
  384 +}
  385 +
  386 +dl.is-item dd > label {
  387 + margin-left: 5px;
  388 +}
不能预览此文件类型
  1 +-----BEGIN CERTIFICATE-----
  2 +MIID9jCCAt6gAwIBAgIUMzR7dgg/jSSD5nVz7fbLpCb8+uMwDQYJKoZIhvcNAQEL
  3 +BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT
  4 +FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg
  5 +Q0EwHhcNMjAwODI0MDcxMDM5WhcNMjUwODIzMDcxMDM5WjCBhzETMBEGA1UEAwwK
  6 +MTYwMTk4NDEwNjEbMBkGA1UECgwS5b6u5L+h5ZWG5oi357O757ufMTMwMQYDVQQL
  7 +DCrljJfkuqzmlrDmoaXkupLogZTkv6Hmga/np5HmioDmnInpmZDlhazlj7gxCzAJ
  8 +BgNVBAYMAkNOMREwDwYDVQQHDAhTaGVuWmhlbjCCASIwDQYJKoZIhvcNAQEBBQAD
  9 +ggEPADCCAQoCggEBAMJvFDVhXDfW4Klj8f/iYN1SFXGG7l34E0Gf6OZepZygy0UG
  10 +8M3c06mK9avv0/KFeXbBq1js/Dp/1g8j+5MTIZUCZmo2XZut3Wq3XST1s+LwufUc
  11 +lpy6uLmQcJ6nbQF3eq4dM2ZVuo+nPxVG5GYV+swj5IBGdP8J5MMLxo9+VwSlKGM5
  12 +n+7Nrq7DBxKfKLpg3TRWTz46EYqd2psUbvZ9csuH1TYbgNQZa47sica9Hs1AGzl1
  13 +FWov/gW2VbKrcJyR3EytCTi2iLUeXsWAMaHViwdt4jk9hCJ7lkeeYwIeWj5FXLss
  14 +yWXSZcOipj1X2DVgTrOqLAJtWjXUxmnr9kpySDUCAwEAAaOBgTB/MAkGA1UdEwQC
  15 +MAAwCwYDVR0PBAQDAgTwMGUGA1UdHwReMFwwWqBYoFaGVGh0dHA6Ly9ldmNhLml0
  16 +cnVzLmNvbS5jbi9wdWJsaWMvaXRydXNjcmw/Q0E9MUJENDIyMEU1MERCQzA0QjA2
  17 +QUQzOTc1NDk4NDZDMDFDM0U4RUJEMjANBgkqhkiG9w0BAQsFAAOCAQEAu3eXDumy
  18 +gVBLw0Qo19LWiBeDTMA22Od8e43GnJPJDrwC7EEwyWOPPGMt/Y+lY/sN6Uz5XvwP
  19 +qOGtE4Y08S2u3w21zZ6oCcm5+8KYqc7+F+7PePDfXv1y1b+uBQpDuKPjvWcM9NJ6
  20 +bB8ATJdDdrWTK8vGCh5XNMtIda/dSXUX0SEkfX52mKZA1w0if75gBqCwz2c+SdBh
  21 +h7Ww7dm7QAVvRL76i+9MWPAQzK2Kk2YFNYhbAVVobmuBl9m3Ym5+P+swsqjBdHRP
  22 +Ulh541WjNx3+v/OnRuoqFmTT8ZjuHZkdQENh5ILOlXnb0ZK/AMkuY+0a/KZnpdIc
  23 +oeiaELfpUxj10A==
  24 +-----END CERTIFICATE-----
  1 +-----BEGIN PRIVATE KEY-----
  2 +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDCbxQ1YVw31uCp
  3 +Y/H/4mDdUhVxhu5d+BNBn+jmXqWcoMtFBvDN3NOpivWr79PyhXl2watY7Pw6f9YP
  4 +I/uTEyGVAmZqNl2brd1qt10k9bPi8Ln1HJacuri5kHCep20Bd3quHTNmVbqPpz8V
  5 +RuRmFfrMI+SARnT/CeTDC8aPflcEpShjOZ/uza6uwwcSnyi6YN00Vk8+OhGKndqb
  6 +FG72fXLLh9U2G4DUGWuO7InGvR7NQBs5dRVqL/4FtlWyq3CckdxMrQk4toi1Hl7F
  7 +gDGh1YsHbeI5PYQie5ZHnmMCHlo+RVy7LMll0mXDoqY9V9g1YE6zqiwCbVo11MZp
  8 +6/ZKckg1AgMBAAECggEALwtTV2scNgKOz014c1Jh34sJ1zMvn5rzhdBcOMkzxwb8
  9 +x9HbF+RTblVKBfT8UlTLQ9muTNDiYoEl2Pn61+YsHPK8tvw6f/18cmK1bcjpYoKe
  10 +a9kj8/4VzghNx4V5EX7INzKnih46CqIL2GkTwEOXMCeLFSJ3EnJhYHQL8hI+mce0
  11 +JcSvZEzvPdmOczmkz0w1AojjK4Mzs3Gef0YWuWOsTRXsIIXAy4Pgm36K5NvmzaPo
  12 +S9aUle8KHNHm/tC1GZB4E9Ybcq5v/ZHHE7V+N9I/Kq3vdlPPrfomncqfWcIqjLkd
  13 +bx/Xbs7B9OL19mDvUqEyK5HLgULEyNIyuUf6nBIGDQKBgQDtkJsext4LA7fvKIxo
  14 +j4XHXiTAYBCqa2RPJbvy6opmTmDe6G+i+AJIYhuNSyZlxUSdq5oju+idf25cO9ac
  15 +8G1XJXzca/QsQI4WBtPW5u6msXCPiwSkaNE5swGagtftU89uV++NFSyh9wOFe0LM
  16 +06QwgDvxugPkSfWRVdPVJ3W0AwKBgQDRhaVY3C6LfMsPcS7sAAZUtOv/QgY4kUuQ
  17 +D6zzlmFaAGKxS6e+pgMmF1ePDN/gxQlkRrm6eaNqNp//AZiokBMfU3wxoWGhPvwF
  18 +TWEr8FkhDkZjxerlmu3yvU7u4dPgcjkxn+/xoyAok+RTJ7ZQiOidZ9HBie/s+dpn
  19 +WBUpIJZJZwKBgQDTuCd241cqS9MWMnYIDbj1OELarALOTs1j+EYrtLlJehq9Ljun
  20 +6w3lwoG5h7sdwhRnzHKf3CzwiIwraSzYSzG3E/LWYuOGuLWojccn5EHWj5a4ZvXp
  21 +LH0ixhTsxiV4HlRki7wzaSaQ5hreUD71Wr0eeHpz8Qrc3skbEHmsovE0TwKBgEOQ
  22 +w4dqSaA1uA3cw/La9y+twETqd7MaN7HXPEC5B9/MLAdcjFWQl4dbE6NP1YpYP/UU
  23 +UaRuwWHpB6NmUmJZQ7FgbLZcgm1LMF5NW76dnUBpqA1Yr60nTwBuR9wE+xxv+T2l
  24 +g+z6W856ErE2JzbFT64i4qN05egXknStFPXUSmKVAoGAXxwyS7upnIupcJdq21EU
  25 +t8Ay6BYf75aanncBi/TabGV67ZUVN1p2sli5a0OcQWpktBGxApC5O38GCkLMbSIX
  26 +tEiutWLXXUjmM+m+xq5wgCw6TFe7yg4r/QBa4QEh9UPIBjC8hDknCjeEhWPU2Htd
  27 +6aFPkcssGWh2mF6t3kZhAuU=
  28 +-----END PRIVATE KEY-----
  1 +<?php
  2 +
  3 +return array (
  4 + 0 =>
  5 + array (
  6 + 'name' => 'app_id',
  7 + 'title' => 'app_id',
  8 + 'type' => 'string',
  9 + 'content' =>
  10 + array (
  11 + ),
  12 + 'value' => 'wxb345558fbe7123f4',
  13 + 'rule' => 'required',
  14 + 'msg' => '',
  15 + 'tip' => '你的微信公众号appid',
  16 + 'ok' => '',
  17 + 'extend' => '',
  18 + ),
  19 + 1 =>
  20 + array (
  21 + 'name' => 'secret',
  22 + 'title' => 'secret',
  23 + 'type' => 'string',
  24 + 'content' =>
  25 + array (
  26 + ),
  27 + 'value' => '7f04ffc23a080d581542e2be5c71486e',
  28 + 'rule' => 'required',
  29 + 'msg' => '',
  30 + 'tip' => '你的微信公众号appsecret',
  31 + 'ok' => '',
  32 + 'extend' => '',
  33 + ),
  34 + 2 =>
  35 + array (
  36 + 'name' => 'token',
  37 + 'title' => 'token',
  38 + 'type' => 'string',
  39 + 'content' =>
  40 + array (
  41 + ),
  42 + 'value' => 'your token',
  43 + 'rule' => 'required',
  44 + 'msg' => '',
  45 + 'tip' => '通信token',
  46 + 'ok' => '',
  47 + 'extend' => '',
  48 + ),
  49 + 3 =>
  50 + array (
  51 + 'name' => 'aes_key',
  52 + 'title' => 'aes_key',
  53 + 'type' => 'string',
  54 + 'content' =>
  55 + array (
  56 + ),
  57 + 'value' => 'your aes_key',
  58 + 'rule' => '',
  59 + 'msg' => '',
  60 + 'tip' => '',
  61 + 'ok' => '',
  62 + 'extend' => '',
  63 + ),
  64 + 4 =>
  65 + array (
  66 + 'name' => 'debug',
  67 + 'title' => '调试模式',
  68 + 'type' => 'radio',
  69 + 'content' =>
  70 + array (
  71 + 0 => '否',
  72 + 1 => '是',
  73 + ),
  74 + 'value' => '1',
  75 + 'rule' => 'required',
  76 + 'msg' => '',
  77 + 'tip' => '',
  78 + 'ok' => '',
  79 + 'extend' => '',
  80 + ),
  81 + 5 =>
  82 + array (
  83 + 'name' => 'log_level',
  84 + 'title' => '日志记录等级',
  85 + 'type' => 'select',
  86 + 'content' =>
  87 + array (
  88 + 'debug' => 'debug',
  89 + 'info' => 'info',
  90 + 'notice' => 'notice',
  91 + 'warning' => 'warning',
  92 + 'error' => 'error',
  93 + 'critical' => 'critical',
  94 + 'alert' => 'alert',
  95 + 'emergency' => 'emergency',
  96 + ),
  97 + 'value' => 'debug',
  98 + 'rule' => 'required',
  99 + 'msg' => '',
  100 + 'tip' => '',
  101 + 'ok' => '',
  102 + 'extend' => '',
  103 + ),
  104 + 6 =>
  105 + array (
  106 + 'name' => 'oauth_callback',
  107 + 'title' => '登录回调',
  108 + 'type' => 'string',
  109 + 'content' =>
  110 + array (
  111 + ),
  112 + 'value' => 'http://www.yoursite.com/addons/wechat/index/callback',
  113 + 'rule' => 'required',
  114 + 'msg' => '',
  115 + 'tip' => '',
  116 + 'ok' => '',
  117 + 'extend' => '',
  118 + ),
  119 +);
  1 +<?php
  2 +
  3 +namespace addons\wechat\controller;
  4 +
  5 +use addons\wechat\library\Wechat;
  6 +use addons\wechat\model\WechatCaptcha;
  7 +use fast\Http;
  8 +
  9 +/**
  10 + * 微信验证码验证接口
  11 + */
  12 +class Captcha extends \think\addons\Controller
  13 +{
  14 +
  15 + public function _initialize()
  16 + {
  17 + parent::_initialize();
  18 + }
  19 +
  20 + /**
  21 + * 验证码检测接口
  22 + */
  23 + public function check()
  24 + {
  25 + $captcha = $this->request->post("captcha");
  26 + $event = $this->request->post("event");
  27 + $result = WechatCaptcha::check($captcha, $event);
  28 + if ($result) {
  29 + $this->success("验证码正确");
  30 + } else {
  31 + $this->error("验证码错误");
  32 + }
  33 + }
  34 +
  35 + /**
  36 + * 验证码发送接口
  37 + */
  38 + public function send()
  39 + {
  40 + $ip = $this->request->ip();
  41 + $event = $this->request->post("event");
  42 + if (!$event) {
  43 + $this->error("参数错误");
  44 + }
  45 + $captch = WechatCaptcha::where('ip', $ip)
  46 + ->where('event', $event)
  47 + ->whereTime('createtime', '-2 minutes')
  48 + ->find();
  49 + if ($captch) {
  50 + $this->error("获取频繁,请稍后重试");
  51 + }
  52 + $token = Wechat::getAccessToken();
  53 + if (!$token) {
  54 + $this->error("发送失败,请稍后重试");
  55 + }
  56 + $url = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token={$token}";
  57 + $params = [
  58 + 'expire_seconds' => 120,
  59 + 'action_name' => 'QR_STR_SCENE',
  60 + 'action_info' => [
  61 + 'scene' => [
  62 + 'scene_str' => "captcha_" . $event . "_" . $ip,
  63 + ]
  64 + ],
  65 +
  66 + ];
  67 + $result = Http::sendRequest($url, json_encode($params));
  68 + if ($result['ret']) {
  69 + $msg = (array)json_decode($result['msg'], true);
  70 + if (isset($msg['ticket']) && isset($msg['url'])) {
  71 + $this->success("", null, ['image' => "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=" . urlencode($msg['ticket']), 'url' => $msg['url']]);
  72 + }
  73 + }
  74 + $this->error("获取失败!请稍后重试");
  75 + }
  76 +
  77 +}
  1 +<?php
  2 +
  3 +namespace addons\wechat\controller;
  4 +
  5 +use addons\wechat\model\WechatAutoreply;
  6 +use addons\wechat\model\WechatCaptcha;
  7 +use addons\wechat\model\WechatContext;
  8 +use addons\wechat\model\WechatResponse;
  9 +use addons\wechat\model\WechatConfig;
  10 +
  11 +use EasyWeChat\Foundation\Application;
  12 +use EasyWeChat\Payment\Order;
  13 +use addons\wechat\library\Wechat as WechatService;
  14 +use addons\wechat\library\Config as ConfigService;
  15 +use think\Log;
  16 +
  17 +/**
  18 + * 微信接口
  19 + */
  20 +class Index extends \think\addons\Controller
  21 +{
  22 +
  23 + public $app = null;
  24 +
  25 + public function _initialize()
  26 + {
  27 + parent::_initialize();
  28 + $this->app = new Application(ConfigService::load());
  29 + }
  30 +
  31 + /**
  32 + *
  33 + */
  34 + public function index()
  35 + {
  36 + $this->error("当前插件暂无前台页面");
  37 + }
  38 +
  39 + /**
  40 + * 微信API对接接口
  41 + */
  42 + public function api()
  43 + {
  44 + $this->app->server->setMessageHandler(function ($message) {
  45 +
  46 + $wechatService = new WechatService;
  47 +
  48 + $matches = null;
  49 + $openid = $message->FromUserName;
  50 + $to_openid = $message->ToUserName;
  51 + $event = $message->Event;
  52 + $eventkey = $message->EventKey ? $message->EventKey : $message->Event;
  53 +
  54 + $unknownMessage = WechatConfig::value('default.unknown.message');
  55 + $unknownMessage = $unknownMessage ? $unknownMessage : "";
  56 +
  57 + switch ($message->MsgType) {
  58 + case 'event': //事件消息
  59 + //验证码消息
  60 + if (in_array($event, ['subscribe', 'SCAN']) && preg_match("/^captcha_([a-zA-Z0-9]+)_([0-9\.]+)/", $eventkey, $matches)) {
  61 + return WechatCaptcha::send($openid, $matches[1], $matches[2]);
  62 + }
  63 + switch ($event) {
  64 + case 'subscribe'://添加关注
  65 + $subscribeMessage = WechatConfig::value('default.subscribe.message');
  66 + $subscribeMessage = $subscribeMessage ? $subscribeMessage : "欢迎关注我们!";
  67 + return $subscribeMessage;
  68 + case 'unsubscribe'://取消关注
  69 + return '';
  70 + case 'LOCATION'://获取地理位置
  71 + return '';
  72 + case 'VIEW': //跳转链接,eventkey为链接
  73 + return '';
  74 + case 'SCAN': //扫码
  75 + return '';
  76 + default:
  77 + break;
  78 + }
  79 +
  80 + $wechatResponse = WechatResponse::where(["eventkey" => $eventkey, 'status' => 'normal'])->find();
  81 + if ($wechatResponse) {
  82 + $responseContent = (array)json_decode($wechatResponse['content'], true);
  83 + $wechatContext = WechatContext::where(['openid' => $openid])->order('id', 'desc')->find();
  84 + $data = ['eventkey' => $eventkey, 'command' => '', 'refreshtime' => time(), 'openid' => $openid];
  85 + if ($wechatContext) {
  86 + $wechatContext->save($data);
  87 + } else {
  88 + $wechatContext = WechatContext::create($data, true);
  89 + }
  90 + $result = $wechatService->response($this, $openid, '', $responseContent, $wechatContext);
  91 + if ($result) {
  92 + return $result;
  93 + }
  94 + }
  95 + return $unknownMessage;
  96 + case 'text': //文字消息
  97 + case 'image': //图片消息
  98 + case 'voice': //语音消息
  99 + case 'video': //视频消息
  100 + case 'location': //坐标消息
  101 + case 'link': //链接消息
  102 + default: //其它消息
  103 + //自动回复处理
  104 + if ($message->MsgType == 'text') {
  105 + $autoreply = null;
  106 + $autoreplyList = WechatAutoreply::where('status', 'normal')->cache(true)->order('weigh DESC,id DESC')->select();
  107 + foreach ($autoreplyList as $index => $item) {
  108 + //完全匹配和正则匹配
  109 + if ($item['text'] == $message->Content || (in_array(mb_substr($item['text'], 0, 1), ['#', '~', '/']) && preg_match($item['text'], $message->Content, $matches))) {
  110 + $autoreply = $item;
  111 + break;
  112 + }
  113 + }
  114 + if ($autoreply) {
  115 + $wechatResponse = WechatResponse::where(["eventkey" => $autoreply['eventkey'], 'status' => 'normal'])->find();
  116 + if ($wechatResponse) {
  117 + $responseContent = (array)json_decode($wechatResponse['content'], true);
  118 + $wechatContext = WechatContext::where(['openid' => $openid])->order('id', 'desc')->find();
  119 + $result = $wechatService->response($this, $openid, $message->Content, $responseContent, $wechatContext, $matches);
  120 + if ($result) {
  121 + return $result;
  122 + }
  123 + }
  124 + }
  125 + }
  126 + return $unknownMessage;
  127 + }
  128 + return ""; //SUCCESS
  129 + });
  130 + $response = $this->app->server->serve();
  131 + // 将响应输出
  132 + $response->send();
  133 + }
  134 +
  135 + /**
  136 + * 登录回调
  137 + */
  138 + public function callback()
  139 + {
  140 +
  141 + }
  142 +
  143 + /**
  144 + * 支付回调
  145 + */
  146 + public function notify()
  147 + {
  148 + Log::record(file_get_contents('php://input'), "notify");
  149 + $response = $this->app->payment->handleNotify(function ($notify, $successful) {
  150 + // 使用通知里的 "微信支付订单号" 或者 "商户订单号" 去自己的数据库找到订单
  151 + $orderinfo = Order::getByTransactionId($notify->transaction_id);
  152 + if ($orderinfo) {
  153 + //订单已处理
  154 + return true;
  155 + }
  156 + $orderinfo = Order::get($notify->out_trade_no);
  157 + if (!$orderinfo) { // 如果订单不存在
  158 + return 'Order not exist.'; // 告诉微信,我已经处理完了,订单没找到,别再通知我了
  159 + }
  160 + // 如果订单存在
  161 + // 检查订单是否已经更新过支付状态,已经支付成功了就不再更新了
  162 + if ($orderinfo['paytime']) {
  163 + return true;
  164 + }
  165 + // 用户是否支付成功
  166 + if ($successful) {
  167 + // 请在这里编写处理成功的处理逻辑
  168 +
  169 + return true; // 返回处理完成
  170 + } else { // 用户支付失败
  171 + return true;
  172 + }
  173 + });
  174 +
  175 + $response->send();
  176 + }
  177 +
  178 +}
  1 +name = wechat
  2 +title = 微信管理
  3 +intro = 微信管理插件
  4 +author = Karson
  5 +website = https://www.fastadmin.net
  6 +version = 1.1.0
  7 +state = 1
  8 +url = /addons/wechat.html
  1 +
  2 +CREATE TABLE IF NOT EXISTS `__PREFIX__wechat_autoreply` (
  3 + `id` int(10) NOT NULL AUTO_INCREMENT,
  4 + `title` varchar(100) NOT NULL DEFAULT '' COMMENT '标题',
  5 + `text` varchar(100) NOT NULL DEFAULT '' COMMENT '触发文本',
  6 + `eventkey` varchar(50) NOT NULL DEFAULT '' COMMENT '响应事件',
  7 + `remark` varchar(255) NOT NULL DEFAULT '' COMMENT '备注',
  8 + `weigh` int(10) NOT NULL DEFAULT '0' COMMENT '权重',
  9 + `createtime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '添加时间',
  10 + `updatetime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间',
  11 + `status` varchar(30) NOT NULL DEFAULT '' COMMENT '状态',
  12 + PRIMARY KEY (`id`),
  13 + KEY `eventkey` (`eventkey`)
  14 +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COMMENT='微信自动回复表';
  15 +
  16 +BEGIN;
  17 +INSERT INTO `__PREFIX__wechat_autoreply`(`id`, `title`, `text`, `eventkey`, `remark`, `weigh`, `createtime`, `updatetime`, `status`) VALUES (1, '输入hello', 'hello', '58c7d908c4570', '', 1, 1493366855, 1493366855, 'normal');
  18 +INSERT INTO `__PREFIX__wechat_autoreply`(`id`, `title`, `text`, `eventkey`, `remark`, `weigh`, `createtime`, `updatetime`, `status`) VALUES (2, '输入你好', '你好', '58fdfaa9e1965', '', 2, 1493704976, 1493704976, 'normal');
  19 +COMMIT;
  20 +
  21 +CREATE TABLE IF NOT EXISTS `__PREFIX__wechat_captcha` (
  22 + `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  23 + `event` varchar(100) DEFAULT '' COMMENT '事件',
  24 + `openid` varchar(255) DEFAULT NULL COMMENT '用户openid',
  25 + `context` varchar(20) DEFAULT NULL COMMENT '上下文',
  26 + `code` varchar(30) DEFAULT NULL COMMENT '验证码',
  27 + `times` int(10) unsigned DEFAULT '0' COMMENT '验证次数',
  28 + `ip` varchar(50) DEFAULT '' COMMENT 'IP',
  29 + `createtime` int(10) DEFAULT NULL COMMENT '创建时间',
  30 + PRIMARY KEY (`id`),
  31 + KEY `ip` (`ip`,`event`) USING BTREE,
  32 + KEY `openid` (`openid`(191),`event`)
  33 +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='微信公众号验证码';
  34 +
  35 +CREATE TABLE IF NOT EXISTS `__PREFIX__wechat_config` (
  36 + `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  37 + `name` varchar(50) NOT NULL DEFAULT '' COMMENT '配置名称',
  38 + `title` varchar(50) NOT NULL DEFAULT '' COMMENT '配置标题',
  39 + `value` text NOT NULL COMMENT '配置值',
  40 + `createtime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
  41 + `updatetime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间',
  42 + PRIMARY KEY (`id`),
  43 + UNIQUE KEY `name` (`name`) USING BTREE
  44 +) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COMMENT='微信配置表';
  45 +
  46 +BEGIN;
  47 +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');
  48 +COMMIT;
  49 +
  50 +CREATE TABLE IF NOT EXISTS `__PREFIX__wechat_context` (
  51 + `id` int(10) NOT NULL AUTO_INCREMENT,
  52 + `openid` varchar(64) NOT NULL DEFAULT '',
  53 + `type` varchar(30) NOT NULL DEFAULT '' COMMENT '类型',
  54 + `eventkey` varchar(64) NOT NULL DEFAULT '',
  55 + `command` varchar(64) NOT NULL DEFAULT '',
  56 + `message` varchar(255) NOT NULL DEFAULT '' COMMENT '内容',
  57 + `refreshtime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最后刷新时间',
  58 + `createtime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
  59 + `updatetime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间',
  60 + PRIMARY KEY (`id`),
  61 + KEY `openid` (`openid`,`eventkey`) USING BTREE
  62 +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='微信上下文表';
  63 +
  64 +CREATE TABLE IF NOT EXISTS `__PREFIX__wechat_response` (
  65 + `id` int(10) NOT NULL AUTO_INCREMENT,
  66 + `title` varchar(100) NOT NULL DEFAULT '' COMMENT '资源名',
  67 + `eventkey` varchar(128) NOT NULL DEFAULT '' COMMENT '事件',
  68 + `type` enum('text','image','news','voice','video','music','link','app') NOT NULL DEFAULT 'text' COMMENT '类型',
  69 + `content` text NOT NULL COMMENT '内容',
  70 + `remark` varchar(255) NOT NULL DEFAULT '' COMMENT '备注',
  71 + `createtime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
  72 + `updatetime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间',
  73 + `status` varchar(30) NOT NULL DEFAULT '' COMMENT '状态',
  74 + PRIMARY KEY (`id`),
  75 + UNIQUE KEY `eventkey` (`eventkey`) USING BTREE
  76 +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COMMENT='微信资源表';
  77 +
  78 +BEGIN;
  79 +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');
  80 +COMMIT;
  81 +
  1 +<?php
  2 +
  3 +namespace addons\wechat\library;
  4 +
  5 +/**
  6 + * 微信配置类
  7 + */
  8 +class Config
  9 +{
  10 +
  11 + public function __construct()
  12 + {
  13 +
  14 + }
  15 +
  16 + public static function load()
  17 + {
  18 + $config = get_addon_config('wechat');
  19 +
  20 + return [
  21 + /**
  22 + * Debug 模式,bool 值:true/false
  23 + *
  24 + * 当值为 false 时,所有的日志都不会记录
  25 + */
  26 + 'debug' => !!$config['debug'],
  27 + /**
  28 + * 账号基本信息,请从微信公众平台/开放平台获取
  29 + */
  30 + 'app_id' => $config['app_id'], // AppID
  31 + 'secret' => $config['secret'], // AppSecret
  32 + 'token' => $config['token'], // Token
  33 + 'aes_key' => $config['aes_key'], // EncodingAESKey,安全模式下请一定要填写!!!
  34 + /**
  35 + * 日志配置
  36 + *
  37 + * level: 日志级别, 可选为:
  38 + * debug/info/notice/warning/error/critical/alert/emergency
  39 + * permission:日志文件权限(可选),默认为null(若为null值,monolog会取0644)
  40 + * file:日志文件位置(绝对路径!!!),要求可写权限
  41 + */
  42 + 'log' => [
  43 + 'level' => $config['log_level'],
  44 + 'permission' => 0777,
  45 + 'file' => ROOT_PATH . '/runtime/log/easywechat.log',
  46 + ],
  47 + /**
  48 + * OAuth 配置
  49 + *
  50 + * scopes:公众平台(snsapi_userinfo / snsapi_base),开放平台:snsapi_login
  51 + * callback:OAuth授权完成后的回调页地址
  52 + */
  53 + 'oauth' => [
  54 + 'scopes' => ['snsapi_userinfo'],
  55 + 'callback' => $config['oauth_callback'],
  56 + ],
  57 + /**
  58 + * 微信支付
  59 + */
  60 + 'payment' => [
  61 + 'merchant_id' => '1601984106',
  62 + 'key' => 'b6iGEk2ZPhj8ElHuWX0slbxojCVSn8oN',
  63 + 'cert_path' => ROOT_PATH.'/addons/wechat/cert/apiclient_cert.pem', // XXX: 绝对路径!!!!
  64 + 'key_path' => ROOT_PATH.'/addons/wechat/cert/apiclient_key.pem', // XXX: 绝对路径!!!!
  65 + // 'device_info' => '013467007045764',
  66 + // 'sub_app_id' => '',
  67 + // 'sub_merchant_id' => '',
  68 + // ...
  69 + ],
  70 + /**
  71 + * Guzzle 全局设置
  72 + *
  73 + * 更多请参考: http://docs.guzzlephp.org/en/latest/request-options.html
  74 + */
  75 + 'guzzle' => [
  76 + 'timeout' => 3.0, // 超时时间(秒)
  77 + //'verify' => false, // 关掉 SSL 认证(强烈不建议!!!)
  78 + ],
  79 + ];
  80 + }
  81 +
  82 +}
  1 +<?php
  2 +
  3 +namespace addons\wechat\library;
  4 +
  5 +use addons\signin\model\Signin;
  6 +use addons\third\model\Third;
  7 +use app\common\model\User;
  8 +use EasyWeChat\Message\News;
  9 +use fast\Date;
  10 +use fast\Http;
  11 +use fast\Random;
  12 +use think\Cache;
  13 +use think\Config;
  14 +
  15 +/**
  16 + * 微信服务类
  17 + */
  18 +class Wechat
  19 +{
  20 + public static function appConfig()
  21 + {
  22 + return array(
  23 + 'signin' => array(
  24 + 'name' => '签到送积分',
  25 + 'config' => array()
  26 + ),
  27 + 'blog' => array(
  28 + 'name' => '关联博客',
  29 + 'config' => array(
  30 + array(
  31 + 'type' => 'text',
  32 + 'caption' => '日志ID',
  33 + 'field' => 'post_id',
  34 + 'rule' => '',
  35 + 'extend' => 'class="form-control selectpage" data-source="blog/post/index" data-field="title"',
  36 + 'options' => '',
  37 + ),
  38 + array(
  39 + 'type' => 'radio',
  40 + 'caption' => '开启搜索日志',
  41 + 'field' => 'searchpost',
  42 + 'rule' => '',
  43 + 'extend' => '',
  44 + 'options' => [
  45 + '1' => '是',
  46 + '0' => '否',
  47 + ],
  48 + ),
  49 + array(
  50 + 'type' => 'text',
  51 + 'caption' => '正则搜索匹配索引',
  52 + 'field' => 'searchregexindex',
  53 + 'rule' => '',
  54 + 'defaultvalue' => '1',
  55 + 'extend' => '',
  56 + 'options' => [],
  57 + )
  58 + )
  59 + ),
  60 + 'cms' => array(
  61 + 'name' => '关联CMS',
  62 + 'config' => array(
  63 + array(
  64 + 'type' => 'text',
  65 + 'caption' => '文章ID',
  66 + 'field' => 'archives_id',
  67 + 'rule' => '',
  68 + 'extend' => 'class="form-control selectpage" data-source="cms/archives/index" data-field="title"',
  69 + 'options' => ''
  70 + ),
  71 + array(
  72 + 'type' => 'text',
  73 + 'caption' => '单页ID',
  74 + 'field' => 'page_id',
  75 + 'rule' => '',
  76 + 'extend' => 'class="form-control selectpage" data-source="cms/page/index" data-field="title"',
  77 + 'options' => ''
  78 + ),
  79 + array(
  80 + 'type' => 'text',
  81 + 'caption' => '专题ID',
  82 + 'field' => 'special_id',
  83 + 'rule' => '',
  84 + 'extend' => 'class="form-control selectpage" data-source="cms/special/index" data-field="title"',
  85 + 'options' => ''
  86 + ),
  87 + array(
  88 + 'type' => 'radio',
  89 + 'caption' => '开启搜索文章',
  90 + 'field' => 'searcharchives',
  91 + 'rule' => '',
  92 + 'extend' => '',
  93 + 'options' => [
  94 + '1' => '是',
  95 + '0' => '否',
  96 + ],
  97 + ),
  98 + array(
  99 + 'type' => 'radio',
  100 + 'caption' => '开启搜索单页',
  101 + 'field' => 'searchpage',
  102 + 'rule' => '',
  103 + 'extend' => '',
  104 + 'options' => [
  105 + '1' => '是',
  106 + '0' => '否',
  107 + ],
  108 + ),
  109 + array(
  110 + 'type' => 'radio',
  111 + 'caption' => '开启搜索专题',
  112 + 'field' => 'searchspecial',
  113 + 'rule' => '',
  114 + 'extend' => '',
  115 + 'options' => [
  116 + '1' => '是',
  117 + '0' => '否',
  118 + ],
  119 + ),
  120 + array(
  121 + 'type' => 'text',
  122 + 'caption' => '正则搜索匹配索引',
  123 + 'field' => 'searchregexindex',
  124 + 'rule' => '',
  125 + 'defaultvalue' => '1',
  126 + 'extend' => '',
  127 + 'options' => [],
  128 + )
  129 + )
  130 + ),
  131 + 'ask' => array(
  132 + 'name' => '关联问答',
  133 + 'config' => array(
  134 + array(
  135 + 'type' => 'text',
  136 + 'caption' => '问题ID',
  137 + 'field' => 'question_id',
  138 + 'extend' => 'class="form-control selectpage" data-source="ask/question/index" data-field="title"',
  139 + 'options' => ''
  140 + ),
  141 + array(
  142 + 'type' => 'text',
  143 + 'caption' => '文章ID',
  144 + 'field' => 'article_id',
  145 + 'extend' => 'class="form-control selectpage" data-source="ask/article/index" data-field="title"',
  146 + 'options' => ''
  147 + ),
  148 + array(
  149 + 'type' => 'radio',
  150 + 'caption' => '开启搜索问题',
  151 + 'field' => 'searchquestion',
  152 + 'rule' => '',
  153 + 'extend' => '',
  154 + 'options' => [
  155 + '1' => '是',
  156 + '0' => '否',
  157 + ],
  158 + ),
  159 + array(
  160 + 'type' => 'radio',
  161 + 'caption' => '开启搜索文章',
  162 + 'field' => 'searcharticle',
  163 + 'rule' => '',
  164 + 'extend' => '',
  165 + 'options' => [
  166 + '1' => '是',
  167 + '0' => '否',
  168 + ],
  169 + ),
  170 + array(
  171 + 'type' => 'text',
  172 + 'caption' => '正则搜索匹配索引',
  173 + 'field' => 'searchregexindex',
  174 + 'rule' => '',
  175 + 'defaultvalue' => '1',
  176 + 'extend' => '',
  177 + 'options' => [],
  178 + )
  179 + )
  180 + ),
  181 + 'vote' => array(
  182 + 'name' => '关联投票',
  183 + 'config' => array(
  184 + array(
  185 + 'type' => 'text',
  186 + 'caption' => '投票主题ID',
  187 + 'field' => 'subject_id',
  188 + 'extend' => 'class="form-control selectpage" data-source="vote/subject/index" data-field="title"',
  189 + 'rule' => '',
  190 + 'options' => ''
  191 + ),
  192 + array(
  193 + 'type' => 'text',
  194 + 'caption' => '参赛人员ID',
  195 + 'field' => 'player_id',
  196 + 'rule' => '',
  197 + 'extend' => 'class="form-control selectpage" data-source="vote/player/index" data-field="nickname"',
  198 + 'options' => ''
  199 + ),
  200 + array(
  201 + 'type' => 'radio',
  202 + 'caption' => '开启搜索主题',
  203 + 'field' => 'searchsubject',
  204 + 'rule' => '',
  205 + 'extend' => '',
  206 + 'options' => [
  207 + '1' => '是',
  208 + '0' => '否',
  209 + ],
  210 + ),
  211 + array(
  212 + 'type' => 'radio',
  213 + 'caption' => '开启搜索参赛人员',
  214 + 'field' => 'searchplayer',
  215 + 'rule' => '',
  216 + 'extend' => '',
  217 + 'options' => [
  218 + '1' => '是',
  219 + '0' => '否',
  220 + ],
  221 + ),
  222 + array(
  223 + 'type' => 'text',
  224 + 'caption' => '正则搜索匹配索引',
  225 + 'field' => 'searchregexindex',
  226 + 'rule' => '',
  227 + 'defaultvalue' => '1',
  228 + 'extend' => '',
  229 + 'options' => [],
  230 + )
  231 + )
  232 + ),
  233 + );
  234 + }
  235 +
  236 + /**
  237 + * 应用交互
  238 + * @return array|bool|mixed|string
  239 + */
  240 + public function response($obj, $openid, $message, $content, $context, $matches = null)
  241 + {
  242 + $response = false;
  243 + if (isset($content['app'])) {
  244 + $entry = null;
  245 + $keyword = isset($content['searchregexindex']) && $content['searchregexindex'] > -1 && $matches && isset($matches[$content['searchregexindex']])
  246 + ? $matches[$content['searchregexindex']] : $message;
  247 + switch ($content['app']) {
  248 + case 'signin':
  249 + $signinInfo = get_addon_info('signin');
  250 + if (!$signinInfo || !$signinInfo['state']) {
  251 + return "请先在后台管理安装并启用《会员签到》插件";
  252 + }
  253 + $thirdInfo = get_addon_info('third');
  254 + if (!$thirdInfo || !$thirdInfo['state']) {
  255 + return "请先在后台管理安装并启用《第三方登录》插件";
  256 + }
  257 + $user = self::getUserByOpenid($openid);
  258 + if (!$user) {
  259 + return "请先在会员中心绑定微信登录,<a href='" . addon_url('third/index/connect', [':platform' => 'wechat'], true, true) . "'>点击这里绑定</a>";
  260 + }
  261 + $config = get_addon_config('signin');
  262 + $signdata = $config['signinscore'];
  263 + $lastdata = Signin::where('user_id', $user->id)->order('id', 'desc')->find();
  264 + $successions = $lastdata && $lastdata['createtime'] > Date::unixtime('day', -1) ? $lastdata['successions'] : 0;
  265 + $signin = Signin::where('user_id', $user->id)->whereTime('createtime', 'today')->find();
  266 + if ($signin) {
  267 + return '今天已签到,请明天再来!';
  268 + } else {
  269 + $successions++;
  270 + Signin::create(['user_id' => $user->id, 'successions' => $successions, 'createtime' => time()]);
  271 + $score = isset($signdata['s' . $successions]) ? $signdata['s' . $successions] : $signdata['sn'];
  272 +
  273 + $user->setInc('score', $score);
  274 + User::score($score, $user->id, "连续签到{$successions}天");
  275 + return '签到成功!连续签到' . $successions . '天!获得' . $score . '积分,';
  276 + }
  277 + break;
  278 + case 'blog':
  279 + $blogInfo = get_addon_info('blog');
  280 + if (!$blogInfo || !$blogInfo['state']) {
  281 + return "请先在后台管理安装并启用《简单博客》插件";
  282 + }
  283 + $entry = \addons\blog\model\Post::get($content['post_id']);
  284 + if ($entry) {
  285 + $entry['image'] = $entry['thumb'];
  286 + }
  287 + if (!$entry && $content['searchpost']) {
  288 + $entry = \addons\blog\model\Post::where("title|description", 'like', "%{$keyword}%")->where('status', 'normal')->find();
  289 + }
  290 + if (!$entry) {
  291 + return "未搜索到任何匹配信息$keyword" . json_encode($matches);
  292 + }
  293 + break;
  294 + case 'cms':
  295 + $cmsInfo = get_addon_info('cms');
  296 + if (!$cmsInfo || !$cmsInfo['state']) {
  297 + return "请先在后台管理安装并启用《CMS内容管理系统》插件";
  298 + }
  299 + if (isset($content['archives_id']) && $content['archives_id']) {
  300 + $entry = \addons\cms\model\Archives::get($content['archives_id']);
  301 + } elseif (isset($content['page_id']) && $content['page_id']) {
  302 + $entry = \addons\cms\model\Page::get($content['page_id']);
  303 + } elseif (isset($content['special_id']) && $content['special_id']) {
  304 + $entry = \addons\cms\model\Special::get($content['special_id']);
  305 + }
  306 + if (!$entry && $content['searcharchives']) {
  307 + $entry = \addons\cms\model\Archives::where("title|description", 'like', "%{$keyword}%")->where('status', 'normal')->find();
  308 + }
  309 + if (!$entry && $content['searchpage']) {
  310 + $entry = \addons\cms\model\Page::where("title|description", 'like', "%{$keyword}%")->where('status', 'normal')->find();
  311 + }
  312 + if (!$entry && $content['searchspecial']) {
  313 + $entry = \addons\cms\model\Special::where("title|description", 'like', "%{$keyword}%")->where('status', 'normal')->find();
  314 + }
  315 + if (!$entry) {
  316 + return "未搜索到任何匹配信息";
  317 + }
  318 + break;
  319 + case 'ask':
  320 + $blogInfo = get_addon_info('ask');
  321 + if (!$blogInfo || !$blogInfo['state']) {
  322 + return "请先在后台管理安装并启用《知识付费问答》插件";
  323 + }
  324 +
  325 + if (isset($content['question_id']) && $content['question_id']) {
  326 + $entry = \addons\ask\model\Question::get($content['question_id']);
  327 + } elseif (isset($content['article_id']) && $content['article_id']) {
  328 + $entry = \addons\ask\model\Article::get($content['article_id']);
  329 + }
  330 +
  331 + if (!$entry && $content['searchquestion']) {
  332 + $entry = \addons\ask\model\Question::where("title", 'like', "%{$keyword}%")->where('status', 'normal')->find();
  333 + }
  334 + if (!$entry && $content['searcharticle']) {
  335 + $entry = \addons\ask\model\Article::where("title|description", 'like', "%{$keyword}%")->where('status', 'normal')->find();
  336 + }
  337 + if (!$entry) {
  338 + return "未搜索到任何匹配信息";
  339 + }
  340 + break;
  341 + case 'vote':
  342 + $blogInfo = get_addon_info('vote');
  343 + if (!$blogInfo || !$blogInfo['state']) {
  344 + return "请先在后台管理安装并启用《在线投票系统》插件";
  345 + }
  346 + if (isset($content['subject_id']) && $content['subject_id']) {
  347 + $entry = \addons\vote\model\Subject::all($content['subject_id']);
  348 + } elseif (isset($content['player_id']) && $content['player_id']) {
  349 + $entry = \addons\vote\model\Player::all($content['player_id']);
  350 + }
  351 +
  352 + if (!$entry && $content['searchsubject']) {
  353 + $entry = \addons\vote\model\Subject::where("title|description", 'like', "%{$keyword}%")->where('status', 'normal')->find();
  354 + }
  355 + if (!$entry && $content['searchplayer']) {
  356 + $entry = \addons\vote\model\Player::where("nickname", 'like', "%{$keyword}%")->where('status', 'normal')->find();
  357 + }
  358 +
  359 + if (!$entry) {
  360 + return "未搜索到任何匹配信息";
  361 + }
  362 + break;
  363 + default:
  364 + break;
  365 + }
  366 + if (isset($entry) && $entry) {
  367 + $news = new News();
  368 + $news->title = isset($entry['title']) ? $entry['title'] : (isset($entry['nickname']) ? $entry['nickname'] : '');
  369 + $news->url = $entry['fullurl'];
  370 + $news->image = cdnurl($entry['image'], true);
  371 + $news->description = isset($entry['description']) ? $entry['description'] : '';
  372 + $response[] = $news;
  373 + }
  374 + } else {
  375 + $response = isset($content['content']) ? $content['content'] : $response;
  376 + }
  377 + return $response;
  378 + }
  379 +
  380 + /**
  381 + * 获取Token
  382 + */
  383 + public static function getAccessToken()
  384 + {
  385 + $token = Cache::get('wechat_access_token');
  386 + if (!$token) {
  387 + $config = get_addon_config('wechat');
  388 + $params = [
  389 + 'grant_type' => 'client_credential',
  390 + 'appid' => $config['app_id'],
  391 + 'secret' => $config['secret'],
  392 + ];
  393 + $url = "https://api.weixin.qq.com/cgi-bin/token";
  394 + $result = Http::sendRequest($url, $params, 'GET');
  395 + if ($result['ret']) {
  396 + $msg = (array)json_decode($result['msg'], true);
  397 + if (isset($msg['access_token'])) {
  398 + $token = $msg['access_token'];
  399 + Cache::set('wechat_access_token', $token, $msg['expires_in'] - 1);
  400 + }
  401 + }
  402 + }
  403 + return $token;
  404 + }
  405 +
  406 + /**
  407 + * 根据Openid获取用户信息
  408 + * @param string $openid 微信OpenID
  409 + * @return User|null
  410 + */
  411 + public static function getUserByOpenid($openid)
  412 + {
  413 + $third = Third::where('platform', 'wechat')->where('openid', $openid)->find();
  414 + if ($third && $third->user_id) {
  415 + return User::get($third->user_id);
  416 + }
  417 + return null;
  418 + }
  419 +}
  1 +<?php
  2 +
  3 +namespace addons\wechat\model;
  4 +
  5 +use think\Model;
  6 +
  7 +class WechatAutoreply extends Model
  8 +{
  9 +
  10 + // 自动写入时间戳字段
  11 + protected $autoWriteTimestamp = 'int';
  12 + // 定义时间戳字段名
  13 + protected $createTime = 'createtime';
  14 + protected $updateTime = 'updatetime';
  15 +
  16 +}
  1 +<?php
  2 +
  3 +namespace addons\wechat\model;
  4 +
  5 +use fast\Random;
  6 +use think\Model;
  7 +
  8 +class WechatCaptcha extends Model
  9 +{
  10 +
  11 + // 表名
  12 + protected $name = 'wechat_captcha';
  13 + // 自动写入时间戳字段
  14 + protected $autoWriteTimestamp = 'int';
  15 + // 定义时间戳字段名
  16 + protected $createTime = 'createtime';
  17 + protected $updateTime = '';
  18 + // 追加属性
  19 + protected $append = [
  20 + ];
  21 +
  22 + /**
  23 + * 发送验证码
  24 + * @param $openid string 用户OpenID
  25 + * @param $event string 事件
  26 + * @param $ip string IP地址
  27 + * @return string
  28 + */
  29 + public static function send($openid, $event, $ip)
  30 + {
  31 + $captcha = self::where(['openid' => $openid, 'event' => $event])->whereTime('createtime', '-2 minutes')->find();
  32 + if ($captcha) {
  33 + return "验证码发送速度过快,请稍后重试";
  34 + }
  35 + $code = Random::alnum(4);
  36 + $data = [
  37 + 'event' => $event,
  38 + 'openid' => $openid,
  39 + 'code' => $code,
  40 + 'ip' => $ip,
  41 + ];
  42 + self::create($data);
  43 + return "你的验证码是:{$code},2分钟内输入有效";
  44 + }
  45 +
  46 + /**
  47 + * 检测验证码
  48 + * @param $code string 验证码
  49 + * @param $event string 事件
  50 + * @param $ip string IP
  51 + * @return bool
  52 + */
  53 + public static function check($code, $event, $ip = null)
  54 + {
  55 + $ip = is_null($ip) ? request()->ip() : $ip;
  56 + $captcha = self::where(['ip' => $ip, 'event' => $event])->whereTime('createtime', '-2 minutes')->find();
  57 + if ($captcha && $captcha->code == $code && $captcha->times < 10) {
  58 + $captcha->setInc("times");
  59 + return true;
  60 + }
  61 + //验证大于10次或超时
  62 + if ($captcha && ($captcha->times >= 10 || time() - $captcha->createtime > 120)) {
  63 + $captcha->delete();
  64 + }
  65 +
  66 + return false;
  67 + }
  68 +
  69 +}
  1 +<?php
  2 +
  3 +namespace addons\wechat\model;
  4 +
  5 +use think\Model;
  6 +
  7 +class WechatConfig extends Model
  8 +{
  9 +
  10 + // 表名,不含前缀
  11 + public $name = 'wechat_config';
  12 + // 自动写入时间戳字段
  13 + protected $autoWriteTimestamp = 'int';
  14 + // 定义时间戳字段名
  15 + protected $createTime = 'createtime';
  16 + protected $updateTime = 'updatetime';
  17 + // 追加属性
  18 + protected $append = [
  19 + ];
  20 +
  21 + /**
  22 + * 读取指定配置名称的值
  23 + * @param string $name
  24 + * @return string
  25 + */
  26 + public static function value($name)
  27 + {
  28 + $item = self::get(['name' => $name]);
  29 + return $item ? $item->value : '';
  30 + }
  31 +
  32 +}
  1 +<?php
  2 +
  3 +namespace addons\wechat\model;
  4 +
  5 +use think\Model;
  6 +
  7 +class WechatContext extends Model
  8 +{
  9 +
  10 + // 自动写入时间戳字段
  11 + protected $autoWriteTimestamp = 'int';
  12 + // 定义时间戳字段名
  13 + protected $createTime = 'createtime';
  14 + protected $updateTime = 'updatetime';
  15 +
  16 +}
  1 +<?php
  2 +
  3 +namespace addons\wechat\model;
  4 +
  5 +use think\Model;
  6 +
  7 +class WechatResponse extends Model
  8 +{
  9 +
  10 + // 自动写入时间戳字段
  11 + protected $autoWriteTimestamp = 'int';
  12 + // 定义时间戳字段名
  13 + protected $createTime = 'createtime';
  14 + protected $updateTime = 'updatetime';
  15 +
  16 +}
  1 +define(['jquery', 'bootstrap', 'backend', 'form', 'table'], function ($, undefined, Backend, Form, Table) {
  2 +
  3 + var Controller = {
  4 + index: function () {
  5 + // 初始化表格参数配置
  6 + Table.api.init({
  7 + extend: {
  8 + index_url: 'wechat/autoreply/index',
  9 + add_url: 'wechat/autoreply/add',
  10 + edit_url: 'wechat/autoreply/edit',
  11 + del_url: 'wechat/autoreply/del',
  12 + multi_url: 'wechat/autoreply/multi',
  13 + }
  14 + });
  15 +
  16 + var table = $("#table");
  17 +
  18 + // 初始化表格
  19 + table.bootstrapTable({
  20 + url: $.fn.bootstrapTable.defaults.extend.index_url,
  21 + sortName: 'id',
  22 + columns: [
  23 + [
  24 + {field: 'state', checkbox: true, },
  25 + {field: 'id', title: __('Id')},
  26 + {field: 'title', title: __('Title')},
  27 + {field: 'text', title: __('Text')},
  28 + {field: 'eventkey', title: __('Event key')},
  29 + {field: 'remark', title: __('Remark')},
  30 + {field: 'createtime', title: __('Create time'), formatter: Table.api.formatter.datetime, operate: 'RANGE', addclass: 'datetimerange', sortable: true},
  31 + {field: 'updatetime', title: __('Update time'), formatter: Table.api.formatter.datetime, operate: 'RANGE', addclass: 'datetimerange', sortable: true},
  32 + {field: 'status', title: __('Status'), formatter: Table.api.formatter.status},
  33 + {field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, formatter: Table.api.formatter.operate}
  34 + ]
  35 + ]
  36 + });
  37 +
  38 + // 为表格绑定事件
  39 + Table.api.bindevent(table);
  40 +
  41 + },
  42 + add: function () {
  43 + Controller.api.bindevent();
  44 + },
  45 + edit: function () {
  46 + Controller.api.bindevent();
  47 + },
  48 + api: {
  49 + bindevent: function () {
  50 + Form.api.bindevent($("form[role=form]"));
  51 +
  52 + var refreshkey = function (data) {
  53 + $("input[name='row[eventkey]']").val(data.eventkey).trigger("change");
  54 + Layer.closeAll();
  55 + var keytitle = data.title;
  56 + var cont = $(".clickbox .create-click:first");
  57 + $(".keytitle", cont).remove();
  58 + if (keytitle) {
  59 + cont.append('<div class="keytitle">' + __('Event key') + ':' + keytitle + '</div>');
  60 + }
  61 + };
  62 + $(document).on('click', "#select-resources", function () {
  63 + var key = $("input[name='row[eventkey]']").val();
  64 + parent.Backend.api.open($(this).attr("href") + "?key=" + key, __('Select'), {callback: refreshkey});
  65 + return false;
  66 + });
  67 +
  68 + $(document).on('click', "#add-resources", function () {
  69 + parent.Backend.api.open($(this).attr("href") + "?key=", __('Add'), {callback: refreshkey});
  70 + return false;
  71 + });
  72 + }
  73 + }
  74 +
  75 + };
  76 + return Controller;
  77 +});
  1 +define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefined, Backend, Table, Form) {
  2 +
  3 + var Controller = {
  4 + index: function () {
  5 + // 初始化表格参数配置
  6 + Table.api.init({
  7 + extend: {
  8 + index_url: 'wechat/config/index',
  9 + add_url: 'wechat/config/add',
  10 + edit_url: 'wechat/config/edit',
  11 + del_url: 'wechat/config/del',
  12 + multi_url: 'wechat/config/multi',
  13 + table: 'wechat_config',
  14 + }
  15 + });
  16 +
  17 + var table = $("#table");
  18 +
  19 + // 初始化表格
  20 + table.bootstrapTable({
  21 + url: $.fn.bootstrapTable.defaults.extend.index_url,
  22 + pk: 'id',
  23 + sortName: 'id',
  24 + columns: [
  25 + [
  26 + {field: 'state', checkbox: true},
  27 + {field: 'id', title: __('Id')},
  28 + {field: 'name', title: __('Name')},
  29 + {field: 'title', title: __('Title')},
  30 + {field: 'createtime', title: __('Createtime'), formatter: Table.api.formatter.datetime, operate: 'RANGE', addclass: 'datetimerange', sortable: true},
  31 + {field: 'updatetime', title: __('Updatetime'), formatter: Table.api.formatter.datetime, operate: 'RANGE', addclass: 'datetimerange', sortable: true},
  32 + {field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, formatter: Table.api.formatter.operate}
  33 + ]
  34 + ]
  35 + });
  36 +
  37 + // 为表格绑定事件
  38 + Table.api.bindevent(table);
  39 + },
  40 + add: function () {
  41 + Controller.api.bindevent();
  42 + },
  43 + edit: function () {
  44 + Controller.api.bindevent();
  45 + },
  46 + api: {
  47 + bindevent: function () {
  48 + Form.api.bindevent($("form[role=form]"));
  49 + $("input[name='row[type]']:checked").trigger("click");
  50 + }
  51 + }
  52 + };
  53 + return Controller;
  54 +});
  1 +define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'sortable'], function ($, undefined, Backend, Table, Form, Sortable) {
  2 + var Controller = {
  3 + index: function () {
  4 + var typeList = [
  5 + {name: "click", title: "发送消息"},
  6 + {name: "view", title: "跳转网页"},
  7 + {name: "miniprogram", title: "跳转小程序"},
  8 + {name: "scancode_push", title: "扫码推"},
  9 + {name: "scancode_waitmsg", title: "扫码推提示框"},
  10 + {name: "pic_sysphoto", title: "拍照发图"},
  11 + {name: "pic_photo_or_album", title: "拍照相册发图"},
  12 + {name: "pic_weixin", title: "相册发图"},
  13 + {name: "location_select", title: "地理位置选择"},
  14 + ];
  15 + String.prototype.subByte = function (start, bytes) {
  16 + for (var i = start; bytes > 0; i++) {
  17 + var code = this.charCodeAt(i);
  18 + bytes -= code < 256 ? 1 : 2;
  19 + }
  20 + return this.slice(start, i + bytes)
  21 + };
  22 +
  23 + //初始化菜单
  24 + $("#menu-list").prepend(Template("menutpl", {menu: menu}));
  25 + //拖动排序
  26 + new Sortable($("#menu-list")[0], {
  27 + draggable: 'li.menu-item',
  28 + onEnd: function () {
  29 + updateChangeMenu();
  30 + }
  31 + });
  32 + //子菜单拖动排序
  33 + $(".sub-menu-list").each(function () {
  34 + new Sortable(this, {
  35 + draggable: 'li.sub-menu-item',
  36 + onEnd: function () {
  37 + updateChangeMenu();
  38 + }
  39 + });
  40 + });
  41 + //添加主菜单
  42 + $(document).on('click', '#add-item', function () {
  43 + var menu_item_total = $(".menu-item").size();
  44 + if (menu_item_total < 3) {
  45 + 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>';
  46 + var itemDom = $(item);
  47 + itemDom.insertBefore(this);
  48 + itemDom.trigger("click");
  49 + $("#item_title").focus().select();
  50 + $(".sub-menu-box", itemDom).show();
  51 + updateChangeMenu();
  52 + new Sortable($(".sub-menu-list", itemDom)[0], {draggable: 'li.sub-menu-item'});
  53 + }
  54 + });
  55 + //切换类型
  56 + $(document).on('change', 'input[name=type]', function () {
  57 + $(".sub-menu-item.current,.menu-item.current").data("type", $(this).val()).trigger("click");
  58 + });
  59 + //删除菜单
  60 + $(document).on('click', '#item_delete', function () {
  61 + var current = $("#menu-list li.current");
  62 + var prev = current.prev("li[data-type]");
  63 + var next = current.next("li[data-type]");
  64 +
  65 + if (prev.size() == 0 && next.size() == 0 && $(".sub-menu-box", current).size() == 0) {
  66 + last = current.closest(".menu-item");
  67 + } else if (prev.size() > 0 || next.size() > 0) {
  68 + last = prev.size() > 0 ? prev : next;
  69 + } else {
  70 + last = null;
  71 + $(".weixin-content").hide();
  72 + $(".no-weixin-content").show();
  73 + }
  74 + if (current.hasClass("sub-menu-item")) {
  75 + $(".add-sub-item", current.parent()).removeClass("hidden");
  76 + }
  77 + current.remove();
  78 + if (last) {
  79 + last.trigger('click');
  80 + } else {
  81 + $("input[name='name']").val('');
  82 + }
  83 +
  84 + updateChangeMenu();
  85 + });
  86 + //更新修改与变动
  87 + var updateChangeMenu = function () {
  88 + var item = $("#menu-list li.current");
  89 + var values = $("#form-item").serializeArray();
  90 + $.each(values, function (i, j) {
  91 + if (j.name == 'name') {
  92 + $(">a", item).html(j.value);
  93 + }
  94 + item.data(j.name, j.value);
  95 + });
  96 + menuUpdate();
  97 + };
  98 + //更新菜单数据
  99 + var menuUpdate = function () {
  100 + $.post("wechat/menu/edit", {menu: JSON.stringify(getMenuList())}, function (data) {
  101 + if (data['code'] == 1) {
  102 + } else {
  103 + Toastr.error(__('Operation failed'));
  104 + }
  105 + }, 'json');
  106 + };
  107 + //获取菜单数据
  108 + var getMenuList = function () {
  109 + var menus = [];
  110 + var sub_button = [];
  111 + var menu_i = 0;
  112 + var sub_menu_i = 0;
  113 + var item;
  114 + $("#menu-list li").each(function (i) {
  115 + item = $(this);
  116 + var name = item.data('name');
  117 + if (name != null) {
  118 + if (item.hasClass('menu-item')) {
  119 + sub_menu_i = 0;
  120 + if (item.find('.sub-menu-item').size() > 0) {
  121 + menus[menu_i] = {"name": name, "sub_button": "sub_button"}
  122 + } else {
  123 + menus[menu_i] = $(this).data();
  124 + }
  125 + if (menu_i > 0) {
  126 + menus[menu_i - 1]['sub_button'] = menus[menu_i - 1]['sub_button'] == "sub_button" ? sub_button : menus[menu_i - 1]['sub_button'];
  127 + }
  128 + sub_button = [];
  129 + menu_i++;
  130 + } else {
  131 + sub_button[sub_menu_i++] = $(this).data();
  132 + }
  133 + }
  134 + });
  135 + if (sub_button.length > 0) {
  136 + var len = menus.length;
  137 + menus[len - 1]['sub_button'] = sub_button;
  138 + }
  139 + return menus;
  140 + };
  141 + //添加子菜单
  142 + $(document).on('click', ".add-sub-item", function () {
  143 + var sub_menu_item_total = $(this).parent().find(".sub-menu-item").size();
  144 + if (sub_menu_item_total < 5) {
  145 + 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>';
  146 + var itemDom = $(item);
  147 + itemDom.insertBefore(this);
  148 + itemDom.trigger("click");
  149 + $("#item_title").focus().select();
  150 + updateChangeMenu();
  151 + if (sub_menu_item_total == 4) {
  152 + $(this).addClass("hidden");
  153 + }
  154 + }
  155 + return false;
  156 + });
  157 + //主菜单子菜单点击事件
  158 + $(document).on('click', ".menu-item, .sub-menu-item", function () {
  159 + var hasChild = $(".sub-menu-item", this).size() > 0 ? true : false;
  160 + if ($(this).hasClass("sub-menu-item")) {
  161 + $("#menu-list li").removeClass('current');
  162 + } else {
  163 + $("#menu-list li").removeClass('current');
  164 + $("#menu-list > li").not(this).find(".sub-menu-box").hide();
  165 + $(".sub-menu-box", this).toggle();
  166 + }
  167 + $(this).addClass('current');
  168 +
  169 + var data = $.extend({}, $(this).data());
  170 + data.keytitle = data.key && typeof responselist[data.key] != 'undefined' ? responselist[data.key] : '';
  171 + data.typeList = typeList;
  172 + data.hasChild = hasChild;
  173 + data.first = $(this).hasClass("menu-item") ? true : false;
  174 + $(".weixin-content").show();
  175 + $(".no-weixin-content").hide();
  176 + $("#item-body").html(Template("itemtpl", data));
  177 +
  178 + return false;
  179 + });
  180 + //触发保存
  181 + $("form#form-item").on('change', "input,textarea", function () {
  182 + updateChangeMenu();
  183 + });
  184 + //点击同步
  185 + $(document).on('click', "#menuSyn", function () {
  186 + $.post("wechat/menu/sync", {}, function (ret) {
  187 + var msg = ret.hasOwnProperty("msg") && ret.msg != "" ? ret.msg : "";
  188 + if (ret.code == 1) {
  189 + Backend.api.toastr.success('菜单同步更新成功,生效时间看微信官网说明,或者你重新关注微信号!');
  190 + } else {
  191 + Backend.api.toastr.error(msg ? msg : __('Operation failed'));
  192 + }
  193 + }, 'json');
  194 + });
  195 + //刷新资源
  196 + var refreshkey = function (data) {
  197 + responselist[data.eventkey] = data.title;
  198 + $("input[name=key]").val(data.eventkey).trigger("change");
  199 + $("#menu-list li.current").trigger("click");
  200 + Layer.closeAll();
  201 + };
  202 + //选择资源
  203 + $(document).on('click', "#select-resources", function () {
  204 + var key = $("#key").val();
  205 + Backend.api.open($(this).attr("href") + "?key=" + key, __('Select'), {
  206 + callback: refreshkey
  207 + });
  208 + return false;
  209 + });
  210 + //添加资源
  211 + $(document).on('click', "#add-resources", function () {
  212 + Backend.api.open($(this).attr("href") + "?key=" + key, __('Add'), {
  213 + callback: refreshkey
  214 + });
  215 + return false;
  216 + });
  217 +
  218 + $("#menu-list li.menu-item:first").trigger("click");
  219 + },
  220 + add: function () {
  221 + Form.api.bindevent($("form[role=form]"));
  222 + },
  223 + edit: function () {
  224 + Form.api.bindevent($("form[role=form]"));
  225 + }
  226 + };
  227 + return Controller;
  228 +});
  1 +define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'adminlte'], function ($, undefined, Backend, Table, Form, Adminlte) {
  2 +
  3 + var Controller = {
  4 + index: function () {
  5 + // 初始化表格参数配置
  6 + Table.api.init({
  7 + extend: {
  8 + index_url: 'wechat/response/index',
  9 + add_url: 'wechat/response/add',
  10 + edit_url: 'wechat/response/edit',
  11 + del_url: 'wechat/response/del',
  12 + multi_url: 'wechat/response/multi',
  13 + }
  14 + });
  15 +
  16 + var table = $("#table");
  17 +
  18 + // 初始化表格
  19 + table.bootstrapTable({
  20 + url: $.fn.bootstrapTable.defaults.extend.index_url,
  21 + sortName: 'id',
  22 + columns: [
  23 + [
  24 + {field: 'state', checkbox: true,},
  25 + {field: 'id', title: 'ID'},
  26 + {field: 'type', title: __('Type')},
  27 + {field: 'title', title: __('Resource title')},
  28 + {field: 'eventkey', title: __('Event key')},
  29 + {field: 'status', title: __('Status'), formatter: Table.api.formatter.status, operate: false},
  30 + {field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, formatter: Table.api.formatter.operate}
  31 + ]
  32 + ]
  33 + });
  34 +
  35 + // 为表格绑定事件
  36 + Table.api.bindevent(table);
  37 + },
  38 + select: function () {
  39 + // 初始化表格参数配置
  40 + Table.api.init({
  41 + extend: {
  42 + index_url: 'wechat/response/index',
  43 + }
  44 + });
  45 +
  46 + var table = $("#table");
  47 +
  48 + // 初始化表格
  49 + table.bootstrapTable({
  50 + url: $.fn.bootstrapTable.defaults.extend.index_url,
  51 + sortName: 'id',
  52 + columns: [
  53 + [
  54 + {field: 'state', checkbox: true,},
  55 + {field: 'id', title: 'ID'},
  56 + {field: 'type', title: __('Type')},
  57 + {field: 'title', title: __('Title')},
  58 + {field: 'eventkey', title: __('Event key')},
  59 + {field: 'status', title: __('Status'), formatter: Table.api.formatter.status, operate: false},
  60 + {
  61 + field: 'operate', title: __('Operate'), events: {
  62 + 'click .btn-chooseone': function (e, value, row, index) {
  63 + Fast.api.close(row);
  64 + },
  65 + }, formatter: function () {
  66 + return '<a href="javascript:;" class="btn btn-danger btn-chooseone btn-xs"><i class="fa fa-check"></i> ' + __('Choose') + '</a>';
  67 + }
  68 + }
  69 + ]
  70 + ]
  71 + });
  72 +
  73 + // 为表格绑定事件
  74 + Table.api.bindevent(table);
  75 + },
  76 + add: function () {
  77 + Form.api.bindevent($("form[role=form]"), function (data) {
  78 + Fast.api.close(data);
  79 + });
  80 + Controller.api.bindevent();
  81 + },
  82 + edit: function () {
  83 + Form.api.bindevent($("form[role=form]"));
  84 + Controller.api.bindevent();
  85 + },
  86 + api: {
  87 + bindevent: function () {
  88 + var getAppFileds = function (id) {
  89 + var app = apps[id];
  90 + var appConfig = app['config'];
  91 + var str = '';
  92 + for (var i in appConfig) {
  93 + var type = appConfig[i]['type'];
  94 + var field = appConfig[i]['field'];
  95 + var caption = appConfig[i]['caption'];
  96 + var defaultvalue = typeof appConfig[i]['defaultvalue'] != 'undefined' ? appConfig[i]['defaultvalue'] : '';
  97 + if (type == 'text' || type == 'textarea') {
  98 + if (type == 'textarea') {
  99 + 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>';
  100 + } else {
  101 + 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>';
  102 + }
  103 + } else {
  104 + var options = appConfig[i]['options'];
  105 + var html = '';
  106 + if (type == 'select') {
  107 + for (var j in options) {
  108 + html += '<option value="' + j + '">' + options[j] + '</option>';
  109 + }
  110 + html = '<select ' + appConfig[i]['extend'] + ' class="form-control" name="row[content][' + field + ']">' + html + '</select>';
  111 + } else if (type == 'checkbox') {
  112 + for (var j in options) {
  113 + html += '<input type="checkbox" name="row[content][' + field + '][]" value="' + j + '"> <span>' + options[j] + '</span> ';
  114 + }
  115 +
  116 + } else if (type == 'radio') {
  117 + var index = 0;
  118 + for (var j in options) {
  119 + html += '<input type="radio" name="row[content][' + field + ']" value="' + j + '" ' + (index == 0 ? 'checked' : '') + '> <span>' + options[j] + '</span> ';
  120 + index++;
  121 + }
  122 + }
  123 + 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>';
  124 + }
  125 +
  126 + }
  127 + return str;
  128 + };
  129 + $(document).on('change', "#app", function () {
  130 + var app = $(this).val();
  131 + $("#appfields").html(getAppFileds(app));
  132 + if (datas.app == app) {
  133 + delete (datas.app);
  134 + var form = $("form.form-ajax");
  135 + $.each(datas, function (i, j) {
  136 + console.log(i, j);
  137 + form.field("row[content][" + i + "]" + ($("input[name='row[content][" + i + "][]']", form).size() > 0 ? '[]' : ''), j);
  138 + });
  139 + }
  140 + Form.api.bindevent("#appfields");
  141 + });
  142 + $(document).on('click', "input[name='row[type]']", function () {
  143 + var type = $(this).val();
  144 + if (type == 'text') {
  145 + $("#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>');
  146 + $("form[role='form']").field("row[content][content]", datas.content);
  147 + } else if (type == 'app') {
  148 + $("#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>');
  149 + $("form[role='form']").field("row[content][app]", datas.app);
  150 + $("#app").trigger('change');
  151 + }
  152 + });
  153 + $("input[name='row[type]']:checked").trigger("click");
  154 + }
  155 + }
  156 + };
  157 + return Controller;
  158 +});
  1 +<?php
  2 +
  3 +namespace app\admin\controller\wechat;
  4 +
  5 +use app\common\controller\Backend;
  6 +use app\admin\model\WechatResponse;
  7 +
  8 +/**
  9 + * 微信自动回复管理
  10 + *
  11 + * @icon fa fa-circle-o
  12 + */
  13 +class Autoreply extends Backend
  14 +{
  15 +
  16 + protected $model = null;
  17 + protected $noNeedRight = ['check_text_unique'];
  18 +
  19 + public function _initialize()
  20 + {
  21 + parent::_initialize();
  22 + $this->model = model('WechatAutoreply');
  23 + }
  24 +
  25 + /**
  26 + * 编辑
  27 + */
  28 + public function edit($ids = null)
  29 + {
  30 + $row = $this->model->get(['id' => $ids]);
  31 + if (!$row) {
  32 + $this->error(__('No Results were found'));
  33 + }
  34 + if ($this->request->isPost()) {
  35 + $params = $this->request->post("row/a");
  36 + if ($params) {
  37 + $row->save($params);
  38 + $this->success();
  39 + }
  40 + $this->error();
  41 + }
  42 + $response = WechatResponse::get(['eventkey' => $row['eventkey']]);
  43 + $this->view->assign("response", $response);
  44 + $this->view->assign("row", $row);
  45 + return $this->view->fetch();
  46 + }
  47 +
  48 + /**
  49 + * 判断文本是否唯一
  50 + * @internal
  51 + */
  52 + public function check_text_unique()
  53 + {
  54 + $row = $this->request->post("row/a");
  55 + $except = $this->request->post("except");
  56 + $text = isset($row['text']) ? $row['text'] : '';
  57 + if ($this->model->where('text', $text)->where(function ($query) use ($except) {
  58 + if ($except) {
  59 + $query->where('text', '<>', $except);
  60 + }
  61 + })->count() == 0) {
  62 + $this->success();
  63 + } else {
  64 + $this->error(__('Text already exists'));
  65 + }
  66 + }
  67 +
  68 +}
  1 +<?php
  2 +
  3 +namespace app\admin\controller\wechat;
  4 +
  5 +use app\common\controller\Backend;
  6 +use think\Controller;
  7 +use think\Request;
  8 +
  9 +/**
  10 + * 微信配置管理
  11 + *
  12 + * @icon fa fa-circle-o
  13 + */
  14 +class Config extends Backend
  15 +{
  16 +
  17 + protected $model = null;
  18 +
  19 + public function _initialize()
  20 + {
  21 + parent::_initialize();
  22 + $this->model = model('WechatConfig');
  23 + }
  24 +
  25 + /**
  26 + * 添加
  27 + */
  28 + public function add()
  29 + {
  30 + if ($this->request->isPost()) {
  31 + $params = $this->request->post("row/a");
  32 + if ($params) {
  33 + foreach ($params as $k => &$v) {
  34 + $v = is_array($v) ? implode(',', $v) : $v;
  35 + }
  36 +
  37 + if ($params['mode'] == 'json') {
  38 + //JSON字段
  39 + $fieldarr = $valuearr = [];
  40 + $field = $this->request->post('field/a');
  41 + $value = $this->request->post('value/a');
  42 + foreach ($field as $k => $v) {
  43 + if ($v != '') {
  44 + $fieldarr[] = $field[$k];
  45 + $valuearr[] = $value[$k];
  46 + }
  47 + }
  48 + $params['value'] = json_encode(array_combine($fieldarr, $valuearr), JSON_UNESCAPED_UNICODE);
  49 + }
  50 + unset($params['mode']);
  51 + try {
  52 + //是否采用模型验证
  53 + if ($this->modelValidate) {
  54 + $name = basename(str_replace('\\', '/', get_class($this->model)));
  55 + $validate = is_bool($this->modelValidate) ? ($this->modelSceneValidate ? $name . '.add' : true) : $this->modelValidate;
  56 + $this->model->validate($validate);
  57 + }
  58 + $result = $this->model->save($params);
  59 + if ($result !== false) {
  60 + $this->success();
  61 + } else {
  62 + $this->error($this->model->getError());
  63 + }
  64 + } catch (\think\exception\PDOException $e) {
  65 + $this->error($e->getMessage());
  66 + }
  67 + }
  68 + $this->error(__('Parameter %s can not be empty', ''));
  69 + }
  70 + return $this->view->fetch();
  71 + }
  72 +
  73 + /**
  74 + * 编辑
  75 + */
  76 + public function edit($ids = NULL)
  77 + {
  78 + $row = $this->model->get($ids);
  79 + if (!$row)
  80 + $this->error(__('No Results were found'));
  81 + if ($this->request->isPost()) {
  82 + $params = $this->request->post("row/a");
  83 + if ($params) {
  84 + foreach ($params as $k => &$v) {
  85 + $v = is_array($v) ? implode(',', $v) : $v;
  86 + }
  87 +
  88 + if ($params['mode'] == 'json') {
  89 + //JSON字段
  90 + $fieldarr = $valuearr = [];
  91 + $field = $this->request->post('field/a');
  92 + $value = $this->request->post('value/a');
  93 + foreach ($field as $k => $v) {
  94 + if ($v != '') {
  95 + $fieldarr[] = $field[$k];
  96 + $valuearr[] = $value[$k];
  97 + }
  98 + }
  99 + $params['value'] = json_encode(array_combine($fieldarr, $valuearr), JSON_UNESCAPED_UNICODE);
  100 + }
  101 + unset($params['mode']);
  102 + try {
  103 + //是否采用模型验证
  104 + if ($this->modelValidate) {
  105 + $name = basename(str_replace('\\', '/', get_class($this->model)));
  106 + $validate = is_bool($this->modelValidate) ? ($this->modelSceneValidate ? $name . '.add' : true) : $this->modelValidate;
  107 + $row->validate($validate);
  108 + }
  109 + $result = $row->save($params);
  110 + if ($result !== false) {
  111 + $this->success();
  112 + } else {
  113 + $this->error($row->getError());
  114 + }
  115 + } catch (\think\exception\PDOException $e) {
  116 + $this->error($e->getMessage());
  117 + }
  118 + }
  119 + $this->error(__('Parameter %s can not be empty', ''));
  120 + }
  121 + $this->view->assign("row", $row);
  122 + $this->view->assign("value", (array)json_decode($row->value, true));
  123 + return $this->view->fetch();
  124 + }
  125 +
  126 +}
  1 +<?php
  2 +
  3 +namespace app\admin\controller\wechat;
  4 +
  5 +use app\common\controller\Backend;
  6 +use app\admin\model\WechatResponse;
  7 +use EasyWeChat\Foundation\Application;
  8 +use think\Exception;
  9 +
  10 +/**
  11 + * 菜单管理
  12 + *
  13 + * @icon fa fa-list-alt
  14 + */
  15 +class Menu extends Backend
  16 +{
  17 + protected $wechatcfg = null;
  18 +
  19 + public function _initialize()
  20 + {
  21 + parent::_initialize();
  22 + $this->wechatcfg = \app\admin\model\WechatConfig::get(['name' => 'menu']);
  23 + }
  24 +
  25 + /**
  26 + * 查看
  27 + */
  28 + public function index()
  29 + {
  30 + $responselist = array();
  31 + $all = WechatResponse::all();
  32 + foreach ($all as $k => $v) {
  33 + $responselist[$v['eventkey']] = $v['title'];
  34 + }
  35 + $this->view->assign('responselist', $responselist);
  36 + $this->view->assign('menu', (array)json_decode($this->wechatcfg->value, true));
  37 + return $this->view->fetch();
  38 + }
  39 +
  40 + /**
  41 + * 修改
  42 + */
  43 + public function edit($ids = null)
  44 + {
  45 + $menu = $this->request->post("menu");
  46 + $menu = (array)json_decode($menu, true);
  47 + foreach ($menu as $index => &$item) {
  48 + if (isset($item['sub_button'])) {
  49 + foreach ($item['sub_button'] as &$subitem) {
  50 + if ($subitem['type'] == 'view') {
  51 + $allowFields = ['type', 'name', 'url'];
  52 + $subitem = ['type' => $subitem['type'], 'name' => $subitem['name'], 'url' => $subitem['url']];
  53 + } else {
  54 + if ($subitem['type'] == 'miniprogram') {
  55 + $allowFields = ['type', 'name', 'url', 'appid', 'pagepath'];
  56 + $subitem = ['type' => $subitem['type'], 'name' => $subitem['name'], 'url' => $subitem['url'], 'appid' => $subitem['appid'], 'pagepath' => $subitem['pagepath']];
  57 + } else {
  58 + $allowFields = ['type', 'name', 'key'];
  59 + $subitem = ['type' => $subitem['type'], 'name' => $subitem['name'], 'key' => $subitem['key']];
  60 + }
  61 + }
  62 + $subitem = array_intersect_key($subitem, array_flip($allowFields));
  63 + }
  64 + } else {
  65 + if ($item['type'] == 'view') {
  66 + $allowFields = ['type', 'name', 'url'];
  67 + } else {
  68 + if ($item['type'] == 'miniprogram') {
  69 + $allowFields = ['type', 'name', 'url', 'appid', 'pagepath'];
  70 + } else {
  71 + $allowFields = ['type', 'name', 'key'];
  72 + }
  73 + }
  74 + $item = array_intersect_key($item, array_flip($allowFields));
  75 + }
  76 + }
  77 + $this->wechatcfg->value = json_encode($menu, JSON_UNESCAPED_UNICODE);
  78 + $this->wechatcfg->save();
  79 + $this->success();
  80 + }
  81 +
  82 + /**
  83 + * 同步
  84 + */
  85 + public function sync($ids = null)
  86 + {
  87 + $app = new Application(get_addon_config('wechat'));
  88 + try {
  89 + $hasError = false;
  90 + $menu = json_decode($this->wechatcfg->value, true);
  91 + foreach ($menu as $k => $v) {
  92 + if (isset($v['sub_button'])) {
  93 + foreach ($v['sub_button'] as $m => $n) {
  94 + if ($n['type'] == 'click' && isset($n['key']) && !$n['key']) {
  95 + $hasError = true;
  96 + break 2;
  97 + }
  98 + }
  99 + } else {
  100 + if ($v['type'] == 'click' && isset($v['key']) && !$v['key']) {
  101 + $hasError = true;
  102 + break;
  103 + }
  104 + }
  105 + }
  106 + if (!$hasError) {
  107 + try {
  108 + $ret = $app->menu->add($menu);
  109 + } catch (\EasyWeChat\Core\Exceptions\HttpException $e) {
  110 + $this->error($e->getMessage());
  111 + }
  112 + if ($ret->errcode == 0) {
  113 + $this->success();
  114 + } else {
  115 + $this->error($ret->errmsg);
  116 + }
  117 + } else {
  118 + $this->error(__('Invalid parameters'));
  119 + }
  120 + } catch (Exception $e) {
  121 + $this->error($e->getMessage());
  122 + }
  123 + }
  124 +}
  1 +<?php
  2 +
  3 +namespace app\admin\controller\wechat;
  4 +
  5 +use app\common\controller\Backend;
  6 +use addons\wechat\library\Wechat;
  7 +
  8 +/**
  9 + * 资源管理
  10 + *
  11 + * @icon fa fa-list-alt
  12 + */
  13 +class Response extends Backend
  14 +{
  15 +
  16 + protected $model = null;
  17 + protected $searchFields = 'id,title';
  18 +
  19 + public function _initialize()
  20 + {
  21 + parent::_initialize();
  22 + $this->model = model('WechatResponse');
  23 + }
  24 +
  25 + /**
  26 + * 选择素材
  27 + */
  28 + public function select()
  29 + {
  30 + return $this->view->fetch();
  31 + }
  32 +
  33 + /**
  34 + * 添加
  35 + */
  36 + public function add()
  37 + {
  38 + if ($this->request->isPost()) {
  39 + $params = $this->request->post("row/a");
  40 + $params['eventkey'] = isset($params['eventkey']) && $params['eventkey'] ? $params['eventkey'] : uniqid();
  41 + $params['content'] = json_encode($params['content']);
  42 + $params['createtime'] = time();
  43 + if ($params) {
  44 + $this->model->save($params);
  45 + $this->success();
  46 + $this->content = $params;
  47 + }
  48 + $this->error();
  49 + }
  50 + $appConfig = Wechat::appConfig();
  51 + $this->view->applist = $appConfig;
  52 + return $this->view->fetch();
  53 + }
  54 +
  55 + /**
  56 + * 编辑
  57 + */
  58 + public function edit($ids = NULL)
  59 + {
  60 + $row = $this->model->get($ids);
  61 + if (!$row)
  62 + $this->error(__('No Results were found'));
  63 + if ($this->request->isPost()) {
  64 + $params = $this->request->post("row/a");
  65 + $params['eventkey'] = isset($params['eventkey']) && $params['eventkey'] ? $params['eventkey'] : uniqid();
  66 + $params['content'] = json_encode($params['content']);
  67 + if ($params) {
  68 + $row->save($params);
  69 + $this->success();
  70 + }
  71 + $this->error();
  72 + }
  73 + $this->view->assign("row", $row);
  74 + $appConfig = Wechat::appConfig();
  75 + $this->view->applist = $appConfig;
  76 + return $this->view->fetch();
  77 + }
  78 +
  79 +}
  1 +<?php
  2 +
  3 +return [
  4 + 'Text' => '文本',
  5 + 'Event key' => '响应标识',
  6 + 'Remark' => '备注',
  7 + 'Text already exists' => '文本已经存在',
  8 +];
  1 +<?php
  2 +
  3 +return [
  4 + 'name' => '配置名称',
  5 + 'value' => '配置值',
  6 + 'Json key' => '键',
  7 + 'Json value' => '值',
  8 + 'Json editor' => 'JSON编辑器',
  9 + 'Insert link' => '插入链接',
  10 + 'createtime' => '创建时间',
  11 + 'updatetime' => '更新时间'
  12 +];
  1 +<?php
  2 +
  3 +return [
  4 + 'Resource title' => '资源标题',
  5 + 'Event key' => '事件标识',
  6 + 'Event' => '事件标识',
  7 + 'Text' => '文本',
  8 + 'App' => '应用',
  9 +];
  1 +<?php
  2 +
  3 +namespace app\admin\model;
  4 +
  5 +use think\Model;
  6 +
  7 +class WechatAutoreply extends Model
  8 +{
  9 +
  10 + // 自动写入时间戳字段
  11 + protected $autoWriteTimestamp = 'int';
  12 + // 定义时间戳字段名
  13 + protected $createTime = 'createtime';
  14 + protected $updateTime = 'updatetime';
  15 +
  16 +}
  1 +<?php
  2 +
  3 +namespace app\admin\model;
  4 +
  5 +use fast\Random;
  6 +use think\Model;
  7 +
  8 +class WechatCaptcha extends Model
  9 +{
  10 +
  11 + // 表名
  12 + protected $name = 'wechat_captcha';
  13 + // 自动写入时间戳字段
  14 + protected $autoWriteTimestamp = 'int';
  15 + // 定义时间戳字段名
  16 + protected $createTime = 'createtime';
  17 + protected $updateTime = '';
  18 + // 追加属性
  19 + protected $append = [
  20 + ];
  21 +
  22 + /**
  23 + * 发送验证码
  24 + * @param $openid string 用户OpenID
  25 + * @param $event string 事件
  26 + * @param $ip string IP地址
  27 + * @return string
  28 + */
  29 + public static function send($openid, $event, $ip)
  30 + {
  31 + $captcha = self::where(['openid' => $openid, 'event' => $event])->whereTime('createtime', '-2 minutes')->find();
  32 + if ($captcha) {
  33 + return "验证码发送速度过快,请稍后重试";
  34 + }
  35 + $code = Random::alnum(4);
  36 + $data = [
  37 + 'event' => $event,
  38 + 'openid' => $openid,
  39 + 'code' => $code,
  40 + 'ip' => $ip,
  41 + ];
  42 + self::create($data);
  43 + return "你的验证码是:{$code},2分钟内输入有效";
  44 + }
  45 +
  46 + /**
  47 + * 检测验证码
  48 + * @param $code string 验证码
  49 + * @param $event string 事件
  50 + * @param $ip string IP
  51 + * @return bool
  52 + */
  53 + public static function check($code, $event, $ip = null)
  54 + {
  55 + $ip = is_null($ip) ? request()->ip() : $ip;
  56 + $captcha = self::where(['ip' => $ip, 'event' => $event])->whereTime('createtime', '-2 minutes')->find();
  57 + if ($captcha && $captcha->code == $code && $captcha->times < 10) {
  58 + $captcha->setInc("times");
  59 + return true;
  60 + }
  61 + //验证大于10次或超时
  62 + if ($captcha && ($captcha->times >= 10 || time() - $captcha->createtime > 120)) {
  63 + $captcha->delete();
  64 + }
  65 +
  66 + return false;
  67 + }
  68 +
  69 +}
  1 +<?php
  2 +
  3 +namespace app\admin\model;
  4 +
  5 +use think\Model;
  6 +
  7 +class WechatConfig extends Model
  8 +{
  9 +
  10 + // 表名,不含前缀
  11 + public $name = 'wechat_config';
  12 + // 自动写入时间戳字段
  13 + protected $autoWriteTimestamp = 'int';
  14 + // 定义时间戳字段名
  15 + protected $createTime = 'createtime';
  16 + protected $updateTime = 'updatetime';
  17 + // 追加属性
  18 + protected $append = [
  19 + ];
  20 +
  21 + /**
  22 + * 读取指定配置名称的值
  23 + * @param string $name
  24 + * @return string
  25 + */
  26 + public static function value($name)
  27 + {
  28 + $item = self::get(['name' => $name]);
  29 + return $item ? $item->value : '';
  30 + }
  31 +
  32 +}
  1 +<?php
  2 +
  3 +namespace app\admin\model;
  4 +
  5 +use think\Model;
  6 +
  7 +class WechatContext extends Model
  8 +{
  9 +
  10 + // 自动写入时间戳字段
  11 + protected $autoWriteTimestamp = 'int';
  12 + // 定义时间戳字段名
  13 + protected $createTime = 'createtime';
  14 + protected $updateTime = 'updatetime';
  15 +
  16 +}
  1 +<?php
  2 +
  3 +namespace app\admin\model;
  4 +
  5 +use think\Model;
  6 +
  7 +class WechatResponse extends Model
  8 +{
  9 +
  10 + // 自动写入时间戳字段
  11 + protected $autoWriteTimestamp = 'int';
  12 + // 定义时间戳字段名
  13 + protected $createTime = 'createtime';
  14 + protected $updateTime = 'updatetime';
  15 +
  16 +}
  1 +<link href="__CDN__/assets/addons/wechat/css/menu.css?v={$site.version}" rel="stylesheet">
  2 +<style>
  3 + .clickbox {margin:0;text-align: left;}
  4 + .create-click {
  5 + margin-left:0;
  6 + }
  7 +</style>
  8 +<form id="add-form" class="form-horizontal form-ajax" role="form" data-toggle="validator" method="POST" action="">
  9 + <div class="form-group">
  10 + <label for="c-title" class="control-label col-xs-12 col-sm-2">{:__('Title')}:</label>
  11 + <div class="col-xs-12 col-sm-8">
  12 + <input type="text" name="row[title]" value="" id="c-title" class="form-control" data-rule="required" />
  13 + </div>
  14 + </div>
  15 + <div class="form-group">
  16 + <label for="c-text" class="control-label col-xs-12 col-sm-2">{:__('Text')}:</label>
  17 + <div class="col-xs-12 col-sm-8">
  18 + <input type="text" name="row[text]" value="" id="c-text" class="form-control" data-rule="required; remote(wechat/autoreply/check_text_unique)" />
  19 + </div>
  20 + </div>
  21 + <div class="form-group">
  22 + <label for="c-eventkey" class="control-label col-xs-12 col-sm-2">{:__('Event key')}:</label>
  23 + <div class="col-xs-12 col-sm-8">
  24 + <input type="hidden" name="row[eventkey]" id="c-eventkey" class="form-control" value="" data-rule="required" readonly />
  25 + <div class="clickbox">
  26 + <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>
  27 + <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>
  28 + </div>
  29 + </div>
  30 + </div>
  31 + <div class="form-group">
  32 + <label for="c-remark" class="control-label col-xs-12 col-sm-2">{:__('Remark')}:</label>
  33 + <div class="col-xs-12 col-sm-8">
  34 + <input type="text" name="row[remark]" value="" id="c-remark" class="form-control" />
  35 + </div>
  36 + </div>
  37 + <div class="form-group">
  38 + <label class="control-label col-xs-12 col-sm-2">{:__('Status')}:</label>
  39 + <div class="col-xs-12 col-sm-8">
  40 + {:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')])}
  41 + </div>
  42 + </div>
  43 + <div class="form-group hide layer-footer">
  44 + <label class="control-label col-xs-12 col-sm-2"></label>
  45 + <div class="col-xs-12 col-sm-8">
  46 + <button type="submit" class="btn btn-success btn-embossed disabled">{:__('OK')}</button>
  47 + <button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
  48 + </div>
  49 + </div>
  50 +
  51 +</form>
  1 +<link href="__CDN__/assets/addons/wechat/css/menu.css?v={$site.version}" rel="stylesheet">
  2 +<style>
  3 + .clickbox {margin:0;text-align: left;}
  4 + .create-click {
  5 + margin-left:0;
  6 + }
  7 +</style>
  8 +<form id="edit-form" class="form-horizontal form-ajax" role="form" data-toggle="validator" method="POST" action="">
  9 +
  10 + <div class="form-group">
  11 + <label for="c-title" class="control-label col-xs-12 col-sm-2">{:__('Title')}:</label>
  12 + <div class="col-xs-12 col-sm-8">
  13 + <input type="text" name="row[title]" value="{$row.title}" id="c-title" class="form-control" data-rule="required" />
  14 + </div>
  15 + </div>
  16 + <div class="form-group">
  17 + <label for="c-text" class="control-label col-xs-12 col-sm-2">{:__('Text')}:</label>
  18 + <div class="col-xs-12 col-sm-8">
  19 + <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})" />
  20 + </div>
  21 + </div>
  22 + <div class="form-group">
  23 + <label for="c-eventkey" class="control-label col-xs-12 col-sm-2">{:__('Content')}:</label>
  24 + <div class="col-xs-12 col-sm-8">
  25 + <div class="clickbox">
  26 + <input type="hidden" name="row[eventkey]" id="c-eventkey" class="form-control" value="{$row.eventkey}" data-rule="required" readonly />
  27 + <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>
  28 + <span class="create-click"><a href="wechat/response/add" id="add-resources"><i class="weixin-icon big-add-gray"></i><strong>添加新资源</strong></a></span>
  29 + </div>
  30 + </div>
  31 + </div>
  32 + <div class="form-group">
  33 + <label for="c-remark" class="control-label col-xs-12 col-sm-2">{:__('Remark')}:</label>
  34 + <div class="col-xs-12 col-sm-8">
  35 + <input type="text" name="row[remark]" value="{$row.remark}" id="c-remark" class="form-control" />
  36 + </div>
  37 + </div>
  38 + <div class="form-group">
  39 + <label class="control-label col-xs-12 col-sm-2">{:__('Status')}:</label>
  40 + <div class="col-xs-12 col-sm-8">
  41 + {:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')], $row['status'])}
  42 + </div>
  43 + </div>
  44 + <div class="form-group hide layer-footer">
  45 + <label class="control-label col-xs-12 col-sm-2"></label>
  46 + <div class="col-xs-12 col-sm-8">
  47 + <button type="submit" class="btn btn-success btn-embossed disabled">{:__('OK')}</button>
  48 + <button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
  49 + </div>
  50 + </div>
  51 +</form>
  1 +<div class="panel panel-default panel-intro">
  2 + {:build_heading()}
  3 +
  4 + <div class="panel-body">
  5 + <div id="myTabContent" class="tab-content">
  6 + <div class="tab-pane fade active in" id="one">
  7 + <div class="widget-body no-padding">
  8 + <div id="toolbar" class="toolbar">
  9 + {:build_toolbar()}
  10 + <div class="dropdown btn-group {:$auth->check('wechat/autoreply/multi')?'':'hide'}">
  11 + <a class="btn btn-primary btn-more dropdown-toggle btn-disabled disabled" data-toggle="dropdown"><i class="fa fa-cog"></i> <?= __('More') ?></a>
  12 + <ul class="dropdown-menu text-left" role="menu">
  13 + <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>
  14 + <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>
  15 + </ul>
  16 + </div>
  17 + </div>
  18 + <table id="table" class="table table-striped table-bordered table-hover"
  19 + data-operate-edit="{:$auth->check('wechat/autoreply/edit')}"
  20 + data-operate-del="{:$auth->check('wechat/autoreply/del')}"
  21 + width="100%">
  22 + </table>
  23 + </div>
  24 + </div>
  25 +
  26 + </div>
  27 + </div>
  28 +</div>
  1 +<form id="add-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
  2 + <input type="hidden" name="row[mode]" value="textarea" />
  3 + <div class="form-group">
  4 + <label for="c-name" class="control-label col-xs-12 col-sm-2">{:__('Name')}:</label>
  5 + <div class="col-xs-12 col-sm-8">
  6 + <input id="c-name" data-rule="required" class="form-control" name="row[name]" type="text" value="">
  7 + </div>
  8 + </div>
  9 + <div class="form-group">
  10 + <label for="c-title" class="control-label col-xs-12 col-sm-2">{:__('Title')}:</label>
  11 + <div class="col-xs-12 col-sm-8">
  12 + <input id="c-title" data-rule="required" class="form-control" name="row[title]" type="text" value="">
  13 + </div>
  14 + </div>
  15 + <div class="form-group">
  16 + <label for="c-value" class="control-label col-xs-12 col-sm-2">{:__('Value')}:</label>
  17 + <div class="col-xs-12 col-sm-8">
  18 + <p>
  19 + <a href="javascript:;" class="btn btn-info btn-jsoneditor"><i class="fa fa-pencil"></i> {:__('Json editor')}</a>
  20 + <a href="javascript:;" class="btn btn-primary btn-insertlink"><i class="fa fa-link"></i> {:__('Insert link')}</a>
  21 + </p>
  22 + <textarea id="c-value" class="form-control " rows="15" name="row[value]"></textarea>
  23 + <dl class="fieldlist hide" rel="1">
  24 + <dd>
  25 + <ins>{:__('Json key')}</ins>
  26 + <ins>{:__('Json value')}</ins>
  27 + </dd>
  28 + <dd>
  29 + <input type="text" name="field[0]" class="form-control" id="field-0" value="" size="10" required />
  30 + <input type="text" name="value[0]" class="form-control" id="value-0" value="" size="40" required />
  31 + <span class="btn btn-sm btn-danger btn-remove"><i class="fa fa-times"></i></span>
  32 + <span class="btn btn-sm btn-primary btn-dragsort"><i class="fa fa-arrows"></i></span>
  33 + </dd>
  34 + <dd><a href="javascript:;" class="append btn btn-sm btn-success"><i class="fa fa-plus"></i> {:__('Append')}</a></dd>
  35 + </dl>
  36 + </div>
  37 + </div>
  38 + <div class="form-group hide layer-footer">
  39 + <label class="control-label col-xs-12 col-sm-2"></label>
  40 + <div class="col-xs-12 col-sm-8">
  41 + <button type="submit" class="btn btn-success btn-embossed disabled">{:__('OK')}</button>
  42 + <button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
  43 + </div>
  44 + </div>
  45 +</form>
  1 +<form id="edit-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
  2 + <input type="hidden" name="row[mode]" value="textarea" />
  3 + <div class="form-group">
  4 + <label for="c-name" class="control-label col-xs-12 col-sm-2">{:__('Name')}:</label>
  5 + <div class="col-xs-12 col-sm-8">
  6 + <input id="c-name" data-rule="required" class="form-control" name="row[name]" type="text" value="{$row.name}">
  7 + </div>
  8 + </div>
  9 + <div class="form-group">
  10 + <label for="c-title" class="control-label col-xs-12 col-sm-2">{:__('Title')}:</label>
  11 + <div class="col-xs-12 col-sm-8">
  12 + <input id="c-title" data-rule="required" class="form-control" name="row[title]" type="text" value="{$row.title}">
  13 + </div>
  14 + </div>
  15 + <div class="form-group">
  16 + <label for="c-value" class="control-label col-xs-12 col-sm-2">{:__('Value')}:</label>
  17 + <div class="col-xs-12 col-sm-8">
  18 + <textarea id="c-value" class="form-control " rows="15" name="row[value]">{$row.value}</textarea>
  19 + </div>
  20 + </div>
  21 + <div class="form-group hide layer-footer">
  22 + <label class="control-label col-xs-12 col-sm-2"></label>
  23 + <div class="col-xs-12 col-sm-8">
  24 + <button type="submit" class="btn btn-success btn-embossed disabled">{:__('OK')}</button>
  25 + <button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
  26 + </div>
  27 + </div>
  28 +</form>
  1 +<div class="panel panel-default panel-intro">
  2 + {:build_heading()}
  3 +
  4 + <div class="panel-body">
  5 + <div id="myTabContent" class="tab-content">
  6 + <div class="tab-pane fade active in" id="one">
  7 + <div class="widget-body no-padding">
  8 + <div id="toolbar" class="toolbar">
  9 + {:build_toolbar()}
  10 + <div class="dropdown btn-group {:$auth->check('wechat/config/multi')?'':'hide'}">
  11 + <a class="btn btn-primary btn-more dropdown-toggle btn-disabled disabled" data-toggle="dropdown"><i class="fa fa-cog"></i> {:__('More')}</a>
  12 + <ul class="dropdown-menu text-left" role="menu">
  13 + <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>
  14 + <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>
  15 + </ul>
  16 + </div>
  17 + </div>
  18 + <table id="table" class="table table-striped table-bordered table-hover"
  19 + data-operate-edit="{:$auth->check('wechat/config/edit')}"
  20 + data-operate-del="{:$auth->check('wechat/config/del')}"
  21 + width="100%">
  22 + </table>
  23 + </div>
  24 + </div>
  25 +
  26 + </div>
  27 + </div>
  28 +</div>
  1 +<link href="__CDN__/assets/addons/wechat/css/menu.css?v={$site['version']}" rel="stylesheet"/>
  2 +<style>
  3 + .form-item dl dt {
  4 + width: 120px;
  5 + }
  6 +
  7 + .form-item dl dd {
  8 + margin-left: 120px;
  9 + }
  10 +
  11 + .form-item dl dd input {
  12 + font-size: 12px;
  13 + }
  14 +</style>
  15 +<div class="panel panel-default panel-intro">
  16 + {:build_heading()}
  17 +
  18 + <div class="panel-body">
  19 + <div id="myTabContent" class="tab-content">
  20 + <div class="tab-pane fade active in" id="one">
  21 + <div class="widget-body no-padding">
  22 + <div class="weixin-menu-setting clearfix">
  23 + <div class="mobile-menu-preview">
  24 + <div class="mobile-head-title">{$site.name}</div>
  25 + <ul class="menu-list" id="menu-list">
  26 + </ul>
  27 + </div>
  28 + <div class="weixin-body">
  29 + <div class="weixin-content" style="display:none">
  30 + <div class="item-info">
  31 + <form id="form-item" class="form-item" data-value="">
  32 + <div class="item-head">
  33 + <h4 id="current-item-name">添加子菜单</h4>
  34 + <div class="item-delete"><a href="javascript:;" id="item_delete">删除菜单</a></div>
  35 + </div>
  36 + <div style="margin-top: 20px;" id="item-body">
  37 +
  38 + </div>
  39 + </form>
  40 + </div>
  41 + </div>
  42 + <div class="no-weixin-content">
  43 + 点击左侧菜单进行编辑操作
  44 + </div>
  45 + </div>
  46 + </div>
  47 +
  48 + <div class="text-center" style="position:relative;">
  49 + <div class="text-danger" style="width:317px;position:absolute;left:0;top:0;">
  50 + <i class="fa fa-lightbulb-o"></i> <small>可直接拖动菜单排序</small>
  51 + </div>
  52 + <div style="padding-left:337px;"><a href="javascript:;" id="menuSyn" class="btn btn-danger">保存并发布</a></div>
  53 + </div>
  54 + </div>
  55 + </div>
  56 +
  57 + </div>
  58 + </div>
  59 +</div>
  60 +<script type="text/html" id="menutpl">
  61 + <%for(var i=0; i< menu.length; i++){%>
  62 + <%var first=menu[i];%>
  63 + <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']%>">
  64 + <a href="javascript:;" class="menu-link">
  65 + <i class="icon-menu-dot"></i> <i class="weixin-icon sort-gray"></i> <span class="title"><%=first['name']%></span>
  66 + </a>
  67 +
  68 + <div class="sub-menu-box" style="display:none;">
  69 + <ul class="sub-menu-list">
  70 + <%if(typeof first['sub_button']!='undefined'){%>
  71 + <%for(var j=0; j< first['sub_button'].length; j++){%>
  72 + <%var second=first['sub_button'][j];%>
  73 + <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>
  74 + <%}%>
  75 + <%}%>
  76 + <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>
  77 + </ul>
  78 + <i class="arrow arrow-out"></i> <i class="arrow arrow-in"></i>
  79 + </div>
  80 + </li>
  81 + <%}%>
  82 + <li class="add-item extra" id="add-item">
  83 + <a href="javascript:;" class="menu-link" title="添加菜单"><i class="weixin-icon add-gray"></i></a>
  84 + </li>
  85 +</script>
  86 +<script type="text/html" id="itemtpl">
  87 + <dl>
  88 + <dt id="current-item-option"><span class="is-sub-item <%=first?'hidden':''%>"></span>菜单标题:</dt>
  89 + <dd>
  90 + <div class="input-box"><input id="item_title" name="name" type="text" value="<%=name%>"></div>
  91 + </dd>
  92 + </dl>
  93 + <%if(!hasChild){%>
  94 + <dl class="is-item">
  95 + <dt id="current-item-type"><span class="is-sub-item <%=first?'hidden':''%>"></span>菜单内容:</dt>
  96 + <dd>
  97 + <%for(var i=0;i< typeList.length; i++){%>
  98 + <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>
  99 + <%}%>
  100 + </dd>
  101 + </dl>
  102 + <div id="menu-content" class="is-item">
  103 + <%if(type=='view'){%>
  104 + <div class="viewbox is-view">
  105 + <p class="menu-content-tips">点击该<span class="is-sub-item <%=first?'hidden':''%>"></span>菜单会跳到以下链接</p>
  106 + <dl>
  107 + <dt>页面地址:</dt>
  108 + <dd>
  109 + <div class="input-box"><input type="text" name="url" value="<%=url%>"></div>
  110 + </dd>
  111 + </dl>
  112 + </div>
  113 + <%}%>
  114 + <%if(type!='view'&&type!='miniprogram'){%>
  115 + <div class="clickbox is-click">
  116 + <input type="hidden" name="key" id="key" value="<%=key%>"/>
  117 + <span class="create-click">
  118 + <%if(keytitle){%>
  119 + <div class="keytitle">资源名:<%=keytitle%></div>
  120 + <%}%>
  121 + <a href="{:url('wechat.response/select')}" id="select-resources"><i class="weixin-icon big-add-gray"></i><strong>选择现有资源</strong></a>
  122 + </span>
  123 + <span class="create-click">
  124 + <a href="{:url('wechat.response/add')}" id="add-resources"><i class="weixin-icon big-add-gray"></i><strong>添加新资源</strong></a>
  125 + </span>
  126 + </div>
  127 + <%}%>
  128 + <%if(type=='miniprogram'){%>
  129 + <div class="viewbox is-miniprogram">
  130 + <p class="menu-content-tips">点击该<span class="is-sub-item <%=first?'':'hidden'%>"></span>菜单会跳到以下小程序</p>
  131 + <dl>
  132 + <dt>小程序ID</dt>
  133 + <dd>
  134 + <div class="input-box"><input type="text" id="appid" name="appid" placeholder="在小程序后台获取" value="<%=appid%>"></div>
  135 + </dd>
  136 + </dl>
  137 + <dl>
  138 + <dt>小程序页面路径:</dt>
  139 + <dd>
  140 + <div class="input-box"><input type="text" id="pagepath" name="pagepath" placeholder="小程序页面路径" value="<%=pagepath%>"></div>
  141 + </dd>
  142 + </dl>
  143 + <dl>
  144 + <dt>页面地址:</dt>
  145 + <dd>
  146 + <div class="input-box"><input type="text" name="url" placeholder="页面地址,当不支持小程序时会跳转此页面" value="<%=url%>"></div>
  147 + </dd>
  148 + </dl>
  149 + </div>
  150 + <%}%>
  151 + </div>
  152 + <%}%>
  153 +</script>
  154 +<!--@formatter:off-->
  155 +<script type="text/javascript">
  156 + var menu = {:json_encode($menu, JSON_UNESCAPED_UNICODE)};
  157 + var responselist = {:json_encode($responselist, JSON_UNESCAPED_UNICODE)};
  158 +</script>
  159 +<!--@formatter:on-->
  1 +<form id="add-form" class="form-horizontal form-ajax" role="form" data-toggle="validator" method="POST" action="">
  2 + <div class="form-group">
  3 + <label for="module" class="control-label col-xs-12 col-sm-2">{:__('Resource title')}:</label>
  4 + <div class="col-xs-12 col-sm-8">
  5 + <input type="text" class="form-control" id="title" name="row[title]" value="" data-rule="required" />
  6 + </div>
  7 + </div>
  8 + <div class="form-group">
  9 + <label for="remark" class="control-label col-xs-12 col-sm-2">{:__('Memo')}:</label>
  10 + <div class="col-xs-12 col-sm-8">
  11 + <textarea class="form-control" id="remark" name="row[remark]"></textarea>
  12 + </div>
  13 + </div>
  14 + <div class="form-group">
  15 + <label for="content" class="control-label col-xs-12 col-sm-2">{:__('Type')}:</label>
  16 + <div class="col-xs-12 col-sm-8">
  17 + <input type="radio" name="row[type]" value="text" id="type-text" checked />
  18 + <label for="type-text">{:__('Text')}</label>
  19 + <input type="radio" name="row[type]" value="app" id="type-app" />
  20 + <label for="type-app">{:__('App')}</label>
  21 + </div>
  22 + </div>
  23 + <div id="expand">
  24 +
  25 + </div>
  26 + <div class="form-group">
  27 + <label for="status" class="control-label col-xs-12 col-sm-2">{:__('Status')}:</label>
  28 + <div class="col-xs-12 col-sm-8">
  29 + {:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')])}
  30 + </div>
  31 + </div>
  32 + <div class="form-group {:input('get.callback')?'':'hidden layer-footer'}">
  33 + <div class="col-xs-2"></div>
  34 + <div class="col-xs-12 col-sm-8">
  35 + <button type="submit" class="btn btn-success btn-embossed disabled">{:__('OK')}</button>
  36 + <button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
  37 + </div>
  38 + </div>
  39 + <select name="applist" disabled="true" class="hidden">
  40 + {foreach $applist as $k => $v}
  41 + <option value="{$k}">{$v.name}</option>
  42 + {/foreach}
  43 + </select>
  44 +</form>
  45 +<script>
  46 + var apps = {:json_encode($applist)};
  47 + var datas = {};
  48 +</script>
  1 +<form id="edit-form" class="form-horizontal form-ajax" role="form" data-toggle="validator" method="POST" action="">
  2 +
  3 + <div class="form-group">
  4 + <label for="module" class="control-label col-xs-12 col-sm-2">{:__('Resource title')}:</label>
  5 + <div class="col-xs-12 col-sm-8">
  6 + <input type="text" class="form-control" id="title" name="row[title]" value="{$row.title}" data-rule="required" />
  7 + </div>
  8 + </div>
  9 + <div class="form-group">
  10 + <label for="controller" class="control-label col-xs-12 col-sm-2">{:__('Event key')}:</label>
  11 + <div class="col-xs-12 col-sm-8">
  12 + <input type='text' class="form-control" id="eventkey" name="row[eventkey]" value="{$row.eventkey}" data-rule="required" readonly />
  13 + </div>
  14 + </div>
  15 + <div class="form-group">
  16 + <label for="remark" class="control-label col-xs-12 col-sm-2">{:__('Memo')}:</label>
  17 + <div class="col-xs-12 col-sm-8">
  18 + <textarea class="form-control" id="remark" name="row[remark]">{$row.remark}</textarea>
  19 + </div>
  20 + </div>
  21 + <div class="form-group">
  22 + <label for="content" class="control-label col-xs-12 col-sm-2">{:__('Type')}:</label>
  23 + <div class="col-xs-12 col-sm-8">
  24 + {:build_radios('row[type]', ['text' => __('Text'), 'app' => __('App')], $row['type'])}
  25 + </div>
  26 + </div>
  27 + <div id="expand">
  28 +
  29 + </div>
  30 + <div class="form-group">
  31 + <div class="col-xs-2"></div>
  32 + <div class="col-xs-12 col-sm-8">
  33 + {:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')], $row['status'])}
  34 + </div>
  35 + </div>
  36 + <div class="form-group hidden layer-footer">
  37 + <div class="col-xs-2"></div>
  38 + <div class="col-xs-12 col-sm-8">
  39 + <button type="submit" class="btn btn-success btn-embossed disabled">{:__('OK')}</button>
  40 + <button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
  41 + </div>
  42 + </div>
  43 + <select name="applist" disabled="true" class="hidden">
  44 + {foreach $applist as $k => $v}
  45 + <option value="{$k}">{$v.name}</option>
  46 + {/foreach}
  47 + </select>
  48 +</form>
  49 +<script>
  50 + var apps = {:json_encode($applist)};
  51 + var datas = {$row.content};
  52 +</script>
  1 +<div class="panel panel-default panel-intro">
  2 + {:build_heading()}
  3 +
  4 + <div class="panel-body">
  5 + <div id="myTabContent" class="tab-content">
  6 + <div class="tab-pane fade active in" id="one">
  7 + <div class="widget-body no-padding">
  8 + <div id="toolbar" class="toolbar">
  9 + {:build_toolbar();}
  10 + </div>
  11 + <table id="table" class="table table-bordered table-hover"
  12 + data-operate-edit="{:$auth->check('wechat/response/edit')}"
  13 + data-operate-del="{:$auth->check('wechat/response/del')}"
  14 + width="100%">
  15 + </table>
  16 + </div>
  17 + </div>
  18 +
  19 + </div>
  20 + </div>
  21 +</div>
  1 +<div class="panel panel-default panel-intro">
  2 + {:build_heading()}
  3 +
  4 + <div class="panel-body">
  5 + <div id="myTabContent" class="tab-content">
  6 + <div class="tab-pane fade active in" id="one">
  7 + <div class="widget-body no-padding">
  8 + <table id="table" class="table table-bordered table-hover" width="100%">
  9 +
  10 + </table>
  11 + </div>
  12 + </div>
  13 +
  14 + </div>
  15 + </div>
  16 +</div>
@@ -697,6 +697,40 @@ class Goods extends Api @@ -697,6 +697,40 @@ class Goods extends Api
697 } 697 }
698 } 698 }
699 699
  700 + private function init_wx_pay_for_gzh($Ischeck=false){
  701 + //这里首先判断 此用户是否绑定了微信公众号
  702 + if($Ischeck){
  703 + $third = Third::where(['user_id' => $this->user_id, 'platform' => 'wechat'])->find();
  704 + if(!$third){
  705 + //从这里自动绑定微信公众号的账户
  706 + $this->error('您未绑定微信号',null,1008);
  707 + }
  708 + }
  709 +
  710 + $config = get_addon_config('litestore');
  711 +
  712 + $third_config = get_addon_config('third');
  713 + $third_options = array_intersect_key($third_config, array_flip(['wechat']));
  714 + $third_options = $third_options['wechat'];
  715 +
  716 + $options = [
  717 + 'debug' => true,
  718 + 'log' => [
  719 + 'level' => 'debug',
  720 + 'file' => '/tmp/easywechat.log',
  721 + ],
  722 + 'app_id' => $third_options['app_id'],
  723 + 'secret' => $third_options['app_secret'],
  724 + 'payment' => [
  725 + 'merchant_id' => $config['MCHIDGZH'],
  726 + 'key' => $config['APIKEYGZH'],
  727 + 'notify_url' => \think\Request::instance()->domain().addon_url('litestore/api.order/callback_for_wxgzh'),
  728 + ],
  729 +
  730 + ];
  731 + $this->wxapp = new WXPAY_APP($options);
  732 + }
  733 +
700 private function init_wx_pay_for_wxapp(){ 734 private function init_wx_pay_for_wxapp(){
701 $config = get_addon_config('litestore'); 735 $config = get_addon_config('litestore');
702 $options = [ 736 $options = [