| @@ -1,57 +0,0 @@ | |||||
| <template> | |||||
| <View/> | |||||
| </template> | |||||
| <script> | |||||
| import { useAuthStore } from '../stores/auth' | |||||
| import {NavigateType, Router} from "tarojs-router-next"; | |||||
| import {Route, Session} from "../utils"; | |||||
| import Taro from "@tarojs/taro"; | |||||
| export default { | |||||
| setup() { | |||||
| const auth = useAuthStore() | |||||
| if (Session.has('expires_in_timestamp') | |||||
| && Session.get('expires_in_timestamp') >= new Date().getTime()+60*60*1000) { | |||||
| auth.isLoggedIn = true; | |||||
| console.log(Session.all()) | |||||
| } else { | |||||
| console.log(SERVER_URL+ '/system/api/weapp/login') | |||||
| Taro.login({ | |||||
| success: function (res) { | |||||
| Taro.request({ | |||||
| url: SERVER_URL + '/system/api/weapp/login', | |||||
| data: {code: res.code}, | |||||
| success: result => { | |||||
| // console.log(result) | |||||
| for (const [key, value] of Object.entries(result.data.data)) { | |||||
| Session.set(key, value); | |||||
| if (key === 'expires_in') { | |||||
| Session.set('expires_in_timestamp', new Date().getTime() + (value * 60 * 1000)) | |||||
| } | |||||
| } | |||||
| console.log(Session.all()) | |||||
| auth.isLoggedIn = true | |||||
| if (!Session.has('access_token')) { | |||||
| if (Taro.getCurrentPages().length > 0) { | |||||
| Router.navigate( {url: '/pages/login/index'}, { data: {prevPage:"/" + Taro.getCurrentPages()[0].route}, type: NavigateType.reLaunch } ) | |||||
| } | |||||
| else { | |||||
| Router.navigate( {url: '/pages/login/index'}, { type: NavigateType.reLaunch } ) | |||||
| } | |||||
| } | |||||
| } | |||||
| }) | |||||
| }, | |||||
| fail: function (res) { | |||||
| console.log(res) | |||||
| } | |||||
| }) | |||||
| } | |||||
| return {} | |||||
| } | |||||
| } | |||||
| </script> | |||||
| @@ -5,22 +5,24 @@ import { Router } from 'tarojs-router-next' | |||||
| import { reactive, ref } from 'vue'; | import { reactive, ref } from 'vue'; | ||||
| import {BjxHelper} from "../../utils"; | |||||
| import {useContactsStore} from "../../stores/contacts"; | |||||
| const props = defineProps(['data']) | const props = defineProps(['data']) | ||||
| // const isSuggestionsShow = ref(false) | |||||
| const contacts = useContactsStore() | |||||
| const state = reactive({ | const state = reactive({ | ||||
| isSuggestionsShow: false, | |||||
| name: props.data ? props.data.name : '', | |||||
| company: props.data ? props.data.company : '', | |||||
| avatar: props.data ? props.data.avatar : '', | |||||
| isNickNameSuggestionsShow: false, | |||||
| contactData: props.data ? props.data : contacts.getEmptyContactData(), | |||||
| newAvatar: '', | |||||
| number: '', | number: '', | ||||
| isFlexVisit: props.data ? props.data.isFlexVisit : true, | |||||
| // lastName: BjxHelper.getBJXFirstChar(name), | |||||
| date: '', | date: '', | ||||
| isDateSelectorVisible: false | isDateSelectorVisible: false | ||||
| }); | }); | ||||
| const openSwitch = (param) => { | const openSwitch = (param) => { | ||||
| state[`${param}`] = true; | state[`${param}`] = true; | ||||
| }; | }; | ||||
| @@ -32,7 +34,7 @@ const setChooseValue = (param) => { | |||||
| }; | }; | ||||
| function setShowSuggestions(isShow: Boolean) { | function setShowSuggestions(isShow: Boolean) { | ||||
| state.isSuggestionsShow = isShow | |||||
| state.isNickNameSuggestionsShow = isShow | |||||
| } | } | ||||
| const updateFacePhoto = () => { | const updateFacePhoto = () => { | ||||
| @@ -41,12 +43,19 @@ const updateFacePhoto = () => { | |||||
| sizeType: ['compressed'], // 可以指定是原图还是压缩图,默认二者都有 | sizeType: ['compressed'], // 可以指定是原图还是压缩图,默认二者都有 | ||||
| success: function (res) { | success: function (res) { | ||||
| // 返回选定照片的本地文件路径列表,tempFilePath可以作为img标签的src属性显示图片 | // 返回选定照片的本地文件路径列表,tempFilePath可以作为img标签的src属性显示图片 | ||||
| state.avatar = res.tempFilePaths | |||||
| console.log(state.avatar) | |||||
| state.newAvatar = res.tempFilePaths | |||||
| console.log(state.newAvatar) | |||||
| }, | }, | ||||
| }) | }) | ||||
| } | } | ||||
| const updateNickName = (value) => { | |||||
| if (value === null) { | |||||
| state.isNickNameSuggestionsShow = false | |||||
| } | |||||
| else { | |||||
| state.contactData.nickName = BjxHelper.getBJXFirstChar(state.contactData.name) + value; | |||||
| } | |||||
| } | |||||
| </script> | </script> | ||||
| <template> | <template> | ||||
| @@ -56,52 +65,59 @@ const updateFacePhoto = () => { | |||||
| <view> | <view> | ||||
| <view class="d-flex justify-content-center"> | <view class="d-flex justify-content-center"> | ||||
| <nut-avatar size="192" @click="updateFacePhoto" class="overflow-hidden"> | <nut-avatar size="192" @click="updateFacePhoto" class="overflow-hidden"> | ||||
| <img v-if="state.avatar" :src="state.avatar" /> | |||||
| <img v-if="state.contactData.avatar" :src="state.contactData.avatar" /> | |||||
| <view v-else class="d-flex align-items-center justify-content-center h-100 w-100"> | <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-6x"></Text> | ||||
| </view> | </view> | ||||
| </nut-avatar> | </nut-avatar> | ||||
| </view> | </view> | ||||
| <view class="d-flex justify-content-center pt-3"> | <view class="d-flex justify-content-center pt-3"> | ||||
| <view class="btn btn-secondary badge-pill" @tap="updateFacePhoto">{{state.avatar?'更换':'添加'}}面部识别照片</view> | |||||
| <view class="btn btn-secondary badge-pill" @tap="updateFacePhoto">{{state.contactData.avatar || state.newAvatar ?'更换':'添加'}}面部识别照片</view> | |||||
| </view> | </view> | ||||
| </view> | </view> | ||||
| </nut-form-item> | </nut-form-item> | ||||
| <nut-form-item label="姓名"> | <nut-form-item label="姓名"> | ||||
| <nut-input v-model="state.name" class="nut-input-text" /> | |||||
| <nut-input v-model="state.contactData.name" class="nut-input-text" /> | |||||
| </nut-form-item> | </nut-form-item> | ||||
| <nut-form-item label="称呼"> | <nut-form-item label="称呼"> | ||||
| <nut-input v-model="state.nickName" class="nut-input-text" @focus="setShowSuggestions(true)" @blur="setShowSuggestions(false)" /> | |||||
| <nut-input v-model="state.contactData.nickName" @change="state.isNickNameSuggestionsShow = false" class="nut-input-text" @focus="setShowSuggestions(true)" @blur="setShowSuggestions(false)" /> | |||||
| </nut-form-item> | </nut-form-item> | ||||
| <view class="suggestions" v-show="state.isSuggestionsShow"> | |||||
| <view>先生</view> | |||||
| <view>女士</view> | |||||
| <view>小姐</view> | |||||
| <view><Text className="fas fa-info-circle text-info"/> 您可以从列表中选择或直接填写</view> | |||||
| <view class="suggestions" style="background-color: #EEE" v-show="state.isNickNameSuggestionsShow"> | |||||
| <view @tap="updateNickName('先生')">{{BjxHelper.getBJXFirstChar(state.contactData.name)}}先生</view> | |||||
| <view @tap="updateNickName('女士')">{{BjxHelper.getBJXFirstChar(state.contactData.name)}}女士</view> | |||||
| <view @tap="updateNickName('小姐')">{{BjxHelper.getBJXFirstChar(state.contactData.name)}}小姐</view> | |||||
| <view @tap="updateNickName(null)"> | |||||
| <View class="d-flex"> | |||||
| <View class="flex-grow-1"> | |||||
| <Text className="fas fa-info-circle text-info"/> 您可以从列表中选择或直接填写 | |||||
| </View> | |||||
| <View class="text-primary pr-3">关闭</View> | |||||
| </View> | |||||
| </view> | |||||
| </view> | </view> | ||||
| <nut-form-item label="电话"> | <nut-form-item label="电话"> | ||||
| <nut-input v-model="state.phone" class="nut-input-text" /> | |||||
| <nut-input v-model="state.contactData.phone" class="nut-input-text" /> | |||||
| </nut-form-item> | </nut-form-item> | ||||
| <nut-form-item label="公司"> | <nut-form-item label="公司"> | ||||
| <nut-input v-model="state.company" class="nut-input-text" /> | |||||
| <nut-input v-model="state.contactData.company" class="nut-input-text" /> | |||||
| </nut-form-item> | </nut-form-item> | ||||
| <nut-form-item label="VIP"> | <nut-form-item label="VIP"> | ||||
| <view class="d-flex"> | <view class="d-flex"> | ||||
| <view class="flex-grow-1"></view> | <view class="flex-grow-1"></view> | ||||
| <nut-switch v-model="state.isVIP" active-color="blue" /> | |||||
| <nut-switch v-model="state.contactData.isVIP" active-color="blue" /> | |||||
| </view> | </view> | ||||
| </nut-form-item> | </nut-form-item> | ||||
| <nut-form-item label="可随时来访"> | <nut-form-item label="可随时来访"> | ||||
| <view class="d-flex"> | <view class="d-flex"> | ||||
| <view class="flex-grow-1"></view> | <view class="flex-grow-1"></view> | ||||
| <nut-switch v-model="state.isFlexVisit" active-color="blue" /> | |||||
| <nut-switch v-model="state.contactData.flexVisit" active-color="blue" /> | |||||
| </view> | </view> | ||||
| </nut-form-item> | </nut-form-item> | ||||
| <nut-form-item label="下次到访日期" v-show="!state.isFlexVisit"> | |||||
| <nut-form-item label="下次到访日期" v-show="!state.contactData.flexVisit"> | |||||
| <view class="d-flex calendar"> | <view class="d-flex calendar"> | ||||
| <view class="flex-grow-1"></view> | <view class="flex-grow-1"></view> | ||||
| <view class="text-dark" @tap="openSwitch('isDateSelectorVisible')"> | <view class="text-dark" @tap="openSwitch('isDateSelectorVisible')"> | ||||
| @@ -4,11 +4,12 @@ import { Router } from 'tarojs-router-next' | |||||
| import {BjxHelper, ScreenHelper} from "../../utils"; | import {BjxHelper, ScreenHelper} from "../../utils"; | ||||
| import "./index.scss"; | import "./index.scss"; | ||||
| import {ref} from "vue"; | import {ref} from "vue"; | ||||
| import {ContactData} from "../../stores/contacts"; | |||||
| const props = defineProps(['items']) | const props = defineProps(['items']) | ||||
| // const elevatorHeight = ref(ScreenHelper.getScreenHeight() - ScreenHelper.getStatusBarHeight() - ScreenHelper.getWindowBarHeight()) | // const elevatorHeight = ref(ScreenHelper.getScreenHeight() - ScreenHelper.getStatusBarHeight() - ScreenHelper.getWindowBarHeight()) | ||||
| const clickItem = (key: string, item: any) => { | |||||
| const clickItem = (key: string, item: ContactData) => { | |||||
| console.log(key); | console.log(key); | ||||
| console.log(item.name); | console.log(item.name); | ||||
| @@ -28,8 +29,9 @@ const clickItem = (key: string, item: any) => { | |||||
| <template #default="{ item }"> | <template #default="{ item }"> | ||||
| <view class="d-flex flex-row pb-2"> | <view class="d-flex flex-row pb-2"> | ||||
| <view class="pr-4 pt-2 h6"> | <view class="pr-4 pt-2 h6"> | ||||
| <nut-avatar size="normal" color="white" :bg-color="BjxHelper.mbString2RgbHex(item.name)"> | |||||
| <view v-if="BjxHelper.getBJXFirstChar(item.name)">{{BjxHelper.getBJXFirstChar(item.name)}}</view> | |||||
| <nut-avatar 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> | <view v-else> | ||||
| <Text className='fas fa-user fa-lg'/> | <Text className='fas fa-user fa-lg'/> | ||||
| </view> | </view> | ||||
| @@ -10,3 +10,8 @@ page { | |||||
| flex: 1; | flex: 1; | ||||
| overflow: scroll; | overflow: scroll; | ||||
| } | } | ||||
| .skeleton { | |||||
| flex: 1; | |||||
| overflow: hidden; | |||||
| } | |||||
| @@ -3,89 +3,53 @@ import Contact from "../../components/contact" | |||||
| import { Router } from 'tarojs-router-next' | import { Router } from 'tarojs-router-next' | ||||
| import {reactive, ref} from 'vue'; | import {reactive, ref} from 'vue'; | ||||
| import './index.scss' | import './index.scss' | ||||
| import { useContactsStore, ContactData } from '../../stores/contacts' | |||||
| import {gql} from "graphql-tag"; | import {gql} from "graphql-tag"; | ||||
| import {PinyinHelper, GQLRequest} from "../../utils"; | |||||
| interface ContactData { | |||||
| id: number | |||||
| name: string | |||||
| isVIP: boolean | |||||
| flexVisit: boolean | |||||
| isBlock: boolean | |||||
| nickName: string | |||||
| avatar: string | |||||
| phone: string | |||||
| company: string | |||||
| nextVisitDate?: string | |||||
| firstCharPinyin: string | |||||
| } | |||||
| import {PinyinHelper, GQLRequest, weappAuth} from "../../utils"; | |||||
| import {useAuthStore} from "../../stores/auth"; | |||||
| // defineExpose({ | |||||
| // loadContactsFromServer, | |||||
| // }); | |||||
| const state = reactive<{ | const state = reactive<{ | ||||
| isContactDataLoading: boolean; | isContactDataLoading: boolean; | ||||
| searchQuery: string; | searchQuery: string; | ||||
| contactFullList: ContactData[]; // Using the ContactData interface | |||||
| contactList: ContactData[]; // Generic type for now | |||||
| contactGroupedList: any[]; // Generic type for now | contactGroupedList: any[]; // Generic type for now | ||||
| }>({ | }>({ | ||||
| isContactDataLoading: true, | isContactDataLoading: true, | ||||
| searchQuery: "", | searchQuery: "", | ||||
| contactFullList: [], | |||||
| contactList: [], | |||||
| contactGroupedList: [], | contactGroupedList: [], | ||||
| }) | }) | ||||
| const contacts = useContactsStore() | |||||
| const val = ref(''); | |||||
| const GQL_QUERY_ALL_CONTACT = gql` | |||||
| query { | |||||
| visitors { | |||||
| id | |||||
| name | |||||
| nickname | |||||
| type | |||||
| visitorCompany | |||||
| avatar | |||||
| phone | |||||
| } | |||||
| } | |||||
| ` | |||||
| GQLRequest.query(GQL_QUERY_ALL_CONTACT, {}) | |||||
| .then( result => { | |||||
| // console.log(result) | |||||
| for(let item of result.data.visitors) { | |||||
| state.contactFullList.push({ | |||||
| id: item.id, | |||||
| name: item.name, | |||||
| nickName: item.nickname, | |||||
| avatar: item.avatar, | |||||
| company: item.visitorCompany, | |||||
| phone: item.phone, | |||||
| flexVisit: "", | |||||
| isBlock: "", | |||||
| isVIP: "", | |||||
| firstCharPinyin: PinyinHelper.getPinyinGroupName(item.name) | |||||
| } as ContactData) | |||||
| } | |||||
| console.log(state.contactFullList) | |||||
| state.isContactDataLoading = false | |||||
| state.contactList = state.contactFullList | |||||
| generateContactGroupList() | |||||
| }) | |||||
| asyncInit() | |||||
| async function asyncInit() { | |||||
| state.isContactDataLoading = true | |||||
| weappAuth() | |||||
| .then(r => { | |||||
| contacts.loadContactsFromServer().then( | |||||
| contactList => { | |||||
| console.log("contactList", contactList) | |||||
| generateContactGroupList(contactList) | |||||
| state.isContactDataLoading = false | |||||
| }) | |||||
| }) | |||||
| } | |||||
| const onSearchValueChange = (value) => { | const onSearchValueChange = (value) => { | ||||
| state.contactList = [] | |||||
| for(const item of state.contactFullList) { | |||||
| if (item.name.match(value)) { | |||||
| state.contactList.push(item) | |||||
| } | |||||
| } | |||||
| generateContactGroupList() | |||||
| console.log(state.contactList) | |||||
| let contactList = contacts.searchContacts(value) | |||||
| generateContactGroupList(contactList) | |||||
| // console.log(state.contactGroupedList) | |||||
| } | } | ||||
| const generateContactGroupList = () => { | |||||
| const generateContactGroupList = (contactList: ContactData[]) => { | |||||
| let contactGroupedList = [] | let contactGroupedList = [] | ||||
| for (const item of state.contactList) { | |||||
| for (const item of contactList) { | |||||
| const firstChar = item.firstCharPinyin; | const firstChar = item.firstCharPinyin; | ||||
| if (!contactGroupedList[firstChar]) { | if (!contactGroupedList[firstChar]) { | ||||
| contactGroupedList[firstChar] = { title: firstChar, list: [] }; | contactGroupedList[firstChar] = { title: firstChar, list: [] }; | ||||
| @@ -125,9 +89,7 @@ const generateContactGroupList = () => { | |||||
| <Contact :items="state.contactGroupedList"/> | <Contact :items="state.contactGroupedList"/> | ||||
| </view> | </view> | ||||
| <View v-else class="d-flex flex-column"> | <View v-else class="d-flex flex-column"> | ||||
| <NutSkeleton class="p-3" height="15px" animated avatar avatar-size="45px" row="1"/> | |||||
| <NutSkeleton class="p-3" height="15px" animated avatar avatar-size="45px" row="1"/> | |||||
| <NutSkeleton class="p-3" height="15px" animated avatar avatar-size="45px" row="1"/> | |||||
| <NutSkeleton v-for="_ in 10" class="p-3" height="15px" animated avatar avatar-size="50px" row="1"/> | |||||
| </View> | </View> | ||||
| </view> | </view> | ||||
| @@ -11,4 +11,8 @@ page { | |||||
| overflow: scroll; | overflow: scroll; | ||||
| } | } | ||||
| .skeleton { | |||||
| flex: 1; | |||||
| overflow: hidden; | |||||
| } | |||||
| @@ -4,15 +4,24 @@ import { View, Text } from '@tarojs/components' | |||||
| import './index.scss' | import './index.scss' | ||||
| import Counter from '../../components/Counter.vue' | import Counter from '../../components/Counter.vue' | ||||
| import Auth from "../../components/Auth" | |||||
| import { ref } from 'vue' | import { ref } from 'vue' | ||||
| import {useDidShow, useLoad} from "@tarojs/taro"; | 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"; | |||||
| const state = reactive<{ | |||||
| isPageDataLoading: boolean, | |||||
| }>({ | |||||
| isPageDataLoading: true, | |||||
| }) | |||||
| asyncInit() | |||||
| async function asyncInit() { | |||||
| await weappAuth() | |||||
| state.isPageDataLoading = false | |||||
| } | |||||
| // | // | ||||
| // | // | ||||
| // const show = ref(false); | // const show = ref(false); | ||||
| @@ -43,7 +52,6 @@ import Notification from "../../components/notification"; | |||||
| <template> | <template> | ||||
| <Auth/> | |||||
| <!-- <view class="h-25 bg-danger">22222222</view>--> | <!-- <view class="h-25 bg-danger">22222222</view>--> | ||||
| <view class="h-100 d-flex flex-column"> | <view class="h-100 d-flex flex-column"> | ||||
| <BackgroundBasic/> | <BackgroundBasic/> | ||||
| @@ -60,7 +68,7 @@ import Notification from "../../components/notification"; | |||||
| </View> | </View> | ||||
| </View> | </View> | ||||
| <view class="scroll"> | |||||
| <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="小明科技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="true" pass-date="2023-12-25" pass-time="22:22:22" today-pass-time='[]'/> | ||||
| @@ -87,6 +95,9 @@ import Notification from "../../components/notification"; | |||||
| </view> | </view> | ||||
| </Scroll-View> | </Scroll-View> | ||||
| </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> | ||||
| @@ -89,7 +89,7 @@ const getPhoneNumber = (e) => { | |||||
| 服务器错误,请稍后重试 | 服务器错误,请稍后重试 | ||||
| </View> | </View> | ||||
| <View class="text-center text-black-50 p-5" v-show="isWxUserNotStaff"> | <View class="text-center text-black-50 p-5" v-show="isWxUserNotStaff"> | ||||
| 您还不是数字人产品中注册的企业员工,请联系您的公司管理员开通,或<ExternalLink class="text-primary" href="https://www.digimeta.com.cn/">与缔智元联系</ExternalLink>。 | |||||
| 您还不是数字人产品中注册的企业员工,请联系您的公司管理员开通,或<ExternalLink class="text-primary" href="https://www.digimeta.com.cn/contact">与缔智元联系</ExternalLink>。 | |||||
| </View> | </View> | ||||
| <View class="w-100 d-flex justify-content-center position-absolute"> | <View class="w-100 d-flex justify-content-center position-absolute"> | ||||
| <NutButton type="success" @click="clickPhoneNumberButton" openType='getPhoneNumber' @getphonenumber="getPhoneNumber"> | <NutButton type="success" @click="clickPhoneNumberButton" openType='getPhoneNumber' @getphonenumber="getPhoneNumber"> | ||||
| @@ -39,7 +39,7 @@ const onExitLoginButtonClicked = () => { | |||||
| } | } | ||||
| const onAboutUsClicked = () => { | const onAboutUsClicked = () => { | ||||
| Route.navigateTo({url: '/pages/web-view/index'}, {data: {href: "https://www.digimeta.com.cn/"}}) | |||||
| Route.navigateTo({url: '/pages/web-view/index'}, {data: {href: "https://www.digimeta.com.cn/contact"}}) | |||||
| console.log("onAboutUsClicked") | console.log("onAboutUsClicked") | ||||
| } | } | ||||
| @@ -0,0 +1,113 @@ | |||||
| // https://pinia.esm.dev/introduction.html | |||||
| import { defineStore } from 'pinia' | |||||
| import {GQLRequest, PinyinHelper, Session} from "../utils"; | |||||
| import Taro from "@tarojs/taro"; | |||||
| import {gql} from "graphql-tag"; | |||||
| import {Ref, ref, UnwrapRef} from "vue"; | |||||
| import {rejects} from "assert"; | |||||
| export interface ContactData { | |||||
| id?: number | |||||
| name: string | |||||
| isVIP: boolean | |||||
| flexVisit: boolean | |||||
| isBlock: boolean | |||||
| nickName: string | |||||
| avatar: string | |||||
| phone: string | |||||
| company: string | |||||
| nextVisitDate?: string | |||||
| firstCharPinyin: string | |||||
| } | |||||
| export const useContactsStore = defineStore('contacts', () => { | |||||
| const isContactsLoaded: Ref<UnwrapRef<boolean>> = ref(false) | |||||
| const allContacts: Ref<UnwrapRef<ContactData[]>> = ref([]) | |||||
| const GQL_QUERY_ALL_CONTACT = gql` | |||||
| query { | |||||
| visitors { | |||||
| id | |||||
| name | |||||
| nickname | |||||
| type | |||||
| visitorCompany | |||||
| avatar | |||||
| phone | |||||
| } | |||||
| } | |||||
| ` | |||||
| function loadContactsFromServer () { | |||||
| return new Promise((resolve, reject) => { | |||||
| isContactsLoaded.value = false | |||||
| const items: ContactData[] = [] | |||||
| return GQLRequest.query(GQL_QUERY_ALL_CONTACT, {}) | |||||
| .then(result => { | |||||
| if (result.code == 500) { | |||||
| reject("Network Error") | |||||
| } else { | |||||
| // console.log(result) | |||||
| for (let item of result.data.visitors) { | |||||
| items.push({ | |||||
| id: item.id, | |||||
| name: item.name, | |||||
| nickName: item.nickname, | |||||
| avatar: item.avatar, | |||||
| company: item.visitorCompany, | |||||
| phone: item.phone, | |||||
| flexVisit: "", | |||||
| isBlock: "", | |||||
| isVIP: "", | |||||
| firstCharPinyin: PinyinHelper.getPinyinGroupName(item.name) | |||||
| } as ContactData) | |||||
| } | |||||
| allContacts.value = items.sort((a, b) => a.firstCharPinyin.localeCompare(b.firstCharPinyin)); | |||||
| console.log(allContacts.value) | |||||
| isContactsLoaded.value = true | |||||
| resolve(allContacts.value) | |||||
| } | |||||
| }) | |||||
| }) | |||||
| } | |||||
| function searchContacts(searchQuery: string) { | |||||
| const searchedContacts: ContactData[] = [] | |||||
| for(const item of allContacts.value) { | |||||
| if (item.name.match(searchQuery)) { | |||||
| searchedContacts.push(item) | |||||
| } | |||||
| } | |||||
| return searchedContacts | |||||
| } | |||||
| function getEmptyContactData(): ContactData { | |||||
| return { | |||||
| id: undefined, | |||||
| name: "", | |||||
| isVIP: false, | |||||
| flexVisit: false, | |||||
| isBlock: false, | |||||
| nickName: "", | |||||
| avatar: "", | |||||
| phone: "", | |||||
| company: "", | |||||
| nextVisitDate: undefined, | |||||
| firstCharPinyin: "" | |||||
| } | |||||
| } | |||||
| return { allContacts , isContactsLoaded, loadContactsFromServer, searchContacts, getEmptyContactData} | |||||
| }) | |||||
| // You can even use a function (similar to a component setup()) to define a Store for more advanced use cases: | |||||
| // export const useCounterStore = defineStore('counter', () => { | |||||
| // const count = ref(0) | |||||
| // | |||||
| // function increment() { | |||||
| // count.value++ | |||||
| // } | |||||
| // | |||||
| // return {count, increment} | |||||
| // }) | |||||
| @@ -5,3 +5,4 @@ export { Route } from './route'; | |||||
| export { ScreenHelper } from './screen-helper'; | export { ScreenHelper } from './screen-helper'; | ||||
| export { Session } from './session' | export { Session } from './session' | ||||
| export { PinyinHelper } from './pinyin-helper' | export { PinyinHelper } from './pinyin-helper' | ||||
| export { weappAuth } from './weapp-auth' | |||||
| @@ -0,0 +1,54 @@ | |||||
| import {useAuthStore} from "../stores/auth"; | |||||
| import {Session} from "./session"; | |||||
| import Taro from "@tarojs/taro"; | |||||
| import {NavigateType, Router} from "tarojs-router-next"; | |||||
| export async function weappAuth () { | |||||
| return new Promise((resolve, reject) => { | |||||
| const auth = useAuthStore() | |||||
| if (Session.has('expires_in_timestamp') | |||||
| && Session.get('expires_in_timestamp') >= new Date().getTime() + 60 * 60 * 1000) { | |||||
| auth.isLoggedIn = true; | |||||
| console.log(Session.all()) | |||||
| resolve() | |||||
| } else { | |||||
| console.log(SERVER_URL + '/system/api/weapp/login') | |||||
| return Taro.login({ | |||||
| success: function (res) { | |||||
| Taro.request({ | |||||
| url: SERVER_URL + '/system/api/weapp/login', | |||||
| data: {code: res.code}, | |||||
| success: result => { | |||||
| if (result.data.code != 200) { | |||||
| reject(result) | |||||
| } else { | |||||
| for (const [key, value] of Object.entries(result.data.data)) { | |||||
| Session.set(key, value); | |||||
| if (key === 'expires_in') { | |||||
| Session.set('expires_in_timestamp', new Date().getTime() + (value * 60 * 1000)) | |||||
| } | |||||
| } | |||||
| console.log(Session.all()) | |||||
| auth.isLoggedIn = true | |||||
| resolve(result.data) | |||||
| if (!Session.has('access_token')) { | |||||
| if (Taro.getCurrentPages().length > 0) { | |||||
| Router.navigate({url: '/pages/login/index'}, { | |||||
| data: {prevPage: "/" + Taro.getCurrentPages()[0].route}, | |||||
| type: NavigateType.reLaunch | |||||
| }) | |||||
| } else { | |||||
| Router.navigate({url: '/pages/login/index'}, {type: NavigateType.reLaunch}) | |||||
| } | |||||
| } | |||||
| }}, | |||||
| fail: res => reject(res) | |||||
| }) | |||||
| }, | |||||
| fail: function (res) { | |||||
| console.log(res) | |||||
| } | |||||
| }) | |||||
| } | |||||
| }) | |||||
| } | |||||