Browse Source

修改流程申请审核管理的bug

master
王泽彦 10 months ago
parent
commit
4588ea879b
  1. 10
      admin/src/app/api/school_approval/process.ts
  2. 265
      admin/src/app/views/school_approval/config/index.vue
  3. 178
      admin/src/app/views/school_approval/process/index.vue
  4. 37
      admin/src/utils/request.ts
  5. 10
      niucloud/app/adminapi/controller/school_approval/Process.php
  6. 2
      niucloud/app/service/school_approval/SchoolApprovalProcessService.php

10
admin/src/app/api/school_approval/process.ts

@ -5,7 +5,7 @@ import request from '@/utils/request'
* @param params
*/
export function getProcessList(params?: Record<string, any>) {
return request.get({ url: '/school_approval/process/lists', params })
return request.get('/school_approval/process/lists', { params })
}
/**
@ -13,7 +13,7 @@ export function getProcessList(params?: Record<string, any>) {
* @param params
*/
export function getProcessInfo(params: { id: number }) {
return request.get({ url: '/school_approval/process/info', params })
return request.get('/school_approval/process/info', { params })
}
/**
@ -21,7 +21,7 @@ export function getProcessInfo(params: { id: number }) {
* @param params
*/
export function createProcess(params: any) {
return request.post({ url: '/school_approval/process/create', data: params })
return request.post('/school_approval/process/create', params)
}
/**
@ -29,7 +29,7 @@ export function createProcess(params: any) {
* @param params
*/
export function approveProcess(params: { process_id: number; status: string; remarks?: string }) {
return request.post({ url: '/school_approval/process/approve', data: params })
return request.post('/school_approval/process/approve', params)
}
/**
@ -37,5 +37,5 @@ export function approveProcess(params: { process_id: number; status: string; rem
* @param params
*/
export function cancelProcess(params: { process_id: number }) {
return request.post({ url: '/school_approval/process/cancel', data: params })
return request.post('/school_approval/process/cancel', params)
}

265
admin/src/app/views/school_approval/config/index.vue

