本文讲述使用网页开发适用于宴会、活动、年会现场 LED 大屏幕显示抽奖、抢楼、游戏、视频等互动界面,通过在浏览器中打开后 F11 全屏投影到大银幕上。
设计思路:
为了保证最佳的显示效果,设计稿的尺寸按大屏幕的“宽高比”来做(本文以4米*3米的大银幕为例,设计稿以800*600像素为例)。
由于电脑的视频输出分辨率很有可能与大银幕的宽高比不同,那么我们全屏投影后会界面会有变形,但是投影设备接收到电脑视频信号后再全屏显示到大银幕时,界面又会变回正常。
按设计稿的尺寸开发网页,并使用 CSS3 的 scale 实现整体伸缩,目的是自适应窗口尺寸来全屏显示内容,无留白、无裁切。整体拉伸的要求是文字、图片、背景、间距、iframe 等所有元素以统一的比例拉伸,无错位。
网页上通过 JS 设置一些快捷键来实现页面展示内容的切换。
若投影设备功能有限或操作员水平有限等原因导致无法顺利设置输入输出分辨率时,网页可设置 x 轴和 y 轴两个方向的伸缩系数、定位偏移量等,更灵活地实现完美投影需求。
开发要点:
网页尺寸按设计稿来定(以 800x600 为例)。
使用 CSS3 的 transform 的 scale 来实现缩放,以电脑显示器分辨率 1920x1080,宽放大 1920/800 倍,高放大 1080/600 倍,即可将设计稿全屏展示到电脑显示器上。为了更灵活,我们使用窗口大小来代替显示器分辨率,这样不管怎么改变窗口大小或者全屏,设计稿都能完整地显示在窗口上。
在页面初始化和窗口尺寸变化时重新调整设置上述比例。
代码参考:
无法向会话状态服务器发出会话状态请求。请确保 ASP.NET State Service (ASP.NET 状态服务)已启动,并且客户端端口与服务器端口相同。如果服务器位于远程计算机上,请检查 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\aspnet_state\Parameters\AllowRemoteConnection 的值,确保服务器接受远程请求。如果服务器位于本地计算机上,并且上面提到的注册表值不存在或者设置为 0,则状态服务器连接字符串必须使用“localhost”或“127.0.0.1”作为服务器名称。
无法连接 StateService 的原因有很多种,我遇到的情况是:
VS 中使用 IISExpress 调试项目,系统没有安装 IIS,
ASP.NET 状态服务是开启状态,
没有对系统作任何修改,无缘无故无法连接。
修改注册表、配置防火墙无效(我没有用到远程连接),
执行 aspnet_regiis -i -enables 无效,
重装 .NET Framework 4.8 Developer Pack 无效,
差点就重装系统了。
最终的解决方法是:
在“启用或关闭 Windows 功能”中勾选安装:Internet Information Services -> 万维网服务 -> 应用程序开发功能 -> ASP.NET 4.8
重启系统。

Visual Studio 2019 已于本月早些时候正式发布,但 MySQL for Visual Studio 却迟迟未更新,目前的 1.2.8 版本只支持到 VS2017。表现在 VS2019 上会发生:
在“服务器资源管理器”中添加连接时,或在新建“ADO.NET 实体数据模型”时,无法看到数据源“MySQL Database (MySQL Data Provider)”
在实体数据模型设计器(Entity Data Model Designer)中执行“从数据库更新数据模型”时出现错误:尝试从数据库进行更新时,遇到类型为“System.ArgumentException”的异常。异常消息为:“无法将运行时连接字符串转换为设计时等效项。没有为提供程序“MySql.Data.MySqlClient”安装为设计目的(DDEX 提供程序)启用 Visual Studio 以便与数据库进行通信所需的库。
幸运的是 VS2019 可以与低版本 VS 共存,使用 ASP.NET 连接 MySQL 的开发者们要再等一等才能用上 VS2019 了。
这几天 MySQL 不断有产品更新,相信 MySQL for Visual Studio 2019 也会在不久的将来面世,本站将第一时间更新下载地址。
2019.8.30 Jose Ramirez(来自 MySQL 的 Windows 团队):MySQL for Visual Studio 的当前版本不支持 Visual Studio 2019。正在努力提供支持,但根据公司政策,我无法披露日期或对何时可用提供任何承诺。查看
2019.9.26 MySQL for Visual Studio 迟迟未更新,但是 VS2019 更新到最新版本(v16.3)已经一切正常了。
MySQL for Visual Studio 的 Repair 功能是鸡肋,有问题的话,Remove 后重新安装才能解决。
微信小程序 login 后,在用 code 换 session 时,如果小程序绑定了微信开放平台,应该会返回 unionid。
文档:https://developers.weixin.qq.com/miniprogram/dev/api-backend/code2Session.html。
但实际上部分用户仍然是没有 unionid 的,把上述接口返回的数据记录下来如:

仔细阅读官方 UnionID 机制说明,通过用户授权 wx.getUserInfo 才会保证有 unionid 返回,否则只会在“用户关注了绑定同一微信开放平台的同主体的公众号”等情况下才会返回 unionid。
确保获取 unionid 流程参此文。
常用获取英文的方法是查看英文版 App。
微信:WeChat
小程序:Mini Programs
公众号:Official Accounts
订阅号:Subscriptions
朋友圈:Moments
已登录的:Logged
服务通知:Service Messages
置顶:Sticky on Top / Remove from Top
发送给朋友:Share to Friends
微信号:WeChat ID
钱包:Wallet
余额:Balance
资金/零钱/基金:Funds
金额:Amount
明细:Details
账单:Transactions
资产:Assets
收入/收益:Income
财富:Fortune
登录:Log In / Login
退出登录:Log Out / Logout
当使用在线编辑器编辑一篇文章(或从 Word 复制)后,会得到包含 HTML 标签的字符串内容,可以直接将它输出到页面上而不需要进行 HTML 编码。
但是,当我们需要改变图片大小时,我们发现有些图片的尺寸是直接使用 style 属性固定的,除了用 JS 进行后期处理,我们可以在服务端对 <img /> 进行修正。
这个场景会在小程序开发的时候遇到。
我们可以在客户端用 JS 进行处理,也可以在服务端用类似的方法处理(使用正则表达式)。参此文
这里使用 HtmlAgilityPack 通过递归节点来处理:
/// <summary>
/// 给所有指定节点添加样式
/// </summary>
/// <param name="html"></param>
/// <param name="tag">节点名称(小写),如:img</param>
/// <param name="styles">要添加的样式,如:max-width:100%;</param>
/// <returns></returns>
public static string AddStyleToHtmlNode(string html, string tag, string styles)
{
HtmlDocument doc = new HtmlDocument();
doc.LoadHtml(html);
for (var i = 0; i < doc.DocumentNode.ChildNodes.Count; i++)
{
doc.DocumentNode.ChildNodes[i] = AddStyleToHtmlNode(doc.DocumentNode.ChildNodes[i], tag, styles);
}
return doc.DocumentNode.OuterHtml;
}
private static HtmlNode AddStyleToHtmlNode(HtmlNode node, string tag, string styles)
{
if (node.Name == tag)
{
var style = node.GetAttributeValue("style", null);
node.SetAttributeValue("style", ((string.IsNullOrWhiteSpace(style) ? "" : style.Trim() + ";") + styles).Replace(";;", ";"));
}
for (var i = 0; i < node.ChildNodes.Count; i++)
{
node.ChildNodes[i] = AddStyleToHtmlNode(node.ChildNodes[i], tag, styles);
}
return node;
}直接调用:
Content = zStringHTML_190126.AddStyleToHtmlNode(Content, "img", "max-width:100%;height:auto;");本文作为 多平台用户登录模块设计 的扩展设计,即以手机号作为用户的唯一凭证。
官方文档的小程序登录时序:
https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html

(图片摘自 2018.10.30)
session_key 是密钥,仅保存于开发者服务器,用于将小程序通过前端接口获取到的数据解密来验证其真实性。详见 https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/signature.html
小程序与服务器是通过自定义登录态来识别用户身份的,以下简称口令(token)。
由于微信未告知 session_key 的有效期,那么为保证小程序发起业务请求成功,token 须要永久有效,但这会带来安全隐患。
如果设置 token 的有效期(如 7200 秒),那么小程序发起业务请求后,服务器必须把 token 的验证结果告知小程序,若失效则重新登录。
微信授权登录(含绑定手机号码)流程图

子流程:授权登录

子流程:绑定手机号

为保证数据安全,针对每个须要授权登录的业务请求,服务器都都会检验 token 的有效性。如果小程序同时发起多个业务请求,并几乎同时收到 token 过期,那么会同时发起多个重新登录流程,服务器多次 code2Session,重新生成多个 token 返回到小程序,那么小程序最终保存的 token 可能不是服务器上认为的最新的一个 token。这样,如果程序设计为获取到 token 继而重新发起业务请求,可能会进入死循环。解决的方法是在同时发起多个业务请求之前先向服务器验证一次 token 的有效性,再发起多个业务请求时就不会出现都过期的情况了。

