掌上报销单据头附件二开方案
【应用场景】
客户在PC端单据头增加了附件字段,需要在掌上报销单据头也额外增加相关功能
【案例演示】
PC端费用报销与掌上报销单据头与单据体的映射关系:https://vip.kingdee.com/article/439370655386174720?productLineId=1&isKnowledge=2
【实现步骤】
1、 更新了多选附件(文件服务)控件的相关示例代码。
2、 本文示例代码以勾选【上传按钮】为基础来实现。如果需要勾选【自动上传】,需要自行调整UploadAttachFiles的逻辑。
掌上报销单据头附件二开步骤主要分为三步:
一、BOS设计器往掌上报销移动表单:掌上报销V3_费用报销单ER_MBReimb_ExpReimbV3里面添加附件上传控件,自行调整样式,勾选【上传按钮】
二、往PC端单据:费用报销单ER_ExpReimbursement增加附件字段,以多选附件(文件服务)为例,往单据头添加多选附件(文件服务)控件
三、需要处理:移动端上传的附件,直接保存到pc单据对应的字段中,pc端上传的单据头附件,在移动端显示。
二开掌上报销移动表单插件:掌上报销V3_费用报销单ER_MBReimb_ExpReimbV3,重写下面方法AfterBindData、AfterMobileUpload(根据移动端控件标识判断是否调用基类方法)
步骤一:单独针对移动端单据头,能够正常显示下载PC端上传的附件。
移动端绑定历史上传附件:
/// <summary> /// 文件FileId与移动端附件控件展示时用的文件名的映射(附件(数据库)专用) /// </summary> Dictionary<string, string> FieIdToDisplayFileName = new Dictionary<string, string>(); /// <summary> /// 多选附件(文件服务)存储在数据库里的文件信息 /// </summary> JSONArray Files = new JSONArray(); private SubmitType submitType; public override void AfterBindData(EventArgs e) { base.AfterBindData(e); BindValue(); } private void BindValue() { var pkValue = BillView.Model.GetPKValue(); if (pkValue.IsEmptyPrimaryKey()) { return; } string _FormId = "ER_ExpReimbursement"; // 目标单据唯一标识,即formid string _InterID = pkValue.ToString(); // 单据内码,这个ID将作为我们下载的识别标识 //string controlKey = "F_MOB_FileServer"; //单据中附件(文件服务)控件的字段名 //string controlKey = "F_MOB_FileDatabase"; //单据中附件(数据库)控件的字段名 //string controlKey = "F_MOB_ImageServer"; //单据中图片(文件服务)控件的字段名 //string controlKey = "F_MOB_ImageDatabase"; //单据中图片(数据库)控件的字段名 //string controlKey = "F_MOB_FileServerMulti"; //单据中多选附件(文件服务)控件的字段名 string controlKey = "F_MOB_FileServerMulti"; var businessInfo = ((FormMetadata)MetaDataServiceHelper.Load(this.Context, _FormId)).BusinessInfo; var dyn = BusinessDataServiceHelper.LoadSingle(this.Context, _InterID, businessInfo.GetDynamicObjectType()); if (dyn == null) return; var dynFile = dyn[controlKey]; if (dynFile == null) return; List<File> files = new List<File>(); string path = HttpContext.Current.Request.PhysicalApplicationPath + KeyConst.TEMPFILEPATH; // 获取文件上传的临时目录 // 下面是pc端不同附件字段对应的移动端显示附件的方式,可根据业务需要来选择 if (controlKey == "F_MOB_FileDatabase") //pc单据对应的控件类型为附件(数据库) { FieIdToDisplayFileName = new Dictionary<string, string>(); string fieldValue = dynFile.ToString(); //附件(数据库),实际存储的是转成base64字符串的json数组 JSONArray fileArray = fieldValue.IsEmpty() ? new JSONArray() : SerializatonUtil.DeserializeFromBase64<JSONArray>(fieldValue); foreach (JSONObject json in fileArray) { string fileId = string.Empty; string serverFileName = json.GetString("ServerFileName"); string fileName = json.GetString("FileName"); string fileType = fileName.Substring(fileName.LastIndexOf(".") + 1); //附件(数据库)储存方式,根据byte数据组保存文件到临时目录,返回文件名给前端,前端根据文件名获取数据 int length = serverFileName.IndexOf(" ") > -1 ? serverFileName.IndexOf(" ") : serverFileName.Length; fileId = serverFileName.Substring(0, length); //附件(数据库),serverFileName为fileid+空格+文件名+后缀,需要截取 fileName = CommonFunctionUtil.FilterOpSysInvalidCharacterForFileName(fileName); var displayFileName = CommonFunctionUtil.GetFileName(this.Context, fileId, fileName); //附件控件展示时用的文件名 string filePath = System.IO.Path.Combine(path, displayFileName); //输出原始文件 if (filePath == "" || !System.IO.File.Exists(filePath)) { byte[] fileBytes = json.GetValue<Byte[]>("FileContent"); if (fileBytes != null) { CreateFile(fileBytes, filePath); } } files.Add(new File() { FileID = fileId, Name = fileName, Type = fileType, IsFieldFile = true }); FieIdToDisplayFileName.Add(fileId, displayFileName); } } else if (controlKey == "F_MOB_FileServerMulti") //pc单据对应的控件类型为多选附件(文件服务) { string fieldValue = dynFile.ToString(); JSONArray fileArray = fieldValue.IsEmpty() ? new JSONArray() : JSONArray.Parse(fieldValue); Files = new JSONArray(); List<string> fileIds = new List<string>(); foreach (Dictionary<string, object> json in fileArray) { JSONObject fileObject = JSONObject.Parse(KDObjectConverter.SerializeObject(json)); Files.Add(fileObject); fileIds.Add(fileObject.GetString("ServerFileName")); } var existFiles = FileServerHelper.FindByIds(this.Context, fileIds); if (existFiles == null || !existFiles.Any()) return; foreach (JSONObject fileObject in Files) { string fileId = fileObject.GetString("ServerFileName"); string fileName = fileObject.GetString("FileName"); var file = existFiles.Find(item => item.ID == fileId); if (file == null) continue; string fileType = file.suffix; files.Add(new File() { FileID = fileId, Name = fileName, Type = fileType, IsFieldFile = true }); } } else if (controlKey == "F_MOB_FileServer" || controlKey == "F_MOB_ImageServer") //pc单据对应的控件类型为附件(文件服务)或图片(文件服务) { string fileId = dynFile.ToString(); //附件(文件服务)和图片(文件服务),实际存储的是文件服务器中的fileid var file = FileServerHelper.GetKDFileById(this.Context, fileId); if (file == null) return; string fileName = file.Name; string fileType = file.suffix; files.Add(new File() { FileID = fileId, Name = fileName, Type = fileType, IsFieldFile = true }); } else if (controlKey == "F_MOB_ImageDatabase") //pc单据对应的控件类型为图片(数据库)库 { string fileId = string.Empty; //图片(数据库),实际存储的是图片的byte数组 string fileType = ".webp"; Random random = new Random(); string fileName = string.Format("{0}{1}{2}{3}{4}", DateTime.Now.ToString("yyyyMMddHHmmssfff"), this.Context.UserId, System.Threading.Thread.CurrentThread.ManagedThreadId, random.Next(int.MaxValue), fileType); string filePath = System.IO.Path.Combine(path, fileName); //输出原始文件 if (filePath == "" || !System.IO.File.Exists(filePath)) { byte[] fileBytes = (byte[])dynFile; if (fileBytes != null) { CreateFile(fileBytes, filePath); } } files.Add(new File() { FileID = fileId, Name = fileName, Type = fileType, IsFieldFile = true }); } AccessoryData data = new AccessoryData() { FormId = _FormId, BillId = _InterID, Data = files }; // 根据不同pc端字段,给移动端对应附件控件绑定附件信息 // 可以一个pc端字段对应移动端一个附件控件 if (controlKey == "F_MOB_FileDatabase" || controlKey == "F_MOB_FileServer") { this.View.GetControl("FFileUpload").SetValue(data.ToJsonString()); } if (controlKey == "F_MOB_ImageDatabase" || controlKey == "F_MOB_ImageServer") { this.View.GetControl("FImageUpload").SetValue(data.ToJsonString()); } if (controlKey == "F_MOB_FileServerMulti") { this.View.GetControl("FFileUploadMulti").SetValue(data.ToJsonString()); } } /// <summary> /// 根据二进制文件,创建临时文件 /// </summary> private void CreateFile(byte[] fileBytes, string filePath) { try { if (!System.IO.File.Exists(filePath) && fileBytes != null && fileBytes.Length > 0) { using (System.IO.FileStream fs = new System.IO.FileStream(filePath, System.IO.FileMode.Create, System.IO.FileAccess.Write, System.IO.FileShare.Read)) using (System.IO.BinaryWriter bw = new System.IO.BinaryWriter(fs)) { bw.Write(fileBytes); } } } catch { } }
步骤二:移动端单据头上传附件,并且绑定附件信息到PC端附件字段上面去
/// <summary> /// 移动端上传附件至临时目录,并且绑定到PC端控件字段上面去 /// </summary> /// <param name="e"></param> public override void AfterMobileUpload(BOS.Mobile.PlugIn.Args.MobileUploadEventArgs e) { if (e.Key.EqualsIgnoreCase("FImageUpload") || e.Key.EqualsIgnoreCase("FFileUpload") || e.Key.EqualsIgnoreCase("FFileUploadMulti")) { var pkValue = BillView.Model.GetPKValue(); if (pkValue.IsEmptyPrimaryKey()) { // 单据不存在,需要先暂存 if (Draft(true, true)) { pkValue = BillView.Model.GetPKValue(); this.UploadAttachFiles(e, pkValue.ToString()); } } else { this.UploadAttachFiles(e, pkValue.ToString()); } } else { base.AfterMobileUpload(e); } } /// <summary> /// 上传附件 /// </summary> /// <param name="e"></param> /// <param name="pkValue">单据内码</param> private void UploadAttachFiles(BOS.Mobile.PlugIn.Args.MobileUploadEventArgs e, string pkValue) { // 获取文件上传的临时目录 string tempDirPath = HttpContext.Current.Request.PhysicalApplicationPath + KeyConst.TEMPFILEPATH; // 仅附件(数据库)、多选附件(文件服务)支持上传多个文件,其他仅支持上传一个文件 // 下面是pc端不同附件字段对应的附件上传方式,可根据业务需要来选择 submitType = SubmitType.FileServerMulti; if (submitType == SubmitType.FileDatabase) //上传附件至数据库 { StringBuilder sb = new StringBuilder(); JSONArray fileArray = new JSONArray(); foreach (FiledUploadEntity file in e.FileNameArray) { if (!file.IsSuccess) continue; // 检查文件是否上传成功 var filePath = string.Empty; // 检查文件是否存在 if (file.isNewFile) { // 新上传的文件,用控件提供的临时文件名查找文件 filePath = System.IO.Path.Combine(tempDirPath, file.FileName); if (!System.IO.File.Exists(filePath)) continue; } else { // 已有的文件,用BindValue方法中动态提供的displayFileName查找文件 string displayFileName = FieIdToDisplayFileName[file.FileId]; filePath = System.IO.Path.Combine(tempDirPath, displayFileName); if (!System.IO.File.Exists(filePath)) continue; } JSONObject fileObject = new JSONObject(); var dataBuff = System.IO.File.ReadAllBytes(filePath); // 新上传的文件需生成FileId,已有的文件用现有的FileId string fileName = file.isNewFile ? Guid.NewGuid().ToString() + " " + file.OldName : file.FileId + " " + file.OldName; fileObject.Put("ServerFileName", fileName); fileObject.Put("FileName", file.OldName); fileObject.Put("FileLength", dataBuff.Length); fileObject.Put("FileBytesLength", dataBuff.Length); fileObject.Put("FileContent", dataBuff); fileArray.Add(fileObject); } if (fileArray.Count == 0) return; this.SaveFilesToBillInfo(pkValue, SerializatonUtil.SerializeToBase64(fileArray)); sb.AppendLine(); sb.AppendLine("上传成功"); this.Model.SetValue("FFileLog", sb.ToString()); } else if (submitType == SubmitType.FileServerMulti) //多选附件上传至文件服务器 { StringBuilder sb = new StringBuilder(); JSONArray successFiles = new JSONArray(); var needUploadFiles = e.FileNameArray.Where(item => item.isNewFile).ToList(); foreach (FiledUploadEntity file in needUploadFiles) { if (!file.IsSuccess) continue; // 检查文件是否上传成功 var filePath = System.IO.Path.Combine(tempDirPath, file.FileName); // 检查文件是否存在 if (!System.IO.File.Exists(filePath)) continue; var dataBuff = System.IO.File.ReadAllBytes(filePath); var result = this.UploadAttachment(file.FileName, dataBuff); // 将数据上传至文件服务器,并返回上传结果 if (result.Success) { JSONObject fileObject = new JSONObject(); fileObject.Put("FileName", file.OldName); fileObject.Put("ServerFileName", result.FileId); fileObject.Put("FileLength", dataBuff.Length); fileObject.Put("FileBytesLength", dataBuff.Length); successFiles.Add(fileObject); } else { sb.AppendLine(string.Format("附件:{0},上传失败:{1}", file.FileName, result.Message)); // 上传失败,收集失败信息 } } if (successFiles.Count >= needUploadFiles.Count) { var oldFileIds = e.FileNameArray.Where(item => !item.isNewFile).Select(item => item.FileId).ToList(); // 只保留还在附件控件上的旧附件,避免多次上传时新附件会重复 Files.RemoveAll(item => !oldFileIds.Exists(fileId => fileId == ((JSONObject)item).GetString("ServerFileName"))); Files.AddRange(successFiles); this.SaveFilesToBillInfo(pkValue, Files.ToJSONString()); sb.AppendLine("上传成功"); } this.Model.SetValue("FFileLog", sb.ToString()); } else { string str = string.Empty; var file = e.FileNameArray[0]; if (file == null || !file.IsSuccess) str = "上传失败"; // 检查文件是否上传成功 var filePath = System.IO.Path.Combine(tempDirPath, file.FileName); if (!System.IO.File.Exists(filePath)) str = "文件不存在"; // 检查文件是否存在 var dataBuff = System.IO.File.ReadAllBytes(filePath); if (submitType == SubmitType.FileServer) //上传附件至文件服务器 { var result = this.UploadAttachment(file.FileName, dataBuff); // 将数据上传至文件服务器,并返回上传结果 if (result.Success) { this.SaveFilesToBillInfo(pkValue, result.FileId); str = "上传成功"; } else { str = string.Format("附件:{0},上传失败:{1}", file.FileName, result.Message); // 上传失败,收集失败信息 } this.Model.SetValue("FFileLog", str); } else if (submitType == SubmitType.ImageServer) //上传图片至文件服务器 { var result = this.UploadAttachment(file.FileName, dataBuff); // 将数据上传至文件服务器,并返回上传结果 if (result.Success) { this.SaveFilesToBillInfo(pkValue, result.FileId); str = "上传成功"; } else { str = string.Format("附件:{0},上传失败:{1}", file.FileName, result.Message); // 上传失败,收集失败信息 } this.Model.SetValue("FImageLog", str); } else if (submitType == SubmitType.ImageDatabase) //上传图片至数据库 { this.SaveFilesToBillInfo(pkValue, dataBuff); str = "上传成功"; this.Model.SetValue("FImageLog", str); } } } /// <summary> /// 上传至文件服务器的方法 /// </summary> /// <param name="fileName"></param> /// <param name="dataBuff"></param> /// <returns></returns> private FileUploadResult UploadAttachment(string fileName, byte[] dataBuff) { // 初始化上传下载服务 var service = new UpDownloadService(); int len = 0, less = 0; string fileId = null; byte[] buff = null; while (len < dataBuff.Length) { // 文件服务器采用分段上传,每次上传4096字节, 最后一次如果不够则上传剩余长度 less = (dataBuff.Length - len) >= 4096 ? 4096 : (dataBuff.Length - len); buff = new byte[less]; Array.Copy(dataBuff, len, buff, 0, less); len += less; using (System.IO.MemoryStream ms = new System.IO.MemoryStream(buff)) { TFileInfo tFile = new TFileInfo() { FileId = fileId, FileName = fileName, CTX = this.Context, Last = len >= dataBuff.Length,//标记是否为文件的最后一个片段 Stream = ms }; var result = submitType == SubmitType.FileServer ? service.UploadAttachment(tFile) : service.UploadImage(tFile); // 注意点:上传时fileId传入null为新文件开始,会返回一个文件的fileId,后续采用这个fileId标识均为同一文件的不同片段。 fileId = result.FileId; if (!result.Success) { return result; } } } return new FileUploadResult() { Success = true, FileId = fileId }; } /// <summary> /// 将附件信息保存至数据库 /// </summary> /// <param name="pkValue"></param> /// <param name="fieldValue"></param> private void SaveFilesToBillInfo(string pkValue, string fieldValue) { if (pkValue == null || pkValue.IsNullOrEmptyOrWhiteSpace()) { return; } StringBuilder sbSql = new StringBuilder(); // F_MOB_FileServerMulti为pc端附件控件的数据库字段,请根据需要修改 sbSql.AppendLine("update T_ER_EXPENSEREIMB set F_MOB_FileServerMulti = @FieldValue where FID = @PKValue"); List<SqlParam> sqlParams = new List<SqlParam>() { new SqlParam("PKValue", KDDbType.String, pkValue), new SqlParam("FieldValue", KDDbType.String, fieldValue) }; DBUtils.Execute(this.Context, sbSql.ToString(), sqlParams); } /// <summary> /// 将二进制附件信息保存至数据库(图片(数据库)使用) /// </summary> /// <param name="pkValue"></param> /// <param name="fieldValue"></param> private void SaveFilesToBillInfo(string pkValue, byte[] fieldValue) { if (pkValue == null || pkValue.IsNullOrEmptyOrWhiteSpace()) { return; } StringBuilder sbSql = new StringBuilder(); // FImage1为pc端附件字段的数据库字段,请根据需要修改 sbSql.AppendLine("update T_ER_EXPENSEREIMB set FIMAGE1 = @FieldValue where FID = @PKValue"); List<SqlParam> sqlParams = new List<SqlParam>() { new SqlParam("PKValue", KDDbType.String, pkValue), new SqlParam("FieldValue", KDDbType.Binary, fieldValue) }; DBUtils.Execute(this.Context, sbSql.ToString(), sqlParams); }
步骤三:
移动端删除附件:重写MobileDeleteFile方法,根据ID标识删除PC端对应附件字段数据即可。
/// <summary> /// 根据ID标识删除PC端对应附件字段数据 /// </summary> /// <param name="e"></param> public override void MobileDeleteFile(MobileDeleteFileEventArgs e) { // 以多选附件(文件服务)为例 if (e.Key.EqualsIgnoreCase("FFileUploadMulti")) { object pkValue = BillView.Model.GetPKValue(); if (e.FileNameArray.IsEmpty() || pkValue.IsEmptyPrimaryKey()) { return; } List<string> fileIds = e.FileNameArray.Select(item => item.FileID).ToList(); Files.RemoveAll(item => fileIds.Exists(deleteId => deleteId == ((JSONObject)item).GetString("ServerFileName"))); this.SaveFilesToBillInfo(pkValue.ToString(), Files.ToJSONString()); } else { base.MobileDeleteFile(e); } } 枚举类: enum SubmitType { FileDatabase, &nbs
掌上报销单据头附件二开方案
【应用场景】客户在PC端单据头增加了附件字段,需要在掌上报销单据头也额外增加相关功能【案例演示】PC端费用报销与掌上报销单据头与单据体...
点击下载文档
本文2024-09-23 03:54:16发表“云星空知识”栏目。
本文链接:https://wenku.my7c.com/article/kingdee-k3cloud-161847.html
您需要登录后才可以发表评论, 登录登录 或者 注册
最新文档
热门文章