2023年2月

在前面随笔《
利用微信公众号实现商品的展示和支付(1)
》介绍了商品的列表和明细信息的处理,本篇随笔接着上一篇,继续介绍关于商品的微信支付和购物车处理方面,其中微信支付里面,也涉及到了获取微信共享地址的处理,从而个更加方便录入邮寄地址信息;购物车可以从本地的localStorage对象进行获取和处理,也可通过接口获取数据库记录的购物车信息,本案例介绍通过localStorage进行购物车处理方式。

1、商品的微信支付处理

商品的微信支付处理,其中涉及了两个场景,一个是直接购买,一个是通过购物车的结算方式,两个处理界面有所差异,直接付款购买的界面是对当前选中商品进行单项的结算处理,默认数量为1;而购物车订单结算则是利用购物车的记录进行微信支付的处理,购物车可以从本地的localStorage对象进行获取和处理,也可通过接口获取数据库记录的购物车信息,本案例介绍通过localStorage进行购物车处理方式。

对选中商品直接结算界面如下所示。

购物车结算界面如下所示。

先来看看微信共享地址的处理界面,代码如下所示。

<!--配送地址处理-->
<divclass="weui-panel">
    <divclass="weui-panel__bd">
        <divclass="weui-media-box weui-media-box_small-appmsg">
            <divclass="weui-cells">
                <divclass="weui-cell weui-cell_access">
                    <divclass="weui-cell__bd weui-cell_primary">
                        <pclass="font-14"><spanclass="mg-r-10">配送地址</span><spanclass="fr"><aonclick="GetAddress()"class="weui-btn_mini weui-btn_primary">获取微信地址</a></span></p>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
<divclass="wy-media-box weui-media-box_text address-select">
    <divclass="weui-media-box_appmsg">
        <divclass="weui-media-box__hd proinfo-txt-l"style="width:20px;"><spanclass="promotion-label-tit"><imgsrc="~/Content/wechat/web/images/icon_nav_city.png" /></span></div>
        <divclass="weui-media-box__bd">
            <ahref="/h5/address"class="weui-cell_access">
                <h4class="address-name"></h4>
                <divclass="address-txt"></div>
            </a>
        </div>
        <divclass="weui-media-box__hd proinfo-txt-l"style="width:16px;"><divclass="weui-cell_access"><spanclass="weui-cell__ft"></span></div></div>
    </div>
</div>

获取微信共享地址,需要配合微信的JSSDK的JS代码进行处理,如下是处理的代码。

    <scriptlanguage="javascript">
        varappid= '@ViewBag.appid';varnoncestr= '@ViewBag.noncestr';varsignature= '@ViewBag.signature';vartimestamp= '@ViewBag.timestamp';

wx.config({
debug:
false,
appId: appid,
//必填,公众号的唯一标识 timestamp: timestamp,//必填,生成签名的时间戳 nonceStr: noncestr,//必填,生成签名的随机串 signature: signature,//必填,签名,见附录1 jsApiList: ['checkJsApi','chooseImage','previewImage','uploadImage','downloadImage','getLocalImgData','openAddress']
});
//所有准备好后 wx.ready(function() {

});
//获取微信共享地址 functionGetAddress() {
wx.openAddress({
success:
function(res) {varuserName=res.userName;//收货人姓名 varpostalCode=res.postalCode;//邮编 varprovinceName=res.provinceName;//国标收货地址第一级地址(省) varcityName=res.cityName;//国标收货地址第二级地址(市) varcountryName=res.countryName;//国标收货地址第三级地址(国家) vardetailInfo=res.detailInfo;//详细收货地址信息 varnationalCode=res.nationalCode;//收货地址国家码 vartelNumber=res.telNumber;//收货人手机号码 //$.alert(`${detailInfo}`); $(".address-name").html(`<span>${userName}</span><span>${telNumber}</span>`);
$(
".address-txt").text(`${detailInfo}`);
}
});
}
</script>

而直接购买的产品信息,就是通过ID获取对应商品信息,绑定在控件上即可,如下界面代码所示。

<!--购买商品信息-->
<divclass="wy-media-box weui-media-box_text">
    <divclass="weui-media-box__bd">
        <divclass="weui-media-box_appmsg ord-pro-list">
            <divclass="weui-media-box__hd"><ahref="pro_info.html"><imgid="productimg"class="weui-media-box__thumb"src=""alt=""></a></div>
            <divclass="weui-media-box__bd">
                <h1class="weui-media-box__desc"><ahref="/h5/ProductDetail?id=@Request["ID"]" class="ord-pro-link"><spanid="productname"></span></a></h1>
                <pclass="weui-media-box__desc">规格:<span></span></p>
                <divclass="clear mg-t-10">
                    <divclass="wy-pro-pri fl">¥<emclass="num font-15"id="price">0</em></div>
                    <divclass="pro-amount fr"><divid="spinner-amount"class="Spinner"></div></div>
                </div>
            </div>
        </div>
    </div>
</div>

JS处理代码如下所示

//绑定产品明细
functionGetProductDetail() {var url = "/H5/GetProductDetail";var data ={
id:
"@Request["ID"]"}//获取数据并绑定界面 $.getJSON(url, data, function(result) {
$(
"#productname").text(result.ProductName);
$(
"#price").text(result.Price);
$(
"#productimg").attr("src", result.Picture);//$("#description").text(result.Description); $("#totalmoney").text(result.Price);
});
}

而微信支付的处理,我们可以通过参考官方的接口定义来处理我们的代码。

https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6

我们先来金额汇总的界面展示,通过按钮我们发起微信支付的处理操作。

<!--金额及发起支付-->
<divclass="wy-media-box weui-media-box_text">
    <divclass="mg10-0 t-c">总金额:<spanclass="wy-pro-pri mg-tb-5">¥<emclass="num font-20"id="totalmoney">0</em></span></div>
    <divclass="mg10-0"><aonclick="PayMoney()"class="weui-btn weui-btn_primary">微信付款</a></div>
    <divclass="mg10-0"><ahref="/h5/ProductList"class="weui-btn weui-btn_plain-primary">返回产品列表</a></div>
</div>

参考下官方案例,我们处理的JS代码如下所示。

        //微信支付的调用处理
        functiononBridgeReady() {//统一下单的数据
            var total_fee = parseFloat($("#totalmoney").text()) * 100;//转换为分单位
            var data ={
total_fee: total_fee,
//订单金额,单位为分 body: $("#productname").text(), //商品或支付单简要描述 detail: $("#productname").text(), //商品名称明细列表 openid: "@ViewBag.openid", //当前用户标识 product_id: "@Request["ID"]", //商品ID, trade_type=NATIVE,此参数必传 trade_type: "JSAPI", //交易类型, JSAPI--公众号支付、NATIVE--原生扫码支付、APP--app支付 attach: "附加数据", //附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据 };//获取预下单参数并调用支付处理 var url = "/h5/GetPreOrder";
$.getJSON(url, data,
function(info) {
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {"appId": info.appId, //公众号名称,由商户传入 "timeStamp": info.timeStamp, //时间戳,自1970年以来的秒数 "nonceStr": info.nonceStr, //随机串 "package": info.package,"signType": info.signType, //微信签名方式: "paySign": info.paySign //微信签名 },function(res) {switch(res.err_msg) {case 'get_brand_wcpay_request:cancel':
$.alert(
"取消支付");break;case 'get_brand_wcpay_request:fail':
$.alert(
"支付失败,可能的原因:签名错误、未注册APPID、项目设置APPID不正确、注册的APPID与设置的不匹配、其他异常等。");break;case 'get_brand_wcpay_request:ok':
$.alert(
"支付成功");break;
}
});
});
}
//微信付款 functionPayMoney() {if (typeof WeixinJSBridge == "undefined") {if(document.addEventListener) {
document.addEventListener(
'WeixinJSBridgeReady', onBridgeReady, false);
}
else if(document.attachEvent) {
document.attachEvent(
'WeixinJSBridgeReady', onBridgeReady);
document.attachEvent(
'onWeixinJSBridgeReady', onBridgeReady);
}
}
else{
onBridgeReady();
}
}

这里的支付所需的参数,我们通过后端的/h5/GetPreOrder 获取生成,然后通过JS进行异步发起支付操作。

这个GetPreOrder主要的操作就是根据我们传入的商品信息,然后获取对应的参数返回给前端。

