Commit 986fcc95 by Jason Zhou

too many updates

parent 2c4e7c9f
node_modules
\ No newline at end of file
{ {
"pages": [ "pages": [
"pages/index/index",
"pages/blog/blog", "pages/blog/blog",
"pages/index/index",
"pages/more/storePhoto", "pages/more/storePhoto",
"pages/comment/storeComments", "pages/comment/storeComments",
"pages/comment/writeComments", "pages/comment/writeComments",
......
...@@ -114,11 +114,11 @@ form#formIdGetter button.form_button { ...@@ -114,11 +114,11 @@ form#formIdGetter button.form_button {
border: 2rpx solid #f14b7d; border: 2rpx solid #f14b7d;
box-sizing: border-box; box-sizing: border-box;
} }
text, /* text,
view { view {
font-size: 32rpx; font-size: 32rpx;
color: #333; color: #333;
} } */
scroll-view { scroll-view {
overflow: scroll; overflow: scroll;
} }
......
Page({ Page({
onLoad() { onPostTap(e) {
console.log(1) const { postId } = e.detail
wx.navigateTo({ url: `plugin://blogPlugin/postDetail?postId=${postId}` })
},
onPostCategory(e) {
const { name, id } = e.detail
wx.navigateTo({
url: `plugin://blogPlugin/postList?category=${name}&categoryId=${id}`
})
} }
}) })
\ No newline at end of file
<blog /> <blog bindPostTap="onPostTap" />
\ No newline at end of file \ No newline at end of file
import urls from '/constants/blog/urls'
import Fly from '/lib/flyio/index'
const fly = new Fly()
const { REQUESTS } = urls
export const getPostDetail = postId => fly.get(REQUESTS.GET_POST(postId))
export const getPosts = (siteId, category, pageNum) =>
fly.get(REQUESTS.GET_POSTS(siteId, category, pageNum))
export const likePost = (status, postId, nickName, avatarUrl) =>
status ? REQUESTS.SET_LIKE_POST(postId) : REQUESTS.DELETE_LIKE_POST(postId)
export const getCategories = siteId => fly.get(REQUESTS.GET_CATEGORIES(siteId))
export const getComments = (postId, pageNum) => fly.get(REQUESTS.GET_COMMENTS(postId, pageNum))
export const setComments = (postId, comment) => fly.post(REQUESTS.SET_COMMENTS(postId), comment)
\ No newline at end of file
const BASE_URL = 'https://www.sxl.cn'
export default { export default {
REQUESTS: { REQUESTS: {
// post.js // post.js
GET_POST: (code, productId) => GET_POST: productId =>
`/r/v1/blog_posts/${productId}?code=${code}&v=${new Date().getTime()}`, `${BASE_URL}/r/v1/blog_posts/${productId}?v=${new Date().getTime()}`,
GET_POSTS: (siteId, code, category, pageNum = 1) => category === 'all' GET_POSTS: (siteId, category, pageNum = 1) =>
? `/r/v1/sites/${siteId}/mini_program/blog?limit=8&page=${pageNum}&code=${code}&expand=blogPosts&exclude_content=true&v=${new Date().getTime()}` category === 'all'
: `/r/v1/sites/${siteId}/mini_program/blog?expand=blogPosts&limit=8&page=${pageNum}&tag=${encodeURI(`${category}`)}&code=${code}&exclude_content=true&v=${new Date().getTime()}`, ? `${BASE_URL}/r/v1/sites/${siteId}/mini_program/blog?limit=8&page=${pageNum}&expand=blogPosts&exclude_content=true&v=${new Date().getTime()}`
: `${BASE_URL}/r/v1/sites/${siteId}/mini_program/blog?expand=blogPosts&limit=8&page=${pageNum}&tag=${encodeURI(
`${category}`
)}&exclude_content=true&v=${new Date().getTime()}`,
GET_POSTS_SEARCH: siteId => GET_POSTS_SEARCH: siteId =>
`/r/v1/mini_program/apps/${siteId}/search_blog_posts?v=${new Date().getTime()}`, `${BASE_URL}/r/v1/mini_program/apps/${siteId}/search_blog_posts?v=${new Date().getTime()}`,
// category // category
GET_CATEGORIES: pageId => GET_CATEGORIES: pageId =>
`/r/v1/sites/${pageId}/blog/tags?v=${new Date().getTime()}`, `${BASE_URL}/r/v1/sites/${pageId}/blog/tags?v=${new Date().getTime()}`,
// comment // comment
GET_COMMENTS: (postId, pageNum) => GET_COMMENTS: (postId, pageNum) =>
`/r/v1/mini_program/blog/blog_posts/${postId}/user_comments?per=9999&page=${pageNum}`, `${BASE_URL}/r/v1/mini_program/blog/blog_posts/${postId}/user_comments?per=9999&page=${pageNum}`,
SET_COMMENTS: postId => SET_COMMENTS: postId =>
`/r/v1/mini_program/blog/blog_posts/${postId}/user_comments`, `${BASE_URL}/r/v1/mini_program/blog/blog_posts/${postId}/user_comments`,
// sharing // sharing
GET_SHARING_BASICINFO: siteId => `/r/v1/sites/${siteId}/mp/basic_info`, GET_SHARING_BASICINFO: siteId =>
SET_SHARING_SCENECODE: siteId => `/r/v1/sites/${siteId}/mp/scene_codes`, `${BASE_URL}/r/v1/sites/${siteId}/mp/basic_info`,
SET_SHARING_SCENECODE: siteId =>
`${BASE_URL}/r/v1/sites/${siteId}/mp/scene_codes`,
// setting // setting
GET_SETTINGS: pageId => GET_SETTINGS: pageId =>
`/r/v1/sites/${pageId}/blog_settings?v=${new Date().getTime()}`, `${BASE_URL}/r/v1/sites/${pageId}/blog_settings?v=${new Date().getTime()}`,
SET_LIKE_POST: postId => `/r/v1/mini_program/blog/blog_posts/${postId}/likes`, SET_LIKE_POST: postId =>
DELETE_LIKE_POST: postId => `/r/v1/mini_program/blog/likes/${postId}` `${BASE_URL}/r/v1/mini_program/blog/blog_posts/${postId}/likes`,
DELETE_LIKE_POST: postId =>
`${BASE_URL}/r/v1/mini_program/blog/likes/${postId}`
}, },
METHODS: { METHODS: {
// post // post
...@@ -39,21 +47,21 @@ export default { ...@@ -39,21 +47,21 @@ export default {
GET_SETTINGS: () => 'GET', GET_SETTINGS: () => 'GET',
// sharing // sharing
GET_SHARING_BASICINFO: () => 'GET', GET_SHARING_BASICINFO: () => 'GET',
SET_SHARING_SCENECODE: () => 'POST', SET_SHARING_SCENECODE: () => 'POST'
}, },
PAGES: { PAGES: {
POST_INDEX: '/pages/blog/postIndex/postIndex', POST_INDEX: '/pages/postIndex/postIndex',
POST_DETAIL: '/pages/blog/postDetail/postDetail', POST_DETAIL: '/pages/postDetail/postDetail',
POST_LIST: '/pages/blog/postList/postList', POST_LIST: '/pages/postList/postList',
POST_SEARCH: '/pages/blog/postSearch/postSearch', POST_SEARCH: '/pages/postSearch/postSearch',
MORE: '/pages/blog/more/more', MORE: '/pages/more/more',
ABOUT: '/pages/blog/about/about', ABOUT: '/pages/about/about',
SXL: '/pages/blog/sxl/sxl', SXL: '/pages/sxl/sxl',
POST_SHARE: '/pages/blog/postShare/postShare', POST_SHARE: '/pages/postShare/postShare',
WMP_SHARE: '/pages/blog/wmpShare/wmpShare', WMP_SHARE: '/pages/wmpShare/wmpShare'
}, },
NAVIGATION: { NAVIGATION: {
POST_INDEX: 'pages#blog#more#more', POST_INDEX: 'pages#blog#more#more',
ABOUT: 'pages#blog#about#about', ABOUT: 'pages#blog#about#about'
}, }
} }
const base_path = ''
export default {
//error
SET_ERROR_MESSAGE: base_path + 'set_error_message',
CLEAR_ERROR_MESSAGE: base_path + 'clear_error_message',
//global
SET_GLOBAL_DATA: base_path + 'miniprogram/set_global_data',
CLEAR_GLOBAL_DATA: base_path + 'miniprogram/clear_store_data',
//setting
REFRESH_DATA: 'app/blog/refresh_data',
}
export default { export default {
onLoad(options) {}, canUseSearch: true,
canUseCollection: false,
canUseShare: false,
} }
const preFix = '/assets/common'
export const iconPath = {
ICON_AUTH_WECHAT: `${preFix}/wechat-icon.png`,
ICON_AUTH_BG: `${preFix}/background.png`,
downloadImage: `${preFix}/icon-download-image.png`,
}
export default iconPath
module.exports = {
GET_COMPONENTS: (siteId, type) =>
`/r/v1/mini_program/apps/${siteId}/components?type[]=${type}`,
GET_RELATION_DATA: siteId =>
`/r/v1/mini_program/apps/${siteId}/relation_data`,
FETCH_ANNOUNCEMENTS: siteId =>
`/r/v1/sites/${siteId}/announcement/mp/announcements`,
PAGES: {
ANNOUNCEMENTS: '/pages/common/announcement/announcement',
},
UPLOAD_IMAGE: siteId =>
`/r/v1/mini_program/apps/${siteId}/asset_images_presign`,
}
This source diff could not be displayed because it is too large. You can view the blob instead.
//! moment.js locale configuration
//! locale : Chinese (China) [zh-cn]
//! author : suupic : https://github.com/suupic
//! author : Zeno Zeng : https://github.com/zenozeng
import moment from './moment-core'
var zhCn = moment.defineLocale('zh-cn', {
months: '一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月'.split('_'),
monthsShort: '1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月'.split('_'),
weekdays: '星期日_星期一_星期二_星期三_星期四_星期五_星期六'.split('_'),
weekdaysShort: '周日_周一_周二_周三_周四_周五_周六'.split('_'),
weekdaysMin: '日_一_二_三_四_五_六'.split('_'),
longDateFormat: {
LT: 'HH:mm',
LTS: 'HH:mm:ss',
L: 'YYYY/MM/DD',
LL: 'YYYY年M月D日',
LLL: 'YYYY年M月D日Ah点mm分',
LLLL: 'YYYY年M月D日ddddAh点mm分',
l: 'YYYY/M/D',
ll: 'YYYY年M月D日',
lll: 'YYYY年M月D日 HH:mm',
llll: 'YYYY年M月D日dddd HH:mm'
},
meridiemParse: /凌晨|早上|上午|中午|下午|晚上/,
meridiemHour: function (hour, meridiem) {
if (hour === 12) {
hour = 0;
}
if (meridiem === '凌晨' || meridiem === '早上' ||
meridiem === '上午') {
return hour;
} else if (meridiem === '下午' || meridiem === '晚上') {
return hour + 12;
} else {
// '中午'
return hour >= 11 ? hour : hour + 12;
}
},
meridiem: function (hour, minute, isLower) {
var hm = hour * 100 + minute;
if (hm < 600) {
return '凌晨';
} else if (hm < 900) {
return '早上';
} else if (hm < 1130) {
return '上午';
} else if (hm < 1230) {
return '中午';
} else if (hm < 1800) {
return '下午';
} else {
return '晚上';
}
},
calendar: {
sameDay: '[今天]LT',
nextDay: '[明天]LT',
nextWeek: '[下]ddddLT',
lastDay: '[昨天]LT',
lastWeek: '[上]ddddLT',
sameElse: 'L'
},
dayOfMonthOrdinalParse: /\d{1,2}(日|月|周)/,
ordinal: function (number, period) {
switch (period) {
case 'd':
case 'D':
case 'DDD':
return number + '日';
case 'M':
return number + '月';
case 'w':
case 'W':
return number + '周';
default:
return number;
}
},
relativeTime: {
future: '%s内',
past: '%s前',
s: '几秒',
ss: '%d 秒',
m: '1 分钟',
mm: '%d 分钟',
h: '1 小时',
hh: '%d 小时',
d: '1 天',
dd: '%d 天',
M: '1 个月',
MM: '%d 个月',
y: '1 年',
yy: '%d 年'
},
week: {
// GB/T 7408-1994《数据元和交换格式·信息交换·日期和时间表示法》与ISO 8601:1988等效
dow: 1, // Monday is the first day of the week.
doy: 4 // The week that contains Jan 4th is the first week of the year.
}
});
export default moment
Page({
onLoad() {
wx.switchTab({
url: '/pages/blog/postIndex/postIndex',
})
},
})
<import src="root/utils/wxParse/wxParse.wxml" /> <import src="/utils/wxParse/wxParse.wxml" />
<import src="./postItem/postItem.wxml" /> <import src="./postItem/postItem.wxml" />
<import src="./commentList/commentList.wxml" /> <import src="./commentList/commentList.wxml" />
<import src="root/templates/messageModal/messageModal.wxml" /> <import src="/templates/messageModal/messageModal.wxml" />
<import src="root/templates/shareView/shareView.wxml" /> <import src="/templates/shareView/shareView.wxml" />
<import src="root/templates/loaderPage/loaderPage.wxml" /> <import src="/templates/loaderPage/loaderPage.wxml" />
<view wx:if="{{errorMessage}}"> <view wx:if="{{errorMessage}}">
<template is="message-modal" data="{{message: errorMessage}}" /> <template is="message-modal" data="{{message: errorMessage}}" />
</view> </view>
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
</view> </view>
<view class="post-body"> <view class="post-body">
<!-- bug fix for RDT-777, prevent rerender using a cached value. For details see postDetail.js --> <!-- bug fix for RDT-777, prevent rerender using a cached value. For details see postDetail.js -->
<block wx:for="{{cachedSections}}" wx:for-item="item" wx:key="item.id"> <block wx:for="{{sections}}" wx:for-item="item" wx:key="item.id">
<template wx:if="{{item.component.type == 'Blog.Quote' || item.component.type == 'Quote'}}" is="post-item-quote" data="{{...item.component.value}}" /> <template wx:if="{{item.component.type == 'Blog.Quote' || item.component.type == 'Quote'}}" is="post-item-quote" data="{{...item.component.value}}" />
<template wx:if="{{item.component.type == 'Blog.Text' || item.component.type == 'RichText'}}" is="post-item-text" data="{{...item.component.value}}" /> <template wx:if="{{item.component.type == 'Blog.Text' || item.component.type == 'RichText'}}" is="post-item-text" data="{{...item.component.value}}" />
<template wx:if="{{item.component.type == 'Blog.Title' || item.component.type == 'Title'}}" is="post-item-title" data="{{...item.component.value}}" /> <template wx:if="{{item.component.type == 'Blog.Title' || item.component.type == 'Title'}}" is="post-item-title" data="{{...item.component.value}}" />
......
@import "/styles/main.wxss"; @import "/styles/main.wxss";
@import "/utils/wxParse/wxParse.wxss"; @import "/utils/wxParse/wxParse.wxss";
@import '/pages/blog/postDetail/commentList/commentList.wxss'; @import '/pages/postDetail/commentList/commentList.wxss';
@import '/pages/blog/postDetail/postItem/postItem.wxss'; @import '/pages/postDetail/postItem/postItem.wxss';
@import "/templates/messageModal/messageModal.wxss"; @import "/templates/messageModal/messageModal.wxss";
@import "/templates/shareView/shareView.wxss"; @import "/templates/shareView/shareView.wxss";
......
<import src="root/utils/wxParse/wxParse.wxml" /> <import src="/utils/wxParse/wxParse.wxml" />
<template name="post-item-quote"> <template name="post-item-quote">
<view class="post-item-quote post-item-wrapper"> <view class="post-item-quote post-item-wrapper">
<view class="post-item-quote-content"> <view class="post-item-quote-content">
......
import { connect } from 'root/wmp-redux'
import compose from 'ramda/src/compose'
import { fetchPosts, likePost } from 'root/actions/blog/post'
import urls from 'root/constants/blog/urls'
import iconPaths from 'root/constants/blog/iconPaths'
import { setComments } from 'root/actions/blog/comment'
import { fetchPostSharing } from 'root/actions/blog/sharing'
import { getPosts } from 'root/selectors/blog/post'
import { getAttr, getUserInfo } from 'root/selectors/common/global'
import { getComponents } from 'root/selectors/common/componentsSelector'
import {
login,
bindGetUserInfoHandler,
} from 'wechat_common/utils/wrappedWXTool'
import { getMixLayout } from 'root/utils/helpers/helper'
import shareView from 'root/templates/shareView/shareView'
import { trackUI } from 'wechat_common/tracker/index.bs'
import { getTeamMemberId } from 'root/selectors/presentation/teamMemberSelector'
const { iconComment, iconLike } = iconPaths
const page = {
data: {
currentCategory: 'all',
iconComment,
iconLike,
shareLoading: false,
selectedPostId: '',
currentComment: '',
isCommenting: false,
},
onLoad(options) {
wx.setNavigationBarTitle({
title: options.category,
})
this.setData({
isFetchingPosts: true,
currentCategory: options.category,
})
const { paginationPosts, siteId } = this.data
if (!paginationPosts) {
this.fetchPosts(siteId, options.category, 1)
}
},
handlePost(e) {
const { id } = e.currentTarget.dataset
wx.navigateTo({ url: `${urls.PAGES.POST_DETAIL}?postId=${id}` })
},
loadMorePosts() {
const { paginationPosts, siteId, currentCategory } = this.data
if (paginationPosts.nextPage) {
this.fetchPosts(siteId, currentCategory, paginationPosts.nextPage)
}
},
handleCard(e) {
const { post } = e.currentTarget.dataset
this.setData({ currentPost: post })
},
onShareAppMessage() {
const { currentPost = {}, showShareVariation, currentCategory } = this.data
const { title, id } = currentPost
if (id && showShareVariation) {
return {
title,
path: `${urls.PAGES.POST_DETAIL}?postId=${id}`,
}
} else {
return {
path: `${urls.PAGES.POST_LIST}?category=${currentCategory}`,
}
}
},
sharePicture() {
const { siteId, currentPost } = this.data
const { id: postId } = currentPost
if (wx.isWept) {
this.setError('预览模式下暂不支持,请手机预览')
} else {
this.setData({
shareLoading: true,
})
this.fetchPostSharing(siteId, {
scene: postId.toString(),
page: 'pages/blog/postDetail/postDetail',
}).then(res => {
trackUI(
'shareImageBlogPost',
postId,
JSON.stringify({
team_member_id: this.data.teamMemberId || -1,
key: `shareImageBlogPost${postId}`,
}),
)
this.setData({
shareLoading: false,
})
if (res.success) {
wx.navigateTo({
url: `${urls.PAGES.POST_SHARE}?postId=${postId}`,
})
} else {
wx.showModal({
title: '网络错误',
content: '请稍后重试',
showCancel: false,
success: res => {
if (res.confirm) {
this.closeShareView()
}
},
})
}
})
}
},
handleLike(e) {
const { postId, isLiked } = e.currentTarget.dataset
const cb = userInfo => {
const { nickName, avatarUrl } = userInfo
this.setData({ selectedPostId: '' })
if (!isLiked) {
trackUI(
'likeBlogPost',
postId,
JSON.stringify({
team_member_id: this.data.teamMemberId || -1,
key: `likeBlogPost${postId}`,
}),
)
}
this.likePost(!isLiked, postId, nickName, avatarUrl)
}
bindGetUserInfoHandler(e, cb)
},
switchOperation(e) {
const { post } = e.currentTarget.dataset
const { selectedPostId } = this.data
this.setData({
isCommenting: false,
currentComment: '',
selectedPostId:
selectedPostId && selectedPostId == post.id ? '' : post.id,
currentPost: post,
})
},
handleComment(e) {
const cb = () => {
this.setData({ isCommenting: true, selectedPostId: '' })
}
bindGetUserInfoHandler(e, cb)
},
handleInputComment(e) {
this.setData({ currentComment: e.detail.value })
},
handleSendComment(e) {
const { currentComment } = this.data
if (!currentComment || !currentComment.trim()) {
this.setError('评论内容不能为空')
} else if (wx.isWept) {
this.setError('预览模式下暂不支持,请手机预览')
} else {
this.setData({ isCommenting: false })
this.createComment(e.detail.formId)
}
},
createComment(formId) {
const { currentPost, currentComment, nickName, avatarUrl } = this.data
const that = this
this.setData({
submitting: true,
})
const sendData = {
content: currentComment,
nickname: nickName,
wechat_photo: [avatarUrl],
settings: {
form_id: formId,
},
}
login({
success: loginRes => {
sendData.code = loginRes.code
that.setComments(
currentPost.id,
sendData,
() => {
that.setData({
submitting: false,
currentComment: '',
})
wx.showToast({
title: '评论成功,审核通过后显示在留言列表',
icon: 'none',
duration: 2000,
})
trackUI(
'commentBlogPost',
currentPost.id,
JSON.stringify({
team_member_id: this.data.teamMemberId || -1,
key: `commentBlogPost${currentPost.id}`,
}),
)
},
() => {
that.setData({ submitting: false })
},
)
},
fail() {
wx.showModal({
content: '请求失败,请重试',
})
that.setData({
submitting: false,
})
},
})
},
handleGlobalTab() {
this.setData({
selectedPostId: '',
isCommenting: false,
currentComment: '',
})
},
onPullDownRefresh() {
const { siteId, currentCategory } = this.data
this.fetchPosts(siteId, currentCategory, 1).then(res =>
wx.stopPullDownRefresh(),
)
},
}
function mapStateToProps(state, options) {
const {
list: posts,
isFetching: isFetchingPosts,
pagination: paginationPosts,
} = getPosts(state, `${urls.PAGES.POST_LIST}_${options.category}`),
{ siteId, layout, mix, companyName, name, logoUrl } = getAttr(state),
enableComments = state.getIn([
'blog',
'setting',
'settings',
'enableComments',
])
const { nickName, avatarUrl } = getUserInfo(state)
let blogLayout = getMixLayout(mix, layout, 'blog')
let components = []
if (state.get('components') !== null) {
components = getComponents(state, 'blog')
const blogAndCategoryComp = components.find(
comp => comp.type === 'blogAndCategory',
)
if (blogAndCategoryComp && blogAndCategoryComp.settings) {
blogLayout = blogAndCategoryComp.settings.layout || 'a'
}
}
const teamMemberId = getTeamMemberId(state)
return {
posts,
isFetchingPosts,
paginationPosts,
enableComments,
teamMemberId,
siteId,
layout: blogLayout,
logoUrl,
companyName,
name,
nickName,
avatarUrl,
}
}
function mapDispatchToProps(dispatch) {
return {
fetchPosts: (siteId, category, pageNum) =>
dispatch(
fetchPosts(
`${urls.PAGES.POST_LIST}_${category}`,
siteId,
category,
pageNum,
),
),
fetchPostSharing: (siteId, data) =>
dispatch(fetchPostSharing(siteId, data)),
setComments: (postId, setData, successCb, failCb) =>
dispatch(setComments(postId, setData, successCb, failCb)),
likePost: (status, postId, nickName, avatarUrl) =>
dispatch(likePost(status, postId, nickName, avatarUrl)),
}
}
const enhance = compose(shareView, connect(mapStateToProps, mapDispatchToProps))
Page(enhance(page))
{
"enablePullDownRefresh": true
}
<import src="root/templates/postCard/postCard.wxml"/>
<import src="root/templates/loaderPage/loaderPage.wxml"/>
<import src="root/templates/loaderBar/loaderBar.wxml"/>
<import src="root/templates/shareView/shareView.wxml"/>
<view class="post-index">
<block wx:if="{{!isFetchingPosts || posts.length > 0}}">
<scroll-view style="height: 100vh; background-color:#fff;" scroll-y="true" lower-threshold="80" bindscrolltolower="loadMorePosts" bindtap="handleGlobalTab">
<view class="posts">
<block wx:for="{{posts}}" wx:key="id" wx:for-item="item">
<template wx:if="{{layout === 'a'}}" is="post-card-a" data="{{...item, handlePost}}"/>
<template wx:if="{{layout === 'b'}}" is="post-card-b" data="{{...item, handlePost}}"/>
<template wx:if="{{layout === 'c'}}" is="post-card-c" data="{{...item, handlePost}}"/>
<template wx:if="{{layout === 'd'}}" is="post-card-moment" data="{{item, index, companyName, logoUrl, name, handleCard, handlePost, handleLike, handleComment, openShareView, selectedPostId, enableComments}}"/>
</block>
</view>
<template
is='loader-bar'
data="{{isLoading: posts.length > 0 && paginationPosts.nextPage, emptyText: posts && posts.length === 0 && '- 暂无文章 -', noMoreText:posts && posts.length !== 0 && paginationPosts.currentPage === paginationPosts.totalPages && '- 无更多文章 -', style:'margin: 0 0 40rpx 0'}}"
/>
</scroll-view>
<form bindsubmit="handleSendComment">
<view class="comment-form" wx:if="{{isCommenting}}">
<input placeholder-class="comment-placeholder" class="comment-input" placeholder="评论" value="{{currentComment}}" bindinput="handleInputComment" cursor-spacing="8" focus="{{true}}"/>
<button class="comment-btn {{currentComment ? '' : 'disabled'}}" formType="submit">发送</button>
</view>
</form>
</block>
<template wx:else is='loader-page'/>
<template is="share-view" data="{{shareLoading, sharePicture, shareAnimation, showShareVariation, closeShareView, openShareView}}"/>
</view>
@import '/components/blog/blog/blog.wxss';
@import "/styles/main.wxss";
import { debounce } from 'wechat_common/utils/utils'
import { connect } from 'root/wmp-redux'
import { afterClearData } from 'root/actions/common/tools'
import {
fetchPostsSearch,
clearPostsSearch,
} from 'root/actions/blog/postSearch'
import urls from 'root/constants/blog/urls.js'
import iconPaths from 'root/constants/blog/iconPaths'
import { getPostsSearch } from 'root/selectors/blog/postSearch'
import { getStyle, getAttr } from 'root/selectors/common/global'
const { iconClear, iconLink, iconNoResult, iconClearWhite } = iconPaths
const page = {
data: {
keywords: '',
isFirst: true,
iconClear,
iconClearWhite,
iconLink,
iconNoResult,
},
handlePost(e) {
const { id } = e.currentTarget.dataset
wx.navigateTo({ url: `${urls.PAGES.POST_DETAIL}?postId=${id}` })
},
handleInput(event) {
const value = event.detail.value
this.setData({ isFirst: false, keywords: value })
const { siteId, keywords } = this.data
this.fetchPostsAfterClear(siteId, keywords, 1)
},
handleClear() {
this.setData({ keywords: '' })
this.clearPosts()
},
loadMorePosts() {
let {
paginationPosts: { pages, currPage },
siteId,
keywords,
isFetchingPosts,
} = this.data
if (!isFetchingPosts && currPage < pages) {
this.fetchPosts(siteId, keywords, ++currPage)
}
},
}
function mapStateToProps(state) {
const {
list: posts,
isFetching: isFetchingPosts,
pagination: paginationPosts,
} = getPostsSearch(state, urls.PAGES.POST_SEARCH),
{ mainBackground: background, isWhiteBackground } = getStyle(state),
{ siteId } = getAttr(state)
return {
posts,
isFetchingPosts,
paginationPosts,
siteId,
background,
isWhiteBackground,
}
}
function mapDispatchToProps(dispatch) {
const PAGE = urls.PAGES.POST_SEARCH
return {
clearPosts: () => dispatch(clearPostsSearch(PAGE)),
fetchPosts: (siteId, keywords, pageNum) =>
dispatch(fetchPostsSearch(PAGE, siteId, keywords, pageNum)),
fetchPostsAfterClear: debounce(
(siteId, keywords, pageNum) =>
dispatch(
afterClearData(
clearPostsSearch(PAGE),
fetchPostsSearch(PAGE, siteId, keywords, pageNum),
),
),
600,
),
}
}
const connectedPage = connect(page, mapStateToProps, mapDispatchToProps)
Page(connectedPage)
{
"navigationBarTitleText": "搜索"
}
\ No newline at end of file
<import src="root/templates/searchBar/searchBar.wxml"/>
<import src="root/templates/loaderBar/loaderBar.wxml"/>
<import src="root/templates/loaderPage/loaderPage.wxml"/>
<import src="root/templates/resultPage/resultPage.wxml"/>
<import src="root/templates/postCard/postCard.wxml"/>
<view class="post-search" style="height: 100vh; overflow:hidden;">
<template
is="search-bar"
data="{{background, isWhiteBackground, value:keywords, placeholder:'搜索文章', iconClear, iconClearWhite, handleInput, handleClear}}"
/>
<template wx:if="{{isFirst || !keywords}}" is='result-page'
data="{{height: 'calc(100vh - 112rpx)', text: '结果将会显示在这里'}}"/>
<block wx:if="{{posts.length}}">
<block wx:if="{{keywords}}">
<view class="search-result" style="width:calc(100vw - 80rpx)">
<text class="search-result-text">{{paginationPosts.totalCount}}条关于"{{keywords}}"的结果</text>
</view>
<scroll-view class="posts-content" lower-threshold="60" scroll-y="true" style="height:calc(100vh - 202rpx)"
bindscrolltolower="loadMorePosts">
<block wx:for="{{posts}}" wx:key="{{item.id}}" wx:for-index="index" wx:for-item="item">
<template is="post-card-d" data="{{...item, handlePost, icon: iconLink}}"/>
</block>
<template
is='loader-bar'
data="{{isLoading: isFetchingPosts, noMoreText: !isFetchingPosts&& paginationPosts.currPage === paginationPosts.pages && '- 无更多结果 -', style:'margin: 56rpx 0 40rpx 0'}}"
/>
</scroll-view>
</block>
</block>
<block wx:else>
<template wx:if="{{isFetchingPosts}}" is='loader-page' data="{{height: 'calc(100vh - 112rpx)'}}"/>
<template wx:else is='result-page'
data="{{height: 'calc(100vh - 109rpx)', icon: iconNoResult, text: '未找到相关结果'}}"/>
</block>
</view>
@import '/templates/searchBar/searchBar.wxss';
@import '/templates/loaderBar/loaderBar.wxss';
@import '/templates/loaderPage/loaderPage.wxss';
@import '/templates/resultPage/resultPage.wxss';
@import '/templates/postCard/postCard.wxss';
@import "/styles/main.wxss";
.post-search .search-result {
padding: 28rpx 16rpx 28rpx 40rpx;
width: 100vw;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.post-search .search-result .search-result-text {
height: 40rpx;
font-size: 28rpx;
color: #b6b6b6;
}
import { connect } from 'root/wmp-redux'
import iconPaths from 'root/constants/blog/iconPaths'
import { cutString, saveCanvasImage } from 'root/utils/helpers/helper'
import { getImageInfo } from 'wechat_common/utils/helpers/shareHelper'
import { getSharing } from 'root/selectors/blog/sharing'
import { replaceQnDomain } from 'wechat_common/utils/helpers/imageHelper'
import { formatProtocol } from 'wechat_common/utils/utils'
import urls from 'root/constants/blog/urls'
import { getPosts } from 'root/selectors/blog/post'
import { getPostDetail } from 'root/selectors/blog/postDetail'
const { iconDownload } = iconPaths
const TITLE_PER_LINE_WORDS = 16
const TITILE_MAX_LINES = 2
const CONTENT_PER_LINE_WORDS = 19
const CONTENT_MAX_LINES = 6
const NAME_PER_LINE_WORDS = 9
const NAME_MAX_LINES = 1
const page = {
data: {
iconDownload,
hasDrawn: false,
},
onLoad(option) {
const { postList } = this.data
const { postId } = option
const { blurb, title: headerTitle, iconUrl: backgroundUrl } = postList.find(
item => item.id == postId,
)
const { officialName = '', officialIconUrl, sceneCodeUrl } = this.data
const title = cutString(headerTitle, TITLE_PER_LINE_WORDS * 2)
const content = cutString(blurb, CONTENT_PER_LINE_WORDS * 2)
const name = cutString(officialName, NAME_PER_LINE_WORDS * 2)
const { screenWidth, screenHeight } = wx.getSystemInfoSync()
const promiseList = [backgroundUrl, officialIconUrl].map(url =>
getImageInfo(formatProtocol(replaceQnDomain(url))),
)
const ctx = wx.createCanvasContext('share-image')
Promise.all(promiseList)
.then(([backgroundUrl, officialIconUrl]) => {
ctx.scale(screenWidth / 375, screenWidth / 375)
ctx.setFillStyle('white')
ctx.fillRect(0, 0, 329, 476)
ctx.drawImage(backgroundUrl.path, 0, 0, 329, 189.5)
ctx.setFillStyle('rgba(0, 0, 0, 0.2)')
ctx.fillRect(0, 0, 329, 189.5)
ctx.setFontSize(18)
ctx.setFillStyle('#ffffff')
ctx.setTextAlign('left')
title.slice(0, TITILE_MAX_LINES).forEach((item, index) => {
if (
title.length > TITILE_MAX_LINES &&
index === TITILE_MAX_LINES - 1
) {
item = `${item.slice(0, item.length - 1)}...`
}
ctx.fillText(item, 18, 155 + index * 22)
})
ctx.setFontSize(15)
ctx.setFillStyle('#222222')
content.slice(0, CONTENT_MAX_LINES).forEach((item, index) => {
if (
content.length > CONTENT_MAX_LINES &&
index === CONTENT_MAX_LINES - 1
) {
item = `${item.slice(0, item.length - 1)}...`
}
ctx.fillText(item, 18, 220 + index * 24)
})
const grd = ctx.createLinearGradient(0, 245, 0, 355)
grd.addColorStop(0, 'rgba(255, 255, 255, 0)')
grd.addColorStop(1, 'white')
ctx.setFillStyle(grd)
ctx.fillRect(0, 245, 329, 100)
ctx.save()
ctx.beginPath()
ctx.arc(43, 401, 23, 0, 2 * Math.PI)
ctx.clip()
ctx.drawImage(officialIconUrl.path, 20, 378, 46, 46)
ctx.restore()
ctx.setFillStyle('#222222')
ctx.setTextAlign('left')
ctx.setFontSize(14)
name.slice(0, NAME_MAX_LINES).forEach((item, index) => {
if (name.length > NAME_MAX_LINES && index === NAME_MAX_LINES - 1) {
item = `${item.slice(0, item.length - 1)}...`
}
ctx.fillText(item, 72, 395)
})
ctx.setFontSize(12)
ctx.setFillStyle('#bbbbbb')
ctx.fillText('扫码查看完整文章', 72, 417)
return getImageInfo(replaceQnDomain(sceneCodeUrl))
})
.then(sceneCodeUrl => {
ctx.drawImage(sceneCodeUrl.path, 201, 350, 110, 110)
ctx.draw()
this.setData({
hasDrawn: true,
})
})
.catch(e => {
ctx.draw()
this.setData({
hasDrawn: true,
})
wx.showModal({
title: '网络错误',
content: '图片生成失败',
showCancel: false,
})
})
},
saveImage() {
saveCanvasImage('share-image')
},
}
function mapStateToProps(state, ownProps) {
const { officialName, officialIconUrl, sceneCodeUrl } = getSharing(state)
const { list: postList } = getPosts(state, urls.PAGES.POST_INDEX)
const { headerTitleString, backgroundUrl } = getPostDetail(state)
return {
officialName,
officialIconUrl,
sceneCodeUrl,
headerTitleString,
backgroundUrl,
postList,
}
}
const connectedPage = connect(page, mapStateToProps, {})
Page(connectedPage)
{
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#292940"
}
<view class="share-container">
<canvas canvas-id="share-image" class="share-canvas" style="display: {{hasDrawn ? 'flex' : 'none' }}"></canvas>
<view class="flex-row flex-col center-align-item" style="width: 658rpx;height: 952rpx; margin-top: 48rpx; background: #fff" wx:if="{{!hasDrawn}}">
<view class="loader"/>
</view>
<view class="share-save" bindtap="saveImage">
<image src="{{iconDownload}}" />
<text>保存图片</text>
</view>
</view>
@import "/styles/main.wxss";
@import "/templates/loader/loader.wxss";
.share-container {
width: 100vw;
height: 100vh;
background: #292940;
display: flex;
justify-content: center;
flex-wrap: wrap;
}
.share-canvas {
width: 658rpx;
height: 952rpx;
margin-top: 48rpx;
background: transparent;
}
.share-save {
display: flex;
width: 656rpx;
height: 96rpx;
background: rgba(255, 255, 255, 0.18);
justify-content: center;
align-items: center;
border: solid 2rpx rgba(255, 255, 255, 0.17);
border-radius: 10rpx;
image {
width: 48rpx;
height: 48rpx;
margin-right: 16rpx;
}
text {
color: rgba(255, 255, 255, 0.8);
font-size: 32rpx;
}
}
...@@ -2,5 +2,9 @@ ...@@ -2,5 +2,9 @@
"publicComponents": { "publicComponents": {
"blog": "components/blog/blog" "blog": "components/blog/blog"
}, },
"pages": {
"postDetail": "pages/postDetail/postDetail",
"postList": "pages/postList/postList"
},
"main": "index.js" "main": "index.js"
} }
\ No newline at end of file
import { extendMethod } from 'root/utils/helpers/helper' function extendMethod(...fns) {
return function (...args) {
fns.forEach(
fn => (typeof fn === 'function' ? fn.bind(this)(...args) : null),
)
}
}
function shareView(app) { function shareView(app) {
app.onLoad = extendMethod(app.onLoad, function() { app.onLoad = extendMethod(app.onLoad, function() {
......
Page()
export const data = {
type: 'product' || 'category' || 'page' || 'usage',
url: '',
value: '' || { id: '', name: '' },
}
export function makePhoneCall(e) {
// if (wx.isWept) {
// this.setError('预览模式下暂不支持拨打电话,请手机预览')
// } else {
// const { phone } = e.currentTarget.dataset
// wx.makePhoneCall({
// phoneNumber: phone,
// })
// }
}
export function handleProduct(e) {
// const { id, name } = e.currentTarget.dataset
// if (id) {
// wx.navigateTo({
// url: ``,
// })
// }
}
export function handleCategory(e) {
// const { id, name } = e.currentTarget.dataset
// if (id) {
// wx.navigateTo({
// url: ``,
// })
// }
}
export function handlePage(e) {
// const { id, name } = e.currentTarget.dataset
// if (id) {
// wx.navigateTo({
// url: ``,
// })
// }
}
<template name="shortcut">
<view class="{{'shortcut shortcut-' + iconType}}">
<image wx:if="{{type == 'product'}}" class="shortcut-image" bindtap="handleProduct" src="{{url}}" data-id="{{value}}"/>
<image wx:elif="{{type == 'category'}}" class="shortcut-image" bindtap="handleCategory" src="{{url}}" data-id="{{value.id}}" data-name="{{value.name}}" />
<block wx:elif="{{type == 'usage'}}">
<button wx:if="{{value == 'livechat'}}" open-type="{{showContactBtn ? 'contact' : ''}}" class="shortcut-button" bindtap="liveChat">
<image src="{{url}}" class="shortcut-image" />
</button>
<button wx:elif="{{value == 'share'}}" open-type="share" class="shortcut-button" bindtap="share">
<image src="{{url}}" class="shortcut-image" />
</button>
<image wx:elif="{{value == 'map'}}" bindtap="navigation" data-coordinate="{{mapCoordinate}}" data-address="{{mapLocation}}" src="{{url}}" class="shortcut-image" />
<image wx:else bindtap="makePhoneCall" data-phone="{{number}}" src="{{url}}" class="shortcut-image" />
</block>
<image wx:elif="{{type == 'page'}}" class="shortcut-image" bindtap="handlePage" src="{{url}}" data-id="{{value}}" />
<image wx:elif="{{type == 'blogPost'}}" class="shortcut-image" bindtap="handlePost" src="{{url}}" data-id="{{value}}" />
<image wx:else src="{{url}}" class="shortcut-image" />
<text class="shortcut-image-text">{{name}}</text>
</view>
</template>
.shortcut {
text-align: center;
}
.shortcut .shortcut-image {
height: 120rpx;
width: 120rpx;
display: block;
}
.shortcut .shortcut-button {
height: 120rpx;
width: 120rpx;
display: block;
padding: 0;
}
.shortcut .shortcut-image-text {
color: #333;
font-size: 15px;
}
.shortcut-container {
text-align: center;
}
.shortcut-square {
border-radius: 3px;
}
.shortcut-circle .shortcut-image,
.shortcut-circle .shortcut-button {
border-radius: 50%;
overflow: hidden;
}
.shortcut-circle button::after {
border: 0;
}
\ No newline at end of file
<import src="root/templates/shortcut/shortcut.wxml"/> <import src="/templates/shortcut/shortcut.wxml"/>
<template name="shortcuts"> <template name="shortcuts">
<view wx:if="{{shortcuts.length !== 0}}" class="shortcuts"> <view wx:if="{{shortcuts.length !== 0}}" class="shortcuts">
......
import {
doAuthorizeGet as doGet,
doPut,
doPost,
doDelete,
poller,
} from 'wechat_common/utils/request'
import { ADMINTOOL_API_URL } from 'root/constants/admintool/urlConstants'
const customHeader = {
headerName: 'Authorization',
}
function getVerifyCode(phone) {
return doPost({
url: ADMINTOOL_API_URL.GET_VERIFY_CODE(),
data: {
phone,
},
})
}
function authorize(phone, verifyCode) {
return doPost({
url: ADMINTOOL_API_URL.LOGIN(),
data: {
phone,
verify_code: verifyCode,
},
})
}
function fetchWmpList() {
return doGet({
url: ADMINTOOL_API_URL.WMPLIST(),
customHeader,
})
}
function fetchUnreadCount() {
return doGet({
url: ADMINTOOL_API_URL.GET_UNREAD_COUNT(),
customHeader,
})
}
function fetchSummaryInfo(appId, storefrontId) {
return doGet({
url: ADMINTOOL_API_URL.SUMMARY_INFO(appId, storefrontId),
customHeader,
})
}
function fetchAnalytics(options) {
return doGet({
url: options,
customHeader,
})
}
function fetchMemberList(appId, orderBy, page) {
return doGet({
url: ADMINTOOL_API_URL.MEMBERLIST(appId, { order_by: orderBy, page }),
customHeader,
})
}
function fetchMemberDetail(appId, memberId) {
return doGet({
url: ADMINTOOL_API_URL.MEMBERLIST(appId, memberId),
customHeader,
})
}
function fetchMiniprogramPaymentOrders(options) {
return doGet({
url: options,
customHeader,
})
}
function fetchOrderDetail(appType, appId, orderNum, orderType) {
const url = ADMINTOOL_API_URL.ORDERDETAIL(appType, appId, orderType, orderNum)
return doGet({
url,
customHeader,
})
}
function updateOrderDetail(appType, appId, orderNum, orderType, remark, status) {
return doPut({
url: ADMINTOOL_API_URL.ORDERDETAIL(appType, appId, orderType, orderNum),
data: {
remark,
status,
},
customHeader,
})
}
function setOrderStatus(appId, orderNum, status, shipping_notes) {
if(status === 'shipped'){
return doPost({
url: ADMINTOOL_API_URL.ORDERDSTATUS(appId, orderNum, status),
data: {
status,
deliveryMemberPhone: shipping_notes,
},
customHeader,
})
}else{
return doPut({
url: ADMINTOOL_API_URL.ORDERDSTATUS(appId, orderNum, status),
data: {status},
customHeader,
})
}
}
function fetchVisitorData(options) {
return new Promise((resolve, reject) => {
poller(options, resolve, reject, customHeader)
})
}
function fetchComments(appId, status, page, storefrontId) {
let options = {}
if (storefrontId) {
options = {
page,
storefrontId,
}
} else {
options = {
page,
}
}
return doGet({
url: ADMINTOOL_API_URL.COMMENTLIST(appId, status, options),
customHeader,
})
}
function approveComment(appId, commentId) {
return doPut({
url: ADMINTOOL_API_URL.APPROVECOMMENT(appId, commentId),
data: {
id: commentId,
},
customHeader,
})
}
function deleteComment(appId, commentId) {
return doDelete({
url: ADMINTOOL_API_URL.DELETECOMMENT(appId, commentId),
data: {
id: commentId,
},
customHeader,
})
}
function replyComment(appId, commentId, content, replyStatus) {
return doPost({
url: ADMINTOOL_API_URL.REPLYCOMMENT(appId),
data: {
id: commentId,
content,
status: replyStatus ? 4 : 3,
},
customHeader,
})
}
function approveAllComments(appId, storefrontId) {
return doPost({
url: ADMINTOOL_API_URL.APPROVEALL(appId, storefrontId),
data: storefrontId
? {
storefrontId,
}
: null,
customHeader,
})
}
function markMemberAsRead(appId, memberId) {
return doPost({
url: ADMINTOOL_API_URL.MARK_MEMBER_AS_READ(appId, memberId),
data: {
id: memberId,
},
customHeader,
})
}
function markOrderAsRead(appId, orderNumber) {
return doPost({
url: ADMINTOOL_API_URL.MARK_ORDER_AS_READ(appId, orderNumber),
data: {
orderNumber,
},
customHeader,
})
}
function markCommentAsRead(appId, commentId) {
return doPost({
url: ADMINTOOL_API_URL.MARK_COMMENT_AS_READ(appId, commentId),
data: {
id: commentId,
},
customHeader,
})
}
function createTicket(appId, content, attachments) {
return doPost({
url: ADMINTOOL_API_URL.CREATE_TICKET(appId),
data: {
id: appId,
content,
attachments,
},
customHeader,
})
}
function upload(options) {
wx.uploadFile({
url: options.url,
filePath: options.filePath,
name: 'file',
formData: {
token: options.token,
},
success: res => {
if (options.success) {
options.success(res)
}
},
})
}
function getUploadInfo(appId) {
return doPost({
url: ADMINTOOL_API_URL.GET_UPLOAD_INFO(appId),
})
}
function fetchOrders({appType, appId, orderType, storefrontId, nextPage, status}) {
let options = { page: nextPage }
if (storefrontId) options.storefrontId = storefrontId
if (status) options.status = status
if (appType === 'restaurant'){
options.type = orderType === 'dinein' ? 'dine_in' : orderType
}
//options.per = 8
return doGet({
url: ADMINTOOL_API_URL.ORDERLIST(appType, appId, orderType, options),
customHeader,
}).then(
res => {
switch(appType) {
case 'restaurant':
res.data.total = res.data.paginationMeta.totalCount || 0
res.data.unread = res.data.unreadCount || 0
break;
case 'showcase':
switch (orderType) {
case 'sales':
res.data.orders = res.data.sales
res.data.total = res.data.totalSalesOrders || 0
res.data.unread = res.data.salesOrdersUnread || 0
break;
case 'recharge':
res.data.orders = res.data.recharges
res.data.total = res.data.totalRechargeOrders || 0
res.data.unread = res.data.rechargeOrdersUnread || 0
break;
case 'groupon':
res.data.orders = res.data.grouponOrders
res.data.total = res.data.paginationMeta.totalCount || 0
res.data.unread = res.data.grouponOrdersUnread || 0
break;
default:
break;
}
break;
default:
break;
}
return res
}
)
}
function fetchDocuments() {
return doGet({
url: ADMINTOOL_API_URL.DOCUMENTLIST(),
customHeader,
})
}
function fetchDocumentUnreadCount() {
return doGet({
url: ADMINTOOL_API_URL.DOCUMENT_UNREAD_COUNT(),
customHeader,
})
}
function fetchDocumentContent(id) {
return doGet({
url: ADMINTOOL_API_URL.DOCUMENT_CONTENT(id),
customHeader,
})
}
const admintoolApi = {
fetchWmpList,
fetchSummaryInfo,
getVerifyCode,
authorize,
fetchComments,
approveComment,
fetchUnreadCount,
fetchAnalytics,
fetchMiniprogramPaymentOrders,
fetchVisitorData,
fetchMemberList,
fetchMemberDetail,
fetchOrderDetail,
deleteComment,
approveAllComments,
replyComment,
markMemberAsRead,
markOrderAsRead,
markCommentAsRead,
createTicket,
upload,
getUploadInfo,
fetchOrders,
updateOrderDetail,
fetchDocuments,
fetchDocumentUnreadCount,
fetchDocumentContent,
setOrderStatus,
}
export default admintoolApi
import { doGet, doPut } from 'wechat_common/utils/request'
import UrlConstants from 'root/constants/ecommerce/urlConstants'
export function getAdvertisePopup(siteId, code) {
const url = UrlConstants.ECOMMERCE.GET_ADVERTISE_POPUP(siteId, code)
return doGet({ url })
}
export function hideAdvertisePopup(siteId, code) {
const url = UrlConstants.ECOMMERCE.UPDATE_ADVERTISE_POPUP(siteId)
return doPut({
url,
data: { code },
})
}
import { doGet } from 'wechat_common/utils/request'
import { FETCH_ANNOUNCEMENTS } from 'root/constants/common/urlConstants'
export function fetchAnnouncements(siteId) {
return doGet({
url: FETCH_ANNOUNCEMENTS(siteId),
})
}
import { doGet, doPost, doPut, doDelete } from 'wechat_common/utils/request'
import { bindSiteId, bindCode } from 'wechat_common/utils/context'
import Urls from 'root/constants/blog/urls'
import compose from 'ramda/src/compose'
import {
bindRetry,
bindShowToast,
} from 'wechat_common/networkErrorManager'
const getPostDetail = compose(bindCode, bindSiteId)(
(siteId, code, { params: { postId }, ...options }) =>
doGet(
bindRetry({
url: Urls.REQUESTS.GET_POST(code, postId),
...options,
}),
),
)
const getPosts = compose(bindCode, bindSiteId)(
(siteId, code, { params: { category, pageNum }, ...options }) =>
doGet(
bindRetry({
url: Urls.REQUESTS.GET_POSTS(siteId, code, category, pageNum),
...options,
}),
),
)
const likePost = compose(bindCode, bindSiteId)(
(
siteId,
code,
{ params: { status, postId, nickname, wechat_photo }, ...options },
) => {
const params = {
url: status
? Urls.REQUESTS.SET_LIKE_POST(postId)
: Urls.REQUESTS.DELETE_LIKE_POST(postId),
data: { code, nickname, wechat_photo },
...options,
}
if (status) {
doPost(bindShowToast(params))
} else {
doDelete(bindShowToast(params))
}
},
)
export default {
likePost,
getPosts,
getPostDetail,
}
import { doGet, doPost, doPut, poller } from 'wechat_common/utils/request'
import { bindSiteId, bindCode } from 'wechat_common/utils/context'
import UrlConstants from 'root/constants/ecommerce/urlConstants'
import compose from 'ramda/src/compose'
import { bindRefresh, bindRetry, bindShowToast } from 'wechat_common/networkErrorManager'
export function getCategories(options) {
return doGet(bindRetry(options))
}
export function getProducts(url) {
return doGet(bindRefresh({ url }))
}
export const getFlashSaleProducts = bindSiteId((siteId, status, pageNum) => {
const url = UrlConstants.ECOMMERCE.GET_FLASH_SALE_PRODUCTS(
siteId,
status,
pageNum,
)
return doGet(bindRetry({ url }))
})
export const getGroupBuyProducts = bindSiteId(
(siteId, status, pageNum, code) => {
const url = UrlConstants.ECOMMERCE.GET_GROUP_BUY_PRODUCTS(
siteId,
status,
pageNum,
code,
)
return doGet(bindRetry({ url }))
},
)
export function getGroupBuyDetail(options) {
return doGet(bindRetry(options))
}
export function getProduct(options) {
return doGet(bindRetry(options))
}
export function getShare(options) {
return doPost(bindShowToast(options))
}
export function getSettings(options) {
return doGet(bindRetry(options))
}
export function convertCoupon(options) {
return doPost(bindShowToast(options))
}
export function getOptimal(options) {
return doPost(bindShowToast(options))
}
export const getCoupons = compose(bindCode, bindSiteId)((siteId, code) =>
doGet(bindRetry({ url: UrlConstants.ECOMMERCE.GET_COUPON_LIST_BY_SITE(siteId, code) })),
)
export const getUserCoupons = compose(bindCode, bindSiteId)(
(siteId, code, status, page, size) =>
doGet(bindRetry({
url: UrlConstants.ECOMMERCE.GET_COUPON_LIST_BY_USER(
siteId,
code,
status,
page,
size,
),
})),
)
export function search(options) {
return doPost(bindRetry(options))
}
export const getMembershipCard = compose(bindCode, bindSiteId)((siteId, code) =>
doGet(bindRetry({ url: UrlConstants.ECOMMERCE.GET_MEMBERSHIP_CARD(siteId, code) })),
)
export function getSignature(url) {
return doGet(bindRetry({ url }))
}
export function getAffiliateSettings(url) {
return doGet(bindRetry({ url }))
}
export function getAffiliateMpCode(url) {
return doGet(bindRetry({ url }))
}
export function fetchCommissionDetail(url) {
return doGet(bindRetry({ url }))
}
export function fetchCommissionHistory(url) {
return doGet(bindRetry({ url }))
}
export function fetchCommissionOrder(url) {
return doGet(bindRetry({ url }))
}
export function applyAffiliate(options) {
return doPost(bindShowToast(options))
}
export function updateAffiliate(options) {
return doPut(bindShowToast(options))
}
export function createWithdrawal(options) {
return doPost(bindShowToast(options))
}
export function createOrder(options) {
doPost(bindShowToast({
url: options.url,
data: options.data,
success(res) {
if (res.status === 200) {
const pollUrl = `/r/v1/tasks/${res.data.type}/${res.data.id}.jsm?v=2`
poller(pollUrl, options.success, options.fail)
} else if (options.success) {
options.success(res)
}
},
fail() {
if (options.fail) {
options.fail()
}
},
}))
}
export function createGroupBuyOrder(options) {
doPost(bindShowToast({
url: options.url,
data: options.data,
success(res) {
if (res.status === 200) {
const pollUrl = `/r/v1/tasks/${res.data.type}/${res.data.id}.jsm?v=2`
poller(pollUrl, options.success, options.fail)
} else if (options.success) {
options.success(res)
}
},
fail(err) {
if (options.fail) {
options.fail(err)
}
},
}))
}
export function getOrders(options) {
doGet(bindRetry(options))
}
export function updateOrder(options) {
doPut(bindShowToast(options))
}
export function getSliders(options) {
doGet(bindRetry(options))
}
export function receviceCoupons(options) {
doPost(bindShowToast(options))
}
const getOrderById = compose(bindCode, bindSiteId)(
(siteId, code, { params: { orderId }, ...options }) => {
doGet(bindRetry({
url: UrlConstants.ECOMMERCE.GET_ORDERS_BY_ID({ siteId, code, orderId }),
...options,
}))
},
)
export function upload(options) {
wx.uploadFile({
url: options.url,
filePath: options.filePath,
name: 'file',
formData: {
token: options.token,
},
success: res => {
if (options.success) {
options.success(res)
}
},
})
}
export function middleWareFunction(options) {
return doPost(bindShowToast(options))
}
export function fetchComments(options) {
return doGet(bindRetry(options))
}
export function fetchPointsRule(url) {
return doGet(bindRetry({ url }))
}
export const fetchPointRecords = compose(bindCode, bindSiteId)(
(siteId, code, page) =>
doGet(bindRetry({
url: UrlConstants.ECOMMERCE.FETCH_POINT_RECORDS(siteId, code, page),
})),
)
export const fetchNewCouponList = compose(bindCode, bindSiteId)(
(siteId, code) =>
new Promise((resolve, reject) => {
poller(
UrlConstants.ECOMMERCE.FETCH_NEW_COUPON_LIST(siteId, code),
resolve,
reject,
)
}),
)
const cancelOrder = compose(bindCode, bindSiteId)(
(siteId, code, { params: { orderId }, ...options }) =>
doPut(bindShowToast({
url: UrlConstants.ECOMMERCE.CANCEL_ORDER(siteId, orderId, code),
...options,
})),
)
const getUserInfo = compose(bindCode, bindSiteId)((siteId, code, options) =>
doGet(bindRetry({
url: UrlConstants.ECOMMERCE.GET_USER_INFO(siteId, code),
...options,
})),
)
export const fetchRechargeSetting = bindSiteId(siteId =>
doGet(bindRetry({
url: UrlConstants.ECOMMERCE.FETCH_RECHARGE_SETTING(siteId),
})),
)
export const getStoreValueCardInfo = compose(bindCode, bindSiteId)(
(siteId, code) =>
doGet(bindRetry({
url: UrlConstants.ECOMMERCE.GET_STORE_VALUE_CARD_INFO(siteId, code),
})),
)
export const recharge = compose(bindCode, bindSiteId)(
(siteId, code, rechargeRuleId) =>
doPost(bindShowToast({
url: UrlConstants.ECOMMERCE.RECHARGE(siteId),
data: {
code,
rechargeRuleId,
},
})).then(res => {
if (res.status === 200) {
const pollUrl = `/r/v1/tasks/${res.data.type}/${res.data.id}.jsm?v=2`
return new Promise(resolve => {
poller(pollUrl, response => {
resolve(response)
})
})
} else {
return Promise.reject(res)
}
}),
)
export const fetchRechargeRecords = compose(bindCode, bindSiteId)(
(siteId, code, page) =>
doGet(bindRetry({
url: UrlConstants.ECOMMERCE.FETCH_RECHARGE_RECORDS(siteId, code, page),
})),
)
export default {
getCategories,
getProducts,
getProduct,
getShare,
getSettings,
convertCoupon,
search,
getMembershipCard,
getSignature,
createOrder,
createGroupBuyOrder,
getOrders,
updateOrder,
getSliders,
getFlashSaleProducts,
getGroupBuyProducts,
getGroupBuyDetail,
getCoupons,
getOptimal,
receviceCoupons,
getOrderById,
upload,
middleWareFunction,
fetchComments,
fetchPointsRule,
fetchPointRecords,
fetchNewCouponList,
cancelOrder,
getUserInfo,
getStoreValueCardInfo,
fetchRechargeSetting,
recharge,
fetchRechargeRecords,
}
import { doGet, doPost, doPut, poller } from 'wechat_common/utils/request'
import URLS from 'root/constants/giftcard/urls'
export function getProducts(url) {
return doGet({ url })
}
export function getProduct(url) {
return doGet({ url })
}
export function getCards(url) {
return doGet({ url })
}
export function createOrder(options) {
return new Promise((resolve, reject) => {
doPost({
url: options.url,
data: options.data,
success(res) {
if (res.status === 200) {
const { type, id } = res.data
const pollUrl = URLS.POLL_ORDER(type, id)
poller(pollUrl, resolve, reject)
} else {
resolve(res)
}
},
fail() {
reject()
},
})
})
}
export function getPackets(url) {
return doGet({ url })
}
export function getPacket(url) {
return doGet({ url })
}
export function updatePacket(options) {
return doPut(options)
}
export function getSettings(url) {
return doGet({ url })
}
import EmojiObj from './emojimap'
const emoji = EmojiObj.emojiList.emoji
const prefix = EmojiObj.emojiSxlUrl
/* eslint-disable max-statements */
function generateRichTextNode(text) {
let tempStr = text
const richTextNode = []
let leftBracketIndex = tempStr.indexOf('[')
let rightBracketIndex = 0
// 没有emoji
if (leftBracketIndex === -1) {
richTextNode.push({
type: 'text',
text: tempStr,
})
return richTextNode
}
while (tempStr.length !== 0) {
// 最前面是文本
if (leftBracketIndex !== 0) {
// 最后全是文字
if (leftBracketIndex === -1) {
richTextNode.push({
type: 'text',
text: tempStr.slice(0, tempStr.length),
})
tempStr = ''
} else {
richTextNode.push({
type: 'text',
text: tempStr.slice(0, leftBracketIndex),
})
tempStr = tempStr.substring(leftBracketIndex, tempStr.length + 1)
}
} else {
// 前面是[
rightBracketIndex = tempStr.indexOf(']')
const emojiName = tempStr.slice(0, rightBracketIndex + 1)
if (emoji[emojiName]) {
richTextNode.push({
name: 'img',
attrs: {
width: '20rpx',
height: '20rpx',
src: emoji[emojiName].img,
},
})
} else {
richTextNode.push({
type: 'text',
text: `${emojiName}`,
})
}
tempStr = tempStr.substring(rightBracketIndex + 1, tempStr.length)
}
leftBracketIndex = tempStr.indexOf('[')
if (tempStr.indexOf(']') === -1) {
leftBracketIndex = -1
}
}
return richTextNode
}
/* eslint-enable max-statements */
function generateImageNode(fileStr) {
let file = {}
try {
file = JSON.parse(fileStr)
} catch (e) {
console.info('JSON parse error')
}
let width = 0
let height = 0
if (file.w > 150) {
width = 100
height = file.h / (file.w / 100)
} else {
width = file.w
height = file.h
}
const richTextNode = []
richTextNode.push({
name: 'img',
attrs: {
width: `${width}rpx`,
height: `${height}rpx`,
src: file.url,
},
})
return richTextNode
}
function generateBigEmojiImageFile(messageStr) {
let content = {}
try {
content = JSON.parse(messageStr)
} catch (e) {
console.info('JSON parse error')
}
const file = { w: content.w, h: content.h, url: '' }
file.url = `${prefix}/${content.catalog}/${content.name}`
return JSON.stringify(file)
}
export const convertChatItem = item => {
if (item.messageType === 'welcome_message') {
return {
...item,
customMessage: item.customMessageChanged
? item.customMessageChanged
: JSON.parse(item.customMessage),
messageType: 'welcome_message',
}
} else if (item.messageType === 'service_notification') {
return {
...item,
messageType: 'service_notification',
}
}
if (item.message) {
return {
...item,
messageType: 'text',
nodes: generateRichTextNode(item.message),
}
} else if (item.messageType === 'image') {
return {
...item,
messageType: 'image',
nodes: generateImageNode(item.customMessage),
}
} else if (item.messageType === 'big_emoji') {
return {
...item,
messageType: 'image',
nodes: generateImageNode(generateBigEmojiImageFile(item.customMessage)),
}
} else {
return {
...item,
messageType: 'text',
}
}
}
const emojiSxlUrl = `https://user-assets.sxlcdn.com/livechat/emoji`
const iconEmojiDelete =
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAMAAABHPGVmAAAAk1BMVEUAAADAwMC3t7e/v7/Gxsa/v7+/v7/AwMC/v7+/v7+/v7++vr7CwsK/v7+/v7+/v7++vr6/v7+/v7+/v7+/v7+/v7/AwMCbm5u/v7+/v7+/v7+/v7/AwMC/v7+/v7+/v7+/v7+/v7+/v7++vr7AwMC9vb2/v7+/v7/CwsLAwMDHx8fExMTIyMjLy8vJycnKysrPz8+3r5/kAAAAKHRSTlMA8Q39A/vcIwf34B0P1uNJIuzPjHFqFwVFmls4J761rMiFoVJ6XTNYKpTiKQAAAylJREFUeF7tmEl3ozAQhNt0ywYM3rfETuJMltEC2P//1w3IVg555olmHjfVlcMnAVUtFfgVFBQUFBQUFBREXAAR8BVnEUNZzF8YwTYdsZQ/Pcc8CsGmNEpyZMoqn/MYzxoFomIIhTDFujtjD3OlEFEyVENqzHUN1HUfx0QiKjliKJEVIorioxuF4LQ0AkW53mddf7Asmh5eNaLQeUfGLtUN4w8wtbaULlshiFZVw3gF7g8Jr4UQ5ZMfQkAvl5pRnWO+teYGhT53YMBbWTP0OAMCrqaJQjmLwKunhmGWkx4MiEZSyZEPQvDeMGRy7MOAbCTRCyHYFqqx+Rxi6KHIC7FhYrCW/gKCQSAuTBRWGwaDCSE4CmmNDtRhJhFxIS5M2o1uweQZmw7iC5N2o58O9EMhyDabjAdxYdJudIINytXiTiGY5tfrKuJBXJjo8a6FsUiMuIynQDfGuEqScgvEgLgwaTU6wVEJFEVDsYxCoOBBXJjI5LvdIOeyoaRTiGGaNowqZ7wuFybYYnT3vlJ928t9H3o2AeoOIdhUHqNbykzb9R/y6jfDDyH40g3jYo3uo6CUEh2jM4TgYE8mv4z+kJIWeFORToDn+FUhEI1/ohNMxtIy5NgyGJBspBDlMvKnYtzMV1SIxo6CYXeiZG69P8g3mbhvIqrGlQyIG1Xi8ukobYzb32UkOu93hrihi6i3QB5G48aD9aKl8B2vlPyAvYehZyc4WZqjMLMLFR5aKdHN5+kC4nvClGcA4Kdwe0ISbMs7g35yTB6B+swTs1wAtUCSpLBZcnt3lTBJTew1GXX+eGrVj69X5/OGkhu5Aeo54y+rGOjh489Py3drmn//z2nlBR5TPKcV5rnr7SEF9nvGuct3HW2uMVzxz8KifAcaDOICRhVboMEg7kiB5m/f+4nyQ34CRtmAGWAnLmD637V2I2Vn7I+8AXPiF0sHiWjyGDrf43W663WPv7wBdW8kqlXUp5Eonn9BfAGTRYxuZX7vVmJGSyQQTcJpiUxLS+QJGKW4fRfavovX3Clmc6f1mtlBXuSQHaSjLAdoU4fvhYdvuIOCgoKCgoL+AXU1fW2kbFGNAAAAAElFTkSuQmCC'
const albumArr = []
const emojiList = {
emoji: {
'[大笑]': { file: 'emoji_0.png' },
'[可爱]': { file: 'emoji_01.png' },
'[色]': { file: 'emoji_02.png' },
'[嘘]': { file: 'emoji_03.png' },
'[亲]': { file: 'emoji_04.png' },
'[呆]': { file: 'emoji_05.png' },
'[口水]': { file: 'emoji_06.png' },
'[汗]': { file: 'emoji_145.png' },
'[呲牙]': { file: 'emoji_07.png' },
'[鬼脸]': { file: 'emoji_08.png' },
'[害羞]': { file: 'emoji_09.png' },
'[偷笑]': { file: 'emoji_10.png' },
'[调皮]': { file: 'emoji_11.png' },
'[可怜]': { file: 'emoji_12.png' },
'[敲]': { file: 'emoji_13.png' },
'[惊讶]': { file: 'emoji_14.png' },
'[流感]': { file: 'emoji_15.png' },
'[委屈]': { file: 'emoji_16.png' },
'[流泪]': { file: 'emoji_17.png' },
'[嚎哭]': { file: 'emoji_18.png' },
'[惊恐]': { file: 'emoji_19.png' },
'[怒]': { file: 'emoji_20.png' },
'[酷]': { file: 'emoji_21.png' },
'[不说]': { file: 'emoji_22.png' },
'[鄙视]': { file: 'emoji_23.png' },
'[阿弥陀佛]': { file: 'emoji_24.png' },
'[奸笑]': { file: 'emoji_25.png' },
'[睡着]': { file: 'emoji_26.png' },
'[口罩]': { file: 'emoji_27.png' },
'[努力]': { file: 'emoji_28.png' },
'[抠鼻孔]': { file: 'emoji_29.png' },
'[疑问]': { file: 'emoji_30.png' },
'[怒骂]': { file: 'emoji_31.png' },
'[晕]': { file: 'emoji_32.png' },
'[呕吐]': { file: 'emoji_33.png' },
'[拜一拜]': { file: 'emoji_160.png' },
'[惊喜]': { file: 'emoji_161.png' },
'[流汗]': { file: 'emoji_162.png' },
'[卖萌]': { file: 'emoji_163.png' },
'[默契眨眼]': { file: 'emoji_164.png' },
'[烧香拜佛]': { file: 'emoji_165.png' },
'[晚安]': { file: 'emoji_166.png' },
'[强]': { file: 'emoji_34.png' },
'[弱]': { file: 'emoji_35.png' },
'[OK]': { file: 'emoji_36.png' },
'[拳头]': { file: 'emoji_37.png' },
'[胜利]': { file: 'emoji_38.png' },
'[鼓掌]': { file: 'emoji_39.png' },
'[握手]': { file: 'emoji_200.png' },
'[发怒]': { file: 'emoji_40.png' },
'[骷髅]': { file: 'emoji_41.png' },
'[便便]': { file: 'emoji_42.png' },
'[火]': { file: 'emoji_43.png' },
'[溜]': { file: 'emoji_44.png' },
'[爱心]': { file: 'emoji_45.png' },
'[心碎]': { file: 'emoji_46.png' },
'[钟情]': { file: 'emoji_47.png' },
'[唇]': { file: 'emoji_48.png' },
'[戒指]': { file: 'emoji_49.png' },
'[钻石]': { file: 'emoji_50.png' },
'[太阳]': { file: 'emoji_51.png' },
'[有时晴]': { file: 'emoji_52.png' },
'[多云]': { file: 'emoji_53.png' },
'[雷]': { file: 'emoji_54.png' },
'[雨]': { file: 'emoji_55.png' },
'[雪花]': { file: 'emoji_56.png' },
'[爱人]': { file: 'emoji_57.png' },
'[帽子]': { file: 'emoji_58.png' },
'[皇冠]': { file: 'emoji_59.png' },
'[篮球]': { file: 'emoji_60.png' },
'[足球]': { file: 'emoji_61.png' },
'[垒球]': { file: 'emoji_62.png' },
'[网球]': { file: 'emoji_63.png' },
'[台球]': { file: 'emoji_64.png' },
'[咖啡]': { file: 'emoji_65.png' },
'[啤酒]': { file: 'emoji_66.png' },
'[干杯]': { file: 'emoji_67.png' },
'[柠檬汁]': { file: 'emoji_68.png' },
'[餐具]': { file: 'emoji_69.png' },
'[汉堡]': { file: 'emoji_70.png' },
'[鸡腿]': { file: 'emoji_71.png' },
'[面条]': { file: 'emoji_72.png' },
'[冰淇淋]': { file: 'emoji_73.png' },
'[沙冰]': { file: 'emoji_74.png' },
'[生日蛋糕]': { file: 'emoji_75.png' },
'[蛋糕]': { file: 'emoji_76.png' },
'[糖果]': { file: 'emoji_77.png' },
'[葡萄]': { file: 'emoji_78.png' },
'[西瓜]': { file: 'emoji_79.png' },
'[光碟]': { file: 'emoji_80.png' },
'[手机]': { file: 'emoji_81.png' },
'[电话]': { file: 'emoji_82.png' },
'[电视]': { file: 'emoji_83.png' },
'[声音开启]': { file: 'emoji_84.png' },
'[声音关闭]': { file: 'emoji_85.png' },
'[铃铛]': { file: 'emoji_86.png' },
'[锁头]': { file: 'emoji_87.png' },
'[放大镜]': { file: 'emoji_88.png' },
'[灯泡]': { file: 'emoji_89.png' },
'[锤头]': { file: 'emoji_90.png' },
'[烟]': { file: 'emoji_91.png' },
'[炸弹]': { file: 'emoji_92.png' },
'[枪]': { file: 'emoji_93.png' },
'[刀]': { file: 'emoji_94.png' },
'[药]': { file: 'emoji_95.png' },
'[打针]': { file: 'emoji_96.png' },
'[钱袋]': { file: 'emoji_97.png' },
'[钞票]': { file: 'emoji_98.png' },
'[银行卡]': { file: 'emoji_99.png' },
'[手柄]': { file: 'emoji_100.png' },
'[麻将]': { file: 'emoji_101.png' },
'[调色板]': { file: 'emoji_102.png' },
'[电影]': { file: 'emoji_103.png' },
'[麦克风]': { file: 'emoji_104.png' },
'[耳机]': { file: 'emoji_105.png' },
'[音乐]': { file: 'emoji_106.png' },
'[吉他]': { file: 'emoji_107.png' },
'[火箭]': { file: 'emoji_108.png' },
'[飞机]': { file: 'emoji_109.png' },
'[火车]': { file: 'emoji_110.png' },
'[公交]': { file: 'emoji_111.png' },
'[轿车]': { file: 'emoji_112.png' },
'[出租车]': { file: 'emoji_113.png' },
'[警车]': { file: 'emoji_114.png' },
'[自行车]': { file: 'emoji_115.png' },
},
}
for (const emoji in emojiList) {
const emojiItem = emojiList[emoji]
for (const key in emojiItem) {
const item = emojiItem[key]
item.img = `${emojiSxlUrl}/${emoji}/${item.file}`
}
}
emojiList.jgz = {}
for (let i = 1; i <= 31; i++) {
const key = `jgz${i >= 10 ? i : `0${i}`}`
emojiList.jgz[key] = { file: `${key}.gif` }
}
// 内容
for (const emoji in emojiList) {
const emojiItem = emojiList[emoji]
for (const key in emojiItem) {
const item = emojiItem[key]
item.img = `${emojiSxlUrl}/${emoji}/${item.file}`
}
// 封面
albumArr.push({
album: emoji,
img: emojiItem[Object.keys(emojiItem)[0]].img,
})
}
// 添加删除按钮
emojiList.emoji['[删除]'] = {}
emojiList.emoji['[删除]'].img = iconEmojiDelete
export default {
emojiList,
albumArr,
emojiSxlUrl,
}
import { dispatch } from 'root/wmp-redux'
import { fetchSettings } from 'root/reducers/ecommerce/settings'
import {
fetchProducts,
fetchFlashSaleProducts,
fetchGroupBuyProducts,
} from 'root/actions/ecommerce/productActions'
import { forceUpdate } from 'root/reducers/ecommerce/global'
import { getMembershipCard } from 'root/actions/ecommerce/membership/cardActions'
export function refreshData(siteId, code, doNotFetchProducts) {
if (!doNotFetchProducts) {
dispatch(fetchProducts(siteId, 'all', 1))
}
dispatch(fetchFlashSaleProducts('all', 1))
code && dispatch(fetchGroupBuyProducts('all', 1, code))
dispatch(fetchSettings(siteId))
dispatch(getMembershipCard())
}
import { PAGES } from 'root/constants/admintool/urlConstants'
export function handleRequestSuccess() {
wx.hideLoading()
}
export function handleRequestError(err) {
wx.hideLoading()
if (err.data.code === 200000) {
wx.showModal({
title: '身份验证失败',
content: '请重新登录',
showCancel: false,
success: () => {
wx.setStorageSync('admin_tool_token', null)
wx.reLaunch({
url: PAGES.LOGIN,
})
},
})
} else if (err.data.code === 200001) {
wx.showModal({
title: '手机号不存在',
content: '请确认您输入的手机号已在员工授权管理中录入',
showCancel: false,
success: () => {
wx.setStorageSync('admin_tool_token', null)
wx.reLaunch({
url: PAGES.LOGIN,
})
},
})
} else if (err.data.code === 200002) {
wx.showModal({
title: '重新登录',
content: '权限发生变更,已被强行登出。请重新登录,或联系管理员。',
showCancel: false,
success: () => {
wx.setStorageSync('admin_tool_token', null)
wx.reLaunch({
url: PAGES.LOGIN,
})
},
})
} else {
wx.showModal({
title: '获取失败',
content: '请重新获取',
showCancel: false,
})
}
}
import {formatProtocol as _formatProtocol} from './tools'
export function formatProtocol(url) {
if (!url) {
return ''
}
return _formatProtocol(url)
}
export function convertPostLikes(post, nickname){
const { isLiked, likes = [] } = post
if(isLiked){
let likedIndex = -1
let isFirst = true
for(let i = 0; i < likes.length; i++){
if(isFirst && likes[i] === nickname){
likedIndex = i
isFirst = false
}
}
post.likes = likes.filter((item, index) => index !== likedIndex)
} else {
(post.likes || []).push(nickname)
}
post.isLiked = !post.isLiked
return post
}
\ No newline at end of file
export const targetMap = {
broadcast: {
className: 'broadcast',
title: '新建群发',
topHint: '您将给所有客户发送该条消息',
field: 'message',
maxLength: -1,
submitText: '发送',
},
welcome: {
className: 'welcome',
title: '设置欢迎语',
placeholder: '请输入欢迎语',
topHint: '当访客进入名片时在聊天窗口显示',
field: 'welcomeMessage',
switchField: 'openHotKey',
maxLength: 200,
},
autoReply: {
className: 'auto-reply',
title: '设置自动回复',
topHint: '当客户发送新消息时,自动回复:',
field: 'autoReply',
maxLength: 200,
},
}
import curry from 'ramda/src/curry'
export function getCacheTime(duration) {
return new Date().getTime() + duration
}
export const commonAnimation = wx.createAnimation({
duration: 400,
timingFunction: 'ease',
})
export const parseScene = scene => {
const params = {}
decodeURIComponent(scene)
.split('&')
.forEach((item, index) => {
const itemArr = item.split('=')
if (itemArr.length === 2) {
params[itemArr[0]] = itemArr[1]
} else {
params[index] = itemArr[0]
}
})
return params
}
// blog ecommerce presentation
export const baseFilterRelationData = curry(
(baseType, data, relationData = {}) => {
const filterData = data.filter(item => {
const type = item.category || baseType
if (item.type === 'blogPost' || item.type === 'product') {
if (
relationData[`${type}ProductIdList`] &&
relationData[`${type}ProductIdList`].find(
value => value.toString() === item.value.toString(),
)
) {
return false
}
} else if (item.type === 'category') {
if (
relationData[`${type}CategoryIdList`] &&
relationData[`${type}CategoryIdList`].find(
value => value.toString() === item.value.id.toString(),
)
) {
return false
}
}
return true
})
return filterData
},
)
// hexToRgba('#fff', 0.1) => rgba(255, 255, 255, 0.1)
export const hexToRgba = function(hex, alpha = 1) {
let c
if (/^#([A-Fa-f0-9]{3}){1,2}$/.test(hex)) {
c = hex.substring(1).split('')
if (c.length === 3) {
c = [c[0], c[0], c[1], c[1], c[2], c[2]]
}
c = `0x${c.join('')}`
return `rgba(${(c >> 16) & 255}, ${(c >> 8) & 255}, ${c & 255}, ${alpha})`
}
return c
}
// 1600 => 1.6k
export const KFormatter = num =>
num > 999 ? `${(num / 1000).toFixed(1)}k` : num
import { iconPath } from 'root/constants/presentation/iconPath'
import {
getRoundImage,
replaceWithDownloadFileDomain,
} from 'root/utils/helpers/presentationHelper'
import { showModal } from 'wechat_common/utils/wrappedWXTool'
export const getScoreText = score => {
const map = {
1: '很差',
2: '不太满意',
3: '一般',
4: '比较满意',
5: '非常满意',
}
return map[score]
}
const getImageInfo = url =>
new Promise((resolve, reject) => {
wx.getImageInfo({
src: url,
success: res => {
resolve(res)
},
fail: e => {
reject(e)
},
})
})
const rpxToPx = size => {
const { windowWidth } = wx.getSystemInfoSync()
return windowWidth * size / 750
}
function drawCloseIcon() {
const ctx = wx.createCanvasContext('closeIcon')
ctx.setFillStyle('#ffffff')
ctx.drawImage(
iconPath.ICON_CANVAS_CLOSE,
rpxToPx(710) - rpxToPx(104),
15,
30,
30,
)
ctx.draw()
}
export const drawContactCard = (
teamMember,
logoUrl,
isWhiteBackground,
companyName,
) =>
new Promise((resolve, reject) => {
const ctx = wx.createCanvasContext('contactCard')
const { profile } = teamMember
// bg
ctx.setFillStyle('#ffffff')
ctx.fillRect(0, 0, rpxToPx(710), rpxToPx(950))
const { avatarUrl, roundAvatarUrl } = profile
const qrCodeUrl = teamMember.qrCode
const imageUrlList = [qrCodeUrl, roundAvatarUrl]
const promiseList = imageUrlList.map(url => getImageInfo(url))
Promise.all(promiseList)
.catch(() => {
showModal({
content: '图片加载失败,请稍后重试',
showCancel: false,
})
reject()
})
.then(([verifyQrCodeInfo, roundAvatarInfo]) => {
const verifyQrCodePath = verifyQrCodeInfo.path
const roundAvatarPath = roundAvatarInfo.path
const cardOrigin = {
x: rpxToPx(43),
y: rpxToPx(220),
}
const avatarData = {
w: rpxToPx(200),
h: rpxToPx(200),
r: rpxToPx(100), // 头像半径
}
const drawImage = (imagePath, beginX, beginY, width, height) => {
ctx.drawImage(imagePath, beginX, beginY, width, height)
}
// 画用户头像
drawImage(roundAvatarPath, 20, 30, avatarData.w, avatarData.h)
// 画用户信息
drawProfile()
// 画下方二维码
drawBottomBar()
ctx.draw()
resolve()
function drawProfile() {
const profileOrigin = {
m: rpxToPx(353), // 中点
l: rpxToPx(43), // 左起点
r: rpxToPx(650), // 右终点
y: rpxToPx(300), // 高度起点
}
const iconData = {
w: rpxToPx(36),
h: rpxToPx(36),
}
drawProfileBackground()
ctx.setFontSize(rpxToPx(30))
drawPhone()
if (profile.wechatAccount) {
drawWechatAccount()
}
if (profile.email) {
drawEmail()
}
function drawProfileBackground() {
// name
ctx.setFontSize(rpxToPx(40))
ctx.setFillStyle('#000000')
ctx.fillText(profile.name, 140, 70)
drawPosition()
if (companyName) {
drawCompany()
}
}
function drawPosition() {
// position 职位背景,icon,职位名称
ctx.setFontSize(rpxToPx(30))
const positionMetrics = ctx.measureText(profile.position).width
ctx.setFillStyle(
profile.color !== '#ffffff' ? profile.color : '#F4F4F4',
)
ctx.fillRect(140, 82, positionMetrics + 8, 24)
// position
ctx.setFillStyle(
profile.color === '#ffffff' ? '#000000' : '#ffffff',
)
ctx.fillText(profile.position, 144, 100)
}
function drawCompany() {
// company
ctx.setFillStyle('#000000')
const companyMetrics = ctx.measureText(companyName).width
ctx.fillText(companyName, 22, profileOrigin.y + rpxToPx(22))
// icon
drawImage(
iconPath.ICON_VERIFY,
companyMetrics + 24,
profileOrigin.y - 2,
15,
15,
)
}
function drawPhone() {
ctx.drawImage(
iconPath.ICON_CANVAS_PHONE,
profileOrigin.l,
profileOrigin.y + rpxToPx(158) - rpxToPx(20),
iconData.w,
iconData.h,
)
ctx.setFillStyle('#999999')
ctx.fillText(
'手机',
profileOrigin.l + iconData.w + rpxToPx(10),
profileOrigin.y + rpxToPx(165),
)
const phoneMetrics = ctx.measureText(profile.phone).width
ctx.setFillStyle('#666666')
ctx.fillText(
profile.phone,
profileOrigin.r - phoneMetrics,
profileOrigin.y + rpxToPx(165),
)
ctx.setStrokeStyle('#e8e8e8')
ctx.setLineWidth(0.5)
ctx.moveTo(profileOrigin.l, profileOrigin.y + rpxToPx(190))
ctx.lineTo(profileOrigin.r, profileOrigin.y + rpxToPx(190))
ctx.stroke()
}
function drawWechatAccount() {
ctx.drawImage(
iconPath.ICON_CANVAS_WECHAT,
profileOrigin.l,
profileOrigin.y + rpxToPx(82) - rpxToPx(20),
iconData.w,
iconData.h,
)
ctx.setFillStyle('#999999')
ctx.fillText(
'微信',
profileOrigin.l + iconData.w + rpxToPx(10),
profileOrigin.y + rpxToPx(89),
)
const wechatMetrics = ctx.measureText(profile.wechatAccount).width
ctx.setFillStyle('#666666')
ctx.fillText(
profile.wechatAccount,
profileOrigin.r - wechatMetrics,
profileOrigin.y + rpxToPx(89),
)
ctx.setStrokeStyle('#e8e8e8')
ctx.setLineWidth(0.5)
ctx.moveTo(profileOrigin.l, profileOrigin.y + rpxToPx(116))
ctx.lineTo(profileOrigin.r, profileOrigin.y + rpxToPx(116))
ctx.stroke()
}
function drawEmail() {
ctx.drawImage(
iconPath.ICON_CANVAS_EMAIL,
profileOrigin.l,
profileOrigin.y + rpxToPx(230) - rpxToPx(20),
iconData.w,
iconData.h,
)
ctx.setFillStyle('#999999')
ctx.fillText(
'邮箱',
profileOrigin.l + iconData.w + rpxToPx(10),
profileOrigin.y + rpxToPx(237),
)
const emailMetrics = ctx.measureText(profile.email).width
ctx.setFillStyle('#666666')
ctx.fillText(
profile.email,
profileOrigin.r - emailMetrics,
profileOrigin.y + rpxToPx(237),
)
}
}
function drawBottomBar() {
ctx.setFontSize(rpxToPx(32))
ctx.setFillStyle('#636972')
ctx.fillText('长按识别二维码', rpxToPx(67), rpxToPx(689))
ctx.fillText('进入小程序了解更多', rpxToPx(67), rpxToPx(744))
ctx.drawImage(
verifyQrCodePath,
rpxToPx(416),
rpxToPx(590),
rpxToPx(219),
rpxToPx(219),
)
ctx.drawImage(
roundAvatarPath,
rpxToPx(416) + rpxToPx(64),
rpxToPx(590) + rpxToPx(64),
rpxToPx(92),
rpxToPx(92),
)
}
drawCloseIcon()
})
.catch(() => {
showModal({
content: '图片渲染失败',
showCancel: false,
})
reject()
})
})
import { formatProtocol } from 'root/utils/helpers/ecommerceHelper'
import moment from 'wechat_common/lib/moment'
import compose from 'ramda/src/compose'
export function getQnUrl(image) {
if (image.url && image.url !== '!') {
return image.url
}
return `https://user-assets.sxlcdn.com/${image.storageKey}`
}
export function formattedPrice(price) {
if (price > 0) {
return Number(price / 100).toFixed(2)
} else {
return 0
}
}
export function getAmount(cart) {
let result = 0
for (const id in cart) {
result += cart[id].quantity * cart[id].price
}
return formattedPrice(result)
}
export function getQuantity(cart) {
let result = 0
for (const id in cart) {
result += cart[id].quantity
}
return result
}
export function getOrderItems(cart) {
const result = []
for (const id in cart) {
result.push({
id,
quantity: cart[id].quantity,
})
}
return result
}
export function formatPack(target) {
function addPacketName(packet) {
const { cards } = packet
const quantity = cards.length
if (quantity === 1) {
packet.name = `${cards[0].title}`
} else if (quantity > 1) {
packet.name = `${cards[0].title}等多份`
} else {
packet.name = ''
}
return packet
}
function fotmatPacketBackground(packet) {
packet.background = formatProtocol(getQnUrl(packet.background))
return packet
}
function _formatTime(time) {
return moment(time).format('YYYY年MM月DD日 HH:mm')
}
function formatPacketTime(packet) {
packet.created = _formatTime(packet.created)
packet.updated = _formatTime(packet.updated)
return packet
}
function formattedPackPrice(packet) {
packet.price = formattedPrice(packet.price)
return packet
}
return compose(
addPacketName,
fotmatPacketBackground,
formatPacketTime,
formattedPackPrice,
)(target)
}
export function getCardList(packet) {
const { cards } = packet
return cards.map(card => {
const newCard = {
cardId: card.cardId,
}
return newCard
})
}
import { getQnUrl } from 'wechat_common/utils/helpers/imageHelper'
export function getMixLayout(mix, layout, type, isMain) {
const index = isMain ? 0 : 1
if(Array.isArray(mix) && Array.isArray(layout) && mix[index] === type){
return layout[index]
}
return layout
}
// Chinese character、full Angle symbol takes up two widths
// English character、half - Angle symbol takes up one widths
export function cutString(str, width) {
const re = /[^\x00-\xff]/g
const result = []
let cnlen = 0
if (str.match(re)) {
cnlen = str.match(re).length
}
let strlen = Number(str.length) + cnlen
while (strlen > width) {
let tmplen = 0
let index = 0
for (let i = 0; i < str.length; i++) {
if (str[i].match(re)) {
tmplen += 2
} else {
tmplen++
}
if (tmplen > width) {
index = i
break
}
}
result.push(str.substring(0, index))
str = str.substring(index)
if (str.match(re)) {
cnlen = str.match(re).length
}
strlen = Number(str.length) + cnlen
}
result.push(str)
return result
}
// save the canvas as a image
export function saveCanvasImage(canvasId) {
wx.canvasToTempFilePath({
canvasId: canvasId,
success(res) {
const filePath = res.tempFilePath
wx.getSetting({
success(res) {
if (!res.authSetting['scope.writePhotosAlbum']) {
wx.authorize({
scope: 'scope.writePhotosAlbum',
success() {
wx.saveImageToPhotosAlbum({
filePath,
success(result) {
wx.showToast({
title: '保存成功',
icon: 'success',
duration: 2000,
})
},
fail(result) {
console.log(result)
},
})
},
fail() {
wx.showModal({
title: '提示',
content: '保存图片需要进行授权操作,请前往小程序设置页完成授权。',
showCancel: false,
})
},
})
} else {
wx.saveImageToPhotosAlbum({
filePath,
success(result) {
wx.showToast({
title: '保存成功',
icon: 'success',
duration: 2000,
})
},
})
}
},
})
},
})
}
// canvas draw image need use this api to get image local path
export const getImageInfo = function(url) {
return new Promise((resolve, reject) => {
wx.getImageInfo({
src: url,
success: res => resolve(res),
fail: e => reject(e),
})
})
}
export function parseUploadImage(upload) {
const uploadImage = []
const previewImage = []
if (upload) {
upload.forEach(image => {
uploadImage.push({
productId: image.productId,
hash: image.hash,
image: getQnUrl(image),
})
previewImage.push({
productId: image.productId,
url: getQnUrl(image)
})
})
}
return {
uploadImage,
previewImage,
}
}
export function extendMethod(...fns) {
return function(...args) {
fns.forEach(
fn => (typeof fn === 'function' ? fn.bind(this)(...args) : null),
)
}
}
...@@ -13,5 +13,5 @@ export function getQnUrl(image) { ...@@ -13,5 +13,5 @@ export function getQnUrl(image) {
return `https://user-assets.sxlcdn.com/${ return `https://user-assets.sxlcdn.com/${
image.storageKey image.storageKey
}?imageMogr2/strip/thumbnail/1200x9000>/format/${image.format}` }?imageMogr2/strip/thumbnail/1200x9000>/format/${image.format}`
} }
import wrappedWXTool from 'wechat_common/utils/wrappedWXTool'
import { setGlobalData } from 'root/reducers/ecommerce/global'
import { dispatch } from 'root/wmp-redux'
export default function login(cb) {
wrappedWXTool.login({
success(loginRes) {
const { code } = loginRes
dispatch(
setGlobalData({
code,
}),
)
if (cb && typeof cb === 'function') {
cb(loginRes)
}
wrappedWXTool.wxGetuserInfo().then(getInfoRes => {
dispatch(
setGlobalData({
userInfo: getInfoRes.userInfo,
}),
)
})
},
})
}
const resetItem = (item, typeMap) => {
item.label = item.label ? item.label : ''
if (item.label.match(/【.*】/)) {
item.label = item.label.match(/【.*】/)[0].replace(/【|】/g, '')
}
let path = item.subtype
if (item.type === 'ui' && item.subtype === 'sharePage') {
try {
const pathParam = JSON.parse(item.extraParam)
path = pathParam.path
for (const key in typeMap) {
const regEx = new RegExp(key)
if (regEx.test(path)) {
if (typeMap[key].isShare) {
return Object.assign(
{},
item,
{ isHiddenRadar: false },
typeMap[key],
)
} else {
return Object.assign(
{},
item,
{ isHiddenRadar: true },
typeMap[key],
)
}
}
}
} catch (err) {
throw new Error('pathParam parse error')
}
}
for (const key in typeMap) {
const regEx = new RegExp(key)
if (regEx.test(path)) {
return Object.assign(
{},
item,
{
isHiddenRadar: false,
isStayTime: typeMap[key].isStayTime,
},
typeMap[key],
)
}
}
return Object.assign({}, item, {
name: item.label,
isHiddenRadar: true,
})
}
export const formatRadar = item => {
// fix label value is null or `点赞了 【博客】`
item.label = item.label ? item.label : ''
const radarUiTypeShow = [
'chat',
'leaveContact',
'submitContactForm',
'likeBlogPost',
'likeContactCard',
'shareContactCard',
'sharePostPage',
'shareProductPage',
'shareImageBlogPost',
'shareImageProduct',
'saveAsImage',
'saveToContact',
'copyWechatId',
'viewQrcode',
'copyEmail',
'enterApp',
'leaveApp',
]
if (item.label.match(/【.*】/)) {
item.label = item.label.match(/【.*】/)[0].replace(/【|】/g, '')
}
const radarPvTypeShow = {
'productDetail/productDetail': {
pageName: '产品',
name: `【${item.label}】`,
highLight: true,
showTimes: true,
isShare: true,
isStayTime: true,
},
'product/product': {
pageName: '产品分类',
name: `【${item.label}】`,
highLight: true,
showTimes: true,
},
'postList/postList': {
pageName: '文章分类',
name: `【${item.label}】`,
highLight: true,
showTimes: true,
},
'postDetail/postDetail': {
pageName: '文章',
name: `【${item.label}】`,
highLight: true,
showTimes: true,
isShare: true,
isStayTime: true,
},
'presentation/index/index': {
pageName: '',
name: `首页`,
highLight: true,
showTimes: true,
isShare: false,
},
'contact/contact': {
isShare: true,
pageName: '您的',
name: `名片`,
highLight: true,
showTimes: true,
isHiddenRadar: true,
},
}
// fix the pv include blog's and product's catgary and detail
if (item.type === 'pv') {
item = resetItem(item, radarPvTypeShow)
} else if (item.type === 'ui') {
if (radarUiTypeShow.indexOf(item.subtype) > -1) {
item.name = `【${item.label}】`
item.isHiddenRadar = false
} else if (item.subtype === 'sharePage') {
item = resetItem(item, radarPvTypeShow)
} else {
item.isHiddenRadar = true
}
}
// the personalized push text
if (item.subtype === 'chat') {
item.welcomeText = item.repeatTimes >= 2 ? '' : '快去回复吧'
} else if (item.subtype === 'submitContactForm') {
item.welcomeText = item.repeatTimes >= 2 ? '' : '快去看看他说了什么吧'
} else if (item.subtype === 'likeContactCard') {
item.welcomeText = item.repeatTimes >= 2 ? '' : '似乎对您有好感,快联系ta吧'
} else if (item.subtype === 'viewContactCard') {
item.showTimes = true
item.welcomeText =
item.repeatTimes >= 2 ? '看来成交在望,快联系ta吧!' : '初识在此刻'
} else if (item.subtype === 'shareContactCard') {
item.showTimes = true
item.welcomeText =
item.repeatTimes >= 2
? '看来成交在望,快联系ta吧!'
: '您的人脉正在扩散!'
} else if (item.subtype === 'saveAsImage') {
item.welcomeText =
item.repeatTimes >= 2 ? '' : '近期可能会联系您,密切关注哦'
} else if (item.subtype === 'copyWechatId') {
item.welcomeText = item.repeatTimes >= 2 ? '' : '可能会加您好友,请留意哦'
} else if (item.subtype === 'shareImageProduct') {
item.welcomeText =
item.repeatTimes >= 2
? '看来成交在望,快联系ta吧'
: '看来还挺感兴趣的,快联系ta吧'
} else if (item.subtype === 'copyEmail') {
item.welcomeText =
item.repeatTimes >= 2 ? '' : '可能会发送邮件给您,请留意哦'
} else if (item.subtype === 'sharePage') {
let path
try {
const pathParam = JSON.parse(item.extraParam)
path = pathParam.path
} catch (err) {
throw new Error('pathParam parse error')
}
if (path.includes('contact/contact')) {
item.welcomeText =
item.repeatTimes >= 2
? '看来成交在望,快联系ta吧!'
: '您的人脉正在扩散!'
}
} else if (item.subtype === 'likeBlogPost') {
item.welcomeText = item.repeatTimes >= 2 ? '' : '看来很认可呢,要不要联系下'
} else if (item.type === 'pv') {
if (item.subtype.includes('postList?category')) {
item.welcomeText =
item.repeatTimes >= 2
? '看来成交在望,快联系ta吧'
: '看来对这类文章还挺感兴趣的,快联系ta介绍更多吧'
} else if (item.subtype.includes('product?category')) {
item.welcomeText =
item.repeatTimes >= 2
? '看来成交在望,快联系ta吧'
: '看来对这类产品还挺感兴趣的,快联系ta介绍更多吧'
} else if (item.subtype.includes('postId')) {
item.welcomeText =
item.repeatTimes >= 2
? '看来成交在望,快联系ta吧'
: '看来对公司还挺感兴趣的,快联系ta吧'
} else if (item.subtype.includes('productId')) {
item.welcomeText =
item.repeatTimes >= 2
? '看来成交在望,快联系ta吧'
: '看来还挺感兴趣的,快联系ta吧'
} else if (item.subtype.includes('presentation/index/index')) {
item.isStayTime = true
item.welcomeText =
item.repeatTimes >= 2
? '看来对公司还挺感兴趣的,快联系ta吧'
: '这是个重要情报哦'
} else if (item.subtype.includes('contact/contact')) {
item.isStayTime = true
item.welcomeText =
item.repeatTimes >= 2 ? '看来成交在望,快联系ta吧' : '初识在此刻'
} else if (item.subtype.includes('businessCard/businessCard')) {
item.highLight = false
item.pageName = '您的'
item.name = '名片夹'
item.showTimes = true
}
}
if (item.subtype === 'leaveContact') {
const str1 = item.label.split(',')[0]
const str2 = str1.split('留下了电话:')
item.phone = str2[1]
item.name = str2[0]
item.nickname = str2[0]
}
if (item.subtype === 'shareProductPage') {
item.welcomeText =
item.repeatTimes >= 2
? '看来成交在望,快联系ta吧!'
: '看来还挺感兴趣的,快联系ta吧'
}
if (item.subtype === 'sharePostPage') {
item.welcomeText =
item.repeatTimes >= 2
? '看来成交在望,快联系ta吧!'
: '看来还挺感兴趣的,快联系ta吧'
}
const hourTime = parseInt(item.dwellTime / 3600)
? `${parseInt(item.dwellTime / 3600)}小时`
: ``
const minuteTime = parseInt((item.dwellTime % 3600) / 60)
? `${parseInt((item.dwellTime % 3600) / 60)}分钟`
: ``
const secondTime = `${
(item.dwellTime % 3600) % 60 && item.type === 'pv'
? (item.dwellTime % 3600) % 60
: (item.dwellTime % 3600) % 60 + 1
}秒`
item.dwellTime = hourTime + minuteTime + secondTime
return item
}
import {doGet, dePostWithPromise} from 'wechat_common/utils/request'
export default function REQUEST(options, bindType) {
switch (options.method) {
case "GET":
return bindType ? doGet(bindType(options)) : doGet(options)
case "POST":
return bindType ? dePostWithPromise(bindType(options)) : dePostWithPromise(options)
default :
return bindType ? doGet(bindType(options)) : doGet(options)
}
}
const lastStateContainer = {}
function _observable(store, keyPath, callback) {
return store.subscribe(() => {
const newState = store.getState().getIn(keyPath)
const keyPathStr = keyPath.join('')
if (lastStateContainer[keyPathStr] !== newState) {
const _lastState = lastStateContainer[keyPathStr]
lastStateContainer[keyPathStr] = newState
callback(newState, _lastState)
}
})
}
export default function createObserveble(store, keyPath) {
let subscribeId = 0
return {
subscribe: callback => {
subscribeId = _observable(store, keyPath, callback)
},
unSubscribe: () => {
store.unsubscribe(subscribeId)
},
}
}
import moment from 'wechat_common/lib/moment'
import { getQnUrl } from 'wechat_common/utils/helpers/imageHelper'
export function reducerHelper(state, action, handlers) {
const handler = handlers[action.type]
return handler ? handler(state, action) : state
}
export function formatFactory(method, key = 'url') {
let assertError = ''
if (typeof method !== 'function') {
assertError = new TypeError(
`method pass into formatFactory must be a function, but get ${typeof method}`,
)
}
return function(obj) {
if (assertError) {
throw assertError
}
return Object.assign({}, obj, {
[key]: method(obj[key]),
})
}
}
export function formatProtocol(url) {
const _url = url.replace('http:', '').replace('https:', '')
return `https:${_url}`
}
// 判断十六进制色值是浅色还是深色 return true: 浅色,false:深色
const isLightFunc = hexColor => {
const color = hexColor.toLowerCase()
const sColorChange = []
for (let i = 1; i < 7; i += 2) {
sColorChange.push(parseInt(`0x${color.slice(i, i + 2)}`, 0))
}
if (
sColorChange[0] * 0.299 +
sColorChange[1] * 0.578 +
sColorChange[2] * 0.114 >=
192
) {
return true
} else {
return false
}
}
export function isWhite(color) {
if (
!color ||
color === '' ||
color === '#fff' ||
color === '#ffffff' ||
color === 'white'
) {
return true
} else if (color === 'black') {
return false
}
return isLightFunc(color)
}
export function getDateDiff(dateTimeStamp) {
const monthCount = moment().diff(dateTimeStamp, 'months')
const weekCount = moment().diff(dateTimeStamp, 'weeks')
const dayCount = moment().diff(dateTimeStamp, 'days')
const hourCount = moment().diff(dateTimeStamp, 'hours')
const minCount = moment().diff(dateTimeStamp, 'minutes')
let result = ''
if (monthCount >= 1) {
result = `${String(parseInt(monthCount, 0))}月前`
} else if (weekCount >= 1) {
result = `${String(parseInt(weekCount, 0))}周前`
} else if (dayCount >= 1) {
result = `${String(parseInt(dayCount, 0))}天前`
} else if (hourCount >= 1) {
result = `${String(parseInt(hourCount, 0))}小时前`
} else if (minCount >= 1) {
result = `${String(parseInt(minCount, 0))}分钟前`
} else {
result = '刚刚'
}
return result
}
export function validatePhoneNumber(phoneNumer) {
return /^1[34578]\d{9}$/.test(phoneNumer)
}
export function formatAmount(amount) {
return (Number(amount) / 100).toFixed(2)
}
export function formatGrouponStatus(status) {
switch (status) {
case 'refund_pending':
return { name: '待退款', color: '#fb7d2b', type: 'effective' }
case 'enabled':
return { name: '未使用', color: '#93b719', type: 'effective' }
case 'used':
return { name: '已核销', color: '#9298a0', type: 'used' }
case 'refunded':
return { name: '已退款', color: '#9298a0', type: 'refunded' }
case 'expired':
return { name: '已过期', color: '#9298a0', type: 'expired' }
case 'disabled':
return { name: '已失效', color: '#e64751', type: 'disabled' }
default:
return ''
}
}
// parse the comment pictures and split comment to show or hide.
export function updateComment(comments) {
let lessCommentImg = []
let moreCommentImg = []
let lessComments = []
let holdPictures = []
let newComments = []
if (comments) {
comments.forEach(value => {
if (value.content && value.content.length > 82) {
value.shortContent = `${value.content.substring(0, 82)}...`
}
value.createdAt = moment(value.createdAt)
.locale('zh-cn')
.fromNow()
if (value.pictures && value.pictures.length > 0) {
value.pictures.map(img => {
if (img.image) {
holdPictures.push(img.image)
} else {
holdPictures.push(getQnUrl(img))
}
})
value.pictures = holdPictures
holdPictures = []
} else {
value.pictures = []
}
if (value.pictures && value.pictures.length > 3) {
lessCommentImg = value.pictures.slice(0, 2)
moreCommentImg = value.pictures.slice(2, 3)
} else if (value.pictures && value.pictures.length <= 3) {
lessCommentImg = value.pictures
}
value.lessCommentImg = lessCommentImg || []
value.moreCommentImg = moreCommentImg || []
})
newComments = [...comments]
lessComments = newComments.slice(0, 1)
}
return {
comments: newComments,
lessComments,
}
}
export function compare(property) {
return (a, b) => {
const value1 = a[property]
const value2 = b[property]
return value2 - value1
}
}
export function simpleString(str = '', length = 5) {
return str.length > length ? `${str.substr(0, length)}...` : str
}
/*
* return the main region of a detailed address
* @param {string} addr detailed address
* @return {string}
*/
export const getRegionFromAddress = addr => {
const regEx = /^(.+[省|自治区])?(.+[地区|自治州|市])?(.+[县|区])?/
const rslt = (addr || '').match(regEx)
return rslt || ['']
}
/*
* get the type of the input value
* @param {string} addr detailed address
* @return {string}
*/
export const getType = param => {
if (Array.isArray(param)) {
return 'array'
}
if (param instanceof Date) {
return 'date'
}
if (param === null) {
return 'null'
}
if (param instanceof RegExp) {
return 'regExp'
}
if (param instanceof Error) {
return 'error'
}
return typeof param
}
/*
* compare the first input value to another,
* return a boolean value if they are as same as each other
* @param {Object} a the first object which would be used to compare to another
* @return {Object} b another object
*/
export const isCongruence = (a, b) => {
const typeFirst = getType(a)
if (typeFirst !== getType(b)) {
return false
}
const TYPE_METHODS_MAP = {
array: (x, y) => {
if (x === y) {
return true
}
if (x.length !== y.length) {
return false
}
const ifEqual = (pre, nex) => {
const compareFirst = isCongruence(pre[0], nex[0])
if (pre.length <= 1) {
return compareFirst
}
return compareFirst && TYPE_METHODS_MAP.array(pre.slice(1), nex.slice(1))
}
return ifEqual(x, y)
},
function: (x, y) => {
if (x === y) {
return true
}
return String(x) === String(y)
},
object: (x, y) => {
if (x === y) {
return true
}
const xKeys = Object.keys(x)
const yKeys = Object.keys(y)
if (xKeys.length !== yKeys.length) {
return false
}
const ifKeysEqual = TYPE_METHODS_MAP.array(xKeys, yKeys)
const ifEqual = keys => {
if (keys.length <= 0) {
return true
}
const compareFirst = isCongruence(x[keys[0]], y[keys[0]])
if (keys.length <= 1) {
return compareFirst
}
return compareFirst && ifEqual(keys.slice(1))
}
return ifKeysEqual && ifEqual(xKeys)
},
otherwise: (x, y) => x === y,
}
return (TYPE_METHODS_MAP[typeFirst] || TYPE_METHODS_MAP.otherwise)(a, b)
}
/**
* This middleware can only work in wmp
* @returns {undefined}
*/
export function errorModalMiddleware({ getState }) {
return next => action => {
next(action)
const state = getState()
const { message } = state.getIn(['error']).toJS()
if (message) {
wx.showModal({
content: message,
showCancel: false,
})
}
}
}
export function debounce(fn, time = 100) {
let handle
handle = 0
return function () {
let args, context
context = this
args = arguments
clearTimeout(handle)
return (handle = setTimeout(() => fn.apply(context, args), time))
}
}
export function formatProtocol(url) {
if (!url) {
return 'https://assets.sxlcdn.com/images/ecommerce/ecommerce-default-image.png'
}
const _url = url.replace('http:', '').replace('https:', '')
return `https:${_url}`
}
export function formatAmount(amount) {
// e.g. turn 1234 into 1.2k
const result = amount / 1000
if (result > 1) {
return `${Math.round(result * 10) / 10}k`
} else {
return amount
}
}
export function customNavigateTo(data) {
setTimeout(() => {
wx.navigateTo(data)
}, 50)
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment