Browse Source

✨ feat: 更改抢课备注为非必填

Pchen. 1 month ago
parent
commit
149848762c
3 changed files with 103 additions and 48 deletions
  1. 13 3
      src/pages/admin/qkTask/index.vue
  2. 86 42
      src/pages/qk/taskList.vue
  3. 4 3
      src/utils/qkTaskRules.js

+ 13 - 3
src/pages/admin/qkTask/index.vue

@@ -84,7 +84,6 @@
               </a-space>
               </a-space>
             </template>
             </template>
           </a-table-column>
           </a-table-column>
-          <a-table-column title="备注" data-index="name" :width="140" ellipsis tooltip />
           <a-table-column title="学号" data-index="student_num" :width="130" />
           <a-table-column title="学号" data-index="student_num" :width="130" />
           <a-table-column title="教务密码" data-index="password" :width="140" ellipsis tooltip />
           <a-table-column title="教务密码" data-index="password" :width="140" ellipsis tooltip />
           <a-table-column title="客户端" data-index="assigned_client_id" :width="180" ellipsis tooltip />
           <a-table-column title="客户端" data-index="assigned_client_id" :width="180" ellipsis tooltip />
@@ -97,6 +96,9 @@
             <template #cell="{ record }">{{ [...(record.courses || []), ...(record.course_groups || [])].join('、') || '-' }}</template>
             <template #cell="{ record }">{{ [...(record.courses || []), ...(record.course_groups || [])].join('、') || '-' }}</template>
           </a-table-column>
           </a-table-column>
           <a-table-column title="错误" data-index="error_msg" :width="220" ellipsis tooltip />
           <a-table-column title="错误" data-index="error_msg" :width="220" ellipsis tooltip />
+          <a-table-column title="备注" :width="140" ellipsis tooltip>
+            <template #cell="{ record }">{{ record.name || '-' }}</template>
+          </a-table-column>
           <a-table-column title="创建时间" :width="180">
           <a-table-column title="创建时间" :width="180">
             <template #cell="{ record }">{{ formatTime(record.create_time) }}</template>
             <template #cell="{ record }">{{ formatTime(record.create_time) }}</template>
           </a-table-column>
           </a-table-column>
@@ -136,8 +138,16 @@
         该任务正在客户端执行,保存后将收回并变为未开始状态。
         该任务正在客户端执行,保存后将收回并变为未开始状态。
       </a-alert>
       </a-alert>
       <a-form :model="form" layout="vertical" class="task-form">
       <a-form :model="form" layout="vertical" class="task-form">
-        <a-form-item label="备注名" required>
-          <a-input v-model="form.name" placeholder="例如:张三选修课" allow-clear />
+        <a-form-item label="备注">
+          <a-textarea
+            v-model="form.name"
+            no-trim
+            placeholder="添加对任务的备注(非必填)"
+            :max-length="{ length: 50 }"
+            allow-clear
+            show-word-limit
+            :auto-size="{ minRows: 2, maxRows: 4 }"
+          />
         </a-form-item>
         </a-form-item>
         <a-form-item label="选课批次" required>
         <a-form-item label="选课批次" required>
           <a-select v-model="form.batch_id" placeholder="请选择抢课批次" allow-clear allow-search>
           <a-select v-model="form.batch_id" placeholder="请选择抢课批次" allow-clear allow-search>

+ 86 - 42
src/pages/qk/taskList.vue

@@ -36,7 +36,6 @@
       >
       >
         <template #columns>
         <template #columns>
           <a-table-column title="ID" data-index="id" :width="80" />
           <a-table-column title="ID" data-index="id" :width="80" />
-          <a-table-column title="备注" data-index="name" :width="140" ellipsis tooltip />
           <a-table-column title="学号" data-index="student_num" :width="130" />
           <a-table-column title="学号" data-index="student_num" :width="130" />
           <a-table-column title="选课批次" :width="220" ellipsis tooltip>
           <a-table-column title="选课批次" :width="220" ellipsis tooltip>
             <template #cell="{ record }">
             <template #cell="{ record }">
@@ -59,10 +58,13 @@
           <a-table-column title="结果" :width="220" ellipsis tooltip>
           <a-table-column title="结果" :width="220" ellipsis tooltip>
             <template #cell="{ record }">{{ resultText(record) }}</template>
             <template #cell="{ record }">{{ resultText(record) }}</template>
           </a-table-column>
           </a-table-column>
