From 769df7900ea578064526ecd0176a36b7c8aa004b Mon Sep 17 00:00:00 2001
From: zlwu <704541015@qq.com>
Date: Mon, 7 Jul 2025 13:25:43 +0800
Subject: [PATCH] =?UTF-8?q?=E5=AF=B9=E7=A7=BB=E5=8A=A8=E7=AB=AF=E7=9A=84?=
=?UTF-8?q?=E5=B7=A5=E4=BD=9C=E6=B5=81=E7=9A=84=E5=88=9D=E6=AD=A5=E8=B0=83?=
=?UTF-8?q?=E6=95=B4=EF=BC=8C=E5=81=9A=E5=AE=9E=E9=AA=8C=E7=94=A8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
mobile/src/pages.json | 14 +
mobile/src/pages/index/index.vue | 667 ++++++++++++++++++++++++
mobile/src/pages/workflow/initiated.vue | 167 ++++++
mobile/src/pages/workflow/process.vue | 117 +++++
mobile/src/utils/workflow-api.js | 146 ++++++
5 files changed, 1111 insertions(+)
create mode 100644 mobile/src/pages/workflow/initiated.vue
create mode 100644 mobile/src/pages/workflow/process.vue
create mode 100644 mobile/src/utils/workflow-api.js
diff --git a/mobile/src/pages.json b/mobile/src/pages.json
index a4fa5cf2..fd8c0b30 100644
--- a/mobile/src/pages.json
+++ b/mobile/src/pages.json
@@ -52,6 +52,20 @@
"navigationBarTitleText": "路由守卫测试",
"navigationStyle": "custom"
}
+ },
+ {
+ "path": "pages/workflow/process",
+ "style": {
+ "navigationBarTitleText": "流程详情",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/workflow/initiated",
+ "style": {
+ "navigationBarTitleText": "我发起的流程",
+ "navigationStyle": "custom"
+ }
}
],
"globalStyle": {
diff --git a/mobile/src/pages/index/index.vue b/mobile/src/pages/index/index.vue
index f7e8914d..40d9dd7c 100644
--- a/mobile/src/pages/index/index.vue
+++ b/mobile/src/pages/index/index.vue
@@ -133,6 +133,50 @@
+
+
+
+
+
+
+
+
+ {{ tab.icon }}
+ {{ tab.label }}
+
+ {{ tab.badge }}
+
+
+
+
+
+
+
+
+ 查看我发起的流程
+
+
+
+
+
+
+ 🚧
+
+ {{ getTabName(activeWorkflowTab) }}功能开发中
+
+
+
+
+
@@ -169,6 +213,7 @@ import GridLayout from '@/components/GridLayout.vue';
import { userStorage } from '@/utils/request';
import { userManager, userState } from '@/utils/user';
import { guardedSwitchTab } from '@/utils/route-guard';
+import { workflowApi, getStatusStyle, formatTime, routerJump } from '@/utils/workflow-api';
export default {
name: 'HomePage',
@@ -279,12 +324,32 @@ export default {
{ title: '校历', icon: '📅' },
{ title: '帮助', icon: '❓' },
],
+ // 工作流相关数据
+ workflowTabs: [
+ { key: 'initiated', label: '我发起的', icon: '📝', badge: 0 },
+ { key: 'todo', label: '我的待办', icon: '⏰', badge: 0 },
+ { key: 'finished', label: '我的已办', icon: '✅', badge: 0 },
+ { key: 'copy', label: '我的抄送', icon: '📋', badge: 0 },
+ ],
+ activeWorkflowTab: 'initiated',
+ workflowList: [],
+ workflowLoading: false,
+ workflowQuery: {
+ pageNum: 1,
+ pageSize: 10,
+ flowCode: '',
+ category: '',
+ },
+ categoryList: [],
+ showCategories: false,
+ hasMore: true,
};
},
onLoad() {
this.checkLoginStatus();
this.updateDateTime();
this.startTimeUpdate();
+ this.loadWorkflowData();
},
onUnload() {
if (this.timeTimer) {
@@ -359,6 +424,282 @@ export default {
viewAllNews() {
uni.showToast({ title: '查看全部动态', icon: 'none' });
},
+
+ // 工作流相关方法
+ switchWorkflowTab(tab) {
+ this.activeWorkflowTab = tab;
+ this.workflowQuery.pageNum = 1;
+ this.workflowList = [];
+ this.hasMore = true;
+
+ if (tab === 'initiated') {
+ this.loadWorkflowData();
+ } else {
+ // 其他标签页暂时不加载数据
+ uni.showToast({ title: this.getTabName(tab) + '功能开发中', icon: 'none' });
+ }
+ },
+
+ async loadWorkflowData() {
+ if (this.workflowLoading) return;
+
+ this.workflowLoading = true;
+ try {
+ const response = await workflowApi.pageByCurrent(this.workflowQuery);
+
+ if (response.code === 200) {
+ const newData = response.rows || [];
+
+ if (this.workflowQuery.pageNum === 1) {
+ this.workflowList = newData;
+ } else {
+ this.workflowList = [...this.workflowList, ...newData];
+ }
+
+ // 检查是否还有更多数据
+ this.hasMore = newData.length === this.workflowQuery.pageSize;
+
+ // 更新待办数量徽章
+ this.updateWorkflowBadges();
+ } else {
+ uni.showToast({
+ title: response.msg || '加载失败',
+ icon: 'error',
+ });
+ }
+ } catch (error) {
+ console.error('加载工作流数据失败:', error);
+ uni.showToast({
+ title: '网络错误',
+ icon: 'error',
+ });
+ } finally {
+ this.workflowLoading = false;
+ }
+ },
+
+ async loadCategoryData() {
+ try {
+ const response = await workflowApi.categoryTree();
+ if (response.code === 200) {
+ this.categoryList = response.data || [];
+ }
+ } catch (error) {
+ console.error('加载分类数据失败:', error);
+ }
+ },
+
+ loadMoreWorkflow() {
+ if (!this.hasMore || this.workflowLoading) return;
+
+ this.workflowQuery.pageNum += 1;
+ this.loadWorkflowData();
+ },
+
+ handleWorkflowSearch() {
+ this.workflowQuery.pageNum = 1;
+ this.workflowList = [];
+ this.hasMore = true;
+ this.loadWorkflowData();
+ },
+
+ showCategoryFilter() {
+ this.showCategories = !this.showCategories;
+ if (this.showCategories && this.categoryList.length === 0) {
+ this.loadCategoryData();
+ }
+ },
+
+ selectCategory(categoryId) {
+ this.workflowQuery.category = categoryId;
+ this.showCategories = false;
+ this.handleWorkflowSearch();
+ },
+
+ // 保证 handleWorkflowItemClick 统一跳转详情
+ handleWorkflowItemClick(item) {
+ this.handleView(item);
+ },
+
+ handleEdit(item) {
+ const routerJumpVo = {
+ businessId: item.businessId,
+ taskId: item.id,
+ type: 'update',
+ formCustom: item.formCustom,
+ formPath: item.formPath,
+ };
+ routerJump(routerJumpVo);
+ },
+
+ handleView(item) {
+ const routerJumpVo = {
+ businessId: item.businessId,
+ taskId: item.id,
+ type: 'view',
+ formCustom: item.formCustom,
+ formPath: item.formPath,
+ };
+ // 直接使用 uni.navigateTo 跳转到新页面
+ const params = Object.keys(routerJumpVo)
+ .filter((key) => routerJumpVo[key] !== undefined && routerJumpVo[key] !== null)
+ .map((key) => `${key}=${encodeURIComponent(routerJumpVo[key])}`)
+ .join('&');
+ uni.navigateTo({
+ url: `/pages/workflow/process?${params}`,
+ fail: () => {
+ uni.showToast({
+ title: '页面跳转失败',
+ icon: 'error',
+ });
+ },
+ });
+ },
+
+ async handleDelete(item) {
+ try {
+ const confirmed = await this.showConfirm('确认删除', '是否确认删除该工作流?');
+ if (!confirmed) return;
+
+ const response = await workflowApi.deleteByInstanceIds([item.id]);
+ if (response.code === 200) {
+ uni.showToast({
+ title: '删除成功',
+ icon: 'success',
+ });
+ this.handleWorkflowSearch();
+ } else {
+ uni.showToast({
+ title: response.msg || '删除失败',
+ icon: 'error',
+ });
+ }
+ } catch (error) {
+ console.error('删除失败:', error);
+ uni.showToast({
+ title: '删除失败',
+ icon: 'error',
+ });
+ }
+ },
+
+ async handleCancel(item) {
+ try {
+ const confirmed = await this.showConfirm('确认撤销', '是否确认撤销该工作流?');
+ if (!confirmed) return;
+
+ const data = {
+ businessId: item.businessId,
+ message: '申请人撤销流程!',
+ };
+
+ const response = await workflowApi.cancelProcessApply(data);
+ if (response.code === 200) {
+ uni.showToast({
+ title: '撤销成功',
+ icon: 'success',
+ });
+ this.handleWorkflowSearch();
+ } else {
+ uni.showToast({
+ title: response.msg || '撤销失败',
+ icon: 'error',
+ });
+ }
+ } catch (error) {
+ console.error('撤销失败:', error);
+ uni.showToast({
+ title: '撤销失败',
+ icon: 'error',
+ });
+ }
+ },
+
+ // 工具方法
+ getStatusText(status) {
+ const statusMap = {
+ draft: '草稿',
+ waiting: '待审批',
+ complete: '已完成',
+ cancel: '已取消',
+ back: '已退回',
+ suspend: '挂起',
+ terminate: '终止',
+ };
+ return statusMap[status] || '未知';
+ },
+
+ getStatusColor(status) {
+ const colorMap = {
+ draft: 'background-color: #909399; color: white',
+ waiting: 'background-color: #e6a23c; color: white',
+ complete: 'background-color: #67c23a; color: white',
+ cancel: 'background-color: #f56c6c; color: white',
+ back: 'background-color: #409eff; color: white',
+ suspend: 'background-color: #909399; color: white',
+ terminate: 'background-color: #f56c6c; color: white',
+ };
+ return colorMap[status] || colorMap.draft;
+ },
+
+ formatWorkflowTime(timeStr) {
+ return formatTime(timeStr);
+ },
+
+ canEdit(status) {
+ return ['draft', 'cancel', 'back'].includes(status);
+ },
+
+ canDelete(status) {
+ return ['draft', 'cancel', 'back'].includes(status);
+ },
+
+ canCancel(status) {
+ return status === 'waiting';
+ },
+
+ getTabName(tab) {
+ const tabMap = {
+ initiated: '我发起的',
+ todo: '我的待办',
+ finished: '我的已办',
+ copy: '我的抄送',
+ };
+ return tabMap[tab] || '';
+ },
+
+ updateWorkflowBadges() {
+ // 这里可以根据实际需求更新各个标签页的徽章数量
+ // 暂时设置为工作流列表的长度
+ const tabIndex = this.workflowTabs.findIndex((tab) => tab.key === 'initiated');
+ if (tabIndex !== -1) {
+ this.workflowTabs[tabIndex].badge = this.workflowList.length;
+ }
+ },
+
+ showConfirm(title, content) {
+ return new Promise((resolve) => {
+ uni.showModal({
+ title,
+ content,
+ success: (res) => {
+ resolve(res.confirm);
+ },
+ fail: () => {
+ resolve(false);
+ },
+ });
+ });
+ },
+ gotoInitiatedList() {
+ uni.navigateTo({
+ url: '/pages/workflow/initiated',
+ });
+ },
+ handleWorkflowTabClick(tab) {
+ this.activeWorkflowTab = tab;
+ // 其他tab逻辑可保留
+ },
},
};
@@ -637,6 +978,332 @@ export default {
height: 20px;
}
+/* 工作流模块样式 */
+.workflow-header {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.workflow-icon {
+ color: #409eff;
+ font-size: $uni-font-size-lg;
+}
+
+.workflow-tabs {
+ display: flex;
+ margin-bottom: $uni-spacing-col-lg;
+ background: $uni-bg-color-grey;
+ border-radius: $uni-border-radius-base;
+ padding: 4px;
+}
+
+.workflow-tab {
+ flex: 1;
+ padding: 8px 12px;
+ text-align: center;
+ border-radius: $uni-border-radius-base;
+ position: relative;
+ transition: $uni-transition;
+
+ &.active {
+ background: $uni-color-primary;
+ color: $uni-text-color-inverse;
+ }
+}
+
+.tab-icon {
+ font-size: 16px;
+ margin-bottom: 2px;
+}
+
+.tab-text {
+ font-size: $uni-font-size-sm;
+ font-weight: 500;
+}
+
+.tab-badge {
+ position: absolute;
+ top: -2px;
+ right: -2px;
+ background: #ff4757;
+ border-radius: $uni-border-radius-circle;
+ min-width: 16px;
+ height: 16px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.tab-badge-text {
+ color: $uni-text-color-inverse;
+ font-size: 10px;
+ font-weight: 500;
+ padding: 0 4px;
+}
+
+.workflow-content {
+ margin-top: $uni-spacing-col-base;
+}
+
+/* 搜索和筛选 */
+.workflow-filter {
+ margin-bottom: $uni-spacing-col-lg;
+}
+
+.filter-row {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ margin-bottom: 12px;
+}
+
+.search-input {
+ flex: 1;
+ display: flex;
+ align-items: center;
+ background: $uni-bg-color-grey;
+ border-radius: $uni-border-radius-base;
+ padding: 0 12px;
+ height: 36px;
+
+ .iconfont {
+ margin-right: 8px;
+ color: $uni-text-color-grey;
+ font-size: 16px;
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='%23999' viewBox='0 0 24 24'%3E%3Cpath d='M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z'/%3E%3C/svg%3E");
+ width: 16px;
+ height: 16px;
+ background-size: contain;
+ background-repeat: no-repeat;
+ background-position: center;
+ filter: invert(60%);
+ }
+
+ input {
+ flex: 1;
+ border: none;
+ background: transparent;
+ font-size: $uni-font-size-base;
+ color: $uni-text-color;
+ }
+}
+
+.filter-button {
+ width: 36px;
+ height: 36px;
+ background: $uni-color-primary;
+ border-radius: $uni-border-radius-base;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ .iconfont {
+ color: $uni-text-color-inverse;
+ font-size: 16px;
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='%23fff' viewBox='0 0 24 24'%3E%3Cpath d='M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z'/%3E%3C/svg%3E");
+ width: 16px;
+ height: 16px;
+ background-size: contain;
+ background-repeat: no-repeat;
+ background-position: center;
+ }
+}
+
+.category-filter {
+ margin-top: 12px;
+}
+
+.category-scroll {
+ width: 100%;
+}
+
+.category-list {
+ display: flex;
+ gap: 8px;
+ white-space: nowrap;
+ padding-bottom: 4px;
+}
+
+.category-item {
+ padding: 6px 12px;
+ background: $uni-bg-color-grey;
+ border-radius: $uni-border-radius-base;
+ white-space: nowrap;
+ transition: $uni-transition;
+
+ &.active {
+ background: $uni-color-primary;
+ color: $uni-text-color-inverse;
+ }
+}
+
+.category-text {
+ font-size: $uni-font-size-sm;
+}
+
+/* 工作流列表 */
+.workflow-loading {
+ text-align: center;
+ padding: 40px 0;
+ color: $uni-text-color-grey;
+}
+
+.workflow-empty {
+ text-align: center;
+ padding: 40px 0;
+ color: $uni-text-color-grey;
+}
+
+.empty-icon {
+ font-size: 48px;
+ margin-bottom: 12px;
+ display: block;
+}
+
+.empty-text {
+ font-size: $uni-font-size-base;
+}
+
+.workflow-list {
+ margin-top: $uni-spacing-col-base;
+}
+
+.workflow-item {
+ background: $uni-bg-color;
+ border-radius: $uni-border-radius-base;
+ padding: $uni-spacing-col-lg;
+ margin-bottom: 12px;
+ box-shadow: $uni-shadow;
+ transition: $uni-transition;
+
+ &:hover {
+ box-shadow: $uni-shadow-hover;
+ }
+}
+
+.workflow-header-info {
+ margin-bottom: 12px;
+}
+
+.workflow-title-row {
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-start;
+ margin-bottom: 8px;
+}
+
+.workflow-title {
+ font-size: $uni-font-size-lg;
+ font-weight: 500;
+ color: $uni-text-color;
+ flex: 1;
+ margin-right: 12px;
+}
+
+.workflow-status {
+ padding: 4px 8px;
+ border-radius: $uni-border-radius-base;
+ font-size: $uni-font-size-sm;
+ font-weight: 500;
+ white-space: nowrap;
+}
+
+.workflow-code {
+ font-size: $uni-font-size-sm;
+ color: $uni-text-color-grey;
+}
+
+.workflow-meta {
+ margin-bottom: 12px;
+}
+
+.meta-item {
+ display: flex;
+ margin-bottom: 4px;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+}
+
+.meta-label {
+ font-size: $uni-font-size-sm;
+ color: $uni-text-color-grey;
+ width: 70px;
+ flex-shrink: 0;
+}
+
+.meta-value {
+ font-size: $uni-font-size-sm;
+ color: $uni-text-color;
+ flex: 1;
+}
+
+.workflow-actions {
+ display: flex;
+ gap: 8px;
+ flex-wrap: wrap;
+}
+
+.action-button {
+ padding: 6px 12px;
+ border-radius: $uni-border-radius-base;
+ font-size: $uni-font-size-sm;
+ font-weight: 500;
+ text-align: center;
+ min-width: 50px;
+ transition: $uni-transition;
+
+ &.edit {
+ background: #409eff;
+ color: $uni-text-color-inverse;
+ }
+
+ &.delete {
+ background: #f56c6c;
+ color: $uni-text-color-inverse;
+ }
+
+ &.view {
+ background: $uni-color-primary;
+ color: $uni-text-color-inverse;
+ }
+
+ &.cancel {
+ background: #e6a23c;
+ color: $uni-text-color-inverse;
+ }
+
+ &:active {
+ opacity: 0.8;
+ transform: scale(0.98);
+ }
+}
+
+.load-more {
+ text-align: center;
+ padding: 20px 0;
+ color: $uni-color-primary;
+ font-size: $uni-font-size-base;
+}
+
+.workflow-placeholder {
+ text-align: center;
+ padding: 60px 0;
+ color: $uni-text-color-grey;
+}
+
+.placeholder-icon {
+ font-size: 48px;
+ margin-bottom: 12px;
+ display: block;
+}
+
+.placeholder-text {
+ font-size: $uni-font-size-base;
+}
+
/* 页面进入动画 */
.page-container {
animation: fadeInUp 0.6s ease-out;
diff --git a/mobile/src/pages/workflow/initiated.vue b/mobile/src/pages/workflow/initiated.vue
new file mode 100644
index 00000000..d0169f7a
--- /dev/null
+++ b/mobile/src/pages/workflow/initiated.vue
@@ -0,0 +1,167 @@
+
+
+
+ 加载中...
+
+ 暂无数据
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mobile/src/pages/workflow/process.vue b/mobile/src/pages/workflow/process.vue
new file mode 100644
index 00000000..8fe3a3ed
--- /dev/null
+++ b/mobile/src/pages/workflow/process.vue
@@ -0,0 +1,117 @@
+
+
+
+ 加载中...
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 提交
+
+
+
+
+
+
+
+
diff --git a/mobile/src/utils/workflow-api.js b/mobile/src/utils/workflow-api.js
new file mode 100644
index 00000000..7b8607da
--- /dev/null
+++ b/mobile/src/utils/workflow-api.js
@@ -0,0 +1,146 @@
+import { request } from './request.js';
+
+// 工作流API配置
+export const workflowApi = {
+ // 获取流程实例分页列表(我发起的)
+ pageByCurrent: (params) =>
+ request({
+ url: '/workflow/instance/pageByCurrent',
+ method: 'GET',
+ data: params,
+ }),
+ // 删除流程实例
+ deleteByInstanceIds: (instanceIds) =>
+ request({
+ url: '/workflow/instance/deleteByInstanceIds',
+ method: 'DELETE',
+ data: instanceIds,
+ }),
+ // 撤销流程申请
+ cancelProcessApply: (data) =>
+ request({
+ url: '/workflow/instance/cancelProcessApply',
+ method: 'POST',
+ data,
+ }),
+ // 获取流程分类树
+ categoryTree: () =>
+ request({
+ url: '/workflow/category/categoryTree',
+ method: 'GET',
+ }),
+ // 获取我的待办任务
+ todoTasks: (params) =>
+ request({
+ url: '/workflow/task/todoTasks',
+ method: 'GET',
+ data: params,
+ }),
+ // 获取我的已办任务
+ finishedTasks: (params) =>
+ request({
+ url: '/workflow/task/finishedTasks',
+ method: 'GET',
+ data: params,
+ }),
+ // 获取我的抄送任务
+ copyTasks: (params) =>
+ request({
+ url: '/workflow/task/copyTasks',
+ method: 'GET',
+ data: params,
+ }),
+ // 启动流程实例
+ startProcessInstance: (data) =>
+ request({
+ url: '/workflow/instance/start',
+ method: 'POST',
+ data,
+ }),
+ // 办理任务
+ complete: (data) =>
+ request({
+ url: '/workflow/task/complete',
+ method: 'POST',
+ data,
+ }),
+ // 获取流程历史
+ processHistory: (instanceId) =>
+ request({
+ url: `/workflow/instance/history/${instanceId}`,
+ method: 'GET',
+ }),
+ // 获取流程实例详情(根据业务ID)
+ processDetail: (businessId) =>
+ request({
+ url: `/workflow/instance/getInfo/${businessId}`,
+ method: 'GET',
+ }),
+};
+
+// 工作流状态映射
+export const workflowStatus = {
+ draft: { label: '草稿', color: '#909399', icon: '📝' },
+ waiting: { label: '待审批', color: '#e6a23c', icon: '⏰' },
+ complete: { label: '已完成', color: '#67c23a', icon: '✅' },
+ cancel: { label: '已取消', color: '#f56c6c', icon: '❌' },
+ back: { label: '已退回', color: '#409eff', icon: '↩️' },
+ suspend: { label: '挂起', color: '#909399', icon: '⏸️' },
+ terminate: { label: '终止', color: '#f56c6c', icon: '🛑' },
+};
+
+// 工作流分类图标映射
+export const categoryIcons = {
+ leave: '📅',
+ repair: '🔧',
+ purchase: '🛒',
+ reimbursement: '💰',
+ approval: '📋',
+ default: '📄',
+};
+
+// 获取状态样式
+export const getStatusStyle = (status) => workflowStatus[status] || workflowStatus.draft;
+
+// 获取分类图标
+export const getCategoryIcon = (category) => categoryIcons[category] || categoryIcons.default;
+
+// 工作流路由跳转方法
+export const routerJump = (routerJumpVo) => {
+ const { businessId, taskId, type, formCustom, formPath } = routerJumpVo;
+ const params = { businessId, taskId, type, formCustom, formPath };
+ const queryString = Object.keys(params)
+ .filter((key) => params[key] !== undefined && params[key] !== null)
+ .map((key) => `${key}=${encodeURIComponent(params[key])}`)
+ .join('&');
+ uni.navigateTo({
+ url: `/pages/workflow/process?${queryString}`,
+ fail: () => {
+ uni.showToast({
+ title: '页面跳转失败',
+ icon: 'error',
+ });
+ },
+ });
+};
+
+// 格式化时间显示
+export const formatTime = (timeStr) => {
+ if (!timeStr) return '';
+ const time = new Date(timeStr);
+ const now = new Date();
+ const diff = now.getTime() - time.getTime();
+ if (diff < 60 * 1000) {
+ return '刚刚';
+ }
+ if (diff < 60 * 60 * 1000) {
+ return `${Math.floor(diff / (60 * 1000))}分钟前`;
+ }
+ if (diff < 24 * 60 * 60 * 1000) {
+ return `${Math.floor(diff / (60 * 60 * 1000))}小时前`;
+ }
+ if (diff < 7 * 24 * 60 * 60 * 1000) {
+ return `${Math.floor(diff / (24 * 60 * 60 * 1000))}天前`;
+ }
+ return time.toLocaleDateString();
+};