Browse Source

适配查位次

shmily1213 1 month ago
parent
commit
f270679639

+ 35 - 0
src/api/modules/vhs.ts

@@ -9,6 +9,7 @@ import {
     VoluntaryRecommend
 } from "@/types/vhs";
 import config from "@/config";
+import { RankingSegmentItem, RankingSegmentScore, RankingSegmentScoreQueryDTO, RankingSegmentTableQueryDTO } from "@/types/career";
 
 
 // 志愿填报  科目和分数要求
@@ -89,4 +90,38 @@ export function getVoluntarySpecialProjectFilter(params: any) {
     // returns ['专项A', '专项B', '专项C', '不看专项']
     // getRecommendVoluntary 新增参数specialProjects: Array // 原来的参数specialProjectNation,specialProjectLocal不动
     return flyio.get(`/front/syzy/zytb/specialProjectFilter`, params) as Promise<ApiResponse<string[]>>
+}
+
+/**
+ * 获取一分一段的省份列表
+ * @returns 获取一分一段的省份列表
+ */
+export function getYfydLocations() {
+  return flyio.get('/front/syzy/yfyd/locations') as Promise<ApiResponseList<string>>
+}
+
+/**
+ * 获取一分一段的年份列表
+ * @returns 获取一分一段的年份列表
+ */
+export function getYfydYears(location: string) {
+  return flyio.get('/front/syzy/yfyd/years', { location }) as Promise<ApiResponseList<number>>
+}
+
+/**
+ * 获取一分一段的分数列表
+ * @param params 查询参数
+ * @returns 获取一分一段的分数列表
+ */
+export function getYfydRankingList(params: RankingSegmentTableQueryDTO) {
+  return flyio.get('/front/syzy/yfyd/list', params) as Promise<ApiResponseList<RankingSegmentItem>>
+}
+
+/**
+ * 获取一分一段的分数详情
+ * @param params 查询参数
+ * @returns 获取一分一段的分数详情
+ */
+export function getYfydRankingScore(params: RankingSegmentScoreQueryDTO) {
+  return flyio.get('/front/syzy/yfyd/getEquivalentScore', params) as Promise<ApiResponse<RankingSegmentScore>>
 }

+ 4 - 1
src/common/routes.ts

@@ -173,7 +173,10 @@ export const routes = {
    * 首页
    */
   pageIndex: '/pagesMain/pages/index/index',
-
+  /**
+   * 查位次
+   */
+  pageRanking: '/pagesOther/pages/ranking/ranking',
 } as const;
 
 export type Routes = keyof typeof routes;

+ 164 - 171
src/components/ie-picker/ie-picker.vue

@@ -1,224 +1,217 @@
 <template>
-    <view :class="width" @click="handleClick">
-        <view class="flex items-center gap-x-6 justify-start" :class="customClass" :style="customStyle">
-            <view v-if="matchValue || customLabel"
-                  class="text-[15px] h-[24px] leading-[24px] flex-1 min-w-1 ellipsis-1"
-                  :style="getValueStyle"
-                  :class="[(disabled || readonly) ? 'text-[#dce4f6]': color]">
-                <slot :label="label">
-                    {{ label }}
-                </slot>
-            </view>
-            <view v-else class="text-[15px] h-[24px] leading-[24px] text-[#c0c4cc] flex-1 min-w-1 ellipsis-1"
-                  :style="getPlaceholderStyle">{{ placeholder }}
-            </view>
-            <slot name="right">
-                <view v-if="!readonly && showArrow" class="transition-all duration-300">
-                    <uv-icon :name="icon" size="15" :color="iconColor"/>
-                </view>
-            </slot>
+  <view :class="width" @click="handleClick">
+    <view class="flex items-center gap-x-6 justify-start" :class="customClass" :style="customStyle">
+      <view v-if="matchValue || customLabel" class="text-[15px] h-[24px] leading-[24px] flex-1 min-w-1 ellipsis-1"
+        :style="getValueStyle" :class="[(disabled || readonly) ? 'text-[#dce4f6]' : color]">
+        <slot :label="label">
+          {{ label }}
+        </slot>
+      </view>
+      <view v-else class="text-[15px] h-[24px] leading-[24px] text-[#c0c4cc] flex-1 min-w-1 ellipsis-1"
+        :style="getPlaceholderStyle">{{ placeholder }}
+      </view>
+      <slot name="right">
+        <view v-if="!readonly && showArrow" class="transition-all duration-300">
+          <uv-icon :name="icon" size="15" :color="iconColor" />
         </view>
+      </slot>
     </view>
-    <!-- #ifdef H5 -->
-    <teleport to="body">
-        <!-- #endif -->
-        <!-- #ifdef MP-WEIXIN -->
-        <root-portal externalClass="theme-ie">
-            <!-- #endif -->
-            <uv-picker ref="pickerRef" :showToolbar="false" :columns="columns" :defaultIndex="defaultIndex" :round="16"
-                       activeColor="#31A0FC" :keyName="keyLabel" :title="title" @change="handleChange" @close="onClose">
-                <template #toolbar>
-                    <view class="theme-ie">
-                        <view class="flex items-center justify-between pt-20">
-                            <view class="px-46 py-20 text-28 text-fore-light" @click="handleCancel">取消</view>
-                            <text class="text-30 text-fore-title font-bold">{{ title }}</text>
-                            <view class="px-46 py-20 text-28 text-fore-title" @click="handleConfirm">确认</view>
-                        </view>
-                    </view>
-                </template>
-            </uv-picker>
-            <!-- #ifdef MP-WEIXIN -->
-        </root-portal>
-        <!-- #endif -->
-        <!-- #ifdef H5 -->
-    </teleport>
+  </view>
+  <!-- #ifdef H5 -->
+  <teleport to="body">
+    <!-- #endif -->
+    <!-- #ifdef MP-WEIXIN -->
+    <root-portal externalClass="theme-ie">
+      <!-- #endif -->
+      <uv-picker ref="pickerRef" :showToolbar="false" :columns="columns" :defaultIndex="defaultIndex" :round="16"
+        activeColor="#31A0FC" :keyName="keyLabel" :title="title" @change="handleChange" @close="onClose">
+        <template #toolbar>
+          <ie-popup-toolbar :title="title" :showCancel="true" :showConfirm="true" @cancel="handleCancel"
+            @confirm="handleConfirm" />
+        </template>
+      </uv-picker>
+      <!-- #ifdef MP-WEIXIN -->
+    </root-portal>
     <!-- #endif -->
