feat(xumu): 实现畜牧平台登录页面
- 新增登录页面组件 AppSign - 添加用户名和密码登录功能 - 集成二维码登录 - 优化页面样式和布局
This commit is contained in:
		| @@ -1,4 +1,5 @@ | |||||||
| VUE_APP_SCOPE=xumu | 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_IS_SIMPLE_SERVER=1 | ||||||
| VUE_APP_PORT=12413 | 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 |     config.url = "/ns" + config.url | ||||||
|   } |   } | ||||||
|   if (process.env.VUE_APP_IS_SIMPLE_SERVER == 1) { |   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)) { |     if (['xumu'].includes(process.env.VUE_APP_SCOPE)) { | ||||||
|       config.url = config.url.replace("/api/", "/") |       config.url = config.url.replace("/api/", "/") | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -13,7 +13,8 @@ | |||||||
|     "predev": "node bin/scanApps.js", |     "predev": "node bin/scanApps.js", | ||||||
|     "preoms": "dotenv -e .env.oms node bin/scanApps.js", |     "preoms": "dotenv -e .env.oms node bin/scanApps.js", | ||||||
|     "prexumu": "dotenv -e .env.xumu 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": { |   "dependencies": { | ||||||
|     "@amap/amap-jsapi-loader": "^1.0.1", |     "@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 => { | instance.interceptors.request.use(config => { | ||||||
|   config.timeout = 300000 |   config.timeout = 300000 | ||||||
|   if (extra?.isSingleService) { |   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")) { |   if (config.url.startsWith("/node")) { | ||||||
|     config.baseURL = "/ns" |     config.baseURL = "/ns" | ||||||
|   | |||||||
| @@ -9,8 +9,9 @@ import CryptoJs from "crypto-js"; | |||||||
| export const $encryption = (params, c = 0) => { | export const $encryption = (params, c = 0) => { | ||||||
|   if (CryptoJs) { |   if (CryptoJs) { | ||||||
|     const key = "thanks,villcloud" |     const key = "thanks,villcloud" | ||||||
|  |     let password = typeof params == "object" ? params.password : params | ||||||
|     let iv = CryptoJs.enc.Latin1.parse(key) |     let iv = CryptoJs.enc.Latin1.parse(key) | ||||||
|     let encrypted = CryptoJs.AES.encrypt(params.password, iv, { |     let encrypted = CryptoJs.AES.encrypt(password, iv, { | ||||||
|       iv, |       iv, | ||||||
|       mode: CryptoJs.mode.CBC, |       mode: CryptoJs.mode.CBC, | ||||||
|       padding: CryptoJs.pad.ZeroPadding |       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; |   white-space: nowrap; | ||||||
|   overflow: hidden; |   overflow: hidden; | ||||||
|   text-overflow: ellipsis; |   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