当前位置: 首页 > news >正文

郑州做网站找赢博科技大连建设网球场价格

郑州做网站找赢博科技,大连建设网球场价格,wordpress如何卡百度,重庆网站建设网站制作社区论坛项目多端开发实战 基于 UniAPP 社区论坛项目多端开发实战一、项目准备1.1 ThinkSNS 简介及相关文档1.2 使用 UniAPP 构建项目1.3 构建项目文件结构1.4 配置页面 TabBar 导航1.5 使用 npm 引入 uView UI 插件库 二、首页功能实现2.1 首页 header 广告位轮播图功能实现2.… 社区论坛项目多端开发实战 基于 UniAPP 社区论坛项目多端开发实战一、项目准备1.1 ThinkSNS 简介及相关文档1.2 使用 UniAPP 构建项目1.3 构建项目文件结构1.4 配置页面 TabBar 导航1.5 使用 npm 引入 uView UI 插件库 二、首页功能实现2.1 首页 header 广告位轮播图功能实现2.2 全局配置 WebView 展示第三方 web 页面2.3 uViewUI 网络 http 请求及 API 集中管理2.4 首页「推荐、资讯」平滑切换实现2.5 首页「推荐」瀑布流布局效果实现2.6 首页「推荐、资讯」轮播切换动态设置轮播器高度2.7 首页滚动状态下动态设置「推荐、资讯」位置及 NavBar 状态2.8 顶部下拉、底部置底请求更新数据 三、资讯动态相关页面功能实现3.1 使用 Grid 栅格布局实现动态瀑布流页面3.2 使用分包构建业务逻辑页面3.3 动态详情页面功能实现3.4 资讯详情页面富文本解析功能实现3.5 评论组件评论列表展示功能实现 四、用户注册、登陆业务功能实现4.1 用户登陆、注册功能组件实现及触发显示4.2 Vuex Storage 登陆状态管理4.3 个人中心页面基础功能实现4.4 用户设置页面基础功能实现 五、登陆态下业务功能实现5.1 使用 mixins 实现动态点赞、取消点赞功能5.2 登陆状态下评论组件点赞、评论功能实现5.3 UGC 动态发布功能实现 六、多端打包发布6.1 微信小程序打包发布6.2 H5 端相关兼容性开发及打包发布6.3 安卓云端体验版本打包发布 基于 UniAPP 社区论坛项目多端开发实战 学习目的 ​ 通过学习对标企业级「大前端」开发实践标准从零到一构建「社区论坛类」客户端应用项目全流程 输出相关 Web H5、微信小程序、安卓 APP、IOS APP 多客户端应用。 一、项目准备 1.1 ThinkSNS 简介及相关文档 ThinkSNS 简介 ThinkSNS 是智士软件旗下的开源微博、社交系统含微博、论坛、资讯、频道、商城、活动、定位、找人、直播、问答等一系列功能。 ThinkSNS 采用 PHPMySQL 技术核心微博、用户系统 多应用 多插件的产品模式拥有Web、H5、IOS、Android APP以及微信、支付宝、百度等小程序多端服务场景。 ThinkSNS 服务 ThinkSNS 能够建立社交网站、微博平台、教育平台、商城系统、粉丝社区、知识社区、企业文化社区等多种互联网平台是移动互联网创业的首选二次开发平台。 社区、论坛实践项目简介 基于 ThinkSNS 开源免费体系搭建行业内基础 PGC、UGC 内容管理社区、论坛服务体系。商家运营人员可以发布管理 PGC 资讯管理用户、动态、广告等操作客户端用户可以查阅、点赞、评论、分享相关动态资讯内容可以自主发布、删除 UGC 内容、同步 UGC 内容审核 相关文档 API 接口文档详情查阅《 ThinkSNS 社区服务接口文档.md 》ThinkSNS 综合管理系统开源代码 git 地址https://gitee.com/joysapp/thinksns-plus客户端小程序源代码 git 地址https://gitee.com/buhehpc/jxsns.git 1.2 使用 UniAPP 构建项目 使用 Hbuilder uni-app 默认空白模板构建项目 study 在 manifest.json 文件中配置微信小程序相关信息 1.3 构建项目文件结构 构建项目基础结构 初始化基础页面及配置 pages.json Pages 路由 pages: [ //pages数组中第一项表示应用启动页参考https://uniapp.dcloud.io/collocation/pages{path: pages/index/index,style: {// custorm 取消顶部的 navBar 显示navigationStyle: custom,enablePullDownRefresh: true}}, {path: pages/feeds/feeds,style: {navigationBarTitleText: 动态中心,enablePullDownRefresh: true}}, {path: pages/me/me,style: {navigationStyle: custom,enablePullDownRefresh: true}} ]1.4 配置页面 TabBar 导航 tabBar: {color: #000,selectedColor: #0050FF,list: [{iconPath: /static/tabbar-icons/index.png,selectedIconPath: /static/tabbar-icons/index_s.png,text: 首页,pagePath: pages/index/index},{iconPath: /static/tabbar-icons/feeds.png,selectedIconPath: /static/tabbar-icons/feeds_s.png,text: 动态,pagePath: pages/feeds/feeds},{iconPath: /static/tabbar-icons/me.png,selectedIconPath: /static/tabbar-icons/me_s.png,text: 我的,pagePath: pages/me/me}] }1.5 使用 npm 引入 uView UI 插件库 使用 HBuilder 导入插件 uViewUI 或者使用 npm 安装相关依赖推荐使用 npm 安装 // 如果您的项目是HX创建的根目录又没有package.json文件的话请先执行如下命令 npm init -y // 安装 npm install uview-ui // 更新 npm update uview-uimain.js引入uView库 // main.js import uView from uview-ui; Vue.use(uView);编辑器安装相关依赖 工具 — 插件安装 — scss 编译支持 App.vue引入基础样式 /* App.vue */ style langscss import uview-ui/index.scss; /styleuni.scss引入全局scss变量文件 /* uni.scss */ import uview-ui/theme.scss;pages.json配置easycom规则(按需引入) // pages.json {easycom: {// 下载安装的方式需要前面的/npm安装的方式无需/// ^u-(.*): /uview-ui/components/u-$1/u-$1.vue// npm安装方式^u-(.*): uview-ui/components/u-$1/u-$1.vue},// 此为本身已有的内容pages: [// ......] }二、首页功能实现 2.1 首页 header 广告位轮播图功能实现 关键技术点 swiper 轮播器组件的使用使用 this.$u.get/post 异步请求数据使用 v-for 循环遍历渲染轮播内容 !-- 页面 header 相关部分 -- view classheader-box!-- 顶部广告位轮播图 --swiper classswiper :indicator-dotsfalse :autoplaytrue :interval2500 :duration500swiper-item v-foritem in swiperAdList :keyitem.idnavigator open-typenavigate :url /pages/webview/webview?urlitem.linkimage classbanner-swiper-img :srcitem.image modeaspectFill //navigator/swiper-item/swiper!-- 遮罩使用弧形框 --image classcrile src/static/crile.png modeaspectFill /!-- 两个选项按钮 --view classcard-headerview classcard-one card-left tapgotoFeeds(/pages/feeds/feeds)image classimg src/static/coffee.png modeaspectFill /view classirightview classtitle精彩动态/view/view/viewview classcard-one card-right tapgotoFeeds(/pages/me/me)image classimg src/static/ran.png modeaspectFill /view classirightview classtitle个人中心/view/view/view/view!-- Tab 选项卡 --view classtabs-boxview classone-nav :classcurrentSwiperIndex 0 ? nav-actived : tapswiperChange(0)推荐/viewview classone-nav :classcurrentSwiperIndex 1 ? nav-actived : tapswiperChange(1)资讯/view/view /view script// 请求 广告轮播图信息async getAdverts() {let adverts await this.$u.api.getAdvert({space: 1,2,3})this.swiperAdList adverts.data.map(item {return {id: item.id,link: item.data.link,image: item.data.image}})} /script2.2 全局配置 WebView 展示第三方 web 页面 关键技术点我们通常为整个应用的 Web H5 相关页面构建一个专门用来展示的 webview 路由页面但是要注意路由传参的时候对应的 url 需要 decodeURI 、encodeURI templateweb-view :srcurl / /template scriptexport default {data() {return {url:}},// 接收传递过来的参数地址将 web-view 相关页面展开集合到一个地方onLoad(options){this.url decodeURI(options.url)}} /script2.3 uViewUI 网络 http 请求及 API 集中管理 请求头、拦截、返回 /common/http.interceptor.js 相关配置 // 这里的vm就是我们在vue文件里面的this所以我们能在这里获取vuex的变量比如存放在里面的token变量 const install (Vue, vm) {// 此为自定义配置参数具体参数见上方说明Vue.prototype.$u.http.setConfig({baseUrl: http://47.115.83.135/api/v2, // 请求的本域名dataType: json, // 设置为json返回后会对数据进行一次JSON.parse()showLoading: true, // 是否显示请求中的loadingloadingText: 请求中..., // 请求loading中的文字提示loadingTime: 800, // 在此时间内请求还没回来的话就显示加载中动画单位msoriginalData: true, // 是否在拦截器中返回服务端的原始数据loadingMask: true, // 展示loading的时候是否给一个透明的蒙层防止触摸穿透// 配置请求头信息header: {content-type: application/json;charsetUTF-8},});// 请求拦截配置Token等参数Vue.prototype.$u.http.interceptor.request (config) {// 引用token// 方式一存放在vuex的token假设使用了uView封装的vuex方式// 见https://uviewui.com/components/globalVariable.html// config.header.token vm.token;// 方式二如果没有使用uView封装的vuex方法那么需要使用$store.state获取// config.header.token vm.$store.state.token;// 方式三如果token放在了globalData通过getApp().globalData获取// config.header.token getApp().globalData.username;// 方式四如果token放在了Storage本地存储中拦截是每次请求都执行的// 所以哪怕您重新登录修改了Storage下一次的请求将会是最新值// const token uni.getStorageSync(token);// config.header.token token;config.header.Token xxxxxx;// 可以对某个url进行特别处理此url参数为this.$u.get(url)中的url值// if(config.url /user/login) config.header.noToken true;// 最后需要将config进行returnreturn config;// 如果return一个false值则会取消本次请求// if(config.url /user/rest) return false; // 取消某次请求}// 响应拦截判断状态码是否通过Vue.prototype.$u.http.interceptor.response (res) {if(res.code 401) {// 如果返回false则会调用Promise的reject回调// 并将进入this.$u.post(url).then().catch(res{})的catch回调中res为服务端的返回值vm.$u.toast(当前请求 API 接口不存在);return false;} else {// res为服务端返回值可能有coderesult等字段// 这里对res.result进行返回将会在this.$u.post(url).then(res {})的then回调中的res的到// 如果配置了originalData为true请留意这里的返回值return res;}} }export default {install }请求 API 集中管理 /common/http.api.js // 此处第二个参数vm就是我们在页面使用的this你可以通过vm获取vuex等操作更多内容详见uView对拦截器的介绍部分 const install (Vue, vm) {let api {}// 获取广告位列表信息api.getAdvert params vm.$u.get(/advertisingspace/advertising, params)// 获取动态列表信息api.getFeeds params vm.$u.get(/feeds, params)// 获取资讯列表信息api.getNews params vm.$u.get(/news, params)// 将各个定义的接口名称统一放进对象挂载到vm.$u.api(因为vm就是this也即this.$u.api)下vm.$u.api api }export default {install }在 main.js 中配置插件注册第二个参数 app 要传入到实例中去所以要注意引入顺序 const app new Vue({...App }) // http拦截器此为需要加入的内容如果不是写在common目录请自行修改引入路径 import httpInterceptor from /common/http.interceptor.js // 这里需要写在最后是为了等Vue创建对象完成引入app对象(也即页面的this实例) Vue.use(httpInterceptor, app) // http接口API集中管理引入部分 import httpApi from /common/http.api.js Vue.use(httpApi, app) app.$mount()2.4 首页「推荐、资讯」平滑切换实现 关键技术点 使用轮播器不自动播放来构建一个左右滑动切换页面的效果需要注意的是轮播器的高度要设置一个固定值 !-- 内容轮播导航实现 -- swiper classswiper-box styleheight:1000upx :current0!-- 推荐动态实现 --swiper-item classswiper-item sns-now...动态页面瀑布流展示/swiper-item!-- 资讯列表实现 --swiper-item classswiper-item sns-news...资讯页面列表展示/swiper-item /swiper2.5 首页「推荐」瀑布流布局效果实现 瀑布流布局其核心是基于一个网格的布局而且每行包含的项目列表高度是随机的随着自己内容动态变化高度同时每个项目列表呈堆栈形式排列最为关键的是堆栈之间彼此之间没有多余的间距差存大。 实现瀑布流的解决方案可以使用 CSS3 布局来实现也可以使用 JS 脚本来实现使用 CSS3 实现代码如下 /*这里的关键属性是column-count设置列数为2。然后是break-inside:avoid为了控制文本块分解成单独的列以免项目列表的内容跨列破坏整体的布局这样就实现了瀑布流布局。 */ .waterfall {width: 80%;margin: 0 auto;/* 瀑布流容器内元素的间隔 */column-gap:10px;/* 瀑布容器内排列的列数 */column-count: 2; } .item {padding: 10px;margin-bottom: 10px;/* avoid避免在主体框中插入任何中断页面列或区域 */break-inside:avoid }使用 CSS3 实现瀑布流布局性能高于js缺点是用户体验差比如移除数据、更新数据会造成整个页面结构的复杂变化让用户突然失去焦点的感觉非常不友好所以使用 js 实现优势也是比较明显的我们这里使用 uViewUI 的瀑布流插件来实现 u-waterfall v-modelflowList refuWaterfalltemplate v-slot:left{leftList}view v-for(item, index) in leftList :keyindex!-- 这里编写您的内容item为您传递给v-model的数组元素 --/view/templatetemplate v-slot:right{rightList}view v-for(item, index) in rightList :keyindex!-- 这里编写您的内容item为您传递给v-model的数组元素 --/view/template /u-waterfall script export default {methods:{remove(id) {this.$refs.uWaterfall.remove(id);},clear() {this.$refs.uWaterfall.clear();}} } /scriptPS swiper 实现 滑动切换页面的效果需要设置一个固定的高度值但是咱们瀑布流的高度是未知的那么该怎么办呢iViewUI 中的 u-waterfall 源码分析我们要通过全局事件设置的方式来动态的设置 高度同时咱们这个定制化的 u-waterfall 需要单独再拿出来进行使用拓展咱们定制化的插件 在 uViewUI 中创建一个自定义使用的 u-waterfall-sns.vue 插件主要分为三步 在 uViewUI 组件库 components 中找到 u-waterfall 文件复制一份命名为u-waterfall-sns文件内 u-waterfall.vue 插件复制一份命名为u-waterfall-sns.vue 插件u-waterfall-sns.vue 插件内name 属性更改为 u-waterfall-sns就可以调用咱们拓展出来的插件了 使用 uni.$on 设置全局事件在 u-waterfall-sns.vue 插件内部瀑布流渲染完毕后触发这个事件动态修改首页轮播切换页面内的 swiper 高度 data() {return {// 轮播器高度swiperSliderHeight: 500px} }, async onLoad() {// 根据瀑布流计算的高度设置全局事件动态修正页面滑动轮播器高度uni.$on(swiperHeightChange, height {console.log(height)this.swiperSliderHeight height}) },设置 image 组件的 mode 属性为 widthFil 让动态瀑布流错落有致的状态更友好 2.6 首页「推荐、资讯」轮播切换动态设置轮播器高度 关键技术点切换 资讯、动态 的时候轮播器切换页面的高度是需要动态调整的我们需要监听行为进行调整 export default {data() {return {// 当前 推荐 资讯 滑动位置currentSwiperIndex: 0,// 滑动页面轮播器的高度swiperSliderHeight: 500px,swiperSliderFeedsHeight: 0,swiperSliderNewsHeight: 0}},async onLoad() {// 在这里注册一个 uniAPP 的顶层事件用来作为数据通信uni.$on(swiperHeightChange, height {this.swiperSliderFeedsHeight heightthis.swiperSliderHeight height})// 我们要在这里初始化请求相关数据this.getAdverts()this.getFeedsList()this.getNewsList()},methods: {// 请求 广告轮播图信息async getAdverts(){},// 请求 feeds 列表数据async getFeedsList(){},// 请求资讯列表数据async getNewsList(){let news await this.$u.api.getNews()let newsList news.data.map(item {console.log(timeFrom(new Date(item.created_at)))return {...item,cover: this.BaseFileURL item.image.id}})this.newsList [...this.newsList , ...newsList]this.swiperSliderNewsHeight this.newsList.length * 95 100 pxthis.swiperSliderHeight this.swiperSliderNewsHeight},// 页面滑动左右分页的时候实现的效果swiperSlider(event){if(event.detail.current 0){this.swiperSliderHeight this.swiperSliderFeedsHeight}else{this.swiperSliderHeight this.swiperSliderNewsHeight}this.currentSwiperIndex event.detail.current},// 点击按钮实现切换效果swiperChange(index){if(index 0){this.swiperSliderHeight this.swiperSliderFeedsHeight}else{this.swiperSliderHeight this.swiperSliderNewsHeight}this.currentSwiperIndex index}} } 2.7 首页滚动状态下动态设置「推荐、资讯」位置及 NavBar 状态 在插件市场找到一个功能比较丰富的 NavBar 插件 https://ext.dcloud.net.cn/plugin?id813 PS注意导入别人的插件后咱们先体验体验在使用的时候尽量去看看插件的源码以便于我们对当前使用插件更深层次的理解和掌握 第一步导入和使用插件 // main.js 中全局引入插件 uni-nav-bar import uniNavBar from /components/uni-nav-bar.vue Vue.component(uni-nav-bar, uniNavBar);!-- 顶部导航 -- uni-nav-bar v-ifnavBarShowTagview classtabs-boxview classone-nav :classcurrentSwiperIndex 0 ? nav-actived : tapswiperChange(0)推荐/viewview classone-nav :classcurrentSwiperIndex 1 ? nav-actived : tapswiperChange(1)资讯/view/view /uni-nav-bar// 根据滚动状态动态显示隐藏导航栏 data() {return {// navBar 显示状态控制navBarShowTag: false,// 记录 推荐滚动 所在的位置oldFeedsScrollTop: 0,// 记录 资讯滚动 所在位置oldNewsScrollTop: 0} }第二步监听页面滚动事件判断滚动显示和隐藏 navBar 导航状态 // 监听滚动事件动态显示隐藏 Navbar onPageScroll(event) {if (event.scrollTop 220) {this.navBarShowTag true} else {this.navBarShowTag false} }第三步记录滚动状态下「动态、资讯」位置 // 监听滚动事件记录滚动位置 onPageScroll(event) {if (this.currentSwiperIndex 0) {this.oldFeedsScrollTop event.scrollTop} else {this.oldNewsScrollTop event.scrollTop} }第四步切换「动态、资讯」滚动到指定记录位置 // 页面滑动左右分页的时候实现的效果 swiperSlider(event) {if(event.detail.current 0) {this.swiperSliderHeight this.swiperSliderFeedsHeightuni.pageScrollTo({duration: 0, //过渡时间必须为0uniapp bug否则运行到手机会报错scrollTop: this.oldFeedsScrollTop, //滚动到目标位置})} else {this.swiperSliderHeight this.swiperSliderNewsHeightuni.pageScrollTo({duration: 0, //过渡时间必须为0uniapp bug否则运行到手机会报错scrollTop: this.oldNewsScrollTop, //滚动到目标位置})}this.currentSwiperIndex event.detail.current }, // 点击按钮实现切换效果 swiperChange(index) {if (index 0) {this.swiperSliderHeight this.swiperSliderFeedsHeightuni.pageScrollTo({duration: 0, //过渡时间必须为0uniapp bug否则运行到手机会报错scrollTop: this.oldFeedsScrollTop, //滚动到目标位置})} else {this.swiperSliderHeight this.swiperSliderNewsHeightuni.pageScrollTo({duration: 0, //过渡时间必须为0uniapp bug否则运行到手机会报错scrollTop: this.oldNewsScrollTop, //滚动到目标位置})}this.currentSwiperIndex index }2.8 顶部下拉、底部置底请求更新数据 关键技术点 onReachBottom、onPullDownRefresh 全局方法调用this.$refs.waterfall.clear() 调用插件对应方法清空数据流 // 下拉到底请求新的数据 onReachBottom() {// 请求新的数据if (this.currentSwiperIndex 0) {this.getFeeds()} else {this.getNews()} }, // 顶部下拉请求新数据 onPullDownRefresh() {this.feedsList []this.$refs.waterfall.clear()if (this.currentSwiperIndex 0) {this.getFeeds()} else {this.getNews()} }, methods: {// 获取动态信息async getFeeds() {let feeds await this.$u.api.getFeeds()let feedsList feeds.feeds.map(item {return {...item,cover: this.BaseFileURL item.images[0].file,avatar: !!item.user.avatar ? item.user.avatar.url : /static/nopic.png,name: item.user.name,}})this.feedsList [...this.feedsList, ...feedsList]// 在这里注册一个 uniAPP 的顶层事件用来作为数据通信uni.$once(swiperHeightChange, height {console.log(瀑布流计算出来轮播器高度为: height)this.swiperSliderFeedsHeight height pxthis.swiperSliderHeight this.swiperSliderFeedsHeight})},// 获取咨询列表信息async getNews() {let news await this.$u.api.getNews()let newsList news.map(item {return {...item,cover: this.BaseFileURL item.image.id}})this.newsList [...this.newsList, ...newsList]this.swiperSliderNewsHeight (this.newsList.length * 95 120) pxthis.swiperSliderHeight this.swiperSliderNewsHeight} }三、资讯动态相关页面功能实现 3.1 使用 Grid 栅格布局实现动态瀑布流页面 技术关键点 动态请求数据并对数据进行加工处理成为我们期望的 分组数组 目标数据grid 栅格化布局顶部下拉刷新更新数据滚动置底请求数据请求状态控制器 templateview classrfeedsview classone-feeds-box v-for (feedsList, i) in showFeedsList :keyiview v-for (item, k) in feedsList :keyitem.id classone-feed :classk % 6 0 ? ( i%20 ? feed-big-left :feed-big-right ) : navigator :url /subpages/feedinfo/feedinfo?id item.idimage :srcitem.cover classfeed-content modeaspectFill :lazy-loadtrue //navigator/view/view!-- 分享按钮组件 --goto-share //view /template scriptexport default {data() {return {// 列表数据feedsList: [],// 用来展示的栅格系统的列表数据showFeedsList: [],// 记录请求 feed 状态canRequestFeeds: yes};},onLoad() {this.getFeeds()},// 顶部下拉刷新新数据onPullDownRefresh() {this.feedsList []this.getFeeds()},// 滚动置底刷新请求数据onReachBottom() {this.getFeeds()},methods: {// 获取请求数据async getFeeds() {if (this.canRequestFeeds yes) {uni.showToast({title: 请求中...,icon: loading,duration: 10000});// 设置状态this.canRequestFeeds nolet res await this.$u.api.getFeeds()let feeds res.feeds.map(item {return {id: item.id,cover: this.BaseFileURL item.images[0].file}})if (feeds.length 5) {// 记录总列表数据this.feedsList [...this.feedsList, ...feeds];// 在这里要处理页面的内容布局模式为栅格模式6个元素一组let showArrList [];for (let i 0; i this.feedsList.length; i) {if (i % 6 0 !!this.feedsList[i 5]) {// 返回一个新的数组包含从 start 到 end 不包括该元素元素。// 该方法并不会修改数组而是返回一个子数组。如果想删除数组中的一段元素应该使用方法 Array.splice()。showArrList.push(this.feedsList.slice(i, i 6))}}this.showFeedsList showArrListthis.canRequestFeeds yes} else {this.canRequestFeeds enduni.showToast({title: 到底啦,icon: success,duration: 1000});}}// 如果请求状态到底了后则提示到底了if (this.canRequestFeeds end) {uni.hideToast();uni.showToast({title: 到底啦,icon: success,duration: 1000}) }},},}; /script3.2 使用分包构建业务逻辑页面 技术关键点page.json 中配置分包业务逻辑 subPackages: [{root: subpages,pages: [{path: feedinfo,style: {navigationBarTitleText: 动态详情,navigationBarBackgroundColor: #FFFFFF,navigationBarTextStyle: black,backgroundColor: #FFFFFF}}] }],3.3 动态详情页面功能实现 技术关键点 分享朋友圈 wx.showShareMenu({withShareTicket: true,menus: [shareAppMessage, shareTimeline] })图片预览 methods: {previewImage(index) {uni.previewImage({current: index,urls: this.feedInfo.images});} }使用日期格式化过滤器显示时间 // 引入 时间日期格式化显示函数 import timeFrom from /tools/timeFrom.js // 设置过滤器 filters: {timeFormate(timeDate) {let Time new Date(timeDate);let timestemp Time.getTime();let t timeFrom(timestemp, yyyy年mm月dd日);return t;} }, // 调用过滤器 text{{ feedInfo.created_at | timeFormate }} 发布/text3.4 资讯详情页面富文本解析功能实现 技术关键点 顶部毛玻璃插件的引入及使用 // 引入毛玻璃组件 import picBlur from /components/pic-blur/pic-blur.vue; components: {picBlur }, !-- 顶部毛玻璃背景图 -- !-- 可选值 xs s m l xl -- pic-blur :params{width:750rpx,height:520rpx,image: newInfo.cover,blur:xs }/pic-blur资讯详情内容 富文本内容 深度解析 import htmlParse from /components/html-parse/parse.vue;view classinfo-contenthtml-parse :contentnewInfo.content / /viewasync onLoad(options) {// 分享wx.showShareMenu({withShareTicket: true,menus: [shareAppMessage, shareTimeline]})let res await this.$u.api.getNewInfo(options);res res.data// let cp res.content.replace(/!\[(\d*).jpg\]\((\d*)\)/g,img src this.BaseFileURL $2 /)let cp res.content.replace(/!\[.*\]\((\d*)\)/g, img src this.BaseFileURL $1 /)this.newInfo {...res,cutTitle: res.title.length 11 ? res.title.substring(0, 11) ... : res.title,cover: this.BaseFileURL res.image.id,userId: res.user_id,content: cp,views_count: res.hits}this.getRequestOK true; }NavBar 根据滚动动态显示 !-- 顶部导航 -- uni-nav-bar backState2000 fontColor#FFF :titleCenterfalse typetransparentview slotleftimage classhicon src/static/home.png modeaspectFit tapgoHome //view /uni-nav-baruni-nav-bar v-ifnavBarShow backState1000 :titleCenterfalse{{ newInfo.cutTitle }} /uni-nav-bardata() {return {// 是否显示 navbarnavBarShow: false}; }, onPageScroll(res) {if (res.scrollTop 100) {this.navBarShow true;} else {this.navBarShow false;} }3.5 评论组件评论列表展示功能实现 关键技术点使用 props 接收到的数据可以直接渲染到页面上但是 props 接收到的数据如果修改后则不会动态触发 对应 dom 结构的内容更新我们需要做一个变量去保存 props 传递的初始化值然后基于此变量更新页面结构和内容 templateview!-- 评论列表 --view classcommentsview classtitle gohere idgohere refgohere最新评论/viewview classno-comment v-ifcommintsList.length 0暂无评论/viewview v-for(commentItem, index) in commintsList :keyindex classone-comment!-- 一级评论相关 --view classcommenterview classinfoview classleftu-avatar size50 classavatar :src!!commentItem.user.avatar ? commentItem.user.avatar.url : /view classname{{ commentItem.user.name }}/view/view/viewview classcontent{{ commentItem.body }}/viewview classuptime{{ commentItem.created_at | timeFormate }} 评论/view/view/view/view/view /templatescriptimport timeFrom from /tools/timeFrom.js;export default {props: {oneInfo: Object,type: String,},data() {return {// 当前动态评论列表详情commintsList: [],// props 传递的数据无法改变触发 DOM 更新oneInfoClone: []};},async created() {this.oneInfoClone this.oneInfothis.getCommentsList();},methods: {// 获取评论列表async getCommentsList() {if (this.type feed) {// 获取当前动态评论列表信息let res await this.$u.api.getFeedComments({id: this.oneInfo.id});this.commintsList res.data.comments;} else {// 获取当前资讯评论列表信息let res await this.$u.api.getNewComments({id: this.oneInfo.id});this.commintsList res.data.comments;}}},// 过滤器filters: {timeFormate(timeDate) {let Time new Date(timeDate);let timestemp Time.getTime();let t timeFrom(timestemp, yyyy年mm月dd日);return t;},},}; /script四、用户注册、登陆业务功能实现 4.1 用户登陆、注册功能组件实现及触发显示 关键技术点 获取当前微信用户的 昵称 和 头像信息使用 u-form 实现登陆、手机注册、邮箱注册基础逻辑使用 u-form 实现表单验证相关逻辑 templateview classlogin :class{show:show}button classsubmit-btn open-typegetUserInfo getuserinfogetWechatUserInfo v-ifgetUserInfoTagimage src/static/wechat.png classwechat-img /text一键获取微信信息/text/buttonu-form :modelform refuForm v-show!getUserInfoTagu-form-item label账号 proplogin label-width150 required v-ifloginType loginu-input v-modelform.login placeholder输入手机号/邮箱/昵称 //u-form-itemu-form-item label密码 label-width150 required proppasswordu-input v-modelform.password typepassword placeholder限4-20个字符,区分大小写 //u-form-itemview classbtnsu-button classubtn clicksubmit提交/u-buttonu-button classubtn clickcancel取消/u-button/viewview classtypeu-subsection active-color#007cba font-size24 height52 :listsubsectionList :current0 changesectionChange/u-subsection/view/u-form/view/view/view /templatescriptexport default {data() {return {// 登陆组件是否显示show: false,// 登陆方式loginType: login,// 登陆方式选择器subsectionList: [{ name: 账号登陆},{ name: 手机注册 },{ name: 邮箱注册 }],// 是否获取用户信息getUserInfoTag: true,// 用户信息输入框form: {login: ,avatar: ,name: ,phone: ,email: ,code: ,password: ,repassword: },// 用户输入规则校验rules: {login: [{validator: (rule, value, callback) {if (this.loginType login) {return !this.$u.test.isEmpty(value)} else {return true}},message: 必填 * 输入内容不许为空,trigger: [change, blur],}],name: [{asyncValidator: (rule, value, callback) {this.$u.api.findUser({name: value}).then(res {// 如果验证不通过需要在callback()抛出new Error(错误提示信息)if (!!value res.statusCode 200) {callback(new Error(当前用户昵称已存在));} else {// 如果校验通过也要执行callback()回调callback();}})},trigger: [blur],}],// ... 其他验证规则}}},// 必须要在onReady生命周期设置校验规则因为onLoad生命周期组件可能尚未创建完毕onReady() {this.$refs.uForm.setRules(this.rules);},async created() {// 每次这个组件展开我们就去判断一下当前 token 是否可以获取新的 token 如果可以获取咱们就关闭登陆状态let res await this.$u.api.getUserMsg()// 点赞消息数量if (res.statusCode 200) {this.show falsereturn} else {this.show true}wx.getSetting({success: res {if (res.authSetting[scope.userInfo]) {uni.getUserInfo({success: res {// 如果用户授权了则做两件事第一件事this.form.login res.userInfo.nickNamethis.form.name res.userInfo.nickNamethis.form.avatar res.userInfo.avatarUrlthis.getUserInfoTag false},fail: () {console.log(用户未授权)}})}}})},methods: {// 关闭弹窗closeLogin() {this.show false},// 打开登陆弹窗openLogin() {this.show true},// 跳转到 H5 页面gotoWeb(url) {wx.navigateTo({url: /pages/webview/webview?url encodeURI(url)});},// 微信授权getWechatUserInfo() {uni.getUserInfo({success: res {// 进行下一步操作this.getUserInfoTag falsethis.form.login res.userInfo.nickNamethis.form.name res.userInfo.nickNamethis.form.avatar res.userInfo.avatarUrl},fail: () {console.log(用户未授权)}})},// 获取验证码getCode() {uni.showModal({title: 验证码获取成功,content: 8888})},// 取消表单输入cancel() {this.form {login: ,name: ,phone: ,email: ,code: ,password: ,repassword: }},// 提交表单验证submit() {this.$refs.uForm.validate(async valid {if (!valid) {uni.showToast({title: 请检查输入,icon: loading})return false}switch (this.loginType) {case login:let resa await this.$u.api.userLogin({login: this.form.login,password: this.form.password})if (resa.statusCode 200) {// 登陆成功this.loginAfter(resa.data.access_token)} else {uni.showModal({title: 登陆失败,content: resa.data.message})}break;case phone:let resb await this.$u.api.userRegister({// 必须用户名name: this.form.name,// 必须验证码发送模式。verifiable_type: sms,// 必须用户收到的验证码。(教学阶段验证码统一使用 8888 )verifiable_code: 8888,// 如果 verifiable_type 为 sms 则必须, 手机号码。phone: this.form.phone,// 可选密码如果不输入密码允许用户无密码注册。password: this.form.password})console.log(resb)if (resb.statusCode 201) {// 登陆成功this.loginAfter(resb.data.token)} else {uni.showModal({title: 登陆失败,content: resb.data.message})}break;case email:let resc await this.$u.api.userRegister({// 必须用户名name: this.form.name,// 必须验证码发送模式。verifiable_type: mail,// 必须用户收到的验证码。(教学阶段验证码统一使用 8888 )verifiable_code: 8888,// 如果 verifiable_type 为 mail 则必须, E-Mail。email: this.form.email,// 可选密码如果不输入密码允许用户无密码注册。password: this.form.password})console.log(resc)if (resc.statusCode 201) {// 登陆成功this.loginAfter(resc.data.token)} else {uni.showModal({title: 登陆失败,content: resc.data.message})}break;default:uni.showToast({title: 未知用户状态,icon: loading})break;}});},// 注册、登陆成功后设置相关逻辑async loginAfter(token) {this.show falseuni.setStorageSync(token, token)// 获取未读消息提示let res await this.$u.api.getUserMsg()let name this.form.nameif (this.loginType login) {name this.form.login}let loginInfo {name,avatar: this.form.avatar,liked: res.data.user.liked,commented: res.data.user.commented}this.userLoginAction(loginInfo)uni.$emit(userLogin)},// 更改 登陆 注册 方式选择sectionChange(index) {switch (index) {case 1:this.loginType phone;break;case 2:this.loginType email;break;default:this.loginType login;break;}}}} /script4.2 Vuex Storage 登陆状态管理 关键技术点 构建 vuex 注册 main.js 全局并配合使用 storage 实现刷新后数据保存 import Vue from vue import Vuex from vuex Vue.use(Vuex) const store new Vuex.Store({// 这个代表全局可以访问数据对象就像是咱们在组件中声明的 data 属性state: {loginState: !!uni.getStorageSync(loginState) ? true : false,userInfo: !!uni.getStorageSync(userInfo) ? JSON.parse(uni.getStorageSync(userInfo)) : {name: 未知用户,avatar: /static/nopic.png,liked: 0,commented: 0}},// 这个实时监听 state 内的数据对象变化类似 咱们组件中的 computed 属性会依赖 state 数据变化而变化getters: {},// 用来同步设置 state 的值mutations: {userLogin(state, userInfo) {state.loginState truestate.userInfo userInfouni.setStorageSync(loginState, ok)uni.setStorageSync(userInfo, JSON.stringify(userInfo))},userLogout(state) {state.loginState falsestate.userInfo {name: 未知用户,avatar: /static/nopic.png,liked: 0,commented: 0}uni.clearStorageSync(userInfo)uni.clearStorageSync(loginState)uni.clearStorageSync(token)}},// 通过提交 mutations 内部的方法异步更新 state 的状态官方推荐都使用这种方法比较合适actions: {userLoginAction(context, userInfo) {context.commit(userLogin, userInfo)},userLogoutAction(context) {context.commit(userLogout)}} }) export default store使用 mapState、mapActions 实现 vuex 变量快速调用 import {mapState,mapActions} from vuex computed: {...mapState([loginState, userInfo]) }, methods: {...mapActions([userLoginAction, userLogoutAction]) }使用 uni.$on $emit 触发首页及个人中心页面登录后对应的数据更新 // index onload uni.$on(indexUserLogin, (){this.currentSwiperIndex 0this.feedsList []this.$refs.waterfall.clear()this.getFeedsList() }) // me onload // 用户登录后触发数据更新 uni.$on(meUserLogin, this.getInfos)uni.$emit(meUserLogin) uni.$emit(indexUserLogin)4.3 个人中心页面基础功能实现 关键技术点 请求当前登录用户发布动态列表数据 // 获取当前用户的 动态信息 let res await this.$u.api.getFeeds({type: users })使用 瀑布流 插件实现排版布局 u-waterfall v-modelfeedsList refwaterfalltemplate v-slot:left{leftList}.../templatetemplate v-slot:left{leftList}.../template /u-waterfall使用 vuex 状态数据显示用户登录状态 import {mapState,mapActions} from vuex computed: {...mapState([loginState, userInfo]) }, methods: {...mapActions([userLoginAction, userLogoutAction]) }用户删除一条动态更新瀑布流并通信 index 页面同步更新 // 选择删除一条动态 async openSheet(fid) {uni.showActionSheet({itemList: [删除],success: async res {console.log(删除 id 为 fid 的动态)await this.$u.api.deleteFeed({id: fid})this.$refs.waterfall.remove(fid);// 触发 首页动态 同步删除事件uni.$emit(indexFeedRemove, fid)uni.showToast({title: 当前动态已删除,duration: 1000})}}); }, // index 页面 // 个人中心删除一条动态后触发更新首页数据 uni.$on(indexFeedRemove, fid {this.$refs.waterfall.remove(fid); })4.4 用户设置页面基础功能实现 关键技术点 使用 vuex 状态管理器 actions 方法快速退出使用 uni.$on $emit 触发首页及个人中心页面用户退出后对应的数据更新 // index onload uni.$on(indexUserLogout, (){this.currentSwiperIndex 0this.feedsList []this.$refs.waterfall.clear()this.getFeedsList() }) // me onload // 用户退出后触发数据更新 uni.$on(meUserLogout, () {console.log(触发了退出操作)this.feedsList []this.avatar this.bio this.$refs.waterfall.clear() }) uni.$emit(meUserLogout) uni.$emit(indexUserLogout)五、登陆态下业务功能实现 5.1 使用 mixins 实现动态点赞、取消点赞功能 关键技术点 使用 mixins 构建复用的动态 点赞、取消点赞 事件方法 let feedMixin {methods: {// 点赞或者取消点赞一条动态async clickLove(item) {// 判断当前登录状态if (!this.loginState) {this.$refs.login.openLogin()return}// 动态点赞if (item.has_like) {--item.like_count;item.has_like false;await this.$u.api.unlikeThisFeed({id: item.id,});uni.showToast({title: 取消点赞,icon: success,duration: 1000,});} else {item.like_count;item.has_like true;await this.$u.api.likeThisFeed({id: item.id,});uni.showToast({title: 点赞成功,icon: success,duration: 1000,});}uni.$emit(indexFeedLoveChange,item)uni.$emit(myFeedLoveChange,item)}} } export default feedMixin调用 mixins 实现点赞功能 // index \ me 两个页面 import feedMixin from /mixins/todoFeed.js mixins: [feedMixin]点赞完成后通信相关页面更新显示状态 // me 页面 用户点赞一条动态后触发数据更新 uni.$on(myFeedLoveChange, item {this.$refs.waterfall.modify(item.id, like_count, item.like_count);this.$refs.waterfall.modify(item.id, has_like, item.has_like); }) // index 用户点赞一条动态后触发数据更新 uni.$on(indexFeedLoveChange, item {this.$refs.waterfall.modify(item.id, like_count, item.like_count);this.$refs.waterfall.modify(item.id, has_like, item.has_like); })// minxins 中触发通信 uni.$emit(indexFeedLoveChange,item) uni.$emit(myFeedLoveChange,item) 5.2 登陆状态下评论组件点赞、评论功能实现 关键技术点 登陆插件引入及登陆状态判断 !-- 登陆组件 -- login reflogin/login // 判断当前登录状态 if (!this.loginState) {this.$refs.login.openLogin()return }正常实现资讯点赞功能 // 点赞逻辑操作 async sendLove(){// 判断当前登录状态if (!this.loginState) {this.$refs.login.openLogin()return}if (this.type feed) {this.clickLove(this.oneInfoClone)} else {// 动态点赞if (this.oneInfoClone.has_like) {--this.oneInfoClone.digg_count;this.oneInfoClone.has_like false;await this.$u.api.unlikeThisNew({id: this.oneInfoClone.id,});uni.showToast({title: 取消点赞,icon: success,duration: 1000,});} else {this.oneInfoClone.digg_count;this.oneInfoClone.has_like true;await this.$u.api.likeThisNew({id: this.oneInfoClone.id,});uni.showToast({title: 点赞成功,icon: success,duration: 1000,});}} }实现评论功能注意评论完成后修改页面数据不能直接修改 props 中的数据通知个人中心评论数量增加 // 发送评论信息 async sendComment(){// 发送状态判定if (this.disableSendCommentTag) returnthis.disableSendCommentTag trueif (this.type feed) {await this.$u.api.commentOneFeed({id: this.oneInfoClone.id,body: this.cinput});this.oneInfoClone.feed_comment_count// 通知 个人中心当前动态评论增加uni.$emit(myFeedCommentChange, this.oneInfoClone)} else {await this.$u.api.commentOneInfo({id: this.oneInfoClone.id,body: this.cinput});this.oneInfoClone.comment_count}uni.showToast({title: 评论成功,icon: success,duration: 1000,});this.cinput this.closeComment()this.getCommentsList() }5.3 UGC 动态发布功能实现 关键技术点 公共组件 发布按钮 注意用户未登录状态下不显示这个按钮 templateview classgoto-share v-ifloginState!-- 个人分享 选择器 --image classshare-img src/static/carm.png tap.stopgotoShare() modeaspectFit //view /template scriptimport {mapState} from vuexexport default {data() {return {};},computed: {...mapState([loginState])},methods: {// 底部个人分享 弹窗相关操作gotoShare() {uni.navigateTo({url: /subpages/share/share,})}}}; /script构建 share 页面用来专门实现动态上传能力 view classshare!-- 内容输入框 --view classuni-textareatextarea placeholder-stylecolor:#AAAAAA placeholder添加描述... inputbindTextAreaInput //view!-- 内容发布 --view classpicsview classmedias v-for( image, index ) in uploadPicsList :keyindeximageclassimg:srcimage.path:data-srcimagetappreviewImage(index)modeaspectFill/u-icon nameclose classiclose color#eee size20 tapremoveImage(index) //view!-- 选择照片按钮 --view classuploadBtn tapchosePicsAndUpload v-ifuploadPicsList.length 9u-icon nameplus size60 color#aaa /view classtext选择照片/view/view/viewview classbtnsu-button typeprimary sizedefault clicksendFeed :disableduploadStatus发布动态/u-button/view/view封装 uploadFile 公共工具 import MD5 from /common/md5.js; const uploadFile async (path, size) {let uploadUrl http://47.115.83.135/api/v2// 第一步 获取 hash 值let hash await new Promise((resolve, reject) {uni.getFileInfo({filePath: path,success: (result) {resolve(result.digest);},});});// console.log(MD5.md5(hash))// 第二步 请求获取基础文件信息// let check await uni.request({// url: uploadUrl/files/uploaded/MD5.md5(hash),// method: GET,// header: {// content-type: application/json,// Accept: application/json,// Authorization: Bearer uni.getStorageSync(token),// }// })// 如果返回的状态码是404那说明没有上传过继续后面的上传流程// 如果返回的是200,那就从body里取出 id这个id就可以直接使用后面的上传流程就可以终止了// console.log(check[1].statusCode)// 第三步 将文件以buffer流方式存入系统let ufile await uni.uploadFile({url: uploadUrl /files,method: POST,header: {content-type: multipart/form-data,Authorization: Bearer uni.getStorageSync(token),},name: file,file: uni.getFileSystemManager().readFileSync(path),filePath: path});// console.log(JSON.parse(ufile[1].data))return JSON.parse(ufile[1].data) }; export default uploadFile; 使用 primose.all 来确保多张照片能够顺利上传 let upStatusArr []; // 循环遍历上传多图 this.uploadPicsList.map((item) {upStatusArr.push(uploadFile(item.path, item.size)); }); let images []; (await Promise.all(upStatusArr)).map((item) {images.push({id : item.id}); }); let pres await this.$u.api.postOneFeed({feed_content: this.feedInfo,feed_from: 5,feed_mark: new Date().getTime(),images });动态发布成功后通知 index me 页面更新 // 通知个人中心 动态更新了 uni.$emit(indexFeedsUpdate) // 通知首页 动态更新了 uni.$emit(myFeedsUpdate); // index 发布新的动态后触发数据更新 uni.$on(indexFeedsUpdate, (){this.currentSwiperIndex 0this.feedsList []this.$refs.waterfall.clear()this.getFeeds() })// me 用户发布一条动态后触发数据更新 uni.$on(myFeedsUpdate, (){this.$refs.waterfall.clear()this.getInfos() })六、多端打包发布 6.1 微信小程序打包发布 6.2 H5 端相关兼容性开发及打包发布 关键调整节点 manifest.json H5 下出现的跨域问题要进行相关配置 h5 : {devServer : {port : 8000, //端口disableHostCheck : true,proxy : {//使用代理/api : {target: http://47.115.83.135/api/v2,changeOrigin : true,pathRewrite : {^/api : }}}} }newinfo \ feedinfo 微信分享仅仅在微信下有效 // #ifdef MP-WEIXIN // 微信条件下分享到朋友圈、群组 wx.showShareMenu({withShareTicket: true,menus: [shareAppMessage, shareTimeline] }) // #endifcomment 组件分享按钮仅仅在 微信小程序下展示同步 css 调整 !-- 转发次数统计 -- !-- #ifdef MP-WEIXIN -- button classmbtn mhare open-typeshareimage classmicon src/static/wx.png modeaspectFit /text classmtext分享/text /button !-- #endif --.minput {background-color: #eee;height: 60upx;// #ifdef MP-WEIXINwidth: 300upx;// #endif// #ifndef MP-WEIXINwidth: 440upx;// #endif }login 登陆组件获取用户信息逻辑调整 // #ifdef MP-WEIXINwx.getSetting({success: res {if (res.authSetting[scope.userInfo]) {uni.getUserInfo({success: res {// 如果用户授权了则做两件事第一件事this.form.login res.userInfo.nickNamethis.form.name res.userInfo.nickNamethis.form.avatar res.userInfo.avatarUrlthis.getUserInfoTag false},fail: () {console.log(用户未授权)}})}}}) // #endif // #ifndef MP-WEIXINthis.getUserInfoTag false // #endifhttp.api.js API 管理器 文件上传逻辑调整 // 文件上传操作 api.uploadFile async file {let rfile file// #ifdef MP-WEIXINrfile uni.getFileSystemManager().readFileSync(file.path)// #endif// 将文件写入后台系统系统let ufile await uni.uploadFile({url: vm.$u.http.config.baseUrl /files,header: {Authorization: Bearer uni.getStorageSync(token),},name: file,file: rfile,filePath: file.path});console.log(JSON.parse(ufile[1].data))return JSON.parse(ufile[1].data) }PS: 可以使用托管工具快速发布体验尤其需要注意的是开发的时候配置的代理服务器在正式环境打包的时候是不会被调用的所以要更改对应的配置信息 6.3 安卓云端体验版本打包发布 ​
http://www.hkea.cn/news/14422249/

