feat(xumu): 实现畜牧平台登录页面
- 新增登录页面组件 AppSign - 添加用户名和密码登录功能 - 集成二维码登录 - 优化页面样式和布局
This commit is contained in:
		| @@ -1,4 +1,5 @@ | ||||
| VUE_APP_SCOPE=xumu | ||||
| VUE_APP_API=http://192.168.1.87:19897 | ||||
| VUE_APP_API=http://192.168.1.251:19998 | ||||
| VUE_APP_IS_SIMPLE_SERVER=1 | ||||
| VUE_APP_PORT=12413 | ||||
| VUE_APP_OMS_ID=2cd70a15-a3cf-4b4d-9a22-0f3b3a888b08   # oms定制方案的ID | ||||
|   | ||||
| @@ -11,7 +11,7 @@ instance.interceptors.request.use(config => { | ||||
|     config.url = "/ns" + config.url | ||||
|   } | ||||
|   if (process.env.VUE_APP_IS_SIMPLE_SERVER == 1) { | ||||
|     config.url = config.url.replace(/(app|auth|admin)\//, "api/") | ||||
|     config.url = config.url.replace(/\/(app|auth|admin)\//, "/api/") | ||||
|     if (['xumu'].includes(process.env.VUE_APP_SCOPE)) { | ||||
|       config.url = config.url.replace("/api/", "/") | ||||
|     } | ||||
|   | ||||
| @@ -13,7 +13,8 @@ | ||||
|     "predev": "node bin/scanApps.js", | ||||
|     "preoms": "dotenv -e .env.oms node bin/scanApps.js", | ||||
|     "prexumu": "dotenv -e .env.xumu node bin/scanApps.js", | ||||
|     "preview:xumu":"dotenv -e .env.xumu node bin/build.js&& vue-cli-service serve --mode xumu" | ||||
|     "view:xumu": "vue-cli-service serve --mode xumu", | ||||
|     "preview:xumu": "dotenv -e .env.xumu node bin/build.js" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "@amap/amap-jsapi-loader": "^1.0.1", | ||||
|   | ||||
							
								
								
									
										229
									
								
								project/xumu/AppSign/AppSign.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										229
									
								
								project/xumu/AppSign/AppSign.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,229 @@ | ||||
| <template> | ||||
|   <section class="AppSign"> | ||||
|     <div class="left signLeftBg"> | ||||
|       <el-row type="flex" align="middle"> | ||||
|         <img class="AiIcon" v-if="/[\\\/]/.test(logo.icon)" :src="logo.icon" alt=""/> | ||||
|         <ai-icon v-else-if="logo.icon" type="logo" :icon="logo.icon"/> | ||||
|         <div v-if="logo.text" class="logoText mar-l8" v-text="logo.text"/> | ||||
|       </el-row> | ||||
|       <div class="signLeftContent"> | ||||
|         <div class="titlePane"> | ||||
|           <b v-text="system.name"/> | ||||
|           <div v-text="system.title"/> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div class="right"> | ||||
|       <div class="projectName mar-b48" :title="system.fullTitle">{{ system.fullTitle }}</div> | ||||
|       <el-card class="signBox"> | ||||
|         <div class="choosePlatform flex column" v-if="!isAdmin&&!form.type"> | ||||
|           <div class="font-20 mar-b40 t-center t-bold">请选择业务端后登陆</div> | ||||
|           <div class="selectPlatform fill"> | ||||
|             <div class="flex center pointer" v-for="op in platforms" :key="op.dictValue" | ||||
|                  v-text="op.dictName" @click="$set(form,'type',op.dictValue)"/> | ||||
|           </div> | ||||
|           <div class="mar-t32 font-12" style="align-self: flex-end"> | ||||
|             未注册用户请扫码添加客服咨询 | ||||
|             <i class="iconfont iconEwm" style="font-size: 20px"/> | ||||
|           </div> | ||||
|         </div> | ||||
|         <template v-else> | ||||
|           <div class="font-20 mar-b40 t-center t-bold"><i v-if="!isAdmin" class="el-icon-back" @click="form.type=null"/>账号登录</div> | ||||
|           <el-form :model="form" ref="form" :rules="rules"> | ||||
|             <el-form-item prop="username"> | ||||
|               <el-input v-model="form.username" placeholder="请输入您的账号"/> | ||||
|             </el-form-item> | ||||
|             <el-form-item prop="password"> | ||||
|               <el-input type="password" v-model="form.password" placeholder="请输入您的密码" show-password/> | ||||
|             </el-form-item> | ||||
|           </el-form> | ||||
|           <div class="t-right font-12">忘记密码请联系客服处理</div> | ||||
|           <el-button type="primary" class="login-btn" @click="handleSignIn">登录</el-button> | ||||
|         </template> | ||||
|       </el-card> | ||||
|       <el-row type="flex" align="middle" class="bottomRecord"> | ||||
|         <div v-if="system.recordDesc" v-text="system.recordDesc"/> | ||||
|         <el-link v-if="system.recordNo" v-text="system.recordNo" :href="system.recordURL"/> | ||||
|         <div v-if="system.ssl" v-html="system.ssl"/> | ||||
|       </el-row> | ||||
|     </div> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import {mapMutations, mapState} from 'vuex' | ||||
|  | ||||
| const rules = { | ||||
|   username: [{required: true, message: '请输入您的账号', trigger: 'blur'}], | ||||
|   password: [{required: true, message: '请输入您的密码', trigger: 'blur'}] | ||||
| } | ||||
|  | ||||
| export default { | ||||
|   name: "AppSign", | ||||
|   label: "登录页", | ||||
|   data() { | ||||
|     return { | ||||
|       rules, | ||||
|       form: {} | ||||
|     } | ||||
|   }, | ||||
|   computed: { | ||||
|     ...mapState(['user', 'sys']), | ||||
|     instance: v => v.$request, | ||||
|     system: v => v.sys?.info || { | ||||
|       fullTitle: '畜牧养殖产业一体化平台' | ||||
|     }, | ||||
|     logo: v => !!v.system.loginLogo ? {icon: v.system.loginLogo, text: v.system.loginLogoText} : {icon: v.system.logo, text: v.system.logoText}, | ||||
|     isAdmin: v => v.$route.hash == "#sinoecare", //用来判断是否是管理员登录, | ||||
|     dict: v => v.$dict, | ||||
|     platforms: v => v.dict.getDict('roleType').filter(e => !['platform', 'other', 'service'].includes(e.dictValue)) | ||||
|   }, | ||||
|   created() { | ||||
|     this.dict.load("roleType") | ||||
|     if (this.user.token) { | ||||
|       this.handleGotoHome() | ||||
|     } else { | ||||
|       const {code} = this.$route.query | ||||
|       if (code) { | ||||
|         this.toLogin(code) | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     ...mapMutations(['setToken']), | ||||
|     login(data) { | ||||
|       if (data?.access_token) { | ||||
|         this.setToken([data.token_type, data.access_token].join(" ")) | ||||
|         this.handleGotoHome() | ||||
|       } | ||||
|     }, | ||||
|     handleGotoHome() { | ||||
|       this.$message.success("登录成功!") | ||||
|       if (this.$route.hash == "#dv") { | ||||
|         this.$router.push({name: "数据大屏入口", hash: "#dv"}) | ||||
|       } else { | ||||
|         this.$router.push({name: "Home"}) | ||||
|       } | ||||
|     }, | ||||
|     handleSignIn() { | ||||
|       this.$refs.form.validate().then(() => { | ||||
|         const password = this.$encryption(this.form.password) | ||||
|         this.form.type = this.form.type || "platform" | ||||
|         this.$request.post("/oauth/token", null, { | ||||
|           auth: {username: 'villcloud', password: "villcloud"}, | ||||
|           params: {grant_type: 'password', scope: 'server', ...this.form, password} | ||||
|         }).then(data => { | ||||
|           this.login(data) | ||||
|         }) | ||||
|       }) | ||||
|     } | ||||
|   }, | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .AppSign { | ||||
|   display: flex; | ||||
|   box-sizing: border-box; | ||||
|   height: 100%; | ||||
|  | ||||
|  | ||||
|   .selectPlatform { | ||||
|     width: 100%; | ||||
|     display: grid; | ||||
|     grid-template-columns: repeat(2, 1fr); | ||||
|     grid-gap: 10px; | ||||
|  | ||||
|     & > div { | ||||
|       color: #fff; | ||||
|       background: $primaryBtnColor; | ||||
|       border-radius: 4px; | ||||
|  | ||||
|       &:hover { | ||||
|         opacity: 0.8; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .signBox { | ||||
|     width: 500px; | ||||
|     min-height: 300px; | ||||
|     position: relative; | ||||
|     color: $primaryColor; | ||||
|  | ||||
|     .choosePlatform { | ||||
|       position: absolute; | ||||
|       top: 0; | ||||
|       left: 0; | ||||
|       width: 100%; | ||||
|       height: 100%; | ||||
|       padding: inherit; | ||||
|     } | ||||
|  | ||||
|     .el-icon-back { | ||||
|       position: absolute; | ||||
|       left: 20px; | ||||
|       top: 25px; | ||||
|     } | ||||
|  | ||||
|     .login-btn { | ||||
|       font-size: 16px; | ||||
|       width: 100%; | ||||
|       height: 40px; | ||||
|       line-height: 40px; | ||||
|       margin: 16px auto; | ||||
|       padding: 0; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .AiIcon { | ||||
|     font-size: 40px; | ||||
|     height: 40px; | ||||
|   } | ||||
|  | ||||
|   .logoText { | ||||
|     font-size: 20px; | ||||
|   } | ||||
|  | ||||
|   :deep(.left) { | ||||
|     width: 480px; | ||||
|     flex-shrink: 0; | ||||
|     background-size: 100% 100%; | ||||
|     background-repeat: no-repeat; | ||||
|     padding-left: 64px; | ||||
|     padding-top: 40px; | ||||
|     box-sizing: border-box; | ||||
|     color: #fff; | ||||
|     font-size: 16px; | ||||
|  | ||||
|     .iconcunwei1 { | ||||
|       font-size: 36px; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   :deep(.right) { | ||||
|     flex: 1; | ||||
|     min-width: 0; | ||||
|     background-color: #F6F8FB; | ||||
|     background-repeat: no-repeat; | ||||
|     background-position: calc(100% - 80px) 0, calc(100% - 40px) 100%; | ||||
|     display: flex; | ||||
|     flex-direction: column; | ||||
|     align-items: center; | ||||
|     justify-content: center; | ||||
|  | ||||
|     .bottomRecord { | ||||
|       font-size: 12px; | ||||
|       color: #999; | ||||
|       gap: 16px; | ||||
|       position: fixed; | ||||
|       bottom: 20px; | ||||
|  | ||||
|       .el-link { | ||||
|         font-size: inherit; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
| } | ||||
| </style> | ||||
| @@ -1,155 +0,0 @@ | ||||
| <template> | ||||
|   <section class="AppSign"> | ||||
|     <div class="left signLeftBg"> | ||||
|       <el-row type="flex" align="middle"> | ||||
|         <img class="AiIcon" v-if="/[\\\/]/.test(logo.icon)" :src="logo.icon" alt="" /> | ||||
|         <ai-icon v-else type="logo" :icon="logo.icon" /> | ||||
|         <div v-if="logo.text" class="logoText mar-l8" v-text="logo.text" /> | ||||
|       </el-row> | ||||
|       <div class="signLeftContent"> | ||||
|         <div class="titlePane"> | ||||
|           <b v-text="system.name" /> | ||||
|           <div v-text="system.title" /> | ||||
|         </div> | ||||
|         <div class="subTitle" v-for="(t, i) in subTitles" :key="i" v-html="t" /> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div class="right"> | ||||
|       <div class="projectName mar-b48" :title="system.fullTitle">{{ system.fullTitle }}</div> | ||||
|       <ai-sign isSignIn @login="login" :instance="instance" visible | ||||
|         :showScanLogin="system.edition == 'standard' || !system.edition" /> | ||||
|       <el-row type="flex" align="middle" class="bottomRecord"> | ||||
|         <div v-if="system.recordDesc" v-text="system.recordDesc" /> | ||||
|         <el-link v-if="system.recordNo" v-text="system.recordNo" :href="system.recordURL" /> | ||||
|         <div v-if="system.ssl" v-html="system.ssl" /> | ||||
|       </el-row> | ||||
|     </div> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import { mapMutations, mapState } from 'vuex' | ||||
|  | ||||
| export default { | ||||
|   name: "AppSign", | ||||
|   computed: { | ||||
|     ...mapState(['user', 'sys']), | ||||
|     instance: v => v.$request, | ||||
|     system: v => v.sys?.info || { | ||||
|       fullTitle: '武汉中卫慧通科技有限公司' | ||||
|     }, | ||||
|     subTitles() { | ||||
|       let list = [ | ||||
|         "构建全域数字大脑,助力政府科学决策", | ||||
|         "全域统一应用入口,移动办公高效协同", | ||||
|         "直接触达居民微信,政民互动“零距离”" | ||||
|       ] | ||||
|       return (typeof this.system.desc == "object" ? this.system.desc : JSON.parse(this.system.desc || null)) || list | ||||
|     }, | ||||
|     logo: v => !!v.system.loginLogo ? { icon: v.system.loginLogo, text: v.system.loginLogoText } : { icon: v.system.logo, text: v.system.logoText }, | ||||
|     isDev: () => process.env.NODE_ENV == "development", | ||||
|     isAdmin: v => v.$route.hash == "#sinoecare" //用来判断是否是管理员登录 | ||||
|   }, | ||||
|   created() { | ||||
|     if (this.user.token) { | ||||
|       this.handleGotoHome() | ||||
|     } else { | ||||
|       const { code } = this.$route.query | ||||
|       if (code) { | ||||
|         this.toLogin(code) | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     ...mapMutations(['setToken']), | ||||
|     login(data) { | ||||
|       if (data?.access_token) { | ||||
|         this.setToken([data.token_type, data.access_token].join(" ")) | ||||
|         this.handleGotoHome() | ||||
|       } | ||||
|     }, | ||||
|     handleGotoHome() { | ||||
|       this.$message.success("登录成功!") | ||||
|       if (this.$route.hash == "#dv") { | ||||
|         this.$router.push({ name: "数据大屏入口", hash: "#dv" }) | ||||
|       } else { | ||||
|         this.$router.push({ name: "Home" }) | ||||
|       } | ||||
|     }, | ||||
|     toLogin(code) { | ||||
|       this.instance.post(`/auth/wechatcp-qr/token`, { | ||||
|         code: code, | ||||
|         type: 'cpuser' | ||||
|       }, { | ||||
|         auth: { | ||||
|           username: 'villcloud', | ||||
|           password: "villcloud" | ||||
|         }, | ||||
|         params: { | ||||
|           grant_type: 'password', | ||||
|           scope: 'server' | ||||
|         } | ||||
|       }).then(this.login) | ||||
|     }, | ||||
|   }, | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .AppSign { | ||||
|   display: flex; | ||||
|   box-sizing: border-box; | ||||
|   height: 100%; | ||||
|  | ||||
|   .AiIcon { | ||||
|     font-size: 40px; | ||||
|     height: 40px; | ||||
|   } | ||||
|  | ||||
|   .logoText { | ||||
|     font-size: 20px; | ||||
|   } | ||||
|  | ||||
|   :deep(.left) { | ||||
|     width: 480px; | ||||
|     flex-shrink: 0; | ||||
|     background-size: 100% 100%; | ||||
|     background-repeat: no-repeat; | ||||
|     padding-left: 64px; | ||||
|     padding-top: 40px; | ||||
|     box-sizing: border-box; | ||||
|     color: #fff; | ||||
|     font-size: 16px; | ||||
|  | ||||
|     .iconcunwei1 { | ||||
|       font-size: 36px; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   :deep(.right) { | ||||
|     flex: 1; | ||||
|     min-width: 0; | ||||
|     background-color: #F6F8FB; | ||||
|     background-image: url("../assets/loginRightTop.png"), url("../assets/loginRightBottom.png"); | ||||
|     background-repeat: no-repeat; | ||||
|     background-position: calc(100% - 80px) 0, calc(100% - 40px) 100%; | ||||
|     display: flex; | ||||
|     flex-direction: column; | ||||
|     align-items: center; | ||||
|     justify-content: center; | ||||
|  | ||||
|     .bottomRecord { | ||||
|       font-size: 12px; | ||||
|       color: #999; | ||||
|       gap: 16px; | ||||
|       position: fixed; | ||||
|       bottom: 20px; | ||||
|  | ||||
|       .el-link { | ||||
|         font-size: inherit; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
| } | ||||
| </style> | ||||
| @@ -11,7 +11,7 @@ instance.defaults.baseURL = baseURLs[process.env.NODE_ENV] | ||||
| instance.interceptors.request.use(config => { | ||||
|   config.timeout = 300000 | ||||
|   if (extra?.isSingleService) { | ||||
|     config.url = config.url.replace(/(app|auth|admin)\//, "api/") | ||||
|     config.url = config.url.replace(/\/(app|auth|admin)\//, "/api/") | ||||
|   } | ||||
|   if (config.url.startsWith("/node")) { | ||||
|     config.baseURL = "/ns" | ||||
|   | ||||
| @@ -9,8 +9,9 @@ import CryptoJs from "crypto-js"; | ||||
| export const $encryption = (params, c = 0) => { | ||||
|   if (CryptoJs) { | ||||
|     const key = "thanks,villcloud" | ||||
|     let password = typeof params == "object" ? params.password : params | ||||
|     let iv = CryptoJs.enc.Latin1.parse(key) | ||||
|     let encrypted = CryptoJs.AES.encrypt(params.password, iv, { | ||||
|     let encrypted = CryptoJs.AES.encrypt(password, iv, { | ||||
|       iv, | ||||
|       mode: CryptoJs.mode.CBC, | ||||
|       padding: CryptoJs.pad.ZeroPadding | ||||
|   | ||||
| @@ -80,12 +80,21 @@ $--font-path: '~element-ui/lib/theme-chalk/fonts'; | ||||
| /** | ||||
|   不换行文本 | ||||
|  */ | ||||
| .nowrap-text { | ||||
| .nowrap-text, .t-nowrap { | ||||
|   white-space: nowrap; | ||||
|   overflow: hidden; | ||||
|   text-overflow: ellipsis; | ||||
| } | ||||
|  | ||||
| .t-center { | ||||
|   text-align: center; | ||||
| } | ||||
| .t-bold{ | ||||
|   font-weight: bold; | ||||
| } | ||||
| .t-right{ | ||||
|   text-align: right; | ||||
| } | ||||
| /** | ||||
|   表头式样 | ||||
|  */ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user