uni app uview新增商品页(无限级分类选择和多图上传)
uni app uview新增商品页(无限级分类选择和多图上传)
给自己的牛腩商品库UNI APP加的一个新增功能,就是通用的新增页面,用的uview2(https://uviewui.com/components/intro.html),能选择无限级分类和多图上传,自已觉得这个新增页面在以后做uni app项目的时候很多地方会用到吧,先记下来了,以后用的时候直接复制粘贴代码就好了,其实那个弹出框选择分类的多少还是有点问题的,不过无伤大雅,就这样吧!!!
![](https://img2024.cnblogs.com/blog/41249/202404/41249-20240421174442348-829072381.gif)
数据库设计:商品表,商品分类表,商品图片表, 商品属性表(这里没有用到)
接口:
上传图片(用牛腩代码生成器生成的HOMEController里有相关的代码,里面加了些代码,生成固定大小的格式,上传到七牛云,)
根据父ID取分类(0为第一级分类)
根据分类ID取上级一连串分类(用于弹出分类框上面那个tabs)
新增商品
上传图片接口代码
/// <summary> /// 图片上传 /// </summary> /// <returns></returns> [HttpPost] public async Task<IActionResult> ImgUpload() { var imgFile = Request.Form.Files[0]; if (imgFile != null && !string.IsNullOrEmpty(imgFile.FileName)) { long size = 0; string tempname = ""; var filename = ContentDispositionHeaderValue .Parse(imgFile.ContentDisposition) .FileName .Trim(); var fileExt = filename.Substring(filename.LastIndexOf('.'), filename.Length - filename.LastIndexOf('.')); //扩展名,如.jpg fileExt = fileExt.Replace("\"", ""); #region 判断后缀 if (!fileExt.ToLower().Contains("jpg") && !fileExt.ToLower().Contains("png") && !fileExt.ToLower().Contains("gif")) { return Json(new { code = 1, msg = "只允许上传jpg,png,gif格式的图片.", }); } #endregion #region 判断大小 long mb = imgFile.Length / 1024 / 1024; // MB if (mb > 20) { return Json(new { code = 1, msg = "只允许上传小于 20MB 的图片.", }); } #endregion string nohouzui = "niunanproduct_" + System.Guid.NewGuid().ToString().Substring(0, 6); String newFileName = nohouzui + fileExt; var path = hostingEnv.WebRootPath; //网站静态文件目录 wwwroot //完整物理路径 string wuli_path = path + $"{Path.DirectorySeparatorChar}upload{Path.DirectorySeparatorChar}"; if (!System.IO.Directory.Exists(wuli_path)) { System.IO.Directory.CreateDirectory(wuli_path); } filename = wuli_path + newFileName; size += imgFile.Length; using (FileStream fs = System.IO.File.Create(filename)) { imgFile.CopyTo(fs); fs.Flush(); fs.Close(); } #region 生成900x900的图片,小于宽900的则不生成 int img_w = 0; int img_h = 0; Tool.GetImgWH(wuli_path + newFileName, out img_w, out img_h); string filename_upload = ""; //最后返回的文件名 string tpath = ""; if (img_w < 900 && img_h<900) { filename_upload = newFileName; tpath = wuli_path + newFileName; } else { filename_upload = nohouzui + "_900x900" + fileExt; string opath = wuli_path + newFileName; //源路径 tpath = wuli_path + filename_upload; //目标路径 Tool.CreateImage(opath, tpath, 900, 900); } #endregion #region 传到七牛云上 string endfilepath = ""; //最后返回 的JSON里的Src if (webSetting.IsUploadTo7Niu == 1) { //using Qiniu.Util; //using Qiniu.Storage; string AccessKey = "WB-23aaaaaaaSZ45kvT_20zbdk2O"; string SecretKey = "cJRT2FAbbbbbbbXTicRNJClNnu3x27nz3D"; string Bucket = "niunan-net"; Mac mac = new Mac(AccessKey, SecretKey); PutPolicy putPolicy = new PutPolicy(); putPolicy.Scope = Bucket; putPolicy.SetExpires(3600); string jstr = putPolicy.ToJsonString(); string token = Auth.CreateUploadToken(mac, jstr); Config config = new Config(); config.Zone = Zone.ZoneCnSouth; config.UseHttps = false; config.UseCdnDomains = true; FormUploader fu = new FormUploader(config); Stream s = new System.IO.FileInfo(tpath).OpenRead(); var result = await fu.UploadStream(s, filename_upload, token, null); s.Dispose(); s.Close(); log.Info($"上传图片到七牛云,文件名【{filename_upload}】,上传结果\r\n{result.Text}\r\n\r\n\r\n"); endfilepath = "http://qiniu.niunan.net/" + filename_upload; //上传完成后要把本地的文件删除掉 try { System.IO.File.Delete(tpath); log.Info($"删除本地文件成功:" + tpath); } catch (Exception ex) { log.Error($"删除本地文件失败:" + ex.Message); } } else { endfilepath = "/upload/"+filename_upload; } #endregion return Json(new { code = 0, msg = "上传成功", data = new { src = endfilepath, title = filename_upload } }); } return Json(new { code = 1, msg = "上传失败", }); }
根据父ID取分类(0为第一级分类)
public async Task<IActionResult> GetList(int pid = 0) { List<Category> list = await _carep.GetListAsync(pid); ArrayList arr = new ArrayList(); foreach (Category c in list) { arr.Add(new { categoryID = c.CategoryID, categoryName = c.CategoryName, proCount = await _carep.GetProCountAsync(c.CategoryID), //商品数量 xjCount = await _caRepository.CountAsync(a => a.ParentID == c.CategoryID),//下级数量 }); } return Json(arr); }
根据分类ID取上级一连串分类(用于弹出分类框上面那个tabs)
/// <summary> /// 反正该分类的所有上级的JSON /// 如【{categoryID:1,categoryName:编程语言,parentid:0},{categoryID:2,categoryName:JAVA,parentID:1},】 /// </summary> /// <param name="caid"></param> /// <returns></returns> public async Task<IActionResult> GetFullPath(int caid) { try { Category ca = _caRepository.FirstOrDefault(a => a.CategoryID == caid); if (ca == null) { throw new Exception("分类不存在!"); } ArrayList arr = new ArrayList(); if (!string.IsNullOrEmpty(ca.CategoryPath)) { string[] ss = ca.CategoryPath.Split(','); foreach (string s in ss) { if (string.IsNullOrEmpty(s)) { continue; } Category ca_temp = _caRepository.FirstOrDefault(a => a.CategoryID == int.Parse(s)); if (ca_temp == null) { continue; } arr.Add(new { categoryID = ca_temp.CategoryID, categoryName = ca_temp.CategoryName,parentID= ca_temp.ParentID }); } } arr.Add(new { categoryID = ca.CategoryID, categoryName = ca.CategoryName,parentID = ca.ParentID }); return Json(new { code = 0, msg = "success", data = arr }); } catch (Exception ex) { return Json(new { code = 1, msg = "出错:" + ex.Message }); } }
新增商品接口
public async Task<IActionResult> Add(Product model, string strsx, string strimg) { try { if (string.IsNullOrEmpty(model.ProductName)) { return Json(new { code = 1, msg = "名字不能为空。" }); } if (string.IsNullOrEmpty(model.ThumbnailImage)) { return Json(new { code = 1, msg = "首图不能为空。" }); } if (model.CategoryID == 0) { return Json(new { code = 1, msg = "分类不能为空。" }); } if (string.IsNullOrEmpty(model.BrandName)) { model.BrandName = "无品牌"; model.BrandID = 0; } if (string.IsNullOrEmpty(model.Unit)) { model.Unit = ""; } if (string.IsNullOrEmpty(model.Code)) { //如果条码是空则给他一个22+guid 前11位 model.Code ="22"+ Guid.NewGuid().ToString().Replace("-","").Substring(0,11); } if (websetting.IsUploadTo7Niu == 1 && !model.ThumbnailImage.StartsWith("http")) { //首图不为空,前台传过来的只是名字,还得加前缀 model.ThumbnailImage = "http://qiniu.niunan.net/" + model.ThumbnailImage; } else { if (model.ProductID==0 && !model.ThumbnailImage.StartsWith("http")) { //新增的时候传入的才只是文件名,修改时不管 model.ThumbnailImage = "/upload/" + model.ThumbnailImage; } } model.LastUpdateTime = DateTime.Now; if (model.ProductID > 0) { #region 编辑 model.Images = GetImg(strimg, model.ProductID); model.ShuXings = GetSX(strsx, model.ProductID); await _prorep.UpdateAsync(model); return Json(new { code = 0, msg = "商品编辑成功。" }); #endregion } else { #region 新增 Niunan.SPK.Models.Product pro = await _productRepository.FirstOrDefaultAsycn(a => a.Code == model.Code); if (pro != null) { throw new Exception("条码重复,数据库中已有对应条码的商品【" + pro.ProductName + "】"); } model.Images = GetImg(strimg, 0); model.ShuXings = GetSX(strsx, 0); if (await _prorep.HasSameName(model.ProductName)) { return Json(new { code = 1, msg = "已有同名商品,不能新增。" }); } model.CreatedTime = DateTime.Now; if (string.IsNullOrEmpty(model.ThumbnailImage) && model.Images.Count > 0) { model.ThumbnailImage = model.Images[0].RelativeUrl; } int proid = await _prorep.AddAsync(model); if (proid == 0) { return Json(new { code = 1, msg = "新增商品失败" }); } else { if (websetting.IsUploadTo7Niu==1) { //只有图片传到七牛云的时候才删除大图 DeleteDaTu(proid); } return Json(new { code = 0, msg = "新增商品成功,新增的商品ID:" + proid }); } #endregion } } catch (Exception ex) { return Json(new { code = 1, msg = "出错:" + ex.Message }); } } /// <summary> /// 删除服务器上的大图 /// </summary> /// <param name="proid"></param> /// <exception cref="NotImplementedException"></exception> private async void DeleteDaTu(int proid) { if (websetting.IsUploadTo7Niu==1) { //传到七牛云上的时候才删除本地磁盘的 string tou = hostingEnv.WebRootPath + "/upload/"; List<Niunan.SPK.Models.ProductImage> list = _productimageRepository.GetAllList(a => a.ProductID == proid); foreach (var item in list) { string filename = item.Comments.Replace("_900x900", ""); string fullfilename = tou + filename; if (!System.IO.File.Exists(fullfilename)) { log.Error(fullfilename + "不存在。"); continue; } try { System.IO.File.Delete(fullfilename); } catch (Exception ex) { log.Error(ex); continue; } } } } //根据字符串取产品图片集合 imgid:img^imgid2:img2... private List<ProductImage> GetImg(string strimg, int productid) { List<Niunan.SPK.Models.ProductImage> listimg = new List<ProductImage>(); if (!string.IsNullOrEmpty(strimg)) { string[] ss = strimg.Split('^'); foreach (var s in ss) { if (!string.IsNullOrEmpty(s)) { string[] ss2 = s.Split(':'); string imgpath = ss2[1]; if (websetting.IsUploadTo7Niu == 1) { if (!imgpath.StartsWith("http")) { imgpath = "http://qiniu.niunan.net/" + imgpath; } } else { imgpath = "/upload/"+imgpath; } listimg.Add(new ProductImage() { ImageID = int.Parse(ss2[0]), ProductID = productid, Comments = ss2[1], RelativeUrl = imgpath, Title = ss2[2], }); } } } return listimg; } //根据字符串取产品属性集合 sxid:name:value^sxid:name2:value2... private List<ProductShuXing> GetSX(string strsx, int productid) { List<Niunan.SPK.Models.ProductShuXing> listsx = new List<ProductShuXing>(); if (!string.IsNullOrEmpty(strsx)) { string[] ss1 = strsx.Split('^'); foreach (var s in ss1) { if (!string.IsNullOrEmpty(s)) { string[] ss2 = s.Split(':'); if (ss2.Length == 3 && !string.IsNullOrEmpty(ss2[1])) { listsx.Add(new ProductShuXing() { ShuXingID = int.Parse(ss2[0]), ProductID = productid, SXName = ss2[1], SXValue = ss2[2] }); } } } } return listsx; }
uni APP中新增商品的页面代码
<template> <view style="padding:10px;"> <u--form labelPosition="left" ref="uForm"> <u-form-item label="名称" borderBottom> <u--input v-model="product.productname" border="none"></u--input> </u-form-item> <u-form-item label="分类" borderBottom @click="showca = true;"> {{cafullname}} <u-icon slot="right" name="arrow-right"></u-icon> </u-form-item> <u-form-item label="品牌" borderBottom> <u--input v-model="product.brandname" border="none"></u--input> </u-form-item> <u-form-item label="单位" borderBottom> <u--input v-model="product.unit" border="none"></u--input> </u-form-item> <u-form-item label="单价" borderBottom> <u--input v-model="product.unitprice" type="number" border="none"></u--input> </u-form-item> <u-form-item label="条码" borderBottom> <u--input v-model="product.code" border="none"></u--input> </u-form-item> <u-form-item label="图片" borderBottom> <u-upload :fileList="fileList1" @afterRead="afterRead" @delete="deletePic" name="1" multiple :maxCount="10"></u-upload> </u-form-item> <u-form-item label="使用感受" borderBottom> <u--textarea v-model="product.usefeel" autoHeight border="none"></u--textarea> </u-form-item> </u--form> <u-button text='提交' @click="sub()" class="niunanbtn2"></u-button> <!--选择分类(无限级)--> <u-popup :show="showca"> <view> <u-row> <u-col span="10"> <u-tabs :list="tabs" keyName="categoryName" :current="currentIndex" @change="changeTabs"></u-tabs> </u-col> <u-col span="2"> <u-button text="确定" class="niunanbtn" @click="selca_ok()"></u-button> </u-col> </u-row> <u-list> <u-list-item v-for="(item, index) in calist" :key="index"> <u-cell :isLink="item.xjCount==0?false:true" @click="toxiaji(item)"> <view slot="title" class="u-slot-title"> {{item.categoryName}}(商品数:{{item.proCount}},下级:{{item.xjCount}}) </view> </u-cell> </u-list-item> </u-list> </view> </u-popup> <!--选择分类(无限级)end --> </view> </template> <script> export default { components: {}, data() { return { showca: false, product: { productid: 0, productname: '', brandname: '', categoryid: 0, unit: '', unitprice: 0, usefeel: '', code: '', strsx: '', strimg: '', ThumbnailImage: '', }, cafullname: '', tabs: [{ categoryID: 0, categoryName: "请选择" }], currentIndex: 0, calist: [{ "categoryID": 1, "categoryName": "电子产品", "proCount": 19, "xjCount": 10 }, ], fileList1: [], //图片集合 } }, onLoad() { console.log(common.apiurl, 'apiurl'); this.getcalist(0); }, methods: { sexSelect(e) { this.model1.userInfo.sex = e.name this.$refs.uForm.validateField('userInfo.sex') }, //选择商品分类 selca(caid) { uni.showToast({ title: caid + "" }) }, //取商品分类 getcalist(caid) { var that = this; var url = "http://localhost:5000/category/getlist?pid=" + caid console.log(url) uni.request({ url: url, method: 'GET', success: (res) => { that.calist = res.data } }) }, //变更分类选项卡 changeTabs(e) { console.log(e) this.getcalist(e.parentID); }, //跳转到下级分类列表 toxiaji(ca) { var that = this; console.log(JSON.stringify(ca)) var url = "http://localhost:5000/category/getfullpath?caid=" + ca.categoryID; console.log(url) uni.request({ url: url, method: 'GET', success: (res) => { if (res.data.code == 0) { that.tabs = res.data.data; if (ca.xjCount != 0) { //有下级,新加一个选项卡, 取下级列表 that.tabs.push({ categoryID: 0, categoryName: '请选择', parentID: 0 }); console.log(JSON.stringify(that.tabs), '当前选项卡') that.currentIndex = that.tabs.length - 1; console.log(that.currentIndex, '当前选项卡索引') that.getcalist(ca.categoryID) } else { console.log(that.tabs, that.currentIndex) that.currentIndex = that.tabs.length - 1; } } else { uni.showModal({ content: '' + res.msg, showCancel: false }) } } }) }, //选择某一分类后点确定 selca_ok() { this.showca = false; this.cafullname = ''; var str = ""; var len = this.tabs.length; for (var i = 0; i < len; i++) { var oneca = this.tabs[i]; str += '/' + oneca.categoryName; if (i == len - 1) { //最后一项,赋值商品模型的分ID this.product.categoryid = oneca.categoryID; } } this.cafullname = str; }, //提交 sub() { var that = this; if (that.fileList1.length == 0) { uni.showToast({ title: '请上传至少一张图片!', icon: 'none' }) return; } that.product.ThumbnailImage = that.fileList1[0].url; //默认第一张为首图 //拼接图片集合 imgid:img^imgid2:img2... 新增时ID为0 var imgs = ''; for (var i = 0; i < that.fileList1.length; i++) { var one = that.fileList1[i]; imgs += '0:' + one.url + '^'; } that.product.strimg = imgs; // uni.showModal({ // content: JSON.stringify(this.product), // showCancel: false // }) uni.showLoading({ title: "加载中..." }) var addurl = "http://localhost:5000/product/add"; uni.request({ url: addurl, method: 'POST', header: { "content-type": "application/x-www-form-urlencoded" }, data: that.product, success: (res) => { uni.hideLoading(); console.log("新增后返回:" + JSON.stringify(res)); if(res.data.code==1){ uni.showModal({ content:res.data.msg, showCancel:false }) }else { uni.showModal({ content:'新增成功', showCancel:false, success: () => { uni.switchTab({ url:'/pages/index/index' }) } }) } } }) }, // 删除图片 deletePic(event) { this[`fileList${event.name}`].splice(event.index, 1) }, // 新增图片 async afterRead(event) { // 当设置 multiple 为 true 时, file 为数组格式,否则为对象格式 let lists = [].concat(event.file) let fileListLen = this[`fileList${event.name}`].length lists.map((item) => { this[`fileList${event.name}`].push({ ...item, status: 'uploading', message: '上传中' }) }) for (let i = 0; i < lists.length; i++) { const result = await this.uploadFilePromise(lists[i].url) console.log("上传一张图片,返回:" + JSON.stringify(result)); var imgurl = result.data.src; console.log(imgurl, "imgurl"); let item = this[`fileList${event.name}`][fileListLen] this[`fileList${event.name}`].splice(fileListLen, 1, Object.assign(item, { status: 'success', message: '', url: imgurl })) fileListLen++ } }, uploadFilePromise(url) { return new Promise((resolve, reject) => { let a = uni.uploadFile({ url: 'http://localhost:5000/home/ImgUpload', // 仅为示例,非真实的接口地址 filePath: url, name: 'file', formData: { user: 'test' }, success: (res) => { var json = JSON.parse(res.data); setTimeout(() => { resolve(json) }, 1000) } }); }) }, } } </script> <style lang="scss"> .niunanbtn { width: 50px !important; height: 30px !important; background-color: darkcyan !important; color: white !important; } .niunanbtn2 { background-color: $uni-color-primary !important; color: white !important; } </style>
注:用本地 H5测试的话记得把接口那加上允许跨域的相关代码!!!
撸码:复制、粘贴,拿起键盘就是“干”!!!