feat: 添加权限查询模块UI

This commit is contained in:
魏少阳 2025-01-22 11:38:52 +08:00
parent cfa564f458
commit 216e29873d
14 changed files with 2002 additions and 3 deletions

View File

@ -286,6 +286,71 @@
"navigationStyle": "custom"
}
},
{
"path": "pages/personnel-passage/AccessManage/access-permission-inquiry",
"type": "page",
"layout": "default",
"style": {
"navigationStyle": "custom"
}
},
{
"path": "pages/personnel-passage/AccessManage/access-right-detection",
"type": "page",
"layout": "default",
"style": {
"navigationStyle": "custom"
}
},
{
"path": "pages/personnel-passage/AccessManage/add-permission-group",
"type": "page",
"layout": "default",
"style": {
"navigationStyle": "custom"
}
},
{
"path": "pages/personnel-passage/AccessManage/delivery-status",
"type": "page",
"layout": "default",
"style": {
"navigationStyle": "custom"
}
},
{
"path": "pages/personnel-passage/AccessManage/permission-query",
"type": "page",
"layout": "default",
"style": {
"navigationStyle": "custom"
}
},
{
"path": "pages/personnel-passage/AccessManage/release-record",
"type": "page",
"layout": "default",
"style": {
"navigationStyle": "custom"
}
},
{
"path": "pages/personnel-passage/AccessManage/send-permission",
"type": "page",
"layout": "default",
"style": {
"navigationStyle": "custom"
}
},
{
"path": "pages/personnel-passage/AccessManage/viewing-delivery-records",
"type": "page",
"layout": "default",
"style": {
"navigationStyle": "custom",
"disableScroll": false
}
},
{
"path": "pages/personnel-passage/access-authority/access-authority",
"type": "page",

View File

@ -7,6 +7,7 @@
}
</route>
<!-- 门禁管理 -->
<template>
<view class="container">
<!-- 开局引导 -->
@ -16,7 +17,7 @@
</view>
<!-- 数据统计 -->
<view class="stats-section">
<view class="stats-section" @click="handleStatsClick">
<view class="stats-item">
<text class="number">0</text>
<text class="label">团队人员</text>
@ -33,7 +34,7 @@
</view>
<!-- 同步提示 -->
<view class="sync-tip">
<view class="sync-tip" @click="handleSyncClick">
<view class="dot"></view>
<text class="text">您有6个人员15条信息未同步到设备</text>
<image class="arrow" src="/static/images/icon_black_right.png" mode="aspectFit" />
@ -101,6 +102,18 @@
desc: '广告、欢迎词发布到屏幕'
}
]
const handleStatsClick = () => {
uni.navigateTo({
url: '/pages/personnel-passage/traffic-record/traffic-record'
})
}
const handleSyncClick = () => {
uni.navigateTo({
url: '/pages/personnel-passage/AccessManage/permission-query'
})
}
</script>
<style scoped lang="scss">

View File

@ -0,0 +1,168 @@
<route lang="json5">
{
layout: 'default',
style: {
navigationStyle: 'custom'
}
}
</route>
<template>
<TopNavigation title="权限查询" />
<view class="inquiry-container">
<!-- 查询表单 -->
<view class="inquiry-form">
<!-- 查询方式 -->
<view class="form-item">
<text class="label">人员查询方式</text>
<view class="select" @click="handleSelectType">
<text>按姓名</text>
<image class="arrow" src="/static/images/icon_black_right.png" mode="aspectFit" />
</view>
</view>
<!-- 姓名输入 -->
<view class="form-item">
<text class="label">人员姓名</text>
<input class="input" type="text" placeholder="请输入" placeholder-class="placeholder" />
</view>
<!-- 门禁范围 -->
<view class="form-item">
<text class="label">门禁点范围</text>
<view class="select" @click="handleSelectDoor">
<text class="placeholder">全选</text>
<image class="arrow" src="/static/images/icon_black_right.png" mode="aspectFit" />
</view>
</view>
<!-- 下发状态 -->
<view class="form-item">
<text class="label">下发状态</text>
<view class="select" @click="handleSelectStatus">
<text class="placeholder">全选</text>
<image class="arrow" src="/static/images/icon_black_right.png" mode="aspectFit" />
</view>
</view>
</view>
<!-- 底部按钮 -->
<view class="bottom-buttons">
<button class="reset-btn">重置</button>
<button class="query-btn">查询</button>
</view>
</view>
</template>
<script setup lang="ts">
import TopNavigation from '@/components/TopNavigation/TopNavigation.vue'
const handleSelectType = () => {
//
}
const handleSelectDoor = () => {
//
}
const handleSelectStatus = () => {
//
}
</script>
<style lang="scss" scoped>
.inquiry-container {
min-height: 100vh;
padding: 0 16px;
background-color: #f5f6fa;
}
.inquiry-form {
margin-top: 12px;
.form-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px;
margin-bottom: 1px;
background-color: #fff;
&:first-child {
border-top-left-radius: 8px;
border-top-right-radius: 8px;
}
&:last-child {
margin-bottom: 0;
border-bottom-right-radius: 8px;
border-bottom-left-radius: 8px;
}
.label {
font-size: 14px;
color: #333;
}
.input {
flex: 1;
height: 20px;
padding: 0 12px;
font-size: 14px;
text-align: right;
}
.select {
display: flex;
gap: 4px;
align-items: center;
text {
font-size: 14px;
color: #333;
}
.placeholder {
color: #999;
}
.arrow {
width: 16px;
height: 16px;
}
}
}
}
.bottom-buttons {
position: fixed;
right: 0;
bottom: 0;
left: 0;
display: flex;
gap: 12px;
padding: 16px;
background-color: #fff;
button {
flex: 1;
height: 44px;
font-size: 16px;
border-radius: 22px;
&::after {
border: none;
}
}
.reset-btn {
color: #666;
background-color: #f5f6fa;
}
.query-btn {
color: #fff;
background-color: #2b5cff;
}
}
</style>

View File

@ -0,0 +1,180 @@
<route lang="json5">
{
layout: 'default',
style: {
navigationStyle: 'custom'
}
}
</route>
<template>
<TopNavigation title="通行权限检测" />
<view class="detection-container">
<!-- 检测说明 -->
<view class="detection-banner">
<image class="banner-img" src="/static/images/bg_no_device.webp" mode="aspectFit" />
<view class="detection-info">
<image class="info-icon" src="/static/images/icon_white_tip.png" mode="aspectFit" />
<view class="info-content">
<text class="info-title">可检测以下两类问题</text>
<text class="info-item">1. 人员无法通行</text>
<text class="info-item">2.通行记录识别为陌生人抓拍图不是该人员</text>
</view>
</view>
</view>
<!-- 检测表单 -->
<view class="detection-form">
<!-- 姓名输入 -->
<view class="form-item">
<text class="label">姓名</text>
<input
class="input"
type="text"
placeholder="请输入完整姓名"
placeholder-class="placeholder"
/>
</view>
<!-- 门禁选择 -->
<view class="form-item">
<text class="label">门禁</text>
<view class="select" @click="handleSelectDoor">
<text class="placeholder">请选择</text>
<image class="arrow" src="/static/images/icon_black_right.png" mode="aspectFit" />
</view>
</view>
</view>
<!-- 开始检测按钮 -->
<button class="start-btn" @click="handleStartDetection">开始检测</button>
</view>
</template>
<script setup lang="ts">
import TopNavigation from '@/components/TopNavigation/TopNavigation.vue'
const handleSelectDoor = () => {
//
}
const handleStartDetection = () => {
//
}
</script>
<style lang="scss" scoped>
.detection-container {
min-height: 100vh;
padding: 0 16px;
background-color: #f5f6fa;
}
.detection-banner {
display: flex;
flex-direction: column;
align-items: center;
padding: 16px;
margin-top: 16px;
background-color: #fff;
border-radius: 8px;
.banner-img {
width: 100%;
aspect-ratio: 1/1;
object-fit: contain;
}
.detection-info {
display: flex;
// width: 100%;
padding: 10px;
background-color: #f8f9fc;
border-radius: 8px;
.info-icon {
width: 16px;
height: 16px;
margin-top: 2px;
margin-right: 8px;
}
.info-content {
display: flex;
flex: 1;
flex-direction: column;
gap: 4px;
.info-title {
font-size: 14px;
color: #333;
}
.info-item {
font-size: 14px;
color: #666;
}
}
}
}
.detection-form {
margin-top: 12px;
.form-item {
display: flex;
align-items: center;
padding: 10px 16px;
margin-bottom: 12px;
background-color: #fff;
border-radius: 8px;
.label {
min-width: 60px;
font-size: 14px;
color: #333;
}
.input {
flex: 1;
height: 40px;
font-size: 14px;
text-align: right;
}
.select {
display: flex;
flex: 1;
align-items: center;
justify-content: space-between;
height: 40px;
.placeholder {
margin-right: 8px;
margin-left: auto;
font-size: 14px;
color: #999;
}
.arrow {
width: 16px;
height: 16px;
}
}
}
}
.start-btn {
width: 100%;
height: 44px;
margin-top: 24px;
font-size: 16px;
color: #fff;
background: #2b5cff;
border-radius: 22px;
&::after {
border: none;
}
}
</style>

View File

@ -0,0 +1,303 @@
<route lang="json5">
{
layout: 'default',
style: {
navigationStyle: 'custom'
}
}
</route>
<template>
<TopNavigation title="添加权限组" />
<scroll-view
class="group-container"
:scroll-y="true"
:style="{ height: 'calc(100vh - var(--status-bar-height) - 44px)' }"
>
<!-- 配置名称 -->
<view class="form-item input-item">
<view class="form-header">
<view class="form-label">
<text class="required">*</text>
<text>配置名称</text>
</view>
</view>
<input
class="input"
type="text"
v-model="name"
@input="nameLength = name.length"
placeholder="必填"
placeholder-class="placeholder"
maxlength="20"
/>
<text class="count">{{ nameLength }}/20</text>
</view>
<!-- 配置描述 -->
<view class="form-item input-item">
<view class="form-header">
<text class="form-label">配置描述</text>
</view>
<textarea
class="textarea"
v-model="desc"
@input="descLength = desc.length"
placeholder="描述权限配置的范围例如1幢1单元门口机出入口"
placeholder-class="placeholder"
maxlength="50"
/>
<text class="count">{{ descLength }}/50</text>
</view>
<!-- 人员配置方式 -->
<view class="form-item select-item">
<view class="form-label">
<text class="required">*</text>
<text>人员配置方式</text>
</view>
<view class="form-value">
<text>按人员/组织</text>
<image class="arrow" src="/static/images/icon_black_right.png" mode="aspectFit" />
</view>
</view>
<!-- 人员范围 -->
<view class="form-item select-item">
<view class="form-label">
<text class="required">*</text>
<text>人员范围</text>
</view>
<view class="form-value">
<text class="placeholder">必选</text>
<image class="arrow" src="/static/images/icon_black_right.png" mode="aspectFit" />
</view>
</view>
<!-- 门禁范围 -->
<view class="form-item select-item">
<view class="form-label">
<text class="required">*</text>
<text>门禁范围</text>
</view>
<view class="form-value">
<text class="placeholder">必选</text>
<image class="arrow" src="/static/images/icon_black_right.png" mode="aspectFit" />
</view>
</view>
<!-- 门禁通行时间 -->
<view class="form-item select-item">
<view class="form-label">
<text class="required">*</text>
<text>门禁通行时间</text>
</view>
<view class="form-value">
<text>全天时段有效</text>
<image class="arrow" src="/static/images/icon_black_right.png" mode="aspectFit" />
</view>
</view>
<!-- 提示说明 -->
<view class="tips">
<view class="tips-header">
<image class="icon" src="/static/images/icon_gray_tip.png" mode="aspectFit" />
<text class="title">提示</text>
</view>
<view class="tips-content">
<view class="tips-list">
<view class="tip-item">
<text>1.</text>
<text>
通行时间仅型号为DS-K1TD1或HST-AU开头的门禁设备有效其余型号仅支持全天时段有效
</text>
</view>
<view class="tip-item">
<text>2.</text>
<text>
通过角色配置[一键开门][密码开门]菜单权限可远程/蓝牙/密码打开此配置范围内的门禁
</text>
</view>
<view class="tip-item">
<text>3.</text>
<text>一键开门不受通行时间门禁状态的限制可全天开门</text>
</view>
</view>
</view>
</view>
<!-- 底部占位防止保存按钮遮挡内容 -->
<view style="height: 76px"></view>
</scroll-view>
<!-- 底部按钮 -->
<button class="save-btn">保存</button>
</template>
<script setup>
import TopNavigation from '@/components/TopNavigation/TopNavigation.vue'
const nameLength = ref(0)
const descLength = ref(0)
</script>
<style lang="scss" scoped>
.group-container {
box-sizing: border-box;
padding: 12px 16px;
background-color: #f5f6fa;
}
.form-item {
position: relative;
padding: 16px;
margin-bottom: 12px;
background-color: #fff;
border-radius: 8px;
}
.input-item {
.form-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 8px;
}
.form-label {
display: flex;
align-items: center;
font-size: 14px;
color: #333;
.required {
margin-right: 4px;
color: #ff4d4f;
}
.placeholder {
margin-left: 4px;
color: #999;
}
}
.input,
.textarea {
display: block; //
width: auto; //
padding: 8px;
// margin-right: 10px;
font-size: 14px;
color: #333;
background-color: #f8f9fc;
border-radius: 4px;
}
.textarea {
height: 80px;
}
.count {
position: absolute;
right: 26px;
bottom: 26px;
font-size: 12px;
color: #999;
}
}
.select-item {
display: flex;
align-items: center;
justify-content: space-between;
.form-label {
display: flex;
align-items: center;
font-size: 14px;
color: #333;
.required {
margin-right: 4px;
color: #ff4d4f;
}
}
.form-value {
display: flex;
gap: 4px;
align-items: center;
font-size: 14px;
color: #999;
.arrow {
width: 16px;
height: 16px;
}
}
}
.tips {
padding: 16px;
margin-top: 12px;
background-color: #fff;
border-radius: 8px;
.tips-header {
display: flex;
align-items: center;
margin-bottom: 8px;
.icon {
width: 16px;
height: 16px;
margin-right: 8px;
}
.title {
font-size: 16px;
font-weight: 500;
color: #333;
}
}
.tips-content {
.tips-list {
padding-left: 4px;
clear: both;
.tip-item {
display: flex;
gap: 4px;
margin-bottom: 8px;
font-size: 14px;
line-height: 1.4;
color: #666;
&:last-child {
margin-bottom: 0;
}
text:last-child {
flex: 1;
}
}
}
}
}
.save-btn {
position: fixed;
right: 16px;
bottom: 16px;
left: 16px;
height: 44px;
font-size: 16px;
color: #fff;
background-color: #2b5cff;
border-radius: 8px;
&::after {
border: none;
}
}
</style>

View File

@ -0,0 +1,278 @@
<route lang="json5">
{
layout: 'default',
style: {
navigationStyle: 'custom'
}
}
</route>
<template>
<TopNavigation title="下发状态" />
<view class="status-container">
<!-- 人员信息 -->
<view class="person-info">
<text class="name">HikMall_30013234</text>
<text class="company">19104656的互联</text>
<!-- 门禁信息 -->
<view class="door-info">
<text class="label">门禁</text>
<text class="value">DS-K(L40959329)</text>
</view>
</view>
<!-- 下发状态列表 -->
<view class="status-list">
<!-- 人员信息 -->
<view class="status-item">
<checkbox class="checkbox" :checked="false" />
<view class="item-content">
<text class="title">人员信息</text>
</view>
<text class="status success">已下发</text>
</view>
<!-- 人脸 -->
<view class="status-item">
<checkbox class="checkbox" :checked="false" />
<view class="item-content">
<text class="title">人脸</text>
<image class="icon" src="/static/images/icon_face.png" mode="aspectFit" />
</view>
<text class="status success">已下发</text>
</view>
<!-- 指纹 -->
<view class="status-item">
<checkbox class="checkbox" :checked="false" />
<view class="item-content">
<text class="title">指纹1</text>
<image class="icon" src="/static/images/icon_fingerprint.png" mode="aspectFit" />
<text class="error-msg">设备离线请稍后重试</text>
</view>
<text class="status error">下发失败</text>
</view>
<!-- 密码 -->
<view class="status-item">
<checkbox class="checkbox" :checked="false" />
<view class="item-content">
<text class="title">密码</text>
<image class="icon" src="/static/images/icon_password.png" mode="aspectFit" />
<text class="password">7****9</text>
</view>
<text class="status success">已下发</text>
</view>
</view>
<!-- 状态说明 -->
<view class="status-desc">
<view class="desc-header">
<image class="info-icon" src="/static/images/icon_info.png" mode="aspectFit" />
<text class="title">状态说明</text>
</view>
<view class="desc-list">
<text class="desc-item">1下发中已配置权限正在同步到门禁点</text>
<text class="desc-item">2删除中已删除权限正在同步到门禁点</text>
<text class="desc-item">3待下发已配置权限未同步到门禁点不可通行</text>
<text class="desc-item">4已下发已配置权限并已同步到门禁点可通行</text>
<text class="desc-item">5下发失败已配置权限同步到门禁点失败不可通行</text>
<text class="desc-item">6待重新下发更新信息待将信息同步到门禁点可通行</text>
<text class="desc-item">7待删除已删除权限未同步到门禁点可通行</text>
</view>
</view>
<!-- 底部按钮 -->
<view class="bottom-buttons">
<button class="record-btn" @click="handleRecordBtnClick">查看记录</button>
<button class="publish-btn">立即下发</button>
</view>
</view>
</template>
<script setup lang="ts">
import TopNavigation from '@/components/TopNavigation/TopNavigation.vue'
const handleRecordBtnClick = () => {
uni.navigateTo({
url: '/pages/personnel-passage/AccessManage/viewing-delivery-records'
})
}
</script>
<style lang="scss" scoped>
.status-container {
min-height: 100vh;
padding: 12px 16px;
background-color: #f5f6fa;
}
.person-info {
padding: 16px;
margin-bottom: 10px;
background-color: #fff;
border-radius: 8px;
.name {
display: block;
margin-bottom: 4px;
font-size: 16px;
font-weight: 500;
color: #333;
}
.company {
font-size: 14px;
color: #666;
}
}
.door-info {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px;
margin-top: 12px;
background-color: #f8f9fc;
border-radius: 8px;
.label {
font-size: 14px;
color: #666;
}
.value {
font-size: 14px;
color: #333;
}
}
.status-list {
margin-bottom: 12px;
background-color: #fff;
border-radius: 8px;
.status-item {
display: flex;
align-items: center;
padding: 16px;
border-bottom: 1px solid #f5f6fa;
&:last-child {
border-bottom: none;
}
.checkbox {
margin-right: 8px;
transform: scale(0.8);
}
.item-content {
display: flex;
flex: 1;
gap: 4px;
align-items: center;
.title {
font-size: 14px;
color: #333;
}
.icon {
width: 16px;
height: 16px;
}
.error-msg {
font-size: 12px;
color: #999;
}
.password {
font-size: 14px;
color: #666;
}
}
.status {
font-size: 14px;
&.success {
color: #333;
}
&.error {
color: #ff4d4f;
}
}
}
}
.status-desc {
padding: 16px;
background-color: #fff;
border-radius: 8px;
.desc-header {
display: flex;
gap: 4px;
align-items: center;
margin-bottom: 12px;
.info-icon {
width: 16px;
height: 16px;
}
title {
font-size: 14px;
color: #333;
}
}
.desc-list {
display: flex;
flex-direction: column;
gap: 8px;
.desc-item {
font-size: 14px;
line-height: 1.4;
color: #666;
}
}
}
.bottom-buttons {
position: fixed;
right: 0;
bottom: 0;
left: 0;
display: flex;
gap: 12px;
padding: 12px;
background-color: #fff;
button {
flex: 1;
height: 44px;
font-size: 16px;
border-radius: 8px;
&::after {
border: none;
}
}
.record-btn {
color: #666;
background-color: #f5f6fa;
}
.publish-btn {
color: #fff;
background-color: #2b5cff;
}
}
</style>

