chore(addon): 新增wechat插件

This commit is contained in:
2025-12-05 15:52:21 +08:00
parent 402c425575
commit ff89fdf5e9
137 changed files with 15906 additions and 0 deletions

View File

@@ -0,0 +1,84 @@
<?php
namespace addon\wechat\api\controller;
use addon\wechat\model\Material as MaterialModel;
use addon\wechat\model\Wechat as WechatModel;
use app\Controller;
use think\facade\Cache;
use think\facade\Log;
class Auth extends Controller
{
public $wechat;
public $config;
public function __construct()
{
parent::__construct();
$site_id = request()->siteid();
$this->wechat = new WechatModel($site_id);
}
/**
* ************************************************************************微信公众号消息相关方法 开始******************************************************
*/
/**
* 关联公众号微信unserialize
*/
public function relateWeixin()
{
Log::write('微信公众号消息');
$this->wechat->app = $this->wechat->app();
$this->wechat->relateWeixin();
}
/**
* ************************************************************************微信公众号消息相关方法 结束******************************************************
*/
/**
* 关联公众号微信unserialize
*/
public function wechatArticle()
{
$id = input('id', '');
$index = input('i', 0);
$material_model = new MaterialModel();
$info = $material_model->getMaterialInfo([ 'id' => $id ]);
if (!empty($info[ 'data' ][ 'value' ]) && json_decode($info[ 'data' ][ 'value' ], true)) {
$info[ 'data' ][ 'value' ] = json_decode($info[ 'data' ][ 'value' ], true);
}
$this->assign('info', $info[ 'data' ]);
$this->assign('index', $index);
$replace = [
'WECHAT_CSS' => __ROOT__ . '/addon/wechat/admin/view/public/css',
'WECHAT_JS' => __ROOT__ . '/addon/wechat/admin/view/public/js',
'WECHAT_IMG' => __ROOT__ . '/addon/wechat/admin/view/public/img',
];
return $this->fetch('wechat/article', [], $replace);
}
/**
* 获取授权信息
*/
public function getAuthInfo()
{
$cache_key = input("cache_key");
$site_id = request()->siteid();
$wechat_model = new WechatModel($site_id);
$res = $wechat_model->authCodeToOpenid(input());
if ($res["code"] >= 0) {
Cache::set($cache_key, $res["data"]);
$this->assign('result', '授权成功!');
return $this->fetch('auth/result');
} else {
$this->assign('result', $res['message']);
return $this->fetch('auth/result');
}
}
}

View File

@@ -0,0 +1,178 @@
<?php
namespace addon\wechat\api\controller;
use addon\wechat\model\Config as ConfigModel;
use addon\wechat\model\Fans;
use addon\wechat\model\Wechat as WechatModel;
use app\api\controller\BaseApi;
use app\model\member\Member as MemberModel;
use think\facade\Cache;
class Wechat extends BaseApi
{
/**
* 获取openid
*/
public function authCodeToOpenid()
{
$weapp_model = new WechatModel($this->site_id);
$res = $weapp_model->getAuthByCode($this->params);
return $this->response($res);
}
/**
* 获取网页授权code
*/
public function authcode()
{
$redirect_url = $this->params[ 'redirect_url' ] ?? '';
$scopes = $this->params[ 'scopes' ] ?? 'snsapi_base';
$weapp_model = new WechatModel($this->site_id);
$res = $weapp_model->getAuthCode($redirect_url, $scopes);
return $this->response($res);
}
/**
* 获取jssdk配置
*/
public function jssdkConfig()
{
$url = $this->params[ 'url' ] ?? '';
$weapp_model = new WechatModel($this->site_id);
$res = $weapp_model->getJssdkConfig($url);
return $this->response($res);
}
/**
* 分享设置
*/
public function share()
{
$this->checkToken();
//页面路径
$url = $this->params[ 'url' ] ?? '';
$url = htmlspecialchars_decode($url);
//sdk配置
$weapp_model = new WechatModel($this->site_id);
$jssdk_config = $weapp_model->getJssdkConfig($url);
if ($jssdk_config[ 'code' ] < 0) return $this->response($jssdk_config);
//分享配置
$share_config = [];
$share_data = event('WchatShareData', [
'url' => $url,
'site_id' => $this->site_id,
'member_id' => $this->member_id,
], true);
if (!empty($share_data)) {
$share_config[ 'permission' ] = $share_data[ 'permission' ];
$share_config[ 'data' ] = $share_data[ 'data' ];
} else {
$share_config[ 'permission' ] = [
'hideOptionMenu' => true,
'hideMenuItems' => [],
];
$share_config[ 'data' ] = null;
}
$data = [
'jssdk_config' => $jssdk_config[ 'data' ],
'share_config' => $share_config,
];
return $this->response($this->success($data));
}
/**
* 绑定店铺openid
* @return false|string
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
*/
public function shopBindOpenid()
{
$key = $this->params[ "key" ];
$weapp_model = new WechatModel($this->site_id);
$res = $weapp_model->authCodeToOpenid($this->params);
if ($res[ "code" ] >= 0) {
Cache::set($key, $res[ "data" ][ "openid" ]);
return $this->response($res);
}
}
/**
* 获取登录二维码
* @return false|string
*/
public function loginCode()
{
$key = str_replace('==', '', base64_encode(uniqid('')));
$expire_time = 600;
$wechat_model = new WechatModel($this->site_id);
$res = $wechat_model->getTempQrcode('key_' . $key, $expire_time);
if ($res[ 'code' ] != 0) return $this->response($res);
Cache::set('wechat_' . $key, [ 'expire_time' => ( time() + $expire_time ) ], 3600);
$data = $this->success([
'key' => $key,
'expire_time' => $expire_time,
'qrcode' => $res[ 'data' ]
]);
return $this->response($data);
}
/*
* 验证公众号是否配置
*/
public function verificationWx()
{
$config_model = new ConfigModel();
$config_info = $config_model->getWechatConfig($this->site_id);
if (!empty($config_info[ 'data' ])) {
$config = $config_info[ 'data' ][ "value" ];
if ($config[ 'appid' ] || $config[ 'appsecret' ]) {
return $this->response($this->success());
}
}
return $this->response($this->error());
}
/**
* 获取公众号关注二维码
* @return false|string
*/
public function followQrcode()
{
if (!in_array($this->params[ 'app_type' ], [ 'weapp', 'wechat' ])) return $this->response($this->error('', '该接口仅支持微信浏览器中调用'));
$token = $this->checkToken();
if ($token[ 'code' ] < 0) return $this->response($token);
$member_model = new MemberModel();
$member_info = $member_model->getMemberInfo([ [ 'member_id', '=', $this->member_id, [ 'site_id', '=', $this->site_id ] ] ], 'member_id,wx_openid')[ 'data' ];
if (!empty($member_info) && !empty($member_info[ 'wx_openid' ])) {
$wechat_fans = ( new Fans() )->getFansInfo([ [ 'site_id', '=', $this->site_id ], [ 'openid', '=', $member_info[ 'wx_openid' ] ], [ 'is_subscribe', '=', 1 ] ], 'fans_id')[ 'data' ];
if (!empty($wechat_fans)) return $this->response($this->success());
if (!empty($wechat_fans[ 'fans_id' ])) {
$wechat_model = new WechatModel();
$res = $wechat_model->getFollowQrcode($wechat_fans[ 'fans_id' ]);
if ($res[ 'code' ] == 0) return $this->response($res);
}
}
// $fans_info = ( new MemberFans() )->getFansInfo([ [ 'fans_id', '=', $this->fans_id ] ], 'openid')[ 'data' ];
// $wechat_fans = ( new Fans() )->getFansInfo([ [ 'site_id', '=', $this->site_id ], [ 'is_subscribe', '=', 1 ], [ 'openid', '=', $fans_info[ 'openid' ] ] ], 'fans_id');
// if (!empty($wechat_fans[ 'data' ])) return $this->response($this->success());
$config_model = new ConfigModel();
$config_result = $config_model->getWechatConfig($this->site_id)[ 'data' ][ 'value' ];
return $this->response($this->success([ 'qrcode' => $config_result[ 'qrcode' ] ]));
}
}

View File

@@ -0,0 +1,20 @@
<html>
<head>
<meta charset="utf-8">
<meta name="renderer" content="webkit">
<meta name="referrer" content="never">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0,viewport-fit=cover"/>
<meta http-equiv="X-UA-COMPATIBLE" content="IE=edge,chrome=1">
<title>微信授权结果</title>
<meta name="keywords" content="">
<meta name="description" content="">
</head>
<body>
<div class='preview-box'>
<p style="margin-top: 20vh;text-align: center;color: orangered;font-size: 20px;">{$result}</p>
</div>
</body>
<script>
</script>
</html>

View File

@@ -0,0 +1,52 @@
<html>
<head>
<meta charset="utf-8">
<meta name="renderer" content="webkit">
<meta name="referrer" content="never">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0,viewport-fit=cover"/>
<meta http-equiv="X-UA-COMPATIBLE" content="IE=edge,chrome=1">
<title>{$info.value[$index]['title']}</title>
<meta name="keywords" content="">
<meta name="description" content="">
<link rel="stylesheet" type="text/css" href="__STATIC__/ext/layui/css/layui.css">
<link rel="stylesheet" href="SITEHOME_CSS/home.css">
<link rel="stylesheet" href="WECHAT_CSS/wx_preview_graphic_message.css">
<script src="__STATIC__/js/jquery-2.2.js"></script>
<style>
#content p{
width: 100%;
word-wrap:break-word;
word-break:normal;
}
#content p>img{
max-width: 100%;
}
</style>
</head>
<body>
<div class='preview-box'>
<div class='graphic-message'>
<h1 class='title'>{$info.value[$index]['title']}</h1>
{if condition="$info.update_time eq 0"}
<span class='time'>{$info['create_time']|time_to_date='Y-m-d'}</span>
{else/}
<span class='time'>{$info['update_time']|time_to_date='Y-m-d'}</span>
{/if}
<span class='author'>{$info.value[$index]['autor']}</span>
<content>
{if condition="$info.value[$index]['show_cover_pic'] == 1"}
<div class="rich-media-thumb" id="media" style="width: 100%">
<img style="max-width: 100%" onerror="this.parentNode.removeChild(this)" src="{$info.value[$index]['cover']['path']}">
</div>
{/if}
<div id="content">
{:html_entity_decode($info.value[$index]['content'])}
</div>
</content>
{if condition="!empty($info.value[$index]['url'])"}
<a class='original-text' href="{$info.value[$index]['url']}">阅读原文</a>
{/if}
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,30 @@
<?php
return [
// 自定义模板页面类型,格式:[ 'title' => '页面类型名称', 'name' => '页面标识', 'path' => '页面路径', 'value' => '页面数据json格式' ]
'template' => [],
// 后台自定义组件——装修
'util' => [],
// 自定义页面路径
'link' => [],
// 自定义图标库
'icon_library' => [],
// uni-app 组件,格式:[ 'name' => '组件名称/文件夹名称', 'path' => '文件路径/目录路径' ]多个逗号隔开自定义组件名称前缀必须是diy-,也可以引用第三方组件
'component' => [],
// uni-app 页面,多个逗号隔开
'pages' => [],
// 模板信息,格式:'title' => '模板名称', 'name' => '模板标识', 'cover' => '模板封面图', 'preview' => '模板预览图', 'desc' => '模板描述'
'info' => [],
// 主题风格配色格式可以自由定义扩展【在uni-app中通过this.themeStyle... 获取定义的颜色字段例如this.themeStyle.main_color】
'theme' => [],
// 自定义页面数据,格式:[ 'title' => '页面名称', 'name' => "页面标识", 'value' => [页面数据json格式] ]
'data' => []
];

View File

@@ -0,0 +1,17 @@
<?php
// 事件定义文件
return [
'bind' => [
],
'listen' => [
'AddSite' => [
'addon\wechat\event\AddSiteReplay',// 添加默认关注回复
],
],
'subscribe' => [
],
];

View File

@@ -0,0 +1,12 @@
<?php
return [
'name' => 'wechat',
'title' => '微信公众号',
'description' => '微信公众号功能',
'type' => 'system', //插件类型 system :系统插件(自动安装), promotion:扩展营销插件 tool:工具插件
'status' => 1,
'author' => '',
'version' => '5.5.2',
'version_no' => '552250604001',
'content' => '',
];

View File

@@ -0,0 +1,209 @@
<?php
return [
[
'name' => 'WECHAT_ROOT',
'title' => '微信公众号',
'url' => 'wechat://shop/wechat/setting',
'parent' => 'CHANNEL_ROOT',
'picture_select' => '',
'picture' => 'addon/wechat/shop/view/public/img/menu_icon/wechat_icon_new.png',
'picture_selected' => 'addon/wechat/shop/view/public/img/menu_icon/wechat_icon_select.png',
'is_show' => 1,
'sort' => 3,
'child_list' => [
[
'name' => 'WCHAT_CONFIG',
'title' => '基础配置',
'url' => 'wechat://shop/wechat/config',
'is_show' => 0,
'sort' => 2,
'type' => 'button',
],
[
'name' => 'WECHAT_MENU',
'title' => '菜单管理',
'url' => 'wechat://shop/menu/menu',
'is_show' => 0,
'sort' => 3,
'type' => 'button',
'child_list' => [
[
'name' => 'WECHAT_MENU_EDIT',
'title' => '编辑微信菜单',
'url' => 'wechat://shop/menu/edit',
'is_show' => 0,
'type' => 'button',
],
]
],
[
'name' => 'WECHAT_MATERIAL',
'title' => '消息素材',
'url' => 'wechat://shop/material/lists',
'is_show' => 0,
'sort' => 4,
'type' => 'button',
'child_list' => [
[
'name' => 'WECHAT_MATERIAL_ADD_TEXT',
'title' => '添加文本素材',
'url' => 'wechat://shop/material/addTextMaterial',
'is_show' => 0,
'type' => 'button',
],
[
'name' => 'WECHAT_MATERIAL_ADD',
'title' => '添加图文',
'url' => 'wechat://shop/material/add',
'is_show' => 0,
'type' => 'button',
],
[
'name' => 'WECHAT_MATERIAL_EDIT',
'title' => '修改图文',
'url' => 'wechat://shop/material/edit',
'is_show' => 0,
'type' => 'button',
],
[
'name' => 'WECHAT_MATERIAL_DELETE',
'title' => '删除图文',
'url' => 'wechat://shop/material/delete',
'is_show' => 0,
'type' => 'button',
],
[
'name' => 'WECHAT_MATERIAL_ADD_TEXT',
'title' => '添加文本',
'url' => 'wechat://shop/material/addtextmaterial',
'is_show' => 0,
'type' => 'button',
],
[
'name' => 'WECHAT_MATERIAL_EDIT_TEXT',
'title' => '修改文本',
'url' => 'wechat://shop/material/edittextmaterial',
'is_show' => 0,
'type' => 'button',
],
[
'name' => 'WECHAT_MATERIAL_DELETE_TEXT',
'title' => '删除文本',
'url' => 'wechat://shop/material/textmaterial',
'is_show' => 0,
'type' => 'button',
],
]
],
[
'name' => 'WECHAT_QRCODE',
'title' => '推广二维码管理',
'url' => 'wechat://shop/wechat/qrcode',
'is_show' => 0,
'type' => 'button',
'child_list' => [
[
'name' => 'WECHAT_QRCODE_ADD',
'title' => '推广二维码添加',
'url' => 'wechat://shop/wechat/addqrcode',
'is_show' => 0,
'type' => 'button',
],
[
'name' => 'WECHAT_QRCODE_EDIT',
'title' => '推广二维码编辑',
'url' => 'wechat://shop/wechat/editqrcode',
'is_show' => 0,
'type' => 'button',
],
[
'name' => 'WECHAT_QRCODE_DELETE',
'title' => '推广二维码删除',
'url' => 'wechat://shop/wechat/deleteqrcode',
'is_show' => 0,
'type' => 'button',
],
[
'name' => 'WECHAT_QRCODE_DEFAULT',
'title' => '设置默认推广二维码',
'url' => 'wechat://shop/wechat/qrcodeDefault',
'is_show' => 0,
'type' => 'button',
],
]
],
[
'name' => 'WECHAT_SHARE',
'title' => '分享内容',
'url' => 'wechat://shop/wechat/share',
'is_show' => 0,
'sort' => 5,
'type' => 'button',
],
[
'name' => 'WECHAT_REPLAY_INDEX',
'title' => '回复设置',
'url' => 'wechat://shop/replay/replay',
'is_show' => 0,
'sort' => 6,
'type' => 'button',
'child_list' => [
[
'name' => 'WECHAT_REPLAY_KEYS',
'title' => '关键词自动回复',
'url' => 'wechat://shop/replay/replay',
'is_show' => 1,
'type' => 'button',
],
[
'name' => 'WECHAT_REPLAY_DEFAULT',
'title' => '默认自动回复',
'url' => 'wechat://shop/replay/default',
'is_show' => 1,
'type' => 'button',
],
[
'name' => 'WECHAT_REPLAY_FOLLOW',
'title' => '关注后自动回复',
'url' => 'wechat://shop/replay/follow',
'is_show' => 1,
'type' => 'button',
],
]
],
[
'name' => 'WECHAT_MESSAGE_CONFIG',
'title' => '模板消息',
'url' => 'wechat://shop/message/config',
'is_show' => 0,
'sort' => 8,
'type' => 'button',
'child_list' => [
[
'name' => 'WECHAT_MESSAGE_EDIT',
'parent' => 'MESSAGE_LISTS',
'title' => '编辑消息模板',
'url' => 'wechat://shop/message/edit',
'is_show' => 0,
'type' => 'button',
],
[
'name' => 'WECHAT_MESSAGE_CONFIG',
'title' => '是否需跳转到小程序',
'url' => 'wechat://shop/message/messageConfig',
'is_show' => 0,
'type' => 'button',
],
[
'name' => 'WECHAT_MESSAGE_SET_WECHAT_STATUS',
'title' => '设置微信模板消息状态',
'url' => 'wechat://shop/message/setWechatStatus',
'is_show' => 0,
'type' => 'button',
],
]
],
]
],
];

View File

@@ -0,0 +1,174 @@
<?php
return [
40001 => '获取 access_token 时 AppSecret 错误,或者 access_token 无效。请认真比对 AppSecret 的正确性,或查看是否正在为恰当的公众号调用接口',
40002 => '不合法的凭证类型',
40003 => '不合法的 OpenID ,请确认 OpenID (该用户)是否已关注公众号,或是否是其他公众号的 OpenID',
40004 => '不合法的媒体文件类型',
40005 => '不合法的文件类型',
40006 => '不合法的文件大小',
40007 => '不合法的媒体文件 id',
40008 => '不合法的消息类型',
40009 => '不合法的图片文件大小',
40010 => '不合法的语音文件大小',
40011 => '不合法的视频文件大小',
40012 => '不合法的缩略图文件大小',
40013 => '不合法的 AppID ,请检查 AppID 的正确性,避免异常字符,注意大小写',
40014 => '不合法的 access_token ,请认真比对 access_token 的有效性(如是否过期),或查看是否正在为恰当的公众号调用接口',
40015 => '不合法的菜单类型',
40016 => '不合法的按钮个数',
40017 => '不合法的按钮类型',
40018 => '不合法的按钮名字长度',
40019 => '不合法的按钮 KEY 长度',
40020 => '不合法的按钮 URL 长度',
40021 => '不合法的菜单版本号',
40022 => '不合法的子菜单级数',
40023 => '不合法的子菜单按钮个数',
40024 => '不合法的子菜单按钮类型',
40025 => '不合法的子菜单按钮名字长度',
40026 => '不合法的子菜单按钮 KEY 长度',
40027 => '不合法的子菜单按钮 URL 长度',
40028 => '不合法的自定义菜单使用用户',
40029 => '无效的 oauth_code',
40030 => '不合法的 refresh_token',
40031 => '不合法的 openid 列表',
40032 => '不合法的 openid 列表长度',
40033 => '不合法的请求字符,不能包含 \uxxxx 格式的字符',
40035 => '不合法的参数',
40038 => '不合法的请求格式',
40039 => '不合法的 URL 长度',
40048 => '无效的url',
40050 => '不合法的分组 id',
40051 => '分组名字不合法',
40060 => '删除单篇图文时,指定的 article_idx 不合法',
40117 => '分组名字不合法',
40118 => 'media_id 大小不合法',
40119 => 'button 类型错误',
40120 => '子 button 类型错误',
40121 => '不合法的 media_id 类型',
40125 => '无效的appsecret',
40132 => '微信号不合法',
40137 => '不支持的图片格式',
40155 => '请勿添加其他公众号的主页链接',
40163 => 'oauth_code已使用',
40164 => 'IP' . request()->ip() . '未加入公众号ip白名单',
40227 => '标题为空',
41001 => '缺少 access_token 参数',
41002 => '缺少 appid 参数',
41003 => '缺少 refresh_token 参数',
41004 => '缺少 secret 参数',
41005 => '缺少多媒体文件数据',
41006 => '缺少 media_id 参数',
41007 => '缺少子菜单数据',
41008 => '缺少 oauth code',
41009 => '缺少 openid',
42001 => 'access_token 超时,请检查 access_token 的有效期',
42002 => 'refresh_token 超时',
42003 => 'oauth_code 超时',
42007 => '用户修改微信密码, accesstoken 和 refreshtoken 失效,需要重新授权',
42010 => '相同 media_id 群发过快,请重试',
43001 => '需要 GET 请求',
43002 => '需要 POST 请求',
43003 => '需要 HTTPS 请求',
43004 => '需要接收者关注',
43005 => '需要好友关系',
43019 => '需要将接收者从黑名单中移除',
44001 => '多媒体文件为空',
44002 => 'POST 的数据包为空',
44003 => '图文消息内容为空',
44004 => '文本消息内容为空',
45001 => '多媒体文件大小超过限制',
45002 => '消息内容超过限制',
45003 => '标题字段超过限制',
45004 => '描述字段超过限制',
45005 => '链接字段超过限制',
45006 => '图片链接字段超过限制',
45007 => '语音播放时间超过限制',
45008 => '图文消息超过限制',
45009 => '接口调用超过限制',
45010 => '创建菜单个数超过限制',
45011 => 'API 调用太频繁,请稍候再试',
45015 => '回复时间超过限制',
45016 => '系统分组,不允许修改',
45017 => '分组名字过长',
45018 => '分组数量超过上限',
45047 => '客服接口下行条数超过上限',
45064 => '创建菜单包含未关联的小程序',
45065 => '相同 clientmsgid 已存在群发记录,返回数据中带有已存在的群发任务的 msgid',
45066 => '相同 clientmsgid 重试速度过快请间隔1分钟重试',
45067 => 'clientmsgid 长度超过限制',
46001 => '不存在媒体数据',
46002 => '不存在的菜单版本',
46003 => '不存在的菜单数据',
46004 => '不存在的用户',
47001 => '解析 JSON/XML 内容错误',
47003 => '参数值不符合限制要求,详情可参考参数值内容限制说明',
48001 => 'api 功能未授权,请确认公众号已获得该接口,可以在公众平台官网 - 中心页中查看接口权限',
48002 => '粉丝拒收消息(粉丝在公众号选项中,关闭了 “ 接收消息 ” ',
48004 => 'api 接口被封禁,请登录 mp.weixin.qq.com 查看详情',
48005 => 'api 禁止删除被自动回复和自定义菜单引用的素材',
48006 => 'api 禁止清零调用次数,因为清零次数达到上限',
48008 => '没有该类型消息的发送权限',
50001 => '用户未授权该 api',
50002 => '用户受限,可能是违规后接口被封禁',
50005 => '用户未关注公众号',
53500 => '发布功能被封禁',
53501 => '频繁请求发布',
53502 => 'Publish ID 无效',
53600 => 'Article ID 无效',
61451 => '参数错误 (invalid parameter)',
61452 => '无效客服账号 (invalid kf_account)',
61453 => '客服帐号已存在 (kf_account exsited)',
61454 => '客服帐号名长度超过限制 ( 仅允许 10 个英文字符,不包括 @ 及 @ 后的公众号的微信号 )(invalid kf_acount length)',
61455 => '客服帐号名包含非法字符 ( 仅允许英文 + 数字 )(illegal character in kf_account)',
61456 => '客服帐号个数超过限制 (10 个客服账号 )(kf_account count exceeded)',
61457 => '无效头像文件类型 (invalid file type)',
61450 => '系统错误 (system error)',
61500 => '日期格式错误',
63001 => '部分参数为空',
63002 => '无效的签名',
65301 => '不存在此 menuid 对应的个性化菜单',
65302 => '没有相应的用户',
65303 => '没有默认菜单,不能创建个性化菜单',
65304 => 'MatchRule 信息为空',
65305 => '个性化菜单数量受限',
65306 => '不支持个性化菜单的帐号',
65307 => '个性化菜单信息为空',
65308 => '包含没有响应类型的 button',
65309 => '个性化菜单开关处于关闭状态',
65310 => '填写了省份或城市信息,国家信息不能为空',
65311 => '填写了城市信息,省份信息不能为空',
65312 => '不合法的国家信息',
65313 => '不合法的省份信息',
65314 => '不合法的城市信息',
65316 => '该公众号的菜单设置了过多的域名外跳(最多跳转到 3 个域名的链接)',
65317 => '不合法的 URL',
87009 => '无效的签名',
9001001 => 'POST 数据参数不合法',
9001002 => '远端服务不可用',
9001003 => 'Ticket 不合法',
9001004 => '获取摇周边用户信息失败',
9001005 => '获取商户信息失败',
9001006 => '获取 OpenID 失败',
9001007 => '上传文件缺失',
9001008 => '上传素材的文件类型不合法',
9001009 => '上传素材的文件尺寸不合法',
9001010 => '上传失败',
9001020 => '帐号不合法',
9001021 => '已有设备激活率低于 50% ,不能新增设备',
9001022 => '设备申请数不合法,必须为大于 0 的数字',
9001023 => '已存在审核中的设备 ID 申请',
9001024 => '一次查询设备 ID 数量不能超过 50',
9001025 => '设备 ID 不合法',
9001026 => '页面 ID 不合法',
9001027 => '页面参数不合法',
9001028 => '一次删除页面 ID 数量不能超过 10',
9001029 => '页面已应用在设备中,请先解除应用关系再删除',
9001030 => '一次查询页面 ID 数量不能超过 50',
9001031 => '时间区间不合法',
9001032 => '保存设备与页面的绑定关系参数错误',
9001033 => '门店 ID 不合法',
9001034 => '设备备注信息过长',
9001035 => '设备申请参数不合法',
9001036 => '查询起始值 begin 不合法',
];

View File

