abpcoder 2 тижнів тому
батько
коміт
53bfdd54d0

+ 12 - 1
src/api/webApi/career-other.js

@@ -220,7 +220,7 @@ export function downloadRecommendReport(params) {
 }
 
 export function getVoluntarySpecialProjectFilter(params) {
-  // params = { year } // 缺省年份返回当前年份的专项选项
+  // params = { year, batch } // 缺省年份返回当前年份的专项选项
   // returns ['专项A', '专项B', '专项C', '不看专项']
   // getRecommendVoluntary 新增参数specialProjects: Array // 原来的参数specialProjectNation,specialProjectLocal不动
   return request({
@@ -229,3 +229,14 @@ export function getVoluntarySpecialProjectFilter(params) {
     params
   })
 }
+
+export function getVoluntaryLimitationTags(params) {
+  // params = { year, batch } // 缺省年份返回当前年份的专项选项
+  // returns ['专项A', '专项B', '专项C', '不看专项']
+  // getRecommendVoluntary 新增参数specialProjects: Array // 原来的参数specialProjectNation,specialProjectLocal不动
+  return request({
+    url: `/front/syzy/zytb/limitationTags`,
+    method: 'get',
+    params
+  })
+}

+ 3 - 2
src/common/MxConfig.js

@@ -323,7 +323,8 @@ export default {
     if (bIdx === -1) bIdx = 9999
     return aIdx - bIdx
   },
-  scoreLockingTips: '现在是志愿填报正式阶段,请准确填入您的高考分数和科目,确认后不可再修改',
+  scoreLockingTips: '系统已开启2025年正式模拟填报版本,请输入真实的高考成绩,保存后不可再修改,请认真填写。',
   scoreLockedTips: '*志愿填报正式阶段不能修改分数与科目',
-  scoreRuleTips: '*出分前分数与科目可以多次输入,出分后只能输入一次,系统使用视频可以在个人中心的使用教程查看'
+  scoreRuleTips: '*出分前分数与科目可以多次输入,出分后只能输入一次,系统使用视频可以在个人中心的使用教程查看',
+  scoreObsoletedTips: '系统已更新2025年数据,此志愿表已作废,请重新填写。'
 }

+ 1 - 0
src/components/MxCondition/condition-object/condition-pre-enroll-tag.js

