2023年2月

ABP框架介绍地址:
http://www.iqidi.com/Framework/AbpIndex.htm

ABP框架文档下载地址:
http://www.iqidi.com/Framework/Abpdoc.htm

1、基于Winform的ABP快速开发架特点:

1、基于ABP框架底层基础上,完善了用户管理、组织机构管理、角色管理、菜单管理、功能管理及权限分配,日志管理、字典管理、产品管理等管理功能,可实现用户的功能及数据权限进行控制管理。
2、集成通用字典管理模块,方便对字典大类及对应字典项目数据的管理。
3、集成菜单权限控制及全局菜单的显示及禁用。
4、和我们的Winform框架一样,提供封装良好的框架组件模块,包括权限系统、字典模块、附件管理模块、自动升级、分页控件、公用类库及界面基类模块等模块。
5、通过代码生成工具Database2sharp配套工具实现业务模块的快速开发,可以快速生成框架底层代码,以及界面代码,后续进行一定的布局调整即可。
6、生成的Winform界面, 包括业务数据的分页查询,数据的Excel导入和导出的功能,操作功能的权限控制等,以及编辑查看界面的数据展示、保存和更新数据操作等功能。
7、底层基于.net core进行构建,并具有.net core 技术带来的相关优点,具有ABP框架所具有的一切优点,如基于领域驱动的开发、依赖注入、仓储模式、异常处理、日志记录、动态Web API等。

2、基于Vue + Element 的ABP快速开发架特点:

1、和基于Winform的ABP框架一样,使用同一个API后端,模块包括用户管理、组织机构管理、角色管理、菜单管理、功能管理及权限分配,日志管理、字典管理、产品管理等管理功能,可实现用户的功能及数据权限进行控制管理。
2、集成通用字典管理模块,方便对字典大类及对应字典项目数据的管理。
3、集成菜单权限控制及全局菜单的显示及禁用,系统以后端配置的动态菜单实现路由过滤,实现严格的界面权限判断处理。
4、基于Vue + Element 的ABP框架,整合了众多前端流行组件,以Element界面组件为基础,拓展很多相关组件,界面设计模块化处理。
5、框架界面美观,整合树列表、表格数据展示,以及增加、编辑、查看、删除等常规功能的功能,首页以图表等多种综合控件展示。
6、基于Vue + Element 的ABP框架,前后端严格分离,后端使用最新流行的.net core 的ABP框架作为API支撑,前端界面以模块化为指导进行开发,API封装类以类的抽象封装解决共同部分接口内容。
7、整个系统除了包括权限系统模块、字典管理模块等内核模块外,提供众多前端展示案例代码参考,框架快速开发提供高效技术支持。

ABP框架的Winform端试用地址:
http://www.iqidi.com/download/abpsetup.rar

ABP框架的动态网站地址:
http://www.iqidi.com:8000

ABP框架的Vue&Element管理后端:
http://www.iqidi.com:8000/#/login

在前面随笔《
使用uView UI+UniApp开发微信小程序
》和《
使用uView UI+UniApp开发微信小程序--判断用户是否登录并跳转
》介绍了微信小程序的常规登录处理和验证码登录处理的一些经验,本篇随笔继续介绍第三种登录方式,微信授权登录。微信授权登录是系统用户表绑定了微信小程序OpenID后,通过前端发起微信的登录授权,顺利获得微信授权后获得code,并在我们自己的服务后台接口通过code解析出用户的OpenId,然后获得对应用户的身份token信息返回给前端,前端完成登录后的跳转处理。

1、微信授权登录界面

我们前面介绍过常规的账号密码登录处理和验证码登录处理,第三种登录方式微信授权登录也是很常见的,因此整合在系统中方便使用,界面效果如下所示。

这个界面很简单,只需要提供一个按钮触发授权登录即可。

不过,授权登录需要用户登录系统后绑定微信才能进行一键登录,否则是无法识别用户的openid,也就无法一键登录了。

