Tasks.vue 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. <template>
  2. <div class="page-container">
  3. <el-card>
  4. <div slot="header">
  5. <span>任务管理</span>
  6. <el-button
  7. type="primary"
  8. size="small"
  9. style="float: right"
  10. @click="handleRefresh"
  11. :loading="loading"
  12. >
  13. 刷新
  14. </el-button>
  15. </div>
  16. <!-- 搜索筛选 -->
  17. <el-form :inline="true" :model="searchForm" style="margin-bottom: 20px">
  18. <el-form-item label="任务标题">
  19. <el-input
  20. v-model="searchForm.title"
  21. placeholder="请输入任务标题"
  22. clearable
  23. @keyup.enter.native="handleSearch"
  24. ></el-input>
  25. </el-form-item>
  26. <el-form-item label="状态">
  27. <el-select v-model="searchForm.status" placeholder="全部状态" clearable @change="handleSearch">
  28. <el-option label="进行中" value="pending"></el-option>
  29. <el-option label="已完成" value="completed"></el-option>
  30. <el-option label="已逾期" value="overdue"></el-option>
  31. <el-option label="已取消" value="cancelled"></el-option>
  32. </el-select>
  33. </el-form-item>
  34. <el-form-item label="孩子ID">
  35. <el-input
  36. v-model="searchForm.childId"
  37. placeholder="请输入孩子ID"
  38. clearable
  39. @keyup.enter.native="handleSearch"
  40. ></el-input>
  41. </el-form-item>
  42. <el-form-item>
  43. <el-button type="primary" @click="handleSearch">搜索</el-button>
  44. <el-button @click="handleReset">重置</el-button>
  45. </el-form-item>
  46. </el-form>
  47. <!-- 统计卡片 -->
  48. <el-row :gutter="20" style="margin-bottom: 20px">
  49. <el-col :span="6">
  50. <el-card class="stat-card">
  51. <div class="stat-label">总任务数</div>
  52. <div class="stat-value">{{ stats.total || 0 }}</div>
  53. </el-card>
  54. </el-col>
  55. <el-col :span="6">
  56. <el-card class="stat-card">
  57. <div class="stat-label">今日任务</div>
  58. <div class="stat-value">{{ stats.today || 0 }}</div>
  59. </el-card>
  60. </el-col>
  61. </el-row>
  62. <!-- 任务列表 -->
  63. <el-table :data="tableData" border v-loading="loading" style="width: 100%">
  64. <el-table-column prop="id" label="ID" width="80"></el-table-column>
  65. <el-table-column prop="title" label="任务名称" min-width="200"></el-table-column>
  66. <el-table-column prop="points" label="积分" width="80">
  67. <template slot-scope="scope">
  68. <el-tag type="success">{{ scope.row.points }}</el-tag>
  69. </template>
  70. </el-table-column>
  71. <el-table-column prop="category" label="分类" width="100"></el-table-column>
  72. <el-table-column prop="deadline" label="截止时间" width="180">
  73. <template slot-scope="scope">
  74. {{ formatDate(scope.row.deadline) }}
  75. </template>
  76. </el-table-column>
  77. <el-table-column prop="status" label="状态" width="100">
  78. <template slot-scope="scope">
  79. <el-tag :type="getStatusType(scope.row.status)" size="small">
  80. {{ getStatusText(scope.row.status) }}
  81. </el-tag>
  82. </template>
  83. </el-table-column>
  84. <el-table-column prop="needReview" label="需审核" width="80">
  85. <template slot-scope="scope">
  86. <el-tag :type="scope.row.needReview ? 'warning' : 'info'" size="small">
  87. {{ scope.row.needReview ? '是' : '否' }}
  88. </el-tag>
  89. </template>
  90. </el-table-column>
  91. <el-table-column prop="childId" label="孩子ID" width="80"></el-table-column>
  92. <el-table-column prop="createdAt" label="创建时间" width="180">
  93. <template slot-scope="scope">
  94. {{ formatDate(scope.row.createdAt) }}
  95. </template>
  96. </el-table-column>
  97. </el-table>
  98. <!-- 分页 -->
  99. <el-pagination
  100. style="margin-top: 20px; text-align: right"
  101. @size-change="handleSizeChange"
  102. @current-change="handleCurrentChange"
  103. :current-page="pagination.current"
  104. :page-sizes="[10, 20, 50, 100]"
  105. :page-size="pagination.size"
  106. :total="pagination.total"
  107. layout="total, sizes, prev, pager, next, jumper"
  108. ></el-pagination>
  109. </el-card>
  110. </div>
  111. </template>
  112. <script>
  113. import { getTasksList, getTaskStats } from '@/api/admin'
  114. export default {
  115. name: 'Tasks',
  116. data() {
  117. return {
  118. loading: false,
  119. tableData: [],
  120. stats: {
  121. total: 0,
  122. today: 0
  123. },
  124. searchForm: {
  125. title: '',
  126. status: '',
  127. childId: ''
  128. },
  129. pagination: {
  130. current: 1,
  131. size: 10,
  132. total: 0
  133. }
  134. }
  135. },
  136. mounted() {
  137. this.loadTasks()
  138. this.loadStats()
  139. },
  140. methods: {
  141. async loadTasks() {
  142. this.loading = true
  143. try {
  144. const params = {
  145. page: this.pagination.current,
  146. size: this.pagination.size,
  147. ...this.searchForm
  148. }
  149. const res = await getTasksList(params)
  150. this.tableData = res.data.records || res.data || []
  151. this.pagination.total = res.data.total || this.tableData.length
  152. } catch (error) {
  153. this.$message.error(error.message || '加载任务列表失败')
  154. } finally {
  155. this.loading = false
  156. }
  157. },
  158. async loadStats() {
  159. try {
  160. const res = await getTaskStats()
  161. this.stats = res.data || { total: 0, today: 0 }
  162. } catch (error) {
  163. console.error('加载统计失败:', error)
  164. }
  165. },
  166. handleSearch() {
  167. this.pagination.current = 1
  168. this.loadTasks()
  169. },
  170. handleReset() {
  171. this.searchForm = {
  172. title: '',
  173. status: '',
  174. childId: ''
  175. }
  176. this.handleSearch()
  177. },
  178. handleRefresh() {
  179. this.loadTasks()
  180. this.loadStats()
  181. },
  182. handleSizeChange(val) {
  183. this.pagination.size = val
  184. this.loadTasks()
  185. },
  186. handleCurrentChange(val) {
  187. this.pagination.current = val
  188. this.loadTasks()
  189. },
  190. getStatusType(status) {
  191. const types = {
  192. pending: 'warning',
  193. completed: 'success',
  194. overdue: 'danger',
  195. cancelled: 'info'
  196. }
  197. return types[status] || 'info'
  198. },
  199. getStatusText(status) {
  200. const texts = {
  201. pending: '进行中',
  202. completed: '已完成',
  203. overdue: '已逾期',
  204. cancelled: '已取消'
  205. }
  206. return texts[status] || status
  207. },
  208. formatDate(dateStr) {
  209. if (!dateStr) return '-'
  210. const date = new Date(dateStr)
  211. return date.toLocaleString('zh-CN')
  212. }
  213. }
  214. }
  215. </script>
  216. <style scoped>
  217. .page-container {
  218. padding: 20px;
  219. }
  220. .stat-card {
  221. text-align: center;
  222. }
  223. .stat-label {
  224. color: #909399;
  225. font-size: 14px;
  226. margin-bottom: 10px;
  227. }
  228. .stat-value {
  229. font-size: 28px;
  230. font-weight: bold;
  231. color: #409EFF;
  232. }
  233. </style>