xy 11 months ago
parent b6eac6a2ec
commit 138882aff4

@ -3,7 +3,7 @@ ENV = 'development'
# base api
#VUE_APP_BASE_API='http://192.167.20.118:8080/'
VUE_APP_BASE_API='http://cz-hjjc-test.115.langye.net'
VUE_APP_UPLOAD_API='http://cz-hjjc-test.115.langye.net/api/upload-file'
VUE_APP_BASE_API='https://cz-hjjc-test.115.langye.net'
VUE_APP_UPLOAD_API='https://cz-hjjc-test.115.langye.net/api/upload-file'
VUE_APP_PREVIEW=//view.langye.net/preview/onlinePreview
VUE_APP_MODULE_NAME=oa

@ -96,6 +96,15 @@ export function flowList(type,params,isLoading = false) {
})
}
// 统计
export function todoTotal() {
return request({
method: 'get',
url: '/api/oa/flow/todo-total',
isLoading: false
})
}
//流转
export function assign(flow_id, data) {
return request({

@ -196,6 +196,7 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab
'collapse-tags': true,
filterable: true,
'value-key': 'id',
'reserve-keyword': true,
'allow-create': !!info.is_select2_tag,
'default-first-option': true
},
@ -668,9 +669,9 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab
const getDetailSelectValue = () => {
let res = ''
if (!!info.multiple) {
res = target[info.name] ? target[info.name].toString()?.split(/,|\|/).map(i => isNaN(Number(i)) ? i : Number(i)) : []
res = target[info.name] ? target[info.name].toString()?.split(/,|\|/).map(i => (isNaN(Number(i)) || !i) ? i : Number(i)) : []
} else {
res = isNaN(Number(target[info.name]) || !target[info.name]) ? target[info.name] : Number(target[info.name])
res = (isNaN(Number(target[info.name]))|| !target[info.name]) ? target[info.name] : Number(target[info.name])
}
if (res instanceof Array) {
if (typeof options[0] === 'object') {
@ -857,30 +858,32 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab
const data = JSON.parse(log.data)
Object.entries(data)?.forEach(([key, value]) => {
if (value.hasOwnProperty('custom_field_id') && (value['custom_field_id'] === info.id)) {
isJointly = log.is_jointly_sign
jointlySignContent.push(h('div',[
h('span', value.value),
h('br'),
info.is_sign ? (
(log.user.sign_file && log.user.sign_file.url) ? h('el-image',{
style: {
'max-height': '80px',
'max-width': '120px'
},
props: {
src: log.user?.sign_file?.url,
fit: 'contain',
alt: log.user?.name
},
attrs: {
src: log.user?.sign_file?.url,
alt: log.user?.name
}
}) : h('span', log.user?.name)
) : '',
info.is_sign ? h('br') : '',
h('span', log.updated_at ? this.$moment(log.updated_at).format('YYYY年MM月DD') : '')
]))
isJointly = !!log.is_jointly_sign
if (log.status) {
jointlySignContent.push(h('div',[
h('span', value.value),
h('br'),
(info.is_sign) ? (
(log.user.sign_file && log.user.sign_file.url) ? h('el-image',{
style: {
'max-height': '80px',
'max-width': '120px'
},
props: {
src: log.user?.sign_file?.url,
fit: 'contain',
alt: log.user?.name
},
attrs: {
src: log.user?.sign_file?.url,
alt: log.user?.name
}
}) : h('span', log.user?.name)
) : '',
info.is_sign ? h('br') : '',
h('span', log.updated_at ? this.$moment(log.updated_at).format('YYYY年MM月DD') : '')
]))
}
}
})
})
@ -895,16 +898,29 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab
'label-width': !info.label_show ? '0': '',
},
style: {
// +1为了工作标题往下顺延
"grid-column-start": info.gs_x + 1,
"grid-column-end": info.gs_x + 1 + info.gs_width,
"grid-row-start": info.gs_y + 1 + (info.name === 'flow_title' ? 0 : 1),
"grid-row-end": info.gs_y + 1 + (info.name === 'flow_title' ? 0 : 1) + info.gs_height,
},
},
isJointly ? [(/\/detail/.test(this.$route.path) && this.$route.query.flow_id && jointlySignContent.length > 0) ? '' : formItem,jointlySignContent] : [formItem,(() => {
isJointly ? [(info._writeable) ? formItem : '',jointlySignContent] : [(() => {
if (info.name === 'flow_title') {
return formItem
}
if (info._writeable) {
return formItem
} else {
let log = this.logs.find(log => log.node?.fields?.findIndex(field => field?.field_id === info.id && field.type === 'write') !== -1 && (target[info.name]))
if (log && log.status) {
return formItem
}
}
})(),(() => {
if (info.is_sign) {
let log = this.logs.find(log => log.node?.fields?.findIndex(field => field?.field?.name === info.name && field.type === 'write') !== -1 && (target[info.name]))
if (log && log.user) {
let log = this.logs.find(log => log.node?.fields?.findIndex(field => field?.field_id === info.id && field.type === 'write') !== -1 && (target[info.name]))
if (log && log.status && log.user) {
return h('div',[
(log.user.sign_file && log.user.sign_file.url) ? h('el-image',{
style: {
@ -1639,7 +1655,7 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab
jointlySignContent.push(h('div',[
h('span', value.value),
h('br'),
info.is_sign ? (
(info.is_sign && log.status) ? (
(log.user.sign_file && log.user.sign_file.url) ? h('el-image',{
style: {
'max-height': '80px',
@ -1665,7 +1681,7 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab
return isJointly ? [(/\/detail/.test(this.$route.path) && this.$route.query.flow_id && jointlySignContent.length > 0) ? '' : formItem,jointlySignContent] : [formItem,(() => {
if (info.is_sign) {
let log = this.logs.find(log => log.node?.fields?.findIndex(field => field?.field?.name === info.name && field.type === 'write') !== -1 && (target[info.name]))
if (log && log.user) {
if (log && log.status && log.user) {
return h('van-cell',[
(log.user.sign_file && log.user.sign_file.url) ? h('el-image',{
style: {

@ -15,6 +15,10 @@ export default {
type: Boolean,
default: true,
},
needFlowTitle: {
type: Boolean,
default: true
},
readable: {
type: Array,
default: () => [],
@ -246,18 +250,20 @@ export default {
this.readable.indexOf(field.id) !== -1 || field.type === "label",
_writeable: this.writeable.indexOf(field.id) !== -1,
}));
authFields.unshift({
name: "flow_title",
label: "工作名称",
type: "text",
gs_x: 0,
gs_y: 0,
gs_width: 12,
gs_height: 1,
label_show: 1,
_readable: !this.isFirstNode,
_writeable: this.isFirstNode,
});
if (this.needFlowTitle) {
authFields.unshift({
name: "flow_title",
label: "工作名称",
type: "text",
gs_x: 0,
gs_y: 0,
gs_width: 12,
gs_height: 1,
label_show: 1,
_readable: !this.isFirstNode,
_writeable: this.isFirstNode,
});
}
return h("div", [
h(
"el-form",

@ -18,6 +18,10 @@ export default {
type: Boolean,
default: true
},
needFlowTitle: {
type: Boolean,
default: true
},
readable: {
type: Array,
default: () => [],
@ -170,6 +174,16 @@ export default {
_writeable: this.writeable.indexOf(field.id) !== -1
}))
const subFields = Array.from(this.subForm).map(i => i[1]?.customModel?.fields).filter(i => i).flat()
if (this.needFlowTitle) {
authFields.unshift({
name: "flow_title",
label: "工作名称",
type: "text",
label_show: 1,
_readable: !this.isFirstNode,
_writeable: this.isFirstNode,
});
}
return h('div',[
h('van-form',{
ref: 'vanForm',

@ -25,7 +25,7 @@
:export-config="{}"
:row-config="{ keyField: 'id' }"
:custom-config="{ mode: 'popup' }"
:data="flows"
:data="dealFlows"
>
<vxe-column
min-width="140"
@ -79,25 +79,79 @@
"
></vxe-column>
</vxe-table>
<template v-if="device === 'desktop'">
<DesktopForm
:device="device"
ref="desktopForm"
:config="config"
:is-first-node="isFirstNode"
:need-flow-title="false"
:sub-form="subConfig"
:fields="fields"
:original-form="form"
:readable="[]"
:script-content="scriptContent"
:writeable="writeableFields"
:rules="rules"
:sub-rules="subRules"
:logs="config.logs"
></DesktopForm>
</template>
<template v-else>
<MobileForm
:device="device"
ref="mobileForm"
:config="config"
:is-first-node="isFirstNode"
:need-flow-title="false"
:sub-form="subConfig"
:fields="fields"
:original-form="form"
:readable="[]"
:script-content="scriptContent"
:writeable="writeableFields"
:rules="rules"
:sub-rules="subRules"
:logs="config.logs"
></MobileForm>
</template>
<template #footer>
<el-button type="primary" :loading="loading" @click="">确认</el-button>
<el-button
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>
</template>
</vxe-modal>
<assign :visible.sync="isShowAssign" :result="result" multiple :multipleIds="dealFlows.map(flow => flow.id)" @refresh="$emit('update:isShow', false),$emit('refresh')" />
</div>
</template>
<script>
import { PopupManager } from 'element-ui/lib/utils/popup'
import { defaultModalSize } from "@/settings";
import { defaultModalSize } from '@/settings'
import {create, deal, fieldConfig, preDeal} from '@/api/flow'
import { validation, validationName } from "@/utils/validate";
import MobileForm from "@/views/flow/MobileForm.vue";
import DesktopForm from "@/views/flow/DesktopForm.vue";
import assign from './assign.vue'
import {deepCopy} from "@/utils";
export default {
components: {
DesktopForm, MobileForm,assign
},
props: {
isShow: {
type: Boolean,
default: false
},
flows: {
dealFlows: {
type: Array,
default: () => []
}
@ -107,14 +161,317 @@ export default {
loading: false,
defaultModalSize,
zIndex: PopupManager.nextZIndex(),
form: {},
subConfig: new Map(),
config: {},
rules: {},
subRules: {},
flows: [],
isShowAssign: false,
result: {},
}
},
methods: {
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",
pattern: validation.get(rule),
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: ({ cellValue }) => {
return new Promise((resolve, reject) => {
if (validation.get(rule).test(cellValue) || cellValue === '') {
resolve()
} else {
reject(
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`] = {}
object[field.name] = [{}];
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')}`
},
async getConfig() {
const firstFlow = this.dealFlows[0]
const loading = this.$loading({
lock: true,
text: "拼命加载中",
spinner: "el-icon-loading",
background: "rgba(0, 0, 0, 0.8)",
});
try {
const res = await preDeal(firstFlow.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);
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 = JSON.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;
if (this.device === "desktop") {
try {
await this.$refs['desktopForm'].validate()
} catch (err) {
console.warn(err)
this.$message.warning('数据校验失败')
return
}
copyForm = deepCopy(this.$refs["desktopForm"].form);
} else {
try {
await this.$refs['mobileForm'].validate()
} catch (err) {
console.warn(err)
this.$message.warning('数据校验失败')
return
}
copyForm = deepCopy(this.$refs["mobileForm"].form);
}
const uploadHandler = (form, fields) => {
let keys = Object.keys(form)
keys.forEach(key => {
if (form[key] instanceof Array) {
if (form[key].length > 0 && typeof form[key][0] === 'object') {
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 {
form[key] = ''
}
} else {
if (form[key] === 'null' || form[key] === 'undefined') {
form[key] = ''
}
}
})
}
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.$emit('update:isShow', false),this.$emit('update:isShow', false))
break;
case "assign":
if (this.$route.query.flow_id) {
copyForm["temporary_save"] = 0;
}
callback = () => (this.isShowAssign = true);
break;
}
const resArr = await Promise.all(this.dealFlows.map(flow => deal(flow.id, copyForm)))
const { flow, is_last_handled_log } = resArr[0]
this.result = flow;
if (!is_last_handled_log) {
await this.$alert(
"办理成功,其他会签办理完成后,由最后办理的成员流转到下一节点。",
"提示",
{
showClose: false,
}
);
callback = () => (this.$emit('update:isShow', false),this.$emit('update:isShow', false))
}
callback();
} catch (err) {
console.error(err);
}
},
},
computed: {
device() {
return this.$store.state.app.device;
},
fields() {
return this.config?.customModel?.fields || [];
},
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;
}
},
readableFields() {
return []
},
writeableFields() {
return this.config?.currentNode?.writeable || [];
},
isFirstNode() {
return this.config?.logs?.length === 0 || this.config?.currentNode?.category === 'start'
}
},
methods: {},
computed: {},
watch: {
isShow(newVal) {
if (newVal) {
this.zIndex = PopupManager.nextZIndex()
if (this.dealFlows instanceof Array && this.dealFlows.length > 0) {
this.getConfig()
}
}
}
}

@ -114,6 +114,14 @@ import {defaultModalSize} from "@/settings";
export default {
props: {
multiple: {
type: Boolean,
default: false
},
multipleIds: {
type: Array,
default: () => []
},
visible: {
type: Boolean,
default: false,
@ -229,11 +237,20 @@ export default {
async submit() {
try {
if(this.isLastNode) {
await assign(this.result.id)
if (this.multiple) {
await Promise.all(this.multipleIds.map(i => assign(i)))
} else {
await assign(this.result.id)
}
} else {
await assign(this.result.id, this.form)
if (this.multiple) {
await Promise.all(this.multipleIds.map(i => assign(i, this.form)))
} else {
await assign(this.result.id, this.form)
}
}
this.$router.push("/flow/list/handled");
this.$emit('update:visible', false)
this.$emit('refresh')
} catch(err) {
}

@ -63,17 +63,28 @@
@click="getList"
>搜索</el-button
>
<!-- <el-button-->
<!-- icon="el-icon-edit"-->
<!-- type="primary"-->
<!-- size="small"-->
<!-- plain-->
<!-- @click="multiDeal"-->
<!-- >批量审批</el-button-->
<!-- >-->
<el-button
v-if="$route.path === '/flow/list/todo'"
icon="el-icon-edit"
type="primary"
size="small"
plain
@click="multiDeal"
>批量审批</el-button
>
</div>
</template>
</vxe-toolbar>
<div class="todo-statistics" v-if="$route.path === '/flow/list/todo'">
<el-tag
v-for="(model, index) in todoTotal"
:key="model.custom_model_id"
:effect="select.custom_model_id === model.custom_model_id ? 'dark' : 'plain'"
:type="['success', 'info', 'warning', 'danger'][index%4]"
@click="select.custom_model_id = model.custom_model_id,getList()">
{{ model.custom_model ? model.custom_model.name : '' }}{{ model.total }}
</el-tag>
</div>
</template>
<template #default>
@ -90,17 +101,33 @@
:export-config="{}"
:expand-config="{
accordion: true,
padding: true
padding: true,
}"
:checkbox-config="{
reserve: true,
highlight: true
highlight: true,
range: true,
checkMethod: ({ row }) => {
if ($route.path !== '/flow/list/todo') {
return true
} else {
if (!select.custom_model_id) return false
if (multiDealFlows.length === 0) {
return true
} else {
const firstFlow = multiDealFlows[0]
return row.current_node.id === firstFlow.current_node.id
}
}
}
}"
:row-config="{ keyField: 'id' }"
:custom-config="{ mode: 'popup' }"
:data="list"
@cell-click="cellClickEvent"
@cell-dblclick="cellDblclickEvent"
@checkbox-change="checkboxChange"
@checkbox-all="checkboxChange"
>
<vxe-column type="checkbox" width="50" align="center"></vxe-column>
<vxe-column :visible="$store.getters.device === 'mobile'" field="m-operate" type="expand" title="操作" width="60" align="center">
@ -150,7 +177,7 @@
header-align="center"
field="title"
title="工作名称"
:title-prefix="{ content: '点击工作名称查看简要' }"
:title-prefix="{ content: '点击工作名称查看简要。\n批量审批请先选择流程类型并勾选同一节点的流程。' }"
></vxe-column>
<vxe-column
:visible="$route.params.type === 'all'"
@ -306,7 +333,7 @@
}
"
:current-page.sync="select.page"
:page-sizes="[10, 20, 30, 40, 50]"
:page-sizes="[10, 20, 30, 50, 100]"
:page-size.sync="select.page_size"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@ -317,12 +344,12 @@
<share ref="share" :is-show.sync="isShowShare" :flow="pickedFlow"></share>
<list-popover :is-show.sync="isShowListPopover" :id="listPopoverId" :pos="listPopoverPos" />
<multi-deal :is-show.sync="isShowMultiDeal" :flows="multiDealFlows" @refresh="getList" />
<multi-deal :is-show.sync="isShowMultiDeal" :deal-flows="multiDealFlows" @refresh="getList" />
</div>
</template>
<script>
import { flowList, toggleFav, destroy, recall, updateFlowTime } from "@/api/flow";
import { flowList, toggleFav, destroy, recall, updateFlowTime, todoTotal } from "@/api/flow";
import moment from "moment/moment";
import ListPopover from "./components/ListPopover.vue";
import MultiDeal from "./components/MultiDeal.vue"
@ -336,6 +363,7 @@ export default {
},
data() {
return {
todoTotal: [],
isShowMultiDeal: false,
multiDealFlows: [],
@ -494,6 +522,7 @@ export default {
},
async getList() {
if (this.loading) return
this.loading = true;
try {
const res = await flowList(this.$route.params.type, this.select, false);
@ -529,20 +558,55 @@ export default {
}
},
multiDeal() {
checkboxChange() {
const reserveRecords = this.$refs['table']?.getCheckboxReserveRecords() || []
const records = this.$refs['table']?.getCheckboxRecords(true) || []
this.multiDealFlows = [...reserveRecords, ...records]
if (this.multiDealFlows.length > 0) {
const firstFlow = this.multiDealFlows[0]
this.multiDealFlows = this.multiDealFlows.filter(flow => {
if (flow.current_node.id === firstFlow.current_node.id) {
return true
} else {
this.$refs['table'].setCheckboxRow(flow, false)
}
})
}
},
async getTodoTotal() {
try {
const res = await todoTotal()
console.log(res)
this.todoTotal = res?.todo || []
} catch (err) {
console.error(err)
}
},
multiDeal() {
if (this.multiDealFlows.length === 0) {
this.$message.warning('请先勾选需要审批的流程')
return
}
this.isShowMultiDeal = true
}
},
computed: {
},
watch: {
'select.custom_model_id': {
handler: function () {
this.$refs['table'].clearCheckboxRow()
this.$refs['table'].clearCheckboxReserve()
this.multiDealFlows = []
}
}
},
activated() {
this.getTodoTotal();
this.getList();
},
created() {
this.getTodoTotal();
this.getList();
},
mounted() {
@ -559,4 +623,10 @@ export default {
.selects > * + * {
margin: 2px 0 2px 6px;
}
.el-tag {
cursor: pointer;
}
.el-tag + .el-tag {
margin-left: 4px;
}
</style>

Loading…
Cancel
Save