首次完整推送,
V:1.20240808.006
This commit is contained in:
223
uni_modules/uni-cms-article/pages/detail/detail.uvue
Normal file
223
uni_modules/uni-cms-article/pages/detail/detail.uvue
Normal file
@ -0,0 +1,223 @@
|
||||
<template>
|
||||
<scroll-view :scroll-y="true" v-if="!loading">
|
||||
<view class="meta">
|
||||
<view class="title">
|
||||
<text class="text">{{ articleDetail.title }}</text>
|
||||
</view>
|
||||
<view class="excerpt">
|
||||
<text class="text">{{ articleDetail.excerpt }}</text>
|
||||
</view>
|
||||
<view class="author">
|
||||
<template v-if="articleDetail.user_id != null">
|
||||
<text class="at">{{ articleDetail.user_id?.nickname ?? '' }}</text>
|
||||
<text class="split">·</text>
|
||||
</template>
|
||||
<text class="date">{{ publishTime(articleDetail.publish_date as number) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="content" v-if="articleDetail.content != null">
|
||||
<template v-for="(block, index) in articleDetail.content" :key="index">
|
||||
<view v-if="block.type == 'mediaVideo'">
|
||||
<video
|
||||
style="width: 300px; height: 200px; margin: 0 auto 20px;"
|
||||
:src="(block.data as UTSJSONObject).getJSON('attributes')!.getString('src')!"
|
||||
:poster="(block.data as UTSJSONObject).getJSON('attributes')!.getString('poster')!"
|
||||
></video>
|
||||
</view>
|
||||
<view v-if="block.type == 'divider'" class="divider"></view>
|
||||
<view v-if="block.type == 'unlockContent'" class="unlock-content">
|
||||
<button @click="unlockContent">请观看广告后解锁全文</button>
|
||||
</view>
|
||||
<rich-text v-if="block.type == 'rich-text'" :selectable="false" :nodes="block.data" @itemclick="richTextItemClick"></rich-text>
|
||||
<render-image-component v-if="block.type == 'image'" :deltaOp="block.data" @image-preview="onImagePreview"></render-image-component>
|
||||
</template>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</template>
|
||||
|
||||
<script lang="uts">
|
||||
import translatePublishTime from "@/uni_modules/uni-cms-article/common/publish-time";
|
||||
import RenderImageComponent from '@/uni_modules/uni-cms-article/components/render-article-detail/image.uvue'
|
||||
|
||||
type Author = {
|
||||
_id: string
|
||||
nickname: string
|
||||
}
|
||||
type Content = {
|
||||
type: string
|
||||
data: any
|
||||
}
|
||||
type Article = {
|
||||
_id: string | null
|
||||
title: string | null
|
||||
content: Content[] | null
|
||||
excerpt: string | null
|
||||
publish_date: number | null
|
||||
user_id: Author | null
|
||||
thumbnail: string[] | null
|
||||
content_images: string[] | null
|
||||
}
|
||||
|
||||
const db = uniCloud.databaseForJQL()
|
||||
const articleDBName = 'uni-cms-articles'
|
||||
const userDBName = 'uni-id-users'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
RenderImageComponent
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
id: "", // 文章ID
|
||||
title: "", // 文章标题
|
||||
articleDetail: {} as Article, // 文章详情
|
||||
// 广告相关配置
|
||||
adpId: "", // TODO: 请填写广告位ID
|
||||
watchAdUniqueType: "device" // TODO: 观看广告的唯一标识类型,可选值为 user 或者 device,user 表示用户唯一,device 表示设备唯一
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
where(): string {
|
||||
//拼接where条件 查询条件 ,更多详见 :https://uniapp.dcloud.net.cn/uniCloud/unicloud-db?id=jsquery
|
||||
return `_id =="${this.id}"`
|
||||
},
|
||||
collection(): any[] {
|
||||
return [
|
||||
db.collection(articleDBName).where(this.where).field('user_id,thumbnail,excerpt,publish_date,title,content').getTemp(),
|
||||
db.collection(userDBName).field('_id, nickname').getTemp()
|
||||
]
|
||||
}
|
||||
},
|
||||
onLoad(event: OnLoadOptions) {
|
||||
if (event.has('id')) {
|
||||
this.id = event.get('id') as string
|
||||
this.load()
|
||||
}
|
||||
|
||||
if (event.has('title')) {
|
||||
uni.setNavigationBarTitle({
|
||||
title: event.get('title') as string
|
||||
})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async load (): Promise<void> {
|
||||
uni.showLoading({
|
||||
title: "加载中..."
|
||||
})
|
||||
const articledb = db.collection(articleDBName).where(this.where).field('user_id,thumbnail,excerpt,publish_date,title,content').getTemp()
|
||||
const userdb = db.collection(userDBName).field('_id, nickname').getTemp()
|
||||
const res = await db.collection(articledb, userdb).get()
|
||||
|
||||
this.loadData(res.data)
|
||||
this.loading = false
|
||||
uni.hideLoading()
|
||||
},
|
||||
// 格式化发布时间
|
||||
publishTime(timestamp: number): string {
|
||||
return translatePublishTime(timestamp)
|
||||
},
|
||||
loadData(data: UTSJSONObject[]) {
|
||||
if (data.length <= 0) return
|
||||
const detail = data[0]
|
||||
|
||||
const user_id = detail.getArray<Author>('user_id')!;
|
||||
|
||||
this.articleDetail = {
|
||||
title: detail.getString('title'),
|
||||
content: detail.getArray<Content>('content'),
|
||||
excerpt: detail.getString('excerpt'),
|
||||
publish_date: detail.getNumber('publish_date'),
|
||||
thumbnail: detail.getArray<string>('thumbnail'),
|
||||
user_id: user_id.length > 0 ? user_id[0]: null,
|
||||
content_images: detail.getArray<string>('content_images')
|
||||
} as Article
|
||||
|
||||
this.title = detail.getString('title')!
|
||||
|
||||
uni.setNavigationBarTitle({
|
||||
title: this.title
|
||||
})
|
||||
},
|
||||
unlockContent () {
|
||||
uni.showModal({
|
||||
content: 'uni-app-x 暂不支持观看广告解锁全文',
|
||||
showCancel: false
|
||||
})
|
||||
},
|
||||
richTextItemClick (e: RichTextItemClickEvent) {
|
||||
if (e.detail.href != null) {
|
||||
uni.navigateTo({
|
||||
url: `/uni_modules/uni-cms-article/pages/webview/webview?url=${encodeURIComponent(e.detail.href as string)}`
|
||||
})
|
||||
}
|
||||
},
|
||||
onImagePreview (url: string) {
|
||||
const contentImages = this.articleDetail.content_images != null ? this.articleDetail.content_images: [] as string[]
|
||||
|
||||
uni.previewImage({
|
||||
current: url, // 当前显示图片的http链接
|
||||
urls: contentImages as string[] // 需要预览的图片http链接列表
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.meta {
|
||||
padding: 20rpx 30rpx 0;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
|
||||
.title {
|
||||
.text {
|
||||
font-size: 40rpx;
|
||||
line-height: 66rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.excerpt {
|
||||
margin-top: 10rpx;
|
||||
|
||||
.text {
|
||||
font-size: 26rpx;
|
||||
line-height: 40rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
.author {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
flex-direction: row;
|
||||
margin-top: 20rpx;
|
||||
|
||||
.at,
|
||||
.split,
|
||||
.date {
|
||||
font-size: 26rpx;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.split {
|
||||
margin: 0 10rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
.content {
|
||||
margin-top: 40rpx;
|
||||
padding: 0 30rpx 80rpx;
|
||||
}
|
||||
.divider {
|
||||
height: 1px;
|
||||
width: 100%;
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
</style>
|
||||
|
217
uni_modules/uni-cms-article/pages/detail/detail.vue
Normal file
217
uni_modules/uni-cms-article/pages/detail/detail.vue
Normal file
@ -0,0 +1,217 @@
|
||||
<template>
|
||||
<unicloud-db v-slot:default="{data, loading, error, options}" :collection="collection" :options="formData"
|
||||
:getone="true" :where="where" :manual="true" ref="detail" foreignKey="uni-cms-articles.user_id"
|
||||
@load="loadData"
|
||||
class="article">
|
||||
<template v-if="!loading && data">
|
||||
<view class="meta">
|
||||
<view class="title">
|
||||
<text class="text">{{ data.title }}</text>
|
||||
</view>
|
||||
<view class="excerpt">
|
||||
<text class="text">{{ data.excerpt }}</text>
|
||||
</view>
|
||||
<view class="author">
|
||||
<template v-if="data.user_id[0]">
|
||||
<text class="at">{{ data.user_id[0].nickname || '' }}</text>
|
||||
<text class="split">·</text>
|
||||
</template>
|
||||
<text class="date">{{ publishTime(data.publish_date) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<render-article-detail
|
||||
:content="data.content"
|
||||
:content-images="data.content_images"
|
||||
:ad-config="{ adpId, watchAdUniqueType }"
|
||||
></render-article-detail>
|
||||
</template>
|
||||
<view class="detail-loading" v-else>
|
||||
<uni-icons type="spinner-cycle" size="35px"/>
|
||||
</view>
|
||||
</unicloud-db>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import uniNavBar from '@/uni_modules/uni-nav-bar/components/uni-nav-bar/uni-nav-bar.vue';
|
||||
import renderArticleDetail from "@/uni_modules/uni-cms-article/components/render-article-detail/index.vue";
|
||||
import translatePublishTime from "@/uni_modules/uni-cms-article/common/publish-time";
|
||||
|
||||
const db = uniCloud.database()
|
||||
const articleDBName = 'uni-cms-articles'
|
||||
const userDBName = 'uni-id-users'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
uniNavBar,
|
||||
renderArticleDetail
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
id: "", // 文章ID
|
||||
title: "", // 文章标题
|
||||
formData: {}, // 表单数据
|
||||
|
||||
// 广告相关配置
|
||||
adpId: "", // TODO: 请填写广告位ID
|
||||
watchAdUniqueType: "device" // TODO: 观看广告的唯一标识类型,可选值为 user 或者 device,user 表示用户唯一,device 表示设备唯一
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
where() {
|
||||
//拼接where条件 查询条件 ,更多详见 :https://uniapp.dcloud.net.cn/uniCloud/unicloud-db?id=jsquery
|
||||
return `_id =="${this.id}"`
|
||||
},
|
||||
collection() {
|
||||
return [
|
||||
db.collection(articleDBName).where(this.where).field('user_id,thumbnail,excerpt,publish_date,title,content').getTemp(),
|
||||
db.collection(userDBName).field('_id, nickname').getTemp()
|
||||
]
|
||||
}
|
||||
},
|
||||
onReady() {
|
||||
// 开始加载数据,修改 where 条件后才开始去加载 clinetDB 的数据 ,需要等组件渲染完毕后才开始执行 loadData,所以不能再 onLoad 中执行
|
||||
if (this.id) { // ID 不为空,则发起查询
|
||||
this.$refs.detail.loadData()
|
||||
} else {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: 'id 不能为空'
|
||||
})
|
||||
}
|
||||
},
|
||||
onLoad(event) {
|
||||
//获取文章id,通常 id 来自上一个页面
|
||||
if (event.id) {
|
||||
this.id = event.id
|
||||
}
|
||||
|
||||
// 监听解锁内容事件
|
||||
uni.$on('onUnlockContent', this.onUnlockContent)
|
||||
},
|
||||
onUnload() {
|
||||
// 页面卸载时,移除监听事件
|
||||
uni.$off('onUnlockContent', this.onUnlockContent)
|
||||
},
|
||||
onPageScroll(e) {
|
||||
// 根据滚动位置判断是否显示导航栏
|
||||
if (e.scrollTop > 100) {
|
||||
uni.setNavigationBarTitle({
|
||||
title: this.title
|
||||
})
|
||||
} else {
|
||||
uni.setNavigationBarTitle({
|
||||
title: ''
|
||||
})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 将时间戳转换为可读的时间格式
|
||||
publishTime(timestamp) {
|
||||
return translatePublishTime(timestamp)
|
||||
},
|
||||
// 将文章加入阅读历史
|
||||
setReadHistory() {
|
||||
// 获取阅读历史缓存,如果不存在则为空数组
|
||||
const historyCache = uni.getStorageSync('readHistory') || []
|
||||
// 过滤掉当前文章的阅读历史
|
||||
const readHistory = historyCache.filter(item => item.article_id !== this.id)
|
||||
// 将当前文章的阅读历史添加到数组最前面
|
||||
readHistory.unshift({
|
||||
article_id: this.id,
|
||||
last_time: Date.now()
|
||||
})
|
||||
// 将更新后的阅读历史缓存到本地
|
||||
uni.setStorageSync('readHistory', readHistory)
|
||||
|
||||
},
|
||||
// 加载数据
|
||||
loadData(data) {
|
||||
// 设置文章标题
|
||||
this.title = data.title
|
||||
|
||||
// 将文章添加进阅读历史
|
||||
this.setReadHistory()
|
||||
},
|
||||
// 监听解锁内容事件,解锁内容后重新加载数据
|
||||
async onUnlockContent() {
|
||||
this.$refs.detail.loadData()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
/* #ifdef APP-NVUE */
|
||||
.article {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
/* #endif */
|
||||
|
||||
@mixin cp {
|
||||
padding: 0 30rpx;
|
||||
}
|
||||
|
||||
.detail-loading {
|
||||
margin: 100rpx auto 0;
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
animation: rotate360 2s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes rotate360 {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
transform-origin: center center;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
transform-origin: center center;
|
||||
}
|
||||
}
|
||||
|
||||
.meta {
|
||||
@include cp;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
padding-top: 20rpx;
|
||||
.title {
|
||||
.text {
|
||||
font-size: 40rpx;
|
||||
line-height: 66rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.excerpt {
|
||||
margin-top: 10rpx;
|
||||
.text {
|
||||
font-size: 26rpx;
|
||||
line-height: 40rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
.author {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
flex-direction: row;
|
||||
margin-top: 20rpx;
|
||||
|
||||
.at,
|
||||
.split,
|
||||
.date {
|
||||
font-size: 26rpx;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.split {
|
||||
margin: 0 10rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
255
uni_modules/uni-cms-article/pages/detail/preview.vue
Normal file
255
uni_modules/uni-cms-article/pages/detail/preview.vue
Normal file
@ -0,0 +1,255 @@
|
||||
<template>
|
||||
<unicloud-db v-slot:default="{loading, error, options}" :collection="collection" :options="formData"
|
||||
:getone="true" :where="where" :manual="true" ref="detail" foreignKey="uni-cms-articles.user_id"
|
||||
@load="loadData"
|
||||
class="article">
|
||||
<template v-if="!loading && articleData">
|
||||
<view class="preview-tip">此页面仅用于临时预览文章,链接将会在短期内失效。</view>
|
||||
<view class="meta">
|
||||
<view class="title">
|
||||
<text class="text">{{ articleData.title }}</text>
|
||||
</view>
|
||||
<view class="excerpt">
|
||||
<text class="text">{{ articleData.excerpt }}</text>
|
||||
</view>
|
||||
<view class="author">
|
||||
<template v-if="articleData.user_id && articleData.user_id[0]">
|
||||
<text class="at">{{ articleData.user_id[0].nickname || '' }}</text>
|
||||
<text class="split">·</text>
|
||||
</template>
|
||||
<text class="date">{{ publishTime(articleData.publish_date) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<render-article-detail
|
||||
:content="articleData.content"
|
||||
:content-images="articleData.content_images"
|
||||
:ad-config="{ adpId, watchAdUniqueType }"
|
||||
></render-article-detail>
|
||||
</template>
|
||||
<view class="detail-loading" v-else>
|
||||
<uni-icons type="spinner-cycle" size="35px"/>
|
||||
</view>
|
||||
</unicloud-db>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import uniNavBar from '@/uni_modules/uni-nav-bar/components/uni-nav-bar/uni-nav-bar.vue';
|
||||
import renderArticleDetail from "@/uni_modules/uni-cms-article/components/render-article-detail/index.vue";
|
||||
import translatePublishTime from "@/uni_modules/uni-cms-article/common/publish-time";
|
||||
|
||||
const db = uniCloud.database()
|
||||
const articleDBName = 'uni-cms-articles'
|
||||
const userDBName = 'uni-id-users'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
uniNavBar,
|
||||
renderArticleDetail
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
id: "", // 文章ID
|
||||
title: "", // 文章标题
|
||||
secret: "", // 文章预览密钥
|
||||
formData: {}, // 表单数据
|
||||
articleData: null, // 文章数据
|
||||
|
||||
// 广告相关配置
|
||||
adpId: "", // TODO: 请填写广告位ID
|
||||
watchAdUniqueType: "device" // TODO: 观看广告的唯一标识类型,可选值为 user 或者 device,user 表示用户唯一,device 表示设备唯一
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
where() {
|
||||
//拼接where条件 查询条件 ,更多详见 :https://uniapp.dcloud.net.cn/uniCloud/unicloud-db?id=jsquery
|
||||
return `_id =="${this.id}" && preview_secret =="${this.secret}"`
|
||||
},
|
||||
collection() {
|
||||
return [
|
||||
db.collection(articleDBName).where(this.where).field('user_id,thumbnail,excerpt,publish_date,title,content,preview_secret,preview_expired,article_status').getTemp(),
|
||||
db.collection(userDBName).field('_id, nickname').getTemp()
|
||||
]
|
||||
}
|
||||
},
|
||||
onReady() {
|
||||
// 开始加载数据,修改 where 条件后才开始去加载 clinetDB 的数据 ,需要等组件渲染完毕后才开始执行 loadData,所以不能再 onLoad 中执行
|
||||
if (this.id) { // ID 不为空,则发起查询
|
||||
this.$refs.detail.loadData()
|
||||
} else {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: 'id 不能为空'
|
||||
})
|
||||
}
|
||||
},
|
||||
onLoad(event) {
|
||||
//获取文章id,通常 id 来自上一个页面
|
||||
if (event.id) {
|
||||
this.id = event.id
|
||||
this.secret = event.secret
|
||||
}
|
||||
|
||||
// 监听解锁内容事件
|
||||
uni.$on('onUnlockContent', this.onUnlockContent)
|
||||
},
|
||||
onUnload() {
|
||||
// 页面卸载时,移除监听事件
|
||||
uni.$off('onUnlockContent', this.onUnlockContent)
|
||||
},
|
||||
onPageScroll(e) {
|
||||
// 根据滚动位置判断是否显示导航栏
|
||||
if (e.scrollTop > 100) {
|
||||
uni.setNavigationBarTitle({
|
||||
title: this.title
|
||||
})
|
||||
} else {
|
||||
uni.setNavigationBarTitle({
|
||||
title: ''
|
||||
})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 将时间戳转换为可读的时间格式
|
||||
publishTime(timestamp) {
|
||||
return translatePublishTime(timestamp)
|
||||
},
|
||||
// 加载数据
|
||||
loadData(data) {
|
||||
if (!data) {
|
||||
return uni.showModal({
|
||||
content: "文章不存在/预览密钥不存在",
|
||||
showCancel: false,
|
||||
success: () => {
|
||||
// #ifdef H5
|
||||
window.close()
|
||||
// #endif
|
||||
|
||||
// #ifndef H5
|
||||
uni.navigateBack()
|
||||
// #endif
|
||||
}
|
||||
})
|
||||
}
|
||||
// 文章已发布,跳转到文章详情页
|
||||
if (data.article_status === 1) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '文章已发布'
|
||||
})
|
||||
uni.redirectTo({
|
||||
url: `/uni_modules/uni-cms-article/pages/detail/detail?id=${this.id}`
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 预览已过期,提示用户
|
||||
if (data.preview_expired < Date.now()) {
|
||||
return uni.showModal({
|
||||
content: "预览已失效",
|
||||
showCancel: false,
|
||||
success: () => {
|
||||
// #ifdef H5
|
||||
window.close()
|
||||
// #endif
|
||||
|
||||
// #ifndef H5
|
||||
uni.navigateBack()
|
||||
// #endif
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 设置文章标题
|
||||
this.title = data.title
|
||||
this.articleData = data
|
||||
},
|
||||
// 监听解锁内容事件,解锁内容后重新加载数据
|
||||
async onUnlockContent() {
|
||||
this.$refs.detail.loadData()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
/* #ifdef APP-NVUE */
|
||||
.article {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
/* #endif */
|
||||
|
||||
@mixin cp {
|
||||
padding: 0 30rpx;
|
||||
}
|
||||
|
||||
.detail-loading {
|
||||
margin: 100rpx auto 0;
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
animation: rotate360 2s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes rotate360 {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
transform-origin: center center;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
transform-origin: center center;
|
||||
}
|
||||
}
|
||||
|
||||
.meta {
|
||||
@include cp;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
padding-top: 20rpx;
|
||||
.title {
|
||||
.text {
|
||||
font-size: 40rpx;
|
||||
line-height: 66rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.excerpt {
|
||||
margin-top: 10rpx;
|
||||
.text {
|
||||
font-size: 26rpx;
|
||||
line-height: 40rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
.author {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
flex-direction: row;
|
||||
margin-top: 20rpx;
|
||||
|
||||
.at,
|
||||
.split,
|
||||
.date {
|
||||
font-size: 26rpx;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.split {
|
||||
margin: 0 10rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.preview-tip {
|
||||
font-size: 13px;
|
||||
color: #333;
|
||||
background: #fcd791;
|
||||
padding: 10px;
|
||||
}
|
||||
</style>
|
298
uni_modules/uni-cms-article/pages/list/list.nvue
Normal file
298
uni_modules/uni-cms-article/pages/list/list.nvue
Normal file
@ -0,0 +1,298 @@
|
||||
<template>
|
||||
<view class="pages">
|
||||
<view class="placeholder-bar">
|
||||
<statusBar></statusBar>
|
||||
<view :style="{ height: `${navBarHeight}px` }"></view>
|
||||
</view>
|
||||
|
||||
<view class="nav-box">
|
||||
<!-- #ifndef H5 -->
|
||||
<statusBar></statusBar>
|
||||
<!-- #endif -->
|
||||
<view class="nav" :style="{ height: `${navBarHeight}px` }">
|
||||
<!-- #ifdef MP -->
|
||||
<view class="mp-button-left-placeholder" :style="{ width: `${mpButtonLeftPlaceholderSize}px` }"></view>
|
||||
<!-- #endif -->
|
||||
<!-- 搜索功能 -->
|
||||
<view class="uni-search-box">
|
||||
<uni-search-bar ref="searchBar" radius="100" cancelButton="none" disabled
|
||||
:placeholder="inputPlaceholder"/>
|
||||
<view class="cover-search-bar" @click="searchClick"></view>
|
||||
</view>
|
||||
<!-- #ifdef MP -->
|
||||
<view class="mp-button-placeholder" :style="{ width: `${mpButtonPlaceholderSize}px` }"></view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<unicloud-db ref='udb' v-slot:default="{ pagination, hasMore, loading, error, options }" @error="onqueryerror"
|
||||
:collection="colList" :page-size="10" orderby="publish_date desc" @load="listLoad">
|
||||
<!-- 基于 uni-list 的页面布局 field="user_id.nickname"-->
|
||||
<!-- #ifdef APP-NVUE -->
|
||||
<list class="uni-list" :border="false" :style="{ height: listHeight }">
|
||||
<!-- #endif -->
|
||||
<!-- #ifndef APP-NVUE -->
|
||||
<scroll-view
|
||||
scroll-y
|
||||
class="uni-list"
|
||||
refresher-enabled
|
||||
:refresher-triggered="loadType=== 'refresh'"
|
||||
:style="{ height: listHeight }"
|
||||
@refresherrefresh="refresh"
|
||||
@scrolltolower="loadMore"
|
||||
>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef APP-NVUE -->
|
||||
<refresh-box :loading="loading" @refresh="refresh"></refresh-box>
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- 列表渲染 -->
|
||||
<template v-for="item in listData">
|
||||
<not-cover v-if="item.thumbnail && item.thumbnail.length === 0" :data="item"></not-cover>
|
||||
<right-small-cover v-else-if="item.thumbnail && item.thumbnail.length === 1"
|
||||
:data="item"></right-small-cover>
|
||||
<three-cover v-else-if="item.thumbnail && item.thumbnail.length === 3"
|
||||
:data="item"></three-cover>
|
||||
</template>
|
||||
|
||||
<!-- 加载状态:上拉加载更多,加载中,没有更多数据了,加载错误 -->
|
||||
<!-- #ifdef APP-PLUS -->
|
||||
<uni-list-item>
|
||||
<template v-slot:body>
|
||||
<!-- #endif -->
|
||||
<uni-load-state @networkResume="refresh"
|
||||
:state="{ data: listData, pagination, hasMore, loading, error }"
|
||||
@loadMore="loadMore">
|
||||
</uni-load-state>
|
||||
<!-- #ifdef APP-PLUS -->
|
||||
</template>
|
||||
</uni-list-item>
|
||||
<!-- #endif -->
|
||||
<!-- #ifndef APP-NVUE -->
|
||||
</scroll-view>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef APP-NVUE -->
|
||||
</list>
|
||||
<!-- #endif -->
|
||||
</unicloud-db>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import statusBar from "@/uni_modules/uni-nav-bar/components/uni-nav-bar/uni-status-bar";
|
||||
import translatePublishTime from "@/uni_modules/uni-cms-article/common/publish-time";
|
||||
import refreshBox from "@/uni_modules/uni-cms-article/components/refresh-box/refreshBox.nvue";
|
||||
|
||||
import notCover from "@/uni_modules/uni-cms-article/components/list-template/not-cover.vue";
|
||||
import rightSmallCover from "@/uni_modules/uni-cms-article/components/list-template/right-small-cover.vue";
|
||||
import threeCover from "@/uni_modules/uni-cms-article/components/list-template/three-cover.vue";
|
||||
import {parseImageUrl} from "@/uni_modules/uni-cms-article/common/parse-image-url";
|
||||
|
||||
const db = uniCloud.database();
|
||||
const articleDBName = 'uni-cms-articles'
|
||||
const userDBName = 'uni-id-users'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
statusBar,
|
||||
refreshBox,
|
||||
notCover,
|
||||
rightSmallCover,
|
||||
threeCover
|
||||
},
|
||||
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', // 查询条件
|
||||
showRefresh: false, // 是否显示刷新按钮
|
||||
listHeight: 0, // 列表高度
|
||||
mpButtonLeftPlaceholderSize: 0, // 小程序左侧icon占位大小
|
||||
mpButtonPlaceholderSize: 87, // 小程序导航栏按钮占位大小
|
||||
navBarHeight: 44, // 导航栏高度
|
||||
refreshStatus: 0, // 刷新状态 0: 未刷新 1: 刷新中 2: 刷新完成
|
||||
listData: [], // 列表数据
|
||||
loadType: null
|
||||
}
|
||||
},
|
||||
async onReady() {
|
||||
// #ifdef MP
|
||||
this.initNavBarSize() // 初始化导航栏大小
|
||||
// #endif
|
||||
/* 可用窗口高度 - 搜索框高 - 状态栏高 */
|
||||
this.listHeight = uni.getSystemInfoSync().windowHeight - uni.getSystemInfoSync().statusBarHeight - this.navBarHeight + 'px'; // 计算列表高度
|
||||
},
|
||||
methods: {
|
||||
async listLoad(data) {
|
||||
const listData = data.map(item => {
|
||||
if (typeof item.thumbnail === 'string') {
|
||||
item.thumbnail = [item.thumbnail]
|
||||
}
|
||||
|
||||
return item
|
||||
})
|
||||
|
||||
// 处理腾讯云文件链接
|
||||
for (const article of listData) {
|
||||
const parseImages = await parseImageUrl(article.thumbnail)
|
||||
|
||||
article.thumbnail = parseImages ? parseImages.map(image => image.src): []
|
||||
}
|
||||
|
||||
this.listData = this.loadType === 'loadMore' ? this.listData.concat(listData) : listData
|
||||
this.loadType = null
|
||||
},
|
||||
// 初始化导航栏大小
|
||||
initNavBarSize() {
|
||||
// 获取小程序导航栏按钮信息
|
||||
// #ifdef MP-TOUTIAO
|
||||
let menuButtonInfo = tt.getCustomButtonBoundingClientRect()
|
||||
menuButtonInfo.width = menuButtonInfo.capsule.width // 小程序按钮区域中使用的按钮宽度
|
||||
this.mpButtonLeftPlaceholderSize = menuButtonInfo.leftIcon.width + 10
|
||||
// #endif
|
||||
// #ifndef MP-TOUTIAO
|
||||
let menuButtonInfo = uni.getMenuButtonBoundingClientRect()
|
||||
// #endif
|
||||
// 计算小程序导航栏按钮占位大小
|
||||
this.mpButtonPlaceholderSize = menuButtonInfo.width + 10
|
||||
// 获取系统信息,判断是否为 iOS 系统,设置导航栏高度
|
||||
this.navBarHeight = uni.getSystemInfoSync().system.toLowerCase().includes('ios') ? 44 : 48
|
||||
},
|
||||
// 格式化时间戳
|
||||
publishTime(timestamp) {
|
||||
return translatePublishTime(timestamp)
|
||||
},
|
||||
// 点击搜索框
|
||||
searchClick(e) {
|
||||
uni.hideKeyboard();
|
||||
uni.navigateTo({
|
||||
url: '/uni_modules/uni-cms-article/pages/search/search'
|
||||
});
|
||||
},
|
||||
// 重试
|
||||
retry() {
|
||||
this.refresh()
|
||||
},
|
||||
// 刷新
|
||||
refresh() {
|
||||
this.loadType = 'refresh'
|
||||
this.$refs.udb.loadData({
|
||||
clear: true
|
||||
}, () => {
|
||||
uni.stopPullDownRefresh()
|
||||
// #ifdef APP-NVUE
|
||||
this.showRefresh = false
|
||||
// #endif
|
||||
})
|
||||
},
|
||||
// 加载更多
|
||||
loadMore() {
|
||||
this.loadType = 'loadMore'
|
||||
this.$refs.udb.loadMore()
|
||||
},
|
||||
// 查询出错
|
||||
onqueryerror(e) {
|
||||
console.error(e);
|
||||
}
|
||||
},
|
||||
// #ifdef H5
|
||||
// 下拉刷新
|
||||
onPullDownRefresh() {
|
||||
this.refresh()
|
||||
},
|
||||
// #endif
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* #ifndef APP-NVUE */
|
||||
.pages view {
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
flex-direction: column;
|
||||
}
|
||||
/* #endif */
|
||||
|
||||
.pages {
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
|
||||
.refresh {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.nav-box {
|
||||
background-color: #FFFFFF;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
|
||||
/* #ifndef APP-PLUS */
|
||||
z-index: 9;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.pages .nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.uni-search-box {
|
||||
flex: 1;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.uni-search-box ::v-deep .uni-searchbar {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.uni-search-box ::v-deep .uni-searchbar__box {
|
||||
height: 32px;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.cover-search-bar {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
top: 0;
|
||||
/* #ifndef APP-NVUE */
|
||||
z-index: 999;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.pages .uni-list ::v-deep .uni-list-item__container {
|
||||
flex-direction: row;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.pages .uni-list ::v-deep .uni-list-item {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.pages .uni-list ::v-deep .uni-load-more {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.pages .uni-list ::v-deep .uni-list--border:after {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
</style>
|
33
uni_modules/uni-cms-article/pages/list/list.uvue
Normal file
33
uni_modules/uni-cms-article/pages/list/list.uvue
Normal file
@ -0,0 +1,33 @@
|
||||
<template>
|
||||
<view class="page">
|
||||
<uni-cms-article-search-bar :show-placeholder="true"></uni-cms-article-search-bar>
|
||||
<uni-cms-article-list
|
||||
:collectionList="colList"
|
||||
:refresherEnabled="true"
|
||||
style="flex: 1;"
|
||||
></uni-cms-article-list>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="uts">
|
||||
const db = uniCloud.databaseForJQL()
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
colList(): any[] {
|
||||
return [
|
||||
db.collection('uni-cms-articles').where("\"article_status\" == 1").field('thumbnail,title,publish_date,user_id').getTemp(), // 文章集合
|
||||
db.collection('uni-id-users').field('_id,nickname').getTemp() // 用户集合
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
752
uni_modules/uni-cms-article/pages/search/search.nvue
Normal file
752
uni_modules/uni-cms-article/pages/search/search.nvue
Normal file
@ -0,0 +1,752 @@
|
||||
<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="none" @clear="cancel" @confirm="confirm"
|
||||
@cancel="cancel" />
|
||||
<uni-icons class="scan-icons" :color="iconColor" size="22" type="scan" @click="scanEvent"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
<view class="search-body">
|
||||
<unicloud-db ref='listUdb' v-slot:default="{ pagination, hasMore, loading, error, options }"
|
||||
@error="onqueryerror" :collection="colList" :page-size="10" orderby="publish_date desc" @load="onDbLoad"
|
||||
loadtime="manual">
|
||||
<template v-if="!isLoadData">
|
||||
<!-- 搜索历史 -->
|
||||
<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>
|
||||
</template>
|
||||
<uni-list v-else class="uni-list" :border="false" :style="{ height: listHeight }">
|
||||
<!-- 列表渲染 -->
|
||||
<uni-list-item :to="'/uni_modules/uni-cms-article/pages/detail/detail?id=' + item._id"
|
||||
v-for="(item, index) in searchList" :key="index">
|
||||
<!-- 通过header插槽定义列表左侧图片 -->
|
||||
<template v-slot:header>
|
||||
<image class="thumbnail" :src="item.thumbnail" 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>
|
||||
<text class="publish_date">{{ publishTime(item.publish_date) }}</text>
|
||||
<!-- -->
|
||||
<!-- <uni-dateformat class="publish_date" :date="item.publish_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: searchList, pagination, hasMore, loading, error }"
|
||||
@loadMore="loadMore">
|
||||
</uni-load-state>
|
||||
<!-- #ifdef APP-PLUS -->
|
||||
</template>
|
||||
</uni-list-item>
|
||||
<!-- #endif -->
|
||||
</uni-list>
|
||||
</unicloud-db>
|
||||
</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.title"
|
||||
@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云端一体搜索模板,自带下拉候选、历史搜索、热搜。无需再开发服务器代码
|
||||
*/
|
||||
|
||||
import translatePublishTime from "@/uni_modules/uni-cms-article/common/publish-time";
|
||||
import parseScanResult from "@/uni_modules/uni-cms-article/common/parse-scan-result";
|
||||
import {parseImageUrl} from "@/uni_modules/uni-cms-article/common/parse-image-url";
|
||||
|
||||
const searchLogDbName = 'opendb-search-log'; // 搜索记录数据库
|
||||
const articleDbName = 'uni-cms-articles'; // 文章数据库
|
||||
const associativeSearchField = 'title'; // 联想时,搜索框值检索数据库字段名
|
||||
const associativeField = '_id,title'; // 联想列表每一项携带的字段
|
||||
const localSearchListKey = '__local_search_history'; // 本地历史存储字段名
|
||||
|
||||
const db = uniCloud.database();
|
||||
const articleDBName = 'uni-cms-articles'
|
||||
const userDBName = 'uni-id-users'
|
||||
|
||||
// 数组去重
|
||||
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 {
|
||||
// 文章数据库名称
|
||||
articleDbName,
|
||||
// 搜索记录数据库名称
|
||||
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 百度
|
||||
// 是否正在加载数据
|
||||
isLoadData: false,
|
||||
// 数据库查询条件
|
||||
where: '"article_status" == 1',
|
||||
// 列表高度
|
||||
listHeight: 0,
|
||||
// 是否显示联想列表
|
||||
associativeShow: false,
|
||||
// 是否显示无联想列表
|
||||
noAssociativeShow: false,
|
||||
// 搜索结果列表
|
||||
searchList: []
|
||||
}
|
||||
},
|
||||
// 组件创建时执行
|
||||
created() {
|
||||
// 初始化数据库
|
||||
this.db = uniCloud.database();
|
||||
this.searchLogDb = this.db.collection(this.searchLogDbName);
|
||||
this.articleDbName = this.db.collection(this.articleDbName);
|
||||
// #ifndef H5
|
||||
// 监听键盘高度变化
|
||||
uni.onKeyboardHeightChange((res) => {
|
||||
this.keyBoardPopup = res.height !== 0;
|
||||
})
|
||||
// #endif
|
||||
},
|
||||
// 计算属性
|
||||
computed: {
|
||||
colList() {
|
||||
// 返回文章和用户列表
|
||||
return [
|
||||
db.collection(articleDBName).where(this.where).field('thumbnail,title,publish_date,user_id').getTemp(),
|
||||
db.collection(userDBName).field('_id,nickname').getTemp()
|
||||
]
|
||||
}
|
||||
},
|
||||
// 页面初次渲染完成时执行
|
||||
onReady() {
|
||||
// #ifdef APP-NVUE
|
||||
/* 可用窗口高度 - 搜索框高 - 状态栏高 */
|
||||
this.listHeight = uni.getSystemInfoSync().windowHeight + 'px';
|
||||
// #endif
|
||||
// #ifndef APP-NVUE
|
||||
this.listHeight = 'auto'
|
||||
// #endif
|
||||
},
|
||||
// 页面加载时执行
|
||||
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.isLoadData = false
|
||||
this.associativeShow = false
|
||||
// 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.noAssociativeShow = true;
|
||||
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()
|
||||
})
|
||||
})
|
||||
},
|
||||
// 获取设备ID
|
||||
getDeviceId() {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 从本地缓存中获取uni_id
|
||||
const uniId = uni.getStorageSync('uni_id');
|
||||
// 如果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
|
||||
// 如果不是APP-PLUS,则返回一个随机字符串
|
||||
resolve(uni.getSystemInfoSync().system + '_' + Math.random().toString(36).substr(2))
|
||||
// #endif
|
||||
} else {
|
||||
// 如果uni_id存在,则直接返回uni_id
|
||||
resolve(uniId)
|
||||
}
|
||||
})
|
||||
},
|
||||
// 点击联想词
|
||||
associativeClick(item) {
|
||||
/**
|
||||
* 注意:这里用户根据自己的业务需要,选择跳转的页面即可
|
||||
*/
|
||||
console.log("associativeClick: ", item, item.title);
|
||||
// 隐藏联想词
|
||||
this.noAssociativeShow = true;
|
||||
// 将搜索框的文本设置为联想词的标题
|
||||
this.searchText = item.title;
|
||||
// 加载列表
|
||||
this.loadList(item.title);
|
||||
},
|
||||
// 加载列表
|
||||
loadList(text = '') {
|
||||
// 设置查询条件
|
||||
let where = '"article_status" == 1 '
|
||||
if (text) {
|
||||
this.where = where + `&& /${text}/.test(title)`;
|
||||
} else {
|
||||
this.where = where;
|
||||
}
|
||||
|
||||
// 隐藏联想词
|
||||
this.associativeList = [];
|
||||
this.associativeShow = false;
|
||||
|
||||
this.searchList = []
|
||||
this.isLoadData = true
|
||||
|
||||
// 延迟0ms后加载数据
|
||||
setTimeout(() => {
|
||||
this.$refs.listUdb.loadData({
|
||||
clear: true
|
||||
})
|
||||
}, 0)
|
||||
},
|
||||
// 数据库加载完成
|
||||
async onDbLoad(data) {
|
||||
console.log('onDbLoad')
|
||||
// 设置数据已加载标志
|
||||
this.isLoadData = true
|
||||
// 显示联想词
|
||||
this.noAssociativeShow = false;
|
||||
|
||||
for (const article of data) {
|
||||
const parseImages = await parseImageUrl(article.thumbnail)
|
||||
|
||||
article.thumbnail = parseImages ? parseImages.map(image => image.src): []
|
||||
}
|
||||
|
||||
this.searchList = data
|
||||
},
|
||||
// 查询错误
|
||||
onqueryerror(e) {
|
||||
console.error(e);
|
||||
},
|
||||
// 刷新
|
||||
refresh() {
|
||||
// 刷新数据
|
||||
this.$refs.listUdb.loadData({
|
||||
clear: true
|
||||
}, () => {
|
||||
// 停止下拉刷新
|
||||
uni.stopPullDownRefresh()
|
||||
// #ifdef APP-NVUE
|
||||
// 隐藏刷新按钮
|
||||
this.showRefresh = false
|
||||
// #endif
|
||||
})
|
||||
},
|
||||
// 加载更多
|
||||
loadMore() {
|
||||
// 加载更多数据
|
||||
this.$refs.listUdb.loadMore()
|
||||
},
|
||||
// 格式化发布时间
|
||||
publishTime(timestamp) {
|
||||
return translatePublishTime(timestamp)
|
||||
},
|
||||
scanEvent () {
|
||||
uni.scanCode({
|
||||
onlyFromCamera: true,
|
||||
scanType: ["qrCode"],
|
||||
success: (e) => parseScanResult(e.result),
|
||||
fail: (e) => {
|
||||
console.error(e)
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
onReachBottom() {
|
||||
// 当滚动到底部时,加载更多数据
|
||||
this.loadMore()
|
||||
},
|
||||
watch: {
|
||||
searchText: debounce(function (value, oldValue) {
|
||||
// 当搜索框的文本发生变化时,执行以下操作
|
||||
if (value === oldValue) return
|
||||
if (this.noAssociativeShow) return
|
||||
|
||||
if (value) {
|
||||
// 根据搜索框的文本,查询联想词
|
||||
this.articleDbName.where({
|
||||
[associativeSearchField]: new RegExp(value, 'gi'),
|
||||
}).field(associativeField).get().then(res => {
|
||||
// 将查询结果赋值给联想词列表,并显示联想词
|
||||
this.associativeList = res.result.data;
|
||||
this.associativeShow = true
|
||||
})
|
||||
} else {
|
||||
// 如果搜索框的文本为空,则清空联想词列表
|
||||
this.associativeList = [];
|
||||
}
|
||||
|
||||
}, 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, .scan-icons {
|
||||
padding: 16rpx;
|
||||
}
|
||||
.scan-icons {
|
||||
padding-left: 0;
|
||||
}
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.thumbnail {
|
||||
width: 240rpx;
|
||||
height: 160rpx;
|
||||
margin-right: 20rpx;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.main {
|
||||
justify-content: space-between;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.info {
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.author,
|
||||
.publish_date {
|
||||
font-size: 28rpx;
|
||||
color: #999999;
|
||||
}
|
||||
</style>
|
363
uni_modules/uni-cms-article/pages/search/search.uvue
Normal file
363
uni_modules/uni-cms-article/pages/search/search.uvue
Normal file
@ -0,0 +1,363 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<view class="search">
|
||||
<uni-cms-article-search-bar
|
||||
ref="searchBar"
|
||||
v-model="searchVal"
|
||||
:show-placeholder="false"
|
||||
:focus="true"
|
||||
@clear="showSearchResultPanel = false"
|
||||
@confirm="search"
|
||||
></uni-cms-article-search-bar>
|
||||
</view>
|
||||
<view class="search-result" v-if="showSearchResultPanel">
|
||||
<uni-cms-article-list
|
||||
ref="articleList"
|
||||
:collectionList="colList"
|
||||
:refresherEnabled="false"
|
||||
loadTime="manual"
|
||||
style="flex: 1;"
|
||||
></uni-cms-article-list>
|
||||
</view>
|
||||
<template v-else>
|
||||
<view class="panel history-panel" v-if="searchHistory.length > 0">
|
||||
<view class="panel__title">
|
||||
<view class="panel__title-text">
|
||||
<text class="text">搜索历史</text>
|
||||
</view>
|
||||
|
||||
<view class="delete-history-btns" v-if="deleteHistoryLoading">
|
||||
<text class="text" @click="deleteAllSearchHistory">全部删除</text>
|
||||
<text class="text danger" @click="deleteHistoryLoading = false">完成</text>
|
||||
</view>
|
||||
<uni-cms-article-icons
|
||||
class="panel__after-icon"
|
||||
type="trash"
|
||||
:size="18"
|
||||
color="#999"
|
||||
@click="deleteHistoryLoading = true"
|
||||
v-else
|
||||
></uni-cms-article-icons>
|
||||
</view>
|
||||
<view class="panel__list">
|
||||
<view class="panel__list-item" v-for="text in searchHistory">
|
||||
<text class="text" @click="search(text)">{{text}}</text>
|
||||
<uni-cms-article-icons
|
||||
class="icon"
|
||||
type="closeempty"
|
||||
:size="12"
|
||||
color="#999"
|
||||
v-if="deleteHistoryLoading"
|
||||
@click="deleteSearchHistory(text)"
|
||||
></uni-cms-article-icons>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<unicloud-db ref="udb" #default="{ data, loading, error }" field="content"
|
||||
collection="opendb-search-hot" orderby="create_date desc,count desc" page-data="replace"
|
||||
:page-size="10">
|
||||
<view class="panel recommend-panel">
|
||||
<view class="panel__title">
|
||||
<view class="panel__title-text">
|
||||
<text class="text">搜索发现</text>
|
||||
<uni-cms-article-icons
|
||||
class="icon"
|
||||
type="reload"
|
||||
:size="14"
|
||||
color="#999"
|
||||
v-if="!hideSearchRecommend"
|
||||
@click="reLoadSearchRecommend"
|
||||
></uni-cms-article-icons>
|
||||
</view>
|
||||
<uni-cms-article-icons
|
||||
class="panel__after-icon"
|
||||
:type="hideSearchRecommend ? 'eye-slash': 'eye'"
|
||||
:size="18"
|
||||
color="#999"
|
||||
@click="hideSearchRecommend = !hideSearchRecommend"
|
||||
></uni-cms-article-icons>
|
||||
</view>
|
||||
<view class="panel__list">
|
||||
<view class="panel__list-tip" v-if="loading">
|
||||
<text class="text">正在加载...</text>
|
||||
</view>
|
||||
<view class="panel__list-tip" v-else-if="error != null">
|
||||
<text class="text">{{error.message}}</text>
|
||||
</view>
|
||||
<view class="panel__list-tip" v-else-if="hideSearchRecommend">
|
||||
<text class="text">当前搜索发现已隐藏</text>
|
||||
</view>
|
||||
<template v-else>
|
||||
<view
|
||||
class="panel__list-item"
|
||||
v-for="(word, index) in data"
|
||||
:key="index"
|
||||
@click="search(word.getString('content')!)"
|
||||
>
|
||||
<text class="text">{{ word.getString('content') }}</text>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
</unicloud-db>
|
||||
</template>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="uts">
|
||||
type ArticleAuthor = {
|
||||
_id: string
|
||||
nickname: string
|
||||
}
|
||||
type ArticleItem = {
|
||||
_id: string
|
||||
title: string
|
||||
publish_date: number
|
||||
thumbnail: string[]
|
||||
user_id: ArticleAuthor[]
|
||||
}
|
||||
|
||||
const db = uniCloud.databaseForJQL()
|
||||
const searchLogDB = db.collection('opendb-search-log')
|
||||
const cmsArticleDB = db.collection('uni-cms-articles')
|
||||
const uniIdUsersDB = db.collection('uni-id-users')
|
||||
const localSearchHistoryKey = '__local_search_history'; // 本地历史存储字段名
|
||||
const localSearchRecommendHiddenKey = '__local_search_recommend_hidden'; // 本地搜索发现开关字段名
|
||||
const localSearchHistoryMax = 10; // 本地历史存储最大值
|
||||
export default {
|
||||
data() {
|
||||
const localSearchRecommendHidden = uni.getStorageSync(localSearchRecommendHiddenKey)
|
||||
|
||||
return {
|
||||
searchVal: "",
|
||||
searchHistory: [] as string[],
|
||||
searchRecommend: [] as string[],
|
||||
deleteHistoryLoading: false,
|
||||
hideSearchRecommend: (localSearchRecommendHidden == "" ? false : localSearchRecommendHidden) as boolean,
|
||||
showSearchResultPanel: false,
|
||||
searchResult: [] as ArticleItem[]
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
hideSearchRecommend(newValue) {
|
||||
uni.setStorageSync(localSearchRecommendHiddenKey, newValue)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
hasSearchValue(): boolean {
|
||||
return this.searchVal != ""
|
||||
},
|
||||
where(): string {
|
||||
let where = "\"article_status\" == 1"
|
||||
|
||||
if (this.searchVal != "") {
|
||||
where += `&& /${this.searchVal}/.test(title)`
|
||||
}
|
||||
|
||||
return where
|
||||
},
|
||||
colList(): any[] {
|
||||
// 返回文章和用户列表
|
||||
return [
|
||||
cmsArticleDB.where(this.where).field('thumbnail,title,publish_date,user_id').getTemp(),
|
||||
uniIdUsersDB.field('_id,nickname').getTemp()
|
||||
]
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 本地历史存储
|
||||
const localSearchHistory = uni.getStorageSync(localSearchHistoryKey)
|
||||
this.searchHistory = (localSearchHistory == "" ? [] as string[] : localSearchHistory) as string[]
|
||||
},
|
||||
methods: {
|
||||
deleteAllSearchHistory() {
|
||||
uni.showModal({
|
||||
title: "确定清空搜索历史吗",
|
||||
confirmText: "删除",
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
this.deleteSearchHistory(null)
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
deleteSearchHistory(searchText: string | null) {
|
||||
let history: string[] = []
|
||||
if (searchText != null) {
|
||||
history = this.searchHistory.filter((item: string): boolean => item != searchText)
|
||||
}
|
||||
|
||||
this.searchHistory = history
|
||||
uni.setStorageSync(localSearchHistoryKey, history)
|
||||
|
||||
console.log(history.length, 'history.length')
|
||||
if (history.length <= 0) {
|
||||
this.deleteHistoryLoading = false
|
||||
}
|
||||
},
|
||||
search(searchText: string) {
|
||||
searchText = searchText.trim()
|
||||
|
||||
if (searchText == "" || this.deleteHistoryLoading) return
|
||||
// 隐藏键盘
|
||||
;(this.$refs['searchBar'] as UniCmsArticleSearchBarComponentPublicInstance)!.hideKeyboard()
|
||||
|
||||
// 保存搜索历史
|
||||
this.setLocalSearchHistory(searchText)
|
||||
// 显示搜索结果Panel
|
||||
this.showSearchResultPanel = true
|
||||
// 搜索
|
||||
this.loadSearchResult(searchText)
|
||||
// 添加搜索记录
|
||||
this.addSearchRecord(searchText)
|
||||
},
|
||||
loadSearchResult(searchText: string) {
|
||||
// 设置查询条件
|
||||
this.searchVal = searchText
|
||||
|
||||
// 延迟0ms后加载数据
|
||||
setTimeout(() => {
|
||||
(this.$refs['articleList'] as UniCmsArticleListComponentPublicInstance)!.reLoadList()
|
||||
}, 0)
|
||||
},
|
||||
addSearchRecord(searchText: string) {
|
||||
const systemInfo = uni.getSystemInfoSync()
|
||||
/*
|
||||
在此处存搜索记录,如果登录则需要存 user_id,若未登录则存device_id
|
||||
*/
|
||||
searchLogDB.add({
|
||||
// user_id: device_id,
|
||||
device_id: systemInfo.deviceId,
|
||||
// device_uuid: systemInfo.deviceId,
|
||||
content: searchText,
|
||||
create_date: Date.now()
|
||||
})
|
||||
},
|
||||
setLocalSearchHistory(searchText: string) {
|
||||
const history = this.searchHistory.filter((item: string): boolean => item != searchText)
|
||||
|
||||
history.unshift(searchText)
|
||||
|
||||
if (history.length > localSearchHistoryMax) {
|
||||
history.pop()
|
||||
}
|
||||
|
||||
this.searchHistory = history
|
||||
this.deleteHistoryLoading = false
|
||||
uni.setStorageSync(localSearchHistoryKey, history)
|
||||
},
|
||||
reLoadSearchRecommend() {
|
||||
(this.$refs['udb'] as UniCloudDBElement)!.loadData({
|
||||
clear: true
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
|
||||
.search-result {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
.panel {
|
||||
margin-top: 40rpx;
|
||||
padding: 10rpx 20rpx;
|
||||
|
||||
&__title {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
&-text {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
.text {
|
||||
color: #3e3e3e;
|
||||
font-size: 30rpx;
|
||||
line-height: 36rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.icon {
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__after-icon {
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
|
||||
&__list {
|
||||
margin-top: 30rpx;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
|
||||
&-item {
|
||||
background-color: #f6f6f6;
|
||||
border-radius: 30rpx;
|
||||
padding: 10rpx 20rpx;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 10rpx;
|
||||
margin-bottom: 10rpx;
|
||||
|
||||
.text {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.icon {
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
}
|
||||
|
||||
&-tip {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
flex: 1;
|
||||
margin-top: 20rpx;
|
||||
|
||||
.text {
|
||||
font-size: 26rpx;
|
||||
color: #808080;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.delete-history-btns {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.text {
|
||||
color: #666;
|
||||
font-size: 22rpx;
|
||||
margin-left: 20rpx;
|
||||
|
||||
&.danger {
|
||||
color: #c0402b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
48
uni_modules/uni-cms-article/pages/webview/webview.uvue
Normal file
48
uni_modules/uni-cms-article/pages/webview/webview.uvue
Normal file
@ -0,0 +1,48 @@
|
||||
<!-- 网络链接内容展示页(用于uni-cms-article展示外链内容) -->
|
||||
<template>
|
||||
<view class="web-view">
|
||||
<web-view
|
||||
class="web-view"
|
||||
v-if="url"
|
||||
:src="url"
|
||||
></web-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="uts">
|
||||
export default {
|
||||
onLoad(e) {
|
||||
const url = e.get('url') as string
|
||||
let title: string | null = e.get('title')
|
||||
|
||||
if (url.substring(0, 4) != 'http') {
|
||||
uni.showModal({
|
||||
title: "错误",
|
||||
content: '不是一个有效的网站链接,' + '"' + decodeURIComponent(url) + '"',
|
||||
showCancel: false,
|
||||
confirmText: "知道了",
|
||||
complete: () => {
|
||||
uni.navigateBack()
|
||||
}
|
||||
})
|
||||
title = "页面路径错误"
|
||||
} else {
|
||||
this.url = decodeURIComponent(url)
|
||||
}
|
||||
if (title != null) {
|
||||
uni.setNavigationBarTitle({title})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
url: null as string | null,
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.web-view {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
35
uni_modules/uni-cms-article/pages/webview/webview.vue
Normal file
35
uni_modules/uni-cms-article/pages/webview/webview.vue
Normal file
@ -0,0 +1,35 @@
|
||||
<!-- 网络链接内容展示页(用于uni-cms-article展示外链内容) -->
|
||||
<template>
|
||||
<view>
|
||||
<web-view v-if="url" :src="url"></web-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
onLoad({url,title}) {
|
||||
if(url.substring(0, 4) !== 'http'){
|
||||
uni.showModal({
|
||||
title:"错误",
|
||||
content: '不是一个有效的网站链接,'+'"'+url+'"',
|
||||
showCancel: false,
|
||||
confirmText:"知道了",
|
||||
complete: () => {
|
||||
uni.navigateBack()
|
||||
}
|
||||
});
|
||||
title = "页面路径错误"
|
||||
}else{
|
||||
this.url = url;
|
||||
}
|
||||
if(title){
|
||||
uni.setNavigationBarTitle({title});
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
url:null
|
||||
};
|
||||
}
|
||||
}
|
||||
</script>
|
Reference in New Issue
Block a user