掌上报销单据头附件二开方案

栏目:云星空知识作者:金蝶来源:金蝶云社区发布:2024-09-23浏览:1

掌上报销单据头附件二开方案

【应用场景】

客户在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端费用报销与掌上报销单据头与单据体...
点击下载文档
确认删除?
回到顶部
客服QQ
  • 客服QQ点击这里给我发消息