
uni.getSystemInfoSync() 返回参数:locationEnabled,总是true
文档说是地理位置的系统开关是否打开。但用户在关闭定位权限情况下也返回true。
文档说是地理位置的系统开关是否打开。但用户在关闭定位权限情况下也返回true。

uniCloud 外部系统联登 注册功能 C# 完整示例
APP端
this.$http
.post(`/uniCloudRegister`, {
clientInfo:JSON.stringify(uni.getSystemInfoSync())
})
.then(res =>{
uni.stopPullDownRefresh()
uni.hideNavigationBarLoading()
console.info(res)
}).catch(err => {
console.error(err)
uni.stopPullDownRefresh()
uni.hideNavigationBarLoading()
})
后台接口 UserController:ApiController
ConstantsConstants//业务系统登录后才需要联登到 uniCloud,所以不需要在注册时执行,而是单独给出了注册功能的接口
[HttpPost]
[Route("uniCloudRegister")]
public async Task<HttpResponseMessage> uniCloudRegister([FromBody] UniClient client)
{
HttpResponseMessage response = null;
try
{
//此为验证当前系统token并转为 user类的工具,这里就不给出详细示例了
var ui = JwtHelper.AnalysisToken(HttpContext.Current.Request);
//见 Utils
string uniAppUserName = Utils.GetExternalUid(ui);
string nonce = Utils.GetNonce();
// 获取当前时间戳(精确到毫秒)
long timestamp = Utils.GetNowTimeStamp();
Dictionary<string, string> paramsDictionary = new Dictionary<string, string>
{
{ "externalUid", uniAppUserName},
{ "nickname", ui.Name},
};
//签名算法见 Utils
string signature = Utils.GetSignature(paramsDictionary, nonce, timestamp);
//client.clientInfo 为JSON.stringify(uni.getSystemInfoSync()) 这里再转回json对象
var cliInfo = JsonConvert.DeserializeObject<JObject>(client.clientInfo);
Dictionary<string, object> param = new Dictionary<string, object>();
param.Add("clientInfo", cliInfo);
param.Add("uniIdToken", "");
param.Add("params", paramsDictionary);
// 将对象序列化为JSON字符串
string jsonContent = JsonConvert.SerializeObject(param);
Dictionary<string, string> headers = new Dictionary<string, string>()
{
{"uni-id-nonce",nonce },
{"uni-id-timestamp",timestamp + ""},
{"uni-id-signature",signature}
};
var res = await HttpHelper.SendPostAsync(Constants.PushRegister, jsonContent, headers);
if (res.Count > 0)
{
response = Request.CreateResponse(System.Net.HttpStatusCode.OK, res);
}
else
{
response = Request.CreateResponse(System.Net.HttpStatusCode.BadRequest, "error");
}
}
catch (Exception ex)
{
response = Request.CreateErrorResponse(System.Net.HttpStatusCode.InternalServerError, "error");
}
return response;
}
Constants
//uniapp 外部系统联登 https://doc.dcloud.net.cn/uniCloud/uni-id/cloud-object.html#external
public static readonly string PushUrl = "you url";
//uniapp 注册
public static readonly string PushRegister = PushUrl + "externalRegister";
//uniapp 登录
public static readonly string PushLogin = PushUrl + "externalLogin";
//uniapp 修改信息
public static readonly string PushUpdateUser = PushUrl + "updateUserInfoByExternal ";
UniClient
public class UniClient
{
public string clientInfo { get; set; }
public string uniIdToken { get; set; }
}
Utils
/// <summary>
/// 根据用户信息 获取uniapp的uid, 这个就根据自己的业务来处理
/// </summary>
/// <param name="ui"></param>
/// <returns></returns>
public static string GetExternalUid(UserInfo ui)
{
//分别为角色ID、 用户ID和用户账号
return ui.Role + "_" + ui.Id + "_" + ui.userName;
}
//获取随机字符串 这里其实可以固定返回一组字符串
public static string GetNonce()
{
return GenerateRandomStringLinq(8);
}
private static string GenerateRandomStringLinq(int length)
{
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
Random random = new Random();
return new string(Enumerable.Repeat(chars, length)
.Select(s => s[random.Next(s.Length)]).ToArray());
}
/// <summary>
/// uniapp 鉴权签名算法
/// </summary>
/// <param name="parameters"></param>
/// <param name="nonce"></param>
/// <param name="timestamp"></param>
/// <returns></returns>
public static string GetSignature(Dictionary<string, string> parameters, string nonce, long timestamp)
{
string paramsStr = GetParamsString(parameters);
using (HMACSHA256 hmacSha256 = new HMACSHA256(Encoding.UTF8.GetBytes(Constants.RequestAuthSecret + nonce)))
{
string message = timestamp.ToString() + paramsStr;
byte[] messageBytes = Encoding.UTF8.GetBytes(message);
byte[] hashBytes = hmacSha256.ComputeHash(messageBytes);
return ByteArrayToHexString(hashBytes).ToUpper();
}
}
/// <summary>
/// 获取当前时间戳 单位毫秒
/// </summary>
/// <returns></returns>
public static long GetNowTimeStamp()
{
return (long)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalMilliseconds;
}
private static string GetParamsString(Dictionary<string, string> parameters)
{
var keys = new List<string>(parameters.Keys);
keys.Sort();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < keys.Count; i++)
{
if (i != 0)
{
sb.Append("&");
}
sb.Append(keys[i]).Append("=").Append(parameters[keys[i]]);
}
return sb.ToString();
}
private static string ByteArrayToHexString(byte[] bytes)
{
StringBuilder sb = new StringBuilder();
foreach (byte b in bytes)
{
string hex = b.ToString("x2");
sb.Append(hex);
}
return sb.ToString();
}
HttpHelper
/// <summary>
/// POST异步请求
///
/// </summary>
/// <param name="url">请求url</param>
/// <param name="jsonContent"></param>
/// <param name="headers"></param>
/// <returns></returns>
// 发送POST请求的函数
public static async Task<JObject> SendPostAsync(string url, string jsonContent, Dictionary<string, string> headers = null)
{
using (var client = new HttpClient())
{
var jsonObject = new JObject();
if (headers != null)
{
foreach (KeyValuePair<string, string> header in headers)
{
client.DefaultRequestHeaders.Add(header.Key, header.Value);
}
}
try
{
// 创建一个HttpContent对象,用于发送JSON数据
var httpContent = new StringContent(jsonContent, Encoding.UTF8, "application/json");
// 发送POST请求
HttpResponseMessage response = await client.PostAsync(url, httpContent);
// 确保HTTP请求成功
//response.EnsureSuccessStatusCode();
// 读取响应内容
var responseBody = await response.Content.ReadAsStringAsync();
//LoggerHelper.Info("请求:" + url + ",参数:" + jsonContent + ",结果:" + responseBody);
jsonObject = JsonConvert.DeserializeObject<JObject>(responseBody);
}
catch (HttpRequestException e)
{
LoggerHelper.Error("请求失败!",e);
}
return jsonObject;
}
}
tips: 出现了 "clientInfo.uniPlatform" is required. 是没有传参数clientInfo
参考 官方文档
APP端
this.$http
.post(`/uniCloudRegister`, {
clientInfo:JSON.stringify(uni.getSystemInfoSync())
})
.then(res =>{
uni.stopPullDownRefresh()
uni.hideNavigationBarLoading()
console.info(res)
}).catch(err => {
console.error(err)
uni.stopPullDownRefresh()
uni.hideNavigationBarLoading()
})
后台接口 UserController:ApiController
ConstantsConstants//业务系统登录后才需要联登到 uniCloud,所以不需要在注册时执行,而是单独给出了注册功能的接口
[HttpPost]
[Route("uniCloudRegister")]
public async Task<HttpResponseMessage> uniCloudRegister([FromBody] UniClient client)
{
HttpResponseMessage response = null;
try
{
//此为验证当前系统token并转为 user类的工具,这里就不给出详细示例了
var ui = JwtHelper.AnalysisToken(HttpContext.Current.Request);
//见 Utils
string uniAppUserName = Utils.GetExternalUid(ui);
string nonce = Utils.GetNonce();
// 获取当前时间戳(精确到毫秒)
long timestamp = Utils.GetNowTimeStamp();
Dictionary<string, string> paramsDictionary = new Dictionary<string, string>
{
{ "externalUid", uniAppUserName},
{ "nickname", ui.Name},
};
//签名算法见 Utils
string signature = Utils.GetSignature(paramsDictionary, nonce, timestamp);
//client.clientInfo 为JSON.stringify(uni.getSystemInfoSync()) 这里再转回json对象
var cliInfo = JsonConvert.DeserializeObject<JObject>(client.clientInfo);
Dictionary<string, object> param = new Dictionary<string, object>();
param.Add("clientInfo", cliInfo);
param.Add("uniIdToken", "");
param.Add("params", paramsDictionary);
// 将对象序列化为JSON字符串
string jsonContent = JsonConvert.SerializeObject(param);
Dictionary<string, string> headers = new Dictionary<string, string>()
{
{"uni-id-nonce",nonce },
{"uni-id-timestamp",timestamp + ""},
{"uni-id-signature",signature}
};
var res = await HttpHelper.SendPostAsync(Constants.PushRegister, jsonContent, headers);
if (res.Count > 0)
{
response = Request.CreateResponse(System.Net.HttpStatusCode.OK, res);
}
else
{
response = Request.CreateResponse(System.Net.HttpStatusCode.BadRequest, "error");
}
}
catch (Exception ex)
{
response = Request.CreateErrorResponse(System.Net.HttpStatusCode.InternalServerError, "error");
}
return response;
}
Constants
//uniapp 外部系统联登 https://doc.dcloud.net.cn/uniCloud/uni-id/cloud-object.html#external
public static readonly string PushUrl = "you url";
//uniapp 注册
public static readonly string PushRegister = PushUrl + "externalRegister";
//uniapp 登录
public static readonly string PushLogin = PushUrl + "externalLogin";
//uniapp 修改信息
public static readonly string PushUpdateUser = PushUrl + "updateUserInfoByExternal ";
UniClient
public class UniClient
{
public string clientInfo { get; set; }
public string uniIdToken { get; set; }
}
Utils
/// <summary>
/// 根据用户信息 获取uniapp的uid, 这个就根据自己的业务来处理
/// </summary>
/// <param name="ui"></param>
/// <returns></returns>
public static string GetExternalUid(UserInfo ui)
{
//分别为角色ID、 用户ID和用户账号
return ui.Role + "_" + ui.Id + "_" + ui.userName;
}
//获取随机字符串 这里其实可以固定返回一组字符串
public static string GetNonce()
{
return GenerateRandomStringLinq(8);
}
private static string GenerateRandomStringLinq(int length)
{
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
Random random = new Random();
return new string(Enumerable.Repeat(chars, length)
.Select(s => s[random.Next(s.Length)]).ToArray());
}
/// <summary>
/// uniapp 鉴权签名算法
/// </summary>
/// <param name="parameters"></param>
/// <param name="nonce"></param>
/// <param name="timestamp"></param>
/// <returns></returns>
public static string GetSignature(Dictionary<string, string> parameters, string nonce, long timestamp)
{
string paramsStr = GetParamsString(parameters);
using (HMACSHA256 hmacSha256 = new HMACSHA256(Encoding.UTF8.GetBytes(Constants.RequestAuthSecret + nonce)))
{
string message = timestamp.ToString() + paramsStr;
byte[] messageBytes = Encoding.UTF8.GetBytes(message);
byte[] hashBytes = hmacSha256.ComputeHash(messageBytes);
return ByteArrayToHexString(hashBytes).ToUpper();
}
}
/// <summary>
/// 获取当前时间戳 单位毫秒
/// </summary>
/// <returns></returns>
public static long GetNowTimeStamp()
{
return (long)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalMilliseconds;
}
private static string GetParamsString(Dictionary<string, string> parameters)
{
var keys = new List<string>(parameters.Keys);
keys.Sort();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < keys.Count; i++)
{
if (i != 0)
{
sb.Append("&");
}
sb.Append(keys[i]).Append("=").Append(parameters[keys[i]]);
}
return sb.ToString();
}
private static string ByteArrayToHexString(byte[] bytes)
{
StringBuilder sb = new StringBuilder();
foreach (byte b in bytes)
{
string hex = b.ToString("x2");
sb.Append(hex);
}
return sb.ToString();
}
HttpHelper
/// <summary>
/// POST异步请求
///
/// </summary>
/// <param name="url">请求url</param>
/// <param name="jsonContent"></param>
/// <param name="headers"></param>
/// <returns></returns>
// 发送POST请求的函数
public static async Task<JObject> SendPostAsync(string url, string jsonContent, Dictionary<string, string> headers = null)
{
using (var client = new HttpClient())
{
var jsonObject = new JObject();
if (headers != null)
{
foreach (KeyValuePair<string, string> header in headers)
{
client.DefaultRequestHeaders.Add(header.Key, header.Value);
}
}
try
{
// 创建一个HttpContent对象,用于发送JSON数据
var httpContent = new StringContent(jsonContent, Encoding.UTF8, "application/json");
// 发送POST请求
HttpResponseMessage response = await client.PostAsync(url, httpContent);
// 确保HTTP请求成功
//response.EnsureSuccessStatusCode();
// 读取响应内容
var responseBody = await response.Content.ReadAsStringAsync();
//LoggerHelper.Info("请求:" + url + ",参数:" + jsonContent + ",结果:" + responseBody);
jsonObject = JsonConvert.DeserializeObject<JObject>(responseBody);
}
catch (HttpRequestException e)
{
LoggerHelper.Error("请求失败!",e);
}
return jsonObject;
}
}
tips: 出现了 "clientInfo.uniPlatform" is required. 是没有传参数clientInfo
参考 官方文档
收起阅读 »
低版本 vue 无法安装 pinia 的解决方案
1 使用 pnpm 安装,安装时需锁定 pinia 版本
2 使用 yarn 安装,安装时需锁定 pinia 版本
1 使用 pnpm 安装,安装时需锁定 pinia 版本
2 使用 yarn 安装,安装时需锁定 pinia 版本

