├── .eslintignore
├── .gitignore
├── .prettierrc
├── src
├── store
│ ├── actions
│ │ ├── index.js
│ │ └── counter.js
│ ├── types
│ │ ├── index.js
│ │ └── counter.js
│ ├── reducers
│ │ ├── index.js
│ │ └── counter.js
│ └── index.js
├── style
│ ├── base
│ │ ├── variable
│ │ │ ├── weui-button.less
│ │ │ ├── weui-grid.less
│ │ │ ├── weui-progress.less
│ │ │ ├── weui-dialog.less
│ │ │ ├── weui-msg.less
│ │ │ ├── global.less
│ │ │ ├── color.less
│ │ │ └── weui-cell.less
│ │ ├── reset.less
│ │ ├── fn.less
│ │ └── mixin
│ │ │ ├── text.less
│ │ │ ├── setOnepx.less
│ │ │ └── setArrow.less
│ ├── widget
│ │ ├── weui-flex
│ │ │ └── weui-flex.less
│ │ ├── weui-cell
│ │ │ ├── weui-switch.less
│ │ │ ├── weui-form.less
│ │ │ ├── weui-check.less
│ │ │ ├── weui-access.less
│ │ │ ├── weui-form
│ │ │ │ ├── weui-vcode.less
│ │ │ │ ├── weui-select.less
│ │ │ │ ├── weui-form_common.less
│ │ │ │ └── weui-form-preview.less
│ │ │ ├── weui-cell.less
│ │ │ └── weui-uploader.less
│ │ ├── weui-button
│ │ │ └── weui-button.less
│ │ ├── weui-progress
│ │ │ └── weui-progress.less
│ │ ├── weui-tab
│ │ │ ├── weui-tab.less
│ │ │ └── weui-navbar.less
│ │ ├── weui-tips
│ │ │ ├── weui-badge.less
│ │ │ └── weui-loadmore.less
│ │ ├── weui-page
│ │ │ ├── weui-article.less
│ │ │ └── weui-msg.less
│ │ ├── weui-panel
│ │ │ └── weui-panel.less
│ │ ├── weui-agree
│ │ │ └── weui-agree.less
│ │ ├── weui-footer
│ │ │ └── weui-footer.less
│ │ ├── weui-grid
│ │ │ └── weui-grid.less
│ │ ├── weui-animate
│ │ │ └── weui-animate.less
│ │ ├── weui-searchbar
│ │ │ └── weui-searchbar.less
│ │ ├── weui-media-box
│ │ │ └── weui-media-box.less
│ │ └── weui-loading
│ │ │ └── weui-loading.less
│ └── weui.less
├── images
│ ├── user.png
│ ├── index.png
│ ├── reply.png
│ ├── trash.png
│ ├── category.png
│ ├── index_selected.png
│ └── user_selected.png
├── utils
│ ├── util.js
│ └── api.js
├── mixins
│ ├── test.js
│ ├── unreadCount.js
│ └── replyMixin.js
├── components
│ ├── replyCreate.wpy
│ ├── groupitem.wpy
│ ├── panel.wpy
│ ├── group.wpy
│ ├── list.wpy
│ ├── wepy-list.wpy
│ ├── counter.wpy
│ └── topicList.wpy
├── index.template.html
├── pages
│ ├── topics
│ │ ├── userIndex.wpy
│ │ ├── index.wpy
│ │ └── show.wpy
│ ├── replies
│ │ ├── userIndex.wpy
│ │ ├── index.wpy
│ │ └── create.wpy
│ ├── users
│ │ ├── show.wpy
│ │ ├── me.wpy
│ │ └── edit.wpy
│ ├── auth
│ │ ├── login.wpy
│ │ └── register.wpy
│ └── notifications
│ │ └── index.wpy
└── app.wpy
├── .wepyignore
├── .editorconfig
├── project.config.json
├── .eslintrc.js
├── README.md
├── package.json
├── wepy.config.js
└── .wepycache
/.eslintignore:
--------------------------------------------------------------------------------
1 | dist/*
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | .DB_store
4 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true
3 | }
4 |
--------------------------------------------------------------------------------
/src/store/actions/index.js:
--------------------------------------------------------------------------------
1 | export * from './counter'
2 |
--------------------------------------------------------------------------------
/src/store/types/index.js:
--------------------------------------------------------------------------------
1 | export * from './counter'
2 |
--------------------------------------------------------------------------------
/src/style/base/variable/weui-button.less:
--------------------------------------------------------------------------------
1 | @weuiBtnDefaultGap:15px;
--------------------------------------------------------------------------------
/.wepyignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | .DB_store
4 | *.wpy___jb_tmp___
5 |
--------------------------------------------------------------------------------
/src/images/user.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/summerblue/larabbs-weapp/HEAD/src/images/user.png
--------------------------------------------------------------------------------
/src/images/index.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/summerblue/larabbs-weapp/HEAD/src/images/index.png
--------------------------------------------------------------------------------
/src/images/reply.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/summerblue/larabbs-weapp/HEAD/src/images/reply.png
--------------------------------------------------------------------------------
/src/images/trash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/summerblue/larabbs-weapp/HEAD/src/images/trash.png
--------------------------------------------------------------------------------
/src/images/category.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/summerblue/larabbs-weapp/HEAD/src/images/category.png
--------------------------------------------------------------------------------
/src/images/index_selected.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/summerblue/larabbs-weapp/HEAD/src/images/index_selected.png
--------------------------------------------------------------------------------
/src/images/user_selected.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/summerblue/larabbs-weapp/HEAD/src/images/user_selected.png
--------------------------------------------------------------------------------
/src/style/base/variable/weui-grid.less:
--------------------------------------------------------------------------------
1 | @weuiGridBorderColor:#D9D9D9;
2 | @weuiGridFontSize: 14px;
3 | @weuiGridIconSize: 28px;
4 | @weuiGridColumnCount: 3;
--------------------------------------------------------------------------------
/src/store/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux'
2 | import counter from './counter'
3 |
4 | export default combineReducers({
5 | counter
6 | })
7 |
--------------------------------------------------------------------------------
/src/store/types/counter.js:
--------------------------------------------------------------------------------
1 | export const INCREMENT = 'INCREMENT'
2 |
3 | export const DECREMENT = 'DECREMENT'
4 |
5 | export const ASYNC_INCREMENT = 'ASYNC_INCREMENT'
6 |
--------------------------------------------------------------------------------
/src/style/widget/weui-flex/weui-flex.less:
--------------------------------------------------------------------------------
1 | @import "../../base/fn.less";
2 |
3 | .weui-flex {
4 | display: flex;
5 | }
6 | .weui-flex__item{
7 | flex: 1;
8 | }
9 |
--------------------------------------------------------------------------------
/src/style/base/reset.less:
--------------------------------------------------------------------------------
1 | @import "fn.less";
2 |
3 | page {
4 | line-height: 1.6;
5 | font-family: @weuiFontDefault;
6 | }
7 |
8 | icon{
9 | vertical-align: middle;
10 | }
11 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/src/style/base/variable/weui-progress.less:
--------------------------------------------------------------------------------
1 | @weuiProgressBg: #EBEBEB;
2 | @weuiProgressColor: #09BB07;
3 | @weuiProgressHeight: 3px;
4 | @weuiProgressCloseBg: #EF4F4F;
5 | @weuiProgressActiveBg: #C13E3E;
6 |
--------------------------------------------------------------------------------
/src/style/base/variable/weui-dialog.less:
--------------------------------------------------------------------------------
1 | @weuiDialogBackgroundColor: #FFFFFF;
2 | @weuiDialogLineColor: #D5D5D6;
3 | @weuiDialogLinkColor: #3CC51F;
4 | @weuiDialogLinkActiveBc: #EEEEEE;
5 | @weuiDialogGapWidth: 1.6em;
6 |
--------------------------------------------------------------------------------
/src/style/base/variable/weui-msg.less:
--------------------------------------------------------------------------------
1 | @weuiMsgPaddingTop:36px;
2 | @weuiMsgIconGap:30px;
3 | @weuiMsgTitleGap:5px;
4 | @weuiMsgTextGap:25px;
5 | @weuiMsgOprGap:25px;
6 | @weuiMsgExtraAreaGap:15px;
7 | @weuiMsgExtraAreaOfMinHeight:438px;
--------------------------------------------------------------------------------
/src/style/base/variable/global.less:
--------------------------------------------------------------------------------
1 | // font
2 | @weuiFontEN:-apple-system-font,"Helvetica Neue";
3 | @weuiFontCN:"PingFang SC","Hiragino Sans GB","Microsoft YaHei";
4 | @weuiFontSans:sans-serif;
5 | @weuiFontDefault:@weuiFontEN,@weuiFontSans;
--------------------------------------------------------------------------------
/src/style/widget/weui-cell/weui-switch.less:
--------------------------------------------------------------------------------
1 | @import "../../base/fn.less";
2 |
3 | .weui-cell_switch{
4 | padding-top: (@weuiCellHeight - @weuiSwitchHeight) / 2;
5 | padding-bottom: (@weuiCellHeight - @weuiSwitchHeight) / 2;
6 | }
7 |
--------------------------------------------------------------------------------
/src/style/widget/weui-cell/weui-form.less:
--------------------------------------------------------------------------------
1 | @import "../../base/fn.less";
2 | @import "./weui-form/weui-form_common.less";
3 | @import "./weui-form/weui-form-preview.less";
4 | @import "./weui-form/weui-select.less";
5 | @import "./weui-form/weui-vcode.less";
6 |
--------------------------------------------------------------------------------
/src/utils/util.js:
--------------------------------------------------------------------------------
1 | import moment from 'moment'
2 | import 'moment/locale/zh-cn'
3 |
4 | const diffForHumans = (date, format = 'YYYYMMDD H:mm:ss') => {
5 | moment.locale('zh-cn')
6 | return moment(date, format).fromNow()
7 | }
8 |
9 | export default {
10 | diffForHumans
11 | }
12 |
--------------------------------------------------------------------------------
/src/style/widget/weui-button/weui-button.less:
--------------------------------------------------------------------------------
1 | @import "../../base/fn.less";
2 |
3 | .weui-btn{
4 | margin-top: @weuiBtnDefaultGap;
5 | &:first-child{
6 | margin-top: 0;
7 | }
8 | }
9 | .weui-btn-area{
10 | margin: @weuiCellsMarginTop @weuiBtnDefaultGap .3em;
11 | }
12 |
--------------------------------------------------------------------------------
/src/store/index.js:
--------------------------------------------------------------------------------
1 | import { createStore, applyMiddleware } from 'redux'
2 | import promiseMiddleware from 'redux-promise'
3 | import rootReducer from './reducers'
4 |
5 | export default function configStore () {
6 | const store = createStore(rootReducer, applyMiddleware(promiseMiddleware))
7 | return store
8 | }
9 |
--------------------------------------------------------------------------------
/src/style/widget/weui-progress/weui-progress.less:
--------------------------------------------------------------------------------
1 | @import "../../base/fn.less";
2 |
3 | .weui-progress {
4 | display: flex;
5 | align-items: center;
6 | }
7 |
8 | .weui-progress__bar {
9 | flex: 1;
10 | }
11 |
12 | .weui-progress__opr {
13 | margin-left: 15px;
14 | font-size: 0;
15 | }
16 |
--------------------------------------------------------------------------------
/src/store/actions/counter.js:
--------------------------------------------------------------------------------
1 | import { ASYNC_INCREMENT } from '../types/counter'
2 | import { createAction } from 'redux-actions'
3 |
4 | export const asyncInc = createAction(ASYNC_INCREMENT, () => {
5 | return new Promise(resolve => {
6 | setTimeout(() => {
7 | resolve(1)
8 | }, 1000)
9 | })
10 | })
11 |
--------------------------------------------------------------------------------
/src/style/widget/weui-tab/weui-tab.less:
--------------------------------------------------------------------------------
1 | @import "../../base/fn.less";
2 | @import "weui-navbar.less";
3 |
4 | .weui-tab {
5 | position: relative;
6 | height: 100%;
7 | }
8 |
9 | .weui-tab__panel{
10 | box-sizing: border-box;
11 | height: 100%;
12 | padding-top: 50px;
13 | overflow: auto;
14 | -webkit-overflow-scrolling: touch;
15 | }
16 |
--------------------------------------------------------------------------------
/project.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "larabbs weapp project",
3 | "setting": {
4 | "urlCheck": false,
5 | "es6": false,
6 | "postcss": false,
7 | "minified": false,
8 | "newFeature": true
9 | },
10 | "compileType": "miniprogram",
11 | "appid": "wx756787de07ac71b1",
12 | "projectname": "larabbs-weapp",
13 | "miniprogramRoot": "dist/",
14 | "condition": {}
15 | }
--------------------------------------------------------------------------------
/src/mixins/test.js:
--------------------------------------------------------------------------------
1 | import wepy from 'wepy'
2 |
3 | export default class testMixin extends wepy.mixin {
4 | data = {
5 | mixin: 'This is mixin data.'
6 | }
7 | methods = {
8 | tap () {
9 | this.mixin = 'mixin data was changed'
10 | console.log('mixin method tap')
11 | }
12 | }
13 |
14 | onShow() {
15 | console.log('mixin onShow')
16 | }
17 |
18 | onLoad() {
19 | console.log('mixin onLoad')
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/style/widget/weui-tips/weui-badge.less:
--------------------------------------------------------------------------------
1 | @import "../../base/fn.less";
2 |
3 | .weui-badge {
4 | display: inline-block;
5 | padding: .15em .4em;
6 | min-width: 8px;
7 | border-radius: 18px;
8 | background-color: @weuiColorWarn;
9 | color: #FFFFFF;
10 | line-height: 1.2;
11 | text-align: center;
12 | font-size: 12px;
13 | vertical-align: middle;
14 | }
15 | .weui-badge_dot {
16 | padding: .4em;
17 | min-width: 0;
18 | }
19 |
--------------------------------------------------------------------------------
/src/style/base/fn.less:
--------------------------------------------------------------------------------
1 | // mixin
2 | @import "mixin/setOnepx.less";
3 | @import "mixin/setArrow.less";
4 | @import "mixin/text.less";
5 |
6 |
7 | // variable
8 | @import "variable/global.less";
9 | @import "variable/color.less";
10 |
11 |
12 | @import "variable/weui-cell.less";
13 | @import "variable/weui-button.less";
14 | @import "variable/weui-msg.less";
15 | @import "variable/weui-grid.less";
16 | @import "variable/weui-progress.less";
17 | @import "variable/weui-dialog.less";
18 |
--------------------------------------------------------------------------------
/src/style/base/variable/color.less:
--------------------------------------------------------------------------------
1 | // color
2 | @weuiColorPrimary: #1AAD19;
3 | @weuiColorWarn: #E64340;
4 |
5 | // link
6 | @weuiLinkColorDefault: #586C94;
7 |
8 | // background
9 | @weuiBgColorDefault: #EFEFF4;
10 | @weuiBgColorActive: #ECECEC;
11 |
12 | // line
13 | @weuiLineColorLight: #E5E5E5;
14 | @weuiLineColorDark: #BCBAB6;
15 |
16 | // text
17 | @weuiTextColorTitle: #000000;
18 | @weuiTextColorTips: #B2B2B2;
19 | @weuiTextColorWarn: @weuiColorWarn;
20 | @weuiTextColorGray: #999999;
--------------------------------------------------------------------------------
/src/style/base/mixin/text.less:
--------------------------------------------------------------------------------
1 | .ellipsis(@w:auto) {
2 | width: @w;
3 | overflow: hidden;
4 | text-overflow: ellipsis;
5 | white-space: nowrap;
6 | word-wrap: normal;
7 | }
8 |
9 | .ellipsisLn(@line) {
10 | overflow: hidden;
11 | text-overflow: ellipsis;
12 | display: -webkit-box;
13 | -webkit-box-orient: vertical;
14 | -webkit-line-clamp: @line;
15 | }
16 | .text_wrap() {
17 | word-wrap:break-word;
18 | word-break:break-all;
19 | }
20 | .hyphens() {
21 | word-wrap:break-word;
22 | -webkit-hyphens:auto;
23 | hyphens:auto;
24 | }
--------------------------------------------------------------------------------
/src/style/widget/weui-page/weui-article.less:
--------------------------------------------------------------------------------
1 | @import "../../base/fn.less";
2 |
3 | .weui-article {
4 | padding: 20px 15px;
5 | font-size: 15px;
6 | }
7 | .weui-article__section {
8 | margin-bottom: 1.5em;
9 | }
10 | .weui-article__h1 {
11 | font-size: 18px;
12 | font-weight:400;
13 | margin-bottom: .9em;
14 | }
15 | .weui-article__h2 {
16 | font-size: 16px;
17 | font-weight:400;
18 | margin-bottom: .34em;
19 | }
20 | .weui-article__h3 {
21 | font-weight:400;
22 | font-size: 15px;
23 | margin-bottom: .34em;
24 | }
25 | .weui-article__p {
26 | margin: 0 0 .8em;
27 | }
28 |
--------------------------------------------------------------------------------
/src/store/reducers/counter.js:
--------------------------------------------------------------------------------
1 | import { handleActions } from 'redux-actions'
2 | import { INCREMENT, DECREMENT, ASYNC_INCREMENT } from '../types/counter'
3 |
4 | export default handleActions({
5 | [INCREMENT] (state) {
6 | return {
7 | ...state,
8 | num: state.num + 1
9 | }
10 | },
11 | [DECREMENT] (state) {
12 | return {
13 | ...state,
14 | num: state.num - 1
15 | }
16 | },
17 | [ASYNC_INCREMENT] (state, action) {
18 | return {
19 | ...state,
20 | asyncNum: state.asyncNum + action.payload
21 | }
22 | }
23 | }, {
24 | num: 0,
25 | asyncNum: 0
26 | })
27 |
--------------------------------------------------------------------------------
/src/components/replyCreate.wpy:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
28 |
--------------------------------------------------------------------------------
/src/style/widget/weui-cell/weui-check.less:
--------------------------------------------------------------------------------
1 | @import "../../base/fn.less";
2 |
3 | // icon
4 | .weui-icon-radio{
5 | margin-left: 3.2px;
6 | margin-right: 3.2px;
7 | }
8 | .weui-icon-checkbox_circle,
9 | .weui-icon-checkbox_success{
10 | margin-left: 4.6px;
11 | margin-right: 4.6px;
12 | }
13 |
14 | .weui-check__label{
15 | &:active{
16 | background-color: @weuiCellActiveBg;
17 | }
18 | }
19 | .weui-check{
20 | position: absolute;
21 | left: -9999px;
22 | }
23 | .weui-check__hd_in-checkbox{
24 | padding-right: @weuiCellInnerGapH;
25 | }
26 | .weui-cell__ft_in-radio{
27 | padding-left: @weuiCellInnerGapH;
28 | }
29 |
--------------------------------------------------------------------------------
/src/index.template.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | 转 WEB DEMO
11 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/style/widget/weui-panel/weui-panel.less:
--------------------------------------------------------------------------------
1 | @import "../../base/fn.less";
2 |
3 |
4 | .weui-panel {
5 | background-color: #FFFFFF;
6 | margin-top: 10px;
7 | &:first-child {
8 | margin-top: 0;
9 | }
10 |
11 | position: relative;
12 | overflow: hidden;
13 | &:before {
14 | .setTopLine(@weuiLineColorLight);
15 | }
16 | &:after {
17 | .setBottomLine(@weuiLineColorLight);
18 | }
19 | }
20 |
21 | .weui-panel__hd {
22 | padding: 14px 15px 10px;
23 | color: @weuiTextColorGray;
24 | font-size: 13px;
25 | position: relative;
26 | &:after {
27 | .setBottomLine(@weuiLineColorLight);
28 | left: 15px;
29 | }
30 | }
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/src/components/groupitem.wpy:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 | --{{gitem.childid}}.
8 | {{gitem.childname}}
9 |
10 |
11 |
28 |
--------------------------------------------------------------------------------
/src/style/widget/weui-cell/weui-access.less:
--------------------------------------------------------------------------------
1 | @import "../../base/fn.less";
2 |
3 | .weui-cell_access {
4 | color: inherit;
5 | }
6 | .weui-cell__ft_in-access {
7 | padding-right: 13px;
8 | position: relative;
9 | &:after {
10 | content: " ";
11 | .setArrow(right, 6px, #C8C8CD, 2px);
12 | position: absolute;
13 | top: 50%;
14 | margin-top: -4px;
15 | right: 2px;
16 | }
17 | }
18 | .weui-cell_link{
19 | color: @weuiLinkColorDefault;
20 | font-size: 14px;
21 |
22 | &:active{
23 | background-color: @weuiCellActiveBg;
24 | }
25 |
26 | // 由于weui-cell:first-child的:before为隐藏,所以这里要重新显示出来
27 | &:first-child{
28 | &:before{
29 | display: block;
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/style/widget/weui-agree/weui-agree.less:
--------------------------------------------------------------------------------
1 | @import "../../base/fn.less";
2 |
3 | .weui-agree{
4 | display: block;
5 | padding: .5em 15px;
6 | font-size: 13px;
7 | }
8 | .weui-agree__text{
9 | color: @weuiTextColorGray;
10 | }
11 | .weui-agree__link{
12 | display: inline;
13 | color: @weuiLinkColorDefault;
14 | }
15 | .weui-agree__checkbox{
16 | position: absolute;
17 | left: -9999px;
18 | }
19 | .weui-agree__checkbox-icon{
20 | position: relative;
21 | top: 2px;
22 | display: inline-block;
23 | border: 1px solid #D1D1D1;
24 | background-color: #FFFFFF;
25 | border-radius: 3px;
26 | width: 11px;
27 | height: 11px;
28 | }
29 | .weui-agree__checkbox-icon-check{
30 | position: absolute;
31 | top: 1px;
32 | left: 1px;
33 | }
34 |
--------------------------------------------------------------------------------
/src/style/widget/weui-cell/weui-form/weui-vcode.less:
--------------------------------------------------------------------------------
1 | @import "../../../base/fn.less";
2 |
3 | .weui-cell_vcode {
4 | padding-right: 0;
5 | }
6 | .weui-vcode-img{
7 | margin-left: 5px;
8 | height: unit(@weuiCellHeight / @weuiCellFontSize, em);
9 | vertical-align: middle;
10 | }
11 | .weui-vcode-btn {
12 | display: inline-block;
13 | height: unit(@weuiCellHeight / @weuiCellFontSize, em);
14 | margin-left: 5px;
15 | padding: 0 0.6em 0 0.7em;
16 | border-left: 1px solid @weuiLineColorLight;
17 | line-height: unit(@weuiCellHeight / @weuiCellFontSize, em);
18 | vertical-align: middle;
19 | font-size: @weuiCellFontSize;
20 | color: @weuiDialogLinkColor;
21 | white-space: nowrap;
22 | &:active {
23 | color: desaturate(@weuiDialogLinkColor, 30%);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | parser: 'babel-eslint',
4 | parserOptions: {
5 | sourceType: 'module'
6 | },
7 | env: {
8 | browser: true
9 | },
10 | // https://github.com/feross/standard/blob/master/RULES.md#javascript-standard-style
11 | extends: 'standard',
12 | // required to lint *.wpy files
13 | plugins: [
14 | 'html'
15 | ],
16 | settings: {
17 | 'html/html-extensions': ['.html', '.wpy']
18 | },
19 | // add your custom rules here
20 | 'rules': {
21 | // allow paren-less arrow functions
22 | 'arrow-parens': 0,
23 | // allow async-await
24 | 'generator-star-spacing': 0,
25 | // allow debugger during development
26 | 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
27 | 'space-before-function-paren': 0
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/style/base/variable/weui-cell.less:
--------------------------------------------------------------------------------
1 | @weuiCellBg:#FFFFFF;
2 | @weuiCellBorderColor:#D9D9D9;
3 | @weuiCellGapV:10px;
4 | @weuiCellGapH:15px;
5 | @weuiCellInnerGapH:.35em;
6 | @weuiCellFontSize:17px;
7 | @weuiCellHeight: 44px;
8 | @weuiCellHeightEm: unit(@weuiCellHeight / @weuiCellFontSize, em);
9 | @weuiCellTipsFontSize:14px;
10 | @weuiCellLabelWidth:105px;
11 | @weuiCellActiveBg: #ECECEC;
12 |
13 | @weuiCellLineHeight: unit((@weuiCellHeight - 2 * @weuiCellGapV) / @weuiCellFontSize); // 高度为44px,减去上下padding的行高
14 | @weuiCellsMarginTop:unit(20 / @weuiCellFontSize, em);
15 |
16 | // weui switch
17 | @weuiSwitchHeight: 32px;
18 |
19 | // weui uploader
20 | @weuiUploaderBorderColor:#D9D9D9;
21 | @weuiUploaderActiveBorderColor:#999999;
22 | @weuiUploaderFileSpacing: 9px;
23 | @weuiUploaderSize: 79px;
24 | @weuiUploaderBorderWidth: 1px;
--------------------------------------------------------------------------------
/src/style/widget/weui-cell/weui-form/weui-select.less:
--------------------------------------------------------------------------------
1 | @import "../../../base/fn.less";
2 |
3 | .weui-cell_select {
4 | padding: 0;
5 | }
6 | .weui-select {
7 | position: relative;
8 | padding-left: @weuiCellGapH;
9 | padding-right: 30px;
10 |
11 | height: @weuiCellHeightEm;
12 | min-height: @weuiCellHeightEm;
13 | line-height: @weuiCellHeightEm;
14 |
15 | border-right: 1rpx solid @weuiCellBorderColor;
16 |
17 | &:before{
18 | content: " ";
19 | .setArrow(right, 6px, #C8C8CD, 2px);
20 |
21 | position: absolute;
22 | top: 50%;
23 | right: @weuiCellGapH;
24 | margin-top: -4px;
25 | }
26 | &_in-select-after{
27 | padding-left: 0;
28 | }
29 | }
30 | .weui-cell__hd_in-select-after,
31 | .weui-cell__bd_in-select-before{
32 | padding-left: @weuiCellGapH;
33 | }
34 |
--------------------------------------------------------------------------------
/src/style/widget/weui-footer/weui-footer.less:
--------------------------------------------------------------------------------
1 | @import "../../base/fn.less";
2 |
3 | .weui-footer {
4 | color: @weuiTextColorGray;
5 | font-size: 14px;
6 | text-align: center;
7 | }
8 | .weui-footer_fixed-bottom{
9 | position: fixed;
10 | bottom: .52em;
11 | left: 0;
12 | right: 0;
13 | }
14 | .weui-footer__links{
15 | font-size: 0;
16 | }
17 | .weui-footer__link{
18 | display: inline-block;
19 | vertical-align: top;
20 | margin: 0 .62em;
21 | position: relative;
22 | font-size: 14px;
23 | color: @weuiLinkColorDefault;
24 | &:before{
25 | .setLeftLine();
26 | left: -.65em;
27 | top: .36em;
28 | bottom: .36em;
29 | }
30 | &:first-child{
31 | &:before{
32 | display: none;
33 | }
34 | }
35 | }
36 | .weui-footer__text{
37 | padding: 0 .34em;
38 | font-size: 12px;
39 | }
40 |
--------------------------------------------------------------------------------
/src/style/base/mixin/setOnepx.less:
--------------------------------------------------------------------------------
1 | .setTopLine(@c: #C7C7C7) {
2 | content: " ";
3 | position: absolute;
4 | left: 0;
5 | top: 0;
6 | right: 0;
7 | height: 1px;
8 | border-top: 1rpx solid @c;
9 | color: @c;
10 | }
11 |
12 | .setBottomLine(@c: #C7C7C7) {
13 | content: " ";
14 | position: absolute;
15 | left: 0;
16 | bottom: 0;
17 | right: 0;
18 | height: 1px;
19 | border-bottom: 1rpx solid @c;
20 | color: @c;
21 | }
22 |
23 | .setLeftLine(@c: #C7C7C7) {
24 | content: " ";
25 | position: absolute;
26 | left: 0;
27 | top: 0;
28 | width: 1px;
29 | bottom: 0;
30 | border-left: 1rpx solid @c;
31 | color: @c;
32 | }
33 |
34 | .setRightLine(@c: #C7C7C7) {
35 | content: " ";
36 | position: absolute;
37 | right: 0;
38 | top: 0;
39 | width: 1px;
40 | bottom: 0;
41 | border-right: 1rpx solid @c;
42 | color: @c;
43 | }
--------------------------------------------------------------------------------
/src/components/panel.wpy:
--------------------------------------------------------------------------------
1 |
29 |
30 |
31 |
32 | Title
33 |
34 |
35 |
36 |
37 |
38 |
44 |
--------------------------------------------------------------------------------
/src/style/widget/weui-tab/weui-navbar.less:
--------------------------------------------------------------------------------
1 | @import "../../base/fn.less";
2 |
3 | @weuiNavBarColor: #1AAD19;
4 | .weui-navbar {
5 | display: flex;
6 | position: absolute;
7 | z-index: 500;
8 | top: 0;
9 | width: 100%;
10 | border-bottom: 1rpx solid #CCCCCC;
11 | }
12 |
13 | .weui-navbar__item {
14 | position: relative;
15 | display: block;
16 | flex: 1;
17 | padding: 13px 0;
18 | text-align: center;
19 | font-size: 0;
20 |
21 | &.weui-bar__item_on {
22 | color: @weuiNavBarColor;
23 | }
24 | }
25 | .weui-navbar__slider {
26 | position: absolute;
27 | content: " ";
28 | left: 0;
29 | bottom: 0;
30 | width: 6em;
31 | height: 3px;
32 | background-color: @weuiNavBarColor;
33 | transition: transform .3s;
34 | }
35 | .weui-navbar__title{
36 | display: inline-block;
37 | font-size: 15px;
38 | max-width: 8em;
39 | .ellipsis();
40 | }
41 |
--------------------------------------------------------------------------------
/src/mixins/unreadCount.js:
--------------------------------------------------------------------------------
1 | import wepy from 'wepy'
2 |
3 | export default class unreadCount extends wepy.mixin {
4 | data = {
5 | // 轮训
6 | interval: null,
7 | // 未读消息数
8 | unreadCount: 0
9 | }
10 | // 页面显示
11 | onShow() {
12 | this.updateUnreadCount()
13 | this.interval = setInterval(() => {
14 | this.updateUnreadCount()
15 | }, 30000)
16 | }
17 | // 页面隐藏
18 | onHide() {
19 | // 关闭轮训
20 | clearInterval(this.interval)
21 | }
22 | // 设置未读消息数
23 | updateUnreadCount() {
24 | // 从全局获取未读消息数
25 | this.unreadCount = this.$parent.globalData.unreadCount
26 | this.$apply()
27 |
28 | if (this.unreadCount) {
29 | // 设置 badge
30 | wepy.setTabBarBadge({
31 | index: 1,
32 | text: this.unreadCount.toString()
33 | })
34 | } else {
35 | // 移除 badge
36 | wepy.removeTabBarBadge({
37 | index: 1
38 | })
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/components/group.wpy:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 | {{grouplist.id}}.
7 | {{grouplist.name}}
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
36 |
--------------------------------------------------------------------------------
/src/style/widget/weui-grid/weui-grid.less:
--------------------------------------------------------------------------------
1 | @import "../../base/fn.less";
2 |
3 | .weui-grids {
4 | border-top: 1rpx solid @weuiGridBorderColor;
5 | border-left: 1rpx solid @weuiGridBorderColor;
6 | overflow: hidden;
7 | }
8 | .weui-grid {
9 | position: relative;
10 | float: left;
11 | padding: 20px 10px;
12 | width: 100% / @weuiGridColumnCount;
13 | box-sizing: border-box;
14 |
15 | border-right: 1rpx solid @weuiGridBorderColor;
16 | border-bottom: 1rpx solid @weuiGridBorderColor;
17 | &_active{
18 | background-color: @weuiBgColorActive;
19 | }
20 | }
21 | .weui-grid__icon {
22 | display: block;
23 | width: @weuiGridIconSize;
24 | height: @weuiGridIconSize;
25 | margin: 0 auto;
26 | }
27 | .weui-grid__label {
28 | margin-top: 5px;
29 | display: block;
30 | text-align: center;
31 | color: @weuiTextColorTitle;
32 | font-size: @weuiGridFontSize;
33 | white-space: nowrap;
34 | text-overflow: ellipsis;
35 | overflow: hidden;
36 | }
37 |
--------------------------------------------------------------------------------
/src/style/widget/weui-animate/weui-animate.less:
--------------------------------------------------------------------------------
1 | @import "../../base/fn.less";
2 |
3 | @keyframes slideUp {
4 | from {
5 | transform: translate3d(0, 100%, 0);
6 | }
7 |
8 | to {
9 | transform: translate3d(0, 0, 0);
10 | }
11 | }
12 |
13 | .weui-animate-slide-up {
14 | animation: slideUp ease .3s forwards;
15 | }
16 |
17 | @keyframes slideDown {
18 | from {
19 | transform: translate3d(0, 0, 0);
20 | }
21 |
22 | to {
23 | transform: translate3d(0, 100%, 0);
24 | }
25 | }
26 |
27 | .weui-animate-slide-down {
28 | animation: slideDown ease .3s forwards;
29 | }
30 |
31 | @keyframes fadeIn {
32 | from {
33 | opacity: 0;
34 | }
35 | to {
36 | opacity: 1;
37 | }
38 | }
39 |
40 | .weui-animate-fade-in {
41 | animation: fadeIn ease .3s forwards;
42 | }
43 |
44 | @keyframes fadeOut {
45 | from {
46 | opacity: 1;
47 | }
48 | to {
49 | opacity: 0;
50 | }
51 | }
52 |
53 | .weui-animate-fade-out {
54 | animation: fadeOut ease .3s forwards;
55 | }
56 |
--------------------------------------------------------------------------------
/src/style/widget/weui-cell/weui-form/weui-form_common.less:
--------------------------------------------------------------------------------
1 | @import "../../../base/fn.less";
2 |
3 | .weui-cell_input{
4 | padding-top: 0;
5 | padding-bottom: 0;
6 | }
7 | .weui-label{
8 | width:@weuiCellLabelWidth;
9 | .text_wrap();
10 | }
11 | .weui-input{
12 | height: @weuiCellHeightEm;
13 | min-height: @weuiCellHeightEm;
14 | line-height: @weuiCellHeightEm;
15 | }
16 | .weui-toptips{
17 | //display: none; // 通过 wx:if 来控制
18 | position: fixed;
19 | transform: translateZ(0);
20 | top: 0;
21 | left: 0;
22 | right: 0;
23 | padding: 5px;
24 | font-size: 14px;
25 | text-align: center;
26 | color: #FFFFFF;
27 | z-index: 5000;
28 | .text_wrap();
29 | }
30 | .weui-toptips_warn{
31 | background-color: @weuiColorWarn;
32 | }
33 | .weui-textarea{
34 | display: block;
35 | width: 100%;
36 | }
37 | .weui-textarea-counter{
38 | color: @weuiTextColorTips;
39 | text-align: right;
40 | }
41 | .weui-textarea-counter_warn{
42 | color: @weuiTextColorWarn;
43 | }
44 | .weui-cell_warn{
45 | color: @weuiTextColorWarn;
46 | }
47 |
--------------------------------------------------------------------------------
/src/style/widget/weui-page/weui-msg.less:
--------------------------------------------------------------------------------
1 | @import "../../base/fn.less";
2 |
3 | .weui-msg {
4 | padding-top: @weuiMsgPaddingTop;
5 | text-align: center;
6 | }
7 | .weui-msg__link{
8 | display: inline;
9 | color: @weuiLinkColorDefault;
10 | }
11 | .weui-msg__icon-area {
12 | margin-bottom: @weuiMsgIconGap;
13 | }
14 | .weui-msg__text-area {
15 | margin-bottom: @weuiMsgTextGap;
16 | padding:0 20px;
17 | }
18 | .weui-msg__title {
19 | margin-bottom: @weuiMsgTitleGap;
20 | font-weight: 400;
21 | font-size: 20px;
22 | }
23 | .weui-msg__desc {
24 | font-size: 14px;
25 | color: @weuiTextColorGray;
26 | }
27 | .weui-msg__opr-area {
28 | margin-bottom: @weuiMsgOprGap;
29 | }
30 | .weui-msg__extra-area {
31 | margin-bottom: @weuiMsgExtraAreaGap;
32 | font-size: 14px;
33 | color: @weuiTextColorGray;
34 | }
35 |
36 | @media screen and (min-height: @weuiMsgExtraAreaOfMinHeight) {
37 | .weui-msg__extra-area {
38 | position: fixed;
39 | left: 0;
40 | bottom: 0;
41 | width: 100%;
42 | text-align: center;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/style/widget/weui-tips/weui-loadmore.less:
--------------------------------------------------------------------------------
1 | @import "../../base/fn.less";
2 |
3 | .weui-loadmore{
4 | width: 65%;
5 | margin:1.5em auto;
6 | line-height: 1.6em;
7 | font-size:14px;
8 | text-align: center;
9 | }
10 | .weui-loadmore__tips{
11 | display: inline-block;
12 | vertical-align: middle;
13 | }
14 | .weui-loadmore_line{
15 | border-top:1px solid @weuiLineColorLight;
16 | margin-top:2.4em;
17 | }
18 | .weui-loadmore__tips_in-line{
19 | position: relative;
20 | top:-.9em;
21 | padding:0 .55em;
22 | background-color: #FFFFFF;
23 | color:@weuiTextColorGray;
24 | }
25 | .weui-loadmore_dot{
26 | }
27 | .weui-loadmore__tips_in-dot{
28 | position: relative;
29 | padding:0 .16em;
30 | width: 4px;
31 | height: 1.6em;
32 | &:before{
33 | content: " ";
34 | position: absolute;
35 | top: 50%;
36 | left: 50%;
37 | margin-top: -1px;
38 | margin-left: -2px;
39 | width: 4px;
40 | height: 4px;
41 | border-radius: 50%;
42 | background-color: @weuiLineColorLight;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/style/weui.less:
--------------------------------------------------------------------------------
1 | @import "base/reset.less";
2 |
3 | @import "widget/weui-cell/weui-cell.less";
4 | @import "widget/weui-cell/weui-access.less";
5 | @import "widget/weui-cell/weui-check.less";
6 | @import "widget/weui-cell/weui-form.less";
7 | @import "widget/weui-cell/weui-switch.less";
8 | @import "widget/weui-cell/weui-uploader.less";
9 |
10 | @import "./widget/weui-page/weui-article.less";
11 | @import "./widget/weui-page/weui-msg.less";
12 |
13 | @import "widget/weui-flex/weui-flex.less";
14 |
15 | @import "widget/weui-button/weui-button.less";
16 |
17 | @import "./widget/weui-agree/weui-agree.less";
18 |
19 | @import "./widget/weui-footer/weui-footer.less";
20 |
21 | @import "./widget/weui-grid/weui-grid.less";
22 |
23 | @import "./widget/weui-loading/weui-loading.less";
24 |
25 | @import "./widget/weui-tips/weui-badge.less";
26 | @import "./widget/weui-tips/weui-loadmore.less";
27 |
28 | @import "./widget/weui-panel/weui-panel.less";
29 |
30 | @import "./widget/weui-media-box/weui-media-box.less";
31 |
32 | @import "./widget/weui-progress/weui-progress.less";
33 |
34 | @import "./widget/weui-tab/weui-tab.less";
35 |
36 | @import "./widget/weui-searchbar/weui-searchbar.less";
37 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | ## 项目概述
3 |
4 | * 产品名称:LaraBBS-WEAPP
5 | * 项目代号:larabbs-weapp
6 |
7 | LaraBBS 是一个简洁的论坛应用,使用 Laravel5.5 编写而成。一步步开发此项目的教程请见 [《Web 开发实战进阶 - 从零开始构建论坛系统》](https://laravel-china.org/topics/6592)。LaraBBS-WEAPP 是利用 LaraBBS 接口实现的小程序,教程请见 [《Laravel 微信小程序开发》](https://laravel-china.org/courses/laravel-weapp)
8 |
9 |
10 | ## 在线体验
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | ## 功能如下
21 |
22 | - 用户认证 —— 注册、登录、退出;
23 | - 个人页面 —— 用户个人信息,编辑资料,上传头像;
24 | - 用户授权 —— 作者才能删除自己的内容;
25 | - 话题 —— 列表,详情,删除;
26 | - 话题回复 —— 列表,发布,删除;
27 | - 消息通知 —— 轮训显示消息提示,消息列表,标记已读;
28 | - 用户权限;
29 | - 页面分享;
30 |
31 |
32 | ## 运行环境要求
33 |
34 | - WePY 1.7+
35 |
36 | ## 开发环境部署/安装
37 |
38 | 本项目代码使用 WePY 框架 [WePY](https://github.com/Tencent/wepy) 开发
39 |
40 | ### 基础安装
41 |
42 | #### 1. 克隆源代码
43 |
44 | 克隆 `larabbs-weapp` 源代码到本地:
45 |
46 | > git clone git@github.com:summerblue/larabbs-weapp.git
47 |
48 | #### 2. 安装扩展包依赖
49 |
50 | ```
51 | > yarn
52 | > yarn global add wepy-cli
53 | ```
54 |
55 | #### 3. 编译
56 |
57 | ```
58 | > npm run build
59 | ```
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/src/components/list.wpy:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | {{item.id}}: {{item.title}}
17 |
18 |
19 |
20 |
21 |
56 |
--------------------------------------------------------------------------------
/src/pages/topics/userIndex.wpy:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
48 |
--------------------------------------------------------------------------------
/src/style/base/mixin/setArrow.less:
--------------------------------------------------------------------------------
1 | ._setArrow(@arrowsize, @borderColor, @borderWidth){
2 | display: inline-block;
3 | height: @arrowsize;
4 | width: @arrowsize;
5 | border-width: @borderWidth @borderWidth 0 0;
6 | border-color: @borderColor;
7 | border-style: solid;
8 | }
9 |
10 | .setArrow(@direction, @arrowsize, @borderColor, @borderWidth) when (@direction = top) {
11 | ._setArrow(@arrowsize, @borderColor, @borderWidth);
12 | transform: matrix(0.71,-0.71,0.71,0.71,0,0); // rotate(-45deg)
13 | }
14 |
15 | .setArrow(@direction, @arrowsize, @borderColor,@borderWidth) when (@direction = right) {
16 | ._setArrow(@arrowsize, @borderColor, @borderWidth);
17 | transform: matrix(0.71,0.71,-0.71,0.71,0,0); // rotate(45deg);
18 |
19 | position: relative;
20 | top: -2px;
21 | }
22 |
23 | .setArrow(@direction, @arrowsize, @borderColor,@borderWidth) when (@direction = down) {
24 | ._setArrow(@arrowsize, @borderColor, @borderWidth);
25 | transform: matrix(-0.71,0.71,-0.71,-0.71,0,0); // rotate(135deg);
26 |
27 | position: relative;
28 | top: -3px;
29 | }
30 |
31 | .setArrow(@direction, @arrowsize, @borderColor,@borderWidth) when (@direction = left) {
32 | ._setArrow(@arrowsize, @borderColor, @borderWidth);
33 | transform: matrix(-0.71,-0.71,0.71,-0.71,0,0); // rotate(-135deg);
34 |
35 | position: relative;
36 | top: -2px;
37 | }
--------------------------------------------------------------------------------
/src/components/wepy-list.wpy:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | {{item.id}}: {{item.title}}
17 |
18 |
19 |
20 |
21 |
56 |
--------------------------------------------------------------------------------
/src/style/widget/weui-cell/weui-cell.less:
--------------------------------------------------------------------------------
1 | @import "../../base/fn.less";
2 |
3 | .weui-cells {
4 | position: relative;
5 | margin-top: @weuiCellsMarginTop;
6 | background-color: @weuiCellBg;
7 | line-height: @weuiCellLineHeight;
8 | font-size: @weuiCellFontSize; //cell中间有效高度23px,跟客户端默认图标尺寸一致
9 | &:before {
10 | .setTopLine(@weuiCellBorderColor);
11 | }
12 | &:after {
13 | .setBottomLine(@weuiCellBorderColor);
14 | }
15 | }
16 |
17 | .weui-cells__title {
18 | margin-top: .77em; // 15px - 行高
19 | margin-bottom: .3em; // 8px - 行高
20 | padding-left: @weuiCellGapH;
21 | padding-right: @weuiCellGapH;
22 | color: @weuiTextColorGray;
23 | font-size: @weuiCellTipsFontSize;
24 | }
25 | .weui-cells_after-title{
26 | margin-top: 0;
27 | }
28 |
29 | .weui-cells__tips {
30 | margin-top: .3em; // 8px - 行高
31 | color: @weuiTextColorGray;
32 | padding-left: @weuiCellGapH;
33 | padding-right: @weuiCellGapH;
34 | font-size: @weuiCellTipsFontSize;
35 | }
36 |
37 | .weui-cell {
38 | padding: @weuiCellGapV @weuiCellGapH;
39 | position: relative; //这个是为了兼容cells容器onepx方案被before挡住而做的
40 | display: flex;
41 | align-items: center;
42 | &:before {
43 | .setTopLine(@weuiCellBorderColor);
44 | left: @weuiCellGapH;
45 | }
46 | &:first-child {
47 | &:before {
48 | display: none;
49 | }
50 | }
51 | }
52 | .weui-cell_active {
53 | background-color: @weuiCellActiveBg;
54 | }
55 | .weui-cell_primary{
56 | align-items: flex-start;
57 | }
58 | .weui-cell__bd{
59 | flex: 1;
60 | }
61 | .weui-cell__ft {
62 | text-align: right;
63 | color: @weuiTextColorGray;
64 | }
65 |
--------------------------------------------------------------------------------
/src/style/widget/weui-searchbar/weui-searchbar.less:
--------------------------------------------------------------------------------
1 | @import "../../base/fn.less";
2 |
3 | @weuiSearchBarHeight: 28px;
4 |
5 | .weui-search-bar {
6 | position: relative;
7 | padding: 8px 10px;
8 | display: flex;
9 | box-sizing: border-box;
10 | background-color: #EFEFF4;
11 | border-top: 1rpx solid #D7D6DC;
12 | border-bottom: 1rpx solid #D7D6DC;
13 | }
14 | .weui-icon-search {
15 | margin-right: 8px;
16 | font-size:inherit;
17 | }
18 | .weui-icon-search_in-box {
19 | position: absolute;
20 | left: 10px;
21 | top: 7px;
22 | }
23 | .weui-search-bar__text{
24 | display: inline-block;
25 | font-size: 14px;
26 | vertical-align: middle;
27 | }
28 | .weui-search-bar__form {
29 | position: relative;
30 | flex: auto;
31 | border-radius: 5px;
32 | background: #FFFFFF;
33 | border: 1rpx solid #E6E6EA;
34 | }
35 | .weui-search-bar__box {
36 | position: relative;
37 | padding-left: 30px;
38 | padding-right: 30px;
39 | width: 100%;
40 | box-sizing: border-box;
41 | z-index: 1;
42 | }
43 | .weui-search-bar__input {
44 | height: @weuiSearchBarHeight;
45 | line-height: @weuiSearchBarHeight;
46 | font-size: 14px;
47 | }
48 | .weui-icon-clear {
49 | position: absolute;
50 | top: 0;
51 | right: 0;
52 | padding: 7px 8px;
53 | font-size: 0;
54 | }
55 | .weui-search-bar__label {
56 | position: absolute;
57 | top: 0;
58 | right: 0;
59 | bottom: 0;
60 | left: 0;
61 | z-index: 2;
62 | border-radius: 3px;
63 | text-align: center;
64 | color: #9B9B9B;
65 | background: #FFFFFF;
66 | line-height: @weuiSearchBarHeight;
67 | }
68 | .weui-search-bar__cancel-btn {
69 | margin-left: 10px;
70 | line-height: @weuiSearchBarHeight;
71 | color: #09BB07;
72 | white-space: nowrap;
73 | }
74 |
--------------------------------------------------------------------------------
/src/style/widget/weui-media-box/weui-media-box.less:
--------------------------------------------------------------------------------
1 | @import "../../base/fn.less";
2 |
3 | .weui-media-box {
4 | padding: 15px;
5 | position: relative;
6 | &:before {
7 | .setTopLine(@weuiLineColorLight);
8 | left: 15px;
9 | }
10 | &:first-child {
11 | &:before {
12 | display: none
13 | }
14 | }
15 | }
16 | .weui-media-box__title {
17 | font-weight: 400;
18 | font-size: 17px;
19 | .ellipsis();
20 | word-wrap: break-word;
21 | word-break: break-all;
22 | }
23 | .weui-media-box__desc {
24 | color: @weuiTextColorGray;
25 | font-size: 13px;
26 | line-height: 1.2;
27 | .ellipsisLn(2);
28 | }
29 | .weui-media-box__info {
30 | margin-top: 15px;
31 | padding-bottom: 5px;
32 | font-size: 13px;
33 | color: #CECECE;
34 | line-height: 1em;
35 | list-style: none;
36 | overflow: hidden;
37 | }
38 | .weui-media-box__info__meta {
39 | float: left;
40 | padding-right: 1em;
41 | }
42 | .weui-media-box__info__meta_extra {
43 | padding-left: 1em;
44 | border-left: 1px solid #CECECE;
45 | }
46 | .weui-media-box_text {
47 | }
48 | .weui-media-box__title_in-text {
49 | margin-bottom: 8px;
50 | }
51 | .weui-media-box_appmsg {
52 | display: flex;
53 | align-items: center;
54 | }
55 | .weui-media-box__thumb {
56 | width: 100%;
57 | height: 100%;
58 | vertical-align: top;
59 | }
60 | .weui-media-box__hd_in-appmsg {
61 | margin-right: .8em;
62 | width: 60px;
63 | height: 60px;
64 | line-height: 60px;
65 | text-align: center;
66 | }
67 | .weui-media-box__bd_in-appmsg {
68 | flex: 1;
69 | min-width: 0;
70 | }
71 | .weui-media-box_small-appmsg {
72 | padding: 0;
73 | }
74 | .weui-cells_in-small-appmsg {
75 | margin-top: 0;
76 | &:before {
77 | display: none;
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "larabbs-weapp",
3 | "version": "0.0.1",
4 | "description": "larabbs weapp project",
5 | "main": "dist/app.js",
6 | "scripts": {
7 | "dev": "wepy build --watch",
8 | "build": "cross-env NODE_ENV=production wepy build --no-cache",
9 | "dev:web": "wepy build --output web",
10 | "clean": "find ./dist -maxdepth 1 -not -name 'project.config.json' -not -name 'dist' | xargs rm -rf",
11 | "test": "echo \"Error: no test specified\" && exit 1",
12 | "eslint": "eslint --fix --ext .js,.wpy src"
13 | },
14 | "wepy": {
15 | "module-a": false,
16 | "./src/components/list": "./src/components/wepy-list.wpy"
17 | },
18 | "author": "liyu001989@gmail.com",
19 | "license": "MIT",
20 | "dependencies": {
21 | "moment": "^2.21.0",
22 | "npm": "^5.8.0",
23 | "promise-polyfill": "^7.1.0",
24 | "redux": "^3.7.2",
25 | "redux-actions": "^2.2.1",
26 | "redux-promise": "^0.5.3",
27 | "wepy": "^1.7.1",
28 | "wepy-async-function": "^1.4.5",
29 | "wepy-com-toast": "^1.0.2",
30 | "wepy-plugin-imagemin": "^1.5.3",
31 | "wepy-plugin-replace": "^1.5.10",
32 | "wepy-redux": "^1.5.3"
33 | },
34 | "devDependencies": {
35 | "babel-eslint": "^7.2.1",
36 | "babel-plugin-transform-class-properties": "^6.24.1",
37 | "babel-plugin-transform-decorators-legacy": "^1.3.4",
38 | "babel-plugin-transform-export-extensions": "^6.22.0",
39 | "babel-plugin-transform-object-rest-spread": "^6.26.0",
40 | "babel-preset-env": "^1.6.1",
41 | "cross-env": "^5.1.4",
42 | "eslint": "^3.18.0",
43 | "eslint-config-standard": "^7.1.0",
44 | "eslint-friendly-formatter": "^2.0.7",
45 | "eslint-plugin-html": "^2.0.1",
46 | "eslint-plugin-promise": "^3.5.0",
47 | "eslint-plugin-standard": "^2.0.1",
48 | "less": "^3.8.1",
49 | "wepy-compiler-babel": "^1.5.2",
50 | "wepy-compiler-less": "^1.3.10",
51 | "wepy-eslint": "^1.5.3",
52 | "wepy-plugin-uglifyjs": "^1.3.7"
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/style/widget/weui-cell/weui-form/weui-form-preview.less:
--------------------------------------------------------------------------------
1 | @import "../../../base/fn.less";
2 |
3 | .weui-form-preview{
4 | position: relative;
5 | background-color: #FFFFFF;
6 | &:before{
7 | .setTopLine(@weuiCellBorderColor);
8 | }
9 | &:after{
10 | .setBottomLine(@weuiCellBorderColor);
11 | }
12 | }
13 | .weui-form-preview__value{
14 | font-size: 14px;
15 | }
16 | .weui-form-preview__value_in-hd{
17 | font-size: 26px;
18 | }
19 | .weui-form-preview__hd{
20 | position: relative;
21 | padding: @weuiCellGapV @weuiCellGapH;
22 | text-align: right;
23 | line-height: 2.5em;
24 | &:after{
25 | .setBottomLine(@weuiCellBorderColor);
26 | left: @weuiCellGapH;
27 | }
28 | }
29 | .weui-form-preview__bd{
30 | padding: @weuiCellGapV @weuiCellGapH;
31 | font-size: .9em;
32 | text-align: right;
33 | color: @weuiTextColorGray;
34 | line-height: 2;
35 | }
36 | .weui-form-preview__ft{
37 | position: relative;
38 | line-height: 50px;
39 | display: flex;
40 | &:after {
41 | .setTopLine(@weuiDialogLineColor);
42 | }
43 | }
44 | .weui-form-preview__item{
45 | overflow: hidden;
46 | }
47 | .weui-form-preview__label{
48 | float: left;
49 | margin-right: 1em;
50 | min-width: 4em;
51 | color: @weuiTextColorGray;
52 | text-align: justify;
53 | text-align-last: justify;
54 | }
55 | .weui-form-preview__value{
56 | display: block;
57 | overflow: hidden;
58 | word-break:normal;
59 | word-wrap: break-word;
60 | }
61 | .weui-form-preview__btn {
62 | position: relative;
63 | display: block;
64 | flex: 1;
65 | color: @weuiDialogLinkColor;
66 | text-align: center;
67 | &:after {
68 | .setLeftLine(@weuiDialogLineColor);
69 | }
70 | &:first-child {
71 | &:after {
72 | display: none;
73 | }
74 | }
75 | }
76 | .weui-form-preview__btn_active{
77 | background-color: @weuiDialogLinkActiveBc;
78 | }
79 | .weui-form-preview__btn_default {
80 | color: @weuiTextColorGray;
81 | }
82 | .weui-form-preview__btn_primary {
83 | color: #0BB20C;
84 | }
85 |
--------------------------------------------------------------------------------
/wepy.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | var prod = process.env.NODE_ENV === 'production';
3 |
4 | module.exports = {
5 | wpyExt: '.wpy',
6 | eslint: true,
7 | cliLogs: !prod,
8 | build: {
9 | web: {
10 | htmlTemplate: path.join('src', 'index.template.html'),
11 | htmlOutput: path.join('web', 'index.html'),
12 | jsOutput: path.join('web', 'index.js')
13 | }
14 | },
15 | resolve: {
16 | alias: {
17 | counter: path.join(__dirname, 'src/components/counter'),
18 | '@': path.join(__dirname, 'src')
19 | },
20 | aliasFields: ['wepy'],
21 | modules: ['node_modules']
22 | },
23 | compilers: {
24 | less: {
25 | compress: prod
26 | },
27 | /*sass: {
28 | outputStyle: 'compressed'
29 | },*/
30 | babel: {
31 | sourceMap: true,
32 | presets: [
33 | 'env'
34 | ],
35 | plugins: [
36 | 'transform-class-properties',
37 | 'transform-decorators-legacy',
38 | 'transform-object-rest-spread',
39 | 'transform-export-extensions',
40 | ]
41 | }
42 | },
43 | plugins: {
44 | replace: {
45 | filter: /\.js$/,
46 | config: {
47 | find: /__BASE_URL__/g,
48 | replace: prod ? "'https://weapp.laravel-china.org/api'" : "'http://larabbs.test/api'"
49 | }
50 | }
51 | },
52 | appConfig: {
53 | noPromiseAPI: ['createSelectorQuery']
54 | }
55 | }
56 |
57 | if (prod) {
58 |
59 | // 压缩sass
60 | // module.exports.compilers['sass'] = {outputStyle: 'compressed'}
61 |
62 | // 压缩js
63 | module.exports.plugins = {
64 | uglifyjs: {
65 | filter: /\.js$/,
66 | config: {
67 | }
68 | },
69 | imagemin: {
70 | filter: /\.(jpg|png|jpeg)$/,
71 | config: {
72 | jpg: {
73 | quality: 80
74 | },
75 | png: {
76 | quality: 80
77 | }
78 | }
79 | },
80 | replace: {
81 | filter: /\.js$/,
82 | config: {
83 | find: /__BASE_URL__/g,
84 | replace: prod ? "'https://weapp.liyu.wiki/api'" : "'http://larabbs.test/api'"
85 | }
86 | }
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/pages/replies/userIndex.wpy:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | {{ reply.topic.title }}
22 |
23 |
24 | {{ reply.created_at_diff }}
25 |
26 |
27 |
28 |
29 |
30 | 没有更多数据
31 |
32 |
33 |
34 |
35 |
36 |
37 |
57 |
--------------------------------------------------------------------------------
/src/components/counter.wpy:
--------------------------------------------------------------------------------
1 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | {{num}}
25 | {{stateNum}}
26 | {{asyncNum}}
27 |
28 |
29 |
88 |
--------------------------------------------------------------------------------
/src/style/widget/weui-cell/weui-uploader.less:
--------------------------------------------------------------------------------
1 | @import "../../base/fn.less";
2 |
3 | .weui-uploader{}
4 | .weui-uploader__hd{
5 | display: flex;
6 | padding-bottom: @weuiCellGapV;
7 | align-items: center;
8 | }
9 | .weui-uploader__title{
10 | flex: 1;
11 | }
12 | .weui-uploader__info{
13 | color: @weuiTextColorTips;
14 | }
15 | .weui-uploader__bd{
16 | margin-bottom: @weuiCellGapH - (@weuiCellGapV + @weuiUploaderFileSpacing);
17 | margin-right: -@weuiUploaderFileSpacing;
18 | overflow: hidden;
19 | }
20 | .weui-uploader__file{
21 | float: left;
22 | margin-right: @weuiUploaderFileSpacing;
23 | margin-bottom: @weuiUploaderFileSpacing;
24 | }
25 | .weui-uploader__img{
26 | display: block;
27 | width: @weuiUploaderSize;
28 | height: @weuiUploaderSize;
29 | }
30 | .weui-uploader__file_status{
31 | position: relative;
32 | &:before{
33 | content: " ";
34 | position: absolute;
35 | top: 0;
36 | right: 0;
37 | bottom: 0;
38 | left: 0;
39 | background-color: rgba(0, 0, 0, .5);
40 | }
41 | }
42 | .weui-uploader__file-content{
43 | position: absolute;
44 | top: 50%;
45 | left: 50%;
46 | transform: translate(-50%, -50%);
47 | color: #FFFFFF;
48 | }
49 | .weui-uploader__input-box{
50 | float:left;
51 | position: relative;
52 | margin-right: @weuiUploaderFileSpacing;
53 | margin-bottom: @weuiUploaderFileSpacing;
54 | width: @weuiUploaderSize - @weuiUploaderBorderWidth * 2;
55 | height: @weuiUploaderSize - @weuiUploaderBorderWidth * 2;
56 | border: @weuiUploaderBorderWidth solid @weuiUploaderBorderColor;
57 | &:before, &:after{
58 | content: " ";
59 | position: absolute;
60 | top: 50%;
61 | left: 50%;
62 | transform: translate(-50%, -50%);
63 | background-color: @weuiUploaderBorderColor;
64 | }
65 | &:before{
66 | width: @weuiUploaderBorderWidth + 1;
67 | height: @weuiUploaderSize / 2;
68 | }
69 | &:after{
70 | width: @weuiUploaderSize / 2;
71 | height: @weuiUploaderBorderWidth + 1;
72 | }
73 | &:active{
74 | border-color: @weuiUploaderActiveBorderColor;
75 | &:before, &:after{
76 | background-color: @weuiUploaderActiveBorderColor;
77 | }
78 | }
79 | }
80 | .weui-uploader__input{
81 | position: absolute;
82 | z-index: 1;
83 | top: 0;
84 | left: 0;
85 | width: 100%;
86 | height: 100%;
87 | opacity: 0;
88 | }
89 |
--------------------------------------------------------------------------------
/src/pages/replies/index.wpy:
--------------------------------------------------------------------------------
1 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | {{ reply.user.name }}
34 |
35 |
36 | {{ reply.created_at_diff }}
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | 没有更多数据
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
87 |
--------------------------------------------------------------------------------
/src/pages/replies/create.wpy:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
110 |
--------------------------------------------------------------------------------
/src/mixins/replyMixin.js:
--------------------------------------------------------------------------------
1 | import wepy from 'wepy'
2 | import util from '@/utils/util'
3 | import api from '@/utils/api'
4 |
5 | export default class ReplyMixin extends wepy.mixin {
6 | data = {
7 | // 回复数据
8 | replies: [],
9 | // 是否有更多数据
10 | noMoreData: false,
11 | // 是否在加载中
12 | isLoading: false,
13 | // 当前页数
14 | page: 1
15 | }
16 | // 获取话题回复
17 | async getReplies(reset = false) {
18 | try {
19 | // 请求话题回复接口
20 | let repliesResponse = await api.request({
21 | url: this.requestData.url,
22 | data: {
23 | page: this.page,
24 | include: this.requestData.include || 'user'
25 | }
26 | })
27 |
28 | if (repliesResponse.statusCode === 200) {
29 | let replies = repliesResponse.data.data
30 |
31 | // 获取当前用户
32 | let user = await this.$parent.getCurrentUser()
33 | // 格式化回复创建时间
34 | replies.forEach((reply) => {
35 | // 控制是否可以删除
36 | reply.can_delete = this.canDelete(user, reply)
37 | reply.created_at_diff = util.diffForHumans(reply.created_at)
38 | })
39 | // 如果reset不为true则合并 this.replies;否则直接覆盖
40 | this.replies = reset ? replies : this.replies.concat(replies)
41 |
42 | let pagination = repliesResponse.data.meta.pagination
43 |
44 | // 根据分页数据判断是否有更多数据
45 | if (pagination.current_page === pagination.total_pages) {
46 | this.noMoreData = true
47 | }
48 | this.$apply()
49 | }
50 |
51 | return repliesResponse
52 | } catch (err) {
53 | console.log(err)
54 | wepy.showModal({
55 | title: '提示',
56 | content: '服务器错误,请联系管理员'
57 | })
58 | }
59 | }
60 | canDelete(user, reply) {
61 | if (!user) {
62 | return false
63 | }
64 |
65 | // 用户未回复发布者 或 有管理内容权限
66 | return (reply.user_id === user.id) || this.$parent.can('manage_contents')
67 | }
68 | async onPullDownRefresh() {
69 | this.noMoreData = false
70 | this.page = 1
71 | await this.getReplies(true)
72 | wepy.stopPullDownRefresh()
73 | }
74 | async onReachBottom () {
75 | // 如果没有更多数据,或者正在加载,直接返回
76 | if (this.noMoreData || this.isLoading) {
77 | return
78 | }
79 | // 设置为加载中
80 | this.isLoading = true
81 | this.page = this.page + 1
82 | await this.getReplies()
83 | this.isLoading = false
84 | this.$apply()
85 | }
86 | methods = {
87 | // 删除回复
88 | async deleteReply(topicId, replyId) {
89 | // 确认是否删除
90 | let res = await wepy.showModal({
91 | title: '确认删除',
92 | content: '您确认删除该回复吗',
93 | confirmText: '删除',
94 | cancelText: '取消'
95 | })
96 |
97 | // 点击取消后返回
98 | if (!res.confirm) {
99 | return
100 | }
101 | try {
102 | // 调用接口删除回复
103 | let deleteResponse = await api.authRequest({
104 | url: 'topics/' + topicId + '/replies/' + replyId,
105 | method: 'DELETE'
106 | })
107 |
108 | // 删除成功
109 | if (deleteResponse.statusCode === 204) {
110 | wepy.showToast({
111 | title: '删除成功',
112 | icon: 'success',
113 | duration: 2000
114 | })
115 | // 将删除了的回复移除
116 | this.replies = this.replies.filter((reply) => reply.id !== replyId)
117 | this.$apply()
118 | }
119 |
120 | return deleteResponse
121 | } catch (err) {
122 | console.log(err)
123 | wepy.showModal({
124 | title: '提示',
125 | content: '服务器错误,请联系管理员'
126 | })
127 | }
128 | }
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/src/pages/users/show.wpy:
--------------------------------------------------------------------------------
1 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | {{ user.name }}
32 | {{ user.introduction }}
33 | 邮箱:{{ user.email }}
34 |
35 | 注册于:{{ user.created_at_diff }}
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | Ta 的话题
44 |
45 |
46 |
47 |
48 |
49 | Ta 的回复
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
115 |
--------------------------------------------------------------------------------
/src/pages/users/me.wpy:
--------------------------------------------------------------------------------
1 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | {{ user.name }}
31 | {{ user.introduction || '' }}
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 | 未登录
42 |
43 |
44 |
45 |
46 |
47 |
48 | 我的消息
49 | {{ unreadCount }}
50 |
51 |
52 |
53 |
54 |
55 |
56 | 我的话题
57 |
58 |
59 |
60 |
61 |
62 | 我的回复
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
114 |
--------------------------------------------------------------------------------
/src/style/widget/weui-loading/weui-loading.less:
--------------------------------------------------------------------------------
1 | @import "../../base/fn.less";
2 |
3 | .weui-loading {
4 | margin: 0 5px;
5 | width:20px;
6 | height:20px;
7 | display: inline-block;
8 | vertical-align: middle;
9 | animation: weuiLoading 1s steps(12, end) infinite;
10 | background: transparent url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAiIGhlaWdodD0iMTIwIiB2aWV3Qm94PSIwIDAgMTAwIDEwMCI+PHBhdGggZmlsbD0ibm9uZSIgZD0iTTAgMGgxMDB2MTAwSDB6Ii8+PHJlY3Qgd2lkdGg9IjciIGhlaWdodD0iMjAiIHg9IjQ2LjUiIHk9IjQwIiBmaWxsPSIjRTlFOUU5IiByeD0iNSIgcnk9IjUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAgLTMwKSIvPjxyZWN0IHdpZHRoPSI3IiBoZWlnaHQ9IjIwIiB4PSI0Ni41IiB5PSI0MCIgZmlsbD0iIzk4OTY5NyIgcng9IjUiIHJ5PSI1IiB0cmFuc2Zvcm09InJvdGF0ZSgzMCAxMDUuOTggNjUpIi8+PHJlY3Qgd2lkdGg9IjciIGhlaWdodD0iMjAiIHg9IjQ2LjUiIHk9IjQwIiBmaWxsPSIjOUI5OTlBIiByeD0iNSIgcnk9IjUiIHRyYW5zZm9ybT0icm90YXRlKDYwIDc1Ljk4IDY1KSIvPjxyZWN0IHdpZHRoPSI3IiBoZWlnaHQ9IjIwIiB4PSI0Ni41IiB5PSI0MCIgZmlsbD0iI0EzQTFBMiIgcng9IjUiIHJ5PSI1IiB0cmFuc2Zvcm09InJvdGF0ZSg5MCA2NSA2NSkiLz48cmVjdCB3aWR0aD0iNyIgaGVpZ2h0PSIyMCIgeD0iNDYuNSIgeT0iNDAiIGZpbGw9IiNBQkE5QUEiIHJ4PSI1IiByeT0iNSIgdHJhbnNmb3JtPSJyb3RhdGUoMTIwIDU4LjY2IDY1KSIvPjxyZWN0IHdpZHRoPSI3IiBoZWlnaHQ9IjIwIiB4PSI0Ni41IiB5PSI0MCIgZmlsbD0iI0IyQjJCMiIgcng9IjUiIHJ5PSI1IiB0cmFuc2Zvcm09InJvdGF0ZSgxNTAgNTQuMDIgNjUpIi8+PHJlY3Qgd2lkdGg9IjciIGhlaWdodD0iMjAiIHg9IjQ2LjUiIHk9IjQwIiBmaWxsPSIjQkFCOEI5IiByeD0iNSIgcnk9IjUiIHRyYW5zZm9ybT0icm90YXRlKDE4MCA1MCA2NSkiLz48cmVjdCB3aWR0aD0iNyIgaGVpZ2h0PSIyMCIgeD0iNDYuNSIgeT0iNDAiIGZpbGw9IiNDMkMwQzEiIHJ4PSI1IiByeT0iNSIgdHJhbnNmb3JtPSJyb3RhdGUoLTE1MCA0NS45OCA2NSkiLz48cmVjdCB3aWR0aD0iNyIgaGVpZ2h0PSIyMCIgeD0iNDYuNSIgeT0iNDAiIGZpbGw9IiNDQkNCQ0IiIHJ4PSI1IiByeT0iNSIgdHJhbnNmb3JtPSJyb3RhdGUoLTEyMCA0MS4zNCA2NSkiLz48cmVjdCB3aWR0aD0iNyIgaGVpZ2h0PSIyMCIgeD0iNDYuNSIgeT0iNDAiIGZpbGw9IiNEMkQyRDIiIHJ4PSI1IiByeT0iNSIgdHJhbnNmb3JtPSJyb3RhdGUoLTkwIDM1IDY1KSIvPjxyZWN0IHdpZHRoPSI3IiBoZWlnaHQ9IjIwIiB4PSI0Ni41IiB5PSI0MCIgZmlsbD0iI0RBREFEQSIgcng9IjUiIHJ5PSI1IiB0cmFuc2Zvcm09InJvdGF0ZSgtNjAgMjQuMDIgNjUpIi8+PHJlY3Qgd2lkdGg9IjciIGhlaWdodD0iMjAiIHg9IjQ2LjUiIHk9IjQwIiBmaWxsPSIjRTJFMkUyIiByeD0iNSIgcnk9IjUiIHRyYW5zZm9ybT0icm90YXRlKC0zMCAtNS45OCA2NSkiLz48L3N2Zz4=) no-repeat;
11 | background-size: 100%;
12 | &.weui-loading_transparent{
13 | background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120' viewBox='0 0 100 100'%3E%3Cpath fill='none' d='M0 0h100v100H0z'/%3E%3Crect xmlns='http://www.w3.org/2000/svg' width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.56)' rx='5' ry='5' transform='translate(0 -30)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.5)' rx='5' ry='5' transform='rotate(30 105.98 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.43)' rx='5' ry='5' transform='rotate(60 75.98 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.38)' rx='5' ry='5' transform='rotate(90 65 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.32)' rx='5' ry='5' transform='rotate(120 58.66 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.28)' rx='5' ry='5' transform='rotate(150 54.02 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.25)' rx='5' ry='5' transform='rotate(180 50 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.2)' rx='5' ry='5' transform='rotate(-150 45.98 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.17)' rx='5' ry='5' transform='rotate(-120 41.34 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.14)' rx='5' ry='5' transform='rotate(-90 35 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.1)' rx='5' ry='5' transform='rotate(-60 24.02 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.03)' rx='5' ry='5' transform='rotate(-30 -5.98 65)'/%3E%3C/svg%3E");
14 | }
15 | }
16 |
17 | @keyframes weuiLoading {
18 | 0% {
19 | transform: rotate3d(0, 0, 1, 0deg);
20 | }
21 |
22 | 100% {
23 | transform: rotate3d(0, 0, 1, 360deg);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/pages/auth/login.wpy:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 | {{ errorMessage }}
10 |
11 | Larabbs 用户登录
12 |
13 |
14 |
15 | 用户名
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | 密码
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | 如果你还没有 Larabbs 用户可以 注册新用户
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
137 |
--------------------------------------------------------------------------------
/src/components/topicList.wpy:
--------------------------------------------------------------------------------
1 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | {{ topic.title }}
35 |
36 |
37 | {{ topic.category.name }} •
38 | {{ topic.user.name }} •
39 | {{ topic.updated_at_diff }}
40 |
41 |
42 | {{ topic.reply_count }}
43 |
44 |
45 |
46 | 没有更多数据
47 |
48 |
49 |
50 |
51 |
138 |
--------------------------------------------------------------------------------
/src/utils/api.js:
--------------------------------------------------------------------------------
1 | import wepy from 'wepy'
2 |
3 | const host = __BASE_URL__
4 |
5 | // 普通请求
6 | const request = async (options, showLoading = true) => {
7 | if (typeof options === 'string') {
8 | options = {
9 | url: options
10 | }
11 | }
12 | // 显示加载中
13 | if (showLoading) {
14 | wepy.showLoading({title: '加载中'})
15 | }
16 | // 拼接请求地址
17 | options.url = host + '/' + options.url
18 | // 调用小程序的 request 方法
19 | let response = await wepy.request(options)
20 |
21 | if (showLoading) {
22 | // 隐藏加载中
23 | wepy.hideLoading()
24 | }
25 |
26 | // 服务器异常后给与提示
27 | if (response.statusCode === 500) {
28 | wepy.showModal({
29 | title: '提示',
30 | content: '服务器错误,请联系管理员或重试'
31 | })
32 | }
33 | return response
34 | }
35 |
36 | // 登录
37 | const login = async (params = {}) => {
38 | // code 只能使用一次,所以每次单独调用
39 | let loginData = await wepy.login()
40 |
41 | // 参数中增加code
42 | params.code = loginData.code
43 |
44 | // 接口请求 weapp/authorizations
45 | let authResponse = await request({
46 | url: 'weapp/authorizations',
47 | data: params,
48 | method: 'POST'
49 | })
50 |
51 | // 登录成功,记录 token 信息
52 | if (authResponse.statusCode === 201) {
53 | wepy.setStorageSync('access_token', authResponse.data.access_token)
54 | wepy.setStorageSync('access_token_expired_at', new Date().getTime() + authResponse.data.expires_in * 1000)
55 | }
56 |
57 | return authResponse
58 | }
59 |
60 | // 刷新 Token
61 | const refreshToken = async (accessToken) => {
62 | // 请求刷新接口
63 | let refreshResponse = await wepy.request({
64 | url: host + '/' + 'authorizations/current',
65 | method: 'PUT',
66 | header: {
67 | 'Authorization': 'Bearer ' + accessToken
68 | }
69 | })
70 |
71 | // 刷新成功状态码为 200
72 | if (refreshResponse.statusCode === 200) {
73 | // 将 Token 及过期时间保存在 storage 中
74 | wepy.setStorageSync('access_token', refreshResponse.data.access_token)
75 | wepy.setStorageSync('access_token_expired_at', new Date().getTime() + refreshResponse.data.expires_in * 1000)
76 | }
77 |
78 | return refreshResponse
79 | }
80 |
81 | // 获取 Token
82 | const getToken = async (options) => {
83 | // 从缓存中取出 Token
84 | let accessToken = wepy.getStorageSync('access_token')
85 | let expiredAt = wepy.getStorageSync('access_token_expired_at')
86 |
87 | // 如果 token 过期了,则调用刷新方法
88 | if (accessToken && new Date().getTime() > expiredAt) {
89 | let refreshResponse = await refreshToken(accessToken)
90 |
91 | // 刷新成功
92 | if (refreshResponse.statusCode === 200) {
93 | accessToken = refreshResponse.data.access_token
94 | } else {
95 | // 刷新失败了,重新调用登录方法,设置 Token
96 | let authResponse = await login()
97 | if (authResponse.statusCode === 201) {
98 | accessToken = authResponse.data.access_token
99 | }
100 | }
101 | }
102 |
103 | return accessToken
104 | }
105 |
106 | // 带身份认证的请求
107 | const authRequest = async (options, showLoading = true) => {
108 | if (typeof options === 'string') {
109 | options = {
110 | url: options
111 | }
112 | }
113 | // 获取Token
114 | let accessToken = await getToken()
115 |
116 | // 将 Token 设置在 header 中
117 | let header = options.header || {}
118 | header.Authorization = 'Bearer ' + accessToken
119 | options.header = header
120 |
121 | return request(options, showLoading)
122 | }
123 |
124 | // 退出登录
125 | const logout = async (params = {}) => {
126 | let accessToken = wepy.getStorageSync('access_token')
127 | // 调用删除 Token 接口,让 Token 失效
128 | let logoutResponse = await wepy.request({
129 | url: host + '/' + 'authorizations/current',
130 | method: 'DELETE',
131 | header: {
132 | 'Authorization': 'Bearer ' + accessToken
133 | }
134 | })
135 |
136 | // 调用接口成功则清空缓存
137 | if (logoutResponse.statusCode === 204) {
138 | wepy.clearStorage()
139 | }
140 |
141 | return logoutResponse
142 | }
143 |
144 | const updateFile = async (options = {}) => {
145 | // 显示loading
146 | wepy.showLoading({title: '上传中'})
147 |
148 | // 获取 token
149 | let accessToken = await getToken()
150 |
151 | // 拼接url
152 | options.url = host + '/' + options.url
153 | let header = options.header || {}
154 | // 将 token 设置在 header 中
155 | header.Authorization = 'Bearer ' + accessToken
156 | options.header = header
157 |
158 | // 上传文件
159 | let response = await wepy.uploadFile(options)
160 |
161 | // 隐藏 loading
162 | wepy.hideLoading()
163 |
164 | return response
165 | }
166 |
167 | export default {
168 | request,
169 | authRequest,
170 | refreshToken,
171 | login,
172 | logout,
173 | updateFile
174 | }
175 |
--------------------------------------------------------------------------------
/src/pages/notifications/index.wpy:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | {{ notification.data.user_name }}
15 | 评论了
16 | {{ notification.data.topic_title }}
17 |
18 |
19 |
20 |
21 | {{ notification.created_at_diff }}
22 |
23 |
24 |
25 |
26 |
27 | 没有更多数据
28 |
29 |
30 |
31 |
32 |
33 |
34 |
140 |
--------------------------------------------------------------------------------
/src/app.wpy:
--------------------------------------------------------------------------------
1 |
33 |
34 |
199 |
--------------------------------------------------------------------------------
/src/pages/topics/index.wpy:
--------------------------------------------------------------------------------
1 |
54 |
55 |
56 |
57 |
58 | {{ currentCategory.name || '话题' }}
59 |
60 |
61 |
62 |
63 |
64 |
65 | 话题
66 |
67 |
68 | {{ category.name }}
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
168 |
--------------------------------------------------------------------------------
/src/pages/users/edit.wpy:
--------------------------------------------------------------------------------
1 |
21 |
22 |
23 |
24 |
78 |
79 |
80 |
81 |
82 |
185 |
--------------------------------------------------------------------------------
/.wepycache:
--------------------------------------------------------------------------------
1 | {"/Users/liyu/Code/larabbs-weapp/node_modules/wepy/lib/wepy.js":1545812947914,"/Users/liyu/Code/larabbs-weapp/node_modules/wepy-async-function/index.js":1545812947986,"/Users/liyu/Code/larabbs-weapp/node_modules/wepy-redux/lib/index.js":1545812948192,"/Users/liyu/Code/larabbs-weapp/node_modules/redux-actions/lib/index.js":1545812947505,"/Users/liyu/Code/larabbs-weapp/node_modules/redux/lib/index.js":1544673483005,"/Users/liyu/Code/larabbs-weapp/node_modules/redux-promise/lib/index.js":1544673484050,"/Users/liyu/Code/larabbs-weapp/node_modules/moment/moment.js":1545812944026,"/Users/liyu/Code/larabbs-weapp/node_modules/moment/locale/zh-cn.js":1545812944026,"/Users/liyu/Code/larabbs-weapp/node_modules/wepy/lib/app.js":1545812947914,"/Users/liyu/Code/larabbs-weapp/node_modules/wepy/lib/page.js":1545812947914,"/Users/liyu/Code/larabbs-weapp/node_modules/wepy/lib/component.js":1545812947914,"/Users/liyu/Code/larabbs-weapp/node_modules/wepy/lib/event.js":1545812947914,"/Users/liyu/Code/larabbs-weapp/node_modules/wepy/lib/base.js":1545812947914,"/Users/liyu/Code/larabbs-weapp/node_modules/wepy/lib/util.js":1545812947914,"/Users/liyu/Code/larabbs-weapp/node_modules/wepy/lib/mixin.js":1545812947914,"/Users/liyu/Code/larabbs-weapp/node_modules/wepy-async-function/global.js":1545812947986,"/Users/liyu/Code/larabbs-weapp/node_modules/promise-polyfill/lib/index.js":1545812947391,"/Users/liyu/Code/larabbs-weapp/node_modules/regenerator-runtime/runtime.js":1543201716605,"/Users/liyu/Code/larabbs-weapp/node_modules/wepy-redux/lib/connect/index.js":1545812948192,"/Users/liyu/Code/larabbs-weapp/node_modules/wepy-redux/lib/store.js":1545812948192,"/Users/liyu/Code/larabbs-weapp/node_modules/wepy-redux/lib/helpers/index.js":1545812948192,"/Users/liyu/Code/larabbs-weapp/node_modules/redux-actions/lib/combineActions.js":1545812947505,"/Users/liyu/Code/larabbs-weapp/node_modules/redux-actions/lib/createAction.js":1545812947505,"/Users/liyu/Code/larabbs-weapp/node_modules/redux-actions/lib/createActions.js":1545812947505,"/Users/liyu/Code/larabbs-weapp/node_modules/redux-actions/lib/createCurriedAction.js":1545812947505,"/Users/liyu/Code/larabbs-weapp/node_modules/redux-actions/lib/handleAction.js":1545812947505,"/Users/liyu/Code/larabbs-weapp/node_modules/redux-actions/lib/handleActions.js":1545812947505,"/Users/liyu/Code/larabbs-weapp/node_modules/redux/lib/createStore.js":1544673483005,"/Users/liyu/Code/larabbs-weapp/node_modules/redux/lib/combineReducers.js":1544673483005,"/Users/liyu/Code/larabbs-weapp/node_modules/redux/lib/bindActionCreators.js":1544673483005,"/Users/liyu/Code/larabbs-weapp/node_modules/redux/lib/applyMiddleware.js":1544673483005,"/Users/liyu/Code/larabbs-weapp/node_modules/redux/lib/compose.js":1544673483005,"/Users/liyu/Code/larabbs-weapp/node_modules/redux/lib/utils/warning.js":1544673483005,"/Users/liyu/Code/larabbs-weapp/node_modules/flux-standard-action/lib/index.js":1544673484135,"/Users/liyu/Code/larabbs-weapp/node_modules/wepy/lib/native.js":1545812947914,"/Users/liyu/Code/larabbs-weapp/node_modules/invariant/browser.js":1543201714316,"/Users/liyu/Code/larabbs-weapp/node_modules/redux-actions/lib/utils/isFunction.js":1545812947505,"/Users/liyu/Code/larabbs-weapp/node_modules/redux-actions/lib/utils/isSymbol.js":1545812947505,"/Users/liyu/Code/larabbs-weapp/node_modules/redux-actions/lib/utils/isEmpty.js":1545812947505,"/Users/liyu/Code/larabbs-weapp/node_modules/redux-actions/lib/utils/toString.js":1545812947505,"/Users/liyu/Code/larabbs-weapp/node_modules/redux-actions/lib/utils/isString.js":1545812947505,"/Users/liyu/Code/larabbs-weapp/node_modules/redux-actions/lib/constants.js":1545812947505,"/Users/liyu/Code/larabbs-weapp/node_modules/redux-actions/lib/utils/identity.js":1545812947505,"/Users/liyu/Code/larabbs-weapp/node_modules/redux-actions/lib/utils/isNull.js":1545812947505,"/Users/liyu/Code/larabbs-weapp/node_modules/redux-actions/lib/utils/isPlainObject.js":1545812947505,"/Users/liyu/Code/larabbs-weapp/node_modules/redux-actions/lib/utils/isArray.js":1545812947505,"/Users/liyu/Code/larabbs-weapp/node_modules/redux-actions/lib/utils/isNil.js":1545812947505,"/Users/liyu/Code/larabbs-weapp/node_modules/redux-actions/lib/utils/getLastElement.js":1545812947505,"/Users/liyu/Code/larabbs-weapp/node_modules/redux-actions/lib/utils/camelCase.js":1545812947505,"/Users/liyu/Code/larabbs-weapp/node_modules/redux-actions/lib/utils/arrayToObject.js":1545812947505,"/Users/liyu/Code/larabbs-weapp/node_modules/redux-actions/lib/utils/flattenActionMap.js":1545812947505,"/Users/liyu/Code/larabbs-weapp/node_modules/redux-actions/lib/utils/unflattenActionCreators.js":1545812947505,"/Users/liyu/Code/larabbs-weapp/node_modules/just-curry-it/index.js":1545812947571,"/Users/liyu/Code/larabbs-weapp/node_modules/redux-actions/lib/utils/isUndefined.js":1545812947505,"/Users/liyu/Code/larabbs-weapp/node_modules/reduce-reducers/lib/index.js":1545812947782,"/Users/liyu/Code/larabbs-weapp/node_modules/redux-actions/lib/utils/isMap.js":1545812947505,"/Users/liyu/Code/larabbs-weapp/node_modules/redux-actions/lib/utils/ownKeys.js":1545812947505,"/Users/liyu/Code/larabbs-weapp/node_modules/redux-actions/lib/utils/flattenReducerMap.js":1545812947505,"/Users/liyu/Code/larabbs-weapp/node_modules/redux-actions/lib/utils/get.js":1545812947505,"/Users/liyu/Code/larabbs-weapp/node_modules/lodash/isPlainObject.js":1543202126725,"/Users/liyu/Code/larabbs-weapp/node_modules/symbol-observable/lib/index.js":1544673483160,"/Users/liyu/Code/larabbs-weapp/node_modules/lodash.isplainobject/index.js":1544673484658,"/Users/liyu/Code/larabbs-weapp/node_modules/to-camel-case/index.js":1545812947731,"/Users/liyu/Code/larabbs-weapp/node_modules/redux-actions/lib/utils/flattenWhenNode.js":1545812947505,"/Users/liyu/Code/larabbs-weapp/node_modules/redux-actions/lib/utils/hasGeneratorInterface.js":1545812947505,"/Users/liyu/Code/larabbs-weapp/node_modules/lodash/_baseGetTag.js":1543202126725,"/Users/liyu/Code/larabbs-weapp/node_modules/lodash/_getPrototype.js":1543202126725,"/Users/liyu/Code/larabbs-weapp/node_modules/lodash/isObjectLike.js":1543202126725,"/Users/liyu/Code/larabbs-weapp/node_modules/symbol-observable/lib/ponyfill.js":1544673483160,"/Users/liyu/Code/larabbs-weapp/node_modules/lodash._basefor/index.js":1544673484758,"/Users/liyu/Code/larabbs-weapp/node_modules/lodash.isarguments/index.js":1544673488386,"/Users/liyu/Code/larabbs-weapp/node_modules/lodash.keysin/index.js":1544673489270,"/Users/liyu/Code/larabbs-weapp/node_modules/to-space-case/index.js":1545812947852,"/Users/liyu/Code/larabbs-weapp/node_modules/lodash/_Symbol.js":1543202126725,"/Users/liyu/Code/larabbs-weapp/node_modules/lodash/_getRawTag.js":1543202126725,"/Users/liyu/Code/larabbs-weapp/node_modules/lodash/_objectToString.js":1543202126725,"/Users/liyu/Code/larabbs-weapp/node_modules/lodash/_overArg.js":1543202126725,"/Users/liyu/Code/larabbs-weapp/node_modules/lodash.isarray/index.js":1544673489671,"/Users/liyu/Code/larabbs-weapp/node_modules/to-no-case/index.js":1545812947904,"/Users/liyu/Code/larabbs-weapp/node_modules/lodash/_root.js":1543202126725,"/Users/liyu/Code/larabbs-weapp/node_modules/lodash/_freeGlobal.js":1543202126725}
--------------------------------------------------------------------------------
/src/pages/topics/show.wpy:
--------------------------------------------------------------------------------
1 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | {{ topic.user.name }}
36 | {{ topic.user.introduction }}
37 |
38 |
39 |
40 |
41 |
42 |
43 | {{ topic.title }}
44 |
45 | {{ topic.category.name }} •
46 | {{ topic.updated_at_diff }} •
47 | {{ topic.reply_count }} 次阅读
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | 回复 ({{ topic.reply_count }})
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | {{ reply.user.name }}
67 |
68 |
69 | {{ reply.created_at_diff }}
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 | 查看全部回复
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
232 |
--------------------------------------------------------------------------------
/src/pages/auth/register.wpy:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
78 |
79 |
80 |
81 | {{ errors.captchaValue[0] }}
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
335 |
--------------------------------------------------------------------------------