正在显示
72 个修改的文件
包含
4417 行增加
和
0 行删除
addons/wechat/Wechat.php
0 → 100644
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 | +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 | +<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> |
addons/wechat/assets/css/menu.css
0 → 100644
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.3 KB
12.4 KB
addons/wechat/assets/images/mobile_index.png
0 → 100644
154 字节
addons/wechat/assets/images/weixin_icon.png
0 → 100644
1.8 KB
addons/wechat/cert/apiclient_cert.p12
0 → 100644
不能预览此文件类型
addons/wechat/cert/apiclient_cert.pem
0 → 100644
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----- |
addons/wechat/cert/apiclient_key.pem
0 → 100644
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----- |
addons/wechat/config.php
0 → 100644
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 | +); |
addons/wechat/controller/Captcha.php
0 → 100644
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 | +} |
addons/wechat/controller/Index.php
0 → 100644
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 | +} |
addons/wechat/info.ini
0 → 100644
addons/wechat/install.sql
0 → 100644
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 | + |
addons/wechat/library/Config.php
0 → 100644
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 | +} |
addons/wechat/library/Wechat.php
0 → 100644
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 | +} |
addons/wechat/model/WechatAutoreply.php
0 → 100644
addons/wechat/model/WechatCaptcha.php
0 → 100644
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 | +} |
addons/wechat/model/WechatConfig.php
0 → 100644
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 | +} |
addons/wechat/model/WechatContext.php
0 → 100644
addons/wechat/model/WechatResponse.php
0 → 100644
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 | +} |
application/admin/controller/wechat/Menu.php
0 → 100644
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 | +} |
application/admin/model/WechatAutoreply.php
0 → 100644
application/admin/model/WechatCaptcha.php
0 → 100644
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 | +} |
application/admin/model/WechatConfig.php
0 → 100644
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 | +} |
application/admin/model/WechatContext.php
0 → 100644
application/admin/model/WechatResponse.php
0 → 100644
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 = [ |
-
请 注册 或 登录 后发表评论