对移动端的工作流的初步调整,做实验用
This commit is contained in:
1 parent
45b5d58ae4
commit
769df7900e
5 files changed
+1111
No files matched your search
@@ -52,6 +52,20 @@
|
||||
"navigationBarTitleText": "路由守卫测试",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/workflow/process",
|
||||
"style": {
|
||||
"navigationBarTitleText": "流程详情",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/workflow/initiated",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我发起的流程",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
}
|
||||
],
|
||||
"globalStyle": {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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();
|
||||
};
|
||||
Reference in new issue
Block a user