一旦绑定微信,也就是建立了用户和小程序OpenId 之间的关联,也就可以实现微信授权登录了。授权绑定需要获取用户信息,因此需要发起用户授权的确认操作,如下界面所示。

如果需要取消,那么随时取消授权也可以,系统提供入口处理即可,一般在同一界面中根据状态绑定或者解绑。

以上就是大概的界面处理流程,剩下的就是我们需要编码实现相应的逻辑即可。

2、微信授权绑定和一键登录系统处理

用户授权绑定或者解绑,我们通过状态来识别提供相关的功能即可,如下界面代码所示。

<viewclass="bottom">
    <u-buttonv-if="!vuex_user.openid"shape="circle"type="success"@click="authLogin">授权绑定</u-button>
    <u-buttonv-else shape="circle"type="error"@click="cancelBind">取消绑定</u-button>
    <u-gapheight="20"></u-gap>
    <u-buttonshape="circle"@click="gotoMyInfo">返回我的页面</u-button>
</view>

微信授权登录的绑定微信操作代码如下所示。

首先先通过uni.getUserProfile获得用户信息,返回的信息,不包括openid信息,只是一些基础的昵称,头像等信息,如下所示

不过它本身的加密信息,是我们可以通过它来在服务器后端解析出来openid的。注意,前端在正式发布后,腾信API是不在授权名单中,因此不能通过纯前端的方式解析openid,除非通过定义云函数方式调用(也就是离不开服务器)。

我们这里通过服务器端进行解析,因此服务端的域名是在授权Https列表中的

一般服务器端的接口顺利获取openid等数据,就直接resolve执行成功的回调操作

服务端直接通过jscode2session的处理即可获取对应的数据了

        public JSCode2SessionResult JSCode2Session(string appid, string secret, string js_code, string grant_type = "authorization_code")
{
var url = string.Format("https://api.weixin.qq.com/sns/jscode2session?appid={0}&secret={1}&js_code={2}&grant_type={3}", appid, secret, js_code, grant_type);var result = WeJsonHelper<JSCode2SessionResult>.ConvertJson(url);returnresult;
}

如果前端通过下面代码测试非正式的环境,也可以获得openid,正式环境下,小程序不接受https://api.weixin.qq.com的域名的。

uni.request({
url:
'https://api.weixin.qq.com/sns/jscode2session?appid=' +data.appid+ '&secret=' +data.secret+ '&js_code=' +loginRes.code+ '&grant_type=authorization_code',
success: codeRes
=>{//console.log(codeRes) resolve(codeRes.data)
},
fail: ()
=>{var tips = '获取 SesssionKey OpenId 失败'vm.$u.toast(tips)
reject(tips)
},
})

一般我们获得用户的openid等详细信息,我们可以把这些信息写入服务器的相关表中,并更新用户的openid即可

服务端接口接受用户数据,并写入或者更新小程序用户表信息,同时更新系统用户的openid即可。

一般前端顺利获得结果,就跳转用户页面即可。

	setTimeout(() => {
		uni.switchTab({ url: '/pages/task/login/myinfo' });
	}, 3000);

而取消用户绑定,只需要置空服务器端用户表对应记录的openId,并置空本地用户的openid信息即可。

cancelBind() { //取消绑定微信
    var param = {id: this.vuex_user.id}this.$u.api.User.CancelBindWechat(param).then(res=>{
console.log(res)
if(res.success){this.$u.toast('已取消绑定')this.$u.vuex('vuex_user.openid', '')this.$u.vuex('vuex_user.wechatInfo', null) //重置微信信息 this.headimg = '/static/images/wx_ico.png'}else{this.$u.toast('取消绑定失败')
}
})
},

至此,我们就完成了用户绑定和解绑微信的相关操作了。剩下的就是我们需要处理登录界面上一键登录的处理了。

登录的方法代码如下所示。

