| @@ -1,11 +1,8 @@ | |||
| <script setup lang="ts"> | |||
| import "./index.scss"; | |||
| import Taro from "@tarojs/taro"; | |||
| import { Router } from 'tarojs-router-next' | |||
| import { reactive, ref } from 'vue'; | |||
| import {BjxHelper} from "../../utils"; | |||
| import {BjxHelper, ScreenHelper} from "../../utils"; | |||
| import {useContactsStore} from "../../stores/contacts"; | |||
| import {useContactItemStore} from "../../stores/contact-item"; | |||
| import {useContactVisitRecordStore} from "../../stores/contact-visit-record"; | |||
| @@ -31,6 +28,9 @@ console.log("id", contact.contact.id) | |||
| const onShowSuggestionsMenu = ref(false) | |||
| const screenWidth = ScreenHelper.getScreenWidth() | |||
| console.log("screenWidth", screenWidth) | |||
| const state = reactive({ | |||
| newAvatar: undefined, | |||
| isDateSelectorVisible: false | |||
| @@ -75,16 +75,16 @@ const onNickNameSelected = (value) => { | |||
| <nut-form-item> | |||
| <view> | |||
| <view class="d-flex justify-content-center"> | |||
| <nut-avatar size="192" @click="updateFacePhoto" class="overflow-hidden"> | |||
| <nut-avatar :size="Math.floor(screenWidth/3)" @click="updateFacePhoto" class="overflow-hidden"> | |||
| <img v-if="state.newAvatar" :src="state.newAvatar"/> | |||
| <img v-else-if="contact.contact.avatar" :src="contact.contact.avatar" /> | |||
| <view v-else class="d-flex align-items-center justify-content-center h-100 w-100"> | |||
| <Text class="fas fa-user fa-6x"></Text> | |||
| <Text class="fas fa-user fa-5x"></Text> | |||
| </view> | |||
| </nut-avatar> | |||
| </view> | |||
| <view class="d-flex justify-content-center pt-3"> | |||
| <view class="btn btn-secondary badge-pill" @tap="updateFacePhoto">{{contact.contact.avatar || state.newAvatar ?'更换':'添加'}}面部识别照片</view> | |||
| <view class="btn-sm btn btn-secondary badge-pill" @tap="updateFacePhoto">{{contact.contact.avatar || state.newAvatar ?'更换':'添加'}}面部识别照片</view> | |||
| </view> | |||
| </view> | |||
| </nut-form-item> | |||
| @@ -26,7 +26,7 @@ const goContactInfoPage = () => { | |||
| <template> | |||
| <view class="container"> | |||
| <view class="pt-2"></view> | |||
| <view class="card mr-3 bg-light"> | |||
| <view class="card mr-3 bg-light shadow-sm"> | |||
| <view class="card-body" hover-class="card-hover-white" @tap="goContactInfoPage"> | |||
| <view class="d-flex flex-row"> | |||
| <view class="pr-3 h6"> | |||
| @@ -1,7 +1,6 @@ | |||
| <script setup lang="ts"> | |||
| import { reactive } from 'vue' | |||
| import { View } from '@tarojs/components' | |||
| import './index.scss' | |||
| import {weappAuth} from "../../utils"; | |||
| import { | |||
| @@ -9,10 +8,11 @@ import { | |||
| useContactPassRecordsStore | |||
| } from "../../stores/contact-pass-records"; | |||
| import {Router} from "tarojs-router-next"; | |||
| import { BjxHelper } from "../../utils"; | |||
| import { BjxHelper, Session } from "../../utils"; | |||
| import {ContactEditLog, useContactEditLogsStore} from "../../stores/contact-edit-logs"; | |||
| const contactData = Router.getData() | |||
| console.log("contactData", contactData) | |||
| @@ -103,7 +103,16 @@ const goBack = () => { | |||
| <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"> | |||
| <view v-if="item.userId === Session.get('staff').id" class="pr-3"> | |||
| <NutAvatar size="normal" color="white" :bg-color="BjxHelper.mbString2RgbHex(Session.get('staff').userName)" class="overflow-hidden"> | |||
| <img v-if="Session.get('staff').avatar" :src="Session.get('staff').avatar" /> | |||
| <view v-else-if="BjxHelper.getBJXFirstChar(Session.get('staff').userName)">{{BjxHelper.getBJXFirstChar(Session.get('staff').userName)}}</view> | |||
| <view v-else> | |||
| <Text className='fas fa-user fa-lg'/> | |||
| </view> | |||
| </NutAvatar> | |||
| </view> | |||
| <view v-else 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> | |||
| @@ -113,7 +122,7 @@ const goBack = () => { | |||
| </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="font-weight-bold">{{ ((item.userId === Session.get('staff').id) ? '我' : item.userName) + ' ' + item.methodDesc + ' ' + item.paramsDesc}}</view> | |||
| <view class="text-black-50 small">{{item.datetime}}</view> | |||
| </view> | |||
| </view> | |||
| @@ -182,16 +182,16 @@ const onSaveAndSendMessage = () => { | |||
| </nut-cell> | |||
| </view> | |||
| <view class="p-3"> | |||
| <view class="pl-3 pr-3 pt-2 pb-2"> | |||
| <nut-button :disabled="contactData.phone.length !== 11 || contactData.isBlock" block type="info" @tap="onSaveAndSendMessage">保存并发送邀约短信</nut-button> | |||
| </view> | |||
| <view class="p-3"> | |||
| <view class="pl-3 pr-3 pt-2 pb-2"> | |||
| <nut-button v-if="contactData.isBlock" block color="red" plain @tap="onBlockConfirm">取消阻止此联系人</nut-button> | |||
| <nut-button v-else block color="red" plain @tap="onBlockVisitor">阻止此联系人</nut-button> | |||
| </view> | |||
| <view class="p-3 pb-5"> | |||
| <view class="pl-3 pr-3 pt-2 pb-4"> | |||
| <nut-button block color="red" @tap="onDeleteVisitor">删除联系人</nut-button> | |||
| </view> | |||
| </view> | |||
| @@ -1,5 +1,5 @@ | |||
| <script setup lang="ts"> | |||
| import { Router } from 'tarojs-router-next' | |||
| import {Router} from 'tarojs-router-next' | |||
| import './index.scss' | |||
| import {useContactItemStore} from "../../stores/contact-item"; | |||
| import {useContactsStore} from "../../stores/contacts"; | |||
| @@ -17,6 +17,7 @@ const contacts = useContactsStore() | |||
| const contactVisitRecord = useContactVisitRecordStore() | |||
| contactVisitRecord.reset() | |||
| const onSaveClicked = (isSmsSend: boolean) => { | |||
| Taro.showLoading({ | |||
| title: 'loading', | |||
| @@ -87,11 +88,12 @@ const onSaveAndSendMessage = () => { | |||
| <view class="scroll pb-2"> | |||
| <ContactForm/> | |||
| <view class="p-3 pb-4"> | |||
| <nut-button :disabled="contact.contact.phone.length !== 11 || contact.contact.isBlock" block type="info" @tap="onSaveAndSendMessage">保存并发送邀约短信</nut-button> | |||
| </view> | |||
| </view> | |||
| <view class="p-3 pb-5"> | |||
| <nut-button :disabled="contact.contact.phone.length !== 11 || contact.contact.isBlock" block type="info" @tap="onSaveAndSendMessage">保存并发送邀约短信</nut-button> | |||
| </view> | |||
| @@ -1,5 +1,5 @@ | |||
| <script setup lang="ts"> | |||
| import { reactive } from 'vue' | |||
| import {onUnmounted, reactive} from 'vue' | |||
| import { View } from '@tarojs/components' | |||
| import './index.scss' | |||
| @@ -13,8 +13,15 @@ import { BjxHelper } from "../../utils"; | |||
| import {ContactEditLog, useContactEditLogsStore} from "../../stores/contact-edit-logs"; | |||
| import {useContactItemStore} from "../../stores/contact-item"; | |||
| import {useContactVisitRecordStore} from "../../stores/contact-visit-record"; | |||
| import Taro from "@tarojs/taro"; | |||
| import Taro, {getCurrentPages} from "@tarojs/taro"; | |||
| onUnmounted(()=> { | |||
| //当上一个页面时新建访客时,自动跳出,避免新建访客保存多次 | |||
| const pages = getCurrentPages() | |||
| if (pages[pages.length - 2].route === 'pages/contact-new/index') { | |||
| Router.back() | |||
| } | |||
| }) | |||
| const contactData = Router.getData() | |||
| console.log("contactData", contactData) | |||
| @@ -27,6 +34,10 @@ const state = reactive({ | |||
| isSmsSent: false | |||
| }); | |||
| // const onUnmounted = () => { | |||
| // } | |||
| const goBack = () => { | |||
| Router.back(undefined, { delta: 2 }) | |||
| } | |||
| @@ -115,74 +126,90 @@ const onSmsSend = () => { | |||
| <View></View> | |||
| </view> | |||
| </view> | |||
| <View class="d-flex flex-column" > | |||
| <View class="d-flex justify-content-center"> | |||
| <Image :src="require('./sms.png')" mode='heightFix' style="height: 30vh"/> | |||
| <View class="scroll pb-2"> | |||
| <View class="d-flex flex-column"> | |||
| <View class="d-flex justify-content-center"> | |||
| <Image :src="require('./sms.png')" mode='heightFix' style="height: 25vh"/> | |||
| </View> | |||
| </View> | |||
| </View> | |||
| <View class="card m-3 mt-5 shadow-lg"> | |||
| <View class="card-body"> | |||
| <View class="p-3 pt-5"> | |||
| <View class="d-flex justify-content-center"> | |||
| <View class="h4 text-muted">发送邀约短信</View> | |||
| </View> | |||
| <View class="d-flex justify-content-center"> | |||
| <View class="text-muted">访客将会收到一个拜访邀约码</View> | |||
| </View> | |||
| </View> | |||
| <nut-form> | |||
| <nut-form-item> | |||
| <view class="d-flex"> | |||
| <view class="mr-4"> | |||
| <NutBadge :value="contact.contact.isVIP?'VIP':''"> | |||
| <NutAvatar size="normal" color="white" :bg-color="BjxHelper.mbString2RgbHex(contact.contact.name)" class="overflow-hidden"> | |||
| <img v-if="contact.contact.avatar" :src="contact.contact.avatar" /> | |||
| <view v-else-if="BjxHelper.getBJXFirstChar(contact.contact.name)">{{BjxHelper.getBJXFirstChar(contact.contact.name)}}</view> | |||
| <view v-else> | |||
| <Text className='fas fa-user fa-lg'/> | |||
| </view> | |||
| </NutAvatar> | |||
| </NutBadge> | |||
| </view> | |||
| <View class="card m-3 shadow-lg"> | |||
| <View class="card-body"> | |||
| <nut-form> | |||
| <nut-form-item> | |||
| <view class="d-flex"> | |||
| <view class="mr-4"> | |||
| <NutBadge :value="contact.contact.isVIP?'VIP':''"> | |||
| <NutAvatar size="normal" color="white" :bg-color="BjxHelper.mbString2RgbHex(contact.contact.name)" | |||
| class="overflow-hidden"> | |||
| <img v-if="contact.contact.avatar" :src="contact.contact.avatar"/> | |||
| <view v-else-if="BjxHelper.getBJXFirstChar(contact.contact.name)"> | |||
| {{ BjxHelper.getBJXFirstChar(contact.contact.name) }} | |||
| </view> | |||
| <view v-else> | |||
| <Text className='fas fa-user fa-lg'/> | |||
| </view> | |||
| </NutAvatar> | |||
| </NutBadge> | |||
| </view> | |||
| <view class="flex-grow-1"> | |||
| <view class="d-flex flex-row"> | |||
| <view v-if="contact.contact.name" class="name text-dark">{{contact.contact.name}}</view> | |||
| <view v-else> | |||
| <view class="text-black-50 name"> | |||
| <Text className='text-info fas fa-exclamation-circle'/> 无姓名 | |||
| <view class="flex-grow-1"> | |||
| <view class="d-flex flex-row"> | |||
| <view v-if="contact.contact.name" class="name text-dark">{{ contact.contact.name }}</view> | |||
| <view v-else> | |||
| <view class="text-black-50 name"> | |||
| <Text className='text-info fas fa-exclamation-circle'/> | |||
| 无姓名 | |||
| </view> | |||
| </view> | |||
| </view> | |||
| <view class="company text-black-50 small"> | |||
| <view v-if="contact.contact.company">{{ contact.contact.company }}</view> | |||
| <view v-else> | |||
| <Text className='text-info fas fa-exclamation-circle'/> | |||
| 无公司信息 | |||
| </view> | |||
| </view> | |||
| </view> | |||
| </view> | |||
| <view class="company text-black-50 small"> | |||
| <view v-if="contact.contact.company">{{contact.contact.company}}</view> | |||
| <view v-else> | |||
| <Text className='text-info fas fa-exclamation-circle'/> 无公司信息 | |||
| </view> | |||
| </view> | |||
| </view> | |||
| </view> | |||
| </view> | |||
| </nut-form-item> | |||
| </nut-form-item> | |||
| <nut-form-item label="约定到访日期" v-show="!contact.contact.flexVisit"> | |||
| <view class="d-flex calendar"> | |||
| <view class="flex-grow-1"></view> | |||
| <view class="text-dark" @tap="openSwitch('isDateSelectorVisible')"> | |||
| {{contact.contact.nextVisitDate ? contact.contact.nextVisitDate : '请选择'}} | |||
| </view> | |||
| </view> | |||
| </nut-form-item> | |||
| <nut-form-item label="约定到访日期" v-show="!contact.contact.flexVisit"> | |||
| <view class="d-flex calendar"> | |||
| <view class="flex-grow-1"></view> | |||
| <view class="text-dark" @tap="openSwitch('isDateSelectorVisible')"> | |||
| {{ contact.contact.nextVisitDate ? contact.contact.nextVisitDate : '请选择' }} | |||
| </view> | |||
| </view> | |||
| </nut-form-item> | |||
| </nut-form> | |||
| </nut-form> | |||
| </View> | |||
| </View> | |||
| </View> | |||
| <view v-if="state.isSmsSent" class="p-3 pb-5"> | |||
| <nut-button block type="info" @tap="goBack">确认</nut-button> | |||
| <view v-if="state.isSmsSent" class="p-3"> | |||
| <nut-button block type="info" @tap="goBack">确认</nut-button> | |||
| </view> | |||
| <view v-else class="p-3 pb-5"> | |||
| <nut-button block type="info" @tap="onSmsSend">发送邀约短信</nut-button> | |||
| <view v-else class="p-3"> | |||
| <nut-button block type="info" @tap="onSmsSend">发送邀约短信</nut-button> | |||
| </view> | |||
| <nut-calendar | |||
| @@ -195,41 +222,9 @@ const onSmsSend = () => { | |||
| </nut-calendar> | |||
| </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> | |||
| @@ -118,8 +118,8 @@ const goToContactEditPage = (item) => { | |||
| </view> | |||
| </view> | |||
| <View class="scroll 100vh w-100"> | |||
| <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="mt-3 p-3" v-if="state.isContactDataLoading === false && state.isAuthError === false && state.isContactDataLoadError === false"> | |||
| <View class="border shadow 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> | |||
| @@ -128,6 +128,7 @@ const goToContactEditPage = (item) => { | |||
| </View> | |||
| </View> | |||
| </View> | |||
| <View v-else></View> | |||
| <View v-if="!state.isContactDataLoading"> | |||
| <View v-if="state.isAuthError"> | |||
| @@ -161,7 +162,7 @@ const goToContactEditPage = (item) => { | |||
| <View v-else></View> | |||
| <View class="w-100 d-flex flex-wrap"> | |||
| <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="m-2 p-3 shadow-sm border" hover-class="card-hover-gray" @tap="goToContactEditPage(item)"> | |||
| <View class="d-flex justify-content-center"> | |||
| <NutBadge :value="item.isBlock?'阻止':''"> | |||
| <NutBadge :value="item.isVIP?'VIP':''"> | |||
| @@ -188,7 +189,7 @@ const goToContactEditPage = (item) => { | |||
| <View v-else></View> | |||
| <View class="w-100 d-flex flex-wrap"> | |||
| <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="m-2 p-3 shadow-sm border" hover-class="card-hover-gray" @tap="goToContactEditPage(item)"> | |||
| <View class="d-flex justify-content-center"> | |||
| <NutBadge :value="item.isBlock?'阻止':''"> | |||
| <NutBadge :value="item.isVIP?'VIP':''"> | |||
| @@ -7,6 +7,7 @@ import dayjs from "dayjs"; | |||
| import {PassRecords} from "./pass-records"; | |||
| export interface ContactEditLog { | |||
| userId: string | |||
| userName: string | |||
| method: string | |||
| methodDesc: string | |||
| @@ -22,6 +23,7 @@ export const useContactEditLogsStore = defineStore('contact-edit-logs', () => { | |||
| const GQL_QUERY_CONTACT_EDIT_LOGS = gql` | |||
| query ($visitorId: ID, $limit: Int ) { | |||
| visitorLogs(visitorId: $visitorId, limit: $limit) { | |||
| userId | |||
| userName | |||
| param | |||
| method | |||
| @@ -44,6 +46,7 @@ export const useContactEditLogsStore = defineStore('contact-edit-logs', () => { | |||
| for (let item of result.data.visitorLogs) { | |||
| // console.log(item) | |||
| items.push({ | |||
| userId: item.userId, | |||
| userName: item.userName, | |||
| method: item.method, | |||
| methodDesc: methodToDesc(item.method), | |||
| @@ -64,9 +67,11 @@ export const useContactEditLogsStore = defineStore('contact-edit-logs', () => { | |||
| } | |||
| function methodToDesc(method: string) { | |||
| if (method === "updateVisitor") return "更新了" | |||
| else if (method === "createVisitor") return "创建了" | |||
| else if (method === "deleteVisitor") return "删除了" | |||
| if (method === "updateVisitor" || method === "updateVisitRecord") return "更新了" | |||
| else if (method === "createVisitor" || method === "createVisitRecord") return "创建了" | |||
| else if (method === "deleteVisitor" || method === "deleteVisitRecord") return "删除了" | |||
| else if (method === "sendSms") return "发送了 短信" | |||
| else return method | |||
| } | |||
| @@ -77,6 +82,10 @@ export const useContactEditLogsStore = defineStore('contact-edit-logs', () => { | |||
| else if (params.indexOf('isBlock":false') !== -1) return "取消阻止状态" | |||
| else return "访客信息" | |||
| } | |||
| else if( method === "updateVisitRecord" || method === "createVisitRecord" || method === "deleteVisitRecord") { | |||
| return "访客邀约信息" | |||
| } | |||
| else if (method === "sendSms") return "" | |||
| else if (method === "deleteVisitor" ) { | |||
| return "此访客" | |||
| } | |||