资源调整
This commit is contained in:
parent
33643f78ae
commit
f525a6ae77
@ -204,6 +204,48 @@ export const zymlFindTreeByZylxApi = async (params: any) => {
|
|||||||
return await get("/api/zyml/qryTreeByZylx", params);
|
return await get("/api/zyml/qryTreeByZylx", params);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ==================== 资源包(Zy)相关API ====================
|
||||||
|
// 获取资源包分页列表
|
||||||
|
export const zyFindPageApi = async (params: any) => {
|
||||||
|
return await get("/api/zy/findPage", params);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 保存资源包(新增/编辑)
|
||||||
|
export const zySaveApi = async (params: any) => {
|
||||||
|
return await post("/api/zy/save", params);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 收藏/取消收藏资源包
|
||||||
|
export const zyCollectApi = async (params: { zyId: string }) => {
|
||||||
|
return await post(`/api/zy/collect?zyId=${params.zyId}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 点赞/取消点赞资源包
|
||||||
|
export const zyLikeApi = async (params: { zyId: string }) => {
|
||||||
|
return await post(`/api/zy/like?zyId=${params.zyId}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 评分资源包
|
||||||
|
export const zyScoreApi = async (params: { zyId: string; score: number; comment?: string }) => {
|
||||||
|
const queryParams = new URLSearchParams({
|
||||||
|
zyId: params.zyId,
|
||||||
|
score: params.score.toString(),
|
||||||
|
...(params.comment ? { comment: params.comment } : {})
|
||||||
|
});
|
||||||
|
return await post(`/api/zy/score?${queryParams.toString()}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 根据ID查询资源包详情(包含资源明细和用户状态)
|
||||||
|
export const zyGetDetailApi = async (params: { id: string }) => {
|
||||||
|
return await get("/api/zy/getDetail", params);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 删除资源包
|
||||||
|
export const zyLogicDeleteApi = async (params: { ids: string }) => {
|
||||||
|
return await post("/api/zy/logicDelete", params);
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================== 资源明细(Zymx)相关API ====================
|
||||||
// 获取资源明细分页
|
// 获取资源明细分页
|
||||||
export const zymxFindPageApi = async (params: any) => {
|
export const zymxFindPageApi = async (params: any) => {
|
||||||
return await get("/api/zymx/findPage", params);
|
return await get("/api/zymx/findPage", params);
|
||||||
|
|||||||
@ -381,6 +381,27 @@
|
|||||||
"enablePullDownRefresh": false
|
"enablePullDownRefresh": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/view/routine/JiaoXueZiYuan/zy-detail",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "资源包详情",
|
||||||
|
"enablePullDownRefresh": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/view/routine/JiaoXueZiYuan/zy-list",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "教学资源包",
|
||||||
|
"enablePullDownRefresh": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/view/routine/JiaoXueZiYuan/zy-add",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "新增资源包",
|
||||||
|
"enablePullDownRefresh": false
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/view/routine/HuoDongZiYuan/index",
|
"path": "pages/view/routine/HuoDongZiYuan/index",
|
||||||
"style": {
|
"style": {
|
||||||
|
|||||||
@ -442,7 +442,7 @@ const sections = reactive<Section[]>([
|
|||||||
{
|
{
|
||||||
id: "r5",
|
id: "r5",
|
||||||
icon: "stxc",
|
icon: "stxc",
|
||||||
text: "食堂巡查",
|
text: "食堂日志",
|
||||||
show: true,
|
show: true,
|
||||||
permissionKey: "routine-stxc", // 食堂巡查权限编码
|
permissionKey: "routine-stxc", // 食堂巡查权限编码
|
||||||
path: "/pages/view/routine/ShiTangXunCha/index",
|
path: "/pages/view/routine/ShiTangXunCha/index",
|
||||||
|
|||||||
@ -133,8 +133,10 @@ const goTo = function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
resourType: leafNode.key, // ✅ 动态获取叶子节点的key
|
zymlId: leafNode.key, // 资源目录ID(用于资源包查询)
|
||||||
category: curCategory.value.key,
|
zyCategory: curCategory.value.key, // 资源类别
|
||||||
|
resourType: leafNode.key, // 资源类型(用于资源明细查询,兼容旧版)
|
||||||
|
category: curCategory.value.key, // 类别(兼容旧版)
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log('选择的参数:', {
|
console.log('选择的参数:', {
|
||||||
@ -148,8 +150,10 @@ const goTo = function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
setData(params);
|
setData(params);
|
||||||
|
|
||||||
|
// 直接跳转到资源包列表
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: `/pages/view/routine/JiaoXueZiYuan/indexList`
|
url: `/pages/view/routine/JiaoXueZiYuan/zy-list`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
703
src/pages/view/routine/JiaoXueZiYuan/zy-add.vue
Normal file
703
src/pages/view/routine/JiaoXueZiYuan/zy-add.vue
Normal file
@ -0,0 +1,703 @@
|
|||||||
|
<template>
|
||||||
|
<BasicLayout :show-nav-bar="true" :nav-bar-props="{ title: modalTitle }">
|
||||||
|
<view class="add-page">
|
||||||
|
<!-- 基本信息 -->
|
||||||
|
<view class="form-section">
|
||||||
|
<view class="section-title">基本信息</view>
|
||||||
|
|
||||||
|
<!-- 资源包名称 -->
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="form-label"><text class="required">*</text> 资源包名称</text>
|
||||||
|
<input
|
||||||
|
v-model="formData.zyName"
|
||||||
|
placeholder="请输入资源包名称"
|
||||||
|
class="form-input"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 资源目录 -->
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="form-label"><text class="required">*</text> 资源目录</text>
|
||||||
|
<view class="picker-box" @click="showResourceTree">
|
||||||
|
<text :class="{ placeholder: !formData.zymlId }">
|
||||||
|
{{ selectedTreeText || '请选择资源目录' }}
|
||||||
|
</text>
|
||||||
|
<uni-icons type="right" size="16" color="#999"></uni-icons>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 资源类别 -->
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="form-label"><text class="required">*</text> 资源类别</text>
|
||||||
|
<picker
|
||||||
|
mode="selector"
|
||||||
|
:range="categoryList"
|
||||||
|
range-key="label"
|
||||||
|
@change="handleCategoryChange"
|
||||||
|
>
|
||||||
|
<view class="picker-box">
|
||||||
|
<text :class="{ placeholder: !formData.zyCategory }">
|
||||||
|
{{ getCategoryText() || '请选择资源类别' }}
|
||||||
|
</text>
|
||||||
|
<uni-icons type="right" size="16" color="#999"></uni-icons>
|
||||||
|
</view>
|
||||||
|
</picker>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 资源描述 -->
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="form-label">资源描述</text>
|
||||||
|
<textarea
|
||||||
|
v-model="formData.zyDesc"
|
||||||
|
placeholder="请输入资源描述"
|
||||||
|
class="form-textarea"
|
||||||
|
maxlength="500"
|
||||||
|
></textarea>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 上传资源 -->
|
||||||
|
<view class="form-section">
|
||||||
|
<view class="section-title">
|
||||||
|
<text>上传资源</text>
|
||||||
|
<text class="required">*</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 上传按钮 -->
|
||||||
|
<view class="upload-box" @click="chooseFile">
|
||||||
|
<uni-icons type="cloud-upload" size="40" color="#1890ff"></uni-icons>
|
||||||
|
<text class="upload-text">点击选择文件上传</text>
|
||||||
|
<text class="upload-hint">支持 .doc、.pdf、.ppt、.xls 等格式</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 已上传文件列表 -->
|
||||||
|
<view v-if="uploadedFiles.length > 0" class="files-list">
|
||||||
|
<view class="list-header">已上传文件 ({{ uploadedFiles.length }})</view>
|
||||||
|
<view
|
||||||
|
v-for="(file, index) in uploadedFiles"
|
||||||
|
:key="index"
|
||||||
|
class="file-item"
|
||||||
|
>
|
||||||
|
<view class="file-info">
|
||||||
|
<text class="file-index">{{ index + 1 }}</text>
|
||||||
|
<view class="file-detail">
|
||||||
|
<text class="file-name">{{ file.fileName }}</text>
|
||||||
|
<text class="file-meta">{{ file.fileType }} • {{ formatFileSize(file.fileSize) }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 资源类型选择 -->
|
||||||
|
<picker
|
||||||
|
mode="selector"
|
||||||
|
:range="resourceTypeOptions"
|
||||||
|
range-key="label"
|
||||||
|
:value="getResourceTypeIndex(file.category)"
|
||||||
|
@change="(e: any) => handleFileTypeChange(e, index)"
|
||||||
|
>
|
||||||
|
<view class="type-picker">
|
||||||
|
<text :class="{ placeholder: !file.category }">
|
||||||
|
{{ getResourceTypeText(file.category) || '选择类型' }}
|
||||||
|
</text>
|
||||||
|
<uni-icons type="right" size="14" color="#999"></uni-icons>
|
||||||
|
</view>
|
||||||
|
</picker>
|
||||||
|
|
||||||
|
<!-- 删除按钮 -->
|
||||||
|
<view class="file-remove" @click="removeFile(index)">
|
||||||
|
<uni-icons type="trash" size="18" color="#ff4d4f"></uni-icons>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 底部操作栏 -->
|
||||||
|
<template #bottom>
|
||||||
|
<view class="bottom-actions">
|
||||||
|
<u-button text="取消" @click="handleCancel" class="action-btn"></u-button>
|
||||||
|
<u-button text="提交" type="primary" @click="handleSubmit" :loading="submitLoading" class="action-btn"></u-button>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
</BasicLayout>
|
||||||
|
|
||||||
|
<!-- 资源目录树选择 -->
|
||||||
|
<BasicTree
|
||||||
|
ref="treeRef"
|
||||||
|
:range="treeData"
|
||||||
|
idKey="key"
|
||||||
|
rangeKey="title"
|
||||||
|
title="选择资源目录"
|
||||||
|
:multiple="false"
|
||||||
|
:selectParent="true"
|
||||||
|
@confirm="onTreeConfirm"
|
||||||
|
@cancel="onTreeCancel"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { onLoad } from '@dcloudio/uni-app';
|
||||||
|
import BasicTree from '@/components/BasicTree/Tree.vue';
|
||||||
|
import {
|
||||||
|
zySaveApi,
|
||||||
|
zymlFindTreeApi,
|
||||||
|
dicFindByPidApi
|
||||||
|
} from "@/api/base/server";
|
||||||
|
import { attachmentUpload } from "@/api/system/upload";
|
||||||
|
|
||||||
|
const modalTitle = ref('新增资源包');
|
||||||
|
const formData = ref<any>({
|
||||||
|
zyName: '',
|
||||||
|
zymlId: '',
|
||||||
|
zyCategory: '',
|
||||||
|
zyDesc: ''
|
||||||
|
});
|
||||||
|
const uploadedFiles = ref<any[]>([]);
|
||||||
|
const submitLoading = ref(false);
|
||||||
|
|
||||||
|
// 资源目录树
|
||||||
|
const treeData = ref<any[]>([]);
|
||||||
|
const treeRef = ref();
|
||||||
|
const selectedTreeText = ref('');
|
||||||
|
|
||||||
|
// 资源类别列表(课程包、练习包等)
|
||||||
|
const categoryList = ref<any[]>([]);
|
||||||
|
|
||||||
|
// 资源类型列表(课件、教案、学案等)- 用于每个文件选择类型
|
||||||
|
const resourceTypeOptions = ref<any[]>([]);
|
||||||
|
|
||||||
|
// 获取类别文本
|
||||||
|
const getCategoryText = () => {
|
||||||
|
const item = categoryList.value.find(c => c.value === formData.value.zyCategory);
|
||||||
|
return item?.label || '';
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取资源类型文本
|
||||||
|
const getResourceTypeText = (typeValue: string) => {
|
||||||
|
const item = resourceTypeOptions.value.find(t => t.value === typeValue);
|
||||||
|
return item?.label || '';
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取资源类型索引
|
||||||
|
const getResourceTypeIndex = (typeValue: string) => {
|
||||||
|
return resourceTypeOptions.value.findIndex(t => t.value === typeValue);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理类别选择
|
||||||
|
const handleCategoryChange = (e: any) => {
|
||||||
|
formData.value.zyCategory = categoryList.value[e.detail.value]?.value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理文件类型选择
|
||||||
|
const handleFileTypeChange = (e: any, index: number) => {
|
||||||
|
uploadedFiles.value[index].category = resourceTypeOptions.value[e.detail.value]?.value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 显示资源目录树
|
||||||
|
const showResourceTree = () => {
|
||||||
|
if (treeRef.value) {
|
||||||
|
treeRef.value._show();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 树形选择确认
|
||||||
|
const onTreeConfirm = (selectedItems: any[]) => {
|
||||||
|
if (selectedItems.length > 0) {
|
||||||
|
const selectedItem = selectedItems[0]; // 单选模式,取第一个
|
||||||
|
formData.value.zymlId = selectedItem.key;
|
||||||
|
selectedTreeText.value = selectedItem.title;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 树形选择取消
|
||||||
|
const onTreeCancel = () => {
|
||||||
|
// 取消选择,不做任何操作
|
||||||
|
};
|
||||||
|
|
||||||
|
// 选择文件
|
||||||
|
const chooseFile = () => {
|
||||||
|
uni.chooseFile({
|
||||||
|
count: 10,
|
||||||
|
type: 'all',
|
||||||
|
// 不限制文件类型
|
||||||
|
success: (res) => {
|
||||||
|
const files = Array.isArray(res.tempFiles) ? res.tempFiles : [res.tempFiles];
|
||||||
|
files.forEach((file: any) => {
|
||||||
|
uploadFile(file);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fail: (error) => {
|
||||||
|
console.error('选择文件失败:', error);
|
||||||
|
uni.showToast({
|
||||||
|
title: '选择文件失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 上传文件
|
||||||
|
const uploadFile = async (file: any) => {
|
||||||
|
uni.showLoading({ title: '上传中...' });
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 使用封装的 attachmentUpload 方法
|
||||||
|
const uploadResult: any = await attachmentUpload(file.path as any);
|
||||||
|
|
||||||
|
console.log('上传响应:', uploadResult);
|
||||||
|
|
||||||
|
if (uploadResult.resultCode === 1 && uploadResult.result && uploadResult.result.length > 0) {
|
||||||
|
const fileInfo = uploadResult.result[0];
|
||||||
|
uploadedFiles.value.push({
|
||||||
|
fileName: file.name,
|
||||||
|
filePath: fileInfo.filePath,
|
||||||
|
fileId: fileInfo.id,
|
||||||
|
fileType: getFileExtension(file.name),
|
||||||
|
fileSize: file.size,
|
||||||
|
category: '' // 待用户选择
|
||||||
|
});
|
||||||
|
|
||||||
|
uni.showToast({
|
||||||
|
title: '上传成功',
|
||||||
|
icon: 'success'
|
||||||
|
});
|
||||||
|
} else if (uploadResult.resultCode === 1 && uploadResult.result && uploadResult.result.length === 0) {
|
||||||
|
console.error('上传成功但返回数据为空:', uploadResult);
|
||||||
|
uni.showToast({
|
||||||
|
title: '上传失败:服务器未返回文件信息',
|
||||||
|
icon: 'none',
|
||||||
|
duration: 2500
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: uploadResult.message || '上传失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('上传失败:', error);
|
||||||
|
uni.showToast({
|
||||||
|
title: '上传失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
uni.hideLoading();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取文件扩展名
|
||||||
|
const getFileExtension = (fileName: string) => {
|
||||||
|
const parts = fileName.split('.');
|
||||||
|
return parts.length > 1 ? parts[parts.length - 1] : '';
|
||||||
|
};
|
||||||
|
|
||||||
|
// 格式化文件大小
|
||||||
|
const formatFileSize = (size: number) => {
|
||||||
|
if (!size) return '0 B';
|
||||||
|
const k = 1024;
|
||||||
|
const sizes = ['B', 'KB', 'MB', 'GB'];
|
||||||
|
const i = Math.floor(Math.log(size) / Math.log(k));
|
||||||
|
return (size / Math.pow(k, i)).toFixed(2) + ' ' + sizes[i];
|
||||||
|
};
|
||||||
|
|
||||||
|
// 移除文件
|
||||||
|
const removeFile = (index: number) => {
|
||||||
|
uploadedFiles.value.splice(index, 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 取消
|
||||||
|
const handleCancel = () => {
|
||||||
|
uni.navigateBack();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 提交
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
// 表单验证
|
||||||
|
if (!formData.value.zyName) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '请输入资源包名称',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!formData.value.zymlId) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '请选择资源目录',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!formData.value.zyCategory) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '请选择资源类别',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uploadedFiles.value.length === 0) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '请至少上传一个资源文件',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证每个文件都已选择类型
|
||||||
|
const unselectedFiles = uploadedFiles.value.filter(file => !file.category);
|
||||||
|
if (unselectedFiles.length > 0) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '请为所有文件选择资源类型',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
submitLoading.value = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 构建提交数据
|
||||||
|
const submitData: any = {
|
||||||
|
zyName: formData.value.zyName,
|
||||||
|
zymlId: formData.value.zymlId,
|
||||||
|
zyCategory: formData.value.zyCategory,
|
||||||
|
zyDesc: formData.value.zyDesc || '',
|
||||||
|
// sort 由后端自动生成
|
||||||
|
zymxList: uploadedFiles.value.map((file, index) => ({
|
||||||
|
resourName: file.fileName,
|
||||||
|
resourUrl: file.filePath,
|
||||||
|
resourId: file.fileId,
|
||||||
|
resSuf: file.fileType,
|
||||||
|
category: file.category,
|
||||||
|
sort: index + 1
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('提交资源包数据:', submitData);
|
||||||
|
|
||||||
|
const res = await zySaveApi(submitData);
|
||||||
|
|
||||||
|
if (res.resultCode === 1) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '保存成功',
|
||||||
|
icon: 'success'
|
||||||
|
});
|
||||||
|
|
||||||
|
// 发送事件通知列表页刷新
|
||||||
|
uni.$emit('refreshResourceList');
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.navigateBack();
|
||||||
|
}, 1500);
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: res.message || '保存失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('提交失败:', error);
|
||||||
|
uni.showToast({
|
||||||
|
title: '提交失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
submitLoading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 加载资源目录树
|
||||||
|
const loadTreeData = async () => {
|
||||||
|
try {
|
||||||
|
const res = await zymlFindTreeApi();
|
||||||
|
if (res && res.result) {
|
||||||
|
// 递归转换数据格式以适配 BasicTree 组件
|
||||||
|
const convertTreeData = (items: any[]): any[] => {
|
||||||
|
return items.map((item: any) => ({
|
||||||
|
key: item.key,
|
||||||
|
title: item.title,
|
||||||
|
value: item.value,
|
||||||
|
children: item.children ? convertTreeData(item.children) : [],
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
treeData.value = convertTreeData(res.result);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载资源目录失败:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 加载资源类别(课程包、练习包等)
|
||||||
|
const loadCategoryList = async () => {
|
||||||
|
try {
|
||||||
|
const pid = '5'; // 资源类别字典父ID
|
||||||
|
const res = await dicFindByPidApi({ pid });
|
||||||
|
|
||||||
|
if (res && res.result && Array.isArray(res.result)) {
|
||||||
|
categoryList.value = res.result.map((item: any) => ({
|
||||||
|
value: item.dictionaryCode || item.dictionaryValue || item.id,
|
||||||
|
label: item.dictionaryName || item.dictionaryValue || item.name
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
categoryList.value = [
|
||||||
|
{ value: '1', label: '课程包' },
|
||||||
|
{ value: '2', label: '练习包' },
|
||||||
|
{ value: '3', label: '试卷包' }
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 默认选中第一条记录
|
||||||
|
if (categoryList.value.length > 0 && !formData.value.zyCategory) {
|
||||||
|
formData.value.zyCategory = categoryList.value[0].value;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载资源类别失败:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 加载资源类型(课件、教案等)
|
||||||
|
const loadResourceTypeOptions = async () => {
|
||||||
|
try {
|
||||||
|
const pid = '1391443399'; // 资源类型字典父ID
|
||||||
|
const res = await dicFindByPidApi({ pid });
|
||||||
|
|
||||||
|
if (res && res.result && Array.isArray(res.result)) {
|
||||||
|
resourceTypeOptions.value = res.result.map((item: any) => ({
|
||||||
|
value: item.dictionaryCode || item.dictionaryValue || item.id,
|
||||||
|
label: item.dictionaryName || item.dictionaryValue || item.name
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
resourceTypeOptions.value = [
|
||||||
|
{ value: '1', label: '课件' },
|
||||||
|
{ value: '2', label: '教案' },
|
||||||
|
{ value: '3', label: '学案' },
|
||||||
|
{ value: '4', label: '作业' },
|
||||||
|
{ value: '5', label: '试卷' }
|
||||||
|
];
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载资源类型失败:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onLoad(async () => {
|
||||||
|
await loadTreeData();
|
||||||
|
await loadCategoryList();
|
||||||
|
await loadResourceTypeOptions();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.add-page {
|
||||||
|
min-height: 100vh;
|
||||||
|
background: #f5f5f5;
|
||||||
|
padding: 20rpx;
|
||||||
|
padding-bottom: 140rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-section {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
padding: 30rpx;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 25rpx;
|
||||||
|
padding-bottom: 15rpx;
|
||||||
|
border-bottom: 1rpx solid #f0f0f0;
|
||||||
|
|
||||||
|
.required {
|
||||||
|
color: #ff4d4f;
|
||||||
|
margin-left: 5rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-item {
|
||||||
|
margin-bottom: 30rpx;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-label {
|
||||||
|
display: block;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 15rpx;
|
||||||
|
|
||||||
|
.required {
|
||||||
|
color: #ff4d4f;
|
||||||
|
margin-right: 5rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-input {
|
||||||
|
width: 100%;
|
||||||
|
height: 70rpx;
|
||||||
|
padding: 0 25rpx;
|
||||||
|
border: 1rpx solid #e0e0e0;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-textarea {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 150rpx;
|
||||||
|
padding: 20rpx;
|
||||||
|
border: 1rpx solid #e0e0e0;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.picker-box {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
height: 70rpx;
|
||||||
|
padding: 0 25rpx;
|
||||||
|
border: 1rpx solid #e0e0e0;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
|
||||||
|
.placeholder {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-box {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 60rpx;
|
||||||
|
border: 2rpx dashed #d9d9d9;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
background: #fafafa;
|
||||||
|
|
||||||
|
.upload-text {
|
||||||
|
margin-top: 20rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-hint {
|
||||||
|
margin-top: 10rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.files-list {
|
||||||
|
margin-top: 30rpx;
|
||||||
|
border: 1rpx solid #f0f0f0;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.list-header {
|
||||||
|
padding: 20rpx 25rpx;
|
||||||
|
background: #fafafa;
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
border-bottom: 1rpx solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 20rpx 25rpx;
|
||||||
|
border-bottom: 1rpx solid #f0f0f0;
|
||||||
|
gap: 15rpx;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-info {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 15rpx;
|
||||||
|
min-width: 0;
|
||||||
|
|
||||||
|
.file-index {
|
||||||
|
width: 40rpx;
|
||||||
|
height: 40rpx;
|
||||||
|
background: #1890ff;
|
||||||
|
color: #fff;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 22rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-detail {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
|
||||||
|
.file-name {
|
||||||
|
display: block;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #333;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
margin-bottom: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-meta {
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.type-picker {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8rpx;
|
||||||
|
padding: 10rpx 20rpx;
|
||||||
|
background: #f5f5f5;
|
||||||
|
border-radius: 6rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
.placeholder {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-remove {
|
||||||
|
flex-shrink: 0;
|
||||||
|
padding: 10rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 20rpx;
|
||||||
|
padding: 20rpx;
|
||||||
|
background: #fff;
|
||||||
|
border-top: 1rpx solid #f0f0f0;
|
||||||
|
|
||||||
|
.action-btn {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
808
src/pages/view/routine/JiaoXueZiYuan/zy-detail.vue
Normal file
808
src/pages/view/routine/JiaoXueZiYuan/zy-detail.vue
Normal file
@ -0,0 +1,808 @@
|
|||||||
|
<template>
|
||||||
|
<BasicLayout :show-nav-bar="true" :nav-bar-props="{ title: '资源包详情' }">
|
||||||
|
<view class="detail-page">
|
||||||
|
<view v-if="loading" class="loading-container">
|
||||||
|
<text class="loading-text">加载中...</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-else-if="resourceDetail" class="detail-content">
|
||||||
|
<!-- 资源包信息卡片 -->
|
||||||
|
<view class="info-card">
|
||||||
|
<view class="package-header">
|
||||||
|
<text class="package-title">{{ resourceDetail.zyName }}</text>
|
||||||
|
<view class="package-category">{{ getCategoryName(resourceDetail.zyCategory) }}</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 评分和操作区域 -->
|
||||||
|
<view class="rating-action-section">
|
||||||
|
<!-- 左侧:评分展示 -->
|
||||||
|
<view class="rating-display">
|
||||||
|
<text class="rating-score">{{ (resourceDetail.scoreAvg || 0).toFixed(1) }}</text>
|
||||||
|
<view class="rating-stars-wrapper">
|
||||||
|
<text class="rating-stars">{{ getStarDisplay(resourceDetail.scoreAvg) }}</text>
|
||||||
|
</view>
|
||||||
|
<text class="rating-label">当前评分</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 右侧:操作按钮 -->
|
||||||
|
<view class="action-buttons">
|
||||||
|
<view class="action-item" @click="handleRating">
|
||||||
|
<uni-icons type="star" size="24" color="#faad14"></uni-icons>
|
||||||
|
<text class="action-label">评分</text>
|
||||||
|
</view>
|
||||||
|
<view class="action-item" @click="handleCollect">
|
||||||
|
<uni-icons
|
||||||
|
:type="resourceDetail.isCollect === '1' ? 'heart-filled' : 'heart'"
|
||||||
|
size="24"
|
||||||
|
:color="resourceDetail.isCollect === '1' ? '#ff4d4f' : '#999'"
|
||||||
|
></uni-icons>
|
||||||
|
<text class="action-label">{{ resourceDetail.isCollect === '1' ? '已收藏' : '收藏' }}</text>
|
||||||
|
<text class="action-count">{{ formatNumber(resourceDetail.collNum || 0) }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="action-item" @click="handleLike">
|
||||||
|
<view class="like-icon">
|
||||||
|
<text class="like-emoji">{{ resourceDetail.isLike === '1' ? '👍' : '👍🏻' }}</text>
|
||||||
|
</view>
|
||||||
|
<text class="action-label">{{ resourceDetail.isLike === '1' ? '已点赞' : '点赞' }}</text>
|
||||||
|
<text class="action-count">{{ formatNumber(resourceDetail.likeNum || 0) }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 描述 -->
|
||||||
|
<view class="description-section">
|
||||||
|
<text class="section-title">资源描述</text>
|
||||||
|
<text class="description-text">{{ resourceDetail.zyDesc || '暂无描述' }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 包含的资源列表 -->
|
||||||
|
<view class="resources-card">
|
||||||
|
<view class="card-title">
|
||||||
|
包含的资源 ({{ resourceDetail.zymxList?.length || 0 }})
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-if="resourceDetail.zymxList && resourceDetail.zymxList.length > 0">
|
||||||
|
<view
|
||||||
|
v-for="(item, index) in resourceDetail.zymxList"
|
||||||
|
:key="item.id"
|
||||||
|
class="resource-item"
|
||||||
|
>
|
||||||
|
<view class="item-index">{{ index + 1 }}</view>
|
||||||
|
<view class="item-info">
|
||||||
|
<text class="item-name">{{ item.resourName }}</text>
|
||||||
|
<view class="item-meta">
|
||||||
|
<text class="meta-tag">{{ item.resSuf }}</text>
|
||||||
|
<text class="meta-stats">浏览 {{ item.lookNum || 0 }}</text>
|
||||||
|
<text class="meta-stats">下载 {{ item.downNum || 0 }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="item-actions">
|
||||||
|
<u-button
|
||||||
|
v-if="canPreview(item.resSuf)"
|
||||||
|
size="mini"
|
||||||
|
type="primary"
|
||||||
|
@click="handlePreview(item)"
|
||||||
|
>
|
||||||
|
预览
|
||||||
|
</u-button>
|
||||||
|
<u-button
|
||||||
|
size="mini"
|
||||||
|
type="success"
|
||||||
|
@click="handleDownload(item)"
|
||||||
|
>
|
||||||
|
下载
|
||||||
|
</u-button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-else class="empty-state">
|
||||||
|
<text class="empty-text">暂无资源</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-else class="error-container">
|
||||||
|
<text class="error-text">加载失败</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 底部返回按钮 -->
|
||||||
|
<template #bottom>
|
||||||
|
<view class="bottom-actions">
|
||||||
|
<u-button
|
||||||
|
type="primary"
|
||||||
|
@click="handleBack"
|
||||||
|
class="back-button"
|
||||||
|
>
|
||||||
|
返回
|
||||||
|
</u-button>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
</BasicLayout>
|
||||||
|
|
||||||
|
<!-- 评分弹窗(使用原生 modal)-->
|
||||||
|
<view v-if="showRatingModal" class="rating-modal-mask" @click="closeRatingModal">
|
||||||
|
<view class="rating-modal" @click.stop>
|
||||||
|
<view class="modal-title">资源评分</view>
|
||||||
|
<view class="modal-content">
|
||||||
|
<text class="resource-name">{{ resourceDetail?.zyName }}</text>
|
||||||
|
|
||||||
|
<view class="rating-input">
|
||||||
|
<view class="star-picker">
|
||||||
|
<view
|
||||||
|
v-for="star in 10"
|
||||||
|
:key="star"
|
||||||
|
class="star-item"
|
||||||
|
@click="selectRating(star / 2)"
|
||||||
|
>
|
||||||
|
<text class="star-icon">{{ getStarIcon(star / 2, ratingValue) }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<text class="rating-value">{{ ratingValue.toFixed(1) }}分</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<textarea
|
||||||
|
v-model="ratingComment"
|
||||||
|
placeholder="请输入评价内容(选填)"
|
||||||
|
maxlength="200"
|
||||||
|
class="comment-textarea"
|
||||||
|
></textarea>
|
||||||
|
<text class="char-count">{{ ratingComment.length }}/200</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="modal-actions">
|
||||||
|
<button class="modal-btn cancel-btn" @click="closeRatingModal">取消</button>
|
||||||
|
<button class="modal-btn confirm-btn" @click="submitRating">确认</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { onLoad } from '@dcloudio/uni-app';
|
||||||
|
import { imagUrl } from "@/utils";
|
||||||
|
import {
|
||||||
|
zyGetDetailApi,
|
||||||
|
zyCollectApi,
|
||||||
|
zyLikeApi,
|
||||||
|
zyScoreApi,
|
||||||
|
dicFindByPidApi
|
||||||
|
} from "@/api/base/server";
|
||||||
|
import {
|
||||||
|
isVideo,
|
||||||
|
isImage,
|
||||||
|
canPreview,
|
||||||
|
previewFile,
|
||||||
|
previewVideo,
|
||||||
|
previewImage,
|
||||||
|
downloadFile
|
||||||
|
} from "@/utils/filePreview";
|
||||||
|
|
||||||
|
const resourceId = ref<string>('');
|
||||||
|
const resourceDetail = ref<any>(null);
|
||||||
|
const loading = ref(false);
|
||||||
|
const categoryOptions = ref<any[]>([]);
|
||||||
|
const showRatingModal = ref(false);
|
||||||
|
const ratingValue = ref(0);
|
||||||
|
const ratingComment = ref('');
|
||||||
|
|
||||||
|
// 获取类别名称
|
||||||
|
const getCategoryName = (category: string) => {
|
||||||
|
const option = categoryOptions.value.find(item => item.key === category);
|
||||||
|
return option?.label || category;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 格式化数字(如:114000 => 11.4万)
|
||||||
|
const formatNumber = (num: number) => {
|
||||||
|
if (num >= 10000) {
|
||||||
|
return (num / 10000).toFixed(1) + '万';
|
||||||
|
}
|
||||||
|
return num.toString();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取星星显示(只读)
|
||||||
|
const getStarDisplay = (score: number) => {
|
||||||
|
if (!score) return '☆☆☆☆☆';
|
||||||
|
|
||||||
|
const fullStars = Math.floor(score);
|
||||||
|
const hasHalfStar = (score % 1) >= 0.25 && (score % 1) < 0.75;
|
||||||
|
const emptyStars = 5 - fullStars - (hasHalfStar ? 1 : 0);
|
||||||
|
|
||||||
|
return '★'.repeat(fullStars) + (hasHalfStar ? '⭐' : '') + '☆'.repeat(Math.max(0, emptyStars));
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取星星图标(评分选择)
|
||||||
|
const getStarIcon = (value: number, currentValue: number) => {
|
||||||
|
if (value <= currentValue) {
|
||||||
|
// 如果是整数位,显示满星
|
||||||
|
if (value % 1 === 0) {
|
||||||
|
return '★';
|
||||||
|
}
|
||||||
|
// 如果是半星位
|
||||||
|
return '⭐';
|
||||||
|
}
|
||||||
|
return '☆';
|
||||||
|
};
|
||||||
|
|
||||||
|
// 选择评分
|
||||||
|
const selectRating = (value: number) => {
|
||||||
|
ratingValue.value = value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 加载资源包详情
|
||||||
|
const loadDetail = async () => {
|
||||||
|
if (!resourceId.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
loading.value = true;
|
||||||
|
try {
|
||||||
|
const res = await zyGetDetailApi({ id: resourceId.value });
|
||||||
|
if (res && res.result) {
|
||||||
|
resourceDetail.value = res.result;
|
||||||
|
console.log('资源包详情:', resourceDetail.value);
|
||||||
|
console.log('资源明细列表:', resourceDetail.value.zymxList);
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: '加载失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载资源包详情失败:', error);
|
||||||
|
uni.showToast({
|
||||||
|
title: '加载失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 收藏/取消收藏
|
||||||
|
const handleCollect = async () => {
|
||||||
|
if (!resourceDetail.value) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await zyCollectApi({ zyId: resourceDetail.value.id });
|
||||||
|
if (res.resultCode === 1) {
|
||||||
|
const isCollected = resourceDetail.value.isCollect === '1';
|
||||||
|
resourceDetail.value.isCollect = isCollected ? '0' : '1';
|
||||||
|
resourceDetail.value.collNum = isCollected
|
||||||
|
? (resourceDetail.value.collNum - 1)
|
||||||
|
: (resourceDetail.value.collNum + 1);
|
||||||
|
|
||||||
|
uni.showToast({
|
||||||
|
title: isCollected ? '取消收藏成功' : '收藏成功',
|
||||||
|
icon: 'success'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('收藏操作失败:', error);
|
||||||
|
uni.showToast({
|
||||||
|
title: '操作失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 点赞/取消点赞
|
||||||
|
const handleLike = async () => {
|
||||||
|
if (!resourceDetail.value) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await zyLikeApi({ zyId: resourceDetail.value.id });
|
||||||
|
if (res.resultCode === 1) {
|
||||||
|
const isLiked = resourceDetail.value.isLike === '1';
|
||||||
|
resourceDetail.value.isLike = isLiked ? '0' : '1';
|
||||||
|
resourceDetail.value.likeNum = isLiked
|
||||||
|
? (resourceDetail.value.likeNum - 1)
|
||||||
|
: (resourceDetail.value.likeNum + 1);
|
||||||
|
|
||||||
|
uni.showToast({
|
||||||
|
title: isLiked ? '取消点赞成功' : '点赞成功',
|
||||||
|
icon: 'success'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('点赞操作失败:', error);
|
||||||
|
uni.showToast({
|
||||||
|
title: '操作失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 打开评分弹窗
|
||||||
|
const handleRating = () => {
|
||||||
|
ratingValue.value = resourceDetail.value?.userScore || 0;
|
||||||
|
ratingComment.value = '';
|
||||||
|
showRatingModal.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 关闭评分弹窗
|
||||||
|
const closeRatingModal = () => {
|
||||||
|
showRatingModal.value = false;
|
||||||
|
ratingValue.value = 0;
|
||||||
|
ratingComment.value = '';
|
||||||
|
};
|
||||||
|
|
||||||
|
// 提交评分
|
||||||
|
const submitRating = async () => {
|
||||||
|
if (ratingValue.value === 0) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '请先评分',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await zyScoreApi({
|
||||||
|
zyId: resourceDetail.value.id,
|
||||||
|
score: ratingValue.value,
|
||||||
|
comment: ratingComment.value
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.resultCode === 1) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '评分成功',
|
||||||
|
icon: 'success'
|
||||||
|
});
|
||||||
|
|
||||||
|
closeRatingModal();
|
||||||
|
loadDetail(); // 刷新详情
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: '评分失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('评分失败:', error);
|
||||||
|
uni.showToast({
|
||||||
|
title: '评分失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 预览资源
|
||||||
|
const handlePreview = (item: any) => {
|
||||||
|
const fileUrl = imagUrl(item.resourUrl);
|
||||||
|
const fileName = item.resourName + '.' + item.resSuf;
|
||||||
|
|
||||||
|
if (isVideo(item.resSuf)) {
|
||||||
|
previewVideo(fileUrl, fileName);
|
||||||
|
} else if (isImage(item.resSuf)) {
|
||||||
|
previewImage(fileUrl);
|
||||||
|
} else if (canPreview(item.resSuf)) {
|
||||||
|
previewFile(fileUrl, fileName, item.resSuf);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 下载资源
|
||||||
|
const handleDownload = (item: any) => {
|
||||||
|
const fileUrl = imagUrl(item.resourUrl);
|
||||||
|
const fileName = item.resourName + '.' + item.resSuf;
|
||||||
|
|
||||||
|
downloadFile(fileUrl, fileName)
|
||||||
|
.then(() => {
|
||||||
|
uni.showToast({
|
||||||
|
title: '下载成功',
|
||||||
|
icon: 'success'
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('下载失败:', error);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 加载资源类别字典
|
||||||
|
const loadCategoryOptions = async () => {
|
||||||
|
try {
|
||||||
|
const pid = '1391443399';
|
||||||
|
const res = await dicFindByPidApi({ pid });
|
||||||
|
|
||||||
|
if (res && res.result && Array.isArray(res.result)) {
|
||||||
|
categoryOptions.value = res.result.map((item: any) => ({
|
||||||
|
key: item.dictionaryCode || item.dictionaryValue || item.id,
|
||||||
|
label: item.dictionaryName || item.dictionaryValue || item.name
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载资源类别失败:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 返回上一页
|
||||||
|
const handleBack = () => {
|
||||||
|
uni.navigateBack();
|
||||||
|
};
|
||||||
|
|
||||||
|
onLoad(async (options) => {
|
||||||
|
if (options?.id) {
|
||||||
|
resourceId.value = options.id;
|
||||||
|
await loadCategoryOptions();
|
||||||
|
await loadDetail();
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: '无效的资源ID',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.detail-page {
|
||||||
|
min-height: 100vh;
|
||||||
|
background: #f5f5f5;
|
||||||
|
padding-bottom: 120rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-container,
|
||||||
|
.error-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 200rpx 0;
|
||||||
|
|
||||||
|
.loading-text,
|
||||||
|
.error-text {
|
||||||
|
margin-top: 20rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-content {
|
||||||
|
padding: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-card {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
padding: 30rpx;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
|
||||||
|
|
||||||
|
.package-header {
|
||||||
|
margin-bottom: 25rpx;
|
||||||
|
|
||||||
|
.package-title {
|
||||||
|
font-size: 36rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
line-height: 1.4;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.package-category {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 10rpx 25rpx;
|
||||||
|
background: #e6f7ff;
|
||||||
|
color: #1890ff;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rating-action-section {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 30rpx 0;
|
||||||
|
border-bottom: 1rpx solid #f0f0f0;
|
||||||
|
|
||||||
|
.rating-display {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8rpx;
|
||||||
|
|
||||||
|
.rating-score {
|
||||||
|
font-size: 48rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #faad14;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rating-stars-wrapper {
|
||||||
|
.rating-stars {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #faad14;
|
||||||
|
letter-spacing: 2rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rating-label {
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: 40rpx;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.action-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8rpx;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 10rpx;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.like-icon {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 48rpx;
|
||||||
|
height: 48rpx;
|
||||||
|
|
||||||
|
.like-emoji {
|
||||||
|
font-size: 48rpx;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-label {
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #666;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-count {
|
||||||
|
font-size: 20rpx;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.description-section {
|
||||||
|
padding-top: 25rpx;
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 15rpx;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description-text {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #666;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.resources-card {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
padding: 30rpx;
|
||||||
|
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 25rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resource-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 25rpx 0;
|
||||||
|
border-bottom: 1rpx solid #f0f0f0;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-index {
|
||||||
|
width: 50rpx;
|
||||||
|
height: 50rpx;
|
||||||
|
background: #f0f0f0;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #666;
|
||||||
|
margin-right: 20rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-info {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
|
||||||
|
.item-name {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 10rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-meta {
|
||||||
|
display: flex;
|
||||||
|
gap: 15rpx;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
.meta-tag {
|
||||||
|
padding: 4rpx 12rpx;
|
||||||
|
background: #f0f0f0;
|
||||||
|
border-radius: 6rpx;
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meta-stats {
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 10rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-left: 15rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state {
|
||||||
|
padding: 80rpx 0;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.empty-text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-actions {
|
||||||
|
background: #fff;
|
||||||
|
padding: 20rpx;
|
||||||
|
border-top: 1rpx solid #f0f0f0;
|
||||||
|
|
||||||
|
.back-button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 评分弹窗遮罩层
|
||||||
|
.rating-modal-mask {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 9999;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 评分弹窗内容
|
||||||
|
.rating-modal {
|
||||||
|
width: 600rpx;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
padding: 40rpx;
|
||||||
|
margin: 0 20rpx;
|
||||||
|
|
||||||
|
.modal-title {
|
||||||
|
font-size: 36rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 30rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
.resource-name {
|
||||||
|
display: block;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #666;
|
||||||
|
margin-bottom: 25rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rating-input {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 20rpx;
|
||||||
|
margin-bottom: 30rpx;
|
||||||
|
|
||||||
|
.star-picker {
|
||||||
|
display: flex;
|
||||||
|
gap: 8rpx;
|
||||||
|
|
||||||
|
.star-item {
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
.star-icon {
|
||||||
|
font-size: 40rpx;
|
||||||
|
color: #faad14;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rating-value {
|
||||||
|
font-size: 36rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #faad14;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.comment-textarea {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 150rpx;
|
||||||
|
padding: 20rpx;
|
||||||
|
border: 1rpx solid #e0e0e0;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.char-count {
|
||||||
|
display: block;
|
||||||
|
text-align: right;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999;
|
||||||
|
margin-top: 10rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 20rpx;
|
||||||
|
margin-top: 30rpx;
|
||||||
|
|
||||||
|
.modal-btn {
|
||||||
|
flex: 1;
|
||||||
|
height: 80rpx;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
font-size: 30rpx;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&.cancel-btn {
|
||||||
|
background: #f0f0f0;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.confirm-btn {
|
||||||
|
background: #1890ff;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
663
src/pages/view/routine/JiaoXueZiYuan/zy-list.vue
Normal file
663
src/pages/view/routine/JiaoXueZiYuan/zy-list.vue
Normal file
@ -0,0 +1,663 @@
|
|||||||
|
<template>
|
||||||
|
<BasicListLayout
|
||||||
|
:show-nav-bar="true"
|
||||||
|
:nav-bar-props="{ title: '教学资源包' }"
|
||||||
|
@register="register"
|
||||||
|
>
|
||||||
|
<template #top>
|
||||||
|
<view class="search-section">
|
||||||
|
<view class="search-box">
|
||||||
|
<uni-icons type="search" size="18" color="#999"></uni-icons>
|
||||||
|
<input
|
||||||
|
class="search-input"
|
||||||
|
type="text"
|
||||||
|
placeholder="搜索资源包名称"
|
||||||
|
v-model="searchKeyword"
|
||||||
|
@confirm="onSearchConfirm"
|
||||||
|
/>
|
||||||
|
<view class="search-clear" v-if="searchKeyword" @click="clearSearch">
|
||||||
|
<uni-icons type="clear" size="16" color="#999"></uni-icons>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<u-button text="搜索" class="search-btn" type="primary" @click="onSearchConfirm"/>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #default="{ data }">
|
||||||
|
<view class="resource-package-card" @click="goToDetail(data)">
|
||||||
|
<!-- 左侧缩略图 -->
|
||||||
|
<view class="card-thumbnail">
|
||||||
|
<image
|
||||||
|
class="thumbnail-image"
|
||||||
|
src="/static/base/view/zyyl.png"
|
||||||
|
mode="aspectFit"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 右侧内容 -->
|
||||||
|
<view class="card-content">
|
||||||
|
<!-- 类别标签 -->
|
||||||
|
<view class="package-category">{{ getCategoryName(data.zyCategory) }}</view>
|
||||||
|
|
||||||
|
<!-- 标题 -->
|
||||||
|
<view class="package-title">{{ data.zyName }}</view>
|
||||||
|
|
||||||
|
<!-- 资源数量 -->
|
||||||
|
<view class="package-meta" v-if="data.zymxList && data.zymxList.length > 0">
|
||||||
|
<uni-icons type="list" size="12" color="#999"></uni-icons>
|
||||||
|
<text class="meta-text">{{ data.zymxList.length }} 个资源</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 底部统计和评分 -->
|
||||||
|
<view class="card-footer">
|
||||||
|
<view class="stats-row">
|
||||||
|
<view class="stat-item">
|
||||||
|
<uni-icons type="eye" size="14" color="#999"></uni-icons>
|
||||||
|
<text>{{ formatNumber(data.lookNum || 0) }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="stat-item">
|
||||||
|
<uni-icons
|
||||||
|
:type="data.isLike === '1' ? 'hand-thumbsup-filled' : 'hand-thumbsup'"
|
||||||
|
size="14"
|
||||||
|
:color="data.isLike === '1' ? '#1890ff' : '#999'"
|
||||||
|
></uni-icons>
|
||||||
|
<text>{{ formatNumber(data.likeNum || 0) }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="rating-score">{{ (data.scoreAvg || 0).toFixed(1) }}分</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 详情箭头 -->
|
||||||
|
<view class="card-arrow">
|
||||||
|
<uni-icons type="forward" size="20" color="#999"></uni-icons>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 底部新增按钮 -->
|
||||||
|
<template #bottom>
|
||||||
|
<view class="bottom-bar">
|
||||||
|
<u-button
|
||||||
|
text="新增资源包"
|
||||||
|
type="primary"
|
||||||
|
@click="navigateToAdd"
|
||||||
|
class="add-btn"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
</BasicListLayout>
|
||||||
|
|
||||||
|
<!-- 评分弹窗(使用原生 modal)-->
|
||||||
|
<view v-if="showRatingModal" class="rating-modal-mask" @click="closeRatingModal">
|
||||||
|
<view class="rating-modal" @click.stop>
|
||||||
|
<view class="modal-title">资源评分</view>
|
||||||
|
<view class="modal-content">
|
||||||
|
<text class="resource-name">{{ currentResource?.zyName }}</text>
|
||||||
|
|
||||||
|
<view class="rating-input">
|
||||||
|
<view class="star-picker">
|
||||||
|
<view
|
||||||
|
v-for="star in 10"
|
||||||
|
:key="star"
|
||||||
|
class="star-item"
|
||||||
|
@click="selectRating(star / 2)"
|
||||||
|
>
|
||||||
|
<text class="star-icon">{{ getStarIcon(star / 2, ratingValue) }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<text class="rating-value">{{ ratingValue.toFixed(1) }}分</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<textarea
|
||||||
|
v-model="ratingComment"
|
||||||
|
placeholder="请输入评价内容(选填)"
|
||||||
|
maxlength="200"
|
||||||
|
class="comment-textarea"
|
||||||
|
></textarea>
|
||||||
|
<text class="char-count">{{ ratingComment.length }}/200</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="modal-actions">
|
||||||
|
<button class="modal-btn cancel-btn" @click="closeRatingModal">取消</button>
|
||||||
|
<button class="modal-btn confirm-btn" @click="submitRating">确认</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, onMounted, onUnmounted } from 'vue';
|
||||||
|
import { useLayout } from "@/components/BasicListLayout/hooks/useLayout";
|
||||||
|
import {
|
||||||
|
zyFindPageApi,
|
||||||
|
zyCollectApi,
|
||||||
|
zyLikeApi,
|
||||||
|
zyScoreApi,
|
||||||
|
dicFindByPidApi
|
||||||
|
} from "@/api/base/server";
|
||||||
|
import { useDataStore } from "@/store/modules/data";
|
||||||
|
|
||||||
|
const { getData } = useDataStore();
|
||||||
|
|
||||||
|
const searchKeyword = ref<string>('');
|
||||||
|
const categoryOptions = ref<any[]>([]);
|
||||||
|
const showRatingModal = ref(false);
|
||||||
|
const currentResource = ref<any>(null);
|
||||||
|
const ratingValue = ref(0);
|
||||||
|
const ratingComment = ref('');
|
||||||
|
|
||||||
|
// 布局Hook
|
||||||
|
const [register, { reload, setParam }] = useLayout({
|
||||||
|
api: zyFindPageApi,
|
||||||
|
componentProps: {
|
||||||
|
defaultPageSize: 10,
|
||||||
|
loadingMoreEnabled: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// 构建查询参数
|
||||||
|
const buildParams = () => {
|
||||||
|
const params = {
|
||||||
|
...getData,
|
||||||
|
zyName: searchKeyword.value,
|
||||||
|
status: 'A'
|
||||||
|
};
|
||||||
|
console.log('查询资源包参数:', params);
|
||||||
|
setParam(params);
|
||||||
|
reload();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 搜索
|
||||||
|
const onSearchConfirm = () => {
|
||||||
|
buildParams();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 清空搜索
|
||||||
|
const clearSearch = () => {
|
||||||
|
searchKeyword.value = '';
|
||||||
|
buildParams();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取类别名称
|
||||||
|
const getCategoryName = (category: string) => {
|
||||||
|
const option = categoryOptions.value.find(item => item.key === category);
|
||||||
|
return option?.label || category;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 格式化数字(万为单位)
|
||||||
|
const formatNumber = (num: number) => {
|
||||||
|
if (num >= 10000) {
|
||||||
|
return (num / 10000).toFixed(1) + '万';
|
||||||
|
}
|
||||||
|
return num.toString();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取星星显示(只读)
|
||||||
|
const getStarDisplay = (score: number) => {
|
||||||
|
if (!score) return '☆☆☆☆☆';
|
||||||
|
|
||||||
|
const fullStars = Math.floor(score);
|
||||||
|
const hasHalfStar = (score % 1) >= 0.25 && (score % 1) < 0.75;
|
||||||
|
const emptyStars = 5 - fullStars - (hasHalfStar ? 1 : 0);
|
||||||
|
|
||||||
|
return '★'.repeat(fullStars) + (hasHalfStar ? '⭐' : '') + '☆'.repeat(Math.max(0, emptyStars));
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取星星图标(评分选择)
|
||||||
|
const getStarIcon = (value: number, currentValue: number) => {
|
||||||
|
if (value <= currentValue) {
|
||||||
|
// 如果是整数位,显示满星
|
||||||
|
if (value % 1 === 0) {
|
||||||
|
return '★';
|
||||||
|
}
|
||||||
|
// 如果是半星位
|
||||||
|
return '⭐';
|
||||||
|
}
|
||||||
|
return '☆';
|
||||||
|
};
|
||||||
|
|
||||||
|
// 选择评分
|
||||||
|
const selectRating = (value: number) => {
|
||||||
|
ratingValue.value = value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 收藏/取消收藏
|
||||||
|
const handleCollect = async (item: any) => {
|
||||||
|
try {
|
||||||
|
const res = await zyCollectApi({ zyId: item.id });
|
||||||
|
if (res.resultCode === 1) {
|
||||||
|
// 更新状态(直接修改对象)
|
||||||
|
const isCollected = item.isCollect === '1';
|
||||||
|
item.isCollect = isCollected ? '0' : '1';
|
||||||
|
item.collNum = isCollected ? (item.collNum - 1) : (item.collNum + 1);
|
||||||
|
|
||||||
|
uni.showToast({
|
||||||
|
title: isCollected ? '取消收藏成功' : '收藏成功',
|
||||||
|
icon: 'success'
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: '操作失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('收藏操作失败:', error);
|
||||||
|
uni.showToast({
|
||||||
|
title: '操作失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 点赞/取消点赞
|
||||||
|
const handleLike = async (item: any) => {
|
||||||
|
try {
|
||||||
|
const res = await zyLikeApi({ zyId: item.id });
|
||||||
|
if (res.resultCode === 1) {
|
||||||
|
// 更新状态(直接修改对象)
|
||||||
|
const isLiked = item.isLike === '1';
|
||||||
|
item.isLike = isLiked ? '0' : '1';
|
||||||
|
item.likeNum = isLiked ? (item.likeNum - 1) : (item.likeNum + 1);
|
||||||
|
|
||||||
|
uni.showToast({
|
||||||
|
title: isLiked ? '取消点赞成功' : '点赞成功',
|
||||||
|
icon: 'success'
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: '操作失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('点赞操作失败:', error);
|
||||||
|
uni.showToast({
|
||||||
|
title: '操作失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 打开评分弹窗
|
||||||
|
const handleRating = (item: any) => {
|
||||||
|
currentResource.value = item;
|
||||||
|
ratingValue.value = item.userScore || 0;
|
||||||
|
ratingComment.value = '';
|
||||||
|
showRatingModal.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 关闭评分弹窗
|
||||||
|
const closeRatingModal = () => {
|
||||||
|
showRatingModal.value = false;
|
||||||
|
currentResource.value = null;
|
||||||
|
ratingValue.value = 0;
|
||||||
|
ratingComment.value = '';
|
||||||
|
};
|
||||||
|
|
||||||
|
// 提交评分
|
||||||
|
const submitRating = async () => {
|
||||||
|
if (ratingValue.value === 0) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '请先评分',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await zyScoreApi({
|
||||||
|
zyId: currentResource.value.id,
|
||||||
|
score: ratingValue.value,
|
||||||
|
comment: ratingComment.value
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.resultCode === 1) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '评分成功',
|
||||||
|
icon: 'success'
|
||||||
|
});
|
||||||
|
|
||||||
|
closeRatingModal();
|
||||||
|
reload(); // 刷新列表
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: '评分失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('评分失败:', error);
|
||||||
|
uni.showToast({
|
||||||
|
title: '评分失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 跳转到详情页
|
||||||
|
const goToDetail = (item: any) => {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `/pages/view/routine/JiaoXueZiYuan/zy-detail?id=${item.id}`
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 跳转到新增页面
|
||||||
|
const navigateToAdd = () => {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/view/routine/JiaoXueZiYuan/zy-add'
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 加载资源类别字典
|
||||||
|
const loadCategoryOptions = async () => {
|
||||||
|
try {
|
||||||
|
const pid = '1391443399'; // 资源类别字典父ID(与后台一致)
|
||||||
|
const res = await dicFindByPidApi({ pid });
|
||||||
|
|
||||||
|
if (res && res.result && Array.isArray(res.result)) {
|
||||||
|
categoryOptions.value = res.result.map((item: any) => ({
|
||||||
|
key: item.dictionaryCode || item.dictionaryValue || item.id,
|
||||||
|
label: item.dictionaryName || item.dictionaryValue || item.name
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载资源类别失败:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 刷新列表的方法
|
||||||
|
const refreshList = () => {
|
||||||
|
console.log('收到刷新事件,重新加载资源包列表');
|
||||||
|
reload();
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
loadCategoryOptions();
|
||||||
|
buildParams();
|
||||||
|
|
||||||
|
// 监听刷新事件
|
||||||
|
uni.$on('refreshResourceList', refreshList);
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
// 移除事件监听
|
||||||
|
uni.$off('refreshResourceList', refreshList);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.search-section {
|
||||||
|
padding: 20rpx;
|
||||||
|
background: #fff;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 15rpx;
|
||||||
|
|
||||||
|
.search-box {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background: #f5f5f5;
|
||||||
|
border-radius: 30rpx;
|
||||||
|
padding: 15rpx 25rpx;
|
||||||
|
|
||||||
|
.search-input {
|
||||||
|
flex: 1;
|
||||||
|
margin-left: 15rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-clear {
|
||||||
|
margin-left: 10rpx;
|
||||||
|
padding: 5rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-btn {
|
||||||
|
width: 120rpx;
|
||||||
|
height: 60rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.resource-package-card {
|
||||||
|
background: #fff;
|
||||||
|
margin: 15rpx 10rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
padding: 20rpx;
|
||||||
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
|
||||||
|
display: flex;
|
||||||
|
gap: 20rpx;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
// 左侧缩略图
|
||||||
|
.card-thumbnail {
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 110rpx;
|
||||||
|
height: 90rpx;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: #f5f5f5;
|
||||||
|
|
||||||
|
.thumbnail-image {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 右侧内容
|
||||||
|
.card-content {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
min-width: 0;
|
||||||
|
padding-right: 50rpx; // 为详情箭头留出空间
|
||||||
|
|
||||||
|
.package-category {
|
||||||
|
display: inline-block;
|
||||||
|
align-self: flex-start;
|
||||||
|
padding: 4rpx 16rpx;
|
||||||
|
background: #1890ff;
|
||||||
|
color: #fff;
|
||||||
|
border-radius: 4rpx;
|
||||||
|
font-size: 22rpx;
|
||||||
|
margin-bottom: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.package-title {
|
||||||
|
font-size: 30rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
line-height: 1.4;
|
||||||
|
margin-bottom: 8rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
.package-meta {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6rpx;
|
||||||
|
margin-bottom: auto; // 推动底部内容到底部
|
||||||
|
|
||||||
|
.meta-text {
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 10rpx;
|
||||||
|
|
||||||
|
.stats-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 25rpx;
|
||||||
|
|
||||||
|
.stat-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rating-score {
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #ff8800;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 右侧详情箭头
|
||||||
|
.card-arrow {
|
||||||
|
position: absolute;
|
||||||
|
right: 20rpx;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 底部新增按钮
|
||||||
|
.bottom-bar {
|
||||||
|
padding: 20rpx;
|
||||||
|
background: #fff;
|
||||||
|
border-top: 1rpx solid #f0f0f0;
|
||||||
|
|
||||||
|
.add-btn {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 评分弹窗遮罩层
|
||||||
|
.rating-modal-mask {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 9999;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 评分弹窗内容
|
||||||
|
.rating-modal {
|
||||||
|
width: 600rpx;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
padding: 40rpx;
|
||||||
|
margin: 0 20rpx;
|
||||||
|
|
||||||
|
.modal-title {
|
||||||
|
font-size: 36rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 30rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
.resource-name {
|
||||||
|
display: block;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #666;
|
||||||
|
margin-bottom: 25rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rating-input {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 20rpx;
|
||||||
|
margin-bottom: 30rpx;
|
||||||
|
|
||||||
|
.star-picker {
|
||||||
|
display: flex;
|
||||||
|
gap: 8rpx;
|
||||||
|
|
||||||
|
.star-item {
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
.star-icon {
|
||||||
|
font-size: 40rpx;
|
||||||
|
color: #faad14;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rating-value {
|
||||||
|
font-size: 36rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #faad14;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.comment-textarea {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 150rpx;
|
||||||
|
padding: 20rpx;
|
||||||
|
border: 1rpx solid #e0e0e0;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.char-count {
|
||||||
|
display: block;
|
||||||
|
text-align: right;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999;
|
||||||
|
margin-top: 10rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 20rpx;
|
||||||
|
margin-top: 30rpx;
|
||||||
|
|
||||||
|
.modal-btn {
|
||||||
|
flex: 1;
|
||||||
|
height: 80rpx;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
font-size: 30rpx;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&.cancel-btn {
|
||||||
|
background: #f0f0f0;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.confirm-btn {
|
||||||
|
background: #1890ff;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
@ -57,18 +57,18 @@
|
|||||||
<text style="margin-right: 8px;">{{ idx + 1 }}、{{ xm.xcMc }}</text>
|
<text style="margin-right: 8px;">{{ idx + 1 }}、{{ xm.xcMc }}</text>
|
||||||
<view style="display: flex; align-items: center;">
|
<view style="display: flex; align-items: center;">
|
||||||
<u-icon
|
<u-icon
|
||||||
:name="xm.xcJg === 'B' ? 'checkmark-circle-fill' : 'close-circle-fill'"
|
:name="xm.xcJg === 'A' ? 'checkmark-circle-fill' : 'close-circle-fill'"
|
||||||
:color="xm.xcJg === 'B' ? '#67c23a' : '#f56c6c'"
|
:color="xm.xcJg === 'A' ? '#67c23a' : '#f56c6c'"
|
||||||
size="18"
|
size="18"
|
||||||
></u-icon>
|
></u-icon>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view style="font-size: 12px; color: #666;">
|
<view style="font-size: 12px; color: #666;">
|
||||||
<text v-if="xm.xcJg === 'A'" style="color: #f56c6c;">
|
<text v-if="xm.xcJg === 'A'" style="color: #67c23a;">
|
||||||
扣分:-{{ xm.xmFz }}分
|
优点
|
||||||
</text>
|
</text>
|
||||||
<text v-else style="color: #67c23a;">
|
<text v-else style="color: #f56c6c;">
|
||||||
不扣分
|
缺点
|
||||||
</text>
|
</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@ -55,18 +55,18 @@
|
|||||||
<text style="margin-right: 8px;">{{ idx + 1 }}、{{ xm.xcMc }}</text>
|
<text style="margin-right: 8px;">{{ idx + 1 }}、{{ xm.xcMc }}</text>
|
||||||
<view style="display: flex; align-items: center;">
|
<view style="display: flex; align-items: center;">
|
||||||
<u-icon
|
<u-icon
|
||||||
:name="xm.xcJg === 'B' ? 'checkmark-circle-fill' : 'close-circle-fill'"
|
:name="xm.xcJg === 'A' ? 'checkmark-circle-fill' : 'close-circle-fill'"
|
||||||
:color="xm.xcJg === 'B' ? '#67c23a' : '#f56c6c'"
|
:color="xm.xcJg === 'A' ? '#67c23a' : '#f56c6c'"
|
||||||
size="18"
|
size="18"
|
||||||
></u-icon>
|
></u-icon>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view style="font-size: 12px; color: #666;">
|
<view style="font-size: 12px; color: #666;">
|
||||||
<text v-if="xm.xcJg === 'A'" style="color: #f56c6c;">
|
<text v-if="xm.xcJg === 'A'" style="color: #67c23a;">
|
||||||
扣分:-{{ xm.xmFz }}分
|
优点
|
||||||
</text>
|
</text>
|
||||||
<text v-else style="color: #67c23a;">
|
<text v-else style="color: #f56c6c;">
|
||||||
不扣分
|
缺点
|
||||||
</text>
|
</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@ -85,7 +85,7 @@ import { useDataStore } from "@/store/modules/data";
|
|||||||
import { getPbPageApi } from "@/api/base/pbApi";
|
import { getPbPageApi } from "@/api/base/pbApi";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
const { getJs } = useUserStore();
|
const { getJs, getUser } = useUserStore();
|
||||||
const { getData, setData } = useDataStore();
|
const { getData, setData } = useDataStore();
|
||||||
|
|
||||||
// 排班列表数据
|
// 排班列表数据
|
||||||
@ -118,7 +118,7 @@ const loadPbList = async (isRefresh = false) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const params = {
|
const params: any = {
|
||||||
page: pagination.page,
|
page: pagination.page,
|
||||||
rows: pagination.pageSize,
|
rows: pagination.pageSize,
|
||||||
xclx: 'C', // 巡查类型:C-值周巡查
|
xclx: 'C', // 巡查类型:C-值周巡查
|
||||||
@ -128,6 +128,30 @@ const loadPbList = async (isRefresh = false) => {
|
|||||||
// xqId: '', // 学期ID
|
// xqId: '', // 学期ID
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 权限判断:如果不是 admin 用户,则传递当前教师ID进行过滤
|
||||||
|
const user = getUser;
|
||||||
|
const js = getJs;
|
||||||
|
const empCode = user?.empCode || '';
|
||||||
|
|
||||||
|
// 调试日志
|
||||||
|
console.log('=== 值周巡查权限判断 ===');
|
||||||
|
console.log('getUser:', user);
|
||||||
|
console.log('getUser.empCode:', user?.empCode);
|
||||||
|
console.log('empCode:', empCode);
|
||||||
|
console.log('getJs:', js);
|
||||||
|
console.log('getJs.id:', js?.id);
|
||||||
|
|
||||||
|
if (empCode === 'admin') {
|
||||||
|
// admin 用户:不传任何过滤参数,可以查看所有排班
|
||||||
|
console.log('权限:admin 用户,查看所有排班');
|
||||||
|
} else {
|
||||||
|
// 非 admin 用户:传递 dqjsId(使用 FIND_IN_SET 精确匹配),只能查看自己参与的排班
|
||||||
|
params.dqjsId = js?.id; // 当前教师ID
|
||||||
|
console.log('权限:普通用户,过滤条件 dqjsId =', params.dqjsId);
|
||||||
|
}
|
||||||
|
console.log('最终请求参数:', params);
|
||||||
|
console.log('=========================');
|
||||||
|
|
||||||
const res: any = await getPbPageApi(params);
|
const res: any = await getPbPageApi(params);
|
||||||
|
|
||||||
// 根据实际API响应结构判断成功条件
|
// 根据实际API响应结构判断成功条件
|
||||||
|
|||||||
@ -53,6 +53,20 @@
|
|||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view v-if="canInspect">
|
<view v-if="canInspect">
|
||||||
|
<!-- 巡查年级班级 -->
|
||||||
|
<view class="section mx-15 mb-15">
|
||||||
|
<view class="section-title-bar">
|
||||||
|
<view class="decorator"></view>
|
||||||
|
<text class="title-text">巡查年级班级</text>
|
||||||
|
</view>
|
||||||
|
<view class="class-select-card bg-white r-md p-15">
|
||||||
|
<view class="class-selector" @click="showClassTree">
|
||||||
|
<text :class="{ placeholder: !selectedClassText }">{{ selectedClassText || "请选择年级班级" }}</text>
|
||||||
|
<uni-icons type="right" size="16" color="#999"></uni-icons>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
<!-- 巡查项目 -->
|
<!-- 巡查项目 -->
|
||||||
<view class="section mx-15 mb-15">
|
<view class="section mx-15 mb-15">
|
||||||
<view class="section-title-bar">
|
<view class="section-title-bar">
|
||||||
@ -160,7 +174,7 @@
|
|||||||
:disabled="isSubmitting"
|
:disabled="isSubmitting"
|
||||||
@click="submit"
|
@click="submit"
|
||||||
>
|
>
|
||||||
{{ isSubmitting ? (xcRecordId ? '更新中...' : '提交中...') : (xcRecordId ? '更新巡查' : '提交巡查') }}
|
{{ isSubmitting ? '提交中...' : '提交巡查' }}
|
||||||
</button>
|
</button>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
@ -198,6 +212,19 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<!-- 班级选择树 -->
|
||||||
|
<BasicTree
|
||||||
|
ref="treeRef"
|
||||||
|
:range="treeData"
|
||||||
|
idKey="key"
|
||||||
|
rangeKey="title"
|
||||||
|
title="选择年级班级"
|
||||||
|
:multiple="false"
|
||||||
|
:selectParent="false"
|
||||||
|
@confirm="onTreeConfirm"
|
||||||
|
@cancel="onTreeCancel"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@ -206,6 +233,8 @@ import { zbXcSaveApi } from "@/api/base/zbXcApi";
|
|||||||
import { attachmentUpload } from "@/api/system/upload";
|
import { attachmentUpload } from "@/api/system/upload";
|
||||||
import BasicLayout from "@/components/BasicLayout/Layout.vue";
|
import BasicLayout from "@/components/BasicLayout/Layout.vue";
|
||||||
import { ImageVideoUpload } from "@/components/ImageVideoUpload";
|
import { ImageVideoUpload } from "@/components/ImageVideoUpload";
|
||||||
|
import BasicTree from '@/components/BasicTree/Tree.vue';
|
||||||
|
import { findAllNjBjTree } from '@/api/base/server';
|
||||||
import { useDataStore } from "@/store/modules/data";
|
import { useDataStore } from "@/store/modules/data";
|
||||||
import { useUserStore } from "@/store/modules/user";
|
import { useUserStore } from "@/store/modules/user";
|
||||||
import { computed, onMounted, ref } from "vue";
|
import { computed, onMounted, ref } from "vue";
|
||||||
@ -233,8 +262,19 @@ const todayInfo = ref({
|
|||||||
// 巡查项目
|
// 巡查项目
|
||||||
const checkItems = ref<any[]>([]);
|
const checkItems = ref<any[]>([]);
|
||||||
|
|
||||||
// 巡查记录ID(用于更新)
|
// 年级班级选择相关
|
||||||
const xcRecordId = ref('');
|
const treeData = ref<any[]>([]);
|
||||||
|
const treeRef = ref();
|
||||||
|
const curNj = ref<any>(null);
|
||||||
|
const curBj = ref<any>(null);
|
||||||
|
|
||||||
|
// 计算属性:显示选中的班级文本
|
||||||
|
const selectedClassText = computed(() => {
|
||||||
|
if (curBj.value && curNj.value) {
|
||||||
|
return `${curNj.value.title} ${curBj.value.title}`;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
});
|
||||||
|
|
||||||
// 导入类型和配置
|
// 导入类型和配置
|
||||||
import { type ImageItem, type VideoItem, COMPRESS_PRESETS } from '@/components/ImageVideoUpload'
|
import { type ImageItem, type VideoItem, COMPRESS_PRESETS } from '@/components/ImageVideoUpload'
|
||||||
@ -265,6 +305,60 @@ const formatTime = (timestamp: string) => {
|
|||||||
return dayjs(timestamp).format('MM-DD');
|
return dayjs(timestamp).format('MM-DD');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 加载树形数据
|
||||||
|
const loadTreeData = async () => {
|
||||||
|
try {
|
||||||
|
const res = await findAllNjBjTree();
|
||||||
|
if (res.resultCode === 1 && res.result) {
|
||||||
|
// 递归转换数据格式以适配 BasicTree 组件
|
||||||
|
const convertTreeData = (items: any[]): any[] => {
|
||||||
|
return items.map((item: any) => ({
|
||||||
|
key: item.key,
|
||||||
|
title: item.title,
|
||||||
|
njmcId: item.njmcId,
|
||||||
|
children: item.children ? convertTreeData(item.children) : [],
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
treeData.value = convertTreeData(res.result);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
uni.showToast({ title: "加载班级数据失败", icon: "error" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 显示班级选择树
|
||||||
|
const showClassTree = () => {
|
||||||
|
if (treeRef.value) {
|
||||||
|
treeRef.value._show();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 树形选择确认
|
||||||
|
const onTreeConfirm = (selectedItems: any[]) => {
|
||||||
|
if (selectedItems.length > 0) {
|
||||||
|
const selectedItem = selectedItems[0]; // 单选模式,取第一个
|
||||||
|
|
||||||
|
// 如果选择的是班级(有parents表示是班级)
|
||||||
|
if (selectedItem.parents && selectedItem.parents.length > 0) {
|
||||||
|
const parent = selectedItem.parents[0]; // 年级信息
|
||||||
|
const nj = parent; // 年级信息
|
||||||
|
const bj = selectedItem; // 班级信息
|
||||||
|
|
||||||
|
curNj.value = nj;
|
||||||
|
curBj.value = bj;
|
||||||
|
} else {
|
||||||
|
// 如果选择的是年级,清空班级选择
|
||||||
|
curNj.value = selectedItem;
|
||||||
|
curBj.value = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 树形选择取消
|
||||||
|
const onTreeCancel = () => {
|
||||||
|
// 取消选择,不做任何操作
|
||||||
|
};
|
||||||
|
|
||||||
// 加载巡查项目
|
// 加载巡查项目
|
||||||
const loadCheckItems = async () => {
|
const loadCheckItems = async () => {
|
||||||
try {
|
try {
|
||||||
@ -289,11 +383,6 @@ const loadCheckItems = async () => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
console.log('巡查项目列表:', checkItems.value);
|
console.log('巡查项目列表:', checkItems.value);
|
||||||
|
|
||||||
// 如果有巡查记录,回显数据
|
|
||||||
if (zb.value.xcRecord) {
|
|
||||||
restoreXcRecord(zb.value.xcRecord);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
checkItems.value = [];
|
checkItems.value = [];
|
||||||
}
|
}
|
||||||
@ -303,85 +392,6 @@ const loadCheckItems = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 回显巡查记录数据
|
|
||||||
const restoreXcRecord = (xcRecord: any) => {
|
|
||||||
try {
|
|
||||||
console.log('========== 开始回显巡查记录 ==========');
|
|
||||||
console.log('巡查记录ID:', xcRecord.id);
|
|
||||||
console.log('巡查记录 zbXcXmList:', xcRecord.zbXcXmList);
|
|
||||||
console.log('当前巡查项目列表:', checkItems.value.map(item => ({
|
|
||||||
id: item.id,
|
|
||||||
xcMc: item.xcMc
|
|
||||||
})));
|
|
||||||
|
|
||||||
// 保存巡查记录ID
|
|
||||||
xcRecordId.value = xcRecord.id || '';
|
|
||||||
console.log('保存的巡查记录ID:', xcRecordId.value);
|
|
||||||
|
|
||||||
// 回显巡查项目数据
|
|
||||||
if (xcRecord.zbXcXmList && xcRecord.zbXcXmList.length > 0) {
|
|
||||||
console.log('开始回显', xcRecord.zbXcXmList.length, '个巡查项目');
|
|
||||||
|
|
||||||
let matchedCount = 0;
|
|
||||||
xcRecord.zbXcXmList.forEach((xcXm: any, index: number) => {
|
|
||||||
console.log(`\n[${index + 1}] 尝试匹配项目:`);
|
|
||||||
console.log(' xcXm.id:', xcXm.id);
|
|
||||||
console.log(' xcXm.xcXmId:', xcXm.xcXmId);
|
|
||||||
console.log(' xcXm.xcMc:', xcXm.xcMc);
|
|
||||||
console.log(' xcXm.xcJg:', xcXm.xcJg);
|
|
||||||
console.log(' xcXm.xcPj:', xcXm.xcPj);
|
|
||||||
|
|
||||||
const checkItem = checkItems.value.find(item => item.id === xcXm.xcXmId);
|
|
||||||
if (checkItem) {
|
|
||||||
console.log(' ✓ 找到匹配项目:', checkItem.xcMc);
|
|
||||||
checkItem.xcJg = xcXm.xcJg || '';
|
|
||||||
checkItem.xcPj = xcXm.xcPj || '';
|
|
||||||
checkItem.xcXmRecordId = xcXm.id || ''; // 保存巡查项目记录ID
|
|
||||||
matchedCount++;
|
|
||||||
console.log(' 设置后 - xcJg:', checkItem.xcJg, ', xcPj:', checkItem.xcPj);
|
|
||||||
} else {
|
|
||||||
console.log(' ✗ 未找到匹配项目!');
|
|
||||||
console.log(' 可用的项目ID:', checkItems.value.map(item => item.id).join(', '));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log(`\n成功匹配 ${matchedCount}/${xcRecord.zbXcXmList.length} 个巡查项目`);
|
|
||||||
} else {
|
|
||||||
console.log('⚠️ 没有巡查项目记录需要回显(zbXcXmList 为空或不存在)');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 回显图片
|
|
||||||
if (xcRecord.zp) {
|
|
||||||
const imageUrls = xcRecord.zp.split(',').map((url: string) => url.trim()).filter((url: string) => url);
|
|
||||||
imageList.value = imageUrls.map((url: string) => ({
|
|
||||||
url: url,
|
|
||||||
path: imagUrl(url),
|
|
||||||
uploaded: true
|
|
||||||
}));
|
|
||||||
console.log('✓ 回显图片:', imageList.value.length, '张');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 回显视频
|
|
||||||
if (xcRecord.sp) {
|
|
||||||
const videoUrls = xcRecord.sp.split(',').map((url: string) => url.trim()).filter((url: string) => url);
|
|
||||||
videoList.value = videoUrls.map((url: string) => ({
|
|
||||||
url: url,
|
|
||||||
path: imagUrl(url),
|
|
||||||
uploaded: true
|
|
||||||
}));
|
|
||||||
console.log('✓ 回显视频:', videoList.value.length, '个');
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('\n回显完成,最终巡查项目状态:');
|
|
||||||
checkItems.value.forEach((item, index) => {
|
|
||||||
console.log(` [${index + 1}] ${item.xcMc} - xcJg: ${item.xcJg || '未选择'}, xcPj: ${item.xcPj || '无'}`);
|
|
||||||
});
|
|
||||||
console.log('========== 回显完成 ==========\n');
|
|
||||||
} catch (error) {
|
|
||||||
console.error('❌ 回显巡查记录失败:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onCheckItemChange = (e: any, item: any) => {
|
const onCheckItemChange = (e: any, item: any) => {
|
||||||
const value = e.detail.value; // 'A' 或 'B'
|
const value = e.detail.value; // 'A' 或 'B'
|
||||||
const label = value === 'A' ? '优点' : '缺点';
|
const label = value === 'A' ? '优点' : '缺点';
|
||||||
@ -506,6 +516,16 @@ const submit = async () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 验证是否选择了年级班级
|
||||||
|
if (!curBj.value || !curNj.value) {
|
||||||
|
uni.showToast({
|
||||||
|
title: "请选择巡查的年级班级",
|
||||||
|
icon: "none",
|
||||||
|
duration: 2000,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 验证巡查项目是否已选择
|
// 验证巡查项目是否已选择
|
||||||
const hasCheckedItems = checkItems.value.some(item => item.xcJg && item.xcPj);
|
const hasCheckedItems = checkItems.value.some(item => item.xcJg && item.xcPj);
|
||||||
if (!hasCheckedItems) {
|
if (!hasCheckedItems) {
|
||||||
@ -524,20 +544,13 @@ const submit = async () => {
|
|||||||
const zbXcXmList = checkItems.value
|
const zbXcXmList = checkItems.value
|
||||||
.filter((item: any) => item.xcJg && item.xcPj)
|
.filter((item: any) => item.xcJg && item.xcPj)
|
||||||
.map((item: any) => {
|
.map((item: any) => {
|
||||||
const newItem = {
|
return {
|
||||||
...item,
|
|
||||||
xcXmId: item.id,
|
xcXmId: item.id,
|
||||||
xcJg: item.xcJg, // A-优点,B-缺点
|
xcJg: item.xcJg, // A-优点,B-缺点
|
||||||
xcPj: item.xcPj, // 评价内容
|
xcPj: item.xcPj, // 评价内容
|
||||||
zbXcId: xcRecordId.value || "", // 巡查记录ID(更新时使用)
|
xmFz: item.xmFz, // 项目分值
|
||||||
|
xcMc: item.xcMc, // 巡查名称
|
||||||
};
|
};
|
||||||
// 如果是更新操作且有巡查项目记录ID,保留该ID
|
|
||||||
if (xcRecordId.value && item.xcXmRecordId) {
|
|
||||||
newItem.id = item.xcXmRecordId;
|
|
||||||
} else {
|
|
||||||
newItem.id = "";
|
|
||||||
}
|
|
||||||
return newItem;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const submitData: any = {
|
const submitData: any = {
|
||||||
@ -548,21 +561,20 @@ const submit = async () => {
|
|||||||
xctime: now.format("YYYY-MM-DD HH:mm:ss"),
|
xctime: now.format("YYYY-MM-DD HH:mm:ss"),
|
||||||
zp: getImageUrls(),
|
zp: getImageUrls(),
|
||||||
sp: getVideoUrls(),
|
sp: getVideoUrls(),
|
||||||
|
njId: curNj.value.key, // 年级ID
|
||||||
|
njmcId: curNj.value.njmcId || curNj.value.key, // 年级名称ID
|
||||||
|
bjId: curBj.value.key, // 班级ID
|
||||||
|
bc: selectedClassText.value, // 别称(如:三年级1班)
|
||||||
zbXcXmList: zbXcXmList,
|
zbXcXmList: zbXcXmList,
|
||||||
};
|
};
|
||||||
|
|
||||||
// 如果是更新操作,添加记录ID
|
|
||||||
if (xcRecordId.value) {
|
|
||||||
submitData.id = xcRecordId.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('提交值周巡查数据:', submitData);
|
console.log('提交值周巡查数据:', submitData);
|
||||||
|
|
||||||
const res = await zbXcSaveApi(submitData);
|
const res = await zbXcSaveApi(submitData);
|
||||||
|
|
||||||
if (res && res.resultCode === 1) {
|
if (res && res.resultCode === 1) {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: xcRecordId.value ? "更新成功" : "提交成功",
|
title: "提交成功",
|
||||||
icon: "success",
|
icon: "success",
|
||||||
});
|
});
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@ -575,14 +587,14 @@ const submit = async () => {
|
|||||||
}, 1500);
|
}, 1500);
|
||||||
} else {
|
} else {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: xcRecordId.value ? "更新失败" : "提交失败",
|
title: "提交失败",
|
||||||
icon: "none",
|
icon: "none",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('提交值周巡查失败:', error);
|
console.error('提交值周巡查失败:', error);
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: xcRecordId.value ? "更新失败" : "提交失败",
|
title: "提交失败",
|
||||||
icon: "none",
|
icon: "none",
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
@ -613,6 +625,7 @@ const getVideoUrls = () => {
|
|||||||
// 页面加载时获取状态选项
|
// 页面加载时获取状态选项
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await loadCheckItems();
|
await loadCheckItems();
|
||||||
|
await loadTreeData(); // 加载年级班级树形数据
|
||||||
checkInspectionTime();
|
checkInspectionTime();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@ -819,6 +832,35 @@ onMounted(async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.class-select-card {
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.class-selector {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 12px 15px;
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid #eee;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
text {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333;
|
||||||
|
|
||||||
|
&.placeholder {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.upload-card {
|
.upload-card {
|
||||||
background-color: white;
|
background-color: white;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -79,13 +79,12 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { getZbXcListApi } from "@/api/base/pbApi";
|
import { getZbXcListApi } from "@/api/base/pbApi";
|
||||||
import { zbXcFindPageApi } from "@/api/base/zbXcApi";
|
|
||||||
import { useDataStore } from "@/store/modules/data";
|
import { useDataStore } from "@/store/modules/data";
|
||||||
import { useUserStore } from "@/store/modules/user";
|
import { useUserStore } from "@/store/modules/user";
|
||||||
import { onBeforeUnmount, onMounted, ref } from "vue";
|
import { onBeforeUnmount, onMounted, ref } from "vue";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
const { getJs } = useUserStore();
|
const { getJs, getUser } = useUserStore();
|
||||||
const dataStore = useDataStore();
|
const dataStore = useDataStore();
|
||||||
|
|
||||||
// 值周列表数据
|
// 值周列表数据
|
||||||
@ -214,16 +213,34 @@ const loadZbXcList = async (pbData: any) => {
|
|||||||
|
|
||||||
const pbId = pbData.pbId;
|
const pbId = pbData.pbId;
|
||||||
|
|
||||||
|
// 权限判断:如果不是 admin 用户,则传递 dqjsId 进行权限过滤
|
||||||
|
const user = getUser;
|
||||||
|
const empCode = user?.empCode || '';
|
||||||
|
|
||||||
|
// 构建请求参数
|
||||||
|
const apiParams: any = {
|
||||||
|
pbId: pbId
|
||||||
|
};
|
||||||
|
|
||||||
|
// 根据用户类型决定传递哪个参数
|
||||||
|
if (empCode === 'admin') {
|
||||||
|
// admin 用户:传递 jsId
|
||||||
|
apiParams.jsId = '';
|
||||||
|
apiParams.qdjsId = getJs.id;
|
||||||
|
console.log('权限:admin 用户,传递 jsId');
|
||||||
|
} else {
|
||||||
|
// 非 admin 用户:传递 dqjsId 进行权限过滤
|
||||||
|
apiParams.jsId = getJs.id;
|
||||||
|
apiParams.qdjsId = '';
|
||||||
|
console.log('权限:普通用户,传递 dqjsId 进行权限过滤');
|
||||||
|
}
|
||||||
|
|
||||||
console.log('API调用参数:', {
|
console.log('API调用参数:', {
|
||||||
jsId: getJs.id,
|
...apiParams,
|
||||||
pbId: pbId,
|
|
||||||
pbData: pbData
|
pbData: pbData
|
||||||
});
|
});
|
||||||
|
|
||||||
const res = await getZbXcListApi({
|
const res = await getZbXcListApi(apiParams);
|
||||||
jsId: getJs.id,
|
|
||||||
pbId: pbId
|
|
||||||
});
|
|
||||||
|
|
||||||
if (res && res.resultCode == 1) {
|
if (res && res.resultCode == 1) {
|
||||||
const list = res.result || [];
|
const list = res.result || [];
|
||||||
@ -295,8 +312,8 @@ const refreshZbList = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 跳转到巡查
|
// 跳转到巡查(纯新增模式,不查询历史记录)
|
||||||
const goXc = async (zb: any) => {
|
const goXc = (zb: any) => {
|
||||||
const pbData = dataStore.getGlobal;
|
const pbData = dataStore.getGlobal;
|
||||||
|
|
||||||
// 检查排班数据是否有效
|
// 检查排班数据是否有效
|
||||||
@ -318,68 +335,6 @@ const goXc = async (zb: any) => {
|
|||||||
xqmc: pbData.xqmc
|
xqmc: pbData.xqmc
|
||||||
};
|
};
|
||||||
|
|
||||||
// 如果已巡查,查询巡查记录数据
|
|
||||||
if (zb.sfxc === '是') {
|
|
||||||
try {
|
|
||||||
uni.showLoading({
|
|
||||||
title: '加载中...'
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log('========== 查询已巡查记录 ==========');
|
|
||||||
console.log('查询参数 pbZbId:', zb.id);
|
|
||||||
|
|
||||||
const res = await zbXcFindPageApi({
|
|
||||||
rows: 10,
|
|
||||||
page: 1,
|
|
||||||
pbZbId: zb.id
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log('查询结果完整数据:', res);
|
|
||||||
console.log('查询结果 resultCode:', res?.resultCode);
|
|
||||||
console.log('查询结果 result 数量:', res?.result?.length);
|
|
||||||
console.log('查询结果 rows:', res?.rows);
|
|
||||||
console.log('查询结果 rows 数量:', res?.rows?.length);
|
|
||||||
|
|
||||||
uni.hideLoading();
|
|
||||||
|
|
||||||
// 尝试从不同的字段获取数据
|
|
||||||
let xcRecordList = res?.result || res?.rows || [];
|
|
||||||
|
|
||||||
if (res && (res.resultCode === 1 || res.resultCode === '1' || xcRecordList.length > 0)) {
|
|
||||||
if (xcRecordList.length > 0) {
|
|
||||||
// 获取最新的一条巡查记录
|
|
||||||
const xcRecord = xcRecordList[0];
|
|
||||||
combinedData.xcRecord = xcRecord;
|
|
||||||
console.log('✓ 获取到巡查记录ID:', xcRecord.id);
|
|
||||||
console.log(' - jsxm:', xcRecord.jsxm);
|
|
||||||
console.log(' - xctime:', xcRecord.xctime);
|
|
||||||
console.log(' - zbXcXmList 数量:', xcRecord.zbXcXmList?.length || 0);
|
|
||||||
if (xcRecord.zbXcXmList && xcRecord.zbXcXmList.length > 0) {
|
|
||||||
console.log(' - zbXcXmList 详情:', xcRecord.zbXcXmList.map((xm: any) => ({
|
|
||||||
xcXmId: xm.xcXmId,
|
|
||||||
xcMc: xm.xcMc,
|
|
||||||
xcJg: xm.xcJg,
|
|
||||||
xcPj: xm.xcPj
|
|
||||||
})));
|
|
||||||
} else {
|
|
||||||
console.log(' ⚠️ zbXcXmList 为空!');
|
|
||||||
}
|
|
||||||
console.log('========== 查询完成 ==========');
|
|
||||||
} else {
|
|
||||||
console.log('❌ 返回数据为空');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.log('❌ 未查询到巡查记录或查询失败');
|
|
||||||
console.log(' 返回数据结构:', Object.keys(res || {}));
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
uni.hideLoading();
|
|
||||||
console.error('查询巡查记录失败:', error);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.log('该值周记录尚未巡查,无需回显');
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('点击巡查,传递数据:', combinedData);
|
console.log('点击巡查,传递数据:', combinedData);
|
||||||
|
|
||||||
dataStore.setData(combinedData);
|
dataStore.setData(combinedData);
|
||||||
|
|||||||
@ -63,18 +63,18 @@
|
|||||||
<text style="margin-right: 8px;">{{ idx + 1 }}、{{ xm.xcMc }}</text>
|
<text style="margin-right: 8px;">{{ idx + 1 }}、{{ xm.xcMc }}</text>
|
||||||
<view style="display: flex; align-items: center;">
|
<view style="display: flex; align-items: center;">
|
||||||
<u-icon
|
<u-icon
|
||||||
:name="xm.xcJg === 'B' ? 'checkmark-circle-fill' : 'close-circle-fill'"
|
:name="xm.xcJg === 'A' ? 'checkmark-circle-fill' : 'close-circle-fill'"
|
||||||
:color="xm.xcJg === 'B' ? '#67c23a' : '#f56c6c'"
|
:color="xm.xcJg === 'A' ? '#67c23a' : '#f56c6c'"
|
||||||
size="18"
|
size="18"
|
||||||
></u-icon>
|
></u-icon>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view style="font-size: 12px; color: #666;">
|
<view style="font-size: 12px; color: #666;">
|
||||||
<text v-if="xm.xcJg === 'A'" style="color: #f56c6c;">
|
<text v-if="xm.xcJg === 'A'" style="color: #67c23a;">
|
||||||
扣分:-{{ xm.xmFz }}分
|
优点
|
||||||
</text>
|
</text>
|
||||||
<text v-else style="color: #67c23a;">
|
<text v-else style="color: #f56c6c;">
|
||||||
不扣分
|
缺点
|
||||||
</text>
|
</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
BIN
src/static/base/view/zyyl.png
Normal file
BIN
src/static/base/view/zyyl.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.9 KiB |
Loading…
x
Reference in New Issue
Block a user