|
|
<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>
|
|
|
<Steps :logs="config.logs" :current-node="node"></Steps>
|
|
|
<div v-if="/\/detail/.test($route.path) && config && config.flow">
|
|
|
<div v-if="config.flow.hasOwnProperty('out_contracts') && config.flow.out_contracts && config.flow.out_contracts.length > 0" style="margin-bottom: 10px;color:#F56C6C;">
|
|
|
<!-- 单条数据 -->
|
|
|
<div v-if="config.flow.out_contracts.length === 1">
|
|
|
<span @click="showContract(config.flow.out_contracts[0].id)" style="cursor: pointer;">
|
|
|
本流程已关联资金预算管理:<span style="color:#409eff;">{{ config.flow.out_contracts[0].name }}</span>,点击查看完整信息链。
|
|
|
</span>
|
|
|
</div>
|
|
|
<!-- 多条数据 -->
|
|
|
<div v-else>
|
|
|
<span>本流程已关联{{ config.flow.out_contracts.length }}条资金预算管理,分别是</span>
|
|
|
<span v-for="(contract, index) in config.flow.out_contracts" :key="contract.id">
|
|
|
<span v-if="index > 0">、</span>
|
|
|
<span @click="showContract(contract.id)" style="cursor: pointer;color:#409eff;">{{ contract.name }}</span>
|
|
|
</span>
|
|
|
<span>,可分别点击查看完整信息链。</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- out_pay 处理 -->
|
|
|
<div v-if="config.flow.hasOwnProperty('out_pay') && config.flow.out_pay && config.flow.out_pay.length > 0" style="margin-bottom: 10px;color:#F56C6C;">
|
|
|
<!-- 单条数据 -->
|
|
|
<div v-if="config.flow.out_pay.length === 1">
|
|
|
<span @click="showContract(config.flow.out_pay[0].contract_id)" style="cursor: pointer;">
|
|
|
本流程已关联资金预算管理:<span style="color:#409eff;">{{ config.flow.out_pay[0].contract.name }}</span>,点击查看完整信息链。
|
|
|
</span>
|
|
|
</div>
|
|
|
<!-- 多条数据 -->
|
|
|
<div v-else>
|
|
|
<span>本流程已关联{{ config.flow.out_pay.length }}条资金预算管理,分别是</span>
|
|
|
<span v-for="(pay, index) in config.flow.out_pay" :key="pay.id">
|
|
|
<span v-if="index > 0">、</span>
|
|
|
<span @click="showContract(pay.contract_id)" style="cursor: pointer;color:#409eff;">{{ pay.contract.name }}</span>
|
|
|
</span>
|
|
|
<span>,可分别点击查看完整信息链。</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<div class="form-container" id="print-content">
|
|
|
<DesktopForm
|
|
|
:device="device"
|
|
|
ref="desktopForm"
|
|
|
:config="config"
|
|
|
:is-first-node="isFirstNode"
|
|
|
:sub-form="subConfig"
|
|
|
:fields="fields"
|
|
|
:original-form="form"
|
|
|
:readable.sync="readableFields"
|
|
|
:script-content="scriptContent"
|
|
|
:writeable.sync="writeableFields"
|
|
|
:rules="rules"
|
|
|
:sub-rules="subRules"
|
|
|
:logs="config.logs"
|
|
|
></DesktopForm>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<!-- 审批日志-->
|
|
|
<div v-if="/\/detail/.test($route.path)" style="margin-top: 10px">
|
|
|
<div>流转记录</div>
|
|
|
<vxe-table
|
|
|
style="margin-top: 10px;"
|
|
|
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 || []"
|
|
|
@cell-dblclick="cellDblclickEvent"
|
|
|
>
|
|
|
<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
|
|
|
v-if="$store.state.user.adminId === 1 && $route.query.flow_id"
|
|
|
icon="el-icon-arrow-left"
|
|
|
type="danger"
|
|
|
size="small"
|
|
|
@click="isShowRollback = true"
|
|
|
>退回</el-button
|
|
|
>
|
|
|
<el-button plain size="small" @click="$router.go(-1)">返回</el-button>
|
|
|
<el-button plain size="small" @click="print(false)">打印</el-button>
|
|
|
<!-- <el-button plain size="small" @click="print(true)">打印(带审批记录)</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>
|
|
|
<!-- 更改时间 -->
|
|
|
<el-dialog
|
|
|
title="请选择时间"
|
|
|
:visible.sync="isShowTime"
|
|
|
:close-on-click-modal="false"
|
|
|
width="30%"
|
|
|
>
|
|
|
<!-- 日期时间选择器 -->
|
|
|
<el-date-picker
|
|
|
v-model="selectedDateTime"
|
|
|
type="datetime"
|
|
|
placeholder="选择日期时间"
|
|
|
format="yyyy-MM-dd HH:mm:ss"
|
|
|
value-format="yyyy-MM-dd HH:mm:ss"
|
|
|
:clearable="false"
|
|
|
style="width: 100%"
|
|
|
></el-date-picker>
|
|
|
|
|
|
<div slot="footer" class="dialog-footer">
|
|
|
<el-button @click="isShowTime = false,timeId='',selectedDateTime='',selectedDateType=''">取消</el-button>
|
|
|
<el-button type="primary" @click="updateTime">确定</el-button>
|
|
|
</div>
|
|
|
</el-dialog>
|
|
|
<!-- 更改承办人员 -->
|
|
|
<el-dialog
|
|
|
title="请选择承办人员"
|
|
|
:visible.sync="isShowUserDialog"
|
|
|
:close-on-click-modal="false"
|
|
|
width="30%"
|
|
|
>
|
|
|
<!-- 用户选择下拉框 -->
|
|
|
<el-select
|
|
|
v-model="selectedUserId"
|
|
|
placeholder="请选择承办人员"
|
|
|
filterable
|
|
|
style="width: 100%"
|
|
|
>
|
|
|
<el-option
|
|
|
v-for="user in userList"
|
|
|
:key="user.id"
|
|
|
:label="user.name"
|
|
|
:value="user.id"
|
|
|
></el-option>
|
|
|
</el-select>
|
|
|
|
|
|
<div slot="footer" class="dialog-footer">
|
|
|
<el-button @click="isShowUserDialog = false,currentRowId='',selectedUserId=''">取消</el-button>
|
|
|
<el-button type="primary" @click="updateUser">确定</el-button>
|
|
|
</div>
|
|
|
</el-dialog>
|
|
|
<!-- 打开支出 详情 -->
|
|
|
<vxe-modal
|
|
|
v-model="isShowModal"
|
|
|
:z-index="999999"
|
|
|
transfer
|
|
|
show-zoom
|
|
|
resize
|
|
|
:fullscreen="$store.getters.device === 'mobile'"
|
|
|
title="查看"
|
|
|
width="100%"
|
|
|
height="100%"
|
|
|
esc-closable
|
|
|
:padding="false"
|
|
|
>
|
|
|
<iframe
|
|
|
:src="contractUrl"
|
|
|
style="display: block;width: 100%;height: 100%;border: 0;"
|
|
|
frameborder="0"
|
|
|
/>
|
|
|
</vxe-modal>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
import Steps from "./components/Steps.vue";
|
|
|
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 { generateRandomString } from '@/utils'
|
|
|
import { userListNoAuth} from "@/api/common";
|
|
|
import {
|
|
|
create,
|
|
|
deal,
|
|
|
fieldConfig, flowList,
|
|
|
preConfig,
|
|
|
preDeal,
|
|
|
updateNodeTime,
|
|
|
view,
|
|
|
} from "@/api/flow";
|
|
|
import { deepCopy } from "@/utils";
|
|
|
import { validation, validationName } from "@/utils/validate";
|
|
|
import { print } from "@/utils/print";
|
|
|
import JSONBigint from 'json-bigint'
|
|
|
import { getToken } from "@/utils/auth";
|
|
|
export default {
|
|
|
components: {
|
|
|
Steps,
|
|
|
DesktopForm,
|
|
|
MobileForm,
|
|
|
assign,
|
|
|
forward,
|
|
|
rollback,
|
|
|
},
|
|
|
data() {
|
|
|
return {
|
|
|
isShowModal:false,
|
|
|
contractUrl:'',
|
|
|
printKey: 0,
|
|
|
isShowRollback: false,
|
|
|
isShowForward: false,
|
|
|
isShowAssign: false,
|
|
|
timeId: '',
|
|
|
isShowTime: false,
|
|
|
selectedDateTime: '',
|
|
|
selectedDateType: '',
|
|
|
isShowUserDialog: false,
|
|
|
selectedUserId: '',
|
|
|
userList: [],
|
|
|
currentRowId: '',
|
|
|
info: [],
|
|
|
config: {},
|
|
|
writeableFields: [],
|
|
|
readableFields: [],
|
|
|
subConfig: new Map(),
|
|
|
myStatus: new Map([
|
|
|
[-2, "会签退回"],
|
|
|
[-1, "退回"],
|
|
|
[0, "办理中"],
|
|
|
[1, "已完成"],
|
|
|
]),
|
|
|
statusColor: new Map([
|
|
|
[-2, "warning"],
|
|
|
[-1, "warning"],
|
|
|
[0, ""],
|
|
|
[1, "success"],
|
|
|
]),
|
|
|
|
|
|
form: {},
|
|
|
result: {},
|
|
|
fileList: {},
|
|
|
subFileList: {},
|
|
|
rules: {},
|
|
|
subRules: {},
|
|
|
flows: [],
|
|
|
csrf_token: '',
|
|
|
};
|
|
|
},
|
|
|
watch:{
|
|
|
isShowModal(val){
|
|
|
if(!val){
|
|
|
this.contractUrl = ''
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
methods: {
|
|
|
// 打开 支出的链接
|
|
|
showContract(id){
|
|
|
this.contractUrl = `${process.env.VUE_APP_BASE_API}/ht/#/contract-flow?auth_token=${window.encodeURIComponent(
|
|
|
getToken()
|
|
|
)}&out_contract_id=`+id
|
|
|
this.isShowModal = true
|
|
|
},
|
|
|
// 处理url中default_json
|
|
|
handleDefaultJSON() {
|
|
|
console.log("123")
|
|
|
this.form['form_canal'] = 'oa'
|
|
|
try {
|
|
|
if(!this.$route.query?.default_json) return
|
|
|
const res = JSON.parse(this.$route.query?.default_json)
|
|
|
for (let key in this.$route.query) {
|
|
|
if(/^out_(.*)_id/.test(key)) {
|
|
|
this.form[key] = this.$route.query[key]
|
|
|
}
|
|
|
if(/^borrow_id/.test(key)) {
|
|
|
this.form[key] = this.$route.query[key]
|
|
|
}
|
|
|
if(/^form_canal/.test(key)){
|
|
|
this.form[key] = this.$route.query[key]
|
|
|
}
|
|
|
}
|
|
|
for (let key in res) {
|
|
|
try {
|
|
|
let jsonObj = JSON.parse(res[key]);
|
|
|
if (this.form.hasOwnProperty(key)) {
|
|
|
this.form[key] = jsonObj;
|
|
|
}
|
|
|
|
|
|
} catch (err) {
|
|
|
if (this.form.hasOwnProperty(key)) {
|
|
|
this.form[key] = res[key];
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
} catch (err) {
|
|
|
console.error(err)
|
|
|
}
|
|
|
},
|
|
|
async print(isLog=false) {
|
|
|
const _this = this
|
|
|
let customModelId = this.config.customModel.id || this.$route.query.module_id
|
|
|
const modelRes = await fieldConfig(customModelId,true)
|
|
|
let pickTemplate = 0
|
|
|
const printTemplates = [{
|
|
|
id: 0,
|
|
|
name: '基础模版',
|
|
|
print_format: modelRes.customModel.print_format
|
|
|
},...modelRes.customModel.print_formats]
|
|
|
const h = this.$createElement;
|
|
|
await this.$msgbox({
|
|
|
title: '打印模版选择',
|
|
|
message: h('div',{
|
|
|
class: 'print-template-radios',
|
|
|
key: this.printKey++
|
|
|
},[
|
|
|
h('div',{
|
|
|
},printTemplates.map(i => h('div',{
|
|
|
style: {
|
|
|
display: 'flex',
|
|
|
'align-items': 'center',
|
|
|
'margin-top': '4px',
|
|
|
}
|
|
|
},[
|
|
|
h('span',{
|
|
|
class: 'el-radio__input'
|
|
|
},[
|
|
|
h('span', {
|
|
|
class: 'el-radio__inner custom-cursor-on-hover'
|
|
|
}),
|
|
|
h('input', {
|
|
|
style: {
|
|
|
cursor: 'pointer',
|
|
|
opacity: 0,
|
|
|
position: 'absolute',
|
|
|
top: 0,
|
|
|
left: 0,
|
|
|
right: 0,
|
|
|
bottom: 0,
|
|
|
margin: 0
|
|
|
},
|
|
|
attrs: {
|
|
|
// 添加属性
|
|
|
type: "radio",
|
|
|
id: `print-radio-${i.id}`,
|
|
|
name: "Radio",
|
|
|
value: i.id,
|
|
|
checked: pickTemplate === i.id,
|
|
|
},
|
|
|
on: {
|
|
|
change: () => {
|
|
|
pickTemplate = i.id
|
|
|
}
|
|
|
}
|
|
|
})
|
|
|
]),
|
|
|
h('label', {
|
|
|
style: {
|
|
|
flex: 1
|
|
|
},
|
|
|
attrs: {
|
|
|
for: `print-radio-${i.id}`
|
|
|
},
|
|
|
class: 'el-radio__label',
|
|
|
}, i.name)
|
|
|
])))
|
|
|
]),
|
|
|
showCancelButton: true,
|
|
|
confirmButtonText: '确定',
|
|
|
cancelButtonText: '取消',
|
|
|
})
|
|
|
const printText = printTemplates.find(i => i.id === pickTemplate)?.print_format
|
|
|
if(isLog) {
|
|
|
const res = await this.$refs['table'].exportData({
|
|
|
type: 'html',
|
|
|
download: false
|
|
|
})
|
|
|
await print.bind(this)(printText, isLog, _this.config.flow, res.content)
|
|
|
} else {
|
|
|
await print.bind(this)(printText, isLog, _this.config.flow)
|
|
|
}
|
|
|
},
|
|
|
|
|
|
generateForm(object, fields, relation = false, pname) {
|
|
|
fields.forEach((field) => {
|
|
|
if (field.rules && field.rules.length > 0 && this.writeableFields.find(i => i === field.id) && !relation) {
|
|
|
this.rules[field.name] = field.rules.map((rule) => {
|
|
|
switch (rule) {
|
|
|
case "required":
|
|
|
if (field.type === 'relation') {
|
|
|
return {
|
|
|
validator: (myRule, value, callback) => {
|
|
|
if (value instanceof Array && value.length > 0) {
|
|
|
callback()
|
|
|
} else {
|
|
|
callback(`请填写${field.label}`)
|
|
|
}
|
|
|
},
|
|
|
message: `请填写${field.label}`,
|
|
|
trigger: "blur",
|
|
|
};
|
|
|
} else {
|
|
|
return {
|
|
|
required: true,
|
|
|
message: `请填写${field.label}`,
|
|
|
trigger: "blur",
|
|
|
};
|
|
|
}
|
|
|
default:
|
|
|
return {
|
|
|
validator: (myRule, value, callback) => {
|
|
|
if (validation.get(rule).test(value) || value === '') {
|
|
|
callback();
|
|
|
} else {
|
|
|
callback(
|
|
|
new Error(
|
|
|
`${field.label}必须为${validationName.get(rule)}`
|
|
|
)
|
|
|
);
|
|
|
}
|
|
|
},
|
|
|
trigger: "blur",
|
|
|
message: `${field.label}必须为${validationName.get(rule)}`,
|
|
|
};
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
if (relation) {
|
|
|
this.subRules[`${pname}_rules`][field.name] = field.rules.map((rule) => {
|
|
|
switch (rule) {
|
|
|
case "required":
|
|
|
return {
|
|
|
required: true,
|
|
|
message: `请填写${field.label}`,
|
|
|
};
|
|
|
default:
|
|
|
return {
|
|
|
validator: this.device === 'desktop' ? ({ cellValue }) => {
|
|
|
return new Promise((resolve, reject) => {
|
|
|
if (validation.get(rule).test(cellValue) || cellValue === '') {
|
|
|
resolve()
|
|
|
} else {
|
|
|
reject(
|
|
|
new Error(
|
|
|
`${field.label}必须为${validationName.get(rule)}`
|
|
|
)
|
|
|
);
|
|
|
}
|
|
|
})
|
|
|
} : (myRule, value, callback) => {
|
|
|
if (validation.get(rule).test(value) || value === '') {
|
|
|
callback();
|
|
|
} else {
|
|
|
callback(
|
|
|
new Error(
|
|
|
`${field.label}必须为${validationName.get(rule)}`
|
|
|
)
|
|
|
);
|
|
|
}
|
|
|
},
|
|
|
trigger: "blur",
|
|
|
pattern: validation.get(rule),
|
|
|
message: `${field.label}必须为${validationName.get(rule)}`,
|
|
|
};
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
if (field.type === "relation") {
|
|
|
this.subRules[`${field.name}_rules`] = {}
|
|
|
let temp = {}
|
|
|
this.subConfig.get(field.sub_custom_model_id)?.customModel?.fields?.forEach(field => temp[field.name] = '')
|
|
|
object[field.name] = [temp];
|
|
|
|
|
|
this.generateForm(
|
|
|
object[field.name][0],
|
|
|
this.subConfig.get(field.sub_custom_model_id)?.customModel?.fields,
|
|
|
true,
|
|
|
field.name
|
|
|
);
|
|
|
} else {
|
|
|
if (/\/detail/.test(this.$route.path) && this.$route.query.flow_id) {
|
|
|
object[field.name] = "";
|
|
|
} else {
|
|
|
if (this.writeableFields.indexOf(field.id) !== -1 || this.readableFields.indexOf(field.id) !== -1) {
|
|
|
object[field.name] = (this.writeableFields.indexOf(field.id) !== -1 && field.default_value) ? field.default_value : (field.type === 'file' ? [] : "");
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
this.form['flow_title'] = this.config?.flow?.title ?? `${this.config.customModel.name}(${this.$store.getters.name} ${this.$moment().format('YYYY-MM-DD HH:mm')})`
|
|
|
},
|
|
|
|
|
|
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);
|
|
|
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.readableFields = /\/detail/.test(this.$route.path)
|
|
|
? this.fields?.map((i) => i.id)
|
|
|
: this.config?.currentNode?.readable || [];
|
|
|
this.writeableFields = this.config?.currentNode?.writeable || [];
|
|
|
// 生成空的form表单对象
|
|
|
this.generateForm(this.form, fields);
|
|
|
// form赋值
|
|
|
const { data } = res?.flow;
|
|
|
for (let key in data) {
|
|
|
try {
|
|
|
let jsonObj = JSON.parse(data[key]);
|
|
|
jsonObj.forEach(item => {
|
|
|
// 遍历对象中的每个键值对
|
|
|
for (const key in item) {
|
|
|
if (typeof item[key] === 'string') {
|
|
|
try {
|
|
|
// 尝试解析字符串为 JSON 对象
|
|
|
const parsedValue = JSONBigint({ storeAsString: true }).parse(item[key]);
|
|
|
// 如果解析成功,替换原始字符串
|
|
|
item[key] = parsedValue;
|
|
|
} catch (e) {
|
|
|
// 如果解析失败,继续保持原始字符串
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
})
|
|
|
if (this.form.hasOwnProperty(key)) {
|
|
|
this.form[key] = jsonObj;
|
|
|
}
|
|
|
} catch (err) {
|
|
|
if (this.form.hasOwnProperty(key)) {
|
|
|
if (data[key] instanceof Array) {
|
|
|
if (data[key].length > 0 && data[key][0].hasOwnProperty('url')) {
|
|
|
this.form[key] = data[key].map(i => ({
|
|
|
name: i.original_name,
|
|
|
url: i.url,
|
|
|
response: i,
|
|
|
}))
|
|
|
} else {
|
|
|
this.form[key] = ''
|
|
|
}
|
|
|
}
|
|
|
this.form[key] = data[key];
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
this.form = Object.assign({}, this.form);
|
|
|
loading.close();
|
|
|
} catch (err) {
|
|
|
console.error(err);
|
|
|
this.$message.error("配置失败");
|
|
|
loading.close();
|
|
|
}
|
|
|
} else if (!this.$route.query.flow_id) {
|
|
|
// 新建
|
|
|
try {
|
|
|
this.csrf_token = generateRandomString()
|
|
|
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.readableFields = /\/detail/.test(this.$route.path)
|
|
|
? this.fields?.map((i) => i.id)
|
|
|
: this.config?.currentNode?.readable || [];
|
|
|
this.writeableFields = this.config?.currentNode?.writeable || [];
|
|
|
this.generateForm(this.form, fields);
|
|
|
this.handleDefaultJSON();
|
|
|
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.readableFields = /\/detail/.test(this.$route.path)
|
|
|
? this.fields?.map((i) => i.id)
|
|
|
: this.config?.currentNode?.readable || [];
|
|
|
this.writeableFields = this.config?.currentNode?.writeable || [];
|
|
|
this.generateForm(this.form, fields);
|
|
|
this.handleDefaultJSON();
|
|
|
const { data } = res?.flow;
|
|
|
for (let key in data) {
|
|
|
try {
|
|
|
let jsonObj = JSON.parse(data[key]);
|
|
|
jsonObj.forEach(item => {
|
|
|
// 遍历对象中的每个键值对
|
|
|
for (const key in item) {
|
|
|
if (typeof item[key] === 'string') {
|
|
|
try {
|
|
|
// 尝试解析字符串为 JSON 对象
|
|
|
const parsedValue = JSONBigint({ storeAsString: true }).parse(item[key]);
|
|
|
// 如果解析成功,替换原始字符串
|
|
|
item[key] = parsedValue;
|
|
|
} catch (e) {
|
|
|
// 如果解析失败,继续保持原始字符串
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
})
|
|
|
if (this.form.hasOwnProperty(key)) {
|
|
|
this.form[key] = jsonObj;
|
|
|
}
|
|
|
} catch (err) {
|
|
|
if (this.form.hasOwnProperty(key)) {
|
|
|
if (data[key] instanceof Array) {
|
|
|
if (data[key].length > 0) {
|
|
|
this.form[key] = data[key];
|
|
|
} else {
|
|
|
this.form[key] = ''
|
|
|
}
|
|
|
}
|
|
|
if (data[key] && data[key] !== 'null' && data[key] !== 'undefined') {
|
|
|
this.form[key] = data[key];
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
this.form = Object.assign({}, this.form);
|
|
|
loading.close();
|
|
|
} catch (err) {
|
|
|
console.error(err);
|
|
|
this.$message.error("配置失败");
|
|
|
loading.close();
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
|
|
|
async submit(type) {
|
|
|
if (window.$_uploading) {
|
|
|
this.$message.warning("文件正在上传中")
|
|
|
return
|
|
|
}
|
|
|
let copyForm;
|
|
|
try {
|
|
|
await this.$refs['desktopForm'].validate()
|
|
|
} catch (err) {
|
|
|
console.warn(err)
|
|
|
this.$message.warning('数据校验失败')
|
|
|
return
|
|
|
}
|
|
|
|
|
|
copyForm = deepCopy(this.$refs["desktopForm"].form);
|
|
|
const uploadHandler = (form, fields) => {
|
|
|
let keys = Object.keys(form)
|
|
|
if(fields){
|
|
|
keys.forEach(key => {
|
|
|
if (form[key] instanceof Array) {
|
|
|
if (form[key].length > 0 && typeof form[key][0] === 'object') {
|
|
|
console.log("key",key,fields)
|
|
|
const myField = fields.find(field => field.name === key)
|
|
|
if (myField) {
|
|
|
if (myField.type === 'file') {
|
|
|
form[key] = form[key].map(i => i.hasOwnProperty('response') ? i.response : i)
|
|
|
} else {
|
|
|
form[key].forEach(i => {
|
|
|
uploadHandler(i, this.subConfig.get(myField.sub_custom_model_id)?.customModel?.fields)
|
|
|
})
|
|
|
}
|
|
|
}
|
|
|
} else if (form[key].length > 0) {
|
|
|
|
|
|
} else {
|
|
|
form[key] = ''
|
|
|
}
|
|
|
} else {
|
|
|
if (form[key] === 'null' || form[key] === 'undefined') {
|
|
|
form[key] = ''
|
|
|
}
|
|
|
}
|
|
|
})
|
|
|
}
|
|
|
|
|
|
}
|
|
|
console.log("copyForm",copyForm,this.fields)
|
|
|
console.log("this.writeableFields",this.writeableFields)
|
|
|
uploadHandler(copyForm, this.fields)
|
|
|
for (let key in copyForm) {
|
|
|
let myField = this.fields.find(i => i.name === key)
|
|
|
|
|
|
if (myField && this.writeableFields.indexOf(myField.id) === -1) {
|
|
|
delete copyForm[key]
|
|
|
}
|
|
|
}
|
|
|
|
|
|
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;
|
|
|
}
|
|
|
console.log("copyForm",copyForm,this.$refs["desktopForm"].form)
|
|
|
// 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 {
|
|
|
copyForm['csrf_token'] = this.csrf_token
|
|
|
this.result = await create(this.$route.query.module_id, copyForm);
|
|
|
}
|
|
|
callback();
|
|
|
} catch (err) {
|
|
|
console.error(err);
|
|
|
}
|
|
|
},
|
|
|
|
|
|
async cellDblclickEvent({ row, column }) {
|
|
|
// if(this.$store.state.user.username !== 'admin') return
|
|
|
if(!this.$store.state.user.roles.includes("全局流程监管")) return
|
|
|
if(column.field === 'created_at' || column.field === 'updated_at') {
|
|
|
this.timeId = row.id
|
|
|
this.selectedDateType = column.field
|
|
|
this.selectedDateTime = row[column.field]
|
|
|
this.isShowTime = true
|
|
|
}
|
|
|
if(column.field === "user.name"){
|
|
|
this.currentRowId = row.id
|
|
|
this.selectedUserId = row.user ? row.user.id : ''
|
|
|
this.isShowUserDialog = true
|
|
|
this.getUserList()
|
|
|
}
|
|
|
},
|
|
|
updateTime() {
|
|
|
updateNodeTime({
|
|
|
id: this.timeId,
|
|
|
date: this.selectedDateTime,
|
|
|
date_type: this.selectedDateType
|
|
|
}).then(_ => {
|
|
|
this.$message.success('更新成功')
|
|
|
this.timeId = ''
|
|
|
this.selectedDateTime = ''
|
|
|
this.selectedDateType = ''
|
|
|
this.isShowTime = false
|
|
|
this.getConfig()
|
|
|
})
|
|
|
},
|
|
|
async getUserList() {
|
|
|
try {
|
|
|
const userRes = await userListNoAuth({
|
|
|
page: 1,
|
|
|
rows: 9999,
|
|
|
})
|
|
|
this.userList = userRes.data || []
|
|
|
} catch (err) {
|
|
|
console.error(err)
|
|
|
this.$message.error('获取用户列表失败')
|
|
|
}
|
|
|
},
|
|
|
updateUser() {
|
|
|
if (!this.selectedUserId) {
|
|
|
this.$message.warning('请选择承办人员')
|
|
|
return
|
|
|
}
|
|
|
updateNodeTime({
|
|
|
id: this.currentRowId,
|
|
|
user_id: this.selectedUserId
|
|
|
}).then(_ => {
|
|
|
this.$message.success('更新成功')
|
|
|
this.currentRowId = ''
|
|
|
this.selectedUserId = ''
|
|
|
this.isShowUserDialog = false
|
|
|
this.getConfig()
|
|
|
})
|
|
|
},
|
|
|
},
|
|
|
computed: {
|
|
|
device() {
|
|
|
return this.$store.state.app.device;
|
|
|
},
|
|
|
fields() {
|
|
|
return this.config?.customModel?.fields || [];
|
|
|
},
|
|
|
// 放到data中,为了修改
|
|
|
// 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 && /\/detail/.test(this.$route.path)) {
|
|
|
return this.config?.customModel?.view_js;
|
|
|
} else if (this.config?.customModel?.js && !/\/detail/.test(this.$route.path)) {
|
|
|
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),
|
|
|
},
|
|
|
];
|
|
|
},
|
|
|
isFirstNode() {
|
|
|
return this.config?.logs?.length === 0 || this.config?.currentNode?.category === 'start'
|
|
|
}
|
|
|
},
|
|
|
created() {
|
|
|
this.getConfig();
|
|
|
},
|
|
|
mounted() {},
|
|
|
};
|
|
|
</script>
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
::v-deep .el-step__title {
|
|
|
font-size: 14px;
|
|
|
line-height: 1.5;
|
|
|
}
|
|
|
::v-deep .el-step__icon.is-icon {
|
|
|
border-radius: 100%;
|
|
|
box-shadow: 2px 0 8px 0 rgba(29, 35, 41, 0.05);
|
|
|
width: 36px;
|
|
|
height: 36px;
|
|
|
border: solid 2px;
|
|
|
}
|
|
|
::v-deep .el-step.is-center .el-step__line {
|
|
|
top: 50%;
|
|
|
}
|
|
|
::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;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
::v-deep .el-steps--horizontal {
|
|
|
display: flex;
|
|
|
flex-wrap: wrap;
|
|
|
}
|
|
|
}
|
|
|
</style>
|
|
|
<style lang="scss">
|
|
|
.log-table-scroll {
|
|
|
::-webkit-scrollbar {
|
|
|
height: 0;
|
|
|
}
|
|
|
}
|
|
|
.print-template-radios .el-radio__input:has(input[type=radio]:checked) .el-radio__inner {
|
|
|
border-color: var(--theme-color);
|
|
|
background: var(--theme-color);
|
|
|
&::after {
|
|
|
transform: translate(-50%,-50%) scale(1);
|
|
|
}
|
|
|
}
|
|
|
/** 移动端展示 **/
|
|
|
@media screen and (max-width: 500px) {
|
|
|
.el-picker-panel__sidebar {
|
|
|
width: 100%;
|
|
|
}
|
|
|
.el-picker-panel {
|
|
|
width: 400px!important;
|
|
|
}
|
|
|
.el-picker-panel__content {
|
|
|
width: 100%;
|
|
|
}
|
|
|
.el-picker-panel__body{
|
|
|
margin-left: 0!important;
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
min-width: auto!important;
|
|
|
}
|
|
|
.el-picker-panel__sidebar {
|
|
|
position: relative;
|
|
|
}
|
|
|
.el-picker-panel__body-wrapper {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
align-items: center;
|
|
|
}
|
|
|
}
|
|
|
</style>
|