login() {
let _self
= this;//0. 显示加载的效果 uni.showLoading({ title: '登录中...'});var data ={}
_self.$u.api.User.wechatlogin(data)
//微信登陆授权 .then(res =>{
console.log(res);
uni.hideLoading();
_self.$u.toast(
'登录成功');
uni.switchTab({
url:
'/pages/task/login/myinfo'});
})
.
catch(error =>{
console.log(error);
_self.error
=error;
});
}

也就是我们封装了一个微信授权登录的逻辑,一旦成功返回,就跳转到指定页面上去即可。

其中封装的Promise函数逻辑代码如下所示

先在前端发起登录处理,获得code提交给服务器进行处理验证即可。

大概的步骤分为:

1)通过code 换取 session_key

2)从系统中判断是否存在用户

3)根据用户身份生成tokenresult

代码逻辑如下所示

以上就是微信授权绑定和一键登录系统的相关处理逻辑及代码,其实无论常规的账号密码登陆、短信验证码登陆、微信一键登录,大致的处理过程都大同小异,就是先通过直接或者间接的方式确认用户身份的有效性,然后获得用户的信息,构建返回的token令牌信息给前端使用即可。前端则负责处理结果逻辑,是提示用户还是顺利跳转到默认用户页面即可。

相关文章:

使用uView UI+UniApp开发微信小程序

使用uView UI+UniApp开发微信小程序--判断用户是否登录并跳转

在我的《
FastReport报表随笔
》介绍过各种FastReport的报表设计和使用,FastReport报表可以弹性的独立设计格式,并可以在Asp.net网站上、Winform端上使用,由于其主要是使用.net后端生成的报表方式,如果不考虑其在线设计处理,那么可以在在Vue&Element前端项目中使用,通过后端生成报表PDF文件,然后通过前端使用pdf.js来呈现报表最终效果即可。

1、使用FastReport生成自定义报表

我们通过定义自己FastReport报表格式,然后在后端生成PDF文件存储在指定目录中,并返回路径给前端,前端就可以通过pdf.js进行预览了。

关于FastReport报表的设计,这里不再赘述,主要就是通过几个简单的案例展示生成的报表逻辑。

我们来看看其中的报表生成代码,主要分为几个步骤:初始化报表、准备数据、运行并导出报表。

其中从初始化报表如下代码所示。

    #region 初始化报表
    //报表文件路径
    string reportPath = "/Report/Pres.frx";//转换为物理路径
    reportPath =System.Web.Hosting.HostingEnvironment.MapPath(reportPath);//导出PDF/jpg的文件路径
    string exportPdfPath = $"/GenerateFiles/Pres/Report_{id}" + (pdf ? ".pdf":".jpg");//转换为物理路径
    string realPath =System.Web.Hosting.HostingEnvironment.MapPath(exportPdfPath);//确保目录生成
    string parentPath =Directory.GetParent(realPath).FullName;
DirectoryUtil.AssertDirExist(parentPath);
//生成PDF报表文档到具体文件 Report report = newReport();
report.Load(reportPath);
#endregion

而初始化报表后,就需要准备相应的参数和数据等信息了,下面是测试数据代替

    dict.Add("Name", "张三");
dict.Add(
"Gender", "");
dict.Add(
"Age", 32);
dict.Add(
"Telephone", "18620292076");
dict.Add(
"CreateTime", "2019-10-13 22:30:15");
dict.Add(
"CheckDoctor", "张医生");
dict.Add(
"CheckPharmacist", "李药师");
dict.Add(
"SendUser", "王小姐");
dict.Add(
"QrCode", "http:www.iqidi.com");
dict.Add(
"BarCode", "1234567890");for (int i = 0; i < 5; i++)
{
var dr =dt.NewRow();
dr[
"ProductName"] = "阿莫西林" +i;
dr[
"Quantity"] =i;
dr[
"Unit"] = "";
dr[
"Specification"] = "一盒24粒" +i;
dr[
"HowTo"] = "口服";
dr[
"Frequency"] = "一次三次,一次一片";
dt.Rows.Add(dr);
}

