Commit 82b112b0 by 姜雷

修改意见反馈功能

parent ff7a7135
......@@ -20,21 +20,21 @@
"author": "",
"license": "MIT",
"dependencies": {
"@tarojs/async-await": "1.3.4",
"@tarojs/components": "1.3.4",
"@tarojs/redux": "1.3.4",
"@tarojs/redux-h5": "1.3.4",
"@tarojs/router": "1.3.4",
"@tarojs/taro": "1.3.4",
"@tarojs/taro-alipay": "1.3.4",
"@tarojs/taro-h5": "1.3.4",
"@tarojs/taro-swan": "1.3.4",
"@tarojs/taro-tt": "1.3.4",
"@tarojs/taro-weapp": "1.3.4",
"@tarojs/async-await": "1.3.14",
"@tarojs/components": "1.3.14",
"@tarojs/redux": "1.3.14",
"@tarojs/redux-h5": "1.3.14",
"@tarojs/router": "1.3.14",
"@tarojs/taro": "1.3.14",
"@tarojs/taro-alipay": "1.3.14",
"@tarojs/taro-h5": "1.3.14",
"@tarojs/taro-swan": "1.3.14",
"@tarojs/taro-tt": "1.3.14",
"@tarojs/taro-weapp": "1.3.14",
"crypto-js": "^3.1.9-1",
"jsbarcode": "^3.11.0",
"nerv-devtools": "^1.4.3",
"nervjs": "^1.4.3",
"nerv-devtools": "^1.4.4",
"nervjs": "^1.4.4",
"npm": "^6.8.0",
"redux": "^4.0.0",
"redux-logger": "^3.0.6",
......@@ -42,11 +42,11 @@
"wxbarcode": "^1.0.2"
},
"devDependencies": {
"@tarojs/plugin-babel": "1.3.4",
"@tarojs/plugin-csso": "1.3.4",
"@tarojs/plugin-sass": "1.3.4",
"@tarojs/plugin-uglifyjs": "1.3.4",
"@tarojs/webpack-runner": "1.3.4",
"@tarojs/plugin-babel": "1.3.14",
"@tarojs/plugin-csso": "1.3.14",
"@tarojs/plugin-sass": "1.3.14",
"@tarojs/plugin-uglifyjs": "1.3.14",
"@tarojs/webpack-runner": "1.3.14",
"@types/react": "^16.4.8",
"@types/webpack-env": "^1.13.6",
"babel-eslint": "^8.2.3",
......@@ -57,10 +57,10 @@
"babel-preset-env": "^1.6.1",
"cross-env": "^5.2.0",
"eslint": "^4.19.1",
"eslint-config-taro": "1.3.4",
"eslint-config-taro": "1.3.14",
"eslint-plugin-import": "^2.12.0",
"eslint-plugin-react": "^7.8.2",
"eslint-plugin-taro": "1.3.4",
"eslint-plugin-taro": "1.3.14",
"eslint-plugin-typescript": "^0.12.0",
"typescript": "^3.0.1"
}
......
import { customerFetch, ResponseDataEntity } from '.';
type FetchFeedbackListParams = {
pageNum: number;
customerId: number;
pageSize: number;
lastId?: number;
};
type CustomerFeedbackDetailVo = {
feedbackId: number;
replayAt: string;
replayContent: string;
};
export type FeedbackItem = {
content: string;
createDate: string;
id: number;
replayList: CustomerFeedbackDetailVo[];
};
export const fetchFeedbackList = (
params: FetchFeedbackListParams,
): Promise<ResponseDataEntity<FeedbackItem[]>> =>
customerFetch({
url: '/dcxy/customerFeedback/customerFeedback',
data: params,
});
type AddFeedbackParams = {
content: string;
customerId: number; // 会员id
deviceInfo?: string;
state?: number;
};
export const addFeedback = (params: AddFeedbackParams) =>
customerFetch({
url: '/dcxy/customerFeedback/customerFeedback',
method: 'POST',
data: params,
});
import Taro, { useEffect, useState } from '@tarojs/taro';
const useButtonPadding = () => {
const [state, setState] = useState(0);
useEffect(() => {
const res = Taro.getSystemInfoSync();
if (/iPhone X(\w)?/.test(res.model)) {
console.log('is iphone X');
setState(34);
}
}, []);
return state;
};
export default useButtonPadding;
import { useReducer } from '@tarojs/taro';
import Actions from '@/types/Store/Actions';
type Pagination = {
pageNum: number;
pageSize: number;
total: number;
};
const initState: Pagination = {
pageNum: 1,
pageSize: 10,
total: 0,
};
const reducer = (state: Pagination, action: Actions): Pagination => {
switch (action.type) {
case 'updatePagination':
return { ...state, ...action.payload };
default:
return state;
}
};
const usePagination = (
initData?: Pagination,
): [Pagination, (e: Pagination) => void] => {
const [state, dispatch] = useReducer(reducer, { ...initState, ...initData });
const updatePagination = (e: Pagination) => {
dispatch({ type: 'updatePagination', payload: e });
};
return [state, updatePagination];
};
export default usePagination;
.Feedback {
box-sizing: border-box;
position: relative;
flex-direction: column;
height: 100%;
background-color: #eee;
.Feedback-input {
border-top: 1px solid #eee;
background-color: #fff;
background-color: #fff;
padding-bottom: 112px;
.Feedback-Content-wrap {
// box-sizing: border-box;
height: 100%;
overflow-y: auto;
.Feedback-Content {
// height: 100%;
}
}
.Feedback-Footer {
position: fixed;
bottom: 0;
box-sizing: border-box;
padding: 20px;
display: flex;
width: 100%;
height: 468px;
line-height: 50px;
font-size: 28px;
}
.Feedback-btn {
margin: 40px 32px 0;
padding: 0 32px;
height: 112px;
align-items: center;
.input-placeholder {
font-size: 28px;
color: #d4d7fd;
}
.Feedback-Footer-Input {
background-color: #8c95fa;
border-radius: 16px;
flex: 1;
height: 92px;
line-height: 92px;
color: #fff;
padding-left: 40px;
}
.Feedback-Footer-Btn {
width: 128px;
margin-left: 20px;
background-color: #ffab3e;
border-radius: 16px;
font-size: 28px;
height: 92px;
line-height: 92px;
}
}
}
import { ComponentClass } from 'react';
import Taro, { Component, Config } from '@tarojs/taro';
import { View, Textarea, Button } from '@tarojs/components';
import { fetchFeedback } from '../../api/customer';
import { connect } from '@tarojs/redux';
import Taro, {
useState,
useEffect,
useShareAppMessage,
clientRectElement,
} from '@tarojs/taro';
import { View, Button, Input, ScrollView } from '@tarojs/components';
import { useSelector } from '@tarojs/redux';
import './Feedback.scss';
import { shareHandle } from '@/common/shareMethod';
import useInputValue from '@/hooks/useInputValue';
import { Customer } from '@/types/Customer/Customer';
import useFeedbackList from './useFeedbackList';
import { addFeedback } from '@/api/feedback';
import useButtonPadding from '@/hooks/useButtonPadding';
import FeedbackItem from './components/FeedbackItem/FeedbackItem';
import { formatDate } from '@/utils/time';
type PageProps = {
areaId: number;
areaName: string;
customerId: number;
customerName: string;
customerPhone: string;
};
type PageState = {
feedbackContent: string;
};
interface Feedback {
props: PageProps;
state: PageState;
}
@connect(({ userinfo }) => ({
areaId: userinfo.areaId,
areaName: userinfo.areaName,
customerId: userinfo.customerId,
customerName: userinfo.customerName,
customerPhone: userinfo.customerPhone,
}))
class Feedback extends Component<PageProps, PageState> {
config: Config = {
navigationBarTitleText: '意见反馈',
};
constructor(props) {
super(props);
this.state = {
feedbackContent: '',
};
}
onShareAppMessage = shareHandle;
feedbackHandle() {
function Feedback() {
useShareAppMessage(shareHandle);
const { value, onChange } = useInputValue('');
const userInfo = useSelector((state: any): Customer => state.userinfo);
const { list, fetchListHandle, pushNewMsg } = useFeedbackList(
userInfo.customerId,
);
const bottomHeight = useButtonPadding();
const [showKeyboard, setShowKeyboard] = useState(false);
const addFeedbackHandle = () => {
Taro.showLoading();
const { feedbackContent } = this.state;
if (feedbackContent && feedbackContent.length >= 5) {
const {
areaId,
areaName,
customerId,
customerName,
customerPhone,
} = this.props;
fetchFeedback({
areaId,
areaName,
customerId,
customerName,
customerPhone,
feedbackContent,
if (value) {
addFeedback({
content: value,
customerId: userInfo.customerId,
})
.then(res => {
Taro.hideLoading();
console.log(res);
Taro.showToast({
title: '反馈成功',
icon: 'success',
onChange({
type: '',
detail: { value: '', cursor: 0, keyCode: 0 },
timeStamp: 0,
target: this,
currentTarget: this,
preventDefault: () => {},
stopPropagation: () => {},
});
pushNewMsg({
id: 0,
content: value,
createDate: formatDate(new Date()),
replayList: [],
});
setTimeout(() => {
Taro.navigateBack();
}, 2000);
})
.catch(err => {
Taro.hideLoading();
......@@ -85,29 +66,80 @@ class Feedback extends Component<PageProps, PageState> {
icon: 'none',
});
}
}
render() {
return (
<View className='Feedback'>
<Textarea
className='Feedback-input'
value={this.state.feedbackContent}
};
const [scrollTop, setScrollTop] = useState(0);
useEffect(() => {
wx.onKeyboardHeightChange(res => {
console.log(res.height);
if (res.height) {
setShowKeyboard(true);
} else {
setShowKeyboard(false);
}
});
}, []);
useEffect(() => {
Taro.createSelectorQuery()
.select('.Feedback-Content')
.boundingClientRect((rect: clientRectElement) => {
console.log(rect);
if (rect) setScrollTop(rect.height);
})
.exec();
}, [list.length]);
const scrollToUpperHandle = () => {
if (list.length) {
fetchListHandle(list[0].id);
}
};
return (
<View
className='Feedback'
style={`padding-bottom: ${Taro.pxTransform(112 + bottomHeight)}`}>
<ScrollView
className='Feedback-Content-wrap'
scrollY
scrollWithAnimation
upperThreshold={-40}
onScrollToUpper={scrollToUpperHandle}
scrollTop={scrollTop}>
<View className='Feedback-Content'>
{list.map(item => (
<FeedbackItem
key={item.id}
msgData={item}
userData={{
customerHead: userInfo.customerHead,
customerSex: userInfo.customerSex,
}}
/>
))}
</View>
</ScrollView>
<View
className='Feedback-Footer'
style={`bottom: ${Taro.pxTransform(bottomHeight)}`}>
<Input
className='Feedback-Footer-Input'
value={value}
onInput={onChange}
placeholder='反馈问题不超过50字'
maxlength={50}
onInput={({ detail: { value } }) => {
this.setState({
feedbackContent: value.trim(),
});
return value.trim();
}}
maxLength={50}
confirmType='send'
onConfirm={addFeedbackHandle}
/>
<Button className='Feedback-btn' onClick={this.feedbackHandle}>
提交
</Button>
{!showKeyboard && (
<Button className='Feedback-Footer-Btn' onClick={addFeedbackHandle}>
发送
</Button>
)}
</View>
);
}
</View>
);
}
export default Feedback as ComponentClass<PageProps, PageState>;
Feedback.config = {
navigationBarTitleText: '意见反馈',
};
export default Feedback;
import { View } from '@tarojs/components';
import Message, { MessageType } from '../Message/Message';
import { FeedbackItem } from '@/api/feedback';
const FeedbackItem = ({
msgData,
userData,
}: {
msgData: FeedbackItem;
userData: {
customerHead: null | string;
customerSex: string;
};
}) => {
return (
<View className='FeedbackItem'>
{msgData && (
<Message
type={MessageType.user}
content={msgData.content}
time={msgData.createDate}
userData={userData}
/>
)}
{msgData &&
msgData.replayList.map(replay => (
<Message
key={replay.feedbackId}
type={MessageType.replay}
content={replay.replayContent}
time={replay.replayAt}
userData={userData}
/>
))}
</View>
);
};
export default FeedbackItem;
.Message {
display: flex;
margin: 10px 32px;
&.user {
justify-content: flex-end;
.Message-Content {
background-color: #8c95fa;
border-radius: 20px 0 20px 20px;
color: #fff;
}
.Message-Content-time {
text-align: right;
}
}
&.replay {
.Message-Content {
background-color: #f7f7f7;
border-radius: 0 20px 20px 20px;
color: #333;
}
}
.Message-UserHeader {
width: 80px;
height: 80px;
.Message-UserHeader-img {
width: 100%;
height: 100%;
}
}
.Message-Content {
box-sizing: border-box;
width: 486px;
margin: 40px 20px 0;
padding: 20px;
font-size: 28px;
.Message-Content-time {
margin-top: 12px;
font-size: 24px;
}
}
}
import { View, Image } from '@tarojs/components';
import BoyIcon from '../../../../images/home/bg_touxiang_boy@2x.png';
import GirlIcon from '../../../../images/home/bg_touxiang_girl@2x.png';
import ReplayIcon from '../../../../images/home/ic_fankui_kefu@2x.png';
import './Message.scss';
import { CustomerSex } from '@/types/Customer/Customer';
export enum MessageType {
user = 'user',
replay = 'replay',
}
const Message = ({
type,
content,
time,
userData,
}: {
type: MessageType;
content: string;
time: string;
userData: {
customerHead: null | string;
customerSex: string;
};
}) => {
return (
<View
className={`Message ${type === MessageType.user ? 'user' : 'replay'}`}>
{type === MessageType.replay && (
<View className='Message-UserHeader'>
<Image className='Message-UserHeader-img' src={ReplayIcon} />
</View>
)}
<View className='Message-Content'>
<View className='Message-Content-msg'>{content}</View>
<View className='Message-Content-time'>{time}</View>
</View>
{type === MessageType.user && (
<View className='Message-UserHeader'>
{userData.customerHead ? (
<Image
className='Message-UserHeader-img'
src={userData.customerHead}
/>
) : userData.customerSex === CustomerSex.boy ? (
<Image className='Message-UserHeader-img' src={BoyIcon} />
) : (
<Image className='Message-UserHeader-img' src={GirlIcon} />
)}
</View>
)}
</View>
);
};
export default Message;
import Taro, { useEffect, useState, useReducer } from '@tarojs/taro';
import { fetchFeedbackList, FeedbackItem } from '@/api/feedback';
import usePagination from '@/hooks/usePagination';
import Actions from '@/types/Store/Actions';
const reducer = (state: FeedbackItem[], action: Actions): FeedbackItem[] => {
switch (action.type) {
case 'getMoreList':
return [...action.payload, ...state];
case 'refreshList':
return action.payload;
case 'addNewMessage':
return [...state, action.payload];
default:
return state;
}
};
const initState: FeedbackItem[] = [];
const useFeedbackList = (customerId: number) => {
const [lastId, setLastId] = useState(0);
const [pagination, setPaination] = usePagination();
const fetchListHandle = (lastId: number) => setLastId(lastId);
const [feedbackList, dispatch] = useReducer(reducer, initState);
useEffect(() => {
Taro.showLoading();
fetchFeedbackList(
lastId
? {
pageNum: pagination.pageNum + 1,
pageSize: pagination.pageSize,
customerId,
lastId,
}
: {
pageNum: 1,
pageSize: pagination.pageSize,
customerId,
},
)
.then(res => {
console.log(res);
if (res.data.length) {
if (lastId) {
dispatch({
type: 'getMoreList',
payload: res.data,
});
setPaination({ ...pagination, pageNum: pagination.pageNum + 1 });
} else {
dispatch({
type: 'refreshList',
payload: res.data,
});
}
}
Taro.hideLoading();
})
.catch(err => {
Taro.hideLoading();
Taro.showToast({
title: err.msg || '请求失败',
icon: 'none',
});
});
}, [lastId]);
const pushNewMsg = (msgData: FeedbackItem) =>
dispatch({
type: 'addNewMessage',
payload: msgData,
});
return { list: feedbackList, fetchListHandle, pushNewMsg };
};
export default useFeedbackList;
export enum CustomerSex {
boy = '1',
girl = '0',
}
export type Customer = {
areaId: number;
areaName: string;
......@@ -8,7 +12,7 @@ export type Customer = {
customerId: number;
customerName: string;
customerPhone: string;
customerSex: string;
customerSex: CustomerSex;
customerType: string;
email: string;
entranceDate: string;
......
export const formatDate = (date: Date, fmt = 'yyyy-MM-dd hh:mm:ss'): string => {
if (!date) {
return '无';
}
if (typeof date === 'string') {
date = new Date(date);
// date = new Date(date.replace('-', '/'));
}
if (/(y+)/.test(fmt)) {
fmt = fmt.replace(
RegExp.$1,
(date.getFullYear() + '').substr(4 - RegExp.$1.length),
);
}
let o = {
'M+': date.getMonth() + 1,
'd+': date.getDate(),
'h+': date.getHours(),
'm+': date.getMinutes(),
's+': date.getSeconds(),
};
for (let k in o) {
if (new RegExp(`(${k})`).test(fmt)) {
let str = o[k] + '';
fmt = fmt.replace(
RegExp.$1,
RegExp.$1.length === 1 ? str : ('00' + str).substr(str.length),
);
}
}
return fmt;
};
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