Commit a3afdbf2 authored by 秦海涛's avatar 秦海涛

init:b端模板

parent 3868b85e
import request from './utils/request';
const API = {
// 上传数据生成xslx文件 uploadDataCreateFile
uploadDataCreateFile: params => request('exportAwardsList', 'POST', params),
// 保存活动配置 saveActivityInfo
saveActivityInfo: params => request('saveActivityInfo', 'POST', params),
// 删除活动 delActivity
delActivity: params => request('delActivity', 'POST', params),
// 授权信息保存 sellerSave
sellerSave: params => request('sellerSave', 'POST', params),
// 生成活动规则 generateRule
generateRule: params => request('generateRule', 'POST', params),
// 获取中奖名单 findWinnerInfoList
findWinnerInfoList: params => request('findWinnerInfoList', 'POST', params),
// 获取活动列表云函数 getActivityList
getActivityList: params => request('getActivityList', 'POST', params),
// 获取活动配置 getActivityDetail
getActivityDetail: params => request('getActivityDetail', 'POST', params),
// 获取淘宝店家商品 findItemListByStatus
findItemListByStatus: params => request('findItemListByStatus', 'POST', params, {
isShowLoading: true
}),
// 通过id去查找商品 findItemListByIds
findItemListByIds: params => request('findItemListByIds', 'POST', params, {
isShowLoading: true
}),
// 通过权益ename查询商品 queryBenefitByEname
queryBenefitByEname: params => request('queryBenefitByEname', 'POST', params),
};
export default API;
\ No newline at end of file
page {
background: #f7f7f7;
min-height: 100vh;
}
.adaptive-framework {
min-height: calc(100vh - 50px);
}
\ No newline at end of file
import cloud from '@tbmp/mp-cloud-sdk';
import { env } from './config';
cloud.init({
env,
});
App({
cloud,
onLaunch(options) {
console.info('App onLaunch');
},
onShow(options) {
// 从后台被 scheme 重新打开
// options.query == {number:1}
},
});
\ No newline at end of file
{
"pages": [
"pages/index/index"
],
"window": {
"defaultTitle": "xxxxx店铺商家后台"
},
"plugins": {
"myPlugin": {
"version": "*",
"provider": "3000000002026202"
}
}
}
.db-dialog-content {
width: 515px;
background-color: #fff;
}
.db-dialog-title {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 16px;
font-weight: bold;
border-bottom: 1px solid rgba(215,219,224,1);
height: 60px;
padding: 0 24px;
}
.db-dialog-content_inner {
/* width: 100%; */
padding: 24px;
}
.db-dialog-footer {
display: flex;
justify-content: flex-end;
border-top: 1px solid rgba(215,219,224,1);
}
.db-dialog-btns {
padding: 12px;
}
.db-dialog-btns Button {
margin-right: 15px;
}
\ No newline at end of file
<overlay
visible="{{ visible }}"
hasMask="{{true}}"
triggerType="click"
wrapperClassName="overlay"
disableScroll
target=".edit-breadcrumb"
data-index="1"
canCloseByMask="{{true}}"
>
<view class="db-dialog-content" style="{{ { width: (width + 'px').replace('pxpx', 'px') } }}">
<view class="db-dialog-title">{{title}}<icon size="xs" type="close" onTap="closeDialog" style="color: #999999;" /></view>
<view class="db-dialog-content_inner">
<slot/>
</view>
<view class="db-dialog-footer" a:if="{{hasFooter}}">
<view class="db-dialog-btns">
<button type="primary">确定</button>
<button onTap="closeDialog">取消</button>
</view>
</view>
</view>
</overlay>
\ No newline at end of file
Component({
mixins: [],
data: {},
props: {
title: '',
visible: false,
onClose: () => {}
},
didMount() {},
didUpdate() {},
didUnmount() {},
methods: {
closeDialog() {
this.props.onClose()
}
},
});
{
"component": true
}
\ No newline at end of file
.db-layout {
padding: 25px 75px;
display: flex;
}
.db-content {
flex:1;
width: 100%;
margin: 0 0 0 25px;
}
\ No newline at end of file
<view class="db-layout">
<side-bar
menu="{{menu}}"
info="{{info}}"
defaultActiveKey="{{defaultActiveKey}}"
activeKey="{{activeKey}}"
onMenuChange="onSideBarMenuChange"
/>
<view class="db-content">
<slot/>
</view>
</view>
\ No newline at end of file
Component({
mixins: [],
data: {},
props: {
menu: () => [],
info: () => ({}),
activeKey: '',
onMenuChange: () => {
}
},
didUpdate() {},
didUnmount() {},
methods: {
onSideBarMenuChange({ key, path }) {
this.props.onMenuChange({ key, path })
}
},
});
{
"component": true,
"usingComponents": {
"side-bar": "/components/base/side-bar/side-bar"
}
}
\ No newline at end of file
.db-side-bar {
width: 180px;
min-width: 180px;
min-height: calc(100vh - 50px);
background-color: #fff;
}
.db-infos {
width: 100%;
height: 144px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.db-logo {
width: 40px;
height: 46px;
}
.db-company-name {
font-size: 14px;
font-weight: bold;
text-align: center;
margin: 18px 0 0 0;
}
.db-menu-list {
height:48px;
color:rgba(51,134,241,1);
line-height:48px;
text-align: center;
font-size: 14px;
}
.db-menu-list_active {
background:rgba(235,243,255,1);
}
\ No newline at end of file
<view class="db-side-bar">
<view class="db-infos">
<image class="db-logo" src="{{info.logo}}" />
<text class="db-company-name">{{info.company}}</text>
</view>
<view>
<view onTap="handleMenuClick" data-key="{{item.key}}" data-path="{{item.path}}" class="{{item.key === activeKey ? `db-menu-list db-menu-list_active`: `db-menu-list`}}" a:for="{{menu}}" a:key="*this">
<text>{{item.title}}</text>
</view>
</view>
</view>
\ No newline at end of file
Component({
mixins: [],
data: {},
props: {
info: () => {
return {
company: '兑吧科技',
miniappName: '兑吧科技',
logo: '//yun.duiba.com.cn/duiba_miniProgram/logo.png'
}
},
activeKey: '',
onMenuChange: () => {}
},
didMount() {},
didUpdate() {},
didUnmount() {},
methods: {
handleMenuClick(event) {
let { key, path } = event.target.dataset;
this.props.onMenuChange({ key, path });
}
},
});
{
"component": true
}
\ No newline at end of file
.confirm-dialog-content {
width: 515px!important;
min-height: 225px;
background-color: #fff;
}
.confirm-dialog-title {
color:rgba(51,51,51,1);
font-size: 16px;
height: 60px;
padding: 0 24px;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid rgba(215,219,224,1);
font-weight: bold;
}
.confirm-dialog-tip {
height: 100px;
padding: 33px 24px 0 24px;
font-size: 12px;
}
.confirm-dialog-tip_inner {
display: flex;
align-items: center;
}
.close-icon {
font-size: 16px;
cursor: pointer;
}
.confirm-dialog-footer {
display: flex;
justify-content: flex-end;
border-top: 1px solid rgba(215,219,224,1);
}
.confirm-dialog-btns {
padding: 12px;
}
.confirm-dialog-btns Button {
margin-right: 15px;
}
\ No newline at end of file
<overlay
visible="{{ visible }}"
hasMask="{{true}}"
triggerType="click"
wrapperClassName="overlay"
disableScroll
target=".edit-breadcrumb"
data-index="1"
canCloseByMask="{{true}}"
>
<view class="confirm-dialog-content">
<view class="confirm-dialog-title">提示<icon size="xs" type="close" onTap="closeDialog" style="color: #999999;" /></view>
<view class="confirm-dialog-tip">
<view class="confirm-dialog-tip_inner">
<icon size="xs" type="warning" onTap="closeDialog" style="color: #FBA628;margin-right: 4px;" />
<text>{{content}}</text>
</view>
</view>
<view class="confirm-dialog-footer">
<view class="confirm-dialog-btns">
<button type="primary" onTap="confirmDialog">确定</button>
<button onTap="closeDialog">取消</button>
</view>
</view>
</view>
</overlay>
\ No newline at end of file
Component({
mixins: [],
data: {},
props: {
content: '',
visible: false,
onClose: () => {},
onConfirm: () => {}
},
didMount() {},
didUpdate() {},
didUnmount() {},
methods: {
closeDialog() {
this.props.onClose("link");
},
confirmDialog() {
this.props.onConfirm();
}
},
});
{
"component": true
}
\ No newline at end of file
.modal-content-imageUpload-wrap{
display:flex;
padding-top:10px;
box-sizing:border-box;
}
.modal-content-imageUpload-btn view{
margin-bottom:5px;
color:#666666
}
.edit-content-formitem-choosePrize-tips-success{
color:#71B204 !important;
padding-top:20px;
font-size:14px;
}
.edit-content-formitem-choosePrize-tips-fail{
color:#F23C3C !important;
padding-top:20px;
font-size:14px;
}
.edit-content-formitem-choosePrize-wrap{
display:flex;
}
.edit-content-input.edit-probablity-input {
width: 300px!important;
margin-right: 10px;
}
.edit-content-formitem-goequity{
width:80px;
height:30px;
border-radius:5px;
background:#3386F1;
color:#fff;
text-align:center;
line-height:30px;
}
.modal-content-imageUpload-btn{
display:flex;
flex-direction:column;
justify-content:space-around;
}
.modal-content-imageUpload-btn text{
font-size:12px;
margin-bottom:5px;
color:#666666;
}
.edit-content-formItem-nocoupon{
color:#000 !important;
margin-left:5px;
}
\ No newline at end of file
<view>
<overlay
visible="{{showPrize}}"
onRequestClose="onClose"
disableScroll
hasMask="{{showDialogMask}}"
wrapperClassName="overlay"
wrapperStyle="{{{'top':dialogTop + 'px'}}}"
target=".edit-breadcrumb"
canCloseByMask="true"
data-index="0"
>
<view class="modal-content">
<view class="modal-content-title">选择奖品<icon size="small" type="close" onTap="closePrize" style="color: #999999;" /></view>
<view class="modal-content-form">
<form class="edit-content-form" inline="true">
<form-item style="width:100%" size="large" class="edit-content-formItem edit-content-formItem-choosePrize" label="奖品类型" required>
<view class="edit-content-formitem-choosePrize-wrap">
<select onChange="onPrizeTypeChange" defaultValue="{{prizeDialogEdit ? prizeDialogData.record.type : prizeInitData.type}}">
<option value="2">优惠券</option>
<option value="4">实物</option>
<option value="3">积分</option>
</select>
</view>
</form-item>
<form-item
a:if="{{prizeInitData.type == OBJECT_TYPE || prizeInitData.type == CREDITS_TYPE}}"
size="large"
class="edit-content-formItem"
label="奖品名称"
validateState="{{prizeNameTips.status}}" help="{{prizeNameTips.content}}"
required>
<input class="edit-content-input" data-name="name" onChange="onChangePrizeValue" maxLength="10" value="{{prizeInitData.name}}" hasLimitHint="true" name="奖品名称" defaultValue="{{prizeDialogEdit ? prizeDialogData.record.name : prizeInitData.name}}" placeholder="请输入奖品名称"/>
</form-item>
<form-item
a:if="{{prizeInitData.type == OBJECT_TYPE}}"
size="large"
class="edit-content-formItem"
label="奖品库存"
validateState="{{prizeNumberTips.status}}" help="{{prizeNumberTips.content}}"
required>
<input type="number" class="edit-content-input" data-name="restStock" onChange="onChangePrizeValue" type="number" value="{{prizeInitData.restStock}}" hasLimitHint="true" name="奖品库存" defaultValue="{{prizeDialogEdit ? prizeDialogData.record.restStock : prizeInitData.restStock}}" placeholder="请输入奖品库存"/>
</form-item>
<form-item
a:if="{{prizeInitData.type == CREDITS_TYPE}}"
size="large"
class="edit-content-formItem"
label="积分价值"
validateState="{{creditsValueTips.status}}" help="{{creditsValueTips.content}}"
required>
<input type="number" class="edit-content-input" data-name="credits" onChange="onChangePrizeValue" type="number" value="{{prizeInitData.credits}}" hasLimitHint="true" name="积分价值" defaultValue="{{prizeDialogEdit ? prizeDialogData.record.credits : prizeInitData.credits}}" placeholder="请输入积分价值"/>
</form-item>
<form-item a:if="{{prizeInitData.type == EQUITY_TYPE}}" validateState="{{choosePrizeTips.status}}" style="width:100%" size="large" help="{{choosePrizeTips.content}}" class="edit-content-formItem edit-content-formItem-choosePrize" label="选择奖品" required>
<view class="edit-content-formitem-choosePrize-wrap">
<input a:if="{{prizeInitData.name}}" disabled="true" class="edit-content-input" style="margin-right:10px" onChange="changeInput" maxLength="10" value="{{prizeInitData.name}}" hasLimitHint="true" name="活动名称" defaultValue="{{prizeDialogEdit ? prizeDialogData.record.name : prizeInitData.name}}" placeholder="请选择奖品"/>
<view class="edit-content-formitem-goequity" onTap="navigateToPlugin" type="primary">{{prizeInitData.ename ? '重新选择' : '选择奖品'}}</view>
</view>
</form-item>
<form-item size="large" required class="edit-content-formItem" label="奖品图片" validateState="{{imageTips.status}}" help="{{imageTips.content}}">
<view class="modal-content-imageUpload-wrap">
<image style="width:90px;height:90px;border:1px solid #ccc;margin-right:10px" src="{{prizeInitData.image}}"></image>
<view class="modal-content-imageUpload-btn">
<view>奖品图片</view>
<text>尺寸为300px * 300px,格式为jpg/png</text>
<view></view>
<button onTap="uploadImage" type="primary">上传图片</button>
</view>
</view>
</form-item>
<form-item size="large" class="edit-content-formItem" label="中奖概率" validateState="{{probablityTips.status}}" help="{{probablityTips.content}}" required>
<input
class="edit-content-input edit-probablity-input" type="number" onChange="onChangePrizeValue" data-name="probablity" value="{{prizeInitData.probablity}}" name="中奖概率" defaultValue="{{prizeInitData.probablity}}" placeholder="当前概率不能超过100"/>%
</form-item>
<!-- <form-item size="large" class="edit-content-formItem" label="奖品库存" required>
<input disabled="true" class="edit-content-input" onChange="changeInput" maxLength="10" value="{{prizeInitData.name}}" hasLimitHint="true" name="活动名称" defaultValue="{{prizeDialogEdit ? prizeDialogData.record.name : prizeInitData.name}}" placeholder="请输入奖品库存"/>
</form-item> -->
</form>
</view>
<view class="modal-content-btn">
<button onTap="updatePrize" type="primary">确定</button>
<button onTap="closePrize">取消</button>
</view>
</view>
</overlay>
</view>
\ No newline at end of file
const {
cloud
} = getApp()
const {
function: fc
} = cloud
const plugin = requirePlugin('myPlugin')
import {
bizCode
} from '../../config';
import {
queryBenefitByEname
} from '../../api';
import { chooseImage, getImageInfo, validateRangeNumber } from '/utils';
const EQUITY_TYPE = 2;
const CREDITS_TYPE = 3;
const OBJECT_TYPE = 4;
const THANKS_TYPE = 5;
Component({
mixins: [],
data: {
EQUITY_TYPE,
CREDITS_TYPE,
OBJECT_TYPE,
THANKS_TYPE,
showDialogMask: true,
dialogTop: '',
rankTips: {
status: 'success',
content: ''
},
imageTips: {
status: 'success',
content: ''
},
probablityTips: {
status: 'success',
content: ''
},
prizeNameTips: {
status: 'success',
content: ''
},
prizeNumberTips: {
status: 'success',
content: ''
},
creditsValueTips: {
status: 'success',
content: ''
},
prizeInitData: {
ename: '',
id: '',
restStock: '',
activityOutId: '',
type: 2,
image: '',
credits: '',
probablity: '',
name: ''
}
},
props: {
prizeDialogData: {
index: '',
record: {}
},
activityOutId: '',
prizeDialogEdit: false,
showPrize: false,
hasEditPrize: false,
datasource: [],
onCloseDialog: function () {},
onUpdateLevel: function () {},
onUpdateHasChange: function () {},
},
didMount() {
this.initPosition()
let that = this
const bridge = {
bizCode, //此处输入想配置的商家应用appID
//这个方法用于获取插件中用户选择的奖池enname
async getCheckBenefitID({ ename, poolID }) {
that.setData({
prizeInitData: {
...that.data.prizeInitData,
ename
}
})
my.showToast({
type: 'success',
content: '已选择权益奖品'
})
const { success, data, message } = await queryBenefitByEname({ ename })
if (success) {
const { benefitName, rightTypeId, startTime, endTime, restStock } = data[0];
that.setData({
prizeInitData: {
...that.data.prizeInitData,
name: benefitName,
type: rightTypeId,
startTime,
endTime,
restStock
}
})
that.$page.data.backPageTimeOut = setTimeout(() => {
my.navigateBack({
delta: 1
})
}, 6000)
} else {
my.showToast({
type: 'fail',
content: message
})
}
}
}
this.resetPrizeInit()
plugin.setBridge(bridge)
},
methods: {
initPosition() {
const {
scrollTop
} = this.$page.data
this.setData({
dialogTop: scrollTop * 2
})
},
onPrizeTypeChange(e) {
let value = e.detail.value;
this.setData({
prizeInitData: {
...this.data.prizeInitData,
type: value
}
})
},
navigateToPlugin() {
clearTimeout(this.$page.data.backPageTimeOut)
my.navigateTo({
url: 'plugin://myPlugin/orightindex-page'
})
},
resetPrizeInit() {
if (this.props.prizeDialogEdit) {
const {
rank,
...rest
} = this.props.prizeDialogData.record
this.setData({
prizeInitData: {
...rest
}
})
}
},
async uploadImage() {
try {
this.setData({
showDialogMask: false
})
const res = await chooseImage();
if (!res.apFilePaths.length) return
const {
height,
width,
type,
path
} = await getImageInfo({
src: res.apFilePaths[0]
});
let imgBool = ~path.indexOf('.png') || ~path.indexOf('.jpg')
if (height !== 300 || width !== 300 || !imgBool) {
this.showItemTips('imageTips', 'error', '请按要求上传图片');
} else {
this.showItemTips('imageTips', 'success', '');
}
const {
url
} = await cloud.file.uploadFile({
filePath: res.apFilePaths[0],
fileType: 'image',
fileName: path.split('/').pop()
})
this.setData({
prizeInitData: {
...this.data.prizeInitData,
image: url
}
})
} catch (error) {
this.setData({
showDialogMask: true
})
console.error(error)
}
},
onChangePrizeValue(e) {
const key = e.target.dataset.name;
const value = e.detail.value;
this.setData({
prizeInitData: {
...this.data.prizeInitData,
[key]: value
}
})
},
changeInput(e) {
this.setData({
prizeInitData: {
...this.data.prizeInitData,
name: e.detail.value
}
})
},
updatePrize() {
const {
name,
ename,
image,
type,
startTime,
probablity,
restStock,
credits,
endTime
} = this.data.prizeInitData
const {
imageTips
} = this.data;
let probablityRexp = /^\d+(\.\d{1,2})?$/;
if (!probablityRexp.test(probablity) || probablity > 100) {
this.showItemTips('probablityTips', 'error', '奖品概率必须在0-100%之间且最多支持两位小数');
} else {
this.showItemTips('probablityTips', 'success', '');
}
const {
LUKCY_POCKET_TYPE,
COUPON_TYPE,
CREDITS_TYPE,
OBJECT_TYPE,
THANKS_TYPE,
} = this.data;
if (type === LUKCY_POCKET_TYPE && !ename) {
my.showToast({
type: 'fail',
content: '优惠券配置错误, 请重新配置'
});
return;
}
if((type == OBJECT_TYPE || type == CREDITS_TYPE) &&!name) {
this.showItemTips('prizeNameTips', 'error', '请输入奖品名称')
} else {
this.showItemTips('prizeNameTips', 'success', '')
}
if ((type == OBJECT_TYPE) && !validateRangeNumber(restStock, [0, 9999])) {
this.showItemTips('prizeNumberTips', 'error', '奖品库存为0-9999之间的整数');
} else {
this.showItemTips('prizeNumberTips', 'success', '');
}
if (type == CREDITS_TYPE && !validateRangeNumber(credits, [0, 999])) {
this.showItemTips('creditsValueTips', 'error', '请输入积分价值为0-999的整数');
} else {
this.showItemTips('creditsValueTips', 'success', '');
}
const { probablityTips, prizeNumberTips, creditsValueTips } = this.data;
const isImagePass = image && imageTips.status !== 'error';
const isEquietyPass = (type == EQUITY_TYPE) && name && isImagePass && ename && probablityTips.status !== 'error';
const isObjectPass = type == OBJECT_TYPE && name && restStock && isImagePass && probablityTips.status !== 'error' && prizeNumberTips.status !== 'error';
const isCreditsPass = type == CREDITS_TYPE && name && credits && isImagePass && probablityTips.status !== 'error' && creditsValueTips.status !== 'error';
if (
isEquietyPass || isObjectPass || isCreditsPass
) {
this.props.onUpdateLevel({
image,
name,
ename,
type,
probablity,
restStock,
credits
},
this.props.prizeDialogData.index
)
if (this.props.activityOutId) {
this.props.onUpdateHasChange(true, this.props.prizeDialogData.index);
}
this.closePrize();
} else {
my.showToast({
type: 'fail',
content: '请完整填写奖品信息'
})
}
},
showItemTips(type, status, content) {
this.setData({
[type]: {
status,
content
}
})
},
closePrize() {
this.setData({
showDialogMask: true
})
this.props.onCloseDialog('prize')
}
}
})
\ No newline at end of file
{
"component": true,
"plugins": {
"myPlugin": {
"version": "0.0.12",
"provider": "3000000002026202"
}
}
}
\ No newline at end of file
export default {
// ams:ams接口,cloud: 云函数
requestType: 'cloud',
// app环境
env: 'test',
// 线上活动地址
activityURL: 'https://m.duanqu.com/?_ariver_appid=3000000002099934&nbsv=1.0.3&nbsource=debug&nbsn=TRIAL',
// 配置的商家应用appID, 权益插件用
bizCode: '3000000003498711'
}
\ No newline at end of file
{
"name": "",
"version": "1.0.0",
"main": "",
"license": "MIT",
"dependencies": {
"@tbmp/mp-cloud-sdk": "1.1.4",
"miniapp-router": "^0.1.6",
"moment": "^2.24.0"
}
}
.edit-container {
background-color: #fff;
}
.edit-title {
font-size: 12px;
font-weight: 400;
color: rgba(153, 153, 153, 1);
}
.edit-breadcrumb {
padding: 20px 24px;
border-bottom: 2px solid rgba(240, 242, 245, 1);
}
.edit-title-separate {
margin: 0 5px;
font-weight: 900;
}
.edit-tip-icon {
width: 14px;
height: 14px;
margin-left: -10px;
}
.edit-content {
padding: 20px;
}
.edit-content-title {
font-size: 16px;
margin-bottom: 20px;
font-weight: 600;
padding-left: 26px;
}
.edit-content-input {
width: 400px !important;
}
.edit-content-formItem {
font-size: 14px;
font-weight: 400;
position: relative;
}
.edit-content-formItem-add {
width: 42px;
height: 42px;
border: 1px dashed #C9CED4;
display: flex;
justify-content: center;
align-items: center;
}
.edit-content-formItem label {
color: #fff;
}
.edit-content-formItem-qunliao {
margin-top: 15px;
font-size: 14px;
}
.edit-content-formItem-qunliao text {
color: #333333;
}
.edit-content-formItem-qunliao .formItem-qunliao-num {
color: #3386F1;
}
.edit-content-formItem-qunliao .formItem-qunliao-btn {
color: #3386F1;
margin-left: 10px;
}
.edit-content-formItem-addTableList {
height: 54px;
/* width: 600px; */
line-height: 54px;
color: #3386F1;
border-left: 1px solid #D7DBE0;
border-bottom: 1px solid #D7DBE0;
border-right: 1px solid #D7DBE0;
}
.edit-content-formItem-addTableList-notAvailable {
height: 54px;
width: 450px;
line-height: 54px;
border-left: 1px solid #D7DBE0;
border-bottom: 1px solid #D7DBE0;
border-right: 1px solid #D7DBE0;
color: #D7DBE0;
}
.edit-content-specialUrl {
color: #000 !important;
margin-right: 10px;
}
.edit-content-formItem-addTableList Icon {
margin: 0 10px;
}
.edit-content-formItem-image {
display: flex;
align-items: center;
}
.edit-content-formItem-image image {
margin-right: 5px;
}
.xunbao-tips {
width:14px;
height:14px;
display: inline-block;
position: relative;
top: 2px;
left: 4px;
background: url(//yun.duiba.com.cn/activity/mini-taobao/question.png) center no-repeat;
background-size: 100% 100%;
}
.xunbao-required {
color: #F23C3C;
font-size: 12px;
padding-right: 4px;
}
.edit-content-formItem-edit {
color: #3386F1;
}
.edit-content-formItem-delete {
margin-left: 10px;
}
.edit-content-formItem-checkbox {
margin-left: 15px;
}
.checkbox-content {
margin-left: 80px;
background-color: #F7F8FA;
padding: 20px;
}
.checkbox-content view {
margin-bottom: 10px;
}
.checkbox-content view text {
color: #3386F1;
}
.checkbox-content view .has-choose-baby {
color: #000;
margin-right: 10px;
}
.checkbox-tips {
margin: 10px 100px;
color: red;
font-size: 12px;
}
.edit-content-formItem-nodata {
height: 40px;
line-height: 40px;
display: flex;
}
.edit-content-formItem-nodata text {
color: #3386F1;
margin-left: 5px;
font-size: 14px;
}
.has-choose-baby {
margin: 0 12px 0 0;
}
.has-choose-baby text {
color: #3386F1;
}
.rule-config-textarea {
margin-left: 100px;
}
.rule-config-textarea Textarea {
width: 400px;
height: 100px;
}
.edit-activity-config-input {
width: 40px;
margin: 4px 0 0 0;
}
.submit-btn {
margin: 20px 0 0 80px;
}
.submit-btn Button {
margin-left: 20px;
}
.edit-activity-config {
display: flex;
align-items: center;
}
.modal-content {
background: #FFFFFF;
width: 738px;
max-height: 600px;
border: 1px solid #D7DBE0;
display: flex;
flex-direction: column;
box-shadow: 0px 6px 8px 0px #aaa;
}
.modal-content-title {
flex-grow: 0;
flex-shrink: 0;
font-size: 16px;
font-weight: 600;
padding: 20px;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #D7DBE0;
}
.modal-content-btn {
flex-grow: 0;
flex-shrink: 0;
padding: 20px;
display: flex;
justify-content: flex-end;
border-top: 1px solid #D7DBE0;
}
.modal-content-btn Button {
margin-right: 15px;
}
.modal-content-form {
padding: 20px 20px 0px;
}
.modal-content-imageUpload {
width: 100%;
padding-left: 80px;
display: flex;
align-items: center;
margin-bottom: 20px;
}
.modal-content-imageUpload image {
margin-right: 10px;
}
.edit-content-formItem-choosePrize text {
color: #3386F1;
margin: 0 5px;
}
.edit-content-formItem-choosePrize-btn {
margin-left: 15px !important;
}
.overlay {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
right: 0;
bottom: 0;
margin: auto;
display: flex;
align-items: center;
justify-content: center;
}
.task-error-tip {
font-size: 12px;
color: #F23C3C;
margin-left: 24px;
}
.add-prize-wrap {
/* display: inline-block; */
display: inline-flex;
/* display: flex; */
align-items: center;
justify-content: center;
width: 14px;
height: 14px;
border-radius: 50%;
margin:0 8px 0 16px;
}
/* 表格样式覆盖 */
.edit-custom-table .next-table-header .next-table-cell-wrapper {
height: 50px;
line-height: 28px;
}
/* 表格样式覆盖 */
\ No newline at end of file
<view class="edit-container">
<view class="edit-breadcrumb">
<text class="edit-title" onTap="backToActivityList">我的活动</text><text class="edit-title edit-title-separate">/</text><text class="edit-title">{{activityId ? '编辑' : '新建'}} 「 xxxx 」 活动</text>
</view>
<view class="edit-content">
<form class="edit-content-form" inline="true" labelTextAlign="right" size="large" labelCol="{{labelCol}}">
<view class="edit-content-title">基础配置</view>
<form-item labelTextAlign="right" style="width:100%" validateState="{{timeRangeTipInfo.status}}" help="{{timeRangeTipInfo.content}}" class="edit-content-formItem" label="活动时间" asterisk="{{false}}">
<range-picker style="width: 400px" onVisibleChange="dialogChange" hasClear="true" value="{{timeRange}}" data-time="{{timeRange}}" onChange="pickerChange" show-time="{{ format: 'HH:mm' }}" class="block" />
</form-item>
</form>
<view class="submit-btn" a:if="{{!isEnd}}">
<button onTap="submitActive" type="primary">确定</button>
<button onTap="backToActivityList">取消</button>
</view>
</view>
</view>
\ No newline at end of file
import moment from 'moment';
import { getActivityDetail, saveActivityInfo } from '../../../api';
import { activityType } from '../const';
import { addFloat } from '../../../utils/helper';
Component({
data: {
activityInfos: {},
timeRange: [],
startTime: '',
endTime: '',
labelCol: {
fixedSpan: 5
}
},
props: {},
didMount() {
const { id } = this.$page.$router.params;
if (id || id === 0) {
this.getActivityInfo(id);
} else {
this.setDefaultTime();
}
},
methods: {
// 创建活动时, 根据规则设置活动时间
setDefaultTime() {
let nowTime = new Date().getTime();
let oneDay = 24 * 3600 * 1000;
// 当前时间 + 10分钟
let startNow = moment(nowTime + 600000).format("YYYY-MM-DD HH:mm:ss");
// 开始时间 + 7天
let endNow = moment(nowTime + 600000 + 7 * oneDay).format("YYYY-MM-DD HH:mm:ss");
this.setData({
timeRange: [ startNow, endNow ],
startTime: startNow,
endTime: endNow
})
},
backToActivityList() {
this.$page.$router.push('/activity/list')
},
// 获取id活动信息
async getActivityInfo(activityId) {
try {
const { success, data, message } = await getActivityDetail({ activityId, activityType });
if (!success) {
this.showFailToast(message);
return;
}
let { prizeInfoList, ...rest } = data;
const startTime = moment(+rest.startTime).format("YYYY-MM-DD HH:mm:ss");
const endTime = moment(+rest.endTime).format("YYYY-MM-DD HH:mm:ss");
this.setData({
activityInfos: data
})
} catch (error) {
console.log(err, 'err')
}
}
}
});
\ No newline at end of file
{
"component": true,
"usingComponents": {
"prize-dialog": "/components/prize-dialog/prize-dialog"
}
}
\ No newline at end of file
<view>
<router-view>
<list slot="list" />
<add slot="add" />
</router-view>
</view>
Component({
mixins: [],
data: {},
props: {},
didMount() {},
didUpdate() {},
didUnmount() {},
methods: {},
});
{
"component": true,
"usingComponents": {
"router-view": "miniapp-router/router-view/router-view",
"list": "../list/list",
"add": "../add/add"
}
}
\ No newline at end of file
export const activityType = 2;
\ No newline at end of file
.create {
display: flex;
justify-content: space-between;
height: 96px;
padding: 0 24px;
background-color: #fff;
margin-bottom: 18px;
align-items: center;
}
.db-title {
font-size:20px;
font-weight: bold;
}
.list-container {
padding: 0 24px;
background-color: #fff;
}
.list-container .next-table-header .next-table-cell {
background-color: #fff;
}
.list-container .next-table-row {
height: 80px;
}
.list-container .next-table-header .next-table-cell-wrapper {
height: 60px;
line-height: 38px;
}
.time-column {
font-size: 14px;
}
.font-14 {
font-size: 14px;
}
.font-13 {
font-size: 13px;
}
.font-12 {
font-size: 12px;
}
.mb-4 {
margin-bottom: 4px;
}
.edit {
margin-right: 8px;
}
.operate-column {
display: flex;
justify-content: center;
}
.pagination {
margin-top: 20px;
align-items: center;
justify-content: flex-end;
display: flex;
}
.balloon-content {
padding: 10px 0px 10px 5px;
}
.balloon-content-title {
font-size: 12px;
padding-bottom: 10px;
}
.balloon-content view {
color: #696969;
}
.balloon-content button {
font-size: 10px;
}
.pagination-text {
font-size: 12px;
color: rgba(51, 51, 51, 1);
margin-right: 10px;
}
\ No newline at end of file
<view class="create">
<text class="db-title">活动管理</text>
<button type="primary" onTap="tapname">创建活动</button>
</view>
<view class="list-container">
<table dataSource="{{dataSource}}" hasBorder="{{false}}" loading="{{isLoadingList}}">
<table-column title="活动名称" >
<view class="font-14" slot-scope="x">
<text>{{x.record.title}}</text>
</view>
</table-column>
<table-column title="活动时间" >
<view class="font-13" slot-scope="x">
<view class="mb-4">起: {{x.record.startTime}}</view>
<view>止: {{x.record.endTime}}</view>
</view>
</table-column>
<table-column title="操作" dataIndex="id" alignHeader="left">
<button class="edit" onTap="handleClickEdit" type="primary" text="true" data-x="{{x}}" slot-scope="x">编辑活动</button>
<button class="edit" type="primary" text="true" data-x="{{x}}" slot-scope="x" onTap="exportWinnerList">导出中奖名单</button>
<button class="edit" type="primary" text="true" data-x="{{x}}" slot-scope="x" onTap="handleTapDelete">删除</button>
<button class="edit" type="primary" text="true" data-x="{{x}}" slot-scope="x" onTap="onCopyLink">复制链接</button>
</table-column>
</table>
</view>
<view class="pagination">
<text class="pagination-text">共{{pageInfo.total}}条</text>
<pagination shape="arrow-only" hideOnlyOnePage="true" defaultCurrent="1" current="{{pageInfo.pageNo}}" pageSize="{{pageInfo.pageSize}}" onChange="changePagination" pageShowCount="5" total="{{pageInfo.total}}" />
</view>
<confirm-dialog
visible="{{confirmDialog.visible}}"
content="{{confirmDialog.content}}"
onClose="onCloseConfirmDialog"
onConfirm="onConfirmDelete"
/>
import moment from 'moment';
import {
getActivityList,
delActivity,
findWinnerInfoList,
sellerSave,
uploadDataCreateFile
} from '/api';
import { activityURL } from '../../../config';
import { activityType } from '../const';
import { setClipboard } from '/utils'
const { cloud } = getApp();
const { function: fc } = cloud;
Component({
mixins: [],
data: {
confirmDialog: {
visible: false,
content: '删除活动后该活动用户参与信息将全部删除,确认删除吗?'
},
winnerListDialogVisible: false,
winnerList: [],
deleteId: '',
selectedItem: '',
dataSource: [],
pageInfo: {
pageNo: 1,
pageSize: 10,
total: 0
},
isLoadingList: false,
showWinning: false
},
props: {},
didMount() {
this.getList();
},
methods: {
/**
*获取活动列表
*
* @param {number} [currentPage=1] 页数
* @param {number} [size=10] 页码尺寸
* @returns {void}
*/
async getList(currentPage = 1, size = 10) {
this.setData({ isLoadingList: true });
try {
const { success, data, message } = await getActivityList({
pageNo: currentPage,
pageSize: size,
activityType
})
this.setData({ isLoadingList: false })
if (!success) {
my.showToast({
type: 'fail',
content: message
})
return;
}
const { list, pageNo, pageSize, total } = data;
this.setData({
dataSource: list,
pageInfo: {
pageNo,
pageSize,
total
}
})
} catch (error) {
this.setData({
isLoadingList: false
})
}
},
// 导出中奖名单
async exportWinnerList(evt) {
const { activityId } = evt.target.dataset.x.record;
my.showLoading({ content: '生成文件中...' })
try {
const { success, data, message } = await uploadDataCreateFile({
activityId,
activityType
})
my.hideLoading();
if (success) {
await setClipboard({ text: data.url.replace(/amp;/g, '') })
my.showToast({
type: 'success',
content: '中奖名单链接复制成功,请在浏览器中打开下载'
});
} else {
my.showToast({
type: 'fail',
content: message
})
}
} catch (error) {
my.hideLoading();
console.log(error, 'exportList-error');
}
},
// 复制链接
async onCopyLink(evt) {
const { activityId } = evt.target.dataset.x.record;
try {
await setClipboard({
text: `${activityURL}&activityId=${activityId}`
})
my.showToast({
type: 'success',
content: '复制活动链接成功'
})
} catch (error) {
console.log(error)
}
},
onCloseConfirmDialog() {
this.setData({
confirmDialog: Object.assign({},
this.data.confirmDialog, {
visible: false
})
})
},
// 点击列表删除
handleTapDelete(evt) {
const { activityId } = evt.target.dataset.x.record;
this.setData({
confirmDialog: Object.assign({}, this.data.confirmDialog, {
visible: true
}),
deleteId: activityId
})
},
onConfirmDelete() {
const activityId = this.data.deleteId;
this.setData({
confirmDialog: Object.assign({}, this.data.confirmDialog, {
visible: false
})
})
activityId && this.deleteActivityFromId(activityId)
},
// 删除活动
async deleteActivityFromId(activityId) {
try {
const { success, message } = await delActivity({ activityId, activityType });
if (success) {
this.getList()
} else {
my.showToast({
type: 'fail',
content: message
})
}
} catch (error) {
console.log(err)
}
},
// 编辑
handleClickEdit(evt) {
let { activityId } = evt.target.dataset.x.record;
this.$page.$router.push(`/activity/edit/${activityId}`)
},
changePagination(evt) {
const { value } = evt.detail;
this.getList(value)
},
// 创建活动
tapname() {
this.$page.$router.push('/activity/add')
}
},
});
\ No newline at end of file
{
"component": true,
"usingComponents": {
"confirm-dialog": "/components/confirm-dialog/confirm-dialog"
}
}
\ No newline at end of file
/* required by usingComponents */
/* 修改模块样式framework */
/* .aside-menu-item {
line-height: 20px !important;
display: flex !important;
align-items: center !important;
justify-content: center;
}
.aside-menu-item:nth-child(2) {
padding: 0 30px !important;
justify-content: flex-start !important;
text-align: left !important;
} */
.db-content-wrap {
max-height: calc(100vh - 50px);
overflow-x: hidden;
overflow-y: auto;
}
\ No newline at end of file
<view id="root">
<layout menu="{{menu}}" info="{{info}}" activeKey="{{activeKey}}" onMenuChange="onMenuChange">
<view class="db-content-wrap">
<router-view>
<base slot="activity" />
</router-view>
</view>
</layout>
</view>
const { cloud } = getApp();
import routerInit from 'miniapp-router';
import routerConfig, { basePath } from '../../router/index'
import { sellerSave } from '../../api';
Page({
data: {
activeKey: 'activity',
showCopyVisible: false,
scrollTop: '',
info: {
company: '兑吧科技',
miniappName: '兑吧科技',
logo: '//yun.duiba.com.cn/duiba_miniProgram/logo.png'
},
menu: [{
name: '活动管理',
key: 'activity',
title: '活动管理',
path: '/activity/list'
}]
},
onUpdateShowCopy() {
this.setData({
showCopyVisible: false
})
},
onLoad(query) {
my.authorize({
scopes: 'scope.userInfo',
success: (res) => {
sellerSave({}).then(res => {
if (!res.success) {
my.showToast({
type: 'fail',
content: res.message
})
}
});
},
error: () => {
}
});
// 页面加载
routerInit.call(this, routerConfig);
// console.info(`Page onLoad with query: ${JSON.stringify(routerConfig)}`);
},
onPageScroll: function (e) { // 获取滚动条当前位置
this.setData({
scrollTop: e.scrollTop,
})
},
onMenuChange({ key, path }) {
this.setData({
activeKey: key
});
this.$router.push(path)
}
});
\ No newline at end of file
{
"component": true,
"usingComponents": {
"router-view": "miniapp-router/router-view/router-view",
"base": "/pages/activity/base/base",
"layout": "/components/base/layout/layout"
},
"plugins": {
"myPlugin": {
"version": "0.0.12",
"provider": "3000000002026202"
}
}
}
\ No newline at end of file
export const basePath = `/activity/list`;
export default {
routes: [{
path: '/activity',
component: 'activity',
children: [
{
path: '/base',
component: 'base'
},
{
path: '/list',
component: 'list'
},
{
path: '/add',
component: 'add'
},
{
path: '/edit/:id',
component: 'add'
}
]
}
],
option: {
initPath: basePath,
}
}
\ No newline at end of file
export function debounce(fn, wait) {
var timeout = null
return function () {
if (timeout !== null) clearTimeout(timeout)
timeout = setTimeout(fn, wait)
}
}
// 解决小数相加精度问题
export function addFloat(num1, num2) {
var r1, r2, m
r1 = num1.toString().split(".")[1] ? num1.toString().split(".")[1].length : 0
r2 = num2.toString().split(".")[1] ? num2.toString().split(".")[1].length : 0
m = Math.pow(10, Math.max(r1, r2))
return (num1 * m + num2 * m) / m
}
\ No newline at end of file
export * from './helper'
export * from './my-api';
export * from './validate';
\ No newline at end of file
// promise化 淘宝api
// 具体 api参数 -> https://miniapp.open.taobao.com/doc.htm?docId=117502&docType=1
export function chooseImage() {
return promisifyMyApi('chooseImage')
}
export function getImageInfo(params) {
return promisifyMyApi('getImageInfo', params)
};
// 剪切板
export function setClipboard(params) {
return promisifyMyApi('setClipboard', params)
};
function promisifyMyApi(methodName, params = {}) {
return new Promise((resolve, reject) => {
my[methodName]({
...params,
success: res => {
resolve(res)
},
fail: err => {
reject(err)
}
})
})
}
\ No newline at end of file
const {
cloud
} = getApp();
import { requestType } from '../config';
const REQUEST_PREFIX = 'backstage';
const request = (url, method, params, ext = {}) => {
const {
isShowLoading
} = ext;
const hideMyLoading = () => {
if (isShowLoading) {
my.hideLoading();
}
};
if (isShowLoading) {
my.showLoading();
}
const requestAms = () => {
return my.request({
url: `https://ams.dui88.com/server/index.php?g=Web&c=Mock&o=simple&projectID=218&uri=${REQUEST_PREFIX}.${url}`,
method,
data: params,
dataType: 'json'
}).then(res => {
hideMyLoading();
return res.data;
}).catch(() => {
hideMyLoading();
});
}
const requestCloud = () => {
return cloud.function.invoke(REQUEST_PREFIX, params, url).then(res => {
hideMyLoading();
return res;
}).catch(() => {
hideMyLoading();
});
}
return requestType === 'ams' ? requestAms() : requestCloud();
};
export default request;
\ No newline at end of file
// 正整数
export const validatePositiveInteger = number => /^[1-9]\d*$/.test(number);
// 整数
export const validateInteger = number => /^-?\d*$/.test(number);
// 判断是否为数字
export const isNumber = val => {
var regPos = /^\d+(\.\d+)?$/; //非负浮点数
var regNeg = /^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$/; //负浮点数
if(regPos.test(val) && regNeg.test(val)){
return true;
}else{
return false;
}
}
/**
*
* 验证一定范围内数,支持整数或小数
* @param {*} number
* @param {*} [ min, max ] 范围 [0,99] [-Infinity, 0] [999, Infinity]
* @param {number} [fixed=0] 小数位数 0时为整数
* @returns { boolean }
*/
export const validateRangeNumber = (number, [ min, max ] = [], fixed = 0) => {
const pattern = new RegExp(`^-?[0-9]+(\\.[0-9]{0,${fixed}})?$`);
if(min > max) {
console.error('数字范围的最小值不能大于最大值');
return;
}
if(!pattern.test(number) || number === '') return false;
if(number < min || number > max) return false;
return true;
}
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@tbmp/mp-cloud-sdk@1.1.4":
version "1.1.4"
resolved "https://registry.npm.taobao.org/@tbmp/mp-cloud-sdk/download/@tbmp/mp-cloud-sdk-1.1.4.tgz#0ccb91cd77d17c52cd6f0cb1584368cfdfd1a9dd"
integrity sha1-DMuRzXfRfFLNbwyxWENoz9/Rqd0=
miniapp-router@^0.1.6:
version "0.1.7"
resolved "https://registry.npm.taobao.org/miniapp-router/download/miniapp-router-0.1.7.tgz#e92989574cbb0cf11793d62508d0413fe165b948"
integrity sha1-6SmJV0y7DPEXk9YlCNBBP+FluUg=
moment@^2.24.0:
version "2.24.0"
resolved "https://registry.npm.taobao.org/moment/download/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b"
integrity sha1-DQVdU/UFKqZTyfbraLtdEr9cK1s=
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment