wwf
22 小时以前 737179a0ce34147269cccf288fecd0e7bb4c309b
成绩查询+个人中心
8个文件已修改
14个文件已添加
1571 ■■■■■ 已修改文件
public/favicon.ico 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/uploadBox/emblem.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/uploadBox/face.png 补丁 | 查看 | 原始文档 | blame | 历史
src/router/main/center/index.js 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/main/certificate/index.js 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/main/examTicket/index.js 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/main/index.js 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/main/score/index.js 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/tool.js 51 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/main/appraisalPlan/components/signupDialog.vue 190 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/main/appraisalPlan/components/signupSuccessDialog.vue 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/main/appraisalPlan/index.vue 143 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/main/center/index.vue 176 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/main/center/mySignup/index.vue 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/main/certificate/index.vue 202 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/main/components/MyHeader.vue 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/main/components/UploadBtn.vue 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/main/components/UploadIdCard.vue 239 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/main/examTicket/index.vue 170 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/main/home/index.vue 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/main/notice/list.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/main/score/index.vue 182 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
public/favicon.ico

src/assets/images/uploadBox/emblem.png
src/assets/images/uploadBox/face.png
src/router/main/center/index.js
New file
@@ -0,0 +1,9 @@
const score = [
  {
    path: 'center',
    name: '个人中心',
    component: () => import('@/views/main/center/index.vue'),
  },
]
export default score
src/router/main/certificate/index.js
New file
@@ -0,0 +1,9 @@
const certificate = [
  {
    path: 'certificate',
    name: '证书查询',
    component: () => import('@/views/main/certificate/index.vue'),
  },
]
export default certificate
src/router/main/examTicket/index.js
New file
@@ -0,0 +1,9 @@
const examTicket = [
  {
    path: 'examTicket',
    name: '准考证查询',
    component: () => import('@/views/main/examTicket/index.vue'),
  },
]
export default examTicket
src/router/main/index.js
@@ -1,11 +1,19 @@
import home from '@/router/main/home/index.js'
import notice from '@/router/main/notice/index.js'
import appraisalPlan from '@/router/main/appraisalPlan'
import examTicket from '@/router/main/examTicket/index.js'
import score from '@/router/main/score/index.js'
import certificate from '@/router/main/certificate/index.js'
import center from '@/router/main/center/index.js'
const mainRouter = [
  ...home,
  ...notice,
  ...appraisalPlan
  ...appraisalPlan,
  ...examTicket,
  ...score,
  ...certificate,
  ...center
]
const router = [
  {
src/router/main/score/index.js
New file
@@ -0,0 +1,9 @@
const score = [
  {
    path: 'score',
    name: '成绩查询',
    component: () => import('@/views/main/score/index.vue'),
  },
]
export default score
src/utils/tool.js
@@ -5,12 +5,12 @@
 * @returns {string} 图片的完整URL
 */
export const getImageUrl = (imageName) => {
  try {
    return new URL('../assets/images/' + imageName, import.meta.url).href;
  } catch (error) {
    console.warn(`Failed to load image: ${imageName}`, error);
    return "";
  }
  // 使用 Vite 的 glob 功能动态导入图片资源
  const modules = import.meta.glob('../assets/images/**/*.{png,jpg,jpeg,gif,svg,webp}', { eager: true });
  // 移除开头的斜杠
  const cleanName = imageName.startsWith('/') ? imageName.slice(1) : imageName;
  const path = `../assets/images/${cleanName}`;
  return modules[path]?.default || '';
};
export const getUUID = () => {
  let s = []
@@ -66,11 +66,39 @@
  }
}
export const getOccupationName = (code) => {
export const getOccupationName = (id) => {
  const { occupationItems } = useOptionItemsStore()
  const obj = occupationItems.find(ele => ele.code == code)
  const obj = occupationItems.find(ele => ele.id == id)
  
  return obj?.name || ''
  return obj?.occupationJob || ''
}
const levelKey = {
  '5': '五级',
  '4': '四级',
  '3': '三级',
  '2': '二级',
  '1': '一级'
}
export const getLevelItems = (occupationId) => {
  const { occupationItems } = useOptionItemsStore()
  const obj = occupationItems.find(ele => ele.id == occupationId)
  if (!obj) return []
  let levelItems = []
  obj.levelStr.split(',').forEach(ele => {
    levelItems.push({ name: levelKey[ele], value: ele },)
  })
  return levelItems || []
}
export const getLevelName = (levelStr) => {
  let levelList = levelStr?.split(',') || []
  let tempList = []
  levelList.forEach(ele => {
    tempList.push(levelKey[ele])
  })
  return tempList.join(',')
}
export const getJobName = (occupationCode, jobCode) => {
@@ -82,4 +110,9 @@
  if (!job) return ''
  return job.jobName || ''
}
export const getFileUrlName = (url = '') => {
  const fullFilename = url.substring(url.lastIndexOf('/') + 1);
  return fullFilename || ''
}
src/views/main/appraisalPlan/components/signupDialog.vue
New file
@@ -0,0 +1,190 @@
<template>
  <el-dialog
    v-model="dialogFlag"
    width="700"
    title="填写报名信息"
    align-center
    :show-close="false"
    :close-on-click-modal="false"
    :close-on-press-escape="false"
  >
    <el-divider class="m-0"></el-divider>
    <div class="py-7">
      <el-row align="middle">
        <el-col :span="3" style="display: flex; flex-direction: column; align-items: center;">
          <el-row>
            <el-tag round :color="active==1?'#0069ff':'#999999'" style="width: 36px;height: 36px;">
              <el-text class="text-white text-lg">1</el-text>
            </el-tag>
          </el-row>
          <el-row class="mt-1"><el-text :style="{color: active==1?'#0069ff':'#666666'}">填写资料</el-text></el-row>
        </el-col>
        <el-col :span="18">
          <div style="width: 100%;border-top: 1px solid #c3c3c3; margin-top: -10px;"></div>
        </el-col>
        <el-col :span="3" style="display: flex; flex-direction: column; align-items: center;">
          <el-row>
            <el-tag round :color="active==2?'#0069ff':'#fff'" style="width: 36px;height: 36px;border-color: #999999;">
              <el-text class="text-lg" :style="{color: active==2?'#fff':'#999999' }">2</el-text>
            </el-tag>
          </el-row>
          <el-row class="mt-1"><el-text :style="{color: active==2?'#0069ff':'#666666'}">确认信息</el-text></el-row>
        </el-col>
      </el-row>
      <el-form ref="form" :model="signupInfo" :rules="rules" class="pb-6">
        <template v-if="active==1">
          <contentTitle title="考试信息" />
          <div class="px-3">
            <el-form-item>
              <el-text>评价职业:</el-text>
              <el-text class="text-black">{{ signupInfo.occupationJob }}</el-text>
            </el-form-item>
            <el-form-item>
              <el-text>评价等级:</el-text>
              <el-text class="text-black">{{ signupInfo.levelText }}</el-text>
            </el-form-item>
          </div>
          <contentTitle title="考试科目" />
          <div class="px-3">
            <el-form-item label="科目" prop="subject">
              <el-checkbox-group v-model="signupInfo.subject">
                <el-checkbox
                  v-for="(subject,index) in subjectItems"
                  :key="`subject${index}`"
                  :label="subject.label"
                  :value="subject.value"
                />
              </el-checkbox-group>
            </el-form-item>
          </div>
          <div class="px-3">
            <el-form-item label="姓名" prop="name" class="flex-1 mr-6">
              <el-input v-model="signupInfo.name" />
            </el-form-item>
            <el-form-item label="性别" prop="gender" class="flex-1 mr-6">
              <el-radio-group v-model="signupInfo.gender">
                <el-radio :value="1">男</el-radio>
                <el-radio :value="2">女</el-radio>
              </el-radio-group>
            </el-form-item>
            <el-form-item label="手机号" prop="mobilePhone" class="flex-1 mr-6">
              <el-input v-model="signupInfo.mobilePhone" />
            </el-form-item>
            <el-form-item label="身份证号" prop="idNumber" class="flex-1 mr-6">
              <el-input v-model="signupInfo.idNumber" />
            </el-form-item>
          </div>
        </template>
        <template v-else-if="active == 2">
          <contentTitle title="考试信息" />
          <div class="px-3">
            <el-form-item>
              <el-text>评价职业:</el-text>
              <el-text class="text-black">{{ signupInfo.occupationJob }}</el-text>
            </el-form-item>
            <el-form-item>
              <el-text>评价等级:</el-text>
              <el-text class="text-black">{{ signupInfo.levelText }}</el-text>
            </el-form-item>
          </div>
          <contentTitle title="考试科目" />
        </template>
      </el-form>
    </div>
    <el-divider class="m-0"></el-divider>
    <template #footer>
      <el-row justify="end">
        <el-button @click="close">取消</el-button>
        <el-button type="primary" @click="lastStep" v-if="active==2">上一步</el-button>
        <el-button type="primary" @click="nextStep" v-if="active==1">下一步</el-button>
        <el-button type="primary" @click="confirm" :loading="confirmLoading" v-if="active==2">提交报名</el-button>
      </el-row>
    </template>
  </el-dialog>
</template>
<script>
import contentTitle from '@/views/main/components/contentTitle.vue'
export default {
  components: {
    contentTitle
  },
  data() {
    return {
      dialogFlag: false,
      active: 1,
      subjectItems: [
        { label: '理论知识', value: 'theory' },
        { label: '实操技能', value: 'operation' }
      ],
      signupInfo: {
        occupationJob: '互联网营销师',
        levelText: '三级',
        subject: [],
        name: '黄婷婷',
        gender: 2,
        mobilePhone: '13537710000',
        idNumber: '445224200000000'
      },
      rules: {
        subject: [ this.$rules.required() ],
        name: [ this.$rules.required() ],
        gender: [ this.$rules.required() ],
        mobilePhone: [ this.$rules.required() ],
        idNumber: [ this.$rules.required() ],
      },
      confirmLoading: false
    }
  },
  props: {
    modelValue: {
      type: Boolean,
      default: false
    },
    currentPlan: {
      type: Object,
      default: () => {
        return {}
      }
    }
  },
  watch: {
    modelValue(val) {
      this.dialogFlag = val
    },
    dialogFlag(val) {
      this.$emit('update:modelValue', val)
    },
  },
  mounted() {
  },
  methods: {
    lastStep() {
      if (this.active == 2) {
        this.active--
      }
    },
    async nextStep() {
      if (this.active == 1) {
        // try {
        //   await this.$refs.form.validateField('code')
        // } catch(error) {
        //   return
        // }
      }
      this.active++
    },
    close() {
      this.dialogFlag = false
    },
    confirm() {
      this.dialogFlag = false
      this.active = 1
      this.$emit('signupSuccess')
    }
  }
}
</script>
<style scoped>
</style>
src/views/main/appraisalPlan/components/signupSuccessDialog.vue
New file
@@ -0,0 +1,63 @@
<template>
  <el-dialog
    v-model="dialogFlag"
    width="700"
    title="报名成功"
    align-center
    :show-close="false"
    :close-on-click-modal="false"
    :close-on-press-escape="false"
  >
    <el-divider class="m-0"></el-divider>
    <div class="py-7">
      <el-row justify="center">
        <Icon icon="iconoir:check-circle-solid" width="70" height="70"  style="color: #1ba53a" />
      </el-row>
      <el-row justify="center" class="mt-2">
        <el-text class="text-black text-lg font-medium">提交报名成功,请耐心等待审核结果。</el-text>
      </el-row>
      <el-row justify="center" class="mt-2">
        <el-text style="color: #666666;">审核结果会以短信形式发送给您,您也可以到【用户中心】-【我的报名】查看审核结果。</el-text>
      </el-row>
    </div>
    <el-divider class="m-0"></el-divider>
    <template #footer>
      <el-row justify="end">
        <el-button type="primary" @click="dialogFlag=false">好的</el-button>
      </el-row>
    </template>
  </el-dialog>
</template>
<script>
export default {
  components: {
  },
  data() {
    return {
      dialogFlag: false,
    }
  },
  props: {
    modelValue: {
      type: Boolean,
      default: false
    },
  },
  watch: {
    modelValue(val) {
      this.dialogFlag = val
    },
    dialogFlag(val) {
      this.$emit('update:modelValue', val)
    },
  },
  mounted() {
  },
  methods: {
  }
}
</script>
<style scoped>
</style>
src/views/main/appraisalPlan/index.vue
@@ -8,7 +8,7 @@
          <el-text class="text-title">评价计划</el-text>
        </el-row>
        <el-row class="mt-5">
        <el-row class="mt-5 custom-input">
          <el-input 
            v-model="filter.keyword" 
            style="width: 739px;height: 70px;"
@@ -24,7 +24,7 @@
      </div>
    </div>
    <div class="main-content py-4">
      <el-form-item label="等级:" class="mb-6">
      <el-form-item label="等级:" class="mb-4">
        <el-radio-group  v-model="filter.level">
          <el-radio
            v-for="(item,index) in levelItems"
@@ -35,7 +35,7 @@
          </el-radio>
        </el-radio-group>
      </el-form-item>
      <el-form-item label="考点:" class="mb-6">
      <el-form-item label="考点:" class="mb-4">
        <el-select v-model="filter.area" placeholder="筛选地市" style="width: 240px">
          <el-option
            v-for="item in areaItems"
@@ -46,6 +46,38 @@
        </el-select>
      </el-form-item>
      <div v-if="calendarFlag">
        <el-row>
          <el-text class="text-lg ">评价计划月历</el-text>
        </el-row>
        <el-form-item class="my-2">
          <el-select v-model="year" placeholder="筛选年份" style="width: 240px">
            <el-option
              v-for="item in yearItems"
              :key="item.value"
              :label="item.label"
              :value="item.value"
            />
          </el-select>
        </el-form-item>
        <el-table :data="monthList" border style="width: 100%" class="mb-6">
          <el-table-column
            v-for="(item,index) in monthHeaders"
            :prop="item.value"
            :label="item.label"
            :key="`month${index}`"
            align="center"
          >
            <template #default="{ row }">
              <div v-if="row[item.value]">
                <Icon icon="iconamoon:check-duotone" width="22" height="22"  style="color: var(--el-color-primary)" />
              </div>
              <span v-else></span>
            </template>
          </el-table-column>
        </el-table>
      </div>
      <el-table 
        :data="filterList" 
        table-layout="auto" 
@@ -61,8 +93,24 @@
          :align="item.align || 'start'"
          :sortable="item.sortable"
        >
          <template #signupTime="{ row }">
            {{ row }}
          <template #default="{ row }">
            <div v-if="item.value=='signupTime'">
              <el-row>报名开始时间:{{ row.startTime }}</el-row>
              <el-row>报名结束时间:{{ row.endTime }}</el-row>
            </div>
            <div v-if="item.value=='orgInfo'">
              <el-row>机构名称:{{ row.orgInfo.name }}</el-row>
              <el-row>机构联系人:{{ row.orgInfo.concatName }}</el-row>
              <el-row>机构联系方式:{{ row.orgInfo.mobile }}</el-row>
            </div>
            <div v-if="item.value=='operation'">
              <el-row @click="signup(row)">
                <el-text v-if="row.signupFlag && userInfo.id">已报名</el-text>
                <el-text v-else tag="ins" class="text-primary cursor-p">
                  报名考试
                </el-text>
              </el-row>
            </div>
          </template>
        </el-table-column>
      </el-table>
@@ -78,12 +126,38 @@
        />
      </el-row>
    </div>
    <signupDialog
      v-model="signupDialogVisible"
      :currentPlan="currentPlan"
      @signupSuccess="signupSuccessDialogVisible=true"
    >
    </signupDialog>
    <signupSuccessDialog
      v-model="signupSuccessDialogVisible"
    ></signupSuccessDialog>
  </div>
  
</template>
<script>
import { useLoginStore } from '@/stores/login.js'
import { useSessionStore } from '@/stores/session.js'
import { storeToRefs } from 'pinia';
import signupDialog from '@/views/main/appraisalPlan/components/signupDialog.vue'
import signupSuccessDialog from '@/views/main/appraisalPlan/components/signupSuccessDialog.vue'
export default {
  components: {
    signupDialog,
    signupSuccessDialog
  },
  setup() {
    const { userInfo } = storeToRefs(useSessionStore())
    const { loginDialogVisible } = storeToRefs(useLoginStore())
    return { userInfo, loginDialogVisible }
  },
  data() {
    return {
      page: 1,
@@ -110,7 +184,7 @@
      ],
      noticeList: [],
      headers: [
        { label: '职业(工种)名称', value: 'occupationJob' },
        { label: '职业(工种)名称', value: 'occupationJob'},
        { label: '等级', value: 'level' },
        { label: '考点', value: 'examAddress' },
        { label: '评价计划名称', value: 'planName', width: 150 },
@@ -118,7 +192,36 @@
        { label: '评价机构信息', value: 'orgInfo' },
        { label: '操作', value: 'operation', align: 'center' },
      ],
      planList: []
      planList: [],
      calendarFlag: true,
      year: '2025',
      yearItems: [
        { label: '2022年', value: '2022' },
        { label: '2023年', value: '2023' },
        { label: '2024年', value: '2024' },
        { label: '2025年', value: '2025' },
        { label: '2026年', value: '2026' },
      ],
      monthHeaders: [
        { label: '1月', value: '1' },
        { label: '2月', value: '2' },
        { label: '3月', value: '3' },
        { label: '4月', value: '4' },
        { label: '5月', value: '5' },
        { label: '6月', value: '6' },
        { label: '7月', value: '7' },
        { label: '8月', value: '8' },
        { label: '9月', value: '9' },
        { label: '10月', value: '10' },
        { label: '11月', value: '11' },
        { label: '12月', value: '12' },
      ],
      monthList: [
        { 1: false, 2: false, 3: true, 4: true, 5: false, 6: false, 7: true, 8: false, 9: false, 10: true, 11: true, 12: false, }
      ],
      signupDialogVisible: false,
      currentPlan: {},
      signupSuccessDialogVisible: false
    }
  },
  computed: {
@@ -146,7 +249,8 @@
              name: '中山市技师学院',
              concatName: '吴*宁',
              mobile: '13424571164'
            }
            },
            signupFlag: false
          },
          {
            id: '2',
@@ -160,7 +264,8 @@
              name: '中山市技师学院',
              concatName: '吴*宁',
              mobile: '13424571164'
            }
            },
            signupFlag: true
          },
          {
            id: '3',
@@ -188,7 +293,8 @@
              name: '中山市技师学院',
              concatName: '吴*宁',
              mobile: '13424571164'
            }
            },
            signupFlag: false
          },
          {
            id: '5',
@@ -202,7 +308,8 @@
              name: '中山市技师学院',
              concatName: '吴*宁',
              mobile: '13424571164'
            }
            },
            signupFlag: false
          },
          {
            id: '6',
@@ -216,12 +323,22 @@
              name: '中山市技师学院',
              concatName: '吴*宁',
              mobile: '13424571164'
            }
            },
            signupFlag: false
          },
        ]
        this.totalCount = 6
      }, 400)
    },
    signup(item) {
      if (this.userInfo.id) {
        this.signupDialogVisible = true
        this.currentPlan = item
      } else {
        this.loginDialogVisible = true
        this.$message.primary('请先登录')
      }
    }
  }
}
@@ -233,7 +350,7 @@
  background-repeat: no-repeat, no-repeat, repeat; /* 分别设置 */
  background-size: cover;
}
:deep(.el-input__inner) {
.custom-input :deep(.el-input__inner) {
  padding-left: 10px;
  font-size: 18px;
}
src/views/main/center/index.vue
New file
@@ -0,0 +1,176 @@
<template>
  <div>
    <div class="content-bg">
      <div class="main-content" style="z-index: 10;">
        <ReturnBtn></ReturnBtn>
        <el-row>
          <el-text class="text-title">个人中心</el-text>
        </el-row>
        <el-card shadow="never" class="mt-4" :style="{ height: contentHeight + 'px'}">
          <el-row style="height: 100%;">
            <el-col :span="2">
              <el-row
                v-for="(tab,index) in tabList"
                :key="`tab${index}`"
                style="height: 50%;width: 100%;border-right: 1px solid #e4e7ed;"
                justify="center"
                align="middle"
                class="cursor-p"
                @click="activeTab=tab.value"
                :class="{ 'active-tab': tab.value==activeTab }"
              >
                <div>
                  <el-row justify="center">
                    <Icon :icon="tab.iconName" width="24" height="24" :style="{color: tab.value==activeTab?'#0069FF':'#333333'}" />
                    </el-row>
                  <el-row class="mt-1 font-medium">{{ tab.label }}</el-row>
                </div>
              </el-row>
            </el-col>
            <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-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-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-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.idCard }}</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="mt-2">
                  <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>
                </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>
                <el-row class="mt-5">
                  <el-text>绑定微信:暂未绑定微信</el-text>
                  <el-text tag="ins" class="ml-6 font-medium text-primary">扫码绑定</el-text>
                </el-row>
              </div>
              <div v-else-if="activeTab=='alreadyExam'">
                <mySignup></mySignup>
              </div>
            </el-col>
          </el-row>
        </el-card>
      </div>
    </div>
  </div>
