增加新人特权的修改

This commit is contained in:
zhonghua.li
2026-05-22 22:37:44 +08:00
parent 0a2efaaf11
commit b5baeee764
3 changed files with 751 additions and 120 deletions

View File

@@ -107,3 +107,14 @@ export function syncHxrAdminColleagueVip(params) {
skipResponseNormalize: true
})
}
/** 按用户 ID 列表批量开通或关闭 hxrd 用户特权 */
export function updateHxrAdminUserVipByUserIds(data) {
return request({
url: '/lbPurchaseApply/updateHxrAdminUserVipByUserIds',
method: 'post',
data,
timeout: 120000,
skipResponseNormalize: true
})
}

View File

@@ -305,6 +305,7 @@ export default {
{ label: '付韾蕊老师', value: '付韾蕊老师' },
{ label: '陈晓琴老师', value: '陈晓琴老师' },
{ label: '李军老师', value: '李军老师' },
{ label: '李军A老师', value: '李军A老师' },
{ label: '刘伟翔老师', value: '刘伟翔老师' }
],
assessmentTeacherOptions: [

View File

@@ -1,130 +1,319 @@
<template>
<div class="app-container">
<el-card class="filter-container" shadow="never">
<el-form :model="queryParams" :inline="true" label-width="90px">
<el-form-item label="申请人">
<el-input v-model="queryParams.applyUser" clearable placeholder="请输入申请人" style="width: 180px" />
</el-form-item>
<el-form-item label="申请电话">
<el-input v-model="queryParams.applyPhone" clearable placeholder="请输入申请电话" style="width: 180px" />
</el-form-item>
<el-form-item label="同事姓名">
<el-input v-model="queryParams.colleagueName" clearable placeholder="请输入同事姓名" style="width: 180px" />
</el-form-item>
<el-form-item label="组长姓名">
<el-input v-model="queryParams.teamLeader" clearable placeholder="请输入组长姓名" style="width: 180px" />
</el-form-item>
<el-form-item label="申请日期">
<el-tabs v-model="activeTab" class="purchase-apply-tabs">
<el-tab-pane label="进货申请" name="purchaseApply">
<el-card class="filter-container" shadow="never">
<el-form :model="queryParams" :inline="true" label-width="90px">
<el-form-item label="申请人">
<el-input v-model="queryParams.applyUser" clearable placeholder="请输入申请人" style="width: 180px" />
</el-form-item>
<el-form-item label="申请电话">
<el-input v-model="queryParams.applyPhone" clearable placeholder="请输入申请电话" style="width: 180px" />
</el-form-item>
<el-form-item label="同事姓名">
<el-input v-model="queryParams.colleagueName" clearable placeholder="请输入同事姓名" style="width: 180px" />
</el-form-item>
<el-form-item label="组长姓名">
<el-input v-model="queryParams.teamLeader" clearable placeholder="请输入组长姓名" style="width: 180px" />
</el-form-item>
<el-form-item label="申请日期">
<el-date-picker
v-model="queryParams.applyDate"
type="date"
value-format="yyyy-MM-dd"
clearable
placeholder="请选择申请日期"
style="width: 180px"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">查询</el-button>
<el-button icon="el-icon-delete" @click="resetQuery">清空</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="action-container" shadow="never">
<el-button type="primary" icon="el-icon-plus" @click="handleAdd">新增</el-button>
<el-date-picker
v-model="queryParams.applyDate"
type="date"
v-model="privilegeDateRange"
type="daterange"
range-separator=""
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="yyyy-MM-dd"
clearable
placeholder="请选择申请日期"
style="width: 180px"
style="width: 280px; margin-left: 12px"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">查询</el-button>
<el-button icon="el-icon-delete" @click="resetQuery">清空</el-button>
</el-form-item>
</el-form>
</el-card>
<el-button type="warning" icon="el-icon-view" :loading="privilegeLoading" @click="handleViewPrivilege">
查看特权
</el-button>
<el-button
type="success"
icon="el-icon-download"
style="margin-left: 12px"
@click="openExportNewcomerPrivilegeDialog"
>
导出新人特权
</el-button>
<el-button
type="success"
icon="el-icon-download"
style="margin-left: 12px"
@click="openExportColleaguePrivilegeDialog"
>
导出同事特权
</el-button>
</el-card>
<el-card class="action-container" shadow="never">
<el-button type="primary" icon="el-icon-plus" @click="handleAdd">新增</el-button>
<el-date-picker
v-model="privilegeDateRange"
type="daterange"
range-separator=""
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="yyyy-MM-dd"
style="width: 280px; margin-left: 12px"
/>
<el-button type="warning" icon="el-icon-view" :loading="privilegeLoading" @click="handleViewPrivilege">
查看特权
</el-button>
<el-button type="success" icon="el-icon-present" style="margin-left: 12px" @click="openNewcomerVipDialog">
开通新人特权
</el-button>
<el-button type="success" icon="el-icon-present" style="margin-left: 12px" @click="openColleagueVipDialog">
开通同事特权
</el-button>
<el-button
type="success"
icon="el-icon-download"
style="margin-left: 12px"
@click="openExportNewcomerPrivilegeDialog"
>
导出新人特权
</el-button>
<el-button
type="success"
icon="el-icon-download"
style="margin-left: 12px"
@click="openExportColleaguePrivilegeDialog"
>
导出同事特权
</el-button>
</el-card>
<el-card class="table-container" shadow="never">
<div v-loading="loading" class="purchase-apply-sheet-wrap">
<table v-if="groupedList.length" class="purchase-apply-sheet">
<thead>
<tr>
<th>日期和人数</th>
<th>序号</th>
<th>昵称</th>
<th>手机号</th>
<th>特权开通日期</th>
<th>推荐人</th>
<th>手机号</th>
<th>领导人</th>
<th>开通日期</th>
<th class="col-actions">操作</th>
</tr>
</thead>
<tbody>
<template v-for="group in groupedList">
<tr v-for="(row, rowIndex) in group.rows" :key="row.id || `${group.key}-${rowIndex}`">
<td
v-if="rowIndex === 0"
:rowspan="group.rows.length"
class="date-cell"
>
{{ group.label }}
</td>
<td>{{ rowIndex + 1 }}</td>
<td>{{ row.applyUser || '-' }}</td>
<td>{{ row.applyPhone || '-' }}</td>
<td>{{ formatTryDate(row.tryDate) }}</td>
<td>{{ row.colleagueName || '-' }}</td>
<td>{{ row.colleaguePhone || '-' }}</td>
<td>{{ row.teamLeader || '-' }}</td>
<td>{{ formatOpenDate(row.applyDate) }}</td>
<td class="col-actions">
<el-button type="text" @click="handleEdit(row)">编辑</el-button>
<el-button type="text" style="color:#F56C6C;" @click="handleDelete(row)">删除</el-button>
</td>
</tr>
</template>
</tbody>
</table>
<div v-else class="purchase-apply-sheet-empty">暂无数据</div>
</div>
<el-card class="table-container" shadow="never">
<div v-loading="loading" class="purchase-apply-sheet-wrap">
<table v-if="groupedList.length" class="purchase-apply-sheet">
<thead>
<tr>
<th>日期和人数</th>
<th>序号</th>
<th>昵称</th>
<th>手机号</th>
<th>特权开通日期</th>
<th>推荐人</th>
<th>手机号</th>
<th>领导人</th>
<th>开通日期</th>
<th class="col-actions">操作</th>
</tr>
</thead>
<tbody>
<template v-for="group in groupedList">
<tr v-for="(row, rowIndex) in group.rows" :key="row.id || `${group.key}-${rowIndex}`">
<td
v-if="rowIndex === 0"
:rowspan="group.rows.length"
class="date-cell"
>
{{ group.label }}
</td>
<td>{{ rowIndex + 1 }}</td>
<td>{{ row.applyUser || '-' }}</td>
<td>{{ row.applyPhone || '-' }}</td>
<td>{{ formatTryDate(row.tryDate) }}</td>
<td>{{ row.colleagueName || '-' }}</td>
<td>{{ row.colleaguePhone || '-' }}</td>
<td>{{ row.teamLeader || '-' }}</td>
<td>{{ formatOpenDate(row.applyDate) }}</td>
<td class="col-actions">
<el-button type="text" @click="handleEdit(row)">编辑</el-button>
<el-button type="text" style="color:#F56C6C;" @click="handleDelete(row)">删除</el-button>
</td>
</tr>
</template>
</tbody>
</table>
<div v-else class="purchase-apply-sheet-empty">暂无数据</div>
</div>
<el-pagination
:current-page="queryParams.current"
:page-size="queryParams.size"
:page-sizes="[10, 20, 50, 100]"
:total="total"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</el-card>
</el-tab-pane>
<el-pagination
:current-page="queryParams.current"
:page-size="queryParams.size"
:page-sizes="[10, 20, 50, 100]"
:total="total"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</el-card>
<el-tab-pane label="特权开通" name="privilege">
<el-card class="filter-container" shadow="never">
<el-form :model="queryParams" :inline="true" label-width="90px">
<el-form-item label="申请人">
<el-input v-model="queryParams.applyUser" clearable placeholder="请输入申请人" style="width: 180px" />
</el-form-item>
<el-form-item label="申请电话">
<el-input v-model="queryParams.applyPhone" clearable placeholder="请输入申请电话" style="width: 180px" />
</el-form-item>
<el-form-item label="同事姓名">
<el-input v-model="queryParams.colleagueName" clearable placeholder="请输入同事姓名" style="width: 180px" />
</el-form-item>
<el-form-item label="组长姓名">
<el-input v-model="queryParams.teamLeader" clearable placeholder="请输入组长姓名" style="width: 180px" />
</el-form-item>
<el-form-item label="申请日期">
<el-date-picker
v-model="queryParams.applyDate"
type="date"
value-format="yyyy-MM-dd"
clearable
placeholder="请选择申请日期"
style="width: 180px"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">查询</el-button>
<el-button icon="el-icon-delete" @click="resetQuery">清空</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="action-container" shadow="never">
<el-button type="primary" icon="el-icon-plus" @click="handleAdd">新增</el-button>
<el-button
type="warning"
icon="el-icon-refresh"
:loading="syncUserLoading"
style="margin-left: 12px"
@click="handleSyncFromHxr"
>
同步用户
</el-button>
<el-date-picker
v-model="privilegeDateRange"
type="daterange"
range-separator=""
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="yyyy-MM-dd"
style="width: 280px; margin-left: 12px"
/>
<el-button type="warning" icon="el-icon-view" :loading="privilegeLoading" @click="handleViewPrivilege">
查看特权
</el-button>
<el-button type="success" icon="el-icon-present" style="margin-left: 12px" @click="openNewcomerVipDialog">
开通新人特权
</el-button>
<el-button type="success" icon="el-icon-present" style="margin-left: 12px" @click="openColleagueVipDialog">
开通同事特权
</el-button>
<el-button
type="primary"
icon="el-icon-setting"
style="margin-left: 12px"
@click="openBatchUserVipDialog('newcomer')"
>
开通关闭新人特权
</el-button>
<el-button
type="primary"
icon="el-icon-setting"
style="margin-left: 12px"
@click="openBatchUserVipDialog('referrer')"
>
开通关闭推荐人特权
</el-button>
</el-card>
<el-card class="table-container" shadow="never">
<div v-loading="loading" class="purchase-apply-sheet-wrap">
<table v-if="groupedList.length" class="purchase-apply-sheet">
<thead>
<tr>
<th>日期和人数</th>
<th class="cell-nickname-check">
<span class="cell-nickname-check-inner">
<el-checkbox
:indeterminate="privilegeSelectAllIndeterminate"
:value="privilegeSelectAllChecked"
@input="handlePrivilegeSelectAllChange"
/>
<span>昵称</span>
</span>
</th>
<th>手机号</th>
<th>特权开通日期</th>
<th>申请人特权</th>
<th>开通日期</th>
<th class="cell-nickname-check">
<span class="cell-nickname-check-inner">
<el-checkbox
:indeterminate="privilegeRecommenderSelectAllIndeterminate"
:value="privilegeRecommenderSelectAllChecked"
@input="handlePrivilegeRecommenderSelectAllChange"
/>
<span>推荐人</span>
</span>
</th>
<th>手机号</th>
<th>最大可抢订单</th>
<th>推荐人特权</th>
<th class="col-actions">操作</th>
</tr>
</thead>
<tbody>
<template v-for="group in groupedList">
<tr v-for="(row, rowIndex) in group.rows" :key="row.id || `privilege-tab-${group.key}-${rowIndex}`">
<td
v-if="rowIndex === 0"
:rowspan="group.rows.length"
class="date-cell"
>
<div class="date-cell-content">
<div class="date-cell-label">{{ group.label }}</div>
<div class="date-cell-checks">
<el-checkbox
:indeterminate="isGroupApplicantIndeterminate(group)"
:value="isGroupApplicantAllSelected(group)"
@input="handleGroupApplicantSelectChange(group, $event)"
>
选中申请人
</el-checkbox>
<el-checkbox
:indeterminate="isGroupRecommenderIndeterminate(group)"
:value="isGroupRecommenderAllSelected(group)"
@input="handleGroupRecommenderSelectChange(group, $event)"
>
选中推荐人
</el-checkbox>
</div>
</div>
</td>
<td class="cell-nickname-check">
<span class="cell-nickname-check-inner">
<el-checkbox
:value="isPrivilegeRowSelected(getPrivilegeRowKey(row, group, rowIndex))"
@input="setPrivilegeRowSelected(getPrivilegeRowKey(row, group, rowIndex), $event)"
/>
<span>{{ row.applyUser || '-' }}</span>
</span>
</td>
<td>{{ row.applyPhone || '-' }}</td>
<td>{{ formatTryDate(row.tryDate) }}</td>
<td>{{ formatApplicantPrivilege(row) }}</td>
<td>{{ formatOpenDate(row.applyDate) }}</td>
<td class="cell-nickname-check">
<span class="cell-nickname-check-inner">
<el-checkbox
:value="isPrivilegeRecommenderRowSelected(getPrivilegeRecommenderRowKey(row, group, rowIndex))"
@input="setPrivilegeRecommenderRowSelected(getPrivilegeRecommenderRowKey(row, group, rowIndex), $event)"
/>
<span>{{ row.colleagueName || '-' }}</span>
</span>
</td>
<td>{{ row.colleaguePhone || '-' }}</td>
<td>{{ formatReferrerMaxOrder(row) }}</td>
<td>{{ formatReferrerPrivilege(row) }}</td>
<td class="col-actions">
<el-button type="text" @click="handleEdit(row)">编辑</el-button>
<el-button type="text" style="color:#F56C6C;" @click="handleDelete(row)">删除</el-button>
</td>
</tr>
</template>
</tbody>
</table>
<div v-else class="purchase-apply-sheet-empty">暂无数据</div>
</div>
<el-pagination
:current-page="queryParams.current"
:page-size="queryParams.size"
:page-sizes="[10, 20, 50, 100]"
:total="total"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</el-card>
</el-tab-pane>
</el-tabs>
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="720px" @close="resetForm">
<el-form ref="dataForm" :model="form" :rules="formRules" label-width="110px">
@@ -342,6 +531,56 @@
</div>
</el-dialog>
<el-dialog
:title="batchUserVipDialogTitle"
:visible.sync="batchUserVipDialogVisible"
width="520px"
@close="resetBatchUserVipDialog"
>
<el-form label-width="120px">
<el-form-item label="已选用户">
<el-input :value="batchUserVipSelectedCountDisplay" disabled />
</el-form-item>
<el-form-item label="特权状态" required>
<el-select v-model="batchUserVipForm.isVip" placeholder="请选择" style="width: 100%">
<el-option label="开通" :value="1" />
<el-option label="关闭" :value="0" />
</el-select>
</el-form-item>
<el-form-item label="VIP到期" required>
<el-date-picker
v-model="batchUserVipForm.vipTime"
type="datetime"
clearable
value-format="yyyy-MM-dd HH:mm:ss"
placeholder="请选择 VIP 到期时间"
style="width: 100%"
/>
</el-form-item>
<el-form-item label="max_order" required>
<el-input-number
v-model="batchUserVipForm.maxOrder"
:min="batchUserVipMaxOrderMin"
:max="999999"
:step="1"
:disabled="batchUserVipMaxOrderDisabled"
controls-position="right"
style="width: 100%"
placeholder="最大订单数"
/>
</el-form-item>
<el-form-item label="租户ID">
<el-input :value="batchUserVipTenantIdDisplay" disabled placeholder="未获取到租户ID" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="batchUserVipDialogVisible = false">取消</el-button>
<el-button type="primary" :loading="batchUserVipSubmitLoading" @click="handleConfirmBatchUserVip">
确定
</el-button>
</div>
</el-dialog>
<el-dialog
title="导出同事特权"
:visible.sync="exportColleaguePrivilegeDialogVisible"
@@ -446,8 +685,10 @@ import {
parseLbPurchaseApplyFromOrderInfo,
syncHxrAdminColleagueVip,
syncHxrAdminUserVip,
updateHxrAdminUserVipByUserIds,
updateLbPurchaseApply
} from '@/api/lb-purchase-apply'
import { syncLbUserFromHxr } from '@/api/lb-user'
import { getBusinessHeaders } from '@/utils/business-headers'
export default {
@@ -455,6 +696,7 @@ export default {
data() {
const today = this.getCurrentDate()
return {
activeTab: 'purchaseApply',
newcomerVipDialogVisible: false,
newcomerVipSubmitLoading: false,
newcomerVipApplyDateRange: [today, today],
@@ -465,6 +707,7 @@ export default {
colleagueVipApplyDateStart: today,
colleagueVipApplyDateEnd: today,
colleagueVipTenantId: '',
syncUserLoading: false,
loading: false,
submitLoading: false,
parseAiLoading: false,
@@ -490,8 +733,20 @@ export default {
exportColleaguePrivilegeApplyDateStart: today,
exportColleaguePrivilegeApplyDateEnd: today,
exportColleaguePrivilegeTenantId: '',
batchUserVipDialogVisible: false,
batchUserVipMode: 'newcomer',
batchUserVipSubmitLoading: false,
batchUserVipTenantIdDisplay: '',
batchUserVipSelectedUserIds: [],
batchUserVipForm: {
isVip: 1,
vipTime: '',
maxOrder: 1
},
privilegeDateRange: [today, today],
privilegeList: [],
privilegeSelectedKeys: [],
privilegeRecommenderSelectedKeys: [],
queryParams: {
current: 1,
size: 10,
@@ -526,6 +781,61 @@ export default {
},
privilegeGroupedList() {
return this.buildGroupedList(this.privilegeList)
},
privilegeTabVisibleRowKeys() {
const keys = []
this.groupedList.forEach((group) => {
group.rows.forEach((row, rowIndex) => {
keys.push(this.getPrivilegeRowKey(row, group, rowIndex))
})
})
return keys
},
privilegeSelectAllChecked() {
const keys = this.privilegeTabVisibleRowKeys
if (!keys.length) return false
return keys.every((key) => this.privilegeSelectedKeys.includes(key))
},
privilegeSelectAllIndeterminate() {
const keys = this.privilegeTabVisibleRowKeys
if (!keys.length) return false
const selectedCount = keys.filter((key) => this.privilegeSelectedKeys.includes(key)).length
return selectedCount > 0 && selectedCount < keys.length
},
privilegeRecommenderTabVisibleRowKeys() {
const keys = []
this.groupedList.forEach((group) => {
group.rows.forEach((row, rowIndex) => {
keys.push(this.getPrivilegeRecommenderRowKey(row, group, rowIndex))
})
})
return keys
},
privilegeRecommenderSelectAllChecked() {
const keys = this.privilegeRecommenderTabVisibleRowKeys
if (!keys.length) return false
return keys.every((key) => this.privilegeRecommenderSelectedKeys.includes(key))
},
privilegeRecommenderSelectAllIndeterminate() {
const keys = this.privilegeRecommenderTabVisibleRowKeys
if (!keys.length) return false
const selectedCount = keys.filter((key) => this.privilegeRecommenderSelectedKeys.includes(key)).length
return selectedCount > 0 && selectedCount < keys.length
},
batchUserVipSelectedCountDisplay() {
const n = Array.isArray(this.batchUserVipSelectedUserIds)
? this.batchUserVipSelectedUserIds.length
: 0
return n > 0 ? `已选 ${n} 个用户` : '未选择用户'
},
batchUserVipDialogTitle() {
return this.batchUserVipMode === 'referrer' ? '开通关闭推荐人特权' : '开通关闭新人特权'
},
batchUserVipMaxOrderDisabled() {
return this.batchUserVipMode === 'referrer'
},
batchUserVipMaxOrderMin() {
return this.batchUserVipMode === 'referrer' ? 0 : 1
}
},
created() {
@@ -597,6 +907,23 @@ export default {
const text = tryDate != null ? String(tryDate).trim() : ''
return text || '-'
},
formatUserPrivilege(user) {
if (!user || Number(user.isVip) !== 1) return '-'
const viptime = user.viptime
if (viptime == null || viptime === '') return '-'
return this.formatOpenDate(viptime)
},
formatApplicantPrivilege(row) {
return this.formatUserPrivilege(row && row.applicantUser)
},
formatReferrerPrivilege(row) {
return this.formatUserPrivilege(row && row.referrerUser)
},
formatReferrerMaxOrder(row) {
const user = row && row.referrerUser
if (!user || user.maxOrder == null || user.maxOrder === '') return '-'
return String(user.maxOrder)
},
formatOpenDate(dateValue) {
const normalized = this.normalizeApplyDateValue(dateValue)
if (!normalized) return '-'
@@ -628,6 +955,224 @@ export default {
})
.finally(() => {
this.loading = false
this.clearPrivilegeRowSelection()
})
},
getPrivilegeRowKey(row, group, rowIndex) {
if (row && row.id != null && row.id !== '') return String(row.id)
return `privilege-tab-${group.key}-${rowIndex}`
},
isPrivilegeRowSelected(key) {
return this.privilegeSelectedKeys.includes(key)
},
setPrivilegeRowSelected(key, checked) {
const selected = new Set(this.privilegeSelectedKeys)
if (checked) selected.add(key)
else selected.delete(key)
this.privilegeSelectedKeys = Array.from(selected)
},
handlePrivilegeSelectAllChange(checked) {
const visibleKeys = this.privilegeTabVisibleRowKeys
if (checked) {
const selected = new Set([...this.privilegeSelectedKeys, ...visibleKeys])
this.privilegeSelectedKeys = Array.from(selected)
return
}
const visibleSet = new Set(visibleKeys)
this.privilegeSelectedKeys = this.privilegeSelectedKeys.filter((key) => !visibleSet.has(key))
},
getPrivilegeRecommenderRowKey(row, group, rowIndex) {
return `recommender:${this.getPrivilegeRowKey(row, group, rowIndex)}`
},
isPrivilegeRecommenderRowSelected(key) {
return this.privilegeRecommenderSelectedKeys.includes(key)
},
setPrivilegeRecommenderRowSelected(key, checked) {
const selected = new Set(this.privilegeRecommenderSelectedKeys)
if (checked) selected.add(key)
else selected.delete(key)
this.privilegeRecommenderSelectedKeys = Array.from(selected)
},
handlePrivilegeRecommenderSelectAllChange(checked) {
const visibleKeys = this.privilegeRecommenderTabVisibleRowKeys
if (checked) {
const selected = new Set([...this.privilegeRecommenderSelectedKeys, ...visibleKeys])
this.privilegeRecommenderSelectedKeys = Array.from(selected)
return
}
const visibleSet = new Set(visibleKeys)
this.privilegeRecommenderSelectedKeys = this.privilegeRecommenderSelectedKeys.filter((key) => !visibleSet.has(key))
},
getGroupApplicantKeys(group) {
if (!group || !Array.isArray(group.rows)) return []
return group.rows.map((row, rowIndex) => this.getPrivilegeRowKey(row, group, rowIndex))
},
getGroupRecommenderKeys(group) {
if (!group || !Array.isArray(group.rows)) return []
return group.rows.map((row, rowIndex) => this.getPrivilegeRecommenderRowKey(row, group, rowIndex))
},
isGroupApplicantAllSelected(group) {
const keys = this.getGroupApplicantKeys(group)
if (!keys.length) return false
return keys.every((key) => this.privilegeSelectedKeys.includes(key))
},
isGroupApplicantIndeterminate(group) {
const keys = this.getGroupApplicantKeys(group)
if (!keys.length) return false
const selectedCount = keys.filter((key) => this.privilegeSelectedKeys.includes(key)).length
return selectedCount > 0 && selectedCount < keys.length
},
handleGroupApplicantSelectChange(group, checked) {
const keys = this.getGroupApplicantKeys(group)
const selected = new Set(this.privilegeSelectedKeys)
keys.forEach((key) => {
if (checked) selected.add(key)
else selected.delete(key)
})
this.privilegeSelectedKeys = Array.from(selected)
},
isGroupRecommenderAllSelected(group) {
const keys = this.getGroupRecommenderKeys(group)
if (!keys.length) return false
return keys.every((key) => this.privilegeRecommenderSelectedKeys.includes(key))
},
isGroupRecommenderIndeterminate(group) {
const keys = this.getGroupRecommenderKeys(group)
if (!keys.length) return false
const selectedCount = keys.filter((key) => this.privilegeRecommenderSelectedKeys.includes(key)).length
return selectedCount > 0 && selectedCount < keys.length
},
handleGroupRecommenderSelectChange(group, checked) {
const keys = this.getGroupRecommenderKeys(group)
const selected = new Set(this.privilegeRecommenderSelectedKeys)
keys.forEach((key) => {
if (checked) selected.add(key)
else selected.delete(key)
})
this.privilegeRecommenderSelectedKeys = Array.from(selected)
},
clearPrivilegeRowSelection() {
this.privilegeSelectedKeys = []
this.privilegeRecommenderSelectedKeys = []
},
extractHxrUserId(user) {
if (!user || typeof user !== 'object') return ''
const id = user.id != null ? user.id : user.userId
return id != null && id !== '' ? String(id) : ''
},
collectSelectedApplicantUserIds() {
const userIds = new Set()
this.groupedList.forEach((group) => {
group.rows.forEach((row, rowIndex) => {
const applicantKey = this.getPrivilegeRowKey(row, group, rowIndex)
if (this.privilegeSelectedKeys.includes(applicantKey)) {
const uid = this.extractHxrUserId(row.applicantUser)
if (uid) userIds.add(uid)
}
})
})
return Array.from(userIds)
},
collectSelectedReferrerUserIds() {
const userIds = new Set()
this.groupedList.forEach((group) => {
group.rows.forEach((row, rowIndex) => {
const recommenderKey = this.getPrivilegeRecommenderRowKey(row, group, rowIndex)
if (this.privilegeRecommenderSelectedKeys.includes(recommenderKey)) {
const uid = this.extractHxrUserId(row.referrerUser)
if (uid) userIds.add(uid)
}
})
})
return Array.from(userIds)
},
openBatchUserVipDialog(mode) {
if (this.activeTab !== 'privilege') return
const isReferrer = mode === 'referrer'
this.batchUserVipMode = isReferrer ? 'referrer' : 'newcomer'
const userIds = isReferrer
? this.collectSelectedReferrerUserIds()
: this.collectSelectedApplicantUserIds()
if (!userIds.length) {
this.$message.warning(
isReferrer ? '请先在表格中勾选推荐人' : '请先在表格中勾选申请人'
)
return
}
this.batchUserVipSelectedUserIds = userIds
this.batchUserVipTenantIdDisplay = this.getTenantIdFromBusinessHeaders()
this.batchUserVipForm.maxOrder = isReferrer ? 0 : 1
if (this.batchUserVipForm.isVip !== 0 && this.batchUserVipForm.isVip !== 1) {
this.batchUserVipForm.isVip = 1
}
this.batchUserVipDialogVisible = true
},
resetBatchUserVipDialog() {
this.batchUserVipSubmitLoading = false
this.batchUserVipMode = 'newcomer'
this.batchUserVipTenantIdDisplay = ''
this.batchUserVipSelectedUserIds = []
this.batchUserVipForm = {
isVip: 1,
vipTime: '',
maxOrder: 1
}
},
handleConfirmBatchUserVip() {
const tenantId = this.getTenantIdFromBusinessHeaders()
if (!tenantId) {
this.$message.error('未获取到租户ID请先登录或配置业务请求头中的租户')
return
}
const userIds = Array.isArray(this.batchUserVipSelectedUserIds)
? this.batchUserVipSelectedUserIds.filter((id) => id)
: []
if (!userIds.length) {
const tip = this.batchUserVipMode === 'referrer'
? '未选择有效用户,请关闭后重新勾选推荐人'
: '未选择有效用户,请关闭后重新勾选申请人'
this.$message.warning(tip)
return
}
const isVip = this.batchUserVipForm.isVip
if (isVip !== 0 && isVip !== 1) {
this.$message.warning('请选择特权状态(开通或关闭)')
return
}
const vipTime = (this.batchUserVipForm.vipTime || '').trim()
if (!vipTime) {
this.$message.warning('请选择 VIP 到期时间')
return
}
const isReferrerMode = this.batchUserVipMode === 'referrer'
const maxOrder = isReferrerMode ? 0 : this.batchUserVipForm.maxOrder
if (!isReferrerMode && (maxOrder == null || maxOrder === '' || Number(maxOrder) < 1)) {
this.$message.warning('请输入有效的 max_order正整数')
return
}
this.batchUserVipSubmitLoading = true
updateHxrAdminUserVipByUserIds({
tenantId,
userIds,
isVip,
vipTime,
maxOrder: Number(maxOrder)
})
.then((res) => {
const ok = res && (res.success === true || res.code === 200 || res.code === 20000)
if (ok) {
this.$message.success((res && res.message) || '操作成功')
this.batchUserVipDialogVisible = false
this.fetchList()
} else {
this.$message.error((res && (res.message || res.msg)) || '操作失败')
}
})
.catch(() => {
// 错误提示由 @/utils/request 响应拦截器统一展示
})
.finally(() => {
this.batchUserVipSubmitLoading = false
})
},
buildListParams() {
@@ -674,6 +1219,27 @@ export default {
const value = headers && headers['X-Tenant-Id']
return value ? String(value).trim() : ''
},
handleSyncFromHxr() {
const tenantId = this.getTenantIdFromBusinessHeaders()
if (!tenantId) {
this.$message.error('未获取到租户ID请先登录或配置业务请求头中的租户')
return
}
this.syncUserLoading = true
syncLbUserFromHxr({ tenantId })
.then((res) => {
const isSuccess = res && (res.success === true || res.code === 200 || res.code === 20000)
if (!isSuccess) {
this.$message.error((res && res.message) || '同步用户失败')
return
}
this.$message.success((res && res.message) || '同步用户成功')
this.fetchList()
})
.finally(() => {
this.syncUserLoading = false
})
},
handleParseFromOrderInfo() {
const text = (this.orderInfo || '').trim()
if (!text) {
@@ -1175,6 +1741,12 @@ export default {
$header-bg: #ffd966;
$date-cell-bg: #fff2cc;
.purchase-apply-tabs {
::v-deep .el-tabs__header {
margin-bottom: 16px;
}
}
.filter-container {
margin-bottom: 20px;
}
@@ -1218,12 +1790,59 @@ export default {
tbody td.date-cell {
background-color: $date-cell-bg;
font-weight: 600;
white-space: nowrap;
white-space: normal;
min-width: 120px;
.date-cell-content {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
}
.date-cell-label {
white-space: nowrap;
}
.date-cell-checks {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 4px;
font-weight: 400;
font-size: 12px;
::v-deep .el-checkbox {
margin-right: 0;
white-space: nowrap;
}
::v-deep .el-checkbox__label {
font-size: 12px;
padding-left: 6px;
}
}
}
.col-actions {
width: 120px;
}
.cell-nickname-check-inner {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 4px;
vertical-align: middle;
::v-deep .el-checkbox {
margin-right: 0;
}
::v-deep .el-checkbox__label {
display: none;
}
}
}
.purchase-apply-sheet-empty {