提交 374dda14 authored 作者: lidongxu's avatar lidongxu

feat(displya_schedule): 新增:小卤链路中心_档期陈列_零食计划新增 2 个表格

上级 8066864f
......@@ -48,6 +48,7 @@
"devDependencies": {
"@vant/auto-import-resolver": "^1.3.0",
"@vitejs/plugin-vue": "5.0.5",
"@vitejs/plugin-vue-jsx": "^5.1.1",
"cz-conventional-changelog": "^3.3.0",
"postcss-pxtorem": "^6.1.0",
"sass": "1.77.5",
......
......@@ -19,7 +19,10 @@ export function submitDisplayPlan(data) {
url: `/operation/sales/ap_display/core/${data.id}`,
method: 'PUT',
data: {
display: data
display: {
...data,
id: undefined // 不携带 id
}
}
});
}
......@@ -47,3 +50,19 @@ export function submitDisplaySchedulePlan(data) {
}
});
}
// 获取-档期陈列列表
export function getDisplayScheduleDetail(params) {
return request({
url: '/operation/sales/ap_display/query/pro_page',
params
})
}
// 获取-零食计划列表
export function getSnackPlanList(params) {
return request({
url: '/operation/sales/ap_display/query/snack_page',
params
})
}
......@@ -126,7 +126,7 @@ aside {
// 全局样式
// 容器
.app-container {
padding: 10px;
padding: 20px;
height: 100%;
// 内容区域
......@@ -134,7 +134,7 @@ aside {
width: 100%;
height: 100%;
background-color: var(--el-bg-color-overlay);
padding: 10px;
padding: 20px;
display: flex;
flex-direction: column;
}
......
<template>
<el-row>
<el-form-item>
<el-radio-group v-model="operation"
@change="checkTableColumns">
<el-radio-button label="平铺模式"
value="平铺模式" />
<el-radio-button label="填报模式"
value="填报模式" />
</el-radio-group>
</el-form-item>
<right-toolbar v-model:showSearch="showSearch"
@queryTable="getTableList()"
:columns="chooseColumns"
:showColumnsType="operation === '平铺模式' ? 'tree' : 'checkbox'"
:defaultCheckedKeys="visibleProps">
</right-toolbar>
</el-row>
<!-- 筛选列组的按钮 -->
<el-table :data="tableData"
border
ref="tableRef"
class="auto-fit-header-table"
v-loading="isLoading">
<template v-for="col in tableColumns">
<el-table-column v-if="col.visible"
:label="col.label"
:key="col.prop"
align="center"
:show-overflow-tooltip="col.type === 'string'"
class-name="column-style"
:width="col.width"
:fixed="operation === '填报模式' && col.fixed">
<template #header="{ column }">
<!-- 只为特定公式列添加问号图标 -->
<span class="formula-column">
{{ column.label }}
<el-tooltip v-if="col.type === 'formula'"
:content="col.formulaStr"
placement="top">
<el-icon><question-filled /></el-icon>
</el-tooltip>
</span>
</template>
<template #default="{ row }">
<!-- 填报模式 -->
<div v-if="operation === '填报模式'" style="overflow: visible !important;">
<!-- 带选择框/输入框的操作单元格 -->
<div v-if="col.render"
class="cell-style">
<component :is="col.render(h, row, col)" />
</div>
<!-- 正常显示/公式计算 -->
<div v-else-if="col.type === 'formula'">
{{ col.func(row) }}
</div>
<div v-else>
{{ row[col.prop] || '-' }}
</div>
</div>
<!-- 平铺模式 -->
<div v-else>{{ row[col.prop] || '-' }}</div>
</template>
</el-table-column>
</template>
</el-table>
<!-- 分页 -->
<pagination :total="total"
v-model:page="params.pageNum"
v-model:limit="params.pageSize"
@pagination="getTableList" />
</template>
<script setup>
import { h } from 'vue'
/*************** 操作类型 ***************/
const operation = ref('平铺模式');
// const operation = ref('填报模式');
const tableRef = ref(null)
const props = defineProps({
tableColumns: { // 表格列
type: Array,
default: () => []
},
chooseColumns: { // 右上角工具选择列
type: Array,
default: () => []
},
visibleProps: { // 右上角工具为树结构,默认勾选的列
type: Array,
default: () => []
},
tableData: { // 数据源
type: Array,
default: () => []
},
isLoading: {
type: Boolean,
default: false
},
total: {
type: Number,
default: 0
},
params: {
type: Object,
default: () => ({
pageNum: 1,
pageSize: 10,
})
}
})
const emit = defineEmits(['updateColumns', 'getTableList'])
// 右上角工具
const showSearch = ref(true)
// 切换平铺/填报模式
const checkTableColumns = async () => {
// 通知外面传入 tableColumns / chooseColumns 数据源
await emit('updateColumns', operation.value)
// 强制表格立即应用所有宽度设置,避免先自适应再调整
nextTick(() => {
if (tableRef.value) {
tableRef.value.doLayout()
}
})
}
onMounted(() => {
checkTableColumns()
})
const getTableList = () => {
emit('getTableList')
}
</script>
<style scoped
lang="scss">
.container {
.el-tabs {
height: 100%;
display: flex;
flex-direction: column-reverse;
.el-tabs__content {
display: flex;
flex-direction: column;
height: 100%;
.el-tab-pane {
height: 100%;
display: flex;
flex-direction: column;
.pagination-container {
margin: 10px;
}
}
.formula-column {
display: flex;
justify-content: center;
align-items: center;
.el-icon {
margin-left: 2px;
}
}
}
}
.el-form-item {
align-items: center;
}
/* 表格区域 */
.auto-fit-header-table {
/* 优化超长文本的显示效果 */
.cell {
/* padding: 0 .2133rem; */
>div {
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
::v-deep(.el-table__row) {
.el-table__cell {
padding: 0;
}
}
::v-deep(.column-style) {
.cell {
/* overflow: visible; */
>div {
/* overflow: visible !important; */
}
.cell-style {
margin: 0 -12px;
>div {
display: flex;
flex-direction: column;
align-items: flex-start;
>span {
text-align: left;
text-indent: 5px;
display: inline-block;
width: 100%;
background-color: #e1e2e6;
border-bottom: 1px solid #ebeef5;
}
}
/* 表格内下拉框 */
.el-select {
width: 100% !important;
padding: 10px;
}
.el-input {
padding: 10px;
}
.date-picker {
width: 100%;
padding: 10px;
.el-input {
width: 100%;
padding: 0;
}
}
}
}
}
}
}
/* 特殊动态渲染单元格样式 */
.render-cell {
p {
margin: 0;
}
}
</style>
\ No newline at end of file
......@@ -12,6 +12,14 @@
name="档期计划">
<Schedule />
</el-tab-pane>
<el-tab-pane label="档期陈列"
name="档期陈列">
<ScheduleDis />
</el-tab-pane>
<el-tab-pane label="零食计划"
name="零食计划">
<Snack />
</el-tab-pane>
</el-tabs>
</div>
</div>
......@@ -20,7 +28,9 @@
<script setup>
import Display from './tabs/display.vue'
import Schedule from './tabs/schedule.vue'
const activeName = ref('陈列计划');
import ScheduleDis from './tabs/schedule_dis.vue'
import Snack from './tabs/snack.vue'
const activeName = ref('档期陈列');
const handleClickTabs = (tab) => {
activeName.value = tab.name;
}
......
<template>
<el-row>
<el-form-item>
<el-radio-group v-model="operation"
@change="checkTableColumns">
<el-radio-button label="平铺模式"
value="平铺模式" />
<el-radio-button label="填报模式"
value="填报模式" />
</el-radio-group>
</el-form-item>
<right-toolbar v-model:showSearch="showSearch"
@queryTable="getTableList()"
:columns="chooseColumns"
:showColumnsType="operation === '平铺模式' ? 'tree' : 'checkbox'"
:defaultCheckedKeys="visibleProps">
</right-toolbar>
</el-row>
<!-- 筛选列组的按钮 -->
<el-table :data="tableData"
border
ref="tableRef"
class="auto-fit-header-table"
v-loading="isLoading">
<template v-for="col in tableColumns">
<el-table-column v-if="col.visible"
:label="col.label"
:key="col.prop"
align="center"
:show-overflow-tooltip="col.type === 'string'"
class-name="column-style"
:width="getColumnMinWidth(col)"
:fixed="operation === '填报模式' && col.fixed">
<template #header="{ column }">
<!-- 只为特定列添加问号图标 -->
<span class="formula-column">
{{ column.label }}
<el-tooltip v-if="col.type === 'formula'"
:content="col.formulaStr"
placement="top">
<el-icon><question-filled /></el-icon>
</el-tooltip>
</span>
</template>
<template #default="{ row }">
<div v-if="col.type === 'select' && operation === '填报模式'"
class="cell-style">
<!-- 实际主货架-数量(要根据实际主货架-形式不为空时才可以选择否则为 0) -->
<div v-if="col.prop === 'actualMainShelfQty'">
<span>{{ row[col.referenceKey] || '-' }}</span>
<el-select :disabled="!row.actualMainShelfType || !row.plannedMainShelfQty"
v-model="row[col.prop]"
placeholder=""
clearable
@change="submitChange(row, col)">
<el-option v-for="item in col.options"
:key="item.value"
:label="item.label"
:value="item.value" />
</el-select>
</div>
<div v-else>
<span>{{ row[col.referenceKey] || '-' }}</span>
<el-select v-model="row[col.prop]"
:disabled="!row[col.referenceKey]"
placeholder=""
clearable
@change="submitChange(row, col)">
<el-option v-for="item in col.options"
:key="item.value"
:label="item.label"
:value="item.value" />
</el-select>
</div>
</div>
<!-- 公式计算 -->
<div v-else-if="col.type === 'formula'">
{{ col.func(row) }}
</div>
<!-- 输入框 -->
<div v-else-if="col.type === 'input' && operation === '填报模式'"
class="cell-style">
<div>
<span>{{ row[col.referenceKey] || '-' }}</span>
<el-input v-model="row[col.prop]"
:disabled="!row[col.referenceKey]"
placeholder=""
@input="submitChange(row, col)" />
</div>
</div>
<!-- 为其他类型或未定义类型提供默认显示 -->
<div v-else>
<div class="ellipsis-text"
style="max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
{{ formatterTableData(row, col) }}
</div>
</div>
</template>
</el-table-column>
</template>
</el-table>
<!-- 分页 -->
<pagination :total="total"
v-model:page="params.pageNum"
v-model:limit="params.pageSize"
@pagination="getTableList" />
<CommonPlan :tableData="tableData"
:isLoading="isLoading"
:total="total"
:tableColumns="tableColumns"
:chooseColumns="chooseColumns"
:visibleProps="visibleProps"
:params="params"
@updateColumns="updateColumns"
@getTableList="getTableList" />
</template>
<script setup>
import { getDisplayList, submitDisplayPlan } from '@/api'
import { parseTime } from '@/utils'
import { onMounted } from 'vue';
<script setup
lang="jsx">
import CommonPlan from '@/views/promotion/components/CommonPlan'
import { getDisplayList, submitDisplayPlan } from '@/api'
import { parseTime } from '@/utils'
import { onMounted } from 'vue';
/*************** 操作类型 ***************/
/*************** 操作类型 ***************/
const operation = ref('平铺模式');
const tableRef = ref(null)
// 全部列
const baseColumns = ref([
// 全部列
const baseColumns = ref([
// 一级类
{
label: "基础信息", // 类型标题
......@@ -125,194 +30,251 @@ const baseColumns = ref([
prop: 'salesMonth', // 列属性
visible: true, // 是否显示
type: 'string', // 列类型
fill: false, // 是否为填报列
fixed: 'left' // 是否固定在左侧
fill: true, // 是否为填报列
fixed: 'left', // 是否固定在左侧
width: 90
},
{
label: "销售大区",
prop: "regionName",
visible: true,
type: 'string',
fill: false
fill: false,
width: 90
},
{
label: "销售战区",
prop: "districtName",
visible: true,
type: 'string',
fill: false
fill: false,
width: 90
},
{
label: "经销商-省份",
prop: "dealerProvince",
visible: true,
type: 'string',
fill: false
fill: false,
width: 105
},
{
label: "经销商-城市",
prop: "dealerCity",
visible: true,
type: 'string',
fill: false
fill: false,
width: 105
},
{
label: "经销商-代码",
prop: "dealerCode",
visible: true,
type: 'string',
fill: false
fill: false,
width: 105
},
{
label: "经销商-类型",
prop: "dealerType",
visible: true,
type: 'string',
fill: false
fill: false,
width: 145
},
{
label: "开户日期",
prop: "openingDate",
visible: true,
type: 'string',
fill: false
fill: false,
width: 105
},
{
label: "闭户日期",
prop: "closingDate",
visible: true,
type: 'string',
fill: false
fill: false,
width: 105
},
{
label: "大区总监",
prop: "regionManager",
visible: true,
type: 'string',
fill: false
fill: false,
width: 90
},
{
label: "战区经理",
prop: "districtManager",
visible: true,
type: 'string',
fill: false
fill: false,
width: 90
},
{
label: "城市经理",
prop: "cityManager",
visible: true,
type: 'string',
fill: false
fill: false,
width: 90
},
{
label: "门店名称",
prop: "storeName",
prop: "storeNameCodeDealerName", // 新增动态列(只在填报列显示)
visible: true,
type: 'string',
fill: true,
fixed: 'left'
onlyFill: true,// 仅仅在填报模式下生效
fixed: 'left',
width: 215,
render: (h, row) => {
return h('div', {
class: 'render-cell',
style: {
alignItems: 'center',
whiteSpace: 'pre-line'
}
}, [
h('p', row.storeName),
h('p', row.storeCode),
h('p', {
style: {
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
maxWidth: '100%'
}
}, row.dealerName)
])
}
},
{
label: "门店名称",
prop: "storeName",
visible: true,
type: 'string',
fill: false,
fixed: 'left',
width: 175
},
{
label: "门店编码",
prop: "storeCode",
visible: true,
type: 'string',
fill: true,
fixed: 'left'
fill: false,
fixed: 'left',
width: 105
},
{
label: "经销商名称",
prop: "dealerName",
visible: true,
type: 'string',
fill: true,
fixed: 'left'
fill: false,
fixed: 'left',
width: 215
},
{
label: "系统类型",
prop: "lineType",
visible: true,
type: 'string',
fill: true,
fixed: 'left'
fill: false,
fixed: 'left',
width: 90
},
{
label: "门店-省份",
prop: "storeProvince",
visible: true,
type: 'string',
fill: false
fill: false,
width: 90
},
{
label: "门店-城市",
prop: "storeCity",
visible: true,
type: 'string',
fill: false
fill: false,
width: 90
},
{
label: "系统名称",
prop: "lineName",
visible: true,
type: 'string',
fill: true
fill: false,
width: 90
},
{
label: "渠道大类",
prop: "channelDl",
visible: true,
type: 'string',
fill: false
fill: false,
width: 90
},
{
label: "渠道小类",
prop: "channelXl",
visible: true,
type: 'string',
fill: false
fill: false,
width: 140
},
{
label: "门店类型",
prop: "storeType",
visible: true,
type: 'string',
fill: false
fill: false,
width: 90
},
{
label: "系统业态",
prop: "systemFormat",
visible: true,
type: 'string',
fill: false
fill: false,
width: 90
},
{
label: "门店面积",
prop: "storeArea",
visible: true,
type: 'string',
fill: false
fill: false,
width: 90
},
{
label: "门店分级(销量坎级)",
prop: "storeLevel",
visible: true,
type: 'string',
fill: false
fill: false,
width: 165
},
{
label: "门店地址",
prop: "storeAddress",
visible: true,
type: 'string',
fill: false
fill: false,
width: 255
},
{
label: "品项数",
prop: "productCount",
visible: true,
type: 'string',
fill: false
fill: false,
width: 80
}
],
prop: 'baseColumns',
......@@ -326,14 +288,16 @@ const baseColumns = ref([
prop: "lfSalesModel",
visible: true,
type: 'string',
fill: false
fill: false,
width: 155
},
{
label: "大业态测试-月均POS",
prop: "lfMonthlyPos",
visible: true,
type: 'string',
fill: false
fill: false,
width: 155
}
],
prop: 'lfColumns',
......@@ -348,28 +312,32 @@ const baseColumns = ref([
prop: "plannedMainShelfType",
visible: true,
type: 'string',
fill: false
fill: false,
width: 130
},
{
label: "计划主货架-数量",
prop: "plannedMainShelfQty",
visible: true,
type: 'string',
fill: false
fill: false,
width: 130
},
{
label: "计划主货架-单个费用",
prop: "plannedMainShelfUnitCost",
visible: true,
type: 'string',
fill: false
fill: false,
width: 155
},
{
label: "计划主货架-总费用",
prop: "plannedMainShelfTotalCost",
visible: true,
type: 'string',
fill: false
fill: false,
width: 145
},
{
label: "实际主货架-形式",
......@@ -384,7 +352,33 @@ const baseColumns = ref([
{ label: '8纵及以上', value: '8纵及以上' }
],
referenceKey: "plannedMainShelfType",
fill: true
fill: true,
width: 140,
render: (_, row, col) => {
return (
<div>
<span>{row[col.referenceKey] || '-'}</span>
<el-select modelValue={row[col.prop]}
onUpdate:modelValue={(value) => {
row[col.prop] = value;
submitChange(row, col);
}}
disabled={!row[col.referenceKey]}
placeholder=""
clearable>
{col.options.map(item => (
<el-option
key={item.value}
label={item.label}
value={item.value}
/>
))}
</el-select>
</div>
)
},
// 请求时需要额外携带影响的列字段值
requestKey: ["actualMainShelfQty", "actualMainShelfExecuted", "regularDisplayExecuted"]
},
{
label: "实际主货架-数量",
......@@ -392,11 +386,36 @@ const baseColumns = ref([
visible: true,
type: 'select',
options: [
{ label: '0', value: 0 },
{ label: '1', value: 1 },
{ label: '2', value: 2 }
],
referenceKey: "plannedMainShelfQty",
fill: true
fill: true,
width: 130,
render: (_, row, col) => {
return (
<div>
<span>{row[col.referenceKey] || '-'}</span>
<el-select modelValue={row[col.prop]}
onUpdate:modelValue={(value) => {
row[col.prop] = value;
submitChange(row, col);
}}
disabled={!row[col.referenceKey]}
placeholder="">
{col.options.map(item => (
<el-option
key={item.value}
label={item.label}
value={item.value}
/>
))}
</el-select>
</div>
)
},
requestKey: ["actualMainShelfExecuted", "regularDisplayExecuted"]
},
{
label: "实际主货架-是否执行",
......@@ -408,25 +427,32 @@ const baseColumns = ref([
return row.actualMainShelfExecuted;
},
formulaStr: '公式:(实际主货架形式 >= 计划主货架形式)并且(实际主货架数量 >= 计划主货架数量)',
fill: true
fill: true,
width: 170
},
{
label: "计划端架-数量", prop: "plannedEndCapQty", visible: true, type: 'string',
fill: false
label: "计划端架-数量",
prop: "plannedEndCapQty",
visible: true,
type: 'string',
fill: false,
width: 115
},
{
label: "计划端架-总费用",
prop: "plannedEndCapTotalCost",
visible: true,
type: 'string',
fill: false
fill: false,
width: 130
},
{
label: "计划端架-单个费用",
prop: "plannedEndCapUnitCost",
visible: true,
type: 'string',
fill: false
fill: false,
width: 140
},
{
label: "实际端架-数量",
......@@ -440,7 +466,32 @@ const baseColumns = ref([
{ label: '2', value: 2 }
],
referenceKey: "plannedEndCapQty",
fill: true
fill: true,
width: 115,
render: (_, row, col) => {
return (
<div>
<span>{row[col.referenceKey] || '-'}</span>
<el-select modelValue={row[col.prop]}
onUpdate:modelValue={(value) => {
row[col.prop] = value;
submitChange(row, col);
}}
disabled={!row[col.referenceKey]}
placeholder="">
{col.options.map(item => (
<el-option
key={item.value}
label={item.label}
value={item.value}
/>
))}
</el-select>
</div>
)
},
// 请求时需要额外携带影响的列字段值
requestKey: ["actualEndCapExecuted", "regularDisplayExecuted"]
},
{
label: "实际端架-是否执行",
......@@ -452,42 +503,48 @@ const baseColumns = ref([
return row.actualEndCapExecuted;
},
formulaStr: '公式:(实际端架数量 >= 计划端架数量)',
fill: true
fill: true,
width: 155
},
{
label: "计划地堆-平米数(㎡)",
prop: "plannedFloorStackArea",
visible: true,
type: 'string',
fill: true
fill: false,
width: 170
},
{
label: "计划地堆-数量",
prop: "plannedFloorStackQty",
visible: true,
type: 'string',
fill: true
fill: false,
width: 115
},
{
label: "计划主题地堆-是否",
prop: "plannedThemedFloorStack",
visible: true,
visible: false,
type: 'string',
fill: true
fill: true,
width: 140
},
{
label: "计划地堆-总费用",
prop: "plannedFloorStackTotalCost",
visible: true,
type: 'string',
fill: false
fill: false,
width: 130
},
{
label: "计划折算1㎡-单个费用",
prop: "plannedFloorStackUnitCostPerSqm",
visible: true,
type: 'string',
fill: false
fill: false,
width: 160
},
{
label: "实际地堆-平米数(㎡)",
......@@ -504,7 +561,32 @@ const baseColumns = ref([
{ label: '4', value: 4 }
],
referenceKey: "plannedFloorStackArea",
fill: true
fill: true,
width: 170,
render: (_, row, col) => {
return (
<div>
<span>{row[col.referenceKey] || '-'}</span>
<el-select modelValue={row[col.prop]}
onUpdate:modelValue={(value) => {
row[col.prop] = value;
submitChange(row, col);
}}
disabled={!row[col.referenceKey]}
placeholder="">
{col.options.map(item => (
<el-option
key={item.value}
label={item.label}
value={item.value}
/>
))}
</el-select>
</div>
)
},
// 请求时需要额外携带影响的列字段值
requestKey: ["actualFloorStackExecuted", "regularDisplayExecuted"]
},
{
label: "实际地堆-数量",
......@@ -518,7 +600,32 @@ const baseColumns = ref([
{ label: '3', value: 3 }
],
referenceKey: "plannedFloorStackQty",
fill: true
fill: true,
width: 115,
render: (_, row, col) => {
return (
<div>
<span>{row[col.referenceKey] || '-'}</span>
<el-select modelValue={row[col.prop]}
onUpdate:modelValue={(value) => {
row[col.prop] = value;
submitChange(row, col);
}}
disabled={!row.actualFloorStackArea || !row[col.referenceKey]}
placeholder="">
{col.options.map(item => (
<el-option
key={item.value}
label={item.label}
value={item.value}
/>
))}
</el-select>
</div>
)
},
// 请求时需要额外携带影响的列字段值
requestKey: ["actualFloorStackExecuted", "regularDisplayExecuted"]
},
{
label: "实际主题地堆-是否",
......@@ -529,8 +636,28 @@ const baseColumns = ref([
{ label: '是', value: '是' },
{ label: '否', value: '否' }
],
referenceKey: "plannedThemedFloorStack",
fill: true
fill: true,
width: 140,
render: (_, row, col) => {
return (
<div>
<el-select modelValue={row[col.prop]}
onUpdate:modelValue={(value) => {
row[col.prop] = value;
submitChange(row, col);
}}
placeholder="">
{col.options.map(item => (
<el-option
key={item.value}
label={item.label}
value={item.value}
/>
))}
</el-select>
</div>
)
}
},
{
label: "实际地堆是否执行",
......@@ -542,21 +669,24 @@ const baseColumns = ref([
return row.actualFloorStackExecuted;
},
formulaStr: '公式:(实际地堆平米数 >= 计划地堆平米数)并且(实际地堆数量 >= 计划地堆数量)',
fill: true
fill: true,
width: 150
},
{
label: "计划多点陈列-数量+形式",
prop: "plannedMultiDisplay",
visible: true,
type: 'string',
fill: false
fill: false,
width: 175
},
{
label: "计划多点陈列-总费用",
prop: "plannedMultiDisplayTotalCost",
visible: true,
type: 'string',
fill: false
fill: false,
width: 155
},
{
label: "实际多点陈列-数量+形式",
......@@ -568,7 +698,25 @@ const baseColumns = ref([
row.actualMultiDisplayExecuted = !row.actualMultiDisplay ? '否' : '是'
},
referenceKey: "plannedMultiDisplay",
fill: true
fill: true,
width: 175,
render: (_, row, col) => {
return (
<div>
<span>{row[col.referenceKey] || '-'}</span>
<el-input modelValue={row[col.prop]}
onUpdate:modelValue={(value) => {
row[col.prop] = value;
submitChange(row, col);
}}
disabled={!row[col.referenceKey]}
placeholder="">
</el-input>
</div>
)
},
// 请求时需要额外携带影响的列字段值
requestKey: ["actualMultiDisplayExecuted"]
},
{
label: "实际多点陈列-是否执行",
......@@ -579,21 +727,48 @@ const baseColumns = ref([
{ label: '是', value: '是' },
{ label: '否', value: '否' }
],
fill: true
fill: true,
width: 170,
render: (_, row, col) => {
return (
<div>
<span>{row[col.referenceKey] || '-'}</span>
<el-select modelValue={row[col.prop]}
onUpdate:modelValue={(value) => {
row[col.prop] = value;
submitChange(row, col);
}}
disabled={!row[col.referenceKey]}
placeholder="">
{col.options.map(item => (
<el-option
key={item.value}
label={item.label}
value={item.value}
/>
))}
</el-select>
</div>
)
},
// 请求时需要额外携带影响的列字段值
requestKey: ["actualMultiDisplayExecuted"]
},
{
label: "合计费用-费用",
prop: "totalCost",
visible: true,
type: 'string',
fill: false
fill: false,
width: 115
},
{
label: "合计费用-费率",
prop: "totalCostRate",
visible: true,
type: 'string',
fill: false
fill: false,
width: 115
},
{
label: "常规陈列是否执行",
......@@ -606,239 +781,198 @@ const baseColumns = ref([
return row.regularDisplayExecuted;
},
formulaStr: '公式:(实际主货架是否执行,并且实际端架是否执行,并且实际地堆是否执行,并且实际多点陈列是否执行)',
fill: true
},
{
label: "综合标签-拜访辅助列",
prop: "visitAssistTag",
visible: true,
type: 'string',
fill: false
fill: true,
width: 150
},
// {
// label: "综合标签-拜访辅助列",
// prop: "visitAssistTag",
// visible: true,
// type: 'string',
// fill: false,
// width: 160
// },
{
label: "付费陈列- 是否",
prop: "paidDisplay",
visible: true,
type: 'string',
fill: false
fill: false,
width: 120
},
{
label: "当月拜访目标",
label: "计划-当月拜访目标",
prop: "monthlyVisitTarget",
visible: true,
type: 'string',
fill: false
fill: false,
width: 135
},
{
label: "当月是否拜访",
label: "实际-当月是否拜访",
prop: "monthlyVisited",
visible: true,
type: 'string',
fill: false
fill: false,
width: 135
},
{
label: '修改人',
prop: 'updateBy',
visible: true,
type: 'string',
fill: false,
width: 120
},
{
label: '修改时间',
prop: 'updateTime',
visible: true,
type: 'string',
fill: false,
width: 140
}
],
visible: true
}
]);
// 表格列
const tableColumns = ref([])
// 右上角工具选择列
const chooseColumns = ref([])
// 计算列具体数据
const checkTableColumns = () => {
if (operation.value === '平铺模式') {
]);
// 表格列
const tableColumns = ref([])
// 右上角工具选择列
const chooseColumns = ref([])
// 计算列具体数据
const updateColumns = (operation) => {
return new Promise((resolve, reject) => {
if (operation === '平铺模式') {
chooseColumns.value = baseColumns.value
tableColumns.value = baseColumns.value.flatMap(item => {
if (item.children) {
return item.children.filter(child => child);
return item.children.filter(child => !child.onlyFill);
}
return [];
});
} else if (operation.value === '填报模式') {
} else if (operation === '填报模式') {
chooseColumns.value = baseColumns.value.flatMap(item => {
if (item.children) {
return item.children
.filter(child => child.fill)
return item.children.filter(child => child.fill === true)
}
return [];
});
tableColumns.value = chooseColumns.value
}
// 强制表格立即应用所有宽度设置,避免先自适应再调整
nextTick(() => {
if (tableRef.value) {
tableRef.value.doLayout()
tableColumns.value = chooseColumns.value
}
resolve()
})
}
}
onMounted(() => {
checkTableColumns()
})
onMounted(() => {
updateColumns()
})
// 树形结构选择列默认有哪些
const visibleProps = computed(() => {
// 树形结构选择列默认有哪些
const visibleProps = computed(() => {
return baseColumns.value.flatMap(item => {
if (item.children) {
return item.children.filter(child => child.visible).map(child => child.prop);
}
return item.prop;
});
});
});
// 提交变更
const submitChange = async (row, col) => {
// 有个类型输入框的需要调用 func
if (col.type === 'input') {
col.func(row)
}
await submitDisplayPlan({
id: row.sadId,
[col.prop]: row[col.prop]
})
// 这些提交变化的,同时会影响公式计算的,需要把公式计算的字段列结果,一起发给后台
// 注意:实际主货架形式改变并为空时,要置空实际主货架数量
// 提交变更
const submitChange = async (row, col) => {
// 需要特殊处理的
// 1. 实际主货架-形式,为空时,置空实际主货架-数量
if (col.prop === 'actualMainShelfType' && !row.actualMainShelfType) {
row.actualMainShelfQty = ''
// 并提交保存一次
await submitDisplayPlan({
id: row.sadId,
actualMainShelfQty: row.actualMainShelfQty,
})
row.actualMainShelfQty = 0
}
// 主货架
if (['actualMainShelfType', 'actualMainShelfQty'].includes(col.prop)) {
await submitDisplayPlan({
id: row.sadId,
actualMainShelfExecuted: row.actualMainShelfExecuted,
})
let requestObj = {}
if (col.requestKey) {
// 关联的公式计算列,需要特殊处理
for (const str of col.requestKey) {
const obj = tableColumns.value.find(item => item.prop === str)
if (obj.type === 'formula') {
obj.func(row)
}
// 端架
if (['actualEndCapQty'].includes(col.prop)) {
await submitDisplayPlan({
id: row.sadId,
actualEndCapExecuted: row.actualEndCapExecuted,
})
}
// 地堆
if (['actualFloorStackArea', 'actualFloorStackQty'].includes(col.prop)) {
await submitDisplayPlan({
id: row.sadId,
actualFloorStackExecuted: row.actualFloorStackExecuted,
})
requestObj = col.requestKey.reduce((acc, key) => ({ ...acc, [key]: row[key] }), {})
}
// 常规陈列是否执行
await submitDisplayPlan({
id: row.sadId,
regularDisplayExecuted: row.regularDisplayExecuted,
[col.prop]: row[col.prop], // 当前修改列的值
...requestObj
})
}
// // 这些提交变化的,同时会影响公式计算的,需要把公式计算的字段列结果,一起发给后台
// // 注意:实际主货架形式改变并为空时,要置空实际主货架数量
// if (col.prop === 'actualMainShelfType' && !row.actualMainShelfType) {
// row.actualMainShelfQty = ''
// // 并提交保存一次
// await submitDisplayPlan({
// id: row.sadId,
// actualMainShelfQty: row.actualMainShelfQty,
// })
// }
// // 主货架
// if (['actualMainShelfType', 'actualMainShelfQty'].includes(col.prop)) {
// await submitDisplayPlan({
// id: row.sadId,
// actualMainShelfExecuted: row.actualMainShelfExecuted,
// })
// }
// // 端架
// if (['actualEndCapQty'].includes(col.prop)) {
// await submitDisplayPlan({
// id: row.sadId,
// actualEndCapExecuted: row.actualEndCapExecuted,
// })
// }
// // 地堆
// if (['actualFloorStackArea', 'actualFloorStackQty'].includes(col.prop)) {
// await submitDisplayPlan({
// id: row.sadId,
// actualFloorStackExecuted: row.actualFloorStackExecuted,
// })
// }
// // 常规陈列是否执行
// await submitDisplayPlan({
// id: row.sadId,
// regularDisplayExecuted: row.regularDisplayExecuted,
// })
}
// 表格数据
const tableData = ref([])
const isLoading = ref(true)
const params = reactive({
// 表格数据
const tableData = ref([])
const isLoading = ref(true)
const params = reactive({
pageNum: 1,
pageSize: 20,
})
const total = ref(0)
})
const total = ref(0)
// 筛选工具
const showSearch = ref(true);
const getTableList = async () => {
// 筛选工具
const getTableList = async () => {
isLoading.value = true
const res = await getDisplayList({
...params
})
res.data.rows.forEach(item => {
// 计划月份
item.salesMonth = parseTime(item.salesMonth, '{y}-{m}')
// 开户日期
item.openingDate = parseTime(item.openingDate, '{y}-{m}-{d}')
// 闭户日期
item.closingDate = parseTime(item.closingDate, '{y}-{m}-{d}')
// 动态新增列:门店名称+门店编码+经销山名称(填报模式下,合并到一起)
item.storeNameCodeDealerName = item.storeName + '\n(' + item.storeCode + ')' + '\n(' + item.dealerName + ')'
})
tableData.value = res.data.rows
total.value = res.data.total
isLoading.value = false
}
getTableList()
// 列宽度
const getColumnMinWidth = (column) => {
// 根据列名或属性判断合适的最小宽度
const widthMap = {
'salesMonth': 90,
'regionName': 90,
'districtName': 90,
'dealerProvince': 105,
'dealerCity': 105,
'dealerCode': 105,
'dealerName': 215,
'dealerType': 145,
'openingDate': 105,
'closingDate': 105,
'regionManager': 90,
'districtManager': 90,
'cityManager': 90,
'storeCode': 105,
'storeName': 175,
'storeProvince': 90,
'storeCity': 90,
'lineName': 90,
'lineType': 90,
'channelDl': 90,
'channelXl': 140,
'storeType': 90,
'systemFormat': 90,
'storeArea': 90,
'storeLevel': 165,
'storeAddress': 255,
'productCount': 80,
'lfSalesModel': 155,
'lfMonthlyPos': 155,
'plannedMainShelfType': 130,
'plannedMainShelfQty': 130,
'plannedMainShelfUnitCost': 155,
'plannedMainShelfTotalCost': 145,
'actualMainShelfType': 140,
'actualMainShelfQty': 130,
'actualMainShelfExecuted': 170,
'plannedEndCapQty': 115,
'plannedEndCapTotalCost': 130,
'plannedEndCapUnitCost': 140,
'actualEndCapQty': 115,
'actualEndCapExecuted': 155,
'plannedFloorStackArea': 170,
'plannedFloorStackQty': 115,
'plannedThemedFloorStack': 140,
'plannedFloorStackTotalCost': 130,
'plannedFloorStackUnitCostPerSqm': 160,
'actualFloorStackArea': 170,
'actualFloorStackQty': 115,
'actualThemedFloorStack': 140,
'actualFloorStackExecuted': 150,
'plannedMultiDisplay': 175,
'plannedMultiDisplayTotalCost': 155,
'actualMultiDisplay': 175,
'actualMultiDisplayExecuted': 170,
'totalCost': 115,
'totalCostRate': 115,
'regularDisplayExecuted': 150,
'visitAssistTag': 160,
'paidDisplay': 120,
'monthlyVisitTarget': 110,
'monthlyVisited': 110
};
return widthMap[column.prop] || 100; // 默认宽度
};
// 格式化函数
const formatterTableData = (row, col) => {
if (col.prop === 'openingDate' || col.prop === 'closingDate') {
return parseTime(row[col.prop], '{y}-{m}-{d}')
}
return row[col.prop] || '-'
}
}
getTableList()
</script>
<style scoped
......
<template>
<el-row>
<el-form-item>
<el-radio-group v-model="operation"
@change="checkTableColumns">
<el-radio-button label="平铺模式"
value="平铺模式" />
<el-radio-button label="填报模式"
value="填报模式" />
</el-radio-group>
</el-form-item>
<right-toolbar v-model:showSearch="showSearch"
@queryTable="getTableList()"
:columns="chooseColumns"
:showColumnsType="operation === '平铺模式' ? 'tree' : 'checkbox'"
:defaultCheckedKeys="visibleProps">
</right-toolbar>
</el-row>
<!-- 筛选列组的按钮 -->
<el-table :data="tableData"
border
ref="tableRef"
class="auto-fit-header-table"
v-loading="isLoading">
<template v-for="col in tableColumns">
<el-table-column v-if="col.visible"
:label="col.label"
:key="col.prop"
align="center"
:show-overflow-tooltip="col.type === 'string'"
class-name="column-style"
:width="getColumnMinWidth(col)"
:fixed="operation === '填报模式' && col.fixed">
<template #header="{ column }">
<!-- 只为特定列添加问号图标 -->
<span class="formula-column">
{{ column.label }}
<el-tooltip v-if="col.type === 'formula'"
:content="col.formulaStr"
placement="top">
<el-icon><question-filled /></el-icon>
</el-tooltip>
</span>
</template>
<template #default="{ row }">
<div v-if="col.type === 'select' && operation === '填报模式'"
class="cell-style">
<div>
<el-tooltip effect="dark"
:content="row[col.referenceKey]"
placement="top-start"
:disabled="!isContentOverflow(col.referenceKey, row[col.referenceKey])">
<span class="ellipsis-tooltip">{{ row[col.referenceKey] || '-' }}</span>
</el-tooltip>
<el-select v-model="row[col.prop]"
:disabled="!row[col.referenceKey]"
placeholder=""
clearable
@change="submitChange(row, col)">
<el-option v-for="item in col.options"
:key="item.value"
:label="item.label"
:value="item.value" />
</el-select>
</div>
</div>
<!-- 公式计算 -->
<div v-else-if="col.type === 'formula'">
{{ col.func(row) }}
</div>
<!-- 输入框 -->
<div v-else-if="col.type === 'input' && operation === '填报模式'"
class="cell-style">
<div>
<el-tooltip effect="dark"
:content="row[col.referenceKey]"
placement="top-start"
:disabled="!isContentOverflow(col.referenceKey, row[col.referenceKey])">
<span class="ellipsis-tooltip">{{ row[col.referenceKey] || '-' }}</span>
</el-tooltip>
<el-input v-model="row[col.prop]"
:disabled="!row[col.referenceKey]"
placeholder=""
:type="col.format === 'number' ? 'number' : 'text'"
@input="submitChange(row, col)" />
</div>
</div>
<!-- 日期选择 -->
<div v-else-if="col.type === 'date' && operation === '填报模式'"
class="cell-style">
<div>
<span class="ellipsis-tooltip">{{ row[col.referenceKey] || '-' }}</span>
<div class="date-picker">
<el-date-picker v-model="row[col.prop]"
type="date"
:disabled="!row[col.referenceKey]"
:format="col.format"
placeholder=""
@change="submitChange(row, col)" />
</div>
</div>
</div>
<!-- 为其他类型或未定义类型提供默认显示 -->
<div v-else>
<div class="ellipsis-text"
style="max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
{{ row[col.prop] }}
</div>
</div>
</template>
</el-table-column>
</template>
</el-table>
<!-- 分页 -->
<pagination :total="total"
v-model:page="params.pageNum"
v-model:limit="params.pageSize"
@pagination="getTableList" />
<CommonPlan :tableData="tableData"
:isLoading="isLoading"
:total="total"
:tableColumns="tableColumns"
:chooseColumns="chooseColumns"
:visibleProps="visibleProps"
:params="params"
@updateColumns="updateColumns"
@getTableList="getTableList" />
</template>
<script setup>
import { getDisplayScheduleList, submitDisplaySchedulePlan } from '@/api'
import { parseTime } from '@/utils'
<script setup
lang="jsx">
import CommonPlan from '@/views/promotion/components/CommonPlan'
import { getDisplayScheduleList, submitDisplaySchedulePlan } from '@/api'
import { parseTime } from '@/utils'
/*************** 操作类型 ***************/
const operation = ref('平铺模式');
const tableRef = ref(null)
// 平铺模式
const baseColumns = ref([
/*************** 操作类型 ***************/
function splitAndFilter(str) {
// 分割字符串并处理空值
return str.split(/[,,]/)
.map(item => item.trim()) // 去除前后空格
.filter(item => item.length > 0); // 过滤空字符串
}
// 基础列配置
const baseColumns = ref([
{
label: "基础信息",
children: [
......@@ -137,42 +34,48 @@ const baseColumns = ref([
visible: true,
type: 'string',
fill: false,
fixed: 'left'
fixed: 'left',
width: 90
},
{
label: "销售大区",
prop: "regionName",
visible: true,
type: "string",
fill: false
fill: false,
width: 90
},
{
label: "销售战区",
prop: "districtName",
visible: true,
type: "string",
fill: false
fill: false,
width: 90
},
{
label: "经销商-省份",
prop: "dealerProvince",
visible: true,
type: "string",
fill: false
fill: false,
width: 105
},
{
label: "经销商-城市",
prop: "dealerCity",
visible: true,
type: "string",
fill: false
fill: false,
width: 105
},
{
label: "经销商-代码",
prop: "dealerCode",
visible: true,
type: "string",
fill: false
fill: false,
width: 105
},
{
label: "经销商名称",
......@@ -180,49 +83,56 @@ const baseColumns = ref([
visible: true,
type: "string",
fill: true,
fixed: 'left'
fixed: 'left',
width: 215
},
{
label: "经销商类型",
prop: "dealerType",
visible: true,
type: "string",
fill: false
fill: false,
width: 115
},
{
label: "开户日期",
prop: "openingDate",
visible: true,
type: "string",
fill: false
fill: false,
width: 105
},
{
label: "闭户日期",
prop: "closingDate",
visible: true,
type: "string",
fill: false
fill: false,
width: 105
},
{
label: "大区总监",
prop: "regionManager",
visible: true,
type: "string",
fill: false
fill: false,
width: 90
},
{
label: "战区经理",
prop: "districtManager",
visible: true,
type: "string",
fill: false
fill: false,
width: 90
},
{
label: "城市经理",
prop: "cityManager",
visible: true,
type: "string",
fill: false
fill: false,
width: 90
},
{
label: "系统名称",
......@@ -230,42 +140,48 @@ const baseColumns = ref([
visible: true,
type: "string",
fill: true,
fixed: 'left'
fixed: 'left',
width: 90
},
{
label: "系统类型",
prop: "lineType",
visible: true,
type: "string",
fill: false
fill: false,
width: 90
},
{
label: "渠道大类",
prop: "channelDl",
visible: true,
type: "string",
fill: false
fill: false,
width: 90
},
{
label: "渠道小类",
prop: "channelXl",
visible: true,
type: "string",
fill: false
fill: false,
width: 140
},
{
label: "系统业态",
prop: "lineLf",
visible: true,
type: "string",
fill: false
fill: false,
width: 90
},
{
label: "门店数",
prop: "storeCount",
visible: true,
type: "string",
fill: false
fill: false,
width: 75
}
],
prop: 'baseColumns',
......@@ -279,21 +195,24 @@ const baseColumns = ref([
prop: "promotionExecutionMonth",
visible: true,
type: "string",
fill: false
fill: false,
width: 110
},
{
label: "档期计划-促销规格",
prop: "plannedPromotionSpec",
visible: true,
type: "string",
fill: false
fill: false,
width: 140
},
{
label: "档期计划-促销口味",
prop: "plannedPromotionFlavor",
visible: true,
type: "string",
fill: false
fill: false,
width: 140
},
{
label: "档期执行-促销规格",
......@@ -335,16 +254,36 @@ const baseColumns = ref([
}
],
referenceKey: "plannedPromotionSpec",
fill: true
fill: true,
width: 190,
render: (_, row, col) => {
return (
<div>
<span>{row[col.referenceKey] || '-'}</span>
<el-select modelValue={row[col.prop]}
onUpdate:modelValue={(value) => {
row[col.prop] = value;
submitChange(row, col);
}}
disabled={!row[col.referenceKey]}
placeholder=""
clearable
>
{col.options.map(item => (
<el-option
key={item.value}
label={item.label}
value={item.value}
/>
))}
</el-select>
</div>
)
},
{
label: "档期执行-促销口味",
prop: "actualPromotionFlavor",
visible: true,
type: 'input',
referenceKey: "plannedPromotionFlavor",
fill: true
// 请求时需要额外携带影响的列字段值
requestKey: ["specExecutionStatus", "promotionExecutionStatus"]
},
{
label: "档期规格是否执行",
prop: "specExecutionStatus",
......@@ -355,7 +294,103 @@ const baseColumns = ref([
return row.specExecutionStatus;
},
formulaStr: '公式:(档期计划促销规格 = 档期执行促销规格)',
fill: true
fill: true,
width: 150
},
{
label: "档期执行-促销口味",
prop: "actualPromotionFlavor",
visible: true,
type: 'select',
options: [],
referenceKey: "plannedPromotionFlavor",
fill: true,
width: 170,
render: (_, row, col) => {
// options 根据参考属性值来决定
let list = splitAndFilter(row[col.referenceKey])
let isFullFlavor = false
// 如果数组里有"全系列"/"全口味"则修改数组
if (list.includes('全口味')) {
list = [
{
label: '全口味',
value: '全口味'
},
{
label: '部分口味',
value: '部分口味'
}
]
isFullFlavor = true
} else {
list = list.map(item => ({
label: item,
value: item
}))
isFullFlavor = false
}
// span 标签超出宽度时显示省略号
return (
<div>
<el-tooltip placement="top" content={row[col.referenceKey] || '-'} disabled={!isContentOverflow(col.referenceKey, row[col.referenceKey])}>
<span style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis; display: inline-block; max-width: 100%;">
{row[col.referenceKey] || '-'}
</span>
</el-tooltip>
<el-select modelValue={row[col.prop]}
onUpdate:modelValue={(value) => {
row[col.prop] = value;
submitChange(row, col);
}}
disabled={!row[col.referenceKey]}
placeholder=""
clearable
multiple={!isFullFlavor}
collapse-tags={!isFullFlavor}
collapse-tags-tooltip={!isFullFlavor}>
{list.map(item => (
<el-option
key={item.value}
label={item.label}
value={item.value}
/>
))}
</el-select>
</div>
)
},
// 请求时需要额外携带影响的列字段值
requestKey: ["flavorExecutionStatus", "promotionExecutionStatus"]
},
{
label: "档期口味是否执行",
prop: "flavorExecutionStatus",
visible: true,
type: 'formula',
func: (row) => {
// 处理逗号的口味形成数组
let ppromotionFlavor = splitAndFilter(row.plannedPromotionFlavor)
if (!row.actualPromotionFlavor) {
// 判断空字符串
row.flavorExecutionStatus = '未执行';
} else if (row.actualPromotionFlavor?.length === 0) {
// 空数组
row.flavorExecutionStatus = '未执行';
} else {
if (row.actualPromotionFlavor === '全口味') {
row.flavorExecutionStatus = '执行';
} else if (row.actualPromotionFlavor?.length === ppromotionFlavor.length) {
row.flavorExecutionStatus = '执行';
} else if (row.actualPromotionFlavor?.length !== ppromotionFlavor?.length) {
row.flavorExecutionStatus = '部分执行';
}
}
return row.flavorExecutionStatus;
},
formulaStr: '公式:(档期计划促销口味 = 档期执行促销口味)',
fill: true,
width: 150
}
],
prop: 'scheduleInfo',
......@@ -370,14 +405,16 @@ const baseColumns = ref([
prop: "totalCost",
visible: true,
type: "string",
fill: false
fill: false,
width: 105
},
{
label: "合计费率",
prop: "totalCostRate",
visible: true,
type: "string",
fill: false
fill: false,
width: 85
}
],
visible: true
......@@ -391,42 +428,56 @@ const baseColumns = ref([
prop: "plannedPromotionFormat",
visible: true,
type: "string",
fill: false
fill: false,
width: 115
},
{
label: "计划-核销方式",
prop: "plannedVerificationMethod",
visible: true,
type: "string",
fill: false
fill: false,
width: 115
},
{
label: "计划-档期规则",
prop: "plannedPromotionRules",
visible: true,
type: "string",
fill: false
fill: false,
width: 115
},
{
label: "计划-档期前补差天数",
prop: "plannedPrePromotionAdjustDays",
visible: true,
type: "string",
fill: false
fill: false,
width: 155
},
{
label: "计划-档期后补差天数",
prop: "plannedPostPromotionAdjustDays",
visible: true,
type: "string",
fill: false
fill: false,
width: 155
},
{
label: "计划-档期开始时间",
prop: "plannedPromotionStartDate",
visible: true,
type: "string",
fill: false
fill: false,
width: 140
},
{
label: "计划-补差开始时间",
prop: "plannedAdjustmentStartDate",
visible: true,
type: "string",
fill: false,
width: 140
},
{
label: "实际-档期开始时间",
......@@ -435,14 +486,44 @@ const baseColumns = ref([
type: 'date',
format: 'MM-DD',
referenceKey: "plannedPromotionStartDate",
fill: true
fill: true,
width: 140,
render: (_, row, col) => {
return (
<div>
<span>{row[col.referenceKey] || '-'}</span>
<div class="date-picker">
<el-date-picker modelValue={row[col.prop]}
onUpdate:modelValue={(value) => {
row[col.prop] = value;
submitChange(row, col);
}}
disabled={!row[col.referenceKey]}
placeholder=""
clearable
type="date"
value-format="MM-DD"
/>
</div>
</div>
)
}
},
{
label: "计划-档期结束时间",
prop: "plannedPromotionEndDate",
visible: true,
type: "string",
fill: false
fill: false,
width: 140,
},
{
label: "计划-补差结束时间",
prop: "plannedAdjustmentEndDate",
visible: true,
type: "string",
fill: false,
width: 140
},
{
label: "实际-档期结束时间",
......@@ -451,7 +532,29 @@ const baseColumns = ref([
type: 'date',
format: 'MM-DD',
fill: true,
referenceKey: "plannedPromotionEndDate"
referenceKey: "plannedPromotionEndDate",
width: 140,
render: (_, row, col) => {
return (
<div>
<span>{row[col.referenceKey] || '-'}</span>
<div class="date-picker">
<el-date-picker modelValue={row[col.prop]}
onUpdate:modelValue={(value) => {
row[col.prop] = value;
submitChange(row, col);
}}
disabled={!row[col.referenceKey]}
placeholder=""
clearable
type="date"
value-format="MM-DD"
/>
</div>
</div>
)
},
requestKey: ["plannedPromotionEndDate"]
},
{
label: "档期是否开展",
......@@ -463,7 +566,8 @@ const baseColumns = ref([
return row.promotionImplementationStatus;
},
formulaStr: '公式:(实际档期开始时间不为空,并且实际档期结束时间不为空)',
fill: true
fill: true,
width: 125
},
{
label: "时间是否执行",
......@@ -475,35 +579,45 @@ const baseColumns = ref([
return row.timeExecutionStatus;
},
formulaStr: '公式:(实际档期开始时间 = 计划档期开始时间,并且实际档期结束时间 = 计划档期结束时间)',
fill: true
},
{
label: "计划-补差开始时间",
prop: "plannedAdjustmentStartDate",
visible: true,
type: "string",
fill: false
},
{
label: "计划-补差结束时间",
prop: "plannedAdjustmentEndDate",
visible: true,
type: "string",
fill: false
fill: true,
width: 125
},
{
label: "计划促销机制",
prop: "plannedPromotionMechanism",
visible: true,
type: "string",
fill: false
fill: false,
width: 115
},
{
label: "实际促销机制",
prop: "actualPromotionMechanism",
visible: true,
type: 'input',
fill: true
referenceKey: "plannedPromotionMechanism",
fill: true,
width: 115,
render: (_, row, col) => {
return (
<div>
<span>{row[col.referenceKey] || '-'}</span>
<div>
<el-input modelValue={row[col.prop]}
onUpdate:modelValue={(value) => {
row[col.prop] = value;
submitChange(row, col);
}}
disabled={!row[col.referenceKey]}
placeholder=""
clearable
/>
</div>
</div>
)
}
},
{
label: "促销机制是否执行",
......@@ -520,14 +634,36 @@ const baseColumns = ref([
value: '否'
}
],
fill: true
fill: true,
width: 135,
render: (_, row, col) => {
return (
<div>
<div style="width: 100%;">
<el-select modelValue={row[col.prop]}
onUpdate:modelValue={(value) => {
row[col.prop] = value;
submitChange(row, col);
}}
placeholder=""
clearable
>
{col.options.map((item) => (
<el-option key={item.value} label={item.label} value={item.value} />
))}
</el-select>
</div>
</div>
)
}
},
{
label: "预估袋数",
prop: "estimatedBagCount",
visible: true,
type: "string",
fill: false
fill: false,
width: 90
},
{
label: "档期备货量(袋)",
......@@ -535,35 +671,59 @@ const baseColumns = ref([
visible: true,
type: 'input',
format: 'number',
fill: true
referenceKey: "estimatedBagCount",
fill: true,
width: 135,
render: (_, row, col) => {
return (
<div>
<span>{row[col.referenceKey] || '-'}</span>
<div>
<el-input modelValue={row[col.prop]}
onUpdate:modelValue={(value) => {
row[col.prop] = value;
submitChange(row, col);
}}
disabled={!row[col.referenceKey]}
placeholder=""
clearable
/>
</div>
</div>
)
}
},
{
label: "单包厂价(单包)",
prop: "unitFactoryPrice",
visible: true,
type: "string",
fill: false
fill: false,
width: 135
},
{
label: "正常供价(单包)",
prop: "normalSupplyPrice",
visible: true,
type: "string",
fill: false
fill: false,
width: 135
},
{
label: "日常零售价(单包)",
prop: "regularRetailPrice",
visible: true,
type: "string",
fill: false
fill: false,
width: 150
},
{
label: "计划促销售价(单包)",
prop: "plannedPromotionPrice",
visible: true,
type: "string",
fill: false
fill: false,
width: 165
},
{
label: "实际促销售价(单包)",
......@@ -571,7 +731,28 @@ const baseColumns = ref([
visible: true,
type: 'input',
format: 'number',
fill: true
referenceKey: "plannedPromotionPrice",
fill: true,
width: 165,
render: (_, row, col) => {
return (
<div>
<span>{row[col.referenceKey] || '-'}</span>
<div>
<el-input modelValue={row[col.prop]}
onUpdate:modelValue={(value) => {
row[col.prop] = value;
submitChange(row, col);
}}
disabled={!row[col.referenceKey]}
placeholder=""
clearable
/>
</div>
</div>
)
},
requestKey: ["promotionPriceExecutionStatus"]
},
{
label: "促销价是否执行",
......@@ -583,63 +764,72 @@ const baseColumns = ref([
return row.promotionPriceExecutionStatus;
},
formulaStr: '公式:(计划促销售价 = 实际促销售价)',
fill: true
fill: true,
width: 140
},
{
label: "正常经销商毛利现状",
prop: "normalDealerMarginStatus",
visible: true,
type: "string",
fill: false
fill: false,
width: 150
},
{
label: "系统前台毛利保证",
prop: "systemFrontendMarginGuarantee",
visible: true,
type: "string",
fill: false
fill: false,
width: 135
},
{
label: "系统后台毛利保证",
prop: "systemBackendMarginGuarantee",
visible: true,
type: "string",
fill: false
fill: false,
width: 135
},
{
label: "系统促销毛利保证",
prop: "systemPromotionMarginGuarantee",
visible: true,
type: "string",
fill: false
fill: false,
width: 135
},
{
label: "经销商毛利保证",
prop: "dealerMarginGuarantee",
visible: true,
type: "string",
fill: false
fill: false,
width: 125
},
{
label: "单袋补差金额(费用)",
prop: "unitBagAdjustmentAmount",
visible: true,
type: "string",
fill: false
fill: false,
width: 165
},
{
label: "补差费比(%)",
prop: "adjustmentCostRatio",
visible: true,
type: "string",
fill: false
fill: false,
width: 125
},
{
label: "整体补差(费用)",
prop: "totalAdjustmentCost",
visible: true,
type: "string",
fill: false
fill: false,
width: 135
},
{
label: "档期是否执行",
......@@ -652,7 +842,8 @@ const baseColumns = ref([
return row.promotionExecutionStatus;
},
formulaStr: '公式:(促销价是否执行 = 是,并且促销机制是否执行 = 是,并且促销规格是否执行 = 是,并且档期是否开展 = 是)',
fill: true
fill: true,
width: 125
}
],
visible: true
......@@ -666,14 +857,16 @@ const baseColumns = ref([
prop: "plannedPosterFormat",
visible: true,
type: "string",
fill: false
fill: false,
width: 115
},
{
label: "计划-海报费用",
prop: "plannedPosterCost",
visible: true,
type: "string",
fill: false
fill: false,
width: 115
},
{
label: "实际-海报形式",
......@@ -686,11 +879,36 @@ const baseColumns = ref([
value: '电子'
},
{
label: '纸制',
value: '纸制'
label: '纸质',
value: '纸质'
}
],
fill: true
referenceKey: "plannedPosterFormat",
fill: true,
width: 115,
render: (_, row, col) => {
return (
<div>
<span>{row[col.referenceKey] || '-'}</span>
<div style="width: 100%">
<el-select modelValue={row[col.prop]}
onUpdate:modelValue={(value) => {
row[col.prop] = value;
submitChange(row, col);
}}
disabled={!row[col.referenceKey]}
placeholder=""
clearable
>
{col.options.map(item => (
<el-option key={item.value} label={item.label} value={item.value} />
))}
</el-select>
</div>
</div>
)
},
requestKey: ["posterExecutionStatus"]
},
{
label: "海报是否执行",
......@@ -698,11 +916,13 @@ const baseColumns = ref([
visible: true,
type: 'formula',
func: (row) => {
if (!row.actualPosterFormat) return row.posterExecutionStatus = '否'
row.posterExecutionStatus = row.plannedPosterFormat == row.actualPosterFormat ? '是' : '否';
return row.posterExecutionStatus;
},
formulaStr: '公式:(计划海报形式 = 实际海报形式)',
fill: true
fill: true,
width: 125
}
],
visible: true
......@@ -716,33 +936,55 @@ const baseColumns = ref([
prop: "storeCountInt",
visible: true,
type: "string",
fill: false
fill: false,
width: 75
},
{
label: "店均费用",
prop: "averageStoreCost",
visible: true,
type: "string",
fill: false
fill: false,
width: 115
},
{
label: "费用",
prop: "cost",
visible: true,
type: "string",
fill: false
fill: false,
width: 115
},
{
label: '修改人',
prop: 'updateBy',
visible: true,
type: 'string',
fill: false,
width: 120
},
{
label: '修改时间',
prop: 'updateTime',
visible: true,
type: 'string',
fill: false,
width: 140
}
],
visible: true
}
]);
// 表格列数据
const tableColumns = ref([])
// 右上角工具选择列
const chooseColumns = ref([])
// 计算列具体数据
const checkTableColumns = () => {
if (operation.value === '平铺模式') {
]);
// 表格列数据
const tableColumns = ref([])
// 右上角工具选择列
const chooseColumns = ref([])
// 计算列具体数据
const updateColumns = (operation) => {
// 保存用户选择的列配置
if (operation === '平铺模式') {
chooseColumns.value = baseColumns.value
tableColumns.value = baseColumns.value.flatMap(item => {
if (item.children) {
......@@ -750,7 +992,7 @@ const checkTableColumns = () => {
}
return [];
});
} else if (operation.value === '填报模式') {
} else if (operation === '填报模式') {
chooseColumns.value = baseColumns.value.flatMap(item => {
if (item.children) {
return item.children
......@@ -760,21 +1002,10 @@ const checkTableColumns = () => {
});
tableColumns.value = chooseColumns.value
}
// 强制表格立即应用所有宽度设置,避免先自适应再调整
nextTick(() => {
if (tableRef.value) {
tableRef.value.doLayout()
}
})
}
onMounted(() => {
checkTableColumns()
})
// 选择列默认选中哪些
const visibleProps = computed(() => {
// // 选择列默认选中哪些
const visibleProps = computed(() => {
// 分情况返回
return tableColumns.value.flatMap(item => {
if (item.children) {
......@@ -782,74 +1013,46 @@ const visibleProps = computed(() => {
}
return item.prop;
});
});
});
// 提交变更
const submitChange = async (row, col) => {
// 如果是 实际促销售价(单包),保留 2 位小数
if (col.prop === 'actualPromotionPrice') {
row[col.prop] = parseFloat(parseFloat(row[col.prop]).toFixed(2))
// 提交变更
const submitChange = async (row, col) => {
// 需要特殊处理的
// 1. 实际主货架-形式,为空时,置空实际主货架-数量
if (col.prop === 'actualMainShelfType' && !row.actualMainShelfType) {
row.actualMainShelfQty = 0
}
await submitDisplaySchedulePlan({
id: row.sapId,
[col.prop]: row[col.prop]
})
// 这些提交变化的,同时会影响公式计算的,需要把公式计算的字段列结果,一起发给后台
// 档期规格是否执行:计划规格和执行规格
if (['plannedPromotionSpec', 'actualPromotionSpec'].includes(col.prop)) {
await submitDisplaySchedulePlan({
id: row.sapId,
specExecutionStatus: row.specExecutionStatus,
})
let requestObj = {}
if (col.requestKey) {
// 关联的公式计算列,需要特殊处理
for (const str of col.requestKey) {
const obj = tableColumns.value.find(item => item.prop == str)
if (obj.type === 'formula') {
obj.func(row)
}
// 档期是否开展:实际档期开始和结束
if (['actualPromotionStartDate', 'actualPromotionEndDate'].includes(col.prop)) {
await submitDisplaySchedulePlan({
id: row.sapId,
promotionImplementationStatus: row.promotionImplementationStatus,
})
}
// 档期是否执行:实际开始和计划开始时间,实际结束时间和计划结束时间
if (['actualPromotionStartDate', 'plannedPromotionStartDate', 'actualPromotionEndDate', 'plannedPromotionEndDate'].includes(col.prop)) {
await submitDisplaySchedulePlan({
id: row.sapId,
timeExecutionStatus: row.timeExecutionStatus,
})
}
// 促销价是否执行:根据计划和实际促销价
if (['actualPromotionPrice', 'plannedPromotionPrice'].includes(col.prop)) {
await submitDisplaySchedulePlan({
id: row.sapId,
promotionPriceExecutionStatus: row.promotionPriceExecutionStatus,
})
requestObj = col.requestKey.reduce((acc, key) => ({ ...acc, [key]: row[key] }), {})
}
// 海报是否执行
if (['plannedPosterFormat', 'actualPosterFormat'].includes(col.prop)) {
await submitDisplaySchedulePlan({
id: row.sapId,
posterExecutionStatus: row.posterExecutionStatus,
[col.prop]: row[col.prop], // 当前修改列的值
...requestObj
})
}
// 档期计划是否执行
await submitDisplaySchedulePlan({
id: row.sapId,
promotionExecutionStatus: row.promotionExecutionStatus,
})
}
// 表格数据
const tableData = ref([])
const isLoading = ref(true)
const params = reactive({
// // 表格数据
const tableData = ref([])
const isLoading = ref(true)
const params = reactive({
pageNum: 1,
pageSize: 20,
})
const total = ref(0)
})
const total = ref(0)
// 筛选工具
const showSearch = ref(true);
const getTableList = async () => {
// // 获取表格数据
const getTableList = async () => {
isLoading.value = true
const res = await getDisplayScheduleList({
...params
......@@ -870,86 +1073,14 @@ const getTableList = async () => {
tableData.value = res.data.rows
total.value = res.data.total
isLoading.value = false
}
getTableList()
// 列宽度
const getColumnMinWidth = (column) => {
// 根据列名或属性判断合适的最小宽度
const widthMap = {
'salesMonth': 90,
'regionName': 90,
'districtName': 90,
'dealerProvince': 105,
'dealerCity': 105,
'dealerCode': 105,
'dealerName': 215,
'dealerType': 115,
'openingDate': 105,
'closingDate': 105,
'regionManager': 90,
'districtManager': 90,
'cityManager': 90,
'lineName': 90,
'lineType': 90,
'channelDl': 90,
'channelXl': 140,
'lineLf': 90,
'storeCount': 75,
'promotionExecutionMonth': 110,
'plannedPromotionSpec': 140,
'plannedPromotionFlavor': 140,
'actualPromotionSpec': 140,
'actualPromotionFlavor': 140,
'specExecutionStatus': 150,
'totalCost': 105,
'totalCostRate': 85,
'plannedPromotionFormat': 115,
'plannedVerificationMethod': 115,
'plannedPromotionRules': 115,
'plannedPrePromotionAdjustDays': 155,
'plannedPostPromotionAdjustDays': 155,
'plannedPromotionStartDate': 140,
'actualPromotionStartDate': 140,
'plannedPromotionEndDate': 140,
'actualPromotionEndDate': 140,
'promotionImplementationStatus': 125,
'timeExecutionStatus': 125,
'plannedAdjustmentStartDate': 140,
'plannedAdjustmentEndDate': 140,
'plannedPromotionMechanism': 115,
'actualPromotionMechanism': 115,
'promotionMechanismExecutionStatus': 135,
'estimatedBagCount': 90,
'promotionStockQuantity': 135,
'unitFactoryPrice': 135,
'normalSupplyPrice': 135,
'regularRetailPrice': 150,
'plannedPromotionPrice': 165,
'actualPromotionPrice': 165,
'promotionPriceExecutionStatus': 140,
'normalDealerMarginStatus': 150,
'systemFrontendMarginGuarantee': 135,
'systemBackendMarginGuarantee': 135,
'systemPromotionMarginGuarantee': 135,
'dealerMarginGuarantee': 125,
'unitBagAdjustmentAmount': 165,
'adjustmentCostRatio': 125,
'totalAdjustmentCost': 135,
'promotionExecutionStatus': 125,
'plannedPosterFormat': 115,
'plannedPosterCost': 115,
'actualPosterFormat': 115,
'posterExecutionStatus': 125,
'storeCountInt': 75,
'averageStoreCost': 115,
'cost': 115
};
}
return widthMap[column.prop] || 150; // 默认宽度
};
onMounted(() => {
updateColumns()
getTableList()
})
const isContentOverflow = (key, content) => {
const isContentOverflow = (key, content) => {
// 创建临时元素用于测量
if (!content) return false;
......@@ -969,34 +1100,14 @@ const isContentOverflow = (key, content) => {
// 当内容宽度大于元素宽度时,表示内容溢出
return scrollWidth > width;
};
};
</script>
<style scoped
lang="scss">
.container {
.el-tabs {
height: 100%;
display: flex;
flex-direction: column-reverse;
.el-tabs__content {
display: flex;
flex-direction: column;
height: 100%;
.el-tab-pane {
height: 100%;
display: flex;
flex-direction: column;
.pagination-container {
margin: 10px;
}
}
/* 保留原有样式,但简化了结构 */
.formula-column {
display: flex;
justify-content: center;
......@@ -1006,29 +1117,11 @@ const isContentOverflow = (key, content) => {
margin-left: 2px;
}
}
}
}
.el-form-item {
align-items: center;
}
/* 表格区域 */
.auto-fit-header-table {
/* 优化超长文本的显示效果 */
::v-deep(.el-table) {
.cell {
/* padding: 0 .2133rem; */
}
::v-deep(.el-table__row) {
.el-table__cell {
padding: 0;
}
}
::v-deep(.column-style) {
.cell-style {
margin: 0 -12px;
......@@ -1074,10 +1167,6 @@ const isContentOverflow = (key, content) => {
}
}
}
}
::v-deep(.el-date-editor) {
width: 100%;
}
......
<!-- 档期陈列 -->
<template>
<CommonPlan :tableData="tableData"
:isLoading="isLoading"
:total="total"
:tableColumns="tableColumns"
:chooseColumns="chooseColumns"
:visibleProps="visibleProps"
:params="params"
@updateColumns="updateColumns"
@getTableList="getTableList" />
</template>
<script setup
lang="jsx">
import CommonPlan from '@/views/promotion/components/CommonPlan'
import { getDisplayScheduleDetail, submitDisplayPlan } from '@/api'
import { parseTime } from '@/utils'
import { onMounted } from 'vue';
/*************** 操作类型 ***************/
// 全部列
const baseColumns = ref([
// 一级类
{
label: "基础信息", // 类型标题
children: [
{
"label": "销售大区",
"prop": "regionName",
"visible": true,
"type": "string",
"fill": false,
"width": 90
},
{
"label": "销售战区",
"prop": "districtName",
"visible": true,
"type": "string",
"fill": false,
"width": 90
},
{
"label": "经销商 - 省份",
"prop": "dealerProvince",
"visible": true,
"type": "string",
"fill": false,
"width": 110
},
{
"label": "经销商 - 城市",
"prop": "dealerCity",
"visible": true,
"type": "string",
"fill": false,
"width": 110
},
{
"label": "经销商 - 代码",
"prop": "dealerCode",
"visible": true,
"type": "string",
"fill": false,
"width": 110
},
{
"label": "经销商名称",
"prop": "dealerName",
"visible": true,
"type": "string",
"fill": false,
"width": 200
},
{
"label": "经销商类型",
"prop": "dealerType",
"visible": true,
"type": "string",
"fill": false,
"width": 150
},
{
"label": "开户日期",
"prop": "openingDate",
"visible": true,
"type": "string",
"fill": false,
"width": 120
},
{
"label": "闭户日期",
"prop": "closingDate",
"visible": true,
"type": "string",
"fill": false,
"width": 120
},
{
"label": "大区总监",
"prop": "regionManager",
"visible": true,
"type": "string",
"fill": false,
"width": 80
},
{
"label": "战区经理",
"prop": "districtManager",
"visible": true,
"type": "string",
"fill": false,
"width": 80
},
{
"label": "城市经理",
"prop": "cityManager",
"visible": true,
"type": "string",
"fill": false,
"width": 80
},
{
"label": "系统名称",
"prop": "lineName",
"visible": true,
"type": "string",
"fill": false,
"width": 100
},
{
"label": "系统类型",
"prop": "lineType",
"visible": true,
"type": "string",
"fill": false,
"width": 110
},
{
"label": "渠道大类",
"prop": "channelDl",
"visible": true,
"type": "string",
"fill": false,
"width": 100
},
{
"label": "渠道小类",
"prop": "channelXl",
"visible": true,
"type": "string",
"fill": false,
"width": 150
},
{
"label": "系统业态",
"prop": "lineLf",
"visible": true,
"type": "string",
"fill": false,
"width": 120
},
{
"label": "门店数",
"prop": "storeCount",
"visible": true,
"type": "string",
"fill": false,
"width": 90
}
],
prop: 'baseColumns',
visible: true
},
{
label: "档期基础信息",
children: [
{
"label": "档期执行月份",
"prop": "promotionExecutionMonth",
"visible": true,
"type": "string",
"fill": false,
"width": 120
},
{
"label": "档期计划 - 促销规格",
"prop": "plannedPromotionSpec",
"visible": true,
"type": "string",
"fill": false,
"width": 160
},
{
"label": "档期计划 - 促销口味",
"prop": "plannedPromotionFlavor",
"visible": true,
"type": "string",
"fill": false,
"width": 160
}
],
prop: 'scheduleBaseColumns',
visible: true
},
{
label: "促销计划",
prop: 'scheduleFpColumns',
children: [
{
"label": "计划 - 档期规则",
"prop": "plannedPromotionRules",
"visible": true,
"type": "string",
"fill": false,
"width": 140
},
{
"label": "计划 - 档期前补差天数",
"prop": "plannedPrePromotionAdjustDays",
"visible": true,
"type": "string",
"fill": false,
"width": 160
},
{
"label": "计划 - 档期后补差天数",
"prop": "plannedPostPromotionAdjustDays",
"visible": true,
"type": "string",
"fill": false,
"width": 160
},
{
"label": "计划 - 档期开始时间",
"prop": "plannedPromotionStartDate",
"visible": true,
"type": "string",
"fill": false,
"width": 160
},
{
"label": "计划 - 补差开始时间",
"prop": "plannedAdjustmentStartDate",
"visible": true,
"type": "string",
"fill": false,
"width": 160
},
{
"label": "计划 - 档期结束时间",
"prop": "plannedPromotionEndDate",
"visible": true,
"type": "string",
"fill": false,
"width": 160
},
{
"label": "计划 - 补差结束时间",
"prop": "plannedAdjustmentEndDate",
"visible": true,
"type": "string",
"fill": false,
"width": 160
}
],
visible: true
},
{
label: '档期门店陈列计划',
prop: 'scheduleStoreColumns',
children: [
{
"label": "计划 - 档期陈列开始时间",
"prop": "plannedPromotionDisplayStartDate",
"visible": true,
"type": "string",
"fill": false,
"width": 180
},
{
"label": "计划 - 档期陈列结束时间",
"prop": "plannedPromotionDisplayEndDate",
"visible": true,
"type": "string",
"fill": false,
"width": 180
},
{
"label": "门店编码",
"prop": "storeCode",
"visible": true,
"type": "string",
"fill": false,
"width": 120
},
{
"label": "门店名称",
"prop": "storeName",
"visible": true,
"type": "string",
"fill": false,
"width": 170
},
{
"label": "门店类型",
"prop": "storeType",
"visible": true,
"type": "string",
"fill": false,
"width": 80
},
{
"label": "门店计数",
"prop": "storeCountNum",
"visible": true,
"type": "string",
"fill": false,
"width": 80
},
{
"label": "门店地址",
"prop": "storeAddress",
"visible": true,
"type": "string",
"fill": false,
"width": 280
},
{
"label": "计划端架 - 数量",
"prop": "plannedEndCapQty",
"visible": true,
"type": "string",
"fill": false,
"width": 140
},
{
"label": "计划端架 - 费用",
"prop": "plannedEndCapTotalCost",
"visible": true,
"type": "string",
"fill": false,
"width": 140
},
{
"label": "实际端架 - 数量",
"prop": "actualEndCapQty",
"visible": true,
"type": "string",
"fill": false,
"width": 140
},
{
"label": "实际端架 - 是否执行",
"prop": "actualEndCapExecuted",
"visible": true,
"type": "string",
"fill": false,
"width": 160
},
{
"label": "计划地堆 - 平米数(㎡)",
"prop": "plannedFloorStackArea",
"visible": true,
"type": "string",
"fill": false,
"width": 180
},
{
"label": "计划地堆 - 数量",
"prop": "plannedFloorStackQty",
"visible": true,
"type": "string",
"fill": false,
"width": 140
},
{
"label": "计划地堆 - 费用",
"prop": "plannedFloorStackTotalCost",
"visible": true,
"type": "string",
"fill": false,
"width": 140
},
{
"label": "实际地堆 - 平米数(㎡)",
"prop": "actualFloorStackArea",
"visible": true,
"type": "string",
"fill": false,
"width": 180
},
{
"label": "实际地堆 - 数量",
"prop": "actualFloorStackQty",
"visible": true,
"type": "string",
"fill": false,
"width": 140
},
{
"label": "实际地堆是否执行",
"prop": "actualFloorStackExecuted",
"visible": true,
"type": "string",
"fill": false,
"width": 140
},
{
"label": "计划其他陈列数量 + 形式",
"prop": "plannedOtherDisplay",
"visible": true,
"type": "string",
"fill": false,
"width": 180
},
{
"label": "计划其他陈列 - 总费用",
"prop": "plannedOtherDisplayTotalCost",
"visible": true,
"type": "string",
"fill": false,
"width": 160
},
{
"label": "实际其他陈列 - 数量 + 形式",
"prop": "actualOtherDisplay",
"visible": true,
"type": "string",
"fill": false,
"width": 190
},
{
"label": "实际其他陈列 - 是否执行",
"prop": "actualOtherDisplayExecuted",
"visible": true,
"type": "string",
"fill": false,
"width": 170
},
{
"label": "合计费用 - 费用",
"prop": "totalCost",
"visible": true,
"type": "string",
"fill": false,
"width": 140
},
{
"label": "档期陈列是否执行",
"prop": "promotionDisplayExecuted",
"visible": true,
"type": "string",
"fill": false,
"width": 140
},
{
"label": "付费陈列 - 是否",
"prop": "paidDisplay",
"visible": true,
"type": "string",
"fill": false,
"width": 120
},
{
"label": "当月拜访目标 - 是否",
"prop": "monthlyVisitTarget",
"visible": true,
"type": "string",
"fill": false,
"width": 160
}
],
visible: true
}
]);
// 表格列
const tableColumns = ref([])
// 右上角工具选择列
const chooseColumns = ref([])
// 计算列具体数据
const updateColumns = (operation) => {
return new Promise((resolve, reject) => {
if (operation === '平铺模式') {
chooseColumns.value = baseColumns.value
tableColumns.value = baseColumns.value.flatMap(item => {
if (item.children) {
return item.children.filter(child => !child.onlyFill);
}
return [];
});
} else if (operation === '填报模式') {
chooseColumns.value = baseColumns.value.flatMap(item => {
if (item.children) {
return item.children.filter(child => child.fill === true)
}
return [];
});
tableColumns.value = chooseColumns.value
}
resolve()
})
}
onMounted(() => {
updateColumns()
})
// 树形结构选择列默认有哪些
const visibleProps = computed(() => {
return baseColumns.value.flatMap(item => {
if (item.children) {
return item.children.filter(child => child.visible).map(child => child.prop);
}
return item.prop;
});
});
// 提交变更
const submitChange = async (row, col) => {
// 需要特殊处理的
// 1. 实际主货架-形式,为空时,置空实际主货架-数量
if (col.prop === 'actualMainShelfType' && !row.actualMainShelfType) {
row.actualMainShelfQty = 0
}
// 关联的公式计算列,需要特殊处理
for (const str of col.requestKey) {
const obj = tableColumns.value.find(item => item.prop === str)
console.log('obj', obj)
if (obj.type === 'formula') {
obj.func(row)
}
}
await submitDisplayPlan({
id: row.sadId,
[col.prop]: row[col.prop], // 当前修改列的值
...col.requestKey.reduce((acc, key) => ({ ...acc, [key]: row[key] }), {}), // 额外携带影响的列字段值
})
// // 这些提交变化的,同时会影响公式计算的,需要把公式计算的字段列结果,一起发给后台
// // 注意:实际主货架形式改变并为空时,要置空实际主货架数量
// if (col.prop === 'actualMainShelfType' && !row.actualMainShelfType) {
// row.actualMainShelfQty = ''
// // 并提交保存一次
// await submitDisplayPlan({
// id: row.sadId,
// actualMainShelfQty: row.actualMainShelfQty,
// })
// }
// // 主货架
// if (['actualMainShelfType', 'actualMainShelfQty'].includes(col.prop)) {
// await submitDisplayPlan({
// id: row.sadId,
// actualMainShelfExecuted: row.actualMainShelfExecuted,
// })
// }
// // 端架
// if (['actualEndCapQty'].includes(col.prop)) {
// await submitDisplayPlan({
// id: row.sadId,
// actualEndCapExecuted: row.actualEndCapExecuted,
// })
// }
// // 地堆
// if (['actualFloorStackArea', 'actualFloorStackQty'].includes(col.prop)) {
// await submitDisplayPlan({
// id: row.sadId,
// actualFloorStackExecuted: row.actualFloorStackExecuted,
// })
// }
// // 常规陈列是否执行
// await submitDisplayPlan({
// id: row.sadId,
// regularDisplayExecuted: row.regularDisplayExecuted,
// })
}
// 表格数据
const tableData = ref([])
const isLoading = ref(true)
const params = reactive({
pageNum: 1,
pageSize: 20,
})
const total = ref(0)
// 筛选工具
const getTableList = async () => {
isLoading.value = true
const res = await getDisplayScheduleDetail({
...params
})
res.data.rows.forEach(item => {
// 计划月份
item.promotionExecutionMonth = parseTime(item.promotionExecutionMonth, '{y}-{m}-{d}')
// 开户日期
item.openingDate = parseTime(item.openingDate, '{y}-{m}-{d}')
// 闭户日期
item.closingDate = parseTime(item.closingDate, '{y}-{m}-{d}')
// 计划 - 档期开始时间
item.plannedPromotionStartDate = parseTime(item.plannedPromotionStartDate, '{y}-{m}-{d}')
// 计划 - 补差开始时间
item.plannedAdjustmentStartDate = parseTime(item.plannedAdjustmentStartDate, '{y}-{m}-{d}')
// 计划 - 档期结束时间
item.plannedPromotionEndDate = parseTime(item.plannedPromotionEndDate, '{y}-{m}-{d}')
// 计划 - 补差结束时间
item.plannedAdjustmentEndDate = parseTime(item.plannedAdjustmentEndDate, '{y}-{m}-{d}')
// 计划 - 档期陈列开始时间
item.plannedPromotionDisplayStartDate = parseTime(item.plannedPromotionDisplayStartDate, '{y}-{m}-{d}')
// 计划 - 档期陈列结束时间
item.plannedPromotionDisplayEndDate = parseTime(item.plannedPromotionDisplayEndDate, '{y}-{m}-{d}')
})
tableData.value = res.data.rows
total.value = res.data.total
isLoading.value = false
}
getTableList()
</script>
<style scoped
lang="scss">
.container {
.el-tabs {
height: 100%;
display: flex;
flex-direction: column-reverse;
.el-tabs__content {
display: flex;
flex-direction: column;
height: 100%;
.el-tab-pane {
height: 100%;
display: flex;
flex-direction: column;
.pagination-container {
margin: 10px;
}
}
.formula-column {
display: flex;
justify-content: center;
align-items: center;
.el-icon {
margin-left: 2px;
}
}
}
}
.el-form-item {
align-items: center;
}
/* 表格区域 */
.auto-fit-header-table {
/* 优化超长文本的显示效果 */
.cell {
/* padding: 0 .2133rem; */
}
::v-deep(.el-table__row) {
.el-table__cell {
padding: 0;
}
}
::v-deep(.column-style) {
.cell-style {
margin: 0 -12px;
>div {
display: flex;
flex-direction: column;
align-items: flex-start;
>span {
text-align: left;
text-indent: 5px;
display: inline-block;
width: 100%;
background-color: #e1e2e6;
border-bottom: 1px solid #ebeef5;
}
}
/* 表格内下拉框 */
.el-select {
width: 100% !important;
padding: 10px;
}
.el-input {
padding: 10px;
}
}
}
}
}
</style>
\ No newline at end of file
<template>
<CommonPlan :tableData="tableData"
:isLoading="isLoading"
:total="total"
:tableColumns="tableColumns"
:chooseColumns="chooseColumns"
:visibleProps="visibleProps"
:params="params"
@updateColumns="updateColumns"
@getTableList="getTableList" />
</template>
<script setup
lang="jsx">
import CommonPlan from '@/views/promotion/components/CommonPlan'
import { getSnackPlanList, submitDisplayPlan } from '@/api'
import { parseTime } from '@/utils'
import { onMounted } from 'vue';
/*************** 操作类型 ***************/
// 全部列
const baseColumns = ref([
// 一级类
{
label: "基础信息",
children: [
{
"label": "销售大区",
"prop": "regionName",
"visible": true,
"type": "string",
"fill": false,
"width": 100
},
{
"label": "销售战区",
"prop": "districtName",
"visible": true,
"type": "string",
"fill": false,
"width": 100
},
{
"label": "经销商 - 省份",
"prop": "dealerProvince",
"visible": true,
"type": "string",
"fill": false,
"width": 110
},
{
"label": "经销商 - 城市",
"prop": "dealerCity",
"visible": true,
"type": "string",
"fill": false,
"width": 110
},
{
"label": "经销商 - 代码",
"prop": "dealerCode",
"visible": true,
"type": "string",
"fill": false,
"width": 110
},
{
"label": "经销商名称",
"prop": "dealerName",
"visible": true,
"type": "string",
"fill": false,
"width": 200
},
{
"label": "经销商类型",
"prop": "dealerType",
"visible": true,
"type": "string",
"fill": false,
"width": 150
},
{
"label": "开户日期",
"prop": "openingDate",
"visible": true,
"type": "string",
"fill": false,
"width": 100
},
{
"label": "闭户日期",
"prop": "closingDate",
"visible": true,
"type": "string",
"fill": false,
"width": 100
},
{
"label": "大区总监",
"prop": "regionManager",
"visible": true,
"type": "string",
"fill": false,
"width": 100
},
{
"label": "战区经理",
"prop": "districtManager",
"visible": true,
"type": "string",
"fill": false,
"width": 100
},
{
"label": "城市经理",
"prop": "cityManager",
"visible": true,
"type": "string",
"fill": false,
"width": 100
},
{
"label": "系统名称",
"prop": "lineName",
"visible": true,
"type": "string",
"fill": false,
"width": 100
},
{
"label": "系统类型",
"prop": "lineType",
"visible": true,
"type": "string",
"fill": false,
"width": 100
},
{
"label": "渠道大类",
"prop": "channelDl",
"visible": true,
"type": "string",
"fill": false,
"width": 100
},
{
"label": "渠道小类",
"prop": "channelXl",
"visible": true,
"type": "string",
"fill": false,
"width": 100
},
{
"label": "系统业态",
"prop": "lineLf",
"visible": true,
"type": "string",
"fill": false,
"width": 100
},
{
"label": "是否直营",
"prop": "isDirect",
"visible": true,
"type": "string",
"fill": false,
"width": 100
}
],
prop: 'baseColumns',
visible: true
},
{
label: "零食折扣门店陈列计划",
children: [
{
"label": "门店编码",
"prop": "storeCode",
"visible": true,
"type": "string",
"fill": false,
"width": 150
},
{
"label": "门店名称",
"prop": "storeName",
"visible": true,
"type": "string",
"fill": false,
"width": 180
},
{
"label": "门店计数",
"prop": "storeCount",
"visible": true,
"type": "string",
"fill": false,
"width": 100
},
{
"label": "门店地址",
"prop": "storeAddress",
"visible": true,
"type": "string",
"fill": false,
"width": 280
},
{
"label": "计划 - 陈列形式",
"prop": "plannedDisplay",
"visible": true,
"type": "string",
"fill": false,
"width": 140
},
{
"label": "实际 - 陈列形式",
"prop": "actualDisplay",
"visible": true,
"type": "string",
"fill": false,
"width": 130
},
{
"label": "陈列形式是否执行",
"prop": "displayExecuted",
"visible": true,
"type": "string",
"fill": false,
"width": 150
},
{
"label": "计划 - 端架数量(组)",
"prop": "plannedEndCapQty",
"visible": true,
"type": "string",
"fill": false,
"width": 160
},
{
"label": "实际 - 端架数量(组)",
"prop": "actualEndCapQty",
"visible": true,
"type": "string",
"fill": false,
"width": 160
},
{
"label": "端架数量是否执行",
"prop": "endCapQtyExecuted",
"visible": true,
"type": "string",
"fill": false,
"width": 140
},
{
"label": "零食陈列是否执行",
"prop": "snackDisplayExecuted",
"visible": true,
"type": "string",
"fill": false,
"width": 140
},
{
"label": "计划 - 陈列费用",
"prop": "totalCost",
"visible": true,
"type": "string",
"fill": false,
"width": 120
},
{
"label": "付费陈列 - 是否",
"prop": "paidDisplay",
"visible": true,
"type": "string",
"fill": false,
"width": 120
},
{
"label": "当月拜访目标 - 是否",
"prop": "monthlyVisitTarget",
"visible": true,
"type": "string",
"fill": false,
"width": 150
}
],
prop: 'snackColumns',
visible: true
}
]);
// 表格列
const tableColumns = ref([])
// 右上角工具选择列
const chooseColumns = ref([])
// 计算列具体数据
const updateColumns = (operation) => {
return new Promise((resolve, reject) => {
if (operation === '平铺模式') {
chooseColumns.value = baseColumns.value
tableColumns.value = baseColumns.value.flatMap(item => {
if (item.children) {
return item.children.filter(child => !child.onlyFill);
}
return [];
});
} else if (operation === '填报模式') {
chooseColumns.value = baseColumns.value.flatMap(item => {
if (item.children) {
return item.children.filter(child => child.fill === true)
}
return [];
});
tableColumns.value = chooseColumns.value
}
resolve()
})
}
onMounted(() => {
updateColumns()
})
// 树形结构选择列默认有哪些
const visibleProps = computed(() => {
return baseColumns.value.flatMap(item => {
if (item.children) {
return item.children.filter(child => child.visible).map(child => child.prop);
}
return item.prop;
});
});
// 提交变更
const submitChange = async (row, col) => {
// 需要特殊处理的
// 1. 实际主货架-形式,为空时,置空实际主货架-数量
if (col.prop === 'actualMainShelfType' && !row.actualMainShelfType) {
row.actualMainShelfQty = 0
}
// 关联的公式计算列,需要特殊处理
for (const str of col.requestKey) {
const obj = tableColumns.value.find(item => item.prop === str)
console.log('obj', obj)
if (obj.type === 'formula') {
obj.func(row)
}
}
await submitDisplayPlan({
id: row.sadId,
[col.prop]: row[col.prop], // 当前修改列的值
...col.requestKey.reduce((acc, key) => ({ ...acc, [key]: row[key] }), {}), // 额外携带影响的列字段值
})
// // 这些提交变化的,同时会影响公式计算的,需要把公式计算的字段列结果,一起发给后台
// // 注意:实际主货架形式改变并为空时,要置空实际主货架数量
// if (col.prop === 'actualMainShelfType' && !row.actualMainShelfType) {
// row.actualMainShelfQty = ''
// // 并提交保存一次
// await submitDisplayPlan({
// id: row.sadId,
// actualMainShelfQty: row.actualMainShelfQty,
// })
// }
// // 主货架
// if (['actualMainShelfType', 'actualMainShelfQty'].includes(col.prop)) {
// await submitDisplayPlan({
// id: row.sadId,
// actualMainShelfExecuted: row.actualMainShelfExecuted,
// })
// }
// // 端架
// if (['actualEndCapQty'].includes(col.prop)) {
// await submitDisplayPlan({
// id: row.sadId,
// actualEndCapExecuted: row.actualEndCapExecuted,
// })
// }
// // 地堆
// if (['actualFloorStackArea', 'actualFloorStackQty'].includes(col.prop)) {
// await submitDisplayPlan({
// id: row.sadId,
// actualFloorStackExecuted: row.actualFloorStackExecuted,
// })
// }
// // 常规陈列是否执行
// await submitDisplayPlan({
// id: row.sadId,
// regularDisplayExecuted: row.regularDisplayExecuted,
// })
}
// 表格数据
const tableData = ref([])
const isLoading = ref(true)
const params = reactive({
pageNum: 1,
pageSize: 20,
})
const total = ref(0)
// 筛选工具
const getTableList = async () => {
isLoading.value = true
const res = await getSnackPlanList({
...params
})
res.data.rows.forEach(item => {
// 计划月份
item.salesMonth = parseTime(item.salesMonth, '{y}-{m}')
// 开户日期
item.openingDate = parseTime(item.openingDate, '{y}-{m}-{d}')
// 闭户日期
item.closingDate = parseTime(item.closingDate, '{y}-{m}-{d}')
})
tableData.value = res.data.rows
total.value = res.data.total
isLoading.value = false
}
getTableList()
</script>
<style scoped
lang="scss">
.container {
.el-tabs {
height: 100%;
display: flex;
flex-direction: column-reverse;
.el-tabs__content {
display: flex;
flex-direction: column;
height: 100%;
.el-tab-pane {
height: 100%;
display: flex;
flex-direction: column;
.pagination-container {
margin: 10px;
}
}
.formula-column {
display: flex;
justify-content: center;
align-items: center;
.el-icon {
margin-left: 2px;
}
}
}
}
.el-form-item {
align-items: center;
}
/* 表格区域 */
.auto-fit-header-table {
/* 优化超长文本的显示效果 */
.cell {
/* padding: 0 .2133rem; */
}
::v-deep(.el-table__row) {
.el-table__cell {
padding: 0;
}
}
::v-deep(.column-style) {
.cell-style {
margin: 0 -12px;
>div {
display: flex;
flex-direction: column;
align-items: flex-start;
>span {
text-align: left;
text-indent: 5px;
display: inline-block;
width: 100%;
background-color: #e1e2e6;
border-bottom: 1px solid #ebeef5;
}
}
/* 表格内下拉框 */
.el-select {
width: 100% !important;
padding: 10px;
}
.el-input {
padding: 10px;
}
}
}
}
}
</style>
\ No newline at end of file
......@@ -3,6 +3,7 @@ import pxtorem from 'postcss-pxtorem'
import { defineConfig, loadEnv } from 'vite'
import createVitePlugins from './vite/plugins'
export default defineConfig(({ mode, command }) => {
const env = loadEnv(mode, process.cwd())
const { VITE_APP_PUBLIC_PATH } = env
......
......@@ -6,9 +6,10 @@ import createSvgIcon from './svg-icon'
import createCompression from './compression'
import createSetupExtend from './setup-extend'
import createHtmlPlugin from './create-html'
import vueJsx from '@vitejs/plugin-vue-jsx' // 导入 JSX 插件
export default function createVitePlugins(viteEnv, isBuild = false) {
const vitePlugins = [vue()]
const vitePlugins = [vue(), vueJsx()]
vitePlugins.push(...createAutoImportVant())
vitePlugins.push(createAutoImport())
vitePlugins.push(createSetupExtend())
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论