更新
This commit is contained in:
+197
-12
@@ -594,9 +594,12 @@
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="r in tb.rows"
|
||||
:key="r.dept_id"
|
||||
:key="`${idx}-${r.dept_id}-${r.dept_name}`"
|
||||
class="yeji-row--action"
|
||||
:class="{ 'yeji-row--idle': r.dept_id <= 0 }"
|
||||
:class="{
|
||||
'yeji-row--unassigned': isUnassignedYejiRow(r),
|
||||
'yeji-row--idle': r.dept_id <= 0 && !isUnassignedYejiRow(r),
|
||||
}"
|
||||
@click="onYejiDeptRowClick(tb, r)"
|
||||
>
|
||||
<td class="col-dept">{{ r.dept_name }}</td>
|
||||
@@ -811,6 +814,50 @@
|
||||
</div>
|
||||
</div>
|
||||
</el-drawer>
|
||||
|
||||
<el-dialog
|
||||
v-model="unassignedDialogVisible"
|
||||
width="min(680px, 94vw)"
|
||||
destroy-on-close
|
||||
class="yeji-unassigned-dialog"
|
||||
>
|
||||
<template #header>
|
||||
<div class="yeji-unassigned-dialog__head">
|
||||
<span class="yeji-unassigned-dialog__title">「未归属中心」业绩拆解</span>
|
||||
<p class="yeji-unassigned-dialog__sub">{{ unassignedDialogSubtitle }}</p>
|
||||
</div>
|
||||
</template>
|
||||
<p v-if="unassignedDialogNote" class="yeji-unassigned-dialog__note">{{ unassignedDialogNote }}</p>
|
||||
<div v-loading="unassignedDialogLoading" class="yeji-unassigned-dialog__body">
|
||||
<el-table
|
||||
v-if="unassignedDialogRows.length > 0 || unassignedDialogLoading"
|
||||
:data="unassignedDialogRows"
|
||||
size="small"
|
||||
stripe
|
||||
border
|
||||
max-height="440"
|
||||
class="yeji-unassigned-dialog__table"
|
||||
:empty-text="unassignedDialogLoading ? '加载中…' : '暂无数据'"
|
||||
>
|
||||
<el-table-column prop="name" label="创建人" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column label="账号" width="88" align="right">
|
||||
<template #default="{ row }">
|
||||
{{ row.admin_id > 0 ? row.admin_id : '—' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="订单数" width="88" align="right">
|
||||
<template #default="{ row }">{{ formatInt(row.order_count) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="业绩金额" min-width="112" align="right">
|
||||
<template #default="{ row }">{{ formatMoney(row.amount) }}</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-empty
|
||||
v-if="!unassignedDialogLoading && unassignedDialogRows.length === 0"
|
||||
description="暂无拆解数据"
|
||||
/>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -820,12 +867,13 @@ import { Search, RefreshRight, InfoFilled } from '@element-plus/icons-vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import vCharts from 'vue-echarts'
|
||||
import {
|
||||
doctorDailyStatsOverview,
|
||||
yejiStatsChannelOptions,
|
||||
yejiStatsDeptOptions,
|
||||
yejiStatsLeaderboard,
|
||||
yejiStatsMulti,
|
||||
yejiStatsOverview,
|
||||
doctorDailyStatsOverview,
|
||||
yejiStatsUnassignedBreakdown,
|
||||
} from '@/api/stats'
|
||||
import { deptPerformanceTargetMonthMatrix } from '@/api/finance'
|
||||
import { prescriptionOrderLists } from '@/api/tcm'
|
||||
@@ -907,6 +955,13 @@ interface ChannelGroup {
|
||||
channels: ChannelOption[]
|
||||
}
|
||||
|
||||
interface YejiUnassignedBreakdownRow {
|
||||
admin_id: number
|
||||
name: string
|
||||
order_count: number
|
||||
amount: number
|
||||
}
|
||||
|
||||
interface YejiRow {
|
||||
dept_id: number
|
||||
dept_name: string
|
||||
@@ -984,6 +1039,12 @@ const tables = ref<YejiTable[]>([])
|
||||
const leaderboardBlock = ref<LeaderboardPack | null>(null)
|
||||
const leaderboardsLoading = ref(false)
|
||||
|
||||
const unassignedDialogVisible = ref(false)
|
||||
const unassignedDialogLoading = ref(false)
|
||||
const unassignedDialogSubtitle = ref('')
|
||||
const unassignedDialogNote = ref('')
|
||||
const unassignedDialogRows = ref<YejiUnassignedBreakdownRow[]>([])
|
||||
|
||||
/** 图表 / 数据表切换 */
|
||||
const yejiDisplayTab = ref<'charts' | 'table'>('table')
|
||||
|
||||
@@ -1934,10 +1995,58 @@ function onOrderDrawerPageSizeChange(size: number) {
|
||||
void fetchOrderDrawerPage()
|
||||
}
|
||||
|
||||
function isUnassignedYejiRow(row: YejiRow): boolean {
|
||||
return row.dept_id <= 0 && row.dept_name === '未归属中心'
|
||||
}
|
||||
|
||||
function buildUnassignedBreakdownSubtitle(tb: YejiTable): string {
|
||||
const parts = [`区间:${tb.start_date} ~ ${tb.end_date}`]
|
||||
if (selectedChannel.value) {
|
||||
parts.push(`渠道:${tb.channel_name || selectedChannel.value}`)
|
||||
}
|
||||
if (selectedDeptIds.value.length > 0) {
|
||||
parts.push(`已选展示部门 ${selectedDeptIds.value.length} 个`)
|
||||
}
|
||||
return parts.join(' · ')
|
||||
}
|
||||
|
||||
async function openUnassignedBreakdown(tb: YejiTable) {
|
||||
unassignedDialogSubtitle.value = buildUnassignedBreakdownSubtitle(tb)
|
||||
unassignedDialogNote.value = ''
|
||||
unassignedDialogRows.value = []
|
||||
unassignedDialogVisible.value = true
|
||||
unassignedDialogLoading.value = true
|
||||
try {
|
||||
const p: Record<string, string> = {
|
||||
start_date: tb.start_date,
|
||||
end_date: tb.end_date,
|
||||
}
|
||||
if (selectedDeptIds.value.length > 0) {
|
||||
p.dept_ids = selectedDeptIds.value.join(',')
|
||||
}
|
||||
if (selectedChannel.value) {
|
||||
p.channel_code = selectedChannel.value
|
||||
}
|
||||
const res: any = await yejiStatsUnassignedBreakdown(p as any)
|
||||
unassignedDialogRows.value = Array.isArray(res?.rows) ? res.rows : []
|
||||
unassignedDialogNote.value = typeof res?.note === 'string' ? res.note : ''
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.msg || e?.message || '加载未归属拆解失败')
|
||||
unassignedDialogRows.value = []
|
||||
unassignedDialogNote.value = ''
|
||||
} finally {
|
||||
unassignedDialogLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 部门业绩行:有子部门 → 仅筛选该节点(后端展开子行);末级 → 侧栏业务订单
|
||||
* 部门业绩行:有子部门 → 仅筛选该节点(后端展开子行);末级 → 侧栏业务订单;「未归属中心」→ 按创建人拆解弹窗
|
||||
*/
|
||||
function onYejiDeptRowClick(tb: YejiTable, row: YejiRow) {
|
||||
if (isUnassignedYejiRow(row)) {
|
||||
void openUnassignedBreakdown(tb)
|
||||
return
|
||||
}
|
||||
if (row.dept_id <= 0) return
|
||||
if (deptHasChildren(row.dept_id)) {
|
||||
selectedDeptIds.value = [row.dept_id]
|
||||
@@ -2312,6 +2421,13 @@ onMounted(async () => {
|
||||
--yj-radius: 16px;
|
||||
--yj-radius-sm: 10px;
|
||||
--yj-font: 'Inter', var(--el-font-family), system-ui, sans-serif;
|
||||
/* 部门列竖条(表头略深、与指标列区分) */
|
||||
--yj-dept-col-bg: #eef2ff;
|
||||
--yj-dept-col-bg-hover: #e0e7ff;
|
||||
--yj-dept-col-head-start: #e0e7ff;
|
||||
--yj-dept-col-head-end: #e8ecff;
|
||||
--yj-dept-col-border: #c7d2fe;
|
||||
--yj-dept-col-head-ink: #3730a3;
|
||||
|
||||
min-height: 100%;
|
||||
padding: 24px;
|
||||
@@ -2911,7 +3027,7 @@ onMounted(async () => {
|
||||
}
|
||||
|
||||
.col-abbr {
|
||||
width: 48px;
|
||||
min-width: 56px;
|
||||
}
|
||||
|
||||
.col-name {
|
||||
@@ -2972,6 +3088,45 @@ onMounted(async () => {
|
||||
font-size: 13px;
|
||||
color: var(--yj-ink);
|
||||
|
||||
thead th.col-dept,
|
||||
thead th.col-abbr {
|
||||
min-width: 88px;
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
var(--yj-dept-col-head-start) 0%,
|
||||
var(--yj-dept-col-head-end) 100%
|
||||
);
|
||||
color: var(--yj-dept-col-head-ink);
|
||||
font-weight: 700;
|
||||
border-right: 2px solid var(--yj-dept-col-border);
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.35);
|
||||
}
|
||||
|
||||
thead th.col-abbr {
|
||||
min-width: 56px;
|
||||
}
|
||||
|
||||
tbody td.col-dept,
|
||||
tbody td.col-abbr {
|
||||
background: var(--yj-dept-col-bg);
|
||||
color: var(--yj-ink);
|
||||
font-weight: 600;
|
||||
border-right: 2px solid var(--yj-dept-col-border);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
tbody tr.yeji-row--action:not(.yeji-row--idle):hover td.col-dept,
|
||||
tbody tr.yeji-row--action:not(.yeji-row--idle):hover td.col-abbr {
|
||||
background: var(--yj-dept-col-bg-hover);
|
||||
}
|
||||
|
||||
.total-row td.col-dept {
|
||||
background: color-mix(in srgb, var(--yj-accent-soft) 55%, var(--yj-dept-col-bg) 45%);
|
||||
color: var(--yj-ink);
|
||||
font-weight: 700;
|
||||
border-right: 2px solid color-mix(in srgb, var(--yj-dept-col-border) 55%, var(--yj-line) 45%);
|
||||
}
|
||||
|
||||
thead th {
|
||||
background: #f1f5f9;
|
||||
color: var(--yj-ink-secondary);
|
||||
@@ -2998,13 +3153,6 @@ onMounted(async () => {
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.col-dept {
|
||||
width: 80px;
|
||||
font-weight: 600;
|
||||
background: var(--yj-surface-muted);
|
||||
color: var(--yj-ink-secondary);
|
||||
}
|
||||
|
||||
tbody tr.yeji-row--action {
|
||||
cursor: pointer;
|
||||
transition: background 0.18s ease;
|
||||
@@ -3013,6 +3161,10 @@ onMounted(async () => {
|
||||
background: var(--yj-brand-soft);
|
||||
}
|
||||
|
||||
&.yeji-row--unassigned {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&.yeji-row--idle {
|
||||
cursor: default;
|
||||
|
||||
@@ -3194,6 +3346,39 @@ onMounted(async () => {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.yeji-unassigned-dialog__head {
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.yeji-unassigned-dialog__title {
|
||||
display: block;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
color: var(--yj-ink, #0f172a);
|
||||
}
|
||||
|
||||
.yeji-unassigned-dialog__sub {
|
||||
margin: 6px 0 0;
|
||||
font-size: 12px;
|
||||
color: var(--yj-ink-secondary, #64748b);
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.yeji-unassigned-dialog__note {
|
||||
margin: 0 0 12px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
line-height: 1.65;
|
||||
color: var(--yj-ink-secondary, #64748b);
|
||||
background: var(--yj-accent-soft, #fff7ed);
|
||||
border-radius: 8px;
|
||||
border: 1px solid color-mix(in srgb, var(--yj-line, #e2e8f0) 80%, transparent);
|
||||
}
|
||||
|
||||
.yeji-unassigned-dialog__body {
|
||||
min-height: 120px;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.date-pill,
|
||||
.filter-btn,
|
||||
|
||||
Reference in New Issue
Block a user