准备好数据后,更新相关参数和数据源即可。

    //刷新数据源
    report.RegisterData(dt, "Detail");foreach (string key indict.Keys)
{
report.SetParameterValue(key, dict[key]);
}

然后我们根据是否PDF格式(否则为图片格式)的标志生成文件,如下所示。

    #region 运行并导出报表report.Prepare();if(pdf)
{
//导出PDF报表 var export = newPDFExport();
report.Export(export, realPath);
}
else{//导出JPG报表 var export = newImageExport();//export.JpegQuality = 392;//export.ResolutionY = 226; report.Export(export, realPath);
}
report.Dispose();
#endregion

最后返回路径给前端即可。

2、前端调用pdf.js插件生成并展示自定义报表

后端生成PDF文件后,返回路径给前端,由于是PDF文件,我们可以利用 pdf.js生成并展示自定义报表文件。

首先在前端的API调用类中构建对应的调用函数,如下所示。

然后在页面中增加对应的处理方式即可。

页面中通过按钮的触发调用这个函数,就可以呈现对应的PDF预览窗口了。

在很多项目开发中,我们为了使用方便,一般都会封装一些自定义组件来简化界面的显示处理,例如参照字典的下拉列表显示,是我们项目中经常用到的功能之一,本篇随笔介绍在Vue&Element前端项目中如何使用自定义封装的字典显示处理。

1、字典内容管理

我们在系统中,往往维护着一些系统常用到的字典信息,在我各个框架中都有一个通用的字典管理模块,对于Vue&Element前端项目,也是一样,我们也需要对它进行管理,方便前端使用。本篇随笔介绍的内容适用于ABP开发框架的Vue&Element前端,微信框架和Bootstrap框架中的Vue&Element前端内容。

字典项目一般包括项目大类,字典项的管理,字典项包括显示内容和值,两者可以不一样,也可以一样,如下所示

或者如下所示

字典大类和字典项目的表设计图如下所示。

字典大类由PID构建无穷级的树结构,而字典项目则通过Name和Value来记录显示内容和值内容。

2、后端WebAPI的数据提供

在建立相关的数据表存储数据外,我们后端也需要提供相应的Web API来给各前端提供数据处理,对于显示处理,我们定义了一个适合于Select组件和Tree组件的数据结构,主要就是提供id和label的属性,如下代码所示。

   /// <summary>
    ///通用树节点的定义/// </summary>
[Serializable]public classTreeNodeItem
{
/// <summary> ///默认构造函数/// </summary> publicTreeNodeItem()
{
}
/// <summary> ///构造函数/// </summary> public TreeNodeItem(CListItem item) :this()
{
this.Label =item.Text;this.Id =item.Value;
}
/// <summary> ///参数化构造CListItem对象/// </summary> /// <param name="label">显示的内容</param> /// <param name="id">实际的值内容</param> /// <param name="key">存储额外的键</param> public TreeNodeItem(string id, string label, string key = null) : this()
{
this.Id =id;this.Label =label;this.Key =key;
}
/// <summary> ///参数化构造CListItem对象/// </summary> /// <param name="label">显示的内容</param> /// <param name="id">实际的值内容</param> /// <param name="key">存储额外的键</param> public TreeNodeItem(int id, string label, string key = null) : this()
{
this.Id =id.ToString();this.Label =label;this.Key =key;
}
/// <summary> ///参数化构造CListItem对象/// </summary> /// <param name="text">显示的内容</param> public TreeNodeItem(string text) : this()
{
this.Id =text;this.Label =text;
}
/// <summary> ///实际值内容/// </summary> public string Id { get; set; }/// <summary> ///显示内容/// </summary> public string Label { get; set; }/// <summary> ///用于存储额外的键/// </summary> public string Key { get; set; }/// <summary> ///子节点集合/// </summary> [JsonProperty(NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling =DefaultValueHandling.Ignore)]public List<TreeNodeItem> children { get; set; }/// <summary> ///返回显示的内容/// </summary> /// <returns></returns> public override stringToString()
{
returnLabel.ToString();
}
}

