| @@ -27,6 +27,8 @@ declare module 'vue' { | |||||
| NutEmpty: typeof import('@nutui/nutui-taro')['Empty'] | NutEmpty: typeof import('@nutui/nutui-taro')['Empty'] | ||||
| NutForm: typeof import('@nutui/nutui-taro')['Form'] | NutForm: typeof import('@nutui/nutui-taro')['Form'] | ||||
| NutFormItem: typeof import('@nutui/nutui-taro')['FormItem'] | NutFormItem: typeof import('@nutui/nutui-taro')['FormItem'] | ||||
| NutGrid: typeof import('@nutui/nutui-taro')['Grid'] | |||||
| NutGridItem: typeof import('@nutui/nutui-taro')['GridItem'] | |||||
| NutInput: typeof import('@nutui/nutui-taro')['Input'] | NutInput: typeof import('@nutui/nutui-taro')['Input'] | ||||
| NutNavbar: typeof import('@nutui/nutui-taro')['Navbar'] | NutNavbar: typeof import('@nutui/nutui-taro')['Navbar'] | ||||
| NutRow: typeof import('@nutui/nutui-taro')['Row'] | NutRow: typeof import('@nutui/nutui-taro')['Row'] | ||||
| @@ -8,7 +8,8 @@ export default defineAppConfig({ | |||||
| 'pages/settings/index', | 'pages/settings/index', | ||||
| 'pages/login/index', | 'pages/login/index', | ||||
| 'pages/web-view/index', | 'pages/web-view/index', | ||||
| 'pages/contact-pass-records/index' | |||||
| 'pages/contact-pass-records/index', | |||||
| 'pages/contact-edit-logs/index' | |||||
| ], | ], | ||||
| window: { | window: { | ||||
| backgroundTextStyle: 'light', | backgroundTextStyle: 'light', | ||||
| @@ -32,7 +33,7 @@ export default defineAppConfig({ | |||||
| pagePath: 'pages/invite/index', | pagePath: 'pages/invite/index', | ||||
| selectedIconPath: 'images/calendar-plus-solid-397ef5.png', | selectedIconPath: 'images/calendar-plus-solid-397ef5.png', | ||||
| iconPath: 'images/calendar-plus-regular-797979.png', | iconPath: 'images/calendar-plus-regular-797979.png', | ||||
| text: '新邀约' | |||||
| text: '邀约' | |||||
| }, | }, | ||||
| { | { | ||||
| pagePath: 'pages/contact/index', | pagePath: 'pages/contact/index', | ||||
| @@ -8,10 +8,10 @@ | |||||
| color: #0056b3 !important; | color: #0056b3 !important; | ||||
| } | } | ||||
| .card-hover-gray { | .card-hover-gray { | ||||
| background-color: #EEE; | |||||
| background-color: #EEE !important; | |||||
| } | } | ||||
| .card-hover-white { | .card-hover-white { | ||||
| background-color: #FFF; | |||||
| background-color: #FFF !important; | |||||
| } | } | ||||
| .btn-secondary:hover { | .btn-secondary:hover { | ||||
| @@ -18,7 +18,7 @@ const contacts = useContactsStore() | |||||
| const goContactInfoPage = () => { | const goContactInfoPage = () => { | ||||
| console.log(item) | console.log(item) | ||||
| const contactData = contacts.getContactById(item.userId) | const contactData = contacts.getContactById(item.userId) | ||||
| Router.toContactEdit({ data: contactData }) | |||||
| if (contactData) Router.toContactEdit({ data: contactData }) | |||||
| } | } | ||||
| </script> | </script> | ||||
| @@ -57,7 +57,7 @@ const goContactInfoPage = () => { | |||||
| </view> | </view> | ||||
| </view> | </view> | ||||
| </view> | </view> | ||||
| <view class="pr-4 d-flex justify-content-end"> | |||||
| <view class="d-flex justify-content-end"> | |||||
| <h5 class="pt-3" v-if="item.isBlock" style="line-height: unset !important;"> | <h5 class="pt-3" v-if="item.isBlock" style="line-height: unset !important;"> | ||||
| <view class="badge badge-pill badge-light text-danger"> | <view class="badge badge-pill badge-light text-danger"> | ||||
| <Text className="fas fa-ban text-danger"></Text> | <Text className="fas fa-ban text-danger"></Text> | ||||
| @@ -0,0 +1,6 @@ | |||||
| export default definePageConfig({ | |||||
| navigationBarTitleText: '', | |||||
| usingComponents: {}, | |||||
| navigationStyle: 'custom', | |||||
| disableScroll: true | |||||
| }) | |||||
| @@ -0,0 +1,20 @@ | |||||
| page { | |||||
| height: 100%; | |||||
| } | |||||
| .nut-navbar { | |||||
| background: transparent; | |||||
| } | |||||
| .scroll { | |||||
| flex: 1; | |||||
| overflow: scroll; | |||||
| } | |||||
| .save-button { | |||||
| font-size: 36px | |||||
| } | |||||
| .nut-action-sheet__item { | |||||
| color: red !important; | |||||
| } | |||||
| @@ -0,0 +1,182 @@ | |||||
| <script setup lang="ts"> | |||||
| import { reactive } from 'vue' | |||||
| import { View } from '@tarojs/components' | |||||
| import './index.scss' | |||||
| import {weappAuth} from "../../utils"; | |||||
| import { | |||||
| GroupedContactPassRecords, | |||||
| useContactPassRecordsStore | |||||
| } from "../../stores/contact-pass-records"; | |||||
| import {Router} from "tarojs-router-next"; | |||||
| import { BjxHelper } from "../../utils"; | |||||
| import {ContactEditLog, useContactEditLogsStore} from "../../stores/contact-edit-logs"; | |||||
| const contactData = Router.getData() | |||||
| console.log("contactData", contactData) | |||||
| const state = reactive<{ | |||||
| isPageDataLoading: boolean, | |||||
| isAuthError: boolean, | |||||
| contactEditLogs: ContactEditLog[], | |||||
| }>({ | |||||
| isPageDataLoading: true, | |||||
| isAuthError: false, | |||||
| contactEditLogs: [], | |||||
| }) | |||||
| const editLogs = useContactEditLogsStore() | |||||
| init() | |||||
| function init() { | |||||
| state.isAuthError = false | |||||
| state.isPageDataLoading = true | |||||
| console.log("contactData", contactData) | |||||
| weappAuth().then(r => { | |||||
| console.log(r) | |||||
| editLogs.loadContactEditLogsFromServer(contactData.id).then( editLogs => { | |||||
| state.isPageDataLoading = false | |||||
| state.contactEditLogs = editLogs | |||||
| // console.log(state.groupedPassRecords) | |||||
| }) | |||||
| .catch(error => { | |||||
| console.log("error", error) | |||||
| state.isAuthError = true | |||||
| state.isPageDataLoading = false | |||||
| }) | |||||
| }).catch(e => { | |||||
| console.log("error", e) | |||||
| state.isAuthError = true | |||||
| state.isPageDataLoading = false | |||||
| }) | |||||
| } | |||||
| const onAuthErrorRefresh = () => { | |||||
| init() | |||||
| } | |||||
| // const onPullDownRefresh = () => { | |||||
| // console.log("onPullDownRefresh") | |||||
| // state.isRefresherTriggered = false | |||||
| // } | |||||
| function onPullDownRefresh (e) { | |||||
| console.log("onPullDownRefresh", e) | |||||
| // state.isRefresherTriggered = false | |||||
| } | |||||
| const goBack = () => { | |||||
| Router.back() | |||||
| } | |||||
| </script> | |||||
| <template> | |||||
| <view class="h-100 d-flex flex-column"> | |||||
| <BackgroundBasic/> | |||||
| <NutNavbar title=""></NutNavbar> | |||||
| <view class="p-3 d-flex"> | |||||
| <view class="flex-grow-1"> | |||||
| <view class="h3"> | |||||
| <view class="fas fa-chevron-left text-primary" @tap="goBack()" hover-class="btn-hover-primary"/> | |||||
| 编辑记录 | |||||
| </view> | |||||
| <view class="text-black-50">此联系人的最近编辑记录</view> | |||||
| </view> | |||||
| <view class="d-flex align-items-end"> | |||||
| <View></View> | |||||
| </view> | |||||
| </view> | |||||
| <View v-if="state.isAuthError"> | |||||
| <nut-empty description="连接出现问题,请稍后重试"> | |||||
| <div style="margin-top: 10px"> | |||||
| <nut-button type="default" @tap="onAuthErrorRefresh">刷新</nut-button> | |||||
| </div> | |||||
| </nut-empty> | |||||
| </View> | |||||
| <View class="scroll 100vh" v-else> | |||||
| <view class="scroll 100vh" v-if="!state.isPageDataLoading"> | |||||
| <Scroll-View style="height: 100vh" :scrollY="true"> | |||||
| <view v-for="item in state.contactEditLogs" class="border m-3 p-3 bg-light"> | |||||
| <view class="d-flex flex-row"> | |||||
| <view class="pr-3"> | |||||
| <NutAvatar size="normal" color="white" :bg-color="BjxHelper.mbString2RgbHex(item.userName)" class="overflow-hidden"> | |||||
| <view v-if="BjxHelper.getBJXFirstChar(item.userName)">{{BjxHelper.getBJXFirstChar(item.userName)}}</view> | |||||
| <view v-else> | |||||
| <Text className='fas fa-user fa-lg'/> | |||||
| </view> | |||||
| </NutAvatar> | |||||
| </view> | |||||
| <view class="d-flex align-items-center text-black-50"> | |||||
| <view> | |||||
| <view class="font-weight-bold">{{item.userName + ' ' + item.methodDesc + ' ' + item.paramsDesc}}</view> | |||||
| <view class="text-black-50 small">{{item.datetime}}</view> | |||||
| </view> | |||||
| </view> | |||||
| </View> | |||||
| </view> | |||||
| <view v-if="state.contactEditLogs.length >= 100" class="pl-5 pr-5 pt-5 pb-3 text-black-50"> | |||||
| <nut-divider> 仅显示最近 100 条记录 </nut-divider> | |||||
| </view> | |||||
| <view v-else-if="state.contactEditLogs.length === 0" class="pl-5 pr-5 pt-5 pb-3 text-black-50"> | |||||
| <nut-divider> 没有编辑记录 </nut-divider> | |||||
| </view> | |||||
| <view v-else class="container pt-5 pb-3"> | |||||
| <view class="row"> | |||||
| <view class="col"/> | |||||
| <view class="col text-center text-black-50"> | |||||
| <nut-divider> 没有更多了 </nut-divider> | |||||
| </view> | |||||
| <view class="col"/> | |||||
| </view> | |||||
| </view> | |||||
| </Scroll-View> | |||||
| </view> | |||||
| <View v-else class="skeleton"> | |||||
| <NutSkeleton v-for="_ in 10" class="pl-4 pt-4 pb-4 pr-5" height="20px" width="60vw" animated avatar avatar-size="50px" row="2"/> | |||||
| </View> | |||||
| </View> | |||||
| </view> | |||||
| <!-- <view class="p-4">--> | |||||
| <!-- <button open-type="getPhoneNumber" bindgetphonenumber="getPhoneNumber">使用电话号码登录</button>--> | |||||
| <!-- </view>--> | |||||
| <!-- <Counter/>--> | |||||
| <!-- <nut-cell title="展示弹出层" is-link @click="show = true"></nut-cell>--> | |||||
| <!-- <nut-popup :style="{ padding: '30px 50px' }" v-model:visible="show">正文</nut-popup>--> | |||||
| <!-- <View><Text>{{state.msg}}</Text></View>--> | |||||
| <!-- <nut-button type="primary" @click="handleClick(state.msg2)">点我</nut-button>--> | |||||
| <!-- <Add color="red" />--> | |||||
| <!-- <div class="alert alert-primary" role="alert">--> | |||||
| <!-- A simple primary alert—check it out!--> | |||||
| <!-- </div>--> | |||||
| <!-- <h1>Example heading <span class="badge bg-secondary">New</span></h1>--> | |||||
| <!-- <button type="button" class="btn btn-primary">--> | |||||
| <!-- Notifications <span class="badge text-bg-secondary">4</span>--> | |||||
| <!-- </button>--> | |||||
| <!-- <button type="button" class="btn btn-primary position-relative">--> | |||||
| <!-- Inbox--> | |||||
| <!-- <span class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger">--> | |||||
| <!-- 99+--> | |||||
| <!-- <span class="visually-hidden">unread messages</span>--> | |||||
| <!-- </span>--> | |||||
| <!-- </button>--> | |||||
| <!-- </view>--> | |||||
| </template> | |||||
| @@ -32,15 +32,16 @@ const onSaveClicked = () => { | |||||
| }).catch(error => { | }).catch(error => { | ||||
| Taro.hideLoading() | Taro.hideLoading() | ||||
| if (error) { | if (error) { | ||||
| const errorMessage = (typeof error === "string")? error : "服务器错误,请稍后重试" | |||||
| Taro.showToast({ | Taro.showToast({ | ||||
| title: error, | |||||
| title: errorMessage, | |||||
| icon: 'none', | icon: 'none', | ||||
| duration: 2000 | duration: 2000 | ||||
| }) | }) | ||||
| } | } | ||||
| else { | else { | ||||
| Taro.showToast({ | Taro.showToast({ | ||||
| title: '网络异常,请稍候重试', | |||||
| title: '网络异常,请稍后重试', | |||||
| icon: 'none', | icon: 'none', | ||||
| duration: 2000 | duration: 2000 | ||||
| }) | }) | ||||
| @@ -70,7 +71,7 @@ const onBlockConfirm = (item) => { | |||||
| }).catch(error => { | }).catch(error => { | ||||
| Taro.hideLoading() | Taro.hideLoading() | ||||
| Taro.showToast({ | Taro.showToast({ | ||||
| title: '网络异常,请稍候重试', | |||||
| title: '网络异常,请稍后重试', | |||||
| icon: 'none', | icon: 'none', | ||||
| duration: 2000 | duration: 2000 | ||||
| }) | }) | ||||
| @@ -81,8 +82,8 @@ const onClickPassRecords = () => { | |||||
| Router.toContactPassRecords({ data: contactData }) | Router.toContactPassRecords({ data: contactData }) | ||||
| } | } | ||||
| const onClickEditRecords = () => { | |||||
| Router.toContactEdit({ data: contactData }) | |||||
| const onClickEditLogs = () => { | |||||
| Router.toContactEditLogs({ data: contactData }) | |||||
| } | } | ||||
| const onDeleteConfirm = (item) => { | const onDeleteConfirm = (item) => { | ||||
| @@ -99,7 +100,7 @@ const onDeleteConfirm = (item) => { | |||||
| }).catch(error => { | }).catch(error => { | ||||
| Taro.hideLoading() | Taro.hideLoading() | ||||
| Taro.showToast({ | Taro.showToast({ | ||||
| title: '网络异常,请稍候重试', | |||||
| title: '网络异常,请稍后重试', | |||||
| icon: 'none', | icon: 'none', | ||||
| duration: 2000 | duration: 2000 | ||||
| }) | }) | ||||
| @@ -141,7 +142,7 @@ const onDeleteConfirm = (item) => { | |||||
| </view> | </view> | ||||
| <view hover-class="card-hover-gray"> | <view hover-class="card-hover-gray"> | ||||
| <nut-cell title="编辑记录" sub-title="本联系人在公司中的所有信息修改记录" @tap="onClickEditRecords"> | |||||
| <nut-cell title="编辑记录" sub-title="此联系人的信息修改记录" @tap="onClickEditLogs"> | |||||
| <template #desc> | <template #desc> | ||||
| <Text className="fas fa-chevron-right"/> | <Text className="fas fa-chevron-right"/> | ||||
| </template> | </template> | ||||
| @@ -29,7 +29,7 @@ const onSaveClicked = () => { | |||||
| }).catch(error => { | }).catch(error => { | ||||
| Taro.hideLoading() | Taro.hideLoading() | ||||
| Taro.showToast({ | Taro.showToast({ | ||||
| title: '网络异常,请稍候重试', | |||||
| title: '网络异常,请稍后重试', | |||||
| icon: 'none', | icon: 'none', | ||||
| duration: 2000 | duration: 2000 | ||||
| }) | }) | ||||
| @@ -125,6 +125,9 @@ const goBack = () => { | |||||
| <view v-if="passRecords.passRecords.length >= 100" class="pl-5 pr-5 pt-5 pb-3 text-black-50"> | <view v-if="passRecords.passRecords.length >= 100" class="pl-5 pr-5 pt-5 pb-3 text-black-50"> | ||||
| <nut-divider> 仅显示最近 100 条记录 </nut-divider> | <nut-divider> 仅显示最近 100 条记录 </nut-divider> | ||||
| </view> | </view> | ||||
| <view v-else-if="passRecords.passRecords.length === 0" class="pl-5 pr-5 pt-5 pb-3 text-black-50"> | |||||
| <nut-divider> 没有到访记录 </nut-divider> | |||||
| </view> | |||||
| <view v-else class="container pt-5 pb-3"> | <view v-else class="container pt-5 pb-3"> | ||||
| <view class="row"> | <view class="row"> | ||||
| @@ -11,4 +11,15 @@ page { | |||||
| overflow: scroll; | overflow: scroll; | ||||
| } | } | ||||
| .nut-grid-item__content { | |||||
| padding: 0; | |||||
| display:unset; | |||||
| } | |||||
| .nut-grid-item__content--border { | |||||
| border-right-width: unset; | |||||
| border-bottom-width: unset; | |||||
| } | |||||
| .nut-grid-item__content--surround { | |||||
| border-top-width: unset; | |||||
| border-left-width: unset; | |||||
| } | |||||
| @@ -1,11 +1,208 @@ | |||||
| <template> | |||||
| <view class="index"> | |||||
| <text>我是邀约页面!</text> | |||||
| </view> | |||||
| </template> | |||||
| <script lang="ts" setup> | |||||
| import Contact from "../../components/contact" | |||||
| import { Router } from 'tarojs-router-next' | |||||
| import {reactive, ref} from 'vue'; | |||||
| import './index.scss' | |||||
| import { useContactsStore, ContactData } from '../../stores/contacts' | |||||
| import { registerRouterBackListener } from 'tarojs-router-next' | |||||
| import {BjxHelper} from "../../utils"; | |||||
| import {gql} from "graphql-tag"; | |||||
| import {PinyinHelper, GQLRequest, weappAuth} from "../../utils"; | |||||
| import {useAuthStore} from "../../stores/auth"; | |||||
| // defineExpose({ | |||||
| // loadContactsFromServer, | |||||
| // }); | |||||
| const state = reactive<{ | |||||
| isAuthError: boolean, | |||||
| isContactDataLoadError: boolean, | |||||
| isContactDataLoading: boolean, | |||||
| searchQuery: string, | |||||
| normalContacts: ContactData[], | |||||
| flexContacts: ContactData[], // Generic type for now | |||||
| }>({ | |||||
| isAuthError: false, | |||||
| isContactDataLoadError: false, | |||||
| isContactDataLoading: true, | |||||
| searchQuery: "", | |||||
| normalContacts: [], | |||||
| flexContacts: [] | |||||
| }) | |||||
| const contacts = useContactsStore() | |||||
| <script> | |||||
| export default { | |||||
| name: 'Invite' | |||||
| init() | |||||
| function init() { | |||||
| state.isAuthError = false | |||||
| state.isContactDataLoadError = false | |||||
| state.isContactDataLoading = true | |||||
| weappAuth() | |||||
| .then(r => { | |||||
| contacts.loadContactsFromServer().then( | |||||
| contactList => { | |||||
| console.log("contactList", contactList) | |||||
| generateContactGroupList(contactList) | |||||
| state.isContactDataLoading = false | |||||
| }) | |||||
| .catch(e => { | |||||
| state.isAuthError = false | |||||
| state.isContactDataLoadError = true | |||||
| state.isContactDataLoading = false | |||||
| }) | |||||
| }) | |||||
| .catch(e=> { | |||||
| console.log("error", e) | |||||
| state.isAuthError = true | |||||
| state.isContactDataLoadError = false | |||||
| state.isContactDataLoading = false | |||||
| }) | |||||
| registerRouterBackListener((to, from) => { | |||||
| if ((from.url === "/pages/contact-new/index" && to.url === "/pages/invite/index") | |||||
| || (from.url === "/pages/contact-edit/index" && to.url === "/pages/invite/index") | |||||
| ) { | |||||
| generateContactGroupList(contacts.allContacts) | |||||
| } | |||||
| }) | |||||
| } | |||||
| const onAuthErrorRefresh = () => { | |||||
| init() | |||||
| } | |||||
| const onContactDataLoadErrorRefresh = () => { | |||||
| state.isContactDataLoadError = false | |||||
| state.isContactDataLoading = true | |||||
| contacts.loadContactsFromServer().then( | |||||
| contactList => { | |||||
| console.log("contactList", contactList) | |||||
| generateContactGroupList(contactList) | |||||
| state.isContactDataLoading = false | |||||
| }) | |||||
| .catch(e => { | |||||
| state.isContactDataLoadError = true | |||||
| state.isContactDataLoading = false | |||||
| }) | |||||
| } | } | ||||
| const generateContactGroupList = (contactList: ContactData[]) => { | |||||
| state.normalContacts = [] | |||||
| state.flexContacts = [] | |||||
| for (const item of contactList) { | |||||
| if (item.flexVisit === true) state.flexContacts.push(item) | |||||
| else state.normalContacts.push(item) | |||||
| } | |||||
| } | |||||
| const goToContactNewPage = () => { | |||||
| Router.toContactNew() | |||||
| } | |||||
| const goToContactEditPage = (item) => { | |||||
| Router.toContactEdit({ data: item }) | |||||
| } | |||||
| </script> | </script> | ||||
| <template> | |||||
| <view class="h-100 d-flex flex-column"> | |||||
| <BackgroundBasic/> | |||||
| <NutNavbar title=""></NutNavbar> | |||||
| <view class="pl-3 pt-3 pr-3 h3 d-flex"> | |||||
| <view class="flex-grow-1"> | |||||
| 我的邀约 | |||||
| </view> | |||||
| </view> | |||||
| <View class="scroll 100vh"> | |||||
| <View class="mt-3 p-3"> | |||||
| <View class="border bg-white p-3 d-flex flex-column" hover-class="card-hover-gray" @tap="goToContactNewPage()" > | |||||
| <View class="d-flex justify-content-center"> | |||||
| <Image :src="require('./invite.jpeg')" class="p-3" mode='heightFix' style="height: 15vh"/> | |||||
| </View> | |||||
| <View class="d-flex justify-content-center"> | |||||
| <View class="fas fa-plus-circle text-black-50" style="font-size: 120%"> 新邀约</View> | |||||
| </View> | |||||
| </View> | |||||
| </View> | |||||
| <View v-if="!state.isContactDataLoading"> | |||||
| <View v-if="state.isAuthError"> | |||||
| <nut-empty description="连接出现问题,请稍后重试"> | |||||
| <div style="margin-top: 10px"> | |||||
| <nut-button type="default" @tap="onAuthErrorRefresh">刷新</nut-button> | |||||
| </div> | |||||
| </nut-empty> | |||||
| </View> | |||||
| <View v-else-if="state.isContactDataLoadError"> | |||||
| <nut-empty description="连接出现问题,请稍后重试"> | |||||
| <div style="margin-top: 10px"> | |||||
| <nut-button type="default" @tap="onContactDataLoadErrorRefresh">刷新</nut-button> | |||||
| </div> | |||||
| </nut-empty> | |||||
| </View> | |||||
| <View v-else> | |||||
| <View class="h4 p-3">来访联系人</View> | |||||
| <View class="container-fluid"> | |||||
| <View class="row w-100"> | |||||
| <View style="width: 25vw" v-for="item in state.normalContacts"> | |||||
| <View class="m-2 p-3 border" hover-class="card-hover-gray" @tap="goToContactEditPage(item)"> | |||||
| <View class="d-flex justify-content-center"> | |||||
| <NutBadge :value="item.isVIP?'VIP':''"> | |||||
| <NutAvatar size="normal" color="white" :bg-color="BjxHelper.mbString2RgbHex(item.name)" class="overflow-hidden"> | |||||
| <img v-if="item.avatar" :src="item.avatar" /> | |||||
| <view v-else-if="BjxHelper.getBJXFirstChar(item.name)">{{BjxHelper.getBJXFirstChar(item.name)}}</view> | |||||
| <view v-else> | |||||
| <Text className='fas fa-user fa-lg'/> | |||||
| </view> | |||||
| </NutAvatar> | |||||
| </NutBadge> | |||||
| </View> | |||||
| <View class="d-flex justify-content-center small b pt-2 text-nowrap overflow-hidden"> | |||||
| {{item.name}} | |||||
| </View> | |||||
| </View> | |||||
| </View> | |||||
| </View> | |||||
| </View> | |||||
| <View class="h4 p-3">可随时来访联系人</View> | |||||
| <View class="container-fluid"> | |||||
| <View class="row w-100"> | |||||
| <View style="width: 25vw" v-for="item in state.flexContacts"> | |||||
| <View class="m-2 p-3 border" hover-class="card-hover-gray" @tap="goToContactEditPage(item)"> | |||||
| <View class="d-flex justify-content-center"> | |||||
| <NutBadge :value="item.isVIP?'VIP':''"> | |||||
| <NutAvatar size="normal" color="white" :bg-color="BjxHelper.mbString2RgbHex(item.name)" class="overflow-hidden"> | |||||
| <img v-if="item.avatar" :src="item.avatar" /> | |||||
| <view v-else-if="BjxHelper.getBJXFirstChar(item.name)">{{BjxHelper.getBJXFirstChar(item.name)}}</view> | |||||
| <view v-else> | |||||
| <Text className='fas fa-user fa-lg'/> | |||||
| </view> | |||||
| </NutAvatar> | |||||
| </NutBadge> | |||||
| </View> | |||||
| <View class="d-flex justify-content-center small b pt-2 text-nowrap overflow-hidden"> | |||||
| {{item.name}} | |||||
| </View> | |||||
| </View> | |||||
| </View> | |||||
| </View> | |||||
| </View> | |||||
| </View> | |||||
| </View> | |||||
| <View v-else class="d-flex flex-column"> | |||||
| <NutSkeleton v-for="_ in 10" class="p-3" height="15px" animated avatar avatar-size="50px" row="1"/> | |||||
| </View> | |||||
| </View> | |||||
| </view> | |||||
| </template> | |||||
| @@ -0,0 +1,88 @@ | |||||
| // 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 dayjs from "dayjs"; | |||||
| import {PassRecords} from "./pass-records"; | |||||
| export interface ContactEditLog { | |||||
| userName: string | |||||
| method: string | |||||
| methodDesc: string | |||||
| datetime: string | |||||
| params: string | |||||
| paramsDesc: string | |||||
| } | |||||
| export const useContactEditLogsStore = defineStore('contact-edit-logs', () => { | |||||
| const editLogs: Ref<UnwrapRef<ContactEditLog[]>> = ref([]) | |||||
| const GQL_QUERY_CONTACT_EDIT_LOGS = gql` | |||||
| query ($visitorId: ID, $limit: Int ) { | |||||
| visitorLogs(visitorId: $visitorId, limit: $limit) { | |||||
| userName | |||||
| param | |||||
| method | |||||
| operateTime | |||||
| } | |||||
| } | |||||
| ` | |||||
| function loadContactEditLogsFromServer (visitorId: number) { | |||||
| return new Promise((resolve, reject) => { | |||||
| const items: ContactEditLog[] = [] | |||||
| const limit = 100 | |||||
| console.log("visitorId", visitorId) | |||||
| return GQLRequest.query(GQL_QUERY_CONTACT_EDIT_LOGS, {visitorId, limit}) | |||||
| .then(result => { | |||||
| if (result.code == 500) { | |||||
| reject("Network Error") | |||||
| } else { | |||||
| console.log(result) | |||||
| for (let item of result.data.visitorLogs) { | |||||
| // console.log(item) | |||||
| items.push({ | |||||
| userName: item.userName, | |||||
| method: item.method, | |||||
| methodDesc: methodToDesc(item.method), | |||||
| datetime: dayjs(item.operateTime, "YYYY-MM-DDTHH:mm:ss").format("YYYY年MM月DD日 HH:mm:ss"), | |||||
| params: item.param, | |||||
| paramsDesc: paramsToDesc(item.method, item.param) | |||||
| } as ContactEditLog) | |||||
| } | |||||
| editLogs.value = items.sort((a, b) => a.datetime.localeCompare(b.datetime)).reverse(); | |||||
| console.log("editLogs", editLogs.value) | |||||
| resolve(editLogs.value) | |||||
| } | |||||
| }).catch(e => { | |||||
| reject(e) | |||||
| }) | |||||
| }) | |||||
| } | |||||
| function methodToDesc(method: string) { | |||||
| if (method === "updateVisitor") return "更新了" | |||||
| else if (method === "createVisitor") return "创建了" | |||||
| else if (method === "deleteVisitor") return "删除了" | |||||
| else return method | |||||
| } | |||||
| function paramsToDesc(method: string, params: string) { | |||||
| if (method === "updateVisitor" || method === "createVisitor") { | |||||
| if (params.indexOf("resourceId") !== -1) return "访客照片和信息" | |||||
| else if (params.indexOf('isBlock":true') !== -1) return "阻止状态" | |||||
| else if (params.indexOf('isBlock":false') !== -1) return "取消阻止状态" | |||||
| else return "访客信息" | |||||
| } | |||||
| else if (method === "deleteVisitor" ) { | |||||
| return "此访客" | |||||
| } | |||||
| else return params | |||||
| } | |||||
| return { loadContactEditLogsFromServer } | |||||
| }) | |||||
| @@ -36,8 +36,8 @@ export const useContactItemStore = defineStore('contact-item', () => { | |||||
| } | } | ||||
| const CREATE_VISITOR = gql` | const CREATE_VISITOR = gql` | ||||
| mutation CreateVisitor($name: String!, $nickName: String, $phone: String, $company: String, $isVIP: Boolean, $flexVisit: Boolean, $resourceId: Long ) { | |||||
| createVisitor( input: { | |||||
| mutation CreateVisitor($staffId: ID!, $name: String!, $nickName: String, $phone: String, $company: String, $isVIP: Boolean, $flexVisit: Boolean, $resourceId: ID ) { | |||||
| createVisitor( staffId: $staffId, input: { | |||||
| name: $name, | name: $name, | ||||
| nickname: $nickName, | nickname: $nickName, | ||||
| phone: $phone, | phone: $phone, | ||||
| @@ -50,8 +50,8 @@ export const useContactItemStore = defineStore('contact-item', () => { | |||||
| ` | ` | ||||
| const UPDATE_VISITOR = gql` | const UPDATE_VISITOR = gql` | ||||
| mutation UpdateVisitor($id: Long!, $name: String!, $nickName: String, $phone: String, $company: String, $isVIP: Boolean, $flexVisit: Boolean, $resourceId: Long ) { | |||||
| updateVisitor( input: { | |||||
| mutation UpdateVisitor($staffId: ID!, $id: ID!, $name: String!, $nickName: String, $phone: String, $company: String, $isVIP: Boolean, $flexVisit: Boolean, $resourceId: ID ) { | |||||
| updateVisitor( staffId: $staffId, input: { | |||||
| id: $id, | id: $id, | ||||
| name: $name, | name: $name, | ||||
| nickname: $nickName, | nickname: $nickName, | ||||
| @@ -65,14 +65,14 @@ export const useContactItemStore = defineStore('contact-item', () => { | |||||
| ` | ` | ||||
| const DELETE_VISITOR = gql` | const DELETE_VISITOR = gql` | ||||
| mutation DeleteVisitor($id: Long! ) { | |||||
| deleteVisitor( id: $id ) | |||||
| mutation DeleteVisitor( $staffId: ID!, $id: ID! ) { | |||||
| deleteVisitor( staffId: $staffId, id: $id ) | |||||
| } | } | ||||
| ` | ` | ||||
| const CHANGE_BLOCK_STATUS = gql` | const CHANGE_BLOCK_STATUS = gql` | ||||
| mutation UpdateVisitor($id: Long!, $isBlock: Boolean ) { | |||||
| updateVisitor( input: { | |||||
| mutation UpdateVisitor( $staffId: ID!, $id: ID!, $isBlock: Boolean ) { | |||||
| updateVisitor( staffId: $staffId, input: { | |||||
| id: $id, | id: $id, | ||||
| isBlock: $isBlock | isBlock: $isBlock | ||||
| } ) | } ) | ||||
| @@ -86,13 +86,15 @@ export const useContactItemStore = defineStore('contact-item', () => { | |||||
| const company = contact.value.company | const company = contact.value.company | ||||
| const isVIP = contact.value.isVIP | const isVIP = contact.value.isVIP | ||||
| const flexVisit = contact.value.flexVisit | const flexVisit = contact.value.flexVisit | ||||
| const staffId = Session.get("staff").id | |||||
| return new Promise((resolve, reject) => { | return new Promise((resolve, reject) => { | ||||
| if (contactNewAvatar.value !== "") { | if (contactNewAvatar.value !== "") { | ||||
| uploadFace().then(r => { | uploadFace().then(r => { | ||||
| console.log(r) | console.log(r) | ||||
| const resourceId = r.data.resourceId | const resourceId = r.data.resourceId | ||||
| GQLRequest.query(CREATE_VISITOR, { name, nickName, phone, company, isVIP, flexVisit, resourceId }) | |||||
| GQLRequest.query(CREATE_VISITOR, { staffId, name, nickName, phone, company, isVIP, flexVisit, resourceId }) | |||||
| .then(r=> resolve(r)) | .then(r=> resolve(r)) | ||||
| .catch(e=> reject(e)) | .catch(e=> reject(e)) | ||||
| }).catch(e => { | }).catch(e => { | ||||
| @@ -100,7 +102,7 @@ export const useContactItemStore = defineStore('contact-item', () => { | |||||
| }) | }) | ||||
| } | } | ||||
| else { | else { | ||||
| GQLRequest.query(CREATE_VISITOR, { name, nickName, phone, company, isVIP, flexVisit }) | |||||
| GQLRequest.query(CREATE_VISITOR, { staffId, name, nickName, phone, company, isVIP, flexVisit }) | |||||
| .then(r=> resolve(r)) | .then(r=> resolve(r)) | ||||
| .catch(e=> reject(e)) | .catch(e=> reject(e)) | ||||
| } | } | ||||
| @@ -117,14 +119,14 @@ export const useContactItemStore = defineStore('contact-item', () => { | |||||
| const company = contact.value.company | const company = contact.value.company | ||||
| const isVIP = contact.value.isVIP | const isVIP = contact.value.isVIP | ||||
| const flexVisit = contact.value.flexVisit | const flexVisit = contact.value.flexVisit | ||||
| const staffId = Session.get("staff").id | |||||
| return new Promise((resolve, reject) => { | return new Promise((resolve, reject) => { | ||||
| if (contactNewAvatar.value !== "") { | if (contactNewAvatar.value !== "") { | ||||
| uploadFace().then(r => { | uploadFace().then(r => { | ||||
| console.log(r) | console.log(r) | ||||
| const resourceId = r.data.resourceId | const resourceId = r.data.resourceId | ||||
| GQLRequest.query(UPDATE_VISITOR, {id, name, nickName, phone, company, isVIP, flexVisit, resourceId }) | |||||
| GQLRequest.query(UPDATE_VISITOR, {staffId, id, name, nickName, phone, company, isVIP, flexVisit, resourceId }) | |||||
| .then(r=> resolve(r)) | .then(r=> resolve(r)) | ||||
| .catch(e=> reject(e)) | .catch(e=> reject(e)) | ||||
| }).catch(e => { | }).catch(e => { | ||||
| @@ -132,7 +134,7 @@ export const useContactItemStore = defineStore('contact-item', () => { | |||||
| }) | }) | ||||
| } | } | ||||
| else { | else { | ||||
| GQLRequest.query(UPDATE_VISITOR, {id, name, nickName, phone, company, isVIP, flexVisit }) | |||||
| GQLRequest.query(UPDATE_VISITOR, {staffId, id, name, nickName, phone, company, isVIP, flexVisit }) | |||||
| .then(r=> resolve(r)) | .then(r=> resolve(r)) | ||||
| .catch(e=> reject(e)) | .catch(e=> reject(e)) | ||||
| } | } | ||||
| @@ -141,13 +143,16 @@ export const useContactItemStore = defineStore('contact-item', () => { | |||||
| function setBlockStatus(isBlock: Boolean) { | function setBlockStatus(isBlock: Boolean) { | ||||
| const id = contact.value.id | const id = contact.value.id | ||||
| return GQLRequest.query(CHANGE_BLOCK_STATUS, { id, isBlock }) | |||||
| const staffId = Session.get("staff").id | |||||
| return GQLRequest.query(CHANGE_BLOCK_STATUS, { staffId, id, isBlock }) | |||||
| } | } | ||||
| function deleteVisitor() { | function deleteVisitor() { | ||||
| const id = contact.value.id | const id = contact.value.id | ||||
| const staffId = Session.get("staff").id | |||||
| return GQLRequest.query(DELETE_VISITOR, { id }) | |||||
| return GQLRequest.query(DELETE_VISITOR, { staffId, id }) | |||||
| } | } | ||||
| function uploadFace() { | function uploadFace() { | ||||
| @@ -78,10 +78,7 @@ export const useContactsStore = defineStore('contacts', () => { | |||||
| function getContactById(id: number) { | function getContactById(id: number) { | ||||
| for(const contact of allContacts.value) { | for(const contact of allContacts.value) { | ||||
| console.log(contact) | |||||
| if (contact.id === id) { | |||||
| return contact | |||||
| } | |||||
| if (contact.id === id) return contact | |||||
| } | } | ||||
| return undefined | return undefined | ||||
| } | } | ||||
| @@ -75,7 +75,7 @@ export const usePassRecordsStore = defineStore('pass-records', () => { | |||||
| if (result.code == 500) { | if (result.code == 500) { | ||||
| reject("Network Error") | reject("Network Error") | ||||
| } else { | } else { | ||||
| console.log(result) | |||||
| // console.log(result) | |||||
| const resultGroups = ['passRecords8', 'passRecords9'] | const resultGroups = ['passRecords8', 'passRecords9'] | ||||
| for (let groupName of resultGroups) { | for (let groupName of resultGroups) { | ||||
| for (let item of result.data[groupName]) { | for (let item of result.data[groupName]) { | ||||
| @@ -100,7 +100,7 @@ export const usePassRecordsStore = defineStore('pass-records', () => { | |||||
| passRecords.value = items.sort((a, b) => a.recognizedTime.localeCompare(b.recognizedTime)).reverse(); | passRecords.value = items.sort((a, b) => a.recognizedTime.localeCompare(b.recognizedTime)).reverse(); | ||||
| // console.log(passRecords.value) | // console.log(passRecords.value) | ||||
| generatePassRecordsGroupList(passRecords.value) | generatePassRecordsGroupList(passRecords.value) | ||||
| console.log(groupedPassRecords.value) | |||||
| // console.log(groupedPassRecords.value) | |||||
| resolve(groupedPassRecords.value) | resolve(groupedPassRecords.value) | ||||
| } | } | ||||
| }).catch(e => { | }).catch(e => { | ||||