View File

@ -0,0 +1,364 @@
<route lang="json5">
{
layout: 'default',
style: {
navigationStyle: 'custom'
}
}
</route>
<template>
<TopNavigation title="权限查询" />
<scroll-view
class="query-container"
:scroll-y="true"
:style="{ height: 'calc(100vh - var(--status-bar-height) - 44px)' }"
>
<!-- 搜索提示卡片 -->
<view class="search-tip">
<text>人员无法通行?</text>
<text class="check-btn" @click="handleCheckClick">去检测</text>
</view>
<!-- 权限查询入口 -->
<view class="query-entry" @click="handleQueryClick">
<text class="entry-title">通行权限查询</text>
<image class="arrow" src="/static/images/icon_black_right.png" mode="aspectFit" />
</view>
<!-- 人员列表 -->
<view class="person-list">
<view class="person-item" v-for="person in personList" :key="person.id">
<view class="person-info">
<text class="person-name">{{ person.name }}</text>
<text class="person-id">{{ person.id }}</text>
</view>
<text class="company">{{ person.company }}</text>
<view class="validity">
<text class="label">有效时间</text>
<text class="value">{{ person.validityPeriod }}</text>
</view>
<view class="access-info">
<view class="door-info">
<image class="icon" src="/static/images/icon_door.png" mode="aspectFit" />
<text>门禁点{{ person.doorPoint }}</text>
</view>
<view class="time-info">
<image class="icon" src="/static/images/icon_time.png" mode="aspectFit" />
<text>通行时间</text>
<view class="time-tag">{{ person.accessTime }}</view>
</view>
</view>
<view class="auth-types" @click="() => handlePersonItemClick(person)">
<view class="type-item" v-for="type in person.authTypes" :key="type.name">
<image class="type-icon" :src="type.icon" mode="aspectFit" />
<text>{{ type.name }}</text>
</view>
</view>
<image class="item-arrow" src="/static/images/icon_black_right.png" mode="aspectFit" />
</view>
</view>
<!-- 底部按钮 -->
<view class="bottom-buttons">
<button class="btn add-btn" @click="handleAddBtnClick">添加权限组</button>
<button class="btn publish-btn" @click="handlePublishBtnClick">下发权限</button>
<button class="btn record-btn" @click="handleRecordBtnClick">下发记录</button>
</view>
</scroll-view>
</template>
<script setup lang="ts">
import TopNavigation from '@/components/TopNavigation/TopNavigation.vue'
interface AuthType {
name: string
icon: string
}
interface Person {
id: string
name: string
company: string
validityPeriod: string
doorPoint: string
accessTime: string
authTypes: AuthType[]
}
const personList = ref<Person[]>([
{
id: 'CY01068968',
name: 'HikMall_30013234',
company: '19104656的互联',
validityPeriod: '永久有效',
doorPoint: 'DS-K(L40959329)',
accessTime: '全天时段有效',
authTypes: [
{ name: '人员', icon: '/static/images/icon_person.png' },
{ name: '人脸', icon: '/static/images/icon_face.png' },
{ name: '指纹', icon: '/static/images/icon_fingerprint.png' },
{ name: '密码', icon: '/static/images/icon_password.png' }
]
},
{
id: 'CY01069559',
name: '4',
company: '19104656的互联/test',
validityPeriod: '2000-01-01至2037-12-31',
doorPoint: 'DS-K(L40959329)',
accessTime: '全天时段有效',
authTypes: [
{ name: '人员', icon: '/static/images/icon_person.png' },
{ name: '指纹', icon: '/static/images/icon_fingerprint.png' }
]
},
{
id: 'CY01069559',
name: '4',
company: '19104656的互联/test',
validityPeriod: '2000-01-01至2037-12-31',
doorPoint: 'DS-K(L40959329)',
accessTime: '全天时段有效',
authTypes: [
{ name: '人员', icon: '/static/images/icon_person.png' },
{ name: '指纹', icon: '/static/images/icon_fingerprint.png' }
]
}
])
const handleCheckClick = () => {
uni.navigateTo({
url: '/pages/personnel-passage/AccessManage/access-right-detection'
})
}
const handleQueryClick = () => {
uni.navigateTo({
url: '/pages/personnel-passage/AccessManage/access-permission-inquiry'
})
}
const handlePersonItemClick = (person: Person) => {
uni.navigateTo({
url: `/pages/personnel-passage/AccessManage/delivery-status?id=${person.id}`
})
}
//
const handleAddBtnClick = () => {
uni.navigateTo({
url: '/pages/personnel-passage/AccessManage/add-permission-group'
})
}
//
const handlePublishBtnClick = () => {
uni.navigateTo({
url: '/pages/personnel-passage/AccessManage/send-permission'
})
}
//
const handleRecordBtnClick = () => {
uni.navigateTo({
url: '/pages/personnel-passage/AccessManage/release-record'
})
}
</script>
<style lang="scss" scoped>
.query-container {
box-sizing: border-box;
padding: 0 16px;
padding-bottom: 76px;
background-color: #f5f6fa;
}
.search-tip {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 16px;
margin-top: 12px;
background-color: #fff7f2;
border-radius: 8px;
.check-btn {
padding: 2px 8px;
font-size: 14px;
color: #333;
background-color: #fff;
border-radius: 4px;
}
}
.query-entry {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px;
margin-top: 12px;
background-color: #fff;
border-radius: 8px;
.entry-title {
font-size: 16px;
color: #333;
}
.arrow {
width: 16px;
height: 16px;
}
}
.person-list {
margin-top: 12px;
.person-item {
position: relative;
padding: 16px;
margin-bottom: 12px;
background-color: #fff;
border-radius: 8px;
.person-info {
display: flex;
gap: 8px;
align-items: center;
margin-bottom: 4px;
.person-name {
font-size: 16px;
font-weight: 500;
color: #333;
}
.person-id {
font-size: 14px;
color: #999;
}
}
.company {
display: block;
margin-bottom: 8px;
font-size: 14px;
color: #666;
}
.validity {
margin-bottom: 12px;
font-size: 14px;
.label {
color: #666;
}
.value {
color: #333;
}
}
.access-info {
padding: 12px;
margin-bottom: 12px;
background-color: #f8f9fc;
border-radius: 8px;
.door-info,
.time-info {
display: flex;
align-items: center;
margin-bottom: 8px;
font-size: 14px;
color: #666;
&:last-child {
margin-bottom: 0;
}
.icon {
width: 16px;
height: 16px;
margin-right: 4px;
}
.time-tag {
padding: 2px 8px;
margin-left: 4px;
font-size: 12px;
color: #2b5cff;
background-color: #eef4ff;
border-radius: 4px;
}
}
}
.auth-types {
display: flex;
gap: 16px;
.type-item {
display: flex;
gap: 4px;
align-items: center;
font-size: 14px;
color: #666;
.type-icon {
width: 16px;
height: 16px;
}
}
}
.item-arrow {
position: absolute;
right: 16px;
bottom: 16px;
width: 16px;
height: 16px;
}
}
}
.bottom-buttons {
position: fixed;
right: 0;
bottom: 0;
left: 0;
display: flex;
padding-top: 12px;
background-color: #fff;
border-top: 1px solid #eee;
.btn {
flex: 1;
height: 44px;
font-size: 14px;
// &::after {
// border: 1px solid #eee;
// border-radius: 0;
// }
}
.add-btn {
color: #2b5cff;
background-color: #fff;
}
.publish-btn {
color: #2b5cff;
background-color: #fff;
}
.record-btn {
color: #2b5cff;
background-color: #fff;
}
}
</style>

View File

@ -0,0 +1,186 @@
<route lang="json5">
{
layout: 'default',
style: {
navigationStyle: 'custom'
}
}
</route>
<template>
<TopNavigation title="下发记录" />
<scroll-view
class="record-container"
:scroll-y="true"
:style="{ height: 'calc(100vh - var(--status-bar-height) - 44px)' }"
>
<view class="record-list">
<view class="record-item" v-for="record in records" :key="record.time">
<view class="record-header">
<view class="title-wrap">
<text class="title">{{ record.title }}</text>
<text class="time">{{ record.time }}</text>
</view>
<view class="status-wrap" @click="handleRecordClick(record)">
<text class="status">{{ record.status }}</text>
<image class="arrow" src="/static/images/icon_black_right.png" mode="aspectFit" />
</view>
</view>
<view class="record-stats">
<view class="stat-item">
<text class="value">{{ record.success }}</text>
<text class="label">成功</text>
</view>
<view class="stat-item">
<text class="value">{{ record.fail }}</text>
<text class="label">失败</text>
</view>
<view class="stat-item">
<text class="value">{{ record.total }}</text>
<text class="label">总数</text>
</view>
</view>
</view>
</view>
</scroll-view>
</template>
<script setup lang="ts">
import TopNavigation from '@/components/TopNavigation/TopNavigation.vue'
interface Record {
title: string
time: string
status: string
success: number
fail: number
total: number
}
const records = ref<Record[]>([
{
title: '系统自动操作下发',
time: '2025-01-16 14:01:41',
status: '已完成',
success: 0,
fail: 4,
total: 4
},
{
title: '杨操作下发',
time: '2025-01-16 09:47:06',
status: '已完成',
success: 0,
fail: 4,
total: 4
},
{
title: '系统自动操作下发',
time: '2025-01-16 06:01:13',
status: '已完成',
success: 0,
fail: 8,
total: 8
},
{
title: '系统自动操作下发',
time: '2025-01-15 06:01:12',
status: '已完成',
success: 0,
fail: 8,
total: 8
},
{
title: '系统自动操作下发',
time: '2025-01-14 15:42:37',
status: '已完成',
success: 0,
fail: 4,
total: 4
}
])
const handleRecordClick = (record: Record) => {
//
}
</script>
<style lang="scss" scoped>
.record-container {
padding: 12px 16px;
background-color: #f5f6fa;
}
.record-list {
.record-item {
padding: 16px;
margin-right: 32px;
margin-bottom: 12px;
background-color: #fff;
border-radius: 8px;
.record-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 16px;
.title-wrap {
.title {
display: block;
margin-bottom: 4px;
font-size: 16px;
color: #333;
}
.time {
font-size: 14px;
color: #999;
}
}
.status-wrap {
display: flex;
gap: 4px;
align-items: center;
.status {
font-size: 14px;
color: #2b5cff;
}
.arrow {
width: 16px;
height: 16px;
}
}
}
.record-stats {
display: flex;
justify-content: space-around;
padding: 12px;
background-color: #f8f9fc;
border-radius: 8px;
.stat-item {
display: flex;
flex-direction: column;
gap: 4px;
align-items: center;
.value {
font-size: 20px;
font-weight: 500;
color: #333;
}
.label {
font-size: 14px;
color: #666;
}
}
}
}
}
</style>