+          <a-table-column title="备注" :width="140" ellipsis tooltip>
+            <template #cell="{ record }">{{ record.name || '-' }}</template>
+          </a-table-column>
           <a-table-column title="更新时间" :width="180">
           <a-table-column title="更新时间" :width="180">
             <template #cell="{ record }">{{ formatTime(record.update_time) }}</template>
             <template #cell="{ record }">{{ formatTime(record.update_time) }}</template>
           </a-table-column>
           </a-table-column>
-          <a-table-column title="操作" :width="220" :fixed="tableFixed('right')">
+          <a-table-column title="操作" :width="280" :fixed="tableFixed('right')">
             <template #cell="{ record }">
             <template #cell="{ record }">
               <a-space wrap>
               <a-space wrap>
                 <a-button
                 <a-button
@@ -84,6 +86,7 @@
                   暂停
                   暂停
                 </a-button>
                 </a-button>
                 <a-button v-if="canSubmit" type="text" size="small" :disabled="!canEdit(record)" @click="openModal(record)">编辑</a-button>
                 <a-button v-if="canSubmit" type="text" size="small" :disabled="!canEdit(record)" @click="openModal(record)">编辑</a-button>
+                <a-button v-if="canSubmit" type="text" size="small" @click="copyTask(record)">复制</a-button>
                 <a-popconfirm v-if="canSubmit" content="确认取消该抢课任务?" @ok="cancelTask(record)">
                 <a-popconfirm v-if="canSubmit" content="确认取消该抢课任务?" @ok="cancelTask(record)">
                   <a-button type="text" status="danger" size="small" :disabled="!canCancel(record)">取消</a-button>
                   <a-button type="text" status="danger" size="small" :disabled="!canCancel(record)">取消</a-button>
                 </a-popconfirm>
                 </a-popconfirm>
@@ -101,16 +104,25 @@
 
 
     <a-modal
     <a-modal
       v-model:visible="visible"
       v-model:visible="visible"
-      :title="form.id ? '编辑抢课任务' : '提交抢课任务'"
+      :title="modalTitle"
       :width="isMobile ? '100%' : '720px'"
       :width="isMobile ? '100%' : '720px'"
       draggable
       draggable
       esc-to-close
       esc-to-close
       :ok-loading="saving"
       :ok-loading="saving"
       :on-before-ok="submitTask"
       :on-before-ok="submitTask"
     >
     >
+      <a-spin :loading="detailLoading" style="width: 100%">
       <a-form :model="form" layout="vertical" class="task-form">
       <a-form :model="form" layout="vertical" class="task-form">
-        <a-form-item label="备注名" required>
-          <a-input v-model="form.name" placeholder="例如:张三选修课" allow-clear />
+        <a-form-item label="备注">
+          <a-textarea
+            v-model="form.name"
+            no-trim
+            placeholder="添加对任务的备注(非必填)"
+            :max-length="{ length: 50 }"
+            allow-clear
+            show-word-limit
+            :auto-size="{ minRows: 2, maxRows: 4 }"
+          />
         </a-form-item>
         </a-form-item>
         <a-form-item label="选课批次" required>
         <a-form-item label="选课批次" required>
           <a-select v-model="form.batch_id" placeholder="请选择抢课批次" allow-clear allow-search>
           <a-select v-model="form.batch_id" placeholder="请选择抢课批次" allow-clear allow-search>
@@ -127,7 +139,7 @@
           </a-col>
           </a-col>
           <a-col :xs="24" :sm="12">
           <a-col :xs="24" :sm="12">
             <a-form-item label="教务密码" :required="!form.id">
             <a-form-item label="教务密码" :required="!form.id">
-              <a-input-password v-model="form.password" :placeholder="form.id ? '不填则不修改' : '请输入教务密码'" />
+              <a-input-password v-model="form.password" :placeholder="passwordPlaceholder" />
             </a-form-item>
             </a-form-item>
           </a-col>
           </a-col>
         </a-row>
         </a-row>
@@ -152,14 +164,15 @@
           </a-col>
           </a-col>
         </a-row>
         </a-row>
       </a-form>
       </a-form>
