Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
W
wangxiaolu-sfa-ui
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
sfa
wangxiaolu-sfa-ui
Commits
d106e050
提交
d106e050
authored
12月 15, 2025
作者:
lidongxu
浏览文件
操作
浏览文件
下载
差异文件
Merge branch 'ap' into dev
上级
dc61f15e
97571154
全部展开
隐藏空白字符变更
内嵌
并排
正在显示
21 个修改的文件
包含
1965 行增加
和
41 行删除
+1965
-41
request.js
src/utils/request.js
+58
-39
index.vue
src/views/mobile/pages/menu/index.vue
+3
-2
index.vue
...pages/storeExecution/dashboard/components/Table/index.vue
+287
-0
index.vue
src/views/mobile/pages/storeExecution/dashboard/index.vue
+112
-0
data.jsx
...views/mobile/pages/storeExecution/dashboard/tabs/data.jsx
+427
-0
display.vue
...ws/mobile/pages/storeExecution/dashboard/tabs/display.vue
+101
-0
schedule.vue
...s/mobile/pages/storeExecution/dashboard/tabs/schedule.vue
+96
-0
schedule_dis.vue
...bile/pages/storeExecution/dashboard/tabs/schedule_dis.vue
+94
-0
six_little_diamonds.vue
...ges/storeExecution/dashboard/tabs/six_little_diamonds.vue
+94
-0
snack.vue
...iews/mobile/pages/storeExecution/dashboard/tabs/snack.vue
+181
-0
three_two_seconds.vue
...pages/storeExecution/dashboard/tabs/three_two_seconds.vue
+97
-0
index.vue
...ges/storeExecution/report/components/SearchList/index.vue
+265
-0
index.vue
...le/pages/storeExecution/report/components/Table/index.vue
+0
-0
index.vue
src/views/mobile/pages/storeExecution/report/index.vue
+150
-0
data.jsx
src/views/mobile/pages/storeExecution/report/tabs/data.jsx
+0
-0
display.vue
...views/mobile/pages/storeExecution/report/tabs/display.vue
+0
-0
schedule.vue
...iews/mobile/pages/storeExecution/report/tabs/schedule.vue
+0
-0
schedule_dis.vue
.../mobile/pages/storeExecution/report/tabs/schedule_dis.vue
+0
-0
six_little_diamonds.vue
.../pages/storeExecution/report/tabs/six_little_diamonds.vue
+0
-0
snack.vue
src/views/mobile/pages/storeExecution/report/tabs/snack.vue
+0
-0
three_two_seconds.vue
...le/pages/storeExecution/report/tabs/three_two_seconds.vue
+0
-0
没有找到文件。
src/utils/request.js
浏览文件 @
d106e050
...
...
@@ -16,6 +16,58 @@ export let isRelogin = { show: false };
// 促销系统后台 baseURL
export
const
promotionBaseURL
=
import
.
meta
.
env
.
VITE_APP_PROMOTION
// 错误消息队列和状态管理
let
errorMessageQueue
=
[];
let
isShowingError
=
false
;
let
lastErrorTime
=
0
;
const
ERROR_DEBOUNCE_TIME
=
1000
;
// 1秒内的错误视为同一批
// 显示错误消息(单例模式)
function
showErrorMessage
(
message
,
type
=
'error'
)
{
const
now
=
Date
.
now
();
// 如果正在显示错误消息,或者距离上次错误时间很短,将消息加入队列
if
(
isShowingError
||
(
now
-
lastErrorTime
<
ERROR_DEBOUNCE_TIME
))
{
// 检查队列中是否已有相同消息,避免重复
const
existingIndex
=
errorMessageQueue
.
findIndex
(
item
=>
item
.
message
===
message
);
if
(
existingIndex
===
-
1
)
{
errorMessageQueue
.
push
({
message
,
type
});
}
return
;
}
// 标记为正在显示
isShowingError
=
true
;
lastErrorTime
=
now
;
// 显示当前错误消息
ElMessage
({
message
:
message
,
type
:
type
,
duration
:
3000
,
// 适当延长显示时间
onClose
:
()
=>
{
isShowingError
=
false
;
// 检查队列中是否有待显示的消息
if
(
errorMessageQueue
.
length
>
0
)
{
// 延迟一段时间再显示下一条,避免消息切换太快
setTimeout
(()
=>
{
const
nextError
=
errorMessageQueue
.
shift
();
if
(
nextError
)
{
showErrorMessage
(
nextError
.
message
,
nextError
.
type
);
}
},
500
);
}
}
});
}
// 清空错误消息队列(可选,在某些场景下可能需要)
export
function
clearErrorMessageQueue
()
{
errorMessageQueue
=
[];
isShowingError
=
false
;
}
axios
.
defaults
.
headers
[
'Content-Type'
]
=
'application/json;charset=utf-8'
// 创建axios实例
const
service
=
axios
.
create
({
...
...
@@ -91,51 +143,18 @@ service.interceptors.response.use(async res => {
return
res
.
data
}
if
(
code
===
401
)
{
if
(
window
.
h5sdk
)
{
// 如果在飞书客户端,则无感知重新获取token
const
code
=
await
fsClientAuth
()
await
useUserStore
().
login
({
type
:
'fs'
,
data
:
{
code
}
})
// 重新发送本次失败的请求
return
service
(
res
.
config
)
}
// PC/移动端刷新 token 有效期
// await useUserStore().refreshTokenFn()
// return service(res.config)
ElMessageBox
.
confirm
(
'登录状态已过期,您可以继续留在该页面,或者重新登录'
,
'系统提示'
,
{
confirmButtonText
:
'重新登录'
,
cancelButtonText
:
'取消'
,
type
:
'warning'
}).
then
(()
=>
{
isRelogin
.
show
=
false
;
useUserStore
().
logOut
().
then
(()
=>
{
// location.href = '#/login';
router
.
push
({
path
:
'/login'
})
})
}).
catch
(()
=>
{
isRelogin
.
show
=
false
;
});
return
Promise
.
reject
(
'无效的会话,或者会话已过期,请重新登录。'
)
// ... existing 401 handling code ...
}
else
if
(
code
===
403
)
{
// 如果刷新 refreshToken 接口也报错了,证明需要重新登录
// ElMessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', { confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' }).then(() => {
// isRelogin.show = false;
// useUserStore().logOut().then(() => {
// location.href = '#/login';
// })
// }).catch(() => {
// isRelogin.show = false;
// });
// return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
ElMessage
({
message
:
msg
,
type
:
'error'
})
showErrorMessage
(
msg
,
'error'
)
return
Promise
.
reject
(
new
Error
(
msg
))
}
else
if
(
code
===
500
)
{
ElMessage
({
message
:
msg
,
type
:
'error'
}
)
showErrorMessage
(
msg
,
'error'
)
return
Promise
.
reject
(
new
Error
(
msg
))
}
else
if
(
code
===
601
)
{
ElMessage
({
message
:
msg
,
type
:
'warning'
}
)
showErrorMessage
(
msg
,
'warning'
)
return
Promise
.
reject
(
new
Error
(
msg
))
}
else
if
(
code
!==
200
)
{
ElNotification
.
error
({
title
:
msg
}
)
showErrorMessage
(
msg
,
'error'
)
return
Promise
.
reject
(
'error'
)
}
else
{
return
Promise
.
resolve
(
res
.
data
)
...
...
@@ -150,7 +169,7 @@ service.interceptors.response.use(async res => {
}
else
if
(
message
.
includes
(
"Request failed with status code"
))
{
message
=
"系统接口"
+
message
.
substr
(
message
.
length
-
3
)
+
"异常"
;
}
ElMessage
({
message
:
message
,
type
:
'error'
,
duration
:
5
*
1000
}
)
showErrorMessage
(
message
,
'error'
)
return
Promise
.
reject
(
error
)
}
)
...
...
src/views/mobile/pages/menu/index.vue
浏览文件 @
d106e050
...
...
@@ -92,7 +92,8 @@ const handleIconClick = (icon) => {
.mobile-container
{
background-color
:
#f3f3f3
;
min-height
:
100vh
;
font-size
:
14px
;
font-size
:
13px
;
// font-size: 12px;
padding
:
20px
;
p
{
...
...
@@ -125,7 +126,7 @@ const handleIconClick = (icon) => {
display
:
grid
;
grid-template-columns
:
repeat
(
4
,
1fr
);
/* 创建4列等宽布局 */
gap
:
10
px
;
gap
:
2
px
;
/* 列之间的间距 */
justify-content
:
space-between
;
/* 两边对齐 */
...
...
src/views/mobile/pages/storeExecution/dashboard/components/Table/index.vue
0 → 100644
浏览文件 @
d106e050
<
template
>
<div
class=
"flex-container"
>
<!-- 操作类型 -->
<el-row>
<el-form-item>
<el-radio-group
v-model=
"operation"
@
change=
"handleChange"
>
<el-radio-button
label=
"大区战区-分析"
value=
"大区战区-分析"
v-hasPermi=
"['promotion:dashboard:list-show']"
/>
<el-radio-button
label=
"城市经理-分析"
value=
"城市经理-分析"
/>
</el-radio-group>
</el-form-item>
</el-row>
<el-table
:data=
"tableData"
border
ref=
"tableRef"
v-loading=
"isLoading"
show-overflow-tooltip
>
<template
v-for=
"col in chooseColumns"
:key=
"col.prop"
>
<!-- 有子列的情况 -->
<el-table-column
v-if=
"col.childColumns && col.childColumns.length > 0"
:label=
"col.label"
align=
"center"
:width=
"col.width"
>
<el-table-column
v-for=
"childCol in col.childColumns"
:key=
"childCol.prop"
:label=
"childCol.label"
:prop=
"childCol.prop"
align=
"center"
:width=
"childCol.width"
>
</el-table-column>
</el-table-column>
<!-- 没有子列的独立列情况 -->
<el-table-column
v-else
:label=
"col.label"
:prop=
"col.prop"
align=
"center"
:width=
"col.width"
>
</el-table-column>
</
template
>
</el-table>
</div>
</template>
<
script
setup
>
import
userStore
from
'@/store/modules/user'
const
props
=
defineProps
({
tableData
:
{
// 数据源
type
:
Array
,
default
:
()
=>
[]
},
baseColumns
:
{
// 表格列
type
:
Array
,
default
:
()
=>
[]
},
total
:
{
// 总条数
type
:
Number
,
default
:
0
},
isLoading
:
{
// 表格加载状态
type
:
Boolean
,
default
:
false
},
formatter
:
{
// 格式化函数
type
:
Function
,
default
:
(
row
,
col
,
cellValue
)
=>
cellValue
}
})
const
emit
=
defineEmits
([
'getTableList'
])
/*************** 工具栏 ***************/
const
operation
=
ref
(
''
);
// 切换平铺/填报模式
const
tableRef
=
ref
(
null
)
const
chooseColumns
=
ref
([])
// 使用的列
// 检查用户是否有特定权限的辅助函数
const
hasPermission
=
(
permission
)
=>
{
return
userStore
().
permissions
.
includes
(
permission
)
}
// 根据权限动态设置默认选中的值
onMounted
(()
=>
{
// 优先检查是否有'promotion:dashboard:list-show'权限,如果有则选中'大区战区-分析'
if
(
hasPermission
(
'promotion:dashboard:list-show'
))
{
operation
.
value
=
'大区战区-分析'
}
else
{
// 否则默认选中'城市经理-分析'
operation
.
value
=
'城市经理-分析'
}
// 初始化列选择
initColumns
()
})
// 初始化列选择的函数
const
initColumns
=
()
=>
{
if
(
operation
.
value
===
'大区战区-分析'
)
{
chooseColumns
.
value
=
props
.
baseColumns
.
filter
(
item
=>
item
.
prop
!==
'cityManager'
)
}
else
{
chooseColumns
.
value
=
props
.
baseColumns
}
}
watch
(
operation
,
(
newVal
)
=>
{
// 如果是大区战区-分析,则隐藏城市经理列
if
(
newVal
===
'大区战区-分析'
)
{
chooseColumns
.
value
=
props
.
baseColumns
.
filter
(
item
=>
item
.
prop
!==
'cityManager'
)
}
else
{
chooseColumns
.
value
=
props
.
baseColumns
}
},
{
immediate
:
true
})
// 切换操作类型时触发的函数
const
handleChange
=
(
newVal
)
=>
{
emit
(
'getTableList'
,
newVal
)
}
</
script
>
<
style
scoped
lang=
"scss"
>
/* 根容器设置为flex布局,占据整个可用空间 */
.flex-container
{
flex
:
1
;
display
:
flex
;
flex-direction
:
column
;
overflow
:
hidden
;
/* 表格样式 */
.table-container
{
flex
:
1
;
/* 这是关键,让表格容器自动占据剩余空间 */
display
:
flex
;
flex-direction
:
column
;
min-height
:
0
;
/* 解决flex子项内容溢出问题 */
.auto-fit-header-table
{
flex
:
1
;
/* 列样式 */
.column-style
{
/* 列头样式 */
.formula-column
{
display
:
flex
;
justify-content
:
center
;
align-items
:
center
;
.column
{
display
:
flex
;
justify-content
:
center
;
align-items
:
center
;
flex-direction
:
column
;
p
{
margin
:
0
;
font-size
:
12px
;
color
:
#606266
;
}
}
.el-icon
{
margin-left
:
2px
;
}
}
/* 单元格样式 */
/* 展示模式单元格(仅展示模式生效) */
.show-cell-style
{
overflow
:
hidden
;
text-overflow
:
ellipsis
;
white-space
:
nowrap
;
}
/* 自定义单元格样式(仅填充模式生效) */
.cell-style
{
>
div
{
display
:
flex
;
flex-direction
:
column
;
align-items
:
center
;
>
span
{
text-align
:
left
;
text-indent
:
5px
;
display
:
inline-block
;
width
:
80%
;
background-color
:
#e1e2e6
;
border-bottom
:
1px
solid
#ebeef5
;
}
}
/* 表格内下拉框 */
.el-select
{
width
:
100%
!
important
;
padding
:
10px
;
/* 非 disabled 状态下的背景颜色 */
&.
no-disabled
:
:
v-deep
(
.
el-select__wrapper
)
{
border
:
4px
solid
var
(
--
el-background-editor-cell
)
!
important
;
}
}
.el-input
{
height
:
32px
!
important
;
/* box-sizing: content-box; */
padding
:
0
10px
;
width
:
100%
;
/* 非 disabled 状态下的背景颜色 */
&.
no-disabled
:
:
v-deep
(
.
el-input__wrapper
)
{
border
:
4px
solid
var
(
--
el-background-editor-cell
)
!
important
;
}
}
.date-picker
{
width
:
100%
;
padding
:
10px
;
/* 非 disabled 状态下的背景颜色 */
&.
no-disabled
:
:
v-deep
(
.
el-input__wrapper
)
{
border
:
4px
solid
var
(
--
el-background-editor-cell
)
!
important
;
}
::v-deep
(
.el-input
)
{
width
:
100%
;
padding
:
0
;
}
}
}
/* 普通文字单元格(仅填充模式生效) */
.fill-span-wrap
{
/* 超出省略号 */
overflow
:
hidden
;
text-overflow
:
ellipsis
;
white-space
:
nowrap
;
padding
:
0
10px
;
}
}
/* 填报模式-单元格 */
&
.cell-no-padding
{
/* 无上下内边距 */
::v-deep
(
.el-table__row
)
{
.el-table__cell
{
padding-top
:
0
;
padding-bottom
:
0
;
.cell
{
padding
:
0
!
important
;
}
}
}
}
}
/* 分页器 */
.pagination-container
{
margin
:
10px
;
}
}
}
/* 加深表头背景颜色 */
::v-deep
(
.el-table__header-wrapper
)
{
.el-table__header
{
th
{
background-color
:
#dcdfe6
!
important
;
color
:
#303133
!
important
;
font-weight
:
bold
;
}
}
}
</
style
>
src/views/mobile/pages/storeExecution/dashboard/index.vue
0 → 100644
浏览文件 @
d106e050
<
template
>
<div
class=
"app-container"
>
<div
class=
"container"
>
<van-nav-bar
fixed
left-arrow
@
click-left=
"router.back()"
title=
"店内执行上报浏览"
/>
<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-tab-pane
label=
"三米两秒"
name=
"三米两秒"
>
<ThreeTwoSeconds
/>
</el-tab-pane>
<el-tab-pane
label=
"六小金刚"
name=
"六小金刚"
>
<SixLittleDiamonds
/>
</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'
import
ThreeTwoSeconds
from
'./tabs/three_two_seconds.vue'
import
SixLittleDiamonds
from
'./tabs/six_little_diamonds.vue'
const
router
=
useRouter
()
const
activeName
=
ref
(
'常规陈列'
);
const
handleClickTabs
=
(
tab
)
=>
{
activeName
.
value
=
tab
.
name
;
}
</
script
>
<
style
scoped
lang=
"scss"
>
.app-container
{
padding
:
10px
;
padding-top
:
46px
;
.container
{
padding
:
10px
;
.el-tabs
{
height
:
100%
;
display
:
flex
;
flex-direction
:
column-reverse
;
::v-deep
(
.el-tabs__header
)
{
// 确保底部边框线完整显示
border-bottom
:
1px
solid
var
(
--
el-border-color-light
);
width
:
100%
;
}
::v-deep
(
.el-tabs__nav-wrap
)
{
overflow-x
:
auto
;
overflow-y
:
hidden
;
-webkit-overflow-scrolling
:
touch
;
// iOS平滑滚动
scrollbar-width
:
none
;
// Firefox隐藏滚动条
&
:
:
after
{
// 隐藏el-tabs默认的底部伪元素边框
display
:
none
;
}
&
:
:-
webkit-scrollbar
{
display
:
none
;
// Chrome/Safari隐藏滚动条
}
}
::v-deep
(
.el-tabs__nav-scroll
)
{
overflow
:
visible
;
width
:
max-content
;
}
::v-deep
(
.el-tabs__content
)
{
display
:
flex
;
flex-direction
:
column
;
overflow-y
:
scroll
;
// height: 100%;
.el-tab-pane
{
// height: 100%;
display
:
flex
;
flex-direction
:
column
;
}
}
}
}
}
</
style
>
\ No newline at end of file
src/views/mobile/pages/storeExecution/dashboard/tabs/data.jsx
0 → 100644
浏览文件 @
d106e050
// 每个表格里的数据列信息集合
// 常规陈列
export
const
getDisplayConfig
=
()
=>
{
return
[
// {
// label: "计划月份",
// prop: "salesMonth",
// width: 100,
// childCol: []
// },
{
label
:
"大区"
,
prop
:
"regionName"
,
childCol
:
[]
},
{
label
:
"战区"
,
prop
:
"districtName"
,
childCol
:
[],
width
:
110
},
{
label
:
"城市经理"
,
prop
:
"cityManager"
,
childCol
:
[]
},
{
label
:
'主货架'
,
prop
:
"mainShelf"
,
childColumns
:
[
{
label
:
'计划'
,
prop
:
"planMsStoreCnt"
,
},
{
label
:
'执行'
,
prop
:
"execMsStoreCnt"
,
},
{
label
:
'执行率'
,
prop
:
"msExecRate"
,
width
:
90
}
]
},
{
label
:
'端架'
,
prop
:
"endShelf"
,
childColumns
:
[
{
label
:
'计划'
,
prop
:
"planRegEndcapStoreCnt"
,
},
{
label
:
'执行'
,
prop
:
"execRegEndcapStoreCnt"
,
},
{
label
:
'执行率'
,
prop
:
"endcapExecRate"
,
width
:
90
}
]
},
{
label
:
'地堆'
,
prop
:
"endcapShelf"
,
childColumns
:
[
{
label
:
'计划'
,
prop
:
"planRegGsStoreCnt"
,
},
{
label
:
'执行'
,
prop
:
"execRegGsStoreCnt"
,
},
{
label
:
'执行率'
,
prop
:
"gsExecRate"
,
width
:
90
}
]
},
{
label
:
'多点陈列'
,
prop
:
"multiShelf"
,
childColumns
:
[
{
label
:
'计划'
,
prop
:
"planMpDispStoreCnt"
,
},
{
label
:
'执行'
,
prop
:
"execMpDispStoreCnt"
,
},
{
label
:
'执行率'
,
prop
:
"mpDispExecRate"
,
width
:
90
}
]
},
{
label
:
'挂条陈列'
,
prop
:
"hangShelf"
,
childColumns
:
[
{
label
:
'计划'
,
prop
:
"planHsStoreCnt"
,
},
{
label
:
'执行'
,
prop
:
"execHsStoreCnt"
,
},
{
label
:
'执行率'
,
prop
:
"HsExecRate"
,
width
:
90
}
]
}
]
}
// 档期计划
export
const
getSchedulePlanConfig
=
(
submitChange
)
=>
{
return
[
// {
// label: "计划月份",
// prop: "salesMonth",
// width: 100
// },
{
label
:
"大区"
,
prop
:
"regionName"
,
width
:
100
},
{
label
:
"战区"
,
prop
:
"districtName"
,
width
:
100
},
{
label
:
"城市经理"
,
prop
:
"cityManager"
,
width
:
100
},
{
label
:
'大区反馈'
,
prop
:
"regionFeedback"
,
childColumns
:
[
{
label
:
'计划'
,
prop
:
"planPromoPeriStoreCnt"
,
},
{
label
:
'执行'
,
prop
:
"launchPromoPeriStoreCnt"
,
},
{
label
:
'执行率'
,
prop
:
"launchRatePromoPeriExec"
,
width
:
90
}
]
},
{
label
:
'规格'
,
prop
:
"spec"
,
childColumns
:
[
{
label
:
'执行'
,
prop
:
"execPsStoreCnt"
,
},
{
label
:
'执行率'
,
prop
:
"psExecRatePromoPeriExec"
,
width
:
90
}
]
},
{
label
:
'口味'
,
prop
:
"flavor"
,
childColumns
:
[
{
label
:
'执行'
,
prop
:
"execPfStoreCnt"
,
},
{
label
:
'执行率'
,
prop
:
"pfExecRatePromoPeriExec"
,
width
:
90
}
]
},
{
label
:
'价格'
,
prop
:
"price"
,
childColumns
:
[
{
label
:
'执行'
,
prop
:
"execPpStoreCnt"
,
},
{
label
:
'执行率'
,
prop
:
"ppExecRatePromoPeriExec"
,
width
:
90
}
]
},
{
label
:
'海报'
,
prop
:
"poster"
,
childColumns
:
[
{
label
:
'计划'
,
prop
:
"planPosterStoreCnt"
,
},
{
label
:
'执行'
,
prop
:
"execPosterStoreCnt"
,
},
{
label
:
'执行率'
,
prop
:
"posterExecRate"
,
width
:
90
}
]
},
]
}
// 档期陈列
export
const
getScheduleDisConfig
=
(
submitChange
)
=>
{
return
[
// {
// label: "计划月份",
// prop: "salesMonth",
// width: 100
// },
{
label
:
"大区"
,
prop
:
"regionName"
,
width
:
100
},
{
label
:
"战区"
,
prop
:
"districtName"
,
width
:
100
},
{
label
:
"城市经理"
,
prop
:
"cityManager"
,
width
:
100
},
{
label
:
'端架'
,
prop
:
"endShelf"
,
childColumns
:
[
{
label
:
'计划'
,
prop
:
"planEndcapStoreCnt"
,
},
{
label
:
'执行'
,
prop
:
"execEndcapStoreCnt"
,
},
{
label
:
'执行率'
,
prop
:
"endcapExecRatePromoPeriDisp"
,
}
]
},
{
label
:
'地堆'
,
prop
:
"endcapShelf"
,
childColumns
:
[
{
label
:
'计划'
,
prop
:
"planGsStoreCnt"
,
},
{
label
:
'执行'
,
prop
:
"execGsStoreCnt"
,
},
{
label
:
'执行率'
,
prop
:
"gsExecRatePromoPeriDisp"
,
}
]
},
{
label
:
'其他陈列'
,
prop
:
"otherShelf"
,
childColumns
:
[
{
label
:
'计划'
,
prop
:
"planOtherDispStoreCnt"
,
},
{
label
:
'执行'
,
prop
:
"execOtherDispStoreCnt"
,
},
{
label
:
'执行率'
,
prop
:
"otherDispExecRatePromoPeriDisp"
,
}
]
},
]
}
// 零食陈列
export
const
getSnackCofing
=
(
submitChange
)
=>
{
return
[
// {
// label: "计划月份",
// prop: "salesMonth",
// width: 100
// },
{
label
:
"大区"
,
prop
:
"regionName"
,
width
:
100
},
{
label
:
"战区"
,
prop
:
"districtName"
,
width
:
100
},
{
label
:
"城市经理"
,
prop
:
"cityManager"
,
width
:
100
},
{
label
:
'计划'
,
prop
:
"planSnackStoreCnt"
,
},
{
label
:
'执行'
,
prop
:
"execSnackStoreCnt"
,
},
{
label
:
'执行率'
,
prop
:
"snackExecRate"
,
}
]
}
// 三米两秒
export
const
getThreeTwoSecondsConfig
=
(
submitChange
)
=>
{
return
[
// {
// label: "计划月份",
// prop: "salesMonth",
// width: 100
// },
{
label
:
"大区"
,
prop
:
"regionName"
,
width
:
100
},
{
label
:
"战区"
,
prop
:
"districtName"
,
width
:
100
},
{
label
:
"城市经理"
,
prop
:
"cityManager"
,
width
:
100
},
{
label
:
'计划'
,
prop
:
"planSLStoreCnt"
,
},
{
label
:
'执行'
,
prop
:
"execSLStoreCnt"
,
},
{
label
:
'执行率'
,
prop
:
"SLExecRate"
,
}
]
}
// 六小金刚
export
const
getSixLittleDiamondsConfig
=
(
submitChange
)
=>
{
return
[
// {
// label: "计划月份",
// prop: "salesMonth",
// width: 100
// },
{
label
:
"大区"
,
prop
:
"regionName"
,
width
:
100
},
{
label
:
"战区"
,
prop
:
"districtName"
,
width
:
100
},
{
label
:
"城市经理"
,
prop
:
"cityManager"
,
width
:
100
},
{
label
:
'计划'
,
prop
:
"planSixJdStoreCnt"
,
},
{
label
:
'执行'
,
prop
:
"execSixJdStoreCnt"
,
},
{
label
:
'执行率'
,
prop
:
"sixJdExecRate"
,
}
]
}
src/views/mobile/pages/storeExecution/dashboard/tabs/display.vue
0 → 100644
浏览文件 @
d106e050
<
template
>
<!-- 常规陈列 -->
<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=
"getTableList"
clearable
/>
</el-form-item>
</el-form>
<CustomTable
:tableData=
"tableData"
:baseColumns=
"baseColumns"
:isLoading=
"isLoading"
@
getTableList=
"getTableList"
@
updateShowSearch=
"v => showSearch.value = v"
/>
</
template
>
<
script
setup
lang=
"jsx"
>
import
CustomTable
from
'../components/Table'
import
{
getDisplayScheduleDashboardList
,
getDisplayScheduleDashboardListArea
}
from
'@/api'
import
{
getDisplayConfig
}
from
'./data.jsx'
import
{
parseTime
}
from
'@/utils'
/*************** 表格操作相关 ***************/
// 提交变更
const
tableColumns
=
getDisplayConfig
()
// 全部列
const
baseColumns
=
ref
(
tableColumns
);
// 表格数据
const
queryParams
=
reactive
({
salesMonth
:
new
Date
(),
})
const
tableData
=
ref
([])
const
isLoading
=
ref
(
true
)
const
total
=
ref
(
0
)
// 筛选列表数据
const
getTableList
=
async
(
operation
)
=>
{
isLoading
.
value
=
true
const
res
=
await
(
operation
===
'大区战区-分析'
?
getDisplayScheduleDashboardList
:
getDisplayScheduleDashboardListArea
)({
...
queryParams
,
salesMonth
:
parseTime
(
queryParams
.
salesMonth
,
'{y}-{m}'
)
})
if
(
operation
===
'大区战区-分析'
)
{
// 合并战区大区结构为扁平化
const
{
zq
,
dq
}
=
res
.
data
tableData
.
value
=
[...
zq
,
...
dq
]
}
else
{
// 合并城市经理结构为扁平化
tableData
.
value
=
res
.
data
}
isLoading
.
value
=
false
}
const
{
proxy
}
=
getCurrentInstance
()
const
isDaQuZQ
=
proxy
.
checkPermi
([
'promotion:dashboard:list-show'
])
getTableList
(
isDaQuZQ
?
'大区战区-分析'
:
'城市经理-分析'
)
</
script
>
<
style
lang=
"scss"
>
// 动态列内容的 render 内样式
// 操作提示列
.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
;
}
}
// 只在填报模式出现的门店名称+门店编码+经销山名称(合并到一起)
.store-name-render-cell
{
display
:
flex
;
flex-direction
:
column
;
align-items
:
flex-start
;
p
{
width
:
100%
;
margin
:
0
;
}
}
</
style
>
\ No newline at end of file
src/views/mobile/pages/storeExecution/dashboard/tabs/schedule.vue
0 → 100644
浏览文件 @
d106e050
<
template
>
<!-- 档期计划 -->
<!-- 隐藏门店搜索 -->
<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=
"getTableList"
clearable
/>
</el-form-item>
</el-form>
<CustomTable
:tableData=
"tableData"
:baseColumns=
"baseColumns"
:isLoading=
"isLoading"
@
getTableList=
"getTableList"
@
updateShowSearch=
"v => showSearch.value = v"
/>
</
template
>
<
script
setup
lang=
"jsx"
>
import
CustomTable
from
'../components/Table'
import
{
getDisplayScheduleDashboardList
,
getDisplayScheduleDashboardListArea
}
from
'@/api'
import
{
parseTime
}
from
'@/utils'
import
{
getSchedulePlanConfig
}
from
'./data.jsx'
const
{
proxy
}
=
getCurrentInstance
()
/*************** 操作类型 ***************/
// 提交变更
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
=
getSchedulePlanConfig
().
flatMap
(
item
=>
{
if
(
item
.
children
)
{
return
item
.
children
.
filter
(
child
=>
!
child
.
onlyFill
);
}
return
[];
}).
find
(
item
=>
item
.
prop
==
str
)
if
(
obj
&&
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
baseColumns
=
ref
(
getSchedulePlanConfig
(
submitChange
));
// 表格数据
const
queryParams
=
reactive
({
salesMonth
:
new
Date
(),
})
const
tableData
=
ref
([])
const
isLoading
=
ref
(
true
)
const
total
=
ref
(
0
)
// 筛选列表数据
const
getTableList
=
async
(
operation
)
=>
{
isLoading
.
value
=
true
const
res
=
await
(
operation
===
'大区战区-分析'
?
getDisplayScheduleDashboardList
:
getDisplayScheduleDashboardListArea
)({
...
queryParams
,
salesMonth
:
parseTime
(
queryParams
.
salesMonth
,
'{y}-{m}'
)
})
if
(
operation
===
'大区战区-分析'
)
{
// 合并战区大区结构为扁平化
const
{
zq
,
dq
}
=
res
.
data
tableData
.
value
=
[...
zq
,
...
dq
]
}
else
{
// 合并城市经理结构为扁平化
tableData
.
value
=
res
.
data
}
isLoading
.
value
=
false
}
const
isDaQuZQ
=
proxy
.
checkPermi
([
'promotion:dashboard:list-show'
])
getTableList
(
isDaQuZQ
?
'大区战区-分析'
:
'城市经理-分析'
)
</
script
>
src/views/mobile/pages/storeExecution/dashboard/tabs/schedule_dis.vue
0 → 100644
浏览文件 @
d106e050
<
template
>
<!-- 档期陈列 -->
<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=
"getTableList"
clearable
/>
</el-form-item>
</el-form>
<CustomTable
:tableData=
"tableData"
:baseColumns=
"baseColumns"
:isLoading=
"isLoading"
@
getTableList=
"getTableList"
@
updateShowSearch=
"v => showSearch.value = v"
/>
</
template
>
<
script
setup
lang=
"jsx"
>
import
CustomTable
from
'../components/Table'
import
{
getDisplayScheduleDashboardList
,
getDisplayScheduleDashboardListArea
}
from
'@/api'
import
{
parseTime
}
from
'@/utils'
import
{
getScheduleDisConfig
}
from
'./data'
/*************** 表格数据 ***************/
// 提交变更
const
submitChange
=
async
(
row
,
col
)
=>
{
// 需要特殊处理的
// 1. 实际主货架-形式,为空时,置空实际主货架-数量
if
(
col
.
prop
===
'actualMainShelfType'
&&
!
row
.
actualMainShelfType
)
{
row
.
actualMainShelfQty
=
0
}
// 关联的公式计算列,需要特殊处理
for
(
const
str
of
col
.
requestKey
)
{
const
obj
=
getScheduleDisConfig
().
flatMap
(
item
=>
{
if
(
item
.
children
)
{
return
item
.
children
.
filter
(
child
=>
!
child
.
onlyFill
);
}
return
[];
}).
find
(
item
=>
item
.
prop
===
str
)
if
(
obj
&&
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
]
}),
{}),
// 额外携带影响的列字段值
// 特殊类型字段处理
// 端架数量实际
actualEndCapQty
:
row
.
actualEndCapQty
||
0
,
// 地堆平米数实际
actualFloorStackArea
:
row
.
actualFloorStackArea
||
0
,
})
}
// 全部列
const
baseColumns
=
ref
(
getScheduleDisConfig
(
submitChange
));
// 表格数据
const
queryParams
=
reactive
({
salesMonth
:
new
Date
(),
})
const
tableData
=
ref
([])
const
isLoading
=
ref
(
true
)
const
total
=
ref
(
0
)
// 筛选列表数据
const
getTableList
=
async
(
operation
)
=>
{
isLoading
.
value
=
true
const
res
=
await
(
operation
===
'大区战区-分析'
?
getDisplayScheduleDashboardList
:
getDisplayScheduleDashboardListArea
)({
...
queryParams
,
salesMonth
:
parseTime
(
queryParams
.
salesMonth
,
'{y}-{m}'
)
})
if
(
operation
===
'大区战区-分析'
)
{
// 合并战区大区结构为扁平化
const
{
zq
,
dq
}
=
res
.
data
tableData
.
value
=
[...
zq
,
...
dq
]
}
else
{
// 合并城市经理结构为扁平化
tableData
.
value
=
res
.
data
}
isLoading
.
value
=
false
}
const
{
proxy
}
=
getCurrentInstance
()
const
isDaQuZQ
=
proxy
.
checkPermi
([
'promotion:dashboard:list-show'
])
getTableList
(
isDaQuZQ
?
'大区战区-分析'
:
'城市经理-分析'
)
</
script
>
\ No newline at end of file
src/views/mobile/pages/storeExecution/dashboard/tabs/six_little_diamonds.vue
0 → 100644
浏览文件 @
d106e050
<
template
>
<!-- 六小金刚 -->
<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=
"getTableList"
clearable
/>
</el-form-item>
</el-form>
<CustomTable
:tableData=
"tableData"
:baseColumns=
"baseColumns"
:isLoading=
"isLoading"
@
getTableList=
"getTableList"
@
updateShowSearch=
"v => showSearch.value = v"
/>
</
template
>
<
script
setup
lang=
"jsx"
>
import
CustomTable
from
'../components/Table'
import
{
getDisplayScheduleDashboardList
,
getDisplayScheduleDashboardListArea
}
from
'@/api'
import
{
getSixLittleDiamondsConfig
}
from
'./data.jsx'
import
{
parseTime
}
from
'@/utils'
const
{
proxy
}
=
getCurrentInstance
()
/*************** 搜索列表 ***************/
const
showSearch
=
ref
(
true
)
/*************** 表格 ***************/
// 提交变更
const
submitChange
=
async
(
row
,
col
)
=>
{
let
requestObj
=
{}
if
(
col
.
requestKey
)
{
// 关联的公式计算列,需要特殊处理
for
(
const
str
of
col
.
requestKey
)
{
const
obj
=
getSixLittleDiamondsConfig
().
flatMap
(
item
=>
{
if
(
item
.
children
)
{
return
item
.
children
.
filter
(
child
=>
!
child
.
onlyFill
);
}
return
[];
}).
find
(
item
=>
item
.
prop
===
str
)
if
(
obj
&&
obj
.
type
===
'formula'
)
{
obj
.
func
(
row
)
}
}
requestObj
=
col
.
requestKey
.
reduce
((
acc
,
key
)
=>
({
...
acc
,
[
key
]:
row
[
key
]
}),
{})
}
await
submitSixLittleDiamondsPlan
({
id
:
row
.
sadjId
,
[
col
.
prop
]:
row
[
col
.
prop
],
// 当前修改列的值
...
requestObj
})
}
// 全部列
const
baseColumns
=
ref
(
getSixLittleDiamondsConfig
(
submitChange
));
// 表格数据
const
queryParams
=
reactive
({
salesMonth
:
new
Date
(),
})
const
tableData
=
ref
([])
const
isLoading
=
ref
(
true
)
const
total
=
ref
(
0
)
// 筛选列表数据
const
getTableList
=
async
(
operation
)
=>
{
isLoading
.
value
=
true
const
res
=
await
(
operation
===
'大区战区-分析'
?
getDisplayScheduleDashboardList
:
getDisplayScheduleDashboardListArea
)({
...
queryParams
,
salesMonth
:
parseTime
(
queryParams
.
salesMonth
,
'{y}-{m}'
)
})
if
(
operation
===
'大区战区-分析'
)
{
// 合并战区大区结构为扁平化
const
{
zq
,
dq
}
=
res
.
data
tableData
.
value
=
[...
zq
,
...
dq
]
}
else
{
// 合并城市经理结构为扁平化
tableData
.
value
=
res
.
data
}
isLoading
.
value
=
false
}
const
isDaQuZQ
=
proxy
.
checkPermi
([
'promotion:dashboard:list-show'
])
getTableList
(
isDaQuZQ
?
'大区战区-分析'
:
'城市经理-分析'
)
</
script
>
<
style
scoped
></
style
>
\ No newline at end of file
src/views/mobile/pages/storeExecution/dashboard/tabs/snack.vue
0 → 100644
浏览文件 @
d106e050
<
template
>
<!-- 零食陈列 -->
<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=
"getTableList"
clearable
/>
</el-form-item>
</el-form>
<CustomTable
:tableData=
"tableData"
:baseColumns=
"baseColumns"
:isLoading=
"isLoading"
@
getTableList=
"getTableList"
@
updateShowSearch=
"v => showSearch.value = v"
/>
</
template
>
<
script
setup
lang=
"jsx"
>
import
CustomTable
from
'../components/Table'
import
{
getDisplayScheduleDashboardList
,
getDisplayScheduleDashboardListArea
}
from
'@/api'
import
{
parseTime
}
from
'@/utils'
import
{
getSnackCofing
}
from
'./data.jsx'
;
/*************** 操作类型 ***************/
// 提交变更
const
submitChange
=
async
(
row
,
col
)
=>
{
// 需要特殊处理的
// 1. 实际主货架-形式,为空时,置空实际主货架-数量
// if (col.prop === 'actualMainShelfType' && !row.actualMainShelfType) {
// row.actualMainShelfQty = 0
// }
// 关联的公式计算列,需要特殊处理
for
(
const
str
of
col
.
requestKey
)
{
const
obj
=
getSnackCofing
().
flatMap
(
item
=>
{
if
(
item
.
children
)
{
return
item
.
children
.
filter
(
child
=>
!
child
.
onlyFill
);
}
return
[];
}).
find
(
item
=>
item
.
prop
===
str
)
if
(
obj
&&
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
]
}),
{}),
// 额外携带影响的列字段值
})
}
// 全部列
const
baseColumns
=
ref
(
getSnackCofing
(
submitChange
));
// 表格数据
const
queryParams
=
reactive
({
salesMonth
:
new
Date
(),
})
const
tableData
=
ref
([])
const
isLoading
=
ref
(
true
)
const
total
=
ref
(
0
)
// 筛选列表数据
const
getTableList
=
async
(
operation
)
=>
{
isLoading
.
value
=
true
const
res
=
await
(
operation
===
'大区战区-分析'
?
getDisplayScheduleDashboardList
:
getDisplayScheduleDashboardListArea
)({
...
queryParams
,
salesMonth
:
parseTime
(
queryParams
.
salesMonth
,
'{y}-{m}'
)
})
if
(
operation
===
'大区战区-分析'
)
{
// 合并战区大区结构为扁平化
const
{
zq
,
dq
}
=
res
.
data
tableData
.
value
=
[...
zq
,
...
dq
]
}
else
{
// 合并城市经理结构为扁平化
tableData
.
value
=
res
.
data
}
isLoading
.
value
=
false
}
const
{
proxy
}
=
getCurrentInstance
()
const
isDaQuZQ
=
proxy
.
checkPermi
([
'promotion:dashboard:list-show'
])
getTableList
(
isDaQuZQ
?
'大区战区-分析'
:
'城市经理-分析'
)
</
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
src/views/mobile/pages/storeExecution/dashboard/tabs/three_two_seconds.vue
0 → 100644
浏览文件 @
d106e050
<
template
>
<!-- 三米两秒 -->
<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=
"getTableList"
clearable
/>
</el-form-item>
</el-form>
<CustomTable
:tableData=
"tableData"
:baseColumns=
"baseColumns"
:isLoading=
"isLoading"
@
getTableList=
"getTableList"
@
updateShowSearch=
"v => showSearch.value = v"
/>
</
template
>
<
script
setup
lang=
"jsx"
>
import
CustomTable
from
'../components/Table'
import
{
getDisplayScheduleDashboardList
,
getDisplayScheduleDashboardListArea
}
from
'@/api'
import
{
getThreeTwoSecondsConfig
}
from
'./data.jsx'
import
{
parseTime
}
from
'@/utils'
const
{
proxy
}
=
getCurrentInstance
()
/*************** 搜索列表 ***************/
const
showSearch
=
ref
(
true
)
/*************** 表格 ***************/
// 提交变更
const
submitChange
=
async
(
row
,
col
)
=>
{
let
requestObj
=
{}
if
(
col
.
requestKey
)
{
// 关联的公式计算列,需要特殊处理
for
(
const
str
of
col
.
requestKey
)
{
const
obj
=
getThreeTwoSecondsConfig
().
flatMap
(
item
=>
{
if
(
item
.
children
)
{
return
item
.
children
.
filter
(
child
=>
!
child
.
onlyFill
);
}
return
[];
}).
find
(
item
=>
item
.
prop
===
str
)
if
(
obj
&&
obj
.
type
===
'formula'
)
{
obj
.
func
(
row
)
}
}
requestObj
=
col
.
requestKey
.
reduce
((
acc
,
key
)
=>
({
...
acc
,
[
key
]:
row
[
key
]
}),
{})
}
await
submitThreeMetersTwoSecondsPlan
({
id
:
row
.
sadsId
,
[
col
.
prop
]:
row
[
col
.
prop
],
// 当前修改列的值
...
requestObj
})
}
// 全部列
const
baseColumns
=
ref
(
getThreeTwoSecondsConfig
(
submitChange
));
// 表格数据
const
queryParams
=
reactive
({
salesMonth
:
new
Date
(),
})
const
tableData
=
ref
([])
const
isLoading
=
ref
(
true
)
const
total
=
ref
(
0
)
// 筛选列表数据
const
getTableList
=
async
(
operation
)
=>
{
isLoading
.
value
=
true
const
res
=
await
(
operation
===
'大区战区-分析'
?
getDisplayScheduleDashboardList
:
getDisplayScheduleDashboardListArea
)({
...
queryParams
,
salesMonth
:
parseTime
(
queryParams
.
salesMonth
,
'{y}-{m}'
)
})
if
(
operation
===
'大区战区-分析'
)
{
// 合并战区大区结构为扁平化
const
{
zq
,
dq
}
=
res
.
data
tableData
.
value
=
[...
zq
,
...
dq
]
}
else
{
// 合并城市经理结构为扁平化
tableData
.
value
=
res
.
data
}
isLoading
.
value
=
false
}
const
isDaQuZQ
=
proxy
.
checkPermi
([
'promotion:dashboard:list-show'
])
getTableList
(
isDaQuZQ
?
'大区战区-分析'
:
'城市经理-分析'
)
</
script
>
<
style
scoped
></
style
>
\ No newline at end of file
src/views/mobile/pages/storeExecution/report/components/SearchList/index.vue
0 → 100644
浏览文件 @
d106e050
<
template
>
<!-- 抽屉搜索组件 -->
<van-popup
v-model:show=
"showDrawer"
position=
"right"
class=
"search-drawer"
:style=
"
{ width: '85%', height: '100%' }"
teleport="body"
@close="handleClose">
<!-- 抽屉头部 -->
<van-nav-bar
title=
"筛选条件"
left-text=
"关闭"
left-arrow
@
click-left=
"handleClose"
/>
<!-- 搜索表单内容 -->
<div
class=
"search-content"
>
<van-cell-group
inset
>
<!-- 计划月份 -->
<van-field
:modelValue=
"parseTime(queryParams.salesMonth, '
{y}-{m}')"
label="计划月份"
placeholder="选择计划月份"
readonly
is-link
@click="showMonthPicker = true">
<template
#
input
>
<span>
{{
queryParams
.
salesMonth
||
'请选择'
}}
</span>
</
template
>
</van-field>
<!-- 大区/战区 -->
<van-field
v-model=
"queryParams.deptName"
label=
"大区/战区"
placeholder=
"请输入大区/战区"
@
update:model-value=
"handleChange"
clearable
/>
<!-- 经销商编码/名称 -->
<van-field
v-model=
"queryParams.dealerCN"
label=
"经销商编码/名称"
placeholder=
"请输入经销商编码/名称"
@
update:model-value=
"handleChange"
clearable
/>
<!-- 系统名称 -->
<van-field
v-model=
"queryParams.lineNameLike"
label=
"系统名称"
placeholder=
"请输入系统名称"
@
update:model-value=
"handleChange"
clearable
/>
<!-- 门店编码/名称 -->
<van-field
v-if=
"showStoreSearch"
v-model=
"queryParams.storeCN"
label=
"门店编码/名称"
placeholder=
"请输入门店编码/名称"
@
update:model-value=
"handleChange"
clearable
/>
<!-- 数据筛选 -->
<van-field
v-model=
"queryParams.rqStatus"
label=
"数据筛选"
placeholder=
"请选择筛选状态"
readonly
is-link
@
click=
"showStatusPicker = true"
>
<
template
#
input
>
<span>
{{
getStatusText
(
queryParams
.
rqStatus
)
}}
</span>
</
template
>
</van-field>
</van-cell-group>
<!-- 月份选择器 -->
<van-popup
v-model:show=
"showMonthPicker"
position=
"bottom"
teleport=
"body"
>
<van-date-picker
v-model=
"currentMonth"
title=
"选择计划月份"
:columns-type=
"['year', 'month']"
:min-date=
"minDate"
:max-date=
"maxDate"
@
confirm=
"confirmMonth"
@
cancel=
"showMonthPicker = false"
/>
</van-popup>
<!-- 状态选择器 -->
<van-popup
v-model:show=
"showStatusPicker"
position=
"bottom"
teleport=
"body"
>
<van-picker
:columns=
"statusOptions"
@
confirm=
"confirmStatus"
@
cancel=
"showStatusPicker = false"
/>
</van-popup>
<!-- 操作按钮 -->
<div
class=
"action-buttons"
>
<van-button
type=
"default"
size=
"large"
@
click=
"handleReset"
class=
"reset-btn"
>
<van-icon
name=
"replay"
/>
重置
</van-button>
<van-button
type=
"primary"
size=
"large"
@
click=
"handleConfirm"
class=
"confirm-btn"
>
<van-icon
name=
"success"
/>
确定
</van-button>
</div>
</div>
</van-popup>
</template>
<
script
setup
>
import
{
ref
,
computed
,
watch
}
from
'vue'
import
{
parseTime
}
from
'@/utils'
const
{
proxy
}
=
getCurrentInstance
()
const
props
=
defineProps
({
showSearch
:
{
type
:
Boolean
,
default
:
false
},
showStoreSearch
:
{
type
:
Boolean
,
default
:
true
},
queryParams
:
{
type
:
Object
,
default
:
()
=>
({})
}
})
const
emits
=
defineEmits
([
'change'
,
'update:showSearch'
])
// 抽屉显示状态
const
showDrawer
=
computed
({
get
:
()
=>
props
.
showSearch
,
set
:
(
value
)
=>
emits
(
'update:showSearch'
,
value
)
})
// 选择器状态
const
showMonthPicker
=
ref
(
false
)
const
showStatusPicker
=
ref
(
false
)
const
currentMonth
=
ref
([])
// 日期范围
const
minDate
=
new
Date
(
2016
,
0
,
1
)
const
maxDate
=
new
Date
(
new
Date
().
getFullYear
(),
new
Date
().
getMonth
()
+
1
,
31
)
// 状态选项
const
statusOptions
=
[
{
text
:
'全部'
,
value
:
''
},
{
text
:
'未执行'
,
value
:
'未执行'
}
]
// 获取状态文本
const
getStatusText
=
(
value
)
=>
{
const
option
=
statusOptions
.
find
(
item
=>
item
.
value
===
value
)
return
option
?
option
.
text
:
'全部'
}
// 确认月份选择
const
confirmMonth
=
({
selectedValues
:
value
})
=>
{
// value 是 [2025, 2] 这种格式
const
year
=
value
[
0
]
const
month
=
String
(
value
[
1
]).
padStart
(
2
,
'0'
)
props
.
queryParams
.
salesMonth
=
`
${
year
}
-
${
month
}
`
showMonthPicker
.
value
=
false
handleChange
()
}
// 确认状态选择
const
confirmStatus
=
(
value
)
=>
{
props
.
queryParams
.
rqStatus
=
value
.
selectedOptions
[
0
]?.
value
||
''
showStatusPicker
.
value
=
false
handleChange
()
}
// 处理变化
const
handleChange
=
()
=>
{
emits
(
'change'
)
}
// 关闭抽屉
const
handleClose
=
()
=>
{
showDrawer
.
value
=
false
}
// 重置表单
const
handleReset
=
()
=>
{
// 重置所有查询参数
Object
.
keys
(
props
.
queryParams
).
forEach
(
key
=>
{
if
(
key
!==
'pageNum'
&&
key
!==
'pageSize'
)
{
props
.
queryParams
[
key
]
=
''
}
})
handleChange
()
}
// 确定搜索
const
handleConfirm
=
()
=>
{
handleChange
()
handleClose
()
}
// 监听外部showSearch变化
watch
(()
=>
props
.
showSearch
,
(
newVal
)
=>
{
if
(
newVal
)
{
// 打开抽屉时初始化当前月份
if
(
props
.
queryParams
.
salesMonth
)
{
// 如果是日期类型转成字符串
if
(
props
.
queryParams
.
salesMonth
instanceof
Date
)
{
props
.
queryParams
.
salesMonth
=
props
.
queryParams
.
salesMonth
.
toISOString
().
split
(
'T'
)[
0
].
substring
(
0
,
7
)
}
const
[
year
,
month
]
=
props
.
queryParams
.
salesMonth
.
split
(
'-'
)
// currentMonth = [2025, 02] 这种格式
currentMonth
.
value
=
[
parseInt
(
year
),
parseInt
(
month
)]
}
else
{
// 当前年月日
currentMonth
.
value
=
[
new
Date
().
getFullYear
(),
new
Date
().
getMonth
()]
}
}
})
</
script
>
<
style
scoped
lang=
"scss"
>
.search-drawer
{
.search-content
{
height
:
calc
(
100%
-
46px
);
// 减去导航栏高度
overflow-y
:
auto
;
padding
:
16px
;
.van-cell-group
{
margin-bottom
:
20px
;
}
.action-buttons
{
position
:
fixed
;
bottom
:
0
;
left
:
0
;
right
:
0
;
background
:
#fff
;
padding
:
16px
;
display
:
flex
;
gap
:
12px
;
border-top
:
1px
solid
#ebedf0
;
.reset-btn
,
.confirm-btn
{
flex
:
1
;
:deep
(
.van-button__content
)
{
display
:
flex
;
align-items
:
center
;
gap
:
4px
;
}
}
}
}
}
</
style
>
\ No newline at end of file
src/views/mobile/pages/storeExecution/report/components/Table/index.vue
0 → 100644
浏览文件 @
d106e050
差异被折叠。
点击展开。
src/views/mobile/pages/storeExecution/report/index.vue
0 → 100644
浏览文件 @
d106e050
<
template
>
<div
class=
"app-container"
>
<div
class=
"container"
>
<van-nav-bar
fixed
left-arrow
@
click-left=
"router.back()"
title=
"店内执行上报浏览"
right-text=
"筛选"
@
click-right=
"handleClickRight"
/>
<SearchList
v-model:showSearch=
"showSearch"
:queryParams=
"params"
@
change=
"getTableList"
/>
<el-tabs
v-model=
"activeName"
class=
"demo-tabs"
@
tab-click=
"handleClickTabs"
>
<el-tab-pane
label=
"常规陈列"
name=
"常规陈列"
>
<Display
:params=
"params"
ref=
"display"
/>
</el-tab-pane>
<el-tab-pane
label=
"档期计划"
name=
"档期计划"
>
<Schedule
:params=
"params"
ref=
"schedule"
/>
</el-tab-pane>
<el-tab-pane
label=
"档期陈列"
name=
"档期陈列"
>
<ScheduleDis
:params=
"params"
ref=
"scheduleDis"
/>
</el-tab-pane>
<el-tab-pane
label=
"零食陈列"
name=
"零食陈列"
>
<Snack
:params=
"params"
ref=
"snack"
/>
</el-tab-pane>
<el-tab-pane
label=
"三米两秒"
name=
"三米两秒"
>
<ThreeTwoSeconds
:params=
"params"
ref=
"threeTwoSeconds"
/>
</el-tab-pane>
<el-tab-pane
label=
"六小金刚"
name=
"六小金刚"
>
<SixLittleDiamonds
:params=
"params"
ref=
"sixLittleDiamonds"
/>
</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'
import
ThreeTwoSeconds
from
'./tabs/three_two_seconds.vue'
import
SixLittleDiamonds
from
'./tabs/six_little_diamonds.vue'
import
SearchList
from
'./components/SearchList'
const
router
=
useRouter
()
const
activeName
=
ref
(
'常规陈列'
);
const
handleClickTabs
=
(
tab
)
=>
{
activeName
.
value
=
tab
.
name
;
}
const
showSearch
=
ref
(
false
)
const
handleClickRight
=
()
=>
{
showSearch
.
value
=
true
;
}
const
params
=
ref
({
pageNum
:
1
,
pageSize
:
50
,
salesMonth
:
new
Date
(),
deptName
:
''
,
dealerCN
:
''
,
lineNameLike
:
''
,
storeCN
:
''
})
const
display
=
ref
(
null
)
const
schedule
=
ref
(
null
)
const
scheduleDis
=
ref
(
null
)
const
snack
=
ref
(
null
)
const
threeTwoSeconds
=
ref
(
null
)
const
sixLittleDiamonds
=
ref
(
null
)
const
getTableList
=
()
=>
{
display
.
value
.
getTableList
()
schedule
.
value
.
getTableList
()
scheduleDis
.
value
.
getTableList
()
snack
.
value
.
getTableList
()
threeTwoSeconds
.
value
.
getTableList
()
sixLittleDiamonds
.
value
.
getTableList
()
}
</
script
>
<
style
scoped
lang=
"scss"
>
.app-container
{
padding
:
10px
;
padding-top
:
46px
;
.container
{
padding
:
0px
;
.el-tabs
{
height
:
100%
;
display
:
flex
;
flex-direction
:
column-reverse
;
::v-deep
(
.el-tabs__header
)
{
// 确保底部边框线完整显示
border-bottom
:
1px
solid
var
(
--
el-border-color-light
);
width
:
100%
;
}
::v-deep
(
.el-tabs__nav-wrap
)
{
overflow-x
:
auto
;
overflow-y
:
hidden
;
-webkit-overflow-scrolling
:
touch
;
// iOS平滑滚动
scrollbar-width
:
none
;
// Firefox隐藏滚动条
&
:
:
after
{
// 隐藏el-tabs默认的底部伪元素边框
display
:
none
;
}
&
:
:-
webkit-scrollbar
{
display
:
none
;
// Chrome/Safari隐藏滚动条
}
}
::v-deep
(
.el-tabs__nav-scroll
)
{
overflow
:
visible
;
width
:
max-content
;
}
::v-deep
(
.el-tabs__content
)
{
display
:
flex
;
flex-direction
:
column
;
overflow-y
:
scroll
;
// height: 100%;
.el-tab-pane
{
// height: 100%;
display
:
flex
;
flex-direction
:
column
;
}
}
}
}
}
</
style
>
\ No newline at end of file
src/views/mobile/pages/storeExecution/report/tabs/data.jsx
0 → 100644
浏览文件 @
d106e050
This source diff could not be displayed because it is too large. You can
view the blob
instead.
src/views/mobile/pages/storeExecution/report/tabs/display.vue
0 → 100644
浏览文件 @
d106e050
差异被折叠。
点击展开。
src/views/mobile/pages/storeExecution/report/tabs/schedule.vue
0 → 100644
浏览文件 @
d106e050
差异被折叠。
点击展开。
src/views/mobile/pages/storeExecution/report/tabs/schedule_dis.vue
0 → 100644
浏览文件 @
d106e050
差异被折叠。
点击展开。
src/views/mobile/pages/storeExecution/report/tabs/six_little_diamonds.vue
0 → 100644
浏览文件 @
d106e050
差异被折叠。
点击展开。
src/views/mobile/pages/storeExecution/report/tabs/snack.vue
0 → 100644
浏览文件 @
d106e050
差异被折叠。
点击展开。
src/views/mobile/pages/storeExecution/report/tabs/three_two_seconds.vue
0 → 100644
浏览文件 @
d106e050
差异被折叠。
点击展开。
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论