feat:家长老师上传人脸
This commit is contained in:
1 parent
e0932b9700
commit
80b88be0e1
14 files changed
+285
-262
No files matched your search
@@ -33,18 +33,18 @@ VITE_OAUTH2_SOCIAL_CLIENT='social:social'
|
||||
# 是否开启websocket 消息接受,
|
||||
VITE_WEBSOCKET_ENABLE = true
|
||||
# --------------------------- start 开发环境 start ----------------------------------
|
||||
VITE_APP_DEV_SERVICE = https://www.zianxin.com/api
|
||||
# VITE_APP_DEV_SERVICE = https://www.zianxin.com/api
|
||||
# VITE_APP_DEV_SERVICE = https://dzc-test-1.fy.takin.cc
|
||||
# VITE_APP_DEV_SERVICE = http://127.0.0.1:9999
|
||||
#VITE_APP_DEV_SERVICE = https://www.zianxin.com/api
|
||||
# VITE_APP_DEV_SERVICE = https://dzc-test-1.fy.takin.cc
|
||||
VITE_APP_DEV_SERVICE = https://dzc-test-1.fy.takin.cc
|
||||
#VITE_APP_DEV_SERVICE = http://127.0.0.1:9999
|
||||
|
||||
VITE_IMAGE_DEV_PATH = https://www.zianxin.com/api
|
||||
# VITE_IMAGE_DEV_PATH = https://www.zianxin.com/api
|
||||
# VITE_IMAGE_DEV_PATH = https://dzc-test-1.fy.takin.cc
|
||||
# VITE_IMAGE_DEV_PATH = http://127.0.0.1:9999
|
||||
#VITE_IMAGE_DEV_PATH = https://www.zianxin.com/api
|
||||
# VITE_IMAGE_DEV_PATH = https://dzc-test-1.fy.takin.cc
|
||||
VITE_IMAGE_DEV_PATH = https://dzc-test-1.fy.takin.cc
|
||||
# VITE_IMAGE_DEV_PATH = http://127.0.0.1:9999
|
||||
# ----------------------------- end 开发环境 end ------------------------------------
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"type" : "uniCloud"
|
||||
},
|
||||
{
|
||||
"customPlaygroundType" : "device",
|
||||
"customPlaygroundType" : "local",
|
||||
"openVueDevtools" : true,
|
||||
"playground" : "custom",
|
||||
"type" : "uni-app:app-ios"
|
||||
|
||||
+2
-2
@@ -2,8 +2,8 @@
|
||||
"name" : "资安信",
|
||||
"appid" : "__UNI__1BFE88F",
|
||||
"description" : "资安信后勤管理系统",
|
||||
"versionName" : "1.5.12",
|
||||
"versionCode" : 1512,
|
||||
"versionName" : "1.5.13",
|
||||
"versionCode" : 1513,
|
||||
"transformPx" : false,
|
||||
/* 5+App特有相关 */
|
||||
"app-plus" : {
|
||||
|
||||
+1
-8
@@ -357,13 +357,6 @@
|
||||
{
|
||||
"root": "pages/human-face",
|
||||
"pages": [
|
||||
{
|
||||
"path": "list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "guardian-ward-apply",
|
||||
"style": {
|
||||
@@ -668,4 +661,4 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
</view>
|
||||
<view class="ml-14rpx min-w-0 flex-1">
|
||||
<view class="sub-text-28 font-700 show-line">{{ wardName }}</view>
|
||||
<view class="second-text-21 mt-3rpx show-line">被监护人人脸信息</view>
|
||||
<view class="second-text-21 mt-3rpx show-line">{{ faceIdentityDescription }}</view>
|
||||
</view>
|
||||
<view class="status-tag shrink-0 ml-12rpx" :class="detailStatus.className">
|
||||
{{ isDetailMode ? detailStatus.label : "需审核" }}
|
||||
@@ -121,6 +121,8 @@ const spuUploadActionUrl = getFileUploadActionUrl()
|
||||
|
||||
const wardUserId = ref("")
|
||||
const wardName = ref("被监护人")
|
||||
/** 当前目标是否为登录用户本人,用于复用同一套上传、审核和撤回流程。 */
|
||||
const currentUser = ref(false)
|
||||
const uploadFiles = ref<any[]>([])
|
||||
const submitting = ref(false)
|
||||
const cancelling = ref(false)
|
||||
@@ -154,7 +156,13 @@ const faceGuides = [
|
||||
|
||||
const uploadHeader = computed(() => hooks.useDefaultHeader())
|
||||
const isDetailMode = computed(() => pageMode.value === "detail")
|
||||
const pageTitle = computed(() => (isDetailMode.value ? "人脸审核详情" : "上传被监护人人脸"))
|
||||
/** 本人与被监护人共用页面时,标题应准确反映当前办理对象。 */
|
||||
const pageTitle = computed(() => {
|
||||
if (isDetailMode.value) return "人脸审核详情"
|
||||
return currentUser.value ? "上传人脸" : "上传被监护人人脸"
|
||||
})
|
||||
/** 顶部身份说明根据目标用户切换,避免本人页面仍显示被监护人文案。 */
|
||||
const faceIdentityDescription = computed(() => (currentUser.value ? "本人人脸信息" : "被监护人人脸信息"))
|
||||
const detailFaceUrl = computed(() => filePath(faceDetail.value?.faceImg))
|
||||
const canCancelFace = computed(() => String(faceDetail.value?.processStatus || "") === taskCheckStatusCodeEnum.PADDING)
|
||||
const canReapplyFace = computed(() => String(faceDetail.value?.processStatus || "") === taskCheckStatusCodeEnum.REJECT)
|
||||
@@ -194,6 +202,7 @@ const toRecords = () => {
|
||||
router.jump("/pages/human-face/guardian-ward-records", {
|
||||
wardUserId: wardUserId.value,
|
||||
wardName: encodeURIComponent(wardName.value),
|
||||
currentUser: currentUser.value ? "1" : "0",
|
||||
})
|
||||
}
|
||||
|
||||
@@ -230,7 +239,7 @@ const confirmSingleFace = () => singleFaceConfirmRef.value?.open() ?? Promise.re
|
||||
const formatTime = (value?: string) => (value ? dayjs(value).format("YYYY-MM-DD HH:mm") : "—")
|
||||
|
||||
/**
|
||||
* 查询被监护人的人脸审核详情,并更新详情模式页面。
|
||||
* 查询本人或被监护人的人脸审核详情,并更新详情模式页面。
|
||||
*/
|
||||
const loadFaceDetail = async () => {
|
||||
if (!faceId.value) return
|
||||
@@ -240,7 +249,7 @@ const loadFaceDetail = async () => {
|
||||
wardUserId.value = String(faceDetail.value.userId || wardUserId.value)
|
||||
wardName.value = faceDetail.value.username || wardName.value
|
||||
} catch (error: any) {
|
||||
console.error("查询被监护人人脸审核详情失败", error)
|
||||
console.error(currentUser.value ? "查询本人人脸审核详情失败" : "查询被监护人人脸审核详情失败", error)
|
||||
uni.showToast({ title: error?.message || error?.msg || "查询审核详情失败", icon: "none" })
|
||||
}
|
||||
}
|
||||
@@ -302,7 +311,7 @@ const reapplyRejectedFace = () => {
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交被监护人的人脸图片并进入审核流程。
|
||||
* 提交本人或被监护人的人脸图片并进入审核流程。
|
||||
*/
|
||||
const submitFaces = async () => {
|
||||
if (submitting.value) return
|
||||
@@ -329,6 +338,7 @@ const submitFaces = async () => {
|
||||
{
|
||||
wardUserId: wardUserId.value,
|
||||
wardName: encodeURIComponent(wardName.value),
|
||||
currentUser: currentUser.value ? "1" : "0",
|
||||
},
|
||||
2,
|
||||
),
|
||||
@@ -346,13 +356,14 @@ onLoad((options?: Record<string, any>) => {
|
||||
pageMode.value = String(options?.mode || "apply")
|
||||
faceId.value = String(options?.id || "")
|
||||
wardUserId.value = String(options?.wardUserId || "")
|
||||
wardName.value = decodeURIComponent(String(options?.wardName || "被监护人"))
|
||||
currentUser.value = String(options?.currentUser || "") === "1"
|
||||
wardName.value = decodeURIComponent(String(options?.wardName || (currentUser.value ? "本人" : "被监护人")))
|
||||
if (isDetailMode.value && faceId.value) {
|
||||
loadFaceDetail()
|
||||
return
|
||||
}
|
||||
if (!wardUserId.value || (isDetailMode.value && !faceId.value)) {
|
||||
uni.showToast({ title: "缺少被监护人信息", icon: "none" })
|
||||
uni.showToast({ title: "缺少目标用户信息", icon: "none" })
|
||||
setTimeout(() => router.back(), 800)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
<z-paging ref="pageRef" v-model="records" height="100%" :fixed="false" :default-page-size="10"
|
||||
:safe-area-inset-bottom="true" :show-scrollbar="false" @query="queryRecords">
|
||||
<template #top>
|
||||
<select-guardian-ward v-model="wardUserId" :initial-name="wardName" @change="changeWard" />
|
||||
<select-guardian-ward v-if="!currentUser" v-model="wardUserId" :initial-name="wardName"
|
||||
@change="changeWard" />
|
||||
<view class="status-tabs card mt-20rpx overflow-hidden bg-white">
|
||||
<z-tabs :current="selectedTab" :list="statusOptions" @change="changeStatus" />
|
||||
</view>
|
||||
@@ -15,7 +16,7 @@
|
||||
<image class="face-image shrink-0" :src="filePath(record.faceImg)" mode="aspectFill" />
|
||||
<view class="ml-14rpx min-w-0 flex-1">
|
||||
<view class="flex items-center justify-between gap-12rpx">
|
||||
<view class="sub-text-25 show-line font-700">{{ record.username || "被监护人" }}</view>
|
||||
<view class="sub-text-25 show-line font-700">{{ record.username || identityName }}</view>
|
||||
<view class="status-tag shrink-0" :class="statusMeta(record.processStatus).className">
|
||||
{{ statusMeta(record.processStatus).label }}
|
||||
</view>
|
||||
@@ -41,14 +42,14 @@
|
||||
|
||||
<template #main-bottom>
|
||||
<view class="submit-bar bg-white">
|
||||
<tm-button block color="#0094ff" @click="openApply">为{{ wardName }}上传人脸</tm-button>
|
||||
<tm-button block color="#0094ff" @click="openApply">{{ uploadButtonText }}</tm-button>
|
||||
</view>
|
||||
</template>
|
||||
</crud-layout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue"
|
||||
import { computed, ref } from "vue"
|
||||
import { onLoad, onShow } from "@dcloudio/uni-app"
|
||||
import dayjs from "dayjs"
|
||||
|
||||
@@ -61,6 +62,8 @@ import zTabs from "@@/components/other/z-tabs/z-tabs.vue"
|
||||
|
||||
const wardUserId = ref("")
|
||||
const wardName = ref("被监护人")
|
||||
/** 当前审核记录是否固定查询登录用户本人,本人模式下不展示被监护人切换器。 */
|
||||
const currentUser = ref(false)
|
||||
const pageRef = ref<any>(null)
|
||||
const records = ref<any[]>([])
|
||||
const selectedTab = ref(0)
|
||||
@@ -71,6 +74,10 @@ const statusOptions = [
|
||||
{ name: taskCheckStatusEnum[taskCheckStatusCodeEnum.RESULT], value: taskCheckStatusCodeEnum.RESULT },
|
||||
{ name: taskCheckStatusEnum[taskCheckStatusCodeEnum.REJECT], value: taskCheckStatusCodeEnum.REJECT },
|
||||
]
|
||||
/** 当前审核记录所属人的身份兜底名称。 */
|
||||
const identityName = computed(() => (currentUser.value ? "本人" : "被监护人"))
|
||||
/** 底部上传按钮在本人模式下不使用代办语气。 */
|
||||
const uploadButtonText = computed(() => (currentUser.value ? "上传人脸" : `为${wardName.value}上传人脸`))
|
||||
|
||||
/**
|
||||
* 将审核状态转换为页面展示文案和颜色。
|
||||
@@ -99,7 +106,7 @@ const statusMeta = (status?: string) => {
|
||||
const formatTime = (value?: string) => (value ? dayjs(value).format("YYYY-MM-DD HH:mm") : "—")
|
||||
|
||||
/**
|
||||
* 分页查询当前选中被监护人的人脸审核记录。
|
||||
* 分页查询当前选中的本人或被监护人的人脸审核记录。
|
||||
*/
|
||||
const queryRecords = async (page: number, size: number) => {
|
||||
if (!wardUserId.value) {
|
||||
@@ -115,7 +122,7 @@ const queryRecords = async (page: number, size: number) => {
|
||||
})
|
||||
pageRef.value?.complete(response?.data?.records || [])
|
||||
} catch (error) {
|
||||
console.error("查询被监护人人脸审核记录失败", error)
|
||||
console.error(currentUser.value ? "查询本人人脸审核记录失败" : "查询被监护人人脸审核记录失败", error)
|
||||
pageRef.value?.complete(false)
|
||||
}
|
||||
}
|
||||
@@ -145,25 +152,28 @@ const openDetail = (record: any) => {
|
||||
mode: "detail",
|
||||
id: String(record.id),
|
||||
wardUserId: String(record.userId || ""),
|
||||
wardName: encodeURIComponent(String(record.username || "被监护人")),
|
||||
wardName: encodeURIComponent(String(record.username || identityName.value)),
|
||||
currentUser: currentUser.value ? "1" : "0",
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开当前被监护人的人脸上传页面。
|
||||
* 打开当前本人或被监护人的人脸上传页面。
|
||||
*/
|
||||
const openApply = () => {
|
||||
router.jump("/pages/human-face/guardian-ward-apply", {
|
||||
wardUserId: wardUserId.value,
|
||||
wardName: encodeURIComponent(wardName.value),
|
||||
currentUser: currentUser.value ? "1" : "0",
|
||||
})
|
||||
}
|
||||
|
||||
onLoad((options?: Record<string, any>) => {
|
||||
wardUserId.value = String(options?.wardUserId || "")
|
||||
wardName.value = decodeURIComponent(String(options?.wardName || "被监护人"))
|
||||
currentUser.value = String(options?.currentUser || "") === "1"
|
||||
wardName.value = decodeURIComponent(String(options?.wardName || (currentUser.value ? "本人" : "被监护人")))
|
||||
if (!wardUserId.value) {
|
||||
uni.showToast({ title: "缺少被监护人信息", icon: "none" })
|
||||
uni.showToast({ title: "缺少目标用户信息", icon: "none" })
|
||||
setTimeout(() => router.back(), 800)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive } from "vue"
|
||||
import { onShow } from "@dcloudio/uni-app"
|
||||
|
||||
import { hooks, router } from "@@/index"
|
||||
import { faceApi } from "@@/api"
|
||||
|
||||
import { filePath } from "@@/utils/file"
|
||||
|
||||
const state = reactive<any>({
|
||||
pa: {
|
||||
pagesize: 10,
|
||||
},
|
||||
})
|
||||
|
||||
const pageRef = ref<any>(null)
|
||||
const pageHook = hooks.usePage(pageRef, faceApi.page, null, "data.records")
|
||||
const query = (page: number, pagesize: number) => {
|
||||
pageHook.startPage(page, pagesize, false, state.pa)
|
||||
}
|
||||
|
||||
onShow(() => {
|
||||
if (pageRef.value) {
|
||||
pageRef.value.reload(false)
|
||||
}
|
||||
})
|
||||
|
||||
const doAdd = () => {
|
||||
router.jump("/pages/human-face/apply")
|
||||
}
|
||||
|
||||
const doEdit = (id: string) => {
|
||||
router.jump("/pages/human-face/apply", { type: "edit", id })
|
||||
}
|
||||
|
||||
const doDel = (id: string) => {
|
||||
faceApi.del({ id }).then(
|
||||
() => {
|
||||
uni.showToast({ title: "删除成功", icon: "none" })
|
||||
pageRef.value.reload(true)
|
||||
},
|
||||
() => {
|
||||
uni.showToast({ title: "删除失败", icon: "none" })
|
||||
},
|
||||
)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<crud-layout title="人脸管理" bg="/static/page/blue-bg.png">
|
||||
<z-paging ref="pageRef" height="100%" :fixed="false" v-model="pageHook.list" :default-page-size="10"
|
||||
safe-area-inset-bottom :loading-more-enabled="true" :show-scrollbar="false" @query="query"
|
||||
:auto-clean-list-when-reload="false" :auto-scroll-to-top-when-reload="false" @scroll="pageHook.scroll">
|
||||
<view class="h-20rpx" />
|
||||
<view class="card flex-col-center p-20rpx mb-20rpx" v-for="(v, i) in pageHook.list" :key="v.id">
|
||||
<image class="w-300rpx h-300rpx rounded-50rpx mb-2" :src="filePath(v.faceImg)" mode="aspectFill"
|
||||
@click.stop="hooks.usePreviewImage(filePath(v.faceImg))" />
|
||||
<view class="sub-text-30 mb-2">{{ v.username }}·{{ v.deptName }}</view>
|
||||
<view v-if="false" class="flex w-full items-center justify-end gap-2 second-text-26">
|
||||
<text class="botder_btn" @click="doEdit(v.id)">修改</text>
|
||||
<tm-modal title="确定删除吗?" height="auto" @confirm="doDel(v.id)">
|
||||
<template v-slot:trigger><text class="botder_btn">删除</text></template>
|
||||
</tm-modal>
|
||||
</view>
|
||||
</view>
|
||||
<template #bottom>
|
||||
<view v-if="false"
|
||||
class="bg-#0094ff py-20rpx text-center rounded-40rpx mx-30rpx my-4 sub-text-30 color-white!"
|
||||
@tap="doAdd">
|
||||
添加
|
||||
</view>
|
||||
</template>
|
||||
</z-paging>
|
||||
</crud-layout>
|
||||
</template>
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<crud-layout title="被监护人菜谱" mainIsScroll bg="/static/page/blue-bg.png">
|
||||
<crud-layout :title="pageTitle" mainIsScroll bg="/static/page/blue-bg.png">
|
||||
<view class="pb-32rpx pt-14rpx" :aria-busy="state.loading">
|
||||
<view class="card min-h-92rpx flex items-center rounded-20rpx bg-white px-18rpx py-14rpx">
|
||||
<view class="h-56rpx w-56rpx flex-row-center shrink-0 rounded-16rpx bg-#eef8ff">
|
||||
@@ -11,7 +11,7 @@
|
||||
</view>
|
||||
<view
|
||||
class="h-54rpx w-54rpx flex-row-center shrink-0 rounded-15rpx bg-#f5f8fb active:opacity-75"
|
||||
aria-label="刷新被监护人菜谱"
|
||||
:aria-label="refreshAriaLabel"
|
||||
@tap="loadWardMenus"
|
||||
>
|
||||
<tm-icon name="reset-right-line" :size="26" color="#64748b" :spin="state.loading" />
|
||||
@@ -56,7 +56,7 @@
|
||||
<tm-icon name="error-warning-line" :size="52" color="#d97706" />
|
||||
</view>
|
||||
<view class="sub-text-26 mt-16rpx">菜谱加载失败</view>
|
||||
<view class="second-text-22 mt-6rpx leading-34rpx">请检查网络或监护关系后重新加载</view>
|
||||
<view class="second-text-22 mt-6rpx leading-34rpx">{{ failureDescription }}</view>
|
||||
<tm-button class="mt-18rpx px-28rpx" size="s" skin="thin" @tap="loadWardMenus">重新加载</tm-button>
|
||||
</view>
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
<tm-icon name="bowl-line" :size="52" color="#94a3b8" />
|
||||
</view>
|
||||
<view class="sub-text-26 mt-16rpx">暂无预约菜谱</view>
|
||||
<view class="second-text-22 mt-6rpx leading-34rpx">该被监护人目前没有今天及以后的未核销预约</view>
|
||||
<view class="second-text-22 mt-6rpx leading-34rpx">{{ emptyDescription }}</view>
|
||||
<tm-button class="mt-18rpx px-28rpx" size="s" skin="thin" @tap="loadWardMenus">刷新看看</tm-button>
|
||||
</view>
|
||||
|
||||
@@ -247,18 +247,22 @@ type MealMeta = {
|
||||
|
||||
/** 页面路由参数。 */
|
||||
type WardMenuRouteOptions = {
|
||||
/** 被监护人用户 ID,服务端据此执行监护关系权限校验。 */
|
||||
/** 目标用户 ID,服务端仅允许查询本人或当前有效被监护人。 */
|
||||
wardUserId?: string
|
||||
/** 经过 URI 编码的被监护人展示名称。 */
|
||||
/** 经过 URI 编码的目标用户展示名称。 */
|
||||
wardName?: string
|
||||
/** 是否查询当前登录用户本人,路由协议值 1 表示本人。 */
|
||||
currentUser?: string
|
||||
}
|
||||
|
||||
/** 页面响应式状态,离开页面后无需跨页面持久化。 */
|
||||
type WardMenuPageState = {
|
||||
/** 当前被监护人用户 ID,长整型始终按字符串保存。 */
|
||||
/** 当前目标用户 ID,长整型始终按字符串保存。 */
|
||||
wardUserId: string
|
||||
/** 当前被监护人展示名称。 */
|
||||
/** 当前目标用户展示名称。 */
|
||||
wardName: string
|
||||
/** 当前查询目标是否为登录用户本人。 */
|
||||
currentUser: boolean
|
||||
/** 已查询到的未来未核销预约订单。 */
|
||||
orders: WardMenuOrder[]
|
||||
/** 菜谱请求是否正在执行,用于阻止重复提交。 */
|
||||
@@ -275,6 +279,7 @@ type WardMenuPageState = {
|
||||
const state = reactive<WardMenuPageState>({
|
||||
wardUserId: "",
|
||||
wardName: "被监护人",
|
||||
currentUser: false,
|
||||
orders: [],
|
||||
loading: false,
|
||||
failed: false,
|
||||
@@ -282,6 +287,17 @@ const state = reactive<WardMenuPageState>({
|
||||
requestSequence: 0,
|
||||
})
|
||||
|
||||
/** 页面标题根据查询目标区分本人和被监护人。 */
|
||||
const pageTitle = computed(() => (state.currentUser ? "我的菜谱" : "被监护人菜谱"))
|
||||
/** 刷新按钮的无障碍说明与当前页面身份保持一致。 */
|
||||
const refreshAriaLabel = computed(() => (state.currentUser ? "刷新我的菜谱" : "刷新被监护人菜谱"))
|
||||
/** 菜谱请求失败时,本人无需提示检查监护关系。 */
|
||||
const failureDescription = computed(() => (state.currentUser ? "请检查网络后重新加载" : "请检查网络或监护关系后重新加载"))
|
||||
/** 空菜谱提示根据当前查询目标展示。 */
|
||||
const emptyDescription = computed(() =>
|
||||
state.currentUser ? "您目前没有今天及以后的未核销预约" : "该被监护人目前没有今天及以后的未核销预约",
|
||||
)
|
||||
|
||||
/** 把接口可能返回的空值或非法数字收敛为非负数。 */
|
||||
const normalizedNumber = (value: unknown) => {
|
||||
const parsedValue = Number(value)
|
||||
@@ -423,7 +439,7 @@ const productKey = (product: WardMenuProduct, index: number) =>
|
||||
product.id || product.productId || `${product.productName || "product"}-${index}`
|
||||
|
||||
/**
|
||||
* 查询被监护人未来未核销预约;后端会再次校验当前登录人和目标用户的有效监护关系。
|
||||
* 查询本人或被监护人未来未核销预约;后端会再次校验目标是否为本人或当前有效被监护人。
|
||||
*/
|
||||
const loadWardMenus = async () => {
|
||||
if (state.loading) return
|
||||
@@ -443,7 +459,7 @@ const loadWardMenus = async () => {
|
||||
state.loaded = true
|
||||
} catch (error: any) {
|
||||
if (currentSequence !== state.requestSequence) return
|
||||
console.error("查询被监护人预约菜谱失败", error)
|
||||
console.error(state.currentUser ? "查询本人预约菜谱失败" : "查询被监护人预约菜谱失败", error)
|
||||
state.orders = []
|
||||
state.failed = true
|
||||
state.loaded = true
|
||||
@@ -453,10 +469,12 @@ const loadWardMenus = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
/** 初始化路由参数并加载目标被监护人的预约菜谱。 */
|
||||
/** 初始化路由参数并加载本人或目标被监护人的预约菜谱。 */
|
||||
onLoad((options: WardMenuRouteOptions) => {
|
||||
state.wardUserId = String(options?.wardUserId || "")
|
||||
state.wardName = decodeRouteText(options?.wardName)
|
||||
state.currentUser = options?.currentUser === "1"
|
||||
state.wardName = state.currentUser && !options?.wardName ? "本人" : decodeRouteText(options?.wardName)
|
||||
uni.setNavigationBarTitle({ title: pageTitle.value })
|
||||
loadWardMenus()
|
||||
})
|
||||
</script>
|
||||
|
||||
+36
-39
@@ -410,37 +410,31 @@ const doAppleLogin = async () => {
|
||||
<text class="color-blue font-size-sm" @click="router.jump('/pages/root/register')">没有账号?注册</text>
|
||||
</view>
|
||||
|
||||
<view v-if="showOtherLogin" class="m-x-30rpx m-t-50rpx">
|
||||
<view class="flex items-center justify-center m-b-30rpx">
|
||||
<view class="login-divider-line" />
|
||||
<text class="login-divider-text second-text-24 color-#999 m-x-20rpx">其他登录方式</text>
|
||||
<view class="login-divider-line" />
|
||||
</view>
|
||||
<view class="flex justify-center items-center social-login-row">
|
||||
<view v-if="state.wxLoginEnabled" class="social-login-btn" @tap="onWechatLoginTap">
|
||||
<image class="social-login-icon" src="/static/icon/weixinlogo.png" mode="aspectFill" />
|
||||
<text class="sub-text-26 color-#333 m-l-16rpx">微信登录</text>
|
||||
<view class="absolute bottom-30rpx">
|
||||
<view v-if="showOtherLogin" class="m-b-50rpx">
|
||||
<view class="flex items-center justify-center m-b-20rpx">
|
||||
<view class="login-divider-line" />
|
||||
<text class="login-divider-text second-text-24 color-#999 m-x-20rpx">其他登录方式</text>
|
||||
<view class="login-divider-line" />
|
||||
</view>
|
||||
<view v-if="state.appleLoginEnabled" class="social-login-btn apple-login-btn" @tap="onAppleLoginTap">
|
||||
<text class="apple-logo"></text>
|
||||
<text class="sub-text-26 color-#fff m-l-12rpx">通过 Apple 登录</text>
|
||||
<view class="flex justify-center items-center social-login-row">
|
||||
<view v-if="state.wxLoginEnabled" class="social-login-btn" role="button" aria-label="微信登录"
|
||||
hover-class="social-login-btn--pressed" :hover-stay-time="120" @tap="onWechatLoginTap">
|
||||
<image class="social-login-icon" src="/static/icon/weixinlogo.png" mode="aspectFill" />
|
||||
</view>
|
||||
<view v-if="state.appleLoginEnabled" class="social-login-btn" role="button" aria-label="Apple 登录"
|
||||
hover-class="social-login-btn--pressed" :hover-stay-time="120" @tap="onAppleLoginTap">
|
||||
<text class="apple-logo"></text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<affirm-check v-model="state.isAgree" />
|
||||
</view>
|
||||
|
||||
<affirm-check v-model="state.isAgree" class="absolute bottom-30rpx" />
|
||||
<affirm-modal ref="affirmModalRef" @confirm="affirmModalConfirm" />
|
||||
|
||||
<tm-modal
|
||||
:show="state.socialBindShow"
|
||||
:showCancel="false"
|
||||
title="绑定账号"
|
||||
zIndex="1000"
|
||||
:height="760"
|
||||
confirmText="绑定并登录"
|
||||
@confirm="confirmSocialBind"
|
||||
@close="closeSocialBindModal"
|
||||
>
|
||||
<tm-modal :show="state.socialBindShow" :showCancel="false" title="绑定账号" zIndex="1000" :height="760"
|
||||
confirmText="绑定并登录" @confirm="confirmSocialBind" @close="closeSocialBindModal">
|
||||
<view class="wx-bind-body">
|
||||
<view class="second-text-26 color-#666 wx-bind-tip">
|
||||
{{ socialBindLabel }}已授权,请登录已有账号完成绑定。绑定后可直接使用{{ socialBindLabel }}登录。
|
||||
@@ -464,41 +458,44 @@ const doAppleLogin = async () => {
|
||||
}
|
||||
|
||||
.login-divider-line {
|
||||
flex: 1;
|
||||
flex: 0 0 44rpx;
|
||||
height: 1rpx;
|
||||
background: #e5e7eb;
|
||||
background: #cfd4da;
|
||||
}
|
||||
|
||||
.social-login-row {
|
||||
flex-wrap: wrap;
|
||||
gap: 24rpx;
|
||||
gap: 30rpx;
|
||||
}
|
||||
|
||||
.social-login-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 18rpx 40rpx;
|
||||
border-radius: 40rpx;
|
||||
border: 1rpx solid #e5e7eb;
|
||||
flex: 0 0 80rpx;
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border: 1rpx solid #e1e5ea;
|
||||
border-radius: 50%;
|
||||
background: #fff;
|
||||
box-shadow: 0 4rpx 12rpx rgba(31, 45, 61, 0.05);
|
||||
transition: opacity 0.15s ease, background-color 0.15s ease;
|
||||
}
|
||||
|
||||
.apple-login-btn {
|
||||
background: #000;
|
||||
border-color: #000;
|
||||
.social-login-btn--pressed {
|
||||
background: #f4f6f8;
|
||||
opacity: 0.74;
|
||||
}
|
||||
|
||||
.apple-logo {
|
||||
font-size: 36rpx;
|
||||
color: #fff;
|
||||
font-size: 48rpx;
|
||||
color: #111;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.social-login-icon {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
border-radius: 8rpx;
|
||||
width: 52rpx;
|
||||
height: 52rpx;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.wx-bind-body {
|
||||
|
||||
@@ -92,7 +92,7 @@ const inputClear = () => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<crud-layout title="商家" bg="/static/page/blue-bg.png">
|
||||
<crud-layout title="订餐" bg="/static/page/blue-bg.png">
|
||||
<z-paging ref="pageRef" height="100%" :fixed="false" v-model="pageHook.list" :default-page-size="10"
|
||||
safe-area-inset-bottom :loading-more-enabled="true" :show-scrollbar="false" @query="query"
|
||||
:auto-clean-list-when-reload="false" :auto-scroll-to-top-when-reload="false" @scroll="pageHook.scroll">
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
v-model:show="show"
|
||||
position="right"
|
||||
size="88%"
|
||||
title="被监护人操作"
|
||||
:title="drawerTitle"
|
||||
:show-close="true"
|
||||
:show-footer="false"
|
||||
z-index="9000"
|
||||
@@ -17,14 +17,14 @@
|
||||
class="drawer-hero__image"
|
||||
src="/static/illustration/guardian-action-hero.png"
|
||||
mode="aspectFit"
|
||||
aria-label="被监护人人脸、饭卡与预约服务插画"
|
||||
:aria-label="drawerImageLabel"
|
||||
/>
|
||||
<view class="relative z-2 flex items-center">
|
||||
<view class="h-74rpx w-74rpx shrink-0 overflow-hidden rounded-20rpx border-4rpx border-white border-solid bg-#eef8ff">
|
||||
<image class="h-full w-full" :src="avatarUrl" mode="aspectFill" />
|
||||
</view>
|
||||
<view class="ml-14rpx min-w-0 flex-1 pr-148rpx">
|
||||
<view class="main-text-28 show-line">{{ ward.wardName || "被监护人" }}</view>
|
||||
<view class="main-text-28 show-line">{{ ward.wardName || identityName }}</view>
|
||||
<view class="mt-4rpx flex flex-wrap items-center gap-6rpx">
|
||||
<view class="rounded-full bg-#eaf5ff px-10rpx py-4rpx text-19rpx color-#007ad6 font-700">
|
||||
{{ ward.walletTypeName || "饭卡" }}
|
||||
@@ -84,7 +84,7 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="!ward.rechargeEnabled" class="relative z-2 mt-10rpx flex items-start rounded-15rpx bg-#fff7ed px-13rpx py-9rpx">
|
||||
<view v-if="!isCurrentUser && !ward.rechargeEnabled" class="relative z-2 mt-10rpx flex items-start rounded-15rpx bg-#fff7ed px-13rpx py-9rpx">
|
||||
<tm-icon name="error-warning-line" :size="22" color="#c2410c" />
|
||||
<view class="ml-7rpx flex-1 text-19rpx color-#9a3412 leading-28rpx">
|
||||
{{ ward.disabledReason || "当前饭卡暂不可充值" }}
|
||||
@@ -123,7 +123,7 @@
|
||||
</view>
|
||||
<view v-else class="drawer-content flex-col-center px-30rpx text-center">
|
||||
<tm-icon name="user-line" :size="52" color="#94a3b8" />
|
||||
<view class="second-text-24 mt-14rpx">未选择被监护人</view>
|
||||
<view class="second-text-24 mt-14rpx">未选择可办理业务的用户</view>
|
||||
</view>
|
||||
</tm-drawer>
|
||||
</template>
|
||||
@@ -138,7 +138,7 @@ import GuardianWardActionDrawerSkeleton from "./guardian-ward-action-drawer-skel
|
||||
|
||||
type WardAction = {
|
||||
/** 操作项唯一标识,仅用于前端分发对应页面跳转。 */
|
||||
key: "menu" | "face-upload" | "face-records" | "recharge"
|
||||
key: "menu" | "face-upload" | "face-records" | "recharge" | "wallet-manage"
|
||||
/** 操作项主标题。 */
|
||||
title: string
|
||||
/** 操作项补充说明,禁用时同时用于解释原因。 */
|
||||
@@ -160,9 +160,9 @@ type WardAction = {
|
||||
const props = defineProps<{
|
||||
/** 右侧操作抽屉是否显示。 */
|
||||
modelValue: boolean
|
||||
/** 是否正在刷新被监护人数据;加载期间显示结构占位并阻止空态闪烁。 */
|
||||
/** 是否正在刷新关系人数据;加载期间显示结构占位并阻止空态闪烁。 */
|
||||
loading?: boolean
|
||||
/** 当前选中的被监护人饭卡与预约摘要。 */
|
||||
/** 当前选中的本人或被监护人饭卡与预约摘要。 */
|
||||
ward?: GuardianWardRechargeItem
|
||||
}>()
|
||||
|
||||
@@ -175,6 +175,14 @@ const show = computed({
|
||||
set: (value: boolean) => emit("update:modelValue", value),
|
||||
})
|
||||
|
||||
/** 当前抽屉是否办理登录用户本人的业务,用于切换文案、操作项和业务路由。 */
|
||||
const isCurrentUser = computed(() => Boolean(props.ward?.currentUser))
|
||||
/** 抽屉标题根据当前办理身份展示,避免把本人误称为被监护人。 */
|
||||
const drawerTitle = computed(() => (isCurrentUser.value ? "本人操作" : "被监护人操作"))
|
||||
/** 当前办理人的身份兜底名称。 */
|
||||
const identityName = computed(() => (isCurrentUser.value ? "本人" : "被监护人"))
|
||||
/** 服务插画的无障碍说明根据当前办理身份展示。 */
|
||||
const drawerImageLabel = computed(() => `${identityName.value}人脸、饭卡与预约服务插画`)
|
||||
const avatarUrl = computed(() => filePath(props.ward?.wardAvatar) || "/static/icon/default-avatar.png")
|
||||
const statusClass = computed(() => (props.ward?.rechargeEnabled ? "bg-#ecfdf5 color-#047857" : "bg-#fff7ed color-#c2410c"))
|
||||
const faceCapacityReached = computed(() => {
|
||||
@@ -204,8 +212,8 @@ const faceMetrics = computed(() => [
|
||||
/**
|
||||
* 操作项通过配置统一渲染,避免各类业务入口复制结构和交互状态。
|
||||
*/
|
||||
const actions = computed<WardAction[]>(() => [
|
||||
{
|
||||
const actions = computed<WardAction[]>(() => {
|
||||
const menuAction: WardAction = {
|
||||
key: "menu",
|
||||
title: "查看菜谱",
|
||||
description:
|
||||
@@ -218,19 +226,21 @@ const actions = computed<WardAction[]>(() => [
|
||||
background: "#fffaf5",
|
||||
borderColor: "#fed7aa",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
}
|
||||
const faceUploadAction: WardAction = {
|
||||
key: "face-upload",
|
||||
title: "上传人脸",
|
||||
description: faceCapacityReached.value ? "审核中和可用人脸合计已达 3 张" : "拍摄并提交被监护人的人脸照片审核",
|
||||
description: faceCapacityReached.value
|
||||
? "审核中和可用人脸合计已达 3 张"
|
||||
: `拍摄并提交${isCurrentUser.value ? "本人" : "被监护人"}的人脸照片审核`,
|
||||
icon: "camera-line",
|
||||
iconBg: "#eef8ff",
|
||||
iconColor: "#0094ff",
|
||||
background: "#f8fcff",
|
||||
borderColor: "#dcefff",
|
||||
disabled: faceCapacityReached.value,
|
||||
},
|
||||
{
|
||||
}
|
||||
const faceRecordsAction: WardAction = {
|
||||
key: "face-records",
|
||||
title: "审核记录",
|
||||
description: "查看人脸审核进度、照片和拒绝原因",
|
||||
@@ -240,19 +250,44 @@ const actions = computed<WardAction[]>(() => [
|
||||
background: "#fbfaff",
|
||||
borderColor: "#e9e2ff",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
key: "recharge",
|
||||
title: "饭卡充值",
|
||||
description: props.ward?.rechargeEnabled ? "使用目标食堂支持的支付方式为饭卡充值" : props.ward?.disabledReason || "当前饭卡暂不可充值",
|
||||
icon: "wallet-line",
|
||||
iconBg: "#ecfdf5",
|
||||
iconColor: "#059669",
|
||||
background: "#f5fdf9",
|
||||
borderColor: "#ccefe0",
|
||||
disabled: !props.ward?.rechargeEnabled,
|
||||
},
|
||||
])
|
||||
}
|
||||
if (isCurrentUser.value) {
|
||||
return [
|
||||
menuAction,
|
||||
faceUploadAction,
|
||||
faceRecordsAction,
|
||||
{
|
||||
key: "wallet-manage",
|
||||
title: "钱包管理",
|
||||
description: "查看本人饭卡,并办理充值、冻结等业务",
|
||||
icon: "wallet-line",
|
||||
iconBg: "#ecfdf5",
|
||||
iconColor: "#059669",
|
||||
background: "#f5fdf9",
|
||||
borderColor: "#ccefe0",
|
||||
disabled: false,
|
||||
},
|
||||
]
|
||||
}
|
||||
return [
|
||||
menuAction,
|
||||
faceUploadAction,
|
||||
faceRecordsAction,
|
||||
{
|
||||
key: "recharge",
|
||||
title: "饭卡充值",
|
||||
description: props.ward?.rechargeEnabled
|
||||
? "使用目标食堂支持的支付方式为饭卡充值"
|
||||
: props.ward?.disabledReason || "当前饭卡暂不可充值",
|
||||
icon: "wallet-line",
|
||||
iconBg: "#ecfdf5",
|
||||
iconColor: "#059669",
|
||||
background: "#f5fdf9",
|
||||
borderColor: "#ccefe0",
|
||||
disabled: !props.ward?.rechargeEnabled,
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
/**
|
||||
* 所有业务跳转先关闭右侧抽屉,避免 App 页面切换时残留遮罩。
|
||||
@@ -260,19 +295,31 @@ const actions = computed<WardAction[]>(() => [
|
||||
const handleAction = (action: WardAction) => {
|
||||
if (!props.ward || action.disabled) return
|
||||
const wardUserId = String(props.ward.wardUserId || "")
|
||||
const wardName = encodeURIComponent(props.ward.wardName || "被监护人")
|
||||
const wardName = encodeURIComponent(props.ward.wardName || identityName.value)
|
||||
show.value = false
|
||||
setTimeout(() => {
|
||||
if (action.key === "menu") {
|
||||
router.jump("/pages/order/guardian-ward-menu", { wardUserId, wardName })
|
||||
router.jump("/pages/order/guardian-ward-menu", { wardUserId, wardName, currentUser: isCurrentUser.value ? "1" : "0" })
|
||||
return
|
||||
}
|
||||
if (action.key === "wallet-manage") {
|
||||
router.jump("/pages/wallet/list")
|
||||
return
|
||||
}
|
||||
if (action.key === "face-upload") {
|
||||
router.jump("/pages/human-face/guardian-ward-apply", { wardUserId, wardName })
|
||||
router.jump("/pages/human-face/guardian-ward-apply", {
|
||||
wardUserId,
|
||||
wardName,
|
||||
currentUser: isCurrentUser.value ? "1" : "0",
|
||||
})
|
||||
return
|
||||
}
|
||||
if (action.key === "face-records") {
|
||||
router.jump("/pages/human-face/guardian-ward-records", { wardUserId, wardName })
|
||||
router.jump("/pages/human-face/guardian-ward-records", {
|
||||
wardUserId,
|
||||
wardName,
|
||||
currentUser: isCurrentUser.value ? "1" : "0",
|
||||
})
|
||||
return
|
||||
}
|
||||
router.jump("/pages/wallet/recharge", { wardUserId })
|
||||
|
||||
@@ -1,56 +1,43 @@
|
||||
<template>
|
||||
<crud-layout
|
||||
mainIsScroll
|
||||
title="图谱列表"
|
||||
bg="/static/page/blue-bg.png"
|
||||
navRightText="关系图谱"
|
||||
navRightIcon="records"
|
||||
@navRightClick="openRelationTree"
|
||||
>
|
||||
<z-paging
|
||||
ref="pageRef"
|
||||
v-model="wards"
|
||||
height="100%"
|
||||
:fixed="false"
|
||||
:default-page-size="10"
|
||||
safe-area-inset-bottom
|
||||
:loading-more-enabled="true"
|
||||
:show-scrollbar="false"
|
||||
@query="queryWards"
|
||||
:auto-clean-list-when-reload="false"
|
||||
:auto-scroll-to-top-when-reload="false"
|
||||
>
|
||||
<crud-layout mainIsScroll title="图谱列表" bg="/static/page/blue-bg.png" navRightText="关系图谱" navRightIcon="records"
|
||||
@navRightClick="openRelationTree">
|
||||
<z-paging ref="pageRef" v-model="wards" height="100%" :fixed="false" :default-page-size="10"
|
||||
safe-area-inset-bottom :loading-more-enabled="true" :show-scrollbar="false" @query="queryWards"
|
||||
:auto-clean-list-when-reload="false" :auto-scroll-to-top-when-reload="false">
|
||||
<template #top>
|
||||
<view class="pt-16rpx">
|
||||
<view class="guardian-hero relative h-228rpx overflow-hidden rounded-24rpx bg-white shadow-main">
|
||||
<view class="relative z-2 h-full w-62% flex flex-col justify-center pl-22rpx">
|
||||
<view class="flex items-center gap-8rpx">
|
||||
<view class="main-text-28">被监护人服务台</view>
|
||||
<view v-if="queryCompleted" class="shrink-0 rounded-full bg-#eaf5ff px-10rpx py-4rpx text-19rpx color-#007ad6 font-700">
|
||||
<view class="main-text-28">关系人服务台</view>
|
||||
<view v-if="queryCompleted"
|
||||
class="shrink-0 rounded-full bg-#eaf5ff px-10rpx py-4rpx text-19rpx color-#007ad6 font-700">
|
||||
{{ totalCount }} 人
|
||||
</view>
|
||||
<base-skeleton v-else width="58" height="28" round="14" color="#dcecff" dark-color="#323232" />
|
||||
<base-skeleton v-else width="58" height="28" round="14" color="#dcecff"
|
||||
dark-color="#323232" />
|
||||
</view>
|
||||
<view class="second-text-20 mt-4rpx leading-30rpx">饭卡、人脸与预约状态集中查看</view>
|
||||
<view class="mt-10rpx grid grid-cols-3 gap-6rpx">
|
||||
<view class="col-span-2 min-w-0 rounded-13rpx border-1rpx border-#d8ecfb border-solid bg-#f7fbff px-9rpx py-6rpx">
|
||||
<view
|
||||
class="col-span-2 min-w-0 rounded-13rpx border-1rpx border-#d8ecfb border-solid bg-#f7fbff px-9rpx py-6rpx">
|
||||
<view class="flex items-center text-16rpx color-#475569 font-700">
|
||||
<tm-icon name="emotion-happy-line" :size="15" color="#007ad6" />
|
||||
<view class="ml-4rpx">人脸情况</view>
|
||||
</view>
|
||||
<view class="mt-2rpx grid grid-cols-2">
|
||||
<view
|
||||
v-for="item in heroFaceSummary"
|
||||
:key="item.key"
|
||||
class="flex min-w-0 items-baseline first:border-r-1rpx first:border-#d8ecfb first:border-r-solid last:pl-7rpx"
|
||||
>
|
||||
<view v-for="item in heroFaceSummary" :key="item.key"
|
||||
class="flex min-w-0 items-baseline first:border-r-1rpx first:border-#d8ecfb first:border-r-solid last:pl-7rpx">
|
||||
<view class="show-line text-15rpx color-#64748b">{{ item.label }}</view>
|
||||
<view v-if="queryCompleted" class="ml-4rpx text-20rpx font-800" :style="{ color: item.color }">{{ item.value }}</view>
|
||||
<base-skeleton v-else class="ml-4rpx" width="22" height="20" round="6" color="#dcecff" dark-color="#323232" />
|
||||
<view v-if="queryCompleted" class="ml-4rpx text-20rpx font-800"
|
||||
:style="{ color: item.color }">{{ item.value }}</view>
|
||||
<base-skeleton v-else class="ml-4rpx" width="22" height="20" round="6"
|
||||
color="#dcecff" dark-color="#323232" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="min-w-0 rounded-13rpx border-1rpx border-#fed7aa border-solid bg-#fffaf3 px-8rpx py-6rpx">
|
||||
<view
|
||||
class="min-w-0 rounded-13rpx border-1rpx border-#fed7aa border-solid bg-#fffaf3 px-8rpx py-6rpx">
|
||||
<view class="flex items-center text-15rpx color-#7c4a19 font-700">
|
||||
<tm-icon name="bowl-line" :size="15" color="#c2410c" />
|
||||
<view class="show-line ml-3rpx">预约订单</view>
|
||||
@@ -60,17 +47,14 @@
|
||||
<view v-if="queryCompleted" class="ml-4rpx text-20rpx color-#c2410c font-800">
|
||||
{{ pendingReservationTotal }}
|
||||
</view>
|
||||
<base-skeleton v-else class="ml-4rpx" width="22" height="20" round="6" color="#dcecff" dark-color="#323232" />
|
||||
<base-skeleton v-else class="ml-4rpx" width="22" height="20" round="6"
|
||||
color="#dcecff" dark-color="#323232" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<image
|
||||
class="guardian-hero__image"
|
||||
src="/static/illustration/guardian-service-hero.png"
|
||||
mode="aspectFit"
|
||||
aria-label="监护人陪同被监护人办理饭卡和人脸业务的插画"
|
||||
/>
|
||||
<image class="guardian-hero__image" src="/static/illustration/guardian-service-hero.png"
|
||||
mode="aspectFit" aria-label="本人及关系人的饭卡、人脸和预约服务插画" />
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
@@ -78,20 +62,22 @@
|
||||
<guardian-ward-list-skeleton v-if="!queryCompleted" />
|
||||
|
||||
<view v-else-if="wards.length" class="mt-14rpx flex-col gap-12rpx pb-32rpx">
|
||||
<view
|
||||
v-for="ward in wards"
|
||||
:key="String(ward.wardUserId)"
|
||||
<view v-for="ward in wards" :key="String(ward.wardUserId)"
|
||||
class="overflow-hidden rounded-24rpx border-1rpx border-#e8f0f8 border-solid bg-white px-20rpx pb-14rpx pt-13rpx shadow-main active:bg-#f6fbff"
|
||||
@tap="openActions(ward)"
|
||||
>
|
||||
@tap="openActions(ward)">
|
||||
<view class="flex items-center">
|
||||
<view class="h-72rpx w-72rpx shrink-0 overflow-hidden rounded-20rpx bg-#eef8ff">
|
||||
<image class="h-full w-full" :src="avatarUrl(ward)" mode="aspectFill" />
|
||||
</view>
|
||||
<view class="ml-14rpx min-w-0 flex-1">
|
||||
<view class="flex items-center gap-8rpx">
|
||||
<view class="main-text-28 show-line min-w-0">{{ ward.wardName || "被监护人" }}</view>
|
||||
<view class="shrink-0 rounded-full px-10rpx py-4rpx text-18rpx font-700" :class="statusClass(ward)">
|
||||
<view class="main-text-28 show-line min-w-0">{{ ward.wardName || "关系人" }}</view>
|
||||
<view class="shrink-0 rounded-full px-10rpx py-4rpx text-18rpx font-700"
|
||||
:class="relationIdentityClass(ward)">
|
||||
{{ relationIdentityText(ward) }}
|
||||
</view>
|
||||
<view class="shrink-0 rounded-full px-10rpx py-4rpx text-18rpx font-700"
|
||||
:class="statusClass(ward)">
|
||||
{{ ward.cardStatusName || "状态异常" }}
|
||||
</view>
|
||||
</view>
|
||||
@@ -103,10 +89,12 @@
|
||||
</slot>
|
||||
</view>
|
||||
</view>
|
||||
<tm-icon name="arrow-right-s-line" :size="26" color="#94a3b8" />
|
||||
<tm-icon :name="ward.currentUser || ward.guardianWard ? 'arrow-right-s-line' : 'eye-line'" :size="26"
|
||||
color="#94a3b8" />
|
||||
</view>
|
||||
<view class="mt-10rpx grid grid-cols-3 gap-8rpx">
|
||||
<view class="col-span-2 rounded-16rpx border-1rpx border-#dcecf9 border-solid bg-#f7fbff px-12rpx py-9rpx">
|
||||
<view
|
||||
class="col-span-2 rounded-16rpx border-1rpx border-#dcecf9 border-solid bg-#f7fbff px-12rpx py-9rpx">
|
||||
<view class="flex items-center">
|
||||
<view class="h-30rpx w-30rpx flex-row-center rounded-full bg-#e3f3ff">
|
||||
<tm-icon name="emotion-happy-line" :size="17" color="#007ad6" />
|
||||
@@ -114,11 +102,8 @@
|
||||
<view class="ml-6rpx text-19rpx color-#334155 font-700">人脸情况</view>
|
||||
</view>
|
||||
<view class="mt-6rpx grid grid-cols-2">
|
||||
<view
|
||||
v-for="metric in faceMetrics(ward)"
|
||||
:key="metric.key"
|
||||
class="flex min-w-0 items-baseline first:border-r-1rpx first:border-#dcecf9 first:border-r-solid last:pl-10rpx"
|
||||
>
|
||||
<view v-for="metric in faceMetrics(ward)" :key="metric.key"
|
||||
class="flex min-w-0 items-baseline first:border-r-1rpx first:border-#dcecf9 first:border-r-solid last:pl-10rpx">
|
||||
<view class="show-line text-18rpx color-#64748b">{{ metric.label }}</view>
|
||||
<view class="ml-6rpx shrink-0 text-23rpx font-800" :style="{ color: metric.color }">
|
||||
{{ metric.value }}
|
||||
@@ -141,29 +126,31 @@
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="mt-10rpx flex items-center justify-between border-t-1rpx border-#edf2f7 border-t-solid pt-10rpx">
|
||||
<view
|
||||
class="mt-10rpx flex items-center justify-between border-t-1rpx border-#edf2f7 border-t-solid pt-10rpx">
|
||||
<view class="flex items-baseline">
|
||||
<view class="second-text-18 mr-8rpx">饭卡余额</view>
|
||||
<view class="text-30rpx color-#202938 font-800">¥{{ hooks.useMoney(ward.balance || 0) }} </view>
|
||||
<view class="text-30rpx color-#202938 font-800">¥{{ hooks.useMoney(ward.balance || 0) }}
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="ward.rechargeEnabled" class="text-21rpx color-#059669 font-700">可充值</view>
|
||||
<view v-if="ward.currentUser" class="text-21rpx color-#007ad6 font-700">可办理</view>
|
||||
<view v-else-if="!ward.guardianWard" class="text-20rpx color-#64748b font-600">仅查看</view>
|
||||
<view v-else-if="ward.rechargeEnabled" class="text-21rpx color-#059669 font-700">可充值</view>
|
||||
<view v-else class="show-line ml-16rpx max-w-330rpx text-20rpx color-#c2410c font-600">
|
||||
{{ ward.disabledReason || "暂不可充值" }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<template #empty>
|
||||
<view v-if="queryCompleted" class="mt-18rpx rounded-24rpx bg-white px-28rpx py-64rpx shadow-main">
|
||||
<view class="flex-col-center text-center">
|
||||
<tm-icon name="user-line" :size="54" color="#94a3b8" />
|
||||
<view class="main-text-27 mt-14rpx">暂无有效被监护人</view>
|
||||
<view class="second-text-21 mt-6rpx leading-34rpx">这里只展示当前登录用户直接且有效的监护关系</view>
|
||||
<view class="main-text-27 mt-14rpx">暂无关系人数据</view>
|
||||
<view class="second-text-21 mt-6rpx leading-34rpx">本人信息或关系数据暂未返回,请稍后重试</view>
|
||||
<view
|
||||
class="mt-22rpx h-88rpx min-w-230rpx flex-row-center rounded-full border-1rpx border-blue border-solid bg-white px-30rpx color-#0094ff text-25rpx font-700 active:bg-#eef8ff"
|
||||
@tap="openRelationTree"
|
||||
>
|
||||
@tap="openRelationTree">
|
||||
查看关系图谱
|
||||
</view>
|
||||
</view>
|
||||
@@ -211,6 +198,25 @@ type HeroFaceSummary = {
|
||||
|
||||
const avatarUrl = (ward: GuardianWardRechargeItem) => filePath(ward.wardAvatar) || "/static/icon/default-avatar.png"
|
||||
const statusClass = (ward: GuardianWardRechargeItem) => (ward.rechargeEnabled ? "bg-#ecfdf5 color-#047857" : "bg-#fff7ed color-#c2410c")
|
||||
|
||||
/**
|
||||
* 返回列表项相对当前登录用户的身份文案,避免把所有关系人都误称为被监护人。
|
||||
*/
|
||||
const relationIdentityText = (ward: GuardianWardRechargeItem) => {
|
||||
if (ward.currentUser) return "本人"
|
||||
if (ward.guardianWard) return "被监护人"
|
||||
return "关系人"
|
||||
}
|
||||
|
||||
/**
|
||||
* 不同关系身份使用稳定的轻量标签,业务权限仍以后端返回的 guardianWard 标记为准。
|
||||
*/
|
||||
const relationIdentityClass = (ward: GuardianWardRechargeItem) => {
|
||||
if (ward.currentUser) return "bg-#e8f5ff color-#007ad6"
|
||||
if (ward.guardianWard) return "bg-#ecfdf5 color-#047857"
|
||||
return "bg-#f1f5f9 color-#475569"
|
||||
}
|
||||
|
||||
const sumMetric = (key: "availableFaceCount" | "pendingFaceCount" | "pendingReservationCount") => {
|
||||
if (!queryCompleted.value) return "0"
|
||||
const values = wardCache.value.map((item) => item[key])
|
||||
@@ -248,17 +254,16 @@ const faceMetrics = (ward: GuardianWardRechargeItem): FaceMetric[] => [
|
||||
]
|
||||
|
||||
/**
|
||||
* 接口当前返回全部有效被监护人,页面按 z-paging 的页码切片回填,统一复用项目列表交互。
|
||||
* 接口返回本人及同一完整关系图谱中的全部关系人,页面按 z-paging 的页码切片回填,统一复用项目列表交互。
|
||||
*/
|
||||
const queryWards = async (page: number, pageSize: number) => {
|
||||
const isFirstPage = page === 1
|
||||
try {
|
||||
if (isFirstPage) {
|
||||
queryCompleted.value = false
|
||||
const response: any = await walletApi.guardianWardList()
|
||||
if (response?.code !== 0) throw new Error(response?.msg || "查询被监护人饭卡失败")
|
||||
const response: any = await walletApi.relatedUserList()
|
||||
if (response?.code !== 0) throw new Error(response?.msg || "查询本人及关系人失败")
|
||||
wardCache.value = response?.data || []
|
||||
console.log("wardCache:", wardCache.value)
|
||||
|
||||
totalCount.value = wardCache.value.length
|
||||
if (selectedWard.value) {
|
||||
@@ -268,7 +273,7 @@ const queryWards = async (page: number, pageSize: number) => {
|
||||
const start = (page - 1) * pageSize
|
||||
pageRef.value?.complete(wardCache.value.slice(start, start + pageSize))
|
||||
} catch (error) {
|
||||
console.error("查询被监护人饭卡列表失败", error)
|
||||
console.error("查询本人及关系人列表失败", error)
|
||||
if (isFirstPage) {
|
||||
wardCache.value = []
|
||||
totalCount.value = 0
|
||||
@@ -279,7 +284,14 @@ const queryWards = async (page: number, pageSize: number) => {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 本人和有效被监护人可打开业务抽屉,其他关系人仅展示摘要,不能借列表入口发起代办业务。
|
||||
*/
|
||||
const openActions = (ward: GuardianWardRechargeItem) => {
|
||||
if (!ward.currentUser && !ward.guardianWard) {
|
||||
uni.showToast({ title: "当前关系人仅支持查看", icon: "none" })
|
||||
return
|
||||
}
|
||||
selectedWard.value = ward
|
||||
drawerVisible.value = true
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ export default {
|
||||
method: "POST",
|
||||
data,
|
||||
}),
|
||||
/** 监护人代被监护人提交一至两张人脸照片。 */
|
||||
/** 为当前登录用户本人或其有效被监护人提交一至两张人脸照片。 */
|
||||
createGuardianWard: (data: { wardUserId: string; faceImgs: string[] }) =>
|
||||
request({
|
||||
url: `/admin/user-face/app/guardian-ward`,
|
||||
@@ -32,21 +32,21 @@ export default {
|
||||
url: `/admin/user-face/app/guardian-ward/options`,
|
||||
method: "GET",
|
||||
}),
|
||||
/** 查询当前监护人名下全部或指定被监护人的人脸审核记录。 */
|
||||
/** 查询当前登录用户本人或其有效被监护人的人脸审核记录。 */
|
||||
guardianWardPage: (params: { wardUserId?: string; current: number; size: number; processStatus?: string }) =>
|
||||
request({
|
||||
url: `/admin/user-face/app/guardian-ward/page`,
|
||||
method: "GET",
|
||||
params,
|
||||
}),
|
||||
/** 查询当前监护人可访问的被监护人人脸审核详情。 */
|
||||
/** 查询当前登录用户本人或其有效被监护人的人脸审核详情。 */
|
||||
guardianWardDetail: (params: { id: string }) =>
|
||||
request({
|
||||
url: `/admin/user-face/app/guardian-ward/detail`,
|
||||
method: "GET",
|
||||
params,
|
||||
}),
|
||||
/** 撤回并删除审核中的被监护人人脸记录,返回原图供重新编辑。 */
|
||||
/** 撤回并删除本人或有效被监护人审核中的人脸记录,返回原图供重新编辑。 */
|
||||
cancelGuardianWard: (params: { id: string }) =>
|
||||
request({
|
||||
url: `/admin/user-face/app/guardian-ward/cancel`,
|
||||
|
||||
@@ -2,6 +2,10 @@ import request from "@/sheep/utils/request"
|
||||
|
||||
export type GuardianWardRechargeItem = {
|
||||
wardUserId: string | number
|
||||
/** 是否为当前登录用户本人,仅“本人及关系人”列表接口返回。 */
|
||||
currentUser?: boolean
|
||||
/** 当前登录用户是否可以按有效监护关系为该用户代办业务。 */
|
||||
guardianWard?: boolean
|
||||
wardName: string
|
||||
wardAvatar?: string
|
||||
walletTypeName?: string
|
||||
@@ -121,6 +125,12 @@ export default {
|
||||
url: "/wallet/wallet-recharge/app/guardian-ward/list",
|
||||
method: "GET",
|
||||
}),
|
||||
/** 查询当前登录用户本人及同一完整关系图谱中全部关系人的业务摘要。 */
|
||||
relatedUserList: (): Promise<any> =>
|
||||
request({
|
||||
url: "/wallet/wallet-recharge/app/related-user/list",
|
||||
method: "GET",
|
||||
}),
|
||||
/** 查询目标饭卡、有效套餐和目标食堂支付平台。 */
|
||||
guardianWardRechargeContext: (params: { wardUserId: string | number }): Promise<any> =>
|
||||
request({
|
||||
|
||||
Reference in new issue
Block a user