Bläddra i källkod

app 考勤打卡

黑艺新账号
yxq 1 år sedan
förälder
incheckning
08ca74600f
8 ändrade filer med 526 tillägg och 25 borttagningar
  1. +41
    -0
      Learun.Framework.Ultimate V7/LearunApp-2.2.0/common/custompage.js
  2. +5
    -3
      Learun.Framework.Ultimate V7/LearunApp-2.2.0/common/wxFn.js
  3. +5
    -2
      Learun.Framework.Ultimate V7/LearunApp-2.2.0/components/learun-app/upload-file.vue
  4. +226
    -0
      Learun.Framework.Ultimate V7/LearunApp-2.2.0/components/uploadImage.vue
  5. +1
    -5
      Learun.Framework.Ultimate V7/LearunApp-2.2.0/manifest.json
  6. +6
    -0
      Learun.Framework.Ultimate V7/LearunApp-2.2.0/pages.json
  7. +80
    -15
      Learun.Framework.Ultimate V7/LearunApp-2.2.0/pages/AttendanceCard/list.vue
  8. +162
    -0
      Learun.Framework.Ultimate V7/LearunApp-2.2.0/pages/AttendanceCard/single.vue

+ 41
- 0
Learun.Framework.Ultimate V7/LearunApp-2.2.0/common/custompage.js Visa fil

@@ -105,6 +105,8 @@ export default {
}
uni.setStorageSync('folderIds',JSON.stringify(folderIds));
return []
case 'upload_old':
return []

case 'guid':
return this.GUID('-')
@@ -252,6 +254,27 @@ export default {
}
return uploadUid;
case 'upload_old':
const valArray = val.map(item=>{
return {
uid:item.uid,
path:item.path===undefined?item:item.path
}
})
const uploadUid_ = []
for (const { path, uid } of valArray) {
if (uid) {
uploadUid_.push(uid)
continue
}
const fileId = await this.HTTP_UPLOAD(path)
if (fileId) {
uploadUid_.push(fileId)
}
}
return uploadUid_.join(',')

default:
return val || ''
@@ -347,6 +370,24 @@ export default {
})
}
return fileList
case 'upload_old':
if (!val) { return [] }
const uidList_ = val.split(',')
const fileList_ = []
for (const uid of uidList_ || []) {
const fileInfo = await this.FETCH_FILEINFO(uid)
if (!fileInfo) { continue }
const fileType = fileInfo.F_FileType
const fileSize = fileInfo.F_FileSize
const fileName = fileInfo.F_FileName
const path = this.API + 'learun/adms/annexes/wxdown?' + this.URL_QUERY(uid, true)
fileList_.push({ path, type: fileType, uid, size: fileSize, name:fileName })
}
return fileList_

case 'radio':
case 'select':


+ 5
- 3
Learun.Framework.Ultimate V7/LearunApp-2.2.0/common/wxFn.js Visa fil

