Procházet zdrojové kódy

优化短信任务视图,增加URL编码解码功能,改进文件上传处理,添加操作节流控制,提升用户体验。

wuyi před 4 dny
rodič
revize
87fdf2a29e
1 změnil soubory, kde provedl 106 přidání a 21 odebrání
  1. 106 21
      src/views/SmsTaskView.vue

+ 106 - 21
src/views/SmsTaskView.vue

@@ -23,7 +23,6 @@ import InputIcon from 'primevue/inputicon'
 import InputText from 'primevue/inputtext'
 import Message from 'primevue/message'
 import Textarea from 'primevue/textarea'
-import FileUpload from 'primevue/fileupload'
 import Tag from 'primevue/tag'
 import ProgressBar from 'primevue/progressbar'
 import { useToast } from 'primevue/usetoast'
@@ -67,6 +66,15 @@ const fetchData = async () => {
         }
 
         const response = await listSmsTasks(params)
+
+        // 对返回的数据进行 URL 解码
+        if (response.content && Array.isArray(response.content)) {
+            response.content = response.content.map(task => ({
+                ...task,
+                message: task.message ? decodeURIComponent(task.message) : task.message
+            }))
+        }
+
         tableData.value = response
     } catch (error) {
         toast.add({
@@ -96,7 +104,7 @@ const getStatusSeverity = (status) => {
     const severityMap = {
         idle: 'secondary',
         pending: 'info',
-        running: 'primary',
+        running: 'info',
         cutting: 'info',
         paused: 'warn',
         queued: 'info',
@@ -141,7 +149,7 @@ const taskForm = ref({
     file: null
 })
 const taskFormLoading = ref(false)
-const fileUploadRef = ref()
+const fileInputRef = ref()
 
 const taskFormResolver = computed(() => {
     const schema = {
@@ -183,8 +191,8 @@ const openEditTaskDialog = (task) => {
 }
 
 // 文件选择处理
-const onFileSelect = (event) => {
-    const files = event.files
+const handleFileChange = (event) => {
+    const files = event.target.files
     if (files && files.length > 0) {
         taskForm.value.file = files[0]
     }
@@ -193,8 +201,8 @@ const onFileSelect = (event) => {
 // 清除文件
 const clearFile = () => {
     taskForm.value.file = null
-    if (fileUploadRef.value) {
-        fileUploadRef.value.clear()
+    if (fileInputRef.value) {
+        fileInputRef.value.value = ''
     }
 }
 
@@ -203,12 +211,15 @@ const saveTask = async ({ valid }) => {
 
     taskFormLoading.value = true
     try {
+        // 对短信内容进行 URL 编码
+        const encodedMessage = encodeURIComponent(taskForm.value.message)
+
         if (isEditMode.value) {
             // 更新任务
             const updateData = {
                 id: taskForm.value.id,
                 name: taskForm.value.name,
-                message: taskForm.value.message
+                message: encodedMessage
             }
             if (taskForm.value.remark) {
                 updateData.remark = taskForm.value.remark
@@ -225,7 +236,7 @@ const saveTask = async ({ valid }) => {
             // 创建任务
             const formData = new FormData()
             formData.append('name', taskForm.value.name)
-            formData.append('message', taskForm.value.message)
+            formData.append('message', encodedMessage)
             if (taskForm.value.remark) {
                 formData.append('remark', taskForm.value.remark)
             }
@@ -262,8 +273,19 @@ const closeTaskDialog = () => {
 }
 
 // ========== 任务操作 ==========
+// 操作中的任务ID集合
+const operatingTaskIds = ref(new Set())
+
+// 检查任务是否正在操作中
+const isTaskOperating = (taskId) => {
+    return operatingTaskIds.value.has(taskId)
+}
+
 // 开始任务
 const handleStartTask = async (task) => {
+    if (isTaskOperating(task.id)) return
+    
+    operatingTaskIds.value.add(task.id)
     try {
         await startSmsTask(task.id)
         toast.add({
@@ -272,8 +294,12 @@ const handleStartTask = async (task) => {
             detail: '任务已开始',
             life: 3000
         })
-        fetchData()
+        setTimeout(() => {
+            fetchData()
+            operatingTaskIds.value.delete(task.id)
+        }, 2000)
     } catch (error) {
+        operatingTaskIds.value.delete(task.id)
         toast.add({
             severity: 'error',
             summary: '错误',
@@ -285,6 +311,9 @@ const handleStartTask = async (task) => {
 
 // 暂停任务
 const handlePauseTask = async (task) => {
+    if (isTaskOperating(task.id)) return
+    
+    operatingTaskIds.value.add(task.id)
     try {
         await pauseSmsTask(task.id)
         toast.add({
@@ -293,8 +322,12 @@ const handlePauseTask = async (task) => {
             detail: '任务已暂停',
             life: 3000
         })
-        fetchData()
+        setTimeout(() => {
+            fetchData()
+            operatingTaskIds.value.delete(task.id)
+        }, 2000)
     } catch (error) {
+        operatingTaskIds.value.delete(task.id)
         toast.add({
             severity: 'error',
             summary: '错误',
@@ -397,14 +430,42 @@ const handleTaskItemPageChange = (event) => {
     fetchTaskItems()
 }
 
+// 节流控制:记录最后一次点击时间
+const lastClickTime = ref({
+    search: 0,
+    reset: 0,
+    refresh: 0
+})
+
+// 节流检查函数
+const checkThrottle = (actionType) => {
+    const now = Date.now()
+    const lastTime = lastClickTime.value[actionType]
+    
+    if (now - lastTime < 1000) {
+        toast.add({
+            severity: 'warn',
+            summary: '提示',
+            detail: '操作过快,请稍后再试',
+            life: 2000
+        })
+        return false
+    }
+    
+    lastClickTime.value[actionType] = now
+    return true
+}
+
 // 应用筛选
 const applyFilters = () => {
+    if (!checkThrottle('search')) return
     tableData.value.metadata.page = 0
     fetchData()
 }
 
 // 重置筛选
 const resetFilters = () => {
+    if (!checkThrottle('reset')) return
     filters.value = {
         status: null
     }
@@ -412,6 +473,12 @@ const resetFilters = () => {
     fetchData()
 }
 
+// 手动刷新(带节流)
+const handleRefresh = () => {
+    if (!checkThrottle('refresh')) return
+    fetchData()
+}
+
 onMounted(() => {
     fetchData()
 })
@@ -437,7 +504,7 @@ onMounted(() => {
                     <Button icon="pi pi-search" label="搜索" @click="applyFilters" size="small" />
 
                     <span class="w-px h-6 bg-[var(--p-content-border-color)] mx-1"></span>
-                    <Button icon="pi pi-refresh" @click="fetchData" label="刷新" size="small" />
+                    <Button icon="pi pi-refresh" @click="handleRefresh" label="刷新" size="small" />
                 </div>
 
                 <!-- 右侧按钮:新建任务 -->
@@ -505,9 +572,14 @@ onMounted(() => {
                     <div class="flex gap-1 justify-center">
                         <Button v-if="canStartTask(slotProps.data)" icon="pi pi-play" severity="success" size="small"
                             text rounded aria-label="开始" v-tooltip.top="'开始'"
+                            :disabled="isTaskOperating(slotProps.data.id)"
+                            :loading="isTaskOperating(slotProps.data.id)"
                             @click="handleStartTask(slotProps.data)" />
                         <Button v-if="canPauseTask(slotProps.data)" icon="pi pi-pause" severity="warn" size="small" text
-                            rounded aria-label="暂停" v-tooltip.top="'暂停'" @click="handlePauseTask(slotProps.data)" />
+                            rounded aria-label="暂停" v-tooltip.top="'暂停'"
+                            :disabled="isTaskOperating(slotProps.data.id)"
+                            :loading="isTaskOperating(slotProps.data.id)"
+                            @click="handlePauseTask(slotProps.data)" />
                         <Button icon="pi pi-eye" severity="info" size="small" text rounded aria-label="详情"
                             v-tooltip.top="'详情'" @click="openTaskDetailDialog(slotProps.data)" v-if="isAdmin" />
                         <Button icon="pi pi-pencil" severity="info" size="small" text rounded aria-label="编辑"
@@ -552,14 +624,27 @@ onMounted(() => {
                 </div>
 
                 <div class="field mt-4" v-if="!isEditMode">
-                    <label class="block mb-2 text-sm">手机号文件 * (.txt格式,每行一个手机号)</label>
-                    <FileUpload ref="fileUploadRef" name="file" accept=".txt" :maxFileSize="10000000"
-                        @select="onFileSelect" :auto="false" :showUploadButton="false" :showCancelButton="false"
-                        chooseLabel="选择文件">
-                        <template #empty>
-                            <p>拖拽文件到这里上传</p>
-                        </template>
-                    </FileUpload>
+                    <div>
+                        <input ref="fileInputRef" type="file" accept=".txt" @change="handleFileChange"
+                            style="display: none;" />
+                        <Button label="选择文件" icon="pi pi-upload" @click="fileInputRef?.click()" size="small"
+                            severity="info" />
+                        <div v-if="taskForm.file" class="mt-2 text-sm text-[var(--p-text-color)]">
+                            <i class="pi pi-file mr-1"></i>{{ taskForm.file.name }}
+                        </div>
+                        <div class="mt-2 text-xs text-[var(--p-text-muted-color)]">
+                            目标号码文件,需要 .txt 格式
+                        </div>
+                        <div class="mt-2 text-xs text-[var(--p-text-muted-color)]">
+                            内容格式示例:区号+手机号,每行一个,如下:
+                            <br>
+                            138001381001
+                            <br>
+                            138001381002
+                            <br>
+                            138001381003
+                        </div>
+                    </div>
                     <Message v-if="$form.file?.invalid" severity="error" size="small" variant="simple" class="mt-2">
                         {{ $form.file.error?.message }}
                     </Message>