|
|
<template>
|
|
|
<div class="icon-btn" @click="onClick" :style="btnStyle">
|
|
|
<AuthComponent :auth="isAuth" class="icon-btn" @click="onClick" :style="btnStyle">
|
|
|
<i class="iconfont" :class="iconClass" :style="iconStyle"></i>
|
|
|
<p v-if="viewText" class="icon-btn-text" :style="textStyle">
|
|
|
<span class="view-text">{{viewText}}</span>
|
|
|
<span class="placeholder-text">{{placText}}</span>
|
|
|
</p>
|
|
|
</div>
|
|
|
</AuthComponent>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
import {forEach, get, fill} from 'lodash';
|
|
|
import {createNamespacedHelpers} from 'vuex';
|
|
|
const {mapActions} = createNamespacedHelpers('user');
|
|
|
|
|
|
const classMap = {
|
|
|
fav: {
|
|
|
default: 'icon-zan',
|
|
|
selected: 'icon-zan-fill'
|
|
|
},
|
|
|
star: {
|
|
|
default: 'icon-star',
|
|
|
selected: 'icon-star-fill'
|
|
|
},
|
|
|
share: {
|
|
|
default: 'icon-share'
|
|
|
import {forEach, fill} from 'lodash';
|
|
|
import {createNamespacedHelpers} from 'vuex';
|
|
|
const {mapActions} = createNamespacedHelpers('user');
|
|
|
|
|
|
const classMap = {
|
|
|
fav: {
|
|
|
default: 'icon-zan',
|
|
|
selected: 'icon-zan-fill'
|
|
|
},
|
|
|
star: {
|
|
|
default: 'icon-star',
|
|
|
selected: 'icon-star-fill'
|
|
|
},
|
|
|
share: {
|
|
|
default: 'icon-share'
|
|
|
},
|
|
|
msg: {
|
|
|
default: 'icon-msg'
|
|
|
}
|
|
|
};
|
|
|
|
|
|
const defaultOption = {
|
|
|
canSelect: true, // 是否支持选中
|
|
|
selected: false, // 初始选中状态(不受是否支持选中控制)
|
|
|
color: '#444', // btn字体颜色
|
|
|
selectedColor: '#d90025', // btn选中状态字体颜色(不设置默认与非选中一致)
|
|
|
iconFontSize: 48, // icon字号(单位px)
|
|
|
textSelectedColor: '#444', // text选中状态字体颜色(不设置默认与selectedColor一致)
|
|
|
textFontSize: 20, // text字号(单位px)
|
|
|
textAlign: 'top', // text位置, 默认normal(支持normal, top, bottom)
|
|
|
textZoom: 0.9, // text缩放
|
|
|
textAutoChange: true, // text自动增减,只支持number类型(受是否支持选中控制)
|
|
|
emitName: '' // 点击触发事件名称
|
|
|
};
|
|
|
|
|
|
export default {
|
|
|
name: 'WidgetIconBtn',
|
|
|
props: {
|
|
|
type: {
|
|
|
type: String,
|
|
|
default: 'fav'
|
|
|
},
|
|
|
msg: {
|
|
|
default: 'icon-msg'
|
|
|
}
|
|
|
};
|
|
|
|
|
|
const defaultOption = {
|
|
|
canSelect: true, // 是否支持选中
|
|
|
selected: false, // 初始选中状态(不受是否支持选中控制)
|
|
|
color: '#444', // btn字体颜色
|
|
|
selectedColor: '#d90025', // btn选中状态字体颜色(不设置默认与非选中一致)
|
|
|
iconFontSize: 48, // icon字号(单位px)
|
|
|
textSelectedColor: '#444', // text选中状态字体颜色(不设置默认与selectedColor一致)
|
|
|
textFontSize: 20, // text字号(单位px)
|
|
|
textAlign: 'top', // text位置, 默认normal(支持normal, top, bottom)
|
|
|
textZoom: 0.9, // text缩放
|
|
|
textAutoChange: true, // text自动增减,只支持number类型(受是否支持选中控制)
|
|
|
emitName: '' // 点击触发事件名称
|
|
|
};
|
|
|
|
|
|
export default {
|
|
|
name: 'WidgetIconBtn',
|
|
|
props: {
|
|
|
type: {
|
|
|
type: String,
|
|
|
default: 'fav'
|
|
|
},
|
|
|
text: {
|
|
|
type: [String, Number],
|
|
|
default() {
|
|
|
return '';
|
|
|
}
|
|
|
},
|
|
|
articleId: Number,
|
|
|
commentId: Number,
|
|
|
option: {
|
|
|
type: Object,
|
|
|
default() {
|
|
|
return defaultOption;
|
|
|
}
|
|
|
text: {
|
|
|
type: [String, Number],
|
|
|
default() {
|
|
|
return '';
|
|
|
}
|
|
|
},
|
|
|
data() {
|
|
|
return {
|
|
|
viewOption: {},
|
|
|
btnSelected: false,
|
|
|
actionClass: '',
|
|
|
editText: null
|
|
|
articleId: Number,
|
|
|
commentId: Number,
|
|
|
option: {
|
|
|
type: Object,
|
|
|
default() {
|
|
|
return defaultOption;
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
data() {
|
|
|
return {
|
|
|
viewOption: {},
|
|
|
btnSelected: false,
|
|
|
actionClass: '',
|
|
|
editText: null
|
|
|
};
|
|
|
},
|
|
|
created() {
|
|
|
forEach(defaultOption, (value, key) => {
|
|
|
this.viewOption[key] = this.option.hasOwnProperty(key) ? this.option[key] : defaultOption[key];
|
|
|
});
|
|
|
this.viewOption = {...this.viewOption};
|
|
|
},
|
|
|
computed: {
|
|
|
syncFnName() {
|
|
|
if (this.articleId) {
|
|
|
if (this.type === 'fav') {
|
|
|
return 'praiseArticle';
|
|
|
} else if (this.type === 'star') {
|
|
|
return 'followArticle';
|
|
|
}
|
|
|
} else if (this.commentId && this.type === 'fav') {
|
|
|
return 'praiseComment';
|
|
|
}
|
|
|
},
|
|
|
created() {
|
|
|
forEach(defaultOption, (value, key) => {
|
|
|
this.viewOption[key] = this.option.hasOwnProperty(key) ? this.option[key] : defaultOption[key];
|
|
|
});
|
|
|
this.viewOption = {...this.viewOption};
|
|
|
isAuth() {
|
|
|
return !!this.syncFnName;
|
|
|
},
|
|
|
computed: {
|
|
|
btnStyle() {
|
|
|
let color = this.viewOption.color || defaultOption.color;
|
|
|
btnStyle() {
|
|
|
let color = this.viewOption.color || defaultOption.color;
|
|
|
|
|
|
return `color: ${this.btnSelected ? (this.viewOption.selectedColor || color) : color};`;
|
|
|
},
|
|
|
iconClass() {
|
|
|
if (this.actionClass) {
|
|
|
return this.actionClass;
|
|
|
}
|
|
|
|
|
|
if (!this._icon) {
|
|
|
this._type = classMap[this.type] ? this.type : 'fav'
|
|
|
this._icon = classMap[this._type];
|
|
|
}
|
|
|
return `color: ${this.btnSelected ? (this.viewOption.selectedColor || color) : color};`;
|
|
|
},
|
|
|
iconClass() {
|
|
|
if (this.actionClass) {
|
|
|
return this.actionClass;
|
|
|
}
|
|
|
if (classMap[this.type]) {
|
|
|
const currentClass = classMap[this.type];
|
|
|
|
|
|
if (this.viewOption.selected) {
|
|
|
this.btnSelected = true;
|
|
|
|
|
|
return this._icon.selected || this._icon.default;
|
|
|
return currentClass.selected || currentClass.default;
|
|
|
}
|
|
|
|
|
|
return this._icon.default;
|
|
|
},
|
|
|
iconStyle() {
|
|
|
return `font-size: ${this.pxToRem(this.viewOption.iconFontSize)};`;
|
|
|
},
|
|
|
textStyle() {
|
|
|
let style = `font-size: ${this.pxToRem(this.viewOption.textFontSize)};`;
|
|
|
|
|
|
let textAlign = this.viewOption.textAlign;
|
|
|
|
|
|
if (['top', 'bottom'].indexOf(textAlign) >= 0) {
|
|
|
style += ` vertical-align: ${textAlign};`;
|
|
|
}
|
|
|
return currentClass.default;
|
|
|
}
|
|
|
},
|
|
|
iconStyle() {
|
|
|
return `font-size: ${this.pxToRem(this.viewOption.iconFontSize)};`;
|
|
|
},
|
|
|
textStyle() {
|
|
|
let style = `font-size: ${this.pxToRem(this.viewOption.textFontSize)};`;
|
|
|
|
|
|
let textZoom = this.viewOption.textZoom;
|
|
|
let textAlign = this.viewOption.textAlign;
|
|
|
|
|
|
if (Number(textZoom) !== NaN) {
|
|
|
style += ` transform: scale(${textZoom}, ${textZoom});`
|
|
|
}
|
|
|
if (['top', 'bottom'].indexOf(textAlign) >= 0) {
|
|
|
style += ` vertical-align: ${textAlign};`;
|
|
|
}
|
|
|
|
|
|
if (this.viewOption.textSelectedColor) {
|
|
|
style += ` color: ${this.viewOption.textSelectedColor};`
|
|
|
}
|
|
|
let textZoom = this.viewOption.textZoom;
|
|
|
|
|
|
return style;
|
|
|
},
|
|
|
viewText() {
|
|
|
return `${(this.editText === null ? this.text : this.editText)}`;
|
|
|
},
|
|
|
placText() {
|
|
|
if (!isNaN(Number(this.text)) && this.viewText.length) {
|
|
|
return fill(Array(this.viewText.length), 0).join('');
|
|
|
} else {
|
|
|
return `${this.text}`;
|
|
|
}
|
|
|
if (Number(textZoom) !== NaN) {
|
|
|
style += ` transform: scale(${textZoom}, ${textZoom});`
|
|
|
}
|
|
|
},
|
|
|
methods: {
|
|
|
...mapActions(['followArticle', 'praiseArticle', 'praiseComment']),
|
|
|
pxToRem(px) {
|
|
|
const rootValue = 40;
|
|
|
|
|
|
if (typeof px !== 'number') {
|
|
|
px = parseInt(`0${px}`);
|
|
|
}
|
|
|
|
|
|
if (px > 2) {
|
|
|
return (px / rootValue).toFixed(2) + 'rem';
|
|
|
} else {
|
|
|
return px + 'px';
|
|
|
}
|
|
|
},
|
|
|
changeBtnStatus() {
|
|
|
this.btnSelected = !this.btnSelected;
|
|
|
if (this.viewOption.textSelectedColor) {
|
|
|
style += ` color: ${this.viewOption.textSelectedColor};`
|
|
|
}
|
|
|
|
|
|
if (this.viewOption.textAutoChange) {
|
|
|
let _text = this.editText || this.text;
|
|
|
return style;
|
|
|
},
|
|
|
viewText() {
|
|
|
return `${(this.editText === null ? this.text : this.editText)}`;
|
|
|
},
|
|
|
placText() {
|
|
|
if (!isNaN(Number(this.text)) && this.viewText.length) {
|
|
|
return fill(Array(this.viewText.length), 0).join('');
|
|
|
} else {
|
|
|
return `${this.text}`;
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
methods: {
|
|
|
...mapActions(['followArticle', 'praiseArticle', 'praiseComment']),
|
|
|
pxToRem(px) {
|
|
|
const rootValue = 40;
|
|
|
|
|
|
if (typeof px !== 'number') {
|
|
|
px = parseInt(`0${px}`, 10);
|
|
|
}
|
|
|
|
|
|
if (!isNaN(Number(this.viewText))) {
|
|
|
this.editText = Number(this.viewText) + (this.btnSelected ? 1 : -1);
|
|
|
}
|
|
|
}
|
|
|
if (px > 2) {
|
|
|
return (px / rootValue).toFixed(2) + 'rem';
|
|
|
} else {
|
|
|
return px + 'px';
|
|
|
}
|
|
|
},
|
|
|
changeBtnStatus() {
|
|
|
this.btnSelected = !this.btnSelected;
|
|
|
|
|
|
if (this._icon.selected) {
|
|
|
this.actionClass = this.btnSelected ? this._icon.selected : this._icon.default;
|
|
|
}
|
|
|
},
|
|
|
syncService(type, data) {
|
|
|
if (typeof this[type] === 'function') {
|
|
|
return this[type](data);
|
|
|
} else {
|
|
|
return Promise.resolve({code: 404});
|
|
|
}
|
|
|
},
|
|
|
onClick(evt) {
|
|
|
if (this.syncing) {
|
|
|
return;
|
|
|
if (this.viewOption.textAutoChange) {
|
|
|
if (!isNaN(Number(this.viewText))) {
|
|
|
this.editText = Number(this.viewText) + (this.btnSelected ? 1 : -1);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (this.viewOption.canSelect) {
|
|
|
this.changeBtnStatus();
|
|
|
|
|
|
let syncFnName = '';
|
|
|
if (classMap[this.type]) {
|
|
|
this.actionClass = this.btnSelected ? classMap[this.type].selected : classMap[this.type].default;
|
|
|
}
|
|
|
},
|
|
|
syncService(type, data) {
|
|
|
if (typeof this[type] === 'function') {
|
|
|
return this[type](data);
|
|
|
} else {
|
|
|
return Promise.resolve({code: 404});
|
|
|
}
|
|
|
},
|
|
|
onClick(evt) {
|
|
|
if (this.syncing) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
if (this.articleId) {
|
|
|
if (this._type === 'fav') {
|
|
|
syncFnName = 'praiseArticle';
|
|
|
} else if (this._type === 'star') {
|
|
|
syncFnName = 'followArticle';
|
|
|
}
|
|
|
} else if (this.commentId && this._type === 'fav') {
|
|
|
syncFnName = 'praiseComment';
|
|
|
if (this.viewOption.canSelect) {
|
|
|
this.changeBtnStatus();
|
|
|
const backFn = (res) => {
|
|
|
this.syncing = false;
|
|
|
|
|
|
if (res.code !== 200) {
|
|
|
this.changeBtnStatus();
|
|
|
this.$createToast && this.$createToast({
|
|
|
txt: res.message || '服务器开小差了',
|
|
|
type: 'warn',
|
|
|
time: 1000
|
|
|
}).show();
|
|
|
}
|
|
|
};
|
|
|
|
|
|
if (syncFnName) {
|
|
|
if (!this.auth()) {
|
|
|
return this.changeBtnStatus();
|
|
|
}
|
|
|
|
|
|
const backFn = (res) => {
|
|
|
this.syncing = false;
|
|
|
|
|
|
if (res.code !== 200) {
|
|
|
this.changeBtnStatus();
|
|
|
|
|
|
if (res.code === 401) {
|
|
|
// TODO go_login
|
|
|
} else {
|
|
|
this.$createToast && this.$createToast({
|
|
|
txt: res.message || '服务器开小差了',
|
|
|
type: 'warn',
|
|
|
time: 1000
|
|
|
}).show();
|
|
|
}
|
|
|
}
|
|
|
};
|
|
|
this.syncing = true;
|
|
|
|
|
|
this.syncing = true;
|
|
|
|
|
|
this.syncService(syncFnName, {
|
|
|
articleId: this.articleId,
|
|
|
commentId: this.commentId,
|
|
|
status: this.btnSelected
|
|
|
}).then(backFn).catch(backFn);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
this.viewOption.emitName && this.$emit(this.viewOption.emitName, evt);
|
|
|
this.syncService(this.syncFnName, {
|
|
|
articleId: this.articleId,
|
|
|
commentId: this.commentId,
|
|
|
status: this.btnSelected
|
|
|
}).then(backFn).catch(backFn);
|
|
|
}
|
|
|
},
|
|
|
};
|
|
|
|
|
|
this.viewOption.emitName && this.$emit(this.viewOption.emitName, evt);
|
|
|
}
|
|
|
},
|
|
|
};
|
|
|
</script>
|
|
|
|
|
|
<style type="scss">
|
...
|
...
|
|