feat:微信支付

This commit is contained in:
MJ 2025-08-07 17:44:13 +08:00
parent 81c2f77434
commit c44b29b11c
59 changed files with 1036 additions and 799 deletions

View File

@ -1,15 +1,15 @@
export * from './global';
export * from './login';
export * from './modules/warranty';
export * from './modules/awardManage';
export * from './modules/taskManage';
export * from './modules/bindDevice';
export * from './modules/parent';
export * from './modules/mySupervisionService';
export * from './modules/bindDevice';
export * from './modules/mine';
export * from './modules/academicReport';
export * from './modules/deviceScreenshotRecord';
export * from './modules/inviteBind';
export * from './modules/home';
export * from './modules/applicationManagement';
export * from './global'
export * from './login'
export * from './modules/warranty'
export * from './modules/awardManage'
export * from './modules/taskManage'
export * from './modules/bindDevice'
export * from './modules/parent'
export * from './modules/mySupervisionService'
export * from './modules/bindDevice'
export * from './modules/mine'
export * from './modules/academic-report'
export * from './modules/deviceScreenshotRecord'
export * from './modules/inviteBind'
export * from './modules/home'
export * from './modules/app-mgr'

View File

@ -1,3 +1,4 @@
import router from '@/router/router'
import { http } from './request/request'
/**
*
@ -50,7 +51,14 @@ export const wxLoginApi = async data =>
*
* @param data
*/
export const getUserInfoApi = async () => await http.get<any>('/sysUser/selectUser')
export const getUserInfoApi = async () => {
const data = await http.get<any>('/sysUser/selectUser')
if (!data?.id) {
router.toLogin()
return undefined
}
return data
}
/**
*

View File

@ -1,11 +1,17 @@
import { http } from '../request/request'
/**
*
*/
export async function getStudentStudyDataApi(data: any) {
return await http.get<any>('/sysUser/queryParentBindChildInfo', data)
}
/**
* -
* @param data
*/
export const subjectApi = async (gradeId: string) =>
await http.get<any>(`/subject/list/${gradeId}`)
export const subjectApi = async (gradeId: string) => await http.get<any>(`/subject/list/${gradeId}`)
/**
* -

View File

@ -2,7 +2,7 @@ import { http } from '../request/request'
import { storeToRefs } from 'pinia'
import { getCache } from '@/utils'
import db from '@/utils/db'
import { userStore } from '@/store'
import { useUserStore } from '@/store'
// 课表日历
export const getScheduleList = async (data?: Record<string, any>): Promise<any> =>
@ -64,7 +64,7 @@ export const getUserInfo = async (data?: Record<string, any>): Promise<any> =>
await http.get<any>('/phone/inspectorTeacher/getCurrentTeacher')
// 文件上传
export const uploadFile = async (file): Promise<any> => {
const { token } = storeToRefs(userStore())
const { token } = storeToRefs(useUserStore())
const mergerToken = token.value

View File

@ -4,15 +4,23 @@ import { http } from '../request/request'
*
* @param params
*/
export const getParentBindChildApi = async (params: any) =>
await http.get<any>('/parentBindChild/list', params)
export const getParentBindChildApi = async (params: any) => {
if (!params.size) {
params.size = -1
}
return await http.get<any>('/parentBindChild/list', params)
}
/**
*
* @param params
*/
export const getParentBindDeviceApi = async (params: any) =>
await http.get<any>('/parentBindDevice/list', params)
export const getParentBindDeviceApi = async (params: any) => {
if (!params.size) {
params.size = -1
}
return await http.get<any>('/parentBindDevice/list', params)
}
/**
* -
@ -46,12 +54,10 @@ export const parentDeviceCurrentLoginApi = async (id: string) =>
* -
* @param params
*/
export const applyBindInfoListApi = async () =>
await http.get<any>('/parentBind/applyInfoList')
export const applyBindInfoListApi = async () => await http.get<any>('/parentBind/applyInfoList')
/**
* -
* @param params
*/
export const userInfoPermitApi = async () =>
await http.get<any>('/parentBind/applyInfoPermit')
export const userInfoPermitApi = async () => await http.get<any>('/parentBind/applyInfoPermit')

View File

@ -1,9 +1,18 @@
import { http } from '../request/request';
import { http } from '../request/request'
export const getChildList = async () => {
return await http.get<any>('/inspectorParent/childList');
};
return await http.get<any>('/inspectorParent/childList')
}
export const getModelList = async () => {
return await http.get<any>('/inspectorParent/inspectorModeList');
};
return await http.get<any>('/inspectorParent/inspectorModeList')
}
// 生成订单
export async function createRechargeOrderApi(money: any) {
return await http.post<any>('/orderManagement/createParentCoinOrder', {
money,
orderType: 4,
paySubType: 'JSAPI',
})
}

View File

