后端框架:FastAdmin (基于 ThinkPHP)
前端技术:Vue.js + 微信 JS-SDK
微信接口:JS-SDK 分享接口
1. 创建微信配置控制器
首先,我们需要创建一个专门处理微信 JS-SDK 配置的控制器。
创建文件:application/api/controller/Wechat.php
<?php
namespace app\api\controller;
use app\common\controller\Api;
use think\Cache;
use think\Log;
use think\Request;
use think\Config;
/**
* 微信JS-SDK配置接口
*/
class Wechat extends Api
{
protected $noNeedLogin = ['config'];
protected $noNeedRight = ['config'];
private $config;
public function _initialize()
{
parent::_initialize();
$this->config = Config::get('wechat');
}
/**
* 获取微信JS-SDK配置
*/
public function config()
{
$url = $this->request->post('url', '');
if (empty($url)) {
$this->error('缺少必要参数: url');
}
if (!filter_var($url, FILTER_VALIDATE_URL)) {
$this->error('无效的URL格式');
}
$accessToken = $this->getAccessToken();
if (!$accessToken) {
$this->error('获取access_token失败');
}
$ticket = $this->getJsapiTicket($accessToken);
if (!$ticket) {
$this->error('获取jsapi_ticket失败');
}
$nonceStr = $this->generateNonceStr();
$timestamp = time();
$signature = $this->generateSignature($ticket, $nonceStr, $timestamp, $url);
$result = [
'appId' => $this->config['app_id'],
'timestamp' => $timestamp,
'nonceStr' => $nonceStr,
'signature' => $signature
];
$this->success('获取成功', $result);
}
/**
* 获取access_token
*/
private function getAccessToken()
{
$cacheKey = 'wechat_access_token';
$accessToken = Cache::get($cacheKey);
if ($accessToken) {
Log::info('使用缓存的access_token: ' . $accessToken);
return $accessToken;
}
$url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$this->config['app_id']}&secret={$this->config['app_secret']}";
$response = $this->httpGet($url);
if (!$response) {
Log::error('获取access_token网络请求失败');
return false;
}
$data = json_decode($response, true);
if (!$data || !isset($data['access_token'])) {
Log::error('获取access_token失败: ' . $response);
return false;
}
// 缓存access_token(提前5分钟过期)
Cache::set($cacheKey, $data['access_token'], $data['expires_in'] - 300);
Log::info('获取新的access_token: ' . $data['access_token']);
return $data['access_token'];
}
/**
* 获取jsapi_ticket
*/
private function getJsapiTicket($accessToken)
{
$cacheKey = 'wechat_jsapi_ticket';
$ticket = Cache::get($cacheKey);
if ($ticket) {
Log::info('使用缓存的jsapi_ticket: ' . $ticket);
return $ticket;
}
$url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token={$accessToken}&type=jsapi";
$response = $this->httpGet($url);
if (!$response) {
Log::error('获取jsapi_ticket网络请求失败');
return false;
}
$data = json_decode($response, true);
if (!$data || !isset($data['ticket'])) {
Log::error('获取jsapi_ticket失败: ' . $response);
return false;
}
// 缓存jsapi_ticket(提前5分钟过期)
Cache::set($cacheKey, $data['ticket'], $data['expires_in'] - 300);
Log::info('获取新的jsapi_ticket: ' . $data['ticket']);
return $data['ticket'];
}
/**
* 生成签名
*/
private function generateSignature($ticket, $nonceStr, $timestamp, $url)
{
$string = "jsapi_ticket={$ticket}&noncestr={$nonceStr}×tamp={$timestamp}&url={$url}";
$signature = sha1($string);
Log::info('签名字符串: ' . $string);
Log::info('生成签名: ' . $signature);
return $signature;
}
/**
* 生成随机字符串
*/
private function generateNonceStr($length = 16)
{
$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$str = '';
for ($i = 0; $i < $length; $i++) {
$str .= $chars[mt_rand(0, strlen($chars) - 1)];
}
return $str;
}
/**
* HTTP GET请求
*/
private function httpGet($url)
{
$curl = curl_init();
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_TIMEOUT, 30);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($curl, CURLOPT_URL, $url);
$response = curl_exec($curl);
$httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
curl_close($curl);
if ($httpCode !== 200) {
Log::error("HTTP请求失败,状态码: {$httpCode}");
return false;
}
return $response;
}
}
2. 创建微信配置文件
创建文件:application/extra/wechat.php
<?php return [ // 微信公众号配置 'app_id' => 'wx12345def', // 替换为你的微信公众号AppID 'app_secret' => 'your_app_secret_here', // 替换为你的微信公众号AppSecret ];
3. 配置路由
编辑文件:application/api/route.php
<?php
use think\Route;
// 微信相关接口
Route::group('wechat', function () {
Route::post('config', 'api/Wechat/config');
});
// 或者直接配置
Route::post('api/wechat/config', 'api/Wechat/config');
4. 配置跨域(如需要)
编辑文件:application/api/behavior/CORS.php
<?php
namespace app\api\behavior;
class CORS
{
public function appInit(&$params)
{
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Headers: Authorization, Content-Type, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, X-Requested-With');
header('Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE');
header('Access-Control-Max-Age: 1728000');
if (request()->isOptions()) {
exit();
}
}
}
1. 引入微信 JS-SDK
在 HTML 页面中引入微信 JS-SDK:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>微信分享测试</title> </head> <body> <!-- 页面内容 --> <!-- 微信JS-SDK --> <script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script> <!-- 你的业务代码 --> <script src="./js/wechat-share.js"></script> </body> </html>
2. 创建微信分享工具类
创建文件:js/wechat-share.js
/**
* 微信分享工具类
*/
class WechatShare {
constructor() {
this.isConfigured = false;
this.shareConfig = {};
}
/**
* 检查是否在微信环境
*/
static isWechat() {
const ua = navigator.userAgent.toLowerCase();
return ua.includes('micromessenger');
}
/**
* 获取微信JS-SDK配置
*/
static async getWechatConfig(url) {
try {
const response = await fetch('/api/wechat/config', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ url })
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const result = await response.json();
if (result.code !== 1) {
throw new Error(result.msg || '获取微信配置失败');
}
return result.data;
} catch (error) {
console.error('获取微信配置失败:', error);
throw error;
}
}
/**
* 配置微信JS-SDK
*/
async config(config) {
return new Promise((resolve, reject) => {
wx.config({
debug: false, // 生产环境设为false
appId: config.appId,
timestamp: config.timestamp,
nonceStr: config.nonceStr,
signature: config.signature,
jsApiList: [
'updateAppMessageShareData', // 新版分享到朋友
'updateTimelineShareData', // 新版分享到朋友圈
'onMenuShareAppMessage', // 旧版分享到朋友(兼容)
'onMenuShareTimeline' // 旧版分享到朋友圈(兼容)
]
});
wx.ready(() => {
console.log('微信JS-SDK配置成功');
this.isConfigured = true;
this.setupShare();
resolve(true);
});
wx.error((res) => {
console.error('微信JS-SDK配置失败:', res);
reject(res);
});
});
}
/**
* 设置分享配置
*/
setShareConfig(config) {
this.shareConfig = {
title: config.title || document.title,
desc: config.desc || document.querySelector('meta[name="description"]')?.content || '',
link: config.link || window.location.href,
imgUrl: config.imgUrl || this.getDefaultShareImage()
};
// 如果已经配置好了,立即设置分享
if (this.isConfigured) {
this.setupShare();
}
}
/**
* 获取默认分享图片
*/
getDefaultShareImage() {
// 尝试获取页面中的第一张图片
const firstImg = document.querySelector('img');
if (firstImg && firstImg.src) {
return firstImg.src;
}
// 或者返回网站logo
return `${window.location.origin}/static/images/logo.png`;
}
/**
* 设置分享内容
*/
setupShare() {
if (!this.isConfigured || !this.shareConfig.title) {
return;
}
console.log('设置分享内容:', this.shareConfig);
// 新版微信分享API
wx.updateAppMessageShareData({
title: this.shareConfig.title,
desc: this.shareConfig.desc,
link: this.shareConfig.link,
imgUrl: this.shareConfig.imgUrl,
success: () => {
console.log('分享到朋友设置成功');
},
fail: (res) => {
console.error('分享到朋友设置失败:', res);
}
});
wx.updateTimelineShareData({
title: this.shareConfig.title,
link: this.shareConfig.link,
imgUrl: this.shareConfig.imgUrl,
success: () => {
console.log('分享到朋友圈设置成功');
},
fail: (res) => {
console.error('分享到朋友圈设置失败:', res);
}
});
// 兼容旧版API
wx.onMenuShareAppMessage({
title: this.shareConfig.title,
desc: this.shareConfig.desc,
link: this.shareConfig.link,
imgUrl: this.shareConfig.imgUrl,
success: () => {
console.log('用户点击分享到朋友');
this.onShareSuccess('friend');
},
cancel: () => {
console.log('用户取消分享到朋友');
}
});
wx.onMenuShareTimeline({
title: this.shareConfig.title,
link: this.shareConfig.link,
imgUrl: this.shareConfig.imgUrl,
success: () => {
console.log('用户点击分享到朋友圈');
this.onShareSuccess('timeline');
},
cancel: () => {
console.log('用户取消分享到朋友圈');
}
});
}
/**
* 分享成功回调
*/
onShareSuccess(type) {
// 可以在这里添加分享成功的统计或其他逻辑
console.log(`分享成功: ${type}`);
// 发送分享统计到后端
this.sendShareAnalytics(type);
}
/**
* 发送分享统计
*/
async sendShareAnalytics(type) {
try {
await fetch('/api/analytics/share', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
type: type,
title: this.shareConfig.title,
url: this.shareConfig.link,
timestamp: Date.now()
})
});
} catch (error) {
console.error('发送分享统计失败:', error);
}
}
}
// 导出工具类
window.WechatShare = WechatShare;
3. 在页面中使用微信分享
创建文件:js/app.js
// 页面加载完成后初始化微信分享
document.addEventListener('DOMContentLoaded', async function() {
// 检查是否在微信环境
if (!WechatShare.isWechat()) {
console.log('非微信环境,跳过微信分享配置');
return;
}
// 创建微信分享实例
const wechatShare = new WechatShare();
try {
// 获取当前页面URL(去除hash部分)
const currentUrl = window.location.href.split('#')[0];
// 获取微信配置
console.log('正在获取微信配置...');
const config = await WechatShare.getWechatConfig(currentUrl);
// 配置微信JS-SDK
await wechatShare.config(config);
// 设置分享内容
wechatShare.setShareConfig({
title: '三个桃子·时间里的约定|「Epic Soul」人文阅读体 issue01',
desc: '每个生命,都有史诗般的灵魂。一颗从时间里走来的果实,在等时间里重逢的人',
link: 'https://p.test.com/?from=wechat_share',
imgUrl: 'https://p.test.com/images/share-cover.jpg'
});
console.log('微信分享配置完成');
} catch (error) {
console.error('微信分享初始化失败:', error);
}
});
// 动态更新分享内容的函数
function updateShareContent(title, desc, link, imgUrl) {
if (window.wechatShareInstance) {
window.wechatShareInstance.setShareConfig({
title,
desc,
link,
imgUrl
});
}
}
4. Vue.js 项目中的使用方式
如果你在 Vue 项目中使用,可以这样封装:
创建文件:src/utils/wechat.js
import WechatShare from './wechat-share.js';
export class WechatUtils {
constructor() {
this.wechatShare = null;
this.isInitialized = false;
}
async init() {
if (this.isInitialized) return;
if (!WechatShare.isWechat()) {
console.log('非微信环境');
return;
}
try {
this.wechatShare = new WechatShare();
const currentUrl = window.location.href.split('#')[0];
const config = await WechatShare.getWechatConfig(currentUrl);
await this.wechatShare.config(config);
this.isInitialized = true;
console.log('微信分享初始化成功');
} catch (error) {
console.error('微信分享初始化失败:', error);
}
}
setShare(shareConfig) {
if (this.wechatShare) {
this.wechatShare.setShareConfig(shareConfig);
}
}
}
export default new WechatUtils();
在 Vue 组件中使用:
<template>
<div class="page">
<h1>{{ pageTitle }}</h1>
<p>{{ pageDesc }}</p>
</div>
</template>
<script setup>
import { onMounted, ref } from 'vue';
import wechatUtils from '@/utils/wechat';
const pageTitle = ref('文章标题');
const pageDesc = ref('文章描述');
onMounted(async () => {
// 初始化微信分享
await wechatUtils.init();
// 设置分享内容
wechatUtils.setShare({
title: pageTitle.value,
desc: pageDesc.value,
link: window.location.href,
imgUrl: 'https://your-domain.com/share-image.jpg'
});
});
</script>