From c52ac07b45015b16aa0bf8b959f559cec6f5a38f Mon Sep 17 00:00:00 2001
From: yeg <1347747024@qq.com>
Date: 星期三, 09 九月 2026 01:34:31 +0800
Subject: [PATCH] 接入待办真实接口与负责人权限

---
 src/views/TodoList.vue |   16 +++++---
 src/stores/todos.ts    |   93 ++++++----------------------------------------
 src/api/todos.ts       |    6 +++
 3 files changed, 28 insertions(+), 87 deletions(-)

diff --git a/src/api/todos.ts b/src/api/todos.ts
new file mode 100644
index 0000000..5ffe960
--- /dev/null
+++ b/src/api/todos.ts
@@ -0,0 +1,6 @@
+import { request, type ApiResponse } from './request'
+export interface TodoDto { id:number; content:string; ownerId:number; owner:string; status:'待处理'|'已处理'; creator:string; createdAt:number; operatedAt:number; handleRemark:string }
+export function listTodos(){ return request<ApiResponse & {rows:TodoDto[]}>('/system/todo/list').then(r=>r.rows || []) }
+export function createTodo(body:{content:string;ownerId:number;ownerName:string}){ return request<ApiResponse & {data:TodoDto}>('/system/todo',{method:'POST',body:JSON.stringify(body)}).then(r=>r.data) }
+export function handleTodo(id:number,remark:string){ return request<ApiResponse>(`/system/todo/${id}/handle`,{method:'PUT',body:JSON.stringify({remark})}) }
+export function removeTodo(id:number){ return request<ApiResponse>(`/system/todo/${id}`,{method:'DELETE'}) }
diff --git a/src/stores/todos.ts b/src/stores/todos.ts
index 32de583..f8edc9a 100644
--- a/src/stores/todos.ts
+++ b/src/stores/todos.ts
@@ -1,85 +1,16 @@
 import { ref } from 'vue'
-
+import { listTodos, createTodo as createTodoApi, handleTodo as handleTodoApi, removeTodo as removeTodoApi } from '@/api/todos'
+import { formatBusinessTime } from '@/utils/businessTime'
 export type TodoStatus = '待处理' | '已处理'
