wwf
15 小时以前 a32100e31b93bc378b11ab93617a9c40b081ad70
移除核验部分+登录注册接口对接
13个文件已删除
18个文件已修改
2个文件已添加
2550 ■■■■ 已修改文件
src/assets/json/idCardTypeItems.json 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/config/property.js 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main.js 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/h5/router.js 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/index.js 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/main/appraisalPlan/index.js 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/main/center/index.js 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/main/certificate/index.js 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/main/examTicket/index.js 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/main/home/index.js 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/main/notice/index.js 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/main/score/index.js 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/stores/session.js 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/ruleGenerator.js 166 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/h5/faceAuth/components/auditDialog.vue 156 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/h5/faceAuth/components/camera.vue 263 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/h5/faceAuth/index.vue 197 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/h5/index.vue 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/h5/login/bind.vue 140 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/h5/login/index.vue 141 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/h5/login/redirect.vue 245 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/h5/signup/BaiduMap.vue 133 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/h5/signup/index.vue 251 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/h5/verify/form.vue 233 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/h5/verify/index.vue 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/h5/verify/noAccess.vue 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/login/index.vue 140 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/main/appraisalPlan/index.vue 37 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/main/center/index.vue 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/main/components/CustomLoading.vue 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/main/components/MyFooter.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/main/components/MyHeader.vue 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/main/index.vue 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/json/idCardTypeItems.json
New file
@@ -0,0 +1,10 @@
[
  {
    "label": "居民身份证",
    "value": 1
  },
  {
    "label": "台港澳证件居民来往内地通行证",
    "value": 2
  }
]
src/config/property.js
@@ -1,3 +1,10 @@
export default {
  sex: {
    key_1: '男',
    key_0: '女'
  },
  idCardType: {
    key_1: '居民身份证',
    key_2: '台港澳证件居民来往内地通行证'
  }
}
src/main.js
@@ -50,7 +50,8 @@
app.component('UploadBtn', UploadBtn)
app.use(ElementPlus, {
  locale: zhCn
  locale: zhCn,
})
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
src/router/h5/router.js
File was deleted
src/router/index.js
@@ -2,11 +2,11 @@
import authPage from '@/router/auth/index.js'
import errorPage from '@/router/error/index.js'
import mainPage from '@/router/main/index.js'
import h5 from '@/router/h5/router.js'
import { useLoginStore } from '@/stores/login.js'
import { useSessionStore } from '@/stores/session.js'
const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [...mainPage, ...authPage, ...errorPage, ...h5],
  routes: [...mainPage, ...authPage, ...errorPage],
})
router.beforeEach((to, from, next) => {
@@ -17,7 +17,18 @@
      next({ path: '/error/404', query: { errorUrl: to.path } })
    }
  } else {
    next()
    if (to.meta.loginRequired) {
      const { userInfo } = useSessionStore()
      const { setLoginDialogVisible } = useLoginStore()
      if (userInfo.id) {
        next()
      } else {
        setLoginDialogVisible(true)
        next(false)
      }
    } else {
      next()
    }
  }
})
src/router/main/appraisalPlan/index.js
@@ -4,6 +4,11 @@
    name: '评价计划',
    component: () => import('@/views/main/appraisalPlan/index.vue'),
  },
  {
    path: 'appraisalPlan/signup/:id',
    name: '评价计划报名',
    component: () => import('@/views/main/appraisalPlan/index.vue'),
  },
]
export default appraisalPlan
src/router/main/center/index.js
@@ -3,6 +3,9 @@
    path: 'center',
    name: '个人中心',
    component: () => import('@/views/main/center/index.vue'),
    meta: {
      loginRequired: false
    }
  },
]
src/router/main/certificate/index.js
@@ -3,6 +3,9 @@
    path: 'certificate',
    name: '证书查询',
    component: () => import('@/views/main/certificate/index.vue'),
    meta: {
      loginRequired: true
    }
  },
]
src/router/main/examTicket/index.js
@@ -3,6 +3,9 @@
    path: 'examTicket',
    name: '准考证查询',
    component: () => import('@/views/main/examTicket/index.vue'),
    meta: {
      loginRequired: true
    }
  },
]
src/router/main/home/index.js
@@ -3,6 +3,9 @@
    path: 'home',
    name: '首页',
    component: () => import('@/views/main/home/index.vue'),
    meta: {
      loginRequired: false
    }
  },
]
src/router/main/notice/index.js
@@ -3,11 +3,17 @@
    path: 'noticeList',
    name: '公告列表',
    component: () => import('@/views/main/notice/list.vue'),
    meta: {
      loginRequired: false
    }
  },
  {
    path: 'noticeDetail/:id',
    name: '公告详情',
    component: () => import('@/views/main/notice/detail.vue'),
    meta: {
      loginRequired: false
    }
  },
]
src/router/main/score/index.js
@@ -3,6 +3,9 @@
    path: 'score',
    name: '成绩查询',
    component: () => import('@/views/main/score/index.vue'),
    meta: {
      loginRequired: true
    }
  },
]
src/stores/session.js
@@ -1,7 +1,17 @@
import { ref } from 'vue'
import { defineStore } from 'pinia'
export const useSessionStore = defineStore('session', () => {
  const userInfo = ref({})
  const userInfo = ref({
    id: '',
    nickName: '',
    idCard: '',
    idCardType: '',
    mobile: '',
    sex: '',
    avatar: '',
    idCardFrontImg: '',
    idCardBackImg: ''
  })
  function setUserInfo(data) {
    userInfo.value = data
src/utils/ruleGenerator.js
@@ -3,7 +3,7 @@
export const ruleGenerator = {
  // 必填字段
  required(message = '此字段为必填项') {
    return { required: true, message, trigger: 'blur' }
    return { required: true, message, trigger: ['blur', 'change'] }
  },
  
  // 长度限制
@@ -35,7 +35,7 @@
          callback(new Error('请输入正确的手机号码'))
        }
      },
      trigger: ['blur', 'change']
      trigger: ['blur']
    }
  },
  //验证码验证
