소스 검색

feat: 管理端分配管理与停接开关页面

iwt 3 주 전
부모
커밋
77bcb1c6b2
4개의 변경된 파일144개의 추가작업 그리고 1개의 파일을 삭제
  1. 27 0
      cfc-web/src/api/butler.js
  2. 6 0
      cfc-web/src/router/index.js
  3. 87 0
      cfc-web/src/views/admin/ButlerAssignments.vue
  4. 24 1
      cfc-web/src/views/admin/review/ButlerAudit.vue

+ 27 - 0
cfc-web/src/api/butler.js

@@ -44,3 +44,30 @@ export function assignMemberToButler(data) {
     data: data
   })
 }
+
+// 分配关系列表
+export function getButlerAssignments(params) {
+  return request({
+    url: '/api/admin/butler/assignments',
+    method: 'post',
+    data: params
+  })
+}
+
+// 强制解绑
+export function unassignButler(data) {
+  return request({
+    url: '/api/admin/butler/unassign',
+    method: 'post',
+    data: data
+  })
+}
+
+// 停接开关
+export function toggleButlerAccepting(data) {
+  return request({
+    url: '/api/admin/butler/toggle-accepting',
+    method: 'post',
+    data: data
+  })
+}

+ 6 - 0
cfc-web/src/router/index.js

@@ -161,6 +161,12 @@ const routes = [
         component: () => import('@/views/admin/ReviewCenter.vue'),
         meta: { title: '审核中心', perm: 'audit' }
       },
+      {
+        path: 'butler-assignments',
+        name: 'ButlerAssignments',
+        component: () => import('@/views/admin/ButlerAssignments.vue'),
+        meta: { title: '管家分配管理', perm: 'audit' }
+      },
       {
         path: 'operation-logs',
         name: 'OperationLogs',

+ 87 - 0
cfc-web/src/views/admin/ButlerAssignments.vue

@@ -0,0 +1,87 @@
+<template>
+  <div class="app-container">
+    <el-card shadow="never">
+      <div slot="header">
+        <span>管家分配管理</span>
+      </div>
+      <el-table v-loading="loading" :data="records" border stripe>
+        <el-table-column prop="id" label="ID" width="80" />
+        <el-table-column prop="familyId" label="家庭ID" width="100" />
+        <el-table-column prop="butlerNickname" label="管家" min-width="140" />
+        <el-table-column prop="butlerUserId" label="管家用户ID" width="110" />
+        <el-table-column prop="level" label="订阅级别" width="100">
+          <template slot-scope="{ row }">
+            <el-tag :type="row.level === 'L2' ? 'warning' : 'info'" size="small">{{ row.level }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="绑定时间" width="170">
+          <template slot-scope="{ row }">{{ formatTime(row.assignedAt) }}</template>
+        </el-table-column>
+        <el-table-column label="操作" width="120" fixed="right">
+          <template slot-scope="{ row }">
+            <el-button size="mini" type="danger" @click="onUnassign(row)">强制解绑</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+      <el-pagination
+        style="margin-top: 16px; text-align: right;"
+        layout="total, prev, pager, next"
+        :total="total"
+        :page-size="pageSize"
+        :current-page.sync="pageNum"
+        @current-change="loadData"
+      />
+    </el-card>
+  </div>
+</template>
+
+<script>
+import { getButlerAssignments, unassignButler } from '@/api/butler'
+
+export default {
+  name: 'ButlerAssignments',
+  data() {
+    return {
+      loading: false,
+      records: [],
+      total: 0,
+      pageNum: 1,
+      pageSize: 10
+    }
+  },
+  created() {
+    this.loadData()
+  },
+  methods: {
+    formatTime(iso) {
+      if (!iso) return '-'
+      return iso.substring(0, 19).replace('T', ' ')
+    },
+    loadData() {
+      this.loading = true
+      getButlerAssignments({ pageNum: this.pageNum, pageSize: this.pageSize })
+        .then(res => {
+          const data = res.data || {}
+          this.records = data.records || []
+          this.total = data.total || 0
+        })
+        .finally(() => { this.loading = false })
+    },
+    onUnassign(row) {
+      this.$confirm(
+        `确定解除家庭 ${row.familyId} 与管家「${row.butlerNickname}」的绑定?`,
+        '强制解绑',
+        { type: 'warning' }
+      ).then(() => unassignButler({ assignmentId: row.id }))
+        .then(res => {
+          if (res.code === 200) {
+            this.$message.success('已解绑')
+            this.loadData()
+          } else {
+            this.$message.error(res.message || '操作失败')
+          }
+        }).catch(() => {})
+    }
+  }
+}
+</script>

+ 24 - 1
cfc-web/src/views/admin/review/ButlerAudit.vue

@@ -30,6 +30,15 @@
           <span v-else class="no-op">—</span>
         </template>
       </el-table-column>
+      <el-table-column label="接单状态" width="110">
+        <template slot-scope="{ row }">
+          <el-switch
+            :value="row.accepting !== 0"
+            active-color="#13ce66"
+            @change="onToggleAccepting(row)" />
+          <div v-if="row.accepting === 0" style="font-size: 12px; color: #f56c6c;">已停接</div>
+        </template>
+      </el-table-column>
     </el-table>
 
     <el-dialog title="拒绝原因" :visible.sync="rejectDialogVisible" width="400px">
@@ -43,7 +52,7 @@
 </template>
 
 <script>
-import { getButlerList, reviewButler } from '@/api/butler'
+import { getButlerList, reviewButler, toggleButlerAccepting } from '@/api/butler'
 import { searchUsers } from '@/api/admin'
 
 export default {
@@ -63,6 +72,20 @@ export default {
   methods: {
     statusType(s) { return { pending: 'warning', approved: 'success', rejected: 'danger' }[s] || 'info' },
     statusLabel(s) { return { pending: '待审核', approved: '已通过', rejected: '已拒绝' }[s] || s },
+    async onToggleAccepting(row) {
+      const next = row.accepting === 0 ? 1 : 0
+      try {
+        const res = await toggleButlerAccepting({ butlerUserId: row.userId, accepting: next })
+        if (res.code === 200) {
+          this.$set(row, 'accepting', next)
+          this.$message.success(next === 1 ? '已恢复接单' : '已停接新单')
+        } else {
+          this.$message.error(res.message || '操作失败')
+        }
+      } catch (e) {
+        this.$message.error('操作失败')
+      }
+    },
     async loadData() {
       this.loading = true
       try {