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