@@ -6,5 +6,6 @@ module.exports = { | |||
framework: 'react', | |||
ts: false | |||
}] | |||
] | |||
], | |||
plugins: ['./scripts/babel-plugin-jsx-classnames-advanced'], | |||
} |
@@ -55,6 +55,7 @@ | |||
}, | |||
"devDependencies": { | |||
"@babel/core": "^7.8.0", | |||
"@babel/helper-module-imports": "^7.14.5", | |||
"@tarojs/mini-runner": "3.2.10", | |||
"@tarojs/webpack-runner": "3.2.10", | |||
"@types/react": "^17.0.2", | |||
@@ -0,0 +1,66 @@ | |||
const { addDefault } = require('@babel/helper-module-imports'); | |||
const defaultOpts = { | |||
nameHint: '_babel_plugin_jsx_classnames_advanced', | |||
attributeNames: [ | |||
'className', | |||
'dropdownClassName', | |||
'wrapperClassName', | |||
'wrapClassName', | |||
'overlayClassName', | |||
], | |||
ignoreMemberExpression: true, | |||
ignoreIdentifier: true, | |||
}; | |||
function f({ types: t }) { | |||
function replaceNode(path, callee) { | |||
path.node.value = t.JSXExpressionContainer( | |||
t.callExpression( | |||
callee, | |||
[path.node.value.expression], | |||
), | |||
); | |||
} | |||
const visitor = { | |||
JSXAttribute(path, state) { | |||
const { | |||
attributeNames, nameHint, ignoreIdentifier, ignoreMemberExpression, | |||
} = { ...defaultOpts, ...state.opts }; | |||
if ( | |||
!attributeNames.some(i => t.isJSXIdentifier(path.node.name, { name: i })) || | |||
!t.isJSXExpressionContainer(path.node.value) || | |||
t.isStringLiteral(path.node.value.expression) || | |||
(t.isMemberExpression(path.node.value.expression) && ignoreMemberExpression) || | |||
(t.isIdentifier(path.node.value.expression) && ignoreIdentifier) | |||
) return; | |||
if (nameHint === false) { | |||
replaceNode(path, addDefault(path, 'classnames', { nameHint: defaultOpts.nameHint })); | |||
return; | |||
} | |||
if (!state.isImported) { | |||
state.importedIndentifier = addDefault(path, 'classnames', { nameHint }); | |||
replaceNode(path, state.importedIndentifier); | |||
state.isImported = 1; | |||
} else { | |||
replaceNode(path, t.identifier(state.importedIndentifier.name)); | |||
} | |||
}, | |||
}; | |||
return { | |||
visitor, | |||
}; | |||
} | |||
module.exports = f; | |||
module.exports.defaultOpts = defaultOpts; |
@@ -3,7 +3,6 @@ import getData from '@root/utils/request'; | |||
import Taro from '@tarojs/taro'; | |||
import { isReqSuccess } from '@root/utils/tool'; | |||
import { firstCharToLowerCase } from '../utils/tool'; | |||
import { getFileUrl } from '../service/oss'; | |||
async function saveSafeArea(dispatch) { | |||
const res = await Taro.getSystemInfo(); | |||
@@ -17,11 +16,8 @@ async function saveSafeArea(dispatch) { | |||
} | |||
} | |||
const hint = (msg) => Taro.showToast({ title: msg, icon: 'none', duration: 2000 }) | |||
export function logout() { | |||
return async (dispatch) => { | |||
storage.remove('token'); | |||
dispatch({ | |||
type: 'app/logout', | |||
}); | |||
@@ -37,7 +33,7 @@ export function initApp() { | |||
export function login({ username, password }) { | |||
return async (dispatch, getState) => { | |||
const res = await getData('authentication/login', { UserName: username, Password: password }); | |||
const res = await getData('authentication/login', { UserName: username, Password: password }, true); | |||
if (!isReqSuccess(res)) { | |||
dispatch(logout()); | |||
return res; | |||
@@ -51,16 +47,17 @@ export function login({ username, password }) { | |||
// dispatch(logout()); | |||
// return false; | |||
// } | |||
console.log(getFileUrl(userData.headImgUrl)); | |||
const payload = { | |||
accountId: userData.id, | |||
accountName: userData.cnName, | |||
customerId: userData.companyId, | |||
avatorUrl: userData.headImgUrl, | |||
}; | |||
storage.set('accountId', payload.accountId); | |||
dispatch({ | |||
type: 'app/login', | |||
payload: { | |||
accountId: userData.id, | |||
accountName: userData.cnName, | |||
customerId: userData.companyId, | |||
avatorUrl: userData.headImgUrl, | |||
} | |||
}) | |||
payload, | |||
}); | |||
return res; | |||
} | |||
@@ -4,6 +4,9 @@ import './custom-taroui-variables.scss'; | |||
import configStore from './store'; | |||
import { initApp } from '@root/actions/app'; | |||
import { Provider } from 'react-redux'; | |||
import dayjs from 'dayjs'; | |||
import 'dayjs/locale/zh-cn'; | |||
dayjs.locale('zh-cn') | |||
const store = configStore(); | |||
@@ -0,0 +1,18 @@ | |||
import { Image, View } from '@tarojs/components'; | |||
import { useSelector } from 'react-redux'; | |||
import React, { useMemo } from 'react'; | |||
import styles from './index.module.scss'; | |||
import { getFileUrl } from '@root/service/oss'; | |||
export default function Avator(props) { | |||
const { src: propsSrc, className, ...restProps } = props; | |||
const accountAvator = useSelector(state => state.app.avatorUrl) | |||
const src = useMemo(() => { | |||
return getFileUrl(propsSrc || accountAvator); | |||
}, [propsSrc, accountAvator]) | |||
return ( | |||
<View className={[styles.avator, className]}> | |||
<Image src={src} /> | |||
</View> | |||
) | |||
} |
@@ -0,0 +1,10 @@ | |||
.avator { | |||
display: inline-block; | |||
border-radius: 50%; | |||
overflow: hidden; | |||
> image { | |||
vertical-align: top; | |||
width: 100%; | |||
height: 100%; | |||
} | |||
} |
@@ -0,0 +1,44 @@ | |||
import { useSelector } from 'react-redux'; | |||
import React, {useState} from 'react'; | |||
import styles from './welcome.module.scss'; | |||
import dayjs from 'dayjs'; | |||
import { useInterval } from '@root/utils/hooks'; | |||
import { Text, View } from '@tarojs/components'; | |||
const getText = () => { | |||
const now = dayjs(); | |||
const hour = now.get('hour'); | |||
const dateText = now.format('M月D日 ddd'); | |||
const welcome = hour > 5 && hour < 12 | |||
? '上午好! ' | |||
: hour === 12 | |||
? '中午好!' | |||
: hour > 12 && hour < 18 | |||
? '下午好!' | |||
: hour > 18 && hour < 22 | |||
? '晚上好!' | |||
: '夜深了,注意休息。'; | |||
return [dateText, welcome] | |||
} | |||
export default function Welcome(props) { | |||
const { className } = props; | |||
const accountName = useSelector(state => state.app.accountName); | |||
const [dateText, setDateText] = useState(''); | |||
const [welcomeText, setWelcomeText] = useState(''); | |||
useInterval(() => { | |||
const [nextDateText, nextWelcomeText] = getText(); | |||
setDateText(nextDateText); | |||
setWelcomeText(nextWelcomeText); | |||
}, 1000 * 60, { immediate: true}); | |||
return ( | |||
<View className={[styles.welcome, className]}> | |||
<Text className={styles.text}>{dateText}</Text> | |||
<Text className={styles.text}>{`${welcomeText} ${accountName}`}</Text> | |||
</View> | |||
) | |||
} |
@@ -0,0 +1,7 @@ | |||
.welcome { | |||
.text { | |||
display: block; | |||
color: rgba(50, 50, 60, 100); | |||
font-size: 40px; | |||
} | |||
} |
@@ -0,0 +1,52 @@ | |||
import { Image, Text, View, ScrollView } from '@tarojs/components'; | |||
import React, { useCallback, useEffect, useLayoutEffect, useState } from 'react'; | |||
import Taro from '@tarojs/taro'; | |||
import styles from './index.module.scss'; | |||
import Avator from '@root/components/Avator'; | |||
import Welcome from './components/welcome'; | |||
import { fetchProjectList } from './service'; | |||
export default function ProjectListView() { | |||
const [projectList, setProjectList] = useState([]); | |||
useLayoutEffect(() => { | |||
Taro.setNavigationBarColor({ | |||
backgroundColor: '#f6f6f6', // 窗口的背景色为白色 | |||
frontColor: '#ffffff', | |||
}); | |||
}, []); | |||
const fetchList = useCallback(async () => { | |||
const dataList = await fetchProjectList(); | |||
setProjectList(dataList); | |||
}, []); | |||
useEffect(() => { | |||
fetchList(); | |||
}, [fetchList]) | |||
return ( | |||
<ScrollView className={styles.project} scrollY> | |||
<View className={styles.wrapPadding}> | |||
<Avator className={styles.avator} /> | |||
</View> | |||
<Welcome className={styles.wrapPadding} /> | |||
<View className={[styles.listTitle, styles.wrapPadding]}> | |||
<Text>我的项目</Text> | |||
</View> | |||
{ | |||
projectList.map(project => ( | |||
<View className={styles.projectItem} key={project.id}> | |||
<Image className={styles.img} src={project.showImgUrl} /> | |||
<View className={styles.label}> | |||
<Text>{project.projName}</Text> | |||
</View> | |||
</View> | |||
)) | |||
} | |||
</ScrollView> | |||
) | |||
} | |||
@@ -0,0 +1,45 @@ | |||
.project { | |||
$commonPadding: 24px * 2; | |||
height: 100vh; | |||
background-color: #f6f6f6; | |||
.wrapPadding { | |||
padding: 0 $commonPadding; | |||
} | |||
.avator { | |||
width: 84px; | |||
height: 84px; | |||
} | |||
.list { | |||
height: calc(100% - 274px * 2); | |||
&Title { | |||
margin-top: 40px * 2; | |||
color: rgba(50, 50, 60, 100); | |||
font-size: 20px * 2; | |||
} | |||
} | |||
&Item { | |||
display: flex; | |||
padding: 8px * 2 $commonPadding; | |||
.img { | |||
flex: none; | |||
width: 108px * 2; | |||
height: 80px * 2; | |||
border-radius: 8px; | |||
box-shadow: 0px 7px 16px 0px rgba(0, 0, 0, 0.2); | |||
} | |||
.label { | |||
flex: 1; | |||
margin-left: 23px * 2; | |||
// white-space: nowrap; | |||
// text-overflow: ellipsis; | |||
// overflow: hidden; | |||
color: rgba(50, 50, 60, 100); | |||
font-size: 18px * 2; | |||
display: flex; | |||
align-items: center; | |||
} | |||
} | |||
} |
@@ -0,0 +1,16 @@ | |||
import { getFileUrl } from "@root/service/oss"; | |||
import getData from "@root/utils/request"; | |||
import * as storage from '@root/utils/storage'; | |||
import { firstCharToLowerCase } from "@root/utils/tool"; | |||
export async function fetchProjectList() { | |||
const accountId = storage.get('accountId'); | |||
const res = await getData('project/queryProjectListByUserId', { userId: accountId }); | |||
const dataList = (res.data || []).map(upperD => { | |||
const d = firstCharToLowerCase(upperD); | |||
d.showImgUrl = getFileUrl(d.showImgUrl); | |||
return d; | |||
}); | |||
return dataList; | |||
} |
@@ -0,0 +1,10 @@ | |||
import { Text, View } from '@tarojs/components'; | |||
import React from 'react'; | |||
export default function RecycleView() { | |||
return ( | |||
<View> | |||
<Text>回收站页</Text> | |||
</View> | |||
) | |||
} |
@@ -28,12 +28,15 @@ export const wrapOssProtocol = path => `oss://${path}`; | |||
* @param {*} saveAs | |||
*/ | |||
export const getFileUrl = memoize(function getFileUrlInner(serverOSSPath = '', saveAs) { | |||
if (serverOSSPath.indexOf('oss://') !== 0) { | |||
return serverOSSPath; | |||
if (serverOSSPath.indexOf('oss://') === 0) { | |||
const objectKey = serverOSSPath.substr(6); | |||
return `https://yiyun-client-files.oss-cn-hangzhou.aliyuncs.com/${objectKey}`; | |||
} | |||
if(serverOSSPath.indexOf('static/') === 0) { // 处理 static/img/faceImg/faceXX.png | |||
return `https://yiyun-client-files.oss-cn-hangzhou.aliyuncs.com/faceImg/${serverOSSPath.substr(19)}`; | |||
} | |||
const objectKey = serverOSSPath.substr(6); | |||
// const filename = saveAs || objectKey.split('/').pop(); | |||
return `https://yiyun-client-files.oss-cn-hangzhou.aliyuncs.com/${objectKey}`; | |||
return serverOSSPath; | |||
}) | |||
// export async function saveStr(saveAs, str) { | |||
// return await getData('oss', 'saveStr', { saveAs, context: str }); | |||
@@ -0,0 +1,21 @@ | |||
import { useEffect, useRef } from 'react'; | |||
export function useInterval(fn,delay,options) { | |||
const immediate = options?.immediate; | |||
const fnRef = useRef(); | |||
fnRef.current = fn; | |||
useEffect(() => { | |||
if (delay === undefined || delay === null) return; | |||
if (immediate) { | |||
fnRef.current?.(); | |||
} | |||
const timer = setInterval(() => { | |||
fnRef.current?.(); | |||
}, delay); | |||
return () => { | |||
clearInterval(timer); | |||
}; | |||
}, [delay]); | |||
} |
@@ -1,24 +1,49 @@ | |||
import Taro from '@tarojs/taro'; | |||
import { parseRequest } from './request.config'; | |||
import { firstCharToLowerCase } from './tool'; | |||
import { firstCharToLowerCase, handleRequest, hint } from './tool'; | |||
import * as storage from './storage'; | |||
const ip = 'http://139.198.180.242:9003'; | |||
const header = { 'Content-Type': 'application/json' }; | |||
const getData = async (path, params = {}) => { | |||
const datas = JSON.stringify({ ...params }); | |||
let gsessionId = storage.get('gssesionid'); | |||
const getData = async (path, params = {}, silent = false) => { | |||
// const datas = JSON.stringify({ ...params }); | |||
if(gsessionId) { | |||
header['Cookie'] = gsessionId; | |||
} | |||
const [method, fullpath] = parseRequest(path); | |||
const httpResponse = await Taro.request({ | |||
url: `${ip}/${fullpath}`, | |||
data: datas, | |||
data: params, | |||
header, | |||
method, | |||
}); | |||
const ifhaveCookie = httpResponse.cookies[0] && httpResponse.cookies[0].match(/gsessionid\=([\d\w]*)/); | |||
if(ifhaveCookie) { | |||
gsessionId = ifhaveCookie[0]; | |||
} | |||
if (httpResponse.statusCode !== 200) { | |||
debugger; | |||
} | |||
return firstCharToLowerCase(httpResponse.data); | |||
const outputData = firstCharToLowerCase(httpResponse.data); | |||
outputData.httpCode = httpResponse.statusCode; | |||
handleRequest(outputData) | |||
.httpError(() => { | |||
if(!silent) { | |||
hint('网络错误,请稍微再试'); | |||
} | |||
}) | |||
.error(() => { | |||
if(!silent) { | |||
hint(outputData.msg); | |||
} | |||
}); | |||
return outputData; | |||
} | |||
export default getData; |
@@ -1,6 +1,5 @@ | |||
export const isReqSuccess = res => res.code === 0 || res.Code === 0; | |||
export function firstCharToLowerCase(obj) { | |||
return Object.entries(obj).reduce((o, [key, value]) => { | |||
@@ -9,9 +8,42 @@ export function firstCharToLowerCase(obj) { | |||
},{}); | |||
} | |||
export const hint = msg => Taro.showToast({ title: msg, icon: 'none', duration: 2000 }); | |||
export function firstCharToUpperCase(obj) { | |||
return Object.entries(obj).reduce((o, [key, value]) => { | |||
o[`${key[0].toLocaleUpperCase()}${key.slice(1)}`] = value; | |||
return o; | |||
},{}); | |||
} | |||
} | |||
export const isReqSuccess = res => res.code === 0 || res.Code === 0; | |||
class RequestHandler { | |||
constructor(response) { | |||
this.response = response; | |||
} | |||
success(f) { | |||
const res = this.response; | |||
if (res.httpCode !== 200) { return; } | |||
if (!isReqSuccess(res)) { return; } | |||
f(res); | |||
return this; | |||
} | |||
error(f) { // 业务上报错 | |||
const res = this.response; | |||
if (res.httpCode !== 200) { return; } | |||
if (isReqSuccess(res)) { return; } | |||
f(res); | |||
return this; | |||
} | |||
httpError(f) { // 请求报错 | |||
const res = this.response; | |||
if(res.httpCode !== 200) { | |||
f(res); | |||
} | |||
return this; | |||
} | |||
} | |||
export const handleRequest = res => new RequestHandler(res); |