@@ -2,6 +2,7 @@ import conditionObjectBase from '../condition-object-base'
 import { getPreEnrollTags } from '@/api/webApi/prev-batch'
 
 export default {
+  /* Important: PC和APP上的preEnrollTag条件有差异!!!不要直接覆盖 */
   ...conditionObjectBase,
   key: 'preEnrollTag',
   dependentKeys: ['preEnrollYear'],

+ 1 - 1
src/components/MxPaper/mx-question-content.vue

@@ -208,7 +208,7 @@ export default {
   margin-left: 0 !important;
   margin-bottom: 15px;
   height: auto !important;
-  padding-bottom: 8px;
+  padding-bottom: 8px !important;
 }
 
 .answer-highlight,

+ 69 - 61
src/utils/request.js

@@ -2,6 +2,8 @@ import axios from 'axios'
 import { Message, MessageBox, Notification } from 'element-ui'
 import auth from '@/utils/auth'
 import errorCode from '@/utils/errorCode'
+import store from '@/store'
+let handling_405 = false
 
 axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
 axios.defaults.headers['accessFrom'] = 'front'
@@ -21,7 +23,7 @@ service.interceptors.request.use(config => {
   }
 
   // 映射params参数
-  if ((config.method == 'get' || config['paramsAsGet']) && config.params) {
+  if ((config.method === 'get' || config['paramsAsGet']) && config.params) {
     let url = config.url + '?'
     for (const propName of Object.keys(config.params)) {
       const value = config.params[propName]
@@ -29,7 +31,7 @@ service.interceptors.request.use(config => {
       if (value !== null && typeof (value) !== 'undefined') {
         if (typeof value === 'object') {
           for (const key of Object.keys(value)) {
-            let params = propName + '[' + key + ']'
+            const params = propName + '[' + key + ']'
             var subPart = encodeURIComponent(params) + '='
             url += subPart + encodeURIComponent(value[key]) + '&'
           }
@@ -50,68 +52,74 @@ service.interceptors.request.use(config => {
 
 // 响应拦截器
 service.interceptors.response.use(res => {
-    // 未设置状态码则默认成功状态
-    const code = res.data.code || 200
-    // 获取错误信息
-    const msg = errorCode[code] || res.data.msg || errorCode['default']
-    if (code === 401 || code === 402) {
-      const defaultMsg = '登录状态已过期,您可以继续留在该页面,或者重新登录'
-      const customMsg = code === 402 ? res.data.msg : defaultMsg
-      MessageBox.confirm(customMsg || defaultMsg, '系统提示', {
-          confirmButtonText: '重新登录',
-          cancelButtonText: '取消',
-          type: 'warning'
-        }
-      ).then(() => {
-        auth.removeToken()
-        location.href = '/login'
-      })
-      return Promise.reject('error')
-    } else if (code === 403) {
-      const defaultMsg = '卡号已过期或已关卡状态,您可以选择续费以重新激活账号'
-      const customMsg = res.data.msg || defaultMsg
-      MessageBox.confirm(customMsg, '系统提示', {
-        confirmButtonText: '续费',
-        cancelButtonText: '取消',
-        type: 'warning'
-      }).then(() => {
-        const { cardNo, mobile } = res.data
-        location.href = `/renew?cardNo=${cardNo}&mobile=${mobile}`
-      })
-      return Promise.reject('error')
-    } else if (code === 500) {
-      Message({
-        message: msg,
-        type: 'error'
-      })
-      return Promise.reject(new Error(msg))
-    } else if (code !== 200) {
-      Notification.error({
-        title: msg
-      })
-      return Promise.reject('error')
-    } else {
-      // blob类型的文件流因为后续还要处理文件其它信息,所以不做处理
-      return (res.data instanceof Blob) ? res : res.data
-    }
-  },
-  error => {
-    console.log('err' + error)
-    let { message } = error
-    if (message == 'Network Error') {
-      message = '网络连接异常'
-    } else if (message.includes('timeout')) {
-      message = '网络请求超时'
-    } else if (message.includes('Request failed with status code')) {
-      message = '系统接口' + message.substr(message.length - 3) + '异常'
-    }
+  // if (res.config.url.includes('zytb/save')) {
+  //   debugger
+  //   store.getters.currentUser.scoreLock = -1
+  //   res.data.code = 405
+  //   res.data.msg = '模拟数据,测试405码'
+  // }
+  // 未设置状态码则默认成功状态
+  const code = res.data.code || 200
+  // 获取错误信息
+  const msg = errorCode[code] || res.data.msg || errorCode['default']
+  if (code === 401 || code === 402) {
+    const defaultMsg = '登录状态已过期,您可以继续留在该页面,或者重新登录'
+    const customMsg = code === 402 ? res.data.msg : defaultMsg
+    MessageBox.confirm(customMsg || defaultMsg, '系统提示', {
+      confirmButtonText: '重新登录',
+      cancelButtonText: '取消',
+      type: 'warning'
+    }).then(() => {
+      auth.removeToken()
+      location.href = '/login'
+    })
+    return Promise.reject('error')
+  } else if (code === 405) {
+    if (handling_405) return Promise.reject()
+    handling_405 = true
+    const defaultMsg = '检测到系统数据更新,请稍后重试'
+    const customMsg = msg || defaultMsg
+    MessageBox.confirm(customMsg, '系统提示', {
+      showCancelButton: false,
+      type: 'warning'
+    }).then(async() => {
+      await store.dispatch('GetInfo')
+      location.href = '/'
+    }).finally(() => handling_405 = false)
+    return Promise.reject()
+  } else if (code === 500) {
     Message({
-      message: message,
-      type: 'error',
-      duration: 5 * 1000
+      message: msg,
+      type: 'error'
+    })
+    return Promise.reject(new Error(msg))
+  } else if (code !== 200) {
+    Notification.error({
+      title: msg
     })
-    return Promise.reject(error)
+    return Promise.reject('error')
+  } else {
+    // blob类型的文件流因为后续还要处理文件其它信息,所以不做处理
+    return (res.data instanceof Blob) ? res : res.data
+  }
+},
+error => {
+  console.log('err' + error)
+  let { message } = error
+  if (message === 'Network Error') {
+    message = '网络连接异常'
+  } else if (message.includes('timeout')) {
+    message = '网络请求超时'
+  } else if (message.includes('Request failed with status code')) {
+    message = '系统接口' + message.substr(message.length - 3) + '异常'
   }
+  Message({
+    message: message,
+    type: 'error',
+    duration: 5 * 1000
+  })
+  return Promise.reject(error)
+}
 )
 
 export default service

+ 5 - 1
src/views/career/bigdataSelectCourse/personalSelectCourseReport.vue

@@ -120,15 +120,19 @@
 <script>
 
 import '@/assets/filp/style/chunk-cac90f3e.4cffd0b2.css'
+import MxTransferMixin from "@/components/mx-transfer-mixin";
 export default {
+  mixins: [MxTransferMixin],
   data() {
     return {}
   },
   methods:{
     goReportDemoOnline() {
-      this.$router.push({ path:'/elective/report/flip',query:{path:'https://online.fliphtml5.com/jkrou/ybov/'} })
+      // this.$router.push({ path:'/elective/report/flip',query:{path:'https://online.fliphtml5.com/jkrou/ybov/'} })
       // const path = 'https://online.fliphtml5.com/jkrou/ybov/'
       // window.open(path)
+      const path = '/elective/report/index'
+      this.transferTo(path)
     },
     goStudyReportDemoOnline() {
       const path = 'https://online.fliphtml5.com/jkrou/hjig/'

+ 21 - 0
src/views/career/components/FilterForm.vue

@@ -195,6 +195,23 @@
           </el-checkbox-button>
         </el-checkbox-group>
       </el-row>
+      <el-row v-if="limitationTagOptions.length" class="radioInput">
+        <div>
+          <span class="radiaTitle">限报要求:</span>
+        </div>
+        <el-checkbox-button :value="!!!filter.limitationTags.length" @click.native="filter.limitationTags=[]">不限
+        </el-checkbox-button>
+        <el-checkbox-group v-model="filter.limitationTags">
+          <el-checkbox-button
+            v-for="item in limitationTagOptions"
+            :key="item"
+            :label="item"
+            style="margin-bottom:10px; margin-left: 10px"
+          >
+            {{ item }}
+          </el-checkbox-button>
+        </el-checkbox-group>
+      </el-row>
     </template>
   </div>
 </template>
@@ -224,6 +241,10 @@ export default {
     specialProjectOptions: {
       type: Array,
       default: () => []
+    },
+    limitationTagOptions: {
+      type: Array,
+      default: () => []
     }
   },
   data() {

+ 6 - 1
src/views/career/components/SelectUniversity.vue

@@ -18,7 +18,12 @@
       <div>
         <el-row :gutter="20">
           <el-col v-for="item in tableData" :key="item.id" :span="6">
-            <div class="universityList" @click="changeUniversity(item)">{{ item.name }}</div>
+            <div class="universityList" @click="changeUniversity(item)">
+              {{ item.name }}
+              <el-tooltip v-if="item.isNew" effect="light" placement="top" content="新增院校">
+                <el-tag size="mini" type="danger" effect="dark" class="ml5">新</el-tag>
+              </el-tooltip>
+            </div>
           </el-col>
         </el-row>
       </div>

+ 68 - 48
src/views/career/information/index.vue

@@ -1,43 +1,38 @@
 <template>
-  <div class="info-container">
-    <el-container>
-      <el-main>
-        <div class="info-tabs">
-          <el-radio-group v-model="type" @change="detailMode = false">
-            <el-radio-button
-              v-for="opt in typeOptions"
-              :key="opt.value"
-              :label="opt.label"
-            >{{ opt.label }}
-            </el-radio-button
-            >
+  <div class="bg-page fx-column fx-cen-cen">
+    <div class="bg-white index-block fx-row jc-bet">
+      <div class="fx-1 width0">
+        <div class="info-tabs ml20 mt20">
+          <el-radio-group v-if="showTabHead" v-model="type" @change="detailMode = false">
+            <el-radio-button v-for="opt in typeOptions" :key="opt.value" :label="opt.label">{{ opt.label }}</el-radio-button>
           </el-radio-group>
+          <div v-if="showSingleHead" class="news-single-head">
+            <span class="pl60">{{ type }}</span>
+          </div>
         </div>
         <info-list
+          v-show="!detailMode"
           class="mt10"
           :type="type"
-          v-show="!detailMode"
           @click="detailList"
-        ></info-list>
+        />
         <info-detail
-          class="mt10"
           v-if="detailMode"
+          :id="detailId"
+          class="mt10"
           :type="type"
           :title="detailTitle"
-          :id="detailId"
           @close="detailClose"
         />
-      </el-main>
-      <el-aside width="400px" style="background-color: #ffffff">
-        <el-card class="mt20" v-for="opt in typeOptions" :key="opt.value">
-          <info-sample
-            :type="opt.label"
-            :title="opt.label"
-            @click="detailSample"
-          />
-        </el-card>
-      </el-aside>
-    </el-container>
+      </div>
+      <div v-if="showHot" v-show="!detailMode" style="width: 400px; padding: 20px; margin-top: 38px">
+        <info-sample
+          :type="type"
+          :title="type"
+          @click="detailSample"
+        />
+      </div>
+    </div>
   </div>
 </template>
 <script>
@@ -48,32 +43,43 @@ import InfoSample from '../components/infoSample.vue'
 import * as career from '@/api/webApi/career-news'
 
 export default {
-  mixins: [transferMixin],
   components: { infoList, InfoSample, InfoDetail },
+  mixins: [transferMixin],
   data() {
     return {
       detailMode: false,
       detailTitle: '',
       detailId: 0,
       type: '',
-      typeOptions: []
+      typeOptions: [],
+      showHot: true
     }
   },
-  watch: {
-    // $route: function() {
-    //   this.getTypes()
-    // }
+  computed: {
+    showTabHead() {
+      // return false
+      // eslint-disable-next-line no-unreachable
+      return this.typeOptions?.length &&
+        this.typeOptions.some(t => t.label === this.type)
+    },
+    showSingleHead() {
+      // return true
+      // eslint-disable-next-line no-unreachable
+      return this.typeOptions?.length && !this.showTabHead
+    }
   },
   created() {
     this.getTypes()
   },
   methods: {
     detailList(item) {
+      if (item.url) return window.open(item.url)
       this.detailTitle = item.title
       this.detailId = item.id
       this.detailMode = true
     },
     detailSample(item) {
+      if (item.url) return window.open(item.url)
       this.detailTitle = item.title
       this.detailId = item.id
       this.detailMode = true
@@ -81,24 +87,38 @@ export default {
     detailClose() {
       this.detailMode = false
     },
-    getTypes() {
-      career.types().then((res) => {
-        console.log('career news res', res)
-        if (res.code == 200 || res.code == 0) {
-          this.typeOptions = res.rows
-          let query = this.$route.query?.name || this.prevData.type
-          let news = this.prevData?.news
-          if(news){
-            this.detailList(news)
-          }
-          this.type = query || this.typeOptions.first()?.label
-        } else {
-          this.msgError(res.msg || 'career news types 请求异常')
-        }
-      })
+    async getTypes() {
+      const res = await career.types()
+      this.typeOptions = res.rows
+      this.type = this.$route.meta.type || this.typeOptions.first()?.label
+      // this.showHot = this.$route.meta.hot || false
+      const news = this.prevData?.news
+      if (news) this.detailList(news)
     }
   }
 }
 </script>
 <style lang="scss" scoped>
+@import "~@/assets/styles/common.scss";
+
+.news-single-head {
+  @extend .pf;
+  @extend .f-fff;
+  @extend .relative;
+  width: 450px;
+  height: 40px;
+  display: flex;
+  align-items: center;
+  border-radius: 0 40px 0 0;
+  background: linear-gradient(to right, $--color-primary-active, rgba($--color-primary-active, 0.2));
+
+  &:before {
+    content: '\00A0';
+    height: 2px;
+    width: 100%;
+    position: absolute;
+    bottom: -3px;
+    background: linear-gradient(to right, $--color-primary-active, rgba($--color-primary-active, 0.2));
+  }
+}
 </style>

+ 46 - 40
src/views/career/information/singleStroke.vue

@@ -1,67 +1,73 @@
 <template>
-  <div class="info-container">
-    <el-container>
-      <el-main>
-        <info-list
-          class="mt10"
-          :type="type"
-          v-show="!detailMode"
-          @click="detailList"
-        ></info-list>
-        <info-detail
-          class="mt10"
-          v-if="detailMode"
-          :type="type"
-          :title="detailTitle"
-          :id="detailId"
-          @close="detailClose"
-        />
-      </el-main>
-    </el-container>
+  <div class="bg-page fx-column fx-cen-cen">
+    <div class="bg-white index-block">
+      <info-list
+        v-show="!detailMode"
+        class="mt10"
+        :type="type"
+        @click="detailList"
+      />
+      <info-detail
+        v-if="detailMode"
+        :id="detailId"
+        class="mt10"
+        :type="type"
+        :title="detailTitle"
+        @close="detailClose"
+      />
+    </div>
   </div>
 </template>
 <script>
-import InfoDetail from "../components/infoDetail.vue";
-import infoList from "../components/infoList.vue";
-import InfoSample from "../components/infoSample.vue";
-import * as career from "@/api/webApi/career-news";
+import InfoDetail from '../components/infoDetail.vue'
+import infoList from '../components/infoList.vue'
 import transferMixin from '@/components/mx-transfer-mixin'
 
 export default {
-  mixins:[transferMixin],
-  components: { infoList, InfoSample, InfoDetail },
+  components: { infoList, InfoDetail },
+  mixins: [transferMixin],
   data() {
     return {
       detailMode: false,
-      detailTitle: "",
+      detailTitle: '',
       detailId: 0,
-      type: ""
-    };
+      type: '',
+      category: {
+        single: '单招志愿',
+        syzx: '生涯资讯',
+        zyjd: '专业解读'
+      }
+    }
   },
   mounted() {
-    this.type = this.$route.meta.type
-    let news = this.prevData?.news
-    if(news){
+    const subPaths = this.$route.path.split('/')
+    const lastSubPath = subPaths[subPaths.length - 1]
+    const typeKey = lastSubPath.split('_')[lastSubPath.split('_').length - 1]
+    this.type = this.category[typeKey]
+    const news = this.prevData?.news
+    if (news) {
       console.log(news)
       this.detailList(news)
     }
   },
   methods: {
     detailList(item) {
-      this.detailTitle = item.title;
-      this.detailId = item.id;
-      this.detailMode = true;
+      if (item.url) return window.open(item.url)
+      this.detailTitle = item.title
+      this.detailId = item.id
+      this.detailMode = true
     },
     detailSample(item) {
-      this.detailTitle = item.title;
-      this.detailId = item.id;
-      this.detailMode = true;
+      if (item.url) return window.open(item.url)
+      this.detailTitle = item.title
+      this.detailId = item.id
+      this.detailMode = true
     },
     detailClose() {
-      this.detailMode = false;
+      this.detailMode = false
     }
-  },
-};
+  }
+}
 </script>
 <style lang="scss" scoped>
 </style>

+ 6 - 1
src/views/career/plan/components/AllUniversity.vue

@@ -12,7 +12,12 @@
               <img style="width: 100%;height: 100%" :src="item.logo" alt="">
             </div>
             <div class="info ml20">
-              <div class="college_name pointer f-333 f20 mb5" @click="toDetail(item)">{{ item.name }}</div>
+              <div class="college_name f-333 f20 mb5">
+                <span class="pointer" @click="toDetail(item)">{{ item.name }}</span>
+                <el-tooltip v-if="item.isNew" effect="light" placement="top" content="新增院校">
+                  <el-tag size="mini" type="danger" effect="dark" class="ml5">新</el-tag>
+                </el-tooltip>
+              </div>
               <div v-if="item.features" class="tags">
                 <span
                   v-for="feature in item.features.split(',')"

+ 1 - 0
src/views/career/plan/components/RankUniversity.vue

@@ -134,6 +134,7 @@ export default {
         ...this.filter_form, pageNum: this.pageForm.pageNum,
         pageSize: this.pageForm.pageSize
       }).then(res => {
+        res.rows.forEach(i => i.universities = i.universities || {})
         this.collegeList = res.rows
         this.pageForm.total = res.total
       }).finally(_ => {

+ 25 - 20
src/views/career/zhiyuan/RecordDetail.vue

@@ -8,21 +8,27 @@
         <el-button v-has-history size="small" round @click="handleRouteBack">返回</el-button>
       </div>
       <div v-if="sorting" class="fx-row fx-bet-cen mb10">
-        <el-input v-model="rename" placeholder="请输入志愿表名称" style="width: 250px" />
+        <el-input v-model="rename" placeholder="请输入志愿表名称" style="width: 250px"/>
         <div class="fx-row sort-fired">
           <div class="fx-row pr15">
-            <el-tag v-for="s in firedSorts" :key="s.name" type="success" closable @close="handleSortRemove(s)">{{ s.short }} <i :class="s.icon" /></el-tag>
+            <el-tag v-for="s in firedSorts" :key="s.name" type="success" closable @close="handleSortRemove(s)">
+              {{ s.short }} <i :class="s.icon"/></el-tag>
           </div>
           <el-popover placement="bottom-end" trigger="hover" popper-class="cart-popper">
             <div class="fx-column">
               <div v-for="s in sortList" :key="s.name" class="sort-item" @click="handleSort(s)">{{ s.name }}</div>
             </div>
-            <el-button slot="reference" size="small" round plain type="primary" icon="el-icon-s-fold">快速排序</el-button>
+            <el-button slot="reference" size="small" round plain type="primary" icon="el-icon-s-fold">快速排序
+            </el-button>
           </el-popover>
-          <div style="width: 10px" />
-          <el-button size="small" round plain type="primary" icon="el-icon-refresh" @click="handleResetSort">重置排序</el-button>
-          <el-button size="small" round plain type="primary" icon="el-icon-check" @click="handleSave">保存修改</el-button>
-          <el-button size="small" round plain class="el-icon-" icon="el-icon-refresh-left" @click="handleResetAll">撤销修改</el-button>
+          <div style="width: 10px"/>
+          <el-button size="small" round plain type="primary" icon="el-icon-refresh" @click="handleResetSort">重置排序
+          </el-button>
+          <el-button size="small" round plain type="primary" icon="el-icon-check" @click="handleSave">保存修改
+          </el-button>
+          <el-button size="small" round plain class="el-icon-" icon="el-icon-refresh-left" @click="handleResetAll">
+            撤销修改
+          </el-button>
         </div>
       </div>
       <table-draggable :drag-options="{handle: '.move-group'}" @drag-change="firedSorts=[]">
@@ -43,7 +49,7 @@
   </div>
 </template>
 <script>
-import { mapGetters } from 'vuex'
+import {mapGetters} from 'vuex'
 import MxConfig from '@/common/MxConfig'
 import ZhiyuanList from '@/views/career/zhiyuan/components/zhiyuan-list'
 import {
@@ -54,12 +60,12 @@ import {
   saveZhiyuan
 } from '@/api/webApi/professlib'
 import IndexCard from '@/views/login/components/modules/shared/IndexCard'
-import { downloadRecommendReport } from '@/api/webApi/career-other'
+import {downloadRecommendReport} from '@/api/webApi/career-other'
 import transferMixin from '@/components/TransferMixin'
 import TableDraggable from '@/components/TableDraggable/index.vue'
 import SearchMajorProviderMixin from '@/views/career/zhiyuan/components/SearchMajorProviderMixin'
 import MxConst from '@/common/MxConst'
-import { downloadBlobFile } from '@/utils/blob'
+import {downloadBlobFile} from '@/utils/blob'
 import VoluntaryIdentifierProvider from '@/views/career/zhiyuan/components/VoluntaryIdentifierProvider'
 
 export default {
@@ -109,7 +115,7 @@ export default {
   },
   methods: {
     handleRouteBack() {
-      if (this.$route.query.id) this.transferTo({ path: '/zhiyuan/apply', replace: true })
+      if (this.$route.query.id) this.transferTo({path: '/zhiyuan/apply', replace: true})
       else this.$router.back()
     },
     async loadDataById() {
@@ -140,8 +146,9 @@ export default {
     // table methods
     getCols() {
       getVoluntaryHeaders({
-        mode: this.prevData.mode,
+        batch: this.prevData.detail.batch.batch,
         year: this.prevData.detail.year,
+        mode: this.prevData.mode,
         isMock: this.prevData.detail.isMock || false // set false as default in edit mode
       }).then(res => {
         this.cols = res.data
@@ -161,6 +168,7 @@ export default {
       item.loading = true
       getVoluntaryMarjors(
         {
+          batch: this.prevData.batch,
           batchName: this.prevData.name,
           collegeCode: item.recruitPlan.collegeCode,
           jCode: item.jCode,
@@ -188,7 +196,7 @@ export default {
         pageSize: 100,
         pageNum: 1
       }
-      getRecommendVoluntary({ ...data }, { ...pageForm }).then(res => {
+      getRecommendVoluntary({...data}, {...pageForm}).then(res => {
         const rows = res.rows.map(item => {
           item.loading = false
           item.isExpand = false
@@ -201,18 +209,15 @@ export default {
       })
     },
     handleEdit() {
-      if (this.scoreLocked) {
-        const { score, mode } = this.currentUser
-        if (this.prevData.score != score || this.prevData.mode != mode) {
-          this.$alert('现在是高考志愿填报高峰期,此志愿表与您的高考分数不符,为保证系统稳定性,填报期间暂停修改非高考得分志愿表')
-          return
-        }
+      if (this.prevData.obsoleted) {
+        this.$alert(MxConfig.scoreObsoletedTips)
+        return
       }
       this.transferTo('/career/RecordDetailEdit', this.prevData, null, false, MxConst.keys.keyVoluntaryEdit)
     },
     async handleDownload() {
       // download blob file
-      const rep = await downloadRecommendReport({ wishResId: this.prevData.id })
+      const rep = await downloadRecommendReport({wishResId: this.prevData.id})
       const fileName = `${this.prevData.name}-${this.prevData.score}-${this.prevData.batchName}`
       downloadBlobFile(rep, fileName)
     },

+ 3 - 0
src/views/career/zhiyuan/components/PreferenceCells/CollegeCell.vue

@@ -6,6 +6,9 @@
         <template v-if="row.recruitPlan.group">({{ row.recruitPlan.group }})</template>
         ({{ row.recruitPlan.collegeCode }})
       </span>
+      <el-tooltip v-if="row.university.isNew" effect="light" placement="top" content="新增院校">
+        <el-tag size="mini" type="danger" effect="dark" class="ml5">新</el-tag>
+      </el-tooltip>
     </p>
     <p v-if="row.specialProject||row.history.sinoforeign" class="f-primary f14">
       <template v-if="row.specialProject">({{ row.specialProject }})</template>

+ 3 - 0
src/views/career/zhiyuan/components/PreferenceCells/MajorCell.vue

@@ -10,6 +10,9 @@
         <!--        <el-image :src="require('@/assets/images/career/icon-fluctuate-tag.png')" style="height: 16px;" class="ml5" />-->
         <el-tag size="mini" type="danger" effect="dark" class="ml5">大小年</el-tag>
       </el-tooltip>
+      <el-tooltip v-if="row.isNew" effect="light" placement="top" content="新增专业">
+        <el-tag size="mini" type="danger" effect="dark" class="ml5">新</el-tag>
+      </el-tooltip>
     </div>
     <div v-if="row.professionType||row.typeNames" class="f12 f-primary mb3">
       {{ row.professionType }} {{ row.typeNames }}

+ 20 - 4
src/views/career/zhiyuan/components/recommend.vue

@@ -29,6 +29,7 @@
         <filter-form
           :filter="filter_form"
           :special-project-options="specialProjectOptions"
+          :limitation-tag-options="limitationTagOptions"
           :level-hide="true"
           multiple
           recommend-features
@@ -122,7 +123,7 @@ import SearchMajorInjectionMixin from '@/views/career/zhiyuan/components/SearchM
 import SimulateScore from '@/views/career/zhiyuan/components/SimulateScore.vue'
 import { debounce, deepClone } from '@/utils'
 import { zytbBatches } from '@/api/webApi/webQue'
-import { getVoluntarySpecialProjectFilter } from '@/api/webApi/career-other'
+import { getVoluntarySpecialProjectFilter, getVoluntaryLimitationTags } from '@/api/webApi/career-other'
 import VoluntaryIdentifierInjection from '@/views/career/zhiyuan/components/VoluntaryIdentifierInjection'
 
 export default {
@@ -190,7 +191,8 @@ export default {
         collect: '',
         specialProjectNation: '',
         specialProjectLocal: '',
-        specialProjects: []
+        specialProjects: [],
+        limitationTags: []
       },
       universityName: '',
       localFilters: {
@@ -201,6 +203,7 @@ export default {
         ]
       },
       specialProjectOptions: [],
+      limitationTagOptions: [],
       pageForm: {
         pageSize: 10,
         pageNum: 1
@@ -249,11 +252,17 @@ export default {
       }
     }
   },
+  provide() {
+    return {
+      fetchVoluntaryFilter: () => this.filter_form
+    }
+  },
   created() {
     this.$nextTick(_ => {
       this.getList()
       this.getCols()
       this.getSpecialProjectFilter()
+      this.getLimitationTags()
     })
   },
   methods: {
@@ -265,9 +274,13 @@ export default {
       this.getList()
     },
     async getSpecialProjectFilter() {
-      const res = await getVoluntarySpecialProjectFilter({ year: this.batch.year })
+      const res = await getVoluntarySpecialProjectFilter({ year: this.batch.year, batch: this.batch.batch })
       this.specialProjectOptions = res.data
     },
+    async getLimitationTags() {
+      const res = await getVoluntaryLimitationTags({ year: this.batch.year, batch: this.batch.batch })
+      this.limitationTagOptions = res.data
+    },
     async save() {
       if (this.id && !this.subTitle) {
         this.msgError('请设置志愿表名称')
@@ -409,8 +422,9 @@ export default {
     },
     getCols() {
       getVoluntaryHeaders({
-        mode: this.mode.toString(),
+        batch: this.batch.batch,
         year: this.batch.year,
+        mode: this.mode.toString(),
         isMock: this.isMockHeader // undefined as default
       }).then(res => {
         this.cols = res.data
@@ -444,6 +458,7 @@ export default {
         specialProjectNation: this.filter_form.specialProjectNation,
         specialProjectLocal: this.filter_form.specialProjectLocal,
         specialProjects: this.filter_form.specialProjects,
+        limitationTags: this.filter_form.limitationTags,
         university: {
           // "code": "string",
           features: this.filter_form.features?.toString(),
@@ -488,6 +503,7 @@ export default {
       item.loading = true
       getVoluntaryMarjors(
         {
+          batch: this.batch.batch,
           batchName: this.batch.name,
           collegeCode: item.recruitPlan.collegeCode,
           jCode: item.jCode,

+ 18 - 1
src/views/career/zhiyuan/components/zhiyuan-list.vue

@@ -68,6 +68,7 @@
             <el-button :type="row.selected ? 'danger' : 'primary'" @click="apply(row,recommend)">
               {{ row.selected ? '取消' : '填报' }}
             </el-button>
+            <div v-if="shouldDisplayLimitationTags(row)" class="f-red mt5">有填报要求</div>
           </template>
           <template #sort="{row, $index}">
             <div class="fx-column">
@@ -129,7 +130,10 @@ export default {
     TypeCell,
     DynamicTable
   },
-  inject: ['fetchVoluntaryData'],
+  inject: {
+    'fetchVoluntaryData': { default: () => ({}) },
+    'fetchVoluntaryFilter': { default: () => ({}) }
+  },
   props: {
     tableList: {
       type: Array,
@@ -236,9 +240,22 @@ export default {
         value: index,
         label: this.generateSeq(index)
       }))
+    },
+    filterLimitationTags() {
+      return this.fetchVoluntaryFilter()?.limitationTags || []
     }
   },
   methods: {
+    shouldDisplayLimitationTags(item) {
+      // 志愿表详情下,全显;推荐列表时,根据筛选条件显示
+      if (this.readonly) {
+        return item.limitationTags?.length > 0
+      }
+      if (!item.limitationTags) return false
+      const safeTags = item.limitationTags?.split(',') || []
+      if (!safeTags.length) return false
+      return safeTags.some(t => this.filterLimitationTags.includes(t))
+    },
     generateSeq(index) {
       switch (this.voluntaryData.sort) {
       case 'letter':

+ 28 - 24
src/views/index/components/index-card-news-single.vue

@@ -1,38 +1,41 @@
 <template>
-  <index-card :title="type"  more-text="查看全部" @more="handleMore">
-        <el-row>
-          <el-col v-for="news in list" :key="news.id" :span="12">
-            <div class="fx-row fx-sta-cen pf f-333 pr40" style="line-height: 35px">
-              <i class="el-icon-alarm-clock"></i>
-              <span class="ml3">{{ news.sendDate }}</span>
-              <span class="ml10 fx-1 text-ellipsis new-title pointer"
-                    :title="news.title" @click="goNewsWithType(type,news)">
-                {{ news.title }}</span>
-            </div>
-          </el-col>
-        </el-row>
+  <index-card :title="type" more-text="更多" @more="handleMore">
+    <el-row>
+      <el-col v-for="news in list" :key="news.id" :span="12">
+        <div class="fx-row fx-sta-cen pf f-333 pr40" style="line-height: 35px">
+          <i class="el-icon-alarm-clock" />
+          <span class="ml3">{{ news.sendDate }}</span>
+          <span
+            class="ml10 fx-1 text-ellipsis new-title pointer"
+            :title="news.title"
+            @click="goNewsWithType(type,news)"
+          >
+            {{ news.title }}</span>
+        </div>
+      </el-col>
+    </el-row>
   </index-card>
 </template>
 
 <script>
-import IndexCard from '@/views/index/components/index-card'
-import loginCheckMixin from '@/views/index/blocks/index-login-interceptor-mixin'
+import loginCheckMixin from '@/views/index/blocks/LoginCheckInterceptor'
 import transferMixin from '@/components/mx-transfer-mixin'
 import * as career from '@/api/webApi/career-news'
+import IndexCard from '@/views/login/components/modules/shared/IndexCard'
 
 export default {
-  mixins: [transferMixin, loginCheckMixin],
-  name: 'index-card-news-single',
+  name: 'IndexCardNewsSingle',
   components: { IndexCard },
-  props:{
-    type:{
+  mixins: [transferMixin, loginCheckMixin],
+  props: {
+    type: {
       type: String,
-      default: '',
+      default: ''
     },
     morePath: {
-      type: String | Object,
-      default: '',
-    },
+      type: String,
+      default: ''
+    }
   },
   data() {
     return {
@@ -47,7 +50,7 @@ export default {
       career.list({
         pageNum: 1,
         pageSize: 10,
-        tag:'',
+        tag: '',
         type: this.type
       }).then(res => {
         this.list = res.rows
@@ -59,6 +62,7 @@ export default {
     },
     async goNewsWithType(type, news) {
       await this.loginCheck()
+      if (news.url) return window.open(news.url)
       this.transferTo(this.morePath, { type, news })
     }
   }
@@ -67,6 +71,6 @@ export default {
 
 <style lang="scss" scoped>
 .new-title:hover {
-  color: #47C6A2;
+  color: var(--themeColor);
 }
 </style>

+ 27 - 25
src/views/index/components/index-card-news.vue

@@ -1,38 +1,39 @@
 <template>
-  <index-card simple title="高考资讯" sub-title="最新、最全的高考资讯" more-text="查看全部" @more="handleMore">
-    <div class="bg-white pd20 rd8" style="height: 270px">
-      <el-tabs v-model="activeName" type="card" @tab-click="handleTabChanged">
-        <el-tab-pane v-for="type in newsTypes" :key="type" :name="type" :label="type">
-          <el-row>
-            <el-col v-for="news in getSafeNews(type)" :key="news.id" :span="12">
-              <div class="fx-row fx-sta-cen pf f-333 pr40" style="line-height: 35px">
-                <i class="el-icon-alarm-clock"></i>
-                <span class="ml3">{{ news.sendDate }}</span>
-                <span class="ml10 fx-1 text-ellipsis new-title pointer"
-                      :title="news.title" @click="goNewsWithType(type,news)">
+  <index-card title="高考资讯" sub-title="最新、最全的高考资讯" more-text="更多" @more="handleMore">
+    <el-tabs v-model="activeName" type="card" @tab-click="handleTabChanged">
+      <el-tab-pane v-for="type in newsTypes" :key="type" :name="type" :label="type">
+        <el-row>
+          <el-col v-for="news in getSafeNews(type)" :key="news.id" :span="12">
+            <div class="fx-row fx-sta-cen pf f-333 pr40" style="line-height: 35px">
+              <i class="el-icon-alarm-clock" />
+              <span class="ml3">{{ news.sendDate }}</span>
+              <span
+                class="ml10 fx-1 text-ellipsis new-title pointer"
+                :title="news.title"
+                @click="goNewsWithType(type,news)"
+              >
                 {{ news.title }}</span>
-              </div>
-            </el-col>
-          </el-row>
-        </el-tab-pane>
-      </el-tabs>
-    </div>
+            </div>
+          </el-col>
+        </el-row>
+      </el-tab-pane>
+    </el-tabs>
   </index-card>
 </template>
 
 <script>
-import IndexCard from '@/views/index/components/index-card'
-import loginCheckMixin from '@/views/index/blocks/index-login-interceptor-mixin'
+import loginCheckMixin from '@/views/index/blocks/LoginCheckInterceptor'
 import transferMixin from '@/components/mx-transfer-mixin'
 import * as career from '@/api/webApi/career-news'
+import IndexCard from '@/views/login/components/modules/shared/IndexCard'
 
 export default {
+  name: 'IndexCardNews',
+  components: { IndexCard },
   mixins: [transferMixin, loginCheckMixin],
-  name: 'index-card-news',
-  components: {IndexCard},
   data() {
     return {
-      morePath: {name: 'NewsAll'},
+      morePath: '/fuzhu/newGaokaoNews',
       newsTypes: [],
       activeName: '',
       newsCache: {}
@@ -54,7 +55,7 @@ export default {
     },
     getList(type) {
       if (!type) return
-      if (this.newsCache.hasOwnProperty(type)) return
+      if (this.newsCache[type]) return
       career.listNoToken({
         pageNum: 1,
         pageSize: 10,
@@ -72,7 +73,8 @@ export default {
     },
     async goNewsWithType(type, news) {
       await this.loginCheck()
-      this.transferTo(this.morePath, {type, news})
+      if (news.url) return window.open(news.url)
+      this.transferTo(this.morePath, { type, news })
     }
   }
 }
@@ -80,6 +82,6 @@ export default {
 
 <style lang="scss" scoped>
 .new-title:hover {
-  color: #47C6A2;
+  color: var(--themeColor);
 }
 </style>

+ 23 - 44
src/views/index/components/index-card-top.vue

@@ -1,60 +1,40 @@
 <template>
-  <index-card :title="title" :more-text="more" :simple="simple" @more="handleMore">
-    <div :class="contentWrapClass">
-      <div :style="{height: listSpace+'px'}"></div>
-      <div v-for="news in topNews" :key="news.id" class="fx-row fx-sta-cen pf f-333 pr40" style="line-height: 35px">
-        <i class="el-icon-alarm-clock"></i>
-        <span class="ml3">{{ news.sendDate }}</span>
-        <span class="ml10 fx-1 text-ellipsis new-title pointer"
-              :title="news.title" @click="handleMore(news)">
-                {{ news.title }}</span>
-      </div>
+  <index-card title="热门资讯">
+    <!-- 空白与左边tabs对齐 -->
+    <div v-if="isSpace" style="height: 55px" />
+    <div v-for="news in topNews" :key="news.id" class="fx-row fx-sta-cen pf f-333 pr40" style="line-height: 35px">
+      <i class="el-icon-alarm-clock" />
+      <span class="ml3">{{ news.sendDate }}</span>
+      <span
+        class="ml10 fx-1 text-ellipsis new-title pointer"
+        :title="news.title"
+        @click="handleMore(news)"
+      >
+        {{ news.title }}</span>
     </div>
   </index-card>
 </template>
 
 <script>
-import IndexCard from '@/views/index/components/index-card'
-import loginCheckMixin from '@/views/index/blocks/index-login-interceptor-mixin'
+import loginCheckMixin from '@/views/index/blocks/LoginCheckInterceptor'
 import transferMixin from '@/components/mx-transfer-mixin'
 import * as career from '@/api/webApi/career-news'
+import IndexCard from '@/views/login/components/modules/shared/IndexCard'
 
 export default {
+  name: 'IndexCardTop',
+  components: { IndexCard },
   mixins: [loginCheckMixin, transferMixin],
-  name: 'index-card-top',
-  components: {IndexCard},
   props: {
-    title: {
-      type: String,
-      default: '热门资讯'
-    },
-    listSpace: {
-      type: Number,
-      default: 55
-    },
-    newsQuery: {
-      type: Object,
-      default: () => ({})
-    },
-    more: {
-      type: String,
-      default: ''
-    },
-    morePath: {
-      type: String | Object,
-      default: () => ({name: 'NewsAll'})
-    },
-    simple: {
+    // 是否向上空出来一块
+    isSpace: {
       type: Boolean,
-      default: false
-    },
-    contentWrapClass: {
-      type: String,
-      default: ''
+      default: true
     }
   },
   data() {
     return {
+      morePath: '/fuzhu/newGaokaoNews',
       topNews: []
     }
   },
@@ -66,16 +46,15 @@ export default {
       career.listNoToken({
         pageNum: 1,
         pageSize: 5,
-        tag: 'hot',
-        ...this.newsQuery
+        tag: 'hot'
       }).then(res => {
         this.topNews = res.rows
       })
     },
     async handleMore(news) {
       await this.loginCheck()
-      const next = news ? {news} : null
-      this.transferTo(this.morePath, next)
+      if (news.url) return window.open(news.url)
+      this.transferTo(this.morePath, { news })
     }
   }
 }