前言
🤟 找工作,来万码优才:👉 #小程序://万码优才/r6rqmzDaXpYkJZF
多张照片的Demo比较丰富
单张照片代码迭代过程中忘记书写,索性给一版Demo
1. 单张照片
基于Vue的xx页面,用户需要拍取xx的特定照片并提交审核
重点实现单张照片的提交功能
模块功能 | 描述 |
---|---|
页面模板 | 定义页面结构,包括拍照字段、照片预览、提交按钮等元素 |
样式与布局 | 定义页面组件的样式和布局风格 |
功能逻辑 | 包括拍照、预览、删除、提交等功能的实现 |
状态提示 | 包括审核结果展示、消息提醒等状态展示 |
我的CSS抽离的代码如下:
<style scoped>
/* 让整个页面更整齐 */
.photo-section {
padding: 10px;
}
/* 让每个上传项的宽度一致 */
.photo-item {
display: flex;
flex-direction: column; /* 纵向排列 */
background: #f9f9f9;
padding: 10px;
border-radius: 10px;
margin-bottom: 12px;
width: 100%;
justify-content: space-between;
}
/* 审核结果 */
.audit-result {
padding: 10px;
background: #fef3c7;
color: #b45309;
border-radius: 5px;
margin: 10px 0;
word-break: break-word; /* 让长单词换行 */
white-space: pre-wrap; /* 保持空格并换行 */
}
/* 文字 + 按钮 + 示意图 在同一行 */
.photo-label {
display: flex;
align-items: center;
width: 100%;
justify-content: space-between;
}
.photo-text {
font-size: 16px;
font-weight: 500;
}
/* “查看示意图” 按钮 */
.guide-btn {
background-color: #007AFF;
color: white;
font-size: 14px;
padding: 4px 12px;
border-radius: 5px;
border: none;
}
/* 示意图(固定展示) */
.guide-image {
width: 80px;
height: 80px;
border-radius: 5px;
border: 1px solid #ddd;
margin-left: 10px;
}
/* 照片区域 */
.photo-container {
display: flex;
align-items: center;
gap: 10px;
}
/* 预览照片 */
.photo-preview {
width: 80px;
height: 80px;
border-radius: 5px;
border: 1px solid #ccc;
}
/* 时间文本 */
.photo-time {
font-size: 12px;
color: gray;
}
/* 删除按钮 */
.delete-btn {
color: red;
border: none;
background: none;
font-size: 14px;
margin-left: 5px;
}
/* 拍照按钮 */
.take-photo-btn {
background-color: #28A745;
color: white;
font-size: 14px;
padding: 6px 12px;
border-radius: 5px;
border: none;
margin-left: 10px;
}
/* 提交按钮 */
.submit-btn {
width: 100%;
margin-top: 15px;
}
</style>
整体Demo如下:(页面Demo)
页面整体模版如下:
<template>
<view>
<uni-card title="提柜操作" type="line">
<view class="info">xx: {{ cntr }}</view>
<!-- 单张照片字段 -->
<view class="photo-item">
<view class="photo-label">
<text>侧面照片</text>
<image
src="/static/images/img_cntr_l.png"
class="guide-image"
@click="previewGuideImage"
/>
</view>
<!-- 照片预览 -->
<view v-if="photoUrl" class="photo-container">
<image :src="photoUrl" class="photo-preview" @click="previewImage" />
<button class="delete-btn" @click="delImage">删除</button>
</view>
<!-- 拍摄按钮 -->
<button v-else class="take-photo-btn" @click="takePhoto">拍照</button>
</view>
<button type="primary" :disabled="!canSubmit" @click="submit">提交</button>
</uni-card>
</view>
</template>
css如下:
<style>
/* 信息显示样式 */
.info {
margin-bottom: 15px;
color: #333;
font-size: 14px;
}
/* 照片拍摄和预览样式 */
.photo-label {
display: flex;
justify-content: space-between;
margin-bottom: 10px;
}
.guide-image {
width: 30px;
height: 30px;
margin-left: 10px;
}
.photo-container {
position: relative;
margin-bottom: 15px;
}
.photo-preview {
width: 100%;
height: 150px;
border-radius: 5px;
}
.delete-btn {
position: absolute;
right: 5px;
top: 5px;
background-color: rgba(0, 0, 0, 0.5);
color: white;
padding: 3px 8px;
border-radius: 3px;
}
.take-photo-btn {
width: 100%;
padding: 10px;
background-color: #007bff;
color: white;
border: none;
border-radius: 5px;
}
/* 状态提示样式 */
.audit-result {
padding: 10px;
margin-bottom: 15px;
background-color: #f8f9fa;
border-radius: 5px;
}
</style>
整体逻辑:
<script>
export default {
data() {
return {
cntr: "CSCU1234567", // 编号
photoUrl: null, // 照片URL存储
canSubmit: false, // 提交按钮状态
auditResult: '', // 审核结果
checkStatus: 0 // 审核状态(0:未审核, 1:已审核)
};
},
computed: {
// 确定提交按钮是否可用
canSubmit() {
return this.photoUrl !== null;
}
},
methods: {
// 拍摄照片
takePhoto() {
const that = this;
uni.chooseImage({
count: 1, // 仅允许选择一张照片
success: function (res) {
that.photoUrl = res.tempFiles[0].url;
that.canSubmit = true;
}
});
},
// 预览图片
previewImage() {
uni.previewImage({
current: this.photoUrl,
urls: [this.photoUrl]
});
},
// 删除图片
delImage() {
this.photoUrl = null;
this.canSubmit = false;
},
// 提交照片
submit() {
// 模拟照片提交逻辑
this.checkStatus = 1;
this.auditResult = '审核通过';
// 此处可以添加真实提交逻辑
setTimeout(() => {
this.showMessage('提交成功,等待审核...');
}, 500);
},
// 预览示意图
previewGuideImage() {
uni.previewImage({
urls: ['/static/images/img_cntr_l.png']
});
},
// 显示消息
showMessage(msg) {
uni.showToast({
title: msg,
icon: 'none'
});
}
}
}
</script>
最后可叠加效果:
<view v-if="checkStatus === 1" class="audit-result">
<text>审核结果: {{ auditResult }}</text>
</view>
2. 多张照片
初始界面如下:
代码如下:(缺点就是 图片太多的话会拥挤在一起)
- takePhoto 方法:限制最多 6 张照片,并在上传过多时给出提示。
- viewPhoto 方法:点击已拍摄的照片时,调用 uni.previewImage 展示大图。
- submit 方法:确保至少拍摄 2 张照片后才能提交,否则会给出提示
<template>
<view>
<uni-card title="换柜操作" type="line">
<view class="info">箱号: {{ cntr }}</view>
<view class="photo-section">
<text>请拍摄照片,最多可提交6张</text>
<view class="photo-list">
<view v-for="(photo, index) in photoList" :key="index" class="photo-item" @click="viewPhoto(index)">
<image :src="photo" class="photo-preview" />
</view>
</view>
<button @click="takePhoto" :disabled="photoList.length >= 6">拍照</button>
<text class="photo-count">当前已拍摄 {{ photoList.length }} 张照片,最多可提交 6 张。</text>
</view>
<button type="primary" :disabled="photoList.length < 1" @click="submit">提交</button>
</uni-card>
</view>
</template>
<script>
export default {
data() {
return {
cntr: '',
photoList: []
};
},
onLoad(options) {
this.cntr = options.cntr || '';
},
methods: {
takePhoto() {
uni.chooseImage({
count: 1,
sourceType: ['camera'],
success: (res) => {
if (this.photoList.length < 6) {
this.photoList.push(res.tempFilePaths[0]);
} else {
uni.showToast({ title: '最多只能上传 6 张照片', icon: 'none' });
}
}
});
},
submit() {
if (this.photoList.length < 2) {
uni.showToast({ title: '请拍摄至少 2 张照片', icon: 'none' });
return;
}
uni.showToast({ title: '提交成功', icon: 'success' });
setTimeout(() => {
uni.navigateBack();
}, 1000);
},
viewPhoto(index) {
uni.previewImage({
current: index, // 当前查看的图片索引
urls: this.photoList // 所有图片的数组
});
}
}
};
</script>
<style scoped>
.photo-section {
padding: 10px;
}
.photo-list {
display: flex;
gap: 10px;
margin: 10px 0;
}
.photo-item {
width: 100px;
height: 100px;
background: #f0f0f0;
overflow: hidden;
border-radius: 5px;
}
.photo-preview {
width: 100%;
height: 100%;
object-fit: cover;
}
.photo-count {
margin-top: 10px;
color: #999;
}
</style>
界面的UI继续优化:
<template>
<view>
<uni-card title="换柜操作" type="line">
<view class="info">箱号: {{ cntr }}</view>
<view class="photo-section">
<text>请拍摄照片,最多可提交6张</text>
<view class="photo-list">
<view v-for="(photo, index) in photoList" :key="index" class="photo-item" @click="viewPhoto(index)">
<image :src="photo" class="photo-preview" />
</view>
</view>
<button @click="takePhoto" :disabled="photoList.length >= 6">拍照</button>
<text class="photo-count">当前已拍摄 {{ photoList.length }} 张照片,最多可提交 6 张。</text>
</view>
<button type="primary" :disabled="photoList.length < 1" @click="submit">提交</button>
</uni-card>
</view>
</template>
<script>
export default {
data() {
return {
cntr: '',
photoList: []
};
},
onLoad(options) {
this.cntr = options.cntr || '';
},
methods: {
takePhoto() {
uni.chooseImage({
count: 1,
sourceType: ['camera'],
success: (res) => {
if (this.photoList.length < 6) {
this.photoList.push(res.tempFilePaths[0]);
} else {
uni.showToast({ title: '最多只能上传 6 张照片', icon: 'none' });
}
}
});
},
submit() {
if (this.photoList.length < 2) {
uni.showToast({ title: '请拍摄至少 2 张照片', icon: 'none' });
return;
}
uni.showToast({ title: '提交成功', icon: 'success' });
setTimeout(() => {
uni.navigateBack();
}, 1000);
},
viewPhoto(index) {
uni.previewImage({
current: index, // 当前查看的图片索引
urls: this.photoList // 所有图片的数组
});
}
}
};
</script>
<style scoped>
.photo-section {
padding: 10px;
}
.photo-list {
display: flex;
flex-wrap: wrap; /* 使图片能自动换行 */
gap: 10px;
margin: 10px 0;
}
.photo-item {
width: calc(33.33% - 10px); /* 每行最多显示 3 张 */
height: 100px;
background: #f0f0f0;
overflow: hidden;
border-radius: 5px;
}
.photo-preview {
width: 100%;
height: 100%;
object-fit: cover;
}
.photo-count {
margin-top: 10px;
color: #999;
}
</style>
后续在此基础上扩充增加删除
由于图片比较多,需要增加一个删除,可以在图片右上角增加一个绝对路径的删除标志
<template>
<view>
<uni-card title="换柜操作" type="line">
<view class="info">箱号: {{ cntr }}</view>
<view class="photo-section">
<text>请拍摄照片,最多可提交6张</text>
<view class="photo-list">
<view v-for="(photo, index) in photoList" :key="index" class="photo-item" @click="viewPhoto(index)">
<image :src="photo" class="photo-preview" />
<!-- 删除按钮 -->
<button class="delete-btn" @click.stop="deletePhoto(index)">x</button>
</view>
</view>
<button @click="takePhoto" :disabled="photoList.length >= 6">拍照</button>
<text class="photo-count">当前已拍摄 {{ photoList.length }} 张照片,最多可提交 6 张。</text>
</view>
<button type="primary" :disabled="photoList.length < 1" @click="submit">提交</button>
</uni-card>
</view>
</template>
<script>
export default {
data() {
return {
cntr: '',
photoList: []
};
},
onLoad(options) {
this.cntr = options.cntr || '';
},
methods: {
takePhoto() {
uni.chooseImage({
count: 1,
sourceType: ['camera'],
success: (res) => {
if (this.photoList.length < 6) {
this.photoList.push(res.tempFilePaths[0]);
} else {
uni.showToast({ title: '最多只能上传 6 张照片', icon: 'none' });
}
}
});
},
submit() {
if (this.photoList.length < 2) {
uni.showToast({ title: '请拍摄至少 2 张照片', icon: 'none' });
return;
}
uni.showToast({ title: '提交成功', icon: 'success' });
setTimeout(() => {
uni.navigateBack();
}, 1000);
},
viewPhoto(index) {
uni.previewImage({
current: index, // 当前查看的图片索引
urls: this.photoList // 所有图片的数组
});
},
// 删除照片
deletePhoto(index) {
this.photoList.splice(index, 1);
}
}
};
</script>
<style scoped>
.photo-section {
padding: 10px;
}
.photo-list {
display: flex;
flex-wrap: wrap; /* 使图片能自动换行 */
gap: 10px;
margin: 10px 0;
}
.photo-item {
width: calc(33.33% - 10px); /* 每行最多显示 3 张 */
height: 100px;
background: #f0f0f0;
overflow: hidden;
border-radius: 5px;
position: relative; /* 为删除按钮提供定位参考 */
}
.photo-preview {
width: 100%;
height: 100%;
object-fit: cover;
}
.delete-btn {
position: absolute;
top: 5px;
right: 5px;
background-color: rgba(0, 0, 0, 0.5);
color: white;
border: none;
border-radius: 50%;
width: 20px;
height: 20px;
font-size: 16px;
text-align: center;
line-height: 20px;
}
.delete-btn:hover {
background-color: rgba(0, 0, 0, 0.7);
}
.photo-count {
margin-top: 10px;
color: #999;
}
</style>
继续扩充一个查看功能
查看对应数据:
由于数值都放在同一个字段中,对应通过JSON进行解析:
通过传输的字段进行获取:
// 已有的数据
const res = await getCabinetSwap(item.cntr);
if (res.code === 0 && res.data) {
const imgPath = res.data.imgPath || []; // 确保 imgPath 存在
const encodedImgPath = encodeURIComponent(JSON.stringify(imgPath));
uni.navigateTo({
url: `/pages/gate/gateCheck/swapList?cntr=${item.cntr}&imgPath=${encodedImgPath}`
});
} else {
uni.showToast({ title: '获取换柜信息失败', icon: 'error' });
}
跳转的页面:
<template>
<view>
<uni-card title="换柜照片查看" type="line">
<view class="info">箱号: {{ cntr }}</view>
<view class="photo-section">
<text>已提交的照片</text>
<view class="photo-list">
<view v-for="(photo, index) in photoList" :key="index" class="photo-item" @click="viewPhoto(index)">
<image :src="photo" class="photo-preview" mode="aspectFill" />
</view>
</view>
<text class="photo-count">共 {{ photoList.length }} 张照片</text>
</view>
</uni-card>
<button type="primary" @click="goBack">返回</button>
</view>
</template>
<script>
export default {
data() {
return {
cntr: '',
photoList: []
};
},
onLoad(options) {
this.cntr = options.cntr || '';
if (options.imgPath) {
try {
this.photoList = JSON.parse(decodeURIComponent(options.imgPath));
} catch (e) {
console.error('解析图片数据失败:', e);
}
}
},
methods: {
viewPhoto(index) {
uni.previewImage({
current: index,
urls: this.photoList
});
},
goBack() {
uni.navigateBack();
}
}
};
</script>
<style scoped>
.photo-section {
padding: 10px;
}
.photo-list {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin: 10px 0;
}
.photo-item {
width: calc(33.33% - 10px);
height: 100px;
background: #f0f0f0;
overflow: hidden;
border-radius: 5px;
}
.photo-preview {
width: 100%;
height: 100%;
object-fit: cover;
}
.photo-count {
margin-top: 10px;
color: #999;
}
</style>