</template>
<script>
import UploadIdCard from '@/views/main/components/UploadIdCard.vue'
import { useLoginStore } from '@/stores/login.js'
import { useSessionStore } from '@/stores/session.js'
import { storeToRefs } from 'pinia';
import mySignup from '@/views/main/center/mySignup/index.vue'
export default {
  components: {
    mySignup,
    UploadIdCard,
  },
  setup() {
    const { userInfo } = storeToRefs(useSessionStore())
    const { loginDialogVisible } = storeToRefs(useLoginStore())
    return { userInfo, loginDialogVisible }
  },
  data() {
    return {
      form: {
        name: '黄婷婷',
        gender: '女',
        mobilePhone: '13000000000',
        idCard: '441481155511116622',
        idCardFace: [],
        idCardEmblem: [],
        certificateAvatar: [],
      },
      tabList: [
        { label: '个人信息', iconName: 'mdi:layers-triple', value: 'toExam' },
        { label: '我的报名', iconName: 'ant-design:file-search-outlined', value: 'alreadyExam' },
      ],
      activeTab: 'toExam',
      contentHeight: 580,
      examList: [],
      searchKeyword: '',
    }
  },
  computed: {
  },
  watch: {
    activeTab: {
      handler: function() {
        this.getExamList()
      },
      immediate: true
    }
  },
  created() {
  },
  methods: {
    getExamList() {
      if (this.activeTab == 'toExam') {
        this.examList = [
        ]
      } else if (this.activeTab == 'alreadyExam') {
        this.examList = [
          { occupationJob: '职业培训师-二级', batch: '2025年12月职业培训师第一批' },
          { occupationJob: '职业培训师-二级', batch: '2025年12月职业培训师第一批' },
          { occupationJob: '职业培训师-二级', batch: '2025年12月职业培训师第一批' },
          { occupationJob: '职业培训师-二级', batch: '2025年12月职业培训师第一批' },
          { occupationJob: '职业培训师-二级', batch: '2025年12月职业培训师第一批' },
          { occupationJob: '职业培训师-二级', batch: '2025年12月职业培训师第一批' },
          { occupationJob: '职业培训师-二级', batch: '2025年12月职业培训师第一批' },
          { occupationJob: '职业培训师-二级', batch: '2025年12月职业培训师第一批' },
          { occupationJob: '职业培训师-二级', batch: '2025年12月职业培训师第一批' },
          { occupationJob: '职业培训师-二级', batch: '2025年12月职业培训师第一批' },
          { occupationJob: '职业培训师-二级', batch: '2025年12月职业培训师第一批' },
          { occupationJob: '职业培训师-二级', batch: '2025年12月职业培训师第一批' },
        ]
      }
    }
  }
}
</script>
<style scoped>
.content-bg {
  height: 240px;
  background-image: url('@/assets/images/contentBg.png');
  background-repeat: no-repeat, no-repeat, repeat; /* 分别设置 */
  background-size: cover;
}
.active-tab {
  background-color: #EEF5FF;
  border: 1px solid #0069FF !important;
  color: #0069FF !important;
}
</style>
src/views/main/center/mySignup/index.vue
New file
@@ -0,0 +1,71 @@
<template>
  <div class="p-4 px-2">
    <el-row class="">
      <Filtrate :filter="filter" :refresh="getList" :loadingFlag="ListLoadingFlag">
        <el-row style="width: 100%;">
          <el-select style="flex: 1;" class="mr-2" v-model="filter.status" placeholder="全部状态">
            <el-option
              v-for="item in statusItems"
              :key="item.status"
              :label="item.label"
              :value="item.status"
            />
          </el-select>
          <el-input
            style="flex: 3;"
            class="mr-2"
            v-model="filter.search"
            placeholder="搜索评价机构名称/统一社会信用代码"
          >
            <template #prefix>
              <el-icon class="el-input__icon"><search /></el-icon>
            </template>
          </el-input>
        </el-row>
      </Filtrate>
    </el-row>
    <DataTable
      v-model:filter="filter"
      :headers="headers"
      :list="list"
      :totalCount="totalCount"
    ></DataTable>
  </div>
