31 changed files with 1063 additions and 162 deletions
@ -0,0 +1,33 @@
|
||||
<template> |
||||
<view class="view"> |
||||
<slot></slot> |
||||
<view class="safe" v-if="isFit"></view> |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
name: 'cs-bottom-wrapper', |
||||
data() { |
||||
return {} |
||||
}, |
||||
computed: { |
||||
isFit() { |
||||
return uni.getStorageSync('SYSTEM').includes('iOS') |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
.view { |
||||
position: absolute; |
||||
bottom: 0; |
||||
left: 0; |
||||
width: 100%; |
||||
background-color: #fff; |
||||
.safe { |
||||
height: 25px; |
||||
} |
||||
} |
||||
</style> |
After Width: | Height: | Size: 5.9 KiB |
After Width: | Height: | Size: 955 B |
After Width: | Height: | Size: 44 KiB |
After Width: | Height: | Size: 77 KiB |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,3 +1,15 @@
|
||||
page { |
||||
background-color: #f9f9f9; |
||||
position: relative; |
||||
} |
||||
|
||||
button { |
||||
padding-left: 0; |
||||
padding-right: 0; |
||||
font-size: 16px; |
||||
line-height: inherit; |
||||
&::after { |
||||
content: ''; |
||||
display: none; |
||||
} |
||||
} |
||||
|
@ -1,46 +1,103 @@
|
||||
<template> |
||||
<view class="wd-flex wd-flex-col wd-flex-center" style="height: 100%; gap: 10px"> |
||||
<icon type="waiting" size="40" /> |
||||
<button @tap="btnLogin">允许登陆</button> |
||||
</view> |
||||
<view class="waiting"> |
||||
<u--image |
||||
src="/static/favicon.png" |
||||
width="144px" |
||||
height="144px" |
||||
mode="aspectFit" |
||||
:fade="true" |
||||
duration="200" |
||||
></u--image> |
||||
<button |
||||
class="login" |
||||
@click="login" |
||||
:loading="loading" |
||||
loadingText="授权中..." |
||||
v-if="!isLogin" |
||||
> |
||||
授权登录 |
||||
</button> |
||||
<text v-else class="title">登录成功</text> |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
import { |
||||
getAccessToken,getOpenId |
||||
} from '@/utils/auth' |
||||
import {qrLogin}from '@/api/login' |
||||
import { getAccessToken, getOpenId } from '@/utils/auth' |
||||
import { qrLogin } from '@/api/login' |
||||
|
||||
export default { |
||||
data() { |
||||
return { |
||||
scene:"" |
||||
} |
||||
}, |
||||
onShow(res) { |
||||
console.log(res) |
||||
}, |
||||
|
||||
methods: { |
||||
btnLogin() { |
||||
|
||||
const openid = getOpenId() |
||||
console.log('登陆信息发送', this.scene) |
||||
console.log('opeid', openid) |
||||
qrLogin({ |
||||
code:this.scene, |
||||
openid:getAccessToken() |
||||
}) |
||||
} |
||||
}, |
||||
|
||||
onLoad(query) { |
||||
// scene 需要使用 decodeURIComponent 才能获取到生成二维码时传入的 scene |
||||
this.scene = decodeURIComponent(query.scene) |
||||
|
||||
} |
||||
|
||||
} |
||||
export default { |
||||
data() { |
||||
return { |
||||
scene: '', |
||||
loading: false, |
||||
isLogin: false |
||||
} |
||||
}, |
||||
methods: { |
||||
login() { |
||||
if (!this.scene) { |
||||
uni |
||||
.showModal({ |
||||
content: '链接失效,请重新扫码', |
||||
showCancel: false |
||||
}) |
||||
.then(() => { |
||||
uni.exitMiniProgram({ |
||||
success: res => { |
||||
console.log(res) |
||||
} |
||||
}) |
||||
}) |
||||
} else { |
||||
this.loading = true |
||||
qrLogin({ |
||||
code: this.scene, |
||||
openid: getAccessToken() |
||||
}).then(res => { |
||||
this.loading = false |
||||
this.isLogin = true |
||||
uni.exitMiniProgram() |
||||
}) |
||||
} |
||||
} |
||||
}, |
||||
onLoad(query) { |
||||
// scene 需要使用 decodeURIComponent 才能获取到生成二维码时传入的 scene |
||||
this.scene = decodeURIComponent(query.scene) |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style lang="scss"></style> |
||||
<style lang="scss" scoped> |
||||
.waiting { |
||||
height: 100vh; |
||||
display: flex; |
||||
flex-flow: column nowrap; |
||||
justify-content: center; |
||||
align-items: center; |
||||
background: #17c653; |
||||
gap: 40px; |
||||
.login { |
||||
display: flex; |
||||
padding: var(--Number-12px, 12px) var(--Number-40px, 40px); |
||||
justify-content: center; |
||||
align-items: center; |
||||
border-radius: var(--Number-120px, 120px); |
||||
background: var(--LightMode-Light-Light, #fff); |
||||
box-shadow: 0px 3px 4px 0px rgba(0, 0, 0, 0.03); |
||||
color: var(--LightMode-Success-Success, #17c653); |
||||
font-family: 'PingFang SC'; |
||||
font-size: 16px; |
||||
font-style: normal; |
||||
font-weight: 500; |
||||
line-height: normal; |
||||
} |
||||
.title { |
||||
font-family: 'PingFang SC'; |
||||
font-size: 16px; |
||||
font-style: normal; |
||||
font-weight: 500; |
||||
line-height: normal; |
||||
} |
||||
} |
||||
</style> |
||||
|
@ -0,0 +1,464 @@
|
||||
<template> |
||||
<view class="view-container"> |
||||
<view class="box"> |
||||
<view class="header"> |
||||
<view class="wd-font-800">执法签到</view> |
||||
<view class="refresh" @tap="refresh"> |
||||
<image |
||||
src="/static/images/icon/refresh.png" |
||||
mode="aspectFit" |
||||
style="width: 16px; height: 16px" |
||||
></image> |
||||
刷新 |
||||
</view> |
||||
</view> |
||||
<view class="row codeview"> |
||||
<image :src="miniCode" mode="aspectFill" class="code"></image> |
||||
<view class="info"> |
||||
<view class=""> |
||||
<view class="wd-font-800">执法协同人员请</view> |
||||
<view class="wd-font-800">扫描二维码签到</view> |
||||
<view class="wd-font-800 wd-mt-8" style="color: #f8285a"> |
||||
(要求2人以上) |
||||
</view> |
||||
</view> |
||||
<view |
||||
class="wd-font-800" |
||||
style="color: #17c653; text-decoration: underline" |
||||
@tap="showTip = true" |
||||
> |
||||
无法正常签到? |
||||
</view> |
||||
</view> |
||||
</view> |
||||
<view class="row" v-if="list.length == 0"> |
||||
<view class="emty"> |
||||
<image |
||||
class="image" |
||||
src="/static/images/emty.png" |
||||
mode="aspectFill" |
||||
></image> |
||||
<text class="wd-text-14" style="color: #99a1b7"> |
||||
暂无处理结果 |
||||
</text> |
||||
</view> |
||||
</view> |
||||
<view class="locate-record row" v-for="item in list" :key="item.id"> |
||||
<view class="info"> |
||||
<u-avatar |
||||
:src="item.avtar" |
||||
size="40px" |
||||
shape="circle" |
||||
></u-avatar> |
||||
<view> |
||||
<view style="margin-bottom: 4px"> |
||||
<text style="font-weight: 800">{{ item.realName }}</text> |
||||
<text style="font-size: 13px; color: #4b5675"> |
||||
{{ item.deptName }} |
||||
</text> |
||||
</view> |
||||
<view style="font-size: 13px; color: #4b5675"> |
||||
{{ $util.formatDate(item.time, 'YYYY年M月D日 hh:mm') }} |
||||
</view> |
||||
</view> |
||||
</view> |
||||
<view class="wd-flex wd-flex-center" style="gap: 4px"> |
||||
<u-icon |
||||
name="checkmark-circle-fill" |
||||
size="16" |
||||
color="#17C653" |
||||
></u-icon> |
||||
<text class="wd-text-13">已签到</text> |
||||
</view> |
||||
</view> |
||||
</view> |
||||
|
||||
<cs-bottom-wrapper v-if="showBtn"> |
||||
<view class="operation"> |
||||
<button |
||||
class="btn green" |
||||
v-if="distance < radius" |
||||
@tap="locate" |
||||
:loading="loading" |
||||
> |
||||
确认签到 |
||||
</button> |
||||
<view class="btn grey" v-else>不在签到范围</view> |
||||
</view> |
||||
</cs-bottom-wrapper> |
||||
|
||||
<u-modal |
||||
:show="showTip" |
||||
width="295px" |
||||
closeOnClickOverlay |
||||
@close="showTip = false" |
||||
> |
||||
<view class="tip"> |
||||
<view class="header"> |
||||
<view>如无法签到 请确认以下</view> |
||||
<view>功能是否正常启用?</view> |
||||
</view> |
||||
<view class="info"> |
||||
<view class="msg"> |
||||
<u-icon name="wifi" color="#17C653" size="20"></u-icon> |
||||
开启手机设置GPS定位功能 |
||||
</view> |
||||
<view class="msg"> |
||||
<u-icon name="wifi" color="#17C653" size="20"></u-icon> |
||||
开启手机授权微信定位功能 |
||||
</view> |
||||
<view class="msg"> |
||||
<u-icon name="wifi" color="#17C653" size="20"></u-icon> |
||||
开启右上角小程序定位功能 |
||||
</view> |
||||
</view> |
||||
</view> |
||||
<template #confirmButton> |
||||
<view class="wd-flex wd-flex-center"> |
||||
<view class="confirm-btn" @tap="showTip = false">确认</view> |
||||
</view> |
||||
</template> |
||||
</u-modal> |
||||
|
||||
<van-action-sheet :show="showModel"> |
||||
<view class="model-view"> |
||||
<image |
||||
class="image" |
||||
src="/static/images/task/position.png" |
||||
mode="aspectFill" |
||||
></image> |
||||
<view class="open-setting" @tap="openSetting()">开启定位</view> |
||||
</view> |
||||
</van-action-sheet> |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
import TencentMap from '@/static/js/qqmap-wx-jssdk.min.js' |
||||
import { getMiniCode } from '@/utils/getCode.js' |
||||
import { InspectionsApi } from '@/api/inspections/index.js' |
||||
import { getEnterPrise } from '@/api/enterprise/index.js' |
||||
export default { |
||||
data() { |
||||
return { |
||||
showModel: false, |
||||
inspectionsId: '', |
||||
miniCode: '', |
||||
enterpriseGps: '', |
||||
location: {}, |
||||
distance: '', |
||||
radius: 1000000, // 打卡范围 |
||||
showTip: false, |
||||
list: [], |
||||
loading: false, |
||||
share: { |
||||
title: '', |
||||
path: '', |
||||
imageUrl: '' |
||||
} |
||||
} |
||||
}, |
||||
computed: { |
||||
showBtn() { |
||||
return ( |
||||
this.list.findIndex(i => i.userId == this.$store.getters.userId) < |
||||
-1 |
||||
) |
||||
} |
||||
}, |
||||
onLoad(res) { |
||||
if (res.scene) { |
||||
const data = decodeURIComponent(res.scene) |
||||
const obj = {} |
||||
data.split('&').forEach(i => { |
||||
const arr = i.split('=') |
||||
obj[arr[0]] = arr[1] |
||||
}) |
||||
this.inspectionsId = obj.inspectionsId |
||||
this.enterpriseId = obj.enterpriseId |
||||
} else { |
||||
this.inspectionsId = res.inspectionsId |
||||
this.enterpriseId = res.enterpriseId |
||||
} |
||||
this.share = { |
||||
title: '', |
||||
path: `/sub/task/locate?inspectionsId=${this.inspectionsId}&enterpriseId=${this.enterpriseId}`, |
||||
imageUrl: require('@/static/images/locateImage.png') |
||||
} |
||||
this.init() |
||||
}, |
||||
onReady() { |
||||
this.useAuth() |
||||
}, |
||||
onShareAppMessage() { |
||||
return this.share |
||||
}, |
||||
onShareTimeline() { |
||||
return this.share |
||||
}, |
||||
methods: { |
||||
async init() { |
||||
const miniCode = await getMiniCode({ |
||||
scene: `inspectionsId=${this.inspectionsId}&enterpriseId=${this.enterpriseId}`, |
||||
page: 'sub/task/locate', |
||||
check_path: false, |
||||
env_version: 'develop', |
||||
is_hyaline: true |
||||
}) |
||||
const enterprise = await getEnterPrise(this.enterpriseId) |
||||
this.enterprisesName = enterprise.data.enterprisesName |
||||
this.enterpriseGps = enterprise.data.gpsLocation |
||||
uni.setNavigationBarTitle({ |
||||
title: this.enterprisesName |
||||
}) |
||||
this.share.title = `执法签到-${this.enterprisesName}` |
||||
this.getLocateList() |
||||
this.miniCode = miniCode |
||||
}, |
||||
async getLocateList() { |
||||
const res = await InspectionsApi.getLocate(this.inspectionsId) |
||||
//找到专管员签到状态 |
||||
const isLead = res.data.findIndex(i => { |
||||
i.userId == this.$store.getters.userId && i.isInspect |
||||
}) |
||||
console.log(isLead) |
||||
this.list = res.data |
||||
}, |
||||
useAuth() { |
||||
const that = this |
||||
uni.getSetting({ |
||||
success: res => { |
||||
// 如果有授权直接获取位置,没有则拉起授权 |
||||
res.authSetting['scope.userLocation'] |
||||
? _getLocation() |
||||
: _getAuth() |
||||
} |
||||
}) |
||||
/** |
||||
* 打开微信设置 |
||||
*/ |
||||
function _getAuth() { |
||||
uni.authorize({ |
||||
scope: 'scope.userLocation', |
||||
success: res => { |
||||
_getLocation() |
||||
}, |
||||
fail: () => { |
||||
that.showModel = true |
||||
} |
||||
}) |
||||
} |
||||
function _getLocation() { |
||||
uni.getLocation({ |
||||
type: 'gcj02', |
||||
success: res => { |
||||
that.location = { |
||||
latitude: res.latitude, |
||||
longitude: res.longitude |
||||
} |
||||
|
||||
that.getDistance() |
||||
}, |
||||
fail: err => { |
||||
that.showTip = true |
||||
uni.showToast({ |
||||
icon: 'none', |
||||
title: '获取定位失败' |
||||
}) |
||||
} |
||||
}) |
||||
} |
||||
}, |
||||
openSetting() { |
||||
const that = this |
||||
uni.openSetting({ |
||||
success: res => { |
||||
that.showModel = false |
||||
that.useAuth() |
||||
} |
||||
}) |
||||
}, |
||||
getDistance() { |
||||
const that = this |
||||
const map = new TencentMap({ |
||||
key: 'PQ5BZ-GZ5C7-RTMXB-HSAPB-3TOAV-5CBLZ' |
||||
}) |
||||
map.calculateDistance({ |
||||
mode: 'straight', |
||||
form: this.location, |
||||
to: this.enterpriseGps, |
||||
success: res => { |
||||
that.distance = res.result.elements[0].distance |
||||
} |
||||
}) |
||||
}, |
||||
refresh() { |
||||
this.getDistance() |
||||
this.getLocateList() |
||||
}, |
||||
async locate() { |
||||
this.loading = true |
||||
const res = await InspectionsApi.locate({ |
||||
inspectionsId: this.inspectionsId |
||||
}) |
||||
this.loading = false |
||||
uni.showToast({ |
||||
icon: 'none', |
||||
title: res.data |
||||
}) |
||||
this.refresh() |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
.view-container { |
||||
height: 100vh; |
||||
padding: 12px; |
||||
|
||||
.box { |
||||
background-color: #fff; |
||||
border-radius: $cs-border-radius; |
||||
padding: 16px; |
||||
display: flex; |
||||
flex-flow: column nowrap; |
||||
gap: 12px; |
||||
} |
||||
.header { |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: space-between; |
||||
.refresh { |
||||
display: flex; |
||||
align-items: center; |
||||
gap: 4px; |
||||
color: $cs-color-main; |
||||
font-weight: bold; |
||||
} |
||||
} |
||||
.row { |
||||
border-radius: var(--Number-8px, 8px); |
||||
border: 1px solid var(--LightMode-Grey-Grey-100, #f9f9f9); |
||||
background: var(--LightMode-Light-Light, #fff); |
||||
padding: 16px; |
||||
} |
||||
.codeview { |
||||
display: flex; |
||||
justify-content: space-between; |
||||
.code { |
||||
width: 320rpx; |
||||
height: 320rpx; |
||||
} |
||||
.info { |
||||
display: flex; |
||||
flex-direction: column; |
||||
justify-content: space-between; |
||||
gap: 80rpx; |
||||
} |
||||
} |
||||
} |
||||
.locate-record { |
||||
display: flex; |
||||
justify-content: space-between; |
||||
.info { |
||||
display: flex; |
||||
justify-content: space-between; |
||||
align-items: center; |
||||
gap: 12px; |
||||
} |
||||
} |
||||
.emty { |
||||
display: flex; |
||||
flex-direction: column; |
||||
align-items: center; |
||||
gap: 12px; |
||||
padding: 24px 12px; |
||||
border-radius: 8px; |
||||
.image { |
||||
width: 64px; |
||||
height: 64px; |
||||
} |
||||
} |
||||
.operation { |
||||
padding: 12px; |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
gap: 12px; |
||||
.btn { |
||||
flex: 1; |
||||
border-radius: 8px; |
||||
display: flex; |
||||
padding: 12px 0; |
||||
align-items: center; |
||||
justify-content: center; |
||||
} |
||||
.green { |
||||
background-color: $cs-color-main; |
||||
color: #fff; |
||||
} |
||||
.grey { |
||||
background: #f1f1f4; |
||||
color: #99a1b7; |
||||
} |
||||
} |
||||
.model-view { |
||||
height: 700rpx; |
||||
display: flex; |
||||
flex-direction: column; |
||||
align-items: center; |
||||
gap: 24px; |
||||
.image { |
||||
width: 470rpx; |
||||
height: 420rpx; |
||||
} |
||||
.open-setting { |
||||
padding: var(--Number-12px, 12px) var(--Number-48px, 48px); |
||||
gap: 10px; |
||||
border-radius: var(--Number-120px, 120px); |
||||
background: var(--LightMode-Success-Success, #17c653); |
||||
color: #fff; |
||||
font-size: 16px; |
||||
font-weight: 500; |
||||
/* Widget Shadow */ |
||||
box-shadow: 0px 3px 4px 0px rgba(0, 0, 0, 0.03); |
||||
} |
||||
} |
||||
.confirm-btn { |
||||
background-color: $cs-color-main; |
||||
padding: 12px 40px; |
||||
color: #fff; |
||||
font-size: 16px; |
||||
font-weight: 600; |
||||
border-radius: 120px; |
||||
} |
||||
.tip { |
||||
display: flex; |
||||
flex-flow: column nowrap; |
||||
gap: 24px; |
||||
.header { |
||||
display: flex; |
||||
flex-flow: column nowrap; |
||||
justify-content: center; |
||||
align-items: center; |
||||
color: #17c653; |
||||
font-style: italic; |
||||
font-weight: bold; |
||||
} |
||||
.info { |
||||
display: flex; |
||||
flex-flow: column nowrap; |
||||
justify-content: center; |
||||
align-items: center; |
||||
color: #071437; |
||||
gap: 16px; |
||||
.msg { |
||||
display: flex; |
||||
justify-content: center; |
||||
align-items: center; |
||||
gap: 4px; |
||||
} |
||||
} |
||||
} |
||||
</style> |
@ -0,0 +1,44 @@
|
||||
let AccessToken |
||||
|
||||
function getAccessToken() { |
||||
return new Promise((resolve, reject) => { |
||||
uni.request({ |
||||
url: 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=wx6d80755768234f3b&secret=c8180f2ab1b8454d403c7aa336782e21', |
||||
method: "GET", |
||||
success(res) { |
||||
resolve(res.data.access_token) |
||||
}, |
||||
fail(err) { |
||||
console.error(err) |
||||
resolve(false) |
||||
} |
||||
}) |
||||
}) |
||||
} |
||||
|
||||
|
||||
function getCode(data, token) { |
||||
return new Promise((resolve) => { |
||||
uni.request({ |
||||
url: `https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=${token}`, |
||||
method: "POST", |
||||
responseType: "arraybuffer", |
||||
data: JSON.stringify(data), |
||||
success(res) { |
||||
resolve(res) |
||||
}, |
||||
fail(err) { |
||||
console.error(err) |
||||
resolve(false) |
||||
} |
||||
}) |
||||
}) |
||||
} |
||||
|
||||
export async function getMiniCode(data) { |
||||
const token = await getAccessToken() |
||||
const code = await getCode(data, token) |
||||
const arrayBuffer = new Uint8Array(code.data) |
||||
const base64 = wx.arrayBufferToBase64(arrayBuffer) |
||||
return `data:image/jpeg;base64,${base64}` |
||||
} |
Loading…
Reference in new issue