xy 2 years ago
parent 903751043f
commit 0ccd9e9291

@ -18,7 +18,7 @@
"axios": "0.18.1",
"core-js": "3.6.5",
"echarts": "^5.0.0",
"element-ui": "2.13.2",
"element-ui": "^2.15.14",
"js-cookie": "2.2.0",
"moment": "^2.29.4",
"normalize.css": "7.0.0",

@ -9,6 +9,7 @@ const getDefaultState = () => {
avatar: '',
username: '',
adminId: '',
yearHoliday: 0,
department: {},
role: [],
}
@ -35,6 +36,9 @@ const mutations = {
SET_ADMIN_ID: (state, id) => {
state.adminId = id
},
SET_YEAR_HOLIDAY: (state, day) => {
state.yearHoliday = day
},
SET_DEPARTMENT: (state, department) => {
state.department = department
},
@ -65,7 +69,7 @@ const actions = {
return new Promise((resolve, reject) => {
getInfo(state.token).then(response => {
const { name, avatar, id, role, department, username } = response
const { name, avatar, id, role, department, username, year_holiday } = response
commit('SET_DEPARTMENT',department)
commit('SET_NAME', name)
@ -73,6 +77,7 @@ const actions = {
commit('SET_ADMIN_ID', id)
commit('SET_ROLE', role)
commit('SET_USERNAME', username)
commit('SET_YEAR_HOLIDAY', year_holiday??0)
resolve(response)
}).catch(error => {
reject(error)

@ -3,6 +3,7 @@ import { CreateElement, VNode } from "vue";
import moment from "moment";
import { getToken } from "@/utils/auth";
import { deepCopy } from "@/utils/index";
import { flowList } from "@/api/flow"
/**
* @param {String} device 'desktop' or 'mobile'
* @param {Object} info field参数
@ -15,12 +16,12 @@ import { deepCopy } from "@/utils/index";
export default function formBuilder(device, info, h, row, pWrite = false,pReadable = false) {
let formItem;
//下拉选项
let options = [];
if (info?.selection_model) {
options = info.selection_model_items;
} else if (info?.stub) {
options = info?.stub?.split(/\r\n/) || [];
}
let options;
if (device === "desktop") {
// 可写并且不为查看和子表单下
if (
@ -516,7 +517,7 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab
formBuilderMap(device).get(info.type),
{
props: {
value: row ? row[info.name] : this.form[info.name],
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,
@ -530,16 +531,16 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab
on: {
input: (e) => {
row
? this.$set(row, info.name, e)
: this.$set(this.form, info.name, e);
? this.$set(row, info.name, e.toString())
: this.$set(this.form, info.name, e.toString());
},
},
},
options.map((option) =>
this.flows.map((option) =>
h("el-option", {
props: {
label: option,
value: option,
label: option.title,
value: option.id,
},
})
)
@ -756,6 +757,21 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab
})?.toString() : typeof findChoicesValue === "object" ? findChoicesValue.name : findChoicesValue
);
break;
case "relation-flow":
formItem = h(
"span",
{
style: {
color: "#333",
},
},
this.form[info.name]?.toString()?.split(',').map(j => {
return this.flows.find((i) =>
i.id == j
)?.title;
})?.toString()
);
break;
case "file":
formItem = h(
formBuilderMap(device).get(info.type),
@ -980,6 +996,10 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab
this.multipleSelectOption.forFormName = info.name;
this.multipleSelectOption.originalObj = row;
this.$set(this.multipleSelectOption, "columns", options);
this.$set(this.multipleSelectOption, "options", {
label: 'name',
value: 'id'
});
this.$set(this.multipleSelectOption, "multipleLimit", info.multiple);
this.$set(this.multipleSelectOption, "isShow", true);
},
@ -1105,6 +1125,32 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab
)
])
break;
case 'relation-flow':
formItem = h("van-field", {
props: {
readonly: true,
clickable: true,
name: info.name,
label: info.label,
value: typeof findValue === "object" ? (findValue.name || findValue.title) : findValue,
clearable: true,
placeholder: info.help_text,
},
on: {
click: (_) => {
this.multipleSelectOption.forFormName = info.name;
this.multipleSelectOption.originalObj = row;
this.$set(this.multipleSelectOption, "columns", this.flows);
this.$set(this.multipleSelectOption, "options", {
label: 'title',
value: 'id'
});
this.$set(this.multipleSelectOption, "multipleLimit", info.multiple);
this.$set(this.multipleSelectOption, "isShow", true);
},
},
});
break;
case "relation":
let copySubForm = deepCopy(this.form[info.name][0])
formItem = h("div", [

@ -18,7 +18,7 @@ const loading = throttle(() => {
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
// withCredentials: true, // send cookies when cross-domain requests
timeout: 10000 // request timeout
timeout: 20000 // request timeout
})
// request interceptor

@ -2,33 +2,74 @@
<div>
<el-calendar v-model="day">
<template #dateCell="{ date, data }">
<div>
<div @click="selectDay = data.day,isShow = true">
<div>{{ data.day.split('-')[2] }}</div>
<div v-if="dayAttendances(data.day) && dayAttendances(data.day).attendance && device === 'desktop'" class="clock-in">
<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).attendance.sign_in_at1" effect="dark" size="mini">{{ $moment(dayAttendances(data.day).attendance.sign_in_at1).format('HH:mm:ss') }}</el-tag>
<el-tag v-if="dayAttendances(data.day).attendance.sign_out_at1" effect="dark" type="success" size="mini">{{ $moment(dayAttendances(data.day).attendance.sign_out_at1).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>
<el-tag v-if="dayAttendances(data.day).attendance.qingxiujia && dayAttendances(data.day).attendance.qingxiujia instanceof Array && dayAttendances(data.day).attendance.qingxiujia[0]" effect="dark" type="info" size="mini">上午请假</el-tag>
<el-tag v-if="dayAttendances(data.day).attendance.qingxiujia && dayAttendances(data.day).attendance.qingxiujia instanceof Array && dayAttendances(data.day).attendance.qingxiujia[1]" effect="dark" type="info" size="mini">下午请假</el-tag>
</div>
</div>
</template>
</el-calendar>
<vxe-modal
v-model="isShow"
show-footer
:z-index="zIndex"
title="打卡详情"
show-confirm-button
:width="680"
:height="540"
esc-closable
:fullscreen="$store.getters.device === 'mobile'"
>
<div>
<template v-if="selectDay && dayAttendances(selectDay).attendance">
<el-timeline>
<el-timeline-item
v-for="(item) in dayAttendances(selectDay).attendance.details"
:key="item.id"
:type="item.sign_at_image ? 'warning' : 'primary'"
:timestamp="item.sign_at">
<p>{{ item.sign_at_address }}{{ item.sign_at_image ? '(外勤)' : '' }}</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>
</template>
<template v-else>
<el-empty description="暂无打卡记录"></el-empty>
</template>
</div>
</vxe-modal>
</div>
</template>
<script>
import { index } from '@/api/attendance'
import { PopupManager } from "element-ui/lib/utils/popup";
export default {
data() {
return {
isShow: false,
zIndex: PopupManager.nextZIndex(),
day: new Date(),
selectDay: '',
clockLogs: {
attendances: [],
is_workday: true,
on_dutys: [],
today_attendance: {}
today_attendance: {
details: []
}
}
}
},
@ -50,6 +91,11 @@ export default {
}
},
immediate: true
},
isShow(newVal) {
if(newVal) {
this.zIndex = PopupManager.nextZIndex()
}
}
},
created() {
@ -60,6 +106,7 @@ export default {
this.clockLogs = await index({
month: this.$moment(this.day).format('YYYY-MM')
})
this.$emit('today-attendance', this.clockLogs.today_attendance.details)
} catch (err) {
console.error(err)
}
@ -82,4 +129,13 @@ export default {
justify-content: flex-end;
align-items: flex-end;
}
@media (max-width: 768px) {
.clock-in > .el-tag {
font-size: 0;
padding: 0;
width: 10px;
height: 10px;
border-radius: 100%;
}
}
</style>

@ -3,7 +3,7 @@
<CardContainer>
<div>
<button class="sign-btn" @click="clockIn">
<span>打卡</span>
<span>{{ isOutSign ? ' 外勤' : '' }}打卡</span>
</button>
<div class="sign-info">
<div class="sign-statue">
@ -11,32 +11,72 @@
<div>当前位置: <span v-if="isGetLocation">
<el-tag size="small" type="primary" effect="dark">{{pos.address}}</el-tag>
({{pos.lng}},{{pos.lat}})</span></div>
<div>当前距离:{{ nowDistance }}</div>
<div>最大打卡范围:{{ maxDistance }}</div>
<div>当前距离:{{ nowDistance }}千米</div>
<div>最大打卡范围:{{ maxDistance }}千米</div>
</div>
<div class="sign-log">
<el-timeline>
<el-timeline-item
v-for="(item) in todayAttendance"
:key="item.id"
:type="item.sign_at_image ? 'warning' : 'primary'"
:timestamp="item.sign_at">
{{ item.sign_at_address }}{{ item.sign_at_image ? '(外勤)' : '' }}
</el-timeline-item>
</el-timeline>
</div>
</div>
</div>
<MonthStatics ref="MonthStatics"></MonthStatics>
<MonthStatics ref="MonthStatics" @today-attendance="e => todayAttendance = e"></MonthStatics>
</CardContainer>
<vxe-modal
v-model="isShow"
show-footer
:z-index="zIndex"
title="外勤打卡"
show-confirm-button
:width="500"
:height="320"
esc-closable
:fullscreen="$store.getters.device === 'mobile'"
>
<van-uploader capture="camera" :max-size="20 * 1024 * 1024" preview-size="140px" :max-count="1" v-model="fileList" @delete="imageId = ''"/>
<p>照片大小不能超过20Mb</p>
<template #footer>
<el-button type="primary" :loading="loading" @click="outClockIn"
>确认打卡</el-button
>
</template>
</vxe-modal>
</div>
</template>
<script>
import { sign, preDistance } from '@/api/attendance'
import { throttle } from '@/utils'
import { getToken } from '@/utils/auth'
import MonthStatics from './components/MonthStatics'
import axios from "axios";
import { PopupManager } from "element-ui/lib/utils/popup";
export default {
components: {
MonthStatics
},
data() {
return {
// start 外勤打卡
loading: false,
action: process.env.VUE_APP_UPLOAD_API,
fileList: [],
isShow: false,
zIndex: PopupManager.nextZIndex(),
imageId: '',
// end
isGetLocation: false,
isOutSign: false,
pos: {
@ -46,14 +86,70 @@ export default {
},
maxDistance: '',
nowDistance: '',
todayAttendance: []
}
},
computed: {
},
watch: {
isShow(newVal) {
if(newVal) {
this.zIndex = PopupManager.nextZIndex()
}
}
},
computed: {},
methods: {
uploadFile(file) {
let data = new FormData()
data.append('file', file.file)
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)
return
}
this.imageId = res.data.data.id
}
})
},
outClockIn: throttle(async function() {
try {
this.loading = true
if(!this.fileList[0]?.file) {
this.$message.warning('外勤打卡请拍照!')
return
}
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
})
this.$message.success('打卡成功')
this.loading = false
this.isShow = false
this.fileList = []
await this.$refs['MonthStatics'].getData()
} catch (err) {
console.error(err)
this.loading = false
}
}, 1000, true),
clockIn: throttle(async function() {
try {
this.getLocation()
await this.getLocation()
if(!this.isGetLocation) return
if(this.isOutSign) {
this.isShow = true
return
}
const res = await sign({
location: `${this.pos.lng},${this.pos.lat}`,
address: this.pos.address
@ -89,69 +185,73 @@ export default {
}
},
getLocation() {
navigator.geolocation.getCurrentPosition((pos) => {
this.isGetLocation = true
console.log('经度', pos.coords.latitude);
console.log('纬度', pos.coords.longitude);
this.pos.lng = pos.coords.longitude
this.pos.lat = pos.coords.latitude
if(this.pos.lat && this.pos.lng && this.isGetLocation) {
this.pos2Address(this.pos.lat, this.pos.lng)
preDistance({
location: `${this.pos.lng},${this.pos.lat}`
}).then(res => {
this.isOutSign = res.distance > Number(res.max_distance)
this.maxDistance = res.max_distance
this.nowDistance = res.distance
})
}
}, (error) => {
console.log(error)
if (error.code) {
switch (error.code) {
case error.PERMISSION_DENIED:
this.$msgbox.confirm("需授权定位后进行打卡", "提示",{
confirmButtonText: '重试'
}).then(_ => {
this.getLocation()
})
this.isGetLocation = false
break;
case error.POSITION_UNAVAILABLE:
this.$msgbox.confirm("无法获取当前位置,请重试", "提示",{
confirmButtonText: '重试'
}).then(_ => {
this.getLocation()
})
this.isGetLocation = false
break;
case error.TIMEOUT:
this.$msgbox.confirm("获取位置超时,请重试", "提示",{
confirmButtonText: '重试'
}).then(_ => {
this.getLocation()
})
this.isGetLocation = false
break;
case error.UNKNOWN_ERROR:
this.$msgbox.confirm("获取位置错误,请重试", "提示",{
confirmButtonText: '重试'
}).then(_ => {
this.getLocation()
})
this.isGetLocation = false
break;
default:
this.$msgbox.confirm("获取位置错误,请重试", "提示",{
confirmButtonText: '重试'
}).then(_ => {
this.getLocation()
})
this.isGetLocation = false
break;
return new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition((pos) => {
this.isGetLocation = true
console.log('经度', pos.coords.latitude);
console.log('纬度', pos.coords.longitude);
this.pos.lng = pos.coords.longitude
this.pos.lat = pos.coords.latitude
if(this.pos.lat && this.pos.lng && this.isGetLocation) {
this.pos2Address(this.pos.lat, this.pos.lng)
preDistance({
location: `${this.pos.lng},${this.pos.lat}`
}).then(res => {
this.isOutSign = res.distance > Number(res.max_distance)
this.maxDistance = res.max_distance
this.nowDistance = res.distance
resolve()
}).catch(err => reject(err))
}
}
});
}, (error) => {
console.log(error)
reject(error)
if (error.code) {
switch (error.code) {
case error.PERMISSION_DENIED:
this.$msgbox.confirm("需授权定位后进行打卡", "提示",{
confirmButtonText: '重试'
}).then(_ => {
this.getLocation()
})
this.isGetLocation = false
break;
case error.POSITION_UNAVAILABLE:
this.$msgbox.confirm("无法获取当前位置,请重试", "提示",{
confirmButtonText: '重试'
}).then(_ => {
this.getLocation()
})
this.isGetLocation = false
break;
case error.TIMEOUT:
this.$msgbox.confirm("获取位置超时,请重试", "提示",{
confirmButtonText: '重试'
}).then(_ => {
this.getLocation()
})
this.isGetLocation = false
break;
case error.UNKNOWN_ERROR:
this.$msgbox.confirm("获取位置错误,请重试", "提示",{
confirmButtonText: '重试'
}).then(_ => {
this.getLocation()
})
this.isGetLocation = false
break;
default:
this.$msgbox.confirm("获取位置错误,请重试", "提示",{
confirmButtonText: '重试'
}).then(_ => {
this.getLocation()
})
this.isGetLocation = false
break;
}
}
});
})
},
},
mounted() {
@ -197,10 +297,20 @@ export default {
bottom: 0;
}
}
.sign-statue {
.sign-info {
margin-top: 20px;
line-height: 2;
text-align: center;
display: flex;
justify-content: center;
padding: 0 10%;
.sign-statue {
line-height: 2;
text-align: center;
flex-basis: 50%;
}
.sign-log {
flex-basis: 50%;
}
}
@keyframes scale {
0%,100% {
@ -210,4 +320,14 @@ export default {
transform: scale(1.1, 1.1);
}
}
@media (max-width: 992px) {
.sign-info {
display: block;
}
.sign-log {
display: flex;
justify-content: center;
margin-top: 10px;
}
}
</style>

@ -1,7 +1,7 @@
<template>
<div>
<card-container>
<vxe-toolbar>
<vxe-toolbar export print ref="toolbar">
<template #buttons>
<el-date-picker v-model="select.month" type="month" size="small" value-format="yyyy-MM"></el-date-picker>
<el-button icon="el-icon-search" type="primary" plain size="small" style="margin-left: 6px;" @click="getStatistics">搜索</el-button>
@ -19,7 +19,8 @@
show-overflow
:row-config="{ isHover: true }"
:header-cell-style="{ 'white-space': 'wrap' }"
:scroll-x="{ enabled: true, gt: 0 }"
:print-config="{}"
:export-config="{}"
:column-config="{ resizable: true }"
:data="tableData.admins">
<vxe-column type="seq" width="50" align="center" fixed="left"></vxe-column>
@ -28,12 +29,28 @@
<vxe-colgroup v-for="item in dateTableColumns" align="center" :title="item.title">
<vxe-column title="上午" width="160" align="center">
<template #default="{ row }">
<span>{{ row.attendance[item.date] instanceof Array ? '' : row.attendance[item.date][0].sign_in_at }}</span>
<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>
<div v-else>
✘
</div>
</template>
</vxe-column>
<vxe-column title="下午" width="160" align="center">
<template #default="{ row }">
<span>{{ row.attendance[item.date] instanceof Array ? '' : row.attendance[item.date][0].sign_out_at }}</span>
<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>
<div v-else>
✘
</div>
</template>
</vxe-column>
</vxe-colgroup>
@ -43,6 +60,7 @@
</template>
<script>
import { throttle } from '@/utils'
import { statistics } from '@/api/attendance'
export default {
data() {
@ -57,6 +75,13 @@ export default {
}
},
methods: {
bindToolbar: throttle(function () {
this.$nextTick(() => {
if (this.$refs["table"] && this.$refs["toolbar"]) {
this.$refs["table"].connect(this.$refs["toolbar"]);
}
});
}, 1000, true),
async getStatistics() {
try {
const res = await statistics(this.select)
@ -68,16 +93,20 @@ export default {
},
computed: {
dateTableColumns() {
return this.tableData.dates.map(i => ({
let columns = this.tableData.dates.map(i => ({
title: `${this.$moment(i.day).format('YYYY年MM月DD日')} ${i.is_workday ? '工作日' : '公休'}`,
field: i.day,
date: i.date
}))
return columns
}
},
created() {
this.getStatistics()
}
},
mounted() {
this.bindToolbar()
},
}
</script>

@ -64,6 +64,7 @@
<el-calendar v-model="calendar">
<template #dateCell="{ date, data }">
<div
class="sign-status"
:class="
!!getTodayAttendance(data.day) ? 'has-attendance' : ''
"
@ -97,6 +98,30 @@
).format("HH:mm")
}}
</div>
<div
class="sign-away"
v-if="getTodayAttendance(data.day) && getTodayAttendance(data.day).chuchai && getTodayAttendance(data.day).chuchai instanceof Array && getTodayAttendance(data.day).chuchai[0]"
>
上午出差
</div>
<div
class="sign-away"
v-if="getTodayAttendance(data.day) && getTodayAttendance(data.day).chuchai && getTodayAttendance(data.day).chuchai instanceof Array && getTodayAttendance(data.day).chuchai[1]"
>
下午出差
</div>
<div
class="sign-leave"
v-if="getTodayAttendance(data.day) && getTodayAttendance(data.day).qingxiujia && getTodayAttendance(data.day).qingxiujia instanceof Array && getTodayAttendance(data.day).qingxiujia[0]"
>
上午请假
</div>
<div
class="sign-leave"
v-if="getTodayAttendance(data.day) && getTodayAttendance(data.day).qingxiujia && getTodayAttendance(data.day).qingxiujia instanceof Array && getTodayAttendance(data.day).qingxiujia[1]"
>
下午请假
</div>
</template>
</div>
</template>
@ -516,6 +541,10 @@ $btn-colors: linear-gradient(90deg, #d4bbfd 0%, #af7bff 100%),
border-color: transparent;
border-radius: 10px;
}
::v-deep .el-calendar-table {
border-collapse: separate;
border-spacing: 4px;
}
::v-deep .el-calendar__body table thead th:nth-last-child(1),
::v-deep .el-calendar__body table thead th:nth-last-child(2) {
color: #dd383d;
@ -525,17 +554,21 @@ $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 {
min-height: 68px;
position: relative;
}
::v-deep .el-calendar-table .el-calendar-day {
border-radius: 10px;
min-height: 68px;
height: auto;
border-radius: 6px;
height: 100%;
transition: all 0.2s;
margin: 2px;
}
::v-deep .el-calendar-table td.is-selected {
background: var(--theme-color) !important;
color: #fff !important;
border-radius: 6px;
border-radius: 10px;
}
::v-deep .el-calendar-table .el-calendar-day:hover {
color: var(--theme-color);
@ -547,7 +580,7 @@ $btn-colors: linear-gradient(90deg, #d4bbfd 0%, #af7bff 100%),
z-index: 1;
}
::v-deep .el-calendar-table .is-today {
position: relative;
&::after {
content: "今";
color: #fff;
@ -673,9 +706,14 @@ $btn-colors: linear-gradient(90deg, #d4bbfd 0%, #af7bff 100%),
border-radius: 10px 10px 0 0;
background: var(--theme-color);
padding: 22px;
&::before {
display: none;
}
}
&__body {
background: #fff;
border-radius: 0 0 10px 10px;
}
}
@ -838,7 +876,7 @@ $btn-colors: linear-gradient(90deg, #d4bbfd 0%, #af7bff 100%),
<style lang="scss">
.el-calendar-day:has(.has-attendance) {
position: relative;
// position: relative;
&::before {
content: "";
@ -851,9 +889,14 @@ $btn-colors: linear-gradient(90deg, #d4bbfd 0%, #af7bff 100%),
top: 0;
left: 0;
}
.sign-status {
display: flex;
flex-direction: column;
justify-content: flex-end;
position: relative;
}
.sign-in {
margin-top: 4px;
float: right;
color: #fff;
font-size: 10px;
border-radius: 3px;
@ -864,7 +907,6 @@ $btn-colors: linear-gradient(90deg, #d4bbfd 0%, #af7bff 100%),
}
.sign-out {
margin-top: 4px;
float: right;
color: #fff;
font-size: 10px;
border-radius: 3px;
@ -873,10 +915,35 @@ $btn-colors: linear-gradient(90deg, #d4bbfd 0%, #af7bff 100%),
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) {
.sign-status {
display: block !important;
}
.sign-in,
.sign-out,
.sign-leave,
.sign-out {
float: none !important;
}

@ -7,6 +7,10 @@ import request from '@/utils/request'
export default {
props: {
flows: {
type: Array,
default: () => [],
},
isFirstNode: {
type: Boolean,
default: true,
@ -129,7 +133,7 @@ export default {
props: {
model: this.form,
"label-position": "right",
"label-width": "100px",
"label-width": "120px",
rules: this.rules,
"inline-message": true,
},

@ -10,6 +10,10 @@ export default {
MobileMultipleSelect
},
props: {
flows: {
type: Array,
default: () => [],
},
isFirstNode: {
type: Boolean,
default: true
@ -83,7 +87,11 @@ export default {
forFormName: "",
columns: [],
originalObj: "",
multipleLimit: 50
multipleLimit: 50,
options: {
label: 'name',
value: 'id'
}
}
}
},
@ -240,6 +248,7 @@ export default {
props: {
selectDataOpts: this.multipleSelectOption.columns,
multipleLimit: this.multipleSelectOption.multipleLimit,
options: this.multipleSelectOption.options,
},
on: {
confirm: value => {

@ -45,6 +45,7 @@
<DesktopForm
:device="device"
ref="desktopForm"
:flows="flows"
:is-first-node="isFirstNode"
:sub-form="subConfig"
:fields="fields"
@ -60,6 +61,7 @@
<MobileForm
:device="device"
ref="mobileForm"
:flows="flows"
:is-first-node="isFirstNode"
:sub-form="subConfig"
:fields="fields"
@ -255,7 +257,7 @@ import rollback from "./components/rollback.vue";
import {
create,
deal,
fieldConfig,
fieldConfig, flowList,
preConfig,
preDeal,
updateNodeTime,
@ -298,6 +300,7 @@ export default {
result: {},
fileList: {},
rules: {},
flows: [],
};
},
methods: {
@ -475,6 +478,17 @@ 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,
@ -745,6 +759,7 @@ export default {
}
},
created() {
this.getFlows();
this.getConfig();
},
mounted() {},

Loading…
Cancel
Save