# 项目开发使用手册-前端

日期 版本 备注
2020-12-12 0.1.0开源 基于三和中台2.0授权登录,前端开源架构版本,开发项目搭建环境说明
2020-03-03 0.1.0开源 认证接口配置增加说明
2020-03-19 0.1.0开源 增加前端组件章节

# 1、背景

前端架构是技术中台提供的中台2.0前端架构,默认与建协云融合为基础搭建,可实现建协云授权登录下开发新项目。开发者可以在该开源架构下根据个人需要做除中台2.0前端核心代码外的个性化调整,和开发新项目模块。

当前版本是第一个版本,中后期版本会将建协云相关功能模块移出,整合到建协云服务器。也就是说,项目部署的服务器必须能访问建协云服务器,否则可能无法实现与建协云融合。

当前框架使用简单,附带使用文档,熟悉该架构。本文档重点讲述如何搭建授权登录开发环境,测试、生产环境同理,不多叙述。

本文档不会讲述如何融入jenkins、docker、rancher,如需了解向技术中心索取相关文档。

# 2、开发说明

# 2.1 代码下载

网页端项目源码: framework-vue-v2.1.2-SNAPSHOT.rar

# 2.2 技术清单

  • 项目管理:npm
  • 运行环境:Nodejs
  • 项目框架:Vuejs
  • UI框架:Element-UI
  • 路由管理:Vue-Router
  • 网络请求:Axios
  • 全局状态:Vuex

# 2.3 授权登录流程

img

用户通过访问前端,前端判断用户没有登录后,跳转到建协云授权登录界面,登录成功后,建协云跳转回前端,并在url上带上参数“code”,前端将“code”传给后端,后端通过code访问建协云用户信息接口,索取用户信息,并缓存登录结果(逻辑请阅读《接入建协云-后端》),最后将用户信息返回给前端。

用户信息包含token和用户信息,详细请查阅《项目开发使用手册-后端》,或技术中台提供的相关文档。

# 2.4 基本命令

  • 下载依赖:

    npm install
    
  • 运行项目

    npm run serve
    
  • 打包编译

    // 开发环境
    npm run build-dev
    // 测试环境
    npm run build-test
    // 正式环境
    npm run build-prod
    

# 3、快速开始

# 3.1 前端组件

中台封装了一些前端组件和方法,供开发者使用:

还提供了demo实例

# 3.2 环境配置

img

.env.dev 是开发环境,开发运行项目时,自动加载该环境。

.env.test 是测试环境,发布到测试环境时,自动加载该环境。

.env.prod 是生产环境,发布到生产环境时,自动加载该环境。

开发者只需要在所需环境下配置好开发、测试、生产的属性项即可。

我们以开发环境为例。

  1. VUE_APP_PORT是建协云api访问地址,项目中登录、菜单等建协云功能都会用到,开发环境无需改变,如有变向技术中台索取,测试和生产环境同理。
  2. VUE_APP_CONCRETE_PORT是“商混后端”访问地址,因为该文档以“商混”为例子,所以这里配置的是商混后端地址。开发者可以根据自己开发的后端自行定义该参数。参数格式是VUE_APP_XXX,只要带VUE_APP_前缀即可。如:检测监管,可以是VUE_APP_JCJG_PORTVUE_APP_JCJG_ADDR等。
  3. 用户中心授权登录的client_id,向技术中台索取。如果不配置,默认使用项目默认的登录界面,使用项目默认的登录界面就要求“前端”和“后端”都接入建协云的集群了。接入建协云集群方案不讲术,有兴趣的自行咨询技术中台。
  4. VUE_APP_UC_URL是跳转授权登录界面的地址,向技术中台索取,测试和生产环境同理。

# 3.3 模块创建

项目以模块划分。模块建立在“src->views”下,如商混创建了“concrete”模块,商混模块的所有功能都在该模块下。

img

# 3.4 路由配置