@@ -0,0 +1,33 @@
<?php
namespace addon\wechat\event;
use addon\wechat\model\Replay;
/**
* 增加站点关注回复
*/
class AddSiteReplay
{
public function handle($param)
{
if (!empty($param[ 'site_id' ])) {
$replay = new Replay();
$data = [
'site_id' => $param[ 'site_id' ],
'rule_name' => '关注回复',
'rule_type' => 'AFTER',
'keywords_json' => '',
'replay_json' => '[{"type":"text","reply_content":"关注回复"}]',
'create_time' => time()
];
$res = $replay->addRule($data);
return $res;
}
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace addon\system\Wechat\event;
use addon\system\Wechat\common\model\WechatMessage;
class CronMessageSend
{
/**
* 邮箱消息延时发送
* @param array $param
*/
public function handle($param = [])
{
$wechat_message = new WechatMessage();
$res = $wechat_message->cronMessageSend($param);
return $res;
}
}

View File

@@ -0,0 +1,17 @@
<?php
namespace addon\Wechat\event;
/**
* 应用安装
*/
class Install
{
/**
* 执行安装
*/
public function handle()
{
return success();
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace addon\system\Wechat\event;
use addon\wechat\model\Wechat;
use liliuwei\think\Jump;
/**
* 应用安装
*/
class OAuthLogin
{
use Jump;
/**
* 授权登录
* @param array $params
*/
public function handle($params = [])
{
if ($params[ 'name' ] == 'Wechat') {
$wechat_model = new Wechat();
$wechat_config = $wechat_model->getWechatConfigInfo($params[ 'site_id' ]);
if (empty($wechat_config[ 'data' ][ 'value' ])) {
$this->error('站点未配置微信公众号');
} else {
$value = $wechat_config[ 'data' ][ 'value' ];
if (empty($value[ 'appid' ]) || empty($value[ 'appsecret' ])) {
$this->error('请配置您公众号的AppID和AppSecret');
} else {
$redirect_url = addon_url('wechat://common/login/callback', [ 'site_id' => $params[ 'site_id' ] ]);
$get_request_code_url = 'https://open.weixin.qq.com/connect/oauth2/authorize?appid=' . $value[ 'appid' ] . '&redirect_uri=' . $redirect_url . '&response_type=code&scope=snsapi_userinfo&state=niucloud#wechat_redirect';
$this->redirect($get_request_code_url);
}
}
}
}
}

View File

@@ -0,0 +1,17 @@
<?php
namespace addon\Wechat\event;
/**
* 应用卸载
*/
class UnInstall
{
/**
* 执行卸载
*/
public function handle()
{
return error('', 'System addon can not be uninstalled');
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace addon\system\Wechat\event;
use liliuwei\think\Jump;
use addon\system\Wechat\common\model\WechatMessage as WechatMessageModel;
class WechatMessage
{
use Jump;
/**
* 微信模板消息
* @param array $param
* @return array
*/
public function handle($param = [])
{
$wechat_message = new WechatMessageModel();
$res = $wechat_message->sendMessage($param);
return $res;
}
}

View File

@@ -0,0 +1,15 @@
<?php
namespace addon\system\Wechat\event;
use addon\system\Wechat\common\model\Wechat;
class WechatMsg
{
public function handle($param = [])
{
$wechat_config = new Wechat();
$res = $wechat_config->sendTemplateMsg(request()->siteid(), $param);
return $res;
}
}

BIN
src/addon/wechat/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1,128 @@
<?php
namespace addon\wechat\model;
use app\model\system\Config as ConfigModel;
use app\model\BaseModel;
use app\model\upload\Upload;
/**
* 微信公众号配置
*/
class Config extends BaseModel
{
/******************************************************************** 微信公众号配置 start ****************************************************************************/
/**
* 设置微信公众号配置
* @param $data
* @param $is_use
* @param $site_id
* @return array
*/
public function setWechatConfig($data, $is_use, $site_id)
{
$config_info = $this->getWechatConfig($site_id);
if (!empty($config_info[ 'data' ][ 'value' ][ 'qrcode' ]) && !empty($data[ 'qrcode' ]) && $config_info[ 'data' ][ 'value' ][ 'qrcode' ] != $data[ 'qrcode' ]) {
$upload_model = new Upload();
$upload_model->deletePic($config_info[ 'data' ][ 'value' ][ 'qrcode' ], $site_id);
}
$config = new ConfigModel();
$res = $config->setConfig($data, '微信公众号设置', $is_use, [ [ 'site_id', '=', $site_id ], [ 'app_module', '=', 'shop' ], [ 'config_key', '=', 'WECHAT_CONFIG' ] ]);
return $res;
}
/**
* 获取微信公众号配置信息
* @param $site_id
* @return array
*/
public function getWechatConfig($site_id)
{
$config = new ConfigModel();
$res = $config->getConfig([ [ 'site_id', '=', $site_id ], [ 'app_module', '=', 'shop' ], [ 'config_key', '=', 'WECHAT_CONFIG' ] ]);
if (empty($res[ 'data' ][ 'value' ])) {
$res[ 'data' ][ 'value' ] = [
'wechat_name' => '',
'wechat_original' => '',
'appid' => '',
'appsecret' => '',
'token' => '',
'encodingaeskey' => '',
'qrcode' => '',
'headimg' => ''
];
}
return $res;
}
/******************************************************************** 微信公众号配置 end ****************************************************************************/
/******************************************************************** 分享内容配置 start ****************************************************************************/
/**
* 设置分享内容
* @param $data
* @param $is_use
* @param $site_id
* @return array
*/
public function setShareConfig($data, $is_use, $site_id)
{
$config = new ConfigModel();
$res = $config->setConfig($data, '分享内容设置', $is_use, [ [ 'site_id', '=', $site_id ], [ 'app_module', '=', 'shop' ], [ 'config_key', '=', 'WECHAT_SHARE_CONFIG' ] ]);
return $res;
}
/**
* 获取分享内容
* @param $site_id
* @return array
*/
public function getShareConfig($site_id)
{
$config = new ConfigModel();
$res = $config->getConfig([ [ 'site_id', '=', $site_id ], [ 'app_module', '=', 'shop' ], [ 'config_key', '=', 'WECHAT_SHARE_CONFIG' ] ]);
if (empty($res[ 'data' ][ 'value' ])) {
$res[ 'data' ][ 'value' ] = [
'goods_param_1' => '优惠价',
'goods_param_2' => '全场正品',
'qrcode_param_1' => '向您推荐',
'qrcode_param_2' => '注册有优惠',
'shop_param_1' => '欢迎打开',
'shop_param_2' => '向您推荐',
'shop_param_3' => '注册有优惠'
];
}
return $res;
}
/******************************************************************** 分享内容配置 end ****************************************************************************/
/**
* 设置模板消息配置
* @param $data
* @param $is_use
* @param $site_id
* @return array
*/
public function setTemplateMessageConfig($data, $is_use, $site_id)
{
$config = new ConfigModel();
$res = $config->setConfig($data, '微信模板消息设置', $is_use, [ [ 'site_id', '=', $site_id ], [ 'app_module', '=', 'shop' ], [ 'config_key', '=', 'WECHAT_TEMPLATE_MESSAGE_CONFIG' ] ]);
return $res;
}
/**
* 获取模板消息配置
* @param $site_id
* @return array
*/
public function getTemplateMessageConfig($site_id)
{
$config = new ConfigModel();
$res = $config->getConfig([ [ 'site_id', '=', $site_id ], [ 'app_module', '=', 'shop' ], [ 'config_key', '=', 'WECHAT_TEMPLATE_MESSAGE_CONFIG' ] ]);
if (empty($res[ 'data' ][ 'value' ])) {
$res[ 'data' ][ 'value' ] = [ 'is_jump_weapp' => 0 ];
}
return $res;
}
}

View File

@@ -0,0 +1,267 @@
<?php
namespace addon\wechat\model;
use app\model\BaseModel;
/**
* 微信粉丝
*/
class Fans extends BaseModel
{
/***************************************************************** 微信粉丝 start ***************************************************************************/
/**
* 获取粉丝列表
* @param array $condition
* @param string $field
* @param string $order
* @param null $limit
* @return array
*/
public function getFansList($condition = [], $field = '*', $order = '', $limit = null)
{
$list = model('wechat_fans')->getList($condition, $field, $order, '', '', '', $limit);
return $this->success($list);
}
/**
* 获取粉丝分页列表
* @param array $condition
* @param int $page
* @param int $page_size
* @param string $order
* @param string $field
* @return array
*/
public function getFansPageList($condition = [], $page = 1, $page_size = PAGE_LIST_ROWS, $order = 'subscribe_time desc', $field = '*')
{
$list = model('wechat_fans')->pageList($condition, $field, $order, $page, $page_size);
return $this->success($list);
}
/**
* 为微信粉丝批量打标签
* @param $data
* @return array
*/
public function fansBatchTagging($data)
{
//调用微信接口服务器配置 修改微信端粉丝标签
$res = model('wechat_fans')->update([ 'tagid_list' => implode(',', $data[ 'tag_id_list' ]) ], [ 'openid' => [ 'in', $data[ 'openid_list' ] ] ]);
return $this->success($res);
}
/**
* 为微信粉丝批量取消标签
* @param $data
* @return array
*/
public function batchUnTagging($data)
{
return $this->success();
}
/**
* 同步粉丝标签
* @param $openid_list
* @return array
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function syncFans($openid_list)
{
$wechat_model = new Wechat();
$select_result = $wechat_model->selectUser($openid_list);//获取多个微信粉丝信息
if ($select_result[ "code" ] != 0)
return $select_result;
$user_list = $select_result[ "data" ][ "user_info_list" ];
$subscribe_scene_array = array (
"ADD_SCENE_SEARCH" => "公众号搜索",
"ADD_SCENE_ACCOUNT_MIGRATION" => "公众号迁移",
"ADD_SCENE_PROFILE_CARD" => "名片分享",
"ADD_SCENE_QR_CODE" => "扫描二维码",
"ADD_SCENE_PROFILE_ LINK" => "图文页内名称点击",
"ADD_SCENE_PROFILE_ITEM" => "图文页右上角菜单",
"ADD_SCENE_PAID" => "支付后关注",
"ADD_SCENE_OTHERS" => "其他",
);
foreach ($user_list as $user_k => $user_item) {
$unionid = empty($user_item[ "unionid" ]) ? '' : $user_item[ "unionid" ];
$province = base64_encode($user_item[ "province" ]);
$city = base64_encode($user_item[ "city" ]);
$nickname = base64_encode($user_item[ 'nickname' ]);
$nickname_decode = preg_replace('/[\x{10000}-\x{10FFFF}]/u', '', $user_item[ 'nickname' ]);
$data = array (
'nickname' => $nickname,
'nickname_decode' => $nickname_decode,
'headimgurl' => $user_item[ 'headimgurl' ],
'sex' => $user_item[ "sex" ],
'language' => $user_item[ "language" ],
'country' => $user_item[ "country" ],
'province' => $province,
'city' => $city,
'district' => '',
'openid' => $user_item[ "openid" ],
'unionid' => $unionid,
'groupid' => $user_item[ "groupid" ],
'is_subscribe' => $user_item[ "subscribe" ],
'remark' => $user_item[ "remark" ],
'update_date' => time(),
'tagid_list' => empty($user_item[ "tagid_list" ]) ? '' : implode(',', $user_item[ "tagid_list" ]),
'subscribe_scene' => $user_item[ "subscribe_scene" ],
'subscribe_scene_name' => $subscribe_scene_array[ $user_item[ "subscribe_scene" ] ],
'qr_scene' => $user_item[ "qr_scene" ],
'qr_scene_str' => $user_item[ "qr_scene_str" ],
'subscribe_time' => $user_item[ "subscribe_time" ],
);
$res = model("wechat_fans")->add($data);
}
return $this->success();
}
/***************************************************************** 微信粉丝 end ***************************************************************************/
/***************************************************************** 微信粉丝标签 start ***************************************************************************/
/**
* 增加粉丝标签
* @param $data
* @return array
*/
public function addFansTag($data)
{
$res = model('wechat_fans_tag')->add($data);
if ($res === false)
return $this->error('', 'UNKNOW_ERROR');
//同步修改微信端粉丝标签
return $this->success($res);
}
/**
* 编辑粉丝标签
* @param $data
* @param $condition
* @return array
*/
public function editFansTag($data, $condition)
{
$res = model('wechat_fans_tag')->update($data, $condition);
if ($res === false) {
return $this->error('', 'UNKNOW_ERROR');
}
//同步修改微信端粉丝标签
return $this->success($res);
}
/**
* 删除标签
* @param $condition
* @return array
*/
public function deleteFansTag($condition)
{
$res = model('wechat_fans_tag')->delete($condition);
return $this->success($res);
}
/**
* 获取粉丝列表
* @param array $condition
* @param string $field
* @param string $order
* @param null $limit
* @return array
*/
public function getFansTagList($condition = [], $field = '*', $order = '', $limit = null)
{
$list = model('wechat_fans_tag')->getList($condition, $field, $order, '', '', '', $limit);
return $this->success($list);
}
/**
* 获取微信粉丝标签列表
* @param array $condition
* @param int $page
* @param int $list_rows
* @param string $order
* @param bool $field
* @return array
*/
public function getFansTagPageList($condition = [], $page = 1, $list_rows = PAGE_LIST_ROWS, $order = '', $field = true)
{
$list = model('wechat_fans_tag')->pageList($condition, $field, $order, $page, $list_rows);
return $this->success($list);
}
/**
* 同步粉丝标签
* @return array
*/
public function syncFansTag()
{
return $this->success();
}
/***************************************************************** 微信粉丝标签 end ***************************************************************************/
/**
* 增加粉丝
* @param $data
* @return array
*/
public function addFans($data)
{
$res = model('wechat_fans')->add($data);
return $this->success($res);
}
/**
* 取消关注
* @param $open_id
* @return array
*/
public function unfollowWechat($open_id)
{
$data = array (
'is_subscribe' => 0,
'update_date' => time(),
'unsubscribe_time' => time()
);
$wechat_fans_model = model('wechat_fans');
$res = $wechat_fans_model->update($data, [ 'openid' => $open_id ]);
return success($res);
}
/**
* 粉丝详情
* @param $condition
* @param string $field
* @return array
*/
public function getFansInfo($condition, $field = "*")
{
if (!empty($cache)) {
return $this->success($cache);
}
$info = model('wechat_fans')->getInfo($condition, $field);
return $this->success($info);
}
/**
* 编辑粉丝
* @param $data
* @param $condition
* @return array
*/
public function editFans($data, $condition)
{
$data[ "update_date" ] = time();
$result = model("wechat_fans")->update($data, $condition);
return $this->success($result);
}
}

View File

@@ -0,0 +1,88 @@
<?php
namespace addon\wechat\model;
use app\model\BaseModel;
/**
* 微信素材管理
*/
class Material extends BaseModel
{
/**
* 添加微信素材
* @param $data
* @return array
*/
public function addMaterial($data)
{
$res = model('wechat_media')->add($data);
return $this->success($res);
}
/**
* 修改微信素材
* @param $data
* @param $condition
* @return array
*/
public function editMaterial($data, $condition)
{
$res = model('wechat_media')->update($data, $condition);
return $this->success($res);
}
/**
* 删除微信素材
* @param $condition
* @return array
*/
public function deleteMaterial($condition)
{
$res = model('wechat_media')->delete($condition);
return $this->success($res);
}
/**
* 获取微信素材信息
* @param $condition
* @param string $field
* @return array
*/
public function getMaterialInfo($condition, $field = '*')
{
$res = model('wechat_media')->getInfo($condition, $field);
return $this->success($res);
}
/**
* 获取微信素材列表
* @param array $condition
* @param string $field
* @param string $order
* @param null $limit
* @return array
*/
public function getMaterialList($condition = [], $field = '*', $order = '', $limit = null)
{
$res = model('wechat_media')->getList($condition, $field, $order, '', '', '', $limit);
return $this->success($res);
}
/**
* 获取微信素材分页列表
* @param array $condition
* @param int $page
* @param int $page_size
* @param string $order
* @param string $field
* @return array
*/
public function getMaterialPageList($condition = [], $page = 1, $page_size = PAGE_LIST_ROWS, $order = 'update_time desc', $field = '*')
{
$list = model('wechat_media')->pageList($condition, $field, $order, $page, $page_size);
return $this->success($list);
}
}

View File

@@ -0,0 +1,40 @@
<?php
namespace addon\wechat\model;
use app\model\system\Config as ConfigModel;
use app\model\BaseModel;
/**
* 微信公众号配置
*/
class Menu extends BaseModel
{
/******************************************************************** 微信公众号菜单配置 start ****************************************************************************/
/**
* 设置微信公众号配置
* @param $data
* @param int $site_id
* @return array
*/
public function setWechatMenuConfig($data, $site_id = 0)
{
$config = new ConfigModel();
$res = $config->setConfig($data, '微信公众号设置', 1, [ [ 'site_id', '=', $site_id ], [ 'app_module', '=', 'shop' ], [ 'config_key', '=', 'WECHAT_MENU_CONFIG' ] ]);
return $res;
}
/**
* 微信公众号菜单配置
* @param int $site_id
* @return array
*/
public function getWechatMenuConfig($site_id = 0)
{
$config = new ConfigModel();
$res = $config->getConfig([ [ 'site_id', '=', $site_id ], [ 'app_module', '=', 'shop' ], [ 'config_key', '=', 'WECHAT_MENU_CONFIG' ] ]);
return $res;
}
/******************************************************************** 微信公众号菜单配置 end ****************************************************************************/
}

View File

@@ -0,0 +1,97 @@
<?php
namespace addon\wechat\model;
use app\model\BaseModel;
use addon\weapp\model\Config as WeappConfig;
use addon\mobileshop\model\Config as MobileShopConfig;
/**
* 微信消息模板
*/
class Message extends BaseModel
{
/**
* 发送模板消息
* @param array $param
* @return array
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function sendMessage(array $param)
{
try {
$site_id = $param[ 'site_id' ];
$support_type = $param[ "support_type" ] ?? [];
if (!empty($support_type) && !in_array("wechat", $support_type)) return $this->success();
if (empty($param[ 'openid' ])) return $this->success('缺少必需参数openid');
$message_info = $param[ 'message_info' ];
if ($message_info[ 'wechat_is_open' ] == 0) return $this->error('未启用模板消息');
$wechat_info = json_decode($message_info[ 'wechat_json' ], true);
if (empty($message_info[ 'wechat_template_id' ])) return $this->error('未配置模板消息');
$template_data = [
// 'first' => [
// 'value' => $wechat_info[ 'headtext' ],
// 'color' => !empty($wechat_info[ 'headtextcolor' ]) ? $wechat_info[ 'headtextcolor' ] : '#f00'
// ],
// 'remark' => [
// 'value' => $wechat_info[ 'bottomtext' ],
// 'color' => !empty($wechat_info[ 'bottomtextcolor' ]) ? $wechat_info[ 'bottomtextcolor' ] : '#333'
// ]
];
if (!empty($param[ 'template_data' ])) $template_data = array_merge($template_data, $param[ 'template_data' ]);
$data = [
'openid' => $param[ 'openid' ],
'template_id' => $message_info[ 'wechat_template_id' ],
'data' => $template_data,
'miniprogram' => [],
'url' => ""
];
if (!empty($param[ 'page' ])) {
// 商家消息
if ($message_info[ 'message_type' ] == 2 && addon_is_exit('mobileshop', $site_id)) {
$config = new MobileShopConfig();
$weapp_config = $config->getWeappConfig($site_id)[ 'data' ][ "value" ];
if (!empty($weapp_config[ 'appid' ])) {
$data[ 'miniprogram' ] = [
'appid' => $weapp_config[ 'appid' ],
'pagepath' => $param[ 'page' ]
];
}
$mshop_config = $config->getMShopDomainName($site_id)[ 'data' ][ 'value' ];
$data[ 'url' ] = $mshop_config[ 'domain_name_mobileshop' ] . '/' . $param[ 'page' ];
} else {
$template_config_model = new Config();
$template_config = $template_config_model->getTemplateMessageConfig($site_id)[ 'data' ][ 'value' ];
if ($template_config[ 'is_jump_weapp' ]) {
// 小程序配置
$weapp_config = new WeappConfig();
$weapp_config = $weapp_config->getWeappConfig($site_id)[ 'data' ][ "value" ];
if (!empty($weapp_config[ 'appid' ])) {
$data[ 'miniprogram' ] = [
'appid' => $weapp_config[ 'appid' ],
'pagepath' => $param[ 'page' ]
];
}
}
$data[ 'url' ] = getH5Domain() . '/' . $param[ 'page' ];
}
}
$wechat = new Wechat($site_id);
$res = $wechat->sendTemplateMessage($data);
return $res;
} catch (\Exception $e) {
return $this->error('', "模板消息发送失败:" . $e->getMessage() . ",File" . $e->getFile() . "line" . $e->getLine());
}
}
}

View File

@@ -0,0 +1,121 @@
<?php
namespace addon\system\Wechat\common\model;
use app\model\BaseModel;
/**
* 微信模板消息消息发送记录
*/
class MessageRecords extends BaseModel
{
/**
* 新建微信模板消息发送记录
* @param $data
* @return array
*/
public function addWechatMessageRecords($data)
{
$id = model("nc_site_message_wechat_list")->add($data);
if ($id === false) {
return $this->error('', 'UNKNOW_ERROR');
}
return $this->success($id);
}
/**
* 批量添加微信模板消息发送记录
* @param $data
* @return array
*/
public function addWechatMessageRecordsList($data)
{
$id = model("nc_site_message_wechat_list")->addList($data);
if ($id === false) {
return $this->error('', 'UNKNOW_ERROR');
}
return $this->success($id);
}
/**
* 编辑微信模板消息发送记录
* @param $data
* @param $condition
* @return array
*/
public function editWechatMessageRecords($data, $condition)
{
$res = model("nc_site_message_wechat_list")->update($data, $condition);
if ($res === false) {
return $this->error('', 'UNKNOW_ERROR');
}
return $this->success($res);
}
/**
* 删除微信模板消息发送记录
* @param $condition
* @return array
*/
public function deleteWechatMessageRecords($condition)
{
$res = model("nc_site_message_wechat_list")->delete($condition);
if ($res === false) {
return $this->error('', 'UNKNOW_ERROR');
}
return $this->success($res);
}
/**
* 查询微信模板消息发送记录
* @param $condition
* @param string $field
* @return array
*/
public function getWechatMessageRecordsInfo($condition, $field = "*")
{
$res = model("nc_site_message_wechat_list")->getInfo($condition, $field);
if (empty($res)) {
return $this->error('', 'UNKNOW_ERROR');
}
return $this->success($res);
}
/**
* 查询微信模板消息发送记录
* @param $condition
* @param string $field
* @return array
*/
public function getWechatMessageRecordsList($condition, $field = "*")
{
$res = model("nc_site_message_wechat_list")->getList($condition, $field);
if (empty($res)) {
return $this->error('', 'UNKNOW_ERROR');
}
return $this->success($res);
}
/**
* 查询微信模板消息发送记录分页列表
* @param array $condition
* @param int $page
* @param int $page_size
* @param string $order
* @param string $field
* @return array
*/
public function getWechatMessageRecordsPageList($condition = [], $page = 1, $page_size = PAGE_LIST_ROWS, $order = '', $field = '*')
{
$res = model("nc_site_message_wechat_list")->pageList($condition, $field, $order, $page, $page_size);
if (empty($res)) {
return $this->error('', 'UNKNOW_ERROR');
}
return $this->success($res);
}
}

View File

@@ -0,0 +1,95 @@
<?php
namespace addon\wechat\model;
use app\model\BaseModel;
use think\facade\Cache;
/**
* 微信二维码
*/
class Qrcode extends BaseModel
{
/***************************************************************** 微信二维码 start ***************************************************************************/
/**
* 添加微信二维码
* @param $data
* @return array
*/
public function addQrcode($data)
{
$data[ "update_time" ] = time();
Cache::tag("wechat_qrcode")->clear();
$result = model("wechat_qrcode")->add($data);
return $this->success($result);
}
/**
* 编辑微信二维码
* @param $data
* @param $condition
* @return array
*/
public function editQrcode($data, $condition)
{
$data[ "update_time" ] = time();
Cache::tag("wechat_qrcode")->clear();
$result = model("wechat_qrcode")->update($data, $condition);
return $this->success($result);
}
/**
* 删除微信二维码
* @param $condition
* @return array
*/
public function deleteQrcode($condition)
{
Cache::tag("wechat_qrcode")->clear();
$result = model("wechat_qrcode")->delete($condition);
return $this->success($result);
}
/**
* 设置默认二维码模板
* @param $condition
* @return array
*/
public function modifyQrcodeDefault($condition)
{
//将全部模板设置为非默认
Cache::tag("wechat_qrcode")->clear();
model("wechat_qrcode")->update([ "is_default" => 0 ], [ 'is_default' => 1 ]);
$res = model("wechat_qrcode")->update([ "is_default" => 1 ], $condition);
return $this->success($res);
}
/**
* 获取二维码模板详情
* @param $condition
* @param string $field
* @return array
*/
public function getQrcodeInfo($condition, $field = "*")
{
$info = model('wechat_qrcode')->getInfo($condition, $field);
return $this->success($info);
}
/**
* 获取微信二维码列表
* @param array $condition
* @param int $page
* @param int $page_size
* @param string $order
* @param string $field
* @return array
*/
public function getQrcodePageList($condition = [], $page = 1, $page_size = PAGE_LIST_ROWS, $order = '', $field = '*')
{
$list = model('wechat_qrcode')->pageList($condition, $field, $order, $page, $page_size);
return $this->success($list);
}
/***************************************************************** 微信粉丝 end ***************************************************************************/
}

View File

@@ -0,0 +1,153 @@
<?php
namespace addon\wechat\model;
use app\model\BaseModel;
/**
* 微信回复
*/
class Replay extends BaseModel
{
/*******************************************************************************微信回复开始*****************************************************/
/**
* 添加微信关键词回复
* @param $data
* @return array
*/
public function addRule($data)
{
$res = model('wechat_replay_rule')->add($data);
if ($res === false) {
return $this->error($res, 'SAVE_FAIL');
}
return $this->success($res, 'SAVE_SUCCESS');
}
/**
* 修改微信关键词回复
* @param $data
* @param $condition
* @return array
*/
public function editRule($data, $condition)
{
$res = model('wechat_replay_rule')->update($data, $condition);
if ($res === false) {
return $this->error($res, 'SAVE_FAIL');
}
return $this->success($res, 'SAVE_SUCCESS');
}
/**
* 删除微信关键词回复
* @param $condition
* @return array
*/
public function deleteRule($condition)
{
$res = model('wechat_replay_rule')->delete($condition);
if ($res === false) {
return $this->error($res, 'DELETE_FAIL');
}
return $this->success($res, 'DELETE_SUCCESS');
}
/**
* 获取微信关键词回复信息
* @param $condition
* @param string $field
* @return array
*/
public function getRuleInfo($condition, $field = '*')
{
$info = model('wechat_replay_rule')->getInfo($condition, $field);
return $this->success($info);
}
/**
* 获取回复列表
* @param array $condition
* @param int $page
* @param int $page_size
* @param string $order
* @param string $field
* @return array
*/
public function getReplayPageList($condition = [], $page = 1, $page_size = PAGE_LIST_ROWS, $order = '', $field = '*')
{
$list = model('wechat_replay_rule')->pageList($condition, $field, $order, $page, $page_size);
return $this->success($list);
}
/**
* 获取站点关键字回复
* @param $keywords
* @param $site_id
* @return array
*/
public function getSiteWechatKeywordsReplay($keywords, $site_id)
{
$list = model('wechat_replay_rule')->getList([['rule_type', '=', 'KEYWORDS'], ['site_id', '=', $site_id]]);
$default_list = model('wechat_replay_rule')->getList([['rule_type', '=', 'DEFAULT'], ['site_id', '=', $site_id]]);
$rule_list = array();
foreach ($list as $k => $v) {
$kewords_array = json_decode($v['keywords_json'], true);
$replay_array = json_decode($v['replay_json'], true);
if (!empty($kewords_array) && !empty($replay_array)) {
foreach ($kewords_array as $k_key => $v_key) {
if (($v_key['keywords_type'] == 0 && $v_key['keywords_name'] == $keywords) || ($v_key['keywords_type'] == 1 && (strpos($keywords, $v_key['keywords_name']) !== false))) {
$num = rand(0, count($replay_array) - 1);
$rule_list[] = $replay_array[$num];
}
}
}
}
if (empty($rule_list)) {
$default_rule_list = array();
foreach ($default_list as $kk => $vv) {
$default_replay_array = json_decode($vv['replay_json'], true);
if (!empty($default_replay_array)) {
$default_rule_list[] = $default_replay_array[0];
}
}
if (!empty($default_rule_list)) {
$rule = $default_rule_list[0];
} else {
$rule = [];
}
} else {
$rule = $rule_list[0];
}
return $this->success($rule);
}
/**
* 获取微信关注回复
* @param $site_id
* @return array
*/
function getWechatFollowReplay($site_id)
{
$follow_info = model('wechat_replay_rule')->getInfo([['rule_type', '=', 'AFTER'], ['site_id', '=', $site_id]]);
$replay_content = '';
if (!empty($follow_info['replay_json'])) {
$replay_info = json_decode($follow_info['replay_json'], true);
switch ($replay_info[0]['type']) {
case 'text' :
$replay_content = $replay_info[0]['reply_content'];
break;
case 'articles' :
$replay_content = '';
break;
}
}
return $this->success($replay_content);
}
/*******************************************************************************微信回复结束*****************************************************/
}

View File

@@ -0,0 +1,273 @@
<?php
namespace addon\wechat\model;
use app\model\BaseModel;
use think\facade\Cache;
/**
* 数据统计与分析
*/
class Stat extends BaseModel
{
/**
* 获取用户增减数据, 最大时间跨度7
* @param $begin_date
* @param $end_date
* @return array
*/
public function userSummary($begin_date, $end_date)
{
$cache = Cache::get("wechat_user_summary_" . "_" . $begin_date . "_" . $end_date);
if (!empty($cache))
return $this->success($cache);
$wechat_model = new Wechat();
$result = $wechat_model->userSummary($begin_date, $end_date);
if ($result[ "code" ] < 0)
return $result;
Cache::tag("wechat_visit")->set("wechat_user_summary_" . "_" . $begin_date . "_" . $end_date, $result);
return $result;
}
/**
* 获取累计用户数据, 最大时间跨度7;
* @param $begin_date
* @param $end_date
* @return array
*/
public function userCumulate($begin_date, $end_date)
{
$cache = Cache::get("wechat_user_cumulate_" . "_" . $begin_date . "_" . $end_date);
if (!empty($cache))
return $this->success($cache);
$wechat_model = new Wechat();
$result = $wechat_model->userCumulate($begin_date, $end_date);
if ($result[ "code" ] < 0)
return $result;
Cache::tag("wechat_visit")->set("wechat_user_cumulate_" . "_" . $begin_date . "_" . $end_date, $result);
return $result;
}
/**
* 获取接口分析分时数据, 最大时间跨度1;
* @param $begin_date
* @param $end_date
* @return array
*/
public function interfaceSummaryHourly($begin_date, $end_date)
{
$cache = Cache::get("wechat_interface_summary_hourly_" . "_" . $begin_date . "_" . $end_date);
if (!empty($cache))
return $this->success($cache);
$wechat_model = new Wechat();
$result = $wechat_model->interfaceSummaryHourly($begin_date, $end_date);
if ($result[ "code" ] < 0)
return $result;
Cache::tag("wechat_visit")->set("wechat_interface_summary_hourly_" . "_" . $begin_date . "_" . $end_date, $result);
return $result;
}
/**
* 获取接口分析数据, 最大时间跨度30;
* @param $begin_date
* @param $end_date
* @return array
*/
public function interfaceSummary($begin_date, $end_date)
{
// $cache = Cache::get("wechat_interface_summary_" . "_" . $begin_date."_" . $end_date);
// if (!empty($cache))
// return $this->success($cache);
$wechat_model = new Wechat();
$result = $wechat_model->interfaceSummary($begin_date, $end_date);
if ($result[ "code" ] < 0)
return $result;
Cache::tag("wechat_visit")->set("wechat_interface_summary_" . "_" . $begin_date . "_" . $end_date, $result);
return $result;
}
/**
* 获取微信粉丝统计(以天为单位)
* @param $begin_date
* @param $end_date
* @return array
*/
public function fans($begin_date, $end_date)
{
// $cache = Cache::get("wechat_fans_statistics_" . "_" . $begin_date."_" . $end_date);
// if (!empty($cache)) {
// return $this->success($cache);
// }
// $data = [ 'begin_date' => $param['begin_date'], 'end_date' => $param['end_date'] ];
$user_summary_result = $this->userSummary($begin_date, $end_date);
if ($user_summary_result[ "code" ] < 0) {
return $user_summary_result;
}
$user_cumulate_result = $this->userCumulate($begin_date, $end_date);
if ($user_cumulate_result[ "code" ] < 0) {
return $user_cumulate_result;
}
$list = [];
foreach ($user_cumulate_result[ "data" ] as $cumulate_k => $cumulate_v) {
$temp_item = $cumulate_v;
$temp_item[ 'cumulate_user' ] = empty($cumulate_v[ 'cumulate_user' ]) ? 0 : $cumulate_v[ 'cumulate_user' ];
$new_user = 0;
$cancel_user = 0;
$net_growth_user = 0;
foreach ($user_summary_result[ 'data' ] as $key => $item) {
if ($item[ "ref_date" ] == $cumulate_v[ "ref_date" ]) {
$new_user += $item[ 'new_user' ];
$cancel_user += $item[ 'cancel_user' ];
$net_growth_user += $item[ "new_user" ] - $item[ "cancel_user" ];
}
}
$temp_item[ 'new_user' ] = $new_user;
$temp_item[ 'cancel_user' ] = $cancel_user;
$temp_item[ 'net_growth_user' ] = $net_growth_user;
$list[] = $temp_item;
}
Cache::tag("wechat_visit")->set("wechat_fans_statistics_" . "_" . $begin_date . "_" . $end_date, $list);
return $this->success($list);
}
/**
* 接口访问数据图
* @param $date_type
* @return array
*/
public function interfaceSummaryStatistics($date_type)
{
$date_data = $this->getDaterange($date_type);
$is_error = true;
$callback_count_data = [];
$fail_count_data = [];
$average_time_cost_data = [];
$max_time_cost_data = [];
foreach ($date_data[ "date_list" ] as $k => $v) {
$callback_count = 0;
$fail_count = 0;
$average_time_cost = 0;
$max_time_cost = 0;
if ($is_error) {
$temp_data = $this->interfaceSummary($v, $v);
if (!empty($temp_data[ "data" ])) {
$temp_date_item = $temp_data[ "data" ];
$callback_count = $temp_date_item[ 0 ][ "callback_count" ];
$fail_count = $temp_date_item[ 0 ][ "fail_count" ];
$average_time_cost = $temp_date_item[ 0 ][ "total_time_cost" ] / $temp_date_item[ 0 ][ "callback_count" ];
$max_time_cost = $temp_date_item[ 0 ][ "max_time_cost" ];
} else {
$is_error = false;
}
}
$callback_count_data[] = $callback_count;
$fail_count_data[] = $fail_count;
$average_time_cost_data[] = $average_time_cost;
$max_time_cost_data[] = $max_time_cost;
}
$return_data = array (
"date" => $date_data[ "date_list" ],
"data" => array (
"callback_count_data" => $callback_count_data,
"fail_count_data" => $fail_count_data,
"average_time_cost_data" => $average_time_cost_data,
"max_time_cost_data" => $max_time_cost_data
)
);
return $this->success($return_data);
}
/**
* 用户访问统计图
* @param $date_type
* @return array
*/
public function userSummaryStatistics($date_type)
{
$is_error = true;
$date_data = $this->getDaterange($date_type);
$new_user_data = [];
$cancel_user_data = [];
$net_growth_user_data = [];
$cumulate_user_data = [];
foreach ($date_data[ "date_list" ] as $k => $v) {
$new_user = 0;
$cancel_user = 0;
$net_growth_user = 0;
$cumulate_user = 0;
if ($is_error) {
$temp_data = $this->fans($v, $v);
if (!empty($temp_data[ "data" ])) {
$temp_date_item = $temp_data[ "data" ];
$new_user = $temp_date_item[ 0 ][ "new_user" ];
$cancel_user = $temp_date_item[ 0 ][ "cancel_user" ];
$net_growth_user = $temp_date_item[ 0 ][ "net_growth_user" ];
$cumulate_user = $temp_date_item[ 0 ][ "cumulate_user" ];
} else {
$is_error = false;
}
}
$new_user_data[] = $new_user;
$cancel_user_data[] = $cancel_user;
$net_growth_user_data[] = $net_growth_user;
$cumulate_user_data[] = $cumulate_user;
}
$return_data = array (
"date" => $date_data[ "date_list" ],
"data" => array (
"new_user_data" => $new_user_data,
"cancel_user_data" => $cancel_user_data,
"net_growth_user_data" => $net_growth_user_data,
"cumulate_user_data" => $cumulate_user_data
)
);
return $this->success($return_data);
}
/**
* 获取时间间隔
* @param $date_type
* @return array
*/
public function getDaterange($date_type)
{
$yesterday = strtotime('-1 days');
switch ( $date_type ) {
case 'yesterday':
$yesterday = strtotime('-1 days');
$begin_time = $yesterday;
$end_time = $yesterday;
break;
case 'week':
$week = strtotime('-7 days');
$begin_time = $week;
$end_time = $yesterday;
break;
case 'month':
$month = strtotime('-30 days');
$begin_time = $month;
$end_time = $yesterday;
break;
}
$date_x = periodGroup($begin_time, $end_time, "Y-m-d");
$begin_date = date("Ymd", $begin_time);
$end_date = date("Ymd", $end_time);
$data = array (
"begin_date" => $begin_date,
"end_date" => $end_date,
"date_list" => $date_x,
);
return $data;
}
}

View File

@@ -0,0 +1,991 @@
<?php
namespace addon\wechat\model;
use addon\wxoplatform\model\OpenPlatform;
use addon\wxoplatform\model\Oplatform;
use EasyWeChat\Factory;
use EasyWeChat\Kernel\Messages\Article;
use EasyWeChat\Kernel\Messages\Text;
use EasyWeChat\Kernel\Messages\News;
use EasyWeChat\Kernel\Messages\NewsItem;
use app\model\BaseModel;
use think\facade\Cache;
use think\facade\Log;
/**
* 微信公众号
*/
class Wechat extends BaseModel
{
public $app;//微信公众对象
private $site_id;//站点
//公众号类型
public $service_type = array (
0 => "订阅号",
1 => "由历史老帐号升级后的订阅号",
2 => "服务号",
);
//公众号认证类型
public $verify_type = array (
-1 => "未认证",
0 => "微信认证",
1 => "新浪微博认证",
2 => "腾讯微博认证",
3 => "已资质认证通过但还未通过名称认证",
4 => "已资质认证通过、还未通过名称认证,但通过了新浪微博认证",
5 => "已资质认证通过、还未通过名称认证,但通过了腾讯微博认证",
);
//business_info 说明
public $business_type = array (
'open_store' => "是否开通微信门店功能",
'open_scan' => "是否开通微信扫商品功能",
'open_pay' => "是否开通微信支付功能",
'open_card' => "是否开通微信卡券功能",
'open_shake' => "是否开通微信摇一摇功能",
);
public function __construct($site_id = 0)
{
$this->site_id = $site_id;
// $response = $this->app->server->serve();
// 将响应输出
// $response->send();exit; // Laravel 里请使用return $response;
}
/**
* 授权实例
*/
public function app()
{
$config_model = new Config();
$config_result = $config_model->getWechatConfig($this->site_id);
$config = $config_result[ "data" ];
if (!empty($config)) {
$config_info = $config[ "value" ];
}
//授权方式 手动授权 或 自动授权(通过开放平台)
$auth_type = $config_info[ "is_authopen" ] ?? '';
if ($auth_type == "1") {
$oplatform_model = new Oplatform();
$config_info[ 'type' ] = 'wechat';
$this->app = $oplatform_model->app($config_info);
} else {
$config = [
'app_id' => $config_info[ "appid" ] ?? '',
'secret' => $config_info[ "appsecret" ] ?? '',
'token' => $config_info[ "token" ] ?? '', // Token
'aes_key' => $config_info[ 'encodingaeskey' ] ?? '', // EncodingAESKey兼容与安全模式下请一定要填写
// 指定 API 调用返回结果的类型array(default)/collection/object/raw/自定义类名
'response_type' => 'array',
/**
* 日志配置
*
* level: 日志级别, 可选为debug/info/notice/warning/error/critical/alert/emergency
* permission日志文件权限(可选)默认为null若为null值,monolog会取0644
* file日志文件位置(绝对路径!!!),要求可写权限
*/
'log' => [
'level' => 'debug',
'permission' => 0777,
'file' => 'runtime/log/wechat/easywechat.logs',
],
];
if (empty($config[ 'app_id' ]) || empty($config[ 'secret' ])) {
throw new \Exception('商家公众号配置有误,请联系平台管理员');
}
$this->app = Factory::officialAccount($config);
}
return $this->app;
}
/**
* 创建微信菜单
* @param $buttons
* @return array
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function menu($buttons)
{
try {
$result = $this->app()->menu->create($buttons);
if ($result[ 'errcode' ] == 0) {
return $this->success();
} else {
return $this->error($result, $result[ "errmsg" ]);
}
} catch (\Exception $e) {
return $this->error([], $e->getMessage());
}
}
/**
* 拉取粉丝列表
* @param null $nextOpenId
* @return array
*/
public function user($nextOpenId = null)
{
try {
$result = $this->app()->user->list($nextOpenId); // $nextOpenId 可选
if (isset($result[ 'errcode' ]) && $result[ 'errcode' ] != 0) {
return $this->error($result, $result[ "errmsg" ]);
}
return $this->success($result);
} catch (\Exception $e) {
return $this->error([], $e->getMessage());
}
}
/**
* 查询多个粉丝信息
* @param $user_list
* @return array
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function selectUser($user_list)
{
try {
$result = $this->app()->user->select($user_list); // $nextOpenId 可选
if (isset($result[ 'errcode' ]) && $result[ 'errcode' ] != 0) {
return $this->error($result, $result[ "errmsg" ]);
}
return $this->success($result);
} catch (\Exception $e) {
return $this->error([], $e->getMessage());
}
}
/**
* 得到粉丝信息
* @param $openId
* @return array
*/
public function getUser($openId)
{
try {
$result = $this->app()->user->get($openId); // $nextOpenId 可选
if (isset($result[ 'errcode' ]) && $result[ 'errcode' ] != 0) {
return $this->error($result, $result[ "errmsg" ]);
}
return $this->success($result);
} catch (\Exception $e) {
return $this->error([], $e->getMessage());
}
}
/**
* 根据 Code 获取用户 session 信息
* @param array $params
* @return array
*/
public function authCodeToOpenid($params = [])
{
try {
if (empty($_REQUEST[ "code" ])) {
$auth_result = $this->getAuthCode(request()->url(true));
if ($auth_result[ "code" ] >= 0) {
header("Location: " . $auth_result[ "data" ]);
} else {
return $auth_result;
}
}
$user = $this->app()->oauth->user();//获取授权用户
$data = [
'openid' => $user->getId(),
'userinfo' => [
'avatarUrl' => $user->getAvatar(),
'nickName' => $user->getNickname()
]
];
return $this->success($data);
} catch (\Exception $e) {
if (property_exists($e, 'body')) {
return $this->handleError($e->body[ 'errcode' ], $e->body[ 'errmsg' ]);
}
return $this->error([], $e->getMessage());
}
}
/**
* 根据code获取授权信息
* @param $params
* @return array
*/
public function getAuthByCode($params)
{
try {
$user = $this->app()->oauth->userByCode($params[ 'code' ]);//获取授权用户
$original = $user->getOriginal();
$data = [
'openid' => $original[ 'openid' ],
'userinfo' => [
'avatarUrl' => $original[ 'headimgurl' ] ?? '',
'nickName' => $original[ 'nickname' ] ?? ''
]
];
if (isset($original[ 'unionid' ])) {
$data[ 'unionid' ] = $original[ 'unionid' ];
}
return $this->success($data);
} catch (\Exception $e) {
if (property_exists($e, 'body')) {
return $this->handleError($e->body[ 'errcode' ], $e->body[ 'errmsg' ]);
}
return $this->error([], $e->getMessage());
}
}
/**
* 获取公众号网页授权code
* @param $redirect
* @param string $scopes snsapi_base:静默授权 snsapi_userinfo:用户授权
* @return array
*/
public function getAuthCode($redirect, $scopes = 'snsapi_base')
{
try {
$response = $this->app()->oauth->scopes([ $scopes ])->redirect($redirect);
return $this->success($response->getTargetUrl());
} catch (\Exception $e) {
return $this->error([], $e->getMessage());
}
}
/**
* 获取永久二维码
* @param $uid
* @return array
* @throws \Exception
*/
public function getQrcode($uid)
{
$result = $this->app()->qrcode->forever($uid);
if (isset($result[ 'errcode' ]) && $result[ 'errcode' ] != 0) {
return $this->error($result, $result[ "errmsg" ]);
}
$url = $this->app()->qrcode->url($result[ 'data' ][ 'ticket' ]);
if (isset($url[ 'errcode' ]) && $url[ 'errcode' ] != 0) {
return $this->error($url, $url[ "errmsg" ]);
}
return $this->success($url);
}
/**
* 获取临时二维码
* @param $scene
* @param $seconds
* @return array
*/
public function getTempQrcode($scene, $seconds)
{
try {
$result = $this->app()->qrcode->temporary($scene, $seconds);
if (isset($result[ 'errcode' ]) && $result[ 'errcode' ] != 0) {
return $this->error($result, $result[ "errmsg" ]);
}
$url = $this->app()->qrcode->url($result[ 'ticket' ]);
if (isset($url[ 'errcode' ]) && $url[ 'errcode' ] != 0) {
return $this->handleError($url[ 'errcode' ], $url[ "errmsg" ]);
}
return $this->success($url);
} catch (\Exception $e) {
if (property_exists($e, 'body')) {
return $this->handleError($e->body[ 'errcode' ], $e->body[ 'errmsg' ]);
}
return $this->error([], $e->getMessage());
}
}
/**
* 获取jssdk配置
* @param $url
* @return array
*/
public function getJssdkConfig($url)
{
try {
$this->app()->jssdk->setUrl($url);
$res = $this->app->jssdk->buildConfig([], false, false, false);
return $this->success($res);
} catch (\Exception $e) {
if (property_exists($e, 'body')) {
return $this->handleError($e->body[ 'errcode' ], $e->body[ 'errmsg' ]);
}
return $this->error([], $e->getMessage());
}
}
/********************************************************** 数据统计与分析start *******************************************************************/
/********************************************************** 数据统计与分析end *******************************************************************/
/*******************************************************************************微信接口连接开始*****************************************************/
/*******************************************************************************微信接口连接结束*****************************************************/
/***************************************************************** 微信公众号 统计 start *****************************************************************************************/
/***************************************************************** 微信公众号 统计 end *****************************************************************************************/
/****************************************************************************** 数据统计与分析***********************************************************/
/**
* 获取用户增减数据, 最大时间跨度7;
* @param $from
* @param $to
* @return array
*/
public function userSummary($from, $to)
{
try {
$result = $this->app()->data_cube->userSummary($from, $to);
if (isset($result[ 'errcode' ]) && $result[ 'errcode' ] != 0) {
return $this->error([], $result[ "errmsg" ]);
}
return $this->success($result[ "list" ]);
} catch (\Exception $e) {
return $this->error([], $e->getMessage());
}
}
/**
* 获取累计用户数据, 最大时间跨度7
* @param $from
* @param $to
* @return array
*/
public function userCumulate($from, $to)
{
try {
$result = $this->app()->data_cube->userCumulate($from, $to);
if (isset($result[ 'errcode' ]) && $result[ 'errcode' ] != 0) {
return $this->error([], $result[ "errmsg" ]);
}
return $this->success($result[ "list" ]);
} catch (\Exception $e) {
return $this->error([], $e->getMessage());
}
}
/**
* 获取接口分析分时数据, 最大时间跨度1;
* @param $from
* @param $to
* @return array
*/
public function interfaceSummaryHourly($from, $to)
{
try {
$result = $this->app()->data_cube->interfaceSummaryHourly($from, $to);
if (isset($result[ 'errcode' ]) && $result[ 'errcode' ] != 0) {
return $this->error([], $result[ "errmsg" ]);
}
return $this->success($result[ "list" ]);
} catch (\Exception $e) {
return $this->error([], $e->getMessage());
}
}
/**
* 获取接口分析数据, 最大时间跨度30;
* @param $from
* @param $to
* @return array
*/
public function interfaceSummary($from, $to)
{
try {
$result = $this->app()->data_cube->interfaceSummary($from, $to);
if (isset($result[ 'errcode' ]) && $result[ 'errcode' ] != 0) {
return $this->error([], $result[ "errmsg" ]);
}
return $this->success($result[ "list" ]);
} catch (\Exception $e) {
return $this->error([], $e->getMessage());
}
}
/****************************************************************************** 数据统计与分析***********************************************************/
/****************************************************************************** 素材start***********************************************************/
/**
* 上传图片
* @param $path
* @return array
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
function uploadImage($path)
{
$result = $this->app()->material->uploadImage($path);
if (isset($result[ 'errcode' ]) && $result[ 'errcode' ] != 0) {
return $this->error($result, $result[ "errmsg" ]);
}
return $this->success($result);
}
/**
* 上传语音
* @param $path
* @return array
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
function uploadVoice($path)
{
$result = $this->app()->material->uploadVoice($path);
if (isset($result[ 'errcode' ]) && $result[ 'errcode' ] != 0) {
return $this->error($result, $result[ "errmsg" ]);
}
return $this->success($result);
}
/**
* 上传视频
* @param $path
* @return array
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
function uploadVideo($path)
{
$result = $this->app()->material->uploadVideo($path);
if (isset($result[ 'errcode' ]) && $result[ 'errcode' ] != 0) {
return $this->error($result, $result[ "errmsg" ]);
}
return $this->success($result);
}
/**
* 上传缩略图
* @param $path
* @return array
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
function uploadThumb($path)
{
$result = $this->app()->material->uploadThumb($path);
if (isset($result[ 'errcode' ]) && $result[ 'errcode' ] != 0) {
return $this->error($result, $result[ "errmsg" ]);
}
return $this->success($result);
}
/**
* 上传图文
* @param $data
* @return array
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function uploadArticle($data)
{
$article_data = [];
foreach ($data as $k => $v) {
$article_data[] = new Article($v);
}
$result = $this->app()->material->uploadArticle($article_data);
if (isset($result[ 'errcode' ]) && $result[ 'errcode' ] != 0) {
return $this->error($result, $result[ "errmsg" ]);
}
return $this->success($result);
}
/**
* 修改图文
* @param $mediaId
* @param string $data 文章详情
* @param int $index 多图文中的第几篇
* @return array
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function updateArticle($mediaId, $data, $index = 0)
{
$result = $this->app()->material->updateArticle($mediaId, $data, $index);
if (isset($result[ 'errcode' ]) && $result[ 'errcode' ] != 0) {
return $this->error($result, $result[ "errmsg" ]);
}
return $this->success($result);
}
/**
* 上传图文消息图片
* @param $path
* @return array
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
*/
public function uploadArticleImage($path)
{
$result = $this->app()->material->uploadArticleImage($path);
if (isset($result[ 'errcode' ]) && $result[ 'errcode' ] != 0) {
return $this->error($result, $result[ "errmsg" ]);
}
return $this->success($result);
}
/**
* 获取永久素材
* @param $mediaId
* @return array
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function getMaterial($mediaId)
{
$result = $this->app()->material->get($mediaId);
if (isset($result[ 'errcode' ]) && $result[ 'errcode' ] != 0) {
return $this->error($result, $result[ "errmsg" ]);
}
return $this->success($result);
}
/**
* 删除永久素材
* @param $mediaId
* @return array
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function deleteMaterial($mediaId)
{
$result = $this->app()->material->delete($mediaId);
if (isset($result[ 'errcode' ]) && $result[ 'errcode' ] != 0) {
return $this->error($result, $result[ "errmsg" ]);
}
return $this->success($result);
}
/****************************************************************************** 素材end***********************************************************/
/****************************************************************************** 回复start***********************************************************/
/**
* 用户事件
* @return array
*/
public function relateWeixin()
{
$server = $this->app->server;
$message = $server->getMessage();
if (isset($message[ 'MsgType' ])) {
switch ( $message[ 'MsgType' ] ) {
case 'event':
$this->app->server->push(function($res) {
if ($res[ 'Event' ] == 'subscribe') {
// 关注公众号
$Userstr = $this->getUser($res[ 'FromUserName' ]);
//获取用户信息
$wechat_user = $this->app->user->get($res[ 'FromUserName' ]);
if (preg_match("/^qrscene_/", $res[ 'EventKey' ])) {
$source_uid = substr($res[ 'EventKey' ], 8);
$_SESSION[ 'source_uid' ] = $source_uid;
} elseif (!empty($_SESSION[ 'source_uid' ])) {
$source_uid = $_SESSION[ 'source_uid' ];
} else {
$source_uid = 0;
}
if (preg_match("/^qrscene_key_/", $res[ 'EventKey' ])) {
//新增2021.06.02
$key = substr($res[ 'EventKey' ], 12);
$cache = Cache::get('wechat_' . $key);
if (!empty($cache)) {
Cache::set('wechat_' . $key, $wechat_user);
}
}
if (preg_match("/^verify_/", $res[ 'EventKey' ])) {
$cache = Cache::get($res[ 'EventKey' ]);
if (empty($cache)) {
Cache::set($res[ 'EventKey' ], $wechat_user, 7200);
}
}
$nickname_decode = preg_replace('/[\x{10000}-\x{10FFFF}]/u', '', $wechat_user[ 'nickname' ]);
$headimgurl = $wechat_user[ 'headimgurl' ];
$sex = $wechat_user[ 'sex' ];
$language = $wechat_user[ 'language' ];
$country = $wechat_user[ 'country' ];
$province = $wechat_user[ 'province' ];
$city = $wechat_user[ 'city' ];
$district = "";
$openid = $wechat_user[ 'openid' ];
$nickname = $wechat_user[ 'nickname' ];
if (!empty($wechat_user[ 'unionid' ])) {
$unionid = $wechat_user[ 'unionid' ];
} else {
$unionid = '';
}
$memo = $wechat_user[ 'remark' ];
$data = array (
'site_id' => $this->site_id,
'nickname' => $nickname,
'nickname_decode' => $nickname_decode,
'headimgurl' => $headimgurl,
'sex' => $sex,
'language' => $language,
'country' => $country,
'province' => $province,
'city' => $city,
'district' => $district,
'openid' => $openid,
'unionid' => $unionid,
'groupid' => '',
'is_subscribe' => 1,
'remark' => $memo,
'subscribe_time' => $wechat_user[ 'subscribe_time' ] ?? 0,
'subscribe_scene' => $wechat_user[ 'subscribe_scene' ] ?? 0,
'unsubscribe_time' => $wechat_user[ 'unsubscribe_time' ] ?? 0,
'update_date' => time()
);
$fans = new Fans();
$fans_info = $fans->getFansInfo([ [ 'openid', '=', $openid ], [ 'site_id', '=', $this->site_id ] ]);
if (empty($fans_info[ 'data' ])) {
$fans->addFans($data);
} else {
$fans->editFans($data, [ [ 'openid', '=', $openid ], [ 'site_id', '=', $this->site_id ] ]);
}
//获取关注发送消息内容
$replay = new Replay();
$replay_content = $replay->getWechatFollowReplay($this->site_id);
return new Text($replay_content[ 'data' ]);
} else if ($res[ 'Event' ] == 'unsubscribe') {
//取消关注
$fans = new Fans();
$openid = $res[ 'FromUserName' ];
$fans->unfollowWechat((string) $openid);
} else if ($res[ 'Event' ] == 'unsubscribe') {
//取消关注
$fans = new Fans();
$openid = $res[ 'FromUserName' ];
$fans->unfollowWechat((string) $openid);
} else if ($res[ 'Event' ] == 'SCAN') {
// SCAN事件 - 用户已关注时的事件推送 - 扫描带参数二维码事件
$openid = $res[ 'FromUserName' ];
$data = $res[ 'EventKey' ];
if (preg_match("/^key_/", $data)) {
$key = substr($data, 4);
$cache = Cache::get('wechat_' . $key);
if (!empty($cache)) {
$wechat_user = $this->app->user->get($res[ 'FromUserName' ]);
Cache::set('wechat_' . $key, $wechat_user);
}
}
if (preg_match("/^verify_/", $res[ 'EventKey' ])) {
$cache = Cache::get($res[ 'EventKey' ]);
if (empty($cache)) {
$wechat_user = $this->app->user->get($res[ 'FromUserName' ]);
Cache::set($res[ 'EventKey' ], $wechat_user, 7200);
}
}
return new Text('扫码成功。');
} else if ($res[ 'Event' ] == 'CLICK') {
// CLICK事件 - 自定义菜单事件
$openid = $res[ 'FromUserName' ];
$data = $res[ 'EventKey' ];
if (strpos($res[ 'EventKey' ], 'MATERIAL_GRAPHIC_MESSAGE_') === 0) {
$material_id = substr($res[ 'EventKey' ], 25);
$material_type = 1;
} else if (strpos($res[ 'EventKey' ], 'MATERIAL_PICTURE_') === 0) {
$material_id = substr($res[ 'EventKey' ], 17);
$material_type = 2;
} else if (strpos($res[ 'EventKey' ], 'MATERIAL_TEXT_MESSAGE_') === 0) {
$material_id = substr($res[ 'EventKey' ], 22);
$material_type = 5;
}
$material = new Material();
$media_info = $material->getMaterialInfo([ [ 'id', '=', $material_id, 'type', '=', $material_type ] ]);
$media_info = $media_info[ 'data' ];
if ($media_info) {
$value = json_decode($media_info[ 'value' ], true);
if ($material_type == 1) {
//图文
$url = __ROOT__;
$url = $url . '/index.php/wechat/api/auth/wechatArticle?id=' . $media_info[ 'id' ];
$items = [
new NewsItem([
'title' => $value[ 0 ][ 'title' ],
'description' => strip_tags($value[ 0 ][ 'content' ]),
'url' => $url,
'image' => $value[ 0 ][ 'cover' ][ 'path' ],
]),
];
return new News($items);
} else if ($material_type == 2) {
//图片
} else if ($material_type == 5) {
//文字
return new Text($value[ 'content' ]);
}
}
} else if ($res[ 'Event' ] == 'weapp_audit_success') {
// 小程序审核通过 自动发布
if (addon_is_exit('wxoplatform')) {
$weapp_original = $res[ 'ToUserName' ];
$site_info = model('config')->getInfo([ [ 'app_module', '=', 'shop' ], [ 'config_key', '=', 'WEAPP_CONFIG' ], [ 'value', 'like', '%is_authopen%' ], [ 'value', 'like', '%' . $weapp_original . '%' ] ], 'site_id');
if (!empty($site_info)) {
// 先将审核中的变更为审核成功
model('weapp_audit_record')->update([ 'status' => 1, 'audit_time' => time() ], [ [ 'status', '=', 0 ], [ 'site_id', '=', $site_info[ 'site_id' ] ] ]);
$platform = new OpenPlatform($site_info[ 'site_id' ]);
$result = $platform->release();
if ($result[ 'code' ] >= 0) {
model('weapp_audit_record')->update([ 'status' => 5, 'release_time' => time() ], [ [ 'status', '=', 1 ], [ 'site_id', '=', $site_info[ 'site_id' ] ] ]);
}
}
}
}
});
$response = $this->app->server->serve();
// 将响应输出
return $response->send();
break;
case 'text':
$this->app->server->push(function($res) {
$replay = new Replay();
$rule = $replay->getSiteWechatKeywordsReplay($res[ 'Content' ], $this->site_id);
if ($rule[ 'data' ]) {
if ($rule[ 'data' ][ 'type' ] == 'text') {
//文字
return new Text($rule[ 'data' ][ 'reply_content' ]);
} else {
$material = new Material();
$media_info = $material->getMaterialInfo([ [ 'media_id', '=', $rule[ 'data' ][ 'media_id' ] ] ]);
$media_info = $media_info[ 'data' ];
if ($media_info) {
$material_type = $media_info[ 'type' ];
$value = json_decode($media_info[ 'value' ], true);
if ($material_type == 1) {
$url = __ROOT__;
$url = $url . '/index.php/wechat/api/auth/wechatArticle?id=' . $media_info[ 'id' ];
$items = [
new NewsItem([
'title' => $value[ 0 ][ 'title' ],
'description' => strip_tags($value[ 0 ][ 'content' ]),
'url' => $url,
'image' => $value[ 0 ][ 'cover' ][ 'path' ],
]),
];
return new News($items);
} else if ($material_type == 2) {
//图片
}
}
}
}
});
$response = $this->app->server->serve();
// 将响应输出
return $response->send();
break;
case 'image':
// return '收到图片消息';
break;
case 'voice':
// return '收到语音消息';
break;
case 'video':
// return '收到视频消息';
break;
case 'location':
//return '收到坐标消息';
break;
case 'link':
//return '收到链接消息';
break;
case 'file':
///return '收到文件消息';
// ... 其它消息
default:
//return '收到其它消息';
break;
}
}
$response = $this->app->server->serve();
return $response->send();
}
/****************************************************************************** 回复end***********************************************************/
/****************************************************************************** 模板消息start ***********************************************************/
/**
* 添加模板消息
* @param $shortId
* @param $keyword_name_list
* @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function getTemplateId($shortId, $keyword_name_list)
{
try {
$res = $this->app()->template_message->addTemplate($shortId, $keyword_name_list);
if (isset($res[ 'errcode' ]) && $res[ 'errcode' ] == 0) return $res;
$errormsg = [
'45026' => '模板数量超出限制'
];
return [ 'errcode' => $res[ 'errcode' ], 'errmsg' => $errormsg[ $res[ 'errcode' ] ] ?? $res[ 'errmsg' ] ];
} catch (\Exception $e) {
return [ 'errcode' => -1, 'errmsg' => $e->getMessage() ];
}
}
/**
* 删除模板消息
* @param $templateId
* @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function deleteTemplate($templateId)
{
try {
$res = $this->app()->template_message->deletePrivateTemplate($templateId);
if (isset($res[ 'errcode' ]) && $res[ 'errcode' ] == 0) return $res;
$errormsg = [
'45026' => '模板数量超出限制'
];
return [ 'errcode' => $res[ 'errcode' ], 'errmsg' => $errormsg[ $res[ 'errcode' ] ] ?? $res[ 'errmsg' ] ];
} catch (\Exception $e) {
return [ 'errcode' => -1, 'errmsg' => $e->getMessage() ];
}
}
/**
* 发送模板消息
* @param array $param
* @return array
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function sendTemplateMessage(array $param)
{
$result = $this->app()->template_message->send([
'touser' => $param[ 'openid' ], // openid
'template_id' => $param[ 'template_id' ],// 模板id
'url' => $param[ 'url' ],// 跳转链接
'miniprogram' => $param[ 'miniprogram' ], // 跳转小程序 ['appid' => 'xxxxxxx','page' => 'pages/xxx',]
'data' => $param[ 'data' ] // 模板变量
]);
if (isset($result[ 'errcode' ]) && $result[ 'errcode' ] != 0) {
return $this->error($result, $result[ "errmsg" ]);
}
return $this->success($result);
}
/****************************************************************************** 模板消息end***********************************************************/
/******************************************************************************* 开放平台start *************************************************************************/
/**
* 将公众号或小程序绑定到指定开放平台帐号下
*/
public function bindTo($open_appid)
{
try {
$res = $this->app()->account->bindTo($open_appid);
return $res;
} catch (\Exception $e) {
return [ 'errcode' => -1, 'errmsg' => $e->getMessage() ];
}
}
/**
* 将公众号/小程序从开放平台帐号下解绑
*/
public function unbindFrom($open_appid)
{
try {
$res = $this->app()->account->unbindFrom($open_appid);
return $res;
} catch (\Exception $e) {
return [ 'errcode' => -1, 'errmsg' => $e->getMessage() ];
}
}
/**
* 获取公众号/小程序所绑定的开放平台帐号
*/
public function getBinding()
{
try {
$res = $this->app()->account->getBinding();
return $res;
} catch (\Exception $e) {
return [ 'errcode' => -1, 'errmsg' => $e->getMessage() ];
}
}
/**
* 获取关注二维码
* @param $fans_id
* @return array|mixed
*/
public function getFollowQrcode($fans_id)
{
$cache = Cache::get('wechat_follow_qrcode_' . $fans_id);
if ($cache) return $cache;
$res = $this->getTempQrcode('follow_key_' . $fans_id, 1728000);
if ($res[ 'code' ] != 0) return $res;
$data = $this->success([
'qrcode' => $res[ 'data' ],
'site_id' => $this->site_id,
'fans_id' => $fans_id
]);
Cache::set('wechat_follow_qrcode_' . $fans_id, $data, 1728000);
return $data;
}
/******************************************************************************* 开放平台end *************************************************************************/
/**
* 处理错误信息
* @param $errcode
* @param string $message
* @return array
*/
public function handleError($errcode, $message = '')
{
$error = require 'addon/wechat/config/wechat_error.php';
return $this->error([], $error[ $errcode ] ?? $message);
}
}

View File

@@ -0,0 +1,248 @@
<?php
namespace addon\system\Wechat\common\model;
use app\model\BaseModel;
/**
* 微信公众号模板消息配置
*/
class WechatMessage extends BaseModel
{
public $site_model;
public function __construct()
{
$this->site_model = new Site();
}
/**
* 发送模板消息
* @param $data
*/
public function send($param)
{
$message_model = new Wechat();
$config_result = $message_model->getWechatConfigInfo($param[ "site_id" ]);
$config_data = $config_result[ "data" ][ "value" ];
$wechat_api = new Weixin('public');
$wechat_api->initWechatPublicAccount($config_data[ "appid" ], $config_data[ "appsecret" ]);
$data = array (
"open_id" => $param[ "open_id" ],
"template_id" => $param[ "template_id" ],
"url" => $param[ "url" ],
"first" => $param[ "first" ],
"remark" => $param[ "remark" ],
"keyword" => $param[ "keyword" ]
);
$res = $wechat_api->tmplmsgSend($data);
if ($res->errcode == 0) {
return $this->success($res);
} else {
return $this->error($res);
}
}
/**
* 获取所有当前绑定微信公众号的模板消息
* @param $param
* @return mixed
*/
public function getWechatTemplateList($param)
{
$config_result = $this->getWechatConfigInfo($param[ "site_id" ]);
$config_data = $config_result[ "data" ][ "value" ];
$wechat_api = new Weixin('public');
$wechat_api->initWechatPublicAccount($config_data[ "appid" ], $config_data[ "appsecret" ]);
$res = $wechat_api->getWechatTemplateList();//获取微信消息模板
return $res;
}
/**
* 获取微信模板消息id
* @param $param
* @return array
*/
public function getMessageTemplateId($param)
{
$wechat_model = new Wechat();
$config_result = $wechat_model->getWechatConfigInfo($param[ "site_id" ]);
$config_data = $config_result[ "data" ][ "value" ];
$wechat_api = new Weixin('public');
$wechat_api->initWechatPublicAccount($config_data[ "appid" ], $config_data[ "appsecret" ]);
$keyword = $param[ "keyword" ];//消息管理关键标识
//微信消息模板
$message_model = new Message();
$message_info_result = $message_model->getSiteWechatMessageInfo([ "keyword" => $keyword, "site_id" => $param[ "site_id" ] ]);
$message_info = $message_info_result[ "data" ];
if (empty($message_info))
return $this->error([], "当前微信消息模板不存在!");
$message_type_model = new MessageModel();
$message_type_info_result = $message_type_model->getMessageTypeInfo([ "keyword" => $keyword ]);
$message_type_info = $message_type_info_result[ "data" ];
if (empty($message_type_info[ "wechat_json" ]))
return $this->error([], "当前微信消息模板不存在!");
$template_res = $wechat_api->getWechatTemplateId($message_type_info[ "wechat_json" ][ "template_no" ]);
if ($template_res->errcode == 0) {
//设置模板编号
$res = $message_model->editSiteWechatMessage([ "template_id" => $template_res->template_id ], [ "keyword" => $keyword, "site_id" => $param[ "site_id" ] ]);
return $res;
} else {
return $this->error($template_res, $template_res->errmsg);
}
}
/**
* 重置模板消息
* @param $param
* @return mixed
*/
public function resetMessage($param)
{
$wechat_model = new Wechat();
$config_result = $wechat_model->getWechatConfigInfo($param[ "site_id" ]);
$config_data = $config_result[ "data" ][ "value" ];
$wechat_api = new Weixin('public');
$wechat_api->initWechatPublicAccount($config_data[ "appid" ], $config_data[ "appsecret" ]);
$res = $wechat_api->getWechatTemplateList();//获取微信消息模板
//申請微信模板消息
$message_model = new Message();
$list = $message_model->getSiteWechatMessageList([ "site_id" => $param[ "site_id" ] ]);
$res = error();
if (!empty($list[ "data" ])) {
foreach ($list[ "data" ] as $k => $v) {
if (!empty($v[ "wechat_json" ])) {
$temp_json = json_decode($v[ "wechat_json" ], true);
if (!empty($temp_json[ "template_no" ])) {
$res = $wechat_api->getWechatTemplateId($temp_json[ "template_no" ]);
if ($res->errcode == 0) {
//设置模板编号
$message_model->editSiteWechatMessage([ "template_id" => $res->template_id ], [ "keyword" => $v[ "keyword" ], "site_id" => $param[ "site_id" ] ]);
} else {
return $this->error($res, $res->errmsg);
}
}
}
}
}
return $res;
}
/**
* 发送模板消息
* @param $param
* @return array
*/
public function sendMessage($param)
{
$message_model = new SiteMessage();
$type_info = $message_model->getSiteMessageTypeViewInfo([ "keyword" => $param[ "keyword" ], "site_id" => $param[ "site_id" ] ]);
if (( ( !empty($param[ "support_type" ]) && stripos($param[ "support_type" ], "Wechat") !== false ) || empty($param[ "support_type" ]) ) && stripos($type_info[ "data" ][ "port" ], "Wechat") !== false && $type_info[ "data" ][ "wechat_is_open" ] == 1) {
$message_model = new Message();
$message_info = $message_model->getSiteWechatMessageInfo([ "keyword" => $param[ "keyword" ], "site_id" => $param[ "site_id" ] ]);
if (empty($message_info[ "data" ][ "template_id" ]))
return $this->error();
if (empty($message_info[ "data" ][ "headtext" ]))
return $this->error();
if (empty($message_info[ "data" ][ "bottomtext" ]))
return $this->error();
//模板消息字段
if (empty($param[ "keyword_json" ]))
return $this->error();
//发送的接收用户
if (empty($param[ "open_id" ]))
return $this->error();
$url = empty($param[ "url" ]) ? '' : $param[ "url" ];
$data = array (
"open_id" => $param[ "open_id" ],
"template_id" => $message_info[ "data" ][ "template_id" ],
"url" => $url,
"first" => $message_info[ "data" ][ "headtext" ],
"remark" => $message_info[ "data" ][ "bottomtext" ],
"keyword" => $param[ "keyword_json" ],
"site_id" => $param[ "site_id" ],
);
//添加发送记录(未即时发送的记录 发送状态为为发送)
$message_records_model = new MessageRecords();
$records_data = array (
"site_id" => $param[ "site_id" ],
"open_id" => $param[ "open_id" ],
"keyword_json" => json_encode($param[ "keyword_json" ]),
"keyword" => $param[ "keyword" ],
"url" => $url,
"create_time" => time()
);
//是否即时发送
if ($type_info[ "data" ][ "is_instant" ] == 1) {
$result = $this->send($data);
if ($result[ "code" ] == 0) {
$status = 1;
$records_data[ "send_time" ] = time();
} else {
$status = -1;
$records_data[ "result" ] = $result[ "data" ]->errmsg;
}
} else {
$status = 0;
}
$records_data[ "status" ] = $status;
$result = $message_records_model->addWechatMessageRecords($records_data);
return $result;
}
}
/**
* 邮箱消息延时发送
* @param array $param
*/
public function cronMessageSend($param = [])
{
$message_records_model = new MessageRecords();
$message_records_list = $message_records_model->getWechatMessageRecordsList([ "status" => 0 ]);
if (!empty($message_records_list[ "data" ])) {
foreach ($message_records_list[ "data" ] as $k => $v) {
$message_model = new Message();
$message_info = $message_model->getSiteWechatMessageInfo([ "keyword" => $param[ "keyword" ], "site_id" => $param[ "site_id" ] ]);
$params = array (
"site_id" => $v[ "site_id" ],
"open_id" => $v[ "open_id" ],
"keyword" => json_decode($v[ "keyword_json" ], true),
"url" => $v[ "url" ],
"template_id" => $message_info[ "data" ][ "template_id" ],
"first" => $message_info[ "data" ][ "headtext" ],
"remark" => $message_info[ "data" ][ "bottomtext" ],
);
$result = $this->send($params);
$data = array ();
$condition = array (
"id" => $v[ "id" ],
"site_id" => $v[ "site_id" ]
);
if ($result[ "code" ] == 0) {
$data[ "send_time" ] = time();
$data[ "status" ] = 1;
} else {
$data[ "status" ] = -1;
$data[ "result" ] = $result[ "message" ];
}
$message_records_model->editWechatMessageRecords($data, $condition);
}
}
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace addon\wechat\shop\controller;
use app\shop\controller\BaseShop;
use think\App;
/**
* 微信控制器基类
*/
class BaseWechat extends BaseShop
{
public function __construct(App $app = null)
{
$this->replace = [
'WECHAT_CSS' => __ROOT__ . '/addon/wechat/shop/view/public/css',
'WECHAT_JS' => __ROOT__ . '/addon/wechat/shop/view/public/js',
'WECHAT_IMG' => __ROOT__ . '/addon/wechat/shop/view/public/img',
];
parent::__construct($app);
}
}

View File

@@ -0,0 +1,297 @@
<?php
namespace addon\wechat\shop\controller;
use addon\wechat\model\Wechat as WechatModel;
use addon\wechat\model\Fans as FansModel;
/**
* 微信粉丝控制器
*/
class Fans extends BaseWechat
{
/**
* 粉丝列表
* @return array|mixed
*/
public function lists()
{
$fans_model = new FansModel();
if (request()->isJson()) {
$page = input('page', 1);
$limit = input('page_size', PAGE_LIST_ROWS);
$is_subscribe = input('is_subscribe', '');//关注
$nickname = input('nickname', '');//粉丝名称
$start_time = input('start_time', '');
$end_time = input('end_time', '');
$condition[] = [ 'site_id', '=', $this->site_id ];
if ($is_subscribe !== '') {
$condition[] = [ 'is_subscribe', "=", $is_subscribe ];
}
if ($nickname != '') {
$condition[] = [ 'nickname', 'like', '%' . $nickname . '%' ];
}
if (!empty($start_time) && empty($end_time)) {
$condition[] = [ "subscribe_time", ">=", date_to_time($start_time) ];
} elseif (empty($start_time) && !empty($end_time)) {
$condition[] = [ "subscribe_time", "<=", date_to_time($end_time) ];
} elseif (!empty($start_time) && !empty($end_time)) {
$condition[] = [ 'subscribe_time', 'between', [ date_to_time($start_time), date_to_time($end_time) ] ];
}
$fans_list = $fans_model->getFansPageList($condition, $page, $limit);
return $fans_list;
}
$tag_list = $fans_model->getFansTagList();
$this->assign('tag_list', $tag_list[ 'data' ]);
return $this->fetch('fans/lists');
}
/**
* 更新粉丝信息
*/
public function syncWechatFans()
{
$page_index = input('page', 0);
$page_size = input('limit', PAGE_LIST_ROWS);
$wechat_model = new WechatModel($this->site_id);
if ($page_index == 0) {
//建立连接同时获取所有用户openid 拉去粉丝信息列表(一次拉取调用最多拉取10000个关注者的OpenID可以通过多次拉取的方式来满足需求。)
$openid_list = [];
$is_continue = true;
$next_openid = null;
do {
$item_result = $wechat_model->user($next_openid);
if ($item_result[ "code" ] < 0)
return $item_result;
if (empty($item_result[ 'data' ][ 'data' ])) {
return success(0, '公众号暂无粉丝');
}
$next_openid = $item_result[ "data" ][ "next_openid" ];
$openid_item = $item_result[ "data" ][ 'data' ][ "openid" ];
if (empty($openid_item)) {
$is_continue = false;
} else {
$is_continue = false;
foreach ($openid_item as $k => $v) {
$openid_list[] = $v;
}
}
} while ($is_continue);
//将粉丝列表存入session
session('wechat_openid_list', $openid_list);
$total = count($openid_list);
if ($openid_list % $page_size == 0) {
$page_count = $total / $page_size;
} else {
$page_count = (int) ( $total / $page_size ) + 1;
}
$data = array (
'total' => $total,
'page_count' => $page_count,
);
return success(0, '', $data);
} else {
//对应页数更新用户粉丝信息
$openid_list = session('wechat_openid_list');
if (empty($openid_list)) {
return error();
}
$start = ( $page_index - 1 ) * $page_size;
$page_fans_openid_list = array_slice($openid_list, $start, $page_size);
if (empty($page_fans_openid_list)) {
return error();
}
$fans_model = new FansModel();
$result = $wechat_model->selectUser($page_fans_openid_list);
if ($result[ 'data' ] && $result[ 'data' ][ 'user_info_list' ]) {
foreach ($result[ 'data' ][ 'user_info_list' ] as $k => $v) {
$nickname_decode = preg_replace('/[\x{10000}-\x{10FFFF}]/u', '', $v[ 'nickname' ]);
$nickname = preg_replace_callback('/./u',
function(array $match) {
return strlen($match[ 0 ]) >= 4 ? '' : $match[ 0 ];
},
$v[ 'nickname' ]);
$add_data = [
'site_id' => $this->site_id,
'nickname' => $nickname,
'nickname_decode' => $nickname_decode,
'headimgurl' => $v[ 'headimgurl' ],
'sex' => $v[ 'sex' ],
'language' => $v[ 'language' ],
'country' => $v[ 'country' ],
'province' => $v[ 'province' ],
'city' => $v[ 'city' ],
'openid' => $v[ 'openid' ],
'unionid' => $v[ 'unionid' ] ?? '',
'groupid' => '',
'is_subscribe' => 1,
'remark' => $v[ 'remark' ],
'subscribe_time' => $v[ 'subscribe_time' ] ?? 0,
'subscribe_scene' => $v[ 'subscribe_scene' ] ?? 0,
'unsubscribe_time' => $v[ 'unsubscribe_time' ] ?? 0,
'update_date' => time()
];
$info = $fans_model->getFansInfo([ 'openid' => $v[ 'openid' ], 'site_id' => $this->site_id ], 'openid');
if (!empty($info[ 'data' ])) {
$fans_model->editFans($add_data, [ [ 'openid', '=', $v[ 'openid' ] ], [ 'site_id', '=', $this->site_id ] ]);
} else {
$fans_model->addFans($add_data);
}
}
}
return $result;
}
}
/**
* 微信粉丝标签
*/
public function fansTagList()
{
if (request()->isJson()) {
$fans_model = new FansModel();
$page = input('page', 1);
$limit = input('limit', PAGE_LIST_ROWS);
$condition = [];
$list = $fans_model->getFansTagPageList($condition, $page, $limit);
return $list;
} else {
return $this->fetch('fans/fans_tag_list');
}
}
/**
* 为微信粉丝批量打标签
*/
public function fansBatchTagging()
{
if (request()->isJson()) {
$fans_model = new FansModel();
$tagids = input('tag_id', '');
$openids = input('openid', '');
if (!empty($openids)) {
$tag_id_list = explode(',', $tagids);
$openid_list = explode(',', $openids);
$data = [
'tag_id_list' => $tag_id_list,
'openid_list' => $openid_list
];
$res = $fans_model->fansBatchTagging($data);
return $res;
}
}
}
/**
* 为微信粉丝打标签
*/
public function fansTagging()
{
if (request()->isJson()) {
$fans_model = new FansModel();
$openid = input('openid', '');
$tagid_list = input('tagid_list', '');
$cancel_tagid_list = input('cancel_tagid_list', '');
if (!empty($openid)) {
$tagid_list_arr = !empty($tagid_list) ? explode(',', $tagid_list) : [];
$cancel_tagid_list_arr = !empty($cancel_tagid_list) ? explode(',', $cancel_tagid_list) : [];
$data = [
'tag_id_list' => $tagid_list_arr,
'openid_list' => [ $openid ]
];
$res = $fans_model->fansBatchTagging($data);//批量增加标签
$data[ 'tag_id_list' ] = $cancel_tagid_list_arr;
$fans_model->batchUnTagging($data);//批量减少标签
return $res;
}
}
}
/**
* 添加标签
* @return array
*/
public function addFansTag()
{
if (request()->isJson()) {
$fans_model = new FansModel();
$tag_name = input('tag_name', '');
if (!empty($tag_name)) {
$data = [
'tag_name' => $tag_name,
];
$data[ "tags" ] = time();
$data[ "tag_id" ] = time();
$res = $fans_model->addFansTag($data);
return $res;
}
}
}
/**
* 编辑标签
*/
public function editFansTag()
{
if (request()->isJson()) {
$fans_model = new FansModel();
$id = input('id', '');
$tag_name = input('tag_name', '');
if (!empty($tag_name)) {
$data = [
'tag_name' => $tag_name,
];
$condition = array (
[ "id", "=", $id ]
);
$res = $fans_model->editFansTag($data, $condition);
return $res;
} else {
return error("", "标签名称不可为空!");
}
}
}
/**
* 删除标签
*/
public function deleteFansTag()
{
if (request()->isJson()) {
$fans_model = new FansModel();
$id = input('id', '');
$condition = [
[ 'id', "=", $id ],
];
$res = $fans_model->deleteFansTag($condition);
return $res;
}
}
/**
* 同步粉丝标签
*/
public function syncFansTag()
{
if (request()->isJson()) {
$fans_model = new FansModel();
$res = $fans_model->syncFansTag();
return $res;
}
}
}

View File

@@ -0,0 +1,304 @@
<?php
namespace addon\wechat\shop\controller;
use addon\wechat\model\Material as MaterialModel;
use addon\wechat\model\Wechat as WechatModel;
/**
* 微信素材控制器
*/
class Material extends BaseWechat
{
/**
* 素材列表--图文消息
*/
public function lists()
{
if (request()->isJson()) {
$type = input('type', '');
$name = input('name', '');
$page_index = input('page', 1);
$page_size = input('limit', 9);
if (!empty($type)) {
$condition[] = [ 'type', "=", $type ];
}
if (!empty($name)) {
$condition[] = array (
'value', 'like', '%"name":"%' . $name . '%","url"%'
);
}
$condition[] = [ 'site_id', '=', $this->site_id ];
$material_model = new MaterialModel();
$material_list = $material_model->getMaterialPageList($condition, $page_index, $page_size, 'type asc,create_time desc');
if (!empty($material_list[ 'data' ][ 'list' ]) && is_array($material_list[ 'data' ][ 'list' ])) {
foreach ($material_list[ 'data' ][ 'list' ] as $k => $v) {
if (!empty($v[ 'value' ]) && json_decode($v[ 'value' ])) {
$material_list[ 'data' ][ 'list' ][ $k ][ 'value' ] = json_decode($v[ 'value' ], true);
}
}
}
return $material_list;
} else {
return $this->fetch('material/lists');
}
}
/**
* 添加图文消息
*/
public function add()
{
if (request()->isJson()) {
$type = input('type', 1);
$param[ 'value' ] = input('value', '');
if ($type != 1) {
// 图片、音频、视频、缩略图素材
$file_path = input('path', '');
$res = $this->uploadApi($type, [ 'path' => $file_path ]);
if ($res[ 'code' ] != 0) {
return $res;
}
$value[ 'file_path' ] = $file_path;
$value[ 'url' ] = $res[ 'data' ][ 'url' ];
$param[ 'value' ] = json_encode($value);
$param[ 'media_id' ] = $res[ 'data' ][ 'media_id' ];
} else {
$param[ 'media_id' ] = time() . 'GRAPHIC' . 'MESSAGE' . rand(1, 1000);
}
$param[ 'type' ] = $type;
$param[ 'create_time' ] = time();
$param[ 'update_time' ] = time();
$param[ 'site_id' ] = $this->site_id;
$material_model = new MaterialModel();
$res = $material_model->addMaterial($param);
return $res;
} else {
$this->assign('material_id', '0');
$this->assign('flag', false);
return $this->fetch('material/edit');
}
}
/**
* 修改图文消息
*/
public function edit()
{
if (request()->isJson()) {
$condition = [];
$condition[ 'id' ] = input('id', '');
$data[ 'value' ] = input('value', '');
$data[ 'update_time' ] = time();
$material_model = new MaterialModel();
$res = $material_model->editMaterial($data, $condition);
return $res;
} else {
$material_id = input('id', '');
$this->assign('material_id', $material_id);
$this->assign('flag', true);
return $this->fetch('material/edit');
}
}
/**
* 添加文本素材
*/
public function addTextMaterial()
{
if (request()->isJson()) {
$type = input('type', 1);
$param[ 'value' ] = input('value', '');
$param[ 'type' ] = $type;
$param[ 'create_time' ] = time();
$param[ 'update_time' ] = time();
$param[ 'site_id' ] = $this->site_id;
$material_model = new MaterialModel();
$res = $material_model->addMaterial($param);
return $res;
} else {
return $this->fetch('material/add_text');
}
}
/**
* 修改文本素材
*/
public function editTextMaterial()
{
$material_model = new MaterialModel();
if (request()->isJson()) {
$media_id = input("media_id", "");
$media_id = str_replace("MATERIAL_TEXT_MESSAGE_", "", $media_id);
$content = input("content", "");
$content = [ "content" => $content ];
$content = json_encode($content, JSON_UNESCAPED_UNICODE);
$condition[ 'id' ] = $media_id;
$data[ 'value' ] = $content;
$data[ 'update_time' ] = time();
$res = $material_model->editMaterial($data, $condition);
return $res;
} else {
$material_id = input('id', '');
$data = $material_model->getMaterialInfo([ [ 'id', '=', $material_id ], [ 'site_id', '=', $this->site_id ] ]);
if (!empty($data[ 'data' ])) {
$data[ 'data' ][ 'value' ] = json_decode($data[ 'data' ][ 'value' ], true);
}
$this->assign('material_data', $data[ 'data' ]);
return $this->fetch('material/edit_text');
}
}
/**
* 预览图文
*/
public function previewGraphicMessage()
{
$id = input('id', '');
$index = input('i', '');
$material_model = new MaterialModel();
$info = $material_model->getMaterialInfo([ 'id' => $id ]);
if (!empty($info[ 'data' ][ 'value' ]) && json_decode($info[ 'data' ][ 'value' ], true)) {
$info[ 'data' ][ 'value' ] = json_decode($info[ 'data' ][ 'value' ], true);
}
$this->assign('info', $info[ 'data' ]);
$this->assign('index', $index);
return $this->fetch('material/preview_material');
}
/**
* 预览文本
*/
public function previewTextMessage()
{
$id = input('id', '');
$material_model = new MaterialModel();
$info = $material_model->getMaterialInfo([ 'id' => $id ]);
if (!empty($info[ 'data' ][ 'value' ]) && json_decode($info[ 'data' ][ 'value' ], true)) {
$info[ 'data' ][ 'value' ] = json_decode($info[ 'data' ][ 'value' ], true);
}
$this->assign('info', $info[ 'data' ]);
return $this->fetch('material/preview_text');
}
/**
* 上传永久素材
* @param $type
* @param $data
* @return array
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function uploadApi($type, $data)
{
$wechat_model = new WechatModel();
if ($type == 1) {
$res = $wechat_model->uploadArticle($data);
} else {
if ($type == 2) {
$type = 'image';
$res = $wechat_model->uploadImage($data[ 'path' ]);
} else if ($type == 3) {
$type = 'voice';
$res = $wechat_model->uploadVoice($data[ 'path' ]);
} else if ($type == 4) {
$type = 'video';
$res = $wechat_model->uploadVideo($data[ 'path' ]);
} else if ($type == 6) {
$res = $wechat_model->uploadVideo($data[ 'path' ]);
$type = 'thumb';
}
}
return $res;
}
/**
* 删除微信素材
*/
public function delete()
{
if (request()->isJson()) {
$id = input('id', 0);
$material_model = new MaterialModel();
$condition = array (
[ "id", "=", $id ]
);
$res = $material_model->deleteMaterial($condition);
return $res;
}
}
/**
* 获取素材详情
* @return mixed
*/
public function getMaterialInfo()
{
if (request()->isJson()) {
$material_model = new MaterialModel();
$condition = array (
[ 'id', '=', input('id', '') ]
);
$material_info = $material_model->getMaterialInfo($condition);
if (json_decode($material_info[ 'data' ][ 'value' ])) {
$material_info[ 'data' ][ 'value' ] = json_decode($material_info[ 'data' ][ 'value' ]);
}
return $material_info;
}
}
/**
* 图文素材
*/
public function articleList()
{
$material_model = new MaterialModel();
$condition = array (
[ 'type', '=', 1 ]
);
$material_list = $material_model->getMaterialList($condition, '*', 'update_time desc');
if (!empty($material_list[ 'data' ]) && is_array($material_list[ 'data' ])) {
foreach ($material_list[ 'data' ] as $k => $v) {
if (!empty($v[ 'value' ]) && json_decode($v[ 'value' ])) {
$material_list[ 'data' ][ $k ][ 'value' ] = json_decode($v[ 'value' ], true);
}
}
}
$this->assign('material_list', $material_list);
return $this->fetch('material/index');
}
/**
* 素材管理
*/
public function material()
{
//这这里的常量要与base中的区分如果一致界面将无法渲染
$type = input("type", 1);
$this->assign("type", $type);
// return array( 'shop/material/material' );
return $this->fetch('material/material');
}
}

View File

@@ -0,0 +1,58 @@
<?php
namespace addon\wechat\shop\controller;
use addon\wechat\model\Menu as MenuModel;
use addon\wechat\model\Wechat as WechatModel;
/**
* 微信菜单控制器
*/
class Menu extends BaseWechat
{
/**
* 微信自定义菜单配置
*/
public function menu()
{
if (request()->isJson()) {
$menu_model = new MenuModel();
$menu_info = $menu_model->getWechatMenuConfig($this->site_id);
return $menu_info;
} else {
return $this->fetch('menu/menu');
}
}
/**
* 修改微信自定义菜单
*/
public function edit()
{
if (request()->isJson()) {
$menu_value = input('value', '');
$menu_json = input('json_data', '');
$menu_model = new MenuModel();
$data = json_decode($menu_value, true);
$res = $menu_model->setWechatMenuConfig($data, $this->site_id);
if ($res[ 'code' ] != 0) {
return $res;
}
$res = $this->sendWeixinMenu($menu_json);
return $res;
}
}
/**
* 公众号同步更新微信菜单
*/
public function sendWeixinMenu($menu_json)
{
$wechat_model = new WechatModel($this->site_id);
$menu_arr = json_decode($menu_json, true);
$res = $wechat_model->menu($menu_arr[ 'button' ]);
return $res;
}
}

View File

@@ -0,0 +1,134 @@
<?php
namespace addon\wechat\shop\controller;
use addon\wechat\model\Config;
use app\model\message\Message as MessageModel;
use app\model\message\MessageTemplate as MessageTemplateModel;
/**
* 微信公众号模板消息
*/
class Message extends BaseWechat
{
/**
* 编辑模板消息
* @return array|mixed|string
*/
public function edit()
{
$message_model = new MessageModel();
$keywords = input("keywords", "");
$info = $message_model->getMessageInfo($this->site_id, $keywords)[ 'data' ];
if (empty($info))
$this->error("不存在的模板信息!");
$wechat_json_array = $info[ "wechat_json_array" ];
if (request()->isJson()) {
$wechat_is_open = input('wechat_is_open', 0);
// $bottomtext = input("bottomtext", "");
// $headtext = input("headtext", "");
// $bottomtextcolor = input("bottomtextcolor", "");
// $headtextcolor = input("headtextcolor", "");
//// $wechat_json_array[ 'headtext' ] = $headtext;//头部标题
// $wechat_json_array[ 'headtextcolor' ] = $headtextcolor;//头部标题颜色
// $wechat_json_array[ 'bottomtext' ] = $bottomtext;//尾部描述
// $wechat_json_array[ 'bottomtextcolor' ] = $bottomtextcolor;//尾部描述颜色
$data = array (
'wechat_json' => json_encode($wechat_json_array),
);
$condition = array (
[ "keywords", "=", $keywords ]
);
$template_model = new MessageTemplateModel();
$res = $template_model->editMessageTemplate($data, $condition);
if ($res[ 'code' ] == 0) {
$res = $message_model->editMessage([ 'wechat_is_open' => $wechat_is_open, 'site_id' => $this->site_id, 'keywords' => $keywords ], [
[ "keywords", "=", $keywords ],
[ 'site_id', '=', $this->site_id ],
]);
}
return $res;
} else {
$this->assign("keywords", $keywords);
if (isset($wechat_json_array[ 'keyword_name_list' ])) {
$wechat_json_array[ 'keyword_name_list' ] = implode(',', $wechat_json_array[ 'keyword_name_list' ]);
}
$this->assign("info", $wechat_json_array);
$this->assign('wechat_is_open', $info[ 'wechat_is_open' ]);
$this->assign('message_title', $info[ 'title' ]);
return $this->fetch('message/edit');
}
}
/**
* 模板消息设置
* @return array|mixed
*/
public function config()
{
$message_model = new MessageModel();
if (request()->isJson()) {
$page = input('page', 1);
$page_size = input('page_size', PAGE_LIST_ROWS);
$condition = array (
[ "support_type", "like", '%wechat%' ],
);
$list = $message_model->getMessagePageList($condition, $this->site_id, $page, $page_size);
return $list;
} else {
$config_model = new Config();
$config = $config_model->getTemplateMessageConfig($this->site_id)[ 'data' ][ 'value' ];
$this->assign('config', $config);
return $this->fetch('message/config');
}
}
/**
* 微信模板消息状态设置
*/
public function setWechatStatus()
{
$message_model = new MessageModel();
if (request()->isJson()) {
$keywords = input("keywords", "");
$wechat_is_open = input('wechat_is_open', 0);
$res = $message_model->getWechatTemplateNo($keywords, $this->site_id, $wechat_is_open);
return $res;
}
}
/**
* 获取模板编号
*/
public function getWechatTemplateNo()
{
if (request()->isJson()) {
$keywords = input("keywords", "");
$message_model = new MessageModel();
$res = $message_model->getWechatTemplateNo($keywords, $this->site_id, -1);
return $res;
}
}
/**
* 设置模板消息配置
* @return array
*/
public function messageConfig()
{
if (request()->isJson()) {
$is_jump_weapp = input("is_jump_weapp", 0);
$config_model = new Config();
$res = $config_model->setTemplateMessageConfig([ 'is_jump_weapp' => $is_jump_weapp ], 1, $this->site_id);
return $res;
}
}
}

View File

@@ -0,0 +1,345 @@
<?php
namespace addon\wechat\shop\controller;
use addon\wechat\model\Replay as ReplayModel;
/**
* 微信回复控制器
*/
class Replay extends BaseWechat
{
/**
* 回复设置--关键字自动回复
*/
public function replay()
{
if (request()->isJson()) {
$page = input('page', 1);
$limit = input('limit', PAGE_LIST_ROWS);
$rule_type = input('rule_type', '');
$search_text = input('search_text', '');
$condition = array (
[ 'rule_type', "=", $rule_type ],
[ 'site_id', '=', $this->site_id ]
);
$condition[] = [
'rule_name', 'like', '%' . $search_text . '%'
];
$order = 'create_time desc';
$replay_model = new ReplayModel();
$list = $replay_model->getReplayPageList($condition, $page, $limit, $order);
foreach ($list[ 'data' ][ 'list' ] as $k => $v) {
$list[ 'data' ][ 'list' ][ $k ][ 'key_list' ] = $v['keywords_json'] ? json_decode($v[ 'keywords_json' ]) : [];
$list[ 'data' ][ 'list' ][ $k ][ 'replay_list' ] = $v['replay_json'] ? json_decode($v[ 'replay_json' ]) : [];
}
return $list;
} else {
$this->assign('link_list', []);
return $this->fetch('replay/replay');
}
}
/**
* 添加关键字回复
*/
public function addOrEditRule()
{
$replay_model = new ReplayModel();
if (request()->isJson()) {
$rule_name = input('rule_name', '');
$rule_id = input('rule_id', 0);
if ($rule_id > 0) {
$data = [
'rule_name' => $rule_name,
'modify_time' => time()
];
$res = $replay_model->editRule($data, [ [ 'rule_id', "=", $rule_id ], [ 'site_id', "=", $this->site_id ] ]);
return $res;
} else {
$data = [
'rule_name' => $rule_name,
'rule_type' => 'KEYWORDS',
'keywords_json' => '',
'replay_json' => '',
'create_time' => time(),
'site_id' => $this->site_id
];
$res = $replay_model->addRule($data);
return $res;
}
}
}
/**
* 添加关键词回复
*/
public function editKeywords()
{
$replay_model = new ReplayModel();
if (request()->isJson()) {
$rule_id = input('rule_id', '');
$keywords_name = input('keywords_name', '');
$keywords_type = input('keywords_type', 0);
$key_id = input('key_id', -1);
$info = $replay_model->getRuleInfo([ [ 'rule_id', "=", $rule_id ] ])[ 'data' ];
if ($info[ 'keywords_json' ]) {
$data = json_decode($info[ 'keywords_json' ]);
if ($key_id > -1) {
$data[ $key_id ] = [
'keywords_name' => $keywords_name,
'keywords_type' => $keywords_type
];
} else {
$data[] = [
'keywords_name' => $keywords_name,
'keywords_type' => $keywords_type
];
}
$data = json_encode($data);
} else {
$data = [
[
'keywords_name' => $keywords_name,
'keywords_type' => $keywords_type
]
];
$data = json_encode($data);
}
$data = [
'keywords_json' => $data,
'modify_time' => time()
];
$res = $replay_model->editRule($data, [ [ 'rule_id', "=", $rule_id ], [ 'site_id', "=", $this->site_id ] ]);
return $res;
}
}
/**
* 删除关键字回复
*/
public function deleteRule()
{
if (request()->isJson()) {
$rule_id = input('rule_id', 0);
$replay_model = new ReplayModel();
$res = $replay_model->deleteRule([ [ 'rule_id', "=", $rule_id ], [ 'site_id', "=", $this->site_id ] ]);
return $res;
}
}
/**
* 删除关键词数据
*/
public function deleteKeywords()
{
$replay_model = new ReplayModel();
if (request()->isJson()) {
$rule_id = input('rule_id', '');
$key_id = input('key_id', '');
$info_result = $replay_model->getRuleInfo([ [ 'rule_id', "=", $rule_id ], [ 'site_id', "=", $this->site_id ] ]);
$info = $info_result[ "data" ];
$keywords_json = '';
if ($info[ 'keywords_json' ]) {
$keywords_json = json_decode($info[ 'keywords_json' ]);
array_splice($keywords_json, $key_id, 1);
$keywords_json = json_encode($keywords_json);
}
$data = [
'keywords_json' => $keywords_json,
];
$res = $replay_model->editRule($data, [ [ 'rule_id', "=", $rule_id ], [ 'site_id', "=", $this->site_id ] ]);
return $res;
}
}
/**
* 修改回复数据
* @return mixed
*/
public function editReplays()
{
$replay_model = new ReplayModel();
if (request()->isJson()) {
$rule_id = input('rule_id', '');
$reply_content = input('reply_content', '');
$media_id = input('media_id', '');
$key_id = input('key_id', -1);
$type = input('type', '');
$replay_type = input('replay_type', '');
$info_result = $replay_model->getRuleInfo([ [ 'rule_id', "=", $rule_id ], [ 'site_id', "=", $this->site_id ] ]);
$info = $info_result[ "data" ];
if (!empty($rule_id)) {
if ($info[ 'replay_json' ]) {
$data = json_decode($info[ 'replay_json' ]);
if ($key_id > -1) {
$data[ $key_id ] = [
'reply_content' => $reply_content,
'type' => $type,
];
if (!empty($media_id)) {
$data[ $key_id ][ 'media_id' ] = $media_id;
}
} else {
if (!empty($media_id)) {
$data[] = [
'reply_content' => $reply_content,
'type' => $type,
'media_id' => $media_id
];
} else {
$data[] = [
'reply_content' => $reply_content,
'type' => $type,
];
}
}
} else {
$data = [
[
'reply_content' => $reply_content,
'type' => $type,
]
];
if (!empty($media_id)) {
$data[ 0 ][ 'media_id' ] = $media_id;
}
}
$data = json_encode($data);
$data = [
'replay_json' => $data,
'modify_time' => time()
];
$res = $replay_model->editRule($data, [ [ 'rule_id', "=", $rule_id ], [ 'site_id', "=", $this->site_id ] ]);
return $res;
} else {
$rule_type = '';
$rule_name = '';
if ($replay_type == "default") {
$rule_type = 'DEFAULT';
$rule_name = '默认回复';
} else if ($replay_type == "follow") {
$rule_type = 'AFTER';
$rule_name = '关注后回复';
}
$data = [
[
'reply_content' => $reply_content,
'type' => $type,
]
];
if (!empty($media_id)) {
$data[ 0 ][ 'media_id' ] = $media_id;
}
$data = json_encode($data);
$data = [
'replay_json' => $data,
'modify_time' => time(),
'rule_name' => $rule_name,
'rule_type' => $rule_type,
'site_id' => $this->site_id,
];
$res = $replay_model->addRule($data);
return $res;
}
}
}
/**
* 删除回复数据
*/
public function deleteReply()
{
$replay_model = new ReplayModel();
if (request()->isJson()) {
$rule_id = input('rule_id', '');
$key_id = input('key_id', '');
$info = $replay_model->getRuleInfo([ [ 'rule_id', "=", $rule_id ], [ 'site_id', "=", $this->site_id ] ]);
$info = $info[ 'data' ];
$replay_json = '';
if ($info[ 'replay_json' ]) {
$replay_json = json_decode($info[ 'replay_json' ]);
array_splice($replay_json, $key_id, 1);
$replay_json = json_encode($replay_json);
}
$data = [
'replay_json' => $replay_json,
];
$res = $replay_model->editRule($data, [ [ 'rule_id', "=", $rule_id ], [ 'site_id', "=", $this->site_id ] ]);
return $res;
}
}
/**
* 关注后回复设置
*/
public function afterAttention()
{
return $this->fetch('replay/aterAttention');
}
/**
* 回复设置--关注后自动回复
*/
public function follow()
{
if (request()->isJson()) {
$page = input('page', 1);
$pagesize = input('limit', PAGE_LIST_ROWS);
$rule_type = input('rule_type', '');
$condition = array (
'rule_type' => $rule_type,
'site_id' => $this->site_id
);
$order = 'create_time desc';
$replay_model = new ReplayModel();
$list = $replay_model->getReplayPageList($condition, $page, $pagesize, $order);
foreach ($list[ 'data' ][ 'list' ] as $k => $v) {
$list[ 'data' ][ 'list' ][ $k ][ 'key_list' ] = $v['keywords_json'] ? json_decode($v[ 'keywords_json' ]) : [];
$list[ 'data' ][ 'list' ][ $k ][ 'replay_list' ] = $v['replay_json'] ? json_decode($v[ 'replay_json' ]) : [];
}
return $list;
} else {
$this->assign('link_list', []);//链接
return $this->fetch('replay/follow');
}
}
public function default()
{
if (request()->isJson()) {
$page = input('page', 1);
$limit = input('limit', PAGE_LIST_ROWS);
// $rule_type = input('rule_type', '');
$search_text = input('search_text', '');
$condition = array (
[ 'rule_type', "=", 'DEFAULT' ], //只获取默认的
[ 'site_id', '=', $this->site_id ]
);
$condition[] = [
'rule_name', 'like', '%' . $search_text . '%'
];
$order = 'create_time desc';
$replay_model = new ReplayModel();
$list = $replay_model->getReplayPageList($condition, $page, $limit, $order);
foreach ($list[ 'data' ][ 'list' ] as $k => $v) {
$list[ 'data' ][ 'list' ][ $k ][ 'key_list' ] = $v['keywords_json'] ? json_decode($v[ 'keywords_json' ]) : [];
$list[ 'data' ][ 'list' ][ $k ][ 'replay_list' ] = $v['replay_json'] ? json_decode($v[ 'replay_json' ]) : [];
}
return $list;
} else {
$this->assign('link_list', []);//链接
return $this->fetch('replay/default');
}
}
}

View File

@@ -0,0 +1,52 @@
<?php
namespace addon\wechat\shop\controller;
use addon\wechat\model\Stat as StatModel;
/**
* 微信公众号基础功能
*/
class Stat extends BaseWechat
{
/**
* 访问统计
*/
public function stat()
{
$stat_model = new StatModel();
$yesterday = date('Y-m-d', strtotime('-1 day'));
//昨天的用户分析数据
$wechat_fans_result = $stat_model->fans($yesterday, $yesterday);
$this->assign('yesterday_user_data', $wechat_fans_result[ 'data' ][ 0 ] ?? []);
//昨天的接口分析数据
$wechat_interface_result = $stat_model->interfaceSummary($yesterday, $yesterday);
$this->assign('yesterday_interface_data', $wechat_interface_result[ 'data' ][ 0 ] ?? []);
return $this->fetch('stat/stat');
}
/**
* 接口调用统计
* @return array
*/
public function interfaceSummaryStatistics()
{
$date_type = input("date_type", "week");
$stat_model = new StatModel();
$result = $stat_model->interfaceSummaryStatistics($date_type);
return $result;
}
/**
* 用户访问统计
* @return array
*/
public function userSummaryStatistics()
{
$date_type = input("date_type", "week");
$stat_model = new StatModel();
$result = $stat_model->userSummaryStatistics($date_type);
return $result;
}
}

View File

@@ -0,0 +1,315 @@
<?php
namespace addon\wechat\shop\controller;
use addon\wechat\model\Config as ConfigModel;
use addon\wechat\model\Qrcode;
use app\model\share\WchatShareBase as ShareModel;
use app\model\system\User;
/**
* 微信公众号基础功能
*/
class Wechat extends BaseWechat
{
/**
* 功能设置
*/
public function setting()
{
$replay_url = 'wechat://shop/replay/replay';
if(!($this->user_info['is_admin'] == 1 || $this->group_info['is_system'] == 1)){
$user_model = new User();
$check_res = $user_model->checkAndGetRedirectUrl(['url' => $replay_url, 'app_module' => $this->app_module], $this->group_info);
if($check_res['redirect_url']){
$replay_url = $check_res[ 'redirect_url' ];
}
}
$setting_arr = [
[
'url' => href_url('wechat://shop/wechat/config'),
'img' => 'config_wechat_icon.png',
'title' => '公众号管理',
'content' => '公众号管理',
],
[
'url' => href_url('wechat://shop/material/lists'),
'img' => 'config_material_icon.png',
'title' => '消息素材',
'content' => '消息素材',
],
// [
// 'url' => href_url('wechat://shop/fans/lists'),
// 'img' => 'config_material_icon.png',
// 'title' => '粉丝列表',
// 'content' => '粉丝列表',
// ],
[
'url' => href_url('wechat://shop/menu/menu'),
'img' => 'config_menu_icon.png',
'title' => '菜单管理',
'content' => '菜单管理',
],
// [
// 'url' => href_url('wechat://shop/wechat/qrcode'),
// 'img' => 'config_qrcode_icon.png',
// 'title' => '推广二维码',
// 'content' => '推广二维码管理',
// ],
[
'url' => href_url('wechat://shop/wechat/share'),
'img' => 'config_share_icon.png',
'title' => '分享内容设置',
'content' => '分享内容设置',
],
[
'url' => href_url($replay_url),
'img' => 'config_replay_icon.png',
'title' => '回复设置',
'content' => '回复设置',
],
[
'url' => href_url('wechat://shop/message/config'),
'img' => 'config_message_icon.png',
'title' => '模板消息',
'content' => '模板消息设置',
],
];
$this->assign('setting_arr', $setting_arr);
return $this->fetch('wechat/setting');
}
/**
* 公众号配置
*/
public function config()
{
$config_model = new ConfigModel();
if (request()->isJson()) {
$wechat_name = input('wechat_name', '');//公众号名称 (备注: 公众号名称,尽量与公众号平台保持一致)
$wechat_original = input('wechat_original', '');//公众号原始id (备注: 公众号原始id,如gh_111111111111)
$appid = input('appid', '');//gh_845581623321AppID (备注: AppID 请注意需要与微信公众号平台保持一致,且注意信息安全,不要随意透露给第三方)
$appsecret = input('appsecret', '');//AppSecret (备注: AppSecret密钥需要与微信公众号平台保持一致没有特别原因不要随意修改也不要随意透露给第三方)
$token = input('token', 'TOKEN');//Token (备注: 自定义的Token值、确保后台与公众号平台填写的一致)
$encodingaeskey = input('encodingaeskey', '');//EncodingAESKey (备注: 由开发者手动填写或随机生成,将用作消息体加解密密钥)
$is_use = input('is_use', 1);//是否启用
$qrcode = input('qrcode', '');//二维码
$headimg = input('headimg', '');//头像
$data = array (
"wechat_name" => $wechat_name,
"wechat_original" => $wechat_original,
"appid" => $appid,
"appsecret" => $appsecret,
"token" => $token,
"encodingaeskey" => $encodingaeskey,
'qrcode' => $qrcode,
'headimg' => $headimg,
);
$res = $config_model->setWechatConfig($data, $is_use, $this->site_id);
return $res;
} else {
$config_result = $config_model->getWechatConfig($this->site_id);
$config = $config_result[ "data" ];
$is_authopen = $config_result[ 'data' ][ 'value' ][ 'is_authopen' ] ?? 0;
if ($is_authopen == 1) {
$config[ 'value' ] = [];
}
$this->assign("config", $config);
// 获取当前域名
$url = __ROOT__;
// 去除链接的http://头部
$url_top = str_replace("https://", "", $url);
$url_top = str_replace("http://", "", $url_top);
// 去除链接的尾部/?s=
$url_top = str_replace('/?s=', '', $url_top);
$call_back_url = addon_url("wechat://api/auth/relateweixin");
$this->assign("url", $url_top);
$this->assign("call_back_url", $call_back_url);
return $this->fetch('wechat/config');
}
}
/**
* 推广二维码模板
*/
public function qrcode()
{
$qrcode_model = new Qrcode();
$template_list = $qrcode_model->getQrcodePageList([ 'is_remove' => 0 ]);
$this->assign("template_list", $template_list[ 'data' ]);
return $this->fetch('wechat/qrcode');
}
/**
* 推广二维码
*/
public function addQrcode()
{
if (request()->isJson()) {
$data = array (
'background' => input("background", ''),
'nick_font_color' => input("nick_font_color", '#000'),
'nick_font_size' => input("nick_font_size", '12'),
'is_logo_show' => input("is_logo_show", '1'),
'header_left' => input("header_left", '59') . 'px',
'header_top' => input("header_top", '15') . 'px',
'name_left' => input("name_left", '128') . 'px',
'name_top' => input("name_top", '23') . 'px',
'logo_left' => input("logo_left", '60') . 'px',
'logo_top' => input("logo_top", '200') . 'px',
'code_left' => input("code_left", '70') . 'px',
'code_top' => input("code_top", '300') . 'px',
);
$qrcode_model = new Qrcode();
$result = $qrcode_model->addQrcode($data);
return $result;
} else {
return $this->fetch('wechat/add_qrcode');
}
}
/**
* 编辑二维码模板
* @return mixed
*/
public function editQrcode()
{
$id = input("id", 0);
$qrcode_model = new Qrcode();
$condition = array (
"id" => $id
);
if (request()->isJson()) {
$data = array (
'background' => input("background", ''),
'nick_font_color' => input("nick_font_color", '#000'),
'nick_font_size' => input("nick_font_size", '12'),
'is_logo_show' => input("is_logo_show", '1'),
'header_left' => input("header_left", '59') . 'px',
'header_top' => input("header_top", '15') . 'px',
'name_left' => input("name_left", '128') . 'px',
'name_top' => input("name_top", '23') . 'px',
'logo_left' => input("logo_left", '60') . 'px',
'logo_top' => input("logo_top", '200') . 'px',
'code_left' => input("code_left", '70') . 'px',
'code_top' => input("code_top", '300') . 'px',
);
$result = $qrcode_model->editQrcode($data, $condition);
return $result;
} else {
$info = $qrcode_model->getQrcodeInfo($condition)[ "data" ];
$this->assign("info", $info);
return $this->fetch('wechat/edit_qrcode');
}
}
/**
* 设置默认推广二维码
*/
public function qrcodeDefault()
{
if (request()->isJson()) {
$id = input('id', 0);
$qrcode_model = new Qrcode();
$result = $qrcode_model->modifyQrcodeDefault([ 'id' => $id ]);
return $result;
}
}
/**
* 删除默认推广二维码
*/
public function deleteQrcode()
{
if (request()->isJson()) {
$id = input('id', 0);
$qrcode_model = new Qrcode();
$result = $qrcode_model->deleteQrcode([ 'id' => $id ]);
return $result;
}
}
/**
* 分享内容设置
*/
public function shareBack()
{
$config_model = new ConfigModel();
if (request()->isJson()) {
$qrcode_param_1 = input('qrcode_param_1', '');
$qrcode_param_2 = input('qrcode_param_2', '');
$goods_param_1 = input('goods_param_1', '');
$goods_param_2 = input('goods_param_2', '');
$shop_param_1 = input('shop_param_1', '');
$shop_param_2 = input('shop_param_2', '');
$shop_param_3 = input('shop_param_3', '');
$data = array (
'qrcode_param_1' => $qrcode_param_1,
'qrcode_param_2' => $qrcode_param_2,
'goods_param_1' => $goods_param_1,
'goods_param_2' => $goods_param_2,
'shop_param_1' => $shop_param_1,
'shop_param_2' => $shop_param_2,
'shop_param_3' => $shop_param_3,
);
$res = $config_model->setShareConfig($data, 1, $this->site_id);
return $res;
} else {
$config_result = $config_model->getShareConfig($this->site_id);
$this->assign("info", $config_result[ "data" ][ 'value' ]);
return $this->fetch('wechat/share_back');
}
}
/**
* 分享内容设置
*/
public function share()
{
if (request()->isJson()) {
$data_json = input('data_json', '');
$data = json_decode($data_json, true);
$share_model = new ShareModel();
return $share_model->setShareConfig($this->site_id, $data);
} else {
$share_config = event('WchatShareConfig', [ 'site_id' => $this->site_id ]);
$config_list = [];
foreach ($share_config as $data) {
foreach ($data[ 'data' ] as $val) {
$config_list[] = $val;
}
}
$this->assign('config_list', $config_list);
return $this->fetch('wechat/share');
}
}
/**
* 公众号设置
*/
public function configSetting()
{
$config_model = new ConfigModel();
$config_result = $config_model->getWechatConfig($this->site_id);
$config = $config_result[ "data" ];
if (!empty($config[ "value" ])) {
//是否是开放平台授权
$is_authopen = $config_result[ 'data' ][ 'value' ][ 'is_authopen' ] ?? 0;
if ($is_authopen > 0) {
$this->redirect(addon_url("wxoplatform://shop/oplatform/wechat"));
} else {
$this->redirect(addon_url("wechat://shop/wechat/config"));
}
} else {
$this->redirect(addon_url("wxoplatform://shop/oplatform/wechatsettled"));
}
}
}

View File

@@ -0,0 +1,158 @@
<style>
.layui-table {margin: 0}
</style>
<div>
<button class="layui-btn" onclick="addOrEditTag('添加标签')">添加标签</button>
<button class="layui-btn" onclick="syncTag()">同步标签</button>
</div>
<div>
<table id="tag_list" lay-filter="tag_list" class="layui-table"></table>
</div>
<script type="text/html" id="operation">
<a class="default" lay-event="edit">编辑</a> |
<a class="default" lay-event="delete">删除</a> <br/>
</script>
<script type="text/html" id="addOrEditTag">
<div class="layui-form" lay-filter="otherInfo">
<div class="layui-form-item">
<label class="layui-form-label sm">标签名称</label>
<div class="layui-input-inline">
<input type="text" name="tag_name" class="layui-input" value="{{d.tag_name}}" lay-verify="required">
</div>
</div>
<div class="form-row sm">
<button class="layui-btn" lay-submit lay-filter="addOrEditTagSubmit">保存</button>
<button type="reset" class="layui-btn layui-btn-primary" onclick="back()">返回</button>
</div>
<input type="hidden" name="id" value="{{d.id}}">
<input type="hidden" name="tag_id" value="{{d.tag_id}}">
</div>
</script>
<script type="text/javascript">
var form,laytpl,index,layer;
var table = new Table({
elem : '#tag_list',
filter : "tag_list",
url : "{:addon_url('wechat://shop/fans/fansTagList')}",
cols : [
[
{
type : 'checkbox',
unresize : 'true'
},
{
field: 'tag_name',
title: '标签名称',
unresize : 'true'
},
{
title : '操作',
toolbar : '#operation',
width : '30%',
align : 'right',
unresize : 'true'
}
]
]
});
table.tool(function(obj){
var data = obj.data;
switch (obj.event) {
case 'edit':
addOrEditTag('编辑标签', data);
break;
case 'delete':
deleteTag(data);
break;
}
});
layui.use([ 'form', 'laytpl'], function() {
laytpl = layui.laytpl;
form = layui.form;
form.render();
var repeat_flag = false;//防重复标识
form.on('submit(addOrEditTagSubmit)', function (data) {
if (repeat_flag) return;
repeat_flag = true;
var url = data.field.id > 0 ? '{:addon_url("wechat://shop/fans/editFansTag")}' : '{:addon_url("wechat://shop/fans/addFansTag")}';
$.ajax({
type: "post",
async: false,
url: url,
dataType: 'json',
data: data.field,
success: function (res) {
back();
layer.msg(res.message);
if (res.code == 0) {
table.reload();
}
repeat_flag = false;
}
})
});
});
function addOrEditTag(title, data) {
var tpl_data = data == undefined ? {tag_name: '', tag_id: 0, id: 0} : data;
laytpl($("#addOrEditTag").html()).render(tpl_data, function (html) {
index = layer.open({
type: 1,
title: title,
skin: 'layer-tips-class',
area: ['450px'],
content: html
})
});
}
var repeat_flag_delete = false;//防重复标识
function deleteTag(data){
if (repeat_flag_delete) return;
repeat_flag_delete = true;
layer.confirm('确定要删除该标签吗?',function (index) {
layer.close(index);
$.ajax({
type : "post",
async : false,
url : '{:addon_url("wechat://shop/fans/deleteFansTag")}',
dataType: 'json',
data : data,
success:function(res){
layer.msg(res.message);
if(res.code == 0){
table.reload();
}
repeat_flag_delete = false;
}
})
});
}
function syncTag(){
$.ajax({
url : '{:addon_url("wechat://shop/fans/syncFansTag")}',
dataType: 'json',
async : false,
type : "post",
beforeSend : function(){
index = layer.load(2);
},
success:function(res){
back();
layer.msg(res.message);
if(res.code == 0){
table.reload();
}
}
})
}
</script>

View File

@@ -0,0 +1,212 @@
<link rel="stylesheet" type="text/css" href="SHOP_CSS/member.css" />
<link rel="stylesheet" type="text/css" href="WECHAT_CSS/wx_fans.css" />
<style>
.layui-layout-admin .tips-wrap{margin-bottom: 15px;}
</style>
<div class="single-filter-box">
<button class="layui-btn btn-hide" lay-submit="" lay-filter="save" onclick="refresh()">同步粉丝信息</button>
<div class="layui-form">
<div class="layui-input-inline">
<input type="text" name="nickname" placeholder="请输入粉丝名称" autocomplete="off" class="layui-input">
<button type="button" class="layui-btn layui-btn-primary" lay-filter="search" lay-submit>
<i class="layui-icon">&#xe615;</i>
</button>
</div>
</div>
</div>
<div class="layui-collapse tips-wrap">
<div class="layui-colla-item">
<h2 class="layui-colla-title">操作提示</h2>
<div class="layui-colla-content layui-show">说明:把同步公众号上的实际粉丝数量与信息同步到商城中展示</div>
</div>
</div>
<!-- 列表 -->
<table id="Fans_list" lay-filter="Fans_list"></table>
<!-- 批量操作 -->
<script type="text/html" id="batchOperation">
</script>
<!-- 地址 -->
<script type="text/html" id="diqu">
<p>{{d.country}}{{d.province}}{{d.city}}{{ d.district != '' && d.district != '无' ? d.district : '' }}</p>
</script>
<!-- 操作 -->
<script type="text/html" id="operation">
<div class="table-btn">
<a class="layui-btn" href="" lay-event="">查看消息</a>
</div>
</script>
<!-- 用户信息 -->
<script type="text/html" id="fansMessage">
<div class='table-title'>
<div class='title-pic'>
{{# if(d.headimgurl){ }}
<img layer-src src={{ns.img(d.headimgurl)}} class="head-portrait">
{{# } }}
</div>
<div class='title-content'>
<p class="layui-elip">用户名{{d.nickname}}</p>
{{# if(d.sex == 1){ }}
<p class="layui-elip">性别</p>
{{# }else{ }}
<p class="layui-elip">性别</p>
{{# } }}
</div>
</div>
</script>
<!-- 用户来源 -->
<script type="text/html" id="subscribe_scene">
{{# if(d.subscribe_scene == 'ADD_SCENE_SEARCH'){ }}
<p>公众号搜索</p>
{{# } }}
{{# if(d.subscribe_scene == 'ADD_SCENE_ACCOUNT_MIGRATION'){ }}
<p>公众号迁移</p>
{{# } }}
{{# if(d.subscribe_scene == 'ADD_SCENE_PROFILE_CARD'){ }}
<p>名片分享</p>
{{# } }}
{{# if(d.subscribe_scene == 'ADD_SCENE_QR_CODE'){ }}
<p>扫描二维码</p>
{{# } }}
{{# if(d.subscribe_scene == 'ADD_SCENE_PROFILE_LINK'){ }}
<p>图文页内名称点击</p>
{{# } }}
{{# if(d.subscribe_scene == 'ADD_SCENE_PROFILE_ITEM'){ }}
<p>图文页右上角菜单</p>
{{# } }}
{{# if(d.subscribe_scene == 'ADD_SCENE_PAID'){ }}
<p>支付后关注</p>
{{# } }}
{{# if(d.subscribe_scene == 'ADD_SCENE_OTHERS '){ }}
<p>其他</p>
{{# } }}
</script>
<div class="progress-layer">
<h3>正在同步中,已完成...</h3>
<div class="layui-progress layui-progress-big" lay-showPercent="true" lay-filter="progress">
<div class="layui-progress-bar layui-bg-blue" lay-percent="0%"></div>
</div>
</div>
<script>
layui.use(['form', 'element'], function() {
var table,
form = layui.form;
form.render();
table = new Table({
elem: '#Fans_list',
url: ns.url("wechat://shop/fans/lists"),
page: true,
cols: [
[{
title: '粉丝信息',
width: '30%',
unresize: 'false',
templet: '#fansMessage'
}, {
field: 'subscribe_time',
title: '关注时间',
width: '20%',
unresize: 'false',
templet: function(data) {
return ns.time_to_date(data.subscribe_time);
}
}, {
field: 'country',
title: '详细地址',
width: '30%',
unresize: 'false',
templet:'#diqu'
}, {
field: 'subscribe_scene',
title: '粉丝来源',
width: '20%',
unresize: 'false',
templet:'#subscribe_scene'
}]
]
});
/**
* 搜索功能
*/
form.on('submit(search)', function(data) {
table.reload({
page: {
curr: 1
},
where: data.field
});
});
});
// 更新粉丝列表
var repeat_flag = false;//防重复标识
var page_index = 0;
var page_count = 0;
var progress_element;
layui.use('element', function(){
progress_element = layui.element;
});
function refresh() {
if(repeat_flag) return;
repeat_flag = true;
$.ajax({
type : "post",
url : "{:addon_url('wechat://shop/fans/syncWechatFans')}",
data : {
"page" : page_index,
},
dataType : "JSON",
beforeSend : function() {
$(".progress-layer").fadeIn();
},
success : function(data) {
repeat_flag = false;
if (data.code != 0 && data.message != undefined && data.message != '') {
layer.msg(data.message);
$(".progress-layer").fadeOut();
} else if (data.code == 0) {
if (page_index == 0) page_count = data['data']["page_count"];
page_index += 1;
if (data.data == null) {
$(".progress-layer").fadeOut();
}
if (page_index <= page_count) {
var speed_of_progress = (page_index / page_count * 100).toFixed(2);
progress_element.progress('progress', speed_of_progress + '%');
}
if (page_index <= page_count) {
repeat_flag = false;
refresh();
} else {
listenerHash(); // 刷新页面
layer.closeAll();
}
} else {
layer.msg('更新失败');
$(".progress-layer").fadeOut();
}
},
error: function (e) {
layer.msg('更新失败');
$(".progress-layer").fadeOut();
}
})
}
</script>

View File

@@ -0,0 +1,80 @@
<link rel="stylesheet" href="WECHAT_CSS/wx_graphic_message.css">
<style type="text/css">
.layui-tab-brief{width: 800px;height: 646px}
.input-text-hint{float:right}
</style>
<div class="layui-collapse tips-wrap">
<div class="layui-colla-item">
<h2 class="layui-colla-title">操作提示<i class="layui-icon layui-colla-icon"></i></h2>
<ul class="layui-colla-content layui-show">
<li>由于微信公众平台的接口规范,仅提供向微信认证服务号商家。如你的公众号同时具有微信支付权限,你还可以在正文内添加超级链接。</li>
</ul>
</div>
</div>
<div id='graphic_message'>
<!-- 添加文本消息 -->
<div class="layui-tab layui-tab-brief" id="add_material_text">
<ul class="layui-tab-title">
<li class="layui-this">添加文本消息</li>
</ul>
<div class="layui-form" style="margin-top: 20px;">
<div class="layui-form-item">
<label class="layui-form-label sm">内容</label>
<div class="layui-input-block">
<textarea name="content" placeholder="请输入内容" id="material_text_content" class="layui-textarea" maxlength="300" lay-verify='material_text_content'></textarea>
<span class='input-text-hint'>剩余300字</span>
</div>
</div>
<div class="form-row sm">
<button class="layui-btn" lay-submit lay-filter="addText">保存</button>
<button type="reset" class="layui-btn layui-btn-primary" onclick="backWechatMaterialList()">取消</button>
</div>
</div>
</div>
</div>
<script>
layui.use('form', function () {
var form = layui.form,repeat_flag = false;
$('#material_text_content').on('input', function (e) {
var num = e.target.value.length;
num = 300 - parseInt(num);
$('#add_material_text .input-text-hint').html('剩余' + num);
});
form.verify({
'material_text_content': function (value, item) {
if (value == '' || value == undefined) {
return '文本内容不可为空';
}
}
});
form.on('submit(addText)', function (data) {
var value = JSON.stringify(data.field);
if (repeat_flag) return;
repeat_flag = true;
$.ajax({
type: 'post',
url: ns.url('wechat://shop/material/addTextMaterial'),
data: {type: 5, value},
dataType: "JSON",
success: function (res) {
if (res.code == 0) {
//_self.material_id = res.data;
location.hash = ns.hash('wechat://shop/material/lists');
}
repeat_flag = false;
layer.msg(res.message);
}
});
});
});
function backWechatMaterialList() {
location.hash = ns.hash("wechat://shop/material/lists");
}
</script>

View File

@@ -0,0 +1,163 @@
<link rel="stylesheet" href="WECHAT_CSS/wx_graphic_message.css">
<style type="text/css">
.img-upload{margin-left: 0}
.body-content{padding-top: 15px !important;}
</style>
<div class="layui-collapse tips-wrap">
<div class="layui-colla-item">
<h2 class="layui-colla-title">操作提示<i class="layui-icon layui-colla-icon"></i></h2>
<ul class="layui-colla-content layui-show">
<li>由于微信公众平台的接口规范,仅提供向微信认证服务号商家。如你的公众号同时具有微信支付权限,你还可以在正文内添加超级链接。</li>
</ul>
</div>
</div>
<div id='graphic_message'>
<div class='graphic-message'>
<img src='WECHAT_IMG/mobile_head.png'/>
<ul class='graphic-message-list'>
<template v-for="(value, index) in article_item_list">
<li @click.stop="chooseGraphicMessage(index)" @mouseenter="moveThis(index)" @mouseleave="leaveThis(index)">
<content>
<template v-if="value.cover.path == ''">
<div class='empty-img'></div>
<span class='empty-hint'>{{index == 0 ? '封面图片' : '缩略图'}}</span>
</template>
<img v-else :src="value.cover.path"/>
<div class='mask-layer'></div>
<h4 class='title'><span>{{value.title == '' ? '标题' : value.title}}</span></h4>
</content>
<div class='action'>
<template v-if="(index == 0 && index == current_msg_index) || (move_index == 0 && index == 0)">
<span class='edit' @click.stop="chooseGraphicMessage(index)">编辑</span>
</template>
<template v-else-if="move_index == index || index == current_msg_index">
<span class='edit' @click.stop="chooseGraphicMessage(index)">编辑</span>
<span class='delete' @click.stop="deleteGraphicMessage(index)">删除</span>
</template>
</div>
</li>
</template>
</ul>
<div class='add-graphic-message'>
<h4>
<a @click="addGraphicMessage()">新增</a>
</h4>
</div>
<div class='bottom-botton'>
<template v-if="material_id == 0">
<button class='layui-btn' @click="saveGraphicMessage()">保存</button>
</template>
<button class='layui-btn' v-else @click="editGraphicMessage()">保存</button>
</div>
</div>
<div class='editor-box' :style="'margin-top:' + editBoxTopPosition + 'px'">
<div class='arrow'></div>
<div class='editor-title'>
<label>标题<span class='hint'>(必填)</span></label>
<input class="layui-input" id="input_title" placeholder="请在这里输入标题" maxlength="64" v-model="inputTitle" max-length="70"/>
</div>
<div class='editor-author'>
<label>作者<span class='hint'>(选填)</span></label>
<input class="layui-input" id="input_autor" placeholder="请输入作者" maxlength="16" v-model="inputAutor" max-length="20"/>
</div>
<div class='editor-cover'>
<label>封面<span class='hint'>图片建议尺寸900 x 500像素 必填)</span></label>
<!--<div class="choose-cover">-->
<!--<div class="choose-cover-pic">-->
<!--<img :src="coverImg"/>-->
<!--</div>-->
<!--<template v-if="coverImg == ''">-->
<!--<a class="text-color" id="uploadImg" href="javascript:;">上传图片...</a>-->
<!--</template>-->
<!--<template v-else>-->
<!--<a id="uploadImg" style="margin-top: 15px;" href="javascript:;">更换封面图...</a>-->
<!--</template>-->
<!--</div>-->
<div class="layui-input-block img-upload">
<div class="upload-img-block">
<div class="upload-img-box hover" v-if="coverImg">
<div class="upload-default" id="uploadImg" >
<div id="preview_uploadImg" class="preview_img">
<img layer-src :src="coverImg" class="img_prev" :data-img="coverImg"/>
</div>
</div>
<div class="operation">
<div>
<i title="图片预览" class="iconfont iconreview js-preview" style="margin-right: 20px;"></i>
<i title="删除图片" class="layui-icon layui-icon-delete js-delete"></i>
</div>
<div class="replace_img js-replace">点击替换</div>
</div>
<input type="hidden" name="logo" id="logo" :value="coverImg"/>
</div>
<div class="upload-img-box" v-else>
<div class="upload-default" id="uploadImg" >
<div class="upload">
<i class="iconfont iconshangchuan"></i>
<p>点击上传</p>
</div>
</div>
<div class="operation">
<div>
<i title="图片预览" class="iconfont iconreview js-preview" style="margin-right: 20px;"></i>
<i title="删除图片" class="layui-icon layui-icon-delete js-delete"></i>
</div>
<div class="replace_img js-replace">点击替换</div>
</div>
<input type="hidden" name="logo" id="logo" />
</div>
</div>
</div>
<label class="editor-msg-label" :class="checkShowCoverPic ? 'selected' : ''" for="check_show_cover_pic">
<input type="checkbox" id="check_show_cover_pic" value="1" v-model="checkShowCoverPic"/>
封面图片显示在正文中
</label>
</div>
<div class='editor-content'>
<label>正文<span class='hint'>(必填)</span></label>
<script id="editor" type="text/plain" style="width:380px; height:300px;"></script>
</div>
<div class='editor-url'>
<label>原文链接<span class='hint'>(选填)</span></label>
<input class="layui-input" id="original_url" placeholder="例http://www.example.com" maxlength="100" v-model="inputOriginalUrl"/>
</div>
</div>
<input type='hidden' id='edit_flag' value='{$flag}'/>
<input type='hidden' id='material_id' value='{$material_id}'/>
<div class="loading" :class="{ show: loading }"><i class=" layui-icon layui-icon-loading layui-icon layui-anim layui-anim-rotate layui-anim-loop"></i></div>
</div>
<script src="STATIC_JS/vue.js"></script>
<script src='WECHAT_JS/wx_graphic_message.js'></script>
<script>
var material_id = $("#material_id").val();
if(material_id != 0){
var timer_new = setInterval(function () {
if($(".img_prev").attr('data-img') && $("#material_id").val()){
loadImgMagnify();
var logo_upload = new Upload({
elem: '#uploadImg',
callback:function (res) {
if (res.code >= 0) {
//成功之后将图片的路径存放再隐藏域中,便于提交使用
// $("input[name='web_qrcode']").val(res.data.pic_path);
vue_obj.coverImg = ns.img(res.data.pic_path);
vue_obj.article_item_list[vue_obj.current_msg_index].cover.path = ns.img(res.data.pic_path);
//将图片展示在页面上
// $("#webQrcodeUpload").html("<img src=" + ns.img(res.data.pic_path) + " >");
}
}
});
clearInterval(timer_new);
}
}, 1000);
}
</script>
<!-- 百度编辑器 -->
<script type="text/javascript" charset="utf-8" src="STATIC_EXT/ueditor/ueditor.config.js?time=20240614"></script>
<script type="text/javascript" charset="utf-8" src="STATIC_EXT/ueditor/ueditor.all.js?time=20240614"></script>
<script type="text/javascript" charset="utf-8" src="STATIC_EXT/ueditor/lang/zh-cn/zh-cn.js"></script>

View File

@@ -0,0 +1,82 @@
<link rel="stylesheet" href="WECHAT_CSS/wx_graphic_message.css">
<style>
.layui-tab-brief{width: 800px;height:646px}
.input-text-hint{float:right}
</style>
<div class="layui-collapse tips-wrap">
<div class="layui-colla-item">
<h2 class="layui-colla-title">操作提示<i class="layui-icon layui-colla-icon"></i></h2>
<ul class="layui-colla-content layui-show">
<li>由于微信公众平台的接口规范,仅提供向微信认证服务号商家。如你的公众号同时具有微信支付权限,你还可以在正文内添加超级链接。</li>
</ul>
</div>
</div>
<div id='graphic_message'>
<!-- 添加文本消息 -->
<div class="layui-tab layui-tab-brief" id="add_material_text">
<ul class="layui-tab-title">
<li class="layui-this">添加文本消息</li>
</ul>
<div class="layui-form" style="margin-top: 20px;">
<div class="layui-form-item">
<label class="layui-form-label sm">内容</label>
<div class="layui-input-block">
<textarea name="content" placeholder="请输入内容" id="material_text_content" class="layui-textarea" maxlength="300" lay-verify='material_text_content'>{$material_data.value.content}</textarea>
<span class='input-text-hint'>剩余300字</span>
<input type="hidden" name="media_id" id="media_id" value="{$material_data.id}">
</div>
</div>
<div class="form-row sm">
<button class="layui-btn" lay-submit lay-filter="addText">保存</button>
<button type="reset" class="layui-btn layui-btn-primary" onclick="backWechatMaterialList()">取消</button>
</div>
</div>
</div>
</div>
<script>
layui.use('form', function () {
var form = layui.form;
$('#material_text_content').on('input', function (e) {
var num = e.target.value.length;
num = 300 - parseInt(num);
$('#add_material_text .input-text-hint').html('剩余' + num);
});
form.verify({
'material_text_content': function (value, item) {
if (value == '' || value == undefined) {
return '文本内容不可为空';
}
}
});
form.on('submit(addText)', function (data) {
if (repeat_flag) return;
repeat_flag = true;
$.ajax({
type: 'post',
url: ns.url('wechat://shop/material/editTextMaterial'),
data: {type: 5,content: data.field.content,media_id:data.field.media_id},
dataType: "JSON",
success: function (res) {
if (res.code == 0) {
location.hash = ns.hash('wechat://shop/material/lists');
} else {
repeat_flag = false;
}
layer.msg(res.message);
}
});
});
});
function backWechatMaterialList() {
location.hash = ns.hash("wechat://shop/material/lists");
}
</script>

View File

@@ -0,0 +1,144 @@
<style>
.news-material>ul{display: flex;flex-wrap: wrap}
.news-material>ul>li{border: 1px solid rgb(233, 233, 233);position: relative;margin:14px 14px 0 0 ;width: calc((100% - 16px*5)/ 5);height: 315px;}
.news-material .add-news{text-align: center;display: flex;position: relative;}
.news-material .add-news .add-box{margin: auto}
.news-material .add-news .iconfont{font-size: 100px; font-weight: 800;color: #606266;}
.news-material .add-news:hover .add-li{display: block;}
.news-material .add-li{display: none;width: 130px;height: 60px; box-shadow:0px 0px 5px rgb(231, 231, 231) ; background: #fff; position: absolute;left: 50%;top: 85%;transform: translate(-50%,-50%)}
.news-material .add-li li{line-height: 30px;}
.news-material .add-li li .iconfont{font-size: 16px;}
.news-material .add-li li:hover{background: var(--base-color)}
.news-material .add-li li:hover span{color: #fff;cursor: pointer;}
.news-material .add-li li:hover i{color: #fff;}
.material-content{padding: 20px}
.news-material .meterail-img{height:200px;text-align: center;line-height: 200px;}
.meterail-img img{max-width: 100%; max-height: 100%;}
.meterail-title {height: 200px;padding: 20px 40px;box-sizing: border-box;text-align: center;display: flex; overflow: hidden}
.meterail-title .title{margin: auto ;}
.meterail-title-text{height: 235px !important;}
.material-content .meterail-text {overflow: hidden;height: 35px;text-overflow: ellipsis;display: -webkit-box;-webkit-line-clamp: 2; overflow:hidden; -webkit-box-orient: vertical;}
.material-content .meterail-time{margin-top: 20px ;color: #999}
.del-edit{position: absolute;right: 10px;top: 10px;display: none;}
.del{margin-right: 5px;}
.del,.edit{display: inline-block;background: #fff;height: 30px; width: 30px;text-align: center;border-radius: 50%;line-height: 30px;box-shadow:0px 0px 5px rgb(231, 231, 231) ;}
.news-material>ul>li:hover .del-edit{display: inline-block}
.del:hover,.edit:hover {color: var(--base-color);cursor: pointer;}
#MaterailList{display: flex;justify-content: flex-end;margin-right: 14px;}
</style>
<div class="news-material">
<ul id="newsMaterialContent"></ul>
<div id="MaterailList"></div>
</div>
<script>
//删除
function delMaterial(id){
$.ajax({
url: ns.url('wechat://shop/material/delete'),
data: {
id,
},
dataType: "JSON",
success: function(res) {
listenerHash(); // 刷新页面
}
});
}
function add_tuwen(){
window.open(ns.href("wechat://shop/material/add"));
}
function add_text(){
window.open(ns.href("wechat://shop/material/addtextmaterial"));
}
var data = '';
function getMaterailList(page){
$.ajax({
url: ns.url('wechat://shop/material/lists'),
data: {
page,
},
dataType: "JSON",
success: function(res) {
this.data = res.data.list;
//列表渲染
var str = ``;
if(page == undefined || page == 1 || !page){
str += `
<li class="add-news">
<div class="add-box">
<i class="iconfont iconadd_light icon-hover"></i><br>
<span>添加素材</span>
<div class="add-li">
<ul>
<li onclick="add_tuwen()">
<i class="iconfont icondoc_plaintext"></i> <span>添加图文</span>
</li>
<li onclick="add_text()">
<i class="iconfont icontext"></i> <span>添加文字</span>
</li>
</ul>
</div>
</div>
</li>
`;
}
$.each(this.data, function(index, item) {
if(item.type == 1){
str += `
<li>
<div class="del-edit">
<a class="del" onclick="delMaterial(`+ item.id +`)" ><i class="iconfont iconshanchu"></i></a>
<a href="`+ ns.href('wechat://shop/material/edit',{"id": item.id,'media_id':item.media_id,'type':item.type}) +`" target='_blank' class="edit"><i class="iconfont iconbianji-copy"></i></a>
</div>
<div class="meterail-img">
<img src=" `+ item.value[0].cover.path +` " alt="">
</div>
<div class="material-content">
<p class="meterail-text">`+ item.value[0].title +`</p>
<p class="meterail-time">更新于<span>`+ ns.time_to_date(item.update_time) +`</span></p>
</div>
</li>
`
}else{
str += `
<li>
<div class="del-edit">
<a class="del" onclick="delMaterial(`+ item.id +`)"><i class="iconfont iconshanchu"></i></a>
<a href="`+ ns.href('wechat://shop/material/edittextmaterial',{"id": item.id,'media_id':item.media_id,'type':item.type}) +`" target='_blank' class="edit"><i class="iconfont iconbianji-copy"></i></a>
</div>
<div class="meterail-title meterail-title-text">
<p class="title">`+ item.value.content +`</p>
</div>
<div class="material-content">
<p class="meterail-time">更新于<span>`+ ns.time_to_date(item.update_time) +`</span></p>
</div>
</li>
`
}
});
$("#newsMaterialContent").html(str);
//分页
layui.use('laypage', function(){
var laypage = layui.laypage;
laypage.render({
elem: 'MaterailList' ,
curr: page, //当前页
count: res.data.count,
layout: ['count', 'prev', 'page', 'next'],
jump: function(obj, first){
//首次不执行
if(!first){
getMaterailList(obj.curr);
}
}
});
});
}
});
}
getMaterailList();
</script>

View File

@@ -0,0 +1,93 @@
<link rel="stylesheet" href="WECHAT_CSS/wx_menu.css">
<script src="WECHAT_JS/common.js"></script>
<!-- 内容 -->
{if $type == 1}
<div class="layui-tab layui-tab-brief" id="marterial_graphic_message">
<ul class="layui-tab-title">
<li class="layui-this">图文消息</li>
<!-- <li>高级图文</li> -->
</ul>
<div class="layui-tab-content">
<div class="layui-tab-item layui-show">
<table id="marterial_graphic_message_list" lay-filter="marterial_graphic_message"></table>
<!-- 标题 -->
<script type="text/html" id="graphic_message_title">
<div class="layui-row grid-demo">
{{# for (var index in d.value) { }}
<div class="layui-col-md12 layui-clear">
<div class="layui-col-md3 article-img" style="float:left;">
<span style="color: #fff;padding: 2px 4px;background: #1aad19;">图文</span>&nbsp;&nbsp;
</div>
<div class="layui-col-md3 title" style="float:left;">
<a href="javascript:void(0);" onclick="preview({{d.id}}, {{index}})">{{d.value[index].title}}</a>
</div>
</div>
{{# } }}
{{# if (d.value.length == 1) { }}
<div class='layui-col-md12 read-all layui-clear' onclick="preview({{d.id}})">
<div class='layui-col-md4' style="float:left;">阅读全文</div>
<div class='layui-col-md4 layui-col-md-offset4'> </div>
</div>
{{# } }}
</div>
</script>
<!-- 创建时间 -->
<script type="text/html" id="create_time">
<div>{{ ns.time_to_date(d.create_time) }}</div>
</script>
<!-- 修改时间 -->
<script type="text/html" id="update_time">
<div>{{ ns.time_to_date(d.update_time) }}</div>
</script>
<!-- 列表操作 -->
<script type="text/html" id="operation">
<a class="default layui-btn-sm hover_cursor text-color" style="cursor:pointer;" lay-event="choose">选取</a>
</script>
</div>
</div>
</div>
{else/}
<!-- 文本消息 -->
<div class="layui-tab layui-tab-brief" id="material_text">
<ul class="layui-tab-title">
<li class="layui-this">文本消息</li>
</ul>
<div class="layui-tab-content">
<div class="layui-tab-item layui-show">
<table id="material_text_list" lay-filter="material_text"></table>
<!-- 内容 -->
<script type="text/html" id="text_content">
<div class="layui-row grid-demo">
<div class="layui-col-md12 layui-clear">
<div class="layui-col-md12 article-img" style="text-align: left;">
<span style="color: #fff;padding: 2px 4px;">文本</span>&nbsp;&nbsp;
</div>
<div class="layui-col-md12 title line-hiding" style="float:left;">
<a href="javascript:void(0);" onclick="previewText('{{d.value.content}}')" title="{{d.value.content}}">{{d.value.content}}</a>
</div>
</div>
</div>
</script>
<!-- 创建时间 -->
<script type="text/html" id="create_time">
<div>{{ ns.time_to_date(d.create_time) }}</div>
</script>
<!-- 修改时间 -->
<script type="text/html" id="update_time">
<div>{{ ns.time_to_date(d.update_time) }}</div>
</script>
<!-- 列表操作 -->
<script type="text/html" id="operation">
<a class="default layui-btn-sm hover_cursor text-color" style="cursor:pointer;" lay-event="choose">选取</a>
</script>
</div>
</div>
</div>
{/if}
<script type="text/javascript" src="WECHAT_JS/wx_material_mannager.js"></script>
<script type="text/javascript">
loadMaterialList({$type});
</script>

View File

@@ -0,0 +1,91 @@
<link rel="stylesheet" href="ADDON_WECHAT_CSS/wx_material.css">
<div class="layui-tab-item layui-show">
<div class='mg'>
<button class="layui-btn" onclick="window.open(ns.href('wechat://shop/material/addgraphicmessage'))">新建图文</button>
</div>
<div>
<table id="graphic_message_list" lay-filter="graphic_message"></table>
</div>
<!-- 标题 -->
<script type="text/html" id="title">
<div class="layui-row grid-demo">
{{# for (var index in d.value) { }}
<div class="layui-col-md12">
<div class="layui-col-md3 article-img">
<span class="bg-color">图文</span>
</div>
<div class="layui-col-md8 title">
<a class="graphic-message-title" href="javascript:void(0);" onclick="preview({{d.id}}, {{index}})">{{d.value[index].title}}</a>
</div>
</div>
{{# } }}
{{# if (d.value.length == 1) { }}
<div class='layui-col-md12 read-all' onclick="preview({{d.id}})">
<div class='layui-col-md4'>阅读全文</div>
<div class='layui-col-md4 layui-col-md-offset4'> > </div>
</div>
{{# } }}
</div>
</script>
<!-- 创建时间 -->
<script type="text/html" id="create_time">
<div>{{ ns.time_to_date(d.create_time) }}</div>
</script>
<!-- 修改时间 -->
<script type="text/html" id="update_time">
<div>{{ ns.time_to_date(d.update_time) }}</div>
</script>
<!-- 列表操作 -->
<script type="text/html" id="operation">
<a class="default" lay-event="edit">编辑</a>
<a class="default" lay-event="delete">删除</a>
</script>
</div>
<script src="ADDON_WECHAT_JS/wx_material.js"></script>
<script>
loadMaterialList(1);
layui.use(['table','form'], function() {
var table,
form = layui.form;
table = new Table({
elem: '#Fans_list',
url: ns.url("wechat://shop/material/lists"),
page: false,
cols: [
[
{
field: 'title',
title: '标题',
unresize: 'false'
}, {
field: 'create_time',
title: '创建时间',
unresize: 'false',
templet: function(data) {
return ns.time_to_date(data.create_time);
}
}, {
field: 'update_time',
title: '更新时间',
unresize: 'false',
templet: function(data) {
return ns.time_to_date(data.update_time);
}
}, {
title: '操作',
toolbar: '#operation',
align: 'right',
unresize: 'false'
}
]
],
bottomToolbar: "#batchOperation"
});
});
</script>

View File

@@ -0,0 +1,48 @@
<html>
<head>
<meta charset="utf-8">
<meta name="renderer" content="webkit">
<meta name="referrer" content="never">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0,viewport-fit=cover"/>
<meta http-equiv="X-UA-COMPATIBLE" content="IE=edge,chrome=1">
<title>{$info.value[$index]['title']}</title>
<meta name="keywords" content="">
<meta name="description" content="">
<link rel="stylesheet" type="text/css" href="__STATIC__/ext/layui/css/layui.css">
<link rel="stylesheet" href="SITEHOME_CSS/home.css">
<link rel="stylesheet" href="WECHAT_CSS/wx_preview_graphic_message.css">
<script src="__STATIC__/js/jquery-2.2.js"></script>
</head>
<body>
<div class='preview-box'>
<img class='head' src='WECHAT_IMG/mobile_head.png'/>
<div class='graphic-message'>
<h1 class='title'>{$info.value[$index]['title']}</h1>
{if condition="$info.update_time eq 0"}
<span class='time'>{$info['create_time']|time_to_date='Y-m-d'}</span>
{else/}
<span class='time'>{$info['update_time']|time_to_date='Y-m-d'}</span>
{/if}
<span class='author'>{$info.value[$index]['autor']}</span>
<content>
{if condition="$info.value[$index]['show_cover_pic'] == 1"}
<div class="rich-media-thumb" id="media">
<img onerror="this.parentNode.removeChild(this)" src="{$info.value[$index]['cover']['path']}">
</div>
{/if}
{:html_entity_decode($info.value[$index]['content'])}
</content>
{if condition="!empty($info.value[$index]['url'])"}
<a class='original-text' href="{$info.value[$index]['url']}">阅读原文</a>
{/if}
</div>
</div>
</body>
<script>
$(function () {
$("img").each(function () {
$(this).replaceWith(this);
})
})
</script>
</html>

View File

@@ -0,0 +1,32 @@
<html>
<head>
<meta charset="utf-8">
<meta name="renderer" content="webkit">
<meta name="referrer" content="never">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0,viewport-fit=cover"/>
<meta http-equiv="X-UA-COMPATIBLE" content="IE=edge,chrome=1">
<meta name="keywords" content="">
<meta name="description" content="">
<link rel="stylesheet" type="text/css" href="__STATIC__/ext/layui/css/layui.css">
<link rel="stylesheet" href="SITEHOME_CSS/home.css">
<link rel="stylesheet" href="WECHAT_CSS/wx_preview_graphic_message.css">
<script src="__STATIC__/js/jquery-2.2.js"></script>
</head>
<body>
<div class='preview-box'>
<img class='head' src='WECHAT_IMG/mobile_head.png'/>
<div class='graphic-message'>
<content>
{:html_entity_decode($info.value['content'])}
</content>
</div>
</div>
</body>
<script>
$(function () {
$("img").each(function () {
$(this).replaceWith(this);
})
})
</script>
</html>

View File

@@ -0,0 +1,266 @@
<link rel="stylesheet" href="WECHAT_CSS/wx_menu.css">
<script src="WECHAT_JS/common.js"></script>
<div class='wx-menu' id='menu'>
<div class='wx-menu-preview'>
<div class='mobile-preview'>
<div class='mobile-hd'>
<span></span>
</div>
<div class='mobile-bd'>
<div class='wx-menu-list'>
<template v-for="(item, index) in button">
<div class="wx-menu-item-box" :class="['wx-menu-item-box-' + index]">
<div class='wx-menu-item' :class="[index==menuIndex[0]&&-1==menuIndex[1] ? 'active' : '']" @click.stop='chooseMenu(index, -1)'>
<template v-if="item.name == ''">
<span>菜单名称</span>
</template>
<template v-else>
<span>{{item.name}}</span>
</template>
</div>
<template v-if="index==menuIndex[0]">
<div class='wx-sub-menu-list' v-if="item" :class="[button.length != undefined && button.length == 2 ? 'two' : '', button.length != undefined && button.length == 3 ? 'three' : '']">
<div class='wx-sub-menu-item' :data="[second_index]" :class="[second_index==menuIndex[1] ? 'active' : '', 'wx-sub-menu-item-' + second_index]" v-for="(item2, second_index) in item.sub_button" @click.stop='chooseMenu(index, second_index)'>
<template v-if="item2.name == ''">
<span>子菜单名称</span>
</template>
<template v-else>
<span>{{item2.name}}</span>
</template>
</div>
<template v-if="subMenuPlusShow">
<div class='wx-sub-menu-item' @click.stop="addSubMenu(index)">+</div>
</template>
</div>
</template>
</div>
</template>
<template v-if="button.length < 3">
<div class='wx-menu-item-box add-menu' @click="addMenu()">+</div>
</template>
</div>
</div>
</div>
</div>
<div class='wx-menu-form'>
<div class='button-list-null' v-if='menuIndex[0] == -1'>点击左侧按钮 + 添加菜单</div>
<template v-if="menuIndex[0] != -1">
<div class='form-editor'>
<div class='form-hd'>
<div class='form-hd-name'>{{name}}</div>
<div class='form-hd-del'><a href="javascript:void(0);" class='layui-btn layui-btn-primary layui-btn-xs' @click='deleteMenu()'>删除菜单</a></div>
</div>
<div class='form-bd'>
<div class='form-bd-list'>
<div class='form-bd-item'>
<label class='layui-form-label sm'>菜单名称</label>
<div class='item-group'>
<template v-if="menuIndex[1] == -1">
<input type='text' class='input layui-input' :class="error_hint == 'name' ? 'error' : ''" value='' @keyup="checkName($event)" v-model='name' placeholder="请输入菜单名称">
<p class="tip" :class="error_hint == 'name' ? 'error' : ''">字数不超过4个汉字或8个字母</p>
</template>
<template v-else>
<input type='text' class='input layui-input' :class="error_hint == 'name' ? 'error' : ''" value='' @keyup="checkName($event, 'sub_button')" v-model='name' placeholder="请输入子菜单名称">
<p class='tip' :class="error_hint == 'name' ? 'error' : ''">字数不超过8个汉字或16个字母</p>
</template>
</div>
</div>
<div class='form-bd-item' v-if="(menuIndex[0] > -1 && (button[menuIndex[0]] == undefined || button[menuIndex[0]].sub_button == undefined || button[menuIndex[0]].sub_button[0] == undefined)) || (menuIndex[1] > -1)">
<label class='layui-form-label sm'>菜单内容</label>
<div class='item-group menu-type'>
<label class='radio-label'>
<i class='layui-icon' :class="type == 'media' ? 'layui-icon-radio' : 'layui-icon-circle'"></i>
<input type='radio' name='type' id='type1' value='media' v-model='type'>
<span>发送素材内容</span>
</label>
<label class='radio-label'>
<i class='layui-icon' :class="type == 'view' ? 'layui-icon-radio' : 'layui-icon-circle'"></i>
<input type='radio' name='type' id='type2' value='view' v-model='type'>
<span>跳转到网页</span>
</label>
<label class='radio-label'>
<i class='layui-icon' :class="type == 'miniprogram' ? 'layui-icon-radio' : 'layui-icon-circle'"></i>
<input type='radio' name='type' id='type3' value='miniprogram' v-model='type'>
<span>跳转小程序</span>
</label>
<label class='radio-label hide'>
<i class='layui-icon' :class="type == 'custom_event' ? 'layui-icon-radio' : 'layui-icon-circle'"></i>
<input type='radio' name='type' id='type4' value='custom_event' v-model='type'>
<span>自定义事件</span>
</label>
</div>
</div>
</div>
<div class='form-bd-content'>
<div class='menu-content' v-if='type == "media"'>
<div class="layui-tab layui-tab-brief">
<ul class="layui-tab-title wechat-media">
<li :class="media_type == 'text' ? 'layui-this' : ''" @click="chooseMediaType(5)"><i class='layui-icon layui-icon-form'></i>文本消息</li>
<li :class="media_type == 'graphic_message' ? 'layui-this' : ''" @click="chooseMediaType(1)"><i class='layui-icon layui-icon-tabs'></i>图文消息</li>
</ul>
<div class="layui-tab-content">
<div class="layui-tab-item" :class="media_type == 'text' ? 'layui-show' : ''">
<template v-if="text == '' || text == undefined">
<div class='material-library' @click="material(5)">
<i></i>
<span>从素材库选择</span>
</div>
<div class='add-material' @click="addMaterial(5)">
<i></i>
<span>添加文本素材</span>
</div>
</template>
<template v-else>
<div class='text-message'>
<div class='text-message-content'>
<div class="material-type">
<span>文本</span>
</div>
<div class="title">
<a href="javascript:void(0);" @click="previewTexts(text)">{{text}}</a>
</div>
</div>
</div>
<span class='del' @click="deleteMaterial(5)">删除</span>
</template>
</div>
<div class="layui-tab-item" :class="media_type == 'graphic_message' ? 'layui-show' : ''">
<template v-if="graphic_message[0] == undefined">
<div class='material-library' @click="material(1)">
<i></i>
<span>从素材库选择</span>
</div>
<div class='add-material' @click="addMaterial(1)">
<i></i>
<span>新建图文素材</span>
</div>
</template>
<template v-else>
<div class='graphic-message-list'>
<template v-for="(value, index) in graphic_message">
<div class='graphic-message-content'>
<div class="material-type">
<span>图文</span>
</div>
<div class="title">
<a href="javascript:void(0);" @click="preview(value.id, index)">{{value.title}}</a>
</div>
</div>
<template v-if="graphic_message.length == 1">
<div class='read-all' @click="preview(value.id)">
<div>阅读全文</div>
<i> > </i>
</div>
</template>
</template>
</div>
<span class='del' @click="deleteMaterial(1)">删除</span>
</template>
</div>
</div>
</div>
</div>
<div class='menu-content view' v-if='type == "view"'>
<p class='tip' style="margin: 10px 0 20px 0;padding-left: 5px;">订阅者点击该子菜单会跳转到一下链接</p>
<div class='form-bd-item'>
<label class='layui-form-label sm'>页面地址</label>
<div class='layui-input-inline'>
<input type='text' class="input layui-input" value='' v-model='url' placeholder='例http://example.com'>
</div>
</div>
</div>
<div class='menu-content menu' v-if='type == "miniprogram"'>
<p class='tip' style="margin: 10px 0 20px 0;padding-left: 5px;">订阅者点击该子菜单会跳到以下小程序</p>
<div class='layui-form-item'>
<label class='layui-form-label'>备用网页</label>
<div class='layui-input-block'>
<input type='text' id='miniprogram_url' class="layui-input" value='' v-model='url' placeholder='例http://example.com'>
<p class='layui-form-mid layui-word-aux'>旧版微信客户端无法支持小程序,用户点击菜单时将会打开备用网页。</p>
</div>
</div>
<div class='layui-form-item'>
<label class='layui-form-label'>小程序APPID</label>
<div class='layui-input-block'>
<input type='text' class="layui-input" value='' v-model='appid'>
<p class='layui-form-mid layui-word-aux'>小程序需要绑定当前公众号。</p>
</div>
</div>
<div class='layui-form-item'>
<label class='layui-form-label'>小程序页面路径</label>
<div class='layui-input-inline'>
<input type='text' class="layui-input" value='' v-model='pagepath'>
</div>
</div>
</div>
<div class='menu-content' v-if='type == "event"'>
<label class='radio-label'>
<input type='radio' name='eventType' value='click' v-model='eventType'>
<span>点击事件</span>
</label>
<label class='radio-label'>
<input type='radio' name='eventType' value='scancode_push' v-model='eventType'>
<span>扫码推事件</span>
</label>
<label class='radio-label'>
<input type='radio' name='eventType' value='scancode_waitmsg' v-model='eventType'>
<span>扫码推事件带提示</span>
</label>
<label class='radio-label'>
<input type='radio' name='eventType' value='pic_sysphoto' v-model='eventType'>
<span>弹出系统拍照发图</span>
</label>
<label class='radio-label'>
<input type='radio' name='eventType' value='pic_photo_or_album' v-model='eventType'>
<span>弹出拍照或者相册发图</span>
</label>
<label class='radio-label'>
<input type='radio' name='eventType' value='pic_wechat' v-model='eventType'>
<span>弹出微信相册发图器</span>
</label>
<label class='radio-label'>
<input type='radio' name='eventType' value='location_select' v-model='eventType'>
<span>弹出地理位置选择器</span>
</label>
<div class='form-bd-item'>
<label class='item-label'>菜单KEY值</label>
<div class='item-group'>
<input type='text' class='input' value='' v-model='key'>
<p class='tip'>旧版微信客户端无法支持小程序,用户点击菜单时将会打开备用网页。</p>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
</div>
<div class='form-ft'>
<button class='layui-btn' @click="saveMenu()">保存并发布</button>
</div>
</div>
<!-- 添加文本消息 -->
<div class="layui-tab layui-tab-brief" id="add_material_text" style="display:none;">
<ul class="layui-tab-title">
<li class="layui-this">添加文本消息</li>
</ul>
<div class="layui-form" style="margin-top: 20px;">
<div class="layui-form-item">
<label class="layui-form-label sm">内容</label>
<div class="layui-input-block">
<textarea name="content" placeholder="请输入内容" id="material_text_content" class="layui-textarea" maxlength="300" lay-verify='material_text_content'></textarea>
<span class='input-text-hint'>剩余300字</span>
</div>
</div>
<div class="form-row sm">
<button class="layui-btn" lay-submit lay-filter="addText">保存</button>
<button type="reset" class="layui-btn layui-btn-primary" onclick="back()">取消</button>
</div>
</div>
</div>
<script src="STATIC_JS/vue.js"></script>
<script type="text/javascript" src="WECHAT_JS/wx_menu.js?v=1"></script>
<script type="text/javascript" src="WECHAT_JS/wx_material_mannager.js"></script>

View File

@@ -0,0 +1,211 @@
<style>
.card-common .layui-card-header{ padding-bottom: 10px; }
</style>
<div class="layui-collapse tips-wrap">
<div class="layui-colla-item">
<h2 class="layui-colla-title">操作提示</h2>
<ul class="layui-colla-content layui-show">
<li>注意:请将微信公众号服务类目选择为:商业服务——>软件/建站/技术开发,生活服务——>百货/超市/便利店,工具—>信息查询,所选行业不一致将会导致模板消息不可用。</li>
<li>公众号可设置20个服务类目每月只能修改5次,请谨慎选择。</li>
<li>公众号最多支持25个模板消息获取时请注意公众号剩余模板数量是否充足</li>
<li>所需跳转到的小程序必须与发模板消息的公众号是绑定关联关系,暂不支持小游戏</li>
</ul>
</div>
</div>
<div class="layui-form">
<div class="layui-card card-common card-brief top">
<div class="layui-card-header">
<span class="card-title">是否需跳转到小程序</span>
<input type="checkbox" name="" value="1" lay-skin="switch" lay-filter="is_jump_weapp" {if $config.is_jump_weapp == 1} checked {/if} />
</div>
</div>
</div>
<table id="template_list" lay-filter="template_list"></table>
<script type="text/html" id="batchOperation">
<button class="layui-btn layui-btn-primary" lay-event="open">批量开启</button>
<button class="layui-btn layui-btn-primary" lay-event="close">批量关闭</button>
<button class="layui-btn layui-btn-primary" lay-event="getAll">批量获取</button>
</script>
<script type="text/html" id="operation">
<div class="table-btn">
{{# if(d.wechat_is_open == 0){ }}
<a class="layui-btn" lay-event="open">开启</a>
{{# }else{ }}
<a class="layui-btn" lay-event="close">关闭</a>
{{# } }}
{{# if(d.wechat_template_id != undefined && d.wechat_template_id != ''){ }}
<a class="layui-btn" lay-event="getTemplateNo">重新获取</a>
{{# } }}
</div>
</script>
<script type="text/html" id="message_type">
{{ d.message_type == 1 ? '买家消息' : '卖家消息' }}
</script>
<script type="text/html" id="template_no">
{{ d.wechat_template_id ? d.wechat_template_id : '' }}
</script>
<script type="text/html" id="wechat_is_open">
{{ d.wechat_is_open == 1 ? '已启动' : '已关闭' }}
</script>
<script type="text/javascript">
var form,table;
layui.use(['form'], function() {
form = layui.form;
form.render();
var repeat_flag = false;//防重复标识
form.on('switch(is_jump_weapp)', function (data) {
data.value = data.elem.checked ? data.value : 0;
$.ajax({
dataType: "JSON",
type: "POST",
data: {"is_jump_weapp": data.value},
url: ns.url("wechat://shop/message/messageConfig"),
success: function (res) {
}
})
});
table = new Table({
elem: '#template_list',
url: ns.url("wechat://shop/message/config"),
cols: [
[
{
width: "3%",
type: 'checkbox',
unresize: 'false'
},
{
field: 'title',
title: '类型',
align: 'left'
},
{
field: 'message_type',
title: '消息类型',
templet: '#message_type',
align: 'center'
},
{
field: 'wechat_is_open',
title: '是否启用',
templet: '#wechat_is_open',
align: 'center'
},
{
field: 'wechat_template_id',
title: '编号',
align: 'center',
templet: '#template_no'
},
{
title: '操作',
toolbar: '#operation',
align: 'right'
}
]
],
bottomToolbar: "#batchOperation"
});
// 批量操作
table.bottomToolbar(function (obj) {
if (obj.data.length < 1) {
layer.msg('请选择要操作的数据');
return;
}
var keywords_array = new Array();
for (i in obj.data) keywords_array.push(obj.data[i].keywords);
switch (obj.event) {
case 'open':
// 开启
setStatus(keywords_array.toString(), 1);
break;
case 'close':
// 关闭
setStatus(keywords_array.toString(), 0);
break;
case 'getAll':
// 批量获取
getTemplate(keywords_array.toString());
break;
}
});
/**
* 监听工具栏操作
*/
table.tool(function (obj) {
var data = obj.data;
switch (obj.event) {
case 'getTemplateNo': //获取模板id
getTemplate(data.keywords);
break;
case 'open': //开启
setStatus(data.keywords, 1);
break;
case 'close': //关闭
setStatus(data.keywords, 0);
break;
}
});
function setStatus(keywords, status) {
$.ajax({
type: "post",
url: '{:addon_url("wechat://shop/message/setWechatStatus")}',
data: {
"wechat_is_open": status,
'keywords': keywords
},
dataType: "JSON",
success: function (res) {
repeat_flag = false;
layer.msg(res.message);
table.reload();
}
});
}
function getTemplate(keywords) {
var loadLayer;
layer.confirm('已存在的模板再次获取会导致模板重复存在,是否继续?', function (index) {
$.ajax({
type: "post",
url: '{:addon_url("wechat://shop/message/getWechatTemplateNo")}',
data: {
'keywords': keywords
},
dataType: "JSON",
beforeSend: function () {
loadLayer = layer.msg("模板获取中,请耐心等待,请勿进行其他操作!", {time: 1000 * 10000});
},
complete: function () {
layer.close(loadLayer);
},
success: function (res) {
repeat_flag = false;
layer.msg(res.message);
layer.close(index);
table.reload();
}
});
})
}
});
</script>

View File

@@ -0,0 +1,84 @@
<div class="layui-form">
<div class="layui-form-item">
<label class="layui-form-label">是否开启:</label>
<div class="layui-input-block">
<input type="checkbox" name="wechat_is_open" value="1" {if $wechat_is_open == 1}checked{/if} lay-skin="switch">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">模板名称:</label>
<div class="layui-input-block">
{$message_title}
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">模板消息ID</label>
<div class="layui-input-block">
<input type="text" name="template_id" value="{if $info}{$info.template_id_short}{/if}" placeholder="模板消息ID" autocomplete="off" class="layui-input len-long" readonly>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">类目模板:</label>
<div class="layui-input-block">
<input type="text" name="keyword_name_list" value="{if $info && isset($info.keyword_name_list)}{$info.keyword_name_list}{/if}" placeholder="类目模板,多个逗号隔开" autocomplete="off" class="layui-input len-long" readonly>
</div>
<div class="word-aux">类目模板的关键词,多个逗号隔开</div>
</div>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label"><span class="required"></span>模板内容:</label>
<div class="layui-input-inline">
<textarea placeholder="" class="layui-textarea len-long" readonly>{if $info}{$info.content}{/if}</textarea>
</div>
</div>
<input type="hidden" name="keywords" value="{$keywords}" />
<div class="form-row">
<button class="layui-btn" lay-submit lay-filter="save">保存</button>
<button type="reset" class="layui-btn layui-btn-primary" onclick="backMessageList()">返回</button>
</div>
</div>
<script>
layui.use(['form', 'colorpicker'], function() {
var form = layui.form,
repeat_flag = false; //防重复标识
form.render();
form.on('submit(save)', function(data) {
if (repeat_flag) return;
repeat_flag = true;
$.ajax({
url: ns.url("wechat://shop/Message/edit"),
data: data.field,
dataType: 'JSON',
type: 'POST',
success: function(res) {
repeat_flag = false;
if (res.code == 0) {
layer.confirm('编辑成功', {
title:'操作提示',
btn: ['返回列表', '继续操作'],
yes: function(index, layero){
location.hash = ns.hash("shop/message/lists")
layer.close(index);
},
btn2: function(index, layero) {
layer.close(index);
}
});
}else{
layer.msg(res.message);
}
}
});
});
});
function backMessageList() {
location.hash = ns.hash("shop/message/lists");
}
</script>

View File

@@ -0,0 +1,9 @@
.access-statistics .access-api-list{display: flex;justify-content: space-between;padding: 0 15px;}
.access-statistics .access-api-item{flex-grow: 1;text-align: center;margin-right: 10px;border: 1px solid #e5e5e5;padding-top: 25px;}
.access-statistics .access-api-item:last-of-type{margin-right:0;}
.access-statistics .access-api-itme-content{height: 80px; line-height: 80px; font-size: 30px;}
.access-statistics .access-api-item-title{position: relative;}
.access-statistics .access-api-item-title h3{display: inline-block;}
.access-statistics .access-api-vice-content{font-size: 25px;}
.layui-fluid .layui-tab-title{margin-top: 0;}
.layui-tab-content-time{display: inline-block; height: 40px; margin-left: 15px; line-height: 40px;}

View File

@@ -0,0 +1,101 @@
.date-picker-btn.selected {
background-color: var(--base-color);
color: #FFF;
}
.search_form {
padding: 15px;
}
.search_form .reset {
background: transparent;
border: none;
margin-left: 10px;
cursor: pointer;
}
.search_form .layui-form-mid {
display: inline-block;
float: unset;
margin-right: 0;
}
.search_form .layui-btn+.layui-btn {
margin-left: 0;
}
.layui-table {
margin: 0;
}
.layui-layer-content {
padding: 15px;
height: auto!important;
}
.align-center {
text-align: center;
}
.headimg-box {
width: 50px;
height: 50px;
display: inline-block;
line-height: 50px;
text-align: center;
margin-right: 8px;
}
.headimg-box img{
max-width: 50px;
max-height: 50px;
margin: auto;
}
.layui-table-cell{
text-overflow: ellipsis;
overflow: initial !important;
white-space: initial;
}
.tag-list{
display: inline-block;
line-height: 40px;
vertical-align: middle;
}
.tag-list span{
margin: 2px;
padding: 2px 5px;
border-radius: 2px;
color: #fff;
white-space: nowrap;
font-size: 12px;
}
.progress-layer {
width: 400px;
background: #fff;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
box-shadow: 1px 1px 50px rgba(0,0,0,.3);
padding: 20px 20px;
z-index: 100;
display: none;
}
.progress-layer h3{
line-height: 1;
margin-bottom: 15px;
text-align: center;
font-size: 14px;
}
.progress-layer .layui-progress-big, .progress-layer .layui-progress-big .layui-progress-bar{
height: 14px;
line-height: 14px;
}
.progress-layer .layui-progress-text{
line-height: 14px;
}

View File

@@ -0,0 +1,742 @@
.layui-layout-admin .layui-body .body-content{
background: #f8f8f8;
padding: 0;
}
.fourstage-nav .layui-tab-title {
background: #fff;
height: 50px;
}
.fourstage-nav .layui-tab-title li {
line-height: 48px;
margin: 0 15px;
}
button, input, select, textarea {
font-family: inherit;
font-size: 100%;
margin: 0;
}
input::-webkit-input-placeholder, textarea::-webkit-input-placeholder {
color: #999;
}
input:-moz-placeholder, textarea:-moz-placeholder {
color: #999;
}
input::-moz-placeholder, textarea::-moz-placeholder {
color: #999;
}
input:-ms-input-placeholder, textarea:-ms-input-placeholder {
color: #999;
}
li {
list-style: none;
}
a:-webkit-any-link {
color: #000;
cursor: pointer;
text-decoration: none;
}
.reply-opts a:-webkit-any-link{
color: var(--base-color);
cursor: pointer;
text-decoration: none;
}
.reply-opts .delet-color:-webkit-any-link {
color: var(--base-color);
}
button {
display: inline-block;
padding: 0 22px;
min-width: 54px;
line-height: 2.42857143;
vertical-align: middle;
text-align: center;
text-decoration: none;
border-radius: 3px;
font-size: 14px;
cursor: pointer;
border-width: 1px;
border-style: solid;
box-sizing: content-box;
}
.layui-body {
overflow-y: auto !important;
}
.hide {
display: none;
}
.page-title h2 {
font-size: 26px;
font-weight: 400;
line-height: 1;
margin-bottom: 20px;
}
.type-menu {
text-align: left;
height: 50px;
line-height: 40px;
border-bottom: 1px solid #E0E1E2;
font-size: 16px;
}
.type-menu li {
float: left;
margin-right: 24px;
line-height: 40px;
font-size: 16px;
}
.replay-info {
padding: 15px 0 0;
height: 30px;
}
.weixin-normal {
position: relative;
margin-top: 15px
}
.replay-info .info {
float: left;
}
.replay-info h3 {
display: inline-block;
font-size: 20px;
font-weight: 400;
line-height: 1;
margin-right: 10px;
}
.replay-info h3:last-child {
font-style: normal;
}
.replay-button {
float: right;
position: relative;
}
.replay-button>div {
float: left;
}
.replay-button:after {
content: '';
clear: both;
}
.replay-button>label {
background-color: #1AAD19;
border-color: #1AAD19;
color: #FFFFFF;
display: inline-block;
min-width: 54px;
line-height: 2.42857143;
vertical-align: middle;
text-align: center;
text-decoration: none;
border-radius: 3px;
font-size: 14px;
cursor: pointer;
border-width: 1px;
border-style: solid;
box-sizing: content-box;
}
.replay-button .layui-layer-page {
text-align: left;
}
.replay-button .layui-form>.layui-btn {
display: block;
margin: auto;
margin-top: 30px;
}
.replay-button label>input {
position: absolute;
left: -9999em;
}
.replay-button .search {
display: inline-block;
vertical-align: middle;
margin-right: .5em;
width: 243px;
}
.replay-button .down {
float: left;
}
.replay-button .down button {
padding: 14px 12px 10px 12px;
min-width: unset;
}
.down .show-menu li a {
color: #00A717;
}
.down .show-menu li a {
padding: 0 15px;
}
.rule-group {
position: relative;
/* border: 1px solid #ccc; */
margin-bottom: 20px;
border-radius: 2px;
background: #fff;
}
.rule-group .rule-meta {
padding: 10px;
}
.rule-group .rule-meta:hover h3 .rule-opts a {
color: var(--base-color);
}
.rule-group .rule-meta h3 {
width: 300px;
position: relative;
font-size: 14px;
margin: 0;
line-height: 1.5em;
font-weight: bold;
}
.rule-group .rule-meta h3 .rule-opts {
position: absolute;
top: 0;
right: -65px;
font-weight: normal;
font-size: 12px;
color: #ddd;
}
.rule-group .rule-meta h3 .rule-opts a {
color: #ddd;
}
.rule-group .rule-body {
border-top: 1px solid #e5e5e5;
margin: 0;
/*padding-top: 5px;*/
overflow: auto;
}
.rule-group .rule-body:before, .rule-group .rule-body:after {
display: table;
content: "";
line-height: 0;
}
.rule-group .long-dashed {
position: absolute;
top: 80px;
width: 100%;
border-bottom: 1px dashed #d7d7d7;
}
.weixin-normal .rule-keywords {
float: left;
}
.rule-group .rule-inner {
font-size: 12px;
padding: 0 10px 5px;
}
.rule-group .rule-inner h4 {
color: #000;
font-weight: normal;
font-size: 14px;
margin: 0 0 5px;
padding: 5px 0;
}
.rule-group .keyword-containe {
padding-top: 5px;
margin-bottom: 5px;
}
.rule-group .info:empty {
padding: 0;
}
.keyword-list .keyword {
position: relative;
margin-right: 10px;
margin-bottom: 10px;
height: 30px;
vertical-align: middle;
cursor: pointer;
display: inline-block;
}
.keyword .close--circle {
display: none;
}
.keyword-list .keyword:hover .close--circle {
display: block;
}
.close--circle {
position: absolute;
z-index: 91;
top: 5px;
right: 10px;
font-size: 28px;
color: #333;
text-align: center;
cursor: pointer;
}
.keyword .value {
background-color: #fff;
text-align: center;
display: inline-block;
height: 20px;
padding: 4px 10px;
font-size: 12px;
line-height: 20px;
color: #555555;
vertical-align: middle;
border-radius: 4px 0 0 4px;
border: 1px solid #ccc;
}
.input-append .add-on, .input-prepend .add-on {
display: inline-block;
width: auto;
height: 20px;
min-width: 16px;
padding: 4px 5px;
font-size: 12px;
font-weight: 400;
line-height: 20px;
text-align: center;
text-shadow: 0 1px 0 #fff;
background-color: #eee;
border: 1px solid #ccc;
margin-left: -4px;
border-radius: 0 4px 4px 0;
vertical-align: middle;
}
.rule-group hr.dashed {
border-bottom: 1px dashed #d7d7d7;
border-top: none;
margin: 0;
background-color: rgb(255, 255, 255);
}
.rule-group .opt {
margin-top: 5px;
}
.rule-group .opt a {
color: var(--base-color);
}
.weixin-normal .rule-replies {
float: left;
}
.rule-group .rule-inner {
font-size: 12px;
padding: 0 10px 5px;
}
.rule-group .rule-inner h4 {
color: #000;
font-weight: normal;
font-size: 14px;
margin: 0 0 5px;
padding: 5px 0;
}
.rule-group .reply-container, .keyword-container {
padding: 5px 0;
}
.rule-group .info {
padding: 5px 0;
color: #999;
}
.rule-group .info:empty {
padding: 0;
}
ol.reply-list {
list-style-type: none;
}
.reply-list li, .quick-dropmenu li {
position: relative;
padding: 8px 90px 8px 5px;
list-style: unset;
}
.reply-list .reply-opts a:hover {
color: var(--base-color);
}
.reply-cont {
display: inline-block;
max-width: 100%;
}
.reply-summary {
display: inline-block;
max-width: 360px;
word-break: break-all;
word-wrap: break-word;
vertical-align: top;
}
.reply-list .reply-opts, .quick-dropmenu .reply-opts {
position: absolute;
top: 8px;
right: 5px;
}
.reply-list li::after, .quick-dropmenu li::after {
content: "";
position: absolute;
border-bottom: 1px dashed #d7d7d7;
bottom: 0;
left: -20px;
right: 0;
}
.reply-list li:last-child::after, .quick-dropmenu li:last-child::after {
border-bottom: none;
}
.rule-group .opt .disable-opt {
color: #999;
}
.badge-success, .label-success {
display: inline-block;
padding: 2px 4px;
font-size: 12px;
line-height: 14px;
color: #fff;
white-space: nowrap;
background: #1AAD19;
}
/***************************************/
.misc {
height: 45px;
}
.misc>a {
display: inline-block;
padding: 10px;
color: var(--base-color) !important
}
.others {
display: inline-block;
position: relative;
}
.others>a {
padding: 10px;
color: var(--base-color) !important
}
.pull-right {
color: #ccc;
float: right;
}
.dropdown-menu {
display: none;
position: absolute;
z-index: 100;
top: 25px;
left: 10px;
width: 110px;
height: 110px;
font-size: 12px;
border: 1px solid rgba(0, 0, 0, 0.125);
cursor: pointer;
background: #fff;
border-radius: 10px;
padding: 10px 0;
overflow: auto;
}
.dropdown-menu li {
padding: 0 10px;
line-height: 30px;
color: #333;
}
.others:hover .dropdown-menu {
display: block;
}
.dropdown-menu li:hover {
background: var(--base-color);
}
.dropdown-menu li:hover a {
color: #fff;
}
.complex-backdrop {
display: none;
position: absolute;
top: 75px;
left: 25px;
width: 86%;
height: 36.5%;
background-color: #fff;
}
.complex-content {
padding: 6px 10px;
}
.ng.ng-image {
width: 80px;
height: 80px;
border: none;
text-align: center;
}
.ng {
position: relative;
vertical-align: top;
width: 250px;
border-radius: 5px;
border: 1px solid #eee;
background-color: #fff;
margin-bottom: 5px;
display: inline-block;
}
.picture>img {
width: auto;
max-width: 100%;
height: auto;
max-height: 100%;
}
.msg-music-thumb {
height: 100px;
text-align: center;
}
.msg-music-thumb a {
width: 100%;
height: 100%;
border: 1px dashed #F2F2F2;
display: block;
line-height: 100px;
color: var(--base-color);
}
#music .layui-textarea {
min-height: 60px;
}
.voice-player {
border-radius: 5px;
position: relative;
border: 1px solid #85ac4c;
display: inline-block;
width: 90px;
height: 25px;
padding: 0 6px 0 7px;
font-size: 12px !important;
line-height: 25px;
cursor: pointer;
background: #a0ce3d;
vertical-align: middle;
margin-left: 7px;
}
.voice-player::before {
position: absolute;
content: "";
left: -13px;
top: 6px;
width: 0;
height: 0;
border: 6px solid transparent;
border-right: 6px solid #85ac4c;
}
.voice-player .stop {
display: inline-block;
color: #fff;
text-shadow: 1px 1px 1px #8ab433;
}
.popover .close--circle {
z-index: initial;
}
.complex-content .close--circle ,.keyword-list .close--circle {
position: absolute;
z-index: 91;
top: -9px;
right: -9px;
width: 20px;
height: 20px;
font-size: 16px;
line-height: 20px;
color: #fff;
text-align: center;
cursor: pointer;
background: rgba(153, 153, 153, 0.6);
border-radius: 10px;
}
.voice-player .second {
display: none;
float: right;
font-size: 12px;
color: #476600;
margin-left: 2px;
}
.voice-player .play {
display: inline-block;
width: 17px;
height: 20px;
margin-top: 2px;
}
.voice-player::after {
position: absolute;
content: "";
left: -12px;
top: 6px;
width: 0;
height: 0;
border: 6px solid transparent;
border-right: 6px solid #a0ce3d;
}
/*其他*/
.ng .close--circle {
top: -9px;
right: -9px;
}
.ng .ng-item {
border-bottom: 1px solid #eee;
overflow: hidden;
padding: 5px 9px;
}
.ng .label {
vertical-align: middle;
}
/*图文*/
.ng .ng-title {
display: inline-block !important;
overflow: hidden !important;
white-space: nowrap !important;
text-overflow: ellipsis !important;
line-height: 16px;
min-height: 16px;
vertical-align: middle;
max-width: 180px;
}
a.new-window {
color: var(--base-color);
}
.ng .ng-item.view-more {
color: #666;
}
.ng .ng-item.view-more a {
color: #666;
}
/*************************************/
.rule-container {
/*position: absolute;*/
/*padding: 5px 10px;*/
background-color: #fff;
border-radius: 4px;
/*box-shadow: 0px 1px 6px 7px rgba(0, 0, 0, 0.1);*/
/*border: 1px solid #e5e5e5; */
/*height: 230px;*/
/*width: 460px;*/
}
.rule-container .add_reply-top{
position: absolute;
width: 0px;
height: 0px;
line-height: 0px;/*为了防止ie下出现题型*/
border-bottom: 10px solid #fff;
border-left: 10px solid rgba(0,0,0,0);
border-right: 10px solid rgba(0,0,0,0);
right: 55px;
top: -10px;
}
.popover>.close--circle {
top: -5px;
right: -5px;
}
.rule-container .arrow {
position: absolute;
width: 0;
height: 0;
top: 50%;
left: -13px;
margin-top: -5px;
}
.rule-container .arrow i {
color: #e5e5e5;
background-color: #fff;
}
.layui-card-body, .layui-card-header, .layui-form-label, .layui-form-mid,
.layui-form-select, .layui-input-block, .layui-input-inline,
.layui-textarea {
position: unset;
}
.rule-group-container {
min-height: 165px
}
.layui-layer-btn .layui-layer-btn1:hover{
color: #333 !important;
}
.layui-layer-btn a:hover{
color: #fff !important;
}

View File

@@ -0,0 +1,338 @@
#graphic_message {
/*width: 760px;*/
margin-bottom: 90px;
padding: 20px;
overflow: hidden;
background: #fff;
border-radius: 5px;
margin-top: 10px;
}
.layui-elem-quote{
margin: 0 0 20px;
}
#graphic_message .graphic-message {
position: relative;
width: 320px;
min-height: 310px;
float: left;
border: 1px solid #C5C5C5;
}
#graphic_message .editor-box {
position: relative;
margin-top: 80px;
width: 380px;
min-height: 400px;
float: left;
padding: 15px;
margin-left: 20px;
border: 1px solid #d1d1d1;
border-radius: 5px;
}
#graphic_message .arrow, #graphic_message .arrow::after {
width: 0;
height: 0;
border-style: solid;
border-width: 7px 7px 6px 0;
border-color: transparent #d1d1d1 transparent transparent;
position: absolute;
left: -7px;
top: 19px;
}
#graphic_message .arrow::after {
content: "";
border-right-color: #f8f8f8;
left: 1px;
top: -7px;
}
#graphic_message .graphic-message-list {
padding: 15px;
}
#graphic_message .graphic-message-list li {
border: 1px solid #AAA;
border-top-color: #FFF;
border-bottom-color: #DDD;
background: #FFF;
box-sizing: border-box;
cursor: pointer;
position: relative;
}
#graphic_message .graphic-message-list li:first-child {
border-top-color: #AAA;
}
#graphic_message .graphic-message-list li:last-child {
border-bottom-color: #AAA;
}
#graphic_message .graphic-message-list li content {
display: block;
margin: 10px;
position: relative;
overflow: hidden;
height: 50px;
}
#graphic_message .graphic-message-list li:first-child content {
height: 150px;
}
#graphic_message .graphic-message-list li .title {
padding-right: 70px;
max-height: 48px;
word-break: break-all;
overflow: hidden;
}
#graphic_message .graphic-message-list li:first-child .title {
position: absolute;
display: block;
width: 100%;
bottom: 0;
left: 0;
background: rgba(0, 0, 0, .5);
color: #FFF;
z-index: 2;
line-height: 30px;
overflow: hidden;
word-wrap: break-word;
word-break: break-all;
max-height: unset;
}
#graphic_message .graphic-message-list li:first-child .title span {
padding: 2px 7px;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
line-height: 26px;
}
#graphic_message .graphic-message-list li .title span {
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}
#graphic_message .graphic-message-list li .action {
position: absolute;
right: 0;
bottom: 0;
z-index: 100;
}
#graphic_message .action span.edit, #graphic_message .action span.delete {
background: rgba(0, 0, 0, .4);
color: #FFF;
font-size: 14px;
padding: 0 5px;
display: inline-block;
margin-left: 1px;
}
#graphic_message .graphic-message-list li content img {
max-width: 100%;
vertical-align: middle;
border: 0;
float: right;
width: 50px;
height: 50px;
background: #EEE;
}
#graphic_message .graphic-message-list li content .empty-img {
width: 50px;
height: 50px;
float: right;
background: #EEE;
}
#graphic_message .graphic-message-list li:first-child content img {
width: 268px;
height: auto;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
position: absolute;
}
#graphic_message .graphic-message-list li:first-child content .empty-img {
width: 268px;
height: auto;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
position: absolute;
}
#graphic_message .graphic-message-list li content .empty-hint {
height: 20px;
width: 60px;
text-align: center;
position: absolute;
top: 0;
right: -5px;
bottom: 0;
margin: auto;
color: #AAA;
font-size: 12px;
}
#graphic_message .graphic-message-list li:first-child content .empty-hint {
height: 20px;
width: 100%;
text-align: center;
position: absolute;
top: 0;
left: 0;
bottom: 7px;
margin: auto;
color: #AAA;
font-size: 16px;
}
#editor {
margin: 0 auto;
}
#graphic_message .add-graphic-message h4::before {
top: -19px;
position: absolute;
content: ' ';
border: 8px solid transparent;
border-bottom-width: 10px;
border-bottom-color: #ddd;
left: 148px;
}
#graphic_message .add-graphic-message h4::after {
position: absolute;
content: ' ';
border: 8px solid transparent;
border-bottom-width: 10px;
border-bottom-color: #f8f8f8;
top: -16px;
left: 148px;
}
#graphic_message .add-graphic-message h4 {
position: relative;
left: 0;
top: 0;
border-top: 1px solid #ddd;
-webkit-box-shadow: 0 0 0 1px #ddd;
box-shadow: 0 0 0 1px #ddd;
padding: 0 6px 4px 10px;
width: 304px;
margin: 0 auto;
text-align: center;
}
#graphic_message .add-graphic-message h4 a {
display: inline-block;
text-align: center;
font-size: 14px;
line-height: 40px;
font-weight: bold;
width: 100%;
height: 100%;
cursor: pointer;
}
#graphic_message .layui-input {
width: 270px;
}
#graphic_message .editor-box label {
display: inline-block;
margin-bottom: 7px;
}
#graphic_message .editor-box label .hint {
color: #AAA;
}
#graphic_message .editor-box .editor-cover,
#graphic_message .editor-box .editor-author,
#graphic_message .editor-box .editor-title,
#graphic_message .editor-box .editor-content {
margin-bottom: 20px;
}
#graphic_message .editor-box .editor-cover .editor-msg-label {
position: relative;
padding-left: 17px;
}
#graphic_message .editor-box .editor-cover #check_show_cover_pic {
position: absolute;
top: 4px;
left: 1px;
height: 13px;
}
#graphic_message .editor-box .editor-cover .choose-cover {
margin-bottom: 7px;
}
#graphic_message .editor-box .editor-cover .choose-cover a {
cursor: pointer;
display: block;
}
#edui1_bottombar.edui-editor-bottomContainer.edui-default {
display: none;
}
.bottom-botton {
position: absolute;
padding: 20px 0;
width: auto;
left: 50%;
transform: translateX(-50%);
bottom: -80px;
text-align: center;
z-index: 1000;
}
.loading {
position: fixed;
width: 100%;
left: 0;
text-align: center;
top: 0;
padding-top: 20%;
display: none;
}
.loading .layui-icon {
font-size: 39px;
}
.show {
display: block !important;
}
.choose-cover-pic{
margin-bottom: 10px;
width: 160px;
height: 160px;
text-align: center;
line-height: 160px;
}
.choose-cover-pic img{
max-height: 100%;
max-width: 100%;
}

View File

@@ -0,0 +1,106 @@
.user_info{
position: relative;
/* margin-left: 80px;
margin-right: 215px; */
}
.message_info{
margin-left:80px;
margin-right:80px;
width:auto;
}
.user_info .avatar{
position: absolute;
top: 0;
left:16px;
}
.user_info img{
width: 48px;
height: 48px;
}
.layui-table .layui-table-cell{
padding:1px;
}
.layui-table thead tr{
display:none;
}
.message_content{
padding-top: 2px;
padding-bottom: 2px;
color: #8d8d8d;
word-wrap: break-word;
word-break: break-all;
white-space:normal;
}
.layui-icon-reply-fill:hover{
color:#393D49 !important;
}
.headimg{
width:80px;
}
.message_list{
font-size:14px;
min-height: 46px;
/* border: 1px solid #e7e7eb;
padding: 15px;*/
}
.message_time{
margin-top: 0;
color: #8d8d8d;
}
.layui-table tbody tr:hover,.layui-table-click{
background-color:#FFF;
}
.layui-table tbody tr{
border:none;
}
.reply-box{
display:none;
border-top:1px solid #e7e7eb;
padding-top:15px;
padding-left:80px;
padding-right:80px;
}
.layui-table-header{
border:none;
}
.reply-button{
padding-top:15px;
}
.reply-button button{
min-width:100px;
}
.userinfo-box-headimg{
float: left;
width: 230px;
height: 230px;
margin-right: 20px;
}
.userinfo-box-user-info{
overflow: hidden;
height: 230px;
position: relative;
}
.userinfo-box-user-info-title{
width:20px;
margin-right:10px;
color: #8d8d8d;
}
.userinfo-box-user-info-content{
font-weight:normal;
}
.layui-layer-tips{
width:auto !important;
}
.userinfo-box-user-info .layui-card-body{
color: #333;
height: 32px;
line-height: 32px;
}
.userinfo-box-user-info-header-title{
margin-right:200px;
}
.layui-tab-title{
margin-bottom:15px;
}

View File

@@ -0,0 +1,280 @@
.right {
float: right;
}
.layui-table-page {
text-align: center;
}
.layui-table-cell {
height: auto;
line-height: 28px;
min-height: 28px;
}
/* ----------------------------------GRAPHIC_MESSAGE_LIST----------------------------- */
#graphic_message_list + div .layui-table-box .layui-table td {
padding: 10px 0;
}
#graphic_message_list + div .layui-table-box .layui-table td[data-field='value'] .layui-row.grid-demo {
border: 1px solid #DEDEDE;
border-radius: 5px;
background: #FFF;
}
#graphic_message_list + div .layui-table-box .layui-table td[data-field='value'] .layui-col-md3.article-img span {
padding: 2px 4px;
color: #fff;
}
#graphic_message_list + div .layui-table-box .layui-table td[data-field='value'] .layui-col-md12 {
padding: 2px 0;
border-bottom: 1px solid #DEDEDE;
}
#graphic_message_list + div .layui-table-box .layui-table td[data-field='value'] .layui-col-md12.read-all {
cursor: pointer;
}
#graphic_message_list + div .layui-table-box .layui-table td[data-field='value'] .layui-col-md12:first-child {
padding: 7px 0;
}
#graphic_message_list + div .layui-table-box .layui-table td[data-field='value'] .layui-col-md12:last-child {
border-bottom: none;
}
#graphic_message_list + div .layui-table-box .layui-table td[data-field='value'] .layui-col-md3 {
padding-left: 10px;
text-align: left;
}
#graphic_message_list + div .layui-table-box .layui-table td[data-field='value'] .layui-col-md3.title {
text-align: left;
font-size: 13px;
}
#graphic_message_list + div .layui-table-box .layui-table td[data-field='value'] .layui-col-md3.title a {
color: #0d73f9;
}
#graphic_message_list + div .layui-table-box .layui-table td[data-field='value'] .layui-col-md4 {
font-size: 13px;
text-align: left;
padding-left: 10px;
}
#graphic_message_list + div .layui-table-box .layui-table td[data-field='value'] .layui-col-md4.layui-col-md-offset4 {
text-align: right;
padding-right: 10px;
}
a.graphic-message-title {
width: 100%;
display: inline-block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
vertical-align: top;
}
/* ----------------------------------MATERIAL_GRAPHIC_MESSAGE_LIST----------------------------- */
#marterial_graphic_message_list + div .layui-table-box .layui-table td {
padding: 10px 0;
}
#marterial_graphic_message_list + div .layui-table-box .layui-table td[data-field='value'] .layui-row.grid-demo {
border: 1px solid #DEDEDE;
border-radius: 5px;
background: #FFF;
}
#marterial_graphic_message_list + div .layui-table-box .layui-table td[data-field='value'] .layui-col-md3.article-img span {
background: green;
color: #FFF;
padding: 2px 4px;
}
#marterial_graphic_message_list + div .layui-table-box .layui-table td[data-field='value'] .layui-col-md12 {
padding: 2px 0;
border-bottom: 1px solid #DEDEDE;
}
#marterial_graphic_message_list + div .layui-table-box .layui-table td[data-field='value'] .layui-col-md12.read-all {
cursor: pointer;
}
#marterial_graphic_message_list + div .layui-table-box .layui-table td[data-field='value'] .layui-col-md12:first-child {
padding: 7px 0;
}
#marterial_graphic_message_list + div .layui-table-box .layui-table td[data-field='value'] .layui-col-md12:last-child {
border-bottom: none;
}
#marterial_graphic_message_list + div .layui-table-box .layui-table td[data-field='value'] .layui-col-md3 {
padding-left: 10px;
text-align: left;
}
#marterial_graphic_message_list + div .layui-table-box .layui-table td[data-field='value'] .layui-col-md3.title {
text-align: left;
font-size: 13px;
width: 150px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
vertical-align: middle;
}
#marterial_graphic_message_list + div .layui-table-box .layui-table td[data-field='value'] .layui-col-md3.title a {
color: #0d73f9;
}
#marterial_graphic_message_list + div .layui-table-box .layui-table td[data-field='value'] .layui-col-md4 {
font-size: 13px;
text-align: left;
padding-left: 10px;
}
#marterial_graphic_message_list + div .layui-table-box .layui-table td[data-field='value'] .layui-col-md4.layui-col-md-offset4 {
text-align: right;
padding-right: 10px;
}
/* ----------------------------------material_text----------------------------- */
#material_text div .layui-table-box .layui-table td {
padding: 10px 0;
}
#material_text div .layui-table-box .layui-table td[data-field='value'] .layui-row.grid-demo {
border: 1px solid #DEDEDE;
border-radius: 5px;
background: #FFF;
}
#material_text div .layui-table-box .layui-table td[data-field='value'] .layui-col-md3.article-img span {
background: green;
color: #FFF;
padding: 2px 4px;
}
#material_text div .layui-table-box .layui-table td[data-field='value'] .layui-col-md12 {
padding: 2px 0;
border-bottom: 1px solid #DEDEDE;
}
#material_text div .layui-table-box .layui-table td[data-field='value'] .layui-col-md12.read-all {
cursor: pointer;
}
#material_text div .layui-table-box .layui-table td[data-field='value'] .layui-col-md12:first-child {
padding: 7px 0;
}
#material_text div .layui-table-box .layui-table td[data-field='value'] .layui-col-md12:last-child {
border-bottom: none;
}
#material_text div .layui-table-box .layui-table td[data-field='value'] .layui-col-md3 {
padding-left: 10px;
text-align: left;
}
#material_text div .layui-table-box .layui-table td[data-field='value'] .layui-col-md3.title {
text-align: left;
font-size: 13px;
width: 150px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
vertical-align: middle;
}
#material_text div .layui-table-box .layui-table td[data-field='value'] .layui-col-md3.title a {
color: #0d73f9;
}
#material_text div .layui-table-box .layui-table td[data-field='value'] .layui-col-md4 {
font-size: 13px;
text-align: left;
padding-left: 10px;
}
#material_text div .layui-table-box .layui-table td[data-field='value'] .layui-col-md4.layui-col-md-offset4 {
text-align: right;
padding-right: 10px;
}
/* --------------------------------ADD_MATERIAL_TEXT -------------------------------------- */
#add_material_text form.layui-form {
padding: 15px;
padding-right: 50px;
padding-left: 0;
}
#add_material_text .input-text-hint {
float: right;
color: #AAA;
margin-top: 5px;
}
/* -------------------------------- MATERIAL_IMAGE -----------------------------------------*/
#material_image .img-list {
padding-top: 26px;
text-align: center;
margin-bottom: 80px;
}
#material_image .img-list::after {
content: '';
display: block;
clear: both;
}
#material_image .img-list .layui-col-md2 .img {
display: inline-block;
background: #ccc;
width: 110px;
height: 110px;
text-align: center;
background-repeat: no-repeat;
background-size: cover !important;
background-position: 50% !important;
}
#material_image .buttom-button {
position: absolute;
bottom: 30px;
left: 50%;
margin-left: -66px;
}
#material_image .img-list p.layui-elip {
max-width: 110px;
padding: 5px 20px;
}
#material_image .img-list .img-check-mask {
position: absolute;
top: 0;
left: 11px;
right: 11px;
bottom: 26px;
line-height: 120px;
background: rgba(0, 0, 0, .5);
}
#material_image .img-list .img-check-mask[data-show='false'] {
display: none;
}
#material_image .img-list .layui-icon-ok {
color: #FFF;
font-size: 36px;
}
.article-img .bg-color {
color: #fff;
padding: 2px 4px;
}