</template>
<script>
export default {
  data() {
    return {
      filter: {
        status: ''
      },
      statusItems: [
        { label: '全部状态', status: '' },
        { label: '审核中', status: 'auditing' },
        { label: '审核不通过', status: 'reject' },
        { label: '审核通过', status: 'pass' },
      ],
      headers: [
        { label: '评价计划名称', value: 'planName' },
        { label: '职业名称', value: 'occupationJob' },
        { label: '等级', value: 'level' },
        { label: '考点', value: 'address' },
        { label: '状态', value: 'status' },
        { label: '操作', value: 'operation' }
      ],
      ListLoadingFlag: false,
    }
  },
  methods: {
    getList() {
    },
  }
}
</script>
<style lang="scss" scoped>
</style>
src/views/main/certificate/index.vue
New file
@@ -0,0 +1,202 @@
<template>
  <div>
    <div class="content-bg">
      <div class="main-content">
        <ReturnBtn></ReturnBtn>
        <el-row class="mt-6">
          <el-text class="text-title">证书查询</el-text>
        </el-row>
      </div>
    </div>
    <div class="main-content py-4" style="margin-top: -130px;">
      <el-card shadow="never" class="p-4" style="min-height: 500px;">
        <el-form-item>
          <el-input v-model="keyword" placeholder="搜索关键字">
            <template #prefix>
              <el-icon class="el-input__icon"><search /></el-icon>
            </template>
          </el-input>
        </el-form-item>
        <el-card
          v-for="(certificate,index) in certificateList"
          :key="`certificate${index}`"
          class="p-4 mb-4"
          shadow="never"
        >
          <el-row justify="space-between" align="middle">
            <div>
              <el-row><el-text class="text-lg text-black font-medium">{{ certificate.occupationJob }}</el-text></el-row>
              <el-row class="mt-2">
                <el-text>{{ certificate.batch }}</el-text>
              </el-row>
            </div>
            <div>
              <el-button text style="background-color: #F0F7FF;">
                <el-row class="mr-1">
                  <Icon icon="flowbite:eye-solid" width="20" height="20"  style="color: #007AFF" />
                </el-row>
                <el-row class="text-primary">查看准考证</el-row>
              </el-button>
              <el-button text style="background-color: #F0F7FF;">
                <el-row class="mr-1">
                  <Icon icon="material-symbols:download" width="20" height="20"  style="color: #007AFF" />
                </el-row>
                <el-row class="text-primary">下载准考证</el-row>
              </el-button>
            </div>
          </el-row>
        </el-card>
        <el-row justify="center" v-if="certificateList.length==0">
          <el-text>暂无数据~</el-text>
        </el-row>
      </el-card>
    </div>
  </div>
