b339acb1 by lttnew

级位审核

1 parent d923ac96
1 <template> 1 <template>
2 <view> 2 <view class="add-apply-page">
3 <view class="pd30"> 3 <!-- 顶部步骤条 -->
4 <uni-steps :options="list1" :active="active" /> 4 <view class="steps-bar">
5 <view class="hasfixedbottom"> 5 <view class="step-item" :class="{ active: active >= 0, current: active == 0 }">
6 <view class="step-circle">1</view>
7 <view class="step-text">考级基本信息</view>
8 </view>
9 <view class="step-line" :class="{ active: active >= 1 }"></view>
10 <view class="step-item" :class="{ active: active >= 1, current: active == 1 }">
11 <view class="step-circle">2</view>
12 <view class="step-text">添加考生</view>
13 </view>
14 </view>
15
16 <view class="page-content">
17 <!-- 步骤1:考级基本信息 -->
6 <view class="wBox" v-if="active == 0"> 18 <view class="wBox" v-if="active == 0">
7 <uni-forms ref="baseForm" :modelValue="form" label-width="100"> 19 <uni-forms ref="baseForm" :modelValue="form" label-width="100">
8 <uni-forms-item label="考试名称"> 20 <uni-forms-item label="考试名称">
...@@ -24,182 +36,148 @@ ...@@ -24,182 +36,148 @@
24 <uni-forms-item label="考级地点" required> 36 <uni-forms-item label="考级地点" required>
25 <uni-easyinput v-model="form.examLocation" placeholder="考级地点" /> 37 <uni-easyinput v-model="form.examLocation" placeholder="考级地点" />
26 </uni-forms-item> 38 </uni-forms-item>
27 <uni-forms-item @updateData="updateData" :label="`考官${ec}`" v-for="ec in examinerForChoose" 39 <uni-forms-item :label="`考官${ec}`" v-for="ec in examinerForChoose" :key="ec">
28 :key="ec">
29 <view class="maskbox"> 40 <view class="maskbox">
30 <view class="mask" @click="selectFN(ec)"></view> 41 <view class="mask" @click="selectFN(ec)"></view>
31 <uni-easyinput v-model="form[`examiner_${ec}`]" clearable placeholder="点击选择考官" /> 42 <uni-easyinput v-model="form[`examiner_${ec}`]" clearable placeholder="点击选择考官" />
32 </view> 43 </view>
33 </uni-forms-item> 44 </uni-forms-item>
34
35 </uni-forms> 45 </uni-forms>
36
37 </view>
38 <view class="wBox" v-if="active == 1">
39 <!-- 添加考生 -->
40 <view class="flexbox">
41 <button class="btn-red-kx mini w100" @click="chooseOnline">
42 <uni-icons type="personadd" size="16" color="#AD181F"></uni-icons>
43 在线选择</button>
44 <!-- <button class="btn-red-kx mini w45" @click="handleUpdate">
45 <uni-icons type="upload" size="16" color="#AD181F"></uni-icons>
46 上传成绩单</button> -->
47 </view> 46 </view>
48 <view class="vipData mt30" style="flex-wrap:wrap"> 47
49 <view class="w25">合计:<text>{{tablePersonInfo.total}}</text></view> 48 <!-- 步骤2:添加考生 -->
50 <view class="w25" v-for="l in tablePersonInfo.levelArr" :key="l.level"> 49 <view class="step2-content" v-if="active == 1">
51 {{ szToHz(l.level) }}级:<text>{{l.num}}</text> 50 <!-- 考级信息卡片 -->
51 <view class="exam-info-card">
52 <view class="card-header">
53 <text class="card-title">考级信息</text>
52 </view> 54 </view>
55 <view class="info-grid">
56 <view class="info-item">
57 <text class="info-label">考试名称</text>
58 <text class="info-value">{{form.name || '-'}}</text>
53 </view> 59 </view>
54 <view class="userlist"> 60 <view class="info-item">
55 <view class="item" v-for="(n,index) in infoList" :key="index" 61 <text class="info-label">申请单位</text>
56 style="background-color: #fffafa;"> 62 <text class="info-value">{{form.memberName || '-'}}</text>
57 <view class="w100">
58 <view class="del" @click="handleDelete(n)">删除</view>
59 <view style="display: flex;">
60 <!--
61 <view class="photobox">
62 <image class="photo" v-if="n.photo" :src="n.photo" mode='aspectFill'></image>
63 <view class="colorful" v-else>{{n.realName?.slice(0,1)}}</view>
64 </view> -->
65 <view>
66 <view class="name">{{n.realName}} <text>{{n.perCode}}</text></view>
67 <view class="date">{{n.idcTypeStr}}{{n.idcCode}}</view>
68 </view> 63 </view>
64 <view class="info-item">
65 <text class="info-label">考试地点</text>
66 <text class="info-value">{{form.examLocation || '-'}}</text>
69 </view> 67 </view>
70 <view class="flexbox mt30"> 68 <view class="info-item">
71 <view> 69 <text class="info-label">考试时间</text>
72 原有级别 70 <text class="info-value">{{formatDateTime(form.startTime)}} - {{formatDateTime(form.endTime)}}</text>
73 <text style="padding: 15rpx 0;">{{ szToHz(n.levelOld) }}</text>
74 </view> 71 </view>
75 <view style="width: 40%;">
76 考试级别
77 <!-- <text style="padding: 15rpx 0;">{{ szToHz(n.levelNew) }}</text> -->
78 <view @click="changeLevelfather(n)">
79 <uni-data-select v-model="n.levelNew" :localdata="levelArr"
80 @change="changeLevel"></uni-data-select>
81 </view> 72 </view>
82
83 </view> 73 </view>
84 74
85 <view style="width: 30%;"> 75 <!-- 操作栏(红框顶部统计+添加按钮) -->
86 是否通过 76 <button class="btn-add-student" @click="goChooseStudent">
87 <view> 77 <uni-icons type="plus" size="16" color="#fff"></uni-icons>
88 <uni-data-select :clear="false" v-model="n.isPass" 78 添加考生
89 :localdata="range"></uni-data-select> 79 </button>
90 </view> 80 <view class="action-bar">
81 <view class="stat-info">
82 <text class="stat-total">{{tablePersonInfo.total || 0}}</text>
83 <view class="level-tags">
84 <view class="level-tag" v-for="l in tablePersonInfo.levelArr" :key="l.level">
85 {{ szToHz(l.level) }}级:{{l.num}}
91 </view> 86 </view>
92 </view> 87 </view>
93 </view> 88 </view>
94 89
95 </view> 90 </view>
96 <view class="nodata" v-if="infoList.length==0"> 91
97 <image mode="aspectFit" :src="config.baseUrl_api + '/fs/static/nodata.png'"></image> 92 <!-- 考生列表(红框主体) -->
98 <text>待添加考生</text> 93 <view class="student-list">
94 <view class="student-card" v-for="(n,index) in infoList" :key="index">
95 <!-- 左侧:头像+考生信息 -->
96 <view class="card-left">
97 <view class="avatar">
98 <image v-if="n.photo" :src="n.photo" mode="aspectFill" />
99 <image v-else :src="config.baseUrl_api + '/fs/static/tx@2x.png'"
100 mode="aspectFill">
101 </image>
102 <!-- <text v-else class="avatar-text">{{(n.realName || '').slice(0,1)}}</text> -->
99 </view> 103 </view>
104 <view class="student-info">
105 <view class="student-name">{{n.realName}} <text class="per-code">{{n.perCode}}</text></view>
106 <view class="student-idcard">{{n.idcTypeStr}}{{n.idcCode}}</view>
100 </view> 107 </view>
101 </view> 108 </view>
109
110 <!-- 右侧:原级别/考试级别/是否通过 -->
111 <view class="card-right">
112 <view class="level-item">
113 <text class="level-label">原级别</text>
114 <text class="level-value">{{ szToHz(n.levelOld) }}</text>
102 </view> 115 </view>
116 <view class="level-item">
117 <text class="level-label">考试级别</text>
118 <view class="select-wrapper" @click="changeLevelfather(n)">
119 <uni-data-select v-model="n.levelNew" :localdata="levelArr" @change="changeLevel" :clear="false" />
103 </view> 120 </view>
104 <view class="fixedBottom" v-if="active == 0">
105 <button class="btn-red-kx" style="width: 40%;" @click="submitForm(0)">保存</button>
106 <button class="btn-red" style="width: 40%;" @click="submitForm(1)">下一步</button>
107 </view> 121 </view>
108 <view class="fixedBottom" v-if="active == 1"> 122 <view class="level-item">
109 <button class="btn-red-kx" style="width: 25%;" @click="active=0">上一步</button> 123 <text class="level-label">是否通过</text>
110 <button class="btn-red-kx" style="width: 25%;" @click="submitForm2(0)">保存</button> 124 <view class="select-wrapper">
111 <button class="btn-red" style="width: 30%;" @click="submitForm2(1)">提交审核</button> 125 <uni-data-select v-model="n.isPass" :localdata="range" :clear="false" />
112 </view> 126 </view>
113
114
115 <uni-popup ref="choseStudent" type="bottom" background-color="#fff" animation>
116 <view class="popBody">
117 <view class="searchbar">
118 <uni-easyinput placeholderStyle="font-size:30rpx" :input-border="false" prefixIcon="search"
119 v-model="studentQuery.name" placeholder="搜索姓名" @blur="chooseOnline" @clear="chooseOnline">
120 </uni-easyinput>
121 </view> 127 </view>
122 <view class="userlist" style="height:80vh;overflow: auto;">
123 <view class="item" v-for=" (n,index) in studentList" :key="index">
124 <view @click="checkThis(n)">
125 <image class="icon" v-if="n.checked"
126 :src="config.baseUrl_api+'/fs/static/member/dx_dwn.png'" />
127 <image class="icon" v-else :src="config.baseUrl_api+'/fs/static/member/dx.png'" />
128 </view> 128 </view>
129 <view class="photobox"> 129
130 <image class="photo" v-if="n.photo" :src="n.photo" mode='aspectFill'> 130 <!-- 删除按钮 -->
131 </image> 131 <view class="delete-btn" @click="handleDelete(n)">
132 <view class="colorful" v-else>{{n.name.slice(0,1)}}</view> 132 <uni-icons type="trash" size="18" color="#dd524d"></uni-icons>
133 </view> 133 </view>
134 <view>
135 <view class="name">{{n.name}} <text>{{n.perCode}}</text></view>
136 <view class="date">到期时间:{{n.validityDate}}</view>
137 <view class="date" style="color: #1561CB;" v-if="n.levelJi&&n.levelJi!=0">
138 级位:{{szToHz(n.levelJi)}}</view>
139 <view class="date" v-else>级位:十级</view>
140 </view> 134 </view>
135
136 <!-- 空状态 -->
137 <view class="empty" v-if="infoList.length==0">
138 <image class="empty-img" mode="aspectFit" :src="config.baseUrl_api + '/fs/static/nodata.png'" />
139 <text class="empty-text">暂无考生信息</text>
141 </view> 140 </view>
142 <view class="nodata" v-if="studentList.length==0">
143 <image mode="aspectFit" :src="config.baseUrl_api + '/fs/static/nodata.png'"></image>
144 <text>无可参加考试会员</text>
145 </view> 141 </view>
146 </view> 142 </view>
147 <button class="btn-red-kx" v-if="studentList.length!=0" @click="submitStudents">确定</button>
148 </view> 143 </view>
149 </uni-popup>
150
151 <uni-popup ref="UpPop" type="bottom" background-color="#fff" animation>
152 <view class="popBody">
153 <view class="h3 text-center">上传成绩单</view>
154 <text class="must">*请上传大小不超过 10MB 格式为 png/jpg/jpeg/pdf/zip 的文件</text>
155 <uni-file-picker v-model="transcript" class="mtb30" file-mediatype="all"
156 file-extname="png,jpg,jpeg,pdf,zip" @select="selectFile" @progress="fileProgress"
157 @delete="delSupplementFile"></uni-file-picker>
158 144
159 <button class="btn-red" @click="uploadSure">确定</button> 145 <!-- 底部按钮 -->
146 <view class="fixedBottom" v-if="active == 0">
147 <button class="btn-red-kx" style="width: 40%;" @click="submitForm(0)">保存</button>
148 <button class="btn-red" style="width: 40%;" @click="submitForm(1)">下一步</button>
149 </view>
150 <view class="fixedBottom" v-if="active == 1">
151 <button class="btn-red-kx" style="width: 25%;" @click="prev">上一步</button>
152 <button class="btn-red-kx" style="width: 25%;" @click="submitForm2(0)">保存</button>
153 <button class="btn-red" style="width: 30%;" @click="submitForm2(1)">提交审核</button>
160 </view> 154 </view>
161 </uni-popup>
162 155
163 </view> 156 </view>
164 </template> 157 </template>
165 158
166 <script setup> 159 <script setup>
167 import { 160 import { ref } from 'vue';
168 ref
169 } from 'vue';
170 import * as api from '@/common/api.js'; 161 import * as api from '@/common/api.js';
171 import { 162 import { onLoad, onShow } from '@dcloudio/uni-app';
172 onLoad,
173 onShow
174 } from '@dcloudio/uni-app';
175 import config from '@/config.js' 163 import config from '@/config.js'
176 import dayjs from 'dayjs' 164 import dayjs from 'dayjs'
177 import _ from 'underscore' 165 import _ from 'underscore'
166
178 const app = getApp(); 167 const app = getApp();
179 const memberInfo = app.globalData.memberInfo 168 const memberInfo = app.globalData.memberInfo
169
180 const form = ref({ 170 const form = ref({
181 type: 1 171 type: '1'
182 }); 172 });
183 const dataList = ref([]); 173
184 const examinerForChoose = ['A', 'B', 'C'] 174 const examinerForChoose = ['A', 'B', 'C']
185 const examinerArr = [] 175 let examinerArr = []
186 const active = ref(0) 176 const active = ref(0)
187 const total = ref(0)
188 const list1 = ref([{
189 title: '考级基本信息'
190 }, {
191 title: '添加考生'
192 }])
193 const choseStudent = ref(null)
194 const UpPop = ref(null)
195 const studentList = ref([])
196 const infoList = ref([]) 177 const infoList = ref([])
197 const ids = ref([])
198 const tablePersonInfo = ref({}) 178 const tablePersonInfo = ref({})
199 const transcript = ref([]) 179 const transcript = ref([]) // 补全缺失变量
200 const studentQuery = ref({ 180
201 name: ''
202 })
203 const levelArr = ref([{ 181 const levelArr = ref([{
204 value: '10', 182 value: '10',
205 text: '十级' 183 text: '十级'
...@@ -231,6 +209,7 @@ ...@@ -231,6 +209,7 @@
231 value: '1', 209 value: '1',
232 text: '一级' 210 text: '一级'
233 }]) 211 }])
212
234 const range = ref([{ 213 const range = ref([{
235 value: '1', 214 value: '1',
236 text: '是' 215 text: '是'
...@@ -238,41 +217,41 @@ ...@@ -238,41 +217,41 @@
238 value: '0', 217 value: '0',
239 text: '否' 218 text: '否'
240 }]) 219 }])
220
241 let examId 221 let examId
222
242 onLoad(option => { 223 onLoad(option => {
243 console.log(option)
244 if (app.globalData.isLogin) { 224 if (app.globalData.isLogin) {
245 form.value.memberName = app.globalData.memberInfo.name 225 initData(option)
246 form.value.applyTime = dayjs().format('YYYY-MM-DD')
247
248 _.each(examinerForChoose, ec => {
249 form.value[`examiner_${ec}`] = null
250 })
251 if (option.examId) {
252 examId = option.examId
253
254 getDetail()
255 }
256 } else { 226 } else {
257
258 app.firstLoadCallback = () => { 227 app.firstLoadCallback = () => {
228 initData(option)
229 };
230 }
231 });
232
233 function initData(option) {
259 form.value.memberName = app.globalData.memberInfo.name 234 form.value.memberName = app.globalData.memberInfo.name
260 form.value.applyTime = dayjs().format('YYYY-MM-DD') 235 form.value.applyTime = dayjs().format('YYYY-MM-DD')
261 236
262 _.each(examinerForChoose, ec => { 237 _.each(examinerForChoose, ec => {
263 form.value[`examiner_${ec}`] = null 238 form.value[`examiner_${ec}`] = null
264 }) 239 })
240
265 if (option.examId) { 241 if (option.examId) {
266 examId = option.examId 242 examId = option.examId
243 // 如果是编辑模式,从URL参数获取active
244 if (option.step == '2') {
245 active.value = 1
246 getDetail()
247 getChosedStudentList()
248 } else {
267 getDetail() 249 getDetail()
268 } 250 }
269 };
270 } 251 }
271 }); 252 }
272 onShow(option => { 253
273 // if(!!option){ 254 onShow(() => {
274 // console.log(option)
275 // }
276 uni.$on('chosen', updateData) 255 uni.$on('chosen', updateData)
277 }) 256 })
278 257
...@@ -282,30 +261,28 @@ ...@@ -282,30 +261,28 @@
282 } 261 }
283 262
284 function getDetail() { 263 function getDetail() {
285 api.getLevelApplyInfo(examId).then(res => { 264 api.getLevelApplyInfo(examId || form.value.examId).then(res => {
286 const data = res.data 265 const data = res.data
287 if (data.examiner) { 266 if (data.examiner) {
288 _.each(data.examiner.split(','), (id) => { 267 _.each(data.examiner.split(','), (id, i) => {
289 examinerArr.push({ 268 examinerArr[i] = { perId: id }
290 perId: id
291 })
292 }) 269 })
293 270
294 _.each(data.examinerNames.split(','), (name, i) => { 271 _.each(data.examinerNames.split(','), (name, i) => {
295 data[`examiner_${examinerForChoose[i]}`] = name 272 data[`examiner_${examinerForChoose[i]}`] = name
273 if (examinerArr[i]) {
296 examinerArr[i].name = name 274 examinerArr[i].name = name
275 }
297 }) 276 })
298 } 277 }
299
300 form.value = data 278 form.value = data
301 }) 279 })
302 } 280 }
303 281
304 function selectFN(ec) { 282 function selectFN(ec) {
305 const chosen = [] 283 const chosen = []
306 const type = form.value.type 284 _.each(examinerForChoose, ecKey => {
307 _.each(examinerForChoose, ec => { 285 const key = `examiner_${ecKey}`
308 const key = `examiner_${ec}`
309 if (form.value[key]) { 286 if (form.value[key]) {
310 const examiner = _.find(examinerArr, (e) => { 287 const examiner = _.find(examinerArr, (e) => {
311 return e.name == form.value[key] 288 return e.name == form.value[key]
...@@ -316,8 +293,7 @@ ...@@ -316,8 +293,7 @@
316 } 293 }
317 }) 294 })
318 const arr = encodeURIComponent(JSON.stringify(chosen)) 295 const arr = encodeURIComponent(JSON.stringify(chosen))
319 console.log(ec, chosen, type) 296 let path = `/level/chooseExaminer?type=${form.value.type}&chosen=${arr}&ec=${ec}`
320 let path = `/level/chooseExaminer?type=${type}&chosen=${arr}&ec=${ec}`
321 uni.navigateTo({ 297 uni.navigateTo({
322 url: path 298 url: path
323 }); 299 });
...@@ -347,33 +323,45 @@ ...@@ -347,33 +323,45 @@
347 form.value.examiner = null 323 form.value.examiner = null
348 form.value.examinerNames = null 324 form.value.examinerNames = null
349 } 325 }
326
327 // draftFlag: 0-保存 1-保存并下一步
350 form.value.draftFlag = flag === 0 ? '1' : '0' 328 form.value.draftFlag = flag === 0 ? '1' : '0'
329
351 if (flag === 0) { 330 if (flag === 0) {
331 // 仅保存
352 save() 332 save()
353 } else { 333 } else {
334 // 保存并下一步 - 需校验
335 if (!form.value.applyTime) {
336 uni.showToast({ title: '请选择申请日期', icon: 'none' })
337 return
338 }
339 if (!form.value.startTime) {
340 uni.showToast({ title: '请选择考试开始时间', icon: 'none' })
341 return
342 }
343 if (!form.value.endTime) {
344 uni.showToast({ title: '请选择考试结束时间', icon: 'none' })
345 return
346 }
347 if (!form.value.examLocation) {
348 uni.showToast({ title: '请输入考级地点', icon: 'none' })
349 return
350 }
354 if (dayjs(form.value.startTime).valueOf() < dayjs(form.value.applyTime).valueOf()) { 351 if (dayjs(form.value.startTime).valueOf() < dayjs(form.value.applyTime).valueOf()) {
355 uni.showToast({ 352 uni.showToast({ title: '考试开始时间应大于申请日期', icon: 'none' })
356 title: `考试开始时间应大于申请日期`,
357 icon: 'none'
358 })
359 return 353 return
360 } 354 }
361 if (dayjs(form.value.endTime).valueOf() <= dayjs(form.value.startTime).valueOf()) { 355 if (dayjs(form.value.endTime).valueOf() <= dayjs(form.value.startTime).valueOf()) {
362 uni.showToast({ 356 uni.showToast({ title: '考试结束时间应大于考试开始时间', icon: 'none' })
363 title: `考试结束时间应大于考试开始时间`,
364 icon: 'none'
365 })
366 return 357 return
367 } 358 }
368 if (examinerIds.length % 2 === 0) { 359 if (examinerIds.length % 2 === 0) {
369 uni.showToast({ 360 uni.showToast({ title: '录入的考官人数必须为单数', icon: 'none' })
370 title: `录入的考官人数必须为单数`,
371 icon: 'none'
372 })
373 return 361 return
374 } 362 }
363
375 save().then(() => { 364 save().then(() => {
376 // form.value.examId 下一步
377 active.value = 1 365 active.value = 1
378 getChosedStudentList() 366 getChosedStudentList()
379 if (form.value.examId) { 367 if (form.value.examId) {
...@@ -390,77 +378,31 @@ ...@@ -390,77 +378,31 @@
390 function save() { 378 function save() {
391 if (form.value.examId) { 379 if (form.value.examId) {
392 return api.updateLevelInfo(form.value).then(() => { 380 return api.updateLevelInfo(form.value).then(() => {
393 uni.showToast({ 381 uni.showToast({ title: '保存成功', icon: 'none' })
394 title: `保存成功`,
395 icon: 'none'
396 })
397 }) 382 })
398 } else { 383 } else {
399 return api.addLevelInfo(form.value).then((res) => { 384 return api.addLevelInfo(form.value).then((res) => {
400 form.value.examId = res.data.examId 385 form.value.examId = res.data.examId
401 form.value.name = res.data.name 386 form.value.name = res.data.name
402 uni.showToast({ 387 uni.showToast({ title: '保存成功', icon: 'none' })
403 title: `保存成功`,
404 icon: 'none'
405 })
406 }) 388 })
407 } 389 }
408 } 390 }
409 391
410 function chooseOnline() { 392 function prev() {
411 uni.showLoading({ 393 active.value = 0
412 title: '加载中',
413 icon: 'none'
414 })
415 var obj = {
416 memId: memberInfo.memId,
417 examId: form.value.examId,
418 examType: form.value.type,
419 name: studentQuery.value.name
420 }
421 api.chooseStudentsList(obj).then(response => {
422 studentList.value = response.rows
423 for (var s of studentList.value) {
424 s.checked = false
425 if (s.photo && s.photo.indexOf('http') == -1) {
426 s.photo = config.baseUrl_api + s.photo
427 } 394 }
428 395
429 } 396 function goChooseStudent() {
430 uni.hideLoading() 397 uni.navigateTo({
431 choseStudent.value.open() 398 url: `/level/chooseStudent?examId=${form.value.examId}&memId=${memberInfo.memId}&examType=${form.value.type}`
432 }) 399 })
433 } 400 }
434 401
435 function checkThis(item) { 402 // 格式化日期时间
436 if (item.checked) { 403 function formatDateTime(dateStr) {
437 item.checked = false 404 if (!dateStr) return '-'
438 } else { 405 return dateStr.substring(0, 10)
439 item.checked = true
440 }
441 }
442
443 function submitStudents() {
444 ids.value = []
445 for (var s of studentList.value) {
446 if (s.checked) {
447 ids.value.push(s.perId)
448 }
449 }
450 if (ids.value.length == 0) {
451 uni.showToast({
452 title: '请选择考生',
453 icon: 'none'
454 })
455 return
456 }
457 api.batchChoose({
458 examId: form.value.examId,
459 perIds: ids.value
460 }).then(() => {
461 getChosedStudentList()
462 choseStudent.value.close()
463 })
464 } 406 }
465 407
466 function getChosedStudentList() { 408 function getChosedStudentList() {
...@@ -491,21 +433,20 @@ ...@@ -491,21 +433,20 @@
491 }) 433 })
492 434
493 infoList.value = res.rows 435 infoList.value = res.rows
494
495 }).then(getTablePersonInfo) 436 }).then(getTablePersonInfo)
496 } 437 }
497 438
498 function getTablePersonInfo() { 439 function getTablePersonInfo() {
499 const total = infoList.value.length 440 const total = infoList.value.length
500 const levelArr = [] 441 const levelArrData = []
501 _.each(infoList.value, (d) => { 442 _.each(infoList.value, (d) => {
502 const temp = _.find(levelArr, (l) => { 443 const temp = _.find(levelArrData, (l) => {
503 return l.level == d.levelNew 444 return l.level == d.levelNew
504 }) 445 })
505 if (temp) { 446 if (temp) {
506 temp.num++ 447 temp.num++
507 } else { 448 } else {
508 levelArr.push({ 449 levelArrData.push({
509 level: d.levelNew, 450 level: d.levelNew,
510 num: 1 451 num: 1
511 }) 452 })
...@@ -514,7 +455,7 @@ ...@@ -514,7 +455,7 @@
514 455
515 tablePersonInfo.value = { 456 tablePersonInfo.value = {
516 total: total, 457 total: total,
517 levelArr: _.sortBy(levelArr, (l) => { 458 levelArr: _.sortBy(levelArrData, (l) => {
518 return l.level 459 return l.level
519 }) 460 })
520 } 461 }
...@@ -540,13 +481,9 @@ ...@@ -540,13 +481,9 @@
540 }) 481 })
541 } 482 }
542 483
543
544 function changeLevel(e) { 484 function changeLevel(e) {
545 if (e == nowRow.levelOld) { 485 if (e == nowRow.levelOld) {
546 uni.showToast({ 486 uni.showToast({ title: '考试级别重复,请重新选择!', icon: 'none' })
547 title: `考试级别重复,请重新选择!`,
548 icon: 'none'
549 })
550 nowRow.levelNew = nowRow.levelRecommend 487 nowRow.levelNew = nowRow.levelRecommend
551 return 488 return
552 } 489 }
...@@ -557,7 +494,7 @@ ...@@ -557,7 +494,7 @@
557 success: function(res) { 494 success: function(res) {
558 if (res.confirm) { 495 if (res.confirm) {
559 getTablePersonInfo() 496 getTablePersonInfo()
560 }else{ 497 } else {
561 nowRow.levelNew = nowRow.levelRecommend 498 nowRow.levelNew = nowRow.levelRecommend
562 } 499 }
563 }, 500 },
...@@ -569,71 +506,41 @@ ...@@ -569,71 +506,41 @@
569 } 506 }
570 507
571 function submitForm2(flag) { 508 function submitForm2(flag) {
572 //循环infoList.value 如果item.levelNew == item.levelOld 提示错误 509 // 循环校验考试级别
573 for (var item of infoList.value) { 510 for (var item of infoList.value) {
574 if (item.levelNew == item.levelOld) { 511 if (item.levelNew == item.levelOld) {
575 uni.showToast({ 512 uni.showToast({ title: `${item.realName}考试级别重复,请重新选择!`, icon: 'none' })
576 title: `${item.realName}考试级别重复,请重新选择!`,
577 icon: 'none'
578 })
579 return 513 return
580 } 514 }
581 if (!item.levelNew) { 515 if (!item.levelNew) {
582 uni.showToast({ 516 uni.showToast({ title: `${item.realName}请选择考试级别!`, icon: 'none' })
583 title: `${item.realName}请选择考试级别!`,
584 icon: 'none'
585 })
586 return 517 return
587 } 518 }
588 } 519 }
520
589 if (flag === 1) { 521 if (flag === 1) {
590 if (infoList.value.length == 0) { 522 if (infoList.value.length == 0) {
591 uni.showToast({ 523 uni.showToast({ title: '请选择考生', icon: 'none' })
592 title: '请选择考生',
593 icon: 'none'
594 })
595 return 524 return
596 } 525 }
597 526
598 // if (!form.value.transcript) {
599 // uni.showToast({
600 // title: '请上传成绩单',
601 // icon: 'none'
602 // })
603 // return
604 // }
605
606 uni.showModal({ 527 uni.showModal({
607 title: '提示', 528 title: '提示',
608 content: `请确认人员照片是否已更新?`, 529 content: `请确认人员照片是否已更新?`,
609 success: function(res) { 530 success: function(res) {
610 if (res.confirm) { 531 if (res.confirm) {
611 console.log('用户点击确定'); 532 saveStep2(flag).then(() => {
612 saveStep2(flag).then(Response => { 533 uni.showToast({ title: '提交成功', icon: 'none' })
613 if (flag === 1) {
614 uni.showToast({
615 title: `提交成功`
616 })
617 uni.navigateTo({ 534 uni.navigateTo({
618 url: `/level/paymentDetail?examId=${form.value.examId}` 535 url: `/level/paymentDetail?examId=${form.value.examId}`
619 }) 536 })
620 } else {
621 uni.showToast({
622 title: `操作成功`
623 }) 537 })
624 } 538 }
625 })
626 } else if (res.cancel) {
627 console.log('用户点击取消');
628 }
629
630 } 539 }
631 }) 540 })
632 } else { 541 } else {
633 saveStep2(flag).then(res => { 542 saveStep2(flag).then(() => {
634 uni.showToast({ 543 uni.showToast({ title: '操作成功', icon: 'none' })
635 title: `操作成功`
636 })
637 }) 544 })
638 } 545 }
639 } 546 }
...@@ -643,7 +550,6 @@ ...@@ -643,7 +550,6 @@
643 return { 550 return {
644 id: d.id, 551 id: d.id,
645 levelNew: d.levelNew, 552 levelNew: d.levelNew,
646 // score: d.score,
647 isPass: d.isPass 553 isPass: d.isPass
648 } 554 }
649 }) 555 })
...@@ -655,88 +561,332 @@ ...@@ -655,88 +561,332 @@
655 }) 561 })
656 } 562 }
657 563
658 function handleUpdate() { 564 function handleDelete(row) {
659 UpPop.value.open() 565 uni.showModal({
566 title: '提示',
567 content: `确定删除${row.realName}?`,
568 success: function(res) {
569 if (res.confirm) {
570 api.dellevelPerson(row.id).then(() => {
571 uni.showToast({ title: '操作成功', icon: 'none' })
572 getChosedStudentList()
573 })
574 }
575 }
576 })
577 }
578 </script>
579 <style lang="scss" scoped>
580 .add-apply-page {
581 min-height: 100vh;
582 background: #f5f5f5;
583 padding-bottom: 120rpx;
584 }
585
586 /* 顶部步骤条 */
587 .steps-bar {
588 display: flex;
589 align-items: center;
590 justify-content: center;
591 padding: 15rpx 60rpx;
592 background: #fff;
593 margin-top: 20rpx;
594
595 .step-item {
596 display: flex;
597 align-items: center;
598
599 .step-circle {
600 width: 48rpx;
601 height: 48rpx;
602 border-radius: 50%;
603 background: #fff;
604 border: 2rpx solid #666;
605 color: #666;
606 font-size: 24rpx;
607 display: flex;
608 align-items: center;
609 justify-content: center;
610 margin-right: 10rpx;
611 transition: all 0.3s;
612 }
613
614 .step-text {
615 font-size: 24rpx;
616 color: #666;
617 transition: all 0.3s;
618 }
619
620 &.active .step-circle {
621 background: #fff;
622 color: #AD181F;
623 border-color: #AD181F;
660 } 624 }
661 let selectFileValue = {}
662 625
663 function selectFile(e) { 626 &.active .step-text {
664 let file = e.tempFiles[0] 627 color: #AD181F;
665 if (!file) { 628 font-weight: 600;
666 return
667 } 629 }
668 for (const n in e.tempFiles) { 630
669 api.uploadFileList(e.tempFilePaths[n]).then(data => { 631 &.current .step-circle {
670 selectFileValue = { 632 background: #AD181F;
671 url: data, 633 color: #fff;
672 name: e.tempFiles[n].name, 634 box-shadow: 0 4rpx 12rpx rgba(173, 24, 31, 0.3);
673 extname: e.tempFiles[n].extname
674 } 635 }
675 636
676 transcript.value.push(selectFileValue) 637 &.current .step-text {
677 }); 638 color: #AD181F;
639 font-weight: 600;
640 }
678 } 641 }
679 642
643 .step-line {
644 width: 120rpx;
645 height: 4rpx;
646 background: #e0e0e0;
647 margin: 0 20rpx;
648 transition: all 0.3s;
649
650 &.active {
651 background: #AD181F;
652 }
653 }
680 } 654 }
681 655
682 function fileProgress(e) { 656 /* 步骤2样式 */
683 console.log('progress:' + e) 657 .step2-content {
658 padding: 0 20rpx;
659 margin-top: 20rpx;
684 } 660 }
685 661
686 function delSupplementFile(e) { 662 /* 考级信息卡片 */
687 transcript.value = _.remove(transcript.value, function(n) { 663 .exam-info-card {
688 return n.name != e.tempFile.name; 664 background: #fff;
689 }); 665 border-radius: 16rpx;
666 margin-bottom: 20rpx;
667 overflow: hidden;
668 box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
669
670 .card-header {
671 padding: 24rpx 30rpx;
672 background: linear-gradient(135deg, #AD181F 0%, #c42a2a 100%);
673
674 .card-title {
675 font-size: 28rpx;
676 font-weight: 600;
677 color: #fff;
678 }
690 } 679 }
691 680
692 function uploadSure() { 681 .info-grid {
693 // 上传确定 682 display: flex;
694 if (transcript.value.length == 0) { 683 flex-wrap: wrap;
695 uni.showToast({ 684 padding: 20rpx 0;
696 title: `请上传成绩单`, 685
697 icon: 'error' 686 .info-item {
698 }) 687 width: 100%;
699 return 688 padding: 12rpx 30rpx;
689 box-sizing: border-box;
690 display: flex;
691
692 .info-label {
693 display: block;
694 font-size: 24rpx;
695 color: #666;
696 margin-bottom: 8rpx;
697
700 } 698 }
701 699
702 form.value.transcript = JSON.stringify(_.map(transcript.value, (t) => { 700 .info-value {
703 return { 701 display: block;
704 name: t.name, 702 font-size: 26rpx;
705 url: t.url 703 color: #333;
704 font-weight: 500;
705 margin-left: 20rpx;
706 }
707 }
706 } 708 }
707 }))
708 UpPop.value.close()
709 } 709 }
710 710
711 function handleDelete(row) { 711 /* 操作栏(统计+添加按钮) */
712 uni.showModal({ 712 .action-bar {
713 title: '提示', 713 display: flex;
714 content: `确定删除${row.realName}?`, 714 justify-content: space-between;
715 success: function(res) { 715 align-items: center;
716 if (res.confirm) { 716 padding: 20rpx 24rpx;
717 api.dellevelPerson(row.id).then(res => { 717 background: #fff;
718 uni.showToast({ 718 border-radius: 16rpx;
719 title: `操作成功` 719 margin: 20rpx 0;
720 }) 720
721 getChosedStudentList() 721 .stat-info {
722 }) 722 display: flex;
723 align-items: center;
724 // justify-self: unset;
725 .stat-total {
726 font-size: 28rpx;
727 font-weight: 600;
728 color: #333;
729 }
730
731 .level-tags {
732 display: flex;
733 flex-wrap: wrap;
734 margin-left: 10rpx;
735 gap: 10rpx;
736
737 .level-tag {
738 padding: 4rpx 12rpx;
739 background: #FFF5F5;
740 border: 1rpx solid #FFDDDD;
741 border-radius: 6rpx;
742 font-size: 24rpx;
743 color: #AD181F;
723 } 744 }
724 } 745 }
725 })
726 } 746 }
727 </script>
728 747
729 <style lang="scss" scoped> 748
730 :deep(.uni-progress-bar) { 749 }
731 display: none; 750 .btn-add-student {
751 display: flex;
752 align-items: center;
753 justify-content: center;
754 padding: 0 30rpx;
755 height: 64rpx;
756 background: linear-gradient(135deg, #AD181F 0%, #c42a2a 100%);
757 border-radius: 32rpx;
758 font-size: 26rpx;
759 color: #fff;
760 box-shadow: 0 4rpx 16rpx rgba(173, 24, 31, 0.3);
761 }
762 /* 考生列表(核心优化) */
763 .student-list {
764 .student-card {
765 position: relative;
766 // display: flex;
767
768 // align-items: center;
769 // justify-content: space-between;
770 padding: 24rpx;
771 background: #fff;
772 border-radius: 16rpx;
773 margin-bottom: 20rpx;
774 box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
775
776 /* 左侧:头像+考生信息 */
777 .card-left {
778 display: flex;
779 align-items: center;
780 flex: 1;
781
782 .avatar {
783 width: 80rpx;
784 height: 80rpx;
785 border-radius: 50%;
786 background: linear-gradient(135deg, #AD181F 0%, #c42a2a 100%);
787 display: flex;
788 align-items: center;
789 justify-content: center;
790 margin-right: 20rpx;
791 overflow: hidden;
792
793 .avatar-text {
794 font-size: 32rpx;
795 color: #fff;
796 font-weight: 600;
797 }
798
799 image {
800 width: 100%;
801 height: 100%;
802 }
732 } 803 }
733 804
734 .item { 805 .student-info {
735 .del { 806 .student-name {
736 color: #AD181F; 807 font-size: 28rpx;
737 position: absolute; 808 font-weight: 600;
738 right: 30rpx; 809 color: #333;
810 margin-bottom: 8rpx;
811
812 .per-code {
813 font-size: 24rpx;
814 color: #666;
815 font-weight: normal;
816 margin-left: 12rpx;
817 }
818 }
819
820 .student-idcard {
821 font-size: 24rpx;
822 color: #666;
823 }
824 }
825 }
826
827 /* 右侧:原级别/考试级别/是否通过(垂直布局,完美对齐) */
828 .card-right {
829 display: flex;
830 justify-content: space-between;
831 // padding:0 30px;
832 margin-top: 20rpx;
833 margin-left:100rpx;
834 // flex-direction: column;
835 // align-items: flex-end;
836 // gap: 16rpx;
837
838 .level-item {
839 display: flex;
840 flex-direction: column;
841 align-items: flex-end;
842
843 .level-label {
844 font-size: 24rpx;
845 color: #666;
846 margin-bottom: 6rpx;
847 }
848
849 .level-value {
739 font-size: 28rpx; 850 font-size: 28rpx;
851 color: #333;
852 font-weight: 500;
853 }
854
855 .select-wrapper {
856 width: 120rpx;
857 }
858 }
859 }
860
861 /* 删除按钮 */
862 .delete-btn {
863 position: absolute;
864 top: 16rpx;
865 right: 16rpx;
866 padding: 8rpx;
867 }
868 }
869
870 /* 空状态 */
871 .empty-state {
872 display: flex;
873 flex-direction: column;
874 align-items: center;
875 justify-content: center;
876 padding: 80rpx 0;
877 background: #fff;
878 border-radius: 16rpx;
879
880 image {
881 width: 240rpx;
882 height: 240rpx;
883 margin-bottom: 20rpx;
884 }
885
886 text {
887 font-size: 26rpx;
888 color: #666;
889 }
740 } 890 }
741 } 891 }
742 892
...@@ -753,10 +903,6 @@ ...@@ -753,10 +903,6 @@
753 padding-bottom: 20rpx; 903 padding-bottom: 20rpx;
754 } 904 }
755 905
756 .popBody {
757 padding: 40rpx 30rpx;
758 }
759
760 .maskbox { 906 .maskbox {
761 position: relative; 907 position: relative;
762 908
...@@ -770,7 +916,38 @@ ...@@ -770,7 +916,38 @@
770 } 916 }
771 } 917 }
772 918
773 :deep(.file-picker__progress) { 919 /* 底部按钮 */
774 opacity: 0; 920 .fixedBottom {
921 position: fixed;
922 bottom: 0;
923 left: 0;
924 right: 0;
925 display: flex;
926 justify-content: space-around;
927 align-items: center;
928 padding: 20rpx 0;
929 background: #fff;
930 border-top: 1rpx solid #f0f0f0;
931 z-index: 999;
932
933 .btn-red {
934 background: linear-gradient(135deg, #AD181F 0%, #c42a2a 100%);
935 color: #fff;
936 border: none;
937 border-radius: 32rpx;
938 height: 72rpx;
939 font-size: 28rpx;
940 font-weight: 600;
941 }
942
943 .btn-red-kx {
944 background: #fff;
945 color: #AD181F;
946 border: 2rpx solid #AD181F;
947 border-radius: 32rpx;
948 height: 72rpx;
949 font-size: 28rpx;
950 font-weight: 600;
951 }
775 } 952 }
776 </style> 953 </style>
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -11,24 +11,27 @@ ...@@ -11,24 +11,27 @@
11 <view class="indexboxre"> 11 <view class="indexboxre">
12 <view class="userlist"> 12 <view class="userlist">
13 <view class="item" v-for="(n,index) in list" :key="index"> 13 <view class="item" v-for="(n,index) in list" :key="index">
14 <view> 14 <view class="item-content">
15 <view class="name-row">
15 <view class="name">{{n.perName}}</view> 16 <view class="name">{{n.perName}}</view>
17 <view class="expired-tag" v-if="n.canChoose != 1">
18 <text class="expired-text">已过期</text>
19 </view>
20 </view>
16 <view class="date">会员号:{{n.perCode||'-'}}</view> 21 <view class="date">会员号:{{n.perCode||'-'}}</view>
17 <view class="date">有效日期:{{n.roleInfo && n.roleInfo.validTime ? n.roleInfo.validTime : '-'}}</view> 22 <view class="date">有效日期:{{ formatDate(n.roleInfo && n.roleInfo.validTime ? n.roleInfo.validTime : '-') }}</view>
18 <view class="date">注册地:{{n.memName||'-'}}</view> 23 <view class="date">注册地:{{n.memName||'-'}}</view>
19 <view class="date" :class="{'text-danger': n.canChoose != 1}">
20 状态:{{ n.canChoose == 1 ? '正常' : '资质已过期' }}
21 </view>
22 </view> 24 </view>
23 <view class="status"> 25 <view class="status">
24 <text v-if="isChosen(n)" class="text-gray">已选</text> 26 <text v-if="isChosen(n)" class="text-gray">已选</text>
25 <text v-else class="text-primary" @click="handleChoose(n)">选择</text> 27 <!-- <text v-else-if="n.canChoose != 1" class="text-gray">不可选</text> -->
28 <text v-if="n.canChoose == 1" class="text-primary" @click="handleChoose(n)">选择</text>
26 </view> 29 </view>
27 </view> 30 </view>
28 </view> 31 </view>
29 <view class="nodata" v-if="list.length==0 && !loading"> 32 <view class="empty" v-if="list.length==0 && !loading">
30 <image mode="aspectFit" :src="config.baseUrl_api + '/fs/static/nodata.png'"></image> 33 <image class="empty-img" mode="aspectFit" :src="config.baseUrl_api + '/fs/static/nodata.png'"></image>
31 <text>暂无考官数据</text> 34 <text class="empty-text">暂无考官数据</text>
32 </view> 35 </view>
33 </view> 36 </view>
34 37
...@@ -132,6 +135,16 @@ ...@@ -132,6 +135,16 @@
132 return chosen.some(c => c.perId == n.perId) 135 return chosen.some(c => c.perId == n.perId)
133 } 136 }
134 137
138 // 格式化日期,去掉时间部分
139 function formatDate(dateStr) {
140 if (!dateStr || dateStr === '-') return '-'
141 // 如果是完整日期时间格式,截取日期部分
142 if (dateStr.length > 10) {
143 return dateStr.substring(0, 10)
144 }
145 return dateStr
146 }
147
135 function handleChoose(row) { 148 function handleChoose(row) {
136 if (row.canChoose != 1) { 149 if (row.canChoose != 1) {
137 uni.showToast({ 150 uni.showToast({
...@@ -252,10 +265,33 @@ ...@@ -252,10 +265,33 @@
252 background: #fff; 265 background: #fff;
253 border-radius: 10rpx; 266 border-radius: 10rpx;
254 267
268 .item-content {
269 flex: 1;
270 }
271
272 .name-row {
273 display: flex;
274 align-items: center;
275 margin-bottom: 10rpx;
276 justify-content: space-between;
277 }
278
255 .name { 279 .name {
256 font-size: 32rpx; 280 font-size: 32rpx;
257 font-weight: bold; 281 font-weight: bold;
258 margin-bottom: 10rpx; 282 }
283
284 .expired-tag {
285 // margin-left: 15rpx;
286 // padding: 0 12rpx;
287 // background: #fff0f0;
288 // border: 1px solid #ffcccc;
289 // border-radius: 6rpx;
290 }
291
292 .expired-text {
293 font-size: 22rpx;
294 color: #dd524d;
259 } 295 }
260 296
261 .date { 297 .date {
......
1 <template>
2 <view class="choose-student-page">
3 <!-- 顶部搜索栏 -->
4 <view class="search-header">
5 <view class="search-box">
6 <uni-icons type="search" size="18" color="#999"></uni-icons>
7 <input
8 class="search-input"
9 v-model="searchName"
10 placeholder="搜索考生姓名"
11 @confirm="searchStudents"
12 />
13 <uni-icons v-if="searchName" type="clear" size="16" color="#999" @click="clearSearch"></uni-icons>
14 </view>
15 <text class="cancel-btn" @click="goBack">取消</text>
16 </view>
17
18 <!-- 已选考生 -->
19 <view class="selected-section" v-if="selectedList.length > 0">
20 <view class="section-header">
21 <text class="section-title">已选考生</text>
22 <text class="section-count">{{selectedList.length}}</text>
23 </view>
24 <scroll-view class="selected-scroll" scroll-x>
25 <view class="selected-item" v-for="(item, index) in selectedList" :key="index">
26 <view class="avatar-small">
27 <image v-if="item.photo" :src="item.photo" mode="aspectFill"></image>
28 <text v-else>{{(item.name || '').slice(0,1)}}</text>
29 </view>
30 <text class="name-small">{{item.name}}</text>
31 <view class="remove-btn" @click="removeSelected(index)">
32 <uni-icons type="closeempty" size="12" color="#fff"></uni-icons>
33 </view>
34 </view>
35 </scroll-view>
36 </view>
37
38 <!-- 考生列表 -->
39 <scroll-view class="student-scroll" scroll-y @scrolltolower="loadMore">
40 <view class="student-list">
41 <view class="student-card" v-for="(item, index) in studentList" :key="index" @click="toggleSelect(item)">
42 <view class="checkbox">
43 <image v-if="item.checked" src="/static/member/dx_dwn.png" mode="aspectFit"></image>
44 <view v-else class="checkbox-empty"></view>
45 </view>
46 <view class="avatar">
47 <image v-if="item.photo" :src="item.photo" mode="aspectFill"></image>
48 <image v-else :src="config.baseUrl_api + '/fs/static/tx@2x.png'"
49 mode="aspectFill">
50 </image>
51 <!-- <text v-else class="avatar-text">{{(item.name || '').slice(0,1)}}</text> -->
52 </view>
53 <view class="student-info">
54 <view class="student-name">
55 {{item.name}}
56 <text class="per-code">{{item.perCode}}</text>
57 </view>
58 <view class="student-detail">
59 <text class="level-info">级位:{{ formatLevel(item.levelJi) }}</text>
60 <text class="validity">到期:{{ formatDate(item.validityDate) }}</text>
61 </view>
62 </view>
63 </view>
64
65 <!-- 加载状态 -->
66 <view class="loading-more" v-if="loading">
67 <text>加载中...</text>
68 </view>
69 <view class="no-more" v-if="!loading && noMore && studentList.length > 0">
70 <text>没有更多了</text>
71 </view>
72
73 <!-- 空状态 -->
74 <view class="empty" v-if="!loading && studentList.length === 0">
75 <image class="empty-img" :src="config.baseUrl_api + '/fs/static/nodata.png'"></image>
76 <text class="empty-text">暂无考生信息</text>
77 </view>
78 </view>
79 </scroll-view>
80
81 <!-- 底部确定按钮 -->
82 <view class="fixedBottom">
83 <view class="bottom-info">
84 <text class="info-text">已选择 <text class="count">{{selectedList.length}}</text></text>
85 </view>
86 <view style="text-align:right">
87 <button class="btn-confirm" @click="confirmSelect">确定添加</button>
88 </view>
89
90 </view>
91 </view>
92 </template>
93
94 <script setup>
95 import * as api from '@/common/api.js'
96 import config from '@/config.js'
97 import {ref} from 'vue'
98 import { onLoad } from '@dcloudio/uni-app'
99
100 let examId = ''
101 let memId = ''
102 let examType = ''
103
104 const searchName = ref('')
105 const studentList = ref([])
106 const selectedList = ref([])
107 const loading = ref(false)
108 const noMore = ref(false)
109 const pageNum = ref(1)
110 const pageSize = 20
111
112 onLoad((option) => {
113 examId = option.examId || ''
114 memId = option.memId || ''
115 examType = option.examType || '1'
116 getStudentList()
117 })
118
119 function getStudentList(isMore = false) {
120 if (loading.value) return
121 loading.value = true
122
123 api.chooseStudentsList({
124 memId: memId,
125 examId: examId,
126 examType: examType,
127 name: searchName.value
128 }).then(res => {
129 const rows = res.rows || []
130 rows.forEach(item => {
131 item.checked = selectedList.value.some(s => s.perId === item.perId)
132 if (item.photo && item.photo.indexOf('http') == -1) {
133 item.photo = config.baseUrl_api + item.photo
134 }
135 })
136
137 if (isMore) {
138 studentList.value = [...studentList.value, ...rows]
139 } else {
140 studentList.value = rows
141 }
142
143 noMore.value = rows.length < pageSize
144 pageNum.value++
145 loading.value = false
146 }).catch(() => {
147 loading.value = false
148 })
149 }
150
151 function loadMore() {
152 if (!noMore.value) {
153 getStudentList(true)
154 }
155 }
156
157 function searchStudents() {
158 pageNum.value = 1
159 noMore.value = false
160 getStudentList()
161 }
162
163 function clearSearch() {
164 searchName.value = ''
165 searchStudents()
166 }
167
168 function toggleSelect(item) {
169 item.checked = !item.checked
170 if (item.checked) {
171 selectedList.value.push({
172 perId: item.perId,
173 name: item.name,
174 photo: item.photo
175 })
176 } else {
177 const index = selectedList.value.findIndex(s => s.perId === item.perId)
178 if (index > -1) {
179 selectedList.value.splice(index, 1)
180 }
181 }
182 }
183
184 function removeSelected(index) {
185 const item = selectedList.value[index]
186 const student = studentList.value.find(s => s.perId === item.perId)
187 if (student) {
188 student.checked = false
189 }
190 selectedList.value.splice(index, 1)
191 }
192
193 function confirmSelect() {
194 if (selectedList.value.length === 0) {
195 uni.showToast({ title: '请选择考生', icon: 'none' })
196 return
197 }
198
199 uni.showLoading({ title: '添加中...' })
200 api.batchChoose({
201 examId: examId,
202 perIds: selectedList.value.map(s => s.perId)
203 }).then(() => {
204 uni.hideLoading()
205 uni.showToast({ title: '添加成功', icon: 'success' })
206 setTimeout(() => {
207 uni.navigateBack()
208 }, 1500)
209 }).catch(() => {
210 uni.hideLoading()
211 })
212 }
213
214 function goBack() {
215 uni.navigateBack()
216 }
217
218 function formatDate(dateStr) {
219 if (!dateStr) return '-'
220 return dateStr.substring(0, 10)
221 }
222
223 function formatLevel(level) {
224 if (!level || level == 0) return '十级'
225 const hzArr = ['〇', '一', '二', '三', '四', '五', '六', '七', '八', '九', '十']
226 return hzArr[parseInt(level)] + '级'
227 }
228 </script>
229
230 <style lang="scss" scoped>
231 .choose-student-page {
232 min-height: 100vh;
233 background: #f5f5f5;
234 display: flex;
235 flex-direction: column;
236 }
237
238 /* 搜索头部 */
239 .search-header {
240 display: flex;
241 align-items: center;
242 padding: 20rpx 30rpx;
243 background: #fff;
244 border-bottom: 1rpx solid #f0f0f0;
245
246 .search-box {
247 flex: 1;
248 display: flex;
249 align-items: center;
250 padding: 0 20rpx;
251 height: 64rpx;
252 background: #f5f5f5;
253 border-radius: 32rpx;
254
255 .search-input {
256 flex: 1;
257 height: 100%;
258 font-size: 26rpx;
259 margin-left: 10rpx;
260 }
261 }
262
263 .cancel-btn {
264 margin-left: 20rpx;
265 font-size: 28rpx;
266 color: #666;
267 }
268 }
269
270 /* 已选区域 */
271 .selected-section {
272 background: #fff;
273 padding: 20rpx 30rpx;
274 border-bottom: 1rpx solid #f0f0f0;
275
276 .section-header {
277 display: flex;
278 align-items: center;
279 margin-bottom: 16rpx;
280
281 .section-title {
282 font-size: 26rpx;
283 font-weight: 600;
284 color: #333;
285 }
286
287 .section-count {
288 margin-left: 12rpx;
289 font-size: 24rpx;
290 color: #AD181F;
291 }
292 }
293
294 .selected-scroll {
295 white-space: nowrap;
296
297 .selected-item {
298 display: inline-flex;
299 align-items: center;
300 position: relative;
301 margin-right: 24rpx;
302
303 .avatar-small {
304 width: 64rpx;
305 height: 64rpx;
306 border-radius: 50%;
307 background: linear-gradient(135deg, #AD181F 0%, #c42a2a 100%);
308 overflow: hidden;
309 display: flex;
310 align-items: center;
311 justify-content: center;
312
313 text {
314 font-size: 24rpx;
315 color: #fff;
316 }
317
318 image {
319 width: 100%;
320 height: 100%;
321 }
322 }
323
324 .name-small {
325 display: block;
326 text-align: center;
327 font-size: 22rpx;
328 color: #666;
329 margin-top: 8rpx;
330 width: 80rpx;
331 overflow: hidden;
332 text-overflow: ellipsis;
333 white-space: nowrap;
334 }
335
336 .remove-btn {
337 position: absolute;
338 top: -6rpx;
339 right: -6rpx;
340 width: 28rpx;
341 height: 28rpx;
342 background: #999;
343 border-radius: 50%;
344 display: flex;
345 align-items: center;
346 justify-content: center;
347 }
348 }
349 }
350 }
351
352 /* 考生列表 */
353 .student-scroll {
354 flex: 1;
355 height: calc(100vh - 280rpx);
356 }
357
358 .student-list {
359 padding: 20rpx 30rpx;
360
361 .student-card {
362 display: flex;
363 align-items: center;
364 padding: 24rpx;
365 background: #fff;
366 border-radius: 16rpx;
367 margin-bottom: 16rpx;
368 box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
369
370 .checkbox {
371 margin-right: 20rpx;
372
373 .checkbox-empty {
374 width: 40rpx;
375 height: 40rpx;
376 border: 2rpx solid #ddd;
377 border-radius: 50%;
378 }
379
380 image {
381 width: 40rpx;
382 height: 40rpx;
383 }
384 }
385
386 .avatar {
387 width: 80rpx;
388 height: 80rpx;
389 border-radius: 50%;
390 background: linear-gradient(135deg, #AD181F 0%, #c42a2a 100%);
391 overflow: hidden;
392 display: flex;
393 align-items: center;
394 justify-content: center;
395 margin-right: 20rpx;
396
397 .avatar-text {
398 font-size: 32rpx;
399 color: #fff;
400 font-weight: 600;
401 }
402
403 image {
404 width: 100%;
405 height: 100%;
406 }
407 }
408
409 .student-info {
410 flex: 1;
411
412 .student-name {
413 font-size: 28rpx;
414 font-weight: 600;
415 color: #333;
416 margin-bottom: 8rpx;
417
418 .per-code {
419 font-size: 22rpx;
420 color: #999;
421 font-weight: normal;
422 margin-left: 12rpx;
423 }
424 }
425
426 .student-detail {
427 display: flex;
428 gap: 20rpx;
429
430 .level-info {
431 font-size: 24rpx;
432 color: #1561CB;
433 }
434
435 .validity {
436 font-size: 24rpx;
437 color: #666;
438 }
439 }
440 }
441 }
442
443 .loading-more,
444 .no-more {
445 text-align: center;
446 padding: 30rpx;
447 font-size: 24rpx;
448 color: #999;
449 }
450
451 }
452
453 /* 底部固定栏 */
454 .fixedBottom {
455 display: flex;
456 align-items: center;
457 justify-content: space-between;
458 padding: 20rpx 30rpx;
459 background: #fff;
460 box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.05);
461
462 .bottom-info {
463 .info-text {
464 font-size: 26rpx;
465 color: #666;
466
467 .count {
468 color: #AD181F;
469 font-weight: 600;
470 }
471 }
472 }
473
474 .btn-confirm {
475 padding: 0 50rpx;
476 height: 72rpx;
477 line-height: 72rpx;
478 background: linear-gradient(135deg, #AD181F 0%, #c42a2a 100%);
479 border-radius: 36rpx;
480 font-size: 28rpx;
481 color: #fff;
482 box-shadow: 0 4rpx 16rpx rgba(173, 24, 31, 0.3);
483 }
484 }
485 .empty {
486 display: flex;
487 flex-direction: column;
488 justify-content: center;
489 align-items: center;
490 padding: 120rpx 0;
491
492 .empty-img {
493 width: 300rpx;
494 height: 300rpx;
495 opacity: 0.08;
496 }
497
498 .empty-text {
499 color: #999;
500 font-size: 28rpx;
501 margin-top: 20rpx;
502 }
503 }
504 </style>
...@@ -680,7 +680,16 @@ ...@@ -680,7 +680,16 @@
680 "enablePullDownRefresh": false 680 "enablePullDownRefresh": false
681 } 681 }
682 682
683 }, { 683 },
684 {
685 "path": "chooseStudent",
686 "style": {
687 "navigationBarTitleText": "添加考生",
688 "enablePullDownRefresh": false
689 }
690
691 },
692 {
684 "path": "addApply", 693 "path": "addApply",
685 "style": { 694 "style": {
686 "navigationBarTitleText": "编辑级位考试", 695 "navigationBarTitleText": "编辑级位考试",
......
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!