View File

@@ -0,0 +1,442 @@
.hide {
display: none !important;
}
.wx-menu {
background: #fff;
height: 100%;
padding: 10px;
}
.wx-menu-preview {
width: 320px;
height: 570px;
float: left;
}
.mobile-preview {
height: 100%;
position: relative;
border: 1px solid #e7e7eb;
}
.mobile-hd {
height: 25px;
padding-top: 35px;
color: #FFF;
background: #000;
font-size: 16px;
background-image: url(../img/mobile_head.png);
text-align: center;
}
.mobile-bd {
height: 50px;
position: absolute;
bottom: 0;
left: 0;
right: 0;
border-top: 1px solid #e7e7eb;
background: transparent url(../img/mobile_foot_default.png) no-repeat 0 0;
}
.wx-menu-list {
height: 100%;
display: flex;
padding-left: 43px;
}
.wx-menu-item-box {
flex: 1;
text-align: center;
line-height: 50px;
color: #616161;
cursor: pointer;
position: relative;
}
.wx-menu-item-box.add-menu {
border-left: 1px solid #e7e7eb;
}
.wx-menu-item {
border-left: 1px solid #e7e7eb;
position: relative;
}
.wx-menu-item.active, .wx-sub-menu-item.active {
border: 1px solid;
height: 48px;
}
.wx-sub-menu-list {
position: absolute;
bottom: 62px;
width: 136px;
border: 1px solid #d0d0d0;
border-top-width: 0;
}
.wx-sub-menu-list.two, .wx-sub-menu-list.three {
width: 90.328px;
}
.wx-sub-menu-list.active-second {
left: 134.328px;
}
.wx-sub-menu-list.active-third {
left: 226.328px;
}
.wx-sub-menu-item {
border-top: 1px solid #d0d0d0;
position: relative;
}
.wx-menu-item, .wx-sub-menu-item {
line-height: 50px;
background-color: #fafafa;
text-align: center;
color: #616161;
cursor: pointer;
}
.wx-sub-menu-list .wx-sub-menu-item {
height: 50px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.wx-menu-form {
width: auto;
margin-left: 340px;
min-height: 570px;
position: relative;
}
.form-editor {
height: 100%;
position: relative;
background-color: #f4f5f9;
border: 1px solid #e7e7eb;
min-width: 560px;
}
.form-editor .form-hd {
padding: 10px 20px;
border-bottom: 1px solid #e7e7eb;
line-height: 31px;
}
.form-hd-name {
float: left;
text-align: left;
}
.form-hd-del {
text-align: right;
}
.form-editor .form-bd {
padding: 15px;
}
.item-label {
width: 5em;
float: left;
margin-top: .3em;
margin-right: 1em;
}
.item-group {
display: table-cell;
vertical-align: top;
float: none;
width: auto;
line-height: 26px;
}
.button-list-null {
position: absolute;
top: 37%;
left: 30%;
color: #999;
}
.tip {
color: #AAA;
padding: 2px 2px 2px 0;
display: inline-block;
}
.tip.error {
color: red;
}
input.error {
border-color: red !important;
}
.form-bd-content {
background-color: #fff;
padding: 0 15px 15px;
min-height: 290px;
margin: 15px 0 0;
border-radius: 7px;
overflow: hidden;
}
.item-group.menu-type {
padding-left: 17px;
}
.form-bd-item:first-child {
padding-top: 15px;
}
.form-bd-item {
min-height: 35px;
line-height: 35px;
padding-bottom: 15px
}
.input.layui-input {
display: inline-block !important;
vertical-align: top;
}
.wechat-media i.layui-icon {
color: #AAA;
font-size: 16px;
padding: 0 7px;
}
.wechat-media .layui-this i.layui-icon {
color: var(--base-color);
}
.radio-label input {
position: absolute;
left: -500vw;
top: -500vw;
}
.radio-label i.layui-icon {
color: #AAA;
}
.radio-label {
margin-right: 15px;
cursor: pointer;
display: inline-block;
height: 36px;
line-height: 36px;
}
.form-ft {
padding-top: 20px;
height: 40px;
width: 100%;
text-align: center;
}
.form-bd-list {
background: #FFF;
border-radius: 7px;
padding-left: 22px;
padding-top: 7px;
padding-bottom: 7px;
}
.material-library, .add-material {
display: inline-block;
width: 40%;
margin-top: 15px;
text-align: center;
padding: 50px 0;
border: 1px dotted #CCC;
border-radius: 15px;
}
.material-library object, .add-material object {
width: 36px;
}
.material-library {
float: left;
margin-left: 5%;
}
.material-library i, .add-material i {
background-size: 100% 100%;
background-color: #FFF;
display: block;
width: 36px;
height: 36px;
margin: auto;
margin-bottom: 10px;
}
.material-library i {
background-image: url(../img/folder.png);
}
.add-material i {
background-image: url(../img/add.png);
}
.add-material {
float: right;
margin-right: 5%;
}
.material-img {
width: 100%;
height: 100%;
}
.material-img img {
max-width: 70%;
max-height: 180px;
}
.del {
display: inline-block;
vertical-align: bottom;
color: var(--base-color);
text-decoration: solid;
cursor: pointer;
}
.menu-content .layui-textarea {
height: 170px;
resize: none;
}
.material-type {
display: inline-block;
}
.material-type span {
background: green;
color: #FFF;
padding: 2px 4px;
display: inline-block;
width: 50px;
text-align: center;
}
.graphic-message-list {
width: 400px;
border: 1px solid #CCC;
border-radius: 5px;
display: inline-block;
}
.graphic-message-list .graphic-message-content {
border-bottom: 1px solid #DDD;
padding: 5px 15px;
}
.graphic-message-list .graphic-message-content:last-child {
border-bottom: none;
}
.graphic-message-list .graphic-message-content .title {
display: inline-block;
width: 75%;
padding-left: 5px;
}
.graphic-message-list .graphic-message-content .title a {
width: 100%;
display: inline-block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
vertical-align: top;
}
.graphic-message-list .read-all {
padding: 5px 15px;
cursor: pointer;
}
.graphic-message-list .read-all::after {
content: '';
display: block;
clear: both;
}
.graphic-message-list .read-all div {
display: inline-block;
float: left;
}
.graphic-message-list .read-all i {
float: right;
margin-top: 2px;
}
.text-message {
width: 400px;
border: 1px solid #CCC;
border-radius: 5px;
display: inline-block;
}
.text-message .text-message-content {
border-bottom: 1px solid #DDD;
padding: 5px 15px;
}
.text-message .text-message-content .title {
width: 380px;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
vertical-align: middle;
border-top: 1px solid #CCC;
margin-top: 10px;
padding-top: 10px;
padding-bottom: 10px;
}
.text-message .text-message-content .title a {
word-wrap: break-word;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 4;
-webkit-box-orient: vertical;
vertical-align: middle;
}
.text-message .text-message-content .material-type {
padding-top: 5px;
}
.wx-menu-item, .wx-menu-item span, .wx-sub-menu-item, .wx-sub-menu-item span {
moz-user-select: -moz-none;
-moz-user-select: none;
-o-user-select: none;
-khtml-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
}
.article-img .bg-color {
color: #fff;
padding: 2px 4px;
}
.text-r{
width: 100%;
height: 100px;
border-color: #ccc;
resize:none
}

View File

@@ -0,0 +1,77 @@
.preview-box {
max-width: 600px;
height: 779px;
background: #FFF;
margin: 0 auto;
overflow-x: hidden;
overflow-y: auto;
position: relative;
}
.rich-media-thumb img {
max-width: 100%;
height: auto;
max-height: 100%;
}
.preview-box::-webkit-scrollbar {
width: 5px;
}
.preview-box::-webkit-scrollbar-track {
background-color: #EEE;
}
.preview-box::-webkit-scrollbar-thumb {
background-color: #CCC;
}
.preview-box::-webkit-scrollbar-thumb:hover {
background-color: #AAA
}
.preview-box::-webkit-scrollbar-thumb:active {
background-color: #AAA
}
.preview-box > img.head {
position: relative;
width: 100%;
top: 0;
left: 0;
}
.preview-box > .graphic-message {
position: relative;
padding-left: 15px;
padding-right: 15px;
padding-bottom: 150px;
}
.preview-box > .graphic-message > span.time, .preview-box > .graphic-message > span.author {
display: inline-block;
color: #AAA;
margin-top: 7px;
}
.preview-box > .graphic-message > content {
display: block;
position: relative;
margin-top: 20px;
}
.preview-box > .graphic-message > .graphic-message-content {
overflow-x: hidden;
overflow-y: auto;
}
.preview-box > .graphic-message a.original-text {
position: absolute;
bottom: 50px;
left: 0px;
color: #607fa6;
}
img {
max-width: 100% !important;
}

View File

@@ -0,0 +1,119 @@
.qrcode-body {
padding: 10px;
margin: 10px 0;
background: #fff;
}
#albumList {
overflow: hidden;
}
.input-file {
position: absolute;
top: -10px !important;
right: 9px !important;
height: 26px !important;
width: 94px !important;
filter: alpha(opacity:0) !important;
opacity: 0 !important;
line-height: 26px;
}
.qrcode_button {
background-color: #51a351;
border: none;
margin-top: 15px;
width: 100%;
color: #FFF;
padding: 5px 10px;
}
.check {
position: absolute;
bottom: 0px;
width: 100%;
height: 100%;
right: -1px;
background-color: rgba(0, 0, 0, 0.8);
display: none;
}
.check div {
width: 60%;
height: 30px;
margin-left: 20%;
border: 1px solid #fff;
margin-top: 10%;
text-align: center;
}
.check div:hover {
background: var(--base-color);
border: 1px solid var(--base-color);
}
.check div span {
font-size: 15px;
color: #fff;
line-height: 30px;
}
.options-btn {
padding-bottom: 10px;
}
#albumList li {
opacity: 1;
height: auto;
position: relative;
float: left;
margin-right: 1%;
margin-top: 10px;
margin-bottom: 10px;
line-height: 20px;
overflow: hidden;
}
.img-block {
width: 100%;
}
.qrcode {
width: 320px;
height: 569px;
background: #f5f5f5;
position: relative;
}
.qrcode #imgLogo {
max-width: 320px;
max-height: 569px;
}
.tdrag-header {
width: 45px;
height: 45px;
position: absolute;
left: 59px; top: 15px;
cursor: move;
}
.tdrag-logo {
width: 43px;
position: absolute;
left: 60px;
top: 210px;
cursor: move;
}
.tdrag-code{
width: 144px;
height: 144px;
position: absolute;
left: 70px;
top: 300px;
cursor: move;
}
.tdrag-name{
font-size: 14px;
color: #000;
position: absolute;
left: 128px;
top: 23px;
cursor: move;
}

