feat: 工作台常用入口可批量编辑,简报改为横幅,列表补下拉刷新
各角色工作台支持批量保存常用入口和业务员看板;AI 简报改成单行横幅避免锁死滑动;仓库/提现/对账等列表统一下拉刷新,并补代付码弹窗。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<!--
|
||||
功能中心页共享组件(诊所管理员/平台管理员/业务员/诊所推广员四端「功能」tab 复用)
|
||||
结构:顶部搜索框 + u-tabs/swiper 联动(常用 | 全部 | 各分组),金刚区宫格展示入口
|
||||
交互:点击入口跳转并静默上报使用;长按入口收藏/取消收藏到「我的常用」;搜索本地过滤
|
||||
结构:顶部搜索框 + u-tabs/swiper 联动(全部 | 各分组),金刚区宫格展示入口
|
||||
常用/最近只在首页,本页不再重复;长按仍可收藏到首页「我的常用」
|
||||
放主包 components/ 原因:四端页面分属不同分包,微信不允许跨分包引用组件(同 xk-service-card)
|
||||
-->
|
||||
<view class="wf-wrap" :style="{ height: wrapHeight }">
|
||||
@@ -33,7 +33,14 @@
|
||||
</view>
|
||||
|
||||
<!-- 搜索态:覆盖 tabs/swiper,展示扁平匹配结果 -->
|
||||
<scroll-view v-else-if="isSearching" scroll-y class="wf-search-scroll">
|
||||
<scroll-view
|
||||
v-else-if="isSearching"
|
||||
scroll-y
|
||||
class="wf-search-scroll"
|
||||
refresher-enabled
|
||||
:refresher-triggered="isRefreshing"
|
||||
@refresherrefresh="onRefresherRefresh"
|
||||
>
|
||||
<view class="wf-page">
|
||||
<view class="wf-panel" v-if="searchItems.length">
|
||||
<view class="wf-grid">
|
||||
@@ -65,40 +72,15 @@
|
||||
</view>
|
||||
<swiper class="wf-swiper" :current="current" @change="onSwiperChange">
|
||||
<swiper-item v-for="(page, pi) in swiperPages" :key="pi">
|
||||
<scroll-view scroll-y class="wf-scroll">
|
||||
<!-- 常用 tab:我的常用 + 最近使用两个分区 -->
|
||||
<view v-if="page.type === 'fav'" class="wf-page">
|
||||
<view class="wf-group-title">我的常用</view>
|
||||
<view class="wf-panel" v-if="favItems.length">
|
||||
<view class="wf-grid">
|
||||
<xk-service-card
|
||||
v-for="item in favItems" :key="item.key"
|
||||
mode="grid"
|
||||
:icon="item.icon" :label="item.label" :theme="item.theme"
|
||||
@click="onItemTap(item)" @longpress="onItemLongpress(item)"
|
||||
></xk-service-card>
|
||||
</view>
|
||||
</view>
|
||||
<view class="wf-hint" v-else>
|
||||
<text>长按任意功能,可添加到我的常用</text>
|
||||
</view>
|
||||
<view class="wf-group-title">最近使用</view>
|
||||
<view class="wf-panel" v-if="recentItems.length">
|
||||
<view class="wf-grid">
|
||||
<xk-service-card
|
||||
v-for="item in recentItems" :key="item.key"
|
||||
mode="grid"
|
||||
:icon="item.icon" :label="item.label" :theme="item.theme"
|
||||
@click="onItemTap(item)" @longpress="onItemLongpress(item)"
|
||||
></xk-service-card>
|
||||
</view>
|
||||
</view>
|
||||
<view class="wf-hint" v-else>
|
||||
<text>暂无使用记录,点击功能后会出现在这里</text>
|
||||
</view>
|
||||
</view>
|
||||
<scroll-view
|
||||
scroll-y
|
||||
class="wf-scroll"
|
||||
refresher-enabled
|
||||
:refresher-triggered="isRefreshing"
|
||||
@refresherrefresh="onRefresherRefresh"
|
||||
>
|
||||
<!-- 全部 tab:所有分组一页看全(分组标题 + 各组金刚区) -->
|
||||
<view v-else-if="page.type === 'all'" class="wf-page">
|
||||
<view v-if="page.type === 'all'" class="wf-page">
|
||||
<view v-for="section in sections" :key="section.key">
|
||||
<view class="wf-group-title">{{ section.title }}</view>
|
||||
<view class="wf-panel">
|
||||
@@ -130,6 +112,12 @@
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
</block>
|
||||
|
||||
<!-- 右下角悬浮:打开全部入口的加减编辑(已加减号 / 未加加号,支持批量) -->
|
||||
<view v-if="!loading && !failed && sections.length" class="wf-fab" hover-class="wf-fab-hover" @tap="openEditor">
|
||||
<u-icon name="plus" color="#FFFFFF" size="40"></u-icon>
|
||||
</view>
|
||||
<workbench-fav-editor ref="favEditor" :api="api" @saved="onEditorSaved"></workbench-fav-editor>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -144,17 +132,20 @@
|
||||
* - getEntries() GET 当前角色启用入口(扁平数组)
|
||||
* - getUserData() GET { favorites: [], recents: [] }
|
||||
* - toggleFavorite(code) POST 收藏/取消收藏,data = { is_favorite }
|
||||
* - batchSaveFavorites(codes) POST 批量保存常用
|
||||
* - reportUsage(code) POST 使用上报(静默,失败不影响跳转)
|
||||
*
|
||||
* 事件:
|
||||
* - @special(item):点击了无 path 的特殊入口(如推广员端 cs_transfer 传方弹窗),由壳页按 code 处理
|
||||
*/
|
||||
import { groupEntriesToSections, normalizeEntry } from '@/utils/workbenchEntries.js';
|
||||
import pullRefreshMixin from '@/subPackages/sub_business_shared/common/pullRefreshMixin.js';
|
||||
|
||||
export default {
|
||||
name: 'WorkbenchFeatures',
|
||||
mixins: [pullRefreshMixin],
|
||||
props: {
|
||||
/** 角色接口集合:{ getEntries, getUserData, toggleFavorite, reportUsage } */
|
||||
/** 角色接口集合:{ getEntries, getUserData, toggleFavorite, batchSaveFavorites, reportUsage } */
|
||||
api: { type: Object, required: true },
|
||||
},
|
||||
data() {
|
||||
@@ -163,9 +154,8 @@ export default {
|
||||
failed: false,
|
||||
/** 分组入口(groupEntriesToSections 输出结构) */
|
||||
sections: [],
|
||||
/** 我的常用 / 最近使用(已归一化为渲染项) */
|
||||
/** 仅用于长按菜单判断是否已收藏,不在本页展示列表(展示在首页) */
|
||||
favItems: [],
|
||||
recentItems: [],
|
||||
keyword: '',
|
||||
current: 0,
|
||||
/** 内容区高度(px):layout 的 navbar 与 tabbar 之间,swiper 需要确定高度才能滚动 */
|
||||
@@ -179,10 +169,9 @@ export default {
|
||||
isSearching() {
|
||||
return this.keyword.trim() !== '';
|
||||
},
|
||||
/** swiper 页序列:固定「常用」「全部」两页在前,其后每个分组一页 */
|
||||
/** swiper 页序列:固定「全部」在前,其后每个分组一页(常用/最近只在首页) */
|
||||
swiperPages() {
|
||||
const pages = [
|
||||
{ key: '__fav__', type: 'fav', title: '常用', items: [] },
|
||||
{ key: '__all__', type: 'all', title: '全部', items: [] },
|
||||
];
|
||||
this.sections.forEach((s) => {
|
||||
@@ -238,34 +227,39 @@ export default {
|
||||
}
|
||||
this.bodyHeight = Math.max(400, sys.windowHeight - top - 100 * rpx - safeBottom);
|
||||
},
|
||||
/** 全量加载:入口列表(必须)+ 用户数据(失败静默,常用/历史留空) */
|
||||
/**
|
||||
* 全量加载入口 + 收藏状态。
|
||||
* 下拉刷新时不切整页 loading,否则 scroll-view 被卸掉转圈收不起来。
|
||||
*/
|
||||
reload() {
|
||||
this.loading = true;
|
||||
this.failed = false;
|
||||
this.api.getEntries().then(({ data, ok }) => {
|
||||
const silent = this.isRefreshing;
|
||||
if (!silent) {
|
||||
this.loading = true;
|
||||
this.failed = false;
|
||||
}
|
||||
const entriesP = this.api.getEntries().then(({ data, ok }) => {
|
||||
if (ok && Array.isArray(data)) {
|
||||
this.sections = groupEntriesToSections(data);
|
||||
// 分组数变化时防止 current 越界(固定页为常用+全部两页,如后台调整了分组)
|
||||
if (this.current >= this.sections.length + 2) this.current = 0;
|
||||
} else {
|
||||
if (this.current >= this.sections.length + 1) this.current = 0;
|
||||
this.failed = false;
|
||||
} else if (!silent) {
|
||||
this.failed = true;
|
||||
}
|
||||
this.loading = false;
|
||||
}).catch(() => {
|
||||
this.failed = true;
|
||||
if (!silent) this.failed = true;
|
||||
}).finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
this.refreshUserData();
|
||||
return Promise.all([entriesP, this.refreshUserData()]);
|
||||
},
|
||||
/** 刷新常用/最近使用(壳页 onShow 也会调用,保证跳转返回后历史及时更新) */
|
||||
/** 刷新收藏状态(长按文案用;列表本身只在首页展示) */
|
||||
refreshUserData() {
|
||||
if (!this.api.getUserData) return;
|
||||
this.api.getUserData().then(({ data, ok }) => {
|
||||
if (!this.api.getUserData) return Promise.resolve();
|
||||
return this.api.getUserData().then(({ data, ok }) => {
|
||||
if (!ok || !data) return;
|
||||
this.favItems = this.normalizeList(data.favorites);
|
||||
this.recentItems = this.normalizeList(data.recents);
|
||||
}).catch(() => {
|
||||
// 用户数据是增强信息,失败静默(不打断功能页主流程)
|
||||
// 收藏状态是增强信息,失败静默(不打断功能页主流程)
|
||||
});
|
||||
},
|
||||
/** 后端入口对象数组 → 渲染项数组(与首页同一映射,字段完全一致) */
|
||||
@@ -318,6 +312,14 @@ export default {
|
||||
},
|
||||
});
|
||||
},
|
||||
/** 打开全部入口编辑抽屉(悬浮按钮) */
|
||||
openEditor() {
|
||||
if (this.$refs.favEditor) this.$refs.favEditor.open();
|
||||
},
|
||||
/** 批量保存后刷新收藏状态,长按菜单文案与首页常用保持一致 */
|
||||
onEditorSaved() {
|
||||
this.refreshUserData();
|
||||
},
|
||||
/** 收藏切换:成功后按后端返回状态提示并刷新常用列表 */
|
||||
doToggleFavorite(item) {
|
||||
if (!this.api.toggleFavorite) return;
|
||||
@@ -340,6 +342,7 @@ export default {
|
||||
<style lang="scss" scoped>
|
||||
/* 纵向 flex:搜索/tabs 固定高度,swiper 吃满剩余空间(swiper 必须有确定高度才能滚动) */
|
||||
.wf-wrap {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: #f9fafb;
|
||||
@@ -429,4 +432,22 @@ export default {
|
||||
.wf-retry-hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
/* 悬浮添加:贴内容区右下,wrap 已扣掉 tabbar 高度 */
|
||||
.wf-fab {
|
||||
position: absolute;
|
||||
right: 32rpx;
|
||||
bottom: 32rpx;
|
||||
width: 104rpx;
|
||||
height: 104rpx;
|
||||
border-radius: 52rpx;
|
||||
background: #6ACDBB;
|
||||
box-shadow: 0 8rpx 24rpx rgba(106, 205, 187, 0.35);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 20;
|
||||
}
|
||||
.wf-fab-hover {
|
||||
opacity: 0.85;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user