View File

@ -0,0 +1,214 @@
<route lang="json5">
{
layout: 'default',
style: {
navigationStyle: 'custom'
}
}
</route>
<template>
<TopNavigation title="下发权限">
<template #right>
<view class="timing-btn" @click="handleTimingClick">
<image class="icon" src="/static/images/icon_timing.png" mode="aspectFit" />
<text>定时下发</text>
</view>
</template>
</TopNavigation>
<view class="send-container">
<!-- 下发选项 -->
<view class="form-item">
<text class="label">下发方式</text>
<view class="select">
<text>普通下发</text>
<image class="arrow" src="/static/images/icon_black_right.png" mode="aspectFit" />
</view>
</view>
<view class="form-item">
<text class="label">门禁范围</text>
<view class="select">
<text>全选</text>
<image class="arrow" src="/static/images/icon_black_right.png" mode="aspectFit" />
</view>
</view>
<!-- 提示说明 -->
<view class="tips">
<view class="tips-header">
<image class="icon" src="/static/images/icon_gray_tip.png" mode="aspectFit" />
<text class="title">提示</text>
</view>
<view class="tips-content">
<view class="tips-list">
<view class="tip-item">
<text>1.</text>
<text>仅可选择已配置过通行权限的门禁进行下发</text>
</view>
<view class="tip-item">
<text>2.</text>
<text>下发信息过多会影响人员通行体验建议避开人流量较大时间错峰下发</text>
</view>
<view class="tip-item">
<text>3.</text>
<text>
设备数据与平台数据不一致时可选择[全量下发]将删除设备本地数据后重新下发平台数据
</text>
</view>
<view class="tip-item">
<text>4.</text>
<text>
通行时间仅型号为DS-K1TD1或HST-AU开头的门禁设备有效其余型号仅支持全天时段有效
</text>
</view>
</view>
<view class="extra-tips">
<text>
新添加或修改权限组需点击[立即下发]将配置的通行凭证下发到对应门禁点成功后可生效通行权限
</text>
<text>
若权限组已按照组织/分组/楼幢进行人员配置则后续在对应的组织/分组/楼幢中加入人员将自动下发新增的通行凭证无需手动下发
</text>
</view>
</view>
</view>
</view>
<!-- 底部按钮 -->
<button class="send-btn">立即下发</button>
</template>
<script setup lang="ts">
import TopNavigation from '@/components/TopNavigation/TopNavigation.vue'
const handleTimingClick = () => {
//
}
</script>
<style lang="scss" scoped>
.send-container {
min-height: 100vh;
padding: 12px 16px;
background-color: #f5f6fa;
}
.timing-btn {
display: flex;
gap: 4px;
align-items: center;
padding: 4px 8px;
font-size: 14px;
color: #333;
.icon {
width: 16px;
height: 16px;
}
}
.form-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px;
margin-bottom: 12px;
background-color: #fff;
border-radius: 8px;
.label {
font-size: 14px;
color: #333;
}
.select {
display: flex;
gap: 4px;
align-items: center;
font-size: 14px;
color: #333;
.arrow {
width: 16px;
height: 16px;
}
}
}
.tips {
padding: 16px;
background-color: #fff;
border-radius: 8px;
.tips-header {
display: flex;
align-items: center;
margin-bottom: 8px;
.icon {
width: 16px;
height: 16px;
margin-right: 8px;
}
.title {
font-size: 16px;
font-weight: 500;
color: #333;
}
}
.tips-content {
.tips-list {
padding-left: 4px;
.tip-item {
display: flex;
gap: 4px;
margin-bottom: 8px;
font-size: 14px;
line-height: 1.4;
color: #666;
&:last-child {
margin-bottom: 12px;
}
text:last-child {
flex: 1;
}
}
}
.extra-tips {
display: flex;
flex-direction: column;
gap: 8px;
padding: 12px;
font-size: 14px;
line-height: 1.4;
color: #666;
background-color: #f5f6fa;
border-radius: 8px;
}
}
}
.send-btn {
position: fixed;
right: 16px;
bottom: 16px;
left: 16px;
height: 44px;
font-size: 16px;
color: #fff;
background-color: #2b5cff;
border-radius: 8px;
&::after {
border: none;
}
}
</style>

View File