@ -6,7 +6,7 @@ import router from '@/router/router'
import db from '@/utils/db'
import toast from '@/utils/hud'
import { storeToRefs } from 'pinia'
import { userStore } from '@/store'
import { useUserStore } from '@/store'
// 请求拦截器
export const requestInterceptor = async (options: RequestOptions): Promise<RequestOptions> => {
@ -14,7 +14,7 @@ export const requestInterceptor = async (options: RequestOptions): Promise<Reque
const header: any = {
...options?.header,
}
const { token } = storeToRefs(userStore())
const { token } = storeToRefs(useUserStore())
// 获取token
// 如果有token添加到请求头

View File

@ -60,13 +60,13 @@ onMounted(async () => {
text: '首页',
},
{
pagePath: '/pages/academicReport/index',
pagePath: '/pages/academic-report/index',
iconPath: '/static/tabBar/studyReportNo.png',
selectedIconPath: '/static/tabBar/studyReport.png',
text: '学情报告',
},
{
pagePath: '/pages/applicationManagement/index',
pagePath: '/pages/app-mgr/index',
iconPath: '/static/tabBar/applicationNo.png',
selectedIconPath: '/static/tabBar/application.png',
text: '应用管控',
@ -92,13 +92,13 @@ onMounted(async () => {
// text: '',
// },
// {
// pagePath: '/pages/academicReport/index',
// pagePath: '/pages/academic-report/index',
// iconPath: '/static/tabBar/studyReportNo.png',
// selectedIconPath: '/static/tabBar/studyReport.png',
// text: '',
// },
// {
// pagePath: '/pages/applicationManagement/index',
// pagePath: '/pages/app-mgr/index',
// iconPath: '/static/tabBar/applicationNo.png',
// selectedIconPath: '/static/tabBar/application.png',
// text: '',

View File

@ -0,0 +1,44 @@
<script setup lang="ts">
import { computed, ref, watch } from 'vue'
defineOptions({ name: 'MjSegment' })
const props = defineProps<{
modelValue: any
options: {
label: string
value: any
}[]
}>()
const emit = defineEmits<{
(e: 'update:modelValue', value: any): void
}>()
const value = computed({
get: () => props.modelValue,
set: val => {
emit('update:modelValue', val)
},
})
//
const values = ref([] as string[])
//
function click({ index }) {
value.value = props.options[index].value
}
watch(
() => props.options,
newVal => {
values.value = newVal.map(item => item.label)
},
{ immediate: true },
)
</script>
<template>
<tui-segmented-control :values="values" @click="click" />
</template>

View File

@ -18,17 +18,17 @@
</report-content-base>
</template>
<script setup lang="ts">
import ColumnChart from '@/pages/academicReport/components/Echart/ColumnChart.vue';
import ReportContentBase from '@/pages/inspector/student-detail/components/report-content-base.vue';
import { computed, ref } from 'vue';
import ColumnChart from '@/pages/academic-report/components/Echart/ColumnChart.vue'
import ReportContentBase from '@/pages/inspector/student-detail/components/report-content-base.vue'
import { computed, ref } from 'vue'
const props = defineProps<{
knowledgeGraphStatistics: any;
}>();
knowledgeGraphStatistics: any
}>()
const data: any = computed(() => {
if (!props.knowledgeGraphStatistics) {
return {};
return {}
}
return {
categories: ['陌生数', '熟悉数', '掌握数'],
@ -50,8 +50,8 @@ const data: any = computed(() => {
],
},
],
};
});
}
})
</script>
<style scoped lang="scss">
.chart_title {

View File

@ -1,11 +1,11 @@
import { useWebSocket } from '@vueuse/core'
import { userStore } from '@/store'
import { useUserStore } from '@/store'
import { ref } from 'vue'
import type { UseWebSocketReturn } from '@vueuse/core'
let SOCKET: UseWebSocketReturn<any>
export const useSocket = () => {
const { token, userInfo } = userStore()
const { token, userInfo } = useUserStore()
const inspectorMessage = ref<any>()
const inspectorDesktopMessage = ref([])
const desktopPath = ref<string>()

View File

@ -13,13 +13,13 @@
}
},
{
"path": "pages/academicReport/index",
"path": "pages/academic-report/index",
"style": {
"navigationBarTitleText": "学情报告"
}
},
{
"path": "pages/applicationManagement/index",
"path": "pages/app-mgr/index",
"style": {
"navigationBarTitleText": "应用管控"
}
@ -387,7 +387,7 @@
"text": "首页"
},
{
"pagePath": "pages/academicReport/index",
"pagePath": "pages/academic-report/index",
"iconPath": "static/tabBar/studyReportNo.png",
"selectedIconPath": "static/tabBar/studyReport.png",
"text": "学情报告"

View File

@ -0,0 +1,126 @@
<template>
<view class="single_box children_box">
<view class="children">
<view class="left">
<image
:src="showChild?.child?.avatar || defaultAvatar"
mode=""
class="children_avatar"
></image>
<view>
<view class="name_vip">
<text class="name">{{ showChild?.child?.name || '未绑定账号' }}</text>
<image
v-if="showChild?.child?.currentLevel"
:src="`${OSS_URL}/iconfont/VIP/VIP_${showChild?.child?.currentLevel}.png`"
class="vip"
/>
</view>
<view class="time">
<template v-if="showChild?.child?.vipEndTime">
有效期至{{
showChild?.child?.vipEndTime
? dayjs(showChild?.child?.vipEndTime).format('YYYY.MM.DD')
: '-'
}}
</template>
<template v-else>请先绑定孩子账号</template>
</view>
</view>
</view>
<view v-if="list.length > 1">
<image
:class="{ icon: true, rotate: showAllChildren }"
:src="arrow"
@click="showAllChildren = !showAllChildren"
/>
</view>
</view>
<template v-if="showAllChildren">
<view
v-for="(i, idx) in otherChildrenList"
:key="idx"
class="children"
@click="handleShowChild(i)"
>
<view class="left">
<image :src="i.child?.avatar || defaultAvatar" mode="" class="children_avatar"></image>
<view>
<view class="name_vip">
<text class="name">{{ i.child?.name || '未绑定账号' }}</text>
<image
v-if="i.child?.currentLevel"
:src="`${OSS_URL}/iconfont/VIP/VIP_${i.child?.currentLevel}.png`"
class="vip"
/>
</view>
<view class="time">
有效期至{{
i.child?.vipEndTime ? dayjs(i.child?.vipEndTime).format('YYYY.MM.DD') : '-'
}}
</view>
</view>
</view>
<image
class="icon checked_icon"
:src="
checkedId === i.child?.id
? `${OSS_URL}/iconfont/checked.png`
: `${OSS_URL}/iconfont/unchecked.png`
"
/>
</view>
</template>
<view class="children_info">
<view class="item">
乐贝<text class="level">{{ showChild?.child?.currency || '-' }}</text>
</view>
<view class="item">
钻石<text class="level">{{ showChild?.child?.diamond || '-' }}</text>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
import { ref, computed, watch } from 'vue'
import type { ChildrenType } from '@/pages/mine/interface'
import '../index.scss'
import dayjs from 'dayjs'
const props = withDefaults(
defineProps<{
list: ChildrenType[]
showItem: ChildrenType
}>(),
{
list: () => [],
showItem: () => ({}) as ChildrenType,
},
)
const emits = defineEmits(['updateShowItem'])
const otherChildrenList = computed(() =>
props.list.filter(i => i.child?.id !== props.showItem.child?.id),
)
const showChild = computed({
get() {
return props.showItem
},
set(newVal) {
emits('updateShowItem', newVal)
},
})
const OSS_URL = import.meta.env.VITE_OSS_HOST
const arrow = `${OSS_URL}/iconfont/down_arrow.png`
const defaultAvatar = `${OSS_URL}/urm/default_avatar.png`
const showAllChildren = ref(false)
const checkedId = ref<string>()
function handleShowChild(i: ChildrenType) {
checkedId.value = i.child?.id
setTimeout(() => {
showChild.value = i
}, 200)
}
</script>

View File

@ -0,0 +1,50 @@
<script lang="ts" setup>
import { onMounted, ref } from 'vue'
import { onLoad, onShow } from '@dcloudio/uni-app'
import Empty from '@/components/Empty/index.vue'
import ChildrenBox from './components/childrenBox.vue'
import type { ChildrenType } from '@/pages/mine/interface'
import './index.scss'
import { storeToRefs } from 'pinia'
import { useBadgeStore } from '@/store/badge'
import { useChildStore } from '@/store/child'
import hud from '@/utils/hud'
import { getStudentStudyDataApi } from '@/api'
const childStore = useChildStore()
const { getChildren } = childStore
const { children } = storeToRefs(childStore)
const { refreshBadge } = useBadgeStore()
const empty = ref(false)
const showChild = ref<ChildrenType>() //
onShow(() => {
Promise.all([getChildren(), refreshBadge()])
.then(() => {
if (!children.value?.length) return
showChild.value = children.value[0]
})
.finally(() => {
hud.hide()
})
//
getStudentStudyDataApi({
queryType: 1,
queryRangeFlag: 1,
})
})
onMounted(() => {
hud.loading()
})
</script>
<template>
<mj-page class="page">
<ChildrenBox :list="children" :showItem="showChild" />
<Empty v-if="empty" content="暂无学情报告数据哦~" />
</mj-page>
</template>

View File

@ -1,129 +0,0 @@
<template>
<view class="single_box children_box">
<view class="children">
<view class="left">
<image
:src="showChildren?.childAvatar ? showChildren?.childAvatar : defaultAvatar"
mode=""
class="children_avatar"
></image>
<view>
<view class="name_vip">
<text class="name">{{
showChildren?.childName ? showChildren?.childName : '未绑定账号'
}}</text>
<image
v-if="showChildren?.currentLevel"
:src="`${OSS_URL}/iconfont/VIP/VIP_${showChildren?.currentLevel}.png`"
class="vip"
/>
</view>
<view class="time">
<template v-if="showChildren?.vipEndTime">
有效期至{{
showChildren?.vipEndTime
? dayjs(showChildren?.vipEndTime).format('YYYY.MM.DD')
: '-'
}}
</template>
<template v-else>请先绑定孩子账号</template>
</view>
</view>
</view>
<view v-if="list.length > 1">
<image
:class="{ icon: true, rotate: showAllChildren }"
:src="arrow"
@click="showAllChildren = !showAllChildren"
/>
</view>
</view>
<template v-if="showAllChildren">
<view
v-for="(i, idx) in otherChildrenList"
:key="idx"
class="children"
@click="handleShowChildren(i)"
>
<view class="left">
<image
:src="i.childAvatar ? i.childAvatar : defaultAvatar"
mode=""
class="children_avatar"
></image>
<view>
<view class="name_vip">
<text class="name">{{ i.childName }}</text>
<image
v-if="i.currentLevel"
:src="`${OSS_URL}/iconfont/VIP/VIP_${i.currentLevel}.png`"
class="vip"
/>
</view>
<view class="time">
有效期至{{ i.vipEndTime ? dayjs(i.vipEndTime).format('YYYY.MM.DD') : '-' }}
</view>
</view>
</view>
<image
class="icon checked_icon"
:src="
checkedId === i.childId
? `${OSS_URL}/iconfont/checked.png`
: `${OSS_URL}/iconfont/unchecked.png`
"
/>
</view>
</template>
<view class="children_info">
<view class="item">
年级<text class="level">{{ showChildren?.gradeName || '-' }}</text>
</view>
<view class="item">
等级<text class="level">{{ showChildren?.expLevel || '-' }}</text>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
import { ref, computed } from 'vue';
import type { ChildrenType } from '@/pages/mine/interface';
import '../index.scss';
import dayjs from 'dayjs';
const props = withDefaults(
defineProps<{
list: ChildrenType[];
showItem: ChildrenType;
}>(),
{
list: () => [],
showItem: () => ({}) as ChildrenType,
},
);
const emits = defineEmits(['updateShowItem']);
const otherChildrenList = computed(() =>
props.list.filter(i => i.childId !== props.showItem.childId),
);
const showChildren = computed({
get() {
return props.showItem;
},
set(newVal) {
emits('updateShowItem', newVal);
},
});
const OSS_URL = import.meta.env.VITE_OSS_HOST;
const arrow = `${OSS_URL}/iconfont/down_arrow.png`;
const defaultAvatar = `${OSS_URL}/urm/default_avatar.png`;
const showAllChildren = ref(false);
const checkedId = ref<string>();
function handleShowChildren(i: ChildrenType) {
checkedId.value = i.childId;
setTimeout(() => {
showChildren.value = i;
}, 200);
}
</script>

View File

@ -1,89 +0,0 @@
<template>
<mj-page class="page">
<ChildrenBox
:list="childrenList"
:showItem="showChildrenInfo"
@updateShowItem="handleUpdateShowChild"
/>
<Empty v-if="empty" content="暂无学情报告数据哦~" />
</mj-page>
</template>
<script lang="ts" setup>
import { onMounted, ref } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import Empty from '@/components/Empty/index.vue'
import ChildrenBox from './components/childrenBox.vue'
import type { ChildrenType } from '@/pages/mine/interface'
import type { SubjectType } from './interface'
import './index.scss'
import { getParentBindChildApi, subjectApi } from '@/api'
import { userStore, bindApplyStore } from '@/store'
import { storeToRefs } from 'pinia'
import hud from '@/utils/hud'
import { badgeStore } from '@/store/badge'
const { userInfo } = storeToRefs(userStore())
const { refreshBadge } = badgeStore()
const childrenList = ref<ChildrenType[]>() //
const empty = ref(false)
const showChildrenInfo = ref<ChildrenType>() //
const subjectList = ref<SubjectType[]>() //
const chooseSubject = ref<SubjectType>() //
async function handleUpdateShowChild(val: ChildrenType) {
showChildrenInfo.value = val
chooseSubject.value = {
name: '',
pictureUrl: '',
subject: '',
subjectId: undefined,
}
try {
hud.loading()
await getSubject()
} finally {
hud.hide()
}
}
//
function tapFilterSubject(i: SubjectType) {
chooseSubject.value = i
}
//
async function getParentBindChild() {
const data = await getParentBindChildApi({ parentId: userInfo.value.id, size: -1 })
empty.value = data.rows?.length ? true : false
childrenList.value = data.rows
showChildrenInfo.value = data.rows?.[0]
}
//
async function getSubject() {
if (!showChildrenInfo.value?.gradeId) {
// hud.error(',')
subjectList.value = []
return
}
const data = await subjectApi(showChildrenInfo.value.gradeId)
subjectList.value = data
chooseSubject.value = data[0]
}
onShow(() => {
refreshBadge()
})
onMounted(async () => {
try {
hud.loading()
await getParentBindChild()
await getSubject()
} finally {
hud.hide()
}
})
</script>

View File

@ -143,13 +143,13 @@ import { getParentBindDeviceApi, parentDeviceCurrentLoginApi, queryAppRecordApi
import CustomPopup from '@/components/CustomPopup/index.vue'
import Empty from '@/components/Empty/index.vue'
import { userStore, bindApplyStore } from '@/store'
import { useUserStore, bindApplyStore } from '@/store'
import { storeToRefs } from 'pinia'
import db from '@/utils/db'
import { badgeStore } from '@/store/badge'
import { useBadgeStore } from '@/store/badge'
const { userInfo } = storeToRefs(userStore())
const { refreshBadge } = badgeStore()
const { userInfo } = storeToRefs(useUserStore())
const { refreshBadge } = useBadgeStore()
const OSS_URL = import.meta.env.VITE_OSS_HOST
const defaultAvatar = `${OSS_URL}/urm/default_avatar.png`
const arrow = `${OSS_URL}/iconfont/down_arrow.png`

View File

@ -108,14 +108,14 @@
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { getCurInfo, parentBindAdmin } from '@/api'
import { userStore, shareConfigStore } from '@/store'
import { useUserStore, shareConfigStore } from '@/store'
import { storeToRefs } from 'pinia'
import TipPopup from '@/components/TipPopup/index.vue'
import router from '@/router/router'
import jWeixin from 'weixin-js-sdk'
const { initWeixinShareConfig } = shareConfigStore()
const { userInfo } = storeToRefs(userStore())
const { userInfo } = storeToRefs(useUserStore())
const OSS_URL = import.meta.env.VITE_OSS_HOST
const show = ref(false)

View File

@ -1,113 +1,20 @@
<template>
<mj-page class="teacher-content">
<!-- <view v-if="equipmentList.length === 0" class="binding">
<image class="scan" :src="scanQr" mode=""></image>扫码绑定设备
</view> -->
<view class="my_equipment">
<view class="header">
<text>我的设备</text>
<image
v-if="equipmentList.length > 1"
:src="arrow"
mode=""
:class="{ arrow_icon: true, rotate: showAllDevice }"
@click="showAllDevice = !showAllDevice"
></image>
</view>
<view v-if="!showAllDevice" class="equipment_item">
<view class="equipment_item_right">
<view class="avatar">
<image
:src="
showEquipment?.curDeviceUserAvatar
? showEquipment?.curDeviceUserAvatar
: defaultAvatar
"
mode=""
></image>
</view>
<view class="right">
<text class="nickname">{{
showEquipment?.curDeviceUserName ? showEquipment?.curDeviceUserName : '未登录账号'
}}</text>
<text class="number">序列号{{ showEquipment?.simSerialNumber }}</text>
</view>
</view>
</view>
<template v-else>
<view
v-for="(i, idx) in equipmentList"
:key="idx"
class="equipment_item"
@click="chooseEquipment(i)"
>
<view class="equipment_item_right">
<view class="avatar">
<image
:src="i?.curDeviceUserAvatar ? i?.curDeviceUserAvatar : defaultAvatar"
mode=""
></image>
</view>
<view class="right">
<text class="nickname">{{
i?.curDeviceUserName ? i?.curDeviceUserName : '未登录账号'
}}</text>
<text class="number">序列号{{ i?.simSerialNumber }}</text>
</view>
</view>
<view class="equipment_item_left">
<image :src="i.id === showEquipment.id ? checked : unchecked" mode=""></image>
</view>
</view>
</template>
<view class="binding">
<view class="scan-bind" @click="scanBind">
<image class="scan" :src="scanQr" mode=""></image>扫码绑定
</view>
<view class="hand-bind" @click="handBind"> 手动绑定 </view>
</view>
</view>
<view class="home_function">
<template v-for="(i, idx) in functionList" :key="idx">
<view class="item" @click="change(i)">
<image class="item_bg" :src="i.developing ? i.developingBg : i.bg" mode=""></image>
</view>
</template>
</view>
<!-- 手动绑定弹框 -->
<mj-dialog
v-model="showHandBindPopup"
title="手动绑定孩子账号"
okText="绑定"
:ok="confirmHandBind"
>
<input
v-model="childAccount"
class="account-input"
placeholder="请输入孩子账号"
type="text"
/>
</mj-dialog>
</mj-page>
</template>
<script setup lang="ts">
import { onMounted, reactive, ref } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import { onLoad, onShow } from '@dcloudio/uni-app'
import { userStore } from '@/store'
import { useUserStore } from '@/store'
import { storeToRefs } from 'pinia'
import hud from '@/utils/hud'
import router from '@/router/router'
import { badgeStore } from '@/store/badge'
import { getParentBindDeviceApi, manualBindApi } from '@/api'
import { useBadgeStore } from '@/store/badge'
import { manualBindApi } from '@/api'
import { useDeviceStore } from '@/store/device'
const store = userStore()
const { getUserInfo } = store
const { userInfo } = storeToRefs(userStore())
const { refreshBadge } = badgeStore()
const { getUserInfo } = useUserStore()
const deviceStore = useDeviceStore()
const { getDevices } = deviceStore
const { devices } = storeToRefs(deviceStore)
const { refreshBadge } = useBadgeStore()
const OSS_URL = import.meta.env.VITE_OSS_HOST
const scanQr = `${OSS_URL}/iconfont/scan_qr.png`
const arrow = `${OSS_URL}/iconfont/down_arrow.png`
@ -150,12 +57,11 @@ const functionList = reactive([
// type: 3,
// },
])
const equipmentList = ref([])
const showEquipment = ref()
const showDevice = ref()
//
function chooseEquipment(i) {
showEquipment.value = i
function chooseEquipment(i: any) {
showDevice.value = i
}
//
@ -194,12 +100,7 @@ function confirmHandBind() {
//
function change(i: any) {
if (equipmentList.value.length === 0) {
// uni.showToast({
// title: '',
// icon: 'none',
// duration: 2000,
// });
if (devices.value.length === 0) {
router.navigateTo('/pages/home/bindDevice/index')
return
}
@ -210,7 +111,7 @@ function change(i: any) {
router.navigateTo({
path: '/pages/home/LockScreen/index',
query: {
simSerialNumber: showEquipment.value.simSerialNumber,
simSerialNumber: showDevice.value.simSerialNumber,
},
})
break
@ -219,7 +120,7 @@ function change(i: any) {
router.navigateTo({
path: '/pages/home/DesktopPreview/index',
query: {
simSerialNumber: showEquipment.value.simSerialNumber,
simSerialNumber: showDevice.value.simSerialNumber,
},
})
break
@ -228,7 +129,7 @@ function change(i: any) {
router.navigateTo({
path: '/pages/home/TimeManagement/index',
query: {
simSerialNumber: showEquipment.value.simSerialNumber,
simSerialNumber: showDevice.value.simSerialNumber,
},
})
break
@ -238,31 +139,103 @@ function change(i: any) {
break
}
}
//
async function getParentBindDevice() {
hud.load({
task: async () => {
equipmentList.value = []
showEquipment.value = {}
const data = await getParentBindDeviceApi({ parentId: userInfo.value.id, size: -1 })
if (data.rows?.length > 0) {
equipmentList.value = data.rows
showEquipment.value = data.rows[0]
}
},
})
}
onShow(() => {
refreshBadge()
Promise.all([getDevices(), refreshBadge()])
.then(() => {
if (!devices.value?.length) return
showDevice.value = devices.value[0]
})
.finally(() => {
hud.hide()
})
})
onMounted(async () => {
onLoad(async () => {
await getUserInfo()
getParentBindDevice()
})
onMounted(() => {
hud.loading()
})
</script>
<template>
<mj-page class="teacher-content">
<!-- <view v-if="equipmentList.length === 0" class="binding">
<image class="scan" :src="scanQr" mode=""></image>扫码绑定设备
</view> -->
<view class="my_equipment">
<view class="header">
<text>我的设备</text>
<image
v-if="devices.length > 1"
:src="arrow"
mode=""
:class="{ arrow_icon: true, rotate: showAllDevice }"
@click="showAllDevice = !showAllDevice"
></image>
</view>
<view v-if="!showAllDevice && devices.length" class="equipment_item">
<view class="equipment_item_right">
<view class="avatar">
<image :src="showDevice?.lastLoginUser?.avatar || defaultAvatar" mode=""></image>
</view>
<view class="right">
<text class="nickname">{{ showDevice?.lastLoginUser?.name || '未登录账号' }}</text>
<text class="number">序列号{{ showDevice?.simSerialNumber }}</text>
</view>
</view>
</view>
<template v-else>
<view
v-for="(i, idx) in devices"
:key="idx"
class="equipment_item"
@click="chooseEquipment(i)"
>
<view class="equipment_item_right">
<view class="avatar">
<image :src="i.lastLoginUser?.avatar || defaultAvatar" mode=""></image>
</view>
<view class="right">
<text class="nickname">{{ i.lastLoginUser?.name || '未登录账号' }}</text>
<text class="number">序列号{{ i.simSerialNumber }}</text>
</view>
</view>
<view class="equipment_item_left">
<image :src="i.id === showDevice.id ? checked : unchecked" mode=""></image>
</view>
</view>
</template>
<view class="binding">
<view class="scan-bind" @click="scanBind">
<image class="scan" :src="scanQr" mode=""></image>扫码绑定
</view>
<view class="hand-bind" @click="handBind"> 手动绑定 </view>
</view>
</view>
<view class="home_function">
<template v-for="(i, idx) in functionList" :key="idx">
<view class="item" @click="change(i)">
<image class="item_bg" :src="i.developing ? i.developingBg : i.bg" mode=""></image>
</view>
</template>
</view>
<!-- 手动绑定弹框 -->
<mj-dialog
v-model="showHandBindPopup"
title="手动绑定孩子账号"
okText="绑定"
:ok="confirmHandBind"
>
<input v-model="childAccount" class="single-input" placeholder="请输入孩子账号" type="text" />
</mj-dialog>
</mj-page>
</template>
<style scoped lang="scss">
.teacher-content {
padding: 30rpx 30rpx 120rpx 30rpx;
@ -411,21 +384,4 @@ onMounted(async () => {
}
}
}
//
.account-input {
width: 100%;
height: 80rpx;
border: 1px solid #e0e0e0;
border-radius: 8rpx;
padding: 0 20rpx;
font-size: 28rpx;
box-sizing: border-box;
color: #333;
&::placeholder {
color: #ccc;
}
}
</style>

View File

@ -58,7 +58,7 @@ import type { UserInfo } from '@/types/inspector/mine'
import { queryUserRoles, switchRole } from '@/api/inspector/mine'
import { getAvatarUrl } from '@/utils'
import { getAuthUrlApi } from '@/api/login'
import { userStore } from '@/store'
import { useUserStore } from '@/store'
import db from '@/utils/db'
const roleRef = ref(null)
@ -67,7 +67,7 @@ const userInfo = inject<Ref<UserInfo>>('userInfo')
const roleList = ref<any[]>([])
const { getUserInfo: getLoginUserInfo } = userStore()
const { getUserInfo: getLoginUserInfo } = useUserStore()
const queryRole = async () => {
const data = await queryUserRoles({ phone: userInfo.value.phone })

View File

@ -181,8 +181,8 @@
</view>
</template>
<script setup lang="ts">
import { userStore } from '@/store'
const { logout } = userStore()
import { useUserStore } from '@/store'
const { logout } = useUserStore()
import { onLaunch, onShow, onHide, onLoad } from '@dcloudio/uni-app'
import { usepersonalStoreHook } from '@/store/inspector'
import Picker from '../releasePlan/components/picker.vue'

View File

@ -34,50 +34,50 @@
</template>
<script setup lang="ts">
import { inject, provide, ref, watchEffect } from 'vue';
import { inject, provide, ref, watchEffect } from 'vue'
// import StudySituationReportContent from '@/pages/inspector/student-detail/components/study-situation-report-content.vue';
// import VideoStudySummary from '@/pages/inspector/student-detail/components/study-situation-report/video-study-summary.vue';
// import KnowledgeAtlas from '@/pages/inspector/student-detail/components/study-situation-report/knowledge-atlas.vue';
// import ReviewQuestionsSummary from '@/pages/inspector/student-detail/components/study-situation-report/review-questions-summary.vue';
// import LanguageSenseLearningStatistics from '@/pages/inspector/student-detail/components/study-situation-report/language-sense-learning-statistics.vue';
import { getSubjectListByGrade } from '@/api/inspector/student-detail';
import { getSubjectListByGrade } from '@/api/inspector/student-detail'
import StudyTime from '@/pages/academicReport/components/studyTime.vue';
import VideoTime from '@/pages/academicReport/components/videoTime.vue';
import KnowledgeChart from '@/pages/academicReport/components/knowledgeChart.vue';
import ExerciseStatistics from '@/pages/academicReport/components/exerciseStatistics.vue';
import LanguageStatistics from '@/pages/academicReport/components/languageStatistics.vue';
import '@/pages/academicReport/index.scss';
const studentInfo = inject('studentInfo', null);
import StudyTime from '@/pages/academic-report/components/studyTime.vue'
import VideoTime from '@/pages/academic-report/components/videoTime.vue'
import KnowledgeChart from '@/pages/academic-report/components/knowledgeChart.vue'
import ExerciseStatistics from '@/pages/academic-report/components/exerciseStatistics.vue'
import LanguageStatistics from '@/pages/academic-report/components/languageStatistics.vue'
import '@/pages/academic-report/index.scss'
const studentInfo = inject('studentInfo', null)
//
const subjectList = ref<any[]>([]);
const subjectList = ref<any[]>([])
// id
const currentSubjectId = ref<number>(0);
const loading = ref(true);
const currentSubjectId = ref<number>(0)
const loading = ref(true)
provide('studySituationParam', {
subjectId: currentSubjectId,
userId: studentInfo.value.userId,
});
})
const querySubjectList = async () => {
loading.value = true;
loading.value = true
try {
const data = await getSubjectListByGrade(studentInfo.value.gradeId);
const data = await getSubjectListByGrade(studentInfo.value.gradeId)
subjectList.value = data.map((item: any) => ({
name: item.name,
value: item.subjectId,
}));
currentSubjectId.value = data[0].subjectId;
}))
currentSubjectId.value = data[0].subjectId
} finally {
loading.value = false;
loading.value = false
}
};
}
watchEffect(() => {
if (studentInfo.value.gradeId) {
querySubjectList();
querySubjectList()
}
});
})
</script>
<style scoped lang="scss">

View File

@ -55,14 +55,14 @@
import { ref, computed, nextTick, onMounted } from 'vue'
import { sendCodeMessageApi, smsLoginApi, wxLoginApi } from '@/api'
import { userStore } from '@/store'
import { useUserStore } from '@/store'
import router from '@/router/router'
import db from '@/utils/db'
import hud from '@/utils/hud'
const emits = defineEmits(['reloadFun'])
const OSS_URL = import.meta.env.VITE_OSS_HOST
const { getUserInfo, setToken } = userStore()
const { getUserInfo, setToken } = useUserStore()
const formatPhone = ref('')
const verifyCode = ref('')

View File

@ -0,0 +1,93 @@
<script setup lang="ts">
import { createRechargeOrderApi } from '@/api'
import hud from '@/utils/hud'
import num from '@/utils/num'
import { computed, ref } from 'vue'
defineOptions({ name: 'Recharge' })
const props = defineProps<{
modelValue: boolean
}>()
const emit = defineEmits<{
(e: 'update:modelValue', value: boolean): void
}>()
const show = computed({
get: () => props.modelValue,
set: val => {
emit('update:modelValue', val)
},
})
//
const defaultMoneyType = 10
const customMoneyType = 0
const moneyTypeOptions = [
{
label: '10元',
value: defaultMoneyType,
},
{
label: '50元',
value: '50',
},
{
label: '100元',
value: '100',
},
{
label: '自定义',
value: customMoneyType,
},
]
const moneyType = ref(defaultMoneyType as any)
const customMoney = ref('')
//
async function ok() {
let money = moneyType.value
if (moneyType.value === customMoneyType) {
money = customMoney.value
}
money = Number(money)
if (Number.isNaN(money) || money < defaultMoneyType || !num.isPositiveInt(num.toFixed2(money))) {
hud.error(`请输入大于等于${defaultMoneyType}的正整数金额`)
return
}
//
const data = await createRechargeOrderApi(money)
console.log(data)
}
</script>
<template>
<mj-dialog v-model="show" title="乐贝充值" :ok="ok">
<view class="title">请选择充值金额(1=100乐贝)</view>
<mj-segment v-model="moneyType" :options="moneyTypeOptions" />
<input
class="single-input"
v-if="moneyType == customMoneyType"
type="number"
v-model="customMoney"
placeholder="请输入自定义金额"
/>
</mj-dialog>
</template>
<style lang="scss" scoped>
.title {
display: flex;
justify-content: center;
align-items: center;
height: 60rpx;
font-size: 30rpx;
margin-bottom: 40rpx;
}
.single-input {
margin-top: 30rpx;
}
</style>

View File

@ -73,8 +73,8 @@ import { storeToRefs } from 'pinia'
import Empty from '@/components/Empty/index.vue'
import CustomPopup from '@/components/CustomPopup/index.vue'
import { updateTaskStatusApi, getParentBindChildApi, getFamilyTaskStuApi } from '@/api'
import { userStore } from '@/store'
const { userInfo } = storeToRefs(userStore())
import { useUserStore } from '@/store'
const { userInfo } = storeToRefs(useUserStore())
const OSS_URL = import.meta.env.VITE_OSS_HOST
const pageStatus = ref('')
const taskType = ref() // 0- 1-

View File

@ -1,82 +1,11 @@
<template>
<view class="about_mine">
<view class="container">
<view class="navigate">
<text
v-for="(i, idx) in navList"
:key="idx"
:class="{ navigate_item: true, active: activeNav.type === i.type }"
@click="switchNav(i)"
>{{ i.title }}</text
>
</view>
<scroll-view scroll-y class="scroll-container">
<view v-if="noData" class="empty_box">
<image :src="empty" mode="" class="empty"></image>
<view class="tip">您还没有绑定任何数据快去扫码绑定吧</view>
<button class="btn" @click="handleBindDevice">扫码绑定</button>
</view>
<view v-else class="content">
<!-- 我的孩子 -->
<template v-if="activeNav.type === nav_child">
<view
v-for="(i, idx) in childList"
:key="idx"
class="children"
@click="handleDetailPage(activeNav.type, i)"
>
<image
:src="i?.childAvatar ? i?.childAvatar : defaultAvatar"
mode=""
class="children_avatar"
></image>
<view class="children-info">
<view class="name_vip">
<text class="name">{{ i.childName }}</text>
<image
v-if="i.currentLevel"
:src="`${OSS_URL}/iconfont/VIP/VIP_${i.currentLevel}.png`"
class="vip"
/>
</view>
<view class="time">
有效期至{{ i.vipEndTime ? dayjs(i.vipEndTime).format('YYYY.MM.DD') : '-' }}
</view>
</view>
<!-- <view v-if="i.havePurchasedInspector" class="service">
<wd-button class="btn" :round="false" @click.stop="toMySupervisionService(i)"
>督学服务</wd-button
>
</view> -->
</view>
</template>
<!-- 我的设备 -->
<template v-else>
<view
v-for="(i, idx) in deviceList"
:key="idx"
class="equipment"
@click="handleDetailPage(activeNav.type, i)"
>
序列号<text class="serial">{{ i.simSerialNumber }}</text>
</view>
</template>
</view>
</scroll-view>
</view>
</view>
</template>
<script setup lang="ts">
import { reactive, ref, onMounted, computed } from 'vue'
import { reactive, ref, computed } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import { getParentBindChildApi, getParentBindDeviceApi } from '@/api'
import { userStore } from '@/store'
import { storeToRefs } from 'pinia'
import router from '@/router/router'
import dayjs from 'dayjs'
const { userInfo } = storeToRefs(userStore())
import { useChildStore } from '@/store/child'
import { useDeviceStore } from '@/store/device'
const OSS_URL = import.meta.env.VITE_OSS_HOST
const defaultAvatar = `${OSS_URL}/urm/default_avatar.png`
const empty = `${OSS_URL}/empty.png`
@ -99,13 +28,19 @@ const navList = reactive([
type: nav_device,
},
])
const childList = ref<any[]>([])
const deviceList = ref<any[]>([])
//
const childStore = useChildStore()
const { getChildren } = childStore
const { children } = storeToRefs(childStore)
//
const deviceStore = useDeviceStore()
const { getDevices } = deviceStore
const { devices } = storeToRefs(deviceStore)
const noData = computed(() => {
return (
(activeNav.value.type === nav_child && childList.value.length === 0) ||
(activeNav.value.type === nav_device && deviceList.value.length === 0)
(activeNav.value.type === nav_child && children.value.length === 0) ||
(activeNav.value.type === nav_device && devices.value.length === 0)
)
})
@ -116,21 +51,9 @@ function switchNav(i: any) {
const loadData = () => {
if (activeNav.value.type === nav_child) {
getParentBindChildApi({ parentId: userInfo.value.id, current: 1, size: 1000 })
.then(res => {
childList.value = res.rows || []
})
.catch(res => {
childList.value = []
})
getChildren()
} else {
getParentBindDeviceApi({ parentId: userInfo.value.id, current: 1, size: 1000 })
.then(res => {
deviceList.value = res.rows || []
})
.catch(res => {
deviceList.value = []
})
getDevices()
}
}
//
@ -156,21 +79,85 @@ function handleBindDevice() {
router.navigateTo('/pages/home/bindDevice/index')
}
const toMySupervisionService = child => {
router.navigateTo({
path: `/pages/parents/mySupervisionService/index`,
query: { id: child.childId },
})
}
// const toMySupervisionService = child => {
// router.navigateTo({
// path: `/pages/parents/mySupervisionService/index`,
// query: { id: child.childId },
// })
// }
onShow(() => {
loadData()
})
onMounted(() => {
loadData()
})
</script>
<template>
<view class="about_mine">
<view class="container">
<view class="navigate">
<text
v-for="(i, idx) in navList"
:key="idx"
:class="{ navigate_item: true, active: activeNav.type === i.type }"
@click="switchNav(i)"
>{{ i.title }}</text
>
</view>
<scroll-view scroll-y class="scroll-container">
<view v-if="noData" class="empty_box">
<image :src="empty" mode="" class="empty"></image>
<view class="tip">您还没有绑定任何数据快去扫码绑定吧</view>
<button class="btn" @click="handleBindDevice">扫码绑定</button>
</view>
<view v-else class="content">
<!-- 我的孩子 -->
<template v-if="activeNav.type === nav_child">
<view
v-for="(i, idx) in children"
:key="idx"
class="children"
@click="handleDetailPage(activeNav.type, i)"
>
<image
:src="i?.child?.avatar || defaultAvatar"
mode=""
class="children_avatar"
></image>
<view class="children-info">
<view class="name_vip">
<text class="name">{{ i.child?.name }}</text>
<image
v-if="i.child?.currentLevel"
:src="`${OSS_URL}/iconfont/VIP/VIP_${i.child?.currentLevel}.png`"
class="vip"
/>
</view>
<view class="time">
有效期至{{
i.child?.vipEndTime ? dayjs(i.child?.vipEndTime).format('YYYY.MM.DD') : '-'
}}
</view>
</view>
</view>
</template>
<!-- 我的设备 -->
<template v-else>
<view
v-for="(i, idx) in devices"
:key="idx"
class="equipment"
@click="handleDetailPage(activeNav.type, i)"
>
序列号<text class="serial">{{ i.simSerialNumber }}</text>
</view>
</template>
</view>
</scroll-view>
</view>
</view>
</template>
<style lang="scss" scoped>
.about_mine {
.container {

View File

@ -1,49 +1,3 @@
<template>
<view class="patriarch_list">
<view class="container" :style="{ margin: `${top}rpx 30rpx 0 30rpx` }">
<view class="title">绑定家长</view>
<scroll-view scroll-y class="scroll-container">
<view v-if="dataList.length === 0" class="empty_box">
<Empty content="暂无绑定家长数据哦~" />
</view>
<view v-else class="content">
<view v-for="(i, idx) in dataList" :key="idx" class="patriarch">
<view class="left">
<image
:src="i.parentAvatar ? i.parentAvatar : defaultAvatar"
mode=""
class="patriarch_avatar"
></image>
<view>
<view class="header">
<view class="name">{{ i.parentName }}</view>
<view class="tag">
<text v-if="i.adminFlag === 1" class="tag_item manager">管理员</text>
<text class="tag_item relation">{{ useRelation1[i.identityType as keyof typeof useRelation1] }}</text>
</view>
</view>
<view class="phone"> {{ maskPhone(i.parentPhone) }} </view>
</view>
</view>
<view class="right"><button class="unbind_btn" @click="handleUnbind(i as any)">解绑</button></view>
</view>
</view>
</scroll-view>
</view>
<CustomPopup
v-model="showCustomPopup"
titleText="温馨提示"
@handleConfirmBtn="handleConfirmPopup"
>
<view class="popup_box">
<text v-if="type === 1" class="tip">
确认解绑该账号解绑后该家长<br />无法查看孩子的学情报告等数据
</text>
<text v-else class="tip"> 确认解绑该账号解绑后该家长<br />无法查看该设备的情况</text>
</view>
</CustomPopup>
</view>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
@ -64,20 +18,20 @@ const props = withDefaults(
}>(),
{
type: 1,
top: 445,
top: 0,
},
)
const OSS_URL = import.meta.env.VITE_OSS_HOST
const defaultAvatar = `${OSS_URL}/urm/default_avatar.png`
const showCustomPopup = ref(false)
const unbind = ref<ChildrenType | DeviceType>()
const dataList = ref<ChildrenType[] | DeviceType[]>([])
const dataList = ref<(ChildrenType & DeviceType)[]>([])
const detailInfo = ref<ChildrenType & DeviceType>()
const loadData = () => {
if (props.type === 1) {
getParentBindChildApi({ childId: detailInfo.value.childId, current: 1, size: 1000 })
getParentBindChildApi({ childId: detailInfo.value.child?.id, current: 1, size: 1000 })
.then(res => {
dataList.value = res.rows || []
})
@ -111,7 +65,7 @@ async function handleConfirmPopup() {
if (props.type === 1) {
await childUnBoundParentApi({
parentId: (unbind.value as any).parentId,
childId: detailInfo.value.childId,
childId: detailInfo.value.child?.id,
})
} else {
await parentUnBoundDeviceApi({
@ -128,22 +82,69 @@ onLoad(option => {
loadData()
})
</script>
<template>
<view class="patriarch_list">
<view class="container" :style="{ margin: `${top}rpx 0 0 0` }">
<view class="title">绑定家长</view>
<view class="scroll-container">
<view v-if="dataList.length === 0" class="empty_box">
<Empty content="暂无绑定家长数据哦~" />
</view>
<view v-else class="content">
<view v-for="(i, idx) in dataList" :key="idx" class="patriarch">
<view class="left">
<image
:src="i.parent?.avatar || defaultAvatar"
mode=""
class="patriarch_avatar"
></image>
<view>
<view class="header">
<view class="name">{{ i.parent.name }}</view>
<view class="tag">
<text v-if="i.adminFlag === 1" class="tag_item manager">管理员</text>
<text class="tag_item relation">{{
useRelation1[i.identityType as keyof typeof useRelation1]
}}</text>
</view>
</view>
<view class="phone"> {{ maskPhone(i.parent.phone) }} </view>
</view>
</view>
<view class="right"
><button class="unbind_btn" @click="handleUnbind(i as any)">解绑</button></view
>
</view>
</view>
</view>
</view>
<CustomPopup
v-model="showCustomPopup"
titleText="温馨提示"
@handleConfirmBtn="handleConfirmPopup"
>
<view class="popup_box">
<text v-if="type === 1" class="tip">
确认解绑该账号解绑后该家长<br />无法查看孩子的学情报告等数据
</text>
<text v-else class="tip"> 确认解绑该账号解绑后该家长<br />无法查看该设备的情况</text>
</view>
</CustomPopup>
</view>
</template>
<style lang="scss" scoped>
.patriarch_list {
height: 100vh;
.container {
height: calc(100vh - 445rpx);
display: flex;
flex-direction: column;
}
.scroll-container {
flex: 1;
height: 0;
margin-top: 20rpx;
}
.empty_box {
.empty {
margin-top: 0 !important;
@ -160,7 +161,7 @@ onLoad(option => {
}
}
.title {
margin: 0 0 10rpx 0;
margin: 0 0 10rpx 30rpx;
font-size: 30rpx;
color: #333;
font-weight: 400;

View File

@ -1,46 +1,16 @@
<template>
<view class="page">
<BackBar title="账号详情" />
<view class="children">
<view class="left">
<image :src="detailInfo.childAvatar" mode="" class="children_avatar"></image>
<view>
<view class="name_vip">
<text class="name">{{ detailInfo.childName }}</text>
<image
v-if="detailInfo.currentLevel"
:src="`${OSS_URL}/iconfont/VIP/VIP_${detailInfo.currentLevel}.png`"
class="vip"
/>
</view>
<view class="time"> 有效期至{{ dayjs(OSS_URL.vipEndTime).format('YYYY.MM.DD') }} </view>
</view>
</view>
<view class="right">
<button class="invite_btn" @click="clickShareBtn">邀请绑定</button>
</view>
</view>
<bindPatriarch :type="1" :top="304" />
<!-- 指示蒙层 -->
<view v-if="showMaskTip" class="mask" @click="showMaskTip = false">
<image :src="`${OSS_URL}/urm/invite_arrow.png`" mode="scaleToFill" class="invite_arrow" />
<text class="word">点击右上角分享给好友哦</text>
</view>
</view>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import dayjs from 'dayjs'
import bindPatriarch from './bindPatriarch.vue'
import type { ChildrenType } from '../interface'
import { userStore, shareConfigStore } from '@/store'
import { useUserStore, shareConfigStore } from '@/store'
import { storeToRefs } from 'pinia'
import { parentBindInviteApi } from '@/api'
import { onMounted } from 'vue'
const { initWeixinShareConfig } = shareConfigStore()
const { appId } = storeToRefs(shareConfigStore())
const { userInfo } = storeToRefs(userStore())
const { userInfo } = storeToRefs(useUserStore())
const detailInfo = ref<ChildrenType>({})
const OSS_URL = import.meta.env.VITE_OSS_HOST
const showMaskTip = ref(false) //
@ -79,6 +49,40 @@ onMounted(async () => {
share()
})
</script>
<template>
<view class="page">
<BackBar title="账号详情" />
<view class="children">
<view class="left">
<image :src="detailInfo.child?.avatar" mode="" class="children_avatar"></image>
<view>
<view class="name_vip">
<text class="name">{{ detailInfo.child?.name }}</text>
<image
v-if="detailInfo.child?.currentLevel"
:src="`${OSS_URL}/iconfont/VIP/VIP_${detailInfo.child?.currentLevel}.png`"
class="vip"
/>
</view>
<view class="time">
有效期至{{ dayjs(detailInfo.child?.vipEndTime).format('YYYY.MM.DD') }}
</view>
</view>
</view>
<view class="right">
<button class="invite_btn" @click="clickShareBtn">邀请绑定</button>
</view>
</view>
<bindPatriarch :type="1" />
<!-- 指示蒙层 -->
<view v-if="showMaskTip" class="mask" @click="showMaskTip = false">
<image :src="`${OSS_URL}/urm/invite_arrow.png`" mode="scaleToFill" class="invite_arrow" />
<text class="word">点击右上角分享给好友哦</text>
</view>
</view>
</template>
<style lang="scss" scoped>
.page {
// padding: 30rpx;

View File

@ -1,49 +1,3 @@
<template>
<view class="page">
<BackBar title="设备详情" />
<view class="equipment">
<view class="left">
序列号<text class="serial">{{ detailInfo.simSerialNumber }}</text>
</view>
<view class="right">
<button class="invite_btn" @click="clickShareBtn">分享设备</button>
</view>
</view>
<view class="current_login">
<view class="title">当前登录</view>
<view class="children">
<image
:src="
currentLogin?.curDeviceUserAvatar ? currentLogin?.curDeviceUserAvatar : defaultAvatar
"
mode=""
class="children_avatar"
></image>
<view>
<view class="name_vip">
<text class="name">{{
currentLogin?.curDeviceUserName ? currentLogin?.curDeviceUserName : '未登录账号'
}}</text>
<image
v-if="currentLogin?.currentLevel"
:src="`${OSS_URL}/iconfont/VIP/VIP_${currentLogin?.currentLevel}.png`"
class="vip"
/>
</view>
<view v-if="currentLogin?.vipEndTime" class="time">
有效期至{{ dayjs(currentLogin?.vipEndTime).format('YYYY.MM.DD') }}
</view>
</view>
</view>
</view>
<bindPatriarch :type="2" :top="519" />
<!-- 指示蒙层 -->
<view v-if="showMaskTip" class="mask" @click="showMaskTip = false">
<image :src="`${OSS_URL}/urm/invite_arrow.png`" mode="scaleToFill" class="invite_arrow" />
<text class="word">点击右上角分享给好友哦</text>
</view>
</view>
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { onLoad, onShareAppMessage } from '@dcloudio/uni-app'
@ -52,13 +6,13 @@ import type { DeviceType, CurrentLoginAccountType } from '../interface'
import bindPatriarch from './bindPatriarch.vue'
import { parentDeviceCurrentLoginApi, parentBindInviteApi } from '@/api'
import { userStore, shareConfigStore } from '@/store'
import { useUserStore, shareConfigStore } from '@/store'
import { storeToRefs } from 'pinia'
import dayjs from 'dayjs'
const { initWeixinShareConfig } = shareConfigStore()
const { appId } = storeToRefs(shareConfigStore())
const { userInfo } = storeToRefs(userStore())
const { userInfo } = storeToRefs(useUserStore())
const showMaskTip = ref(false)
const detailInfo = ref<DeviceType>()
const currentLogin = ref<CurrentLoginAccountType>()
@ -105,6 +59,50 @@ onLoad(option => {
detailInfo.value = JSON.parse(decodeURIComponent(option.details))
})
</script>
<template>
<view class="page">
<BackBar title="设备详情" />
<view class="equipment">
<view class="left">
序列号<text class="serial">{{ detailInfo.simSerialNumber }}</text>
</view>
<view class="right">
<button class="invite_btn" @click="clickShareBtn">分享设备</button>
</view>
</view>
<view class="current_login">
<view class="title">当前登录</view>
<view class="children">
<image
:src="currentLogin?.curDeviceUserAvatar || defaultAvatar"
mode=""
class="children_avatar"
></image>
<view>
<view class="name_vip">
<text class="name">{{ currentLogin?.curDeviceUserName || '未登录账号' }}</text>
<image
v-if="currentLogin?.currentLevel"
:src="`${OSS_URL}/iconfont/VIP/VIP_${currentLogin?.currentLevel}.png`"
class="vip"
/>
</view>
<view v-if="currentLogin?.vipEndTime" class="time">
有效期至{{ dayjs(currentLogin?.vipEndTime).format('YYYY.MM.DD') }}
</view>
</view>
</view>
</view>
<bindPatriarch :type="2" />
<!-- 指示蒙层 -->
<view v-if="showMaskTip" class="mask" @click="showMaskTip = false">
<image :src="`${OSS_URL}/urm/invite_arrow.png`" mode="scaleToFill" class="invite_arrow" />
<text class="word">点击右上角分享给好友哦</text>
</view>
</view>
</template>
<style lang="scss" scoped>
.page {
// padding: 30rpx;
@ -153,7 +151,7 @@ onLoad(option => {
}
.current_login {
.title {
margin: 40rpx 0 10rpx 0;
margin: 40rpx 0 10rpx 30rpx;
font-size: 30rpx;
color: #333;
font-weight: 400;

View File

@ -1,20 +1,15 @@
<template>
<view class="content">
<PatriarchCard />
<AboutMine />
</view>
</template>
<script setup lang="ts">
import { onShow } from '@dcloudio/uni-app'
import PatriarchCard from './patriarchCard.vue'
import AboutMine from './aboutMine.vue'
import { badgeStore } from '@/store/badge'
import PatriarchCard from './patriarch-card.vue'
import AboutMine from './about-mine.vue'
import { useUserStore, useBadgeStore } from '@/store'
const { refreshBadge } = badgeStore()
const { refreshBadge } = useBadgeStore()
const { getUserInfo } = useUserStore()
onShow(async () => {
getUserInfo()
refreshBadge()
})
</script>
@ -24,3 +19,10 @@ onShow(async () => {
padding: 30rpx;
}
</style>
<template>
<mj-page class="content">
<PatriarchCard />
<AboutMine />
</mj-page>
</template>

View File

@ -1,27 +1,35 @@
// 我的孩子
export interface ChildrenType {
createTime?: string
createUser?: string
updateTime?: string
updateUser?: string
id?: string
parentId?: string
childId?: string
adminFlag?: number
identityType?: null
bindFlag?: number
bindTime?: string
unBindTime?: string
childName?: string
childAvatar?: string
parentName?: string
parentAvatar?: string
parentPhone?: string
vipEndTime?: string
type ParentInfo = {
id?: any
name?: string
account?: string
avatar?: string
phone?: string
}
type ChildInfo = {
id?: any
name?: string
diamond?: number
currency?: number
account?: string
avatar?: string
vipEndTime?: string // yyyy-MM-dd HH:mm:ss
currentLevel?: number
gradeId?: string
gradeName?: string
expLevel?: number
gradeId?: number
gradeName?: string
}
export interface ChildrenType {
id?: number
bindFlag?: 0 | 1 // 0-未绑定 1-已绑定
bindTime?: string // yyyy-MM-dd HH:mm:ss
unBindTime?: string // yyyy-MM-dd HH:mm:ss
adminFlag?: 0 | 1 // 0-否 1-是
identityType?: 1 | 2 | 3 | 4 | 5 | 6 | 7 // 1:父亲2:母亲3:爷爷4:奶奶5:外公6:外婆7:其他
child?: ChildInfo
parent?: ParentInfo
}
// 我的设备
export interface DeviceType {

View File

@ -1,49 +1,18 @@
<template>
<view class="patriarch_card">
<view class="info" @click="handlePrefectInfo">
<image
:src="userInfo.avatarUrl ? userInfo.avatarUrl : defaultAvatar"
mode=""
class="header"
></image>
<text class="name">{{ userInfo.nickName }}</text>
<text class="identity">家长</text>
</view>
<view class="function">
<template v-for="i in list" :key="i.title">
<view v-if="i.show" class="function_item" @click="handleJump(i)">
<view class="photo_box">
<image :src="i.photo" mode="" class="photo"></image>
<text v-if="i.id === id_apply && bindApplyList?.length" class="unread">
{{ bindApplyList?.length }}
</text>
</view>
<text class="title">{{ i.title }}</text>
</view>
</template>
</view>
</view>
</template>
<script lang="ts" setup>
import { onMounted, ref } from 'vue'
import { bindApplyStore, userStore } from '@/store'
import { bindApplyStore, useUserStore } from '@/store'
import { storeToRefs } from 'pinia'
import router from '@/router/router'
import recharge from './Components/recharge/index.vue'
const { userInfo } = storeToRefs(userStore())
const { userInfo } = storeToRefs(useUserStore())
const { bindApplyList } = storeToRefs(bindApplyStore())
const id_apply = 2
const id_apply = 1
const id_recharge = 2
const OSS_URL = import.meta.env.VITE_OSS_HOST
const defaultAvatar = `${OSS_URL}/urm/default_avatar.png`
const list = ref([
// {
// title: '',
// photo: `${OSS_URL}/urm/my_scan_binding.png`,
// show: true,
// url: '/pages/home/bindDevice/index',
// },
{
id: id_apply,
title: '绑定申请',
@ -51,6 +20,18 @@ const list = ref([
show: true,
url: '/pages/mine/bindApply/index',
},
{
id: id_recharge,
title: '乐贝充值',
photo: `${OSS_URL}/urm/my_award_manage.png`,
show: true,
},
// {
// title: '',
// photo: `${OSS_URL}/urm/my_scan_binding.png`,
// show: true,
// url: '/pages/home/bindDevice/index',
// },
// {
// title: '',
// photo: `${OSS_URL}/urm/my_warranty.png`,
@ -76,10 +57,16 @@ const list = ref([
// url: '/pages/mine/awardManage/index',
// },
])
const showRecharge = ref(false)
function handleJump(i: any) {
router.navigateTo({
path: i.url,
})
if (i.url) {
router.navigateTo({
path: i.url,
})
} else if (i.id === id_recharge) {
showRecharge.value = true
}
}
//
function handlePrefectInfo() {
@ -92,6 +79,43 @@ function handlePrefectInfo() {
onMounted(() => {})
</script>
<template>
<view class="patriarch_card">
<view class="info" @click="handlePrefectInfo">
<image
:src="userInfo.avatarUrl ? userInfo.avatarUrl : defaultAvatar"
mode=""
class="header"
></image>
<view class="right">
<view class="right-top">
<text class="name">{{ userInfo.nickName }}</text>
<text class="identity">家长</text>
</view>
<view class="right-bottom">
<text
>乐贝<text class="currency">{{ userInfo.currency }}</text></text
>
</view>
</view>
</view>
<view class="function">
<template v-for="i in list" :key="i.title">
<view v-if="i.show" class="function_item" @click="handleJump(i)">
<view class="photo_box">
<image :src="i.photo" mode="" class="photo"></image>
<text v-if="i.id === id_apply && bindApplyList?.length" class="unread">
{{ bindApplyList?.length }}
</text>
</view>
<text class="title">{{ i.title }}</text>
</view>
</template>
</view>
<recharge v-model="showRecharge" />
</view>
</template>
<style lang="scss" scoped>
.patriarch_card {
padding: 30rpx;
@ -101,6 +125,7 @@ onMounted(() => {})
.info {
display: flex;
align-items: center;
gap: 30rpx;
.header {
width: 100rpx;
@ -108,23 +133,46 @@ onMounted(() => {})
border-radius: 50%;
}
.name {
display: inline-block;
margin: 0 10rpx 0 20rpx;
font-weight: 500;
font-size: 32rpx;
line-height: 48rpx;
color: rgba(51, 51, 51, 1);
}
.right {
display: flex;
flex-direction: column;
gap: 10rpx;
justify-content: ce;
.identity {
display: inline-block;
padding: 0 10rpx 0 10rpx;
background-color: rgba(93, 185, 255, 1);
border-radius: 6rpx;
font-size: 22rpx;
line-height: 30rpx;
color: #fff;
.right-top {
display: flex;
gap: 10rpx;
align-items: center;
.name {
display: inline-block;
font-weight: 500;
font-size: 32rpx;
line-height: 48rpx;
color: rgba(51, 51, 51, 1);
}
.identity {
display: block;
height: 30rpx;
padding: 0 10rpx;
background-color: rgba(93, 185, 255, 1);
border-radius: 6rpx;
font-size: 22rpx;
line-height: 30rpx;
color: #fff;
}
}
.right-bottom {
font-size: 28rpx;
line-height: 40rpx;
color: $font-aux-color;
.currency {
color: #333;
}
}
}
}

View File

@ -64,7 +64,7 @@ import { storeToRefs } from 'pinia'
import router from '@/router/router'
import CustomPopup from '@/components/CustomPopup/index.vue'
// #ifdef H5
import { h5Authorization, userStore } from '@/store'
import { h5Authorization, useUserStore } from '@/store'
import hud from '@/utils/hud'
const { authorizedLogin } = h5Authorization()
// #endif
@ -72,7 +72,7 @@ const popupRef = ref(null)
const OSS_URL = import.meta.env.VITE_OSS_HOST
const defaultAvatar = `${OSS_URL}/urm/default_avatar.png`
const arrow = `${OSS_URL}/iconfont/down_arrow.png`
const store = userStore()
const store = useUserStore()
const { logout, getUserInfo } = store
const { userInfo } = storeToRefs(store)
const indicatorStyle = ref(`height: 50px;`)

View File

@ -1,7 +1,7 @@
import db from '@/utils/db'
import type { PiniaPluginContext, StateTree, Store } from 'pinia'
const cache = ['user']
const cache = ['user', 'device', 'child']
function getStoreKey(key: string) {
return `store_${key}`

View File

@ -1,5 +1,5 @@
/* eslint-disable no-param-reassign */
import { userStore } from '@/store'
import { useUserStore } from '@/store'
import db from '../utils/db'
import page from '../utils/page'
import qs from 'qs'
@ -111,7 +111,7 @@ function beforeEach(options: RouterOptions) {
console.log('beforeEach', from, to)
// 执行跳转
const { token } = storeToRefs(userStore())
const { token } = storeToRefs(useUserStore())
if (!noTokenRoutes.includes(to.path) && !token.value) {
console.log('token 过期')
toLogin(to)

View File

@ -1,6 +1,6 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
import { userStore } from '@/store'
import { useUserStore } from '@/store'
import { bindAuthInfoApi } from '@/api'
import db from '@/utils/db'
@ -22,7 +22,7 @@ export const h5Authorization = defineStore('h5Authorization', () => {
// setCache('H5_OPENID', res.data.openId);
// setCache('H5_UNIONID', res.data.unionId);
// setCache('token', res.data.token);
const { afterLogin } = userStore()
const { afterLogin } = useUserStore()
await afterLogin()
})
} else {

View File

@ -1,7 +1,7 @@
import { defineStore } from 'pinia'
import { bindApplyStore } from './bindApply'
export const badgeStore = defineStore('badge', () => {
export const useBadgeStore = defineStore('badge', () => {
const { getApplyBindInfoList } = bindApplyStore()
async function refreshBadge() {

20
src/store/child.ts Normal file
View File

@ -0,0 +1,20 @@
import { defineStore, storeToRefs } from 'pinia'
import { useUserStore } from './user'
import { getParentBindChildApi } from '@/api'
import { ref } from 'vue'
export const useChildStore = defineStore('child', () => {
const children = ref([] as any)
const { userInfo } = storeToRefs(useUserStore())
async function getChildren() {
children.value =
(
await getParentBindChildApi({
parentId: userInfo.value?.id,
})
)?.rows || []
return children.value
}
return { children, getChildren }
})

20
src/store/device.ts Normal file
View File

@ -0,0 +1,20 @@
import { defineStore, storeToRefs } from 'pinia'
import { useUserStore } from './user'
import { getParentBindDeviceApi } from '@/api'
import { ref } from 'vue'
export const useDeviceStore = defineStore('device', () => {
const devices = ref([] as any)
const { userInfo } = storeToRefs(useUserStore())
async function getDevices() {
devices.value =
(
await getParentBindDeviceApi({
parentId: userInfo.value?.id,
})
)?.rows || []
return devices.value
}
return { devices, getDevices }
})

View File

@ -1,6 +1,9 @@
export * from './user';
export * from './global';
export * from './H5Authorization';
export * from './share';
export * from './bindApply';
export * from './socket';
export * from './user'
export * from './global'
export * from './H5Authorization'
export * from './share'
export * from './bindApply'
export * from './socket'
export * from './badge'
export * from './device'
export * from './child'

View File

@ -4,9 +4,9 @@ import { ref } from 'vue'
import { global } from './global'
import { getCache, setCache } from '@/utils'
import db from '@/utils/db'
import { userStore } from './user'
import { useUserStore } from './user'
export const socket = defineStore('socket', () => {
const { token } = storeToRefs(userStore())
const { token } = storeToRefs(useUserStore())
const socket = ref()
const status = ref(true) // true在线 false离线
let hbTimer: any

View File

@ -7,7 +7,7 @@ import { global } from '@/store'
import router from '@/router/router'
import db from '@/utils/db'
export const userStore = defineStore('user', () => {
export const useUserStore = defineStore('user', () => {
const userInfo = ref({} as any)
const token = ref('')
const { getDicts } = global()

View File

@ -44,3 +44,22 @@ button {
.custom-popup {
border-radius: 20rpx 20rpx 0 0;
}
.tui-dialog__hd {
padding: 32rpx 24rpx 16rpx !important;
}
.single-input {
width: 100%;
height: 80rpx;
border: 1px solid #e0e0e0;
border-radius: 8rpx;
padding: 0 20rpx;
font-size: 28rpx;
box-sizing: border-box;
color: #333;
&::placeholder {
color: #ccc;
}
}

47
src/utils/num.ts Normal file
View File

@ -0,0 +1,47 @@
// 数字相关
// 去掉小数点后面无用的0
function trimZero(num: any) {
if (num === undefined || num === null) return ''
let str = `${num}`
if (!str.includes('.')) {
// 没有小数点,直接返回
return str
}
while (str.endsWith('0')) {
str = str.slice(0, -1)
}
if (str.endsWith('.')) {
str = str.slice(0, -1)
}
return str
}
function toFixed2(num: any) {
num = Number(num)
if (Number.isNaN(num)) return ''
return trimZero(num.toFixed(2))
}
function toPercent(val: any) {
val = Number(val)
if (Number.isNaN(val)) return ''
return `${toFixed2(val * 100)}%`
}
/**
*
*/
function isPositiveInt(val: any) {
val = String(val)
if (val.includes('.')) return false
const numVal = Number(val)
return !Number.isNaN(numVal) && numVal > 0
}
export default {
trimZero,
toFixed2,
toPercent,
isPositiveInt,
}