有了这些数据结构,我们可以在Web API的控制器中提供相应的数据了。

而对于ABP框架后端,它们的调用方式也是类似的。

后端API执行返回的JSON数据结构如下所示。

有了这些数据,就需要在前端进行显示即可。

3、Element 前端组件显示

Vue&Element的前端,需要调用后端接口的时候,需要对API类进行一个简单的封装,这样可以方便通过类的方式进行访问后端接口。

前端界面组件中需要用到这个API调用的时候,import进来即可。

//引入API模块类方法
import dictdata from '@/api/system/dictdata'

有了提供的数据结构和API的封装,我们可以在前端进行展示了,我们来看看Element的Select组件例子代码

<template>
  <el-select v-model="value" placeholder="请选择">
    <el-option
v
-for="item in options":key="item.value":label="item.label":value="item.value"> </el-option> </el-select> </template> <script>exportdefault{
data() {
return{
options: [{
value:
'选项1',
label:
'黄金糕'}, {
value:
'选项2',
label:
'双皮奶'}, {
value:
'选项3',
label:
'蚵仔煎'}, {
value:
'选项4',
label:
'龙须面'}, {
value:
'选项5',
label:
'北京烤鸭'}],
value:
''}
}
}
</script>

如果我们每次都需要用这个原始组件来进行展示,那么就需要编写很多代码,我们希望在编写显示字典列表的代码时候,尽量减少代码,因此我们定义了字典组件,用于接收两个数据参数,一个是字典大类名称,通过字典大类名称获取字典列表,并绑定的select组件中;另一个则是标准的集合列表。

我们来看看使用的界面效果和实际代码。

而使用代码如下所示。

<el-form-itemlabel="付款方式"prop="payType">
        <my-dictdatav-model="searchForm.status"type-name="付款方式" />
</el-form-item>

上面通过type-name来声明字典大类,从而由组件逻辑实现数据源的绑定处理。

另一种方式就是绑定数据列表,通过options变量进行绑定,如下所示。

<el-form-itemlabel="表单分类"prop="category">
       <my-dictdatav-model="searchForm.category":options="FormCategorys" />
</el-form-item>

而其中这个数据源则可以在页面或者组件中实现获取即可。

4、自定义组件处理

上面介绍了如何实现自定义字典组件,那么字典组件如何自定义处理呢,我们来看看一般的处理如下。

<template>
  <el-selectv-model="keyword"filterable :clearable="clearable":multiple="multiple":disabled="disabled":placeholder="placeholder"@change="change">
    <el-optionv-for="(item, index) in dictItems":key="index":label="item.label":value="item.id">
      <spanstyle="float: left;color:yello;">
        <iclass="el-icon-tickets"style="color:blue;" />{{ item.label }}</span>
    </el-option>
  </el-select>
</template>

不过上面这种对于字符型的数据显示没问题,如果对于包含数值型的选项赋值,则会出现不匹配的问题,因此我们改进一下上面的选项处理代码,以便适应字符型和数值型的绑定值处理。

<template>
  <el-selectv-model="keyword"filterable :clearable="clearable":multiple="multiple":disabled="disabled":placeholder="placeholder"@change="change">
    <templatev-for="(item, index) in dictItems">
      <el-optionv-if="typeof(keyword)=='string'":key="index":label="item.label":value="'' + item.id" />
      <el-optionv-else-if="typeof(keyword)=='number'":key="index":label="item.label":value="('' + item.id).trim() == '' ? '' : parseInt(item.id)" />
      <el-optionv-else :key="index":label="item.label":value="item.id" />
    </template>
  </el-select>
</template>

以上代码通过判断选项绑定的值类型,从而进行相应的处理,避免数据格式不一致的问题。

而其中的字典列表,这是通过判断prop参数进行获取处理的。

