JavaScript Tip之:用&&和||来模拟if-else

本文介绍如何利用逻辑运算符&&和||在JavaScript中实现类似于if-else的逻辑判断,通过实例演示其原理及应用。

1. &&和||来模拟if-else:

var returnStr = aBoo&&aStr||bBoo&&bStr||cBoo&&cStr||dBoo&&dStr

等同于

var returnStr = “”;
if(aBoo){
  returnStr = aStr;
}else if(bBoo){
  returnStr = bStr;
}else if(cBoo){
  returnStr = cStr;
}else if(dBoo){
  returnStr = dStr;
}

原理:

true&&"abc" --> "abc";

!!"abc" --> true;

false&&"abc" --> false;

<template> <div class="side-project"> <div v-for="item in projectType" :key="item.key" @click="changeProjectType(item.key)" :class="{ 'project-type': true, 'active': assignRole === item.key }" > {{ item.name }} </div> <div class="project-list-wrap" v-permission="['task:manage:list:current:aggregation']"> <div class="project-filter"> <el-popover width="410" trigger="click" :visible="filterVisible"> <template #reference> <div class="filter-title" @click="filterVisible = !filterVisible"> 项目 <i class="iconfont srt-icon-filter"/> </div> </template> <div class="filter-content"> <div class="filter-item"> <div>项目时间</div> <el-date-picker v-model="filterParams.projectTime" type="daterange" range-separator="至" start-placeholder="开始时间" end-placeholder="结束时间" style="width: 364px" :teleported="false" value-format="YYYY-MM-DD HH:mm:ss" /> </div> <div class="filter-item"> <div>项目状态</div> <div class="stage-list"> <div v-for="item in projectStageList" :key="item.value" @click="changeProjectStage(item.value)" :class="{ 'stage-item': true, 'active': filterParams.projectStage.includes(item.value) }" > {{ item.label }} </div> </div> </div> <p @click="saveFilterStatus" class="save-filter">保存筛选设置</p> </div> </el-popover> <span class="tip">我的任务进度</span> </div> <!-- 添加滚动容器,并绑定滚动事件 --> <div class="project-list-container" ref="projectListContainer" @scroll="handleScroll"> <div class="project-list" v-if="projectList.length"> <div v-for="item in projectList" :key="item.projectId" @click="changeActiveProject(item.projectId)" :class="{ 'project-item': true, 'active': projectIdList.includes(item.projectId) }" > <div class="project-item-top"> <span>{{ item.projectName }}</span> <span>{{ item.finishedTaskCount }}/{{ item.totalTaskCount }}</span> </div> <div class="project-item-content text-overflow"> {{ item.projectStatus || '' }} </div> </div> <!-- 加载提示 --> <div v-if="loading" class="loading-tip">加载中...</div> <div v-if="!hasMore && projectList.length > 0" class="no-more-tip">没有更多数据了</div> </div> <!-- 空状态 --> <div v-else-if="!loading" class="empty-tip">暂无数据~</div> </div> </div> </div> </template> <script setup> import { ref, computed, watch, onMounted, nextTick } from 'vue'; import { useUserStore } from '@/store/user'; const userStore = useUserStore(); const emit = defineEmits(['refresh-list']); const permissionKeys = computed(() => { return userStore.userInfo?.permissionKeys || [] }) // 负责/参 const assignRole = ref(1); // 项目列表选中项 const projectIdList = ref([]); const projectType = computed(() => { return [ permissionKeys.value.includes('task:manage:list:current:responsibility') ? { key: 1, name: '我负责的' } : '', permissionKeys.value.includes('task:manage:list:current:join') ? { key: 2, name: '我参的' } : '' ].filter(i => !!i) }); const projectStageList = ref([ { label: '全部', value: '' }, { label: '立项中', value: '3' }, { label: '待执行', value: '0' }, { label: '执行中', value: '1' }, { label: '已结束', value: '2' }, { label: '已归档', value: '4' }, ]) const filterParams = ref({ projectTime: [], projectStage: ['0', '1', '2'], }) const projectList = ref([]); const filterVisible = ref(false); const projectListContainer = ref(null); const loading = ref(false); // 加载状态 const hasMore = ref(true); // 是否有更多数据 const currentPage = ref(1); // 当前页码 const pageSize = ref(1); // 每页条数,从10改为1 // 项目类型变更 const changeProjectType = (type) => { assignRole.value = type; } // 项目状态筛选 const changeProjectStage = (stage) => { filterParams.value.projectStage = filterParams.value.projectStage.includes(stage) ? filterParams.value.projectStage.filter(item => item !== stage) : [...filterParams.value.projectStage, stage]; } // 项目选中 const changeActiveProject = (id) => { projectIdList.value = projectIdList.value.includes(id) ? projectIdList.value.filter(item => item !== id) : [...projectIdList.value, id]; } // 保存筛选条件 const saveFilterStatus = () => { filterVisible.value = false; projectIdList.value = []; resetScrollLoad(); // 重置滑动加载状态 } // 重置滑动加载状态 const resetScrollLoad = () => { projectList.value = []; currentPage.value = 1; hasMore.value = true; nextTick(() => { if (projectListContainer.value) { projectListContainer.value.scrollTop = 0; } getMyTaskProjects(); // 重新加载第一页数据 }); } // 滚动事件处理 - 优化触发条件 const handleScroll = () => { if (!projectListContainer.value || loading.value || !hasMore.value) { return; } const container = projectListContainer.value; const { scrollTop, scrollHeight, clientHeight } = container; // 优化触发条件:当滚动到底部20px内时,加载更多 if (scrollHeight - scrollTop - clientHeight < 20) { loadMore(); } }; // 加载更多数据 const loadMore = () => { if (!hasMore.value || loading.value) return; currentPage.value++; getMyTaskProjects(true); // true表示加载更多 }; function getMyTaskProjects(isLoadMore = false) { if (loading.value) return; loading.value = true; G_transmit('getMyTaskProjects', { queries: { // assignRole: assignRole.value, pageIndex: currentPage.value, pageSize: pageSize.value, startTime: filterParams.value?.projectTime?.[0] || '', endTime: filterParams.value?.projectTime?.[1] || '', projectStatusList: filterParams.value?.projectStage?.join(',') || '', } }).then((res) => { const newData = res.data.rows || []; if (isLoadMore) { projectList.value = [...projectList.value, ...newData]; } else { projectList.value = newData; } hasMore.value = projectList.value.length < res.data.total; }).catch((err) => { console.log(err); // 加载失败时回滚页码 if (isLoadMore) { currentPage.value--; } }).finally(() => { loading.value = false; }); } // 监听项目类型变化 watch(() => projectType.value, () => { assignRole.value = projectType.value[0]?.key || 1; }, { immediate: true, deep: true }) // 监听筛选条件变化 watch(() => [assignRole.value, projectIdList.value], () => { emit('refresh-list', assignRole.value, projectIdList.value); },{immediate: true, deep: true}) onMounted(() => { getMyTaskProjects(); }) </script> <style lang="scss" scoped> .side-project { width: 280px; padding: 16px 0; background-color: #ffffff; border-radius: 6px; } .project-type { height: 28px; line-height: 28px; padding: 10px; color: rgba(0, 0, 0, 0.88); font-weight: 600; cursor: pointer; &:hover, &.active { background-color: rgba(22, 119, 255, 0.06); color: #1677ff; } } .project-list-wrap { padding: 10px; } .project-filter { display: flex; justify-content: space-between; align-items: center; border-top: 1px solid rgba(0, 0, 0, 0.06); padding-top: 16px; .filter-title { font-size: 14px; font-weight: 600; color: rgba(0, 0, 0, 0.88); & i { cursor: pointer; } } .tip { font-size: 12px; font-weight: 400; color: rgba(0, 0, 0, 0.45); } } .filter-content { .filter-item { margin-bottom: 16px; &>div:first-child { margin-bottom: 8px; } } .stage-list { display: flex; align-items: center; border: 1px solid rgba(0, 0, 0, 0.06); padding: 4px; .stage-item { height: 24px; line-height: 24px; padding: 0 10px; border-radius: 4px; font-size: 12px; font-weight: 400; color: rgba(0, 0, 0, 0.88); margin-right: 8px; cursor: pointer; &:hover, &.active { background-color: rgba(22, 119, 255, 0.06); color: #1677ff; } } } .save-filter { font-size: 12px; color: #007CFE; cursor: pointer; text-align: right; } } .project-list-container { max-height: 49px; overflow-y: auto; margin-top: 10px; } .project-list { .project-item { padding: 10px; margin-top: 10px; background-color: #f5f7fa; border-radius: 6px; color: rgba(0, 0, 0, 0.88); cursor: pointer; height: 50px; box-sizing: border-box; .project-item-top { display: flex; justify-content: space-between; align-items: center; } .project-item-content { margin-top: 5px; } &:hover, &.active { background-color: rgba(22, 119, 255, 0.06); color: #1677ff; } .text-overflow { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } } } // 加载提示样式 .loading-tip, .no-more-tip, .empty-tip { margin-top: 4px; text-align: center; padding: 20px; color: #999; font-size: 12px; } // 第一条项目没有上边距 .project-list .project-item:first-child { margin-top: 0; } </style>帮我整改一下,实现滚动分页加载projectList的数据,我先用的一条来测试
11-01
D:\tools\python\python.exe D:\历史项目留存2\诺褀2025\python加工浦发模型模拟\py搭建\pythonProject1\爬虫抖音视频\测试01.py ================================================== 小说下载器 - 蚂蚁文学优化版 ================================================== 请输入小说起始URL: https://www.mayiwsk.com/127_127460/53169900.html 请输入输出文件名(默认: novel.txt): 01.txt 2025-09-01 09:51:28,043 - INFO - 开始下载小说,起始URL: https://www.mayiwsk.com/127_127460/53169900.html 2025-09-01 09:51:28,043 - INFO - 基础URL: https://www.mayiwsk.com 2025-09-01 09:51:28,044 - INFO - 正在下载第 1 章: https://www.mayiwsk.com/127_127460/53169900.html 2025-09-01 09:51:31,802 - INFO - 检测到编码: utf-8 2025-09-01 09:51:31,810 - INFO - 使用选择器: ('div', {'id': 'content'}) 找到内容容器 2025-09-01 09:51:31,812 - ERROR - 过滤导航元素'大'时出错: 'NavigableString' object has no attribute 'parent' 2025-09-01 09:51:31,815 - INFO - 通过JavaScript变量找到下一页: https://www.mayiwsk.com/127_127460/53172991.html 2025-09-01 09:51:31,815 - INFO - 成功写入第 1 章内容 (48 字符) 2025-09-01 09:51:31,815 - INFO - 正在下载第 2 章: https://www.mayiwsk.com/127_127460/53172991.html 2025-09-01 09:51:33,791 - INFO - 检测到编码: utf-8 2025-09-01 09:51:33,799 - INFO - 使用选择器: ('div', {'id': 'content'}) 找到内容容器 2025-09-01 09:51:33,802 - ERROR - 过滤导航元素'大'时出错: 'NavigableString' object has no attribute 'parent' 2025-09-01 09:51:33,802 - INFO - 通过JavaScript变量找到下一页: https://www.mayiwsk.com/127_127460/53188336.html 2025-09-01 09:51:33,802 - INFO - 成功写入第 2 章内容 (47 字符) 2025-09-01 09:51:33,802 - INFO - 正在下载第 3 章: https://www.mayiwsk.com/127_127460/53188336.html 2025-09-01 09:51:36,094 - INFO - 检测到编码: utf-8 2025-09-01 09:51:36,104 - INFO - 使用选择器: ('div', {'id': 'content'}) 找到内容容器 2025-09-01 09:51:36,106 - ERROR - 过滤导航元素'大'时出错: 'NavigableString' object has no attribute 'parent' 2025-09-01 09:51:36,107 - INFO - 通过JavaScript变量找到下一页: https://www.mayiwsk.com/127_127460/53188337.html 2025-09-01 09:51:36,107 - INFO - 成功写入第 3 章内容 (40 字符) 2025-09-01 09:51:36,107 - INFO - 正在下载第 4 章: https://www.mayiwsk.com/127_127460/53188337.html 2025-09-01 09:51:39,880 - INFO - 检测到编码: utf-8 2025-09-01 09:51:39,888 - INFO - 使用选择器: ('div', {'id': 'content'}) 找到内容容器 2025-09-01 09:51:39,890 - ERROR - 过滤导航元素'大'时出错: 'NavigableString' object has no attribute 'parent' 2025-09-01 09:51:39,891 - INFO - 通过JavaScript变量找到下一页: https://www.mayiwsk.com/127_127460/53198526.html 2025-09-01 09:51:39,891 - INFO - 成功写入第 4 章内容 (40 字符) 2025-09-01 09:51:39,891 - INFO - 正在下载第 5 章: https://www.mayiwsk.com/127_127460/53198526.html 用户中断,程序退出 Process finished with exit code 0
09-02
<script lang="ts" setup> import { ref, onMounted } from 'vue' import Navigater from '@/components/Navigation/Navigater.vue' import {getTaskDetail} from '@/pages/taskDetail/api/index' import type { UploadFile } from 'element-plus' import { Edit,Delete, Download, Plus, ZoomIn} from '@element-plus/icons-vue' import type { FormInstance, FormRules } from 'element-plus' const route = useRoute() // 正确获取当前 route 实例 const id = ref() const taskDetail = ref()// 任务详情 const partyUserInfo = ref() const checked1 = ref(false) const addTxt1 = ref('') const container = ref(null) let scaleValue = 1 const getData = async (id) => { const res = await getTaskDetail({id:id}) taskDetail.value = res.data partyUserInfo.value = res.data.partyUserInfo console.log(partyUserInfo.value.avatar)//输出结果https://cdn.shenyantuling.com/prod/image/head/shutiao1.png } // 图片上传 const dialogImageUrl = ref('') const dialogVisible = ref(false) const disabled = ref(false) const handleRemove = (file: UploadFile) => { console.log(file) } const handlePictureCardPreview = (file: UploadFile) => { dialogImageUrl.value = file.url! dialogVisible.value = true } const handleDownload = (file: UploadFile) => { console.log(file) } // 表单校验 const ruleFormRef = ref<FormInstance>() const ruleForm = reactive({ // .... }) const handleResize = () => { const containerWidth = container.value.offsetWidth const containerHeight = container.value.offsetHeight const windowWidth = window.innerWidth const windowHeight = window.innerHeight // 计算缩放比例,保持内容完整显示 scaleValue = Math.min( windowWidth / containerWidth, windowHeight / containerHeight ) container.value.style.transform = `scale(${scaleValue})` container.value.style.transformOrigin = 'top left' } onMounted(() => { handleResize() window.addEventListener('resize', handleResize) id.value = route.query.id getData(id.value) }) onBeforeUnmount(() => { window.removeEventListener('resize', handleResize) }) </script> <template> <div class="scale-container" ref="container"> <div class="content"> <!-- 顶部 logo 导航栏 --> <Navigater class="top"></Navigater> <!-- 任务详情 --> <div class="wrap"> <div class="topBack"> <div class="return"> <img src="@/assets/navigater/return.png" alt=""> </div> <div class="title">任务详情</div> </div> <div class="taskBox"> <div class="taskLeft"> <!-- 发布者 --> <div class="publisherBox"> <div class="avatar"> <img :src=partyUserInfo.avatar alt=""> </div> <div class="info"> <div class="infoTop"> <div class="name">avatar</div> <img src="@/assets/navigater/task1.png" alt=""> <img src="@/assets/navigater/task1.png" alt=""> </div> <div class="dic"> The First Onchain Treasury Network for Web 3The First Onchain Treasury Network for Web 3The First Onchain Treasury Network for Web 3 </div> </div> </div> <!-- 任务 --> <div class="taskDic"> <div class="taskImg"> <img src="@/assets/navigater/task1.png" alt=""> </div> <div class="taskText"> <div class="taskTitle">任务标题:关注微博账号赠送3000积分积分赠送</div> <div class="botBox"> <div class="type">任务类型</div> <div class="copy"> <img src="@/assets/navigater/task2.png" alt=""> </div> </div> </div> </div> <div class="tip"> <img src="@/assets/navigater/task1.png" alt=""> <div class="tipTxt">Joinly 将在发放奖励前仔细检查,已提交的任务</div> </div> <div class="taskMain"> <div class="mainTop"> <img src="@/assets/navigater/task2.png" alt=""> <div class="mainTxt">关注 crazy crap在微博的账号,账号名称crazy crap</div> </div> <div class="main"> <el-form ref="ruleFormRef" style="max-width: 600px" :model="ruleForm" status-icon label-width="auto" class="demo-ruleForm" > <!-- :rules="rules" --> <el-card class="step"> <div class="stepTop"> <div class="stepTit"> <el-checkbox v-model="checked1" size="large" /> <div class="stepTxt">关闭微博关闭微博关闭微博关闭微博关闭微博关闭微博关闭微博关闭微博关闭微博关闭微博</div> </div> <el-button class="stepBtn" color="#715EFF" type="primary" :icon="Edit" disabled>保存修改</el-button> </div> <div class="stepMain"> <div class="title">添加图片</div> <el-upload action="#" list-type="picture-card" :auto-upload="false" :limit=3> <el-icon><Plus /></el-icon> <template #file="{ file }"> <div> <img class="el-upload-list__item-thumbnail" :src="file.url" alt="" /> <span class="el-upload-list__item-actions"> <span class="el-upload-list__item-preview" @click="handlePictureCardPreview(file)" > <el-icon><zoom-in /></el-icon> </span> <span v-if="!disabled" class="el-upload-list__item-delete" @click="handleRemove(file)" > <el-icon><Delete /></el-icon> </span> </span> </div> </template> </el-upload> <el-dialog v-model="dialogVisible"> <img w-full :src="dialogImageUrl" alt="Preview Image" /> </el-dialog> <div class="title">添加文字</div> <el-input class="addTxt" type="textarea" v-model="addTxt1" placeholder="请输入文字" /> </div> </el-card> <el-card class="step"> <div class="stepTop"> <div class="stepTit"> <el-checkbox v-model="checked1" size="large" /> <div class="stepTxt">关闭微博关闭微博关闭微博关闭微博关闭微博关闭微博关闭微博关闭微博关闭微博关闭微博</div> </div> <el-button class="stepBtn" color="#715EFF" type="primary" :icon="Edit" disabled>保存修改</el-button> </div> <div class="stepMain"> <div class="title">添加图片</div> <el-upload action="#" list-type="picture-card" :auto-upload="false" :limit=3> <el-icon><Plus /></el-icon> <template #file="{ file }"> <div> <img class="el-upload-list__item-thumbnail" :src="file.url" alt="" /> <span class="el-upload-list__item-actions"> <span class="el-upload-list__item-preview" @click="handlePictureCardPreview(file)" > <el-icon><zoom-in /></el-icon> </span> <span v-if="!disabled" class="el-upload-list__item-delete" @click="handleRemove(file)" > <el-icon><Delete /></el-icon> </span> </span> </div> </template> </el-upload> <el-dialog v-model="dialogVisible"> <img w-full :src="dialogImageUrl" alt="Preview Image" /> </el-dialog> <div class="title">添加文字</div> <el-input class="addTxt" type="textarea" v-model="addTxt1" placeholder="请输入文字" /> </div> </el-card> <el-card class="step"> <div class="stepTop"> <div class="stepTit"> <el-checkbox v-model="checked1" size="large" /> <div class="stepTxt">关闭微博关闭微博关闭微博关闭微博关闭微博关闭微博关闭微博关闭微博关闭微博关闭微博</div> </div> <el-button class="stepBtn" color="#715EFF" type="primary" :icon="Edit" disabled>保存修改</el-button> </div> <div class="stepMain"> <div class="title">添加图片</div> <el-upload action="#" list-type="picture-card" :auto-upload="false" :limit=3> <el-icon><Plus /></el-icon> <template #file="{ file }"> <div> <img class="el-upload-list__item-thumbnail" :src="file.url" alt="" /> <span class="el-upload-list__item-actions"> <span class="el-upload-list__item-preview" @click="handlePictureCardPreview(file)" > <el-icon><zoom-in /></el-icon> </span> <span v-if="!disabled" class="el-upload-list__item-delete" @click="handleRemove(file)" > <el-icon><Delete /></el-icon> </span> </span> </div> </template> </el-upload> <el-dialog v-model="dialogVisible"> <img w-full :src="dialogImageUrl" alt="Preview Image" /> </el-dialog> <div class="title">添加文字</div> <el-input class="addTxt" type="textarea" v-model="addTxt1" placeholder="请输入文字" /> </div> </el-card> </el-form> </div> </div> </div> <div class="taskRight"> </div> </div> </div> </div> </div> </template> <style scoped lang="scss"> .scale-container { width: 1920px; /* 设计稿宽度 */ position: absolute; display: block; top: 0; left: 0; .content { width: 100%; height: 100%; background-color: #000; display: block; .wrap{ width: 1552px; margin: 0 auto; .topBack{ display: flex; width: 1552px; height: 74px; justify-content: left; align-items: center; border-bottom: 1px solid #444; margin-bottom: 31px; .return{ width: 24px; height: 24px; margin-right: 10px; img{ width: 100%; height: 100%; } } .title{ font-family: PingFang SC; font-weight: 600; font-style: Semibold; font-size: 20px; color:#fff; line-height: 100%; letter-spacing: -0.3px; } } .taskBox{ display: flex; width: 1174px; margin: 0 auto; .taskLeft{ width: 787px; .publisherBox{ width: 787px; height:100px; display: flex; background-color: #232323; border-radius: 12px; padding: 16px; margin-bottom: 30px; .avatar{ width: 48px; height: 48px; margin-right: 16px; img{ width: 100%; height: 100%; border-radius: 50%; } } .info{ .infoTop{ display: flex; justify-content: left; align-items: center; .name{ font-family: PingFang SC; font-weight: 600; font-style: Semibold; font-size: 20px; color:#fff; line-height: 100%; letter-spacing: -0.3px; } img{ width: 16px; height: 16px; border-radius: 50%; margin-left: 8px; } } .dic{ width: 536px; margin-top: 14px; font-family: PingFang SC; font-weight: 400; font-style: Regular; font-size: 14px; line-height: 21px; letter-spacing: -0.3px; color:#A6A6A6; display: -webkit-box; /* 关键属性:弹性伸缩盒子 */ -webkit-box-orient: vertical; /* 排列方向:垂直 */ -webkit-line-clamp: 2; /* 限制行数:2行 */ overflow: hidden; /* 溢出隐藏 */ text-overflow: ellipsis; /* 溢出显示省略号 */ word-break: break-word; /* 长单词换行处理 */ } } } .taskDic{ width: 786px; height: 150px; top: 346px; left: 373px; display: flex; padding-top: 32px; border-top: 1px solid #444; .taskImg{ img{ width: 160px; height: 120PX; border-radius: 12px; } } .taskText{ color: #fff; margin-left: 23px; .taskTitle{ width: 546px; height: 80px; font-family: PingFang SC; font-weight: 600; font-style: Semibold; font-size: 26px; letter-spacing: -0.3px; // display: -webkit-box; /* 关键属性:弹性伸缩盒子 */ // -webkit-box-orient: vertical; /* 排列方向:垂直 */ // -webkit-line-clamp: 2; /* 限制行数:2行 */ // overflow: hidden; /* 溢出隐藏 */ // text-overflow: ellipsis; /* 溢出显示省略号 */ // word-break: break-word; /* 长单词换行处理 */ } .botBox{ display: flex; justify-content: space-between; align-items: center; width: 603px; .type{ width: 64px; height: 30px; text-align: center; font-family: PingFang SC; font-weight: 600; font-style: Semibold; font-size: 12px; line-height: 30px; letter-spacing: -0.3px; border-radius: 4px; background-color: #232323; } .copy{ width: 30px; height: 30px; img{ width: 100%; height: 100%; } } } } } .tip{ display: flex; justify-content: left; align-items: center; margin: 32px auto; img{ width: 16px; height: 16px; border-radius: 50%; margin-right: 10px; } .tipTxt{ color:#fff;font-family: PingFang SC; font-weight: 400; font-style: Regular; font-size: 14px; line-height: 100%; letter-spacing: -0.3px; } } .taskMain{ width: 787px; padding: 24px; border-radius: 12px; background-color: #232323; .mainTop{ display: flex; justify-content: left; align-items: center; margin-bottom: 23px; img{ width: 24px; height: 24px; border-radius: 4px; margin-right: 10px; } .mainTxt{ font-family: PingFang SC; font-weight: 600; font-style: Semibold; font-size: 18px; color:#fff; line-height: 100%; letter-spacing: -0.3px; } } .main{ .step{ width: 739px; background-color: #121212; border: 1px solid #121212; .stepTop{ width: 715px; display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid #444; .stepTit{ display: flex; justify-content: space-between; align-items: center; .stepTxt{ font-family: PingFang SC; font-weight: 400; font-style: Regular; font-size: 14px; color:#A6A6A6; line-height: 100%; letter-spacing: -0.3px; margin-left: 10px; } } .stepBtn{ margin-right: 10px; margin-bottom: 3px; } } .stepMain{ width: 667px; margin: 0 auto; .title{ font-family: PingFang SC; font-weight: 400; font-style: Regular; font-size: 12px; color:#fff; letter-spacing: -0.3px; margin: 10px 0; } .addTxt{ &:deep(.el-input){ display: block; width: 667px; height: 80px !important; background-color: #363636 !important; border: none; border-radius: 8px; } } } } } } } .taskRight{ width: 339px; margin-left: 48px; } } } } } </style> 中console.log(partyUserInfo.value.avatar)的输出结果为https://cdn.shenyantuling.com/prod/image/head/shutiao1.png。 但是 报错显示:taskDetail.vue:98 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'avatar') at Proxy._sfc_render (taskDetail.vue:98:53) 即<img :src=partyUserInfo.avatar alt="">中找不到avatar
07-29
<!-- 修复后的 videoUpload.vue --> <template> <view class="video-upload-container"> <view v-if="permission === 'E' && !readonly" class="video-upload-area"> <view v-if="!videoUrl" class="upload-placeholder" @click="chooseVideo"> <text class="upload-icon">+</text> <text class="upload-text">点击上传视频</text> </view> <view v-else class="video-preview"> <video :src="getVideoUrl()" :controls="true" :autoplay="false" :show-fullscreen-btn="true" :show-play-btn="true" class="video-player" object-fit="cover" @loadstart="() => console.log('开始加载视频')" @loadeddata="() => console.log('视频数据加载完成')"> </video> <button v-if="!disabled" class="remove-btn" @click="removeVideo">删除视频</button> </view> <!-- <view v-if="isShowTip" class="upload-tip"> <text class="tip-text">支持格式: {{ fileType.join(', ') }} | 大小不超过 {{ fileSize }}MB</text> </view> --> </view> <view v-else class="readonly-video"> <video v-if="videoUrl" :src="getVideoUrl()" :controls="true" :autoplay="false" :show-fullscreen-btn="true" :show-play-btn="true" class="video-player" object-fit="cover" @loadstart="() => console.log('开始加载视频')" @loadeddata="() => console.log('视频数据加载完成')" > </video> <view v-else class="no-video"> <text>未上传视频</text> </view> </view> </view> </template> <script> import config from '../../../common/config'; export default { name: "VideoUpload", props: { field: { type: String, required: true }, videoUrl: { type: String, default: "" }, permission: { type: String, default: "R" }, readonly: { type: Boolean, default: false }, disabled: { type: Boolean, default: false }, uploadFileUrl: { type: String, default: "/base/base/file/upload" }, fileSize: { type: Number, default: 50 }, fileType: { type: Array, default: () => ['video/mp4', 'video/avi', 'video/mov', 'video/wmv', 'video/flv', 'video/mkv'] }, isShowTip: { type: Boolean, default: true }, baseURL: { type: String, default: '' } }, methods: { getVideoUrl() { const url = this.videoUrl; // 如果没有URL,返回空字符串 if (!url) return ''; // 如果URL包含http,则直接返回 if (url.includes('http')) { // 如果URL包含/hvs,则替换为https://oss.0898hq.com/hvs if (url.includes('/hvs')) { return config.imageUrl + url.substring(url.indexOf('/hvs')); } return url; } else { // 修改URL前缀为 https://oss.0898hq.com if (url.startsWith('/hvs')) { return config.imageUrl + url; } // 确保基础URL末尾没有斜杠,路径开头有斜杠 const base = this.baseURL || config.baseUrl; if (url.startsWith('/')) { return base.replace(/\/$/, '') + url; } else { return base.replace(/\/$/, '') + '/' + url; } } }, // 修改 chooseVideo 方法 chooseVideo() { if (this.disabled) return; uni.chooseVideo({ count: 1, sourceType: ['album', 'camera'], compressed: true, maxDuration: 60, success: (res) => { console.log('视频信息', res); // 检查文件格式 const tempFilePath = res.tempFilePath; let fileName = res.name; // 如果没有文件名,从路径中提取 if (!fileName) { fileName = tempFilePath.split('/').pop(); } // 转换为小写进行比较 const fileExt = fileName.split('.').pop().toLowerCase(); console.log('文件扩展名:', fileExt); const supportedFormats = ['mp4', 'mov', 'avi', 'wmv', 'flv', 'mkv']; if (!supportedFormats.includes(fileExt)) { uni.showToast({ title: '不支持的视频格式', icon: 'none' }); return; } this.uploadVideo(res.tempFilePath); }, fail: (err) => { console.error('选择视频失败:', err); uni.showToast({ title: '选择视频失败', icon: 'none' }); } }); }, // 修改 uploadVideo 方法,使其更接近 imageUpload 的处理方式 async uploadVideo(filePath) { try { uni.showLoading({ title: '上传中...' }); // 使用 Promise 包装 uni.uploadFile 以便使用 async/await const uploadResult = await new Promise((resolve, reject) => { uni.uploadFile({ url: config.baseUrl + this.uploadFileUrl, filePath: filePath, name: 'files[0]', formData: { dir: 'video' }, success: (res) => resolve(JSON.parse(res.data)), fail: (err) => reject(err) }); }); if (uploadResult.code === 0 && uploadResult.data && uploadResult.data.length > 0) { const videoInfo = uploadResult.data[0]; const urls = videoInfo.url.split(','); // 取第一个URL并处理 let url = urls[0]; // 处理URL前缀, getImageUrl 保持一致 if (url.includes('/hvs')) { url = config.imageUrl + url.substring(url.indexOf('/hvs')); } else if (url.startsWith('/hvs')) { url = config.imageUrl + url; } else { const base = this.baseURL || config.baseUrl; if (url.startsWith('/')) { url = base.replace(/\/$/, '') + url; } else { url = base.replace(/\/$/, '') + '/' + url; } } // 验证并设置视频URL this.validateAndSetVideoUrl(url); } else { throw new Error(uploadResult.msg || '上传失败'); } } catch (error) { console.error('上传失败:', error); uni.showToast({ title: error.message || '上传失败', icon: 'none' }); } finally { uni.hideLoading(); } }, // 验证并设置视频URL validateAndSetVideoUrl(url) { console.log('视频地址:', url); if (!url) { uni.showToast({ title: '视频地址无效', icon: 'none' }); return; } // 检查是否以http/https开头 if (url.startsWith('http') || url.startsWith('https')) { this.$emit('update:videoUrl', url); uni.showToast({ title: '上传成功', icon: 'success' }); } else { uni.showToast({ title: '视频地址格式错误', icon: 'none' }); } }, // 处理视频错误 handleVideoError(e) { console.error('视频加载错误:', e); uni.showToast({ title: '视频加载失败,请检查网络或视频格式', icon: 'none', duration: 3000 }); }, removeVideo() { this.$emit('update:videoUrl', ''); } } } </script> <style scoped> .video-upload-container { width: 100%; } .upload-placeholder { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 200rpx; border: 2rpx dashed #ccc; border-radius: 10rpx; background-color: #f9f9f9; } .upload-icon { font-size: 60rpx; color: #999; } .upload-text { margin-top: 10rpx; color: #999; font-size: 28rpx; } .video-player { width: 100%; height: 300rpx; border-radius: 10rpx; } .video-preview { } .remove-btn { margin-top: 20rpx; background-color: #ff4d4f; color: white; border: none; border-radius: 8rpx; padding: 10rpx 20rpx; font-size: 26rpx; } .upload-tip { margin-top: 15rpx; } .tip-text { color: #999; font-size: 24rpx; } .readonly-video .video-player { width: 100%; height: 300rpx; border-radius: 10rpx; overflow: hidden; } .no-video { padding: 40rpx; text-align: center; color: #999; background-color: #f5f5f5; border-radius: 10rpx; } </style>的.uni-video-cover在手机预览的时候是固定在原地的。但是工具预览时是正常随页面滚动的
11-17
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值