+    <!-- #ifdef H5 -->
+  </teleport>
+  <!-- #endif -->
 </template>
 <script lang="ts" setup>
 defineOptions({
-    options: {
-        virtualHost: true
-    }
+  options: {
+    virtualHost: true
+  }
 });
 const modelValue = defineModel<string | number>('modelValue', {
-    default: ''
+  default: ''
 });
 const props = defineProps({
-    placeholder: {
-        type: String,
-        default: '请选择',
-    },
-    customLabel: {
-        type: String,
-        default: '',
-    },
-    title: {
-        type: String,
-        default: '',
-    },
-    icon: {
-        type: String,
-        default: 'arrow-right',
-    },
-    iconColor: {
-        type: String,
-        default: '#B3B3B3'
-    },
-    list: {
-        type: Array as PropType<any[]>,
-        default: () => [],
-    },
-    keyLabel: {
-        type: String,
-        default: 'label',
-    },
-    keyValue: {
-        type: String,
-        default: 'value',
-    },
-    disabled: {
-        type: Boolean,
-        default: false,
-    },
-    readonly: {
-        type: Boolean,
-        default: false,
-    },
-    customStyle: {
-        type: Object,
-        default: () => ({}),
-    },
-    customClass: {
-        type: [String,Array],
-        default: ''
-    },
-    width: {
-        type: String,
-        default: 'w-full'
-    },
-    color: {
-        type: String,
-        default: 'text-fore-title'
-    },
-    placeholderStyle: {
-        type: Object,
-        default: () => ({}),
-    },
-    fontSize: {
-        type: Number,
-        default: 15,
-    },
-    showArrow: {
-        type: Boolean,
-        default: true,
-    },
+  placeholder: {
+    type: String,
+    default: '请选择',
+  },
+  customLabel: {
+    type: String,
+    default: '',
+  },
+  title: {
+    type: String,
+    default: '',
+  },
+  icon: {
+    type: String,
+    default: 'arrow-right',
+  },
+  iconColor: {
+    type: String,
+    default: '#B3B3B3'
+  },
+  list: {
+    type: Array as PropType<any[]>,
+    default: () => [],
+  },
+  keyLabel: {
+    type: String,
+    default: 'label',
+  },
+  keyValue: {
+    type: String,
+    default: 'value',
+  },
+  disabled: {
+    type: Boolean,
+    default: false,
+  },
+  readonly: {
+    type: Boolean,
+    default: false,
+  },
+  customStyle: {
+    type: Object,
+    default: () => ({}),
+  },
+  customClass: {
+    type: [String, Array],
+    default: ''
+  },
+  width: {
+    type: String,
+    default: 'w-full'
+  },
+  color: {
+    type: String,
+    default: 'text-fore-title'
+  },
+  placeholderStyle: {
+    type: Object,
+    default: () => ({}),
+  },
+  fontSize: {
+    type: Number,
+    default: 15,
+  },
+  showArrow: {
+    type: Boolean,
+    default: true,
+  },
 });
 const defaultIndex = ref([0]);
 const label = ref('');
 const isOpen = ref(false);
 const matchValue = ref(false);
 const columns = computed(() => {
-    return [props.list];
+  return [props.list];
 });
 const getPlaceholderStyle = computed(() => {
-    return {
-        ...props.placeholderStyle,
-        fontSize: props.fontSize + 'px'
-    }
+  return {
+    ...props.placeholderStyle,
+    fontSize: props.fontSize + 'px'
+  }
 });
 const getValueStyle = computed(() => {
-    return {
-        fontSize: props.fontSize + 'px'
-    }
+  return {
+    fontSize: props.fontSize + 'px'
+  }
 });
 const init = () => {
-    if (props.customLabel) {
-        label.value = props.customLabel;
-        matchValue.value = true;
-        return;
-    }
+  if (props.customLabel) {
+    label.value = props.customLabel;
+    matchValue.value = true;
+    return;
+  }
 
-    if (modelValue.value !== null && modelValue.value !== undefined && modelValue.value !== '') {
-        const index = props.list.findIndex(item => item[props.keyValue] == modelValue.value);
-        if (index !== -1) {
-            defaultIndex.value = [index];
-            label.value = props.list[index][props.keyLabel];
-            matchValue.value = true;
-        } else {
-            // 如果找不到匹配项,重置状态
-            defaultIndex.value = [0];
-            label.value = modelValue.value + '';
-            matchValue.value = true;
-        }
+  if (modelValue.value !== null && modelValue.value !== undefined && modelValue.value !== '') {
+    const index = props.list.findIndex(item => item[props.keyValue] == modelValue.value);
+    if (index !== -1) {
+      defaultIndex.value = [index];
+      label.value = props.list[index][props.keyLabel];
+      matchValue.value = true;
     } else {
-        defaultIndex.value = [0];
-        label.value = props.placeholder;
-        matchValue.value = false;
+      // 如果找不到匹配项,重置状态
+      defaultIndex.value = [0];
+      label.value = modelValue.value + '';
+      matchValue.value = true;
     }
+  } else {
+    defaultIndex.value = [0];
+    label.value = props.placeholder;
+    matchValue.value = false;
+  }
 }
 
 // 使用 watch 替代 watchEffect,避免循环更新
-watch([() => modelValue.value, () => props.list], init, {immediate: true});
+watch([() => modelValue.value, () => props.list], init, { immediate: true });
 
 const pickerRef = ref();
 
 const emit = defineEmits<{
-    (e: 'change', value: number, selectedItem: any): void;
-    (e: 'click'): void;
+  (e: 'change', value: number, selectedItem: any): void;
+  (e: 'click'): void;
 }>();
 
 const handleConfirm = () => {
-    const {value} = pickerRef.value.manualConfirm();
-    const oldValue = modelValue.value;
-    const newValue = value[0][props.keyValue];
+  const { value } = pickerRef.value.manualConfirm();
+  const oldValue = modelValue.value;
+  const newValue = value[0][props.keyValue];
 
-    // 更新 modelValue
-    modelValue.value = newValue;
+  // 更新 modelValue
+  modelValue.value = newValue;
 
-    // 手动更新显示状态,确保界面同步
-    const selectedItem = value[0];
-    label.value = selectedItem[props.keyLabel];
-    matchValue.value = true;
+  // 手动更新显示状态,确保界面同步
+  const selectedItem = value[0];
+  label.value = selectedItem[props.keyLabel];
+  matchValue.value = true;
 
-    // 发出 change 事件
-    if (oldValue !== newValue) {
-        emit('change', newValue, selectedItem);
-    }
+  // 发出 change 事件
+  if (oldValue !== newValue) {
+    emit('change', newValue, selectedItem);
+  }
 
-    pickerRef.value.close();
+  pickerRef.value.close();
 }
 const handleCancel = () => {
-    pickerRef.value.close();
+  pickerRef.value.close();
 }
 const handleChange = () => {
 }
 
 const handleClick = () => {
-    emit('click');
-    if (props.disabled || props.readonly) {
-        return;
-    }
-    if (!props.list.length) {
-        uni.$ie.showToast('暂无数据');
-        return;
-    }
-    init();
-    isOpen.value = true;
-    pickerRef.value.open();
+  emit('click');
+  if (props.disabled || props.readonly) {
+    return;
+  }
+  if (!props.list.length) {
+    uni.$ie.showToast('暂无数据');
+    return;
+  }
+  init();
+  isOpen.value = true;
+  pickerRef.value.open();
 }
 
 const onClose = () => {
-    isOpen.value = false;
+  isOpen.value = false;
 }
 </script>
 <style lang="scss" scoped></style>

+ 6 - 2
src/components/ie-popup-toolbar/ie-popup-toolbar.vue

@@ -1,8 +1,12 @@
 <template>
   <view class="flex items-center justify-center pt-20">
-    <view v-if="showCancel" class="px-46 py-20 text-28 text-fore-light" @click="handleCancel">{{ cancelText }}</view>
+    <view v-if="showCancel" class="px-46 py-20 text-28 text-fore-light font-medium" @click="handleCancel">
+      {{ cancelText }}
+    </view>
     <text class="text-30 text-fore-title font-bold flex-1 text-center">{{ title }}</text>
-    <view v-if="showConfirm" class="px-46 py-20 text-28 text-fore-title" @click="handleConfirm">{{ confirmText }}</view>
+    <view v-if="showConfirm" class="px-46 py-20 text-28 text-fore-title font-medium" @click="handleConfirm">
+      {{ confirmText }}
+    </view>
   </view>
 </template>
 <script lang="ts" setup>

+ 22 - 10
src/components/ie-search/ie-search.vue

@@ -1,26 +1,38 @@
 <template>
-  <view class="ie-search px-20 py-20 border-0 border-b border-solid border-border-light">
-    <uv-search v-model="modelValue" shape="square" :showAction="false" :placeholder="placeholder" @input="handleInput"
-      @search="handleSearch" @clear="handleClear">
-      <!-- <template #suffix>
-        <view class="text-24 text-fore-title bg-primary text-white px-24 py-10 rounded-full translate-x-10"
+  <view class="ie-search px-20 py-20" :class="{ 'border-0 border-b border-solid border-border-light': border }">
+    <uv-search v-model="modelValue" shape="square" :showAction="showAction" :placeholder="placeholder" :inputType="inputType"
+      @input="handleInput" @search="handleSearch" @clear="handleClear" @custom="handleSearch">
+      <template #suffix>
+        <view v-if="showSuffix"
+          class="text-24 text-fore-title bg-primary text-white px-24 py-10 rounded-full translate-x-10"
           @click="handleSearch">搜索
         </view>
-      </template> -->
+      </template>
     </uv-search>
   </view>
 </template>
 <script lang="ts" setup>
-const props = defineProps<{
+type Props = {
   placeholder?: string;
-}>();
+  border?: true;
+  inputType?: 'text' | 'number';
+  showSuffix?: boolean;
+  showAction?: boolean;
+}
+const props = withDefaults(defineProps<Props>(), {
+  placeholder: '请输入',
+  border: true,
+  inputType: 'text',
+  showSuffix: false,
+  showAction: false
+});
 const modelValue = defineModel<string>('modelValue', {
   default: ''
 });
 
 const emit = defineEmits(['search', 'clear']);
-const handleSearch = (value: string) => {
-  emit('search', value);
+const handleSearch = () => {
+  emit('search');
 };
 const handleInput = (value: string) => {
   modelValue.value = value;

+ 2 - 1
src/main.ts

@@ -58,7 +58,8 @@ export function createApp() {
       },
       search: {
         color: { default: 'var(--main-color)' },
-        actionStyle: { default: () => ({ color: 'var(--primary-color)' }) }
+        actionStyle: { default: () => ({ color: 'var(--primary-color)' }) },
+        inputType: { default: 'text' }
       },
       empty: {
         icon: { default: '/static/icon-empty.png' },

+ 26 - 20
src/pages.json

@@ -195,26 +195,32 @@
             "navigationBarTitleText": ""
           }
         },
-        {
-          "path": "pages/vhs/index/index",
-          "style": {
-            "navigationBarTitleText": ""
-          }
-        },
-        {
-          "path": "pages/vhs/detail/detail",
-          "style": {
-            "navigationBarTitleText": ""
-          }
-        },
-        {
-          "path": "pages/vhs/edit/edit",
-          "style": {
-            "navigationBarTitleText": ""
-          }
-        },
-        {
-          "path": "pages/vhs/list/list",
+        // {
+        //   "path": "pages/vhs/index/index",
+        //   "style": {
+        //     "navigationBarTitleText": ""
+        //   }
+        // },
+        // {
+        //   "path": "pages/vhs/detail/detail",
+        //   "style": {
+        //     "navigationBarTitleText": ""
+        //   }
+        // },
+        // {
+        //   "path": "pages/vhs/edit/edit",
+        //   "style": {
+        //     "navigationBarTitleText": ""
+        //   }
+        // },
+        // {
+        //   "path": "pages/vhs/list/list",
+        //   "style": {
+        //     "navigationBarTitleText": ""
+        //   }
+        // },
+        {
+          "path": "pages/ranking/ranking",
           "style": {
             "navigationBarTitleText": ""
           }

+ 23 - 29
src/pagesMain/pages/index/components/index-banner.vue

@@ -47,25 +47,25 @@ const navigateTo = async (item: MenuItem) => {
     });
   }
 }
-const rankingPageUrl = computed(() => {
-  const token = userStore.accessToken;
-  const h5Url = config.h5SiteUrl;
-  const oldRankPageUrl = '/pagesOther/pages/career/query-segment/query-segment';
-  const params = {}
-  const h5Params = {
-    token,
-    redirect: oldRankPageUrl,
-    params: JSON.stringify(params)
-  };
-  const transerParams = {
-    url: h5Url,
-    params: JSON.stringify(h5Params),
-    title: '查位次',
-    showNavbar: true
-  };
-  return `${routes.pageWebview}?${Object.entries(transerParams).map(([key, value]) => `${key}=${value}`).join('&')}`;
+// const rankingPageUrl = computed(() => {
+//   const token = userStore.accessToken;
+//   const h5Url = config.h5SiteUrl;
+//   const oldRankPageUrl = '/pagesOther/pages/career/query-segment/query-segment';
+//   const params = {}
+//   const h5Params = {
+//     token,
+//     redirect: oldRankPageUrl,
+//     params: JSON.stringify(params)
+//   };
+//   const transerParams = {
+//     url: h5Url,
+//     params: JSON.stringify(h5Params),
+//     title: '查位次',
+//     showNavbar: true
+//   };
+//   return `${routes.pageWebview}?${Object.entries(transerParams).map(([key, value]) => `${key}=${value}`).join('&')}`;
 
-});
+// });
 const validMenus = computed(() => {
   const menus: MenuItem[] = [
     {
@@ -108,17 +108,11 @@ const validMenus = computed(() => {
       pageUrl: '/pagesOther/pages/news/index/index',
       noLogin: true
     },
-    // {
-    //   name: '专升本',
-    //   icon: '/menu/menu-upgrade.png',
-    //   pageUrl: '/pages/index/index',
-    // }
-    // {
-    //   name: '查位次',
-    //   icon: '/menu/menu-upgrade.png',
-    //   pageUrl: '',
-    //   // pageUrl: rankingPageUrl.value,
-    // }
+    {
+      name: '查位次',
+      icon: '/menu/menu-upgrade.png',
+      pageUrl: routes.pageRanking,
+    }
   ]
   return menus.filter(item => item.visible !== false);
 });

+ 226 - 0
src/pagesOther/pages/ranking/ranking.vue

@@ -0,0 +1,226 @@
+<template>
+  <ie-page>
+    <z-paging ref="paging" v-model="list" :auto="false" :safe-area-inset-bottom="true" :hide-no-more-by-limit="10"
+      @query="handleQuery">
+      <template #top>
+        <ie-navbar title="一分一段" />
+        <view class="px-30 pt-30 flex items-center gap-40">
+          <view
+            class="text-[15px] text-primary px-30 py-8 bg-primary-100 rounded-full whitespace-nowrap border border-solid border-primary flex items-center gap-10">
+            <uv-icon name="lock" size="20" color="var(--primary-color)" />
+            <text>{{ userStore.examMajorName }}</text>
+          </view>
+          <view class="w-fit">
+            <ie-picker v-model="queryParams.location" :list="locations" icon="arrow-down" icon-color="#666666" />
+          </view>
+          <view class="w-fit">
+            <ie-picker v-model="queryParams.year" :list="years" icon="arrow-down" icon-color="#666666" />
+          </view>
+        </view>
+        <view class="px-10 mt-10">
+          <ie-search v-model="queryParams.score" placeholder="请输入分数" inputType="number" :showAction="true"
+            :border="false" @search="queryScore(true)" />
+        </view>
+      </template>
+      <view>
+        <view class="flex items-center py-20">
+          <view class="flex-1 text-center">
+            <view class="text-24 text-fore-tip">分数</view>
+            <view class="mt-10 text-30 text-fore-title font-bold">{{ scoreStat?.score || '-' }}</view>
+          </view>
+          <view class="flex-1 text-center">
+            <view class="text-24 text-fore-tip">同分人数</view>
+            <view class="mt-10 text-30 text-fore-title font-bold">{{ scoreStat?.num || '-' }}</view>
+          </view>
+          <view class="flex-1 text-center">
+            <view class="text-24 text-fore-tip">位次区间</view>
+            <view class="mt-10 text-30 text-fore-title font-bold">
+              {{ scoreRange }}
+            </view>
+          </view>
+        </view>
+        <template v-if="scoreStat">
+          <view class="h-20 bg-back-light"></view>
+          <view>
+            <view class="p-30">历年等效分</view>
+            <view class="px-30">
+              <ie-table :table-columns="scoresTableColumns" :data="scoresTableData" />
+              <view class="my-20 flex items-center gap-10">
+                <uv-icon name="info-circle" size="16" color="var(--danger)" />
+                <text
+                  class="text-24 text-fore-tip">因高考人数及招生计划每年都存在一定的增减,分数和位次也会有相应浮动,我们根据历年省控线变化幅度等比计算出历年等效位次和等效分。</text>
+              </view>
+            </view>
+          </view>
+        </template>
+        <view class="h-20 bg-back-light"></view>
+        <view class="p-30">一分一段表</view>
+        <view class="px-30">
+          <ie-table :table-columns="scoresSegmentColumns" :data="list" />
+        </view>
+      </view>
+    </z-paging>
+  </ie-page>
+</template>
+<script lang="ts" setup>
+import { useUserStore } from '@/store/userStore';
+import { getYfydLocations, getYfydYears, getYfydRankingList, getYfydRankingScore } from '@/api/modules/vhs';
+import type { PickerItem, Career } from '@/types';
+
+type EquivalentScore = {
+  score: number;
+  num: number;
+  lowestRank: number;
+  highestRank: number;
+};
+
+const userStore = useUserStore();
+const { getLocation, userInfo } = userStore;
+
+const locations = ref<PickerItem[]>([]);
+const years = ref<PickerItem[]>([]);
+const queryParams = ref<Partial<Career.RankingSegmentScoreQueryDTO>>({
+  location: getLocation || '',
+  mode: userInfo.examMajor
+});
+const list = ref<Career.RankingSegmentItem[]>([]);
+const scoreStat = ref<EquivalentScore | null>(null);
+const scores = ref<Career.RankingSegmentScore[]>([]);
+
+watch(() => [queryParams.value.location, queryParams.value.year], () => {
+  queryScore();
+  paging.value?.reload();
+});
+const scoreRange = computed(() => {
+  if (!scoreStat.value) {
+    return '-';
+  }
+  return `${scoreStat.value.lowestRank}~${scoreStat.value.highestRank}`;
+});
+
+const scoresTableColumns = computed(() => {
+  if (scores.value.length === 0) {
+    return [];
+  }
+  return [
+    {
+      prop: 'name',
+      label: '类型/年份',
+      flex: 1.6,
+    },
+    ...scores.value.map(item => {
+      return {
+        prop: item.year,
+        label: item.year,
+        flex: 1,
+      }
+    }),
+  ];
+});
+const scoresSegmentColumns = computed(() => {
+  return [
+    {
+      prop: 'score',
+      label: '分数',
+      flex: 1,
+    },
+    {
+      prop: 'seat',
+      label: '位次区间',
+      flex: 1,
+    },
+    {
+      prop: 'num',
+      label: '同分人数',
+      flex: 1,
+    },
+  ];
+});
+const scoresTableData = computed(() => {
+  if (scores.value.length === 0) {
+    return [];
+  }
+  return [
+    {
+      name: '等效分',
+      [scores.value[0].year]: scores.value[0].score,
+      [scores.value[1].year]: scores.value[1].score,
+      [scores.value[2].year]: scores.value[2].score,
+    },
+    {
+      name: '等效位',
+      [scores.value[0].year]: scores.value[0].seat,
+      [scores.value[1].year]: scores.value[1].seat,
+      [scores.value[2].year]: scores.value[2].seat,
+    },
+  ];
+});
+const paging = ref<ZPagingInstance>();
+
+const queryScore = (showTip = false) => {
+  if (!queryParams.value.score) {
+    if (showTip) {
+      uni.$ie.showToast('请输入分数');
+    }
+    return;
+  }
+  if (isNaN(queryParams.value.score) || queryParams.value.score < 0) {
+    if (showTip) {
+      uni.$ie.showToast('请输入合法的分数');
+    }
+    return;
+  }
+  getYfydRankingScore({ ...queryParams.value, score: queryParams.value.score || 0 }).then(res => {
+    console.log(res)
+    if (res.match) {
+      scoreStat.value = {
+        score: res.match.score,
+        num: res.match.num,
+        lowestRank: res.match.lowestRank,
+        highestRank: res.match.highestRank,
+      }
+    } else {
+      scoreStat.value = null;
+    }
+    if (res.scores) {
+      scores.value = res.scores;
+    } else {
+      scores.value = [];
+    }
+  });
+}
+const handleQuery = (page: number, size: number) => {
+  getYfydRankingList({ ...queryParams.value, pageNum: page, pageSize: size }).then(res => {
+    paging.value?.completeByTotal(res.rows.map(item => {
+      return {
+        ...item,
+        seat: `${item.lowestRank}~${item.highestRank}`,
+      }
+    }), res.total);
+  }).catch(() => paging.value?.complete(false));
+}
+const loadData = () => {
+  getYfydLocations().then(res => {
+    locations.value = res.rows.map(item => {
+      return {
+        label: item,
+        value: item,
+      }
+    });
+  });
+  getYfydYears(queryParams.value.location || '').then(res => {
+    queryParams.value.year = res.rows[0];
+    years.value = res.rows.map(item => {
+      return {
+        label: item.toString(),
+        value: item,
+      }
+    });
+  });
+}
+
+onLoad(() => {
+  loadData();
+});
+</script>
+<style lang="scss" scoped></style>

+ 1 - 0
src/pagesOther/pages/voluntary/result/components/voluntary-result-history.vue

@@ -69,6 +69,7 @@ const getDiffOpt = (item: VoluntaryResultHistory) => {
     if (item.diff < 0) return {clazz: 'text-primary', text: '低' + Math.abs(item.diff) + '分'}
     if (item.diff > 0) return {clazz: 'text-primary', text: '高' + Math.abs(item.diff) + '分'}
     if (item.diff === 0) return {clazz: 'text-primary', text: '持平'}
+    return {}
 }
 </script>
 

+ 38 - 0
src/types/career.ts

@@ -137,4 +137,42 @@ export interface University {
   star: string;
   type: string;
   webSite: string;
+}
+
+export interface RankingSegmentItem {
+  id: number;
+  location: string;
+  year: number;
+  mode: string;
+  modeName: string | null;
+  score: number;
+  num: number;
+  numTotal: number;
+  status: number;
+  maxScore: number;
+  lowestRank: number;
+  highestRank: number;
+  scoreRank: number | null;
+  type: string | null;
+}
+
+export interface RankingSegmentScore {
+  score: number;
+  seat: number;
+  year: number;
+}
+
+export interface RankingSegmentTableQueryDTO {
+  location?: string;
+  year?: number;
+  mode?: string;
+  pageNum: number;
+  pageSize: number;
+}
+
+export interface RankingSegmentScoreQueryDTO {
+  location?: string;
+  year?: number;
+  mode?: string;
+  score?: number;
 }

+ 4 - 1
src/types/index.ts

@@ -33,7 +33,10 @@ export interface ApiResponse<T> {
     contactPhone: string;
     logo: string;
     orgName: string;
-  }
+  },
+  // 以下是一分一段接口特有的
+  match?: Career.RankingSegmentItem,
+  scores?: Career.RankingSegmentScore[],
 }
 
 export interface ApiCaptchaResponse {

+ 1 - 1
src/uni_modules/uv-search/components/uv-search/uv-search.vue

@@ -39,7 +39,7 @@
                 :placeholder="placeholder"
                 :placeholder-style="`color: ${placeholderColor}`"
                 class="uv-search__content__input"
-                type="text"
+                :type="inputType"
                 :style="[{
 					textAlign: inputAlign,
 					color: color,