+      </a-spin>
     </a-modal>
     </a-modal>
   </div>
   </div>
 </template>
 </template>
 
 
 <script setup>
 <script setup>
 import { reactive, ref, onMounted, onUnmounted, computed } from 'vue'
 import { reactive, ref, onMounted, onUnmounted, computed } from 'vue'
-import { Notification, Modal } from '@arco-design/web-vue'
-import { getQkTask, saveQkTask, deleteQkTask, startQkTask, pauseQkTask, getQkBatchList } from '@/api/qk'
+import { Notification } from '@arco-design/web-vue'
+import { getQkTask, getQkTaskDetail, saveQkTask, deleteQkTask, startQkTask, pauseQkTask, getQkBatchList } from '@/api/qk'
 import { useResponsiveTable } from '@/hooks/useResponsiveTable'
 import { useResponsiveTable } from '@/hooks/useResponsiveTable'
 import { hasAnyPermission, QK_ASSISTANT_CONTROL_CODES, QK_ASSISTANT_SUBMIT_CODES } from '@/utils/permission'
 import { hasAnyPermission, QK_ASSISTANT_CONTROL_CODES, QK_ASSISTANT_SUBMIT_CODES } from '@/utils/permission'
 import {
 import {
@@ -175,7 +188,9 @@ const canControl = computed(() => hasAnyPermission(QK_ASSISTANT_CONTROL_CODES))
 
 
 const loading = ref(false)
 const loading = ref(false)
 const saving = ref(false)
 const saving = ref(false)
+const detailLoading = ref(false)
 const visible = ref(false)
 const visible = ref(false)
+const isCopy = ref(false)
 const data = ref([])
 const data = ref([])
 const batchOptions = ref([])
 const batchOptions = ref([])
 const form = reactive({
 const form = reactive({
@@ -195,6 +210,16 @@ const targetCount = computed(() => {
 })
 })
 const minInterval = computed(() => getMinQkInterval(targetCount.value))
 const minInterval = computed(() => getMinQkInterval(targetCount.value))
 const targetHint = computed(() => getQkTargetHint(form.coursesText, form.courseGroupsText))
 const targetHint = computed(() => getQkTargetHint(form.coursesText, form.courseGroupsText))
+const modalTitle = computed(() => {
+  if (form.id) return '编辑抢课任务'
+  if (isCopy.value) return '复制抢课任务'
+  return '提交抢课任务'
+})
+const passwordPlaceholder = computed(() => {
+  if (form.id) return '已自动填充,修改后保存将更新密码'
+  if (isCopy.value) return '已自动填充'
+  return '请输入教务密码'
+})
 
 
 const splitLines = splitQkLines
 const splitLines = splitQkLines
 
 
@@ -236,6 +261,7 @@ const canCancel = (record) => ['paused', 'pending', 'failed'].includes(record.st
 const resultText = (record) => record.error_msg || record.result_json?.message || record.result_json?.course || '-'
 const resultText = (record) => record.error_msg || record.result_json?.message || record.result_json?.course || '-'
 
 
 const resetForm = () => {
 const resetForm = () => {
+  isCopy.value = false
   Object.assign(form, {
   Object.assign(form, {
     id: '',
     id: '',
     name: '',
     name: '',
@@ -249,38 +275,63 @@ const resetForm = () => {
   })
   })
 }
 }
 
 
-const openModal = (record) => {
-  resetForm()
-  if (record) {
-    Object.assign(form, {
-      id: record.id,
-      name: record.name,
-      batch_id: record.batch_id || '',
-      student_num: record.student_num,
-      coursesText: (record.courses || []).join('\n'),
-      courseGroupsText: (record.course_groups || []).join('\n'),
-      enable_ggxxk: record.enable_ggxxk,
-      interval_ms: record.interval_ms
-    })
+const fillFormFromTask = (task, { copy = false } = {}) => {
+  Object.assign(form, {
+    id: copy ? '' : task.id,
+    name: task.name,
+    batch_id: task.batch_id || '',
+    student_num: task.student_num,
+    password: task.password || '',
+    coursesText: (task.courses || []).join('\n'),
+    courseGroupsText: (task.course_groups || []).join('\n'),
+    enable_ggxxk: task.enable_ggxxk,
+    interval_ms: task.interval_ms
+  })
+}
+
+const loadTaskDetail = async (id) => {
+  const res = await getQkTaskDetail({ id })
+  if (!res || res.code !== 0) {
+    throw new Error(res?.msg || '获取任务详情失败')
   }
   }
-  visible.value = true
+  const task = res.data?.task
+  if (!task) {
+    throw new Error('任务不存在')
+  }
+  return task
 }
 }
 
 
-const buildCopySnapshot = (validated) => ({
-  name: validated.name,
-  batch_id: validated.batch_id,
-  student_num: validated.student_num,
-  password: validated.password || '',
-  coursesText: validated.courses.join('\n'),
-  courseGroupsText: validated.courseGroups.join('\n'),
-  enable_ggxxk: form.enable_ggxxk,
-  interval_ms: validated.intervalMs
-})
+const openModal = async (record) => {
+  resetForm()
+  visible.value = true
+  if (!record) return
+  try {
+    detailLoading.value = true
+    const task = await loadTaskDetail(record.id)
+    fillFormFromTask(task)
+  } catch (error) {
+    visible.value = false
+    Notification.error({ title: '获取任务详情失败', content: error.message || '请稍后再试' })
+  } finally {
+    detailLoading.value = false
+  }
+}
 
 
-const openModalFromSnapshot = (snapshot) => {
+const copyTask = async (record) => {
   resetForm()
   resetForm()
-  Object.assign(form, snapshot)
+  isCopy.value = true
   visible.value = true
   visible.value = true
+  try {
+    detailLoading.value = true
+    const task = await loadTaskDetail(record.id)
+    fillFormFromTask(task, { copy: true })
+  } catch (error) {
+    visible.value = false
+    isCopy.value = false
+    Notification.error({ title: '复制失败', content: error.message || '请稍后再试' })
+  } finally {
+    detailLoading.value = false
+  }
 }
 }
 
 
 const loadBatches = async () => {
 const loadBatches = async () => {
@@ -338,15 +389,8 @@ const submitTask = async () => {
       Notification.error({ title: '保存抢课任务失败', content: res?.msg || '请稍后再试' })
       Notification.error({ title: '保存抢课任务失败', content: res?.msg || '请稍后再试' })
       return false
       return false
     }
     }
-    const snapshot = buildCopySnapshot(validated)
+    Notification.success({ title: '保存成功' })
     await getList()
     await getList()
-    Modal.confirm({
-      title: '保存成功',
-      content: '是否复制此任务并继续提交新任务?',
-      okText: '复制任务',
-      cancelText: '关闭',
-      onOk: () => openModalFromSnapshot(snapshot)
-    })
     return true
     return true
   } catch (error) {
   } catch (error) {
     Notification.error({ title: '保存抢课任务失败', content: error.message || '请稍后再试' })
     Notification.error({ title: '保存抢课任务失败', content: error.message || '请稍后再试' })

+ 4 - 3
src/utils/qkTaskRules.js

@@ -1,4 +1,5 @@
 export const MAX_QK_TARGETS = 10
 export const MAX_QK_TARGETS = 10
+export const MAX_QK_REMARK_LENGTH = 50
 
 
 export function splitQkLines(value) {
 export function splitQkLines(value) {
   return String(value || '').split(/[\n,,]/).map(item => item.trim()).filter(Boolean)
   return String(value || '').split(/[\n,,]/).map(item => item.trim()).filter(Boolean)
@@ -40,9 +41,9 @@ export function validateQkTaskTargets(coursesText, courseGroupsText, intervalMs)
 }
 }
 
 
 export function validateQkTaskForm(form) {
 export function validateQkTaskForm(form) {
-  const name = String(form?.name || '').trim()
-  if (!name) {
-    return { ok: false, message: '请填写备注名' }
+  const name = form?.name === undefined || form?.name === null ? '' : String(form.name)
+  if (name.length > MAX_QK_REMARK_LENGTH) {
+    return { ok: false, message: `备注不能超过 ${MAX_QK_REMARK_LENGTH} 个字符` }
   }
   }
   if (!form?.batch_id) {
   if (!form?.batch_id) {
     return { ok: false, message: '请选择选课批次' }
     return { ok: false, message: '请选择选课批次' }