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