对移动端的工作流的初步调整,做实验用

This commit is contained in:
zlwu committed 2025-07-07 13:25:43 +08:00
1 parent 45b5d58ae4
commit 769df7900e
5 files changed
+1111

No files matched your search

+14
View File
@@ -52,6 +52,20 @@
"navigationBarTitleText": "路由守卫测试",
"navigationStyle": "custom"
}
},
{
"path": "pages/workflow/process",
"style": {
"navigationBarTitleText": "流程详情",
"navigationStyle": "custom"
}
},
{
"path": "pages/workflow/initiated",
"style": {
"navigationBarTitleText": "我发起的流程",
"navigationStyle": "custom"
}
}
],
"globalStyle": {
+667
View File
@@ -133,6 +133,50 @@
</template>
</Card>
<!-- 工作流模块 -->
<Card>
<template #header>
<view class="workflow-header">
<text class="workflow-icon">🔄</text>
<text class="card-title">工作流</text>
</view>
</template>
<template #default>
<view class="workflow-tabs">
<view
v-for="tab in workflowTabs"
:key="tab.key"
class="workflow-tab"
:class="{ active: activeWorkflowTab === tab.key }"
@click="handleWorkflowTabClick(tab.key)"
>
<text class="tab-icon">{{ tab.icon }}</text>
<text class="tab-text">{{ tab.label }}</text>
<view v-if="tab.badge > 0" class="tab-badge">
<text class="tab-badge-text">{{ tab.badge }}</text>
</view>
</view>
</view>
<!-- 我发起的内容 -->
<view v-if="activeWorkflowTab === 'initiated'" class="workflow-content">
<view class="workflow-empty">
<u-button type="primary" @click="gotoInitiatedList">
查看我发起的流程
</u-button>
</view>
</view>
<!-- 其他标签页内容(暂时显示占位) -->
<view v-else class="workflow-placeholder">
<text class="placeholder-icon">🚧</text>
<text class="placeholder-text">
{{ getTabName(activeWorkflowTab) }}功能开发中
</text>
</view>
</template>
</Card>
<!-- 快捷功能 -->
<Card>
<template #header>
@@ -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逻辑可保留
},
},
};
</script>
@@ -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;
+167
View File
@@ -0,0 +1,167 @@
<template>
<view class="initiated-container">
<view class="header">
<text class="title">我发起的流程</text>
</view>
<view v-if="loading" class="loading">加载中...</view>
<view v-else-if="workflowList.length === 0" class="empty">
<text>暂无数据</text>
</view>
<view v-else class="workflow-list">
<view
v-for="item in workflowList"
:key="item.id"
class="workflow-item touchable"
@click="gotoDetail(item)"
>
<view class="workflow-header-info">
<view class="workflow-title-row">
<text class="workflow-title">{{ item.flowName }}</text>
<view class="workflow-status" :style="getStatusColor(item.flowStatus)">
<text class="status-text">{{ getStatusText(item.flowStatus) }}</text>
</view>
</view>
<text class="workflow-code">{{ item.flowCode }}</text>
</view>
</view>
</view>
</view>
</template>
<script>
import { workflowApi, formatTime } from '@/utils/workflow-api';
export default {
data() {
return {
workflowList: [],
loading: true,
};
},
onLoad() {
this.loadWorkflowData();
},
methods: {
async loadWorkflowData() {
this.loading = true;
try {
const res = await workflowApi.pageByCurrent({ pageNum: 1, pageSize: 20 });
if (res.code === 200) {
this.workflowList = res.rows || [];
} else {
uni.showToast({ title: res.msg || '加载失败', icon: 'error' });
}
} catch (e) {
uni.showToast({ title: '网络错误', icon: 'error' });
} finally {
this.loading = false;
}
},
gotoDetail(item) {
const params = [
`businessId=${encodeURIComponent(item.businessId)}`,
`taskId=${encodeURIComponent(item.id)}`,
`type=view`,
`formCustom=${encodeURIComponent(item.formCustom || '')}`,
`formPath=${encodeURIComponent(item.formPath || '')}`,
].join('&');
uni.navigateTo({
url: `/pages/workflow/process?${params}`,
});
},
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;
},
formatTime,
},
};
</script>
<style scoped>
.initiated-container {
padding: 20px;
background: #fff;
min-height: 100vh;
}
.header {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 20px;
}
.title {
font-size: 20px;
font-weight: bold;
}
.loading {
text-align: center;
color: #999;
margin-top: 40px;
}
.empty {
text-align: center;
color: #bbb;
margin-top: 40px;
}
.workflow-list {
margin-top: 12px;
}
.workflow-item {
background: #f8f9fa;
border-radius: 8px;
padding: 16px;
margin-bottom: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.03);
transition: box-shadow 0.2s;
}
.workflow-item:active {
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.06);
}
.workflow-header-info {
margin-bottom: 8px;
}
.workflow-title-row {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 4px;
}
.workflow-title {
font-size: 16px;
font-weight: 500;
color: #333;
flex: 1;
margin-right: 12px;
}
.workflow-status {
padding: 2px 8px;
border-radius: 12px;
font-size: 12px;
font-weight: 500;
white-space: nowrap;
}
.workflow-code {
font-size: 12px;
color: #888;
}
</style>
+117
View File
@@ -0,0 +1,117 @@
<template>
<view class="process-container">
<view class="header">
<text class="title">流程详情</text>
<text v-if="isViewMode" class="view-mode">(只读模式)</text>
</view>
<view v-if="loading" class="loading">加载中...</view>
<view v-else>
<u-form :disabled="isViewMode">
<u-form-item label="流程名称">
<u-input v-model="processDetail.flowName" disabled />
</u-form-item>
<u-form-item label="流程编码">
<u-input v-model="processDetail.flowCode" disabled />
</u-form-item>
<u-form-item label="分类">
<u-input v-model="processDetail.categoryName" disabled />
</u-form-item>
<u-form-item label="状态">
<u-input :value="processDetail.flowStatusName || statusText" disabled />
</u-form-item>
<u-form-item label="创建时间">
<u-input :value="formatTime(processDetail.createTime)" disabled />
</u-form-item>
<!-- 可扩展更多字段 -->
</u-form>
<view v-if="!isViewMode" class="actions">
<u-button type="primary" @click="handleSubmit">提交</u-button>
</view>
</view>
</view>
</template>
<script>
import { workflowApi, formatTime, getStatusStyle } from '@/utils/workflow-api';
export default {
data() {
return {
processDetail: {},
loading: true,
isViewMode: false,
};
},
computed: {
statusText() {
const statusMap = {
draft: '草稿',
waiting: '待审批',
complete: '已完成',
cancel: '已取消',
back: '已退回',
suspend: '挂起',
terminate: '终止',
};
return statusMap[this.processDetail.flowStatus] || '未知';
},
},
onLoad(options) {
this.isViewMode = options.type === 'view';
this.loadDetail(options);
},
methods: {
async loadDetail(options) {
this.loading = true;
try {
// 使用新接口获取详情
const res = await workflowApi.processDetail(options.businessId);
if (res.code === 200 && res.data) {
this.processDetail = res.data;
} else {
uni.showToast({ title: res.msg || '加载失败', icon: 'error' });
}
} catch (e) {
uni.showToast({ title: '网络错误', icon: 'error' });
} finally {
this.loading = false;
}
},
formatTime,
handleSubmit() {
uni.showToast({ title: '提交功能开发中', icon: 'none' });
},
},
};
</script>
<style scoped>
.process-container {
padding: 20px;
background: #fff;
min-height: 100vh;
}
.header {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 20px;
}
.title {
font-size: 20px;
font-weight: bold;
}
.view-mode {
color: #909399;
font-size: 14px;
}
.loading {
text-align: center;
color: #999;
margin-top: 40px;
}
.actions {
margin-top: 32px;
display: flex;
justify-content: flex-end;
}
</style>
+146
View File
@@ -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();
};