linqilong 1 місяць тому
батько
коміт
05f94e94db

+ 16 - 14
src/api/login.js

@@ -1,12 +1,11 @@
 import request from "@/utils/request";
 
 /**
- * 获取验证码
- * @param {*} id 随机字符串
+ * 获取验证码图片
  */
-export function qrCodeLogin(id) {
+export function getCaptchaImage() {
     return request({
-        url: '/qrcode/login/' + id,
+        url: '/bis/insp/getCaptchaImage',
         method: 'GET',
     })
 }
@@ -37,18 +36,21 @@ export function sendMessage(phoneNumber, imgCodeId, code) {
  * @param {*} code 验证码
  * @param {*} message 短信验证码
  * 
- */ 
-export function loginWithMessage(code, message) {
+ */
+export function loginWithMessage(phone, code, message) {
     return request({
         url: '/bis/insp/loginByCode',
         method: 'POST',
-        data: {
-            'code': message,
-            'phone': code
-        },
-        params: {
-            'code': message,
-            'phone': code
-        }
+        data: { "account": phone, "code": code, "password": message, "phone": phone }
     })
 }
+
+/**
+ * 登出
+ */
+export function logout() {
+    return request({
+        url: '/bis/insp/logout',
+        method: 'POST',
+    })
+}

BIN
src/assets/images/login_background.png


+ 3 - 1
src/layout/components/BottomNav.vue

@@ -12,11 +12,13 @@
 <script setup>
 import { ref } from 'vue';
 import { useRouter, useRoute } from 'vue-router';
+import { useAppStore } from '@/stores/app'
+const appStore = useAppStore()
 
 const menuList = [
     { name: '首页', icon: 'wap-home-o', path: '/home' },
     { name: '一张图', icon: 'location-o', path: '/map' },
-    { name: '督查', icon: 'completed-o', path: '/inspect' },
+    { name: appStore.ownApp === "1" ? '督查': '稽查', icon: 'completed-o', path: '/inspect' },
     { name: '问题', icon: 'orders-o', path: '/question' },
     { name: '我的', icon: 'user-o', path: '/about' },
 ]

+ 2 - 0
src/main.js

@@ -8,6 +8,8 @@ import router from './router';
 import 'vant/lib/index.css';
 import '@/assets/styles/index.scss';
 
+import './permission.js';
+
 const app = createApp(App);
 
 app.use(createPinia());

+ 46 - 0
src/permission.js

@@ -0,0 +1,46 @@
+import router from "./router";
+import { getToken } from "@/utils/auth";
+
+const whiteList = [
+  "/check",
+  '/login'
+];
+
+router.beforeEach((to, from, next) => {
+  // 验证 TOKEN
+  if (getToken()) {
+    /* 登录成功 */
+    if (to.path === '/login') {
+      next({ path: "/" });
+    } else {
+      next();
+      // if (store.getters.roles.length === 0) {
+      //   // 判断当前用户是否已拉取完user_info信息
+      //   store.dispatch("GetInfo")
+      //     .then(() => {
+      //       store.dispatch("GenerateRoutes").then((accessRoutes) => {
+      //         // 根据roles权限生成可访问的路由表
+      //         router.addRoutes(accessRoutes); // 动态新建可访问路由表
+      //         next({...to, replace: true}); // hack方法 确保addRoutes已完成
+      //       });
+      //     })
+      //     .catch((err) => {
+      //       store.dispatch("LogOut").then(() => {
+      //         Message.error(err);
+      //         next({path: "/"});
+      //       });
+      //     });
+      // } else {
+      //   next();
+      // }
+    }
+  } else {
+    /* 未登录 */
+    if (whiteList.indexOf(to.path) !== -1) {
+      // 在免登录白名单,直接进入
+      next();
+    } else {
+      next(`/login`);
+    }
+  }
+});

+ 5 - 0
src/router/index.js

@@ -3,6 +3,11 @@ import { createRouter, createWebHistory } from 'vue-router';
 import layout from '@/layout/index.vue'
 
 const routes = [
+    {
+        path: '/login',
+        name: 'login',
+        component: () => import('@/views/Login/index.vue'),
+    },
     {
         path: '/',
         redirect: '/home',

+ 6 - 2
src/stores/app.js

@@ -5,7 +5,11 @@ export const useAppStore = defineStore('app', () => {
   // 当前机构
   const currentOrgId = ref('034')
   // 当前应用 督察:1;稽查:2;
-  const ownApp = ref('2')
+  const ownApp = ref('1')
 
-  return {currentOrgId, ownApp}
+  function setCurrentOrgId(id) {
+    currentOrgId.value = id
+  }
+
+  return {currentOrgId, ownApp, setCurrentOrgId}
 })

+ 106 - 4
src/stores/user.js

@@ -1,8 +1,110 @@
-import {ref} from 'vue'
-import {defineStore} from 'pinia'
+import { ref } from 'vue'
+import { defineStore } from 'pinia'
+import { setToken, getToken, removeToken } from '@/utils/auth'
+import { loginWithMessage, logout } from '@/api/login'
+import { useAppStore } from '@/stores/app'
 
 export const useUserStore = defineStore('user', () => {
-  const userId = ref('414244bd0324447ba1b640b3ffa6fb8e')
+  const userId = ref(null)
+  const nickname = ref('')
+  const orgName = ref('')
+  const avatar = ref('')
+  const roles = ref([])
+  const permissions = ref([])
 
-  return {userId}
+  const token = ref(getToken())
+
+  function setUserInfo(data) {
+    userId.value = data.userId
+  }
+
+  function Login(phone, captcha, smsCode) {
+    return new Promise((resolve, reject) => {
+      loginWithMessage(phone, captcha, smsCode)
+        .then((res) => {
+          let userid = res.data.guid;
+          let loginName = res.data.persName;
+          let guid = res.data.orgId;
+          let orgNm = res.data.orgNm;
+          let isAdmin = res.data.permission;
+          let allNode = JSON.stringify(res.data.allNode);//当前用户所在组信息
+          let allOrg = JSON.stringify(res.data.allOrg);
+          let persType = res.data.persType;
+          let accessToken = res.accessToken;  //token是否过期
+          localStorage.setItem('userid', userid);
+          localStorage.setItem('account', loginName);
+          localStorage.setItem('guid', guid);
+          localStorage.setItem('orgNm', orgNm);
+          localStorage.setItem('isAdmin', isAdmin);
+          localStorage.setItem('persType', persType);
+          localStorage.setItem('accessToken', accessToken);
+          let orgId = res.data.orgId;
+          localStorage.setItem('orgId', orgId);
+          localStorage.setItem('allOrg', allOrg);
+          localStorage.setItem("currentOrgId", res.data.defaultOrg.orgId);  //省级当前的机构id
+          localStorage.setItem("currentRlcode", res.data.defaultOrg.rlcode);  //省级当前的行政区划编码
+          localStorage.setItem("currentOrgNm", res.data.defaultOrg.orgNm);  //省级当前的机构名称
+          localStorage.setItem('allNode', allNode);
+
+          userId.value = userid;
+          nickname.value = loginName;
+          orgName.value = orgNm;
+          setToken(res.accessToken);
+          token.value = res.accessToken;
+
+          const appStore = useAppStore()
+          appStore.setCurrentOrgId(res.data.defaultOrg.orgId)
+
+          
+          resolve();
+        })
+        .catch((error) => {
+          reject(error);
+        });
+    });
+  }
+  // 查询用户信息
+  // function getInfo() {
+  //   return new Promise((resolve, reject) => {
+  //     getInfo()
+  //       .then((res) => {
+  //         const user = res.user;
+  //         const avatar =
+  //           user.avatar == ""
+  //             ? require("@/assets/images/default-user-img.jpg")
+  //             : process.env.VUE_APP_BASE_API + user.avatar;
+  //         if (res.roles && res.roles.length > 0) {
+  //           // 验证返回的roles是否是一个非空数组
+  //           commit("SET_ROLES", res.roles);
+  //           commit("SET_PERMISSIONS", res.permissions);
+  //         } else {
+  //           commit("SET_ROLES", ["ROLE_DEFAULT"]);
+  //         }
+  //         commit("SET_USER_ID", user.userId);
+  //         commit("SET_NAME", user.userName);
+  //         commit("SET_NICKNAME", user.nickName);
+  //         commit("SET_AVATAR", avatar);
+  //         resolve(res);
+  //       })
+  //       .catch((error) => {
+  //         reject(error);
+  //       });
+  //   });
+  // }
+
+  // 退出系统
+  function LogOut() {
+    return new Promise((resolve, reject) => {
+      logout().then(() => {
+        token.value = ""
+        removeToken();
+        resolve();
+      })
+        .catch((error) => {
+          reject(error);
+        });
+    });
+  }
+
+  return { userId, nickname, orgName, setUserInfo, Login, LogOut }
 })

+ 2 - 4
src/utils/auth.js

@@ -4,13 +4,11 @@ const {cookies} = useCookies();
 const TokenKey = 'Admin-Token'
 
 export function getToken() {
-  // return cookies.get(TokenKey)
-  return 'a29319df503e4b39b391092692cd9868'
+  return cookies.get(TokenKey)
 }
 
 export function setToken(token) {
-  // 12小时过期
-  cookies.set(TokenKey, token, 60 * 60 * 12)
+  cookies.set(TokenKey, token)
 }
 
 export function removeToken() {

+ 4 - 4
src/utils/request.js

@@ -2,13 +2,11 @@ import axios from "axios";
 import { getToken } from "@/utils/auth";
 import { getErrorMessage } from "@/utils/errorCode";
 import { validURL } from "@/utils/validate";
-import {useAppStore} from '@/stores/app';
-import {useUserStore} from '@/stores/user';
+import { useAppStore } from '@/stores/app';
+import { useUserStore } from '@/stores/user';
 
 axios.defaults.headers["Content-Type"] = "application/json;charset=utf-8";
 
-const appStore = useAppStore()
-const userStore = useUserStore()
 
 // 创建axios实例
 const service = axios.create({
@@ -26,6 +24,8 @@ service.interceptors.request.use(
         // 是否需要设置 token
         const isToken = (config.headers || {}).isToken === false;
         if (getToken() && !isToken) {
+            const appStore = useAppStore()
+            const userStore = useUserStore()
             config.headers["Orgid"] = appStore.currentOrgId || '034'
             config.headers["Persid"] = userStore.userId
             config.headers["Accesstoken"] = getToken(); // 让每个请求携带自定义token

+ 12 - 7
src/views/AboutView.vue

@@ -1,13 +1,18 @@
 <template>
     <div class="container">
-        <h2>关于我们</h2>
-        <p>这是关于福建水利监管工作平台的介绍。</p>
+        <van-card style="margin: 10px 0;" :desc="userStore.orgName" :title="userStore.nickname"
+            thumb="https://fastly.jsdelivr.net/npm/@vant/assets/ipad.jpeg" />
+        <van-button @click="logout()" block>退&nbsp;出&nbsp;登&nbsp;录</van-button>
     </div>
 </template>
+<script setup>
+import { useUserStore } from '@/stores/user'
 
-<style scoped>
-.container {
-    padding: 20px;
-    overflow-y: auto; 
+const userStore = useUserStore()
+
+function logout() {
+    userStore.LogOut()
 }
-</style>
+
+</script>
+<style lang="scss" scoped></style>

+ 2 - 3
src/views/Inspect/Object/Question/index.vue

@@ -1,9 +1,9 @@
 <template>
   <div style="height: 100%;">
-    <van-pull-refresh class="inspect-object-wrapper" v-model="loading" @refresh="getData()">
+    <van-pull-refresh class="inspect-object-question-wrapper" v-model="loading" @refresh="getData()">
       <card01 v-for="item in list" :key="item" :title="item.ojbNm + ''" icon="notes"
         :description="renderData(item, objectConfig.description)"
-        @click="jumpPage(`/inspect/${item.plnaId}/object/${item.id}/questions`)" />
+        @click="jumpPage(`/inspect/${item.plnaId}/object/${item.id}/question/`)" />
     </van-pull-refresh>
   </div>
 </template>
@@ -22,7 +22,6 @@ const loading = ref(false);
 const inspectType = route.query.inspectType;
 const objectConfig = ref(getBaseByInspectType(inspectType));
 
-
 function getData() {
   getTacQuestionList(route.params.objId).then(res => {
     list.value = res.data;

+ 136 - 72
src/views/Login/index.vue

@@ -1,80 +1,144 @@
 <template>
+  <div class="login-wrapper">
+    <div class="login-container">
+      <div class="login-main">
+        <van-tabs v-model:active="appStore.ownApp">
+          <van-tab title="水利督察" name="1"></van-tab>
+          <van-tab title="水利稽查" name="2"></van-tab>
+        </van-tabs>
+        <van-form @submit="handleLogin">
+          <van-cell-group>
+            <van-field v-model="form.phone" label="手机号码" placeholder="请输入手机号码"
+              :rules="[{ required: true, message: '请填写手机号码' }]" />
 
+            <van-field v-model="form.captcha" label="图片验证码" placeholder="请输入验证码"
+              :rules="[{ required: true, message: '请填写验证码' }]">
+              <template #button>
+                <van-image width="100" height="40" :src="captchaImage" @click="refreshCaptcha" />
+              </template>
+            </van-field>
+
+            <van-field v-model="form.smsCode" label="短信验证码" placeholder="请输入短信验证码"
+              :rules="[{ required: true, message: '请填写短信验证码' }]">
+              <template #button>
+                <van-button size="small" type="primary" :disabled="countDown > 0" @click="sendSmsCode">
+                  {{ countDown > 0 ? `${countDown}秒后重试` : '获取验证码' }}
+                </van-button>
+              </template>
+            </van-field>
+          </van-cell-group>
+
+          <div style="margin: 16px;">
+            <van-button round block type="primary" native-type="submit">
+              登录
+            </van-button>
+          </div>
+        </van-form>
+      </div>
+    </div>
+  </div>
 </template>
 <script setup>
-import { v4 as uuidv4 } from 'uuid';
-import QRCode from 'qrcode'
-let qrUuid = uuidv4();
-let loginType = 3;
-let countTimeNum = 0;
-
-qrCodeLogin(qrUuid).then(res => {
-    if (res.success) {
-        qrUuid = res.data.key;
-        setTimeout(() => {
-            QRCode.toCanvas(canvas, qrUuid, (error) => {
-                if (error) console.error(error);
-                console.log('success!');
-                toLoginWithQrCode();
-            })
-        }, 1000);
-    }
+import { ref, onBeforeUnmount, onMounted, nextTick } from 'vue'
+import { showToast } from 'vant'
+import { setToken } from '@/utils/auth'
+import { useAppStore } from '@/stores/app'
+import { useUserStore } from '@/stores/user'
+import { loginWithMessage, getCaptchaImage } from '@/api/login'
+import { useRouter } from 'vue-router';
+
+const appStore = useAppStore()
+const userStore = useUserStore()
+const router = useRouter()
+
+// 表单数据
+const form = ref({
+  phone: '13960760931',
+  captcha: '336699',
+  smsCode: '336699'
 })
 
-function toLoginWithQrCode() {
-    let qrCode = qrUuid;
-    let logtyp = this.loginType;
-    let timer = setTimeout(function () {
-        countTimeNum++;
-        if (countTimeNum > 20) {
-            clearTimeout(timer);
-            countTimeNum = 0;
-        } else {
-            if (logtyp == 3) {
-                console.log('调用二维码登录');
-                qrCodeLogin(qrCode).then(
-                    (res) => {
-                        console.log(res);
-                        if (res.success) {
-                            let userid = res.data.guid;
-                            let loginName = res.data.persName;
-                            let guid = res.data.orgId;
-                            let orgNm = res.data.orgNm;
-                            let isAdmin = res.data.permission;
-                            let allNode = JSON.stringify(res.data.allNode);//当前用户所在组信息
-                            let allOrg = JSON.stringify(res.data.allOrg);
-                            let persType = res.data.persType;
-                            let accessToken = res.accessToken;  //token是否过期
-                            localStorage.setItem('userid', userid);
-                            localStorage.setItem('account', loginName);
-                            localStorage.setItem('guid', guid);
-                            localStorage.setItem('orgNm', orgNm);
-                            localStorage.setItem('isAdmin', isAdmin);
-                            localStorage.setItem('persType', persType);
-                            localStorage.setItem('accessToken', accessToken);
-                            let orgId = res.data.orgId;
-                            localStorage.setItem('orgId', orgId);
-                            localStorage.setItem('allOrg', allOrg);
-                            localStorage.setItem("currentOrgId", res.data.defaultOrg.orgId);  //省级当前的机构id
-                            localStorage.setItem("currentRlcode", res.data.defaultOrg.rlcode);  //省级当前的行政区划编码
-                            // 改变全局adcode状态
-                            $store.commit('changeAdCode', res.data.defaultOrg.rlcode)
-                            localStorage.setItem("currentOrgNm", res.data.defaultOrg.orgNm);  //省级当前的机构名称
-                            localStorage.setItem('allNode', allNode);
-                            $message.success({
-                                message: '登录成功',
-                                duration: 700
-                            });
-                            $router.push('/')
-                        } else {
-                            toLoginWithQrCode();
-
-                        }
-                    }
-                );
-            }
-        }
-    }, 3000);
+// 图片验证码
+const captchaImage = ref(null)
+const imgcodeid = ref(null)
+const countDown = ref(0)
+let countTimer = null
+
+function refreshCaptcha() {
+  getCaptchaImage().then(async res => {
+    await nextTick();
+    imgcodeid.value = res.data.imgCodeId;
+    captchaImage.value = "data:image/jpg;base64," + res.data.img;
+  })
+}
+
+function sendSmsCode() {
+  if (!form.value.phone) {
+    showToast('请输入手机号码')
+    return
+  }
+  if (!form.value.captcha) {
+    showToast('请输入图片验证码')
+    return
+  }
+  // 倒计时
+  countDown.value = 60
+  countTimer = setInterval(() => {
+    countDown.value--
+    if (countDown.value <= 0) {
+      clearInterval(countTimer)
+    }
+  }, 1000)
+  sendMessage(form.value.phone, imgcodeid.value, form.value.captcha).then((res) => {
+    if (!res.success) {
+      showToast(res.message);
+    }
+  })
+
+}
+
+
+
+// 处理登录
+function handleLogin() {
+  if (!form.value.phone || !form.value.captcha || !form.value.smsCode) {
+    showToast('请填写完整信息')
+    return
+  }
+  userStore.Login(form.value.phone, form.value.captcha, form.value.smsCode).then(res =>{
+    router.push('/')
+  })
 }
 
-</script>
+onMounted(() => {
+  refreshCaptcha()
+})
+
+// 组件卸载时清除定时器
+onBeforeUnmount(() => {
+  if (countTimer) clearInterval(countTimer)
+})
+</script>
+<style lang="scss" scoped>
+.login-wrapper {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  background-image: url('@/assets/images/login_background.png');
+
+  .login-container {
+    padding: 16px;
+
+    .login-main {
+      border-radius: 10px;
+      background-color: #fff;
+      padding-bottom: 10px;
+      overflow: hidden;
+    }
+
+  }
+
+}
+</style>

+ 22 - 12
src/views/Question/index.vue

@@ -1,21 +1,31 @@
 <template>
-  <div>
-    question
-  </div>
+  <van-tabs v-model="active" @click-tab="onClickTab">
+    <van-tab title="问题清单">
+      <listquestions-component></listquestions-component>
+    </van-tab>
+    <van-tab title="问题统计">
+      <questionstatistics-component></questionstatistics-component>
+    </van-tab>
+  </van-tabs>
 </template>
 <script setup>
 
-import {onMounted} from "vue";
-import {getRStLLMaxDate} from "@/api/home";
+import { ref } from "vue";
+import ListQuestions from "./listquestions.vue";
+import QuestionsTatistics from "./questionstatistics.vue";
+import { showToast } from 'vant';
 
-onMounted(() => {
-  getRStLLMaxDate().then(res => {
-    console.log(res)
-  })
-})
+
+const active = ref('问题清单');
+const onClickTab = ({ title }) => showToast(title);
+// onMounted(() => {
+//   getRStLLMaxDate().then(res => {
+//     console.log(res)
+//   })
+// })
 </script>
 <style scoped>
-.container {
+/* .container {
   padding: 20px;
   overflow-y: auto;
 }
@@ -35,5 +45,5 @@ onMounted(() => {
   padding: 10px 20px;
   text-decoration: none;
   border-radius: 5px;
-}
+} */
 </style>

+ 5 - 0
src/views/Question/listquestions.vue

@@ -0,0 +1,5 @@
+<template>
+    <div class="content-wrapper">
+        问题清单
+    </div>
+</template>

+ 5 - 0
src/views/Question/questionstatistics.vue

@@ -0,0 +1,5 @@
+<template>
+    <div class="content-wrapper">
+        问题统计
+    </div>
+</template>