@@ -65,21 +65,7 @@
      trigger: ['blur', 'change']
    }
  },
  // 多选框验证
  checkbox(message) {
    return {
      validator: (rule, value, callback) => {
        if (!value) {
          callback(new Error(message));
        } else {
          callback();
        }
      },
      trigger: 'change'
    }
  },
  // 邮箱验证
  email() {
    return {
@@ -93,5 +79,149 @@
      },
      trigger: 'blur'
    }
  }
  },
  // 大于等于0的正整数验证
  positiveInteger(min = 0) {
    return {
      validator: (rule, value, callback) => {
        if (!value) {
          callback()
        } else {
          // 检查是否为正整数且大于等于min
          const num = Number(value)
          const isPositiveInteger = Number.isInteger(num) && num >= min && num >= 0
          if (isPositiveInteger) {
            callback()
          } else {
            callback(new Error(min === 0 ? '请输入大于等于0的正整数' : `请输入大于等于${min}的正整数`))
          }
        }
      },
      trigger: ['blur', 'change']
    }
  },
  // 身份证号验证
  idCard() {
    return {
      validator: (rule, value, callback) => {
        if (!value) {
          callback()
          return
        }
        // 18位身份证正则表达式
        const reg18 = /^[1-9]\d{5}(19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/
        // 15位身份证正则表达式(老式)
        const reg15 = /^[1-9]\d{5}\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}$/
        if (reg18.test(value)) {
          // 18位身份证校验码验证
          const weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
          const checkCodes = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2']
          let sum = 0
          for (let i = 0; i < 17; i++) {
            sum += parseInt(value[i]) * weights[i]
          }
          const checkCode = checkCodes[sum % 11]
          if (checkCode.toUpperCase() === value[17].toUpperCase()) {
            callback()
          } else {
            callback(new Error('请输入正确的身份证号码'))
          }
        } else if (reg15.test(value)) {
          // 15位身份证校验通过
          callback()
        } else {
          callback(new Error('请输入正确的身份证号码'))
        }
      },
      trigger: ['blur', 'change']
    }
  },
  // 统一社会信用代码验证
  creditCode() {
    return {
      validator: (rule, value, callback) => {
        if (!value) {
          callback()
          return
        }
        // 统一社会信用代码由18位字符组成:1位登记管理部门代码 + 1位机构类别代码 + 6位登记管理机关行政区划码 + 9位主体识别码(组织机构代码) + 1位校验码
        const reg = /^[0-9A-HJ-NPQRTUWXY]{2}\d{6}[0-9A-HJ-NPQRTUWXY]{10}$/
        if (!reg.test(value)) {
          callback(new Error('请输入正确的统一社会信用代码'))
          return
        }
        // 校验码计算
        const weights = [1, 3, 9, 27, 19, 26, 16, 17, 20, 29, 25, 13, 8, 24, 10, 30, 28]
        const checkCodes = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'T', 'U', 'W', 'X', 'Y']
        let sum = 0
        for (let i = 0; i < 17; i++) {
          const char = value[i]
          // 将字符转换为对应的数值
          const num = char >= '0' && char <= '9' ? parseInt(char) : char.charCodeAt(0) - 55
          sum += num * weights[i]
        }
        const mod = 31
        const remainder = sum % mod
        const checkCode = checkCodes[(mod - remainder) % mod]
        if (checkCode === value[17].toUpperCase()) {
          callback()
        } else {
          callback(new Error('请输入正确的统一社会信用代码'))
        }
      },
      trigger: ['blur', 'change']
    }
  },
  // 金额验证(保留2位小数)
  money(min = 0) {
    return {
      validator: (rule, value, callback) => {
        if (!value) {
          callback()
          return
        }
        // 金额正则:整数部分不限位数,小数部分最多2位
        const reg = /^(0|[1-9]\d*)(\.\d{1,2})?$/
        if (!reg.test(value)) {
          callback(new Error('请输入正确的金额'))
          return
        }
        const num = parseFloat(value)
        if (num < min) {
          callback(new Error(`金额不能小于${min}`))
        } else {
          callback()
        }
      },
      trigger: ['blur', 'change']
    }
  },
    // 多选框验证
  checkbox(message) {
    return {
      validator: (rule, value, callback) => {
        if (!value) {
          callback(new Error(message));
        } else {
          callback();
        }
      },
      trigger: 'change'
    }
  },
}
src/views/h5/faceAuth/components/auditDialog.vue
File was deleted
src/views/h5/faceAuth/components/camera.vue
File was deleted
src/views/h5/faceAuth/index.vue
File was deleted
src/views/h5/index.vue
File was deleted
src/views/h5/login/bind.vue
File was deleted
src/views/h5/login/index.vue
File was deleted
src/views/h5/login/redirect.vue
File was deleted
src/views/h5/signup/BaiduMap.vue
File was deleted
src/views/h5/signup/index.vue
File was deleted
src/views/h5/verify/form.vue
File was deleted
src/views/h5/verify/index.vue
File was deleted
src/views/h5/verify/noAccess.vue
File was deleted
src/views/login/index.vue
@@ -32,11 +32,17 @@
            </template>
          </el-input>
        </el-form-item>
        <el-form-item prop="agreement" style="height: 30px;">
        <!-- <el-form-item prop="agreement" style="height: 30px;">
          <el-checkbox v-model="form.agreement" label="同意xxx服务协议" size="large" />
        </el-form-item>
        </el-form-item> -->
        <el-button class="mt-1" @click="submitLogin()" :loading="loginLoading" type="primary" size="large" style="width: 100%;">
        <el-button
          class="mt-3"
          @click="submitLogin()"
          :loading="loginLoading"
          type="primary" size="large"
          style="width: 100%;"
        >
          <el-text class="text-lg text-white">登录</el-text>
        </el-button>
      </el-form>
