“hanlingqiang 1 год назад
Родитель
Сommit
d0c178e1e6

BIN
src/assets/images/home/bg.png


BIN
src/assets/images/home/logo.png


BIN
src/assets/images/home/logout.png


BIN
src/assets/images/home/tip_1.png


BIN
src/assets/images/home/tip_2.png


BIN
src/assets/images/home/tip_3.png


BIN
src/assets/images/home/tip_4.png


+ 1 - 1
src/permission.ts

@@ -10,7 +10,7 @@ import useSettingsStore from '@/store/modules/settings'
 import usePermissionStore from '@/store/modules/permission'
 
 NProgress.configure({ showSpinner: false })
-const whiteList = ['/login', '/register']
+const whiteList = ['/login', '/register', '/logintip']
 
 router.beforeEach(async (to, from, next) => {
   NProgress.start()

+ 10 - 0
src/router/index.ts

@@ -37,6 +37,16 @@ export const constantRoutes: RouteOption[] = [
       },
     ],
   },
+  {
+    path: '/tipweb',
+    component: () => import('@/views/tipweb.vue'),
+    hidden: true,
+  },
+  {
+    path: '/logintip',
+    component: () => import('@/views/logintip.vue'),
+    hidden: true,
+  },
   {
     path: '/login',
     component: () => import('@/views/login.vue'),

+ 347 - 0
src/views/logintip.vue

@@ -0,0 +1,347 @@
+<template>
+  <div class="login flex">
+    <div class="login-l">
+      <div class="title">康养云物联网平台</div>
+      <div class="desc">智能互联<span></span>助力康养</div>
+      <div class="icon">
+        <img src="@/assets/images/login/let_img.png" alt="" />
+      </div>
+    </div>
+    <div class="login-r">
+      <el-form ref="loginRef" :model="loginForm" :rules="loginRules" class="login-form">
+        <h3 class="logo">
+          <img src="@/assets/logo/logo.png" alt="" />
+        </h3>
+        <div class="title">登录</div>
+        <el-form-item prop="tenantId" v-if="tenantEnabled">
+          <el-select v-model="loginForm.tenantId" filterable placeholder="请选择/输入公司名称" style="width: 100%">
+            <el-option v-for="item in tenantList" :key="item.tenantId" :label="item.companyName" :value="item.tenantId" />
+            <template #prefix><svg-icon icon-class="company" class="el-input__icon input-icon" /></template>
+          </el-select>
+        </el-form-item>
+        <el-form-item prop="username">
+          <el-input v-model="loginForm.username" type="text" size="large" auto-complete="off" placeholder="账号">
+            <template #prefix>
+              <el-icon><User /></el-icon>
+            </template>
+          </el-input>
+        </el-form-item>
+        <el-form-item prop="password">
+          <el-input v-model="loginForm.password" type="password" size="large" auto-complete="off" placeholder="密码" @keyup.enter="handleLogin">
+            <template #prefix>
+              <el-icon><Lock /></el-icon>
+            </template>
+          </el-input>
+        </el-form-item>
+        <el-form-item prop="code" v-if="captchaEnabled">
+          <el-input v-model="loginForm.code" size="large" auto-complete="off" placeholder="验证码" style="width: 67%" @keyup.enter="handleLogin">
+            <template #prefix><svg-icon icon-class="validCode" class="el-input__icon input-icon" /></template>
+          </el-input>
+          <div class="login-code">
+            <img :src="codeUrl" @click="getCode" class="login-code-img" />
+          </div>
+        </el-form-item>
+        <el-checkbox v-model="loginForm.rememberMe" style="margin:0px 0px 25px 0px;" >记住密码</el-checkbox>
+        <el-form-item style="width:100%;">
+          <el-button :loading="loading" size="large" type="primary" style="width:100%;" color="#00957A" @click.prevent="handleLogin">
+            <span v-if="!loading">登 录</span>
+            <span v-else>登 录 中...</span>
+          </el-button>
+          <div style="float: right;" v-if="register">
+            <router-link class="link-type" :to="'/register'">立即注册</router-link>
+          </div>
+        </el-form-item>
+      </el-form>
+      <div class="round1 round"></div>
+      <div class="round2 round"></div>
+      <div class="round3 round"></div>
+      <!--  底部  -->
+      <div class="el-login-footer">
+        <span>Copyright © 2023 </span>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { getCodeImg, getTenantList } from '@/api/login'
+import Cookies from 'js-cookie'
+import { encrypt, decrypt } from '@/utils/jsencrypt'
+import { useUserStore } from '@/store/modules/user'
+import { LoginData, TenantVO } from '@/api/types'
+import { FormInstance, FormRules } from 'element-plus'
+import { to } from 'await-to-js'
+
+const userStore = useUserStore()
+const router = useRouter()
+
+// const title = import.meta.env.VITE_APP_TITLE
+const loginForm = ref<LoginData>({
+  tenantId: '000000',
+  username: 'admin',
+  password: '',
+  rememberMe: false,
+  code: '',
+  uuid: ''
+})
+
+const loginRules: FormRules = {
+  tenantId: [{ required: true, trigger: 'blur', message: '请输入您的租户编号' }],
+  username: [{ required: true, trigger: 'blur', message: '请输入您的账号' }],
+  password: [{ required: true, trigger: 'blur', message: '请输入您的密码' }],
+  code: [{ required: true, trigger: 'change', message: '请输入验证码' }]
+}
+
+const codeUrl = ref('')
+const loading = ref(false)
+// 验证码开关
+const captchaEnabled = ref(true)
+// 租户开关
+const tenantEnabled = ref(true)
+
+
+// 注册开关
+const register = ref(false)
+const redirect = ref(undefined)
+const loginRef = ref<FormInstance>()
+// 租户列表
+const tenantList = ref<TenantVO[]>([])
+
+const handleLogin = () => {
+  loginRef.value?.validate(async (valid:boolean, fields: any) => {
+    if (valid) {
+      loading.value = true
+      // 勾选了需要记住密码设置在 cookie 中设置记住用户名和密码
+      if (loginForm.value.rememberMe) {
+        Cookies.set('tenantId', loginForm.value.tenantId, { expires: 30 })
+        Cookies.set('username', loginForm.value.username, { expires: 30 })
+        Cookies.set('password', String(encrypt(loginForm.value.password)), { expires: 30 })
+        Cookies.set('rememberMe', String(loginForm.value.rememberMe), { expires: 30 })
+      } else {
+        // 否则移除
+        Cookies.remove('tenantId')
+        Cookies.remove('username')
+        Cookies.remove('password')
+        Cookies.remove('rememberMe')
+      }
+      // 调用action的登录方法
+      // prittier-ignore
+      const [err] = await to(userStore.login(loginForm.value))
+      if (!err) {
+        // await router.push({ path: redirect.value || '/' })
+        await router.push({ path: '/tipweb' || '/' })
+      } else {
+        loading.value = false
+        // 重新获取验证码
+        if (captchaEnabled.value) {
+          await getCode()
+        }
+      }
+    } else {
+      console.log('error submit!', fields)
+    }
+  })
+}
+
+/**
+ * 获取验证码
+ */
+const getCode = async () => {
+  const res = await getCodeImg()
+  const { data } = res
+  captchaEnabled.value = data.captchaEnabled === undefined ? true : data.captchaEnabled
+  if (captchaEnabled.value) {
+    codeUrl.value = 'data:image/gif;base64,' + data.img
+    loginForm.value.uuid = data.uuid
+  }
+}
+
+const getCookie = () => {
+  const tenantId = Cookies.get('tenantId')
+  const username = Cookies.get('username')
+  const password = Cookies.get('password')
+  const rememberMe = Cookies.get('rememberMe')
+  loginForm.value = {
+    tenantId: tenantId === undefined ? loginForm.value.tenantId : tenantId,
+    username: username === undefined ? loginForm.value.username : username,
+    password: password === undefined ? loginForm.value.password : (decrypt(password) as string),
+    rememberMe: rememberMe === undefined ? false : Boolean(rememberMe)
+  }
+}
+
+
+/**
+ * 获取租户列表
+ */
+const initTenantList = async () => {
+  const { data } = await getTenantList()
+  tenantEnabled.value = data.tenantEnabled === undefined ? true : data.tenantEnabled
+  if (tenantEnabled.value) {
+    tenantList.value = data.voList
+    if (tenantList.value != null && tenantList.value.length !== 0) {
+      loginForm.value.tenantId = tenantList.value[0].tenantId
+    }
+  }
+}
+
+onMounted(() => {
+  getCode()
+  initTenantList()
+  getCookie()
+})
+</script>
+
+<style lang="scss" scoped>
+  .checkbox:checked + label { background-color: #f0f0f0; border-color: #00957A; }
+
+
+
+
+.login {
+  height: 100%;
+  &-l {
+    width: 50%;
+    padding: 0 60px;
+    background-image: url('@/assets/images/login/bg.png');
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    align-items: center;
+    position: relative;
+    overflow: hidden;
+    &::after {
+      content: '';
+      position: absolute;
+      bottom: 0;
+      left: 0;
+      width: 160px;
+      height: 160px;
+      border-radius: 50%;
+      transform: translate(-30%, 40%);
+      background: linear-gradient(18deg, #eaf2fbff 0%, #ebf2fc00 100%);
+    }
+    .title {
+      font-size: 30px;
+      color: #fff;
+      opacity: 0.8;
+    }
+    .desc {
+      padding: 22px 0;
+      font-size: 36px;
+      color: #fff;
+      font-weight: 500;
+      span {
+        display: inline-block;
+        width: 50px;
+      }
+    }
+    .icon {
+      width: 100%;
+      max-width: 600px;
+      img {
+        width: 100%;
+        height: auto;
+      }
+    }
+  }
+  &-r {
+    width: 50%;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    height: 100%;
+    background-size: cover;
+    position: relative;
+    overflow: hidden;
+    .round {
+      position: absolute;
+      border-radius: 50%;
+      background: linear-gradient(18deg, #eaf2fbff 0%, #ebf2fc00 100%);
+      z-index: 1;
+    }
+    .round1 {
+      width: 220px;
+      height: 220px;
+      bottom: 0;
+      right: 0;
+      transform: translate(40%, 20%);
+    }
+    .round2 {
+      width: 72px;
+      height: 72px;
+      bottom: 100px;
+      left: 70px;
+      transform: translate(40%, 20%);
+    }
+    .round3 {
+      width: 36px;
+      height: 36px;
+      top: 100px;
+      right: 170px;
+    }
+    .logo {
+      width: 230px;
+      margin: 0 auto;
+      margin-bottom: 80px;
+      img {
+        width: 100%;
+      }
+    }
+    .title {
+      color: rgba(11, 29, 48, 1);
+      font-size: 24px;
+      margin-bottom: 30px;
+      font-weight: 600;
+    }
+    .login-form {
+      border-radius: 6px;
+      background: #ffffff;
+      width: 400px;
+      position: relative;
+      z-index: 2;
+      .el-input {
+        height: 40px;
+        input {
+          height: 40px;
+        }
+      }
+      .input-icon {
+        height: 39px;
+        width: 14px;
+        margin-left: 0px;
+      }
+    }
+    .login-tip {
+      font-size: 13px;
+      text-align: center;
+      color: #bfbfbf;
+    }
+    .login-code {
+      width: 33%;
+      height: 40px;
+      float: right;
+      img {
+        cursor: pointer;
+        vertical-align: middle;
+        width: 100%;
+      }
+    }
+    .el-login-footer {
+      position: absolute;
+      bottom: 0;
+      height: 40px;
+      line-height: 40px;
+      width: 50%;
+      text-align: center;
+      color: #87909D;
+      font-family: Arial,serif;
+      font-size: 12px;
+      letter-spacing: 1px;
+    }
+    .login-code-img {
+      height: 40px;
+      padding-left: 12px;
+    }
+  }
+}
+</style>

+ 194 - 0
src/views/tipweb.vue

@@ -0,0 +1,194 @@
+<template>
+  <div class="app-container home">
+    <img class="logoicon" :src="logo" alt="" />
+    <div class="box_bg">
+      <el-row :gutter="10" class="top_row">
+        <el-col :span="8" class="box-card_bg" v-for="(item, index) in state.jumpListData" :key="index">
+          <el-card @click="jumpweb(item.type)" :style="{ background: item.bgColor }" class="box-card-num">
+            <el-image style="width:80px;height:80px" :src="item.icon" fit="cover"></el-image>
+            <p class="jump_title" :style="{ '--colorTitle': item.color }">{{ item.name }}</p>
+          </el-card>
+        </el-col>
+      </el-row>
+    </div>
+    <div class="logout_bg" @click="logout">
+      <el-image style="width:40px;height:40px" :src="logouticon" fit="cover"></el-image>
+    </div>
+  </div>
+</template>
+
+<script setup name="Index" lang="ts">
+import useUserStore from '@/store/modules/user'
+import { customerApi } from '@/api/home'
+import { md5Encrypt } from '@/utils/jsencrypt'
+import logo from '@/assets/images/home/logo.png'
+import logouticon from '@/assets/images/home/logout.png'
+import home_1 from '@/assets/images/home/tip_1.png'
+import home_2 from '@/assets/images/home/tip_2.png'
+import home_3 from '@/assets/images/home/tip_3.png'
+import { getName } from '@/utils/auth'
+enum TypeApi {
+  tend = 'tend',
+  health = 'health',
+  bed = 'bed',
+}
+const router = useRouter()
+const userStore = useUserStore()
+const state = reactive({
+  jumpListData: [
+    {
+      type: TypeApi.tend,
+      name: '安全照护',
+      //   bgColor: 'linear-gradient(225deg, #F1F7FF 0%, #FFFFFF 36%, #FFFFFF 100%)',
+      bgColor: '#30CA79',
+      //   color: '#477ec6',
+      color: '#FFFFFF',
+      icon: home_1,
+    },
+    {
+      type: TypeApi.health,
+      name: '健康监测',
+      //   bgColor: 'linear-gradient(225deg, #FFF6EE 0%, #FFFFFF 36%, #FFFFFF 100%)',
+      bgColor: '#03A06C',
+      //   color: '#da8a45',
+      color: '#FFFFFF',
+      icon: home_2,
+    },
+    {
+      type: TypeApi.bed,
+      name: '数字家床',
+      //   bgColor: 'linear-gradient(225deg, #EEFFF5 0%, #FFFFFF 36%, #FFFFFF 100%)',
+      bgColor: '#29A2AD',
+      //   color: '#26b669',
+      color: '#FFFFFF',
+      icon: home_3,
+    },
+  ],
+  pieColor: [
+    'rgba(255, 162, 23, 1)',
+    'rgba(255, 119, 106, 1)',
+    'rgba(108, 201, 255, 1)',
+    'rgba(108, 158, 254, 1)',
+    'rgba(85, 116, 251, 1)',
+    'rgba(139, 126, 255, 1)',
+    'rgba(95, 208, 164, 1)',
+    'rgba(250, 204, 62, 1)',
+  ],
+})
+
+const jumpweb = async (type: any) => {
+  let md5Txt = md5Encrypt(String(getName()).toLowerCase())
+  let paraStr = `user=${getName()}&token=${md5Txt}`
+  switch (type) {
+    case TypeApi.tend:
+      {
+        window.open(`https://web.poteviohealth.com/zhylsafecase/index.html?${paraStr}`, '_blank')
+      }
+      break
+    case TypeApi.health:
+      {
+        let customerData = await customerApiF(md5Txt)
+        if (customerData['status'] == 0) {
+          // https://web.poteviohealth.com/r/daping/health/index.html?customerId=160
+          window.open(`https://web.poteviohealth.com/r/daping/health/index.html?customerId=${customerData['customerId']}`, '_blank')
+        } else {
+          ElMessage.error('跳转出错:' + JSON.stringify(customerData))
+        }
+      }
+      break
+    case TypeApi.bed:
+      {
+        window.open(`http://web.poteviohealth.com/boss/daping/data.html?${paraStr}`, '_blank')
+      }
+      break
+    default:
+      break
+  }
+}
+const customerApiF = async (md5Txt: string) => {
+  const customerData = await customerApi({
+    command: 'customerloginiot',
+    account: 'iot' + getName(),
+    password: md5Txt,
+    terminalType: 'health',
+  })
+  return customerData
+}
+const logout = async () => {
+  await ElMessageBox.confirm('确定注销并退出系统吗?', '提示', {
+    confirmButtonText: '确定',
+    cancelButtonText: '取消',
+    type: 'warning',
+  })
+  await userStore.logout()
+//   location.href = import.meta.env.VITE_APP_CONTEXT_PATH
+  router.replace({ path: '/logintip' })
+}
+onMounted(() => {})
+</script>
+
+<style scoped lang="scss">
+.home {
+  width: 100%;
+  height: 100%;
+  background-image: url('@/assets/images/home/bg.png');
+  background-size: cover;
+  font-family: 'open sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
+  font-size: 13px;
+  color: #676a6c;
+  overflow-x: hidden;
+  position: absolute;
+  .logoicon {
+    margin: 50px 60px 80px 60px;
+  }
+
+  .box_bg {
+    width: 100%;
+    display: flex;
+    justify-content: center;
+    .box-card_bg {
+      display: flex;
+      justify-content: center;
+    }
+  }
+
+  .top_row {
+    width: 86%;
+  }
+  .box-card-num {
+    transition: transform 0.5s ease;
+    width: 80%;
+    height: 600px;
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    align-items: center;
+    cursor: pointer;
+    .el-card__body {
+      display: flex;
+      .el-image {
+        margin-left: 33px;
+      }
+      .jump_title {
+        margin-top: 10px;
+        color: var(--colorTitle) !important;
+        box-sizing: border-box;
+        font-size: 40px !important;
+        font-weight: 500 !important;
+      }
+    }
+  }
+  .box-card-num:hover {
+    transform: scale(1.06);
+  }
+
+  .logout_bg {
+    position: absolute;
+    top: 80px;
+    right: 60px;
+    width: 50px;
+    height: 50px;
+    cursor: pointer;
+  }
+}
+</style>