@ -0,0 +1,220 @@
<route lang="json5">
{
layout: 'default',
style: {
navigationStyle: 'custom',
disableScroll: false
}
}
</route>
<template>
<TopNavigation title="查看记录" />
<view class="records-container">
<!-- 人员信息 -->
<view class="person-info">
<text class="name">HikMall_30013234</text>
<text class="company">19104656的互联</text>
<!-- 门禁信息 -->
<view class="door-info">
<text class="label">门禁</text>
<text class="value">DS-K(L40959329)</text>
</view>
</view>
<!-- 记录列表 -->
<view class="records-list">
<!-- 记录项 -->
<view class="record-item" v-for="(item, index) in records" :key="index">
<text class="time">{{ item.time }}</text>
<view class="record-content">
<view class="content-header">
<text class="title">{{ item.title }}</text>
<text :class="['status', item.status === '下发失败' ? 'error' : 'success']">
{{ item.status }}
</text>
</view>
<view class="operation-info">
<image class="icon" src="/static/images/icon_operation.png" mode="aspectFit" />
<text class="label">权限操作</text>
<text class="value">{{ item.operation }}</text>
</view>
<view v-if="item.error" class="error-info">
<image class="icon" src="/static/images/icon_info.png" mode="aspectFit" />
<text class="label">失败原因</text>
<text class="value">{{ item.error }}</text>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import TopNavigation from '@/components/TopNavigation/TopNavigation.vue'
const records = [
{
time: '2025-01-16 06:01:13',
title: '指纹1',
status: '下发失败',
operation: '下发权限',
error: '设备离线,请稍后重试'
},
{
time: '2025-01-15 06:01:12',
title: '指纹1',
status: '下发失败',
operation: '下发权限',
error: '设备离线,请稍后重试'
},
{
time: '2025-01-14 06:01:17',
title: '指纹1',
status: '下发失败',
operation: '下发权限',
error: '设备离线,请稍后重试'
},
{
time: '2025-01-13 11:22:18',
title: '人员信息',
status: '下发成功',
operation: '下发权限'
},
{
time: '2025-01-15 06:01:12',
title: '指纹1',
status: '下发失败',
operation: '下发权限',
error: '设备离线,请稍后重试'
},
{
time: '2025-01-15 06:01:12',
title: '指纹1',
status: '下发失败',
operation: '下发权限',
error: '设备离线,请稍后重试'
}
]
</script>
<style lang="scss" scoped>
.records-container {
min-height: 100vh;
padding: 12px 16px;
background-color: #f5f6fa;
}
.person-info {
padding: 16px;
margin-bottom: 10px;
background-color: #fff;
border-radius: 8px;
.name {
display: block;
margin-bottom: 4px;
font-size: 16px;
font-weight: 500;
color: #333;
}
.company {
font-size: 14px;
color: #666;
}
}
.door-info {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px;
margin-top: 12px;
background-color: #f8f9fc;
border-radius: 8px;
.label {
font-size: 14px;
color: #666;
}
.value {
font-size: 14px;
color: #333;
}
}
.records-list {
.record-item {
margin-bottom: 12px;
.time {
display: block;
margin-bottom: 8px;
font-size: 14px;
color: #666;
}
.record-content {
padding: 16px;
background-color: #fff;
border-radius: 8px;
.content-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 12px;
.title {
font-size: 14px;
color: #333;
}
.status {
font-size: 14px;
&.success {
color: #52c41a;
}
&.error {
color: #ff4d4f;
}
}
}
.operation-info,
.error-info {
display: flex;
gap: 4px;
align-items: center;
margin-bottom: 8px;
&:last-child {
margin-bottom: 0;
}
.icon {
width: 16px;
height: 16px;
}
.label {
font-size: 14px;
color: #666;
}
.value {
font-size: 14px;
color: #333;
}
}
}
}
}
</style>

View File

@ -14,7 +14,7 @@
<view class="password-card">
<view class="help-icon">
<text>通行帮助</text>
<image src="/static/images/personnelPassage/icon_tip.png" mode="aspectFit" />
<image src="/static/images/icon_white_tip.png" mode="aspectFit" />
</view>
<view class="password-display">

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -35,6 +35,14 @@ interface NavigateToOptions {
"/pages/attendance/attendance-add-group/attendance-time" |
"/pages/attendance/attendance-add-group/outside-rules" |
"/pages/attendance/attendance-add-group/special-date-set" |
"/pages/personnel-passage/AccessManage/access-permission-inquiry" |
"/pages/personnel-passage/AccessManage/access-right-detection" |
"/pages/personnel-passage/AccessManage/add-permission-group" |
"/pages/personnel-passage/AccessManage/delivery-status" |
"/pages/personnel-passage/AccessManage/permission-query" |
"/pages/personnel-passage/AccessManage/release-record" |
"/pages/personnel-passage/AccessManage/send-permission" |
"/pages/personnel-passage/AccessManage/viewing-delivery-records" |
"/pages/personnel-passage/access-authority/access-authority" |
"/pages/personnel-passage/one-click-open-door/one-click-open-door" |
"/pages/personnel-passage/password-open-door/password-open-door" |