@@ -1,4 +1,5 @@
import wx from '@/common/js/weixin-js-sdk.js';
// 参考文档 https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html
export default {
methods: {
data(){
@@ -29,7 +30,7 @@ export default {
});
wx.ready(() => {
this.wxInit = true
this.TOAST("wx初始化成功")
// this.TOAST("wx初始化成功")
resolve(true)
})
wx.error(() => {
@@ -51,7 +52,7 @@ export default {
}
}
if(!wx.getLocation){
this.TOAST("无效方法")
this.TOAST("获取定位失败")
resolve(false)
}
wx.getLocation({
@@ -67,7 +68,7 @@ export default {
})
},
fail: function(error) {
this.TOAST("定位失败")
this.TOAST("获取定位失败")
resolve(false)
}
});
@@ -93,6 +94,7 @@ export default {
sourceType: ['camera'], // 可以指定来源是相册还是相机,默认二者都有
success: function(res) {
var localIds = res.localIds;// 返回选定照片的本地ID列表,localId可以作为img标签的src属性显示图片
resolve(localIds)
},
fail: function(error) {
this.TOAST("定位失败")


+ 5
- 2
Learun.Framework.Ultimate V7/LearunApp-2.2.0/components/learun-app/upload-file.vue Visa fil

@@ -53,7 +53,10 @@ export default {
readonly: {},
value: { default: () => [] },
title: {},
required: {}
required: {},
accept:{
default:()=>['album', 'camera']
}
},

methods: {
@@ -104,7 +107,7 @@ export default {
uni.chooseFile({
count: Number(this.number),
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera'],
sourceType: this.accept,
success: ({ tempFilePaths,tempFiles }) => {
const newList = JSON.parse(JSON.stringify(this.value || [])).concat(
// tempFilePaths//.map(t => ({ path: t, type: this.getFileExt(t) }))


+ 226
- 0
Learun.Framework.Ultimate V7/LearunApp-2.2.0/components/uploadImage.vue Visa fil

@@ -0,0 +1,226 @@
<template>
<view>
<view class="cu-form-group" style="border-bottom: none; padding-bottom: 0;">
<view class="title">
<text v-if="required" class="lr-required">*</text>
{{ title || '' }}
</view>
</view>

<view class="cu-form-group" style="border-top: none;">
<view class="grid col-4 grid-square flex-sub">
<view v-for="(file, index) in value" :key="index" class="bg-img" style="position: relative;">
<!-- :style="{borderColor:file.noUpdated?'#EEE8AA':'#fff'}" -->
<view v-if="file.noUpdated" class="mask"></view>
<image
v-if="isImgFile(file)"
@click="fileClick(index)"
:src="file.path?file.path:file"
:webp="file.type === 'webp'"
mode="aspectFill"
></image>
<view v-else-if="isDocFile(file)" @click="fileClick(index)" class="file-icon solids">
<l-icon type="text" />
</view>
<view v-else class="file-icon solids" @click="fileClick(index)">
<l-icon type="text" />
</view>

<view v-if="!readonly" @click.stop="delFile(index)" class="cu-tag bg-red" style="height: 24px; width: 24px;">
<l-icon type="close" color="white" style="width: 18px; height: 24px; font-size: 24px;" />
</view>
<view class="fileName">
<text>{{file.name}}</text>
</view>
</view>

<view v-if="!readonly && value.length < Number(number)" @click="chooseFile" class="solids">
<l-icon type="file" />
</view>
</view>
</view>
</view>
</template>

<script>
// import uniCopy from '@/common/js/uni-copy.js'
export default {
name: 'l-upload-file',

props: {
number: { default: 1 },
readonly: {},
value: { default: () => [] },
title: {},
required: {},
accept:{
default:()=>['album', 'camera']
}
},

methods: {
getFileExt(path) {
return /\.(\w{2,5})$/.exec(path)[1] || null
},
isImgFile(file) {
const typeString = (file.type || '').toLowerCase()
return ['jpg','image/jpg','jpeg','image/jpeg', 'png', 'image/png','gif', 'image/gif','bmp', 'image/bmp','webp', 'image/webp','image'].includes(typeString)
},

isDocFile(file) {
const typeString = (file.type || '').toLowerCase()
return ['doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'pdf'].includes(typeString)
return true
},

async delFile(index) {
if (!(await this.CONFIRM('删除文件', '确定要删除该文件吗?', true))) {
return
}
const newList = JSON.parse(JSON.stringify(this.value))
newList.splice(index, 1)
this.$emit('input', newList)
this.$emit('change')
this.$emit('del')
},

chooseFile() {
// #ifdef MP-DINGTALK
// dd.chooseImage({
// count: Number(this.number),
// success: ({ filePaths }) => {
// if (filePaths) {
// const newList = JSON.parse(JSON.stringify(this.value || [])).concat(
// filePaths.map(t => ({ path: t, type: this.getFileExt(t) }))
// )
// this.$emit('input', newList)
// this.$emit('change', newList)
// this.$emit('add')
// }
// }
// })
// #endif

// #ifndef MP-DINGTALK
uni.chooseImage({
count: Number(this.number),
sizeType: ['original', 'compressed'],
sourceType: this.accept,
success: ({ tempFilePaths,tempFiles }) => {
const newList = JSON.parse(JSON.stringify(this.value || [])).concat(
// tempFilePaths//.map(t => ({ path: t, type: this.getFileExt(t) }))
tempFilePaths.map((t,i) => ({ path: t, type: tempFiles[i].type, size:tempFiles[i].size, name:tempFiles[i].name, noUpdated:true} ))
)
this.$emit('input', newList)
this.$emit('change', newList)
this.$emit('add')
}
})
// #endif
},

async fileClick(index) {
const { path, type, uid, size = 0 } = this.value[index]
if (this.isImgFile(this.value[index])) {
uni.previewImage({ urls: [path], current: path })
}else{
uni.openDocument({ filePath: this.value[index].path, fileType: type })
// uniCopy({
// content:this.value[index].path,
// success:(res)=>{
// uni.showToast({
// title: "复制链接成功!请在浏览器进行打开~",
// icon: 'none',
// duration:3000,
// })
// },
// error:(e)=>{
// uni.showToast({
// title: e,
// icon: 'none',
// duration:3000,
// })
// }
// })
}
// if (this.isImgFile(this.value[index])) {
// uni.previewImage({ urls: [path], current: path })
// } else if (this.isDocFile(this.value[index])) {
// // #ifndef H5 || MP-DINGTALK
// if (size >= 50 * 1024 * 1024) {
// this.TOAST('小程序端无法下载超过50MB的文件,此文件大小为${size}KB,超过限制')
// return
// }
// // #endif

// // #ifndef MP-DINGTALK
// const tempFilePath = await this.HTTP_DOWNLOAD(uid)
// uni.openDocument({ filePath: tempFilePath, fileType: type })
// // #endif

// // #ifdef MP-DINGTALK
// this.TOAST('钉钉小程序只支持查看图片文件')
// // #endif
// } else {
// // #ifndef MP-DINGTALK
// this.TOAST('小程序端只支持打开图片和文档(word、pdf等)文件')
// // #endif

// // #ifdef MP-DINGTALK
// this.TOAST('钉钉小程序只支持查看图片文件')
// // #endif

// // #ifdef APP-VUE
// const tempFilePath = await this.HTTP_DOWNLOAD(uid)
// uni.openDocument({ filePath: tempFilePath, fileType: type })
// // #endif

// // #ifdef H5
// await this.HTTP_DOWNLOAD(uid)
// // #endif
// }
},
// ClickDownload(url) {
// var src = url;
// var iframe = document.createElement('iframe');
// iframe.style.display = 'none';
// iframe.src = "javascript: '<script>location.href=\"" + src + "\"<\/script>'";
// document.getElementsByTagName('body')[0].appendChild(iframe);
// }
}
}
</script>

<style lang="less" scoped>
.file-icon {
line-height: 100%;
position: static;
}
.fileName{
padding: 2px 2px;
margin-bottom: 2px;
text-align: center;
position: absolute;
bottom: 0px;
width: 100%;
background: rgba(0,0,0,0.2);
color: #fff;
font-size: 12px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.mask{
position: absolute;
top: 0;left: 0;
width: 100%;
height: 100%;
// background: rgba(255,252,153,0.2);
background: rgba(0,0,0,0.7);
z-index: 100;
pointer-events: none;
}
</style>

+ 1
- 5
Learun.Framework.Ultimate V7/LearunApp-2.2.0/manifest.json Visa fil

@@ -191,11 +191,7 @@
},
"domain" : "testapp.bjquanjiang.com",
"sdkConfigs" : {
"maps" : {
"qqmap" : {
"key" : "EZCBZ-IDUCZ-G3UXY-ZMP3T-5XNQE-AXBZS"
}
}
"maps" : {}
}
},
"mp-qq" : {


+ 6
- 0
Learun.Framework.Ultimate V7/LearunApp-2.2.0/pages.json Visa fil

@@ -407,6 +407,12 @@
"navigationBarTitleText": "考勤打卡"
}
},
{
"path": "pages/AttendanceCard/single",
"style": {
"navigationBarTitleText": "外勤打卡"
}
},
//班级自诊打卡
{
"path": "pages/EducationalAdministration/Thermography/list",


+ 80
- 15
Learun.Framework.Ultimate V7/LearunApp-2.2.0/pages/AttendanceCard/list.vue Visa fil

@@ -7,7 +7,7 @@
上班 {{info.WorkTime}}
</view>
<view class="">
{{info.UserWorkTime?info.UserWorkTime.substring(8)+' 打卡':'未打卡'}}
{{info.UserWorkTime?info.UserWorkTime.split(' ')[1]+' 打卡':'未打卡'}}
</view>
</view>
<view class="">
@@ -15,7 +15,7 @@
下班 {{info.CloseTime}}
</view>
<view class="">
{{info.UserCloseTime?info.UserCloseTime.substring(8)+' 打卡':'未打卡'}}
{{info.UserCloseTime?info.UserCloseTime.split(' ')[1]+' 打卡':'未打卡'}}
</view>
</view>
</view>
@@ -25,7 +25,12 @@
<img :class="{gray:info.AttendanceType != 1}" id="attimg" :src="imgsrc" alt="" width="100%">
</view>
<view class="title">
{{info.AttendanceTypeString}}
<text>{{resInfo.AttendanceTypeString}}</text>
<text>{{postData.AIsOut== 1 ? ' | ' + info.AttendanceTypeString:''}}</text>
<view style="color: #666;">
{{postData.ClockPlace}}
</view>
</view>
</view>
<view class="footer">
@@ -73,11 +78,15 @@
return {
// 页面相关参数
info: {},
resInfo:{},
now: null,
imgsrc: dk,
ready: false,
timer: '',
timer1:'',
map: null,
postData: {},
isGetingLocal:false,
}
},

@@ -92,9 +101,9 @@

this.now = this.getCurrentTime()
this.timer = setInterval(this.getCurrentTime, 1000)
await this.judgeIsDK()
let res = await this.judgeIsDK()

this.ready = true
this.ready = res ? true : false
this.HIDE_LOADING()
},

@@ -102,7 +111,11 @@
async action(type) {
switch (type) {
case 'dk':
if (this.imgsrc == dkred) {
if ([5].includes(this.info.AttendanceType)) {
return
}
if ([4].includes(this.info.AttendanceType)) {
this.NAV_TO(`./single`, this.postData,true)
return
}
this.LOADING()
@@ -126,7 +139,7 @@
async judgeIsDK() {
let success = await this.HTTP_GET('learun/adms/attendance/IsAttendance', {}, '判断当前时间是否可以打卡失败')
if (!success) {
return
return false
}
this.info = success.data
this.imgsrc = dk;
@@ -138,10 +151,28 @@
// this.imgsrc=dkred;
// }
if (![5].includes(this.info.AttendanceType)) {
// 保存原打卡状态,改为外勤
this.resInfo = {
AttendanceType:this.info.AttendanceType,
AttendanceTypeString:this.info.AttendanceTypeString,
}
this.$set(this.info, 'AttendanceType', 4)
this.$set(this.info, 'AttendanceTypeString', '外勤打卡')
this.$set(this.postData, 'AIsOut', 1)
// 获取定位,不是外勤时候改变状态
if (!window.BMapGL) await this.loadJScript()
this.map = new BMapGL.Map('container');
await this.isFieldPersonnel()
this.timer1 = setInterval(async ()=>{
if(this.isGetingLocal)return
this.isGetingLocal = true
await this.isFieldPersonnel()
// console.log(this.postData)
this.isGetingLocal = false
},3000)
}
return true
},
//返回
back() {
@@ -181,37 +212,71 @@
// new BMapGL.Point(116.404, 39.915)
let myP2 = await this.local();
if (!myP2) {
this.TOAST('获取定位失败!')
// this.TOAST('获取定位失败!')
this.$set(this.info, 'AttendanceType', 4)
this.$set(this.info, 'AttendanceTypeString', '外勤打卡')
this.$set(this.postData, 'AIsOut', 1)
return
}
let distance = this.map.getDistance(myP1, myP2).toFixed(2)
console.log('距离', distance, '打卡坐标:', myP1, '当前坐标:', myP2)
// alert('距离'+ distance)
// console.log('距离', distance, '打卡坐标:', myP1, '当前坐标:', myP2)
if (Number(distance) > Number(this.info.GPSRange)) {
this.$set(this.info, 'AttendanceType', 4)
this.$set(this.info, 'AttendanceTypeString', '外勤打卡')
this.$set(this.postData, 'AIsOut', 1)
} else {
this.$set(this.info, 'AttendanceType', this.resInfo.AttendanceType)
this.$set(this.info, 'AttendanceTypeString', this.resInfo.AttendanceTypeString)
this.$set(this.postData, 'AIsOut', 0)
}
},
// 获取当前位置
local() {
return new Promise(async (resolve) => {
let res = await this.getLocation()
// let res = {
// lng: 112.57205562051,
// lat: 37.742374280962
// }
if (!res) {
this.$set(this.postData, 'ALon', '')
this.$set(this.postData, 'ALat', '')
this.$set(this.postData, 'ClockPlace', '')
resolve(false)
}
new BMapGL.Convertor().translate([res], 3, 5, data => {
if(res.status == 0){
alert(res.points[0].lng + '' + res.points[0].lat)
resolve(res.points[0])
}else{
if (data.status == 0) {
// alert(data.points[0].lng + '' + data.points[0].lat)
this.$set(this.postData, 'ALon', data.points[0].lng)
this.$set(this.postData, 'ALat', data.points[0].lat)
let geoc = new BMapGL.Geocoder();
geoc.getLocation(data.points[0], (rs) => {
let addComp = rs.addressComponents;
let address =
addComp.province +
addComp.city +
addComp.district +
addComp.street +
addComp.streetNumber
this.$set(this.postData, 'ClockPlace', address)
resolve(data.points[0])
});
} else {
this.$set(this.postData, 'ALon', '')
this.$set(this.postData, 'ALat', '')
this.$set(this.postData, 'ClockPlace', '')
this.TOAST('获取定位失败!')
resolve(false)
}
})
});
},
}
},
destroyed() {
clearInterval(this.timer)
clearInterval(this.timer1)
}
}
</script>


+ 162
- 0
Learun.Framework.Ultimate V7/LearunApp-2.2.0/pages/AttendanceCard/single.vue Visa fil

@@ -0,0 +1,162 @@
<template>
<view class="page">
<view v-if="ready">
<uploadImage @input="setValue('AttendanceCard.ADPhoto', $event)" :accept="['camera']" :value="getValue('AttendanceCard.ADPhoto')"
:readonly="!edit" :number="1" title="照片上传" required/>
<l-textarea @input="setValue('AttendanceCard.ARemark', $event)" :value="getValue('AttendanceCard.ARemark')"
:readonly="!edit" title="备注" />
</view>

<view v-if="ready" class="bg-white margin-tb padding" style="padding-top: 0; overflow: hidden;">
<l-button v-if="edit" @click="action('save')" size="lg" color="green" class="block margin-top" block>
外勤打卡
</l-button>
</view>
</view>
</template>


<script>
/*
* 版 本 Learun-ADMS V7.0.3 力软敏捷开发框架(http://www.learun.cn)
* Copyright (c) 2013-2020 上海力软信息技术有限公司
* 创建人:超级管理员
* 日 期:2020-10-21 10:28
* 描 述:听课记录
*/

/**
* 本段代码由移动端代码生成器输出,移动端须 2.2.0 版本及以上可以使用
* 请在移动端 /pages.json 中的 pages 字段中添加一条记录:
* { "path": "pages/LogisticsManagement/AttendanceCard/single", "style": { "navigationBarTitleText": "表单详情页" } }
*
* (navigationBarTitleText 字段为本页面的标题文本,可以修改)
* (必须自行操作该步骤,力软代码生成器不会自动帮您修改 /pages.json 文件)
*/

import get from 'lodash/get'
import set from 'lodash/set'
import moment from 'moment'
import customPageMixins from '@/common/custompage.js'
import uploadImage from '@/components/uploadImage.vue'

export default {
mixins: [customPageMixins],
components:{
uploadImage
},

data() {
return {
// 页面相关参数
edit:true,
mode:null,
ready: false,
id:null,
params:{},

// 表单数据
current: {},
origin: {},

// 表单项数据结构
scheme: {
AttendanceCard: {
ADPhoto: {
type: 'upload_old',
title: '图片',
verify:'NotNull'
},
ARemark: {
type: 'textarea',
title: '备注'
},
},

},

// 数据源
dataSource: {
AttendanceCard: {},
}
}
},

async onLoad({
type,
id
}) {
await this.init(type, id)
},

methods: {
// 页面初始化
async init(type, id) {
this.LOADING('加载数据中...')
this.params = this.GET_PARAM()

// 拉取表单数据,同时拉取所有来自数据源的选单数据
await Promise.all([
() => {}
])
await this.fetchForm()

this.ready = true
this.HIDE_LOADING()
},

// 加载表单数据
async fetchForm() {
this.origin = await this.getDefaultForm()
this.current = this.COPY(this.origin)
},

// 点击 「编辑」、「重置」、「保存」、「删除」 按钮
async action(type) {
switch (type) {
case 'save':
const verifyResult = this.verifyForm()
if (verifyResult.length > 0) {
this.CONFIRM('表单验证失败', verifyResult.join('\n'))
return
}

if (!(await this.CONFIRM('提交确认', '确定要提交本页表单内容吗?', true))) {
return
}

this.LOADING('正在提交...')
const postData = await this.getPostData(this.id)
console.log(postData)
let strEntity = JSON.parse(postData.strEntity)
let strEntity_ = {...strEntity,...this.params}
this.HTTP_POST('learun/adms/attendance/clockin', {strEntity:JSON.stringify(strEntity_)}, '打卡失败').then(success => {
this.HIDE_LOADING()
if (!success) {
this.TOAST('打卡失败')
return
}
this.TOAST('打卡成功', 'success')
setTimeout(()=>{
this.NAV_BACK(2)
}, 500)
})
break

default:
break
}
},

// 获取表单值
getValue(path) {
return get(this.current, path)
},

// 设置表单值
setValue(path, val) {
set(this.current, path, val)
},
}
}
</script>

Laddar…
Avbryt
Spara