开发了功能后,前端页面还不能被vue检测到,需要开发者自己将页面的路由配置好,在src->router下,创建属于自己模块的路由文件,如商混创建了“concrete.js”。

img

路由默认为:模块名 + 页面 名,() => import()进行懒加载

如下:

{
    path: '/concrete/table',
    meta: { title: '列表'},
    component: () => import('@/views/concrete/table/Index')
}

路由默认有路由守卫进行权限控制,没登陆访问菜单时会跳回登录页,访问后端没传回的菜单时,会跳405没有权限页。

  • 如果要访问没有在建协云注册菜单的路由,需要在路由里面加isMenu: false, 如下

    {
        path: '/concrete/table',
        meta: { title: '列表' , isMenu: false},
        component: () => import('@/views/concrete/table/Index')
    }
    
  • 如果要没有登录访问路由,需要在路由里面加isLogin: false, 如下

    {
        path: '/concrete/table',
        meta: { title: '列表', isLogin: false},
        component: () => import('@/views/concrete/table/Index')
    }
    

# 3.5 认证接口配置

img

src->store下的index.js中,配置ucLoginUrl参数,为开发者的认证接口。

即:

ucLoginUrl: process.env.VUE_APP_CONCRETE_PORT + "/authentication"

VUE_APP_CONCRETE_PORT是在“环境配置”章节中说到的应用后端地址。

# 3.6 后端API配置

如果我们在页面上写死请求后端的API,很容易会出现耦合,而且不方便维护。我们应当将后端API统一配置,每个模块的API都放到一个文件中,这样就能有效统一管理了。

还是以商混为例,在“src->apis”下,创建模块的api配置文件:

img

进入api配置文件,在首行引入在环境配置中的“后端访问地址”,如商混引入VUE_APP_CONCRETE_PORT,并被每一个API引用,如下图所示:

img

如果后端API来源不同访问地址,应配置多个。

# 4、iframe嵌入页面

# 4.1 嵌入组织中心、待办

1、新建iframe/Index.vue文件,其中process.env.VUE_APP_CONSTRUCTION 为系统的后端地址,/getAccessToken接口为跨应用重新获取token

<template>
  <div>
    <iframe ref="iframe" :src="url" height="740px" width="100%" frameborder="0"></iframe>
  </div>
</template>

<script>
export default {
  data() {
    return {
      url: ''
    }
  },
  watch: {
    $route: {
      handler: function (val) {
        this.getToken(val.path)
      },
      immediate: true,
    }
  },
  methods: {
    getToken (route) {
      this.$get(
        process.env.VUE_APP_CONSTRUCTION + '/getAccessToken',
        {},
        data => {
          if (route.indexOf('/iframe')  == 0) {  // 管理后台
            let path = route.replace('/iframe', 'other')
            this.url = `${process.env.VUE_APP_JXY}/${path}?code=${data.data}`
          } else if (route.indexOf('/sub/admin')  == 0) {  // 运营平台
          	let path = route.replace('/sub', '')
            this.url = `${process.env.VUE_APP_SUB_WEB}/${path}?code=${data.data}`
          } else { // 个人端
            let path = route.replace('/jxy', '')
            this.url = `${process.env.VUE_APP_PORTAL}${path}?code=${data.data}`
          }
        }
      )
    }
  }
}
</script>

2、注册路由iframe.js(Iframe对应上面的文件路径):