/// <summary>
///预生成订单支付需要的前端参数/// </summary>
/// <param name="order">订单数据</param>
/// <returns></returns>
publicActionResult GetPreOrder(PayOrderData order)
{
dynamic obj = newExpandoObject();if (order != null && order.total_fee > 0)
{
//调用支付API,获取统一下单数据 var payApi = new TenPayApi(this.accountInfo);var preResult =payApi.UnifiedOrder(order);var prepay_id = preResult.prepay_id;//预支付回话标识//生成支付所需数据和签名 WxPayData data = newWxPayData();
data.SetValue(
"appId", accountInfo.AppID);//公众帐号id data.SetValue("timeStamp", data.GenerateTimeStamp());//随机字符串 data.SetValue("nonceStr", data.GenerateNonceStr());//随机字符串 data.SetValue("package", string.Format("prepay_id={0}", prepay_id));//统一下单接口返回的prepay_id参数值,提交格式如:prepay_id=*** data.SetValue("signType", "MD5");//签名类型,默认为MD5,支持HMAC-SHA256和MD5。注意此处需与统一下单的签名类型一致 data.SetValue("paySign", data.MakeSign(accountInfo.PayAPIKey));//签名//构建一个前端使用的对象,用于配置支付处理的JS参数 obj.appId =accountInfo.AppID;
obj.nonceStr
= data.GetValue("nonceStr");
obj.timeStamp
= data.GetValue("timeStamp");
obj.package
= data.GetValue("package");
obj.signType
= data.GetValue("signType");
obj.paySign
= data.GetValue("paySign");
}
returnToJsonContent(obj);
}

这样我们就可以发起微信支付处理操作了。

当然,我们JS方面,除了刚才利用getBrandWCPayRequest 来发起支付处理,我们还可以利用微信JSSDK的chooseWXPay函数支付发起处理的,如下代码所示。

