├── static ├── .gitkeep └── img │ ├── preload.png │ ├── recomend.png │ ├── success.png │ ├── open_in_weixin.png │ └── share_in_weixin.png ├── test ├── unit │ ├── setup.js │ ├── .eslintrc │ ├── specs │ │ └── HelloWorld.spec.js │ └── jest.conf.js └── e2e │ ├── specs │ └── test.js │ ├── custom-assertions │ └── elementCount.js │ ├── nightwatch.conf.js │ └── runner.js ├── .eslintignore ├── config ├── prod.env.js ├── test.env.js ├── dev.env.js └── index.js ├── screenshots ├── fav.png ├── post.png ├── index.png ├── articlelist.png ├── circledetail.png ├── circlelist.png ├── storedetail.png ├── storeindex.png ├── articledetail.png ├── articledetail2.png ├── articledetail3.png ├── circledetail2.png ├── circledetail3.png ├── collectionlist.png ├── collectiondetail.png ├── collectiondetail11.png └── collectiondetail12.png ├── src ├── assets │ └── gotop.png ├── fonts │ ├── MaterialIcons-Regular.eot │ ├── MaterialIcons-Regular.ttf │ ├── Framework7Icons-Regular.eot │ ├── Framework7Icons-Regular.ttf │ ├── Framework7Icons-Regular.woff │ ├── MaterialIcons-Regular.woff │ ├── MaterialIcons-Regular.woff2 │ ├── fontawesome-webfont93e3.eot │ ├── fontawesome-webfont93e3.ttf │ ├── fontawesome-webfont93e3.woff │ ├── fontawesome-webfontd41d.eot │ ├── Framework7Icons-Regular.woff2 │ ├── fontawesome-webfont93e3.woff2 │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.ttf │ ├── glyphicons-halflings-regular.woff │ ├── glyphicons-halflings-regular.woff2 │ └── glyphicons-halflings-regulard41d.eot ├── store │ ├── mutation-types.js │ ├── index.js │ ├── getters.js │ ├── actions.js │ └── mutations.js ├── css │ ├── webuploader │ │ └── webuploader.css │ ├── icons.css │ ├── article.css │ └── webuploader-demo.min.css ├── .project ├── hiapp-pages │ ├── feedback.vue │ ├── home.vue │ ├── language.vue │ ├── publisher.vue │ ├── comment.vue │ ├── profile.vue │ ├── contacts.vue │ ├── about.vue │ ├── settings.vue │ ├── message.vue │ ├── card.vue │ └── post.vue ├── js │ ├── config.js │ ├── myAmap.js │ ├── wxShare.js │ ├── plugin │ │ └── jquery.lazyload.min.js │ └── f7-base.js ├── components │ ├── remote-js.vue │ ├── editor.vue │ └── card.vue ├── pages │ ├── home.vue │ ├── not-found.vue │ ├── my │ │ ├── follow-member.vue │ │ ├── fav-collection.vue │ │ ├── fav-store.vue │ │ ├── fav-goods.vue │ │ ├── fav-article.vue │ │ ├── follow-circle.vue │ │ ├── circletheme.vue │ │ └── fav-circletheme.vue │ ├── form.vue │ ├── circle-list.vue │ ├── collection-list.vue │ ├── article-list.vue │ ├── store-list.vue │ └── store-index.vue ├── main.js ├── App.vue └── router │ └── index.js ├── .editorconfig ├── .gitignore ├── .postcssrc.js ├── .babelrc ├── .project ├── index.html ├── package.json └── README.md /static/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/unit/setup.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | Vue.config.productionTip = false 4 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /src/ 3 | /config/ 4 | /dist/ 5 | /*.js 6 | /test/unit/coverage/ 7 | -------------------------------------------------------------------------------- /config/prod.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | module.exports = { 3 | NODE_ENV: '"production"' 4 | } 5 | -------------------------------------------------------------------------------- /screenshots/fav.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evangui/vue-f7-circle/HEAD/screenshots/fav.png -------------------------------------------------------------------------------- /screenshots/post.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evangui/vue-f7-circle/HEAD/screenshots/post.png -------------------------------------------------------------------------------- /src/assets/gotop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evangui/vue-f7-circle/HEAD/src/assets/gotop.png -------------------------------------------------------------------------------- /screenshots/index.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evangui/vue-f7-circle/HEAD/screenshots/index.png -------------------------------------------------------------------------------- /static/img/preload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evangui/vue-f7-circle/HEAD/static/img/preload.png -------------------------------------------------------------------------------- /static/img/recomend.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evangui/vue-f7-circle/HEAD/static/img/recomend.png -------------------------------------------------------------------------------- /static/img/success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evangui/vue-f7-circle/HEAD/static/img/success.png -------------------------------------------------------------------------------- /test/unit/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "jest": true 4 | }, 5 | "globals": { 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /screenshots/articlelist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evangui/vue-f7-circle/HEAD/screenshots/articlelist.png -------------------------------------------------------------------------------- /screenshots/circledetail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evangui/vue-f7-circle/HEAD/screenshots/circledetail.png -------------------------------------------------------------------------------- /screenshots/circlelist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evangui/vue-f7-circle/HEAD/screenshots/circlelist.png -------------------------------------------------------------------------------- /screenshots/storedetail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evangui/vue-f7-circle/HEAD/screenshots/storedetail.png -------------------------------------------------------------------------------- /screenshots/storeindex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evangui/vue-f7-circle/HEAD/screenshots/storeindex.png -------------------------------------------------------------------------------- /screenshots/articledetail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evangui/vue-f7-circle/HEAD/screenshots/articledetail.png -------------------------------------------------------------------------------- /screenshots/articledetail2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evangui/vue-f7-circle/HEAD/screenshots/articledetail2.png -------------------------------------------------------------------------------- /screenshots/articledetail3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evangui/vue-f7-circle/HEAD/screenshots/articledetail3.png -------------------------------------------------------------------------------- /screenshots/circledetail2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evangui/vue-f7-circle/HEAD/screenshots/circledetail2.png -------------------------------------------------------------------------------- /screenshots/circledetail3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evangui/vue-f7-circle/HEAD/screenshots/circledetail3.png -------------------------------------------------------------------------------- /screenshots/collectionlist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evangui/vue-f7-circle/HEAD/screenshots/collectionlist.png -------------------------------------------------------------------------------- /static/img/open_in_weixin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evangui/vue-f7-circle/HEAD/static/img/open_in_weixin.png -------------------------------------------------------------------------------- /static/img/share_in_weixin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evangui/vue-f7-circle/HEAD/static/img/share_in_weixin.png -------------------------------------------------------------------------------- /screenshots/collectiondetail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evangui/vue-f7-circle/HEAD/screenshots/collectiondetail.png -------------------------------------------------------------------------------- /screenshots/collectiondetail11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evangui/vue-f7-circle/HEAD/screenshots/collectiondetail11.png -------------------------------------------------------------------------------- /screenshots/collectiondetail12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evangui/vue-f7-circle/HEAD/screenshots/collectiondetail12.png -------------------------------------------------------------------------------- /src/fonts/MaterialIcons-Regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evangui/vue-f7-circle/HEAD/src/fonts/MaterialIcons-Regular.eot -------------------------------------------------------------------------------- /src/fonts/MaterialIcons-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evangui/vue-f7-circle/HEAD/src/fonts/MaterialIcons-Regular.ttf -------------------------------------------------------------------------------- /src/fonts/Framework7Icons-Regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evangui/vue-f7-circle/HEAD/src/fonts/Framework7Icons-Regular.eot -------------------------------------------------------------------------------- /src/fonts/Framework7Icons-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evangui/vue-f7-circle/HEAD/src/fonts/Framework7Icons-Regular.ttf -------------------------------------------------------------------------------- /src/fonts/Framework7Icons-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evangui/vue-f7-circle/HEAD/src/fonts/Framework7Icons-Regular.woff -------------------------------------------------------------------------------- /src/fonts/MaterialIcons-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evangui/vue-f7-circle/HEAD/src/fonts/MaterialIcons-Regular.woff -------------------------------------------------------------------------------- /src/fonts/MaterialIcons-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evangui/vue-f7-circle/HEAD/src/fonts/MaterialIcons-Regular.woff2 -------------------------------------------------------------------------------- /src/fonts/fontawesome-webfont93e3.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evangui/vue-f7-circle/HEAD/src/fonts/fontawesome-webfont93e3.eot -------------------------------------------------------------------------------- /src/fonts/fontawesome-webfont93e3.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evangui/vue-f7-circle/HEAD/src/fonts/fontawesome-webfont93e3.ttf -------------------------------------------------------------------------------- /src/fonts/fontawesome-webfont93e3.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evangui/vue-f7-circle/HEAD/src/fonts/fontawesome-webfont93e3.woff -------------------------------------------------------------------------------- /src/fonts/fontawesome-webfontd41d.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evangui/vue-f7-circle/HEAD/src/fonts/fontawesome-webfontd41d.eot -------------------------------------------------------------------------------- /src/fonts/Framework7Icons-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evangui/vue-f7-circle/HEAD/src/fonts/Framework7Icons-Regular.woff2 -------------------------------------------------------------------------------- /src/fonts/fontawesome-webfont93e3.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evangui/vue-f7-circle/HEAD/src/fonts/fontawesome-webfont93e3.woff2 -------------------------------------------------------------------------------- /src/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evangui/vue-f7-circle/HEAD/src/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /src/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evangui/vue-f7-circle/HEAD/src/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /src/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evangui/vue-f7-circle/HEAD/src/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /src/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evangui/vue-f7-circle/HEAD/src/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /src/fonts/glyphicons-halflings-regulard41d.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evangui/vue-f7-circle/HEAD/src/fonts/glyphicons-halflings-regulard41d.eot -------------------------------------------------------------------------------- /src/store/mutation-types.js: -------------------------------------------------------------------------------- 1 | export const INIT_CONTACTS = 'INIT_CONTACTS' 2 | export const INIT_TIMETIME = 'INIT_TIMETIME' 3 | export const UPDATE_TIMETIME = 'UPDATE_TIMETIME' 4 | -------------------------------------------------------------------------------- /config/test.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const merge = require('webpack-merge') 3 | const devEnv = require('./dev.env') 4 | 5 | module.exports = merge(devEnv, { 6 | NODE_ENV: '"testing"' 7 | }) 8 | -------------------------------------------------------------------------------- /config/dev.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const merge = require('webpack-merge') 3 | const prodEnv = require('./prod.env') 4 | 5 | module.exports = merge(prodEnv, { 6 | NODE_ENV: '"development"' 7 | }) 8 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | /dist/ 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | /test/unit/coverage/ 8 | /test/e2e/reports/ 9 | selenium-debug.log 10 | 11 | # Editor directories and files 12 | .idea 13 | .vscode 14 | *.suo 15 | *.ntvs* 16 | *.njsproj 17 | *.sln 18 | -------------------------------------------------------------------------------- /.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | "plugins": { 5 | "postcss-import": {}, 6 | "postcss-url": {}, 7 | // to edit target browsers: use "browserslist" field in package.json 8 | "autoprefixer": {} 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/unit/specs/HelloWorld.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import HelloWorld from '@/components/HelloWorld' 3 | 4 | describe('HelloWorld.vue', () => { 5 | it('should render correct contents', () => { 6 | const Constructor = Vue.extend(HelloWorld) 7 | const vm = new Constructor().$mount() 8 | expect(vm.$el.querySelector('.hello h1').textContent) 9 | .toEqual('Welcome to Your Vue.js App') 10 | }) 11 | }) 12 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "modules": false, 5 | "targets": { 6 | "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] 7 | } 8 | }], 9 | "stage-2" 10 | ], 11 | "plugins": ["transform-vue-jsx", "transform-runtime"], 12 | "env": { 13 | "test": { 14 | "presets": ["env", "stage-2"], 15 | "plugins": ["transform-vue-jsx", "transform-es2015-modules-commonjs", "dynamic-import-node"] 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/e2e/specs/test.js: -------------------------------------------------------------------------------- 1 | // For authoring Nightwatch tests, see 2 | // http://nightwatchjs.org/guide#usage 3 | 4 | module.exports = { 5 | 'default e2e tests': function (browser) { 6 | // automatically uses dev Server port from /config.index.js 7 | // default: http://localhost:8080 8 | // see nightwatch.conf.js 9 | const devServer = browser.globals.devServerURL 10 | 11 | browser 12 | .url(devServer) 13 | .waitForElementVisible('#app', 5000) 14 | .assert.elementPresent('.hello') 15 | .assert.containsText('h1', 'Welcome to Your Vue.js App') 16 | .assert.elementCount('img', 1) 17 | .end() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/css/webuploader/webuploader.css: -------------------------------------------------------------------------------- 1 | .webuploader-container { 2 | position: relative; 3 | } 4 | .webuploader-element-invisible { 5 | position: absolute !important; 6 | clip: rect(1px 1px 1px 1px); /* IE6, IE7 */ 7 | clip: rect(1px,1px,1px,1px); 8 | } 9 | .webuploader-pick { 10 | position: relative; 11 | display: inline-block; 12 | cursor: pointer; 13 | background: #00b7ee; 14 | padding: 10px 15px; 15 | color: #fff; 16 | text-align: center; 17 | border-radius: 3px; 18 | overflow: hidden; 19 | } 20 | .webuploader-pick-hover { 21 | background: #00a2d4; 22 | } 23 | 24 | .webuploader-pick-disable { 25 | opacity: 0.6; 26 | pointer-events:none; 27 | } 28 | 29 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | hnp-vue 4 | 5 | 6 | 7 | 8 | 9 | com.aptana.ide.core.unifiedBuilder 10 | 11 | 12 | 13 | 14 | 15 | com.aptana.projects.webnature 16 | 17 | 18 | 19 | 1522493752490 20 | 21 | 26 22 | 23 | org.eclipse.ui.ide.multiFilter 24 | 1.0-name-matches-false-false-node_modules 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | vue-myproject 4 | 5 | 6 | 7 | 8 | 9 | com.aptana.ide.core.unifiedBuilder 10 | 11 | 12 | 13 | 14 | 15 | com.aptana.projects.webnature 16 | 17 | 18 | 19 | 0 20 | 21 | 26 22 | 23 | org.eclipse.ui.ide.multiFilter 24 | 1.0-name-matches-false-false-node_modules 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 杏运树 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /test/unit/jest.conf.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | module.exports = { 4 | rootDir: path.resolve(__dirname, '../../'), 5 | moduleFileExtensions: [ 6 | 'js', 7 | 'json', 8 | 'vue' 9 | ], 10 | moduleNameMapper: { 11 | '^@/(.*)$': '/src/$1' 12 | }, 13 | transform: { 14 | '^.+\\.js$': '/node_modules/babel-jest', 15 | '.*\\.(vue)$': '/node_modules/vue-jest' 16 | }, 17 | testPathIgnorePatterns: [ 18 | '/test/e2e' 19 | ], 20 | snapshotSerializers: ['/node_modules/jest-serializer-vue'], 21 | setupFiles: ['/test/unit/setup'], 22 | mapCoverage: true, 23 | coverageDirectory: '/test/unit/coverage', 24 | collectCoverageFrom: [ 25 | 'src/**/*.{js,vue}', 26 | '!src/main.js', 27 | '!src/router/index.js', 28 | '!**/node_modules/**' 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /src/hiapp-pages/feedback.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | import * as actions from './actions' 4 | import mutations from './mutations' 5 | import getters from './getters' 6 | 7 | import vuexAlong from 'vuex-along'; 8 | 9 | Vue.use(Vuex) 10 | //vuexAlong.watch(['localNum'],true); 11 | //vuexAlong.watchSession(['sessionNum'],true); 12 | vuexAlong.onlySession(true); 13 | 14 | 15 | const state = { 16 | position: {}, 17 | mutArticlesState: {a:1}, 18 | mutArticleRepliesState: {a:1}, 19 | 20 | mutCollectionsState: {a:1}, 21 | mutCirclesState: {a:1}, 22 | mutThemesState: {a:1}, 23 | mutThemeRepliesState: {a:1}, 24 | mutStoresState: {}, 25 | mutGoodsState: {}, 26 | } 27 | 28 | export default new Vuex.Store({ 29 | // strict: process.env.NODE_ENV !== 'production', 30 | state, 31 | getters, 32 | actions, 33 | mutations, 34 | plugins: [vuexAlong] 35 | }) -------------------------------------------------------------------------------- /src/js/config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 环境相关配置文件 3 | */ 4 | let serverIndex; 5 | switch (document.location.host){ 6 | case 'm2.cattlepie.com': 7 | serverIndex = '2'; 8 | break; 9 | case 'm1.cattlepie.com': 10 | serverIndex = '1'; 11 | break; 12 | case 'm.cattlepie.com': 13 | case 'm.cattlepie.me': 14 | serverIndex = ''; 15 | break; 16 | default: 17 | serverIndex = '2'; 18 | break; 19 | } 20 | 21 | //serverIndex = ''; 22 | var domainSuffix = '.com'; //.me or .com 23 | 24 | var config = { 25 | domainSuffix : domainSuffix, 26 | apiUrl: '//apidev' + serverIndex + '.cattlepie' + domainSuffix + '/', 27 | siteUrl: '//' + document.location.host + '/', 28 | userSiteUrl: '//m' + serverIndex + '.cattlepie' + domainSuffix + '/', 29 | shopSiteUrl: '//m' + serverIndex + '.cattlepie' + domainSuffix + '/', 30 | imgServer: 'http://img0.cattlepie.com/', 31 | } 32 | 33 | export default config; -------------------------------------------------------------------------------- /src/components/remote-js.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | -------------------------------------------------------------------------------- /src/hiapp-pages/home.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /test/e2e/custom-assertions/elementCount.js: -------------------------------------------------------------------------------- 1 | // A custom Nightwatch assertion. 2 | // The assertion name is the filename. 3 | // Example usage: 4 | // 5 | // browser.assert.elementCount(selector, count) 6 | // 7 | // For more information on custom assertions see: 8 | // http://nightwatchjs.org/guide#writing-custom-assertions 9 | 10 | exports.assertion = function (selector, count) { 11 | this.message = 'Testing if element <' + selector + '> has count: ' + count 12 | this.expected = count 13 | this.pass = function (val) { 14 | return val === this.expected 15 | } 16 | this.value = function (res) { 17 | return res.value 18 | } 19 | this.command = function (cb) { 20 | var self = this 21 | return this.api.execute(function (selector) { 22 | return document.querySelectorAll(selector).length 23 | }, [selector], function (res) { 24 | cb.call(self, res) 25 | }) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/pages/home.vue: -------------------------------------------------------------------------------- 1 | 16 | 19 | 20 | -------------------------------------------------------------------------------- /src/hiapp-pages/language.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | -------------------------------------------------------------------------------- /src/hiapp-pages/publisher.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | -------------------------------------------------------------------------------- /src/hiapp-pages/comment.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | -------------------------------------------------------------------------------- /test/e2e/nightwatch.conf.js: -------------------------------------------------------------------------------- 1 | require('babel-register') 2 | var config = require('../../config') 3 | 4 | // http://nightwatchjs.org/gettingstarted#settings-file 5 | module.exports = { 6 | src_folders: ['test/e2e/specs'], 7 | output_folder: 'test/e2e/reports', 8 | custom_assertions_path: ['test/e2e/custom-assertions'], 9 | 10 | selenium: { 11 | start_process: true, 12 | server_path: require('selenium-server').path, 13 | host: '127.0.0.1', 14 | port: 4444, 15 | cli_args: { 16 | 'webdriver.chrome.driver': require('chromedriver').path 17 | } 18 | }, 19 | 20 | test_settings: { 21 | default: { 22 | selenium_port: 4444, 23 | selenium_host: 'localhost', 24 | silent: true, 25 | globals: { 26 | devServerURL: 'http://localhost:' + (process.env.PORT || config.dev.port) 27 | } 28 | }, 29 | 30 | chrome: { 31 | desiredCapabilities: { 32 | browserName: 'chrome', 33 | javascriptEnabled: true, 34 | acceptSslCerts: true 35 | } 36 | }, 37 | 38 | firefox: { 39 | desiredCapabilities: { 40 | browserName: 'firefox', 41 | javascriptEnabled: true, 42 | acceptSslCerts: true 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/hiapp-pages/profile.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 34 | 35 | -------------------------------------------------------------------------------- /src/components/editor.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 21 | 22 | -------------------------------------------------------------------------------- /src/hiapp-pages/contacts.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 39 | 40 | -------------------------------------------------------------------------------- /test/e2e/runner.js: -------------------------------------------------------------------------------- 1 | // 1. start the dev server using production config 2 | process.env.NODE_ENV = 'testing' 3 | 4 | const webpack = require('webpack') 5 | const DevServer = require('webpack-dev-server') 6 | 7 | const webpackConfig = require('../../build/webpack.prod.conf') 8 | const devConfigPromise = require('../../build/webpack.dev.conf') 9 | 10 | let server 11 | 12 | devConfigPromise.then(devConfig => { 13 | const devServerOptions = devConfig.devServer 14 | const compiler = webpack(webpackConfig) 15 | server = new DevServer(compiler, devServerOptions) 16 | const port = devServerOptions.port 17 | const host = devServerOptions.host 18 | return server.listen(port, host) 19 | }) 20 | .then(() => { 21 | // 2. run the nightwatch test suite against it 22 | // to run in additional browsers: 23 | // 1. add an entry in test/e2e/nightwatch.conf.js under "test_settings" 24 | // 2. add it to the --env flag below 25 | // or override the environment flag, for example: `npm run e2e -- --env chrome,firefox` 26 | // For more information on Nightwatch's config file, see 27 | // http://nightwatchjs.org/guide#settings-file 28 | let opts = process.argv.slice(2) 29 | if (opts.indexOf('--config') === -1) { 30 | opts = opts.concat(['--config', 'test/e2e/nightwatch.conf.js']) 31 | } 32 | if (opts.indexOf('--env') === -1) { 33 | opts = opts.concat(['--env', 'chrome']) 34 | } 35 | 36 | const spawn = require('cross-spawn') 37 | const runner = spawn('./node_modules/.bin/nightwatch', opts, { stdio: 'inherit' }) 38 | 39 | runner.on('exit', function (code) { 40 | server.close() 41 | process.exit(code) 42 | }) 43 | 44 | runner.on('error', function (err) { 45 | server.close() 46 | throw err 47 | }) 48 | }) 49 | -------------------------------------------------------------------------------- /src/hiapp-pages/about.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | -------------------------------------------------------------------------------- /src/pages/not-found.vue: -------------------------------------------------------------------------------- 1 | 42 | 45 | 46 | -------------------------------------------------------------------------------- /src/store/getters.js: -------------------------------------------------------------------------------- 1 | export default { 2 | // checkoutStatus: state => state.checkoutStatus, 3 | 4 | getMutArticle: (state) => (id, init=1) => { 5 | return state.mutArticlesState['k' + id] || (init ? { 6 | article_id : 0, 7 | article_like_count : 0, 8 | article_click: 0, 9 | article_up: 0, 10 | article_comment_count: 0, 11 | liked: 0, 12 | uped:0, 13 | } : undefined); 14 | 15 | // return state.added.map(item => { 16 | // const product = state.products.all.find(product => product.id === id) 17 | // return { 18 | // title: product.title, 19 | // price: product.price, 20 | // quantity: item.quantity 21 | // } 22 | // }) 23 | }, 24 | 25 | getMutArticleReply: (state) => (id, init=1) => { 26 | return state.mutArticleRepliesState['k' + id] || (init ? { 27 | reply_up : 0, 28 | uped: 0, 29 | } : undefined); 30 | 31 | }, 32 | 33 | getMutThemeReply: (state) => (id, init=1) => { 34 | return state.mutThemeRepliesState['k' + id] || (init ? { 35 | reply_up : 0, 36 | uped: 0, 37 | } : undefined); 38 | 39 | }, 40 | 41 | getMutCollection: (state) => (id, init=1) => { 42 | return state.mutCollectionsState['k' + id] || (init ? { 43 | cl_like_count : 0, 44 | liked: 0, 45 | } : undefined); 46 | }, 47 | 48 | getMutTheme: (state) => (id, init=1) => { 49 | return state.mutThemesState['k' + id] || (init ? { 50 | theme_like_count : 0, 51 | theme_up_count: 0, 52 | theme_commentcount: 0, 53 | liked: 0, 54 | uped: 0, 55 | } : undefined); 56 | }, 57 | 58 | getMutCircle: (state) => (id, init=1) => { 59 | return state.mutCirclesState['k' + id] || (init ? { 60 | circle_thcount : 0, 61 | circle_mcount: 0, 62 | joined: 0, 63 | } : undefined); 64 | 65 | }, 66 | 67 | getMutStore: (state) => (id, init=1) => { 68 | return state.mutStoresState['k' + id] || (init ? { 69 | // store_like_count : 0, 70 | liked: 0, 71 | } : undefined); 72 | }, 73 | 74 | getMutGoods: (state) => (id, init=1) => { 75 | return state.mutGoodsState['k' + id] || (init ? { 76 | // store_like_count : 0, 77 | liked: 0, 78 | } : undefined); 79 | }, 80 | 81 | } -------------------------------------------------------------------------------- /src/css/icons.css: -------------------------------------------------------------------------------- 1 | /* Material Icons Font (for MD theme) */ 2 | @font-face { 3 | font-family: 'Material Icons'; 4 | font-style: normal; 5 | font-weight: 400; 6 | src: url(../fonts/MaterialIcons-Regular.eot); /* For IE6-8 */ 7 | src: local('Material Icons'), 8 | local('MaterialIcons-Regular'), 9 | url(../fonts/MaterialIcons-Regular.woff2) format('woff2'), 10 | url(../fonts/MaterialIcons-Regular.woff) format('woff'), 11 | url(../fonts/MaterialIcons-Regular.ttf) format('truetype'); 12 | } 13 | 14 | .material-icons { 15 | font-family: 'Material Icons'; 16 | font-weight: normal; 17 | font-style: normal; 18 | font-size: 24px; /* Preferred icon size */ 19 | display: inline-block; 20 | line-height: 1; 21 | text-transform: none; 22 | letter-spacing: normal; 23 | word-wrap: normal; 24 | white-space: nowrap; 25 | direction: ltr; 26 | 27 | /* Support for all WebKit browsers. */ 28 | -webkit-font-smoothing: antialiased; 29 | /* Support for Safari and Chrome. */ 30 | text-rendering: optimizeLegibility; 31 | 32 | /* Support for Firefox. */ 33 | -moz-osx-font-smoothing: grayscale; 34 | 35 | /* Support for IE. */ 36 | font-feature-settings: 'liga'; 37 | } 38 | 39 | /* Framework7 Icons Font (for iOS theme) */ 40 | @font-face { 41 | font-family: 'Framework7 Icons'; 42 | font-style: normal; 43 | font-weight: 400; 44 | src: url("../fonts/Framework7Icons-Regular.eot"); 45 | src: url("../fonts/Framework7Icons-Regular.woff2") format("woff2"), 46 | url("../fonts/Framework7Icons-Regular.woff") format("woff"), 47 | url("../fonts/Framework7Icons-Regular.ttf") format("truetype"); 48 | } 49 | 50 | .f7-icons { 51 | font-family: 'Framework7 Icons'; 52 | font-weight: normal; 53 | font-style: normal; 54 | font-size: 25px; 55 | line-height: 1; 56 | letter-spacing: normal; 57 | text-transform: none; 58 | display: inline-block; 59 | white-space: nowrap; 60 | word-wrap: normal; 61 | direction: ltr; 62 | -webkit-font-smoothing: antialiased; 63 | text-rendering: optimizeLegibility; 64 | -moz-osx-font-smoothing: grayscale; 65 | -webkit-font-feature-settings: "liga"; 66 | -moz-font-feature-settings: "liga=1"; 67 | -moz-font-feature-settings: "liga"; 68 | font-feature-settings: "liga"; 69 | text-align: center; 70 | } 71 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | // Ponyfill 2 | //import 'es6-object-assign/auto' 3 | //import 'es6-promise/auto' 4 | import 'babel-polyfill';// 或者require('babel-polyfill'); 5 | 6 | // Import F7 Styles 7 | import Framework7Styles from 'framework7/dist/css/framework7.ios.min.css'; 8 | 9 | // Import Icons and App Custom Styles 10 | import './css/font-awesome.min.css'; 11 | import IconsStyles from './css/icons.css'; 12 | //import AliIconsStyles from 'at.alicdn.com/t/font_551867_u8ixrzn4edji3sor.css'; 13 | import AppStyles from './css/app.css'; 14 | 15 | import Vue from 'vue' 16 | import App from './App' 17 | import router from './router' 18 | 19 | // Import Vuex store 20 | import store from './store' 21 | 22 | Vue.config.productionTip = false 23 | 24 | //vue-lazyload 25 | import VueLazyload from 'vue-lazyload'; 26 | Vue.use(VueLazyload, { 27 | preLoad: 1.3, 28 | error: 'static/img/preload.png', 29 | loading: 'static/img/preload.png', 30 | attempt: 2 31 | }); 32 | 33 | //引入vue基础框架 34 | import base from './js/base.js'; 35 | import config from './js/config.js'; 36 | import wap from './js/wap.js'; 37 | Vue.use(base, config, wap); 38 | 39 | //引入f7基础框架 40 | //import Framework7 from 'framework7/dist/framework7.esm.bundle.js'; 41 | import Framework7 from 'framework7'; 42 | import Swiper from 'framework7/dist/components/swiper/swiper.js'; 43 | import Popup from 'framework7/dist/components/popup/popup.js'; 44 | import Popover from 'framework7/dist/components/popover/popover.js'; 45 | import Preloader from 'framework7/dist/components/preloader/preloader.js'; 46 | import PullToRefresh from 'framework7/dist/components/pull-to-refresh/pull-to-refresh.js'; 47 | import Toast from 'framework7/dist/components/toast/toast.js'; 48 | import PhotoBrowser from 'framework7/dist/components/photo-browser/photo-browser.js'; 49 | import Dialog from 'framework7/dist/components/dialog/dialog.js'; 50 | import Sheet from 'framework7/dist/components/sheet/sheet.js'; 51 | import InfiniteScroll from 'framework7/dist/components/infinite-scroll/infinite-scroll.js'; 52 | 53 | Framework7.use([Swiper,Popup,Popover,Preloader,PullToRefresh,Toast,PhotoBrowser,Dialog,Sheet,InfiniteScroll]); 54 | 55 | import F7Base from './js/f7-base.js' 56 | Vue.use(F7Base, Framework7); 57 | 58 | 59 | var _hmt = window._hmt || [] 60 | 61 | /* eslint-disable no-new */ 62 | new Vue({ 63 | el: '#app', 64 | store, 65 | router, 66 | components: { App }, 67 | template: '' 68 | }) 69 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 42 | 43 | -------------------------------------------------------------------------------- /src/hiapp-pages/settings.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 63 | 64 | -------------------------------------------------------------------------------- /config/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | // Template version: 1.3.1 3 | // see http://vuejs-templates.github.io/webpack for documentation. 4 | 5 | const path = require('path') 6 | 7 | module.exports = { 8 | dev: { 9 | 10 | // Paths 11 | assetsSubDirectory: 'static', 12 | assetsPublicPath: '/', 13 | proxyTable: {}, 14 | 15 | // Various Dev Server settings 16 | host: '192.168.0.108', // can be overwritten by process.env.HOST 17 | port: 9999, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined 18 | autoOpenBrowser: false, 19 | errorOverlay: true, 20 | notifyOnErrors: true, 21 | poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- 22 | 23 | // Use Eslint Loader? 24 | // If true, your code will be linted during bundling and 25 | // linting errors and warnings will be shown in the console. 26 | // useEslint: true, 27 | // If true, eslint errors and warnings will also be shown in the error overlay 28 | // in the browser. 29 | // showEslintErrorsInOverlay: false, 30 | 31 | /** 32 | * Source Maps 33 | */ 34 | 35 | // https://webpack.js.org/configuration/devtool/#development 36 | devtool: 'cheap-module-eval-source-map', 37 | 38 | // If you have problems debugging vue-files in devtools, 39 | // set this to false - it *may* help 40 | // https://vue-loader.vuejs.org/en/options.html#cachebusting 41 | cacheBusting: true, 42 | 43 | cssSourceMap: true 44 | }, 45 | 46 | build: { 47 | // Template for index.html 48 | index: path.resolve(__dirname, '../dist/index.html'), 49 | 50 | // Paths 51 | assetsRoot: path.resolve(__dirname, '../dist'), 52 | assetsSubDirectory: 'static', 53 | assetsPublicPath: '', 54 | 55 | /** 56 | * Source Maps 57 | */ 58 | 59 | productionSourceMap: false, 60 | // https://webpack.js.org/configuration/devtool/#production 61 | devtool: '#source-map', 62 | 63 | // Gzip off by default as many popular static hosts such as 64 | // Surge or Netlify already gzip all static assets for you. 65 | // Before setting to `true`, make sure to: 66 | // npm install --save-dev compression-webpack-plugin 67 | productionGzip: false, 68 | productionGzipExtensions: ['js', 'css'], 69 | 70 | // Run the build command with an extra argument to 71 | // View the bundle analyzer report after build finishes: 72 | // `npm run build --report` 73 | // Set to `true` or `false` to always turn it on or off 74 | bundleAnalyzerReport: process.env.npm_config_report 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/js/myAmap.js: -------------------------------------------------------------------------------- 1 | //微信分享事件监听,常用功能封装 2 | var myAmap = function(opts) { 3 | var defaults = { 4 | title: '分享标题', //分享标题,不能为空 5 | desc: '', //分享描述,可以为空,(分享到朋友圈,不支持描述) 6 | link: '', //分享页面地址,不能为空 7 | imgUrl: '', //分享是封面图片,不能为空 8 | success: function() {}, //分享成功触发 9 | cancel: function() {} //分享取消触发,需要时可以调用 10 | } 11 | this.opts = arguments[0] ? opts : defaults; 12 | } 13 | 14 | myAmap.prototype = { 15 | plugnToolBar: function() { 16 | map.plugin(["AMap.ToolBar"], function() { 17 | map.addControl(new AMap.ToolBar()); 18 | }); 19 | if(location.href.indexOf('&guide=1') !== -1) { 20 | map.setStatus({ 21 | scrollWheel: false 22 | }) 23 | } 24 | }, 25 | 26 | //逆地理编码 27 | //逆向地理编码 28 | // var x = localStorage.getItem('user_lng'); 29 | // var y = localStorage.getItem('user_lat'); 30 | // regeocoder([x, y]); 31 | regeocoder: function(lnglatXY) { 32 | AMap.plugin('AMap.Geocoder', function() { 33 | map.addControl(geocoder); 34 | }) 35 | var geocoder = new AMap.Geocoder({ 36 | radius: 1000, 37 | extensions: "all" 38 | }); 39 | 40 | geocoder.getAddress(lnglatXY, function(status, result) { 41 | if(status === 'complete' && result.info === 'OK') { 42 | geocoder_CallBack(result); 43 | } 44 | }); 45 | 46 | function geocoder_CallBack(data) { 47 | var address = data.regeocode.formattedAddress; //返回地址描述 48 | // alert(address); 49 | localStorage.setItem('locations', address); 50 | } 51 | }, 52 | 53 | //请求地理位置定位 54 | reqLocation: function(onComplete, onError) { 55 | var onComplete = arguments[0] ? arguments[0] : onCompleteFunc; 56 | var onError = arguments[1] ? arguments[1] : onErrorFunc; 57 | 58 | var map, geolocation; 59 | //加载地图,调用浏览器定位服务 60 | map = new AMap.Map('container', { 61 | resizeEnable: true, 62 | }); 63 | 64 | map.plugin('AMap.Geolocation', function() { 65 | geolocation = new AMap.Geolocation({ 66 | enableHighAccuracy: true, //是否使用高精度定位,默认:true 67 | timeout: 1000, //超过10秒后停止定位,默认:无穷大 68 | zoom: 16, 69 | }); 70 | map.addControl(geolocation); 71 | geolocation.getCurrentPosition(); 72 | AMap.event.addListener(geolocation, 'complete', onComplete); //返回定位信息 73 | AMap.event.addListener(geolocation, 'error', onError); //返回定位出错信息 74 | }); 75 | 76 | //解析定位结果 77 | function onCompleteFunc(data) { 78 | alert(data); 79 | console.log(data); 80 | var user_lng = data.position.getLng(); 81 | var user_lat = data.position.getLat(); 82 | 83 | var str = ['定位成功']; 84 | str.push('经度:' + user_lng); 85 | str.push('纬度:' + user_lat); 86 | localStorage.setItem('user_lng', user_lng); 87 | localStorage.setItem('user_lat', user_lat); 88 | } 89 | 90 | function onErrorFunc(data) { 91 | /*alert('定位失败');*/ 92 | console.log(data) 93 | } 94 | }, 95 | } 96 | 97 | export default myAmap; 98 | -------------------------------------------------------------------------------- /src/js/wxShare.js: -------------------------------------------------------------------------------- 1 | //微信分享事件监听,常用功能封装 2 | (function() { 3 | var wxShare = function(opts) { 4 | var defaults = { 5 | title: '杏运树趣玩-一个好玩、好吃、好实惠的平台', //分享标题,不能为空 6 | desc: '让美食与运动成为一种健康习惯,让吃货用更佳的方式享悦生活!', //分享描述,可以为空,(分享到朋友圈,不支持描述) 7 | link: '', //分享页面地址,不能为空 8 | imgUrl: '', //分享是封面图片,不能为空 9 | success: function() {}, //分享成功触发 10 | cancel: function() {} //分享取消触发,需要时可以调用 11 | } 12 | this.opts = $.extend({}, defaults, opts); 13 | } 14 | wxShare.prototype = { 15 | //绑定微信朋友圈,发送朋友 16 | bindWX: function() { 17 | var _opts = this.opts; 18 | //监听,分享到朋友圈 19 | wx.onMenuShareTimeline({ 20 | title: _opts.title, 21 | link: _opts.link, 22 | imgUrl: _opts.imgUrl, 23 | success: function() { 24 | if(_opts.success) 25 | _opts.success(); 26 | }, 27 | calcel: function() { 28 | if(_opts.cancel) 29 | _opts.cancel(); 30 | } 31 | }); 32 | //监听,分享给朋友 (type,dataurl基本可以放弃不使用) 33 | wx.onMenuShareAppMessage({ 34 | title: _opts.title, // 分享标题 35 | desc: _opts.desc, // 分享描述 36 | link: _opts.link, // 分享链接 37 | imgUrl: _opts.imgUrl, // 分享图标 38 | success: function() { 39 | if(_opts.success) 40 | _opts.success(); 41 | }, 42 | cancel: function() { 43 | if(_opts.cancel) 44 | _opts.cancel(); 45 | } 46 | }); 47 | }, 48 | //绑定QQ空间,QQ好友 49 | bindQQ: function() { 50 | var _opts = this.opts; 51 | //监听,分享到QQ空间 52 | wx.onMenuShareQZone({ 53 | title: _opts.title, // 分享标题 54 | desc: _opts.desc, // 分享描述 55 | link: _opts.link, // 分享链接 56 | imgUrl: _opts.imgUrl, // 分享图标 57 | success: function() { 58 | if(_opts.success) 59 | _opts.success(); 60 | }, 61 | cancel: function() { 62 | if(_opts.cancel) 63 | _opts.cancel(); 64 | } 65 | }); 66 | //监听,分享到QQ 67 | wx.onMenuShareQQ({ 68 | title: _opts.title, // 分享标题 69 | desc: _opts.desc, // 分享描述 70 | link: _opts.link, // 分享链接 71 | imgUrl: _opts.imgUrl, // 分享图标 72 | success: function() { 73 | if(_opts.success) 74 | _opts.success(); 75 | }, 76 | cancel: function() { 77 | if(_opts.cancel) 78 | _opts.cancel(); 79 | } 80 | }); 81 | }, 82 | //绑定默认,不使用腾讯微博 83 | bind: function() { 84 | this.bindWX(); 85 | this.bindQQ(); 86 | }, 87 | //绑定所有,包括腾讯微博 88 | bindAll: function() { 89 | this.bind(); 90 | var _opts = this.opts; 91 | //监听,分享到腾讯微博 (基本可以放弃不使用) 92 | wx.onMenuShareWeibo({ 93 | title: _opts.title, // 分享标题 94 | desc: _opts.desc, // 分享描述 95 | link: _opts.link, // 分享链接 96 | imgUrl: _opts.imgUrl, // 分享图标 97 | success: function() { 98 | if(_opts.success) 99 | _opts.success(); 100 | }, 101 | cancel: function() { 102 | if(_opts.cancel) 103 | _opts.cancel(); 104 | } 105 | }); 106 | } 107 | } 108 | window.wxShare = wxShare; 109 | })(); -------------------------------------------------------------------------------- /src/css/article.css: -------------------------------------------------------------------------------- 1 | article p { 2 | word-wrap: break-word; 3 | text-align: justify; 4 | margin: 0 0 20px; 5 | color: #505050; 6 | line-height: 1.7; 7 | padding: 0 8 | } 9 | 10 | article table { 11 | margin: 0 auto; 12 | font-size: 14px; 13 | text-align: center; 14 | border-spacing: 0; 15 | border-collapse: collapse; 16 | color: #303030 17 | } 18 | 19 | article table td { 20 | border: 1px solid #dadada; 21 | padding: 2px 22 | } 23 | 24 | article img { 25 | display: block; 26 | max-width: 100%; 27 | background: #efefef; 28 | border-radius: 0; 29 | margin: 8px auto 30 | } 31 | 32 | article h1, 33 | article h2, 34 | article h3, 35 | article h4 { 36 | position: relative; 37 | display: block; 38 | font-weight: 700; 39 | word-wrap: break-word; 40 | margin: 20px 0; 41 | padding-left: 9px; 42 | color: #333333; 43 | line-height: normal 44 | } 45 | 46 | article h1 { 47 | font-size: 1.1em; 48 | } 49 | 50 | article h2 { 51 | font-size: 1em; 52 | } 53 | article h3 { 54 | font-size: 1em; 55 | padding-left: 0; 56 | text-align: left; 57 | } 58 | article h4 { 59 | font-size: 1em; 60 | padding-left: 0; 61 | text-align: center; 62 | } 63 | article h5 { 64 | font-size: 0.9em; 65 | text-align: center; 66 | } 67 | 68 | article h1:before, 69 | article h2:before{ 70 | content: ''; 71 | position: absolute; 72 | width: 4px; 73 | height: 100%; 74 | top: 0; 75 | left: 0; 76 | background: #ff9500; 77 | border-radius: 20%; 78 | } 79 | 80 | article blockquote p { 81 | line-height: 1.6; 82 | margin: 0 0 20px; 83 | color: #999 84 | } 85 | 86 | article blockquote p:last-child { 87 | margin: 0 88 | } 89 | 90 | article blockquote p br:only-child { 91 | display: none 92 | } 93 | 94 | article pre, 95 | article code { 96 | font-size: 12px; 97 | line-height: 1.5em; 98 | background: #eee; 99 | padding: 10px 100 | } 101 | 102 | article blockquote { 103 | position: relative; 104 | font-size: .8823em; 105 | margin: 20px 0; 106 | padding: 12px 10px; 107 | line-height: 1.4; 108 | color: #999; 109 | background: #fff; 110 | border: 1px solid #ddd 111 | } 112 | 113 | article table { 114 | margin: 15px auto; 115 | color: #464646 116 | } 117 | 118 | article table td, 119 | article table th { 120 | text-align: center; 121 | font-size: 14px; 122 | line-height: 1.5em 123 | } 124 | 125 | article table.border th, 126 | article table.border tr:nth-child(2n) { 127 | background: rgba(100, 100, 100, .1) 128 | } 129 | 130 | article table.border td, 131 | article table.border th { 132 | padding: 5px; 133 | font-size: 12px; 134 | border: 1px solid #d4d4d4 135 | } 136 | 137 | @media only screen and (min-width:375px) { 138 | article { 139 | font-size: 18px; 140 | line-height: 1.7 141 | } 142 | article p { 143 | line-height: 1.7 144 | } 145 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-project", 3 | "version": "1.0.0", 4 | "description": "A Vue.js project", 5 | "author": "", 6 | "private": true, 7 | "scripts": { 8 | "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", 9 | "start": "npm run dev", 10 | "unit": "jest --config test/unit/jest.conf.js --coverage", 11 | "e2e": "node test/e2e/runner.js", 12 | "test": "npm run unit && npm run e2e", 13 | "lint": "eslint --ext .js,.vue src test/unit test/e2e/specs", 14 | "build": "node build/build.js" 15 | }, 16 | "dependencies": { 17 | "babel-polyfill": "^6.26.0", 18 | "framework7": "^2.0.10", 19 | "framework7-icons": "^0.8.9", 20 | "vue": "^2.5.2", 21 | "vue-lazyload": "^1.2.2", 22 | "vue-router": "^3.0.1", 23 | "vuex": "^3.0.1", 24 | "vuex-along": "^1.1.2" 25 | }, 26 | "devDependencies": { 27 | "autoprefixer": "^7.1.2", 28 | "babel-core": "^6.22.1", 29 | "babel-eslint": "^8.2.1", 30 | "babel-helper-vue-jsx-merge-props": "^2.0.3", 31 | "babel-jest": "^21.0.2", 32 | "babel-loader": "^7.1.1", 33 | "babel-plugin-dynamic-import-node": "^1.2.0", 34 | "babel-plugin-syntax-jsx": "^6.18.0", 35 | "babel-plugin-transform-es2015-modules-commonjs": "^6.26.0", 36 | "babel-plugin-transform-runtime": "^6.22.0", 37 | "babel-plugin-transform-vue-jsx": "^3.5.0", 38 | "babel-preset-env": "^1.3.2", 39 | "babel-preset-stage-2": "^6.22.0", 40 | "babel-register": "^6.22.0", 41 | "chalk": "^2.0.1", 42 | "chromedriver": "^2.27.2", 43 | "copy-webpack-plugin": "^4.0.1", 44 | "cross-spawn": "^5.0.1", 45 | "css-loader": "^0.28.0", 46 | "eslint": "^4.15.0", 47 | "eslint-config-standard": "^10.2.1", 48 | "eslint-friendly-formatter": "^3.0.0", 49 | "eslint-loader": "^1.7.1", 50 | "eslint-plugin-import": "^2.7.0", 51 | "eslint-plugin-node": "^5.2.0", 52 | "eslint-plugin-promise": "^3.4.0", 53 | "eslint-plugin-standard": "^3.0.1", 54 | "eslint-plugin-vue": "^4.0.0", 55 | "extract-text-webpack-plugin": "^3.0.0", 56 | "file-loader": "^1.1.4", 57 | "friendly-errors-webpack-plugin": "^1.6.1", 58 | "html-webpack-plugin": "^2.30.1", 59 | "jest": "^22.0.4", 60 | "jest-serializer-vue": "^0.3.0", 61 | "nightwatch": "^0.9.12", 62 | "node-notifier": "^5.1.2", 63 | "optimize-css-assets-webpack-plugin": "^3.2.0", 64 | "ora": "^1.2.0", 65 | "portfinder": "^1.0.13", 66 | "postcss-import": "^11.0.0", 67 | "postcss-loader": "^2.0.8", 68 | "postcss-url": "^7.2.1", 69 | "rimraf": "^2.6.0", 70 | "selenium-server": "^3.0.1", 71 | "semver": "^5.3.0", 72 | "shelljs": "^0.7.6", 73 | "uglifyjs-webpack-plugin": "^1.1.1", 74 | "url-loader": "^0.5.8", 75 | "vue-jest": "^1.0.2", 76 | "vue-loader": "^13.3.0", 77 | "vue-style-loader": "^3.0.1", 78 | "vue-template-compiler": "^2.5.2", 79 | "webpack": "^3.6.0", 80 | "webpack-bundle-analyzer": "^2.9.0", 81 | "webpack-dev-server": "^2.9.1", 82 | "webpack-merge": "^2.6.1" 83 | }, 84 | "engines": { 85 | "node": ">= 6.0.0", 86 | "npm": ">= 3.0.0" 87 | }, 88 | "browserslist": [ 89 | "> 1%", 90 | "last 2 versions", 91 | "not ie <= 8" 92 | ] 93 | } 94 | -------------------------------------------------------------------------------- /src/js/plugin/jquery.lazyload.min.js: -------------------------------------------------------------------------------- 1 | /*! Lazy Load 1.9.3 - MIT license - Copyright 2010-2013 Mika Tuupola */ 2 | !function(a,b,c,d){var e=a(b);a.fn.lazyload=function(f){function g(){var b=0;i.each(function(){var c=a(this);if(!j.skip_invisible||c.is(":visible"))if(a.abovethetop(this,j)||a.leftofbegin(this,j));else if(a.belowthefold(this,j)||a.rightoffold(this,j)){if(++b>j.failure_limit)return!1}else c.trigger("appear"),b=0})}var h,i=this,j={threshold:0,failure_limit:0,event:"scroll",effect:"show",container:b,data_attribute:"original",skip_invisible:!0,appear:null,load:null,placeholder:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC"};return f&&(d!==f.failurelimit&&(f.failure_limit=f.failurelimit,delete f.failurelimit),d!==f.effectspeed&&(f.effect_speed=f.effectspeed,delete f.effectspeed),a.extend(j,f)),h=j.container===d||j.container===b?e:a(j.container),0===j.event.indexOf("scroll")&&h.bind(j.event,function(){return g()}),this.each(function(){var b=this,c=a(b);b.loaded=!1,(c.attr("src")===d||c.attr("src")===!1)&&c.is("img")&&c.attr("src",j.placeholder),c.one("appear",function(){if(!this.loaded){if(j.appear){var d=i.length;j.appear.call(b,d,j)}a("").bind("load",function(){var d=c.attr("data-"+j.data_attribute);c.hide(),c.is("img")?c.attr("src",d):c.css("background-image","url('"+d+"')"),c[j.effect](j.effect_speed),b.loaded=!0;var e=a.grep(i,function(a){return!a.loaded});if(i=a(e),j.load){var f=i.length;j.load.call(b,f,j)}}).attr("src",c.attr("data-"+j.data_attribute))}}),0!==j.event.indexOf("scroll")&&c.bind(j.event,function(){b.loaded||c.trigger("appear")})}),e.bind("resize",function(){g()}),/(?:iphone|ipod|ipad).*os 5/gi.test(navigator.appVersion)&&e.bind("pageshow",function(b){b.originalEvent&&b.originalEvent.persisted&&i.each(function(){a(this).trigger("appear")})}),a(c).ready(function(){g()}),this},a.belowthefold=function(c,f){var g;return g=f.container===d||f.container===b?(b.innerHeight?b.innerHeight:e.height())+e.scrollTop():a(f.container).offset().top+a(f.container).height(),g<=a(c).offset().top-f.threshold},a.rightoffold=function(c,f){var g;return g=f.container===d||f.container===b?e.width()+e.scrollLeft():a(f.container).offset().left+a(f.container).width(),g<=a(c).offset().left-f.threshold},a.abovethetop=function(c,f){var g;return g=f.container===d||f.container===b?e.scrollTop():a(f.container).offset().top,g>=a(c).offset().top+f.threshold+a(c).height()},a.leftofbegin=function(c,f){var g;return g=f.container===d||f.container===b?e.scrollLeft():a(f.container).offset().left,g>=a(c).offset().left+f.threshold+a(c).width()},a.inviewport=function(b,c){return!(a.rightoffold(b,c)||a.leftofbegin(b,c)||a.belowthefold(b,c)||a.abovethetop(b,c))},a.extend(a.expr[":"],{"below-the-fold":function(b){return a.belowthefold(b,{threshold:0})},"above-the-top":function(b){return!a.belowthefold(b,{threshold:0})},"right-of-screen":function(b){return a.rightoffold(b,{threshold:0})},"left-of-screen":function(b){return!a.rightoffold(b,{threshold:0})},"in-viewport":function(b){return a.inviewport(b,{threshold:0})},"above-the-fold":function(b){return!a.belowthefold(b,{threshold:0})},"right-of-fold":function(b){return a.rightoffold(b,{threshold:0})},"left-of-fold":function(b){return!a.rightoffold(b,{threshold:0})}})}(jQuery,window,document); -------------------------------------------------------------------------------- /src/pages/my/follow-member.vue: -------------------------------------------------------------------------------- 1 | 49 | 88 | -------------------------------------------------------------------------------- /src/hiapp-pages/message.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 29 | 30 | -------------------------------------------------------------------------------- /src/pages/form.vue: -------------------------------------------------------------------------------- 1 | 103 | 104 | 107 | -------------------------------------------------------------------------------- /src/components/card.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 107 | 108 | -------------------------------------------------------------------------------- /src/hiapp-pages/card.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 107 | 108 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | //import HelloWorld from '@/components/HelloWorld' 4 | 5 | import HomePage from '@/pages/home.vue'; 6 | import IndexPage from '@/pages/index.vue'; 7 | import MinePage from '@/pages/mine.vue'; 8 | import StoreIndexPage from '@/pages/store-index.vue'; 9 | import ChannelPage from '@/pages/channel.vue'; 10 | import AttentionPage from '@/pages/attention.vue'; 11 | 12 | import NotFoundPage from '@/pages/not-found.vue'; 13 | 14 | import ArticleListPage from '@/pages/article-list.vue'; 15 | import StoreListPage from '@/pages/store-list.vue'; 16 | import CollectionListPage from '@/pages/collection-list.vue'; 17 | import CircleListPage from '@/pages/circle-list.vue'; 18 | 19 | import ArticleDetailPage from '@/pages/article-detail.vue'; 20 | import ArticleCommentPage from '@/pages/article-comment.vue'; 21 | import ArticleCommentSubPage from '@/pages/article-comment-sub.vue'; 22 | import StoreDetailPage from '@/pages/store-detail.vue'; 23 | import CollectionDetailPage from '@/pages/collection-detail.vue'; 24 | import CircleDetailPage from '@/pages/circle-detail.vue'; 25 | import CirclethemeDetailPage from '@/pages/circletheme-detail.vue'; 26 | import CirclethemePostPage from '@/pages/circletheme-post.vue'; 27 | 28 | import My_CirclethemePage from '@/pages/my/circletheme.vue'; 29 | import My_FavStorePage from '@/pages/my/fav-store.vue'; 30 | import My_FavGoodsPage from '@/pages/my/fav-goods.vue'; 31 | import My_FavArticlePage from '@/pages/my/fav-article.vue'; 32 | import My_FavCirclethemePage from '@/pages/my/fav-circletheme.vue'; 33 | import My_FavCollectionPage from '@/pages/my/fav-collection.vue'; 34 | import My_FollowCirclePage from '@/pages/my/follow-circle.vue'; 35 | import My_FollowMemberPage from '@/pages/my/follow-member.vue'; 36 | 37 | Router.prototype.goBack = function () { 38 | this.isBack = true 39 | window.history.go(-1) 40 | } 41 | Vue.use(Router) 42 | 43 | 44 | const router = new Router({ 45 | routes: [{ 46 | path: "/", 47 | component: HomePage, 48 | }, 49 | { 50 | path: "/index/", 51 | component: IndexPage, 52 | }, 53 | { 54 | path: "/mine/", 55 | component: MinePage, 56 | }, 57 | { 58 | path: '/store-index/', 59 | component: StoreIndexPage, 60 | }, 61 | { 62 | path: '/attention/', 63 | component: AttentionPage, 64 | }, 65 | { 66 | path: '/channel/id/:id/', 67 | component: ChannelPage, 68 | }, 69 | { 70 | path: '/article-list/', 71 | component: ArticleListPage, 72 | }, 73 | { 74 | path: '/store-list/', 75 | component: StoreListPage, 76 | }, 77 | { 78 | path: '/circle-list/', 79 | component: CircleListPage, 80 | }, 81 | { 82 | path: '/collection-list/', 83 | component: CollectionListPage, 84 | }, 85 | 86 | { 87 | path: '/article-detail/id/:id/', 88 | component: ArticleDetailPage, 89 | }, 90 | { 91 | path: '/article-comment/id/:id/', 92 | component: ArticleCommentPage, 93 | }, 94 | { 95 | path: '/article-comment-sub/id/:id/', 96 | component: ArticleCommentSubPage, 97 | }, 98 | { 99 | path: '/store-detail/id/:id/', 100 | component: StoreDetailPage, 101 | }, 102 | { 103 | path: '/collection-detail/id/:id/', 104 | component: CollectionDetailPage, 105 | }, 106 | { 107 | path: '/circle-detail/id/:id/', 108 | component: CircleDetailPage, 109 | }, 110 | { 111 | path: '/circletheme-detail/id/:id/', 112 | component: CirclethemeDetailPage, 113 | }, 114 | { 115 | path: '/circletheme-post/', 116 | component: CirclethemePostPage, 117 | }, 118 | 119 | { 120 | path: '/my/circletheme/', 121 | component: My_CirclethemePage, 122 | }, 123 | { 124 | path: '/my/fav-store/', 125 | component: My_FavStorePage, 126 | }, 127 | { 128 | path: '/my/fav-goods/', 129 | component: My_FavGoodsPage, 130 | }, 131 | { 132 | path: '/my/fav-article/', 133 | component: My_FavArticlePage, 134 | }, 135 | { 136 | path: '/my/fav-circletheme/', 137 | component: My_FavCirclethemePage, 138 | }, 139 | { 140 | path: '/my/fav-collection/', 141 | component: My_FavCollectionPage, 142 | }, 143 | { 144 | path: '/my/follow-circle/', 145 | component: My_FollowCirclePage, 146 | }, 147 | { 148 | path: '/my/follow-member/', 149 | component: My_FollowMemberPage, 150 | }, 151 | 152 | //{ 153 | // path: '/dynamic-route/blog/:blogId/post/:postId/', 154 | // component: DynamicRoutePage, 155 | //}, 156 | { 157 | path: '(.*)', 158 | component: NotFoundPage, 159 | }, 160 | ] 161 | }) 162 | 163 | //百度统计 164 | router.beforeEach((to, from, next) => { 165 | _hmt.push(['_trackPageview', '/v2/#' + to.path]) 166 | document.title = to.meta.title || '杏运树趣看' 167 | next() 168 | }) 169 | 170 | export default router; -------------------------------------------------------------------------------- /src/pages/my/fav-collection.vue: -------------------------------------------------------------------------------- 1 | 60 | 63 | -------------------------------------------------------------------------------- /src/pages/my/fav-store.vue: -------------------------------------------------------------------------------- 1 | 63 | 66 | -------------------------------------------------------------------------------- /src/hiapp-pages/post.vue: -------------------------------------------------------------------------------- 1 | 41 | 42 | 112 | 113 | -------------------------------------------------------------------------------- /src/pages/my/fav-goods.vue: -------------------------------------------------------------------------------- 1 | 64 | 67 | -------------------------------------------------------------------------------- /src/pages/my/fav-article.vue: -------------------------------------------------------------------------------- 1 | 64 | 103 | -------------------------------------------------------------------------------- /src/js/f7-base.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 注册vue的framework7基础全局变量与全局方法 3 | * 4 | * @param {Object} Vue 5 | * @param {Object} Framework7 6 | * @param {Object} options 7 | */ 8 | exports.install = function(Vue, Framework7, options) { 9 | Vue.prototype.$f7 = {}; 10 | Vue.prototype.$$ = {}; 11 | 12 | /** 13 | * 实例化f7对象,模仿f7做一些基本的初始化动作 14 | */ 15 | Vue.prototype.setF7 = function() { 16 | var self = this; 17 | var app = new Framework7({ 18 | theme: 'ios', // Automatic theme detection 19 | lazy: { 20 | threshold: 0, 21 | sequential: false, 22 | //laceholder: 'http://m.cattlepie.com/img/login/login_icon.png', 23 | placeholder: 'static/img/preload.png', 24 | }, 25 | dialog: { 26 | // set default title for all dialog shortcuts 27 | title: '杏运树', 28 | // change default "OK" button text 29 | buttonOk: '确定', 30 | buttonCancel: '取消', 31 | }, 32 | }); 33 | // app.router.off('touchstart'); 34 | 35 | this.$f7 = app; 36 | this.$$ = app.$; 37 | // console.log(app) 38 | 39 | //自动初始化 40 | if (this.$$('.infinite-scroll-content').length > 0) { 41 | app.infiniteScroll.create('.infinite-scroll-content'); 42 | } 43 | if (this.$$('.ptr-content').length > 0) { 44 | this.$f7.ptr = app.ptr.create('.ptr-content'); 45 | } 46 | if (this.$$('.link.back').length > 0) { 47 | this.$$('.link.back').each(function() { 48 | self.$$(this).on('click', function(e) { 49 | self.$router.goBack() 50 | // self.$router.go(-1); 51 | }); 52 | }); 53 | } 54 | 55 | return app; 56 | }; 57 | 58 | //创建gotop按钮并初始化滚动事件 59 | Vue.prototype.$initGotop = function() { 60 | var self = this; 61 | if (this.$$('#tbox').length<1) { 62 | var $li = this.$$('
'); 63 | $li.appendTo($(document.body)), 64 | $('#gotop').click(function(){ 65 | self.$scrollTop(0, 300); 66 | }) 67 | } 68 | 69 | self.$rightb(); 70 | this.$$('.page-content').scroll(function(e){ 71 | self.$rightb(); 72 | }) 73 | }, 74 | 75 | Vue.prototype.$rightb = function() { 76 | h = 200; 77 | t = this.$scrollTop(); 78 | // console.log(t) 79 | if(t > h){ 80 | //alert("111"); 81 | this.$$('#gotop').show(); 82 | }else{ 83 | this.$$('#gotop').hide(); 84 | } 85 | }, 86 | 87 | /** 88 | * 基于f7的滚动到顶部 89 | * @param {Object} position 90 | * @param {Object} duration 91 | * @param {Object} callback 92 | */ 93 | Vue.prototype.$scrollTop = function(position, duration, callback) { 94 | return this.$$('.page-content').scrollTop(position, duration, callback); 95 | }, 96 | 97 | /** 98 | * 基于f7的滚动到固定选择器 99 | * @param {Object} selector 100 | * @param {Object} speed 101 | */ 102 | Vue.prototype.$goAnchor = function(selector, speed) { 103 | speed = speed || 300; 104 | let offsetY = this.$$(selector).offset().top + this.$$('.page-content').scrollTop(); 105 | this.$scrollTop(offsetY, speed); 106 | }, 107 | 108 | /** 109 | * toast方法 110 | * 111 | * @param {Object} text 112 | * @param {Object} timeout 113 | * @param {Object} positon 114 | * @param {Object} closeButton 115 | * @param {Object} icon 116 | */ 117 | Vue.prototype.$toast = function(text, timeout, positon, closeButton, icon=null) { 118 | timeout = arguments[1] ? arguments[1] : 3000; 119 | positon = arguments[2] ? arguments[2] : 'bottom'; 120 | closeButton = arguments[3] ? arguments[3] : false; 121 | closeButtonText = arguments[4] ? arguments[4] : '关闭'; 122 | 123 | var params = { 124 | text: text, 125 | closeTimeout: timeout, 126 | position: positon, 127 | closeButton: closeButton, 128 | closeButtonText: closeButtonText, 129 | closeButtonColor: 'yellow', 130 | }; 131 | if(arguments[4] != null && arguments[4] != undefined) { 132 | //self.$theme.ios ? 'star' : 'star', 133 | if (icon) { 134 | params.icon = 'check'; 135 | } else { 136 | params.icon = 'info'; 137 | } 138 | } 139 | 140 | this.$f7.toast.create(params).open(); 141 | } 142 | 143 | Vue.prototype.$toastWithCallback = function() { 144 | var self = this; 145 | // Create toast 146 | if(!self.toastWithCallback) { 147 | self.toastWithCallback = self.$f7.toast.create({ 148 | text: 'Callback on close', 149 | closeButton: true, 150 | on: { 151 | close: function() { 152 | self.$f7.dialog.alert('Toast closed'); 153 | }, 154 | } 155 | }); 156 | } 157 | // Open it 158 | self.toastWithCallback.open(); 159 | } 160 | 161 | //打开单张图片浏览器 162 | Vue.prototype.$openOnePhoto = function(url) { 163 | var pb = this.$f7.photoBrowser.create({ 164 | photos: [url], 165 | theme: 'dark', 166 | type: 'popup', //popup, page, standalone 167 | 168 | navbarOfText: '/', 169 | backLinkText: '', 170 | toolbar: false, 171 | routableModals: false, 172 | }); 173 | pb.on('click', function() { 174 | this.close(); 175 | }); 176 | 177 | pb.open(); 178 | return pb; 179 | }; 180 | 181 | //创建相册浏览器,不直接打开 182 | Vue.prototype.$createPhotoBrowser = function(_photos) { 183 | var pb = this.$f7.photoBrowser.create({ 184 | photos: _photos, 185 | theme: 'dark', 186 | type: 'popup', //popup, page, standalone 187 | 188 | navbarOfText: '/', 189 | backLinkText: '关闭', 190 | routableModals: false, 191 | }); 192 | pb.on('click', function() { 193 | this.close(); 194 | }); 195 | 196 | return pb; 197 | }; 198 | 199 | //遮罩层开关 200 | Vue.prototype.$toggleBackdrop = function() { 201 | if('none' == this.$$('.backdrop-in').css('display')) { 202 | this.$$('.backdrop-in').show(500); 203 | } else { 204 | this.$$('.backdrop-in').hide(500); 205 | } 206 | }; 207 | }; -------------------------------------------------------------------------------- /src/pages/my/follow-circle.vue: -------------------------------------------------------------------------------- 1 | 68 | 107 | -------------------------------------------------------------------------------- /src/pages/my/circletheme.vue: -------------------------------------------------------------------------------- 1 | 66 | 118 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-f7-circle说明 2 | 3 | > vue-f7-circle是一套基于framework7(http://framework7.io/) + Vue全家桶(Vue2.x + Vue-router2.x + Vuex)的用于实际项目的圈子社交系统。 4 | 5 | > 本项目最初是用framework7自整理的Framework7 Vue项目框架http://framework7.io/vue/ 开发,后来发现返回页面时,容易出现页面被挡住的现象,对照官方文档研究两天无法解决。后转为基于vue,将framework7的核心方法与对象作为vue的全局方法与对象,界面交互api与界面样式,还是用的framework7的,体验效果还不错。 6 | 7 | [项目演示地址 http://m.cattlepie.com/v2/](http://m.cattlepie.com/v2/) 8 | 9 | 项目代码所含功能板块为: 10 | 1. 圈子,帖子 11 | 2. 合集(店铺+文章+圈子组合) 12 | 3. 文章 13 | 4. 店铺 14 | 15 | 本项目依赖服务端接口,服务端接口项目后续将整理上传 16 | 17 | ## main.js 18 | ```js 19 | //vue-lazyload 20 | import VueLazyload from 'vue-lazyload'; 21 | Vue.use(VueLazyload, { 22 | preLoad: 1.3, 23 | error: 'static/img/preload.png', 24 | loading: 'static/img/preload.png', 25 | attempt: 2 26 | }); 27 | 28 | //引入vue基础框架 29 | import base from './js/base.js'; 30 | import config from './js/config.js'; 31 | import wap from './js/wap.js'; 32 | Vue.use(base, config, wap); 33 | 34 | //引入f7基础框架 35 | //import Framework7 from 'framework7/dist/framework7.esm.bundle.js'; 36 | import Framework7 from 'framework7'; 37 | import Swiper from 'framework7/dist/components/swiper/swiper.js'; 38 | import Popup from 'framework7/dist/components/popup/popup.js'; 39 | import Popover from 'framework7/dist/components/popover/popover.js'; 40 | import Preloader from 'framework7/dist/components/preloader/preloader.js'; 41 | import PullToRefresh from 'framework7/dist/components/pull-to-refresh/pull-to-refresh.js'; 42 | import Toast from 'framework7/dist/components/toast/toast.js'; 43 | import PhotoBrowser from 'framework7/dist/components/photo-browser/photo-browser.js'; 44 | import Dialog from 'framework7/dist/components/dialog/dialog.js'; 45 | import Sheet from 'framework7/dist/components/sheet/sheet.js'; 46 | import InfiniteScroll from 'framework7/dist/components/infinite-scroll/infinite-scroll.js'; 47 | 48 | Framework7.use([Swiper,Popup,Popover,Preloader,PullToRefresh,Toast,PhotoBrowser,Dialog,Sheet,InfiniteScroll]); 49 | 50 | import F7Base from './js/f7-base.js' 51 | Vue.use(F7Base, Framework7); 52 | 53 | 54 | var _hmt = window._hmt || [] 55 | 56 | /* eslint-disable no-new */ 57 | new Vue({ 58 | el: '#app', 59 | store, 60 | router, 61 | components: { App }, 62 | template: '' 63 | }) 64 | ``` 65 | ## f7全局对象与方法注册 66 | ```js 67 | /** 68 | * 注册vue的framework7基础全局变量与全局方法 69 | * 70 | * @param {Object} Vue 71 | * @param {Object} Framework7 72 | * @param {Object} options 73 | */ 74 | exports.install = function(Vue, Framework7, options) { 75 | Vue.prototype.$f7 = {}; 76 | Vue.prototype.$$ = {}; 77 | 78 | /** 79 | * 实例化f7对象,模仿f7做一些基本的初始化动作 80 | */ 81 | Vue.prototype.setF7 = function() { 82 | var self = this; 83 | var app = new Framework7({ 84 | theme: 'ios', // Automatic theme detection 85 | lazy: { 86 | threshold: 0, 87 | sequential: false, 88 | //laceholder: 'http://m.cattlepie.com/img/login/login_icon.png', 89 | placeholder: 'static/img/preload.png', 90 | }, 91 | dialog: { 92 | // set default title for all dialog shortcuts 93 | title: '杏运树', 94 | // change default "OK" button text 95 | buttonOk: '确定', 96 | buttonCancel: '取消', 97 | }, 98 | }); 99 | // app.router.off('touchstart'); 100 | 101 | this.$f7 = app; 102 | this.$$ = app.$; 103 | // console.log(app) 104 | 105 | //自动初始化 106 | if (this.$$('.infinite-scroll-content').length > 0) { 107 | app.infiniteScroll.create('.infinite-scroll-content'); 108 | } 109 | if (this.$$('.ptr-content').length > 0) { 110 | this.$f7.ptr = app.ptr.create('.ptr-content'); 111 | } 112 | if (this.$$('.link.back').length > 0) { 113 | this.$$('.link.back').each(function() { 114 | self.$$(this).on('click', function(e) { 115 | self.$router.goBack() 116 | // self.$router.go(-1); 117 | }); 118 | }); 119 | } 120 | 121 | return app; 122 | }; 123 | 124 | /** 125 | * 基于f7的滚动到顶部 126 | * @param {Object} position 127 | * @param {Object} duration 128 | * @param {Object} callback 129 | */ 130 | Vue.prototype.$scrollTop = function(position, duration, callback) { 131 | return this.$$('.page-content').scrollTop(position, duration, callback); 132 | }, 133 | 134 | /** 135 | * 基于f7的滚动到固定选择器 136 | * @param {Object} selector 137 | * @param {Object} speed 138 | */ 139 | Vue.prototype.$goAnchor = function(selector, speed) { 140 | speed = speed || 300; 141 | let offsetY = this.$$(selector).offset().top + this.$$('.page-content').scrollTop(); 142 | this.$scrollTop(offsetY, speed); 143 | } 144 | } 145 | ``` 146 | ## 截图演示 147 | ![Image text](https://raw.githubusercontent.com/evangui/vue-f7-circle/master/screenshots/index.png) 148 | ![](https://raw.githubusercontent.com/evangui/vue-f7-circle/master/screenshots/circlelist.png) 149 | ![](https://raw.githubusercontent.com/evangui/vue-f7-circle/master/screenshots/circledetail.png) 150 | ![](https://raw.githubusercontent.com/evangui/vue-f7-circle/master/screenshots/circledetail2.png) 151 | ![](https://raw.githubusercontent.com/evangui/vue-f7-circle/master/screenshots/circledetail3.png) 152 | ![](https://raw.githubusercontent.com/evangui/vue-f7-circle/master/screenshots/articledetail.png) 153 | ![](https://raw.githubusercontent.com/evangui/vue-f7-circle/master/screenshots/articledetail2.png) 154 | ![](https://raw.githubusercontent.com/evangui/vue-f7-circle/master/screenshots/articledetail3.png) 155 | ![](https://raw.githubusercontent.com/evangui/vue-f7-circle/master/screenshots/articlelist.png) 156 | ![](https://raw.githubusercontent.com/evangui/vue-f7-circle/master/screenshots/collectiondetail.png) 157 | ![](https://raw.githubusercontent.com/evangui/vue-f7-circle/master/screenshots/collectiondetail11.png) 158 | ![](https://raw.githubusercontent.com/evangui/vue-f7-circle/master/screenshots/collectiondetail12.png) 159 | ![](https://raw.githubusercontent.com/evangui/vue-f7-circle/master/screenshots/post.png) 160 | 161 | ## Build Setup 162 | 163 | ``` bash 164 | # install dependencies 165 | npm install 166 | 167 | # serve with hot reload at localhost:8080 168 | npm run dev 169 | 170 | # build for production with minification 171 | npm run build 172 | 173 | # build for production and view the bundle analyzer report 174 | npm run build --report 175 | 176 | # run unit tests 177 | npm run unit 178 | 179 | # run e2e tests 180 | npm run e2e 181 | 182 | # run all tests 183 | npm test 184 | ``` 185 | 186 | 187 | -------------------------------------------------------------------------------- /src/pages/my/fav-circletheme.vue: -------------------------------------------------------------------------------- 1 | 75 | 128 | -------------------------------------------------------------------------------- /src/pages/circle-list.vue: -------------------------------------------------------------------------------- 1 | 89 | 128 | -------------------------------------------------------------------------------- /src/pages/collection-list.vue: -------------------------------------------------------------------------------- 1 | 80 | 118 | -------------------------------------------------------------------------------- /src/pages/article-list.vue: -------------------------------------------------------------------------------- 1 | 85 | 97 | -------------------------------------------------------------------------------- /src/pages/store-list.vue: -------------------------------------------------------------------------------- 1 | 78 | 109 | -------------------------------------------------------------------------------- /src/css/webuploader-demo.min.css: -------------------------------------------------------------------------------- 1 | #container { 2 | color: #838383; 3 | font-size: 12px 4 | } 5 | 6 | #uploader .queueList { 7 | margin: 20px; 8 | border: 3px dashed #e6e6e6 9 | } 10 | 11 | #uploader .queueList.filled { 12 | padding: 17px; 13 | margin: 0; 14 | border: 3px dashed transparent 15 | } 16 | 17 | #uploader .queueList.webuploader-dnd-over { 18 | border: 3px dashed #999 19 | } 20 | 21 | #uploader p { 22 | margin: 0 23 | } 24 | 25 | .element-invisible { 26 | position: absolute !important; 27 | clip: rect(1px 1px 1px 1px); 28 | clip: rect(1px, 1px, 1px, 1px) 29 | } 30 | 31 | #uploader .placeholder { 32 | min-height: 100px; 33 | padding-top: 40px; 34 | text-align: center; 35 | color: #ccc; 36 | font-size: 18px; 37 | position: relative 38 | } 39 | 40 | #uploader .placeholder .webuploader-pick { 41 | font-size: 18px; 42 | background: #00b7ee; 43 | border-radius: 3px; 44 | line-height: 44px; 45 | padding: 0 30px; 46 | *width: 120px; 47 | color: #fff; 48 | display: inline-block; 49 | margin: 0 auto 20px; 50 | cursor: pointer; 51 | box-shadow: 0 1px 1px rgba(0, 0, 0, .1) 52 | } 53 | 54 | #uploader .placeholder .webuploader-pick-hover { 55 | background: #00a2d4 56 | } 57 | 58 | #uploader .placeholder .flashTip { 59 | color: #666; 60 | font-size: 12px; 61 | position: absolute; 62 | width: 100%; 63 | text-align: center; 64 | bottom: 20px 65 | } 66 | 67 | #uploader .placeholder .flashTip a { 68 | color: #0785d1; 69 | text-decoration: none 70 | } 71 | 72 | #uploader .placeholder .flashTip a:hover { 73 | text-decoration: underline 74 | } 75 | 76 | #uploader .filelist { 77 | list-style: none; 78 | margin: 0; 79 | padding: 0 80 | } 81 | 82 | #uploader .filelist:after { 83 | content: ''; 84 | display: block; 85 | width: 0; 86 | height: 0; 87 | overflow: hidden; 88 | clear: both 89 | } 90 | 91 | #uploader .filelist li { 92 | width: 110px; 93 | height: 110px; 94 | text-align: center; 95 | margin: 0 8px 20px 0; 96 | position: relative; 97 | display: inline; 98 | float: left; 99 | overflow: hidden; 100 | font-size: 12px 101 | } 102 | 103 | #uploader .filelist li p.log { 104 | position: relative; 105 | top: -45px 106 | } 107 | 108 | #uploader .filelist li p.title { 109 | position: absolute; 110 | left: 0; 111 | width: 100%; 112 | overflow: hidden; 113 | white-space: nowrap; 114 | text-overflow: ellipsis; 115 | top: 5px; 116 | text-indent: 5px; 117 | text-align: left 118 | } 119 | 120 | #uploader .filelist li p.progress { 121 | position: absolute; 122 | width: 100%; 123 | bottom: 0; 124 | left: 0; 125 | height: 8px; 126 | overflow: hidden; 127 | z-index: 50; 128 | margin: 0; 129 | border-radius: 0; 130 | background: 0 0; 131 | -webkit-box-shadow: 0 0 0 132 | } 133 | 134 | #uploader .filelist li p.progress span { 135 | display: none; 136 | overflow: hidden; 137 | width: 0; 138 | height: 100%; 139 | -webit-transition: width 200ms linear; 140 | -moz-transition: width 200ms linear; 141 | -o-transition: width 200ms linear; 142 | -ms-transition: width 200ms linear; 143 | transition: width 200ms linear; 144 | -webkit-animation: progressmove 2s linear infinite; 145 | -moz-animation: progressmove 2s linear infinite; 146 | -o-animation: progressmove 2s linear infinite; 147 | -ms-animation: progressmove 2s linear infinite; 148 | animation: progressmove 2s linear infinite; 149 | -webkit-transform: translateZ(0) 150 | } 151 | 152 | @-webkit-keyframes progressmove { 153 | 0% { 154 | background-position: 0 0 155 | } 156 | 100% { 157 | background-position: 17px 0 158 | } 159 | } 160 | 161 | @-moz-keyframes progressmove { 162 | 0% { 163 | background-position: 0 0 164 | } 165 | 100% { 166 | background-position: 17px 0 167 | } 168 | } 169 | 170 | @keyframes progressmove { 171 | 0% { 172 | background-position: 0 0 173 | } 174 | 100% { 175 | background-position: 17px 0 176 | } 177 | } 178 | 179 | #uploader .filelist li p.imgWrap { 180 | position: relative; 181 | z-index: 2; 182 | line-height: 110px; 183 | vertical-align: middle; 184 | overflow: hidden; 185 | width: 110px; 186 | height: 110px; 187 | -webkit-transform-origin: 50% 50%; 188 | -moz-transform-origin: 50% 50%; 189 | -o-transform-origin: 50% 50%; 190 | -ms-transform-origin: 50% 50%; 191 | transform-origin: 50% 50%; 192 | -webit-transition: 200ms ease-out; 193 | -moz-transition: 200ms ease-out; 194 | -o-transition: 200ms ease-out; 195 | -ms-transition: 200ms ease-out; 196 | transition: 200ms ease-out 197 | } 198 | 199 | #uploader .filelist li img { 200 | width: 100% 201 | } 202 | 203 | #uploader .filelist li p.error { 204 | background: #f43838; 205 | color: #fff; 206 | position: absolute; 207 | bottom: 0; 208 | left: 0; 209 | height: 28px; 210 | line-height: 28px; 211 | width: 100%; 212 | z-index: 100 213 | } 214 | 215 | #uploader .filelist li .success { 216 | display: block; 217 | position: absolute; 218 | left: 0; 219 | bottom: 0; 220 | height: 40px; 221 | width: 100%; 222 | z-index: 200; 223 | } 224 | 225 | #uploader .filelist div.file-panel { 226 | position: absolute; 227 | height: 0; 228 | filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#80000000', endColorstr='#80000000') \0; 229 | background: rgba(0, 0, 0, .5); 230 | width: 100%; 231 | top: 0; 232 | left: 0; 233 | overflow: hidden; 234 | z-index: 300 235 | } 236 | 237 | #uploader .filelist div.file-panel span { 238 | width: 24px; 239 | height: 24px; 240 | display: inline; 241 | float: right; 242 | text-indent: -9999px; 243 | overflow: hidden; 244 | margin: 5px 1px 1px; 245 | cursor: pointer 246 | } 247 | 248 | #uploader .filelist div.file-panel span.rotateLeft { 249 | background-position: 0 -24px 250 | } 251 | 252 | #uploader .filelist div.file-panel span.rotateLeft:hover { 253 | background-position: 0 0 254 | } 255 | 256 | #uploader .filelist div.file-panel span.rotateRight { 257 | background-position: -24px -24px 258 | } 259 | 260 | #uploader .filelist div.file-panel span.rotateRight:hover { 261 | background-position: -24px 0 262 | } 263 | 264 | #uploader .filelist div.file-panel span.cancel { 265 | background-position: -48px -24px 266 | } 267 | 268 | #uploader .filelist div.file-panel span.cancel:hover { 269 | background-position: -48px 0 270 | } 271 | 272 | #uploader .statusBar { 273 | height: 63px; 274 | border-top: 1px solid #dadada; 275 | padding: 0 20px; 276 | line-height: 63px; 277 | vertical-align: middle; 278 | position: relative 279 | } 280 | 281 | #uploader .statusBar .progress { 282 | border: 1px solid #1483d8; 283 | width: 198px; 284 | background: #fff; 285 | height: 18px; 286 | display: inline-block; 287 | text-align: center; 288 | line-height: 20px; 289 | color: #6dbfff; 290 | position: relative; 291 | margin: 0 10px 0 0 292 | } 293 | 294 | #uploader .statusBar .progress span.percentage { 295 | width: 0; 296 | height: 100%; 297 | left: 0; 298 | top: 0; 299 | background: #1483d8; 300 | position: absolute 301 | } 302 | 303 | #uploader .statusBar .progress span.text { 304 | position: relative; 305 | z-index: 10 306 | } 307 | 308 | #uploader .statusBar .info { 309 | display: inline-block; 310 | font-size: 14px; 311 | color: #666 312 | } 313 | 314 | #uploader .statusBar .btns { 315 | position: absolute; 316 | top: 10px; 317 | right: 20px; 318 | line-height: 40px 319 | } 320 | 321 | #filePicker2 { 322 | display: inline-block; 323 | float: left 324 | } 325 | 326 | #uploader .statusBar .btns .uploadBtn, #uploader .statusBar .btns .uploadBtn.state-paused, #uploader .statusBar .btns .uploadBtn.state-uploading, #uploader .statusBar .btns .webuploader-pick { 327 | background: #fff; 328 | border: 1px solid #cfcfcf; 329 | color: #565656; 330 | padding: 0 18px; 331 | display: inline-block; 332 | border-radius: 3px; 333 | margin-left: 10px; 334 | cursor: pointer; 335 | font-size: 14px; 336 | float: left 337 | } 338 | 339 | #uploader .statusBar .btns .uploadBtn.state-paused:hover, #uploader .statusBar .btns .uploadBtn.state-uploading:hover, #uploader .statusBar .btns .uploadBtn:hover, #uploader .statusBar .btns .webuploader-pick-hover { 340 | background: #f0f0f0 341 | } 342 | 343 | #uploader .statusBar .btns .uploadBtn { 344 | background: #00b7ee; 345 | color: #fff; 346 | border-color: transparent 347 | } 348 | 349 | #uploader .statusBar .btns .uploadBtn:hover { 350 | background: #00a2d4 351 | } 352 | 353 | #uploader .statusBar .btns .uploadBtn.disabled { 354 | pointer-events: none; 355 | opacity: .6 356 | } 357 | -------------------------------------------------------------------------------- /src/store/actions.js: -------------------------------------------------------------------------------- 1 | import wap from '../js/wap.js' 2 | 3 | /** 4 | * 顶/取消顶 各类资源对象 5 | */ 6 | export function upItem({commit}, {objType, objId, type, callback = () => {} }) { 7 | let mutation = ''; 8 | let apiUpType = ''; 9 | switch(objType) { 10 | case 'article': 11 | mutation = 'CHANGE_ARTICLE'; 12 | apiUpType = 'article'; 13 | break 14 | case 'article_reply': 15 | mutation = 'CHANGE_ARTICLE_REPLY'; 16 | apiUpType = 'article_reply'; 17 | break 18 | case 'theme': 19 | mutation = 'CHANGE_THEME'; 20 | apiUpType = 'theme'; 21 | break 22 | case 'theme_reply': 23 | mutation = 'CHANGE_THEME_REPLY'; 24 | apiUpType = 'theme_reply'; 25 | break 26 | } 27 | 28 | let data = { 29 | obj_id: objId, 30 | up_type: apiUpType, 31 | }; 32 | let apiUri = type == 'up' ? 'api/user/memberup/up' : 'api/user/memberup/cancelUp'; 33 | //提前改变本地状态 34 | commit(mutation, { 35 | mid: data.obj_id, 36 | type: type //up or unup 37 | }); 38 | 39 | wap.doajax(apiUri, data, function(res) { 40 | if(res.code != '200' && res.code != '202') { 41 | //服务端处理失败,反向改变本地状态 42 | commit(mutation, { 43 | mid: data.obj_id, 44 | type: type == 'up' ? 'unup' : 'up' //up or unup 45 | }); 46 | } 47 | 48 | callback(res); 49 | }, 'v1', true); 50 | } 51 | 52 | /** 53 | * 收藏/取消收藏 各类资源对象 54 | */ 55 | export function likeItem({commit}, {objType, objId, type, callback = () => {} }) { 56 | let mutation = ''; 57 | let apiUpType = ''; 58 | switch(objType) { 59 | case 'article': 60 | mutation = 'CHANGE_ARTICLE'; 61 | apiUpType = 'article'; 62 | break; 63 | case 'theme': 64 | mutation = 'CHANGE_THEME'; 65 | apiUpType = 'theme'; 66 | break; 67 | case 'collection': 68 | mutation = 'CHANGE_COLLECTION'; 69 | apiUpType = 'collection'; 70 | break; 71 | case 'store': 72 | mutation = 'CHANGE_STORE'; 73 | apiUpType = 'store'; 74 | break; 75 | case 'goods': 76 | mutation = 'CHANGE_GOODS'; 77 | apiUpType = 'goods'; 78 | break; 79 | } 80 | 81 | let data = { 82 | obj_id: objId, 83 | fav_type: apiUpType, 84 | }; 85 | let apiUri = type == 'like' ? 'api/user/memberfav/fav' : 'api/user/memberfav/cancelFav'; 86 | //提前改变本地状态 87 | commit(mutation, { 88 | mid: data.obj_id, 89 | type: type //like or unlike 90 | }); 91 | 92 | wap.doajax(apiUri, data, function(res) { 93 | if(res.code != '200' && res.code != '202') { 94 | //服务端处理失败,反向改变本地状态 95 | commit(mutation, { 96 | mid: data.obj_id, 97 | type: type == 'like' ? 'unlike' : 'like' //up or unup 98 | }); 99 | } 100 | 101 | callback(res); 102 | }, 'v1', true); 103 | } 104 | 105 | /** 106 | * 文章动态变化数据 服务端获取,并写入state 107 | */ 108 | export function getArticleMut({commit}, {data={}, callback = () => {} }) { 109 | wap.getajax('api/user/cmsarticle/mutDetail', data, function(res, fromCache) { 110 | if(res.status == 'success') { 111 | let _item = { 112 | article_id : res.data.article_id, 113 | article_like_count : res.data.article_like_count, 114 | article_click: res.data.article_click, 115 | article_up: res.data.article_up, 116 | article_comment_count: res.data.article_comment_count, 117 | liked: res.data.liked || 0, 118 | uped: res.data.uped || 0, 119 | }; 120 | commit('ADD_ARTICLES_MUT', {items: [_item]}); 121 | } 122 | callback(res); 123 | 124 | }, 'v1', true, 0); 125 | } 126 | 127 | /** 128 | * 文章点击服务端处理,并写入state 129 | */ 130 | export function clickArticle({commit}, {article_id, callback = () => {} }) { 131 | let data = {article_id: article_id}; 132 | wap.getajax('api/user/cmsarticle/click', data, function(res) { 133 | if(res.status == 'success') { 134 | commit('CHANGE_ARTICLE', { 135 | mid: article_id, 136 | type: 'click', 137 | }); 138 | } 139 | callback(); 140 | }, 'v1', true); 141 | } 142 | 143 | /** 144 | * 文章回复列表数据 服务端获取,并将可变化数据写入state 145 | */ 146 | export function getArticleReplies({commit}, {data={}, cacheTime=0, callback = () => {} }) { 147 | wap.getajax('api/user/articlereply/searchList', data, function(res, fromCache) { 148 | // debugger 149 | if(res.status == 'success') { 150 | commit('ADD_ARTICLE_REPLIES_MUT', {items: res.data.datalist}); 151 | callback(res); 152 | } 153 | 154 | }, 'v1', true, cacheTime); 155 | } 156 | 157 | /** 158 | * 圈子数据 服务端获取,并将可变化数据写入state 159 | */ 160 | export function getCircleMut({commit,getters}, {circleID, callback = () => {} }) { 161 | // debugger 162 | //详情 163 | wap.getajax('api/user/circle/detail?circle_id=' + circleID, {}, function(res) { 164 | if(!wap.checkLogin(false)) { 165 | callback(res); 166 | return; 167 | } 168 | 169 | //关注状态等动态数据 170 | let mutCircle = getters.getMutCircle(circleID, 0); 171 | if (undefined != mutCircle) { 172 | callback(res); 173 | return; 174 | } 175 | 176 | //如果不存在,则重新获取变化数据 177 | wap.getajax('api/user/circle/getJoinState?circle_id=' + circleID, {}, function(res2, fromCache) { 178 | if(res2.status == 'success') { 179 | // if (!fromCache) { 180 | let _item = { 181 | circle_id: res.data.circle_id, 182 | circle_thcount: res.data.circle_thcount, 183 | circle_mcount: res.data.circle_mcount, 184 | joined: res2.data.join_state[0] == 1 ? 1 : 0, 185 | }; 186 | commit('ADD_CIRCLES_MUT', {items: [_item]}); 187 | // } 188 | callback(res); 189 | } 190 | }, 'v1', true, 0); 191 | 192 | }, 'v1', true, 3600); 193 | 194 | } 195 | 196 | /** 197 | * 加入、退出圈子 服务端处理,并写入state 198 | */ 199 | export function joinCircle({commit}, {circle_id,circle_name, type, callback = () => {} }) { 200 | let data = { 201 | circle_id: circle_id, 202 | circle_name: circle_name, 203 | }; 204 | 205 | let apiUri = type == 'join' ? 'api/user/circle/join' : 'api/user/circle/quit'; 206 | 207 | wap.doajax(apiUri, data, function(res) { 208 | if(res.code == '200' || res.code == '403') { 209 | commit('CHANGE_CIRCLE', { 210 | mid: data.circle_id, 211 | type: type //join or unjoin 212 | }); 213 | } 214 | 215 | callback(res); 216 | }, 'v1', true); 217 | } 218 | 219 | /** 220 | * 主题帖数据 服务端获取,并将可变化数据写入state 221 | */ 222 | export function getThemes({commit}, {data={}, cacheTime=0, callback = () => {} }) { 223 | wap.getajax('api/user/circletheme/searchList', data, function(res, fromCache) { 224 | if(res.status == 'success') { 225 | commit('ADD_THEMES_MUT', {items: res.data.datalist}); 226 | callback(res); 227 | } 228 | 229 | }, 'v1', true, cacheTime); 230 | } 231 | 232 | 233 | /** 234 | * 帖子变化数据 服务端获取,并写入state 235 | */ 236 | export function getThemeMut({commit}, {data={}, callback = () => {} }) { 237 | wap.getajax('api/user/circletheme/mutDetail', data, function(res, fromCache) { 238 | if(res.status == 'success') { 239 | let _item = { 240 | theme_id : res.data.theme_id, 241 | theme_like_count : res.data.theme_like_count, 242 | theme_up_count: res.data.theme_up_count, 243 | theme_commentcount: res.data.theme_commentcount, 244 | liked: res.data.liked || 0, 245 | uped: res.data.uped || 0, 246 | }; 247 | commit('ADD_THEMES_MUT', {items: [_item]}); 248 | } 249 | callback(res); 250 | 251 | }, 'v1', true, 0); 252 | } 253 | 254 | 255 | /** 256 | * 文章回复列表数据 服务端获取,并将可变化数据写入state 257 | */ 258 | export function getThemeReplies({commit}, {data={}, cacheTime=0, callback = () => {} }) { 259 | wap.getajax('api/user/circlethreply/searchList', data, function(res, fromCache) { 260 | // debugger 261 | if(res.status == 'success') { 262 | commit('ADD_THEME_REPLIES_MUT', {items: res.data.datalist}); 263 | callback(res); 264 | } 265 | 266 | }, 'v1', true, cacheTime); 267 | } 268 | 269 | 270 | /** 271 | * 主题帖数据 服务端获取,并将可变化数据写入state 272 | */ 273 | export function getCollections({commit}, {data={}, cacheTime=0, callback = () => {} }) { 274 | wap.getajax('api/user/collection/searchList', data, function(res, fromCache) { 275 | if(res.status == 'success') { 276 | commit('ADD_COLLECTIONS_MUT', {items: res.data.datalist}); 277 | callback(res); 278 | } 279 | 280 | }, 'v1', true, cacheTime); 281 | } 282 | 283 | 284 | /** 285 | * 帖子变化数据 服务端获取,并写入state 286 | */ 287 | export function getCollectionMut({commit}, {data={}, callback = () => {} }) { 288 | wap.getajax('api/user/collection/mutDetail', data, function(res, fromCache) { 289 | if(res.status == 'success') { 290 | let _item = { 291 | cl_id : res.data.cl_id, 292 | cl_like_count : res.data.cl_like_count, 293 | uped: res.data.uped, 294 | liked: res.data.liked, 295 | }; 296 | commit('ADD_COLLECTIONS_MUT', {items: [_item]}); 297 | } 298 | callback(res); 299 | 300 | }, 'v1', true, 300); 301 | } 302 | 303 | 304 | 305 | /** 306 | * 帖子变化数据 服务端获取,并写入state 307 | */ 308 | export function getStoreMut({commit}, {data={}, callback = () => {} }) { 309 | wap.getajax('api/user/store/mutDetail', data, function(res, fromCache) { 310 | if(res.status == 'success') { 311 | let _item = { 312 | store_id : res.data.store_id, 313 | liked: res.data.liked || 0, 314 | uped: res.data.uped || 0, 315 | }; 316 | commit('ADD_COLLECTIONS_MUT', {items: [_item]}); 317 | } 318 | callback(res); 319 | 320 | }, 'v1', true, 300); 321 | } -------------------------------------------------------------------------------- /src/store/mutations.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | export default { 4 | SET_SCROLL_TOP(state, {k,v} ) { 5 | Vue.set(state.position, k, v); 6 | // state.position = val; 7 | }, 8 | 9 | /** 10 | * 文章变化数据 记录到state 11 | */ 12 | ADD_ARTICLES_MUT(state, { 13 | items 14 | }) { 15 | // for (var i=0; i p.article_id === items[i].article_id); 17 | // if (undefined == item) { 18 | // state.stateArticles.push(items[i]); 19 | // } 20 | // } 21 | for(var i = 0; i < items.length; i++) { 22 | let k = 'k' + items[i].article_id; 23 | Vue.set(state.mutArticlesState, k, items[i]); 24 | } 25 | }, 26 | 27 | /** 28 | * 文章 单项记录值 修改 29 | */ 30 | CHANGE_ARTICLE(state, { 31 | mid, 32 | type, 33 | val=1 34 | }) { 35 | let item = state.mutArticlesState['k' + mid]; 36 | let update = {} 37 | switch(type) { 38 | case 'up': 39 | update.article_up = item.article_up + val 40 | update.uped = true 41 | break; 42 | case 'unup': 43 | update.article_up = item.article_up - val 44 | update.uped = false 45 | break; 46 | case 'like': 47 | update.article_like_count = item.article_like_count + val 48 | update.liked = true 49 | break; 50 | case 'unlike': 51 | update.article_like_count = item.article_like_count - val 52 | update.liked = false 53 | break; 54 | case 'click': 55 | update.article_click = item.article_click + 1 56 | break; 57 | case 'reply': 58 | update.article_comment_count = item.article_comment_count + 1 59 | break; 60 | } 61 | 62 | // Yes, Object.assign can update state and UI component at same time. 63 | item = Object.assign(item, update) 64 | }, 65 | 66 | /** 67 | * 文章回复 的变化数据 记录到state 68 | */ 69 | ADD_ARTICLE_REPLIES_MUT(state, { 70 | items, cover=0 71 | }) { 72 | for(var i = 0; i < items.length; i++) { 73 | let k = 'k' + items[i].reply_id; 74 | //如果state里有该项数据,则忽略不覆盖 75 | if (undefined !== state.mutArticleRepliesState[k] && !cover) { 76 | continue; 77 | } 78 | 79 | let item = { 80 | reply_up: items[i].reply_up, 81 | uped: items[i].uped || 0, 82 | }; 83 | Vue.set(state.mutArticleRepliesState, k, item); 84 | } 85 | }, 86 | 87 | /** 88 | * 文章回复 的记录值 修改 89 | */ 90 | CHANGE_ARTICLE_REPLY(state, { 91 | mid, 92 | type 93 | }) { 94 | let item = state.mutArticleRepliesState['k' + mid]; 95 | let update = {} 96 | switch(type) { 97 | case 'up': 98 | update.reply_up = item.reply_up + 1 99 | update.uped = true 100 | break 101 | case 'unup': 102 | update.reply_up = item.reply_up - 1 103 | update.uped = false 104 | break 105 | } 106 | 107 | // Yes, Object.assign can update state and UI component at same time. 108 | item = Object.assign(item, update) 109 | }, 110 | 111 | /** 112 | * 帖子 的变化数据 记录到state 113 | */ 114 | ADD_COLLECTIONS_MUT(state, { 115 | items, cover=0 116 | }) { 117 | for(var i = 0; i < items.length; i++) { 118 | let k = 'k' + items[i].cl_id; 119 | //如果state里有该项数据,则忽略不覆盖 120 | if (undefined !== state.mutCollectionsState[k] && !cover) { 121 | continue; 122 | } 123 | 124 | let item = { 125 | cl_like_count: items[i].cl_like_count, 126 | liked: items[i].liked || 0, 127 | }; 128 | Vue.set(state.mutCollectionsState, k, item); 129 | } 130 | }, 131 | 132 | /** 133 | * 帖子 的变化数据 记录到state 134 | */ 135 | CHANGE_COLLECTION(state, { 136 | mid, 137 | type 138 | }) { 139 | let item = state.mutCollectionsState['k' + mid]; 140 | let update = {} 141 | switch(type) { 142 | case 'like': 143 | update.cl_like_count = item.cl_like_count + 1 144 | update.liked = true 145 | break 146 | case 'unlike': 147 | update.cl_like_count = item.cl_like_count - 1 148 | update.liked = false 149 | break 150 | } 151 | 152 | // Yes, Object.assign can update state and UI component at same time. 153 | item = Object.assign(item, update) 154 | }, 155 | 156 | 157 | /** 158 | * 圈子 的变化数据 记录到state 159 | */ 160 | ADD_CIRCLES_MUT(state, { 161 | items 162 | }) { 163 | for(var i = 0; i < items.length; i++) { 164 | let k = 'k' + items[i].circle_id; 165 | //如果state里有该项数据,则忽略不覆盖 166 | if (undefined !== state.mutCirclesState[k]) { 167 | continue; 168 | } 169 | 170 | Vue.set(state.mutCirclesState, k, items[i]); 171 | } 172 | }, 173 | 174 | /** 175 | * 圈子 的记录值 修改 176 | */ 177 | CHANGE_CIRCLE(state, { 178 | mid, 179 | type 180 | }) { 181 | let item = state.mutCirclesState['k' + mid]; 182 | let update = {} 183 | switch(type) { 184 | case 'join': 185 | update.circle_mcount = item.circle_mcount + 1 186 | update.joined = true 187 | break 188 | case 'unjoin': 189 | update.circle_mcount = item.circle_mcount - 1 190 | update.joined = false 191 | break 192 | } 193 | 194 | // Yes, Object.assign can update state and UI component at same time. 195 | item = Object.assign(item, update) 196 | }, 197 | 198 | /** 199 | * 帖子 的变化数据 记录到state 200 | */ 201 | ADD_THEMES_MUT(state, { 202 | items, cover=0 203 | }) { 204 | for(var i = 0; i < items.length; i++) { 205 | let k = 'k' + items[i].theme_id; 206 | //如果state里有该项数据,则忽略不覆盖 207 | if (undefined !== state.mutThemesState[k] && !cover) { 208 | continue; 209 | } 210 | 211 | let item = { 212 | theme_like_count: items[i].theme_like_count, 213 | theme_up_count: items[i].theme_up_count, 214 | theme_commentcount: items[i].theme_commentcount, 215 | liked: items[i].liked || 0, 216 | uped: items[i].uped || 0, 217 | }; 218 | Vue.set(state.mutThemesState, k, item); 219 | } 220 | }, 221 | 222 | /** 223 | * 帖子 的变化数据 记录到state 224 | */ 225 | CHANGE_THEME(state, { 226 | mid, 227 | type, 228 | val 229 | }) { 230 | let item = state.mutThemesState['k' + mid]; 231 | let update = {} 232 | switch(type) { 233 | case 'up': 234 | update.theme_up_count = item.theme_up_count + 1 235 | update.uped = true 236 | break 237 | case 'unup': 238 | update.theme_up_count = item.theme_up_count - 1 239 | update.uped = false 240 | break 241 | case 'like': 242 | update.theme_like_count = item.theme_like_count + 1 243 | update.liked = true 244 | break 245 | case 'unlike': 246 | update.theme_like_count = item.theme_like_count - 1 247 | update.liked = false 248 | break 249 | case 'reply': 250 | update.theme_commentcount = item.theme_commentcount + 1 251 | break 252 | case 'uped': 253 | update.uped = item.val; 254 | break 255 | case 'liked': 256 | update.liked = item.val; 257 | break 258 | } 259 | 260 | // Yes, Object.assign can update state and UI component at same time. 261 | item = Object.assign(item, update) 262 | }, 263 | 264 | /** 265 | * 帖子回复 的变化数据 记录到state 266 | */ 267 | ADD_THEME_REPLIES_MUT(state, { 268 | items 269 | }) { 270 | for(var i = 0; i < items.length; i++) { 271 | let k = 'k' + items[i].reply_id; 272 | //如果state里有该项数据,则忽略不覆盖 273 | if (undefined !== state.mutThemeRepliesState[k]) { 274 | continue; 275 | } 276 | 277 | let item = { 278 | reply_up: items[i].reply_up, 279 | uped: 0, 280 | }; 281 | Vue.set(state.mutThemeRepliesState, k, item); 282 | } 283 | }, 284 | 285 | /** 286 | * 帖子回复 的记录值 修改 287 | */ 288 | CHANGE_THEME_REPLY(state, { 289 | mid, 290 | type 291 | }) { 292 | let item = state.mutThemeRepliesState['k' + mid]; 293 | let update = {} 294 | switch(type) { 295 | case 'up': 296 | update.reply_up = item.reply_up + 1 297 | update.uped = true 298 | break 299 | case 'unup': 300 | update.reply_up = item.reply_up - 1 301 | update.uped = false 302 | break 303 | } 304 | 305 | // Yes, Object.assign can update state and UI component at same time. 306 | item = Object.assign(item, update) 307 | }, 308 | 309 | /** 310 | * 帖子 的变化数据 记录到state 311 | */ 312 | ADD_STORES_MUT(state, { 313 | items, cover=0 314 | }) { 315 | for(var i = 0; i < items.length; i++) { 316 | let k = 'k' + items[i].store_id; 317 | //如果state里有该项数据,则忽略不覆盖 318 | if (undefined !== state.mutStoresState[k] && !cover) { 319 | continue; 320 | } 321 | 322 | let item = { 323 | // store_like_count: items[i].store_like_count, 324 | liked: items[i].liked || 0, 325 | }; 326 | Vue.set(state.mutStoresState, k, item); 327 | } 328 | }, 329 | 330 | /** 331 | * 帖子 的变化数据 记录到state 332 | */ 333 | CHANGE_STORE(state, { 334 | mid, 335 | type 336 | }) { 337 | let item = state.mutThemesState['k' + mid]; 338 | let update = {} 339 | switch(type) { 340 | case 'like': 341 | // update.theme_like_count = item.theme_like_count + 1 342 | update.liked = true 343 | break 344 | case 'unlike': 345 | // update.theme_like_count = item.theme_like_count - 1 346 | update.liked = false 347 | break 348 | } 349 | 350 | // Yes, Object.assign can update state and UI component at same time. 351 | if (!item) { 352 | let k = 'k' + mid; 353 | Vue.set(state.mutStoresState, k, update); 354 | } else { 355 | item = Object.assign(item, update) 356 | } 357 | }, 358 | 359 | /** 360 | * 帖子 的变化数据 记录到state 361 | */ 362 | ADD_GOODS_MUT(state, { 363 | items, cover=0 364 | }) { 365 | for(var i = 0; i < items.length; i++) { 366 | let k = 'k' + items[i].goods_id; 367 | //如果state里有该项数据,则忽略不覆盖 368 | if (undefined !== state.mutGoodsState[k] && !cover) { 369 | continue; 370 | } 371 | 372 | let item = { 373 | liked: items[i].liked || 0, 374 | }; 375 | Vue.set(state.mutGoodsState, k, item); 376 | } 377 | }, 378 | 379 | /** 380 | * 帖子 的变化数据 记录到state 381 | */ 382 | CHANGE_GOODS(state, { 383 | mid, 384 | type, 385 | val 386 | }) { 387 | let item = state.mutGoodsState['k' + mid]; 388 | let update = {} 389 | switch(type) { 390 | case 'like': 391 | // update.theme_like_count = item.theme_like_count + 1 392 | update.liked = true 393 | break 394 | case 'unlike': 395 | // update.theme_like_count = item.theme_like_count - 1 396 | update.liked = false 397 | break 398 | } 399 | 400 | // Yes, Object.assign can update state and UI component at same time. 401 | if (!item) { 402 | let k = 'k' + mid; 403 | Vue.set(state.mutGoodsState, k, update); 404 | } else { 405 | item = Object.assign(item, update) 406 | } 407 | }, 408 | } -------------------------------------------------------------------------------- /src/pages/store-index.vue: -------------------------------------------------------------------------------- 1 | 105 | 158 | --------------------------------------------------------------------------------