|
|
@@ -0,0 +1,373 @@
|
|
|
+<template>
|
|
|
+ <uv-popup ref="popup" mode="bottom" round="16" closeable>
|
|
|
+ <view class="h-[50px] px-40 fx-row fx-bet-cen text-main text-lg font-bold">
|
|
|
+ <template v-if="!id">
|
|
|
+ 志愿表预览
|
|
|
+ </template>
|
|
|
+ <view v-else class="fx-row flex-1 pr-30">
|
|
|
+ <text v-if="!nameEditing">{{ name }}</text>
|
|
|
+ <uv-input v-else v-model="name" placeholder="志愿表名称"/>
|
|
|
+ <uv-icon name="edit-pen" size="18" class="ml-10" @click="nameEditing=!nameEditing"/>
|
|
|
+ </view>
|
|
|
+ <uv-tags icon="list-dot" text="快速排序" plain class="mr-50" @click="openSortList"/>
|
|
|
+ </view>
|
|
|
+ <scroll-view :scroll-y="!anyDragging" class="bg-bg" style="height: 50vh" lower-threshold="100"
|
|
|
+ @scrolltolower="handleGroupScroll">
|
|
|
+ <uv-sticky v-if="firedSorts.length" :offsetTop="-44">
|
|
|
+ <view class="px-20 pb-20 fx-row bg-white">
|
|
|
+ <uv-tags v-for="s in firedSorts" :key="s.name" :text="s.short" :icon="s.icon" size="mini"
|
|
|
+ type="success" closable plain-fill @close="handleSortRemove(s)"/>
|
|
|
+ </view>
|
|
|
+ </uv-sticky>
|
|
|
+ <view class="p-20 fx-col gap-20">
|
|
|
+ <view v-for="(college,index) in pagedSelectedList" class="fx-row fx-bet-sta bg-white mx-card">
|
|
|
+ <view class="ml-10 mb-10 text-sm">
|
|
|
+ <text class="fx-row rounded-b-full px-15 pt-5 pb-10 text-white bg-primary">
|
|
|
+ {{ generateSeq(index) }}
|
|
|
+ </text>
|
|
|
+ </view>
|
|
|
+ <view class="flex-1 p-20 fx-col">
|
|
|
+ <view class="font-bold">
|
|
|
+ {{ college.university.name }}
|
|
|
+ <!-- TODO:志愿表还未保存group信息,所以这里显示会在编辑时表现不一致 -->
|
|
|
+ <template v-if="false">({{ college.recruitPlan.group }})</template>
|
|
|
+ ({{ college.recruitPlan.collegeCode }})
|
|
|
+ </view>
|
|
|
+ <view class="fx-row fx-bet-cen mt-10 text-content text-2xs gap-20">
|
|
|
+ <view>录取概率:
|
|
|
+ <view class="font-bold text-main">
|
|
|
+ {{ college.enrollRatio || '-' }}%
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <view>最低位次:
|
|
|
+ <view class="font-bold text-main">
|
|
|
+ {{ college.history && college.history.seat || '-' }}
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <view>院校排名:
|
|
|
+ <view class="font-bold text-main">
|
|
|
+ {{ college.university.ranking || '-' }}
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <view class="fx-row gap-20 my-20">
|
|
|
+ <uv-button :disabled="selectedList.length<2" type="primary" size="mini" plain
|
|
|
+ shape="circle" icon="arrow-down-fill" :text="generateSeq(index)"
|
|
|
+ icon-color="primary" @click="openSeqSelect(index)"/>
|
|
|
+ <uv-button :disabled="index==0" type="primary" size="mini" plain shape="circle"
|
|
|
+ icon="arrow-up" icon-color="primary" @click="handleMoveUp(college)"/>
|
|
|
+ <uv-button :disabled="index==selectedList.length-1" type="primary" size="mini" plain
|
|
|
+ shape="circle" icon="arrow-down" icon-color="primary"
|
|
|
+ @click="handleMoveDown(college)"/>
|
|
|
+ <uv-button type="primary" size="mini" plain shape="circle" icon="trash"
|
|
|
+ icon-color="primary" @click="handleRemoveAll(college)"/>
|
|
|
+ </view>
|
|
|
+ <m-drag ref="drag" :list="getSelectedSortedMajors(college)" :item-height="44"
|
|
|
+ @change="handleDragComplete">
|
|
|
+ <template #default="{item:major,index:majorIndex}">
|
|
|
+ <view class="fx-row items-center text-xs text-content h-[44px] box-border mx-border-b">
|
|
|
+ <view class="flex-1 truncate">
|
|
|
+ <text v-if="majorIndex>-1" class="mr-20">{{ majorIndex + 1 }}</text>
|
|
|
+ <text :class="{'highlight-major': isFormedMajorFired(major)}">
|
|
|
+ {{ major.marjorName }}[{{ major.marjorBelongs }}]
|
|
|
+ </text>
|
|
|
+ </view>
|
|
|
+ <!-- 因为手机上区域比较小,只保留了拖拽排序 -->
|
|
|
+ <uv-icon v-if="false" name="arrow-up" size="20px" class="mr10"
|
|
|
+ @click="handleMajorUp(major, college)"></uv-icon>
|
|
|
+ <uv-icon v-if="false" name="arrow-down" size="20px" class="mr10"
|
|
|
+ @click="handleMajorDown(major, college)"></uv-icon>
|
|
|
+ <uv-icon name="trash" size="20px" class="mr10"
|
|
|
+ @click="handleMajorDelete(major, college)"></uv-icon>
|
|
|
+ </view>
|
|
|
+ </template>
|
|
|
+ </m-drag>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </scroll-view>
|
|
|
+ <view class="h-[50px] px-40 fx-row items-center mx-border-t">
|
|
|
+ <uv-button type="primary" text="保存志愿表" shape="circle" :loading="locking"
|
|
|
+ @click="handleSave"></uv-button>
|
|
|
+ </view>
|
|
|
+ <uv-action-sheet ref="actionSheet" :actions="sortList" :cancel-text="cancelSortText" @select="handleSort"
|
|
|
+ @cancel="handleSortReset"/>
|
|
|
+ <!-- We don't use `u-action-sheet` because there maybe a great number of seq options -->
|
|
|
+ <uv-picker ref="picker" title="指定志愿顺序" :columns="[sequenceOptions]" :default-index="[seqIndex]"
|
|
|
+ @confirm="handleSeq"/>
|
|
|
+ </uv-popup>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+import {ref, computed, watch} from 'vue';
|
|
|
+import {useInjectVoluntaryCart} from "@/pagesOther/pages/voluntary/hooks/useVoluntaryCartInjection";
|
|
|
+import {toValue} from "@vueuse/core";
|
|
|
+import {useVoluntarySortService} from "@/pagesOther/pages/voluntary/hooks/useVoluntarySortService";
|
|
|
+import {useInjectVoluntaryMajorHighlight} from "@/pagesOther/pages/voluntary/hooks/useVoluntaryMajorHighlightInjection";
|
|
|
+import {confirmAsync} from "@/utils/uni-helper";
|
|
|
+import {useInjectVoluntaryAssistant} from "@/pagesOther/pages/voluntary/hooks/useVoluntaryAssistantInjection";
|
|
|
+import {useInjectVoluntaryHeader} from "@/pagesOther/pages/voluntary/hooks/useVoluntaryHeaderInjection";
|
|
|
+
|
|
|
+const popup = ref(null)
|
|
|
+const drag = ref(null)
|
|
|
+const picker = ref(null)
|
|
|
+const actionSheet = ref(null)
|
|
|
+const seqIndex = ref(0)
|
|
|
+const nameEditing = ref(false) // 暂时不允许修改名称
|
|
|
+const {id, name, locking, selectedList, defaultSort, recalculatePureSelectedList} = useInjectVoluntaryCart()
|
|
|
+const {isFormedMajorFired, snapshotSearchingMajorWhenApply} = useInjectVoluntaryMajorHighlight()
|
|
|
+const {
|
|
|
+ firedSorts, sortList, cancelSortText, generateSeq, sequenceOptions,
|
|
|
+ handleSort, handleSortReset, handleSortRemove,
|
|
|
+ getSelectedSortedMajors, sortInterrupt,
|
|
|
+ handleMoveDown, handleMoveUp
|
|
|
+} = useVoluntarySortService(popup, defaultSort)
|
|
|
+const {isMock} = useInjectVoluntaryHeader()
|
|
|
+const {save} = useInjectVoluntaryAssistant()
|
|
|
+const anyDragging = computed(() => drag.value?.some(d => d.dragging))
|
|
|
+
|
|
|
+// optimization for faster rendering
|
|
|
+const localPageNum = ref(1)
|
|
|
+const localPageSize = ref(4)
|
|
|
+const pagedSelectedList = computed(() => {
|
|
|
+ return selectedList.value.slice(0, toValue(localPageNum) * toValue(localPageSize))
|
|
|
+})
|
|
|
+const handleGroupScroll = () => {
|
|
|
+ if (toValue(pagedSelectedList).length >= toValue(selectedList).length) return
|
|
|
+ localPageNum.value += 1
|
|
|
+}
|
|
|
+
|
|
|
+const openSortList = () => {
|
|
|
+ actionSheet.value.open()
|
|
|
+}
|
|
|
+
|
|
|
+const handleDragComplete = (newList, oldList) => {
|
|
|
+ // make localPriority exchange when drag complete.
|
|
|
+ const copyPriorities = oldList.map(i => i.localPriority)
|
|
|
+ copyPriorities.forEach((p, i) => newList[i].localPriority = p)
|
|
|
+}
|
|
|
+
|
|
|
+const handleRemoveAll = async (college) => {
|
|
|
+ await confirmAsync('确认删除该专业组下的全部专业?')
|
|
|
+ college.majors.forEach(m => {
|
|
|
+ m.selected = false
|
|
|
+ snapshotSearchingMajorWhenApply(m)
|
|
|
+ })
|
|
|
+ recalculatePureSelectedList()
|
|
|
+ sortInterrupt()
|
|
|
+}
|
|
|
+
|
|
|
+// sort for major item
|
|
|
+const handleMajorUp = (major, majorGroup) => {
|
|
|
+ // TODO: HM-DragSorts 已经没有再使用了,所以不存在深拷贝的问题,但这个方法目前没有使用,先不优化
|
|
|
+ const source = getSelectedSortedMajors(majorGroup)
|
|
|
+ // HM-DragSorts clone element will cause index error
|
|
|
+ const index = source.findIndex(s => s.marjorBelongs == major.marjorBelongs)
|
|
|
+ const targetIndex = index - 1
|
|
|
+ // exchange two majors' localPriority
|
|
|
+ if (targetIndex >= 0) {
|
|
|
+ major = source[index] // this is the original major
|
|
|
+ const targetMajor = source[targetIndex]
|
|
|
+ const temp = major.localPriority
|
|
|
+ major.localPriority = targetMajor.localPriority
|
|
|
+ targetMajor.localPriority = temp
|
|
|
+ }
|
|
|
+}
|
|
|
+const handleMajorDown = (major, majorGroup) => {
|
|
|
+ // TODO: HM-DragSorts 已经没有再使用了,所以不存在深拷贝的问题,但这个方法目前没有使用,先不优化
|
|
|
+ const source = getSelectedSortedMajors(majorGroup)
|
|
|
+ // HM-DragSorts clone element will cause index error
|
|
|
+ const index = source.findIndex(s => s.marjorBelongs == major.marjorBelongs)
|
|
|
+ const targetIndex = index + 1
|
|
|
+ // exchange two majors' localPriority
|
|
|
+ if (targetIndex < source.length) {
|
|
|
+ major = source[index] // this is the original major
|
|
|
+ const targetMajor = source[targetIndex]
|
|
|
+ const temp = major.localPriority
|
|
|
+ major.localPriority = targetMajor.localPriority
|
|
|
+ targetMajor.localPriority = temp
|
|
|
+ }
|
|
|
+}
|
|
|
+const handleMajorDelete = (major) => {
|
|
|
+ major.selected = false
|
|
|
+ snapshotSearchingMajorWhenApply(major)
|
|
|
+ if (recalculatePureSelectedList()) sortInterrupt()
|
|
|
+}
|
|
|
+
|
|
|
+const openSeqSelect = (groupIndex) => {
|
|
|
+ seqIndex.value = groupIndex
|
|
|
+ picker.value.open()
|
|
|
+}
|
|
|
+const handleSeq = ({indexs}) => {
|
|
|
+ const oldIndex = seqIndex.value
|
|
|
+ const newIndex = indexs[0]
|
|
|
+ if (oldIndex != newIndex) {
|
|
|
+ const seqGroup = selectedList.value[oldIndex]
|
|
|
+ selectedList.value.splice(oldIndex, 1)
|
|
|
+ selectedList.value.splice(newIndex, 0, seqGroup)
|
|
|
+ sortInterrupt()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const handleSave = async () => {
|
|
|
+ await save(isMock.value)
|
|
|
+}
|
|
|
+
|
|
|
+const open = () => {
|
|
|
+ localPageNum.value = 1
|
|
|
+ popup.value.open()
|
|
|
+}
|
|
|
+
|
|
|
+const close = () => {
|
|
|
+ popup.value.close()
|
|
|
+}
|
|
|
+
|
|
|
+defineExpose({open, close})
|
|
|
+// export default {
|
|
|
+// props: {
|
|
|
+// id: {
|
|
|
+// type: String | Number,
|
|
|
+// default: 0
|
|
|
+// },
|
|
|
+// name: {
|
|
|
+// type: String,
|
|
|
+// default: ''
|
|
|
+// },
|
|
|
+// show: {
|
|
|
+// type: Boolean,
|
|
|
+// default: false
|
|
|
+// },
|
|
|
+// selectedList: {
|
|
|
+// type: Array,
|
|
|
+// default: () => []
|
|
|
+// },
|
|
|
+// defaultSort: {
|
|
|
+// type: Array,
|
|
|
+// default: () => []
|
|
|
+// },
|
|
|
+// locking: {
|
|
|
+// type: Boolean,
|
|
|
+// default: false
|
|
|
+// }
|
|
|
+// },
|
|
|
+// data() {
|
|
|
+// return {
|
|
|
+// nameEditing: false,
|
|
|
+// confirmGroup: null,
|
|
|
+// showConfirm: false,
|
|
|
+// showSort: false,
|
|
|
+// showSeq: false,
|
|
|
+// seqIndex: 0,
|
|
|
+// }
|
|
|
+// },
|
|
|
+// watch: {
|
|
|
+// show: function (val) {
|
|
|
+// // u-popup will release dom elements when not showing
|
|
|
+// // so reset the current page number here
|
|
|
+// if (val) this.localPageNum = 1
|
|
|
+// }
|
|
|
+// },
|
|
|
+// methods: {
|
|
|
+// getSelectedSortedMajors(group) {
|
|
|
+// return group.majors.filter(m => m.selected).sort(MxConst.recommendMajorSortFn)
|
|
|
+// },
|
|
|
+// getGroupHeight(group) {
|
|
|
+// const majors = this.getSelectedSortedMajors(group)
|
|
|
+// return majors.length * 45
|
|
|
+// },
|
|
|
+// openSeqSelect(groupIndex) {
|
|
|
+// this.seqIndex = groupIndex
|
|
|
+// this.showSeq = true
|
|
|
+// },
|
|
|
+// handleSeq({indexs}) {
|
|
|
+// const oldIndex = this.seqIndex
|
|
|
+// const newIndex = indexs[0]
|
|
|
+// if (oldIndex != newIndex) {
|
|
|
+// const seqGroup = this.selectedList[oldIndex]
|
|
|
+// this.selectedList.splice(oldIndex, 1)
|
|
|
+// this.selectedList.splice(newIndex, 0, seqGroup)
|
|
|
+// this.sortInterrupt()
|
|
|
+// }
|
|
|
+// this.showSeq = false
|
|
|
+// },
|
|
|
+// close() {
|
|
|
+// this.$emit('closeVolunteer')
|
|
|
+// },
|
|
|
+// open() {
|
|
|
+//
|
|
|
+// },
|
|
|
+// save() {
|
|
|
+// this.$emit('save')
|
|
|
+// },
|
|
|
+// handleRemoveAll() {
|
|
|
+// this.confirmGroup.majors.forEach(m => {
|
|
|
+// m.selected = false
|
|
|
+// this.snapshotSearchingMajorWhenApply(m)
|
|
|
+// })
|
|
|
+// this.$emit('change')
|
|
|
+// this.showConfirm = false
|
|
|
+// },
|
|
|
+// // sort for major item
|
|
|
+// handleMajorUp(major, majorGroup) {
|
|
|
+// const source = this.getSelectedSortedMajors(majorGroup)
|
|
|
+// // HM-DragSorts clone element will cause index error
|
|
|
+// const index = source.findIndex(s => s.marjorBelongs == major.marjorBelongs)
|
|
|
+// const targetIndex = index - 1
|
|
|
+// // exchange two majors' localPriority
|
|
|
+// if (targetIndex >= 0) {
|
|
|
+// major = source[index] // this is the original major
|
|
|
+// const targetMajor = source[targetIndex]
|
|
|
+// const temp = major.localPriority
|
|
|
+// major.localPriority = targetMajor.localPriority
|
|
|
+// targetMajor.localPriority = temp
|
|
|
+// }
|
|
|
+// },
|
|
|
+// handleMajorDown(major, majorGroup) {
|
|
|
+// const source = this.getSelectedSortedMajors(majorGroup)
|
|
|
+// // HM-DragSorts clone element will cause index error
|
|
|
+// const index = source.findIndex(s => s.marjorBelongs == major.marjorBelongs)
|
|
|
+// const targetIndex = index + 1
|
|
|
+// // exchange two majors' localPriority
|
|
|
+// if (targetIndex < source.length) {
|
|
|
+// major = source[index] // this is the original major
|
|
|
+// const targetMajor = source[targetIndex]
|
|
|
+// const temp = major.localPriority
|
|
|
+// major.localPriority = targetMajor.localPriority
|
|
|
+// targetMajor.localPriority = temp
|
|
|
+// }
|
|
|
+// },
|
|
|
+// deleteMajor(major, majorGroup) {
|
|
|
+// const source = this.getSelectedSortedMajors(majorGroup)
|
|
|
+// // HM-DragSorts clone element will cause index error
|
|
|
+// const index = source.findIndex(s => s.marjorBelongs == major.marjorBelongs)
|
|
|
+// major = source[index] // this is the original major
|
|
|
+// major.selected = false
|
|
|
+// this.snapshotSearchingMajorWhenApply(major)
|
|
|
+// this.$emit('change')
|
|
|
+// },
|
|
|
+// handleMajorDrag(group, event) {
|
|
|
+// const source = this.getSelectedSortedMajors(group)
|
|
|
+// const oldIndex = event.index * 1
|
|
|
+// const newIndex = event.moveTo * 1
|
|
|
+// if (oldIndex == newIndex) return
|
|
|
+// // get localPriority one by one
|
|
|
+// const oldSeq = source.map(m => m.localPriority)
|
|
|
+// const oldMajor = source[oldIndex]
|
|
|
+// const newMajor = source[newIndex]
|
|
|
+// const newSource = [...source]
|
|
|
+// newSource.splice(oldIndex, 1)
|
|
|
+// newSource.splice(newIndex, 0, oldMajor)
|
|
|
+// // assign oldSeq to newSource one by one
|
|
|
+// newSource.forEach((m, idx) => {
|
|
|
+// m.localPriority = oldSeq[idx]
|
|
|
+// })
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped lang="scss">
|
|
|
+.uv-button-wrapper {
|
|
|
+ flex: 1 !important;
|
|
|
+}
|
|
|
+</style>
|