You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

565 lines
17 KiB

<template>
<div class="container">
<el-card :shadow="device === 'desktop' ? 'always' : 'never'"
class="card"
:style="{
border: device === 'desktop' ? '' : 'none',
background: device === 'desktop' ? '' : '#f7f8fa',
'min-height': device === 'desktop' ? '' : '100vh',
}">
<template #header>
<p>{{ config.customModel ? config.customModel.name : "办理" }}</p>
</template>
<template>
<div class="steps" v-if="!/\/detail/.test($route.path)">
<el-steps :space="120" finish-status="success" align-center>
<el-step
:title="node.name"
status="finish"
icon="el-icon-edit"
></el-step>
<el-step
v-for="nextNode in node.nextNodes"
:key="nextNode.id"
:title="nextNode.name"
icon="el-icon-right"
status="wait"
></el-step>
</el-steps>
<el-divider></el-divider>
</div>
<div class="form-container">
<template v-if="device === 'desktop'">
<DesktopForm
:device="device"
ref="desktopForm"
:sub-form="subConfig"
:fields="fields"
:original-form="form"
:readable="readableFields"
:file-list="fileList"
:script-content="scriptContent"
:writeable="writeableFields"
:rules="rules"
></DesktopForm>
</template>
<template v-else>
<MobileForm
:device="device"
ref="mobileForm"
:sub-form="subConfig"
:fields="fields"
:original-form="form"
:readable="readableFields"
:file-list="fileList"
:script-content="scriptContent"
:writeable="writeableFields"
></MobileForm>
</template>
</div>
</template>
<!-- 审批日志-->
<div v-if="/\/detail/.test($route.path)" style="margin-top: 10px">
<div>流转记录</div>
<vxe-table
show-footer
ref="table"
stripe
class="log-table-scroll"
keep-source
show-overflow
:column-config="{ resizable: true }"
:print-config="{}"
:export-config="{}"
:custom-config="{ mode: 'popup' }"
:footer-data="footerData"
:data="config.logs || []"
>
<vxe-column type="seq" width="62" align="center" field="seq" title="编号"/>
<vxe-column width="140" title="节点名称" align="center" field="node.name" :formatter="({ cellValue }) => (cellValue || '节点已调整')"></vxe-column>
<vxe-column width="80" align="center" title="办理状态" field="status" :formatter="({ cellValue }) => (myStatus.get(cellValue))">
<template #default="{ row }">
<el-tag
size="mini"
:type="statusColor.get(row.status)"
effect="dark"
>{{ myStatus.get(row.status) }}</el-tag
>
</template>
</vxe-column>
<vxe-column align="center" width="80" title="承办人员" field="user.name"></vxe-column>
<vxe-column align="center" width="200" title="流转时间" field="created_at" :formatter="({ cellValue }) => $moment(cellValue).format('YYYY年MM月DD日 HH:mm:ss')"></vxe-column>
<vxe-column min-width="200" header-align="center" title="退回原因" field="reason"></vxe-column>
<vxe-column align="center" width="200" title="办理时间" field="updated_at">
<template #default="{ row }">
<span :style="{ 'color': (row.deadline && row.updated_at && $moment(row.updated_at).isAfter($moment(row.deadline).endOf('day')) ? 'red' : '') }">{{ $moment(row.updated_at).format('YYYY年MM月DD日 HH:mm:ss') }}</span>
</template>
</vxe-column>
<vxe-column align="center" title="耗时" field="use_time" width="120">
<template #default="{ row }">
<span>{{ diffTime(row.updated_at, row.created_at) }}</span>
</template>
</vxe-column>
</vxe-table>
</div>
<div class="btns" ref="btns">
<template v-if="!/\/detail/.test($route.path)">
<el-button
v-if="$route.query.flow_id"
icon="el-icon-arrow-left"
type="danger"
size="small"
@click="isShowRollback = true"
>退回</el-button
>
<el-button
v-if="$route.query.flow_id"
icon="el-icon-caret-right"
type="primary"
plain
size="small"
@click="isShowForward = true"
>部门内转办</el-button
>
<el-button
v-if="$route.query.flow_id"
icon="el-icon-document-add"
type="info"
size="small"
@click="submit('only-submit')"
>暂存不流转</el-button
>
<el-button type="primary" size="small" @click="submit('assign')"
>保存并流转 <i class="el-icon-right"></i
></el-button>
<el-button
v-if="!$route.query.flow_id"
type="info"
size="small"
@click="$router.go(-1)"
>返回</el-button
>
</template>
<template v-else>
<el-button
plain
size="small"
@click="$router.go(-1)"
>返回</el-button
>
<el-button
plain
size="small"
>打印</el-button
>
<el-button
plain
size="small"
>打印(带审批记录)</el-button
>
<el-button
plain
size="small"
>下载附件</el-button
>
</template>
</div>
</el-card>
<assign
ref="assign"
:visible.sync="isShowAssign"
:config="config"
:result="result"
></assign>
<forward
ref="forward"
:is-show.sync="isShowForward"
:flow="config.flow"></forward>
<rollback ref="rollback"
:is-show.sync="isShowRollback"
:flow="config.flow"></rollback>
<el-backtop></el-backtop>
</div>
</template>
<script>
import DesktopForm from "./DesktopForm.vue";
import MobileForm from "./MobileForm.vue";
import assign from "./components/assign.vue";
import forward from "./components/forward.vue";
import rollback from "./components/rollback.vue";
import { create, deal, fieldConfig, preConfig, preDeal, view } from "@/api/flow";
import { deepCopy } from "@/utils";
import { validation, validationName } from "@/utils/validate";
export default {
components: {
DesktopForm,
MobileForm,
assign,
forward,
rollback,
},
data() {
return {
isShowRollback: false,
isShowForward: false,
isShowAssign: false,
info: [],
config: {},
subConfig: new Map(),
myStatus: new Map([
[-2,'会签退回'],
[-1,'退回'],
[0, "办理中"],
[1, "已完成"],
]),
statusColor: new Map([
[-2, "warning"],
[-1, "warning"],
[0, ""],
[1, "success"],
]),
form: {},
result: {},
fileList: {},
rules: {}
};
},
methods: {
generateForm(object, fields) {
this.rules = {}
fields.forEach((field) => {
if(field.rules && field.rules.length > 0) {
this.rules[field.name] = field.rules.map(rule => {
switch (rule) {
case 'required':
return { required: true, message: `请填写${field.label}`, trigger: 'blur' }
default:
return {
validator: (myRule, value, callback) => {
if(validation.get(rule).test(value)) {
callback()
} else {
callback(new Error(`${field.label}必须为${validationName.get(rule)}`))
}
},
trigger: 'blur'
}
}
})
}
if (field.type === "file") {
this.fileList[field.name] = [];
}
if (field.type === "relation") {
object[field.name] = [{}];
this.generateForm(
object[field.name][0],
this.subConfig.get(field.sub_custom_model_id)?.customModel?.fields
);
} else {
object[field.name] = "";
}
});
},
formatTime(time) {
const days = parseInt(time / (1000 * 60 * 60 * 24));
const hours = parseInt((time % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
const minutes = parseInt((time % (1000 * 60 * 60)) / (1000 * 60));
const seconds = (time % (1000 * 60)) / 1000;
return `${days > 0 ? (days) + '天' : ''}${hours > 0 ? (hours) + '时' : ''}${minutes}分${seconds}秒`;
},
async getConfig() {
const loading = this.$loading({
lock: true,
text: "拼命加载中",
spinner: "el-icon-loading",
background: "rgba(0, 0, 0, 0.8)",
});
//路由为detail为详情
if(/\/detail/.test(this.$route.path) && this.$route.query.flow_id) {
try {
const res = await view(this.$route.query.flow_id);
console.log('view',res)
const { fields } = res?.customModel;
let subFormRequest = [];
const getSubForm = (id) => {
subFormRequest.push(fieldConfig(id));
};
fields.forEach((field) => {
if (field.sub_custom_model_id) {
getSubForm(field.sub_custom_model_id);
}
});
const subConfigs = await Promise.all(subFormRequest);
subConfigs.forEach((sub) => {
if (sub.customModel?.id) {
this.subConfig.set(sub.customModel?.id, sub);
}
});
this.config = res;
this.generateForm(this.form, fields);
this.form = Object.assign({}, this.form);
const { data } = res?.flow;
for (let key in data) {
try {
let jsonObj = JSON.parse(data[key]);
if (this.form.hasOwnProperty(key)) {
this.form[key] = jsonObj;
}
if (this.fileList.hasOwnProperty(key)) {
this.fileList[key] = jsonObj.map((i) => ({
name: i.name,
url: i.url,
response: i,
}));
}
} catch (err) {
if (this.form.hasOwnProperty(key)) {
this.form[key] = data[key];
}
}
}
this.fileList = Object.assign({}, this.fileList);
loading.close();
} catch (err) {
console.error(err);
this.$message.error("配置失败");
loading.close();
}
} else if (!this.$route.query.flow_id) {
try {
const res = await preConfig(this.$route.query.module_id);
const { fields } = res?.customModel;
let subFormRequest = [];
const getSubForm = (id) => {
subFormRequest.push(fieldConfig(id));
};
fields.forEach((field) => {
if (field.sub_custom_model_id) {
getSubForm(field.sub_custom_model_id);
}
});
const subConfigs = await Promise.all(subFormRequest);
subConfigs.forEach((sub) => {
if (sub.customModel?.id) {
this.subConfig.set(sub.customModel?.id, sub);
}
});
this.config = res;
this.generateForm(this.form, fields);
this.form = Object.assign({}, this.form);
loading.close();
} catch (err) {
console.error(err);
this.$message.error("配置失败");
loading.close();
}
} else {
try {
const res = await preDeal(this.$route.query.flow_id);
const { fields } = res?.customModel;
let subFormRequest = [];
const getSubForm = (id) => {
subFormRequest.push(fieldConfig(id));
};
fields.forEach((field) => {
if (field.sub_custom_model_id) {
getSubForm(field.sub_custom_model_id);
}
});
const subConfigs = await Promise.all(subFormRequest);
subConfigs.forEach((sub) => {
if (sub.customModel?.id) {
this.subConfig.set(sub.customModel?.id, sub);
}
});
this.config = res;
this.generateForm(this.form, fields);
this.form = Object.assign({}, this.form);
const { data } = res?.flow;
for (let key in data) {
try {
let jsonObj = JSON.parse(data[key]);
if (this.form.hasOwnProperty(key)) {
this.form[key] = jsonObj;
}
if (this.fileList.hasOwnProperty(key)) {
this.fileList[key] = jsonObj.map((i) => ({
name: i.name,
url: i.url,
response: i,
}));
}
} catch (err) {
if (this.form.hasOwnProperty(key)) {
this.form[key] = data[key];
}
}
}
this.fileList = Object.assign({}, this.fileList);
loading.close();
} catch (err) {
console.error(err);
this.$message.error("配置失败");
loading.close();
}
}
},
async submit(type) {
let copyForm,copyFile;
if (this.device === "desktop") {
copyForm = deepCopy(this.$refs["desktopForm"].form);
copyFile = deepCopy(this.$refs["desktopForm"].file);
} else {
copyForm = deepCopy(this.$refs["mobileForm"].form);
copyFile = deepCopy(this.$refs["mobileForm"].file);
}
console.log(copyForm,copyFile)
for (let [key, value] of Object.entries(copyFile)) {
if (copyForm.hasOwnProperty(key)) {
copyForm[key] = value.map((i) => i.response?.id)?.toString();
}
}
copyForm["current_node_id"] = this.config.currentNode.id;
try {
let callback;
switch (type) {
case "only-submit":
if (this.$route.query.flow_id) {
copyForm["temporary_save"] = 1;
}
callback = () => this.$router.push("/flow/list/todo");
break;
case "assign":
if (this.$route.query.flow_id) {
copyForm["temporary_save"] = 0;
}
callback = () => (this.isShowAssign = true);
break;
}
if (this.device === "desktop") {
if(!await this.$refs['desktopForm'].validate()) return
}
if (this.$route.query.flow_id) {
copyForm.id = this.$route.query.flow_id;
const { flow, is_last_handled_log } = await deal(
this.$route.query.flow_id,
copyForm
);
this.result = flow;
if(!is_last_handled_log) {
await this.$alert('办理成功,其他会签办理完成后,由最后办理的成员流转到下一节点。','提示',{
showClose: false,
})
callback = () => this.$router.push("/flow/list/todo");
}
} else {
this.result = await create(this.$route.query.module_id, copyForm);
}
callback();
} catch (err) {
console.error(err);
}
},
},
computed: {
device() {
return this.$store.state.app.device;
},
fields() {
return this.config?.customModel?.fields || [];
},
readableFields() {
return /\/detail/.test(this.$route.path) ? this.fields?.map(i => i.id) : (this.config?.currentNode?.readable || []);
},
writeableFields() {
return this.config?.currentNode?.writeable || [];
},
node() {
return this.config?.currentNode || {};
},
scriptContent() {
if (this.config?.customModel?.view_js && this.$route.query.flow_id) {
return this.config?.customModel?.view_js;
} else if (this.config?.customModel?.js && !this.$route.query.flow_id) {
return this.config?.customModel?.js;
}
},
diffTime() {
return function (end,start) {
const diff = this.$moment(end).diff(this.$moment(start))
return this.formatTime(diff)
}
},
footerData() {
const diff = this.$moment(this.config?.logs?.at(-1)?.updated_at).diff(this.$moment(this.config?.logs?.at(0)?.created_at));
return [
{
seq: "总耗时",
'use_time': this.formatTime(diff),
}
]
},
},
created() {
this.getConfig();
},
mounted() {},
};
</script>
<style scoped lang="scss">
::v-deep .el-card__header {
}
.container {
padding: 20px;
.card {
position: relative;
}
.btns {
display: flex;
justify-content: center;
margin-top: 10px;
flex-wrap: wrap;
}
}
.form-container {
}
@media (max-width: 768px) {
.container {
padding: 0;
}
.btns {
justify-content: space-evenly;
& > * {
margin: 4px 6px;
}
}
}
</style>
<style lang="scss">
.log-table-scroll {
::-webkit-scrollbar {
height: 0;
}
}
</style>