-
-export interface TodoRecord {
-  id: number
-  content: string
-  owner: string
-  status: TodoStatus
-  creator: string
-  createdAt: string
-  operatedAt: string
-  handleRemark: string
-}
-
-export interface TodoPayload {
-  content: string
-  owner: string
-  creator: string
-}
-
-const STORAGE_KEY = 'battery_detection_todos_v1'
-
-const seedRecords: TodoRecord[] = [
-  { id: 7012, content: '复核高温循环测试的壳体温度异常', owner: '张工', status: '待处理', creator: 'Admin', createdAt: '2026-08-06 09:18', operatedAt: '2026-08-06 09:18', handleRemark: '' },
-  { id: 7011, content: '确认检测柜 A-03 通道校准结果', owner: '李工', status: '待处理', creator: 'Admin', createdAt: '2026-08-06 08:52', operatedAt: '2026-08-06 08:52', handleRemark: '' },
-  { id: 7010, content: '补充 SP2 化成报表测试方法说明', owner: '王工', status: '已处理', creator: '李工', createdAt: '2026-08-05 16:44', operatedAt: '2026-08-06 08:36', handleRemark: '测试方法已补充并提交复核。' },
-  { id: 7009, content: '复核低温充放电测试修订数据', owner: '周工', status: '待处理', creator: '王工', createdAt: '2026-08-05 15:20', operatedAt: '2026-08-05 15:20', handleRemark: '' },
-  { id: 7008, content: '处理采集器 COL-08 离线告警', owner: '陈工', status: '已处理', creator: 'Admin', createdAt: '2026-08-05 13:08', operatedAt: '2026-08-05 14:16', handleRemark: '重新连接通信线并恢复采集。' },
-  { id: 7007, content: '确认容量一致性测试的复测样品', owner: '赵工', status: '待处理', creator: '李工', createdAt: '2026-08-05 11:42', operatedAt: '2026-08-05 11:42', handleRemark: '' },
-  { id: 7006, content: '整理首充容量标定拼接报表', owner: '王工', status: '已处理', creator: 'Admin', createdAt: '2026-08-04 17:35', operatedAt: '2026-08-05 10:12', handleRemark: '拼接报表已保存。' },
-  { id: 7005, content: '检查 3C 倍率放电测试温升数据', owner: '张工', status: '待处理', creator: 'Admin', createdAt: '2026-08-04 15:26', operatedAt: '2026-08-04 15:26', handleRemark: '' },
-  { id: 7004, content: '更新高温 A 批电池组字段模板', owner: '李工', status: '已处理', creator: '周工', createdAt: '2026-08-04 11:10', operatedAt: '2026-08-04 14:28', handleRemark: '字段模板已更新。' },
-  { id: 7003, content: '复核过充安全边界测试结论', owner: '陈工', status: '已处理', creator: 'Admin', createdAt: '2026-08-03 16:48', operatedAt: '2026-08-04 09:05', handleRemark: '保护响应符合预期。' },
-  { id: 7002, content: '补录低温启动测试环境参数', owner: '赵工', status: '待处理', creator: '王工', createdAt: '2026-08-03 13:24', operatedAt: '2026-08-03 13:24', handleRemark: '' },
-  { id: 7001, content: '归档七月检测柜维护记录', owner: '周工', status: '已处理', creator: 'Admin', createdAt: '2026-08-01 09:30', operatedAt: '2026-08-02 16:40', handleRemark: '维护记录已归档。' },
-]
-
-function loadRecords() {
-  try {
-    const stored = localStorage.getItem(STORAGE_KEY)
-    if (stored) return JSON.parse(stored) as TodoRecord[]
-  } catch {
-    localStorage.removeItem(STORAGE_KEY)
-  }
-  return seedRecords
-}
-
-const records = ref<TodoRecord[]>(loadRecords())
-
-function persist() {
-  localStorage.setItem(STORAGE_KEY, JSON.stringify(records.value))
-}
-
-function nowString() {
-  const date = new Date()
-  const pad = (value: number) => String(value).padStart(2, '0')
-  return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}`
-}
-
+export interface TodoRecord { id:number; content:string; ownerId:number; owner:string; status:TodoStatus; creator:string; createdAt:string; operatedAt:string; handleRemark:string }
+export interface TodoPayload { content:string; ownerId:number; owner:string }
+const records = ref<TodoRecord[]>([])
+const loaded = ref(false)
+function map(item: any): TodoRecord { return {...item, createdAt: formatBusinessTime(item.createdAt), operatedAt: formatBusinessTime(item.operatedAt)} }
 export function useTodos() {
-  function createTodo(payload: TodoPayload) {
-    const id = Math.max(7000, ...records.value.map(item => item.id)) + 1
-    const timestamp = nowString()
-    records.value.unshift({ ...payload, id, status: '待处理', createdAt: timestamp, operatedAt: timestamp, handleRemark: '' })
-    persist()
-  }
-
-  function handleTodo(id: number, remark: string) {
-    const target = records.value.find(item => item.id === id)
-    if (!target || target.status === '已处理') return false
-    target.status = '已处理'
-    target.handleRemark = remark
-    target.operatedAt = nowString()
-    persist()
-    return true
-  }
-
-  function removeTodo(id: number) {
-    records.value = records.value.filter(item => item.id !== id)
-    persist()
-  }
-
-  return { records, createTodo, handleTodo, removeTodo }
+  async function load(){ records.value=(await listTodos()).map(map); loaded.value=true }
+  async function createTodo(payload:TodoPayload){ const item=await createTodoApi({content:payload.content,ownerId:payload.ownerId,ownerName:payload.owner}); records.value.unshift(map(item)) }
+  async function handleTodo(id:number,remark:string){ await handleTodoApi(id,remark); await load() }
+  async function removeTodo(id:number){ await removeTodoApi(id); records.value=records.value.filter(item=>item.id!==id) }
+  return {records,loaded,load,createTodo,handleTodo,removeTodo}
 }
diff --git a/src/views/TodoList.vue b/src/views/TodoList.vue
index 321a3bc..0e62c91 100644
--- a/src/views/TodoList.vue
+++ b/src/views/TodoList.vue
@@ -1,8 +1,9 @@
 <script setup lang="ts">
-import { computed, reactive, ref } from 'vue'
+import { computed, onMounted, reactive, ref } from 'vue'
 import { ElMessage, ElMessageBox } from 'element-plus'
 import { getCurrentUser } from '@/utils/auth'
 import { useTodos, type TodoRecord, type TodoStatus } from '@/stores/todos'
+import { getEnabledUserOptions } from '@/api/users'
 
 interface FilterState {
   owner: string
@@ -10,9 +11,9 @@
   dateRange: [string, string] | null
 }
 
-const owners = ['张工', '李工', '王工', '周工', '赵工', '陈工', 'Admin']
+const owners = ref<{ id:number; name:string }[]>([])
 const statuses: TodoStatus[] = ['待处理', '已处理']
-const { records, createTodo, handleTodo, removeTodo } = useTodos()
+const { records, createTodo, handleTodo, removeTodo, load } = useTodos()
 const currentOwner = computed(() => getCurrentUser()?.displayName || getCurrentUser()?.username || 'Admin')
 const filters = reactive<FilterState>({ owner: '', status: '', dateRange: null })
 const appliedFilters = ref<FilterState>({ owner: '', status: '', dateRange: null })
@@ -20,6 +21,7 @@
 const pageSize = ref(10)
 const createDialogVisible = ref(false)
 const createForm = reactive({ content: '', owner: '' })
+onMounted(async () => { await load(); owners.value = await getEnabledUserOptions() })
 
 const filteredRecords = computed(() => {
   const query = appliedFilters.value
@@ -57,7 +59,9 @@
   const content = createForm.content.trim()
   if (!content) { ElMessage.warning('请输入待办内容'); return }
   if (!createForm.owner) { ElMessage.warning('请选择负责人'); return }
-  createTodo({ content, owner: createForm.owner, creator: getCurrentUser()?.displayName || 'Admin' })
+  const owner = owners.value.find(item => item.name === createForm.owner)
+  if (!owner) { ElMessage.warning('负责人不存在'); return }
+  createTodo({ content, ownerId: owner.id, owner: owner.name })
   createDialogVisible.value = false
   currentPage.value = 1
   ElMessage.success('待办已新增')
@@ -97,7 +101,7 @@
     <section class="filter-panel" aria-label="待办查询条件">
       <header class="section-heading"><div><i></i><strong>查询条件</strong><span>TODO FILTER CONDITIONS</span></div><small>共 {{ records.length }} 条待办</small></header>
       <div class="filter-grid">
-        <label><span>负责人</span><el-select v-model="filters.owner" clearable placeholder="全部"><el-option label="全部" value="全部" /><el-option v-for="item in owners" :key="item" :label="item" :value="item" /></el-select></label>
+        <label><span>负责人</span><el-select v-model="filters.owner" clearable placeholder="全部"><el-option label="全部" value="全部" /><el-option v-for="item in owners" :key="item.id" :label="item.name" :value="item.name" /></el-select></label>
         <label><span>状态</span><el-select v-model="filters.status" clearable placeholder="全部"><el-option label="全部" value="全部" /><el-option v-for="item in statuses" :key="item" :label="item" :value="item" /></el-select></label>
         <label class="date-filter"><span>操作时间</span><el-date-picker v-model="filters.dateRange" type="daterange" value-format="YYYY-MM-DD" start-placeholder="开始日期" end-placeholder="结束日期" range-separator="至" unlink-panels /></label>
         <div class="filter-actions"><button class="primary-button" type="button" @click="applySearch"><el-icon><Search /></el-icon>查询</button><button class="secondary-button" type="button" @click="resetSearch"><el-icon><RefreshLeft /></el-icon>重置</button></div>
@@ -119,7 +123,7 @@
     </section>
 
     <el-dialog v-model="createDialogVisible" title="新增待办" width="600px" align-center destroy-on-close>
-      <div class="create-form"><label><span>待办内容</span><el-input v-model="createForm.content" type="textarea" :rows="4" maxlength="200" show-word-limit placeholder="请输入待办内容" /></label><label><span>负责人</span><el-select v-model="createForm.owner" filterable placeholder="请选择负责人"><el-option v-for="item in owners" :key="item" :label="item" :value="item" /></el-select></label></div>
+      <div class="create-form"><label><span>待办内容</span><el-input v-model="createForm.content" type="textarea" :rows="4" maxlength="200" show-word-limit placeholder="请输入待办内容" /></label><label><span>负责人</span><el-select v-model="createForm.owner" filterable placeholder="请选择负责人"><el-option v-for="item in owners" :key="item.id" :label="item.name" :value="item.name" /></el-select></label></div>
       <template #footer><button class="secondary-button" type="button" @click="createDialogVisible = false">取消</button><button class="primary-button" type="button" @click="submitCreate"><el-icon><Check /></el-icon>确定新增</button></template>
     </el-dialog>
   </div>

--
Gitblit v1.7.1