<route lang="json"> { "style": { "navigationBarTitleText": "全部设计师", "navigationBarBackgroundColor": "#fff" } } </route> <script setup lang="ts"> import Card from '@/components/card.vue' import DataForm from '@/components/data-form.vue' import PageHelperEvo from '@/components/page-helper-evo.vue' import { createFollowUp, focusOrCancel, getDesigners } from '../../../core/libs/agent-requests' import { filterIcon } from '@designer-hub/assets/src/svgs' import { Designer } from '@designer-hub/app/src/core/libs/models' import { requestToast } from '@designer-hub/app/src/core/utils/common' import { ComponentExposed } from 'vue-component-type-helpers' import { useFollowUp } from '../../../composables/followUp' import SectionHeading from '@designer-hub/app/src/components/section-heading.vue' import { useMemberLevelsStore } from '../../../store/member-levles' import { storeToRefs } from 'pinia' import { useUserStore } from '../../../store' import link from '@designer-hub/assets/src/libs/assets/link' import { beforeNow } from '@/utils/date-util' import { NetImages } from '@/core/libs/enums' import AMapWX from '@/pages/common/amap-wx.130' import { addUnit } from 'wot-design-uni/components/common/util' const action = ref(`${import.meta.env.VITE_SERVER_BASEURL}/app-api/infra/file/upload`) const userStore = useUserStore() const { userInfo } = storeToRefs(userStore) const searchText = ref('') const publishState = ref(false) const filterState = ref(false) const fileList = ref<string[]>([]) const pageHelperRef = ref<ComponentExposed<typeof PageHelperEvo>>() const schemaTypeOnlineRef = ref<ComponentExposed<typeof DataForm>>() const { schema, rules, schemaTypeOnline } = useFollowUp() const memberLevelsStore = useMemberLevelsStore() const { memberLevels } = storeToRefs(memberLevelsStore) const { getMemberLevelLogo } = memberLevelsStore const followUpForm = ref({ stylistId: '', followType: '1', followTime: new Date().getTime(), address: { latitude: 0, longitude: 0, address: '', }, imgUrl: '', }) const followUpFormRef = ref({}) const currentAddress = ref<any>([]) // 地图实例化 const AmapFun = new AMapWX.AMapWX({ key: 'efde483f8801a09d3c5db032556e6593' }) const wxGetAddress = (longitude: number, latitude: number) => { return new Promise((resolve, reject) => { AmapFun.getRegeo({ location: `${longitude},${latitude}`, success: (res: any) => { resolve(res) }, fail: (err: any) => { reject(err) }, }) }) } const getCurrentLocation = () => { console.log('点击地址') uni.getLocation({ type: 'gcj02', success: async (success: any) => { currentAddress.value = await wxGetAddress(success?.longitude, success?.latitude) const { name, latitude, longitude } = currentAddress.value[0] followUpForm.value.address.address = name followUpForm.value.address.latitude = latitude followUpForm.value.address.longitude = longitude console.log('提交信息:::', followUpForm.value) }, fail: (err) => { console.log('获取地址失败', err) if (err.errCode === 1005 || err.errCode === 10001) { console.log('位置权限未授权') uni.authorize({ scope: 'scope.userLocation', success: () => { uni.getLocation({ type: 'gcj02', success: async (success) => { console.log('授权后,获取地址', success) currentAddress.value = await wxGetAddress(success?.longitude, success?.latitude) const { name, latitude, longitude } = currentAddress.value[0] followUpForm.value.address.address = name followUpForm.value.address.latitude = latitude followUpForm.value.address.longitude = longitude console.log('提交信息:::', followUpForm.value) }, fail: function (err) { console.log('获取位置失败:', err) }, }) }, fail: function () { console.log('用户拒绝授权,不再提示') // 用户拒绝授权,可以选择记录下来,不再提示 uni.showToast({ title: '您拒绝了位置授权', icon: 'none', duration: 2000, }) // 可以引导用户去设置中授权 uni.showModal({ title: '提示', content: '请在系统设置中打开定位服务权限', success: function (modalRes) { if (modalRes.confirm) { uni.openSetting() } }, }) }, }) } }, }) } const filterQuery = ref<{ tags: any[] levels: any[] retryStatus: any[] minPoints?: string maxPoints?: string brokerId?: string recommend?: boolean name?: string }>({ tags: [], levels: [], retryStatus: [], brokerId: String(userInfo.value.userId), }) const query = ref({}) const searchFocus = async () => { console.log('focus') } const searchBlur = async () => { console.log('Blur') query.value = { ...filterQuery.value, tags: filterQuery.value.tags.join(','), levels: filterQuery.value.levels.join(','), } await pageHelperRef.value?.refresh() } const search = async () => { console.log('search') query.value = { ...filterQuery.value, tags: filterQuery.value.tags.join(','), levels: filterQuery.value.levels.join(','), } await pageHelperRef.value?.refresh() } const cancelSearch = () => { console.log('cancel') } const searchChange = (e: any) => { console.log(e) } const toDetail = async (designer: any) => { await uni.navigateTo({ url: '/pages/agent/designer/detail' + '?id=' + designer.id }) } const callPhone = (phoneNumber: string) => { uni.makePhoneCall({ phoneNumber, }) } const filterData = () => { filterState.value = true } const handleImportant = async (designer: Designer) => { const { code } = await requestToast( () => focusOrCancel({ brokerId: Number(userInfo.value.userId), userId: Number(designer.id), }), { success: true, successTitle: '操作成功', }, ) if (code === 0) { await pageHelperRef.value?.refresh() } } const saveFollowUp = (item: any) => { console.log(item) schema.value.stylistId.props.columns = [{ value: item.id, label: item.name }] followUpForm.value.stylistId = item.id followUpForm.value.address.address = '' followUpForm.value.address.latitude = 0 followUpForm.value.address.longitude = 0 followUpForm.value.imgUrl = '' publishState.value = true } const createFollowUpSubmit = async () => { const { valid } = await followUpFormRef.value.validate() if (!valid) { return } if (fileList.value.length) { const temp: string[] = [] fileList.value.forEach((each: any) => { temp.push(JSON.parse(each.response).data) }) followUpForm.value.imgUrl = temp.join(',') } if (!followUpForm.value.imgUrl) { uni.showToast({ icon: 'none', title: '请上传图片' }) return false } if (!followUpForm.value.address && followUpForm.value.followType === '1') { uni.showToast({ icon: 'none', title: '请刷新定位' }) return false } const { code } = await requestToast(() => createFollowUp(followUpForm.value), { success: true, successTitle: '跟进成功', }) if (code === 0) { publishState.value = false } } const handleChange = ({ fileList: files }) => { fileList.value = files console.log(fileList.value) } const handleSubmit = () => { query.value = { ...filterQuery.value, tags: filterQuery.value.tags ? filterQuery.value.tags.join(',') : '', levels: filterQuery.value.levels ? filterQuery.value.levels.join(',') : '', retryStatus: filterQuery.value.retryStatus ? filterQuery.value.retryStatus.join(',') : '', } filterState.value = false } const handleReset = () => { filterQuery.value = { tags: [], levels: [], brokerId: String(userInfo.value.userId) } query.value = {} } onLoad(async (params: { title?: string; filter?: string; tags?: string }) => { if (params.title) { uni.setNavigationBarTitle({ title: params.title }) } if (params.filter) { const filter = JSON.parse(params.filter) as { tags: '' } } if (params.tags) { filterQuery.value.tags = params.tags.split(',') } query.value = { ...filterQuery.value, tags: filterQuery.value.tags.join(','), levels: filterQuery.value.levels.join(','), } }) </script> <template> <view class="flex-grow"> <PageHelperEvo ref="pageHelperRef" :request="getDesigners" :query="query"> <template #top> <div class="flex items-center justify-between bg-white pr-3.5"> <div class="flex-1"> <wd-search v-model="filterQuery.name" placeholder="输入设计师姓名模糊搜索" @focus="searchFocus" @blur="searchBlur" @search="search" @cancel="cancelSearch" @change="searchChange" hide-cancel /> </div> <wd-img :src="filterIcon" width="22px" height="22px" @click="filterData"></wd-img> </div> </template> <template #default="{ source }"> <div class="p-3.5 gap-3.5 flex flex-col"> <template v-for="(it, i) in source?.list" :key="i"> <Card> <div class="items-center" @click="toDetail(it)"> <div class=""> <div class="flex items-center"> <div class="w-[55px] h-[55px] bg-neutral-100 rounded-full mr-2 flex items-center justify-center relative" > <wd-img width="100%" height="100%" round :src="it.avatar || NetImages.DefaultAvatar" ></wd-img> <div v-if="it.retryStatus === 1" class="absolute right-0 bottom--1"> <wd-img width="14" height="14" :src="link"></wd-img> </div> </div> <div class="flex flex-col flex-1"> <div class="flex-row flex items-center justify-between w-full"> <div class="flex-row flex items-center"> <div class="text-black/90 text-base font-normal font-['PingFang_SC']"> <!-- 苏小萌 --> {{ it.name }} </div> <div class="h-4 rounded-[20px] justify-start items-center inline-flex flex-row ml-[9px]" > <div v-if="it.recommend" class="text-[10px] bg-[#fff3e4] px-[4px] py-[4px] c-[#f2a64f] rounded-[3px]" > 推荐设计师 </div> <wd-img v-if="it.levelId" width="63" height="18.6" custom-class="ml-[9px]" :src="getMemberLevelLogo(Number(it.levelId))" ></wd-img> </div> </div> <!-- <div--> <!-- class="text-black/60 text-xs font-normal font-['PingFang_SC'] leading-snug flex items-center"--> <!-- @click.stop="toHomePage(it.id)"--> <!-- >--> <!-- <div>个人主页</div>--> <!-- <wd-img width="13" height="13" :src="rightArrowIcon"></wd-img>--> <!-- </div>--> </div> <div class="flex items-center gap-2 mt-[18px]"> <div class="text-black/30 text-xs font-normal font-['PingFang_SC'] leading-none" > {{ it.accessTime ? `${beforeNow(new Date(it.accessTime))}跟进` : '' }} </div> <div class="bg-[#eeeeee] w-[2px] h-[10px]"></div> <div class="text-black/30 text-xs font-normal font-['PingFang_SC'] leading-none" > 积分:{{ it.points || 0 }} </div> </div> </div> </div> </div> <div class="row-start-2 col-start-2 col-end-4"> <div class="flex items-center w-full mt-[20px]" style="justify-content: flex-start" > <div v-if="!it.followUp30Days" class="flex items-center justify-center w-[30%]"> <div class="w-2 h-2 bg-[#89f4e2] rounded-full mr-[7px]"></div> <div class="text-black/90 text-xs font-normal font-['PingFang_SC'] leading-snug" > 30天未跟进 </div> </div> <div class="flex items-center justify-center w-[35%]" v-if="!it.generatePoints60Days" > <div class="w-2 h-2 bg-[#ffb96a] rounded-full mr-[7px]"></div> <div class="text-black/90 text-xs font-normal font-['PingFang_SC'] leading-snug" > 60天未产生积分 </div> </div> <div class="flex items-center justify-center w-[35%]" v-if="!it.expendPoints60Days" > <div class="w-2 h-2 bg-[#c493ff] rounded-full mr-[7px]"></div> <div class="text-black/90 text-xs font-normal font-['PingFang_SC'] leading-snug" > 60天未消耗积分 </div> </div> </div> </div> <div class="row-start-5 col-start-2 col-end-4 flex items-center mt-[26px] justify-around" > <div v-if="!it.focus" class="px-3 py-1.5 rounded-[30px] border border-solid border-[#ff2d2d] justify-center items-center gap-1 flex" @click.stop="handleImportant(it)" > <!-- <span style="color: #ff2d2d" class="flex items-center">+</span> --> <wd-icon name="add" color="#ff2d2d" size="10"></wd-icon> <div class="text-[#ff2d2d] text-xs font-normal font-['PingFang_SC'] leading-none" > 重点跟进 </div> </div> <div v-else class="px-3 py-1.5 rounded-[30px] border border-solid border-[#dcdcdc] justify-center items-center gap-1 inline-flex" @click.stop="handleImportant(it)" > <!-- 对号图标 --> <wd-icon name="check-bold" color="#8b8b8b" size="14"></wd-icon> <div class="text-[#8b8b8b] text-xs font-normal font-['PingFang_SC'] leading-none" > 已重点跟进 </div> </div> <div class="px-5 py-1 bg-[#e1ecff] rounded-[30px] border border-[#2357e9] justify-center items-center gap-1 inline-flex" @click.stop="callPhone(it.mobile)" > <div class="text-center text-[#2357e9] text-sm font-normal font-['PingFang_SC'] leading-normal" > 打电话 </div> </div> <div class="px-5 py-1 bg-[#0052d9] rounded-[30px] justify-center items-center gap-1 inline-flex" > <div class="text-center text-white text-sm font-normal font-['PingFang_SC'] leading-normal" @click.stop="saveFollowUp(it)" > 写跟进 </div> </div> </div> </div> </Card> </template> </div> </template> </PageHelperEvo> </view> <wd-action-sheet v-model="publishState" title="创建跟进" @close="publishState = false"> <view class="flex flex-col p-4 overflow-y-auto h-[calc(75vh)]"> <div> <DataForm ref="followUpFormRef" :schema="schema" v-model="followUpForm" :rules="rules" direction="horizontal" ></DataForm> <!-- 根据 followType 值 区分 线下 线上 --> <template v-if="followUpForm.followType === '1'"> <div class="grid mb-4 items-start" :style="{ 'grid-template-columns': `${addUnit(64)} auto` }" > <label class="text-sm font-normal leading-relaxed text-black/60 h-10 flex items-center"> <span class="text-[#ef4343] text-base font-normal font-['PingFang_SC'] leading-normal visible" > * </span> 地址 </label> <div class="wd-input h-[40px] lh-[40px] flex justify-between px-[20px]"> <div> {{ !followUpForm.address.address ? '点击获取当前位置' : followUpForm.address.address }} </div> <wd-icon name="refresh" size="14px" @click="getCurrentLocation"></wd-icon> </div> </div> </template> <div class="grid mb-4 items-start" :style="{ 'grid-template-columns': `${addUnit(64)} auto` }" > <label class="text-sm font-normal leading-relaxed text-black/60 h-10 flex items-center"> <span class="text-[#ef4343] text-base font-normal font-['PingFang_SC'] leading-normal visible" > * </span> 图片 </label> <wd-upload :file-list="fileList" image-mode="aspectFill" accept="media" :action="action" :multiple="true" :limit="9" @change="handleChange" ></wd-upload> </div> </div> <div><wd-button block :round="false" @click="createFollowUpSubmit">提交</wd-button></div> </view> </wd-action-sheet> <!-- 筛选action-sheet --> <wd-action-sheet v-model="filterState" title="筛选" @close="filterState = false"> <view class="flex flex-col p-4 overflow-y-auto h-[calc(75vh)]"> <SectionHeading title="标签"></SectionHeading> <wd-checkbox-group shape="button" v-model="filterQuery.tags"> <template v-for="(tag, index) in [ // { label: '全部', value: '' }, { label: '重点跟进', value: '1' }, { label: '本月新增', value: '2' }, { label: '超过30天未跟进', value: '3' }, { label: '超过60天未产生积分', value: '4' }, { label: '超过60天未消耗积分', value: '5' }, { label: '未成交过', value: '6' }, ]" :key="index" > <wd-checkbox custom-class="w-50%!" :model-value="tag.value"> {{ tag.label }} </wd-checkbox> </template> </wd-checkbox-group> <SectionHeading title="会员等级"></SectionHeading> <wd-checkbox-group shape="button" v-model="filterQuery.levels"> <template v-for="(tag, index) in memberLevels.map((it) => ({ label: it.memberLevelName, value: it.id, }))" :key="index" > <wd-checkbox custom-class="w-50%!" :model-value="tag.value">{{ tag.label }}</wd-checkbox> </template> </wd-checkbox-group> <SectionHeading title="积分区间"></SectionHeading> <div class="flex items-center justify-between py-4"> <wd-input v-model="filterQuery.minPoints" custom-class="h-10 bg-[#f5f7f9]!" no-border ></wd-input> <div class="w-4 h-.25 bg-black/35"></div> <wd-input v-model="filterQuery.maxPoints" custom-class="h-10 bg-[#f5f7f9]!" no-border ></wd-input> </div> <SectionHeading title="绑定关系"></SectionHeading> <wd-checkbox-group shape="button" v-model="filterQuery.retryStatus"> <template v-for="(tag, index) in [ // { label: '全部', value: '' }, { label: '弱绑定', value: '0' }, { label: '强绑定', value: '1' }, ]" :key="index" > <wd-checkbox custom-class="w-50%!" :model-value="tag.value"> {{ tag.label }} </wd-checkbox> </template> </wd-checkbox-group> <SectionHeading title="推荐设计师"></SectionHeading> <wd-checkbox-group shape="button" v-model="filterQuery.recommend"> <template v-for="(tag, index) in [ // { label: '全部', value: '' }, { label: '否', value: false }, { label: '是', value: true }, ]" :key="index" > <wd-checkbox custom-class="w-50%!" :model-value="tag.value"> {{ tag.label }} </wd-checkbox> </template> </wd-checkbox-group> <div class="flex gap-4 pt-[10px]"> <div class="flex-1"> <wd-button block :round="false" @click="handleReset">重置</wd-button> </div> <div class="flex-1"> <wd-button block :round="false" @click="handleSubmit">提交</wd-button> </div> </div> </view> </wd-action-sheet> </template> <style scoped lang="scss"> .filter-item { @apply w-[168px] h-10 bg-[#f5f7f9] rounded-lg flex items-center justify-center; &-text { @apply text-black/90 text-sm font-normal font-['PingFang_SC'] leading-none; } } </style>