</template>
<script>
import { useLoginStore } from '@/stores/login.js'
import { useSessionStore } from '@/stores/session.js'
import { storeToRefs } from 'pinia';
export default {
  components: {
  },
  setup() {
    const { userInfo } = storeToRefs(useSessionStore())
    const { loginDialogVisible } = storeToRefs(useLoginStore())
    return { userInfo, loginDialogVisible }
  },
  data() {
    return {
      certificateList: [],
      keyword: '',
      fieldList: [
        { label: '姓名', value: 'name' },
        { label: '身份证号', value: 'idCard' },
        { label: '准考证号', value: 'examCard' },
        { label: '理论知识成绩', value: 'theoryScore' },
        { label: '操作技能成绩', value: 'operationScore' },
        { label: '成绩状态', value: 'statusText' },
      ]
    }
  },
  computed: {},
  watch: {
  },
  created() {
    this.getCertificateList()
  },
  methods: {
    getCertificateList() {
      this.certificateList = [
        {
          id: 1,
          occupationJob: '电工(维修电工)-四级',
          batch: '2025年12月公共营养师第1批',
          name: '黄婷婷',
          idCard: '440203198906302518',
          examCard: '250610110746092',
          theoryScore: '--',
          operationScore: '--',
          status: 'auditing',
          statusText: '成绩审核中',
          foldFlag: false
        },
        {
          id: 2,
          occupationJob: '电工(维修电工)-四级',
          batch: '2025年12月公共营养师第1批',
          name: '黄婷婷',
          idCard: '440203198906302518',
          examCard: '250610110746092',
          theoryScore: '78分(合格)',
          operationScore: '85分(合格)',
          status: 'public',
          statusText: '成绩公示中',
          foldFlag: true
        },
        {
          id: 3,
          occupationJob: '电工(维修电工)-四级',
          batch: '2025年12月公共营养师第1批',
          name: '黄婷婷',
          idCard: '440203198906302518',
          examCard: '250610110746092',
          theoryScore: '78分(合格)',
          operationScore: '85分(合格)',
          status: 'effectiveness',
          statusText: '成绩已生效',
          foldFlag: true
        },
        {
          id: 4,
          occupationJob: '电工(维修电工)-四级',
          batch: '2025年12月公共营养师第1批',
          name: '黄婷婷',
          idCard: '440203198906302518',
          examCard: '250610110746092',
          theoryScore: '85分(合格)',
          operationScore: '78分(合格)',
          status: 'cancellation',
          statusText: '成绩已作废',
          failReason: '违规作弊',
          foldFlag: true
        },
        {
          id: 5,
          occupationJob: '电工(维修电工)-四级',
          batch: '2025年12月公共营养师第1批',
          name: '黄婷婷',
          idCard: '440203198906302518',
          examCard: '250610110746092',
          theoryScore: '85分(合格)',
          operationScore: '78分(合格)',
          status: 'cancellation',
          statusText: '成绩已作废',
          failReason: '违规作弊',
          foldFlag: true
        },
        {
          id: 6,
          occupationJob: '电工(维修电工)-四级',
          batch: '2025年12月公共营养师第1批',
          name: '黄婷婷',
          idCard: '440203198906302518',
          examCard: '250610110746092',
          theoryScore: '85分(合格)',
          operationScore: '78分(合格)',
          status: 'cancellation',
          statusText: '成绩已作废',
          failReason: '违规作弊',
          foldFlag: true
        },
      ]
    },
    getStyle(status) {
      let obj = {
        auditing: '#007AFF',
        public: '#FF9009',
        effectiveness: '#1BA53A',
        cancellation: '#FF4040',
      }
      return { color: obj[status] }
    }
  }
}
</script>
<style scoped>
.content-bg {
  height: 240px;
  background-image: url('@/assets/images/contentBg.png');
  background-repeat: no-repeat, no-repeat, repeat; /* 分别设置 */
  background-size: cover;
}
.active-tab {
  background-color: #EEF5FF;
  border: 1px solid #0069FF !important;
  color: #0069FF !important;
}
</style>
src/views/main/components/MyHeader.vue
@@ -10,7 +10,7 @@
        </el-row>
        <template #dropdown>
          <el-dropdown-menu>
            <el-dropdown-item>个人中心</el-dropdown-item>
            <el-dropdown-item @click="gotoCenter()">个人中心</el-dropdown-item>
            <el-dropdown-item @click="logout()">退出登录</el-dropdown-item>
          </el-dropdown-menu>
        </template>
