每次有更新需要推送到客户时,在根目录下执行 npm version [patch|minor|major|prerelease]
等( version 更迭不支持 yarn 命令),有关 npm version,参考这里
例如,即将从 v4.0.0-rc.6
更新到 v4.0.0-rc.7
,这是一个 prerelease 版本迭代,则执行 npm version prerelease
后 push 到远端即可,该命令会自动 release tag 并推进客户端版本号。
本项目与 npm 版本号迭代规则略有不同,基本遵循以下特点
- 有问题修复,UI变更,功能优化,文案变更等内容,则发布 patch 版本
- 有新功能点、接口变更等,则发布 minor 版本
- 全面迭代,则发布 major 版本
- major 版本迭代后宣布稳定前,则发布 prerelease 版本
为了高效率生成 CHANGELOG (版本更新内容一览),则 commit message 需要根据一定规范进行编写。
本项目使用 angular 规范 (可以参考阮一峰的 Commit message 和 Change log 编写指南)
每次执行 npm version **
时会自动生成截至上一版发布期间的所有 feature、fix、break change 内容。
考虑到本项目的后续维护者参与维护,在参与开发前,请尽量阅读 Vue 官方编写的 风格指南。
为了快速入手项目的二次开发,这里列出一些必要的规范,请务必阅读以下规范后再进行编码工作。
按照 vue 官方推荐的组件命名方法,文件名命名方式为大驼峰式 (CamelCase),组件的 name
属性值要与文件名一致,便于使用 vue devtools 进行调试和快速定位文件。
可跨模块使用的组件存放在 @/components
目录中,按功能归类存放于子文件夹下。
在模块内使用的组件存放在 @/page/[模块名]/components
目录下。
example 以动态模块为例,动态模块的视图存放在
@/page/feed
下,
其中动态列表视图在@/page/feed/FeedList.vue
,动态详情视图在@/page/feed/FeedDetail.vue
;
动态列表视图中含有普通的动态卡片组件和广告卡片组件,
动态卡片组件在@/page/feed/components/FeedCard.vue
,广告卡片组件在@/page/feed/components/AdCard.vue
。
全局钩子挂载于 ./src/bus.js
下的事件总线上。
呼出底部按钮弹框
/**
* @typedef {Object} ActionButton
* @property {string} text - 按钮的文字
* @property {Function} method - 按钮的回调方法
* @property {Object} [style] - 按钮的样式
*/
const actions = [
{ text: '确定', method: () => { console.log('button clicked!') }, style: { color: 'green' } }
]
/**
* Call actionSheet
* @author jsonleex <[email protected]>
* @param {ActionButton[]} btnLists - [{ text: "确定", method: () => {} }, ...]
* @param {string} [cancelTxt] - "取消"
* @param {string} [tips] - 提示文字
*/
this.$bus.$emit('actionSheet', actions, '取消', '你确定要这么做吗?');
呼出打赏窗口
以打赏动态为例
// ...
const api = axios.post(url, payload, {validateStatus});
const cb = amount => { console.log(amount); }
/**
* 弹出打赏窗口 (hooks -> reward)
* @author mutoe <[email protected]>
* @param {Object} options
* @param {string} options.type 打赏的类型
* @param {AxiosPromise} options.api 打赏的 api,接受 axios promise 对象
* @param {string|Object} options.payload api 的第一个参数,取决于 api
* @param {requestCallback} [options.callback = noop] 打赏成功后的回调方法, 接受一个参数 amount 打赏金额
*/
this.$bus.$emit("reward", {
type: "feed",
api,
payload: { feedID },
callback:cb
});
// ...
const api = axios.post(url, payload, {validateStatus});
const cb = () => { console.log("success"); }
/**
* 弹出申请置顶窗口 (hooks -> applyTop)
* @author mutoe <[email protected]>
* @param {Object} options
* @param {string} options.type 申请置顶类型
* @param {AxiosPromise} options.api 申请置顶的 api,需要返回 axios promise 对象
* @param {string|Object} options.payload 申请置顶 api 的第一个参数,取决于 api 中的设定
* @param {boolean} [options.isOwner = false] 是否是文章的所有者, 文章的所有者申请置顶时文案略有不同
* @param {requestCallback} [options.callback = noop] 申请置顶成功后执行的回调方法
*/
this.$bus.$emit("applyTop", {
type: "",
api,
payload: { feedID },
isOwner: true,
callback: cb
})
@/components/common/CommonHeader.vue
用于各页面顶部导航栏. 通用组件默认注入到全局,你可以不用在页面内单独注册通用组件。
<template>
<div>
<common-header class="header">
钱包
<template slot="right">
<router-link :to="{ path: 'detail' }"> 明细 </router-link>
</template>
</common-header>
<main>...<main>
</div>
</template>
<script>
export default {}
</script>
点击后退按钮的行为, 如果要执行除后退以外的其他操作, 给这个 prop 传入一个方法即可. 默认值为全局注入的 goBack
方法.
含有一个匿名slot和两个具名slot。
显示为标题,居中显示,最大宽度支持 12em。
显示在导航栏左边,最大宽度支持 4em
如果导航栏的左边没有其他特殊需要,可以不声明该插槽,默认生成一个含有 goBack 事件的返回按钮。
如果左边不想要任何东西,可以声明一个空模版。像这样
<common-header>
左边没有按钮
<template slot="left" />
</common-header>
显示在导航栏右边,最大宽度支持 4em
你在新建页面时可以不必去其他页面复制过来这么一大坨代码,也可以让其他人维护这个页面的时候很清除的知道这个页面有哪些东西而不是如何实现的。
只要你知道这个组件的名字就可以很方便的添加头部了。
<!-- before -->
<header class="m-box m-justify-bet m-aln-center m-bb1 m-head-top m-main m-lim-width">
<div class="m-box m-flex-grow1 m-aln-center m-flex-base0">
<svg class="m-style-svg m-svg-def" @click="goBack">
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#icon-back"/>
</svg>
</div>
<div class="m-box-model m-flex-grow1 m-aln-center m-flex-base0 m-head-top-title">
<span>提现</span>
</div>
<div class="m-box m-flex-grow1 m-aln-center m-flex-base0 m-justify-end"/>
</header>
<!-- after -->
<common-header>提现</common-header>
@/components/common/NavTab.vue
<template>
<div class="p-group-search">
<nav-tab :nav="nav" :route="{name: 'searchGroup', query: 'type'}" />
<!-- ... -->
</div>
</template>
<script>
import NavTab from '@/components/common/NavTab.vue';
export default {
components: { NavTab },
data: () => {
nav: {
groups: '圈子',
posts: '帖子'
}
},
created() {
if (!this.$route.query.type)
this.$router.replace(
Object.assign({}, this.$route, { query: { type: "groups" } })
);
},
}
</script>
@/components/common/SearchBar.vue
<template>
<div class="p-search-post">
<search-bar v-model="keyword" />
<!-- ... -->
</div>
</template>
<script>
import SearchBar from '@/components/common/SearchBar.vue';
export default {
components: { SearchBar },
data: () => {keyword: ''},
}
</script>
点击后退按钮的行为, 如果要执行除后退以外的其他操作, 给这个 prop 传入一个方法即可. 默认值为全局注入的 goBack
方法.
输入搜索内容, 双向绑定特性(使用 v-model
进行绑定)
提示内容, 默认值为 "搜索"
@/components/PopupDialog.vue
用于各种临时信息展示,如规则展示,协议弹框展示,首次进入某功能弹框提示信息。
<template>
<div>
<button @click="showDialog">show dialog</button>
<popup-dialog ref="dialog" title="充值提现规则" @confirm="onConfirm">
{{ 今晚打老虎今晚打老虎今晚打老虎今晚打老虎今晚打老虎... }}
</popup-dialog>
<div>
</template>
<script>
export default {
methods: {
showDialog() { this.$refs.dialog.show() },
onConfirm() { console.log('confirm clicked!') }
}
}
</script>
含有一个匿名 slot,为 dialog 主要内容
可选,弹框标题
可选,弹框的确定按钮文案。默认值为“知道了”
当按下确定按钮时的回调事件
显示 dialog,缓慢上移淡入的动画。
隐藏 dialog,缓慢下移淡出的动画
@/components/ImagePoster.vue
用于各页面中图片上传相关内容,使用方法详见 @/pages/profile/Certificate.vue
<template>
<image-poster @uploaded="uploaded">
<span>点击上传反面身份证照片</span>
</image-poster>
</template>
<script>
import ImagePoster from "@/components/ImagePoster.vue";
export default {
components: { ImagePoster }
methods: {
uploaded(poster) {
console.log(poster);
}
}
}
</script>
含有一个匿名 slot,支持任何 html 标签,显示在上传组件的 icon 下方
图片上传成功后的回调方法,接受一个参数,值为已上传的图片信息。
图片上传失败的回调方法
@/components/advertisement/BannerAd.vue
该组件使用了轮播图插件, 依赖
c-swipe
pspgbhu/vue-swipe-mobile, gzip 10KB
用于各详情页广告位插槽,用法非常简单
<template>
<div>
<banner-ad type="feed:hot"/>
</div>
</template>
<script>
import BannerAd from "@/components/advertisiment/BannerAd.vue";
export default {
components: { BannerAd }
}
</script>
显示的广告位类型, 支持的值有
feed:hot
热门动态列表页news
资讯列表页
用于获取对应页面广告具体数据
轮播图循环时间,单位 ms
@/components/advertisement/DetailAd.vue
用于各详情页广告位插槽,用法非常简单
<template>
<div>
<detail-ad type="feed"/>
</div>
</template>
<script>
import DetailAd from "@/components/advertisement/DetailAd.vue";
export default {
components: { DetailAd }
}
</script>
显示的广告位类型, 支持的值有
feed
动态详情页news
资讯详情页group:home
圈子首页group:post
圈子帖子详情页
用于获取对应页面广告具体数据
表单组件用于快速构建样式和交互方式统一的表单项
表单组件包含以下组件
FormInputItem
文本输入框FormLocationItem
位置选择框FormAvatarItem
头像选择栏FormTagsItem
标签选择栏FormSelectItem
通用选择框FormSwitchItem
切换开关栏TextareaInput
多行输入文本框(不含label)
<template>
<form>
<form-avatar-item v-model="avatar" label="请上传圈子头像" />
<form-input-item v-model="name" placeholder="请输入圈子名称, 20字以内" maxlength="20" />
<form-input-item v-model="summary" type="textarea" placeholder="请输入圈子简介, 255字以内" maxlength="255" warnlength="200" />
<form-tags-item v-model="tags" placeholder="请选择圈子标签"/>
<form-select-item v-model="modeMap[mode] || ''" placeholder="请选择圈子类别" @click="mode = 'private'" />
<form-location-item v-model="location" placeholder="设置圈子的地理位置" />
<form-switch-item v-model="allowFeed" label="是否将帖子同步至动态" />
</form>
<textarea-input v-model="summary" maxlength="50" warnlength="30" placeholder="这是一个占满宽度的文本输入框" />
</template>
<script>
export default {
data: () => {
name: '',
location: '',
avatar: '', // base64 string
tags: [], // tag array
mode: 'public',
allowFeed: false,
modeMap: {
public: "公开圈子",
private: "私有圈子",
paid: "付费圈子"
}
}
}
</script>
通用适用于所有 FormItem 子组件
�输入框标签 label
占位符
只读
该组件支持普通单行文本和多行文本
输入框的类型, 支持 input(单行文本框) 和 textarea(多行文本框)
支持的最大字符数
仅当 type="textarea" 时有效 当字符数超过多少时显示字数提示