xy 9 months ago
parent 7844b639f6
commit af6be7ede1

@ -8,6 +8,16 @@ import { flowList } from "@/api/flow";
import MobilePicker from '@/components/MobilePicker/index.vue'; import MobilePicker from '@/components/MobilePicker/index.vue';
import MobileMultipleSelect from "@/components/MobileMultipleSelect/index.vue"; import MobileMultipleSelect from "@/components/MobileMultipleSelect/index.vue";
import { Message } from 'element-ui' import { Message } from 'element-ui'
function isJSON(str) {
if (typeof str !== 'string') return false;
try {
const result = JSON.parse(str);
const type = Object.prototype.toString.call(result);
return type === '[object Object]' || type === '[object Array]';
} catch (e) {
return false;
}
}
/** /**
* @param {String} device 'desktop' | 'mobile' * @param {String} device 'desktop' | 'mobile'
* @param {Object} info field参数 * @param {Object} info field参数
@ -428,12 +438,17 @@ export default function formBuilder(
break; break;
case "relation-flow": case "relation-flow":
if (!this.flows[info.name]) { if (!this.flows[info.name]) {
let extraParam = {}
if (isJSON(info.stub)) {
extraParam = JSON.parse(info.stub)
}
flowList("all", { flowList("all", {
page: 1, page: 1,
page_size: 9999, page_size: 9999,
is_simple: 1, is_simple: 1,
custom_model_id: info.stub, custom_model_id: isJSON(info.stub) ? '' : info.stub,
is_auth: 1, is_auth: 1,
...extraParam
}).then((res) => { }).then((res) => {
this.$set(this.flows, info.name, res.data.data); this.$set(this.flows, info.name, res.data.data);
}); });
@ -441,6 +456,7 @@ export default function formBuilder(
formItem = h( formItem = h(
"el-select", "el-select",
{ {
ref: `relation-flow-${info.name}`,
props: { props: {
value: target[info.name] value: target[info.name]
? target[info.name] ? target[info.name]
@ -484,13 +500,42 @@ export default function formBuilder(
h("div", { h("div", {
}, [ }, [
h("span", {},option.title), h("span", {},option.title),
h("span", { h("el-button", {
style: { style: {
color: '#999', float: 'right'
float: 'right', },
'font-size': '12px' props: {
type: 'primary',
size: 'mini',
icon: 'el-icon-search'
},
on: {
click: e => {
e.stopPropagation()
let target = this.$router.resolve({
path: "/flow/detail",
query: {
module_id: option.custom_model_id,
flow_id: option.id,
isSinglePage: 1,
},
});
this.modalRender = (h) =>
h("iframe", {
attrs: {
src: target.href,
},
style: {
border: "none",
width: "100%",
height: "100%",
},
});
this.isShowModal = true;
this.$refs[`relation-flow-${info.name}`]?.blur()
}
} }
},option.no) }, '查看')
]) ])
]) ])
) )
@ -728,12 +773,17 @@ export default function formBuilder(
break; break;
case "relation-flow": case "relation-flow":
if (!this.flows[info.name]) { if (!this.flows[info.name]) {
let extraParam = {}
if (isJSON(info.stub)) {
extraParam = JSON.parse(info.stub)
}
flowList("all", { flowList("all", {
page: 1, page: 1,
page_size: 9999, page_size: 9999,
is_simple: 1, is_simple: 1,
custom_model_id: info.stub, custom_model_id: isJSON(info.stub) ? '' : info.stub,
is_auth: 1, is_auth: 1,
...extraParam
}).then((res) => { }).then((res) => {
this.$set(this.flows, info.name, res.data.data); this.$set(this.flows, info.name, res.data.data);
}); });
@ -1356,12 +1406,17 @@ export default function formBuilder(
break; break;
case "relation-flow": case "relation-flow":
if (!this.flows[info.name]) { if (!this.flows[info.name]) {
let extraParam = {}
if (isJSON(info.stub)) {
extraParam = JSON.parse(info.stub)
}
flowList("all", { flowList("all", {
page: 1, page: 1,
page_size: 9999, page_size: 9999,
is_simple: 1, is_simple: 1,
custom_model_id: info.stub, custom_model_id: isJSON(info.stub) ? '' : info.stub,
is_auth: 1, is_auth: 1,
...extraParam
}).then((res) => { }).then((res) => {
this.$set(this.flows, info.name, res.data.data); this.$set(this.flows, info.name, res.data.data);
}); });
@ -1670,12 +1725,17 @@ export default function formBuilder(
break; break;
case "relation-flow": case "relation-flow":
if (!this.flows[info.name]) { if (!this.flows[info.name]) {
let extraParam = {}
if (isJSON(info.stub)) {
extraParam = JSON.parse(info.stub)
}
flowList("all", { flowList("all", {
page: 1, page: 1,
page_size: 9999, page_size: 9999,
is_simple: 1, is_simple: 1,
custom_model_id: info.stub, custom_model_id: isJSON(info.stub) ? '' : info.stub,
is_auth: 1, is_auth: 1,
...extraParam
}).then((res) => { }).then((res) => {
this.$set(this.flows, info.name, res.data.data); this.$set(this.flows, info.name, res.data.data);
}); });

@ -15,6 +15,30 @@
</el-date-picker> </el-date-picker>
<el-button icon="el-icon-search" type="primary" plain size="small" style="margin-left: 10px;" @click="getList"></el-button> <el-button icon="el-icon-search" type="primary" plain size="small" style="margin-left: 10px;" @click="getList"></el-button>
<el-button icon="el-icon-plus" type="primary" size="small" style="margin-left: 10px;" @click="isShowAdd = true">补卡</el-button> <el-button icon="el-icon-plus" type="primary" size="small" style="margin-left: 10px;" @click="isShowAdd = true">补卡</el-button>
<el-popover
placement="bottom-start"
title="导入"
width="400"
trigger="click">
<template #reference>
<el-button style="margin-left: 10px;" type="primary" size="small" icon="el-icon-upload">导入</el-button>
</template>
<template #default>
<el-upload
ref="upload"
:action="action"
:on-success="uploadSuccess"
accept=".xls,.xlsx"
:headers="{
'Authorization': 'Bearer ' + getToken()
}"
:auto-upload="false">
<el-button slot="trigger" size="small" type="primary">选取文件</el-button>
<el-button style="margin-left: 10px;" size="small" type="success" @click="uploadData"></el-button>
<div slot="tip" class="el-upload__tip">只能上传xls/xlsx文件</div>
</el-upload>
</template>
</el-popover>
</template> </template>
</vxe-toolbar> </vxe-toolbar>
<vxe-table <vxe-table
@ -28,24 +52,45 @@
:min-height="400" :min-height="400"
:export-config="{}" :export-config="{}"
:print-config="{}" :print-config="{}"
:edit-config="{ trigger: 'manual', mode: 'row', showStatus: true, autoClear: false, expandALl: true }"
align="center" align="center"
:column-config="{ resizable: true }" :column-config="{ resizable: true }"
:data="tableData" :data="tableData"
> >
<vxe-column type="seq" width="58" align="center" /> <vxe-column type="seq" width="58" align="center" />
<vxe-column width="120" field="date" title="日期" align="center" /> <vxe-column width="120" field="date" title="日期" align="center" />
<vxe-column field="attendance.sign_in_at" title="签到时间" width="200"></vxe-column> <vxe-column field="attendance.sign_in_at" title="签到时间" width="200" :edit-render="{}">
<vxe-column field="attendance.sign_in_at_address" title="签到地址" width="140"></vxe-column> <template #edit="{ row }">
<vxe-column field="attendance.sign_in_at_location" title="签到坐标" width="140"></vxe-column> <el-date-picker style="width: 100%;" v-model="row.attendance.sign_in_at" type="datetime" value-format="yyyy-MM-dd HH:mm:ss" size="small"></el-date-picker>
</template>
</vxe-column>
<vxe-column field="attendance.sign_in_at_address" title="签到地址" width="140" :edit-render="{ name: 'input' }"></vxe-column>
<vxe-column field="attendance.sign_in_at_location" title="签到坐标" width="140" :edit-render="{ name: 'input' }"></vxe-column>
<vxe-column field="attendance.sign_out_at" title="签退时间" width="200"></vxe-column> <vxe-column field="attendance.sign_out_at" title="签退时间" width="200" :edit-render="{}">
<vxe-column field="attendance.sign_out_at_address" title="签退地址" width="140"></vxe-column> <template #edit="{ row }">
<vxe-column field="attendance.sign_out_at_location" title="签退坐标" width="140"></vxe-column> <el-date-picker style="width: 100%;" v-model="row.attendance.sign_out_at" type="datetime" value-format="yyyy-MM-dd HH:mm:ss" size="small"></el-date-picker>
</template>
</vxe-column>
<vxe-column field="attendance.sign_out_at_address" title="签退地址" width="140" :edit-render="{ name: 'input' }"></vxe-column>
<vxe-column field="attendance.sign_out_at_location" title="签退坐标" width="140" :edit-render="{ name: 'input' }"></vxe-column>
<vxe-column field="attendance.status" title="类型" width="140" :edit-render="{ name: 'select', options: [{ label: '-',value: ''},{ label: '',value: 1},{ label: '',value: 2},{ label: '',value: 3}] }" />
<vxe-column fixed="right" field="operate" title="操作" min-width="160" header-align="center" align="left">
<template #default="{ row }">
<template v-if="isActiveStatus(row)">
<el-button size="small" type="primary" @click="saveRowEvent(row)"></el-button>
<el-button size="small" type="primary" plain @click="cancelRowEvent(row)"></el-button>
</template>
<template v-else>
<el-button size="small" type="warning" @click="editRowEvent(row)"></el-button>
</template>
</template>
</vxe-column>
</vxe-table> </vxe-table>
</card-container> </card-container>
<AddSign :is-show.sync="isShowAdd" :users="users"></AddSign> <AddSign :is-show.sync="isShowAdd" :users="users" @refresh="getList"></AddSign>
</div> </div>
</template> </template>
@ -53,12 +98,18 @@
import { userListNoAuth } from "@/api/common"; import { userListNoAuth } from "@/api/common";
import { index } from '@/api/attendance'; import { index } from '@/api/attendance';
import AddSign from "./components/AddSign.vue"; import AddSign from "./components/AddSign.vue";
import { deepCopy } from "@/utils";
import { updateSign } from "@/api/attendance";
import { getToken } from "@/utils/auth";
export default { export default {
components: { components: {
AddSign AddSign
}, },
data() { data() {
return { return {
action: `${process.env.VUE_APP_BASE_API}/api/oa/statistics/import-overtime`,
isShowAdd: false, isShowAdd: false,
select: { select: {
month: this.$moment().format('YYYY-MM'), month: this.$moment().format('YYYY-MM'),
@ -70,6 +121,59 @@ export default {
} }
}, },
methods: { methods: {
getToken,
uploadData() {
this.$refs.upload.submit();
},
uploadSuccess(response) {
console.log(response)
if (response.hasOwnProperty('errcode')) {
this.$message.error(response.errmsg)
} else {
this.$message.success(`已导入${response.total}`)
this.getList(true)
}
},
editRowEvent(row) {
if (!row.attendance.id) {
this.$message.warning("当前日期无打卡")
return
}
if (this.$refs['table']) {
this.$refs['table'].setEditRow(row)
}
},
cancelRowEvent(row) {
if (this.$refs['table']) {
this.$refs['table'].clearEdit().then(() => {
//
this.$refs['table'].revertData(row)
})
}
},
async saveRowEvent(row) {
try {
await this.$confirm('确认保存?', '提示', {
confirmButtonText: '确认',
cancelButtonText: '取消'
})
await this.$refs['table'].clearEdit()
console.log(row)
this.loading = true
await updateSign({
user_id: this.select.user_id,
date: row.date,
sign_in_at: row.attendance.sign_in_at,
sign_out_at: row.attendance.sign_out_at,
status: row.attendance.status
})
await this.getList()
this.loading = false
} catch (err) {
this.loading = false
}
},
async getUsers () { async getUsers () {
try { try {
const res = await userListNoAuth({ const res = await userListNoAuth({
@ -93,6 +197,13 @@ export default {
} }
}, },
computed: { computed: {
isActiveStatus() {
return function(row) {
if (this.$refs['table']) {
return this.$refs['table'].isEditByRow(row)
}
}
}
}, },
created() { created() {
this.getUsers() this.getUsers()

@ -1,7 +1,7 @@
<script> <script>
import {deepCopy} from "@/utils"; import {deepCopy} from "@/utils";
import formBuilder from "@/utils/formBuilder"; import formBuilder from "@/utils/formBuilder";
import {PopupManager} from "element-ui/lib/utils/popup"; import { PopupManager } from "element-ui/lib/utils/popup";
import request from '@/utils/request' import request from '@/utils/request'
import moment from "moment/moment"; import moment from "moment/moment";
import {defaultModalSize} from "@/settings"; import {defaultModalSize} from "@/settings";

@ -0,0 +1,95 @@
<template>
<div class="steps" v-if="!/\/detail/.test($route.path) && logs.length > 0">
<el-steps :space="120" finish-status="success" align-center>
<el-step
v-for="(logGroup, index) in groupedLogs"
v-if="!(logGroup[0].flow_node_id === currentNode.id && isLastGroup(index))"
:title="logGroup[0].node ? logGroup[0].node.name : '节点已调整'"
:status="logGroup[0].status !== -1 ? 'success' : 'error'"
icon="el-icon-circle-check"
>
<template #title>
<template v-if="logGroup[0].status === -1">
<el-tooltip effect="dark" :content="logGroup[0].reason" placement="bottom">
<span>{{ logGroup[0].node ? logGroup[0].node.name : '节点已调整' }}</span>
</el-tooltip>
</template>
<template v-else>
{{ logGroup[0].node ? logGroup[0].node.name : '节点已调整' }}
</template>
</template>
</el-step>
<el-step status="finish" :title="currentNode.name + '(正在办理)'" icon="el-icon-edit"></el-step>
<el-step v-if="currentNode.nextNodes && currentNode.nextNodes.length" status="wait" icon="el-icon-right">
<template #title>
<div style="max-width: 180px;overflow: hidden;white-space: nowrap;text-overflow: ellipsis;">
<span v-for="(nextNode, index) in ((currentNode.nextNodes && currentNode.nextNodes instanceof Array) ? currentNode.nextNodes : [])">{{ index === 0 ? '' : ',' }}{{ nextNode.name }}</span>
</div>
<div v-if="currentNode.nextNodes">{{ currentNode.nextNodes.length }}</div>
</template>
</el-step>
</el-steps>
<el-divider></el-divider>
</div>
</template>
<script>
export default {
props: {
logs: {
type: Array,
default: () => []
},
currentNode: {
type: Object,
default: () => ({})
}
},
data() {
return {}
},
methods: {
//
isLastGroup(index) {
return index === this.groupedLogs.length - 1;
}
},
computed: {
groupedLogs() {
const groups = {};
this.logs.forEach(log => {
const key = log.jointly_sign_id;
if (!groups[key]) {
groups[key] = [];
}
groups[key].push(log);
});
return Object.values(groups);
}
},
}
</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%;
}
@media (max-width: 768px) {
::v-deep .el-steps--horizontal {
display: flex;
flex-wrap: wrap;
}
}
</style>

@ -27,7 +27,7 @@
</el-radio> </el-radio>
</el-radio-group> </el-radio-group>
<h4>请输入退回原因</h4> <h4><span style="color: #F56C6C;"> * </span>请输入退回原因</h4>
<el-input v-model="form.reason" type="textarea" :autosize="{ minRows: 2 }"></el-input> <el-input v-model="form.reason" type="textarea" :autosize="{ minRows: 2 }"></el-input>
</div> </div>
@ -84,6 +84,10 @@ export default {
this.$message.warning("请选择退回步骤!") this.$message.warning("请选择退回步骤!")
return return
} }
if(!this.form.reason) {
this.$message.warning("请填写退回原因!")
return
}
try { try {
this.form.id = this.flow.id; this.form.id = this.flow.id;
const res = await rollback(this.form) const res = await rollback(this.form)

@ -14,37 +14,38 @@
</template> </template>
<template> <template>
<div class="steps" v-if="!/\/detail/.test($route.path)"> <Steps :logs="config.logs" :current-node="node"></Steps>
<el-steps :space="120" finish-status="success" align-center> <!-- <div class="steps" v-if="!/\/detail/.test($route.path)">-->
<template v-if="!isFirstNode"> <!-- <el-steps :space="120" finish-status="success" align-center>-->
<el-step <!-- <template v-if="!isFirstNode">-->
v-for="step in config.logs" <!-- <el-step-->
v-if="(step.flow_node_id !== node.id && step.status !== -1)" <!-- v-for="step in config.logs"-->
:title="step.node.name" <!-- v-if="(step.flow_node_id !== node.id && step.status !== -1)"-->
:status="step.status !== -1 ? 'success' : 'error'" <!-- :title="step.node.name"-->
icon="el-icon-circle-check" <!-- :status="step.status !== -1 ? 'success' : 'error'"-->
></el-step> <!-- icon="el-icon-circle-check"-->
</template> <!-- ></el-step>-->
<el-step <!-- </template>-->
:title="node.name" <!-- <el-step-->
status="finish" <!-- :title="node.name"-->
icon="el-icon-edit" <!-- status="finish"-->
></el-step> <!-- icon="el-icon-edit"-->
<el-step <!-- ></el-step>-->
icon="el-icon-right" <!-- <el-step-->
status="wait" <!-- icon="el-icon-right"-->
> <!-- status="wait"-->
<template #title> <!-- >-->
<div style="max-width: 180px;overflow: hidden;white-space: nowrap;text-overflow: ellipsis;"> <!-- <template #title>-->
<span v-for="(nextNode, index) in ((node.nextNodes && node.nextNodes instanceof Array) ? node.nextNodes : [])">{{ index === 0 ? '' : ',' }}{{ nextNode.name }}</span> <!-- <div style="max-width: 180px;overflow: hidden;white-space: nowrap;text-overflow: ellipsis;">-->
</div> <!-- <span v-for="(nextNode, index) in ((node.nextNodes && node.nextNodes instanceof Array) ? node.nextNodes : [])">{{ index === 0 ? '' : ',' }}{{ nextNode.name }}</span>-->
<div v-if="node.nextNodes">{{ node.nextNodes.length }}</div> <!-- </div>-->
</template> <!-- <div v-if="node.nextNodes">{{ node.nextNodes.length }}</div>-->
</el-step> <!-- </template>-->
</el-steps> <!-- </el-step>-->
<!-- </el-steps>-->
<el-divider></el-divider> <!-- <el-divider></el-divider>-->
</div> <!-- </div>-->
<div class="form-container" id="print-content"> <div class="form-container" id="print-content">
<DesktopForm <DesktopForm
@ -207,6 +208,14 @@
> >
</template> </template>
<template v-else> <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="$router.go(-1)"></el-button>
<el-button plain size="small" @click="print(false)"></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" @click="print(true)">()</el-button>
@ -239,6 +248,7 @@
</template> </template>
<script> <script>
import Steps from "./components/Steps.vue";
import DesktopForm from "./DesktopForm.vue"; import DesktopForm from "./DesktopForm.vue";
import MobileForm from "./MobileForm.vue"; import MobileForm from "./MobileForm.vue";
import assign from "./components/assign.vue"; import assign from "./components/assign.vue";
@ -260,6 +270,7 @@ import { print } from "@/utils/print";
import JSONBigint from 'json-bigint' import JSONBigint from 'json-bigint'
export default { export default {
components: { components: {
Steps,
DesktopForm, DesktopForm,
MobileForm, MobileForm,
assign, assign,

Loading…
Cancel
Save