首次完整推送,
V:1.20240808.006
This commit is contained in:
385
pages/uni-starter/list/detail.vue
Normal file
385
pages/uni-starter/list/detail.vue
Normal file
@ -0,0 +1,385 @@
|
||||
<template>
|
||||
<view class="article">
|
||||
<!-- #ifdef APP-PLUS -->
|
||||
<uni-nav-bar :statusBar="true" :border="false"></uni-nav-bar>
|
||||
<!-- #endif -->
|
||||
<view class="article-title">{{ title }}</view>
|
||||
<unicloud-db v-slot:default="{data, loading, error, options}" :options="formData"
|
||||
collection="uni-cms-articles,uni-id-users" :field="field" :getone="true" :where="where" :manual="true"
|
||||
ref="detail" foreignKey="uni-cms-articles.user_id" @load="loadData">
|
||||
<template v-if="!loading && data">
|
||||
<uni-list :border="false">
|
||||
<uni-list-item thumbSize="lg" :thumb="data.image">
|
||||
<!-- 通过body插槽定义作者信息内容 -->
|
||||
<template v-slot:body>
|
||||
<view class="header-content">
|
||||
<view class="uni-title">
|
||||
{{data.user_id && data.user_id[0] && data.user_id[0].nickname || '未知'}}
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<template v-slot:footer>
|
||||
<view class="footer">
|
||||
<view class="uni-note">更新于
|
||||
<uni-dateformat :date="data.last_modify_date" format="yyyy-MM-dd hh:mm"
|
||||
:threshold="[60000, 2592000000]" />
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</uni-list-item>
|
||||
</uni-list>
|
||||
<view class="banner">
|
||||
<!-- 文章开头,缩略图 -->
|
||||
<image class="banner-img" :src="data.thumbnail" mode="widthFix"></image>
|
||||
<!-- 文章摘要 -->
|
||||
<view class="banner-title">
|
||||
<text class="uni-ellipsis">{{data.excerpt}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="article-content">
|
||||
<rich-text :nodes="data.content"></rich-text>
|
||||
</view>
|
||||
</template>
|
||||
</unicloud-db>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// #ifdef APP
|
||||
import UniShare from '@/uni_modules/uni-share/js_sdk/uni-share.js';
|
||||
import uniNavBar from '@/uni_modules/uni-nav-bar/components/uni-nav-bar/uni-nav-bar.vue';
|
||||
const uniShare = new UniShare()
|
||||
// #endif
|
||||
const db = uniCloud.database();
|
||||
const readNewsLog = db.collection('read-news-log')
|
||||
export default {
|
||||
// #ifdef APP
|
||||
components: {
|
||||
"uni-nav-bar": uniNavBar
|
||||
},
|
||||
onBackPress({
|
||||
from
|
||||
}) {
|
||||
if (from == 'backbutton') {
|
||||
if (uniShare.isShow) {
|
||||
this.$nextTick(function() {
|
||||
console.log(uniShare);
|
||||
uniShare.hide()
|
||||
})
|
||||
}
|
||||
return uniShare.isShow;
|
||||
}
|
||||
},
|
||||
// #endif
|
||||
data() {
|
||||
return {
|
||||
// 当前显示 _id
|
||||
id: "",
|
||||
title: 'title',
|
||||
// 数据表名
|
||||
// 查询字段,多个字段用 , 分割
|
||||
field: 'user_id.nickname,user_id._id,avatar,excerpt,last_modify_date,comment_count,like_count,title,content',
|
||||
formData: {
|
||||
noData: '<p style="text-align:center;color:#666">详情加载中...</p>'
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
uniStarterConfig() {
|
||||
return getApp().globalData.config
|
||||
},
|
||||
where() {
|
||||
//拼接where条件 查询条件 ,更多详见 :https://uniapp.dcloud.net.cn/uniCloud/unicloud-db?id=jsquery
|
||||
return `_id =="${this.id}"`
|
||||
}
|
||||
},
|
||||
onLoad(event) {
|
||||
//获取真实新闻id,通常 id 来自上一个页面
|
||||
if (event.id) {
|
||||
this.id = event.id
|
||||
}
|
||||
//若上一页传递了标题过来,则设置导航栏标题
|
||||
if (event.title) {
|
||||
this.title = event.title
|
||||
uni.setNavigationBarTitle({
|
||||
title: event.title
|
||||
})
|
||||
}
|
||||
},
|
||||
onReady() {
|
||||
// 开始加载数据,修改 where 条件后才开始去加载 clinetDB 的数据 ,需要等组件渲染完毕后才开始执行 loadData,所以不能再 onLoad 中执行
|
||||
if (this.id) { // ID 不为空,则发起查询
|
||||
this.$refs.detail.loadData()
|
||||
} else {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: this.$t('listDetail.newsErr')
|
||||
})
|
||||
}
|
||||
},
|
||||
onNavigationBarButtonTap(event) {
|
||||
if (event.type == 'share') {
|
||||
this.shareClick();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
$log(...args) {
|
||||
console.log('args', ...args, this.id)
|
||||
},
|
||||
setReadNewsLog() {
|
||||
let item = {
|
||||
"article_id": this.id,
|
||||
"last_time": Date.now()
|
||||
},
|
||||
readNewsLog = uni.getStorageSync('readNewsLog') || [],
|
||||
index = -1;
|
||||
readNewsLog.forEach(({
|
||||
article_id
|
||||
}, i) => {
|
||||
if (article_id == item.article_id) {
|
||||
index = i
|
||||
}
|
||||
})
|
||||
if (index === -1) {
|
||||
readNewsLog.push(item)
|
||||
} else {
|
||||
readNewsLog.splice(index, 1, item)
|
||||
}
|
||||
uni.setStorageSync('readNewsLog', readNewsLog)
|
||||
console.log(readNewsLog);
|
||||
},
|
||||
setFavorite() {
|
||||
if (uniCloud.getCurrentUserInfo().tokenExpired < Date.now()) {
|
||||
return console.log('未登录用户');
|
||||
}
|
||||
let article_id = this.id,
|
||||
last_time = Date.now();
|
||||
console.log({
|
||||
article_id,
|
||||
last_time
|
||||
});
|
||||
readNewsLog.where(`"article_id" == "${article_id}" && "user_id"==$env.uid`)
|
||||
.update({
|
||||
last_time
|
||||
})
|
||||
.then(({
|
||||
result: {
|
||||
updated
|
||||
}
|
||||
}) => {
|
||||
console.log('updated', updated);
|
||||
if (!updated) {
|
||||
readNewsLog.add({
|
||||
article_id
|
||||
}).then(e => {
|
||||
console.log(e);
|
||||
}).catch(err => {
|
||||
console.log(err);
|
||||
})
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err);
|
||||
})
|
||||
},
|
||||
loadData(data) {
|
||||
//如果上一页未传递标题过来(如搜索直达详情),则从新闻详情中读取标题
|
||||
if (this.title == '' && data[0].title) {
|
||||
this.title = data[0].title
|
||||
uni.setNavigationBarTitle({
|
||||
title: data[0].title
|
||||
});
|
||||
|
||||
}
|
||||
this.setReadNewsLog();
|
||||
},
|
||||
/**
|
||||
* followClick
|
||||
* 点击关注
|
||||
*/
|
||||
followClick() {
|
||||
uni.showToast({
|
||||
title: this.$t('listDetail.follow'),
|
||||
icon: 'none'
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 分享该文章
|
||||
*/
|
||||
// #ifdef APP
|
||||
shareClick() {
|
||||
let {
|
||||
_id,
|
||||
title,
|
||||
excerpt,
|
||||
avatar
|
||||
} = this.$refs.detail.dataList
|
||||
console.log(JSON.stringify({
|
||||
_id,
|
||||
title,
|
||||
excerpt,
|
||||
avatar
|
||||
}));
|
||||
uniShare.show({
|
||||
content: { //公共的分享类型(type)、链接(herf)、标题(title)、summary(描述)、imageUrl(缩略图)
|
||||
type: 0,
|
||||
href: this.uniStarterConfig.h5.url +
|
||||
`/#/pages/uni-starter/list/detail?id=${_id}&title=${title}`,
|
||||
title: this.title,
|
||||
summary: excerpt,
|
||||
imageUrl: avatar +
|
||||
'?x-oss-process=image/resize,m_fill,h_100,w_100' //压缩图片解决,在ios端分享图过大导致的图片失效问题
|
||||
},
|
||||
menus: [{
|
||||
"img": "/static/app-plus/sharemenu/wechatfriend.png",
|
||||
"text": this.$t('common.wechatFriends'),
|
||||
"share": {
|
||||
"provider": "weixin",
|
||||
"scene": "WXSceneSession"
|
||||
}
|
||||
},
|
||||
{
|
||||
"img": "/static/app-plus/sharemenu/wechatmoments.png",
|
||||
"text": this.$t('common.wechatBbs'),
|
||||
"share": {
|
||||
"provider": "weixin",
|
||||
"scene": "WXSceneTimeline"
|
||||
}
|
||||
},
|
||||
{
|
||||
"img": "/static/app-plus/sharemenu/mp_weixin.png",
|
||||
"text": this.$t('common.wechatApplet'),
|
||||
"share": {
|
||||
provider: "weixin",
|
||||
scene: "WXSceneSession",
|
||||
type: 5,
|
||||
miniProgram: {
|
||||
id: this.uniStarterConfig.mp.weixin.id,
|
||||
path: `/pages/uni-starter/list/detail?id=${_id}&title=${title}`,
|
||||
webUrl: this.uniStarterConfig.h5.url +
|
||||
`/#/pages/uni-starter/list/detail?id=${_id}&title=${title}`,
|
||||
type: 0
|
||||
},
|
||||
}
|
||||
},
|
||||
{
|
||||
"img": "/static/app-plus/sharemenu/weibo.png",
|
||||
"text": this.$t('common.weibo'),
|
||||
"share": {
|
||||
"provider": "sinaweibo"
|
||||
}
|
||||
},
|
||||
{
|
||||
"img": "/static/app-plus/sharemenu/qq.png",
|
||||
"text": "QQ",
|
||||
"share": {
|
||||
"provider": "qq"
|
||||
}
|
||||
},
|
||||
{
|
||||
"img": "/static/app-plus/sharemenu/copyurl.png",
|
||||
"text": this.$t('common.copy'),
|
||||
"share": "copyurl"
|
||||
},
|
||||
{
|
||||
"img": "/static/app-plus/sharemenu/more.png",
|
||||
"text": this.$t('common.more'),
|
||||
"share": "shareSystem"
|
||||
}
|
||||
],
|
||||
cancelText: this.$t('common.cancelShare'),
|
||||
}, e => { //callback
|
||||
console.log(e);
|
||||
})
|
||||
}
|
||||
// #endif
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.header-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* 标题 */
|
||||
.uni-title {
|
||||
display: flex;
|
||||
margin-bottom: 5px;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
color: #3b4144;
|
||||
}
|
||||
|
||||
/* 描述 额外文本 */
|
||||
.uni-note {
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.footer-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
height: 30px;
|
||||
color: #fff;
|
||||
background-color: #ff5a5f;
|
||||
}
|
||||
|
||||
.banner {
|
||||
position: relative;
|
||||
margin: 0 15px;
|
||||
height: 180px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.banner-img {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.banner-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
padding: 0 15px;
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
height: 30px;
|
||||
font-size: 14px;
|
||||
color: #fff;
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.uni-ellipsis {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.article-title {
|
||||
padding: 20px 15px;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.article-content {
|
||||
padding: 15px;
|
||||
font-size: 15px;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
256
pages/uni-starter/list/list.nvue
Normal file
256
pages/uni-starter/list/list.nvue
Normal file
@ -0,0 +1,256 @@
|
||||
<template>
|
||||
<view class="pages">
|
||||
<!-- #ifndef H5 -->
|
||||
<statusBar></statusBar>
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- 搜索功能 -->
|
||||
<view class="uni-search-box">
|
||||
<uni-search-bar v-model="keyword" ref="searchBar" radius="100" cancelButton="none" disabled
|
||||
:placeholder="inputPlaceholder" />
|
||||
<view class="cover-search-bar" @click="searchClick"></view>
|
||||
</view>
|
||||
|
||||
<unicloud-db ref='udb' v-slot:default="{data,pagination,hasMore, loading, error, options}" @error="onqueryerror"
|
||||
:collection="colList" :page-size="10">
|
||||
<!-- 基于 uni-list 的页面布局 field="user_id.nickname"-->
|
||||
<uni-list class="uni-list" :border="false" :style="{height:listHight}">
|
||||
|
||||
<!-- 作用于app端nvue页面的下拉加载 -->
|
||||
<!-- #ifdef APP-NVUE -->
|
||||
<refreshBox @refresh="refresh" :loading="loading"></refreshBox>
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- 列表渲染 -->
|
||||
<uni-list-item :to="'./detail?id='+item._id+'&title='+item.title" v-for="(item,index) in data"
|
||||
:key="index">
|
||||
<!-- 通过header插槽定义列表左侧图片 -->
|
||||
<template v-slot:header>
|
||||
<image class="avatar" :src="item.avatar" mode="aspectFill"></image>
|
||||
</template>
|
||||
<!-- 通过body插槽定义布局 -->
|
||||
<template v-slot:body>
|
||||
<view class="main">
|
||||
<text class="title">{{item.title}}</text>
|
||||
<view class="info">
|
||||
<text class="author">{{item.user_id[0]?item.user_id[0].nickname:''}}</text>
|
||||
<uni-dateformat class="last_modify_date" :date="item.last_modify_date"
|
||||
format="yyyy-MM-dd" :threshold="[60000, 2592000000]" />
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</uni-list-item>
|
||||
<!-- 加载状态:上拉加载更多,加载中,没有更多数据了,加载错误 -->
|
||||
<!-- #ifdef APP-PLUS -->
|
||||
<uni-list-item>
|
||||
<template v-slot:body>
|
||||
<!-- #endif -->
|
||||
<uni-load-state @networkResume="refresh" :state="{data,pagination,hasMore, loading, error}"
|
||||
@loadMore="loadMore">
|
||||
</uni-load-state>
|
||||
<!-- #ifdef APP-PLUS -->
|
||||
</template>
|
||||
</uni-list-item>
|
||||
<!-- #endif -->
|
||||
</uni-list>
|
||||
</unicloud-db>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
let cdbRef;
|
||||
import statusBar from "@/uni_modules/uni-nav-bar/components/uni-nav-bar/uni-status-bar";
|
||||
|
||||
import Gps from '@/uni_modules/json-gps/js_sdk/gps.js';
|
||||
const gps = new Gps();
|
||||
|
||||
const db = uniCloud.database();
|
||||
const articleDBName = 'opendb-news-articles';
|
||||
const userDBName = 'uni-id-users';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
statusBar,
|
||||
},
|
||||
computed: {
|
||||
// 根据当前语言返回不同的搜索框占位符
|
||||
inputPlaceholder(e) {
|
||||
if (uni.getStorageSync('CURRENT_LANG') == "en") {
|
||||
return 'Please enter the search content'
|
||||
} else {
|
||||
return '请输入搜索内容'
|
||||
}
|
||||
},
|
||||
// 连表查询,返回两个集合的查询结果
|
||||
colList() {
|
||||
return [
|
||||
db.collection(articleDBName).where(this.where).field('thumbnail,title,publish_date,user_id')
|
||||
.getTemp(), // 文章集合
|
||||
db.collection(userDBName).field('_id,nickname').getTemp() // 用户集合
|
||||
]
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
where: '"article_status" == 1',
|
||||
keyword: "",
|
||||
showRefresh: false,
|
||||
listHight: 0
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
keyword(keyword, oldValue) {
|
||||
let where = '"article_status" == 1 '
|
||||
if (keyword) {
|
||||
this.where = where + `&& /${keyword}/.test(title)`;
|
||||
} else {
|
||||
this.where = where;
|
||||
}
|
||||
}
|
||||
},
|
||||
async onReady() {
|
||||
// #ifdef APP-NVUE
|
||||
/* 可用窗口高度 - 搜索框高 - 状态栏高 */
|
||||
this.listHight = uni.getSystemInfoSync().windowHeight - uni.getSystemInfoSync().statusBarHeight - 50 +
|
||||
'px';
|
||||
// #endif
|
||||
// #ifndef APP-NVUE
|
||||
this.listHight = 'auto'
|
||||
// #endif
|
||||
cdbRef = this.$refs.udb
|
||||
},
|
||||
async onShow() {
|
||||
this.keyword = getApp().globalData.searchText
|
||||
getApp().globalData.searchText = ''
|
||||
//这里仅演示如何,在onShow生命周期获取设备位置,并在设备或者应用没有权限时自动引导。设置完毕自动重新获取。
|
||||
//你可以基于他做自己的业务,比如:根据距离由近到远排序列表数据等
|
||||
// uni.showLoading({
|
||||
// title:"获取定位中"
|
||||
// });
|
||||
//默认h5端不获取定位
|
||||
// #ifndef H5
|
||||
let location = await gps.getLocation({
|
||||
geocode: true
|
||||
})
|
||||
// console.log(location);
|
||||
// #endif
|
||||
// if(location){
|
||||
// uni.showToast({
|
||||
// title: JSON.stringify(location),
|
||||
// icon: 'none'
|
||||
// });
|
||||
// }
|
||||
// uni.hideLoading()
|
||||
},
|
||||
methods: {
|
||||
searchClick(e) { //点击搜索框
|
||||
uni.hideKeyboard();
|
||||
uni.navigateTo({
|
||||
url: './search/search',
|
||||
animationType: 'fade-in'
|
||||
});
|
||||
},
|
||||
retry() {
|
||||
this.refresh()
|
||||
},
|
||||
refresh() {
|
||||
cdbRef.loadData({
|
||||
clear: true
|
||||
}, () => {
|
||||
uni.stopPullDownRefresh()
|
||||
// #ifdef APP-NVUE
|
||||
this.showRefresh = false
|
||||
// #endif
|
||||
console.log('end');
|
||||
})
|
||||
console.log('refresh');
|
||||
},
|
||||
loadMore() {
|
||||
cdbRef.loadMore()
|
||||
},
|
||||
onqueryerror(e) {
|
||||
console.error(e);
|
||||
},
|
||||
onpullingdown(e) {
|
||||
console.log(e);
|
||||
this.showRefresh = true
|
||||
if (e.pullingDistance > 100) {
|
||||
this.refresh()
|
||||
}
|
||||
}
|
||||
},
|
||||
// #ifndef APP-NVUE
|
||||
onPullDownRefresh() {
|
||||
this.refresh()
|
||||
},
|
||||
onReachBottom() {
|
||||
this.loadMore()
|
||||
}
|
||||
// #endif
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* #ifndef APP-NVUE */
|
||||
view {
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* #endif */
|
||||
.pages {
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
|
||||
.main {
|
||||
justify-content: space-between;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.info {
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.author,
|
||||
.last_modify_date {
|
||||
font-size: 14px;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.uni-search-box {
|
||||
background-color: #FFFFFF;
|
||||
position: sticky;
|
||||
height: 50px;
|
||||
top: 0;
|
||||
left: 0;
|
||||
/* #ifndef APP-PLUS */
|
||||
z-index: 9;
|
||||
/* #endif */
|
||||
/* #ifdef MP-WEIXIN */
|
||||
width: 580rpx;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.cover-search-bar {
|
||||
height: 50px;
|
||||
position: relative;
|
||||
top: -50px;
|
||||
margin-bottom: -50px;
|
||||
/* #ifndef APP-NVUE */
|
||||
z-index: 999;
|
||||
/* #endif */
|
||||
}
|
||||
</style>
|
505
pages/uni-starter/list/search/search.nvue
Normal file
505
pages/uni-starter/list/search/search.nvue
Normal file
@ -0,0 +1,505 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<view class="search-container">
|
||||
<!-- 搜索框 -->
|
||||
<view class="search-container-bar">
|
||||
<!-- #ifdef APP-PLUS -->
|
||||
<uni-icons class="search-icons" :color="iconColor" size="22" type="mic-filled" @click="speech" />
|
||||
<!-- #endif -->
|
||||
<!-- :cancelText="keyBoardPopup ? '取消' : '搜索'" -->
|
||||
<uni-search-bar ref="searchBar" style="flex:1;" radius="100" v-model="searchText" :focus="focus" :placeholder="hotWorld"
|
||||
clearButton="auto" cancelButton="always" @clear="clear" @confirm="confirm" @cancel="cancel" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="search-body">
|
||||
<!-- 搜索历史 -->
|
||||
<view class="word-container" v-if="localSearchList.length">
|
||||
<view class="word-container_header">
|
||||
<text class="word-container_header-text">搜索历史</text>
|
||||
<uni-icons v-if="!localSearchListDel" @click="localSearchListDel = true" class="search-icons" style="padding-right: 0;"
|
||||
:color="iconColor" size="18" type="trash"></uni-icons>
|
||||
<view v-else class="flex-center flex-row" style="font-weight: 500;justify-content: space-between;">
|
||||
<text style="font-size: 22rpx;color: #666;padding-top:4rpx;padding-bottom:4rpx;padding-right:20rpx;" @click="LocalSearchListClear">全部删除</text>
|
||||
<text style="font-size: 22rpx;color: #c0402b;padding-top:4rpx;padding-bottom:4rpx;padding-left:20rpx;" @click="localSearchListDel = false">完成</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="word-container_body">
|
||||
<view class="flex-center flex-row word-container_body-text" v-for="(word,index) in localSearchList" :key="index"
|
||||
@click="LocalSearchlistItemClick(word,index)">
|
||||
<text class="word-display" :key="word">{{word}}</text>
|
||||
<uni-icons v-if="localSearchListDel" size="12" type="closeempty" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 搜索发现 -->
|
||||
<view class="word-container">
|
||||
<view class="word-container_header">
|
||||
<view class="flex-center flex-row">
|
||||
<text class="word-container_header-text">搜索发现</text>
|
||||
<uni-icons v-if="!netHotListIsHide" class="search-icons" :color="iconColor" size="14" type="reload" @click="searchHotRefresh"></uni-icons>
|
||||
</view>
|
||||
<uni-icons class="search-icons" style="padding-right: 0;" :color="iconColor" size="18" :type="netHotListIsHide ? 'eye-slash' : 'eye'"
|
||||
@click="netHotListIsHide = !netHotListIsHide"></uni-icons>
|
||||
</view>
|
||||
|
||||
<unicloud-db ref="udb" #default="{data, loading, error, options}" field="content" collection="opendb-search-hot"
|
||||
orderby="create_date desc,count desc" page-data="replace" :page-size="10">
|
||||
<text v-if="loading && !netHotListIsHide" class="word-container_body-info">正在加载...</text>
|
||||
<view v-else class="word-container_body">
|
||||
<template v-if="!netHotListIsHide">
|
||||
<text v-if="error" class="word-container_body-info">{{error.message}}</text>
|
||||
<template v-else>
|
||||
<text v-for="(word,index) in data" class="word-container_body-text" :key="index" @click="search(word.content)">{{word.content}}</text>
|
||||
</template>
|
||||
</template>
|
||||
<view v-else style="flex:1;">
|
||||
<text class="word-container_body-info">当前搜索发现已隐藏</text>
|
||||
</view>
|
||||
</view>
|
||||
</unicloud-db>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 搜索联想 -->
|
||||
<view class="search-associative" v-if="associativeShow">
|
||||
<uni-list>
|
||||
<uni-list-item v-for="(item,index) in associativeList" :key="item._id" :ellipsis="1" :title="item.name" @click="associativeClick(item)" show-extra-icon
|
||||
clickable :extra-icon="{size:18,color:iconColor,type:'search'}" >
|
||||
</uni-list-item>
|
||||
</uni-list>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* 云端一体搜索模板
|
||||
* @description uniCloud云端一体搜索模板,自带下拉候选、历史搜索、热搜。无需再开发服务器代码
|
||||
*/
|
||||
const searchLogDbName = 'opendb-search-log'; // 搜索记录数据库
|
||||
const mallGoodsDbName = 'opendb-news-articles'; // 文章数据库
|
||||
const associativeSearchField = 'title'; // 联想时,搜索框值检索数据库字段名
|
||||
const associativeField = '_id,title'; // 联想列表每一项携带的字段
|
||||
const localSearchListKey = '__local_search_history'; // 本地历史存储字段名
|
||||
|
||||
// 数组去重
|
||||
const arrUnique = arr => {
|
||||
for (let i = arr.length - 1; i >= 0; i--) {
|
||||
const curIndex = arr.indexOf(arr[i]);
|
||||
const lastIndex = arr.lastIndexOf(arr[i])
|
||||
curIndex != lastIndex && arr.splice(lastIndex, 1)
|
||||
}
|
||||
return arr
|
||||
} // 节流
|
||||
// 防抖
|
||||
function debounce(fn, interval, isFirstAutoRun) {
|
||||
/**
|
||||
*
|
||||
* @param {要执行的函数} fn
|
||||
* @param {在操作多长时间后可再执行,第一次立即执行} interval
|
||||
*/
|
||||
var _self = fn;
|
||||
var timer = null;
|
||||
var first = true;
|
||||
|
||||
if (isFirstAutoRun) {
|
||||
_self();
|
||||
}
|
||||
|
||||
return function() {
|
||||
var args = arguments;
|
||||
var _me = this;
|
||||
if (first) {
|
||||
first = false;
|
||||
_self.apply(_me, args);
|
||||
}
|
||||
|
||||
if (timer) {
|
||||
clearTimeout(timer)
|
||||
// return false;
|
||||
}
|
||||
|
||||
timer = setTimeout(function() {
|
||||
clearTimeout(timer);
|
||||
timer = null;
|
||||
_self.apply(_me, args);
|
||||
}, interval || 200);
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
mallGoodsDbName,
|
||||
searchLogDbName,
|
||||
statusBarHeight:'0px',
|
||||
localSearchList: uni.getStorageSync(localSearchListKey),
|
||||
localSearchListDel: false,
|
||||
netHotListIsHide: false,
|
||||
searchText: '',
|
||||
iconColor: '#999999',
|
||||
associativeList: [],
|
||||
keyBoardPopup: false,
|
||||
hotWorld: 'DCloud', // 搜索热词,如果没有输入即回车,则搜索热词,但是不会加入搜索记录
|
||||
focus: true, // 是否自动聚焦
|
||||
speechEngine: 'iFly' // 语音识别引擎 iFly 讯飞 baidu 百度
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.db = uniCloud.database();
|
||||
this.searchLogDb = this.db.collection(this.searchLogDbName);
|
||||
this.mallGoodsDb = this.db.collection(this.mallGoodsDbName);
|
||||
// #ifndef H5
|
||||
uni.onKeyboardHeightChange((res) => {
|
||||
this.keyBoardPopup = res.height !== 0;
|
||||
})
|
||||
// #endif
|
||||
|
||||
this.searchText = getApp().globalData.searchText;
|
||||
},
|
||||
computed: {
|
||||
associativeShow() {
|
||||
return this.searchText && this.associativeList.length;
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
//#ifdef APP-PLUS
|
||||
this.statusBarHeight = `${uni.getSystemInfoSync().statusBarHeight}px`;
|
||||
//#endif
|
||||
},
|
||||
methods: {
|
||||
clear(res) {
|
||||
console.log("res: ", res);
|
||||
},
|
||||
confirm(res) {
|
||||
// 键盘确认
|
||||
this.search(res.value);
|
||||
},
|
||||
cancel(res) {
|
||||
uni.hideKeyboard();
|
||||
this.searchText = '';
|
||||
this.loadList();
|
||||
},
|
||||
search(value) {
|
||||
if (!value && !this.hotWorld) {
|
||||
return;
|
||||
}
|
||||
if (value) {
|
||||
if (this.searchText !== value) {
|
||||
this.searchText = value
|
||||
}
|
||||
|
||||
this.localSearchListManage(value);
|
||||
|
||||
this.searchLogDbAdd(value)
|
||||
} else if (this.hotWorld) {
|
||||
this.searchText = this.hotWorld
|
||||
}
|
||||
|
||||
uni.hideKeyboard();
|
||||
this.loadList(this.searchText);
|
||||
},
|
||||
localSearchListManage(word) {
|
||||
let list = uni.getStorageSync(localSearchListKey);
|
||||
if (list.length) {
|
||||
this.localSearchList.unshift(word);
|
||||
arrUnique(this.localSearchList);
|
||||
if (this.localSearchList.length > 10) {
|
||||
this.localSearchList.pop();
|
||||
}
|
||||
} else {
|
||||
this.localSearchList = [word];
|
||||
}
|
||||
uni.setStorageSync(localSearchListKey, this.localSearchList);
|
||||
},
|
||||
LocalSearchListClear() {
|
||||
uni.showModal({
|
||||
content: "确认清空搜索历史吗",
|
||||
confirmText: "删除",
|
||||
confirmColor: 'red',
|
||||
cancelColor: '#808080',
|
||||
success: res => {
|
||||
if (res.confirm) {
|
||||
this.localSearchListDel = false;
|
||||
this.localSearchList = [];
|
||||
uni.removeStorageSync(localSearchListKey)
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
LocalSearchlistItemClick(word, index) {
|
||||
if (this.localSearchListDel) {
|
||||
this.localSearchList.splice(index, 1);
|
||||
uni.setStorageSync(localSearchListKey, this.localSearchList);
|
||||
if (!this.localSearchList.length) {
|
||||
this.localSearchListDel = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
this.search(word);
|
||||
},
|
||||
searchHotRefresh() {
|
||||
this.$refs.udb.refresh();
|
||||
},
|
||||
speech() {
|
||||
// #ifdef APP-PLUS
|
||||
plus.speech.startRecognize({
|
||||
engine: this.speechEngine,
|
||||
punctuation: false, // 标点符号
|
||||
timeout: 10000
|
||||
}, word => {
|
||||
word = word instanceof Array ? word[0] : word;
|
||||
this.search(word)
|
||||
}, err => {
|
||||
console.error("语音识别错误: ", err);
|
||||
});
|
||||
// #endif
|
||||
},
|
||||
searchLogDbAdd(value) {
|
||||
/*
|
||||
在此处存搜索记录,如果登录则需要存 user_id,若未登录则存device_id
|
||||
*/
|
||||
this.getDeviceId().then(device_id => {
|
||||
this.searchLogDb.add({
|
||||
// user_id: device_id,
|
||||
device_id,
|
||||
content: value,
|
||||
create_date: Date.now()
|
||||
})
|
||||
})
|
||||
},
|
||||
getDeviceId() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const uniId = uni.getStorageSync('uni_id');
|
||||
if (!uniId) {
|
||||
// #ifdef APP-PLUS
|
||||
plus.device.getInfo({
|
||||
success: (deviceInfo) => {
|
||||
resolve(deviceInfo.uuid)
|
||||
},
|
||||
fail: () => {
|
||||
resolve(uni.getSystemInfoSync().system + '_' + Math.random().toString(36).substr(2))
|
||||
}
|
||||
});
|
||||
// #endif
|
||||
// #ifndef APP-PLUS
|
||||
resolve(uni.getSystemInfoSync().system + '_' + Math.random().toString(36).substr(2))
|
||||
// #endif
|
||||
} else {
|
||||
resolve(uniId)
|
||||
}
|
||||
})
|
||||
},
|
||||
associativeClick(item) {
|
||||
/**
|
||||
* 注意:这里用户根据自己的业务需要,选择跳转的页面即可
|
||||
*/
|
||||
console.log("associativeClick: ", item);
|
||||
this.loadList(item.title);
|
||||
},
|
||||
loadList(text = '') {
|
||||
getApp().globalData.searchText = text;
|
||||
uni.switchTab({
|
||||
url:'/pages/list/list'
|
||||
})
|
||||
},
|
||||
backPage(){
|
||||
uni.navigateBack();
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
searchText: debounce(function(value) {
|
||||
if (value) {
|
||||
this.mallGoodsDb.where({
|
||||
[associativeSearchField]: new RegExp(value, 'gi'),
|
||||
}).field(associativeField).get().then(res => {
|
||||
this.associativeList = res.result.data;
|
||||
})
|
||||
} else {
|
||||
this.associativeList.length = 0;
|
||||
getApp().globalData.searchText = '';
|
||||
}
|
||||
}, 100)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* #ifndef APP-NVUE */
|
||||
page {
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
}
|
||||
/* #endif */
|
||||
</style>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
$search-bar-height:52px;
|
||||
$word-container_header-height:72rpx;
|
||||
.status-bar{
|
||||
background-color: #fff;
|
||||
}
|
||||
.container {
|
||||
/* #ifndef APP-NVUE */
|
||||
height: 100%;
|
||||
/* #endif */
|
||||
flex: 1;
|
||||
background-color: #f7f7f7;
|
||||
}
|
||||
|
||||
.search-body {
|
||||
background-color: #fff;
|
||||
border-bottom-right-radius: 10px;
|
||||
border-bottom-left-radius: 10px;
|
||||
}
|
||||
|
||||
@mixin uni-flex {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
@mixin words-display {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.flex-center {
|
||||
@include uni-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.flex-row {
|
||||
@include uni-flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
/* #ifdef APP-PLUS */
|
||||
/* #ifndef APP-NVUE || VUE3*/
|
||||
::v-deep
|
||||
/* #endif */
|
||||
.uni-searchbar {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
/* #endif */
|
||||
|
||||
/* #ifndef APP-NVUE || VUE3*/
|
||||
::v-deep
|
||||
/* #endif */
|
||||
.uni-searchbar__box {
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
/* #ifndef APP-NVUE || VUE3 */
|
||||
::v-deep
|
||||
/* #endif */
|
||||
.uni-input-placeholder {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.search-container {
|
||||
height: $search-bar-height;
|
||||
@include uni-flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
background-color: #fff;
|
||||
|
||||
@at-root {
|
||||
#{&}-bar {
|
||||
@include uni-flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.search-associative {
|
||||
/* #ifndef APP-NVUE */
|
||||
overflow-y: auto;
|
||||
/* #endif */
|
||||
position: absolute;
|
||||
top: $search-bar-height;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: #fff;
|
||||
margin-top: 10rpx;
|
||||
padding-left: 10rpx;
|
||||
padding-right: 10rpx;
|
||||
}
|
||||
|
||||
.search-icons {
|
||||
padding: 16rpx;
|
||||
}
|
||||
|
||||
.word-display {
|
||||
@include words-display;
|
||||
}
|
||||
|
||||
.word-container {
|
||||
padding: 20rpx;
|
||||
|
||||
@at-root {
|
||||
#{&}_header {
|
||||
@include uni-flex;
|
||||
height: $word-container_header-height;
|
||||
line-height: $word-container_header-height;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
@at-root {
|
||||
#{&}-text {
|
||||
color: #3e3e3e;
|
||||
font-size: 30rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#{&}_body {
|
||||
@include uni-flex;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: row;
|
||||
|
||||
@at-root {
|
||||
#{&}-text {
|
||||
@include uni-flex;
|
||||
@include words-display;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: #f6f6f6;
|
||||
padding: 10rpx 20rpx;
|
||||
margin: 20rpx 30rpx 0 0;
|
||||
border-radius: 30rpx;
|
||||
/* #ifndef APP-NVUE */
|
||||
box-sizing: border-box;
|
||||
/* #endif */
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#{&}-info {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: block;
|
||||
/* #endif */
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
font-size: 26rpx;
|
||||
color: #808080;
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
Reference in New Issue
Block a user