| @@ -2,15 +2,17 @@ | |||||
| import './index.scss' | import './index.scss' | ||||
| import { BjxHelper } from "../../utils"; | import { BjxHelper } from "../../utils"; | ||||
| /** | /** | ||||
| * props: todayPassTime: Json Array | * props: todayPassTime: Json Array | ||||
| * example: [["YYYY-MM-DD hh:mm:ss"], ["YYYY-MM-DD hh:mm:ss"]] | * example: [["YYYY-MM-DD hh:mm:ss"], ["YYYY-MM-DD hh:mm:ss"]] | ||||
| */ | */ | ||||
| const props = defineProps(['avatar', 'name', 'company', 'is-vip', 'pass-date', 'pass-time', 'today-pass-time']) | |||||
| let lastName = BjxHelper.getBJXFirstChar(props.name) | |||||
| let isVIP = JSON.parse(props.isVip) | |||||
| let todayPassJsonObject = JSON.parse(props.todayPassTime) | |||||
| const props = defineProps(['item']) | |||||
| const item = props.item.passRecords[0] | |||||
| const otherItems = props.item.passRecords.slice(1) | |||||
| console.log(item.userName, otherItems.length , otherItems) | |||||
| let lastName = BjxHelper.getBJXFirstChar(item.userName) | |||||
| // let isVIP = props.isVip | |||||
| // let todayPassJsonObject = JSON.parse(props.todayPassTime) | |||||
| </script> | </script> | ||||
| @@ -21,61 +23,67 @@ let todayPassJsonObject = JSON.parse(props.todayPassTime) | |||||
| <view class="card-body"> | <view class="card-body"> | ||||
| <view class="d-flex flex-row"> | <view class="d-flex flex-row"> | ||||
| <view class="pr-3 h6"> | <view class="pr-3 h6"> | ||||
| <nut-avatar size="normal" color="white" :bg-color="BjxHelper.mbString2RgbHex(lastName)"> | |||||
| <view> | |||||
| <view v-if="lastName">{{lastName}}</view> | |||||
| <NutBadge :value="item.isVIP?'VIP':''"> | |||||
| <NutAvatar size="normal" color="white" :bg-color="BjxHelper.mbString2RgbHex(item.userName)" class="overflow-hidden"> | |||||
| <img v-if="item.faceUrl" :src="item.faceUrl" /> | |||||
| <view v-else-if="BjxHelper.getBJXFirstChar(item.userName)">{{BjxHelper.getBJXFirstChar(item.userName)}}</view> | |||||
| <view v-else> | <view v-else> | ||||
| <Text className='fa fa-user'/> | |||||
| <Text className='fas fa-user fa-lg'/> | |||||
| </view> | </view> | ||||
| </view> | |||||
| </nut-avatar> | |||||
| </NutAvatar> | |||||
| </NutBadge> | |||||
| </view> | </view> | ||||
| <view> | |||||
| <view class="flex-grow-1"> | |||||
| <view class="d-flex flex-row"> | <view class="d-flex flex-row"> | ||||
| <view v-if="props.name" class="name">{{props.name}}</view> | |||||
| <view v-if="item.userName" class="name">{{item.userName}}</view> | |||||
| <view v-else> | <view v-else> | ||||
| <view class="text-black-50 name"> | <view class="text-black-50 name"> | ||||
| <Text className='text-info fas fa-exclamation-circle'/> 无姓名 | <Text className='text-info fas fa-exclamation-circle'/> 无姓名 | ||||
| </view> | </view> | ||||
| </view> | </view> | ||||
| <view class="pl-2"> | |||||
| <view v-if="isVIP" class="badge badge-pill badge-danger">VIP</view> | |||||
| <view v-else></view> | |||||
| </view> | |||||
| </view> | </view> | ||||
| <view class="company text-black-50"> | <view class="company text-black-50"> | ||||
| <view v-if="props.company">{{props.company}}</view> | |||||
| <view v-if="item.company">{{item.company}}</view> | |||||
| <view v-else> | <view v-else> | ||||
| <Text className='text-info fas fa-exclamation-circle'/> 无公司信息 | <Text className='text-info fas fa-exclamation-circle'/> 无公司信息 | ||||
| </view> | </view> | ||||
| </view> | </view> | ||||
| </view> | </view> | ||||
| <view class="pr-4 d-flex justify-content-end"> | |||||
| <h5 class="pt-3" v-if="item.isBlock" style="line-height: unset !important;"> | |||||
| <view class="badge badge-pill badge-light text-danger"> | |||||
| <Text className="fas fa-ban text-danger"></Text> | |||||
| 被阻止人员 | |||||
| </view> | |||||
| </h5> | |||||
| <view v-else></view> | |||||
| </view> | |||||
| </view> | </view> | ||||
| </view> | </view> | ||||
| <view class="card-footer bg-white"> | <view class="card-footer bg-white"> | ||||
| <view v-if="todayPassJsonObject.length === 0"> | |||||
| <view v-if="otherItems.length === 0"> | |||||
| <nut-collapse> | <nut-collapse> | ||||
| <nut-collapse-item name="name1" :title="'来访时间:'+ props.passDate" :value="props.passTime" :border=false> | |||||
| <nut-collapse-item name="name1" :title="'来访时间:'+ item.date" :value="item.time" :border=false> | |||||
| <template #icon> </template> | <template #icon> </template> | ||||
| </nut-collapse-item> | </nut-collapse-item> | ||||
| </nut-collapse> | </nut-collapse> | ||||
| </view> | </view> | ||||
| <view v-else> | <view v-else> | ||||
| <nut-collapse> | <nut-collapse> | ||||
| <nut-collapse-item name="name1" :title="'来访时间:'+ props.passDate" :value="props.passTime" :border=false> | |||||
| <view v-for="(passTime, index) in todayPassJsonObject" :key="passTime"> | |||||
| <nut-collapse-item name="name1" :title="'来访时间:'+ item.date" :value="item.time" :border=false> | |||||
| <view v-for="(passTime, index) in otherItems" :key="passTime.id"> | |||||
| <view class="d-flex"> | <view class="d-flex"> | ||||
| <view class="flex-grow-1"> | <view class="flex-grow-1"> | ||||
| <view v-if="index === 0">当日还有 {{todayPassJsonObject.length}} 次记录</view> | |||||
| <view v-if="index === 0">当日还有 {{otherItems.length}} 次记录</view> | |||||
| <view v-else></view> | <view v-else></view> | ||||
| </view> | </view> | ||||
| <view> {{ passTime }} </view> | |||||
| <view> {{ passTime.time }} </view> | |||||
| </view> | </view> | ||||
| </view> | </view> | ||||
| </nut-collapse-item> | </nut-collapse-item> | ||||
| </nut-collapse> | </nut-collapse> | ||||
| </view> | </view> | ||||
| </view> | </view> | ||||
| @@ -1,4 +1,4 @@ | |||||
| <script setup> | |||||
| <script setup lang="ts"> | |||||
| import { reactive } from 'vue' | import { reactive } from 'vue' | ||||
| import { View, Text } from '@tarojs/components' | import { View, Text } from '@tarojs/components' | ||||
| @@ -9,14 +9,20 @@ import {useDidShow, useLoad} from "@tarojs/taro"; | |||||
| import { Date } from '@nutui/icons-vue-taro'; | import { Date } from '@nutui/icons-vue-taro'; | ||||
| import Notification from "../../components/notification"; | import Notification from "../../components/notification"; | ||||
| import {weappAuth} from "../../utils"; | import {weappAuth} from "../../utils"; | ||||
| import {useContactsStore} from "../../stores/contacts"; | |||||
| import {GroupedPassRecords, usePassRecordsStore} from "../../stores/pass-records"; | |||||
| const state = reactive<{ | const state = reactive<{ | ||||
| isPageDataLoading: boolean, | isPageDataLoading: boolean, | ||||
| isAuthError: boolean, | isAuthError: boolean, | ||||
| groupedPassRecords: GroupedPassRecords[], | |||||
| }>({ | }>({ | ||||
| isPageDataLoading: true, | isPageDataLoading: true, | ||||
| isAuthError: false | |||||
| isAuthError: false, | |||||
| groupedPassRecords: [] | |||||
| }) | }) | ||||
| const passRecords = usePassRecordsStore() | |||||
| init() | init() | ||||
| @@ -25,7 +31,17 @@ function init() { | |||||
| state.isPageDataLoading = true | state.isPageDataLoading = true | ||||
| weappAuth().then(r => { | weappAuth().then(r => { | ||||
| console.log(r) | console.log(r) | ||||
| state.isPageDataLoading = false | |||||
| passRecords.loadPassRecordsFromServer().then( passRecords => { | |||||
| state.isPageDataLoading = false | |||||
| state.groupedPassRecords = passRecords | |||||
| console.log("aaa",state.groupedPassRecords ) | |||||
| // console.log(state.groupedPassRecords) | |||||
| }) | |||||
| .catch(error => { | |||||
| console.log("error", error) | |||||
| state.isAuthError = true | |||||
| state.isPageDataLoading = false | |||||
| }) | |||||
| }).catch(e => { | }).catch(e => { | ||||
| console.log("error", e) | console.log("error", e) | ||||
| state.isAuthError = true | state.isAuthError = true | ||||
| @@ -92,19 +108,19 @@ const onAuthErrorRefresh = () => { | |||||
| <View class="scroll 100vh" v-else> | <View class="scroll 100vh" v-else> | ||||
| <view class="scroll 100vh" v-if="!state.isPageDataLoading"> | <view class="scroll 100vh" v-if="!state.isPageDataLoading"> | ||||
| <Scroll-View :scroll-y="true"> | <Scroll-View :scroll-y="true"> | ||||
| <Notification name="" company="小明科技2" is-vip="true" pass-date="2023-12-25" pass-time="22:22:22" today-pass-time='["22:22:21","22:22:20"]'/> | |||||
| <Notification name="王远" company="joydata科技" is-vip="true" pass-date="2023-12-25" pass-time="22:22:22" today-pass-time='[]'/> | |||||
| <Notification name="华雨" company="joydata科技" is-vip="false" pass-date="2023-12-25" pass-time="22:22:22" today-pass-time='["22:22:21"]'/> | |||||
| <Notification name="邸为荣" company="" is-vip="false" pass-date="2023-12-25" pass-time="22:22:22" today-pass-time='["22:22:21","22:22:20"]'/> | |||||
| <Notification v-for="item in state.groupedPassRecords" :item="item"/> | |||||
| <!-- <Notification name="王远" company="joydata科技" is-vip="true" pass-date="2023-12-25" pass-time="22:22:22" today-pass-time='[]'/>--> | |||||
| <!-- <Notification name="华雨" company="joydata科技" is-vip="false" pass-date="2023-12-25" pass-time="22:22:22" today-pass-time='["22:22:21"]'/>--> | |||||
| <!-- <Notification name="邸为荣" company="" is-vip="false" pass-date="2023-12-25" pass-time="22:22:22" today-pass-time='["22:22:21","22:22:20"]'/>--> | |||||
| <Notification name="邸为荣" company="" is-vip="false" pass-date="2023-12-25" pass-time="22:22:22" today-pass-time='["22:22:21","22:22:20"]'/> | |||||
| <Notification name="邸为荣" company="" is-vip="false" pass-date="2023-12-25" pass-time="22:22:22" today-pass-time='["22:22:21","22:22:20"]'/> | |||||
| <Notification name="邸为荣" company="" is-vip="false" pass-date="2023-12-25" pass-time="22:22:22" today-pass-time='["22:22:21","22:22:20"]'/> | |||||
| <!-- <Notification name="邸为荣" company="" is-vip="false" pass-date="2023-12-25" pass-time="22:22:22" today-pass-time='["22:22:21","22:22:20"]'/>--> | |||||
| <!-- <Notification name="邸为荣" company="" is-vip="false" pass-date="2023-12-25" pass-time="22:22:22" today-pass-time='["22:22:21","22:22:20"]'/>--> | |||||
| <!-- <Notification name="邸为荣" company="" is-vip="false" pass-date="2023-12-25" pass-time="22:22:22" today-pass-time='["22:22:21","22:22:20"]'/>--> | |||||
| <Notification name="邸为荣" company="" is-vip="false" pass-date="2023-12-25" pass-time="22:22:22" today-pass-time='["22:22:21","22:22:20"]'/> | |||||
| <Notification name="邸为荣" company="" is-vip="false" pass-date="2023-12-25" pass-time="22:22:22" today-pass-time='["22:22:21","22:22:20"]'/> | |||||
| <Notification name="邸为荣" company="" is-vip="false" pass-date="2023-12-25" pass-time="22:22:22" today-pass-time='["22:22:21","22:22:20"]'/> | |||||
| <!-- <Notification name="邸为荣" company="" is-vip="false" pass-date="2023-12-25" pass-time="22:22:22" today-pass-time='["22:22:21","22:22:20"]'/>--> | |||||
| <!-- <Notification name="邸为荣" company="" is-vip="false" pass-date="2023-12-25" pass-time="22:22:22" today-pass-time='["22:22:21","22:22:20"]'/>--> | |||||
| <!-- <Notification name="邸为荣" company="" is-vip="false" pass-date="2023-12-25" pass-time="22:22:22" today-pass-time='["22:22:21","22:22:20"]'/>--> | |||||
| <view class="container pt-5 pb-3"> | <view class="container pt-5 pb-3"> | ||||
| <view class="row"> | <view class="row"> | ||||
| @@ -0,0 +1,131 @@ | |||||
| // https://pinia.esm.dev/introduction.html | |||||
| import { defineStore } from 'pinia' | |||||
| import {GQLRequest, PinyinHelper, Session} from "../utils"; | |||||
| import {gql} from "graphql-tag"; | |||||
| import {ref, Ref, UnwrapRef} from "vue"; | |||||
| import Taro from "@tarojs/taro"; | |||||
| import moment from "moment"; | |||||
| export interface PassRecords { | |||||
| key: string | |||||
| id: number | |||||
| userName: string | |||||
| userId: number | |||||
| faceUrl: string | |||||
| recognizedTime: string | |||||
| createTime: Date | |||||
| type: number | |||||
| company: string | |||||
| isVIP: boolean | |||||
| isBlock: boolean | |||||
| date: string | |||||
| time: string | |||||
| } | |||||
| export interface GroupedPassRecords { | |||||
| key: string | |||||
| passRecords: PassRecords[] | |||||
| } | |||||
| export const usePassRecordsStore = defineStore('pass-records', () => { | |||||
| const passRecords: Ref<UnwrapRef<PassRecords[]>> = ref([]) | |||||
| const groupedPassRecords: Ref<UnwrapRef<GroupedPassRecords[]>> = ref([]) | |||||
| const GQL_QUERY_30_DAYS_PASS_RECORDS = gql` | |||||
| query ($startDate: Date, $endDate: Date ) { | |||||
| passRecords8: visitorPassRecords(type: 8, startDate: $startDate, endDate: $endDate) { | |||||
| id | |||||
| userName | |||||
| userId | |||||
| faceUrl | |||||
| recognizedTime | |||||
| createTime | |||||
| type | |||||
| visitor { | |||||
| isVip | |||||
| isBlock | |||||
| visitorCompany | |||||
| } | |||||
| } | |||||
| passRecords9: visitorPassRecords(type: 9, startDate: $startDate, endDate: $endDate) { | |||||
| id | |||||
| userName | |||||
| userId | |||||
| faceUrl | |||||
| recognizedTime | |||||
| createTime | |||||
| type | |||||
| visitor { | |||||
| isVip | |||||
| isBlock | |||||
| visitorCompany | |||||
| } | |||||
| } | |||||
| } | |||||
| ` | |||||
| function loadPassRecordsFromServer () { | |||||
| return new Promise((resolve, reject) => { | |||||
| const items: PassRecords[] = [] | |||||
| const startDate = moment().subtract(30, "days" ).format("YYYY-MM-DD") | |||||
| const endDate = moment().add(1, "days").format("YYYY-MM-DD") | |||||
| console.log(startDate, endDate) | |||||
| return GQLRequest.query(GQL_QUERY_30_DAYS_PASS_RECORDS, {startDate, endDate}) | |||||
| .then(result => { | |||||
| if (result.code == 500) { | |||||
| reject("Network Error") | |||||
| } else { | |||||
| console.log(result) | |||||
| const resultGroups = ['passRecords8', 'passRecords9'] | |||||
| for (let groupName of resultGroups) { | |||||
| for (let item of result.data[groupName]) { | |||||
| // console.log(item) | |||||
| items.push({ | |||||
| key: moment(item.recognizedTime, "YYYY-MM-DDTHH:mm:ss").format("YYYY-MM-DD")+item.userId, | |||||
| id: item.id, | |||||
| userName: item.userName, | |||||
| userId: item.userId, | |||||
| faceUrl: item.faceUrl, | |||||
| recognizedTime: item.recognizedTime, | |||||
| createTime: item.createTime, | |||||
| type: item.type, | |||||
| company: item.visitor ? item.visitor.visitorCompany : "", | |||||
| isVIP: item.visitor ? item.visitor.isVip: false, | |||||
| isBlock: item.visitor ? item.visitor.isBlock : false, | |||||
| date: moment(item.recognizedTime, "YYYY-MM-DDTHH:mm:ss").format("YYYY-MM-DD"), | |||||
| time: moment(item.recognizedTime, "YYYY-MM-DDTHH:mm:ss").format("HH:mm:ss"), | |||||
| } as PassRecords) | |||||
| } | |||||
| } | |||||
| passRecords.value = items.sort((a, b) => a.recognizedTime.localeCompare(b.recognizedTime)).reverse(); | |||||
| // console.log(passRecords.value) | |||||
| generatePassRecordsGroupList(passRecords.value) | |||||
| console.log(groupedPassRecords.value) | |||||
| resolve(groupedPassRecords.value) | |||||
| } | |||||
| }).catch(e => { | |||||
| reject(e) | |||||
| }) | |||||
| }) | |||||
| } | |||||
| const generatePassRecordsGroupList = (passRecords: PassRecords[]) => { | |||||
| let passRecordsGroupedList = [] | |||||
| for (const item of passRecords) { | |||||
| const key = item.key; | |||||
| if (!passRecordsGroupedList[key]) { | |||||
| passRecordsGroupedList[key] = { key: key, passRecords: [] }; | |||||
| } | |||||
| passRecordsGroupedList[key].passRecords.push(item); | |||||
| } | |||||
| groupedPassRecords.value = Object.values(passRecordsGroupedList) | |||||
| } | |||||
| const getGroupedList = () => { | |||||
| return groupedPassRecords.value | |||||
| } | |||||
| return { loadPassRecordsFromServer, getGroupedList} | |||||
| }) | |||||