xy 1 year ago
parent 46218e6bba
commit c562392f2e

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

@ -256,9 +256,10 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab
props: {
value: row[info.name],
'progress-text': "{percent}%",
'more-config': {maxCount: 1, layout: 'horizontal'},
'more-config': { maxCount: 1, layout: 'horizontal' },
'show-button-text': false,
'limit-size': 20,
'limit-count': info.multiple ? 20 : 1,
multiple: !!info.multiple,
readonly: pReadable,
'upload-method': ({ file }) => {
@ -274,9 +275,10 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab
row[info.name] = []
}
row[info.name].push({
...response.data.data,
response: response.data.data,
name: response.data.data.original_name,
url: response.data.data.url,
TYPE_FILE: 1
})
} else {
this.$message.error("上传失败")
@ -297,7 +299,8 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab
accept:
"application/msword,image/jpeg,application/pdf,image/png,application/vnd.ms-powerpoint,text/plain,application/x-zip-compressed,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document",
multiple: !!info.multiple,
fileList: this.file[info.name],
limit: info.multiple ? 20 : 1,
fileList: this.form[info.name],
beforeUpload: (file) => {
if (file.size / 1024 / 1024 > 20) {
this.$message({
@ -313,13 +316,22 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab
file.response = file.response.data;
}
});
this.file[info.name] = fileList;
this.form[info.name] = fileList.map(i => ({
...i,
TYPE_FILE: 1
}));
},
onRemove: (file, fileList) => {
this.file[info.name] = fileList;
this.form[info.name] = fileList.map(i => ({
...i,
TYPE_FILE: 1
}));
},
onError: (err, file, fileList) => {
this.file[info.name] = fileList;
this.form[info.name] = fileList.map(i => ({
...i,
TYPE_FILE: 1
}));
this.$message({
type: "warning",
message: err,
@ -362,13 +374,8 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab
class: "el-icon-close",
on: {
["click"]: () => {
this.$set(
this.file,
info.field,
this.file[info.field].filter(
(item) => item !== file
)
);
if (file.status === "uploading") return
this.form[info.name].splice(this.form[info.name].indexOf(file), 1)
},
},
}),
@ -504,10 +511,13 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab
formBuilderMap(device).get(info.type),
{
props: {
value: row ? (row[info.name] ? row[info.name].split(',').map(i => Number(i)) : []) : (this.form[info.name] ? this.form[info.name].split('|').map(i => Number(i)) : []),
value: row ? (row[info.name] ? row[info.name].split(',').map(i => Number(i)) : []) : (this.form[info.name] ? this.form[info.name].split(',').map(i => Number(i)) : []),
clearable: true,
placeholder: info.help_text,
multiple: true,
filterable: true,
'reserve-keyword': true,
loading: this.flowSelectLoading
},
attrs: {
placeholder: info.help_text,
@ -521,6 +531,23 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab
? this.$set(row, info.name, e.toString())
: this.$set(this.form, info.name, e.toString());
},
['visible-change']:e => {
if (e) {
if (this.nowSelectId === info.stub && this.flows.length > 0) return
this.flowSelectLoading = true
flowList('all', {
page: 1,
page_size: 9999,
custom_model_id: info.stub
}).then(res => {
this.nowSelectId = info.stub
this.flows = res.data.data
this.flowSelectLoading = false
}).catch(err => {
this.flowSelectLoading = false
})
}
}
},
},
this.flows.map((option) =>
@ -600,6 +627,7 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab
this.$refs[`subForm-${info.name}`];
if ($table) {
await $table.remove(row);
this.form[info.name] = $table.getTableData()?.tableData
}
},
},
@ -647,22 +675,22 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab
"edit-render": {},
},
scopedSlots: {
edit: ({ row }) => {
edit: ({ row: myrow }) => {
return formBuilder.bind(this)(
device,
subField,
h,
row,
myrow,
info._writeable,
false,
);
},
[subField.type === 'file' ? 'default' : false]: ({ row }) => {
[subField.type === 'file' ? 'default' : false]: ({ row: myrow }) => {
return formBuilder.bind(this)(
device,
subField,
h,
row,
myrow,
info._writeable,
true,
);
@ -674,7 +702,7 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab
);
break;
}
} else if (info._readable) {
} else if (info._readable || pReadable) {
switch (info.type) {
case "date":
formItem = h(
@ -771,7 +799,22 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab
);
break;
case "file":
formItem = h(
formItem = row ?
h(
'vxe-upload',
{
props: {
value: row[info.name],
'progress-text': "{percent}%",
'more-config': { maxCount: 1, layout: 'horizontal' },
'show-button-text': false,
'limit-size': 20,
'limit-count': info.multiple ? 20 : 1,
readonly: true
}
}
) :
h(
formBuilderMap(device).get(info.type),
{
props: {
@ -783,7 +826,7 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab
accept:
"application/msword,image/jpeg,application/pdf,image/png,application/vnd.ms-powerpoint,text/plain,application/x-zip-compressed,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document",
multiple: true,
fileList: this.file[info.name],
fileList: (this.form[info.name] instanceof Array) ? this.form[info.name] : [],
},
scopedSlots: {
file: (scope) => {
@ -824,7 +867,7 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab
{
slot: "trigger",
},
`数量:${this.file[info.name].length}`
`数量:${(this.form[info.name] instanceof Array) ? this.form[info.name]?.length : 0}`
),
]
);
@ -860,7 +903,29 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab
"grid-row-end": info.gs_y + 1 + (info.name === 'flow_title' ? 0 : 1) + info.gs_height,
},
},
[formItem]
[formItem,(() => {
if (info.is_sign) {
let log = this.logs.find(log => log.node?.fields?.findIndex(field => field?.field?.name === info.name) !== -1)
if (log) {
return h('div',[
h('el-image',{
style: {
'max-height': '80px'
},
props: {
src: log.user.sign_file?.url,
fit: 'contain',
alt: log.user.sign_id
},
attrs: {
src: log.user.sign_file?.url,
alt: log.user.sign_id
}
})
])
}
}
})()]
);
}
}
@ -1021,8 +1086,8 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab
},
accept:
"application/msword,image/jpeg,application/pdf,image/png,application/vnd.ms-powerpoint,text/plain,application/x-zip-compressed,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document",
multiple: true,
fileList: this.file[info.name],
multiple: !!info.multiple,
fileList: row ? row[info.name] : this.form[info.name],
beforeUpload: (file) => {
if (file.size / 1024 / 1024 > 20) {
this.$message({
@ -1038,13 +1103,13 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab
file.response = file.response.data;
}
});
this.file[info.name] = fileList;
row ? row[info.name] = fileList : this.form[info.name] = fileList;
},
onRemove: (file, fileList) => {
this.file[info.name] = fileList;
row ? row[info.name] = fileList : this.form[info.name] = fileList;
},
onError: (err, file, fileList) => {
this.file[info.name] = fileList;
row ? row[info.name] = fileList : this.form[info.name] = fileList;
this.$message({
type: "warning",
message: err,
@ -1291,7 +1356,7 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab
title: info.label,
}
},
this.file[info.name].map(file => h("div", {}, [
(row ? row[info.name] : this.form[info.name]).map(file => h("div", {}, [
h(
"a",
{

@ -22,15 +22,24 @@ export function print(printJs, isLog, form, logContent) {
while ((match = regexField.exec(printJs)) !== null) {
fieldMaths.push(match[0]); // 提取 <field> 之间的内容
}
fieldMaths.forEach(fieldMath => {
fieldMaths.forEach(async fieldMath => {
const matchName = fieldMath.match(/name="([^"]+)"/);
if (matchName) {
const nameValue = matchName[1];
if (Array.from(staticMap.keys()).indexOf(nameValue) !== -1) {
printStr = printStr.replace(fieldMath, staticMap.get(nameValue))
} else {
const value = store.getters.device === 'desktop' ? document.querySelector(`[for=${nameValue}]+div`).innerHTML : document.querySelector(`[for=${nameValue}] > div:nth-child(2)`).innerHTML
printStr = printStr.replace(fieldMath,`<span>${value}</span>`)
let value = store.getters.device === 'desktop' ? (document.querySelector(`[for="${nameValue}"]+div`) ? document.querySelector(`[for="${nameValue}"]+div`).innerHTML : '') : (document.querySelector(`[for="${nameValue}"] > div:nth-child(2)`) ? document.querySelector(`[for="${nameValue}"] > div:nth-child(2)`).innerHTML : '')
if (/<table/g.test(value)) {
let subForm = await this.$refs['desktopForm'].$refs[`subForm-${nameValue}`].exportData({
type: 'html',
download: false,
})
let subFormBody = subForm.content.match(/<table(.*?)<\/table>/g)[0]
printStr = printStr.replace(fieldMath,subFormBody)
} else {
printStr = printStr.replace(fieldMath,`<span>${value}</span>`)
}
}
} else {
console.log('未找到name属性');
@ -42,7 +51,7 @@ export function print(printJs, isLog, form, logContent) {
logStyle.forEach(item => {
totalLogStyle += item
})
printStr = printStr.replace('</style>',`</style>${totalLogStyle}`)
printStr = printStr.replace('</style>',`</style>${totalLogStyle}<style>.vxe-table { width: 100%; } .vxe-table * { width: auto !important;white-space: initial; }.vxe-table:not(.is--print) .col--ellipsis > div { word-break: break-all;white-space: normal;overflow: initial; }.tblPrint .vxe-table td,.tblPrint .vxe-table th { font-size: 20px;padding: 0; }</style>`)
const logBody = logContent.match(/<table(.*?)<\/table>/g)[0]
printStr = printStr.replace('</table>',`</talbe>${logBody}`)
}

@ -13,12 +13,13 @@
>
<el-form ref="elForm" :model="form" :rules="rules" label-position="top" label-width="100">
<el-form-item label="日期" prop="date" required>
<el-date-picker v-model="form.date" value-format="yyyy-MM-dd" />
<el-date-picker v-model="form.date" style="width: 100%;" value-format="yyyy-MM-dd" />
</el-form-item>
<el-form-item label="值班人员" prop="user_id">
<el-select
v-model="form.user_id"
style="width: 100%;"
filterable
>
<el-option v-for="user in users" :key="user.id" :value="user.id" :label="user.name" />
</el-select>
@ -27,18 +28,19 @@
<el-select
v-model="form.leader_id"
style="width: 100%;"
filterable
>
<el-option v-for="user in users" :key="user.id" :value="user.id" :label="user.name" />
</el-select>
</el-form-item>
<el-form-item label="带班领导人员状态" prop="leader_status">
<el-select v-model="form.leader_status">
<el-select v-model="form.leader_status" style="width: 100%;">
<el-option :value="0" label="未值班" />
<el-option :value="1" label="已完成值班" />
</el-select>
</el-form-item>
<el-form-item label="值班人员状态" prop="status">
<el-select v-model="form.status">
<el-select v-model="form.status" style="width: 100%;">
<el-option :value="0" label="未值班" />
<el-option :value="1" label="已完成值班" />
</el-select>
@ -76,8 +78,8 @@ export default {
date: '',
user_id: '',
leader_id: '',
leader_status: 1,
status: 1,
leader_status: 0,
status: 0,
remark: ''
},
rules: {

@ -29,6 +29,7 @@
<vxe-column field="user.name" title="值班人员" min-width="160" :edit-render="{}">
<template #edit="{ row }">
<el-select
filterable
v-model="row.user_id"
style="width: 100%;"
>
@ -39,6 +40,7 @@
<vxe-column field="leader.name" title="带班领导" min-width="160" :edit-render="{}">
<template #edit="{ row }">
<el-select
filterable
v-model="row.leader_id"
style="width: 100%;"
>

@ -5,8 +5,12 @@
<div @click="selectDay = data.day,isShow = true">
<div>{{ data.day.split('-')[2] }}</div>
<div v-if="dayAttendances(data.day) && dayAttendances(data.day).attendance" class="clock-in">
<el-tag v-if="dayAttendances(data.day).attendance.sign_in_at" effect="dark" size="mini">{{ $moment(dayAttendances(data.day).attendance.sign_in_at).format('HH:mm:ss') }}</el-tag>
<el-tag v-if="dayAttendances(data.day).attendance.sign_out_at" effect="dark" type="success" size="mini">{{ $moment(dayAttendances(data.day).attendance.sign_out_at).format('HH:mm:ss') }}</el-tag>
<el-tag v-if="dayAttendances(data.day).on_duty_schedules" effect="dark" size="mini" type="danger">
{{ dayAttendances(data.day).on_duty_schedules.status ? '已值班' : '待值班' }}
</el-tag>
<el-tag v-if="dayAttendances(data.day).attendance.sign_in_at" effect="dark" size="mini">{{ $moment(dayAttendances(data.day).attendance.sign_in_at).format('HH:mm:ss') }}</el-tag>
<el-tag v-if="dayAttendances(data.day).attendance.sign_out_at" effect="dark" type="success" size="mini">{{ $moment(dayAttendances(data.day).attendance.sign_out_at).format('HH:mm:ss') }}退</el-tag>
<el-tag v-if="dayAttendances(data.day).attendance.chuchai && dayAttendances(data.day).attendance.chuchai instanceof Array && dayAttendances(data.day).attendance.chuchai[0]" effect="dark" type="warning" size="mini"></el-tag>
<el-tag v-if="dayAttendances(data.day).attendance.chuchai && dayAttendances(data.day).attendance.chuchai instanceof Array && dayAttendances(data.day).attendance.chuchai[1]" effect="dark" type="warning" size="mini"></el-tag>
@ -31,6 +35,49 @@
:fullscreen="$store.getters.device === 'mobile'"
>
<div>
<el-tag v-if="selectDay && dayAttendances(selectDay).on_duty_schedules" style="margin-bottom: 10px;" effect="dark" size="mini" type="danger">
{{ dayAttendances(selectDay).on_duty_schedules.status ? '已值班' : '待值班' }}
</el-tag>
<template v-if="selectDay && dayAttendances(selectDay).attendance.chuchai && dayAttendances(selectDay).attendance.chuchai instanceof Array && dayAttendances(selectDay).attendance.chuchai[0]">
<el-card>
<el-descriptions :column="2" size="mini">
<el-descriptions-item :span="2">
<el-tag>上午出差</el-tag>
</el-descriptions-item>
</el-descriptions>
</el-card>
</template>
<template v-if="selectDay && dayAttendances(selectDay).attendance.qingxiujia && dayAttendances(selectDay).attendance.qingxiujia instanceof Array && dayAttendances(selectDay).attendance.qingxiujia[0]">
<el-descriptions :column="2" size="mini" border>
<template #title>
<el-tag effect="dark" type="info" size="mini">上午请假</el-tag>
</template>
<el-descriptions-item label="请假类别">{{ dayAttendances(selectDay).attendance.qingxiujia[0].qingjialeibie }}</el-descriptions-item>
<el-descriptions-item label="请假时长">{{ dayAttendances(selectDay).attendance.qingxiujia[0].shijiqingjiashichang }}</el-descriptions-item>
<el-descriptions-item label="开始时间">{{ dayAttendances(selectDay).attendance.qingxiujia[0].kaishishijian }}</el-descriptions-item>
<el-descriptions-item label="结束时间">{{ dayAttendances(selectDay).attendance.qingxiujia[0].jieshushijian }}</el-descriptions-item>
<el-descriptions-item label="实际开始时间">{{ dayAttendances(selectDay).attendance.qingxiujia[0].shijikaishishijian }}</el-descriptions-item>
<el-descriptions-item label="实际结束时间">{{ dayAttendances(selectDay).attendance.qingxiujia[0].shijijieshushijian }}</el-descriptions-item>
</el-descriptions>
</template>
<template v-if="selectDay && dayAttendances(selectDay).attendance.qingxiujia && dayAttendances(selectDay).attendance.qingxiujia instanceof Array && dayAttendances(selectDay).attendance.qingxiujia[1]">
<el-descriptions :column="2" size="mini" border>
<template #title>
<el-tag effect="dark" type="info" size="mini">下午请假</el-tag>
</template>
<el-descriptions-item label="请假类别">{{ dayAttendances(selectDay).attendance.qingxiujia[1].qingjialeibie }}</el-descriptions-item>
<el-descriptions-item label="请假时长">{{ dayAttendances(selectDay).attendance.qingxiujia[1].shijiqingjiashichang }}</el-descriptions-item>
<el-descriptions-item label="开始时间">{{ dayAttendances(selectDay).attendance.qingxiujia[1].kaishishijian }}</el-descriptions-item>
<el-descriptions-item label="结束时间">{{ dayAttendances(selectDay).attendance.qingxiujia[1].jieshushijian }}</el-descriptions-item>
<el-descriptions-item label="实际开始时间">{{ dayAttendances(selectDay).attendance.qingxiujia[1].shijikaishishijian }}</el-descriptions-item>
<el-descriptions-item label="实际结束时间">{{ dayAttendances(selectDay).attendance.qingxiujia[1].shijijieshushijian }}</el-descriptions-item>
</el-descriptions>
</template>
<template v-if="selectDay && dayAttendances(selectDay).attendance">
<el-timeline>
<el-timeline-item
@ -39,6 +86,7 @@
:type="item.sign_at_image ? 'warning' : 'primary'"
:timestamp="item.sign_at">
<p>{{ item.sign_at_address }}{{ item.sign_at_image ? '(外勤)' : '' }}</p>
<p style="color: #909399;">{{ item.remark }}</p>
<el-image v-if="item.sign_at_image" style="max-width: 120px" fit="contain" :src="item.sign_at_image.url"></el-image>
</el-timeline-item>
</el-timeline>

@ -2,6 +2,7 @@
<div>
<CardContainer>
<div>
<el-button v-if="isShowDuty" type="primary" style="display: block;margin: auto auto 20px;" @click="startDuty"></el-button>
<button class="sign-btn" @click="clockIn">
<span>{{ isOutSign ? ' 外勤' : '' }}打卡</span>
</button>
@ -15,7 +16,7 @@
<div>最大打卡范围{{ maxDistance }}千米</div>
</div>
<div class="sign-log">
<div class="sign-log" v-if="todayAttendance.length > 0">
<el-timeline>
<el-timeline-item
v-for="(item) in todayAttendance"
@ -38,14 +39,18 @@
:z-index="zIndex"
title="外勤打卡"
show-confirm-button
:width="500"
:height="320"
:width="560"
:height="440"
esc-closable
:fullscreen="$store.getters.device === 'mobile'"
>
<div style="line-height: 3;color: #333;font-weight: 600;">打卡照片</div>
<van-uploader capture="camera" :max-size="20 * 1024 * 1024" preview-size="140px" :max-count="1" v-model="fileList" @delete="imageId = ''"/>
<p>照片大小不能超过20Mb</p>
<div style="line-height: 3;color: #333;font-weight: 600;">描述</div>
<el-input v-model="remark" type="textarea" :autosize="{ minRows: 2 }"></el-input>
<template #footer>
<el-button type="primary" :loading="loading" @click="outClockIn"
>确认打卡</el-button
@ -56,6 +61,7 @@
</template>
<script>
import { index, save } from '@/api/onDutySchedules'
import { sign, preDistance } from '@/api/attendance'
import { throttle } from '@/utils'
import { getToken } from '@/utils/auth'
@ -76,6 +82,7 @@ export default {
isShow: false,
zIndex: PopupManager.nextZIndex(),
imageId: '',
remark: '',
// end
isGetLocation: false,
isOutSign: false,
@ -86,7 +93,11 @@ export default {
},
maxDistance: '',
nowDistance: '',
todayAttendance: []
todayAttendance: [],
//
isShowDuty: false,
dutyDetail: {},
}
},
computed: {
@ -100,16 +111,16 @@ export default {
},
methods: {
uploadFile(file) {
const _this = this
let data = new FormData()
data.append('file', file.file)
data.append('file', file)
axios.post(this.action, data, {
return axios.post(this.action, data, {
'Content-type' : 'multipart/form-data',
headers: {
Authorization: `Bearer ${getToken()}`
}
}).then(res => {
console.log(res)
if(res.status === 200) {
if(res.data.code) {
Promise.reject(res.data.msg)
@ -122,15 +133,19 @@ export default {
outClockIn: throttle(async function() {
try {
this.loading = true
if(!this.fileList[0]?.file) {
this.$message.warning('外勤打卡请拍照!')
return
if(!(this.fileList[0]?.file || this.remark)) {
this.$message.warning('外勤打卡请拍照或者填写描述!')
throw Error('外勤打卡请拍照或者填写描述!')
}
if (this.fileList[0]?.file) {
await this.uploadFile(this.fileList[0].file)
}
await this.uploadFile(this.fileList[0].file)
const res = await sign({
location: `${this.pos.lng},${this.pos.lat}`,
address: this.pos.address,
image_id: this.imageId
image_id: this.imageId,
remark: this.remark
})
this.$message.success('打卡成功')
this.loading = false
@ -170,6 +185,41 @@ export default {
this.getLocation()
}
},
async getMyDuty () {
try {
const res = await index({
page: 1,
page_size: 999,
'filter[0][key]': 'user_id',
'filter[0][op]': 'eq',
'filter[0][value]': this.$store.state.user.adminId,
'filter[1][key]': 'date',
'filter[1][op]': 'like',
'filter[1][value]': this.$moment().format('YYYY-MM-DD'),
'filter[2][key]': 'status',
'filter[2][op]': 'eq',
'filter[2][value]': '0',
})
if (res?.data[0]) {
this.dutyDetail = res.data[0]
this.isShowDuty = true
}
} catch(err) {
console.error(err)
}
},
async startDuty () {
try {
this.dutyDetail.status = 1
await save(this.dutyDetail)
this.getMyDuty()
this.$refs['MonthStatics'].getData()
} catch (err) {
console.error(err)
}
},
async pos2Address(lat, lng) {
try {
const res = await this.$jsonp('https://apis.map.qq.com/ws/geocoder/v1/',{
@ -255,6 +305,7 @@ export default {
},
},
mounted() {
this.getMyDuty()
this.isAuthPermission()
}
}

@ -30,11 +30,15 @@
<vxe-colgroup v-for="item in dateTableColumns" align="center" :title="item.title">
<vxe-column title="上午" width="160" align="center">
<template #default="{ row }">
<div v-if="!(row.attendance[item.date] instanceof Array)">
<span v-if="row.attendance[item.date]['0'].sign_in_at"></span>
<span v-else-if="row.attendance[item.date]['qingxiujia'][0]">{{ row.attendance[item.date]['qingxiujia'][0].qingjialeibie }}</span>
<span v-else-if="row.attendance[item.date]['chuchai'][0]">C</span>
<span v-else></span>
<div v-if="row.attendance[item.date]['qingxiujia'] && row.attendance[item.date]['qingxiujia'][0]">
{{ row.attendance[item.date]['qingxiujia'][0].qingjialeibie }}
</div>
<div v-else-if="row.attendance[item.date]['chuchai'] && row.attendance[item.date]['chuchai'][0]">
出差
</div>
<div v-else-if="(row.attendance[item.date].hasOwnProperty('0')) && row.attendance[item.date]['0'].sign_in_at">
</div>
<div v-else>
@ -43,11 +47,15 @@
</vxe-column>
<vxe-column title="下午" width="160" align="center">
<template #default="{ row }">
<div v-if="!(row.attendance[item.date] instanceof Array)">
<span v-if="row.attendance[item.date]['0'].sign_out_at"></span>
<span v-else-if="row.attendance[item.date]['qingxiujia'][1]">{{ row.attendance[item.date]['qingxiujia'][1].qingjialeibie }}</span>
<span v-else-if="row.attendance[item.date]['chuchai'][1]">C</span>
<span v-else></span>
<div v-if="row.attendance[item.date]['qingxiujia'] && row.attendance[item.date]['qingxiujia'][1]">
{{ row.attendance[item.date]['qingxiujia'][1].qingjialeibie }}
</div>
<div v-else-if="row.attendance[item.date]['chuchai'] && row.attendance[item.date]['chuchai'][1]">
出差
</div>
<div v-else-if="(row.attendance[item.date].hasOwnProperty('0')) && row.attendance[item.date]['0'].sign_out_at">
</div>
<div v-else>
@ -55,7 +63,11 @@
</template>
</vxe-column>
</vxe-colgroup>
<vxe-column title="备注" min-width="200" header-align="center" align="left"></vxe-column>
<vxe-column title="备注" min-width="200" header-align="center" align="left">
<template #default="{ row }">
<span>{{ getRemark(row) }}</span>
</template>
</vxe-column>
</vxe-table>
</card-container>
</div>
@ -92,6 +104,45 @@ export default {
} catch (err) {
console.error(err)
}
},
getRemark(row) {
let result = ''
let overtimeDay = 0
let leave = new Map(this.tableData.leave_types.map(i => [i, 0]))
let signDay = Object.entries(row.attendance).reduce((pre, [key, value]) => {
if (value['qingxiujia'][0]) {
console.log(value['qingxiujia'][0])
leave.set(value['qingxiujia'][0]['qingjialeibie'], Number(leave.get(value['qingxiujia'][0]['qingjialeibie']) ?? 0) + Number(value['qingxiujia'][0]['day'] ?? 0))
}
if (value['qingxiujia'][1]) {
leave.set(value['qingxiujia'][1]['qingjialeibie'], Number(leave.get(value['qingxiujia'][1]['qingjialeibie']) ?? 0) + Number(value['qingxiujia'][1]['day'] ?? 0))
}
return pre + (!value.hasOwnProperty('0') ? 0 : (
this.workDates.indexOf(key) !== -1 ? (
(() => {
let temp = 0
temp += value['0'].sign_in_at ? 0.5 : 0
temp += value['0'].sign_out_at ? 0.5 : 0
return temp
})()
) : ((
(() => {
overtimeDay += value['0'].sign_in_at ? 0.5 : 0
overtimeDay += value['0'].sign_out_at ? 0.5 : 0
return 0
})()
)??0)
))
}, 0)
result += signDay === this.workDates.length ? '全勤' : `缺勤${this.workDates.length-signDay}`
result += overtimeDay ? `,加班${overtimeDay}` : ''
Array.from(leave).forEach(([name, day]) => {
if (day) {
result += `,${name}${day}`
}
})
return result
}
},
computed: {
@ -102,6 +153,9 @@ export default {
date: i.date
}))
return columns
},
workDates() {
return this.tableData.dates.filter(i => i.is_workday).map(i => i.date)
}
},
created() {

@ -66,12 +66,22 @@
<div
class="sign-status"
:class="
!!getTodayAttendance(data.day) ? 'has-attendance' : ''
(getTodayAttendance(data.day) && (getTodayAttendance(data.day).sign_in_at || getTodayAttendance(data.day).sign_out_at)) ? 'has-attendance' : ''
"
>
{{ data.day.split("-")[2] }}
<template v-if="getTodayAttendance(data.day)">
<div
class="sign-duty"
v-if="
getTodayAttendance(data.day, 'on_duty_schedules')
"
>
{{
getTodayAttendance(data.day, 'on_duty_schedules').status ? '已值班' : '待值班'
}}
</div>
<div
class="sign-in"
v-if="
@ -299,7 +309,7 @@ export default {
]),
weather: {},
calendar: this.$moment().format("YYYY-MM-DD"),
calendar: new Date(),
attendanceData: {
attendances: [],
},
@ -367,17 +377,22 @@ export default {
} else {
this.modalUrl = ''
}
},
calendar(newVal, oldVal) {
if (this.$moment(oldVal).format('YYYY-MM') !== this.$moment(newVal).format('YYYY-MM')) {
this.getAttendance()
}
}
},
methods: {
getTodayAttendance(date) {
return this.attendanceData.attendances.find((i) => i.date === date)
?.attendance;
getTodayAttendance(date,key = 'attendance') {
let temp = this.attendanceData.attendances.find((i) => i.date === date) ?? {}
return temp[key]
},
async getAttendance() {
try {
const res = await index({
month: this.calendar,
month: this.$moment(this.calendar).format('YYYY-MM'),
});
this.attendanceData = res;
} catch (err) {
@ -554,7 +569,7 @@ $btn-colors: linear-gradient(90deg, #d4bbfd 0%, #af7bff 100%),
::v-deep .el-calendar-table tr td:first-child {
border: none;
}
::v-deep .el-calendar-table__row .current {
::v-deep .el-calendar-table__row .current, ::v-deep .el-calendar-table__row .next, ::v-deep .el-calendar-table__row .prev{
min-height: 68px;
position: relative;
}
@ -563,7 +578,6 @@ $btn-colors: linear-gradient(90deg, #d4bbfd 0%, #af7bff 100%),
min-height: 68px;
height: 100%;
transition: all 0.2s;
margin: 2px;
}
::v-deep .el-calendar-table td.is-selected {
background: var(--theme-color) !important;
@ -875,66 +889,72 @@ $btn-colors: linear-gradient(90deg, #d4bbfd 0%, #af7bff 100%),
</style>
<style lang="scss">
.el-calendar-day:has(.has-attendance) {
// position: relative;
&::before {
content: "";
border-radius: 10px;
background: var(--theme-color);
opacity: 0.4;
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
}
.sign-status {
display: flex;
flex-direction: column;
justify-content: flex-end;
position: relative;
}
.sign-in {
margin-top: 4px;
color: #fff;
font-size: 10px;
border-radius: 3px;
background: var(--theme-color);
padding: 2px 4px;
margin-left: auto;
text-align: center;
}
.sign-out {
margin-top: 4px;
color: #fff;
font-size: 10px;
border-radius: 3px;
background-color: #251f83;
padding: 2px 4px;
margin-left: auto;
text-align: center;
}
.sign-leave {
margin-top: 4px;
color: #fff;
font-size: 8px;
border-radius: 3px;
background-color: #e6a23c;
padding: 2px 4px;
margin-left: auto;
text-align: center;
}
.sign-away {
margin-top: 4px;
color: #fff;
font-size: 8px;
border-radius: 3px;
background-color: #909399;
padding: 2px 4px;
margin-left: auto;
text-align: center;
}
.el-calendar-day:has(.has-attendance)::before{
content: "";
border-radius: 10px;
background: var(--theme-color);
opacity: 0.4;
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
}
.el-calendar-day:has(.has-attendance) .sign-status {
display: flex;
flex-direction: column;
justify-content: flex-end;
position: relative;
}
.sign-duty {
margin-top: 4px;
color: #fff;
font-size: 10px;
border-radius: 3px;
background: #f56c6c;
padding: 2px 4px;
margin-left: auto;
text-align: center;
}
.sign-in {
margin-top: 4px;
color: #fff;
font-size: 10px;
border-radius: 3px;
background: var(--theme-color);
padding: 2px 4px;
margin-left: auto;
text-align: center;
}
.sign-out {
margin-top: 4px;
color: #fff;
font-size: 10px;
border-radius: 3px;
background-color: #251f83;
padding: 2px 4px;
margin-left: auto;
text-align: center;
}
.sign-leave {
margin-top: 4px;
color: #fff;
font-size: 8px;
border-radius: 3px;
background-color: #e6a23c;
padding: 2px 4px;
margin-left: auto;
text-align: center;
}
.sign-away {
margin-top: 4px;
color: #fff;
font-size: 8px;
border-radius: 3px;
background-color: #909399;
padding: 2px 4px;
margin-left: auto;
text-align: center;
}
@media (max-width: 992px) {

@ -1,17 +1,12 @@
<script>
import { deepCopy } from "@/utils";
import {deepCopy} from "@/utils";
import formBuilder from "@/utils/formBuilder";
import { PopupManager } from "element-ui/lib/utils/popup";
import { getToken } from "@/utils/auth";
import {PopupManager} from "element-ui/lib/utils/popup";
import request from '@/utils/request'
import moment from "moment/moment";
export default {
props: {
flows: {
type: Array,
default: () => [],
},
isFirstNode: {
type: Boolean,
default: true,
@ -45,11 +40,6 @@ export default {
default: () => [],
required: true,
},
fileList: {
type: Object,
default: () => ({}),
required: true,
},
scriptContent: String,
rules: {
type: Object,
@ -59,6 +49,10 @@ export default {
type: Object,
default: () => ({}),
},
logs: {
type: Array,
default: () => []
}
},
data() {
return {
@ -69,7 +63,6 @@ export default {
action: process.env.VUE_APP_BASE_API,
form: {},
file: {},
datetimeFormat: 'yyyy-MM-dd HH:mm',
copyShortcuts: [
@ -177,14 +170,30 @@ export default {
picker.$emit("pick", moment().add(1, "years").toDate());
},
},
]
],
flows: [],
flowSelectLoading: false,
nowSelectId: ''
};
},
methods: {
request,
async validate() {
return await this.$refs["elForm"].validate();
}
const $elForm = this.$refs['elForm']
if ($elForm) {
await this.$refs['elForm'].validate()
}
let subFormName = this.fields.filter(i => i.type === 'relation').map(i => i.name)
for (let i = 0;i < subFormName.length;i++) {
let $subForm = this.$refs[`subForm-${subFormName[i]}`]
if ($subForm) {
const errMap = await this.$refs[`subForm-${subFormName[i]}`].validate()
if (errMap) {
throw new Error(errMap)
}
}
}
},
},
computed: {},
watch: {
@ -197,13 +206,6 @@ export default {
originalForm(newVal) {
this.form = deepCopy(newVal);
},
fileList: {
handler: function (newVal) {
this.file = deepCopy(newVal);
},
immediate: true,
deep: true,
},
scriptContent(newVal) {
if (newVal) {
try {

@ -45,24 +45,22 @@
<DesktopForm
:device="device"
ref="desktopForm"
:flows="flows"
:is-first-node="isFirstNode"
:sub-form="subConfig"
:fields="fields"
:original-form="form"
:readable="readableFields"
:file-list="fileList"
:script-content="scriptContent"
:writeable="writeableFields"
:rules="rules"
:sub-rules="subRules"
:logs="config.logs"
></DesktopForm>
</template>
<template v-else>
<MobileForm
:device="device"
ref="mobileForm"
:flows="flows"
:is-first-node="isFirstNode"
:sub-form="subConfig"
:fields="fields"
@ -326,13 +324,6 @@ export default {
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] = res[key];
@ -421,13 +412,13 @@ export default {
type: 'html',
download: false
})
print(printText, isLog, _this.config.flow, res.content)
print.bind(this)(printText, isLog, _this.config.flow, res.content)
} else {
print(printText, isLog, _this.config.flow)
print.bind(this)(printText, isLog, _this.config.flow)
}
},
generateForm(object, fields, relation = false, pname, pFilelist = this.fileList) {
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) => {
@ -484,26 +475,21 @@ export default {
}
});
}
if (field.type === "file") {
pFilelist[field.name] = relation ? [[]] : [];
}
if (field.type === "relation") {
this.subRules[`${field.name}_rules`] = {}
object[field.name] = [{}];
pFilelist[field.name] = {}
this.generateForm(
object[field.name][0],
this.subConfig.get(field.sub_custom_model_id)?.customModel?.fields,
true,
field.name,
pFilelist[field.name]
field.name
);
} else {
if (/\/detail/.test(this.$route.path) && this.$route.query.flow_id) {
object[field.name] = "";
} else {
object[field.name] = (this.writeableFields.indexOf(field.id) !== -1 && field.default_value) ? field.default_value : "";
object[field.name] = (this.writeableFields.indexOf(field.id) !== -1 && field.default_value) ? field.default_value : (field.type === 'file' ? [] : "");
}
}
});
@ -519,17 +505,6 @@ export default {
}${minutes}分${seconds}`;
},
async getFlows() {
try {
this.flows = (await flowList('all', {
page: 1,
page_size: 9999
})).data.data
} catch (err) {
console.error(err)
}
},
async getConfig() {
const loading = this.$loading({
lock: true,
@ -541,7 +516,6 @@ export default {
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) => {
@ -561,28 +535,46 @@ export default {
this.config = res;
this.generateForm(this.form, fields);
this.form = Object.assign({}, this.form);
this.fileList = Object.assign({}, this.fileList);
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;
}
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)) {
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,
TYPE_FILE: 1
}))
} else {
this.form[key] = ''
}
}
this.form[key] = data[key];
}
}
}
this.fileList = Object.assign({}, this.fileList);
loading.close();
} catch (err) {
console.error(err);
@ -614,7 +606,6 @@ export default {
this.generateForm(this.form, fields);
this.handleDefaultJSON();
this.form = Object.assign({}, this.form);
this.fileList = Object.assign({}, this.fileList);
loading.close();
} catch (err) {
console.error(err);
@ -622,6 +613,7 @@ export default {
loading.close();
}
} else {
//
try {
const res = await preDeal(this.$route.query.flow_id);
const { fields } = res?.customModel;
@ -647,23 +639,37 @@ export default {
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;
}
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)) {
if (data[key] instanceof Array) {
if (data[key].length > 0) {
this.form[key] = data[key];
} else {
this.form[key] = ''
}
}
this.form[key] = data[key];
}
}
}
this.fileList = Object.assign({}, this.fileList);
this.form = Object.assign({}, this.form);
loading.close();
} catch (err) {
@ -675,20 +681,42 @@ export default {
},
async submit(type) {
let copyForm, copyFile;
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);
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();
}
const uploadHandler = (form) => {
let keys = Object.keys(form)
keys.forEach(key => {
if (form[key] instanceof Array) {
if (form[key].length > 0) {
if (form[key][0].hasOwnProperty('TYPE_FILE') && form[key][0]['TYPE_FILE']) {
form[key] = form[key].map(i => i.response)
} else {
form[key].forEach(i => {
uploadHandler(i)
})
}
} else {
form[key] = ''
}
} else {
if (form[key] === 'null' || form[key] === 'undefined') {
form[key] = ''
}
}
})
}
uploadHandler(copyForm)
copyForm["current_node_id"] = this.config.currentNode.id;
try {
@ -707,9 +735,7 @@ export default {
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(
@ -805,7 +831,6 @@ export default {
}
},
created() {
this.getFlows();
this.getConfig();
},
mounted() {},

@ -90,7 +90,8 @@ export default {
toCreate(flow,cate) {
if(isExternal(flow.url)) {
if (/\?.+/.test(flow.url)) {
const url = new URL(flow.url)
if (/\?.+/g.test(url.hash)) {
window.open(flow.url + `&auth_token=${getToken()}`,'_blank')
} else {
window.open(flow.url + `?auth_token=${getToken()}`,'_blank')

Loading…
Cancel
Save