Ver Fonte

feat: 复核校验ui

zhangjie há 9 meses atrás
pai
commit
9d5bec4dce

+ 1 - 1
src/render/App.vue

@@ -1,5 +1,5 @@
 <template>
-  <qm-config-provider>
+  <qm-config-provider :autoInsertSpaceInButton="false">
     <a-spin
       :tip="appStore.loadingStatus.tip"
       :spinning="appStore.loadingStatus.spinning"

+ 0 - 0
src/render/assets/imgs/room-icon.png → src/render/assets/imgs/room_icon.png


+ 3 - 0
src/render/components.d.ts

@@ -11,6 +11,8 @@ declare module 'vue' {
     AButton: typeof import('@qmth/ui')['Button']
     ACard: typeof import('@qmth/ui')['Card']
     ACol: typeof import('@qmth/ui')['Col']
+    ACollapse: typeof import('@qmth/ui')['Collapse']
+    ACollapsePanel: typeof import('@qmth/ui')['CollapsePanel']
     ADivider: typeof import('@qmth/ui')['Divider']
     AForm: typeof import('@qmth/ui')['Form']
     AFormItem: typeof import('@qmth/ui')['FormItem']
@@ -18,6 +20,7 @@ declare module 'vue' {
     AInputNumber: typeof import('@qmth/ui')['InputNumber']
     AInputPassword: typeof import('@qmth/ui')['InputPassword']
     AModal: typeof import('@qmth/ui')['Modal']
+    APagination: typeof import('@qmth/ui')['Pagination']
     APopconfirm: typeof import('@qmth/ui')['Popconfirm']
     AProgress: typeof import('@qmth/ui')['Progress']
     ARadio: typeof import('@qmth/ui')['Radio']

+ 48 - 2
src/render/components/MyQuote/index.vue

@@ -1,10 +1,10 @@
 <template>
   <div :class="['my-quote', `is-${type}`]">
-    <template v-if="showIcon">
+    <div v-if="showIcon" class="my-quote-icon">
       <slot name="icon">
         <InfoCircleOutlined :rotate="180" />
       </slot>
-    </template>
+    </div>
 
     <slot>
       {{ message }}
@@ -31,3 +31,49 @@ const props = withDefaults(
   }
 );
 </script>
+
+<style lang="less" scoped>
+.my-quote {
+  background: #f2f3f5;
+  font-weight: 400;
+  font-size: 14px;
+  color: #262626;
+  line-height: 22px;
+  padding: 8px 8px 8px 26px;
+  border-left: 2px solid @text-color3;
+
+  &-icon {
+    display: inline-block;
+    vertical-align: middle;
+    margin-left: -16px;
+    margin-right: 4px;
+    margin-top: -2px;
+  }
+
+  &.is-primary {
+    border-color: @brand-color;
+
+    .my-quote-icon {
+      color: @brand-color;
+    }
+  }
+  &.is-error {
+    border-color: @error-color;
+    .my-quote-icon {
+      color: @error-color;
+    }
+  }
+  &.is-success {
+    border-color: @success-color;
+    .my-quote-icon {
+      color: @success-color;
+    }
+  }
+  &.is-warning {
+    border-color: @warning-color;
+    .my-quote-icon {
+      color: @warning-color;
+    }
+  }
+}
+</style>

+ 40 - 1
src/render/components/SimplePagination/index.vue

@@ -5,6 +5,7 @@
     :total="total"
     :default-page-size="pageSize"
     show-quick-jumper
+    :showTotal="(totalCount) => `共${totalCount}条`"
     :show-size-changer="false"
     @change="onChange"
   >
@@ -15,7 +16,6 @@
       <template v-else-if="type === 'next'">
         <CaretRightFilled />
       </template>
-      <template v-else-if="type === 'page'"></template>
       <component :is="originalElement" v-else></component>
     </template>
   </a-pagination>
@@ -41,3 +41,42 @@ function onChange(page: number, pageSize: number) {
   emit("change", page, pageSize);
 }
 </script>
+
+<style lang="less">
+.simple-pagination {
+  color: @text-color2;
+
+  .ant-pagination-item {
+    display: none;
+  }
+  .ant-pagination-prev,
+  .ant-pagination-next {
+    border: 1px solid @border-color1;
+    font-size: 10px;
+  }
+  .ant-pagination-disabled {
+    color: #d9d9d9;
+  }
+  .ant-pagination-total-text {
+    margin-right: 40px;
+  }
+  .ant-pagination-options-quick-jumper {
+    margin: 0;
+  }
+  .ant-pagination-options {
+    margin-left: 8px;
+    color: @text-color3;
+
+    input {
+      width: 32px;
+      background: #ffffff;
+      border-radius: 6px;
+      padding-left: 4px;
+      padding-right: 4px;
+      margin: 0 5px;
+      border: 1px solid @border-color1;
+      text-align: center;
+    }
+  }
+}
+</style>

+ 1 - 1
src/render/hooks/useTable.ts

@@ -60,7 +60,7 @@ export default function useTable<T extends Record<string, any>>(
     total,
     current: pageNumber,
     pageSize,
-    showTotal: (totalCount: number) => `共${totalCount}`,
+    showTotal: (totalCount: number) => `共${totalCount}`,
     showQuickJumper: true,
     showSizeChanger: false,
     onChange: toPage,

+ 1 - 1
src/render/store/modules/review/index.ts

@@ -8,7 +8,7 @@ interface ReviewState {
 
 export const useReviewStore = defineStore("review", {
   state: (): ReviewState => ({
-    tabkey: "",
+    tabKey: "review",
     curTask: null,
   }),
 

+ 38 - 0
src/render/styles/antui-reset.less

@@ -0,0 +1,38 @@
+.operation-cell {
+  .ant-btn-link {
+    padding: 0;
+    height: auto;
+    border: none;
+
+    + .ant-btn-link {
+      margin-left: 12px;
+    }
+  }
+}
+
+// ant-modal
+.ant-modal {
+  .ant-modal-content {
+    padding: 0;
+  }
+  .ant-modal-close {
+    top: 13px;
+    color: @text-color2;
+  }
+  .ant-modal-header {
+    padding: 12px 20px;
+    height: 48px;
+    background: linear-gradient(180deg, #ffffff 0%, #f2f3f5 100%);
+    border-bottom: 1px solid @border-color1;
+    margin: 0;
+  }
+  .ant-modal-title {
+    font-weight: 500;
+    font-size: 16px;
+    color: @text-color1;
+    line-height: 24px;
+  }
+  .ant-modal-body {
+    padding: 20px;
+  }
+}

+ 5 - 4
src/render/styles/index.less

@@ -1,4 +1,5 @@
-@import "animation.less";
-@import "reset.less";
-@import "base.less";
-@import "pages.less";
+@import "./animation.less";
+@import "./reset.less";
+@import "./antui-reset.less";
+@import "./base.less";
+@import "./pages.less";

+ 281 - 0
src/render/styles/pages.less

@@ -1,3 +1,4 @@
+// arbitrate
 .arbitrate {
   height: 100%;
   display: flex;
@@ -133,3 +134,283 @@
     }
   }
 }
+
+// review
+.review {
+  height: 100%;
+  overflow: hidden;
+  position: relative;
+
+  &-head {
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 100%;
+    padding: 11px 16px 10px;
+    height: 54px;
+    z-index: 9;
+    background-color: #fff;
+    border: 1px solid @background-color;
+
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+  }
+
+  &-title {
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+    line-height: 32px;
+    font-size: 14px;
+    padding-left: 30px;
+    width: calc(50% - 100px);
+
+    background-image: url(../assets/imgs/bg-student.png);
+    background-size: 22px 22px;
+    background-position: 0 5px;
+    background-repeat: no-repeat;
+  }
+
+  &-prev {
+    text-align: right;
+    width: calc(50% - 120px);
+  }
+
+  &-stat {
+    width: 240px;
+    text-align: center;
+    line-height: 32px;
+
+    > p {
+      display: inline-block;
+
+      &:first-child {
+        margin-right: 25px;
+      }
+
+      .anticon {
+        margin-right: 5px;
+      }
+    }
+  }
+
+  &-body {
+    position: absolute;
+    top: 54px;
+    left: 0;
+    bottom: 0;
+    right: 278px;
+    background-color: @background-color;
+  }
+
+  // action
+  &-action {
+    position: absolute;
+    width: 278px;
+    top: 54px;
+    right: 0;
+    bottom: 0;
+    z-index: 9;
+
+    .review-tabs {
+      height: 46px;
+      font-size: 0;
+      border-bottom: 1px solid @border-color1;
+    }
+    .review-tab {
+      position: relative;
+      display: inline-block;
+      vertical-align: middle;
+      line-height: 46px;
+      width: 50%;
+      text-align: center;
+      font-size: 14px;
+      color: @text-color3;
+      cursor: pointer;
+
+      &:hover {
+        color: @text-color1;
+      }
+
+      &.is-active {
+        color: @text-color1;
+
+        &::after {
+          content: "";
+          display: block;
+          position: absolute;
+          width: 56px;
+          height: 4px;
+          background: #165dff;
+          bottom: 0;
+          left: 50%;
+          transform: translateX(-50%);
+        }
+      }
+    }
+
+    .review-tbody {
+      position: absolute;
+      top: 46px;
+      bottom: 0;
+      left: 0;
+      right: 0;
+      overflow: auto;
+      z-index: 9;
+
+      &.tbody-history {
+        display: flex;
+        flex-direction: column;
+        justify-content: space-between;
+        overflow: hidden;
+
+        .review-tabs,
+        .ant-collapse,
+        .history-footer {
+          flex-grow: 0;
+          flex-shrink: 0;
+        }
+        .history-footer {
+          padding: 8px 16px;
+          color: @text-color2;
+        }
+        .history-list {
+          flex-grow: 2;
+          overflow-x: hidden;
+          overflow-y: auto;
+          border-top: 1px solid @border-color1;
+          border-bottom: 1px solid @border-color1;
+          padding: 0 8px;
+
+          .table {
+            width: 100%;
+            border-spacing: 0;
+            border-collapse: collapse;
+            text-align: left;
+            color: @text-color1;
+            line-height: 22px;
+
+            th {
+              color: @text-color2;
+              padding: 8px 8px 8px 12px;
+              font-weight: 400;
+            }
+            td {
+              padding: 0 8px 0 12px;
+              line-height: 24px;
+            }
+            tbody {
+              tr {
+                border-radius: 6px;
+                cursor: pointer;
+                &:hover,
+                &.is-active {
+                  background: #e8f3ff;
+                }
+              }
+            }
+          }
+        }
+      }
+
+      .ant-collapse-item:first-child {
+        .ant-collapse-header {
+          border-top: none;
+        }
+      }
+    }
+  }
+
+  // mark-pan
+  &-mark-pan {
+    position: fixed;
+    top: 200px;
+    left: 200px;
+    width: 212px;
+    width: 212px;
+    background: #f2f3f5;
+    box-shadow: 0px 10px 10px 0px rgba(54, 61, 89, 0.2);
+    overflow: hidden;
+
+    .pan-head {
+      height: 22px;
+      background: linear-gradient(180deg, #ffffff 0%, #f2f3f5 100%);
+      border-radius: 8px 8px 0px 0px;
+      border-bottom: 1px solid @border-color1;
+      text-align: center;
+      line-height: 20px;
+      font-size: 0;
+      cursor: move;
+
+      &::before {
+        content: "";
+        display: inline-block;
+        vertical-align: middle;
+
+        width: 40px;
+        height: 6px;
+        background: #d9d9d9;
+        box-shadow: inset 0px 1px 3px 0px rgba(0, 0, 0, 0.2);
+        border-radius: 3px;
+      }
+    }
+    .pan-body {
+      padding: 16px;
+
+      > p {
+        font-weight: 500;
+        font-size: 16px;
+        color: @warning-color;
+        line-height: 24px;
+        margin-bottom: 8px;
+
+        &:nth-last-of-type(1) {
+          margin-bottom: 16px;
+        }
+      }
+    }
+  }
+
+  // ant
+  .ant-collapse {
+    border-radius: 0;
+
+    &-item {
+      border-radius: 0 !important;
+      border: none !important;
+    }
+
+    &-header {
+      height: 34px;
+      line-height: 22px !important;
+      padding: 5px 16px !important;
+      border-top: 1px solid @border-color1;
+      border-bottom: 1px solid @border-color1;
+      background: linear-gradient(180deg, #ffffff 0%, #f2f3f5 100%);
+    }
+
+    &-header-text {
+      .anticon {
+        color: #bfbfbf;
+        margin-right: 8px;
+      }
+    }
+
+    &-expand-icon {
+      display: none !important;
+    }
+
+    &-content {
+      background-color: #fff !important;
+    }
+
+    &-content-box {
+      padding: 10px 0 10px 16px !important;
+    }
+  }
+  .ant-btn {
+    padding-left: 12px;
+    padding-right: 12px;
+  }
+}

+ 0 - 13
src/render/styles/reset.less

@@ -392,16 +392,3 @@ Ensure the default browser behavior of the `hidden` attribute.
   background-color: rgba(0, 0, 0, 0);
   border-radius: 0;
 }
-
-// ant-design
-.operation-cell {
-  .ant-btn-link {
-    padding: 0;
-    height: auto;
-    border: none;
-
-    + .ant-btn-link {
-      margin-left: 12px;
-    }
-  }
-}

+ 38 - 1
src/render/views/Review/ExportTypeDialog.vue

@@ -16,7 +16,7 @@
       </a-col>
       <a-col :span="12">
         <div class="type-box" @click="seleted('room')">
-          <img src="@/assets/imgs/scan_login_icon.png" alt="按考场导出" />
+          <img src="@/assets/imgs/room_icon.png" alt="按考场导出" />
           <p>按考场导出</p>
           <p>EXPORT BY TEST ROOM</p>
         </div>
@@ -42,3 +42,40 @@ function seleted(type: "student" | "room") {
   emit("confirm", type);
 }
 </script>
+
+<style lang="less" scoped>
+.type-box {
+  height: 140px;
+  border-radius: 6px;
+  padding: 26px;
+  border: 1px solid @border-color1;
+  text-align: center;
+  cursor: pointer;
+
+  &:hover {
+    background: #f3f4f6;
+  }
+
+  > img {
+    display: block;
+    height: 35px;
+    width: 32px;
+    margin: 0 auto;
+    margin-bottom: 12px;
+  }
+
+  > p:nth-of-type(1) {
+    height: 24px;
+    font-weight: 500;
+    font-size: 16px;
+    color: #14161a;
+    line-height: 24px;
+  }
+  > p:nth-of-type(2) {
+    height: 18px;
+    font-size: 10px;
+    color: #8c8c8c;
+    line-height: 18px;
+  }
+}
+</style>

+ 1 - 1
src/render/views/Review/ResetConfirmDialog.vue

@@ -2,7 +2,7 @@
   <a-modal v-model:open="visible" :width="424" style="top: 10vh" @ok="confirm">
     <template #title> 重置 </template>
 
-    <my-quote :message="quoteContent" type="error"></my-quote>
+    <my-quote class="m-b-16px" :message="quoteContent" type="error"></my-quote>
 
     <a-form
       ref="formRef"

+ 103 - 87
src/render/views/Review/ReviewAction.vue

@@ -1,24 +1,30 @@
 <template>
-  <div class="review-tabs">
-    <div
-      :class="['review-tab', { 'is-active': tabKey === 'review' }]"
-      @click="switchTab('review')"
-    >
-      复核校验
-    </div>
-    <div
-      :class="['review-tab', { 'is-active': tabKey === 'history' }]"
-      @click="switchTab('history')"
-    >
-      历史记录
+  <div class="review-action">
+    <div class="review-tabs">
+      <div
+        :class="[
+          'review-tab',
+          { 'is-active': reviewStore.tabKey === 'review' },
+        ]"
+        @click="switchTab('review')"
+      >
+        复核校验
+      </div>
+      <div
+        :class="[
+          'review-tab',
+          { 'is-active': reviewStore.tabKey === 'history' },
+        ]"
+        @click="switchTab('history')"
+      >
+        历史记录
+      </div>
     </div>
-  </div>
 
-  <div v-show="tabKey === 'review'" class="review-tab-body">
-    <a-collapse v-model:activeKey="reviewKey">
-      <a-collapse-panel key="1">
-        <template #header><FilterFilled />搜索条件 </template>
-        <a-space>
+    <div v-show="reviewStore.tabKey === 'review'" class="review-tbody">
+      <a-collapse v-model:activeKey="reviewKey" :bordered="false">
+        <a-collapse-panel key="1">
+          <template #header><FilterFilled />搜索条件 </template>
           <span>科目:</span>
           <a-select
             v-model:value="searchCourseCode"
@@ -26,15 +32,15 @@
             :options="courses"
             :field-names="fieldNames"
             filter-option
-            style="width: 200px"
+            style="width: 140px"
           ></a-select>
-          <a-button type="primary" @click="onSearch">搜索</a-button>
-        </a-space>
-      </a-collapse-panel>
-      <a-collapse-panel key="2">
-        <template #header><WarningFilled />导出异常 </template>
+          <a-button class="m-l-8px" type="primary" @click="onSearch"
+            >搜索</a-button
+          >
+        </a-collapse-panel>
+        <a-collapse-panel key="2">
+          <template #header><WarningFilled />导出异常 </template>
 
-        <a-space>
           <span>科目:</span>
           <a-select
             v-model:value="exportCourseCode"
@@ -42,23 +48,23 @@
             :options="courses"
             :field-names="fieldNames"
             filter-option
-            style="width: 200px"
+            style="width: 140px"
           ></a-select>
-          <a-button :loading="downloading" @click="onExport">导出</a-button>
-        </a-space>
-      </a-collapse-panel>
-      <a-collapse-panel key="3">
-        <template #header><PushpinFilled />复核标记 </template>
-
-        <a-radio-group v-model:value="result" @change="onMark">
-          <a-radio :value="1">正常</a-radio>
-          <a-radio :value="0">异常</a-radio>
-        </a-radio-group>
-      </a-collapse-panel>
-      <a-collapse-panel key="4">
-        <template #header><RightSquareFilled />重置 </template>
-
-        <a-space>
+          <a-button class="m-l-8px" :disabled="downloading" @click="onExport">
+            导出
+          </a-button>
+        </a-collapse-panel>
+        <a-collapse-panel key="3">
+          <template #header><PushpinFilled />复核标记 </template>
+
+          <a-radio-group v-model:value="result" @change="onMark">
+            <a-radio :value="1">正常</a-radio>
+            <a-radio :value="0">异常</a-radio>
+          </a-radio-group>
+        </a-collapse-panel>
+        <a-collapse-panel key="4">
+          <template #header><RightSquareFilled />重置 </template>
+
           <span>科目:</span>
           <a-select
             v-model:value="resetCourseCode"
@@ -66,52 +72,62 @@
             :options="courses"
             :field-names="fieldNames"
             filter-option
-            style="width: 200px"
+            style="width: 140px"
           ></a-select>
-          <qm-button type="danger" @click="onReset">重置</qm-button>
-        </a-space>
-      </a-collapse-panel>
-    </a-collapse>
-  </div>
-  <div v-show="tabKey === 'history'" class="review-tab-body">
-    <a-collapse collapsible="disabled">
-      <a-collapse-panel>
-        <template #header><PushpinFilled />复核标记 </template>
-
-        <a-radio-group v-model:value="historyResult" @change="onMark">
-          <a-radio :value="1">正常</a-radio>
-          <a-radio :value="0">异常</a-radio>
-        </a-radio-group>
-      </a-collapse-panel>
-    </a-collapse>
-    <div class="history-list">
-      <table>
-        <colgroup>
-          <col />
-          <col width="60" />
-        </colgroup>
-        <tr>
-          <th>准考证号</th>
-          <th>状态</th>
-        </tr>
-        <tr
-          v-for="(item, index) in dataList"
-          :key="item.id"
-          @click="setCurTask(index)"
-        >
-          <td>{{ item.examNumber }}</td>
-          <td :class="item.markStatus ? 'color-success' : 'color-error'">
-            {{ item.markStatus ? "正常" : "异常" }}
-          </td>
-        </tr>
-      </table>
+          <a-button class="m-l-8px" type="primary" danger @click="onReset"
+            >重置</a-button
+          >
+        </a-collapse-panel>
+      </a-collapse>
     </div>
-    <div class="history-footer">
-      <SimplePagination
-        :total="pagination.total"
-        :page-size="pagination.pageSize"
-        @change="toPage"
-      />
+    <div
+      v-show="reviewStore.tabKey === 'history'"
+      class="review-tbody tbody-history"
+    >
+      <a-collapse :activeKey="['1']" :bordered="false">
+        <a-collapse-panel key="1">
+          <template #header><PushpinFilled />复核标记 </template>
+
+          <a-radio-group v-model:value="historyResult" @change="onMark">
+            <a-radio :value="1">正常</a-radio>
+            <a-radio :value="0">异常</a-radio>
+          </a-radio-group>
+        </a-collapse-panel>
+      </a-collapse>
+      <div class="history-list">
+        <table class="table">
+          <colgroup>
+            <col />
+            <col width="80" />
+          </colgroup>
+          <thead>
+            <tr>
+              <th>准考证号</th>
+              <th>状态</th>
+            </tr>
+          </thead>
+          <tbody>
+            <tr
+              v-for="(item, index) in dataList"
+              :key="item.id"
+              :class="{ 'is-active': reviewStore.curTask?.id === item.id }"
+              @click="setCurTask(index)"
+            >
+              <td>{{ item.examNumber }}</td>
+              <td :class="item.markStatus ? 'color-success' : 'color-error'">
+                {{ item.markStatus ? "正常" : "异常" }}
+              </td>
+            </tr>
+          </tbody>
+        </table>
+      </div>
+      <div class="history-footer">
+        <SimplePagination
+          :total="pagination.total"
+          :page-size="pagination.pageSize"
+          @change="toPage"
+        />
+      </div>
     </div>
   </div>
 
@@ -153,7 +169,7 @@ const reviewStore = useReviewStore();
 const fieldNames = { label: "subjectName", value: "subjectCode" };
 
 // tab
-const reviewKey = ref("1");
+const reviewKey = ref(["1", "2", "3", "4"]);
 
 async function switchTab(key: "review" | "history") {
   reviewStore.setInfo({ tabKey: key });
@@ -178,7 +194,7 @@ const result = ref(1);
 const historyResult = ref(1);
 
 // history
-const curHistoryTaskIndex = res(0);
+const curHistoryTaskIndex = ref(0);
 const { dataList, pagination, loading, getList, toPage, setPageSize } =
   useTable<ReviewTaskListItem>(
     reviewTaskHistory,

+ 65 - 23
src/render/views/Review/index.vue

@@ -1,26 +1,25 @@
 <template>
   <div class="review">
     <div class="review-head">
-      <a-row align="center">
-        <a-col :span="9">
-          <h2 class="review-title">419872222786601 - 考生2 - CET4</h2>
-        </a-col>
-        <a-col :span="6">
-          <div class="review-stat">
-            <p class="color-success">
-              <CheckCircleFilled />已处理:{{ progress.finishCount }}
-            </p>
-            <p class="color-error">
-              <CloseCircleFilled />未处理:{{ progress.todoCount }}
-            </p>
-          </div>
-        </a-col>
-        <a-col :span="9">
-          <a-button v-if="reviewStore.tabKey === 'review'" :disabled="loading">
-            <template #icon><ArrowLeftOutlined /></template> 上一个
-          </a-button>
-        </a-col>
-      </a-row>
+      <h2 v-if="reviewStore.curTask" class="review-title">
+        {{ reviewStore.curTask.examNumber }} - {{ reviewStore.curTask.name }} -
+        CET4
+      </h2>
+
+      <div class="review-stat">
+        <p class="color-success">
+          <CheckCircleFilled />已处理:{{ progress.finishCount }}
+        </p>
+        <p class="color-error">
+          <CloseCircleFilled />未处理:{{ progress.todoCount }}
+        </p>
+      </div>
+
+      <div class="review-prev">
+        <a-button v-if="reviewStore.tabKey === 'review'" :disabled="loading">
+          <template #icon><ArrowLeftOutlined /></template> 上一个
+        </a-button>
+      </div>
     </div>
 
     <!-- body -->
@@ -58,7 +57,12 @@ import {
 } from "@ant-design/icons-vue";
 import { message } from "ant-design-vue";
 
-import { reviewTaskList, reviewTaskReset, reviewTaskSave } from "@/ap/review";
+import {
+  reviewTaskList,
+  reviewTaskReset,
+  reviewTaskSave,
+  reviewProgress,
+} from "@/ap/review";
 import { ReviewTaskListItem } from "@/ap/types/review";
 import { SubjectItem } from "@/ap/types/base";
 
@@ -77,6 +81,17 @@ defineOptions({
   name: "review",
 });
 
+// 任务进度
+const progress = ref({
+  finishCount: 0,
+  todoCount: 0,
+});
+async function updateProgress() {
+  const res = await reviewProgress({ examId: userStore.curExam.id });
+  progress.value = res || {};
+}
+
+// 任务相关
 const searchModel = reactive({
   examId: userStore.curExam.id,
   subjectCode: "",
@@ -96,7 +111,7 @@ function setCurTask() {
   reviewStore.setInfo({ curTask: dataList.value[curTaskIndex.value] });
 }
 
-function getNextTask() {
+async function getNextTask() {
   if (curTaskIndex.value >= dataList.value.length - 1) {
     await getTasks();
     if (!dataList.value.length) {
@@ -113,7 +128,7 @@ function getNextTask() {
   setCurTask();
 }
 
-function getPrevTask() {
+async function getPrevTask() {
   if (loading.value) return;
   loading.value = true;
 
@@ -211,6 +226,33 @@ function keyEventHandle(e: KeyboardEvent) {
 
 onMounted(() => {
   registKeyEvent();
+  // test
+  dataList.value = [
+    {
+      id: 1,
+      name: "考生2",
+      studentCode: "360080241304012",
+      subjectCode: "SX0001",
+      subjectName: "数学",
+      examNumber: "360080241304012",
+      markStatus: false,
+      breachCount: 1,
+      pages: [
+        {
+          index: 1,
+          uri: "",
+        },
+        {
+          index: 2,
+          uri: "",
+        },
+      ],
+    },
+  ];
+  reviewStore.setInfo({
+    curTask: dataList.value[curTaskIndex.value] || null,
+    tabKey: "review",
+  });
 });
 
 onBeforeUnmount(() => {