修改
This commit is contained in:
1 parent
e871efadd9
commit
e44cf79082
10 files changed
+1391
-1642
No files matched your search
+1
-45
@@ -4,52 +4,8 @@
|
||||
// onShow(() => {})
|
||||
// onHide(() => {})
|
||||
|
||||
interface TabbarItem {
|
||||
pagePath: string
|
||||
text: string
|
||||
iconPath: string
|
||||
selectedIconPath: string
|
||||
launchParam: string[]
|
||||
auth: string[]
|
||||
}
|
||||
|
||||
const tabbar: TabbarItem[] = []
|
||||
|
||||
// 获取启动参数
|
||||
const getLaunchParam = () => {
|
||||
return new URL(window.location.href).searchParams.get("launchParam")
|
||||
}
|
||||
|
||||
// 设置tabbar项目
|
||||
const setTabbarItem = (index: number, item: TabbarItem, visible: boolean) => {
|
||||
uni.setTabBarItem({
|
||||
index,
|
||||
pagePath: item.pagePath,
|
||||
text: item.text,
|
||||
iconPath: item.iconPath,
|
||||
selectedIconPath: item.selectedIconPath,
|
||||
visible,
|
||||
})
|
||||
}
|
||||
|
||||
// 根据启动参数设置tabbar项目
|
||||
const setTabbarByLaunchParam = (launchParam: string) => {
|
||||
tabbar.forEach((item, index) => {
|
||||
const visible = item.launchParam.includes(launchParam)
|
||||
setTabbarItem(index, item, visible)
|
||||
})
|
||||
}
|
||||
|
||||
// 根据用户角色设置tabbar项目
|
||||
const setTabbarByUserRole = (role: string) => {
|
||||
tabbar.forEach((item, index) => {
|
||||
const visible = item.auth.includes(role)
|
||||
setTabbarItem(index, item, visible)
|
||||
})
|
||||
}
|
||||
|
||||
export default {
|
||||
async onLaunch() {
|
||||
onLaunch() {
|
||||
// 检查登录状态
|
||||
const token = uni.getStorageSync("token")
|
||||
if (!token) {
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
<view class="visitor-page">
|
||||
<!-- 顶部导航 -->
|
||||
<view class="header">
|
||||
<view class="back-btn" @tap="goBack">
|
||||
<text class="back-icon">〈</text>
|
||||
</view>
|
||||
<view class="title">普通访客入园预约</view>
|
||||
<view class="right-placeholder"></view>
|
||||
</view>
|
||||
@@ -13,7 +10,7 @@
|
||||
<scroll-view scroll-y class="page-content">
|
||||
<view class="content-wrapper">
|
||||
<view class="tips-box">
|
||||
<text class="tips-icon">ℹ</text>
|
||||
<text class="tips-icon">!</text>
|
||||
<text class="tips-text">入园预约请填写以下信息:</text>
|
||||
</view>
|
||||
|
||||
@@ -21,13 +18,13 @@
|
||||
<view class="form-content">
|
||||
<view class="form-item">
|
||||
<text class="label">您的姓名</text>
|
||||
<text class="required">*必填</text>
|
||||
<text class="required">*</text>
|
||||
<input type="text" v-model="formData.name" placeholder="请输入姓名" placeholder-class="placeholder" class="input" />
|
||||
</view>
|
||||
|
||||
<view class="form-item">
|
||||
<text class="label">联系方式</text>
|
||||
<text class="required">*必填</text>
|
||||
<text class="required">*</text>
|
||||
<input type="text" v-model="formData.mobile" placeholder="请输入手机号" placeholder-class="placeholder" class="input" />
|
||||
</view>
|
||||
|
||||
@@ -44,7 +41,7 @@
|
||||
<text :class="['placeholder', formData.reasonValue ? 'active' : '']">
|
||||
{{ formData.reasonLabel || "请选择拜访原因" }}
|
||||
</text>
|
||||
<text class="arrow">></text>
|
||||
<text class="c-coolGray"> ></text>
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
@@ -96,7 +93,7 @@
|
||||
|
||||
<view class="form-item select-item" @tap="showUserPicker">
|
||||
<text class="label">接待人</text>
|
||||
<text class="required">*必填</text>
|
||||
<text class="required">*</text>
|
||||
<view class="select-right">
|
||||
<text class="select-value">{{ selectedUserName || "请选择" }}</text>
|
||||
<text class="select-arrow">></text>
|
||||
@@ -105,7 +102,7 @@
|
||||
|
||||
<view class="form-item select-item" @tap="showTimePicker">
|
||||
<text class="label">到访时间</text>
|
||||
<text class="required">*必填</text>
|
||||
<text class="required">*</text>
|
||||
<view class="select-right">
|
||||
<text class="select-value">{{ selectedTime || "请选择" }}</text>
|
||||
<text class="select-arrow">></text>
|
||||
@@ -221,21 +218,6 @@
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<scroll-view scroll-y class="user-list" :style="{ height: '50vh' }">
|
||||
<view
|
||||
v-for="user in filteredUserList"
|
||||
:key="user.userId"
|
||||
class="user-item"
|
||||
:class="{ active: selectedUserId === user.userId }"
|
||||
@tap="selectUser(user)"
|
||||
>
|
||||
<text class="user-name">{{ user.name }}</text>
|
||||
</view>
|
||||
<view v-if="filteredUserList.length === 0" class="empty-tip">
|
||||
{{ searchKeyword ? "未找到相关接待人" : "暂无接待人数据" }}
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<view class="popup-footer">
|
||||
<view class="confirm-btn" @tap="confirmUserSelect">确定</view>
|
||||
</view>
|
||||
@@ -754,6 +736,7 @@ onMounted(async () => {
|
||||
color: #323233;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.5rpx;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.right-placeholder {
|
||||
@@ -826,7 +809,6 @@ onMounted(async () => {
|
||||
}
|
||||
|
||||
.select-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@@ -1,93 +1,72 @@
|
||||
<template>
|
||||
<view class="visitor-list">
|
||||
<!-- 顶部标签页 -->
|
||||
<view class="tabs">
|
||||
<view
|
||||
v-for="(tab, index) in tabs"
|
||||
:key="index"
|
||||
class="tab-item"
|
||||
:class="{ active: currentTab === index }"
|
||||
@tap="switchTab(index)"
|
||||
>
|
||||
<text>{{ tab }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="visitor-list">
|
||||
<!-- 顶部标签页 -->
|
||||
<view class="tabs">
|
||||
<view v-for="(tab, index) in tabs" :key="index" class="tab-item" :class="{ active: currentTab === index }" @tap="switchTab(index)">
|
||||
<text>{{ tab }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 访客记录列表 -->
|
||||
<scroll-view
|
||||
scroll-y
|
||||
class="list-content"
|
||||
:refresher-enabled="true"
|
||||
:refresher-triggered="refreshing"
|
||||
@refresherrefresh="onRefresh"
|
||||
@scrolltolower="loadMore"
|
||||
>
|
||||
<view
|
||||
v-for="item in visitorList"
|
||||
:key="item.id"
|
||||
class="visitor-item"
|
||||
@tap="goToDetail(item)"
|
||||
>
|
||||
<view class="item-header">
|
||||
<text class="visit-time">{{ formatTime(item.vstartDate, item.vendDate) }}</text>
|
||||
<text :class="['status-tag', getStatusClass(item.checkStatus)]">
|
||||
{{ getStatusText(item.checkStatus) }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="item-content">
|
||||
<view class="info-row">
|
||||
<text class="label">接待人:</text>
|
||||
<text class="value">{{ item.userName }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="label">联系方式:</text>
|
||||
<text class="value">{{ item.mobile }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="label">预约时间:</text>
|
||||
<text class="value">{{ formatCreateTime(item.createTime) }}</text>
|
||||
</view>
|
||||
<view v-if="item.description" class="info-row">
|
||||
<text class="label">备注:</text>
|
||||
<text class="value">{{ item.description }}</text>
|
||||
</view>
|
||||
<view v-if="item.checkReason" class="info-row check-reason">
|
||||
<text class="label">审核意见:</text>
|
||||
<text class="value">{{ item.checkReason }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="item-footer" v-if="canCancel(item.checkStatus) || canVisit(item.checkStatus)">
|
||||
<view class="btn-group">
|
||||
<view
|
||||
v-if="canVisit(item.checkStatus)"
|
||||
class="visit-btn"
|
||||
@tap="handleVisit(item)"
|
||||
>已到访</view>
|
||||
<view
|
||||
v-if="canCancel(item.checkStatus)"
|
||||
class="cancel-btn"
|
||||
@tap="handleCancel(item)"
|
||||
>取消</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 访客记录列表 -->
|
||||
<scroll-view
|
||||
scroll-y
|
||||
class="list-content"
|
||||
:refresher-enabled="true"
|
||||
:refresher-triggered="refreshing"
|
||||
@refresherrefresh="onRefresh"
|
||||
@scrolltolower="loadMore"
|
||||
>
|
||||
<view v-for="item in visitorList" :key="item.id" class="visitor-item" @tap="goToDetail(item)">
|
||||
<view class="item-header">
|
||||
<text class="visit-time">{{ formatTime(item.vstartDate, item.vendDate) }}</text>
|
||||
<text :class="['status-tag', getStatusClass(item.checkStatus)]">
|
||||
{{ getStatusText(item.checkStatus) }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="item-content">
|
||||
<view class="info-row">
|
||||
<text class="label">接待人:</text>
|
||||
<text class="value">{{ item.userName }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="label">联系方式:</text>
|
||||
<text class="value">{{ item.mobile }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="label">预约时间:</text>
|
||||
<text class="value">{{ formatCreateTime(item.createTime) }}</text>
|
||||
</view>
|
||||
<view v-if="item.description" class="info-row">
|
||||
<text class="label">备注:</text>
|
||||
<text class="value">{{ item.description }}</text>
|
||||
</view>
|
||||
<view v-if="item.checkReason" class="info-row check-reason">
|
||||
<text class="label">审核意见:</text>
|
||||
<text class="value">{{ item.checkReason }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="item-footer" v-if="canCancel(item.checkStatus) || canVisit(item.checkStatus)">
|
||||
<view class="btn-group">
|
||||
<view v-if="canVisit(item.checkStatus)" class="visit-btn" @tap="handleVisit(item)">已到访</view>
|
||||
<view v-if="canCancel(item.checkStatus)" class="cancel-btn" @tap="handleCancel(item)">取消</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 加载状态 -->
|
||||
<view v-if="loading && !refreshing" class="loading">加载中...</view>
|
||||
<view v-if="!loading && !hasMore" class="no-more">没有更多数据了</view>
|
||||
<view v-if="!loading && visitorList.length === 0" class="empty">
|
||||
暂无访客记录
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
<!-- 加载状态 -->
|
||||
<view v-if="loading && !refreshing" class="loading">加载中...</view>
|
||||
<view v-if="!loading && !hasMore" class="no-more">没有更多数据了</view>
|
||||
<view v-if="!loading && visitorList.length === 0" class="empty">暂无访客记录</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import visitorApi from '@/sheep/api/integrated/visitors'
|
||||
import { ref, onMounted } from "vue"
|
||||
import visitorApi from "@/sheep/api/integrated/visitors"
|
||||
|
||||
// 标签页配置
|
||||
const tabs = ['全部', '待审核', '待到访', '已到访', '已取消']
|
||||
const tabs = ["全部", "待审核", "待到访", "已到访", "已取消"]
|
||||
const currentTab = ref(0)
|
||||
|
||||
// 列表数据
|
||||
@@ -100,380 +79,383 @@ const size = 10
|
||||
|
||||
// 格式化访问时间
|
||||
const formatTime = (start: string, end: string) => {
|
||||
if (!start || !end) return ''
|
||||
const startDate = new Date(start)
|
||||
const endDate = new Date(end)
|
||||
return `${startDate.getMonth() + 1}/${startDate.getDate()} ${startDate.getHours()}:00-${endDate.getHours()}:00`
|
||||
if (!start || !end) return ""
|
||||
const startDate = new Date(start)
|
||||
const endDate = new Date(end)
|
||||
return `${startDate.getMonth() + 1}/${startDate.getDate()} ${startDate.getHours()}:00-${endDate.getHours()}:00`
|
||||
}
|
||||
|
||||
// 格式化创建时间
|
||||
const formatCreateTime = (time: string) => {
|
||||
if (!time) return ''
|
||||
const date = new Date(time)
|
||||
return `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()} ${date.getHours()}:${date.getMinutes()}`
|
||||
if (!time) return ""
|
||||
const date = new Date(time)
|
||||
return `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()} ${date.getHours()}:${date.getMinutes()}`
|
||||
}
|
||||
|
||||
// 获取状态文本
|
||||
const getStatusText = (status: string) => {
|
||||
const statusMap: Record<string, string> = {
|
||||
'1': '待审核',
|
||||
'2': '待到访',
|
||||
'0': '已到访',
|
||||
'5': '已取消'
|
||||
}
|
||||
return statusMap[status] || status
|
||||
const statusMap: Record<string, string> = {
|
||||
"1": "待审核",
|
||||
"2": "待到访",
|
||||
"0": "已到访",
|
||||
"5": "已取消",
|
||||
}
|
||||
return statusMap[status] || status
|
||||
}
|
||||
|
||||
// 获取状态样式
|
||||
const getStatusClass = (status: string) => {
|
||||
const statusMap: Record<string, string> = {
|
||||
'1': 'status-pending',
|
||||
'2': 'status-waiting',
|
||||
'0': 'status-visited',
|
||||
'5': 'status-cancelled'
|
||||
}
|
||||
return statusMap[status] || ''
|
||||
const statusMap: Record<string, string> = {
|
||||
"1": "status-pending",
|
||||
"2": "status-waiting",
|
||||
"0": "status-visited",
|
||||
"5": "status-cancelled",
|
||||
}
|
||||
return statusMap[status] || ""
|
||||
}
|
||||
|
||||
// 切换标签页
|
||||
const switchTab = (index: number) => {
|
||||
if (currentTab.value === index) return
|
||||
currentTab.value = index
|
||||
resetList()
|
||||
getVisitorList()
|
||||
if (currentTab.value === index) return
|
||||
currentTab.value = index
|
||||
resetList()
|
||||
getVisitorList()
|
||||
}
|
||||
|
||||
// 重置列表状态
|
||||
const resetList = () => {
|
||||
current.value = 1
|
||||
visitorList.value = []
|
||||
hasMore.value = true
|
||||
current.value = 1
|
||||
visitorList.value = []
|
||||
hasMore.value = true
|
||||
}
|
||||
|
||||
// 获取访客记录列表
|
||||
const getVisitorList = async () => {
|
||||
if (loading.value || (!hasMore.value && !refreshing.value)) return
|
||||
loading.value = true
|
||||
|
||||
try {
|
||||
const params: any = {
|
||||
current: current.value,
|
||||
size
|
||||
}
|
||||
|
||||
// 添加状态筛选
|
||||
if (currentTab.value > 0) {
|
||||
const statusMap = ['', '1', '2', '0', '5']
|
||||
params.checkStatus = statusMap[currentTab.value]
|
||||
}
|
||||
if (loading.value || (!hasMore.value && !refreshing.value)) return
|
||||
loading.value = true
|
||||
|
||||
const { code, data } = await visitorApi.getVisitorList(params)
|
||||
|
||||
if (code === 0 && data) {
|
||||
if (refreshing.value || current.value === 1) {
|
||||
visitorList.value = data.records || []
|
||||
} else {
|
||||
visitorList.value.push(...(data.records || []))
|
||||
}
|
||||
|
||||
hasMore.value = (data.records || []).length === size
|
||||
if (hasMore.value) {
|
||||
current.value++
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取访客记录失败:', error)
|
||||
uni.showToast({
|
||||
title: '获取数据失败',
|
||||
icon: 'none'
|
||||
})
|
||||
} finally {
|
||||
loading.value = false
|
||||
refreshing.value = false
|
||||
}
|
||||
try {
|
||||
const params: any = {
|
||||
current: current.value,
|
||||
size,
|
||||
}
|
||||
|
||||
// 添加状态筛选
|
||||
if (currentTab.value > 0) {
|
||||
const statusMap = ["", "1", "2", "0", "5"]
|
||||
params.checkStatus = statusMap[currentTab.value]
|
||||
}
|
||||
|
||||
const { code, data } = await visitorApi.getVisitorList(params)
|
||||
|
||||
if (code === 0 && data) {
|
||||
if (refreshing.value || current.value === 1) {
|
||||
visitorList.value = data.records || []
|
||||
} else {
|
||||
visitorList.value.push(...(data.records || []))
|
||||
}
|
||||
|
||||
hasMore.value = (data.records || []).length === size
|
||||
if (hasMore.value) {
|
||||
current.value++
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("获取访客记录失败:", error)
|
||||
uni.showToast({
|
||||
title: "获取数据失败",
|
||||
icon: "none",
|
||||
})
|
||||
} finally {
|
||||
loading.value = false
|
||||
refreshing.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 下拉刷新
|
||||
const onRefresh = async () => {
|
||||
refreshing.value = true
|
||||
resetList()
|
||||
await getVisitorList()
|
||||
refreshing.value = true
|
||||
resetList()
|
||||
await getVisitorList()
|
||||
}
|
||||
|
||||
// 上拉加载更多
|
||||
const loadMore = () => {
|
||||
if (!loading.value && hasMore.value) {
|
||||
getVisitorList()
|
||||
}
|
||||
if (!loading.value && hasMore.value) {
|
||||
getVisitorList()
|
||||
}
|
||||
}
|
||||
|
||||
// 判断是否可以取消
|
||||
const canCancel = (status: string) => {
|
||||
return status === '1' || status === '2' || status === '5'
|
||||
return status === "1" || status === "2" || status === "5"
|
||||
}
|
||||
|
||||
// 判断是否可以标记到访
|
||||
const canVisit = (status: string) => {
|
||||
return status === '2'
|
||||
return status === "2"
|
||||
}
|
||||
|
||||
// 修改取消预约方法
|
||||
const handleCancel = (item: any) => {
|
||||
if (!item.id) return
|
||||
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定要取消此次预约吗?',
|
||||
success: async (res) => {
|
||||
if (res.confirm) {
|
||||
try {
|
||||
uni.showLoading({
|
||||
title: '取消中...',
|
||||
mask: true
|
||||
})
|
||||
|
||||
const { code, msg } = await visitorApi.cancelVisitor(item.id)
|
||||
|
||||
uni.hideLoading()
|
||||
|
||||
if (code === 0) {
|
||||
uni.showToast({
|
||||
title: '取消成功',
|
||||
icon: 'success'
|
||||
})
|
||||
// 刷新列表
|
||||
resetList()
|
||||
getVisitorList()
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: msg || '取消失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: '取消失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
if (!item.id) return
|
||||
|
||||
uni.showModal({
|
||||
title: "提示",
|
||||
content: "确定要取消此次预约吗?",
|
||||
success: async (res) => {
|
||||
if (res.confirm) {
|
||||
try {
|
||||
uni.showLoading({
|
||||
title: "取消中...",
|
||||
mask: true,
|
||||
})
|
||||
|
||||
const { code, msg } = await visitorApi.cancelVisitor(item.id)
|
||||
|
||||
uni.hideLoading()
|
||||
|
||||
if (code === 0) {
|
||||
uni.showToast({
|
||||
title: "取消成功",
|
||||
icon: "success",
|
||||
})
|
||||
// 刷新列表
|
||||
resetList()
|
||||
getVisitorList()
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: msg || "取消失败",
|
||||
icon: "none",
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: "取消失败",
|
||||
icon: "none",
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// 处理到访
|
||||
const handleVisit = (item: any) => {
|
||||
if (!item.id) return
|
||||
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确认访客已到访?',
|
||||
success: async (res) => {
|
||||
if (res.confirm) {
|
||||
try {
|
||||
uni.showLoading({
|
||||
title: '处理中...',
|
||||
mask: true
|
||||
})
|
||||
|
||||
const { code, msg } = await visitorApi.haveVisitor(item.id)
|
||||
|
||||
uni.hideLoading()
|
||||
|
||||
if (code === 0) {
|
||||
uni.showToast({
|
||||
title: '操作成功',
|
||||
icon: 'success'
|
||||
})
|
||||
// 刷新列表
|
||||
resetList()
|
||||
getVisitorList()
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: msg || '操作失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: '操作失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
if (!item.id) return
|
||||
|
||||
uni.showModal({
|
||||
title: "提示",
|
||||
content: "确认访客已到访?",
|
||||
success: async (res) => {
|
||||
if (res.confirm) {
|
||||
try {
|
||||
uni.showLoading({
|
||||
title: "处理中...",
|
||||
mask: true,
|
||||
})
|
||||
|
||||
const { code, msg } = await visitorApi.haveVisitor(item.id)
|
||||
|
||||
uni.hideLoading()
|
||||
|
||||
if (code === 0) {
|
||||
uni.showToast({
|
||||
title: "操作成功",
|
||||
icon: "success",
|
||||
})
|
||||
// 刷新列表
|
||||
resetList()
|
||||
getVisitorList()
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: msg || "操作失败",
|
||||
icon: "none",
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: "操作失败",
|
||||
icon: "none",
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// 跳转到详情页
|
||||
const goToDetail = (item: any) => {
|
||||
uni.navigateTo({
|
||||
url: `/pages/integrated/visitor/visitorDetail?id=${item.id}&token=${item.token}`
|
||||
})
|
||||
uni.navigateTo({
|
||||
url: `/pages/integrated/visitor/visitorDetail?id=${item.id}&token=${item.token}`,
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getVisitorList()
|
||||
getVisitorList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.visitor-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
|
||||
.tabs {
|
||||
display: flex;
|
||||
background-color: #fff;
|
||||
padding: 20rpx 0;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
border-bottom: 1rpx solid #eee;
|
||||
.tabs {
|
||||
display: flex;
|
||||
background-color: #fff;
|
||||
padding: 20rpx 0;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
border-bottom: 1rpx solid #eee;
|
||||
|
||||
.tab-item {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
padding: 20rpx 0;
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
position: relative;
|
||||
.tab-item {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
padding: 20rpx 0;
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
position: relative;
|
||||
|
||||
&.active {
|
||||
color: #1777FF;
|
||||
font-weight: bold;
|
||||
&.active {
|
||||
color: #1777ff;
|
||||
font-weight: bold;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 40rpx;
|
||||
height: 4rpx;
|
||||
background-color: #1777FF;
|
||||
border-radius: 2rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 40rpx;
|
||||
height: 4rpx;
|
||||
background-color: #1777ff;
|
||||
border-radius: 2rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.list-content {
|
||||
flex: 1;
|
||||
height: calc(100vh - 100rpx);
|
||||
padding: 20rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.list-content {
|
||||
flex: 1;
|
||||
height: calc(100vh - 100rpx);
|
||||
padding: 20rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.visitor-item {
|
||||
background-color: #fff;
|
||||
border-radius: 12rpx;
|
||||
padding: 20rpx;
|
||||
margin-bottom: 20rpx;
|
||||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
|
||||
.visitor-item {
|
||||
background-color: #fff;
|
||||
border-radius: 12rpx;
|
||||
padding: 20rpx;
|
||||
margin-bottom: 20rpx;
|
||||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
|
||||
|
||||
.item-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20rpx;
|
||||
.item-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
.visit-time {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
.visit-time {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.status-tag {
|
||||
padding: 4rpx 16rpx;
|
||||
border-radius: 20rpx;
|
||||
font-size: 24rpx;
|
||||
.status-tag {
|
||||
padding: 4rpx 16rpx;
|
||||
border-radius: 20rpx;
|
||||
font-size: 24rpx;
|
||||
|
||||
&.status-pending {
|
||||
background-color: #fff7e6;
|
||||
color: #fa8c16;
|
||||
}
|
||||
&.status-pending {
|
||||
background-color: #fff7e6;
|
||||
color: #fa8c16;
|
||||
}
|
||||
|
||||
&.status-waiting {
|
||||
background-color: #e6f7ff;
|
||||
color: #1890ff;
|
||||
}
|
||||
&.status-waiting {
|
||||
background-color: #e6f7ff;
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
&.status-visited {
|
||||
background-color: #f6ffed;
|
||||
color: #52c41a;
|
||||
}
|
||||
&.status-visited {
|
||||
background-color: #f6ffed;
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
&.status-cancelled {
|
||||
background-color: #fff1f0;
|
||||
color: #f5222d;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.status-cancelled {
|
||||
background-color: #fff1f0;
|
||||
color: #f5222d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.item-content {
|
||||
.info-row {
|
||||
display: flex;
|
||||
margin-bottom: 12rpx;
|
||||
font-size: 26rpx;
|
||||
.item-content {
|
||||
.info-row {
|
||||
display: flex;
|
||||
margin-bottom: 12rpx;
|
||||
font-size: 26rpx;
|
||||
|
||||
.label {
|
||||
color: #999;
|
||||
width: 140rpx;
|
||||
}
|
||||
.label {
|
||||
color: #999;
|
||||
width: 140rpx;
|
||||
}
|
||||
|
||||
.value {
|
||||
color: #333;
|
||||
flex: 1;
|
||||
}
|
||||
.value {
|
||||
color: #333;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
&.check-reason {
|
||||
.value {
|
||||
color: #f5222d;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&.check-reason {
|
||||
.value {
|
||||
color: #f5222d;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.item-footer {
|
||||
margin-top: 20rpx;
|
||||
padding-top: 20rpx;
|
||||
border-top: 1rpx solid #eee;
|
||||
.item-footer {
|
||||
margin-top: 20rpx;
|
||||
padding-top: 20rpx;
|
||||
border-top: 1rpx solid #eee;
|
||||
|
||||
.btn-group {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 20rpx;
|
||||
.btn-group {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 20rpx;
|
||||
|
||||
.visit-btn, .cancel-btn {
|
||||
padding: 10rpx 30rpx;
|
||||
border-radius: 30rpx;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
.visit-btn,
|
||||
.cancel-btn {
|
||||
padding: 10rpx 30rpx;
|
||||
border-radius: 30rpx;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
.visit-btn {
|
||||
background-color: #1777FF;
|
||||
color: #fff;
|
||||
}
|
||||
.visit-btn {
|
||||
background-color: #1777ff;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.cancel-btn {
|
||||
background-color: #fff;
|
||||
color: #666;
|
||||
border: 1rpx solid #ddd;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.cancel-btn {
|
||||
background-color: #fff;
|
||||
color: #666;
|
||||
border: 1rpx solid #ddd;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.loading, .no-more, .empty {
|
||||
text-align: center;
|
||||
padding: 30rpx;
|
||||
color: #999;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
.loading,
|
||||
.no-more,
|
||||
.empty {
|
||||
text-align: center;
|
||||
padding: 30rpx;
|
||||
color: #999;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,226 +1,180 @@
|
||||
<template>
|
||||
<view class="verify-page">
|
||||
<!-- 顶部导航 -->
|
||||
<view class="header">
|
||||
<view class="back-btn" @tap="goBack">
|
||||
<text class="back-icon">〈</text>
|
||||
</view>
|
||||
<view class="title">访客核销</view>
|
||||
<view class="right-placeholder"></view>
|
||||
</view>
|
||||
<view class="verify-page">
|
||||
<!-- 扫码按钮 -->
|
||||
<view class="scan-section">
|
||||
<view class="scan-btn" @tap="handleScan">
|
||||
<text class="scan-icon">📷</text>
|
||||
<text class="scan-text">扫描访客二维码</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 扫码按钮 -->
|
||||
<view class="scan-section">
|
||||
<view class="scan-btn" @tap="handleScan">
|
||||
<text class="scan-icon">📷</text>
|
||||
<text class="scan-text">扫描访客二维码</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 最近核销记录 -->
|
||||
<view class="verify-history" v-if="recentVerifyList.length > 0">
|
||||
<view class="section-title">最近核销记录</view>
|
||||
<view class="history-list">
|
||||
<view v-for="(item, index) in recentVerifyList" :key="index" class="history-item">
|
||||
<view class="visitor-info">
|
||||
<text class="name">{{ item.name }}</text>
|
||||
<text class="time">{{ formatDateTime(item.verifyTime) }}</text>
|
||||
</view>
|
||||
<view class="visit-info">
|
||||
<text class="dept">{{ item.deptName }}</text>
|
||||
<text class="status success">已核销</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 最近核销记录 -->
|
||||
<view class="verify-history" v-if="recentVerifyList.length > 0">
|
||||
<view class="section-title">最近核销记录</view>
|
||||
<view class="history-list">
|
||||
<view v-for="(item, index) in recentVerifyList" :key="index" class="history-item">
|
||||
<view class="visitor-info">
|
||||
<text class="name">{{ item.name }}</text>
|
||||
<text class="time">{{ formatDateTime(item.verifyTime) }}</text>
|
||||
</view>
|
||||
<view class="visit-info">
|
||||
<text class="dept">{{ item.deptName }}</text>
|
||||
<text class="status success">已核销</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import visitorApi from '@/sheep/api/integrated/visitors'
|
||||
import dayjs from 'dayjs'
|
||||
import { ref } from "vue"
|
||||
import visitorApi from "@/sheep/api/integrated/visitors"
|
||||
import dayjs from "dayjs"
|
||||
|
||||
const recentVerifyList = ref<any[]>([])
|
||||
|
||||
// 格式化时间
|
||||
const formatDateTime = (time: string) => {
|
||||
if (!time) return ''
|
||||
return dayjs(time).format('YYYY-MM-DD HH:mm')
|
||||
if (!time) return ""
|
||||
return dayjs(time).format("YYYY-MM-DD HH:mm")
|
||||
}
|
||||
|
||||
// 处理扫码
|
||||
const handleScan = () => {
|
||||
uni.scanCode({
|
||||
scanType: ['qrCode'],
|
||||
success: async (res) => {
|
||||
try {
|
||||
// 假设扫描结果就是token
|
||||
const token = res.result
|
||||
const { code, msg } = await visitorApi.verifyVisitor({ token })
|
||||
|
||||
if (code === 0) {
|
||||
uni.showToast({
|
||||
title: '核销成功',
|
||||
icon: 'success'
|
||||
})
|
||||
// 刷新最近核销记录
|
||||
// getRecentVerifyList()
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: msg || '核销失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
uni.showToast({
|
||||
title: '核销失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
},
|
||||
fail: () => {
|
||||
uni.showToast({
|
||||
title: '扫码失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
uni.scanCode({
|
||||
scanType: ["qrCode"],
|
||||
success: async (res) => {
|
||||
try {
|
||||
// 假设扫描结果就是token
|
||||
const token = res.result
|
||||
const { code, msg }: any = await visitorApi.verifyVisitor({ token })
|
||||
|
||||
// 返回上一页
|
||||
const goBack = () => {
|
||||
uni.navigateBack()
|
||||
if (code === 0) {
|
||||
uni.showToast({
|
||||
title: "核销成功",
|
||||
icon: "success",
|
||||
})
|
||||
// 刷新最近核销记录
|
||||
// getRecentVerifyList()
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: msg || "核销失败",
|
||||
icon: "none",
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
uni.showToast({
|
||||
title: "核销失败",
|
||||
icon: "none",
|
||||
})
|
||||
}
|
||||
},
|
||||
fail: () => {
|
||||
uni.showToast({
|
||||
title: "扫码失败",
|
||||
icon: "none",
|
||||
})
|
||||
},
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.verify-page {
|
||||
min-height: 100vh;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.header {
|
||||
background: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 90rpx;
|
||||
padding: 0 30rpx;
|
||||
border-bottom: 1rpx solid #f5f5f5;
|
||||
|
||||
.back-btn {
|
||||
width: 60rpx;
|
||||
height: 90rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.back-icon {
|
||||
font-size: 38rpx;
|
||||
color: #323233;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 36rpx;
|
||||
color: #323233;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.right-placeholder {
|
||||
width: 60rpx;
|
||||
}
|
||||
min-height: 100vh;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.scan-section {
|
||||
padding: 60rpx 30rpx;
|
||||
padding: 60rpx 30rpx;
|
||||
|
||||
.scan-btn {
|
||||
background: #fff;
|
||||
height: 200rpx;
|
||||
border-radius: 16rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
|
||||
.scan-btn {
|
||||
background: #fff;
|
||||
height: 200rpx;
|
||||
border-radius: 16rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
|
||||
|
||||
&:active {
|
||||
opacity: 0.8;
|
||||
}
|
||||
&:active {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.scan-icon {
|
||||
font-size: 60rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
.scan-icon {
|
||||
font-size: 60rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.scan-text {
|
||||
font-size: 32rpx;
|
||||
color: #323233;
|
||||
}
|
||||
}
|
||||
.scan-text {
|
||||
font-size: 32rpx;
|
||||
color: #323233;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.verify-history {
|
||||
padding: 0 30rpx;
|
||||
padding: 0 30rpx;
|
||||
|
||||
.section-title {
|
||||
font-size: 32rpx;
|
||||
color: #323233;
|
||||
font-weight: 500;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
.section-title {
|
||||
font-size: 32rpx;
|
||||
color: #323233;
|
||||
font-weight: 500;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.history-list {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
.history-list {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
|
||||
.history-item {
|
||||
padding: 20rpx 0;
|
||||
border-bottom: 1rpx solid #f5f5f5;
|
||||
.history-item {
|
||||
padding: 20rpx 0;
|
||||
border-bottom: 1rpx solid #f5f5f5;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.visitor-info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 12rpx;
|
||||
.visitor-info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 12rpx;
|
||||
|
||||
.name {
|
||||
font-size: 30rpx;
|
||||
color: #323233;
|
||||
font-weight: 500;
|
||||
}
|
||||
.name {
|
||||
font-size: 30rpx;
|
||||
color: #323233;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.time {
|
||||
font-size: 26rpx;
|
||||
color: #969799;
|
||||
}
|
||||
}
|
||||
.time {
|
||||
font-size: 26rpx;
|
||||
color: #969799;
|
||||
}
|
||||
}
|
||||
|
||||
.visit-info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
.visit-info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.dept {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
}
|
||||
.dept {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.status {
|
||||
font-size: 26rpx;
|
||||
.status {
|
||||
font-size: 26rpx;
|
||||
|
||||
&.success {
|
||||
color: #07C160;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&.success {
|
||||
color: #07c160;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
@@ -1,486 +1,486 @@
|
||||
<template>
|
||||
<view class="detail-page">
|
||||
<!-- 顶部导航 -->
|
||||
<view class="header">
|
||||
<view class="back-btn" @tap="goBack">
|
||||
<text class="back-icon">〈</text>
|
||||
</view>
|
||||
<view class="title">详情</view>
|
||||
<view class="right-placeholder"></view>
|
||||
</view>
|
||||
<view class="detail-page">
|
||||
<!-- 顶部导航 -->
|
||||
<view class="header">
|
||||
<view class="back-btn" @tap="goBack">
|
||||
<text class="back-icon">〈</text>
|
||||
</view>
|
||||
<view class="title">详情</view>
|
||||
<view class="right-placeholder"></view>
|
||||
</view>
|
||||
|
||||
<!-- 访客卡片 -->
|
||||
<view class="visit-card">
|
||||
<!-- 公司Logo -->
|
||||
<view class="company-logo">
|
||||
<image src="/static/images/company-logo.png" mode="aspectFit"></image>
|
||||
</view>
|
||||
<!-- 访客卡片 -->
|
||||
<view class="visit-card">
|
||||
<!-- 公司Logo -->
|
||||
<view class="company-logo">
|
||||
<image src="/static/images/company-logo.png" mode="aspectFit"></image>
|
||||
</view>
|
||||
|
||||
<!-- 访问信息 -->
|
||||
<view class="visit-info">
|
||||
<view class="visit-time">{{ formatDate(detail.vstartDate) }} {{ formatTime(detail.vstartDate) }}</view>
|
||||
<view class="visit-location">{{ detail.deptName }}</view>
|
||||
<view class="visit-address"></view>
|
||||
</view>
|
||||
<!-- 访问信息 -->
|
||||
<view class="visit-info">
|
||||
<view class="visit-time">{{ formatDate(detail.vstartDate) }} {{ formatTime(detail.vstartDate) }}</view>
|
||||
<view class="visit-location">{{ detail.deptName }}</view>
|
||||
<view class="visit-address"></view>
|
||||
</view>
|
||||
|
||||
<!-- 访客信息 -->
|
||||
<view class="visitor-info">
|
||||
<view class="info-row">
|
||||
<text class="label">邀请人</text>
|
||||
<text class="value">{{ detail.name }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="label">访客</text>
|
||||
<text class="value">{{ detail.userName }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 访客信息 -->
|
||||
<view class="visitor-info">
|
||||
<view class="info-row">
|
||||
<text class="label">邀请人</text>
|
||||
<text class="value">{{ detail.name }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="label">访客</text>
|
||||
<text class="value">{{ detail.userName }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 二维码 -->
|
||||
<view class="qr-code">
|
||||
<canvas type="2d" id="qrcode" canvas-id="qrcode" class="qr-canvas"></canvas>
|
||||
<text class="qr-tip">请于门卫或前台进行预访登记</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 二维码 -->
|
||||
<view class="qr-code">
|
||||
<canvas type="2d" id="qrcode" canvas-id="qrcode" class="qr-canvas"></canvas>
|
||||
<text class="qr-tip">请于门卫或前台进行预访登记</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 展开更多按钮 -->
|
||||
<view class="expand-btn" @tap="toggleExpand">
|
||||
<text>{{ isExpanded ? '收起' : '查看详细信息' }}</text>
|
||||
<text :class="['expand-icon', { 'expanded': isExpanded }]">{{ isExpanded ? '∧' : '∨' }}</text>
|
||||
</view>
|
||||
<!-- 展开更多按钮 -->
|
||||
<view class="expand-btn" @tap="toggleExpand">
|
||||
<text>{{ isExpanded ? "收起" : "查看详细信息" }}</text>
|
||||
<text :class="['expand-icon', { expanded: isExpanded }]">{{ isExpanded ? "∧" : "∨" }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 详细信息 -->
|
||||
<view class="detail-content" v-show="isExpanded">
|
||||
<!-- 原有的详细信息内容 -->
|
||||
<view class="detail-section">
|
||||
<view class="section-title">基本信息</view>
|
||||
<view class="section-item">
|
||||
<text class="label">拜访时间</text>
|
||||
<text class="value">{{ formatDateTime(detail.vstartDate) }} - {{ formatDateTime(detail.vendDate, true) }}</text>
|
||||
</view>
|
||||
<view class="section-item">
|
||||
<text class="label">访客姓名</text>
|
||||
<text class="value">{{ detail.name }}</text>
|
||||
</view>
|
||||
<view class="section-item">
|
||||
<text class="label">联系方式</text>
|
||||
<text class="value">{{ detail.mobile }}</text>
|
||||
</view>
|
||||
<view class="section-item">
|
||||
<text class="label">所属公司</text>
|
||||
<text class="value">{{ detail.companyName }}</text>
|
||||
</view>
|
||||
<view class="section-item">
|
||||
<text class="label">拜访对象</text>
|
||||
<text class="value">{{ detail.userName }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 详细信息 -->
|
||||
<view class="detail-content" v-show="isExpanded">
|
||||
<!-- 原有的详细信息内容 -->
|
||||
<view class="detail-section">
|
||||
<view class="section-title">基本信息</view>
|
||||
<view class="section-item">
|
||||
<text class="label">拜访时间</text>
|
||||
<text class="value">{{ formatDateTime(detail.vstartDate) }} - {{ formatDateTime(detail.vendDate, true) }}</text>
|
||||
</view>
|
||||
<view class="section-item">
|
||||
<text class="label">访客姓名</text>
|
||||
<text class="value">{{ detail.name }}</text>
|
||||
</view>
|
||||
<view class="section-item">
|
||||
<text class="label">联系方式</text>
|
||||
<text class="value">{{ detail.mobile }}</text>
|
||||
</view>
|
||||
<view class="section-item">
|
||||
<text class="label">所属公司</text>
|
||||
<text class="value">{{ detail.companyName }}</text>
|
||||
</view>
|
||||
<view class="section-item">
|
||||
<text class="label">拜访对象</text>
|
||||
<text class="value">{{ detail.userName }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 随行人员信息 -->
|
||||
<view class="detail-section" v-if="detail.isOtherPeople === '1' && detail.visitorsPeopleAppVos?.length">
|
||||
<view class="section-title">随行人员信息</view>
|
||||
<view v-for="(person, index) in detail.visitorsPeopleAppVos" :key="index" class="companion-item">
|
||||
<view class="section-item">
|
||||
<text class="label">访客姓名</text>
|
||||
<text class="value">{{ person.name }}</text>
|
||||
</view>
|
||||
<view class="section-item">
|
||||
<text class="label">手机号码</text>
|
||||
<text class="value">{{ person.mobile }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 随行人员信息 -->
|
||||
<view class="detail-section" v-if="detail.isOtherPeople === '1' && detail.visitorsPeopleAppVos?.length">
|
||||
<view class="section-title">随行人员信息</view>
|
||||
<view v-for="(person, index) in detail.visitorsPeopleAppVos" :key="index" class="companion-item">
|
||||
<view class="section-item">
|
||||
<text class="label">访客姓名</text>
|
||||
<text class="value">{{ person.name }}</text>
|
||||
</view>
|
||||
<view class="section-item">
|
||||
<text class="label">手机号码</text>
|
||||
<text class="value">{{ person.mobile }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 车辆信息 -->
|
||||
<view class="detail-section" v-if="detail.isDrive === '1' && detail.visitorsCarAppVos?.length">
|
||||
<view class="section-title">车辆信息</view>
|
||||
<view v-for="(car, index) in detail.visitorsCarAppVos" :key="index" class="section-item">
|
||||
<text class="label">车牌号</text>
|
||||
<text class="value">{{ car.carNumber }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 车辆信息 -->
|
||||
<view class="detail-section" v-if="detail.isDrive === '1' && detail.visitorsCarAppVos?.length">
|
||||
<view class="section-title">车辆信息</view>
|
||||
<view v-for="(car, index) in detail.visitorsCarAppVos" :key="index" class="section-item">
|
||||
<text class="label">车牌号</text>
|
||||
<text class="value">{{ car.carNumber }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 审核信息 -->
|
||||
<view class="detail-section" v-if="detail.checkStatus === '1' && detail.checkReason">
|
||||
<view class="section-title">审核信息</view>
|
||||
<view class="section-item">
|
||||
<text class="label">审核意见</text>
|
||||
<text class="value">{{ detail.checkReason }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 审核信息 -->
|
||||
<view class="detail-section" v-if="detail.checkStatus === '1' && detail.checkReason">
|
||||
<view class="section-title">审核信息</view>
|
||||
<view class="section-item">
|
||||
<text class="label">审核意见</text>
|
||||
<text class="value">{{ detail.checkReason }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 提交信息 -->
|
||||
<view class="detail-section">
|
||||
<view class="section-item">
|
||||
<text class="label">提交时间</text>
|
||||
<text class="value">{{ formatDateTime(detail.createTime) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 提交信息 -->
|
||||
<view class="detail-section">
|
||||
<view class="section-item">
|
||||
<text class="label">提交时间</text>
|
||||
<text class="value">{{ formatDateTime(detail.createTime) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, nextTick } from 'vue'
|
||||
import visitorApi from '@/sheep/api/integrated/visitors'
|
||||
import dayjs from 'dayjs'
|
||||
import QRCode from 'qrcode'
|
||||
import { ref, onMounted, nextTick } from "vue"
|
||||
import visitorApi from "@/sheep/api/integrated/visitors"
|
||||
import dayjs from "dayjs"
|
||||
import QRCode from "qrcode"
|
||||
|
||||
const detail = ref<any>({})
|
||||
const isExpanded = ref(false)
|
||||
|
||||
// 格式化日期(年月日)
|
||||
const formatDate = (time: string) => {
|
||||
if (!time) return ''
|
||||
const weekMap: { [key: string]: string } = {
|
||||
'Mon': '一',
|
||||
'Tue': '二',
|
||||
'Wed': '三',
|
||||
'Thu': '四',
|
||||
'Fri': '五',
|
||||
'Sat': '六',
|
||||
'Sun': '日'
|
||||
}
|
||||
const date = dayjs(time)
|
||||
const weekEn = date.format('ddd')
|
||||
const weekCn = weekMap[weekEn] || weekEn
|
||||
return `${date.format('M月D日')} 周${weekCn}`
|
||||
if (!time) return ""
|
||||
const weekMap: { [key: string]: string } = {
|
||||
Mon: "一",
|
||||
Tue: "二",
|
||||
Wed: "三",
|
||||
Thu: "四",
|
||||
Fri: "五",
|
||||
Sat: "六",
|
||||
Sun: "日",
|
||||
}
|
||||
const date = dayjs(time)
|
||||
const weekEn = date.format("ddd")
|
||||
const weekCn = weekMap[weekEn] || weekEn
|
||||
return `${date.format("M月D日")} 周${weekCn}`
|
||||
}
|
||||
|
||||
// 格式化时间(时分)
|
||||
const formatTime = (time: string) => {
|
||||
if (!time) return ''
|
||||
return dayjs(time).format('HH:mm')
|
||||
if (!time) return ""
|
||||
return dayjs(time).format("HH:mm")
|
||||
}
|
||||
|
||||
// 格式化完整时间
|
||||
const formatDateTime = (time: string, onlyTime = false) => {
|
||||
if (!time) return ''
|
||||
return onlyTime ? dayjs(time).format('HH:mm') : dayjs(time).format('YYYY-MM-DD HH:mm')
|
||||
if (!time) return ""
|
||||
return onlyTime ? dayjs(time).format("HH:mm") : dayjs(time).format("YYYY-MM-DD HH:mm")
|
||||
}
|
||||
|
||||
// 生成二维码
|
||||
const generateQRCode = async (token: string) => {
|
||||
try {
|
||||
// #ifdef H5
|
||||
const base64 = await QRCode.toDataURL(token, {
|
||||
width: 200,
|
||||
margin: 2,
|
||||
color: {
|
||||
dark: '#000000',
|
||||
light: '#ffffff'
|
||||
}
|
||||
})
|
||||
if (base64) {
|
||||
const img = new Image()
|
||||
img.src = base64
|
||||
img.style.width = '400rpx'
|
||||
img.style.height = '400rpx'
|
||||
const container = document.querySelector('.qr-code')
|
||||
const canvas = document.querySelector('#qrcode')
|
||||
if (container && canvas) {
|
||||
const div = document.createElement('div')
|
||||
div.style.width = '400rpx'
|
||||
div.style.height = '400rpx'
|
||||
div.style.marginBottom = '20rpx'
|
||||
div.appendChild(img)
|
||||
container.replaceChild(div, canvas)
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
try {
|
||||
// #ifdef H5
|
||||
const base64 = await QRCode.toDataURL(token, {
|
||||
width: 200,
|
||||
margin: 2,
|
||||
color: {
|
||||
dark: "#000000",
|
||||
light: "#ffffff",
|
||||
},
|
||||
})
|
||||
if (base64) {
|
||||
const img = new Image()
|
||||
img.src = base64
|
||||
img.style.width = "400rpx"
|
||||
img.style.height = "400rpx"
|
||||
const container = document.querySelector(".qr-code")
|
||||
const canvas = document.querySelector("#qrcode")
|
||||
if (container && canvas) {
|
||||
const div = document.createElement("div")
|
||||
div.style.width = "400rpx"
|
||||
div.style.height = "400rpx"
|
||||
div.style.marginBottom = "20rpx"
|
||||
div.appendChild(img)
|
||||
container.replaceChild(div, canvas)
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
const query = uni.createSelectorQuery()
|
||||
query
|
||||
.select('#qrcode')
|
||||
.fields({ node: true, size: true })
|
||||
.exec((res) => {
|
||||
if (res[0] && res[0].node) {
|
||||
const canvas = res[0].node
|
||||
const ctx = canvas.getContext('2d')
|
||||
// #ifdef MP-WEIXIN
|
||||
const query = uni.createSelectorQuery()
|
||||
query
|
||||
.select("#qrcode")
|
||||
.fields({ node: true, size: true })
|
||||
.exec((res) => {
|
||||
if (res[0] && res[0].node) {
|
||||
const canvas = res[0].node
|
||||
const ctx = canvas.getContext("2d")
|
||||
|
||||
// 设置canvas大小
|
||||
const dpr = uni.getSystemInfoSync().pixelRatio
|
||||
canvas.width = 200 * dpr
|
||||
canvas.height = 200 * dpr
|
||||
ctx.scale(dpr, dpr)
|
||||
// 设置canvas大小
|
||||
const dpr = uni.getSystemInfoSync().pixelRatio
|
||||
canvas.width = 200 * dpr
|
||||
canvas.height = 200 * dpr
|
||||
ctx.scale(dpr, dpr)
|
||||
|
||||
// 生成二维码
|
||||
QRCode.toCanvas(canvas, token, {
|
||||
width: 200,
|
||||
margin: 2,
|
||||
color: {
|
||||
dark: '#000000',
|
||||
light: '#ffffff'
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
} catch (error) {
|
||||
console.error('生成二维码失败:', error)
|
||||
}
|
||||
// 生成二维码
|
||||
QRCode.toCanvas(canvas, token, {
|
||||
width: 200,
|
||||
margin: 2,
|
||||
color: {
|
||||
dark: "#000000",
|
||||
light: "#ffffff",
|
||||
},
|
||||
})
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
} catch (error) {
|
||||
console.error("生成二维码失败:", error)
|
||||
}
|
||||
}
|
||||
|
||||
// 展开/收起详情
|
||||
const toggleExpand = () => {
|
||||
isExpanded.value = !isExpanded.value
|
||||
isExpanded.value = !isExpanded.value
|
||||
}
|
||||
|
||||
// 获取详情数据
|
||||
const getDetail = async () => {
|
||||
try {
|
||||
const pages = getCurrentPages()
|
||||
const currentPage = pages[pages.length - 1]
|
||||
const { token } = currentPage.$page?.options || {}
|
||||
try {
|
||||
const pages = getCurrentPages()
|
||||
const currentPage = pages[pages.length - 1]
|
||||
const { token } = currentPage.$page?.options || {}
|
||||
|
||||
if (!token) {
|
||||
uni.showToast({
|
||||
title: '参数错误',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
if (!token) {
|
||||
uni.showToast({
|
||||
title: "参数错误",
|
||||
icon: "none",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
const { code, data } = await visitorApi.getVisitorDetail({ token })
|
||||
if (code === 0 && data) {
|
||||
detail.value = data
|
||||
// 延迟生成二维码,确保canvas已经渲染
|
||||
// #ifdef H5
|
||||
nextTick(() => {
|
||||
generateQRCode(token)
|
||||
})
|
||||
// #endif
|
||||
const { code, data } = await visitorApi.getVisitorDetail({ token })
|
||||
if (code === 0 && data) {
|
||||
detail.value = data
|
||||
// 延迟生成二维码,确保canvas已经渲染
|
||||
// #ifdef H5
|
||||
nextTick(() => {
|
||||
generateQRCode(token)
|
||||
})
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
setTimeout(() => {
|
||||
generateQRCode(token)
|
||||
}, 300)
|
||||
// #endif
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取详情失败:', error)
|
||||
uni.showToast({
|
||||
title: '获取详情失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
// #ifdef MP-WEIXIN
|
||||
setTimeout(() => {
|
||||
generateQRCode(token)
|
||||
}, 300)
|
||||
// #endif
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("获取详情失败:", error)
|
||||
uni.showToast({
|
||||
title: "获取详情失败",
|
||||
icon: "none",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 返回上一页
|
||||
const goBack = () => {
|
||||
uni.navigateBack()
|
||||
uni.navigateBack()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getDetail()
|
||||
getDetail()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.detail-page {
|
||||
min-height: 100vh;
|
||||
background: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.header {
|
||||
background: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 90rpx;
|
||||
padding: 0 30rpx;
|
||||
border-bottom: 1rpx solid #f5f5f5;
|
||||
background: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 90rpx;
|
||||
padding: 0 30rpx;
|
||||
border-bottom: 1rpx solid #f5f5f5;
|
||||
|
||||
.back-btn {
|
||||
width: 60rpx;
|
||||
height: 90rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.back-btn {
|
||||
width: 60rpx;
|
||||
height: 90rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.back-icon {
|
||||
font-size: 38rpx;
|
||||
color: #323233;
|
||||
}
|
||||
.back-icon {
|
||||
font-size: 38rpx;
|
||||
color: #323233;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 36rpx;
|
||||
color: #323233;
|
||||
font-weight: 500;
|
||||
}
|
||||
.title {
|
||||
font-size: 36rpx;
|
||||
color: #323233;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.right-placeholder {
|
||||
width: 60rpx;
|
||||
}
|
||||
.right-placeholder {
|
||||
width: 60rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.visit-card {
|
||||
margin: 30rpx;
|
||||
padding: 40rpx;
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
|
||||
margin: 30rpx;
|
||||
padding: 40rpx;
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
|
||||
|
||||
.company-logo {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
margin: 0 auto 30rpx;
|
||||
background: #f5f5f5;
|
||||
border-radius: 8rpx;
|
||||
overflow: hidden;
|
||||
.company-logo {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
margin: 0 auto 30rpx;
|
||||
background: #f5f5f5;
|
||||
border-radius: 8rpx;
|
||||
overflow: hidden;
|
||||
|
||||
image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.visit-info {
|
||||
text-align: center;
|
||||
margin-bottom: 40rpx;
|
||||
.visit-info {
|
||||
text-align: center;
|
||||
margin-bottom: 40rpx;
|
||||
|
||||
.visit-time {
|
||||
font-size: 36rpx;
|
||||
color: #323233;
|
||||
font-weight: 500;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
.visit-time {
|
||||
font-size: 36rpx;
|
||||
color: #323233;
|
||||
font-weight: 500;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.visit-location {
|
||||
font-size: 32rpx;
|
||||
color: #323233;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
.visit-location {
|
||||
font-size: 32rpx;
|
||||
color: #323233;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.visit-address {
|
||||
font-size: 28rpx;
|
||||
color: #969799;
|
||||
}
|
||||
}
|
||||
.visit-address {
|
||||
font-size: 28rpx;
|
||||
color: #969799;
|
||||
}
|
||||
}
|
||||
|
||||
.visitor-info {
|
||||
margin-bottom: 40rpx;
|
||||
.visitor-info {
|
||||
margin-bottom: 40rpx;
|
||||
|
||||
.info-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 20rpx 0;
|
||||
border-bottom: 1rpx solid #f5f5f5;
|
||||
.info-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 20rpx 0;
|
||||
border-bottom: 1rpx solid #f5f5f5;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 28rpx;
|
||||
color: #969799;
|
||||
}
|
||||
.label {
|
||||
font-size: 28rpx;
|
||||
color: #969799;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 28rpx;
|
||||
color: #323233;
|
||||
}
|
||||
}
|
||||
}
|
||||
.value {
|
||||
font-size: 28rpx;
|
||||
color: #323233;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.qr-code {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin: 40rpx 0;
|
||||
.qr-code {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin: 40rpx 0;
|
||||
|
||||
.qr-canvas {
|
||||
width: 400rpx !important;
|
||||
height: 400rpx !important;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
.qr-canvas {
|
||||
width: 400rpx !important;
|
||||
height: 400rpx !important;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.qr-tip {
|
||||
font-size: 24rpx;
|
||||
color: #969799;
|
||||
}
|
||||
}
|
||||
.qr-tip {
|
||||
font-size: 24rpx;
|
||||
color: #969799;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.expand-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20rpx;
|
||||
margin: 0 30rpx;
|
||||
background: #fff;
|
||||
border-radius: 8rpx;
|
||||
font-size: 28rpx;
|
||||
color: #1989FA;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20rpx;
|
||||
margin: 0 30rpx;
|
||||
background: #fff;
|
||||
border-radius: 8rpx;
|
||||
font-size: 28rpx;
|
||||
color: #1989fa;
|
||||
|
||||
.expand-icon {
|
||||
margin-left: 8rpx;
|
||||
transition: transform 0.3s;
|
||||
.expand-icon {
|
||||
margin-left: 8rpx;
|
||||
transition: transform 0.3s;
|
||||
|
||||
&.expanded {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
&.expanded {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.detail-content {
|
||||
padding: 20rpx 30rpx;
|
||||
padding: 20rpx 30rpx;
|
||||
}
|
||||
|
||||
.detail-section {
|
||||
margin-bottom: 20rpx;
|
||||
padding: 20rpx;
|
||||
background: #fff;
|
||||
border-radius: 12rpx;
|
||||
margin-bottom: 20rpx;
|
||||
padding: 20rpx;
|
||||
background: #fff;
|
||||
border-radius: 12rpx;
|
||||
|
||||
.section-title {
|
||||
font-size: 30rpx;
|
||||
color: #323233;
|
||||
font-weight: 500;
|
||||
margin-bottom: 20rpx;
|
||||
padding-left: 20rpx;
|
||||
border-left: 4rpx solid #1989FA;
|
||||
}
|
||||
.section-title {
|
||||
font-size: 30rpx;
|
||||
color: #323233;
|
||||
font-weight: 500;
|
||||
margin-bottom: 20rpx;
|
||||
padding-left: 20rpx;
|
||||
border-left: 4rpx solid #1989fa;
|
||||
}
|
||||
}
|
||||
|
||||
.section-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 24rpx 0;
|
||||
border-bottom: 1rpx solid #f5f5f5;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 24rpx 0;
|
||||
border-bottom: 1rpx solid #f5f5f5;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 28rpx;
|
||||
color: #969799;
|
||||
}
|
||||
.label {
|
||||
font-size: 28rpx;
|
||||
color: #969799;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 28rpx;
|
||||
color: #323233;
|
||||
text-align: right;
|
||||
flex: 1;
|
||||
margin-left: 20rpx;
|
||||
}
|
||||
.value {
|
||||
font-size: 28rpx;
|
||||
color: #323233;
|
||||
text-align: right;
|
||||
flex: 1;
|
||||
margin-left: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.companion-item {
|
||||
margin-bottom: 20rpx;
|
||||
padding: 20rpx;
|
||||
background: #f7f8fa;
|
||||
border-radius: 8rpx;
|
||||
margin-bottom: 20rpx;
|
||||
padding: 20rpx;
|
||||
background: #f7f8fa;
|
||||
border-radius: 8rpx;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.section-item {
|
||||
padding: 12rpx 0;
|
||||
}
|
||||
.section-item {
|
||||
padding: 12rpx 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
+17
-116
@@ -21,103 +21,35 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="m-t-20rpx rounded-20rpx shadow-main p-30rpx p-b-40rpx bg-white!">
|
||||
<view class="service_item flex-row items-center justify-between" v-for="(v, i) in state.menuList" :key="i" @tap="onTabMenu(v, i)">
|
||||
<view class="flex-row-center">
|
||||
<image class="w-40rpx h-40rpx" :src="v.icon" mode="aspectFill" />
|
||||
<text class="sub-text-28 m-l-20rpx">{{ v.text }}</text>
|
||||
</view>
|
||||
<view class="mt-4 rounded-20rpx shadow-main p-30rpx bg-white!">
|
||||
<view class="flex items-center justify-between" @tap="onTabMenu">
|
||||
<text class="sub-text-36 m-l-20rpx">核销</text>
|
||||
<view class="iconfont icon-youjiantou" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- <view v-if="isDevEnv()" class="flex-col-center m-t-40rpx m-x-30rpx">
|
||||
<view class="w-full text-center sub-text-28 p-y-20rpx rounded-40rpx border-solid border border-#0094ff" @tap="doLogout">
|
||||
退出登录
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="state.appInfo == 'sh'" class="flex-col-center m-t-40rpx m-x-30rpx">
|
||||
<view class="w-full text-center sub-text-28 p-y-20rpx rounded-40rpx border-solid border border-#0094ff" @tap="doChangeRole">
|
||||
切换角色
|
||||
</view>
|
||||
</view> -->
|
||||
</crud-layout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref } from "vue"
|
||||
import { onLoad, onShow } from "@dcloudio/uni-app"
|
||||
import { reactive } from "vue"
|
||||
import { onLoad } from "@dcloudio/uni-app"
|
||||
import { isEmpty } from "radash"
|
||||
import { filePath } from "@@/utils/file"
|
||||
import { getAppInfo } from "@@/utils/getAppInfo"
|
||||
import { hooks, router, constant } from "@@/index"
|
||||
import { userApi } from "@@/api"
|
||||
import isDevEnv from "@/sheep/utils/isDevEnv"
|
||||
import { usePayStore } from "@/sheep/stores/pay"
|
||||
|
||||
const state = reactive<{
|
||||
wallet: any[]
|
||||
userInfo: any
|
||||
menuList: {
|
||||
text: string
|
||||
icon: string
|
||||
router: string
|
||||
}[]
|
||||
}>({
|
||||
wallet: [],
|
||||
const state = reactive<any>({
|
||||
userInfo: {},
|
||||
menuList: [{ text: "人脸管理", icon: "/static/icon/service_5.png", router: "/pages/human-face/list" }],
|
||||
})
|
||||
|
||||
// 常量
|
||||
const STORAGE_KEYS = {
|
||||
TOKEN: constant.storageConstant.TOKEN,
|
||||
USER_INFO: constant.storageConstant.USERINFO,
|
||||
}
|
||||
// 获取存储的用户信息
|
||||
const getUserInfo = () => {
|
||||
const tokenStorage = uni.getStorageSync("token")
|
||||
const userStorage = uni.getStorageSync("user_info")
|
||||
|
||||
const storage = {
|
||||
// 获取存储的用户信息
|
||||
getUserInfo: () => {
|
||||
const tokenStorage = uni.getStorageSync(STORAGE_KEYS.TOKEN)
|
||||
const userStorage = uni.getStorageSync(STORAGE_KEYS.USER_INFO)
|
||||
|
||||
if (!isEmpty(userStorage) && !isEmpty(tokenStorage)) {
|
||||
state.userInfo = userStorage
|
||||
} else {
|
||||
state.userInfo = {}
|
||||
}
|
||||
},
|
||||
|
||||
// 保存用户信息到存储
|
||||
saveUserInfo: (userData: any) => {
|
||||
uni.setStorageSync(STORAGE_KEYS.USER_INFO, userData)
|
||||
storage.getUserInfo()
|
||||
},
|
||||
|
||||
// 保存token到存储
|
||||
saveToken: (tokenData: any) => {
|
||||
uni.setStorageSync(STORAGE_KEYS.TOKEN, tokenData)
|
||||
},
|
||||
|
||||
// 清除用户登录相关的存储
|
||||
clearUserStorage: () => {
|
||||
uni.removeStorageSync(STORAGE_KEYS.TOKEN)
|
||||
uni.removeStorageSync(STORAGE_KEYS.USER_INFO)
|
||||
if (!isEmpty(userStorage) && !isEmpty(tokenStorage)) {
|
||||
state.userInfo = userStorage
|
||||
} else {
|
||||
state.userInfo = {}
|
||||
},
|
||||
}
|
||||
|
||||
const login = {
|
||||
// 退出登录
|
||||
logout: async () => {
|
||||
const isLogin = await hooks.useCheckLogin()
|
||||
if (!isLogin) return
|
||||
|
||||
uni.showLoading({ title: "退出登录中...", mask: true })
|
||||
storage.clearUserStorage()
|
||||
uni.hideLoading()
|
||||
uni.showToast({ title: "退出登录成功", icon: "none" })
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// 点击登录
|
||||
@@ -128,42 +60,11 @@ const onTabLogin = () => {
|
||||
}
|
||||
|
||||
// 点击菜单
|
||||
const onTabMenu = (v: any, i: number) => {
|
||||
if (v.router) {
|
||||
uni.navigateTo({
|
||||
url: v.router,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 退出登录
|
||||
const doLogout = () => {
|
||||
uni.showModal({
|
||||
title: "提示",
|
||||
content: "确定要退出登录吗?",
|
||||
success: async (res) => {
|
||||
if (res.confirm) {
|
||||
await login.logout()
|
||||
}
|
||||
},
|
||||
})
|
||||
const onTabMenu = () => {
|
||||
uni.navigateTo({ url: "/pages/integrated/visitor/verify" })
|
||||
}
|
||||
|
||||
onLoad(() => {
|
||||
storage.getUserInfo()
|
||||
})
|
||||
|
||||
onShow(() => {
|
||||
storage.getUserInfo()
|
||||
getUserInfo()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.service_item {
|
||||
padding: 20rpx 0;
|
||||
border-bottom: 1rpx solid #f5f5f5;
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -94,9 +94,6 @@ const login = async () => {
|
||||
code: result.code,
|
||||
}
|
||||
|
||||
console.log("result.code", result.code) // 0e3XvSll2ptntf4bg3ol2BdVFH1XvSlM
|
||||
console.log("params", params)
|
||||
|
||||
const res: any = await request({
|
||||
url: "/auth/oauth2/token?grant_type=mobile",
|
||||
method: "post",
|
||||
|
||||
@@ -2,8 +2,7 @@ import request from "@/sheep/utils/request"
|
||||
import { encryption } from "@/sheep/utils/encryption"
|
||||
|
||||
// 将 Basic Auth 字符串提取为常量
|
||||
// const BASIC_AUTH = "Basic " + window.btoa(import.meta.env.VITE_OAUTH2_APP_CLIENT)
|
||||
const BASIC_AUTH = "Basic " + import.meta.env.VITE_OAUTH2_APP_CLIENT
|
||||
const BASIC_AUTH = "Basic " + window.btoa(import.meta.env.VITE_OAUTH2_APP_CLIENT)
|
||||
|
||||
// 定义接口类型
|
||||
interface LoginParams {
|
||||
|
||||
@@ -78,6 +78,7 @@ function handleSuccessMessage(response: any) {
|
||||
// 创建请求实例
|
||||
const http = new Request({
|
||||
baseURL: import.meta.env.VITE_API_URL,
|
||||
// baseURL: import.meta.env.VITE_APP_PROD_SERVICE,
|
||||
timeout: 15000,
|
||||
method: "GET",
|
||||
header: {
|
||||
|
||||
Reference in new issue
Block a user