//发起一个微信支付
functionchooseWXPay() {//统一下单的数据
    var total_fee = parseFloat($("#totalmoney").text()) * 100;//转换为分单位
    var data ={
total_fee: total_fee,
//订单金额,单位为分 body: $("#productname").text(), //商品或支付单简要描述 detail: $("#productname").text(), //商品名称明细列表 openid: "@ViewBag.openid", //当前用户标识 product_id: "@Request["ID"]", //商品ID, trade_type=NATIVE,此参数必传 trade_type: "JSAPI", //交易类型, JSAPI--公众号支付、NATIVE--原生扫码支付、APP--app支付 attach: "附加数据", //附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据 };//获取预下单参数并调用支付处理 var url = "/h5/GetPreOrder";
$.getJSON(url, data,
function(info) {
wx.chooseWXPay({
appId: info.appId,
timestamp: info.timeStamp,
//支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符 nonceStr: info.nonceStr, //支付签名随机串,不长于 32 位 package: info.package, //统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=***) signType: info.signType, //签名方式,默认为'SHA1',使用新版支付需传入'MD5' paySign: info.paySign, //支付签名 success: function (res) { //支付成功后的回调函数 if (res.errMsg == 'chooseWXPay:ok') {
$.toast(
'支付成功');//setTimeout(function () { //window.location.href = "/";//这里默认跳转到主页 //}, 2000); //window.location.href = "/Pay/order_details?orderId=" + $("#orderId").val(); } else if (res.errMsg == 'chooseWXPay:cancel' || res.errMsg == 'chooseWXPay:fail') {
$.toast(
"支付失败");//window.location.href = "/Pay/order_details?orderId=" + $("#orderId").val(); }
},
cancel:
function() {
$.toast(
"用户取消了支付2");//window.location.href = "/Pay/order_details?orderId=" + $("#orderId").val(); }
});

wx.error(
function(res) {
$.toast(
"调用支付出现异常");
});
});
}

同样可以实现一样的微信支付效果。

2、商品的购物车处理

前面介绍过,购物车可以从本地的localStorage对象进行获取和处理,也可通过接口获取数据库记录的购物车信息,本案例介绍通过localStorage进行购物车处理方式。

为了封装对localStorage购物车的处理,我们引入一个对这个购物车的操作处理,主要就是添加、更新、获取列表、清空列表等相关操作。

对于以上JS的代码使用demo代码如下所示。

<script>$(function() {varproduct={'id':1,      属性名用引号括起来,属性间由逗号隔开'name':'商品1','num':1,'price':1};

cart.addProduct(product); 商品加入到购物车
varproductlist=cart.getProductList(); 取出购物车商品
console.log(productlist);
varisExist=cart.existProduct();
console.log(isExist);
cart.deleteProduct(
1);varisExist1=cart.existProduct();
console.log(isExist1);
});
</script>

购物车列表界面如下所示,主要就是展开存储里面的商品信息,并汇总展示,如下所示。

这个页面视图的代码如下所示。

<!--主体-->
<headerclass="wy-header">
    <divclass="wy-header-icon-back"><span></span></div>
    <divclass="wy-header-title">购物车</div>
</header>
<divclass="weui-content"id="divCart">
</div>

<!--底部导航-->
<divclass="foot-black"></div>
<divclass="weui-tabbar wy-foot-menu">
    <divclass="npd cart-foot-check-item weui-cells_checkbox allselect">
        <divclass="weui-cell allsec-well weui-check__label"for="all">
            <divclass="weui-cell__hd">
                <inputtype="checkbox"class="weui-check"name="all-sec"id="all">
                <iclass="weui-icon-checked"></i>
            </div>
            <divclass="weui-cell__bd">
                <pclass="font-14">全选</p>
            </div>
        </div>
    </div>
    <divclass="weui-tabbar__item  npd">
        <pclass="cart-total-txt">合计:<i></i><emclass="num font-16"id="zong1">0.00</em></p>
    </div>
    <ahref="/h5/shopcartorder"class="red-color npd w-90 t-c">
        <pclass="promotion-foot-menu-label">去结算</p>
    </a>
</div>

这里主要就是根据数据,动态构建divCart里面的列表数据,相关的JS代码如下所示。

//显示购物车列表
functionshowCartList() {var productlist = cart.getProductList();    //取出购物车商品
    $("#divCart").html("");//构建HTML控件
    $.each(productlist, function(i, item) {var html =`<div class="weui-panel weui-panel_access">
            <div class="weui-panel__hd"><span>广州爱奇迪</span><a href="javascript:;" class="wy-dele"></a></div>
            <div class="weui-panel__bd">
                <div class="weui-media-box_appmsg pd-10">
                    <div class="weui-media-box__hd check-w weui-cells_checkbox">
                        <div class="weui-check__label" for="cart-pto1">
                            <div class="weui-cell__hd cat-check"><input type="checkbox" class="weui-check" name="cartpro" id="cart-pto1"><i class="weui-icon-checked"></i></div>
                        </div>
                    </div>
                    <div class="weui-media-box__hd"><a href="/h5/ProductDetail?id=${item.id}"><img class="weui-media-box__thumb" src="${item.picture}" alt=""></a></div>
                    <div class="weui-media-box__bd">
                        <h1 class="weui-media-box__desc"><a href="/h5/ProductDetail?id=${item.id}" class="ord-pro-link">${item.name}</a></h1>
                        <p class="weui-media-box__desc">规格:<span>套</span></p>
                        <div class="clear mg-t-10">
                            <div class="wy-pro-pri fl">¥<em class="num font-15">${item.price}</em></div>
                            <div class="pro-amount fr">
                                <div class="weui-count">
                                    <a class="weui-count__btn weui-count__decrease"></a>
                                    <input class="weui-count__number" type="number" value="${item.num}" />
                                    <a class="weui-count__btn weui-count__increase"></a>
                                    <input type="hidden" class="pro-id" value="${item.id}"/>
                                 </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>`;
$("#divCart").append(html);
});

...................

对于数量的修改,我们同时修改购物车里面的数据即可,如下代码所示。

//对数量改变进行处理,更新购物车的对应产品id的数量
var MAX = 99, MIN = 1;
$(
'.weui-count__decrease').each(function(i) {
$(
this).click(function(e) {var $input = $(e.currentTarget).parent().find('.weui-count__number');var number = parseInt($input.val() || "0") - 1 if (number < MIN) number =MIN;
$input.val(number);
var id = $(e.currentTarget).parent().find(".pro-id").val();
updateProductNum(id, number);
});
});
$(
'.weui-count__increase').each(function(i) {
$(
this).click(function(e) {var $input = $(e.currentTarget).parent().find('.weui-count__number');var number = parseInt($input.val() || "0") + 1 if (number > MAX) number =MAX;
$input.val(number);
var id = $(e.currentTarget).parent().find(".pro-id").val();
updateProductNum(id, number);
});
});

其中updateProductNum是对购物车数据的修改。

//修改购物车的内容
functionupdateProductNum(id, number) {
console.log(id
+ ':' +number);
cart.updateProductNum(id, number);
//显示数量和金额 var amount = orderdetail.totalAmount;//获取购物车的总金额 $("#zong1").text(amount);
}

购物车的清空指定商品的JS代码如下所示。

$(document).on("click", ".wy-dele", function() {
$.confirm(
"您确定要把此商品从购物车删除吗?", "确认删除?", function() {var id = $(this).parent().parent().find(".pro-id").val();
cart.clearProduct(id);
//移除指定商品 showCartList();//更新列表 $.toast("文件已经删除!");
},
function() {//取消操作 });
});

购物车的结算页面,前面介绍过了,就是如下图所示,类似我们直接购买商品的界面。

这个地方主要差异在于也是动态获取一个或多个商品记录的信息,如下所示。

HTML视图界面代码如下所示。

        <divid="divCart">@*<divclass="wy-media-box weui-media-box_text">
            <divclass="weui-media-box__bd">
                <divclass="weui-media-box_appmsg ord-pro-list">
                    <divclass="weui-media-box__hd"><ahref="pro_info.html"><imgid="productimg"class="weui-media-box__thumb"src=""alt=""></a></div>
                    <divclass="weui-media-box__bd">
                        <h1class="weui-media-box__desc"><ahref="/h5/ProductDetail?id=@Request["ID"]" class="ord-pro-link"><spanid="productname"></span></a></h1>
                        <pclass="weui-media-box__desc">规格:<span></span></p>
                        <divclass="clear mg-t-10">
                            <divclass="wy-pro-pri fl">¥<emclass="num font-15"id="price">0</em></div>
                            <divclass="pro-amount fr"><spanclass="font-13">数量×<emclass="name">1</em></span></div>
                        </div>
                    </div>
                </div>
            </div>
        </div>*@</div>

JS代码主要就是动态构建列表,divCart里面的内容实现根据购物车商品列表进行处理,如下代码所示。

        //显示购物车列表
        functionshowCartList() {var productlist = cart.getProductList();    //取出购物车商品
            $("#divCart").html("");//构建HTML控件
            var html = "";
$.each(productlist,
function(i, item) {
html
+=`<div class="wy-media-box weui-media-box_text"> <div class="weui-media-box__bd"> <div class="weui-media-box_appmsg ord-pro-list"> <div class="weui-media-box__hd"><a href="/h5/ProductDetail?id=${item.id}"><img id="productimg" class="weui-media-box__thumb" src="${item.picture}" alt=""></a></div> <div class="weui-media-box__bd"> <h1 class="weui-media-box__desc"><a href="/h5/ProductDetail?id=${item.id}" class="ord-pro-link">${item.name}</a></h1> <p class="weui-media-box__desc">规格:<span>套</span></p> <div class="clear mg-t-10"> <div class="wy-pro-pri fl">¥<em class="num font-15" id="price">${item.price}</em></div> <div class="pro-amount fr"><span class="font-13">数量×<em class="name">${item.num}</em></span></div> <input type="hidden" class="pro-id" value="${item.id}"/> </div> </div> </div> </div> </div>`; });
$(
"#divCart").append(html);//显示数量和金额 var amount = orderdetail.totalAmount;//获取购物车的总金额 $("#totalmoney").text(amount);
}

之前一直采用VS进行各种前端后端的开发,随着项目的需要,正逐步融合纯前端的开发模式,开始主要选型为Vue + Element 进行BS前端的开发,后续会进一步整合Vue + AntDesign的界面套件,作为两种不同界面框架的展现方式。采用Vue + Element 的前端开发和之前的开发模式需要有较大的转变,以及需要接触更多的相关知识,本系列随笔基于循序渐进的学习研究方式,对使用Vue + Element 这种前端开发的各个方面进行一个完整的介绍,并结合我对BS前端已有的框架功能,进行两者的融合。本篇随笔主要介绍开发环境的准备工作,包括需要准备好相关的开发工具,插件辅助等,以及对开发保存的自动修正处理,调试的配置的内容等。

1、开发所需的软件环境

有别于之前的Asp.net的开发,纯前端的开发,一般不会再采用笨重的VS进行前端的开发,而改用VS Code或者WebStorm等轻型的开发工具来进行前端代码的开发和维护,虽然是轻型开发工具,不过功能也是非常强大的,而且开发环境可以在Windows系统,也可以在Mac系统等,实现多平台的开发环境。

1)VS code的安装

VS Code(Visual Studio Code)是由微软研发的一款免费、开源的跨平台文本(代码)编辑器。几乎完美的编辑器。

官网:
https://code.visualstudio.com

文档:
https://code.visualstudio.com/docs

源码:
https://github.com/Microsoft/vscode

VS Code的界面大概如下所示,一般安装后,如果为英文界面,则安装它的中文包即可。

VS Code安装后,我们一般还需要搜索安装一些所需要的插件辅助开发。安装插件很简单,在搜索面板中查找到后,直接安装即可。

一般我们需要安装这些vs code 插件:

Vetur

Vue多功能集成插件,包括:语法高亮,智能提示,emmet,错误提示,格式化,自动补全,debugger。vscode官方钦定Vue插件,Vue开发者必备。

ESLint

ESLint 是一个语法规则和代码风格的检查工具,可以用来保证写出语法正确、风格统一的代码。

而 VSCode 中的 ESLint 插件就直接将 ESLint 的功能集成好,安装后即可使用,对于代码格式与规范的细节还可以自定义,并且一个团队可以共享同一个配置文件,这样一个团队所有人写出的代码就可以使用同一个代码规范,在代码签入前每个人可以完成自己的代码规范检查。

VS Code - Debugger for Chrome
结合Chrome进行调试的插件

此工具简直就是前端开发必备,将大大地改变你的开发与调试模式。

以往的前端调试,主要是 JavaScript 调试,你需要在 Chrome 的控制台中找到对应代码的部分,添加上断点,然后在 Chrome 的控制台中单步调试并在其中查看值的变化。

而在使用了 Debugger for Chrome 后,当代码在 Chrome 中运行后,你可以直接在 VSCode 中加上断点,点击运行后,Chrome 中的页面继续运行,执行到你在 VSCode 中添加的断点后,你可以直接在 VSCode 中进行单步调试。

Beautify

Beautify 插件可以快速格式化你的代码格式,让你在编写代码时杂乱的代码结构瞬间变得非常规整,代码强迫症必备,较好的代码格式在后期维护以及他人阅读时都会有很多的便利。

2) 安装node开发环境

利用VS Code开发,我们很多时候,需要使用命令行npm进行相关模块的安装,这些需要node环境的支持,安装好node后,npm也就一起安装好了。

node 下载:
https://nodejs.org/en/

安装后,我们可以通过命令行或者VS Code 里面的Shell 进行查看node 和npm 的版本号了

node -v

npm -v

3)vue脚手架的安装

Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。

全局安装:npm install vue-cli -g (全局卸载:npm uninstall vue-cli -g)

4)Vue DevTool Chrome插件的安装

这个插件也是开发Vue必备的Chrome插件,一般没有外网,不能直接在Chrome的插件官网上进行安装,而通过GitHub下载进行编译在安装又显得太过麻烦,后来在一个网站上下载安装成功。

https://chrome.zzzmh.cn/info?token=nhdogjmejiglipccpnnnanhbledajbpd

2、开发环境的配置使用

对于
Vetur
等代码自动修正处理,我们需要在VS Code里面进行设置好,在【文件】【首选项】【设置】中,然后单击Settings.json进行编辑即可。

我这里主要设置保存代码后能够对代码进行缩进排版的常规的处理

调试环境的处理,为了结合Chrome调试VScode,我们需要安装插件Debugger for Chrome ,然后进行Vue项目代码的设置处理即可。