const Iframe = () => import('@/views/iframe/Index')
export const iframe = [
	{
      path: '/iframe/orgType',
      meta: { title: '组织维度'},
      component: Iframe
    },
    {
      path: '/iframe/orgManagenent',
      meta: { title: '组织管理'},
      component: Iframe
    },
    {
      path: '/iframe/personType',
      meta: { title: '权限管理'},
      component: Iframe
    },
    {
      path: '/iframe/userManagement',
      meta: { title: '用户管理'},
      component: Iframe
    },
    {
      path: '/iframe/chargeOrganization',
      meta: { title: '分/子组织'},
      component: Iframe
    },
    {
      path: '/iframe/applicationManagement',
      meta: { title: '应用管理'},
      component: Iframe
    },
    {
      path: '/iframe/selfMenu',
      meta: { title: '菜单管理'},
      component: Iframe
    },
    {
      path: '/iframe/selfResource',
      meta: { title: '资源管理'},
      component: Iframe
    },
     {
      path: '/iframe/meeting',
      meta: { title: '会议室管理'},
      component: Iframe
    },
    {
      path: '/iframe/roleManagement',
      meta: { title: '角色管理'},
      component: Iframe
    },
    {
      path: '/iframe/unitType',
      meta: { title: '企业类型'},
      component: Iframe
    },
    {
      path: '/iframe/unitAuth',
      meta: { title: '企业授权'},
      component: Iframe
    },
    // 个人端
    {
      path: '/jxy/xy/backlog',
      meta: { title: '待办事项'},
      component: Iframe
    },
    {
      path: '/jxy/xy/backlogProcessedList',
      meta: { title: '已办事项'},
      component: Iframe
    },
    {
      path: '/jxy/xy/waitDoneOverTimeRecord',
      meta: { title: '超时记录'},
      component: Iframe
    },
    {
      path: '/jxy/xy/waitDoneOverTimeStatistics',
      meta: { title: '超时统计'},
      component: Iframe
    },
    {
      path: '/jxy/other/common',
      meta: { title: '通用审批'},
      component: Iframe
    },
    {
      path: '/jxy/other/fileSend',
      meta: { title: '发文管理'},
      component: Iframe
    },
    {
      path: '/jxy/other/fileReceived',
      meta: { title: '收文管理'},
      component: Iframe
    },
     {
      path: '/jxy/other/noticePublish',
      meta: { title: '通知公告'},
      component: Iframe
    },
    {
      path: '/jxy/other/noticeAll',
      meta: { title: '全部公告'},
      component: Iframe
    },
    {
      path: '/jxy/other/enterpriseNotice',
      meta: { title: '公告发布'},
      component: Iframe
    },
    {
      path: '/jxy/other/platformNotice',
      meta: { title: '平台通告'},
      component: Iframe
    },
    {
      path: '/jxy/other/myConference',
      meta: { title: '我的会议'},
      component: Iframe
    },
    {
      path: '/jxy/other/meetingReserve',
      meta: { title: '会议室预定'},
      component: Iframe
    }, 
    {
      path: '/jxy/xy/attachment/publish',
      meta: { title: '发布附件'},
      component: Iframe
    },
    {
      path: '/jxy/xy/attachment/publishPlatform',
      meta: { title: '发布平台附件'},
      component: Iframe
    },
    {
      path: '/jxy/xy/attachment/all',
      meta: { title: '全部附件'},
      component: Iframe
    },
    {
      path: '/jxy/fourLibrary/index',
      meta: { title: '综合查询'},
      component: Iframe
    },
    {
      path: '/jxy/fourLibrary/projectInfo',
      meta: { title: '项目信息'},
      component: Iframe
    },
    {
      path: '/jxy/fourLibrary/singleProject',
      meta: { title: '单项工程'},
      component: Iframe
    },
    {
      path: '/jxy/fourLibrary/unitProject',
      meta: { title: '工程信息'},
      component: Iframe
    },
    {
      path: '/jxy/fourLibrary/monomerProject',
      meta: { title: '单体工程'},
      component: Iframe
    },
    {
      path: '/jxy/fourLibrary/landPlanPermission',
      meta: { title: '用地规划'},
      component: Iframe
    },
    {
      path: '/jxy/fourLibrary/workDrawingExamine',
      meta: { title: '施工图审查'},
      component: Iframe
    },
    {
      path: '/jxy/fourLibrary/projectDesign',
      meta: { title: '工程设计'},
      component: Iframe
    },
    {
      path: '/jxy/fourLibrary/projectBidding',
      meta: { title: '工程招投标'},
      component: Iframe
    },
    {
      path: '/jxy/fourLibrary/workLicence',
      meta: { title: '施工许可'},
      component: Iframe
    },
    {
      path: '/jxy/fourLibrary/enterpriseInfo',
      meta: { title: '企业信息'},
      component: Iframe
    },
    {
      path: '/jxy/fourLibrary/personalnfo',
      meta: { title: '人员信息'},
      component: Iframe
    },
    {
      path: '/jxy/fourLibraryManage/index',
      meta: { title: '综合查询'},
      component: Iframe
    },
    {
      path: '/jxy/fourLibraryManage/projectInfo',
      meta: { title: '项目信息'},
      component: Iframe
    },
    {
      path: '/jxy/fourLibraryManage/singleProject',
      meta: { title: '单项工程'},
      component: Iframe
    },
    {
      path: '/jxy/fourLibraryManage/unitProject',
      meta: { title: '工程信息完善'},
      component: Iframe
    },
    {
      path: '/jxy/fourLibraryManage/monomerProject',
      meta: { title: '单体工程'},
      component: Iframe
    },
    {
      path: '/jxy/fourLibraryManage/enterpriseInfo',
      meta: { title: '企业信息'},
      component: Iframe
    },
    {
      path: '/jxy/fourLibraryManage/personalnfo',
      meta: { title: '人员信息'},
      component: Iframe
    },
    {
      path: '/jxy/fourLibraryManage/projectPreparation',
      meta: { title: '开工前资料准备'},
      component: Iframe
    },
    // 运营平台
    {
      path: '/sub/admin/unitList',
      meta: { title: '企业列表'},
      component: Iframe
    },
    {
      path: '/sub/admin/userList',
      meta: { title: '用户列表'},
      component: Iframe
    },
  ]

