一师一策调整
This commit is contained in:
parent
05e789ebb4
commit
5580fc4c5c
@ -117,4 +117,18 @@ export function rwzxBatchCreateApi(params: any) {
|
||||
*/
|
||||
export function getTeacherTasksApi(params: any) {
|
||||
return get('/api/rwzx/getTeacherTasks', params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新任务评价
|
||||
*/
|
||||
export function updateEvaluationApi(params: any) {
|
||||
return post('/api/rwzx/updateEvaluation', params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 积分统计:按学期统计每位教师的积分汇总
|
||||
*/
|
||||
export function getScoreStatisticsApi(params: any) {
|
||||
return get('/api/rwzx/getScoreStatistics', params);
|
||||
}
|
||||
@ -13,4 +13,9 @@ export const xxtsListApi = async (params: any) => {
|
||||
// 添加更新消息推送
|
||||
export const xxtsSaveApi = async (params: any) => {
|
||||
return await post("/api/xxts/save", params);
|
||||
};
|
||||
|
||||
// 更新消息状态为已阅
|
||||
export const xxtsUpdateDbZtApi = async (id: string) => {
|
||||
return await post("/api/xxts/updateDbZt", id);
|
||||
};
|
||||
822
src/pages.json
822
src/pages.json
@ -185,6 +185,13 @@
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/view/routine/yishiyice/kcxxpj",
|
||||
"style": {
|
||||
"navigationBarTitleText": "作品评价",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/view/routine/yishiyice/zy/index",
|
||||
"style": {
|
||||
@ -234,6 +241,27 @@
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/view/routine/yishiyice/jf/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "学习积分",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/view/routine/yishiyice/jf/kcxxpj",
|
||||
"style": {
|
||||
"navigationBarTitleText": "学习详情",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/view/routine/yishiyice/jf/pjtj",
|
||||
"style": {
|
||||
"navigationBarTitleText": "积分统计",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/view/routine/yishiyice/success",
|
||||
"style": {
|
||||
@ -989,796 +1017,4 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/hr/teacherProfile/ExperienceInfo",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "学习及工作经历",
|
||||
|
||||
"enablePullDownRefresh": false
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/hr/teacherProfile/FamilyInfo",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "家庭成员情况",
|
||||
|
||||
"enablePullDownRefresh": false
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/hr/teacherProfile/EmergencyContact",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "紧急联系人",
|
||||
|
||||
"enablePullDownRefresh": false
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/routine/JiFenPingJia/PersonalHonor",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "个人荣誉",
|
||||
|
||||
"enablePullDownRefresh": false
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/routine/JiFenPingJia/PublicClassAwards",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "公开课获奖",
|
||||
|
||||
"enablePullDownRefresh": false
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/hr/teacherProfile/RecordMaterials",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "备案资料",
|
||||
|
||||
"enablePullDownRefresh": false
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/hr/salarySlip/detail",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "工资条详情",
|
||||
|
||||
"enablePullDownRefresh": false
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/homeSchool/parentAddressBook/index",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "家长通讯录",
|
||||
|
||||
"enablePullDownRefresh": false
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/homeSchool/parentAddressBook/detail",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "学生详情",
|
||||
|
||||
"enablePullDownRefresh": false
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/notice/index",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "发布接龙",
|
||||
|
||||
"enablePullDownRefresh": false
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/notice/detail",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "接龙详情",
|
||||
|
||||
"enablePullDownRefresh": false
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/notice/publish",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "接龙推送",
|
||||
|
||||
"enablePullDownRefresh": false
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/notice/push-list",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "推送清单",
|
||||
|
||||
"enablePullDownRefresh": false
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/notice/selectStudents",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "选择学生",
|
||||
|
||||
"enablePullDownRefresh": false
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/routine/RengJiaoRengZhi/index",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "任教任职",
|
||||
|
||||
"enablePullDownRefresh": false
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/routine/GongZuoLiang/index",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "工作量",
|
||||
|
||||
"enablePullDownRefresh": false
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/routine/xk/xkList",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "选课列表",
|
||||
|
||||
"enablePullDownRefresh": false
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/routine/xk/xkkcDetail",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "选课课程详情"
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/routine/xk/dmIndex",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "点名选课列表"
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/routine/xk/dm",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "学生点名",
|
||||
|
||||
"enablePullDownRefresh": false
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/routine/xk/dmList",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "点名列表",
|
||||
|
||||
"enablePullDownRefresh": false
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/routine/xk/dmXsList",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "点名学生列表",
|
||||
|
||||
"enablePullDownRefresh": false
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/routine/xk/tf/detail",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "选课退费详情",
|
||||
|
||||
"enablePullDownRefresh": false
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/routine/xk/tf/sp",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "选课退费审批",
|
||||
|
||||
"enablePullDownRefresh": false
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/routine/kefuxuncha/xcRecord",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "选课巡查记录"
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/routine/kefuxuncha/kyRecord",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "课业巡查记录"
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/base/xs/qj/sp",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "学生请假审批",
|
||||
|
||||
"enablePullDownRefresh": false
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/base/xs/qj/detail",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "学生请假详情",
|
||||
|
||||
"enablePullDownRefresh": false
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/analysis/xs/studentArchive",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "学生档案",
|
||||
|
||||
"enablePullDownRefresh": false
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/analysis/xk/xkCourse",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "课程明单",
|
||||
|
||||
"enablePullDownRefresh": false
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/analysis/xk/xkList",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "选课清单",
|
||||
|
||||
"enablePullDownRefresh": false
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/analysis/xk/dmStatistics",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "点名统计",
|
||||
|
||||
"enablePullDownRefresh": false
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/analysis/xk/dmXkList",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "点名选课列表",
|
||||
|
||||
"enablePullDownRefresh": false
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/routine/qd/index",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "签到发布"
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/routine/qd/publish",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "新增签到"
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/routine/qd/push-list",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "推送清单"
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/routine/qd/detail",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "签到详情"
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/routine/qd/selectTeachers",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "选择教师"
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/routine/qd/qr-code",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "签到二维码",
|
||||
|
||||
"navigationStyle": "custom"
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/routine/qd/confirm",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "确认签到",
|
||||
|
||||
"navigationStyle": "custom"
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/routine/JiaoXueZiYuan/add-resource",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "上传资源",
|
||||
|
||||
"enablePullDownRefresh": false,
|
||||
|
||||
"navigationBarBackgroundColor": "#ffffff",
|
||||
|
||||
"navigationBarTextStyle": "black",
|
||||
|
||||
"backgroundColor": "#f4f5f7"
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/routine/jc/bzList",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "就餐标准列表"
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/routine/jc/index",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "就餐点名"
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/routine/jc/detail",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "就餐点名详情"
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/quantitativeAssessment/assessment/assessment",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "考核评价",
|
||||
|
||||
"enablePullDownRefresh": false
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/quantitativeAssessment/casualShot/casualShot",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "随手拍",
|
||||
|
||||
"enablePullDownRefresh": false
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/quantitativeAssessment/distribute/distribute",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "分配",
|
||||
|
||||
"enablePullDownRefresh": false
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/quantitativeAssessment/index/index",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "量化考核首页",
|
||||
|
||||
"enablePullDownRefresh": false
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/quantitativeAssessment/index/details",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "量化考核详情",
|
||||
|
||||
"enablePullDownRefresh": false
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/quantitativeAssessment/quantitativeSummary/quantitativeSummary",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "量化汇总",
|
||||
|
||||
"enablePullDownRefresh": false
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/quantitativeAssessment/index/noticeAnnouncement",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "通知公告",
|
||||
|
||||
"enablePullDownRefresh": false
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/view/quantitativeAssessment/index/playPage",
|
||||
|
||||
"style": {
|
||||
|
||||
"navigationBarTitleText": "播放页面",
|
||||
|
||||
"enablePullDownRefresh": false
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
],
|
||||
|
||||
"globalStyle": {
|
||||
|
||||
"navigationBarTextStyle": "black",
|
||||
|
||||
"navigationBarTitleText": "uni-app",
|
||||
|
||||
"navigationBarBackgroundColor": "#fff",
|
||||
|
||||
"backgroundColor": "#F8F8F8",
|
||||
|
||||
"orientation": "portrait",
|
||||
|
||||
"navigationStyle": "custom",
|
||||
|
||||
"app-plus": {
|
||||
|
||||
"background": "#efeff4",
|
||||
|
||||
"titleView": false
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
"tabBar": {
|
||||
|
||||
"selectedColor": "#447ade",
|
||||
|
||||
"color": "#999999",
|
||||
|
||||
"borderStyle": "black",
|
||||
|
||||
"backgroundColor": "#ffffff",
|
||||
|
||||
"list": [
|
||||
|
||||
{
|
||||
|
||||
"text": "消息",
|
||||
|
||||
"pagePath": "pages/base/message/index",
|
||||
|
||||
"iconPath": "static/tabBar/x1.png",
|
||||
|
||||
"selectedIconPath": "static/tabBar/1.png"
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"text": "自助服务",
|
||||
|
||||
"pagePath": "pages/base/service/index",
|
||||
|
||||
"iconPath": "static/tabBar/x2.png",
|
||||
|
||||
"selectedIconPath": "static/tabBar/2.png"
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"text": "我的",
|
||||
|
||||
"pagePath": "pages/base/mine/index",
|
||||
|
||||
"iconPath": "static/tabBar/x3.png",
|
||||
|
||||
"selectedIconPath": "static/tabBar/3.png"
|
||||
|
||||
}
|
||||
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -20,19 +20,25 @@
|
||||
</view>
|
||||
</template>
|
||||
<template #default="{ data }">
|
||||
<view class="white-bg-color r-md p-15 mb-15 flex-row no-side-margin" @click="goToDetail(data)">
|
||||
<view class="card-left">
|
||||
<view class="card-title">{{ data.xxbt }}</view>
|
||||
<view class="card-desc" v-html="data.xxzy"></view>
|
||||
<view class="card-meta">
|
||||
<text>{{ data.xxtstime }}</text>
|
||||
<text>{{ getTimeAgo(data.xxtstime) }}</text>
|
||||
<view class="white-bg-color r-md p-15 mb-15 flex-row no-side-margin message-card-wrapper">
|
||||
<view class="card-content" @click="goToDetail(data)">
|
||||
<view class="card-left">
|
||||
<view class="card-title">{{ data.xxbt }}</view>
|
||||
<view class="card-desc" v-html="data.xxzy"></view>
|
||||
<view class="card-meta">
|
||||
<text>{{ data.xxtstime }}</text>
|
||||
<text>{{ getTimeAgo(data.xxtstime) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="card-right">
|
||||
<view class="tag" :class="getTagClass(data.xxlx)">
|
||||
{{ getTagText(data.xxlx) }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="card-right">
|
||||
<view class="tag" :class="getTagClass(data.xxlx)">
|
||||
{{ getTagText(data.xxlx) }}
|
||||
</view>
|
||||
<!-- 删除图标 -->
|
||||
<view class="delete-icon" @click.stop="handleDelete(data)">
|
||||
<text class="delete-text">×</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
@ -43,7 +49,7 @@
|
||||
<script lang="ts" setup>
|
||||
import {ref, onMounted, onActivated} from "vue";
|
||||
import { useLayout } from "@/components/BasicListLayout/hooks/useLayout";
|
||||
import { xxtsListApi } from "@/api/base/xxtsApi";
|
||||
import { xxtsListApi, xxtsUpdateDbZtApi } from "@/api/base/xxtsApi";
|
||||
import { getTimeAgo } from "@/utils/dateUtils";
|
||||
import { useUserStore } from "@/store/modules/user";
|
||||
import { useDataStore } from "@/store/modules/data";
|
||||
@ -115,11 +121,48 @@ onActivated(() => {
|
||||
|
||||
const goToDetail = (data: any) => {
|
||||
if (data && data.id) {
|
||||
setDb(data);
|
||||
setXxts(data); // 同时设置xxts数据,以便兼容微信工作号跳转
|
||||
uni.navigateTo({
|
||||
url: data.mobileUrl
|
||||
});
|
||||
// 检查mobileUrl是否为空
|
||||
if (!data.mobileUrl || data.mobileUrl.trim() === '') {
|
||||
// 弹出确认对话框
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '信息是否已阅?',
|
||||
success: async (res) => {
|
||||
if (res.confirm) {
|
||||
// 用户点击"是",调用更新接口
|
||||
try {
|
||||
const result = await xxtsUpdateDbZtApi(data.id);
|
||||
if (result && result.resultCode === 1) {
|
||||
uni.showToast({
|
||||
title: '已标记为已阅',
|
||||
icon: 'success'
|
||||
});
|
||||
// 刷新列表数据
|
||||
fetchListData(currentTab.value);
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: result?.message || '更新失败',
|
||||
icon: 'error'
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('更新消息状态失败:', error);
|
||||
uni.showToast({
|
||||
title: '更新失败',
|
||||
icon: 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// mobileUrl不为空,正常跳转
|
||||
setDb(data);
|
||||
setXxts(data); // 同时设置xxts数据,以便兼容微信工作号跳转
|
||||
uni.navigateTo({
|
||||
url: data.mobileUrl
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -146,6 +189,49 @@ const getTagText = (xxlx: string) => {
|
||||
// 如果xxlx有值,直接显示;否则显示默认文本
|
||||
return xxlx || '通知';
|
||||
};
|
||||
|
||||
// 删除消息
|
||||
const handleDelete = async (data: any) => {
|
||||
if (!data || !data.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 弹出确认对话框
|
||||
uni.showModal({
|
||||
title: '确认删除',
|
||||
content: '确定要删除这条消息吗?',
|
||||
confirmText: '删除',
|
||||
cancelText: '取消',
|
||||
confirmColor: '#ef4444',
|
||||
success: async (res) => {
|
||||
if (res.confirm) {
|
||||
try {
|
||||
// 调用删除接口
|
||||
const result = await xxtsUpdateDbZtApi(data.id);
|
||||
if (result && result.resultCode === 1) {
|
||||
uni.showToast({
|
||||
title: '删除成功',
|
||||
icon: 'success'
|
||||
});
|
||||
// 刷新列表数据
|
||||
fetchListData(currentTab.value);
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: result?.message || '删除失败',
|
||||
icon: 'error'
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('删除消息失败:', error);
|
||||
uni.showToast({
|
||||
title: '删除失败',
|
||||
icon: 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@ -283,6 +369,54 @@ const getTagText = (xxlx: string) => {
|
||||
border-radius: 0; // 移除圆角,让卡片完全贴合边缘
|
||||
}
|
||||
|
||||
// 消息卡片包装器
|
||||
.message-card-wrapper {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.card-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.delete-icon {
|
||||
position: absolute;
|
||||
top: -8px;
|
||||
right: -8px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 2px 8px rgba(239, 68, 68, 0.3);
|
||||
transition: all 0.3s ease;
|
||||
z-index: 10;
|
||||
border: 2px solid #ffffff;
|
||||
|
||||
.delete-text {
|
||||
color: white;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.1);
|
||||
box-shadow: 0 4px 12px rgba(239, 68, 68, 0.4);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card-right {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@ -79,7 +79,7 @@
|
||||
<view class="folder-background">
|
||||
<view class="folder-items-grid">
|
||||
<view
|
||||
v-for="(folderItem, folderIdx) in (item.folderItems || []).slice(0, 9)"
|
||||
v-for="(folderItem, folderIdx) in getVisibleFolderItems(item.folderItems).slice(0, 9)"
|
||||
:key="folderItem.id"
|
||||
class="folder-item-icon"
|
||||
>
|
||||
@ -118,21 +118,22 @@
|
||||
</view>
|
||||
<view class="modal-body">
|
||||
<view class="course-options">
|
||||
<view
|
||||
v-for="option in courseManagementOptions"
|
||||
:key="option.id"
|
||||
class="course-option"
|
||||
@click="handleCourseOptionClick(option)"
|
||||
>
|
||||
<view class="option-icon-container">
|
||||
<image
|
||||
class="option-icon"
|
||||
:src="`/static/base/home/${option.icon}.png`"
|
||||
mode="aspectFit"
|
||||
></image>
|
||||
<template v-for="option in courseManagementOptions" :key="option.id">
|
||||
<view
|
||||
v-if="option.show && hasPermissionDirect(option.permissionKey)"
|
||||
class="course-option"
|
||||
@click="handleCourseOptionClick(option)"
|
||||
>
|
||||
<view class="option-icon-container">
|
||||
<image
|
||||
class="option-icon"
|
||||
:src="`/static/base/home/${option.icon}.png`"
|
||||
mode="aspectFit"
|
||||
></image>
|
||||
</view>
|
||||
<text class="option-text">{{ option.text }}</text>
|
||||
</view>
|
||||
<text class="option-text">{{ option.text }}</text>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@ -301,8 +302,16 @@ const sections = reactive<Section[]>([
|
||||
icon: "xxjf",
|
||||
text: "学习积分",
|
||||
show: true,
|
||||
permissionKey: "rysyc-xxjf",
|
||||
path: "/pages/view/rw/index",
|
||||
permissionKey: "ysyc-xxjf",
|
||||
path: "/pages/view/routine/yishiyice/jf/index",
|
||||
},
|
||||
{
|
||||
id: "gnyy8",
|
||||
icon: "xxjf",
|
||||
text: "积分统计",
|
||||
show: true,
|
||||
permissionKey: "ysyc-pjtj",
|
||||
path: "/pages/view/routine/yishiyice/jf/pjtj",
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -681,6 +690,18 @@ const hasPermissionDirect = (permissionKey: string) => {
|
||||
return uniquePermissions.includes(permissionKey);
|
||||
};
|
||||
|
||||
// 获取有权限且显示的文件夹子项
|
||||
const getVisibleFolderItems = (folderItems: GridItem[] | undefined) => {
|
||||
if (!folderItems || !Array.isArray(folderItems)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// 过滤出有权限且show为true的子项
|
||||
return folderItems.filter(item => {
|
||||
return item.show && hasPermissionDirect(item.permissionKey);
|
||||
});
|
||||
};
|
||||
|
||||
// 暴露生命周期钩子给uni-app
|
||||
defineExpose({
|
||||
onShow,
|
||||
|
||||
@ -159,8 +159,14 @@
|
||||
scroll-y
|
||||
class="member-scroll-view"
|
||||
@scrolltolower="loadMoreMembers"
|
||||
lower-threshold="100"
|
||||
lower-threshold="50"
|
||||
enable-back-to-top
|
||||
>
|
||||
<!-- 成员统计信息 -->
|
||||
<view v-if="memberList.length > 0" class="member-stats">
|
||||
<text class="stats-text">共 {{ memberList.length }} 位成员</text>
|
||||
</view>
|
||||
|
||||
<view v-if="isLoadingMembers && memberList.length === 0" class="loading-indicator">
|
||||
<text class="loading-icon">⏳</text>
|
||||
<text class="loading-text">加载中...</text>
|
||||
@ -435,9 +441,10 @@ const loadMorePosts = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 查询成员
|
||||
// 查询成员 - 一次性加载全部
|
||||
const queryMembers = async (isLoadMore = false) => {
|
||||
if (isLoadingMembers.value) return;
|
||||
// 如果已经加载过数据且不是首次加载,直接返回
|
||||
if (isLoadMore || isLoadingMembers.value) return;
|
||||
|
||||
try {
|
||||
isLoadingMembers.value = true;
|
||||
@ -452,49 +459,38 @@ const queryMembers = async (isLoadMore = false) => {
|
||||
return;
|
||||
}
|
||||
|
||||
// 首次加载时解析所有教师ID
|
||||
if (!isLoadMore || allTeacherIds.value.length === 0) {
|
||||
// 解析 jsIds(可能是 JSON 字符串格式)
|
||||
let teacherIds: string[] = [];
|
||||
try {
|
||||
// 尝试解析 JSON 格式
|
||||
if (jsIds.value.startsWith('[') && jsIds.value.endsWith(']')) {
|
||||
teacherIds = JSON.parse(jsIds.value);
|
||||
} else {
|
||||
// 如果不是 JSON 格式,按逗号分割
|
||||
teacherIds = jsIds.value.split(',').map(id => id.trim()).filter(id => id);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('解析 jsIds 失败:', error);
|
||||
// 如果解析失败,尝试按逗号分割
|
||||
// 解析 jsIds(可能是 JSON 字符串格式)
|
||||
let teacherIds: string[] = [];
|
||||
try {
|
||||
// 尝试解析 JSON 格式
|
||||
if (jsIds.value.startsWith('[') && jsIds.value.endsWith(']')) {
|
||||
teacherIds = JSON.parse(jsIds.value);
|
||||
} else {
|
||||
// 如果不是 JSON 格式,按逗号分割
|
||||
teacherIds = jsIds.value.split(',').map(id => id.trim()).filter(id => id);
|
||||
}
|
||||
|
||||
if (teacherIds.length === 0) {
|
||||
console.log('解析后的 teacherIds 为空');
|
||||
memberList.value = [];
|
||||
hasMoreMembers.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('解析后的 teacherIds:', teacherIds);
|
||||
allTeacherIds.value = teacherIds;
|
||||
} catch (error) {
|
||||
console.error('解析 jsIds 失败:', error);
|
||||
// 如果解析失败,尝试按逗号分割
|
||||
teacherIds = jsIds.value.split(',').map(id => id.trim()).filter(id => id);
|
||||
}
|
||||
|
||||
// 计算当前页的数据
|
||||
const pageNo = isLoadMore ? currentMemberPage.value + 1 : 1;
|
||||
const startIndex = (pageNo - 1) * memberPageSize.value;
|
||||
const endIndex = startIndex + memberPageSize.value;
|
||||
const pageTeacherIds = allTeacherIds.value.slice(startIndex, endIndex);
|
||||
|
||||
if (pageTeacherIds.length === 0) {
|
||||
console.log('当前页没有数据');
|
||||
if (teacherIds.length === 0) {
|
||||
console.log('解析后的 teacherIds 为空');
|
||||
memberList.value = [];
|
||||
hasMoreMembers.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('解析后的 teacherIds:', teacherIds);
|
||||
console.log('总成员数:', teacherIds.length);
|
||||
allTeacherIds.value = teacherIds;
|
||||
|
||||
// 一次性加载所有教师信息
|
||||
console.log('一次性加载全部成员');
|
||||
|
||||
// 调用 findByIds 接口获取教师详细信息
|
||||
const teacherRes = await findByIdsApi({ ids: pageTeacherIds.join(',') });
|
||||
const teacherRes = await findByIdsApi({ ids: teacherIds.join(',') });
|
||||
|
||||
console.log('findByIds API 返回:', teacherRes);
|
||||
|
||||
@ -519,18 +515,11 @@ const queryMembers = async (isLoadMore = false) => {
|
||||
|
||||
console.log('转换后的成员列表:', members);
|
||||
|
||||
if (isLoadMore) {
|
||||
// 加载更多
|
||||
memberList.value = [...memberList.value, ...members];
|
||||
currentMemberPage.value = pageNo;
|
||||
} else {
|
||||
// 刷新数据
|
||||
memberList.value = members;
|
||||
currentMemberPage.value = 1;
|
||||
}
|
||||
// 直接设置全部成员
|
||||
memberList.value = members;
|
||||
hasMoreMembers.value = false; // 已经全部加载完成
|
||||
|
||||
// 判断是否还有更多数据
|
||||
hasMoreMembers.value = endIndex < allTeacherIds.value.length;
|
||||
console.log(`成员加载完成, 共加载: ${memberList.value.length}/${allTeacherIds.value.length} 位成员`);
|
||||
|
||||
} catch (error) {
|
||||
console.error('查询成员失败:', error);
|
||||
@ -539,11 +528,10 @@ const queryMembers = async (isLoadMore = false) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 加载更多成员
|
||||
// 加载更多成员(已改为一次性加载,此方法不再需要)
|
||||
const loadMoreMembers = () => {
|
||||
if (hasMoreMembers.value && !isLoadingMembers.value) {
|
||||
queryMembers(true);
|
||||
}
|
||||
// 所有成员已一次性加载完成,无需分页加载
|
||||
console.log('所有成员已加载完成,共', memberList.value.length, '位成员');
|
||||
};
|
||||
|
||||
// 加入社区
|
||||
@ -759,14 +747,16 @@ const goPublish = () => {
|
||||
flex: 1;
|
||||
padding: 0 20rpx;
|
||||
box-sizing: border-box;
|
||||
height: 0; // 关键:确保scroll-view可以正确滚动
|
||||
/* 计算高度:100vh - tabs高度(约88rpx) - 筛选栏高度(约104rpx) - 底部按钮区域(约130rpx) - 其他间距(约40rpx) */
|
||||
height: calc(100vh - 360rpx);
|
||||
}
|
||||
|
||||
.member-scroll-view {
|
||||
flex: 1;
|
||||
padding: 0 20rpx;
|
||||
box-sizing: border-box;
|
||||
height: 0; // 关键:确保scroll-view可以正确滚动
|
||||
/* 计算高度:100vh - tabs高度(约88rpx) - 底部按钮区域(约130rpx) - 其他间距(约40rpx) */
|
||||
height: calc(100vh - 260rpx);
|
||||
}
|
||||
|
||||
.post-list {
|
||||
@ -892,6 +882,28 @@ const goPublish = () => {
|
||||
}
|
||||
}
|
||||
|
||||
.member-stats {
|
||||
background: #fff;
|
||||
padding: 24rpx;
|
||||
margin-bottom: 20rpx;
|
||||
border-radius: 16rpx;
|
||||
text-align: center;
|
||||
|
||||
.stats-text {
|
||||
display: block;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.stats-hint {
|
||||
display: block;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
.member-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
@ -156,9 +156,11 @@
|
||||
import { ref, reactive, onMounted, onUnmounted } from 'vue';
|
||||
import { communityFindPageApi } from '@/api/forum';
|
||||
import { useDicStore } from '@/store/modules/dic';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
import BasicSearch from '@/components/BasicSearch/Search.vue';
|
||||
|
||||
const { findByPid } = useDicStore();
|
||||
const { getJs, getUser } = useUserStore();
|
||||
|
||||
// 搜索关键词
|
||||
const searchKeyword = ref('');
|
||||
@ -231,6 +233,12 @@ const queryData = async (isLoadMore = false) => {
|
||||
communityName: searchKeyword.value,
|
||||
status: 'A'
|
||||
};
|
||||
|
||||
// 如果不是 admin 用户,则传递当前教师ID进行过滤
|
||||
const empCode = getUser?.empCode || '';
|
||||
if (empCode !== 'admin') {
|
||||
params.dqjsId = getJs.id; // 当前教师ID
|
||||
}
|
||||
|
||||
// 根据Tab筛选
|
||||
if (activeTab.value !== 'all' && activeTab.value !== 'my') {
|
||||
|
||||
@ -61,6 +61,10 @@
|
||||
<text class="stat-number unsigned">{{ unsignedCount }}</text>
|
||||
<text class="stat-label">未签到</text>
|
||||
</view>
|
||||
<view class="stat-item" @click="showTeacherList('qj')">
|
||||
<text class="stat-number qj">{{ qjCount }}</text>
|
||||
<text class="stat-label">请假</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@ -78,6 +82,14 @@
|
||||
<view class="teacher-info">
|
||||
<text class="teacher-name">{{ teacher.jsxm }}</text>
|
||||
<text class="teacher-position">{{ teacher.dzzw || '' }} {{ teacher.qtzw || '' }}</text>
|
||||
<!-- 请假信息 -->
|
||||
<view v-if="teacher.qjlx" class="qj-info">
|
||||
<text class="qj-type">{{ teacher.qjlx }}</text>
|
||||
<text class="qj-reason">{{ teacher.qjsy || '无' }}</text>
|
||||
<text class="qj-status" :class="getSpResultClass(teacher.spResult)">
|
||||
{{ getSpResultText(teacher.spResult) }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="teacher-status">
|
||||
<text class="status-text" :class="getStatusClass(teacher.qdStatus)">
|
||||
@ -119,6 +131,14 @@
|
||||
<view class="popup-teacher-info">
|
||||
<text class="popup-teacher-name">{{ teacher.jsxm }}</text>
|
||||
<text class="popup-teacher-position">{{ teacher.dzzw || '' }} {{ teacher.qtzw || '' }}</text>
|
||||
<!-- 请假信息 -->
|
||||
<view v-if="teacher.qjlx" class="popup-qj-info">
|
||||
<text class="popup-qj-type">{{ teacher.qjlx }}</text>
|
||||
<text class="popup-qj-reason">{{ teacher.qjsy || '无' }}</text>
|
||||
<text class="popup-qj-status" :class="getSpResultClass(teacher.spResult)">
|
||||
{{ getSpResultText(teacher.spResult) }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="popup-teacher-status">
|
||||
<text class="popup-status-text" :class="getStatusClass(teacher.qdStatus)">
|
||||
@ -163,6 +183,9 @@ interface TeacherInfo {
|
||||
qtzw: string;
|
||||
qdStatus: string;
|
||||
qdwctime: string;
|
||||
qjlx?: string; // 请假类型
|
||||
qjsy?: string; // 请假事由
|
||||
spResult?: string; // 审批结果
|
||||
}
|
||||
|
||||
const qdId = ref<string>('');
|
||||
@ -185,6 +208,12 @@ const lateCount = computed(() => {
|
||||
}).length;
|
||||
});
|
||||
|
||||
// 请假统计
|
||||
const qjCount = computed(() => teacherList.value.filter(t => t.qjlx && t.qjlx.trim() !== '').length);
|
||||
const qjPendingCount = computed(() => teacherList.value.filter(t => t.spResult === 'A').length);
|
||||
const qjApprovedCount = computed(() => teacherList.value.filter(t => t.spResult === 'B').length);
|
||||
const qjRejectedCount = computed(() => teacherList.value.filter(t => t.spResult === 'C').length);
|
||||
|
||||
const filteredTeacherList = computed(() => {
|
||||
switch (currentFilter.value) {
|
||||
case 'signed':
|
||||
@ -199,6 +228,14 @@ const filteredTeacherList = computed(() => {
|
||||
});
|
||||
case 'unsigned':
|
||||
return teacherList.value.filter(t => t.qdStatus === '0');
|
||||
case 'qj':
|
||||
return teacherList.value.filter(t => t.qjlx && t.qjlx.trim() !== '');
|
||||
case 'qj_pending':
|
||||
return teacherList.value.filter(t => t.spResult === 'A');
|
||||
case 'qj_approved':
|
||||
return teacherList.value.filter(t => t.spResult === 'B');
|
||||
case 'qj_rejected':
|
||||
return teacherList.value.filter(t => t.spResult === 'C');
|
||||
default:
|
||||
return teacherList.value;
|
||||
}
|
||||
@ -257,6 +294,18 @@ const showTeacherList = (filter: string) => {
|
||||
case 'unsigned':
|
||||
popupTitle.value = `未签到人员 (${unsignedCount.value}人)`;
|
||||
break;
|
||||
case 'qj':
|
||||
popupTitle.value = `请假人员 (${qjCount.value}人)`;
|
||||
break;
|
||||
case 'qj_pending':
|
||||
popupTitle.value = `待审批请假 (${qjPendingCount.value}人)`;
|
||||
break;
|
||||
case 'qj_approved':
|
||||
popupTitle.value = `已同意请假 (${qjApprovedCount.value}人)`;
|
||||
break;
|
||||
case 'qj_rejected':
|
||||
popupTitle.value = `已驳回请假 (${qjRejectedCount.value}人)`;
|
||||
break;
|
||||
default:
|
||||
popupTitle.value = `全部人员 (${totalCount.value}人)`;
|
||||
}
|
||||
@ -299,6 +348,34 @@ const getStatusText = (status: string) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 获取审批状态文本
|
||||
const getSpResultText = (spResult: string) => {
|
||||
switch (spResult) {
|
||||
case 'A':
|
||||
return '待审批';
|
||||
case 'B':
|
||||
return '同意';
|
||||
case 'C':
|
||||
return '驳回';
|
||||
default:
|
||||
return '无';
|
||||
}
|
||||
};
|
||||
|
||||
// 获取审批状态样式类
|
||||
const getSpResultClass = (spResult: string) => {
|
||||
switch (spResult) {
|
||||
case 'A':
|
||||
return 'sp-pending';
|
||||
case 'B':
|
||||
return 'sp-approved';
|
||||
case 'C':
|
||||
return 'sp-rejected';
|
||||
default:
|
||||
return 'sp-none';
|
||||
}
|
||||
};
|
||||
|
||||
const formatTime = (time: string) => {
|
||||
if (!time) return '未设置';
|
||||
return new Date(time).toLocaleString('zh-CN', {
|
||||
@ -454,6 +531,22 @@ const handleBack = () => {
|
||||
&.late {
|
||||
color: #ff9800;
|
||||
}
|
||||
|
||||
&.qj {
|
||||
color: #9c27b0;
|
||||
}
|
||||
|
||||
&.qj-pending {
|
||||
color: #ff9800;
|
||||
}
|
||||
|
||||
&.qj-approved {
|
||||
color: #4caf50;
|
||||
}
|
||||
|
||||
&.qj-rejected {
|
||||
color: #f44336;
|
||||
}
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
@ -510,6 +603,56 @@ const handleBack = () => {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.qj-info {
|
||||
margin-top: 8px;
|
||||
padding: 8px 12px;
|
||||
background: #f8f9fa;
|
||||
border-radius: 6px;
|
||||
border-left: 3px solid #9c27b0;
|
||||
}
|
||||
|
||||
.qj-type {
|
||||
display: block;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: #9c27b0;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.qj-reason {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.qj-status {
|
||||
font-size: 11px;
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
font-weight: 500;
|
||||
|
||||
&.sp-pending {
|
||||
background: #fff3cd;
|
||||
color: #856404;
|
||||
}
|
||||
|
||||
&.sp-approved {
|
||||
background: #d4edda;
|
||||
color: #155724;
|
||||
}
|
||||
|
||||
&.sp-rejected {
|
||||
background: #f8d7da;
|
||||
color: #721c24;
|
||||
}
|
||||
|
||||
&.sp-none {
|
||||
background: #e9ecef;
|
||||
color: #6c757d;
|
||||
}
|
||||
}
|
||||
|
||||
.teacher-status {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@ -615,6 +758,56 @@ const handleBack = () => {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.popup-qj-info {
|
||||
margin-top: 6px;
|
||||
padding: 6px 10px;
|
||||
background: #f8f9fa;
|
||||
border-radius: 4px;
|
||||
border-left: 2px solid #9c27b0;
|
||||
}
|
||||
|
||||
.popup-qj-type {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: #9c27b0;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
.popup-qj-reason {
|
||||
display: block;
|
||||
font-size: 11px;
|
||||
color: #666;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
.popup-qj-status {
|
||||
font-size: 10px;
|
||||
padding: 1px 4px;
|
||||
border-radius: 3px;
|
||||
font-weight: 500;
|
||||
|
||||
&.sp-pending {
|
||||
background: #fff3cd;
|
||||
color: #856404;
|
||||
}
|
||||
|
||||
&.sp-approved {
|
||||
background: #d4edda;
|
||||
color: #155724;
|
||||
}
|
||||
|
||||
&.sp-rejected {
|
||||
background: #f8d7da;
|
||||
color: #721c24;
|
||||
}
|
||||
|
||||
&.sp-none {
|
||||
background: #e9ecef;
|
||||
color: #6c757d;
|
||||
}
|
||||
}
|
||||
|
||||
.popup-teacher-status {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@ -117,7 +117,7 @@
|
||||
src="/static/base/details.png"
|
||||
class="task-action-icon"
|
||||
/>
|
||||
<view v-if="showTooltip" class="tooltip">执行情况</view>
|
||||
<view v-if="showTooltip" class="tooltip">执行评价</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@ -603,9 +603,9 @@ const viewExecution = (task: TaskItem) => {
|
||||
// 将任务数据存储到全局存储中
|
||||
uni.setStorageSync('taskExecutionData', task);
|
||||
|
||||
// 跳转到任务执行情况页面
|
||||
// 跳转到任务执行情况页面,同时传递任务ID作为备用参数
|
||||
uni.navigateTo({
|
||||
url: `/pages/view/routine/yishiyice/kcrwzx?courseId=${courseId.value}`
|
||||
url: `/pages/view/routine/yishiyice/kcrwzx?courseId=${courseId.value}&taskId=${task.id}`
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
783
src/pages/view/routine/yishiyice/jf/index.vue
Normal file
783
src/pages/view/routine/yishiyice/jf/index.vue
Normal file
@ -0,0 +1,783 @@
|
||||
<template>
|
||||
<view class="teacher-tasks-page">
|
||||
<!-- 页面标题横幅 -->
|
||||
<view class="page-banner">
|
||||
<view class="banner-content">
|
||||
<view class="banner-title-wrapper">
|
||||
<text class="banner-icon">📋</text>
|
||||
<view class="banner-text">
|
||||
<text class="banner-title">学习积分</text>
|
||||
<text class="banner-subtitle">待评价 · 已评价</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="task-count">
|
||||
<text class="count-number">{{ totalCount }}</text>
|
||||
<text class="count-label">个任务</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 任务统计 -->
|
||||
<view class="task-summary">
|
||||
<view
|
||||
class="summary-item pending"
|
||||
:class="{ active: taskFilter === 'pending' }"
|
||||
@click="setTaskFilter('pending')"
|
||||
>
|
||||
<text class="summary-count">{{ pendingCount }}</text>
|
||||
<text class="summary-label">待评价</text>
|
||||
</view>
|
||||
<view
|
||||
class="summary-item completed"
|
||||
:class="{ active: taskFilter === 'completed' }"
|
||||
@click="setTaskFilter('completed')"
|
||||
>
|
||||
<text class="summary-count">{{ completedCount }}</text>
|
||||
<text class="summary-label">已评价</text>
|
||||
</view>
|
||||
<view
|
||||
class="summary-item all"
|
||||
:class="{ active: taskFilter === 'all' }"
|
||||
@click="setTaskFilter('all')"
|
||||
>
|
||||
<text class="summary-count">{{ totalCount }}</text>
|
||||
<text class="summary-label">全部</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 任务列表 -->
|
||||
<view class="task-list">
|
||||
<scroll-view
|
||||
scroll-y
|
||||
class="list-scroll-view"
|
||||
@scrolltolower="loadMore"
|
||||
lower-threshold="100"
|
||||
>
|
||||
<!-- 加载状态 -->
|
||||
<view v-if="isLoading && taskList.length === 0" class="loading-indicator">
|
||||
<text class="loading-icon">⏳</text>
|
||||
<text class="loading-text">加载中...</text>
|
||||
</view>
|
||||
|
||||
<!-- 任务列表 -->
|
||||
<template v-else-if="taskList.length > 0">
|
||||
<view
|
||||
v-for="task in taskList"
|
||||
:key="task.id"
|
||||
class="task-card"
|
||||
@click="handleTaskClick(task)"
|
||||
>
|
||||
<view class="task-header">
|
||||
<view class="task-title-row">
|
||||
<view
|
||||
class="status-dot"
|
||||
:class="getStatusDotClass(task)"
|
||||
></view>
|
||||
<text class="task-title">{{ task.rwmc }}</text>
|
||||
<text class="arrow-icon">›</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="task-body">
|
||||
<view v-if="task.rwms" class="task-description">
|
||||
<text class="description-label">任务描述:</text>
|
||||
<text class="description-content">{{ task.rwms }}</text>
|
||||
</view>
|
||||
|
||||
<view class="task-info">
|
||||
<view class="info-item" v-if="task.rwfzrxm">
|
||||
<text class="info-icon">👤</text>
|
||||
<text class="info-label">负责人:</text>
|
||||
<text class="info-value">{{ task.rwfzrxm }}</text>
|
||||
</view>
|
||||
|
||||
<view class="info-item" v-if="task.rwjstime">
|
||||
<text class="info-icon">⏰</text>
|
||||
<text class="info-label">截止时间:</text>
|
||||
<text class="info-value">{{ formatDate(task.rwjstime) }}</text>
|
||||
</view>
|
||||
|
||||
<view class="info-item" v-if="task.rwzxtime">
|
||||
<text class="info-icon">✅</text>
|
||||
<text class="info-label">完成时间:</text>
|
||||
<text class="info-value">{{ formatDate(task.rwzxtime) }}</text>
|
||||
</view>
|
||||
|
||||
<view class="info-item">
|
||||
<text class="info-icon">📊</text>
|
||||
<text class="info-label">状态:</text>
|
||||
<text
|
||||
class="info-value status-text"
|
||||
:class="getStatusClass(task)"
|
||||
>{{ getStatusText(task) }}</text>
|
||||
</view>
|
||||
|
||||
<view class="info-item" v-if="task.ispj === 'A' && task.rwpjjf">
|
||||
<text class="info-icon">⭐</text>
|
||||
<text class="info-label">积分:</text>
|
||||
<text class="info-value score-text">{{ task.rwpjjf }}分</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view v-else class="empty-state">
|
||||
<text class="empty-icon">📦</text>
|
||||
<text class="empty-text">{{ getEmptyStateText() }}</text>
|
||||
<text class="empty-hint">{{ getEmptyStateHint() }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 加载更多 -->
|
||||
<view v-if="isLoading && taskList.length > 0" class="loading-more">
|
||||
<text class="loading-more-icon">⏳</text>
|
||||
<text class="loading-more-text">加载中...</text>
|
||||
</view>
|
||||
|
||||
<!-- 没有更多数据 -->
|
||||
<view v-if="!hasMore && taskList.length > 0" class="no-more">
|
||||
<text class="no-more-icon">✓</text>
|
||||
<text class="no-more-text">已加载全部任务</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted } from "vue";
|
||||
import { onShow } from "@dcloudio/uni-app";
|
||||
import { getTeacherTasksApi } from "@/api/base/rwzxApi";
|
||||
import { useUserStore } from "@/store/modules/user";
|
||||
|
||||
const { getJs } = useUserStore();
|
||||
|
||||
// 任务数据接口定义
|
||||
interface TaskItem {
|
||||
id: string;
|
||||
rwId: string; // 任务ID
|
||||
rwmc: string; // 任务名称
|
||||
rwms: string; // 任务描述
|
||||
rwfzr: string; // 任务负责人ID
|
||||
rwfzrxm: string; // 任务负责人姓名
|
||||
rwjstime: string; // 任务截止时间
|
||||
rwzxfzr: string; // 执行负责人ID
|
||||
rwzxfzrxm: string; // 执行负责人姓名
|
||||
rwzxtime: string; // 执行时间
|
||||
rwzxnr: string; // 执行内容
|
||||
iszxwc: string; // 是否执行完成:A-已完成,B-未完成
|
||||
rwzxzt: string; // 执行状态:A-已完成,B-未完成
|
||||
rwlyId: string; // 任务来源ID
|
||||
ispj: string; // 是否评价:A-已评价,B-未评价
|
||||
rwpjjf?: string; // 任务评价积分
|
||||
rwpjrxm?: string; // 任务评价人姓名
|
||||
rwpjms?: string; // 任务评价描述
|
||||
rwpjtime?: string; // 任务评价时间
|
||||
createTime: string;
|
||||
}
|
||||
|
||||
// 响应式数据
|
||||
const taskFilter = ref<'all' | 'pending' | 'completed'>('pending'); // 默认显示待评价
|
||||
const allTaskList = ref<TaskItem[]>([]); // 存储所有任务数据
|
||||
const taskList = ref<TaskItem[]>([]); // 当前显示的任务列表(经过筛选)
|
||||
const isLoading = ref(false);
|
||||
const hasMore = ref(true);
|
||||
const currentPage = ref(1);
|
||||
const pageSize = ref(10);
|
||||
|
||||
// 统计数据
|
||||
const pendingCount = ref(0);
|
||||
const completedCount = ref(0);
|
||||
const totalCount = ref(0);
|
||||
|
||||
// 加载任务列表(一次性加载所有数据)
|
||||
const loadTaskList = async (isLoadMore = false) => {
|
||||
if (isLoading.value) return;
|
||||
|
||||
isLoading.value = true;
|
||||
try {
|
||||
const jsId = getJs.id;
|
||||
if (!jsId) {
|
||||
uni.showToast({
|
||||
title: '未获取到教师信息',
|
||||
icon: 'error'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const params: any = {
|
||||
jsId,
|
||||
page: isLoadMore ? currentPage.value + 1 : 1,
|
||||
rows: pageSize.value
|
||||
};
|
||||
|
||||
// 不传 ispj 参数,获取所有任务
|
||||
console.log('加载任务列表,参数:', params);
|
||||
const response = await getTeacherTasksApi(params);
|
||||
console.log('任务列表API响应:', response);
|
||||
|
||||
const newData = response?.rows || [];
|
||||
|
||||
if (isLoadMore) {
|
||||
allTaskList.value.push(...newData);
|
||||
currentPage.value++;
|
||||
} else {
|
||||
allTaskList.value = newData;
|
||||
currentPage.value = 1;
|
||||
}
|
||||
|
||||
hasMore.value = newData.length === pageSize.value;
|
||||
|
||||
// 计算统计数据
|
||||
updateStatistics();
|
||||
|
||||
// 根据当前筛选条件过滤数据
|
||||
filterTaskList();
|
||||
|
||||
console.log('任务列表加载完成:', {
|
||||
total: allTaskList.value.length,
|
||||
pending: pendingCount.value,
|
||||
completed: completedCount.value,
|
||||
hasMore: hasMore.value
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('加载任务列表失败:', error);
|
||||
uni.showToast({
|
||||
title: '加载失败',
|
||||
icon: 'error'
|
||||
});
|
||||
allTaskList.value = [];
|
||||
taskList.value = [];
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 更新统计数据
|
||||
const updateStatistics = () => {
|
||||
pendingCount.value = allTaskList.value.filter(task => task.ispj === 'B').length;
|
||||
completedCount.value = allTaskList.value.filter(task => task.ispj === 'A').length;
|
||||
totalCount.value = allTaskList.value.length;
|
||||
};
|
||||
|
||||
// 根据筛选条件过滤任务列表
|
||||
const filterTaskList = () => {
|
||||
if (taskFilter.value === 'pending') {
|
||||
taskList.value = allTaskList.value.filter(task => task.ispj === 'B');
|
||||
} else if (taskFilter.value === 'completed') {
|
||||
taskList.value = allTaskList.value.filter(task => task.ispj === 'A');
|
||||
} else {
|
||||
taskList.value = allTaskList.value;
|
||||
}
|
||||
};
|
||||
|
||||
// 设置任务筛选
|
||||
const setTaskFilter = (filter: 'all' | 'pending' | 'completed') => {
|
||||
if (taskFilter.value === filter) return;
|
||||
|
||||
taskFilter.value = filter;
|
||||
filterTaskList(); // 使用前端筛选,不重新请求
|
||||
};
|
||||
|
||||
// 获取状态点样式
|
||||
const getStatusDotClass = (task: TaskItem) => {
|
||||
return task.iszxwc === 'A' ? 'completed' : 'pending';
|
||||
};
|
||||
|
||||
// 获取状态样式类
|
||||
const getStatusClass = (task: TaskItem) => {
|
||||
// 使用 ispj 字段判断评价状态
|
||||
return task.ispj === 'A' ? 'status-completed' : 'status-pending';
|
||||
};
|
||||
|
||||
// 获取状态文本
|
||||
const getStatusText = (task: TaskItem) => {
|
||||
// 使用 ispj 字段判断评价状态
|
||||
return task.ispj === 'A' ? '已评价' : '未评价';
|
||||
};
|
||||
|
||||
// 获取空状态文本
|
||||
const getEmptyStateText = () => {
|
||||
switch (taskFilter.value) {
|
||||
case 'pending':
|
||||
return '暂无待评价任务';
|
||||
case 'completed':
|
||||
return '暂无已评价任务';
|
||||
default:
|
||||
return '暂无任务';
|
||||
}
|
||||
};
|
||||
|
||||
// 获取空状态提示
|
||||
const getEmptyStateHint = () => {
|
||||
switch (taskFilter.value) {
|
||||
case 'pending':
|
||||
return '您当前没有需要评价的任务';
|
||||
case 'completed':
|
||||
return '您还没有评价过任务';
|
||||
default:
|
||||
return '当前没有任何任务';
|
||||
}
|
||||
};
|
||||
|
||||
// 格式化日期
|
||||
const formatDate = (dateStr: string) => {
|
||||
if (!dateStr) return '';
|
||||
const date = new Date(dateStr);
|
||||
return `${date.getFullYear()}/${(date.getMonth() + 1).toString().padStart(2, '0')}/${date.getDate().toString().padStart(2, '0')} ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`;
|
||||
};
|
||||
|
||||
// 处理任务点击
|
||||
const handleTaskClick = (task: TaskItem) => {
|
||||
console.log('点击任务:', task);
|
||||
|
||||
// 跳转到评价页面
|
||||
const evaluationData = {
|
||||
taskInfo: {
|
||||
id: task.rwId,
|
||||
rwmc: task.rwmc,
|
||||
rwms: task.rwms,
|
||||
rwjstime: task.rwjstime,
|
||||
rwfzrxm: task.rwfzrxm,
|
||||
rwfzr: task.rwfzr,
|
||||
rwStatus: task.rwzxzt,
|
||||
rwlyId: task.rwlyId
|
||||
},
|
||||
executionInfo: {
|
||||
id: task.id,
|
||||
rwId: task.rwId,
|
||||
rwzxfzr: task.rwzxfzr,
|
||||
rwzxfzrxm: task.rwzxfzrxm,
|
||||
iszxwc: task.iszxwc,
|
||||
rwzxzt: task.rwzxzt,
|
||||
rwzxtime: task.rwzxtime,
|
||||
rwzxnr: task.rwzxnr,
|
||||
createTime: task.createTime,
|
||||
ispj: task.ispj,
|
||||
jsId: task.rwzxfzr,
|
||||
rwpjjf: task.rwpjjf,
|
||||
rwpjrxm: task.rwpjrxm,
|
||||
rwpjms: task.rwpjms,
|
||||
rwpjtime: task.rwpjtime
|
||||
},
|
||||
isReadOnly: true // 强制设置为只读模式
|
||||
};
|
||||
|
||||
uni.setStorageSync('evaluationData', evaluationData);
|
||||
|
||||
uni.navigateTo({
|
||||
url: `/pages/view/routine/yishiyice/jf/kcxxpj`,
|
||||
success: () => {
|
||||
console.log('跳转到评价页面成功');
|
||||
},
|
||||
fail: (error) => {
|
||||
console.error('跳转到评价页面失败:', error);
|
||||
uni.showToast({
|
||||
title: '页面跳转失败',
|
||||
icon: 'error'
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 加载更多
|
||||
const loadMore = () => {
|
||||
if (!isLoading.value && hasMore.value) {
|
||||
loadTaskList(true);
|
||||
}
|
||||
};
|
||||
|
||||
// 页面加载
|
||||
onMounted(() => {
|
||||
console.log('页面加载,当前教师:', getJs);
|
||||
loadTaskList(false); // 加载任务列表(同时更新统计数据)
|
||||
});
|
||||
|
||||
// 页面显示时刷新数据
|
||||
onShow(() => {
|
||||
console.log('页面显示,刷新数据');
|
||||
loadTaskList(false); // 刷新任务列表(同时更新统计数据)
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.teacher-tasks-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
background: linear-gradient(180deg, #f0f5ff 0%, #f5f7fa 100%);
|
||||
}
|
||||
|
||||
// 页面横幅样式
|
||||
.page-banner {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
padding: 20px 16px;
|
||||
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
|
||||
|
||||
.banner-content {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.banner-title-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.banner-icon {
|
||||
font-size: 32px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.banner-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.banner-title {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.banner-subtitle {
|
||||
font-size: 13px;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.task-count {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
backdrop-filter: blur(10px);
|
||||
padding: 10px 16px;
|
||||
border-radius: 12px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
.count-number {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.count-label {
|
||||
font-size: 11px;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
margin-top: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
// 任务统计
|
||||
.task-summary {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
margin: 0;
|
||||
padding: 12px 8px;
|
||||
background-color: #ffffff;
|
||||
border-radius: 0;
|
||||
border: none;
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
}
|
||||
|
||||
.summary-item {
|
||||
text-align: center;
|
||||
padding: 8px 6px;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
border: 2px solid transparent;
|
||||
min-width: 80px;
|
||||
}
|
||||
|
||||
.summary-item:hover {
|
||||
background-color: rgba(0, 0, 0, 0.02);
|
||||
}
|
||||
|
||||
.summary-item.active {
|
||||
background-color: rgba(24, 144, 255, 0.1);
|
||||
border-color: #1890ff;
|
||||
}
|
||||
|
||||
.summary-item.pending.active {
|
||||
background-color: rgba(250, 140, 22, 0.1);
|
||||
border-color: #fa8c16;
|
||||
}
|
||||
|
||||
.summary-item.completed.active {
|
||||
background-color: rgba(82, 196, 26, 0.1);
|
||||
border-color: #52c41a;
|
||||
}
|
||||
|
||||
.summary-count {
|
||||
display: block;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.summary-item.pending .summary-count {
|
||||
color: #fa8c16;
|
||||
}
|
||||
|
||||
.summary-item.completed .summary-count {
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
.summary-label {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
// 任务列表
|
||||
.task-list {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.list-scroll-view {
|
||||
flex: 1;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 任务卡片
|
||||
.task-card {
|
||||
background-color: white;
|
||||
border-radius: 0;
|
||||
margin-bottom: 0;
|
||||
box-shadow: none;
|
||||
overflow: hidden;
|
||||
transition: all 0.3s ease;
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
}
|
||||
|
||||
.task-card:active {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
.task-header {
|
||||
padding: 14px 16px;
|
||||
background: #ffffff;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.task-title-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.status-dot {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
background: #999;
|
||||
flex-shrink: 0;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.status-dot.completed {
|
||||
background: #52c41a;
|
||||
}
|
||||
|
||||
.status-dot.pending {
|
||||
background: #fa8c16;
|
||||
}
|
||||
|
||||
.task-title {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #1f2937;
|
||||
flex: 1;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.arrow-icon {
|
||||
font-size: 20px;
|
||||
color: #999;
|
||||
font-weight: bold;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.task-body {
|
||||
padding: 0 16px 14px 16px;
|
||||
}
|
||||
|
||||
.task-description {
|
||||
font-size: 12px;
|
||||
line-height: 1.6;
|
||||
color: #666;
|
||||
margin-bottom: 8px;
|
||||
display: flex;
|
||||
|
||||
.description-label {
|
||||
flex-shrink: 0;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.description-content {
|
||||
margin-left: 4px;
|
||||
flex: 1;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.task-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
font-size: 12px;
|
||||
line-height: 1.8;
|
||||
color: #666;
|
||||
display: flex;
|
||||
|
||||
.info-icon {
|
||||
font-size: 14px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
flex-shrink: 0;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
margin-left: 4px;
|
||||
flex: 1;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.status-text {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.status-text.status-completed {
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
.status-text.status-pending {
|
||||
color: #fa8c16;
|
||||
}
|
||||
|
||||
.score-text {
|
||||
font-weight: 700;
|
||||
color: #ff9800;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
// 通用样式
|
||||
.loading-indicator,
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 60px 20px;
|
||||
color: #666;
|
||||
background-color: white;
|
||||
margin: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.loading-icon {
|
||||
font-size: 32px;
|
||||
margin-bottom: 12px;
|
||||
animation: pulse 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
font-size: 13px;
|
||||
color: #667eea;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
font-size: 64px;
|
||||
margin-bottom: 16px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 16px;
|
||||
color: #4a5568;
|
||||
font-weight: 600;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.empty-hint {
|
||||
font-size: 13px;
|
||||
color: #a0aec0;
|
||||
}
|
||||
|
||||
.loading-more {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
padding: 20px;
|
||||
background-color: white;
|
||||
|
||||
.loading-more-icon {
|
||||
font-size: 18px;
|
||||
animation: pulse 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.loading-more-text {
|
||||
color: #667eea;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.no-more {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
padding: 24px 20px;
|
||||
margin: 8px 16px 16px 16px;
|
||||
background: linear-gradient(135deg, #f7f9fc 0%, #e8f0fe 100%);
|
||||
border-radius: 12px;
|
||||
|
||||
.no-more-icon {
|
||||
font-size: 16px;
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
.no-more-text {
|
||||
color: #718096;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
opacity: 0.6;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
973
src/pages/view/routine/yishiyice/jf/kcxxpj.vue
Normal file
973
src/pages/view/routine/yishiyice/jf/kcxxpj.vue
Normal file
@ -0,0 +1,973 @@
|
||||
<template>
|
||||
<view class="evaluation-page">
|
||||
<!-- 提交遮罩层 -->
|
||||
<view v-if="isSubmitting" class="submit-overlay">
|
||||
<view class="submit-loading">
|
||||
<view class="loading-spinner"></view>
|
||||
<text class="loading-text">提交评价中...</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="isLoading" class="loading-indicator">加载中...</view>
|
||||
<view v-else class="evaluation-content">
|
||||
<!-- 左侧:作品预览和详情 -->
|
||||
<view class="work-preview-section">
|
||||
<!-- 完成成果区域 -->
|
||||
<view class="work-preview">
|
||||
<view class="preview-title-header">
|
||||
<text class="preview-title-icon">📋</text>
|
||||
<text class="preview-title">完成成果</text>
|
||||
</view>
|
||||
<view class="preview-content">
|
||||
<!-- 任务基本信息 -->
|
||||
<view class="task-info">
|
||||
<view class="info-item">
|
||||
<text class="info-label">任务名称:</text>
|
||||
<text class="info-value">{{ taskInfo.rwmc }}</text>
|
||||
</view>
|
||||
<view class="info-item inline">
|
||||
<text class="info-label">提交人:</text>
|
||||
<text class="info-value">{{ executionInfo.rwzxfzrxm }}</text>
|
||||
</view>
|
||||
<view class="info-item inline">
|
||||
<text class="info-label">提交时间:</text>
|
||||
<text class="info-value">{{ formatDateTime(executionInfo.rwzxtime) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 提交内容 -->
|
||||
<view class="submitted-content">
|
||||
<text class="content-title">提交内容:</text>
|
||||
<view v-if="submittedContent.length > 0" class="content-display">
|
||||
<view
|
||||
v-for="(item, index) in submittedContent"
|
||||
:key="index"
|
||||
class="content-item"
|
||||
>
|
||||
<view class="content-label">{{ item.label }}</view>
|
||||
<view class="content-value">
|
||||
<!-- 文本内容 -->
|
||||
<text v-if="item.type === 'text'" class="text-content">{{ item.value }}</text>
|
||||
|
||||
<!-- 图片内容 -->
|
||||
<view v-else-if="item.type === 'image'" class="media-content">
|
||||
<ImageVideoUpload
|
||||
:enableImage="true"
|
||||
:enableVideo="false"
|
||||
:enableFile="false"
|
||||
:imageList="item.value"
|
||||
:disabled="true"
|
||||
:readonly="true"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 视频内容 -->
|
||||
<view v-else-if="item.type === 'video'" class="media-content">
|
||||
<ImageVideoUpload
|
||||
:enableImage="false"
|
||||
:enableVideo="true"
|
||||
:enableFile="false"
|
||||
:videoList="item.value"
|
||||
:disabled="true"
|
||||
:readonly="true"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 文档内容 -->
|
||||
<view v-else-if="item.type === 'file'" class="media-content">
|
||||
<ImageVideoUpload
|
||||
:enableImage="false"
|
||||
:enableVideo="false"
|
||||
:enableFile="true"
|
||||
:fileList="item.value"
|
||||
:disabled="true"
|
||||
:readonly="true"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else class="empty-content">
|
||||
<text class="empty-text">暂无提交内容</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
|
||||
<!-- 右侧:评价功能 -->
|
||||
<view class="evaluation-section">
|
||||
<!-- 成果评价 -->
|
||||
<view class="rating-section">
|
||||
<view class="section-header">
|
||||
<text class="section-icon">⭐</text>
|
||||
<text class="section-title">成果评价</text>
|
||||
</view>
|
||||
|
||||
<view class="rating-scale">
|
||||
<view
|
||||
v-for="score in ratingOptions"
|
||||
:key="score.value"
|
||||
class="rating-option"
|
||||
:class="{ active: evaluationData.score === score.value, disabled: isReadOnly }"
|
||||
@click="setRating(score.value)"
|
||||
>
|
||||
<view class="rating-circle">{{ score.value }}</view>
|
||||
<text class="rating-label">{{ score.label }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 文字评价 -->
|
||||
<view class="comment-section">
|
||||
<text class="comment-label">文字评价</text>
|
||||
<textarea
|
||||
v-model="evaluationData.comment"
|
||||
class="comment-input"
|
||||
placeholder="请在此输入您的评价意见..."
|
||||
:disabled="isSubmitting || isReadOnly"
|
||||
></textarea>
|
||||
</view>
|
||||
|
||||
<!-- AI智能评价 -->
|
||||
<view class="ai-evaluation-section" v-if="!isReadOnly">
|
||||
<view class="section-header">
|
||||
<text class="section-icon">🤖</text>
|
||||
<text class="section-title">AI智能评价</text>
|
||||
<button
|
||||
class="ai-button"
|
||||
@click="getAIEvaluation"
|
||||
:disabled="isSubmitting || aiLoading"
|
||||
>
|
||||
<text class="ai-text">{{ aiLoading ? 'AI分析中...' : '获取' }}</text>
|
||||
</button>
|
||||
</view>
|
||||
|
||||
<!-- AI评价结果 -->
|
||||
<view v-if="aiEvaluation" class="ai-result">
|
||||
<text class="ai-result-title">AI评价结果:</text>
|
||||
<text class="ai-result-content">{{ aiEvaluation }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部固定提交按钮 -->
|
||||
<view class="bottom-submit" v-if="!isReadOnly">
|
||||
<button
|
||||
class="submit-button"
|
||||
@click="submitEvaluation"
|
||||
:disabled="isSubmitting || !evaluationData.score"
|
||||
>
|
||||
<text class="button-icon">📤</text>
|
||||
<text class="button-text">{{ isSubmitting ? '提交评价中...' : '提交评价' }}</text>
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, onMounted } from "vue";
|
||||
import { onLoad as onPageLoad } from "@dcloudio/uni-app";
|
||||
import { rwFindInfoByRwId } from "@/api/base/server";
|
||||
import { rwzxExecutedInfoByRwIdAndJsApi } from "@/api/base/server";
|
||||
import { updateEvaluationApi } from "@/api/base/rwzxApi";
|
||||
import { useUserStore } from "@/store/modules/user";
|
||||
import { ImageVideoUpload, type FileItem, COMPRESS_PRESETS } from "@/components/ImageVideoUpload";
|
||||
|
||||
// 接口类型定义
|
||||
interface TaskInfo {
|
||||
id: string;
|
||||
rwmc: string;
|
||||
rwms: string;
|
||||
rwjstime: string;
|
||||
rwfzrxm: string;
|
||||
rwfzr: string;
|
||||
rwStatus: string;
|
||||
rwlyId: string;
|
||||
}
|
||||
|
||||
interface ExecutionInfo {
|
||||
id: string;
|
||||
rwId: string;
|
||||
rwzxfzr: string;
|
||||
rwzxfzrxm: string;
|
||||
iszxwc: string;
|
||||
rwzxzt: string;
|
||||
rwzxtime: string;
|
||||
rwzxnr: string;
|
||||
createTime: string;
|
||||
jsId?: string;
|
||||
ispj?: string;
|
||||
rwpjr?: string;
|
||||
rwpjrxm?: string;
|
||||
rwpjjf?: string;
|
||||
rwpjms?: string;
|
||||
rwpjtime?: string;
|
||||
}
|
||||
|
||||
interface SubmittedContent {
|
||||
label: string;
|
||||
type: 'text' | 'image' | 'video' | 'file';
|
||||
value: any;
|
||||
}
|
||||
|
||||
// 响应式数据
|
||||
const isLoading = ref(false);
|
||||
const isSubmitting = ref(false);
|
||||
const aiLoading = ref(false);
|
||||
const isReadOnly = ref(false); // 是否只读模式
|
||||
const taskInfo = ref<TaskInfo>({
|
||||
id: '',
|
||||
rwmc: '',
|
||||
rwms: '',
|
||||
rwjstime: '',
|
||||
rwfzrxm: '',
|
||||
rwfzr: '',
|
||||
rwStatus: '',
|
||||
rwlyId: ''
|
||||
});
|
||||
const executionInfo = ref<ExecutionInfo>({
|
||||
id: '',
|
||||
rwId: '',
|
||||
rwzxfzr: '',
|
||||
rwzxfzrxm: '',
|
||||
iszxwc: '',
|
||||
rwzxzt: '',
|
||||
rwzxtime: '',
|
||||
rwzxnr: '',
|
||||
createTime: ''
|
||||
});
|
||||
const submittedContent = ref<SubmittedContent[]>([]);
|
||||
|
||||
// 评价数据
|
||||
const evaluationData = reactive({
|
||||
score: '',
|
||||
comment: ''
|
||||
});
|
||||
|
||||
const aiEvaluation = ref('');
|
||||
|
||||
// 评分选项
|
||||
const ratingOptions = [
|
||||
{ value: '1', label: '很差' },
|
||||
{ value: '2', label: '较差' },
|
||||
{ value: '3', label: '一般' },
|
||||
{ value: '4', label: '较好' },
|
||||
{ value: '5', label: '优秀' }
|
||||
];
|
||||
|
||||
const userStore = useUserStore();
|
||||
const { getJs } = userStore;
|
||||
|
||||
// 页面加载
|
||||
onPageLoad(async (options: any) => {
|
||||
console.log('评价页面接收到的参数:', options);
|
||||
|
||||
// 从全局存储中获取参数数据
|
||||
const storedParams = uni.getStorageSync('evaluationData');
|
||||
|
||||
if (storedParams) {
|
||||
try {
|
||||
taskInfo.value = storedParams.taskInfo;
|
||||
executionInfo.value = storedParams.executionInfo;
|
||||
isReadOnly.value = storedParams.isReadOnly || false; // 获取只读模式标志
|
||||
console.log('从全局存储解析参数成功:', {
|
||||
taskInfo: taskInfo.value,
|
||||
executionInfo: executionInfo.value,
|
||||
isReadOnly: isReadOnly.value
|
||||
});
|
||||
|
||||
// 清除存储的参数数据
|
||||
uni.removeStorageSync('evaluationData');
|
||||
|
||||
await loadSubmittedContent();
|
||||
|
||||
// 如果是只读模式,加载已有的评价信息
|
||||
if (isReadOnly.value) {
|
||||
loadExistingEvaluation();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('解析全局存储参数失败:', error);
|
||||
uni.showToast({
|
||||
title: '参数解析失败',
|
||||
icon: 'error'
|
||||
});
|
||||
setTimeout(() => {
|
||||
uni.navigateBack();
|
||||
}, 1500);
|
||||
}
|
||||
} else {
|
||||
console.error('缺少评价参数');
|
||||
uni.showToast({
|
||||
title: '缺少评价参数',
|
||||
icon: 'error'
|
||||
});
|
||||
setTimeout(() => {
|
||||
uni.navigateBack();
|
||||
}, 1500);
|
||||
}
|
||||
});
|
||||
|
||||
// 加载提交的内容
|
||||
const loadSubmittedContent = async () => {
|
||||
isLoading.value = true;
|
||||
try {
|
||||
// 获取任务详情
|
||||
const taskResult = await rwFindInfoByRwId({ rwId: taskInfo.value.id });
|
||||
const rwflx = taskResult.result.rwlxes;
|
||||
|
||||
// 获取执行记录
|
||||
const executionResult = await rwzxExecutedInfoByRwIdAndJsApi({
|
||||
rwId: taskInfo.value.id,
|
||||
jsId: executionInfo.value.rwzxfzr
|
||||
});
|
||||
|
||||
if (executionResult && executionResult.result && executionResult.result.length) {
|
||||
const rwzxqds = executionResult.result;
|
||||
const content: SubmittedContent[] = [];
|
||||
|
||||
for (let i = 0; i < rwzxqds.length; i++) {
|
||||
const record = rwzxqds[i];
|
||||
const rwlxId = record.rwlxId;
|
||||
const rwzxqdtx = record.rwzxqdtx;
|
||||
|
||||
// 查找对应的任务类型
|
||||
const taskType = rwflx.find((item: any) => item.id === rwlxId);
|
||||
|
||||
if (taskType && rwzxqdtx) {
|
||||
if (taskType.rwfl === "sctp") {
|
||||
// 图片上传类型
|
||||
const urls = rwzxqdtx.split(',').filter(Boolean);
|
||||
const names = record.wjmc ? record.wjmc.split(',').filter(Boolean) : [];
|
||||
const images = urls.map((url: string, index: number) => ({
|
||||
url: url.trim(),
|
||||
name: names[index] || url.split('/').pop() || 'image.jpg'
|
||||
}));
|
||||
content.push({
|
||||
label: taskType.rwbt,
|
||||
type: 'image',
|
||||
value: images
|
||||
});
|
||||
} else if (taskType.rwfl === "scsp") {
|
||||
// 视频上传类型
|
||||
const urls = rwzxqdtx.split(',').filter(Boolean);
|
||||
const names = record.wjmc ? record.wjmc.split(',').filter(Boolean) : [];
|
||||
const videos = urls.map((url: string, index: number) => ({
|
||||
url: url.trim(),
|
||||
name: names[index] || url.split('/').pop() || 'video.mp4'
|
||||
}));
|
||||
content.push({
|
||||
label: taskType.rwbt,
|
||||
type: 'video',
|
||||
value: videos
|
||||
});
|
||||
} else if (taskType.rwfl === "scwd") {
|
||||
// 文档上传类型
|
||||
const urls = rwzxqdtx.split(',').filter(Boolean);
|
||||
const names = record.wjmc ? record.wjmc.split(',').filter(Boolean) : [];
|
||||
const files = urls.map((url: string, index: number) => ({
|
||||
url: url.trim(),
|
||||
name: names[index] || url.split('/').pop() || 'document'
|
||||
}));
|
||||
content.push({
|
||||
label: taskType.rwbt,
|
||||
type: 'file',
|
||||
value: files
|
||||
});
|
||||
} else {
|
||||
// 文本等其他类型
|
||||
content.push({
|
||||
label: taskType.rwbt,
|
||||
type: 'text',
|
||||
value: rwzxqdtx
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
submittedContent.value = content;
|
||||
console.log('加载提交内容成功:', submittedContent.value);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载提交内容失败:', error);
|
||||
uni.showToast({
|
||||
title: '加载内容失败',
|
||||
icon: 'error'
|
||||
});
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 加载已有的评价信息
|
||||
const loadExistingEvaluation = () => {
|
||||
if (executionInfo.value.rwpjjf) {
|
||||
evaluationData.score = executionInfo.value.rwpjjf;
|
||||
}
|
||||
if (executionInfo.value.rwpjms) {
|
||||
evaluationData.comment = executionInfo.value.rwpjms;
|
||||
}
|
||||
console.log('加载已有评价信息:', {
|
||||
score: evaluationData.score,
|
||||
comment: evaluationData.comment
|
||||
});
|
||||
};
|
||||
|
||||
// 设置评分
|
||||
const setRating = (score: string) => {
|
||||
if (isReadOnly.value) {
|
||||
return; // 只读模式不允许修改
|
||||
}
|
||||
evaluationData.score = score;
|
||||
};
|
||||
|
||||
// 获取AI评价
|
||||
const getAIEvaluation = async () => {
|
||||
aiLoading.value = true;
|
||||
try {
|
||||
// TODO: 调用AI评价接口
|
||||
// 这里模拟AI评价结果
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
aiEvaluation.value = `正在接入AI评价,敬请期待...`;
|
||||
} catch (error) {
|
||||
console.error('获取AI评价失败:', error);
|
||||
uni.showToast({
|
||||
title: 'AI评价失败',
|
||||
icon: 'error'
|
||||
});
|
||||
} finally {
|
||||
aiLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 提交评价
|
||||
const submitEvaluation = async () => {
|
||||
if (isSubmitting.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!evaluationData.score) {
|
||||
uni.showToast({
|
||||
title: '请选择评分',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
isSubmitting.value = true;
|
||||
|
||||
try {
|
||||
// 调用评价提交接口
|
||||
console.log('教师信息:', getJs);
|
||||
|
||||
const evaluationParams = {
|
||||
rwzxId: executionInfo.value.id,
|
||||
rwpjr: getJs.id,
|
||||
rwpjrxm: getJs.jsxm,
|
||||
rwpjjf: evaluationData.score,
|
||||
rwpjms: evaluationData.comment || ''
|
||||
};
|
||||
|
||||
console.log('提交评价参数:', evaluationParams);
|
||||
|
||||
const result = await updateEvaluationApi(evaluationParams);
|
||||
|
||||
if (result && result.resultCode === 1) {
|
||||
uni.showToast({
|
||||
title: '评价提交成功',
|
||||
icon: 'success'
|
||||
});
|
||||
|
||||
// 发送刷新事件
|
||||
uni.$emit('refreshTaskExecution');
|
||||
|
||||
// 延迟跳转
|
||||
setTimeout(() => {
|
||||
uni.navigateBack();
|
||||
}, 1500);
|
||||
} else {
|
||||
throw new Error(result?.message || result?.msg || '提交失败');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('提交评价失败:', error);
|
||||
uni.showToast({
|
||||
title: '提交失败,请重试',
|
||||
icon: 'error'
|
||||
});
|
||||
} finally {
|
||||
isSubmitting.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
// 工具函数
|
||||
const formatDateTime = (dateStr: string) => {
|
||||
if (!dateStr) return '';
|
||||
const date = new Date(dateStr);
|
||||
return `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()} ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.evaluation-page {
|
||||
min-height: 100vh;
|
||||
background-color: #f5f7fa;
|
||||
padding: 16px;
|
||||
box-sizing: border-box;
|
||||
|
||||
&:has(.bottom-submit) {
|
||||
padding-bottom: 80px; /* 有提交按钮时留出空间 */
|
||||
}
|
||||
}
|
||||
|
||||
.loading-indicator {
|
||||
text-align: center;
|
||||
color: #999;
|
||||
padding: 40px 15px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.evaluation-content {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* 左侧:作品预览和详情 */
|
||||
.work-preview-section {
|
||||
flex: 1;
|
||||
background-color: #fff;
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.work-preview {
|
||||
margin-bottom: 24px;
|
||||
|
||||
.preview-title-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.preview-title-icon {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.preview-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.preview-content {
|
||||
min-height: 200px;
|
||||
background-color: #fff;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.task-info {
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 15px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
margin-bottom: 8px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&.inline {
|
||||
display: inline-flex;
|
||||
margin-right: 24px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.info-label {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
min-width: 80px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.submitted-content {
|
||||
.content-title {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
margin-bottom: 12px;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.content-display {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.content-item {
|
||||
margin-bottom: 16px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.content-label {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
margin-bottom: 8px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.content-value {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.text-content {
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.media-content {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.empty-content {
|
||||
text-align: center;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* 右侧:评价功能 */
|
||||
.evaluation-section {
|
||||
flex: 1;
|
||||
background-color: #fff;
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 16px;
|
||||
|
||||
.section-icon {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.ai-button {
|
||||
padding: 6px 12px;
|
||||
background-color: #1890ff;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
background-color: #1976d2;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background-color: #d9d9d9;
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
.ai-text {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.rating-section {
|
||||
margin-bottom: 24px;
|
||||
|
||||
.rating-scale {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.rating-option {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
padding: 8px 4px;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
border: 2px solid transparent;
|
||||
min-width: 0;
|
||||
|
||||
&.active {
|
||||
background-color: rgba(24, 144, 255, 0.1);
|
||||
border-color: #1890ff;
|
||||
}
|
||||
|
||||
&:hover:not(.disabled) {
|
||||
background-color: rgba(0, 0, 0, 0.02);
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
.rating-circle {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background-color: #f5f5f5;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #666;
|
||||
margin: 0 auto 6px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.rating-option.active .rating-circle {
|
||||
background-color: #1890ff;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.rating-label {
|
||||
font-size: 11px;
|
||||
color: #666;
|
||||
display: block;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.rating-option.active .rating-label {
|
||||
color: #1890ff;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.comment-section {
|
||||
margin-bottom: 24px;
|
||||
|
||||
.comment-label {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
margin-bottom: 8px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.comment-input {
|
||||
width: 100%;
|
||||
min-height: 120px;
|
||||
padding: 12px;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
resize: vertical;
|
||||
box-sizing: border-box;
|
||||
|
||||
&:focus {
|
||||
border-color: #1890ff;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background-color: #f5f5f5;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ai-evaluation-section {
|
||||
margin-bottom: 24px;
|
||||
|
||||
.ai-result {
|
||||
margin-top: 16px;
|
||||
padding: 16px;
|
||||
background-color: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
border-left: 4px solid #1890ff;
|
||||
|
||||
.ai-result-title {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
margin-bottom: 8px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.ai-result-content {
|
||||
font-size: 14px;
|
||||
color: #555;
|
||||
line-height: 1.6;
|
||||
word-break: break-word;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 底部固定提交按钮 */
|
||||
.bottom-submit {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: #fff;
|
||||
padding: 16px;
|
||||
border-top: 1px solid #e5e5e5;
|
||||
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.06);
|
||||
z-index: 1000;
|
||||
|
||||
.submit-button {
|
||||
width: 100%;
|
||||
height: 44px;
|
||||
background-color: #1890ff;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
background-color: #1976d2;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background-color: #d9d9d9;
|
||||
color: #999;
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
.button-icon {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.button-text {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 提交遮罩层样式 */
|
||||
.submit-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.submit-loading {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #fff;
|
||||
padding: 30px;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 3px solid #f3f3f3;
|
||||
border-top: 3px solid #1890ff;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media screen and (max-width: 768px) {
|
||||
.evaluation-content {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.rating-scale {
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.rating-option {
|
||||
min-width: 0;
|
||||
padding: 6px 2px;
|
||||
}
|
||||
|
||||
.rating-circle {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 12px;
|
||||
margin: 0 auto 4px;
|
||||
}
|
||||
|
||||
.rating-label {
|
||||
font-size: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
716
src/pages/view/routine/yishiyice/jf/pjtj.vue
Normal file
716
src/pages/view/routine/yishiyice/jf/pjtj.vue
Normal file
@ -0,0 +1,716 @@
|
||||
<template>
|
||||
<view class="score-statistics-page">
|
||||
<!-- 页面标题横幅 -->
|
||||
<view class="page-banner">
|
||||
<view class="banner-content">
|
||||
<view class="banner-title-wrapper">
|
||||
<text class="banner-icon">🏆</text>
|
||||
<view class="banner-text">
|
||||
<text class="banner-title">积分统计</text>
|
||||
<text class="banner-subtitle">教师积分汇总</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="total-score">
|
||||
<text class="score-number">{{ totalScore }}</text>
|
||||
<text class="score-label">总积分</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 学期选择 -->
|
||||
<view class="filter-bar">
|
||||
<view class="filter-left">
|
||||
<text class="filter-icon">📅</text>
|
||||
<text class="filter-text">学期筛选</text>
|
||||
</view>
|
||||
<view class="filter-right">
|
||||
<uni-data-select
|
||||
v-model="selectedXqId"
|
||||
:localdata="xqList"
|
||||
placeholder="选择学期"
|
||||
@change="handleXqChange"
|
||||
class="xq-select"
|
||||
></uni-data-select>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 统计摘要 -->
|
||||
<view class="summary-section">
|
||||
<view class="summary-item">
|
||||
<text class="summary-number">{{ teacherCount }}</text>
|
||||
<text class="summary-label">教师数</text>
|
||||
</view>
|
||||
<view class="summary-item">
|
||||
<text class="summary-number">{{ totalTasks }}</text>
|
||||
<text class="summary-label">任务总数</text>
|
||||
</view>
|
||||
<view class="summary-item">
|
||||
<text class="summary-number">{{ evaluatedTasks }}</text>
|
||||
<text class="summary-label">已评价</text>
|
||||
</view>
|
||||
<view class="summary-item">
|
||||
<text class="summary-number">{{ unevaluatedTasks }}</text>
|
||||
<text class="summary-label">未评价</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 教师积分列表 -->
|
||||
<view class="teacher-list">
|
||||
<scroll-view
|
||||
scroll-y
|
||||
class="list-scroll-view"
|
||||
@scrolltolower="loadMore"
|
||||
lower-threshold="100"
|
||||
>
|
||||
<!-- 加载状态 -->
|
||||
<view v-if="isLoading && teacherScoreList.length === 0" class="loading-indicator">
|
||||
<text class="loading-icon">⏳</text>
|
||||
<text class="loading-text">加载中...</text>
|
||||
</view>
|
||||
|
||||
<!-- 教师积分列表 -->
|
||||
<template v-else-if="teacherScoreList.length > 0">
|
||||
<view
|
||||
v-for="(teacher, index) in teacherScoreList"
|
||||
:key="teacher.rwzxfzr"
|
||||
class="teacher-card"
|
||||
>
|
||||
<view class="card-main">
|
||||
<!-- 排名标识 -->
|
||||
<view class="rank-badge" :class="getRankClass(index)">
|
||||
<text class="rank-number">{{ index + 1 }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 教师姓名 -->
|
||||
<text class="teacher-name">{{ teacher.rwzxfzrxm || '未知' }}</text>
|
||||
|
||||
<!-- 积分徽章 -->
|
||||
<view class="score-badge">
|
||||
<text class="score-value">{{ teacher.totalScore || 0 }}</text>
|
||||
<text class="score-unit">分</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="teacher-info">
|
||||
|
||||
<view class="teacher-stats">
|
||||
<view class="stat-item">
|
||||
<text class="stat-icon">📋</text>
|
||||
<text class="stat-text">任务:{{ teacher.taskCount || 0 }}个</text>
|
||||
</view>
|
||||
<view class="stat-item">
|
||||
<text class="stat-icon">✅</text>
|
||||
<text class="stat-text">已评:{{ teacher.evaluatedCount || 0 }}</text>
|
||||
</view>
|
||||
<view class="stat-item">
|
||||
<text class="stat-icon">⏳</text>
|
||||
<text class="stat-text">待评:{{ teacher.unevaluatedCount || 0 }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view v-else class="empty-state">
|
||||
<text class="empty-icon">📊</text>
|
||||
<text class="empty-text">暂无积分数据</text>
|
||||
<text class="empty-hint">当前学期还没有教师积分记录</text>
|
||||
</view>
|
||||
|
||||
<!-- 加载更多 -->
|
||||
<view v-if="isLoading && teacherScoreList.length > 0" class="loading-more">
|
||||
<text class="loading-more-icon">⏳</text>
|
||||
<text class="loading-more-text">加载中...</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<!-- 底部返回按钮 -->
|
||||
<view class="bottom-actions">
|
||||
<button class="back-btn" @click="goBack">返回</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted } from "vue";
|
||||
import { onShow } from "@dcloudio/uni-app";
|
||||
import { getScoreStatisticsApi } from "@/api/base/rwzxApi";
|
||||
import { getXqList } from "@/api/base/kcjbApi";
|
||||
|
||||
// 教师积分数据接口
|
||||
interface TeacherScore {
|
||||
rwzxfzr: string; // 教师ID
|
||||
rwzxfzrxm: string; // 教师姓名
|
||||
totalScore: number; // 总积分
|
||||
taskCount: number; // 任务总数
|
||||
evaluatedCount: number; // 已评价任务数
|
||||
unevaluatedCount: number; // 未评价任务数
|
||||
}
|
||||
|
||||
// 响应式数据
|
||||
const selectedXqId = ref('');
|
||||
const xqList = ref<Array<{ value: string; text: string }>>([]);
|
||||
const teacherScoreList = ref<TeacherScore[]>([]);
|
||||
const isLoading = ref(false);
|
||||
|
||||
// 计算属性 - 统计数据
|
||||
const teacherCount = computed(() => teacherScoreList.value.length);
|
||||
|
||||
const totalScore = computed(() => {
|
||||
return teacherScoreList.value.reduce((sum, teacher) => sum + (Number(teacher.totalScore) || 0), 0);
|
||||
});
|
||||
|
||||
const totalTasks = computed(() => {
|
||||
return teacherScoreList.value.reduce((sum, teacher) => sum + (Number(teacher.taskCount) || 0), 0);
|
||||
});
|
||||
|
||||
const evaluatedTasks = computed(() => {
|
||||
return teacherScoreList.value.reduce((sum, teacher) => sum + (Number(teacher.evaluatedCount) || 0), 0);
|
||||
});
|
||||
|
||||
const unevaluatedTasks = computed(() => {
|
||||
return teacherScoreList.value.reduce((sum, teacher) => sum + (Number(teacher.unevaluatedCount) || 0), 0);
|
||||
});
|
||||
|
||||
// 获取学期列表
|
||||
const loadXqList = async () => {
|
||||
try {
|
||||
const response: any = await getXqList();
|
||||
console.log('学期列表原始数据:', response);
|
||||
|
||||
// 转换数据格式为 uni-data-select 需要的格式
|
||||
const data = Array.isArray(response) ? response : (response?.result || response?.data || response?.rows || []);
|
||||
console.log('提取的学期数据:', data);
|
||||
|
||||
xqList.value = data.map((item: any) => ({
|
||||
value: item.id,
|
||||
text: item.xqmc || item.name
|
||||
}));
|
||||
console.log('转换后的下拉选项:', xqList.value);
|
||||
|
||||
// 查找当前学期(dqxq = "是")
|
||||
const currentXq = data.find((item: any) => item.dqxq === '是');
|
||||
if (currentXq) {
|
||||
selectedXqId.value = currentXq.id;
|
||||
console.log('默认选择当前学期:', selectedXqId.value);
|
||||
} else if (xqList.value.length > 0) {
|
||||
// 如果没有当前学期,默认选择第一条
|
||||
selectedXqId.value = xqList.value[0].value;
|
||||
console.log('默认选择第一个学期:', selectedXqId.value);
|
||||
}
|
||||
|
||||
// 自动触发查询
|
||||
if (selectedXqId.value) {
|
||||
loadScoreStatistics();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取学期列表失败:', error);
|
||||
xqList.value = [];
|
||||
}
|
||||
};
|
||||
|
||||
// 处理学期选择变化
|
||||
const handleXqChange = (value: string) => {
|
||||
console.log('选择的学期ID:', value);
|
||||
loadScoreStatistics();
|
||||
};
|
||||
|
||||
// 加载积分统计数据
|
||||
const loadScoreStatistics = async () => {
|
||||
if (isLoading.value) return;
|
||||
|
||||
isLoading.value = true;
|
||||
try {
|
||||
console.log('加载积分统计,学期ID:', selectedXqId.value);
|
||||
|
||||
const params: any = {
|
||||
xqId: selectedXqId.value
|
||||
};
|
||||
|
||||
const response = await getScoreStatisticsApi(params);
|
||||
console.log('积分统计API响应:', response);
|
||||
|
||||
if (response && response.resultCode === 1) {
|
||||
teacherScoreList.value = response.result || [];
|
||||
console.log('积分统计数据:', teacherScoreList.value);
|
||||
} else {
|
||||
teacherScoreList.value = [];
|
||||
uni.showToast({
|
||||
title: response?.message || '查询失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载积分统计失败:', error);
|
||||
uni.showToast({
|
||||
title: '加载失败',
|
||||
icon: 'error'
|
||||
});
|
||||
teacherScoreList.value = [];
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 获取排名样式类
|
||||
const getRankClass = (index: number) => {
|
||||
if (index === 0) return 'rank-gold';
|
||||
if (index === 1) return 'rank-silver';
|
||||
if (index === 2) return 'rank-bronze';
|
||||
return 'rank-normal';
|
||||
};
|
||||
|
||||
// 加载更多(暂不需要,因为一次性加载所有数据)
|
||||
const loadMore = () => {
|
||||
// 预留接口
|
||||
};
|
||||
|
||||
// 返回上一页
|
||||
const goBack = () => {
|
||||
uni.navigateBack();
|
||||
};
|
||||
|
||||
// 页面加载
|
||||
onMounted(() => {
|
||||
console.log('积分统计页面加载');
|
||||
loadXqList();
|
||||
});
|
||||
|
||||
// 页面显示时刷新数据
|
||||
onShow(() => {
|
||||
console.log('页面显示,刷新数据');
|
||||
if (selectedXqId.value) {
|
||||
loadScoreStatistics();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.score-statistics-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
background: linear-gradient(180deg, #f0f5ff 0%, #f5f7fa 100%);
|
||||
}
|
||||
|
||||
// 页面横幅样式
|
||||
.page-banner {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
padding: 20px 16px;
|
||||
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
|
||||
|
||||
.banner-content {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.banner-title-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.banner-icon {
|
||||
font-size: 32px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.banner-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.banner-title {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.banner-subtitle {
|
||||
font-size: 13px;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.total-score {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
backdrop-filter: blur(10px);
|
||||
padding: 10px 16px;
|
||||
border-radius: 12px;
|
||||
min-width: 70px;
|
||||
}
|
||||
|
||||
.score-number {
|
||||
font-size: 26px;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.score-label {
|
||||
font-size: 11px;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
margin-top: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
// 学期筛选栏
|
||||
.filter-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 14px 16px;
|
||||
background-color: #ffffff;
|
||||
border-bottom: 1px solid #e9ecef;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
||||
gap: 12px;
|
||||
|
||||
.filter-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex-shrink: 0;
|
||||
|
||||
.filter-icon {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.filter-text {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #2c3e50;
|
||||
}
|
||||
}
|
||||
|
||||
.filter-right {
|
||||
flex: 1;
|
||||
max-width: 200px;
|
||||
|
||||
.xq-select {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 统计摘要
|
||||
.summary-section {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
padding: 16px;
|
||||
background-color: #ffffff;
|
||||
margin-bottom: 8px;
|
||||
border-bottom: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.summary-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
|
||||
.summary-number {
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
color: #667eea;
|
||||
}
|
||||
|
||||
.summary-label {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
// 教师列表
|
||||
.teacher-list {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.list-scroll-view {
|
||||
flex: 1;
|
||||
padding: 12px 16px 80px 16px;
|
||||
box-sizing: border-box;
|
||||
height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 教师卡片
|
||||
.teacher-card {
|
||||
background: linear-gradient(135deg, #ffffff 0%, #f5f7ff 100%);
|
||||
border-radius: 16px;
|
||||
margin-bottom: 16px;
|
||||
box-shadow:
|
||||
0 2px 16px rgba(0, 0, 0, 0.08),
|
||||
0 0 0 1px rgba(102, 126, 234, 0.1);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
|
||||
&:active {
|
||||
transform: translateY(2px) scale(0.98);
|
||||
box-shadow:
|
||||
0 1px 8px rgba(0, 0, 0, 0.12),
|
||||
0 0 0 1px rgba(102, 126, 234, 0.15);
|
||||
}
|
||||
|
||||
// 左侧彩条
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 5px;
|
||||
background: linear-gradient(180deg, #667eea 0%, #764ba2 100%);
|
||||
border-radius: 16px 0 0 16px;
|
||||
}
|
||||
|
||||
.card-main {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 16px;
|
||||
padding-left: 20px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.rank-badge {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||
flex-shrink: 0;
|
||||
border: 2px solid #ffffff;
|
||||
|
||||
.rank-number {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
&.rank-gold {
|
||||
background: linear-gradient(135deg, #ffd700 0%, #ffb300 100%);
|
||||
}
|
||||
|
||||
&.rank-silver {
|
||||
background: linear-gradient(135deg, #c0c0c0 0%, #a8a8a8 100%);
|
||||
}
|
||||
|
||||
&.rank-bronze {
|
||||
background: linear-gradient(135deg, #cd7f32 0%, #b87333 100%);
|
||||
}
|
||||
|
||||
&.rank-normal {
|
||||
background: linear-gradient(135deg, #78909c 0%, #607d8b 100%);
|
||||
}
|
||||
}
|
||||
|
||||
.teacher-name {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #1a202c;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.score-badge {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 2px;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
padding: 8px 14px;
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);
|
||||
flex-shrink: 0;
|
||||
|
||||
.score-value {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.score-unit {
|
||||
font-size: 12px;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
}
|
||||
|
||||
.teacher-info {
|
||||
padding: 12px 16px 16px 16px;
|
||||
|
||||
.teacher-stats {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.stat-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 6px 10px;
|
||||
background: rgba(102, 126, 234, 0.05);
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(102, 126, 234, 0.1);
|
||||
|
||||
.stat-icon {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.stat-text {
|
||||
font-size: 12px;
|
||||
color: #4a5568;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 加载状态
|
||||
.loading-indicator {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 60px 20px;
|
||||
|
||||
.loading-icon {
|
||||
font-size: 32px;
|
||||
margin-bottom: 12px;
|
||||
animation: pulse 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
color: #667eea;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
// 空状态
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 80px 20px;
|
||||
|
||||
.empty-icon {
|
||||
font-size: 64px;
|
||||
margin-bottom: 16px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 16px;
|
||||
color: #4a5568;
|
||||
font-weight: 600;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.empty-hint {
|
||||
font-size: 13px;
|
||||
color: #a0aec0;
|
||||
}
|
||||
}
|
||||
|
||||
// 加载更多
|
||||
.loading-more {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
padding: 20px;
|
||||
|
||||
.loading-more-icon {
|
||||
font-size: 18px;
|
||||
animation: pulse 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.loading-more-text {
|
||||
color: #667eea;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
// 底部操作
|
||||
.bottom-actions {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 12px 16px;
|
||||
background: #ffffff;
|
||||
border-top: 1px solid #e9ecef;
|
||||
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.05);
|
||||
|
||||
.back-btn {
|
||||
width: 100%;
|
||||
height: 44px;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 22px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);
|
||||
|
||||
&:active {
|
||||
transform: translateY(1px);
|
||||
box-shadow: 0 1px 4px rgba(102, 126, 234, 0.3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 动画
|
||||
@keyframes pulse {
|
||||
0%, 100% {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
opacity: 0.6;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
}
|
||||
|
||||
// 深度选择器用于第三方组件样式调整
|
||||
:deep(.filter-right .uni-data-select) {
|
||||
width: 100%;
|
||||
|
||||
.uni-select {
|
||||
background-color: #f5f7ff;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #d4dbff;
|
||||
height: 36px;
|
||||
min-height: 36px;
|
||||
}
|
||||
|
||||
.uni-select__input-text {
|
||||
color: #212529;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.uni-select__input-placeholder {
|
||||
color: #adb5bd;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.uni-select__selector {
|
||||
padding: 0 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -9,83 +9,88 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 完成情况统计 -->
|
||||
<view class="completion-summary">
|
||||
<view class="summary-header">
|
||||
<text class="summary-title">完成情况:{{ completionStats.completed }} | {{ completionStats.total }}</text>
|
||||
<!-- 执行情况统计 -->
|
||||
<view class="task-summary">
|
||||
<view class="summary-item pending-evaluate" :class="{ active: taskFilter === 'pendingEvaluate' }" @click="setTaskFilter('pendingEvaluate')">
|
||||
<text class="summary-count">{{ executionStats.pendingEvaluateNum }}</text>
|
||||
<text class="summary-label">待评价</text>
|
||||
</view>
|
||||
|
||||
<!-- Tab切换 -->
|
||||
<view class="tab-container">
|
||||
<view
|
||||
class="tab-item"
|
||||
:class="{ active: activeTab === 'submitted' }"
|
||||
@click="switchTab('submitted')"
|
||||
>
|
||||
<text class="tab-text">已提交</text>
|
||||
<text class="tab-count">({{ completionStats.completed }})</text>
|
||||
</view>
|
||||
<view
|
||||
class="tab-item"
|
||||
:class="{ active: activeTab === 'pending' }"
|
||||
@click="switchTab('pending')"
|
||||
>
|
||||
<text class="tab-text">未提交</text>
|
||||
<text class="tab-count">({{ completionStats.pending }})</text>
|
||||
</view>
|
||||
<view class="summary-item evaluated" :class="{ active: taskFilter === 'evaluated' }" @click="setTaskFilter('evaluated')">
|
||||
<text class="summary-count">{{ executionStats.evaluatedNum }}</text>
|
||||
<text class="summary-label">已评价</text>
|
||||
</view>
|
||||
<view class="summary-item not-submitted" :class="{ active: taskFilter === 'notSubmitted' }" @click="setTaskFilter('notSubmitted')">
|
||||
<text class="summary-count">{{ executionStats.notSubmittedNum }}</text>
|
||||
<text class="summary-label">未提交</text>
|
||||
</view>
|
||||
<view class="summary-item submitted" :class="{ active: taskFilter === 'submitted' }" @click="setTaskFilter('submitted')">
|
||||
<text class="summary-count">{{ executionStats.submittedNum }}</text>
|
||||
<text class="summary-label">已提交</text>
|
||||
</view>
|
||||
<view class="summary-item" :class="{ active: taskFilter === 'all' }" @click="setTaskFilter('all')">
|
||||
<text class="summary-count">{{ executionStats.allNum }}</text>
|
||||
<text class="summary-label">全部</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 执行人员列表 -->
|
||||
<view class="execution-list">
|
||||
<scroll-view scroll-y class="execution-scroll">
|
||||
<view v-if="loading" class="loading-container">
|
||||
<text class="loading-text">加载中...</text>
|
||||
</view>
|
||||
|
||||
<view v-else-if="filteredExecutionList.length === 0" class="empty-state">
|
||||
<text class="empty-text">{{ activeTab === 'submitted' ? '暂无已提交记录' : '暂无未提交记录' }}</text>
|
||||
</view>
|
||||
<view v-if="loading" class="loading-container">
|
||||
<text class="loading-text">加载中...</text>
|
||||
</view>
|
||||
|
||||
<view v-else-if="filteredExecutionList.length === 0" class="empty-state">
|
||||
<text class="empty-icon">📦</text>
|
||||
<text class="empty-text">{{ getEmptyStateText() }}</text>
|
||||
</view>
|
||||
|
||||
<view v-else>
|
||||
<view
|
||||
v-for="execution in filteredExecutionList"
|
||||
:key="execution.id"
|
||||
class="execution-item"
|
||||
@click="viewExecutionDetail(execution)"
|
||||
>
|
||||
<!-- 执行人信息 -->
|
||||
<view class="executor-info">
|
||||
<view class="executor-avatar">
|
||||
<text class="avatar-text">{{ execution.rwzxfzrxm?.charAt(0) || '?' }}</text>
|
||||
</view>
|
||||
<view class="executor-details">
|
||||
<text class="executor-name">{{ execution.rwzxfzrxm }}</text>
|
||||
</view>
|
||||
<view v-else>
|
||||
<view
|
||||
v-for="execution in filteredExecutionList"
|
||||
:key="execution.id"
|
||||
class="execution-item"
|
||||
@click="viewExecutionDetail(execution)"
|
||||
>
|
||||
<view class="execution-header">
|
||||
<view class="execution-title-row">
|
||||
<view
|
||||
class="status-dot"
|
||||
:class="getStatusDotClass(execution)"
|
||||
></view>
|
||||
<text class="execution-title">{{ execution.rwzxfzrxm }}</text>
|
||||
<text class="arrow-icon">›</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 执行状态 -->
|
||||
<view class="execution-status">
|
||||
<view :class="['status-badge', execution.rwzxzt === 'A' ? 'completed' : 'pending']">
|
||||
<text class="status-text">{{ execution.rwzxzt === 'A' ? '已完成' : '未完成' }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 完成时间 -->
|
||||
<view v-if="execution.rwzxzt === 'A' && execution.rwzxtime" class="completion-time">
|
||||
<text class="time-text">{{ formatDateTime(execution.rwzxtime) }}</text>
|
||||
</view>
|
||||
<view class="execution-body">
|
||||
<view class="execution-detail">
|
||||
<text class="detail-label">提交状态:</text>
|
||||
<text class="detail-content">{{ getSubmitStatusText(execution) }}</text>
|
||||
</view>
|
||||
<view class="execution-detail">
|
||||
<text class="detail-label">评价状态:</text>
|
||||
<text class="detail-content">{{ getEvaluateStatusText(execution) }}</text>
|
||||
</view>
|
||||
<view v-if="execution.rwzxzt === 'A' && execution.rwzxtime" class="execution-detail">
|
||||
<text class="detail-label">提交时间:</text>
|
||||
<text class="detail-content">{{ formatDateTime(execution.rwzxtime) }}</text>
|
||||
</view>
|
||||
<view v-if="execution.rwzxnr" class="execution-detail">
|
||||
<text class="detail-label">执行内容:</text>
|
||||
<text class="detail-content">{{ execution.rwzxnr }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, computed, onLoad, onUnmounted } from "vue";
|
||||
import { ref, reactive, computed, onUnmounted } from "vue";
|
||||
import { onLoad as onPageLoad, onUnload } from "@dcloudio/uni-app";
|
||||
import { executedInfoByRwIdApi } from "@/api/base/rwzxApi";
|
||||
import { rwFindInfoByRwId } from "@/api/base/server";
|
||||
|
||||
// 接口类型定义
|
||||
interface TaskInfo {
|
||||
@ -110,6 +115,12 @@ interface ExecutionItem {
|
||||
rwzxnr: string;
|
||||
createTime: string;
|
||||
jsId?: string; // 教师ID(可选)
|
||||
ispj?: string; // 任务是否评价:A: 已评价, B: 未评价
|
||||
rwpjr?: string; // 任务评价人id
|
||||
rwpjrxm?: string; // 任务评价人姓名
|
||||
rwpjjf?: string; // 任务评价积分
|
||||
rwpjms?: string; // 任务评价描述
|
||||
rwpjtime?: string; // 任务评价时间
|
||||
}
|
||||
|
||||
// 响应式数据
|
||||
@ -125,37 +136,41 @@ const taskInfo = ref<TaskInfo>({
|
||||
});
|
||||
|
||||
const courseId = ref('');
|
||||
const activeTab = ref('submitted'); // submitted: 已提交, pending: 未提交
|
||||
const taskFilter = ref('pendingEvaluate'); // 'all', 'pendingEvaluate', 'evaluated', 'notSubmitted', 'submitted'
|
||||
const loading = ref(false);
|
||||
const executionList = ref<ExecutionItem[]>([]);
|
||||
|
||||
const completedCount = ref(0);
|
||||
const pendingCount = ref(0);
|
||||
|
||||
// 统计数据
|
||||
const completionStats = computed(() => {
|
||||
const total = completedCount.value + pendingCount.value;
|
||||
|
||||
return {
|
||||
total,
|
||||
completed: completedCount.value,
|
||||
pending: pendingCount.value
|
||||
};
|
||||
// 执行情况统计
|
||||
const executionStats = ref({
|
||||
allNum: 0,
|
||||
pendingEvaluateNum: 0,
|
||||
evaluatedNum: 0,
|
||||
notSubmittedNum: 0,
|
||||
submittedNum: 0
|
||||
});
|
||||
|
||||
// 根据Tab过滤执行列表(基于iszxwc字段:A=已提交,B=未提交)
|
||||
// 根据筛选条件过滤执行列表
|
||||
const filteredExecutionList = computed(() => {
|
||||
return executionList.value.filter(item => {
|
||||
if (activeTab.value === 'submitted') {
|
||||
return item.iszxwc === 'A'; // 已提交
|
||||
} else {
|
||||
return item.iszxwc === 'B'; // 未提交
|
||||
}
|
||||
});
|
||||
if (taskFilter.value === 'all') {
|
||||
return executionList.value;
|
||||
} else if (taskFilter.value === 'pendingEvaluate') {
|
||||
// 已提交但待评价
|
||||
return executionList.value.filter(item => item.iszxwc === 'A' && item.ispj === 'B');
|
||||
} else if (taskFilter.value === 'evaluated') {
|
||||
// 已提交且已评价
|
||||
return executionList.value.filter(item => item.iszxwc === 'A' && item.ispj === 'A');
|
||||
} else if (taskFilter.value === 'notSubmitted') {
|
||||
// 未提交
|
||||
return executionList.value.filter(item => item.iszxwc === 'B');
|
||||
} else if (taskFilter.value === 'submitted') {
|
||||
// 已提交
|
||||
return executionList.value.filter(item => item.iszxwc === 'A');
|
||||
}
|
||||
return executionList.value;
|
||||
});
|
||||
|
||||
// 页面加载
|
||||
onPageLoad((options: any) => {
|
||||
onPageLoad(async (options: any) => {
|
||||
console.log('任务执行页面接收到的参数:', options);
|
||||
|
||||
// 从全局存储中获取任务数据
|
||||
@ -171,11 +186,7 @@ onPageLoad((options: any) => {
|
||||
// 清除存储的任务数据
|
||||
uni.removeStorageSync('taskExecutionData');
|
||||
|
||||
loadExecutionList();
|
||||
// 在加载执行列表后再加载统计数据
|
||||
setTimeout(() => {
|
||||
loadStatistics();
|
||||
}, 100);
|
||||
await loadExecutionList();
|
||||
|
||||
// 设置刷新事件监听
|
||||
setupRefreshListener();
|
||||
@ -190,48 +201,99 @@ onPageLoad((options: any) => {
|
||||
}, 1500);
|
||||
}
|
||||
} else {
|
||||
console.error('缺少任务信息参数');
|
||||
uni.showToast({
|
||||
title: '缺少任务信息',
|
||||
icon: 'error'
|
||||
});
|
||||
setTimeout(() => {
|
||||
uni.navigateBack();
|
||||
}, 1500);
|
||||
// 如果没有存储的任务数据,尝试从URL参数或通过API获取
|
||||
console.log('未找到存储的任务数据,尝试从URL参数获取任务ID');
|
||||
|
||||
const taskId = options.taskId || options.id;
|
||||
const courseIdParam = options.courseId;
|
||||
|
||||
if (taskId) {
|
||||
console.log('从URL参数获取到任务ID:', taskId);
|
||||
try {
|
||||
// 通过API获取任务信息
|
||||
await loadTaskInfoById(taskId, courseIdParam);
|
||||
await loadExecutionList();
|
||||
setupRefreshListener();
|
||||
} catch (error) {
|
||||
console.error('通过API获取任务信息失败:', error);
|
||||
uni.showToast({
|
||||
title: '获取任务信息失败',
|
||||
icon: 'error'
|
||||
});
|
||||
setTimeout(() => {
|
||||
uni.navigateBack();
|
||||
}, 1500);
|
||||
}
|
||||
} else {
|
||||
console.error('缺少任务信息参数');
|
||||
uni.showToast({
|
||||
title: '缺少任务信息',
|
||||
icon: 'error'
|
||||
});
|
||||
setTimeout(() => {
|
||||
uni.navigateBack();
|
||||
}, 1500);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 加载统计数据(基于iszxwc字段统计)
|
||||
// 通过任务ID加载任务信息
|
||||
const loadTaskInfoById = async (taskId: string, courseIdParam?: string) => {
|
||||
try {
|
||||
console.log('开始通过API获取任务信息,任务ID:', taskId);
|
||||
|
||||
const response = await rwFindInfoByRwId({ rwId: taskId });
|
||||
console.log('任务信息API响应:', response);
|
||||
|
||||
if (response && response.result) {
|
||||
taskInfo.value = response.result;
|
||||
courseId.value = courseIdParam || taskInfo.value.rwlyId || '';
|
||||
console.log('成功获取任务信息:', taskInfo.value);
|
||||
console.log('课程ID:', courseId.value);
|
||||
} else {
|
||||
throw new Error('API返回数据格式错误');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('通过API获取任务信息失败:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
// 加载统计数据
|
||||
const loadStatistics = async () => {
|
||||
try {
|
||||
console.log('开始加载统计数据,任务ID:', taskInfo.value.id);
|
||||
console.log('当前执行列表数据:', executionList.value);
|
||||
|
||||
// 只调用executedInfoByRwId接口
|
||||
const response = await executedInfoByRwIdApi({ rwId: taskInfo.value.id });
|
||||
console.log('执行情况API响应:', response);
|
||||
const allData = executionList.value;
|
||||
|
||||
// 处理API响应数据
|
||||
let allData = [];
|
||||
if (response && response.resultCode === 1) {
|
||||
allData = response.result || response.rows || response.data || [];
|
||||
} else if (Array.isArray(response)) {
|
||||
allData = response;
|
||||
}
|
||||
// 计算5个统计值
|
||||
executionStats.value = {
|
||||
allNum: allData.length,
|
||||
pendingEvaluateNum: allData.filter(item => item.iszxwc === 'A' && item.ispj === 'B').length,
|
||||
evaluatedNum: allData.filter(item => item.iszxwc === 'A' && item.ispj === 'A').length,
|
||||
notSubmittedNum: allData.filter(item => item.iszxwc === 'B').length,
|
||||
submittedNum: allData.filter(item => item.iszxwc === 'A').length
|
||||
};
|
||||
|
||||
// 根据iszxwc字段统计:A=已提交,B=未提交
|
||||
const submittedData = allData.filter(item => item.iszxwc === 'A');
|
||||
const pendingData = allData.filter(item => item.iszxwc === 'B');
|
||||
|
||||
completedCount.value = submittedData.length;
|
||||
pendingCount.value = pendingData.length;
|
||||
|
||||
console.log('统计结果 - 已提交:', completedCount.value, '未提交:', pendingCount.value);
|
||||
console.log('统计结果:', executionStats.value);
|
||||
console.log('详细统计计算:');
|
||||
console.log('- 全部数量:', allData.length);
|
||||
console.log('- 未提交数量:', allData.filter(item => item.iszxwc === 'B').length);
|
||||
console.log('- 已提交数量:', allData.filter(item => item.iszxwc === 'A').length);
|
||||
console.log('- 待评价数量:', allData.filter(item => item.iszxwc === 'A' && item.ispj === 'B').length);
|
||||
console.log('- 已评价数量:', allData.filter(item => item.iszxwc === 'A' && item.ispj === 'A').length);
|
||||
|
||||
} catch (error) {
|
||||
console.error('加载统计数据失败:', error);
|
||||
// 失败时使用默认值
|
||||
completedCount.value = 0;
|
||||
pendingCount.value = 0;
|
||||
executionStats.value = {
|
||||
allNum: 0,
|
||||
pendingEvaluateNum: 0,
|
||||
evaluatedNum: 0,
|
||||
notSubmittedNum: 0,
|
||||
submittedNum: 0
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@ -250,19 +312,19 @@ const loadExecutionList = async () => {
|
||||
if (response) {
|
||||
if (response.hasOwnProperty('resultCode')) {
|
||||
if (response.resultCode === 1 || response.resultCode === 0) {
|
||||
executionData = response.result || response.rows || response.data || [];
|
||||
executionData = response.result || response.rows || (response as any).data || [];
|
||||
} else {
|
||||
throw new Error(response?.message || response?.msg || '获取执行情况失败');
|
||||
throw new Error((response as any)?.message || (response as any)?.msg || '获取执行情况失败');
|
||||
}
|
||||
} else if (response.rows || response.data) {
|
||||
executionData = response.rows || response.data || [];
|
||||
} else if ((response as any).rows || (response as any).data) {
|
||||
executionData = (response as any).rows || (response as any).data || [];
|
||||
} else if (Array.isArray(response)) {
|
||||
executionData = response;
|
||||
}
|
||||
}
|
||||
|
||||
// 设置所有数据,根据iszxwc字段区分状态
|
||||
executionList.value = executionData.map(item => ({
|
||||
executionList.value = executionData.map((item: any) => ({
|
||||
...item,
|
||||
// 根据iszxwc字段设置rwzxzt状态:A=已完成(已提交),B=未完成(未提交)
|
||||
rwzxzt: item.iszxwc === 'A' ? 'A' : 'B'
|
||||
@ -270,6 +332,9 @@ const loadExecutionList = async () => {
|
||||
|
||||
console.log('解析后的执行情况列表:', executionList.value);
|
||||
|
||||
// 数据加载完成后立即更新统计数据
|
||||
loadStatistics();
|
||||
|
||||
} catch (error) {
|
||||
console.error('加载任务执行情况失败:', error);
|
||||
uni.showToast({
|
||||
@ -282,44 +347,113 @@ const loadExecutionList = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
// Tab切换
|
||||
const switchTab = (tab: string) => {
|
||||
activeTab.value = tab;
|
||||
// Tab切换时不需要重新加载数据,通过computed属性filteredExecutionList自动过滤
|
||||
// 设置任务筛选状态
|
||||
const setTaskFilter = (filter: string) => {
|
||||
taskFilter.value = filter;
|
||||
};
|
||||
|
||||
// 获取空状态文本
|
||||
const getEmptyStateText = () => {
|
||||
switch (taskFilter.value) {
|
||||
case 'pendingEvaluate':
|
||||
return '暂无待评价记录';
|
||||
case 'evaluated':
|
||||
return '暂无已评价记录';
|
||||
case 'notSubmitted':
|
||||
return '暂无未提交记录';
|
||||
case 'submitted':
|
||||
return '暂无已提交记录';
|
||||
default:
|
||||
return '暂无执行记录';
|
||||
}
|
||||
};
|
||||
|
||||
// 获取状态点样式
|
||||
const getStatusDotClass = (execution: ExecutionItem) => {
|
||||
if (execution.iszxwc === 'A' && execution.ispj === 'A') {
|
||||
return 'evaluated'; // 已评价
|
||||
} else if (execution.iszxwc === 'A') {
|
||||
return 'pending-evaluate'; // 待评价
|
||||
} else {
|
||||
return 'not-submitted'; // 未提交
|
||||
}
|
||||
};
|
||||
|
||||
// 获取提交状态文本
|
||||
const getSubmitStatusText = (execution: ExecutionItem) => {
|
||||
return execution.iszxwc === 'A' ? '已提交' : '未提交';
|
||||
};
|
||||
|
||||
// 获取评价状态文本
|
||||
const getEvaluateStatusText = (execution: ExecutionItem) => {
|
||||
if (execution.iszxwc === 'B') {
|
||||
return '未提交';
|
||||
}
|
||||
return execution.ispj === 'A' ? '已评价' : '待评价';
|
||||
};
|
||||
|
||||
// 查看执行详情
|
||||
const viewExecutionDetail = (execution: ExecutionItem) => {
|
||||
// 将参数数据存储到全局存储中
|
||||
const params = {
|
||||
id: taskInfo.value.id,
|
||||
jsId: execution.rwzxfzr,
|
||||
executionId: execution.id,
|
||||
isReadOnly: execution.rwzxzt === 'A' // 如果已完成,则为只读模式
|
||||
};
|
||||
console.log('点击执行记录:', execution);
|
||||
|
||||
uni.setStorageSync('taskSubmitData', params);
|
||||
|
||||
// 跳转到任务提交页面
|
||||
uni.navigateTo({
|
||||
url: `/pages/view/routine/yishiyice/kcrwzxtj`,
|
||||
success: () => {
|
||||
console.log('跳转到任务提交页面成功', {
|
||||
taskId: taskInfo.value.id,
|
||||
jsId: execution.rwzxfzr,
|
||||
executionId: execution.id,
|
||||
isReadOnly: params.isReadOnly,
|
||||
executionData: execution
|
||||
});
|
||||
},
|
||||
fail: (error) => {
|
||||
console.error('跳转到任务提交页面失败:', error);
|
||||
uni.showToast({
|
||||
title: '页面跳转失败',
|
||||
icon: 'error'
|
||||
});
|
||||
}
|
||||
});
|
||||
// 判断是否为待评价状态
|
||||
if (execution.iszxwc === 'A' && execution.ispj === 'B') {
|
||||
// 待评价状态,跳转到评价页面
|
||||
const evaluationData = {
|
||||
taskInfo: taskInfo.value,
|
||||
executionInfo: execution
|
||||
};
|
||||
|
||||
uni.setStorageSync('evaluationData', evaluationData);
|
||||
|
||||
uni.navigateTo({
|
||||
url: '/pages/view/routine/yishiyice/kcxxpj',
|
||||
success: () => {
|
||||
console.log('跳转到评价页面成功', {
|
||||
taskId: taskInfo.value.id,
|
||||
executionId: execution.id,
|
||||
executionData: execution
|
||||
});
|
||||
},
|
||||
fail: (error) => {
|
||||
console.error('跳转到评价页面失败:', error);
|
||||
uni.showToast({
|
||||
title: '页面跳转失败',
|
||||
icon: 'error'
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// 其他状态,跳转到任务提交页面
|
||||
const params = {
|
||||
id: taskInfo.value.id,
|
||||
jsId: execution.rwzxfzr,
|
||||
executionId: execution.id,
|
||||
isReadOnly: execution.rwzxzt === 'A' // 如果已完成,则为只读模式
|
||||
};
|
||||
|
||||
uni.setStorageSync('taskSubmitData', params);
|
||||
|
||||
uni.navigateTo({
|
||||
url: `/pages/view/routine/yishiyice/kcrwzxtj`,
|
||||
success: () => {
|
||||
console.log('跳转到任务提交页面成功', {
|
||||
taskId: taskInfo.value.id,
|
||||
jsId: execution.rwzxfzr,
|
||||
executionId: execution.id,
|
||||
isReadOnly: params.isReadOnly,
|
||||
executionData: execution
|
||||
});
|
||||
},
|
||||
fail: (error) => {
|
||||
console.error('跳转到任务提交页面失败:', error);
|
||||
uni.showToast({
|
||||
title: '页面跳转失败',
|
||||
icon: 'error'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 工具函数
|
||||
@ -340,11 +474,8 @@ const setupRefreshListener = () => {
|
||||
// 监听任务执行刷新事件
|
||||
uni.$on('refreshTaskExecution', () => {
|
||||
console.log('收到刷新任务执行事件');
|
||||
// 重新加载执行列表和统计数据
|
||||
// 重新加载执行列表(统计数据会在loadExecutionList中自动更新)
|
||||
loadExecutionList();
|
||||
setTimeout(() => {
|
||||
loadStatistics();
|
||||
}, 100);
|
||||
});
|
||||
};
|
||||
|
||||
@ -357,9 +488,7 @@ onUnload(() => {
|
||||
<style lang="scss" scoped>
|
||||
.task-execution-page {
|
||||
min-height: 100vh;
|
||||
background-color: #f5f7fa;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.task-info-card {
|
||||
@ -369,7 +498,7 @@ onUnload(() => {
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
border: none;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
|
||||
.task-title {
|
||||
font-size: 16px;
|
||||
@ -390,175 +519,182 @@ onUnload(() => {
|
||||
}
|
||||
}
|
||||
|
||||
.completion-summary {
|
||||
/* 任务统计 */
|
||||
.task-summary {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
margin: 0;
|
||||
background-color: #fff;
|
||||
padding: 12px 8px;
|
||||
background-color: #ffffff;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
border: none;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
overflow: hidden;
|
||||
|
||||
.summary-header {
|
||||
padding: 16px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
background-color: #f8f9fa;
|
||||
|
||||
.summary-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.tab-container {
|
||||
display: flex;
|
||||
|
||||
.tab-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 12px;
|
||||
background-color: #fff;
|
||||
border-right: 1px solid #f0f0f0;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: #1890ff;
|
||||
|
||||
.tab-text, .tab-count {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.tab-text {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #666;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.tab-count {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
}
|
||||
|
||||
.summary-item {
|
||||
text-align: center;
|
||||
padding: 8px 6px;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
border: 2px solid transparent;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
.summary-item:hover {
|
||||
background-color: rgba(0, 0, 0, 0.02);
|
||||
}
|
||||
|
||||
.summary-item.active {
|
||||
background-color: rgba(24, 144, 255, 0.1);
|
||||
border-color: #1890ff;
|
||||
}
|
||||
|
||||
.summary-item.pending-evaluate.active {
|
||||
background-color: rgba(250, 140, 22, 0.1);
|
||||
border-color: #fa8c16;
|
||||
}
|
||||
|
||||
.summary-item.evaluated.active {
|
||||
background-color: rgba(82, 196, 26, 0.1);
|
||||
border-color: #52c41a;
|
||||
}
|
||||
|
||||
.summary-item.not-submitted.active {
|
||||
background-color: rgba(255, 77, 79, 0.1);
|
||||
border-color: #ff4d4f;
|
||||
}
|
||||
|
||||
.summary-item.submitted.active {
|
||||
background-color: rgba(24, 144, 255, 0.1);
|
||||
border-color: #1890ff;
|
||||
}
|
||||
|
||||
.summary-count {
|
||||
display: block;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.summary-item.pending-evaluate .summary-count {
|
||||
color: #fa8c16;
|
||||
}
|
||||
|
||||
.summary-item.evaluated .summary-count {
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
.summary-item.not-submitted .summary-count {
|
||||
color: #ff4d4f;
|
||||
}
|
||||
|
||||
.summary-item.submitted .summary-count {
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
.summary-label {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* 执行列表 */
|
||||
.execution-list {
|
||||
flex: 1;
|
||||
margin: 0;
|
||||
background-color: #fff;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
border: none;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.execution-scroll {
|
||||
max-height: 60vh;
|
||||
min-height: 200px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.execution-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 16px;
|
||||
background-color: #fff;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:active {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
background-color: white;
|
||||
border-radius: 0;
|
||||
margin-bottom: 0;
|
||||
box-shadow: none;
|
||||
overflow: hidden;
|
||||
transition: all 0.3s ease;
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
}
|
||||
|
||||
.executor-info {
|
||||
.execution-item:active {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
.execution-header {
|
||||
padding: 14px 16px;
|
||||
background: #ffffff;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.execution-title-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
flex: 1;
|
||||
|
||||
.executor-avatar {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.status-dot {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
background-color: #1890ff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.avatar-text {
|
||||
color: #fff;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
background: #999;
|
||||
flex-shrink: 0;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.executor-details {
|
||||
|
||||
.status-dot.evaluated {
|
||||
background: #52c41a;
|
||||
}
|
||||
|
||||
.status-dot.pending-evaluate {
|
||||
background: #fa8c16;
|
||||
}
|
||||
|
||||
.status-dot.not-submitted {
|
||||
background: #ff4d4f;
|
||||
}
|
||||
|
||||
.execution-title {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #1f2937;
|
||||
flex: 1;
|
||||
|
||||
.executor-name {
|
||||
display: block;
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.execution-status {
|
||||
.arrow-icon {
|
||||
font-size: 20px;
|
||||
color: #999;
|
||||
font-weight: bold;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.execution-body {
|
||||
padding: 0 16px 14px 16px;
|
||||
}
|
||||
|
||||
.execution-detail {
|
||||
font-size: 12px;
|
||||
line-height: 1.8;
|
||||
color: #666666;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
gap: 4px;
|
||||
|
||||
.status-badge {
|
||||
padding: 6px 12px;
|
||||
border-radius: 16px;
|
||||
|
||||
&.completed {
|
||||
background-color: #f6ffed;
|
||||
border: 1px solid #b7eb8f;
|
||||
|
||||
.status-text {
|
||||
color: #52c41a;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
&.pending {
|
||||
background-color: #fff2e8;
|
||||
border: 1px solid #ffbb96;
|
||||
|
||||
.status-text {
|
||||
color: #fa8c16;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.completion-time {
|
||||
.time-text {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.execution-detail:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.detail-label {
|
||||
flex-shrink: 0;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.detail-content {
|
||||
margin-left: 4px;
|
||||
flex: 1;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* 通用样式 */
|
||||
.loading-container,
|
||||
.empty-state {
|
||||
display: flex;
|
||||
@ -567,26 +703,25 @@ onUnload(() => {
|
||||
justify-content: center;
|
||||
padding: 40px 20px;
|
||||
color: #666;
|
||||
|
||||
.loading-text,
|
||||
.empty-text {
|
||||
font-size: 14px;
|
||||
}
|
||||
background-color: white;
|
||||
margin: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
// 响应式调整
|
||||
@media screen and (max-width: 375px) {
|
||||
.execution-item {
|
||||
padding: 10px;
|
||||
|
||||
.executor-avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
|
||||
.avatar-text {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.loading-text {
|
||||
font-size: 13px;
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
font-size: 48px;
|
||||
margin-bottom: 12px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 14px;
|
||||
color: #909399;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
</style>
|
||||
@ -1,6 +1,6 @@
|
||||
<!-- src/pages/base/message/detail.vue -->
|
||||
<!-- 任务执行提交页面 -->
|
||||
<template>
|
||||
<view class="message-detail-page" :class="{ 'readonly-mode': isReadOnly }">
|
||||
<view class="rw-detail-page" :class="{ 'readonly-mode': isReadOnly }">
|
||||
<!-- 提交遮罩层 -->
|
||||
<view v-if="isSubmitting" class="submit-overlay">
|
||||
<view class="submit-loading">
|
||||
@ -10,30 +10,62 @@
|
||||
</view>
|
||||
|
||||
<view v-if="isLoading" class="loading-indicator">加载中...</view>
|
||||
<view v-else class="detail-content">
|
||||
<view class="detail-header">
|
||||
<view class="title-tag-row">
|
||||
<text class="detail-title">{{ rw.rwmc }}</text>
|
||||
<view v-else class="content-wrapper">
|
||||
<view class="p-15">
|
||||
<!-- 第一部分:任务要求 -->
|
||||
<view class="rw-info-section">
|
||||
<view class="section-title">任务要求</view>
|
||||
|
||||
<!-- 只读模式提示 -->
|
||||
<view v-if="isReadOnly" class="readonly-tip">
|
||||
<text class="tip-text">📖 只读模式 - 查看已提交的内容</text>
|
||||
</view>
|
||||
|
||||
<!-- 任务名称 -->
|
||||
<view class="info-item">
|
||||
<text class="label">任务名称:</text>
|
||||
<text class="value title-bold">{{ rw.rwmc }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 任务描述 -->
|
||||
<view v-if="rw.rwms" class="info-item">
|
||||
<text class="label">任务描述:</text>
|
||||
<text class="value">{{ rw.rwms }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 任务时间 -->
|
||||
<view v-if="rw.rwkstime" class="info-item">
|
||||
<text class="label">任务时间:</text>
|
||||
<text class="value">{{ rw.rwkstime }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 附件预览 -->
|
||||
<BasicFilePreview
|
||||
v-if="rw.rwfj && rw.fjmx"
|
||||
:file-url="rw.rwfj"
|
||||
:file-name="rw.fjmx"
|
||||
:file-format="getFileFormat(rw.fjmx)"
|
||||
class="mt-15"
|
||||
/>
|
||||
</view>
|
||||
<view v-if="isReadOnly" class="readonly-tip">
|
||||
<text class="tip-text">📖 只读模式 - 查看已提交的内容</text>
|
||||
|
||||
<!-- 第二部分:任务执行 -->
|
||||
<view class="rw-execute-section">
|
||||
<view class="section-title">任务执行</view>
|
||||
<view class="execute-form">
|
||||
<BasicForm :schema="schema" v-model="formData" :disabled="isReadOnly">
|
||||
</BasicForm>
|
||||
</view>
|
||||
</view>
|
||||
<view class="detail-meta">
|
||||
<text>{{ rw.rwkstime }}</text>
|
||||
<!-- <text>{{ messageDetail.timeAgo }}</text>-->
|
||||
</view>
|
||||
</view>
|
||||
<view class="detail-body">
|
||||
<BasicForm :schema="schema" v-model="formData" :disabled="isReadOnly">
|
||||
</BasicForm>
|
||||
</view>
|
||||
<view v-if="!isReadOnly" class="detail-footer">
|
||||
<button class="action-button" @click="saveRwZx" :disabled="isSubmitting">
|
||||
{{ isSubmitting ? '提交中...' : '提交' }}
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
<!-- <view v-else class="empty-state">消息详情未找到</view>-->
|
||||
|
||||
<!-- 提交按钮 - 固定在底部 -->
|
||||
<view v-if="!isReadOnly" class="submit-button-fixed">
|
||||
<button class="action-button" @click="saveRwZx" :disabled="isSubmitting">
|
||||
{{ isSubmitting ? '提交中...' : '提交' }}
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@ -46,6 +78,7 @@ import {navigateBack, showToast} from "@/utils/uniapp";
|
||||
import {useUserStore} from "@/store/modules/user";
|
||||
import { ImageVideoUpload, type FileItem, COMPRESS_PRESETS } from "@/components/ImageVideoUpload";
|
||||
import { attachmentUpload } from "@/api/system/upload";
|
||||
import BasicFilePreview from "@/components/BasicFile/preview.vue";
|
||||
|
||||
interface MessageDetail {
|
||||
id: string; // Assuming an ID is passed or can be derived
|
||||
@ -55,9 +88,23 @@ interface MessageDetail {
|
||||
timeAgo: string;
|
||||
tagText: string;
|
||||
tagType: string;
|
||||
likes?: number;
|
||||
comments?: number;
|
||||
// Add other fields as necessary
|
||||
}
|
||||
|
||||
interface RwInfo {
|
||||
id?: string;
|
||||
rwmc?: string; // 任务名称
|
||||
rwms?: string; // 任务描述
|
||||
rwkstime?: string; // 任务开始时间
|
||||
rwfj?: string; // 任务附件URL
|
||||
fjmx?: string; // 附件名称
|
||||
rwlxes?: any[]; // 任务类型列表
|
||||
rwzxqds?: any[]; // 任务执行清单
|
||||
[key: string]: any; // 允许其他属性
|
||||
}
|
||||
|
||||
const formData: any = ref({})
|
||||
const messageId = ref<string>('');
|
||||
const jsId = ref<string>(''); // 教师ID
|
||||
@ -79,7 +126,7 @@ const messageDetail = ref<MessageDetail | null>({
|
||||
});
|
||||
const isLoading = ref(false);
|
||||
const rwflx: any = ref([])
|
||||
const rw = ref<any>({})
|
||||
const rw = ref<RwInfo>({})
|
||||
const schema = ref<FormsSchema[]>([])
|
||||
const {getUser} = useUserStore()
|
||||
|
||||
@ -202,20 +249,13 @@ async function performSubmit() {
|
||||
})
|
||||
showToast("操作成功!");
|
||||
|
||||
// 发送刷新事件
|
||||
uni.$emit('refreshTaskExecution');
|
||||
|
||||
// 延迟一下再跳转,让用户看到成功提示
|
||||
setTimeout(() => {
|
||||
// 直接跳转到课程详情页面
|
||||
uni.redirectTo({
|
||||
url: `/pages/view/routine/yishiyice/detail?kcjbId=${rw.value.rwlyId}&kcmc=${encodeURIComponent(rw.value.rwmc)}`,
|
||||
success: () => {
|
||||
console.log('跳转到课程详情页面成功');
|
||||
},
|
||||
fail: (error) => {
|
||||
console.error('跳转到课程详情页面失败:', error);
|
||||
// 如果跳转失败,回退到上一页
|
||||
uni.navigateBack({ delta: 1 });
|
||||
}
|
||||
});
|
||||
// 返回到任务执行页面
|
||||
uni.navigateBack();
|
||||
}, 1500);
|
||||
|
||||
} catch (error) {
|
||||
@ -226,6 +266,13 @@ async function performSubmit() {
|
||||
}
|
||||
}
|
||||
|
||||
// 获取文件格式
|
||||
const getFileFormat = (fileName: string) => {
|
||||
if (!fileName) return '';
|
||||
const parts = fileName.split('.');
|
||||
return parts.length > 1 ? parts[parts.length - 1].toLowerCase() : '';
|
||||
};
|
||||
|
||||
const rwzxqds = ref<any[]>([])
|
||||
onLoad(async (options: any) => {
|
||||
console.log('页面加载参数:', options);
|
||||
@ -492,79 +539,49 @@ onLoad(async (options: any) => {
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.message-detail-page {
|
||||
.rw-detail-page {
|
||||
background-color: #f4f5f7;
|
||||
min-height: 100vh;
|
||||
padding: 15px;
|
||||
padding-bottom: 80px; // 为底部按钮留出空间
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.loading-indicator,
|
||||
.empty-state {
|
||||
.loading-indicator {
|
||||
text-align: center;
|
||||
color: #999;
|
||||
padding: 40px 15px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.detail-content {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
.content-wrapper {
|
||||
.p-15 {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.mt-15 {
|
||||
margin-top: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.detail-header {
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
padding-bottom: 15px;
|
||||
// 第一部分:任务要求
|
||||
.rw-info-section {
|
||||
margin-bottom: 20px;
|
||||
|
||||
.title-tag-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start; // Align items to the top
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.detail-title {
|
||||
font-size: 18px;
|
||||
padding: 15px;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
|
||||
.section-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 15px;
|
||||
color: #333;
|
||||
flex: 1; // Allow title to take available space
|
||||
margin-right: 10px; // Space between title and tag
|
||||
line-height: 1.4;
|
||||
text-align: center; // 居中显示
|
||||
border-bottom: 2px solid #007aff;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.tag { // Reuse tag styles from index page if possible, or define here
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0; // Prevent tag from shrinking
|
||||
|
||||
&.notice {
|
||||
background-color: #447ade;
|
||||
}
|
||||
|
||||
&.task {
|
||||
background-color: #19be6b;
|
||||
}
|
||||
|
||||
&.approval {
|
||||
background-color: #ff9f0a;
|
||||
}
|
||||
|
||||
&.submit {
|
||||
background-color: #8e8e93;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.readonly-tip {
|
||||
margin: 8px 0;
|
||||
margin-bottom: 15px;
|
||||
padding: 8px 12px;
|
||||
background-color: #e6f7ff;
|
||||
border: 1px solid #91d5ff;
|
||||
@ -575,59 +592,96 @@ onLoad(async (options: any) => {
|
||||
color: #1890ff;
|
||||
}
|
||||
}
|
||||
|
||||
.detail-meta {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
|
||||
text {
|
||||
margin-right: 15px;
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
margin-bottom: 15px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.label {
|
||||
width: 80px;
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.value {
|
||||
flex: 1;
|
||||
color: #333;
|
||||
font-size: 14px;
|
||||
word-break: break-word;
|
||||
|
||||
&.title-bold {
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.detail-body {
|
||||
margin-bottom: 40px;
|
||||
|
||||
.detail-desc {
|
||||
font-size: 15px;
|
||||
color: #555;
|
||||
line-height: 1.7;
|
||||
word-break: break-word;
|
||||
}
|
||||
}
|
||||
|
||||
.detail-footer {
|
||||
// text-align: center; // Removed center alignment
|
||||
// Add margin if needed, e.g., margin-top: 20px;
|
||||
}
|
||||
|
||||
// Style for the action button
|
||||
.action-button {
|
||||
width: 100%; // Make button full width
|
||||
height: 44px; // Standard button height
|
||||
line-height: 44px; // Match height for vertical centering
|
||||
font-size: 16px; // Slightly larger font
|
||||
font-weight: 500; // Medium weight
|
||||
border-radius: 8px; // Consistent border radius
|
||||
margin-top: 20px; // Add space above the button
|
||||
background-color: #1890ff; // 蓝色背景
|
||||
color: #ffffff; // 白色文字
|
||||
border: none; // 移除边框
|
||||
// 第二部分:任务执行
|
||||
.rw-execute-section {
|
||||
margin-bottom: 20px;
|
||||
padding: 15px;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
|
||||
&:active:not(:disabled) {
|
||||
background-color: #1976d2; // 点击时的深蓝色
|
||||
.section-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 15px;
|
||||
color: #333;
|
||||
border-bottom: 2px solid #007aff;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background-color: #d9d9d9;
|
||||
color: #999;
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
.execute-form {
|
||||
padding-top: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
// 为输入框添加基本边框(参考原生 textFiled 样式)
|
||||
// 提交按钮 - 固定在底部
|
||||
.submit-button-fixed {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 15px;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
|
||||
z-index: 10;
|
||||
border-top: 1px solid #eee;
|
||||
|
||||
.action-button {
|
||||
width: 100%;
|
||||
height: 44px;
|
||||
line-height: 44px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
border-radius: 8px;
|
||||
background-color: #409eff;
|
||||
color: #ffffff;
|
||||
border: none;
|
||||
|
||||
&:active:not(:disabled) {
|
||||
background-color: #3a8ee6;
|
||||
transform: translateY(1px);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background-color: #d9d9d9 !important;
|
||||
color: #999 !important;
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 为输入框添加基本边框
|
||||
:deep(.uni-input),
|
||||
:deep(.uni-textarea) {
|
||||
width: 100% !important;
|
||||
@ -639,21 +693,6 @@ onLoad(async (options: any) => {
|
||||
box-sizing: border-box !important;
|
||||
}
|
||||
|
||||
// 为 BasicForm 组件内的输入框添加边框
|
||||
:deep(.basic-form) {
|
||||
.uni-input,
|
||||
.uni-textarea {
|
||||
width: 100% !important;
|
||||
min-height: 35px !important;
|
||||
font-size: 13px !important;
|
||||
border: 1px #CCCCCC solid !important;
|
||||
border-radius: 3px !important;
|
||||
padding: 8px 12px !important;
|
||||
box-sizing: border-box !important;
|
||||
}
|
||||
}
|
||||
|
||||
// 尝试直接选择 input 和 textarea 标签
|
||||
:deep(input),
|
||||
:deep(textarea) {
|
||||
width: 100% !important;
|
||||
@ -665,6 +704,32 @@ onLoad(async (options: any) => {
|
||||
box-sizing: border-box !important;
|
||||
}
|
||||
|
||||
// 为 BasicForm 中的 label 添加样式
|
||||
:deep(.basic-form) {
|
||||
.form-label,
|
||||
.label,
|
||||
.uni-form-item__label {
|
||||
width: 100% !important;
|
||||
white-space: normal !important;
|
||||
word-break: break-word !important;
|
||||
font-size: 15px !important;
|
||||
font-weight: bold !important;
|
||||
color: #333 !important;
|
||||
text-align: left !important;
|
||||
display: block !important;
|
||||
line-height: 1.4 !important;
|
||||
max-width: 100% !important;
|
||||
overflow-wrap: break-word !important;
|
||||
}
|
||||
}
|
||||
|
||||
// 直接针对包含标签文本的 view 元素
|
||||
:deep(view) {
|
||||
white-space: normal !important;
|
||||
word-break: normal !important;
|
||||
overflow-wrap: break-word !important;
|
||||
}
|
||||
|
||||
// 只读模式样式
|
||||
.readonly-mode {
|
||||
:deep(input),
|
||||
@ -712,7 +777,7 @@ onLoad(async (options: any) => {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 3px solid #f3f3f3;
|
||||
border-top: 3px solid #1890ff;
|
||||
border-top: 3px solid #409eff;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
margin-bottom: 16px;
|
||||
@ -728,5 +793,4 @@ onLoad(async (options: any) => {
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
973
src/pages/view/routine/yishiyice/kcxxpj.vue
Normal file
973
src/pages/view/routine/yishiyice/kcxxpj.vue
Normal file
@ -0,0 +1,973 @@
|
||||
<template>
|
||||
<view class="evaluation-page">
|
||||
<!-- 提交遮罩层 -->
|
||||
<view v-if="isSubmitting" class="submit-overlay">
|
||||
<view class="submit-loading">
|
||||
<view class="loading-spinner"></view>
|
||||
<text class="loading-text">提交评价中...</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="isLoading" class="loading-indicator">加载中...</view>
|
||||
<view v-else class="evaluation-content">
|
||||
<!-- 左侧:作品预览和详情 -->
|
||||
<view class="work-preview-section">
|
||||
<!-- 完成成果区域 -->
|
||||
<view class="work-preview">
|
||||
<view class="preview-title-header">
|
||||
<text class="preview-title-icon">📋</text>
|
||||
<text class="preview-title">完成成果</text>
|
||||
</view>
|
||||
<view class="preview-content">
|
||||
<!-- 任务基本信息 -->
|
||||
<view class="task-info">
|
||||
<view class="info-item">
|
||||
<text class="info-label">任务名称:</text>
|
||||
<text class="info-value">{{ taskInfo.rwmc }}</text>
|
||||
</view>
|
||||
<view class="info-item inline">
|
||||
<text class="info-label">提交人:</text>
|
||||
<text class="info-value">{{ executionInfo.rwzxfzrxm }}</text>
|
||||
</view>
|
||||
<view class="info-item inline">
|
||||
<text class="info-label">提交时间:</text>
|
||||
<text class="info-value">{{ formatDateTime(executionInfo.rwzxtime) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 提交内容 -->
|
||||
<view class="submitted-content">
|
||||
<text class="content-title">提交内容:</text>
|
||||
<view v-if="submittedContent.length > 0" class="content-display">
|
||||
<view
|
||||
v-for="(item, index) in submittedContent"
|
||||
:key="index"
|
||||
class="content-item"
|
||||
>
|
||||
<view class="content-label">{{ item.label }}</view>
|
||||
<view class="content-value">
|
||||
<!-- 文本内容 -->
|
||||
<text v-if="item.type === 'text'" class="text-content">{{ item.value }}</text>
|
||||
|
||||
<!-- 图片内容 -->
|
||||
<view v-else-if="item.type === 'image'" class="media-content">
|
||||
<ImageVideoUpload
|
||||
:enableImage="true"
|
||||
:enableVideo="false"
|
||||
:enableFile="false"
|
||||
:imageList="item.value"
|
||||
:disabled="true"
|
||||
:readonly="true"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 视频内容 -->
|
||||
<view v-else-if="item.type === 'video'" class="media-content">
|
||||
<ImageVideoUpload
|
||||
:enableImage="false"
|
||||
:enableVideo="true"
|
||||
:enableFile="false"
|
||||
:videoList="item.value"
|
||||
:disabled="true"
|
||||
:readonly="true"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 文档内容 -->
|
||||
<view v-else-if="item.type === 'file'" class="media-content">
|
||||
<ImageVideoUpload
|
||||
:enableImage="false"
|
||||
:enableVideo="false"
|
||||
:enableFile="true"
|
||||
:fileList="item.value"
|
||||
:disabled="true"
|
||||
:readonly="true"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else class="empty-content">
|
||||
<text class="empty-text">暂无提交内容</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
|
||||
<!-- 右侧:评价功能 -->
|
||||
<view class="evaluation-section">
|
||||
<!-- 成果评价 -->
|
||||
<view class="rating-section">
|
||||
<view class="section-header">
|
||||
<text class="section-icon">⭐</text>
|
||||
<text class="section-title">成果评价</text>
|
||||
</view>
|
||||
|
||||
<view class="rating-scale">
|
||||
<view
|
||||
v-for="score in ratingOptions"
|
||||
:key="score.value"
|
||||
class="rating-option"
|
||||
:class="{ active: evaluationData.score === score.value, disabled: isReadOnly }"
|
||||
@click="setRating(score.value)"
|
||||
>
|
||||
<view class="rating-circle">{{ score.value }}</view>
|
||||
<text class="rating-label">{{ score.label }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 文字评价 -->
|
||||
<view class="comment-section">
|
||||
<text class="comment-label">文字评价</text>
|
||||
<textarea
|
||||
v-model="evaluationData.comment"
|
||||
class="comment-input"
|
||||
placeholder="请在此输入您的评价意见..."
|
||||
:disabled="isSubmitting || isReadOnly"
|
||||
></textarea>
|
||||
</view>
|
||||
|
||||
<!-- AI智能评价 -->
|
||||
<view class="ai-evaluation-section" v-if="!isReadOnly">
|
||||
<view class="section-header">
|
||||
<text class="section-icon">🤖</text>
|
||||
<text class="section-title">AI智能评价</text>
|
||||
<button
|
||||
class="ai-button"
|
||||
@click="getAIEvaluation"
|
||||
:disabled="isSubmitting || aiLoading"
|
||||
>
|
||||
<text class="ai-text">{{ aiLoading ? 'AI分析中...' : '获取' }}</text>
|
||||
</button>
|
||||
</view>
|
||||
|
||||
<!-- AI评价结果 -->
|
||||
<view v-if="aiEvaluation" class="ai-result">
|
||||
<text class="ai-result-title">AI评价结果:</text>
|
||||
<text class="ai-result-content">{{ aiEvaluation }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部固定提交按钮 -->
|
||||
<view class="bottom-submit" v-if="!isReadOnly">
|
||||
<button
|
||||
class="submit-button"
|
||||
@click="submitEvaluation"
|
||||
:disabled="isSubmitting || !evaluationData.score"
|
||||
>
|
||||
<text class="button-icon">📤</text>
|
||||
<text class="button-text">{{ isSubmitting ? '提交评价中...' : '提交评价' }}</text>
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, onMounted } from "vue";
|
||||
import { onLoad as onPageLoad } from "@dcloudio/uni-app";
|
||||
import { rwFindInfoByRwId } from "@/api/base/server";
|
||||
import { rwzxExecutedInfoByRwIdAndJsApi } from "@/api/base/server";
|
||||
import { updateEvaluationApi } from "@/api/base/rwzxApi";
|
||||
import { useUserStore } from "@/store/modules/user";
|
||||
import { ImageVideoUpload, type FileItem, COMPRESS_PRESETS } from "@/components/ImageVideoUpload";
|
||||
|
||||
// 接口类型定义
|
||||
interface TaskInfo {
|
||||
id: string;
|
||||
rwmc: string;
|
||||
rwms: string;
|
||||
rwjstime: string;
|
||||
rwfzrxm: string;
|
||||
rwfzr: string;
|
||||
rwStatus: string;
|
||||
rwlyId: string;
|
||||
}
|
||||
|
||||
interface ExecutionInfo {
|
||||
id: string;
|
||||
rwId: string;
|
||||
rwzxfzr: string;
|
||||
rwzxfzrxm: string;
|
||||
iszxwc: string;
|
||||
rwzxzt: string;
|
||||
rwzxtime: string;
|
||||
rwzxnr: string;
|
||||
createTime: string;
|
||||
jsId?: string;
|
||||
ispj?: string;
|
||||
rwpjr?: string;
|
||||
rwpjrxm?: string;
|
||||
rwpjjf?: string;
|
||||
rwpjms?: string;
|
||||
rwpjtime?: string;
|
||||
}
|
||||
|
||||
interface SubmittedContent {
|
||||
label: string;
|
||||
type: 'text' | 'image' | 'video' | 'file';
|
||||
value: any;
|
||||
}
|
||||
|
||||
// 响应式数据
|
||||
const isLoading = ref(false);
|
||||
const isSubmitting = ref(false);
|
||||
const aiLoading = ref(false);
|
||||
const isReadOnly = ref(false); // 是否只读模式
|
||||
const taskInfo = ref<TaskInfo>({
|
||||
id: '',
|
||||
rwmc: '',
|
||||
rwms: '',
|
||||
rwjstime: '',
|
||||
rwfzrxm: '',
|
||||
rwfzr: '',
|
||||
rwStatus: '',
|
||||
rwlyId: ''
|
||||
});
|
||||
const executionInfo = ref<ExecutionInfo>({
|
||||
id: '',
|
||||
rwId: '',
|
||||
rwzxfzr: '',
|
||||
rwzxfzrxm: '',
|
||||
iszxwc: '',
|
||||
rwzxzt: '',
|
||||
rwzxtime: '',
|
||||
rwzxnr: '',
|
||||
createTime: ''
|
||||
});
|
||||
const submittedContent = ref<SubmittedContent[]>([]);
|
||||
|
||||
// 评价数据
|
||||
const evaluationData = reactive({
|
||||
score: '',
|
||||
comment: ''
|
||||
});
|
||||
|
||||
const aiEvaluation = ref('');
|
||||
|
||||
// 评分选项
|
||||
const ratingOptions = [
|
||||
{ value: '1', label: '很差' },
|
||||
{ value: '2', label: '较差' },
|
||||
{ value: '3', label: '一般' },
|
||||
{ value: '4', label: '较好' },
|
||||
{ value: '5', label: '优秀' }
|
||||
];
|
||||
|
||||
const userStore = useUserStore();
|
||||
const { getJs } = userStore;
|
||||
|
||||
// 页面加载
|
||||
onPageLoad(async (options: any) => {
|
||||
console.log('评价页面接收到的参数:', options);
|
||||
|
||||
// 从全局存储中获取参数数据
|
||||
const storedParams = uni.getStorageSync('evaluationData');
|
||||
|
||||
if (storedParams) {
|
||||
try {
|
||||
taskInfo.value = storedParams.taskInfo;
|
||||
executionInfo.value = storedParams.executionInfo;
|
||||
isReadOnly.value = storedParams.isReadOnly || false; // 获取只读模式标志
|
||||
console.log('从全局存储解析参数成功:', {
|
||||
taskInfo: taskInfo.value,
|
||||
executionInfo: executionInfo.value,
|
||||
isReadOnly: isReadOnly.value
|
||||
});
|
||||
|
||||
// 清除存储的参数数据
|
||||
uni.removeStorageSync('evaluationData');
|
||||
|
||||
await loadSubmittedContent();
|
||||
|
||||
// 如果是只读模式,加载已有的评价信息
|
||||
if (isReadOnly.value) {
|
||||
loadExistingEvaluation();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('解析全局存储参数失败:', error);
|
||||
uni.showToast({
|
||||
title: '参数解析失败',
|
||||
icon: 'error'
|
||||
});
|
||||
setTimeout(() => {
|
||||
uni.navigateBack();
|
||||
}, 1500);
|
||||
}
|
||||
} else {
|
||||
console.error('缺少评价参数');
|
||||
uni.showToast({
|
||||
title: '缺少评价参数',
|
||||
icon: 'error'
|
||||
});
|
||||
setTimeout(() => {
|
||||
uni.navigateBack();
|
||||
}, 1500);
|
||||
}
|
||||
});
|
||||
|
||||
// 加载提交的内容
|
||||
const loadSubmittedContent = async () => {
|
||||
isLoading.value = true;
|
||||
try {
|
||||
// 获取任务详情
|
||||
const taskResult = await rwFindInfoByRwId({ rwId: taskInfo.value.id });
|
||||
const rwflx = taskResult.result.rwlxes;
|
||||
|
||||
// 获取执行记录
|
||||
const executionResult = await rwzxExecutedInfoByRwIdAndJsApi({
|
||||
rwId: taskInfo.value.id,
|
||||
jsId: executionInfo.value.rwzxfzr
|
||||
});
|
||||
|
||||
if (executionResult && executionResult.result && executionResult.result.length) {
|
||||
const rwzxqds = executionResult.result;
|
||||
const content: SubmittedContent[] = [];
|
||||
|
||||
for (let i = 0; i < rwzxqds.length; i++) {
|
||||
const record = rwzxqds[i];
|
||||
const rwlxId = record.rwlxId;
|
||||
const rwzxqdtx = record.rwzxqdtx;
|
||||
|
||||
// 查找对应的任务类型
|
||||
const taskType = rwflx.find((item: any) => item.id === rwlxId);
|
||||
|
||||
if (taskType && rwzxqdtx) {
|
||||
if (taskType.rwfl === "sctp") {
|
||||
// 图片上传类型
|
||||
const urls = rwzxqdtx.split(',').filter(Boolean);
|
||||
const names = record.wjmc ? record.wjmc.split(',').filter(Boolean) : [];
|
||||
const images = urls.map((url: string, index: number) => ({
|
||||
url: url.trim(),
|
||||
name: names[index] || url.split('/').pop() || 'image.jpg'
|
||||
}));
|
||||
content.push({
|
||||
label: taskType.rwbt,
|
||||
type: 'image',
|
||||
value: images
|
||||
});
|
||||
} else if (taskType.rwfl === "scsp") {
|
||||
// 视频上传类型
|
||||
const urls = rwzxqdtx.split(',').filter(Boolean);
|
||||
const names = record.wjmc ? record.wjmc.split(',').filter(Boolean) : [];
|
||||
const videos = urls.map((url: string, index: number) => ({
|
||||
url: url.trim(),
|
||||
name: names[index] || url.split('/').pop() || 'video.mp4'
|
||||
}));
|
||||
content.push({
|
||||
label: taskType.rwbt,
|
||||
type: 'video',
|
||||
value: videos
|
||||
});
|
||||
} else if (taskType.rwfl === "scwd") {
|
||||
// 文档上传类型
|
||||
const urls = rwzxqdtx.split(',').filter(Boolean);
|
||||
const names = record.wjmc ? record.wjmc.split(',').filter(Boolean) : [];
|
||||
const files = urls.map((url: string, index: number) => ({
|
||||
url: url.trim(),
|
||||
name: names[index] || url.split('/').pop() || 'document'
|
||||
}));
|
||||
content.push({
|
||||
label: taskType.rwbt,
|
||||
type: 'file',
|
||||
value: files
|
||||
});
|
||||
} else {
|
||||
// 文本等其他类型
|
||||
content.push({
|
||||
label: taskType.rwbt,
|
||||
type: 'text',
|
||||
value: rwzxqdtx
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
submittedContent.value = content;
|
||||
console.log('加载提交内容成功:', submittedContent.value);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载提交内容失败:', error);
|
||||
uni.showToast({
|
||||
title: '加载内容失败',
|
||||
icon: 'error'
|
||||
});
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 加载已有的评价信息
|
||||
const loadExistingEvaluation = () => {
|
||||
if (executionInfo.value.rwpjjf) {
|
||||
evaluationData.score = executionInfo.value.rwpjjf;
|
||||
}
|
||||
if (executionInfo.value.rwpjms) {
|
||||
evaluationData.comment = executionInfo.value.rwpjms;
|
||||
}
|
||||
console.log('加载已有评价信息:', {
|
||||
score: evaluationData.score,
|
||||
comment: evaluationData.comment
|
||||
});
|
||||
};
|
||||
|
||||
// 设置评分
|
||||
const setRating = (score: string) => {
|
||||
if (isReadOnly.value) {
|
||||
return; // 只读模式不允许修改
|
||||
}
|
||||
evaluationData.score = score;
|
||||
};
|
||||
|
||||
// 获取AI评价
|
||||
const getAIEvaluation = async () => {
|
||||
aiLoading.value = true;
|
||||
try {
|
||||
// TODO: 调用AI评价接口
|
||||
// 这里模拟AI评价结果
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
aiEvaluation.value = `正在接入AI评价,敬请期待...`;
|
||||
} catch (error) {
|
||||
console.error('获取AI评价失败:', error);
|
||||
uni.showToast({
|
||||
title: 'AI评价失败',
|
||||
icon: 'error'
|
||||
});
|
||||
} finally {
|
||||
aiLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 提交评价
|
||||
const submitEvaluation = async () => {
|
||||
if (isSubmitting.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!evaluationData.score) {
|
||||
uni.showToast({
|
||||
title: '请选择评分',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
isSubmitting.value = true;
|
||||
|
||||
try {
|
||||
// 调用评价提交接口
|
||||
console.log('教师信息:', getJs);
|
||||
|
||||
const evaluationParams = {
|
||||
rwzxId: executionInfo.value.id,
|
||||
rwpjr: getJs.id,
|
||||
rwpjrxm: getJs.jsxm,
|
||||
rwpjjf: evaluationData.score,
|
||||
rwpjms: evaluationData.comment || ''
|
||||
};
|
||||
|
||||
console.log('提交评价参数:', evaluationParams);
|
||||
|
||||
const result = await updateEvaluationApi(evaluationParams);
|
||||
|
||||
if (result && result.resultCode === 1) {
|
||||
uni.showToast({
|
||||
title: '评价提交成功',
|
||||
icon: 'success'
|
||||
});
|
||||
|
||||
// 发送刷新事件
|
||||
uni.$emit('refreshTaskExecution');
|
||||
|
||||
// 延迟跳转
|
||||
setTimeout(() => {
|
||||
uni.navigateBack();
|
||||
}, 1500);
|
||||
} else {
|
||||
throw new Error(result?.message || result?.msg || '提交失败');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('提交评价失败:', error);
|
||||
uni.showToast({
|
||||
title: '提交失败,请重试',
|
||||
icon: 'error'
|
||||
});
|
||||
} finally {
|
||||
isSubmitting.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
// 工具函数
|
||||
const formatDateTime = (dateStr: string) => {
|
||||
if (!dateStr) return '';
|
||||
const date = new Date(dateStr);
|
||||
return `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()} ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.evaluation-page {
|
||||
min-height: 100vh;
|
||||
background-color: #f5f7fa;
|
||||
padding: 16px;
|
||||
box-sizing: border-box;
|
||||
|
||||
&:has(.bottom-submit) {
|
||||
padding-bottom: 80px; /* 有提交按钮时留出空间 */
|
||||
}
|
||||
}
|
||||
|
||||
.loading-indicator {
|
||||
text-align: center;
|
||||
color: #999;
|
||||
padding: 40px 15px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.evaluation-content {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* 左侧:作品预览和详情 */
|
||||
.work-preview-section {
|
||||
flex: 1;
|
||||
background-color: #fff;
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.work-preview {
|
||||
margin-bottom: 24px;
|
||||
|
||||
.preview-title-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.preview-title-icon {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.preview-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.preview-content {
|
||||
min-height: 200px;
|
||||
background-color: #fff;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.task-info {
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 15px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
margin-bottom: 8px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&.inline {
|
||||
display: inline-flex;
|
||||
margin-right: 24px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.info-label {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
min-width: 80px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.submitted-content {
|
||||
.content-title {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
margin-bottom: 12px;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.content-display {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.content-item {
|
||||
margin-bottom: 16px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.content-label {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
margin-bottom: 8px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.content-value {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.text-content {
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.media-content {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.empty-content {
|
||||
text-align: center;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* 右侧:评价功能 */
|
||||
.evaluation-section {
|
||||
flex: 1;
|
||||
background-color: #fff;
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 16px;
|
||||
|
||||
.section-icon {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.ai-button {
|
||||
padding: 6px 12px;
|
||||
background-color: #1890ff;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
background-color: #1976d2;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background-color: #d9d9d9;
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
.ai-text {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.rating-section {
|
||||
margin-bottom: 24px;
|
||||
|
||||
.rating-scale {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.rating-option {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
padding: 8px 4px;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
border: 2px solid transparent;
|
||||
min-width: 0;
|
||||
|
||||
&.active {
|
||||
background-color: rgba(24, 144, 255, 0.1);
|
||||
border-color: #1890ff;
|
||||
}
|
||||
|
||||
&:hover:not(.disabled) {
|
||||
background-color: rgba(0, 0, 0, 0.02);
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
.rating-circle {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background-color: #f5f5f5;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #666;
|
||||
margin: 0 auto 6px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.rating-option.active .rating-circle {
|
||||
background-color: #1890ff;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.rating-label {
|
||||
font-size: 11px;
|
||||
color: #666;
|
||||
display: block;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.rating-option.active .rating-label {
|
||||
color: #1890ff;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.comment-section {
|
||||
margin-bottom: 24px;
|
||||
|
||||
.comment-label {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
margin-bottom: 8px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.comment-input {
|
||||
width: 100%;
|
||||
min-height: 120px;
|
||||
padding: 12px;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
resize: vertical;
|
||||
box-sizing: border-box;
|
||||
|
||||
&:focus {
|
||||
border-color: #1890ff;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background-color: #f5f5f5;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ai-evaluation-section {
|
||||
margin-bottom: 24px;
|
||||
|
||||
.ai-result {
|
||||
margin-top: 16px;
|
||||
padding: 16px;
|
||||
background-color: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
border-left: 4px solid #1890ff;
|
||||
|
||||
.ai-result-title {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
margin-bottom: 8px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.ai-result-content {
|
||||
font-size: 14px;
|
||||
color: #555;
|
||||
line-height: 1.6;
|
||||
word-break: break-word;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 底部固定提交按钮 */
|
||||
.bottom-submit {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: #fff;
|
||||
padding: 16px;
|
||||
border-top: 1px solid #e5e5e5;
|
||||
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.06);
|
||||
z-index: 1000;
|
||||
|
||||
.submit-button {
|
||||
width: 100%;
|
||||
height: 44px;
|
||||
background-color: #1890ff;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
background-color: #1976d2;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background-color: #d9d9d9;
|
||||
color: #999;
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
.button-icon {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.button-text {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 提交遮罩层样式 */
|
||||
.submit-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.submit-loading {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #fff;
|
||||
padding: 30px;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 3px solid #f3f3f3;
|
||||
border-top: 3px solid #1890ff;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media screen and (max-width: 768px) {
|
||||
.evaluation-content {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.rating-scale {
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.rating-option {
|
||||
min-width: 0;
|
||||
padding: 6px 2px;
|
||||
}
|
||||
|
||||
.rating-circle {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 12px;
|
||||
margin: 0 auto 4px;
|
||||
}
|
||||
|
||||
.rating-label {
|
||||
font-size: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -1,6 +1,6 @@
|
||||
<!-- src/pages/base/message/detail.vue -->
|
||||
<!-- 任务执行提交页面 -->
|
||||
<template>
|
||||
<view class="message-detail-page">
|
||||
<view class="rw-detail-page">
|
||||
<!-- 提交遮罩层 -->
|
||||
<view v-if="isSubmitting" class="submit-overlay">
|
||||
<view class="submit-loading">
|
||||
@ -10,20 +10,49 @@
|
||||
</view>
|
||||
|
||||
<view v-if="isLoading" class="loading-indicator">加载中...</view>
|
||||
<view v-else class="detail-content">
|
||||
<view class="detail-header">
|
||||
<view class="title-tag-row">
|
||||
<view class="detail-title">{{ rw.rwmc }}</view>
|
||||
<view v-else class="content-wrapper">
|
||||
<view class="p-15">
|
||||
<!-- 第一部分:任务要求 -->
|
||||
<view class="rw-info-section">
|
||||
<view class="section-title">任务要求</view>
|
||||
|
||||
<!-- 任务名称 -->
|
||||
<view class="info-item">
|
||||
<text class="label">任务名称:</text>
|
||||
<text class="value title-bold">{{ rw.rwmc }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 任务描述 -->
|
||||
<view v-if="rw.rwms" class="info-item">
|
||||
<text class="label">任务描述:</text>
|
||||
<text class="value">{{ rw.rwms }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 任务时间 -->
|
||||
<view v-if="rw.rwkstime" class="info-item">
|
||||
<text class="label">任务时间:</text>
|
||||
<text class="value">{{ rw.rwkstime }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 附件预览 -->
|
||||
<BasicFilePreview
|
||||
v-if="rw.rwfj && rw.fjmx"
|
||||
:file-url="rw.rwfj"
|
||||
:file-name="rw.fjmx"
|
||||
:file-format="getFileFormat(rw.fjmx)"
|
||||
class="mt-15"
|
||||
/>
|
||||
</view>
|
||||
<view class="detail-meta">
|
||||
<text>{{ rw.rwkstime }}</text>
|
||||
<!-- <text>{{ messageDetail.timeAgo }}</text>-->
|
||||
|
||||
<!-- 第二部分:任务执行 -->
|
||||
<view class="rw-execute-section">
|
||||
<view class="section-title">任务执行</view>
|
||||
<view class="execute-form">
|
||||
<BasicForm :schema="schema" v-model="formData">
|
||||
</BasicForm>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="detail-body">
|
||||
<BasicForm :schema="schema" v-model="formData">
|
||||
</BasicForm>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 提交按钮 - 固定在底部 -->
|
||||
@ -32,7 +61,6 @@
|
||||
{{ isSubmitting ? '提交中...' : '提交' }}
|
||||
</button>
|
||||
</view>
|
||||
<!-- <view v-else class="empty-state">消息详情未找到</view>-->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@ -45,6 +73,7 @@ import {navigateBack, showToast} from "@/utils/uniapp";
|
||||
import {useUserStore} from "@/store/modules/user";
|
||||
import { ImageVideoUpload, type FileItem, COMPRESS_PRESETS } from "@/components/ImageVideoUpload";
|
||||
import { attachmentUpload } from "@/api/system/upload";
|
||||
import BasicFilePreview from "@/components/BasicFile/preview.vue";
|
||||
|
||||
interface MessageDetail {
|
||||
id: string; // Assuming an ID is passed or can be derived
|
||||
@ -54,9 +83,23 @@ interface MessageDetail {
|
||||
timeAgo: string;
|
||||
tagText: string;
|
||||
tagType: string;
|
||||
likes?: number;
|
||||
comments?: number;
|
||||
// Add other fields as necessary
|
||||
}
|
||||
|
||||
interface RwInfo {
|
||||
id?: string;
|
||||
rwmc?: string; // 任务名称
|
||||
rwms?: string; // 任务描述
|
||||
rwkstime?: string; // 任务开始时间
|
||||
rwfj?: string; // 任务附件URL
|
||||
fjmx?: string; // 附件名称
|
||||
rwlxes?: any[]; // 任务类型列表
|
||||
rwzxqds?: any[]; // 任务执行清单
|
||||
[key: string]: any; // 允许其他属性
|
||||
}
|
||||
|
||||
const formData: any = ref({})
|
||||
const messageId = ref<string>('');
|
||||
const jsId = ref<string>(''); // 教师ID
|
||||
@ -78,7 +121,7 @@ const messageDetail = ref<MessageDetail | null>({
|
||||
});
|
||||
const isLoading = ref(false);
|
||||
const rwflx: any = ref([])
|
||||
const rw = ref({})
|
||||
const rw = ref<RwInfo>({})
|
||||
const schema = ref<FormsSchema[]>([])
|
||||
const {getUser} = useUserStore()
|
||||
|
||||
@ -209,6 +252,13 @@ async function performSubmit() {
|
||||
}
|
||||
}
|
||||
|
||||
// 获取文件格式
|
||||
const getFileFormat = (fileName: string) => {
|
||||
if (!fileName) return '';
|
||||
const parts = fileName.split('.');
|
||||
return parts.length > 1 ? parts[parts.length - 1].toLowerCase() : '';
|
||||
};
|
||||
|
||||
const rwzxqds = ref([])
|
||||
onLoad(async (options) => {
|
||||
console.log('页面加载参数:', options);
|
||||
@ -536,98 +586,95 @@ onLoad(async (options) => {
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.message-detail-page {
|
||||
.rw-detail-page {
|
||||
background-color: #f4f5f7;
|
||||
min-height: 100vh;
|
||||
padding: 15px;
|
||||
padding-bottom: 80px; // 为底部按钮留出空间
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.loading-indicator,
|
||||
.empty-state {
|
||||
.loading-indicator {
|
||||
text-align: center;
|
||||
color: #999;
|
||||
padding: 40px 15px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.detail-content {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
.content-wrapper {
|
||||
.p-15 {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.mt-15 {
|
||||
margin-top: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.detail-header {
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
padding-bottom: 15px;
|
||||
// 第一部分:任务要求
|
||||
.rw-info-section {
|
||||
margin-bottom: 20px;
|
||||
|
||||
.title-tag-row {
|
||||
display: flex;
|
||||
justify-content: center; // 改为居中对齐
|
||||
align-items: flex-start; // Align items to the top
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.detail-title {
|
||||
font-size: 18px;
|
||||
padding: 15px;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
|
||||
.section-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 15px;
|
||||
color: #333;
|
||||
line-height: 1.4;
|
||||
text-align: center; // 居中显示
|
||||
white-space: normal; // 允许正常换行
|
||||
word-break: break-word; // 在单词边界换行
|
||||
width: 100%; // 使用全部宽度
|
||||
display: block; // 确保是块级元素
|
||||
border-bottom: 2px solid #007aff;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.tag { // Reuse tag styles from index page if possible, or define here
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0; // Prevent tag from shrinking
|
||||
|
||||
&.notice {
|
||||
background-color: #447ade;
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
margin-bottom: 15px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&.task {
|
||||
background-color: #19be6b;
|
||||
|
||||
.label {
|
||||
width: 80px;
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&.approval {
|
||||
background-color: #ff9f0a;
|
||||
}
|
||||
|
||||
&.submit {
|
||||
background-color: #8e8e93;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.detail-meta {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
|
||||
text {
|
||||
margin-right: 15px;
|
||||
|
||||
.value {
|
||||
flex: 1;
|
||||
color: #333;
|
||||
font-size: 14px;
|
||||
word-break: break-word;
|
||||
|
||||
&.title-bold {
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.detail-body {
|
||||
margin-bottom: 20px; // 减少底部边距
|
||||
|
||||
.detail-desc {
|
||||
font-size: 15px;
|
||||
color: #555;
|
||||
line-height: 1.7;
|
||||
word-break: break-word;
|
||||
// 第二部分:任务执行
|
||||
.rw-execute-section {
|
||||
margin-bottom: 20px;
|
||||
padding: 15px;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
|
||||
.section-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 15px;
|
||||
color: #333;
|
||||
border-bottom: 2px solid #007aff;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.execute-form {
|
||||
padding-top: 5px; // 表单内容区域
|
||||
}
|
||||
}
|
||||
|
||||
@ -644,18 +691,18 @@ onLoad(async (options) => {
|
||||
border-top: 1px solid #eee;
|
||||
|
||||
.action-button {
|
||||
width: 100%; // Make button full width
|
||||
height: 44px; // Standard button height
|
||||
line-height: 44px; // Match height for vertical centering
|
||||
font-size: 16px; // Slightly larger font
|
||||
font-weight: 500; // Medium weight
|
||||
border-radius: 8px; // Consistent border radius
|
||||
background-color: #409eff; // 蓝色背景
|
||||
color: #ffffff; // 白色文字
|
||||
border: none; // 移除边框
|
||||
width: 100%;
|
||||
height: 44px;
|
||||
line-height: 44px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
border-radius: 8px;
|
||||
background-color: #409eff;
|
||||
color: #ffffff;
|
||||
border: none;
|
||||
|
||||
&:active:not(:disabled) {
|
||||
background-color: #3a8ee6; // 点击时的深蓝色
|
||||
background-color: #3a8ee6;
|
||||
transform: translateY(1px);
|
||||
}
|
||||
|
||||
@ -668,95 +715,6 @@ onLoad(async (options) => {
|
||||
}
|
||||
}
|
||||
|
||||
// 为输入框添加基本边框(参考原生 textFiled 样式)
|
||||
:deep(.uni-input),
|
||||
:deep(.uni-textarea) {
|
||||
width: 100% !important;
|
||||
min-height: 35px !important;
|
||||
font-size: 13px !important;
|
||||
border: 1px #CCCCCC solid !important;
|
||||
border-radius: 3px !important;
|
||||
padding: 8px 12px !important;
|
||||
box-sizing: border-box !important;
|
||||
}
|
||||
|
||||
// 为 BasicForm 组件内的输入框添加边框
|
||||
:deep(.basic-form) {
|
||||
.uni-input,
|
||||
.uni-textarea {
|
||||
width: 100% !important;
|
||||
min-height: 35px !important;
|
||||
font-size: 13px !important;
|
||||
border: 1px #CCCCCC solid !important;
|
||||
border-radius: 3px !important;
|
||||
padding: 8px 12px !important;
|
||||
box-sizing: border-box !important;
|
||||
}
|
||||
}
|
||||
|
||||
// 尝试直接选择 input 和 textarea 标签
|
||||
:deep(input),
|
||||
:deep(textarea) {
|
||||
width: 100% !important;
|
||||
min-height: 35px !important;
|
||||
font-size: 13px !important;
|
||||
border: 1px #CCCCCC solid !important;
|
||||
border-radius: 3px !important;
|
||||
padding: 8px 12px !important;
|
||||
box-sizing: border-box !important;
|
||||
}
|
||||
|
||||
// 为 BasicForm 中的 label 添加样式
|
||||
:deep(.basic-form) {
|
||||
.form-label,
|
||||
.label,
|
||||
.uni-form-item__label {
|
||||
width: 100% !important;
|
||||
white-space: normal !important;
|
||||
word-break: break-word !important;
|
||||
font-size: 15px !important;
|
||||
font-weight: bold !important;
|
||||
color: #333 !important;
|
||||
text-align: left !important;
|
||||
display: block !important;
|
||||
line-height: 1.4 !important;
|
||||
max-width: 100% !important;
|
||||
overflow-wrap: break-word !important;
|
||||
}
|
||||
}
|
||||
|
||||
// 专门针对任务标题的样式
|
||||
:deep(.basic-form) {
|
||||
.form-item {
|
||||
.form-label {
|
||||
white-space: pre-wrap !important;
|
||||
word-break: keep-all !important;
|
||||
overflow-wrap: break-word !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 更具体的标签样式修复 - 直接针对 FormsItem 组件
|
||||
:deep(.forms-item-row) {
|
||||
.flex-row {
|
||||
view:last-child {
|
||||
white-space: normal !important;
|
||||
word-break: normal !important;
|
||||
overflow-wrap: break-word !important;
|
||||
max-width: 100% !important;
|
||||
display: block !important;
|
||||
line-height: 1.4 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 直接针对包含标签文本的 view 元素
|
||||
:deep(view) {
|
||||
white-space: normal !important;
|
||||
word-break: normal !important;
|
||||
overflow-wrap: break-word !important;
|
||||
}
|
||||
|
||||
// 提交遮罩层样式
|
||||
.submit-overlay {
|
||||
position: fixed;
|
||||
@ -803,6 +761,52 @@ onLoad(async (options) => {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
// 按钮禁用状态已移至固定按钮样式中
|
||||
// 为输入框添加基本边框
|
||||
:deep(.uni-input),
|
||||
:deep(.uni-textarea) {
|
||||
width: 100% !important;
|
||||
min-height: 35px !important;
|
||||
font-size: 13px !important;
|
||||
border: 1px #CCCCCC solid !important;
|
||||
border-radius: 3px !important;
|
||||
padding: 8px 12px !important;
|
||||
box-sizing: border-box !important;
|
||||
}
|
||||
|
||||
:deep(input),
|
||||
:deep(textarea) {
|
||||
width: 100% !important;
|
||||
min-height: 35px !important;
|
||||
font-size: 13px !important;
|
||||
border: 1px #CCCCCC solid !important;
|
||||
border-radius: 3px !important;
|
||||
padding: 8px 12px !important;
|
||||
box-sizing: border-box !important;
|
||||
}
|
||||
|
||||
// 为 BasicForm 中的 label 添加样式
|
||||
:deep(.basic-form) {
|
||||
.form-label,
|
||||
.label,
|
||||
.uni-form-item__label {
|
||||
width: 100% !important;
|
||||
white-space: normal !important;
|
||||
word-break: break-word !important;
|
||||
font-size: 15px !important;
|
||||
font-weight: bold !important;
|
||||
color: #333 !important;
|
||||
text-align: left !important;
|
||||
display: block !important;
|
||||
line-height: 1.4 !important;
|
||||
max-width: 100% !important;
|
||||
overflow-wrap: break-word !important;
|
||||
}
|
||||
}
|
||||
|
||||
// 直接针对包含标签文本的 view 元素
|
||||
:deep(view) {
|
||||
white-space: normal !important;
|
||||
word-break: normal !important;
|
||||
overflow-wrap: break-word !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<!-- src/pages/base/message/detail.vue -->
|
||||
<!-- 任务执行提交页面 -->
|
||||
<template>
|
||||
<view class="message-detail-page">
|
||||
<view class="rw-detail-page">
|
||||
<!-- 提交遮罩层 -->
|
||||
<view v-if="isSubmitting" class="submit-overlay">
|
||||
<view class="submit-loading">
|
||||
@ -10,20 +10,49 @@
|
||||
</view>
|
||||
|
||||
<view v-if="isLoading" class="loading-indicator">加载中...</view>
|
||||
<view v-else class="detail-content">
|
||||
<view class="detail-header">
|
||||
<view class="title-tag-row">
|
||||
<view class="detail-title">{{ rw.rwmc }}</view>
|
||||
<view v-else class="content-wrapper">
|
||||
<view class="p-15">
|
||||
<!-- 第一部分:任务要求 -->
|
||||
<view class="rw-info-section">
|
||||
<view class="section-title">任务要求</view>
|
||||
|
||||
<!-- 任务名称 -->
|
||||
<view class="info-item">
|
||||
<text class="label">任务名称:</text>
|
||||
<text class="value title-bold">{{ rw.rwmc }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 任务描述 -->
|
||||
<view v-if="rw.rwms" class="info-item">
|
||||
<text class="label">任务描述:</text>
|
||||
<text class="value">{{ rw.rwms }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 任务时间 -->
|
||||
<view v-if="rw.rwkstime" class="info-item">
|
||||
<text class="label">任务时间:</text>
|
||||
<text class="value">{{ rw.rwkstime }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 附件预览 -->
|
||||
<BasicFilePreview
|
||||
v-if="rw.rwfj && rw.fjmx"
|
||||
:file-url="rw.rwfj"
|
||||
:file-name="rw.fjmx"
|
||||
:file-format="getFileFormat(rw.fjmx)"
|
||||
class="mt-15"
|
||||
/>
|
||||
</view>
|
||||
<view class="detail-meta">
|
||||
<text>{{ rw.rwkstime }}</text>
|
||||
<!-- <text>{{ messageDetail.timeAgo }}</text>-->
|
||||
|
||||
<!-- 第二部分:任务执行 -->
|
||||
<view class="rw-execute-section">
|
||||
<view class="section-title">任务执行</view>
|
||||
<view class="execute-form">
|
||||
<BasicForm :schema="schema" v-model="formData">
|
||||
</BasicForm>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="detail-body">
|
||||
<BasicForm :schema="schema" v-model="formData">
|
||||
</BasicForm>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 提交按钮 - 固定在底部 -->
|
||||
@ -32,7 +61,6 @@
|
||||
{{ isSubmitting ? '提交中...' : '提交' }}
|
||||
</button>
|
||||
</view>
|
||||
<!-- <view v-else class="empty-state">消息详情未找到</view>-->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@ -45,6 +73,7 @@ import {navigateBack, showToast} from "@/utils/uniapp";
|
||||
import {useUserStore} from "@/store/modules/user";
|
||||
import { ImageVideoUpload, type FileItem, COMPRESS_PRESETS } from "@/components/ImageVideoUpload";
|
||||
import { attachmentUpload } from "@/api/system/upload";
|
||||
import BasicFilePreview from "@/components/BasicFile/preview.vue";
|
||||
|
||||
interface MessageDetail {
|
||||
id: string; // Assuming an ID is passed or can be derived
|
||||
@ -54,9 +83,23 @@ interface MessageDetail {
|
||||
timeAgo: string;
|
||||
tagText: string;
|
||||
tagType: string;
|
||||
likes?: number;
|
||||
comments?: number;
|
||||
// Add other fields as necessary
|
||||
}
|
||||
|
||||
interface RwInfo {
|
||||
id?: string;
|
||||
rwmc?: string; // 任务名称
|
||||
rwms?: string; // 任务描述
|
||||
rwkstime?: string; // 任务开始时间
|
||||
rwfj?: string; // 任务附件URL
|
||||
fjmx?: string; // 附件名称
|
||||
rwlxes?: any[]; // 任务类型列表
|
||||
rwzxqds?: any[]; // 任务执行清单
|
||||
[key: string]: any; // 允许其他属性
|
||||
}
|
||||
|
||||
const formData: any = ref({})
|
||||
const messageId = ref<string>('');
|
||||
const jsId = ref<string>(''); // 教师ID
|
||||
@ -78,7 +121,7 @@ const messageDetail = ref<MessageDetail | null>({
|
||||
});
|
||||
const isLoading = ref(false);
|
||||
const rwflx: any = ref([])
|
||||
const rw = ref({})
|
||||
const rw = ref<RwInfo>({})
|
||||
const schema = ref<FormsSchema[]>([])
|
||||
const {getUser} = useUserStore()
|
||||
|
||||
@ -209,6 +252,13 @@ async function performSubmit() {
|
||||
}
|
||||
}
|
||||
|
||||
// 获取文件格式
|
||||
const getFileFormat = (fileName: string) => {
|
||||
if (!fileName) return '';
|
||||
const parts = fileName.split('.');
|
||||
return parts.length > 1 ? parts[parts.length - 1].toLowerCase() : '';
|
||||
};
|
||||
|
||||
const rwzxqds = ref([])
|
||||
onLoad(async (options) => {
|
||||
console.log('页面加载参数:', options);
|
||||
@ -536,99 +586,95 @@ onLoad(async (options) => {
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.message-detail-page {
|
||||
.rw-detail-page {
|
||||
background-color: #f4f5f7;
|
||||
min-height: 100vh;
|
||||
padding: 15px;
|
||||
padding-bottom: 80px; // 为底部按钮留出空间
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.loading-indicator,
|
||||
.empty-state {
|
||||
.loading-indicator {
|
||||
text-align: center;
|
||||
color: #999;
|
||||
padding: 40px 15px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.detail-content {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
.content-wrapper {
|
||||
.p-15 {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.mt-15 {
|
||||
margin-top: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.detail-header {
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
padding-bottom: 15px;
|
||||
// 第一部分:任务要求
|
||||
.rw-info-section {
|
||||
margin-bottom: 20px;
|
||||
|
||||
.title-tag-row {
|
||||
display: flex;
|
||||
justify-content: center; // 改为居中对齐
|
||||
align-items: flex-start; // Align items to the top
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.detail-title {
|
||||
font-size: 18px;
|
||||
padding: 15px;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
|
||||
.section-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 15px;
|
||||
color: #333;
|
||||
line-height: 1.4;
|
||||
text-align: center; // 居中显示
|
||||
white-space: nowrap; // 不换行
|
||||
overflow: hidden; // 隐藏溢出内容
|
||||
text-overflow: ellipsis; // 显示省略号
|
||||
width: 100%; // 使用全部宽度
|
||||
display: block; // 确保是块级元素
|
||||
border-bottom: 2px solid #007aff;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.tag { // Reuse tag styles from index page if possible, or define here
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0; // Prevent tag from shrinking
|
||||
|
||||
&.notice {
|
||||
background-color: #447ade;
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
margin-bottom: 15px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&.task {
|
||||
background-color: #19be6b;
|
||||
|
||||
.label {
|
||||
width: 80px;
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&.approval {
|
||||
background-color: #ff9f0a;
|
||||
}
|
||||
|
||||
&.submit {
|
||||
background-color: #8e8e93;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.detail-meta {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
|
||||
text {
|
||||
margin-right: 15px;
|
||||
|
||||
.value {
|
||||
flex: 1;
|
||||
color: #333;
|
||||
font-size: 14px;
|
||||
word-break: break-word;
|
||||
|
||||
&.title-bold {
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.detail-body {
|
||||
margin-bottom: 20px; // 减少底部边距
|
||||
|
||||
.detail-desc {
|
||||
font-size: 15px;
|
||||
color: #555;
|
||||
line-height: 1.7;
|
||||
word-break: break-word;
|
||||
// 第二部分:任务执行
|
||||
.rw-execute-section {
|
||||
margin-bottom: 20px;
|
||||
padding: 15px;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
|
||||
.section-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 15px;
|
||||
color: #333;
|
||||
border-bottom: 2px solid #007aff;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.execute-form {
|
||||
padding-top: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -645,18 +691,18 @@ onLoad(async (options) => {
|
||||
border-top: 1px solid #eee;
|
||||
|
||||
.action-button {
|
||||
width: 100%; // Make button full width
|
||||
height: 44px; // Standard button height
|
||||
line-height: 44px; // Match height for vertical centering
|
||||
font-size: 16px; // Slightly larger font
|
||||
font-weight: 500; // Medium weight
|
||||
border-radius: 8px; // Consistent border radius
|
||||
background-color: #409eff; // 蓝色背景
|
||||
color: #ffffff; // 白色文字
|
||||
border: none; // 移除边框
|
||||
width: 100%;
|
||||
height: 44px;
|
||||
line-height: 44px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
border-radius: 8px;
|
||||
background-color: #409eff;
|
||||
color: #ffffff;
|
||||
border: none;
|
||||
|
||||
&:active:not(:disabled) {
|
||||
background-color: #3a8ee6; // 点击时的深蓝色
|
||||
background-color: #3a8ee6;
|
||||
transform: translateY(1px);
|
||||
}
|
||||
|
||||
@ -669,7 +715,7 @@ onLoad(async (options) => {
|
||||
}
|
||||
}
|
||||
|
||||
// 为输入框添加基本边框(参考原生 textFiled 样式)
|
||||
// 为输入框添加基本边框
|
||||
:deep(.uni-input),
|
||||
:deep(.uni-textarea) {
|
||||
width: 100% !important;
|
||||
@ -681,21 +727,6 @@ onLoad(async (options) => {
|
||||
box-sizing: border-box !important;
|
||||
}
|
||||
|
||||
// 为 BasicForm 组件内的输入框添加边框
|
||||
:deep(.basic-form) {
|
||||
.uni-input,
|
||||
.uni-textarea {
|
||||
width: 100% !important;
|
||||
min-height: 35px !important;
|
||||
font-size: 13px !important;
|
||||
border: 1px #CCCCCC solid !important;
|
||||
border-radius: 3px !important;
|
||||
padding: 8px 12px !important;
|
||||
box-sizing: border-box !important;
|
||||
}
|
||||
}
|
||||
|
||||
// 尝试直接选择 input 和 textarea 标签
|
||||
:deep(input),
|
||||
:deep(textarea) {
|
||||
width: 100% !important;
|
||||
@ -713,17 +744,26 @@ onLoad(async (options) => {
|
||||
.label,
|
||||
.uni-form-item__label {
|
||||
width: 100% !important;
|
||||
white-space: nowrap !important;
|
||||
overflow: hidden !important;
|
||||
text-overflow: ellipsis !important;
|
||||
white-space: normal !important;
|
||||
word-break: break-word !important;
|
||||
font-size: 15px !important;
|
||||
font-weight: bold !important;
|
||||
color: #333 !important;
|
||||
text-align: left !important;
|
||||
display: block !important;
|
||||
line-height: 1.4 !important;
|
||||
max-width: 100% !important;
|
||||
overflow-wrap: break-word !important;
|
||||
}
|
||||
}
|
||||
|
||||
// 直接针对包含标签文本的 view 元素
|
||||
:deep(view) {
|
||||
white-space: normal !important;
|
||||
word-break: normal !important;
|
||||
overflow-wrap: break-word !important;
|
||||
}
|
||||
|
||||
// 提交遮罩层样式
|
||||
.submit-overlay {
|
||||
position: fixed;
|
||||
@ -769,7 +809,4 @@ onLoad(async (options) => {
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
// 按钮禁用状态已移至固定按钮样式中
|
||||
|
||||
</style>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user