关于@uni-helper/uni-app-types 安装失败的原因
通过报错,可以得出是 ts 版本冲突;(大家检查一下 ts 版本;命令 tsc -v)所以需要到 npm 上安装低版本的,个人推荐 0.5.13,
地址:https://www.npmjs.com/package/@uni-helper/uni-app-types/v/0.5.13?activeTab=versions
通过报错,可以得出是 ts 版本冲突;(大家检查一下 ts 版本;命令 tsc -v)所以需要到 npm 上安装低版本的,个人推荐 0.5.13,
地址:https://www.npmjs.com/package/@uni-helper/uni-app-types/v/0.5.13?activeTab=versions

【奇异事件】云打包一直编译中
PC开发环境操作系统: MacPC/WinPC
开发环境操作系统版本号: 13.3.1 (22E261)/Win11
基项目创建方式: CLI
CLI版本号: 3.0.0-4000820240401001(应该没关系)
问题出现:
使用调试基座开发没有任何问题,直到发版需要打包;
无论如何使用HbuilderX进行打包就会出现,一直在编译,后台esbuild一直在运行;
代码一直没有提交进入云端打包,改依赖换CLI版本甚至换HbuilderX版本都无法解决;
问题排查
一次次的回退git,才定位到问题

没错,就是watch这个配置项导致一直编译无法完成
这个watch是为什么要加呢,因为我使用了unocss。
他在Windows下运行经常会出现错误导致app需要重新运行,如图
所以我在 官方issues 找到了一个相对简单的解决方法
没想到会出现一个更加诡异的问题
希望能帮到你们
PC开发环境操作系统: MacPC/WinPC
开发环境操作系统版本号: 13.3.1 (22E261)/Win11
基项目创建方式: CLI
CLI版本号: 3.0.0-4000820240401001(应该没关系)
问题出现:
使用调试基座开发没有任何问题,直到发版需要打包;
无论如何使用HbuilderX进行打包就会出现,一直在编译,后台esbuild一直在运行;
代码一直没有提交进入云端打包,改依赖换CLI版本甚至换HbuilderX版本都无法解决;
问题排查
一次次的回退git,才定位到问题
没错,就是watch这个配置项导致一直编译无法完成
这个watch是为什么要加呢,因为我使用了unocss。
他在Windows下运行经常会出现错误导致app需要重新运行,如图
所以我在 官方issues 找到了一个相对简单的解决方法
没想到会出现一个更加诡异的问题
希望能帮到你们
收起阅读 »
鸿蒙 1.3.5 无法登录腾讯云 IM,或其他 Websocket 问题
修改 entry/src/main/ets/uni-app-harmony/uni.api.ets ws.connect 方法入参 protocol 的值为 args.protocols ? Array.isArray(args.protocols) ? args.protocols.join(',') : args.protocols : ''
修改 entry/src/main/ets/uni-app-harmony/uni.api.ets ws.connect 方法入参 protocol 的值为 args.protocols ? Array.isArray(args.protocols) ? args.protocols.join(',') : args.protocols : ''
收起阅读 »
【webview】实现应用页面刷新,webview重新请求接口
1、h5环境,webview可以接收主项目发过来的值
主项目添加以下代码:
<web-view ref="webview" :src="path"></web-view>
onShow() {
this.$nextTick(function() {
if (this.path) {
// #ifdef H5
// 向webview发送消息
this.$refs.webview.iframe.contentWindow.postMessage({ isOnShow: true }, "*");
// #endif
}
})
},
webview项目添加以下代码:
// h5环境的webview环境
window.addEventListener("message", (e) => {
if (e.data.isOnShow) {
// 执行相关逻辑
}
}, false);
2、app环境,webview可以接收主项目发过来的值
主项目添加以下代码:
<web-view :src="path"></web-view>
onShow() {
this.$nextTick(function() {
// #ifdef APP
if (this.$scope.$getAppWebview().children() && this.$scope.$getAppWebview().children().length > 0) {
const webview = this.$scope.$getAppWebview().children()[0];
const msg = { isOnShow: true }
webview.evalJS(`msgFromApp(${JSON.stringify(msg)})`);
}
// #endif
})
}
webview项目添加以下代码:
// app环境的webview环境
window.msgFromApp = (e) => {
if (e.isOnShow) {
// 执行相关逻辑
}
}
3、微信小程序环境(只在手机上生效),webview不可以接收主项目发过来的值,除非更换webview标签上的src属性
主项目添加以下代码:
<web-view :src="path"></web-view>
webview项目添加以下代码:
const enums = {
"hidden": "visibilitychange",
"mozHidden": "mozVisibilitychange",
"msHidden": "msVisibilitychange",
"webkitHidden": "webkitVisibilitychange"
}
let visibilitychange = "";
let hidden = "";
Object.keys(enums).map(key => {
if (typeof document[key] === "boolean" && !visibilitychange) {
hidden = key;
visibilitychange = enums[key];
}
})
visibilitychange = visibilitychange || "visibilitychange";
hidden = hidden || "hidden";
window.frames.document.addEventListener(visibilitychange, () => {
console.log("当前页面是否隐藏:", document[hidden]);
if (document[hidden]) {
console.log('页面隐藏');
} else {
console.log('页面显示')
// 执行相关逻辑
}
})
↓↓↓ 各位大佬点点赞
1、h5环境,webview可以接收主项目发过来的值
主项目添加以下代码:
<web-view ref="webview" :src="path"></web-view>
onShow() {
this.$nextTick(function() {
if (this.path) {
// #ifdef H5
// 向webview发送消息
this.$refs.webview.iframe.contentWindow.postMessage({ isOnShow: true }, "*");
// #endif
}
})
},
webview项目添加以下代码:
// h5环境的webview环境
window.addEventListener("message", (e) => {
if (e.data.isOnShow) {
// 执行相关逻辑
}
}, false);
2、app环境,webview可以接收主项目发过来的值
主项目添加以下代码:
<web-view :src="path"></web-view>
onShow() {
this.$nextTick(function() {
// #ifdef APP
if (this.$scope.$getAppWebview().children() && this.$scope.$getAppWebview().children().length > 0) {
const webview = this.$scope.$getAppWebview().children()[0];
const msg = { isOnShow: true }
webview.evalJS(`msgFromApp(${JSON.stringify(msg)})`);
}
// #endif
})
}
webview项目添加以下代码:
// app环境的webview环境
window.msgFromApp = (e) => {
if (e.isOnShow) {
// 执行相关逻辑
}
}
3、微信小程序环境(只在手机上生效),webview不可以接收主项目发过来的值,除非更换webview标签上的src属性
主项目添加以下代码:
<web-view :src="path"></web-view>
webview项目添加以下代码:
const enums = {
"hidden": "visibilitychange",
"mozHidden": "mozVisibilitychange",
"msHidden": "msVisibilitychange",
"webkitHidden": "webkitVisibilitychange"
}
let visibilitychange = "";
let hidden = "";
Object.keys(enums).map(key => {
if (typeof document[key] === "boolean" && !visibilitychange) {
hidden = key;
visibilitychange = enums[key];
}
})
visibilitychange = visibilitychange || "visibilitychange";
hidden = hidden || "hidden";
window.frames.document.addEventListener(visibilitychange, () => {
console.log("当前页面是否隐藏:", document[hidden]);
if (document[hidden]) {
console.log('页面隐藏');
} else {
console.log('页面显示')
// 执行相关逻辑
}
})
↓↓↓ 各位大佬点点赞
收起阅读 »
有没有合租的商业地图key
本公司就是一个小公司。一天地图打开次数就是10次左右,但是为了方便用户查找,又不得不用地图功能。5万的费用,对于大公司倒是无所谓,我们这种小公司,真付不起。有没有谁家买了商业授权,我分担一下。
本公司就是一个小公司。一天地图打开次数就是10次左右,但是为了方便用户查找,又不得不用地图功能。5万的费用,对于大公司倒是无所谓,我们这种小公司,真付不起。有没有谁家买了商业授权,我分担一下。