3、登录企业账号在菜单管理里面配置对应的菜单和路径,如:

菜单名称:菜单管理
菜单url: /iframe/selfMenu

注意:组织中心为管理后台,待办为建协云个人端,对应的各个环境地址如下

管理后台:
    // 开发地址
    VUE_APP_PORTAL = 'http://192.168.0.220:8097'
    // 测试环境
    VUE_APP_PORTAL =  'http://zt.3hmis.com:30004'
    // 正式地址
    VUE_APP_PORTAL = 'https://e.jianxiecloud.com'

个人端:
    //开发环境
    VUE_APP_JXY = 'http://192.168.0.220:8090'
    //测试环境
    VUE_APP_JXY = 'http://zt.3hmis.com:30001'
    //生产环境
    VUE_APP_JXY = 'https://p.jianxiecloud.com'
    
运营平台:
	//开发环境
    VUE_APP_SUB_WEB = 'http://192.168.0.220:32014'
    //测试环境
    VUE_APP_SUB_WEB = 'http://zt.3hmis.com:32014'
    //生产环境
    VUE_APP_SUB_WEB = 'https://z.jianxiecloud.com'

# 4.2 抛出嵌入页

  1. 新建文件src/layout/components/OtherBlankLayout.vue代码如下, 其中process.env.VUE_APP_CONSTRUCTION业务后端地址,/grantAuthentication接口为跨应用获取用户信息以及权限

    <template>
      <div class="blank-layout" v-if="show">
        <router-view></router-view>
      </div>
    </template>
    
    <script>
    export default {
      data () {
        return {
          show: false
        }
      },
      methods: {
        getUserInfo(code) {
          this.$post(
            process.env.VUE_APP_CONSTRUCTION + '/grantAuthentication',
            {code: code || this.$store.state.token},
            data => {
            	if (code) {
                  let user = data.data
                  // 存token
                  sessionStorage.setItem('token', user.token)
                  this.$store.commit('token', user.token)
                  // 存用户信息
                  sessionStorage.setItem('user', JSON.stringify(user))
                  this.$store.commit('user', user)
                  // 重新加载按钮权限
                  Vue.prototype.$isCode = this.$getResourceCode()
    
                }
              this.show = true
            },
            error => {
              this.$message.error(error.msg)
              this.show = true
            }
          )
        }
      },
      created () {
        let code = this.$route.query.code
        if (code) { // iframe嵌入
          this.getUserInfo(code)
        } else { // 微前端
          this.getUserInfo()
        }
      }
    }
    </script>
    
    
  2. 然后把对应页面的路由注册到该布局下,例如建协云的应用管理:

    export const other =  [
      {
        path: '/other',
        meta: { title: '布局', isLogin: false},
        component: () => import('@/layout/components/OtherBlankLayout'),
        children: [
          {
            path: '/other/applicationManagement',
            meta: { title: '应用管理', isLogin: false},
            component: () => import('@/views/admin/applicationManagement/Index')
          }
       }
     ]
    
  3. 要嵌入其它系统嵌入页请参考嵌入建协云组织中心和待办

# 5、登录

# 5.1 用户中心登录

配置clent_id后会跳转用户中心默认登录页进行登录

如需配置各项目的自定义的登录页请提供如下资料并以文档形式给到技术中台,改完后会随着用户中心的下次升级而升级(升级周期1~2周),如需紧急升级请联系技术中台。

1、项目名称
2、项目systemCode
3、登录页设计图、背景图、logo

如需其他特殊配置请自行标注:
	如注册按钮、注册链接、扫描登录、页脚显示信息

# 5.2 嵌入登录页

在系统中嵌入用户中心的登录页

  • 在路由中使用已封装好的登录,配置如下

    
     {
        path: '/login',
        meta: { title: '登录', isLogin: false},
        component: SANHUI.Login
      },
    
  • 删除.env.xxx配置文件中的clientId

  • 在vuex(store -> index.js)中,配置接入中户中心的systemCode,如:

    image-20210519174826996

  • systemCode及为应用编码,在新建应用时填写

    image-20210610112004191

# 5.3 嵌入登录表单

  • 删除.env.xxx配置文件中的clientId

  • 在vuex(store -> index.js)中,配置接入中户中心的systemCode

  • 创建自己的登录页,以iframe形式嵌入,如下:

    <template>
      <div class="box">
        <iframe :src="url" width="100%" height="100%" frameborder="0"></iframe>
      </div>
    </template>
    
    <script>
    export default {
      data () {
        return {
          url: `${this.$store.state.env.VUE_APP_UC_URL}/#/loginForm?systemCode=${this.$store.state.systemCode}&target=_parent`
        }
      }
    }
    </script>
    
    <style lang="scss" scoped>
    .box{
      position: fixed;
      top: 30%;
      right: 10%;
      width: 400px;
      background: #fff;
      padding: 20px;
      height: 400px;
    }
    </style>
    

# 5.4 用户信息和token

前端登录成功后会默认把用户信息和toekn存在vuex里面,获取方法如下:

<template>
  <div>
    <p>用户信息:{{ user }}</p>
    <p>token:{{ token }}</p>
  </div>
</template>

<script>
import { mapState } from 'vuex'
export default {
  computed: {
     ...mapState(['user', 'token'])
  },
  mounted () {
  	console.log('用户信息', this.user)
  	console.log('token', this.token)
  }
}
</script>

# 5.5 退出登录

登录只会默认退出用户中心登录,各应用也需要退出则需要

src->store下的index.js中,配置logoutUrl参数,为业务端的退出认证接口。

即:

logoutUrl: process.env.VUE_APP_CONCRETE_PORT + "/authenticationLogout"

VUE_APP_CONCRETE_PORT对应的业务端后端地址。

# 6、修改密码

默认在右上角个人信息处修改密码

image-20210519110408128

也可以在任意一处调用封装方法打开修改密码弹框:

SANHUI.updatePassword()

image-20210519110539550