View File

@@ -0,0 +1,811 @@
button, input, select, textarea {
font-family: inherit;
font-size: 100%;
margin: 0;
}
input::-webkit-input-placeholder, textarea::-webkit-input-placeholder {
color: #999;
}
input:-moz-placeholder, textarea:-moz-placeholder {
color: #999;
}
.empty{width: 100%;margin-top: 15px;text-align: center;padding: 75px 0;}
input::-moz-placeholder, textarea::-moz-placeholder {
color: #999;
}
input:-ms-input-placeholder, textarea:-ms-input-placeholder {
color: #999;
}
li {
list-style: none;
}
a:-webkit-any-link {
color: #333;
cursor: pointer;
text-decoration: none;
}
.reply-opts a:-webkit-any-link {
color: var(--base-color);
cursor: pointer;
text-decoration: none;
}
.reply-opts .delet-color:-webkit-any-link {
color: #ff3d3d;
}
.rule-group .rule-meta h3 .rule-opts .delet-color {
color: #ff3d3d;
}
button {
display: inline-block;
padding: 0 22px;
min-width: 54px;
line-height: 2.42857143;
vertical-align: middle;
text-align: center;
text-decoration: none;
border-radius: 3px;
font-size: 14px;
cursor: pointer;
border-width: 1px;
border-style: solid;
box-sizing: content-box;
}
.layui-body {
overflow-y: auto !important;
}
.hide {
display: none;
}
.page-title h2 {
font-size: 26px;
font-weight: 400;
line-height: 1;
margin-bottom: 20px;
}
.type-menu {
text-align: left;
height: 50px;
line-height: 40px;
border-bottom: 1px solid #E0E1E2;
font-size: 16px;
}
.type-menu li {
float: left;
margin-right: 24px;
line-height: 40px;
font-size: 16px;
}
.replay-info {
padding: 15px 0 0;
height: 30px;
}
.weixin-normal {
margin-top: 15px;
position: relative;
}
.replay-info .info {
float: left;
}
.replay-info h3 {
display: inline-block;
font-size: 20px;
font-weight: 400;
line-height: 1;
margin-right: 10px;
}
.replay-info h3:last-child {
font-style: normal;
}
.replay-button {
float: right;
position: relative;
}
.replay-button > div {
float: left;
}
.replay-button:after {
content: '';
clear: both;
}
.replay-button > label {
background-color: #1AAD19;
border-color: #1AAD19;
color: #FFFFFF;
display: inline-block;
min-width: 54px;
line-height: 2.42857143;
vertical-align: middle;
text-align: center;
text-decoration: none;
border-radius: 3px;
font-size: 14px;
cursor: pointer;
border-width: 1px;
border-style: solid;
box-sizing: content-box;
}
.replay-button .layui-layer-page {
text-align: left;
}
.replay-button .layui-form > .layui-btn {
display: block;
margin: auto;
margin-top: 30px;
}
.replay-button label > input {
position: absolute;
left: -9999em;
}
.replay-button .search {
display: inline-block;
vertical-align: middle;
margin-right: .5em;
width: 50px;
}
.replay-button .down {
float: left;
}
.replay-button .down button {
padding: 14px 12px 10px 12px;
min-width: unset;
}
.down .show-menu li a {
color: #00A717;
}
.down .show-menu li a {
padding: 0 15px;
}
.rule-group {
border: 1px solid #e5e5e5;
margin-bottom: 20px;
border-radius: 2px;
background: #fff;
}
.rule-group:hover {
border: 1px solid #e5e5e5;
}
.rule-group .rule-meta {
padding: 5px 10px;
}
.rule-group .rule-meta h3 .rule-opts a:hover {
color: var(--base-color);
}
.rule-group .rule-meta h3 {
width: 100%;
position: relative;
font-size: 14px;
margin: 0;
line-height: 1.5em;
font-weight: bold;
}
.rule-group .rule-meta h3 .rule-opts {
position: absolute;
top: 0;
right: 0px;
font-weight: normal;
font-size: 12px;
color: #ddd;
}
.rule-group .rule-meta h3 .rule-opts a {
color: var(--base-color);
}
.rule-group .rule-body {
border-top: 1px solid #e5e5e5;
/* margin: 0 0 10px;
padding-top: 5px; */
overflow: auto;
}
.rule-group .rule-body:before, .rule-group .rule-body:after {
display: table;
content: "";
line-height: 0;
}
/* .rule-group .long-dashed {
position: absolute;
top: 80px;
width: 100%;
border-bottom: 1px dashed #e5e5e5;
} */
.weixin-normal .rule-keywords {
float: left;
margin-top:10px;
}
.rule-group .rule-inner {
font-size: 12px;
padding: 0 10px 5px;
}
.rule-group .rule-inner h4 {
color: #000;
font-weight: normal;
font-size: 14px;
}
.rule-group .keyword-containe {
padding-top: 5px;
margin-bottom: 5px;
}
.rule-group .info:empty {
padding: 0;
}
.rule-group .keyword-list, .reply-list {
padding-top: 10px;
}
.keyword-list .keyword {
position: relative;
margin-right: 10px;
margin-bottom: 10px;
height: 30px;
vertical-align: middle;
cursor: pointer;
display: inline-block;
}
.keyword .close--circle {
display: none;
}
.keyword-list .keyword:hover .close--circle {
display: block;
}
.complex-content .close--circle ,.keyword-list .close--circle {
position: absolute;
z-index: 91;
top: -9px;
right: -9px;
width: 20px;
height: 20px;
font-size: 16px;
line-height: 20px;
color: #fff;
text-align: center;
cursor: pointer;
background: rgba(153, 153, 153, 0.6);
border-radius: 10px;
}
.keyword .value {
background-color: #fff;
text-align: center;
display: inline-block;
height: 20px;
padding: 2px 10px;
font-size: 12px;
line-height: 20px;
color: #333;
vertical-align: middle;
border-radius: 2px 0 0 2px;
border: 1px solid #ccc;
}
.input-append .add-on, .input-prepend .add-on {
display: inline-block;
width: auto;
height: 20px;
min-width: 16px;
padding: 2px 7px;
font-size: 10px;
font-weight: 400;
line-height: 20px;
text-align: center;
text-shadow: 0 1px 0 #fff;
background-color: #f7f7f7;
border: 1px solid #ccc;
margin-left: -4px;
border-radius: 0 2px 2px 0;
vertical-align: middle;
}
/* .rule-group hr.dashed {
border-bottom: 1px dashed #d7d7d7;
border-top: none;
margin: 0;
background-color: rgb(255, 255, 255);
} */
.layui-textarea {
resize:none
}
.rule-group .opt {
margin-top: 5px;
}
.rule-group .opt a {
color: var(--base-color);
}
.inner-keywordbox a {
color: var(--base-color);
}
.weixin-normal .rule-replies {
position: static;
float: left;
border-left: 1px solid #f7f7f7;
padding-top: 10px;
min-height: 115px;
}
.rule-group .rule-inner {
font-size: 12px;
padding: 0 10px 5px;
}
.rule-group .rule-inner .inner-keywordbox {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
padding: 8px 15px;
background: #f7f7f7;
border-radius: 2px;
}
.rule-group .rule-inner h4 {
color: #000;
font-weight: normal;
font-size: 14px;
}
.rule-group .reply-container, .keyword-container {
padding: 5px 0;
}
.rule-group .info {
padding: 5px 0;
color: #999;
}
.rule-group .info:empty {
padding: 0;
}
ol.reply-list {
list-style-type: decimal !important;
}
.reply-list li, .quick-dropmenu li {
position: relative;
padding: 8px 90px 8px 5px;
margin-left: 20px;
list-style: unset;
}
.reply-list .reply-opts a:hover {
color: var(--base-color);
}
.reply-cont {
display: inline-block;
max-width: 100%;
}
.reply-summary {
display: inline-block;
max-width: 360px;
word-break: break-all;
word-wrap: break-word;
vertical-align: top;
}
.reply-list .reply-opts, .quick-dropmenu .reply-opts {
position: absolute;
top: 8px;
right: 5px;
}
.reply-list li::after, .quick-dropmenu li::after {
content: "";
position: absolute;
border-bottom: 1px dashed #d7d7d7;
bottom: 0;
left: -20px;
right: 0;
}
.reply-list li:last-child::after, .quick-dropmenu li:last-child::after {
border-bottom: none;
}
.rule-group .opt .disable-opt {
color: #999;
}
.badge-success, .label-success {
display: inline-block;
padding: 2px 4px;
font-size: 12px;
line-height: 14px;
color: #fff;
white-space: nowrap;
background-color: #1AAD19;
}
/***************************************/
.misc {
height: 45px;
}
.misc > a {
display: inline-block;
padding: 7px 10px;
color: var(--base-color) !important;
border-bottom: 2px solid #fff;
}
.misc .active {
color: var(--base-color)!important;
border-color: var(--base-color);
}
.others {
display: inline-block;
position: relative;
}
.others > a {
padding: 10px;
color: var(--base-color) !important
}
.pull-right {
color: #ccc;
float: right;
}
.dropdown-menu {
display: none;
position: absolute;
z-index: 100;
top: 25px;
left: 10px;
width: 110px;
height: 110px;
font-size: 12px;
border: 1px solid rgba(0, 0, 0, 0.125);
cursor: pointer;
background: #fff;
border-radius: 10px;
padding: 10px 0;
overflow: auto;
}
.dropdown-menu li {
padding: 0 10px;
line-height: 30px;
color: #333;
}
.others:hover .dropdown-menu {
display: block;
}
.dropdown-menu li:hover {
background: var(--base-color);
}
.dropdown-menu li:hover a {
color: #fff;
}
.complex-backdrop {
display: none;
position: absolute;
top: 75px;
left: 25px;
width: 86%;
height: 36.5%;
background-color: #fff;
/* -webkit-box-shadow: inset 0px 1px 3px rgba(0, 0, 0, 0.25);
box-shadow: inset 0px 1px 3px rgba(0, 0, 0, 0.25); */
/* border:1px solid #E6E6E6; */
/* padding-top: 12px; */
}
.layui-layout-admin .layui-body .body-content {
background: #f8f8f8;
padding: 0;
}
.fourstage-nav .layui-tab-title {
background: #fff;
height: 50px;
}
.fourstage-nav .layui-tab-title li {
line-height: 48px;
margin: 0 15px;
}
.keyword-content {
background: #fff;
padding: 15px;
margin-top: 15px;
}
[lay-id=marterial_graphic_message_list] .article-img .bg-color {
background-color: #1aad19 !important;
}
.layui-table-view{
background-color: red!important;
}
.complex-content {
padding: 15px;
}
.ng.ng-image {
width: 80px;
height: 80px;
border: none;
text-align: center;
}
.insert-connect {
position: absolute;
right: 10px;
bottom: 20px;
color: var(--base-color) !important;
}
.ng {
position: relative;
vertical-align: top;
width: 250px;
border-radius: 5px;
border: 1px solid #eee;
background-color: #fff;
margin-bottom: 5px;
display: inline-block;
}
.picture > img {
width: auto;
max-width: 100%;
height: auto;
max-height: 100%;
}
.msg-music-thumb {
height: 100px;
text-align: center;
}
.msg-music-thumb a {
width: 100%;
height: 100%;
border: 1px dashed #F2F2F2;
display: block;
line-height: 100px;
color: var(--base-color);
}
#music .layui-textarea {
min-height: 60px;
}
.voice-player {
border-radius: 5px;
position: relative;
border: 1px solid #85ac4c;
display: inline-block;
width: 90px;
height: 25px;
padding: 0 6px 0 7px;
font-size: 12px !important;
line-height: 25px;
cursor: pointer;
background: #a0ce3d;
vertical-align: middle;
margin-left: 7px;
}
.voice-player::before {
position: absolute;
content: "";
left: -13px;
top: 6px;
width: 0;
height: 0;
border: 6px solid transparent;
border-right: 6px solid #85ac4c;
}
.voice-player .stop {
display: inline-block;
color: #fff;
text-shadow: 1px 1px 1px #8ab433;
}
.popover .close--circle {
z-index: initial;
}
.close--circle {
position: absolute;
z-index: 91;
top: 5px;
right: 10px;
font-size: 28px;
color: #333;
text-align: center;
cursor: pointer;
}
.voice-player .second {
display: none;
float: right;
font-size: 12px;
color: #476600;
margin-left: 2px;
}
.voice-player .play {
display: inline-block;
width: 17px;
height: 20px;
margin-top: 2px;
}
.voice-player::after {
position: absolute;
content: "";
left: -12px;
top: 6px;
width: 0;
height: 0;
border: 6px solid transparent;
border-right: 6px solid #a0ce3d;
}
/*其他*/
.ng .close--circle {
top: -9px;
right: -9px;
}
.ng .ng-item {
border-bottom: 1px solid #eee;
overflow: hidden;
padding: 5px 9px;
}
.ng .label {
vertical-align: middle;
}
/*图文*/
.ng .ng-title {
display: inline-block !important;
overflow: hidden !important;
white-space: nowrap !important;
text-overflow: ellipsis !important;
line-height: 16px;
min-height: 16px;
vertical-align: middle;
max-width: 180px;
}
a.new-window {
color: var(--base-color);
}
.ng .ng-item.view-more {
color: #666;
}
.ng .ng-item.view-more a {
color: #666;
}
/*************************************/
.rule-container .add_reply-top{
position: absolute;
width: 0px;
height: 0px;
line-height: 0px;/*为了防止ie下出现题型*/
border-bottom: 10px solid #fff;
border-left: 10px solid rgba(0,0,0,0);
border-right: 10px solid rgba(0,0,0,0);
right: 55px;
top: -10px;
}
.popover > .close--circle {
top: -5px;
right: -5px;
}
.rule-container .arrow {
position: absolute;
width: 0;
height: 0;
top: 50%;
left: -13px;
margin-top: -5px;
}
.rule-container .arrow i {
color: #e5e5e5;
background: #fff;
}
.rule-group-container {
position: relative;
min-height: 165px
}
.search {
position: absolute;
z-index: 1;
top: 1px;
right: 0;
}
.layui-form-item .layui-textarea {
width: 100%;
}
.layui-form.hyperlink {
padding: 10px;
margin-bottom: 0;
}
.layui-form.hyperlink .layui-form-item {
margin-bottom: 0;
}
.layui-layout-shop .fourstage-nav ul li.layui-this:after {
border: none !important;
width: 100%;
height: 4px;
background-color: var(--base-color);
border-radius: 30px !important;
position: absolute;
top: 40px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1003 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Some files were not shown because too many files have changed in this diff Show More