|
@@ -10,23 +10,24 @@
|
|
|
<el-button size="small" @click="loadData" :loading="loading">刷新</el-button>
|
|
<el-button size="small" @click="loadData" :loading="loading">刷新</el-button>
|
|
|
</div>
|
|
</div>
|
|
|
<div class="adm-table-wrap">
|
|
<div class="adm-table-wrap">
|
|
|
-<el-table :data="list" v-loading="loading" border stripe style="min-width:900px;" height="100%">
|
|
|
|
|
- <el-table-column prop="id" label="档案ID" width="80">
|
|
|
|
|
- <template slot="header">
|
|
|
|
|
- <span class="sort-header" @click.stop="onSortClick('id', $event)">档案ID<span v-if="sortIndex('id') !== null" class="sort-index" :class="sortClass('id') === 'sort-asc' ? 'sort-asc-badge' : 'sort-desc-badge'">{{ sortIndex('id') }}</span>{{ sortIcon('id') }}</span>
|
|
|
|
|
- </template>
|
|
|
|
|
|
|
+<el-table :data="pagedList" v-loading="loading" border stripe style="min-width:900px;" height="100%">
|
|
|
|
|
+ <el-table-column label="用户ID" width="80">
|
|
|
|
|
+ <template slot-scope="{ row }">{{ row.userId }}</template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <el-table-column label="用户" width="120" show-overflow-tooltip>
|
|
|
|
|
+ <template slot-scope="{ row }">{{ row.nickname || row.realName || '用户' + row.userId }}</template>
|
|
|
</el-table-column>
|
|
</el-table-column>
|
|
|
- <el-table-column label="用户" width="120">
|
|
|
|
|
- <template slot-scope="{ row }">{{ row.userName || '用户' + row.userId }}</template>
|
|
|
|
|
- </el-table-column>
|
|
|
|
|
<el-table-column prop="tier" label="等级" width="80">
|
|
<el-table-column prop="tier" label="等级" width="80">
|
|
|
- <template slot="header">
|
|
|
|
|
- <span class="sort-header" @click.stop="onSortClick('tier', $event)">等级<span v-if="sortIndex('tier') !== null" class="sort-index" :class="sortClass('tier') === 'sort-asc' ? 'sort-asc-badge' : 'sort-desc-badge'">{{ sortIndex('tier') }}</span>{{ sortIcon('tier') }}</span>
|
|
|
|
|
- </template>
|
|
|
|
|
|
|
+ <template slot-scope="{ row }">{{ row.tier || '-' }}</template>
|
|
|
</el-table-column>
|
|
</el-table-column>
|
|
|
<el-table-column prop="description" label="申请说明" min-width="200" show-overflow-tooltip>
|
|
<el-table-column prop="description" label="申请说明" min-width="200" show-overflow-tooltip>
|
|
|
- <template slot="header">
|
|
|
|
|
- <span class="sort-header" @click.stop="onSortClick('description', $event)">申请说明<span v-if="sortIndex('description') !== null" class="sort-index" :class="sortClass('description') === 'sort-asc' ? 'sort-asc-badge' : 'sort-desc-badge'">{{ sortIndex('description') }}</span>{{ sortIcon('description') }}</span>
|
|
|
|
|
|
|
+ <template slot-scope="{ row }">{{ row.description || '-' }}</template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <el-table-column label="来源" width="100">
|
|
|
|
|
+ <template slot-scope="{ row }">
|
|
|
|
|
+ <el-tag size="small" :type="row.source === 'service_role' ? 'success' : 'info'">
|
|
|
|
|
+ {{ row.source === 'service_role' ? '服务角色申请' : '用户资料' }}
|
|
|
|
|
+ </el-tag>
|
|
|
</template>
|
|
</template>
|
|
|
</el-table-column>
|
|
</el-table-column>
|
|
|
<el-table-column label="审核状态" width="100">
|
|
<el-table-column label="审核状态" width="100">
|
|
@@ -34,55 +35,113 @@
|
|
|
<el-tag :type="statusType(row.status)">{{ statusLabel(row.status) }}</el-tag>
|
|
<el-tag :type="statusType(row.status)">{{ statusLabel(row.status) }}</el-tag>
|
|
|
</template>
|
|
</template>
|
|
|
</el-table-column>
|
|
</el-table-column>
|
|
|
- <el-table-column label="操作" width="180" fixed="right">
|
|
|
|
|
|
|
+ <el-table-column label="操作" width="120" fixed="right">
|
|
|
<template slot-scope="{ row }">
|
|
<template slot-scope="{ row }">
|
|
|
- <template v-if="row.status === 'pending'">
|
|
|
|
|
- <el-button size="mini" type="success" @click="handleReview(row, 'approve')">通过</el-button>
|
|
|
|
|
- <el-button size="mini" type="danger" @click="showReject(row)">拒绝</el-button>
|
|
|
|
|
- </template>
|
|
|
|
|
- <span v-else class="no-op">—</span>
|
|
|
|
|
|
|
+ <el-button size="mini" type="primary" @click="showDetail(row)">{{ row.status === 'pending' ? '审核' : '查看' }}</el-button>
|
|
|
</template>
|
|
</template>
|
|
|
</el-table-column>
|
|
</el-table-column>
|
|
|
<el-table-column label="接单状态" width="110">
|
|
<el-table-column label="接单状态" width="110">
|
|
|
<template slot-scope="{ row }">
|
|
<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 v-if="row.status === 'approved'">
|
|
|
|
|
+ <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>
|
|
|
|
|
+ <span v-else style="color:#999">-</span>
|
|
|
</template>
|
|
</template>
|
|
|
</el-table-column>
|
|
</el-table-column>
|
|
|
</el-table>
|
|
</el-table>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
- <el-dialog title="拒绝原因" :visible.sync="rejectDialogVisible" width="400px">
|
|
|
|
|
- <el-input v-model="rejectReason" type="textarea" :rows="3" placeholder="请输入拒绝原因" />
|
|
|
|
|
- <span slot="footer">
|
|
|
|
|
- <el-button @click="rejectDialogVisible = false">取消</el-button>
|
|
|
|
|
- <el-button type="danger" @click="confirmReject">确认拒绝</el-button>
|
|
|
|
|
- </span>
|
|
|
|
|
- </el-dialog>
|
|
|
|
|
|
|
+ <el-pagination
|
|
|
|
|
+ v-if="total > 0"
|
|
|
|
|
+ :current-page="page"
|
|
|
|
|
+ :page-size="size"
|
|
|
|
|
+ :total="total"
|
|
|
|
|
+ layout="total, prev, pager, next"
|
|
|
|
|
+ @current-change="onPageChange"
|
|
|
|
|
+ style="margin-top:12px;text-align:right;"
|
|
|
|
|
+ />
|
|
|
|
|
+
|
|
|
|
|
+ <audit-detail-drawer
|
|
|
|
|
+ :visible.sync="detailVisible"
|
|
|
|
|
+ title="管家审核详情"
|
|
|
|
|
+ :status="currentRow ? currentRow.status : ''"
|
|
|
|
|
+ :loading="auditLoading"
|
|
|
|
|
+ @approve="handleApprove"
|
|
|
|
|
+ @reject="handleReject">
|
|
|
|
|
+ <el-form v-if="currentRow" label-width="110px" size="small">
|
|
|
|
|
+ <el-form-item label="用户ID">{{ currentRow.userId }}</el-form-item>
|
|
|
|
|
+ <el-form-item label="昵称">{{ currentRow.nickname || '-' }}</el-form-item>
|
|
|
|
|
+ <el-form-item label="真实姓名">{{ currentRow.realName || '-' }}</el-form-item>
|
|
|
|
|
+ <el-form-item label="手机号">{{ currentRow.phone || '-' }}</el-form-item>
|
|
|
|
|
+ <el-form-item label="来源">{{ currentRow.source === 'service_role' ? '服务角色申请' : '用户资料' }}</el-form-item>
|
|
|
|
|
+ <el-form-item label="管家等级">{{ currentRow.tier || '-' }}</el-form-item>
|
|
|
|
|
+ <el-form-item label="服务说明">{{ currentRow.description || '-' }}</el-form-item>
|
|
|
|
|
+ <template v-if="currentRow.source === 'service_role'">
|
|
|
|
|
+ <el-form-item label="身份证号">{{ currentRow.idCard || '-' }}</el-form-item>
|
|
|
|
|
+ <el-form-item label="身份证照片">
|
|
|
|
|
+ <div v-if="currentRow.idCardImages && currentRow.idCardImages.length">
|
|
|
|
|
+ <img v-for="(img, i) in currentRow.idCardImages" :key="'id' + i" :src="img" class="audit-img" />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <span v-else>-</span>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="资质证书">
|
|
|
|
|
+ <div v-if="currentRow.certificateImages && currentRow.certificateImages.length">
|
|
|
|
|
+ <img v-for="(img, i) in currentRow.certificateImages" :key="'cert' + i" :src="img" class="audit-img" />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <span v-else>-</span>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="所在地区">{{ [currentRow.province, currentRow.city, currentRow.district, currentRow.street].filter(Boolean).join(' ') || '-' }}</el-form-item>
|
|
|
|
|
+ <el-form-item label="详细地址">{{ currentRow.addressDetail || '-' }}</el-form-item>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ <el-form-item label="审核状态">
|
|
|
|
|
+ <el-tag :type="statusType(currentRow.status)">{{ statusLabel(currentRow.status) }}</el-tag>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="驳回原因" v-if="currentRow.rejectReason" style="color:#f56c6c;">
|
|
|
|
|
+ {{ currentRow.rejectReason }}
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-form>
|
|
|
|
|
+ </audit-detail-drawer>
|
|
|
</div>
|
|
</div>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script>
|
|
<script>
|
|
|
import SortMixin from '@/mixins/SortMixin'
|
|
import SortMixin from '@/mixins/SortMixin'
|
|
|
-import { getButlerList, reviewButler, toggleButlerAccepting } from '@/api/butler'
|
|
|
|
|
-import { searchUsers } from '@/api/admin'
|
|
|
|
|
|
|
+import { getButlerUserList, reviewButler, toggleButlerAccepting } from '@/api/butler'
|
|
|
|
|
+import { listServiceRoleApplications, approveServiceRoleApplication, rejectServiceRoleApplication } from '@/api/service-role'
|
|
|
|
|
+import { mapServiceRoleRow, mergeAuditRows } from '@/utils/auditRowMapping'
|
|
|
|
|
+import AuditDetailDrawer from './components/AuditDetailDrawer.vue'
|
|
|
|
|
|
|
|
export default {
|
|
export default {
|
|
|
name: 'ButlerAudit',
|
|
name: 'ButlerAudit',
|
|
|
|
|
+ components: { AuditDetailDrawer },
|
|
|
mixins: [SortMixin],
|
|
mixins: [SortMixin],
|
|
|
computed: {
|
|
computed: {
|
|
|
tableHeight() {
|
|
tableHeight() {
|
|
|
return window.innerHeight - 300
|
|
return window.innerHeight - 300
|
|
|
|
|
+ },
|
|
|
|
|
+ mergedList() {
|
|
|
|
|
+ let rows = mergeAuditRows(this.userRows, this.serviceRoleRows)
|
|
|
|
|
+ if (this.statusFilter) rows = rows.filter(r => r.status === this.statusFilter)
|
|
|
|
|
+ return rows
|
|
|
|
|
+ },
|
|
|
|
|
+ pagedList() {
|
|
|
|
|
+ const start = (this.page - 1) * this.size
|
|
|
|
|
+ return this.mergedList.slice(start, start + this.size)
|
|
|
|
|
+ },
|
|
|
|
|
+ total() {
|
|
|
|
|
+ return this.mergedList.length
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
data() {
|
|
data() {
|
|
|
return {
|
|
return {
|
|
|
- list: [], loading: false, statusFilter: 'pending',
|
|
|
|
|
- sortableColumns: { id: '档案ID', tier: '等级', description: '申请说明' },
|
|
|
|
|
- rejectDialogVisible: false, rejectTarget: null, rejectReason: ''
|
|
|
|
|
|
|
+ userRows: [], serviceRoleRows: [], loading: false, statusFilter: 'pending',
|
|
|
|
|
+ page: 1, size: 20,
|
|
|
|
|
+ sortableColumns: { id: '用户ID', tier: '等级', description: '申请说明' },
|
|
|
|
|
+ detailVisible: false, currentRow: null, auditLoading: false
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
mounted() { this.loadData() },
|
|
mounted() { this.loadData() },
|
|
@@ -90,6 +149,57 @@ export default {
|
|
|
loadList() { this.loadData() },
|
|
loadList() { this.loadData() },
|
|
|
statusType(s) { return { pending: 'warning', approved: 'success', rejected: 'danger' }[s] || 'info' },
|
|
statusType(s) { return { pending: 'warning', approved: 'success', rejected: 'danger' }[s] || 'info' },
|
|
|
statusLabel(s) { return { pending: '待审核', approved: '已通过', rejected: '已拒绝' }[s] || s },
|
|
statusLabel(s) { return { pending: '待审核', approved: '已通过', rejected: '已拒绝' }[s] || s },
|
|
|
|
|
+ onPageChange(p) { this.page = p },
|
|
|
|
|
+ parseButlerInfo(info) {
|
|
|
|
|
+ if (!info) return {}
|
|
|
|
|
+ try {
|
|
|
|
|
+ return typeof info === 'string' ? JSON.parse(info) : info
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ return {}
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ serviceRoleStatusParam() {
|
|
|
|
|
+ if (this.statusFilter === 'pending') return 0
|
|
|
|
|
+ if (this.statusFilter === 'approved') return 1
|
|
|
|
|
+ if (this.statusFilter === 'rejected') return 2
|
|
|
|
|
+ return ''
|
|
|
|
|
+ },
|
|
|
|
|
+ async loadData() {
|
|
|
|
|
+ this.loading = true
|
|
|
|
|
+ this.page = 1
|
|
|
|
|
+ try {
|
|
|
|
|
+ const [userRes, srRes] = await Promise.all([
|
|
|
|
|
+ getButlerUserList({ status: this.statusFilter || undefined, page: 1, size: 500 }),
|
|
|
|
|
+ listServiceRoleApplications({ status: this.serviceRoleStatusParam(), role: 'butler' })
|
|
|
|
|
+ ])
|
|
|
|
|
+ const records = (userRes && userRes.data && userRes.data.list) || []
|
|
|
|
|
+ this.userRows = records.map(r => {
|
|
|
|
|
+ const info = this.parseButlerInfo(r.butlerInfo)
|
|
|
|
|
+ return {
|
|
|
|
|
+ source: 'user',
|
|
|
|
|
+ userId: r.userId,
|
|
|
|
|
+ nickname: r.nickname,
|
|
|
|
|
+ realName: r.realName,
|
|
|
|
|
+ phone: r.phone,
|
|
|
|
|
+ status: r.butlerStatus || 'pending',
|
|
|
|
|
+ rejectReason: info.rejectReason,
|
|
|
|
|
+ tier: info.tier,
|
|
|
|
|
+ description: info.description,
|
|
|
|
|
+ accepting: info.accepting,
|
|
|
|
|
+ submittedAt: r.updatedAt,
|
|
|
|
|
+ raw: r
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ const srData = (srRes && srRes.data) || []
|
|
|
|
|
+ this.serviceRoleRows = srData.map(a => mapServiceRoleRow(a))
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error(e)
|
|
|
|
|
+ this.$message.error('加载失败')
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ this.loading = false
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ showDetail(row) { this.currentRow = row; this.detailVisible = true },
|
|
|
async onToggleAccepting(row) {
|
|
async onToggleAccepting(row) {
|
|
|
const next = row.accepting === 0 ? 1 : 0
|
|
const next = row.accepting === 0 ? 1 : 0
|
|
|
try {
|
|
try {
|
|
@@ -104,60 +214,35 @@ export default {
|
|
|
this.$message.error('操作失败')
|
|
this.$message.error('操作失败')
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
- async loadData() {
|
|
|
|
|
- this.loading = true
|
|
|
|
|
|
|
+ async handleApprove() {
|
|
|
|
|
+ if (!this.currentRow) return
|
|
|
|
|
+ this.auditLoading = true
|
|
|
try {
|
|
try {
|
|
|
- const params = { status: this.statusFilter || undefined, page: 1, size: 50 }
|
|
|
|
|
- if (this.sortState.length > 0) params.sort = this.sortState
|
|
|
|
|
- const res = await getButlerList(params)
|
|
|
|
|
- this.list = res.data && res.data.records || res.data && res.data.list || res.data || []
|
|
|
|
|
- await this.resolveUserNames(this.list, 'userId')
|
|
|
|
|
- } catch (e) { this.$message.error('加载失败')
|
|
|
|
|
- } finally { this.loading = false }
|
|
|
|
|
- },
|
|
|
|
|
- async handleReview(row, action) {
|
|
|
|
|
- try {
|
|
|
|
|
- await reviewButler({ id: row.id, action })
|
|
|
|
|
|
|
+ if (this.currentRow.source === 'service_role') {
|
|
|
|
|
+ await approveServiceRoleApplication(this.currentRow.applyId)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ await reviewButler({ userId: this.currentRow.userId, action: 'approve' })
|
|
|
|
|
+ }
|
|
|
this.$message.success('审核完成')
|
|
this.$message.success('审核完成')
|
|
|
- this.rejectDialogVisible = false
|
|
|
|
|
|
|
+ this.detailVisible = false
|
|
|
this.loadData()
|
|
this.loadData()
|
|
|
- } catch (e) { this.$message.error('操作失败') }
|
|
|
|
|
|
|
+ } catch (e) { this.$message.error('操作失败')
|
|
|
|
|
+ } finally { this.auditLoading = false }
|
|
|
},
|
|
},
|
|
|
- showReject(row) { this.rejectTarget = row; this.rejectReason = ''; this.rejectDialogVisible = true },
|
|
|
|
|
- resolveUserNames(items, idField) {
|
|
|
|
|
- if (!items || items.length === 0) return Promise.resolve()
|
|
|
|
|
- var ids = []
|
|
|
|
|
- items.forEach(function(item) {
|
|
|
|
|
- if (item[idField] && ids.indexOf(item[idField]) === -1) ids.push(item[idField])
|
|
|
|
|
- })
|
|
|
|
|
- var nameMap = {}
|
|
|
|
|
- return Promise.all(ids.map(function(id) {
|
|
|
|
|
- return searchUsers({ keyword: String(id) })
|
|
|
|
|
- .then(function(res) {
|
|
|
|
|
- if (res.data && res.data.length > 0) {
|
|
|
|
|
- var u = res.data[0]
|
|
|
|
|
- nameMap[id] = u.nickname || u.realName || '用户' + id
|
|
|
|
|
- } else {
|
|
|
|
|
- nameMap[id] = '用户' + id
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
- .catch(function() {
|
|
|
|
|
- nameMap[id] = '用户' + id
|
|
|
|
|
- })
|
|
|
|
|
- })).then(function() {
|
|
|
|
|
- items.forEach(function(item) {
|
|
|
|
|
- item.userName = nameMap[item[idField]] || '用户' + item[idField]
|
|
|
|
|
- })
|
|
|
|
|
- })
|
|
|
|
|
- },
|
|
|
|
|
- async confirmReject() {
|
|
|
|
|
- if (!this.rejectReason) return this.$message.warning('请填写拒绝原因')
|
|
|
|
|
|
|
+ async handleReject(reason) {
|
|
|
|
|
+ if (!this.currentRow) return
|
|
|
|
|
+ this.auditLoading = true
|
|
|
try {
|
|
try {
|
|
|
- await reviewButler({ id: this.rejectTarget.id, action: 'reject' })
|
|
|
|
|
|
|
+ if (this.currentRow.source === 'service_role') {
|
|
|
|
|
+ await rejectServiceRoleApplication(this.currentRow.applyId, reason)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ await reviewButler({ userId: this.currentRow.userId, action: 'reject', reason })
|
|
|
|
|
+ }
|
|
|
this.$message.success('已拒绝')
|
|
this.$message.success('已拒绝')
|
|
|
- this.rejectDialogVisible = false
|
|
|
|
|
|
|
+ this.detailVisible = false
|
|
|
this.loadData()
|
|
this.loadData()
|
|
|
- } catch (e) { this.$message.error('操作失败') }
|
|
|
|
|
|
|
+ } catch (e) { this.$message.error('操作失败')
|
|
|
|
|
+ } finally { this.auditLoading = false }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -171,4 +256,5 @@ export default {
|
|
|
.sort-index { color: #fff; border-radius: 8px; font-size: 10px; padding: 0 4px; margin: 0 2px; }
|
|
.sort-index { color: #fff; border-radius: 8px; font-size: 10px; padding: 0 4px; margin: 0 2px; }
|
|
|
.sort-asc-badge { background: #f56c6c; }
|
|
.sort-asc-badge { background: #f56c6c; }
|
|
|
.sort-desc-badge { background: #409eff; }
|
|
.sort-desc-badge { background: #409eff; }
|
|
|
-</style>
|
|
|
|
|
|
|
+.audit-img { max-width: 160px; max-height: 110px; border-radius: 4px; margin-right: 8px; border: 1px solid #eee; }
|
|
|
|
|
+</style>
|