@@ -55,8 +61,18 @@
            <el-radio :value="0">女</el-radio>
          </el-radio-group>
        </el-form-item>
        <el-form-item prop="idCard">
          <el-input v-model="form.idCard" placeholder="请输入身份证号" />
        <el-form-item prop="idCardType">
          <el-select v-model="form.idCardType" placeholder="证件类型">
            <el-option
              v-for="item in idCardTypeItems"
              :key="item.value"
              :label="item.label"
              :value="item.value"
            />
          </el-select>
        </el-form-item>
        <el-form-item prop="idCard" :rules="[$rules.required('请输入证件号码'), form.idCardType == 1 ? $rules.idCard('请输入正确的身份证号'): '' ]">
          <el-input v-model="form.idCard" placeholder="请输入证件号码" />
        </el-form-item>
        <el-form-item prop="mobile">
          <el-input v-model="form.mobile" placeholder="请输入手机号" />
@@ -79,11 +95,17 @@
            </template>
          </el-input>
        </el-form-item>
        <el-form-item prop="agreement" style="height: 30px;">
        <!-- <el-form-item prop="agreement" style="height: 30px;">
          <el-checkbox v-model="form.agreement" label="同意xxx服务协议" size="large" />
        </el-form-item>
        </el-form-item> -->
        <el-button class="mt-1" @click="submitRegister()" type="primary" size="large" style="width: 100%;">
        <el-button
          class="mt-5"
          :loading="registerLoading"
          @click="submitRegister()"
          type="primary" size="large"
          style="width: 100%;"
        >
          <el-text class="text-lg text-white">注册</el-text>
        </el-button>
      </el-form>
@@ -91,7 +113,7 @@
      <el-row class="mt-7" justify="center">
        <el-button v-if="loginType!='register'" text type="primary" @click="changeLoginType('register')">注册账号</el-button>
        <el-divider v-if="loginType!='register'" direction="vertical" class="m-0 mt-1 mx-4" style="height: 24px !important" />
        <el-button v-if="loginType!='qrCode'" text type="primary" @click="changeLoginType('qrCode')">二维码登录</el-button>
        <el-button v-if="loginType!='qrCode'" text type="primary" @click="changeLoginType('qrCode')">微信扫码登录</el-button>
        <el-divider v-if="loginType=='register'" direction="vertical" class="m-0 mt-1 mx-4" style="height: 24px !important" />
        <el-button v-if="loginType!='mobile'" text type="primary" @click="changeLoginType('mobile')">手机号登录</el-button>
      </el-row>
@@ -103,6 +125,8 @@
import { useLoginStore } from '@/stores/login.js'
import { useSessionStore } from '@/stores/session.js'
import { storeToRefs } from 'pinia';
import idCardTypeItems from '@/assets/json/idCardTypeItems.json'
import { tokenUtils } from '@/utils/axios.js';
export default {
  components: {},
  setup() {
@@ -116,14 +140,15 @@
      form: {
        name: '',
        sex: 1,
        idCardType: 1,
        idCard: '',
        mobile: '13537719675',
        mobile: '',
        code: '',
        agreement: false
      },
      idCardTypeItems: idCardTypeItems,
      rules: {
        name: [ this.$rules.required('请输入姓名') ],
        idCard: [ this.$rules.required('请输入身份证号') ],
        mobile: [ this.$rules.required('请输入手机号'), this.$rules.phone() ],
        code: [ this.$rules.required('请填写验证码'), this.$rules.code() ],
        agreement: [ this.$rules.checkbox('请阅读并勾选同意协议') ]
@@ -131,7 +156,8 @@
      countdown: 180,
      countdownInterval: null,
      sendCodeLoading: false,
      loginLoading: false
      loginLoading: false,
      registerLoading: false
    }
  },
  computed: {
@@ -151,15 +177,45 @@
    this.clearCountdownInterval()
  },
  watch: {
    loginDialogVisible: function(val) {
      if (!val) {
        this.clearCountdownInterval()
      }
    }
  },
  methods: {
    async sendCode() {
      try {
        await this.$refs.accountForm.validateField('mobile')
        this.startCountdownInterval()
        this.$message.success('已发送验证码,请注意查收')
        let ref = null
        let scene = 0
        if (this.loginType == 'mobile') {
          ref = this.$refs.accountForm
          scene = 33
        } else if (this.loginType == 'register') {
          ref = this.$refs.registerForm
          scene = 32
        }
        const validate = await ref.validateField('mobile')
        if (validate) {
          const data = {
            captchaVerification: '',
            mobile: this.form.mobile,
            scene: scene,
          }
          this.sendCodeLoading = true
          this.$axios.post('/system/auth/send-sms-code', data).then(res => {
            if (res.data.code == 0) {
              this.startCountdownInterval()
              this.$message.success('已发送验证码,请注意查收')
            } else {
              this.$message.error(res.data.msg || '获取验证码失败')
            }
          }).finally(() => {
            this.sendCodeLoading = false
          })
        }
      } catch (error) {
        console.log(error)
        this.$message.error('请输入手机号码')
      }
    },
@@ -183,29 +239,57 @@
      try {
        await this.$refs.accountForm.validate()
        this.loginLoading = true
        setTimeout(() => {
          localStorage.setItem('accessToken', '123456')
          this.getUserInfo()
          this.$message.success('登录成功')
          this.loginDialogVisible = false
        const data = {
          mobile: this.form.mobile,
          code: this.form.code
        }
        this.$axios.post('/system/kw/examinee/login', data).then(res => {
          if (res.data.code == 0) {
            const resData = res.data.data || {}
              tokenUtils.setTokens(resData.accessToken, resData.refreshToken)
              this.getUserInfo()
              this.$message.success('登录成功')
              this.loginDialogVisible = false
          } else {
            this.$message.error(res.data.msg)
          }
        }).finally(() => {
          this.loginLoading = false
        }, 1000)
        })
      } catch (error) {
        console.log()
      }
    },
    getUserInfo() {
      this.setUserInfo({
        id: '123456',
        username: '黄婷婷',
      this.$axios.get('/system/kw/examinee/profile').then(res => {
        if (res.data.code == 0) {
          const resData = res.data.data || {}
          this.setUserInfo(resData)
        } else {
          this.$message.error(res.data.msg)
        }
      })
    },
    async submitRegister() {
      try {
        await this.$refs.registerForm.validate()
        const validate = await this.$refs.registerForm.validate()
        if (validate) {
          const data = { ...this.form }
          this.$axios.post('/system/kw/examinee/register', data).then(res => {
            if (res.data.code == 0) {
              const resData = res.data.data || {}
              tokenUtils.setTokens(resData.accessToken, resData.refreshToken)
              this.getUserInfo()
              this.$message.success('注册成功')
              this.loginDialogVisible = false
            } else {
              this.$message.error(res.data.msg)
            }
          })
        }
      } catch (error) {
        console.log()
        console.log(error)
        this.$message.error('请检查必填字段')
      }
    },
    changeLoginType(type) {
src/views/main/appraisalPlan/index.vue
@@ -227,10 +227,28 @@
  computed: {
    filterList() {
      return this.planList.slice((this.filter.pageNo-1)*this.filter.pageSize, this.filter.pageNo*this.filter.pageSize)
    },
    query() {
      return this.$route.query
    }
  },
  watch: {
    signupDialogVisible: {
      handler: function(val) {
        if (!val) {
          this.$router.replace({ path: this.$route.path })
        }
      }
    }
  },
  created() {
    this.getPlanList()
  },
  mounted() {
    if (this.query.signupDialogFlag == '1') {
      this.signup({ id: this.query.appraisalId })
    }
  },
  methods: {
    getPlanList() {
@@ -331,13 +349,18 @@
      }, 400)
    },
    signup(item) {
      if (this.userInfo.id) {
        this.signupDialogVisible = true
        this.currentPlan = item
      } else {
        this.loginDialogVisible = true
        this.$message.primary('请先登录')
      }
      this.signupDialogVisible = true
      this.currentPlan = item
      this.$router.replace({ path: this.$route.path, query: { appraisalId: item.id, signupDialogFlag: 1 } })
      // if (this.userInfo.id) {
      //   this.signupDialogVisible = true
      //   this.currentPlan = item
      //   this.$router.replace({ path: this.$route.path, query: { appraisalId: item.id, signupDialogFlag: 1 } })
      // }
      // else {
      //   this.loginDialogVisible = true
      //   this.$message.primary('请先登录')
      // }
    }
  }
}
src/views/main/center/index.vue
@@ -32,25 +32,28 @@
            <el-col :span="22">
              <div v-if="activeTab=='toExam'" class="p-4 px-6">
                <el-row align="middle">
                  <el-text class="font-medium">姓名:{{ form.name }}</el-text>
                  <el-text class="font-medium">姓名:{{ userInfo.nickname }}</el-text>
                  <el-button text class="ml-2">
                    <Icon icon="ix:pen-filled" width="18" height="18"  style="color: black" />
                  </el-button>
                </el-row>
                <el-row align="middle">
                  <el-text class="font-medium">性别:{{ form.gender }}</el-text>
                  <el-text class="font-medium">性别:{{ sexText }}</el-text>
                  <el-button text class="ml-2">
                    <Icon icon="ix:pen-filled" width="18" height="18"  style="color: black" />
                  </el-button>
                </el-row>
                <el-row align="middle">
                  <el-text class="font-medium">手机号:{{ form.mobilePhone }}</el-text>
                  <el-text class="font-medium">手机号:{{ userInfo.mobile }}</el-text>
                  <el-button text class="ml-2">
                    <Icon icon="ix:pen-filled" width="18" height="18"  style="color: black" />
                  </el-button>
                </el-row>
                <el-row align="middle" class="my-1">
                  <el-text class="font-medium">证件类型:{{ idCardTypeText }}</el-text>
                </el-row>
                <el-row align="middle">
                  <el-text class="font-medium">身份证号:{{ form.idCard }}</el-text>
                  <el-text class="font-medium">证件正反面:{{ userInfo.idCard }}</el-text>
                  <el-button text class="ml-2">
                    <Icon icon="ix:pen-filled" width="18" height="18"  style="color: black" />
                  </el-button>
@@ -59,13 +62,13 @@
                  <el-text class="font-medium">身份证正反面:</el-text>
                </el-row>
                <el-row>
                  <UploadIdCard v-model="form.idCardFace" :accept="['jpg', 'png']" type="face"></UploadIdCard>
                  <UploadIdCard v-model="form.idCardEmblem" :accept="['jpg', 'png']" type="emblem"></UploadIdCard>
                  <UploadIdCard v-model="userInfo.idCardFace" :accept="['jpg', 'png']" type="face"></UploadIdCard>
                  <UploadIdCard v-model="userInfo.idCardEmblem" :accept="['jpg', 'png']" type="emblem"></UploadIdCard>
                </el-row>
                <el-row align="middle" class="mt-2">
                  <el-text class="font-medium">证书/准考证头像:</el-text>
                </el-row>
                <UploadBtn v-model="form.certificateAvatar" listType="picture-card" :accept="['jpg', 'png']" ></UploadBtn>
                <UploadBtn v-model="userInfo.certificateAvatar" listType="picture-card" :accept="['jpg', 'png']" ></UploadBtn>
                <el-row class="mt-5">
                  <el-text>绑定微信:暂未绑定微信</el-text>
@@ -121,7 +124,12 @@
    }
  },
  computed: {
    sexText() {
      return this.$property.sex[`key_${this.userInfo.sex}`]
    },
    idCardTypeText() {
      return this.$property.idCardType[`key_${this.userInfo.idCardType}`]
    }
  },
  watch: {
    activeTab: {
src/views/main/components/CustomLoading.vue
New file
@@ -0,0 +1,86 @@
<template>
  <div class="loading-content">
    <div class="loading-spinner">
      <div class="spinner-ring"></div>
      <div class="spinner-ring"></div>
      <div class="spinner-ring"></div>
    </div>
    <div class="loading-text">加载中...</div>
  </div>
</template>
<style>
.loading-container {
  display: flex;
  justify-content: center;
  align-items: center;
}
.loading-content {
  text-align: center;
}
.loading-spinner {
  position: relative;
  width: 60px;
  height: 60px;
  margin: 0 auto 16px;
}
.spinner-ring {
  position: absolute;
  width: 100%;
  height: 100%;
  border: 3px solid transparent;
  border-top-color: #007aff;
  border-radius: 50%;
  animation: spin 1.2s linear infinite;
}
.spinner-ring:nth-child(1) {
  animation-delay: 0s;
}
.spinner-ring:nth-child(2) {
  width: 80%;
  height: 80%;
  top: 10%;
  left: 10%;
  animation-delay: 0.15s;
  border-top-color: #66b1ff;
}
.spinner-ring:nth-child(3) {
  width: 60%;
  height: 60%;
  top: 20%;
  left: 20%;
  animation-delay: 0.3s;
  border-top-color: #a0cfff;
}
@keyframes spin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}
.loading-text {
  font-size: 14px;
  color: #666;
  animation: pulse 1.5s ease-in-out infinite;
}
@keyframes pulse {
  0%, 100% {
    opacity: 0.6;
  }
  50% {
    opacity: 1;
  }
}
</style>
src/views/main/components/MyFooter.vue
@@ -1,6 +1,6 @@
<template>
  <div class="custom-footer">
    <el-row justify="center">
    <!-- <el-row justify="center">
      <el-select 
        v-for="(item,index) in selectList"
        v-model="item.value" 
@@ -16,7 +16,7 @@
          :value="item.value"
        />
      </el-select>
    </el-row>
    </el-row> -->
    <el-row>
      <el-text class="text-white font-medium text-default">
        ©2025 深圳市企鹅网络科技有限公司 粤ICP备15026064号   粤公网安备 44030502007327 号
@@ -58,7 +58,7 @@
<style scoped>
.custom-footer {
  width: 100%;
  min-height: 185px !important;
  min-height: 60px !important;
  display: flex;
  padding: 20px;
  flex-direction: column;
src/views/main/components/MyHeader.vue
@@ -5,7 +5,7 @@
      <el-dropdown v-if="userInfo.id" placement="bottom">
        <el-row  align="middle">
          <Icon icon="fa:user-circle" width="22" height="22" class="mr-2"  style="color: #fff" />
          <el-text class="text-white cursor-p text-lg font-bold">黄婷婷</el-text>
          <el-text class="text-white cursor-p text-lg font-bold">{{ userInfo.nickname }}</el-text>
          <Icon icon="flowbite:caret-down-solid" width="22" height="22" class="ml-2"  style="color: #fff" />
        </el-row>
        <template #dropdown>
@@ -15,8 +15,8 @@
          </el-dropdown-menu>
        </template>
      </el-dropdown>
      <el-button  v-else color="#007AFF">
        <el-text class="text-white cursor-p text-lg font-bold" @click="login">登 录</el-text>
      <el-button @click="login" v-else color="#007AFF">
        <el-text class="text-white cursor-p text-lg font-bold">登 录</el-text>
      </el-button>
    </el-row>
  </div>
@@ -26,7 +26,7 @@
import { useSessionStore } from '@/stores/session.js' 
import { useLoginStore } from '@/stores/login.js'
import { storeToRefs } from 'pinia';
import { tokenUtils } from '@/utils/axios.js'
export default {
  setup() {
    const { loginDialogVisible } = storeToRefs(useLoginStore())
@@ -36,13 +36,13 @@
  },
  data() {
    return {
    }
  },
  created() {
    const token =  localStorage.getItem('accessToken')
    if (token) {
    if (tokenUtils.getAccessToken()) {
      this.getUserInfo()
    } else {
      this.$emit('getUserInfoFlag')
    }
  },
  methods: {
@@ -50,12 +50,16 @@
      this.loginDialogVisible = true
    },
    getUserInfo() {
      setTimeout(() => {
        this.setUserInfo({
          id: '12345',
          username: '黄婷婷'
        })
      }, 1000)
      this.$axios.get('/system/kw/examinee/profile').then(res => {
        if (res.data.code == 0) {
          const resData = res.data.data || {}
          this.setUserInfo(resData)
        } else {
          this.$message.error(res.data.msg)
        }
      }).finally(() => {
        this.$emit('getUserInfoFlag')
      })
    },
    gotoCenter() {
      this.$router.push('/main/center')
@@ -64,9 +68,12 @@
      this.$messageBox.confirm('确定要退出登录吗', '提示', 
      { confirmButtonText: '确定', cancelButtonText: '取消', type: 'error' }).then(res => {
        if (res == 'confirm') {
          localStorage.removeItem('accessToken')
          tokenUtils.clearTokens()
          this.setUserInfo({})
          this.$message.success('退出登录')
          if (this.$route.meta.loginRequired) {
            this.$router.replace('/main/home')
          }
        }
      })
    }
src/views/main/index.vue
@@ -1,15 +1,17 @@
<template>
  <el-container direction="vertical" style="height: 100vh;">
    <el-header class="p-0">
      <MyHeader></MyHeader>
      <MyHeader @getUserInfoFlag="getUserInfoFlag=true"></MyHeader>
    </el-header>
    <el-main :style="{height: mainHeight}" class="custom-main p-0" >
      <div>
      <div v-if="getUserInfoFlag">
        <router-view></router-view>
      </div>
      <div v-else :style="{'min-height': `${height-150}px`}" class="loading-container">
        <CustomLoading />
      </div>
      <MyFooter></MyFooter>
    </el-main>
    <LoginDialog />
  </el-container>
</template>
@@ -18,21 +20,37 @@
import MyHeader from '@/views/main/components/MyHeader.vue'
import MyFooter from '@/views/main/components/MyFooter.vue'
import LoginDialog from '@/views/login/index.vue'
import CustomLoading from '@/views/main/components/CustomLoading.vue'
export default {
  components: {
    MyHeader,
    MyFooter,
    LoginDialog
    LoginDialog,
    CustomLoading
  },
  setup() {
    const { height } = useWindowSize()
    return { height }
  },
  data() {
    return {
      getUserInfoFlag: false
    }
  },
  computed: {
    mainHeight: function() {
      return `${this.height - 60}px`
    }
  },
  created() {
    this.getUserInfo()
  },
  methods: {
    getUserInfo() {
    }
  }
}
</script>