@@ -57,6 +57,9 @@
        })
      }, 1000)
    },
    gotoCenter() {
      this.$router.push('/main/center')
    },
    logout() {
      this.$messageBox.confirm('确定要退出登录吗', '提示', 
      { confirmButtonText: '确定', cancelButtonText: '取消', type: 'error' }).then(res => {
src/views/main/components/UploadBtn.vue
@@ -77,12 +77,14 @@
</template>
<script>
import { getFileUrlName } from '@/utils/tool.js'
const pdf = 'application/pdf'
const xls = 'application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
const doc = 'application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document'
const image = 'image/*'
const jpg = 'image/jpeg'
const png = 'image/png'
const zip = 'application/zip,application/x-zip-compressed'
import { genFileId } from 'element-plus'
export default {
  data() {
@@ -101,7 +103,7 @@
    accept: {
      type: Array,
      default: () => {
        return ['pdf', 'xls', 'doc', 'image', 'jpg', 'png']
        return ['pdf', 'xls', 'doc', 'image', 'jpg', 'png', 'zip']
      }
    },
    limitFileCount: {
@@ -123,19 +125,26 @@
    modelValue: {
      handler: function() {
        this.list = this.modelValue
        this.list.forEach(ele => {
          if (!ele.name) {
            ele.name = getFileUrlName(ele.url)
          }
        })
      },
      immediate: true,
      deep: true
    },
    list: {
      handler: function(val) {
        this.$emit('update:modelValue', val)
      },
      immediate: true,
      deep: true
    }
  },
  computed: {
    acceptType() {
      let obj = { pdf, xls, doc, image, jpg, png }
      let obj = { pdf, xls, doc, image, jpg, png, zip }
      return this.accept.map(ele => obj[ele]).join(',')
    },
    tip() {
@@ -145,7 +154,8 @@
        doc: 'WORD',
        image: '图片',
        jpg: 'JPEG/JPG',
        png: 'PNG'
        png: 'PNG',
        zip: 'ZIP'
      }
      let tip = this.accept.map(ele => obj[ele]).join('、')
      return `支持${tip}类型文件` 
src/views/main/components/UploadIdCard.vue
New file
@@ -0,0 +1,239 @@
<template>
  <el-upload
    ref="upload"
    class="my-3 mr-4" action="#"
    v-model:file-list="list"
    :accept="acceptType"
    list-type="picture-card"
    :http-request="uploadRequest"
    :multiple="true"
    :limit="limitFileCount"
    :on-preview="handlePreview"
    :on-remove="handleRemove"
    :before-remove="beforeRemove"
    :on-exceed="handleExceed"
    :disabled="disabled"
    :class="{ hideUpload: hideUpload }"
  >
    <template #default>
      <el-image
        ref="backgroundImg"
        :src="$getImageUrl(`/uploadBox/${type}.png`)"
      >
      </el-image>
    </template>
    <template #file="{ file, index }">
      <el-image
        ref="previewImg"
        :src="file.url"
        :initial-index="initialPreviewImgIndex"
        :preview-src-list="filterPreviewImgList"
      >
      </el-image>
      <span class="el-upload-list__item-actions">
        <span @click="previewImage(index)">
          <el-icon><zoom-in /></el-icon>
        </span>
        <!-- <span @click="replaceUpload(index)">
          <el-icon><upload /></el-icon>
        </span> -->
        <span @click="deleteFileItem(index)">
          <el-icon><delete /></el-icon>
        </span>
      </span>
    </template>
  </el-upload>
  <el-dialog v-model="imgPreviewDialogFlag">
    <img w-full :src="previewUrl" alt="Preview Image" />
  </el-dialog>
</template>
<script>
import { getFileUrlName } from '@/utils/tool.js'
const pdf = 'application/pdf'
const xls = 'application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
const doc = 'application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document'
const image = 'image/*'
const jpg = 'image/jpeg'
const png = 'image/png'
import { genFileId } from 'element-plus'
export default {
  data() {
    return {
      list: [],
      imgPreviewDialogFlag: false,
      previewUrl: '',
      initialPreviewImgIndex: 0
    }
  },
  props: {
    listType: {
      type: String,
      default: 'text'
    },
    accept: {
      type: Array,
      default: () => {
        return ['image', 'jpg', 'png']
      }
    },
    limitFileCount: {
      type: Number,
      default: 1
    },
    modelValue: {
      type: Array,
      default: () => {
        return []
      }
    },
    disabled: {
      type: Boolean,
      default: false
    },
    type: {
      type: String,
      default: 'face'
    }
  },
  watch: {
    modelValue: {
      handler: function() {
        this.list = this.modelValue
        this.list.forEach(ele => {
          if (!ele.name) {
            ele.name = getFileUrlName(ele.url)
          }
        })
      },
      immediate: true,
      deep: true
    },
    list: {
      handler: function(val) {
        this.$emit('update:modelValue', val)
      },
      immediate: true,
      deep: true
    }
  },
  computed: {
    acceptType() {
      let obj = { pdf, xls, doc, image, jpg, png }
      return this.accept.map(ele => obj[ele]).join(',')
    },
    tip() {
      let obj = {
        pdf: 'PDF',
        xls: 'EXCEL',
        doc: 'WORD',
        image: '图片',
        jpg: 'JPEG/JPG',
        png: 'PNG'
      }
      let tip = this.accept.map(ele => obj[ele]).join('、')
      return `支持${tip}类型文件`
    },
    againUploadFlag() {
      return this.limitFileCount == 1  && this.list.length == 1
    },
    filterPreviewImgList() {
      let list = this.list.map(ele => ele.url )
      return list
    },
    hideUpload() {
      return this.list.length >= this.limitFileCount
    }
  },
  methods: {
    uploadRequest(UploadRequestOptions) {
      const data = {
        file: UploadRequestOptions.file,
        directory: ''
      }
      this.$axios.post('/infra/file/upload', data, {
        headers: { 'Content-Type': "multipart/form-data" }
      }).then(res => {
        let index = this.list.findIndex(ele => ele.uid == data.file.uid)
        if (res.data.code == 0) {
          let index = this.list.findIndex(ele => ele.uid == data.file.uid)
          if (index != -1) {
            this.list[index].uploadStatus = 'success'
            this.list[index].url = res.data.data
          }
          this.$message.success('上传成功')
        } else {
          if (index != -1) {
            this.list[index].uploadStatus = 'fail'
          }
          this.$message.error(res.data.msg || '上传失败')
        }
      })
    },
    deleteFileItem(index) {
      this.list.splice(index, 1)
    },
    previewImage(index) {
      this.initialPreviewImgIndex = index
      this.$refs.previewImg?.showPreview()
    },
    replaceUpload() {
    },
    handleRemove() {},
    beforeRemove() {},
    handlePreview() {},
    handleExceed(file) {
      if (this.againUploadFlag) {
        this.$refs.upload?.clearFiles()
        let newFile = file[0]
        newFile.uid = genFileId()
        this.$refs.upload?.handleStart(newFile)
        this.$refs.upload?.submit()
      } else {
        this.$message.error(`最多支持上传 ${this.limitFileCount} 个文件`)
      }
    }
  }
}
</script>
<style scoped>
.hideUpload :deep(.el-upload--picture-card) {
  display: none !important;
}
.file-box {
  display: flex;
  color: #007AFF;
  align-content: center;
  background-color: #ECF5FF;
  padding: 8px;
  font-size: 15px;
  line-height: 16px;
}
:deep(.el-upload--picture-card), :deep(.el-upload-list__item) {
  width: 160px !important;
  height: 90px !important;
  padding: 1px 0;
}
.el-upload-list__item-actions {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.5); /* 半透明黑色蒙版 */
  display: flex;
  justify-content: center;
  align-items: center;
  opacity: 0; /* 默认透明 */
  transition: opacity .3s; /* 添加过渡动画效果 */
}
/* 鼠标悬停时显示蒙版 */
.el-upload-list__item:hover .el-upload-list__item-actions {
  opacity: 1;
}
</style>
src/views/main/examTicket/index.vue
New file
@@ -0,0 +1,170 @@
<template>
  <div>
    <div class="content-bg">
      <div class="main-content" style="z-index: 10;">
        <ReturnBtn></ReturnBtn>
        <el-row class="mt-6">
          <el-text class="text-title">准考证查询</el-text>
        </el-row>
        <el-card shadow="never" class="mt-6" :style="{ height: contentHeight + 'px'}">
          <el-row style="height: 100%;">
            <el-col :span="2">
              <el-row
                v-for="(tab,index) in tabList"
                :key="`tab${index}`"
                style="height: 50%;width: 100%;border-right: 1px solid #e4e7ed;"
                justify="center"
                align="middle"
                class="cursor-p"
                @click="activeTab=tab.value"
                :class="{ 'active-tab': tab.value==activeTab }"
              >
                <div>
                  <el-row justify="center">
                    <Icon :icon="tab.iconName" width="24" height="24"  :style="{color: tab.value==activeTab?'#0069FF':'#333333'}" />
                    </el-row>
                  <el-row class="mt-1 font-medium">{{ tab.label }}</el-row>
                </div>
              </el-row>
            </el-col>
            <el-col :span="22">
              <el-row class="p-4 text-primary font-medium" justify="space-between">
                <span>共 {{ examList.length }} 个准考证</span>
                <el-form-item class="mb-0">
                  <el-input v-model="searchKeyword" placeholder="搜索关键字">
                    <template #append>
                      <el-button>查询</el-button>
                    </template>
                  </el-input>
                </el-form-item>
              </el-row>
              <el-divider class="m-0"></el-divider>
              <el-scrollbar ref="scrollbarRef" class="p-4" :style="{ height: (contentHeight - 70)  + 'px'}" always>
                <el-card
                  v-for="(exam,index) in examList"
                  :key="`exam${index}`"
                  class="p-4 mb-4"
                  shadow="never"
                >
                  <el-row justify="space-between" align="middle">
                    <div>
                      <el-row><el-text class="text-lg text-black font-medium">{{ exam.occupationJob }}</el-text></el-row>
                      <el-row class="mt-2">
                        <el-text>{{ exam.batch }}</el-text>
                      </el-row>
                    </div>
                    <div>
                      <el-button text style="background-color: #F0F7FF;">
                        <el-row class="mr-1">
                          <Icon icon="flowbite:eye-solid" width="20" height="20"  style="color: #007AFF" />
                        </el-row>
                        <el-row class="text-primary">查看准考证</el-row>
                      </el-button>
                      <el-button text style="background-color: #F0F7FF;">
                        <el-row class="mr-1">
                          <Icon icon="material-symbols:download" width="20" height="20"  style="color: #007AFF" />
                        </el-row>
                        <el-row class="text-primary">下载准考证</el-row>
                      </el-button>
                    </div>
                  </el-row>
                </el-card>
                <el-row justify="center" v-if="examList.length==0">
                  <el-text>暂无数据~</el-text>
                </el-row>
              </el-scrollbar>
            </el-col>
          </el-row>
        </el-card>
      </div>
    </div>
    <div class="main-content py-4">
    </div>
  </div>
</template>
<script>
import { useLoginStore } from '@/stores/login.js'
import { useSessionStore } from '@/stores/session.js'
import { storeToRefs } from 'pinia';
export default {
  components: {
  },
  setup() {
    const { userInfo } = storeToRefs(useSessionStore())
    const { loginDialogVisible } = storeToRefs(useLoginStore())
    return { userInfo, loginDialogVisible }
  },
  data() {
    return {
      tabList: [
        { label: '待考试', iconName: 'gravity-ui:pencil-to-line', value: 'toExam' },
        { label: '已考试', iconName: 'ic:round-history', value: 'alreadyExam' },
      ],
      activeTab: 'toExam',
      contentHeight: 530,
      examList: [],
      searchKeyword: '',
    }
  },
  computed: {
  },
  watch: {
    activeTab: {
      handler: function() {
        this.getExamList()
      },
      immediate: true
    }
  },
  created() {
  },
  methods: {
    getExamList() {
      if (this.activeTab == 'toExam') {
        this.examList = [
        ]
      } else if (this.activeTab == 'alreadyExam') {
        this.examList = [
          { occupationJob: '职业培训师-二级', batch: '2025年12月职业培训师第一批' },
          { occupationJob: '职业培训师-二级', batch: '2025年12月职业培训师第一批' },
          { occupationJob: '职业培训师-二级', batch: '2025年12月职业培训师第一批' },
          { occupationJob: '职业培训师-二级', batch: '2025年12月职业培训师第一批' },
          { occupationJob: '职业培训师-二级', batch: '2025年12月职业培训师第一批' },
          { occupationJob: '职业培训师-二级', batch: '2025年12月职业培训师第一批' },
          { occupationJob: '职业培训师-二级', batch: '2025年12月职业培训师第一批' },
          { occupationJob: '职业培训师-二级', batch: '2025年12月职业培训师第一批' },
          { occupationJob: '职业培训师-二级', batch: '2025年12月职业培训师第一批' },
          { occupationJob: '职业培训师-二级', batch: '2025年12月职业培训师第一批' },
          { occupationJob: '职业培训师-二级', batch: '2025年12月职业培训师第一批' },
          { occupationJob: '职业培训师-二级', batch: '2025年12月职业培训师第一批' },
        ]
      }
    }
  }
}
</script>
<style scoped>
.content-bg {
  height: 240px;
  background-image: url('@/assets/images/contentBg.png');
  background-repeat: no-repeat, no-repeat, repeat; /* 分别设置 */
  background-size: cover;
}
.active-tab {
  background-color: #EEF5FF;
  border: 1px solid #0069FF !important;
  color: #0069FF !important;
}
</style>
src/views/main/home/index.vue
@@ -54,7 +54,15 @@
</template>
<script>
import { useLoginStore } from '@/stores/login.js'
import { useSessionStore } from '@/stores/session.js'
import { storeToRefs } from 'pinia';
export default {
  setup() {
    const { userInfo } = storeToRefs(useSessionStore())
    const { loginDialogVisible } = storeToRefs(useLoginStore())
    return { userInfo, loginDialogVisible }
  },
  data() {
    return {
      operationList: [
@@ -114,6 +122,11 @@
      this.$router.push('/main/noticeList')
    },
    goOperationPage(item) {
      if (item.value != 'appraisalPlan' && !this.userInfo.id) {
        this.loginDialogVisible = true
        this.$message.primary('请先登录')
        return
      }
      this.$router.push(`/main/${item.value}`)
    }
  }
src/views/main/notice/list.vue
@@ -8,7 +8,7 @@
          <el-text class="text-title">通知公告</el-text>
        </el-row>
        <el-row class="mt-5">
        <el-row class="mt-5 custom-input">
          <el-input 
            v-model="filter.keyword" 
            style="width: 739px;height: 70px;"
@@ -185,7 +185,7 @@
  background-repeat: no-repeat, no-repeat, repeat; /* 分别设置 */
  background-size: cover;
}
:deep(.el-input__inner) {
.custom-input :deep(.el-input__inner) {
  padding-left: 10px;
  font-size: 18px;
}
src/views/main/score/index.vue
New file
@@ -0,0 +1,182 @@
<template>
  <div>
    <div class="content-bg">
      <div class="main-content">
        <ReturnBtn></ReturnBtn>
        <el-row class="mt-6">
          <el-text class="text-title">成绩查询</el-text>
        </el-row>
      </div>
    </div>
    <div class="main-content py-4" style="margin-top: -130px;">
      <el-card shadow="never" class="p-4" style="min-height: 500px;">
        <el-form-item>
          <el-input v-model="keyword" placeholder="搜索关键字">
            <template #prefix>
              <el-icon class="el-input__icon"><search /></el-icon>
            </template>
          </el-input>
        </el-form-item>
        <div v-for="(score,index) in scoreList"
          class="mt-3"
          :key="`scoreCard${index}`"
          style="border-radius: 5px;"
          :style="{ border: `1px solid ${score.foldFlag?'#dcdcdc':'#007aff'}` }"
        >
          <el-card shadow="never" class="p-3  cursor-p" style="background: #F7F7F7;border: none;" @click="score.foldFlag=!score.foldFlag">
            <el-row justify="space-between" align="middle">
              <div>
                <el-row><el-text class="text-lg text-black font-medium">{{ score.occupationJob }}</el-text></el-row>
                <el-row class="mt-1">
                  <el-text>{{ score.batch }}</el-text>
                </el-row>
              </div>
              <Icon v-if="score.foldFlag" icon="mingcute:down-line" width="22" height="22"  style="color: #333333" />
              <Icon v-else icon="mingcute:up-line" width="22" height="22"  style="color: #007AFF" />
            </el-row>
          </el-card>
          <div v-if="!score.foldFlag" class="px-4">
            <el-row v-for="(field,index) in fieldList"
              style="border-bottom: 1px solid #DCDCDC;"
              class="py-4"
              :key="`field${index}`"
            >
              <el-text style="width: 170px;color: #666666;" class="font-medium">{{ field.label }}</el-text>
              <el-text v-if="field.value == 'statusText'" class="font-medium">
                <div style="display: flex;flex-direction: column;">
                  <el-text :style="getStyle(score.status)" style="align-self: start;">{{ score.statusText }}</el-text>
                  <el-text v-if="score.status=='cancellation'">作废原因:{{ score.failReason }}</el-text>
                </div>
              </el-text>
              <el-text v-else class=" text-black font-medium">{{score[field.value]}}</el-text>
            </el-row>
          </div>
        </div>
        <el-row justify="center" v-if="scoreList.length==0">
          <el-text>暂无数据~</el-text>
        </el-row>
      </el-card>
    </div>
  </div>
</template>
<script>
import { useLoginStore } from '@/stores/login.js'
import { useSessionStore } from '@/stores/session.js'
import { storeToRefs } from 'pinia';
export default {
  components: {
  },
  setup() {
    const { userInfo } = storeToRefs(useSessionStore())
    const { loginDialogVisible } = storeToRefs(useLoginStore())
    return { userInfo, loginDialogVisible }
  },
  data() {
    return {
      scoreList: [],
      keyword: '',
      fieldList: [
        { label: '姓名', value: 'name' },
        { label: '身份证号', value: 'idCard' },
        { label: '准考证号', value: 'examCard' },
        { label: '理论知识成绩', value: 'theoryScore' },
        { label: '操作技能成绩', value: 'operationScore' },
        { label: '成绩状态', value: 'statusText' },
      ]
    }
  },
  computed: {
  },
  watch: {
  },
  created() {
    this.getScoreList()
  },
  methods: {
    getScoreList() {
      this.scoreList = [
        {
          id: 1,
          occupationJob: '电工(维修电工)-四级',
          batch: '2025年12月公共营养师第1批',
          name: '黄婷婷',
          idCard: '440203198906302518',
          examCard: '250610110746092',
          theoryScore: '--',
          operationScore: '--',
          status: 'auditing',
          statusText: '成绩审核中',
          foldFlag: false
        },
        {
          id: 2,
          occupationJob: '电工(维修电工)-四级',
          batch: '2025年12月公共营养师第1批',
          name: '黄婷婷',
          idCard: '440203198906302518',
          examCard: '250610110746092',
          theoryScore: '78分(合格)',
          operationScore: '85分(合格)',
          status: 'public',
          statusText: '成绩公示中',
          foldFlag: true
        },
        {
          id: 3,
          occupationJob: '电工(维修电工)-四级',
          batch: '2025年12月公共营养师第1批',
          name: '黄婷婷',
          idCard: '440203198906302518',
          examCard: '250610110746092',
          theoryScore: '78分(合格)',
          operationScore: '85分(合格)',
          status: 'effectiveness',
          statusText: '成绩已生效',
          foldFlag: true
        },
        {
          id: 4,
          occupationJob: '电工(维修电工)-四级',
          batch: '2025年12月公共营养师第1批',
          name: '黄婷婷',
          idCard: '440203198906302518',
          examCard: '250610110746092',
          theoryScore: '85分(合格)',
          operationScore: '78分(合格)',
          status: 'cancellation',
          statusText: '成绩已作废',
          failReason: '违规作弊',
          foldFlag: true
        },
      ]
    },
    getStyle(status) {
      let obj = {
        auditing: '#007AFF',
        public: '#FF9009',
        effectiveness: '#1BA53A',
        cancellation: '#FF4040',
      }
      return { color: obj[status] }
    }
  }
}
</script>
<style scoped>
.content-bg {
  height: 240px;
  background-image: url('@/assets/images/contentBg.png');
  background-repeat: no-repeat, no-repeat, repeat; /* 分别设置 */
  background-size: cover;
}
.active-tab {
  background-color: #EEF5FF;
  border: 1px solid #0069FF !important;
  color: #0069FF !important;
}
</style>