更新时间:2025-02-28 GMT+08:00
分享

(可选)开发自定义登录组件

“开发自定义登录组件”步骤可以直接跳过,本示例已为您提供了开发好的自定义登录组件。如果您想要自定义登录组件的开发方法,可参考本章节执行。

自定义登录组件

  1. 进入创建“设备维修管理系统”应用中创建的应用。
  2. 在左侧资产下的组件模板中,单击“widgetVue3Template”,再单击“下载”。

    图1 下载组件模板

  3. 在弹出的窗口中,输入组件名称,并单击“保存”,将组件模板保存到本地,并解压。

    图2 输入组件名称

  4. 查看解压后的组件目录。

    目录结构如图3所示,其中userLogin.js文件是写vue业务逻辑的代码,userLogin.ftl用于写html代码,userLogin.css用于写样式代码,userLogin.editor.js以及packageinfo.json是配置文件。
    图3 目录结构

  5. 在解压后的文件夹中,创建一个imgs文件夹,并放入一个登录页面的背景图片。

    假设放入的背景图片名称为“imagebg.jpg”

  6. 在本地编辑器中打开文件夹,把userLogin.editor.js文件中的propertiesConfig代码改为如下代码,用于配置桥接器。

    propertiesConfig: [
    		{
    			headerTitle: 'Parameters',
    			accordion: true,
    			accordionState: 'open',
    			config: [
    				{
    					type: 'text',
    					name: 'loginAPI',
    					label: 'login API',
    					value: '',
    				},
    			],
    		},
    		{
    			config: [
    				{
    					type: 'connectorV2',
    					name: 'FlowConnector',
    					label: 'Flow Connector',
    					model: 'ViewModel',
    				},
    				{
    					type: 'connectorV2',
    					name: 'common.GetConnector',
    					label: 'View API Get Connector',
    					model: 'ViewModel',
    				},
    				{
    					type: 'connectorV2',
    					name: 'common.PostConnector',
    					label: 'View API Post Connector',
    					model: 'ViewModel',
    				},
    				{
    					type: 'connectorV2',
    					name: 'common.PutConnector',
    					label: 'View API Put Connector',
    					model: 'ViewModel',
    				},
    				{
    					type: 'connectorV2',
    					name: 'common.DeleteConnector',
    					label: 'View API Delete Connector',
    					model: 'ViewModel',
    				},
    			],
    		},
    	],

  7. 把packageinfo.json文件中加入如下加粗内容

    {
    	"widgetApi": [
    		{
    			"name": "userLogin"
    		}
    	],
    	"widgetDescription": "",
    	"authorName": "",
    	"localFileBasePath": "",
    	"requires": [
    		{
    			"name": "global_Vue3",
    			"version": "3.3.4"
    		},
    		{
    			"name": "global_ElementPlus",
    			"version": "2.6.0"
    		},
    		{
    			"name": "global_Vue3I18n",
    			"version": "9.10.1"
    		},
    		{
    			"name": "global_Vue3Router",
    			"version": "4.3.0"
    		}
    	]
    }

  8. 将userLogin.ftl文件中的内容,替换为如下示例。

    <style>
        [v-cloak] {
            display: none
        }
    </style>
    <div v-cloak id="userLogin" class="page-login" v-cloak>
    	<div :class="backgroundClass" class="bg-box">
    		<!-- <div class="title">
    			<span>设备管理系统</span>
    		</div> -->
    		<div class="login-box">
    			<div class="login-title">用户登录</div>
    			<input name="username" type="text" style="display: none" />
    			<input name="password" type="password" style="display: none" />
    			<div class="login-form">
    				<div v-show="errorDesc" class="error-line">
    					<!-- <img :src="BasePath + 'imgs/btn_errorInfo.png'" /> -->
    					<span class="error-text" v-html="errorDesc"></span>
    				</div>
    				<div class="login-item mg-top10">
    					<!-- :placeholder="isUserName ? getTransLang('ds.commerce.storefront.web.loginname') :  getTransLang('ds.commerce.storefront.web.loginEmailOrPhone')" -->
    					<input
    						ref="accountInput"
    						v-model="account"
    						placeholder="请输入用户名"
    						@keyup.enter="validateBeforeSubmit"
    						autocomplete="off"
    						maxlength="32"
    						type="text"
    					/>
    				</div>
    				<div class="login-item mg-top10">
    					<!-- :placeholder="getTransLang('ds.commerce.storefront.web.password')" -->
    					<input
    						v-model="password"
    						@keyup.enter="validateBeforeSubmit"
    						placeholder="请输入密码"
    						autocomplete="off"
    						maxlength="32"
    						type="password"
    					/>
    				</div>
    				<div v-if="needVerify" class="login-item">
    					<!-- :placeholder="getTransLang('ds.commerce.storefront.web.verificationcode')" -->
    					<input
    						v-model="inputImgCode"
    						@keyup.enter="validateBeforeSubmit"
    						placeholder="请输入验证码"
    						autocomplete="off"
    						maxlength="10"
    						type="text"
    					/>
    					<div class="verify-code">
    						<img :src="imgCode" @click="getVerifyCode()" />
    					</div>
    				</div>
    				<div class="login-button" @click="validateBeforeSubmit">
    					登录
    				</div>
    			</div>
    		</div>
    	</div>
    </div>

  9. 将userLogin.js文件内容,替换为如下示例。

    userLogin = StudioWidgetWrapper.extend({
    	init: function () {
    		var thisObj = this
    		thisObj._super.apply(thisObj, arguments)
    		thisObj.render()
    		thisObj.initBusi()
    
    		if (typeof Studio != 'undefined' && Studio) {
    			Studio.registerEvents(thisObj, 'goHomepage', 'go Homepage', [])
    		}
    	},
    
    	render: function () {
    		var thisObj = this
            let tenantId;
                if (Studio.inReader) {
                    tenantId = STUDIO_DATA.catalogProperties["tenant-id"].value
                } else {
                    tenantId = magno.pageService.getCatalogProperties()["tenant-id"].value
                }
            HttpUtils.setCookie("tenant-id", tenantId)
            HttpUtils.setCookie("locale","zh_CN");
    		var elem = thisObj.getContainer()
    
    		if (elem) {
    			var containerDiv = $('.scfClientRenderedContainer', elem)
    			if (containerDiv.length) {
    				$(containerDiv).empty()
    			} else {
    				containerDiv = document.createElement('div')
    				containerDiv.className = 'scfClientRenderedContainer'
    				$(elem).append(containerDiv)
    			}
    		}
    
    		thisObj.sksBindItemEvent()
    		$(window).resize(function () {
    			thisObj.sksRefreshEvents()
    		})
    	},
    
    	initBusi: function () {
    		var thisObj = this
    		var widgetProperties = thisObj.getProperties()
    		var BasePath = thisObj.getWidgetBasePath() // 本地路径
    		var elem = thisObj.getContainer()
    		const app  = Vue.createApp({
    			data(){
    				return {
    					BasePath: BasePath,
    					imgCode: '',
    					account: '',
    					password: '',
    					inputImgCode: '',
    					errorDesc: '',
    					needRead: false,
    					backgroundClass: 'backgroundClass',
    					needVerify: false,
    					accountName: '',
    					isMobile: false,
    				}
    			},	
    			created() {
    				this.getVerifyCode()
    				this.checkIsLogin()
    				this.isMobile = /Android|webOS|iPhone|iPad|BlackBerry/i.test(
    					navigator.userAgent
    				)
    			},
    			watch: {
    				account(newVal, oldVal) {
    					if (newVal !== oldVal) {
    						this.password = ''
    					}
    				},
    			},
    			methods: {
    				checkIsLogin() {
    					const userInfo = JSON.parse(
    						window.sessionStorage.getItem('userInfo')
    					)
    					if (userInfo !== null && userInfo.username) {
    						setTimeout(() => {
    								thisObj.triggerEvent('goHomepage', {})
    							
    						}, 1000)
    					}
    				},
    				shouldShowImgCode() {
    					connService(
    						thisObj,
    						`${ds_baseUrl}/identities/isNeedCaptcha`,
    						{
    							username: this.account,
    						},
    						'common.PostConnector'
    					)
    						.then((res) => {
    							const { data = [], resp = {} } = res
    
    							if (resp.code !== '0') {
    								this.$message.error(resp.message)
    							}
    
    							if (data && data.length) {
    								this.needVerify = data[0].needVerify
    
    								if (!this.needVerify) {
    									this.confirmLogin()
    								}
    							}
    						})
    						.catch((e) => {
    							this.$message.error(e.response.resMsg)
    						})
    				},
    				// 登录按钮
    				validateBeforeSubmit() {
    					if (!this.password) {
    						this.errorDesc = '请输入密码。'
    						this.needVerify = true
    						return false
    					}
    					if (!this.account) {
    						this.errorDesc = '请输入用户名。'
    						this.needVerify = true
    						return false
    					}
    
    					if (!this.inputImgCode && this.needVerify) {
    						this.errorDesc = '验证码错误。'
    						return false
    					}
    
    					if (this.needVerify && this.inputImgCode) {
    						this.confirmLogin()
    						return
    					}
    					this.confirmLogin()
    				},
    
    				// 调登录接口
    				confirmLogin() {
    					var request = {
    						username: this.account,
    						password: this.password,
    						captcha: this.inputImgCode,
    					}
    					//
    					this.callFlowConn1(
                widgetProperties.loginAPI,
    						request,
    						this.callLogin,
    						this.loginFail,
    						'post'
    					)
    				},
    
    				// 登录接口成功函数
    				callLogin(response) {
    					if (response) {
                            this.errorDesc = ''
                            let userInfo = {
                                username: response.data[0].username,
                                userId: response.data[0].userId,
                                profile: response.data[0].profile,
                            }
                            HttpUtils.setCookie('isLogged', true)
                            window.sessionStorage.setItem(
                                'userInfo',
                                JSON.stringify(userInfo)
                            )
                            thisObj.triggerEvent('goHomepage', {})
    
    					}
    				},
                    // 登录失败resMsg
                    loginFail(data){
                        this.errorDesc = data.response.resMsg
                        this.getVerifyCode()
                        this.needVerify = true
                    },
    				// 获取验证码
    				getVerifyCode() {
    					this.imgCode =
    						'/u-route/baas/sys/v1.0/verificationcode?type=login&t=' +
    						Date.parse(new Date())
    				},
    				// 封装flow调用后台
    				callFlowConn1: function (service, param, callbackFunc,callbackfail, method) {
    					var thisView = this
    					let mMethod
    					switch (method) {
    						case 'get':
    							mMethod = 'common.GetConnector'
    							break
    						case 'post':
    							mMethod = 'common.PostConnector'
    							break
    						case 'put':
    							mMethod = 'common.PutConnector'
    							break
    						default:
    							mMethod = 'common.FlowConnector'
    							break
    					}
    
    					var connector = thisObj.getConnectorInstanceByName(mMethod)
    					if (connector) {
    						connector.setInputParams({
    							service: service,
    							needSchema: 'data',
    							async: false,
    						})
    						connector
    							.query(param) // 调用接口,以param为入参
    							.done(function (response) {
    								if (response.resp && response.resp.code) {
    									callbackFunc.call(thisView, response)
    								}
    							})
    							.fail(function (response) {
    								// 代表接口执行失败
    								callbackfail.call(thisView, response)
    							})
    					}
    				},
    			},
    		})
    		app.use(ElementPlus);
    		thisObj.vm = app.mount($("#userLogin", elem)[0]);
    
    	},
    })

  10. 将userLogin.css文件内容,替换为如下示例。

    需要注意的是,“background”的取值需要和5中创建的文件夹和图片名称保持一致。
    .page-login {
      /* 使用webkit内核的浏览器 */
      /* Firefox版本4-18 */
      /* Firefox版本19+ */
    }
    .page-login * {
      box-sizing: border-box;
    }
    .page-login .flex {
      display: flex;
    }
    .page-login .login_tip {
      font-size: 12px;
      color: #167aeb;
    }
    .page-login .bg-box {
      position: relative;
      display: flex;
      justify-content: center;
      width: 100%;
      height: 100%;
      background: url('imgs/imagebg.jpg') no-repeat 50%;
      background-size: cover;
    }
    .page-login .bg-box > img {
      width: 100%;
    }
    @media (min-width: 767px) {
      .page-login .bg-box .login-box {
        position: absolute;
        right: 18%;
        top: 26%;
        padding: 30px 30px 20px 30px;
        width: 380px;
        background: #fff;
        border-radius: 10px;
        box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.1);
      }
      .page-login .bg-box .login-box .login-title {
        font-size: 24px;
        color: #333;
        text-align: center;
      }
      .page-login .bg-box .login-box .login-form {
        padding-top: 20px;
      }
      .page-login .bg-box .login-box .login-form .error-line {
        display: flex;
        align-items: center;
        background: #ffebeb;
        color: #e4393c;
        border: 1px solid #faccc6;
        padding: 3px 10px 3px 10px;
        height: auto;
        text-align: left;
        font-size: 12px;
      }
      .page-login .bg-box .login-box .login-form .error-line .error-text {
        padding-left: 5px;
      }
      .page-login .bg-box .login-box .login-form .error-line .error-text .change_tab {
        color: #167aeb;
        cursor: pointer;
      }
      .page-login .bg-box .login-box .login-form .login-item {
        display: flex;
        align-items: center;
        margin-top: 20px;
      }
      .page-login .bg-box .login-box .login-form .login-item .el-input__inner {
        height: 36px;
      }
      .page-login .bg-box .login-box .login-form .login-item > input {
        flex: 1;
        padding: 5px 10px;
        height: 36px;
        border-radius: 3px;
        border: 1px solid #ddd;
      }
      .page-login .bg-box .login-box .login-form .login-item .verify-code {
        height: 36px;
        margin-left: 5px;
      }
      .page-login .bg-box .login-box .login-form .login-item .verify-code img {
        height: 100%;
      }
      .page-login .bg-box .login-box .login-form .login-item .el-checkbox {
        margin-right: 10px;
      }
      .page-login .bg-box .login-box .login-form .login-item .read-text {
        font-size: 12px;
        color: #333;
      }
      .page-login .bg-box .login-box .login-form .login-item .read-text .clickable {
        color: #167aeb;
        cursor: pointer;
      }
      .page-login .bg-box .login-box .login-form .login-item .read-text .clickable:hover {
        text-decoration: underline;
      }
      .page-login .bg-box .login-box .login-form .mg-top10 {
        margin-top: 10px;
      }
      .page-login .bg-box .login-box .login-button {
        margin-top: 20px;
        height: 40px;
        line-height: 40px;
        border-radius: 3px;
        background: #3d88ff;
        text-align: center;
        color: #fff;
        font-size: 14px;
        cursor: pointer;
      }
      .page-login .bg-box .login-box .login-button:hover {
        opacity: 0.8;
      }
      .page-login .bg-box .login-box .divide-line {
        margin-top: 20px;
        height: 1px;
        background: #eee;
        opacity: 0.8;
      }
      .page-login .bg-box .login-box .login-bottom {
        margin-top: 20px;
        justify-content: center;
        align-items: center;
      }
      .page-login .bg-box .login-box .login-bottom .bottom-text {
        font-size: 12px;
        color: #999;
      }
      .page-login .bg-box .login-box .login-bottom .type-item {
        margin-left: 10px;
        width: 30px;
        height: 23px;
        cursor: pointer;
      }
      .page-login .bg-box .login-box .login-bottom .type-item img {
        width: 100%;
      }
    }
    @media (max-width: 767px) {
      .page-login .bg-box .login-box {
        position: absolute;
        right: auto;
        top: 26%;
        padding: 30px 30px 20px 30px;
        width: 380px;
        background: #fff;
        border-radius: 10px;
        box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.1);
      }
      .page-login .bg-box .login-box .login-title {
        font-size: 24px;
        color: #333;
        text-align: center;
      }
      .page-login .bg-box .login-box .login-form {
        padding-top: 20px;
      }
      .page-login .bg-box .login-box .login-form .error-line {
        display: flex;
        align-items: center;
        background: #ffebeb;
        color: #e4393c;
        border: 1px solid #faccc6;
        padding: 3px 10px 3px 10px;
        height: auto;
        text-align: left;
        font-size: 12px;
      }
      .page-login .bg-box .login-box .login-form .error-line .error-text {
        padding-left: 5px;
      }
      .page-login .bg-box .login-box .login-form .error-line .error-text .change_tab {
        color: #167aeb;
        cursor: pointer;
      }
      .page-login .bg-box .login-box .login-form .login-item {
        display: flex;
        align-items: center;
        margin-top: 20px;
      }
      .page-login .bg-box .login-box .login-form .login-item .el-input__inner {
        height: 36px;
      }
      .page-login .bg-box .login-box .login-form .login-item > input {
        flex: 1;
        padding: 5px 10px;
        height: 36px;
        border-radius: 3px;
        border: 1px solid #ddd;
      }
      .page-login .bg-box .login-box .login-form .login-item .verify-code {
        height: 36px;
        margin-left: 5px;
      }
      .page-login .bg-box .login-box .login-form .login-item .verify-code img {
        height: 100%;
      }
      .page-login .bg-box .login-box .login-form .login-item .el-checkbox {
        margin-right: 10px;
      }
      .page-login .bg-box .login-box .login-form .login-item .read-text {
        font-size: 12px;
        color: #333;
      }
      .page-login .bg-box .login-box .login-form .login-item .read-text .clickable {
        color: #167aeb;
        cursor: pointer;
      }
      .page-login .bg-box .login-box .login-form .login-item .read-text .clickable:hover {
        text-decoration: underline;
      }
      .page-login .bg-box .login-box .login-form .mg-top10 {
        margin-top: 10px;
      }
      .page-login .bg-box .login-box .login-button {
        margin-top: 20px;
        height: 40px;
        line-height: 40px;
        border-radius: 3px;
        background: #3d88ff;
        text-align: center;
        color: #fff;
        font-size: 14px;
        cursor: pointer;
      }
      .page-login .bg-box .login-box .login-button:hover {
        opacity: 0.8;
      }
      .page-login .bg-box .login-box .divide-line {
        margin-top: 20px;
        height: 1px;
        background: #eee;
        opacity: 0.8;
      }
      .page-login .bg-box .login-box .login-bottom {
        margin-top: 20px;
        justify-content: center;
        align-items: center;
      }
      .page-login .bg-box .login-box .login-bottom .bottom-text {
        font-size: 12px;
        color: #999;
      }
      .page-login .bg-box .login-box .login-bottom .type-item {
        margin-left: 10px;
        width: 30px;
        height: 23px;
        cursor: pointer;
      }
      .page-login .bg-box .login-box .login-bottom .type-item img {
        width: 100%;
      }
    }
    .page-login ::-webkit-input-placeholder {
      font-size: 12px;
      color: #aaa;
    }
    .page-login :-moz-placeholder {
      font-size: 12px;
      color: #aaa;
    }
    .page-login ::-moz-placeholder {
      font-size: 12px;
      color: #aaa;
    }
    .page-login :-ms-input-placeholder {
      font-size: 12px;
      color: #aaa;
    }
    .page-login .logining-text {
      margin-top: 120px;
      text-align: center;
      font-size: 30px;
      color: #333;
    }

  11. 将修改后的组件文件压缩成一个zip包。

    压缩后,即可根据需要上传到华为云Astro轻应用,供高级页面使用。

更新自定义组件

当开发的自定义组件功能有变动,即组件代码发生变动后,需要更新组件版本。更新后,组件所在的页面也会随之生效。

  1. 在应用中,单击左下方的“页面设置”,再选择“插件”页签,找到需要更新的组件(例如userLogin),单击组件所在行右侧“查看详情”,进入组件详情页。

    图4 页面设置下的组件列表

    如果页面图标高亮,则需要先单击解锁页面。

  2. 单击“更新”按钮进入组件更新页面。

    图5 选择更新按钮

  3. 单击“请选择源文件(.zip)”,上传本地的组件zip包,再单击“更新”。

    图6 上传本地组件包

  4. 返回“插件”页签,单击刚刚上传组件(userLogin)的升级按钮,然后单击保存升级,最后单击发布即可。

    图7 更新组件

相关文档