@ -4,60 +4,60 @@
<div class="flex justify-between items-center">
<div>
<el-button type="primary" @click="handleAdd">
<icon name="add" class="mr-5px" />
<icon name="add" class="mr-5px"/>
添加
</el-button>
</div>
<div class="flex items-center">
<el-input
v-model="state.searchParams.config_name"
class="w-200px mr-15px"
placeholder="请输入配置名称"
clearable
@keyup.enter="handleSearch"
@clear="handleSearch"
v-model="state.searchParams.config_name"
class="w-200px mr-15px"
placeholder="请输入配置名称"
clearable
@keyup.enter="handleSearch"
@clear="handleSearch"
/>
<el-select
v-model="state.searchParams.status"
class="w-150px mr-15px"
placeholder="请选择状态"
clearable
@change="handleSearch"
v-model="state.searchParams.status"
class="w-150px mr-15px"
placeholder="请选择状态"
clearable
@change="handleSearch"
>
<el-option label="启用" :value="1" />
<el-option label="禁用" :value="0" />
<el-option label="启用" :value="1"/>
<el-option label="禁用" :value="0"/>
</el-select>
<el-button type="primary" @click="handleSearch">
<icon name="search" class="mr-5px" />
<icon name="search" class="mr-5px"/>
搜索
</el-button>
<el-button @click="handleReset">
<icon name="refresh-right" class="mr-5px" />
<icon name="refresh-right" class="mr-5px"/>
重置
</el-button>
</div>
</div>
<el-table
v-loading="state.loading"
class="mt-15px"
:data="state.configList"
:header-cell-style="{ background: '#fafafa', color: '#606266' }"
v-loading="state.loading"
class="mt-15px"
:data="state.configList"
:header-cell-style="{ background: '#fafafa', color: '#606266' }"
>
<el-table-column label="ID" prop="id" width="80" />
<el-table-column label="配置名称" prop="config_name" min-width="180" />
<el-table-column label="配置描述" prop="description" min-width="200" show-overflow-tooltip />
<el-table-column label="ID" prop="id" width="80"/>
<el-table-column label="配置名称" prop="config_name" min-width="180"/>
<el-table-column label="配置描述" prop="description" min-width="200" show-overflow-tooltip/>
<el-table-column label="状态" prop="status" width="100">
<template #default="{ row }">
<el-switch
v-model="row.status"
:active-value="1"
:inactive-value="0"
@change="handleStatusChange(row)"
v-model="row.status"
:active-value="1"
:inactive-value="0"
@change="handleStatusChange(row)"
/>
</template>
</el-table-column>
<el-table-column label="创建时间" prop="created_at" width="180" />
<el-table-column label="创建时间" prop="created_at" width="180"/>
<el-table-column label="操作" width="180" fixed="right">
<template #default="{ row }">
<el-button type="primary" link @click="handleEdit(row)">
@ -75,68 +75,69 @@
<div class="flex justify-end mt-15px">
<el-pagination
v-model:current-page="state.searchParams.page"
v-model:page-size="state.searchParams.limit"
:page-sizes="[10, 20, 50, 100]"
:total="state.total"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
v-model:current-page="state.searchParams.page"
v-model:page-size="state.searchParams.limit"
:page-sizes="[10, 20, 50, 100]"
:total="state.total"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</el-card>
<!-- 新增/编辑弹窗 -->
<el-dialog
v-model="state.dialog.visible"
:title="state.dialog.title"
width="800px"
:close-on-click-modal="false"
:destroy-on-close="true"
v-model="state.dialog.visible"
:title="state.dialog.title"
width="800px"
:close-on-click-modal="false"
:destroy-on-close="true"
>
<el-form
ref="formRef"
:model="state.dialog.form"
:rules="state.dialog.rules"
label-width="120px"
ref="formRef"
:model="state.dialog.form"
:rules="state.dialog.rules"
label-width="120px"
>
<el-form-item label="配置名称" prop="config_name">
<el-input v-model="state.dialog.form.config_name" placeholder="请输入配置名称" />
<el-input v-model="state.dialog.form.config_name" placeholder="请输入配置名称"/>
</el-form-item>
<el-form-item label="配置描述" prop="description">
<el-input
v-model="state.dialog.form.description"
type="textarea"
:rows="3"
placeholder="请输入配置描述"
v-model="state.dialog.form.description"
type="textarea"
:rows="3"
placeholder="请输入配置描述"
/>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-switch
v-model="state.dialog.form.status"
:active-value="1"
:inactive-value="0"
v-model="state.dialog.form.status"
:active-value="1"
:inactive-value="0"
/>
</el-form-item>
<el-form-item label="审批节点" prop="nodes">
<div class="mb-10px">
<el-button type="primary" @click="handleAddNode">
<icon name="add" class="mr-5px" />
<icon name="add" class="mr-5px"/>
添加节点
</el-button>
</div>
<div v-if="state.dialog.form.nodes.length === 0" class="text-gray-400 text-center py-20px border border-dashed rounded">
<div v-if="state.dialog.form.nodes.length === 0"
class="text-gray-400 text-center py-20px border border-dashed rounded">
暂无审批节点请点击添加
</div>
<div v-else class="node-list">
<div
v-for="(element, index) in state.dialog.form.nodes"
:key="index"
class="node-item p-15px mb-15px border border-gray-200 rounded"
v-for="(element, index) in state.dialog.form.nodes"
:key="index"
class="node-item p-15px mb-15px border border-gray-200 rounded"
>
<div class="flex justify-between items-center mb-10px">
<div class="flex items-center">
<icon name="rank" class="drag-handle mr-5px cursor-move" />
<icon name="rank" class="drag-handle mr-5px cursor-move"/>
<span class="font-bold">节点 {{ index + 1 }}</span>
</div>
<el-button type="danger" link @click="handleRemoveNode(index)">
@ -146,53 +147,53 @@
<div class="flex items-center mb-10px">
<span class="w-100px">节点名称</span>
<el-input
v-model="element.node_name"
placeholder="请输入节点名称"
v-model="element.node_name"
placeholder="请输入节点名称"
/>
</div>
<div class="flex items-center mb-10px">
<span class="w-100px">审批人类型</span>
<el-select
v-model="element.approver_type"
class="flex-1"
placeholder="请选择审批人类型"
v-model="element.approver_type"
class="flex-1"
placeholder="请选择审批人类型"
>
<el-option label="指定用户" value="user" />
<el-option label="指定角色" value="role" />
<el-option label="指定部门" value="department" />
<el-option label="指定用户" value="user"/>
<el-option label="指定角色" value="role"/>
<el-option label="指定部门" value="department"/>
</el-select>
</div>
<div class="flex items-center mb-10px">
<span class="w-100px">审批人</span>
<el-select
v-model="element.approver_ids"
class="flex-1"
multiple
placeholder="请选择审批人"
v-model="element.approver_ids"
class="flex-1"
multiple
placeholder="请选择审批人"
>
<!-- 这里根据 approver_type 不同显示不同的选项 -->
<template v-if="element.approver_type === 'user'">
<el-option
v-for="item in state.userOptions"
:key="item.value"
:label="item.label"
:value="item.value"
v-for="item in state.userOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</template>
<template v-else-if="element.approver_type === 'role'">
<el-option
v-for="item in state.roleOptions"
:key="item.value"
:label="item.label"
:value="item.value"
v-for="item in state.roleOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</template>
<template v-else-if="element.approver_type === 'department'">
<el-option
v-for="item in state.departmentOptions"
:key="item.value"
:label="item.label"
:value="item.value"
v-for="item in state.departmentOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</template>
</el-select>
@ -218,10 +219,10 @@
<!-- 详情弹窗 -->
<el-dialog
v-model="state.detailDialog.visible"
title="审批流配置详情"
width="800px"
:destroy-on-close="true"
v-model="state.detailDialog.visible"
title="审批流配置详情"
width="800px"
:destroy-on-close="true"
>
<el-descriptions :column="1" border>
<el-descriptions-item label="ID">
@ -247,10 +248,10 @@
<div class="font-bold text-16px mb-10px">审批节点列表</div>
<el-timeline>
<el-timeline-item
v-for="(node, index) in state.detailDialog.info?.nodes || []"
:key="index"
:type="getNodeType(node.sign_type)"
:color="getNodeColor(node.sign_type)"
v-for="(node, index) in state.detailDialog.info?.nodes || []"
:key="index"
:type="getNodeType(node.sign_type)"
:color="getNodeColor(node.sign_type)"
>
<div class="font-bold mb-5px">{{ node.node_name }}</div>
<div class="text-gray-500">
@ -270,9 +271,16 @@
</template>
<script lang="ts" setup>
import { reactive, ref, onMounted } from 'vue'
import { ElMessage, ElMessageBox, FormInstance } from 'element-plus'
import { getConfigList, getConfigInfo, addConfig, editConfig, deleteConfig, changeConfigStatus } from '@/app/api/school_approval/config'
import {reactive, ref, onMounted} from 'vue'
import {ElMessage, ElMessageBox, FormInstance} from 'element-plus'
import {
getConfigList,
getConfigInfo,
addConfig,
editConfig,
deleteConfig,
changeConfigStatus
} from '@/app/api/school_approval/config'
// draggable使
// import draggable from 'vuedraggable'
@ -320,17 +328,17 @@ const state = reactive({
},
// API
userOptions: [
{ label: '用户1', value: '1' },
{ label: '用户2', value: '2' },
{ label: '用户3', value: '3' }
{label: '用户1', value: '1'},
{label: '用户2', value: '2'},
{label: '用户3', value: '3'}
],
roleOptions: [
{ label: '角色1', value: '1' },
{ label: '角色2', value: '2' }
{label: '角色1', value: '1'},
{label: '角色2', value: '2'}
],
departmentOptions: [
{ label: '部门1', value: '1' },
{ label: '部门2', value: '2' }
{label: '部门1', value: '1'},
{label: '部门2', value: '2'}
],
dialog: {
visible: false,
@ -346,10 +354,10 @@ const state = reactive({
},
rules: {
config_name: [
{ required: true, message: '请输入配置名称', trigger: 'blur' }
{required: true, message: '请输入配置名称', trigger: 'blur'}
],
nodes: [
{ required: true, validator: validateNodes, trigger: 'change' }
{required: true, validator: validateNodes, trigger: 'change'}
]
}
},
@ -446,15 +454,15 @@ async function handleEdit(row: any) {
state.dialog.title = '编辑审批流配置'
state.dialog.loading = true
try {
const res = await getConfigInfo({ id: row.id })
state.dialog.form = { ...res.data }
const res = await getConfigInfo({id: row.id})
state.dialog.form = {...res.data}
// key
if (state.dialog.form.nodes && Array.isArray(state.dialog.form.nodes)) {
state.dialog.form.nodes = state.dialog.form.nodes.map((node: any, index: number) => {
// approver_idssplit
const approverIds = typeof node.approver_ids === 'string'
? node.approver_ids.split(',')
: (Array.isArray(node.approver_ids) ? node.approver_ids : []);
? node.approver_ids.split(',')
: (Array.isArray(node.approver_ids) ? node.approver_ids : []);
return {
...node,
@ -477,7 +485,7 @@ async function handleEdit(row: any) {
//
async function handleDetail(row: any) {
try {
const res = await getConfigInfo({ id: row.id })
const res = await getConfigInfo({id: row.id})
state.detailDialog.info = res.data
state.detailDialog.visible = true
} catch (error) {
@ -492,26 +500,27 @@ function handleDelete(row: any) {
cancelButtonText: '取消',
type: 'warning'
})
.then(async () => {
try {
await deleteConfig({ id: row.id })
ElMessage.success('删除成功')
getList()
} catch (error) {
console.error(error)
}
})
.catch(() => {})
.then(async () => {
try {
await deleteConfig({id: row.id})
ElMessage.success('删除成功')
getList()
} catch (error) {
console.error(error)
}
})
.catch(() => {
})
}
//
async function handleStatusChange(row: any) {
try {
await changeConfigStatus({ id: row.id, status: row.status })
await changeConfigStatus({id: row.id, status: row.status})
ElMessage.success(
row.status
? '启用成功'
: '禁用成功'
row.status
? '启用成功'
: '禁用成功'
)
} catch (error) {
console.error(error)
@ -545,17 +554,17 @@ async function handleSubmit() {
state.dialog.loading = true
try {
//
const formData = { ...state.dialog.form }
const formData = {...state.dialog.form}
//
formData.nodes = formData.nodes.map((node: any, index: number) => {
// keyAPI
const { key, ...nodeWithoutKey } = node;
const {key, ...nodeWithoutKey} = node;
// approver_idsjoin
const approverIds = Array.isArray(node.approver_ids)
? node.approver_ids.join(',')
: (typeof node.approver_ids === 'string' ? node.approver_ids : '');
? node.approver_ids.join(',')
: (typeof node.approver_ids === 'string' ? node.approver_ids : '');
return {
...nodeWithoutKey,
@ -613,9 +622,15 @@ onMounted(() => {
</script>
<style lang="scss" scoped>
.node-list {
margin-left: 10px;
}
.node-item {
background-color: #f9f9f9;
transition: all 0.3s;
padding: 10px;
margin-top: 10px;
&:hover {
background-color: #f2f2f2;

178
admin/src/app/views/school_approval/process/index.vue

@ -5,14 +5,14 @@
<div>
<el-button type="primary" @click="handleCreate">
<icon name="add" class="mr-5px" />
{{ $t('approval.process.create') }}
{{ '创建' }}
</el-button>
</div>
<div class="flex items-center">
<el-input
v-model="state.searchParams.process_name"
class="w-200px mr-15px"
:placeholder="$t('approval.process.searchPlaceholder')"
class="w-200 mr-15"
:placeholder="'搜索'"
clearable
@keyup.enter="handleSearch"
@clear="handleSearch"
@ -20,29 +20,29 @@
<el-select
v-model="state.searchParams.approval_status"
class="w-150px mr-15px"
:placeholder="$t('approval.process.statusPlaceholder')"
:placeholder="'状态'"
clearable
@change="handleSearch"
>
<el-option :label="$t('approval.process.pending')" value="pending" />
<el-option :label="$t('approval.process.approved')" value="approved" />
<el-option :label="$t('approval.process.rejected')" value="rejected" />
<el-option :label="'待审批'" value="pending" />
<el-option :label="'已审批'" value="approved" />
<el-option :label="'已拒绝'" value="rejected" />
</el-select>
<el-button type="primary" @click="handleSearch">
<icon name="search" class="mr-5px" />
{{ $t('common.search') }}
{{ '搜索' }}
</el-button>
<el-button @click="handleReset">
<icon name="refresh-right" class="mr-5px" />
{{ $t('common.reset') }}
{{ '重置' }}
</el-button>
</div>
</div>
<el-tabs v-model="state.activeTab" class="mt-15px" @tab-click="handleTabChange">
<el-tab-pane :label="$t('approval.process.all')" name="all" />
<el-tab-pane :label="$t('approval.process.myCreate')" name="myCreate" />
<el-tab-pane :label="$t('approval.process.myApproval')" name="myApproval" />
<el-tab-pane :label="'全部'" name="all" />
<el-tab-pane :label="'我创建的'" name="myCreate" />
<el-tab-pane :label="'我审批的'" name="myApproval" />
</el-tabs>
<el-table
@ -50,22 +50,22 @@
:data="state.processList"
:header-cell-style="{ background: '#fafafa', color: '#606266' }"
>
<el-table-column :label="$t('approval.process.id')" prop="id" width="80" />
<el-table-column :label="$t('approval.process.processName')" prop="process_name" min-width="180" />
<el-table-column :label="$t('approval.process.applicantId')" prop="applicant_id" width="100" />
<el-table-column :label="$t('approval.process.applicationTime')" prop="application_time" width="180" />
<el-table-column :label="$t('approval.process.currentApproverId')" prop="current_approver_id" width="100" />
<el-table-column :label="$t('approval.process.approvalStatus')" prop="approval_status" width="100">
<el-table-column :label="'ID'" prop="id" width="80" />
<el-table-column :label="'流程名称'" prop="process_name" min-width="180" />
<el-table-column :label="'申请人ID'" prop="applicant_id" width="100" />
<el-table-column :label="'申请时间'" prop="application_time" width="180" />
<el-table-column :label="'当前审批人ID'" prop="current_approver_id" width="100" />
<el-table-column :label="'审批状态'" prop="approval_status" width="100">
<template #default="{ row }">
<el-tag :type="getStatusType(row.approval_status)">
{{ getStatusText(row.approval_status) }}
</el-tag>
</template>
</el-table-column>
<el-table-column :label="$t('common.action')" width="180" fixed="right">
<el-table-column :label="'操作'" width="180" fixed="right">
<template #default="{ row }">
<el-button type="primary" link @click="handleDetail(row)">
{{ $t('common.detail') }}
{{ '详情' }}
</el-button>
<el-button
v-if="row.approval_status === 'pending' && row.applicant_id === state.userInfo.uid"
@ -73,7 +73,7 @@
link
@click="handleCancel(row)"
>
{{ $t('approval.process.cancel') }}
{{ '取消' }}
</el-button>
<el-button
v-if="row.approval_status === 'pending' && row.current_approver_id === state.userInfo.uid"
@ -81,7 +81,7 @@
link
@click="handleApprove(row)"
>
{{ $t('approval.process.approve') }}
{{ '审批' }}
</el-button>
</template>
</el-table-column>
@ -103,7 +103,7 @@
<!-- 创建审批弹窗 -->
<el-dialog
v-model="state.createDialog.visible"
:title="$t('approval.process.create')"
:title="'创建审批'"
width="600px"
:close-on-click-modal="false"
:destroy-on-close="true"
@ -114,17 +114,17 @@
:rules="state.createDialog.rules"
label-width="120px"
>
<el-form-item :label="$t('approval.process.processName')" prop="process_name">
<el-form-item :label="'流程名称'" prop="process_name">
<el-input
v-model="state.createDialog.form.process_name"
:placeholder="$t('approval.process.processNamePlaceholder')"
:placeholder="'请输入流程名称'"
/>
</el-form-item>
<el-form-item :label="$t('approval.process.configId')" prop="config_id">
<el-form-item :label="'审批流配置'" prop="config_id">
<el-select
v-model="state.createDialog.form.config_id"
class="w-full"
:placeholder="$t('approval.process.configIdPlaceholder')"
:placeholder="'请选择审批流配置'"
>
<el-option
v-for="item in state.configOptions"
@ -134,19 +134,19 @@
/>
</el-select>
</el-form-item>
<el-form-item :label="$t('approval.process.remarks')" prop="remarks">
<el-form-item :label="'备注'" prop="remarks">
<el-input
v-model="state.createDialog.form.remarks"
type="textarea"
:rows="3"
:placeholder="$t('approval.process.remarksPlaceholder')"
:placeholder="'请输入备注'"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="state.createDialog.visible = false">{{ $t('common.cancel') }}</el-button>
<el-button @click="state.createDialog.visible = false">{{ '取消' }}</el-button>
<el-button type="primary" :loading="state.createDialog.loading" @click="handleCreateSubmit">
{{ $t('common.confirm') }}
{{ '确认' }}
</el-button>
</template>
</el-dialog>
@ -154,7 +154,7 @@
<!-- 审批弹窗 -->
<el-dialog
v-model="state.approveDialog.visible"
:title="$t('approval.process.approve')"
:title="'审批'"
width="500px"
:close-on-click-modal="false"
:destroy-on-close="true"
@ -165,25 +165,25 @@
:rules="state.approveDialog.rules"
label-width="80px"
>
<el-form-item :label="$t('approval.process.status')" prop="status">
<el-form-item :label="'审批状态'" prop="status">
<el-radio-group v-model="state.approveDialog.form.status">
<el-radio label="approved">{{ $t('approval.process.approved') }}</el-radio>
<el-radio label="rejected">{{ $t('approval.process.rejected') }}</el-radio>
<el-radio label="approved">{{ '已审批' }}</el-radio>
<el-radio label="rejected">{{ '已拒绝' }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="$t('approval.process.remarks')" prop="remarks">
<el-form-item :label="'备注'" prop="remarks">
<el-input
v-model="state.approveDialog.form.remarks"
type="textarea"
:rows="3"
:placeholder="$t('approval.process.remarksPlaceholder')"
:placeholder="'请输入备注'"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="state.approveDialog.visible = false">{{ $t('common.cancel') }}</el-button>
<el-button @click="state.approveDialog.visible = false">{{ '取消' }}</el-button>
<el-button type="primary" :loading="state.approveDialog.loading" @click="handleApproveSubmit">
{{ $t('common.confirm') }}
{{ '确认' }}
</el-button>
</template>
</el-dialog>
@ -191,41 +191,41 @@
<!-- 详情弹窗 -->
<el-dialog
v-model="state.detailDialog.visible"
:title="$t('approval.process.detail')"
:title="'审批详情'"
width="800px"
:destroy-on-close="true"
>
<el-descriptions :column="1" border>
<el-descriptions-item :label="$t('approval.process.id')">
<el-descriptions-item :label="'ID'">
{{ state.detailDialog.info.id }}
</el-descriptions-item>
<el-descriptions-item :label="$t('approval.process.processName')">
<el-descriptions-item :label="'流程名称'">
{{ state.detailDialog.info.process_name }}
</el-descriptions-item>
<el-descriptions-item :label="$t('approval.process.applicantId')">
<el-descriptions-item :label="'申请人ID'">
{{ state.detailDialog.info.applicant_id }}
</el-descriptions-item>
<el-descriptions-item :label="$t('approval.process.applicationTime')">
<el-descriptions-item :label="'申请时间'">
{{ state.detailDialog.info.application_time }}
</el-descriptions-item>
<el-descriptions-item :label="$t('approval.process.currentApproverId')">
<el-descriptions-item :label="'当前审批人ID'">
{{ state.detailDialog.info.current_approver_id }}
</el-descriptions-item>
<el-descriptions-item :label="$t('approval.process.approvalStatus')">
<el-descriptions-item :label="'审批状态'">
<el-tag :type="getStatusType(state.detailDialog.info.approval_status)">
{{ getStatusText(state.detailDialog.info.approval_status) }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item :label="$t('approval.process.approvalTime')">
<el-descriptions-item :label="'审批时间'">
{{ state.detailDialog.info.approval_time || '-' }}
</el-descriptions-item>
<el-descriptions-item :label="$t('approval.process.remarks')">
<el-descriptions-item :label="'备注'">
{{ state.detailDialog.info.remarks || '-' }}
</el-descriptions-item>
</el-descriptions>
<div class="mt-20px">
<div class="font-bold text-16px mb-10px">{{ $t('approval.process.participants') }}</div>
<div class="font-bold text-16px mb-10px">{{ '参与者' }}</div>
<el-timeline>
<el-timeline-item
v-for="(node, index) in state.detailDialog.info.participants"
@ -234,22 +234,22 @@
:color="getNodeColor(node.status)"
>
<div class="font-bold mb-5px">
{{ $t('approval.process.participantLabel', { index: index + 1 }) }}
{{ '参与者' + (index + 1) }}
</div>
<div class="text-gray-500">
{{ $t('approval.process.participantId') }}{{ node.participant_id }}
{{ '参与者ID' }}{{ node.participant_id }}
</div>
<div class="text-gray-500">
{{ $t('approval.process.sequence') }}{{ node.sequence }}
{{ '顺序' }}{{ node.sequence }}
</div>
<div class="text-gray-500">
{{ $t('approval.process.status') }}{{ getStatusText(node.status) }}
{{ '状态' }}{{ getStatusText(node.status) }}
</div>
<div class="text-gray-500">
{{ $t('approval.process.signType') }}{{ node.sign_type === 'or_sign' ? $t('approval.process.orSign') : $t('approval.process.andSign') }}
{{ '签名类型' }}{{ node.sign_type === 'or_sign' ? '或签名' : '和签名' }}
</div>
<div class="text-gray-500">
{{ $t('approval.process.remarks') }}{{ node.remarks || '-' }}
{{ '备注' }}{{ node.remarks || '-' }}
</div>
</el-timeline-item>
</el-timeline>
@ -263,23 +263,39 @@ import { reactive, ref, onMounted } from 'vue'
import { ElMessage, ElMessageBox, FormInstance } from 'element-plus'
import { getProcessList, getProcessInfo, createProcess, approveProcess, cancelProcess } from '@/app/api/school_approval/process'
import { getConfigList } from '@/app/api/school_approval/config'
import { useI18n } from 'vue-i18n'
// import { useI18n } from 'vue-i18n'
// import { useUserInfo } from '@/stores/userInfo'
const { t } = useI18n()
// const { t } = useI18n()
// const userInfo = useUserInfo()
//
const createFormRef = ref<FormInstance>()
const approveFormRef = ref<FormInstance>()
// script
interface ConfigOption {
label: string;
value: number;
}
interface Participant {
participant_id: number;
sequence: number;
status: string;
sign_type: string;
remarks?: string;
}
//
const state = reactive({
loading: false,
processList: [],
total: 0,
activeTab: 'all',
userInfo: null,
userInfo: {
uid: 1 // IDstore
},
searchParams: {
page: 1,
limit: 10,
@ -288,7 +304,7 @@ const state = reactive({
applicant_id: 0,
approver_id: 0
},
configOptions: [], //
configOptions: [] as ConfigOption[], //
createDialog: {
visible: false,
loading: false,
@ -299,10 +315,10 @@ const state = reactive({
},
rules: {
process_name: [
{ required: true, message: t('approval.process.processNameRequired'), trigger: 'blur' }
{ required: true, message: '请输入流程名称', trigger: 'blur' }
],
config_id: [
{ required: true, message: t('approval.process.configIdRequired'), trigger: 'change' }
{ required: true, message: '请选择审批流配置', trigger: 'change' }
]
}
},
@ -316,13 +332,23 @@ const state = reactive({
},
rules: {
status: [
{ required: true, message: t('approval.process.statusRequired'), trigger: 'change' }
{ required: true, message: '请选择审批状态', trigger: 'change' }
]
}
},
detailDialog: {
visible: false,
info: {}
info: {
id: 0,
process_name: '',
applicant_id: 0,
application_time: '',
current_approver_id: 0,
approval_status: '',
approval_time: '',
remarks: '',
participants: [] as Participant[] //
}
}
})
@ -394,11 +420,11 @@ function handleTabChange() {
async function getConfigOptions() {
try {
const res = await getConfigList({ status: 1 })
state.configOptions = res.data.list.map((item: any) => {
state.configOptions = (res.data.list || []).map((item: any) => {
return {
label: item.config_name,
value: item.id
}
label: item.config_name || '',
value: item.id || 0
} as ConfigOption
})
} catch (error) {
console.error(error)
@ -425,7 +451,7 @@ async function handleCreateSubmit() {
state.createDialog.loading = true
try {
await createProcess(state.createDialog.form)
ElMessage.success(t('approval.process.createSuccess'))
ElMessage.success('创建成功')
state.createDialog.visible = false
getList()
} catch (error) {
@ -460,7 +486,7 @@ async function handleApproveSubmit() {
status: state.approveDialog.form.status,
remarks: state.approveDialog.form.remarks
})
ElMessage.success(t('approval.process.approveSuccess'))
ElMessage.success('审批成功')
state.approveDialog.visible = false
getList()
} catch (error) {
@ -473,15 +499,15 @@ async function handleApproveSubmit() {
//
function handleCancel(row: any) {
ElMessageBox.confirm(t('approval.process.confirmCancel'), t('common.warning'), {
confirmButtonText: t('common.confirm'),
cancelButtonText: t('common.cancel'),
ElMessageBox.confirm('确认取消审批?', '警告', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning'
})
.then(async () => {
try {
await cancelProcess({ process_id: row.id })
ElMessage.success(t('approval.process.cancelSuccess'))
ElMessage.success('取消成功')
getList()
} catch (error) {
console.error(error)
@ -514,9 +540,9 @@ function getStatusType(status: string) {
//
function getStatusText(status: string) {
const map: Record<string, string> = {
pending: t('approval.process.pending'),
approved: t('approval.process.approved'),
rejected: t('approval.process.rejected')
pending: '待审批',
approved: '已审批',
rejected: '已拒绝'
}
return map[status] || status
}

37
admin/src/utils/request.ts

@ -8,7 +8,6 @@ import type {
import { getToken, isUrl } from './common'
import { ElMessage } from 'element-plus'
import type { MessageParams } from 'element-plus'
import { t } from '@/lang'
import useUserStore from '@/stores/modules/user'
import storage from '@/utils/storage'
@ -172,13 +171,13 @@ class Request {
const errStatus = err.response.status
switch (errStatus) {
case 400:
errMessage = t('axios.400')
errMessage = '请求错误'
break
case 401:
errMessage = t('axios.401')
errMessage = '未授权,请登录'
break
case 403:
errMessage = t('axios.403')
errMessage = '拒绝访问'
break
case 404:
let baseURL = '';
@ -191,38 +190,38 @@ class Request {
} catch (e) {
baseURL = location.origin;
}
errMessage = baseURL + t('axios.baseUrlError')
errMessage = baseURL + '请求地址出错'
break
case 405:
errMessage = t('axios.405')
errMessage = '请求方法未允许'
break
case 408:
errMessage = t('axios.408')
errMessage = '请求超时'
break
case 409:
errMessage = t('axios.409')
errMessage = '资源冲突'
break
case 500:
errMessage = t('axios.500')
errMessage = '服务器内部错误'
break
case 501:
errMessage = t('axios.501')
errMessage = '服务未实现'
break
case 502:
errMessage = t('axios.502')
errMessage = '网关错误'
break
case 503:
errMessage = t('axios.503')
errMessage = '服务不可用'
break
case 504:
errMessage = t('axios.504')
errMessage = '网关超时'
break
case 505:
errMessage = t('axios.505')
errMessage = 'HTTP版本不受支持'
break
}
}
err.message.includes('timeout') && (errMessage = t('axios.timeout'))
err.message.includes('timeout') && (errMessage = '请求超时')
if (err.code == 'ERR_NETWORK') {
let baseURL = '';
try {
@ -234,7 +233,7 @@ class Request {
} catch (e) {
baseURL = location.origin;
}
errMessage = baseURL + t('axios.baseUrlError')
errMessage = baseURL + '请求地址出错'
}
errMessage &&
this.showElMessage({
@ -256,7 +255,11 @@ class Request {
private messageCache = new Map()
private showElMessage(options: MessageParams) {
const cacheKey = options.message
// 处理类型问题,安全地获取消息
const message = typeof options === 'string' ? options : (options as any).message;
if (!message) return;
const cacheKey = message;
const cachedMessage = this.messageCache.get(cacheKey)
if (!cachedMessage || Date.now() - cachedMessage.timestamp > 5000) {

10
niucloud/app/adminapi/controller/school_approval/Process.php

@ -58,7 +58,7 @@ class Process extends BaseAdminController
$where[] = ['approval_status', '=', 'pending'];
}
$data = $this->service->getList($where, $page, $limit);
$data = $this->service->getList($where, (int)$page, (int)$limit);
return success($data);
}
@ -73,7 +73,7 @@ class Process extends BaseAdminController
return fail('参数错误');
}
$info = $this->service->getInfo($id);
$info = $this->service->getInfo((int)$id);
if (empty($info)) {
return fail('审批流程不存在');
}
@ -99,7 +99,7 @@ class Process extends BaseAdminController
}
// 设置申请人ID
$data['applicant_id'] = $this->user_info['uid'];
$data['applicant_id'] = $this->request->uid();
try {
$process_id = $this->service->create($data, $config_id);
@ -127,7 +127,7 @@ class Process extends BaseAdminController
}
try {
$result = $this->service->approve($process_id, $this->user_info['uid'], $status, $remarks);
$result = $this->service->approve((int)$process_id, $this->request->uid(), $status, $remarks);
return success($result);
} catch (\Exception $e) {
return fail($e->getMessage());
@ -146,7 +146,7 @@ class Process extends BaseAdminController
}
try {
$result = $this->service->cancel($process_id, $this->user_info['uid']);
$result = $this->service->cancel($process_id, $this->request->uid());
return success($result);
} catch (\Exception $e) {
return fail($e->getMessage());

2
niucloud/app/service/school_approval/SchoolApprovalProcessService.php

@ -81,7 +81,7 @@ class SchoolApprovalProcessService
$process = [
'process_name' => $data['process_name'],
'applicant_id' => $data['applicant_id'],
'application_time' => time(),
'application_time' => date("Y-m-d H:i:s"),
'current_approver_id' => 0, // 初始时为0,后面会更新
'approval_status' => SchoolApprovalProcess::STATUS_PENDING,
'remarks' => $data['remarks'] ?? ''

Loading…
Cancel
Save