源码可以提供下载,详情访问末尾码云地址
新建模板项目hello uniapp
复制模板common下的css
在本项目的app.vue进行引入css文件 @import “./common/uni.css ” (还要引入uni.tff文件,否则报错)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xizHMjdI-1649380676444)(uniapp社区交友开发.assets/image-20220322164136184-16479384973031.png)]
阿里巴巴矢量图标库 https://www.iconfont.cn/
下载所选图标至项目打包 得到压缩文件
修改icon.css文件去掉url,引入文件 测试图标使用
<view>
<text class="iconfont icon-smile" style="font-size: 100rpx; color: red;"></text>
</view>
下载animate.css
引入animate.css
测试css动画库的使用
<view style="display: flex;justify-content: center;padding: 50rpx;">
<view class="" hover-class=" animated rubberBand" style="border: 1rpx solid black; padding: 20rpx;">
点击效果
</view>
</view>
解析page.json文件 看官方文档
设置导航栏的样式
"globalStyle": {
"navigationBarTextStyle": "black", //导航栏字体颜色
"navigationBarTitleText": "社区交友",//导航栏文字
"navigationBarBackgroundColor": "#FFF",//背景颜色
"backgroundColor": "#FFF"
}
设置图标(图片为png,81*81)用矢量图标库下载
配置tabBar前提要配置pages数组页面
"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
{
"path": "pages/index/index",
"style": {
}
}
,{
"path" : "pages/news/news",
"style" :
{
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
}
,{
"path" : "pages/msg/msg",
"style" :
{
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
}
,{
"path" : "pages/my/my",
"style" :
{
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
}
],
"tabBar": {
"color": "#323232",
"selectedColor": "#FC5C82",
"backgroundColor": "#FFF",
"borderStyle": "black",
"list": [
{
"pagePath": "pages/index/index",
"text": "首页",
"iconPath": "static/tabbar/index.png",
"selectedIconPath": "static/tabbar/indexed.png"
},
{
"pagePath": "pages/news/news",
"text": "动态",
"iconPath": "static/tabbar/news.png",
"selectedIconPath": "static/tabbar/newsed.png"
},
{
"pagePath": "pages/msg/msg",
"text": "消息",
"iconPath": "static/tabbar/paper.png",
"selectedIconPath": "static/tabbar/papered.png"
},
{
"pagePath": "pages/my/my",
"text": "我的",
"iconPath": "static/tabbar/home.png",
"selectedIconPath": "static/tabbar/homeed.png"
}
]
}
普通的选择器 id就用#,class就用.,什么都不加默认全部
父级下的子级菜单的选择器
.box>view:first-of-type{
background-color: red;
}
.box>view:last-of-type{
background-color: pink;
}
.box>view:nth-child(2){
background-color: yellow;
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SvOmDgZ5-1649380676446)(uniapp社区交友开发.assets/image-20220323110150462-16480045116861.png)]
奇偶选择器
//奇数选择器
.box>view:nth-of-type(odd){
background-color: red;
}
//偶数选择器
.box>view:nth-of-type(even){
background-color: green;
}
//偶数选择器
.box:nth-of-type(even){
background-color: green;
}
watch
测试
<template>
<view>
<view>{{num}}</view>
<button @tap="changNum()">按钮</button>
</view>
</template>
<script>
export default {
data() {
return {
num:0
}
},
watch:{
num(val){
console.log(val);
}
},
methods: {
changNum(){
this.num++;
}
}
}
</script>
<style>
button{
background: blue;
display: flex;
justify-content: center;
align-items: center;
font-size: 15px;
color: white;
}
</style>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Gyz1MrF9-1649380676447)(uniapp社区交友开发.assets/image-20220323154220758-16480213421241.png)]
常用于数据的格式化
computed
测试运用
<template>
<view>
{{formatWeight}}
</view>
</template>
<script>
export default {
data() {
return {
weight:2100
}
},
computed:{
formatWeight(){
return (this.weight>1000)?this.weight/1000+'kg':this.weight+'g';
}
}
}
</script>
<style>
</style>
导航栏配置根据官方文档配置
{
"path": "pages/index/index",
"style": {
"app-plus": {
// 导航栏配置
"titleNView":{
// 搜索框配置
"searchInput":{
"align":"center",
"backgroundColor":"#F5F4F2",
"borderRadius":"4px",
"disabled":true,
"placeholder":"搜索帖子",
"placeholderColor":"#6D6C67"
},
"buttons":[
{
"color":"#333333",
"colorPressed":"#FD597C",
"float":"right",
"fontSize":"20px",
"fontSrc":"/static/iconfont.ttf",
"text":"\ue668"
}
]
}
}
}
}
用真机调试成功,微信开发者工具旁边无显示
封装free.css把常用样式封装 如==flex:display;justify-content:center;algin-items:center、
引入自定义css库
列表开发
<view style="padding: 20rpx;">
<view class="flex;justify-between;align-center">
<!-- 头像,昵称 -->
<view class="flex;align-center">
<!-- 头像 -->
<image src="/static/common//nothing.png" class="mr-1; rounded" style="width:65rpx;height:65rpx"></image>
<view>
<view style="font-size: 30rpx;">昵称</view>
<view style="color: #9d9589;font-size: 20rpx;">2022-3-15</view>
</view>
</view >
<!-- 按钮 -->
<view class="flex;align-center; justify-center" style="background: #FF4A6A; width:90rpx; height:50rpx;
color: white;" >
关注
</view>
</view>
<!-- 文章内容 -->
<view>
哈哈哈
</view>
<!-- 图片 -->
<view class="mt-1" >
<image src="/static/demo/datapic/45.jpg" style="width: 100%; height: 350rpx;" lazy-load="true"></image>
</view>
<!-- 按钮 -->
<view class="flex" >
<view class="flex;align-center; justify-center flex-1">
<view class="iconfont icon-dianzan2 "></view>
<view>赞</view>
</view>
<view class="flex;align-center; justify-center flex-1">
<view class="iconfont icon-cai; mr-2" ></view>
<view>踩</view>
</view>
<view class="flex;align-center; justify-center flex-1">
<view class="iconfont icon-pinglun2; mr-2" ></view>
<view>评论</view>
</view>
<view class="flex;align-center; justify-center flex-1">
<view class="iconfont icon-zhuanfa1; mr-2" ></view>
<view>转发</view>
</view>
</view>
</view>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-daoAQo1N-1649380676447)(uniapp社区交友开发.assets/image-20220323193232107.png)]
继续使用free.css封装代码
<view>
<view class="p-2">
<view class="flex;justify-between;align-center">
<!-- 头像,昵称 -->
<view class="flex;align-center">
<!-- 头像 -->
<image src="/static/common//nothing.png" class="mr-1; rounded-circle" style="width:65rpx;height:65rpx"></image>
<view>
<view class="font" style="line-height: 1.5;">昵称</view>
<view style=" ine-height: 1.5;" class="font-small; text-light-muted">2022-3-15</view>
</view>
</view >
<!-- 按钮 -->
<view class="flex;align-center; justify-center; rounded; text-white; bg-main" style=" width:90rpx; height:50rpx;" >
关注
</view>
</view>
<!-- 文章内容 -->
<view class="font-md; my-1">
我是标题
</view>
<!-- 图片 -->
<view class="mt-1" >
<image src="/static/demo/datapic/45.jpg" class="rounded" style="width: 100%; height: 350rpx;" lazy-load="true"></image>
</view>
<!-- 按钮 -->
<view class="flex" >
<view class="flex;align-center; justify-center flex-1">
<view class="iconfont icon-dianzan2 "></view>
<view>赞</view>
</view>
<view class="flex;align-center; justify-center flex-1">
<view class="iconfont icon-cai; mr-2" ></view>
<view>踩</view>
</view>
<view class="flex;align-center; justify-center flex-1">
<view class="iconfont icon-pinglun2; mr-2" ></view>
<view>评论</view>
</view>
<view class="flex;align-center; justify-center flex-1">
<view class="iconfont icon-zhuanfa1; mr-2" ></view>
<view>转发</view>
</view>
</view>
</view>
</view>
封装本项目公共css(common.css 记得引入)
.bg-main{
background: #FF4A6A;
}
封装成组件动态渲染
<block v-for="(item,index) in list" :key="index">
<commonList :item="item" :index="index" ></commonList>
</block>
import commonList from '@/components/common/common-list';
components:{
commonList
},
<view class="p-2">
<view class="flex;justify-between;align-center">
<!-- 头像,昵称 -->
<view class="flex;align-center">
<!-- 头像 -->
<image :src="item.userPic" class="mr-1; rounded-circle" style="width:65rpx;height:65rpx"></image>
<view>
<view class="font" style="line-height: 1.5;">{{item.username}}</view>
<view style=" ine-height: 1.5;" class="font-small; text-light-muted">{{item.newstime}}</view>
</view>
</view >
<!-- 按钮 -->
<view class="flex;align-center; justify-center; rounded; text-white; bg-main" style=" width:90rpx; height:50rpx;" >
关注
</view>
</view>
<!-- 文章内容 -->
<view class="font-md; my-1">
{{item.title}}
</view>
<!-- 图片 -->
<view class="mt-1" >
<image :src="item.titlePic" class="rounded" style="width: 100%; height: 350rpx;" lazy-load="true"></image>
</view>
<!-- 按钮 -->
<view class="flex" >
<view class="flex;align-center; justify-center flex-1">
<view class="iconfont icon-dianzan2; mr-2"></view>
<view>{{item.support.support_count}}</view>
</view>
<view class="flex;align-center; justify-center flex-1">
<view class="iconfont icon-cai; mr-2" ></view>
<view>{{item.support.unsupport_count}}</view>
</view>
<view class="flex;align-center; justify-center flex-1">
<view class="iconfont icon-pinglun2; mr-2" ></view>
<view>{{item.comment_count}}</view>
</view>
<view class="flex;align-center; justify-center flex-1">
<view class="iconfont icon-zhuanfa1; mr-2" ></view>
<view>{{item.share_num}}</view>
</view>
</view>
</view>
export default{
props:{
item:Object,
key:Number
}
}
封装组件divider.vue
<!-- 分割线样式 -->
<view style="height: 15rpx; background-color: #F5F5F4;"></view>
引入全局组件(分割线常用)
import divider from '@/components/common/divider.vue';
Vue.component('divider',divider);
<block v-for="(item,index) in list" :key="index">
<commonList :item="item" :index="index"></commonList>
<divider></divider>
</block>
<template>
<view>
<view class="p-2">
<view class="flex;justify-between;align-center">
<!-- 头像,昵称 -->
<view class="flex;align-center">
<!-- 头像 -->
<image :src="item.userPic" class="mr-1; rounded-circle" style="width:65rpx;height:65rpx" @click="openSapce()"></image>
<view>
<view class="font" style="line-height: 1.5;">{{item.username}}</view>
<view style=" ine-height: 1.5;" class="font-small; text-light-muted">{{item.newstime}}</view>
</view>
</view>
<!-- 按钮 -->
<view class="flex;align-center; justify-center; rounded; text-white; bg-main; animated faster "
style=" width:90rpx; height:50rpx;" hover-class="rubberBand" @click="follow()">
关注
</view>
</view>
<!-- 文章内容 -->
<view class="font-md; my-1" @click="openDetail()" >
{{item.title}}
</view>
<!-- 图片 -->
<view class="mt-1" v-if="item.titlePic" @click="openDetail()">
<image :src="item.titlePic" class="rounded" style="width: 100%; height: 350rpx;" lazy-load="true">
</image>
</view>
<!-- 按钮 -->
<view class="flex">
<view class="flex;align-center; justify-center flex-1 animated faster" hover-class="jello text-main" @click="doSupport('support')">
<view class="iconfont icon-dianzan2; mr-2;"></view>
<view>{{item.support.support_count}}</view>
</view>
<view class="flex;align-center; justify-center flex-1 animated faster" hover-class="jello text-main" @click="doSupport('unsupport')">
<view class="iconfont icon-cai; mr-2 "></view>
<view>{{item.support.unsupport_count}}</view>
</view>
<view class="flex;align-center; justify-center flex-1 animated faster" hover-class="jello text-main" @click="openDetail()">
<view class="iconfont icon-pinglun2; mr-2"></view>
<view>{{item.comment_count}}</view>
</view>
<view class="flex;align-center; justify-center flex-1 animated faster" hover-class="jello text-main" @click="openDetail()">
<view class="iconfont icon-zhuanfa1; mr-2"></view>
<view>{{item.share_num}}</view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
props: {
item: Object,
index: Number
},
methods:{
openSapce(){
console.log("打开个人空间");
},
follow(){
console.log("关注");
},
openDetail(){
console.log("打开详情页");
},
doSupport(type){
console.log(type);
}
}
}
</script>
<style>
</style>
<view class="flex;align-center; justify-center; rounded; text-white; bg-main; animated faster "
style=" width:90rpx; height:50rpx;" hover-class="rubberBand" @click="follow()" v-if="!item.isFollow">
关注
</view>
follow(){
// 触发父级follow方法
this.$emit('follow',this.index);
},
<commonList :item="item" :index="index" @follow="follow(index)"></commonList>
methods: {
follow(index){
this.list[index].isFollow=true;
uni.showToast({
title:'关注成功'
})
}
}
<view class="flex;align-center; justify-center flex-1 animated faster" hover-class="jello text-main" @click="doSupport('support')"
:class="item.support.type==='support'?'support-active':''">
<view class="iconfont icon-dianzan2; mr-2;"></view>
<view>{{item.support.support_count>0?item.support.support_count:'支持'}}</view>
</view>
doSupport(type){
this.$emit('doSupport',{
type:type,
index:this.index
})
}
<commonList :item="item" :index="index" @follow="follow" @doSupport="doSupport"></commonList>
doSupport(e) {
let obj=this.newList[this.tabIndex].list[e.index]
console.log(obj);
if (obj.support.type === '') {
//无操作
obj.support[e.type + '_count']++;
} else if (obj.support.type === 'support' && e.type === 'unsupport') {
//之前是顶,顶减一,踩加一
obj.support.support_count--;
obj.support.unsupport_count++;
} else if (obj.support.type === 'unsupport' && e.type === 'support') {
//之前是踩,顶加一,踩减一
obj.support.support_count++;
obj.support.unsupport_count--;
}
obj.support.type = e.type;
let msg = e.type === 'support' ? '顶' : '踩';
uni.showToast({
title: msg + '成功'
})
},
顶部导航选项卡
<scroll-view class="scroll-row border-bottom" scroll-x="true" :scroll-into-view="scrollInto" scroll-with-animation="true">
<view v-for="(item,index) in tabBars" :key="index" class="scroll-row-item px-3 py-2 font-md " :id="'tab'+index"
:class="tabIndex===index?'text-main font-lg font-weight-bold':''" @click="doTab(index)">
{{item.name}}
</view>
</scroll-view>
tabIndex:0,
scrollInto:'',
tabBars:[
{
name:'首页'
},
{
name:'体育'
},
{
name:'军事'
},
{
name:'热点'
},
{
name:'新闻'
},
{
name:'娱乐'
},
{
name:'电竞'
},
{
name:'国际'
},
{
name:'国家'
}
],
doTab(index){
this.tabIndex=index;
this.scrollInto='tab'+index;
}
下面容器能做到切换与导航栏一样,容器也能拉取
<swiper duration=150 @change="onChange" :current="tabIndex" :style="'height:'+scrollH+'px'">
<swiper-item v-for="(item,index) in tabBars" :key="index" >
<scroll-view scroll-y="true" :style="'height:'+scrollH+'px'">
<view v-for="i in 100">{{i}}</view>
</scroll-view>
</swiper-item>
</swiper>
doTab(index){
this.tabIndex=index;
this.scrollInto='tab'+index;
},
onChange(e){
this.doTab(e.detail.current);
}
onLoad() {
const res = uni.getSystemInfoSync();
this.scrollH=res.windowHeight-uni.upx2px(101);
console.log(this.scrollH);
},
列表显示
<swiper duration=150 @change="onChange" :current="tabIndex" :style="'height:'+scrollH+'px'">
<swiper-item v-for="(item,index) in newList" :key="index" >
<scroll-view scroll-y="true" :style="'height:'+scrollH+'px'">
<block v-for="(item2,index2) in item.list" :key="index">
<commonList :item="item2" :index="index2" @follow="follow()" @doSupport="doSupport()"></commonList>
<divider></divider>
</block>
</scroll-view>
</swiper-item>
</swiper>
getData(){
let arr=[];
for (var i = 0; i < this.tabBars.length; i++) {
arr.push({
list: [{
userPic: '/static/common//nothing.png',
username: '',
newstime: '',
isFollow: false,
title: '我是标题',
titlePic: '/static/demo/datapic/45.jpg',
support: {
type: 'support',
support_count: 1,
unsupport_count: 2
},
comment_count: 1,
share_num: 0
},
{
userPic: '/static/common//nothing.png',
username: '',
newstime: '',
isFollow: false,
title: '我是标题',
titlePic: '',
support: {
type: 'unsupport',
support_count: 2,
unsupport_count: 2
},
comment_count: 1,
share_num: 1
},
{
userPic: '/static/common//nothing.png',
username: '',
newstime: '',
isFollow: false,
title: '我是标题',
titlePic: '',
support: {
type: '',
support_count: 2,
unsupport_count: 2
},
comment_count: 1,
share_num: 1
}
]
})
};
this.newList=arr;
},
静态的开发
<scroll-view scroll-y="true" :style="'height:'+scrollH+'px'" @scrolltolower="loadMore(index)">
<block v-for="(item2,index2) in item.list" :key="index">
<commonList :item="item2" :index="index2" @follow="follow()" @doSupport="doSupport()">
</commonList>
<divider></divider>
</block>
<view class="py-2 flex justify-center align-center">
<view class="text-light-muted">{{item.loading}}</view>
</view>
</scroll-view>
触底函数的开发
loadMore(index){
let item=this.newList[index];
item.loading='加载中.';
setTimeout(()=>{
//复制文本
item.list=[...item.list,...item.list];
},2000)
}
优化加载判断
loadMore(index){
let item=this.newList[index];
if(!item==='上拉加载更多'){
return;
}
item.loading='加载中...';
setTimeout(()=>{
item.list=[...item.list,...item.list];
item.loading='上拉加载更多';
},10000)
}
封装load-more.vue 并引入
<template>
<view class="py-2 flex justify-center align-center">
<view class="text-light-muted">{{loading}}</view>
</view>
</template>
<script>
export default{
props:{
loading:String
}
}
</script>
import loadMore from '@/components/common/load-more';
export default {
components: {
commonList,
loadMore
},
}
无数据环境测试
const demo=[
{
userPic: '/static/common//nothing.png',
username: '',
newstime: '',
isFollow: false,
title: '我是标题',
titlePic: '/static/demo/datapic/45.jpg',
support: {
type: 'support',
support_count: 1,
unsupport_count: 2
},
comment_count: 1,
share_num: 0
},
{
userPic: '/static/common//nothing.png',
username: '',
newstime: '',
isFollow: false,
title: '我是标题',
titlePic: '/static/demo/datapic/45.jpg',
support: {
type: 'unsupport',
support_count: 2,
unsupport_count: 2
},
comment_count: 1,
share_num: 1
},
{
userPic: '/static/common//nothing.png',
username: '',
newstime: '',
isFollow: false,
title: '我是标题',
titlePic: '',
support: {
type: '',
support_count: 2,
unsupport_count: 2
},
comment_count: 1,
share_num: 1
}
]
getData() {
let arr = [];
for (var i = 0; i < this.tabBars.length; i++) {
let obj = {
loading:'上拉加载更多',
list: []
}
if(i<2){
obj.list=demo;
}
arr.push(obj)
};
this.newList = arr;
},
封装nothing.vue组件 全局引入
<template v-if="item.list.length>0">
<scroll-view scroll-y="true" :style="'height:'+scrollH+'px'" @scrolltolower="loadMore(index)">
<block v-for="(item2,index2) in item.list" :key="index">
<commonList :item="item2" :index="index2" @follow="follow()" @doSupport="doSupport()">
</commonList>
<divider></divider>
</block>
<loadMore :loading="item.loading"></loadMore>
</scroll-view>
</template>
<template v-else>
<no-thing></no-thing>
</template>
import noThing from '@/components/common/no-thing.vue';
Vue.component('no-thing',noThing);
创建页面search,配置pages.json
{
"path": "pages/index/index",
"style": {
"app-plus": {
// 导航栏配置
"titleNView":{
// 搜索框配置
"searchInput":{
"align":"center",
"backgroundColor":"#F5F4F2",
"borderRadius":"4px",
"disabled":true,
"placeholder":"搜索帖子",
"placeholderColor":"#6D6C67"
},
"buttons":[
{
"color":"#333333",
"colorPressed":"#FD597C",
"float":"right",
"fontSize":"20px",
"fontSrc":"/static/iconfont.ttf",
"text":"\ue668"
}
]
}
}
}
}
,{
"path" : "pages/news/news",
"style" :
{
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
}
,{
"path" : "pages/msg/msg",
"style" :
{
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
}
,{
"path" : "pages/my/my",
"style" :
{
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
}
,{
"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":"搜索"
}
]
}
}
}
}
监听点击导航栏搜索框事件, 实现跳转,用官方api
onNavigationBarSearchInputClicked() {
uni.navigateTo({
url: '../search/search'
})
},
搜索历史开发
<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 " v-for="(item,index) in list" :key="index"
hover-class="bg-light">
{{item}}
</view>
</view>
</view>
监听导航输入
onNavigationBarSearchInputChanged(e) {
this.searchText=e.text;
}
监听导航搜索按钮
onNavigationBarButtonTap(e) {
if(e.index===0){
this.searchEvent();
}
},
搜索事件, 收起键盘,处于loading状态, 展示搜索结果,隐藏loading提示框
searchEvent(){
uni.hideKeyboard();
uni.showLoading({
title:'加载中'
})
setTimeout(()=>{
this.serachList=demo;
uni.hideLoading();
},3000)
}
搜索结果列表 引入commonlist,遍历,优化搜索历史与列表存在问题
<view>
<template v-if="this.serachList.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 " v-for="(item,index) in list" :key="index"
hover-class="bg-light">
{{item}}
</view>
</view>
</template>
<template v-else>
<block v-for="(item,index) in serachList" :key="index" >
<commonList :item="item" :index="index"></commonList>
</block>
</template>
</view>
import commonList from '@/components/common/common-list.vue';
export default {
components:{
commonList
},
}
点击搜索历史的事件
<view class="flex flex-wrap">
<view class="border rounded font mx-2 my-1 px-2 " v-for="(item,index) in list" :key="index"
hover-class="bg-light" @click="historyEvent(item)">
{{item}}
</view>
</view>
historyEvent(item){
console.log(item);
this.searchText=item;
this.searchEvent();
}
新建发布页面add-input,取消原生导航
{
"path" : "pages/add-input/add-input",
"style" :
{
"app-plus": {
"titleNView": false
}
}
}
首页导航按钮跳转页面
onNavigationBarButtonTap() {
uni.navigateTo({
url:'../add-input/add-input'
})
},
自定义导航栏,添加uni-nav-bar依赖, 根据官方文档调用
<uni-nav-bar left-icon="back" statusBar>
<view class="flex justify-center align-center w-100">
所有人可见<text class="iconfont icon-shezhi ml-1"></text>
</view>
</uni-nav-bar>
utextarea, 动态绑定
<textarea v-model="content" placeholder="说一句话吧~" class="px-2 uni-textarea" />
<view style="height: 85rpx" class="fixed-bottom bg-white flex align-center">
<view class="iconfont icon-caidan footer-btn"></view>
<view class="iconfont icon-huati footer-btn"></view>
<view class="iconfont icon-tupian footer-btn"></view>
<view class="bg-main text-white justify-center align-center ml-auto flex mr-2" style="width: 140rpx; height: 60rpx;">发送</view>
</view>
<style>
.footer-btn{
width: 86rpx;
height: 86rpx;
justify-content: center;
display: flex;
align-items: center;
font-size: 50rpx;
}
</style>
upload-image组件开发, 引官方组件,对应引入也需要引入
对官方组件的修改 添加mode压缩,修改内边距, 修改圆角
<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}}/9</view>
</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"
@tap="previewImage" mode="aspectFill"></image>
</view>
</block>
<view class="uni-uploader__input-box">
<view class="uni-uploader__input rounded" @tap="chooseImage"></view>
</view>
</view>
</view>
</view>
</view>
上传图片成功的内容保存 上传图片的回调success方法中
success: (res) => {
this.imageList = this.imageList.concat(res.tempFilePaths);
this.$emit('choose',this.imageList);
},
<uploadImage @choose="choose"></uploadImage>
choose(e){
console.log(e);
this.imageList=e;
}
静态图标的实现
<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"
@tap="previewImage" mode="aspectFill"></image>
<view class="bg-dark 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>
添加删除功能的函数, 通知父组件, 优化父组件方法, 给予交互反馈提示
<text class="iconfont icon-shanchu text-white" @click="deleteImage(index)"></text>
deleteImage(index){
uni.showModal({
title:'提示',
content:'是否要删除该图片',
confirmText:'删除',
cancelText:'不删除',
success: (res) => {
if(res.confirm){
this.imageList.splice(index,1);
this.$emit('change',this.imageList);
}
}
})
},
添加返回首页方法
<uni-nav-bar left-icon="back" statusBar @clickLeft="goBack">
<view class="flex justify-center align-center w-100 font-weight-bold">
所有人可见<text class="iconfont icon-shezhi ml-1"></text>
</view>
</uni-nav-bar>
goBack(){
uni.navigateBack({
delta:1
})
},
监听返回, 交互提示反馈 return true即能返回
onBackPress() {
if((this.content!==''||this.imageList.length>0)&&!this.showBack){
uni.showModal({
content: '是否保存为草稿',
showCancel: true,
cancelText: '不保存',
confirmText: '保存',
success: res => {
if(res.confirm){
console.log('保存');
}
//手动执行返回
// goBack(){
// uni.navigateBack({
// delta:1
// })
// },
this.goBack();
}
});
this.showBack=true;
return true;
}
},
保存的方法
store(){
let obj={
content:this.content,
imageList:this.imageList
};
uni.setStorage({
key:'add-input',
data:JSON.stringify(obj)
})
}
取出的方法 用同步取出
onLoad() {
var res=uni.getStorageSync('add-input');
if(res){
var obj=JSON.parse(res);
// console.log(result);
this.content=obj.content;
this.imageList=obj.imageList;
}
},
优化图片的草稿功能, 上传图片的组件imageList要用props传值
<uploadImage :list="imageList" @change="change"></uploadImage>
//组件里面
props: {
list: Array
},
created(){
this.imageList=this.list;
},
不保存草稿功能的实现, 清楚缓存
onBackPress() {
if((this.content!==''||this.imageList.length>0)&&!this.showBack){
uni.showModal({
content: '是否保存为草稿',
showCancel: true,
cancelText: '不保存',
confirmText: '保存',
success: res => {
if(res.confirm){
// console.log('保存');
this.store();
}else{
uni.removeStorageSync('add-input');
}
//手动执行返回
this.goBack();
}
});
this.showBack=true;
return true;
}
},
下面图标插入图片的方法(优化不需要上传图片时隐藏上传图片组件)
<view class="iconfont icon-tupian footer-btn" @click="iconClickEvent('uploadImage')"></view>
<uploadImage ref="uploadImage" :show="show" :list="imageList" @change="change"></uploadImage>
iconClickEvent(e){
switch (e){
case 'uploadImage': this.$refs.uploadImage.chooseImage();
break;
}
}
computed:{
show(){
return this.imageList.length>0;
}
},
在子组件中加入v-if=“show”
自定义导航栏的静态开发
<uni-nav-bar statusBar="true" border="false" >
<view class="flex align-center justify-center font-weight-bold w-100">
<view class="font-lg text-main mx-1">关注</view>
<view class="font-md text-light-muted mx-1">话题</view>
</view>
<view slot="right" class="iconfont icon-fatie_icon"></view>
</uni-nav-bar>
取消原生导航栏
{
"path": "pages/news/news",
"style": {
"app-plus": {
"titleNView": false
}
}
},
导航栏右边图标的单击事件
<uni-nav-bar statusBar="true" border="false" @clickRight="openAddInput">
<view class="flex align-center justify-center font-weight-bold w-100">
<view class="font-lg text-main mx-1">关注</view>
<view class="font-md text-light-muted mx-1">话题</view>
</view>
<view slot="right" class="iconfont icon-fatie_icon"></view>
</uni-nav-bar>
openAddInput(){
uni.navigateTo({
url: '../add-input/add-input'
})
}
tabBar的动态循环渲染
<uni-nav-bar statusBar="true" border="false" @clickRight="openAddInput">
<view class="flex align-center justify-center font-weight-bold w-100 font-md text-light-muted">
<view v-for="(item,index) in tabBars" :key="index"
:class="tabIndex===index? 'text-main font-lg':''"
@click="changeTab(index)" class="mx-1">
{{item.name}}
</view>
</view>
<view slot="right" class="iconfont icon-fatie_icon"></view>
</uni-nav-bar>
data() {
return {
tabIndex:0,
tabBars:[
{
name:'关注'
},{
name:'话题'
}
]
}
},
changeTab(index){
this.tabIndex=index;
}
引入首页的swipper和scrollview组件, 对应修改即可
<swiper duration=150 @change="onChange" :current="tabIndex" :style="'height:'+scrollH+'px'">
<swiper-item>
<scroll-view scroll-y="true" :style="'height:'+scrollH+'px'">
</scroll-view>
</swiper-item>
</swiper>
计算区域高度
onLoad() {
const res = uni.getSystemInfoSync();
this.scrollH=res.screenHeight-res.statusBarHeight-44;
console.log(this.scrollH);
},
有两个swipperitem 表示关注和话题
<swiper duration=150 @change="onChange" :current="tabIndex" :style="'height:'+scrollH+'px'">
<swiper-item>
<scroll-view scroll-y="true" :style="'height:'+scrollH+'px'">
<view v-for=" (item,index) in 100" :key="index">{{item}}</view>
</scroll-view>
</swiper-item>
<swiper-item>
<view>话题</view>
</swiper-item>
</swiper>
引入common-list组件
分割线的使用
<swiper-item>
<scroll-view scroll-y="true" :style="'height:'+scrollH+'px'">
<block v-for="(item,index) in list" :key="index" >
<commonList :item="item" :index="index"></commonList>
<divider></divider>
</block>
</scroll-view>
</swiper-item>
<swiper-item>
<view>话题</view>
</swiper-item>
导航与列表的联动实现
<swiper duration=150 @change="onChangeTab" :current="tabIndex" :style="'height:'+scrollH+'px'">
<swiper-item>
<scroll-view scroll-y="true" :style="'height:'+scrollH+'px'">
<block v-for="(item,index) in list" :key="index" >
<commonList :item="item" :index="index"></commonList>
<divider></divider>
</block>
</scroll-view>
</swiper-item>
<swiper-item>
<view>话题</view>
</swiper-item>
</swiper>
onChangeTab(e){
// console.log(e.detail);
this.tabIndex=e.detail.current;
}
静态页面开发
<swiper-item>
<view class="flex justify-between align-center px-2">
<text class="font-md">热门分类</text>
<view class="flex align-center font text-secondary">
更多<text class="iconfont icon-jinru"></text>
</view>
</view>
<view class="flex align-center border-bottom px-2 py-3">
<view class="rounded border bg-light mx-1 px-2">关注</view>
<view class="rounded border bg-light mx-1 px-2">关注</view>
<view class="rounded border bg-light mx-1 px-2">关注</view>
<view class="rounded border bg-light mx-1 px-2">关注</view>
</view>
</swiper-item>
添加点击动画效果
<view class="rounded border bg-light mx-1 px-2 animated" hover-class="jello">关注</view>
热门分类对象数组构建
hotCate:[{
name:'关注'
},
{
name:'推荐'
},
{
name:'体育'
},
{
name:'军事'
}
]
封装组件, 导入组件,遍历输出, 父子传值
留两个接口
<hotCate :hotCate="hotCate"></hotCate>
<template>
<view>
<view class="flex justify-between align-center px-2">
<text class="font-md">热门分类</text>
<view class="flex align-center font text-secondary animated" hover-class="jello" @click="openMore">
更多<text class="iconfont icon-jinru"></text>
</view>
</view>
<view class="flex align-center border-bottom px-2 py-3">
<view class="rounded border bg-light mx-1 px-2 animated" hover-class="jello"
v-for="(item,index) in hotCate" :key="index" @click="openDetail">{{item.name}}</view>
</view>
</view>
</template>
<script>
export default{
props:['hotCate'],
methods:{
openMore(){
console.log("点击更多");
},
openDetail(){
console.log("点击进入详情页");
}
}
}
</script>
<style>
</style>
静态页面开发
<view class="p-2">
<view class="flex align-center justify-center py-2 rounded bg-light text-secondary">
<text class="iconfont icon-sousuo mr-2"></text>搜索话题
</view>
</view>
<swiper :indicator-dots="true" :autoplay="true" :interval="3000" :duration="1000" class="px-2 pb-2">
<swiper-item>
<image src="/static/demo/banner3.jpg" class="w-100 rounded" style="height: 300rpx;"></image>
</swiper-item>
</swiper>
<divider></divider>
静态页面开发
<view class="p-2 font-md"> 最近更新</view>
<view class="flex align-center p-2">
<image src="../../static/demo/topicpic/1.jpeg" style="width: 150rpx; height: 150rpx;"
class="rounded mr-2"></image>
<view class="flex flex-column ">
<text class="font-md text-dark">话题哈哈哈</text>
<text class="font text-secondary">话题描述</text>
<view class="flex align-center font text-secondary">
<text class="mr-2">今日:0</text>
<text>关注:0</text>
</view>
</view>
</view>
声明topicList
topicList: [{
cover: '/static/demo/topicpic/1.jpeg',
title: '话题标题',
desc: '话题描述',
news_count: '10',
today_count: '10'
},
{
cover: '/static/demo/topicpic/1.jpeg',
title: '话题标题',
desc: '话题描述',
news_count: '10',
today_count: '10'
},
{
cover: '/static/demo/topicpic/1.jpeg',
title: '话题标题',
desc: '话题描述',
news_count: '10',
today_count: '10'
},
{
cover: '/static/demo/topicpic/1.jpeg',
title: '话题标题',
desc: '话题描述',
news_count: '10',
today_count: '10'
}
]
封装组件,引入组件
<block v-for="(item,index) in topicList" :key="index">
<topicList :item="item" :index="index"></topicList>
</block>
<view class="flex align-center p-2">
<image :src="item.cover" style="width: 150rpx; height: 150rpx;"
class="rounded mr-2"></image>
<view class="flex flex-column ">
<text class="font-md text-dark">{{item.title}}</text>
<text class="font text-secondary">{{item.desc}}</text>
<view class="flex align-center font text-secondary">
<text class="mr-2">动态:{{item.news_count}}</text>
<text>今日:{{item.today_count}}</text>
</view>
</view>
</view>
新建页面
{
"path" : "pages/topic-nav/topic-nav",
"style" :
{
"navigationBarTitleText": "话题分类"
}
}
导航进入
openMore(){
uni.navigateTo({
url:'../topic-nav/topic-nav'
})
},
引入首页,修改topic—nav页面,修改common-list组件, 修改数据
<template>
<view>
<scroll-view class="scroll-row border-bottom border-light-secondary" scroll-x="true"
:scroll-into-view="scrollInto" scroll-with-animation="true" style="height: 100rpx;">
<view v-for="(item,index) in tabBars" :key="index" class="scroll-row-item px-3 py-2 font-md "
:id="'tab'+index" :class="tabIndex===index?'text-main font-lg font-weight-bold':''"
@click="doTab(index)">
{{item.name}}
</view>
</scroll-view>
<swiper duration=150 @change="onChange" :current="tabIndex" :style="'height:'+scrollH+'px'">
<swiper-item v-for="(item,index) in newList" :key="index">
<template v-if="item.list.length>0">
<scroll-view scroll-y="true" :style="'height:'+scrollH+'px'" @scrolltolower="loadMore(index)">
<block v-for="(item2,index2) in item.list" :key="index">
<!-- <commonList :item="item2" :index="index2" @follow="follow()" @doSupport="doSupport()">
</commonList> -->
<topicList :item="item2" :index="index2"></topicList>
</block>
<loadMore :loading="item.loading"></loadMore>
</scroll-view>
</template>
<template v-else>
<no-thing></no-thing>
</template>
</swiper-item>
</swiper>
</view>
</template>
<script>
import topicList from '@/components/news/topic-list';
import loadMore from '@/components/common/load-more';
const demo = [{
cover: '/static/demo/topicpic/1.jpeg',
title: '话题标题',
desc: '话题描述',
news_count: '10',
today_count: '10'
},
{
cover: '/static/demo/topicpic/1.jpeg',
title: '话题标题',
desc: '话题描述',
news_count: '10',
today_count: '10'
},
{
cover: '/static/demo/topicpic/1.jpeg',
title: '话题标题',
desc: '话题描述',
news_count: '10',
today_count: '10'
},
{
cover: '/static/demo/topicpic/1.jpeg',
title: '话题标题',
desc: '话题描述',
news_count: '10',
today_count: '10'
},
{
cover: '/static/demo/topicpic/1.jpeg',
title: '话题标题',
desc: '话题描述',
news_count: '10',
today_count: '10'
},
{
cover: '/static/demo/topicpic/1.jpeg',
title: '话题标题',
desc: '话题描述',
news_count: '10',
today_count: '10'
},
{
cover: '/static/demo/topicpic/1.jpeg',
title: '话题标题',
desc: '话题描述',
news_count: '10',
today_count: '10'
},
{
cover: '/static/demo/topicpic/1.jpeg',
title: '话题标题',
desc: '话题描述',
news_count: '10',
today_count: '10'
}
];
export default {
components: {
topicList,
loadMore
},
data() {
return {
scrollH: 200,
tabIndex: 0,
scrollInto: '',
newList: [],
tabBars: [{
name: '首页'
},
{
name: '体育'
},
{
name: '军事'
},
{
name: '热点'
},
{
name: '新闻'
},
{
name: '娱乐'
},
{
name: '电竞'
},
{
name: '国际'
},
{
name: '国家'
}
]
}
},
onNavigationBarSearchInputClicked() {
uni.navigateTo({
url: '../search/search'
})
},
onLoad() {
const res = uni.getSystemInfoSync();
this.scrollH = res.windowHeight - uni.upx2px(101);
this.getData();
},
onNavigationBarButtonTap() {
uni.navigateTo({
url:'../add-input/add-input'
})
},
methods: {
getData() {
let arr = [];
for (var i = 0; i < this.tabBars.length; i++) {
let obj = {
loading: '上拉加载更多',
list: []
}
if (i < 2) {
obj.list = demo;
}
arr.push(obj)
};
this.newList = arr;
},
follow(index) {
this.list[index].isFollow = true;
uni.showToast({
title: '关注成功'
})
},
doSupport(e) {
let obj = this.list[e.index];
if (obj.support.type === '') {
//无操作
obj.support[e.type + '_count']++;
} else if (obj.support.type === 'support' && e.type === 'unsupport') {
//之前是顶,顶减一,踩加一
obj.support.support_count--;
obj.support.unsupport_count++;
} else if (obj.support.type === 'unsupport' && e.type === 'support') {
//之前是踩,顶加一,踩减一
obj.support.support_count++;
obj.support.unsupport_count--;
}
obj.support.type = e.type;
let msg = e.type === 'support' ? '顶' : '踩';
uni.showToast({
title: msg + '成功'
})
},
doTab(index) {
this.tabIndex = index;
this.scrollInto = 'tab' + index;
},
onChange(e) {
this.doTab(e.detail.current);
},
loadMore(index) {
let item = this.newList[index];
if ((item.loading) !== '上拉加载更多') {
return;
}
item.loading = '加载中...';
setTimeout(() => {
item.list = [...item.list, ...item.list];
item.loading = '上拉加载更多';
}, 2000)
}
}
}
</script>
<style>
</style>
新建topic-detail页面
配置导航栏, 渐变式透明, 图标
{
"path" : "pages/topic-detail/topic-detail",
"style" :
{
"navigationBarTitleText": "",
"app-plus": {
"titleNView": {
"type": "transparent",
"buttons": [
{
"type": "menu"
}
]
}
}
}
}
导航到话题详情页, 传递json字符串对象, 接受数据转换object
<view class="flex align-center p-2" @click="openDetail(item)">
openDetail(item){
uni.navigateTo({
url:'../topic-detail/topic-detail?detail='+JSON.stringify(item)
})
}
onLoad(e){
if(e.detail){
var obj=JSON.parse(e.detail);
// console.log(obj);
}
},
图片模糊状态
静态开发
<view>
<view class="position-relative">
<image src="/static/demo/topicpic/1.jpeg" mode="aspectFill"
style="height: 300rpx;" class="w-100 filter"></image>
</view>
<view class=" px-2 bg-white position-relative" style="z-index: 10;">
<view class="flex align-center">
<image src="/static/demo/topicpic/1.jpeg" class="w-100" style="height: 150rpx; width: 150rpx;
margin-top: -75rpx;"></image>
<text class="font-md">#话题标题#</text>
</view>
<view class="flex align-center font text-secondary mt-2">
<text class="mr-1">动态:0</text>
<text>今日:0</text>
</view>
<view class="font text-secondary">话题描述</view>
</view>
</view>
<style>
.filter{
filter: blur(10px);
}
</style>
新建topic-info组件
动态替换, 数据渲染
<template>
<view>
<view class="position-relative">
<image :src="info.cover" mode="aspectFill" style="height: 300rpx;" class="w-100 filter">
</image>
</view>
<view class=" px-2 bg-white position-relative pb-1" style="z-index: 10;">
<view class="flex align-center">
<image :src="info.cover" class="w-100" style="height: 150rpx; width: 150rpx;
margin-top: -75rpx;"></image>
<text class="font-md">{{info.title}}</text>
</view>
<view class="flex align-center font text-secondary mt-2">
<text class="mr-1">动态:{{info.news_count}}</text>
<text>今日:{{info.today_count}}</text>
</view>
<view class="font text-secondary">{{info.desc}}</view>
</view>
</view>
</template>
<script>
export default {
props:['info']
}
</script>
<style>
</style>
<topicInfo :info="info"></topicInfo>
<divider></divider>
<view class="flex p-2 border-bottom">
<text class="iconfont icon-xihuan text-main"></text>
<text class="font text-darker text-ellipsis">
【新人必读】uni-app实战项目第四季社区交友开发
</text>
</view>
<view class="flex p-2 border-bottom">
<text class="iconfont icon-xihuan text-main"></text>
<text class="font text-darker text-ellipsis">
【新人必读】uni-app实战项目第四季社区交友开发
</text>
</view>
优化精华帖子列表开发, 用循环
<block v-for="(item,index) in hotList" :key="index">
<view class="flex p-2 border-bottom">
<text class="iconfont icon-xihuan text-main"></text>
<text class="font text-darker text-ellipsis">
{{item.title}}
</text>
</view>
</block>
hotList: [{
title: '【新人必读】uni-app实战项目第四季社区交友开发'
},
{
title: '【新人必读】Hillky社区规范'
}
]
tab选项开发
<view class="flex align-center py-2">
<text class="flex-1 flex align-center justify-center font-weight-bold font-lg text-main">默认</text>
<text class="flex-1 flex align-center justify-center font-weight-bold font-md text-dark">最新</text>
</view>
列表开发, 引入公共列表, 声明两个数组list1,2
<block v-for="(item,index) in list1" :key="index">
<commonList :item="item" :index="index"></commonList>
</block>
tab切换实现
<view class="flex align-center py-2" >
<text class="flex-1 flex align-center justify-center"
v-for="(item,index) in tabBar" :key="index"
:class="tabIndex===index?'font-lg text-main font-weight-bold':'font-md text-dark'"
@click="changeTab(index)">{{item.name}}</text>
</view>
tabIndex: 0,
tabBar:[
{
name:'默认'
},
{
name:'最新'
}
]
用计算属性切换数组
computed: {
listData() {
if (this.tabIndex === 0) {
return this.list1;
}
if (this.tabIndex === 1) {
return this.list2;
}
}
},
遍历列表要分割线
判断数组长度, 用nothing组件
<template v-if="listData.length>0">
<block v-for="(item,index) in listData" :key="index">
<commonList :item="item" :index="index"></commonList>
<divider></divider>
</block>
</template>
<template v-else>
<no-thing></no-thing>
</template>
触底事件
onReachBottom() {
this.loadMore();
},
引入上拉加载组件
import loadMore from '@/components/common/load-more';
<loadMore :loading="loadText"></loadMore>
loadText计算属性区分是哪个列表的上拉加载
loadText1:'上拉加载更多',
loadText2:'上拉加载更多'
loadText(){
if(this.tabIndex===0){
return this.loadText1
}else{
return this.loadText2
}
}
上拉加载更多事件
loadMore(){
let index =this.tabIndex
if(this['loadText'+(index+1)]!=='上拉加载更多'){
return;
}
this['loadText'+(index+1)]='加载中...'
setTimeout(()=>{
this['loadText'+(index+1)]='上拉加载更多';
this['list'+(index+1)]=[...this['list'+(index+1)],...this['list'+(index+1)]];
},2000)
}
配置顶部导航栏, 配置左右图标
{
"path": "pages/msg/msg",
"style": {
"navigationBarTitleText": "消息列表",
"app-plus": {
"titleNView": {
"buttons": [
{
"color": "#333333",
"colorPressed": "#FD597C",
"float": "left",
"fontSize": "20px",
"fontSrc": "/static/iconfont.ttf",
"text": "\ue611"
},
{
"color": "#333333",
"colorPressed": "#FD597C",
"float": "right",
"fontSize": "20px",
"fontSrc": "/static/iconfont.ttf",
"text": "\ue649"
}
]
}
}
}
静态开发
引入数字脚标组件
<view>
<view class="flex align-center justify-center p-2 border-bottom border-light-secondary">
<image src="../../static/default.jpg" style="height: 80rpx; width: 80rpx;" class="rounded-circle mr-2"></image>
<view class="flex-column flex flex-1">
<view class="flex align-center justify-between">
<text class="font-md text-dark">昵称</text>
<text class="font-sm text-secondary">17:00</text>
</view>
<view class="py-1 flex align-center justify-between">
<view class=" font-sm text-secondary">内容</view>
<uni-badge text="1" type="error"></uni-badge>
</view>
</view>
</view>
</view>
优化内容, 添加对应css和最大宽度
<view class="pt-1 flex align-center justify-between">
<view class=" font-sm text-secondary text-ellipsis" style="max-width: 500rpx;">内容内容内容内容内容内容内容内容内容内容内容内容内容</view>
<uni-badge text="1" type="error"></uni-badge>
</view>
封装数据,封装组件, 时间使用在线时间戳
引入time.js, 使用对应库,使用过滤器
import $T from '@/common/time.js'
filters:{
formatTime(value){
return $T.gettime(value);
}
},
组件分离
<block v-for="(item,index) in list" :key="index">
<msgList :item="item" :index="index"></msgList>
</block>
<template>
<view class="flex align-center justify-center p-2 border-bottom border-light-secondary">
<image :src="item.avatar" style="height: 80rpx; width: 80rpx;" class="rounded-circle mr-2"></image>
<view class="flex-column flex flex-1">
<view class="flex align-center justify-between">
<text class="font-md text-dark">{{item.username}}</text>
<text class="font-sm text-secondary">{{item.update_time | formatTime}}</text>
</view>
<view class="pt-1 flex align-center justify-between">
<view class=" font-sm text-secondary text-ellipsis" style="max-width: 500rpx;">{{item.data}}</view>
<uni-badge :text="item.noread" type="error"></uni-badge>
</view>
</view>
</view>
</template>
<script>
import $T from '@/common/time.js'
export default {
props:{
item:Object,
index:Number
},
filters: {
formatTime(value){
// console.log(value);
return $T.gettime(value);
}
}
}
</script>
<style>
</style>
page.json配置
"enablePullDownRefresh": true,
监听下拉刷新, 写入方法
onPullDownRefresh() {
this.refresh();
},
refresh(){
setTimeout(()=>{
this.list=demo;
uni.stopPullDownRefresh();
},2000);
}
引入nothing组件, v-if判断
<template>
<view>
<template v-if="this.list.length>0">
<block v-for="(item,index) in list" :key="index">
<msgList :item="item" :index="index"></msgList>
</block>
</template>
<template v-else>
<no-thing></no-thing>
</template>
</view>
</template>
<script>
const demo=[{
avatar:'../../static/default.jpg',
username:'昵称',
update_time:1648458088,
data:'内容内容内容内容内容内容内容内容内容内容内容内容内容',
noread:20
},
{
avatar:'../../static/default.jpg',
username:'昵称',
update_time:1648458088,
data:'内容内容内容内容内容内容内容内容内容内容内容内容内容',
noread:20
},
{
avatar:'../../static/default.jpg',
username:'昵称',
update_time:1648458088,
data:'内容内容内容内容内容内容内容内容内容内容内容内容内容',
noread:20
},
{
avatar:'../../static/default.jpg',
username:'昵称',
update_time:1648458088,
data:'内容内容内容内容内容内容内容内容内容内容内容内容内容',
noread:20
}];
import msgList from '@/components/msg/msg-list';
export default {
components:{
msgList
},
data() {
return {
list:[]
}
},
onLoad() {
this.list=demo;
console.log(this.list);
},
onPullDownRefresh() {
this.refresh();
},
methods: {
refresh(){
setTimeout(()=>{
this.list=[...this.list,...this.list];
uni.stopPullDownRefresh();
},2000);
}
}
}
</script>
<style>
</style>
使用uni-poup组件,
监听原生导航栏按钮事件,弹出弹出层
<uni-popup ref="popup" type="top" background-color="#fff">123</uni-popup>
onNavigationBarButtonTap(e) {
switch (e.index){
case 0:
break;
case 1:
this.$refs.popup.open();
break;
}
},
静态开发
添加点击事件
<uni-popup ref="popup" type="top" background-color="#fff">
<view class="flex justify-center align-center font-md border-bottom py-2" hover-class="bg-light"
@click="popupEvent('findFriend')">
<text class="iconfont icon-sousuo mr-2" ></text>搜索好友
</view>
<view class="flex justify-center align-center font-md py-2" hover-class="bg-light"
@click="popupEvent('deleteList')">
<text class="iconfont icon-shanchu mr-2"></text>删除列表
</view>
</uni-popup>
popupEvent(event){
switch (event){
case 'findFriend':
{
console.log('findFriend');
this.$refs.popup.close();
break;
}
case 'deleteList':
{
console.log('deleteList');
this.$refs.popup.close();
break;
}
}
}
新建user-list, 导航进入该页面
onNavigationBarButtonTap(e) {
switch (e.index){
case 0:
uni.navigateTo({
url:'../user-list/user-list'
})
break;
case 1:
this.$refs.popup.open();
break;
}
},
配置page.json
{
"path": "pages/user-list/user-list",
"style": {
"navigationBarTitleText": "",
"app-plus": {
"animationType": "slide-in-left",
"titleNView": {
"autoBackButton": true,
"searchInput": {
"align": "center",
"backgroundColor": "#F5F4F2",
"borderRadius": "4px",
"disabled": true,
"placeholder": "搜索用户",
"placeholderColor": "#6D6C67"
},
"buttons": [{
"color": "#333333",
"colorPressed": "#FD597C",
"float": "right",
"fontSize": "14px",
"text": "取消"
}]
}
}
}
监听点击输入框事件, 监听取消按钮事件
onNavigationBarSearchInputClicked() {
// console.log('跳转');
uni.navigateTo({
url:'../search/search'
})
},
onNavigationBarButtonTap() {
uni.navigateBack({
delta: 1
})
},
引入tabBar导航
条件渲染数字
<view class="flex align-center py-2">
<text class="flex-1 flex align-center justify-center" v-for="(item,index) in tabBar" :key="index"
:class="tabIndex===index?'font-lg text-main font-weight-bold':'font-md text-dark'"
@click="changeTab(index)">{{item.name}} <text v-if="item.num>0" class="ml-1">{{item.num}}</text> </text>
</view>
引入首页的scrollview, 下拉, 对应修改和引入
列表样式静态开发
<swiper duration=150 @change="onChange" :current="tabIndex" :style="'height:'+scrollH+'px'">
<swiper-item v-for="(item,index) in newList" :key="index">
<template v-if="item.list.length>0">
<scroll-view scroll-y="true" :style="'height:'+scrollH+'px'" @scrolltolower="loadMore(index)">
<block v-for="(item2,index2) in item.list" :key="index2">
<view class="flex align-center p-2 border-bottom border-light-secondary">
<image src="../../static/default.jpg" class="rounded-circle mr-2" style="width: 100rpx; height: 100rpx;"></image>
<view class="flex flex-column flex-1">
<text class="font-md text-dark">昵称</text>
<text class="font-sm mt-1">性别</text>
</view>
<view class="uni-icon uni-icon-checkbox-filled text-light-muted "></view>
</view>
</block>
<loadMore :loading="item.loading"></loadMore>
</scroll-view>
</template>
<template v-else>
<no-thing></no-thing>
</template>
</swiper-item>
</swiper>
getData() {
let arr = [];
for (var i = 0; i < this.tabBars.length; i++) {
let obj = {
loading: '上拉加载更多',
list: []
}
if (i < 2) {
obj.list = [1,2,3,4];
}
arr.push(obj)
};
this.newList = arr;
},
changeTab(index){
this.tabIndex=index;
},
onChange(e){
this.changeTab(e.detail.current);
},
loadMore(index) {
let item = this.newList[index];
if ((item.loading) !== '上拉加载更多') {
return;
}
item.loading = '加载中...';
setTimeout(() => {
item.list = [...item.list, ...item.list];
item.loading = '上拉加载更多';
}, 2000)
}
使用uni-badge, 插入图标, 优化性别显示
<view>
<text class="iconfont icon-nv text-secondary"style="font-size: 18rpx;"></text>
<uni-badge type="error" text="24">
</uni-badge>
</view>
测试demo数据
const demo=[{
avatar:'../../static/default.jpg',
username:'昵称',
sex:1,
age:24,
isFollow:false
},
{
avatar:'../../static/default.jpg',
username:'昵称',
sex:2,
age:24,
isFollow:true
}
];
数据渲染,动态绑定性别class, 点击灰色
封装组件, 引入组件
<template>
<view>
<view class="flex align-center p-2 border-bottom border-light-secondary " hover-class="bg-light">
<image :src="item.avatar" class="rounded-circle mr-2" style="width: 100rpx; height: 100rpx;"></image>
<view class="flex flex-column flex-1">
<text class="font-md text-dark">{{item.username}}</text>
<view v-if="item.sex>0">
<text class="iconfont text-secondary" :class="item.sex===1?'icon-nv':'icon-nan'" style="font-size: 18rpx;"></text>
<uni-badge :type="item.sex===1?'error':'primary'" :text="item.age">
</uni-badge>
</view>
</view>
<view class="uni-icon uni-icon-checkbox-filled " :class="item.isFollow?'text-light-muted':'text-main'"></view>
</view>
</view>
</template>
<script>
export default {
name:"user-list",
props:{
item:Object,
index:Number
},
data() {
return {
};
}
}
</script>
<style>
</style>
import userList from '@/components/user-list/user-list.vue';
<userList :item="item2" :index="index2"></userList>
优化scrollH问题,引入首页方法
onLoad() {
this.getData();
const res = uni.getSystemInfoSync();
this.scrollH = res.windowHeight - uni.upx2px(101);
// console.log(this.scrollH);
},
隐藏上拉加载组件
<loadMore :loading="item.loading" v-if="item.list.length>10"></loadMore>
新建页面user-chat, 导航进入
配置导航栏
{
"path" : "pages/user-chat/user-chat",
"style" :{
"app-plus": {
"titleNView": {
"buttons": [
{
"color": "#333333",
"colorPressed": "#FD597C",
"float": "right",
"fontSize": "20px",
"fontSrc": "/static/iconfont.ttf",
"text": "\ue628"
}
]
}
}
}
底部操作条开发
<view class="flex fixed-bottom align-center bg-white border-top" style="height: 100rpx;">
<view class="flex-1">
<input type="text" placeholder="请文明发言" class="rounded ml-2 bg-light p-1" />
</view>
<view class="iconfont icon-fabu flex align-center justify-center font-lg animated" style="width: 100rpx;"
hover-class="jello text-main"></view>
</view>
scrollH获取
<scroll-view :style="'height: '+this.scrollH+'px;'" scroll-y="true">
<view v-for="i in 100" :key="i">{{i}}</view>
</scroll-view>
onLoad() {
const res = uni.getSystemInfoSync();
this.scrollH = res.windowHeight - uni.upx2px(101);
},
聊天列表组件开发
右边气泡的开发
<scroll-view :style="'height: '+this.scrollH+'px;'" scroll-y="true">
<view class="flex align-start px-2 my-2">
<image src="../../static/default.jpg" class="rounded-circle"
style="height: 100rpx; width: 100rpx;"></image>
<view class="bg-light mx-2 p-2 rounded mt-1" style="max-width: 400rpx; min-width: 100rpx;">
你好啊
</view>
</view>
<view class="flex align-start px-2 my-2" style="flex-direction: row-reverse;">
<image src="../../static/default.jpg" class="rounded-circle"
style="height: 100rpx; width: 100rpx;"></image>
<view class="bg-light mx-2 p-2 rounded mt-1" style="max-width: 400rpx; min-width: 100rpx;">
你好啊
</view>
</view>
</scroll-view>
新建user-chat-list, 封装数据
计算属性, 是否本人
isShelf(){
return this.item.userId===1
},
时间显示开发
<view class="my-2 flex align-center justify-center text-secondary font-sm">
{{shortTime}}
</view>
引入time.js, 优化时间显示, 用计算属性
shortTime(){
return $T.getChatTime(this.item.create_time,this.preTime);
}
<userChatList :item="item" :index="index" :preTime=" index>0 ? list[index-1].create_time : 0"></userChatList>
输入框绑定内容, 绑定发送事件, 发送功能实现
sendMessage(){
let obj={
userId:1,
avatar: '../../static/default.jpg',
data: this.content,
type: 'text',
create_time: (new Date()).getTime()
};
if(obj.data===''){
uni.showToast({
title:"请输入内容"
});
return;
}
this.list.push(obj);
this.content=''
}
优化功能, 最小宽度去掉, 键盘推页面false, 清空输入框, 丢弃scrollH
<input type="text" placeholder="请文明发言" class="rounded ml-2 bg-light p-1" v-model="content" adjust-position="false" />
<scroll-view style="position: absolute; left: 0 ; top: 0; right: 0; bottom: 100rpx;" scroll-y="true">
滚动到底部实现
<scroll-view style="position: absolute; left: 0 ; top: 0; right: 0; bottom: 100rpx;" scroll-y="true"
:scroll-into-view="scrollInto" scroll-with-animation>
<block v-for="(item,index) in list" :key="index">
<userChatList :id="'chat'+index" :item="item" :index="index" :preTime=" index>0 ? list[index-1].create_time : 0"></userChatList>
</block>
</scroll-view>
pageToBottom(){
let lastIndex=this.list.length-1;
if(lastIndex < 0){
return
}
this.scrollInto='chat'+lastIndex;
console.log(this.scrollInto);
}
onReady() {
this.pageToBottom();
},
添加搜索标识
onNavigationBarSearchInputClicked() {
uni.navigateTo({
url: '../search/search?type=post'
})
},
获取类型, 修改搜索占位, 添加如果说是app端
if (e.type) {
this.type = e.type;
}
let pageTitle='';
switch (this.type) {
case 'post':
pageTitle='搜索帖子'
break;
case 'topic':
pageTitle='搜索话题'
break;
case 'friend':
pageTitle='搜索好友'
break;
}
console.log(this.type);
// #ifdef APP-PLUS
let currentWebview=this.$scope.$getAppWebview();
let tn=currentWebview.getStyle().titleNView;
tn.searchInput.placeholder=pageTitle;
console.log(tn);
currentWebview.setStyle({
titleNView:tn
})
// #e
搜索结果完善优化, 根据不同搜索,组件,数据都要不同
searchEvent() {
uni.hideKeyboard();
uni.showLoading({
title: '加载中'
})
setTimeout(() => {
switch (this.type) {
case 'post':
this.serachList = demo1;
break;
case 'topic':
this.serachList = demo2;
break;
case 'friend':
this.serachList = demo3;
break;
}
uni.hideLoading();
}, 3000)
},
<block v-for="(item,index) in serachList" :key="index">
<template v-if="type==='post'">
<commonList :item="item" :index="index"></commonList>
</template>
<template v-if="type==='topic'">
<topicList :item="item" :index="index"></topicList>
</template>
<template v-if="type==='friend'">
<userList :item="item" :index="index"></userList>
</template>
</block>
新建页面,detail, 导航入口
,{
"path" : "pages/detail/detail",
"style" :
{
"app-plus": {
"titleNView": {
"buttons": [
{
"float": "right",
"type": "menu"
}
]
}
}
}
}
把对象作为参数传过去,初始化
openDetail(){
console.log("打开详情页");
uni.navigateTo({
url:'../../pages/detail/detail?detail='+JSON.stringify(this.item)
})
},
onLoad(e){
if(e.detail){
this.__init(JSON.parse(e.detail));
}
},
methods: {
__init(data){
uni.setNavigationBarTitle({
title:data.title
})
}
}
修改公共列表组件, 添加isDetail, prop, 评论和分享功能
props: {
item: Object,
index: {
type: Number,
default: -1
},
isDetail: {
type: Boolean,
default: false
}
},
doComment(){
if(!this.isDetail){
return this.openDetail();
}
this.$emit('doComment');
},
doShare(){
if(!this.isDetail){
return this.openDetail();
}
this.$emit('doShare');
}
<commonList :item="this.item" :isDetail="true" @doComment="doComment" @doShare="doShare">
帖子详请
</commonList>
关注事件, 修改index默认值
follow() {
this.info.isFollow = true;
}
index: {
type: Number,
default: -1
}
顶踩方法改写
doSupport(e) {
let obj = this.info
if (obj.support.type === e.type) {
uni.showToast({
title: '你已经操作过了'
})
return;
}
if (obj.support.type === '') {
//无操作
obj.support[e.type + '_count']++;
} else if (obj.support.type === 'support' && e.type === 'unsupport') {
//之前是顶,顶减一,踩加一
obj.support.support_count--;
obj.support.unsupport_count++;
} else if (obj.support.type === 'unsupport' && e.type === 'support') {
//之前是踩,顶加一,踩减一
obj.support.support_count++;
obj.support.unsupport_count--;
}
obj.support.type = e.type;
let msg = e.type === 'support' ? '顶' : '踩';
uni.showToast({
title: msg + '成功'
})
}
增加content,image
content:"Hillky正在开发ing.....",
images:[
{
url:"https://tupian.qqw21.com/article/UploadPic/2020-5/20205622141239876.jpg"
},
{
url:"https://tse1-mm.cn.bing.net/th/id/R-C.df1d553893d9b7888c725b8dbcbcf439?rik=hpbIzO6xZ3Qchw&riu=http%3a%2f%2fwww.chabeichong.com%2fimages%2f2016%2f11%2f12-04122113.jpg&ehk=%2fe971CgX%2bMeAgZuGCVac3td74wDOd1%2bWzz0q4IsP1Lc%3d&risl=&pid=ImgRaw&r=0&sres=1&sresct=1"
}
]
增加图片预览功能
<image v-for="(item,index) in info.images" :src="item.url" class="w-100" mode="widthFix" @click="preview(index)"></image>
preview(index){
// console.log(this.imageList);
uni.previewImage({
urls:this.imageList,
current:index
})
}
computed:{
imageList(){
return this.info.images.map(item=>item.url);
}
},
先对聊天室底部操作条的封装,测试是否成功
<template>
<view class="flex fixed-bottom align-center bg-white border-top" style="height: 100rpx;">
<view class="flex-1">
<input type="text" placeholder="请文明发言" class="rounded ml-2 bg-light p-1" v-model="content"
adjust-position="false" />
</view>
<view class="iconfont icon-fabu flex align-center justify-center font-lg animated" style="width: 100rpx;"
hover-class="jello text-main" @click="sendMessage"></view>
</view>
</template>
<script>
export default {
data(){
return{
content:''
}
},
methods:{
sendMessage(){
if (this.content === '') {
uni.showToast({
title: "请输入内容",
icon:"none"
});
return;
}
this.$emit('submit',this.content);
this.content = '';
}
}
}
</script>
<style>
</style>
<bottomBtn @submit="submit"></bottomBtn>
submit(content) {
let obj = {
userId: 1,
avatar: '../../static/default.jpg',
data: content,
type: 'text',
create_time: (new Date()).getTime()
};
this.list.push(obj);
this.pageToBottom();
},
再到detail页面使用该组件,需要占位
使用官方评论界面,进行静态开发
<view class="px-2">
<!-- 评论区 start -->
<view class="uni-comment">
<view class="uni-comment-list">
<view class="uni-comment-face" style="margin-top: 15rpx;">
<image src="https://img-cdn-qiniu.dcloud.net.cn/uniapp/images/uni@2x.png" mode="widthFix">
</image>
</view>
<view class="uni-comment-body">
<view class="uni-comment-top">
<text>网友</text>
</view>
<view class="uni-comment-date">
<text>08/10 08:12</text>
</view>
<view class="uni-comment-content">很酷的HBuilderX和uni-app,开发一次既能生成小程序,又能生成App</view>
</view>
</view>
</view>
</view>
弹出层,底部弹出, 监听导航栏按钮触发, 层级关系的修改-z-index
<uni-popup ref="popup" type="bottom" background-color="#fff">
<view style="height: 300rpx;">
123
</view>
</uni-popup>
onNavigationBarButtonTap() {
this.$refs.popup.open();
},
uni-popup {
position: fixed;
/* #ifndef APP-NVUE */
z-index: 9999;
监听返回事件,隐藏弹出层
onBackPress() {
this.$refs.popup.close();
},
静态开发
<view class="font-md border-bottom border-light-secondary text-center py-2">分享到</view>
<view class="flex align-center ">
<view class="flex flex-1 flex-column justify-center align-center py-2">
<view class="iconfont icon-QQ rounded-circle bg-primary text-white flex align-center justify-center font-lg" style="width: 100rpx; height: 100rpx;"></view>
<view class="font mt-1 text-light-muted">QQ好友</view>
</view>
...
</view>
<view class="font-md border-top border-light-secondary text-center py-2">取消</view>
遍历图标
<block v-for="(item,index) in btnList" :key="index">
<view class="flex flex-1 flex-column justify-center align-center py-2">
<view
class="iconfont rounded-circle bg-primary text-white flex align-center justify-center font-lg"
:class="item.icon+' '+item.color "
style="width: 100rpx; height: 100rpx;"></view>
<view class="font mt-1 text-light-muted">{{item.name}}</view>
</view>
</block>
封装组件 more-share.vue
<template>
<view>
<uni-popup ref="popup" type="bottom" background-color="#fff">
<view class="font-md border-bottom border-light-secondary text-center py-2">分享到</view>
<view class="flex align-center ">
<block v-for="(item,index) in btnList" :key="index">
<view class="flex flex-1 flex-column justify-center align-center py-2">
<view
class="iconfont rounded-circle bg-primary text-white flex align-center justify-center font-lg"
:class="item.icon+' '+item.color " style="width: 100rpx; height: 100rpx;"></view>
<view class="font mt-1 text-light-muted">{{item.name}}</view>
</view>
</block>
</view>
<view class="font-md border-top border-light-secondary text-center py-2">取消</view>
</uni-popup>
</view>
</template>
<script>
export default {
data(){
return{
btnList: [{
"icon": 'icon-QQ',
"color": 'bg-primary',
"name": 'QQ好友'
},
{
"icon": 'icon-QQ',
"color": 'bg-primary',
"name": 'QQ好友'
},
{
"icon": 'icon-QQ',
"color": 'bg-primary',
"name": 'QQ好友'
},
{
"icon": 'icon-QQ',
"color": 'bg-primary',
"name": 'QQ好友'
}
],
}
},
methods:{
open(){
this.$refs.popup.open();
},
close(){
this.$refs.popup.close();
}
}
}
</script>
<style>
</style>
<moreShare ref="share"></moreShare>
onNavigationBarButtonTap() {
this.$refs.share.open();
},
onBackPress() {
this.$refs.share.close();
},
用官方组件分享动态渲染数据, 子组件用created()
created(){
uni.getProvider({
service: 'share',
success: (e) => {
console.log(e);
let data = []
for (let i = 0; i < e.provider.length; i++) {
switch (e.provider[i]) {
case 'weixin':
data.push({
name: '微信好友',
id: 'weixin',
icon:'icon-weixin',
color:'bg-success',
sort:0
})
data.push({
name: '朋友圈',
id: 'weixin',
icon:'icon-huati',
color:'bg-dark',
type:'WXSenceTimeline',
sort:1
})
break;
case 'sinaweibo':
data.push({
name: '新浪微博',
icon:'icon-xinlangweibo',
color:'bg-danger',
id: 'sinaweibo',
sort:2
})
break;
case 'qq':
data.push({
name: 'QQ好友',
id: 'qq',
icon:'icon-QQ',
color:'bg-primary',
sort:3
})
break;
default:
break;
}
}
this.providerList = data.sort((x,y) => {
return x.sort - y.sort
});
},
fail: (e) => {
console.log('获取分享通道失败', e);
uni.showModal({
content:'获取分享通道失败',
showCancel:false
})
}
});
},
分享方法
async share(e) {
console.log('分享通道:'+ e.id +'; 分享类型:' + this.shareType);
if(!this.shareText && (this.shareType === 1 || this.shareType === 0)){
uni.showModal({
content:'分享内容不能为空',
showCancel:false
})
return;
}
if(!this.image && (this.shareType === 2 || this.shareType === 0)){
uni.showModal({
content:'分享图片不能为空',
showCancel:false
})
return;
}
let shareOPtions = {
provider: e.id,
scene: e.type && e.type === 'WXSenceTimeline' ? 'WXSenceTimeline' : 'WXSceneSession', //WXSceneSession”分享到聊天界面,“WXSenceTimeline”分享到朋友圈,“WXSceneFavorite”分享到微信收藏
type: this.shareType,
success: (e) => {
console.log('success', e);
uni.showModal({
content: '已分享',
showCancel:false
})
},
fail: (e) => {
console.log('fail', e)
uni.showModal({
content: e.errMsg,
showCancel:false
})
},
complete:function(){
console.log('分享操作结束!')
}
}
switch (this.shareType){
case 0:
shareOPtions.summary = this.shareText;
shareOPtions.imageUrl = this.image;
shareOPtions.title = '欢迎体验uniapp';
shareOPtions.href = 'https://uniapp.dcloud.io';
break;
case 1:
shareOPtions.summary = this.shareText;
break;
case 2:
shareOPtions.imageUrl = this.image;
break;
case 5:
shareOPtions.imageUrl = this.image ? this.image : 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/b6304f00-5168-11eb-bd01-97bc1429a9ff.png'
shareOPtions.title = '欢迎体验uniapp';
shareOPtions.miniProgram = {
id:'gh_33446d7f7a26',
path:'/pages/tabBar/component/component',
webUrl:'https://uniapp.dcloud.io',
type:0
};
break;
default:
break;
}
if(shareOPtions.type === 0 && plus.os.name === 'iOS'){//如果是图文分享,且是ios平台,则压缩图片
shareOPtions.imageUrl = await this.compress();
}
if(shareOPtions.type === 1 && shareOPtions.provider === 'qq'){//如果是分享文字到qq,则必须加上href和title
shareOPtions.href = 'https://uniapp.dcloud.io';
shareOPtions.title = '欢迎体验uniapp';
}
uni.share(shareOPtions);
},
配置page.json
{
"path": "pages/my/my",
"style": {
"navigationBarTitleText": "我的",
"app-plus": {
"titleNView": {
"buttons": [
{
"type": "menu"
}
]
}
}
}
},
静态开发第一个view
<view class="flex align-center p-2">
<image src="../../static/default.jpg" style="width: 100rpx; height: 100rpx;" class="rounded-circle"></image>
<view class="flex flex-column flex-1 px-2">
<text class="font-lg font-weight-bold text-dark ">昵称</text>
<text class="font text-muted mt-1"> 总帖子10 今日发帖0</text>
</view>
<view class="iconfont icon-jinru"></view>
</view>
第二个view开发,使用遍历方式
<view class="flex align-center px-3 py-2">
<view class="flex-1 flex-column flex align-center justify-center" v-for="(item,index) in this.itemList " :key="index">
<text class="font-lg text-dark">{{item.num}}</text>
<text class="text-muted fony">{{item.name}}</text>
</view>
</view>
第三个view, 广告位的开发
<view class="px-3 py-2">
<image src="/static/demo/banner1.jpg" style="height: 300rpx; width: 100%;" mode="aspectFill" class="rounded"></image>
</view>
引入uni-list-item
<uni-list-item title="浏览历史" :showExtraIcon="true" :extra-icon="extraIcon1" link :border="false"></uni-list-item>
<uni-list-item title="社区认证" :showExtraIcon="true" :extra-icon="extraIcon2" link :border="false">
</uni-list-item>
<uni-list-item title="审核帖子" :show-extra-icon="true" link :border="false">
<text slot="icon" class="iconfont icon-keyboard font-lg"></text>
</uni-list-item>
import uniListItem from '@/components/uni-list-item/uni-list-item.vue';
extraIcon1: {
type: 'eye',
color: '#000000',
size: 20
},
extraIcon2: {
type: 'vip',
color: '#000000',
size: 20
},
新建页面,导航进入
退出登录静态开发
<template>
<view>
<uni-list-item title="账号与安全" link :border="false" ></uni-list-item>
<uni-list-item title="资料编辑" link :border="false" ></uni-list-item>
<uni-list-item title="清楚缓存" link :border="false"></uni-list-item>
<uni-list-item title="意见反馈" link :border="false"></uni-list-item>
<uni-list-item title="关于社区" link :border="false"></uni-list-item>
<view class="py-2 px-3">
<button class="bg-main text-white" style="border-radius: 50rpx;">退出登录</button>
</view>
</view>
</template>
<script>
import uniListItem from '@/components/uni-list-item/uni-list-item.vue';
export default {
components:{
uniListItem
},
data() {
return {
}
},
methods: {
}
}
</script>
<style>
</style>
新建页面,导航进入
<uni-list-item title="账号与安全" link :border="false" @click="open"></uni-list-item>
uni.navigateTo({
url:'../userPassword/userPassword'
})
静态页面开发
<view class="px-1">
<input class="uni-input" value="" type="text" placeholder="输入旧密码"/>
<input class="uni-input" value="" type="text" placeholder="输入新密码"/>
<input class="uni-input" value="" type="text" placeholder="输入确认密码"/>
<view class="py-2 px-3">
<button class="bg-main text-white" style="border-radius: 50rpx;">设置</button>
</view>
</view>
输入框绑定vue, disabled属性绑定
computed:{
disable(){
return this.oldPassword===''||this.newPassword===''||this.renewPassword==='';
}
},
<button class="bg-main text-white" style="border-radius: 50rpx;" :disabled="disable">设置</button>
验证功能实现
check(){
if(this.newPassword!==this.renewPassword){
uni.showToast({
title:'两次输入密码不一致',
icon:"none"
})
return false;
}
return true;
},
submit(){
if(this.check()){
console.log('提交成功');
}
}
新建页面, 配置page.json
,{
"path" : "pages/userEmail/userEmail",
"style" :
{
"navigationBarTitleText": "设置邮箱"
}
}
拼接字符串, 配置导航,引号不一样用1左边的那个,${}拼接
open(path){
uni.navigateTo({
url:`../${path}/${path}`,
fail(e) {
console.log(e);
}
})
}
静态页面开发,与修改密码差不多的功能,禁用
<template>
<view>
<input type="text" class="uni-input" placeholder="请输入邮箱" v-model="email"/>
<input type="text" class="uni-input" placeholder="请输入密码" v-model="password"/>
<button class="bg-main text-white mt-1" style="border-radius: 50rpx;" :disabled="disable">绑定邮箱</button>
</view>
</template>
<script>
export default {
data() {
return {
email:'',
password:''
}
},
computed:{
disable(){
return this.email===''||this.password===''
}
},
methods: {
}
}
</script>
<style>
</style>
利用正则表达式完成邮箱功能验证js常用正则表达式
methods: {
check(){
var ePattern = /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/;
if(!ePattern.test(this.email)){
uni.showToast({
title:'邮箱格式不正确',
icon:"none"
})
return false;
}
return true;
},
submit(){
if(this.check()){
console.log("提交成功");
}
return;
}
}
新建页面,配置page.json,配置导航
,{
"path" : "pages/userInfo/userInfo",
"style" :
{
"navigationBarTitleText": "资料编辑"
}
}
使用uni-list-item实现静态页面,使用插槽修改
<template>
<view>
<uni-list-item title="头像" :border="false" >
<view slot="right" class="flex">
<image class="rounded" style="width: 100rpx; height: 100rpx;" src="../../static/default.jpg"></image>
<text class="flex align-center iconfont icon-bianji1 ml-2" ></text>
</view>
</uni-list-item>
<uni-list-item title="昵称" :border="false">
<view slot="right" class="flex ">
哈哈
<text class="flex align-center iconfont icon-bianji1 ml-2 " ></text>
</view>
</uni-list-item>
<uni-list-item title="性别" :border="false">
<view slot="right" class="flex ">
未知
<text class="flex align-center iconfont icon-bianji1 ml-2 " ></text>
</view>
</uni-list-item>
<uni-list-item title="生日" :border="false">
<view slot="right" class="flex ">
2021-4-1
<text class="flex align-center iconfont icon-bianji1 ml-2 " ></text>
</view>
</uni-list-item>
<uni-list-item title="情感" :border="false">
<view slot="right" class="flex ">
已婚
<text class="flex align-center iconfont icon-bianji1 ml-2 " ></text>
</view>
</uni-list-item>
<uni-list-item title="职业" :border="false">
<view slot="right" class="flex align-center">
程序员
<text class="flex align-center iconfont icon-bianji1 ml-2 " ></text>
</view>
</uni-list-item>
<uni-list-item title="家乡" :border="false">
<view slot="right" class="flex ">
广东广州
<text class="flex align-center iconfont icon-bianji1 ml-2 " ></text>
</view>
</uni-list-item>
<view class="py-2 px-3">
<button class="bg-main text-white" style="border-radius: 50rpx;">完成</button>
</view>
</view>
</template>
<script>
import uniListItem from '@/components/uni-list-item/uni-list-item.vue';
export default {
components:{
uniListItem
},
data() {
return {
}
},
methods: {
}
}
</script>
<style>
</style>
添加点击事件, 动态绑定userPIc
<uni-list-item title="头像" :border="false">
<view slot="right" class="flex">
<image class="rounded" style="width: 100rpx; height: 100rpx;" :src="userPic"></image>
<text class="flex align-center iconfont icon-bianji1 ml-2" @click="changePic" ></text>
</view>
</uni-list-item>
uni.chooseImage的使用, 修改头像功能实现
changePic(){
uni.chooseImage({
count:1,
sizeType:["compressed"],
sourceType:["album","camera"],
success:(e)=>{
this.userPic=e.tempFilePaths[0];
}
})
}
修改昵称,用输入框
<input v-model="nickname" class="text-right"/>
修改性别, 使用showActionSheet,使用计算属性渲染
<uni-list-item title="性别" :border="false">
<view slot="right" class="flex ">
{{sexText}}
<text class="flex align-center iconfont icon-bianji1 ml-2 " @click="changeSex"></text>
</view>
</uni-list-item>
const sexArray=["保密","男","女"];
sex:0,
sexText(){
return sexArray[this.sex];
},
changeSex(){
uni.showActionSheet({
itemList:sexArray,
success:(e)=>{
// this.sex=sexArray[e.tapIndex];
this.sex=e.tapIndex;
}
})
},
修改情感, 使用一样的方法
修改职业, 使用一样的方法
changeJob(){
uni.showActionSheet({
itemList:jobArray,
success:(e)=>{
this.job=jobArray[e.tapIndex];
}
})
}
使用picker,完成该功能
<picker mode="date" :value="birthday" @change="onDateChange">
<uni-list-item title="生日" :border="false">
<view slot="right" class="flex ">
{{birthday}}
<text class="flex align-center iconfont icon-bianji1 ml-2 "></text>
</view>
</uni-list-item>
</picker>
onDateChange(e){
this.birthday=e.detail.value;
}
使用官方组件mpvueCityPicker
<uni-list-item title="家乡" :border="false">
<view slot="right" class="flex ">
{{pickerText}}
<text class="flex align-center iconfont icon-bianji1 ml-2 " @click="showCityPicker"></text>
</view>
</uni-list-item>
<mpvue-city-picker :themeColor="themeColor" ref="mpvueCityPicker" :pickerValueDefault="cityPickerValueDefault"
@onConfirm="onConfirm"></mpvue-city-picker>
import mpvueCityPicker from '@/components/mpvue-citypicker/mpvueCityPicker.vue'
mpvueCityPicker
themeColor:'#007AFF',
cityPickerValueDefault:[0, 0, 1],
pickerText:'广东广州'
onBackPress() {
if (this.$refs.mpvueCityPicker.showPicker) {
this.$refs.mpvueCityPicker.pickerCancel();
return true;
}
},
onUnload() {
if (this.$refs.mpvueCityPicker.showPicker) {
this.$refs.mpvueCityPicker.pickerCancel()
}
},
onConfirm(e){
this.pickerText = e.label;
},
showCityPicker(){
this.$refs.mpvueCityPicker.show()
}
新建页面,配置page.json
使用组件uni-collapse
<view>
<uni-collapse accordion>
<uni-collapse-item title="默认开启" >
<text>折叠内容</text>
</uni-collapse-item>
<uni-collapse-item title="默认开启" >
<text>折叠内容</text>
</uni-collapse-item>
</uni-collapse>
<view class="py-2 px-3">
<button class="bg-main text-white" style="border-radius: 50rpx;">意见反馈</button>
</view>
</view>
新建页面,配置page.json
静态页面开发
<view>
<view class="flex align-center justify-center flex-column pt-4 pb-3">
<image src="../../static/common/nothing.png" style="width: 300rpx; height: 300rpx;" class="rounded-circle"></image>
<text class="font text-muted mt-2">version 1.0.1</text>
</view>
<uni-list-item title="新版本检测" :border="false" link></uni-list-item>
<uni-list-item title="社区用户协议" :border="false" link></uni-list-item>
</view>
新建页面,取消原生导航,导航进入
,{
"path" : "pages/login/login",
"style" :
{
"navigationBarTitleText": "",
"app-plus": {
"titleNView": false
}
}
}
<navigator url="../login/login">
<view class="flex align-center p-2" hover-class="bg-light">
<image src="../../static/default.jpg" style="width: 100rpx; height: 100rpx;" class="rounded-circle">
</image>
<view class="flex flex-column flex-1 px-2">
<text class="font-lg font-weight-bold text-dark ">昵称</text>
<text class="font text-muted mt-1"> 总帖子10 今日发帖0</text>
</view>
<view class="iconfont icon-jinru"></view>
</view>
</navigator>
x的样式开发
<view class="flex iconfont icon-guanbi algin-center justify-center font-lg " style="width: 100rpx; height: 100rpx;
position: fixed; left: 0;"
:style="'top:'+statusBarHeight+'px;'" hover-class="bg-light" @click="back"></view>
var res=uni.getSystemInfoSync();
this.statusBarHeight=res.statusBarHeight;
back(){
uni.navigateBack({
delta:1
})
}
对之前的开发进行修改,引用一个view代替状态栏即可
<template>
<view>
<view class="status_bar">
<!-- 这里是状态栏 -->
</view>
<view> 状态栏下的文字 </view>
</view>
</template>
<style>
.status_bar {
height: var(--status-bar-height);
width: 100%;
}
</style>
静态页面开发
<view class="flex iconfont icon-guanbi algin-center justify-center font-lg " style="width: 100rpx; height: 100rpx;
position: fixed; left: 0;" hover-class="bg-light" @click="back">
</view>
<view class="flex algin-center justify-center " style="margin-top: 300rpx;">
<text class="text-secondary " style="font-size: 60rpx;">账号密码登录</text>
</view>
<view class="flex algin-center border-bottom py-3 px-2 " style="margin-top: 100rpx;">
<input type="text" placeholder="昵称/手机号/邮箱" class="" />
</view>
<view class="flex algin-center border-bottom">
<input type="text" placeholder="请输入密码" class="py-3 px-2 flex-1" />
<text style="width: 150rpx;" class="text-muted py-3">忘记密码</text>
</view>
<view class="py-2 px-3 " style="margin-top: 100rpx;">
<button class="bg-main text-white" style="border-radius: 50rpx;">登录</button>
</view>
<view class="mt-5 flex algin-center justify-center">
<view class="text-primary pr-2">验证码登录</view>
<view class="text-secondary">|</view>
<view class="text-primary pl-2">登录遇到问题</view>
</view>
<view class="flex algin-center justify-center mt-5">
<view style="width: 100rpx; border-bottom: solid #ddd ;" class="mb-2" ></view>
<view class="text-muted mx-2">社交账号登录</view>
<view style="width: 100rpx; border-bottom: solid #ddd ; "class="mb-2" ></view>
</view>
静态开发图标列表
<view class="flex align-center px-5 py-3" style="padding-left: 100rpx; padding-right: 100rpx;">
<view class="flex-1 flex align-center justify-center">
<view
class="iconfont icon-QQ font-lg bg-primary text-white flex align-center justify-center rounded-circle "
style="width: 100rpx;height: 100rpx;">
</view>
</view>
<view class="flex-1 flex align-center justify-center ">
<view
class="iconfont icon-QQ font-lg bg-primary text-white flex align-center justify-center rounded-circle"
style="width: 100rpx;height: 100rpx;">
</view>
</view>
<view class="flex-1 flex align-center justify-center ">
<view
class="iconfont icon-QQ font-lg bg-primary text-white flex align-center justify-center rounded-circle"
style="width: 100rpx;height: 100rpx;">
</view>
</view>
</view>
<view class="flex algin-center justify-center px-5 text-muted">
注册即代表同意<text class="text-primary">《xxx社区协议》</text>
</view>
切换验证码的静态显示
<view class="flex algin-center justify-center " style="margin-top: 300rpx;">
<text class="text-secondary " style="font-size: 60rpx;">
{{status?'账号密码登录':'验证码登录'}}</text>
</view>
<template v-if="status">
<view class="flex algin-center border-bottom py-3 px-2 " style="margin-top: 100rpx;">
<input type="text" placeholder="昵称/手机号/邮箱" class="" />
</view>
<view class="flex algin-center border-bottom">
<input type="text" placeholder="请输入密码" class="py-3 px-2 flex-1" />
<text style="width: 150rpx;" class="text-muted py-3">忘记密码</text>
</view>
</template>
<template v-else>
<view class="flex algin-center border-bottom py-3 px-2 " style="margin-top: 100rpx;">
<text class="text-dark mr-2">+86</text>
<input type="text" placeholder="手机号" class="" />
</view>
<view class="flex algin-center border-bottom px-2">
<input type="text" placeholder="请输入验证码" class="py-3 px-2 flex-1" />
<text style="width: 200rpx;" class="text-white bg-main py-3 flex justify-center">获取验证码</text>
</view>
</template>
changeStatus(){
this.status=!this.status
}
输入框绑定内容,计算属性disable要对于账号密码与手机验证码都ok
<input type="text" placeholder="昵称/手机号/邮箱" v-model="username" class="" />
disabled(){
if((this.username===''||this.password==='')&&(this.phone===''||this.code==='')){
return true;
}
return false;
}
<button class="bg-main text-white" style="border-radius: 50rpx;" :disabled="disabled">登录</button>
切换登录方式要初始化表单
initForm(){
this.username='';
this.password='';
this.phone='';
this.code='';
},
changeStatus(){
this.initForm();
this.status=!this.status
}
获取验证码的功能, 倒计时的功能
getCode(){
if(this.codeTime>0){
return;
}
this.codeTime=60;
let timer=setInterval(()=>{
if(this.codeTime>=1){
this.codeTime--;
}else{
this.codeTime=0;
clearInterval(timer);
}
},1000)
}
<text style="width: 200rpx;" class="text-white py-3 flex justify-center" @click="getCode"
:class="codeTime>0?'bg-main-disable':'bg-main'">
{{codeTime>0?this.codeTime+'s':'获取验证码'}}</text>
验证手机号规则编写,用正则表达式验证, 提交方法绑定
getCode() {
if (this.codeTime > 0) {
return;
}
if(!this.validate()){
return;
}
this.codeTime = 60;
let timer = setInterval(() => {
if (this.codeTime >= 1) {
this.codeTime--;
} else {
this.codeTime = 0;
clearInterval(timer);
}
}, 1000)
},
validate() {
var mPattern = /^1[34578]\d{9}$/;
var flag = mPattern.test(this.phone)
// console.log(flag);
if (!flag) {
uni.showToast({
title: '手机格式不正确',
icon: "none"
})
return false;
}
return true;
},
submit() {
if(!this.validate()){
return;
}
console.log('验证成功');
this.validate();
}
新建other-login组件,实现第三方登录
<template>
<view class="flex align-center px-5 py-3" style="padding-left: 100rpx; padding-right: 100rpx;">
<view class="flex-1 flex align-center justify-center" v-for="(item,index) in this.resultList" :key
="index">
<view :class="item.icon+' '+item.bgColor"
class="iconfont font-lg text-white flex align-center justify-center rounded-circle "
style="width: 100rpx;height: 100rpx;">
</view>
</view>
</view>
</template>
<script>
export default{
data(){
return {
providerList: [],
resultList:[]
}
},
mounted() {
uni.getProvider({
service: 'oauth',
success: (result) => {
this.providerList = result.provider.map((value) => {
let providerName = '';
let icon='';
let bgColor='';
switch (value) {
case 'weixin':
providerName = '微信登录'
icon='icon-weixin';
bgColor='bg-success';
break;
case 'qq':
providerName = 'QQ登录';
icon='icon-QQ';
bgColor='bg-primary';
break;
case 'sinaweibo':
providerName = '新浪微博登录'
icon='icon-xinlangweibo';
bgColor='bg-danger';
break;
default:
break;
}
return {
name: providerName,
id: value,
icon:icon,
bgColor:bgColor
}
});
console.log(this.providerList);
for(var i=0;i<this.providerList.length;i++){
if(this.providerList[i].id==='weixin'||this.providerList[i].id==='qq'||
this.providerList[i].id==='sinaweibo'){
this.resultList.push(this.providerList[i]);
}
}
console.log(this.resultList);
},
fail: (error) => {
console.log('获取登录通道失败', error);
}
});
}
}
</script>
<style>
</style>
新建页面, 配置导航栏按钮,导航进入
,{
"path" : "pages/user-space/user-space",
"style" :
{
"navigationBarTitleText": "",
"app-plus": {
"titleNView": {
"titleText": "个人空间",
"buttons": [
{
"type": "menu"
}
]
}
}
}
}
openSapce() {
uni.navigateTo({
url:'../../pages/user-space/user-space'
})
},
静态开发
<view class="flex align-center justify-center p-3 border-bottom border-light-secondary">
<image src="../../static/default.jpg" class="rounded " style="width: 180rpx; height: 180rpx;"></image>
<view class="flex flex-column flex-1 align-center justify-center " >
<view class="flex align-center justify-center " style="width: 400rpx;">
<view class=" flex-1 flex align-center justify-center flex-column ">
<text class="font-lg font-weight-bold">1</text>
<text class="font text-muted">粉丝</text>
</view>
<view class=" flex flex-1 align-center justify-center flex-column">
<text class="font-lg font-weight-bold">1</text>
<text class="font text-muted">粉丝</text>
</view>
<view class=" flex flex-1 align-center justify-center flex-column">
<text class="font-lg font-weight-bold">1</text>
<text class="font text-muted">粉丝</text>
</view>
</view>
<view class="flex align-center justify-center pt-2">
<button class="bg-main text-white" style="width: 400rpx;">关注</button>
</view>
</view>
</view>
借用好友列表的tabBar
<view class="flex align-center py-4" style="height: 100rox;">
<text class="flex-1 flex align-center justify-center" v-for="(item,index) in tabBars" :key="index"
:class="tabIndex===index?'font-lg text-main font-weight-bold':'font-md text-dark'"
@click="changeTab(index)">{{item.name}}</text>
</view>
data() {
return {
tabIndex:0,
tabBars: [{
name: '主页',
},
{
name: '帖子',
},
{
name: '动态',
}
]
}
},
changeTab(index){
this.tabIndex=index;
}
主页的实现
<template v-if="tabIndex===0">
<view class="px-3 border-bottom py-2">
<view class="font-md">账号信息</view>
<view class="font">账号年龄:12个月</view>
<view class="font">账号id:1</view>
</view>
<view class="px-3 border-bottom py-2">
<view class="font-md">个人信息</view>
<view class="font">星座:天蝎座</view>
<view class="font">职业:IT</view>
<view class="font">故乡:中国</view>
<view class="font">情感:未婚</view>
</view>
</template>
<template v-else>
<text>帖子/动态</text>
</template>
帖子动态的实现, 引入commonlist组件,loadMore组件,引入方法,添加动画效果
<view class="animated fast fadeIn">
<block v-for="(item,index) in list" :key="index">
<commonList :item="item" :index="index" @follow="follow" @doSupport="doSupport">
</commonList>
<divider></divider>
</block>
<loadMore :loading="loading"></loadMore>
</view>
follow(index) {
let obj=this.tabBars[this.tabIndex].list[index];
obj.isFollow=true;
uni.showToast({
title: '关注成功'
})
},
doSupport(e) {
let obj=this.tabBars[this.tabIndex].list[e.index]
console.log(obj);
if (obj.support.type === '') {
//无操作
obj.support[e.type + '_count']++;
} else if (obj.support.type === 'support' && e.type === 'unsupport') {
//之前是顶,顶减一,踩加一
obj.support.support_count--;
obj.support.unsupport_count++;
} else if (obj.support.type === 'unsupport' && e.type === 'support') {
//之前是踩,顶加一,踩减一
obj.support.support_count++;
obj.support.unsupport_count--;
}
obj.support.type = e.type;
let msg = e.type === 'support' ? '顶' : '踩';
uni.showToast({
title: msg + '成功'
})
},
使用弹出层,进行修改即可
<uni-popup ref="popup" type="top" background-color="#fff">
<view class="flex justify-center align-center font-md border-bottom py-2" hover-class="bg-light"
@click="popupEvent('findFriend')">
<text class="iconfont icon-sousuo mr-2" ></text>加入黑名单
</view>
<view class="flex justify-center align-center font-md py-2" hover-class="bg-light"
@click="popupEvent('deleteList')">
<text class="iconfont icon-shanchu mr-2"></text>聊天
</view>
</uni-popup>
onNavigationBarButtonTap() {
this.$refs.popup.open();
},
==文档只有一些功能缺失,但源码已经完善所有功能,详情请点击码云地址下载源码学习 https://gitee.com/HelLichu/friend ==
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看rubyzip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于
我正在尝试使用ruby和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t
我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h
我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po