加载的时候,获取数据进行显示绑定即可。

以上就是我们进行用到的字典处理过程,包括后端提供数据API、前端对API的封装,以及组件封装,然后就是界面组件的调用,这种方式极大的提高了自定义组件的使用效率,简化代码,一旦我们封装好自定义组件,使用起来非常方便。

在Vue前端界面中,自定义组件很重要,也很方便,我们一般是把一些通用的界面模块进行拆分,创建自己的自定义组件,这样操作可以大大降低页面的代码量,以及提高功能模块的开发效率,本篇随笔继续介绍在Vue&Element前端项目中如何使用自定义封装的组件,实现附件的展示场景。

1、界面模块的拆分

Vue的前端界面,对界面内容部分可以根据需要进行适当的拆分,也可以把通用的部分封装为组件进行使用,如我在随笔《
循序渐进VUE+Element 前端应用开发(16)--- 组织机构和角色管理模块的处理
》中介绍过机构或角色信息中,界面内容比较多,可以进行拆分,根据内容的展示不同,拆分为各自的组件模块,然后合并使用即可,如下所示。

在对象UML的图例中,应该是如下所示的效果图,组织机构包含组织成员和角色的内容。

在界面上,组织成员还需要添加成员的功能,同理角色也需要添加角色的处理,如下UML图示。

角色界面模块UML类图如下所示。

那么对应界面元素上,我们就应该以不同的Tab来展示这些信息,如下所示。  其中可以看到不同的Tab显示不同的内容。

完成添加成员、添加角色的界面组件后,我们就可以在组织机构界面里面引入使用。

接着加入对应的组件集合里面即可。

然后在界面部分加入对应的组件呈现代码,如下所示。

2、通用组件的拆分

前面介绍了按页面内容进行的组件封装处理,有时候,我们也可以根据特定功能的内容进行组件封装,这样我们就简化了界面的呈现处理逻辑,只需要传递一定的参数给它即可。

如我在随笔《
使用Vue-TreeSelect组件实现公司-部门-人员级联下拉列表的处理
》中对树列表功能的封装,使用起来代码就很简单,赋值相关的参数即可。

<treeselect:options="myDeptTree"v-model="addForm.dept_ID":searchable="false":default-expand-level="Infinity":open-on-click="true":open-on-focus="true"@input="updateDeptUser":normalizer="normalizer"placeholder="所属部门" />

以及在随笔《
自定义Vue&Element组件,实现用户选择和显示
》实现选择用户信息的处理组件。

选择用户的弹出界面如下所示,其中可以根据部门分类、岗位分类进行快速的查询,同时也可以根据用户名进行查询。

而如果我们这个组件通过v-modal绑定的值,如下界面代码所示

<select-post-userv-if="isEdit"ref="editForm.participant"v-model="editForm.participant" />

还有自定义字典组件的处理,在随笔《
在Vue&Element前端项目中,对于字典列表的显示处理
》中介绍。

而使用代码如下所示。

<el-form-itemlabel="付款方式"prop="payType">
        <my-dictdatav-model="searchForm.status"type-name="付款方式" />
</el-form-item>

上面通过type-name来声明字典大类,从而由组件逻辑实现数据源的绑定处理。

另一种方式就是绑定数据列表,通过options变量进行绑定,如下所示。

<el-form-itemlabel="表单分类"prop="category">
       <my-dictdatav-model="searchForm.category":options="FormCategorys" />
</el-form-item>

3、附件展示的自定义组件开发

一般情况下,我们管理的附件都是诸如图片、Excel文件、PDF文件等附件的管理。

附件表是一个综合管理这些文件的记录表,虽然附件一般是独立上传到服务器端的文件系统里面,不过也需要记录这些文件的名称、类别名称、大小、后缀名、创建时间、创建人等信息。

数据库设计表如下所示。

记录明细大概如下所示。

为了管理好这些文件信息,我们在界面提供一些条件供查询,如下是管理界面。