相关文章:

  • 网站开发实战asp制作视频教程驻马店市做网站
  • 徐州网站无障碍建设北京网站建设主页
  • 广东省住房城乡建设厅网站住房和城乡建设部标准定额网站
  • 男男床做视频网站在线如何做可以赚钱的网站
  • 伪原创嵌入网站初期做网站
  • wordpress付费建站小规模企业所得税2023年税收政策
  • 住房和建设建设局网站化妆品网站欣赏
  • 58临沂网站建设网站建设客源
  • 网站地图提交给百度wordpress微信登录的插件
  • 免费建立个人网站广州网络营销推广
  • 石排镇专业建站公司教医学生做科研的网站有哪些
  • 深圳龙华做网站微信公众号的微网站怎么做
  • zzcms网站开发wordpress的主机
  • 宁波建站服务做网站备案什么意思
  • 网站大全正能量免费2020网站尺寸大小
  • 珠市口网站建设宽屏网站
  • 宿州官方网站建设wordpress安装插件返回空白
  • 看剧资源网站怎么做的杭州网络推广
  • 怎么做多个域名指向一个网站荣耀手机商城官方网站登录入口
  • 密云网站建设公司网站收款接口
  • 中国十大网站建设wordpress一键分享代码
  • 怎样在各大网站做推广28商机网
  • 小视频解析网站怎么做php网站后台反应慢怎么解决
  • 做网站的空间需要买吗个人网站主机的配置
  • 如何撤销网站备案替换wordpress google 插件
  • 提升学历大概要多少钱德州哪家网站优化公司专业
  • 沧州做网站哪家好辽宁城乡住房建设厅网站
  • 网站背景颜色代码网址大全有用吗
  • 撰写网站规划书石家庄外贸网站推广
  • 网站模板 外贸工厂北京企业网站建设公司哪家好