你可能打开了假的工具[捂嘴]
查看你打开工具的图标快捷方式是指向到哪的?

打开属性,点击“打开文件所在的位置”,是不是那个英文名的文件是高亮的?
你可以直接双击打开这个英文工具“wechatdevtools.exe”,确实打开的是上面的 NW.JS 界面,
然后打开中文名的工具“微信web开发者工具.exe”,正常运行。
那么就在这个文件上右键,选择你喜欢的打开方式吧:“固定到开始屏幕”或“固定到任务栏”或“发送到桌面快捷方式”。
附微信web开发者工具下载地址:https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html
一: 执行sql语句,返回受影响的行数
在mysql里面,如果没有影响,那么返回行数为 -1 ,sqlserver 里面 还没有测试过
(var ctx = MyDbContext())
{
ctx.Database.ExecuteSqlCommand("");
}二 : Database.SqlQuery<T> EF5执行sql查询语句 Database.SqlQuery 带返回值
这个准确的说是 IEnumerable<T> SqlQuery<T>(string sql, params object[] parameters) ,注意返回值是 IEnumerable
这个是执行sql语句,返回你想要的类型的列表
dbMain.Database.SqlQuery<int>("select max(UserId) from tb_user_account").First();
或者假如你自己有个类别
PersonView
{ PersonID { ; ; } Name { ; ; }
}
那么就可以直接返回这个 PersonView类
(var ctx = MyDbContext())
{
var peopleViews = ctx.SqlQuery<PersonView>("").ToList();
}直接返回你想要的数据. 例如这里是 List<PersonView> 列表
函数功能:添加/修改/删除/获取 URL 中的参数(锚点敏感)
/// <summary>
/// 设置 URL 参数(设 value 为 undefined 或 null 删除该参数)
/// <param name="url">URL</param>
/// <param name="key">参数名</param>
/// <param name="value">参数值</param>
/// </summary>
function fn_set_url_param(url, key, value) {
// 第一步:分离锚点
var anchor = "";
var anchor_index = url.indexOf("#");
if (anchor_index >= 0) {
anchor = url.substr(anchor_index);
url = url.substr(0, anchor_index);
}
// 第二步:删除参数
key = encodeURIComponent(key);
var patt;
// &key=value
patt = new RegExp("\\&" + key + "=[^&]*", "gi");
url = url.replace(patt, "");
// ?key=value&okey=value -> ?okey=value
patt = new RegExp("\\?" + key + "=[^&]*\\&", "gi");
url = url.replace(patt, "?");
// ?key=value
patt = new RegExp("\\?" + key + "=[^&]*$", "gi");
url = url.replace(patt, "");
// 第三步:追加参数
if (value != undefined && value != null) {
value = encodeURIComponent(value);
url += (url.indexOf("?") >= 0 ? "&" : "?") + key + "=" + value;
}
// 第四步:连接锚点
url += anchor;
return url;
}
/// <summary>
/// 获取 URL 参数(无此参数返回 null,多次匹配使用“,”连接)
/// <param name="url">URL</param>
/// <param name="key">参数名</param>
/// </summary>
function fn_get_url_param(url, key) {
key = encodeURIComponent(key);
var patt = new RegExp("[?&]" + key + "=([^&#]*)", "gi");
var values = [];
while ((result = patt.exec(url)) != null) {
values.push(decodeURIComponent(result[1]));
}
if (values.length > 0) {
return values.join(",");
} else {
return null;
}
}调用示例:
var url = window.location.href;
// 设置参数
url = fn_set_url_param(url, 'a', 123);
// 获取参数
console.log(fn_get_url_param, url, 'a');压缩版:
function fn_set_url_param(b,c,e){var a="";var f=b.indexOf("#");if(f>=0){a=b.substr(f);b=b.substr(0,f)}c=encodeURIComponent(c);var d;d=new RegExp("\\&"+c+"=[^&]*","gi");b=b.replace(d,"");d=new RegExp("\\?"+c+"=[^&]*\\&","gi");b=b.replace(d,"?");d=new RegExp("\\?"+c+"=[^&]*$","gi");b=b.replace(d,"");if(e!=undefined&&e!=null){e=encodeURIComponent(e);b+=(b.indexOf("?")>=0?"&":"?")+c+"="+e}b+=a;return b}
function fn_get_url_param(b,c){c=encodeURIComponent(c);var d=new RegExp("[?&]"+c+"=([^&#]*)","gi");var a=[];while((result=d.exec(b))!=null){a.push(decodeURIComponent(result[1]))}if(a.length>0){return a.join(",")}else{return null}};