feat: base 修改wf组件

This commit is contained in:
caoqianming 2025-10-17 11:07:06 +08:00
parent 7b2e58c318
commit 56ad08d378
5 changed files with 351 additions and 28 deletions

View File

@ -1,27 +1,26 @@
<template>
<view class="container">
<view class="header">
<uni-row :gutter="12">
<uni-col :span="20">
<uni-segmented-control :current="current" :values="items" styleType="button" @clickItem="onClickItem">
</uni-segmented-control>
</uni-col>
<uni-col :span="4">
<view @click="openDg" style="text-align: center;">
筛选
</view>
</uni-col>
</uni-row>
<view class="header_sticky">
<uni-segmented-control :current="current" :values="items" styleType="button" @clickItem="onClickItem" style="flex:1;">
</uni-segmented-control>
<view @click="openDg" style="text-align: center; width: 100rpx;">
筛选
</view>
</view>
<view style="height: 8rpx;"></view>
<view style="height:8rpx"></view>
<view>
<uni-list :border="true">
<uni-list-item v-for="(item ,index) in tlist" :index="index" :key="index" direction="column">
<uni-list-item v-for="(item ,index) in tlist" :index="index" :key="index" direction="column" @click="itemClick(item)" :clickable="true">
<template v-slot:header>{{item.title}}</template>
<template v-slot:body>
<view style="color: gray; font-size: 28rpx;">所属工作流: {{item.workflow_.name}}</view>
<view style="color: gray; font-size: 28rpx;">工单状态: {{item.state_.name}}</view>
<view style="color: gray; font-size: 28rpx;">提交时间: {{item.create_time}}</view>
<view style="color: gray; font-size: 28rpx;">最近更新: {{item.update_time}}</view>
<view style="color: gray; font-size: 26rpx;">工作流: {{item.workflow_.name}}</view>
<view style="color: gray; font-size: 26rpx;">工单状态:
<uni-tag :text="actStateEnum[item.act_state]?.text" :circle="true" size="small" :type="actStateEnum[item.act_state]?.type" :inverted="true"
style="font-weight: 460;"></uni-tag>
{{item.state_.name}}
</view>
<view style="color: gray; font-size: 26rpx;">提交时间: {{item.create_time}}</view>
<view style="color: gray; font-size: 26rpx;">最近更新: {{item.update_time}}</view>
</template>
</uni-list-item>
</uni-list>
@ -30,12 +29,15 @@
<view style="height: 188rpx;"></view>
</uni-popup>
</view>
</view>
</template>
<script>
import {actStateEnum} from "@/utils/enum.js"
export default {
data() {
return {
actStateEnum,
searchValue: '',
current: 0,
items_e: ['duty', 'owner', 'worked', 'all'],
@ -43,18 +45,29 @@
pageSize: 20,
items: ['待办', '我的', '已办', '抄送我'],
tlist: [],
status: 'noMore'
status: 'more',
}
},
onLoad(query) {
this.current = this.items_e.indexOf(query.category)
},
onShow() {
this.getTicket()
},
onPullDownRefresh() {
this.page = 1;
this.getTicket()
},
onReachBottom() {
this.onReachBottom();
},
methods: {
onReachBottom() {
if (this.status === 'more') {
this.page++;
this.getTicket()
}
},
onClickItem(e) {
if (this.current !== e.currentIndex) {
this.current = e.currentIndex
@ -72,13 +85,17 @@
var that = this;
that.status = 'loading';
that.$api.getTicket({category: that.items_e[that.current], page: that.page, page_size: that.pageSize}).then(data=>{
if(data.results.length <= that.pageSize){
if(data.results.length < that.pageSize){
that.status = 'noMore';
}else{
that.status = 'more';
}
if(that.page == 1){
that.tlist = data.results;
uni.pageScrollTo({
scrollTop: 0, //
duration: 300 //
});
}
else{
that.tlist = that.tlist.concat(data.results)
@ -92,16 +109,17 @@
change(e) {
console.log('当前模式:' + e.type + ',状态:' + e.show);
},
itemClick(row) {
const viewPath = row.workflow_.view_path2?row.workflow_.view_path2:row.workflow_.view_path;
const t_id = row.ticket_data.t_id;
uni.navigateTo({
url: `/pages${viewPath}?t_id=${t_id}`
})
},
}
}
</script>
<style>
.header {
position: sticky;
top: 0;
z-index: 1;
background-color: white;
padding: 8rpx;
}
</style>

129
pages/wf/ticketd.vue Normal file
View File

@ -0,0 +1,129 @@
<template>
<view v-if="ticket_">
<uni-forms-item label="审批状态">
<uni-tag :text="actStateEnum[ticket_?.act_state]?.text" :circle="true" size="small"
:type="actStateEnum[ticket_?.act_state]?.type" :inverted="true" style="font-weight: 460;"></uni-tag>
<span style="margin-left: 12rpx" @click="handleDetail">{{ticket_?.state_.name}}</span>
<button v-if="ticket_?.state_.enable_retreat && isOwn" type="warn" size="mini" plain="true"
@click="handleRetreat">撤回</button>
<span style="margin-left: 12rpx;color:blue" @click="handleDetail">更多信息</span>
</uni-forms-item>
<uni-forms-item label="可处理人" v-if="ticket_.participant_">
<span v-if="
ticket_.participant_type == 2 ||
ticket_.participant_type == 1
">
<span v-for="item in ticket_.participant_" :key="item.id">{{ item.name }}/</span>
</span>
<span v-else> </span>
</uni-forms-item>
<uni-popup ref="popup" background-color="#fff">
<scroll-view scroll-y="true" style="height: 600rpx;padding: 20rpx">
<uni-forms-item label="审批状态">
<uni-tag :text="actStateEnum[ticket_f?.act_state]?.text" :circle="true" size="small"
:type="actStateEnum[ticket_f?.act_state]?.type" :inverted="true" style="font-weight: 460;"></uni-tag>
<span style="margin-left: 12rpx">{{ticket_f?.state_.name}}</span>
<button v-if="ticket_f?.state_.enable_retreat && isOwn" type="warn" size="mini" plain="true"
@click="handleRetreat">撤回</button>
</uni-forms-item>
<uni-forms-item label="可处理人" v-if="ticket_f.participant_">
<span v-if="
ticket_f.participant_type == 2 ||
ticket_f.participant_type == 1
">
<span v-for="item in ticket_f.participant_" :key="item.id">{{ item.name }}/</span>
</span>
<span v-else> </span>
</uni-forms-item>
<div style="font-size: 28rpx;">
<div v-for="item in flowsteps" :key="item.id" style="margin-bottom: 16rpx;border-top: 1px solid #000;">
<div>
<strong>{{ item.state_?.name || "未知状态" }}</strong>
</div>
<div>
<span style="font-size: 22rpx">{{item.create_time}}-</span>
<span v-if="item.intervene_type != 0"><uni-tag
:type="interveneTypeEnum[item.intervene_type]?.type" :inverted="true" :circle="true"
:text="interveneTypeEnum[item.intervene_type]?.text" style="font-weight: 460;">
</uni-tag></span>
<span v-if="item.transition_"
:style="{ color: item.transition_.attribute_type === 1 ? '#67C23A' : '#F56C6C' }">
{{ item.transition_.name }}
</span>
<span v-if="item.participant_">-{{ item.participant_.name }}</span>
</div>
<div v-if="item.suggestion">
<span v-if="item.suggestion.length > 20">{{ item.suggestion.slice(0, 20) + "..." }}</span>
<span v-else>
{{ item.suggestion }}
</span>
</div>
</div>
</div>
</scroll-view>
</uni-popup>
</view>
</template>
<script setup>
import {
ref,
reactive,
onMounted,
defineEmits,
watch
} from 'vue'
import API from '@/utils/api';
import {
actStateEnum, interveneTypeEnum
} from "@/utils/enum.js"
const props = defineProps({
ticket_: {
type: Object,
default: null,
required: false
}
})
const ticket_f = ref({});
const isOwn = ref(false);
watch(
() => props.ticket_,
async (newVal) => {
if (newVal && Object.keys(newVal).length > 0) {
if(props.ticket_.create_by === uni.getStorageSync("userInfo").id){
isOwn.value = true;
}
}
},
{ immediate: false }
)
onMounted(() => {
})
const popup = ref(null);
const flowsteps = ref([]);
const handleDetail = async () => {
ticket_f.value = await API.getTicketItem(props.ticket_.id)
popup.value.open("top");
API.getTicketFlowLogs(props.ticket_.id).then(res=>{
flowsteps.value = res
})
}
const handleRetreat = () => {
uni.showModal({
title: "撤回原因",
editable: true,
success(res) {
if(res.confirm){
API.ticketRetreat(props.ticket_.id, {"suggestion": res.content}).then(res=>{
uni.navigateBack()
})
}
}
})
}
</script>

158
pages/wf/ticketd_b.vue Normal file
View File

@ -0,0 +1,158 @@
<template>
<span v-if="actionShow">
<view style="width: 100%; display: flex;">
<view style="width: 100rpx;">意见</view>
<view style="flex:1">
<uni-easyinput v-model="suggestion"></uni-easyinput>
</view>
</view>
<view style="height: 6rpx;margin-top:6rpx;border-top: 1px solid #000;"></view>
<view style="display:flex">
<view style="margin:auto">
<button
v-for="item in transitions"
:key="item.id"
:type="item.attribute_type === 2 ? 'warn' : 'primary'"
:loading="isSaveing"
:disabled="isSaveing"
@click="submit(item.id)"
style="margin-right: 2px"
size="mini"
>{{ item.name }}</button>
</view>
<!-- <view style="margin-left:auto">
<button v-if="props.ticket_?.state_.enable_retreat && isOwn" type="warn" size="mini"
@click="handleRetreat">撤回</button>
</view> -->
</view>
</span>
</template>
<script setup>
import { ref, reactive, onMounted, defineEmits, watch } from 'vue'
import API from '@/utils/api';
const props = defineProps({
workflow_key: {type: String, default: null, required: false},
ticket_: {type: Object, default: null, required: false},
t_id: {type: String, default: null, required: true},
title: {type: String, default: null, required: false},
submit_b_func: {type: Function, default: null, required: false},
ticket_data: {type: Object, default: null, required: false},
})
const workflow = ref(null);
const transitions = ref([]);
onMounted(async () => {
await getUser()
await init()
})
watch(
() => props.ticket_,
async (newVal) => {
if (newVal && Object.keys(newVal).length > 0) {
await init()
}
},
{ immediate: false }
)
const actionShow = ref(false);
const suggestion = ref(null)
const isOwn = ref(false)
const init = async () => {
transitions.value = [];
actionShow.value = false;
if (props.ticket_ != null && props.ticket_ != undefined) {
isOwn.value = props.ticket_.create_by === currentUser.value;
const isParticipant =
(props.ticket_.participant_type === 1 && props.ticket_.participant === currentUser.value) ||
(props.ticket_.participant_type === 2 && props.ticket_.participant.includes(currentUser.value))
if (isParticipant) {
actionShow.value = true;
transitions.value = await API.getTransition(props.ticket_.id);
}
}else if (props.workflow_key !=null && props.workflow_key != undefined) {
let res = await API.workflowInitkey(props.workflow_key);
actionShow.value = true
transitions.value = res.transitions;
workflow.value = res.workflow
}else{
uni.showToast({
icon:"error",
title:"缺少workflow_key或ticketId"
})
}
}
const currentUser = ref(null);
const getUser = async() =>{
currentUser.value = uni.getStorageSync("userInfo").id
}
const isSaveing = ref(false);
const emit = defineEmits(["success"]);
const submit = async (transition_id) => {
isSaveing.value = true;
if (props.submit_b_func) {
try{
await props.submit_b_func();
}catch (e) {
isSaveing.value = false;
return;
}
}
if (props.ticket_?.id) {
let params = new Object();
params.transition = transition_id;
if (props.ticket_data) {
params.ticket_data = props.ticket_data;
}else{
params.ticket_data = {};
}
try{
let res = await API.ticketHandle(props.ticket_.id, params);
isSaveing.value = false;
uni.showToast({
title: '提交成功',
icon: 'success'
});
emit("success", props.ticket_.id)
} catch (e) {
isSaveing.value = false;
return;
}
} else {
let ticket = {};
ticket.title = props.title;
ticket.workflow = workflow.value;
if (props.t_id != null && props.t_id != undefined) {
ticket.ticket_data = {
t_id: props.t_id,
};
} else {
uni.showToast({
title: '缺少t_id',
icon: 'error'
});
isSaveing.value = false;
return;
}
ticket.transition = transition_id;
try {
let res = await API.ticketCreate(ticket);
isSaveing.value = false;
uni.showToast({
title: '提交成功',
icon: 'success'
});
emit("success", res.id)
} catch (e) {
isSaveing.value = false;
return;
}
}
};
const handleRetreat = async () =>{
await API.ticketRetreat(props.ticket_.id, {"suggestion": suggestion.value});
}
</script>

View File

@ -14,11 +14,15 @@ export default {
apkCheck: ()=>http('/system/apk/'), //apk检查
getTicket:(data) => http('/wf/ticket/', 'GET', data),
getTicketItem:(id) => http(`/wf/ticket/${id}/`, 'GET'),
getTicketFlowLogs:(id) => http(`/wf/ticket/${id}/flowlogs/`, 'GET'),
ticketCreate:(data)=>http('/wf/ticket/', 'POST', data),
ticketHandle:(id,data)=>http(`/wf/ticket/${id}/handle/`, 'POST', data),
getMroom:(data) => http('/ofm/mroom/', 'GET', data),
ticketRetreat:(id,data)=>http(`/wf/ticket/${id}/retreat/`, 'POST', data),
getTransition:(id) => http(`/wf/ticket/${id}/transitions/`, 'GET'),
workflowInitkey:(key) => http(`/wf/workflow/${key}/init_key/`, 'GET'),
getMroom:(data) => http('/ofm/mroom/', 'GET', data),
bookingList:(data) => http('/ofm/mroombooking/', 'GET', data),
bookingItem:(id,data) => http(`/ofm/mroombooking/${id}/`, 'GET', data),
bookingCreate:(data) => http(`/ofm/mroombooking/`, 'POST', data),

View File

@ -5,4 +5,18 @@ export const actStateEnum = Object.freeze({
3: { text: '被撤回', type: 'warning' },
4: { text: '已完成', type: 'success' },
5: { text: '已关闭', type: 'default' },
})
export const interveneTypeEnum = Object.freeze({
0: { text: '正常', type: 'success' },
1: { text: '转交', type: 'primary' },
2: { text: '加签', type: 'default' },
3: { text: '加签处理完成', type: 'primary' },
4: { text: '接单', type: 'success' },
5: { text: '评论', type: 'default' },
6: { text: '删除', type: 'error' },
7: { text: '强制关闭', type: 'error' },
8: { text: '强制修改状态', type: 'error' },
10: { text: '撤回', type: 'warning' },
11: { text: '抄送', type: 'default' },
})