提交 410b7509 authored 作者: lidongxu's avatar lidongxu

Merge branch 'depart_change'

......@@ -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",
......
......@@ -26,6 +26,7 @@ export * from './monitor/job'
export * from './monitor/jobLog'
export * from './monitor/online'
export * from './monitor/server'
export * from './promotion/display_schedule'
export * from './promotion/plan'
export * from './promotion/task'
export * from './scm/logistics_receipt'
......
import request from '@/utils/request';
// 获取-陈列计划列表
export function getDisplayList(params) {
return request({
url: '/operation/sales/ap_display/query/page',
params
})
}
// 填报-陈列计划
export function submitDisplayPlan(data) {
// 遍历 data 每对 key value,发现 value 是 undefined 替换成空字符串
Object.keys(data).forEach(key => {
if (data[key] === undefined || data[key] === null) {
data[key] = ''
}
})
return request({
url: `/operation/sales/ap_display/core/${data.id}`,
method: 'PUT',
data: {
display: {
...data,
id: undefined // 不携带 id
}
}
});
}
// 获取档期计划列表
export function getDisplayScheduleList(params) {
return request({
url: '/operation/sales/ap_promotion/query/page',
params
})
}
// 填报-档期计划
export function submitDisplaySchedulePlan(data) {
Object.keys(data).forEach(key => {
if (data[key] === undefined || data[key] === null) {
data[key] = ''
}
})
return request({
url: `/operation/sales/ap_promotion/core/${data.id}`,
method: 'PUT',
data: {
promotion: data
}
});
}
// 获取-档期陈列列表
export function getDisplayScheduleDetail(params) {
return request({
url: '/operation/sales/ap_display/query/pro_page',
params
})
}
// 填报-档期陈列
export function submitDisplayScheduleDetail(data) {
Object.keys(data).forEach(key => {
if (data[key] === undefined || data[key] === null) {
data[key] = ''
}
})
return request({
url: `/operation/sales/ap_display/core_pro/${data.id}`,
method: 'PUT',
data: {
promotionDisplay: {
...data,
id: undefined // 不携带 id
}
}
});
}
// 获取-零食计划列表
export function getSnackPlanList(params) {
return request({
url: '/operation/sales/ap_display/query/snack_page',
params
})
}
// 填报-零食计划
export function submitSnackPlan(data) {
Object.keys(data).forEach(key => {
if (data[key] === undefined || data[key] === null) {
data[key] = ''
}
})
return request({
url: `/operation/sales/ap_display/core_snack/${data.id}`,
method: 'PUT',
data: {
snackDisplay: {
...data,
id: undefined // 不携带 id
}
}
});
}
\ No newline at end of file
......@@ -126,7 +126,7 @@ aside {
// 全局样式
// 容器
.app-container {
padding: 30px;
padding: 20px;
height: 100%;
// 内容区域
......
......@@ -19,7 +19,7 @@
icon="Refresh"
@click="refresh()" />
</el-tooltip>
<el-tooltip class="item"
<el-tooltip class="item "
effect="dark"
content="显隐列"
placement="top"
......@@ -31,7 +31,8 @@
<el-dropdown trigger="click"
:hide-on-click="false"
style="padding-left: 12px"
v-if="showColumnsType == 'checkbox'">
v-if="showColumnsType == 'checkbox'"
class="columns-dropdown">
<el-button circle
icon="Menu" />
<template #dropdown>
......@@ -39,14 +40,36 @@
<template v-for="item in columns"
:key="item.key">
<el-dropdown-item>
<el-checkbox :checked="item.visible"
@change="checkboxChange($event, item.label)"
<el-checkbox v-model="item.visible"
:label="item.label" />
</el-dropdown-item>
</template>
</el-dropdown-menu>
</template>
</el-dropdown>
<!-- 树结构选择方式 -->
<el-dropdown trigger="click"
:hide-on-click="false"
style="padding-left: 12px"
v-if="showColumnsType == 'tree'"
>
<el-button circle
icon="Menu" />
<template #dropdown>
<el-dropdown-menu class="tree-dropdown-menu">
<el-tree
:data="treeData"
show-checkbox
node-key="prop"
ref="columnTree"
:props="treeProps"
default-expand-all
check-on-click-node
:default-checked-keys="defaultCheckedKeys"
@check="handleTreeCheck" />
</el-dropdown-menu>
</template>
</el-dropdown>
</el-tooltip>
<!-- 自定义功能 -->
<slot></slot>
......@@ -79,7 +102,7 @@ const props = defineProps({
type: Boolean,
default: true,
},
/* 显隐列类型(transfer穿梭框、checkbox复选框) */
/* 显隐列类型(transfer穿梭框、checkbox复选框、tree树形结构) */
showColumnsType: {
type: String,
default: "checkbox",
......@@ -89,6 +112,19 @@ const props = defineProps({
type: Number,
default: 10,
},
/* 树形结构配置(可选) */
treeConfig: {
type: Object,
default: () => ({
labelKey: 'label',
childrenKey: 'children'
})
},
/* 树形结构默认选中的节点 */
defaultCheckedKeys: {
type: Array,
default: () => []
}
})
const emits = defineEmits(['update:showSearch', 'queryTable']);
......@@ -99,6 +135,15 @@ const value = ref([]);
const title = ref("显示/隐藏");
// 是否显示弹出层
const open = ref(false);
// 树结构数据
const treeData = ref([]);
// 树结构配置
const treeProps = {
label: props.treeConfig.labelKey || 'label',
children: props.treeConfig.childrenKey || 'children'
};
// 树引用
const columnTree = ref(null);
const style = computed(() => {
const ret = {};
......@@ -131,6 +176,82 @@ function showColumn() {
open.value = true;
}
// 树形结构节点勾选变化
function handleTreeCheck(currentNode, checkedInfo) {
const node = currentNode;
// 判断当前节点是否被勾选(检查当前节点prop是否在checkedKeys中)
const isChecked = checkedInfo.checkedKeys.includes(node.prop);
// 处理父节点的特殊情况:如果是父节点(有children),则更新其所有子节点
if (node.children && node.children.length > 0) {
// 获取该父节点下的所有子节点prop
const allChildProps = [];
const collectChildProps = (nodes) => {
nodes.forEach(child => {
if (child.children && child.children.length > 0) {
collectChildProps(child.children);
} else {
allChildProps.push(child.prop);
}
});
};
collectChildProps(node.children);
// 更新所有子节点的visible属性
allChildProps.forEach(prop => {
updateColumnVisibilityByProp(prop, isChecked);
});
} else {
// 对于单个节点,直接更新其visible属性
updateColumnVisibilityByProp(node.prop, isChecked);
}
}
// 新增:根据prop更新对应列的visible属性
function updateColumnVisibilityByProp(prop, visible) {
const updateColumn = (columns) => {
for (let i = 0; i < columns.length; i++) {
const item = columns[i];
if (item.children && item.children.length > 0) {
// 递归查找子节点
const found = updateColumn(item.children);
if (found) return true;
} else if (item.prop === prop) {
// 找到匹配的节点,更新其visible属性
item.visible = visible;
return true;
}
}
return false;
};
// 从props.columns中查找并更新
updateColumn(props.columns);
}
// 3. 修改initTreeData方法,确保正确处理prop字段
function initTreeData() {
if (props.columns && props.showColumnsType === 'tree') {
// 创建树数据但不深拷贝,保持引用关系
treeData.value = props.columns;
}
}
watch(() => props.columns, () => {
if (props.showColumnsType === 'tree') {
initTreeData();
}
})
// 再添加一个watcher来监听showColumnsType变化
watch(
() => props.showColumnsType,
(newType, oldType) => {
if (newType === 'tree') {
initTreeData();
}
}
)
if (props.showColumnsType == 'transfer') {
// 显隐列初始默认隐藏列
for (let item in props.columns) {
......@@ -138,13 +259,19 @@ if (props.showColumnsType == 'transfer') {
value.value.push(parseInt(item));
}
}
} else if (props.showColumnsType === 'tree') {
// 初始化树数据
initTreeData();
}
// 勾选
function checkboxChange(event, label) {
props.columns.filter(item => item.label == label)[0].visible = event;
}
// 勾选 - 修改为更健壮的实现
// function checkboxChange(event, label) {
// // 使用find方法而不是filter,更高效
// const column = props.columns.find(item => item.label === label);
// if (column) {
// column.visible = event;
// }
// }
</script>
<style lang='scss'
......@@ -174,4 +301,25 @@ function checkboxChange(event, label) {
}
}
/* 树结构下拉菜单样式 */
.tree-dropdown-menu {
max-height: 400px;
overflow-y: auto;
min-width: 300px;
padding: 10px;
}
:deep(.el-tree) {
max-height: 380px;
overflow-y: auto;
}
/* 添加选中节点文字蓝色样式 */
:deep(.el-tree-node.is-checked > .el-tree-node__content .el-tree-node__label) {
color: #409EFF;
}
:deep(.el-tree-node__content .el-checkbox__input.is-checked + .el-checkbox__label) {
color: #409EFF;
}
</style>
\ No newline at end of file
......@@ -69,7 +69,7 @@ import VConsole from 'vconsole'
// 创建vConsole实例
// 生产环境不加载
if (import.meta.env.VITE_APP_ENV !== 'production') {
if (import.meta.env.VITE_APP_ENV === 'staging') {
new VConsole()
}
......
......@@ -50,10 +50,7 @@ router.beforeEach((to, from, next) => {
})
})
}).catch(err => {
useUserStore().logOut().then(() => {
ElMessage.error(err)
next({ path: '/' })
})
})
} else {
next()
......
......@@ -7,6 +7,7 @@ import cache from '@/plugins/cache'
import { saveAs } from 'file-saver'
import useUserStore from '@/store/modules/user'
import { fsClientAuth } from '@/api'
import router from '@/router'
let downloadLoadingInstance;
// 是否显示重新登录
......@@ -103,14 +104,15 @@ service.interceptors.response.use(async res => {
// PC/移动端刷新 token 有效期
// await useUserStore().refreshTokenFn()
// return service(res.config)
// ElMessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', { confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' }).then(() => {
// isRelogin.show = false;
// useUserStore().logOut().then(() => {
ElMessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', { confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' }).then(() => {
isRelogin.show = false;
useUserStore().logOut().then(() => {
// location.href = '#/login';
// })
// }).catch(() => {
// isRelogin.show = false;
// });
router.push({ path: '/login' })
})
}).catch(() => {
isRelogin.show = false;
});
return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
} else if (code === 403) {
......
<template>
<div class="app-container">
<div class="container">
卤币明细
</div>
</div>
</template>
<script setup>
</script>
<style scoped
lang="scss"></style>
\ No newline at end of file
<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 "
:class="{ 'cell-no-padding': operation === '填报模式' }"
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.showOverflowTooltip"
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
class="overflow_wrap">
<!-- 超出部分显示省略号 -->
<span>
{{ row[col.prop] || '-' }}
</span>
</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', 'updateShowSearch'])
// 右上角工具
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')
}
watch(showSearch, (newVal) => {
emit('updateShowSearch', newVal)
})
</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;
}
}
&.cell-no-padding {
::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;
}
}
/* 操作提示列 */
.operation_tip_cell {
display: flex;
flex-direction: column;
align-items: flex-start;
p {
width: 100%;
margin: 0;
}
p:first-child {
background-color: #e1e2e6;
border-bottom: 1px solid #ebeef5;
}
p:last-child {
padding: 15px 0;
}
}
/* 超出部分显示省略号 */
.overflow_wrap {
width: 100%;
/* 固定宽度 */
white-space: nowrap;
/* 不换行 */
overflow: hidden;
/* 隐藏超出部分 */
text-overflow: ellipsis;
/* 显示省略号 */
display: inline-block;
}
</style>
\ No newline at end of file
<template>
<div class="search" v-show="showSearch">
<el-form :inline="true"
:model="queryParams"
class="demo-form-inline">
<el-form-item label="计划月份">
<el-date-picker v-model="queryParams.salesMonth"
type="month"
placeholder="选择计划月份"
@change="handleChange"
clearable />
</el-form-item>
<el-form-item label="大区/战区">
<el-input v-model="queryParams.deptName"
placeholder="请输入大区/战区"
@input="handleChange"
clearable />
</el-form-item>
<el-form-item label="经销商编码/名称">
<el-input v-model="queryParams.dealerCN"
placeholder="请输入经销商编码/名称"
@input="handleChange"
clearable />
</el-form-item>
<el-form-item label="系统名称">
<el-input v-model="queryParams.lineNameLike"
placeholder="请输入系统名称"
@input="handleChange"
clearable />
</el-form-item>
<el-form-item label="门店编码/名称">
<el-input v-model="queryParams.storeCN"
placeholder="请输入门店编码/名称"
@input="handleChange"
clearable />
</el-form-item>
</el-form>
</div>
</template>
<script setup>
const props = defineProps({
showSearch: {
type: Boolean,
default: true
}
})
const queryParams = reactive({
salesMonth: '',
deptName: '',
dealerCN: '',
lineNameLike: '',
storeCN: ''
})
const emits = defineEmits(['change'])
const handleChange = () => {
emits('change', queryParams)
}
</script>
<style scoped
lang="scss"></style>
\ No newline at end of file
<template>
<div class="app-container">
<div class="container">
<el-tabs v-model="activeName"
class="demo-tabs"
@tab-click="handleClickTabs">
<el-tab-pane label="常规陈列"
name="常规陈列">
<Display />
</el-tab-pane>
<el-tab-pane label="档期计划"
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>
</template>
<script setup>
import Display from './tabs/display.vue'
import Schedule from './tabs/schedule.vue'
import ScheduleDis from './tabs/schedule_dis.vue'
import Snack from './tabs/snack.vue'
const activeName = ref('常规陈列');
const handleClickTabs = (tab) => {
activeName.value = tab.name;
}
</script>
<style scoped
lang="scss">
.app-container {
padding: 10px;
.container {
padding: 10px;
.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;
}
.formula-column {
display: flex;
justify-content: center;
align-items: center;
.el-icon {
margin-left: 2px;
}
}
}
}
.el-form-item {
align-items: center;
}
}
}
</style>
\ No newline at end of file
<template>
<!-- 常规陈列 -->
<SearchList :showSearch="showSearch"
@change="searchChange" />
<CommonPlan :tableData="tableData"
:isLoading="isLoading"
:total="total"
:tableColumns="tableColumns"
:chooseColumns="chooseColumns"
:visibleProps="visibleProps"
:params="params"
@updateColumns="updateColumns"
@getTableList="getTableList"
@updateShowSearch="updateShowSearch" />
</template>
<script setup
lang="jsx">
import CommonPlan from '@/views/promotion/components/CommonPlan'
import SearchList from '@/views/promotion/components/SearchList'
import { getDisplayList, submitDisplayPlan } from '@/api'
import { parseTime } from '@/utils'
import { onMounted } from 'vue';
/*************** 操作类型 ***************/
// 全部列
const baseColumns = ref([
// 一级类
{
label: "基础信息", // 类型标题
children: [
{
label: '操作提示',
prop: 'operationTip', // 列属性
visible: true, // 是否显示
type: 'string', // 列类型
fill: true, // 是否为填报列
fixed: 'left', // 是否固定在左侧
width: 100,
onlyFill: true, // 只在填报列显示
render: (_, row, col) => {
return (
<div class="operation_tip_cell">
<p>计划</p>
<p>实际</p>
</div>
)
}
},
{
label: '计划月份', // 列标题
prop: 'salesMonth', // 列属性
visible: true, // 是否显示
type: 'string', // 列类型
fill: true, // 是否为填报列
fixed: 'left', // 是否固定在左侧
width: 90
},
{
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: 105
},
{
label: "经销商-城市",
prop: "dealerCity",
visible: true,
type: 'string',
fill: false,
width: 105
},
{
label: "经销商-代码",
prop: "dealerCode",
visible: true,
type: 'string',
fill: false,
width: 105
},
{
label: "经销商-类型",
showOverflowTooltip: true,
prop: "dealerType",
visible: true,
type: 'string',
fill: false,
width: 145
},
{
label: "开户日期",
prop: "openingDate",
visible: true,
type: 'string',
fill: false,
width: 105
},
{
label: "闭户日期",
prop: "closingDate",
visible: true,
type: 'string',
fill: false,
width: 105
},
{
label: "大区总监",
prop: "regionManager",
visible: true,
type: 'string',
fill: false,
width: 90
},
{
label: "战区经理",
prop: "districtManager",
visible: true,
type: 'string',
fill: false,
width: 90
},
{
label: "城市经理",
prop: "cityManager",
visible: true,
type: 'string',
fill: false,
width: 90
},
{
label: "门店名称",
prop: "storeNameCodeDealerName", //d 新增动态列(只在填报列显示)
visible: true,
type: 'string',
fill: true,
showOverflowTooltip: true,
onlyFill: true,// 仅仅在填报模式下生效
fixed: 'left',
width: 225,
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",
showOverflowTooltip: true,
visible: true,
type: 'string',
fill: false,
fixed: 'left',
width: 175
},
{
label: "门店编码",
prop: "storeCode",
showOverflowTooltip: true,
visible: true,
type: 'string',
fill: false,
fixed: 'left',
width: 125
},
{
label: "经销商名称",
prop: "dealerName",
showOverflowTooltip: true,
visible: true,
type: 'string',
fill: false,
fixed: 'left',
width: 215
},
{
label: "系统类型",
prop: "lineType",
visible: true,
type: 'string',
fill: false,
fixed: 'left',
width: 90
},
{
label: "门店-省份",
prop: "storeProvince",
visible: true,
type: 'string',
fill: false,
width: 90
},
{
label: "门店-城市",
prop: "storeCity",
visible: true,
type: 'string',
fill: false,
width: 90
},
{
label: "系统名称",
prop: "lineName",
showOverflowTooltip: true,
visible: true,
type: 'string',
fill: false,
width: 130
},
{
label: "渠道大类",
prop: "channelDl",
showOverflowTooltip: true,
visible: true,
type: 'string',
fill: false,
width: 90
},
{
label: "渠道小类",
prop: "channelXl",
showOverflowTooltip: true,
visible: true,
type: 'string',
fill: false,
width: 140
},
{
label: "门店类型",
prop: "storeType",
visible: true,
type: 'string',
showOverflowTooltip: true,
fill: false,
width: 90
},
{
label: "系统业态",
prop: "systemFormat",
showOverflowTooltip: true,
visible: true,
type: 'string',
fill: false,
width: 90
},
{
label: "门店面积",
prop: "storeArea",
showOverflowTooltip: true,
visible: true,
type: 'string',
fill: false,
width: 140
},
{
label: "门店分级(销量坎级)",
prop: "storeLevel",
visible: true,
showOverflowTooltip: true,
type: 'string',
fill: false,
width: 215
},
{
label: "门店地址",
prop: "storeAddress",
showOverflowTooltip: true,
visible: true,
type: 'string',
fill: false,
width: 325
},
{
label: "品项数",
prop: "productCount",
visible: true,
type: 'string',
fill: false,
width: 80
}
],
prop: 'baseColumns',
visible: true
},
{
label: "大业态测试",
children: [
{
label: "大业态测试-动销模型",
prop: "lfSalesModel",
visible: true,
type: 'string',
fill: false,
width: 155
},
{
label: "大业态测试-月均POS",
prop: "lfMonthlyPos",
visible: true,
type: 'string',
fill: false,
width: 155
}
],
prop: 'lfColumns',
visible: true
},
{
label: "费用计划",
prop: 'fpColumns',
children: [
{
label: "计划主货架-形式",
prop: "plannedMainShelfType",
visible: true,
type: 'string',
fill: false,
width: 130
},
{
label: "计划主货架-数量",
prop: "plannedMainShelfQty",
visible: true,
type: 'string',
fill: false,
width: 130
},
{
label: "计划主货架-单个费用",
prop: "plannedMainShelfUnitCost",
visible: true,
type: 'string',
fill: false,
width: 155
},
{
label: "计划主货架-总费用",
prop: "plannedMainShelfTotalCost",
visible: true,
type: 'string',
fill: false,
width: 145
},
{
label: "实际主货架-形式",
prop: "actualMainShelfType",
visible: true,
type: 'select',
options: [
{ label: '4纵', value: '4纵' },
{ label: '5纵', value: '5纵' },
{ label: '6纵', value: '6纵' },
{ label: '7纵', value: '7纵' },
{ label: '8纵及以上', value: '8纵及以上' }
],
referenceKey: "plannedMainShelfType",
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: "实际主货架-数量",
prop: "actualMainShelfQty",
visible: true,
type: 'select',
options: [
{ label: '0', value: 0 },
{ label: '1', value: 1 },
{ label: '2', value: 2 }
],
referenceKey: "plannedMainShelfQty",
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: "实际主货架-是否执行",
prop: "actualMainShelfExecuted",
visible: true,
type: 'formula',
func: (row) => {
row.actualMainShelfExecuted = ((parseInt(row.actualMainShelfType) >= parseInt(row.plannedMainShelfType)) && (parseInt(row.actualMainShelfQty) >= parseInt(row.plannedMainShelfQty))) ? '是' : '否';
return row.actualMainShelfExecuted;
},
formulaStr: '公式:(实际主货架形式 >= 计划主货架形式)并且(实际主货架数量 >= 计划主货架数量)',
fill: true,
width: 170
},
{
label: "计划端架-数量",
prop: "plannedEndCapQty",
visible: true,
type: 'string',
fill: false,
width: 115
},
{
label: "计划端架-总费用",
prop: "plannedEndCapTotalCost",
visible: true,
type: 'string',
fill: false,
width: 130
},
{
label: "计划端架-单个费用",
prop: "plannedEndCapUnitCost",
visible: true,
type: 'string',
fill: false,
width: 140
},
{
label: "实际端架-数量",
prop: "actualEndCapQty",
visible: true,
type: 'select',
options: [
{ label: '0', value: 0 },
{ label: '0.5', value: 0.5 },
{ label: '1', value: 1 },
{ label: '2', value: 2 }
],
referenceKey: "plannedEndCapQty",
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: "实际端架-是否执行",
prop: "actualEndCapExecuted",
visible: true,
type: 'formula',
func: (row) => {
row.actualEndCapExecuted = ((parseFloat(row.actualEndCapQty) >= parseFloat(row.plannedEndCapQty))) ? '是' : '否';
return row.actualEndCapExecuted;
},
formulaStr: '公式:(实际端架数量 >= 计划端架数量)',
fill: true,
width: 155
},
{
label: "计划地堆-平米数(㎡)",
prop: "plannedFloorStackArea",
visible: true,
type: 'string',
fill: false,
width: 170
},
{
label: "计划地堆-数量",
prop: "plannedFloorStackQty",
visible: true,
type: 'string',
fill: false,
width: 115
},
{
label: "计划主题地堆-是否",
prop: "plannedThemedFloorStack",
visible: true,
type: 'string',
fill: true,
width: 140
},
{
label: "计划地堆-总费用",
prop: "plannedFloorStackTotalCost",
visible: true,
type: 'string',
fill: false,
width: 130
},
{
label: "计划折算1㎡-单个费用",
prop: "plannedFloorStackUnitCostPerSqm",
visible: true,
type: 'string',
fill: false,
width: 160
},
{
label: "实际地堆-平米数(㎡)",
prop: "actualFloorStackArea",
visible: true,
type: 'select',
options: [
{ label: '0', value: 0 },
{ label: '0.5', value: 0.5 },
{ label: '0.8', value: 0.8 },
{ label: '1', value: 1 },
{ label: '2', value: 2 },
{ label: '3', value: 3 },
{ label: '4', value: 4 }
],
referenceKey: "plannedFloorStackArea",
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: "实际地堆-数量",
prop: "actualFloorStackQty",
visible: true,
type: 'select',
options: [
{ label: '0', value: 0 },
{ label: '1', value: 1 },
{ label: '2', value: 2 },
{ label: '3', value: 3 }
],
referenceKey: "plannedFloorStackQty",
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: "实际主题地堆-是否",
prop: "actualThemedFloorStack",
referenceKey: "actualFloorStackQty",
visible: true,
type: 'select',
options: [
{ label: '是', value: '是' },
{ label: '否', value: '否' }
],
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);
}}
disabled={!row[col.referenceKey]}
placeholder="">
{col.options.map(item => (
<el-option
key={item.value}
label={item.label}
value={item.value}
/>
))}
</el-select>
</div>
)
}
},
{
label: "实际地堆是否执行",
prop: "actualFloorStackExecuted",
visible: true,
type: 'formula',
func: (row) => {
row.actualFloorStackExecuted = ((parseFloat(row.actualFloorStackArea) >= parseFloat(row.plannedFloorStackArea)) && (parseInt(row.actualFloorStackQty) >= parseInt(row.plannedFloorStackQty))) ? '是' : '否';
return row.actualFloorStackExecuted;
},
formulaStr: '公式:(实际地堆平米数 >= 计划地堆平米数)并且(实际地堆数量 >= 计划地堆数量)',
fill: true,
width: 150
},
{
label: "计划多点陈列-数量+形式",
prop: "plannedMultiDisplay",
visible: true,
type: 'string',
fill: false,
width: 175
},
{
label: "计划多点陈列-总费用",
prop: "plannedMultiDisplayTotalCost",
visible: true,
type: 'string',
fill: false,
width: 155
},
{
label: "实际多点陈列-数量+形式",
prop: "actualMultiDisplay",
visible: true,
type: 'input',
func: (row) => {
// 没有输入内容时,则是否执行设置为否
row.actualMultiDisplayExecuted = !row.actualMultiDisplay ? '否' : '是'
},
referenceKey: "plannedMultiDisplay",
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: "实际多点陈列-是否执行",
prop: "actualMultiDisplayExecuted",
visible: true,
type: 'select',
options: [
{ label: '是', value: '是' },
{ label: '否', value: '否' }
],
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,
width: 115
},
{
label: "合计费用-费率",
prop: "totalCostRate",
visible: true,
type: 'string',
fill: false,
width: 115
},
{
label: "常规陈列是否执行",
prop: "regularDisplayExecuted",
visible: true,
type: 'formula',
func: (row) => {
// 实际主货架-执行 && 实际端架-执行 && 实际地堆-执行 && 实际多点陈列-执行
row.regularDisplayExecuted = (row.actualMainShelfExecuted === '是' && row.actualEndCapExecuted === '是' && row.actualFloorStackExecuted === '是' && row.actualMultiDisplayExecuted === '是') ? '是' : '否';
return row.regularDisplayExecuted;
},
formulaStr: '公式:(实际主货架执行,并且实际端架执行,并且实际地堆执行,并且实际多点陈列执行)',
fill: true,
width: 150
},
// {
// label: "综合标签-拜访辅助列",
// prop: "visitAssistTag",
// visible: true,
// type: 'string',
// fill: false,
// width: 160
// },
{
label: "付费陈列- 是否",
prop: "paidDisplay",
visible: true,
type: 'string',
fill: false,
width: 120
},
{
label: "计划-当月拜访目标",
prop: "monthlyVisitTarget",
visible: true,
type: 'string',
fill: false,
width: 135
},
{
label: "实际-当月是否拜访",
prop: "monthlyVisited",
visible: true,
type: 'string',
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: 180
}
],
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
}
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)
}
}
requestObj = col.requestKey.reduce((acc, key) => ({ ...acc, [key]: row[key] }), {})
}
await submitDisplayPlan({
id: row.sadId,
[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)
let params = reactive({
pageNum: 1,
pageSize: 20,
})
const total = ref(0)
// 筛选工具
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.updateTime = parseTime(item.updateTime, '{y}-{m}-{d} {h}:{i}:{s}')
// 动态新增列:门店名称+门店编码+经销山名称(填报模式下,合并到一起)
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 showSearch = ref(true)
const updateShowSearch = (newVal) => {
showSearch.value = newVal
}
const searchChange = (newVal) => {
// 计划月份
if (newVal.salesMonth) {
newVal.salesMonth = parseTime(newVal.salesMonth, '{y}-{m}-{d}')
}
params = { ...params, ...newVal }
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>
<!-- 档期计划 -->
<SearchList :showSearch="showSearch" @change="searchChange"/>
<CommonPlan :tableData="tableData"
:isLoading="isLoading"
:total="total"
:tableColumns="tableColumns"
:chooseColumns="chooseColumns"
:visibleProps="visibleProps"
:params="params"
@updateColumns="updateColumns"
@getTableList="getTableList"
@updateShowSearch="updateShowSearch" />
</template>
<script setup
lang="jsx">
import CommonPlan from '@/views/promotion/components/CommonPlan'
import SearchList from '@/views/promotion/components/SearchList'
import { getDisplayScheduleList, submitDisplaySchedulePlan } from '@/api'
import { parseTime } from '@/utils'
/*************** 操作类型 ***************/
function splitAndFilter(str) {
// 分割字符串并处理空值
return str.split(/[,,]/)
.map(item => item.trim()) // 去除前后空格
.filter(item => item.length > 0); // 过滤空字符串
}
// 基础列配置
const baseColumns = ref([
{
label: "基础信息",
children: [
{
label: '操作提示',
prop: 'operationTip', // 列属性
visible: true, // 是否显示
type: 'string', // 列类型
fill: true, // 是否为填报列
fixed: 'left', // 是否固定在左侧
width: 100,
onlyFill: true, // 只在填报列显示
render: (_, row, col) => {
return (
<div class="operation_tip_cell">
<p>计划</p>
<p>实际</p>
</div>
)
}
},
{
label: '计划月份',
prop: 'salesMonth',
visible: true,
type: 'string',
fill: true,
fixed: 'left',
width: 90
},
{
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: 105
},
{
label: "经销商-城市",
prop: "dealerCity",
visible: true,
type: "string",
fill: false,
width: 105
},
{
label: "经销商-代码",
prop: "dealerCode",
visible: true,
type: "string",
fill: false,
width: 105
},
{
label: "经销商名称",
prop: "dealerName",
visible: true,
showOverflowTooltip: true,
type: "string",
fill: true,
fixed: 'left',
width: 200
},
{
label: "经销商类型",
prop: "dealerType",
visible: true,
showOverflowTooltip: true,
type: "string",
fill: false,
width: 155
},
{
label: "开户日期",
prop: "openingDate",
visible: true,
type: "string",
fill: false,
width: 105
},
{
label: "闭户日期",
prop: "closingDate",
visible: true,
type: "string",
fill: false,
width: 105
},
{
label: "大区总监",
prop: "regionManager",
visible: true,
type: "string",
fill: false,
width: 90
},
{
label: "战区经理",
prop: "districtManager",
visible: true,
type: "string",
fill: false,
width: 90
},
{
label: "城市经理",
prop: "cityManager",
visible: true,
type: "string",
fill: false,
width: 90
},
{
label: "系统名称",
prop: "lineName",
visible: true,
type: "string",
fill: true,
fixed: 'left',
showOverflowTooltip: true,
width: 100
},
{
label: "系统类型",
prop: "lineType",
showOverflowTooltip: true,
visible: true,
type: "string",
fill: false,
width: 90
},
{
label: "渠道大类",
showOverflowTooltip: true,
prop: "channelDl",
visible: true,
type: "string",
fill: false,
width: 90
},
{
label: "渠道小类",
prop: "channelXl",
showOverflowTooltip: true,
visible: true,
type: "string",
fill: false,
width: 140
},
{
label: "系统业态",
showOverflowTooltip: true,
prop: "lineLf",
visible: true,
type: "string",
fill: false,
width: 90
},
{
label: "门店数",
prop: "storeCount",
visible: true,
type: "string",
fill: false,
width: 75
}
],
prop: 'baseColumns',
visible: true
},
{
label: "档期基础信息",
children: [
{
label: "档期执行月份",
prop: "promotionExecutionMonth",
visible: true,
type: "string",
fill: false,
width: 110
},
{
label: "档期计划-促销规格",
prop: "plannedPromotionSpec",
showOverflowTooltip: true,
visible: true,
type: "string",
fill: false,
width: 140
},
{
label: "档期计划-促销口味",
prop: "plannedPromotionFlavor",
showOverflowTooltip: true,
visible: true,
type: "string",
fill: false,
width: 140
},
{
label: "档期执行-促销规格",
prop: "actualPromotionSpec",
visible: true,
type: 'select',
options: [
{
label: '虎皮105g',
value: '虎皮105g'
},
{
label: '虎皮210g',
value: '虎皮210g'
},
{
label: '去骨72g',
value: '去骨72g'
},
{
label: '去骨138g',
value: '去骨138g'
},
{
label: '小鸡腿80g',
value: '小鸡腿80g'
},
{
label: '老卤95g',
value: '老卤95g'
},
{
label: '鸡肉豆堡',
value: '鸡肉豆堡'
},
{
label: '牛肉豆堡',
value: '牛肉豆堡'
}
],
referenceKey: "plannedPromotionSpec",
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>
)
},
// 请求时需要额外携带影响的列字段值
requestKey: ["specExecutionStatus", "promotionExecutionStatus"]
},
{
label: "档期规格是否执行",
prop: "specExecutionStatus",
visible: true,
type: 'formula',
func: (row) => {
row.specExecutionStatus = row.plannedPromotionSpec === row.actualPromotionSpec ? '是' : '否';
return row.specExecutionStatus;
},
formulaStr: '公式:(档期计划促销规格 = 档期执行促销规格)',
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], col)}>
<span >
{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',
visible: true
},
{
label: "合计费用",
prop: 'totalCostParent',
children: [
{
label: "合计费用",
prop: "totalCost",
visible: true,
type: "string",
fill: false,
width: 105
},
{
label: "合计费率",
prop: "totalCostRate",
visible: true,
type: "string",
fill: false,
width: 85
}
],
visible: true
},
{
label: "促销计划",
prop: 'promotionPlan',
children: [
{
label: "计划-档期形式",
prop: "plannedPromotionFormat",
visible: true,
type: "string",
fill: false,
width: 115
},
{
label: "计划-核销方式",
prop: "plannedVerificationMethod",
visible: true,
type: "string",
fill: false,
width: 115
},
{
label: "计划-档期规则",
prop: "plannedPromotionRules",
visible: true,
type: "string",
fill: false,
width: 115
},
{
label: "计划-档期前补差天数",
prop: "plannedPrePromotionAdjustDays",
visible: true,
type: "string",
fill: false,
width: 155
},
{
label: "计划-档期后补差天数",
prop: "plannedPostPromotionAdjustDays",
visible: true,
type: "string",
fill: false,
width: 155
},
{
label: "计划-档期开始时间",
prop: "plannedPromotionStartDate",
visible: true,
type: "string",
fill: false,
width: 140
},
{
label: "计划-补差开始时间",
prop: "plannedAdjustmentStartDate",
visible: true,
type: "string",
fill: false,
width: 140
},
{
label: "实际-档期开始时间",
prop: "actualPromotionStartDate",
visible: true,
type: 'date',
referenceKey: "plannedPromotionStartDate",
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"
/>
</div>
</div>
)
},
requestKey: ["timeExecutionStatus", 'promotionImplementationStatus']
},
{
label: "计划-档期结束时间",
prop: "plannedPromotionEndDate",
visible: true,
type: "string",
fill: false,
width: 140,
},
{
label: "计划-补差结束时间",
prop: "plannedAdjustmentEndDate",
visible: true,
type: "string",
fill: false,
width: 140
},
{
label: "实际-档期结束时间",
prop: "actualPromotionEndDate",
visible: true,
type: 'date',
fill: true,
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"
/>
</div>
</div>
)
},
requestKey: ["timeExecutionStatus", 'promotionImplementationStatus']
},
{
label: "档期是否开展",
prop: "promotionImplementationStatus",
visible: true,
type: 'formula',
func: (row) => {
row.promotionImplementationStatus = row.actualPromotionStartDate && row.actualPromotionEndDate ? '是' : '否';
return row.promotionImplementationStatus;
},
formulaStr: '公式:(实际档期开始时间不为空,并且实际档期结束时间不为空)',
fill: true,
width: 125
},
{
label: "时间是否执行",
prop: "timeExecutionStatus",
visible: true,
type: 'formula',
func: (row) => {
row.timeExecutionStatus = (parseTime(row.actualPromotionStartDate) === parseTime(row.plannedPromotionStartDate)) && parseTime(row.actualPromotionEndDate) === parseTime(row.plannedPromotionEndDate) ? '是' : '否';
return row.timeExecutionStatus;
},
formulaStr: '公式:(实际档期开始时间 = 计划档期开始时间,并且实际档期结束时间 = 计划档期结束时间)',
fill: true,
width: 125
},
{
label: "计划促销机制",
prop: "plannedPromotionMechanism",
visible: true,
type: "string",
fill: false,
width: 115
},
{
label: "实际促销机制",
prop: "actualPromotionMechanism",
visible: true,
type: 'input',
referenceKey: "plannedPromotionMechanism",
fill: true,
width: 185,
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: "promotionMechanismExecutionStatus",
visible: true,
type: 'select',
options: [
{
label: '是',
value: '是'
},
{
label: '否',
value: '否'
}
],
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,
width: 90
},
{
label: "档期备货量(袋)",
prop: "promotionStockQuantity",
visible: true,
type: 'input',
format: 'number',
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,
width: 135
},
{
label: "正常供价(单包)",
prop: "normalSupplyPrice",
visible: true,
type: "string",
fill: false,
width: 135
},
{
label: "日常零售价(单包)",
prop: "regularRetailPrice",
visible: true,
type: "string",
fill: false,
width: 150
},
{
label: "计划促销售价(单包)",
prop: "plannedPromotionPrice",
visible: true,
type: "string",
fill: false,
width: 165
},
{
label: "实际促销售价(单包)",
prop: "actualPromotionPrice",
visible: true,
type: 'input',
format: 'number',
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: "促销价是否执行",
prop: "promotionPriceExecutionStatus",
visible: true,
type: 'formula',
func: (row) => {
row.promotionPriceExecutionStatus = row.plannedPromotionPrice == row.actualPromotionPrice ? '是' : '否';
return row.promotionPriceExecutionStatus;
},
formulaStr: '公式:(计划促销售价 = 实际促销售价)',
fill: true,
width: 140
},
{
label: "正常经销商毛利现状",
prop: "normalDealerMarginStatus",
visible: true,
type: "string",
fill: false,
width: 150
},
{
label: "系统前台毛利保证",
prop: "systemFrontendMarginGuarantee",
visible: true,
type: "string",
fill: false,
width: 135
},
{
label: "系统后台毛利保证",
prop: "systemBackendMarginGuarantee",
visible: true,
type: "string",
fill: false,
width: 135
},
{
label: "系统促销毛利保证",
prop: "systemPromotionMarginGuarantee",
visible: true,
type: "string",
fill: false,
width: 135
},
{
label: "经销商毛利保证",
prop: "dealerMarginGuarantee",
visible: true,
type: "string",
fill: false,
width: 125
},
{
label: "单袋补差金额(费用)",
prop: "unitBagAdjustmentAmount",
visible: true,
type: "string",
fill: false,
width: 165
},
{
label: "补差费比(%)",
prop: "adjustmentCostRatio",
visible: true,
type: "string",
fill: false,
width: 125
},
{
label: "整体补差(费用)",
prop: "totalAdjustmentCost",
visible: true,
type: "string",
fill: false,
width: 135
},
{
label: "档期是否执行",
prop: "promotionExecutionStatus",
visible: true,
type: 'formula',
func: (row) => {
// 促销价是否执行,促销机制是否执行,促销规格是否执行,档期是否开展
row.promotionExecutionStatus = row.promotionPriceExecutionStatus == '是' && row.promotionMechanismExecutionStatus == '是' && row.specExecutionStatus == '是' && row.promotionImplementationStatus == '是' ? '是' : '否';
return row.promotionExecutionStatus;
},
formulaStr: '公式:(促销价是否执行 = 是,并且促销机制是否执行 = 是,并且促销规格是否执行 = 是,并且档期是否开展 = 是)',
fill: true,
width: 125
}
],
visible: true
},
{
label: "海报计划",
prop: 'posterPlan',
children: [
{
label: "计划-海报形式",
prop: "plannedPosterFormat",
visible: true,
type: "string",
fill: false,
width: 115
},
{
label: "计划-海报费用",
prop: "plannedPosterCost",
visible: true,
type: "string",
fill: false,
width: 115
},
{
label: "实际-海报形式",
prop: "actualPosterFormat",
visible: true,
type: 'select',
options: [
{
label: '电子',
value: '电子'
},
{
label: '纸质',
value: '纸质'
}
],
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: "海报是否执行",
prop: "posterExecutionStatus",
visible: true,
type: 'formula',
func: (row) => {
if (!row.actualPosterFormat) return row.posterExecutionStatus = '否'
row.posterExecutionStatus = row.plannedPosterFormat == row.actualPosterFormat ? '是' : '否';
return row.posterExecutionStatus;
},
formulaStr: '公式:(计划海报形式 = 实际海报形式)',
fill: true,
width: 125
}
],
visible: true
},
{
label: "档期陈列",
prop: 'displayPlan',
children: [
{
label: "门店数",
prop: "storeCountInt",
visible: true,
type: "string",
fill: false,
width: 75
},
{
label: "店均费用",
prop: "averageStoreCost",
visible: true,
type: "string",
fill: false,
width: 115
},
{
label: "费用",
prop: "cost",
visible: true,
type: "string",
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: 180
}
],
visible: true
}
]);
// 表格列数据
const tableColumns = ref([])
// 右上角工具选择列
const chooseColumns = ref([])
// 计算列具体数据
const updateColumns = (operation) => {
// 保存用户选择的列配置
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)
}
return [];
});
tableColumns.value = chooseColumns.value
}
}
// // 选择列默认选中哪些
const visibleProps = computed(() => {
// 分情况返回
return tableColumns.value.flatMap(item => {
if (item.children) {
return item.children.map(child => child.prop);
}
return item.prop;
});
});
// 提交变更
const submitChange = async (row, col) => {
// 需要特殊处理的
// 实际主货架-形式,为空时,置空实际主货架-数量
if (col.prop === 'actualMainShelfType' && !row.actualMainShelfType) {
row.actualMainShelfQty = 0
}
let requestObj = {}
if (col.requestKey) {
// 关联的公式计算列,需要特殊处理
for (const str of col.requestKey) {
const obj = tableColumns.value.find(item => item.prop == str)
console.log(obj, str, 'y')
if (obj.type === 'formula') {
obj.func(row)
}
}
requestObj = col.requestKey.reduce((acc, key) => ({ ...acc, [key]: row[key] }), {})
}
await submitDisplaySchedulePlan({
id: row.sapId,
[col.prop]: row[col.prop], // 当前修改列的值
...requestObj,
actualPromotionFlavor: Array.isArray(row.actualPromotionFlavor) ? row.actualPromotionFlavor.join(',') : '-', // 档期执行-促销口味,为数组时,转成字符串
actualPromotionStartDate: row.actualPromotionStartDate ? parseTime(row.actualPromotionStartDate, '{y}-{m}-{d}') : '', // 档期执行-促销开始日期,为字符串时,转成日期格式
})
}
// 表格数据
const tableData = ref([])
const isLoading = ref(true)
let params = reactive({
pageNum: 1,
pageSize: 20,
})
const total = ref(0)
// 获取表格数据
const getTableList = async () => {
isLoading.value = true
const res = await getDisplayScheduleList({
...params
})
// 处理日期格式
res.data.rows.forEach(item => {
item.salesMonth = parseTime(item.salesMonth, '{y}-{m}')
item.openingDate = parseTime(item.openingDate, '{y}-{m}-{d}')
item.plannedAdjustmentStartDate = parseTime(item.plannedAdjustmentStartDate, '{y}-{m}-{d}')
item.plannedAdjustmentEndDate = parseTime(item.plannedAdjustmentEndDate, '{y}-{m}-{d}')
item.plannedPromotionStartDate = parseTime(item.plannedPromotionStartDate, '{y}-{m}-{d}')
item.actualPromotionStartDate = parseTime(item.actualPromotionStartDate, '{y}-{m}-{d}')
item.plannedPromotionEndDate = parseTime(item.plannedPromotionEndDate, '{y}-{m}-{d}')
item.actualPromotionEndDate = parseTime(item.actualPromotionEndDate, '{y}-{m}-{d}')
item.totalCostRate = item.totalCostRate ? `${(item.totalCostRate).toFixed(2)}%` : '-'
item.adjustmentCostRatio = item.adjustmentCostRatio ? `${(item.adjustmentCostRatio).toFixed(2)}%` : '-'
// 档期执行-促销口味从字符串转成数组
item.actualPromotionFlavor = item.actualPromotionFlavor ? item.actualPromotionFlavor.split(',') : []
// 修改时间
item.updateTime = parseTime(item.updateTime, '{y}-{m}-{d} {h}:{i}:{s}')
})
tableData.value = res.data.rows
total.value = res.data.total
isLoading.value = false
}
onMounted(() => {
updateColumns()
getTableList()
})
const isContentOverflow = (key, content, col) => {
if (!content) return false; // 没有内容则直接禁用
// 创建 DOM 计算
const tempSpan = document.createElement('span');
tempSpan.textContent = content;
tempSpan.className = 'ellipsis-tooltip';
tempSpan.style.visibility = 'hidden';
tempSpan.style.position = 'absolute';
document.body.appendChild(tempSpan);
// 获取元素宽度和内容宽度
const width = tempSpan.getBoundingClientRect().width;
// 移除临时元素
document.body.removeChild(tempSpan);
// 当内容宽度大于元素宽度时,表示内容溢出
return width > col.width;
};
const showSearch = ref(true)
const updateShowSearch = (newVal) => {
showSearch.value = newVal
}
const searchChange = (newVal) => {
// 计划月份
if (newVal.salesMonth) {
newVal.salesMonth = parseTime(newVal.salesMonth, '{y}-{m}-{d}')
}
params = { ...params, ...newVal }
getTableList()
}
</script>
<style scoped
lang="scss">
/* 保留原有样式,但简化了结构 */
.formula-column {
display: flex;
justify-content: center;
align-items: center;
.el-icon {
margin-left: 2px;
}
}
::v-deep(.el-table) {
.cell {
padding: 0;
}
.cell-style {
margin: 0 -12px;
>div {
display: flex;
flex-direction: column;
align-items: flex-start;
>span.ellipsis-tooltip {
text-align: left;
text-indent: 5px;
display: inline-block;
width: 100%;
background-color: #e1e2e6;
border-bottom: 1px solid #ebeef5;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: inline-block;
max-width: 100%;
}
}
/* 表格内下拉框 */
.el-select {
width: 100% !important;
padding: 10px;
}
.el-input {
padding: 10px;
}
.date-picker {
padding: 10px;
.el-input {
padding: 0;
}
}
}
}
::v-deep(.el-date-editor) {
width: 100%;
}
</style>
\ No newline at end of file
<template>
<!-- 档期陈列 -->
<SearchList :showSearch="showSearch" @change="searchChange"/>
<CommonPlan :tableData="tableData"
:isLoading="isLoading"
:total="total"
:tableColumns="tableColumns"
:chooseColumns="chooseColumns"
:visibleProps="visibleProps"
:params="params"
@updateColumns="updateColumns"
@getTableList="getTableList"
@updateShowSearch="updateShowSearch" />
</template>
<script setup
lang="jsx">
import CommonPlan from '@/views/promotion/components/CommonPlan'
import SearchList from '@/views/promotion/components/SearchList'
import { getDisplayScheduleDetail, submitDisplayScheduleDetail } from '@/api'
import { parseTime } from '@/utils'
import { onMounted } from 'vue';
/*************** 操作类型 ***************/
// 全部列
const baseColumns = ref([
// 一级类
{
label: "基础信息", // 类型标题
children: [
{
label: '操作提示',
prop: 'operationTip', // 列属性
visible: true, // 是否显示
type: 'string', // 列类型
fill: true, // 是否为填报列
fixed: 'left', // 是否固定在左侧
width: 100,
onlyFill: true, // 只在填报列显示
render: (_, row, col) => {
return (
<div class="operation_tip_cell">
<p>计划</p>
<p>实际</p>
</div>
)
}
},
{
label: '计划月份', // 列标题
prop: 'salesMonth', // 列属性
visible: true, // 是否显示
type: 'string', // 列类型
fill: true, // 是否为填报列
fixed: 'left', // 是否固定在左侧
width: 90
},
{
"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": true,
"width": 200,
showOverflowTooltip: true,
fixed: 'left'
},
{
"label": "经销商类型",
"prop": "dealerType",
showOverflowTooltip: true,
"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": true,
"width": 100,
fixed: 'left'
},
{
"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",
showOverflowTooltip: true,
"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",
showOverflowTooltip: true,
"fill": false,
"width": 120
},
{
"label": "门店名称",
"prop": "storeName",
"visible": true,
"type": "string",
showOverflowTooltip: true,
"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",
showOverflowTooltip: true,
"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": "select",
referenceKey: "plannedEndCapQty",
"options": [
{
"label": "0",
"value": 0
},
{
"label": "0.5",
"value": 0.5
},
{
"label": "1",
"value": 1
},
{
"label": "2",
"value": 2
}
],
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>
)
},
"fill": true,
"width": 140,
requestKey: ["actualEndCapExecuted", "promotionDisplayExecuted"]
},
{
"label": "实际端架 - 是否执行",
"prop": "actualEndCapExecuted",
"visible": true,
"type": "formula",
"fill": true,
"width": 160,
func: (row) => {
row.actualEndCapExecuted = row.actualEndCapQty >= row.plannedEndCapQty ? '是' : '否'
return row.actualEndCapExecuted
},
formulaStr: "执行端架数量 >= 计划端架数量"
},
{
"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": "select",
"fill": true,
"width": 180,
referenceKey: "plannedFloorStackArea",
"options": [
{
"label": "0",
"value": 0
},
{
"label": "0.5",
"value": 0.5
},
{
"label": "0.8",
"value": 0.8
},
{
"label": "1",
"value": 1
},
{
"label": "2",
"value": 2
},
{
"label": "3",
"value": 3
},
{
"label": "4",
"value": 4
}
],
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", "promotionDisplayExecuted"]
},
{
"label": "实际地堆 - 数量",
"prop": "actualFloorStackQty",
"visible": true,
"type": "select",
"fill": true,
"width": 140,
referenceKey: "plannedFloorStackQty",
"options": [
{
"label": "0",
"value": 0
},
{
"label": "1",
"value": 1
},
{
"label": "2",
"value": 2
},
{
"label": "3",
"value": 3
}
],
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", "promotionDisplayExecuted"]
},
{
"label": "实际地堆是否执行",
"prop": "actualFloorStackExecuted",
"visible": true,
"type": "formula",
"fill": true,
"width": 150,
func: (row) => {
row.actualFloorStackExecuted = row.actualFloorStackArea >= row.plannedFloorStackArea && row.actualFloorStackQty >= row.plannedFloorStackQty ? '是' : '否'
return row.actualFloorStackExecuted
},
formulaStr: "执行平米数 >= 计划平米数,并且 执行地堆数量 >= 计划地堆数量"
},
{
"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": "input",
"fill": true,
"width": 190,
referenceKey: "plannedOtherDisplay",
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: ["actualOtherDisplayExecuted", "promotionDisplayExecuted"]
},
{
"label": "实际其他陈列 - 是否执行",
"prop": "actualOtherDisplayExecuted",
"visible": true,
"type": "formula",
"fill": true,
"width": 190,
func: (row) => {
row.actualOtherDisplayExecuted = !row.actualOtherDisplay ? '否' : '是'
return row.actualOtherDisplayExecuted
},
formulaStr: "执行其他陈列数量 + 形式,是否等于计划其他陈列数量 + 形式"
},
{
"label": "合计费用 - 费用",
"prop": "totalCost",
"visible": true,
"type": "string",
"fill": false,
"width": 140
},
{
"label": "档期陈列是否执行",
"prop": "promotionDisplayExecuted",
"visible": true,
"type": "formula",
"fill": true,
"width": 160,
func: (row) => {
// 实际端架-是否执行 && 实际地堆是否执行 && 实际其他陈列-是否执行
row.promotionDisplayExecuted = row.actualEndShelfExecuted === '是' && row.actualFloorStackExecuted === '是' && row.actualOtherDisplayExecuted === '是' ? '是' : '否'
return row.promotionDisplayExecuted
},
formulaStr: "实际端架-是否执行 === '是',并且 实际地堆是否执行 === '是',并且 实际其他陈列-是否执行 === '是'"
},
{
"label": "付费陈列 - 是否",
"prop": "paidDisplay",
"visible": true,
"type": "string",
"fill": false,
"width": 120
},
{
"label": "当月拜访目标 - 是否",
"prop": "monthlyVisitTarget",
"visible": true,
"type": "string",
"fill": false,
"width": 160
},
{
label: '修改人',
prop: 'updateBy',
visible: true,
type: 'string',
fill: false,
width: 120
},
{
label: '修改时间',
prop: 'updateTime',
visible: true,
type: 'string',
fill: false,
width: 180
}
],
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 submitDisplayScheduleDetail({
id: row.sapdId,
[col.prop]: row[col.prop], // 当前修改列的值
...col.requestKey.reduce((acc, key) => ({ ...acc, [key]: row[key] }), {}), // 额外携带影响的列字段值
})
}
// 表格数据
const tableData = ref([])
const isLoading = ref(true)
let 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.salesMonth = parseTime(item.salesMonth, '{y}-{m}')
// 开户日期
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}')
// 修改时间
item.updateTime = parseTime(item.updateTime, '{y}-{m}-{d} {h}:{i}:{s}')
})
tableData.value = res.data.rows
total.value = res.data.total
isLoading.value = false
}
getTableList()
const showSearch = ref(true)
const updateShowSearch = (newVal) => {
showSearch.value = newVal
}
const searchChange = (newVal) => {
// 计划月份
if (newVal.salesMonth) {
newVal.salesMonth = parseTime(newVal.salesMonth, '{y}-{m}-{d}')
}
params = { ...params, ...newVal }
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>
<!-- 零食陈列 -->
<SearchList :showSearch="showSearch" @change="searchChange"/>
<CommonPlan :tableData="tableData"
:isLoading="isLoading"
:total="total"
:tableColumns="tableColumns"
:chooseColumns="chooseColumns"
:visibleProps="visibleProps"
:params="params"
@updateColumns="updateColumns"
@getTableList="getTableList"
@updateShowSearch="updateShowSearch" />
</template>
<script setup
lang="jsx">
import CommonPlan from '@/views/promotion/components/CommonPlan'
import SearchList from '@/views/promotion/components/SearchList'
import { getSnackPlanList, submitSnackPlan } from '@/api'
import { parseTime } from '@/utils'
import { onMounted } from 'vue';
/*************** 操作类型 ***************/
// 全部列
const baseColumns = ref([
// 一级类
{
label: "基础信息",
children: [
{
label: '操作提示',
prop: 'operationTip', // 列属性
visible: true, // 是否显示
type: 'string', // 列类型
fill: true, // 是否为填报列
fixed: 'left', // 是否固定在左侧
width: 100,
onlyFill: true, // 只在填报列显示
render: (_, row, col) => {
return (
<div class="operation_tip_cell">
<p>计划</p>
<p>实际</p>
</div>
)
}
},
{
label: '计划月份', // 列标题
prop: 'salesMonth', // 列属性
visible: true, // 是否显示
type: 'string', // 列类型
fill: true, // 是否为填报列
fixed: 'left', // 是否固定在左侧
width: 90
},
{
"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,
showOverflowTooltip: true,
"type": "string",
"fill": true,
"width": 200,
fixed: 'left'
},
{
"label": "经销商类型",
"prop": "dealerType",
"visible": true,
showOverflowTooltip: 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": true,
"width": 100,
fixed: 'left'
},
{
"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",
showOverflowTooltip: true,
"fill": false,
"width": 180
},
{
"label": "门店计数",
"prop": "storeCount",
"visible": true,
"type": "string",
"fill": false,
"width": 100
},
{
"label": "门店地址",
"prop": "storeAddress",
"visible": true,
showOverflowTooltip: true,
"type": "string",
"fill": false,
"width": 280
},
{
"label": "计划 - 陈列形式",
"prop": "plannedDisplay",
"visible": true,
"type": "string",
"fill": false,
"width": 140
},
{
"label": "实际 - 陈列形式",
"prop": "actualDisplay",
"visible": true,
"type": "select",
referenceKey: "plannedDisplay",
"fill": true,
"width": 160,
"options": [
{
"label": "端架",
"value": "端架"
},
{
"label": "收银台",
"value": "收银台"
},
{
"label": "端架和收银台",
"value": "端架和收银台"
}
],
render: (_, row, col) => {
return (
<div>
<el-tooltip placement="top" content={row[col.referenceKey] || '-'} disabled={!isContentOverflow(col.referenceKey, row[col.referenceKey], col)}>
<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="">
{col.options.map(item => (
<el-option
key={item.value}
label={item.label}
value={item.value}
/>
))}
</el-select>
</div>
)
},
requestKey: ["displayExecuted", "snackDisplayExecuted"]
},
{
"label": "陈列形式是否执行",
"prop": "displayExecuted",
"visible": true,
"type": "formula",
"fill": true,
"width": 150,
func: (row) => {
row.displayExecuted = row.actualDisplay === row.plannedDisplay ? '是' : '否'
return row.displayExecuted
},
formulaStr: "实际陈列形式 === 计划陈列形式"
},
{
"label": "计划 - 端架数量(组)",
"prop": "plannedEndCapQty",
"visible": true,
"type": "string",
"fill": false,
"width": 160
},
{
"label": "实际 - 端架数量(组)",
"prop": "actualEndCapQty",
"visible": true,
"type": "select",
referenceKey: "plannedEndCapQty",
"fill": true,
"options": [
{
"label": "0",
"value": 0
},
{
"label": "0.5",
"value": 0.5
},
{
"label": "1",
"value": 1
},
{
"label": "2",
"value": 2
}
],
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>
)
},
"width": 160,
requestKey: ["endCapQtyExecuted", "snackDisplayExecuted"]
},
{
"label": "端架数量是否执行",
"prop": "endCapQtyExecuted",
"visible": true,
"type": "formula",
"fill": true,
"width": 150,
func: (row) => {
row.endCapQtyExecuted = row.actualEndCapQty >= row.plannedEndCapQty ? '是' : '否'
return row.endCapQtyExecuted
},
formulaStr: "执行端架数量 == 计划端架数量"
},
{
"label": "零食陈列是否执行",
"prop": "snackDisplayExecuted",
"visible": true,
"type": "formula",
"fill": true,
"width": 150,
func: (row) => {
row.snackDisplayExecuted = row.displayExecuted === '是' && row.endCapQtyExecuted === '是' ? '是' : '否'
return row.snackDisplayExecuted
},
formulaStr: "陈列形式是否执行 === 是,并且端架数量是否执行 === 是"
},
{
"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
},
{
label: '修改人',
prop: 'updateBy',
visible: true,
type: 'string',
fill: false,
width: 120
},
{
label: '修改时间',
prop: 'updateTime',
visible: true,
type: 'string',
fill: false,
width: 180
}
],
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 submitSnackPlan({
id: row.sasdId,
[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)
let 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}')
// 修改时间
item.updateTime = parseTime(item.updateTime, '{y}-{m}-{d} {h}:{i}:{s}')
})
tableData.value = res.data.rows
total.value = res.data.total
isLoading.value = false
}
getTableList()
const isContentOverflow = (key, content, col) => {
if (!content) return false; // 没有内容则直接禁用
// 创建 DOM 计算
const tempSpan = document.createElement('span');
tempSpan.textContent = content;
tempSpan.className = 'ellipsis-tooltip';
tempSpan.style.visibility = 'hidden';
tempSpan.style.position = 'absolute';
document.body.appendChild(tempSpan);
// 获取元素宽度和内容宽度
const width = tempSpan.getBoundingClientRect().width;
// 移除临时元素
document.body.removeChild(tempSpan);
// 当内容宽度大于元素宽度时,表示内容溢出
return width > col.width;
};
const showSearch = ref(true)
const updateShowSearch = (newVal) => {
showSearch.value = newVal
}
const searchChange = (newVal) => {
// 计划月份
if (newVal.salesMonth) {
newVal.salesMonth = parseTime(newVal.salesMonth, '{y}-{m}-{d}')
}
params = { ...params, ...newVal }
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
......@@ -197,7 +197,7 @@ const columns = ref([
{
label: '快递单号',
prop: 'expressNo',
width: 150
width: 220
},
{
label: 'DD 单号',
......
......@@ -63,6 +63,9 @@
<el-table-column prop="deptName"
label="部门名称"
width="260"></el-table-column>
<el-table-column prop="deptCode"
label="部门编码"
width="200"></el-table-column>
<el-table-column prop="orderNum"
label="排序"
width="200"></el-table-column>
......@@ -110,7 +113,8 @@
<el-dialog :title="title"
v-model="open"
append-to-body
draggable overflow>
draggable
overflow>
<el-form ref="deptRef"
:model="form"
:rules="rules"
......@@ -135,6 +139,14 @@
placeholder="请输入部门名称" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="部门编码"
prop="deptCode">
<el-input v-model="form.deptCode"
placeholder="请输入 WB 字符开头数字字母组合字符"
maxlength="20" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="显示排序"
prop="orderNum">
......@@ -176,6 +188,7 @@
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
......@@ -216,9 +229,23 @@
rules: {
parentId: [{ required: true, message: "上级部门不能为空", trigger: "blur" }],
deptName: [{ required: true, message: "部门名称不能为空", trigger: "blur" }],
deptCode: [{ required: true, message: "部门编码不能为空", trigger: "blur" }, {
min: 3, max: 10, message: "部门编码长度必须在 3-10 位之间", trigger: "blur"
},
{
validator: (rule, value, callback) => {
// 部门编码只能是 WB 字符开头数字字母组合的 1-10 位字符
if (!/^WB[0-9a-zA-Z-_]{1,8}$/.test(value)) {
callback(new Error("部门编码只能是 WB 字符开头,数字字母组合的字符"));
} else {
callback();
}
}, trigger: "blur"
}],
orderNum: [{ required: true, message: "显示排序不能为空", trigger: "blur" }],
email: [{ type: "email", message: "请输入正确的邮箱地址", trigger: ["blur", "change"] }],
phone: [{ pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: "请输入正确的手机号码", trigger: "blur" }]
phone: [{ pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: "请输入正确的手机号码", trigger: "blur" }],
},
});
......
......@@ -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 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论