【招生小程序】 新增# 1、对接电销的添加跟进、跟进记录列表;2、对接招生的添加进展;3、修复:刷新页面时底部导航栏显示首页路径错误;4、修复:点击底部导航栏中的总结模板后,再点击首页后,页面无反应的问题

master
kaeery 2025-02-27 19:40:00 +08:00
parent 2de7edf2d4
commit 30d61360fa
37 changed files with 515 additions and 425 deletions

View File

@ -6,7 +6,7 @@
# @FilePath: \chargingpile-uniapp\.env.development # @FilePath: \chargingpile-uniapp\.env.development
### ###
# 请求域名 # 请求域名
# VITE_APP_BASE_URL= 'http://120.77.216.5:8084' # VITE_APP_BASE_URL= 'http://192.168.111.5:8086'
VITE_APP_BASE_URL="http://192.168.111.98:8084" VITE_APP_BASE_URL="http://192.168.111.98:8086"
# VITE_APP_BASE_URL="https://wechat.szcxj2024.com" # VITE_APP_BASE_URL="https://124.220.209.120:8086"
# VITE_APP_SOCKET_URL = 'wss://front.yuegoodlife.com' # VITE_APP_SOCKET_URL = 'wss://front.yuegoodlife.com'

View File

@ -13,7 +13,7 @@ import { getAllDict } from '@/hooks/useDictOptions'
const appStore = useAppStore() const appStore = useAppStore()
const userStore = useUserStore() const userStore = useUserStore()
// getAllDict() getAllDict()
onLaunch(async () => { onLaunch(async () => {
appStore.getSystemInfoFn() appStore.getSystemInfoFn()
@ -24,28 +24,9 @@ onLaunch(async () => {
onShow(async () => { onShow(async () => {
const token = userStore.token const token = userStore.token
if (token) { if (token) {
await appStore.getConfig() // await appStore.getConfig()
appStore.updateLocation() appStore.updateLocation()
} else appStore.closeTimer() } else appStore.closeTimer()
}) })
// function genDates() {
// const weekdays = ['', '', '', '', '', '', '']
// const currentDate = new Date()
// const currentDayOfWeek = currentDate.getDay()
// let preDateOfWeek = currentDate.getDate() - 1
// const daysInRange = []
// for (let i = currentDayOfWeek; i <= weekdays.length - 1; i++) {
// preDateOfWeek = preDateOfWeek + 1
// daysInRange.push(preDateOfWeek)
// }
// let currentDateOfWeek = currentDate.getDate()
// for (let i = 0; i <= weekdays.length - daysInRange.length; i++) {
// currentDateOfWeek = currentDateOfWeek - 1
// daysInRange.unshift(currentDateOfWeek)
// }
// console.log(daysInRange)
// }
// genDates()
</script> </script>
<style lang="scss"></style> <style lang="scss"></style>

34
src/api/clue.ts 100644
View File

@ -0,0 +1,34 @@
import request from '@/utils/request'
// 新增线索
export function apiAddCluse(params: any) {
return request.post({ url: '/clue/add', data: params })
}
// 线索列表
export function apiCluseList(params: any) {
return request.get({ url: '/clue/list', data: params })
}
// 领取线索
export function apiGetCluse(params: any) {
return request.post({ url: '/clue/grabTheOrder', data: params })
}
// 线索详情
export function apiCluseDetail(params: any) {
return request.get({ url: '/clue/detail', data: params })
}
// 添加进展
export function apiAddCluseProgress(params: any) {
return request.post({ url: '/clue/addProgress', data: params })
}
// 转化完成
export function apiCompleteCluse(params: any) {
return request.post({ url: '/clue/conversionCompleted', data: params })
}
// 修改备注
export function apiEditRemark(params: any) {
return request.post({ url: '/clue/modifyRemarks', data: params })
}
// 修改跟进
export function apiEditClue(params: any) {
return request.post({ url: '/clue/edit', data: params })
}

View File

@ -2,10 +2,13 @@ import request from '@/utils/request'
// 所有字典以及字典下的所有数据 // 所有字典以及字典下的所有数据
export function dictAllDataList() { export function dictAllDataList() {
return request.get({ return request.get(
url: '/setting/dict/type/allDataList', {
data: { url: '/setting/dict/type/allDataList',
pageSize: 60 data: {
} pageSize: 60
}) }
},
{ isAuth: true }
)
} }

View File

@ -193,7 +193,7 @@ async function handleLogin(e) {
scene: LoginTypeEnum.MNP, scene: LoginTypeEnum.MNP,
iv: e.detail.iv, iv: e.detail.iv,
encryptedData: e.detail.encryptedData, encryptedData: e.detail.encryptedData,
channel: ChannelEnum.STAFF_PLATFORM channel: ChannelEnum.USER_PLATFORM
}) })
// //
loginData.value = data loginData.value = data
@ -228,14 +228,14 @@ async function loginHandle(data: any) {
// //
await userStore.getUser() await userStore.getUser()
// //
await appStore.getConfig() // await appStore.getConfig()
// //
uni.$u.toast('登录成功') uni.$u.toast('登录成功')
// //
uni.hideLoading() uni.hideLoading()
appStore.setFirstLogin(true) // appStore.setFirstLogin(true)
const { userInfo } = userStore const { userInfo } = userStore
if (userInfo.roles?.length > 1) { if (userInfo.roles?.length > 1) {
uni.redirectTo({ uni.redirectTo({

View File

@ -2,9 +2,9 @@
<view class="bg-[#FAFAFE]"> <view class="bg-[#FAFAFE]">
<view class="bg-white px-[32rpx]"> <view class="bg-white px-[32rpx]">
<TForm ref="tForm" :model="form" :rules="rules" errorType="toast"> <TForm ref="tForm" :model="form" :rules="rules" errorType="toast">
<TFormItem prop="publishName"> <TFormItem prop="recruitTeacherName">
<TInputField <TInputField
v-model="form.publishName" v-model="form.recruitTeacherName"
label="发布人" label="发布人"
placeholder="" placeholder=""
inputAlign="left" inputAlign="left"
@ -13,9 +13,9 @@
:labelWidth="100" :labelWidth="100"
/> />
</TFormItem> </TFormItem>
<TFormItem prop="clientName"> <TFormItem prop="studentName">
<TInputField <TInputField
v-model="form.clientName" v-model="form.studentName"
label="客户姓名" label="客户姓名"
placeholder="" placeholder=""
inputAlign="left" inputAlign="left"
@ -24,9 +24,9 @@
:labelWidth="100" :labelWidth="100"
/> />
</TFormItem> </TFormItem>
<TFormItem prop="mobile"> <TFormItem prop="phone">
<TInputField <TInputField
v-model="form.mobile" v-model="form.phone"
label="电话" label="电话"
placeholder="" placeholder=""
inputAlign="left" inputAlign="left"
@ -35,8 +35,9 @@
:labelWidth="100" :labelWidth="100"
/> />
</TFormItem> </TFormItem>
<TFormItem prop="desc"> <TFormItem prop="basicInformation">
<TTextareaField <TTextareaField
v-model="form.basicInformation"
label="基本情况" label="基本情况"
placeholder="" placeholder=""
autoHeight autoHeight
@ -45,9 +46,9 @@
:labelWidth="100" :labelWidth="100"
/> />
</TFormItem> </TFormItem>
<TFormItem prop="status"> <TFormItem prop="state">
<TMultiSelect <TMultiSelect
v-model="form.status" v-model="form.state"
label="状态" label="状态"
:groupList="statusList" :groupList="statusList"
:labelWidth="100" :labelWidth="100"
@ -56,50 +57,84 @@
</TForm> </TForm>
</view> </view>
<view class="px-[60rpx]"> <view class="px-[60rpx]">
<u-button class="btn" color="#0E66FB" shape="circle" @click="handleSubmit"> <u-button
提交 class="btn"
color="#0E66FB"
shape="circle"
:loading="loading"
@click="handleSubmit"
>
提交{{ form.state }}
</u-button> </u-button>
</view> </view>
</view> </view>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { apiAddCluseProgress, apiCluseEdit } from '@/api/clue'
import { stateEnum } from '@/enums'
import { useClueDetail } from '@/hooks/useCommon'
import { toast } from '@/utils/util'
import { onLoad } from '@dcloudio/uni-app' import { onLoad } from '@dcloudio/uni-app'
import { shallowRef } from 'vue' import { shallowRef } from 'vue'
import { ref } from 'vue' import { ref } from 'vue'
const { fetchClueDetail, clueDetailInfo } = useClueDetail()
const id = ref('') const id = ref('')
const tForm = ref() const tForm = ref()
const form = ref({ const form = ref({
publishName: '', id: '',
clientName: '张三', recruitTeacherName: '',
mobile: '', studentName: '',
desc: '', phone: '',
status: '' basicInformation: '',
state: null
}) })
const rules = { const rules = {
status: [{ required: true, message: '请选择状态' }] state: [{ required: true, message: '请选择状态' }]
} }
const statusList = shallowRef([ const statusList = shallowRef([
{ label: '账号已添加', value: 1 }, { label: '账号已添加', value: stateEnum.ADD_RELATION },
{ label: '账号不存在', value: 2 }, { label: '账号不存在', value: stateEnum.NO_EXIST },
{ label: '账号未通过', value: 3 } { label: '账号未通过', value: stateEnum.UN_PASS }
]) ])
onLoad(option => { onLoad(async option => {
if (option?.id) { if (option?.id) {
id.value = option.id id.value = option.id
await fetchClueDetail(id.value)
setFormData()
} }
}) })
const loading = ref(false)
const handleSubmit = () => { const handleSubmit = () => {
tForm.value tForm.value
.validate() .validate()
.then(valid => { .then(async valid => {
if (valid) { if (valid) {
console.log('校验通过') loading.value = true
try {
const data = {
id: form.value.id,
state: form.value.state
}
await apiAddCluseProgress(data)
toast('添加进展成功')
uni.navigateBack()
uni.$emit('refreshPage')
} catch (error) {}
} }
}) })
.catch(() => {}) .catch(() => {})
loading.value = false
}
const setFormData = () => {
for (const key in clueDetailInfo.value) {
if (Object.prototype.hasOwnProperty.call(form.value, key)) {
form.value[key] = clueDetailInfo.value[key]
}
}
} }
</script> </script>

View File

@ -17,7 +17,6 @@
flex-direction: column; flex-direction: column;
.wrapper { .wrapper {
flex: 1; flex: 1;
background-color: #fafafe;
@apply flex flex-col; @apply flex flex-col;
} }
} }

View File

@ -120,6 +120,13 @@ export default defineComponent({
}) })
}) })
} }
const resetFields = () => {
instance.proxy.children.map(child => {
const prop = child.prop
const value = uni.$u.getProperty(originalModel.value, prop)
uni.$u.setProperty(props.model, prop, value)
})
}
watch( watch(
() => props.rules, () => props.rules,
newVal => { newVal => {
@ -139,7 +146,8 @@ export default defineComponent({
) )
return { return {
children: [], children: [],
validate validate,
resetFields
} }
} }
}) })

