小编目前在做毕业设计,主题为“高考志愿信息交流平台”,面向高中生和大学生,辛苦各位读者大佬朋友们填下问卷,点击链接https://www.wjx.cn/jq/98944127.aspx或扫描二维码、微信小程序码均可,希望各位能提供一些调查数据,先在这里谢过各位了(*^_^*)
本文先介绍了搜索页的开发,包括页面的搭建(搜索框、搜索历史和搜索结果)和搜索逻辑的优化;
再重点介绍了发布页的开发:自定义导航栏的实现,文本输入框的实现,底部操作条图标和按钮的实现,图品上传和删除的实现以及编辑保存草稿的实现。
搜索页可以根据关键字搜索。
pages下新建搜索页search.vue(需要创建同名目录,以后创建页面默认会创建同名目录),并在pages.json中配置搜索页导航栏,如下:
{
"path" : "pages/search/search",
"style" :
{
"app-plus": {
// 导航栏配置
"titleNView": {
// 搜索框配置
"searchInput": {
"align":"center",
"backgroundColor":"#F5F4F2",
"borderRadius":"4px",
"placeholder": "搜索帖子",
"placeholderColor": "#6D6C67"
},
// 按钮设置
"buttons": [
{
"color":"#333333",
"colorPressed":"#FD597C",
"float":"right",
"fontSize":"14px",
"text": "搜索"
}
]
}
}
}
}
从本文开始,提供的代码一般只提供增量部分(包括增加和修改的部分),以减少文章篇幅、优化文章结构。
index.vue中增加监听导航栏搜索框生命周期,如下:
// 监听导航栏搜索框
onNavigationBarSearchInputClicked() {
uni.navigateTo({
url: '../search/search'
})
},
其中,onNavigationBarSearchInputClicked
用于监听原生标题栏搜索输入框点击事件,接口uni.navigateTo(OBJECT)
用于保留当前页面、跳转到应用内的某个页面,即跳转到搜索页。
显示:
可以看到,点击搜索栏,跳转到了搜索页。
现进一步完成搜索历史,search.vue如下:
<template>
<view>
<view class="py-2 font-md px-2">搜索历史view>
<view class="flex flex-wrap">
<view class="border rounded font mx-2 my-1 px-2" hover-class="bg-light" v-for="(item, index) in list" :key="index">{
{item}}view>
view>
view>
template>
<script>
export default {
data() {
return {
list: [
'uni-app实战之社区交友APP',
'uni-app入门教程',
'面试之算法基础系列',
'Python全栈',
'商业数据分析从入门到入职',
'Python数据分析实战',
'Django+Vue开发生鲜电商平台'
]
}
},
methods: {
}
}
script>
<style>
style>
base.css如下:
/* 内外边距 */
.p-2 {
padding: 20rpx;
}
/* flex布局 */
.flex {
/* #ifndef APP-APP-PLUS-NVUE */
display: flex;
/* #endif */
flex-direction: row;
}
.flex-wrap {
flex-wrap: wrap;
}
.flex-column {
flex-direction: column;
}
.align-center {
align-items: center;
}
.justify-between {
justify-content: space-between;
}
.justify-center {
justify-content: center;
}
.flex-1 {
flex: 1;
}
/* 圆角 */
.rounded-circle {
border-radius: 100%;
}
.rounded {
border-radius: 8rpx;
}
/* margin */
.mr-2 {
margin-right: 20rpx;
}
.my-1 {
margin-top: 10rpx;
margin-bottom: 10rpx;
}
.mx-2 {
margin-left: 20rpx;
margin-right: 20rpx;
}
/* padding */
.px-5 {
padding-left: 50rpx;
padding-right: 50rpx;
}
.px-3 {
padding-left: 30rpx;
padding-right: 30rpx;
}
.px-2 {
padding-left: 20rpx;
padding-right: 20rpx;
}
.py-3 {
padding-top: 30rpx;
padding-bottom: 30rpx;
}
.py-2 {
padding-top: 20rpx;
padding-bottom: 20rpx;
}
.pt-7 {
padding-top: 70rpx;
}
/* 边框 */
.border {
border-width: 1rpx;
border-style: solid;
border-color: #DEE2E6;
}
/* 字体 */
.font-lg {
font-size: 40rpx;
}
.font-md {
font-size: 35rpx;
}
.font {
font-size: 30rpx;
}
.font-sm {
font-size: 25rpx;
}
.font-weight-bold {
font-weight: bold;
}
/* 文字颜色 */
.text-white {
color: #FFFFFF;
}
.text-light-muted {
color: #A9A5A0;
}
/* 宽度 */
/* #ifndef APP-PLUS-NVUE */
.w-100 {
width: 100%;
}
/* #endif */
/* scroll-view */
/* #ifndef APP-PLUS-NVUE */
.scroll-row {
width: 100%;
white-space: nowrap;
}
.scroll-row-item {
display: inline-block !important;
}
/* #endif */
/* 背景 */
.bg-light {
background-color: #F8F9FA;
}
可以看到,实现了搜索关键词的显示和点击。
先通过监听获取数据,如下:
<template>
<view>
<template v-if="searchList.length === 0">
<view class="py-2 font-md px-2">搜索历史view>
<view class="flex flex-wrap">
<view class="border rounded font mx-2 my-1 px-2" hover-class="bg-light" v-for="(item, index) in list" :key="index">{
{item}}view>
view>
template>
<template v-else>
<block v-for="(item, index) in searchList" :key="index">
<common-list :item="item" :index="index">common-list>
block>
template>
view>
template>
<script>
// 测试数据
const test_data = [{
username: "Corley",
userpic: "/static/img/userpic/12.jpg",
newstime: "2021-01-24 上午11:30",
isFollow: false,
title: "uni-app入门教程",
titlepic: "/static/img/datapic/42.jpg",
support: {
type: "support", // 顶
support_count: 1,
unsupport_count: 2
},
comment_count: 2,
share_count: 2
},
{
username: "Brittany",
userpic: "/static/img/userpic/16.jpg",
newstime: "2021-01-24 下午14:00",
isFollow: false,
title: "商业数据分析从入门到入职",
support: {
type: "unsupport", // 踩
support_count: 2,
unsupport_count: 3
},
comment_count: 5,
share_count: 1
},
{
username: "Ashley",
userpic: "/static/img/userpic/20.jpg",
newstime: "2021-01-24 下午18:20",
isFollow: true,
title: "uni-app实战之社区交友APP",
titlepic: "/static/img/datapic/30.jpg",
support: {
type: "support",
support_count: 5,
unsupport_count: 1
},
comment_count: 3,
share_count: 0
}
];
import commonList from '@/components/common/common-list.vue';
export default {
data() {
return {
list: [
'uni-app实战之社区交友APP',
'uni-app入门教程',
'面试之算法基础系列',
'Python全栈',
'商业数据分析从入门到入职',
'Python数据分析实战',
'Django+Vue开发生鲜电商平台'
],
searchText: '',
// 搜索结果
searchList: []
}
},
components: {
commonList
},
// 监听导航栏搜索框输入
onNavigationBarSearchInputChanged(e) {
console.log(e);
this.searchText = e.text;
},
// 监听点击导航栏搜索按钮
onNavigationBarButtonTap(e) {
console.log(e);
if (e.index === 0) {
this.searchEvent();
}
},
methods: {
// 搜索事件
searchEvent() {
// 收起键盘
uni.hideKeyboard();
// 请求搜索
setTimeout(()=>{
this.searchList = test_data;
}, 2500)
}
}
}
script>
<style>
style>
其中,onNavigationBarSearchInputChanged()
生命周期用于监听原生标题栏搜索输入框输入内容变化事件,onNavigationBarButtonTap()
用于监听原生标题栏按钮点击事件;
搜索结果使用之前封装的common-list组件实现;
触发搜索事件时,调用接口uni.hideKeyboard()
收起软键盘,同时获取数据。
显示:
可以看到,已经模拟出了搜索的效果。
再添加搜索过程中的加载状态loading,使用的是uni.showLoading()
接口,search.vue如下:
searchEvent() {
// 收起键盘
uni.hideKeyboard();
// 显示loading状态
uni.showLoading({
title: '加载中...',
mask: false
});
// 请求搜索
setTimeout(()=>{
this.searchList = test_data;
// 隐藏loading状态
uni.hideLoading();
}, 2500)
}
显示:
可以看到,模拟出了正在加载的效果。
再实现点击搜索历史进行搜索,search.vue如下:
<view class="flex flex-wrap">
<view class="border rounded font mx-2 my-1 px-2" hover-class="bg-light" v-for="(item, index) in list" :key="index"
@click="clickSearchHistory(item)">{
{item}}view>
view>
// 点击搜索历史
clickSearchHistory(text) {
this.searchText = text;
this.searchEvent();
}
显示:
可以看到,实现了点击搜索历史进行搜索。
创建新页面add-input,index.vue增加发布页入口,通过监听导航按钮点击事件实现,如下:
// 监听导航按钮点击事件
onNavigationBarButtonTap() {
uni.navigateTo({
url: '../add-input/add-input'
})
},
pages.json配置如下:
{
"path" : "pages/add-input/add-input",
"style" :
{
"app-plus": {
"titleNView": false
}
}
}
在components目录下新建uni-ui,用于保存uni-app官方提供的组件,将之前创建的uni-app模板项目hello_uniapp中uni-app提供的官方组件uni-icons
、uni-nav-bar
和uni-status-bar
(位于components目录下)连同目录拷贝到uni-ui下,add-input.vue如下:
<template>
<view>
<uni-nav-bar left-icon="back" title="Community Dating">1uni-nav-bar>
view>
template>
<script>
import uniNavBar from '@/components/uni-ui/uni-nav-bar/uni-nav-bar.vue';
export default {
data() {
return {
}
},
components: {
uniNavBar
},
methods: {
}
}
script>
<style>
style>
可以看到,实现了基本的导航栏展示。
再自定义导航栏标题,如下:
<uni-nav-bar left-icon="back" :statusBar="true" :border="false">
<view class="flex justify-center align-center w-100">
所有人可见<text class="iconfont icon-shezhi">text>
view>
uni-nav-bar>
需要在https://www.iconfont.cn/中选择设置
图标并添加至项目,下载解压后,将iconfont.css更新至common/icon.css中。
可以看到,自定义出了导航栏。
文本输入框使用文本域组件,即textarea,如下:
<template>
<view>
<uni-nav-bar left-icon="back" :statusBar="true" :border="false">
<view class="flex justify-center align-center w-100">
所有人可见<text class="iconfont icon-shezhi">text>
view>
uni-nav-bar>
<textarea v-model="content" placeholder="说一句话吧~" class="uni-textarea px-2" />
view>
template>
<script>
import uniNavBar from '@/components/uni-ui/uni-nav-bar/uni-nav-bar.vue';
export default {
data() {
return {
content: ''
}
},
components: {
uniNavBar
},
methods: {
}
}
script>
<style>
style>
可以看到,输入框定义完成。
底部操作条包括选择分类、添加话题、选择图片和发布按钮等,位于底部。
需要在https://www.iconfont.cn/中菜单
、话题
和图片
等图标,并更新icon.css。
add-input.vue如下:
<view class="fixed-bottom bg-white flex align-center" style="height: 85rpx;">
<view class="iconfont icon-caidan">view>
<view class="iconfont icon-huati">view>
<view class="iconfont icon-tupian">view>
<view class="bg-main text-white ml-auto">发送view>
view>
base.css如下:
/* 内外边距 */
.p-2 {
padding: 20rpx;
}
/* flex布局 */
.flex {
/* #ifndef APP-APP-PLUS-NVUE */
display: flex;
/* #endif */
flex-direction: row;
}
.flex-wrap {
flex-wrap: wrap;
}
.flex-column {
flex-direction: column;
}
.align-center {
align-items: center;
}
.justify-between {
justify-content: space-between;
}
.justify-center {
justify-content: center;
}
.flex-1 {
flex: 1;
}
/* 圆角 */
.rounded-circle {
border-radius: 100%;
}
.rounded {
border-radius: 8rpx;
}
/* margin */
.mr-2 {
margin-right: 20rpx;
}
.my-1 {
margin-top: 10rpx;
margin-bottom: 10rpx;
}
.mx-2 {
margin-left: 20rpx;
margin-right: 20rpx;
}
.ml-auto {
margin-left: auto;
}
/* padding */
.px-5 {
padding-left: 50rpx;
padding-right: 50rpx;
}
.px-3 {
padding-left: 30rpx;
padding-right: 30rpx;
}
.px-2 {
padding-left: 20rpx;
padding-right: 20rpx;
}
.py-3 {
padding-top: 30rpx;
padding-bottom: 30rpx;
}
.py-2 {
padding-top: 20rpx;
padding-bottom: 20rpx;
}
.pt-7 {
padding-top: 70rpx;
}
/* 边框 */
.border {
border-width: 1rpx;
border-style: solid;
border-color: #DEE2E6;
}
/* 字体 */
.font-lg {
font-size: 40rpx;
}
.font-md {
font-size: 35rpx;
}
.font {
font-size: 30rpx;
}
.font-sm {
font-size: 25rpx;
}
.font-weight-bold {
font-weight: bold;
}
/* 文字颜色 */
.text-white {
color: #FFFFFF;
}
.text-light-muted {
color: #A9A5A0;
}
/* 宽度 */
/* #ifndef APP-PLUS-NVUE */
.w-100 {
width: 100%;
}
/* #endif */
/* scroll-view */
/* #ifndef APP-PLUS-NVUE */
.scroll-row {
width: 100%;
white-space: nowrap;
}
.scroll-row-item {
display: inline-block !important;
}
/* #endif */
/* 背景 */
.bg-light {
background-color: #F8F9FA;
}
.bg-white {
background-color: #FFFFFF;
}
/* 定位 */
.position-relative {
position: relative;
}
.position-absolute {
position: absolute;
}
.position-fixed {
position: fixed;
}
/* 定位-固定顶部 */
.fixed-top {
position: fixed;
top: 0;
right: 0;
left: 0;
z-index: 1030;
}
/* 定位-固定底部 */
.fixed-bottom {
position: fixed;
right: 0;
bottom: 0;
left: 0;
z-index: 1030;
}
.top-0 {
top: 0;
}
.left-0 {
left: 0;
}
.right-0 {
right: 0;
}
.bottom-0 {
bottom: 0;
}
底部已有大概的轮廓。
进一步完善尺寸和布局,如下:
<template>
<view>
<uni-nav-bar left-icon="back" :statusBar="true" :border="false">
<view class="flex justify-center align-center w-100">
所有人可见<text class="iconfont icon-shezhi">text>
view>
uni-nav-bar>
<textarea v-model="content" placeholder="说一句话吧~" class="uni-textarea px-2" />
<view class="fixed-bottom bg-white flex align-center" style="height: 85rpx;">
<view class="iconfont icon-caidan footer-btn animate__animated" hover-class="animate__jello">view>
<view class="iconfont icon-huati footer-btn animate__animated" hover-class="animate__jello">view>
<view class="iconfont icon-tupian footer-btn animate__animated" hover-class="animate__jello">view>
<view class="bg-main text-white ml-auto flex align-center justify-center rounded mr-2 animate__animated" hover-class="animate__jello" style="width: 140rpx; height: 60rpx;">发送view>
view>
view>
template>
<script>
import uniNavBar from '@/components/uni-ui/uni-nav-bar/uni-nav-bar.vue';
export default {
data() {
return {
content: ''
}
},
components: {
uniNavBar
},
methods: {
}
}
script>
<style>
.footer-btn {
width: 86rpx;
height: 86rpx;
display: flex;
justify-content: center;
align-items: center;
font-size: 50rpx;
}
style>
显示:
可以看到,不仅调了尺寸,而且还添加了动画效果。
uni-app官方模板hello_uniapp项目提供了多图上传接口,位于pages/API/image目录下,可以将image.vue拷贝到components/common下并重命名为unpload-image.vue,再稍作修改即可使用,如下:
<template>
<view>
<view class="uni-common-mt">
<view class="uni-list list-pd">
<view class="uni-list-cell cell-pd">
<view class="uni-uploader">
<view class="uni-uploader-head">
<view class="uni-uploader-title">点击可预览选好的图片view>
<view class="uni-uploader-info">{
{imageList.length}}/9view>
view>
<view class="uni-uploader-body">
<view class="uni-uploader__files">
<block v-for="(image,index) in imageList" :key="index">
<view class="uni-uploader__file">
<image class="uni-uploader__img" :src="image" :data-src="image" @tap="previewImage">image>
view>
block>
<view class="uni-uploader__input-box">
<view class="uni-uploader__input" @tap="chooseImage">view>
view>
view>
view>
view>
view>
view>
view>
view>
template>
<script>
import permision from "@/common/permission.js"
var sourceType = [
['camera'],
['album'],
['camera', 'album']
]
var sizeType = [
['compressed'],
['original'],
['compressed', 'original']
]
export default {
data() {
return {
title: 'choose/previewImage',
imageList: [],
sourceTypeIndex: 2,
sourceType: ['拍照', '相册', '拍照或相册'],
sizeTypeIndex: 2,
sizeType: ['压缩', '原图', '压缩或原图'],
countIndex: 8,
count: [1, 2, 3, 4, 5, 6, 7, 8, 9]
}
},
onUnload() {
this.imageList = [],
this.sourceTypeIndex = 2,
this.sourceType = ['拍照', '相册', '拍照或相册'],
this.sizeTypeIndex = 2,
this.sizeType = ['压缩', '原图', '压缩或原图'],
this.countIndex = 8;
},
methods: {
chooseImage: async function() {
// #ifdef APP-PLUS
// TODO 选择相机或相册时 需要弹出actionsheet,目前无法获得是相机还是相册,在失败回调中处理
if (this.sourceTypeIndex !== 2) {
let status = await this.checkPermission();
if (status !== 1) {
return;
}
}
// #endif
if (this.imageList.length === 9) {
let isContinue = await this.isFullImg();
console.log("是否继续?", isContinue);
if (!isContinue) {
return;
}
}
uni.chooseImage({
sourceType: sourceType[this.sourceTypeIndex],
sizeType: sizeType[this.sizeTypeIndex],
count: this.imageList.length + this.count[this.countIndex] > 9 ? 9 - this.imageList.length : this.count[this.countIndex],
success: (res) => {
this.imageList = this.imageList.concat(res.tempFilePaths);
},
fail: (err) => {
// #ifdef APP-PLUS
if (err['code'] && err.code !== 0 && this.sourceTypeIndex === 2) {
this.checkPermission(err.code);
}
// #endif
// #ifdef MP
uni.getSetting({
success: (res) => {
let authStatus = false;
switch (this.sourceTypeIndex) {
case 0:
authStatus = res.authSetting['scope.camera'];
break;
case 1:
authStatus = res.authSetting['scope.album'];
break;
case 2:
authStatus = res.authSetting['scope.album'] && res.authSetting['scope.camera'];
break;
default:
break;
}
if (!authStatus) {
uni.showModal({
title: '授权失败',
content: 'Hello uni-app需要从您的相机或相册获取图片,请在设置界面打开相关权限',
success: (res) => {
if (res.confirm) {
uni.openSetting()
}
}
})
}
}
})
// #endif
}
})
},
isFullImg: function() {
return new Promise((res) => {
uni.showModal({
content: "已经有9张图片了,是否清空现有图片?",
success: (e) => {
if (e.confirm) {
this.imageList = [];
res(true);
} else {
res(false)
}
},
fail: () => {
res(false)
}
})
})
},
previewImage: function(e) {
var current = e.target.dataset.src
uni.previewImage({
current: current,
urls: this.imageList
})
},
async checkPermission(code) {
let type = code ? code - 1 : this.sourceTypeIndex;
let status = permision.isIOS ? await permision.requestIOS(sourceType[type][0]) :
await permision.requestAndroid(type === 0 ? 'android.permission.CAMERA' :
'android.permission.READ_EXTERNAL_STORAGE');
if (status === null || status === 1) {
status = 1;
} else {
uni.showModal({
content: "没有开启权限",
confirmText: "设置",
success: function(res) {
if (res.confirm) {
permision.gotoAppSetting();
}
}
})
}
return status;
}
}
}
script>
<style>
.cell-pd {
padding: 22rpx 30rpx;
}
.list-pd {
margin-top: 50rpx;
}
style>
在add_input.vue中导入并使用upload-image组件,如下:
<template>
<view>
<uni-nav-bar left-icon="back" :statusBar="true" :border="false">
<view class="flex justify-center align-center w-100">
所有人可见<text class="iconfont icon-shezhi">text>
view>
uni-nav-bar>
<textarea v-model="content" placeholder="说一句话吧~" class="uni-textarea px-2" />
<upload-image>upload-image>
<view class="fixed-bottom bg-white flex align-center" style="height: 85rpx;">
<view class="iconfont icon-caidan footer-btn animate__animated" hover-class="animate__jello">view>
<view class="iconfont icon-huati footer-btn animate__animated" hover-class="animate__jello">view>
<view class="iconfont icon-tupian footer-btn animate__animated" hover-class="animate__jello">view>
<view class="bg-main text-white ml-auto flex align-center justify-center rounded mr-2 animate__animated" hover-class="animate__jello" style="width: 140rpx; height: 60rpx;">发送view>
view>
view>
template>
<script>
import uniNavBar from '@/components/uni-ui/uni-nav-bar/uni-nav-bar.vue';
import uploadImage from '../../components/common/upload-image.vue';
export default {
data() {
return {
content: ''
}
},
components: {
uniNavBar,
uploadImage
},
methods: {
}
}
script>
<style>
.footer-btn {
width: 86rpx;
height: 86rpx;
display: flex;
justify-content: center;
align-items: center;
font-size: 50rpx;
}
style>
显示:
可以看到,实现了上传图片,还可以进行预览。
现进一步调整样式,unload-image.vue完善如下:
<template>
<view class="px-2">
<view class="uni-uploader">
<view class="uni-uploader-head">
<view class="uni-uploader-title">点击预览view>
<view class="uni-uploader-info">{
{imageList.length}}/9view>
view>
<view class="uni-uploader-body">
<view class="uni-uploader__files">
<block v-for="(image,index) in imageList" :key="index">
<view class="uni-uploader__file">
<image class="uni-uploader__img rounded" :src="image" :data-src="image" mode="aspectFill" @tap="previewImage">image>
view>
block>
<view class="uni-uploader__input-box rounded">
<view class="uni-uploader__input" @tap="chooseImage">view>
view>
view>
view>
view>
view>
template>
<script>
import permision from "@/common/permission.js"
var sourceType = [
['camera'],
['album'],
['camera', 'album']
]
var sizeType = [
['compressed'],
['original'],
['compressed', 'original']
]
export default {
data() {
return {
title: 'choose/previewImage',
imageList: [],
sourceTypeIndex: 2,
sourceType: ['拍照', '相册', '拍照或相册'],
sizeTypeIndex: 2,
sizeType: ['压缩', '原图', '压缩或原图'],
countIndex: 8,
count: [1, 2, 3, 4, 5, 6, 7, 8, 9]
}
},
onUnload() {
this.imageList = [],
this.sourceTypeIndex = 2,
this.sourceType = ['拍照', '相册', '拍照或相册'],
this.sizeTypeIndex = 2,
this.sizeType = ['压缩', '原图', '压缩或原图'],
this.countIndex = 8;
},
methods: {
chooseImage: async function() {
// #ifdef APP-PLUS
// TODO 选择相机或相册时 需要弹出actionsheet,目前无法获得是相机还是相册,在失败回调中处理
if (this.sourceTypeIndex !== 2) {
let status = await this.checkPermission();
if (status !== 1) {
return;
}
}
// #endif
if (this.imageList.length === 9) {
let isContinue = await this.isFullImg();
console.log("是否继续?", isContinue);
if (!isContinue) {
return;
}
}
uni.chooseImage({
sourceType: sourceType[this.sourceTypeIndex],
sizeType: sizeType[this.sizeTypeIndex],
count: this.imageList.length + this.count[this.countIndex] > 9 ? 9 - this.imageList.length : this.count[this.countIndex],
success: (res) => {
this.imageList = this.imageList.concat(res.tempFilePaths);
this.$emit('choose', this.imageList);
},
fail: (err) => {
// #ifdef APP-PLUS
if (err['code'] && err.code !== 0 && this.sourceTypeIndex === 2) {
this.checkPermission(err.code);
}
// #endif
// #ifdef MP
uni.getSetting({
success: (res) => {
let authStatus = false;
switch (this.sourceTypeIndex) {
case 0:
authStatus = res.authSetting['scope.camera'];
break;
case 1:
authStatus = res.authSetting['scope.album'];
break;
case 2:
authStatus = res.authSetting['scope.album'] && res.authSetting['scope.camera'];
break;
default:
break;
}
if (!authStatus) {
uni.showModal({
title: '授权失败',
content: 'Hello uni-app需要从您的相机或相册获取图片,请在设置界面打开相关权限',
success: (res) => {
if (res.confirm) {
uni.openSetting()
}
}
})
}
}
})
// #endif
}
})
},
isFullImg: function() {
return new Promise((res) => {
uni.showModal({
content: "已经有9张图片了,是否清空现有图片?",
success: (e) => {
if (e.confirm) {
this.imageList = [];
res(true);
} else {
res(false)
}
},
fail: () => {
res(false)
}
})
})
},
previewImage: function(e) {
var current = e.target.dataset.src
uni.previewImage({
current: current,
urls: this.imageList
})
},
async checkPermission(code) {
let type = code ? code - 1 : this.sourceTypeIndex;
let status = permision.isIOS ? await permision.requestIOS(sourceType[type][0]) :
await permision.requestAndroid(type === 0 ? 'android.permission.CAMERA' :
'android.permission.READ_EXTERNAL_STORAGE');
if (status === null || status === 1) {
status = 1;
} else {
uni.showModal({
content: "没有开启权限",
confirmText: "设置",
success: function(res) {
if (res.confirm) {
permision.gotoAppSetting();
}
}
})
}
return status;
}
}
}
script>
<style>
.cell-pd {
padding: 22rpx 30rpx;
}
.list-pd {
margin-top: 50rpx;
}
style>
在完善样式的同时向父组件传递图片列表,实现消息传递。
add-input.vue如下:
<template>
<view>
<uni-nav-bar left-icon="back" :statusBar="true" :border="false">
<view class="flex justify-center align-center w-100">
所有人可见<text class="iconfont icon-shezhi">text>
view>
uni-nav-bar>
<textarea v-model="content" placeholder="说一句话吧~" class="uni-textarea px-2" />
<upload-image @choose="choose">upload-image>
<view class="fixed-bottom bg-white flex align-center" style="height: 85rpx;">
<view class="iconfont icon-caidan footer-btn animate__animated" hover-class="animate__jello">view>
<view class="iconfont icon-huati footer-btn animate__animated" hover-class="animate__jello">view>
<view class="iconfont icon-tupian footer-btn animate__animated" hover-class="animate__jello">view>
<view class="bg-main text-white ml-auto flex align-center justify-center rounded mr-2 animate__animated" hover-class="animate__jello" style="width: 140rpx; height: 60rpx;">发送view>
view>
view>
template>
<script>
import uniNavBar from '@/components/uni-ui/uni-nav-bar/uni-nav-bar.vue';
import uploadImage from '../../components/common/upload-image.vue';
export default {
data() {
return {
content: '',
imageList: []
}
},
components: {
uniNavBar,
uploadImage
},
methods: {
choose(e) {
console.log(e);
this.imageList = e;
}
}
}
script>
<style>
.footer-btn {
width: 86rpx;
height: 86rpx;
display: flex;
justify-content: center;
align-items: center;
font-size: 50rpx;
}
style>
显示:
可以看到,add-input页面中获取到了上传的图片路径列表,可用于后面上传到服务器。
删除选中图片需要在图片右上角添加删除图标,即需要改写upload-imgae.vue,如下:
<block v-for="(image,index) in imageList" :key="index">
<view class="uni-uploader__file position-relative">
<image class="uni-uploader__img rounded" :src="image" :data-src="image" mode="aspectFill" @tap="previewImage">image>
<view class="position-absolute top-0 right-0 rounded" style="padding: 0 15rpx; background-color: rgba(0, 0, 0, 0.5);">
<text class="iconfont icon-shanchu text-white">text>
view>
view>
block>
需要在https://www.iconfont.cn/中选择删除
图标并添加至项目,下载解压后,将iconfont.css更新至common/icon.css中。
可以看到,已经在图片右上角显示出删除图标。
现增加点击事件、实现删除,upload-image.vue如下:
<template>
<view class="px-2">
<view class="uni-uploader">
<view class="uni-uploader-head">
<view class="uni-uploader-title">点击预览view>
<view class="uni-uploader-info">{
{imageList.length}}/9view>
view>
<view class="uni-uploader-body">
<view class="uni-uploader__files">
<block v-for="(image,index) in imageList" :key="index">
<view class="uni-uploader__file position-relative">
<image class="uni-uploader__img rounded" :src="image" :data-src="image" mode="aspectFill" @tap="previewImage">image>
<view class="position-absolute top-0 right-0 rounded" style="padding: 0 15rpx; background-color: rgba(0, 0, 0, 0.5);" @click.stop="deleteImage(index)">
<text class="iconfont icon-shanchu text-white">text>
view>
view>
block>
<view class="uni-uploader__input-box rounded">
<view class="uni-uploader__input" @tap="chooseImage">view>
view>
view>
view>
view>
view>
template>
<script>
import permision from "@/common/permission.js"
var sourceType = [
['camera'],
['album'],
['camera', 'album']
]
var sizeType = [
['compressed'],
['original'],
['compressed', 'original']
]
export default {
data() {
return {
title: 'choose/previewImage',
imageList: [],
sourceTypeIndex: 2,
sourceType: ['拍照', '相册', '拍照或相册'],
sizeTypeIndex: 2,
sizeType: ['压缩', '原图', '压缩或原图'],
countIndex: 8,
count: [1, 2, 3, 4, 5, 6, 7, 8, 9]
}
},
onUnload() {
this.imageList = [],
this.sourceTypeIndex = 2,
this.sourceType = ['拍照', '相册', '拍照或相册'],
this.sizeTypeIndex = 2,
this.sizeType = ['压缩', '原图', '压缩或原图'],
this.countIndex = 8;
},
methods: {
chooseImage: async function() {
// #ifdef APP-PLUS
// TODO 选择相机或相册时 需要弹出actionsheet,目前无法获得是相机还是相册,在失败回调中处理
if (this.sourceTypeIndex !== 2) {
let status = await this.checkPermission();
if (status !== 1) {
return;
}
}
// #endif
if (this.imageList.length === 9) {
let isContinue = await this.isFullImg();
console.log("是否继续?", isContinue);
if (!isContinue) {
return;
}
}
uni.chooseImage({
sourceType: sourceType[this.sourceTypeIndex],
sizeType: sizeType[this.sizeTypeIndex],
count: this.imageList.length + this.count[this.countIndex] > 9 ? 9 - this.imageList.length : this.count[this.countIndex],
success: (res) => {
this.imageList = this.imageList.concat(res.tempFilePaths);
this.$emit('change', this.imageList);
},
fail: (err) => {
// #ifdef APP-PLUS
if (err['code'] && err.code !== 0 && this.sourceTypeIndex === 2) {
this.checkPermission(err.code);
}
// #endif
// #ifdef MP
uni.getSetting({
success: (res) => {
let authStatus = false;
switch (this.sourceTypeIndex) {
case 0:
authStatus = res.authSetting['scope.camera'];
break;
case 1:
authStatus = res.authSetting['scope.album'];
break;
case 2:
authStatus = res.authSetting['scope.album'] && res.authSetting['scope.camera'];
break;
default:
break;
}
if (!authStatus) {
uni.showModal({
title: '授权失败',
content: 'Hello uni-app需要从您的相机或相册获取图片,请在设置界面打开相关权限',
success: (res) => {
if (res.confirm) {
uni.openSetting()
}
}
})
}
}
})
// #endif
}
})
},
isFullImg: function() {
return new Promise((res) => {
uni.showModal({
content: "已经有9张图片了,是否清空现有图片?",
success: (e) => {
if (e.confirm) {
this.imageList = [];
res(true);
} else {
res(false)
}
},
fail: () => {
res(false)
}
})
})
},
previewImage: function(e) {
var current = e.target.dataset.src
uni.previewImage({
current: current,
urls: this.imageList
})
},
async checkPermission(code) {
let type = code ? code - 1 : this.sourceTypeIndex;
let status = permision.isIOS ? await permision.requestIOS(sourceType[type][0]) :
await permision.requestAndroid(type === 0 ? 'android.permission.CAMERA' :
'android.permission.READ_EXTERNAL_STORAGE');
if (status === null || status === 1) {
status = 1;
} else {
uni.showModal({
content: "没有开启权限",
confirmText: "设置",
success: function(res) {
if (res.confirm) {
permision.gotoAppSetting();
}
}
})
}
return status;
},
deleteImage(index) {
uni.showModal({
title: '删除提示',
content: '是否要删除该图片?',
showCancel: true,
cancelText: '不删除',
confirmText: '删除',
success: res => {
if (res.confirm) {
this.imageList.splice(index, 1);
this.$emit('change', this.imageList);
}
},
fail: () => {
},
complete: () => {
}
});
}
}
}
script>
<style>
.cell-pd {
padding: 22rpx 30rpx;
}
.list-pd {
margin-top: 50rpx;
}
style>
add-input.vue修改如下:
<template>
<view>
<uni-nav-bar left-icon="back" :statusBar="true" :border="false">
<view class="flex justify-center align-center w-100">
所有人可见<text class="iconfont icon-shezhi">text>
view>
uni-nav-bar>
<textarea v-model="content" placeholder="说一句话吧~" class="uni-textarea px-2" />
<upload-image @change="changeImage">upload-image>
<view class="fixed-bottom bg-white flex align-center" style="height: 85rpx;">
<view class="iconfont icon-caidan footer-btn animate__animated" hover-class="animate__jello">view>
<view class="iconfont icon-huati footer-btn animate__animated" hover-class="animate__jello">view>
<view class="iconfont icon-tupian footer-btn animate__animated" hover-class="animate__jello">view>
<view class="bg-main text-white ml-auto flex align-center justify-center rounded mr-2 animate__animated" hover-class="animate__jello" style="width: 140rpx; height: 60rpx;">发送view>
view>
view>
template>
<script>
import uniNavBar from '@/components/uni-ui/uni-nav-bar/uni-nav-bar.vue';
import uploadImage from '../../components/common/upload-image.vue';
export default {
data() {
return {
content: '',
imageList: []
}
},
components: {
uniNavBar,
uploadImage
},
methods: {
changeImage(e) {
console.log(e);
this.imageList = e;
}
}
}
script>
<style>
.footer-btn {
width: 86rpx;
height: 86rpx;
display: flex;
justify-content: center;
align-items: center;
font-size: 50rpx;
}
style>
显示:
可以看到,实现了删除功能,并且在删除前会给出提示。
一般编辑时,为了友好性和更好的体验,一般会将草稿保存下来,原理是使用页面生命周期onBackPress
。
先模拟保存草稿,演示如下:
// 监听返回
onBackPress() {
if ((this.content !== '' || this.imageList.length > 0) && !this.showBack) {
uni.showModal({
title: '返回提示',
content: '是否要保存为草稿?',
showCancel: true,
cancelText: '不保存',
confirmText: '保存',
success: res => {
// 点击确认
if (re.confirm) {
console.log('保存');
}
// 手动执行返回
uni.navigateBack({
delta: 1
});
}
});
this.showBack = true;
return true;
}
}
显示:
显然,模拟出了保存的效果。
现进一步实现保存编辑内容,如下:
// 监听返回
onBackPress() {
if ((this.content !== '' || this.imageList.length > 0) && !this.showBack) {
uni.showModal({
title: '返回提示',
content: '是否要保存为草稿?',
showCancel: true,
cancelText: '不保存',
confirmText: '保存',
success: res => {
// 点击确认
if (res.confirm) {
this.store();
}
// 手动执行返回
uni.navigateBack({
delta: 1
});
}
});
this.showBack = true;
return true;
}
},
// 页面加载
onLoad() {
uni.getStorage({
key: 'add-input',
success: (res) => {
console.log(res);
if (res.data) {
let result = JSON.parse(res.data);
this.content = result.content;
this.imageList = result.imageList;
}
}
})
},
显示:
可以看到,已经可以保存文本了。
现在实现保存图片,需要父组件(add-input)向子组件(upload-image)传递消息,add-input.vue如下:
<upload-image :list='imageList' @change="changeImage">upload-image>
unpload-image.vue如下:
<template>
<view class="px-2">
<view class="uni-uploader">
<view class="uni-uploader-head">
<view class="uni-uploader-title">点击预览view>
<view class="uni-uploader-info">{
{imageList.length}}/9view>
view>
<view class="uni-uploader-body">
<view class="uni-uploader__files">
<block v-for="(image,index) in imageList" :key="index">
<view class="uni-uploader__file position-relative">
<image class="uni-uploader__img rounded" :src="image" :data-src="image" mode="aspectFill" @tap="previewImage">image>
<view class="position-absolute top-0 right-0 rounded" style="padding: 0 15rpx; background-color: rgba(0, 0, 0, 0.5);"
@click.stop="deleteImage(index)">
<text class="iconfont icon-shanchu text-white">text>
view>
view>
block>
<view class="uni-uploader__input-box rounded">
<view class="uni-uploader__input" @tap="chooseImage">view>
view>
view>
view>
view>
view>
template>
<script>
import permision from "@/common/permission.js"
var sourceType = [
['camera'],
['album'],
['camera', 'album']
]
var sizeType = [
['compressed'],
['original'],
['compressed', 'original']
]
export default {
props: ['list'],
data() {
return {
title: 'choose/previewImage',
imageList: [],
sourceTypeIndex: 2,
sourceType: ['拍照', '相册', '拍照或相册'],
sizeTypeIndex: 2,
sizeType: ['压缩', '原图', '压缩或原图'],
countIndex: 8,
count: [1, 2, 3, 4, 5, 6, 7, 8, 9]
}
},
onUnload() {
this.imageList = [],
this.sourceTypeIndex = 2,
this.sourceType = ['拍照', '相册', '拍照或相册'],
this.sizeTypeIndex = 2,
this.sizeType = ['压缩', '原图', '压缩或原图'],
this.countIndex = 8;
},
mounted() {
console.log(this.list);
this.imageList = this.list;
},
methods: {
chooseImage: async function() {
// #ifdef APP-PLUS
// TODO 选择相机或相册时 需要弹出actionsheet,目前无法获得是相机还是相册,在失败回调中处理
if (this.sourceTypeIndex !== 2) {
let status = await this.checkPermission();
if (status !== 1) {
return;
}
}
// #endif
if (this.imageList.length === 9) {
let isContinue = await this.isFullImg();
console.log("是否继续?", isContinue);
if (!isContinue) {
return;
}
}
uni.chooseImage({
sourceType: sourceType[this.sourceTypeIndex],
sizeType: sizeType[this.sizeTypeIndex],
count: this.imageList.length + this.count[this.countIndex] > 9 ? 9 - this.imageList.length : this.count[this.countIndex],
success: (res) => {
this.imageList = this.imageList.concat(res.tempFilePaths);
this.$emit('change', this.imageList);
},
fail: (err) => {
// #ifdef APP-PLUS
if (err['code'] && err.code !== 0 && this.sourceTypeIndex === 2) {
this.checkPermission(err.code);
}
// #endif
// #ifdef MP
uni.getSetting({
success: (res) => {
let authStatus = false;
switch (this.sourceTypeIndex) {
case 0:
authStatus = res.authSetting['scope.camera'];
break;
case 1:
authStatus = res.authSetting['scope.album'];
break;
case 2:
authStatus = res.authSetting['scope.album'] && res.authSetting['scope.camera'];
break;
default:
break;
}
if (!authStatus) {
uni.showModal({
title: '授权失败',
content: 'Hello uni-app需要从您的相机或相册获取图片,请在设置界面打开相关权限',
success: (res) => {
if (res.confirm) {
uni.openSetting()
}
}
})
}
}
})
// #endif
}
})
},
isFullImg: function() {
return new Promise((res) => {
uni.showModal({
content: "已经有9张图片了,是否清空现有图片?",
success: (e) => {
if (e.confirm) {
this.imageList = [];
res(true);
} else {
res(false)
}
},
fail: () => {
res(false)
}
})
})
},
previewImage: function(e) {
var current = e.target.dataset.src
uni.previewImage({
current: current,
urls: this.imageList
})
},
async checkPermission(code) {
let type = code ? code - 1 : this.sourceTypeIndex;
let status = permision.isIOS ? await permision.requestIOS(sourceType[type][0]) :
await permision.requestAndroid(type === 0 ? 'android.permission.CAMERA' :
'android.permission.READ_EXTERNAL_STORAGE');
if (status === null || status === 1) {
status = 1;
} else {
uni.showModal({
content: "没有开启权限",
confirmText: "设置",
success: function(res) {
if (res.confirm) {
permision.gotoAppSetting();
}
}
})
}
return status;
},
deleteImage(index) {
uni.showModal({
title: '删除提示',
content: '是否要删除该图片?',
showCancel: true,
cancelText: '不删除',
confirmText: '删除',
success: res => {
if (res.confirm) {
this.imageList.splice(index, 1);
this.$emit('change', this.imageList);
}
},
fail: () => {
},
complete: () => {
}
});
}
}
}
script>
<style>
.cell-pd {
padding: 22rpx 30rpx;
}
.list-pd {
margin-top: 50rpx;
}
style>
显示:
可以看到,实现了保存图片到草稿。
这时候还可能存在一个问题,当编辑之后,返回所如果选择不保存、之前保存到缓存中的内容还可能会存在,因此需要在点击时删除该数据缓存;
同时,需要实现点击左上角返回按钮,可以正常返回,此时需要父组件与子组件进行事件传递。
add-input.vue如下:
<template>
<view>
<uni-nav-bar left-icon="back" :statusBar="true" :border="false" @clickLeft="goBack()">
<view class="flex justify-center align-center w-100">
所有人可见<text class="iconfont icon-shezhi">text>
view>
uni-nav-bar>
<textarea v-model="content" placeholder="说一句话吧~" class="uni-textarea px-2" />
<upload-image :list='imageList' @change="changeImage">upload-image>
<view class="fixed-bottom bg-white flex align-center" style="height: 85rpx;">
<view class="iconfont icon-caidan footer-btn animate__animated" hover-class="animate__jello">view>
<view class="iconfont icon-huati footer-btn animate__animated" hover-class="animate__jello">view>
<view class="iconfont icon-tupian footer-btn animate__animated" hover-class="animate__jello">view>
<view class="bg-main text-white ml-auto flex align-center justify-center rounded mr-2 animate__animated" hover-class="animate__jello" style="width: 140rpx; height: 60rpx;">发送view>
view>
view>
template>
<script>
import uniNavBar from '@/components/uni-ui/uni-nav-bar/uni-nav-bar.vue';
import uploadImage from '../../components/common/upload-image.vue';
export default {
data() {
return {
content: '',
imageList: [],
// 是否已经弹出提示框
showBack: false
}
},
components: {
uniNavBar,
uploadImage
},
// 监听返回
onBackPress() {
if ((this.content !== '' || this.imageList.length > 0) && !this.showBack) {
uni.showModal({
title: '返回提示',
content: '是否要保存为草稿?',
showCancel: true,
cancelText: '不保存',
confirmText: '保存',
success: res => {
// 点击确认
if (res.confirm) {
this.store();
}
// 点击取消
else {
uni.removeStorage({
key: 'add-input'
});
}
// 手动执行返回
uni.navigateBack({
delta: 1
});
}
});
this.showBack = true;
return true;
}
},
// 页面加载
onLoad() {
uni.getStorage({
key: 'add-input',
success: (res) => {
console.log(res);
if (res.data) {
let result = JSON.parse(res.data);
this.content = result.content;
this.imageList = result.imageList;
}
}
})
},
methods: {
changeImage(e) {
console.log(e);
this.imageList = e;
},
// 保存草稿
store() {
let obj = {
content: this.content,
imageList: this.imageList
};
// 保存为本地存储
uni.setStorage({
key: 'add-input',
data: JSON.stringify(obj)
})
},
// 返回上一步
goBack() {
uni.navigateBack({
delta: 1
})
}
}
}
script>
<style>
.footer-btn {
width: 86rpx;
height: 86rpx;
display: flex;
justify-content: center;
align-items: center;
font-size: 50rpx;
}
style>
显示:
显然,已实现了预期的效果。
再实现隐藏图片添加预览区域、点击图标上传图片后再显示添加图片区域,原理是点击add-input组件的icon-tupian
图标,触发子组件upload-image的chooseImage()
方法,此时通过ref
属性实现,同时还会用到计算属性等特性。
如下:
<template>
<view>
<uni-nav-bar left-icon="back" :statusBar="true" :border="false" @clickLeft="goBack()">
<view class="flex justify-center align-center w-100">
所有人可见<text class="iconfont icon-shezhi">text>
view>
uni-nav-bar>
<textarea v-model="content" placeholder="说一句话吧~" class="uni-textarea px-2" />
<upload-image :show="show" ref="uploadImage" :list='imageList' @change="changeImage">upload-image>
<view class="fixed-bottom bg-white flex align-center" style="height: 85rpx;">
<view class="iconfont icon-caidan footer-btn animate__animated" hover-class="animate__jello">view>
<view class="iconfont icon-huati footer-btn animate__animated" hover-class="animate__jello">view>
<view class="iconfont icon-tupian footer-btn animate__animated" hover-class="animate__jello" @click="iconClickEvent('uploadImage')">view>
<view class="bg-main text-white ml-auto flex align-center justify-center rounded mr-2 animate__animated" hover-class="animate__jello" style="width: 140rpx; height: 60rpx;">发送view>
view>
view>
template>
<script>
import uniNavBar from '@/components/uni-ui/uni-nav-bar/uni-nav-bar.vue';
import uploadImage from '../../components/common/upload-image.vue';
export default {
data() {
return {
content: '',
imageList: [],
// 是否已经弹出提示框
showBack: false
}
},
components: {
uniNavBar,
uploadImage
},
computed: {
show() {
return this.imageList.length > 0;
}
},
// 监听返回
onBackPress() {
if ((this.content !== '' || this.imageList.length > 0) && !this.showBack) {
uni.showModal({
title: '返回提示',
content: '是否要保存为草稿?',
showCancel: true,
cancelText: '不保存',
confirmText: '保存',
success: res => {
// 点击确认
if (res.confirm) {
this.store();
}
// 点击取消
else {
uni.removeStorage({
key: 'add-input'
});
}
// 手动执行返回
uni.navigateBack({
delta: 1
});
}
});
this.showBack = true;
return true;
}
},
// 页面加载
onLoad() {
uni.getStorage({
key: 'add-input',
success: (res) => {
console.log(res);
if (res.data) {
let result = JSON.parse(res.data);
this.content = result.content;
this.imageList = result.imageList;
}
}
})
},
methods: {
changeImage(e) {
console.log(e);
this.imageList = e;
},
// 保存草稿
store() {
let obj = {
content: this.content,
imageList: this.imageList
};
// 保存为本地存储
uni.setStorage({
key: 'add-input',
data: JSON.stringify(obj)
})
},
// 返回上一步
goBack() {
uni.navigateBack({
delta: 1
})
},
// 底部图标点击事件
iconClickEvent(e) {
switch (e){
case 'uploadImage':
this.$refs.uploadImage.chooseImage();
break;
}
}
}
}
script>
<style>
.footer-btn {
width: 86rpx;
height: 86rpx;
display: flex;
justify-content: center;
align-items: center;
font-size: 50rpx;
}
style>
upload-image.vue如下:
<template>
<view class="px-2">
<view class="uni-uploader" v-if="show">
<view class="uni-uploader-head">
<view class="uni-uploader-title">点击预览view>
<view class="uni-uploader-info">{
{imageList.length}}/9view>
view>
<view class="uni-uploader-body">
<view class="uni-uploader__files">
<block v-for="(image,index) in imageList" :key="index">
<view class="uni-uploader__file position-relative">
<image class="uni-uploader__img rounded" :src="image" :data-src="image" mode="aspectFill" @tap="previewImage">image>
<view class="position-absolute top-0 right-0 rounded" style="padding: 0 15rpx; background-color: rgba(0, 0, 0, 0.5);"
@click.stop="deleteImage(index)">
<text class="iconfont icon-shanchu text-white">text>
view>
view>
block>
<view class="uni-uploader__input-box rounded">
<view class="uni-uploader__input" @tap="chooseImage">view>
view>
view>
view>
view>
view>
template>
<script>
import permision from "@/common/permission.js"
var sourceType = [
['camera'],
['album'],
['camera', 'album']
]
var sizeType = [
['compressed'],
['original'],
['compressed', 'original']
]
export default {
props: {
list: Array,
show: {
type: Boolean,
default: true
}
},
data() {
return {
title: 'choose/previewImage',
imageList: [],
sourceTypeIndex: 2,
sourceType: ['拍照', '相册', '拍照或相册'],
sizeTypeIndex: 2,
sizeType: ['压缩', '原图', '压缩或原图'],
countIndex: 8,
count: [1, 2, 3, 4, 5, 6, 7, 8, 9]
}
},
onUnload() {
this.imageList = [],
this.sourceTypeIndex = 2,
this.sourceType = ['拍照', '相册', '拍照或相册'],
this.sizeTypeIndex = 2,
this.sizeType = ['压缩', '原图', '压缩或原图'],
this.countIndex = 8;
},
mounted() {
console.log(this.list);
this.imageList = this.list;
},
methods: {
chooseImage: async function() {
// #ifdef APP-PLUS
// TODO 选择相机或相册时 需要弹出actionsheet,目前无法获得是相机还是相册,在失败回调中处理
if (this.sourceTypeIndex !== 2) {
let status = await this.checkPermission();
if (status !== 1) {
return;
}
}
// #endif
if (this.imageList.length === 9) {
let isContinue = await this.isFullImg();
console.log("是否继续?", isContinue);
if (!isContinue) {
return;
}
}
uni.chooseImage({
sourceType: sourceType[this.sourceTypeIndex],
sizeType: sizeType[this.sizeTypeIndex],
count: this.imageList.length + this.count[this.countIndex] > 9 ? 9 - this.imageList.length : this.count[this.countIndex],
success: (res) => {
this.imageList = this.imageList.concat(res.tempFilePaths);
this.$emit('change', this.imageList);
},
fail: (err) => {
// #ifdef APP-PLUS
if (err['code'] && err.code !== 0 && this.sourceTypeIndex === 2) {
this.checkPermission(err.code);
}
// #endif
// #ifdef MP
uni.getSetting({
success: (res) => {
let authStatus = false;
switch (this.sourceTypeIndex) {
case 0:
authStatus = res.authSetting['scope.camera'];
break;
case 1:
authStatus = res.authSetting['scope.album'];
break;
case 2:
authStatus = res.authSetting['scope.album'] && res.authSetting['scope.camera'];
break;
default:
break;
}
if (!authStatus) {
uni.showModal({
title: '授权失败',
content: 'Hello uni-app需要从您的相机或相册获取图片,请在设置界面打开相关权限',
success: (res) => {
if (res.confirm) {
uni.openSetting()
}
}
})
}
}
})
// #endif
}
})
},
isFullImg: function() {
return new Promise((res) => {
uni.showModal({
content: "已经有9张图片了,是否清空现有图片?",
success: (e) => {
if (e.confirm) {
this.imageList = [];
res(true);
} else {
res(false)
}
},
fail: () => {
res(false)
}
})
})
},
previewImage: function(e) {
var current = e.target.dataset.src
uni.previewImage({
current: current,
urls: this.imageList
})
},
async checkPermission(code) {
let type = code ? code - 1 : this.sourceTypeIndex;
let status = permision.isIOS ? await permision.requestIOS(sourceType[type][0]) :
await permision.requestAndroid(type === 0 ? 'android.permission.CAMERA' :
'android.permission.READ_EXTERNAL_STORAGE');
if (status === null || status === 1) {
status = 1;
} else {
uni.showModal({
content: "没有开启权限",
confirmText: "设置",
success: function(res) {
if (res.confirm) {
permision.gotoAppSetting();
}
}
})
}
return status;
},
deleteImage(index) {
uni.showModal({
title: '删除提示',
content: '是否要删除该图片?',
showCancel: true,
cancelText: '不删除',
confirmText: '删除',
success: res => {
if (res.confirm) {
this.imageList.splice(index, 1);
this.$emit('change', this.imageList);
}
},
fail: () => {
},
complete: () => {
}
});
}
}
}
script>
<style>
.cell-pd {
padding: 22rpx 30rpx;
}
.list-pd {
margin-top: 50rpx;
}
style>
显示:
显然,已经实现了预期的效果。
首页导航栏的实现主要包括搜索页和贴子发布页的实现,这是开发的基础功能搜索页展示话题和帖子、发布页发布帖子,也包含了很多功能细节,需要一一实现。