导出招商催办的用户
This commit is contained in:
@@ -62,3 +62,15 @@ export function getLbDepartmentUserTree(params) {
|
|||||||
params
|
params
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 按租户ID与用户ID列表导出 Excel */
|
||||||
|
export function exportLbDepartmentUsers(tenantId, userIds) {
|
||||||
|
return request({
|
||||||
|
url: '/lbDepartmentUser/exportUsers',
|
||||||
|
method: 'post',
|
||||||
|
params: { tenantId },
|
||||||
|
data: userIds,
|
||||||
|
responseType: 'blob',
|
||||||
|
timeout: 120000
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ export function syncLbOrderRowFromThird(data) {
|
|||||||
return request({
|
return request({
|
||||||
url: '/lbOrderRow/sync-order-from-third',
|
url: '/lbOrderRow/sync-order-from-third',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
|
timeout: 2 * 60 * 1000,
|
||||||
data
|
data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
</el-card>
|
</el-card>
|
||||||
|
|
||||||
<el-card class="table-container" shadow="never">
|
<el-card class="table-container" shadow="never">
|
||||||
<el-table v-loading="loading" :data="list" style="width: 100%">
|
<el-table v-loading="loading" :data="list" border class="assessment-apply-table" style="width: 100%">
|
||||||
<el-table-column label="申请人姓名" prop="applicantName" min-width="108" />
|
<el-table-column label="申请人姓名" prop="applicantName" min-width="108" />
|
||||||
<el-table-column label="申请人电话" prop="applicantPhone" min-width="126" />
|
<el-table-column label="申请人电话" prop="applicantPhone" min-width="126" />
|
||||||
<el-table-column label="同事姓名" prop="colleagueName" min-width="108" />
|
<el-table-column label="同事姓名" prop="colleagueName" min-width="108" />
|
||||||
@@ -301,7 +301,11 @@ export default {
|
|||||||
{ label: '李海民老师', value: '李海民老师' },
|
{ label: '李海民老师', value: '李海民老师' },
|
||||||
{ label: '闫丽娜老师', value: '闫丽娜老师' },
|
{ label: '闫丽娜老师', value: '闫丽娜老师' },
|
||||||
{ label: '张凌峰老师', value: '张凌峰老师' },
|
{ label: '张凌峰老师', value: '张凌峰老师' },
|
||||||
{ label: '张宝凤老师', value: '张宝凤老师' }
|
{ label: '张宝凤老师', value: '张宝凤老师' },
|
||||||
|
{ label: '付韾蕊老师', value: '付韾蕊老师' },
|
||||||
|
{ label: '陈晓琴老师', value: '陈晓琴老师' },
|
||||||
|
{ label: '李军老师', value: '李军老师' },
|
||||||
|
{ label: '刘伟翔老师', value: '刘伟翔老师' }
|
||||||
],
|
],
|
||||||
assessmentTeacherOptions: [
|
assessmentTeacherOptions: [
|
||||||
{ label: '张健老师', value: '张健老师' },
|
{ label: '张健老师', value: '张健老师' },
|
||||||
@@ -774,6 +778,98 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.table-container {
|
.table-container {
|
||||||
|
$table-border: #000;
|
||||||
|
$header-bg: #ffd966;
|
||||||
|
|
||||||
|
::v-deep .assessment-apply-table {
|
||||||
|
border: 1px solid $table-border;
|
||||||
|
color: #000;
|
||||||
|
font-family: 'Microsoft YaHei', 'PingFang SC', sans-serif;
|
||||||
|
|
||||||
|
&::before,
|
||||||
|
&::after {
|
||||||
|
background-color: $table-border;
|
||||||
|
}
|
||||||
|
|
||||||
|
th,
|
||||||
|
td {
|
||||||
|
border-color: $table-border !important;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
thead th {
|
||||||
|
background-color: $header-bg;
|
||||||
|
color: #000 !important;
|
||||||
|
font-weight: 700 !important;
|
||||||
|
padding: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
thead th .cell {
|
||||||
|
color: #000 !important;
|
||||||
|
font-weight: 700 !important;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody td {
|
||||||
|
background-color: #fff;
|
||||||
|
color: #000;
|
||||||
|
padding: 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody td .cell {
|
||||||
|
color: #000;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody tr:hover > td {
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .assessment-apply-table .el-table__fixed-right,
|
||||||
|
::v-deep .assessment-apply-table .el-table__fixed-header-wrapper {
|
||||||
|
thead th {
|
||||||
|
background-color: $header-bg;
|
||||||
|
border-color: $table-border !important;
|
||||||
|
color: #000 !important;
|
||||||
|
font-weight: 700 !important;
|
||||||
|
padding: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
thead th .cell {
|
||||||
|
color: #000 !important;
|
||||||
|
font-weight: 700 !important;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody td {
|
||||||
|
background-color: #fff;
|
||||||
|
border-color: $table-border !important;
|
||||||
|
color: #000;
|
||||||
|
padding: 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody td .cell {
|
||||||
|
color: #000;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody tr:hover > td {
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .assessment-apply-table .el-table__fixed-right-patch {
|
||||||
|
background-color: $header-bg;
|
||||||
|
border-color: $table-border !important;
|
||||||
|
}
|
||||||
|
|
||||||
.el-pagination {
|
.el-pagination {
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
|||||||
@@ -95,6 +95,48 @@
|
|||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
|
||||||
<el-tab-pane label="催办招商" name="urge">
|
<el-tab-pane label="催办招商" name="urge">
|
||||||
|
<el-card class="filter-container" shadow="never">
|
||||||
|
<el-form :model="urgeTreeQueryParams" :inline="true" label-width="100px">
|
||||||
|
<el-form-item label="是否活跃">
|
||||||
|
<el-select
|
||||||
|
v-model="urgeTreeQueryParams.isActive"
|
||||||
|
clearable
|
||||||
|
placeholder="全部"
|
||||||
|
style="width: 140px"
|
||||||
|
>
|
||||||
|
<el-option label="活跃" :value="1" />
|
||||||
|
<el-option label="不活跃" :value="0" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="晚于加入日期">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="urgeTreeQueryParams.joinDate"
|
||||||
|
type="date"
|
||||||
|
clearable
|
||||||
|
value-format="yyyy-MM-dd"
|
||||||
|
placeholder="查询加入日期大于该日期的数据"
|
||||||
|
style="width: 280px"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="晚于最后成交日期">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="urgeTreeQueryParams.lastTradeDate"
|
||||||
|
type="date"
|
||||||
|
clearable
|
||||||
|
value-format="yyyy-MM-dd"
|
||||||
|
placeholder="查询最后成交日期大于该日期的数据"
|
||||||
|
style="width: 280px"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="el-icon-search" :loading="urgeTreeLoading" @click="handleUrgeTreeQuery">
|
||||||
|
查询
|
||||||
|
</el-button>
|
||||||
|
<el-button icon="el-icon-delete" @click="resetUrgeTreeQuery">清空</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
<el-card shadow="never" class="urge-tree-panel">
|
<el-card shadow="never" class="urge-tree-panel">
|
||||||
<div class="urge-tree-toolbar">
|
<div class="urge-tree-toolbar">
|
||||||
<el-button
|
<el-button
|
||||||
@@ -105,6 +147,25 @@
|
|||||||
>
|
>
|
||||||
刷新
|
刷新
|
||||||
</el-button>
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
type="success"
|
||||||
|
icon="el-icon-download"
|
||||||
|
:loading="urgeTreeExportLoading"
|
||||||
|
:disabled="!urgeTreeCheckedUserIds.length"
|
||||||
|
@click="handleUrgeTreeExport"
|
||||||
|
>
|
||||||
|
导出
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
icon="el-icon-close"
|
||||||
|
:disabled="!urgeTreeData.length || !urgeTreeCheckedUserIds.length"
|
||||||
|
@click="handleUrgeTreeUncheckAll"
|
||||||
|
>
|
||||||
|
取消勾选
|
||||||
|
</el-button>
|
||||||
|
<span v-if="urgeTreeCheckedUserIds.length" class="urge-tree-selected-tip">
|
||||||
|
已选 {{ urgeTreeCheckedUserIds.length }} 人
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<el-alert
|
<el-alert
|
||||||
:title="`共 ${urgeTreeTotal} 人`"
|
:title="`共 ${urgeTreeTotal} 人`"
|
||||||
@@ -116,7 +177,7 @@
|
|||||||
<el-input
|
<el-input
|
||||||
v-model="urgeTreeFilterText"
|
v-model="urgeTreeFilterText"
|
||||||
clearable
|
clearable
|
||||||
placeholder="搜索姓名、用户ID、电话、加入日期"
|
placeholder="搜索姓名、用户ID、电话、加入日期、最后成交日期"
|
||||||
prefix-icon="el-icon-search"
|
prefix-icon="el-icon-search"
|
||||||
class="urge-tree-filter"
|
class="urge-tree-filter"
|
||||||
/>
|
/>
|
||||||
@@ -128,13 +189,24 @@
|
|||||||
:props="urgeTreeProps"
|
:props="urgeTreeProps"
|
||||||
:filter-node-method="filterUrgeTreeNode"
|
:filter-node-method="filterUrgeTreeNode"
|
||||||
node-key="userId"
|
node-key="userId"
|
||||||
|
show-checkbox
|
||||||
|
check-strictly
|
||||||
highlight-current
|
highlight-current
|
||||||
|
@check="handleUrgeTreeCheck"
|
||||||
>
|
>
|
||||||
<span slot-scope="{ data }" class="urge-tree-node">
|
<span
|
||||||
|
slot-scope="{ data }"
|
||||||
|
class="urge-tree-node"
|
||||||
|
:class="{
|
||||||
|
'urge-tree-node--alert': isUrgeTreeLeafOverdue(data),
|
||||||
|
'urge-tree-node--checked': isUrgeTreeNodeChecked(data.userId)
|
||||||
|
}"
|
||||||
|
>
|
||||||
<span class="urge-tree-node-name">{{ data.name || '-' }}</span>
|
<span class="urge-tree-node-name">{{ data.name || '-' }}</span>
|
||||||
<span class="urge-tree-node-meta">用户ID:{{ data.userId || '-' }}</span>
|
<span class="urge-tree-node-meta">用户ID:{{ data.userId || '-' }}</span>
|
||||||
<span class="urge-tree-node-meta">电话:{{ data.phone || '-' }}</span>
|
<span class="urge-tree-node-meta">电话:{{ data.phone || '-' }}</span>
|
||||||
<span class="urge-tree-node-meta">加入日期:{{ data.joinDate || '-' }}</span>
|
<span class="urge-tree-node-meta">加入日期:{{ data.joinDate || '-' }}</span>
|
||||||
|
<span class="urge-tree-node-meta">最后成交日期:{{ data.lastTradeDate || '-' }}</span>
|
||||||
</span>
|
</span>
|
||||||
</el-tree>
|
</el-tree>
|
||||||
<el-empty v-else description="暂无数据,请点击刷新加载" />
|
<el-empty v-else description="暂无数据,请点击刷新加载" />
|
||||||
@@ -269,6 +341,7 @@ import {
|
|||||||
addLbDepartmentUser,
|
addLbDepartmentUser,
|
||||||
bindLbDepartmentUserParent,
|
bindLbDepartmentUserParent,
|
||||||
deleteLbDepartmentUser,
|
deleteLbDepartmentUser,
|
||||||
|
exportLbDepartmentUsers,
|
||||||
getLbDepartmentUserList,
|
getLbDepartmentUserList,
|
||||||
getLbDepartmentUserTree,
|
getLbDepartmentUserTree,
|
||||||
syncLbDepartmentUserFromDailyUserTrade,
|
syncLbDepartmentUserFromDailyUserTrade,
|
||||||
@@ -277,6 +350,13 @@ import {
|
|||||||
} from '@/api/lb-department-user'
|
} from '@/api/lb-department-user'
|
||||||
import { getBusinessHeaders } from '@/utils/business-headers'
|
import { getBusinessHeaders } from '@/utils/business-headers'
|
||||||
|
|
||||||
|
function getDefaultUrgeTreeQueryParams() {
|
||||||
|
return {
|
||||||
|
isActive: 1,
|
||||||
|
lastTradeDate: '2026-04-26'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'LbDepartmentUser',
|
name: 'LbDepartmentUser',
|
||||||
data() {
|
data() {
|
||||||
@@ -287,9 +367,13 @@ export default {
|
|||||||
syncLoading: false,
|
syncLoading: false,
|
||||||
syncParentChainLoading: false,
|
syncParentChainLoading: false,
|
||||||
urgeTreeLoading: false,
|
urgeTreeLoading: false,
|
||||||
|
urgeTreeExportLoading: false,
|
||||||
urgeTreeData: [],
|
urgeTreeData: [],
|
||||||
urgeTreeTotal: 0,
|
urgeTreeTotal: 0,
|
||||||
urgeTreeFilterText: '',
|
urgeTreeFilterText: '',
|
||||||
|
urgeTreeCheckedUserIds: [],
|
||||||
|
urgeTreeSyncingChecks: false,
|
||||||
|
urgeTreeQueryParams: getDefaultUrgeTreeQueryParams(),
|
||||||
urgeTreeProps: {
|
urgeTreeProps: {
|
||||||
children: 'children',
|
children: 'children',
|
||||||
label: 'name'
|
label: 'name'
|
||||||
@@ -480,6 +564,28 @@ export default {
|
|||||||
if (!tab || tab.name !== 'urge') return
|
if (!tab || tab.name !== 'urge') return
|
||||||
this.fetchUrgeTree()
|
this.fetchUrgeTree()
|
||||||
},
|
},
|
||||||
|
buildUrgeTreeParams() {
|
||||||
|
const tenantId = this.getTenantIdFromBusinessHeaders()
|
||||||
|
const params = { tenantId }
|
||||||
|
const { isActive, joinDate, lastTradeDate } = this.urgeTreeQueryParams
|
||||||
|
if (isActive === 0 || isActive === 1) {
|
||||||
|
params.is_active = isActive
|
||||||
|
}
|
||||||
|
if (joinDate) {
|
||||||
|
params.join_date = joinDate
|
||||||
|
}
|
||||||
|
if (lastTradeDate) {
|
||||||
|
params.last_trade_date = lastTradeDate
|
||||||
|
}
|
||||||
|
return params
|
||||||
|
},
|
||||||
|
handleUrgeTreeQuery() {
|
||||||
|
this.fetchUrgeTree()
|
||||||
|
},
|
||||||
|
resetUrgeTreeQuery() {
|
||||||
|
this.urgeTreeQueryParams = getDefaultUrgeTreeQueryParams()
|
||||||
|
this.fetchUrgeTree()
|
||||||
|
},
|
||||||
fetchUrgeTree() {
|
fetchUrgeTree() {
|
||||||
const tenantId = this.getTenantIdFromBusinessHeaders()
|
const tenantId = this.getTenantIdFromBusinessHeaders()
|
||||||
if (!tenantId) {
|
if (!tenantId) {
|
||||||
@@ -487,7 +593,7 @@ export default {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.urgeTreeLoading = true
|
this.urgeTreeLoading = true
|
||||||
getLbDepartmentUserTree({ tenantId })
|
getLbDepartmentUserTree(this.buildUrgeTreeParams())
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
const hasData = res && Array.isArray(res.data)
|
const hasData = res && Array.isArray(res.data)
|
||||||
const isSuccess = res && (res.success === true || res.code === 200 || res.code === 20000 || hasData)
|
const isSuccess = res && (res.success === true || res.code === 200 || res.code === 20000 || hasData)
|
||||||
@@ -498,11 +604,7 @@ export default {
|
|||||||
this.urgeTreeData = hasData ? res.data : []
|
this.urgeTreeData = hasData ? res.data : []
|
||||||
this.urgeTreeTotal = typeof res.total === 'number' ? res.total : this.urgeTreeData.length
|
this.urgeTreeTotal = typeof res.total === 'number' ? res.total : this.urgeTreeData.length
|
||||||
this.urgeTreeFilterText = ''
|
this.urgeTreeFilterText = ''
|
||||||
this.$nextTick(() => {
|
this.applyUrgeTreeOverdueChecks()
|
||||||
if (this.$refs.urgeTree) {
|
|
||||||
this.$refs.urgeTree.filter('')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if (res && res.message) {
|
if (res && res.message) {
|
||||||
this.$message.success(res.message)
|
this.$message.success(res.message)
|
||||||
}
|
}
|
||||||
@@ -518,9 +620,117 @@ export default {
|
|||||||
if (!value) return true
|
if (!value) return true
|
||||||
const keyword = String(value).trim().toLowerCase()
|
const keyword = String(value).trim().toLowerCase()
|
||||||
if (!keyword) return true
|
if (!keyword) return true
|
||||||
const fields = [data.name, data.userId, data.phone, data.joinDate]
|
const fields = [data.name, data.userId, data.phone, data.joinDate, data.lastTradeDate]
|
||||||
return fields.some((field) => field != null && String(field).toLowerCase().includes(keyword))
|
return fields.some((field) => field != null && String(field).toLowerCase().includes(keyword))
|
||||||
},
|
},
|
||||||
|
isUrgeTreeLeafNode(data) {
|
||||||
|
return !data.children || !data.children.length
|
||||||
|
},
|
||||||
|
isJoinDateOverOneMonth(joinDate) {
|
||||||
|
if (!joinDate) return false
|
||||||
|
const join = new Date(String(joinDate).replace(/T.*$/, ''))
|
||||||
|
if (Number.isNaN(join.getTime())) return false
|
||||||
|
const oneMonthAgo = new Date()
|
||||||
|
oneMonthAgo.setMonth(oneMonthAgo.getMonth() - 1)
|
||||||
|
oneMonthAgo.setHours(0, 0, 0, 0)
|
||||||
|
join.setHours(0, 0, 0, 0)
|
||||||
|
return join < oneMonthAgo
|
||||||
|
},
|
||||||
|
isUrgeTreeLeafOverdue(data) {
|
||||||
|
return this.isUrgeTreeLeafNode(data) && this.isJoinDateOverOneMonth(data.joinDate)
|
||||||
|
},
|
||||||
|
collectUrgeTreeOverdueLeafUserIds(nodes, result = []) {
|
||||||
|
if (!nodes || !nodes.length) return result
|
||||||
|
nodes.forEach((node) => {
|
||||||
|
if (this.isUrgeTreeLeafOverdue(node) && node.userId) {
|
||||||
|
result.push(node.userId)
|
||||||
|
}
|
||||||
|
if (node.children && node.children.length) {
|
||||||
|
this.collectUrgeTreeOverdueLeafUserIds(node.children, result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return result
|
||||||
|
},
|
||||||
|
normalizeUrgeTreeUserId(userId) {
|
||||||
|
return userId == null || userId === '' ? '' : String(userId)
|
||||||
|
},
|
||||||
|
isUrgeTreeKeyInCheckedList(userId, checkedKeys) {
|
||||||
|
const key = this.normalizeUrgeTreeUserId(userId)
|
||||||
|
if (!key) return false
|
||||||
|
return (checkedKeys || []).some((k) => this.normalizeUrgeTreeUserId(k) === key)
|
||||||
|
},
|
||||||
|
syncUrgeTreeCheckedFromTree() {
|
||||||
|
const tree = this.$refs.urgeTree
|
||||||
|
if (!tree) return
|
||||||
|
this.urgeTreeCheckedUserIds = tree.getCheckedKeys().map((k) => this.normalizeUrgeTreeUserId(k))
|
||||||
|
},
|
||||||
|
applyUrgeTreeOverdueChecks() {
|
||||||
|
const overdueIds = this.collectUrgeTreeOverdueLeafUserIds(this.urgeTreeData)
|
||||||
|
this.urgeTreeSyncingChecks = true
|
||||||
|
this.$nextTick(() => {
|
||||||
|
const tree = this.$refs.urgeTree
|
||||||
|
if (tree) {
|
||||||
|
tree.setCheckedKeys([...new Set(overdueIds)])
|
||||||
|
tree.filter(this.urgeTreeFilterText || '')
|
||||||
|
this.syncUrgeTreeCheckedFromTree()
|
||||||
|
}
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.urgeTreeSyncingChecks = false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
isUrgeTreeNodeChecked(userId) {
|
||||||
|
return this.isUrgeTreeKeyInCheckedList(userId, this.urgeTreeCheckedUserIds)
|
||||||
|
},
|
||||||
|
handleUrgeTreeCheck() {
|
||||||
|
if (this.urgeTreeSyncingChecks) return
|
||||||
|
this.syncUrgeTreeCheckedFromTree()
|
||||||
|
},
|
||||||
|
handleUrgeTreeUncheckAll() {
|
||||||
|
const tree = this.$refs.urgeTree
|
||||||
|
if (!tree) return
|
||||||
|
this.urgeTreeSyncingChecks = true
|
||||||
|
tree.setCheckedKeys([])
|
||||||
|
this.urgeTreeCheckedUserIds = []
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.urgeTreeSyncingChecks = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleUrgeTreeExport() {
|
||||||
|
const tenantId = this.getTenantIdFromBusinessHeaders()
|
||||||
|
if (!tenantId) {
|
||||||
|
this.$message.error('未获取到租户ID,请先登录或配置业务请求头中的租户')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.syncUrgeTreeCheckedFromTree()
|
||||||
|
const userIds = [...new Set(this.urgeTreeCheckedUserIds.filter((id) => id))]
|
||||||
|
if (!userIds.length) {
|
||||||
|
this.$message.warning('请先勾选要导出的用户')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.urgeTreeExportLoading = true
|
||||||
|
exportLbDepartmentUsers(tenantId, userIds)
|
||||||
|
.then((blob) => {
|
||||||
|
const safeBlob = blob instanceof Blob ? blob : new Blob([blob], {
|
||||||
|
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||||
|
})
|
||||||
|
const url = window.URL.createObjectURL(safeBlob)
|
||||||
|
const link = document.createElement('a')
|
||||||
|
link.href = url
|
||||||
|
link.download = `lbDepartmentUser_export_${tenantId}_${Date.now()}.xlsx`
|
||||||
|
document.body.appendChild(link)
|
||||||
|
link.click()
|
||||||
|
document.body.removeChild(link)
|
||||||
|
window.URL.revokeObjectURL(url)
|
||||||
|
this.$message.success('导出成功')
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.$message.error('导出失败')
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.urgeTreeExportLoading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
handleEdit(row) {
|
handleEdit(row) {
|
||||||
this.dialogTitle = '编辑部门人员'
|
this.dialogTitle = '编辑部门人员'
|
||||||
this.isEdit = true
|
this.isEdit = true
|
||||||
@@ -774,6 +984,15 @@ export default {
|
|||||||
.urge-tree-panel {
|
.urge-tree-panel {
|
||||||
.urge-tree-toolbar {
|
.urge-tree-toolbar {
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.urge-tree-selected-tip {
|
||||||
|
color: #606266;
|
||||||
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -867,6 +1086,18 @@ export default {
|
|||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.urge-tree-node--alert,
|
||||||
|
.urge-tree-node--checked {
|
||||||
|
.urge-tree-node-name,
|
||||||
|
.urge-tree-node-meta {
|
||||||
|
color: #f56c6c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.urge-tree-node-name {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.selected-tip {
|
.selected-tip {
|
||||||
float: left;
|
float: left;
|
||||||
color: #606266;
|
color: #606266;
|
||||||
|
|||||||
@@ -70,30 +70,50 @@
|
|||||||
</el-card>
|
</el-card>
|
||||||
|
|
||||||
<el-card class="table-container" shadow="never">
|
<el-card class="table-container" shadow="never">
|
||||||
<el-table v-loading="loading" :data="list" style="width: 100%">
|
<div v-loading="loading" class="purchase-apply-sheet-wrap">
|
||||||
<el-table-column label="申请人" prop="applyUser" min-width="120" />
|
<table v-if="groupedList.length" class="purchase-apply-sheet">
|
||||||
<el-table-column label="申请电话" prop="applyPhone" min-width="130" />
|
<thead>
|
||||||
<el-table-column label="同事姓名" prop="colleagueName" min-width="120" />
|
<tr>
|
||||||
<el-table-column label="同事电话" prop="colleaguePhone" min-width="130" show-overflow-tooltip />
|
<th>日期和人数</th>
|
||||||
<el-table-column label="组长姓名" prop="teamLeader" min-width="120" />
|
<th>序号</th>
|
||||||
<el-table-column label="申请日期" prop="applyDate" min-width="120" />
|
<th>昵称</th>
|
||||||
<el-table-column label="创建时间" prop="createTime" min-width="170">
|
<th>手机号</th>
|
||||||
<template slot-scope="scope">
|
<th>特权开通日期</th>
|
||||||
{{ formatDateTime(scope.row.createTime) }}
|
<th>推荐人</th>
|
||||||
</template>
|
<th>手机号</th>
|
||||||
</el-table-column>
|
<th>领导人</th>
|
||||||
<el-table-column label="更新时间" prop="updateTime" min-width="170">
|
<th>开通日期</th>
|
||||||
<template slot-scope="scope">
|
<th class="col-actions">操作</th>
|
||||||
{{ formatDateTime(scope.row.updateTime) }}
|
</tr>
|
||||||
</template>
|
</thead>
|
||||||
</el-table-column>
|
<tbody>
|
||||||
<el-table-column label="操作" fixed="right" width="140">
|
<template v-for="group in groupedList">
|
||||||
<template slot-scope="scope">
|
<tr v-for="(row, rowIndex) in group.rows" :key="row.id || `${group.key}-${rowIndex}`">
|
||||||
<el-button type="text" @click="handleEdit(scope.row)">编辑</el-button>
|
<td
|
||||||
<el-button type="text" style="color:#F56C6C;" @click="handleDelete(scope.row)">删除</el-button>
|
v-if="rowIndex === 0"
|
||||||
</template>
|
:rowspan="group.rows.length"
|
||||||
</el-table-column>
|
class="date-cell"
|
||||||
</el-table>
|
>
|
||||||
|
{{ 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
|
<el-pagination
|
||||||
:current-page="queryParams.current"
|
:current-page="queryParams.current"
|
||||||
@@ -361,16 +381,52 @@
|
|||||||
</div>
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
<el-dialog title="特权推荐人列表" :visible.sync="privilegeDialogVisible" width="760px" @close="resetPrivilegeDialog">
|
<el-dialog
|
||||||
<el-table v-loading="privilegeLoading" :data="privilegeList" style="width: 100%">
|
title="特权推荐人列表"
|
||||||
<el-table-column type="index" label="#" width="60" />
|
:visible.sync="privilegeDialogVisible"
|
||||||
<el-table-column label="申请人" prop="applyUser" min-width="120" show-overflow-tooltip />
|
width="960px"
|
||||||
<el-table-column label="申请电话" prop="applyPhone" min-width="140" show-overflow-tooltip />
|
custom-class="purchase-privilege-dialog"
|
||||||
<el-table-column label="同事姓名" prop="colleagueName" min-width="120" show-overflow-tooltip />
|
@close="resetPrivilegeDialog"
|
||||||
<el-table-column label="同事电话" prop="colleaguePhone" min-width="140" show-overflow-tooltip />
|
>
|
||||||
<el-table-column label="组长姓名" prop="teamLeader" min-width="120" show-overflow-tooltip />
|
<div v-loading="privilegeLoading" class="purchase-apply-sheet-wrap">
|
||||||
<el-table-column label="申请日期" prop="applyDate" min-width="120" />
|
<table v-if="privilegeGroupedList.length" class="purchase-apply-sheet">
|
||||||
</el-table>
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>日期和人数</th>
|
||||||
|
<th>序号</th>
|
||||||
|
<th>昵称</th>
|
||||||
|
<th>手机号</th>
|
||||||
|
<th>特权开通日期</th>
|
||||||
|
<th>推荐人</th>
|
||||||
|
<th>手机号</th>
|
||||||
|
<th>领导人</th>
|
||||||
|
<th>开通日期</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<template v-for="group in privilegeGroupedList">
|
||||||
|
<tr v-for="(row, rowIndex) in group.rows" :key="row.id || `priv-${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>
|
||||||
|
</tr>
|
||||||
|
</template>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div v-else class="purchase-apply-sheet-empty">暂无数据</div>
|
||||||
|
</div>
|
||||||
<div slot="footer" class="dialog-footer">
|
<div slot="footer" class="dialog-footer">
|
||||||
<el-button @click="privilegeDialogVisible = false">关闭</el-button>
|
<el-button @click="privilegeDialogVisible = false">关闭</el-button>
|
||||||
</div>
|
</div>
|
||||||
@@ -464,6 +520,14 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
groupedList() {
|
||||||
|
return this.buildGroupedList(this.list)
|
||||||
|
},
|
||||||
|
privilegeGroupedList() {
|
||||||
|
return this.buildGroupedList(this.privilegeList)
|
||||||
|
}
|
||||||
|
},
|
||||||
created() {
|
created() {
|
||||||
this.fetchList()
|
this.fetchList()
|
||||||
},
|
},
|
||||||
@@ -475,6 +539,75 @@ export default {
|
|||||||
const day = String(date.getDate()).padStart(2, '0')
|
const day = String(date.getDate()).padStart(2, '0')
|
||||||
return `${year}-${month}-${day}`
|
return `${year}-${month}-${day}`
|
||||||
},
|
},
|
||||||
|
buildGroupedList(records) {
|
||||||
|
if (!Array.isArray(records) || !records.length) return []
|
||||||
|
const sorted = [...records].sort((a, b) => {
|
||||||
|
const da = this.normalizeApplyDateValue(a.applyDate)
|
||||||
|
const db = this.normalizeApplyDateValue(b.applyDate)
|
||||||
|
if (da !== db) return db.localeCompare(da)
|
||||||
|
const idA = a.id != null ? String(a.id) : ''
|
||||||
|
const idB = b.id != null ? String(b.id) : ''
|
||||||
|
return idA.localeCompare(idB)
|
||||||
|
})
|
||||||
|
const map = new Map()
|
||||||
|
sorted.forEach((row) => {
|
||||||
|
const dateKey = this.normalizeApplyDateValue(row.applyDate) || '__no_date__'
|
||||||
|
if (!map.has(dateKey)) {
|
||||||
|
map.set(dateKey, [])
|
||||||
|
}
|
||||||
|
map.get(dateKey).push(row)
|
||||||
|
})
|
||||||
|
return Array.from(map.entries()).map(([dateKey, rows]) => ({
|
||||||
|
key: dateKey,
|
||||||
|
label: this.formatDateGroupLabel(dateKey, rows.length),
|
||||||
|
rows
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
formatDateGroupLabel(applyDate, count) {
|
||||||
|
const displayDate = this.subtractOneDay(applyDate)
|
||||||
|
const md = this.formatShortMd(displayDate, false)
|
||||||
|
const n = typeof count === 'number' && count > 0 ? count : 0
|
||||||
|
return md ? `${md}招商${n}人` : `招商${n}人`
|
||||||
|
},
|
||||||
|
subtractOneDay(dateValue) {
|
||||||
|
const normalized = this.normalizeApplyDateValue(dateValue)
|
||||||
|
if (!normalized) return ''
|
||||||
|
const date = new Date(`${normalized}T12:00:00`)
|
||||||
|
if (Number.isNaN(date.getTime())) return normalized
|
||||||
|
date.setDate(date.getDate() - 1)
|
||||||
|
const year = date.getFullYear()
|
||||||
|
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||||
|
const day = String(date.getDate()).padStart(2, '0')
|
||||||
|
return `${year}-${month}-${day}`
|
||||||
|
},
|
||||||
|
formatShortMd(dateValue, padMonthDay = true) {
|
||||||
|
const normalized = this.normalizeApplyDateValue(dateValue)
|
||||||
|
if (!normalized) return ''
|
||||||
|
const parts = normalized.split('-')
|
||||||
|
if (parts.length < 3) return normalized
|
||||||
|
const month = Number(parts[1])
|
||||||
|
const day = Number(parts[2])
|
||||||
|
if (Number.isNaN(month) || Number.isNaN(day)) return normalized
|
||||||
|
if (padMonthDay) {
|
||||||
|
return `${String(month).padStart(2, '0')}.${String(day).padStart(2, '0')}`
|
||||||
|
}
|
||||||
|
return `${month}.${day}`
|
||||||
|
},
|
||||||
|
formatTryDate(tryDate) {
|
||||||
|
const text = tryDate != null ? String(tryDate).trim() : ''
|
||||||
|
return text || '-'
|
||||||
|
},
|
||||||
|
formatOpenDate(dateValue) {
|
||||||
|
const normalized = this.normalizeApplyDateValue(dateValue)
|
||||||
|
if (!normalized) return '-'
|
||||||
|
const parts = normalized.split('-')
|
||||||
|
if (parts.length < 3) return normalized
|
||||||
|
const year = parts[0]
|
||||||
|
const month = Number(parts[1])
|
||||||
|
const day = Number(parts[2])
|
||||||
|
if (Number.isNaN(month) || Number.isNaN(day)) return normalized
|
||||||
|
return `${year}/${month}/${day}`
|
||||||
|
},
|
||||||
fetchList() {
|
fetchList() {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
getLbPurchaseApplyList(this.buildListParams())
|
getLbPurchaseApplyList(this.buildListParams())
|
||||||
@@ -1038,6 +1171,10 @@ export default {
|
|||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.app-container {
|
.app-container {
|
||||||
|
$table-border: #000;
|
||||||
|
$header-bg: #ffd966;
|
||||||
|
$date-cell-bg: #fff2cc;
|
||||||
|
|
||||||
.filter-container {
|
.filter-container {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
@@ -1047,11 +1184,110 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.table-container {
|
.table-container {
|
||||||
|
.purchase-apply-sheet-wrap {
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.purchase-apply-sheet {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
table-layout: fixed;
|
||||||
|
color: #000;
|
||||||
|
font-family: 'Microsoft YaHei', 'PingFang SC', sans-serif;
|
||||||
|
font-size: 14px;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
th,
|
||||||
|
td {
|
||||||
|
border: 1px solid $table-border;
|
||||||
|
padding: 8px 6px;
|
||||||
|
vertical-align: middle;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
thead th {
|
||||||
|
background-color: $header-bg;
|
||||||
|
font-weight: 700;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody td {
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody td.date-cell {
|
||||||
|
background-color: $date-cell-bg;
|
||||||
|
font-weight: 600;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-actions {
|
||||||
|
width: 120px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.purchase-apply-sheet-empty {
|
||||||
|
padding: 40px 0;
|
||||||
|
text-align: center;
|
||||||
|
color: #909399;
|
||||||
|
border: 1px solid $table-border;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
.el-pagination {
|
.el-pagination {
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.purchase-privilege-dialog {
|
||||||
|
.purchase-apply-sheet-wrap {
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.purchase-apply-sheet {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
table-layout: fixed;
|
||||||
|
color: #000;
|
||||||
|
font-family: 'Microsoft YaHei', 'PingFang SC', sans-serif;
|
||||||
|
font-size: 14px;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
th,
|
||||||
|
td {
|
||||||
|
border: 1px solid #000;
|
||||||
|
padding: 8px 6px;
|
||||||
|
vertical-align: middle;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
thead th {
|
||||||
|
background-color: #ffd966;
|
||||||
|
font-weight: 700;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody td {
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody td.date-cell {
|
||||||
|
background-color: #fff2cc;
|
||||||
|
font-weight: 600;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.purchase-apply-sheet-empty {
|
||||||
|
padding: 40px 0;
|
||||||
|
text-align: center;
|
||||||
|
color: #909399;
|
||||||
|
border: 1px solid #000;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user