打开项目根目录的Vue.Config.js文件,在合适的位置,加入 productionSourceMap: true 以及 devtool: 'source-map'  如下所示

然后再在运行面板里面,进行调试参数设置的处理,如下所示

指定这些设置后,我们就可以以调试模式进行调试VS Code里面的代码了,代码只需要设置对应的断点即可跟踪对象的数据。

调试前,记得先使用npm run dev 启动项目,项目完全启动后会在Chrome浏览器打开项目地址,再使用F5进行项目代码的调试。

Vue DevTools也是用来跟踪Vue项目路由、状态等信息的,可以信息很好的跟踪处理。

为了点亮Chrome浏览器上面Vue DevTools图标,我们可以在Vue项目的main.js里面加入一行代码。

Vue.config.devtools = process.env.NODE_ENV === 'development'
如下界面所示

这篇随笔作为一个简单的开篇,主要介绍VS Code环境的安装,以及对应插件的配置,并联合Chrome如何实现项目代码的调试处理。

为了方便读者理解,我列出一下前面几篇随笔的连接,供参考:

循序渐进VUE+Element 前端应用开发(1)--- 开发环境的准备工作

循序渐进VUE+Element 前端应用开发(2)--- Vuex中的API、Store和View的使用

循序渐进VUE+Element 前端应用开发(3)--- 动态菜单和路由的关联处理

循序渐进VUE+Element 前端应用开发(4)--- 获取后端数据及产品信息页面的处理

循序渐进VUE+Element 前端应用开发(5)--- 表格列表页面的查询,列表展示和字段转义处理


循序渐进VUE+Element 前端应用开发(6)--- 常规Element 界面组件的使用

循序渐进VUE+Element 前端应用开发(7)--- 介绍一些常规的JS处理函数

循序渐进VUE+Element 前端应用开发(8)--- 树列表组件的使用


循序渐进VUE+Element 前端应用开发(9)--- 界面语言国际化的处理

循序渐进VUE+Element 前端应用开发(10)--- 基于vue-echarts处理各种图表展示

循序渐进VUE+Element 前端应用开发(11)--- 图标的维护和使用


循序渐进VUE+Element 前端应用开发(12)--- 整合ABP框架的前端登录处理


循序渐进VUE+Element 前端应用开发(13)--- 前端API接口的封装处理

循序渐进VUE+Element 前端应用开发(14)--- 根据ABP后端接口实现前端界面展示

循序渐进VUE+Element 前端应用开发(15)--- 用户管理模块的处理

循序渐进VUE+Element 前端应用开发(16)--- 组织机构和角色管理模块的处理

循序渐进VUE+Element 前端应用开发(17)--- 菜单管理

循序渐进VUE+Element 前端应用开发(18)--- 功能点管理及权限控制

VUE+Element 前端应用开发框架功能介绍

循序渐进VUE+Element 前端应用开发(19)--- 后端查询接口和Vue前端的整合

使用代码生成工具快速生成基于ABP框架的Vue+Element的前端界面

循序渐进VUE+Element 前端应用开发(20)--- 使用组件封装简化界面代码

循序渐进VUE+Element 前端应用开发(21)--- 省市区县联动处理的组件使用

循序渐进VUE+Element 前端应用开发(22)--- 简化main.js处理代码,抽取过滤器、全局界面函数、组件注册等处理逻辑到不同的文件中

循序渐进VUE+Element 前端应用开发(23)--- 基于ABP实现前后端的附件上传,图片或者附件展示管理

循序渐进VUE+Element 前端应用开发(24)--- 修改密码的前端界面和ABP后端设置处理

循序渐进VUE+Element 前端应用开发(25)--- 各种界面组件的使用(1)

循序渐进VUE+Element 前端应用开发(26)--- 各种界面组件的使用(2)

电商商品数据库的设计和功能界面的处理

循序渐进VUE+Element 前端应用开发(27)--- 数据表的动态表单设计和数据存储

循序渐进VUE+Element 前端应用开发(28)--- 附件内容的管理

循序渐进VUE+Element 前端应用开发(29)--- 高级查询条件的界面设计

部署基于.netcore5.0的ABP框架后台Api服务端,以及使用Nginx部署Vue+Element前端应用

循序渐进VUE+Element 前端应用开发(30)--- ABP后端和Vue+Element前端结合的分页排序处理

循序渐进VUE+Element 前端应用开发(31)--- 系统的日志管理,包括登录日志、接口访问日志、实体变化历史日志

循序渐进VUE+Element 前端应用开发(32)--- 手机短信动态码登陆处理

循序渐进VUE+Element 前端应用开发(33)--- 邮件参数配置和模板邮件发送处理

使用代码生成工具快速开发ABP框架项目

使用Vue-TreeSelect组件实现公司-部门-人员级联下拉列表的处理

使用Vue-TreeSelect组件的时候,用watch变量方式解决弹出编辑对话框界面无法触发更新的问题

在我们开发Vue应用的时候,很多时候需要记录一些变量的内容,这些可以用来做界面状态的承载,也可以作为页面间交换数据的处理,处理这些内容可以归为Vuex的状态控制。例如我们往往前端需要访问后端数据,一般是通过封装的Web API调用获取数据,而使用Store模式来处理相关的数据或者状态的变化,而视图View主要就是界面的展示处理。本篇随笔主要介绍如何整合这三者之间的关系,实现数据的获取、处理、展示等逻辑操作。

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。关于Vuex的相关State、Getter、Mutation、Action、Module之间的差异和联系,详细可以参考下:
https://vuex.vuejs.org/zh/

1、前后端的分离和Web API 优先路线设计

Web API 是一种应用接口框架,它能够构建HTTP服务以支撑更广泛的客户端(包括浏览器,手机和平板电脑等移动设备)的框架,
ASP.NET Web API 是一种用于在 .NET Framework/ .net Core 上构建 RESTful 应用程序的理想平台。
在目前发达的应用场景下,我们往往需要接入Winform客户端、APP程序、网站程序、以及目前热火朝天的微信应用等,这些数据应该可以由同一个服务提供,这个就是我们所需要构建的Web API平台。由于Web API层作为一个公共的接口层,我们就很好保证了各个界面应用层的数据一致性。

由于倾向于前后端的完全分离,我们后端就可以完全由Web API统一构建支持,可以采用.net framework或者.net core构建的统一接口平台,可以简单由Asp.net 做的Web API接口平台,也可以基于ABP-aspnetboilerplate(
ABP框架随笔介绍
)框架基础上构建的Web API平台。

这样我们就可以基于这些API接口构建前端多项应用,如包括Web前端、Winform前端、以及对接各种APP等应用。

引入了前后端分离的VUE + Element 的开发方式,那么前后端的边界则非常清晰,前端可以在通过网络获取对应的JSON就可以构建前端的应用了。

在前端处理中,主要就是利用Vuex模式中的Store对象里实现对Action和Mutation的请求处理,获取数据后,实现对State状态中的数据进行更新。如果仅仅是当前页面的数据处理,甚至可以不需要存储State信息,直接获取到返回的数据,直接更新到界面视图上即可。

在开发前期,我们甚至可以不需要和后端发生任何关系,通过Mock数据代替从Web API上请求数据,只要Mock的数据结构和Web API接口返回的JSON一致,我们就可以在后期实现快速的对接,而不影响现有的代码处理方式。

2、Axios网络请求处理

在我们进一步处理前,我们需要知道Vuex里面的一些对象概念和他们之间的关系。

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。关于Vuex的相关State、Getter、Mutation、Action、Module之间的差异和联系,详细可以参考下:
https://vuex.vuejs.org/zh/

在开始发起网络请求之前,我们需要了解axios 这个东西,axios 是一个基于Promise 用于浏览器和 nodejs 的 HTTP 客户端,本质上也是对原生XHR的封装,只不过它是Promise的实现版本,符合最新的ES规范。在这里我们只需要知道它是非常强大的网络请求处理库,且得到广泛应用即可,列举几个代码案例进行了解。

POST请求

axios({
method:
'post',
url:
'/user/12345',
data: {
firstName:
'Fred',
lastName:
'Flintstone'}
})
.then(
function(response) {
console.log(response);
})
.
catch(function(error) {
console.log(error);
});

GET请求

axios
.get(
'http://rap2api.taobao.org/app/mock/23080/resources/search',{
params: {
id:
5}
})
.then(res
=>{
console.log(
'数据是:', res);
})
.
catch((e) =>{
console.log(
'获取数据失败');
});

如果我们要跨域请求数据,在配置文件里设置代理,vue-cli3项目,需要在vue.config.js里面写配置。

可以分别设置请求拦截和响应拦截,在发出请求和响应到达then之前进行判断处理,一般的处理方式就是封装一个类如request类,然后进行对拦截器的统一处理,如在请求前增加一些用户身份信息等。

//create an axios instance
const service =axios.create({
timeout:
5000 //request timeout })//request 请求拦截 service.interceptors.request.use(
config
=>{if(store.getters.token) {
config.headers[
'X-Token'] =getToken()
}
returnconfig
},
error
=>{//do something with request error console.log(error) //for debug returnPromise.reject(error)
}
)

3、Vuex中的API、Store和View的使用

我们再次回到Vuex中的API、Store和View的使用介绍上。

我们来看看API的封装请求调用类的封装,如下所示,我们创建了一些操作数据的API类文件,每个API名称对应一个业务的集中处理,包括特定业务的列表请求、单个请求、增加、删除、修改等等都可以封装在一个API类里面。

我们来看看Product.js的类文件定义如下所示。

这里我用了Request和Axios的操作对比,两者很接近,因为request是对Axios的简单封装,主要就是拦截注入一些登录信息和一些响应的错误处理而已。

import request from '@/utils/request'
import axios from 'axios'

这里的Url里面,通过代理配置的处理,会把对应的iqidi替换为对应外部域名的处理,从而实现对跨域处理请求数据的获取了,我们这里只需要知道,url最终会转换为类似

http://www.iqidi.com/h5/GetProductList 这样实际的地址进行请求的即可,返回是一个JSON数据集合。

由于Vue视图里面的JS处理部分,可以直接引入API进行请求数据,如下所示。

import { GetProductList } from '@/api/product'

然后我们就可以在method方法里面定义一个获取API数据的方法了。

methods: {
getlist(type) {
GetProductList({ type: type }).then(response
=>{
const { data }
=responsethis.productlist =data.listthis.listLoading = false})
}

这种调用是最直接的API调用,没有引入Store模块中封装的Action或者Mutation进行异步或者同步的处理。一般情况下直接使用这种方式比较简洁,因为大多数页面处理或者组件处理,不需要对数据进行全局状态的存储处理,也就是不需要进行全局Store对象的处理了。

如果我们需要在全局存储对应的信息,那么就需要引入Store模块中对API调用的封装了,包括Action或者Mutation的处理。

我们先来定义Store存储类,如下界面所示。

如果我们需要对产品列表等数据进行全局状态的存储,那么我们可以考虑创建一个对应Store目录下的模块,如product.js,来管理Action、Mutation和State等信息。

import { GetProductList, GetProductDetail } from '@/api/product'const state={
productlist: [],
productdetail:
null}
const mutations
={
SET_PRODUCT_LIST: (state, list)
=>{
state.productlist
=list
},
SET_PRODUCT_DETAIL: (state, detail)
=>{
state.productdetail
=detail
}
}

const actions
={//产品列表 getProductList({ commit }, { type }) {
console.log(type);
return new Promise((resolve, reject) =>{
GetProductList({ type: type }).then(response
=>{
const { data }
=response
commit(
'SET_PRODUCT_LIST', data)
resolve(data)
}).
catch(error =>{
reject(error)
})
})
},
//获取产品明细 getProductDetail({ commit }, { id }) {return new Promise((resolve, reject) =>{
GetProductDetail({ id: id }).then(response
=>{
const { data }
=response
commit(
'SET_PRODUCT_DETAIL', data)
resolve(data)
}).
catch(error =>{
reject(error)
})
})
}
}

export
default{
namespaced:
true,
state,
mutations,
actions
}

我们下来看看,如果引入了Store模块的业务类,那么在界面视图中调用代码则修改为调用对应的Action或者Mutation了。

methods: {
getlist(type) {
//GetProductList({ type: type }).then(response => { //const { data } = response //this.productlist = data.list //this.listLoading = false //}) this.$store.dispatch('product/getProductList', { type: type }).then(data =>{this.productlist =data.list//this.loading = false }).catch((e) =>{//this.loading = false })
}

我们这里强调一下,一般情况下在视图模块中使用API的类调用即可,不需要累赘的每个业务模块,都创建一个Store的模块类进行相应的管理,只有在这些状态数据需要在多个页面或者组件中需要共享的时候,才考虑引入Store模块类进行细化管理。

我们刚才说到,如果需要创建对应业务模块的Store状态管理模块,那么需要创建对应的模块类,如前面说到的product.js类文件。

其中Modules目录里面的按照业务区分边界的Vuex的Store管理类了,每个对应业务创建一个单独的文件进行管理(如果需要用到的话)。

在index.js里面我们通过模块动态加载的方式,把这些类按照不同的命名空间进行加载进来,统一使用。

import Vue from 'vue'import Vuex from'vuex'import getters from'./getters'Vue.use(Vuex)//https://webpack.js.org/guides/dependency-management/#requirecontext
const modulesFiles = require.context('./modules', true, /\.js$/)//you do not need `import app from './modules/app'`//it will auto require all vuex module from modules file
const modules = modulesFiles.keys().reduce((modules, modulePath) =>{//set './app.js' => 'app'
  const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
const value
=modulesFiles(modulePath)
modules[moduleName]
= value.default returnmodules
}, {})

const store
= newVuex.Store({
modules,
getters
})

export
default store

为了方便读者理解,我列出一下前面几篇随笔的连接,供参考:

循序渐进VUE+Element 前端应用开发(1)--- 开发环境的准备工作

循序渐进VUE+Element 前端应用开发(2)--- Vuex中的API、Store和View的使用

循序渐进VUE+Element 前端应用开发(3)--- 动态菜单和路由的关联处理

循序渐进VUE+Element 前端应用开发(4)--- 获取后端数据及产品信息页面的处理

循序渐进VUE+Element 前端应用开发(5)--- 表格列表页面的查询,列表展示和字段转义处理


循序渐进VUE+Element 前端应用开发(6)--- 常规Element 界面组件的使用

循序渐进VUE+Element 前端应用开发(7)--- 介绍一些常规的JS处理函数

循序渐进VUE+Element 前端应用开发(8)--- 树列表组件的使用


循序渐进VUE+Element 前端应用开发(9)--- 界面语言国际化的处理

循序渐进VUE+Element 前端应用开发(10)--- 基于vue-echarts处理各种图表展示

循序渐进VUE+Element 前端应用开发(11)--- 图标的维护和使用


循序渐进VUE+Element 前端应用开发(12)--- 整合ABP框架的前端登录处理


循序渐进VUE+Element 前端应用开发(13)--- 前端API接口的封装处理

循序渐进VUE+Element 前端应用开发(14)--- 根据ABP后端接口实现前端界面展示

循序渐进VUE+Element 前端应用开发(15)--- 用户管理模块的处理

循序渐进VUE+Element 前端应用开发(16)--- 组织机构和角色管理模块的处理

循序渐进VUE+Element 前端应用开发(17)--- 菜单管理

循序渐进VUE+Element 前端应用开发(18)--- 功能点管理及权限控制

VUE+Element 前端应用开发框架功能介绍

循序渐进VUE+Element 前端应用开发(19)--- 后端查询接口和Vue前端的整合

使用代码生成工具快速生成基于ABP框架的Vue+Element的前端界面

循序渐进VUE+Element 前端应用开发(20)--- 使用组件封装简化界面代码

循序渐进VUE+Element 前端应用开发(21)--- 省市区县联动处理的组件使用

循序渐进VUE+Element 前端应用开发(22)--- 简化main.js处理代码,抽取过滤器、全局界面函数、组件注册等处理逻辑到不同的文件中

循序渐进VUE+Element 前端应用开发(23)--- 基于ABP实现前后端的附件上传,图片或者附件展示管理

循序渐进VUE+Element 前端应用开发(24)--- 修改密码的前端界面和ABP后端设置处理

循序渐进VUE+Element 前端应用开发(25)--- 各种界面组件的使用(1)

循序渐进VUE+Element 前端应用开发(26)--- 各种界面组件的使用(2)

电商商品数据库的设计和功能界面的处理

循序渐进VUE+Element 前端应用开发(27)--- 数据表的动态表单设计和数据存储

循序渐进VUE+Element 前端应用开发(28)--- 附件内容的管理

循序渐进VUE+Element 前端应用开发(29)--- 高级查询条件的界面设计

部署基于.netcore5.0的ABP框架后台Api服务端,以及使用Nginx部署Vue+Element前端应用

循序渐进VUE+Element 前端应用开发(30)--- ABP后端和Vue+Element前端结合的分页排序处理

循序渐进VUE+Element 前端应用开发(31)--- 系统的日志管理,包括登录日志、接口访问日志、实体变化历史日志

循序渐进VUE+Element 前端应用开发(32)--- 手机短信动态码登陆处理

循序渐进VUE+Element 前端应用开发(33)--- 邮件参数配置和模板邮件发送处理

使用代码生成工具快速开发ABP框架项目

使用Vue-TreeSelect组件实现公司-部门-人员级联下拉列表的处理

使用Vue-TreeSelect组件的时候,用watch变量方式解决弹出编辑对话框界面无法触发更新的问题

在我开发的很多系统里面,包括Winform混合框架、Bootstrap开发框架等系列产品中,我都倾向于动态配置菜单,并管理对应角色的菜单权限和页面权限,实现系统对用户权限的控制,菜单一般包括有名称、图标、顺序、URL连接等相关信息,对于VUE+Element 前端应用来说,应该原理上差不多,本篇随笔介绍结合服务端的动态菜单配置和本地路由的关联处理,实现动态菜单的维护和展示的处理。

1、菜单和路由的处理过程

由于Vue前端还需要引入路由这个概念,路由是我们前端可以访问到的对应路径集合,路由定义了常规菜单说没有的很多复杂信息,但是往往这些是我们不能随意修改的,因此我们做法是以本地配置好的路由列表为基准,而菜单我们采用在后盾配置方式,前端通过接口动态获取菜单列表,通过菜单的名称和路由名称的对应关系,我们以菜单集合为对照,然后过滤本地所有静态路由的列表,然后获得用户可以访问的路由列表,设置动态路由给前端,从而实现了界面根据用户角色/权限的不同,而变化用户的菜单界面和可访问路由集合。

菜单路由处理的大概的操作过程如下所示

前端界面的动态菜单、本地路由、菜单导航和可访问路由的几个概念如下所示。

在前端界面处理中,我们通过Element界面组件的方式展示动态菜单信息,并结合菜单和路由的关系,实现菜单跳转到对应视图的处理过程。

2、菜单和路由列表

根据前面的介绍,我们定义了一些从服务端返回的动态菜单信息,这些菜单信息是一个JSON对象集合,如下界面所示。

[
{
id:
'1',
pid:
'-1',
text:
'首页',
icon:
'dashboard',
name:
'dashboard'},
{
id:
'2',
pid:
'-1',
text:
'产品列表',
icon:
'table',
name:
'product'},
{
id:
'3',
pid:
'-1',
text:
'一级菜单',
icon:
'example',
children: [
{
id:
'3-1',
pid:
'3',
text:
'二级菜单1',
name:
'icon',
icon:
'example'},
{
id:
'3-2',
pid:
'3',
text:
'二级菜单2',
icon:
'tree',
children: [
{
id:
'3-2-1',
pid:
'3-2',
text:
'三级菜单1',
name:
'form',
icon:
'form'},
{
id:
'3-2-2',
pid:
'3-2',
text:
'三级菜单2',
name:
'menu1-1',
icon:
'form'},
{
id:
'3-2-3',
pid:
'3-2',
text:
'三级菜单3',
name:
'menu1-2',
icon:
'form'},
{
id:
'3-2-4',
pid:
'3-2',
text:
'三级菜单4',
name:
'menu1-3',
icon:
'form'}
]
}
]
},
{
id:
'99',
pid:
'-1',
text:
'公司官网',
icon:
'table',
name:
'external-link'}
]

菜单的JSON是根据角色进行动态获取的,不同的角色对应不同的菜单集合,并且菜单是一个多层次的树列表,可以定义无穷多级的展示,JSON格式化视图如下所示。

而Vue前端需要初始化定义前端页面的所有路由,这些包括路由页面的Layout等信息。

我们可以在一个JS文件里面定义好对应前端所有的路由信息,如下所示

//定义本系统的所有路由,具体路由呈现经过菜单数据过滤
export const asyncRoutes ={'dashboard': {
path:
'/dashboard',
component: Layout,
children: [{
path:
'dashboard',
name:
'dashboard',
component: ()
=> import('@/views/dashboard/index')
}]
},
'product': {
path:
'/product',
component: Layout,
children: [{
path:
'/product',
name:
'product',
component: ()
=> import('@/views/Product/index')
}]
},

..............................
//省略部分 'icon': {
path:
'/icon',
component: Layout,
children: [{
path:
'/icon',
name:
'icon',
component: ()
=> import('@/views/icons/index')
}]
},
'external-link': {
path:
'http://www.iqidi.com',
name:
'external-link'}
}

这里的路由不需要嵌套,因为菜单展示才需要定义嵌套关系。

另外,由于系统在未登录请求后端动态菜单前,我们系统也需要正常运行起来,那么就需要预设一些基础的路由信息,如登录界面、重定向页面、首页链接等这些路由信息,因此我们可以分开两个路由对象,用来分开管理这些信息。

对路由的管理,一个需要默认创建路由的处理、重置路由的处理,以及动态设置新的路由处理,我们封装几个函数来处理这些操作。

const createRouter = () => newRouter({//mode: 'history', // require service support
  scrollBehavior: () => ({ y: 0}),
routes: constantRoutes
})

const router
=createRouter()//重置路由 export functionresetRouter() {
const newRouter
=createRouter()
router.matcher
= newRouter.matcher //reset router }

用户在经过登录界面处理后,就会通过对应的Action获取动态路由信息(注意,这里是先获取动态菜单,然后过滤本地路由,即为动态路由信息),获得动态路由后,就设置前端所能访问的路由集合即可,如下代码所示。

有了这些新的路由允许,前端系统的菜单才能够正常运转起来,否则即使界面展示了菜单,也不能访问特定的视图页面而跳到了404页面,因为路由没有。

3、登录的过程处理

前面大概介绍了路由的处理过程,其实我们路由信息,应该需要从登录界面开始讲起。

以登录界面为例,在用户登录处理后,需要先验证用户的账号密码,成功后继续请求该用户对应的动态菜单集合,并通过路由切换到对应的页面或者首页。

在Store/Modules/user.js模块里面,定义了对应的登陆处理Action,如下所示

我们这里忽略用户登录的检验和处理token的过程,主要关注动态菜单请求并设置路由的过程。

在我们需要拦截路由到达前的处理中,我们定义对应的路由信息请求逻辑,如下所示。

