一、 css样式
1、划线价格
. 利用text-decoration来实现
text-decoration: line-through red; /*红色中划线*/
属性 | 释义 |
---|---|
none | 默认。定义标准的文本。 |
underline | 定义文本下的一条线。 ͟h͟e͟l͟l͟o͟ |
overline | 定义文本上的一条线。 |
line-through | 定义穿过文本的一条线(划线价格使用这个) ̶h̶e̶l̶l̶o̶ |
blink | 定义闪烁的文本 |
inherit | 规定应该从父元素继承 text-decoration 属性的值 |
2.less、sass、stylus 区别和变量使用
他们都是css的预处理语言,将css赋予了动态语言的特性,如:变量、继承、运算、函数。但是被浏览器直接识别不了,所以要用工具来编译(命令行工具、gulp、webpack、考拉less现在不用了的)
3、小程序背景和uniapp背景
小程序背景写在page,不影响滚动。
uniapp写在最外面的盒子给了背景不给高度,在一些留白很多的页面就无法全部展示,如果写了100vh就无法滚动
小程序
page{
background:#ccc;
}
uniapp
page{
background:#ccc;
width:100vw;
height:100vh;
position:fixed;
z-index:-1;
}
4、小程序radio、checkbox、input、textarea默认样式修改。
checkbox 圆形,不点击是灰色的框,填充是亮红色。#FE2C55
/*checkbox 选项框大小 */
checkbox .wx-checkbox-input {
width: 34rpx;
height: 34rpx;
border-radius: 50%;
border: none;
border: 1rpx solid #ccc;
}
/*checkbox选中后样式 */
checkbox .wx-checkbox-input.wx-checkbox-input-checked {
background: #FE2C55;
}
/*checkbox选中后图标样式 */
checkbox .wx-checkbox-input.wx-checkbox-input-checked::before {
width: 28rpx;
height: 28rpx;
line-height: 28rpx;
text-align: center;
font-size: 22rpx;
color: #fff;
background: transparent;
transform: translate(-50%, -50%) scale(1);
-webkit-transform: translate(-50%, -50%) scale(1);
}
radio 圆形,不点击是灰色的框,填充是亮红色。#FE2C55
radio .wx-radio-input {
border-radius: 50%;
width: 20px;
height: 20px;
}
radio .wx-radio-input.wx-radio-input-checked {
border-color: #F0302F !important;
background: #F0302F !important;
}
radio .wx-radio-input.wx-radio-input-checked::before {
border-radius: 50%;
width: 20px;
height: 20px;
line-height: 20px;
text-align: center;
font-size: 15px;
color: #fff;
background: transparent;
transform: translate(-50%, -50%) scale(1);
-webkit-transform: translate(-50%, -50%) scale(1);
}
input 修改placeholder的样式
placeholder-style="color:#AAAAAA;"
或者
input::-webkit-input-placeholder {
/* placeholder颜色 */
color: #ff0000;
/* placeholder字号 */
font-size: 0.14rem;
}
input::-moz-placeholder {
/* Mozilla Firefox 19+ */
color: #ff0000;
font-size: 0.14rem;
}
input:-moz-placeholder {
/* Mozilla Firefox 4 to 18 */
color: #ff0000;
font-size: 0.14rem;
}
input:-ms-input-placeholder {
/* Internet Explorer 10-11 */
color: #ff0000;
font-size: 0.14rem;
}
textarea 修改placeholder的样式
textarea::-webkit-input-placeholder {
/* WebKit browsers */
/* placeholder颜色 */
color: #ff0000;
/* placeholder字号 */
font-size: 0.14rem;
}
textarea:-moz-placeholder {
/* Mozilla Firefox 4 to 18 */
color: #ff0000;
font-size: 0.14rem;
}
textarea::-moz-placeholder {
/* Mozilla Firefox 19+ */
color: #ff0000;
font-size: 0.14rem;
}
textarea::-ms-input-placeholder {
/* Internet Explorer 10+ */
color: #ff0000;
font-size: 0.14rem;
}
5、小程序多行省略
小程序直接在css就可以设置。
word-break: break-all;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;//这个就是行数
overflow: hidden;
6、flex和grid详解
7、px,repx单位区别
8、命名方式
(1)大驼峰 SingleLady 两个单词第一个字母都大写 类名用这个
(2)小驼峰 singleLady 第一个单词的首字母小写 方法名、参数名、成员变量、局部变量
(3)蛇形命名 single_lady 用 “_” 连接 测试方法名、常量、枚举名称
(4)串式命名 single-lady 用 “—” 连接 文件名字
二、方法
1.正则表达式校验
正则表达生成器 www.w3cschool.cn/tools/index…
教程:www.runoob.com/regexp/rege…
使用:正则表达式.test(检测值)
a、 校验非0正整数(价格单位为分的校验)
可以输入0
/(^[1-9]\d*$)/
不能输入0
/^0*/g
b、 校验非0数包括小数(适合价格校验,小数不超过两位)
/^(([1-9][0-9]*)|(([0].\d{1,2}|[1-9][0-9]*.\d{1,2})))$/
c、替换输入的emoji(小程序输入商品名字替换表情)
replace(/[\uD83C|\uD83D|\uD83E][\uDC00-\uDFFF][\u200D|\uFE0F]|[\uD83C|\uD83D|\uD83E][\uDC00-\uDFFF]|[0-9|*|#]\uFE0F\u20E3|[0-9|#]\u20E3|[\u203C-\u3299]\uFE0F\u200D|[\u203C-\u3299]\uFE0F|[\u2122-\u2B55]|\u303D|[\A9|\AE]\u3030|\uA9|\uAE|\u3030/gi , "")
c、手机号校验
这个校验不包含11 、12、16、开头的需要自己加。
/^((13[0-9])|(14[0-9])|(15([0-9]))|(18[0-9])|(17[0-9])|(19[0-9]))\d{8}$/
2、vue2-input直接校验是否输入正整数、前后去除空格
input直接校验是否输入正整数。
@input="(val)=>{establishData.stockNum = val.replace(/[^\d]/g,'')}"
input前后去除空格 v-model.trim
<el-input v-model.trim="establishData.stockNum " @input="(val)=>{establishData.stockNum = val.replace(/[^\d]/g,'')}" type="number" autocomplete="off"
placeholder="请输入库存总数"
style="width: 80px">
</el-input>份
vue2里面v-model有内置修饰符
修饰符 | 释义 |
---|---|
.trim | 去除前后空格 |
.number | 将值处理成number类型,并不影响你输入汉字 |
.lazy | 让值在change时更新而不是input |
自定义 | v2.cn.vuejs.org/v2/guide/co… |
装饰器是将vue2的一些方法改写,method等,为了配合使用ts。
3、时间获取
创建date对象new Date()
方法 | 释义 |
---|---|
getDay() | |
getFullYear() | 年 |
getMonth() | 月,要加一,少一个月 |
getDate() | 日 |
getHours() | 时 |
getMinutes() | 分 |
getSeconds() | 秒 |
getMilliseconds() | 毫秒 |
getTime() | 把时间格式转换成毫秒 |
toLocaleDateString() | 把date对象日期部分转成2025/5/27 |
toLocaleTimeString() | 把date对象时间部分转成 15:14:55 |
toLocaleString() | 把date对象转成 2025/5/27 15:16:09 |
toString() | Tue May 27 2025 15:16:54 GMT+0800 (中国标准时间) |
toTimeString() | 15:17:23 GMT+0800 (中国标准时间) |
toDateString() | Tue May 27 2025 |
toJSON() | 2025-05-27T07:17:57.481Z |
获取上个月的第一天和最后一天
function getFirstDay() {
//当前月第一天
var y = new Date().getFullYear(); //获取年份
var m = new Date().getMonth() + 1; //获取月份
var d = "01";
m = m < 10 ? "0" + m : m; //月份补 0
return [y, m, d].join("-");
}
function getLastDay() {
//当前月最后一天
var y = new Date().getFullYear(); //获取年份
var m = new Date().getMonth() + 1; //获取月份
var d = new Date(y, m, 0).getDate(); //获取当月最后一日
m = m < 10 ? "0" + m : m; //月份补 0
d = d < 10 ? "0" + d : d; //日数补 0
return [y, m, d].join("-");
}
function getLastMonthFirstDay() {
date = new Date();
date.setDate(0);
var y = date.getFullYear(); //获取年份
var m = date.getMonth() + 1; //获取月份
m = m < 10 ? "0" + m : m; //月份补 0
return [y, m, '01'].join("-");
}
function getLastMonthLastDay() {
date = new Date();
date.setDate(0);
var y = date.getFullYear(); //获取年份
var m = date.getMonth() + 1; //获取月份
var d = new Date(y, m, 0).getDate(); //获取当月最后一日
m = m < 10 ? "0" + m : m; //月份补 0
d = d < 10 ? "0" + d : d; //日数补 0
return [y, m, d].join("-");
}
获取本周、上周的第一天和最后一天。
本周
export function getThisWeekData() {//获得本周周一~周日的年月日
var thisweek = {};
var date = new Date();
// 本周一的日期
date.setDate(date.getDate() - date.getDay() + 1);
thisweek.start_day = date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate() ;
// 本周日的日期
date.setDate(date.getDate() + 6);
thisweek.end_day = date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate();
return thisweek
}
上周
export function getLastWeekData() {//获得上周周一~周日的年月日
var lastweek = {};
var date = new Date();
// 上周一的日期
date.setDate(date.getDate()-7 - date.getDay() + 1);
lastweek.start_day = date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate() ;
// 上周日的日期
date.setDate(date.getDate() +6);
lastweek.end_day = date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate();
return lastweek
}
4.防抖节流
/*函数节流*/
throttle(fn, interval) {
var enterTime = 0;//触发的时间
var gapTime = interval || 300 ;//间隔时间,如果interval不传,则默认300ms
return function() {
var context = this;
var backTime = new Date();//第一次函数return即触发的时间
if (backTime - enterTime > gapTime) {
fn.call(context,arguments);
enterTime = backTime;//赋值给第一次触发的时间,这样就保存了第二次触发的时间
}
};
},
/*函数防抖*/
debounce(fn, interval) {
var timer;
var gapTime = interval || 1000;//间隔时间,如果interval不传,则默认1000ms
return function() {
clearTimeout(timer);
var context = this;
var args = arguments;//保存此处的arguments,因为setTimeout是全局的,arguments不是防抖函数需要的。
timer = setTimeout(function() {
fn.call(context,args);
}, gapTime);
}
}
//用法
breakUp: util.throttle(function(msg){
函数内容
},10000),
5、vue2 el-date-picker 时间限制
<el-form-item label="活动时间" prop="time1">
<el-date-picker v-model="establishData.time1" type="daterange" :clearable="false" align="right"
range-separator="至" start-placeholder="活动开始时间" end-placeholder="活动结束时间"
:picker-options="pickerOptions"
value-format="yyyy-MM-dd"
format="yyyy-MM-dd">
</el-date-picker>
</el-form-item>
pickerOptions: {
disabledDate(time) {
return time.getTime() < Date.now()- 1 * 24 * 60 * 60 * 1000
}
},
三、 功能实现
1、小程序地图
1.使用地图和利用地图做过像链家一样得聚合和分开得效果,配合后端一起。
developers.weixin.qq.com/miniprogram…
我在项目进件助手里面使用到得
页面
<!-- 这一块才是地图 show-location-->
<map class="class-map" id="myMap" latitude="{{latitude}}" longitude="{{longitude}}" bindmarkertap="markertap"
bindcallouttap="calloutTap" bindregionchange="regionChange" markers="{{markers}}" scale="{{scale}}">
<view slot="callout">
<block wx:for="{{bubble}}" wx:key="*this">
<cover-view class="customCallout" marker-id="{{item.mapid}}">
<cover-view class="map-content">
</cover-view>
</cover-view>
</block>
</view>
</map>
逻辑
Page({
data: {
// 地图
mapbubble: false,
longitude: '',
latitude: '',
scale: 16,
bubble: [],
mapCtx: null,
markers: [],
// 筛选地区
address: '',
newVisit: '',
ajaxAddress: null,
chooseSignedTips: false,
closeAlert: false,
allLits: [], //最初的数据
notSigned: false, //未签约
signed: false, //已签约
signedBD: false, //bd
listInfo: [], //签约的店铺
noList: [], //没签约的
phone: null, //电话号码
visit: null, //visit
yan: true,
level: 4,
initlat: null,
initlong: null,
city: null
},
// 用省市区筛选信息
areaFiltering() {
request.authRequest({
url: config.api.host + config.sever.MERCHANT_STANDALONE + config.api.aMap + config.api.districtQuery + '/' + this.data.city,
method: 'GET'
}).then(res => {
let coordinate = res.districts[0].center.split(',')
setTimeout(() => {
this.setData({
latitude: coordinate[1],
longitude: coordinate[0],
scale: 15
})
})
})
request.authRequest({
url: config.api.host + config.sever.MERCHANT_STANDALONE + config.api.businessVisit + config.api.listInAreaByKeyWord + '/' + this.data.ajaxAddress,
method: 'GET',
}).then(res => {
if (res.length == 0) {
wx.showToast({
title: '没有数据',
icon: 'error'
})
} else {
this.setData({
allLits: res
})
// 数据格式
let arr = []
res.forEach((item, index) => {
// 蓝色
if (item.type == 0) {
arr.push({
id: index,
latitude: item.latitude,
longitude: item.longitude,
iconPath: '/image/la-blue.png',
alpha: 1,
width: 10,
height: 10,
callout: {
content: item.name,
color: '#FFFFFF',
fontSize: 12,
bgColor: '#2F5AFF',
padding: 6,
borderRadius: 8,
display: 'ALWAYS',
textAlign: 'center'
},
})
return
} else {
// 橙色
arr.push({
id: index,
latitude: item.latitude,
longitude: item.longitude,
iconPath: '/image/la-blue.png',
alpha: 1,
width: 10,
height: 10,
callout: {
content: item.name,
color: '#FFFFFF',
fontSize: 12,
bgColor: '#F27326',
padding: 6,
borderRadius: 8,
display: 'ALWAYS',
textAlign: 'center'
},
})
}
})
arr.push({
id: 9999999,
latitude: this.data.initlat,
longitude: this.data.initlong,
iconPath: '/image/zbzzz.png',
zIndex: 9999,
alpha: 1,
width: 28,
height: 28,
})
this.setData({
markers: arr
})
}
})
},
// 根据范围查询
coordinateQuery(e) {
let longitude = e.detail.centerLocation.longitude
let latitude = e.detail.centerLocation.latitude
let _this = this
let _level = _this.getLevel(e)
request.authRequest({
url: config.api.host + config.sever.ELASTIC_STANDALONE + config.api.esBusinessVisitMerge + config.api.listByLevel,
method: 'POST',
data: {
southwest: e.detail.region.southwest,
northeast: e.detail.region.northeast,
center: {
longitude,
latitude
},
level: _level
}
}).then(res => {
if(res){
let arr = []
res.forEach((item, index) => {
let _maker = {
id: null,
latitude: null,
longitude: null,
iconPath: '/image/la-blue.png',
alpha: 1,
width: 10,
height: 10,
callout: {
content: '',
color: '#FFFFFF',
fontSize: 12,
bgColor: '#2F5AFF',
borderRadius: 8,
padding: 6,
display: 'ALWAYS',
textAlign: 'center',
},
}
_maker.id = index
_maker.callout.content = item.name
_maker.latitude = item.latitude
_maker.longitude = item.longitude
_maker.storeId = item.id
if(_level==4){
// 蓝色
if(item.type==1){
_maker.type = 1
}else {
// 橙色已签约
_maker.iconPath = '/image/la-organe.png'
_maker.callout.bgColor = '#F27326'
_maker.type = 0
}
}else {
// 粉色
_maker.iconPath = '/image/la-organe.png'
_maker.callout.bgColor = '#FF7971'
_maker.type = 3
}
arr.push(_maker)
})
// 自己位置
arr.push({
id: 9999999,
latitude: this.data.initlat,
longitude: this.data.initlong,
iconPath: '/image/zbzzz.png',
zIndex: 9999,
alpha: 1,
width: 28,
height: 28,
})
_this.setData({
markers: arr,
yan: true,
allLits:res
})
}
})
},
// 上面的地区筛选器
pickerValue(e) {
this.setData({
address: e.detail.value[0] + e.detail.value[1] + e.detail.value[2],
ajaxAddress: e.detail.value[1] + '&' + e.detail.value[2],
city: e.detail.value[1]
})
this.areaFiltering()
},
// 获取当前经纬度
getAddress() {
let _this = this
wx.getLocation({
type: 'gcj02',
success(res) {
_this.setData({
latitude: res.latitude,
longitude: res.longitude,
initlat: res.latitude,
initlong: res.longitude
})
}
})
},
// 缩放获取坐标
regionChange(e) {
let _this = this
if(e.type=='end'){
this.setData({
yan: false
})
this.coordinateQuery(e)
}
},
/**
* 生命周期函数--监听页面加载
*/
getLevel(e){
let _level = 4
let _scale = e.detail.scale
if (_scale > 14) {
// 店
_level = 4
}
// 街道
if (_scale > 11 && _scale <= 14) {
_level = 3
}
// 区
if (_scale > 7 && _scale <= 11) {
_level = 2
}
// 市
if (_scale > 5 && _scale <= 7) {
_level = 1
}
// 省
if (_scale <= 5) {
_level = 0
}
return _level
},
// getRegion() {
// let _this = this
// return new Promise((resolve, rejects) => {
// _this.mapCtx.getRegion({
// success(res) {
// let _level = 4
// _this.mapCtx.getScale({
// success(res2) {
// if (res2.scale > 14) {
// // 店
// _level = 4
// _this.setData({
// level: 4
// })
// }
// // 街道
// if (res2.scale > 11 && res2.scale <= 14) {
// _level = 3
// _this.setData({
// level: 3
// })
// }
// // 区
// if (res2.scale > 7 && res2.scale <= 11) {
// _level = 2
// _this.setData({
// level: 2
// })
// }
// // 市
// if (res2.scale > 5 && res2.scale <= 7) {
// _level = 1
// _this.setData({
// level: 1
// })
// }
// // 省
// if (res2.scale <= 5) {
// _level = 0
// _this.setData({
// level: 0
// })
// }
// resolve({
// northeast: res.northeast,
// southwest: res.southwest,
// level: _level
// })
// }
// })
//
// }
// })
// })
// },
onLoad: function (options) {
let _this = this
this.getAddress()
this.data.markers.push({
id: 9999999,
latitude: this.data.initlat,
longitude: this.data.initlong,
iconPath: '/image/zbzzz.png',
zIndex: 9999,
alpha: 1,
width: 40,
height: 40,
})
this.setData({
markers: this.data.markers
})
// 获取map创建对象
this.mapCtx = wx.createMapContext('myMap')
},
// 点聚合
// pointAggregation() {
// this.mapCtx.initMarkerCluster({
// // initMarkerCluster: 1000
// })
// this.mapCtx.addMarkers({
// markers: this.data.markers,
// clear: true,
// complete(res) {
// }
// })
// },
// 缩放倍率
// getScale() {
// let _this = this
// _this.mapCtx.getScale({
// complete(res) {
// if (res.scale > 14) {
// // 店
// _this.setData({
// level: 4
// })
// return
// }
// // 街道
// if (res.scale > 11 && res.scale <= 14) {
// _this.setData({
// level: 3
// })
// return
// }
// // 区
// if (res.scale > 7 && res.scale <= 11) {
// _this.setData({
// level: 2
// })
// return
// }
// // 市
// if (res.scale > 5 && res.scale <= 7) {
// _this.setData({
// level: 1
// })
// return
// }
// // 省
// if (res.scale <= 5) {
// _this.setData({
// level: 0
// })
// return
// }
// _this.setData({
// scale: res.scale
// })
// }
// });
// },
})
样式
/* 地图 */
.map {
overflow: hidden;
position: relative;
top: 0;
left: 0;
height: 100%;
width: 100%;
z-index: 0;
}
.class-map {
height: 100%;
width: 100%;
}
/* 地图上面的气泡 */
.customCallout {
width: 154rpx;
height: 53rpx;
}
.map-content {
font-size: 24rpx;
font-family: Source Han Sans CN;
font-weight: 400;
text-align: center;
color: #FFFFFF;
line-height: 53rpx;
}
.mask-close>image {
width: 32rpx;
height: 32rpx;
}
2、小程序合成海报
a、 合成海报 利用canvas画图合成。用的组件合成返回
在主页面 主页面得canvas最好不要显示出来,画到视图之外。
<share-goods-poster id="goods-share" bind:changeCar="changeCar" open="{{open}}"></share-goods-poster>
<view class="right-item-a" bind:tap="shareStore">
逻辑
因为我们得项目不只一种分享得方式,所以有不同得type
// 联系商家分享按钮
shareStore() {
let type = 1
const storeShare = this.selectComponent('#goods-share')
storeShare.composeStoreShareImage(this, type, '商家分享')
},
组件得页面
<!-- 第一个绘制海报的遮罩层+canvas图片 -->
<block>
<view id="shared-graph2" class="shared-graph2" wx:if="{{showModalStatus}}" bindtap="_toggle"></view>
<view class="poster-share-box" bindtap="_toggle" wx:if="{{showModalStatus}}" catchtouchmove='true'>
<view class="image-box">
<image class="posterImage" wx:if="{{ posterImage && posterImage !== '' }}" mode="aspectFit"
src="{{ posterImage }}"></image>
<!-- <view wx:if="{{ posterImage && posterImage !== '' }}" class="iconfont icon-guanbi" bind:tap="hideModal"></view>-->
<view style="margin-top:20rpx;">
<image wx:if="{{ posterImage && posterImage !== '' }}" mode='aspectFit' bindtap="downloadPoster"
class="share-icon" src="https://thirdpartimg.51ishare.com/download.png"></image>
</view>
</view>
</view>
</block>
组件逻辑
// 门店分享 按钮
composeStoreShareImage(from, type,str) {
this.setData({
type: type,
str:str
})
let _this = this
this.setData({
from: from
}, () => {
let storeInfo = wx.getStorageSync('storeInfo')
_this._composeImage(storeInfo, params => {
if (_this.data.from) {
_this.data.from.setData({
shareBody: null
}, () => _this.data.from = null)
}
_this._toggle()
_this._cleanGoods()
_this._composeStoreSharePoster(storeInfo, from,str)
})
})
},
_composeImage(params, composeImage) {
composeImage(params)
},
// 绘制店铺海报图片
_composeStoreSharePoster(storeInfo, from,str) {
const self = this
const wxGetImageInfo = Poster.promisify(wx.getImageInfo)
const {
id
} = storeInfo
wx.showToast({
title: "正在合成海报中",
icon: "loading",
duration: 30000,
mask: true
})
// 这边得poster是一个方法
Poster.baseMap("https://thirdpartimg.51ishare.com/dituofstore.png", "shareCanvas", 1, self, from,str, () => {
// 合成店铺信息
Poster.drawStore(wxGetImageInfo, storeInfo, () => {
// 合成店铺商品二维码信息
Poster.drawBarCode(wxGetImageInfo, id, 0, "pages/index/index", () => {
// 开始绘制
Poster.draw(ctx => {
wx.hideToast()
wx.canvasToTempFilePath({
canvasId: 'shareCanvas',
success(res) {
self.setData({
posterImage: res.tempFilePath
}, () => {
ctx.draw()
})
},
fail(res) {
ctx.draw()
console.error(res)
}
})
})
})
})
})
},
封装得整个poster 只用一个就可以,按照自己得话,我们项目用到得情况比较多
import config from "./config";
const Poster = {
context: null,
promisify: api => {
return (options, ...params) => {
return new Promise((resolve, reject) => {
const extras = {
success: resolve,
fail: reject
}
api({
...options,
...extras
}, ...params)
})
}
},
//分享特价商品
drawEvent: function (baseMapPic, goods, canvasId) {
const wxGetImageInfo = Poster.promisify(wx.getImageInfo)
return new Promise((resolve, reject) => {
wxGetImageInfo({
src: goods.coverImage
}).then(resp => {
const ctx = wx.createCanvasContext(canvasId)
ctx.drawImage(resp.path, 15, 50, 80, 80)
ctx.drawImage(baseMapPic, 0, 0, 200, 160)
// 画价格
const price = goods.name
ctx.font = ' bold 20px Source Han Sans CN';
ctx.setFontSize(24)
ctx.setFillStyle("#FFEBAC");
ctx.fillText(price, 130, 70, 40)
// 画划线价
// var str = "¥" + goods.deductionAmount
// ctx.setFontSize(12)
// ctx.setFillStyle("#999999");
// ctx.fillText(str, 142, 90, 40)
// ctx.moveTo(140, 86)
// ctx.lineTo(163, 86)
// ctx.setStrokeStyle('#999999');
// ctx.stroke()
ctx.draw(false, () => {
wx.canvasToTempFilePath({
canvasId: canvasId,
success: function success(res1) {
wx.saveFile({
tempFilePath: res1.tempFilePath,
success: function success(res) {
resolve(res.savedFilePath)
}
});
}
});
})
})
})
},
// 绘制分享商品的图片
drawBase: function (baseMapPic, goods, canvasId) {
const wxGetImageInfo = Poster.promisify(wx.getImageInfo)
return new Promise((resolve, reject) => {
wxGetImageInfo({
src: goods.goodsImage
}).then(resp => {
const ctx = wx.createCanvasContext(canvasId)
ctx.drawImage(resp.path, 15, 50, 80, 80)
ctx.drawImage(baseMapPic, 0, 0, 200, 160)
// 画价格
const price = "¥" + goods.goodsPrice
ctx.font = 'normal bold 20px sans-serif';
ctx.setFontSize(24)
ctx.setFillStyle("#E21500");
ctx.fillText(price, 120, 70, 60)
// 画价格
ctx.draw(false, () => {
wx.canvasToTempFilePath({
canvasId: canvasId,
success: function success(res1) {
wx.saveFile({
tempFilePath: res1.tempFilePath,
success: function success(res) {
resolve(res.savedFilePath)
}
});
}
});
})
})
})
},
// 绘制分享的特价商品的图片(specOffer)
drawspec: function (baseMapPic, goods, canvasId) {
const wxGetImageInfo = Poster.promisify(wx.getImageInfo)
return new Promise((resolve, reject) => {
wxGetImageInfo({
src: goods.goodsImage
}).then(resp => {
const ctx = wx.createCanvasContext(canvasId)
ctx.drawImage(resp.path, 15, 50, 80, 80)
ctx.drawImage(baseMapPic, 0, 0, 200, 160)
// 画活动价格
const price = "¥" + goods.goodsPromotionPrice
ctx.font = 'normal bold 20px sans-serif';
ctx.setFontSize(14)
ctx.setFillStyle("#E21500");
ctx.fillText(price, 126, 86, 60)
// 画原价划线价格
var str = "¥" + goods.goodsPrice
ctx.setFontSize(10)
ctx.setFillStyle("#999999");
ctx.fillText(str, 130, 105, 60)
ctx.moveTo(128, 102)
ctx.lineTo(170, 102)
ctx.setStrokeStyle('#999999');
ctx.stroke()
ctx.draw(false, () => {
wx.canvasToTempFilePath({
canvasId: canvasId,
success: function success(res1) {
wx.saveFile({
tempFilePath: res1.tempFilePath,
success: function success(res) {
resolve(res.savedFilePath)
}
});
}
});
})
})
})
},
baseMap: (baseMapPic, canvasId, scale, self, from, str,callback) => {
const wxGetImageInfo = Poster.promisify(wx.getImageInfo)
wxGetImageInfo({
src: baseMapPic
}).then(resp => {
const ctx = wx.createCanvasContext(canvasId)
Poster.context = ctx
// 画布缩放
if(str){
resp.height=resp.height-130
}
if (from) {
from.setData({
style: 'zoom: ' + scale + '; height: ' + resp.height + 'px;'
}, () => {
self.setData({
baseMapWidth: resp.width,
baseMapHeight: resp.height
}, () => {
// 绘制底图
ctx.drawImage(resp.path, 0, 0, resp.width, resp.height)
ctx.save()
callback()
})
})
} else {
self.setData({
baseMapWidth: resp.width,
baseMapHeight: resp.height
}, () => {
// 绘制底图
ctx.drawImage(resp.path, 0, 0, resp.width, resp.height)
ctx.save()
callback()
})
}
}, error => {
console.error(error)
})
},
drawGoods: (wxGetImageInfo, goods, self, callback) => {
const ctx = Poster.context
wxGetImageInfo({
src: goods.goodsImage
}).then(resp => {
let left = (self.data.baseMapWidth - 400) / 2
// 商品活动
ctx.drawImage(resp.path, left, 175, 400, 400)
ctx.save()
let charArr = goods.goodsName.split('')
let temp = ""
let row = []
ctx.setFontSize(48)
ctx.setFillStyle("#333333")
for (let i = 0; i < charArr.length; i++) {
if (ctx.measureText(temp).width < 590) {
temp += charArr[i]
} else {
i--;
row.push(temp)
temp = ""
}
}
row.push(temp)
if (row.length > 2) {
let rowCut = row.slice(0, 2);
let rowPart = rowCut[1];
let test = "";
let empty = [];
for (let a = 0; a < rowPart.length; a++) {
if (ctx.measureText(test).width < 590) {
test += rowPart[a];
} else {
break;
}
}
empty.push(test);
//这里只显示两行,超出的用...表示
let group = empty[0] + "..."
rowCut.splice(1, 1, group);
row = rowCut;
}
for (let b = 0; b < row.length; b++) {
ctx.font = 'normal bold 48px Arial'
// 黑色的商品标题name
ctx.fillText(row[b], 80, 650 + b * 72, 590)
}
ctx.setFillStyle("#EA524A")
ctx.font = 'normal bold 32px sans-serif'
// 这个是红色¥
ctx.fillText('¥', 80, 650 + row.length * 72 + 22)
let unitWidth = ctx.measureText("¥").width;
ctx.font = 'normal bold 56px sans-serif'
var _goodsPromotionPrice = goods.goodsPrice
if (goods.goodsPromotionPrice) {
_goodsPromotionPrice = goods.goodsPromotionPrice
}
// 红色的价格
ctx.fillText(_goodsPromotionPrice, 80 + unitWidth, 650 + row.length * 72 + 22)
let memberPriceWidth = ctx.measureText("26.00").width
ctx.setFontSize(26)
ctx.setFillStyle("#999999")
// 灰色的价格
ctx.fillText("¥" + goods.goodsPrice, 96 + unitWidth + memberPriceWidth, 650 + row.length * 72 + 22)
let originPriceWidth = ctx.measureText('¥' + goods.goodsPrice).width
// 划线
ctx.setStrokeStyle('#999999')
ctx.beginPath()
// 灰色的划线价格的划线
ctx.moveTo(96 + unitWidth + memberPriceWidth, 650 + row.length * 72 + 13)
ctx.lineTo(96 + unitWidth + memberPriceWidth + originPriceWidth, 650 + row.length * 72 + 13)
ctx.stroke()
ctx.save()
callback()
})
},
drawBarCode: (wxGetImageInfo, storeId, goodsId, page, callback) => {
const ctx = Poster.context
const appId = wx.getAccountInfoSync().miniProgram.appId
const scene = storeId + (goodsId === 0 ? '' : '$' + goodsId)
let src = config.api.host + config.api.store + config.api.qrImage + config.api.fix + '?scene=' + scene + '&appId=' + appId + '&page=' + page
wxGetImageInfo({
src: src,
}, () => {
callback()
}).then(resp => {
if (goodsId === 0) {
// 这个是联系上商家分享的二维码图片
ctx.drawImage(resp.path, 566, 1020, 135, 135)
} else {
// 这个是商品详情分享的二维码图片
ctx.drawImage(resp.path, 566, 985, 135, 135)
}
ctx.save()
callback()
})
},
draw: (callback) => {
const ctx = Poster.context
ctx.draw(false, () => {
callback(ctx)
Poster.context = null
})
},
drawStore(wxGetImageInfo, storeInfo, callback) {
let _this = this
const {
name,
workBeginTime,
workEndTime,
logo,
orderNotifyTelephone
} = storeInfo
wxGetImageInfo({
src: logo
}).then(resp => {
const {
path,
width,
height
} = resp
const containerWidth = 670
const containerHeight = 377
const ctx = Poster.context
_this.drawImage(ctx, containerWidth, containerHeight, path, width, height, 40, 320)
ctx.save()
// 店铺名称
ctx.setFillStyle("#333333")
ctx.font = 'normal bold 36px sans-serif'
ctx.fillText(name, 80, 750)
// 营业时间
ctx.setFillStyle("#333333")
ctx.font = 'normal bold 28px sans-serif'
ctx.fillText(workBeginTime + '-' + workEndTime, 80, 860)
ctx.fillText(orderNotifyTelephone, 375, 860)
ctx.save()
callback()
})
},
drawImage(ctx, bg_w, bg_h, imgPath, imgWidth, imgHeight, x, y) {
let dWidth = bg_w / imgWidth; // canvas与图片的宽度比例
let dHeight = bg_h / imgHeight; // canvas与图片的高度比例
if (imgWidth > bg_w && imgHeight > bg_h || imgWidth < bg_w && imgHeight < bg_h) {
if (dWidth > dHeight) {
ctx.drawImage(imgPath, 0, (imgHeight - bg_h / dWidth) / 2, imgWidth, bg_h / dWidth, x, y, bg_w, bg_h)
} else {
ctx.drawImage(imgPath, (imgWidth - bg_w / dHeight) / 2, 0, bg_w / dHeight, imgHeight, x, y, bg_w, bg_h)
}
} else {
if (imgWidth < bg_w) {
ctx.drawImage(imgPath, 0, (imgHeight - bg_h / dWidth) / 2, imgWidth, bg_h / dWidth, x, y, bg_w, bg_h)
} else {
ctx.drawImage(imgPath, (imgWidth - bg_w / dHeight) / 2, 0, bg_w / dHeight, imgHeight, x, y, bg_w, bg_h)
}
}
}
};
export {
Poster
}
b、利用微信自带方法右上角点击分享,按照格式分享出去。 onShareAppMessage
developers.weixin.qq.com/miniprogram…
/**
* 用户点击右上角分享
*/
onShareAppMessage: function (e) {
if (e && e.from == 'button' && e.from === 'button' && this.data.shareBody) {
this.hideModal()
return this.data.shareBody
} else {
var _shareBody = app.globalData.shareBodyp
_shareBody.path = '/pages/index/index?storeId=' + wx.getStorageSync("storeId")
return _shareBody
}
},
c、利用button 按钮设置type 分享图片出去。
//利用button得open-type来打开分享
<button open-type="share">分享</button>
3.vue2-elm-下拉选择带远程模糊搜索
<el-select v-model="establishData.promotionPersonId"
filterable
remote
:remote-method="brandMethod"
:loading="brandLoading"
placeholder="请选择活动商户">
<el-option
v-for="item in select"
:key="item.promotionPersonId"
:label="item.personName"
:value="item.promotionPersonId">
</el-option>
</el-select>
query就是输入的模糊查询的关键字。不等于空的时候调接口,里面使用到的值自己在data声明一下。
brandMethod(query) {
if (query !== '') {
this.brandLoading = true
setTimeout(() => {
this.brandLoading = false
this.promotionMerchantList({
accountName: query,
pageIndex: 1,
pageSize: 1000,
}).then(res => {
this.loadingStore = false
const {
list
} = res
this.select = list
})
}, 200)
} else {
this.select = []
}
},
4.vue2-elm-时间选择
选择开始-结束时间
type daterange(不带时分秒的筛选) datetimerange(带时分秒的筛选) picker-options 设置快捷选项,近一个月,一周等等。 value-format设置值的格式 format展示格式
<el-date-picker
v-model="time"
type="daterange"
:clearable="false" align="right"
@change="showTime"
unlink-panels
range-separator="至" start-placeholder="订单开始日期"
end-placeholder="订单结束日期"
value-format="yyyy-MM-dd"
format="yyyy-MM-dd">
</el-date-picker>
5.vue2-elm-上传单张图片
页面:
<el-upload
class="avatar-uploader"
:action="upLoadUrl"
:show-file-list="false"
:headers="token"
:on-success="handleAvatarSuccess"
>
<img v-if="ruleForm.url" :src="ruleForm.url" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
引入依赖
import { upLoadUrl } from '@/common/js/util'
import { getToken } from '@/utils/auth'
依赖导出地址
export const upLoadUrl = 'http://xxx.168.2.3:8080/v1/resource/upload'
记得把token带上
export function getToken() {
return Cookies.get(TokenKey)
}
数据
upLoadUrl: upLoadUrl,
token: {
Authorization: 'Bearer ' + getToken()
},
change事件的时候拿到数据,等submit提交。
handleAvatarSuccess(res) {
this.ruleForm.url = res.url
},
6.vue2-elm-table表格里面的图片可以放大查看的
preview-src-list主要是这个属性
<el-table-column
label="背景图"
>
<template slot-scope="scope">
<el-image
style="width: 100px; height: 100px"
:src="scope.row.url"//当前图片
:preview-src-list="[scope.row.url,scope.row.url]"//接受数组
>
</el-image>
</template>
</el-table-column>
7. 穿梭框使用带搜索的。
a、这个是vue,elm自带的
html:弹窗里面使用的
<el-dialog
title="场次人员"
:visible.sync="isShowConfig"
width="40%"
:close-on-click-modal="false"
>
<el-transfer
filterable
:filter-method="filterMethod"
filter-placeholder="请输入名字"
v-model="chooseUserList"
:data="userList">
</el-transfer>
<span slot="footer" class="dialog-footer">
<el-button @click="close">取消</el-button>
<el-button type="primary" @click="submitFormUser()">确 定</el-button>
</span>
</el-dialog>
js
//data
userListInitial: [],
isShowConfig: false,
userList: [],
chooseUserList: [],
filterMethod(query, item) {
return item.label.indexOf(query) > -1;
}
请求数据
mounted() {
this.loadingBrand = true
this.getList()
user({
current: 1,
size: 100
}).then(res => {
if (res) {
res.content.map(item => {
this.userListInitial.push({
key: item.id,
label: item.username
})
})
}
})
// 注释这一条 自己造数据测试
this.userListInitial=[{
key:1,
label:'zsd'
},{
key:2,
label:'admin'
},{
key:3,
label:'阿什顿'
},{
key:4,
label:'iwej'
},{
key:5,
label:'是多么'
},{
key:6,
label:'ask你'
}]
this.userListInitial.map(item=>{
this.userList.push(item)
})
},
// 配置人员
personnelConfiguration() {
this.isShowConfig = true
},
//提交
submitFormUser() {
console.log(this.chooseUserList)
},
不知道为啥第一次高德就是不出现数据。但是第二次又好了,可能是哪里写错字了,下次发现再说。
b、vue2自制穿梭框,用两个table伪造。自己做模糊匹配。
数据操作会遇到问题,回显选中左侧数据会触发 @selection-change="handleSelectionChangeLeft"方法,只能加锁来控制数据不刷新。因为左边和右边选中的数据来自不同的表。 父组件
<el-button type="primary" @click="addChoseGoods">点击选择</el-button>
<!-- 选择商品-->
<AddGoodsActivity ref="choseGoods" @choseChildGoods="choseChildGoods"/>
//选择商品
addChoseGoods() {
this.$refs.choseGoods.open()
},
子组件
<template>
<div>
<el-dialog
title="商品"
:visible.sync="isShowAddGoods"
@opened="initTable"
width="1200px"
:before-close="handleClose"
:close-on-click-modal="false">
<div style="display: flex;justify-content: space-between">
<div style="width: 45%;height: 100%">
<h3>查看商品</h3>
<span style="display: flex;align-items: center;margin-bottom: 16px;">
<el-input
style="width: 150px;"
v-model="pageInfo.goodsName"
size="mini"
clearable
placeholder="输入商品名字搜索"/>
<el-input
style="width: 150px;margin-left: 10px"
v-model="pageInfo.goodsId"
size="mini"
clearable
placeholder="输入商品条码搜索"/>
<el-button style="margin-left: 10px" type="primary" size="mini" @click="searchGoods">查询</el-button>
<el-button style="margin-left: 10px" type="primary" size="mini" @click="restGoods">重置</el-button>
</span>
<el-table
:data="bindShopListParam"
stripe
border
height="500"
ref="multipleTable"
@selection-change="handleSelectionChangeLeft"
style="width: 100%;">
<el-table-column
type="selection"
width="55">
</el-table-column>
<el-table-column
label="商品名字"
prop="goodsName">
</el-table-column>
<el-table-column
label="商品条码"
prop="goodsId">
</el-table-column>
</el-table>
<div>
<el-pagination
@size-change="handleSizeChangeGoods" @current-change="handleCurrentChangeGoods"
:current-page="pageIndexGoods"
background
:page-sizes="[10, 20, 30, 40]"
:pager-count="5"
layout=" prev, pager, next,sizes"
:total="bindShopTotal"
style="margin-top:24px">
</el-pagination>
</div>
</div>
<div>
<div style="display: flex;align-items: center;margin-left: 50px ">
<h3>参与商品</h3>
</div>
<div style="height:500px;width:100%;overflow-y: auto;overflow-x: hidden" class="scrollClass">
<div style="display: flex;align-items: center">
<el-button style="margin-right: 12px" type="primary" size="mini" @click="goRight"> 向右边 </el-button>
<div style="width: 100%">
<div style="display: flex;justify-content: space-between;align-items: center">
<span style="display: flex;align-items: center;">
<el-input
style="margin: 12px 12px 12px 0"
v-model="goodsName"
size="mini"
clearable
placeholder="输入商品条码名称"/>
<el-input
style="margin: 12px 12px 12px 0"
v-model="goodsId"
size="mini"
clearable
placeholder="输入商品条码搜索"/>
<el-button style="margin-left: 10px" type="primary" size="mini"
@click="searchGoods1">查询</el-button>
<el-button style="margin-left: 10px" type="primary" size="mini" @click="restGoods1">重置</el-button>
</span>
</div>
<el-table
:data="choseLeftList"
stripe
border
height="440px"
style="width:500px;">
<el-table-column
label="商品名字" prop="goodsName">
</el-table-column>
<el-table-column
label="商品条码"
prop="goodsId">
</el-table-column>
<el-table-column label="金额/元" width="180">
<template slot-scope="scope">
<div style="display: flex;align-items: center;">
<span style="color:#be0c0c;margin-right: 5px;">*</span>
<el-input v-model.trim="scope.row.goodsPrice"
@input="(val)=>{scope.row.goodsPrice= val.replace(/[^\d]/g,'')}"
type="number" autocomplete="off"
placeholder="请输入"
maxlength="9"
style="width: 100px"/>分
</div>
</template>
</el-table-column>
<el-table-column
label="操作"
width="80px"
fixed="right">
<template slot-scope="scope">
<el-button style="margin-right: 10px" type="danger" size="mini"
@click="deleteNewGoods(scope.row)">删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
</div>
</div>
</div>
</div>
<div style="text-align: right;padding-top:10px">
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="sureClick">确 定</el-button>
</span>
</div>
</el-dialog>
</div>
</template>
<script>
import {placeGoodsList} from '@/api'
import {mixins} from '@/layout/mixin/commonExport'
export default {
name: 'addGoods',
mixins: [mixins],
data() {
return {
pageInfo: {
goodsId: '',
pageSize: 10,
pageIndex: 1,
goodsName: ''
},
goodsName: '',
goodsId: '',
choseLeftList: [],
choseLeftListAll: [],
isShowAddGoods: false,
rowSelectFlag: false,
total: 0,
pageSizeGoods: 10,
pageIndexGoods: 1,
bindShopTotal: 0,
bindShopListParam: [],
arr: [],
}
},
mounted() {
this.bindGoodsInfo()
},
methods: {
deleteNewGoods(row) {
let _this = this
this.$nextTick(() => {
this.$refs.multipleTable.selection.forEach((select, index) => {
if (select.goodsId === row.goodsId) {
_this.$refs.multipleTable.toggleRowSelection(select, false);
}
})
})
this.choseLeftList = this.choseLeftListAll.filter((item) => {
return item.goodsId !== row.goodsId
})
this.choseLeftListAll = this.choseLeftListAll.filter((item) => {
return item.goodsId !== row.goodsId
})
},
newBindGoodsInfo(arr) {
arr.forEach(item=>{
item.goodsPrice = Number(item.goodsPrice)
})
this.choseLeftListAll = arr
this.choseLeftList = arr
},
initTable(){
this.$nextTick(() => {
this.rowSelectFlag = true
this.bindShopListParam.forEach(item => {
this.choseLeftList.forEach(items=>{
if (items.goodsId === item.goodsId) {
this.$refs.multipleTable.toggleRowSelection(item, true);
}
})
})
this.rowSelectFlag = false
})
},
open() {
this.isShowAddGoods = true
},
goRight() {
this.choseLeftList.forEach(item=>{
this.choseLeftListAll.forEach(items=>{
if(item.goodsId === items.goodsId){
items.goodsPrice = item.goodsPrice
}
})
})
this.choseLeftList = this.choseLeftListAll
},
searchGoods() {
this.pageInfo.pageIndex = 1
this.bindGoodsInfo()
},
restGoods() {
this.pageInfo.pageIndex = 1
this.pageInfo = {
goodsName: '',
goodsId: ''
}
this.bindGoodsInfo()
},
searchGoods1() {
if (this.goodsName !== '') {
this.choseLeftList = this.choseLeftList.filter(item => {
return item.goodsName.indexOf(this.goodsName) !== -1
})
}
if (this.goodsId !== '') {
this.choseLeftList = this.choseLeftList.filter(item => {
return item.goodsId.indexOf(this.goodsId) !== -1
})
}
},
restGoods1() {
this.goodsName = ''
this.goodsId = ''
this.choseLeftList = this.choseLeftListAll
},
handleSizeChangeGoods(val) {
this.pageSizeGoods = val
this.bindGoodsInfo()
},
handleCurrentChangeGoods(val) {
this.pageIndexGoods = val
this.bindGoodsInfo()
},
bindGoodsInfo() {
placeGoodsList(this.pageInfo).then(res => {
const {
list,
count
} = res
this.bindShopListParam = list
this.bindShopTotal = count
})
},
handleSelectionChangeLeft(list) {
if (this.rowSelectFlag) return
this.choseLeftListAll = list
},
sureClick() {
if (this.choseLeftListAll.length === 0) {
this.$message.error('请选择商品')
return
}
var arr = this.choseLeftListAll.filter(item => {
if (item.goodsPrice && item.goodsPrice !== '' && item.goodsPrice !== null && item.goodsPrice !== ' ') {
} else {
return item
}
})
if (arr.length !== 0) {
this.$message.error('请输入金额')
} else {
this.choseLeftListAll.forEach(item => {
this.arr.push({
goodsId: item.goodsId,
goodsPrice: item.goodsPrice ? item.goodsPrice : null
})
})
this.$emit('choseChildGoods', this.arr)
setTimeout(() => {
this.isShowAddGoods = false
}, 100)
}
},
handleClose() {
this.pageInfo = {
goodsId: '',
goodsName: ''
}
this.goodsName =''
this.goodsId =''
this.isShowAddGoods = false
this.choseLeftList = []
this.choseLeftListAll = []
this.$refs.multipleTable.clearSelection()
},
}
}
</script>
<style scoped>
/deep/ .el-transfer-panel {
width: 400px;
}
</style>
8.vue2-elm-富文本
<el-card style="height: 610px;">
<quill-editor v-model="ruleForm.description" ref="myQuillEditor" style="height: 500px;"
class="bottom-jianxi" :options="editorOption">
</quill-editor>
</el-card>
import {
quillEditor
} from 'vue-quill-editor'
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import 'quill/dist/quill.bubble.css'
components: {
quillEditor
},
在components文件夹创建ue.vue组件,如下
<!-- 组件代码如下 -->
<template>
<div>
<script id="editor" type="text/plain"></script>
</div>
</template>
<script>
export default {
name: 'UE',
data () {
return {
editor: null
}
},
props: {
defaultMsg: {
type: String
},
config: {
type: Object
}
},
mounted () {
const _this = this
// eslint-disable-next-line no-undef
this.editor = UE.getEditor('editor', this.config) // 初始化UE
this.editor.addListener('ready', function () {
_this.editor.setContent(_this.defaultMsg) // 确保UE加载完成后,放入内容。
})
},
methods: {
getUEContent () { // 获取内容方法
return this.editor.getContent()
}
},
destroyed () {
this.editor.destroy()
}
}
</script>