mirror of https://github.com/alibaba/ice.git
chroe: add todos example for miniapp (#3396)
This commit is contained in:
parent
0bcbd669de
commit
105a496f68
|
@ -0,0 +1,3 @@
|
|||
# todos example with react for miniapp
|
||||
|
||||
https://github.com/ice-lab/icejs/tree/master/examples
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"targets": ["miniapp"]
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"name": "todos-example-with-miniapp",
|
||||
"description": "miniapp example",
|
||||
"dependencies": {
|
||||
"react": "^16.4.1",
|
||||
"react-dom": "^16.4.1",
|
||||
"universal-request": "^2.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"miniapp-history": "^0.1.2",
|
||||
"miniapp-render": "^1.0.1"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "icejs start",
|
||||
"build": "icejs build"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
import { runApp } from 'ice';
|
||||
|
||||
runApp({
|
||||
app: {
|
||||
onShow() {
|
||||
console.log('app show...');
|
||||
},
|
||||
onHide() {
|
||||
console.log('app hide...');
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"routes": [
|
||||
{
|
||||
"path": "/todos",
|
||||
"source": "pages/todos/index"
|
||||
},
|
||||
{
|
||||
"path": "/add-todo",
|
||||
"source": "pages/add-todo/index"
|
||||
}
|
||||
],
|
||||
"window": {
|
||||
"defaultTitle": "Todo App",
|
||||
"titleBarColor": "#323239"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
import React from 'react';
|
||||
import styles from './index.module.scss';
|
||||
|
||||
function AddButton (props) {
|
||||
const { text, onClickMe } = props;
|
||||
|
||||
return (
|
||||
<button type='button' className={styles['add-button']} onClick={onClickMe}>
|
||||
<text className={styles['add-icon']}>+</text>
|
||||
<text>{text}</text>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export default AddButton;
|
|
@ -0,0 +1,13 @@
|
|||
.add-button {
|
||||
display: inline-block;
|
||||
background: none;
|
||||
color: #FFF;
|
||||
border: none;
|
||||
width: 300rpx;
|
||||
}
|
||||
|
||||
.add-icon {
|
||||
font-size: 52rpx;
|
||||
color: #00FFD6;
|
||||
margin-right: 10rpx;
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
import React, { useState } from 'react';
|
||||
|
||||
import AddButton from '@/components/add-button';
|
||||
import styles from './index.module.scss';
|
||||
|
||||
const AddTodo = (props) => {
|
||||
const { history } = props;
|
||||
|
||||
// state
|
||||
const [value, setValue] = useState('');
|
||||
|
||||
// handlers
|
||||
const onChange = (e) => {
|
||||
setValue(e.target.value);
|
||||
};
|
||||
|
||||
const add = () => {
|
||||
const storageKey = 'todos';
|
||||
|
||||
// eslint-disable-next-line
|
||||
const { data } = my.getStorageSync({
|
||||
key: storageKey
|
||||
});
|
||||
|
||||
if (data !== null) {
|
||||
data.todos.push({
|
||||
text: value,
|
||||
completed: false
|
||||
});
|
||||
}
|
||||
|
||||
// eslint-disable-next-line
|
||||
my.setStorageSync({
|
||||
key: storageKey,
|
||||
data: {
|
||||
todos: data.todos
|
||||
}
|
||||
});
|
||||
|
||||
history.push('/todos');
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<view className={styles['page-add-todo']}>
|
||||
<view className={styles['add-todo']}>
|
||||
<input
|
||||
className={styles['add-todo-input']}
|
||||
placeholder="What needs to be done?"
|
||||
value={value}
|
||||
onChange={onChange} />
|
||||
</view>
|
||||
|
||||
<view className={styles['todo-footer']}>
|
||||
<AddButton text="Add Todo" onClickMe={add}/>
|
||||
</view>
|
||||
</view>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddTodo;
|
|
@ -0,0 +1,38 @@
|
|||
page {
|
||||
background: #323239;
|
||||
font-family: "pingFang SC" "pingFang";
|
||||
}
|
||||
|
||||
.page-add-todo {
|
||||
font-family: "pingFang SC" "pingFang";
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.add-todo {
|
||||
padding: 40px;
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.add-todo-input {
|
||||
display: block;
|
||||
font-size: 50rpx;
|
||||
font-weight: 100;
|
||||
padding: 5px 5px;
|
||||
background: none;
|
||||
border:none;
|
||||
border-bottom: 1px solid #DFDFDF;
|
||||
color: #0EFFD6;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.todo-footer {
|
||||
padding: 50rpx 0 100rpx;
|
||||
font-size: 48rpx;
|
||||
font-weight: 200;
|
||||
text-align: center;
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
import React, { useState } from 'react';
|
||||
import { usePageShow } from 'ice';
|
||||
|
||||
import AddButton from '@/components/add-button';
|
||||
import logo from '@/public/logo.svg';
|
||||
import styles from './index.module.scss';
|
||||
|
||||
const Todos = (props) => {
|
||||
const { history } = props;
|
||||
|
||||
// state
|
||||
const [userInfo, setUserInfo] = useState({});
|
||||
const [todos, setTodos] = useState([
|
||||
{ text: 'Learning Javascript', completed: true },
|
||||
{ text: 'Learning ES2016', completed: true },
|
||||
{ text: 'Learning 小程序', completed: false },
|
||||
]);
|
||||
|
||||
// handlers
|
||||
const addTodo = () => {
|
||||
history.push('/add-todo');
|
||||
};
|
||||
|
||||
const onTodoChange = (text) => {
|
||||
const changedTodos = todos.map(todo => {
|
||||
const completed = todo.completed;
|
||||
return {
|
||||
...todo,
|
||||
completed: text === todo.text ? !completed : completed
|
||||
};
|
||||
});
|
||||
setTodos(changedTodos);
|
||||
};
|
||||
|
||||
// lifecyle function
|
||||
usePageShow(async () => {
|
||||
// my is global variable in mini program
|
||||
// eslint-disable-next-line
|
||||
const myUserInfo = await my.getUserInfo();
|
||||
setUserInfo(myUserInfo);
|
||||
console.log('userInfo:', myUserInfo);
|
||||
|
||||
// eslint-disable-next-line
|
||||
const { data } = my.getStorageSync({
|
||||
key: 'todos'
|
||||
});
|
||||
|
||||
if (data === null) {
|
||||
// eslint-disable-next-line
|
||||
my.setStorageSync({
|
||||
key: 'todos',
|
||||
data: {
|
||||
todos
|
||||
}
|
||||
});
|
||||
} else {
|
||||
setTodos(data.todos);
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<view className={styles['page-todos']}>
|
||||
<view className={styles.user}>
|
||||
<image className={styles.avatar} src={userInfo.avatar ? logo : logo} alt="用户头像" />
|
||||
<text className={styles.nickname}>{userInfo.nickName ? `${userInfo.nickName}'s` : 'My' } Todo List</text>
|
||||
</view>
|
||||
|
||||
<view className={styles['todo-items']}>
|
||||
<view className={styles['todo-items-group']}>
|
||||
{
|
||||
todos.map(todo => (
|
||||
<view
|
||||
className={`${styles['todo-item']} ${todo.completed ? styles.checked : ''}`}
|
||||
onClick={() => onTodoChange(todo.text)}
|
||||
key={todo.text}>
|
||||
<checkbox className={styles['todo-item-checkbox']} checked={todo.completed} />
|
||||
<text className={styles['todo-item-text']}>{todo.text}</text>
|
||||
</view>
|
||||
))
|
||||
}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view className={styles['todo-footer']}>
|
||||
<AddButton text="Add Todo" onClickMe={addTodo} />
|
||||
</view>
|
||||
</view>
|
||||
);
|
||||
};
|
||||
|
||||
export default Todos;
|
|
@ -0,0 +1,130 @@
|
|||
page {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
background: #323239;
|
||||
font-family: "pingFang SC" "pingFang";
|
||||
}
|
||||
|
||||
.page-todos {
|
||||
font-family: "pingFang SC" "pingFang";
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 750rpx;
|
||||
max-height: 100vh;
|
||||
}
|
||||
|
||||
.user {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
padding: 30px;
|
||||
color: #FFF;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 130rpx;
|
||||
height: 130rpx;
|
||||
border-radius: 50%;
|
||||
background-color: #FFF;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.nickname {
|
||||
padding-top: 40rpx;
|
||||
text-align: center;
|
||||
font-size: 40rpx;
|
||||
font-weight: 100;
|
||||
}
|
||||
|
||||
.todo-items {
|
||||
flex-grow: 1;
|
||||
font-size: 34rpx;
|
||||
padding: 50rpx 120rpx;
|
||||
color: #0EFFD6;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.todo-items-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.todo-item {
|
||||
position: relative;
|
||||
margin-bottom: 50rpx;
|
||||
padding-left:80rpx;
|
||||
line-height: 70rpx;
|
||||
height: 80rpx;
|
||||
box-sizing: border-box;
|
||||
border: 2px solid rgb(14, 255, 214);
|
||||
border-radius: 100rpx;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
/* white-space:nowrap; */
|
||||
|
||||
transition: border 0.2s;
|
||||
}
|
||||
|
||||
.todo-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.todo-item::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 12rpx;
|
||||
margin-right: 20rpx;
|
||||
width: 45rpx;
|
||||
height: 45rpx;
|
||||
background-color: rgba(14, 222, 255, 0.3);
|
||||
border-radius: 50%;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.todo-item::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 29rpx;
|
||||
width: 8rpx;
|
||||
height: 18rpx;
|
||||
top: 50%;
|
||||
transform: translateY(-60%) rotate(38deg);
|
||||
border: 4rpx solid #FFF;
|
||||
border-width: 0 4rpx 4rpx 0;
|
||||
opacity: 0;
|
||||
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
|
||||
.todo-item-checkbox {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.checked .todo-item-text {
|
||||
text-decoration: line-through;
|
||||
color: #1AA0B8;
|
||||
}
|
||||
|
||||
.checked.todo-item {
|
||||
border: 2px solid rgba(14, 222, 255, 0.2);
|
||||
}
|
||||
|
||||
.checked.todo-item::before {
|
||||
background-color: rgba(14, 222, 255, 0.2);
|
||||
}
|
||||
|
||||
.checked.todo-item::after {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.todo-footer {
|
||||
flex-shrink: 0;
|
||||
padding: 50rpx 0 100rpx;
|
||||
font-size: 48rpx;
|
||||
font-weight: 200;
|
||||
text-align: center;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-11.5 -10.23174 23 20.46348">
|
||||
<title>React Logo</title>
|
||||
<circle cx="0" cy="0" r="2.05" fill="#61dafb"/>
|
||||
<g stroke="#61dafb" stroke-width="1" fill="none">
|
||||
<ellipse rx="11" ry="4.2"/>
|
||||
<ellipse rx="11" ry="4.2" transform="rotate(60)"/>
|
||||
<ellipse rx="11" ry="4.2" transform="rotate(120)"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 366 B |
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"compileOnSave": false,
|
||||
"buildOnSave": false,
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"module": "esnext",
|
||||
"target": "es6",
|
||||
"jsx": "react",
|
||||
"moduleResolution": "node",
|
||||
"sourceMap": true,
|
||||
"alwaysStrict": true,
|
||||
"paths": {
|
||||
"@/*": ["./src/*"],
|
||||
"ice": [".ice/index.ts"]
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue