来源: UNIAPP实战项目笔记38 购物车的添加商品到购物车功能_uniapp加入购物车数量-CSDN博客
UNIAPP实战项目笔记38 购物车的加入购物车功能
通过mapGetters实现此功能
在 shopcart.vue中
使用mapGetters中的 addShopCart方法实现商品数量的增加核心代码 detail.vue
<template>
<view class=”details”>
<!– 商品图 –>
<swiper :indicator-dots=”true” :autoplay=”true” :interval=”3000″ :duration=”1000″>
<swiper-item>
<view class=”swiper-item”>
<image class=”swiper-img” :src=”goodsContent.imgUrl” mode=””></image>
</view>
</swiper-item>
</swiper>
<!– 价格和名称 –>
<view class=”details-goods”>
<view class=”goods-pprice”>¥{{goodsContent.pprice}} </view>
<view class=”goods-oprice”>¥{{goodsContent.oprice}} </view>
<view class=”goods-name”>{{goodsContent.name}} </view>
</view>
<!– 商品详情图 –>
<view class=””>
<view class=””><image class=”details-img” src=”../../static/img/b3.jpg” mode=””></image></view>
<view class=””><image class=”details-img” src=”../../static/img/b3.jpg” mode=””></image></view>
<view class=””><image class=”details-img” src=”../../static/img/b3.jpg” mode=””></image></view>
<view class=””><image class=”details-img” src=”../../static/img/b3.jpg” mode=””></image></view>
<view class=””><image class=”details-img” src=”../../static/img/b3.jpg” mode=””></image></view>
<view class=””><image class=”details-img” src=”../../static/img/b3.jpg” mode=””></image></view>
</view><!– 商品列表 –>
<Card cardTitle=”看了又看”></Card>
<CommodityList :dataList=”dataList”></CommodityList><!– 底部 –>
<view class=”details-foot”>
<view class=”iconfont icon-xiaoxi”></view>
<view class=”iconfont icon-31gouwuche” @tap=”goShopCart”></view>
<view class=”add-shopcart” @tap=”showPop”>加入购物车</view>
<view class=”purchase” @tap=”showPop”>立刻购买</view>
</view><!– 底部弹出层 –>
<view class=”pop” v-show=”isShow” @touchmove.stop=””>
<!– 蒙层 –>
<view class=”pop-mask” @tap=”hidePop”> </view>
<!– 内容块 –>
<view class=”pop-box” :animation=”animationData”>
<view class=””>
<image class=”pop-img” :src=”goodsContent.imgUrl” mode=””></image>
</view>
<view class=”pop-num”>
<view class=””>购买数量</view>
<UniNumberBox
:min=1
:value=”num”
@change=”changeNumber”
></UniNumberBox>
</view>
<view class=”pop-sub” @tap=”addCart”>确定</view>
</view>
</view>
</view>
</template><script>
import $http from ‘@/common/api/request.js’
import Card from ‘@/components/common/Card.vue’;
import CommodityList from ‘@/components/common/CommodityList.vue’;
import UniNumberBox from ‘@/components/uni/uni-number-box/uni-number-box.vue’;
import {mapMutations} from ‘vuex’
export default {
data() {
return {
isShow:false,
goodsContent:{},
animationData:{},
num:1,
swiperList:[
{imgUrl:”../../static/img/b3.jpg”},
{imgUrl:”../../static/img/b3.jpg”},
{imgUrl:”../../static/img/b3.jpg”}
],
dataList:[{
id:1,
imgUrl:”../../static/logo.png”,
name:”迪奥绒毛大衣,今年必抢,错过瞬时不爽了,爆款疯狂销售”,
pprice:”299″,
oprice:”659″,
discount:”5.2″
},
{
id:2,
imgUrl:”../../static/logo.png”,
name:”迪奥绒毛大衣,今年必抢,错过瞬时不爽了,爆款疯狂销售”,
pprice:”299″,
oprice:”659″,
discount:”5.2″
},{
id:3,
imgUrl:”../../static/logo.png”,
name:”迪奥绒毛大衣,今年必抢,错过瞬时不爽了,爆款疯狂销售”,
pprice:”299″,
oprice:”659″,
discount:”5.2″
}]
};
},
components:{
Card,
CommodityList,
UniNumberBox
},
onLoad(e) {
// console.log(e.id);
// 设置默认id=1
if(!e.id)
e.id = 1;
this.getData(e.id);
},
// 修改返回默认行为
onBackPress(){
if (this.isShow) {
this.hidePop();
return true;
}},
// 点击分享
onNavigationBarButtonTap(e) {
if(e.type===’share’){
uni.share({
provider:”weixin”,
type:0,
scene:”WXSceneSession”,
title:this.goodsContent.name,
href:”http://127.0.0.1:8080/#/pages/details/details?id=”+this.goodsContent.id,
imageUrl:this.goodsContent.imageUrl,
success:function(res){
uni.showTabBar({
title:”分享成功”
})
},
fail: (err) => {
console.log(“fail:”+ JSON.stringify(err));
}
})
}
},
methods:{
…mapMutations([‘addShopCart’]),
// 改变商品数量
changeNumber(value){
this.num = value;
},
// 请求商品
getData(id){
$http.request({
url:”/goods/id”,
data:{
id:id
}
}).then((res)=>{
this.goodsContent = res[0];
}).catch(()=>{
uni.showToast({
title:’请求失败’,
icon:’none’
})
})
},
showPop(){
// 初始化一个动画
var animation = uni.createAnimation({
duration:200 // 动画时间
});
// 定义动画内容
animation.translateY(600).step();
// 导出动画数据传递给data层
this.animationData = animation.export();
this.isShow = true;
setTimeout(()=>{
animation.translateY(0).step();
this.animationData = animation.export();
},200)
},
hidePop(){
var animation = uni.createAnimation({
duration:200
});
animation.translateY(600).step();
this.animationData = animation.export();
this.isShow = true;
setTimeout(()=>{
animation.translateY(0).step();
this.isShow = false;
},200)
},
// 跳转到购物车页面
goShopCart(){
uni.switchTab({
url:’../shopcart/shopcart’
})
},
// 加入购物车
addCart(){
let goods = this.goodsContent;
this.goodsContent[‘checked’] = false;
this.goodsContent[‘num’] = this.num;
// 加入购物车
this.addShopCart(goods);
// 隐藏弹出框
this.hidePop();
// 提示信息
uni.showToast({
title:”成功加入购物车”,
icon:”none”
})
},
}
}
</script><style lang=”scss”>
swiper{
width: 100%;
height: 700rpx;
}
.swiper-img{
width: 100%;
height: 700rpx;
}
.details{
padding-bottom: 90rpx;
}
.details-goods{
text-align: center;
font-weight: bold;
font-size: 36rpx;
padding: 10rpx 0;
}
.details-img{
width: 100%;
}
.details-foot{
position: fixed;
left: 0;
bottom: 0;
width: 100%;
height: 90rpx;
display: flex;
align-items: center;
justify-content: center;
background-color: #FFFFFF;
}
.details-foot .iconfont{
width: 60rpx;
height: 60rpx;
border-radius: 100%;
background-color: #000000;
color: #FFFFFF;
text-align: center;
margin: 0 10rpx;
line-height: 60rpx;
}
.add-shopcart{
margin: 0 40rpx;
padding: 6rpx 30rpx;
background-color: #000000;
color: #FFFFFF;
border-radius: 40rpx;}
.purchase{
margin: 0 40rpx;
padding: 6rpx 30rpx;
background-color: #49BDFB;
color: #FFFFFF;
border-radius: 40rpx;
}
.pop{
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
z-index: 9999;
}
.pop-mask{
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.3);
}
.pop-box{
position: absolute;
left: 0;
bottom: 0;
width: 100%;
background-color: #FFFFFF;
}
.pop-img{
width: 260rpx;
height: 260rpx;
top:-130rpx;
border-radius:20rpx 20rpx 0 0;
margin: 30rpx;
}
.pop-sub{
line-height: 80rpx;
background-color: #49BDFB;
color: #FFFFFF;
text-align: center;
}
.pop-num{
padding: 20rpx;
display: flex;
justify-content: space-between;
}</style>
核心代码 cart.js部分
export default{
state:{
list:[
/* {
id:1,
name:”332经济法能聚聚会技能大赛 经济法能聚聚会技能大赛”,
color:”颜色:嘿嘿嘿激活”,
imgUrl:”../../static/logo.png”,
pprice:”27″,
num:1,
checked:false
},{
id:2,
name:”032经济法能聚聚会技能大赛 经济法能聚聚会技能大赛”,
color:”颜色:嘿嘿嘿激活”,
imgUrl:”../../static/logo.png”,
pprice:”48″,
num:6,
checked:false
} */
],
selectedList:[]},
getters:{
// 判断是否 全选
checkedAll(state){
return state.list.length === state.selectedList.length;
},
// 合计 结算数量
totalCount(state){
let total = {
pprice:0,
num:0
}
state.list.forEach(v=>{
// 是否选中
if(state.selectedList.indexOf(v.id) > -1){
// 合计
total.pprice += v.pprice*v.num;
// 结算数量
total.num = state.selectedList.length;
}
})return total;
}
},
mutations:{
// 全选
checkAll(state){
state.selectedList = state.list.map(v=>{
v.checked = true;
return v.id;
})
},
// 全不选
unCheckAll(state){
state.list.forEach(v=>{
v.checked = false;
})
state.selectedList = [];
},
// 单选
selectedItem(state,index){
let id = state.list[index].id;
let i = state.selectedList.indexOf(id);
// 如果selectList已经存在就代表他之前的选中状态,checked=false,并且在selectedList删除
if (i>-1) {
state.list[index].checked = false;
return state.selectedList.splice(i,1);
}
// 如果之前没有选中,checked=true,把当前的id添加到selectedList
state.list[index].checked = true;
state.selectedList.push(id);
},
//
delGoods(state){
state.list = state.list.filter(v=>{
return state.selectedList.indexOf(v.id) === -1;
})
},
// 加入购物车
addShopCart(state, goods){
state.list.push(goods);
}
},
actions:{
checkedAllFn({commit,getters}){
getters.checkedAll ? commit(“unCheckAll”) : commit(“checkAll”)
},
delGoodsFn({commit}){
commit(‘delGoods’);
commit(“unCheckAll”);
uni.showToast({
title:’删除成功’,
icon:”none”
})
}
}
}实际案例图片 购物车的添加商品功能
目录结构
前端目录结构
manifest.json 配置文件: appid、logo…pages.json 配置文件: 导航、 tabbar、 路由
main.js vue初始化入口文件
App.vue 全局配置:样式、全局监视
static 静态资源:图片、字体图标
page 页面
index
index.vue
list
list.vue
my
my.vue
search
search.vue
search-list
search-list.vue
shopcart
shopcart.vue
details
details.vue
components 组件index
Banner.vue
Hot.vue
Icons.vue
indexSwiper.vue
Recommend.vue
Shop.vue
common
Card.vue
Commondity.vue
CommondityList.vue
Line.vue
ShopList.vue
uni
uni-number-box
uni-number-box.vue
uni-icons
uni-icons.vue
uni-nav-bar
uni-nav-bar.vue
common 公共文件:全局css文件 || 全局js文件api
request.js
common.css
uni.css
store vuex状态机文件modules
cart.js
index.js
————————————————版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/gixome/article/details/127867852
uniapp 实现保持登录状态_uniapp保持登录状态-CSDN博客
来源: uniapp 实现保持登录状态_uniapp保持登录状态-CSDN博客
import qs from ‘qs‘ qs库的使用_import qs from 'qs-CSDN博客
来源: import qs from ‘qs‘ qs库的使用_import qs from ‘qs-CSDN博客
1.npm地址
https://www.npmjs.com/package/qs
2、概述
将url中的参数转为对象;
将对象转为url参数形式
3、示例
-
import qs from ‘qs’;
-
const url = ‘method=query_SQL_dataset_data&projectId=85&appToken=7d22e38e-5717-11e7-907b-a6006ad3dba0’;
-
// 转为对象
-
console.log(qs.parse(url));
-
const a = {name:‘hehe’,age:10};
-
// 转为url参数形式
-
console.log(qs.stringify(a))
// 控制台输出
一套.NetCore+Vue+Uniapp前后端分离的低代码快速开发框架_c# 网站后端 低代码框架-CSDN博客
来源: 一套.NetCore+Vue+Uniapp前后端分离的低代码快速开发框架_c# 网站后端 低代码框架-CSDN博客
今天给大家推荐一个后端基于.NetCore+Vue前后端分离,移动端基于Uniap可发布IOS、Android、小程序、H5框架系统。
项目简介
这是一个基于.Net Core构建的简单、跨平台快速开发框架。前后端封装了上千个常用类,方便扩展;集成了代码生成器,支持前后端业务代码生成,实现快速开发,提升工作效率;框架集成了表单、表格、图表等各种常用的Demo方便直接使用;后端框架支持Vue2、Vue3。
技术架构
1、跨平台:这是基于.Net Core开发的系统,可以部署在Docker, Windows, Linux, Mac。
2、数据库支持:Microsoft SQL Server, PostgreSQL, MySQL, SQLite。
3、系统分为三端:后台管理系统、API、移动端Uniapp。
4、后端基于.Net Core + EFCore + Jwt +Dapper+signalR 开发。
5、前端基于Vue + iView + Element-ui开发。
项目结构
**后台管理系统
**
系统功能
1、低代码设计器:低代码设计器、表单配置、数据收集
2、消息推送:消息推送
3、流程管理:审批流程列表、可视化编辑流程
4、用户信息:用户管理、权限管理、角色管理、角色管理(Tree)
5、代码生成器:用户基础信息、表单设计、测试完整示例、配置管理、日志管理
6、系统设置:菜单设置、下拉框绑定设置
7、日志管理:系统日志
组件Demo
1、表单数据:基础只读表单、自动绑定下拉框、多弹出框上传、导入导出表单、编辑器表格换行、多列合并显示、在线表格编辑
2、一对多:主从一对一多个Demo、主从一对多弹出框、 从表图片上传
3、多页签/多表头:多页签、多表头
4、表单布局:单列表单、两列表单、多列表单自动数据源、Table+表单、表单只读
5、表单一对多:表单一对多table、混合表单一对多、表单+图表
6、图表表单:图表、图表表单、数字排版
7、table编辑:table编辑、api加载table数据
8、树形菜单:tree与代码生成页面、tree与自定义table数据、treetable
9、后台校验:后台参数校验
10、文件上传:文件上传、表单与上传下载
11、内容发布:编辑器、静态页面发布、静态页面列表
后台功能截图
低代码设计器
审批流程列表
可视化编辑流程
用户管理
权限管理
角色管理
代码生成器
菜单设置
下拉框绑定设置
基础只读表单
多列合并显示
在线表格编辑
主从一对一多个Demo
多页签
多表头
表单布局
表单一对多
图表表单
table编辑
树形菜单
后台校验
文件上传
编辑器
项目地址:https://github.com/cq-panda/Vue.NetCore
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/daremeself/article/details/127522233
ASP.NET Core 中 HttpContext 详解与使用 | Microsoft.AspNetCore.Http 详解 - 痴者工良 - 博客园
来源: ASP.NET Core 中 HttpContext 详解与使用 | Microsoft.AspNetCore.Http 详解 – 痴者工良 – 博客园
笔者没有学 ASP.NET,直接学 ASP.NET Core ,学完 ASP.NET Core MVC 基础后,开始学习 ASP.NET Core 的运行原理。发现应用程序有一个非常主要的 “传导体” HttpContext 。
赶忙写一下笔记先。
目录
“传导体” HttpContext
要理解 HttpContext 是干嘛的,首先,看图
图一 内网访问程序
图二 反向代理访问程序
ASP.NET Core 程序中,Kestrel 是一个基于 libuv 的跨平台 ASP.NET Core web 服务器。不清楚 Kerstrel 没关系,以后慢慢了解。
我们可以理解成,外部访问我们的程序,通过 Http 或者 Https 访问,例如 https://localhost:44337/Home/Index,需要通过一个网址,来寻向访问特定的页面。
访问页面时,会产生 Cookie、Seesion、提交表单、上传数据、身份认证等,外部与应用程序之间传导的导体就是 HttpContext。
总之,客户端跟 Web应用程序交互 是通过 HttpContext 传导的。
原理
ASP.NET Core 本质是一个控制台程序!ASP.NET Core 程序并不直接监听请求,而是通过依赖 HTTP Server ,来实现把各自请求转发到应用程序中。这个被转发的请求相当于我们日常浏览网页、上传文件、提交表单等的网络请求,这些请求会被包装,然后组合到 HttpContext 中。
就好像顾客到餐厅吃饭
- 需要先点菜、提出服务要求
- 服务员把你的菜单、需求送到厨房
- 厨师在加工好食品
- 服务员再把食品递给你
HttpContext 相当于这个服务员,她在前、后传递信息。
操作 HttpContext 前期准备
一般来说,我们主要写好Web程序,而无需理会 数据是怎么传导的。就好像两台电脑能够发送资料,我们用不着知道他们是通过无线Wifi、光纤还是铜线电缆传输的。
当有需要时,自然需要用~ 废话少说,先简单操作 HttpContext 了解下。后面接着解析这个对象。
如果你不需要练习,请直接跳过这一节内容。
- 打开 VS(2017)
- 新建项目
- ASP.NET Core Web 应用程序
- Web应用程序(模型视图控制器)
- 打开 Startup.cs ,在 ConfigureServices 中,加上
1
|
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); //先不用管这个是干嘛的 |
- 打开 HomeController.cs ,在顶部 using Microsoft.AspNetCore.Http; 把 HomeController 类替换成以下代码
public class HomeController : Controller { private IHttpContextAccessor _accessor; public HomeController(IHttpContextAccessor accessor) { _accessor = accessor; } [HttpGet] public IActionResult Index(int? id) { var httpcontext = _accessor.HttpContext; return View(httpcontext); } }
- 打开 Views/Home 目录,删除除 Index.cshtml 外的其它视图
- 把 Index.cshtml 的代码改成
@model Microsoft.AspNetCore.Http.HttpContext @{ Layout = null; }
到这里,准备已经完成。
以上代码的作用是把 HttpContext 对象 传递到 视图 中,直接在视图中使用。这样我们在理解时,只需在视图测试即可。
HttpContext 类型的属性和方法
在 ASP.NET Core 中,系统为每一个请求分配一个线程,HttpContext 针对的,就是一个线程。所以它的类、方法、属性等,都是针对当前请求起作用。
Properties(特性)
Authentication | 这个已经用不到了,这里只是列一下表。
用于身份认证(ASP.NET中用到),官方不建议在ASP.NT Core中使用。替代方案 Microsoft.AspNetCore.Authentication.AuthenticationHttpContextExtensions |
Connection | 获取有关此请求的基础连接的信息 |
Features | 获取此请求上可用的服务器和中间件提供的HTTP特性的集合 |
Items | 获取或设置可用于在该请求范围内共享数据的键/值集合 |
Request | 请求 |
RequestAborted | 通知此请求基础的连接何时中止,因此请求操作应取消 |
RequestServices | 获取或设置 IServiceProvider 集合,提供访问的请求的服务容器 |
Response | 响应 |
Session | 获取或设置用于管理此请求的用户会话数据的对象 |
TraceIdentifier | 获取或设置用于在跟踪日志中表示此请求的唯一标识符 |
User | 获取或设置此请求的用户 |
WebSockets | 获取一个对象,该对象管理此请求的WebSu套连接的建立 |
Item、Session、Response 等对象都是需要经常使用到的,下面笔者会详细实践。
HttpContext 对象实践与测试
Request
用于获取用户请求的对象,浏览器向Web程序提交表单、访问的URL、URL中包含的查询字符串、报文请求头等等。
Body | 获取或设置 RequestBody 流 |
ContentLength | 获取或设置 Content-Length 头 |
ContentType | 获取或设置Content-Type 头 |
Cookies | 获取或设置 Cookies |
Form | 获取或设置 表单内容 |
HasFormContentType | Checks the Content-Type header for form types. |
Headers | Gets the request headers. |
Host | 获取或设置主机头。可以包括端口 |
HttpContext | 获取或设置请求上下文 |
IsHttps | 检测当前是否HTTPS连接 |
Method | 获取或设置HTTP方法 |
Path | 获取或设置当前请求的路径,即URL |
PathBase | 获取或设置 RequestPathBase,就是URL前面那一段,如https://docs.microsoft.com |
Protocol | Gets or sets the RequestProtocol. |
Query | 查询字符串的集合 |
QueryString | 获取或设置用于在Request.Query中创建查询集合的原始查询字符串 |
Scheme | 获取或设置HTTP请求方案 |
试一试
打开 Index.Cshtml ,把以下代码加上去
(为了看得清楚一点,我加了表格)
<table> <tr> <td>RequestBody流</td> <td> @Model.Request.Body</td> </tr> <tr> <td>Content-Length头</td> <td>@Model.Request.ContentLength</td> </tr> <tr> <td>Content-Type头</td> <td> @Model.Request.ContentType</td> <tr> <td>Cookies </td> <td>@Model.Request.Cookies</td> </tr> <tr> <td>IsHttps</td> <td>@Model.Request.IsHttps</td> </tr> <tr> <td>Host </td> <td>@Model.Request.Host</td> </tr> </table>
运行Web程序,结果如下
在浏览器 F12 后,可以看到控制台的内容。请查看 下图的 1、3部分
Request 的其它使用方法,就不再赘述,你可以在视图中 @Model.Request. 加上需要测试的属性即可。
推荐别人关于 Request 的文章 https://www.cnblogs.com/Sea1ee/p/7240943.html
Response
Request 是 客户端向 Web 发送请求,而 Response 则是 Web 响应 客户端 的请求。这里笔者就不全部翻译了
使用Response可以直接影响服务器响应,设置响应内容、响应类型(发送网页、文件、图片等)、视图响应前重定向。
Response 应该在控制器中使用。具体使用方法笔者这里就不赘述。
Body | 获取或设置响应体流 |
ContentLength | Gets or sets the value for the Content-Type response header. |
ContentType | 获取或设置内容类型响应标头的值 |
Cookies | 获取一个对象,该对象可用于管理此响应的Cookie |
HasStarted | Gets a value indicating whether response headers have been sent to the client. |
Headers | Gets the response headers. |
HttpContext | Gets the HttpContext for this response. |
StatusCode | Gets or sets the HTTP response code. |
Response 的方法
OnCompleted(Func<Task>) |
在响应已发送到客户端之后添加要调用的委托 |
OnCompleted(Func<Object,Task>, Object) | 响应已发送到客户端之后添加要调用的委托 |
OnStarting(Func<Task>) | 在响应头将被发送到客户端之前添加要调用的委托 |
OnStarting(Func<Object,Task>, Object) | 在响应头将被发送到客户端之前添加要调用的委托 |
Redirect(String) | 向客户端返回一个临时重定向响应(HTTP 302) |
Redirect(String, Boolean) | 向客户端返回重定向响应(HTTP 301或HTTP 302) |
RegisterForDispose(IDisposable) | 处置(不可分)在请求完成处理后,注册主机处理的对象 |
Response 拓展方法
GetTypedHeaders(HttpResponse) | |
WriteAsync(HttpResponse, String, Encoding, CancellationToken) | 取消令牌使用给定的编码将给定文本写入响应体 |
WriteAsync(HttpResponse, String, CancellationToken) | 将给定文本写入响应体。UTF-8编码将被使用 |
Clear(HttpResponse) | |
SendFileAsync(HttpResponse, IFileInfo, Int64, Nullable<Int64>, CancellationToken) | 使用Sendfile 扩展发送给定的文件 |
SendFileAsync(HttpResponse, IFileInfo, CancellationToken) | Sends the given file using the SendFile extension. |
SendFileAsync(HttpResponse, String, Int64, Nullable<Int64>, CancellationToken) | Sends the given file using the SendFile extension. |
SendFileAsync(HttpResponse, String, CancellationToken) | Sends the given file using the SendFile extension. |
请参考下图的第 2 部分
Item
如果你使用过 ViewData,就不难理解 HttpContext.Item
HttpContext.Item 是一个字典集合类型,具体类型为 IDictionary<TModel,TModel>。它的使用方法像 ViewData。(不要跟我说说你不知道 ViewBag、ViewData 是什么~)
打开 Index.Cshtml ,用下面代码复制替换
@model Microsoft.AspNetCore.Http.HttpContext @{ Layout = null; } @{ List<string> i = new List<string>(); i.Add("a"); i.Add("b"); i.Add("c"); i.Add("d"); i.Add("e"); i.Add("f"); i.Add("g"); i.Add("h"); i.Add("i"); Model.Items["Test"] = i; /* Model.Items 是字典类型 这里设置 键 Test 值 i ,它的值是 List<string> 类型 */ foreach(var item in Model.Items["Test"] as List<string>) //字典类型,必须先用 as 转为对应类型 { <br> @item } }
结果
可以用 HttpContext.Item 来存储当前请求的意向有用的数据。
HttpContext 的其它方法使用这里不再赘述,需要注意的是,HttpContext 是针对一个请求的而产生的。
uni-app发布 h5 与ASP .NET MVC 后台 发布 到 IIS的同一端口 并配置跨域_uniapp发布h5部署iis-CSDN博客
来源: uni-app发布 h5 与ASP .NET MVC 后台 发布 到 IIS的同一端口 并配置跨域_uniapp发布h5部署iis-CSDN博客
iis 安装URL重写
选择对应的后台项目,进行url重写
编辑【模式】部分的内容的重写规则,我这里是h5中请求的前缀是api,大家可以根据自己的前缀进行修改。
编写【操作类型】为重写,并写重写url,按照图中设置即可。
uni-app web发布配置
uni-app项目打包成H5部署到服务器(超详细步骤)_uni-app unpackage-CSDN博客
来源: uni-app项目打包成H5部署到服务器(超详细步骤)_uni-app unpackage-CSDN博客
咳咳, 第一次写博客, 有点激动, 昨天get了一个新技能, 也是我之前一直最不懂的一块, 就是项目做完后如何部署到服务器,昨天尝试了部署uni-app项目到测试服务器, 成功之后很开心, 后面又自己上网学了下怎么部署vue项目, 所以这篇会讲怎么部署uni-app项目, 下一篇讲怎么部署vue项目, 好, 上操作
一 ,打包项目
HBuilderX下载地址: https://www.dcloud.io/hbuilderx.html
1 , HBuilderX打开你的uni-app项目 — > 点发行 – > 网站 – PC Web或手机H5
2 , 点完之后会弹出一个框, 填写网站标题和和域名之后点发行
3 , 点完之后控制台会显示正在编译中… , 稍等一会
4, 打包成功 , 生成了unpackage文件夹, 打包好的文件存放在里面
二 , 用一个工具, 把打包好的文件上传到你的服务器 , 我用的是Xftp , 用别的工具也可以
Xftp下载链接:https://pan.baidu.com/s/1z7-AYhinrWR3qIuZTkk0XQ
提取码:cn6m
1, 打开Xftp , 点小窗口的新建
2 , 点完之后弹出这个框, 填写完信息之后点连接 , 服务器的相关信息问公司的人要
3 , 点完连接之后 , 弹出这个框 , 点接受并保存
4 , 然后会看到右侧出现了新建会话的窗口 , 表示连接到服务器了,
左侧是你自己的电脑本地 , 右侧是服务器电脑
左侧进到刚刚打包好的文件存放路径 , unpackage/dist/build/h5 , 把static文件夹和index.html 复制到右侧服务器中部署的文件夹里 , 看你们是部署到哪个文件夹就拖到哪里
5 , 部署成功 !! 打开浏览器,输入服务器ip地址,访问一首页的内容吧 ^_^
地址就是 服务器ip/部署的文件夹/ , 比如 服务器ip是123.60.120.40:3006 ,部署的文件夹名字是project , 地址就是如下
http://123.60.120.40:3006/project/
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/weixin_49577940/article/details/118058899
uView 2.0版本 http请求封装_uni.$u.route-CSDN博客
来源: uView 2.0版本 http请求封装_uni.$u.route-CSDN博客
config/config.js
写入
// 此vm参数为页面的实例,可以通过它引用vuex中的变量
module.exports = (vm) => {
// 初始化请求配置
uni.$u.http.setConfig((config) => {
/* config 为默认全局配置*/
config.baseURL = ''; /* 根域名 */
return config
})
// 请求拦截
uni.$u.http.interceptors.request.use((config) => { // 可使用async await 做异步操作
// 初始化请求拦截器时,会执行此方法,此时data为undefined,赋予默认{}
config.data = config.data || {}
config.header['Content-Type'] = 'application/x-www-form-urlencoded'
// console.log(uni.getStorageSync('storage_key'))
// 根据custom参数中配置的是否需要token,添加对应的请求头
if (uni.getStorageSync('storage_key')) {
// 可以在此通过vm引用vuex中的变量,具体值在vm.$store.state中
config.header.token = uni.getStorageSync('storage_key').token.token
}
return config
}, config => { // 可使用async await 做异步操作
return Promise.reject(config)
})
// 响应拦截
uni.$u.http.interceptors.response.use((response) => {
/* 对响应成功做点什么 可使用async await 做异步操作*/
const data = response.data
console.log(response.statusCode)
// 自定义参数
// const custom = response.config?.custom
if (data.code != 1) {
// 如果没有显式定义custom的toast参数为false的话,默认对报错进行toast弹出提示
if (data.msg) {
uni.$u.toast(data.msg)
}
// // 如果需要catch返回,则进行reject
// if (custom?.catch) {
// return Promise.reject(data)
// } else {
// // 否则返回一个pending中的promise,请求不会进入catch中
// return new Promise(() => {})
// }
}
return data === undefined ? {} : data
}, (response) => {
// 对响应错误做点什么 (statusCode !== 200)
if(response.statusCode==401){
uni.$u.route('/pages/jy_Sign/jy_Sign')
}
return Promise.reject(response)
})
}
config/api.js
封装
const http = uni.$u.http
import qs from 'qs'
export const restlist = (params, config = {}) => http.post('/api/ebook/restlist', qs.stringify(params), config)
引入配置
main.js
引入
// 引入请求封装,将app参数传递到配置中
require('./config/request.js')(app)
app.$mount()
使用
import {
restlist
} from '../../config/api.js'
getdata() {
var that = this;
that.page = 1;
this.status='loading'
var data = {
page: this.page,
keywords: this.selected,
};
restlist(data).then(res => {
this.status='loadmore'
if (res.code == 1) {
that.list = res.data
} else {
uni.showToast({
title: res.msg,
icon: 'none',
duration: 2000
});
}
}).catch(err => {
})
},
getmore() {
var that = this;
that.page = that.page + 1;
that.status = 'loading';
let data = {
page: this.page,
keywords: this.selected,
};
restlist(data).then(res => {
var data = res.data;
if (!data || data.length == 0) {
that.status = 'nomore';
}
for (var i = 0; i < data.length; i++) {
that.list.push(data[i]);
}
}).catch(err => {
})
},
request
// 此vm参数为页面的实例,可以通过它引用vuex中的变量
import configs from './config.js'
import {
fungoPreviousPage
} from "@/utils/util.js"
module.exports = (vm) => {
// 初始化请求配置
uni.$u.http.setConfig((config) => {
/* config 为默认全局配置*/
config.baseURL = configs.baseUrl; /* 根域名 */
return config
})
// 请求拦截
uni.$u.http.interceptors.request.use((config) => { // 可使用async await 做异步操作
config.header['Content-Type'] = 'application/json'
config.data = config.data || {}
// 根据custom参数中配置的是否需要token,添加对应的请求头
config.header['cookie'] = uni.getStorageSync('cookie')
// if (config?.custom?.auth) {
// if (uni.getStorageSync('cookie')) {
// // 可以在此通过vm引用vuex中的变量,具体值在vm.$store.state中
// } else {
// uni.redirectTo({
// url: '/pages/login/wxlogin'
// })
// }
// }
return config
}, config => { // 可使用async await 做异步操作
return Promise.reject(config)
})
// 响应拦截
uni.$u.http.interceptors.response.use((response) => {
/* 对响应成功做点什么 可使用async await 做异步操作*/
const data = response.data
if (response.header['Set-Cookie']) {
// Set-Cookie字符串获取
var cookies = response.header['Set-Cookie']
// 字符串分割成数组
var cookieArray = cookies.split(/,(?=[^,]*=)/)
// 分号拼接数组
var newCookie = cookieArray.join(';')
uni.setStorageSync('cookie', newCookie)
}
// 自定义参数
// const custom = response.config?.custom
if (data.code != 200) {
// 如果没有显式定义custom的toast参数为false的话,默认对报错进行toast弹出提示
if (data.message) {
uni.$u.toast(data.message)
}
if (data.code == 401) {
// uni.clearStorageSync()
fungoPreviousPage()
uni.$u.route('/pages/login/wxlogin')
}
if (data.code == 602) {
// uni.clearStorageSync()
fungoPreviousPage()
uni.$u.route('/pages/login/wxlogin')
}
}
return data === undefined ? {} : data
}, (response) => {
// 对响应错误做点什么 (statusCode !== 200)
if (response.statusCode == 401) {
fungoPreviousPage()
uni.$u.route('/pages/login/wxlogin')
}
return Promise.reject(response)
})
}
uni-app.02.提交form表单的两种方式_uniapp form表单提交-CSDN博客
来源: uni-app.02.提交form表单的两种方式_uniapp form表单提交-CSDN博客
uni-app的H5版使用注意事项 - DCloud问答
来源: uni-app的H5版使用注意事项 – DCloud问答
uni-app x的web版运行注意事项,另见https://doc.dcloud.net.cn/uni-app-x/web/
HBuilderX 1.2开始包含了uni-app的web平台支持。
使用方式
- 打开uni-app项目下的vue文件
- 点击菜单 运行->运行到浏览器->Chrome
- 在Chrome內打开调试模式(右键->检查)开启设备模拟,模拟移动设备(如果UI变形刷新即可)
- HBuilderX修改代码后会自动刷新chrome的页面
- 审查元素
在chrome控制台安装vue devtools后可查看节点关系。
安装方式自行搜索。
每个页面都在page节点下,pageHead是微信和app下的原生导航栏,即pages.json里配的导航栏。
pageBody是导航栏下的页面内容。
所有标签为了避免和标准H5标签冲突,都加了U前缀。 - 断点Debug
点chrome控制台的source,可以给js打断点调试。
找到同名的文件,如果没有同名vue文件,一般会有一个同文件名的js文件,此时会提示检测到sourcemap,是否引入,点允许。然后就会有同名的vue文件。如果找不到,则把焦点放到source的代码区,然后敲ctrl+p打开文件查找窗口,然后敲入vue页面名字,然后打开vue页面。
这个vue里,只有js,没有tag和css,但可以打断点调试。
发布方式
- 配置发行后的路径(发行在网站根目录可不配置),比如发行网站路径是www.xxx.com/html5,在manifest.json可视化界面 – H5配置 – 运行的基础路径中设置,也可以在源码视图内编辑h5节点,router下增加base属性为html5。
可视化界面设置:源码视图设置:
- 点击菜单 发行->H5
- 在当下项目下的unpackage/dist/build/h5目录找到出的资源,部署服务器(或者使用本地服务器预览,不要直接在浏览器打开html文件)。如果发布使用的history模式,需要服务端配合,参考:后端配置方式
跨端注意
uni-app由uni的通用api和平台专有api组成,H5版也不例外。可以使用uni的通用api完成很多工作,也可以在条件编译里调用H5版的浏览器专有api。
虽然dom、window都可以用了,但如果要跨端,还是少写这样的代码好。
H5仍应该使用pages.json管理页面,强烈不建议使用浏览器的跳转页面的api。
H5的条件编译写法是把之前的app-plus换成H5。敲ifdef会有代码助手提示。
复制代码//#ifdef H5
this.titleHeight = 44
//#endif
条件编译目前有7个平台,APP-PLUS、APP-PLUS-NVUE、MP-WEIXIN、H5、MP、MP-BAIDU、MP-ALIPAY。
其中APP-PLUS-NVUE是APP-PLUS的子集,用于weex下单独写专用代码。
为了方便多平台选择,还引入了~#ifndef~,也就是ifdef的not,反向选择。以及或语法,及||。这些命名都是c语言条件编译的标准命名。
复制代码// #ifndef H5
console.log("这段代码编译到非H5平台");
// #endif
开发者之前为微信或app写的代码,H5的平台不支持时,需要注意把这些代码放到条件编译里。
经过这样的处理,之前做好的App或小程序才能正常运行到H5版里。
小程序版在UI上,尤其是导航栏上限制较多,H5在这里是参考了app,默认解析了pages.json下的app-plus的节点,实现了titleNView、buttons、下拉刷新(下拉刷新只有circle方式,因为只有这样的下拉刷新在H5版上可以保障流畅体验)
组件和API支持情况
目前的H5版,还没有100%实现uni的所有api,但大部分已经完成,具体参考uniapp文档。
第三方组件支持
- 支持mpvue组件
- 支持普通vue组件(仅H5平台)
- 支持微信小程序组件(HBuilderX2.5.0+开始支持编译到H5)
- 支持nvue
vue语法支持
H5版支持完整的vue语法,同时校验器也校验了更严格的vue语法,有些写法不规范会报警。比如data后面写对象会报警,必须写function。
注意事项(必看)
- 编译为H5版后生成的是单页应用,SPA。如果想要SEO优化,首页可以在template模板中配置keyword。二级页不支持配置。但一个更酷的方式是用uni-app直接发布一版百度小程序,搜索权重更高。
- 编译后看日志和错误,要看浏览器的控制台,而不是HBuilderX的控制台。浏览器的控制台会有错误提示。
- 网络请求(request、uploadFile、downloadFile等)在浏览器存在跨域限制(CORS、Cross-Origin),解决方案详见:https://ask.dcloud.net.cn/article/35267
- APP 和微信的原生导航栏和tabbar下,元素区域坐标是不包含原生导航栏和tabbar的。而 H5 里原生导航栏和tabbar是 div 模拟实现的,所以元素坐标会包含导航栏和tabbar的高度。为了优雅的解决多端高度定位问题,uni-app新增了2个css变量:–window-top和–window-bottom,这代表了页面的内容区域距离顶部和底部的距离。举个实例,如果你想在原生tabbar上方悬浮一个菜单,之前写bottom:0。这样的写法编译到h5后,这个菜单会和tabbar重叠,位于屏幕底部。而改为使用bottom:var(–window-bottom),则不管在app下还是在h5下,这个菜单都是悬浮在tabbar上浮的。这就避免了写条件编译代码。当然你也仍然可以使用 H5 的条件编译处理界面的不同。
- CSS內使用vh单位的时候注意100vh包含导航栏,使用时需要减去导航栏和tabBar高度,部分浏览器还包含浏览器操作栏高度,使用时请注意。
- event 对象上使用的 mpvue 独有的属性需调整(比如 event.pageY,可能需要加上44px的导航栏高度)。
- fixed定位的组件有可能遮挡框架内置UI组件,如果不希望遮挡可以分平台判断,在H5平台避开内置UI。
- 正常支持rpx。px是真实物理像素。暂不支持通过设manifest的”transformPx” : true,把px当动态单位使用。
- 使用罗盘、地理位置、加速计等相关接口需要使用https协议,本地预览(localhost)可以使用 http 协议。
- PC 端 Chrome 浏览器模拟器设备测试的时候,获取定位 API 需要连接谷歌服务器,需要翻墙。
- 组件内(页面除外)不支持onLoad生命周期。
- 为避免和内置组件冲突,自定义组件请加上前缀(但不能是u和uni)。比如可使用的自定义组件名称:my-view、m-input、we-icon,例如不可使用的自定义组件名称:u-view、uni-input。如果已有项目使用了可能造成冲突的名称,请修改名称。另外微信小程序下自定义组件名称不能以wx开头。
- 在tabBar页面,如果page高度设置为100%时,页面超出滚动会导致底部被tabbar遮挡,可在tabbar页面去掉height:100%或者改用min-height:100%。
- 编写组件时需要遵守vue的规范,之前在app端和小程序端能使用的一些不规范写法需要纠正,比如:不要修改props的值、组件最外层template节点下不允许包含多个节点。
- 开发App时,不可在H5预览后直接云打包。需在HBuilderX里点运行-选择运行到手机,真机调试无误后再打包。
- H5端 “网络不给力” 原因及解决办法:https://ask.dcloud.net.cn/article/37065。
相关
uni-app编译H5底层技术解析:https://juejin.im/post/5c1b0d715188256973244377