1. 新增模块品类穿透
This commit is contained in:
@@ -21,6 +21,8 @@ export interface ExecuteProcedureParams {
|
||||
* 注意与 axios 的 `params` 配置重名,此处为查询参数字段名
|
||||
*/
|
||||
params?: string
|
||||
/** 存储过程入参 @ztj(如 YDY_AI_GET_SPTPQ 正特价维度) */
|
||||
ztj?: string
|
||||
}
|
||||
|
||||
/** 日报表-当日总销售数据 */
|
||||
|
||||
@@ -56,7 +56,9 @@ const styles = computed(() => {
|
||||
|
||||
return {
|
||||
width,
|
||||
height
|
||||
height,
|
||||
/** 嵌套 flex 時避免高度鏈斷裂導致圖表區變矮、模組下半截空白 */
|
||||
minHeight: height === '100%' ? '100%' : undefined
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -775,7 +775,10 @@ const remainingRouter: AppRouteRecordRaw[] = [
|
||||
hidden: true,
|
||||
canTo: true
|
||||
},
|
||||
component: () => import('@/views/ydoyun/report/lijun/reportpage6/components/categoryCardListComponents.vue')
|
||||
component: () =>
|
||||
import(
|
||||
'@/views/ydoyun/report/lijun/reportpage6/components/categoryCardListComponents.vue'
|
||||
)
|
||||
},
|
||||
{
|
||||
path: 'lijun/supplier-ranking',
|
||||
@@ -786,7 +789,8 @@ const remainingRouter: AppRouteRecordRaw[] = [
|
||||
hidden: true,
|
||||
canTo: true
|
||||
},
|
||||
component: () => import('@/views/ydoyun/report/lijun/reportpage6/components/SupplierRanking.vue')
|
||||
component: () =>
|
||||
import('@/views/ydoyun/report/lijun/reportpage6/components/SupplierRanking.vue')
|
||||
},
|
||||
{
|
||||
path: 'lijun/supplier-performance',
|
||||
@@ -796,7 +800,8 @@ const remainingRouter: AppRouteRecordRaw[] = [
|
||||
hidden: true,
|
||||
canTo: true
|
||||
},
|
||||
component: () => import('@/views/ydoyun/report/lijun/reportpage6/components/SupplierPerformancePage.vue')
|
||||
component: () =>
|
||||
import('@/views/ydoyun/report/lijun/reportpage6/components/SupplierPerformancePage.vue')
|
||||
},
|
||||
{
|
||||
path: 'lijun/middle-class-ranking',
|
||||
@@ -807,7 +812,8 @@ const remainingRouter: AppRouteRecordRaw[] = [
|
||||
hidden: true,
|
||||
canTo: true
|
||||
},
|
||||
component: () => import('@/views/ydoyun/report/lijun/reportpage6/components/MiddleClassRanking.vue')
|
||||
component: () =>
|
||||
import('@/views/ydoyun/report/lijun/reportpage6/components/MiddleClassRanking.vue')
|
||||
},
|
||||
{
|
||||
path: 'lijun/product-cards',
|
||||
@@ -818,7 +824,8 @@ const remainingRouter: AppRouteRecordRaw[] = [
|
||||
hidden: true,
|
||||
canTo: true
|
||||
},
|
||||
component: () => import('@/views/ydoyun/report/lijun/reportpage6/components/ProductCardsPage.vue')
|
||||
component: () =>
|
||||
import('@/views/ydoyun/report/lijun/reportpage6/components/ProductCardsPage.vue')
|
||||
},
|
||||
{
|
||||
path: 'lijun/reportpage6/detail',
|
||||
@@ -841,6 +848,28 @@ const remainingRouter: AppRouteRecordRaw[] = [
|
||||
canTo: true
|
||||
},
|
||||
component: () => import('@/views/ydoyun/report/wangbuliao/productSplb/index.vue')
|
||||
},
|
||||
{
|
||||
path: 'product-daily/top-wall-more',
|
||||
name: 'ProductDailyTopWallMore',
|
||||
meta: {
|
||||
title: 'TOP图片墙 - 更多',
|
||||
noCache: true,
|
||||
hidden: true,
|
||||
canTo: true
|
||||
},
|
||||
component: () => import('@/views/ydoyun/report/productDaily/TopWallMore.vue')
|
||||
},
|
||||
{
|
||||
path: 'sales-daily/category-penetration',
|
||||
name: 'SalesDailyCategoryPenetration',
|
||||
meta: {
|
||||
title: '品类穿透看板',
|
||||
noCache: true,
|
||||
hidden: true,
|
||||
canTo: true
|
||||
},
|
||||
component: () => import('@/views/ydoyun/report/salesdaily/CategoryPenetrationBoard.vue')
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -860,7 +889,8 @@ const remainingRouter: AppRouteRecordRaw[] = [
|
||||
noCache: true,
|
||||
canTo: true
|
||||
},
|
||||
component: () => import(/* webpackChunkName: "custom-tag" */ '@/views/ydoyun/customtag/index.vue')
|
||||
component: () =>
|
||||
import(/* webpackChunkName: "custom-tag" */ '@/views/ydoyun/customtag/index.vue')
|
||||
},
|
||||
{
|
||||
path: 'product-custom-tag',
|
||||
@@ -870,7 +900,10 @@ const remainingRouter: AppRouteRecordRaw[] = [
|
||||
noCache: true,
|
||||
canTo: true
|
||||
},
|
||||
component: () => import(/* webpackChunkName: "product-custom-tag" */ '@/views/ydoyun/productcustomtag/index.vue')
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "product-custom-tag" */ '@/views/ydoyun/productcustomtag/index.vue'
|
||||
)
|
||||
},
|
||||
{
|
||||
path: 'store-custom-tag',
|
||||
@@ -880,7 +913,10 @@ const remainingRouter: AppRouteRecordRaw[] = [
|
||||
noCache: true,
|
||||
canTo: true
|
||||
},
|
||||
component: () => import(/* webpackChunkName: "store-custom-tag" */ '@/views/ydoyun/storecustomtag/index.vue')
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "store-custom-tag" */ '@/views/ydoyun/storecustomtag/index.vue'
|
||||
)
|
||||
},
|
||||
{
|
||||
path: 'supplier-custom-tag',
|
||||
@@ -890,7 +926,10 @@ const remainingRouter: AppRouteRecordRaw[] = [
|
||||
noCache: true,
|
||||
canTo: true
|
||||
},
|
||||
component: () => import(/* webpackChunkName: "supplier-custom-tag" */ '@/views/ydoyun/suppliercustomtag/index.vue')
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "supplier-custom-tag" */ '@/views/ydoyun/suppliercustomtag/index.vue'
|
||||
)
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -207,7 +207,7 @@
|
||||
<div class="value">{{ rawCellValue(summary.ZTJ ?? summary.ztj ?? summary.zjzb) }}</div>
|
||||
</div>
|
||||
|
||||
<!-- 第 3 行:同比业绩、同比毛利率、库存量 -->
|
||||
<!-- 第 3 行:同比业绩、同比毛利率、库存量、周转天 -->
|
||||
<div class="stat-box">
|
||||
<span class="label">同比业绩</span>
|
||||
<div class="value">{{ formatMoney(summary.TBYJ) }}</div>
|
||||
@@ -220,6 +220,10 @@
|
||||
<span class="label">库存量</span>
|
||||
<div class="value">{{ formatNumber(summary.kcl) }}</div>
|
||||
</div>
|
||||
<div class="stat-box">
|
||||
<span class="label">周转天</span>
|
||||
<div class="value">{{ formatNumber(summary.zzt ?? summary.ZZT) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -230,7 +234,7 @@
|
||||
>
|
||||
<div class="card-title trend-title">
|
||||
<span class="trend-title-main">TOP图片墙</span>
|
||||
<div class="trend-title-sub trend-inline-filters">
|
||||
<div class="trend-title-sub trend-inline-filters trend-image-wall-filters">
|
||||
<el-select
|
||||
v-model="imageFilter.storeValues"
|
||||
multiple
|
||||
@@ -293,6 +297,16 @@
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
<el-button
|
||||
v-if="isProductDailyReport"
|
||||
link
|
||||
type="primary"
|
||||
size="small"
|
||||
class="trend-more-link"
|
||||
@click="goTopWallMorePage"
|
||||
>
|
||||
更多
|
||||
</el-button>
|
||||
</div>
|
||||
<div
|
||||
class="trend-chart-wrap trend-product-wrap"
|
||||
@@ -442,12 +456,33 @@
|
||||
<el-dialog
|
||||
v-model="imagePreviewVisible"
|
||||
title="图片详情"
|
||||
width="680px"
|
||||
width="720px"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
class="image-detail-dialog"
|
||||
@closed="resetImagePreviewState"
|
||||
>
|
||||
<div class="image-preview-wrap">
|
||||
<img v-if="currentPreviewImage" :src="currentPreviewImage" class="image-preview-main" alt="图片详情" />
|
||||
<div class="image-preview-body">
|
||||
<div class="image-preview-wrap">
|
||||
<img
|
||||
v-if="currentPreviewImage"
|
||||
:src="currentPreviewImage"
|
||||
class="image-preview-main"
|
||||
alt="商品图片"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="previewImageDetailEntries.length" class="image-preview-detail">
|
||||
<div class="image-preview-detail-title">全部信息</div>
|
||||
<el-descriptions :column="2" border size="small" class="image-preview-descriptions">
|
||||
<el-descriptions-item
|
||||
v-for="ent in previewImageDetailEntries"
|
||||
:key="ent.key"
|
||||
:label="ent.label"
|
||||
>
|
||||
<span class="image-preview-detail-value">{{ ent.value }}</span>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
@@ -457,7 +492,7 @@
|
||||
<script setup lang="ts">
|
||||
import dayjs from 'dayjs'
|
||||
import { computed, nextTick, onActivated, onDeactivated, onMounted, onUnmounted, reactive, ref, watch } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ReportApi, type ExecuteProcedureParams } from '@/api/ydoyun/report/reportpage'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
import { Icon } from '@/components/Icon'
|
||||
@@ -470,6 +505,7 @@ import AiPromptEditDialog from '../lijun/reportpage6/components/AiPromptEditDial
|
||||
defineOptions({ name: 'ProductDaily' })
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const reportScene = computed(() => String((route.meta as any)?.query?.scene || '').trim().toLowerCase())
|
||||
const isProductDailyReport = computed(() => reportScene.value === 'product')
|
||||
/** 与 setPageModuleCode、AiPromptEditDialog 保存逻辑一致:component = moduleKey 冒号前一段 */
|
||||
@@ -593,6 +629,31 @@ const daleiOptions = ref<Array<{ label: string; value: string }>>([])
|
||||
const ztjOptions = ref<Array<{ label: string; value: string }>>([])
|
||||
const zhongleiOptions = ref<Array<{ label: string; value: string }>>([])
|
||||
|
||||
/** TOP 明细页 ckdm:与图片墙「门店」大图筛选一致(cangku 模式用选中门店,否则主表门店) */
|
||||
const topWallMoreCkdmParam = computed(() => {
|
||||
if (imageFilter.activeType === 'cangku' && imageFilter.storeValues.length) {
|
||||
return imageFilter.storeValues.map((v) => String(v).trim()).filter(Boolean).join(',')
|
||||
}
|
||||
return ckdmQueryParam.value
|
||||
})
|
||||
|
||||
/** 跳转 TOP图片墙明细:携带门店 / 日期 / 品牌 / 正特价 */
|
||||
function goTopWallMorePage() {
|
||||
const rqStr = String(queryParams.rq || '').trim()
|
||||
router.push({
|
||||
path: '/reports/product-daily/top-wall-more',
|
||||
query: {
|
||||
fromProductDaily: '1',
|
||||
pp: String(queryParams.brandCode || ''),
|
||||
ckdm: topWallMoreCkdmParam.value,
|
||||
rq: rqStr,
|
||||
rq_s: rqStr,
|
||||
rq_e: rqStr,
|
||||
ztj: String(imageFilter.ztjValue || '').trim()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const routeSelectOptions = computed(() => {
|
||||
const seen = new Set<string>()
|
||||
return userStoreRows.value
|
||||
@@ -895,11 +956,20 @@ const summary = reactive<any>({})
|
||||
const categoryRowsRaw = ref<any[]>([])
|
||||
/** 仓库节点是否展开(默认折叠,点击箭头展开子行) */
|
||||
const warehouseExpanded = ref<Record<string, boolean>>({})
|
||||
const productImageRows = ref<Array<{ spdm: string; pic: string }>>([])
|
||||
/** TOP 图片墙行:含接口完整 raw,便于详情弹窗展示全部字段 */
|
||||
interface ProductImageWallItem {
|
||||
spdm: string
|
||||
pic: string
|
||||
raw: Record<string, any>
|
||||
}
|
||||
|
||||
const productImageRows = ref<ProductImageWallItem[]>([])
|
||||
const topProductImageRows = computed(() => productImageRows.value.slice(0, 10))
|
||||
const imageWallLoading = ref(false)
|
||||
const imagePreviewVisible = ref(false)
|
||||
const currentPreviewImage = ref('')
|
||||
/** 图片详情:与 SPRB2 返回行一致,用于列出全部键值 */
|
||||
const currentPreviewRow = ref<Record<string, any> | null>(null)
|
||||
|
||||
function clearReactiveObject(obj: Record<string, any>) {
|
||||
Object.keys(obj).forEach((k) => delete obj[k])
|
||||
@@ -997,6 +1067,7 @@ async function loadSummary() {
|
||||
if (anySum.PJZ == null && anySum.pjz != null) anySum.PJZ = anySum.pjz
|
||||
if (anySum.zjzb == null && anySum.ZJZB != null) anySum.zjzb = anySum.ZJZB
|
||||
if (anySum.kcl == null && anySum.KCL != null) anySum.kcl = anySum.KCL
|
||||
if (anySum.zzt == null && anySum.ZZT != null) anySum.zzt = anySum.ZZT
|
||||
// 毛利率:YDY_AI_GET_SXRB 可能返回 mll / MLL / ML
|
||||
if (anySum.mll == null && anySum.MLL != null) anySum.mll = anySum.MLL
|
||||
if (anySum.mll == null && anySum.ML != null) anySum.mll = anySum.ML
|
||||
@@ -1178,10 +1249,14 @@ async function loadProductImageList() {
|
||||
p: PROCEDURE_SECRET,
|
||||
username: username.value
|
||||
} satisfies ExecuteProcedureParams)
|
||||
productImageRows.value = extractProcedureList(res).map((row: any) => ({
|
||||
spdm: String(row.spdm ?? row.SPDM ?? '').trim(),
|
||||
pic: normalizeWeatherIconUrl(String(row.pic ?? row.PIC ?? '').trim())
|
||||
}))
|
||||
productImageRows.value = extractProcedureList(res).map((row: any) => {
|
||||
const r = row && typeof row === 'object' ? row : {}
|
||||
return {
|
||||
spdm: String(r.spdm ?? r.SPDM ?? '').trim(),
|
||||
pic: normalizeWeatherIconUrl(String(r.pic ?? r.PIC ?? '').trim()),
|
||||
raw: r as Record<string, any>
|
||||
}
|
||||
})
|
||||
} catch (e) {
|
||||
console.warn('商品图片查询加载失败:', e)
|
||||
productImageRows.value = []
|
||||
@@ -1207,12 +1282,58 @@ function handleImageScopeChange(type: 'cangku' | 'dalei' | 'fjsx4' | 'fjsx1') {
|
||||
handleImageFilterChange()
|
||||
}
|
||||
|
||||
function openImagePreview(item: { pic: string }) {
|
||||
function openImagePreview(item: ProductImageWallItem) {
|
||||
if (!item?.pic) return
|
||||
currentPreviewImage.value = item.pic
|
||||
currentPreviewRow.value = item.raw && typeof item.raw === 'object' ? item.raw : {}
|
||||
imagePreviewVisible.value = true
|
||||
}
|
||||
|
||||
function resetImagePreviewState() {
|
||||
currentPreviewImage.value = ''
|
||||
currentPreviewRow.value = null
|
||||
}
|
||||
|
||||
/** 图片详情弹窗:字段中文标签(未知字段仍显示键名) */
|
||||
function imageDetailFieldLabel(key: string): string {
|
||||
const k = key.toLowerCase()
|
||||
const map: Record<string, string> = {
|
||||
pic: '图片地址',
|
||||
spdmt: '商品代码(拓展)',
|
||||
spdm: '商品代码',
|
||||
spmc: '商品名称',
|
||||
ppmc: '品牌名称',
|
||||
sl: '数量',
|
||||
ckdm: '仓库/门店代码',
|
||||
ckmc: '仓库/门店名称'
|
||||
}
|
||||
return map[k] ?? key
|
||||
}
|
||||
|
||||
function formatImageDetailValue(v: any): string {
|
||||
if (v == null || v === '') return '—'
|
||||
if (typeof v === 'object') {
|
||||
try {
|
||||
return JSON.stringify(v)
|
||||
} catch {
|
||||
return String(v)
|
||||
}
|
||||
}
|
||||
const s = String(v).trim()
|
||||
return s === '' ? '—' : s
|
||||
}
|
||||
|
||||
/** 弹窗中按接口原字段顺序展示全部信息 */
|
||||
const previewImageDetailEntries = computed(() => {
|
||||
const row = currentPreviewRow.value
|
||||
if (!row || typeof row !== 'object') return [] as { key: string; label: string; value: string }[]
|
||||
return Object.keys(row).map((key) => ({
|
||||
key,
|
||||
label: imageDetailFieldLabel(key),
|
||||
value: formatImageDetailValue(row[key])
|
||||
}))
|
||||
})
|
||||
|
||||
// ========= 字段归一/展示 =========
|
||||
function toFiniteNumber(v: any): number {
|
||||
if (v == null || v === '') return NaN
|
||||
@@ -1930,8 +2051,22 @@ function formatAmountSlashQuantity(v: any): string {
|
||||
.trend-title {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
gap: 4px 10px;
|
||||
align-items: center;
|
||||
gap: 6px 10px;
|
||||
}
|
||||
|
||||
/* 中部筛选项占满中间区域,「更多」自然靠最右 */
|
||||
.trend-image-wall-filters {
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.trend-more-link {
|
||||
flex-shrink: 0;
|
||||
margin-left: auto;
|
||||
padding: 0 2px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.trend-title-main {
|
||||
@@ -2087,19 +2222,52 @@ function formatAmountSlashQuantity(v: any): string {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.image-preview-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
max-height: 85vh;
|
||||
}
|
||||
|
||||
.image-preview-wrap {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 260px;
|
||||
min-height: 200px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.image-preview-main {
|
||||
max-width: 100%;
|
||||
max-height: 70vh;
|
||||
max-height: 46vh;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.image-preview-detail {
|
||||
min-height: 0;
|
||||
max-height: 38vh;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.image-preview-detail-title {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: var(--el-text-color-primary);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.image-preview-descriptions {
|
||||
:deep(.el-descriptions__label) {
|
||||
width: 120px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.image-preview-detail-value {
|
||||
word-break: break-all;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.trend-chart-inner {
|
||||
flex: 1;
|
||||
min-height: 160px;
|
||||
|
||||
@@ -256,8 +256,13 @@
|
||||
element-loading-text="加载中..."
|
||||
>
|
||||
<div class="card-title category-title">
|
||||
<span class="category-title-main">品类全维度分析</span>
|
||||
<span class="category-title-sub">实收/标准/正价/特价/对比/目标</span>
|
||||
<div class="category-title-left">
|
||||
<span class="category-title-main">品类全维度分析</span>
|
||||
<span class="category-title-sub">实收/标准/正价/特价/对比/目标</span>
|
||||
</div>
|
||||
<el-button type="primary" link class="category-image-wall-link" @click="goProductImageWall">
|
||||
商品图片墙
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<div class="table-scroll">
|
||||
@@ -293,7 +298,9 @@
|
||||
<tr v-for="(row, idx) in categoryRows" :key="row.PP || idx">
|
||||
<td class="sxrb-td-sticky sxrb-col-brand">
|
||||
<div class="sxrb-brand-cell">
|
||||
<span class="sxrb-brand-name">{{ row.PP || '-' }}</span>
|
||||
<el-button link type="primary" class="sxrb-brand-btn" @click="goCategoryPenetration(row)">
|
||||
<span class="sxrb-brand-name">{{ row.PP || '-' }}</span>
|
||||
</el-button>
|
||||
<span class="sxrb-brand-zb">{{ formatCategoryPpzb(row.PPZB) }}</span>
|
||||
</div>
|
||||
</td>
|
||||
@@ -354,7 +361,7 @@ import dayjs from 'dayjs'
|
||||
import type { EChartsOption } from 'echarts'
|
||||
import { useCssVar } from '@vueuse/core'
|
||||
import { computed, nextTick, onActivated, onDeactivated, onMounted, onUnmounted, reactive, ref, watch } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ReportApi, type ExecuteProcedureParams } from '@/api/ydoyun/report/reportpage'
|
||||
import { getWeatherAggregate } from '@/api/ydoyun/weather'
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
@@ -374,6 +381,69 @@ const SALES_DAILY_COMPONENT = 'SalesDaily'
|
||||
const SALES_DAILY_MODULE_KEY = `${SALES_DAILY_COMPONENT}:main`
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
/** 品类行品牌代号(存储过程 YDY_AI_GET_PLCT1 的 @pp):优先 PPDM */
|
||||
function resolveCategoryRowPpCode(row: Record<string, any>): string {
|
||||
return String(
|
||||
row?.PPDM ??
|
||||
row?.ppdm ??
|
||||
row?.PPBH ??
|
||||
row?.ppbh ??
|
||||
row?.BH ??
|
||||
row?.bh ??
|
||||
row?.CODE ??
|
||||
row?.code ??
|
||||
row?.PP ??
|
||||
row?.pp ??
|
||||
''
|
||||
).trim()
|
||||
}
|
||||
|
||||
/** 跳转品类全维度穿透看板 */
|
||||
function goCategoryPenetration(row: Record<string, any>) {
|
||||
const pp = resolveCategoryRowPpCode(row)
|
||||
if (!pp) {
|
||||
ElMessage.warning('该行缺少品牌代号,无法穿透')
|
||||
return
|
||||
}
|
||||
if (!String(queryParams.ckdm || '').trim() || !String(queryParams.rq || '').trim()) {
|
||||
ElMessage.warning('请先选择门店与日期并完成查询')
|
||||
return
|
||||
}
|
||||
router.push({
|
||||
path: '/reports/sales-daily/category-penetration',
|
||||
query: {
|
||||
rq: String(queryParams.rq).trim(),
|
||||
ckdm: String(queryParams.ckdm).trim(),
|
||||
pp,
|
||||
ppmc: String(row.PP ?? row.pp ?? '').trim()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/** 跳转商品图片墙明细(与商品日报「更多」同源页,携带门店/日期) */
|
||||
function goProductImageWall() {
|
||||
if (!String(queryParams.ckdm || '').trim()) {
|
||||
ElMessage.warning('请先选择门店')
|
||||
return
|
||||
}
|
||||
if (!String(queryParams.rq || '').trim()) {
|
||||
ElMessage.warning('请先选择日期')
|
||||
return
|
||||
}
|
||||
const rqStr = String(queryParams.rq).trim()
|
||||
router.push({
|
||||
path: '/reports/product-daily/top-wall-more',
|
||||
query: {
|
||||
fromSalesDaily: '1',
|
||||
ckdm: String(queryParams.ckdm).trim(),
|
||||
rq: rqStr,
|
||||
rq_s: rqStr,
|
||||
rq_e: rqStr
|
||||
}
|
||||
})
|
||||
}
|
||||
/** 每日汇报模块名称:与后台菜单名称一致(meta.title),缺省为「销售日报」 */
|
||||
const aiMenuModuleName = computed(() => {
|
||||
const t = route.meta?.title
|
||||
@@ -688,8 +758,18 @@ watch(weatherPrimaryStackRef, () => nextTick(bindWeatherPrimaryStackResizeObserv
|
||||
flush: 'post'
|
||||
})
|
||||
|
||||
/** 與 onMounted 一致:從 keep-alive 再次進入時重拉報表(避免分頁切回仍為舊數據);首次掛載已由 onMounted 查詢,此處跳過一次 */
|
||||
let skipSalesDailyActivateRefetchOnce = true
|
||||
|
||||
onActivated(() => {
|
||||
setAiAssistantFabEnabled(true)
|
||||
if (skipSalesDailyActivateRefetchOnce) {
|
||||
skipSalesDailyActivateRefetchOnce = false
|
||||
return
|
||||
}
|
||||
if (queryParams.ckdm && queryParams.rq) {
|
||||
void handleQuery()
|
||||
}
|
||||
})
|
||||
|
||||
onDeactivated(() => {
|
||||
@@ -1901,10 +1981,25 @@ const timeSlotBarChartOptions = computed<EChartsOption>(() => {
|
||||
}
|
||||
|
||||
.category-title {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.category-title-left {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
gap: 4px 10px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.category-image-wall-link {
|
||||
flex-shrink: 0;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.category-title-main {
|
||||
@@ -2028,9 +2123,24 @@ const timeSlotBarChartOptions = computed<EChartsOption>(() => {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.sxrb-brand-btn {
|
||||
max-width: 100%;
|
||||
min-width: 0;
|
||||
flex: 1 1 auto;
|
||||
padding: 0 !important;
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
.sxrb-brand-btn :deep(.el-button__text) {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.sxrb-brand-name {
|
||||
font-weight: 500;
|
||||
color: var(--el-text-color-primary);
|
||||
color: inherit;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
Reference in New Issue
Block a user