View File

@ -21,6 +21,7 @@
:inputAlign="inputAlign" :inputAlign="inputAlign"
:readonly="readonly" :readonly="readonly"
:maxlength="maxlength" :maxlength="maxlength"
placeholderStyle="color: '#7c7e82'"
></u-input> ></u-input>
</view> </view>
</template> </template>

View File

@ -58,7 +58,6 @@ const innerValue = ref(props.modelValue)
const handleSelectedItem = (val: number) => { const handleSelectedItem = (val: number) => {
emit('update:modelValue', val) emit('update:modelValue', val)
innerValue.value = val innerValue.value = val
console.log(val)
} }
</script> </script>

View File

@ -58,7 +58,7 @@ const props = defineProps({
default: false default: false
} }
}) })
const emit = defineEmits(['update:modelValue']) const emit = defineEmits(['update:modelValue', 'onInput'])
const innerValue = ref('') const innerValue = ref('')
@ -96,6 +96,7 @@ const isShowClear = computed(() => {
const onInput = e => { const onInput = e => {
innerValue.value = e.detail.value innerValue.value = e.detail.value
emit('update:modelValue', innerValue.value) emit('update:modelValue', innerValue.value)
emit('onInput')
} }
const onClear = () => { const onClear = () => {
innerValue.value = '' innerValue.value = ''

View File

@ -47,8 +47,8 @@ const { roles } = useRoleData()
const emit = defineEmits(['update:modelValue', 'close']) const emit = defineEmits(['update:modelValue', 'close'])
const activeRole = ref(props.modelValue) const activeRole = ref(props.modelValue)
const visible = ref(false) const visible = ref(false)
let currentRoutes = getCurrentPages() // const currentRoutes = getCurrentPages() //
let currentRoute = currentRoutes[currentRoutes.length - 1].route // const currentRoute = currentRoutes[currentRoutes.length - 1].route //
const handleActiveRole = (val: number) => { const handleActiveRole = (val: number) => {
activeRole.value = val activeRole.value = val
} }

View File

@ -39,6 +39,7 @@ const props = withDefaults(defineProps<TabBarProps>(), {
const tabBarStore = useTabBarStore() const tabBarStore = useTabBarStore()
const { tabBarList, activeTabBar } = storeToRefs(tabBarStore) const { tabBarList, activeTabBar } = storeToRefs(tabBarStore)
const handleTabbar = (index: number) => { const handleTabbar = (index: number) => {
tabBarStore.setActiveTabBar(index) tabBarStore.setActiveTabBar(index)
navigateTo(unref(tabBarList)[index], 'reLaunch') navigateTo(unref(tabBarList)[index], 'reLaunch')

View File

@ -8,12 +8,7 @@
<view class="px-[24rpx] py-[16rpx] flex flex-col gap-[16rpx]"> <view class="px-[24rpx] py-[16rpx] flex flex-col gap-[16rpx]">
<slot name="content" /> <slot name="content" />
</view> </view>
<view <slot name="action" v-if="isSlotAction" />
class="px-[32rpx] border-t border-solid border-[#F3F3F3] flex justify-end py-[12rpx]"
v-if="isSlotAction"
>
<slot name="action" />
</view>
</view> </view>
</template> </template>

View File

@ -14,7 +14,7 @@ import { computed } from 'vue'
const props = defineProps({ const props = defineProps({
curDate: { curDate: {
type: String, type: Number,
default: new Date().getTime() default: new Date().getTime()
}, },
type: { type: {

View File

@ -3,9 +3,9 @@
<template #title> <template #title>
<view class="flex justify-between w-full py-[10rpx]"> <view class="flex justify-between w-full py-[10rpx]">
<view class="flex"> <view class="flex">
<text>韩梅梅</text> <text>{{ item.studentName }}</text>
<view class="flex ml-[48rpx] gap-[4rpx] items-center"> <view class="flex ml-[48rpx] gap-[4rpx] items-center">
<text class="text-primary">18138952909</text> <text class="text-primary">{{ item.phone }}</text>
<u-copy content="uview-plus is great !"> <u-copy content="uview-plus is great !">
<TIcon name="icon-copy" color="#0E66FB" /> <TIcon name="icon-copy" color="#0E66FB" />
</u-copy> </u-copy>
@ -15,7 +15,10 @@
</template> </template>
<template #content> <template #content>
<view class="flex flex-col gap-[16rpx]"> <view class="flex flex-col gap-[16rpx]">
<view class="flex gap-[8rpx] items-center"> <view
class="flex gap-[8rpx] items-center"
v-if="item.situation == converStatusEnum.EXCEPTION"
>
<TIcon name="icon-warning" color="#F5222D" /> <TIcon name="icon-warning" color="#F5222D" />
<text class="text-error">待电销老师重新跟进</text> <text class="text-error">待电销老师重新跟进</text>
</view> </view>
@ -23,22 +26,33 @@
<text class="text-muted w-[128rpx]">基本情况</text> <text class="text-muted w-[128rpx]">基本情况</text>
<view class="flex gap-[4rpx] flex-1 items-end"> <view class="flex gap-[4rpx] flex-1 items-end">
<text> <text>
学生爸爸接电话学生高三毕业300多分家长不清楚学生收到录取通知家长说学生不读书了我让家长先问问学生对未来的规划先和学生沟通一下家长同意我们加他微信发专业资料给他看看可以在微信上问问学生具体情况推荐3+2给家长发一下学校简介和专业资料 {{ item.basicInformation }}
</text> </text>
<u-copy content="uview-plus is great !"> <u-copy content="uview-plus is great !">
<TIcon name="icon-copy" color="#0E66FB" /> <TIcon name="icon-copy" color="#0E66FB" />
</u-copy> </u-copy>
</view> </view>
</view> </view>
<view class="flex gap-[20rpx]"> <view class="flex gap-[20rpx]" v-if="item.telemarketingTeacherId !== null">
<text class="text-muted w-[128rpx]">电销老师</text> <text class="text-muted w-[128rpx]">电销老师</text>
<text class="flex-1">王五</text> <text class="flex-1">{{ item.telemarketingTeacherName }}</text>
</view> </view>
<view class="flex gap-[20rpx]"> <view
class="flex gap-[20rpx]"
v-if="
item.situation == converStatusEnum.CONVERTED_PROCESS && item.state !== null
"
>
<text class="text-muted w-[128rpx]">状态</text> <text class="text-muted w-[128rpx]">状态</text>
<text class="flex-1 text-error">账号不存在</text> <text class="flex-1 text-error">{{ parseStateText }}</text>
</view> </view>
<view class="flex gap-[20rpx]"> <view
class="flex gap-[20rpx]"
v-if="
item.situation == converStatusEnum.CONVERTED ||
item.situation == converStatusEnum.FAILED
"
>
<text class="text-muted w-[128rpx]">备注</text> <text class="text-muted w-[128rpx]">备注</text>
<view class="flex gap-[12rpx]"> <view class="flex gap-[12rpx]">
<text class="flex-1 text-error">已交一部分定位金</text> <text class="flex-1 text-error">已交一部分定位金</text>
@ -51,19 +65,42 @@
</view> </view>
</view> </view>
</view> </view>
<view class="flex gap-[20rpx]"> <view class="flex gap-[20rpx]" v-if="item.situation == converStatusEnum.CONVERTED">
<text class="text-muted w-[128rpx]">成交时间</text> <text class="text-muted w-[128rpx]">成交时间</text>
<text class="flex-1">2025-02-10 16:04:00</text> <text class="flex-1">2025-02-10 16:04:00</text>
</view> </view>
</view> </view>
</template> </template>
<template #action> <template #action>
<view class="flex justify-end gap-[16rpx]"> <view
<u-button color="#0E66FB" shape="circle" @click="handleGet"></u-button> class="px-[32rpx] border-t border-solid border-[#F3F3F3] flex justify-end py-[12rpx]"
<u-button color="#0E66FB" shape="circle" @click="handleAddProgress"> >
添加进展 <view class="flex justify-end gap-[16rpx]">
</u-button> <u-button
<u-button color="#0E66FB" shape="circle" @click="handleComplete"></u-button> color="#0E66FB"
shape="circle"
v-if="item.situation == converStatusEnum.UN_RECEIVED"
@click="handleGet"
>
领取
</u-button>
<u-button
color="#0E66FB"
shape="circle"
v-if="item.situation == converStatusEnum.CONVERTED_PROCESS"
@click="handleAddProgress"
>
添加进展
</u-button>
<u-button
color="#0E66FB"
shape="circle"
v-if="item.situation == converStatusEnum.ADD_RELATION"
@click="handleComplete"
>
转化完成
</u-button>
</view>
</view> </view>
</template> </template>
</w-card> </w-card>
@ -71,18 +108,36 @@
<script setup lang="ts"> <script setup lang="ts">
import { toast } from '@/utils/util' import { toast } from '@/utils/util'
import { PropType } from 'vue'
import { IClue } from '../telesale/clue-card.vue'
import { converStatusEnum, stateEnum } from '@/enums'
import { apiGetCluse } from '@/api/clue'
import { computed } from 'vue'
const props = defineProps({ const props = defineProps({
item: { item: {
type: Object, type: Object as PropType<IClue>,
default: () => ({}) default: () => ({})
} }
}) })
const emit = defineEmits(['handleUpdateRemark']) const emit = defineEmits(['handleUpdateRemark', 'refreshPage'])
const stateMap: Record<stateEnum, string> = {
[stateEnum.ADD_RELATION]: '账号已添加',
[stateEnum.NO_EXIST]: '账号不存在',
[stateEnum.UN_PASS]: '账号未通过'
}
const parseStateText = computed(() => stateMap[props.item.state])
// //
const handleGet = () => { const handleGet = async () => {
const { item } = props const {
toast('领取成功') item: { id }
} = props
try {
await apiGetCluse({ id })
toast('领取成功')
emit('refreshPage')
} catch (error) {}
} }
// //
const handleAddProgress = () => { const handleAddProgress = () => {

View File

@ -3,15 +3,17 @@
<template #title> <template #title>
<view class="flex justify-between w-full py-[10rpx]"> <view class="flex justify-between w-full py-[10rpx]">
<view class="flex"> <view class="flex">
<text>韩梅梅</text> <text>{{ item.studentName }}</text>
<view class="flex ml-[48rpx] gap-[4rpx] items-center"> <view class="flex ml-[48rpx] gap-[4rpx] items-center">
<text class="text-primary">18138952909</text> <text class="text-primary">{{ item.phone }}</text>
<u-copy content="uview-plus is great !"> <u-copy content="uview-plus is great !">
<TIcon name="icon-copy" color="#0E66FB" /> <TIcon name="icon-copy" color="#0E66FB" />
</u-copy> </u-copy>
</view> </view>
</view> </view>
<text>待领取</text> <text v-if="item.situation == converStatusEnum.UN_RECEIVED" class="text-[#ED6D41]">
待领取
</text>
</view> </view>
</template> </template>
<template #content> <template #content>
@ -23,34 +25,62 @@
</view> </view>
<view class="flex gap-[20rpx] mb-[16rpx]"> <view class="flex gap-[20rpx] mb-[16rpx]">
<text class="text-muted w-[128rpx]">跟进时间</text> <text class="text-muted w-[128rpx]">跟进时间</text>
<text class="flex-1">2025-02-08 11:11:30</text> <text class="flex-1">{{ item.createTime }}</text>
</view> </view>
<view class="flex gap-[20rpx] mb-[16rpx]"> <view class="flex gap-[20rpx] mb-[16rpx]" v-if="item.recruitTeacherId !== null">
<text class="text-muted w-[128rpx]">招生老师</text> <text class="text-muted w-[128rpx]">招生老师</text>
<text class="flex-1">王五</text> <text class="flex-1">{{ item.recruitTeacherName }}</text>
</view> </view>
<view class="flex gap-[20rpx]"> <view class="flex gap-[20rpx]" v-if="item.state !== null">
<text class="text-muted w-[128rpx]">状态</text> <text class="text-muted w-[128rpx]">状态</text>
<text class="flex-1">账号不存在</text> <text class="flex-1">账号不存在</text>
</view> </view>
</template> </template>
<template #action> <template #action>
<u-button color="#0E66FB" shape="circle" @click="handleEditFollow"></u-button> <view
class="px-[32rpx] border-t border-solid border-[#F3F3F3] flex justify-end py-[12rpx]"
v-if="item.situation == converStatusEnum.EXCEPTION"
>
<u-button color="#0E66FB" shape="circle" @click="handleEditFollow">
修改跟进
</u-button>
</view>
</template> </template>
</w-card> </w-card>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { PropType } from 'vue'
import { computed } from 'vue' import { computed } from 'vue'
import { converStatusEnum, stateEnum } from '@/enums'
export interface IClue {
studentName: string
phone: string
id: number
basicInformation: string
remark: string
isConversion: number
listSource: number //线
state: stateEnum //
situation: number //
createTime: string //
telemarketingTeacherName: string //
recruitTeacherName: string //
recruitTeacherId: number
telemarketingTeacherId: number
}
const props = defineProps({ const props = defineProps({
item: { item: {
type: Object, type: Object as PropType<IClue>,
default: () => ({}) default: () => ({})
} }
}) })
const ellipsisDesc = computed( const ellipsisDesc = computed(
() => item => item.desc?.length >= 14 ? item.desc?.slice(0, 14) + '...' : item.desc () => (item: IClue) =>
item.basicInformation?.length >= 14
? item.basicInformation?.slice(0, 14) + '...'
: item.basicInformation
) )
const handleEditFollow = () => { const handleEditFollow = () => {
const { item } = props const { item } = props

View File

@ -2,57 +2,86 @@
<view class="mt-[32rpx] p-[32rpx] bg-white"> <view class="mt-[32rpx] p-[32rpx] bg-white">
<view class="flex justify-between items-center mb-[20rpx]"> <view class="flex justify-between items-center mb-[20rpx]">
<text class="text-[44rpx] font-bold">跟进信息</text> <text class="text-[44rpx] font-bold">跟进信息</text>
<text class="text-[28rpx]">全部清</text> <text class="text-[28rpx]" @click="handleClear"></text>
</view> </view>
<TForm ref="tForm" :model="form" :rules="rules" errorType="toast"> <TForm ref="tForm" :model="form" :rules="rules" errorType="toast">
<TFormItem prop="clientName"> <TFormItem prop="studentName">
<TInputField <TInputField
v-model="form.clientName" v-model="form.studentName"
label="客户姓名" label="客户姓名"
placeholder="请填写客户姓名(必填)" placeholder="请填写客户姓名(必填)"
inputAlign="left" inputAlign="left"
/> />
</TFormItem> </TFormItem>
<TFormItem prop="mobile"> <TFormItem prop="phone">
<TInputField <TInputField
v-model="form.mobile" v-model="form.phone"
label="电话" label="电话"
placeholder="请填写电话(必填)" placeholder="请填写电话(必填)"
inputAlign="left" inputAlign="left"
/> />
</TFormItem> </TFormItem>
<TFormItem prop="desc"> <TFormItem prop="basicInformation">
<TTextareaField label="基本情况" placeholder="请填写基本情况(必填)" autoHeight /> <TTextareaField
v-model="form.basicInformation"
label="基本情况"
placeholder="请填写基本情况(必填)"
autoHeight
/>
</TFormItem> </TFormItem>
</TForm> </TForm>
<u-button class="btn" color="#0E66FB" shape="circle" @click="handleConfirm"></u-button> <u-button
class="btn"
color="#0E66FB"
shape="circle"
:loading="loading"
@click="handleConfirm"
>
确认
</u-button>
</view> </view>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { apiAddCluse } from '@/api/clue'
import { toast, validate } from '@/utils/util'
import { validateContact } from '@/utils/validate'
import { ref } from 'vue' import { ref } from 'vue'
const tForm = ref() const tForm = ref()
const form = ref({ const form = ref({
clientName: '', studentName: '',
mobile: '', phone: '',
desc: '' basicInformation: ''
}) })
const loading = ref(false)
const rules = { const rules = {
clientName: [{ required: true, message: '请填写客户姓名', trigger: 'blur' }], studentName: [{ required: true, message: '请填写客户姓名', trigger: 'blur' }],
mobile: [{ required: true, message: '请填写电话', trigger: 'blur' }], phone: [
desc: [{ required: true, message: '请填写基本情况', trigger: 'blur' }] { required: true, message: '请填写电话', trigger: 'blur' },
{ validator: validateContact, trigger: 'blur' }
],
basicInformation: [{ required: true, message: '请填写基本情况', trigger: 'blur' }]
} }
const handleConfirm = () => { const handleConfirm = () => {
tForm.value tForm.value
.validate() .validate()
.then(valid => { .then(async valid => {
if (valid) { if (valid) {
console.log('校验通过') loading.value = true
try {
await apiAddCluse(form.value)
toast('添加成功')
handleClear()
} catch (error) {}
loading.value = false
} }
}) })
.catch(() => {}) .catch(() => {})
} }
const handleClear = () => {
tForm.value.resetFields()
}
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@ -1,46 +1,47 @@
<template> <template>
<view class="flex-1 h-full flex flex-col"> <view class="flex-1 h-full flex flex-col">
<TSearch <TSearch
v-model="searchValue" v-model="queryParams.likeWork"
placeholder="搜索客户姓名/手机号码" placeholder="搜索客户姓名/手机号码"
backgroundColor="#F5F5F5" backgroundColor="#F5F5F5"
@on-input="searchChange"
/> />
<view class="flex-1 mt-3 px-2 overflow-auto bg-[#FAFAFE]"> <view class="flex-1 mt-3 px-2 overflow-auto bg-[#FAFAFE]">
<!-- <z-paging <z-paging
ref="paging" ref="paging"
v-model="dataList" v-model="dataList"
@query="queryList" @query="queryList"
:fixed="false" :fixed="false"
height="100%" height="100%"
> --> >
<clue-card
<clue-card v-for="(item, index) in dataList"
v-for="(item, index) in dataList" :key="`${index} + 'unique'`"
:key="`${index} + 'unique'`" :item="item"
:item="item" />
/> </z-paging>
<!-- </z-paging> -->
</view> </view>
</view> </view>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { apiOverhaulPagelist } from '@/api/overhaul'
import { useZPaging } from '@/hooks/useZPaging' import { useZPaging } from '@/hooks/useZPaging'
import { ref } from 'vue' import { ref } from 'vue'
import clueCard from './clue-card.vue' import clueCard from './clue-card.vue'
import { apiCluseList } from '@/api/clue'
import { debounce } from 'lodash-es'
const searchValue = ref('') const queryParams = ref({
const queryParams = ref({}) likeWork: ''
const dataList = ref([ })
{ const dataList = ref([])
id: 1
}
])
const { paging, queryList, refresh, changeApi, setParams } = useZPaging( const { paging, queryList, refresh, changeApi, setParams } = useZPaging(
queryParams.value, queryParams.value,
apiOverhaulPagelist, apiCluseList,
() => {} () => {}
) )
const searchChange = debounce(() => {
refresh()
}, 300)
</script> </script>
<style scoped></style> <style scoped></style>

View File

@ -6,8 +6,8 @@ export enum ThemeEnum {
DARK = 'dark' DARK = 'dark'
} }
export enum ChannelEnum { export enum ChannelEnum {
USER_PLATFORM = 0, USER_PLATFORM = 0, // 用户端
STAFF_PLATFORM = 1 STAFF_PLATFORM = 1 //师傅端
} }
// 客户端 // 客户端
export enum ClientEnum { export enum ClientEnum {

19
src/enums/index.ts 100644
View File

@ -0,0 +1,19 @@
export enum teleSaleEnum {
ADD_FOLLOW = 0,
FOLLOW_RECORD = 1
}
export enum converStatusEnum {
INTENTION = 0, //有意向
UN_RECEIVED = 1, //待领取
CONVERTED_PROCESS = 2, //转化中
ADD_RELATION = 3, //已添加
EXCEPTION = 4, //异常待处理
CONVERTED = 5, //已成交
FAILED = 6 //已战败
}
export enum stateEnum {
ADD_RELATION = 0, //账号已添加
NO_EXIST = 1, //账号不存在
UN_PASS = 2 //账号未通过
}

View File

@ -0,0 +1,21 @@
import { apiCluseDetail } from '@/api/clue'
import { ref } from 'vue'
// 获取线索详情
export function useClueDetail() {
const clueDetailInfo = ref()
const fetchClueDetail = async (id: number | string) => {
uni.showLoading({
title: '加载中'
})
try {
const result = await apiCluseDetail({ id })
clueDetailInfo.value = result
} catch (error) {}
uni.hideLoading()
}
return {
clueDetailInfo,
fetchClueDetail
}
}

View File

@ -23,110 +23,50 @@ export interface RoleItem {
export function useRoleData() { export function useRoleData() {
const roles: RoleItem[] = [ const roles: RoleItem[] = [
{ {
name: '业务员', name: '电销老师',
ids: [8, 9], ids: [5],
tabBarList: [ tabBarList: [
{ {
text: '客户库', text: '首页',
path: '/pages/telesale/home/index',
inactiveIcon: clientInActive, inactiveIcon: clientInActive,
activeIcon: clientActive, activeIcon: clientActive
path: '/pages/salesman/client/index'
}, },
{ {
text: '合同管理', text: '总结模板',
path: '/pages/salesman/contract/index', path: '/pages/telesale/summary/index',
inactiveIcon: contractInActive, inactiveIcon: contractInActive,
activeIcon: contractActive activeIcon: contractActive
}, },
{ {
text: '个人中心', text: '个人中心',
path: '/pages/salesman/profile/index', path: '/pages/telesale/my/index',
inactiveIcon: profileInActive, inactiveIcon: profileInActive,
activeIcon: profileActive activeIcon: profileActive
} }
] ]
}, },
{ {
name: '检修员', name: '招生老师',
ids: [6], ids: [6],
tabBarList: [ tabBarList: [
{ {
text: '工单池', text: '首页',
inactiveIcon: order, inactiveIcon: order,
activeIcon: orderActive, activeIcon: orderActive,
path: '/pages/overhaul/pool/index' path: '/pages/recruitsale/home/index'
}, },
{ {
text: '我的任务', text: '总结模板',
path: '/pages/recruitsale/summary/index',
inactiveIcon: task, inactiveIcon: task,
activeIcon: taskActive, activeIcon: taskActive
path: '/pages/overhaul/task/index'
}, },
{ {
text: '个人中心', text: '个人中心',
path: '/pages/recruitsale/my/index',
inactiveIcon: profileInActive, inactiveIcon: profileInActive,
activeIcon: profileActive, activeIcon: profileActive
path: '/pages/overhaul/my/index'
}
]
},
{
name: '客户',
ids: [1, 2, 11],
tabBarList: [
{
text: '工单记录',
inactiveIcon: order,
activeIcon: orderActive,
path: '/pages/client/workOrder/index'
},
{
text: '个人中心',
inactiveIcon: profileInActive,
activeIcon: profileActive,
path: '/pages/client/my/index'
}
]
},
{
name: '维修员',
ids: [7],
tabBarList: [
// {
// text: '工单池',
// inactiveIcon: order,
// activeIcon: orderActive,
// path: '/pages/repair/pool/index'
// },
{
text: '工单列表',
inactiveIcon: task,
activeIcon: taskActive,
path: '/pages/repair/task/index'
},
{
text: '个人中心',
inactiveIcon: profileInActive,
activeIcon: profileActive,
path: '/pages/repair/my/index'
}
]
},
{
name: '维修主管',
ids: [10],
tabBarList: [
{
text: '工单列表',
inactiveIcon: task,
activeIcon: taskActive,
path: '/pages/charge/order/index'
},
{
text: '个人中心',
inactiveIcon: profileInActive,
activeIcon: profileActive,
path: '/pages/charge/my/index'
} }
] ]
} }

View File

@ -1,29 +1,56 @@
{ {
"pages": [ "pages": [
{ {
"path": "pages/recruitsale/home/index", "path": "pages/index/index",
"style": { "style": {
"navigationBarTitleText": "" "navigationBarTitleText": "首页",
} "navigationStyle": "custom"
},
{
"path": "pages/recruitsale/summary/index",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/telesale/summary/index",
"style": {
"navigationBarTitleText": ""
} }
}, },
{ {
"path": "pages/telesale/home/index", "path": "pages/telesale/home/index",
"style": { "style": {
"navigationBarTitleText": "" "navigationBarTitleText": ""
},
"auth": true
},
{
"path": "pages/recruitsale/home/index",
"style": {
"navigationBarTitleText": ""
},
"auth": true
},
{
"path": "pages/telesale/summary/index",
"style": {
"navigationBarTitleText": ""
},
"auth": true
},
{
"path": "pages/recruitsale/summary/index",
"style": {
"navigationBarTitleText": ""
},
"auth": true
},
{
"path": "pages/recruitsale/my/index",
"style": {
"navigationBarTitleText": "个人中心",
"navigationStyle": "custom"
},
"auth": true
},
{
"path": "pages/telesale/my/index",
"style": {
"navigationBarTitleText": "个人中心",
"navigationStyle": "custom"
} }
}, },
{ {
"path": "pages/salesman/contract/index", "path": "pages/salesman/contract/index",
"style": { "style": {
@ -44,21 +71,6 @@
}, },
"auth": true "auth": true
}, },
{
"path": "pages/client/my/index",
"style": {
"navigationBarTitleText": "个人中心",
"navigationStyle": "custom"
},
"auth": true
},
{
"path": "pages/salesman/profile/index",
"style": {
"navigationBarTitleText": "个人中心",
"navigationStyle": "custom"
}
},
{ {
"path": "pages/overhaul/pool/index", "path": "pages/overhaul/pool/index",
"style": { "style": {

View File

@ -1,86 +1,19 @@
<!--
* @Author: micky
* @Date: 2024-08-10 15:24:06
* @LastEditors: micky
* @LastEditTime: 2024-08-28 18:39:12
* @FilePath: \chargingpile-uniapp\src\pages\index\index.vue
-->
<template> <template>
<view class="wrapper"> <view></view>
<swiper class="swiper" @change="swiperChange">
<swiper-item v-for="(item, index) in list" :key="index" class="item">
<image :src="item.image"></image>
<view class="title">{{ item.title }}</view>
<view class="desc">{{ item.desc }}</view>
</swiper-item>
</swiper>
<view class="rowDot">
<view v-for="(item, index) in list" :key="index" class="dots">
<view :class="['dot', index === swiperCurrent ? 'active' : '']"></view>
</view>
</view>
<u-button
type="primary"
class="login-btn"
shape="circle"
v-if="swiperCurrent == 3"
@click="toPage"
>
立即登录
</u-button>
</view>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { reactive, ref } from 'vue' import { ROLEINDEX } from '@/enums/cacheEnums'
import Step1 from '@/static/images/step_1.png'
import Step2 from '@/static/images/step_2.png'
import Step3 from '@/static/images/step_3.png'
import Step4 from '@/static/images/step_4.png'
import { onLoad } from '@dcloudio/uni-app'
import { useUserStore } from '@/stores/user'
import { useRoleData } from '@/hooks/useRoleData' import { useRoleData } from '@/hooks/useRoleData'
import { useTabBarStore } from '@/stores/tabbar' import { useTabBarStore } from '@/stores/tabbar'
import { useUserStore } from '@/stores/user'
import cache from '@/utils/cache' import cache from '@/utils/cache'
import { ROLEINDEX } from '@/enums/cacheEnums' import { onLoad } from '@dcloudio/uni-app'
const userStore = useUserStore() const userStore = useUserStore()
const { roles } = useRoleData() const { roles } = useRoleData()
const tabBarStore = useTabBarStore() const tabBarStore = useTabBarStore()
const swiperCurrent = ref(0)
const list = reactive([
{
image: Step1,
title: '用户报修',
desc: '用户可以通过在线报修、电话报修或扫码报修等多种方式提交维修请求。'
},
{
image: Step2,
title: '桩点检修',
desc: '检修员接单后将上门对客户上报的桩点进行现场维修。'
},
{
image: Step3,
title: '上门巡检',
desc: '检修员在巡检过程中发现设备异常时需提交检修单。'
},
{
image: Step4,
title: '模块维修',
desc: '维修员通过扫描模块条形码来创建维修工单。'
}
])
const swiperChange = e => {
swiperCurrent.value = e.detail.current
}
const toPage = () => {
uni.redirectTo({
url: '/bundle/pages/login/login'
})
}
const setNextRoute = () => { const setNextRoute = () => {
const ind = cache.get(ROLEINDEX) || 0 const ind = cache.get(ROLEINDEX) || 0
const { userInfo } = userStore const { userInfo } = userStore
@ -100,51 +33,4 @@ onLoad(() => {
} }
}) })
</script> </script>
<style scoped></style>
<style lang="scss" scoped>
.wrapper {
@apply h-full relative z-10;
.swiper {
@apply px-[30px] h-[400px] pt-[150px];
.item {
@apply flex flex-col items-center;
}
image {
@apply w-[100vw] h-[310px] mb-[48rpx];
line-height: 90rpx;
}
.title {
@apply text-[24px] text-[#2F2F52] mb-[15px];
}
.desc {
@apply text-[#334155];
}
}
.rowDot {
@apply flex justify-center mt-[66px];
.dots {
flex-direction: row;
justify-content: center;
align-items: center;
align-content: center;
.dot {
margin-right: 8rpx;
width: 20rpx;
height: 8rpx;
opacity: 1;
border-radius: 6rpx;
background: #efefef;
}
.dot.active {
width: 48rpx;
background: #1a66ff;
}
}
}
:deep(.login-btn) {
button {
@apply w-[200px] h-[40px] -mt-[20px];
}
}
}
</style>

View File

@ -1,7 +1,7 @@
<template> <template>
<view class="flex-1 h-screen flex flex-col"> <TContainer>
<TSearch <TSearch
v-model="queryParams.searchValue" v-model="queryParams.likeWork"
placeholder="搜索客户姓名/手机号码" placeholder="搜索客户姓名/手机号码"
backgroundColor="#F5F5F5" backgroundColor="#F5F5F5"
showBorder showBorder
@ -13,27 +13,26 @@
:activeStyle="{ color: '#0E66FB' }" :activeStyle="{ color: '#0E66FB' }"
lineWidth="49" lineWidth="49"
lineColor="#0E66FB" lineColor="#0E66FB"
:current="activeTab"
@change="handleChangeTab" @change="handleChangeTab"
></u-tabs> ></u-tabs>
<view class="flex-1 pt-[24rpx] px-[24rpx] overflow-auto bg-[#F8F8F8]"> <view class="flex-1 pt-[24rpx] px-[24rpx] overflow-auto bg-[#F8F8F8]">
<!-- <z-paging <z-paging
ref="paging" ref="paging"
v-model="dataList" v-model="dataList"
@query="queryList" @query="queryList"
:fixed="false" :fixed="false"
height="100%" height="100%"
> --> >
<clue-card
<clue-card v-for="(item, index) in dataList"
v-for="(item, index) in dataList" :key="`${index} + 'unique'`"
:key="`${index} + 'unique'`" :item="item"
:item="item" @handle-update-remark="handleUpdateRemark"
@handle-update-remark="handleUpdateRemark" @refresh-page="refresh"
/> />
<!-- </z-paging> --> </z-paging>
</view> </view>
</view> </TContainer>
<w-confirm-popup v-model="popupShow" @confirm="handleConfirm"> <w-confirm-popup v-model="popupShow" @confirm="handleConfirm">
<template #content> <template #content>
<TTextareaField v-model="contentText" border="surround" :required="false" /> <TTextareaField v-model="contentText" border="surround" :required="false" />
@ -48,40 +47,33 @@ import { ref } from 'vue'
import clueCard from '@/components/widgets/recruitsale/clue-card.vue' import clueCard from '@/components/widgets/recruitsale/clue-card.vue'
import { shallowRef } from 'vue' import { shallowRef } from 'vue'
import { computed } from 'vue' import { computed } from 'vue'
import { apiCluseList } from '@/api/clue'
import { converStatusEnum } from '@/enums'
import { onLoad } from '@dcloudio/uni-app'
const tabs = shallowRef([ const tabs = shallowRef([
{ name: '待领取', value: 0 }, { name: '待领取', value: converStatusEnum.UN_RECEIVED },
{ name: '转化中', value: 1 }, { name: '转化中', value: converStatusEnum.CONVERTED_PROCESS },
{ name: '已成交', value: 2 }, { name: '已成交', value: converStatusEnum.CONVERTED },
{ name: '已战败', value: 3 } { name: '已战败', value: converStatusEnum.FAILED }
]) ])
const activeTab = ref(0) const activeTab = ref(converStatusEnum.UN_RECEIVED)
const queryParams = computed(() => { const queryParams = computed(() => {
const payload = { const payload = {
status: activeTab.value, situation: activeTab.value,
searchValue: '' likeWork: ''
} }
return payload return payload
}) })
const dataList = ref([ const dataList = ref([])
{
id: 1
},
{
id: 2
},
{
id: 3
}
])
const { paging, queryList, refresh, changeApi, setParams } = useZPaging( const { paging, queryList, refresh, changeApi, setParams } = useZPaging(
queryParams.value, queryParams.value,
apiOverhaulPagelist, apiCluseList,
() => {} () => {}
) )
const handleChangeTab = item => { const handleChangeTab = item => {
activeTab.value = item.value activeTab.value = item.value
// refresh(queryParams.value) refresh(queryParams.value)
} }
const popupShow = ref(false) const popupShow = ref(false)
const contentText = ref('') const contentText = ref('')
@ -91,5 +83,10 @@ const handleUpdateRemark = item => {
contentText.value = remark contentText.value = remark
} }
const handleConfirm = () => {} const handleConfirm = () => {}
onLoad(() => {
uni.$on('refreshPage', () => {
refresh(queryParams.value)
})
})
</script> </script>
<style scoped></style> <style scoped></style>

View File

@ -0,0 +1,7 @@
<template>
<TProfile :isShow="false"></TProfile>
</template>
<script setup lang="ts"></script>
<style scoped lang="scss"></style>

View File

@ -1,9 +1,15 @@
<template> <template>
<view class="pb-[24rpx]"> <TContainer>
<w-date-more :curDate="curDate" type="recruitsale" /> <view class="pb-[24rpx]">
<date-strip v-model="curDate" @change="handleDateChange" height="160rpx" /> <w-date-more :curDate="curDate" type="recruitsale" />
<w-summary-form v-model="templateItems" @handle-confirm="handleConfirm" :readonly="type" /> <date-strip v-model="curDate" @change="handleDateChange" height="160rpx" />
</view> <w-summary-form
v-model="templateItems"
@handle-confirm="handleConfirm"
:readonly="type"
/>
</view>
</TContainer>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">

View File

@ -1,5 +1,5 @@
<template> <template>
<view class="flex flex-col h-screen"> <TContainer>
<u-tabs <u-tabs
:list="tabs" :list="tabs"
:scrollable="false" :scrollable="false"
@ -11,10 +11,10 @@
@change="handleChangeTab" @change="handleChangeTab"
></u-tabs> ></u-tabs>
<view class="flex-1 bg-gray3"> <view class="flex-1 bg-gray3">
<follow-form v-if="activeTab === 0" /> <follow-form v-if="activeTab === teleSaleEnum.ADD_FOLLOW" />
<follow-record v-else-if="activeTab === 1" /> <follow-record v-else-if="activeTab === teleSaleEnum.FOLLOW_RECORD" />
</view> </view>
</view> </TContainer>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@ -22,11 +22,12 @@ import { shallowRef } from 'vue'
import { ref } from 'vue' import { ref } from 'vue'
import followForm from '@/components/widgets/telesale/follow-form.vue' import followForm from '@/components/widgets/telesale/follow-form.vue'
import followRecord from '@/components/widgets/telesale/follow-record.vue' import followRecord from '@/components/widgets/telesale/follow-record.vue'
import { teleSaleEnum } from '@/enums'
const activeTab = ref(1) const activeTab = ref(teleSaleEnum.ADD_FOLLOW)
const tabs = shallowRef([ const tabs = shallowRef([
{ name: '新增跟进', value: 0 }, { name: '新增跟进', value: teleSaleEnum.ADD_FOLLOW },
{ name: '跟进动态', value: 1 } { name: '跟进动态', value: teleSaleEnum.FOLLOW_RECORD }
]) ])
const handleChangeTab = item => { const handleChangeTab = item => {
activeTab.value = item.value activeTab.value = item.value

View File

@ -0,0 +1,7 @@
<template>
<TProfile :isShow="false"></TProfile>
</template>
<script setup lang="ts"></script>
<style scoped lang="scss"></style>

View File

@ -3,6 +3,7 @@
<w-date-more :curDate="curDate" type="telesale" /> <w-date-more :curDate="curDate" type="telesale" />
<date-strip v-model="value" @change="handleDateChange" height="160rpx" /> <date-strip v-model="value" @change="handleDateChange" height="160rpx" />
<w-summary-form v-model="templateItems" @handle-confirm="handleConfirm" /> <w-summary-form v-model="templateItems" @handle-confirm="handleConfirm" />
<tabbar />
</view> </view>
</template> </template>

View File

@ -1,8 +1,8 @@
@font-face { @font-face {
font-family: 'iconfont'; /* Project id 4837700 */ font-family: 'iconfont'; /* Project id 4837700 */
src: url('//at.alicdn.com/t/c/font_4837700_wjnzp8mer6o.woff2?t=1740542409608') format('woff2'), src: url('//at.alicdn.com/t/c/font_4837700_mvrnof6x61s.woff2?t=1740644911077') format('woff2'),
url('//at.alicdn.com/t/c/font_4837700_wjnzp8mer6o.woff?t=1740542409608') format('woff'), url('//at.alicdn.com/t/c/font_4837700_mvrnof6x61s.woff?t=1740644911077') format('woff'),
url('//at.alicdn.com/t/c/font_4837700_wjnzp8mer6o.ttf?t=1740542409608') format('truetype'); url('//at.alicdn.com/t/c/font_4837700_mvrnof6x61s.ttf?t=1740644911077') format('truetype');
} }
.iconfont { .iconfont {
@ -13,6 +13,14 @@
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
.icon-clear:before {
content: '\e601';
}
.icon-search:before {
content: '\e67d';
}
.icon-left:before { .icon-left:before {
content: '\e83d'; content: '\e83d';
} }

View File

@ -60,11 +60,11 @@ export const useAppStore = defineStore({
return url ? `${this.config.domain}${url}` : '' return url ? `${this.config.domain}${url}` : ''
// return url || '' // return url || ''
}, },
async getConfig() { // async getConfig() {
const data = await apiConfig() // const data = await apiConfig()
this.config = data // this.config = data
cache.set(CONFIG, data) // cache.set(CONFIG, data)
}, // },
// setCityInfo(city: City) { // setCityInfo(city: City) {
// this.cityInfo = city // this.cityInfo = city

View File

@ -5,7 +5,7 @@
* @LastEditTime: 2024-10-27 18:19:43 * @LastEditTime: 2024-10-27 18:19:43
* @FilePath: \chargingpile-uniapp\src\stores\user.ts * @FilePath: \chargingpile-uniapp\src\stores\user.ts
*/ */
import { apiUserInfo, getUserCenter } from '@/api/user' import { apiUserInfo } from '@/api/user'
import { TOKEN_KEY, USER_INFO, CONFIG, ROLEINDEX } from '@/enums/cacheEnums' import { TOKEN_KEY, USER_INFO, CONFIG, ROLEINDEX } from '@/enums/cacheEnums'
import cache from '@/utils/cache' import cache from '@/utils/cache'
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
@ -48,14 +48,6 @@ export const useUserStore = defineStore({
const data = await apiUserInfo({ const data = await apiUserInfo({
token: this.token || this.temToken token: this.token || this.temToken
}) })
let roleArr = []
for (const key in data.roleMap) {
roleArr.push({
name: data.roleMap[key],
id: Number(key)
})
}
data.roles = roleArr
this.userInfo = data this.userInfo = data
cache.set(USER_INFO, data) cache.set(USER_INFO, data)
} }

View File

@ -288,3 +288,8 @@ page {
} }
} }
} }
.u-tabbar {
.u-tabbar__content {
z-index: 99999 !important;
}
}

View File

@ -0,0 +1,15 @@
const mobileReg =
/^(?:(?:\+|00)86)?1(?:(?:3[\d])|(?:4[5-79])|(?:5[0-35-9])|(?:6[5-7])|(?:7[0-8])|(?:8[\d])|(?:9[189]))\d{8}$/
/**手机号码 */
export function validateContact(rule: any, value: any, callback: any) {
if (value) {
if (!mobileReg.test(value)) {
callback(new Error('请输入正确的电话'))
} else {
callback()
}
} else {
callback()
}
}

View File

@ -31,7 +31,8 @@ module.exports = {
lightblack: '#3D3D3D', lightblack: '#3D3D3D',
border: '#F1F1F1', border: '#F1F1F1',
border2: '#F3F3F3', border2: '#F3F3F3',
blue2: '#EEF6FF' blue2: '#EEF6FF',
orage: '#ED6D41'
}, },
fontSize: { fontSize: {
xs: '24rpx', xs: '24rpx',