├── .babelrc
├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .postcssrc.js
├── LICENSE
├── README.md
├── build
├── build.js
├── check-versions.js
├── logo.png
├── utils.js
├── vue-loader.conf.js
├── webpack.base.conf.js
├── webpack.dev.conf.js
└── webpack.prod.conf.js
├── config
├── dev.env.js
├── index.js
└── prod.env.js
├── demo
├── T.ts
├── class.ts
├── interface.ts
├── namespace.ts
├── test.ts
└── types.ts
├── index.html
├── package.json
├── src
├── App.vue
├── assets
│ └── mock
│ │ ├── board.js
│ │ ├── day_cutdown.js
│ │ ├── headline.js
│ │ ├── index.js
│ │ ├── shop_list.js
│ │ ├── slides.js
│ │ └── super_sale.js
├── components
│ ├── board.vue
│ ├── header.vue
│ ├── headline.vue
│ ├── modules.vue
│ ├── shops.vue
│ └── super_sale.vue
├── main.ts
├── router
│ └── index.ts
├── store
│ ├── actions.ts
│ ├── getters.ts
│ ├── index.ts
│ ├── mutations.ts
│ └── types.ts
├── typings
│ ├── ajax.d.ts
│ ├── store.d.ts
│ ├── tools.d.ts
│ └── vue-shims.d.ts
└── views
│ └── main.vue
├── static
└── .gitkeep
├── temme
└── tsconfig.json
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["env", {
4 | "modules": false
5 | }],
6 | "stage-2"
7 | ],
8 | "plugins": ["transform-runtime"]
9 | }
10 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | /build/
2 | /config/
3 | /dist/
4 | /*.js
5 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | // https://eslint.org/docs/user-guide/configuring
2 |
3 | module.exports = {
4 | root: true,
5 | parser: 'babel-eslint',
6 | parserOptions: {
7 | sourceType: 'module'
8 | },
9 | env: {
10 | browser: true,
11 | },
12 | // https://github.com/standard/standard/blob/master/docs/RULES-en.md
13 | extends: 'standard',
14 | // required to lint *.vue files
15 | plugins: [
16 | 'html'
17 | ],
18 | // add your custom rules here
19 | rules: {
20 | // allow async-await
21 | 'generator-star-spacing': 'off',
22 | // allow debugger during development
23 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | /dist/
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Editor directories and files
9 | .idea
10 | .vscode
11 | *.suo
12 | *.ntvs*
13 | *.njsproj
14 | *.sln
15 |
--------------------------------------------------------------------------------
/.postcssrc.js:
--------------------------------------------------------------------------------
1 | // https://github.com/michael-ciniawsky/postcss-load-config
2 |
3 | module.exports = {
4 | "plugins": {
5 | // to edit target browsers: use "browserslist" field in package.json
6 | "postcss-import": {},
7 | "autoprefixer": {}
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Simon Zhang
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |

4 |

5 |
6 |
7 | ## 功能
8 |
9 | - [x] 轮播
10 | - [x] 搜索
11 | - [x] 列表
12 | - [x] 懒加载
13 | - [x] 简单动画
14 | - [x] loading
15 | - [x] vue-router.ts
16 | - [x] vuex.ts
17 | - [x] vue-class-component使用
18 | - [x] vuex-class使用
19 | - [x] xxx.d.ts声明文件
20 | - [x] 基于类的编写方式
21 | - [x] mock数据
22 | - [x] tsconfig.json
23 | - [x] webpack配置
24 | - [x] vue-typescript-cli
25 |
26 | ## 完成后的简单例子
27 | 基于类的写法加上静态类型检查,简直不能再嗨
28 |
29 | ```javascript
30 |
49 | ```
50 |
51 | # 为什么使用TypeScript
52 |
53 | ### 1. JavaScript的超集
54 | 支持所有原生JavaScript的语法
55 | ### 2. 强类型语言
56 | 现在很多主流语言都是强类型的,而这点也一直是JavaScript所被人诟病的地方。使用TypeScript之后,将会在代码调试、重构等步骤节省很多时间。
57 |
58 | > 比如说:函数在返回值的时候可能经过复杂的操作,那我们如果想要知道这个值的结构就需要去仔细阅读这段代码。那如果有了TypeScript之后,直接就可以看到函数的返回值结构,将会非常的方便
59 |
60 | ### 3. 强大的IDE支持
61 | 现在的主流编辑器如`VSCode`、`WebStorm`、`Atom`、`Sublime`等都对TypeScript有着非常友好的支持,主要体现在智能提示上,非常的方便
62 | ### 4. 可运行于任何浏览器、计算机、操作系统
63 | 强大的编译引擎
64 | ### 5. 迭代更新快
65 | 不断更新,提供更加方便友好的Api
66 | ### 6. 微软和Google爸爸
67 | TypeScript是微软开发的语言,而Google的`Angular`使用的就是TypeScript,所以不用担心会停止维护,至少在近几年内TypeScript都会一门主流开发语言
68 | ### 7. npm下载量非常高
69 | 截止2017.12.17, TypeScript在全球范围内的npm日均下载量在`30w`左右,这个数字将近是vue下载量的10倍,可见TypeScript还是非常受欢迎的
70 |
71 | # Vue-TypeScript-Cli
72 | 官方虽然明确提出对TypeScript的支持,但是并没有明确的配置文档,自己在配置的时候还是需要查阅很多资料以及踩很多坑的(这个过程真的很蓝瘦-_-)
73 |
74 | 但是现在可以不用踩这个坑啦,我基于官方的`vue-cli`写了一个[vue-typescript-cli](https://github.com/SimonZhangITer/vue-typescript-template),可以一键构建TypeScript模板
75 |
76 | ### 用法
77 |
78 | ```bash
79 | vue init SimonZhangITer/vue-typescript-template
80 | ```
81 |
82 | 比如
83 |
84 | ```bash
85 | vue init SimonZhangITer/vue-typescript-template my-project
86 | ```
87 | 然后配置好的TypeScript模板就下载到`./my-project`文件夹了,`npm run dev`即可运行
88 | # TypeScript配置
89 | 这里记录一下当时的踩坑过程,所有配置已经在[vue-typescript-template](https://github.com/SimonZhangITer/vue-typescript-template)配置完毕
90 |
91 | ## 1. Webpack
92 |
93 | #### 安装ts-loader
94 | 首先需要安装`ts-loader`,这是TypeScript为Webpack提供的编译器,类似于`babel-loader`
95 |
96 | ```bash
97 | npm i ts-loader -D
98 | ```
99 |
100 | #### 配置rules
101 | 接着在Webpack的`module.rules`里面添加对ts的支持(我这里的webpack版本是2.x):
102 |
103 | ```javascript
104 | {
105 | test: /\.vue$/,
106 | loader: 'vue-loader',
107 | options: vueLoaderConfig
108 | },
109 | {
110 | test: /\.ts$/,
111 | loader: 'ts-loader',
112 | options: {
113 | appendTsSuffixTo: [/\.vue$/],
114 | }
115 | }
116 | ```
117 | #### 配置extensions
118 | 添加可识别文件后缀对ts的支持,如:
119 |
120 | ```javascript
121 | extensions: ['.js', '.vue', '.json', '.ts']
122 | ```
123 |
124 |
125 | ## 2. tsconfig.json
126 |
127 | 创建tsconfig.json文件,放在根目录下,和`package.json`同级
128 |
129 | 配置内容主要也看个人需求,具体可以去typescript的官网查看,但是有一点需要注意:
130 | > 在Vue中,你需要引入 strict: true (或者至少 noImplicitThis: true,这是 strict 模式的一部分) 以利用组件方法中 this 的类型检查,否则它会始终被看作 any 类型。
131 |
132 | 这里列出我的配置,功能在注释中给出
133 |
134 | ```javascript
135 | {
136 | "include": [
137 | "src/*",
138 | "src/**/*"
139 | ],
140 | "exclude": [
141 | "node_modules"
142 | ],
143 | "compilerOptions": {
144 | // types option has been previously configured
145 | "types": [
146 | // add node as an option
147 | "node"
148 | ],
149 | // typeRoots option has been previously configured
150 | "typeRoots": [
151 | // add path to @types
152 | "node_modules/@types"
153 | ],
154 | // 以严格模式解析
155 | "strict": true,
156 | // 在.tsx文件里支持JSX
157 | "jsx": "preserve",
158 | // 使用的JSX工厂函数
159 | "jsxFactory": "h",
160 | // 允许从没有设置默认导出的模块中默认导入
161 | "allowSyntheticDefaultImports": true,
162 | // 启用装饰器
163 | "experimentalDecorators": true,
164 | "strictFunctionTypes": false,
165 | // 允许编译javascript文件
166 | "allowJs": true,
167 | // 采用的模块系统
168 | "module": "esnext",
169 | // 编译输出目标 ES 版本
170 | "target": "es5",
171 | // 如何处理模块
172 | "moduleResolution": "node",
173 | // 在表达式和声明上有隐含的any类型时报错
174 | "noImplicitAny": true,
175 | "lib": [
176 | "dom",
177 | "es5",
178 | "es6",
179 | "es7",
180 | "es2015.promise"
181 | ],
182 | "sourceMap": true,
183 | "pretty": true
184 | }
185 | }
186 | ```
187 |
188 | ## 3. 修改main.js
189 | 1. 把项目主文件`main.js`修改成`main.ts`,里面的写法基本不变,但是有一点需要注意:
190 | 引入Vue文件的时候需要加上`.vue`后缀,否则编辑器识别不到
191 |
192 | 2. 把webpack的entry文件也修改成`main.ts`
193 |
194 | ## 4. vue-shims.d.ts
195 | TypeScript并不支持Vue文件,所以需要告诉TypeScript`*.vue`文件交给vue编辑器来处理。解决方案就是在创建一个vue-shims.d.ts文件,建议放在src目录下再创建一个`typings`文件夹,把这个声明文件放进去,如:`src/typings/vue-shims.d.ts`,文件内容:
196 |
197 | > `*.d.ts`类型文件不需要手动引入,TypeScript会自动加载
198 |
199 | ```javascript
200 | declare module '*.vue' {
201 | import Vue from 'vue'
202 | export default Vue
203 | }
204 | ```
205 |
206 | 到这里TypeScript在Vue中配置就完成了,可以愉快的撸代码了~
207 |
208 | # 第三方插件库
209 | 现在Vue官方已经明确提出支持TypeScript,并考虑出一个对应的`vue-cli`,在这之前,Vue开发团队已经开发出了一些插件库来支持TypeScript,这里简单和大家介绍一下。
210 |
211 | ### Vue-Class-Component
212 | [vue-class-component](https://github.com/vuejs/vue-class-component)是官方维护的TypeScript装饰器,写法比较扁平化。Vue对其做到完美兼容,如果你在声明组件时更喜欢基于类的 API,这个库一定不要错过
213 |
214 | ps:用了这个装饰器之后写方法不需要额外加逗号,贼嗨~~~
215 |
216 | ```javascript
217 | import Vue from "vue";
218 | import Component from "vue-class-component";
219 |
220 | @Component
221 | export default class App extends Vue {
222 | name:string = 'Simon Zhang'
223 |
224 | // computed
225 | get MyName():string {
226 | return `My name is ${this.name}`
227 | }
228 |
229 | // methods
230 | sayHello():void {
231 | alert(`Hello ${this.name}`)
232 | }
233 |
234 | mounted() {
235 | this.sayHello();
236 | }
237 | }
238 | ```
239 | 这个代码如果用原生Vue语法来写的话就是这样:
240 |
241 | ```javascript
242 | export default {
243 | data () {
244 | return {
245 | name: 'Simon Zhang'
246 | }
247 | },
248 |
249 | mounted () {
250 | this.sayHello()
251 | },
252 |
253 | computed: {
254 | MyName() {
255 | return `My name is ${this.name}`
256 | }
257 | },
258 |
259 | methods: {
260 | sayHello() {
261 | alert(`Hello ${this.name}`)
262 | },
263 | }
264 | }
265 | ```
266 |
267 | ### Vuex-Class
268 | [vuex-class](https://github.com/ktsn/vuex-class)是基于基于`vue-class-component`对Vuex提供的装饰器。它的作者同时也是`vue-class-component`的主要贡献者,质量还是有保证的。
269 |
270 | ```javascript
271 | import Vue from "vue";
272 | import Component from "vue-class-component";
273 | import { State, Action, Getter } from "vuex-class";
274 |
275 | @Component
276 | export default class App extends Vue {
277 | name:string = 'Simon Zhang'
278 | @State login: boolean;
279 | @Action initAjax: () => void;
280 | @Getter load: boolean;
281 |
282 | get isLogin(): boolean {
283 | return this.login;
284 | }
285 |
286 | mounted() {
287 | this.initAjax();
288 | }
289 | }
290 | ```
291 | 上面的代码就相当于:
292 |
293 | ```javascript
294 | export default {
295 | data() {
296 | return {
297 | name: 'Simon Zhang'
298 | }
299 | },
300 |
301 | mounted() {
302 | this.initAjax()
303 | },
304 |
305 | computed: {
306 | login() {
307 | return this.$store.state.login
308 | },
309 | load() {
310 | return this.$store.getters.load
311 | }
312 | },
313 |
314 | methods: {
315 | initAjax() {
316 | this.$store.dispatch('initAjax')
317 | }
318 | }
319 | }
320 | ```
321 |
322 | ### Vue-Property-Decorator
323 | [vue-property-decorator](https://github.com/kaorun343/vue-property-decorator) 是在 vue-class-component 上增强了更多的结合 Vue 特性的装饰器,新增了这 7 个装饰器
324 |
325 | - `@Emit`
326 | - `@Inject`
327 | - `@Model`
328 | - `@Prop`
329 | - `@Provide`
330 | - `@Watch`
331 | - `@Component` (从 vue-class-component 继承)
332 |
333 | # 坑
334 | ### 引入部分第三方库的时候需要额外声明文件
335 | 比如说我想引入`vue-lazyload`,虽然已经在本地安装,但是typescript还是提示找不到模块。原因是typescript是从`node_modules/@types`目录下去找模块声明,有些库并没有提供typescript的声明文件,所以就需要自己去添加
336 |
337 | 解决办法:在`src/typings`目前下建一个`tools.d.ts`文件,声明这个模块即可
338 |
339 | ```javascript
340 | declare module 'vue-awesome-swiper' {
341 | export const swiper: any
342 | export const swiperSlide: any
343 | }
344 |
345 | declare module 'vue-lazyload'
346 | ```
347 |
348 | ### 对vuex的支持不是很好
349 | 在TypeScript里面使用不了mapState、mapGetters等方法,只能一个变量一个变量的去引用,这个要麻烦不少。不过使用`vuex-class`库之后,写法上也还算简洁美观
350 |
351 | ```javascript
352 | export default class modules extends Vue {
353 | @State login: boolean; // 对应this.$store.state.login
354 | @State headline: StoreState.headline[]; // 对应this.$store.state.headline
355 |
356 | private swiperOption: Object = {
357 | autoplay: true,
358 | loop: true,
359 | direction: "vertical"
360 | };
361 |
362 | logoClick(): void {
363 | alert("点我干嘛");
364 | }
365 | }
366 | ```
367 |
368 | # 项目截图
369 |
374 |
375 | # 总结
376 | TypeScript还是非常值得学习和使用一个语言,还是有很多优点的
377 |
378 | 欢迎大家对我的项目提建议,欢迎Star~
379 |
380 | QQ交流群:323743292
381 | ## Build Setup
382 |
383 | ``` bash
384 | # 安装依赖
385 | npm install
386 |
387 | # 启动项目
388 | npm run dev
389 |
390 | # 打包项目
391 | npm run build
392 | ```
393 |
394 |
395 |
396 |
--------------------------------------------------------------------------------
/build/build.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | require('./check-versions')()
3 |
4 | process.env.NODE_ENV = 'production'
5 |
6 | const ora = require('ora')
7 | const rm = require('rimraf')
8 | const path = require('path')
9 | const chalk = require('chalk')
10 | const webpack = require('webpack')
11 | const config = require('../config')
12 | const webpackConfig = require('./webpack.prod.conf')
13 |
14 | const spinner = ora('building for production...')
15 | spinner.start()
16 |
17 | rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
18 | if (err) throw err
19 | webpack(webpackConfig, (err, stats) => {
20 | spinner.stop()
21 | if (err) throw err
22 | process.stdout.write(stats.toString({
23 | colors: true,
24 | modules: false,
25 | children: false,
26 | chunks: false,
27 | chunkModules: false
28 | }) + '\n\n')
29 |
30 | if (stats.hasErrors()) {
31 | console.log(chalk.red(' Build failed with errors.\n'))
32 | process.exit(1)
33 | }
34 |
35 | console.log(chalk.cyan(' Build complete.\n'))
36 | console.log(chalk.yellow(
37 | ' Tip: built files are meant to be served over an HTTP server.\n' +
38 | ' Opening index.html over file:// won\'t work.\n'
39 | ))
40 | })
41 | })
42 |
--------------------------------------------------------------------------------
/build/check-versions.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const chalk = require('chalk')
3 | const semver = require('semver')
4 | const packageConfig = require('../package.json')
5 | const shell = require('shelljs')
6 |
7 | function exec (cmd) {
8 | return require('child_process').execSync(cmd).toString().trim()
9 | }
10 |
11 | const versionRequirements = [
12 | {
13 | name: 'node',
14 | currentVersion: semver.clean(process.version),
15 | versionRequirement: packageConfig.engines.node
16 | }
17 | ]
18 |
19 | if (shell.which('npm')) {
20 | versionRequirements.push({
21 | name: 'npm',
22 | currentVersion: exec('npm --version'),
23 | versionRequirement: packageConfig.engines.npm
24 | })
25 | }
26 |
27 | module.exports = function () {
28 | const warnings = []
29 |
30 | for (let i = 0; i < versionRequirements.length; i++) {
31 | const mod = versionRequirements[i]
32 |
33 | if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
34 | warnings.push(mod.name + ': ' +
35 | chalk.red(mod.currentVersion) + ' should be ' +
36 | chalk.green(mod.versionRequirement)
37 | )
38 | }
39 | }
40 |
41 | if (warnings.length) {
42 | console.log('')
43 | console.log(chalk.yellow('To use this template, you must update following to modules:'))
44 | console.log()
45 |
46 | for (let i = 0; i < warnings.length; i++) {
47 | const warning = warnings[i]
48 | console.log(' ' + warning)
49 | }
50 |
51 | console.log()
52 | process.exit(1)
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/build/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SimonZhangITer/vue-typescript-dpapp-demo/33945274ddb51214e8467dc00b7e29d98dd87aaf/build/logo.png
--------------------------------------------------------------------------------
/build/utils.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const path = require('path')
3 | const config = require('../config')
4 | const ExtractTextPlugin = require('extract-text-webpack-plugin')
5 | const packageConfig = require('../package.json')
6 |
7 | exports.assetsPath = function (_path) {
8 | const assetsSubDirectory = process.env.NODE_ENV === 'production'
9 | ? config.build.assetsSubDirectory
10 | : config.dev.assetsSubDirectory
11 |
12 | return path.posix.join(assetsSubDirectory, _path)
13 | }
14 |
15 | exports.cssLoaders = function (options) {
16 | options = options || {}
17 |
18 | const cssLoader = {
19 | loader: 'css-loader',
20 | options: {
21 | sourceMap: options.sourceMap
22 | }
23 | }
24 |
25 | const postcssLoader = {
26 | loader: 'postcss-loader',
27 | options: {
28 | sourceMap: options.sourceMap
29 | }
30 | }
31 |
32 | // generate loader string to be used with extract text plugin
33 | function generateLoaders (loader, loaderOptions) {
34 | const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]
35 |
36 | if (loader) {
37 | loaders.push({
38 | loader: loader + '-loader',
39 | options: Object.assign({}, loaderOptions, {
40 | sourceMap: options.sourceMap
41 | })
42 | })
43 | }
44 |
45 | // Extract CSS when that option is specified
46 | // (which is the case during production build)
47 | if (options.extract) {
48 | return ExtractTextPlugin.extract({
49 | use: loaders,
50 | fallback: 'vue-style-loader'
51 | })
52 | } else {
53 | return ['vue-style-loader'].concat(loaders)
54 | }
55 | }
56 |
57 | // https://vue-loader.vuejs.org/en/configurations/extract-css.html
58 | return {
59 | css: generateLoaders(),
60 | postcss: generateLoaders(),
61 | less: generateLoaders('less'),
62 | sass: generateLoaders('sass', { indentedSyntax: true }),
63 | scss: generateLoaders('sass'),
64 | stylus: generateLoaders('stylus'),
65 | styl: generateLoaders('stylus')
66 | }
67 | }
68 |
69 | // Generate loaders for standalone style files (outside of .vue)
70 | exports.styleLoaders = function (options) {
71 | const output = []
72 | const loaders = exports.cssLoaders(options)
73 |
74 | for (const extension in loaders) {
75 | const loader = loaders[extension]
76 | output.push({
77 | test: new RegExp('\\.' + extension + '$'),
78 | use: loader
79 | })
80 | }
81 |
82 | return output
83 | }
84 |
85 | exports.createNotifierCallback = () => {
86 | const notifier = require('node-notifier')
87 |
88 | return (severity, errors) => {
89 | if (severity !== 'error') return
90 |
91 | const error = errors[0]
92 | const filename = error.file && error.file.split('!').pop()
93 |
94 | notifier.notify({
95 | title: packageConfig.name,
96 | message: severity + ': ' + error.name,
97 | subtitle: filename || '',
98 | icon: path.join(__dirname, 'logo.png')
99 | })
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/build/vue-loader.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const utils = require('./utils')
3 | const config = require('../config')
4 | const isProduction = process.env.NODE_ENV === 'production'
5 | const sourceMapEnabled = isProduction
6 | ? config.build.productionSourceMap
7 | : config.dev.cssSourceMap
8 |
9 | module.exports = {
10 | loaders: utils.cssLoaders({
11 | sourceMap: sourceMapEnabled,
12 | extract: isProduction
13 | }),
14 | cssSourceMap: sourceMapEnabled,
15 | cacheBusting: config.dev.cacheBusting,
16 | transformToRequire: {
17 | video: ['src', 'poster'],
18 | source: 'src',
19 | img: 'src',
20 | image: 'xlink:href'
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/build/webpack.base.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const path = require('path')
3 | const utils = require('./utils')
4 | const config = require('../config')
5 | const vueLoaderConfig = require('./vue-loader.conf')
6 |
7 | function resolve(dir) {
8 | return path.join(__dirname, '..', dir)
9 | }
10 |
11 | const createLintingRule = () => ({
12 | test: /\.(js|vue|ts|tsx)$/,
13 | loader: 'eslint-loader',
14 | enforce: 'pre',
15 | include: [resolve('src'), resolve('test')],
16 | options: {
17 | formatter: require('eslint-friendly-formatter'),
18 | emitWarning: !config.dev.showEslintErrorsInOverlay
19 | }
20 | })
21 |
22 | module.exports = {
23 | context: path.resolve(__dirname, '../'),
24 | entry: {
25 | app: './src/main.ts'
26 | },
27 | output: {
28 | path: config.build.assetsRoot,
29 | filename: '[name].js',
30 | publicPath: process.env.NODE_ENV === 'production'
31 | ? config.build.assetsPublicPath
32 | : config.dev.assetsPublicPath
33 | },
34 | resolve: {
35 | extensions: ['.js', '.vue', '.json', '.ts'],
36 | alias: {
37 | 'vue$': 'vue/dist/vue.esm.js',
38 | '@': resolve('src'),
39 | }
40 | },
41 | module: {
42 | rules: [
43 | // ...(config.dev.useEslint ? [createLintingRule()] : []),
44 | {
45 | test: /\.vue$/,
46 | loader: 'vue-loader',
47 | options: vueLoaderConfig
48 | },
49 | {
50 | test: /\.ts$/,
51 | loader: 'ts-loader',
52 | options: {
53 | appendTsSuffixTo: [/\.vue$/],
54 | }
55 | },
56 | {
57 | test: /\.js$/,
58 | loader: 'babel-loader',
59 | include: [resolve('src'), resolve('test')]
60 | },
61 | {
62 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
63 | loader: 'url-loader',
64 | options: {
65 | limit: 10000,
66 | name: utils.assetsPath('img/[name].[hash:7].[ext]')
67 | }
68 | },
69 | {
70 | test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
71 | loader: 'url-loader',
72 | options: {
73 | limit: 10000,
74 | name: utils.assetsPath('media/[name].[hash:7].[ext]')
75 | }
76 | },
77 | {
78 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
79 | loader: 'url-loader',
80 | options: {
81 | limit: 10000,
82 | name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
83 | }
84 | }
85 | ]
86 | },
87 | node: {
88 | // prevent webpack from injecting useless setImmediate polyfill because Vue
89 | // source contains it (although only uses it if it's native).
90 | setImmediate: false,
91 | // prevent webpack from injecting mocks to Node native modules
92 | // that does not make sense for the client
93 | dgram: 'empty',
94 | fs: 'empty',
95 | net: 'empty',
96 | tls: 'empty',
97 | child_process: 'empty'
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/build/webpack.dev.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const utils = require('./utils')
3 | const webpack = require('webpack')
4 | const config = require('../config')
5 | const merge = require('webpack-merge')
6 | const baseWebpackConfig = require('./webpack.base.conf')
7 | const HtmlWebpackPlugin = require('html-webpack-plugin')
8 | const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
9 | const portfinder = require('portfinder')
10 |
11 | const HOST = process.env.HOST
12 | const PORT = process.env.PORT && Number(process.env.PORT)
13 |
14 | const devWebpackConfig = merge(baseWebpackConfig, {
15 | module: {
16 | rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
17 | },
18 | // cheap-module-eval-source-map is faster for development
19 | devtool: config.dev.devtool,
20 |
21 | // these devServer options should be customized in /config/index.js
22 | devServer: {
23 | clientLogLevel: 'warning',
24 | historyApiFallback: true,
25 | hot: true,
26 | compress: true,
27 | host: HOST || config.dev.host,
28 | port: PORT || config.dev.port,
29 | open: config.dev.autoOpenBrowser,
30 | overlay: config.dev.errorOverlay
31 | ? { warnings: false, errors: true }
32 | : false,
33 | publicPath: config.dev.assetsPublicPath,
34 | proxy: config.dev.proxyTable,
35 | quiet: true, // necessary for FriendlyErrorsPlugin
36 | watchOptions: {
37 | poll: config.dev.poll,
38 | }
39 | },
40 | plugins: [
41 | new webpack.DefinePlugin({
42 | 'process.env': require('../config/dev.env')
43 | }),
44 | new webpack.HotModuleReplacementPlugin(),
45 | new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.
46 | new webpack.NoEmitOnErrorsPlugin(),
47 | // https://github.com/ampedandwired/html-webpack-plugin
48 | new HtmlWebpackPlugin({
49 | filename: 'index.html',
50 | template: 'index.html',
51 | inject: true
52 | }),
53 | ]
54 | })
55 |
56 | module.exports = new Promise((resolve, reject) => {
57 | portfinder.basePort = process.env.PORT || config.dev.port
58 | portfinder.getPort((err, port) => {
59 | if (err) {
60 | reject(err)
61 | } else {
62 | // publish the new Port, necessary for e2e tests
63 | process.env.PORT = port
64 | // add port to devServer config
65 | devWebpackConfig.devServer.port = port
66 |
67 | // Add FriendlyErrorsPlugin
68 | devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
69 | compilationSuccessInfo: {
70 | messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
71 | },
72 | onErrors: config.dev.notifyOnErrors
73 | ? utils.createNotifierCallback()
74 | : undefined
75 | }))
76 |
77 | resolve(devWebpackConfig)
78 | }
79 | })
80 | })
81 |
--------------------------------------------------------------------------------
/build/webpack.prod.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const path = require('path')
3 | const utils = require('./utils')
4 | const webpack = require('webpack')
5 | const config = require('../config')
6 | const merge = require('webpack-merge')
7 | const baseWebpackConfig = require('./webpack.base.conf')
8 | const CopyWebpackPlugin = require('copy-webpack-plugin')
9 | const HtmlWebpackPlugin = require('html-webpack-plugin')
10 | const ExtractTextPlugin = require('extract-text-webpack-plugin')
11 | const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
12 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
13 |
14 | const env = require('../config/prod.env')
15 |
16 | const webpackConfig = merge(baseWebpackConfig, {
17 | module: {
18 | rules: utils.styleLoaders({
19 | sourceMap: config.build.productionSourceMap,
20 | extract: true,
21 | usePostCSS: true
22 | })
23 | },
24 | devtool: config.build.productionSourceMap ? config.build.devtool : false,
25 | output: {
26 | path: config.build.assetsRoot,
27 | filename: utils.assetsPath('js/[name].[chunkhash].js'),
28 | chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
29 | },
30 | plugins: [
31 | // http://vuejs.github.io/vue-loader/en/workflow/production.html
32 | new webpack.DefinePlugin({
33 | 'process.env': env
34 | }),
35 | new UglifyJsPlugin({
36 | uglifyOptions: {
37 | compress: {
38 | warnings: false
39 | }
40 | },
41 | sourceMap: config.build.productionSourceMap,
42 | parallel: true
43 | }),
44 | // extract css into its own file
45 | new ExtractTextPlugin({
46 | filename: utils.assetsPath('css/[name].[contenthash].css'),
47 | // set the following option to `true` if you want to extract CSS from
48 | // codesplit chunks into this main css file as well.
49 | // This will result in *all* of your app's CSS being loaded upfront.
50 | allChunks: false,
51 | }),
52 | // Compress extracted CSS. We are using this plugin so that possible
53 | // duplicated CSS from different components can be deduped.
54 | new OptimizeCSSPlugin({
55 | cssProcessorOptions: config.build.productionSourceMap
56 | ? { safe: true, map: { inline: false } }
57 | : { safe: true }
58 | }),
59 | // generate dist index.html with correct asset hash for caching.
60 | // you can customize output by editing /index.html
61 | // see https://github.com/ampedandwired/html-webpack-plugin
62 | new HtmlWebpackPlugin({
63 | filename: config.build.index,
64 | template: 'index.html',
65 | inject: true,
66 | minify: {
67 | removeComments: true,
68 | collapseWhitespace: true,
69 | removeAttributeQuotes: true
70 | // more options:
71 | // https://github.com/kangax/html-minifier#options-quick-reference
72 | },
73 | // necessary to consistently work with multiple chunks via CommonsChunkPlugin
74 | chunksSortMode: 'dependency'
75 | }),
76 | // keep module.id stable when vender modules does not change
77 | new webpack.HashedModuleIdsPlugin(),
78 | // enable scope hoisting
79 | new webpack.optimize.ModuleConcatenationPlugin(),
80 | // split vendor js into its own file
81 | new webpack.optimize.CommonsChunkPlugin({
82 | name: 'vendor',
83 | minChunks (module) {
84 | // any required modules inside node_modules are extracted to vendor
85 | return (
86 | module.resource &&
87 | /\.js$/.test(module.resource) &&
88 | module.resource.indexOf(
89 | path.join(__dirname, '../node_modules')
90 | ) === 0
91 | )
92 | }
93 | }),
94 | // extract webpack runtime and module manifest to its own file in order to
95 | // prevent vendor hash from being updated whenever app bundle is updated
96 | new webpack.optimize.CommonsChunkPlugin({
97 | name: 'manifest',
98 | minChunks: Infinity
99 | }),
100 | // This instance extracts shared chunks from code splitted chunks and bundles them
101 | // in a separate chunk, similar to the vendor chunk
102 | // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
103 | new webpack.optimize.CommonsChunkPlugin({
104 | name: 'app',
105 | async: 'vendor-async',
106 | children: true,
107 | minChunks: 3
108 | }),
109 |
110 | // copy custom static assets
111 | new CopyWebpackPlugin([
112 | {
113 | from: path.resolve(__dirname, '../static'),
114 | to: config.build.assetsSubDirectory,
115 | ignore: ['.*']
116 | }
117 | ])
118 | ]
119 | })
120 |
121 | if (config.build.productionGzip) {
122 | const CompressionWebpackPlugin = require('compression-webpack-plugin')
123 |
124 | webpackConfig.plugins.push(
125 | new CompressionWebpackPlugin({
126 | asset: '[path].gz[query]',
127 | algorithm: 'gzip',
128 | test: new RegExp(
129 | '\\.(' +
130 | config.build.productionGzipExtensions.join('|') +
131 | ')$'
132 | ),
133 | threshold: 10240,
134 | minRatio: 0.8
135 | })
136 | )
137 | }
138 |
139 | if (config.build.bundleAnalyzerReport) {
140 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
141 | webpackConfig.plugins.push(new BundleAnalyzerPlugin())
142 | }
143 |
144 | module.exports = webpackConfig
145 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/config/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | // Template version: 1.2.5
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: 'localhost', // can be overwritten by process.env.HOST
17 | port: 8080, // 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: '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 | // CSS Sourcemaps off by default because relative paths are "buggy"
44 | // with this option, according to the CSS-Loader README
45 | // (https://github.com/webpack/css-loader#sourcemaps)
46 | // In our experience, they generally work as expected,
47 | // just be aware of this issue when enabling this option.
48 | cssSourceMap: false,
49 | },
50 |
51 | build: {
52 | // Template for index.html
53 | index: path.resolve(__dirname, '../dist/index.html'),
54 |
55 | // Paths
56 | assetsRoot: path.resolve(__dirname, '../dist'),
57 | assetsSubDirectory: 'static',
58 | assetsPublicPath: '/',
59 |
60 | /**
61 | * Source Maps
62 | */
63 |
64 | productionSourceMap: true,
65 | // https://webpack.js.org/configuration/devtool/#production
66 | devtool: '#source-map',
67 |
68 | // Gzip off by default as many popular static hosts such as
69 | // Surge or Netlify already gzip all static assets for you.
70 | // Before setting to `true`, make sure to:
71 | // npm install --save-dev compression-webpack-plugin
72 | productionGzip: false,
73 | productionGzipExtensions: ['js', 'css'],
74 |
75 | // Run the build command with an extra argument to
76 | // View the bundle analyzer report after build finishes:
77 | // `npm run build --report`
78 | // Set to `true` or `false` to always turn it on or off
79 | bundleAnalyzerReport: process.env.npm_config_report
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/config/prod.env.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | module.exports = {
3 | NODE_ENV: '"production"'
4 | }
5 |
--------------------------------------------------------------------------------
/demo/T.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 泛型
3 | *
4 | */
5 |
6 | function identityString(arg: number): number {
7 | return arg;
8 | }
9 |
10 | function identityNumber(arg: string): string {
11 | return arg;
12 | }
13 |
14 | function identity(arg: T): T {
15 | return arg;
16 | }
17 |
--------------------------------------------------------------------------------
/demo/class.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Class
3 | *
4 | * public
5 | * 公有的,属性对外可见,默认public
6 | */
7 |
8 | class Animal {
9 | public name: string
10 |
11 | constructor(name: string) {
12 | this.name = name
13 | }
14 |
15 | move(distance: number) {
16 | console.log(`Animal moved ${distance}m`);
17 | }
18 | }
19 |
20 | let animal: Animal = new Animal('zhangsan')
21 |
22 | /**
23 | * extends 继承
24 | *
25 | * 类从基类中继承属性和方法
26 | */
27 | class Dog extends Animal {
28 | type: string
29 |
30 | constructor(name: string, type: string) {
31 | super(name)
32 | this.type = type
33 | }
34 |
35 | bark() {
36 | console.log('Woof! Woof')
37 | }
38 | }
39 |
40 | let dog: Dog = new Dog('jack', 'type')
41 |
42 |
43 | /**
44 | * private
45 | * 私有的,只有自己可见
46 | */
47 | class Animal2 {
48 | private name: string;
49 | constructor(theName: string) {
50 | this.name = theName;
51 | }
52 | }
53 |
54 | let cat: Animal2 = new Animal2("Cat");
55 |
56 | // cat.name// 错误: 'name' 是私有的.
57 |
58 |
59 | /**
60 | * protected
61 | * 受保护的,在派生类中可见
62 | */
63 |
64 | class Person2 {
65 | protected bloodType: string = 'A';
66 | name: string
67 | constructor(name: string) {
68 | this.name = name;
69 | }
70 | }
71 |
72 | class child extends Person2 {
73 | constructor(name: string) {
74 | super(name)
75 | }
76 |
77 | getBloodType(): string {
78 | return this.bloodType
79 | }
80 | }
81 |
82 | let person: Person2 = new Person2('zhangsan')
83 | let howard = new child('Howard')
84 |
85 | // person.bloodType // error 属性“bloodType”受保护,只能在类“Person”及其子类中访问
86 | howard.getBloodType() // A
87 |
88 |
89 | /**
90 | * readonly
91 | * 将属性设置为只读的
92 | * 只读属性必须在声明时或构造函数里被初始化
93 | */
94 |
95 | class Octopus {
96 | readonly name: string;
97 | readonly numberOfLegs: number = 8;
98 | constructor(theName: string) {
99 | this.name = theName;
100 | }
101 | }
102 | let dad = new Octopus("Man with the 8 strong legs");
103 | // dad.name = "Man with the 3-piece suit"; // 错误! name 是只读的.
104 |
--------------------------------------------------------------------------------
/demo/interface.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Interface
3 | *
4 | * TypeScript的核心原则之一是对值所具有的结构进行类型检查。
5 | * 它有时被称做“鸭式辨型法”或“结构性子类型化”。
6 | * 在TypeScript里,接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约。
7 | */
8 |
9 | interface Person {
10 | name: string,
11 | age: number,
12 | isMale: boolean,
13 | birthDay?: Date, // 问号表示可有可无
14 | addition?: {
15 | rich: boolean,
16 | married: boolean
17 | }
18 | }
19 |
20 | let zhangsan: Person = {
21 | name: 'zhangsan',
22 | age: 18,
23 | isMale: true,
24 | birthDay: new Date('2017-10-19'),
25 | }
26 |
27 | // zhangsan.birthDay = '1990-10-19'
28 |
29 | function getMyName(person: Person): string {
30 | return `i am ${person.name}`
31 | }
32 |
33 | getMyName(zhangsan)
34 |
--------------------------------------------------------------------------------
/demo/namespace.ts:
--------------------------------------------------------------------------------
1 | namespace Util {
2 | export class lazy { }
3 |
4 | export class trim { }
5 |
6 | export class getParam { }
7 | }
8 |
9 | // export class lazy { }
10 |
11 | // export class trim { }
12 |
13 | // export class getParam { }
14 |
15 |
16 | // import * as Util from './namespace'
17 |
--------------------------------------------------------------------------------
/demo/test.ts:
--------------------------------------------------------------------------------
1 | // import * as Util from './namespace'
2 |
3 | // Util.getParam
4 |
5 | ///
6 |
7 | namespace Util {
8 | interface Test {}
9 | }
10 |
--------------------------------------------------------------------------------
/demo/types.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * string
3 | * 字符串
4 | */
5 | let str: string = 'haha'
6 |
7 | str = 'xixi'
8 |
9 | /**
10 | * number
11 | * 数字
12 | */
13 | let num: number = 1
14 |
15 | num = 2
16 |
17 | /**
18 | * boolean
19 | * 布尔
20 | */
21 | let bool: boolean = true
22 |
23 | bool = false
24 |
25 |
26 | /**
27 | * 类型推断
28 | */
29 | let x = 'test'
30 |
31 | // x = 1
32 |
33 | /**
34 | * array
35 | * 数组
36 | */
37 | let arr1: number[] = [2, 3]
38 |
39 | let arr2: Array = [2, 3]
40 |
41 | /**
42 | * tuple
43 | * 元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同
44 | */
45 | let tup1: [number, string] = [2, '2']
46 |
47 | let tup2: [number] = [1, 2]
48 |
49 | // tup1.push(false)
50 |
51 | /**
52 | * 枚举
53 | * enum类型是对JavaScript标准数据类型的一个补充
54 | * 像C#等其它语言一样,使用枚举类型可以为一组数值赋予友好的名字。
55 | *
56 | * 默认情况下,从0开始为元素编号
57 | */
58 | enum Type {
59 | Shop = 2,
60 | Food = 1,
61 | Drink = 3
62 | }
63 |
64 | let shop: Type = Type.Shop
65 |
66 |
67 | /**
68 | * any
69 | * 可存储任何类型
70 | */
71 |
72 | let any: any = 3
73 |
74 | any = 'haha'
75 | any = true
76 |
77 | /**
78 | * void
79 | * 没有类型,多用于函数
80 | */
81 | function warnUser(): void {
82 | alert("This is my warning message")
83 | }
84 |
85 | function getName(name: string): string {
86 | return `my name is ${name}`
87 | }
88 |
89 | getName('haha')
90 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | vue-typecript-dpapp-demo
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-typecript-dpapp-demo",
3 | "version": "1.0.0",
4 | "description": "a typeScript vue.js demo",
5 | "author": "SimonZhang ",
6 | "private": true,
7 | "scripts": {
8 | "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
9 | "start": "npm run dev",
10 | "lint": "eslint --ext .js,.vue src",
11 | "build": "node build/build.js"
12 | },
13 | "dependencies": {
14 | "vue": "^2.5.9",
15 | "vue-awesome-swiper": "^3.0.6",
16 | "vue-router": "^3.0.1",
17 | "vuex": "^3.0.1",
18 | "vuex-class": "^0.3.0"
19 | },
20 | "devDependencies": {
21 | "@types/node": "^8.0.58",
22 | "autoprefixer": "^7.1.2",
23 | "axios": "^0.17.1",
24 | "babel-core": "^6.22.1",
25 | "babel-eslint": "^7.1.1",
26 | "babel-loader": "^7.1.1",
27 | "babel-plugin-transform-runtime": "^6.22.0",
28 | "babel-preset-env": "^1.3.2",
29 | "babel-preset-stage-2": "^6.22.0",
30 | "chalk": "^2.0.1",
31 | "copy-webpack-plugin": "^4.0.1",
32 | "css-loader": "^0.28.7",
33 | "eslint": "^3.19.0",
34 | "eslint-config-standard": "^10.2.1",
35 | "eslint-friendly-formatter": "^3.0.0",
36 | "eslint-loader": "^1.7.1",
37 | "eslint-plugin-html": "^3.0.0",
38 | "eslint-plugin-import": "^2.7.0",
39 | "eslint-plugin-node": "^5.2.0",
40 | "eslint-plugin-promise": "^3.4.0",
41 | "eslint-plugin-standard": "^3.0.1",
42 | "eventsource-polyfill": "^0.9.6",
43 | "extract-text-webpack-plugin": "^3.0.0",
44 | "file-loader": "^1.1.4",
45 | "friendly-errors-webpack-plugin": "^1.6.1",
46 | "html-webpack-plugin": "^2.30.1",
47 | "mockjs": "^1.0.1-beta3",
48 | "node-notifier": "^5.1.2",
49 | "optimize-css-assets-webpack-plugin": "^3.2.0",
50 | "ora": "^1.2.0",
51 | "portfinder": "^1.0.13",
52 | "postcss-import": "^11.0.0",
53 | "postcss-loader": "^2.0.8",
54 | "pug": "^2.0.0-rc.4",
55 | "rimraf": "^2.6.0",
56 | "sass": "^1.0.0-beta.4",
57 | "semver": "^5.3.0",
58 | "shelljs": "^0.7.6",
59 | "stylus": "^0.54.5",
60 | "stylus-loader": "^3.0.1",
61 | "ts-loader": "^3.2.0",
62 | "typescript": "^2.6.2",
63 | "uglifyjs-webpack-plugin": "^1.1.1",
64 | "url-loader": "^0.5.8",
65 | "vue-class-component": "^6.1.0",
66 | "vue-lazyload": "^1.1.4",
67 | "vue-loader": "^13.3.0",
68 | "vue-style-loader": "^3.0.3",
69 | "vue-template-compiler": "^2.5.2",
70 | "webpack": "^3.6.0",
71 | "webpack-bundle-analyzer": "^2.9.0",
72 | "webpack-dev-server": "^2.9.1",
73 | "webpack-merge": "^4.1.0"
74 | },
75 | "engines": {
76 | "node": ">= 4.0.0",
77 | "npm": ">= 3.0.0"
78 | },
79 | "browserslist": [
80 | "> 1%",
81 | "last 2 versions",
82 | "not ie <= 8"
83 | ]
84 | }
85 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
16 |
17 |
28 |
--------------------------------------------------------------------------------
/src/assets/mock/board.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'list': [
3 | [{
4 | title: '1212红包',
5 | desc: '0门槛限时抢',
6 | imgUrl: '//op.meituan.net/oppkit_pic/20160310031829-c118226a-1/9693db0d230637f008dc7ad5fe4c4a50.png'
7 | }, {
8 | title: '吃全球美食',
9 | desc: '领50元红包',
10 | imgUrl: '//op.meituan.net/oppkit_pic/20160310031829-c118226a-1/762773b66e7bf87228febb5c19a270c4.png'
11 | }],
12 | [{
13 | title: '天降大红包',
14 | desc: '火速领取',
15 | imgUrl: '//op.meituan.net/oppkit_pic/20160310032241-1e027deb-2/73d631dc6390bc48d7faff969a2fd3e1.png'
16 | }, {
17 | title: '1212红包',
18 | desc: '0门槛限时抢',
19 | imgUrl: '//op.meituan.net/oppkit_pic/20160310031829-c118226a-1/9693db0d230637f008dc7ad5fe4c4a50.png'
20 | }]
21 | ]
22 | }
23 |
--------------------------------------------------------------------------------
/src/assets/mock/day_cutdown.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'list': [
3 | {
4 | 'url': '//m.dianping.com/tuan/deal/20013185?from=m_reduce',
5 | 'imgUrl': '//p1.meituan.net/deal/2c12dccbf82e1ea051317a79a9de434d17813.jpg%40120w_90h_1e_1c_1l%7Cwatermark%3D1%26%26r%3D1%26p%3D9%26x%3D20%26y%3D20',
6 | 'title': 'AimiCake艾米蛋糕',
7 | 'price_current': '198'
8 | },
9 | {
10 | 'url': '//m.dianping.com/tuan/deal/19605977?from=m_reduce',
11 | 'imgUrl': '//p1.meituan.net/deal/33714adbb8abc509b72003a66cc5d5c634781.jpg%40120w_90h_1e_1c_1l%7Cwatermark%3D1%26%26r%3D1%26p%3D9%26x%3D20%26y%3D20',
12 | 'title': '龙华素斋',
13 | 'price_current': '97',
14 | 'price_down': '立减2元'
15 | },
16 | {
17 | 'url': '//m.dianping.com/tuan/deal/20800516?from=m_reduce',
18 | 'imgUrl': '//p0.meituan.net/deal/ce50f15b3207185ab1cda459046999d459731.jpg%40120w_90h_1e_1c_1l%7Cwatermark%3D1%26%26r%3D1%26p%3D9%26x%3D20%26y%3D20',
19 | 'title': '星期五翻糖蛋糕',
20 | 'price_current': '298',
21 | 'price_down': '立减20元'
22 | }
23 | ]
24 | }
25 |
--------------------------------------------------------------------------------
/src/assets/mock/headline.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'list': [
3 | {
4 | 'url': '//h5.dianping.com/app/h5-ranklist-static/list_nearby.html?headlineId=3700104&source=weixinM&from=headline',
5 | 'title': '绚景楼藏惊喜:有公主陪伴的圣诞午餐会有多神奇',
6 | 'imgUrl': 'https://img.meituan.net/dpgroup/a2c05c7fdc804cf414854b3a5ba2fd4e231267.jpg%40100w_100h_1e_1c_1l%7Cwatermark%3D1%26%26r%3D1%26p%3D9%26x%3D20%26y%3D20',
7 | 'imgNum': '1'
8 | },
9 | {
10 | 'url': '//h5.dianping.com/app/h5-ranklist-static/list_nearby.html?headlineId=3278443&source=weixinM&from=headline',
11 | 'title': '老娘舅17年来第一封致歉信',
12 | 'imgUrl': 'https://img.meituan.net/dpgroup/7b571b075b7f42e55c061615e9f65b57633847.jpg%40100w_100h_1e_1c_1l%7Cwatermark%3D1%26%26r%3D1%26p%3D9%26x%3D20%26y%3D20',
13 | 'imgNum': '17'
14 | },
15 | {
16 | 'url': '//h5.dianping.com/app/h5-ranklist-static/list_nearby.html?headlineId=3369501&source=weixinM&from=headline',
17 | 'title': '揭秘!老娘舅好吃的米饭是这样做成的 | 至纯源味',
18 | 'imgUrl': 'https://img.meituan.net/dpgroup/7b571b075b7f42e55c061615e9f65b57633847.jpg%40100w_100h_1e_1c_1l%7Cwatermark%3D1%26%26r%3D1%26p%3D9%26x%3D20%26y%3D20',
19 | 'imgNum': '35'
20 | },
21 | {
22 | 'url': '//h5.dianping.com/app/h5-ranklist-static/list_nearby.html?headlineId=3678071&source=weixinM&from=headline',
23 | 'title': '假如维密天使们也做美甲,画风应该是这样的…',
24 | 'imgUrl': 'https://p0.meituan.net/gpa/d4322e5f9e8b621dd437079cb548f1a093719.jpg%40100w_100h_1e_1c_1l%7Cwatermark%3D1%26%26r%3D1%26p%3D9%26x%3D20%26y%3D20',
25 | 'imgNum': '77'
26 | },
27 | {
28 | 'url': '//h5.dianping.com/app/h5-ranklist-static/list_nearby.html?headlineId=3706721&source=weixinM&from=headline',
29 | 'title': '临近圣诞,怎么能少了一款圣诞美甲?',
30 | 'imgUrl': 'https://p1.meituan.net/poicontent/1d9c7614c2d01795ea271bac1a3e78d864323.jpg%40100w_100h_1e_1c_1l%7Cwatermark%3D1%26%26r%3D1%26p%3D9%26x%3D20%26y%3D20',
31 | 'imgNum': '29'
32 | }
33 | ]
34 | }
35 |
--------------------------------------------------------------------------------
/src/assets/mock/index.js:
--------------------------------------------------------------------------------
1 | import Mock from 'mockjs'
2 | import modules from './slides'
3 | import headline from './headline'
4 | import board from './board'
5 | import supersale from './super_sale'
6 | import shops from './shop_list'
7 |
8 | // modules
9 | Mock
10 | .mock('/modules', 'get', () => {
11 | return {
12 | code: 200,
13 | result: modules
14 | }
15 | })
16 |
17 | // headline
18 | .mock('/headline', 'get', () => {
19 | return {
20 | code: 200,
21 | result: headline
22 | }
23 | })
24 |
25 | // activity
26 | .mock('/activity', 'get', () => {
27 | return {
28 | code: 200,
29 | result: board
30 | }
31 | })
32 |
33 | // super_sale
34 | .mock('/supersale', 'get', () => {
35 | return {
36 | code: 200,
37 | result: supersale
38 | }
39 | })
40 |
41 | // shops
42 | .mock('/shops', 'get', () => {
43 | return {
44 | code: 200,
45 | result: shops
46 | }
47 | })
48 |
--------------------------------------------------------------------------------
/src/assets/mock/shop_list.js:
--------------------------------------------------------------------------------
1 |
2 | export default {
3 | 'list': [
4 | {
5 | 'url': '//m.dianping.com/tuan/deal/18954707?from=m_reculike&rec_query_id=187b73b1-e193-45ec-8e02-1e75cfedb06a',
6 | 'imgUrl': '//p1.meituan.net/deal/e09fd65dee8728d69daa2a0841cdbb4e28902.jpg%40180w_180h_1e_1c_1l_80q%7Cwatermark%3D0',
7 | 'shopName': '音乐之声大酒店',
8 | 'distance': '286m',
9 | 'desc': '[中山公园] 20元代金券1张,可叠加',
10 | 'price_current': '1',
11 | 'price_old': '20',
12 | 'sale_count': '已售6236'
13 | },
14 | {
15 | 'url': '//m.dianping.com/tuan/deal/24207934?from=m_reculike&rec_query_id=187b73b1-e193-45ec-8e02-1e75cfedb06a',
16 | 'imgUrl': '//p0.meituan.net/deal/f4a3f28a951305341aabca03cb86dba0312270.jpeg%40180w_180h_1e_1c_1l_80q%7Cwatermark%3D0',
17 | 'shopName': '二人转东北菜',
18 | 'distance': '591m',
19 | 'desc': '[中山公园] 单人超值套餐,提供免费WiFi',
20 | 'price_current': '12.9',
21 | 'price_old': '29',
22 | 'sale_count': '已售455'
23 | },
24 | {
25 | 'url': '//m.dianping.com/tuan/deal/21955594?from=m_reculike&rec_query_id=187b73b1-e193-45ec-8e02-1e75cfedb06a',
26 | 'imgUrl': '//p1.meituan.net/deal/757052f29bca1bc1fc078debe772c23483229.jpg%40180w_180h_1e_1c_1l_80q%7Cwatermark%3D0',
27 | 'shopName': '谷斯汀老香港手工蛋糕',
28 | 'distance': '226m',
29 | 'desc': '[中山公园] 招牌原味蛋糕1个,约430克,长方形',
30 | 'price_current': '15',
31 | 'price_old': '25',
32 | 'sale_count': '已售1243'
33 | },
34 | {
35 | 'url': '//m.dianping.com/tuan/deal/17612261?from=m_reculike&rec_query_id=187b73b1-e193-45ec-8e02-1e75cfedb06a',
36 | 'imgUrl': '//p0.meituan.net/deal/3fcd2c658be1e6560154769bc69127f63562164.jpg%40180w_180h_1e_1c_1l_80q%7Cwatermark%3D0',
37 | 'shopName': '正新鸡排',
38 | 'distance': '422m',
39 | 'desc': '[218店通用] 【官方】正新鸡排单人餐',
40 | 'price_current': '10.9',
41 | 'price_cutdown': '新客减9.5',
42 | 'sale_count': '已售197971'
43 | },
44 | {
45 | 'url': '//m.dianping.com/tuan/deal/28056644?from=m_reculike&rec_query_id=187b73b1-e193-45ec-8e02-1e75cfedb06a',
46 | 'imgUrl': '//p0.meituan.net/deal/cbb2af6d46cd989915a49bc9a5c157d3355447.jpg%40180w_180h_1e_1c_1l_80q%7Cwatermark%3D0',
47 | 'shopName': '爱茜茜里意大利健康冰淇淋',
48 | 'distance': '539m',
49 | 'desc': '[龙之梦购物中心等53店] 意大利风味玫瑰芝士1个',
50 | 'price_current': '11',
51 | 'price_old': '28',
52 | 'sale_count': '已售937'
53 | },
54 | {
55 | 'url': '//m.dianping.com/tuan/deal/27633000?from=m_reculike&rec_query_id=187b73b1-e193-45ec-8e02-1e75cfedb06a',
56 | 'imgUrl': '//p1.meituan.net/deal/bee92c0066a347e533532c86d2b29ce324710.jpg%40180w_180h_1e_1c_1l_80q%7Cwatermark%3D0',
57 | 'shopName': '24TEA廿四茶',
58 | 'distance': '219m',
59 | 'desc': '[中山公园] 思慕雪系列2选1',
60 | 'price_current': '12.8',
61 | 'price_old': '22',
62 | 'sale_count': '已售6'
63 | },
64 | {
65 | 'url': '//m.dianping.com/tuan/deal/27609833?from=m_reculike&rec_query_id=187b73b1-e193-45ec-8e02-1e75cfedb06a',
66 | 'imgUrl': '//p1.meituan.net/deal/684234759406f032259051c4b112ccf856681.jpg%40180w_180h_1e_1c_1l_80q%7Cwatermark%3D0',
67 | 'shopName': '赞蛙',
68 | 'distance': '545m',
69 | 'desc': '[5店通用] 100元代金券1张,全场通用',
70 | 'price_current': '69.9',
71 | 'price_old': '100',
72 | 'sale_count': '已售694'
73 | },
74 | {
75 | 'url': '//m.dianping.com/tuan/deal/22261052?from=m_reculike&rec_query_id=187b73b1-e193-45ec-8e02-1e75cfedb06a',
76 | 'imgUrl': '//p0.meituan.net/deal/9e78d1f1cab490eb1e3d2b9c20768637100034.jpg%40180w_180h_1e_1c_1l_80q%7Cwatermark%3D0',
77 | 'shopName': '龙门花甲',
78 | 'distance': '466m',
79 | 'desc': '[11店通用] 招牌花甲,建议1-2人使用',
80 | 'price_current': '16.8',
81 | 'price_old': '22',
82 | 'sale_count': '已售9365'
83 | },
84 | {
85 | 'url': '//m.dianping.com/tuan/deal/8341015?from=m_reculike&rec_query_id=187b73b1-e193-45ec-8e02-1e75cfedb06a',
86 | 'imgUrl': '//p0.meituan.net/deal/0be8d6f4610dee0150b65f874192cabd47239.jpg%40180w_180h_1e_1c_1l_80q%7Cwatermark%3D0',
87 | 'shopName': '鲜之恋情水果吧',
88 | 'distance': '545m',
89 | 'desc': '[龙之梦购物中心等4店] 鲜榨果汁5选1,包间免费',
90 | 'price_current': '12.8',
91 | 'price_old': '21',
92 | 'sale_count': '已售66297'
93 | },
94 | {
95 | 'url': '//m.dianping.com/tuan/deal/23858190?from=m_reculike&rec_query_id=187b73b1-e193-45ec-8e02-1e75cfedb06a',
96 | 'imgUrl': '//p0.meituan.net/deal/b3018dd6f296f225bf4ada408949a70144400.jpg%40180w_180h_1e_1c_1l_80q%7Cwatermark%3D0',
97 | 'shopName': '真功夫',
98 | 'distance': '454m',
99 | 'desc': '[21店通用] 鱼香茄子饭+金杏蜜桃汁1份',
100 | 'price_current': '17',
101 | 'sale_count': '已售9063',
102 | 'price_cutdown': '新客减10.5'
103 | }
104 | ]
105 | }
106 |
--------------------------------------------------------------------------------
/src/assets/mock/slides.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'list': [
3 | [
4 | {
5 | 'url': '//link.dianping.com/universal-link?originalUrl=https%3A%2F%2Fm.dianping.com%2Fshoplist%2F1%2Fd%2F1%2Fc%2F10%2Fs%2Fs_-1%3Ffrom%3Dm_nav_1_meishi&schema=dianping%3A%2F%2Ffoodmain%3Fcategoryid%3D10%26utm%3Dulink_meishi%3Adefault%3ASafari%3Am',
6 | 'imgUrl': '//www.dpfile.com/sc/eleconfig/20160126194705meishi.png',
7 | 'name': '美食'
8 | },
9 | {
10 | 'url': '//link.dianping.com/universal-link?originalUrl=https%3A%2F%2Fm.maoyan.com%3Ffrom%3Dm_nav_2_maoyandianying&schema=dianping%3A%2F%2Fmoviemain%3Ftab%3D0%26utm%3Dulink_maoyandianying%3Adefault%3ASafari%3Am',
11 | 'imgUrl': '//www.dpfile.com/sc/eleconfig/20170223152109dp_wx_maoyan_icon.png',
12 | 'name': '猫眼电影'
13 | },
14 | {
15 | 'url': '//link.dianping.com/universal-link?originalUrl=https%3A%2F%2Fm.dianping.com%2Fawp%2Fh5%2Fhotel-dp%2Flist%2Flist.html%3Fcityid%3D1%26from%3Dm_nav_3_jiudian&schema=dianping%3A%2F%2Fhotellist%3Futm%3Dulink_jiudian%3Adefault%3ASafari%3Am',
16 | 'imgUrl': '//www.dpfile.com/sc/eleconfig/20160126203337jiudian.png',
17 | 'name': '酒店'
18 | },
19 | {
20 | 'url': '//link.dianping.com/universal-link?originalUrl=https%3A%2F%2Fm.dianping.com%2Fplay%2Fmain%2Findex%3Ffrom%3Dm_nav_4_xiuxianyule&schema=dianping%3A%2F%2Fweb%3Fnotitlebar%3D1%26url%3Dhttps%253A%252F%252Fm.dianping.com%252Fplay%252Fmain%252Findex%253Fcityid%253D*%2526latitude%253D*%2526longitude%253D*%2526dpid%253D*%2526utm%253Dxiuxianyule%26utm%3Dulink_xiuxianyule%3Adefault%3ASafari%3Am',
21 | 'imgUrl': '//www.dpfile.com/sc/eleconfig/20160126202841xiuxianyule.png',
22 | 'name': '休闲娱乐'
23 | },
24 | {
25 | 'url': '//link.dianping.com/universal-link?originalUrl=https%3A%2F%2Ftakeaway.dianping.com%2Fwaimai%2Fnewm%3Ffrom%3Dm_nav_5_waimai&schema=dianping%3A%2F%2Ftakeawayshoplist%3Ftab%3D1%26utm%3Dulink_waimai%3Adefault%3ASafari%3Am',
26 | 'imgUrl': '//www.dpfile.com/sc/eleconfig/20160126203251waimai.png',
27 | 'name': '外卖'
28 | },
29 | {
30 | 'url': '//link.dianping.com/universal-link?originalUrl=https%3A%2F%2Fm.dianping.com%2Fshoplist%2F1%2Fd%2F1%2Fc%2F110%2Fs%2Fs_-1%3Ffrom%3Dm_nav_6_huoguo&schema=dianping%3A%2F%2Flocalshoplist%3Fcategoryid%3D110%26utm%3Dulink_huoguo%3Adefault%3ASafari%3Am',
31 | 'imgUrl': '//www.dpfile.com/sc/eleconfig/20160204172927huoguo.png',
32 | 'name': '火锅'
33 | },
34 | {
35 | 'url': '//link.dianping.com/universal-link?originalUrl=https%3A%2F%2Fm.dianping.com%2Fbeauty%2Fnode%2Fhtml%2Fdpindex.html%3Fcityid%3D1%26from%3Dm_nav_7_liren&schema=dianping%3A%2F%2Fcomplexweb%3Furl%3Dhttp%253A%252F%252Fevt.dianping.com%252Fchannel%252Fbeauty%252Findex.html%253Fcityid%253D!%2526longitude%253D*%2526latitude%253D*%2526token%253D*%26utm%3Dulink_liren%3Adefault%3ASafari%3Am',
36 | 'imgUrl': '//www.dpfile.com/sc/eleconfig/20160126202946liren.png',
37 | 'name': '丽人'
38 | },
39 | {
40 | 'url': '//link.dianping.com/universal-link?originalUrl=https%3A%2F%2Fm.dianping.com%2Fshopping%2Findex%3Ffrom%3Dm_nav_8_gouwu&schema=dianping%3A%2F%2Fweb%3Fnotitlebar%3Dtrue%26url%3Dhttp%253A%252F%252Fm.dianping.com%252Fshopping%252Findex%253Fcityid%253D%2521%2526latitude%253D%252A%2526longitude%253D%252A%2526h5Title%253Dhide%26utm%3Dulink_gouwu%3Adefault%3ASafari%3Am',
41 | 'imgUrl': '//www.dpfile.com/sc/eleconfig/20160314121215icongouwu135.png',
42 | 'name': '购物'
43 | },
44 | {
45 | 'url': '//link.dianping.com/universal-link?originalUrl=https%3A%2F%2Fi.meituan.com%2Ftrip%2Flvyou%2Ftriplist%2Fpoi%2F%3Flimit%3D40%26offset%3D0%26sort%3Dsmart%26cateId%3D195%26selectedCityId%3D1%26ste%3D_bdpiother%26source%3Ddp%26from%3Dm_nav_9_zhoubianyou&schema=dianping%3A%2F%2Ftravel_poi_list%3Ffrom%3Dscene%26cateid%3D1%26travelcallapp%3Dtravelcallapptravel_poi_list%26utm%3Dulink_zhoubianyou%3Adefault%3ASafari%3Am',
46 | 'imgUrl': '//www.dpfile.com/sc/eleconfig/20160126203440zhoubianyou.png',
47 | 'name': '周边游'
48 | },
49 | {
50 | 'url': '//link.dianping.com/universal-link?originalUrl=https%3A%2F%2Fm.dianping.com%2Fshoplist%2F1%2Fd%2F1%2Fc%2F135%2Fs%2Fs_-1%3Ffrom%3Dm_nav_10_KTV&schema=dianping%3A%2F%2Fktvshoplist%3Fcategoryid%3D135%26utm%3Dulink_KTV%3Adefault%3ASafari%3Am',
51 | 'imgUrl': '//www.dpfile.com/sc/eleconfig/20160126203542ktv.png',
52 | 'name': 'KTV'
53 | }
54 | ],
55 | [
56 | {
57 | 'url': '//link.dianping.com/universal-link?originalUrl=https%3A%2F%2Fm.dianping.com%2Fapp%2Fapp-m-wedding-product-peon%2Findex.html%3Fcityid%3D1%26source%3Dm%26from%3Dm_nav_11_hunshasheying&schema=dianping%3A%2F%2Fweb%3Furl%3Dhttp%253A%252F%252Fm.dianping.com%252Fwed%252Fmobile%252Fwedding%252Findex%253Fcityid%253D!%26utm%3Dulink_hunshasheying%3Adefault%3ASafari%3Am',
58 | 'imgUrl': '//www.dpfile.com/sc/eleconfig/20160126203830jiehun.png',
59 | 'name': '婚纱摄影'
60 | },
61 | {
62 | 'url': '//link.dianping.com/universal-link?originalUrl=https%3A%2F%2Fm.dianping.com%2Feasylife%2Fnode%2Fhtml%2Fcommunity%2Findex.html%3Ffrom%3Dm_nav_12_shenghuofuwu&schema=dianping%3A%2F%2Fweb%3Furl%3Dhttp%253A%252F%252Fh5.dianping.com%252Fplatform%252Feasylife%252Findex.html%26utm%3Dulink_shenghuofuwu%3Adefault%3ASafari%3Am',
63 | 'imgUrl': '//www.dpfile.com/sc/eleconfig/20170308125500community_new.png',
64 | 'name': '生活服务'
65 | },
66 | {
67 | 'url': '//link.dianping.com/universal-link?originalUrl=https%3A%2F%2Fm.dianping.com%2Fshoplist%2F1%2Fd%2F1%2Fc%2F35%2Fs%2Fs_-1%3Ffrom%3Dm_nav_13_jingdian&schema=dianping%3A%2F%2Flocalshoplist%3Fcategoryid%3D35%26utm%3Dulink_jingdian%3Adefault%3ASafari%3Am',
68 | 'imgUrl': '//www.dpfile.com/sc/eleconfig/20160126205135jingguan.png',
69 | 'name': '景点'
70 | },
71 | {
72 | 'url': '//link.dianping.com/universal-link?originalUrl=https%3A%2F%2Fm.dianping.com%2Fshoplist%2F1%2Fd%2F1%2Fc%2F65%2Fs%2Fs_-1%3Ffrom%3Dm_nav_14_aiche&schema=dianping%3A%2F%2Fweb%3Furl%3Dhttps%253A%252F%252Fm.dianping.com%252Feasylife%252Fnode%252Fhtml%252Fcardpindex.html%253Fcityid%253D*%2526token%253D*%2526latitude%253D*%2526longitude%253D*%2526dpid%253D*%2526product%253Ddpapp%26utm%3Dulink_aiche%3Adefault%3ASafari%3Am',
73 | 'imgUrl': '//www.dpfile.com/sc/eleconfig/20160126203742aiche.png',
74 | 'name': '爱车'
75 | },
76 | {
77 | 'url': '//link.dianping.com/universal-link?originalUrl=https%3A%2F%2Fm.dianping.com%2Ffitness%2Fnode%2Fhtml%2Fdpindex.html%3Ffrom%3Dm_nav_15_yundongjianshen&schema=dianping%3A%2F%2Fweb%3Fshark%3D1%26url%3Dhttps%253A%252F%252Fm.dianping.com%252Ffitness%252Fnode%252Fhtml%252Fdpindex.html%253Fcityid%253D*%2526token%253D*%2526latitude%253D*%2526longitude%253D*%2526dpid%253D*%26notitlebar%3D1%26utm%3Dulink_yundongjianshen%3Adefault%3ASafari%3Am',
78 | 'imgUrl': '//www.dpfile.com/sc/eleconfig/20160126203617jianshen.png',
79 | 'name': '运动健身'
80 | },
81 | {
82 | 'url': '//link.dianping.com/universal-link?originalUrl=https%3A%2F%2Fm.dianping.com%2Fbaby%2Fmobile%2Findex%3Fcityid%3D1%26source%3Dm%26from%3Dm_nav_16_qinzi&schema=dianping%3A%2F%2Fweb%3Fnotitlebar%3D1%26url%3Dhttp%253a%252f%252fm.dianping.com%252fbaby%252fmobile%252findex%253fcityid%253d!%2526latitude%253d*%2526longitude%253d*%2526token%253d*%26utm%3Dulink_qinzi%3Adefault%3ASafari%3Am',
83 | 'imgUrl': '//www.dpfile.com/sc/eleconfig/20160126203905qinzi.png',
84 | 'name': '亲子'
85 | },
86 | {
87 | 'url': '//link.dianping.com/universal-link?originalUrl=https%3A%2F%2Fm.dianping.com%2Fshanghai%2Fhome%3Futm_source%3Ddp_m_shouyeicon%26from%3Dm_nav_17_jiazhuang&schema=dianping%3A%2F%2Fweb%3Furl%3Dhttps%253A%252F%252Fm.dianping.com%252Fmtdp%252Fhome%253Futm_source%253Ddp_m_shouyeicon%2526token%253D*%2526cityid%253D*%2526dpid%253D*%26utm%3Dulink_jiazhuang%3Adefault%3ASafari%3Am',
88 | 'imgUrl': '//www.dpfile.com/sc/eleconfig/20171009183850zhaungxiugongsi.png',
89 | 'name': '家装'
90 | },
91 | {
92 | 'url': '//link.dianping.com/universal-link?originalUrl=https%3A%2F%2Fm.dianping.com%2Feducation%2Fnode%2Fhtml%2Fdpchannel.html%3Fcityid%3D1%26lat%3D31.217169%26lng%3D121.41557479999999%26from%3Dm_nav_18_xuexipeixun&schema=dianping%3A%2F%2Fweb%3Furl%3Dhttps%253A%252F%252Fm.dianping.com%252Feducation%252Fnode%252Fhtml%252Fdpchannel.html%253Fcityid%253D*%2526lat%253D*%2526lng%253D*%2526token%253D*%2526dpid%253D*%26utm%3Dulink_xuexipeixun%3Adefault%3ASafari%3Am',
93 | 'imgUrl': '//www.dpfile.com/gp/cms/1455525720807.png',
94 | 'name': '学习培训'
95 | },
96 | {
97 | 'url': '//link.dianping.com/universal-link?originalUrl=https%3A%2F%2Fm.dianping.com%2Feasylife%2Fnode%2Fhtml%2Fmedicaldpindex.html%3Ffrom%3Dm_nav_19_yiliaojiankang&schema=dianping%3A%2F%2Fweb%3Furl%3Dhttp%253a%252f%252fm.dianping.com%252fmedicine%252findex%253fcityid%253d!%2526latitude%253d*%2526longitude%253d*%2526h5Title%253dhide%26utm%3Dulink_yiliaojiankang%3Adefault%3ASafari%3Am',
98 | 'imgUrl': '//www.dpfile.com/sc/eleconfig/20160126204327yiliao.png',
99 | 'name': '医疗健康'
100 | },
101 | {
102 | 'url': '//link.dianping.com/universal-link?originalUrl=https%3A%2F%2Fevt.dianping.com%2Fchannel%2Ftohome%2Findex.html%3Ffrom%3Dm_nav_20_daojia&schema=dianping%3A%2F%2Fweb%3Furl%3Dhttp%253A%252F%252Fevt.dianping.com%252Fchannel%252Ftohome%252Findex.html%2523s%253Dicon%26utm%3Dulink_daojia%3Adefault%3ASafari%3Am',
103 | 'imgUrl': '//www.dpfile.com/sc/eleconfig/20160126203812daojia.png',
104 | 'name': '到家'
105 | }
106 | ],
107 | [
108 | {
109 | 'url': '//link.dianping.com/universal-link?originalUrl=https%3A%2F%2Fm.dianping.com%2Fshoplist%2F1%2Fd%2F1%2Fc%2F112%2Fs%2Fs_-1%3Ffrom%3Dm_nav_21_xiaochikuaican&schema=dianping%3A%2F%2Flocalshoplist%3Fcategoryid%3D112%26utm%3Dulink_xiaochikuaican%3Adefault%3ASafari%3Am',
110 | 'imgUrl': '//www.dpfile.com/sc/eleconfig/20160204173331xiaochikuaican.png',
111 | 'name': '小吃快餐'
112 | },
113 | {
114 | 'url': '//link.dianping.com/universal-link?originalUrl=https%3A%2F%2Fm.dianping.com%2Fshoplist%2F1%2Fd%2F1%2Fc%2F111%2Fs%2Fs_-1%3Ffrom%3Dm_nav_22_zizhucan&schema=dianping%3A%2F%2Flocalshoplist%2F%3Fcategoryid%3D111%26utm%3Dulink_zizhucan%3Adefault%3ASafari%3Am',
115 | 'imgUrl': '//www.dpfile.com/sc/eleconfig/20160204173511zizhucan.png',
116 | 'name': '自助餐'
117 | },
118 | {
119 | 'url': '//link.dianping.com/universal-link?originalUrl=https%3A%2F%2Fm.dianping.com%2Fshoplist%2F1%2Fd%2F1%2Fc%2F113%2Fs%2Fs_-1%3Ffrom%3Dm_nav_23_ribencai&schema=dianping%3A%2F%2Flocalshoplist%2F%3Fcategoryid%3D113%26utm%3Dulink_ribencai%3Adefault%3ASafari%3Am',
120 | 'imgUrl': '//www.dpfile.com/sc/eleconfig/20160415121719rihanliaoli.png',
121 | 'name': '日本菜'
122 | },
123 | {
124 | 'url': '//link.dianping.com/universal-link?originalUrl=https%3A%2F%2Fm.dianping.com%2Fshoplist%2F1%2Fd%2F1%2Fc%2F157%2Fs%2Fs_-1%3Ffrom%3Dm_nav_24_meifa&schema=dianping%3A%2F%2Flocalshoplist%3Fcategoryid%3D157%26utm%3Dulink_meifa%3Adefault%3ASafari%3Am',
125 | 'imgUrl': '//www.dpfile.com/sc/eleconfig/20160316142804meifa.png',
126 | 'name': '美发'
127 | },
128 | {
129 | 'url': '//link.dianping.com/universal-link?originalUrl=https%3A%2F%2Fm.dianping.com%2Fshoplist%2F1%2Fd%2F1%2Fc%2F33761%2Fs%2Fs_-1%3Ffrom%3Dm_nav_25_meijiameijie&schema=dianping%3A%2F%2Flocalshoplist%3Fcategoryid%3D33761%26utm%3Dulink_meijiameijie%3Adefault%3ASafari%3Am',
130 | 'imgUrl': '//www.dpfile.com/sc/eleconfig/20160316143047meijia.png',
131 | 'name': '美甲美睫'
132 | },
133 | {
134 | 'url': '//link.dianping.com/universal-link?originalUrl=https%3A%2F%2Fm.dianping.com%2Fshoplist%2F1%2Fd%2F1%2Fc%2F158%2Fs%2Fs_-1%3Ffrom%3Dm_nav_26_meirongSPA&schema=dianping%3A%2F%2Flocalshoplist%3Fcategoryid%3D158%26utm%3Dulink_meirongSPA%3Adefault%3ASafari%3Am',
135 | 'imgUrl': '//www.dpfile.com/sc/eleconfig/20160316143239meirong.png',
136 | 'name': '美容SPA'
137 | },
138 | {
139 | 'url': '//link.dianping.com/universal-link?originalUrl=https%3A%2F%2Fm.dianping.com%2Fshoplist%2F1%2Fd%2F1%2Fc%2F159%2Fs%2Fs_-1%3Ffrom%3Dm_nav_27_shoushenxianti&schema=dianping%3A%2F%2Flocalshoplist%3Fcategoryid%3D159%26utm%3Dulink_shoushenxianti%3Adefault%3ASafari%3Am',
140 | 'imgUrl': '//www.dpfile.com/sc/eleconfig/20160316143316shoushen.png',
141 | 'name': '瘦身纤体'
142 | },
143 | {
144 | 'url': '//link.dianping.com/universal-link?originalUrl=https%3A%2F%2Fm.dianping.com%2Fshoplist%2F1%2Fc%2F193%3Ffrom%3Dm_nav_28_qinzisheying&schema=dianping%3A%2F%2Fregionshoplist%3Fcategoryid%3D193%26utm%3Dulink_qinzisheying%3Adefault%3ASafari%3Am',
145 | 'imgUrl': '//www.dpfile.com/sc/eleconfig/20160316143612qinzisheying.png',
146 | 'name': '亲子摄影'
147 | },
148 | {
149 | 'url': '//link.dianping.com/universal-link?originalUrl=https%3A%2F%2Fm.dianping.com%2Fshoplist%2F1%2Fc%2F161%3Ffrom%3Dm_nav_29_qinziyoule&schema=dianping%3A%2F%2Fregionshoplist%3Fcategoryid%3D161%26utm%3Dulink_qinziyoule%3Adefault%3ASafari%3Am',
150 | 'imgUrl': '//www.dpfile.com/sc/eleconfig/20160316143656qinziyoule.png',
151 | 'name': '亲子游乐'
152 | },
153 | {
154 | 'url': '//m.dianping.com/shanghai/category?from=m_nav_30_quanbufenlei',
155 | 'imgUrl': '//www.dpfile.com/sc/eleconfig/20160125182200more.png',
156 | 'name': '全部分类'
157 | }
158 | ]
159 | ]
160 | }
161 |
--------------------------------------------------------------------------------
/src/assets/mock/super_sale.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'list': [
3 | [
4 | {
5 | 'url': '//m.dianping.com/tuan/deal/26457650?from=m_discount',
6 | 'imgUrl': '//p1.meituan.net/dpdeal/bac0bcf62326e5cdd9548ac8d6c21cfe180480.jpg%40120w_90h_1e_1c_1l%7Cwatermark%3D1%26%26r%3D1%26p%3D9%26x%3D20%26y%3D20',
7 | 'title': '樱花国际日语',
8 | 'price_current': '1',
9 | 'price_old': '990'
10 | },
11 | {
12 | 'url': '//m.dianping.com/tuan/deal/24089247?from=m_discount',
13 | 'imgUrl': '//p0.meituan.net/dpdeal/dc5fa9c65b4b78db268e467826806dae65429.jpg%40120w_90h_1e_1c_1l%7Cwatermark%3D1%26%26r%3D1%26p%3D9%26x%3D20%26y%3D20',
14 | 'title': '维丽娅化妆美容美甲专业培训学校',
15 | 'price_current': '1',
16 | 'price_old': '99'
17 | },
18 | {
19 | 'url': '//m.dianping.com/tuan/deal/18419791?from=m_discount',
20 | 'imgUrl': '//p1.meituan.net/dpdeal/31143c5ae8aaacae43d53805d5c26d12351389.jpg%40120w_90h_1e_1c_1l%7Cwatermark%3D1%26%26r%3D1%26p%3D9%26x%3D20%26y%3D20',
21 | 'title': '柏斯琴行',
22 | 'price_current': '1',
23 | 'price_old': '40'
24 | }
25 | ],
26 | [
27 | {
28 | 'url': '//m.dianping.com/tuan/deal/20013185?from=m_reduce',
29 | 'imgUrl': '//p1.meituan.net/deal/2c12dccbf82e1ea051317a79a9de434d17813.jpg%40120w_90h_1e_1c_1l%7Cwatermark%3D1%26%26r%3D1%26p%3D9%26x%3D20%26y%3D20',
30 | 'title': 'AimiCake艾米蛋糕',
31 | 'price_current': '198'
32 | },
33 | {
34 | 'url': '//m.dianping.com/tuan/deal/19605977?from=m_reduce',
35 | 'imgUrl': '//p1.meituan.net/deal/33714adbb8abc509b72003a66cc5d5c634781.jpg%40120w_90h_1e_1c_1l%7Cwatermark%3D1%26%26r%3D1%26p%3D9%26x%3D20%26y%3D20',
36 | 'title': '龙华素斋',
37 | 'price_current': '97',
38 | 'price_down': '立减2元'
39 | },
40 | {
41 | 'url': '//m.dianping.com/tuan/deal/20800516?from=m_reduce',
42 | 'imgUrl': '//p0.meituan.net/deal/ce50f15b3207185ab1cda459046999d459731.jpg%40120w_90h_1e_1c_1l%7Cwatermark%3D1%26%26r%3D1%26p%3D9%26x%3D20%26y%3D20',
43 | 'title': '星期五翻糖蛋糕',
44 | 'price_current': '298',
45 | 'price_down': '立减20元'
46 | }
47 | ]
48 | ]
49 | }
50 |
--------------------------------------------------------------------------------
/src/components/board.vue:
--------------------------------------------------------------------------------
1 |
2 | .board-wrap
3 | .divider
4 | .wrap(v-for="(board,index) in activities" :key="index")
5 | .board
6 | .item(v-for="item in board" :key="item.imgUrl")
7 | .cnt
8 | .title {{item.title}}
9 | .desc {{item.desc}}
10 | img(:src="item.imgUrl")
11 | .divider
12 |
13 |
14 |
24 |
25 |
92 |
--------------------------------------------------------------------------------
/src/components/header.vue:
--------------------------------------------------------------------------------
1 |
2 | header
3 | .city 上海
4 | .search-wrap(ref="search")
5 | span(ref="placeholder" v-if="!searchValue") 输入商户名、地点
6 | input(@input="onValueChange" @focus="onfocus" @blur="onBlur" ref="input" v-model="searchValue")
7 | .user
8 | .icon
9 |
10 |
11 |
60 |
61 |
137 |
138 |
--------------------------------------------------------------------------------
/src/components/headline.vue:
--------------------------------------------------------------------------------
1 |
2 | .headline-wrap
3 | .logo(@click="logoClick")
4 | swiper.headline(:options="swiperOption")
5 | swiper-slide.article(v-for="(item,index) in headline" :key="index")
6 | .title {{item.title}}
7 | .img-wrap
8 | img(:src="item.imgUrl")
9 | .img-count {{item.imgCount}}
10 |
11 |
12 |
38 |
39 |
93 |
--------------------------------------------------------------------------------
/src/components/modules.vue:
--------------------------------------------------------------------------------
1 |
2 | swiper.modules(:options="swiperOption")
3 | swiper-slide.page(v-for="(page,index) in modules" :key="index")
4 | .module(v-for="module in page" :key="module.name")
5 | img(v-lazy="module.imgUrl")
6 | .name {{module.name}}
7 | .swiper-pagination(slot="pagination")
8 |
9 |
10 |
32 |
33 |
64 |
--------------------------------------------------------------------------------
/src/components/shops.vue:
--------------------------------------------------------------------------------
1 |
2 | .shop-list-wrap
3 | .head 猜你喜欢
4 | .shop-list
5 | .shop(v-for="shop in shopList" :key="shop.url")
6 | .shop-logo
7 | img(v-lazy="shop.imgUrl" width="90px" height="90px")
8 | .tag 免预约
9 | .content
10 | .title {{shop.shopName}}
11 | .desc {{shop.desc}}
12 | .status
13 | .price-wrap
14 | span.price.now {{shop.price_current}}
15 | span.price.old {{shop.price_old}}
16 | span.cutdown {{shop.price_cutdown}}
17 | .sale-count {{shop.sale_count}}
18 |
19 |
20 |
39 |
40 |
154 |
--------------------------------------------------------------------------------
/src/components/super_sale.vue:
--------------------------------------------------------------------------------
1 |
2 | .sales-wrap
3 | .wrap(v-for="(saleItem,index) in superSales" :key="index")
4 | .sale-item
5 | .title-wrap
6 | .titleIcon
7 | .more-benift
8 | .content
9 | .item(v-for="item in saleItem" :key="item.title")
10 | img(:src="item.imgUrl" width="100%")
11 | .title {{item.title}}
12 | .price-wrap
13 | span.price.now {{item.price_current}}
14 | span.price.old {{item.price_old}}
15 | span.cutdown {{item.price_down}}
16 | .divider
17 |
18 |
19 |
29 |
30 |
136 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import App from './App.vue'
3 | import router from './router'
4 | import store from './store'
5 | import VueLazyload from 'vue-lazyload'
6 | import './assets/mock'
7 |
8 | Vue.use(VueLazyload, { preLoad: 1 })
9 |
10 | Vue.config.productionTip = false
11 |
12 | const app: Vue = new Vue({
13 | el: '#app',
14 | router,
15 | store,
16 | render: h => h(App)
17 | })
18 |
19 | export default app
20 |
--------------------------------------------------------------------------------
/src/router/index.ts:
--------------------------------------------------------------------------------
1 | import Vue, { AsyncComponent } from 'vue'
2 | import Router, { RouteConfig, Route, NavigationGuard } from 'vue-router'
3 |
4 | const main: AsyncComponent = (): any => import('@/views/main.vue')
5 |
6 | // import main from '@/views/main.vue'
7 |
8 | Vue.use(Router)
9 |
10 | const routes: RouteConfig[] = [
11 | {
12 | path: '/',
13 | redirect: '/main'
14 | },
15 | {
16 | path: '/main',
17 | name: 'main',
18 | component: main
19 | }
20 | ]
21 |
22 | const router: Router = new Router({
23 | mode: 'history',
24 | base: '/',
25 | routes
26 | })
27 |
28 | export default router
29 |
--------------------------------------------------------------------------------
/src/store/actions.ts:
--------------------------------------------------------------------------------
1 | import { ActionTree } from 'vuex'
2 | import axios from 'axios'
3 | import TYPES from './types'
4 |
5 | const actions: ActionTree = {
6 | // ajax 初始化
7 | async initAjax({ dispatch }) {
8 | dispatch('getModules')
9 | dispatch('getHeadline')
10 | setTimeout(() => dispatch('getActivity'), 2000)
11 | dispatch('getSuperSale')
12 | dispatch('getShops')
13 | },
14 | // 获取modules
15 | async getModules({ state, commit }) {
16 | const res: Ajax.AjaxResponse = await axios.get('/modules').then((res) => res.data).catch((e: string) => console.error(e))
17 | if (res && res.code == 200) commit(TYPES.SET_MODULES, res.result.list)
18 | },
19 | // 点评头条
20 | async getHeadline({ state, commit }) {
21 | const res: Ajax.AjaxResponse = await axios.get('/headline').then((res) => res.data).catch((e: string) => console.error(e))
22 | if (res && res.code == 200) commit(TYPES.SET_HEADLINE, res.result.list)
23 | },
24 | // 活动面板
25 | async getActivity({ state, commit }) {
26 | const res: Ajax.AjaxResponse = await axios.get('/activity').then((res) => res.data).catch((e: string) => console.error(e))
27 | if (res && res.code == 200) commit(TYPES.SET_ACTIVITIES, res.result.list)
28 | },
29 | // 特卖优惠
30 | async getSuperSale({ state, commit }) {
31 | const res: Ajax.AjaxResponse = await axios.get('/supersale').then((res) => res.data).catch((e: string) => console.error(e))
32 | if (res && res.code == 200) commit(TYPES.SET_SUPER_SALE, res.result.list)
33 | },
34 | // 商户列表
35 | async getShops({ state, commit }) {
36 | const res: Ajax.AjaxResponse = await axios.get('/shops').then((res) => res.data).catch((e: string) => console.error(e))
37 | if (res && res.code == 200) commit(TYPES.SET_SHOPS, res.result.list)
38 | },
39 | // 更新搜索内容
40 | setSearchVal({ commit }, val: string) {
41 | commit(TYPES.SET_SEARCH_VAL, val)
42 | }
43 | }
44 |
45 | export default actions
46 |
--------------------------------------------------------------------------------
/src/store/getters.ts:
--------------------------------------------------------------------------------
1 | import { GetterTree } from 'vuex'
2 |
3 | const getters: GetterTree = {
4 | load(state): boolean {
5 | const { modules, headline, activities, superSales, shops } = state
6 | return !!(modules.length && headline.length && activities.length && superSales.length && shops.length)
7 | }
8 | }
9 |
10 | export default getters
11 |
--------------------------------------------------------------------------------
/src/store/index.ts:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Vuex, { ActionTree, MutationTree } from 'vuex'
3 | import actions from './actions'
4 | import mutations from './mutations'
5 | import getters from './getters'
6 | // import 'babel-polyfill'
7 |
8 | Vue.use(Vuex)
9 |
10 | interface State {
11 | login: Boolean,
12 | modules: StoreState.module[],
13 | headline: StoreState.headline[],
14 | activities: StoreState.activity[],
15 | superSales: StoreState.superSale[],
16 | shops: StoreState.shop[],
17 | searchVal: string
18 | }
19 |
20 | let state: State = {
21 | login: false,
22 | modules: [],
23 | headline: [],
24 | activities: [],
25 | superSales: [],
26 | shops: [],
27 | searchVal: ''
28 | }
29 |
30 | export default new Vuex.Store({
31 | state,
32 | actions,
33 | getters,
34 | mutations
35 | })
36 |
--------------------------------------------------------------------------------
/src/store/mutations.ts:
--------------------------------------------------------------------------------
1 | import TYPES from './types'
2 | import { MutationTree } from 'vuex'
3 |
4 | const mutations: MutationTree = {
5 | [TYPES.SET_MODULES](state, modules): void {
6 | state.modules = modules
7 | },
8 | [TYPES.SET_HEADLINE](state, headline): void {
9 | state.headline = headline
10 | },
11 | [TYPES.SET_ACTIVITIES](state, activities): void {
12 | state.activities = activities
13 | },
14 | [TYPES.SET_SUPER_SALE](state, superSales): void {
15 | state.superSales = superSales
16 | },
17 | [TYPES.SET_SHOPS](state, shops): void {
18 | state.shops = shops
19 | },
20 | [TYPES.SET_SEARCH_VAL](state, val): void {
21 | state.searchVal = val
22 | }
23 | }
24 | export default mutations
25 |
--------------------------------------------------------------------------------
/src/store/types.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | SET_MODULES: 'SET_MODULES',
3 | SET_HEADLINE: 'SET_HEADLINE',
4 | SET_ACTIVITIES: 'SET_ACTIVITIES',
5 | SET_SUPER_SALE: 'SET_SUPER_SALE',
6 | SET_SHOPS: 'SET_SHOPS',
7 | SET_SEARCH_VAL: 'SET_SEARCH_VAL',
8 | }
9 |
--------------------------------------------------------------------------------
/src/typings/ajax.d.ts:
--------------------------------------------------------------------------------
1 | declare namespace Ajax {
2 |
3 | // axios 返回数据
4 | export interface AxiosResponse {
5 | data: AjaxResponse;
6 | }
7 |
8 | // 请求接口数据
9 | export interface AjaxResponse {
10 | /**
11 | * 状态码
12 | * @type { number }
13 | */
14 | code: number;
15 |
16 | /**
17 | * 数据
18 | * @type { any }
19 | */
20 | result: any;
21 |
22 | /**
23 | * 消息
24 | * @type { string }
25 | */
26 | message?: string
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/typings/store.d.ts:
--------------------------------------------------------------------------------
1 | declare namespace StoreState {
2 |
3 | interface User {
4 | name: string,
5 | age: number
6 | }
7 |
8 | // 顶部可左右滑动模块
9 | export interface module {
10 | // 点击跳转链接
11 | url: string,
12 | // 图片链接
13 | imgUrl: string,
14 | // 模块名称
15 | name: string,
16 | // 用户信息
17 | user: User
18 | }
19 |
20 | // 点评头条
21 | export interface headline {
22 | // 点击跳转链接
23 | url: string,
24 | // 图片链接
25 | imgUrl: string,
26 | // 文章标题
27 | title: string,
28 | // 文章图片数量统计
29 | imgCount: number
30 | }
31 |
32 | // 活动特惠
33 | export interface activity {
34 | // 活动标题
35 | title: string,
36 | // 活动描述
37 | desc: string,
38 | // 活动图片链接
39 | imgUrl: string
40 | }
41 |
42 | // 超值特惠
43 | export interface superSale {
44 | // 点击跳转链接
45 | url: string,
46 | // 图片链接
47 | imgUrl: string,
48 | // 活动标题
49 | title: string,
50 | // 当前价格
51 | price_current: string,
52 | // 原价
53 | price_old: string
54 | }
55 |
56 | // 商户
57 | export interface shop {
58 | // 商户跳转链接
59 | url: string,
60 | // 商户图片链接
61 | imgUrl: string,
62 | // 商户名称
63 | shopName: string,
64 | // 距离
65 | distance: string,
66 | // 简介
67 | desc: string,
68 | // 当前价格
69 | price_current: number,
70 | // 原价
71 | price_old: number,
72 | // 已售
73 | sale_count: string
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/typings/tools.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'vue-awesome-swiper' {
2 | export const swiper: any
3 | export const swiperSlide: any
4 | }
5 |
6 | declare module 'vue-lazyload'
7 |
--------------------------------------------------------------------------------
/src/typings/vue-shims.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.vue' {
2 | import Vue from 'vue'
3 | export default Vue
4 | }
5 |
--------------------------------------------------------------------------------
/src/views/main.vue:
--------------------------------------------------------------------------------
1 |
2 | .main-wrapper
3 | v-header
4 | .content-wrapper
5 | modules
6 | div(v-if="load")
7 | headline
8 | board
9 | super_sale
10 | shop_list
11 | .loading(v-else)
12 |
13 |
14 |
49 |
50 |
61 |
--------------------------------------------------------------------------------
/static/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SimonZhangITer/vue-typescript-dpapp-demo/33945274ddb51214e8467dc00b7e29d98dd87aaf/static/.gitkeep
--------------------------------------------------------------------------------
/temme:
--------------------------------------------------------------------------------
1 | .page@list {
2 | .item@ {
3 | .icon[src=$imgUrl nav-lazy-src=$imgUrl];
4 | div{$name};
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "include": [
3 | "src/*",
4 | "src/**/*"
5 | ],
6 | "exclude": [
7 | "node_modules"
8 | ],
9 | "compilerOptions": {
10 | "strictPropertyInitialization": false,
11 | // types option has been previously configured
12 | "types": [
13 | // add node as an option
14 | "node"
15 | ],
16 | // typeRoots option has been previously configured
17 | "typeRoots": [
18 | // add path to @types
19 | "node_modules/@types"
20 | ],
21 | // 以严格模式解析
22 | "strict": true,
23 | // 在.tsx文件里支持JSX
24 | "jsx": "preserve",
25 | // 使用的JSX工厂函数
26 | "jsxFactory": "h",
27 | // 允许从没有设置默认导出的模块中默认导入
28 | "allowSyntheticDefaultImports": true,
29 | // 启用装饰器
30 | "experimentalDecorators": true,
31 | "strictFunctionTypes": false,
32 | // 允许编译javascript文件
33 | "allowJs": true,
34 | // 采用的模块系统
35 | "module": "esnext",
36 | // 编译输出目标 ES 版本
37 | "target": "es5",
38 | // 如何处理模块
39 | "moduleResolution": "node",
40 | // 在表达式和声明上有隐含的any类型时报错
41 | "noImplicitAny": true,
42 | "lib": [
43 | "dom",
44 | "es5",
45 | "es6",
46 | "es7",
47 | "es2015.promise"
48 | ],
49 | "sourceMap": true,
50 | "pretty": true
51 | }
52 | }
53 |
--------------------------------------------------------------------------------