router.beforeEach(async(to, from, next) => {

在处理菜单路由的对应模块里面,我们定义了一个状态用来承载这些重要信息,如下定义State所示。

const state ={menuItems:[],
routes: [],
addRoutes: [],
asyncRoutes: asyncRoutes
}
//定义了路由和菜单的Mutation
const mutations ={
SET_ROUTES: (state, routes)
=>{//var list = convertRoute(routes) routes.push({ path: '*', redirect: '/404', hidden: true }) //此为默认错误路由 state.addRoutes=routes
state.routes
= [].concat(routes)//constantRoutes.concat(routes) },
SET_MENUS: (state, menus)
=>{
state.menuItems
=menus
}
}
//定义了生成动态路由的Action处理
const actions ={
generateRoutes({ commit }, roles) {
return new Promise(resolve =>{
getMenus().then(res
=>{
const menus
= res.data || [] //统一通过接口获取菜单信息 const routes =[]

menus.forEach(item
=>{
filterRoutes(routes, item)
})
console.log(routes)
//打印路由 commit('SET_ROUTES', routes)
commit(
'SET_MENUS', menus)
resolve(routes)
});
})
}
}

最后返回对应的JS定义模块类信息即可。

export default{
namespaced:
true,
state,
mutations,
actions
}

在前端界面处理中,我们通过Element界面组件的方式展示动态菜单信息,并结合菜单和路由的关系,实现菜单跳转到对应视图的处理过程。

我们来看看界面生成的动态菜单效果。

由于菜单动态展示和动态路由配合,因此既能在前端实现动态菜单的展示,又会根据菜单的集合刷新可访问路由,两者结合就可以顺利打开对应的视图页面了。

再来回顾一下,菜单路由处理的大概的操作过程如下所示

为了方便读者理解,我列出一下前面几篇随笔的连接,供参考:

循序渐进VUE+Element 前端应用开发(1)--- 开发环境的准备工作

循序渐进VUE+Element 前端应用开发(2)--- Vuex中的API、Store和View的使用

循序渐进VUE+Element 前端应用开发(3)--- 动态菜单和路由的关联处理

循序渐进VUE+Element 前端应用开发(4)--- 获取后端数据及产品信息页面的处理

循序渐进VUE+Element 前端应用开发(5)--- 表格列表页面的查询,列表展示和字段转义处理


循序渐进VUE+Element 前端应用开发(6)--- 常规Element 界面组件的使用

循序渐进VUE+Element 前端应用开发(7)--- 介绍一些常规的JS处理函数

循序渐进VUE+Element 前端应用开发(8)--- 树列表组件的使用


循序渐进VUE+Element 前端应用开发(9)--- 界面语言国际化的处理

循序渐进VUE+Element 前端应用开发(10)--- 基于vue-echarts处理各种图表展示

循序渐进VUE+Element 前端应用开发(11)--- 图标的维护和使用


循序渐进VUE+Element 前端应用开发(12)--- 整合ABP框架的前端登录处理


循序渐进VUE+Element 前端应用开发(13)--- 前端API接口的封装处理

循序渐进VUE+Element 前端应用开发(14)--- 根据ABP后端接口实现前端界面展示

循序渐进VUE+Element 前端应用开发(15)--- 用户管理模块的处理

循序渐进VUE+Element 前端应用开发(16)--- 组织机构和角色管理模块的处理

循序渐进VUE+Element 前端应用开发(17)--- 菜单管理

循序渐进VUE+Element 前端应用开发(18)--- 功能点管理及权限控制

VUE+Element 前端应用开发框架功能介绍

循序渐进VUE+Element 前端应用开发(19)--- 后端查询接口和Vue前端的整合

使用代码生成工具快速生成基于ABP框架的Vue+Element的前端界面

循序渐进VUE+Element 前端应用开发(20)--- 使用组件封装简化界面代码

循序渐进VUE+Element 前端应用开发(21)--- 省市区县联动处理的组件使用

循序渐进VUE+Element 前端应用开发(22)--- 简化main.js处理代码,抽取过滤器、全局界面函数、组件注册等处理逻辑到不同的文件中

循序渐进VUE+Element 前端应用开发(23)--- 基于ABP实现前后端的附件上传,图片或者附件展示管理

循序渐进VUE+Element 前端应用开发(24)--- 修改密码的前端界面和ABP后端设置处理

循序渐进VUE+Element 前端应用开发(25)--- 各种界面组件的使用(1)

循序渐进VUE+Element 前端应用开发(26)--- 各种界面组件的使用(2)

电商商品数据库的设计和功能界面的处理

循序渐进VUE+Element 前端应用开发(27)--- 数据表的动态表单设计和数据存储

循序渐进VUE+Element 前端应用开发(28)--- 附件内容的管理

循序渐进VUE+Element 前端应用开发(29)--- 高级查询条件的界面设计

部署基于.netcore5.0的ABP框架后台Api服务端,以及使用Nginx部署Vue+Element前端应用

循序渐进VUE+Element 前端应用开发(30)--- ABP后端和Vue+Element前端结合的分页排序处理

循序渐进VUE+Element 前端应用开发(31)--- 系统的日志管理,包括登录日志、接口访问日志、实体变化历史日志

循序渐进VUE+Element 前端应用开发(32)--- 手机短信动态码登陆处理

循序渐进VUE+Element 前端应用开发(33)--- 邮件参数配置和模板邮件发送处理

使用代码生成工具快速开发ABP框架项目

使用Vue-TreeSelect组件实现公司-部门-人员级联下拉列表的处理

使用Vue-TreeSelect组件的时候,用watch变量方式解决弹出编辑对话框界面无法触发更新的问题

在前面随笔《
循序渐进VUE+Element 前端应用开发(3)--- 动态菜单和路由的关联处理
》中介绍了在Vue + Element整合框架中,实现了动态菜单和动态路由的处理,从而可以根据用户角色对应的菜单实现本地路由的过滤和绑定,菜单顺利弄好了,就需要进一步开发页面功能了,本篇随笔介绍一个案例,通过获取后端数据后,进行产品信息页面的处理。

1、后端数据的获取处理

前面随笔,我们介绍过了Vue + Element的前端框架中,主要通过后端获取数据,并呈现在界面视图或者组件上的。

前后端的边界则非常清晰,前端可以在通过网络获取对应的JSON就可以构建前端的应用了。

我们通过Vue.config.js的配置信息,可以指定Proxy来处理是本地Mock数据还是实际的后端数据,如下所示。

我们要跨域请求数据,在配置文件里设置代理,vue-cli3项目,需要在vue.config.js里面写配置。

我们创建了一些操作数据的API类文件,每个API名称对应一个业务的集中处理,包括特定业务的列表请求、单个请求、增加、删除、修改等等都可以封装在一个API类里面。

然后在对应的业务API访问类,如product.js 里面定义我们的请求逻辑,主要就是利用简单封装axios的request辅助类来实现数据的请求。

下一步就是在src/views/product目录里面定义我们的视图文件,这个也就是页面文件,其中包含了常规VUE的几个部分,包括

<template>
  
</template>

<script>exportdefault{

}
</script> <style> </style>

其中在<template>里面的是界面元素部分,可以添加我们相关的界面组件等内容,如element的界面组件在里面。

其中<script>是vue脚本交互的部分了,这里面封装我们很多处理逻辑和对应的modal对象信息,在调用API类进行访问数据前,我们往往需要引入对应的API类文件,如下所示。

import { GetTopBanners, GetProductList } from '@/api/product'

其中<style>则定义相关的样式。

在开始介绍Vue 的Script部分,我们认为你已经对VUE的script相关内容,以及它的生命周期有所了解了,script部分的内容包括有。

<script>exportdefault{data() {return{};
},
methods: {
//组件的方法 },
watch: {
//watch擅长处理的场景:一个数据影响多个数据 },
computed: {
//computed擅长处理的场景:一个数据受多个数据影响 },
beforeCreate:
function() {//在实例初始化之后,数据观测(data observer) 和 event/watcher 事件配置之前被调用。 },
created:
function() {//实例已经创建完成之后被调用。在这一步,实例已完成以下的配置:数据观测(data observer),属性和方法的运算, watch/event 事件回调。然而,挂载阶段还没开始,$el 属性目前不可见。 },
beforeMount:
function() {//在挂载开始之前被调用:相关的 render 函数首次被调用。 },
mounted:
function() {//编译好的HTML挂载到页面完成后执行的事件钩子 //el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子。 //此钩子函数中一般会做一些ajax请求获取数据进行数据初始化 console.log("Home done");
},
beforeUpdate:
function() {//数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前。 你可以在这个钩子中进一步地更改状态,这不会触发附加的重渲染过程。 },
updated:
function() {//由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。 //当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。然而在大多数情况下,你应该避免在此期间更改状态,因为这可能会导致更新无限循环。 //该钩子在服务器端渲染期间不被调用。 },
beforeDestroy:
function() {//实例销毁之前调用。在这一步,实例仍然完全可用。 },
destroyed:
function() {//Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。 该钩子在服务器端渲染期间不被调用。 }
};
</script>

其中我们主要涉及到内容包括:

data,用于定义整个页面的modal对象或属性,

method,用于定义各种处理方法

computed,用于定义一些计算的树形

created,用于我们在元素创建,但是没有挂载的时候

mounted,完成页面挂载的时候

2、界面展示处理

例如我们要展示一个界面内容,需要展示产品的图片,以及产品信息介绍

那么需要定义相关的数据模板,以及对应的处理方法,在页面加载前实现数据的绑定处理。

export default{
data() {
return{
activeName:
'all',
currentDate:
newDate(),
banners: [],
productlist: [],
pageinfo: {
pageindex:
1,
pagesize:
10,
total:
0},
producttype:
'all'};
},
created() {
this.loadbanners()
this
.getlist()
},

界面部分,我们利用Element的界面组件定义一个走马灯的展示效果,如下所示。

在Template模块里面定义好的界面元素如下所示。

这里面的el-carousel 是Element组件的走马灯,而 v-for="item in banners" 就是vue的处理语法,对data模型里面的数据进行循环处理,然后逐一展示多个图片,从而实现了走马灯的效果展示。

对于列表展示,我们采用了一个卡片的展示内容,以及分页处理的方式实现数据的展示。

分类按钮部分,代码如下所示。

<el-row:gutter="20"style="padding:20px">
  <el-buttontype="primary"icon="el-icon-search":plain="producttype !='all'"@click="handleClick($event, 'all')">全部</el-button>
  <el-buttontype="success"icon="el-icon-search":plain="producttype !='1'"@click="handleClick($event, '1')">框架产品</el-button>
  <el-buttontype="warning"icon="el-icon-search":plain="producttype !='2'"@click="handleClick($event, '2')">软件产品</el-button>
  <el-buttontype="danger"icon="el-icon-search":plain="producttype !='3'"@click="handleClick($event, '3')">开发组件</el-button>
</el-row>

主要就是根据data属性进行一些样式的控制,以及响应对应的click事件。

而每个卡片内容介绍,Demo代码如下所示。

<el-col:span="4"><divclass="grid-content bg-purple" />
<el-cardclass="box-card">
  <imgsrc="https://shadow.elemecdn.com/app/element/hamburger.9cf7b091-55e9-11e9-a976-7f4d0b07eef6.png"class="image">
  <divstyle="padding: 14px;">
    <span>好吃的汉堡</span>
    <divclass="bottom clearfix">
      <timeclass="time">价格:¥200</time>
      <el-buttontype="text"class="button"@click="loadbanners()">操作按钮</el-button>
    </div>
  </div>
</el-card>
</el-col>

但是我们要根据实际获得的动态数据进行绑定,因此需要一个循环来进行处理,类似上面的v-for循环,对产品列表进行展示即可。

<el-colv-for="(item, index) in productlist":key="index":span="4"style="min-width:250px">
<divclass="grid-content bg-purple">
  <el-cardclass="box-card">
    <img:src="item.Picture"class="image"style="width:200px;height:200px">
    <divstyle="padding: 14px;">
      <span>{{ item.ProductName }}</span>
      <divclass="bottom clearfix">
        <!--<time class="time">价格:¥{{ item.Price }}</time>-->
        <el-buttontype="text"class="button">操作按钮</el-button>
      </div>
    </div>
  </el-card>
</div>
</el-col>

为了有效的请求和展示数据,我们还需要利用分页组件来进行数据的分页查询处理,分页组件界面的定义代码如下所示。

    <el-paginationbackground
layout
="prev, pager, next":page-sizes="[10,20,50]":total="pageinfo.total":current-page="pageinfo.pageindex":page-size="pageinfo.pagesize"@size-change="handleSizeChange"@current-change="handleCurrentChange" />

为了实现对数据的分页,我们需要定义当前页码、每页面数据大小、总共数据记录数等几个变量,用来进行分页查询的需要。

我们定义的getList的方法如下所示。

getlist() {//构造分页查询条件
      var param ={
type:
this.producttype === 'all' ? '' : this.producttype,
pageindex:
this.pageinfo.pageindex,
pagesize:
this.pageinfo.pagesize
};
this.listLoading = true //发起数据查询请求,并设置本地Data的值 GetProductList(param).then(data =>{this.productlist =data.listthis.pageinfo.total =data.total_countthis.listLoading = false})
},

另外定义几个方法,对数据进行查询的操作。

    //单击某类别的时候,进行查询
handleClick(e, type) {//console.log(e, type);

      this.producttype =typethis.pageinfo.pageindex = 1;this.getlist()
},
//页面数量改变后查询处理 handleSizeChange(val) {
console.log(`每页 ${val} 条`);
this.pageinfo.pagesize =val;this.getlist()
},
//页码改变后查询处理 handleCurrentChange(val) {
console.log(`当前页: ${val}`);
this.pageinfo.pageindex =val;this.getlist()
}

以上就是我们利用Element的界面组件,以及Vue的方法进行数据的分页查询请求的基础操作,通过这个简单的案例,我们可以了解一些基础的Element 界面组件的使用,以及对Data / Method等内容的了解,并指导如何封装调用跨域的API请求,实现后端数据在界面上的展示处理了。

为了方便读者理解,我列出一下前面几篇随笔的连接,供参考:

循序渐进VUE+Element 前端应用开发(1)--- 开发环境的准备工作

循序渐进VUE+Element 前端应用开发(2)--- Vuex中的API、Store和View的使用

循序渐进VUE+Element 前端应用开发(3)--- 动态菜单和路由的关联处理

循序渐进VUE+Element 前端应用开发(4)--- 获取后端数据及产品信息页面的处理

循序渐进VUE+Element 前端应用开发(5)--- 表格列表页面的查询,列表展示和字段转义处理


循序渐进VUE+Element 前端应用开发(6)--- 常规Element 界面组件的使用

循序渐进VUE+Element 前端应用开发(7)--- 介绍一些常规的JS处理函数

循序渐进VUE+Element 前端应用开发(8)--- 树列表组件的使用


循序渐进VUE+Element 前端应用开发(9)--- 界面语言国际化的处理

循序渐进VUE+Element 前端应用开发(10)--- 基于vue-echarts处理各种图表展示

循序渐进VUE+Element 前端应用开发(11)--- 图标的维护和使用


循序渐进VUE+Element 前端应用开发(12)--- 整合ABP框架的前端登录处理


循序渐进VUE+Element 前端应用开发(13)--- 前端API接口的封装处理

循序渐进VUE+Element 前端应用开发(14)--- 根据ABP后端接口实现前端界面展示

循序渐进VUE+Element 前端应用开发(15)--- 用户管理模块的处理

循序渐进VUE+Element 前端应用开发(16)--- 组织机构和角色管理模块的处理

循序渐进VUE+Element 前端应用开发(17)--- 菜单管理

循序渐进VUE+Element 前端应用开发(18)--- 功能点管理及权限控制

VUE+Element 前端应用开发框架功能介绍

循序渐进VUE+Element 前端应用开发(19)--- 后端查询接口和Vue前端的整合

使用代码生成工具快速生成基于ABP框架的Vue+Element的前端界面

循序渐进VUE+Element 前端应用开发(20)--- 使用组件封装简化界面代码

循序渐进VUE+Element 前端应用开发(21)--- 省市区县联动处理的组件使用

循序渐进VUE+Element 前端应用开发(22)--- 简化main.js处理代码,抽取过滤器、全局界面函数、组件注册等处理逻辑到不同的文件中

循序渐进VUE+Element 前端应用开发(23)--- 基于ABP实现前后端的附件上传,图片或者附件展示管理

循序渐进VUE+Element 前端应用开发(24)--- 修改密码的前端界面和ABP后端设置处理

循序渐进VUE+Element 前端应用开发(25)--- 各种界面组件的使用(1)

循序渐进VUE+Element 前端应用开发(26)--- 各种界面组件的使用(2)

电商商品数据库的设计和功能界面的处理

循序渐进VUE+Element 前端应用开发(27)--- 数据表的动态表单设计和数据存储

循序渐进VUE+Element 前端应用开发(28)--- 附件内容的管理

循序渐进VUE+Element 前端应用开发(29)--- 高级查询条件的界面设计

部署基于.netcore5.0的ABP框架后台Api服务端,以及使用Nginx部署Vue+Element前端应用

循序渐进VUE+Element 前端应用开发(30)--- ABP后端和Vue+Element前端结合的分页排序处理

循序渐进VUE+Element 前端应用开发(31)--- 系统的日志管理,包括登录日志、接口访问日志、实体变化历史日志

循序渐进VUE+Element 前端应用开发(32)--- 手机短信动态码登陆处理

循序渐进VUE+Element 前端应用开发(33)--- 邮件参数配置和模板邮件发送处理

使用代码生成工具快速开发ABP框架项目

使用Vue-TreeSelect组件实现公司-部门-人员级联下拉列表的处理

使用Vue-TreeSelect组件的时候,用watch变量方式解决弹出编辑对话框界面无法触发更新的问题