微信开发者工具1.06.2402040,使用uview-plus报错,无依赖uview-plus
打开本地设置 ——> 上传时过滤无依赖文件选项;重新勾选,便可解决这个问题
打开本地设置 ——> 上传时过滤无依赖文件选项;重新勾选,便可解决这个问题

开源VUE盲盒小程序源码多端二开搭建(app+h5+pc+小程序)带数码商城和交友盲盒系统
本文将围绕盲盒小程序源码的设计、实现及运维过程,详细介绍从项目构思到最终上线的全流程,旨在为读者提供一个全面的参考和指导。
源码设计:n.ymzan.top
一、项目背景与需求分析
1.1 项目背景
盲盒小程序旨在为用户提供一个线上购买和开启盲盒的平台,用户可以通过小程序轻松购买盲盒,并在开启后获得随机的商品。这种新颖的消费模式不仅满足了用户的猎奇心理,还通过社交分享等功能增强了用户之间的互动和粘性。
1.2 需求分析
在项目启动前,我们进行了详细的需求分析,主要包括以下几个方面:
用户注册与登录:用户可以通过邮箱或手机号进行注册,支持第三方社交账号登录。
盲盒购买:用户可以在小程序中浏览盲盒列表,选择心仪的盲盒进行购买,支持多种支付方式。
盲盒开启:用户购买盲盒后,可以立即开启,系统根据预设的概率算法决定用户获得的商品。
商品展示:展示盲盒内的商品信息,包括图片、描述、价格等。
订单管理:用户可以查看自己的购买记录和订单状态,支持订单跟踪和售后服务。
社交分享:用户可以将自己的开箱体验分享到社交平台,增加用户之间的互动和传播。
二、技术选型与架构设计
2.1 技术选型
基于项目的需求,我们选择了以下技术栈进行开发:
前端:uniapp。uniapp 是一个使用 Vue.js 开发所有前端应用的框架,支持编译到 iOS、Android、H5、以及各种小程序等多个平台,极大地提高了开发效率。
后端:PHP TP6 框架。TP6 是一个现代化的 PHP 后端开发框架,拥有简洁的语法、丰富的功能组件和高效的性能,适合快速开发企业级应用。
数据库:MySQL。MySQL 是一个流行的关系型数据库管理系统,支持大型数据库和事务处理,能够满足本项目的数据存储需求。
2.2 架构设计
项目的整体架构采用前后端分离的模式,前端负责页面展示和用户交互,后端负责业务逻辑处理和数据库操作。具体架构如下:
前端:使用 uniapp 开发,包括页面设计、用户交互、API 接口调用等。
后端:使用 PHP TP6 框架,包括用户注册登录、盲盒购买逻辑、数据库操作等。
数据库:使用 MySQL 存储用户信息、盲盒信息、商品信息、订单信息等数据。
三、开发实现
3.1 前端开发
3.1.1 用户注册与登录
在 pages 文件夹下创建 register 和 login 页面,分别用于用户注册和登录。使用 uniapp 提供的表单组件和 API 接口调用功能,实现用户信息的提交和验证。
vue
// register.vue
async submitForm() {
const formData = {
username: this.username,
password: this.password,
};
try {
const res = await uni.request({
url: '/api/user/register',
method: 'POST',
data: formData,
});
if (res.data.code === 0) {
uni.showToast({
title: '注册成功',
icon: 'success',
});
this.$router.push('/pages/login');
} else {
uni.showToast({
title: '注册失败',
icon: 'none',
});
}
} catch (err) {
console.error(err);
}
}
3.1.2 盲盒购买与开启
在 pages 文件夹下创建 blindbox 页面,用于展示盲盒列表和购买操作。用户点击购买按钮后,调用后端接口完成购买操作,并实时更新页面状态。
vue
// blindbox.vue
async buyBlindBox(id) {
try {
const res = await uni.request({
url: /api/blindbox/buy/${id}
,
method: 'POST',
});
if (res.data.code === 0) {
uni.showToast({
title: '购买成功',
icon: 'success',
});
this.getBlindBoxList(); // 重新加载盲盒列表
} else {
uni.showToast
({
title: '购买失败',
icon: 'none',
});
}
// 开启盲盒
if (res.data.opened) {
this.openBlindBox(res.data.blindboxId);
}
} catch (err) {
console.error(err);
uni.showToast({
title: '网络错误',
icon: 'none',
});
}
}
async openBlindBox(blindboxId) {
try {
const res = await uni.request({
url: /api/blindbox/open/${blindboxId},
method: 'GET',
});
if (res.data.code === 0) {
uni.showModal({
title: '恭喜你!',
content: 你获得了${res.data.product.name}!,
showCancel: false,
success: () => {
// 可以添加分享到朋友圈或社交平台的逻辑
this.shareToFriends(res.data.product);
}
});
} else {
uni.showToast({
title: '开启失败',
icon: 'none',
});
}
} catch (err) {
console.error(err);
uni.showToast({
title: '网络错误',
icon: 'none',
});
}
}
shareToFriends(product) {
uni.share({
provider: 'weixin', // 指定分享到微信
title: 我获得了${product.name}!,
path: '/pages/blindbox/detail?productId=' + product.id, // 分享的页面路径
success: () => {
uni.showToast({
title: '分享成功',
icon: 'success',
});
},
fail: () => {
uni.showToast({
title: '分享失败',
icon: 'none',
});
}
});
}
### 3.2 后端开发
#### 3.2.1 用户注册与登录
在 PHP TP6 框架中,创建 User
模型和 UserController
控制器来处理用户注册和登录的逻辑。
```php
// UserController.php
public function register(Request $request)
{
$data = $request->post();
$user = new User();
$user->username = $data['username'];
$user->password = password_hash($data['password'], PASSWORD_DEFAULT);
if ($user->save()) {
return json(['code' => 0, 'msg' => '注册成功']);
} else {
return json(['code' => 1, 'msg' => '注册失败']);
}
}
public function login(Request $request)
{
$data = $request->post();
$user = User::where('username', $data['username'])->find();
if ($user && password_verify($data['password'], $user->password)) {
// 生成Token等操作
return json(['code' => 0, 'msg' => '登录成功', 'token' => 'your_token_here']);
} else {
return json(['code' => 1, 'msg' => '用户名或密码错误']);
}
}
3.2.2 盲盒购买与开启
在 BlindBox 模型和 BlindBoxController 控制器中处理盲盒的购买和开启逻辑。
php
// BlindBoxController.php
public function buy($id, Request $request)
{
// 验证用户身份、库存等
// ...
$blindbox = BlindBox::find($id);
if (!$blindbox || $blindbox->stock <= 0) {
return json(['code' => 1, 'msg' => '盲盒不存在或库存不足']);
}
// 减库存、创建订单等操作
$blindbox->stock--;
$blindbox->save();
// 假设这里直接开启盲盒
$product = $this->openBlindBox($blindbox);
return json([
'code' => 0,
'msg' => '购买成功',
'opened' => true,
'blindboxId' => $blindbox->id,
'product' => $product,)
本文将围绕盲盒小程序源码的设计、实现及运维过程,详细介绍从项目构思到最终上线的全流程,旨在为读者提供一个全面的参考和指导。
源码设计:n.ymzan.top
一、项目背景与需求分析
1.1 项目背景
盲盒小程序旨在为用户提供一个线上购买和开启盲盒的平台,用户可以通过小程序轻松购买盲盒,并在开启后获得随机的商品。这种新颖的消费模式不仅满足了用户的猎奇心理,还通过社交分享等功能增强了用户之间的互动和粘性。
1.2 需求分析
在项目启动前,我们进行了详细的需求分析,主要包括以下几个方面:
用户注册与登录:用户可以通过邮箱或手机号进行注册,支持第三方社交账号登录。
盲盒购买:用户可以在小程序中浏览盲盒列表,选择心仪的盲盒进行购买,支持多种支付方式。
盲盒开启:用户购买盲盒后,可以立即开启,系统根据预设的概率算法决定用户获得的商品。
商品展示:展示盲盒内的商品信息,包括图片、描述、价格等。
订单管理:用户可以查看自己的购买记录和订单状态,支持订单跟踪和售后服务。
社交分享:用户可以将自己的开箱体验分享到社交平台,增加用户之间的互动和传播。
二、技术选型与架构设计
2.1 技术选型
基于项目的需求,我们选择了以下技术栈进行开发:
前端:uniapp。uniapp 是一个使用 Vue.js 开发所有前端应用的框架,支持编译到 iOS、Android、H5、以及各种小程序等多个平台,极大地提高了开发效率。
后端:PHP TP6 框架。TP6 是一个现代化的 PHP 后端开发框架,拥有简洁的语法、丰富的功能组件和高效的性能,适合快速开发企业级应用。
数据库:MySQL。MySQL 是一个流行的关系型数据库管理系统,支持大型数据库和事务处理,能够满足本项目的数据存储需求。
2.2 架构设计
项目的整体架构采用前后端分离的模式,前端负责页面展示和用户交互,后端负责业务逻辑处理和数据库操作。具体架构如下:
前端:使用 uniapp 开发,包括页面设计、用户交互、API 接口调用等。
后端:使用 PHP TP6 框架,包括用户注册登录、盲盒购买逻辑、数据库操作等。
数据库:使用 MySQL 存储用户信息、盲盒信息、商品信息、订单信息等数据。
三、开发实现
3.1 前端开发
3.1.1 用户注册与登录
在 pages 文件夹下创建 register 和 login 页面,分别用于用户注册和登录。使用 uniapp 提供的表单组件和 API 接口调用功能,实现用户信息的提交和验证。
vue
// register.vue
async submitForm() {
const formData = {
username: this.username,
password: this.password,
};
try {
const res = await uni.request({
url: '/api/user/register',
method: 'POST',
data: formData,
});
if (res.data.code === 0) {
uni.showToast({
title: '注册成功',
icon: 'success',
});
this.$router.push('/pages/login');
} else {
uni.showToast({
title: '注册失败',
icon: 'none',
});
}
} catch (err) {
console.error(err);
}
}
3.1.2 盲盒购买与开启
在 pages 文件夹下创建 blindbox 页面,用于展示盲盒列表和购买操作。用户点击购买按钮后,调用后端接口完成购买操作,并实时更新页面状态。
vue
// blindbox.vue
async buyBlindBox(id) {
try {
const res = await uni.request({
url: /api/blindbox/buy/${id}
,
method: 'POST',
});
if (res.data.code === 0) {
uni.showToast({
title: '购买成功',
icon: 'success',
});
this.getBlindBoxList(); // 重新加载盲盒列表
} else {
uni.showToast
({
title: '购买失败',
icon: 'none',
});
}
// 开启盲盒
if (res.data.opened) {
this.openBlindBox(res.data.blindboxId);
}
} catch (err) {
console.error(err);
uni.showToast({
title: '网络错误',
icon: 'none',
});
}
}
async openBlindBox(blindboxId) {
try {
const res = await uni.request({
url: /api/blindbox/open/${blindboxId},
method: 'GET',
});
if (res.data.code === 0) {
uni.showModal({
title: '恭喜你!',
content: 你获得了${res.data.product.name}!,
showCancel: false,
success: () => {
// 可以添加分享到朋友圈或社交平台的逻辑
this.shareToFriends(res.data.product);
}
});
} else {
uni.showToast({
title: '开启失败',
icon: 'none',
});
}
} catch (err) {
console.error(err);
uni.showToast({
title: '网络错误',
icon: 'none',
});
}
}
shareToFriends(product) {
uni.share({
provider: 'weixin', // 指定分享到微信
title: 我获得了${product.name}!,
path: '/pages/blindbox/detail?productId=' + product.id, // 分享的页面路径
success: () => {
uni.showToast({
title: '分享成功',
icon: 'success',
});
},
fail: () => {
uni.showToast({
title: '分享失败',
icon: 'none',
});
}
});
}
### 3.2 后端开发
#### 3.2.1 用户注册与登录
在 PHP TP6 框架中,创建 User
模型和 UserController
控制器来处理用户注册和登录的逻辑。
```php
// UserController.php
public function register(Request $request)
{
$data = $request->post();
$user = new User();
$user->username = $data['username'];
$user->password = password_hash($data['password'], PASSWORD_DEFAULT);
if ($user->save()) {
return json(['code' => 0, 'msg' => '注册成功']);
} else {
return json(['code' => 1, 'msg' => '注册失败']);
}
}
public function login(Request $request)
{
$data = $request->post();
$user = User::where('username', $data['username'])->find();
if ($user && password_verify($data['password'], $user->password)) {
// 生成Token等操作
return json(['code' => 0, 'msg' => '登录成功', 'token' => 'your_token_here']);
} else {
return json(['code' => 1, 'msg' => '用户名或密码错误']);
}
}
3.2.2 盲盒购买与开启
在 BlindBox 模型和 BlindBoxController 控制器中处理盲盒的购买和开启逻辑。
php
// BlindBoxController.php
public function buy($id, Request $request)
{
// 验证用户身份、库存等
// ...
$blindbox = BlindBox::find($id);
if (!$blindbox || $blindbox->stock <= 0) {
return json(['code' => 1, 'msg' => '盲盒不存在或库存不足']);
}
// 减库存、创建订单等操作
$blindbox->stock--;
$blindbox->save();
// 假设这里直接开启盲盒
$product = $this->openBlindBox($blindbox);
return json([
'code' => 0,
'msg' => '购买成功',
'opened' => true,
'blindboxId' => $blindbox->id,
'product' => $product,)

开发ios应用,6.7寸和5.5寸的截屏方法
无论是使用hbuilderx还是使用原生的ios语言开发,上架都需要截屏,当前最新的上架是需要6.7寸、6.5寸、5.5寸,假如支持ipad,还需要ipad最新4代的13英寸、2代13英寸。
但是最新的版本的xcode模拟器,发现安装完后,模拟器默认只是支持iphone15这些最新的设备,默认最新的sdk已经不支持以前的5.5寸那些旧设备。
假如要5.5寸截屏,需要在xcode下载很旧的sdk,好几十G,本身xcode就很大,旧版本的sdk更大,还经常下载失败。但是在苹果的上架流程中,5.5寸可是必须要提供的,苹果还真不考虑我们开发者的感受啊,最新版的xcode默认还不能截屏5.5寸,还需要自己去下载旧版的sdk,可是旧版的sdk下载起来比较麻烦,而是很占体积,把旧的sdk都下载下来,要占几十个G的硬盘呢。
没有模拟器可以截屏,我们假如去买个旧版的5.5寸的真机设备去截屏也不现实,那可是淘汰产品,谁买谁就是冤大头。
其实,假如只是想上架截屏,其实并不一定需要用模拟器截屏
可以使用在线合成截屏的平台,这里我推荐香蕉云编,可以实现最新尺寸的截屏:
https://www.yunedit.com/jietu
无论是使用hbuilderx还是使用原生的ios语言开发,上架都需要截屏,当前最新的上架是需要6.7寸、6.5寸、5.5寸,假如支持ipad,还需要ipad最新4代的13英寸、2代13英寸。
但是最新的版本的xcode模拟器,发现安装完后,模拟器默认只是支持iphone15这些最新的设备,默认最新的sdk已经不支持以前的5.5寸那些旧设备。
假如要5.5寸截屏,需要在xcode下载很旧的sdk,好几十G,本身xcode就很大,旧版本的sdk更大,还经常下载失败。但是在苹果的上架流程中,5.5寸可是必须要提供的,苹果还真不考虑我们开发者的感受啊,最新版的xcode默认还不能截屏5.5寸,还需要自己去下载旧版的sdk,可是旧版的sdk下载起来比较麻烦,而是很占体积,把旧的sdk都下载下来,要占几十个G的硬盘呢。
没有模拟器可以截屏,我们假如去买个旧版的5.5寸的真机设备去截屏也不现实,那可是淘汰产品,谁买谁就是冤大头。
其实,假如只是想上架截屏,其实并不一定需要用模拟器截屏
可以使用在线合成截屏的平台,这里我推荐香蕉云编,可以实现最新尺寸的截屏:
https://www.yunedit.com/jietu
收起阅读 »