diff --git a/.env b/.env index 3bfcb50..0cde6d6 100644 --- a/.env +++ b/.env @@ -2,3 +2,4 @@ TARO_APP_API=https://playedu.yaojiankang.top TARO_APP_LGOIN=true + diff --git a/.env.playedu b/.env.playedu index b5cf466..6eab0de 100644 --- a/.env.playedu +++ b/.env.playedu @@ -1 +1,2 @@ TARO_APP_API=https://playedu.yaojiankang.top +TARO_APP_LGOIN=false diff --git a/project.config.json b/project.config.json index cd682e8..d27e980 100644 --- a/project.config.json +++ b/project.config.json @@ -2,7 +2,7 @@ "miniprogramRoot": "./dist", "projectname": "video", "description": "", - "appid": "wxd8ae2141559dcf5b", + "appid": "wx703940a70f0f1be7", "setting": { "urlCheck": true, "es6": false, diff --git a/project.tt.json b/project.tt.json index a319feb..cbd6ef4 100644 --- a/project.tt.json +++ b/project.tt.json @@ -2,7 +2,7 @@ "miniprogramRoot": "Progress/", "projectname": "video", "description": "", - "appid": "wxd8ae2141559dcf5b", + "appid": "wx703940a70f0f1be7", "setting": { "urlCheck": true, "es6": false, diff --git a/src/api/manage.ts b/src/api/manage.ts index d792c0a..5b3e189 100644 --- a/src/api/manage.ts +++ b/src/api/manage.ts @@ -80,11 +80,11 @@ export const ManageApi = { putUser(id: number, data: Student) { return request(`/api/v1/user/front/${id}`, "PUT", data) }, - del(id: number) { + del(id: string) { return request(`/api/v1/user/${id}`, "DELETE") }, /** 修改学员类型 */ - setRoleType(id: number, data: setRoleTypeData) { + setRoleType(id: string, data: setRoleTypeData) { return request(`/api/v1/user/${id}`, "PUT", data) }, /** 部门 */ diff --git a/src/api/user.ts b/src/api/user.ts index 03bd5fb..25cdf06 100644 --- a/src/api/user.ts +++ b/src/api/user.ts @@ -46,6 +46,11 @@ interface HourCourse { duration: Record } +export interface StatisticsParam { + start_time: number + end_time: string +} + export const userApi = { login(code: string) { return request('/api/v1/auth/login/wechat', 'POST', {code}) @@ -81,6 +86,12 @@ export const userApi = { }, meetingSave(data: any) { return request(`/api/v1/user/meeting/save`, "POST", data) + }, + info(user_id: string) { + return request(`/api/v1/statistics/${user_id}`, "GET") + }, + /**获取指定学员指定时间学习记录 */ + statistics(user_id: string, data: StatisticsParam) { + return request<{data:Record}>(`/api/v1/statistics/statistics/${user_id}?start_time=${data.start_time}&end_time=${data.end_time}`, "GET") } - } diff --git a/src/app.config.ts b/src/app.config.ts index b12d73d..0f9b58d 100644 --- a/src/app.config.ts +++ b/src/app.config.ts @@ -64,6 +64,7 @@ export default defineAppConfig({ 'spotMeeting/spotMeeting', 'selectDep/selectDep', 'meetings/meetings', + 'userInfo/userInfo', ] } ], diff --git a/src/app.scss b/src/app.scss index 1b4a95c..8deaaf8 100644 --- a/src/app.scss +++ b/src/app.scss @@ -68,7 +68,7 @@ .mt-1 {margin-top: 10rpx} .mt-2 {margin-top: 20rpx} -.mt-3 {margin-top: 30rpx} +.mt-3 {margin-top: 30rpx !important;} .mt-4 {margin-top: 40rpx} .mt-5 {margin-top: 50rpx} .mt-6 {margin-top: 60rpx} diff --git a/src/components/lineChart/lineChart.module.scss b/src/components/lineChart/lineChart.module.scss new file mode 100644 index 0000000..ed31596 --- /dev/null +++ b/src/components/lineChart/lineChart.module.scss @@ -0,0 +1,39 @@ +.lineChart { + display: flex; + align-items: flex-end; + justify-content: left; + flex-wrap: nowrap; + height: 400px; +} + +.columnBox { + display: flex; + flex-direction: column; + align-items: center; +} + +.column { + width: 30rpx; + background: linear-gradient(180deg, #03D9B3 0%, #05BF88 100%); + border-radius: 30rpx; + margin-bottom: 20rpx; + overflow: hidden; + animation: rise 300ms ease-in-out forwards; + max-height: 0; +} + +.line { + width: 1rpx; + background: #ddd; + height: 100%; + margin-bottom: 10px; +} + +@keyframes rise { + from { + max-height: 0; + } + to { + max-height: 300rpx; + } +} diff --git a/src/components/lineChart/lineChart.tsx b/src/components/lineChart/lineChart.tsx new file mode 100644 index 0000000..f8e7780 --- /dev/null +++ b/src/components/lineChart/lineChart.tsx @@ -0,0 +1,43 @@ +import {ScrollView, View} from "@tarojs/components"; +import {FC, useEffect, useState} from "react"; +import style from './lineChart.module.scss' + +export interface lineData { + time: string + value: number +} + +interface Props { + data: lineData[] +} + +const height = 180 +const LineChart: FC = ({data}) => { + const [maxHeight, setMaxHeight] = useState(0) + const [lineChartList, setLineChartList] = useState(data) + + useEffect(() => { + setLineChartList(data) + setMaxHeight(data.reduce((pre, cur) => { + return Math.max(pre, cur.value) + }, 0)) + }, [data]) + + return ( + + + { + lineChartList.map(d => + + + + {d.time} + + ) + } + + + ) +} + +export default LineChart diff --git a/src/pages/business/videoInfo/components/catalogue.tsx b/src/pages/business/videoInfo/components/catalogue.tsx index 1157383..d3269e2 100644 --- a/src/pages/business/videoInfo/components/catalogue.tsx +++ b/src/pages/business/videoInfo/components/catalogue.tsx @@ -29,7 +29,9 @@ const Catalogue: FC = ({data, setHors, id, playId}) => { const [show, setShow] = useState(false) function jumCurHistory() { - Taro.navigateTo({url: `/pages/business/hourHistory/hourHistory?courseId=${id}&hourId=${playId}`}) + if (playId) { + Taro.navigateTo({url: `/pages/business/hourHistory/hourHistory?courseId=${id}&hourId=${playId}`}) + } } function tabChange({tab}: OnChangOpt) { @@ -94,12 +96,14 @@ const Catalogue: FC = ({data, setHors, id, playId}) => { } } + + return ( <> - {current === 0 && {data?.course.short_desc || '无'}} + {current === 0 && {data?.course.short_desc || data?.course.title}} {current === 1 && 课程目录 {data?.chapters.length ? Object.values(data?.chapters || {}).map((d, index) => @@ -134,12 +138,11 @@ const Catalogue: FC = ({data, setHors, id, playId}) => { 课程记录 - { - playId != null && - - 当前课时学习记录 - - } + + + + 当前课时学习记录 + diff --git a/src/pages/business/videoInfo/videoInfo.scss b/src/pages/business/videoInfo/videoInfo.scss index 0358040..2aa3be5 100644 --- a/src/pages/business/videoInfo/videoInfo.scss +++ b/src/pages/business/videoInfo/videoInfo.scss @@ -100,3 +100,7 @@ } } +.filter-saturate { + filter: saturate(0); +} + diff --git a/src/pages/login/login.tsx b/src/pages/login/login.tsx index fe947e1..73a0533 100644 --- a/src/pages/login/login.tsx +++ b/src/pages/login/login.tsx @@ -180,7 +180,6 @@ const Login: FC = () => { Taro.reLaunch({url: '/pages/index/index'}) } - return ( setCode(null)}> @@ -208,7 +207,7 @@ const Login: FC = () => { - {process.env.TARO_APP_LGOIN && 线下登录} + {process.env.TARO_APP_LGOIN === 'true' && 线下登录} ) } diff --git a/src/pages/manage/depAdmin/depAdmin.tsx b/src/pages/manage/depAdmin/depAdmin.tsx index afd5fcb..a0e3bdf 100644 --- a/src/pages/manage/depAdmin/depAdmin.tsx +++ b/src/pages/manage/depAdmin/depAdmin.tsx @@ -87,81 +87,11 @@ const DepAdmin: FC = () => { }) } - function delUser(id: number) { - Taro.showModal({ - title: '是否确认删除', - async success({confirm}) { - if (confirm) { - await ManageApi.del(id) - Taro.showToast({title: '删除成功'}) - await getData() - } - } - }) - } - - function userManagesSheet(user: User) { - const itemList = [ - '修改学员', - '删除学员', - "学习记录", - ] - if (user.role_type === 1) { - itemList.push('取消管理员') - } else if (user.role_type === 0) { - itemList.push('设置为管理员') - } - - Taro.showActionSheet({ - itemList, - success({tapIndex}) { - switch (tapIndex) { - case 0: - Taro.navigateTo({url: "/pages/manage/addStudent/addStudent" + (user.id ? `?id=${user.id}` : '')}) - break - case 1: - delUser(user.id) - break - case 2: - Taro.navigateTo({url: `/pages/manage/college/college?id=${user.id}&name=${user.name}`}) - break - case 3: - setRoleType(user) - break - } - }, - fail() { - } - }) - } function jumpAddStudent() { Taro.navigateTo({url: '/pages/manage/addStudent/addStudent'}) } - function setRoleType(user: User) { - if (user.role_type === 2) { - Taro.showModal({title: "禁止修改超级管理员"}) - return - } - const type = user.role_type === 0 ? 1 : 0 - Taro.showModal({ - title: "设置为" + ['学员', '管理员'][type], - async success({confirm}) { - if (confirm) { - try { - Taro.showLoading() - await ManageApi.setRoleType(user.id, {auth_id: user?.id!, role_type: type}) - Taro.hideLoading() - Taro.showToast({title: "设置成功"}) - await getData() - } catch (e) { - } - } - } - }) - } - async function addDep() { if (!depName) { Taro.showToast({title: "请认真填写", icon: "error"}) @@ -192,7 +122,7 @@ const DepAdmin: FC = () => { useEffect(() => { getData() Taro.setNavigationBarTitle({ - title: decodeURI(params.name) || '部门管理' + title: decodeURI(params.name) ?? '部门管理' }) }, []) @@ -212,7 +142,7 @@ const DepAdmin: FC = () => { key={d.id} leftImage={d.avatar} title={d.name} - onClick={() => userManagesSheet(d)} + onClick={() => Taro.navigateTo({url: '/pages/manage/userInfo/userInfo?userId=' + d.id})} content={['学员', '管理员', '超级管理员'][d.role_type]} />)} diff --git a/src/pages/manage/userInfo/components/info.tsx b/src/pages/manage/userInfo/components/info.tsx new file mode 100644 index 0000000..db7f5fc --- /dev/null +++ b/src/pages/manage/userInfo/components/info.tsx @@ -0,0 +1,42 @@ +import {FC} from "react"; +import {Image, Text, View} from "@tarojs/components"; +import styles from '../userInfo.module.scss' + +interface Props { + data: User | null +} + +const Info: FC = ({data}) => { + + + return ( + <> + + + + + {data?.name} + + {data?.is_lock ? '禁用' : '正常'} + + + {['学员', '管理员', '超级管理员'][data?.role_type || 0]} + + + + + + 学员编号 + {data?.id}号 + + + 手机号 + {data?.phone_number} + + + + ) +} + +export default Info diff --git a/src/pages/manage/userInfo/userInfo.config.ts b/src/pages/manage/userInfo/userInfo.config.ts new file mode 100644 index 0000000..98173d0 --- /dev/null +++ b/src/pages/manage/userInfo/userInfo.config.ts @@ -0,0 +1,3 @@ +export default definePageConfig({ + navigationBarTitleText: '学员详情', +}) diff --git a/src/pages/manage/userInfo/userInfo.module.scss b/src/pages/manage/userInfo/userInfo.module.scss new file mode 100644 index 0000000..11d96fc --- /dev/null +++ b/src/pages/manage/userInfo/userInfo.module.scss @@ -0,0 +1,51 @@ +.page { + padding: 15rpx; +} + +.box { + background: #fff; + border-radius: 20rpx; + padding: 20rpx; + display: flex; + align-items: center; + box-sizing: border-box; + line-height: 1.75; +} + +.image { + width: 120rpx; + height: 120rpx; + background: #ddd; + border-radius: 10rpx; + overflow: hidden; +} + +.tag { + background: #00D6AC; + font-size: 24rpx; + color: #fff; + margin-left: 20rpx; + padding: 3rpx 10rpx; + border-radius: 8rpx; + box-sizing: border-box; +} + +.tag_muted { + background: #909795; + font-size: 24rpx; + color: #fff; + margin-left: 20rpx; + padding: 3rpx 10rpx; + border-radius: 8rpx; + box-sizing: border-box; +} + +.information { + width: 100%; + color: #606563; + padding: 20rpx 0; +} + +.total{ + text-align: center; +} diff --git a/src/pages/manage/userInfo/userInfo.tsx b/src/pages/manage/userInfo/userInfo.tsx new file mode 100644 index 0000000..c8c5160 --- /dev/null +++ b/src/pages/manage/userInfo/userInfo.tsx @@ -0,0 +1,140 @@ +import {Text, View} from "@tarojs/components"; +import {FC, useEffect, useState} from "react"; +import styles from './userInfo.module.scss' +import Taro from "@tarojs/taro"; +import Info from "@/pages/manage/userInfo/components/info"; +import Tabs, {OnChangOpt, TabList} from "@/components/tabs/tabs"; +import MyButton from "@/components/button/MyButton"; +import {ManageApi, StatisticsParam, userApi} from "@/api"; +import {Profile} from "@/store"; +import {everyDay, getMonday, getSunday, monthEnd, monthFirst} from "@/utils/time"; +import LineChart from "@/components/lineChart/lineChart"; + + +const tabList: TabList[] = [ + { + title: '日', + value: { + start_time: new Date().setHours(0, 0, 0, 0), + end_time: new Date().setHours(24, 0, 0, 0) + } + }, + { + title: '周', value: { + start_time: getMonday(), + end_time: getSunday() + } + }, + { + title: '月', value: { + start_time: monthFirst(), + end_time: monthEnd() + } + }, +] + +const UserInfo: FC = () => { + const {userId} = Taro.getCurrentInstance().router?.params as { userId: string } + const [data, setData] = useState(null) + const [lineData, setLineData] = useState([]) + const {user} = Profile.useContainer() + + function setRoleType() { + if (!data) return; + if (data.role_type === 2) { + Taro.showModal({title: "禁止修改超级管理员"}) + return + } + const type = data.role_type === 0 ? 1 : 0 + Taro.showModal({ + title: "设置为" + ['学员', '管理员'][type], + async success({confirm}) { + if (confirm) { + try { + await ManageApi.setRoleType(userId, {auth_id: user?.id!, role_type: type}) + Taro.showToast({title: "设置成功"}) + setData({ + ...data, + role_type: type + }) + } catch (e) { + } + } + } + }) + } + + function delUser() { + Taro.showModal({ + title: '是否确认删除', + async success({confirm}) { + if (confirm) { + await ManageApi.del(userId) + Taro.showToast({title: '删除成功'}) + Taro.navigateBack() + } + } + }) + } + + async function getStatistics(data: StatisticsParam) { + try { + const res = await userApi.statistics(userId, data) + const everyDayValue = everyDay(data.start_time, Number(data.end_time), res.data) + setLineData(everyDayValue) + } catch (e) { + + } + } + + function tabChange({tab}: OnChangOpt) { + getStatistics(tab?.value! as StatisticsParam) + } + + useEffect(() => { + getStatistics(tabList[0].value) + userApi.info(userId).then(res => { + setData(res) + }) + }, []) + + return ( + + + + + + + 总共学习 + 121212 + 分钟 + + + + + + + { + data?.role_type !== 2 + && {['设置为管理员', '设置为学员'][data?.role_type || 0]} + } + Taro.navigateTo({url: "/pages/manage/addStudent/addStudent?id=" + userId})} + className={'mt-3'}> + 修改 + + + 删除 + + + + ) +} + +export default UserInfo diff --git a/src/utils/time.ts b/src/utils/time.ts index 7a3a857..c043e85 100644 --- a/src/utils/time.ts +++ b/src/utils/time.ts @@ -4,6 +4,8 @@ * 20:20:20 * 10:10 * */ +import {lineData} from "@/components/lineChart/lineChart"; + export function formatMinute(s: number | string): string { const time = Number(s) if (isNaN(time)) { @@ -62,3 +64,59 @@ export function formatDate(date, format) { .replace(/mm/g, preArr[min] || min) .replace(/ss/g, preArr[sec] || sec); } + + +export function getMonday(): number { + const now = new Date(); + const nowTime = now.setHours(0, 0, 0, 0); + const day = now.getDay() || 7; //为周日的时候 day 修改为7 否则当天周天会有问题 + const oneDayTime = 24 * 60 * 60 * 1000; + return nowTime - (day - 1) * oneDayTime;//显示周一 +} + + +export function getSunday(): number { + const now = new Date(); + const nowTime = now.setHours(0, 0, 0, 0); + const day = now.getDay() || 7; //为周日的时候 day 修改为7 否则当天周天会有问题 + const oneDayTime = 24 * 60 * 60 * 1000; + return nowTime + (7 - day) * oneDayTime +} + +export function monthFirst(): number { + const data = new Date(); + data.setDate(1); + return data.setHours(0, 0, 0, 0) +} + +export function monthEnd(): number { + const data = new Date(); + if (data.getMonth() === 11) { + data.setMonth(0); + } else { + data.setMonth(data.getMonth() + 1); + } + data.setDate(1); + data.setHours(0); + data.setSeconds(0); + data.setMinutes(0); + return (parseInt(String(data.getTime() / 1000)) - 1) * 1000; +} + +export function everyDay(start_time: number, end_time: number, data: Record): lineData[] { + const time = 86400000 + try { + const days = Math.floor((end_time - start_time) / time) + if (isNaN(days)) return []; + return new Array(days === 1 ? 1 : days + 1).fill(0).map((_, index) => { + const day = start_time + index * time + return { + time: formatDateTime(new Date(day), 'MM-dd'), + value: data?.[String(day)] || 0 + } + }) + + } catch (e) { + return [] + } +}