以上是附件的统一管理界面,我们在实际业务中很多业务表都可能用到附件信息,因此可以在业务表中增加一个字符串字段,用来存储附件组的GUID,如下所示。

上面饿AttachGUID对应FileUpload表中的AttachmentGuid,可以用来获得一个获得多个附件列表。

我们先来看看业务表单中展示附件的效果,如下所示。

由于我们要获取附件列表,输入参数应该是附件分组的AttachGUID,输出就是显示文件或者图片列表,图片方便展示即可,文件则提供连接地址下载查看等。

那么我们就需要根据这个场景来定义我们的附件展示组件了。封装好的附件展示界面组件的使用代码如下所示。

<el-form-itemlabel="附件">
  <my-attachment v-modal="viewForm.attachGUID" />
</el-form-item>

代码非常简单,就和使用其他原生组件是差不多的,而我们可以在各个模块用到附件的地方都如此展示,这样处理不仅简化了展示代码,而且不用关心太多的处理逻辑,确实很方便的。

那我们现在来看看,这个附件展示的自定义组件是如何工作的。

附件展示的代码如下所示,主要就是区分是图片,还是常规文件进行不同的展示。

<template>
  <divstyle="box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1)">
    <templatev-if="!attachFiles || attachFiles.length == 0">
      <el-empty:description="placeholder":image-size="50" />
    </template>
    <templatev-else>
      <templatev-for="(item, index) in attachFiles">
        <el-imagev-if="isImage(item)":key="index"style="padding:5px;width: 100px; height: 100px":src="getUrl(item)":preview-src-list="imageList":z-index="3000" />
        <el-linkv-else :key="index":href="getUrl(item)":underline="false"target="_blank"style="padding:5px;">
          <span>{{ item.fileName }}</span>
        </el-link>
      </template>
    </template>
  </div>
</template>

对于附件的API操作,我们引入对应的封装类如下所示。

//引入API模块类方法
import fileupload from '@/api/system/fileupload'

为了传入对应的参数值,我们定义两个prop属性,如下所示。

<script>
//引入API模块类方法
import fileupload from '@/api/system/fileupload'exportdefault{
name:
'MyAttachment', //组件的名称 props: {
placeholder: {
//空白提示 type: String,default: '暂无数据'},
value: {
//接受外部v-model传入的值 type: [String, Number],default: ''}
},

另外,对于传入的v-modal或者value值,需要监控watch它的变化,如果变化,我们触发附件的获取展示逻辑,如下代码所示。

data () {return{
attachFiles: [],
imageList: [],
//预览图片列表 keyword: this.value //选中的值 }
},
watch: {
//判断下拉框的值是否有改变 keyword (val, oldVal) {//console.log(val, oldVal) if (val !==oldVal) {//组件内对值变更后向外部发送事件通知 this.getData()this.$nextTick(() =>{this.$emit('input', val)
})
}
},
value (val, oldVal) {
//监听外部对props属性变更,如通过ResetFields()方法重置值的时候 this.keyword =val
}
},

其中数据的获取,主要是调用服务端API的处理,获得对应的附件列表,并判断是否为图片进行添加即可。

methods: {
getData () {
//console.log(this.value) if (this.value && this.value !== '') {var param = { guid: this.value }
fileupload.GetByAttachGUID(param).then(data
=>{this.attachFiles = data.result//生成并添加预览图片列表 this.imageList =[]this.attachFiles.map(item =>{if (this.isImage(item)) {var imageUrl = this.getUrl(item)this.imageList.push(imageUrl)
}
})
})
}
},

这样我们就把通用的附件管理界面,业务表附件的展示统一起来,实现了比较好的附件通用管理,和通用字典模块一样,一旦完成了组件的封装,使用起来非常简单,代码也比较容易维护了。

组件的拆分和封装,是我们前端开发中非常重要的部分,也是我们快速构建复杂页面功能的,又能轻松应对的必杀技之一。