first commit

This commit is contained in:
ye1023
2026-04-09 15:09:34 +08:00
commit d8e3a63df1
2246 changed files with 352109 additions and 0 deletions
@@ -0,0 +1,413 @@
<template>
<div>
<BasicTable @register="registerTable" :rowSelection="rowSelection">
<template #form-bizTaskType="{ model, field }">
<a-radio-group v-model:value="queryParam.bizTaskType" @change="onBizTaskTypeChange(model, field)">
<a-radio value="1">待我审批</a-radio>
<a-radio value="2">我发起的申请</a-radio>
</a-radio-group>
</template>
<template #notify="{ text, record }">
<SoundTwoTone title="催办提醒" v-if="record.taskUrge" twoToneColor="#eb2f96" @click.stop="taskNotifyMe(flowCode, record.id)" />
{{ text }}
</template>
<template #tableTitle>
<template v-if="queryParam.bizTaskType == '2'">
<a-button type="primary" preIcon="ant-design:plus-outlined" @click="handleCreate"> 新增</a-button>
<a-dropdown v-if="selectedRowKeys.length > 0">
<template #overlay>
<a-menu>
<a-menu-item key="1" @click="batchHandleDelete">
<Icon icon="ant-design:delete-outlined"></Icon>
删除
</a-menu-item>
</a-menu>
</template>
<a-button
>批量操作
<Icon icon="mdi:chevron-down"></Icon>
</a-button>
</a-dropdown>
</template>
<template v-else>
<a-button @click="handleBatch(1)" type="primary" preIcon="ant-design:caret-right-outlined">批量发送</a-button>
<a-button @click="handleBatch(2)" type="primary" preIcon="ant-design:user-outlined">批量委托</a-button>
<a-button @click="handleBatch(3)" type="primary" preIcon="ant-design:rollback-outlined">批量退回</a-button>
<a-button @click="handleBatch(4)" type="primary" preIcon="ant-design:lock-outlined">批量挂起</a-button>
<a-button @click="handleBatch(5)" type="primary" preIcon="ant-design:unlock-outlined">批量解挂</a-button>
</template>
</template>
<!--操作栏-->
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
</template>
</BasicTable>
<!--新增编辑弹窗-->
<BizLeaveModal @register="registerModal" @success="reload"></BizLeaveModal>
<!--业务办理弹窗-->
<BpmBizTaskDealModal ref="taskDealModal" :path="path" :formData="formData" @ok="handleClear"></BpmBizTaskDealModal>
<!--审批跟踪记录弹窗-->
<BpmProcessTrackModal ref="trackModal"></BpmProcessTrackModal>
<!--催办弹窗-->
<BizTaskNotifyModal ref="taskNotifyModal"></BizTaskNotifyModal>
<!--催办自己弹窗-->
<BizTaskNotifyMeModal ref="taskNotifyMeModal"></BizTaskNotifyMeModal>
<!--批量发送-->
<BpmBizBatchCompleteDealModal ref="completeModal" @ok="handleClear"></BpmBizBatchCompleteDealModal>
<!--批量委托-->
<BpmBizBatchEntrusterDealModal ref="entrusterModal" @ok="handleClear"></BpmBizBatchEntrusterDealModal>
<!--批量退回-->
<BpmBizBatchRejectDealModal ref="rejectModal" @ok="handleClear"></BpmBizBatchRejectDealModal>
<!--批量挂起-->
<BpmBizBatchSuspendDealModal ref="suspendModal" @ok="handleClear"></BpmBizBatchSuspendDealModal>
<!--批量解挂-->
<BpmBizBatchRestartDealModal ref="restartModal" @ok="handleClear"></BpmBizBatchRestartDealModal>
<!-- 测试流程设计modal -->
<mini-des-flow-modal @register="registerMiniDesFlowModal"></mini-des-flow-modal>
</div>
</template>
<script lang="ts" name="biz-leave-list" setup>
import { ref, reactive, toRaw, getCurrentInstance, unref } from 'vue';
import { BasicTable, TableAction } from '/src/components/Table';
import BizLeaveModal from './components/BizLeaveModal.vue';
import BpmBizTaskDealModal from './components/BpmBizTaskDealModal.vue';
import BpmProcessTrackModal from './components/BpmProcessTrackModal.vue';
import BizTaskNotifyModal from './components/BizTaskNotifyModal.vue';
import BizTaskNotifyMeModal from './components/BizTaskNotifyMeModal.vue';
import BpmBizBatchCompleteDealModal from './components/BpmBizBatchCompleteDealModal.vue';
import BpmBizBatchEntrusterDealModal from './components/BpmBizBatchEntrusterDealModal.vue';
import BpmBizBatchRejectDealModal from './components/BpmBizBatchRejectDealModal.vue';
import BpmBizBatchSuspendDealModal from './components/BpmBizBatchSuspendDealModal.vue';
import BpmBizBatchRestartDealModal from './components/BpmBizBatchRestartDealModal.vue';
import { SoundTwoTone } from '@ant-design/icons-vue';
import { useModal } from '/src/components/Modal';
import { useMessage } from '/src/hooks/web/useMessage';
import { showDealBtn } from '/src/utils';
import { columns, searchFormSchema } from './leave.data';
import { list, startProcess, deleteOne, invalidProcess, queryFlowData, batchDelete, checkNotify } from './leave.api';
import { getBizProcessNodeInfo } from '/src/views/super/bpm/process/manage/components/bpm.api.ts';
import { useListPage } from '/src/hooks/system/useListPage';
const { createMessage } = useMessage();
//弹窗
const [registerModal, { openModal }] = useModal();
//查询条件
const queryParam = reactive({
bizTaskType: '1',
name: '',
});
//弹窗示例
const instance = getCurrentInstance();
const formData = ref({});
const path = ref('');
const flowCode = 'TEST001';
const formUrl = 'super/bpm/example/batch/components/BizLeaveForm';
const formUrlMobile = 'super/bpm/example/batch/components/BizLeaveForm';
// 列表页面公共参数、方法 NY5LzSY2VW1BSthYSnJArCFqbgwtZqSuyPQ/OD1n1twWJGU2RN/wkzf+kBVO5DztN85Ca9keeuaRAiwcatr8N0M15+Wv2SmRw82lMwawE2naX5tpJMpkxrUhUUcjnC+BSBL4+PV2JUXBFW8/oOG8HLqYvmPxoP7MMBhMi9D7lRY=
const { prefixCls, tableContext } = useListPage({
designScope: 'leave-list',
tableProps: {
title: '业务办理',
api: list,
columns: columns,
canResize: false,
afterFetch: afterFetch,
formConfig: {
schemas: searchFormSchema,
resetFunc: handleReset,
},
actionColumn: {
width: 120,
fixed: 'right',
},
},
});
const [registerTable, { reload, clearSelectedRowKeys }, { rowSelection, selectedRowKeys, selectedRows }] = tableContext;
const [registerMiniDesFlowModal, { openModal: openDesignModalTest }] = useModal();
/**
* 新增
*/
function handleCreate() {
openModal(true, {
isUpdate: false,
});
}
/**
* 编辑
*/
function handleEdit(record: Recordable) {
console.log('点击了编辑', record);
openModal(true, {
record,
isUpdate: true,
});
}
/**
* 删除
* @param id
*/
async function handleDelete(id) {
console.log('点击了删除', id);
await deleteOne({ id }, reload);
}
/**
* 详情
* @param id
*/
async function handleDetail(record) {
console.log('点击了详情', record);
openModal(true, {
record,
isUpdate: true,
isDetail: true,
});
}
/**
* 办理
* @param record
*/
async function handleProcess(record) {
console.log('点击了办理', record);
let res = await getBizProcessNodeInfo({ flowCode: flowCode, dataId: record.id });
if (res.success) {
console.log('获取流程节点信息', res);
let data = {
dataId: res.result.dataId,
taskId: res.result.taskId,
taskDefKey: res.result.taskDefKey,
procInsId: res.result.procInsId,
tableName: res.result.tableName,
permissionList: res.result.permissionList,
bizTaskList: res.result.bizTaskList,
vars: res.result.records,
};
formData.value = data;
console.log('------获取流程节点信息', unref(formData));
path.value = res.result.formUrl;
console.log('获取流程节点信息', unref(path));
instance.refs.taskDealModal.deal(data);
instance.refs.taskDealModal.title = '流程办理';
}
}
/**
* 提交流程
* @param record
*/
async function handleStartProcess(record) {
let params = {
flowCode: flowCode,
id: record.id,
formUrl: formUrl,
formUrlMobile: formUrlMobile,
};
let res = await startProcess(params);
if (res && res.success) {
createMessage.success(res.message);
handleClear();
} else {
createMessage.warning(res.message || '流程启动异常');
}
}
/**
* 清空并重新加载
* @param record
*/
function handleClear() {
reload();
clearSelectedRowKeys();
}
/**
* 列表接口请求后处理
* @param record
*/
async function afterFetch(data) {
if (queryParam.bizTaskType == '1') {
for (let i = 0; i < data.length; i++) {
let item = data[i];
let params = { flowCode: flowCode, dataId: item.id }; //查询条件
let res2 = await checkNotify(params);
if (res2.result) {
item.taskUrge = true;
}
}
}
}
//提醒我的
async function taskNotifyMe(flowCode, dataId) {
let params = { flowCode: flowCode, dataId: dataId }; //查询条件
await queryFlowData(params, (res) => {
if (res.success) {
instance.refs.taskNotifyMeModal.notify(res.result.processInstId);
instance.refs.taskNotifyMeModal.title = '催办提醒';
}
});
}
/**
* 批量删除事件
*/
async function batchHandleDelete() {
await batchDelete({ ids: selectedRowKeys.value }, () => {
selectedRowKeys.value = [];
reload();
});
}
//批量挂起
function handleBatch(type) {
let rows = toRaw(selectedRows.value);
if (rows.length > 0) {
let param = [];
for (let i = 0; i < rows.length; i++) {
let data = { dataId: rows[i].id, flowCode, bizTitle: rows[i].name };
param.push(data);
}
switch (type) {
case 1:
instance.refs.completeModal.deal(param);
instance.refs.completeModal.title = '批量发送';
break;
case 2:
instance.refs.entrusterModal.deal(param);
instance.refs.entrusterModal.title = '批量委派';
break;
case 3:
instance.refs.rejectModal.deal(param);
instance.refs.rejectModal.title = '批量退回';
break;
case 4:
instance.refs.suspendModal.deal(param);
instance.refs.suspendModal.title = '批量挂起';
break;
case 5:
instance.refs.restartModal.deal(param);
instance.refs.restartModal.title = '批量解挂';
break;
}
} else {
createMessage.warning('请选择一条记录!');
}
}
//催办
async function taskNotify(record) {
let params = { flowCode: flowCode, dataId: record.id }; //查询条件
await queryFlowData(params, (res) => {
if (res.success) {
instance.refs.taskNotifyModal.notify(res.result.processInstId);
instance.refs.taskNotifyModal.data.title = '催办提醒';
}
});
}
//审批进度
function handleTrack(record) {
let params = { flowCode: flowCode, dataId: record.id }; //查询条件
instance.refs.trackModal.handleTrack(params);
instance.refs.trackModal.data.title = '审批跟踪记录';
}
//审批进度(简版)
function handleSimpleTrack(record) {
openDesignModalTest(true, {
dataId: record.id,
preview:"true",
flowCode: flowCode
});
}
//作废流程
async function handleInvalidProcess(record) {
await invalidProcess({ flowCode: flowCode, dataId: record.id }, reload);
}
//类型切换
function onBizTaskTypeChange(model, field) {
model[field] = queryParam.bizTaskType;
reload();
}
//自定义重置
function handleReset() {
queryParam.bizTaskType = '1';
}
/**
* 操作栏
*/
function getTableAction(record) {
return [
{
label: '编辑',
onClick: handleEdit.bind(null, record),
ifShow: () => {
return record.bpmStatus === '1';
},
},
{
label: '提交流程',
popConfirm: {
title: '确认提交流程吗?',
confirm: handleStartProcess.bind(null, record),
},
ifShow: () => {
return record.bpmStatus === '1';
},
},
{
label: '办理',
onClick: handleProcess.bind(null, record),
ifShow: () => {
return showDealBtn(record.bpmStatus) && queryParam.bizTaskType == '1';
},
},
];
}
/**
* 下拉操作栏
*/
function getDropDownAction(record) {
return [
{
label: '详情',
onClick: handleDetail.bind(null, record),
ifShow: () => {
return queryParam.bizTaskType == '2';
},
},
{
label: '催办',
onClick: taskNotify.bind(null, record),
ifShow: () => {
return record.bpmStatus !== '1' && record.bpmStatus !== '3' && queryParam.bizTaskType == '2';
},
},
{
label: '审批进度',
onClick: handleTrack.bind(null, record),
ifShow: () => {
return record.bpmStatus !== '1';
},
},
{
label: '审批进度(简版)',
onClick: handleSimpleTrack.bind(null, record),
ifShow: () => {
return record.bpmStatus !== '1';
},
},
{
label: '作废流程',
popConfirm: {
title: '是否确认作废',
confirm: handleInvalidProcess.bind(null, record),
},
ifShow: () => {
return showDealBtn(record.bpmStatus) && queryParam.bizTaskType == '2';
},
},
{
label: '删除',
popConfirm: {
title: '是否确认删除',
confirm: handleDelete.bind(null, record.id),
},
ifShow: () => {
return record.bpmStatus === '1';
},
},
];
}
</script>
@@ -0,0 +1,43 @@
<template>
<div>
<BasicForm ref="form" @register="registerForm" />
</div>
</template>
<script lang="ts" setup>
import { ref, computed, unref } from 'vue';
import { BasicForm, useForm } from '/src/components/Form';
import { formSchema } from '../leave.data';
import { queryById } from '../leave.api';
const props = defineProps({
formData: {
type: Object,
},
});
const form = ref(null);
//表单配置
const [registerForm] = useForm({
// labelWidth: 150,
schemas: formSchema,
showActionButtonGroup: false,
disabled: true,
});
/**
* 初始化表单数据
*/
async function initFormData() {
if (unref(form)) {
form.value.resetFields();
let res = await queryById({ id: props.formData.dataId });
if (res.success) {
form.value.setFieldsValue(res.result);
}
} else {
setTimeout(() => {
initFormData();
}, 10);
}
}
initFormData();
</script>
@@ -0,0 +1,55 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" :bodyStyle="{height: '350px'}" :title="title" @ok="handleSubmit" :width="700">
<BasicForm @register="registerForm" />
</BasicModal>
</template>
<script lang="ts" setup>
import { ref, computed, unref } from 'vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { BasicForm, useForm } from '/@/components/Form';
import { formSchema } from '../leave.data';
import { saveOrUpdate } from '../leave.api';
// 声明Emits
const emit = defineEmits(['success', 'register']);
const isUpdate = ref(true);
//表单配置
const [registerForm, { resetFields, setFieldsValue, validate, setProps }] = useForm({
// labelWidth: 150,
schemas: formSchema,
showActionButtonGroup: false,
});
//表单赋值
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
//重置表单
await resetFields();
setModalProps({ confirmLoading: false, showOkBtn: !!!data?.isDetail });
// update-begin--author:liaozhiyang---date:20240625---for:【TV360X-1359】批量审批详情弹窗应该禁用
setProps({ disabled: !!data?.isDetail });
// update-end--author:liaozhiyang---date:20240625---for:【TV360X-1359】批量审批详情弹窗应该禁用
isUpdate.value = !!data?.isUpdate;
if (unref(isUpdate)) {
//表单赋值
await setFieldsValue({
...data.record,
});
}
});
//设置标题
const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
//表单提交事件
async function handleSubmit() {
try {
const values = await validate();
setModalProps({ confirmLoading: true });
//提交表单
await saveOrUpdate(values, isUpdate.value);
//关闭弹窗
closeModal();
//刷新列表
emit('success');
} finally {
setModalProps({ confirmLoading: false });
}
}
</script>
@@ -0,0 +1,301 @@
<template>
<div>
<a-modal centered :title="title" :width="1200" :open="visible" @ok="handleOk" @cancel="handleCancel" cancelText="关闭">
<a-row :gutter="18">
<a-col :xs="24" :sm="16">
<a-card title="选择人员" :bordered="true">
<!-- 查询区域 -->
<div class="jeecg-basic-table-form-container" @keyup.enter="searchQuery">
<a-form ref="formRef" :model="queryParam" :label-col="labelCol" :wrapper-col="wrapperCol">
<a-row :gutter="24">
<a-col :span="8">
<a-form-item label="用户姓名">
<a-input placeholder="请输入姓名" v-model:value="queryParam.realname"></a-input>
</a-form-item>
</a-col>
<span style="float: left; overflow: hidden" class="table-page-search-submitButtons">
<a-col :span="6">
<a-button type="primary" preIcon="ant-design:search-outlined" @click="searchQuery">查询</a-button>
<a-button type="primary" preIcon="ant-design:reload-outlined" @click="searchReset" style="margin-left: 8px">重置</a-button>
</a-col>
</span>
</a-row>
</a-form>
</div>
<!-- table区域-begin -->
<div>
<a-table
size="small"
bordered
rowKey="id"
:columns="columns1"
:dataSource="dataSource1"
:pagination="ipagination"
:loading="loading"
:rowSelection="{ selectedRowKeys: selectedRowKeys, onSelectAll: onSelectAll, onSelect: onSelect, onChange: onSelectChange }"
@change="handleTableChange"
>
<template #username="{ text, record }">
<JEllipsis :value="text" :length="15" />
</template>
<template #realname="{ text, record }">
<JEllipsis :value="text" :length="10" />
</template>
</a-table>
</div>
<!-- table区域-end -->
</a-card>
</a-col>
<a-col :xs="24" :sm="8">
<a-card title="用户选择" :bordered="true">
<div>
<a-table size="small" bordered rowKey="id" :columns="columns2" :dataSource="dataSource2" :loading="loading" :scroll="{ y: 240 }">
<template #action="{ text, record }">
<a-button type="primary" size="small" @click="handleDelete(record)" preIcon="ant-design:delete-outlined">删除</a-button>
</template>
</a-table>
</div>
</a-card>
</a-col>
</a-row>
</a-modal>
</div>
</template>
<script lang="ts">
import { defineComponent, reactive, toRefs } from 'vue';
import { getBizProcessNodeInfo, suspend } from '/src/views/super/bpm/process/manage/components/bpm.api';
import { useMessage } from '/src/hooks/web/useMessage';
import { list } from '/src/views/system/user/user.api.ts';
import JEllipsis from '/src/components/Form/src/jeecg/components/JEllipsis.vue';
import { filterObj } from '/src/utils/common/compUtils';
export default defineComponent({
props: ['formData'],
components: {
JEllipsis,
},
emits: ['selectFinished'],
setup(_, { emit }) {
const { createMessage } = useMessage();
function searchQuery() {
loadData(1);
}
function searchReset() {
_this.queryParam = {};
loadData(1);
}
function handleCancel() {
_this.visible = false;
}
function handleOk() {
if (_this.dataSource2.length <= 0) {
createMessage.warning('请选用户信息');
return;
}
emit('selectFinished', _this.dataSource2);
_this.visible = false;
}
function add() {
_this.visible = true;
}
function loadData(arg?) {
//加载数据 若传入参数1则加载第一页的内容
if (arg === 1) {
_this.ipagination.current = 1;
}
let params = getQueryParams(); //查询条件
list(params).then((res) => {
if (res.records) {
_this.dataSource1 = res.records;
_this.ipagination.total = res.total;
}
});
}
function getQueryParams() {
let param = Object.assign({}, _this.queryParam, _this.isorter);
param.pageNo = _this.ipagination.current;
param.pageSize = _this.ipagination.pageSize;
return filterObj(param);
}
function onSelectAll(selected, selectedRows, changeRows) {
if (selected === true) {
for (let a = 0; a < changeRows.length; a++) {
_this.dataSource2.push(changeRows[a]);
}
} else {
for (let b = 0; b < changeRows.length; b++) {
_this.dataSource2.splice(_this.dataSource2.indexOf(changeRows[b]), 1);
}
}
// console.log(selected, selectedRows, changeRows);
}
function onSelect(record, selected) {
if (selected === true) {
_this.dataSource2.push(record);
} else {
var index = _this.dataSource2.indexOf(record);
//console.log();
if (index >= 0) {
_this.dataSource2.splice(_this.dataSource2.indexOf(record), 1);
}
}
}
function onSelectChange(selectedRowKeys, selectedRows) {
_this.selectedRowKeys = selectedRowKeys;
_this.selectionRows = selectedRows;
}
function onClearSelected() {
_this.selectedRowKeys = [];
_this.selectionRows = [];
}
function handleDelete(record) {
_this.dataSource2.splice(_this.dataSource2.indexOf(record), 1);
}
function handleTableChange(pagination, filters, sorter) {
//分页、排序、筛选变化时触发
//TODO 筛选
if (Object.keys(sorter).length > 0) {
_this.isorter.column = sorter.field;
_this.isorter.order = 'ascend' == sorter.order ? 'asc' : 'desc';
}
_this.ipagination = pagination;
loadData();
}
const _this = reactive({
title: '用户列表',
names: [],
visible: false,
placement: 'right',
description: '人员管理页面',
// 查询条件
queryParam: {},
dataSource1: [],
dataSource2: [],
// 分页参数
ipagination: {
current: 1,
pageSize: 5,
pageSizeOptions: ['5', '10', '20'],
showTotal: (total, range) => {
return range[0] + '-' + range[1] + ' 共' + total + '条';
},
showQuickJumper: true,
showSizeChanger: true,
total: 0,
},
isorter: {
column: 'createTime',
order: 'desc',
},
loading: false,
selectedRowKeys: [],
selectedRows: [],
// 表头
columns1: [
{
title: '#',
dataIndex: '',
key: 'rowIndex',
width: 30,
align: 'center',
customRender: ({ index }) => {
return parseInt(index) + 1;
},
},
{
title: '用户账号',
align: 'center',
dataIndex: 'username',
width: 120,
slots: { customRender: 'username' },
},
{
title: '用户姓名',
align: 'center',
width: 100,
dataIndex: 'realname',
slots: { customRender: 'realname' },
},
],
columns2: [
{
title: '用户姓名',
align: 'center',
width: '60%',
dataIndex: 'realname',
},
{
title: '操作',
dataIndex: 'action',
align: 'center',
width: '40%',
slots: { customRender: 'action' },
},
],
dealStatus: false,
disabledButton: false,
});
const labelCol = reactive({
xs: { span: 24 },
sm: { span: 7 },
});
const wrapperCol = reactive({
xs: { span: 24 },
sm: { span: 16 },
});
loadData(1);
return {
...toRefs(_this),
handleOk,
handleCancel,
searchQuery,
searchReset,
onSelectAll,
onSelect,
onSelectChange,
handleTableChange,
handleDelete,
labelCol,
wrapperCol,
add,
};
},
});
</script>
<style lang="less" scoped>
.ant-card-body .table-operator {
margin-bottom: 18px;
}
.anty-row-operator button {
margin: 0 5px;
}
.ant-btn-danger {
background-color: #fff;
}
.ant-modal-cust-warp {
height: 100%;
}
.ant-modal-cust-warp .ant-modal-body {
height: calc(100% - 110px) !important;
overflow-y: auto;
}
.ant-modal-cust-warp .ant-modal-content {
height: 90% !important;
overflow-y: hidden;
}
.jeecg-basic-table-form-container {
.table-page-search-submitButtons {
display: block;
margin-bottom: 24px;
white-space: nowrap;
}
}
</style>
@@ -0,0 +1,422 @@
<template>
<!--流程办理的任务处理弹窗-->
<div style="background: #fff; margin-right: 10px">
<!-- 步骤条 -->
<a-spin :spinning="loading">
<div class="ant-alert ant-alert-info" style="margin-bottom: 16px">
当前任务办理环节
<a-select style="width: 300px" v-model:value="currTask.id">
<a-select-option :value="currTask.id">{{ currTask.taskName }}</a-select-option>
</a-select>
</div>
<a-card>
<a-steps progressDot :current="stepIndex" style="padding: 10px" size="default">
<template v-if="resultObj.bpmLogListCount > 3">
<a-step>
<template #title>
<div class="task-title">...</div>
</template>
</a-step>
</template>
<template v-for="(item, index) in resultObj.bpmLogStepList" :key="index">
<a-step>
<template #title>
<div class="task-title">{{ item.taskName }}</div>
</template>
<template #description>
<div class="task-date">
<span><JEllipsis :value="'处理时间:' + item.opTime"></JEllipsis></span>
</div>
<div class="task-user">操作人{{ item.opUserName }}</div>
</template>
</a-step>
</template>
<a-step v-if="resultObj.taskName && resultObj.taskName != ''">
<template #title>
<div class="task-title">{{ resultObj.taskName }}</div>
</template>
<template #description>
<div class="task-date">
<span style="color: #ff6d75"><JEllipsis :value="'处理时间:' + resultObj.taskNameStartTime"></JEllipsis></span
></div>
<div class="task-user">操作人{{ resultObj.taskAssigneeName }}</div>
</template>
</a-step>
<a-step>
<template #title>
<div class="task-title">...</div>
</template>
</a-step>
</a-steps>
</a-card>
<!-- 意见 -->
<a-card title="意见信息" :bodyStyle="{ padding: '0 20px' }" size="default" style="margin-top: 20px">
<a-list itemLayout="vertical">
<template v-for="(item, index) in resultObj.bpmLogList" :key="index">
<a-list-item>
<a-list-item-meta :description="item.remarks || '无意见信息'">
<template #title>
<a
><p>{{ item.opUserName }}</p
><span style="color: #ff6d75">[{{ item.taskName }}]</span> {{ item.opTime }}</a
>
</template>
<template #avatar>
<a-avatar :size="36" style="background-color: #51cbff">
<template #icon><UserOutlined /></template>
</a-avatar>
</template>
</a-list-item-meta>
<template v-for="(file, index) in item.bpmFiles" :key="index">
<div class="ant-upload-list ant-upload-list-text">
<div class="ant-upload-list-item ant-upload-list-item-done">
<div class="ant-upload-list-item-info">
<span>
<PaperClipOutlined />
<a
target="_blank"
rel="noopener noreferrer"
:title="file.fileName"
:href="getFileAccessHttpUrl(file.filePath)"
class="ant-upload-list-item-name"
>{{ file.fileName }}</a
>
</span>
</div>
</div>
</div>
</template>
</a-list-item>
</template>
<a-list-item>
<div style="width: 100%">
<div style="margin-bottom: 5px">
处理意见
<a-select style="width: 300px" placeholder="常用审批语" @change="handleChangeSelect">
<template #suffixIcon>
<Icon icon="ant-design:smile-outlined" />
</template>
<a-select-option v-for="(item, key) in remarksDictOptions" :key="key" :value="item.value">{{ item.text }}</a-select-option>
</a-select>
</div>
<a-textarea rows="3" v-model:value="model.reason" />
</div>
</a-list-item>
<a-list-item>
<JUpload v-model:value="fileList" :returnUrl="false" :maxCount="10"></JUpload>
</a-list-item>
<a-list-item v-show="false">
<a-row>
<a-col :span="24">
<a-radio-group v-model:value="model.processModel">
<a-radio :checked="true" :value="1">单分支模式</a-radio>
<a-radio :value="2">多分支模式</a-radio>
<a-radio :value="3" v-if="resultObj.histListSize > 0">驳回</a-radio>
</a-radio-group>
<span :hidden="model.processModel !== 2">
<span style="color: red">多分支模式默认执行所有分支</span>
<template v-for="(item, index) in resultObj.transitionList" :key="index">
<a-checkbox :checked="true" :value="item.nextnode">{{ item.Transition }}</a-checkbox>
</template>
</span>
<!-- 选择要驳回的节点 -->
<span :hidden="model.processModel !== 3" v-if="resultObj.histListSize > 0">
<a-select v-model:value="model.rejectModelNode" style="width: 150px">
<template v-for="(item, index) in resultObj.histListNode" :key="index">
<a-select-option :value="item.TASK_DEF_KEY_">{{ item.NAME_ }}</a-select-option>
</template>
</a-select>
</span>
</a-col>
</a-row>
</a-list-item>
<!--选择下一步处理/抄送人-->
<a-list-item>
<a-checkbox :checked="checkedNext" @change="handleCheckedNextChange">指定下一步操作人指定下一步会签人员</a-checkbox>
<a-checkbox :checked="checkedCc" @change="handleCheckedCcChange">是否抄送</a-checkbox>
</a-list-item>
<a-list-item style="line-height: 32px" :hidden="!checkedNext">
<span>指定下一步操作人指定下一步会签人员</span>
<a-select style="width: 300px" mode="multiple" placeholder="点击选择按钮" :value="hqUserList"></a-select>
<a-button type="primary" @click="handleHqUserSelect" preIcon="ant-design:search-outlined" style="margin-left: 8px">选择</a-button>
<a-button type="primary" @click="hqUserSelectReset" preIcon="ant-design:reload-outlined" style="margin-left: 8px">清空</a-button>
<span>如果不指定则按照系统默认</span>
</a-list-item>
<a-list-item style="line-height: 32px" :hidden="!checkedCc">
<span>抄送给</span>
<a-select style="width: 300px" mode="multiple" placeholder="点击选择按钮" :value="ccUserList"></a-select>
<a-button type="primary" @click="handleCcUserSelect" preIcon="ant-design:search-outlined" style="margin-left: 8px">选择</a-button>
<a-button type="primary" @click="ccUserSelectReset" preIcon="ant-design:reload-outlined" style="margin-left: 8px">清空</a-button>
</a-list-item>
</a-list>
<!-- 流转按钮 -->
<div style="margin-top: 20px; text-align: center">
<template v-if="model.processModel == 1">
<template v-for="(item, index) in resultObj.transitionList">
<a-button type="primary" @click="handleProcessComplete(item.nextnode)">{{ item.Transition }}</a-button>
</template>
</template>
<template v-else>
<a-button type="primary" @click="handleManyProcessComplete()">确认提交</a-button>
</template>
</div>
<br />
</a-card>
<BizSelectUserModal ref="selectHqUserModal" @selectFinished="selectHqUserOK"></BizSelectUserModal>
<BizSelectUserModal ref="selectCcUserModal" @selectFinished="selectCcUserOK"></BizSelectUserModal>
</a-spin>
</div>
</template>
<script lang="ts">
import { ref, toRefs, onMounted, computed, defineComponent, reactive } from 'vue';
import JEllipsis from '/src/components/Form/src/jeecg/components/JEllipsis.vue';
import BizSelectUserModal from './BizSelectUserModal.vue';
import { JUpload } from '/src/components/Form/src/jeecg/components/JUpload';
import { getFileAccessHttpUrl } from '/src/utils/common/compUtils';
import { UserOutlined, PaperClipOutlined } from '@ant-design/icons-vue';
import { initDictOptions } from '/src/utils/dict';
import { getProcessTaskTransInfo, processComplete } from '/src/views/super/bpm/process/manage/components/bpm.api.ts';
import { getToken } from '/src/utils/auth';
import { useMessage } from '/src/hooks/web/useMessage';
import { isString } from "@/utils/is";
export default defineComponent({
props: {
formData: { type: Object },
},
components: { UserOutlined, PaperClipOutlined, JEllipsis, JUpload, BizSelectUserModal },
emits: ['complete'],
setup(props, { emit }) {
const { createMessage, createConfirm } = useMessage();
//选择用户弹窗dom
const selectHqUserModal = ref(null);
const selectCcUserModal = ref(null);
//数据
const _this = reactive({
headers: {},
resultObj: {},
checkedNext: false,
transition: [],
hqUserSelectList: [],
ccUserSelectList: [],
remarksDictOptions: [],
currTask: {},
model: {
taskId: '',
nextnode: '',
nextCodeCount: '',
reason: '',
processModel: 1,
rejectModelNode: '',
nextUserName: '',
nextUserId: '',
ccUserIds: '',
ccUserRealNames: '',
fileList: '',
},
bodyStyle: {
padding: '10px',
},
checkedCc: false,
fileList: [],
loading: false,
});
//步骤点
const stepIndex = computed(() => {
if (_this.resultObj.bpmLogListCount > 3) {
return _this.resultObj.bpmLogStepListCount + 1;
}
return _this.resultObj.bpmLogStepListCount;
});
//下一步用户
const hqUserList = computed(() => {
let names = [];
let ids = [];
for (let a = 0; a < _this.hqUserSelectList.length; a++) {
names.push(_this.hqUserSelectList[a].realname);
ids.push(_this.hqUserSelectList[a].username);
}
_this.model.nextUserId = ids.join(',');
_this.model.nextUserName = names.join(',');
return names;
});
//抄送用户
const ccUserList = computed(() => {
let names = [];
let ids = [];
for (let a = 0; a < _this.ccUserSelectList.length; a++) {
names.push(_this.ccUserSelectList[a].realname);
ids.push(_this.ccUserSelectList[a].username);
}
_this.model.ccUserIds = ids.join(',');
_this.model.ccUserRealNames = names.join(',');
return names;
});
//下拉选择处理意见
function handleChangeSelect(value) {
_this.model.reason = value;
}
//初始化字典值
async function initDictConfig() {
let res = await initDictOptions('approval_remarks');
_this.remarksDictOptions = res && res.length > 0 ? res : [];
}
//是否指定下一步操作人
function handleCheckedNextChange(e) {
_this.checkedNext = e.target.checked;
hqUserSelectReset();
}
//是否指定抄送
function handleCheckedCcChange(e) {
_this.checkedCc = e.target.checked;
ccUserSelectReset();
}
//办理流程
function handleProcessComplete(nextnode?) {
if (!_this.model.reason || _this.model.reason.length == 0) {
//update-begin-author:taoyan date:2022-9-5 for: VUEN-2157 2. 意见允许为空
//createMessage.warning('请填写处理意见');
//return;
//update-end-author:taoyan date:2022-9-5 for: VUEN-2157 2. 意见允许为空
}
if (nextnode) {
_this.model.nextnode = nextnode;
}
console.log('流程办理数据:_this:------>', _this);
createConfirm({
title: '提示',
content: '确认提交审批吗?',
centered: false,
onOk: async () => {
_this.loading = true;
console.log('_this.fileList------->>', _this.fileList);
_this.model.fileList = isString(_this.fileList) ? _this.fileList : JSON.stringify(_this.fileList);
let res = await processComplete(_this.model);
_this.loading = false;
if (res.success) {
createMessage.success(res.message);
emit('complete');
} else {
createMessage.warning(res.message);
}
},
});
}
//发送流程
function handleManyProcessComplete() {
if (_this.model.processModel == 3) {
if (!_this.model.rejectModelNode || _this.model.rejectModelNode.length == 0) {
createMessage.warning('请选择驳回节点');
return;
}
}
handleProcessComplete();
}
//选择下一步操作人
function handleHqUserSelect() {
selectHqUserModal.value.add();
}
//选择下一步操作人回调
function selectHqUserOK(data) {
_this.hqUserSelectList = data;
}
//下一步操作人清除
function hqUserSelectReset() {
_this.hqUserSelectList = [];
}
//选择抄送人员
function handleCcUserSelect() {
selectCcUserModal.value.add();
}
//选择抄送人员回调
function selectCcUserOK(data) {
_this.ccUserSelectList = data;
}
//抄送人员清除
function ccUserSelectReset() {
_this.ccUserSelectList = [];
}
/**
* 加载数据
* @param formData
*/
async function loadData(formData) {
let params = { taskId: formData.taskId }; //查询条件
_this.loading = true;
const res = await getProcessTaskTransInfo(params);
_this.loading = false;
if (res.success) {
_this.resultObj = res.result;
}
}
onMounted(() => {
const token = getToken();
_this.headers = { 'X-Access-Token': token };
console.log('任务办理组件数据:', props.formData);
_this.currTask = props.formData.bizTaskList[0];
_this.model.taskId = _this.currTask.id;
loadData(props.formData);
initDictConfig();
});
return {
handleChangeSelect,
handleCheckedNextChange,
handleCheckedCcChange,
handleProcessComplete,
handleManyProcessComplete,
getFileAccessHttpUrl,
handleCcUserSelect,
ccUserSelectReset,
handleHqUserSelect,
hqUserSelectReset,
selectHqUserOK,
selectCcUserOK,
stepIndex,
hqUserList,
ccUserList,
selectHqUserModal,
selectCcUserModal,
...toRefs(_this),
};
},
});
</script>
<style scoped>
.task-info {
margin: 20px 0;
}
.task-title {
font-weight: bold;
}
.task-date {
text-overflow: ellipsis;
white-space: nowrap;
}
.ant-steps-item-description {
max-width: 200px !important;
}
/** Button按钮间距 */
.ant-btn {
margin-left: 3px;
}
/** 标题和描述对齐 */
:deep(.ant-steps-item-content) {
text-align: left;
margin-left: 50px;
}
/** 描述的样式 */
.descriptionDiv {
display: flex;
justify-content: left;
align-items: center;
margin-top: 5px;
}
</style>
@@ -0,0 +1,78 @@
<template>
<!--催办form-->
<div style="padding: 24px">
<a-form ref="formRef" :rules="rules" :model="formState" :label-col="labelCol" :wrapper-col="wrapperCol">
<a-form-item label="催办类型">
<a-checkbox-group v-model:value="formState.notifyType">
<a-checkbox value="1" name="type">页面通知</a-checkbox>
<a-checkbox value="2" name="type">邮件</a-checkbox>
</a-checkbox-group>
</a-form-item>
<a-form-item label="催办内容" name="remarks">
<a-textarea rows="3" v-model:value="formState.remarks" />
</a-form-item>
</a-form>
<div style="text-align: center; margin-top: 10px">
<a-button type="primary" :loading="loading" @click="handleOk()">保存</a-button>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, computed, unref, reactive, toRaw } from 'vue';
import { saveOrUpdateNotify } from '/src/views/super/bpm/process/manage/components/bpm.api.ts';
import { propTypes } from '/src/utils/propTypes';
import { ValidateErrorEntity } from 'ant-design-vue/es/form/interface';
//props声明
const props = defineProps({
procInstId: propTypes.string.def(''),
});
// Emits声明
const emit = defineEmits(['success']);
const loading = ref(false);
const formRef = ref();
const formState = reactive({
notifyType: '1,2',
remarks: '',
procInstId: props.procInstId,
});
const labelCol = {
xs: { span: 24 },
sm: { span: 5 },
};
const wrapperCol = {
xs: { span: 24 },
sm: { span: 16 },
};
const rules = {
remarks: [{ required: true, message: '催办内容不允许为空!' }],
};
/**
* 初始化表单数据
*/
function initFormData() {
formState.notifyType = '1,2';
formState.procInstId = props.procInstId;
}
//表单提交事件
function handleOk() {
let values = toRaw(unref(formState));
formRef.value
.validate()
.then(async () => {
loading.value = true;
values.notifyType = Array.isArray(values.notifyType) ? values.notifyType.join(',') : values.notifyType;
//提交表单
await saveOrUpdateNotify(values);
loading.value = false;
//刷新列表
emit('success');
})
.catch((error: ValidateErrorEntity<any>) => {
console.log('error', error);
});
}
initFormData();
</script>
@@ -0,0 +1,71 @@
<template>
<BasicTable @register="registerTable" :rowSelection="rowSelection"> </BasicTable>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { BasicTable } from '/src/components/Table';
import { getNotifyMeList } from '/src/views/super/bpm/process/manage/components/bpm.api.ts';
import { useListPage } from '/src/hooks/system/useListPage';
import { propTypes } from '/src/utils/propTypes';
const props = defineProps({
procInstId: propTypes.string.def(''),
});
// 列表页面公共参数、方法
const { prefixCls, tableContext } = useListPage({
tableProps: {
api: getNotifyMeList,
columns: [
{
title: '流程名称',
align: 'center',
dataIndex: 'procName',
},
{
title: '任务名称',
align: 'center',
dataIndex: 'taskName',
},
{
title: '任务处理人',
align: 'center',
dataIndex: 'taskAssignee',
},
{
title: '催办时间',
align: 'center',
dataIndex: 'opTime',
},
{
title: '催办类型',
align: 'center',
dataIndex: 'notifyType',
customRender: ({ text }) => {
var srtArr = text.split(',');
var value = '';
if (srtArr.includes('1')) {
value += ',系统通知';
}
if (srtArr.includes('2')) {
value += ',邮件';
}
return value.substring(1);
},
},
{
title: '催办说明',
align: 'center',
dataIndex: 'remarks',
},
],
size: 'middle',
maxHeight: 200,
useSearchForm: false,
showTableSetting: false,
showActionColumn: false,
searchInfo: { procInstId: props.procInstId },
},
});
const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
</script>
@@ -0,0 +1,48 @@
<template>
<!-- 审催办弹出框 -->
<a-modal width="60%" style="top: 20px" destroyOnClose :title="data.title" v-model:open="data.visible" :footer="null" @cancel="handleModalCancel">
<a-tabs defaultActiveKey="1">
<a-tab-pane key="1">
<template #tab>
<Icon icon="ant-design:user-outlined" />
<span>提醒我的</span>
</template>
<BizTaskNotifyMeList :procInstId="procInstId"></BizTaskNotifyMeList>
</a-tab-pane>
</a-tabs>
</a-modal>
</template>
<script lang="ts" setup>
import { computed, ref, unref, reactive } from 'vue';
import BizTaskNotifyMeList from './BizTaskNotifyMeList.vue';
//数据
const data = reactive({
loading: false,
title: '催办',
visible: false,
});
const procInstId = ref('');
/**
* 关闭弹窗
*/
function handleModalCancel() {
data.visible = false;
}
/**
* 催办
* @param record
*/
async function notify(id) {
procInstId.value = id;
data.visible = true;
}
defineExpose({
notify,
data,
});
</script>
@@ -0,0 +1,58 @@
<template>
<!-- 催办弹出框 -->
<a-modal width="60%" style="top: 20px" destroyOnClose :title="data.title" v-model:open="data.visible" :footer="null" @cancel="handleModalCancel">
<a-tabs defaultActiveKey="1">
<a-tab-pane key="1">
<template #tab>
<Icon icon="ant-design:file-text-outlined" />
<span>催办</span>
</template>
<BizTaskNotifyForm :procInstId="procInstId" @success="handleModalCancel"></BizTaskNotifyForm>
</a-tab-pane>
<a-tab-pane key="2">
<template #tab>
<Icon icon="ant-design:user-outlined" />
<span>我提醒的</span>
</template>
<MyBizTaskNotifyList :procInstId="procInstId"></MyBizTaskNotifyList>
</a-tab-pane>
</a-tabs>
</a-modal>
</template>
<script lang="ts" setup>
import { computed, ref, unref, reactive } from 'vue';
import { createAsyncComponent } from '/src/utils/factory/createAsyncComponent';
import MyBizTaskNotifyList from './MyBizTaskNotifyList.vue';
const BizTaskNotifyForm = createAsyncComponent(() => import('./BizTaskNotifyForm.vue'), { loading: true });
//数据
const data = reactive({
loading: false,
title: '催办',
visible: false,
});
const procInstId = ref('');
/**
* 关闭弹窗
*/
function handleModalCancel() {
data.visible = false;
}
/**
* 催办
* @param record
*/
async function notify(id) {
procInstId.value = id;
data.visible = true;
}
defineExpose({
notify,
data,
});
</script>
@@ -0,0 +1,403 @@
<template>
<!--驳回的任务办理-->
<div style="background: #fff; margin-right: 10px">
<!-- 步骤条 -->
<a-spin :spinning="loading">
<div class="ant-alert ant-alert-info" style="margin-bottom: 16px">
当前任务办理环节
<a-select style="width: 300px" v-model:value="currTask.id">
<a-select-option :value="currTask.id">{{ currTask.taskName }}</a-select-option>
</a-select>
<span :hidden="model.processModel !== 3" v-if="resultObj.histListSize > 0">
&nbsp;&nbsp;&nbsp;驳回到
<a-select v-model:value="model.rejectModelNode" style="width: 200px">
<template v-for="(item, index) in resultObj.histListNode" :key="index">
<a-select-option :value="item.TASK_DEF_KEY_">{{ item.NAME_ }}</a-select-option>
</template>
</a-select>
</span>
</div>
<a-card>
<a-steps progressDot :current="stepIndex" style="padding: 10px" size="default">
<template v-if="resultObj.bpmLogListCount > 3">
<a-step>
<template #title>
<div class="task-title">...</div>
</template>
</a-step>
</template>
<template v-for="(item, index) in resultObj.bpmLogStepList" :key="index">
<a-step>
<template #title>
<div class="task-title">{{ item.taskName }}</div>
</template>
<template #description>
<div class="task-date">
<span><JEllipsis :value="'处理时间:' + item.opTime"></JEllipsis></span>
</div>
<div class="task-user">操作人{{ item.opUserName }}</div>
</template>
</a-step>
</template>
<a-step v-if="resultObj.taskName && resultObj.taskName != ''">
<template #title>
<div class="task-title">{{ resultObj.taskName }}</div>
</template>
<template #description>
<div class="task-date">
<span style="color: #ff6d75"><JEllipsis :value="'处理时间:' + resultObj.taskNameStartTime"></JEllipsis></span
></div>
<div class="task-user">操作人{{ resultObj.taskAssigneeName }}</div>
</template>
</a-step>
<a-step>
<template #title>
<div class="task-title">...</div>
</template>
</a-step>
</a-steps>
</a-card>
<!-- 意见 -->
<a-card title="意见信息" :bodyStyle="{ padding: '0 20px' }" size="default" style="margin-top: 20px">
<a-list itemLayout="vertical">
<template v-for="(item, index) in resultObj.bpmLogList" :key="index">
<a-list-item>
<a-list-item-meta :description="item.remarks||'无意见信息'">
<template #title>
<a
><p>{{ item.opUserName }}</p
><span style="color: #ff6d75">[{{ item.taskName }}]</span> {{ item.opTime }}</a
>
</template>
<template #avatar>
<a-avatar :size="36" style="background-color: #51cbff">
<template #icon><UserOutlined /></template>
</a-avatar>
</template>
</a-list-item-meta>
<template v-for="(file, index) in item.bpmFiles" :key="index">
<div class="ant-upload-list ant-upload-list-text">
<div class="ant-upload-list-item ant-upload-list-item-done">
<div class="ant-upload-list-item-info">
<span>
<PaperClipOutlined />
<a
target="_blank"
rel="noopener noreferrer"
:title="file.fileName"
:href="getFileAccessHttpUrl(file.filePath)"
class="ant-upload-list-item-name"
>{{ file.fileName }}</a
>
</span>
</div>
</div>
</div>
</template>
</a-list-item>
</template>
<a-list-item>
<div style="width: 100%">
<div style="margin-bottom: 5px">
处理意见
<a-select style="width: 300px" placeholder="常用审批语" @change="handleChangeSelect">
<template #suffixIcon>
<Icon icon="ant-design:smile-outlined" />
</template>
<a-select-option v-for="(item, key) in remarksDictOptions" :key="key" :value="item.value">{{ item.text }}</a-select-option>
</a-select>
</div>
<a-textarea rows="3" v-model:value="model.reason" />
</div>
</a-list-item>
<a-list-item>
<JUpload v-model:value="fileList" :returnUrl="false"></JUpload>
</a-list-item>
<!--选择下一步处理/抄送人-->
<a-list-item v-show="false">
<a-checkbox :checked="checkedNext" @change="handleCheckedNextChange">指定下一步操作人指定下一步会签人员</a-checkbox>
<a-checkbox :checked="checkedCc" @change="handleCheckedCcChange">是否抄送</a-checkbox>
</a-list-item>
<a-list-item style="line-height: 32px" :hidden="!checkedNext">
<span>指定下一步操作人指定下一步会签人员</span>
<a-select style="width: 300px" mode="multiple" placeholder="点击选择按钮" :value="hqUserList"></a-select>
<a-button type="primary" @click="handleHqUserSelect" preIcon="ant-design:search-outlined" style="margin-left: 8px">选择</a-button>
<a-button type="primary" @click="hqUserSelectReset" preIcon="ant-design:reload-outlined" style="margin-left: 8px">清空</a-button>
<span>如果不指定则按照系统默认</span>
</a-list-item>
<a-list-item style="line-height: 32px" :hidden="!checkedCc">
<span>抄送给</span>
<a-select style="width: 300px" mode="multiple" placeholder="点击选择按钮" :value="ccUserList"></a-select>
<a-button type="primary" @click="handleCcUserSelect" preIcon="ant-design:search-outlined" style="margin-left: 8px">选择</a-button>
<a-button type="primary" @click="ccUserSelectReset" preIcon="ant-design:reload-outlined" style="margin-left: 8px">清空</a-button>
</a-list-item>
</a-list>
<!-- 流转按钮 -->
<div v-if="resultObj.histListSize > 0" style="margin-top: 20px; text-align: center">
<template v-if="model.processModel == 1">
<template v-for="(item, index) in resultObj.transitionList">
<a-button type="primary" @click="handleProcessComplete(item.nextnode)">{{ item.Transition }}</a-button>
</template>
</template>
<template v-else>
<a-button type="primary" @click="handleManyProcessComplete()">确认提交</a-button>
</template>
</div>
<div v-else style="margin-top: 20px; text-align: center">
<div class="ant-alert ant-alert-info" style="margin-bottom: 16px"> 暂无可驳回的任务 </div>
</div>
<br />
</a-card>
<BizSelectUserModal ref="selectHqUserModal" @selectFinished="selectHqUserOK"></BizSelectUserModal>
<BizSelectUserModal ref="selectCcUserModal" @selectFinished="selectCcUserOK"></BizSelectUserModal>
</a-spin>
</div>
</template>
<script lang="ts">
import { ref, toRefs, onMounted, computed, defineComponent, reactive } from 'vue';
import JEllipsis from '/src/components/Form/src/jeecg/components/JEllipsis.vue';
import BizSelectUserModal from './BizSelectUserModal.vue';
import { JUpload } from '/src/components/Form/src/jeecg/components/JUpload';
import { getFileAccessHttpUrl } from '/src/utils/common/compUtils';
import { UserOutlined, PaperClipOutlined } from '@ant-design/icons-vue';
import { initDictOptions } from '/src/utils/dict';
import { getProcessTaskTransInfo, processComplete } from '/src/views/super/bpm/process/manage/components/bpm.api.ts';
import { getToken } from '/src/utils/auth';
import { useMessage } from '/src/hooks/web/useMessage';
import {isString} from "@/utils/is";
export default defineComponent({
props: {
formData: { type: Object },
},
components: { UserOutlined, PaperClipOutlined, JEllipsis, JUpload, BizSelectUserModal },
emits: ['complete'],
setup(props, { emit }) {
const { createMessage, createConfirm } = useMessage();
const selectHqUserModal = ref(null);
const selectCcUserModal = ref(null);
const _this = reactive({
headers: {},
resultObj: {},
checkedNext: false,
transition: [],
hqUserSelectList: [],
ccUserSelectList: [],
remarksDictOptions: [],
currTask: {},
model: {
taskId: '',
nextnode: '',
nextCodeCount: '',
reason: '',
processModel: 3,
rejectModelNode: '',
nextUserName: '',
nextUserId: '',
ccUserIds: '',
ccUserRealNames: '',
fileList: '',
},
bodyStyle: {
padding: '10px',
},
checkedCc: false,
fileList: [],
loading: false,
});
//步骤点
const stepIndex = computed(() => {
if (_this.resultObj.bpmLogListCount > 3) {
return _this.resultObj.bpmLogStepListCount + 1;
}
return _this.resultObj.bpmLogStepListCount;
});
const hqUserList = computed(() => {
let names = [];
let ids = [];
for (let a = 0; a < _this.hqUserSelectList.length; a++) {
names.push(_this.hqUserSelectList[a].realname);
ids.push(_this.hqUserSelectList[a].username);
}
_this.model.nextUserId = ids.join(',');
_this.model.nextUserName = names.join(',');
return names;
});
const ccUserList = computed(() => {
let names = [];
let ids = [];
for (let a = 0; a < _this.ccUserSelectList.length; a++) {
names.push(_this.ccUserSelectList[a].realname);
ids.push(_this.ccUserSelectList[a].username);
}
_this.model.ccUserIds = ids.join(',');
_this.model.ccUserRealNames = names.join(',');
return names;
});
function handleChangeSelect(value) {
_this.model.reason = value;
}
function handleChangeSelect(value) {
_this.model.reason = value;
}
async function initDictConfig() {
let res = await initDictOptions('approval_remarks');
_this.remarksDictOptions = res && res.length > 0 ? res : [];
}
function handleCheckedNextChange(e) {
_this.checkedNext = e.target.checked;
hqUserSelectReset();
}
function handleCheckedCcChange(e) {
_this.checkedCc = e.target.checked;
ccUserSelectReset();
}
function handleProcessComplete(nextnode?) {
if (!_this.model.reason || _this.model.reason.length == 0) {
createMessage.warning('请填写处理意见');
return;
}
if (nextnode) {
_this.model.nextnode = nextnode;
}
createConfirm({
title: '提示',
content: '确认驳回吗?',
centered: false,
onOk: async () => {
_this.loading = true;
_this.model.fileList = isString(_this.fileList) ? _this.fileList : JSON.stringify(_this.fileList);
let res = await processComplete(_this.model);
_this.loading = false;
if (res.success) {
createMessage.success(res.message);
emit('complete');
} else {
createMessage.warning(res.message);
}
},
});
}
function handleManyProcessComplete() {
if (_this.model.processModel == 3) {
if (!_this.model.rejectModelNode || _this.model.rejectModelNode.length == 0) {
createMessage.warning('请选择驳回节点');
return;
}
}
handleProcessComplete();
}
function handleHqUserSelect() {
selectHqUserModal.value.add();
}
function selectHqUserOK(data) {
_this.hqUserSelectList = data;
}
function hqUserSelectReset() {
_this.hqUserSelectList = [];
}
function handleCcUserSelect() {
selectCcUserModal.value.add();
}
function selectCcUserOK(data) {
_this.ccUserSelectList = data;
}
function ccUserSelectReset() {
_this.ccUserSelectList = [];
}
/**
* 加载数据
* @param formData
*/
async function loadData(formData) {
let params = { taskId: formData.taskId }; //查询条件
_this.loading = true;
const res = await getProcessTaskTransInfo(params);
_this.loading = false;
if (res.success) {
_this.resultObj = res.result;
getDefaultRejectNode();
}
}
function getDefaultRejectNode() {
let taskDefKey = '';
for (let item of _this.resultObj.histListNode) {
if (item.TASK_DEF_KEY_ == _this.currTask.taskId) {
break;
}
taskDefKey = item.TASK_DEF_KEY_;
}
_this.model.rejectModelNode = taskDefKey;
}
onMounted(() => {
const token = getToken();
_this.headers = { 'X-Access-Token': token };
console.log('任务办理组件数据:', props.formData);
_this.currTask = props.formData.bizTaskList[0];
_this.model.taskId = _this.currTask.id;
loadData(props.formData);
initDictConfig();
});
return {
handleChangeSelect,
handleCheckedNextChange,
handleCheckedCcChange,
handleProcessComplete,
handleManyProcessComplete,
getFileAccessHttpUrl,
handleCcUserSelect,
ccUserSelectReset,
handleHqUserSelect,
hqUserSelectReset,
selectHqUserOK,
selectCcUserOK,
stepIndex,
hqUserList,
ccUserList,
selectHqUserModal,
selectCcUserModal,
...toRefs(_this),
};
},
});
</script>
<style scoped>
.task-info {
margin: 20px 0;
}
.task-title {
font-weight: bold;
}
.task-date {
text-overflow: ellipsis;
white-space: nowrap;
}
.ant-steps-item-description {
max-width: 200px !important;
}
/** Button按钮间距 */
.ant-btn {
margin-left: 3px;
}
/** 标题和描述对齐 */
:deep(.ant-steps-item-content) {
text-align: left;
margin-left: 50px;
}
/** 描述的样式 */
.descriptionDiv {
display: flex;
justify-content: left;
align-items: center;
margin-top: 5px;
}
</style>
@@ -0,0 +1,317 @@
<template>
<!-- 弹出框 -->
<a-modal
:open="visible"
:title="title"
width="80%"
:bodyStyle="{ height: '80vh', overflow:'auto' }"
style="top: 20px"
:footer="null"
destroyOnClose
@cancel="handleModalCancel"
>
<a-spin :spinning="confirmLoading">
<!--已选择单据-->
<div style="width: 60%; margin: 0 auto">
<a-divider orientation="left">已选择的单据</a-divider>
<a-table
ref="table"
size="middle"
bordered
rowKey="dataId"
:pagination="false"
:columns="columns"
:dataSource="dataSource"
:loading="loading"
>
</a-table>
</div>
<!--处理意见-->
<div style="width: 60%; margin: 0 auto">
<a-divider orientation="left">处理意见</a-divider>
<a-row>
<a-col :span="24">
<a-list-item>
<div style="width: 100%">
<div style="margin-bottom: 5px">
处理意见
<a-select style="width: 300px" placeholder="常用审批语" @change="handleChangeSelect">
<template #suffixIcon>
<Icon icon="ant-design:smile-outlined" />
</template>
<a-select-option v-for="(item, key) in remarksDictOptions" :key="key" :value="item.value">{{ item.text }}</a-select-option>
</a-select>
</div>
<a-textarea rows="3" v-model:value="model.reason" />
</div>
</a-list-item>
</a-col>
</a-row>
<a-row>
<a-col :span="24">
<div style="text-align: center">
<a-button type="primary" :disabled="disabledButton" @click="handleBatchComplete">确认提交</a-button>
</div>
</a-col>
</a-row>
</div>
<!--反馈结果-->
<div style="width: 60%; margin: 0 auto" v-show="dealStatus">
<a-divider orientation="left">处理结果</a-divider>
<a-table
ref="table"
size="middle"
bordered
rowKey="dataId"
:pagination="false"
:columns="columns2"
:dataSource="dataSource2"
:loading="loading"
>
</a-table>
</div>
</a-spin>
</a-modal>
</template>
<script lang="ts">
import { defineComponent, reactive, toRefs } from 'vue';
import { initDictOptions } from '/src/utils/dict';
import { getBizProcessNodeInfo, getProcessTaskTransInfo, processComplete } from '/src/views/super/bpm/process/manage/components/bpm.api';
import { useMessage } from '/src/hooks/web/useMessage';
export default defineComponent({
props: ['paramData'],
emits: ['ok'],
setup(_, { emit }) {
const { createMessage, createConfirm } = useMessage();
async function initDictConfig() {
//初始化字典
let res = await initDictOptions('approval_remarks');
data.remarksDictOptions = res && res.length > 0 ? res : [];
}
function handleChangeSelect(value) {
data.model.reason = value;
}
// 关闭模态框
function handleModalCancel() {
data.visible = false;
}
function deal(param) {
data.dealStatus = false;
data.model.reason = '';
data.dataSource = [];
data.dataSource2 = [];
data.disabledButton = false;
data.visible = true;
initFlowData(param);
}
//加载流程信息
async function initFlowData(param) {
data.loading = true;
for (let i = 0; i < param.length; i++) {
let params = { flowCode: param[i].flowCode, dataId: param[i].dataId }; //查询条件
let res = await getBizProcessNodeInfo(params);
if (res.success) {
let currTask = res.result.bizTaskList[0];
let taskId = currTask.id;
let taskName = currTask.taskName;
param[i].taskClaimFlag = currTask.taskClaimFlag;
param[i].taskName = taskName;
param[i].taskId = taskId;
let res2 = await getProcessTaskTransInfo({ taskId });
if (res2.success) {
if (res2.result.nextCodeCount == 1) {
param[i].status = '-1'; //待处理
param[i].nextnode = res2.result.transitionList[0].nextnode;
continue;
} else if (res2.result.nextCodeCount > 1) {
param[i].msg = '多流转分支不能进行提交处理';
}
}
}
param[i].status = '0';
}
data.loading = false;
data.dataSource = param;
}
//批量处理
function handleBatchComplete() {
if (!data.model.reason || data.model.reason.length == 0) {
//update-begin-author:taoyan date:2022-9-5 for: VUEN-2157 2. 意见允许为空
//createMessage.warning('请填写处理意见');
//return;
//update-end-author:taoyan date:2022-9-5 for: VUEN-2157 2. 意见允许为空
}
createConfirm({
title: '确认提交',
centered: false,
content: '是否提交选中数据?',
onOk: () => {
handleProcessComplete();
},
});
}
//批量完成
async function handleProcessComplete() {
data.confirmLoading = true;
data.disabledButton = true;
for (var i = 0; i < data.dataSource.length; i++) {
if (data.dataSource[i].taskClaimFlag) {
//未签收,不做处理
data.dataSource[i].msg = '未签收不能进行提交处理';
data.dataSource[i].status = '0';
continue;
}
if (data.dataSource[i].status == '-1') {
let param = {
taskId: data.dataSource[i].taskId,
nextnode: data.dataSource[i].nextnode,
nextCodeCount: '1',
reason: data.model.reason,
processModel: 1,
rejectModelNode: '',
nextUserName: '',
nextUserId: '',
ccUserIds: '',
ccUserRealNames: '',
fileList: '',
};
console.log('流程办理数据:', param);
let res = await processComplete(param);
if (res.success) {
data.dataSource[i].status = '1';
continue;
}
}
data.dataSource[i].status = '0';
}
data.dealStatus = true;
data.dataSource2 = data.dataSource;
data.confirmLoading = false;
emit('ok');
}
//初始化字典
initDictConfig();
//初始化数据
const data = reactive({
loading: false,
title: '批量处理',
visible: false,
confirmLoading: false,
currTask: {},
dataSource: [],
dataSource2: [],
// 表头
columns: [
{
title: '#',
dataIndex: '',
key: 'rowIndex',
width: 60,
align: 'center',
customRender: ({ index }) => {
return parseInt(index) + 1;
},
},
{
title: '标题',
align: 'center',
dataIndex: 'bizTitle',
},
{
title: '流程编码',
align: 'center',
dataIndex: 'flowCode',
},
{
title: '业务key',
align: 'center',
dataIndex: 'dataId',
},
{
title: '当前环节',
align: 'center',
dataIndex: 'taskName',
},
],
columns2: [
{
title: '#',
dataIndex: '',
key: 'rowIndex',
width: 60,
align: 'center',
customRender: ({ index }) => {
return parseInt(index) + 1;
},
},
{
title: '标题',
align: 'center',
dataIndex: 'bizTitle',
},
{
title: '流程编码',
align: 'center',
dataIndex: 'flowCode',
},
{
title: '业务key',
align: 'center',
dataIndex: 'dataId',
},
{
title: '当前环节',
align: 'center',
dataIndex: 'taskName',
},
{
title: '处理状态',
align: 'center',
dataIndex: 'status',
customRender: ({ text }) => {
if (text == '1') {
return '处理成功';
} else if (text == '0') {
return '处理失败';
} else {
return '待处理';
}
},
},
{
title: '描述',
align: 'center',
dataIndex: 'msg',
},
],
remarksDictOptions: [],
dealStatus: false,
disabledButton: false,
model: {
taskId: '',
nextnode: '',
nextCodeCount: '',
reason: '',
processModel: 1,
rejectModelNode: '',
nextUserName: '',
nextUserId: '',
ccUserIds: '',
ccUserRealNames: '',
fileList: '',
},
});
return {
deal,
handleModalCancel,
handleChangeSelect,
handleBatchComplete,
...toRefs(data),
};
},
});
</script>
@@ -0,0 +1,262 @@
<template>
<!-- 弹出框 -->
<a-modal
:open="visible"
:title="title"
width="80%"
:bodyStyle="{ height: '80vh', overflow:'auto' }"
style="top: 20px"
:footer="null"
destroyOnClose
@cancel="handleModalCancel"
>
<a-spin :spinning="confirmLoading">
<!--已选择单据-->
<div style="width: 60%; margin: 0 auto">
<a-divider orientation="left">已选择的单据</a-divider>
<a-table
ref="table"
size="middle"
bordered
rowKey="dataId"
:pagination="false"
:columns="columns"
:dataSource="dataSource"
:loading="loading"
>
</a-table>
</div>
<!--处理意见-->
<div style="width: 60%; margin: 0 auto">
<a-divider orientation="left">选择委托人</a-divider>
<a-row>
<a-col :span="18">
<a-form-item label="用户名:" :label-col="{ span: 5 }" :wrapper-col="{ span: 18 }">
<JSelectUserByDept :isRadioSelection="true" :showButton="false" v-model:value="model.userName"></JSelectUserByDept>
</a-form-item>
</a-col>
<a-col :span="6">
<a-form-item>
<a-button type="primary" :disabled="disabledButton" @click="handleBatchEntruster">确认委托</a-button>
</a-form-item>
</a-col>
</a-row>
</div>
<!--反馈结果-->
<div style="width: 60%; margin: 0 auto" v-show="dealStatus">
<a-divider orientation="left">处理结果</a-divider>
<a-table
ref="table"
size="middle"
bordered
rowKey="dataId"
:pagination="false"
:columns="columns2"
:dataSource="dataSource2"
:loading="loading"
>
</a-table>
</div>
</a-spin>
</a-modal>
</template>
<script lang="ts">
import { defineComponent, reactive, toRefs, toRaw } from 'vue';
import JSelectUserByDept from '/src/components/Form/src/jeecg/components/JSelectUserByDept.vue';
import { getBizProcessNodeInfo } from '/src/views/super/bpm/process/manage/components/bpm.api';
import { taskEntrust } from '../leave.api';
import { useMessage } from '/src/hooks/web/useMessage';
export default defineComponent({
props: ['formData'],
components: { JSelectUserByDept },
emits: ['ok'],
setup(_, { emit }) {
const { createMessage, createConfirm } = useMessage();
// 关闭模态框
function handleModalCancel() {
data.visible = false;
}
function deal(processData) {
data.dealStatus = false;
data.model.reason = '';
data.dataSource = [];
data.dataSource2 = [];
data.disabledButton = false;
data.model.userName = '';
data.userInfo = {};
data.visible = true;
initFlowData(processData);
}
async function initFlowData(processData) {
data.loading = true;
for (let i = 0; i < processData.length; i++) {
let params = { flowCode: processData[i].flowCode, dataId: processData[i].dataId }; //查询条件
let res = await getBizProcessNodeInfo(params);
if (res.success) {
let currTask = res.result.bizTaskList[0];
let taskId = currTask.id;
let taskName = currTask.taskName;
processData[i].taskClaimFlag = currTask.taskClaimFlag;
processData[i].taskName = taskName;
processData[i].taskId = taskId;
processData[i].status = '-1'; //待处理
continue;
}
processData[i].status = '0';
}
data.loading = false;
data.dataSource = processData;
console.log('------数据初始化--------', data.dataSource);
}
function handleBatchEntruster() {
let username = toRaw(data.model.userName);
if (!username || username.length == 0) {
createMessage.warning('请选择委托人!');
return;
}
createConfirm({
title: '确认委托',
centered: false,
content: '是否委托选中数据?',
onOk: () => {
batchEntruster();
},
});
}
async function batchEntruster() {
data.confirmLoading = true;
data.disabledButton = true;
let username = toRaw(data.model.userName);
let taskAssignee = Array.isArray(username) ? username.join('') : username;
for (var i = 0; i < data.dataSource.length; i++) {
if (data.dataSource[i].taskClaimFlag) {
//未签收,不做处理
data.dataSource[i].msg = '未签收不能进行委托处理';
data.dataSource[i].status = '0';
continue;
}
if (data.dataSource[i].status == '-1') {
let params = {
taskId: data.dataSource[i].taskId,
taskAssignee: taskAssignee,
};
console.log('委托', params);
let res = await taskEntrust(params);
if (res.success) {
data.dataSource[i].status = '1';
continue;
}
}
data.dataSource[i].status = '0';
}
data.dealStatus = true;
data.dataSource2 = data.dataSource;
data.confirmLoading = false;
emit('ok');
}
const data = reactive({
loading: false,
title: '批量处理',
visible: false,
confirmLoading: false,
currTask: {},
dataSource: [],
dataSource2: [],
// 表头
columns: [
{
title: '#',
dataIndex: '',
key: 'rowIndex',
width: 60,
align: 'center',
customRender: ({ index }) => {
return parseInt(index) + 1;
},
},
{
title: '标题',
align: 'center',
dataIndex: 'bizTitle',
},
{
title: '流程编码',
align: 'center',
dataIndex: 'flowCode',
},
{
title: '业务key',
align: 'center',
dataIndex: 'dataId',
},
{
title: '当前环节',
align: 'center',
dataIndex: 'taskName',
},
],
columns2: [
{
title: '#',
dataIndex: '',
key: 'rowIndex',
width: 60,
align: 'center',
customRender: ({ index }) => {
return parseInt(index) + 1;
},
},
{
title: '标题',
align: 'center',
dataIndex: 'bizTitle',
},
{
title: '流程编码',
align: 'center',
dataIndex: 'flowCode',
},
{
title: '业务key',
align: 'center',
dataIndex: 'dataId',
},
{
title: '当前环节',
align: 'center',
dataIndex: 'taskName',
},
{
title: '处理状态',
align: 'center',
dataIndex: 'status',
customRender: ({ text }) => {
if (text == '1') {
return '处理成功';
} else if (text == '0') {
return '处理失败';
} else {
return '待处理';
}
},
},
],
dealStatus: false,
disabledButton: false,
model: {
userName: '',
},
});
return {
deal,
handleModalCancel,
handleBatchEntruster,
...toRefs(data),
};
},
});
</script>
@@ -0,0 +1,348 @@
<template>
<!-- 弹出框 -->
<a-modal
:open="visible"
:title="title"
width="80%"
:bodyStyle="{ height: '80vh', overflow:'auto' }"
style="top: 20px"
:footer="null"
destroyOnClose
@cancel="handleModalCancel"
>
<a-spin :spinning="confirmLoading">
<!--已选择单据-->
<div style="width: 60%; margin: 0 auto">
<a-divider orientation="left">已选择的单据</a-divider>
<a-table
ref="table"
size="middle"
bordered
rowKey="dataId"
:pagination="false"
:columns="columns"
:dataSource="dataSource"
:loading="loading"
>
<template #currTask="{ text, record, index }">
<a-select style="width: 200px" :defaultValue="record.taskId">
<a-select-option :value="record.taskId">{{ record.taskName }}</a-select-option>
</a-select>
</template>
<template #rejectTask="{ text, record, index }">
<a-select v-model:value="record.rejectModelNode" style="width: 200px" @change="handleRejectNodeChange">
<template v-for="(item, index) in record.histListNode" :key="index">
<a-select-option :value="item.TASK_DEF_KEY_">{{ item.NAME_ }}</a-select-option>
</template>
</a-select>
</template>
</a-table>
</div>
<!--处理意见-->
<div style="width: 60%; margin: 0 auto">
<a-divider orientation="left">处理意见</a-divider>
<a-row>
<a-col :span="24">
<a-list-item>
<div style="width: 100%">
<div style="margin-bottom: 5px">
处理意见
<a-select style="width: 300px" placeholder="常用审批语" @change="handleChangeSelect">
<template #suffixIcon>
<Icon icon="ant-design:smile-outlined" />
</template>
<a-select-option v-for="(item, key) in remarksDictOptions" :key="key" :value="item.value">{{ item.text }}</a-select-option>
</a-select>
</div>
<a-textarea rows="3" v-model:value="model.reason" />
</div>
</a-list-item>
</a-col>
</a-row>
<a-row>
<a-col :span="24">
<div style="text-align: center">
<a-button type="primary" :disabled="disabledButton" @click="handleBatchReject">确认驳回</a-button>
</div>
</a-col>
</a-row>
</div>
<!--反馈结果-->
<div style="width: 60%; margin: 0 auto" v-show="dealStatus">
<a-divider orientation="left">处理结果</a-divider>
<a-table
ref="table"
size="middle"
bordered
rowKey="dataId"
:pagination="false"
:columns="columns2"
:dataSource="dataSource2"
:loading="loading"
>
</a-table>
</div>
</a-spin>
</a-modal>
</template>
<script lang="ts">
import { defineComponent, reactive, toRefs } from 'vue';
import { initDictOptions } from '/src/utils/dict';
import { getBizProcessNodeInfo, getProcessTaskTransInfo, processComplete } from '/src/views/super/bpm/process/manage/components/bpm.api';
import { useMessage } from '/src/hooks/web/useMessage';
export default defineComponent({
props: ['paramData'],
emits: ['ok'],
setup(_, { emit }) {
const { createMessage, createConfirm } = useMessage();
async function initDictConfig() {
//初始化字典
let res = await initDictOptions('approval_remarks');
data.remarksDictOptions = res && res.length > 0 ? res : [];
}
function handleChangeSelect(value) {
data.model.reason = value;
}
// 关闭模态框
function handleModalCancel() {
data.visible = false;
}
function deal(processData) {
data.dealStatus = false;
data.model.reason = '';
data.dataSource = [];
data.dataSource2 = [];
data.disabledButton = false;
data.visible = true;
initFlowData(processData);
}
async function initFlowData(processData) {
data.loading = true;
for (let i = 0; i < processData.length; i++) {
let params = { flowCode: processData[i].flowCode, dataId: processData[i].dataId }; //查询条件
let res = await getBizProcessNodeInfo(params);
if (res.success) {
let currTask = res.result.bizTaskList[0];
let taskId = currTask.id;
let taskName = currTask.taskName;
processData[i].taskClaimFlag = currTask.taskClaimFlag;
processData[i].taskName = taskName;
processData[i].taskId = taskId;
let res2 = await getProcessTaskTransInfo({ taskId });
if (res2.success) {
processData[i].histListNode = res2.result.histListNode;
processData[i].rejectModelNode = getDefaultRejectNode(processData[i].histListNode, currTask);
processData[i].status = '-1'; //待处理
continue;
}
}
processData[i].status = '0';
}
data.loading = false;
data.dataSource = processData;
console.log('processData------------->', processData);
}
function getDefaultRejectNode(histListNode, currTask) {
let taskDefKey = '';
for (let item of histListNode) {
if (item.TASK_DEF_KEY_ == currTask.taskId) {
break;
}
taskDefKey = item.TASK_DEF_KEY_;
}
return taskDefKey;
}
function handleBatchReject() {
if (!data.model.reason || data.model.reason.length == 0) {
createMessage.warning('请填写处理意见');
return;
}
createConfirm({
title: '确认驳回',
centered: false,
content: '是否驳回选中数据?',
onOk: () => {
handleProcessComplete();
},
});
}
async function handleProcessComplete() {
data.confirmLoading = true;
data.disabledButton = true;
for (var i = 0; i < data.dataSource.length; i++) {
if (data.dataSource[i].taskClaimFlag) {
//未签收,不做处理
data.dataSource[i].msg = '未签收不能进行驳回处理';
data.dataSource[i].status = '0';
continue;
}
if (data.dataSource[i].rejectModelNode == '') {
data.dataSource[i].msg = '未选中驳回节点不能进行驳回处理';
data.dataSource[i].status = '0';
continue;
}
if (data.dataSource[i].status == '-1') {
let param = {
taskId: data.dataSource[i].taskId,
nextnode: data.dataSource[i].nextnode,
nextCodeCount: '1',
reason: data.model.reason,
processModel: 3,
rejectModelNode: data.dataSource[i].rejectModelNode,
nextUserName: '',
nextUserId: '',
ccUserIds: '',
ccUserRealNames: '',
fileList: '',
};
console.log('驳回办理数据:', param);
let res = await processComplete(param);
if (res.success) {
data.dataSource[i].status = '1';
continue;
}
}
data.dataSource[i].status = '0';
}
data.dealStatus = true;
data.dataSource2 = data.dataSource;
data.confirmLoading = false;
emit('ok');
}
function handleRejectNodeChange(value) {
console.log('------handleRejectNodeChange--------', data.dataSource);
}
initDictConfig();
const data = reactive({
loading: false,
title: '批量处理',
visible: false,
confirmLoading: false,
currTask: {},
dataSource: [],
dataSource2: [],
// 表头
columns: [
{
title: '#',
dataIndex: '',
key: 'rowIndex',
width: 60,
align: 'center',
customRender: ({ index }) => {
return parseInt(index) + 1;
},
},
{
title: '标题',
align: 'center',
dataIndex: 'bizTitle',
},
{
title: '流程编码',
align: 'center',
dataIndex: 'flowCode',
},
{
title: '业务key',
align: 'center',
dataIndex: 'dataId',
},
{
title: '当前环节',
align: 'center',
dataIndex: 'currTask',
slots: { customRender: 'currTask' },
},
{
title: '驳回到',
align: 'center',
dataIndex: 'rejectTask',
slots: { customRender: 'rejectTask' },
},
],
columns2: [
{
title: '#',
dataIndex: '',
key: 'rowIndex',
width: 60,
align: 'center',
customRender: ({ index }) => {
return parseInt(index) + 1;
},
},
{
title: '标题',
align: 'center',
dataIndex: 'bizTitle',
},
{
title: '流程编码',
align: 'center',
dataIndex: 'flowCode',
},
{
title: '业务key',
align: 'center',
dataIndex: 'dataId',
},
{
title: '当前环节',
align: 'center',
dataIndex: 'taskName',
},
{
title: '处理状态',
align: 'center',
dataIndex: 'status',
customRender: ({ text }) => {
if (text == '1') {
return '处理成功';
} else if (text == '0') {
return '处理失败';
} else {
return '待处理';
}
},
},
{
title: '描述',
align: 'center',
dataIndex: 'msg',
},
],
remarksDictOptions: [],
dealStatus: false,
disabledButton: false,
model: {
taskId: '',
nextnode: '',
nextCodeCount: '',
reason: '',
processModel: 3,
rejectModelNode: '',
nextUserName: '',
nextUserId: '',
ccUserIds: '',
ccUserRealNames: '',
fileList: '',
},
});
return {
deal,
handleModalCancel,
handleChangeSelect,
handleBatchReject,
handleRejectNodeChange,
...toRefs(data),
};
},
});
</script>
@@ -0,0 +1,250 @@
<template>
<!-- 弹出框 -->
<a-modal
:open="visible"
:title="title"
width="80%"
:bodyStyle="{ height: '80vh', overflow:'auto' }"
style="top: 20px"
:footer="null"
destroyOnClose
@cancel="handleModalCancel"
>
<a-spin :spinning="confirmLoading">
<!--已选择单据-->
<div style="width: 60%; margin: 0 auto">
<a-divider orientation="left">已选择的单据</a-divider>
<a-table
ref="table"
size="middle"
bordered
rowKey="dataId"
:pagination="false"
:columns="columns"
:dataSource="dataSource"
:loading="loading"
>
</a-table>
</div>
<!--处理意见-->
<div style="width: 60%; margin: 0 auto">
<a-divider orientation="left">解挂</a-divider>
<a-row>
<a-col :span="24">
<div style="text-align: center">
<a-button type="primary" :disabled="disabledButton" @click="handleBatchRestart">确认解挂</a-button>
</div>
</a-col>
</a-row>
</div>
<!--反馈结果-->
<div style="width: 60%; margin: 0 auto" v-show="dealStatus">
<a-divider orientation="left">处理结果</a-divider>
<a-table
ref="table2"
size="middle"
bordered
rowKey="dataId"
:pagination="false"
:columns="columns2"
:dataSource="dataSource2"
:loading="loading"
>
</a-table>
</div>
</a-spin>
</a-modal>
</template>
<script lang="ts">
import { defineComponent, reactive, toRefs } from 'vue';
import { getBizProcessNodeInfo, restart } from '/src/views/super/bpm/process/manage/components/bpm.api';
import { useMessage } from '/src/hooks/web/useMessage';
export default defineComponent({
props: ['formData'],
emits: ['ok'],
setup(_, { emit }) {
const { createMessage, createConfirm } = useMessage();
// 关闭模态框
function handleModalCancel() {
data.visible = false;
}
function deal(processData) {
data.dealStatus = false;
data.dataSource = [];
data.dataSource2 = [];
data.disabledButton = false;
data.visible = true;
initFlowData(processData);
}
function completeProcess() {
data.visible = false;
emit('ok');
}
async function initFlowData(processData) {
data.loading = true;
for (let i = 0; i < processData.length; i++) {
let params = { flowCode: processData[i].flowCode, dataId: processData[i].dataId }; //查询条件
let res = await getBizProcessNodeInfo(params);
if (res.success) {
let currTask = res.result.bizTaskList[0];
let taskId = currTask.id;
let taskName = currTask.taskName;
processData[i].taskClaimFlag = currTask.taskClaimFlag;
processData[i].procInstId = currTask.procInstId;
processData[i].taskName = taskName;
processData[i].taskId = taskId;
processData[i].status = '-1'; //待处理
continue;
}
processData[i].status = '0';
}
data.loading = false;
data.dataSource = processData;
console.log('------数据初始化--------', data.dataSource);
}
function handleBatchRestart() {
createConfirm({
title: '确认解挂',
centered: false,
content: '是否解挂选中数据?',
onOk: () => {
batchRestart();
},
});
}
async function batchRestart() {
data.confirmLoading = true;
data.disabledButton = true;
for (var i = 0; i < data.dataSource.length; i++) {
if (data.dataSource[i].status == '-1') {
let param = { processInstanceId: data.dataSource[i].procInstId };
let res = await restart(param);
if (res.success) {
data.dataSource[i].status = '1';
continue;
}
}
data.dataSource[i].status = '0';
}
data.dealStatus = true;
data.dataSource2 = data.dataSource;
data.confirmLoading = false;
emit('ok');
}
const data = reactive({
loading: false,
title: '批量处理',
visible: false,
confirmLoading: false,
currTask: {},
dataSource: [],
dataSource2: [],
// 表头
columns: [
{
title: '#',
dataIndex: '',
key: 'rowIndex',
width: 60,
align: 'center',
customRender: ({ index }) => {
return parseInt(index) + 1;
},
},
{
title: '标题',
align: 'center',
dataIndex: 'bizTitle',
},
{
title: '流程编码',
align: 'center',
dataIndex: 'flowCode',
},
{
title: '业务key',
align: 'center',
dataIndex: 'dataId',
},
{
title: '当前环节',
align: 'center',
dataIndex: 'taskName',
},
],
columns2: [
{
title: '#',
dataIndex: '',
key: 'rowIndex',
width: 60,
align: 'center',
customRender: ({ index }) => {
return parseInt(index) + 1;
},
},
{
title: '标题',
align: 'center',
dataIndex: 'bizTitle',
},
{
title: '流程编码',
align: 'center',
dataIndex: 'flowCode',
},
{
title: '业务key',
align: 'center',
dataIndex: 'dataId',
},
{
title: '当前环节',
align: 'center',
dataIndex: 'taskName',
},
{
title: '处理状态',
align: 'center',
dataIndex: 'status',
customRender: ({ text }) => {
if (text == '1') {
return '处理成功';
} else if (text == '0') {
return '处理失败';
} else {
return '待处理';
}
},
},
],
remarksDictOptions: [],
dealStatus: false,
disabledButton: false,
model: {
taskId: '',
nextnode: '',
nextCodeCount: '',
reason: '',
processModel: 1,
rejectModelNode: '',
nextUserName: '',
nextUserId: '',
ccUserIds: '',
ccUserRealNames: '',
fileList: '',
},
});
return {
deal,
handleModalCancel,
handleBatchRestart,
...toRefs(data),
};
},
});
</script>
@@ -0,0 +1,239 @@
<template>
<!-- 弹出框 -->
<a-modal
:open="visible"
:title="title"
width="80%"
:bodyStyle="{ height: '80vh', overflow:'auto' }"
style="top: 20px"
:footer="null"
destroyOnClose
@cancel="handleModalCancel"
>
<a-spin :spinning="confirmLoading">
<!--已选择单据-->
<div style="width: 60%; margin: 0 auto">
<a-divider orientation="left">已选择的单据</a-divider>
<a-table
ref="table"
size="middle"
bordered
rowKey="dataId"
:pagination="false"
:columns="columns"
:dataSource="dataSource"
:loading="loading"
>
</a-table>
</div>
<!--处理意见-->
<div style="width: 60%; margin: 0 auto">
<a-divider orientation="left">挂起</a-divider>
<a-row>
<a-col :span="24">
<div style="text-align: center">
<a-button type="primary" :disabled="disabledButton" @click="handleBatchSuspend">确认挂起</a-button>
</div>
</a-col>
</a-row>
</div>
<!--反馈结果-->
<div style="width: 60%; margin: 0 auto" v-show="dealStatus">
<a-divider orientation="left">处理结果</a-divider>
<a-table
ref="table2"
size="middle"
bordered
rowKey="dataId"
:pagination="false"
:columns="columns2"
:dataSource="dataSource2"
:loading="loading"
>
</a-table>
</div>
</a-spin>
</a-modal>
</template>
<script lang="ts">
import { defineComponent, reactive, toRefs } from 'vue';
import { getBizProcessNodeInfo, suspend } from '/src/views/super/bpm/process/manage/components/bpm.api';
import { useMessage } from '/src/hooks/web/useMessage';
export default defineComponent({
props: ['formData'],
emits: ['ok'],
setup(_, { emit }) {
const { createMessage, createConfirm } = useMessage();
// 关闭模态框
function handleModalCancel() {
data.visible = false;
}
function deal(processData) {
data.dealStatus = false;
data.dataSource = [];
data.dataSource2 = [];
data.disabledButton = false;
data.visible = true;
initFlowData(processData);
}
async function initFlowData(processData) {
data.loading = true;
for (let i = 0; i < processData.length; i++) {
let params = { flowCode: processData[i].flowCode, dataId: processData[i].dataId }; //查询条件
let res = await getBizProcessNodeInfo(params);
if (res.success) {
let currTask = res.result.bizTaskList[0];
let taskId = currTask.id;
let taskName = currTask.taskName;
processData[i].taskClaimFlag = currTask.taskClaimFlag;
processData[i].procInstId = currTask.procInstId;
processData[i].taskName = taskName;
processData[i].taskId = taskId;
processData[i].status = '-1'; //待处理
continue;
}
processData[i].status = '0';
}
data.loading = false;
data.dataSource = processData;
console.log('------数据初始化--------', data.dataSource);
}
function handleBatchSuspend() {
createConfirm({
title: '确认挂起',
centered: false,
content: '是否挂起选中数据?',
onOk: () => {
batchSuspend();
},
});
}
async function batchSuspend() {
data.confirmLoading = true;
data.disabledButton = true;
for (var i = 0; i < data.dataSource.length; i++) {
if (data.dataSource[i].taskClaimFlag) {
//未签收,不做处理
data.dataSource[i].msg = '未签收不能进行挂起处理';
data.dataSource[i].status = '0';
continue;
}
if (data.dataSource[i].status == '-1') {
let param = { processInstanceId: data.dataSource[i].procInstId };
console.log('挂起:', param);
let res = await suspend(param);
if (res.success) {
data.dataSource[i].status = '1';
continue;
}
}
data.dataSource[i].status = '0';
}
data.dealStatus = true;
data.dataSource2 = data.dataSource;
data.confirmLoading = false;
emit('ok');
}
const data = reactive({
loading: false,
title: '批量处理',
visible: false,
confirmLoading: false,
currTask: {},
dataSource: [],
dataSource2: [],
// 表头
columns: [
{
title: '#',
dataIndex: '',
key: 'rowIndex',
width: 60,
align: 'center',
customRender: ({ index }) => {
return parseInt(index) + 1;
},
},
{
title: '标题',
align: 'center',
dataIndex: 'bizTitle',
},
{
title: '流程编码',
align: 'center',
dataIndex: 'flowCode',
},
{
title: '业务key',
align: 'center',
dataIndex: 'dataId',
},
{
title: '当前环节',
align: 'center',
dataIndex: 'taskName',
},
],
columns2: [
{
title: '#',
dataIndex: '',
key: 'rowIndex',
width: 60,
align: 'center',
customRender: ({ index }) => {
return parseInt(index) + 1;
},
},
{
title: '标题',
align: 'center',
dataIndex: 'bizTitle',
},
{
title: '流程编码',
align: 'center',
dataIndex: 'flowCode',
},
{
title: '业务key',
align: 'center',
dataIndex: 'dataId',
},
{
title: '当前环节',
align: 'center',
dataIndex: 'taskName',
},
{
title: '处理状态',
align: 'center',
dataIndex: 'status',
customRender: ({ text }) => {
if (text == '1') {
return '处理成功';
} else if (text == '0') {
return '处理失败';
} else {
return '待处理';
}
},
},
],
dealStatus: false,
disabledButton: false,
});
return {
deal,
handleModalCancel,
handleBatchSuspend,
...toRefs(data),
};
},
});
</script>
@@ -0,0 +1,265 @@
<template>
<!-- 批量业务办理弹出框 -->
<a-modal :open="visible" width="100%" destroyOnClose :bodyStyle="bodyStyle" style="top: 0" :footer="null" @cancel="handleModalCancel">
<a-spin :spinning="confirmLoading">
<div class="ant-alert ant-alert-info" style="margin-bottom: 16px">
当前任务环节
<a-select style="width: 300px" :defaultValue="currTask.id">
<a-select-option :value="currTask.id">{{ currTask.taskName }}</a-select-option>
</a-select>
<template v-if="!currTask.suspendFlag">
<template v-if="currTask.taskClaimFlag">
<a-button @click="handleClaim()" type="primary" preIcon="ant-design:caret-right-outlined">签收</a-button>
</template>
<template v-else>
<a-button @click="handleOpt('submit')" type="primary" preIcon="ant-design:caret-right-outlined">发送</a-button>
<a-button @click="handleOpt('reject')" type="primary" preIcon="ant-design:rollback-outlined">退回</a-button>
<a-button @click="selectEntruster()" type="primary" preIcon="ant-design:user-outlined">委托</a-button>
<a-button @click="handleSuspend()" type="primary" preIcon="ant-design:lock-outlined">挂起</a-button>
</template>
</template>
<template v-else>
<a-button @click="handleActive()" type="primary" preIcon="ant-design:unlock-outlined">解挂</a-button>
</template>
<span style="color: red" v-if="currTask.suspendFlag">当前流程已挂起需要进行解挂再进行办理</span>
</div>
<div>
<template v-if="isComp">
<DynamicLink :path="path" :formData="formData"></DynamicLink>
</template>
<template v-else>
<iframe :src="iframeUrl" frameborder="0" width="100%" :height="height" scrolling="auto"></iframe>
</template>
</div>
</a-spin>
<DelegateModal @register="registerModal" @success="handleEntruster"></DelegateModal>
<BpmBizTaskOptModal ref="bpmBizTaskOptModal" :formData="formData" @success="completeProcess"></BpmBizTaskOptModal>
</a-modal>
</template>
<script lang="ts">
import { defineComponent, reactive, toRefs, computed, getCurrentInstance } from 'vue';
import DelegateModal from '/src/views/super/bpm/process/manage/components/DelegateModal.vue';
import DynamicLink from '/src/views/super/bpm/process/manage/components/DynamicLink.vue';
import BpmBizTaskOptModal from './BpmBizTaskOptModal.vue';
import { isUrl, getBpmFormUrl } from '/src/utils/is';
import { getToken } from '/src/utils/auth';
import { useGlobSetting } from '/src/hooks/setting';
import { useMessage } from '/src/hooks/web/useMessage';
import { useModal } from '/src/components/Modal';
import { claim, taskEntrust, suspend, restart } from '/src/views/super/bpm/process/manage/components/bpm.api.ts';
export default defineComponent({
name: 'BpmBizTaskDealModal',
components: {
DynamicLink,
DelegateModal,
BpmBizTaskOptModal,
},
props: ['path', 'formData'],
emits: ['ok'],
setup(props, { emit }) {
const globSetting = useGlobSetting();
const { createMessage, createConfirm } = useMessage();
//委派弹窗
const [registerModal, { openModal }] = useModal();
//弹窗示例
const instance = getCurrentInstance();
const data = reactive({
loading: false,
title: '流程',
visible: false,
confirmLoading: false,
currTask: {},
bodyStyle: {
padding: '0',
height: window.innerHeight + 'px',
'overflow-y': 'auto',
},
iframeUrl: '',
});
let TOKEN = getToken();
let DOMAIN_URL = globSetting.domainUrl;
let TASKID = props.formData.taskDefKey;
//是否组件
const isComp = computed(() => {
//获取流程审批url
//let URL = (props.path || '').replace(/{{([^}}]+)?}}/g, (s1, s2) => eval(s2)); // URL支持{{ window.xxx }}占位符变量
let URL = getBpmFormUrl(props.path, TOKEN, DOMAIN_URL, TASKID);
if (isUrl(URL)) {
data.iframeUrl = URL;
return false;
}
return true;
});
// 关闭模态框
function handleModalCancel() {
data.visible = false;
}
/**
* 打开弹窗处理数据
* @param processData
*/
function deal(processData) {
console.log('-----任务办理组件数据:-------', processData);
let taskList = processData.bizTaskList;
data.currTask = taskList && taskList.length > 0 ? taskList[0] : {};
data.visible = true;
}
/**
* 完成
*/
function completeProcess() {
data.visible = false;
emit('ok');
}
/**
* 发送
* @param opt
*/
function handleOpt(opt) {
console.log('handleOpt', opt);
instance.refs.bpmBizTaskOptModal.deal(opt);
instance.refs.bpmBizTaskOptModal.data.title = opt == 'submit' ? '发送' : '退回';
}
//签收
function handleClaim() {
let params = { taskId: data.currTask?.id }; //查询条件
createConfirm({
title: '确认签收吗',
centered: false,
content: '是否签收该任务?',
onOk: async () => {
data.confirmLoading = true;
let res = await claim(params);
data.confirmLoading = false;
if (res.success) {
createMessage.success(res.message);
completeProcess();
} else {
createMessage.warning(res.message);
}
},
});
}
//委托
function selectEntruster() {
openModal(true);
}
/**
* 委派选择回调
* @param data
*/
async function handleEntruster(obj) {
let params = { taskId: data.currTask.id, taskAssignee: obj.username };
await taskEntrust(params, (res) => {
if (res.success) {
createMessage.success(res.message);
completeProcess();
} else {
createMessage.warning(res.message);
}
});
}
//挂起
function handleSuspend() {
let params = { processInstanceId: data.currTask?.procInstId }; //查询条件
createConfirm({
title: '确认挂起吗',
centered: false,
content: '是否挂起该任务?',
onOk: async () => {
data.confirmLoading = true;
let res = await suspend(params);
data.confirmLoading = false;
if (res.success) {
createMessage.success(res.message);
completeProcess();
} else {
createMessage.warning(res.message);
}
},
});
}
/**
* 解挂
*/
function handleActive() {
let params = { processInstanceId: data.currTask?.procInstId }; //查询条件
createConfirm({
title: '确认解挂吗',
centered: false,
content: '是否解挂该任务?',
onOk: async () => {
data.confirmLoading = true;
let res = await restart(params);
data.confirmLoading = false;
if (res.success) {
createMessage.success(res.message);
completeProcess();
} else {
createMessage.warning(res.message);
}
},
});
}
return {
handleModalCancel,
handleOpt,
handleClaim,
selectEntruster,
handleEntruster,
handleActive,
handleSuspend,
completeProcess,
deal,
registerModal,
isComp,
...toRefs(data),
};
},
});
</script>
<style lang="less" scoped>
html[data-theme='light'] {
.ant-alert {
color: rgba(0, 0, 0, 0.85);
}
}
/** Button按钮间距 */
.ant-btn {
margin-left: 3px;
}
.ant-alert {
box-sizing: border-box;
margin: 0;
padding: 0;
font-size: 14px;
font-variant: tabular-nums;
line-height: 1.5715;
list-style: none;
font-feature-settings: tnum;
position: relative;
display: flex;
align-items: center;
padding: 8px 15px;
word-wrap: break-word;
border-radius: 2px;
}
.ant-alert-info {
background-color: @alert-info-bg-color;
border: 1px solid @alert-info-border-color;
}
</style>
@@ -0,0 +1,116 @@
<template>
<!-- 流程任务处理弹出框 -->
<a-modal
width="100%"
style="top: 0"
wrapClassName="full-modal"
destroyOnClose
:title="data.title"
v-model:open="data.visible"
:bodyStyle="data.bodyStyle"
:footer="null"
@cancel="handleModalCancel"
>
<a-tabs defaultActiveKey="1" tabPosition="left">
<a-tab-pane key="1">
<template #tab>
<Icon icon="ant-design:file-text-outlined" />
<span>任务处理</span>
</template>
<template v-if="data.opt == 'submit'">
<BizTaskModal :formData="formData" @complete="completeProcess"></BizTaskModal>
</template>
<template v-else-if="data.opt == 'reject'">
<BizTaskRejectModal :formData="formData" @complete="completeProcess"></BizTaskRejectModal>
</template>
</a-tab-pane>
<a-tab-pane key="2">
<template #tab>
<Icon icon="ant-design:sliders-outlined" />
<span>流程图</span>
</template>
<ProcessDiagram :formData="formData"></ProcessDiagram>
</a-tab-pane>
</a-tabs>
</a-modal>
</template>
<script lang="ts" setup>
import { computed, reactive, toRef } from 'vue';
import {getBpmFormUrl, isUrl} from '/src/utils/is';
import { getToken } from '/src/utils/auth';
import { useGlobSetting } from '/src/hooks/setting';
import BizTaskModal from './BizTaskModal.vue';
import BizTaskRejectModal from './BizTaskRejectModal.vue';
import ProcessDiagram from '/src/views/super/bpm/process/manage/components/ProcessDiagram.vue';
const globSetting = useGlobSetting();
//声明props
const props = defineProps({
path: { type: String },
formData: { type: Object },
});
// 声明Emits
const emit = defineEmits(['success']);
//数据
const data = reactive({
loading: false,
title: '流程',
visible: false,
bodyStyle: {
padding: '0',
height: window.innerHeight - 80 + 'px',
'overflow-y': 'auto',
},
height: window.innerHeight - 120 + 'px',
iframeUrl: '',
opt: '',
});
let TOKEN = getToken();
let DOMAIN_URL = globSetting.domainUrl;
let TASKID = props.formData.taskDefKey;
//是否组件
const isComp = computed(() => {
//获取流程审批url
//let URL = (props.path || '').replace(/{{([^}}]+)?}}/g, (s1, s2) => eval(s2)); // URL支持{{ window.xxx }}占位符变量
let URL = getBpmFormUrl(props.path, TOKEN, DOMAIN_URL, TASKID);
if (isUrl(URL)) {
data.iframeUrl = URL;
return false;
}
return true;
});
/**
* 关闭弹窗
*/
function completeProcess() {
data.visible = false;
emit('success');
}
/**
* 关闭弹窗
*/
function handleModalCancel() {
data.visible = false;
}
/**
* 打开弹窗前处理
* @param record
*/
function deal(opt) {
data.opt = opt;
data.visible = true;
}
defineExpose({
deal,
data,
});
</script>
<style lang="less" scoped></style>
@@ -0,0 +1,89 @@
<template>
<!-- 审批进度弹出框 -->
<a-modal
width="80%"
style="top: 20px"
destroyOnClose
:title="data.title"
v-model:open="data.visible"
:bodyStyle="data.bodyStyle"
:footer="null"
@cancel="handleModalCancel"
>
<a-tabs defaultActiveKey="1" tabPosition="left">
<a-tab-pane key="1">
<template #tab>
<Icon icon="ant-design:file-text-outlined" />
<span>审批记录</span>
</template>
<HisTaskModule :formData="formData"></HisTaskModule>
</a-tab-pane>
<a-tab-pane key="2">
<template #tab>
<Icon icon="ant-design:sliders-outlined" />
<span>流程跟踪</span>
</template>
<ProcessDiagram :formData="formData"></ProcessDiagram>
</a-tab-pane>
</a-tabs>
</a-modal>
</template>
<script lang="ts" setup>
import { computed, ref, unref, reactive } from 'vue';
import ProcessDiagram from '/src/views/super/bpm/process/manage/components/ProcessDiagram.vue';
import HisTaskModule from '/src/views/super/bpm/process/manage/components/HisTaskModule.vue';
import { getBizHisProcessNodeInfo } from '/src/views/super/bpm/process/manage/components/bpm.api';
//数据
const data = reactive({
loading: false,
title: '流程',
visible: false,
bodyStyle: {
padding: '0',
height: window.innerHeight - 80 + 'px',
'overflow-y': 'auto',
},
height: window.innerHeight - 120 + 'px',
});
const formData = ref({});
const path = ref('');
/**
* 关闭弹窗
*/
function handleModalCancel() {
data.visible = false;
}
/**
* 打开弹窗前处理
* @param record
*/
async function handleTrack(params) {
let res = await getBizHisProcessNodeInfo(params);
if (res.success) {
console.log('获取流程节点信息', res);
formData.value = {
dataId: res.result.dataId,
procInsId: res.result.procInsId,
tableName: res.result.tableName,
vars: res.result.records,
};
path.value = res.result.formUrl;
data.visible = true;
}
}
defineExpose({
handleTrack,
data,
});
</script>
<style lang="less" scoped>
.ant-tabs-left-content {
padding-top: 10px !important;
}
</style>
@@ -0,0 +1,71 @@
<template>
<BasicTable @register="registerTable" :rowSelection="rowSelection"> </BasicTable>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { BasicTable } from '/src/components/Table';
import { getNotifyList } from '/src/views/super/bpm/process/manage/components/bpm.api.ts';
import { useListPage } from '/src/hooks/system/useListPage';
import { propTypes } from '/src/utils/propTypes';
const props = defineProps({
procInstId: propTypes.string.def(''),
});
// 列表页面公共参数、方法
const { prefixCls, tableContext } = useListPage({
tableProps: {
api: getNotifyList,
columns: [
{
title: '流程名称',
align: 'center',
dataIndex: 'procName',
},
{
title: '任务名称',
align: 'center',
dataIndex: 'taskName',
},
{
title: '任务处理人',
align: 'center',
dataIndex: 'taskAssignee',
},
{
title: '催办时间',
align: 'center',
dataIndex: 'opTime',
},
{
title: '催办类型',
align: 'center',
dataIndex: 'notifyType',
customRender: ({ text }) => {
let srtArr = text.split(',');
let value = '';
if (srtArr.includes('1')) {
value += ',页面通知';
}
if (srtArr.includes('2')) {
value += ',邮件';
}
return value.substring(1);
},
},
{
title: '催办说明',
align: 'center',
dataIndex: 'remarks',
},
],
size: 'middle',
maxHeight: 200,
useSearchForm: false,
showTableSetting: false,
showActionColumn: false,
searchInfo: { procInstId: props.procInstId },
},
});
const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
</script>
@@ -0,0 +1,95 @@
import { defHttp } from '/src/utils/http/axios';
import { Modal } from 'ant-design-vue';
enum Api {
list = '/joa/biz/extBizLeave/list',
add = '/joa/biz/extBizLeave/add',
edit = '/joa/biz/extBizLeave/edit',
delete = '/joa/biz/extBizLeave/delete',
deleteBatch = '/joa/biz/extBizLeave/deleteBatch',
queryById = '/joa/biz/extBizLeave/queryById',
startProcess = '/act/process/extActProcess/startMutilProcess',
invalidProcess = '/act/task/invalidBizProcess',
queryFlowDataByCodeAndId = '/act/process/extActFlowData/queryFlowDataByCodeAndId',
taskEntrust = '/act/task/taskEntrust',
checkNotify = '/act/process/extActFlowData/checkNotify',
}
/**
* 列表
* @param params
*/
export const list = (params) => defHttp.get({ url: Api.list, params });
/**
* 根据id查询
* @param params
*/
export const queryById = (params) => defHttp.get({ url: Api.queryById, params }, { isTransformResponse: false });
/**
* 保存或者更新
* @param params
*/
export const saveOrUpdate = (params, isUpdate) => {
let url = isUpdate ? Api.edit : Api.add;
return defHttp.post({ url: url, params });
};
/**
* 删除
*/
export const deleteOne = (params, handleSuccess) => {
return defHttp.delete({ url: Api.delete, params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
};
/**
* 批量删除
* @param params
*/
export const batchDelete = (params, handleSuccess) => {
Modal.confirm({
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.delete({ url: Api.deleteBatch, data: params }, { joinParamsToUrl: true }).then((res) => {
handleSuccess(res);
});
},
});
};
/**
* 提交流程
* @param params
*/
export const startProcess = (params) => {
return defHttp.post({ url: Api.startProcess, params }, { isTransformResponse: false });
};
/**
* 作废流程
* @param params
*/
export const invalidProcess = (params, handleSuccess) => {
return defHttp.put({ url: Api.invalidProcess, params }).then(() => {
handleSuccess();
});
};
/**
* 查询流程数据
* @param params
*/
export const queryFlowData = (params, handleSuccess) => {
return defHttp.get({ url: Api.queryFlowDataByCodeAndId, params }, { isTransformResponse: false }).then((res) => {
handleSuccess(res);
});
};
/**
* 查询催办
* @param params
*/
export const checkNotify = (params) => {
return defHttp.get({ url: Api.checkNotify, params }, { isTransformResponse: false });
};
/**
* 委派
* @param params
*/
export const taskEntrust = (params) => defHttp.put({ url: Api.taskEntrust, params }, { isTransformResponse: false });
@@ -0,0 +1,107 @@
import { FormSchema } from '/src/components/Table';
import { render } from '/src/utils/common/renderUtils';
export const columns = [
{
title: '请假人',
dataIndex: 'name',
width: 100,
slots: { customRender: 'notify' },
},
{
title: '请假天数',
dataIndex: 'days',
width: 100,
},
{
title: '开始时间',
dataIndex: 'beginDate',
width: 100,
},
{
title: '结束时间',
dataIndex: 'endDate',
width: 100,
},
{
title: '请假原因',
dataIndex: 'reason',
ellipsis: true,
width: 150,
},
{
title: '状态',
dataIndex: 'bpmStatus',
width: 80,
customRender: ({ text }) => {
return render.renderDict(text, 'bpm_status');
},
},
];
export const searchFormSchema: FormSchema[] = [
{
field: 'bizTaskType',
label: '类型',
component: 'Input',
defaultValue: '1',
slot: 'bizTaskType',
},
{
field: 'name',
label: '请假人',
component: 'Input',
},
];
/**
* 表单form
*/
export const formSchema: FormSchema[] = [
{
field: 'id',
label: '',
component: 'Input',
show: false,
},
{
label: '请假人',
field: 'name',
required: true,
component: 'Input',
},
{
label: '请假天数',
field: 'days',
component: 'InputNumber',
componentProps: {
width: '100%',
},
},
{
label: '开始时间',
field: 'beginDate',
component: 'DatePicker',
componentProps: {
showTime: true,
valueFormat: 'YYYY-MM-DD HH:mm:ss',
placeholder: '请选择开始时间',
},
},
{
label: '结束时间',
field: 'endDate',
component: 'DatePicker',
componentProps: {
showTime: true,
valueFormat: 'YYYY-MM-DD HH:mm:ss',
placeholder: '请选择结束时间',
},
},
{
label: '请假原因',
field: 'reason',
component: 'InputTextArea',
componentProps: {
rows: 4
}
},
];
@@ -0,0 +1,60 @@
import { defHttp } from '/@/utils/http/axios';
import { Modal } from 'ant-design-vue';
enum Api {
list = '/joa/joaBusinesStrip/list',
queryById = '/joa/joaBusinesStrip/queryById',
save = '/joa/joaBusinesStrip/add',
edit = '/joa/joaBusinesStrip/edit',
deleteOne = '/joa/joaBusinesStrip/delete',
deleteBatch = '/joa/joaBusinesStrip/deleteBatch',
getDepartName = '/sys/sysDepart/listAll',
}
/**
* 列表
* @param params
*/
export const list = (params) => defHttp.get({ url: Api.list, params });
/**
* 获取部门名称
* @param params
*/
export const getDepartName = (params) => defHttp.get({ url: Api.getDepartName, params });
/**
* 根据id查询
* @param params
*/
export const queryById = (params) => defHttp.get({ url: Api.queryById, params }, { isTransformResponse: false });
/**
* 删除一个
*/
export const deleteOne = (params, handleSuccess) => {
return defHttp.delete({ url: Api.deleteOne, params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
};
/**
* 批量删除
* @param params
*/
export const batchDelete = (params, handleSuccess) => {
Modal.confirm({
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.delete({ url: Api.deleteBatch, data: params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
},
});
};
/**
* 保存或者更新
* @param params
*/
export const saveOrUpdate = (params, isUpdate) => {
let url = isUpdate ? Api.edit : Api.save;
return defHttp.post({ url: url, params });
};
@@ -0,0 +1,78 @@
import { FormSchema } from '/@/components/Table';
import { render } from '/@/utils/common/renderUtils';
export const columns = [
{
title: '出差人',
dataIndex: 'applyUserName',
width: 100,
},
{
title: '部门名称',
dataIndex: 'departName',
width: 100,
},
{
title: '项目名称',
dataIndex: 'projectName',
width: 100,
},
{
title: '目的地',
dataIndex: 'destination',
width: 100,
slots: { customRender: 'pcaSlot' },
},
{
title: '出发时间',
dataIndex: 'departureTime',
width: 100,
customRender: function ({ text }) {
if (!text) {
return '';
}
if (text.length > 10) {
return text.substring(0, 10);
}
return text;
},
},
{
title: '出差天数',
dataIndex: 'dayNum',
width: 100,
},
{
title: '出行工具',
dataIndex: 'travelTool',
width: 100,
customRender: ({ text }) => {
if (text == 1) {
return '客车';
} else if (text == 2) {
return '火车';
} else if (text == 3) {
return '飞机';
} else {
return text;
}
},
},
{
title: '流程状态',
align: 'center',
dataIndex: 'bpmStatus',
width: 100,
customRender: ({ text }) => {
return render.renderDict(text, 'bpm_status');
},
},
];
export const searchFormSchema: FormSchema[] = [
{
field: 'applyUserName',
label: '出差人',
component: 'Input',
colProps: { span: 6 },
},
];
@@ -0,0 +1,180 @@
<template>
<BasicTable @register="registerTable" :rowSelection="rowSelection">
<template #tableTitle>
<a-button type="primary" preIcon="ant-design:plus-outlined" @click="handleCreate"> 新增</a-button>
<a-dropdown v-if="selectedRowKeys.length > 0">
<template #overlay>
<a-menu>
<a-menu-item key="1" @click="batchHandleDelete">
<Icon icon="ant-design:delete-outlined"></Icon>
删除
</a-menu-item>
</a-menu>
</template>
<a-button
>批量操作
<Icon icon="mdi:chevron-down"></Icon>
</a-button>
</a-dropdown>
</template>
<template #pcaSlot="{ text }">
{{ getAreaTextByCode(text) }}
</template>
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
</template>
</BasicTable>
<!--角色工单授权-->
<BusinessTripModal @register="registerModal" @success="reload" />
<BpmPictureModal @register="registerBpmModal" />
</template>
<script lang="ts" name="leave-list" setup>
import { ref } from 'vue';
import { BasicTable, TableAction } from '/@/components/Table';
import { useModal } from '/@/components/Modal';
import BusinessTripModal from './components/BusinessTripModal.vue';
import BpmPictureModal from '/@/views/super/bpm/process/manage/components/BpmPictureModal.vue';
import { columns, searchFormSchema } from './business.trip.data';
import { list, deleteOne, batchDelete } from './business.trip.api';
import { useListPage } from '/@/hooks/system/useListPage';
import { startProcess } from '/@/views/super/bpm/example/batch/leave.api';
import { useMessage } from '/@/hooks/web/useMessage';
import { getAreaTextByCode } from '/@/components/Form/src/utils/Area';
const [registerModal, { openModal }] = useModal();
const [registerBpmModal, { openModal: bpmPicModal }] = useModal();
const { createMessage, createConfirm } = useMessage();
// 列表页面公共参数、方法
const { prefixCls, tableContext } = useListPage({
designScope: 'business-trip-list',
tableProps: {
api: list,
columns: columns,
canResize: false,
formConfig: {
schemas: searchFormSchema,
},
actionColumn: {
width: 120,
},
},
});
const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
const flowCode = 'joa_biz_trip_01';
/**
* 新增
*/
function handleCreate() {
openModal(true, {
isUpdate: false,
});
}
/**
* 编辑
*/
function handleEdit(record: Recordable) {
openModal(true, {
record,
isUpdate: true,
});
}
/**
* 详情
*/
function handleDetail(record) {
openModal(true, {
record,
isUpdate: true,
isDetail: true,
});
}
/**
* 删除事件
*/
async function handleDelete(record) {
await deleteOne({ id: record.id }, reload);
}
/**
* 批量删除事件
*/
async function batchHandleDelete() {
await batchDelete({ ids: selectedRowKeys.value }, reload);
}
/**
* 提交流程
*/
function handleStartProcess(record) {
createConfirm({
iconType: 'warning',
title: '提示',
content: '确认提交流程吗?',
onOk: async () => {
let res = await startProcess({
flowCode: flowCode,
id: record.id,
formUrl: 'super/bpm/example/joa/businessTrip/components/BusinessTripForm',
formUrlMobile: 'applyform/businesStrip',
});
if (res.success) {
createMessage.success(res.message);
reload();
} else {
createMessage.warning(res.message);
}
},
});
}
/**
* 审批进度
*/
async function handlePreviewPic(record) {
bpmPicModal(true, {
flowCode,
dataId: record.id,
});
}
/**
* 操作栏
*/
function getTableAction(record) {
return [
{
label: '编辑',
onClick: handleEdit.bind(null, record),
ifShow: record.bpmStatus === '1',
},
{
label: '提交流程',
onClick: handleStartProcess.bind(null, record),
ifShow: record.bpmStatus === '1',
},
];
}
/**
* 下拉操作栏
*/
function getDropDownAction(record) {
return [
{
label: '详情',
onClick: handleDetail.bind(null, record),
},
{
label: '删除',
popConfirm: {
title: '是否确认删除',
confirm: handleDelete.bind(null, record),
},
ifShow: record.bpmStatus === '1',
},
{
label: '审批进度',
onClick: handlePreviewPic.bind(null, record),
ifShow: record.bpmStatus !== '1',
},
];
}
</script>
@@ -0,0 +1,384 @@
<template>
<div>
<a-form ref="formRef" :model="model" :labelCol="labelCol" :wrapperCol="wrapperCol" :rules="validatorRules" style="padding-bottom: 10px">
<JFormContainer :disabled="true">
<a-card id="staffCard" class="ant-card">
<span id="staffEvectionTitle">员工出差申请单</span>
<table border="1px" id="staffEvectionTable">
<tr>
<td class="firstTr">出差人</td>
<td class="firstTr" colspan="2">
<a-form-item>
<span class="fontiframe">{{ nickname }}</span>
</a-form-item>
</td>
<td class="firstTr">部门</td>
<td class="firstTr" colspan="2">
<a-form-item>
<JSelectDept v-model:value="model.departId" :multiple="false" :checkStrictly="true" :showButton="false"></JSelectDept>
</a-form-item>
</td>
</tr>
<tr>
<td> 目的地 </td>
<td colspan="2">
<a-form-item name="destination" class="mudidi">
<JAreaLinkage placeholder="请选择" v-model:value="model.destination" :showArea="true" :showAll="false" />
</a-form-item>
</td>
<td> 项目名称 </td>
<td colspan="2">
<a-form-item name="projectName">
<a-input class="text" v-model:value="model.projectName" />
</a-form-item>
</td>
</tr>
<tr>
<td> 出发时间 </td>
<td colspan="2">
<a-form-item>
<a-date-picker class="input" @change="difference" format="YYYY-MM-DD" v-model:value="model.departureTime" :allowClear="false" />
</a-form-item>
</td>
<td> 计划返回时间 </td>
<td colspan="2">
<a-form-item>
<a-date-picker class="input" @change="difference" format="YYYY-MM-DD" v-model:value="model.plannedReturnTime" :allowClear="false" />
</a-form-item>
</td>
</tr>
<tr>
<td> 实际返回时间 </td>
<td colspan="2">
<a-form-item>
<a-date-picker class="input" placeholder="" format="YYYY-MM-DD" v-model:value="model.actualReturnTime" :allowClear="false" />
</a-form-item>
</td>
<td> 出差天数 </td>
<td colspan="2">
<a-form-item>
<a-input class="text" v-model:value="model.dayNum" />
</a-form-item>
</td>
</tr>
<tr>
<td> 出差经费支出 </td>
<td colspan="5">
<a-form-item>
<a-radio-group class="fontiframe" name="radioGroup" v-model:value="model.travelExpensesType">
<a-radio class="radioGroup" value="1">预支借款</a-radio>
<a-radio class="radioGroup" value="2">个人垫付</a-radio>
</a-radio-group>
</a-form-item>
</td>
</tr>
<tr>
<td> 出发地 </td>
<td colspan="2">
<a-form-item>
<JAreaLinkage v-model:value="model.departAddress" placeholder="请选择" :showArea="true" :showAll="false" />
</a-form-item>
</td>
<td> 出行工具 </td>
<td colspan="2">
<a-form-item>
<a-radio-group class="fontiframe" name="radioGroup" v-model:value="model.travelTool">
<a-radio class="radioGroup" :value="1">客车</a-radio>
<a-radio class="radioGroup" :value="2">火车</a-radio>
<a-radio class="radioGroup" :value="3">飞机</a-radio>
</a-radio-group>
</a-form-item>
</td>
</tr>
<tr>
<td> 任务及事由 </td>
<td colspan="5">
<a-form-item>
<a-textarea v-model:value="model.reason" class="textArea"></a-textarea>
</a-form-item>
</td>
</tr>
<tr style="height: 38px">
<td> 部门领导审核 </td>
<td colspan="2">
<span>{{ model.departLeaderAudit }}</span>
</td>
<td> 财务审核 </td>
<td colspan="2">
<span>{{ model.financeAudit }}</span>
</td>
</tr>
<tr style="height: 38px">
<td> 出纳放款 </td>
<td colspan="2">
<span>{{ model.cashierLoanAmount }}</span>
</td>
<td> 总经理审核 </td>
<td colspan="2">
<span>{{ model.managerAudit }}</span>
</td>
</tr>
</table>
</a-card>
</JFormContainer>
</a-form>
</div>
</template>
<script lang="ts" setup>
import { ref, computed, unref, reactive, toRaw } from 'vue';
import JAreaLinkage from '/@/components/Form/src/jeecg/components/JAreaLinkage.vue';
import JSelectDept from '/@/components/Form/src/jeecg/components/JSelectDept.vue';
import JFormContainer from '/@/components/Form/src/jeecg/components/JFormContainer.vue';
import { getRealCode } from '/@/components/Form/src/utils/areaDataUtil.js';
import { useUserStore } from '/@/store/modules/user';
import { queryById } from '../business.trip.api';
import dayjs from "dayjs";
// props声明
const props = defineProps({
formData: {
type: Object,
default: () => {},
},
});
const userStore = useUserStore();
const formRef = ref(null);
const nickname = ref(userStore.getUserInfo?.realname);
const model = reactive({
applyUserName: '',
departId: '',
destination: '',
projectName: '',
departureTime: null,
plannedReturnTime: null,
actualReturnTime: null,
dayNum: 0,
travelExpensesType: '1',
departAddress: '',
travelTool: 1,
reason: '',
departLeaderAudit: '',
financeAudit: '',
cashierLoanAmount: '',
managerAudit: '',
});
//表单校验
const validatorRules = {
destination: [{ required: true, message: '目的地不能为空!' }],
projectName: [{ required: true, message: '请输入项目名称!' }],
};
const labelCol = {
xs: { span: 24 },
sm: { span: 3 },
};
const wrapperCol = {
xs: { span: 24 },
sm: { span: 21 },
};
async function initFormData() {
let res = await queryById({ id: props.formData.dataId });
if (res.success) {
console.log('获取流程节点信息', res);
let obj = res.result;
//表单赋值
obj.destination = getRealCode(obj.destination, 3);
obj.departAddress && (obj.departAddress = getRealCode(obj.departAddress, 3));
Object.assign(model, { ...obj });
//时间格式化(升级antd3后,时间值不允许是字符串)
model.departureTime = model.departureTime?dayjs(model.departureTime,'YYYY-MM-DD'):null;
model.plannedReturnTime = model.plannedReturnTime?dayjs(model.plannedReturnTime,'YYYY-MM-DD'):null;
model.actualReturnTime = model.actualReturnTime?dayjs(model.actualReturnTime,'YYYY-MM-DD'):null;
}
}
initFormData();
</script>
<style lang="less" scoped>
html[data-theme='light'] {
.ant-card {
color: rgba(0, 0, 0, 0.65);
background: #fff;
border: 1px solid #e8e8e8;
}
#staffCard {
border: 1px solid #fff;
box-shadow:
0 0 1px 1px #aaa,
3px 0 5px 0 #aaa,
0 4px 7px 0 #aaa;
}
#staffEvectionTitle {
color: black;
}
#staffEvectionTable {
background-color: #fff;
border: 1px solid #000;
tr,
th {
border-bottom: 1px solid #000;
}
td,
th {
border-right: 1px solid #000;
border-bottom: 1px solid #000;
}
.input {
:deep(.ant-input) {
border: 0 solid black !important;
background-color: #fff;
}
}
.ant-input {
border: 0 solid black !important;
background-color: #fff;
}
.text {
background-color: #fff;
}
.firstTr {
color: #000;
}
.smallText .ant-input-number-input {
background-color: #fff;
}
.textArea {
border: 0 solid white;
}
:deep(.ant-input-number) {
border: 0 solid black !important;
}
}
/**去掉日期控件边框*/
.ant-picker {
border: 0 solid black !important;
}
}
html[data-theme='dark'] {
@borderColor: #3a3a3a;
#staffEvectionTable {
border: 1px solid @borderColor;
tr,
th {
border-bottom: 1px solid @borderColor;
}
td,
th {
border-right: 1px solid @borderColor;
border-bottom: 1px solid @borderColor;
}
}
}
.ant-card {
border-radius: 2px;
margin: 0 auto;
width: 1000px;
}
#staffCard {
margin-top: 10px;
margin-bottom: 10px;
border-radius: 5px;
height: auto;
}
#staffEvectionTitle {
margin-top: 1px;
font-weight: bold;
text-align: center;
display: block;
font-size: 24px;
}
#staffEvectionTable {
width: 100%;
font-size: 12px;
text-align: center;
tr td:last-child {
border-right: 0;
}
tr:last-child {
border-bottom: 0;
}
.fontiframe {
font-size: 12px;
}
.ant-form-item {
margin: 0;
}
.input {
:deep(.ant-input) {
border-radius: 0;
display: inherit;
}
}
.ant-input {
border-radius: 0;
display: inherit;
margin: 0;
width: 255px;
}
:deep(.ant-calendar-picker-input) {
border: none !important;
}
.text {
border: none !important;
border-radius: 0;
margin: 0;
width: 100%;
font-size: 12px;
}
.textArea {
resize: none;
height: 98px;
width: 100%;
font-size: 12px;
border-radius: 0px;
margin-bottom: 0px;
}
.colfirst {
width: 90px;
}
.colfour {
width: 90px;
}
.firstTr {
width: 100px;
}
.radioGroup {
font-size: 12px;
}
.smallText .ant-input-number-input {
border: 0;
margin: 0;
}
:deep(.ant-input-number) {
border: 0 solid black !important;
border-radius: 0;
}
}
/**去掉地区控件边框、字号改小*/
:deep(.ant-select:not(.ant-select-customize-input) .ant-select-selector){
font-size: 12px;
}
</style>
@@ -0,0 +1,484 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" :title="title" @ok="handleSubmit" width="1200px">
<a-form ref="formRef" :model="model" :labelCol="labelCol" :wrapperCol="wrapperCol" :rules="validatorRules" style="padding-bottom: 10px">
<JFormContainer :disabled="formDisabled">
<a-card id="staffCard" class="ant-card">
<span id="staffEvectionTitle">员工出差申请单</span>
<table border="1px" id="staffEvectionTable">
<tr>
<td class="firstTr">出差人</td>
<td class="firstTr" colspan="2">
<a-form-item>
<span class="fontiframe">{{ nickname }}</span>
</a-form-item>
</td>
<td class="firstTr">部门</td>
<td class="firstTr" colspan="2">
<a-form-item>
<JSelectDept v-model:value="model.departId" :multiple="false" :checkStrictly="true" :showButton="false"></JSelectDept>
</a-form-item>
</td>
</tr>
<tr>
<td> 目的地 </td>
<td colspan="2">
<a-form-item name="destination" class="mudidi">
<JAreaLinkage placeholder="请选择" v-model:value="model.destination" :showArea="true" :showAll="false" />
</a-form-item>
</td>
<td> 项目名称 </td>
<td colspan="2">
<a-form-item name="projectName">
<a-input class="text" v-model:value="model.projectName" />
</a-form-item>
</td>
</tr>
<tr>
<td> 出发时间 </td>
<td colspan="2">
<a-form-item>
<a-date-picker class="input" @change="difference" format="YYYY-MM-DD" v-model:value="model.departureTime" :allowClear="false" />
</a-form-item>
</td>
<td> 计划返回时间 </td>
<td colspan="2">
<a-form-item>
<a-date-picker class="input" @change="difference" format="YYYY-MM-DD" v-model:value="model.plannedReturnTime" :allowClear="false" />
</a-form-item>
</td>
</tr>
<tr>
<td> 实际返回时间 </td>
<td colspan="2">
<a-form-item>
<a-date-picker class="input" placeholder="" format="YYYY-MM-DD" v-model:value="model.actualReturnTime" :allowClear="false" />
</a-form-item>
</td>
<td> 出差天数 </td>
<td colspan="2">
<a-form-item>
<a-input class="text" v-model:value="model.dayNum" />
</a-form-item>
</td>
</tr>
<tr>
<td> 出差经费支出 </td>
<td colspan="5">
<a-form-item>
<a-radio-group class="fontiframe" name="radioGroup" v-model:value="model.travelExpensesType">
<a-radio class="radioGroup" value="1">预支借款</a-radio>
<a-radio class="radioGroup" value="2">个人垫付</a-radio>
</a-radio-group>
</a-form-item>
</td>
</tr>
<tr>
<td> 出发地 </td>
<td colspan="2">
<a-form-item class="mudidi">
<JAreaLinkage v-model:value="model.departAddress" placeholder="请选择" :showArea="true" :showAll="false" />
</a-form-item>
</td>
<td> 出行工具 </td>
<td colspan="2">
<a-form-item>
<a-radio-group class="fontiframe" name="radioGroup" v-model:value="model.travelTool">
<a-radio class="radioGroup" :value="1">客车</a-radio>
<a-radio class="radioGroup" :value="2">火车</a-radio>
<a-radio class="radioGroup" :value="3">飞机</a-radio>
</a-radio-group>
</a-form-item>
</td>
</tr>
<tr>
<td> 任务及事由 </td>
<td colspan="5">
<a-form-item>
<a-textarea v-model:value="model.reason" class="textArea"></a-textarea>
</a-form-item>
</td>
</tr>
<tr style="height: 38px">
<td> 部门领导审核 </td>
<td colspan="2">
<span>{{ model.departLeaderAudit }}</span>
</td>
<td> 财务审核 </td>
<td colspan="2">
<span>{{ model.financeAudit }}</span>
</td>
</tr>
<tr style="height: 38px">
<td> 出纳放款 </td>
<td colspan="2">
<span>{{ model.cashierLoanAmount }}</span>
</td>
<td> 总经理审核 </td>
<td colspan="2">
<span>{{ model.managerAudit }}</span>
</td>
</tr>
</table>
</a-card>
</JFormContainer>
</a-form>
</BasicModal>
</template>
<script lang="ts" setup>
import { ref, computed, unref, reactive, toRaw } from 'vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
import JAreaLinkage from '/@/components/Form/src/jeecg/components/JAreaLinkage.vue';
import JSelectDept from '/@/components/Form/src/jeecg/components/JSelectDept.vue';
import JFormContainer from '/@/components/Form/src/jeecg/components/JFormContainer.vue';
import { useMessage } from '/@/hooks/web/useMessage';
import { saveOrUpdate, getDepartName } from '../business.trip.api';
import { formatToDateTime } from '/@/utils/dateUtil';
import { getRealCode } from '/@/components/Form/src/utils/areaDataUtil.js';
import { useUserStore } from '/@/store/modules/user';
import dayjs from "dayjs";
// Emits声明
const emit = defineEmits(['register', 'success']);
//提示弹窗
const $message = useMessage();
const userStore = useUserStore();
const isUpdate = ref(true);
const formRef = ref(null);
const nickname = ref(userStore.getUserInfo?.realname);
const formDisabled = ref(false);
const model = reactive({
applyUserName: '',
departId: '',
destination: '',
projectName: '',
departureTime: null,
plannedReturnTime: null,
actualReturnTime: null,
dayNum: 0,
travelExpensesType: '1',
departAddress: '',
travelTool: 1,
reason: '',
departLeaderAudit: '',
financeAudit: '',
cashierLoanAmount: '',
managerAudit: '',
});
//表单校验
const validatorRules = {
destination: [{ required: true, message: '目的地不能为空!' }],
projectName: [{ required: true, message: '请输入项目名称!' }],
};
//表单赋值
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
setModalProps({ confirmLoading: false, showOkBtn: !!!data?.isDetail });
isUpdate.value = !!data?.isUpdate;
if (unref(isUpdate)) {
//转换地区选择组件数据格式复核要求
let obj = { ...data.record };
obj.destination = getRealCode(obj.destination, 3);
obj.departAddress && (obj.departAddress = getRealCode(obj.departAddress, 3));
Object.assign(model, { ...obj });
} else {
Object.assign(model, { ...initData });
}
//时间格式化(升级antd3后,时间值不允许是字符串)
model.departureTime = model.departureTime?dayjs(model.departureTime,'YYYY-MM-DD'):null;
model.plannedReturnTime = model.plannedReturnTime?dayjs(model.plannedReturnTime,'YYYY-MM-DD'):null;
model.actualReturnTime = model.actualReturnTime?dayjs(model.actualReturnTime,'YYYY-MM-DD'):null;
// update-begin-author:taoyan date:2022-9-5 for: VUEN-2157 出差申请、借款申请、请假申请、公文申请都有这个问题,详情不让改
if(data.isDetail === true){
formDisabled.value = true;
}else{
formDisabled.value = false;
}
// update-end-author:taoyan date:2022-9-5 for: VUEN-2157 出差申请、借款申请、请假申请、公文申请都有这个问题,详情不让改
});
//设置标题
const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
//表单提交事件
function handleSubmit() {
try {
formRef.value
.validate()
.then(async () => {
let formData = toRaw(unref(model));
//时间格式化
formData.departureTime = formData.departureTime ? formatToDateTime(formData.departureTime, 'YYYY-MM-DD HH:mm:ss') : null;
formData.plannedReturnTime = formData.plannedReturnTime ? formatToDateTime(formData.plannedReturnTime, 'YYYY-MM-DD HH:mm:ss') : null;
formData.actualReturnTime = formData.actualReturnTime ? formatToDateTime(formData.actualReturnTime, 'YYYY-MM-DD HH:mm:ss') : null;
formData.cashierLoanTime = formData.cashierLoanTime ? formatToDateTime(formData.cashierLoanTime, 'YYYY-MM-DD HH:mm:ss') : null;
//地区数据处理
formData.destination = formData.destination ? formData.destination[2] : '';
formData.departAddress = formData.departAddress ? formData.departAddress[2] : '';
if (formData.departId) {
let result = await getDepartName({ id: formData.departId });
formData.departName = result[0].departName;
}
setModalProps({ confirmLoading: true });
//提交表单
await saveOrUpdate(formData, isUpdate.value);
//关闭弹窗
closeModal();
//刷新列表
emit('success');
})
.catch((error) => {
console.log('error', error);
});
} finally {
setModalProps({ confirmLoading: false });
}
}
const labelCol = {
xs: { span: 24 },
sm: { span: 3 },
};
const wrapperCol = {
xs: { span: 24 },
sm: { span: 21 },
};
//初始化数据
const initData = {
applyUserName: userStore.getUserInfo?.username,
departId: '',
destination: '',
projectName: '',
departureTime: null,
plannedReturnTime: null,
actualReturnTime: null,
dayNum: 0,
travelExpensesType: '1',
departAddress: '',
travelTool: 1,
reason: '',
departLeaderAudit: '',
financeAudit: '',
cashierLoanAmount: '',
managerAudit: '',
};
/**
* 获取当前时间
*/
function nowTimes() {
let date = new Date();
let year = date.getFullYear();
let month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1;
let day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate();
return dayjs(year + '-' + month + '-' + day);
}
/**
* 计算天数
*/
function difference() {
let data = toRaw(unref(model));
if (data.departureTime && data.plannedReturnTime) {
let beginTime = formatToDateTime(data.departureTime, 'YYYY-MM-DD HH:mm:ss');
let endTime = formatToDateTime(data.plannedReturnTime, 'YYYY-MM-DD HH:mm:ss');
if (beginTime != '' && endTime != '') {
let dateBegin = new Date(beginTime);
let dateEnd = new Date(endTime);
let dateDiff = dateEnd.getTime() - dateBegin.getTime(); //时间差的毫秒数
let dayDiff = Math.floor(dateDiff / (24 * 3600 * 1000)); //计算出相差天数
if (dayDiff < 0) {
$message.createMessage.warning('结束时间不能小于开始时间');
model.dayNum = 0;
} else {
model.dayNum = dayDiff;
}
}
}
}
</script>
<style lang="less" scoped>
html[data-theme='light'] {
.ant-card {
color: rgba(0, 0, 0, 0.65);
background: #fff;
border: 1px solid #e8e8e8;
}
#staffCard {
border: 1px solid #fff;
box-shadow:
0 0 1px 1px #aaa,
3px 0 5px 0 #aaa,
0 4px 7px 0 #aaa;
}
#documentsIssuedTitle {
color: black;
}
#staffEvectionTable {
background-color: #fff;
border: 1px solid #000;
tr,
th {
border-bottom: 1px solid #000;
}
td,
th {
border-right: 1px solid #000;
border-bottom: 1px solid #000;
}
.input {
:deep(.ant-input) {
border: 0 solid black !important;
background-color: #fff;
}
}
.ant-input {
border: 0 solid black !important;
background-color: #fff;
}
.text {
background-color: #fff;
}
.firstTr {
color: #000;
}
.smallText .ant-input-number-input {
background-color: #fff;
}
.textArea {
border: 0 solid white;
}
:deep(.ant-input-number) {
border: 0 solid black !important;
}
}
/**去掉日期控件边框*/
.ant-picker {
border: 0 solid black !important;
}
}
html[data-theme='dark'] {
@borderColor: #3a3a3a;
#staffEvectionTable {
border: 1px solid @borderColor;
tr,
th {
border-bottom: 1px solid @borderColor;
}
td,
th {
border-right: 1px solid @borderColor;
border-bottom: 1px solid @borderColor;
}
}
}
.ant-card {
border-radius: 2px;
margin: 0 auto;
width: 1000px;
}
#staffCard {
margin-top: 10px;
margin-bottom: 10px;
border-radius: 5px;
height: auto;
}
#staffEvectionTitle {
margin-top: 1px;
font-weight: bold;
text-align: center;
display: block;
font-size: 24px;
}
#staffEvectionTable {
width: 100%;
font-size: 12px;
text-align: center;
tr td:last-child {
border-right: 0;
}
tr:last-child {
border-bottom: 0;
}
.fontiframe {
font-size: 12px;
}
.ant-form-item {
margin: 0;
}
.input {
:deep(.ant-input) {
border-radius: 0;
display: inherit;
}
}
.ant-input {
border-radius: 0;
display: inherit;
margin: 0;
width: 255px;
}
:deep(.ant-calendar-picker-input) {
border: none !important;
}
.text {
border: none !important;
border-radius: 0;
margin: 0;
width: 100%;
font-size: 12px;
}
.textArea {
resize: none;
box-shadow: none;
height: 98px;
width: 100%;
font-size: 12px;
border-radius: 0;
margin-bottom: 0;
}
.colfirst {
width: 90px;
}
.colfour {
width: 90px;
}
.firstTr {
width: 100px;
}
.radioGroup {
font-size: 12px;
}
.smallText .ant-input-number-input {
border: 0;
margin: 0;
}
:deep(.ant-input-number) {
border: 0 solid black !important;
border-radius: 0;
}
}
/**去掉地区控件边框、字号改小*/
.ant-select:not(.ant-select-customize-input) .ant-select-selector{
font-size: 12px;
border: 0 solid black !important;
}
</style>
@@ -0,0 +1,389 @@
<template>
<div>
<a-form ref="formRef" :model="model" :labelCol="labelCol" :wrapperCol="wrapperCol" :rules="validatorRules" style="padding-bottom: 10px">
<JFormContainer :disabled="true">
<a-card id="staffCard" class="ant-card">
<span id="documentsIssuedTitle">发文单</span>
<table border="1px" id="documentsIssueTable">
<tr>
<td class="firstTr">公文标题</td>
<td class="firstTr" colspan="2">
<a-form-item name="title">
<a-input class="text" v-model:value="model.title" />
</a-form-item>
</td>
<td class="firstTr">发文字号</td>
<td class="firstTr" colspan="2">
<a-form-item>
<a-input style="text-align: left" class="text" readOnly placeholder="< 系统自动生成 >" v-model:value="model.docCode" />
</a-form-item>
</td>
</tr>
<tr>
<td> 公文分类 </td>
<td colspan="2" style="width: 200px">
<a-form-item :wrapperCol="{ xs: { span: 20 } }">
<a-radio-group class="fontiframe" name="radioGroup" v-model:value="model.docType">
<a-radio class="radioGroup" value="1">普通文件</a-radio>
<a-radio class="radioGroup" value="2">盖章文件</a-radio>
<a-radio class="radioGroup" value="3">正式文件</a-radio>
</a-radio-group>
</a-form-item>
</td>
<td> 文种 </td>
<td colspan="2" style="width: 200px">
<a-form-item>
<a-select v-model:value="model.classification">
<a-select-option value="1">公告</a-select-option>
<a-select-option value="2">通知</a-select-option>
</a-select>
</a-form-item>
</td>
</tr>
<tr>
<td> 缓急程度 </td>
<td colspan="2">
<a-form-item :wrapperCol="{ xs: { span: 15 } }">
<a-radio-group class="fontiframe" name="radioGroup" v-model:value="model.urgency">
<a-radio class="radioGroup" value="1">普通</a-radio>
<a-radio class="radioGroup" value="2">特急</a-radio>
<a-radio class="radioGroup" value="3">紧急</a-radio>
</a-radio-group>
</a-form-item>
</td>
<td> 印刷份数 </td>
<td>
<a-form-item>
<a-input class="text" v-model:value="model.printScore" />
</a-form-item>
</td>
</tr>
<tr>
<td> 发文目标 </td>
<td colspan="2" style="width: 260px">
<a-form-item :wrapperCol="{ xs: { span: 23 } }">
<a-radio-group class="fontiframe" name="radioGroup" v-model:value="model.sendTarget">
<a-radio class="radioGroup" value="1">公司内</a-radio>
<a-radio class="radioGroup" value="2">公司外</a-radio>
<a-radio class="radioGroup" value="3">子公司</a-radio>
<a-radio class="radioGroup" value="4">主公司</a-radio>
</a-radio-group>
</a-form-item>
</td>
<td> 机密程度 </td>
<td colspan="2">
<a-form-item :wrapperCol="{ xs: { span: 20 } }">
<a-radio-group class="fontiframe" name="radioGroup" v-model:value="model.confidentiality">
<a-radio class="radioGroup" value="1">公开</a-radio>
<a-radio class="radioGroup" value="2">秘密</a-radio>
<a-radio class="radioGroup" value="3">机密</a-radio>
<a-radio class="radioGroup" value="4">绝密</a-radio>
</a-radio-group>
</a-form-item>
</td>
</tr>
<tr>
<td> 机关代字 </td>
<td colspan="2">
<a-form-item>
<a-input class="text" v-model:value="model.officeCode" />
</a-form-item>
</td>
<td> 排序码 </td>
<td colspan="2">
<a-form-item>
<a-input-number class="text" v-model:value="model.orderNo" />
</a-form-item>
</td>
</tr>
<tr>
<td> 主题词 </td>
<td colspan="5">
<a-form-item>
<a-textarea v-model:value="model.theme" class="textArea"></a-textarea>
</a-form-item>
</td>
</tr>
<tr>
<td> 收文人 </td>
<td colspan="5">
<a-form-item>
<a-textarea v-model:value="model.receiverName" class="textArea"></a-textarea>
</a-form-item>
</td>
</tr>
<tr style="height: 35px">
<td> 文件 </td>
<td colspan="5"> </td>
</tr>
<tr>
<td> 登记人 </td>
<td colspan="2">
<span>{{ nickname }}</span>
</td>
<td> 登记时间 </td>
<td colspan="2">
<a-form-item>
<a-date-picker class="input" placeholder="" format="YYYY-MM-DD" v-model:value="model.bookDate" />
</a-form-item>
</td>
</tr>
<tr>
<td> 成文日期 </td>
<td colspan="2">
<a-form-item>
<a-date-picker class="input" placeholder="" format="YYYY-MM-DD" v-model:value="model.writtenDate" />
</a-form-item>
</td>
<td> 审阅时间 </td>
<td colspan="2">
<a-form-item>
<a-date-picker class="input" placeholder="" format="YYYY-MM-DD" v-model:value="model.reviewDate" />
</a-form-item>
</td>
</tr>
</table>
</a-card>
</JFormContainer>
</a-form>
</div>
</template>
<script lang="ts" setup>
import { ref, computed, unref, reactive, toRaw } from 'vue';
import { useUserStore } from '/@/store/modules/user';
import JFormContainer from '/@/components/Form/src/jeecg/components/JFormContainer.vue';
import { queryById } from '../doc.send.api';
import dayjs from "dayjs";
// props声明
const props = defineProps({
formData: {
type: Object,
default: () => {},
},
});
const userStore = useUserStore();
const formRef = ref(null);
const nickname = ref(userStore.getUserInfo?.realname);
const model = reactive({
id: '',
title: '',
docCode: '',
docType: '1',
classification: '1',
urgency: '1',
printScore: 0,
sendTarget: '1',
confidentiality: '1',
officeCode: '',
orderNo: '',
theme: '',
receiverName: '',
bookDate: null,
writtenDate: null,
reviewDate: null,
});
const labelCol = {
xs: { span: 24 },
sm: { span: 5 },
};
const wrapperCol = {
xs: { span: 24 },
sm: { span: 24 },
};
async function initFormData() {
let res = await queryById({ id: props.formData.dataId });
console.log('DocSendForm获取流程节点信息', res);
if (res.success) {
let obj = res.result;
//表单赋值
Object.assign(model, { ...obj });
//时间格式化(升级antd3后,时间值不允许是字符串)
model.bookDate = model.bookDate?dayjs(model.bookDate,'YYYY-MM-DD'):null;
model.writtenDate = model.writtenDate?dayjs(model.writtenDate,'YYYY-MM-DD'):null;
model.reviewDate = model.reviewDate?dayjs(model.reviewDate,'YYYY-MM-DD'):null;
}
}
initFormData();
</script>
<style lang="less" scoped>
html[data-theme='light'] {
.ant-card {
color: rgba(0, 0, 0, 0.65);
background: #fff;
border: 1px solid #e8e8e8;
}
#staffCard {
border: 1px solid #fff;
box-shadow:
0 0 1px 1px #aaa,
3px 0 5px 0 #aaa,
0 4px 7px 0 #aaa;
}
#documentsIssuedTitle {
color: black;
}
#documentsIssueTable {
background-color: #fff;
border: 1px solid #000;
tr,
th {
border-bottom: 1px solid #000;
}
td,
th {
border-right: 1px solid #000;
border-bottom: 1px solid #000;
}
.input {
:deep(.ant-input) {
border: 0 solid black !important;
background-color: #fff;
}
}
.ant-input {
border: 0 solid black !important;
background-color: #fff;
}
.text {
background-color: #fff;
}
.firstTr {
color: #000;
}
.smallText .ant-input-number-input {
background-color: #fff;
}
.textArea {
border: 0 solid white;
}
}
/**去掉日期控件边框*/
.ant-picker {
border: 0 solid black !important;
}
}
html[data-theme='dark'] {
@borderColor: #3a3a3a;
#documentsIssueTable {
border: 1px solid @borderColor;
}
tr,
th {
border-bottom: 1px solid @borderColor;
}
td,
th {
border-right: 1px solid @borderColor;
border-bottom: 1px solid @borderColor;
}
}
.ant-card {
border-radius: 2px;
margin: 0 auto;
width: 1000px;
}
#staffCard {
margin-top: 10px;
margin-bottom: 10px;
border-radius: 5px;
height: auto;
}
#documentsIssuedTitle {
margin-top: 1px;
font-weight: bold;
text-align: center;
display: block;
font-size: 24px;
}
#documentsIssueTable {
width: 100%;
font-size: 12px;
text-align: center;
tr td:last-child {
border-right: 0;
}
tr:last-child {
border-bottom: 0;
}
.fontiframe {
font-size: 12px;
}
.ant-form-item {
margin: 0;
}
.input {
:deep(.ant-input) {
border-radius: 0;
display: inherit;
}
}
.ant-input {
border-radius: 0;
display: inherit;
margin: 0;
width: 255px;
}
:deep(.ant-calendar-picker-input) {
border: none !important;
}
.text {
border: none !important;
border-radius: 0;
margin: 0;
width: 100%;
font-size: 12px;
}
.textArea {
resize: none;
box-shadow: none;
height: 98px;
width: 100%;
font-size: 12px;
border-radius: 0;
margin-bottom: 0;
}
.colfirst {
width: 90px;
}
.colfour {
width: 90px;
}
.firstTr {
width: 100px;
}
.radioGroup {
font-size: 12px;
}
.smallText .ant-input-number-input {
border: 0;
margin: 0;
}
:deep(.ant-input-number) {
border-radius: 0;
}
}
</style>
@@ -0,0 +1,451 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" :title="title" @ok="handleSubmit" width="1200px">
<a-form ref="formRef" :model="model" :labelCol="labelCol" :wrapperCol="wrapperCol" :rules="validatorRules" style="padding-bottom: 10px">
<JFormContainer :disabled="formDisabled">
<a-card id="staffCard" class="ant-card">
<span id="documentsIssuedTitle">发文单</span>
<table border="1px" id="documentsIssueTable">
<tr>
<td class="firstTr">公文标题</td>
<td class="firstTr" colspan="2">
<a-form-item name="title">
<a-input class="text" v-model:value="model.title" />
</a-form-item>
</td>
<td class="firstTr">发文字号</td>
<td class="firstTr" colspan="2">
<a-form-item>
<a-input style="text-align: left" class="text" readOnly placeholder="< 系统自动生成 >" v-model:value="model.docCode" />
</a-form-item>
</td>
</tr>
<tr>
<td> 公文分类 </td>
<td colspan="2" style="width: 200px">
<a-form-item :wrapperCol="{ xs: { span: 20 } }">
<a-radio-group class="fontiframe" name="radioGroup" v-model:value="model.docType">
<a-radio class="radioGroup" value="1">普通文件</a-radio>
<a-radio class="radioGroup" value="2">盖章文件</a-radio>
<a-radio class="radioGroup" value="3">正式文件</a-radio>
</a-radio-group>
</a-form-item>
</td>
<td> 文种 </td>
<td colspan="2" style="width: 200px">
<a-form-item>
<a-select v-model:value="model.classification">
<a-select-option value="1">公告</a-select-option>
<a-select-option value="2">通知</a-select-option>
</a-select>
</a-form-item>
</td>
</tr>
<tr>
<td> 缓急程度 </td>
<td colspan="2">
<a-form-item :wrapperCol="{ xs: { span: 15 } }">
<a-radio-group class="fontiframe" name="radioGroup" v-model:value="model.urgency">
<a-radio class="radioGroup" value="1">普通</a-radio>
<a-radio class="radioGroup" value="2">特急</a-radio>
<a-radio class="radioGroup" value="3">紧急</a-radio>
</a-radio-group>
</a-form-item>
</td>
<td> 印刷份数 </td>
<td>
<a-form-item>
<a-input class="text" v-model:value="model.printScore" />
</a-form-item>
</td>
</tr>
<tr>
<td> 发文目标 </td>
<td colspan="2" style="width: 260px">
<a-form-item :wrapperCol="{ xs: { span: 23 } }">
<a-radio-group class="fontiframe" name="radioGroup" v-model:value="model.sendTarget">
<a-radio class="radioGroup" value="1">公司内</a-radio>
<a-radio class="radioGroup" value="2">公司外</a-radio>
<a-radio class="radioGroup" value="3">子公司</a-radio>
<a-radio class="radioGroup" value="4">主公司</a-radio>
</a-radio-group>
</a-form-item>
</td>
<td> 机密程度 </td>
<td colspan="2">
<a-form-item :wrapperCol="{ xs: { span: 20 } }">
<a-radio-group class="fontiframe" name="radioGroup" v-model:value="model.confidentiality">
<a-radio class="radioGroup" value="1">公开</a-radio>
<a-radio class="radioGroup" value="2">秘密</a-radio>
<a-radio class="radioGroup" value="3">机密</a-radio>
<a-radio class="radioGroup" value="4">绝密</a-radio>
</a-radio-group>
</a-form-item>
</td>
</tr>
<tr>
<td> 机关代字 </td>
<td colspan="2">
<a-form-item>
<a-input class="text" v-model:value="model.officeCode" />
</a-form-item>
</td>
<td> 排序码 </td>
<td colspan="2">
<a-form-item>
<a-input-number class="text" v-model:value="model.orderNo" />
</a-form-item>
</td>
</tr>
<tr>
<td> 主题词 </td>
<td colspan="5">
<a-form-item>
<a-textarea v-model:value="model.theme" class="textArea"></a-textarea>
</a-form-item>
</td>
</tr>
<tr>
<td> 收文人 </td>
<td colspan="5">
<a-form-item>
<a-textarea v-model:value="model.receiverName" class="textArea"></a-textarea>
</a-form-item>
</td>
</tr>
<tr style="height: 35px">
<td> 文件 </td>
<td colspan="5"> </td>
</tr>
<tr>
<td> 登记人 </td>
<td colspan="2">
<span>{{ nickname }}</span>
</td>
<td> 登记时间 </td>
<td colspan="2">
<a-form-item>
<a-date-picker class="input" placeholder="" format="YYYY-MM-DD" v-model:value="model.bookDate" />
</a-form-item>
</td>
</tr>
<tr>
<td> 成文日期 </td>
<td colspan="2">
<a-form-item>
<a-date-picker class="input" placeholder="" format="YYYY-MM-DD" v-model:value="model.writtenDate" />
</a-form-item>
</td>
<td> 审阅时间 </td>
<td colspan="2">
<a-form-item>
<a-date-picker class="input" placeholder="" format="YYYY-MM-DD" v-model:value="model.reviewDate" />
</a-form-item>
</td>
</tr>
</table>
</a-card>
</JFormContainer>
</a-form>
</BasicModal>
</template>
<script lang="ts" setup>
import { ref, computed, unref, reactive, toRaw } from 'vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { useMessage } from '/@/hooks/web/useMessage';
import { saveOrUpdate, getDepartName } from '../doc.send.api';
import { formatToDateTime } from '/@/utils/dateUtil';
import { useUserStore } from '/@/store/modules/user';
import JFormContainer from '/@/components/Form/src/jeecg/components/JFormContainer.vue';
import dayjs from "dayjs";
// Emits声明
const emit = defineEmits(['register', 'success']);
//提示弹窗
const $message = useMessage();
const userStore = useUserStore();
const isUpdate = ref(true);
const formRef = ref(null);
const nickname = ref(userStore.getUserInfo?.realname);
const model = reactive({
id: '',
title: '',
docCode: '',
docType: '1',
classification: '1',
urgency: '1',
printScore: 0,
sendTarget: '1',
confidentiality: '1',
officeCode: '',
orderNo: '',
theme: '',
receiverName: '',
bookDate: null,
writtenDate: null,
reviewDate: null,
});
//表单校验
const validatorRules = {
title: [{ required: true, message: '请输入公文标题!' }],
};
const formDisabled = ref(false);
//表单赋值
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
setModalProps({ confirmLoading: false, showOkBtn: !!!data?.isDetail });
isUpdate.value = !!data?.isUpdate;
if (unref(isUpdate)) {
let obj = { ...data.record };
Object.assign(model, { ...obj });
} else {
Object.assign(model, { ...initData });
}
//时间格式化(升级antd3后,时间值不允许是字符串)
model.bookDate = model.bookDate?dayjs(model.bookDate,'YYYY-MM-DD'):null;
model.writtenDate = model.writtenDate?dayjs(model.writtenDate,'YYYY-MM-DD'):null;
model.reviewDate = model.reviewDate?dayjs(model.reviewDate,'YYYY-MM-DD'):null;
// update-begin-author:taoyan date:2022-9-5 for: VUEN-2157 出差申请、借款申请、请假申请、公文申请都有这个问题,详情不让改
if(data.isDetail === true){
formDisabled.value = true;
}else{
formDisabled.value = false;
}
// update-end-author:taoyan date:2022-9-5 for: VUEN-2157 出差申请、借款申请、请假申请、公文申请都有这个问题,详情不让改
});
//设置标题
const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
//表单提交事件
function handleSubmit() {
try {
formRef.value
.validate()
.then(async () => {
let formData = toRaw(unref(model));
//时间格式化
formData.bookDate = formData.bookDate ? formatToDateTime(formData.bookDate, 'YYYY-MM-DD HH:mm:ss') : null;
formData.writtenDate = formData.writtenDate ? formatToDateTime(formData.writtenDate, 'YYYY-MM-DD HH:mm:ss') : null;
formData.reviewDate = formData.reviewDate ? formatToDateTime(formData.reviewDate, 'YYYY-MM-DD HH:mm:ss') : null;
setModalProps({ confirmLoading: true });
//提交表单
await saveOrUpdate(formData, isUpdate.value);
//关闭弹窗
closeModal();
//刷新列表
emit('success');
})
.catch((error) => {
console.log('error', error);
});
} finally {
setModalProps({ confirmLoading: false });
}
}
const labelCol = {
xs: { span: 24 },
sm: { span: 5 },
};
const wrapperCol = {
xs: { span: 24 },
sm: { span: 24 },
};
//初始化数据
const initData = {
title: '',
docCode: '',
docType: '1',
classification: '1',
urgency: '1',
printScore: 0,
sendTarget: '1',
confidentiality: '1',
officeCode: '',
orderNo: '',
theme: '',
receiverName: '',
bookDate: null,
writtenDate: null,
reviewDate: null,
};
</script>
<style lang="less" scoped>
html[data-theme='light'] {
.ant-card {
color: rgba(0, 0, 0, 0.65);
background: #fff;
border: 1px solid #e8e8e8;
}
#staffCard {
border: 1px solid #fff;
box-shadow:
0 0 1px 1px #aaa,
3px 0 5px 0 #aaa,
0 4px 7px 0 #aaa;
}
#documentsIssuedTitle {
color: black;
}
#documentsIssueTable {
background-color: #fff;
border: 1px solid #000;
tr,
th {
border-bottom: 1px solid #000;
}
td,
th {
border-right: 1px solid #000;
border-bottom: 1px solid #000;
}
.input {
:deep(.ant-input) {
border: 0 solid black !important;
background-color: #fff;
}
}
.ant-input {
border: 0 solid black !important;
background-color: #fff;
}
.text {
background-color: #fff;
}
.firstTr {
color: #000;
}
.smallText .ant-input-number-input {
background-color: #fff;
}
.textArea {
border: 0 solid white;
}
}
/**去掉日期控件边框*/
.ant-picker {
border: 0 solid black !important;
}
}
html[data-theme='dark'] {
@borderColor: #3a3a3a;
#documentsIssueTable {
border: 1px solid @borderColor;
}
tr,
th {
border-bottom: 1px solid @borderColor;
}
td,
th {
border-right: 1px solid @borderColor;
border-bottom: 1px solid @borderColor;
}
}
.ant-card {
border-radius: 2px;
margin: 0 auto;
width: 1000px;
}
#staffCard {
margin-top: 10px;
margin-bottom: 10px;
border-radius: 5px;
height: auto;
}
#documentsIssuedTitle {
margin-top: 1px;
font-weight: bold;
text-align: center;
display: block;
font-size: 24px;
}
#documentsIssueTable {
width: 100%;
font-size: 12px;
text-align: center;
tr td:last-child {
border-right: 0;
}
tr:last-child {
border-bottom: 0;
}
.fontiframe {
font-size: 12px;
}
.ant-form-item {
margin: 0;
}
.input {
:deep(.ant-input) {
border-radius: 0;
display: inherit;
}
}
.ant-input {
border-radius: 0;
display: inherit;
margin: 0;
width: 255px;
}
:deep(.ant-calendar-picker-input) {
border: none !important;
}
.text {
border: none !important;
border-radius: 0;
margin: 0;
width: 100%;
font-size: 12px;
}
.textArea {
resize: none;
box-shadow: none;
height: 98px;
width: 100%;
font-size: 12px;
border-radius: 0;
margin-bottom: 0;
}
.colfirst {
width: 90px;
}
.colfour {
width: 90px;
}
.firstTr {
width: 100px;
}
.radioGroup {
font-size: 12px;
}
.smallText .ant-input-number-input {
border: 0;
margin: 0;
}
:deep(.ant-input-number) {
border-radius: 0;
}
}
</style>
@@ -0,0 +1,54 @@
import { defHttp } from '/@/utils/http/axios';
import { Modal } from 'ant-design-vue';
enum Api {
list = '/joa/joaDocSending/list',
queryById = '/joa/joaDocSending/queryById',
save = '/joa/joaDocSending/add',
edit = '/joa/joaDocSending/edit',
deleteOne = '/joa/joaDocSending/delete',
deleteBatch = '/joa/joaDocSending/deleteBatch',
}
/**
* 列表
* @param params
*/
export const list = (params) => defHttp.get({ url: Api.list, params });
/**
* 根据id查询
* @param params
*/
export const queryById = (params) => defHttp.get({ url: Api.queryById, params }, { isTransformResponse: false });
/**
* 删除一个
*/
export const deleteOne = (params, handleSuccess) => {
return defHttp.delete({ url: Api.deleteOne, params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
};
/**
* 批量删除
* @param params
*/
export const batchDelete = (params, handleSuccess) => {
Modal.confirm({
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.delete({ url: Api.deleteBatch, data: params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
},
});
};
/**
* 保存或者更新
* @param params
*/
export const saveOrUpdate = (params, isUpdate) => {
let url = isUpdate ? Api.edit : Api.save;
return defHttp.post({ url: url, params });
};
@@ -0,0 +1,85 @@
import { FormSchema } from '/@/components/Table';
import { render } from '/@/utils/common/renderUtils';
export const columns = [
{
title: '公文标题',
dataIndex: 'title',
width: 100,
},
{
title: '文种',
dataIndex: 'classification',
width: 100,
customRender: ({ text }) => {
if (text == 1) {
return '公告';
} else if (text == 2) {
return '通知';
} else {
return text;
}
},
},
{
title: '公文分类',
dataIndex: 'docType',
width: 100,
customRender: ({ text }) => {
if (text == 1) {
return '普通文件';
} else if (text == 2) {
return '盖章通知';
} else if (text == 3) {
return '正式文件';
} else {
return text;
}
},
},
{
title: '缓急程度',
dataIndex: 'urgency',
width: 100,
customRender: ({ text }) => {
if (text == 1) {
return '普通';
} else if (text == 2) {
return '紧急';
} else if (text == 3) {
return '特急';
} else {
return text;
}
},
},
{
title: '印刷分数',
dataIndex: 'printScore',
width: 100,
},
{
title: '登记时间',
dataIndex: 'bookDate',
width: 100,
},
{
title: '流程状态',
align: 'center',
dataIndex: 'bpmStatus',
width: 100,
customRender: ({ text }) => {
return render.renderDict(text, 'bpm_status');
},
},
];
export const searchFormSchema: FormSchema[] = [
{
field: 'title',
label: '公文标题',
component: 'Input',
colProps: { span: 6 },
},
];
@@ -0,0 +1,179 @@
<template>
<BasicTable @register="registerTable" :rowSelection="rowSelection">
<template #tableTitle>
<a-button type="primary" preIcon="ant-design:plus-outlined" @click="handleCreate"> 新增</a-button>
<a-dropdown v-if="selectedRowKeys.length > 0">
<template #overlay>
<a-menu>
<a-menu-item key="1" @click="batchHandleDelete">
<Icon icon="ant-design:delete-outlined"></Icon>
删除
</a-menu-item>
</a-menu>
</template>
<a-button
>批量操作
<Icon icon="mdi:chevron-down"></Icon>
</a-button>
</a-dropdown>
</template>
<template #pcaSlot="{ text }">
{{ getAreaTextByCode(text) }}
</template>
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
</template>
</BasicTable>
<!--角色工单授权-->
<DocSendModal @register="registerModal" @success="reload" />
<BpmPictureModal @register="registerBpmModal" />
</template>
<script lang="ts" name="doc-send-list" setup>
import { ref } from 'vue';
import { BasicTable, TableAction } from '/@/components/Table';
import { useModal } from '/@/components/Modal';
import DocSendModal from './components/DocSendModal.vue';
import BpmPictureModal from '/@/views/super/bpm/process/manage/components/BpmPictureModal.vue';
import { columns, searchFormSchema } from './doc.send.data';
import { list, deleteOne, batchDelete } from './doc.send.api';
import { useListPage } from '/@/hooks/system/useListPage';
import { startProcess } from '/@/views/super/bpm/example/batch/leave.api';
import { useMessage } from '/@/hooks/web/useMessage';
const [registerModal, { openModal }] = useModal();
const [registerBpmModal, { openModal: bpmPicModal }] = useModal();
const { createMessage, createConfirm } = useMessage();
// 列表页面公共参数、方法
const { prefixCls, tableContext } = useListPage({
designScope: 'doc-send-list',
tableProps: {
api: list,
columns: columns,
canResize: false,
formConfig: {
schemas: searchFormSchema,
},
actionColumn: {
width: 120,
},
},
});
const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
const flowCode = 'joa_doc_send_01';
/**
* 新增
*/
function handleCreate() {
openModal(true, {
isUpdate: false,
});
}
/**
* 编辑
*/
function handleEdit(record: Recordable) {
openModal(true, {
record,
isUpdate: true,
});
}
/**
* 详情
*/
function handleDetail(record) {
openModal(true, {
record,
isUpdate: true,
isDetail: true,
});
}
/**
* 删除事件
*/
async function handleDelete(record) {
await deleteOne({ id: record.id }, reload);
}
/**
* 批量删除事件
*/
async function batchHandleDelete() {
await batchDelete({ ids: selectedRowKeys.value }, reload);
}
/**
* 提交流程
*/
function handleStartProcess(record) {
createConfirm({
iconType: 'warning',
title: '提示',
content: '确认提交流程吗?',
onOk: async () => {
let res = await startProcess({
flowCode: flowCode,
id: record.id,
formUrl: 'super/bpm/example/joa/docSend/components/DocSendForm',
formUrlMobile: 'applyform/docSend',
});
if (res.success) {
createMessage.success(res.message);
reload();
} else {
createMessage.warning(res.message);
}
},
});
}
/**
* 审批进度
*/
async function handlePreviewPic(record) {
bpmPicModal(true, {
flowCode,
dataId: record.id,
});
}
/**
* 操作栏
*/
function getTableAction(record) {
return [
{
label: '编辑',
onClick: handleEdit.bind(null, record),
ifShow: record.bpmStatus === '1',
},
{
label: '提交流程',
onClick: handleStartProcess.bind(null, record),
ifShow: record.bpmStatus === '1',
},
];
}
/**
* 下拉操作栏
*/
function getDropDownAction(record) {
return [
{
label: '详情',
onClick: handleDetail.bind(null, record),
},
{
label: '删除',
popConfirm: {
title: '是否确认删除',
confirm: handleDelete.bind(null, record),
},
ifShow: record.bpmStatus === '1',
},
{
label: '审批进度',
onClick: handlePreviewPic.bind(null, record),
ifShow: record.bpmStatus !== '1',
},
];
}
</script>
@@ -0,0 +1,368 @@
<template>
<a-form ref="formRef" :model="model" :labelCol="labelCol" :wrapperCol="wrapperCol">
<JFormContainer :disabled="true">
<a-card id="staffCard" class="ant-card">
<span id="staffLeaveTitle">员工请假单</span>
<div class="staffLeaveTableId" style="margin-bottom: 5px">
<a-form-item :labelCol="{ xs: { span: 24 }, sm: { span: 10 } }" :wrapperCol="{ xs: { span: 24 }, sm: { span: 10 } }" label="编号:">
<a-input class="fontiframe" style="border: none" readOnly v-model:value="model.applyNo" />
</a-form-item>
</div>
<table id="staffLeaveTable">
<tr class="tr-style">
<td class="firstTr">请假人</td>
<td class="firstTr">
<a-form-item>
<span class="fontiframe">{{ nickname }}</span>
</a-form-item>
</td>
<td class="firstTr">部门</td>
<td class="firstTr">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol">
<span class="fontiframe">{{ model.department }}</span>
</a-form-item>
</td>
<td class="firstTr">职务</td>
<td class="firstTr">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol">
<span class="fontiframe">{{ model.duty }}</span>
</a-form-item>
</td>
</tr>
<tr class="tr-style">
<td colspan="6">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" style="font-size: 12px" label="请假类别:">
<a-radio-group class="fontiframe" name="radioGroup" v-model:value="model.leaveCategory">
<template v-for="(item, index) in leaveCategoryOpt" :key="index">
<a-radio class="radioGroup" :value="item.value">{{ item.label }}</a-radio>
</template>
</a-radio-group>
</a-form-item>
</td>
</tr>
<tr>
<td colspan="6">
<a-form-item label="请假事由:">
<a-textarea v-model:value="model.leaveReason" class="textArea"></a-textarea>
</a-form-item>
</td>
</tr>
<tr class="tr-style">
<td colspan="6">
<a-form-item class="fontiframe lineHeight" label="请假时间:">
(
<a-date-picker class="input" format="YYYY-MM-DD" v-model:value="model.leaveStartDate" :allowClear="false" />
) (
<a-date-picker class="input" format="YYYY-MM-DD" v-model:value="model.leaveEndDate" :allowClear="false" />
) 总共请<a-input-number class="smallText" v-model:value="model.total" size="small" :min="0" /><br />
<span class="fontiframe" style="color: #f00; position: relative; right: 86px"
>1.请假半天可以写0.5不能写0.1,0.2等小数2.全天假以00:00:00开始以23:59:59结束,下午请假以12:00:00开始</span
>
</a-form-item>
</td>
</tr>
<tr class="tr-style">
<td colspan="3">
<a-form-item :labelCol="{ xs: { span: 24 }, sm: { span: 10 } }" :wrapperCol="wrapperCol" label="休息期间联系方式:">
<a-input class="text" v-model:value="model.contactWay" />
</a-form-item>
</td>
<td colspan="3">
<a-form-item :labelCol="{ xs: { span: 24 }, sm: { span: 11 } }" :wrapperCol="wrapperCol" label="休息期间应急工作委托人:">
<a-input class="text" v-model:value="model.dutyDeputy" />
</a-form-item>
</td>
</tr>
<tr style="height: 100px; line-height: 100px">
<td colspan="3">
<a-form-item :labelCol="{ xs: { span: 24 }, sm: { span: 10 } }" :wrapperCol="wrapperCol" label="部门主管(经理)意见:">
<div style="display: flex; margin-top: 20px; height: 80px">
<div>{{ model.leaderApproval }}</div>
<div class="fontiframe" style="position: absolute; bottom: 10px">负责人:</div>
</div>
</a-form-item>
</td>
<td colspan="3">
<a-form-item :labelCol="{ xs: { span: 24 }, sm: { span: 11 } }" :wrapperCol="wrapperCol" label="人力资源部(行政办)意见:">
<div style="display: flex; margin-top: 20px; height: 80px">
<div class="fontiframe">{{ model.hrPrincipalApproval }}</div
><br />
<div class="fontiframe" style="position: absolute; bottom: 10px">负责人:</div>
</div>
</a-form-item>
</td>
</tr>
<tr>
<td colspan="3">
<a-form-item :labelCol="{ xs: { span: 24 }, sm: { span: 10 } }" :wrapperCol="wrapperCol" label="总经理意见:">
<div style="display: flex; height: 100px">
<div class="fontiframe" style="margin-top: 30px">{{ model.deptPrincipalApproval }}</div>
<div class="fontiframe" style="position: absolute; bottom: 10px">总经理:</div>
</div>
</a-form-item>
</td>
<td colspan="3" style="text-align: left">
<p class="fontiframe">
说明<br />
1.返回公司报到时间为销假时间<br />
2.所有员工3天及以上请假需总经理批准<br />
3.本表存人力资源部(行政办)备案<br />
</p>
</td>
</tr>
</table>
</a-card>
<a-form-item hidden>
<a-date-picker class="input" placeholder="" format="YYYY-MM-DD" disabled v-model:value="model.applyDate" />
</a-form-item>
</JFormContainer>
</a-form>
</template>
<script lang="ts" setup>
import { ref, unref, reactive, toRaw } from 'vue';
import { queryById } from '../leave.api';
import { useUserStore } from '/@/store/modules/user';
import JFormContainer from '/@/components/Form/src/jeecg/components/JFormContainer.vue';
import { leaveCategoryOpt } from '../leave.data';
import dayjs from "dayjs";
// props声明
const props = defineProps({
formData: {
type: Object,
default: () => {},
},
});
const userStore = useUserStore();
const nickname = ref(userStore.getUserInfo?.realname);
const model = reactive({
applyNo: '',
department: '',
duty: '',
leaveReason: '',
leaveStartDate: null,
leaveEndDate: null,
leaveCategory: '1',
total: 0,
contactWay: '',
dutyDeputy: '',
leaderApproval: '',
hrPrincipalApproval: '',
deptPrincipalApproval: '',
applyDate: null,
});
const labelCol = {
xs: { span: 24 },
sm: { span: 3 },
};
const wrapperCol = {
xs: { span: 24 },
sm: { span: 21 },
};
async function initFormData() {
let res = await queryById({ id: props.formData.dataId });
console.log('LeaveForm获取流程节点信息', res);
if (res.success) {
//表单赋值
Object.assign(model, { ...res.result });
//时间格式化(antd3升级后,时间值不允许是字符串)
model.leaveStartDate = model.leaveStartDate?dayjs(model.leaveStartDate,'YYYY-MM-DD'):null;
model.leaveEndDate = model.leaveEndDate?dayjs(model.leaveEndDate,'YYYY-MM-DD'):null;
model.applyDate = model.applyDate?dayjs(model.applyDate,'YYYY-MM-DD'):null;
}
}
initFormData();
</script>
<style lang="less" scoped>
html[data-theme='light'] {
.ant-card {
color: rgba(0, 0, 0, 0.65);
background: #fff;
border: 1px solid #e8e8e8;
}
#staffCard {
border: 1px solid #fff;
box-shadow:
0 0 1px 1px #aaa,
3px 0 5px 0 #aaa,
0 4px 7px 0 #aaa;
}
#staffLeaveTitle {
color: black;
}
#staffLeaveTable {
background-color: #fff;
border: 1px solid #000;
tr,
th {
border-bottom: 1px solid #000;
}
td,
th {
border-right: 1px solid #000;
border-bottom: 1px solid #000;
}
.input {
:deep(.ant-input) {
border: 0 solid black !important;
background-color: #fff;
}
}
.ant-input {
border: 0 solid black !important;
background-color: #fff;
}
.text {
background-color: #fff;
}
.firstTr {
color: #000;
}
.smallText .ant-input-number-input {
background-color: #fff;
}
.textArea {
border: 0 solid white;
}
}
/**去掉日期控件边框*/
.ant-picker {
border: 0 solid black !important;
}
}
html[data-theme='dark'] {
@borderColor: #3a3a3a;
#staffLeaveTable {
border: 1px solid @borderColor;
}
tr,
th {
border-bottom: 1px solid @borderColor;
}
td,
th {
border-right: 1px solid @borderColor;
border-bottom: 1px solid @borderColor;
}
}
.ant-card {
border-radius: 2px;
margin: 0 auto;
width: 750px;
}
#staffCard {
margin-top: 10px;
margin-bottom: 10px;
border-radius: 5px;
height: auto;
}
#staffLeaveTitle {
margin-top: 1px;
font-weight: bold;
text-align: center;
display: block;
font-size: 24px;
}
#staffLeaveTable {
width: 100%;
font-size: 12px;
text-align: center;
.tr-style {
height: 50px;
line-height: 50px;
}
tr td:last-child {
border-right: 0;
}
tr:last-child {
border-bottom: 0;
}
.fontiframe {
font-size: 12px;
}
.ant-form-item {
margin: 0;
}
.input {
:deep(.ant-input) {
border-radius: 0;
display: inherit;
}
}
.ant-input {
border-radius: 0;
display: inherit;
margin: 0;
width: 255px;
}
:deep(.ant-calendar-picker-input) {
border: none !important;
font-size: 12px;
}
:deep(.ant-input-number-sm) {
border: none !important;
font-size: 12px;
}
.text {
border: none !important;
border-radius: 0;
margin: 0;
width: 100%;
font-size: 12px;
}
.firstTr {
width: 16%;
.ant-form-item-control-wrapper {
width: 50%;
}
}
.smallText .ant-input-number-input {
border: 0;
margin: 0;
}
.lineHeight .ant-form-item-control {
line-height: 0;
}
.textArea {
resize: none;
height: 118px;
font-size: 12px;
border-radius: 0;
margin-bottom: 0;
width: 100%;
}
}
.staffLeaveTableId {
margin-right: 87px;
float: right;
height: 35px;
}
.ant-form label {
font-size: 12px;
}
:deep(.ant-form-item-label label) {
font-size: 12px;
}
</style>
@@ -0,0 +1,468 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" :bodyStyle="{ height: '720px' }" :title="title" @ok="handleSubmit" width="950px">
<a-form ref="formRef" :model="model" :labelCol="labelCol" :wrapperCol="wrapperCol">
<JFormContainer :disabled="formDisabled">
<a-card id="staffCard" class="ant-card">
<span id="staffLeaveTitle">员工请假单</span>
<div class="staffLeaveTableId">
<a-form-item :labelCol="{ xs: { span: 24 }, sm: { span: 10 } }" :wrapperCol="{ xs: { span: 24 }, sm: { span: 10 } }" label="编号:">
<a-input class="fontiframe" style="border: none" readOnly v-model:value="model.applyNo" />
</a-form-item>
</div>
<table id="staffLeaveTable">
<tr class="tr-style">
<td class="firstTr">请假人</td>
<td class="firstTr">
<a-form-item>
<span class="fontiframe">{{ nickname }}</span>
</a-form-item>
</td>
<td class="firstTr">部门</td>
<td class="firstTr">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol">
<span class="fontiframe">{{ model.department }}</span>
</a-form-item>
</td>
<td class="firstTr">职务</td>
<td class="firstTr">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol">
<span class="fontiframe">{{ model.duty }}</span>
</a-form-item>
</td>
</tr>
<tr class="tr-style">
<td colspan="6">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" style="font-size: 12px" label="请假类别:">
<a-radio-group class="fontiframe" name="radioGroup" v-model:value="model.leaveCategory">
<template v-for="(item, index) in leaveCategoryOpt" :key="index">
<a-radio class="radioGroup" :value="item.value">{{ item.label }}</a-radio>
</template>
</a-radio-group>
</a-form-item>
</td>
</tr>
<tr>
<td colspan="6">
<a-form-item label="请假事由:">
<a-textarea v-model:value="model.leaveReason" class="textArea"></a-textarea>
</a-form-item>
</td>
</tr>
<tr class="tr-style">
<td colspan="6">
<a-form-item class="fontiframe lineHeight" label="请假时间:">
(
<a-date-picker class="input" format="YYYY-MM-DD" @change="dateChange" v-model:value="model.leaveStartDate" :allowClear="false" />
) (
<a-date-picker class="input" format="YYYY-MM-DD" @change="dateChange" v-model:value="model.leaveEndDate" :allowClear="false" />
) 总共请<a-input-number class="smallText" v-model:value="model.total" size="small" :min="0" /><br />
<span class="fontiframe" style="display:none; color: #f00; position: relative; right: 86px"
>1.请假半天可以写0.5不能写0.1,0.2等小数2.全天假以00:00:00开始以23:59:59结束,下午请假以12:00:00开始</span
>
</a-form-item>
</td>
</tr>
<tr class="tr-style">
<td colspan="3">
<a-form-item :labelCol="{ xs: { span: 24 }, sm: { span: 10 } }" :wrapperCol="wrapperCol" label="休息期间联系方式:">
<a-input class="text" v-model:value="model.contactWay" />
</a-form-item>
</td>
<td colspan="3">
<a-form-item :labelCol="{ xs: { span: 24 }, sm: { span: 11 } }" :wrapperCol="wrapperCol" label="休息期间应急工作委托人:">
<a-input class="text" v-model:value="model.dutyDeputy" />
</a-form-item>
</td>
</tr>
<tr style="height: 100px; line-height: 100px">
<td colspan="3">
<a-form-item :labelCol="{ xs: { span: 24 }, sm: { span: 10 } }" :wrapperCol="wrapperCol" label="部门主管(经理)意见:">
<div style="display: flex; margin-top: 20px; height: 80px">
<div>{{ model.leaderApproval }}</div>
<div class="fontiframe" style="position: absolute; bottom: 10px">负责人:</div>
</div>
</a-form-item>
</td>
<td colspan="3">
<a-form-item :labelCol="{ xs: { span: 24 }, sm: { span: 11 } }" :wrapperCol="wrapperCol" label="人力资源部(行政办)意见:">
<div style="display: flex; margin-top: 20px; height: 80px">
<div class="fontiframe">{{ model.hrPrincipalApproval }}</div
><br />
<div class="fontiframe" style="position: absolute; bottom: 10px">负责人:</div>
</div>
</a-form-item>
</td>
</tr>
<tr>
<td colspan="3">
<a-form-item :labelCol="{ xs: { span: 24 }, sm: { span: 10 } }" :wrapperCol="wrapperCol" label="总经理意见:">
<div style="display: flex; height: 100px">
<div class="fontiframe" style="margin-top: 30px">{{ model.deptPrincipalApproval }}</div>
<div class="fontiframe" style="position: absolute; bottom: 10px">总经理:</div>
</div>
</a-form-item>
</td>
<td colspan="3" style="text-align: left">
<p class="fontiframe">
说明<br />
1.返回公司报到时间为销假时间<br />
2.所有员工3天及以上请假需总经理批准<br />
3.本表存人力资源部(行政办)备案<br />
</p>
</td>
</tr>
</table>
</a-card>
</JFormContainer>
<a-form-item hidden>
<a-date-picker class="input" placeholder="" format="YYYY-MM-DD" disabled v-model:value="model.applyDate" />
</a-form-item>
</a-form>
</BasicModal>
</template>
<script lang="ts" setup>
import { ref, computed, unref, reactive, toRaw } from 'vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { useMessage } from '/@/hooks/web/useMessage';
import { saveOrUpdate } from '../leave.api';
import { leaveCategoryOpt } from '../leave.data';
import { formatToDateTime } from '/@/utils/dateUtil';
import { useUserStore } from '/@/store/modules/user';
import JFormContainer from '/@/components/Form/src/jeecg/components/JFormContainer.vue';
import {string} from "vue-types";
import dayjs from 'dayjs';
// Emits声明
const emit = defineEmits(['register', 'success']);
//提示弹窗
const $message = useMessage();
const userStore = useUserStore();
const isUpdate = ref(true);
const nickname = ref(userStore.getUserInfo?.realname);
const model = reactive({
applyNo: '',
department: '',
duty: '',
leaveReason: '',
leaveCategory: '1',
leaveStartDate: '',
leaveEndDate: '',
total: 0,
contactWay: '',
dutyDeputy: '',
leaderApproval: '',
hrPrincipalApproval: '',
deptPrincipalApproval: '',
applyDate: null,
});
const formDisabled = ref(false);
//表单赋值
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
setModalProps({ confirmLoading: false, showOkBtn: !!!data?.isDetail });
isUpdate.value = !!data?.isUpdate;
if (unref(isUpdate)) {
Object.assign(model, { ...data.record });
} else {
Object.assign(model, { ...initData });
}
//时间初始化(antd3升级后,时间值不允许是字符串)
model.applyDate = model.applyDate ? dayjs(model.applyDate) : null;
model.leaveStartDate = model.leaveStartDate ? dayjs(model.leaveStartDate) : null;
model.leaveEndDate = model.leaveEndDate ? dayjs(model.leaveEndDate) : null;
// update-begin-author:taoyan date:2022-9-5 for: VUEN-2157 出差申请、借款申请、请假申请、公文申请都有这个问题,详情不让改
if(data.isDetail === true){
formDisabled.value = true;
}else{
formDisabled.value = false;
}
// update-end-author:taoyan date:2022-9-5 for: VUEN-2157 出差申请、借款申请、请假申请、公文申请都有这个问题,详情不让改
});
//设置标题
const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
//表单提交事件
async function handleSubmit() {
try {
let formData = toRaw(unref(model));
if (!model.leaveStartDate) {
$message.createMessage.warning('请假开始时间不能是空');
return false;
}
if (!model.leaveEndDate) {
$message.createMessage.warning('请假结束时间不能是空');
return false;
}
if (model.total == 0) {
$message.createMessage.warning('请假天数不能是0天');
return false;
}
if (model.total == '' || model.total == undefined || model.total == null) {
$message.createMessage.warning('请假天数不能是空');
return false;
}
//时间格式化
formData.applyDate = formData.applyDate ? formatToDateTime(formData.applyDate, 'YYYY-MM-DD HH:mm:ss') : null;
formData.leaveStartDate = formData.leaveStartDate ? formatToDateTime(formData.leaveStartDate, 'YYYY-MM-DD HH:mm:ss') : null;
formData.leaveEndDate = formData.leaveEndDate ? formatToDateTime(formData.leaveEndDate, 'YYYY-MM-DD HH:mm:ss') : null;
setModalProps({ confirmLoading: true });
//提交表单
await saveOrUpdate(formData, isUpdate.value);
//关闭弹窗
closeModal();
//刷新列表
emit('success');
} finally {
setModalProps({ confirmLoading: false });
}
}
const labelCol = {
xs: { span: 24 },
sm: { span: 3 },
};
const wrapperCol = {
xs: { span: 24 },
sm: { span: 21 },
};
const initData = {
id: '',
applyNo: '',
name: userStore.getUserInfo?.username,
department: '系统管理部',
duty: '普通员工',
leaveReason: '',
leaveStartDate: null,
leaveEndDate: null,
leaveCategory: '1',
total: 0,
contactWay: '',
dutyDeputy: '',
leaderApproval: '',
hrPrincipalApproval: '',
deptPrincipalApproval: '小于三天无需总经理批准',
applyDate: nowTimes()
};
function nowTimes() {
let date = new Date();
let year = date.getFullYear();
let month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1;
let day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate();
return dayjs(year + '-' + month + '-' + day);
}
//update-begin-author:taoyan date:2022-9-5 for: VUEN-2157 4、请假申请,只需要年月日不需要时分秒,默认计算请假天数
function dateChange(){
let { leaveEndDate, leaveStartDate} = model;
if(leaveEndDate && leaveStartDate){
let leaveStartDateTemp = dayjs(leaveStartDate,'YYYY-MM-DD');
let leaveEndDateTemp = dayjs(leaveEndDate,'YYYY-MM-DD');
let temp = leaveEndDateTemp.diff(leaveStartDateTemp, 'days');
if(temp === 0){
model.total = 1
}else if(temp && temp > 0){
model.total = temp + 1;
}else{
model.total = 0;
}
}
}
//update-end-author:taoyan date:2022-9-5 for: VUEN-2157 4、请假申请,只需要年月日不需要时分秒,默认计算请假天数
</script>
<style lang="less" scoped>
html[data-theme='light'] {
.ant-card {
color: rgba(0, 0, 0, 0.65);
background: #fff;
border: 1px solid #e8e8e8;
}
#staffCard {
border: 1px solid #fff;
box-shadow:
0 0 1px 1px #aaa,
3px 0 5px 0 #aaa,
0 4px 7px 0 #aaa;
}
#staffLeaveTitle {
color: black;
}
#staffLeaveTable {
background-color: #fff;
border: 1px solid #000;
tr,
th {
border-bottom: 1px solid #000;
}
td,
th {
border-right: 1px solid #000;
border-bottom: 1px solid #000;
}
.input {
:deep(.ant-input) {
border: 0 solid black !important;
background-color: #fff;
}
}
.ant-input {
border: 0 solid black !important;
background-color: #fff;
}
.firstTr {
color: #000;
}
.smallText .ant-input-number-input {
background-color: #fff;
}
.textArea {
border: 0 solid white;
}
}
/**去掉日期控件边框*/
.ant-picker {
border: 0 solid black !important;
}
}
html[data-theme='dark'] {
@borderColor: #3a3a3a;
#staffLeaveTable {
border: 1px solid @borderColor;
}
tr,
th {
border-bottom: 1px solid @borderColor;
}
td,
th {
border-right: 1px solid @borderColor;
border-bottom: 1px solid @borderColor;
}
}
.ant-card {
border-radius: 2px;
margin: 0 auto;
width: 750px;
height: 700px;
}
#staffCard {
margin-top: 10px;
margin-bottom: 10px;
border-radius: 5px;
height: auto;
}
#staffLeaveTitle {
margin-top: 1px;
font-weight: bold;
text-align: center;
display: block;
font-size: 24px;
}
#staffLeaveTable {
width: 100%;
font-size: 12px;
text-align: center;
.tr-style {
height: 50px;
line-height: 50px;
}
tr td:last-child {
border-right: 0;
}
tr:last-child {
border-bottom: 0;
}
.fontiframe {
font-size: 12px;
}
.ant-form-item {
margin: 0;
}
.input {
:deep(.ant-input) {
border-radius: 0;
display: inherit;
}
}
.ant-input {
border-radius: 0;
display: inherit;
margin: 0;
width: 255px;
font-size: 12px;
}
:deep(.ant-calendar-picker-input) {
border: none !important;
font-size: 12px;
}
:deep(.ant-input-number-sm) {
border: none !important;
font-size: 12px;
}
.text {
border: none !important;
border-radius: 0;
margin: 0;
width: 100%;
font-size: 12px;
}
.firstTr {
width: 16%;
.ant-form-item-control-wrapper {
width: 50%;
}
}
.smallText .ant-input-number-input {
border: 0;
margin: 0;
}
.lineHeight .ant-form-item-control {
line-height: 0;
}
.textArea {
resize: none;
box-shadow: none;
height: 118px;
font-size: 12px;
border-radius: 0;
margin-bottom: 0;
width: 100%;
}
}
.staffLeaveTableId {
margin-right: 87px;
float: right;
height: 35px;
}
.ant-form label {
font-size: 12px;
}
:deep(.ant-form-item-label label) {
font-size: 12px;
}
</style>
@@ -0,0 +1,60 @@
import { defHttp } from '/@/utils/http/axios';
import { Modal } from 'ant-design-vue';
enum Api {
list = '/joa/joaEmployeeLeave/list',
taskList = '/joa/joaEmployeeLeave/taskList',
queryById = '/joa/joaEmployeeLeave/queryById',
save = '/joa/joaEmployeeLeave/add',
edit = '/joa/joaEmployeeLeave/edit',
deleteOne = '/joa/joaEmployeeLeave/delete',
deleteBatch = '/joa/joaEmployeeLeave/deleteBatch',
}
/**
* 列表
* @param params
*/
export const list = (params) => defHttp.get({ url: Api.list, params });
/**
* 任务列表
* @param params
*/
export const taskList = (params) => defHttp.get({ url: Api.taskList, params });
/**
* 根据id查询
* @param params
*/
export const queryById = (params) => defHttp.get({ url: Api.queryById, params }, { isTransformResponse: false });
/**
* 删除一个
*/
export const deleteOne = (params, handleSuccess) => {
return defHttp.delete({ url: Api.deleteOne, params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
};
/**
* 批量删除
* @param params
*/
export const batchDelete = (params, handleSuccess) => {
Modal.confirm({
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.delete({ url: Api.deleteBatch, data: params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
},
});
};
/**
* 保存或者更新
* @param params
*/
export const saveOrUpdate = (params, isUpdate) => {
let url = isUpdate ? Api.edit : Api.save;
return defHttp.post({ url: url, params });
};
@@ -0,0 +1,116 @@
import { FormSchema } from '/@/components/Table';
import { render } from '/@/utils/common/renderUtils';
export const leaveCategoryOpt = [
{
label: '病假',
value: '1',
key: '1',
},
{
label: '事假',
value: '2',
key: '2',
},
{
label: '年假',
value: '3',
key: '3',
},
{
label: '婚假',
value: '4',
key: '4',
},
{
label: '产假',
value: '5',
key: '5',
},
{
label: '丧假',
value: '6',
key: '6',
},
{
label: '探亲假',
value: '7',
key: '7',
},
{
label: '护理假',
value: '8',
key: '8',
},
{
label: '其他',
value: '9',
key: '9',
},
];
export const columns = [
{
title: '申请编号',
dataIndex: 'applyNo',
width: 100,
},
{
title: '名称',
dataIndex: 'name',
width: 100,
},
{
title: '申请日期',
dataIndex: 'applyDate',
width: 100,
},
{
title: '请假类别',
dataIndex: 'leaveCategory',
width: 100,
customRender: ({ text }) => {
let item = leaveCategoryOpt.filter((t) => t.value == text);
if (item && item.length > 0) {
return item[0].label;
} else {
return text;
}
},
},
{
title: '请假开始时间',
dataIndex: 'leaveStartDate',
width: 100,
},
{
title: '请假结束时间',
dataIndex: 'leaveEndDate',
width: 100,
},
{
title: '流程状态',
align: 'center',
dataIndex: 'bpmStatus',
width: 100,
customRender: ({ text }) => {
return render.renderDict(text, 'bpm_status');
},
},
];
export const searchFormSchema: FormSchema[] = [
{
field: 'name',
label: '名称',
component: 'Input',
colProps: { span: 6 },
},
{
field: 'leaveCategory',
label: '请假类别',
component: 'Select',
componentProps: {
options: leaveCategoryOpt,
},
colProps: { span: 6 },
},
];
@@ -0,0 +1,130 @@
<template>
<BasicTable @register="registerTable">
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" />
</template>
</BasicTable>
<!--角色工单授权-->
<LeaveModal @register="registerModal" @success="reload" />
<BpmPictureModal @register="registerBpmModal" />
<!--业务办理弹窗-->
<BpmBizTaskDealModal ref="taskDealModal" :path="path" :formData="formData" @ok="taskOk"></BpmBizTaskDealModal>
</template>
<script lang="ts" name="leave-list" setup>
import { ref } from 'vue';
import { BasicTable, TableAction } from '/@/components/Table';
import { useModal } from '/@/components/Modal';
import { useListPage } from '/@/hooks/system/useListPage';
import { useMessage } from '/@/hooks/web/useMessage';
import LeaveModal from './components/LeaveModal.vue';
import BpmPictureModal from '/@/views/super/bpm/process/manage/components/BpmPictureModal.vue';
import BpmBizTaskDealModal from '/@/views/super/bpm/example/batch/components/BpmBizTaskDealModal.vue';
import { columns, searchFormSchema } from './leave.data';
import { taskList } from './leave.api';
import { claim, getBizProcessNodeInfo } from '/@/views/super/bpm/process/manage/components/bpm.api.ts';
const [registerModal, { openModal }] = useModal();
const [registerBpmModal, { openModal: bpmPicModal }] = useModal();
const { createMessage, createConfirm } = useMessage();
// 列表页面公共参数、方法
const { prefixCls, tableContext } = useListPage({
designScope: 'leave-list',
tableProps: {
api: taskList,
columns: columns,
canResize: false,
formConfig: {
schemas: searchFormSchema,
},
actionColumn: {
width: 120,
},
},
});
const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
const flowCode = 'joa_leave_01';
const path = ref('');
const formData = ref({});
const taskDealModal = ref(null);
/**
* 签收
*/
function handleClaim(record) {
createConfirm({
iconType: 'warning',
title: '确认签收吗',
content: '是否签收该任务?',
onOk: async () => {
let res = await claim({ taskId: record.taskId });
if (res.success) {
createMessage.success(res.message);
} else {
createMessage.warning(res.message);
}
reload();
},
});
}
/**
* 办理
*/
async function handleProcess(record) {
let res = await getBizProcessNodeInfo({ flowCode: flowCode, dataId: record.id });
if (res.success) {
console.log('获取流程节点信息', res);
console.log('表单数据', res.result.records);
let data = {
dataId: res.result.dataId,
taskId: res.result.taskId,
flowCode: flowCode,
taskDefKey: res.result.taskDefKey,
procInsId: res.result.procInsId,
tableName: res.result.tableName,
permissionList: res.result.permissionList,
bizTaskList: res.result.bizTaskList,
vars: res.result.records,
};
formData.value = data;
path.value = res.result.formUrl;
console.log('------获取流程节点信息>>', data);
console.log('------流程表单地址>>', path.value);
taskDealModal.value.deal(data);
}
}
function taskOk() {
reload();
}
/**
* 审批进度
*/
function handlePreviewPic(record) {
bpmPicModal(true, {
flowCode,
dataId: record.id,
});
}
/**
* 操作栏
*/
function getTableAction(record) {
return [
{
label: '签收',
onClick: handleClaim.bind(null, record),
ifShow: !!!record.assignee,
},
{
label: '办理',
onClick: handleProcess.bind(null, record),
ifShow: !!record.assignee,
},
{
label: '审批进度',
onClick: handlePreviewPic.bind(null, record),
},
];
}
</script>
@@ -0,0 +1,176 @@
<template>
<BasicTable @register="registerTable" :rowSelection="rowSelection">
<template #tableTitle>
<a-button type="primary" preIcon="ant-design:plus-outlined" @click="handleCreate"> 新增</a-button>
<a-dropdown v-if="selectedRowKeys.length > 0">
<template #overlay>
<a-menu>
<a-menu-item key="1" @click="batchHandleDelete">
<Icon icon="ant-design:delete-outlined"></Icon>
删除
</a-menu-item>
</a-menu>
</template>
<a-button
>批量操作
<Icon icon="mdi:chevron-down"></Icon>
</a-button>
</a-dropdown>
</template>
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
</template>
</BasicTable>
<!--角色工单授权-->
<LeaveModal @register="registerModal" @success="reload" />
<BpmPictureModal @register="registerBpmModal" />
</template>
<script lang="ts" name="leave-list" setup>
import { ref } from 'vue';
import { BasicTable, TableAction } from '/@/components/Table';
import { useModal } from '/@/components/Modal';
import LeaveModal from './components/LeaveModal.vue';
import BpmPictureModal from '/@/views/super/bpm/process/manage/components/BpmPictureModal.vue';
import { columns, searchFormSchema } from './leave.data';
import { list, deleteOne, batchDelete } from './leave.api';
import { useListPage } from '/@/hooks/system/useListPage';
import { startProcess } from '/@/views/super/bpm/example/batch/leave.api.ts';
import { useMessage } from '/@/hooks/web/useMessage';
const [registerModal, { openModal }] = useModal();
const [registerBpmModal, { openModal: bpmPicModal }] = useModal();
const { createMessage, createConfirm } = useMessage();
// 列表页面公共参数、方法
const { prefixCls, tableContext } = useListPage({
designScope: 'leave-list',
tableProps: {
api: list,
columns: columns,
canResize: false,
formConfig: {
schemas: searchFormSchema,
},
actionColumn: {
width: 120,
},
},
});
const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
const flowCode = 'joa_leave_01';
/**
* 新增
*/
function handleCreate() {
openModal(true, {
isUpdate: false,
});
}
/**
* 编辑
*/
function handleEdit(record: Recordable) {
openModal(true, {
record,
isUpdate: true,
});
}
/**
* 详情
*/
function handleDetail(record) {
openModal(true, {
record,
isUpdate: true,
isDetail: true,
});
}
/**
* 删除事件
*/
async function handleDelete(record) {
await deleteOne({ id: record.id }, reload);
}
/**
* 批量删除事件
*/
async function batchHandleDelete() {
await batchDelete({ ids: selectedRowKeys.value }, reload);
}
/**
* 提交流程
*/
function handleStartProcess(record) {
createConfirm({
iconType: 'warning',
title: '提示',
content: '确认提交流程吗?',
onOk: async () => {
let res = await startProcess({
flowCode: flowCode,
id: record.id,
formUrl: 'super/bpm/example/joa/leave/components/LeaveForm',
formUrlMobile: 'applyform/leave',
});
if (res.success) {
createMessage.success(res.message);
reload();
} else {
createMessage.warning(res.message);
}
},
});
}
/**
* 审批进度
*/
async function handlePreviewPic(record) {
bpmPicModal(true, {
flowCode,
dataId: record.id,
});
}
/**
* 操作栏
*/
function getTableAction(record) {
return [
{
label: '编辑',
onClick: handleEdit.bind(null, record),
ifShow: record.bpmStatus === '1',
},
{
label: '提交流程',
onClick: handleStartProcess.bind(null, record),
ifShow: record.bpmStatus === '1',
},
];
}
/**
* 下拉操作栏
*/
function getDropDownAction(record) {
return [
{
label: '详情',
onClick: handleDetail.bind(null, record),
},
{
label: '删除',
popConfirm: {
title: '是否确认删除',
confirm: handleDelete.bind(null, record),
},
ifShow: record.bpmStatus === '1',
},
{
label: '审批进度',
onClick: handlePreviewPic.bind(null, record),
ifShow: record.bpmStatus !== '1',
},
];
}
</script>
@@ -0,0 +1,576 @@
<template>
<a-card id="staffCard" class="ant-card">
<a-form ref="formRef" :model="model" :labelCol="labelCol" :wrapperCol="wrapperCol" :rules="validatorRules">
<span id="staffTitle">借款单主流程</span>
<table border="1px" class="staffTable">
<tr>
<td class="colfirst">借款人</td>
<td class="secend">
<a-form-item>
<a-input class="text" :disabled="disabled" v-model:value="model.loanUserName" />
</a-form-item>
</td>
<td>借款人部门</td>
<td style="width: 100px">
<a-form-item>
<span class="fontiframe">{{ model.departName }}</span>
</a-form-item>
</td>
<td> 借款时间 </td>
<td>
<a-form-item>
<a-date-picker class="input" format="YYYY-MM-DD" disabled v-model:value="model.loanTime" />
</a-form-item>
</td>
</tr>
<tr>
<td>借款金额</td>
<td>
<a-form-item name="loanAmount">
<a-input-number class="smallText" v-model:value="model.loanAmount" size="small" :min="0" :max="99999999" @change="upperOnChange" />
</a-form-item>
</td>
<td>金额大写</td>
<td colspan="3">
{{ model.upperSum }}
</td>
</tr>
<tr>
<td> 借款用途 </td>
<td colspan="5">
<a-form-item>
<a-textarea v-model:value="model.loanUsage" class="textArea"></a-textarea>
</a-form-item>
</td>
</tr>
<tr>
<td> 备注 </td>
<td colspan="5">
<a-form-item>
<a-textarea v-model:value="model.remarks" class="textArea"> </a-textarea>
</a-form-item>
</td>
</tr>
<tr style="height: 45px">
<td> 部门领导审核 </td>
<td>
<span>{{ model.departLeaderAudit }}</span>
</td>
<td> 财务审核 </td>
<td>
<span>{{ model.financeAudit }}</span>
</td>
<td> 总经理审核 </td>
<td>
<span>{{ model.managerAudit }}</span>
</td>
</tr>
<tr style="height: 45px">
<td> 出纳放款 </td>
<td>
<span>{{ model.cashierLoanAmount }}</span>
</td>
<td>借款发放时间</td>
<td colspan="3">
<span>
{{ model.cashierLoanTime }}
</span>
</td>
</tr>
</table>
<div style="text-align: center; margin-top: 10px">
<a-button type="primary" @click="handleOk()">保存</a-button>
</div>
</a-form>
<br />
<a-form :model="model2" :labelCol="labelCol" :wrapperCol="wrapperCol2">
<a-divider orientation="left">员工出差申请记录</a-divider>
<JFormContainer :disabled="true">
<table border="1px" class="staffTable">
<tr>
<td class="firstTr">出差人</td>
<td class="firstTr" colspan="2">
<a-form-item>
<span class="fontiframe">{{ nickname }}</span>
</a-form-item>
</td>
<td class="firstTr">部门</td>
<td class="firstTr" colspan="2">
<a-form-item>
<JSelectDept v-model:value="model2.departId" :multiple="false" :checkStrictly="true" :showButton="false"></JSelectDept>
</a-form-item>
</td>
</tr>
<tr>
<td> 目的地 </td>
<td colspan="2">
<a-form-item name="destination">
<JAreaLinkage placeholder="请选择" v-model:value="model2.destination" :showArea="true" :showAll="false" />
</a-form-item>
</td>
<td> 项目名称 </td>
<td colspan="2">
<a-form-item name="projectName">
<a-input class="text" v-model:value="model2.projectName" />
</a-form-item>
</td>
</tr>
<tr>
<td> 出发时间 </td>
<td colspan="2">
<a-form-item>
<a-date-picker class="input" format="YYYY-MM-DD" v-model:value="model2.departureTime" :allowClear="false" />
</a-form-item>
</td>
<td> 计划返回时间 </td>
<td colspan="2">
<a-form-item>
<a-date-picker class="input" format="YYYY-MM-DD" v-model:value="model2.plannedReturnTime" :allowClear="false" />
</a-form-item>
</td>
</tr>
<tr>
<td> 实际返回时间 </td>
<td colspan="2">
<a-form-item>
<a-date-picker class="input" placeholder="" format="YYYY-MM-DD" v-model:value="model2.actualReturnTime" :allowClear="false" />
</a-form-item>
</td>
<td> 出差天数 </td>
<td colspan="2">
<a-form-item>
<a-input class="text" v-model:value="model2.dayNum" />
</a-form-item>
</td>
</tr>
<tr>
<td> 出差经费支出 </td>
<td colspan="5">
<a-form-item>
<a-radio-group class="fontiframe" name="radioGroup" v-model:value="model2.travelExpensesType">
<a-radio class="radioGroup" value="1">预支借款</a-radio>
<a-radio class="radioGroup" value="2">个人垫付</a-radio>
</a-radio-group>
</a-form-item>
</td>
</tr>
<tr>
<td> 出发地 </td>
<td colspan="2">
<a-form-item>
<JAreaLinkage v-model:value="model2.departAddress" placeholder="请选择" :showArea="true" :showAll="false" />
</a-form-item>
</td>
<td> 出行工具 </td>
<td colspan="2">
<a-form-item>
<a-radio-group class="fontiframe" name="radioGroup" v-model:value="model2.travelTool">
<a-radio class="radioGroup" :value="1">客车</a-radio>
<a-radio class="radioGroup" :value="2">火车</a-radio>
<a-radio class="radioGroup" :value="3">飞机</a-radio>
</a-radio-group>
</a-form-item>
</td>
</tr>
<tr>
<td> 任务及事由 </td>
<td colspan="5">
<a-form-item>
<a-textarea v-model:value="model2.reason" class="textArea"></a-textarea>
</a-form-item>
</td>
</tr>
<tr style="height: 38px">
<td> 部门领导审核 </td>
<td colspan="2">
<span>{{ model2.departLeaderAudit }}</span>
</td>
<td> 财务审核 </td>
<td colspan="2">
<span>{{ model2.financeAudit }}</span>
</td>
</tr>
<tr style="height: 38px">
<td> 出纳放款 </td>
<td colspan="2">
<span>{{ model2.cashierLoanAmount }}</span>
</td>
<td> 总经理审核 </td>
<td colspan="2">
<span>{{ model2.managerAudit }}</span>
</td>
</tr>
</table>
</JFormContainer>
</a-form>
</a-card>
</template>
<script lang="ts" setup>
import { ref, computed, unref, reactive, toRaw } from 'vue';
import { numToUpper } from '/@/utils';
import { useUserStore } from '/@/store/modules/user';
import { queryByTripApplyNo, queryTripByApplyNo, saveOrUpdate } from '../loan.api';
import JAreaLinkage from '/@/components/Form/src/jeecg/components/JAreaLinkage.vue';
import JSelectDept from '/@/components/Form/src/jeecg/components/JSelectDept.vue';
import JFormContainer from '/@/components/Form/src/jeecg/components/JFormContainer.vue';
import { getRealCode } from '/@/components/Form/src/utils/areaDataUtil.js';
import { formatToDateTime } from '/@/utils/dateUtil';
import dayjs from "dayjs";
// props声明
const props = defineProps({
formData: {
type: Object,
default: () => {},
},
});
const userStore = useUserStore();
const formRef = ref(null);
const nickname = ref(userStore.getUserInfo?.realname);
const disabled = ref(true);
const model = reactive({
id: '',
departName: '',
loanUserName: '',
loanTime: nowTimes(),
loanAmount: 0,
upperSum: '',
loanUsage: '',
remarks: '',
departLeaderAudit: '',
financeAudit: 0,
managerAudit: '',
cashierLoanAmount: '',
cashierLoanTime: null,
});
const model2 = reactive({
applyUserName: '',
departId: '',
destination: '',
projectName: '',
departureTime: null,
plannedReturnTime: null,
actualReturnTime: null,
dayNum: 0,
travelExpensesType: '1',
departAddress: '',
travelTool: 1,
reason: '',
departLeaderAudit: '',
financeAudit: '',
cashierLoanAmount: '',
managerAudit: '',
loanMoney: 0,
});
//表单校验
const validatorRules = {
loanAmount: [{ required: true, message: '请输入借款金额!' }],
};
function nowTimes() {
let date = new Date();
let year = date.getFullYear();
let month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1;
let day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate();
return dayjs(year + '-' + month + '-' + day);
}
function upperOnChange(value) {
let numText = numToUpper(value);
if (numText) {
model.upperSum = numText;
} else {
model.loanAmount = 0;
model.upperSum = '';
}
}
const labelCol = {
xs: { span: 24 },
sm: { span: 3 },
};
const wrapperCol = {
xs: { span: 24 },
sm: { span: 25 },
};
const wrapperCol2 = {
xs: { span: 24 },
sm: { span: 21 },
};
async function initFormData() {
let res = await queryByTripApplyNo({ id: props.formData.vars.apply_no });
if (res.success) {
console.log('获取流程节点信息', res.result);
//表单赋值
edit(res.result);
} else {
edit({ tripApplyNo: props.formData.vars.apply_no });
}
//-------------------------------------------------------------------------------------------------
//通过出差单号,查询出差单
let params = { tripApplyNo: props.formData.vars.apply_no }; //查询条件
let res2 = await queryTripByApplyNo(params);
if (res2.success) {
let obj = res2.result;
console.log('获取出差单信息', obj);
//表单赋值
detailStrip(obj);
}
//-------------------------------------------------------------------------------------------------
}
function add() {
edit({});
}
function edit(record) {
Object.assign(model, { ...record });
if (model.loanAmount != '' && model.loanAmount != null && model.loanAmount != undefined) {
upperOnChange(model.loanAmount);
}
//时间格式化(升级antd3后,时间值不允许是字符串)
model.cashierLoanTime = model.cashierLoanTime ? formatToDateTime(model.cashierLoanTime, 'YYYY-MM-DD HH:mm:ss') : null;
model.loanTime = nowTimes();
model.loanUserName = userStore.getUserInfo?.username;
model.departName = '系统管理部';
model.departLeaderAudit = '';
model.financeAudit = '';
model.managerAudit = '金额大于五千才需向总经理审批';
model.cashierLoanAmount = '';
}
function detailStrip(record) {
//表单赋值
record.destination = getRealCode(record.destination, 3);
record.departAddress && (record.departAddress = getRealCode(record.departAddress, 3));
Object.assign(model2, { ...record });
//时间格式化(升级antd3后,时间值不允许是字符串)
model2.departureTime = model2.departureTime?dayjs(model2.departureTime,'YYYY-MM-DD'):null;
model2.plannedReturnTime = model2.plannedReturnTime?dayjs(model2.plannedReturnTime,'YYYY-MM-DD'):null;
model2.actualReturnTime = model2.actualReturnTime?dayjs(model2.actualReturnTime,'YYYY-MM-DD'):null;
if (model2.loanMoney != '' && model2.loanMoney != null && model2.loanMoney != undefined) {
upperOnChange(model2.loanMoney);
}
}
function handleOk() {
try {
formRef.value
.validate()
.then(async () => {
let formData = toRaw(unref(model));
console.log('formData------>', formData);
console.log('!!formData.id------>', !!formData.id);
//时间格式化
formData.loanTime = formData.loanTime ? formatToDateTime(formData.loanTime, 'YYYY-MM-DD HH:mm:ss') : null;
formData.cashierLoanTime = formData.cashierLoanTime ? formatToDateTime(formData.cashierLoanTime, 'YYYY-MM-DD HH:mm:ss') : null;
//提交表单
await saveOrUpdate(formData, !!formData.id);
//刷新表单
initFormData();
})
.catch((error) => {
console.log('error', error);
});
} finally {
}
}
initFormData();
</script>
<style lang="less" scoped>
html[data-theme='light'] {
.ant-card {
color: rgba(0, 0, 0, 0.65);
background: #fff;
border: 1px solid #e8e8e8;
}
#staffCard {
border: 1px solid #fff;
box-shadow:
0 0 1px 1px #aaa,
3px 0 5px 0 #aaa,
0 4px 7px 0 #aaa;
}
#documentsIssuedTitle {
color: black;
}
.staffTable {
background-color: #fff;
border: 1px solid #000;
}
tr,
th {
border-bottom: 1px solid #000;
}
td,
th {
border-right: 1px solid #000;
border-bottom: 1px solid #000;
}
.input {
:deep(.ant-input) {
border: 0 solid black !important;
background-color: #fff;
}
}
.ant-input {
border: 0 solid black !important;
background-color: #fff;
}
.text {
background-color: #fff;
}
.firstTr {
color: #000;
}
.smallText .ant-input-number-input {
background-color: #fff;
}
.textArea {
border: 0 solid white;
}
/**去掉日期控件边框*/
.ant-picker {
border: 0 solid black !important;
}
}
html[data-theme='dark'] {
@borderColor: #3a3a3a;
.staffTable {
border: 1px solid @borderColor;
}
tr,
th {
border-bottom: 1px solid @borderColor;
}
td,
th {
border-right: 1px solid @borderColor;
border-bottom: 1px solid @borderColor;
}
}
.ant-card {
border-radius: 2px;
margin: 0 auto;
width: 950px;
}
#staffCard {
margin-top: 10px;
margin-bottom: 10px;
border-radius: 5px;
height: auto;
}
#staffTitle {
margin-top: 1px;
font-weight: bold;
text-align: center;
display: block;
font-size: 24px;
}
.staffTable {
width: 100%;
font-size: 12px;
text-align: center;
.tr-style {
height: 50px;
line-height: 50px;
}
tr td:last-child {
border-right: 0;
}
tr:last-child {
border-bottom: 0;
}
.colfirst {
width: 90px;
}
.colfour {
width: 90px;
}
.fontiframe {
font-size: 12px;
}
.ant-form-item {
margin: 0;
}
.input {
:deep(.ant-input) {
border-radius: 0;
display: inherit;
}
}
.ant-input {
border-radius: 0;
display: inherit;
margin: 0;
width: 255px;
}
:deep(.ant-calendar-picker-input) {
border: none !important;
}
.text {
border: none !important;
border-radius: 0;
margin: 0;
width: 100%;
font-size: 12px;
}
.firstTr {
width: 16%;
.ant-form-item-control-wrapper {
width: 50%;
}
}
.smallText .ant-input-number-input {
border: 0;
margin: 0;
}
.lineHeight .ant-form-item-control {
line-height: 0;
}
.textArea {
resize: none;
box-shadow: none;
height: 118px;
font-size: 12px;
border-radius: 0;
margin-bottom: 0;
}
}
.staffLeaveTableId {
margin-right: 87px;
float: right;
height: 35px;
}
.ant-form label {
font-size: 12px;
}
:deep(.ant-form-item-label label) {
font-size: 12px;
}
</style>
@@ -0,0 +1,523 @@
<template>
<a-card id="staffCard" class="ant-card">
<a-form :model="model" :labelCol="labelCol" :wrapperCol="wrapperCol">
<span id="staffTitle">借款单子流程</span>
<table border="1px" class="staffTable">
<tr>
<td class="colfirst">借款人</td>
<td class="secend">
<a-form-item>
<a-input class="text" :disabled="true" v-model:value="model.loanUserName" />
</a-form-item>
</td>
<td>借款人部门</td>
<td style="width: 100px">
<a-form-item>
<span class="fontiframe">{{ model.departName }}</span>
</a-form-item>
</td>
<td> 借款时间 </td>
<td>
<a-form-item>
<a-date-picker class="input" format="YYYY-MM-DD" disabled v-model:value="model.loanTime" />
</a-form-item>
</td>
</tr>
<tr>
<td>借款金额</td>
<td>
<a-form-item>
<a-input-number
class="smallText"
:disabled="true"
v-model:value="model.loanAmount"
size="small"
:min="0"
:max="99999999"
@change="upperOnChange"
/>
</a-form-item>
</td>
<td>金额大写</td>
<td colspan="3">
{{ model.upperSum }}
</td>
</tr>
<tr>
<td> 借款用途 </td>
<td colspan="5">
<a-form-item>
<a-textarea :disabled="true" v-model:value="model.loanUsage" class="textArea"></a-textarea>
</a-form-item>
</td>
</tr>
<tr>
<td> 备注 </td>
<td colspan="5">
<a-form-item>
<a-textarea :disabled="true" v-model:value="model.remarks" class="textArea"> </a-textarea>
</a-form-item>
</td>
</tr>
<tr style="height: 45px">
<td> 部门领导审核 </td>
<td>
<span>{{ model.departLeaderAudit }}</span>
</td>
<td> 财务审核 </td>
<td>
<span>{{ model.financeAudit }}</span>
</td>
<td> 总经理审核 </td>
<td>
<span>{{ model.managerAudit }}</span>
</td>
</tr>
<tr style="height: 45px">
<td> 出纳放款 </td>
<td>
<span>{{ model.cashierLoanAmount }}</span>
</td>
<td>借款发放时间</td>
<td colspan="3">
<span>
{{ model.cashierLoanTime }}
</span>
</td>
</tr>
</table>
</a-form>
<br />
<a-form :model="model2" :labelCol="labelCol" :wrapperCol="wrapperCol2">
<a-divider orientation="left">员工出差申请记录</a-divider>
<JFormContainer :disabled="true">
<table border="1px" class="staffTable">
<tr>
<td class="firstTr">出差人</td>
<td class="firstTr" colspan="2">
<a-form-item>
<span class="fontiframe">{{ nickname }}</span>
</a-form-item>
</td>
<td class="firstTr">部门</td>
<td class="firstTr" colspan="2">
<a-form-item>
<JSelectDept v-model:value="model2.departId" :multiple="false" :checkStrictly="true" :showButton="false"></JSelectDept>
</a-form-item>
</td>
</tr>
<tr>
<td> 目的地 </td>
<td colspan="2">
<a-form-item name="destination">
<JAreaLinkage placeholder="请选择" v-model:value="model2.destination" :showArea="true" :showAll="false" />
</a-form-item>
</td>
<td> 项目名称 </td>
<td colspan="2">
<a-form-item name="projectName">
<a-input class="text" v-model:value="model2.projectName" />
</a-form-item>
</td>
</tr>
<tr>
<td> 出发时间 </td>
<td colspan="2">
<a-form-item>
<a-date-picker class="input" format="YYYY-MM-DD" v-model:value="model2.departureTime" :allowClear="false" />
</a-form-item>
</td>
<td> 计划返回时间 </td>
<td colspan="2">
<a-form-item>
<a-date-picker class="input" format="YYYY-MM-DD" v-model:value="model2.plannedReturnTime" :allowClear="false" />
</a-form-item>
</td>
</tr>
<tr>
<td> 实际返回时间 </td>
<td colspan="2">
<a-form-item>
<a-date-picker class="input" placeholder="" format="YYYY-MM-DD" v-model:value="model2.actualReturnTime" :allowClear="false" />
</a-form-item>
</td>
<td> 出差天数 </td>
<td colspan="2">
<a-form-item>
<a-input class="text" v-model:value="model2.dayNum" />
</a-form-item>
</td>
</tr>
<tr>
<td> 出差经费支出 </td>
<td colspan="5">
<a-form-item>
<a-radio-group class="fontiframe" name="radioGroup" v-model:value="model2.travelExpensesType">
<a-radio class="radioGroup" value="1">预支借款</a-radio>
<a-radio class="radioGroup" value="2">个人垫付</a-radio>
</a-radio-group>
</a-form-item>
</td>
</tr>
<tr>
<td> 出发地 </td>
<td colspan="2">
<a-form-item>
<JAreaLinkage v-model:value="model2.departAddress" placeholder="请选择" :showArea="true" :showAll="false" />
</a-form-item>
</td>
<td> 出行工具 </td>
<td colspan="2">
<a-form-item>
<a-radio-group class="fontiframe" name="radioGroup" v-model:value="model2.travelTool">
<a-radio class="radioGroup" :value="1">客车</a-radio>
<a-radio class="radioGroup" :value="2">火车</a-radio>
<a-radio class="radioGroup" :value="3">飞机</a-radio>
</a-radio-group>
</a-form-item>
</td>
</tr>
<tr>
<td> 任务及事由 </td>
<td colspan="5">
<a-form-item>
<a-textarea v-model:value="model2.reason" class="textArea"></a-textarea>
</a-form-item>
</td>
</tr>
<tr style="height: 38px">
<td> 部门领导审核 </td>
<td colspan="2">
<span>{{ model2.departLeaderAudit }}</span>
</td>
<td> 财务审核 </td>
<td colspan="2">
<span>{{ model2.financeAudit }}</span>
</td>
</tr>
<tr style="height: 38px">
<td> 出纳放款 </td>
<td colspan="2">
<span>{{ model2.cashierLoanAmount }}</span>
</td>
<td> 总经理审核 </td>
<td colspan="2">
<span>{{ model2.managerAudit }}</span>
</td>
</tr>
</table>
</JFormContainer>
</a-form>
</a-card>
</template>
<script lang="ts" setup>
import { ref, computed, unref, reactive, toRaw } from 'vue';
import { numToUpper } from '/@/utils';
import { useUserStore } from '/@/store/modules/user';
import { queryByTripApplyNo, queryTripByApplyNo } from '../loan.api';
import JAreaLinkage from '/@/components/Form/src/jeecg/components/JAreaLinkage.vue';
import JSelectDept from '/@/components/Form/src/jeecg/components/JSelectDept.vue';
import JFormContainer from '/@/components/Form/src/jeecg/components/JFormContainer.vue';
import { getRealCode } from '/@/components/Form/src/utils/areaDataUtil.js';
import dayjs from "dayjs";
// props声明
const props = defineProps({
formData: {
type: Object,
default: () => {},
},
});
const userStore = useUserStore();
const nickname = ref(userStore.getUserInfo?.realname);
const model = reactive({
id: '',
departName: '',
loanUserName: '',
loanTime: nowTimes(),
loanAmount: '',
upperSum: '',
loanUsage: '',
remarks: '',
departLeaderAudit: '',
financeAudit: 0,
managerAudit: '',
cashierLoanAmount: '',
cashierLoanTime: null,
});
const model2 = reactive({
applyUserName: '',
departId: '',
destination: '',
projectName: '',
departureTime: null,
plannedReturnTime: null,
actualReturnTime: null,
dayNum: 0,
travelExpensesType: '1',
departAddress: '',
travelTool: 1,
reason: '',
departLeaderAudit: '',
financeAudit: '',
cashierLoanAmount: '',
managerAudit: '',
});
function nowTimes() {
let date = new Date();
let year = date.getFullYear();
let month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1;
let day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate();
return dayjs(year + '-' + month + '-' + day);
}
function upperOnChange(value) {
let numText = numToUpper(value);
if (numText) {
model.upperSum = numText;
} else {
model.loanAmount = 0;
model.upperSum = '';
}
}
const labelCol = {
xs: { span: 24 },
sm: { span: 3 },
};
const wrapperCol = {
xs: { span: 24 },
sm: { span: 25 },
};
const wrapperCol2 = {
xs: { span: 24 },
sm: { span: 21 },
};
async function initFormData() {
//update-begin-author:liusq---date:20220614--for: 借款申请在历史流程节点时,id取值存在问题 ---
let id = props.formData.vars.id ? props.formData.vars.id : props.formData.dataId;
let res = await queryByTripApplyNo({ id });
//update-end-author:liusq---date:20220614--for: 借款申请在历史流程节点时,id取值存在问题 ---
if (res.success) {
console.log('获取流程节点信息', res.result);
//表单赋值
model.upperSum = numToUpper(res.result.loanAmount);
Object.assign(model, { ...res.result });
//时间格式化(升级antd3后,时间值不允许是字符串)
model.loanTime = model.loanTime?dayjs(model.loanTime,'YYYY-MM-DD'):null;
model.cashierLoanTime = model.cashierLoanTime?dayjs(model.cashierLoanTime,'YYYY-MM-DD'):null;
//-------------------------------------------------------------------------------------------------
//通过出差单号,查询出差单
let params = { tripApplyNo: res.result.tripApplyNo }; //查询条件
let res2 = await queryTripByApplyNo(params);
if (res2.success) {
let obj = res2.result;
console.log('获取出差单信息', obj);
//表单赋值
obj.destination = getRealCode(obj.destination, 3);
obj.departAddress && (obj.departAddress = getRealCode(obj.departAddress, 3));
Object.assign(model2, { ...obj });
}
//时间格式化(升级antd3后,时间值不允许是字符串)
model2.departureTime = model2.departureTime?dayjs(model2.departureTime,'YYYY-MM-DD'):null;
model2.plannedReturnTime = model2.plannedReturnTime?dayjs(model2.plannedReturnTime,'YYYY-MM-DD'):null;
model2.actualReturnTime = model2.actualReturnTime?dayjs(model2.actualReturnTime,'YYYY-MM-DD'):null;
//-------------------------------------------------------------------------------------------------
}
}
initFormData();
</script>
<style lang="less" scoped>
html[data-theme='light'] {
.ant-card {
color: rgba(0, 0, 0, 0.65);
background: #fff;
border: 1px solid #e8e8e8;
}
#staffCard {
border: 1px solid #fff;
box-shadow:
0 0 1px 1px #aaa,
3px 0 5px 0 #aaa,
0 4px 7px 0 #aaa;
}
#staffTitle {
color: black;
}
.staffTable {
background-color: #fff;
border: 1px solid #000;
tr,
th {
border-bottom: 1px solid #000;
}
td,
th {
border-right: 1px solid #000;
border-bottom: 1px solid #000;
}
.input {
:deep(.ant-input) {
border: 0 solid black !important;
background-color: #fff;
}
}
.ant-input {
border: 0 solid black !important;
background-color: #fff;
}
.firstTr {
color: #000;
}
.smallText .ant-input-number-input {
background-color: #fff;
}
.textArea {
border: 0 solid white;
}
}
/**去掉日期控件边框*/
.ant-picker {
border: 0 solid black !important;
}
}
html[data-theme='dark'] {
@borderColor: #3a3a3a;
.staffTable {
border: 1px solid @borderColor;
tr,
th {
border-bottom: 1px solid @borderColor;
}
td,
th {
border-right: 1px solid @borderColor;
border-bottom: 1px solid @borderColor;
}
}
}
.ant-card {
border-radius: 2px;
margin: 0 auto;
width: 950px;
}
#staffCard {
margin-top: 10px;
margin-bottom: 10px;
border-radius: 5px;
height: auto;
}
#staffTitle {
margin-top: 1px;
font-weight: bold;
text-align: center;
display: block;
font-size: 24px;
}
.staffTable {
width: 100%;
font-size: 12px;
text-align: center;
.tr-style {
height: 50px;
line-height: 50px;
}
tr td:last-child {
border-right: 0;
}
tr:last-child {
border-bottom: 0;
}
.colfirst {
width: 90px;
}
.colfour {
width: 90px;
}
.fontiframe {
font-size: 12px;
}
.ant-form-item {
margin: 0;
}
.input {
:deep(.ant-input) {
border-radius: 0;
display: inherit;
}
}
.ant-input {
border-radius: 0;
display: inherit;
margin: 0;
width: 255px;
}
:deep(.ant-calendar-picker-input) {
border: none !important;
}
.text {
border: none !important;
border-radius: 0;
margin: 0;
width: 100%;
font-size: 12px;
}
.firstTr {
width: 16%;
.ant-form-item-control-wrapper {
width: 50%;
}
}
.smallText .ant-input-number-input {
border: 0;
margin: 0;
}
.lineHeight .ant-form-item-control {
line-height: 0;
}
.textArea {
resize: none;
box-shadow: none;
height: 118px;
font-size: 12px;
border-radius: 0;
margin-bottom: 0;
}
}
.staffLeaveTableId {
margin-right: 87px;
float: right;
height: 35px;
}
.ant-form label {
font-size: 12px;
}
:deep(.ant-form-item-label label) {
font-size: 12px;
}
</style>
@@ -0,0 +1,408 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" :title="title" @ok="handleSubmit" width="950px">
<a-form ref="formRef" :model="model" :labelCol="labelCol" :wrapperCol="wrapperCol" :rules="validatorRules" style="padding-bottom: 10px">
<JFormContainer :disabled="formDisabled">
<a-card id="staffCard" class="ant-card">
<span id="staffBmTitle">借款单</span>
<table border="1px" id="staffBmTable">
<tr>
<td class="colfirst">借款人</td>
<td class="secend">
<a-form-item>
<span class="fontiframe">{{ nickname }}</span>
</a-form-item>
</td>
<td>借款人部门</td>
<td style="width: 100px">
<a-form-item>
<span class="fontiframe">{{ model.departName }}</span>
</a-form-item>
</td>
<td> 借款时间 </td>
<td>
<a-form-item>
<a-date-picker class="input" format="YYYY-MM-DD" disabled v-model:value="model.loanTime" />
</a-form-item>
</td>
</tr>
<tr>
<td>借款金额</td>
<td>
<a-form-item name="loanAmount">
<a-input-number class="smallText" v-model:value="model.loanAmount" size="small" :min="0" :max="99999999" @change="upperOnChange" />
</a-form-item>
</td>
<td>金额大写</td>
<td colspan="3">
{{ model.upperSum }}
</td>
</tr>
<tr>
<td> 借款用途 </td>
<td colspan="5">
<a-form-item>
<a-textarea v-model:value="model.loanUsage" class="textArea"></a-textarea>
</a-form-item>
</td>
</tr>
<tr>
<td> 备注 </td>
<td colspan="5">
<a-form-item>
<a-textarea v-model:value="model.remarks" class="textArea"> </a-textarea>
</a-form-item>
</td>
</tr>
<tr style="height: 45px">
<td> 部门领导审核 </td>
<td>
<span>{{ model.departLeaderAudit }}</span>
</td>
<td> 财务审核 </td>
<td>
<span>{{ model.financeAudit }}</span>
</td>
<td> 总经理审核 </td>
<td>
<span>{{ model.managerAudit }}</span>
</td>
</tr>
<tr style="height: 45px">
<td> 出纳放款 </td>
<td>
<span>{{ model.cashierLoanAmount }}</span>
</td>
<td>借款发放时间</td>
<td colspan="3">
<span>
{{ model.cashierLoanTime }}
</span>
</td>
</tr>
</table>
</a-card>
</JFormContainer>
</a-form>
</BasicModal>
</template>
<script lang="ts" setup>
import { ref, computed, unref, reactive, toRaw } from 'vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { useMessage } from '/@/hooks/web/useMessage';
import { saveOrUpdate } from '../loan.api';
import { formatToDateTime } from '/@/utils/dateUtil';
import { numToUpper } from '/@/utils';
import { useUserStore } from '/@/store/modules/user';
import JFormContainer from '/@/components/Form/src/jeecg/components/JFormContainer.vue';
import dayjs from "dayjs";
// Emits声明
const emit = defineEmits(['register', 'success']);
//提示弹窗
const $message = useMessage();
const userStore = useUserStore();
const formRef = ref(null);
const isUpdate = ref(true);
const nickname = ref(userStore.getUserInfo?.realname);
const model = reactive({
id: '',
departName: '系统管理部',
loanUserName: userStore.getUserInfo?.username,
loanTime: nowTimes(),
loanAmount: '',
upperSum: '',
loanUsage: '',
remarks: '',
departLeaderAudit: '',
financeAudit: 0,
managerAudit: '金额大于五千才需向总经理审批',
cashierLoanAmount: '',
cashierLoanTime: null,
});
//表单校验
const validatorRules = {
loanAmount: [{ required: true, message: '请输入借款金额!' }],
};
const formDisabled = ref(false);
//表单赋值
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
setModalProps({ confirmLoading: false, showOkBtn: !!!data?.isDetail });
isUpdate.value = !!data?.isUpdate;
if (unref(isUpdate)) {
Object.assign(model, { ...data.record });
} else {
Object.assign(model, { ...initData });
}
//时间格式化(升级antd3后,时间值不允许是字符串)
model.loanTime = model.loanTime?dayjs(model.loanTime,'YYYY-MM-DD'):null;
model.cashierLoanTime = model.cashierLoanTime?dayjs(model.cashierLoanTime,'YYYY-MM-DD'):null;
// update-begin-author:taoyan date:2022-9-5 for: VUEN-2157 出差申请、借款申请、请假申请、公文申请都有这个问题,详情不让改
if(data.isDetail === true){
formDisabled.value = true;
}else{
formDisabled.value = false;
}
// update-end-author:taoyan date:2022-9-5 for: VUEN-2157 出差申请、借款申请、请假申请、公文申请都有这个问题,详情不让改
});
//设置标题
const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
//表单提交事件
async function handleSubmit() {
try {
formRef.value
.validate()
.then(async () => {
let formData = toRaw(unref(model));
//时间格式化
formData.loanTime = formData.loanTime ? formatToDateTime(formData.loanTime, 'YYYY-MM-DD HH:mm:ss') : null;
formData.cashierLoanTime = formData.cashierLoanTime ? formatToDateTime(formData.cashierLoanTime, 'YYYY-MM-DD HH:mm:ss') : null;
setModalProps({ confirmLoading: true });
//提交表单
await saveOrUpdate(formData, isUpdate.value);
//关闭弹窗
closeModal();
//刷新列表
emit('success');
})
.catch((error) => {
console.log('error', error);
});
} finally {
setModalProps({ confirmLoading: false });
}
}
function nowTimes() {
let date = new Date();
let year = date.getFullYear();
let month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1;
let day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate();
return dayjs(year + '-' + month + '-' + day);
}
function upperOnChange(value) {
let numText = numToUpper(value);
if (numText) {
model.upperSum = numText;
} else {
model.loanAmount = 0;
model.upperSum = '';
}
}
const labelCol = {
xs: { span: 24 },
sm: { span: 3 },
};
const wrapperCol = {
xs: { span: 24 },
sm: { span: 25 },
};
const initData = {
departName: '系统管理部',
loanUserName: userStore.getUserInfo?.username,
loanTime: nowTimes(),
loanAmount: '',
upperSum: '',
loanUsage: '',
remarks: '',
departLeaderAudit: '',
financeAudit: 0,
managerAudit: '金额大于五千才需向总经理审批',
cashierLoanAmount: '',
cashierLoanTime: null,
};
</script>
<style lang="less" scoped>
html[data-theme='light'] {
.ant-card {
color: rgba(0, 0, 0, 0.65);
background: #fff;
border: 1px solid #e8e8e8;
}
#staffCard {
border: 1px solid #fff;
box-shadow:
0 0 1px 1px #aaa,
3px 0 5px 0 #aaa,
0 4px 7px 0 #aaa;
}
#staffBmTitle {
color: black;
}
#staffBmTable {
background-color: #fff;
border: 1px solid #000;
tr,
th {
border-bottom: 1px solid #000;
}
td,
th {
border-right: 1px solid #000;
border-bottom: 1px solid #000;
}
.input {
:deep(.ant-input) {
border: 0 solid black !important;
background-color: #fff;
}
}
.ant-input {
border: 0 solid black !important;
background-color: #fff;
}
.firstTr {
color: #000;
}
.smallText .ant-input-number-input {
background-color: #fff;
}
.textArea {
border: 0 solid white;
}
}
/**去掉日期控件边框*/
.ant-picker {
border: 0 solid black !important;
}
}
html[data-theme='dark'] {
@borderColor: #3a3a3a;
#staffBmTable {
border: 1px solid @borderColor;
tr,
th {
border-bottom: 1px solid @borderColor;
}
td,
th {
border-right: 1px solid @borderColor;
border-bottom: 1px solid @borderColor;
}
}
}
.ant-card {
border-radius: 2px;
margin: 0 auto;
width: 750px;
}
#staffCard {
margin-top: 10px;
margin-bottom: 10px;
border-radius: 5px;
height: auto;
}
#staffBmTitle {
margin-top: 1px;
font-weight: bold;
text-align: center;
display: block;
font-size: 24px;
}
#staffBmTable {
width: 100%;
font-size: 12px;
text-align: center;
.tr-style {
height: 50px;
line-height: 50px;
}
tr td:last-child {
border-right: 0;
}
tr:last-child {
border-bottom: 0;
}
.colfirst {
width: 90px;
}
.colfour {
width: 90px;
}
.fontiframe {
font-size: 12px;
}
.ant-form-item {
margin: 0;
}
.input {
:deep(.ant-input) {
border-radius: 0;
display: inherit;
}
}
.ant-input {
border-radius: 0;
display: inherit;
margin: 0;
width: 255px;
}
:deep(.ant-calendar-picker-input) {
border: none !important;
}
.text {
border: none !important;
border-radius: 0;
margin: 0;
width: 100%;
font-size: 12px;
}
.firstTr {
width: 16%;
.ant-form-item-control-wrapper {
width: 50%;
}
}
.smallText .ant-input-number-input {
border: 0;
margin: 0;
}
.lineHeight .ant-form-item-control {
line-height: 0;
}
.textArea {
resize: none;
height: 118px;
font-size: 12px;
border-radius: 0;
margin-bottom: 0;
}
}
.staffLeaveTableId {
margin-right: 87px;
float: right;
height: 35px;
}
.ant-form label {
font-size: 12px;
}
:deep(.ant-form-item-label label) {
font-size: 12px;
}
</style>
@@ -0,0 +1,60 @@
import { defHttp } from '/@/utils/http/axios';
import { Modal } from 'ant-design-vue';
enum Api {
list = '/joa/joaLoan/list',
queryByTripApplyNo = '/joa/joaLoan/queryByTripApplyNo',
queryTripByApplyNo = '/joa/joaBusinesStrip/queryByTripApplyNo',
save = '/joa/joaLoan/add',
edit = '/joa/joaLoan/edit',
deleteOne = '/joa/joaLoan/delete',
deleteBatch = '/joa/joaLoan/deleteBatch',
}
/**
* 列表
* @param params
*/
export const list = (params) => defHttp.get({ url: Api.list, params });
/**
* 根据借款信息
* @param params
*/
export const queryByTripApplyNo = (params) => defHttp.get({ url: Api.queryByTripApplyNo, params }, { isTransformResponse: false });
/**
* 根据出差信息
* @param params
*/
export const queryTripByApplyNo = (params) => defHttp.get({ url: Api.queryTripByApplyNo, params }, { isTransformResponse: false });
/**
* 删除一个
*/
export const deleteOne = (params, handleSuccess) => {
return defHttp.delete({ url: Api.deleteOne, params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
};
/**
* 批量删除
* @param params
*/
export const batchDelete = (params, handleSuccess) => {
Modal.confirm({
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.delete({ url: Api.deleteBatch, data: params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
},
});
};
/**
* 保存或者更新
* @param params
*/
export const saveOrUpdate = (params, isUpdate) => {
let url = isUpdate ? Api.edit : Api.save;
return defHttp.post({ url: url, params });
};
@@ -0,0 +1,42 @@
import { FormSchema } from '/@/components/Table';
export const columns = [
{
title: '借款人',
dataIndex: 'loanUserName',
width: 100,
},
{
title: '部门名称',
dataIndex: 'departName',
width: 100,
},
{
title: '借款时间',
dataIndex: 'loanTime',
width: 100,
},
{
title: '借款金额',
dataIndex: 'loanAmount',
width: 100,
},
{
title: '借款用途',
dataIndex: 'loanUsage',
width: 100,
},
{
title: '备注',
dataIndex: 'remarks',
width: 100,
},
];
export const searchFormSchema: FormSchema[] = [
{
field: 'loanUserName',
label: '借款人',
component: 'Input',
colProps: { span: 6 },
},
];
@@ -0,0 +1,136 @@
<template>
<BasicTable @register="registerTable" :rowSelection="rowSelection">
<template #tableTitle>
<!--<a-button type="primary" preIcon="ant-design:plus-outlined" @click="handleCreate"> 新增</a-button>-->
<a-dropdown v-if="selectedRowKeys.length > 0">
<template #overlay>
<a-menu>
<a-menu-item key="1" @click="batchHandleDelete">
<Icon icon="ant-design:delete-outlined"></Icon>
删除
</a-menu-item>
</a-menu>
</template>
<a-button
>批量操作
<Icon icon="mdi:chevron-down"></Icon>
</a-button>
</a-dropdown>
</template>
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
</template>
</BasicTable>
<!--借款申请-->
<LoanModal @register="registerModal" @success="reload" />
</template>
<script lang="ts" name="leave-list" setup>
import { ref } from 'vue';
import { BasicTable, TableAction } from '/@/components/Table';
import { useModal } from '/@/components/Modal';
import LoanModal from './components/LoanModal.vue';
import { columns, searchFormSchema } from './loan.data';
import { list, deleteOne, batchDelete } from './loan.api';
import { useListPage } from '/@/hooks/system/useListPage';
import { useMessage } from '/@/hooks/web/useMessage';
const [registerModal, { openModal }] = useModal();
const [registerBpmModal, { openModal: bpmPicModal }] = useModal();
const { createMessage, createConfirm } = useMessage();
// 列表页面公共参数、方法
const { prefixCls, tableContext } = useListPage({
designScope: 'loan-list',
tableProps: {
api: list,
columns: columns,
canResize: false,
formConfig: {
schemas: searchFormSchema,
},
actionColumn: {
width: 120,
},
},
});
const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
/**
* 新增
*/
function handleCreate() {
openModal(true, {
isUpdate: false,
});
}
/**
* 编辑
*/
function handleEdit(record: Recordable) {
openModal(true, {
record,
isUpdate: true,
});
}
/**
* 详情
*/
function handleDetail(record) {
openModal(true, {
record,
isUpdate: true,
isDetail: true,
});
}
/**
* 删除事件
*/
async function handleDelete(record) {
await deleteOne({ id: record.id }, reload);
}
/**
* 批量删除事件
*/
async function batchHandleDelete() {
await batchDelete({ ids: selectedRowKeys.value }, reload);
}
/**
* 审批进度
*/
async function handlePreviewPic(record) {
bpmPicModal(true, {
flowCode,
dataId: record.id,
});
}
/**
* 操作栏
*/
function getTableAction(record) {
return [
{
label: '编辑',
onClick: handleEdit.bind(null, record),
},
];
}
/**
* 下拉操作栏
*/
function getDropDownAction(record) {
return [
{
label: '详情',
onClick: handleDetail.bind(null, record),
},
{
label: '删除',
popConfirm: {
title: '是否确认删除',
confirm: handleDelete.bind(null, record),
},
},
];
}
</script>