同步代码

This commit is contained in:
zhangmanman
2021-09-15 17:30:46 +08:00
parent 1d6e2451a7
commit 409eb507f3
525 changed files with 65145 additions and 0 deletions

View File

@@ -0,0 +1,20 @@
{ // launch.json 配置了启动调试时相关设置configurations下节点名称可为 app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/
// launchtype项可配置值为local或remote, local代表前端连本地云函数remote代表前端连云端云函数
"version": "0.0",
"configurations": [{
"app-plus" :
{
"launchtype" : "remote"
},
"default" :
{
"launchtype" : "local"
},
"h5" :
{
"launchtype" : "local"
},
"type" : "uniCloud"
}
]
}

View File

@@ -0,0 +1,22 @@
<script>
export default {
onLaunch () {
console.log('App Launch')
},
onShow () {
console.log('App Show')
},
onHide () {
console.log('App Hide')
},
globalData: {
mainColor: "white"
}
}
</script>
<style>
page{
background: #f5f5f5;
}
</style>

View File

@@ -0,0 +1,53 @@
# 易货App
_开发临时存储资源存放在/static/dev目录下_
## 公共的组件
### 1.商品列表
_商品列表主要应用于首页产品列表等_
> 参数说明
| 参数 | 参数 | 是否必填 | 数据类型 | 默认值 | 可选值 |
| :---: | :---: | :---: | :---: | :---: | :---: |
| 数据列表 | list | 是 | Array | [] | {{list}} |
| 货币类型 | priceType | 否 | String | EB | EB, CNY|
| 空提示 | toast | 否 | String | 暂无商品数据 | - |
> list格式说明
```
list: [
{
id : "1"
cover: "https://yanxuan-item.nosdn.127.net/d3b072b5006f56935d15d239cbc5c953.jpg",
title: "片片大果仁,夏威夷坚果脆片 可可味 55克",
price: "16",
sales: "25"
},{
...
}
]
```
> 引入🙇🏻栗子
```
import goodsList from '@/components/goods-list/goods-list'
export default {
components:{
goodsList
}
}
<template>
<goods-list></goods-list>
or
<goods-list/>
<template/>
```

View File

@@ -0,0 +1,158 @@
/**
* Web唐明明
* 匆匆数载恍如梦,岁月迢迢华发增。
* 碌碌无为枉半生,一朝惊醒万事空。
*/
import store from '@/store'
// 基础配置
const config = {
apiUrl : 'https://e-chain.cnskl.com/api/',
timeout : 60000
}
let loginHintState = false
// 网络请求
const request = (parameter) => {
// 检查url配置
if(parameter.url === 'undefined' || parameter.url === ''){
uni.showToast({
title: '请求地址不能为空',
icon : 'none'
})
return
}
// 注入header
config.header = {
'Accept': 'application/json',
'Authorization': store.getters.getToken || ''
}
console.log('parameterDbug', parameter)
// 加载提示
uni.showLoading({
title: '加载中',
mask : true
});
// 请求实例
return new Promise((resolve, reject) => {
uni.request({
url : config.apiUrl + parameter.url,
timeout : config.timeout,
header : config.header || {},
data : parameter.data || {},
method : parameter.method || 'GET',
success : res => {
if (res.header.Authorization){
updateToken('token', res.header.Authorization)
}
if(res.statusCode === 200){
uni.hideLoading()
const resolveData = res.data
if(resolveData.status_code === 200) {
resolve(resolveData.data)
return
}
if(resolveData.status_code === 401) {
loginHint()
return
}
reject(resolveData)
return
}
errToast(res.statusCode)
}
})
})
}
// 文件上传
const uploading = (paths) => {
uni.showLoading({
title: '上传中',
mask : true
});
// 注入header
config.header = {
'Accept': 'application/json',
'Authorization': store.getters.getToken || ''
}
// 上传图片
return new Promise((resolve, reject) => {
uni.uploadFile({
url : config.apiUrl + 'storage/uploads',
files : paths,
header : config.header || {},
success : res=>{
if(res.statusCode === 200){
uni.hideLoading()
let updData = JSON.parse(res.data)
if(updData.status_code === 200){
resolve(updData.data)
return
}
reject(updData)
return
}
errToast(res.statusCode)
}
})
})
}
// 处理一些http请求错误提示
const errToast = (code) => {
switch (code){
case 404:
uni.showToast({
title: code + '接口不存在,请联系系统管理员',
icon : 'none'
})
break;
case 405:
uni.showToast({
title: code + '请检查接口请求方式错误',
icon : 'none'
})
break;
case 500:
uni.showToast({
title: code + '服务端错误,请检查服务器信息',
icon : 'none'
})
break;
}
}
// 更新token
const updateToken = (token) => {
store.commit('setToken', token)
}
// 处理登录提示
const loginHint = () => {
if( loginHintState ) return
if(!loginHintState) loginHintState = true
updateToken('')
uni.showModal({
title: '登录提示',
content: '您的登录信息已过期,请重新登录',
confirmColor: '#009B69',
showCancel:false,
success: res=> {
loginHintState = false
if (res.confirm) uni.reLaunch({
url: '/pages/equity/index'
})
}
})
}
export {
request,
uploading
}

View File

@@ -0,0 +1,42 @@
/**
* Web唐明明
* 匆匆数载恍如梦,岁月迢迢华发增。
* 碌碌无为枉半生,一朝惊醒万事空。
* moduleName: 鉴权
*/
import { request } from '../index'
// 一键登录
const keyAuth = (data) => {
return request({
url: 'user/socialite/login/unicloud/app',
method: 'POST',
data: data
})
}
// 验证码登录
const smsAuth = (data) =>{
return request({
url: "user/auth/sms",
method: 'POST',
data: data
})
}
// 获取验证码
const getSms = (data) =>{
return request({
url: "user/auth/verify",
method: 'POST',
data: data
})
}
export {
keyAuth,
smsAuth,
getSms
}

View File

@@ -0,0 +1,91 @@
/**
* Web唐明明
* 匆匆数载恍如梦,岁月迢迢华发增。
* 碌碌无为枉半生,一朝惊醒万事空。
* moduleName: 企业
*/
import { request } from '../index'
// 企业注册配置信息
const createConfig = () => {
return request({
url: 'companies/inits/create'
})
}
// 企业行业信息
const inits = data => {
return request({
url: 'companies/inits',
method: 'POST',
data
})
}
// 企业广场
const companies = data => {
return request({
url: 'companies',
data
})
}
// 企业列表
const companiesList = data => {
return request({
url: 'companies/lists',
data
})
}
// 企业认证配置信息
const appliesCreate = () => {
return request({
url: 'companies/applies/create'
})
}
// 企业认证提交、编辑
const applies = (data,method) => {
return request({
url: 'companies/applies',
method,
data
})
}
// 企业认证前置条件
const isallow = () => {
return request({
url: 'companies/applies/isallow',
method: 'POST'
})
}
// 企业申请状态
const appliesQuery = () => {
return request({
url: 'companies/applies/query'
})
}
// 企业信息展示
const appliesInfo = () => {
return request({
url: 'companies/applies'
})
}
export {
createConfig,
inits,
companies,
companiesList,
appliesCreate,
applies,
isallow,
appliesQuery,
appliesInfo
}

View File

@@ -0,0 +1,66 @@
/**
* Web唐明明
* 匆匆数载恍如梦,岁月迢迢华发增。
* 碌碌无为枉半生,一朝惊醒万事空。
* moduleName: 优惠券
*/
import { request } from '../index'
// 优惠券管理
const toolsCoupons = (data) => {
return request({
url: 'coupons/tools/coupons',
data
})
}
// 发布优惠券
const pushCoupons = (data) => {
return request({
url: 'coupons/tools/coupons',
method: 'POST',
data
})
}
// 关联券产品
const couponsGoods = id => {
return request({
url: 'coupons/tools/coupons/' + id + '/goods'
})
}
// 设置关联商品
const couponsAddgoods = (id, data) => {
return request({
url: 'coupons/tools/coupons/' + id + '/addgoods',
method: 'POST',
data
})
}
// 管理优惠券详情
const magCouponsInfo = (id) => {
return request({
url: 'coupons/tools/coupons/' + id
})
}
// 上下架
const magCouponsStatus = (id) => {
return request({
url: 'coupons/tools/coupons/' + id + '/status',
method: 'POST'
})
}
export {
toolsCoupons,
pushCoupons,
couponsGoods,
couponsAddgoods,
magCouponsInfo,
magCouponsStatus
}

View File

@@ -0,0 +1,38 @@
/**
* Web唐明明
* 匆匆数载恍如梦,岁月迢迢华发增。
* 碌碌无为枉半生,一朝惊醒万事空。
* moduleName: 企业员工管理
*/
import { request } from '../index'
// 员工列表
const employees = () => {
return request({
url: 'companies/employees'
})
}
// 添加员工配置项
const employeesConfig = () => {
return request({
url: 'companies/employees/create'
})
}
// 添加员工
const addEmployees = (data) => {
return request({
url: 'companies/employees',
method: 'POST',
data
})
}
export {
employees,
employeesConfig,
addEmployees
}

View File

@@ -0,0 +1,101 @@
/**
* Web唐明明
* 匆匆数载恍如梦,岁月迢迢华发增。
* 碌碌无为枉半生,一朝惊醒万事空。
* moduleName: 商品
*/
import { request } from '../index'
// 商城首页
const mall = data => {
return request({
url: "mall"
})
}
// 商品列表
const list = data => {
return request({
url: "mall/goods"
})
}
// 商品详情
const goods = id => {
return request({
url: 'mall/goods/' + id
})
}
// 商品管理-商品列表
const managesGoodsIndex = data => {
return request({
url: 'manages/goods/index',
data:data
})
}
// 商品管理-商品增发
const managesGoodsMint = data => {
return request({
url: 'manages/goods/'+data.id+'/mint',
data:data,
method:'POST'
})
}
// 商品管理-商品燃烧
const managesGoodsBurn = data => {
return request({
url: 'manages/goods/'+data.id+'/burn',
data:data,
method:'POST'
})
}
// 商品管理-商品上架
const managesGoodsOnsale = id => {
return request({
url: 'manages/goods/'+id+'/onsale',
method:'PUT'
})
}
// 商品管理-商品下架
const managesGoodsOffsale = id => {
return request({
url: 'manages/goods/'+id+'/offsale',
method:'PUT'
})
}
// 发布商品前置 manages/goods/create
const managesGoodsCreateBefore = () => {
return request({
url: 'manages/goods/create'
})
}
// 发布商品
const managesGoodsCreate = (data) => {
return request({
url: 'manages/goods',
method:'POST',
data:data
})
}
export {
mall,
list,
goods,
managesGoodsIndex,
managesGoodsMint,
managesGoodsBurn,
managesGoodsOnsale,
managesGoodsOffsale,
managesGoodsCreateBefore,
managesGoodsCreate
}

View File

@@ -0,0 +1,30 @@
/**
* Web唐明明
* 匆匆数载恍如梦,岁月迢迢华发增。
* 碌碌无为枉半生,一朝惊醒万事空。
* moduleName: 订单
*/
import { request } from '../index'
// 创建,确认订单
const buy = (data, method) => {
return request({
url: 'mall/buy/goods',
method,
data
})
}
// eb支付
const eb = (no) => {
return request({
url: 'mall/pay/' + no + '/eb',
})
}
export {
buy,
eb
}

View File

@@ -0,0 +1,57 @@
/**
* Web唐明明
* 匆匆数载恍如梦,岁月迢迢华发增。
* 碌碌无为枉半生,一朝惊醒万事空。
* moduleName: 部门/门店
*/
import { request } from '../index'
// 列表
const shops = () => {
return request({
url: 'coupons/tools/stores'
})
}
// 创建
const create = data => {
return request({
url: 'coupons/tools/stores',
method: 'POST',
data: data
})
}
// 详情
const editInfo = storeId => {
return request({
url: 'coupons/tools/stores/' + storeId + '/edit'
})
}
// 编辑
const putShop = (storeId, data) => {
return request({
url: 'coupons/tools/stores/' + storeId,
method: 'PUT',
data
})
}
// 删除
const deleteShop = storeId => {
return request({
url: 'coupons/tools/stores/' + storeId,
method: 'DELETE'
})
}
export {
shops,
create,
editInfo,
putShop,
deleteShop
}

View File

@@ -0,0 +1,56 @@
/**
* Web唐明明
* 匆匆数载恍如梦,岁月迢迢华发增。
* 碌碌无为枉半生,一朝惊醒万事空。
* moduleName: 企业工具
*/
import { request } from '../index'
// 企业首页
const index = () => {
return request({
url: 'companies/index'
})
}
// 成交客户
const customer = data => {
return request({
url: 'mall/statistics',
data
})
}
// 访客记录
const visitors = data => {
return request({
url: 'companies/visitors/lists',
data
})
}
// 基础信息模块
const basicsConfig = () => {
return request({
url: 'companies/info/create'
})
}
// 企业基础信息 编辑
const basicsInfo = (method, data) => {
return request({
url: 'companies/info',
method,
data
})
}
export {
index,
customer,
visitors,
basicsConfig,
basicsInfo
}

View File

@@ -0,0 +1,17 @@
/**
* Web唐明明
* 匆匆数载恍如梦,岁月迢迢华发增。
* 碌碌无为枉半生,一朝惊醒万事空。
* moduleName: 上传图片
*/
import { uploading as upd } from '../index'
const uploads = (paths) => {
return upd(paths)
}
export {
uploads
}

View File

@@ -0,0 +1,37 @@
/**
* Web唐明明
* 匆匆数载恍如梦,岁月迢迢华发增。
* 碌碌无为枉半生,一朝惊醒万事空。
* moduleName: 会员
*/
import { request } from '../index'
// 会员身份信息
const identities = () => {
return request({
url: 'user/identities'
})
}
// 开通会员
const vipOrder = id =>{
return request({
url : 'user/identities/create/' + id,
method : 'POST'
})
}
// 开通会员微信支付
const vipWechatPay = id => {
return request({
url : 'user/identities/pay/' + id + '/wechat'
})
}
export {
identities,
vipOrder,
vipWechatPay
}

View File

@@ -0,0 +1,137 @@
<template>
<view class="goods--list">
<block v-if="list.length > 0">
<view class="goods--item" v-for="(item, index) in list" :key="index" @click="goods(item)">
<view class="cover">
<image class="cover--src" :src="item.cover" mode="aspectFill" />
</view>
<view class="content">
<view class="title">{{item.name}}</view>
<view class="content-flex">
<view class="price eb" v-if="priceType === 'EB'">
{{item.price}}<text>易币</text>
</view>
<view class="price cny" v-if="priceType === 'CNY'">
<text></text>{{item.original_price}}
</view>
<view class="sales">
<slot name="statistics" :value="item">
月易量{{item.sales}}
</slot>
</view>
</view>
<slot name="footer" :value="item" />
</view>
</view>
</block>
<block v-else>
<view class="goods--null">
<view>{{toast}}</view>
</view>
</block>
</view>
</template>
<script>
export default {
name : 'goodsList',
props : {
// 数据列表
list: {
type : Array,
default : () => {
return new Array
}
},
// 价格类型
priceType: {
type : String,
default : 'EB'
},
// 列表空提示
toast : {
type : String,
default : '暂无商品数据 -_-!'
}
},
methods:{
goods(e){
this.$emit('on-goods', e)
}
}
};
</script>
<style lang="scss" scoped>
.goods--list{
padding: calc(#{$padding} - 10rpx);
display: flex;
flex-wrap: wrap;
.goods--item{
background: white;
box-sizing: border-box;
width: calc(50% - 20rpx);
margin: 10rpx;
border-radius: $radius/2;
overflow: hidden;
.cover{
position: relative;
width: 100%;
padding-top: 100%;
.cover--src{
position: absolute;
height: 100%;
width: 100%;
top: 0;
left: 0;
}
}
.content{
padding: $padding/2;
.title{
font-size: $title-size-lg;
line-height: 40rpx;
height: 80rpx;
text-align: justify;
@extend .ellipsis;
}
.content-flex{
width: 100%;
display: flex;
justify-content: space-between;
padding-top: $padding/2;
.price{
width: 50%;
color: $text-price;
font-weight: bold;
font-size: $title-size;
@extend .nowrap;
text{
font-size: $title-size-sm;
font-weight: normal;
padding-left: $padding/4;
line-height: 50rpx;
}
}
.sales{
width: 50%;
font-size: $title-size-sm;
color: $text-gray;
line-height: 50rpx;
text-align: right;
@extend .nowrap;
}
}
}
}
}
// 数据空
.goods--null{
width: 100%;
padding: 20vh 0;
text-align: center;
font-size: $title-size-m;
color: $text-gray;
}
</style>

View File

@@ -0,0 +1,206 @@
<template>
<view class="industry--list">
<block v-if="list.length > 0">
<view class="industry--box" v-for="(item, index) in list" :key="index" @click="industry(item)">
<image class="industry--cover" :src="item.cover" mode="aspectFill"></image>
<view class="industry--vip">{{item.level.name}}</view>
<view class="industry--content">
<view class="industry--title nowrap">{{item.name}}</view>
<view class="industry--credit">信用值 {{item.integrity}}</view>
<view class="industry--trade nowrap" v-if="item.industry">行业{{item.industry.title}}</view>
<view class="industry--bar">
<view class="industry--color">
<view class="industry--strip" :style="{width: item.process + '%'}"></view>
</view>
<view class="industry--per">{{item.process}}%</view>
</view>
<!-- <view class="industry--credibility">
<uni-rate
:readonly="true"
color="#ddd"
active-color="#e93340"
:value="item.star"
:size="14"
/>
</view> -->
<!-- <uni-icons class="industry--arrow" type="arrowright" color="#ddd" size="14" /> -->
<view class="industry--tool">
<view class="industry--deal">
权证数<text class="industry--number">{{item.goodsCount}}</text>
</view>
</view>
</view>
</view>
</block>
<block v-else>
<view class="industry--null">
<view>{{toast}}</view>
</view>
</block>
</view>
</template>
<script>
export default {
name:"industry-list",
props: {
// 数据列表
list: {
type : Array,
default : () => {
return new Array
}
},
// 列表空提示
toast : {
type : String,
default : '暂无商品数据 -_-!'
}
},
methods:{
industry(e){
this.$emit('on-industry', e)
}
}
}
</script>
<style lang="scss" scoped>
// 列表信息
.industry--list{
padding-bottom: $padding;
}
.industry--box{
position: relative;
margin: $margin - 10 $margin;
background: white;
border-radius: $radius/2;
padding: $padding ($padding*3) $padding ($padding * 2 + 128);
min-height: 128rpx;
.industry--vip {
position: absolute;
top: $padding;
left: $padding;
background-color: #191919;
color: #f3c8a8;
font-size: 24rpx;
line-height: 32rpx;
padding: 0 8rpx;
border-radius: 6rpx 6rpx 6rpx 0;
}
.industry--cover{
position: absolute;
left: $padding;
top: $padding;
width: 128rpx;
height: 128rpx;
}
.industry--title{
font-weight: bold;
font-size: $title-size-lg;
width: 80%;
line-height: 40rpx;
}
.industry--credit {
font-size: 22rpx;
display: inline-block;
font-weight: normal;
color: #ec652f;
border: 2rpx solid #ec652f;
line-height: 36rpx;
border-radius: 6rpx;
padding: 0 6rpx;
margin: 10rpx 0;
}
.industry--trade {
font-size: 24rpx;
color: $text-gray;
line-height: 40rpx;
}
.industry--bar {
display: flex;
width: 70%;
margin-top: 4rpx;
.industry--color{
background: #ebebeb;
border-left: 2rpx solid transparent;
border-right: 2rpx solid transparent;
width: calc(100% - 50rpx);
border-radius: 60rpx;
margin: 6rpx 10rpx 0 0;
height: 18rpx;
.industry--strip {
background-color: #ff8562;
border: 1px solid #ff8562;
border-radius: 10px;
box-shadow: 1vw 3vh 10vh rgba(168, 7, 7, 0.8);
background-size: 3em 3em;
background-image: linear-gradient(-45deg, transparent 0em, transparent 0.8em, #ec3950 0.9em, #ec3950 2.1em, transparent 2.1em, transparent 2.9em, #ec3950 3.1em);
height: 14rpx;
border-radius: 60rpx;
position: relative;
&::after {
content: '';
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 1;
height: 100%;
background-image: linear-gradient(to bottom, #db152e, rgba(168, 7, 7, 0.6) 15%, transparent 60%, #db152e);
border-radius: 20rpx;
}
}
}
.industry--per {
font-size: 20rpx;
color: rgba(0, 0, 0, 0.8);
transform: scale(.8);
}
}
.industry--tool {
position: absolute;
top: 60rpx;
right: $padding;
.industry--deal {
width: 120rpx;
height: 120rpx;
text-align: center;
background: linear-gradient(to bottom, #e1293f, #f85d31);
box-shadow: 0 0 14rpx rgba(260, 60, 80, .9);
color: #FFFFFF;
font-size: 26rpx;
border-radius: 50%;
padding-top: 25rpx;
box-sizing: border-box;
font-size: 24rpx;
.industry--number {
font-size: 28rpx;
font-weight: 600;
display: block;
margin-top: 4rpx;
}
}
}
.industry--credibility{
padding-top: 8rpx;
height: 48rpx;
box-sizing: border-box;
}
.industry--arrow{
position: absolute;
right: $margin;
top: 50%;
margin-top: -7px;
}
}
// 数据空
.industry--null{
width: 100%;
padding: 20vh 0;
text-align: center;
font-size: $title-size-m;
color: $text-gray;
}
</style>

View File

@@ -0,0 +1,342 @@
<template>
<view class="boos">
<!-- 店铺交易数据 -->
<view class="statistical">
<!-- <view class="item">
<view class="number">{{wordData.top.barter_total || 0}}</view>
<view class="text">总易货额</view>
</view>
<view class="item">
<view class="number">{{wordData.top.trading_day || 0}}</view>
<view class="text">今日交易额</view>
</view>
<view class="item">
<view class="number">{{wordData.top.eb_in || 0}}</view>
<view class="text">E货额度收入</view>
</view>
<view class="item">
<view class="number">{{wordData.top.cash_in || 0}}</view>
<view class="text">现金收入</view>
</view> -->
</view>
<!-- 店铺概况 -->
<view class="general">
<view class="general-box member">
<view class="member-name">
会员中心
</view>
<view class="member-cont">
<view class="member-tips">
<view class="member-tips-title">会员升级/续费</view>
<view class="member-tips-time">有效期2022年10月1日</view>
</view>
<view class="member-btn" @click="$Router.push({name: 'Vip'})">
<image class="member-btn-icon" src="../../static/icons/store_icon_vip.png" mode="aspectFill"></image>
<view class="member-btn-name">去升级</view>
</view>
</view>
</view>
<view class="general-box">
<view class="general-item" @click="$Router.push({name: 'Visitors'})">
<view class="number">{{wordData.middle.visitors || 0}}</view>
<view class="text">访客统计</view>
</view>
<view class="general-item" @click="$Router.push({name: 'Customer'})">
<view class="number">{{wordData.middle.clinch || 0}}</view>
<view class="text">成交客户</view>
</view>
<!-- <view class="general-item" @click="$Router.push({name: 'Employees'})">
<view class="number">{{wordData.middle.employees || 0}}</view>
<view class="text">员工数量</view>
</view>
<view class="general-item">
<view class="number">{{wordData.middle.hold || 0}}</view>
<view class="text">权证持有</view>
</view>
<view class="general-item">
<view class="number">{{wordData.middle.transfer || 0}}</view>
<view class="text">转让权证</view>
</view> -->
<view class="general-item">
<view class="number">{{wordData.top.barter_total || 0}}</view>
<view class="text">总收益额</view>
</view>
<view class="general-item" @click="$Router.push({name: 'GoodsMag'})">
<view class="number">{{wordData.middle.sale || 0}}</view>
<view class="text">在售权证</view>
</view>
<view class="general-item">
<view class="number">{{wordData.top.barter_total || 0}}</view>
<view class="text">退货单处理</view>
</view>
<view class="general-item">
<view class="number">{{wordData.top.barter_total || 0}}</view>
<view class="text">发货单处理</view>
</view>
</view>
</view>
<!-- 店铺订单管理 -->
<!-- <view class="tool-flex order">
<view class="order-item" @click="$Router.push({name: 'Order', params: {type: 'deliver'}})">
<view class="number" v-if="wordData.order.not_shipped > 0">{{wordData.order.not_shipped}}</view>
<image class="icon" src="@/static/icons/order_icon_01.png" mode="aspectFill"></image>
<view class="title">待发货</view>
</view>
<view class="order-item" @click="$Router.push({name: 'Order', params: {type: 'shipped'}})">
<view class="number" v-if="wordData.order.already_shipped > 0">{{wordData.order.already_shipped}}</view>
<image class="icon" src="@/static/icons/order_icon_02.png" mode="aspectFill"></image>
<view class="title">已发货</view>
</view>
<view class="order-item" @click="$Router.push({name: 'Order', params: {type: 'sign'}})">
<view class="number" v-if="wordData.order.not_pick > 0">{{wordData.order.not_pick}}</view>
<image class="icon" src="@/static/icons/order_icon_03.png" mode="aspectFill"></image>
<view class="title">待提货</view>
</view>
<view class="order-item" @click="$Router.push({name: 'Order', params: {type: 'take'}})">
<view class="number" v-if="wordData.order.already_pick > 0">{{wordData.order.already_pick}}</view>
<image class="icon" src="@/static/icons/order_icon_00.png" mode="aspectFill"></image>
<view class="title">已提货</view>
</view>
<view class="order-item" @click="$Router.push({name: 'Sales'})">
<view class="number" v-if="wordData.order.after_sale > 0">{{wordData.order.after_sale}}</view>
<image class="icon" src="@/static/icons/order_icon_04.png" mode="aspectFill"></image>
<view class="title">退换货</view>
</view>
</view> -->
<!-- 店铺工具 -->
<view class="tool-flex store">
<view class="store-item" @click="$Router.push({name: 'Verification'})">
<image class="icon" src="@/static/icons/tool_icon_00.png" mode="aspectFill"></image>
<view class="title">扫码验证</view>
</view>
<view class="store-item" @click="$Router.push({name: 'GoodsMag'})">
<image class="icon" src="@/static/icons/tool_icon_01.png" mode="aspectFill"></image>
<view class="title">商品权证</view>
</view>
<view class="store-item" @click="$Router.push({name: 'CouponsMag'})">
<image class="icon" src="@/static/icons/tool_icon_02.png" mode="aspectFill"></image>
<view class="title">优惠券管理</view>
</view>
<view class="store-item" @click="$Router.push({name: 'Collection'})">
<image class="icon" src="@/static/icons/tool_icon_03.png" mode="aspectFill"></image>
<view class="title">收款管理</view>
</view>
</view>
<!-- 企业营销工具管理 -->
<view class="tool-flex store">
<view class="store-item" @click="$Router.push({name: 'Basics'})">
<image class="icon" src="@/static/icons/tool_icon_04.png" mode="aspectFill"></image>
<view class="title">基础信息</view>
</view>
<!-- <view class="store-item">
<image class="icon" src="@/static/icons/tool_icon_05.png" mode="aspectFill"></image>
<view class="title">智能名片</view>
</view> -->
<view class="store-item">
<image class="icon" src="@/static/icons/tool_icon_06.png" mode="aspectFill"></image>
<view class="title">营销推广码</view>
</view>
<view class="store-item" @click="$Router.push({name: 'shopLists'})">
<image class="icon" src="@/static/icons/tool_icon_07.png" mode="aspectFill"></image>
<view class="title">部门/门店</view>
</view>
<view class="store-item" @click="$Router.push({name: 'Employees'})">
<image class="icon" src="@/static/icons/tool_icon_08.png" mode="aspectFill"></image>
<view class="title">员工管理</view>
</view>
</view>
</view>
</template>
<script>
export default {
name:"store-boss",
props:{
// 工作台信息
wordData:{
type: Object,
default: () => {
return {
top : {},
middle : {},
order : {}
}
}
}
}
}
</script>
<style lang="scss" scoped>
.boos{
// 店铺统计
.statistical{
display: flex;
background: $text-price;
padding: $padding ($padding/2) $padding*2;
flex-wrap: wrap;
justify-content: space-between;
.item{
width: calc(25% - #{$padding});
text-align: center;
color: white;
margin: 0 $margin / 2;
.number{
font-weight: bold;
font-size: $title-size;
}
.text{
font-size: $title-size-sm;
}
}
}
// 店铺概况
.general{
margin: -$margin*2 $margin 0 $margin;
.general-box{
background-color: white;
border-radius: $radius/2;
display: flex;
padding: $padding $padding/2;
flex-wrap: wrap;
.general-item{
width: 33.33%;
text-align: center;
padding: $padding/2;
box-sizing: border-box;
.number{
font-weight: bold;
font-size: $title-size;
line-height: 50rpx;
}
.text{
font-size: $title-size-sm;
color: $text-gray;
line-height: 40rpx;
}
}
}
// 新增
.member {
margin-bottom: $margin;
font-size: 26rpx;
padding: 0;
box-sizing: border-box;
background-image: linear-gradient(to right, #fbdba7, #ddb264);
position: relative;
color: #533108;
height: 120rpx;
.member-name {
width: 160rpx;
line-height: 120rpx;
text-align: center;
font-size: 28rpx;
font-weight: 700;
position: relative;
&::after {
position: absolute;
content: '';
right: 0;
top: 25%;
height: 50%;
width: 4rpx;
background-color: #d8b66f;
}
}
.member-cont {
display: flex;
position: absolute;
left: 0;
top: 0;
width: 100%;
padding-left: 180rpx;
padding-right: 20rpx;
box-sizing: border-box;
.member-tips {
flex: 1;
padding-top: 30rpx;
.member-tips-time {
font-size: 26rpx;
}
}
.member-btn{
height: 64rpx;
line-height: 64rpx;
border-radius: 10rpx;
margin-top: 30rpx;
padding: 0 15rpx;
background-image: linear-gradient(to right, #6f747f, #3e4655);
display: flex;
.member-btn-name {
font-size: 26rpx;
font-weight: 700;
background: linear-gradient(to right, #f6d8ac, #c39f63);
-webkit-background-clip: text;
color: transparent;
}
.member-btn-icon {
width: 32rpx;
height: 32rpx;
margin: 15rpx 10rpx 0 0;
}
}
}
}
}
// 店铺工具
.tool-flex{
background: white;
border-radius: $radius/2;
padding: $padding/2;
margin: $margin;
display: flex;
flex-wrap: wrap;
.store-item{
padding: $padding/2;
text-align: center;
width: 25%;
box-sizing: border-box;
.icon{
width: 68rpx;
height: 68rpx;
vertical-align: top;
}
.title{
font-size: $title-size-sm;
color: $text-gray;
padding-top: $padding/3;
}
}
.order-item{
position: relative;
padding: $padding/2;
text-align: center;
width: 20%;
box-sizing: border-box;
.icon{
width: 58rpx;
height: 58rpx;
vertical-align: top;
}
.title{
font-size: $title-size-sm;
color: $text-gray;
}
.number{
position: absolute;
top: 10rpx;
left: calc( 50% + 10rpx );
font-size: $title-size-sm;
background: $text-price;
color: white;
height: 30rpx;
line-height: 30rpx;
border-radius: 15rpx;
min-width: 30rpx;
z-index: 9;
}
}
}
}
</style>

View File

@@ -0,0 +1,159 @@
<template>
<view class="staff">
员工
</view>
</template>
<script>
export default {
name:"store-staff",
props:{
// 店铺统计
top: {
type: Object,
default: ()=> {
return {
barter_total: 0,
trading_day : 0,
eb_in : 0,
cash_in : 0
}
}
},
// 店铺概况
middle: {
type: Object,
default: ()=> {
return {
visitors : 0,
clinch : 0,
employees: 0,
sale : 0,
hold : 0,
transfer : 0
}
}
},
// 店铺订单
order: {
type: Object,
default: ()=> {
return {
not_shipped : 0,
already_shipped : 0,
not_pick : 0,
already_pick : 0,
after_sale : 0
}
}
}
}
}
</script>
<style lang="scss" scoped>
.staff{
// 店铺统计
.statistical{
display: flex;
background: $text-price;
padding: $padding ($padding/2) $padding*5;
flex-wrap: wrap;
justify-content: space-between;
.item{
width: calc(25% - #{$padding});
text-align: center;
color: white;
margin: 0 $margin / 2;
.number{
font-weight: bold;
font-size: $title-size;
}
.text{
font-size: $title-size-sm;
}
}
}
// 店铺概况
.general{
margin: -$margin*4 $margin 0 $margin;
.general-box{
background-color: white;
border-radius: $radius/2;
display: flex;
padding: $padding $padding/2;
flex-wrap: wrap;
.general-item{
width: 33.33%;
text-align: center;
padding: $padding/2;
box-sizing: border-box;
.number{
font-weight: bold;
font-size: $title-size;
line-height: 50rpx;
}
.text{
font-size: $title-size-sm;
color: $text-gray;
line-height: 40rpx;
}
}
}
}
// 店铺工具
.tool-flex{
background: white;
border-radius: $radius/2;
padding: $padding/2;
margin: $margin;
display: flex;
flex-wrap: wrap;
.store-item{
padding: $padding/2;
text-align: center;
width: 25%;
box-sizing: border-box;
.icon{
width: 68rpx;
height: 68rpx;
vertical-align: top;
}
.title{
font-size: $title-size-sm;
color: $text-gray;
padding-top: $padding/3;
}
}
.order-item{
position: relative;
padding: $padding/2;
text-align: center;
width: 20%;
box-sizing: border-box;
.icon{
width: 58rpx;
height: 58rpx;
vertical-align: top;
}
.title{
font-size: $title-size-sm;
color: $text-gray;
}
.number{
position: absolute;
top: 10rpx;
left: calc( 50% + 10rpx );
font-size: $title-size-sm;
background: $text-price;
color: white;
height: 30rpx;
line-height: 30rpx;
border-radius: 15rpx;
min-width: 30rpx;
z-index: 9;
}
}
}
}
</style>

View File

@@ -0,0 +1,904 @@
<template>
<view v-if="isShow" class="picker">
<view @click="onCancel" style="position: fixed;width: 100%;height: 100%;left: 0;top: 0;background: rgba(60,60,60,0.5)"></view>
<!-- 日期选择器 -->
<view v-if="type!='time'" :class="isShow?'myfirst':''" class="picker-modal">
<view class="picker-header">
<view class="close" @click="onCancel" style="font-size: 22px;width: 50upx;"></view>
<view style="flex: 1;font-size: 16px; text-align: center;color: rgb(102,101,91);">选择日期</view>
<view style="width: 50upx;"></view>
</view>
<view class="picker-week-header">
<view v-for="item in weekArr">{{item}}</view>
</view>
<scroll-view class="picker-modal-body" @scroll="scrollPage" scroll-y="true" id="target">
<view class="picker-modal-header-title" style="position: fixed;height: 93upx;line-height: 93upx;margin-top:-1px;right: 1px;background: #fff;z-index: 999;">{{dateTitleArr[scrollIndex]}}</view>
<view class="picker-calendar-box" :id="'calendar_module'+calendar_Index" v-for="(calendar,calendar_Index) in calendars"
:key="calendar_Index">
<view class="picker-modal-header-title" :class="" style="color:rgb(77,77,77); font-size: 16px; width: 100%;height: 90upx;line-height: 90upx;text-align: center;">{{dateTitleArr[calendar_Index]}}</view>
<view class="picker-calendar">
<view class="picker-calendar-view" v-for="(date,dateIndex) in calendar" :key="dateIndex">
<view v-show="!date.isOtherMonth || calendar_Index == 0" @click="onSelectDate(date)">
<!-- 背景样式 -->
<view v-show="date.bgStyle.type" :class="'picker-calendar-view-'+date.bgStyle.type" :style="{background: date.bgStyle.background}"></view>
<!-- 正常和选中样式 -->
<view class="picker-calendar-view-item" :style="{opacity: date.statusStyle.opacity, color: date.statusStyle.color, background: date.statusStyle.background}">
<text>{{date.title}}</text>
</view>
<!-- 小圆点样式 -->
<view class="picker-calendar-view-dot" :style="{opacity: date.dotStyle.opacity, background: date.dotStyle.background}"></view>
<!-- 信息样式 -->
<view v-show="date.tips" class="picker-calendar-view-tips">{{date.tips}}</view>
</view>
</view>
</view>
</view>
<view class="picker-modal-footer" v-show="checkeds.length==2" style="position: fixed;bottom: 0;z-index: 1002;">
<view style="position: absolute;width: 100%;height: 100%;background: #fff;z-index: -1;left:0;top:1px;"></view>
<view class="picker-modal-footer-btnOK" :style="{'background':color}" @click="onConfirm">完成</view>
</view>
<view class="picker-modal-footer" v-show="checkeds.length==2"></view>
</scroll-view>
</view>
</view>
</template>
<script>
/**
* 工具函数库
*/
const DateTools = {
/**
* 获取公历节日
* @param date Date对象
*/
getHoliday(date) {
let holidays = {
'0101': '元旦',
'0214': '情人',
'0308': '妇女',
'0312': '植树',
'0401': '愚人',
'0501': '劳动',
'0504': '青年',
'0601': '儿童',
'0701': '建党',
'0801': '建军',
'0903': '抗日',
'0910': '教师',
'1001': '国庆',
'1031': '万圣',
'1224': '平安',
'1225': '圣诞'
};
let value = this.format(date, 'mmdd');
if (holidays[value]) return holidays[value];
return false;
},
/**
* 解析标准日期格式
* @param s 日期字符串
* @return 返回Date对象
*/
parse: s => new Date(s.replace(/(年|月|-)/g, '/').replace(/(日)/g, '')),
/**
* 比较日期是否为同一天
* @param a Date对象
* @param b Date对象
* @return Boolean
*/
isSameDay: (a, b) => a.getMonth() == b.getMonth() && a.getFullYear() == b.getFullYear() && a.getDate() == b.getDate(),
/**
* 格式化Date对象
* @param d 日期对象
* @param f 格式字符串
* @return 返回格式化后的字符串
*/
format(d, f) {
var o = {
"m+": d.getMonth() + 1,
"d+": d.getDate(),
"h+": d.getHours(),
"i+": d.getMinutes(),
"s+": d.getSeconds(),
"q+": Math.floor((d.getMonth() + 3) / 3),
};
if (/(y+)/.test(f))
f = f.replace(RegExp.$1, (d.getFullYear() + "").substr(4 - RegExp.$1.length));
for (var k in o)
if (new RegExp("(" + k + ")").test(f))
f = f.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
return f;
},
/**
* 用于format格式化后的反解析
* @param s 日期字符串
* @param f 格式字符串
* @return 返回Date对象
*/
inverse(s, f) {
var o = {
"y": '',
"m": '',
"d": '',
"h": '',
"i": '',
"s": '',
};
let d = new Date();
if (s.length != f.length) return d;
for (let i in f)
if (o[f[i]] != undefined) o[f[i]] += s[i];
if (o.y) d.setFullYear(o.y.length < 4 ? (d.getFullYear() + '').substr(0, 4 - o.y.length) + o.y : o.y);
o.m && d.setMonth(o.m - 1, 1);
o.d && d.setDate(o.d - 0);
o.h && d.setHours(o.h - 0);
o.i && d.setMinutes(o.i - 0);
o.s && d.setSeconds(o.s - 0);
return d;
},
/**
* 获取日历数组42天
* @param date 日期对象或日期字符串
* @param proc 处理日历(和forEach类似)传递一个数组中的item
* @return Array
*/
getCalendar(date, proc,beforeDateDisable) {
let it = new Date(date),
calendars = [];
let nowDate = new Date(new Date(new Date().toLocaleDateString()).getTime());
it.setDate(1);
if(it.getDay()>0){
it.setDate(it.getDate() - (it.getDay() == 0 ? 7 : it.getDay())); //偏移量
}
for (let i = 0; i < 42; i++) {
let tmp = {
dateObj: new Date(it),
title: it.getDate(),
isOtherMonth: it.getMonth() < date.getMonth() || it.getMonth() > date.getMonth()
};
if(beforeDateDisable){
tmp.isBeforeNowDay = it<nowDate;
}
if (tmp.title == 1 && calendars.length > 7) {
break;
}
calendars.push(Object.assign(tmp, proc ? proc(tmp) : {}));
it.setDate(it.getDate() + 1);
}
return calendars;
},
/**
* 获取日期到指定的月份1号(不改变原来的date对象)
* @param d Date对象
* @param v 指定的月份
* @return Date对象
*/
getDateToMonth(d, v) {
let n = new Date(d);
n.setMonth(v, 1);
return n;
},
/**
* 把时间数组转为时间字符串
* @param t Array[时,分,秒]
* @param showSecinds 是否显示秒
* @return 字符串 时:分[:秒]
*/
formatTimeArray(t, s) {
let r = [...t];
if (!s) r.length = 2;
r.forEach((v, k) => r[k] = ('0' + v).slice(-2));
return r.join(':');
}
};
export default {
props: {
//颜色
color: {
type: String,
default: 'rgb(230,86,86)'
},
//显示未来几个月
beforeDateDisable: {
type: Boolean,
default: false
},
//显示未来几个月
monthNum: {
type: Number,
default: 6
},
//是否显示秒 针对type为datetime或time时生效
showSeconds: {
type: Boolean,
default: false
},
//初始的值
value: [String, Array],
//类型date time datetime range rangetime
type: {
type: String,
default: 'range'
},
//是否显示
show: {
type: Boolean,
default: false
},
//初始格式
format: {
type: String,
default: ''
},
//显示公历节日
showHoliday: {
type: Boolean,
default: true
},
//显示提示
showTips: {
type: Boolean,
default: false
},
//开始文案 针对type为范围选择时生效
beginText: {
type: String,
default: '开始'
},
//结束文案 针对type为范围选择时生效
endText: {
type: String,
default: '结束'
}
},
data() {
return {
isShow: false, //是否显示
isMultiSelect: false, //是否为多选
isContainTime: false, //是否包含时间
date: {}, //当前日期对象
weekArr: ["日", "一", "二", "三", "四", "五", "六"],
scrollTopArr: [],
scrollIndex: 0,
dateTitleArr: [],
title: '初始化', //标题
calendars: [], //日历数组
calendarIndex: 1, //当前日历索引
checkeds: [], //选中的日期对象集合
showTimePicker: false, //是否显示时间选择器
timeValue: [0, 0, 0], //时间选择器的值
timeType: 'begin', //当前时间选择的类型
beginTime: [0, 0, 0], //当前所选的开始时间值
endTime: [0, 0, 0], //当前所选的结束时间值
};
},
methods: {
//监听滚动条
scrollPage(e) {
let scrollIndex = 0;
if (this.scrollTopArr.length >= 6) {
this.scrollTopArr.some(function(item, index) {
if (e.detail.scrollTop >= item) {
scrollIndex = index;
}
})
}
this.scrollIndex = scrollIndex;
},
//设置值
setValue(value) {
this.date = new Date();
this.checkeds = [];
this.isMultiSelect = this.type.indexOf('range') >= 0;
this.isContainTime = this.type.indexOf('time') >= 0;
//将字符串解析为Date对象
let parseDateStr = (str) => (this.format ? DateTools.inverse(str, this.format) : DateTools.parse(str));
if (value) {
if (this.isMultiSelect) {
Array.isArray(value) && value.forEach((dateStr, index) => {
let date = parseDateStr(dateStr);
let time = [date.getHours(), date.getMinutes(), date.getSeconds()];
if (index == 0) this.beginTime = time;
else this.endTime = time;
this.checkeds.push(date);
});
} else {
if (this.type == 'time') {
let date = parseDateStr('2019/1/1 ' + value);
this.beginTime = [date.getHours(), date.getMinutes(), date.getSeconds()];
this.onShowTimePicker('begin');
} else {
this.checkeds.push(parseDateStr(value));
if (this.isContainTime) this.beginTime = [
this.checkeds[0].getHours(),
this.checkeds[0].getMinutes(),
this.checkeds[0].getSeconds()
];
}
}
if (this.checkeds.length) this.date = new Date(this.checkeds[0]);
} else {
if (this.isContainTime) {
this.beginTime = [this.date.getHours(), this.date.getMinutes(), this.date.getSeconds()];
if (this.isMultiSelect) this.endTime = [...this.beginTime];
}
this.checkeds.push(new Date(this.date));
}
if (this.type != 'time') this.refreshCalendars(true);
else this.onShowTimePicker('begin');
},
//设置时间选择器的显示状态
onShowTimePicker(type) {
this.showTimePicker = true;
this.timeType = type;
this.timeValue = type == 'begin' ? [...this.beginTime] : [...this.endTime];
},
//处理日历
procCalendar(item) {
//定义初始样式
item.statusStyle = {
opacity: 1,
color: item.isOtherMonth ? '#ddd' : 'rgb(108,108,108)',
background: 'transparent'
};
if(item.isBeforeNowDay || item.isOtherMonth){
item.statusStyle.color = '#ddd';
item.isGray = true;
}
item.bgStyle = {
type: '',
background: 'transparent'
};
item.dotStyle = {
opacity: 1,
background: 'transparent'
};
item.tips = "";
//标记今天的日期
if (DateTools.isSameDay(new Date(), item.dateObj)) {
item.statusStyle.color = this.color;
if (item.isOtherMonth) item.statusStyle.opacity = 0.3;
}
//标记选中项
this.checkeds.forEach(date => {
if (DateTools.isSameDay(date, item.dateObj)) {
item.statusStyle.background = this.color;
item.statusStyle.color = '#fff';
item.statusStyle.opacity = 1;
if (this.isMultiSelect && this.showTips) item.tips = this.beginText;
}
});
//节假日或今日的日期标点
if (item.statusStyle.background != this.color) {
let holiday = this.showHoliday ? DateTools.getHoliday(item.dateObj) : false;
if (holiday || DateTools.isSameDay(new Date(), item.dateObj)) {
item.title = holiday || item.title;
item.dotStyle.background = this.color;
if (item.isOtherMonth) item.dotStyle.opacity = 0.2;
}
} else {
item.title = item.dateObj.getDate();
}
//有两个日期
if (this.checkeds.length == 2) {
if (DateTools.isSameDay(this.checkeds[0], item.dateObj)) { //开始日期
item.bgStyle.type = 'bgbegin';
}
if (DateTools.isSameDay(this.checkeds[1], item.dateObj)) { //结束日期
if (this.isMultiSelect && this.showTips) item.tips = item.bgStyle.type ? this.beginText + ' / ' + this.endText :
this.endText;
if (!item.bgStyle.type) { //开始日期不等于结束日期
item.bgStyle.type = 'bgend';
} else {
item.bgStyle.type = '';
}
}
if (!item.bgStyle.type && (+item.dateObj > +this.checkeds[0] && +item.dateObj < +this.checkeds[1])) { //中间的日期
item.bgStyle.type = 'bg';
item.statusStyle.color = this.color;
}
if (item.bgStyle.type) {
item.bgStyle.background = this.color;
item.dotStyle.opacity = 1;
item.statusStyle.opacity = 1;
}
}
},
//刷新日历
refreshCalendars(refresh = false) {
let date = new Date(this.date);
let before = DateTools.getDateToMonth(date, date.getMonth() - 1);
let after = DateTools.getDateToMonth(date, date.getMonth() + 1);
let _this = this;
let mArr = [];
for (let m_num = 0; m_num < this.monthNum; m_num++) {
let new_date = DateTools.getDateToMonth(date, date.getMonth() + m_num);
_this.dateTitleArr.push(DateTools.format(new_date, 'yyyy年mm月'))
mArr.push(new_date)
}
mArr.some(function(item, index) {
_this.calendars.splice(index, 1, DateTools.getCalendar(item, _this.procCalendar,_this.beforeDateDisable));
})
this.title = DateTools.format(this.date, 'yyyy年mm月');
setTimeout(function(){
let domArr = [];
let h = 0;
const query = uni.createSelectorQuery().in(_this);
for (let m_domNum = 0; m_domNum < _this.monthNum; m_domNum++) {
let className = '#calendar_module' + m_domNum;
let view = uni.createSelectorQuery().select(className);
//console.log(view);
view.fields({
size: true, // 是否返回节点尺寸
}, data => { // data是方法的回调函数参数是指定的相关节点信息。
// console.log(data);
let model = {};
model.top = h; // 顶部高度
h += data.height;
model.bottom = h; // 底部高度
domArr.push(model.top);
}).exec();
}
_this.scrollTopArr = domArr;
},100)
},
//选中日期
onSelectDate(date) {
if(date.isGray){
return;
}
if (~this.type.indexOf('range') && this.checkeds.length == 2) this.checkeds = [];
else if (!(~this.type.indexOf('range')) && this.checkeds.length) this.checkeds = [];
this.checkeds.push(new Date(date.dateObj));
this.checkeds.sort((a, b) => a - b); //从小到大排序
this.calendars.forEach(calendar => {
calendar.forEach(this.procCalendar); //重新处理
});
},
//时间选择取消
onCancelTime() {
this.showTimePicker = false;
this.type == 'time' && this.onCancel();
},
//时间选择确定
onConfirmTime() {
if (this.timeType == 'begin') this.beginTime = this.timeValue;
else this.endTime = this.timeValue;
this.showTimePicker = false;
this.type == 'time' && this.onConfirm();
},
//取消
onCancel() {
this.$emit('cancel', false);
},
//确定
onConfirm() {
let result = {
value: null,
date: null
};
//定义默认格式
let defaultFormat = {
'date': 'yyyy/mm/dd',
'time': 'hh:ii' + (this.showSeconds ? ':ss' : ''),
'datetime': ''
};
defaultFormat['datetime'] = defaultFormat.date + ' ' + defaultFormat.time;
let fillTime = (date, timeArr) => {
date.setHours(timeArr[0], timeArr[1]);
if (this.showSeconds) date.setSeconds(timeArr[2]);
};
if (this.type == 'time') {
let date = new Date();
fillTime(date, this.beginTime);
result.value = DateTools.format(date, this.format ? this.format : defaultFormat.time);
result.date = date;
} else {
if (this.isMultiSelect) {
let values = [],
dates = [];
if (this.checkeds.length < 2) return uni.showToast({
icon: 'none',
title: '请选择两个日期'
});
this.checkeds.forEach((date, index) => {
let newDate = new Date(date);
if (this.isContainTime) {
let time = [this.beginTime, this.endTime];
fillTime(newDate, time[index]);
}
values.push(DateTools.format(newDate, this.format ? this.format : defaultFormat[this.isContainTime ?
'datetime' : 'date']));
dates.push(newDate);
});
result.value = values;
result.date = dates;
} else {
let newDate = new Date(this.checkeds[0]);
if (this.isContainTime) {
newDate.setHours(this.beginTime[0], this.beginTime[1]);
if (this.showSeconds) newDate.setSeconds(this.beginTime[2]);
}
result.value = DateTools.format(newDate, this.format ? this.format : defaultFormat[this.isContainTime ?
'datetime' : 'date']);
result.date = newDate;
}
}
this.$emit('confirm', result);
}
},
computed: {
BeginTitle() {
let value = '未选择';
if (this.checkeds.length) value = DateTools.format(this.checkeds[0], 'yy/mm/dd');
return value;
},
EndTitle() {
let value = '未选择';
if (this.checkeds.length == 2) value = DateTools.format(this.checkeds[1], 'yy/mm/dd');
return value;
}
},
watch: {
show(newValue, oldValue) {
newValue && this.setValue(this.value);
this.isShow = newValue;
},
value(newValue, oldValue) {
setTimeout(() => {
this.setValue(newValue);
}, 0);
}
}
}
</script>
<style lang="scss" scoped>
.close {
position: relative;
width: 35upx;
height: 35upx;
}
.close::before,
.close::after {
position: absolute;
content: ' ';
background-color: rgb(101, 100, 91);
left: 20upx;
width: 1px;
height: 35upx;
}
.close::before {
transform: rotate(45deg);
}
.close::after {
transform: rotate(-45deg);
}
@keyframes myfirst
{
0% {top: 100vh;}
100% {top: 30vh;}
}
@-moz-keyframes myfirst /* Firefox */
{
0% {top: 100vh;}
100% {top: 30vh;}
}
@-webkit-keyframes myfirst /* Safari 和 Chrome */
{
0% {top: 100vh;}
100% {top: 30vh;}
}
@-o-keyframes myfirst /* Opera */
{
0% {top: 100vh;}
100% {top: 30vh;}
}
$z-index: 100;
$cell-spacing: 20upx;
$calendar-size: 630upx;
$calendar-item-size: 100upx;
.picker {
position: fixed;
z-index: $z-index;
background: rgba(60, 60, 60, 0);
left: 0;
top: 0;
width: 100%;
height: 100%;
font-size: 28upx;
&-header {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
width: 100%;
padding: 20upx 30upx;
box-sizing: border-box;
}
&-week-header {
font-size: 16px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
border-bottom: 2px solid rgb(244, 244, 244);
padding: 10px 20px;
color: rgb(136, 136, 136);
font-size: 14px;
}
&-week-header>view:first-child {
color: rgb(214, 142, 135);
}
&-week-header>view:last-child {
color: rgb(214, 142, 135);
}
&-btn {
padding: $cell-spacing*0.5 $cell-spacing;
border-radius: 12upx;
color: #666;
&-active {
background: rgba(0, 0, 0, .1);
}
}
&-display {
color: #666;
&-text {
color: #000;
margin: 0 $cell-spacing*0.5;
}
&-link {
display: inline-block;
&-active {
background: rgba(0, 0, 0, .1);
}
}
}
&-time {
width: 100%; //$calendar-size - 80upx !important;
left: ((750upx - $calendar-size) / 2 + 40upx) !important;
}
&-modal {
background: #fff;
box-sizing: border-box;
position: fixed;
bottom: 0;
left: 0; //(750upx - $calendar-size) / 2;
width: 100%; //$calendar-size;
box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.1);
border-radius: 12upx;
display: flex;
flex-direction: column;
justify-content: flex-start;
height: 70vh;
animation: myfirst 0.5s;
-moz-animation: myfirst 0.5s; /* Firefox */
-webkit-animation: myfirst 0.5s; /* Safari 和 Chrome */
-o-animation: myfirst 0.5s; /* Opera */
&-header {
text-align: center;
line-height: 80upx;
font-size: 32upx;
&-title {
display: inline-block;
width: 40%;
color: rgb(77, 77, 77);
font-size: 16px;
width: 100%;
height: 90upx;
line-height: 90upx;
text-align: center;
border-bottom: 1px solid rgb(244, 244, 244);
}
.picker-icon {
display: inline-block;
line-height: 50upx;
width: 50upx;
height: 50upx;
border-radius: 50upx;
text-align: center;
margin: 10upx;
background: #fff;
font-size: 36upx;
&-active {
background: rgba(0, 0, 0, .1);
}
}
}
&-body {
width: 100%; //$calendar-size !important;
position: relative;
overflow: auto;
}
&-time {
width: 100%;
height: 180upx;
text-align: center;
line-height: 60upx;
}
&-footer {
display: flex;
justify-content: space-between;
align-items: center;
padding: $cell-spacing;
height: 130upx;
width: 100%;
box-sizing: border-box;
border-top: 1px solid rgb(244, 244, 244);
&-btnOK {
width: calc(100% - 100upx);
height: 100%;
margin: 0 auto;
border-radius: 6upx;
display: flex;
justify-content: center;
align-items: center;
font-size: 16px;
color: #fff;
}
&-info {
flex-grow: 1;
}
&-btn {
flex-shrink: 0;
display: flex;
}
}
}
&-calendar {
width: 100%;
height: calc(100% - 90upx);
display: flex;
align-items: center;
justify-content: flex-start;
flex-wrap: wrap;
flex-direction: row;
&-box {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
}
&-view:nth-child(7n){
width: 14.2%;
}
&-view {
position: relative;
width: 14.3%; //$calendar-item-size;
height: $calendar-item-size;
text-align: center;
&-bgbegin,
&-bg,
&-bgend,
&-item,
&-dot,
&-tips {
position: absolute;
transition: .2s;
}
&-bgbegin,
&-bg,
&-bgend {
opacity: .15;
height: 100%;
}
&-bg {
left: 0;
top: 0%;
width: 100%;
}
&-bgbegin {
border-radius: $calendar-item-size 0 0 $calendar-item-size;
top: 0%;
left: 0%;
width: 100%;
}
&-bgend {
border-radius: 0 $calendar-item-size $calendar-item-size 0;
top: 0%;
left: 0%;
width: 100%;
}
&-item {
left: 0%;
top: 0%;
width: 100%;
height: 100%;
border-radius: 5px;
display: flex;
align-items: center;
justify-content: center;
}
&-dot {
right: 10%;
top: 10%;
width: 12upx;
height: 12upx;
border-radius: 12upx;
}
&-tips {
bottom: 100%;
left: 50%;
transform: translateX(-50%);
background: #4E4B46;
color: #fff;
border-radius: 12upx;
padding: 10upx 20upx;
font-size: 24upx;
width: max-content;
margin-bottom: 5px;
pointer-events: none;
z-index: 1000;
&:after {
content: "";
position: absolute;
top: 100%;
left: 50%;
transform: translateX(-50%);
width: 0;
height: 0;
border-style: solid;
border-width: 5px 5px 0 5px;
border-color: #4E4B46 transparent transparent transparent;
}
}
}
}
}
</style>

View File

@@ -0,0 +1,93 @@
<template>
<swiper class="image-container" previous-margin="45rpx" next-margin="45rpx" circular @change="swiperChange">
<swiper-item :class="currentIndex == index ? 'swiper-item' : 'swiper-item-side'" v-for="(item, index) in imgList" :key="item[urlKey]">
<image @click="clickImg(item)" :class="currentIndex == index ? 'item-img' : 'item-img-side'" :src="item[urlKey]" lazy-load :style="dontFirstAnimation ? 'animation: none;' : ''" mode="aspectFill"></image>
</swiper-item>
</swiper>
</template>
<script>
export default {
props: {
imgList: {
type: Array,
default() {
return []
}
},
urlKey: {
type: String,
default() {
return ''
}
},
},
data() {
return {
currentIndex: 0,
dontFirstAnimation: true
}
},
methods: {
swiperChange(e) {
this.dontFirstAnimation = false
this.currentIndex = e.detail.current
},
clickImg(item) {
this.$emit('selected', item, this.currentIndex)
}
}
}
</script>
<style scoped>
.image-container {
width: 750rpx;
height: 350rpx;
}
.item-img {
width: 630rpx;
height: 300rpx;
border-radius: 14rpx;
animation: to-big .3s;
}
.swiper-item {
width: 630rpx;
height: 300rpx;
display: flex;
justify-content: center;
align-items: center;
}
.item-img-side {
width: 630rpx;
height: 260rpx;
border-radius: 14rpx;
animation: to-mini .3s;
}
.swiper-item-side {
width: 630rpx;
height: 260rpx;
display: flex;
justify-content: center;
align-items: center;
}
@keyframes to-mini
{
from {
height: 300rpx;
}
to {
height: 260rpx;
}
}
@keyframes to-big
{
from {
height: 260rpx;
}
to {
height: 300rpx;
}
}
</style>

View File

@@ -0,0 +1,23 @@
import Vue from 'vue'
import App from './App'
import { router, RouterMount } from './router'
import store from './store'
Vue.use(router)
Vue.config.productionTip = false
Vue.prototype.$store = store
App.mpType = 'app'
const app = new Vue({
...App
})
//v1.3.5起 H5端 你应该去除原有的app.$mount();使用路由自带的渲染方式
// #ifdef H5
RouterMount(app,router,'#app')
// #endif
// #ifndef H5
app.$mount(); //为了兼容小程序及app端必须这样写才有效果
// #endif

View File

@@ -0,0 +1,141 @@
{
"name" : "易品新境",
"appid" : "__UNI__CD19AAD",
"description" : "易品新境为商家提供营销引流工具",
"versionName" : "1.0.0",
"versionCode" : "100",
"transformPx" : false,
/* 5+App */
"app-plus" : {
"usingComponents" : true,
"nvueStyleCompiler" : "uni-app",
"compilerVersion" : 3,
"splashscreen" : {
"alwaysShowBeforeRender" : false,
"waiting" : true,
"autoclose" : true,
"delay" : 0
},
"safearea" : {
"bottom" : {
"offset" : "none"
}
},
/* */
"modules" : {
"OAuth" : {},
"Payment" : {},
"Share" : {},
"Geolocation" : {}
},
/* */
"distribute" : {
/* android */
"android" : {
"permissions" : [
"<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-permission android:name=\"android.permission.MODIFY_AUDIO_SETTINGS\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
]
},
/* ios */
"ios" : {},
/* SDK */
"sdkConfigs" : {
"oauth" : {
"univerify" : {},
"weixin" : {
"appid" : "wx222fbe58feee7819",
"appsecret" : "3d24525a636d7573a8fae885097d5cf7",
"UniversalLinks" : ""
}
},
"payment" : {
"weixin" : {
"__platform__" : [ "android" ],
"appid" : "wx222fbe58feee7819",
"UniversalLinks" : ""
}
},
"share" : {
"weixin" : {
"appid" : "wx222fbe58feee7819",
"UniversalLinks" : ""
}
},
"maps" : {},
"ad" : {},
"geolocation" : {}
},
"icons" : {
"android" : {
"hdpi" : "unpackage/res/icons/72x72.png",
"xhdpi" : "unpackage/res/icons/96x96.png",
"xxhdpi" : "unpackage/res/icons/144x144.png",
"xxxhdpi" : "unpackage/res/icons/192x192.png"
},
"ios" : {
"appstore" : "unpackage/res/icons/1024x1024.png",
"ipad" : {
"app" : "unpackage/res/icons/76x76.png",
"app@2x" : "unpackage/res/icons/152x152.png",
"notification" : "unpackage/res/icons/20x20.png",
"notification@2x" : "unpackage/res/icons/40x40.png",
"proapp@2x" : "unpackage/res/icons/167x167.png",
"settings" : "unpackage/res/icons/29x29.png",
"settings@2x" : "unpackage/res/icons/58x58.png",
"spotlight" : "unpackage/res/icons/40x40.png",
"spotlight@2x" : "unpackage/res/icons/80x80.png"
},
"iphone" : {
"app@2x" : "unpackage/res/icons/120x120.png",
"app@3x" : "unpackage/res/icons/180x180.png",
"notification@2x" : "unpackage/res/icons/40x40.png",
"notification@3x" : "unpackage/res/icons/60x60.png",
"settings@2x" : "unpackage/res/icons/58x58.png",
"settings@3x" : "unpackage/res/icons/87x87.png",
"spotlight@2x" : "unpackage/res/icons/80x80.png",
"spotlight@3x" : "unpackage/res/icons/120x120.png"
}
}
},
"splashscreen" : {
"androidStyle" : "common"
}
}
},
/* */
"quickapp" : {},
/* */
"mp-weixin" : {
"appid" : "",
"setting" : {
"urlCheck" : false
},
"usingComponents" : true
},
"mp-alipay" : {
"usingComponents" : true
},
"mp-baidu" : {
"usingComponents" : true
},
"mp-toutiao" : {
"usingComponents" : true
},
"uniStatistics" : {
"enable" : false
}
}

View File

@@ -0,0 +1,12 @@
{
"name": "barter-app",
"lockfileVersion": 2,
"requires": true,
"packages": {
"node_modules/uni-simple-router": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/uni-simple-router/-/uni-simple-router-2.0.6.tgz",
"integrity": "sha512-n5gepoT3QcBrvVLeTCY/DjUjC4m1hNzwFlOa1VlCsww/4/34MGfvegpN9OWR8jOxxtUPKGHLAG0yODL/ITCwbw=="
}
}
}

View File

@@ -0,0 +1,108 @@
# uni-read-pages
![coverage](https://img.shields.io/badge/coverage%20-98%25-green) ![npm](https://img.shields.io/badge/npm%20-v2.6.11-blue) ![license](https://img.shields.io/badge/license-MIT-red) ![size](https://img.shields.io/badge/size-1.48%20kb-yellowgreen)
通过 [vue.config.js](https://cli.vuejs.org/zh/config/) 配合此库,可以随心所欲的读取 `pages.json` 下的所有配置
## 安装
您可以使用 `Yarn``npm` 安装该软件包(选择一个):
##### Yarn
```sh
yarn add uni-read-pages
```
##### npm
```sh
npm install uni-read-pages
```
## 开始
配置 `vue.config.js` 通过 `webpack` 注入全局变量 [查看文档](https://www.webpackjs.com/plugins/define-plugin/)
#### 配置 `vue.config.js`
```js
//vue.config.js
const TransformPages = require('uni-read-pages')
const tfPages = new TransformPages()
module.exports = {
configureWebpack: {
plugins: [
new tfPages.webpack.DefinePlugin({
ROUTES: JSON.stringify(tfPages.routes)
})
]
}
}
```
借助`webpack.DefinePlugin` 轻松注入全局变量。`ROUTES` 及可全局使用
#### 使用
```js
// xxx.vue
<script>
export default {
data() {
return {
title: 'Hello'
}
},
onLoad() {
console.log(ROUTES)
},
}
</script>
```
## API
#### options
```js
//默认值
const CONFIG={
cli:false, //当前是否为脚手架初始化的项目
includes:['path','aliasPath','name'] //需要获取包涵的字段
}
```
#### Instance method
* **getPagesRoutes**
* 通过读取 `pages.json` 文件 生成直接可用的routes
* **parsePages(pageCallback, subPageCallback)**
* 单条page对象解析
* **resolvePath(dir)**
* 解析绝对路径
#### Instance attr
* **CONFIG**
* 当前配置项
* **webpack**
* 当前工程下需要用到 `webpack`
* **uniPagesJSON**
* 当前 `uni-app` 内置对象,可以通过此属性调用一些内置方法
* **routes**
* 通过 **includes** 解析后得到的路由表 **可直接使用**
#### getter
* **pagesJson**
* 获取所有 `pages.json` 下的内容 返回 `json`
#### uniPagesJSON method
* getMainEntry()
* getNVueMainEntry()
* parsePages (pagesJson, pageCallback, subPageCallback)
* parseEntry (pagesJson)
* getPagesJson()
* parsePagesJson (content, loader)
#### uniPagesJSON attr
* pagesJsonJsFileName //默认值 pages.js

View File

@@ -0,0 +1,83 @@
const path = require('path')
const CONFIG = {
includes: ['path', 'aliasPath', 'name']
}
const rootPath = path.resolve(process.cwd(), 'node_modules');
/** 解析绝对路径
* @param {Object} dir
*/
function resolvePath(dir) {
return path.resolve(rootPath, dir);
}
class TransformPages {
constructor(config) {
config = {
...CONFIG,
...config
};
this.CONFIG = config;
this.webpack = require(resolvePath('webpack'));
this.uniPagesJSON = require(resolvePath('@dcloudio/uni-cli-shared/lib/pages.js'));
this.routes = this.getPagesRoutes().concat(this.getNotMpRoutes());
}
/**
* 获取所有pages.json下的内容 返回json
*/
get pagesJson() {
return this.uniPagesJSON.getPagesJson();
}
/**
* 通过读取pages.json文件 生成直接可用的routes
*/
getPagesRoutes(pages = this.pagesJson.pages, rootPath = null) {
const routes = [];
for (let i = 0; i < pages.length; i++) {
const item = pages[i];
const route = {};
for (let j = 0; j < this.CONFIG.includes.length; j++) {
const key = this.CONFIG.includes[j];
let value = item[key];
if (key === 'path') {
value = rootPath ? `/${rootPath}/${value}` : `/${value}`
}
if (key === 'aliasPath' && i == 0 && rootPath == null) {
route[key] = route[key] || '/'
} else if (value !== undefined) {
route[key] = value;
}
}
routes.push(route);
}
return routes;
}
/**
* 解析小程序分包路径
*/
getNotMpRoutes() {
const {
subPackages
} = this.pagesJson;
let routes = [];
if (subPackages == null || subPackages.length == 0) {
return [];
}
for (let i = 0; i < subPackages.length; i++) {
const subPages = subPackages[i].pages;
const root = subPackages[i].root;
const subRoutes = this.getPagesRoutes(subPages, root);
routes = routes.concat(subRoutes)
}
return routes
}
/**
* 单条page对象解析
* @param {Object} pageCallback
* @param {Object} subPageCallback
*/
parsePages(pageCallback, subPageCallback) {
this.uniPagesJSON.parsePages(this.pagesJson, pageCallback, subPageCallback)
}
}
module.exports = TransformPages

View File

@@ -0,0 +1,51 @@
{
"_from": "uni-read-pages",
"_id": "uni-read-pages@1.0.5",
"_inBundle": false,
"_integrity": "sha512-GkrrZ0LX0vn9R5k6RKEi0Ez3Q3e2vUpjXQ8Z6/K/d28KudI9ajqgt8WEjQFlG5EPm1K6uTArN8LlqmZTEixDUA==",
"_location": "/uni-read-pages",
"_phantomChildren": {},
"_requested": {
"type": "tag",
"registry": true,
"raw": "uni-read-pages",
"name": "uni-read-pages",
"escapedName": "uni-read-pages",
"rawSpec": "",
"saveSpec": null,
"fetchSpec": "latest"
},
"_requiredBy": [
"#USER",
"/"
],
"_resolved": "https://registry.npmjs.org/uni-read-pages/-/uni-read-pages-1.0.5.tgz",
"_shasum": "452c8dcaa8977bbaef600909be926c8d9704387c",
"_spec": "uni-read-pages",
"_where": "/Users/WebTmm/Desktop/barter-app",
"author": "",
"bugs": {
"url": "https://github.com/SilurianYang/uni-read-pages/issues"
},
"bundleDependencies": false,
"deprecated": false,
"description": "read `pages.json` file to generate the routes table",
"directories": {
"example": "examples"
},
"homepage": "https://github.com/SilurianYang/uni-read-pages#readme",
"keywords": [],
"license": "ISC",
"main": "index.js",
"name": "uni-read-pages",
"repository": {
"type": "git",
"url": "git+https://github.com/SilurianYang/uni-read-pages.git"
},
"scripts": {
"build": "webpack --progress --config webpack/webpack.prod.js",
"dev": "webpack --watch --progress --config webpack/webpack.dev.js",
"postinstall": "node -e \"console.log('\\x1b[91m','\\n\\n uni-simple-router 垫脚片,欢迎下载!\\n \\n 开源不易,需要鼓励。去给 uni-read-pages 项目 点个 star 吧 \\n\\n')\""
},
"version": "1.0.5"
}

View File

@@ -0,0 +1,6 @@
dist
/node_modules
/webpack
/src/global.d.ts
/test
/jest.config.js

View File

@@ -0,0 +1,256 @@
module.exports = {
root: true,
env: {
browser: true,
node: true,
es6: true
},
globals: {
uni: true,
plus: true,
getCurrentPages: true,
getApp: true,
__uniConfig: true,
__uniRoutes: true
},
parser: '@typescript-eslint/parser',
extends: ['eslint:recommended'],
plugins: ['@typescript-eslint'],
rules: {
'@typescript-eslint/consistent-type-definitions': [
'error',
'interface'
],
'accessor-pairs': 2,
'arrow-spacing': [
2,
{
before: true,
after: true
}
],
'block-spacing': [2, 'always'],
'brace-style': [
2,
'1tbs',
{
allowSingleLine: true
}
],
camelcase: [
0,
{
properties: 'always'
}
],
'comma-dangle': [2, 'never'],
'comma-spacing': [
2,
{
before: false,
after: true
}
],
'comma-style': [2, 'last'],
'constructor-super': 2,
curly: [2, 'multi-line'],
'dot-location': [2, 'property'],
'eol-last': 2,
eqeqeq: ['error', 'always', {null: 'ignore'}],
'generator-star-spacing': [
2,
{
before: true,
after: true
}
],
'handle-callback-err': [2, '^(err|error)$'],
indent: ['error', 4],
'jsx-quotes': [2, 'prefer-single'],
'key-spacing': [
2,
{
beforeColon: false,
afterColon: true
}
],
'keyword-spacing': [
2,
{
before: true,
after: true
}
],
'new-cap': [
2,
{
newIsCap: true,
capIsNew: false
}
],
'new-parens': 2,
'no-array-constructor': 2,
'no-caller': 2,
'no-console': 'off',
'no-class-assign': 2,
'no-cond-assign': 2,
'no-const-assign': 2,
'no-control-regex': 0,
'no-delete-var': 2,
'no-dupe-args': 2,
'no-dupe-class-members': 2,
'no-dupe-keys': 2,
'no-duplicate-case': 2,
'no-empty-character-class': 2,
'no-empty-pattern': 2,
'no-eval': 2,
'no-ex-assign': 2,
'no-extend-native': 2,
'no-extra-bind': 2,
'no-extra-boolean-cast': 2,
'no-extra-parens': [2, 'functions'],
'no-fallthrough': 2,
'no-floating-decimal': 2,
'no-func-assign': 2,
'no-implied-eval': 2,
'no-inner-declarations': [2, 'functions'],
'no-invalid-regexp': 2,
'no-irregular-whitespace': 2,
'no-iterator': 2,
'no-label-var': 2,
'no-labels': [
2,
{
allowLoop: false,
allowSwitch: false
}
],
'no-lone-blocks': 2,
'no-mixed-spaces-and-tabs': 2,
'no-multi-spaces': 2,
'no-multi-str': 2,
'no-multiple-empty-lines': [
2,
{
max: 1
}
],
'no-native-reassign': 2,
'no-negated-in-lhs': 2,
'no-new-object': 2,
'no-new-require': 2,
'no-new-symbol': 2,
'no-new-wrappers': 2,
'no-obj-calls': 2,
'no-octal': 2,
'no-octal-escape': 2,
'no-path-concat': 2,
'no-proto': 2,
'no-redeclare': 2,
'no-regex-spaces': 2,
'no-return-assign': [2, 'except-parens'],
'no-self-assign': 2,
'no-self-compare': 2,
'no-sequences': 2,
'no-shadow-restricted-names': 2,
'no-spaced-func': 2,
'no-sparse-arrays': 2,
'no-this-before-super': 2,
'no-throw-literal': 2,
'no-trailing-spaces': 2,
'no-undef': 2,
'no-undef-init': 2,
'no-unexpected-multiline': 2,
'no-unmodified-loop-condition': 2,
'no-unneeded-ternary': [
2,
{
defaultAssignment: false
}
],
'no-unreachable': 2,
'no-unsafe-finally': 2,
'no-unused-vars': [
2,
{
vars: 'all',
args: 'none'
}
],
'no-useless-call': 2,
'no-useless-computed-key': 2,
'no-useless-constructor': 2,
'no-useless-escape': 0,
'no-whitespace-before-property': 2,
'no-with': 2,
'one-var': [
2,
{
initialized: 'never'
}
],
'operator-linebreak': [
2,
'after',
{
overrides: {
'?': 'before',
':': 'before'
}
}
],
'padded-blocks': [2, 'never'],
quotes: [
2,
'single',
{
avoidEscape: true,
allowTemplateLiterals: true
}
],
semi: 'off',
'semi-spacing': [
2,
{
before: false,
after: true
}
],
'space-before-blocks': [2, 'always'],
'space-before-function-paren': [2, 'never'],
'space-in-parens': [2, 'never'],
'space-infix-ops': 2,
'space-unary-ops': [
2,
{
words: true,
nonwords: false
}
],
'spaced-comment': [
2,
'always',
{
markers: [
'global',
'globals',
'eslint',
'eslint-disable',
'*package',
'!',
','
]
}
],
'template-curly-spacing': [2, 'never'],
'use-isnan': 2,
'valid-typeof': 2,
'wrap-iife': [2, 'any'],
'yield-star-spacing': [2, 'both'],
yoda: [2, 'never'],
'prefer-const': 2,
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
'object-curly-spacing': 'off',
'array-bracket-spacing': [2, 'never']
}
};

View File

@@ -0,0 +1,39 @@
---
name: 报告问题Bug report
about: 详细描述你遇到的问题并寻求社区帮助
title: ''
labels: ''
assignees: ''
---
**问题描述**
[问题描述:尽可能简洁清晰地把问题描述清楚]
**复现步骤**
[复现问题的步骤]
1. 启动 '...'
2. 点击 '....'
3. 查看
[或者可以直接贴源代码]
**预期结果**
[使用简洁清晰的语言描述你希望生效的预期结果]
**实际结果**
[这里请贴上你的报错截图或文字]
**系统信息:**
- 发行平台: [如 微信小程序、H5平台、5+ App等]
- 操作系统 [如 iOS 12.1.2、Android 7.0]
- HBuilderX版本 [如使用HBuilderX则需提供 HBuilderX 版本号]
- 项目创建方法 [如使用Vue-cli创建/HBuilderX]
- 设备信息 [如 iPhone8 Plus]
- uni-simple-router版本 [如 v1.5.4]
**补充信息**
[可选]
[根据你的分析,出现这个问题的原因可能在哪里?]

View File

@@ -0,0 +1,21 @@
---
name: 建议新功能Feature Request
about: 对 uni-simple-router 提出改善建议
title: ''
labels: ''
assignees: ''
---
**新功能描述**
简洁描述你希望补充完善的增强功能
**现状及问题**
[当前现状及由此导致的不便]
**尝试方案**
[如果你有尝试绕开或其它解决方案,在这里描述你的建议方案]
**补充信息**
[其它你认为有参考价值的信息]

View File

@@ -0,0 +1,76 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at 1606726660@qq.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 hhyang
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,49 @@
# uni-simple-router
> 一个更为简洁的[Vue-router](https://router.vuejs.org/zh/),专为 [uni-app](https://uniapp.dcloud.io/) 量身打造
## 介绍
`uni-simple-router` 是专为 [uni-app](https://uniapp.dcloud.io/) 打造的路由器。它与 [uni-app](https://uniapp.dcloud.io/) 核心深度集成,使使用 [uni-app](https://uniapp.dcloud.io/) 轻松构建单页应用程序变得轻而易举。功能包括:
* `H5端` 能完全使用 `vue-router` 进行开发。
* 模块化,基于组件的路由器配置。
* 路由参数,查询,通配符。
* `H5端` 查看由 `uni-simple-router` 过渡系统提供动力的过渡效果。
* 更细粒度的导航控制。
* `H端`自动控制活动的CSS类链接。
* 通配小程序端、APP端、H5端。
开始使用 [查看文档](http://hhyang.cn),或 [使用示例](https://github.com/SilurianYang/uni-simple-router/tree/master/examples)(请参见下面的示例)。
## 问题
在提交问题的之前,请确保阅读 [“问题报告清单”](https://github.com/SilurianYang/uni-simple-router/issues/new?assignees=&labels=&template=bug_report.md&title=) 。不符合准则的问题可能会立即被解决。
## 贡献
提出拉取请求之前,请务必先阅读 [查看文档](http://hhyang.cn)(请参见下面的示例)。。
## 变更日志
[发行说明](https://github.com/SilurianYang/uni-simple-router/releases) 中记录了每个发行版的详细信息更改。
## 特别感谢
特别感谢 [markrgba](https://github.com/markrgba) 一直以来对文档和相关测试的维护。
## 技术交流
<a target="_blank" href="//shang.qq.com/wpa/qunwpa?idkey=0f4d7f38e5d15dd49bf7c3032c80ed3f54ecfa3dd800053d6ae145c869f9eb47"><img border="0" src="http://pub.idqqimg.com/wpa/images/group.png" alt="uni-app 插件" title="uni-app 插件"></a>
## 成品预览
<div style="display: -webkit-box;display: flex; flex-direction: column;align-items: center;">
<p style="color: #3eaf7c;font-size:18px">uni-simple-router@2.0+ts+uni-app</p>
<img src="https://hhyang.cn/images/ad1.jpg" width="200" height="200">
</div>

View File

@@ -0,0 +1,38 @@
```flow
st=>start: 开始跳转
e=>end: 跳转结束
platform=>operation: 平台选择
H5=>condition: H5
APP=>condition: APP
applets=>condition: 小程序
routerBeforeEach=>operation: routerBeforeEach
lock=>condition: 跳转加锁
runH5=>operation: H5
runAPP=>parallel: APP
runapplets=>parallel: 小程序
beforeRouteLeave=>condition: beforeRouteLeave
beforeEach=>condition: beforeEach
beforeEnter=>condition: beforeEnter
afterEach=>operation: afterEach
runJump=>condition: 执行跳转成功或者失败
stopJump=>operation: next(false) 停止跳转
errorJump=>operation: 跳转失败
routerErrorEach=>operation: routerErrorEach
routerAfterEach=>operation: routerAfterEach
st->platform(right)->applets(yes)->routerBeforeEach
applets(no)->APP(yes)->routerBeforeEach
APP(no)->H5(yes)->routerBeforeEach
routerBeforeEach->lock(yes)->runAPP(path1)->runapplets(path1)->beforeRouteLeave
lock(no)->runH5->beforeRouteLeave(no)->stopJump->routerErrorEach
beforeRouteLeave(yes)->beforeEach(no)->stopJump->routerErrorEach
beforeEach(yes)->beforeEnter(no)->stopJump->routerErrorEach
beforeEnter(yes)->runJump(no)->errorJump->routerErrorEach
runJump(yes)->afterEach->routerAfterEach
routerAfterEach->e
routerErrorEach->e
```

View File

@@ -0,0 +1,50 @@
// this the shared base config for all packages.
{
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
"mainEntryPointFilePath": "./dist/src/index.d.ts",
"apiReport": {
"enabled": true,
"reportFolder": "./temp/"
},
"docModel": {
"enabled": true
},
"dtsRollup": {
"enabled": true,
"untrimmedFilePath": "./dist/<unscopedPackageName>.d.ts"
},
"tsdocMetadata": {
"enabled": false
},
"messages": {
"compilerMessageReporting": {
"default": {
"logLevel": "warning"
}
},
"extractorMessageReporting": {
"default": {
"logLevel": "warning",
"addToApiReportFile": true
},
"ae-missing-release-tag": {
"logLevel": "none"
}
},
"tsdocMessageReporting": {
"default": {
"logLevel": "warning"
}
}
}
}

View File

@@ -0,0 +1,79 @@
<template>
<view @click="gotoPage()"><slot></slot></view>
</template>
<script>
const navType = {
push: 'push',
replace: 'replace',
replaceAll: 'replaceAll',
pushTab: 'pushTab',
back:'back'
};
export default {
props: {
to: {
type: [String, Object],
required: true
},
stopNavto: {
type: Boolean,
default: false,
},
navType: {
type: String,
default: 'push',
},
level: {
type: Number,
default: 1,
},
append: {
type: Boolean,
default: false,
},
},
methods: {
formatNav(text) {
if (text != null && text.constructor === String) {
text = text.replace(/\'/g, '');
text = text.replace(/(\w+)(?=:)/g, function (val) {
return `"${val}"`;
});
text = text.replace(/:\s*([^,{}\s"]+)/g, function (val) {
const arr = val.split(':');
return `:"${arr[1].trim()}"`;
});
try {
text = JSON.parse(text);
} catch (e) {}
}
if (this.append) {
let pathArr = this.$Route.path.split('/');
pathArr.splice(pathArr.length - this.level, this.level);
pathArr = pathArr.join('/');
if (text.constructor === Object) {
if (text.path) {
text.path = pathArr + text.path;
}
} else {
text = pathArr + text;
}
}
return text;
},
gotoPage() {
if (this.stopNavto) {
return true;
}
const type = navType[this.navType];
if (type == null) {
return console.error(` "navType" unknown type \n\n value${Object.values(navType).join('、')}`);
}
const navInfo = this.formatNav(this.to);
this.$Router[type](navInfo);
},
},
};
</script>

View File

@@ -0,0 +1,330 @@
export declare interface AppConfig {
registerLoadingPage?: boolean;
loadingPageStyle?: () => object;
loadingPageHook?: (view: any) => void;
launchedHook?: () => void;
animation?: startAnimationRule;
}
export declare interface appletConfig {
animationDuration?: number;
}
export declare interface appletsVueHookConfig {
app: appVueHookConfig;
page: pageVueHookConfig;
component: baseAppHookConfig[];
}
export declare interface appVueHookConfig extends baseAppHookConfig {
onLaunch: Array<hookObjectRule | Function>;
onShow: Array<hookObjectRule | Function>;
onHide: Array<hookObjectRule | Function>;
}
export declare type appVueSortHookRule = 'beforeCreate' | 'created' | 'beforeMount' | 'mounted' | 'onLaunch' | 'onShow' | 'onHide' | 'beforeDestroy' | 'destroyed';
export declare type backTypeRule = 'backbutton' | 'navigateBack';
export declare interface baseAppHookConfig {
[key: string]: Array<hookObjectRule | Function>;
created: Array<hookObjectRule | Function>;
beforeMount: Array<hookObjectRule | Function>;
mounted: Array<hookObjectRule | Function>;
beforeDestroy: Array<hookObjectRule | Function>;
destroyed: Array<hookObjectRule | Function>;
}
export declare type comVueSortHookRule = 'beforeCreate' | 'created' | 'beforeMount' | 'mounted' | 'beforeDestroy' | 'destroyed';
export declare function createRouter(params: InstantiateConfig): Router;
export declare interface debuggerArrayConfig {
error?: boolean;
warn?: boolean;
log?: boolean;
}
export declare type debuggerConfig = boolean | debuggerArrayConfig;
export declare interface endAnimationRule {
animationType?: endAnimationType;
animationDuration?: number;
}
export declare type endAnimationType = 'slide-out-right' | 'slide-out-left' | 'slide-out-top' | 'slide-out-bottom' | 'pop-out' | 'fade-out' | 'zoom-in' | 'zoom-fade-in' | 'none';
export declare type guardHookRule = (to: totalNextRoute, from: totalNextRoute, next: (rule?: navtoRule | false) => void) => void;
export declare interface H5Config {
paramsToQuery?: boolean;
vueRouterDev?: boolean;
vueNext?: boolean;
mode?: string;
base?: string;
linkActiveClass?: string;
linkExactActiveClass?: string;
scrollBehavior?: Function;
fallback?: boolean;
}
export declare interface h5NextRule {
fullPath?: string | undefined;
hash?: string | undefined;
matched?: Array<object>;
meta?: object;
name?: undefined | string;
type?: undefined | string;
}
export declare type hookListRule = Array<(router: Router, to: totalNextRoute, from: totalNextRoute, toRoute: RoutesRule, next: Function) => void>;
export declare interface hookObjectRule {
options: Array<any>;
hook: Function;
}
export declare enum hookToggle {
'beforeHooks' = "beforeEach",
'afterHooks' = "afterEach",
'enterHooks' = "beforeEnter"
}
export declare interface InstantiateConfig {
[key: string]: any;
keepUniOriginNav?: boolean;
platform: 'h5' | 'app-plus' | 'app-lets' | 'mp-weixin' | 'mp-baidu' | 'mp-alipay' | 'mp-toutiao' | 'mp-qq' | 'mp-360';
h5?: H5Config;
APP?: AppConfig;
applet?: appletConfig;
debugger?: debuggerConfig;
routerBeforeEach?: (to: navtoRule, from: navtoRule, next: (rule?: navtoRule | false) => void) => void;
routerAfterEach?: (to: navtoRule, from: navtoRule, next?: Function) => void;
routerErrorEach?: (error: navErrorRule, router: Router) => void;
resolveQuery?: (jsonQuery: objectAny) => objectAny;
parseQuery?: (jsonQuery: objectAny) => objectAny;
detectBeforeLock?: (router: Router, to: string | number | totalNextRoute | navRoute, navType: NAVTYPE) => void;
routes: RoutesRule[];
}
export declare interface LifeCycleConfig {
beforeHooks: hookListRule;
afterHooks: hookListRule;
routerBeforeHooks: hookListRule;
routerAfterHooks: hookListRule;
routerErrorHooks: Array<(error: navErrorRule, router: Router) => void>;
}
export declare interface navErrorRule {
type: navRuleStatus;
msg: string;
to?: totalNextRoute;
from?: totalNextRoute;
nextTo?: any;
[propName: string]: any;
}
export declare type navMethodRule = Promise<void | undefined | navRuleStatus>;
export declare interface navRoute extends h5NextRule, navtoRule {
}
export declare type navRuleStatus = 0 | 1 | 2 | 3;
export declare interface navtoRule {
NAVTYPE?: NAVTYPE;
path?: string;
name?: string | undefined;
query?: objectAny;
params?: objectAny;
animationType?: startAnimationType | endAnimationType;
animationDuration?: number;
events?: objectAny;
success?: Function;
fail?: Function;
complete?: Function;
}
export declare type NAVTYPE = 'push' | 'replace' | 'replaceAll' | 'pushTab' | 'back';
export declare enum navtypeToggle {
'push' = "navigateTo",
'replace' = "redirectTo",
'replaceAll' = "reLaunch",
'pushTab' = "switchTab",
'back' = "navigateBack"
}
export declare type notCallProxyHookRule = 'onHide' | 'beforeDestroy' | 'destroyed' | 'onUnload' | 'onResize';
export declare type objectAny = {
[propName: string]: any;
};
export declare interface originMixins extends uniNavApiRule {
BACKTYPE: '' | backTypeRule;
}
export declare type pageTypeRule = 'app' | 'page' | 'component';
export declare interface pageVueHookConfig extends baseAppHookConfig {
onShow: Array<hookObjectRule | Function>;
onHide: Array<hookObjectRule | Function>;
onLoad: Array<hookObjectRule | Function>;
onReady: Array<hookObjectRule | Function>;
onUnload: Array<hookObjectRule | Function>;
onResize: Array<hookObjectRule | Function>;
}
export declare type pageVueSortHookRule = 'beforeCreate' | 'created' | 'beforeMount' | 'mounted' | 'onLoad' | 'onReady' | 'onShow' | 'onResize' | 'onHide' | 'beforeDestroy' | 'destroyed' | 'onUnload';
export declare type PromiseResolve = (value?: void | PromiseLike<void> | undefined) => void;
export declare type proxyHookName = 'beforeHooks' | 'afterHooks';
export declare type reloadNavRule = totalNextRoute | false | undefined | string;
export declare type reNavMethodRule = 'navigateTo' | 'redirectTo' | 'reLaunch' | 'switchTab';
export declare type reNotNavMethodRule = 'navigateBack';
export declare enum rewriteMethodToggle {
'navigateTo' = "push",
'navigate' = "push",
'redirectTo' = "replace",
'reLaunch' = "replaceAll",
'switchTab' = "pushTab",
'navigateBack' = "back"
}
export declare interface Router {
[key: string]: any;
readonly lifeCycle: LifeCycleConfig;
readonly options: InstantiateConfig;
$lockStatus: boolean;
$route: object | null;
enterPath: string;
Vue: any;
appProxyHook: {
app: appVueHookConfig;
};
appMain: {
NAVTYPE: reNavMethodRule | reNotNavMethodRule;
path: string;
} | {};
appletsProxyHook: appletsVueHookConfig;
routesMap: routesMapRule | {};
mount: Array<{
app: any;
el: string;
}>;
install(Vue: any): void;
push(to: totalNextRoute | navRoute | string, from?: totalNextRoute): void;
replace(to: totalNextRoute | navRoute | string, from?: totalNextRoute): void;
replaceAll(to: totalNextRoute | navRoute | string, from?: totalNextRoute): void;
pushTab(to: totalNextRoute | navRoute | string, from?: totalNextRoute): void;
back(level: number | undefined, origin?: uniBackRule | uniBackApiRule): void;
forceGuardEach(navType: NAVTYPE | undefined, forceNav: boolean): void;
beforeEach(userGuard: guardHookRule): void;
afterEach(userGuard: (to: totalNextRoute, from: totalNextRoute) => void): void;
}
export declare function RouterMount(Vim: any, router: Router, el?: string | undefined): void | never;
export declare interface routeRule {
name: string | undefined;
meta: objectAny;
path: string;
query: objectAny;
params: objectAny;
fullPath: string;
NAVTYPE: NAVTYPE | '';
BACKTYPE?: backTypeRule | '';
[propName: string]: any;
}
export declare type routesMapKeysRule = 'finallyPathList' | 'finallyPathMap' | 'aliasPathMap' | 'pathMap' | 'nameMap' | 'vueRouteMap';
export declare interface routesMapRule {
[key: string]: any;
finallyPathList: Array<string>;
finallyPathMap: RoutesRule;
aliasPathMap: RoutesRule;
pathMap: RoutesRule;
nameMap: RoutesRule;
vueRouteMap: objectAny;
}
export declare interface RoutesRule {
path: string;
component?: object;
name?: string;
components?: object;
redirect?: string | Function;
props?: boolean | object | Function;
aliasPath?: string;
alias?: string | Array<string>;
children?: Array<RoutesRule>;
beforeEnter?: guardHookRule;
meta?: any;
[propName: string]: any;
}
export declare function runtimeQuit(title?: string | undefined): void;
export declare interface startAnimationRule {
animationType?: startAnimationType;
animationDuration?: number;
}
export declare type startAnimationType = 'slide-in-right' | 'slide-in-left' | 'slide-in-top' | 'slide-in-bottom' | 'pop-in' | 'fade-in' | 'zoom-out' | 'zoom-fade-out' | 'none';
export declare interface totalNextRoute extends h5NextRule, navtoRule {
path: string;
delta?: number;
[propName: string]: any;
}
export declare interface uniBackApiRule {
delta?: number;
animationDuration?: number;
animationType?: endAnimationType;
}
export declare interface uniBackRule {
from: backTypeRule;
}
export declare interface uniNavApiRule {
url: string;
openType?: 'appLaunch';
query?: objectAny;
path?: string;
delta?: number;
detail?: {
[propName: string]: any;
};
animationType?: startAnimationType;
animationDuration?: number;
events?: {
[propName: string]: any;
};
success?: Function;
fail?: Function;
complete?: Function;
animation?: {
animationType?: startAnimationType;
animationDuration?: number;
};
}
export { }
// @ts-ignore
declare module 'vue/types/vue' {
interface Vue {
$Router: Router;
$Route: routeRule;
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,107 @@
#!/bin/bash
# author hhyang
# home https://github.com/SilurianYang
printf "\n -------------- Ctrl+D可以退出程序 --------------- \n\n"
select name in "auto" "status" "add" "commit" "push" "pull" "branch" "checkout" "*"; do
case "$name" in
# 自动同步文件
"auto")
cp -avx ./examples/node_modules/uni-simple-router/* ./npm-package
rm -rf ./npm-package/package-lock.json
cp -avx ./README.md ./npm-package
cp -avx ./package.json ./npm-package
cp -avx ./npm-package/* ./src
rm -rf ./src/README.md
rm -rf ./src/package.json
printf "\n -------------- 自动化构建目录完毕 --------------- \n\n"
;;
# 查询status
"status")
git status
printf "\n -------------- 查询完毕 --------------- \n\n"
;;
# 添加文件 .或* 全部文件 可自定义文件路径
"add")
while read -p "请输入更多提交命令 【默认全部.】 " add; do
if [[ "$add" == "" ]]; then
eval "git add ."
else
eval "git add ${add}"
fi
printf "\n -------------- 添加完成 --------------- \n\n"
break
done
;;
# 提交文件
"commit")
while read -p "请输入提交信息:" readme; do
if [[ "$readme" != "" ]]; then
eval "git commit -m '${readme}'"
printf "\n -------------- 提交本地完成 --------------- \n\n"
break
else
printf "\n警告====> 提交信息不能为空! \n \n"
fi
done
;;
# 推送到服务端
"push")
read -p "请输入提交的分支(不输入默认主分支 [master] )" branch
printf "\n\n -------------- 正在推送github,请稍后.... --------------- \n\n"
if [[ "$branch" == "" ]]; then
git push
else
eval "git push origin ${branch}"
fi
printf "\n -------------- 推送github完成 --------------- \n\n"
;;
# 拉取最新代码
"pull")
printf "\n\n -------------- 正在拉取,请稍后.... --------------- \n\n"
git pull
printf "\n -------------- 正在拉取完成 --------------- \n\n"
;;
# 切换分支操作
"branch")
read -p "请输入添加更多指令 【分支】 " branchs
if [[ "$branchs" == "" ]]; then
printf "\n分支列表如下\n\n"
git branch
else
eval "git branch ${branchs}"
fi
printf "\n -------------- 分支操作完毕 --------------- \n\n"
;;
#
"checkout")
read -p "请输入添加更多指令 【默认切换到master】 " out
if [[ "$out" == "" ]]; then
git checkout master
else
eval "git checkout ${out}"
fi
printf "\n -------------- 执行完毕 --------------- \n\n"
;;
# 自定义指令
*)
while read -p "请输入自定义命令 【输入:q退出】" code; do
if [[ "$code" == ":q" ]];then
printf "\n"
break
fi
printf "\n\n -------------- 正在执行,请稍后.... --------------- \n\n"
eval "$code"
printf "\n -------------- 执行完毕 --------------- \n\n"
done
esac
done

View File

@@ -0,0 +1,5 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
moduleDirectories:['node_modules','src']
};

View File

@@ -0,0 +1,61 @@
{
"_from": "uni-simple-router@^2.0.6",
"_id": "uni-simple-router@2.0.6",
"_inBundle": false,
"_integrity": "sha512-n5gepoT3QcBrvVLeTCY/DjUjC4m1hNzwFlOa1VlCsww/4/34MGfvegpN9OWR8jOxxtUPKGHLAG0yODL/ITCwbw==",
"_location": "/uni-simple-router",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "uni-simple-router@^2.0.6",
"name": "uni-simple-router",
"escapedName": "uni-simple-router",
"rawSpec": "^2.0.6",
"saveSpec": null,
"fetchSpec": "^2.0.6"
},
"_requiredBy": [
"#USER",
"/"
],
"_resolved": "https://registry.npmjs.org/uni-simple-router/-/uni-simple-router-2.0.6.tgz",
"_shasum": "f6d48d6d29766c4b2471bb019192989e784fb684",
"_spec": "uni-simple-router@^2.0.6",
"_where": "/Users/WebTmm/Desktop/barter-app",
"author": {
"name": "hhyang"
},
"bugs": {
"url": "https://github.com/SilurianYang/uni-simple-router/issues"
},
"bundleDependencies": false,
"deprecated": false,
"description": "> 一个更为简洁的[Vue-router](https://router.vuejs.org/zh/),专为 [uni-app](https://uniapp.dcloud.io/) 量身打造",
"homepage": "https://github.com/SilurianYang/uni-simple-router#readme",
"keywords": [
"router",
"uni-app-router",
"interceptor",
"uni-app",
"uniapp"
],
"license": "MIT",
"main": "dist/uni-simple-router.js",
"name": "uni-simple-router",
"repository": {
"type": "git",
"url": "git+https://github.com/SilurianYang/uni-simple-router.git"
},
"scripts": {
"build": "webpack --progress --config webpack/webpack.prod.js",
"build:dts": "api-extractor run --local --verbose",
"dev": "webpack --watch --progress --config webpack/webpack.dev.js",
"lint": "eslint --ext .js,.ts src",
"lintFix": "eslint --ext .js,.ts src --fix",
"publish": "node ./publish/index.js",
"test": "jest test/path-to-regexp.spec.ts"
},
"types": "dist/uni-simple-router.d.ts",
"version": "2.0.6"
}

View File

@@ -0,0 +1,76 @@
import {RoutesRule, Router, routesMapRule, totalNextRoute, hookToggle, navtoRule} from '../options/base';
import {H5Config} from '../options/config';
import {warn} from '../helpers/warn'
import {getDataType, getRoutePath} from '../helpers/utils'
import { onTriggerEachHook } from '../public/hooks';
export function buildVueRoutes(router: Router, vueRouteMap:RoutesRule):RoutesRule {
const {pathMap, finallyPathList} = (router.routesMap as routesMapRule);
const vueRoutePathList:Array<string> = Object.keys(vueRouteMap);
for (let i = 0; i < vueRoutePathList.length; i++) {
const path = vueRoutePathList[i];
const myRoute:RoutesRule = pathMap[path];
const vueRoute:RoutesRule = vueRouteMap[path];
if (!myRoute) {
warn(`${path} 路由地址在路由表中未找到,确定是否传递漏啦`, router, true);
} else {
const {finallyPath} = getRoutePath(myRoute, router);
if (finallyPath instanceof Array) {
throw new Error(`非 vueRouterDev 模式下alias、aliasPath、path 无法提供数组类型! ${JSON.stringify(myRoute)}`);
}
if (myRoute.name != null) {
vueRoute.name = myRoute.name;
}
const vuePath = vueRoute['path'];
const vueAlias = vueRoute['alias'];
delete vueRoute['alias'];
vueRoute['path'] = (finallyPath as string);
if (vuePath === '/' && vueAlias != null) {
vueRoute['alias'] = vueAlias;
vueRoute['path'] = vuePath;
}
const beforeEnter = myRoute.beforeEnter;
if (beforeEnter) {
vueRoute['beforeEnter'] = function(
to:totalNextRoute,
from: totalNextRoute,
next:(rule?: navtoRule|false)=>void,
):void{
onTriggerEachHook(to, from, router, hookToggle['enterHooks'], next)
};
}
}
}
if (finallyPathList.includes('*')) {
vueRouteMap['*'] = pathMap['*']
}
return vueRouteMap
}
export function buildVueRouter(router:Router, vueRouter:any, vueRouteMap:RoutesRule|RoutesRule[]) :void |never {
let routes:RoutesRule[] = [];
if (getDataType<RoutesRule|RoutesRule[]>(vueRouteMap) === '[object Array]') {
routes = (vueRouteMap as RoutesRule[]);
} else {
routes = Object.values(vueRouteMap);
}
const {scrollBehavior, fallback} = router.options.h5 as H5Config;
const oldScrollBehavior = vueRouter.options.scrollBehavior;
vueRouter.options.scrollBehavior = function proxyScrollBehavior(
to:totalNextRoute,
from:totalNextRoute,
savedPosition:any
) {
oldScrollBehavior && oldScrollBehavior(to, from, savedPosition);
return (scrollBehavior as Function)(to, from, savedPosition)
}
vueRouter.fallback = fallback;
// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
const newVueRouter:any = new vueRouter.constructor({
...router.options.h5,
base: vueRouter.options.base,
mode: vueRouter.options.mode,
routes
});
vueRouter.matcher = newVueRouter.matcher;
}

View File

@@ -0,0 +1,71 @@
import {Router, proxyHookName, totalNextRoute, navtoRule} from '../options/base';
export class MyArray extends Array {
constructor(
private router:Router,
private vueEachArray:Array<Function>,
private myEachHook:Function,
private hookName:'beforeHooks'| 'afterHooks',
) {
super();
Object.setPrototypeOf(this, MyArray.prototype)
}
push(v:any):any {
this.vueEachArray.push(v);
const index = this.length;
this[this.length] = (to: totalNextRoute, from: totalNextRoute, next:(rule?: navtoRule|false)=>void) => {
if (index > 0) {
this.vueEachArray[index](to, from, () => {
next && next()
});
} else {
this.myEachHook(to, from, (nextTo?:navtoRule|false) => {
// Fixe https://github.com/SilurianYang/uni-simple-router/issues/241 2021年3月6日22:15:27
// 目前不调用uni-app的守卫函数因为会丢失页面栈信息
if (nextTo === false) {
next(false);
} else {
this.vueEachArray[index](to, from, (uniNextTo?:navtoRule|false) => {
next(nextTo);
})
}
}, this.router, true);
}
};
}
}
export function proxyEachHook(router:Router, vueRouter:any):void {
const hookList:Array<'beforeHooks'| 'afterHooks'> = ['beforeHooks', 'afterHooks'];
for (let i = 0; i < hookList.length; i++) {
const hookName = hookList[i];
const myEachHook = router.lifeCycle[(hookName as proxyHookName)][0];
if (myEachHook) {
const vueEachArray:Array<Function> = vueRouter[hookName];
vueRouter[hookName] = new MyArray(router, vueEachArray, myEachHook, hookName);
}
}
}
export function proxyH5Mount(router:Router):void {
if (router.mount.length === 0) {
if (router.options.h5?.vueRouterDev) {
return
}
const uAgent = navigator.userAgent;
const isIos = !!uAgent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/)
if (isIos) {
// 【Fixe】 https://github.com/SilurianYang/uni-simple-router/issues/109
setTimeout(() => {
const element = document.getElementsByTagName('uni-page');
if (element.length > 0) {
return false
}
window.location.reload();
}, 0);
}
} else {
const [{app}] = router.mount;
app.$mount();
router.mount = [];
}
}

View File

@@ -0,0 +1,40 @@
import { Router } from '../options/base';
import { AppConfig } from '../options/config';
let quitBefore:number|null = null;
export function registerLoddingPage(
router:Router,
):void{
if (router.options.registerLoadingPage) {
const { loadingPageHook, loadingPageStyle } = router.options.APP as AppConfig; // 获取app所有配置
const view = new plus.nativeObj.View('router-loadding', {
top: '0px',
left: '0px',
height: '100%',
width: '100%',
...(loadingPageStyle as Function)()
});
(loadingPageHook as Function)(view); // 触发等待页面生命周期
}
}
export function runtimeQuit(
title:string|undefined = '再按一次退出应用'
):void{
const nowTime = +new Date();
if (!quitBefore) {
quitBefore = nowTime;
uni.showToast({
title,
icon: 'none',
position: 'bottom',
duration: 1000
});
setTimeout(() => { quitBefore = null }, 1000);
} else {
if (nowTime - quitBefore < 1000) {
plus.runtime.quit();
}
}
}

View File

@@ -0,0 +1,18 @@
import { Router} from '../options/base';
export function getEnterPath(
vueVim:any,
router:Router,
) :string {
switch (router.options.platform) {
case 'mp-alipay':
case 'mp-weixin':
case 'mp-toutiao':
case 'mp-qq':
return vueVim.$options.mpInstance.route;
case 'mp-baidu':
// 【Fixe】 https://github.com/SilurianYang/uni-simple-router/issues/251
return vueVim.$options.mpInstance.is || vueVim.$options.mpInstance.pageinstance.route;
}
return vueVim.$options.mpInstance.route; // 这是暂时的 因为除了以上的小程序 其他没测试 先这样写
}

View File

@@ -0,0 +1,79 @@
<template>
<view @click="gotoPage()"><slot></slot></view>
</template>
<script>
const navType = {
push: 'push',
replace: 'replace',
replaceAll: 'replaceAll',
pushTab: 'pushTab',
back:'back'
};
export default {
props: {
to: {
type: [String, Object],
required: true
},
stopNavto: {
type: Boolean,
default: false,
},
navType: {
type: String,
default: 'push',
},
level: {
type: Number,
default: 1,
},
append: {
type: Boolean,
default: false,
},
},
methods: {
formatNav(text) {
if (text != null && text.constructor === String) {
text = text.replace(/\'/g, '');
text = text.replace(/(\w+)(?=:)/g, function (val) {
return `"${val}"`;
});
text = text.replace(/:\s*([^,{}\s"]+)/g, function (val) {
const arr = val.split(':');
return `:"${arr[1].trim()}"`;
});
try {
text = JSON.parse(text);
} catch (e) {}
}
if (this.append) {
let pathArr = this.$Route.path.split('/');
pathArr.splice(pathArr.length - this.level, this.level);
pathArr = pathArr.join('/');
if (text.constructor === Object) {
if (text.path) {
text.path = pathArr + text.path;
}
} else {
text = pathArr + text;
}
}
return text;
},
gotoPage() {
if (this.stopNavto) {
return true;
}
const type = navType[this.navType];
if (type == null) {
return console.error(` "navType" unknown type \n\n value${Object.values(navType).join('、')}`);
}
const navInfo = this.formatNav(this.to);
this.$Router[type](navInfo);
},
},
};
</script>

View File

@@ -0,0 +1,6 @@
declare var uni:any;
declare var plus:any;
declare var __uniConfig:any;
declare var __uniRoutes:any;
declare function getCurrentPages(isAll:boolean|undefined=false):any;
declare function getApp(args?:{allowDefault: true}):any;

View File

@@ -0,0 +1,102 @@
import {err} from './warn'
import {appletsVueHookConfig, appVueHookConfig, pageVueHookConfig, InstantiateConfig, LifeCycleConfig} from '../options/config'
import { copyData} from './utils';
import { appVueSortHookRule, pageVueSortHookRule, notCallProxyHookRule, comVueSortHookRule } from '../options/base';
export const keyword = ['query'];
export const mpPlatformReg = '(^mp-weixin$)|(^mp-baidu$)|(^mp-alipay$)|(^mp-toutiao$)|(^mp-qq$)|(^mp-360$)' // 小程序下不能直接导出正则 需要重新组装成正则 不然bug一推 诡异
export const baseConfig:InstantiateConfig = {
h5: {
paramsToQuery: false,
vueRouterDev: false,
vueNext: false,
mode: 'hash',
base: '/',
linkActiveClass: 'router-link-active',
linkExactActiveClass: 'router-link-exact-active',
scrollBehavior: (to:any, from:any, savedPostion:Function) => ({ x: 0, y: 0 }),
fallback: true
},
APP: {
registerLoadingPage: true,
loadingPageStyle: () => JSON.parse('{"backgroundColor":"#FFF"}'),
loadingPageHook: (view:any) => { view.show(); },
launchedHook: () => { plus.navigator.closeSplashscreen(); },
animation: {}
},
applet: {
animationDuration: 300
},
platform: 'h5',
keepUniOriginNav: false,
debugger: false,
routerBeforeEach: (to, from, next) => { next() },
routerAfterEach: (to, from) => {},
routerErrorEach: (error, router) => { router.$lockStatus = false; err(error, router, true); },
detectBeforeLock: (router, to, navType) => {},
routes: [
{
path: '/choose-location'
},
{
path: '/open-location'
},
{
path: '/preview-image'
}
]
}
export const lifeCycle:LifeCycleConfig = {
beforeHooks: [],
afterHooks: [],
routerBeforeHooks: [],
routerAfterHooks: [],
routerErrorHooks: []
};
export const appProxyHook:{
app:appVueHookConfig
} = {
app: {
created: [],
beforeMount: [],
mounted: [],
onLaunch: [],
onShow: [],
onHide: [],
beforeDestroy: [],
destroyed: []
}
}
export const indexProxyHook:appletsVueHookConfig = {
app: appProxyHook.app,
page: (function(
appHooks:appVueHookConfig
) :pageVueHookConfig {
// eslint-disable-next-line no-unused-vars
const {onLaunch, ...otherHooks} = appHooks;
return {
...otherHooks,
onLoad: [],
onReady: [],
onUnload: [],
onResize: []
};
})(copyData<appVueHookConfig>(appProxyHook.app)),
component: []
}
export const proxyVueSortHookName:{
app:Array<appVueSortHookRule>,
page:Array<pageVueSortHookRule>,
component:Array<comVueSortHookRule>
} = {
app: ['created', 'beforeMount', 'mounted', 'onLaunch', 'onShow', 'onHide', 'beforeDestroy', 'destroyed'],
page: ['created', 'beforeMount', 'mounted', 'onLoad', 'onReady', 'onShow', 'onResize', 'onHide', 'beforeDestroy', 'destroyed', 'onUnload'],
component: ['created', 'beforeMount', 'mounted', 'beforeDestroy', 'destroyed']
}
export const notCallProxyHook:Array<notCallProxyHookRule> = [
'onHide', 'beforeDestroy', 'destroyed', 'destroyed', 'onUnload', 'onResize'
];

View File

@@ -0,0 +1,47 @@
import {RoutesRule, Router, routesMapRule} from '../options/base';
import {H5Config} from '../options/config';
import {warn} from './warn'
import {getRoutePath} from './utils'
export function createRouteMap(
router: Router,
routes: RoutesRule[],
): routesMapRule|never {
const routesMap:routesMapRule = {
finallyPathList: [],
finallyPathMap: Object.create(null),
aliasPathMap: Object.create(null),
pathMap: Object.create(null),
vueRouteMap: Object.create(null),
nameMap: Object.create(null)
}
routes.forEach(route => {
const { finallyPath, aliasPath, path} = getRoutePath(route, router);
if (path == null) {
throw new Error(`请提供一个完整的路由对象,包括以绝对路径开始的 path 字符串 ${JSON.stringify(route)}`);
}
if (finallyPath instanceof Array) {
if (!(router.options.h5 as H5Config).vueRouterDev && router.options.platform === 'h5') {
throw new Error(`非 vueRouterDev 模式下route.alias 目前无法提供数组类型! ${JSON.stringify(route)}`);
}
}
const strFinallyPath = (finallyPath as string);
const strAliasPath = (aliasPath as string);
if (router.options.platform !== 'h5') {
if (strFinallyPath.indexOf('/') !== 0 && path !== '*') {
warn(`当前路由对象下route${JSON.stringify(route)} 是否缺少了前缀 /`, router, true);
}
}
if (!routesMap.finallyPathMap[strFinallyPath]) {
routesMap.finallyPathMap[strFinallyPath] = route;
routesMap.aliasPathMap[strAliasPath] = route;
routesMap.pathMap[path] = route;
routesMap.finallyPathList.push(strFinallyPath);
if (route.name != null) {
routesMap.nameMap[route.name] = route;
}
}
})
return routesMap;
}

View File

@@ -0,0 +1,36 @@
import { navtoRule, navErrorRule, Router, proxyHookName, guardHookRule, totalNextRoute, hookToggle} from '../options/base';
import { LifeCycleConfig, InstantiateConfig} from '../options/config';
import {onTriggerEachHook} from '../public/hooks'
export function registerHook(list:Array<Function>, fn:Function):void {
list[0] = fn;
}
export function registerRouterHooks<T extends LifeCycleConfig>(cycleHooks:T, options:InstantiateConfig):T {
registerHook(cycleHooks.routerBeforeHooks, function(to:totalNextRoute, from: totalNextRoute, next:(rule?: navtoRule|false)=>void):void {
(options.routerBeforeEach as Function)(to, from, next);
})
registerHook(cycleHooks.routerAfterHooks, function(to:totalNextRoute, from: totalNextRoute):void {
(options.routerAfterEach as Function)(to, from);
})
registerHook(cycleHooks.routerErrorHooks, function(error:navErrorRule, router:Router):void {
(options.routerErrorEach as Function)(error, router);
})
return cycleHooks;
}
export function registerEachHooks(router:Router, hookType:proxyHookName, userGuard:guardHookRule) {
registerHook(router.lifeCycle[hookType], function(
to:totalNextRoute,
from: totalNextRoute,
next:(rule?: navtoRule|false)=>void,
router:Router,
auto:boolean,
):void {
if (auto) { // h5端 vue-router自动触发 非自己调用触发
onTriggerEachHook(to, from, router, hookToggle[hookType], next)
} else {
userGuard(to, from, next)
}
})
}

View File

@@ -0,0 +1,97 @@
import { Router, routesMapRule, RoutesRule, pageTypeRule} from '../options/base';
import {createRouteMap} from '../helpers/createRouteMap'
import {buildVueRoutes, buildVueRouter} from '../H5/buildRouter'
import {proxyEachHook} from '../H5/proxyHook'
import {registerLoddingPage} from '../app/appPatch';
import { proxyPageHook } from '../public/page';
import { forceGuardEach } from '../public/methods';
import { assertParentChild } from './utils';
import { getEnterPath } from '../applets/appletPatch';
import { mpPlatformReg } from './config';
let registerRouter:boolean = false;
let onloadProxyOk:boolean = false;
const appletProxy:{
app:boolean;
page:string;
} = {
app: false,
page: ''
}
export function getMixins(Vue:any, router: Router):{
beforeCreate(this: any): void;
} | {
beforeCreate(): void;
} | {
onLaunch(): void;
} {
let platform = router.options.platform;
if (new RegExp(mpPlatformReg, 'g').test(platform)) {
platform = 'app-lets';
}
const toggleHooks = {
h5: {
beforeCreate(this: any): void {
if (this.$options.router) {
router.$route = this.$options.router; // 挂载vue-router到路由对象下
let vueRouteMap:RoutesRule[]|RoutesRule = [];
if (router.options.h5?.vueRouterDev) {
vueRouteMap = router.options.routes;
} else {
vueRouteMap = createRouteMap(router, this.$options.router.options.routes).finallyPathMap;
(router.routesMap as routesMapRule).vueRouteMap = vueRouteMap;
buildVueRoutes(router, vueRouteMap);
}
buildVueRouter(router, this.$options.router, vueRouteMap);
proxyEachHook(router, this.$options.router);
}
}
},
'app-plus': {
beforeCreate(this: any): void {
if (!registerRouter) {
registerRouter = true;
proxyPageHook(this, router, 'appProxyHook', 'app');
registerLoddingPage(router);
}
}
},
'app-lets': {
beforeCreate(this: any): void {
const pageType:pageTypeRule = this.$options.mpType;
if (pageType === 'component' && !onloadProxyOk) {
const isProxy = assertParentChild(appletProxy['page'], this);
if (isProxy) {
proxyPageHook(this, router, 'appletsProxyHook', pageType)
}
} else if (pageType !== 'component') {
if (!appletProxy[pageType]) { // 没有处理
if (pageType === 'page') {
appletProxy[pageType] = getEnterPath(this, router);
router.enterPath = appletProxy[pageType]; // 我不确定在不同端是否都是同样的变现?可能有的为非绝对路径?
} else {
appletProxy[pageType] = true;
}
proxyPageHook(this, router, 'appletsProxyHook', pageType)
}
}
},
onLoad(this: any):void{
if (!onloadProxyOk && assertParentChild(appletProxy['page'], this)) {
onloadProxyOk = true;
forceGuardEach(router);
}
}
}
};
return toggleHooks[(platform as 'h5'|'app-plus'|'app-lets')];
}
export function initMixins(Vue: any, router: Router) {
const routesMap = createRouteMap(router, router.options.routes);
router.routesMap = routesMap; // 挂载自身路由表到路由对象下
// Vue.util.defineReactive(router, '_Route', createRoute(router, 19970806))
Vue.mixin({
...getMixins(Vue, router)
});
}

View File

@@ -0,0 +1,523 @@
import {appVueHookConfig, H5Config, pageVueHookConfig, InstantiateConfig, appletsVueHookConfig, baseAppHookConfig} from '../options/config';
import {RoutesRule, routesMapRule, routesMapKeysRule, Router, totalNextRoute, objectAny, navErrorRule, hookObjectRule, notCallProxyHookRule, NAVTYPE, navRoute, pageTypeRule} from '../options/base';
import {baseConfig, notCallProxyHook, proxyVueSortHookName, keyword} from '../helpers/config';
import {ERRORHOOK} from '../public/hooks'
import {warnLock} from '../helpers/warn'
import { createRoute, navjump } from '../public/methods';
const Regexp = require('path-to-regexp');
export function voidFun():void{}
export function def(
defObject:objectAny,
key:string,
getValue:Function
) {
Object.defineProperty(defObject, key, {
get() {
return getValue();
}
})
}
export function timeOut(time:number):Promise<void> {
return new Promise(resolve => {
setTimeout(() => {
resolve();
}, time)
})
}
export function mergeConfig<T extends InstantiateConfig>(baseConfig: T, userConfig: T): T {
const config: {[key: string]: any} = Object.create(null);
const baseConfigKeys: Array<string> = Object.keys(baseConfig).concat(['resolveQuery', 'parseQuery']);
for (let i = 0; i < baseConfigKeys.length; i += 1) {
const key = baseConfigKeys[i];
if (userConfig[key] != null) {
if (userConfig[key].constructor === Object) {
config[key] = {
...baseConfig[key],
...userConfig[key]
};
} else if (key === 'routes') {
config[key] = [
...baseConfig[key],
...userConfig[key]
];
} else {
config[key] = userConfig[key];
}
} else {
config[key] = baseConfig[key];
}
}
return config as T;
}
export function notDeepClearNull<T>(object:T):T {
for (const key in object) {
if (object[key] == null) {
delete object[key];
}
}
return object;
}
export function getRoutePath(route: RoutesRule, router:Router): {
finallyPath: string | string[];
aliasPath: string;
path: string;
alias: string | string[] | undefined;
} {
let finallyPath = route.aliasPath || route.alias || route.path;
if (router.options.platform !== 'h5') {
finallyPath = route.path
}
return {
finallyPath,
aliasPath: route.aliasPath || route.path,
path: route.path,
alias: route.alias
}
}
export function assertNewOptions<T extends InstantiateConfig>(
options: T
): T | never {
const {platform, routes} = options;
if (platform == null) {
throw new Error(`你在实例化路由时必须传递 'platform'`);
}
if (routes == null || routes.length === 0) {
throw new Error(`你在实例化路由时必须传递 routes 为空,这是无意义的。`);
}
if (options.platform === 'h5') {
if (options.h5?.vueRouterDev) {
baseConfig.routes = [];
}
}
const mergeOptions = mergeConfig<T>(baseConfig as T, options);
return mergeOptions;
}
export function getWildcardRule(
router:Router,
msg?:navErrorRule
):RoutesRule|never {
const routesMap = (router.routesMap as routesMapRule);
const route = routesMap.finallyPathMap['*'];
if (route) { // 有写通配符
return route
}
if (msg) {
ERRORHOOK[0](msg, router);
}
throw new Error(`当前路由表匹配规则已全部匹配完成,未找到满足的匹配规则。你可以使用 '*' 通配符捕捉最后的异常`);
}
export function notRouteTo404(
router:Router,
toRoute:RoutesRule|{
redirect:any;
path:string
},
parseToRule:totalNextRoute,
navType:NAVTYPE
):RoutesRule|totalNextRoute|never {
if (toRoute.path !== '*') { // 不是通配符 正常匹配成功
return (toRoute as RoutesRule);
}
const redirect = toRoute.redirect;
if (typeof redirect === 'undefined') {
throw new Error(` * 通配符必须配合 redirect 使用。redirect: string | Location | Function`);
}
let newRoute = redirect;
if (typeof newRoute === 'function') {
newRoute = newRoute(parseToRule) as totalNextRoute;
}
const redirectRule = navjump(newRoute as totalNextRoute, router, navType, undefined, undefined, undefined, false);
return (redirectRule as totalNextRoute);
}
export function routesForMapRoute(
router: Router,
path: string,
mapArrayKey:Array<routesMapKeysRule>
):RoutesRule|never {
if (router.options.h5?.vueRouterDev) {
return {path}
}
// 【Fixe】 https://github.com/SilurianYang/uni-simple-router/issues/252
const startPath = path.split('?')[0];
let wildcard = '';
const routesMap = (router.routesMap as routesMapRule);
for (let i = 0; i < mapArrayKey.length; i++) {
const mapKey = mapArrayKey[i];
const mapList = routesMap[mapKey];
for (const [key, value] of Object.entries(mapList)) {
if (key === '*') {
if (wildcard === '') {
wildcard = '*'
}
continue;
}
const route:string|RoutesRule = value;
let rule:string = key;
if (getDataType<Array<string>|objectAny>(mapList) === '[object Array]') {
rule = (route as string);
}
const pathRule:RegExp = Regexp(rule);
const result = pathRule.exec(startPath);
if (result != null) {
if (getDataType<string|RoutesRule>(route) === '[object String]') {
return routesMap.finallyPathMap[(route as string)];
}
return (route as RoutesRule);
}
}
}
if (wildcard !== '') {
return getWildcardRule(router);
}
throw new Error(`${path} 路径无法在路由表中找到!检查跳转路径及路由表`);
}
export function getDataType<T>(data:T):string {
return Object.prototype.toString.call(data)
}
export function copyData<T>(object:T): T {
return JSON.parse(JSON.stringify(object))
}
export function getUniCachePage<T extends objectAny>(pageIndex?:number):T|[] {
const pages:T = getCurrentPages();
if (pageIndex == null) {
return pages
}
if (pages.length === 0) {
return pages;
}
const page = pages.reverse()[pageIndex];
if (page == null) {
return []
}
return page;
}
export function urlToJson(url :string):{
path:string;
query:objectAny
} {
const query:objectAny = {};
const [path, params] = url.split('?');
if (params != null) {
const parr = params.split('&');
for (const i of parr) {
const arr = i.split('=');
query[arr[0]] = arr[1];
}
}
return {
path,
query
}
}
export function forMatNextToFrom<T extends totalNextRoute>(
router:Router,
to:T,
from:T
):{
matTo:T;
matFrom: T;
} {
let [matTo, matFrom] = [to, from];
if (router.options.platform === 'h5') {
const {vueNext, vueRouterDev} = (router.options.h5 as H5Config);
if (!vueNext && !vueRouterDev) {
matTo = createRoute(router, undefined, matTo) as T;
matFrom = createRoute(router, undefined, matFrom) as T;
}
} else {
matTo = createRoute(router, undefined, deepClone<T>(matTo)) as T;
matFrom = createRoute(router, undefined, deepClone<T>(matFrom)) as T;
}
return {
matTo: matTo,
matFrom: matFrom
}
}
export function paramsToQuery(
router:Router,
toRule:totalNextRoute|string
):totalNextRoute|string {
if (router.options.platform === 'h5' && !router.options.h5?.paramsToQuery) {
return toRule;
}
if (getDataType<totalNextRoute|string>(toRule) === '[object Object]') {
const {name, params, ...moreToRule} = (toRule as totalNextRoute);
let paramsQuery = params;
if (router.options.platform !== 'h5' && paramsQuery == null) {
paramsQuery = {};
}
if (name != null && paramsQuery != null) {
let route = (router.routesMap as routesMapRule).nameMap[name];
if (route == null) {
route = getWildcardRule(router, { type: 2, msg: `命名路由为:${name} 的路由,无法在路由表中找到!`, toRule});
}
const {finallyPath} = getRoutePath(route, router);
if (finallyPath.includes(':')) { // 动态路由无法使用 paramsToQuery
ERRORHOOK[0]({ type: 2, msg: `动态路由:${finallyPath} 无法使用 paramsToQuery`, toRule}, router);
} else {
return {
...moreToRule,
path: finallyPath as string,
query: paramsQuery
}
}
}
}
return toRule
}
export function assertDeepObject(object:objectAny):boolean {
let arrMark = null;
try {
arrMark = JSON.stringify(object).match(/\{|\[|\}|\]/g);
} catch (error) {
warnLock(`传递的参数解析对象失败。` + error)
}
if (arrMark == null) {
return false
}
if (arrMark.length > 3) {
return true;
}
return false
}
export function baseClone<
T extends {
[key:string]:any
}, K extends keyof T
>(
source:T,
target:Array<any>|objectAny
):void {
for (const key of Object.keys(source)) {
const dyKey = key as T[K];
if (source[key] === source) continue
if (typeof source[key] === 'object') {
target[dyKey] = getDataType<T>(source[key]) === '[object Array]' ? ([] as Array<any>) : ({} as objectAny)
baseClone(source[key], target[dyKey])
} else {
target[dyKey] = source[key]
}
}
}
export function deepClone<T>(source:T):T {
const __ob__ = getDataType<T>(source) === '[object Array]' ? ([] as Array<any>) : ({} as objectAny);
baseClone(source, __ob__)
return __ob__ as T
}
export function lockDetectWarn(
router:Router,
to:string|number|totalNextRoute|navRoute,
navType:NAVTYPE,
next:Function,
passiveType?:'beforeHooks'| 'afterHooks'
):void{
if (passiveType === 'afterHooks') {
next();
} else {
const {detectBeforeLock} = router.options;
detectBeforeLock && detectBeforeLock(router, to, navType);
if (router.$lockStatus) {
(router.options.routerErrorEach as (error: navErrorRule, router:Router) => void)({
type: 2,
msg: '当前页面正在处于跳转状态,请稍后再进行跳转....'
}, router);
} else {
next();
}
}
}
export function replaceHook(
router:Router,
vueVim:any,
proxyHookKey:'appProxyHook'|'appletsProxyHook',
pageType:pageTypeRule,
):void{
const vueOptions:appVueHookConfig|pageVueHookConfig = vueVim.$options;
const proxyHook = router[proxyHookKey][(pageType as 'app')];
let proxyHookChild:baseAppHookConfig|objectAny = {};
if (getDataType(proxyHook) === '[object Array]') {
proxyHookChild = {
beforeCreate: [],
created: [],
beforeMount: [],
mounted: [],
beforeDestroy: [],
destroyed: []
}
}
if (proxyHook != null) {
const proxyName = proxyVueSortHookName[pageType];
for (let i = 0; i < proxyName.length; i++) {
const keyName = proxyName[i];
const originHook = vueOptions[keyName] as Array<Function>|undefined;
if (getDataType<Array<Function>|undefined>(originHook) === '[object Array]') {
const proxyInfo:hookObjectRule = {
options: [],
hook: Function
};
const hook = (originHook as Array<Function>).splice((originHook as Array<Function>).length - 1, 1, (...options:Array<any>) => (proxyInfo.options = options))[0]
proxyInfo.hook = function resetHook(enterPath:string):Function {
if (router.enterPath.replace(/^\//, '') !== enterPath.replace(/^\//, '') && pageType !== 'app') {
return () => {};
}
if (!notCallProxyHook.includes(keyName as notCallProxyHookRule)) {
hook.apply(vueVim, proxyInfo.options)
}
return () => {
(originHook as Array<Function>).splice((originHook as Array<Function>).length - 1, 1, hook);
};
}
if (Object.keys(proxyHookChild).length > 0) {
proxyHookChild[(keyName as string)] = [proxyInfo];
} else {
proxyHook[keyName] = [proxyInfo]
}
}
}
if (Object.keys(proxyHookChild).length > 0) {
// @ts-ignore
(proxyHook as appletsVueHookConfig['component']).push(proxyHookChild);
}
}
}
export function callHook(
value:objectAny,
enterPath:string
):Array<Function> {
const resetHookFun:Array<Function> = [];
for (const [, [origin]] of Object.entries(value as objectAny)) {
if (origin && origin.hook) {
resetHookFun.push(origin.hook(enterPath))
}
}
return resetHookFun;
}
export function resetPageHook(
router:Router,
enterPath:string
):void{
// Fixe: https://github.com/SilurianYang/uni-simple-router/issues/206
const pathInfo = enterPath.trim().match(/^(\/?[^\?\s]+)(\?[\s\S]*$)?$/);
if (pathInfo == null) {
throw new Error(`还原hook失败。请检查 【${enterPath}】 路径是否正确。`);
}
enterPath = pathInfo[1];
let proxyHookKey:'appProxyHook'|'appletsProxyHook' = 'appletsProxyHook';
if (router.options.platform === 'app-plus') {
proxyHookKey = 'appProxyHook';
}
let resetHookFun:Array<Function> = [];
for (const [, value] of Object.entries(router[proxyHookKey])) {
if (getDataType(value) === '[object Array]') {
for (let i = 0; i < value.length; i++) {
resetHookFun = resetHookFun.concat(callHook(value[i], enterPath));
}
} else {
resetHookFun = resetHookFun.concat(callHook(value, enterPath));
}
}
setTimeout(() => {
for (let i = 0; i < resetHookFun.length; i++) {
resetHookFun[i]();
}
}, 500)
}
export function reservedWord(
params:string|totalNextRoute
):string|totalNextRoute {
if (typeof params === 'string') {
return params
}
const query = {
...(copyData(params.params || {}) as object),
...(copyData(params.query || {}) as object)
};
for (let i = 0; i < keyword.length; i++) {
const hasKey = keyword[i];
if (Reflect.has(query, hasKey)) {
if (getDataType(params.query) === '[object Object]') {
delete (params.query as objectAny)[hasKey];
}
if (getDataType(params.params) === '[object Object]') {
delete (params.params as objectAny)[hasKey];
}
warnLock(`${JSON.stringify(keyword)} 作为插件的保留字,在参数传递中不允许使用。已自动被过滤掉!换个参数名试试吧! `)
}
}
return params
}
export function assertParentChild(
parentPath:string,
vueVim:any,
):boolean {
while (vueVim.$parent != null) {
const mpPage = vueVim.$parent.$mp;
if (mpPage.page && mpPage.page.is === parentPath) {
return true;
}
vueVim = vueVim.$parent;
}
try {
if (vueVim.$mp.page.is === parentPath || vueVim.$mp.page.route === parentPath) {
return true
}
} catch (error) {
return false
}
return false
}
export function resolveAbsolutePath(
path:string,
router:Router
):string|never {
const reg = /^\/?([^\?\s]+)(\?.+)?$/;
const trimPath = path.trim();
if (!reg.test(trimPath)) {
throw new Error(`${path}】 路径错误,请提供完整的路径(10001)。`);
}
const paramsArray = trimPath.match(reg);
if (paramsArray == null) {
throw new Error(`${path}】 路径错误,请提供完整的路径(10002)。`);
}
const query:string = paramsArray[2] || '';
if (/^\.\/[^\.]+/.test(trimPath)) { // 当前路径下
const navPath:string = router.currentRoute.path + path;
return navPath.replace(/[^\/]+\.\//, '');
}
const relative = paramsArray[1].replace(/\//g, `\\/`).replace(/\.\./g, `[^\\/]+`).replace(/\./g, '\\.');
const relativeReg = new RegExp(`^\\/${relative}$`);
const route = router.options.routes.filter(it => relativeReg.test(it.path));
if (route.length !== 1) {
throw new Error(`${path}】 路径错误,尝试转成绝对路径失败,请手动转成绝对路径(10003)。`);
}
return route[0].path + query;
}

View File

@@ -0,0 +1,37 @@
import {debuggerConfig, debuggerArrayConfig} from '../options/config'
import {Router} from '../options/base'
type callType='error'|'warn'|'log';
export function isLog(type:callType, dev:debuggerConfig, errText:any, enforce:boolean = false):boolean {
if (!enforce) {
const isObject = dev.toString() === '[object Object]';
if (dev === false) {
return false
} else if (isObject) {
if ((dev as debuggerArrayConfig)[type] === false) {
return false;
}
}
}
console[type](errText);
return true;
}
export function err(errText:any, router:Router, enforce?:boolean):void {
const dev = (router.options.debugger as debuggerConfig);
isLog('error', dev, errText, enforce);
}
export function warn(errText:any, router:Router, enforce?:boolean):void {
const dev = (router.options.debugger as debuggerConfig);
isLog('warn', dev, errText, enforce);
}
export function log(errText:any, router:Router, enforce?:boolean):void {
const dev = (router.options.debugger as debuggerConfig);
isLog('log', dev, errText, enforce);
}
export function warnLock(errText:any):void {
console.warn(errText);
}

View File

@@ -0,0 +1,11 @@
export * from './options/base'
export * from './options/config'
export {
runtimeQuit
} from './app/appPatch'
export {
RouterMount,
createRouter
} from './public/router'

View File

@@ -0,0 +1,236 @@
import {appletsVueHookConfig, appVueHookConfig, InstantiateConfig, LifeCycleConfig} from '../options/config';
export enum hookToggle{
'beforeHooks'='beforeEach',
'afterHooks'='afterEach',
'enterHooks'='beforeEnter'
}
export enum navtypeToggle{
'push'='navigateTo',
'replace'='redirectTo',
'replaceAll'='reLaunch',
'pushTab'='switchTab',
'back'='navigateBack'
}
export enum rewriteMethodToggle{
'navigateTo'='push',
'navigate'='push',
'redirectTo'='replace',
'reLaunch'='replaceAll',
'switchTab'='pushTab',
'navigateBack'='back',
}
export type backTypeRule='backbutton'|'navigateBack'
export type pageTypeRule='app'|'page'|'component';
export type notCallProxyHookRule='onHide'|'beforeDestroy'|'destroyed'|'onUnload'|'onResize';
export type appVueSortHookRule='beforeCreate'|'created'|'beforeMount'|'mounted'|'onLaunch'|'onShow'|'onHide'|'beforeDestroy'|'destroyed';
export type pageVueSortHookRule='beforeCreate'|'created'|'beforeMount'|'mounted'|'onLoad'|'onReady'|'onShow'|'onResize'|'onHide'|'beforeDestroy'|'destroyed'|'onUnload';
export type comVueSortHookRule= 'beforeCreate'|'created'| 'beforeMount'| 'mounted'|'beforeDestroy'| 'destroyed';
export type reNavMethodRule='navigateTo'|'redirectTo'|'reLaunch'|'switchTab';
export type reNotNavMethodRule='navigateBack';
export type reloadNavRule=totalNextRoute | false | undefined|string;
export type hookListRule=Array<(router:Router, to:totalNextRoute, from: totalNextRoute, toRoute:RoutesRule,next:Function)=>void>
export type guardHookRule=(to: totalNextRoute, from: totalNextRoute, next:(rule?: navtoRule|false)=>void)=>void;
export type navRuleStatus= 0|1|2|3; //0: next(false) 1:next(unknownType) 2:加锁状态,禁止跳转 3:在获取页面栈的时候页面栈不够level获取
export type proxyHookName='beforeHooks'|'afterHooks';
export type navMethodRule = Promise<void | undefined | navRuleStatus>;
export type objectAny={[propName: string]: any;};
export type NAVTYPE = 'push' | 'replace' | 'replaceAll' | 'pushTab'|'back';
export type startAnimationType =
| 'slide-in-right'
| 'slide-in-left'
| 'slide-in-top'
| 'slide-in-bottom'
| 'pop-in'
| 'fade-in'
| 'zoom-out'
| 'zoom-fade-out'
| 'none';
export type endAnimationType =
| 'slide-out-right'
| 'slide-out-left'
| 'slide-out-top'
| 'slide-out-bottom'
| 'pop-out'
| 'fade-out'
| 'zoom-in'
| 'zoom-fade-in'
| 'none';
// 跳转api时传递的跳转规则
export interface navtoRule {
NAVTYPE?: NAVTYPE; // 跳转类型 v1.1.0+
path?: string; // 跳转路径 绝对路径
name?: string | undefined; // 跳转路径名称
query?: objectAny; // 跳转使用path时 query包含需要传递的参数
params?: objectAny; // 跳转使用name时 params包含需要传递的参数
animationType?: startAnimationType|endAnimationType;
animationDuration?: number;
events?: objectAny;
success?: Function;
fail?: Function;
complete?: Function;
}
// h5 next管道函数中传递的from及to对象
export interface h5NextRule {
fullPath?: string | undefined;
hash?: string | undefined;
matched?: Array<object>;
meta?: object;
name?: undefined | string;
type?: undefined | string;
}
export interface totalNextRoute extends h5NextRule, navtoRule {
path:string;
delta?:number;
[propName: string]: any;
}
export interface navRoute extends h5NextRule, navtoRule {
}
// 开始切换窗口动画 app端可用
export interface startAnimationRule {
animationType?: startAnimationType; // 窗口关闭的动画效果
animationDuration?: number; // 窗口关闭动画的持续时间
}
// 关闭窗口时的动画 app端可用
export interface endAnimationRule {
animationType?: endAnimationType; // 窗口关闭的动画效果
animationDuration?: number; // 窗口关闭动画的持续时间
}
export interface hookObjectRule {
options:Array<any>;
hook:Function;
}
// 执行路由跳转失败或者 next(false) 时走的规则
export interface navErrorRule {
type: navRuleStatus;
msg: string;
to?:totalNextRoute;
from?:totalNextRoute;
nextTo?:any;
[propName:string]:any;
}
// uni原生api跳转时的规则
export interface uniNavApiRule {
url: string;
openType?:'appLaunch',
query?:objectAny;
path?:string;
delta?:number;
detail?:{[propName:string]:any};
animationType?:startAnimationType;
animationDuration?:number;
events?:{[propName:string]:any};
success?:Function;
fail?:Function;
complete?:Function;
animation?:{
animationType?:startAnimationType;
animationDuration?:number;
}
}
export interface originMixins extends uniNavApiRule{
BACKTYPE:''|backTypeRule
}
// uni-app 原始返回api 回调参数
export interface uniBackRule{
from:backTypeRule;
}
export interface uniBackApiRule{
delta?: number;
animationDuration?: number;
animationType?:endAnimationType;
}
export type routesMapKeysRule=
'finallyPathList'|
'finallyPathMap'|
'aliasPathMap'|
'pathMap'|
'nameMap'|
'vueRouteMap';
export interface routesMapRule{
[key:string]:any;
finallyPathList: Array<string>;
finallyPathMap:RoutesRule;
aliasPathMap: RoutesRule;
pathMap: RoutesRule;
nameMap:RoutesRule,
vueRouteMap:objectAny
}
export interface routeRule{
name:string|undefined;
meta:objectAny;
path:string;
query:objectAny;
params:objectAny;
fullPath:string;
NAVTYPE:NAVTYPE|'';
BACKTYPE?:backTypeRule|''; // v2.0.5 +
[propName: string]: any;
}
export interface RoutesRule {
path: string; // pages.json中的path 必须加上 '/' 开头
component?: object; // H5端可用
name?: string; // 命名路由
components?: object; // 命名视图组件H5端可用
redirect?: string | Function; // H5端可用
props?: boolean | object | Function; // H5端可用
aliasPath?: string; // h5端 设置一个别名路径来替换 uni-app的默认路径
alias?: string | Array<string>; // H5端可用
children?: Array<RoutesRule>; // 嵌套路由H5端可用
beforeEnter?:guardHookRule; // 路由元守卫
meta?: any; // 其他格外参数
[propName: string]: any;
}
export interface Router {
[key:string]:any;
readonly lifeCycle: LifeCycleConfig;
readonly options: InstantiateConfig;
$lockStatus:boolean;
$route: object | null;
enterPath:string;
Vue:any;
appProxyHook:{
app:appVueHookConfig
};
appMain:{
NAVTYPE:reNavMethodRule|reNotNavMethodRule,
path:string
}|{};
appletsProxyHook:appletsVueHookConfig;
routesMap: routesMapRule|{};
mount: Array<{app: any; el: string}>;
install(Vue: any): void;
push(to: totalNextRoute|navRoute | string,from?:totalNextRoute): void; // 动态的导航到一个新 URL 保留浏览历史
replace(to: totalNextRoute|navRoute | string,from?:totalNextRoute): void; // 动态的导航到一个新 URL 关闭当前页面,跳转到的某个页面。
replaceAll(to: totalNextRoute|navRoute | string,from?:totalNextRoute): void; // 动态的导航到一个新 URL 关闭所有页面,打开到应用内的某个页面
pushTab(to: totalNextRoute|navRoute | string,from?:totalNextRoute): void; // 动态的导航到一个新 url 关闭所有页面打开到应用内的某个tab
back(level:number|undefined,origin?:uniBackRule|uniBackApiRule):void;
forceGuardEach(navType:NAVTYPE|undefined,forceNav:boolean):void; //强制触发当前守卫
beforeEach(userGuard:guardHookRule): void; // 添加全局前置路由守卫
afterEach(userGuard:(to: totalNextRoute, from: totalNextRoute)=>void): void; // 添加全局后置路由守卫
}
export type PromiseResolve=(value?: void | PromiseLike<void> | undefined) => void;
// @ts-ignore
declare module 'vue/types/vue' {
interface Vue {
$Router: Router;
$Route: routeRule;
}
}

View File

@@ -0,0 +1,84 @@
import {startAnimationRule, hookListRule, RoutesRule, navtoRule, navErrorRule, Router, objectAny, hookObjectRule, NAVTYPE, totalNextRoute, navRoute} from './base';
export type debuggerConfig=boolean|debuggerArrayConfig;
export interface H5Config {
paramsToQuery?: boolean; // h5端上通过params传参时规则是vue-router 刷新会丢失 开启此开关将变成?连接的方式
vueRouterDev?: boolean; // 完全使用采用vue-router的开发模式
vueNext?: boolean; // 在next管道函数中是否获取vueRouter next的原本参数
mode?: string;
base?: string;
linkActiveClass?: string;
linkExactActiveClass?: string;
scrollBehavior?: Function;
fallback?: boolean;
}
export interface AppConfig {
registerLoadingPage?:boolean; // 是否注册过渡加载页 +v2.0.6
loadingPageStyle?: () => object; // 当前等待页面的样式 必须返回一个json
loadingPageHook?: (view:any)=>void; // 刚刚打开页面处于等待状态,会触发此函数
launchedHook?:()=>void; // 首次启动app完成
animation?: startAnimationRule; // 页面切换动画
}
export interface appletConfig {
animationDuration?:number; // 页面切换时间,有助于路由锁精准解锁
}
export interface debuggerArrayConfig{
error?:boolean;
warn?:boolean;
log?:boolean;
}
export interface InstantiateConfig {
[key:string]:any;
keepUniOriginNav?:boolean; // 重写uni-app的跳转方法关闭后使用uni-app的原始方法跳转和插件api跳转等同
platform:'h5'|'app-plus'|'app-lets'|'mp-weixin'|'mp-baidu'|'mp-alipay'|'mp-toutiao'|'mp-qq'|'mp-360'; // 当前运行平台
h5?: H5Config;
APP?: AppConfig;
applet?:appletConfig;
debugger?: debuggerConfig; // 是否处于开发阶段 设置为true则打印日志
routerBeforeEach?: (to:navtoRule, from:navtoRule, next:(rule?: navtoRule|false)=>void) => void; // router 前置路由函数 每次触发跳转前先会触发此函数
routerAfterEach?: (to:navtoRule, from:navtoRule, next?: Function) => void; // router 后置路由函数 每次触发跳转后会触发此函数
routerErrorEach?: (error: navErrorRule, router:Router) => void;
resolveQuery?:(jsonQuery:objectAny)=>objectAny; // 跳转之前把参数传递给此函数、返回最终的数据!有此函数不走默认方法
parseQuery?:(jsonQuery:objectAny)=>objectAny; // 读取值之前把参数传递给此函数,返回最终的数据!有此函数不走默认方法
detectBeforeLock?:(router:Router, to:string|number|totalNextRoute|navRoute, navType:NAVTYPE)=>void; // 在检测路由锁之前触发的函数
routes: RoutesRule[];
}
export interface LifeCycleConfig{
beforeHooks: hookListRule;
afterHooks: hookListRule;
routerBeforeHooks: hookListRule;
routerAfterHooks: hookListRule;
routerErrorHooks: Array<(error:navErrorRule, router:Router)=>void>;
}
export interface baseAppHookConfig{
[key:string]:Array<hookObjectRule|Function>;
created:Array<hookObjectRule|Function>;
beforeMount:Array<hookObjectRule|Function>;
mounted:Array<hookObjectRule|Function>;
beforeDestroy:Array<hookObjectRule|Function>;
destroyed:Array<hookObjectRule|Function>;
}
export interface appVueHookConfig extends baseAppHookConfig{
onLaunch:Array<hookObjectRule|Function>;
onShow:Array<hookObjectRule|Function>;
onHide:Array<hookObjectRule|Function>;
}
export interface pageVueHookConfig extends baseAppHookConfig{
onShow:Array<hookObjectRule|Function>;
onHide:Array<hookObjectRule|Function>;
onLoad:Array<hookObjectRule|Function>;
onReady:Array<hookObjectRule|Function>;
onUnload:Array<hookObjectRule|Function>;
onResize:Array<hookObjectRule|Function>;
}
export interface appletsVueHookConfig{
app:appVueHookConfig;
page:pageVueHookConfig,
component:baseAppHookConfig[]
}

View File

@@ -0,0 +1,171 @@
import {
Router,
hookListRule,
navtoRule,
reloadNavRule,
totalNextRoute,
hookToggle,
NAVTYPE,
navErrorRule,
objectAny
} from '../options/base';
import {
routesForMapRoute,
getDataType,
forMatNextToFrom,
getUniCachePage,
voidFun
} from '../helpers/utils'
import { navjump } from './methods';
import { proxyH5Mount } from '../H5/proxyHook';
export const ERRORHOOK:Array<(error:navErrorRule, router:Router)=>void> = [
(error, router) => router.lifeCycle.routerErrorHooks[0](error, router)
]
export const HOOKLIST: hookListRule = [
(router, to, from, toRoute, next) => callHook(router.lifeCycle.routerBeforeHooks[0], to, from, router, next),
(router, to, from, toRoute, next) => callBeforeRouteLeave(router, to, from, next),
(router, to, from, toRoute, next) => callHook(router.lifeCycle.beforeHooks[0], to, from, router, next),
(router, to, from, toRoute, next) => callHook(toRoute.beforeEnter, to, from, router, next),
(router, to, from, toRoute, next) => callHook(router.lifeCycle.afterHooks[0], to, from, router, next, false),
(router, to, from, toRoute, next) => {
router.$lockStatus = false;
if (router.options.platform === 'h5') {
proxyH5Mount(router);
}
return callHook(router.lifeCycle.routerAfterHooks[0], to, from, router, next, false)
}
];
export function callBeforeRouteLeave(
router:Router,
to:totalNextRoute,
from:totalNextRoute,
resolve:Function
):void {
const page = getUniCachePage<objectAny>(0);
let beforeRouteLeave;
if (Object.keys(page).length > 0) {
let leaveHooks:Array<Function>|undefined|Function;
if (router.options.platform === 'h5') {
leaveHooks = (page as objectAny).$options.beforeRouteLeave;
} else {
if ((page as objectAny).$vm != null) {
leaveHooks = (page as objectAny).$vm.$options.beforeRouteLeave;
}
}
switch (getDataType<Array<Function>>((leaveHooks as Array<Function>))) {
case '[object Array]': // h5端表现
beforeRouteLeave = (leaveHooks as Array<Function>)[0];
beforeRouteLeave = beforeRouteLeave.bind(page)
break;
case '[object Function]': // 目前app端表现
beforeRouteLeave = (leaveHooks as Function).bind((page as objectAny).$vm);
break
}
}
return callHook(beforeRouteLeave, to, from, router, resolve);
}
export function callHook(
hook:Function|undefined,
to:totalNextRoute,
from: totalNextRoute,
router:Router,
resolve:Function,
hookAwait:boolean|undefined = true
):void {
if (hook != null && hook instanceof Function) {
if (hookAwait === true) {
hook(to, from, resolve, router, false);
} else {
hook(to, from, () => {}, router, false);
resolve();
}
} else {
resolve();
}
}
export function onTriggerEachHook(
to:totalNextRoute,
from: totalNextRoute,
router:Router,
hookType:hookToggle,
next:(rule?: navtoRule|false)=>void,
):void {
let callHookList:hookListRule = [];
switch (hookType) {
case 'beforeEach':
callHookList = HOOKLIST.slice(0, 3);
break;
case 'afterEach':
callHookList = HOOKLIST.slice(4);
break
case 'beforeEnter':
callHookList = HOOKLIST.slice(3, 4);
break;
}
transitionTo(router, to, from, 'push', callHookList, next);
}
export function transitionTo(
router:Router,
to:totalNextRoute,
from: totalNextRoute,
navType:NAVTYPE,
callHookList:hookListRule,
hookCB:Function
) :void{
const {matTo, matFrom} = forMatNextToFrom<totalNextRoute>(router, to, from);
if (router.options.platform === 'h5') {
loopCallHook(callHookList, 0, hookCB, router, matTo, matFrom, navType);
} else {
loopCallHook(callHookList.slice(0, 4), 0, () => {
hookCB(() => { // 非H5端等他跳转完才触发最后两个生命周期
loopCallHook(callHookList.slice(4), 0, voidFun, router, matTo, matFrom, navType);
});
}, router, matTo, matFrom, navType);
}
}
export function loopCallHook(
hooks:hookListRule,
index:number,
next:Function,
router:Router,
matTo:totalNextRoute,
matFrom: totalNextRoute,
navType:NAVTYPE,
): void|Function {
const toRoute = routesForMapRoute(router, matTo.path, ['finallyPathMap', 'pathMap']);
if (hooks.length - 1 < index) {
return next();
}
const hook = hooks[index];
const errHook = ERRORHOOK[0];
hook(router, matTo, matFrom, toRoute, (nextTo:reloadNavRule) => {
if (nextTo === false) {
if (router.options.platform === 'h5') {
next(false);
}
errHook({ type: 0, msg: '管道函数传递 false 导航被终止!', matTo, matFrom, nextTo }, router)
} else if (typeof nextTo === 'string' || typeof nextTo === 'object') {
let newNavType = navType;
let newNextTo = nextTo;
if (typeof nextTo === 'object') {
const {NAVTYPE: type, ...moreTo} = nextTo;
newNextTo = moreTo;
if (type != null) {
newNavType = type;
}
}
navjump(newNextTo, router, newNavType, {from: matFrom, next})
} else if (nextTo == null) {
index++;
loopCallHook(hooks, index, next, router, matTo, matFrom, navType)
} else {
errHook({ type: 1, msg: '管道函数传递未知类型,无法被识别。导航被终止!', matTo, matFrom, nextTo }, router)
}
});
}

View File

@@ -0,0 +1,255 @@
import {
NAVTYPE,
Router,
totalNextRoute,
objectAny,
routeRule,
reNavMethodRule,
rewriteMethodToggle,
navtypeToggle,
navErrorRule,
uniBackApiRule,
uniBackRule,
navRoute
} from '../options/base'
import {
queryPageToMap,
resolveQuery,
parseQuery
} from './query'
import {
voidFun,
paramsToQuery,
getUniCachePage,
routesForMapRoute,
copyData,
lockDetectWarn,
getDataType,
reservedWord,
notRouteTo404
} from '../helpers/utils'
import { transitionTo } from './hooks';
import {createFullPath, createToFrom} from '../public/page'
import {HOOKLIST} from './hooks'
export function lockNavjump(
to:string|totalNextRoute|navRoute,
router:Router,
navType:NAVTYPE,
forceNav?:boolean,
animation?:uniBackApiRule|uniBackRule
):void{
lockDetectWarn(router, to, navType, () => {
if (router.options.platform !== 'h5') {
router.$lockStatus = true;
}
navjump(to as totalNextRoute, router, navType, undefined, forceNav, animation);
});
}
export function navjump(
to:string|totalNextRoute,
router:Router,
navType:NAVTYPE,
nextCall?:{
from:totalNextRoute;
next:Function;
},
forceNav?:boolean,
animation?:uniBackApiRule|uniBackRule,
callHook:boolean|undefined = true
) :void|never|totalNextRoute {
if (navType === 'back') {
let level:number = 1;
if (typeof to === 'string') {
level = +to;
} else {
level = to.delta || 1;
}
if (router.options.platform === 'h5') {
(router.$route as any).go(-level);
// Fixe https://github.com/SilurianYang/uni-simple-router/issues/266 2021年6月3日11:14:38
// @ts-ignore
const success = (animation || {success: voidFun}).success || voidFun;
// @ts-ignore
const complete = (animation || {complete: voidFun}).complete || voidFun;
success({errMsg: 'navigateBack:ok'});
complete({errMsg: 'navigateBack:ok'});
return;
} else {
to = backOptionsBuild(router, level, animation);
}
}
to = reservedWord(to);
const {rule} = queryPageToMap(to, router);
rule.type = navtypeToggle[navType];
const toRule = paramsToQuery(router, rule);
let parseToRule = resolveQuery(toRule as totalNextRoute, router);
if (router.options.platform === 'h5') {
if (navType !== 'push') {
navType = 'replace';
}
if (nextCall != null) { // next 管道函数拦截时 直接next即可
nextCall.next({
replace: navType !== 'push',
...parseToRule
})
} else {
// Fixe https://github.com/SilurianYang/uni-simple-router/issues/240 2021年3月7日14:45:36
if (navType === 'push' && Reflect.has(parseToRule, 'events')) {
if (Reflect.has(parseToRule, 'name')) {
throw new Error(`在h5端上使用 'push'、'navigateTo' 跳转时,如果包含 events 不允许使用 name 跳转,因为 name 实现了动态路由。请更换为 path 或者 url 跳转!`);
} else {
uni['navigateTo'](parseToRule, true, voidFun, forceNav);
}
} else {
(router.$route as any)[navType](parseToRule, (parseToRule as totalNextRoute).success || voidFun, (parseToRule as totalNextRoute).fail || voidFun)
}
}
} else {
let from:totalNextRoute = {path: ''};
if (nextCall == null) {
let toRoute = routesForMapRoute(router, parseToRule.path, ['finallyPathMap', 'pathMap']);
toRoute = notRouteTo404(router, toRoute, parseToRule, navType);
parseToRule = { ...toRoute, ...{params: {}}, ...parseToRule, ...{path: toRoute.path} }
from = createToFrom(parseToRule, router);
} else {
from = nextCall.from;
}
createFullPath(parseToRule, from);
if (callHook === false) {
return parseToRule;
}
transitionTo(router, parseToRule, from, navType, HOOKLIST, function(
callOkCb:Function
):void {
uni[navtypeToggle[navType]](parseToRule, true, callOkCb, forceNav);
})
}
}
export function backOptionsBuild(
router:Router,
level:number,
animation?:uniBackApiRule|uniBackRule,
):totalNextRoute {
const toRule = createRoute(router, level);
const navjumpRule:totalNextRoute = {
...animation || {},
path: toRule.path,
query: toRule.query,
delta: level
}
if (getDataType<any>(animation) === '[object Object]') {
const {animationDuration, animationType} = (animation as uniBackApiRule)
if (animationDuration != null) {
navjumpRule.animationDuration = animationDuration;
}
if (animationType != null) {
navjumpRule.animationType = animationType;
}
const {from} = (animation as uniBackRule)
if (from != null) {
navjumpRule.BACKTYPE = from;
}
}
return navjumpRule;
}
export function forceGuardEach(
router:Router,
navType:NAVTYPE|undefined = 'replaceAll',
forceNav:undefined|boolean = false
):void|never {
if (router.options.platform === 'h5') {
throw new Error(`在h5端上使用forceGuardEach 是无意义的,目前 forceGuardEach 仅支持在非h5端上使用`);
}
const page = getUniCachePage<objectAny>(0);
if (Object.keys(page).length === 0) {
(router.options.routerErrorEach as (error: navErrorRule, router:Router) => void)({
type: 3,
msg: `不存在的页面栈,请确保有足够的页面可用,当前 level:0`
}, router);
}
const {route, options} = page as objectAny;
lockNavjump({
path: `/${route}`,
query: options
}, router, navType, forceNav);
}
export function createRoute(
router:Router,
level:number|undefined = 0,
orignRule?:totalNextRoute,
):routeRule|never {
const route:routeRule = {
name: '',
meta: {},
path: '',
fullPath: '',
NAVTYPE: '',
query: {},
params: {},
BACKTYPE: (orignRule || {BACKTYPE: ''}).BACKTYPE || '' // v2.0.5 +
};
if (level === 19970806) { // 首次构建响应式 页面不存在 直接返回
return route
}
if (router.options.platform === 'h5') {
let vueRoute:totalNextRoute = {path: ''};
if (orignRule != null) {
vueRoute = orignRule;
} else {
vueRoute = (router.$route as objectAny).currentRoute;
}
const matRouteParams = copyData(vueRoute.params as objectAny);
delete matRouteParams.__id__;
const toQuery = parseQuery({...matRouteParams, ...copyData(vueRoute.query as objectAny)}, router);
vueRoute = {...vueRoute, query: toQuery}
route.path = vueRoute.path;
route.fullPath = vueRoute.fullPath || '';
route.query = vueRoute.query || {};
route.NAVTYPE = rewriteMethodToggle[vueRoute.type as reNavMethodRule || 'reLaunch'];
} else {
let appPage:objectAny = {};
if (orignRule != null) {
appPage = {...orignRule, openType: orignRule.type};
} else {
const page = getUniCachePage<objectAny>(level);
if (Object.keys(page).length === 0) {
(router.options.routerErrorEach as (error: navErrorRule, router:Router) => void)({
type: 3,
msg: `不存在的页面栈,请确保有足够的页面可用,当前 level:${level}`
}, router);
throw new Error(`不存在的页面栈,请确保有足够的页面可用,当前 level:${level}`)
}
// Fixes: https://github.com/SilurianYang/uni-simple-router/issues/196
let pageOptions = (page as objectAny).options || {};
const originQuery = pageOptions.query;
if (originQuery != null && Object.keys(pageOptions).length === 1) {
pageOptions = JSON.parse(decodeURIComponent(originQuery))
}
const pageQuery = JSON.parse(decodeURIComponent(JSON.stringify(pageOptions)))
appPage = {
...(page as objectAny).$page || {},
query: pageQuery,
fullPath: decodeURIComponent(((page as objectAny).$page || {}).fullPath || '/' + (page as objectAny).route)
}
if (router.options.platform !== 'app-plus') {
appPage.path = `/${(page as objectAny).route}`
}
}
const openType:reNavMethodRule|'navigateBack' = appPage.openType;
route.query = appPage.query;
route.path = appPage.path;
route.fullPath = appPage.fullPath;
route.NAVTYPE = rewriteMethodToggle[openType || 'reLaunch'];
}
const tableRoute = routesForMapRoute(router, route.path, ['finallyPathMap', 'pathMap'])
const perfectRoute = { ...route, ...tableRoute};
perfectRoute.query = parseQuery(perfectRoute.query, router);
return perfectRoute;
}

View File

@@ -0,0 +1,41 @@
import { getDataType, getUniCachePage, deepClone, replaceHook} from '../helpers/utils';
import { objectAny, pageTypeRule, Router, totalNextRoute } from '../options/base';
import {createRoute} from './methods'
import { stringifyQuery } from './query';
export function createToFrom(
to:totalNextRoute,
router:Router,
):totalNextRoute {
let fromRoute:totalNextRoute = {path: ''};
const page = getUniCachePage<Array<any>|objectAny>(0);
if (getDataType<Array<any>|objectAny>(page) === '[object Array]') {
fromRoute = deepClone<totalNextRoute>(to)
} else {
fromRoute = createRoute(router) as totalNextRoute;
}
return fromRoute;
}
export function createFullPath(
to:totalNextRoute,
from:totalNextRoute
):void{
if (to.fullPath == null) {
const strQuery = stringifyQuery(to.query as objectAny);
to.fullPath = to.path + strQuery;
}
if (from.fullPath == null) {
const strQuery = stringifyQuery(from.query as objectAny);
from.fullPath = from.path + strQuery;
}
}
export function proxyPageHook(
vueVim:any,
router:Router,
proxyHookKey:'appProxyHook'|'appletsProxyHook',
pageType:pageTypeRule,
):void {
replaceHook(router, vueVim, proxyHookKey, pageType);
}

View File

@@ -0,0 +1,200 @@
import {
objectAny,
Router,
routesMapRule,
RoutesRule,
totalNextRoute
} from '../options/base';
import {
getDataType,
urlToJson,
routesForMapRoute,
getRoutePath,
assertDeepObject,
copyData,
getWildcardRule
} from '../helpers/utils'
import {ERRORHOOK} from './hooks'
import {warn} from '../helpers/warn'
const encodeReserveRE = /[!'()*]/g
const encodeReserveReplacer = (c:string) => '%' + c.charCodeAt(0).toString(16)
const commaRE = /%2C/g
const encode = (str:string) =>
encodeURIComponent(str)
.replace(encodeReserveRE, encodeReserveReplacer)
.replace(commaRE, ',')
export function queryPageToMap(
toRule:string|totalNextRoute,
router:Router
) :{
rule:totalNextRoute;
route:RoutesRule,
query:objectAny
} {
let query:objectAny = {};
let route:RoutesRule|string = '';
let successCb = (toRule as totalNextRoute).success;
let failCb = (toRule as totalNextRoute).fail;
if (getDataType<string|totalNextRoute>(toRule) === '[object Object]') {
const objNavRule = (toRule as totalNextRoute);
if (objNavRule.path != null) {
const {path, query: newQuery} = urlToJson(objNavRule.path);
route = routesForMapRoute(router, path, ['finallyPathList', 'pathMap']);
query = {...newQuery, ...((toRule as totalNextRoute).query || {})};
objNavRule.path = path;
objNavRule.query = query;
delete (toRule as totalNextRoute).params;
} else if (objNavRule.name != null) {
route = (router.routesMap as routesMapRule).nameMap[objNavRule.name];
if (route == null) {
route = getWildcardRule(router, { type: 2, msg: `命名路由为:${objNavRule.name} 的路由,无法在路由表中找到!`, toRule});
} else {
query = (toRule as totalNextRoute).params || {};
delete (toRule as totalNextRoute).query;
}
} else {
route = getWildcardRule(router, { type: 2, msg: `${toRule} 解析失败,请检测当前路由表下是否有包含。`, toRule});
}
} else {
toRule = urlToJson((toRule as string)) as totalNextRoute;
route = routesForMapRoute(router, toRule.path, ['finallyPathList', 'pathMap'])
query = toRule.query as objectAny;
}
if (router.options.platform === 'h5') {
const {finallyPath} = getRoutePath(route as RoutesRule, router);
if (finallyPath.includes(':') && (toRule as totalNextRoute).name == null) {
ERRORHOOK[0]({ type: 2, msg: `当有设置 alias或者aliasPath 为动态路由时,不允许使用 path 跳转。请使用 name 跳转!`, route}, router)
}
const completeCb = (toRule as totalNextRoute).complete;
const cacheSuccess = (toRule as totalNextRoute).success;
const cacheFail = (toRule as totalNextRoute).fail;
if (getDataType<Function|undefined>(completeCb) === '[object Function]') {
const publicCb = function(this:any, args:Array<any>, callHook:Function|undefined):void {
if (getDataType<Function|undefined>(callHook) === '[object Function]') {
(callHook as Function).apply(this, args);
}
(completeCb as Function).apply(this, args);
}
successCb = function(this:any, ...args:any):void{
publicCb.call(this, args, cacheSuccess);
};
failCb = function(this:any, ...args:any):void{
publicCb.call(this, args, cacheFail);
};
}
}
const rule = (toRule as totalNextRoute);
if (getDataType<Function|undefined>(rule.success) === '[object Function]') {
rule.success = successCb;
}
if (getDataType<Function|undefined>(rule.fail) === '[object Function]') {
rule.fail = failCb;
}
return {
rule,
route: (route as RoutesRule),
query
}
}
export function resolveQuery(
toRule:totalNextRoute,
router:Router
):totalNextRoute {
let queryKey:'params'|'query' = 'query';
if (toRule.params as objectAny != null) {
queryKey = 'params';
}
if (toRule.query as objectAny != null) {
queryKey = 'query';
}
const query = copyData(toRule[queryKey] || {});
const {resolveQuery: userResolveQuery} = router.options;
if (userResolveQuery) {
const jsonQuery = userResolveQuery(query);
if (getDataType<objectAny>(jsonQuery) !== '[object Object]') {
warn('请按格式返回参数: resolveQuery?:(jsonQuery:{[propName: string]: any;})=>{[propName: string]: any;}', router)
} else {
toRule[queryKey] = jsonQuery;
}
} else {
const deepObj = assertDeepObject(query as objectAny);
if (!deepObj) {
return toRule;
}
const encode = JSON.stringify(query);
toRule[queryKey] = {
query: encode
}
}
return toRule
}
export function parseQuery(
query:objectAny,
router:Router,
):objectAny {
const {parseQuery: userParseQuery} = router.options;
if (userParseQuery) {
query = userParseQuery(copyData(query));
if (getDataType<objectAny>(query) !== '[object Object]') {
warn('请按格式返回参数: parseQuery?:(jsonQuery:{[propName: string]: any;})=>{[propName: string]: any;}', router)
}
} else {
if (Reflect.get(query, 'query')) { // 验证一下是不是深度对象
const deepQuery = Reflect.get(query, 'query');
let jsonQuery:objectAny = {
query: decodeURIComponent(deepQuery)
};
try {
jsonQuery = JSON.parse(jsonQuery.query);
if (typeof jsonQuery === 'object') {
return jsonQuery;
}
} catch (error) {
warn('尝试解析深度对象失败,按原样输出。' + error, router)
}
}
}
return query
}
export function stringifyQuery(obj:objectAny): string {
const res = obj
? Object.keys(obj)
.map(key => {
const val = obj[key]
if (val === undefined) {
return ''
}
if (val === null) {
return encode(key)
}
if (Array.isArray(val)) {
const result:Array<any> = []
val.forEach(val2 => {
if (val2 === undefined) {
return
}
if (val2 === null) {
result.push(encode(key))
} else {
result.push(encode(key) + '=' + encode(val2))
}
})
return result.join('&')
}
return encode(key) + '=' + encode(val)
})
.filter(x => x.length > 0)
.join('&')
: null
return res ? `?${res}` : ''
}

View File

@@ -0,0 +1,158 @@
import {
uniNavApiRule,
reNavMethodRule,
reNotNavMethodRule,
Router,
rewriteMethodToggle,
uniBackRule,
uniBackApiRule,
navtoRule,
totalNextRoute,
originMixins,
objectAny
} from '../options/base'
import {
routesForMapRoute,
getRoutePath,
getDataType,
notDeepClearNull,
resolveAbsolutePath,
getUniCachePage,
timeOut
} from '../helpers/utils'
import {
warn
} from '../helpers/warn'
import {uniOriginJump} from './uniOrigin'
const rewrite: Array<reNavMethodRule|reNotNavMethodRule> = [
'navigateTo',
'redirectTo',
'reLaunch',
'switchTab',
'navigateBack'
];
export function rewriteMethod(
router:Router
): void {
if (router.options.keepUniOriginNav === false) {
rewrite.forEach(name => {
const oldMethod: Function = uni[name];
uni[name] = function(
params:originMixins|{from:string}|navtoRule,
originCall:boolean = false,
callOkCb?:Function,
forceNav?:boolean
):void {
if (originCall) {
uniOriginJump(router, oldMethod, name, params as originMixins, callOkCb, forceNav)
} else {
if (router.options.platform === 'app-plus') {
if (Object.keys(router.appMain).length === 0) {
router.appMain = {
NAVTYPE: name,
path: (params as uniNavApiRule).url
}
}
}
callRouterMethod(params as uniNavApiRule, name, router);
}
};
})
}
}
function callRouterMethod(
option: uniNavApiRule|uniBackRule|uniBackApiRule,
funName:reNavMethodRule|reNotNavMethodRule,
router:Router
): void {
if (router.options.platform === 'app-plus') {
let openType = null;
if (option) {
openType = (option as uniNavApiRule).openType;
}
if (openType != null && openType === 'appLaunch') {
funName = 'reLaunch'
}
}
if (funName === 'reLaunch' && JSON.stringify(option) === '{"url":"/"}') {
warn(
`uni-app 原生方法reLaunch({url:'/'}) 默认被重写啦!你可以使用 this.$Router.replaceAll() 或者 uni.reLaunch({url:'/?xxx=xxx'})`,
router,
true
);
funName = 'navigateBack';
option = {
from: 'backbutton'
}
}
if (funName === 'navigateBack') {
let level:number = 1;
if (option == null) {
option = {delta: 1};
}
if (getDataType<number|undefined>((option as uniBackApiRule).delta) === '[object Number]') {
level = ((option as uniBackApiRule).delta as number);
}
router.back(level, (option as uniBackRule|uniBackApiRule));
} else {
const routerMethodName = rewriteMethodToggle[(funName as reNavMethodRule)]
let path = (option as uniNavApiRule).url;
if (!path.startsWith('/')) {
const absolutePath = resolveAbsolutePath(path, router);
path = absolutePath;
(option as uniNavApiRule).url = absolutePath;
}
if (funName === 'switchTab') {
const route = routesForMapRoute(router, path, ['pathMap', 'finallyPathList'])
const {finallyPath} = getRoutePath(route, router);
if (getDataType<string | string[]>(finallyPath) === '[object Array]') {
warn(
`uni-app 原生方法跳转路径为:${path}。此路为是tab页面时不允许设置 alias 为数组的情况,并且不能为动态路由!当然你可以通过通配符*解决!`,
router,
true
);
}
if ((finallyPath as string) === '*') {
warn(
`uni-app 原生方法跳转路径为:${path}。在路由表中找不到相关路由表!当然你可以通过通配符*解决!`,
router,
true
);
}
// Fixe h5 端无法触发 onTabItemTap hook 2021年6月3日17:26:47
if (router.options.platform === 'h5') {
const {success: userSuccess} = option as uniNavApiRule;
(option as uniNavApiRule).success = (...args:Array<any>) => {
userSuccess?.apply(null, args);
timeOut(150).then(() => {
const cbArgs = (option as uniNavApiRule).detail || {};
if (Object.keys(cbArgs).length > 0 && Reflect.has(cbArgs, 'index')) {
const cachePage = getUniCachePage(0);
if (Object.keys(cachePage).length === 0) {
return false
}
const page = cachePage as objectAny;
const hooks = page.$options.onTabItemTap;
if (hooks) {
for (let j = 0; j < hooks.length; j++) {
hooks[j].call(page, cbArgs)
}
}
}
});
}
}
path = (finallyPath as string);
}
const {events, success, fail, complete, animationType, animationDuration} = option as uniNavApiRule;
const jumpOptions:totalNextRoute = {path, events, success, fail, complete, animationDuration, animationType};
router[routerMethodName](
notDeepClearNull<totalNextRoute>(jumpOptions)
)
}
}

View File

@@ -0,0 +1,122 @@
import {PromiseResolve, Router, uniBackApiRule, uniBackRule} from '../options/base';
import {InstantiateConfig, LifeCycleConfig} from '../options/config';
import {appProxyHook, indexProxyHook, lifeCycle, keyword} from '../helpers/config';
import {assertNewOptions, def, getDataType} from '../helpers/utils';
import {registerRouterHooks, registerEachHooks} from '../helpers/lifeCycle';
import {initMixins} from '../helpers/mixins'
import {lockNavjump, forceGuardEach, createRoute} from '../public/methods'
import {rewriteMethod} from '../public/rewrite'
let AppReadyResolve:PromiseResolve = () => {};
const AppReady:Promise<void> = new Promise(resolve => (AppReadyResolve = resolve));
function createRouter(params: InstantiateConfig):Router {
const options = assertNewOptions<InstantiateConfig>(params);
const router:Router = {
options,
mount: [],
Vue: null,
appProxyHook: appProxyHook,
appletsProxyHook: indexProxyHook,
appMain: {},
enterPath: '',
$route: null,
$lockStatus: false,
routesMap: {},
lifeCycle: registerRouterHooks<LifeCycleConfig>(lifeCycle, options),
push(to) {
lockNavjump(to, router, 'push');
},
replace(to) {
lockNavjump(to, router, 'replace');
},
replaceAll(to) {
lockNavjump(to, router, 'replaceAll');
},
pushTab(to) {
lockNavjump(to, router, 'pushTab');
},
back(level = 1, animation) {
if (getDataType(animation) !== '[object Object]') {
const backRule:uniBackRule = {
from: 'navigateBack'
}
animation = backRule;
} else {
if (!Reflect.has((animation as uniBackRule | uniBackApiRule), 'from')) {
animation = {
...animation,
from: 'navigateBack'
};
}
}
lockNavjump(level + '', router, 'back', undefined, animation)
},
forceGuardEach(navType, forceNav) {
forceGuardEach(router, navType, forceNav)
},
beforeEach(userGuard):void {
registerEachHooks(router, 'beforeHooks', userGuard);
},
afterEach(userGuard):void {
registerEachHooks(router, 'afterHooks', userGuard);
},
install(Vue:any):void{
router.Vue = Vue;
rewriteMethod(this);
initMixins(Vue, this);
Object.defineProperty(Vue.prototype, '$Router', {
get() {
return router;
}
});
Object.defineProperty(Vue.prototype, '$Route', {
get() {
return createRoute(router);
}
});
// 【Fixe】 https://github.com/SilurianYang/uni-simple-router/issues/254
Object.defineProperty(Vue.prototype, '$AppReady', {
get() {
if (router.options.platform === 'h5') {
return Promise.resolve();
}
return AppReady;
},
set(value:boolean) {
if (value === true) {
AppReadyResolve();
}
}
});
}
}
def(router, 'keyword', () => keyword);
def(router, 'currentRoute', () => createRoute(router));
router.beforeEach((to, from, next) => next());
router.afterEach(() => {});
return router;
}
function RouterMount(Vim:any, router:Router, el:string | undefined = '#app') :void|never {
if (getDataType<Array<any>>(router.mount) === '[object Array]') {
router.mount.push({
app: Vim,
el
})
} else {
throw new Error(`挂载路由失败router.app 应该为数组类型。当前类型:${typeof router.mount}`);
}
if (router.options.platform === 'h5') {
const vueRouter = (router.$route as any);
vueRouter.replace({
path: vueRouter.currentRoute.fullPath
});
} // 其他端目前不需要做啥
}
export {
RouterMount,
createRouter
}

View File

@@ -0,0 +1,99 @@
import { originMixins, reNavMethodRule, reNotNavMethodRule, Router, startAnimationRule, uniNavApiRule } from '../options/base';
import { stringifyQuery } from './query';
import {notDeepClearNull, resetPageHook, timeOut} from '../helpers/utils'
import { mpPlatformReg } from '../helpers/config';
let routerNavCount:number = 0;
let lastNavType:reNavMethodRule|reNotNavMethodRule = 'reLaunch'
export function uniOriginJump(
router:Router,
originMethod:Function,
funName:reNavMethodRule|reNotNavMethodRule,
options: originMixins,
callOkCb?:Function,
forceNav?:boolean
):void {
const {complete, ...originRule} = formatOriginURLQuery(router, options, funName);
const platform = router.options.platform.trim();
if (routerNavCount === 0 && platform !== 'h5') { // 还原app.vue下已经重写后的生命周期
resetPageHook(router, originRule.url)
}
if (forceNav != null && forceNav === false) {
if (routerNavCount === 0) {
routerNavCount++
// 【Fixe】 https://github.com/SilurianYang/uni-simple-router/issues/254
// 在小程序端 next 直接放行会执行这个
if (platform !== 'h5') {
router.Vue.prototype.$AppReady = true;
}
}
complete && complete.apply(null, {msg: 'forceGuardEach强制触发并且不执行跳转'});
callOkCb && callOkCb.apply(null, {msg: 'forceGuardEach强制触发并且不执行跳转'})
} else {
originMethod({
...originRule,
from: options.BACKTYPE,
complete: async function(...args:Array<any>) {
if (routerNavCount === 0) {
routerNavCount++
// 【Fixe】 https://github.com/SilurianYang/uni-simple-router/issues/254
// 在小程序端 第一次 next 做跳转 会触发这个 、在app端首次必定会触发这个
if (platform !== 'h5') {
router.Vue.prototype.$AppReady = true;
}
if (platform === 'app-plus') {
const waitPage = plus.nativeObj.View.getViewById('router-loadding');
waitPage && waitPage.close();
const launchedHook = router.options.APP?.launchedHook;
launchedHook && launchedHook();
}
}
let time:number = 0;
if (new RegExp(mpPlatformReg, 'g').test(platform)) {
time = (router.options.applet?.animationDuration) as number
} else if (platform === 'app-plus') {
if (funName === 'navigateBack' && lastNavType === 'navigateTo') {
time = (router.options.APP?.animation?.animationDuration) as number
}
}
if (funName === 'navigateTo' || funName === 'navigateBack') {
if (time !== 0) {
await timeOut(time);
}
}
lastNavType = funName;
complete && complete.apply(null, args);
callOkCb && callOkCb.apply(null, args)
}
});
}
}
export function formatOriginURLQuery(
router:Router,
options:uniNavApiRule,
funName:reNavMethodRule|reNotNavMethodRule
):uniNavApiRule {
const {url, path, query, animationType, animationDuration, events, success, fail, complete, delta, animation} = options;
const strQuery = stringifyQuery(query || {});
const queryURL = strQuery === '' ? (path || url) : (path || url) + strQuery;
let animationRule:startAnimationRule = {};
if (router.options.platform === 'app-plus') {
if (funName !== 'navigateBack') {
animationRule = router.options.APP?.animation || {};
animationRule = {...animationRule, ...animation || {}};
}
}
return notDeepClearNull<uniNavApiRule>({
delta,
url: queryURL,
animationType: animationType || animationRule.animationType,
animationDuration: animationDuration || animationRule.animationDuration,
events,
success,
fail,
complete
})
}

View File

@@ -0,0 +1,61 @@
import {createRouter, routesMapKeysRule} from '../src/index';
import {routesForMapRoute} from '../src/helpers/utils';
const routes = [
{path: '/pages/login/login', name: 'login', aliasPath: '/'},
{path: '/pages/page2/page2', name: 'page2', aliasPath: '/page2/:id'},
{path: '/pages/page3/page3', aliasPath: '/:name/page3/:id'},
{path: '/pages/animation/animation', aliasPath: '/an-(\\d+)-on'},
{path: '/static/1/1', aliasPath: '/static/(.*)'},
{path: '/dynamic/1/1', aliasPath: '/dynamic-*'},
{path: '*'}
];
const router = createRouter({
platform: 'h5',
keepUniOriginNav: true,
routes,
});
const Vue = function () {};
Vue.mixin = () => {};
router.install(Vue);
const rules: routesMapKeysRule[] = ['finallyPathMap', 'pathMap'];
it('匹配路由', () => {
const toRoute1 = routesForMapRoute(router, '/pages/login/login', rules);
expect(toRoute1).toEqual(routes[0]);
const toRoute2 = routesForMapRoute(router,'/pages/login/login?id=666',rules);
expect(toRoute2).toEqual(routes[0]);
const toRoute3 = routesForMapRoute(router, '/page2/6666', rules);
expect(toRoute3).toEqual(routes[1]);
const toRoute4 = routesForMapRoute(router, '/page2/6666?id=555', rules);
expect(toRoute4).toEqual(routes[1]);
const toRoute5 = routesForMapRoute(router, '/pages/page3/page3', rules);
expect(toRoute5).toEqual(routes[2]);
const toRoute6 = routesForMapRoute(router, '/test/page3/123', rules);
expect(toRoute6).toEqual(routes[2]);
const toRoute7 = routesForMapRoute(router, '/an-123-on', rules);
expect(toRoute7).toEqual(routes[3]);
const toRoute8 = routesForMapRoute(router, '/static/aaa/bbb?id=1444&name=999', rules);
expect(toRoute8).toEqual(routes[4]);
const toRoute9 = routesForMapRoute(router, '/dynamic-6666-5555', rules);
expect(toRoute9).toEqual(routes[5]);
const toRoute10 = routesForMapRoute(router, '/aaaaaa', rules);
expect(toRoute10).toEqual(routes[6]);
const toRoute11 = routesForMapRoute(router, '---48848--14545', rules);
expect(toRoute11).toEqual(routes[6]);
});

View File

@@ -0,0 +1,19 @@
{
"compilerOptions": {
"target": "es5",
"module": "CommonJS",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"lib": ["ES2015", "DOM"],
"outDir": "dist/src",
"declaration": true,
"declarationMap": false,
"rootDir": "./src",
"baseUrl": ".",
"paths": {}
},
"include": ["./src/global.d.ts", "./src/*"],
"exclude": ["node_modules", "**/*.spec.ts"]
}

View File

@@ -0,0 +1,35 @@
const {resolve} = require('path');
const CopyPlugin = require('copy-webpack-plugin');
module.exports = {
entry: './src/index.ts',
output: {
library: 'Router',
libraryTarget: 'umd',
},
resolve: {
extensions: ['.tsx', '.ts', 'd.ts', '.js', '.json'],
},
module: {
rules: [
{
test: /\.tsx?$/,
use: [
{
loader: 'ts-loader',
},
],
exclude: /node_modules/,
},
],
},
plugins: [
new CopyPlugin([
{
force: true,
from: resolve(__dirname, '../src/component'),
to: resolve(__dirname, '../dist'),
},
]),
],
};

View File

@@ -0,0 +1,22 @@
const {merge} = require("webpack-merge");
const {resolve} = require('path');
const common = require("./webpack.common.js");
const CopyPlugin = require('copy-webpack-plugin');
const output=resolve(__dirname, '../examples/uni-simple-router2.0/dist');
module.exports = merge(common, {
mode: 'development',
devtool: 'source-map',
output: {
path:output ,
filename: 'uni-simple-router.js',
},
plugins: [
new CopyPlugin([{
force: true,
from: resolve(__dirname, '../src/component'),
to: output,
}]),
]
});

View File

@@ -0,0 +1,19 @@
const {resolve} = require('path');
const {merge} = require("webpack-merge");
const common = require("./webpack.common.js");
const rimraf = require('rimraf');
function resolvePath(dir) {
return resolve(__dirname, '../', dir)
}
rimraf('dist', () => {});
module.exports = merge(common, {
mode: "production",
output: {
path: resolvePath('dist'),
filename: 'uni-simple-router.js',
},
})

View File

@@ -0,0 +1,356 @@
## [3.6.2](https://github.com/vuejs/vuex/compare/v3.6.1...v3.6.2) (2021-01-26)
### Bug Fixes
* **build:** fix wrong path name for the export module ([679313b](https://github.com/vuejs/vuex/commit/679313bf5e4de066f340a06ef9bfe08d1536fadd))
## [3.6.1](https://github.com/vuejs/vuex/compare/v3.6.0...v3.6.1) (2021-01-26)
### Bug Fixes
* fix tree shaking notworking in webpack bundle ([#1906](https://github.com/vuejs/vuex/issues/1906)) ([1dc2d1f](https://github.com/vuejs/vuex/commit/1dc2d1f21de42138053ea3281dde05487642a76d))
# [3.6.0](https://github.com/vuejs/vuex/compare/v3.5.1...v3.6.0) (2020-11-25)
### Bug Fixes
* stop throwing an error on `hasModule` when parent does not exists ([#1850](https://github.com/vuejs/vuex/issues/1850)) ([#1851](https://github.com/vuejs/vuex/issues/1851)) ([12aabe4](https://github.com/vuejs/vuex/commit/12aabe4cc470916d40691097dcb95badb8212f5c))
### Features
* **types:** adding logger type for logger plugin ([#1853](https://github.com/vuejs/vuex/issues/1853)) ([cb3198d](https://github.com/vuejs/vuex/commit/cb3198d5998bdb11ef05dfa5ef98d5c5fa873089))
* **build:** enable named esm module import on node.js >= 14 ([#1872](https://github.com/vuejs/vuex/issues/1872)) ([acddab2](https://github.com/vuejs/vuex/commit/acddab20769d1bb6125f2da78ac47561c682fc98))
## [3.5.1](https://github.com/vuejs/vuex/compare/v3.5.0...v3.5.1) (2020-06-29)
### Bug Fixes
* **types:** add missing `logger.d.ts` file to the package ([#1789](https://github.com/vuejs/vuex/issues/1789)) ([a477334](https://github.com/vuejs/vuex/commit/a477334b909913f6a92bdbedcf4a3016a62eab7a))
* warn when unregistering non existing module ([#1786](https://github.com/vuejs/vuex/issues/1786)) ([7cec79d](https://github.com/vuejs/vuex/commit/7cec79d339b874ec41f35891c891dfd27460c1d3))
# [3.5.0](https://github.com/vuejs/vuex/compare/v3.4.0...v3.5.0) (2020-06-29)
### Features
* include logger plugin to the core export ([#1783](https://github.com/vuejs/vuex/issues/1783)) ([04e2bd8](https://github.com/vuejs/vuex/commit/04e2bd8b3509c67398a6fe73a3d53660069feca8))
# [3.4.0](https://github.com/vuejs/vuex/compare/v3.3.0...v3.4.0) (2020-05-11)
### Features
* Allow action subscribers to catch rejections. ([#1740](https://github.com/vuejs/vuex/issues/1740)) ([6ebbe64](https://github.com/vuejs/vuex/commit/6ebbe64c5821d19e55a41dc8b1d81cfce6cbd195)), closes [#1489](https://github.com/vuejs/vuex/issues/1489) [#1558](https://github.com/vuejs/vuex/issues/1558) [#1625](https://github.com/vuejs/vuex/issues/1625)
# [3.3.0](https://github.com/vuejs/vuex/compare/v3.2.0...v3.3.0) (2020-04-25)
### Bug Fixes
* Prepend devtool handler ([#1358](https://github.com/vuejs/vuex/issues/1358)) ([a39d076](https://github.com/vuejs/vuex/commit/a39d0767e4041cdd5cf8050774106c01d39024e0)), closes [vuejs/vue-devtools#678](https://github.com/vuejs/vue-devtools/issues/678)
* **types:** Add `devtools` to store options type ([#1478](https://github.com/vuejs/vuex/issues/1478)) ([38c11dc](https://github.com/vuejs/vuex/commit/38c11dcbaea7d7e661a1623cabb5aef7c6e47ba7))
### Features
* Add `prepend` option for `subscribe` and `subscribeAction` ([#1358](https://github.com/vuejs/vuex/issues/1358)) ([a39d076](https://github.com/vuejs/vuex/commit/a39d0767e4041cdd5cf8050774106c01d39024e0))
* **logger:** `createLogger` can optionally log actions ([#987](https://github.com/vuejs/vuex/issues/987)) ([18be128](https://github.com/vuejs/vuex/commit/18be128ad933d1fca6da05c060f7664ce0c819ae))
# [3.2.0](https://github.com/vuejs/vuex/compare/v3.1.3...v3.2.0) (2020-04-19)
### Features
* add Store#hasModule(path) API ([#834](https://github.com/vuejs/vuex/issues/834)) ([d65d142](https://github.com/vuejs/vuex/commit/d65d14276e87aca17cfbd3fbf4af9e8dbb808f24))
## [3.1.3](https://github.com/vuejs/vuex/compare/v3.1.2...v3.1.3) (2020-03-09)
### Bug Fixes
* Prevent invalidating subscription iterator ([#1438](https://github.com/vuejs/vuex/issues/1438)) ([e012653](https://github.com/vuejs/vuex/commit/e0126533301febf66072f1865cf9a77778cf2176))
## [3.1.2](https://github.com/vuejs/vuex/compare/v3.1.1...v3.1.2) (2019-11-10)
### Bug Fixes
* tweak mapping helper warning message ([#1641](https://github.com/vuejs/vuex/issues/1641)) ([e60bc76](https://github.com/vuejs/vuex/commit/e60bc76154bb05c12b24342617b946d9a6e2f476))
* **types:** avoid broadening vue instance type when using map helpers ([#1639](https://github.com/vuejs/vuex/issues/1639)) ([9a96720](https://github.com/vuejs/vuex/commit/9a9672050bcfee198c379069ec0e1b03ca6cb965))
* add warnings when the different namespaced modules has the same names… ([#1554](https://github.com/vuejs/vuex/issues/1554)) ([91f3e69](https://github.com/vuejs/vuex/commit/91f3e69ed9e290cf91f8885c6d5ae2c97fa7ab81))
* Should vuex mapState print error message [#1093](https://github.com/vuejs/vuex/issues/1093) ([#1297](https://github.com/vuejs/vuex/issues/1297)) ([e5ca2d5](https://github.com/vuejs/vuex/commit/e5ca2d52e89a126bd48bd8a6003be77379960ea9))
* Warn about conflicts between state and module ([#1365](https://github.com/vuejs/vuex/issues/1365)) ([538ee58](https://github.com/vuejs/vuex/commit/538ee5803bbca2fc8077208fb30c8d56d8be5cae))
* **docs:** Clearify state object type ([#1601](https://github.com/vuejs/vuex/issues/1601)) ([de06f76](https://github.com/vuejs/vuex/commit/de06f76380e7429489c0eb15acc8e0b34a383860))
### Performance Improvements
* Implementing a cache for the gettersProxy object creation ([#1546](https://github.com/vuejs/vuex/issues/1546)) ([4003382](https://github.com/vuejs/vuex/commit/40033825b7259c2e9b702bdf94e0b24ed4511d7c))
## [3.1.1](https://github.com/vuejs/vuex/compare/v3.1.0...v3.1.1) (2019-05-08)
### Bug Fixes
* Memory leak happening while using registerModule/u… ([#1508](https://github.com/vuejs/vuex/issues/1508)) ([cb9986a](https://github.com/vuejs/vuex/commit/cb9986ae5a62e002a1d876e881ee5f31dd410888)), closes [issue#1507](https://github.com/issue/issues/1507)
* **types:** Make mutation and action payload optional in definition file ([#1517](https://github.com/vuejs/vuex/issues/1517)) ([0e109e2](https://github.com/vuejs/vuex/commit/0e109e2a38dafdc0c2bd6bd3892bc66cfe252b16)), closes [#1491](https://github.com/vuejs/vuex/issues/1491)
### Features
* **devtool:** allow usage in non-browser environments ([#1404](https://github.com/vuejs/vuex/issues/1404)) ([665455f](https://github.com/vuejs/vuex/commit/665455f8daf8512e7adbf63c2842bc0b1e39efdb))
* **esm build:** build ES modules for browser ([#1533](https://github.com/vuejs/vuex/issues/1533)) ([d7c7f98](https://github.com/vuejs/vuex/commit/d7c7f9844831f98c5c9aaca213746c4ccc5d6929))
# [3.1.0](https://github.com/vuejs/vuex/compare/v3.0.1...v3.1.0) (2019-01-17)
### Bug Fixes
* **types:** add helpers to default export type declaration as in sources ([#1408](https://github.com/vuejs/vuex/issues/1408)) ([404d0de](https://github.com/vuejs/vuex/commit/404d0de9531322a1a462e53dfd858d20f0bd99af))
* **types:** add type annotation for the context of actions ([#1322](https://github.com/vuejs/vuex/issues/1322)) ([d1b5c66](https://github.com/vuejs/vuex/commit/d1b5c66961ab53e0172cbc706ff616227bcb5c77))
* **types:** allow a function type for root `state` option ([#1132](https://github.com/vuejs/vuex/issues/1132)) ([d39791b](https://github.com/vuejs/vuex/commit/d39791bd05830b1889705761ef5779449e35e97f))
* Add key to v-for ([#1369](https://github.com/vuejs/vuex/issues/1369)) ([a9bd047](https://github.com/vuejs/vuex/commit/a9bd047ea147cacfcb4003946aeebccd2c5e1e4e))
* avoid to call root state function twice ([#1034](https://github.com/vuejs/vuex/issues/1034)) ([86677eb](https://github.com/vuejs/vuex/commit/86677ebcbfaecf712f339b73a568150fc9fd5f5e))
* fix [#1032](https://github.com/vuejs/vuex/issues/1032), relax vue typing in helpers ([#1044](https://github.com/vuejs/vuex/issues/1044)) ([7c7ed1d](https://github.com/vuejs/vuex/commit/7c7ed1d37ee8a5058082d763d80529e5fef86a0b))
### Features
* add ability to turn off devtools on vuex by passing an off options ([#1407](https://github.com/vuejs/vuex/issues/1407)) ([be75d41](https://github.com/vuejs/vuex/commit/be75d41cf54d50177a7db7e9218e8d1c820ae830))
* ensure errors in action subscribers do not break actions ([acd7249](https://github.com/vuejs/vuex/commit/acd72492eaffff3661f75860a3d7ab37b73c3906))
### Reverts
* Revert "Update util find (#1205)" (fix #1286) ([273bf86](https://github.com/vuejs/vuex/commit/273bf86b330ee580a73176c300919996b7d9c2c3)), closes [#1286](https://github.com/vuejs/vuex/issues/1286)
## [3.0.1](https://github.com/vuejs/vuex/compare/v3.0.0...v3.0.1) (2017-11-01)
# [3.0.0](https://github.com/vuejs/vuex/compare/v2.5.0...v3.0.0) (2017-10-11)
### Features
* **typings:** adapt to the new Vue typings ([#909](https://github.com/vuejs/vuex/issues/909)) ([65dbfec](https://github.com/vuejs/vuex/commit/65dbfec40d5fe7aac05aab333c7b70768997ca7f))
### BREAKING CHANGES
* **typings:** It is no longer compatible with the old Vue typings
* chore(package): bump typescript and vue core typings
* chore: bump vue
* Update package.json
# [2.5.0](https://github.com/vuejs/vuex/compare/v2.4.1...v2.5.0) (2017-10-11)
### Bug Fixes
* initialize root state as an empty object if state function returns no value ([#927](https://github.com/vuejs/vuex/issues/927)) ([0e9756b](https://github.com/vuejs/vuex/commit/0e9756b93c5de8e03286d93f0b50af5f8dfd3bac))
### Features
* add logger plugin logger config support ([#771](https://github.com/vuejs/vuex/issues/771)) ([804c3bb](https://github.com/vuejs/vuex/commit/804c3bbd2e60f11412f5a7cb7694969f8f6c215c))
* preserve state with registerModule ([#837](https://github.com/vuejs/vuex/issues/837)) ([4c1841e](https://github.com/vuejs/vuex/commit/4c1841e79e63ca0ca95d0cc1b218fde258f23c20))
* root actions in namespaced modules ([#941](https://github.com/vuejs/vuex/issues/941)) ([73189eb](https://github.com/vuejs/vuex/commit/73189eb35509de7d49bd2b577900ad560d37dcb0))
* subscribeAction ([#960](https://github.com/vuejs/vuex/issues/960)) ([a8326b1](https://github.com/vuejs/vuex/commit/a8326b1bd77158e7e5903eed4cc98b52599e3dbd))
## [2.4.1](https://github.com/vuejs/vuex/compare/v2.4.0...v2.4.1) (2017-09-27)
### Bug Fixes
* allow installation on extended Vue copies ([c87b72f](https://github.com/vuejs/vuex/commit/c87b72f2ff7f65e708c4b59a752ef234d0f28d1f))
* link to details of mutations in components ([#930](https://github.com/vuejs/vuex/issues/930)) ([e82782b](https://github.com/vuejs/vuex/commit/e82782ba81c398dd5b78a195257a9d1c3a6d85ef))
* move auto installation code into the store constructor ([#914](https://github.com/vuejs/vuex/issues/914)) ([852ac43](https://github.com/vuejs/vuex/commit/852ac43ea4813ecaeb1e5106c4a29c74e57c2fd7))
### Features
* allow to passing functions in mapActions/mapMutations (fix [#750](https://github.com/vuejs/vuex/issues/750)) ([#924](https://github.com/vuejs/vuex/issues/924)) ([be15f32](https://github.com/vuejs/vuex/commit/be15f32c0077d8fe9bafa38c1b319b655cfd5f86))
# [2.4.0](https://github.com/vuejs/vuex/compare/v2.3.0...v2.4.0) (2017-08-29)
### Bug Fixes
* **typings:** watch() returns an unwatch function ([#922](https://github.com/vuejs/vuex/issues/922)) ([a4bd081](https://github.com/vuejs/vuex/commit/a4bd0816838cc4a843d833363b9aa412c1256e5e))
* add missing typings and docs of createNamespacedHelpers ([#910](https://github.com/vuejs/vuex/issues/910)) ([7ad573b](https://github.com/vuejs/vuex/commit/7ad573bba59d23dbd66e3a25e6614296aeb98d42))
### Features
* **store:** bind mutation and action handlers to store ([#872](https://github.com/vuejs/vuex/issues/872)) ([67da622](https://github.com/vuejs/vuex/commit/67da6225552e46266ed059c7f0d0128294cd08ed))
### Performance Improvements
* do not connect devtools if Vue.config.devtools == false ([#881](https://github.com/vuejs/vuex/issues/881)) ([dd7f817](https://github.com/vuejs/vuex/commit/dd7f8178d93e6121a447c410b9c652f40cd80937))
# [2.3.0](https://github.com/vuejs/vuex/compare/v2.2.1...v2.3.0) (2017-04-13)
* Add '-loader' suffix to webpack config (#722) ([84b4634](https://github.com/vuejs/vuex/commit/84b463438ea4133f7f326dc18212e3d4b7b5a177)), closes [#722](https://github.com/vuejs/vuex/issues/722)
### BREAKING CHANGES
* It's no longer allowed to omit the '-loader' suffix when using loaders. You need to specify 'babel-loader' instead of 'babel'.
My version of webpack: 2.2.0-rc.3
Adding the '-loader' suffix fixed the problem.
Not sure though how safe it is to use 'babel-loader' instead of 'babel' with previous webpack versions...
## [2.2.1](https://github.com/vuejs/vuex/compare/v2.2.0...v2.2.1) (2017-02-26)
# [2.2.0](https://github.com/vuejs/vuex/compare/v2.1.2...v2.2.0) (2017-02-26)
## [2.1.2](https://github.com/vuejs/vuex/compare/v2.1.1...v2.1.2) (2017-02-06)
### Reverts
* Revert "Update modules.md (#534)" ([5e145b3](https://github.com/vuejs/vuex/commit/5e145b3a2d45977b52cfff41b3b663f629d67e74)), closes [#534](https://github.com/vuejs/vuex/issues/534)
## [2.1.1](https://github.com/vuejs/vuex/compare/v2.1.0...v2.1.1) (2016-12-17)
# [2.1.0](https://github.com/vuejs/vuex/compare/v2.0.0...v2.1.0) (2016-12-16)
# [2.0.0](https://github.com/vuejs/vuex/compare/v2.0.0-rc.6...v2.0.0) (2016-09-30)
# [2.0.0-rc.6](https://github.com/vuejs/vuex/compare/v2.0.0-rc.5...v2.0.0-rc.6) (2016-09-24)
# [2.0.0-rc.5](https://github.com/vuejs/vuex/compare/v2.0.0-rc.4...v2.0.0-rc.5) (2016-08-15)
# [2.0.0-rc.4](https://github.com/vuejs/vuex/compare/v2.0.0-rc.3...v2.0.0-rc.4) (2016-08-05)
# [2.0.0-rc.3](https://github.com/vuejs/vuex/compare/v2.0.0-rc.1...v2.0.0-rc.3) (2016-07-11)
# [2.0.0-rc.1](https://github.com/vuejs/vuex/compare/v1.0.0-rc...v2.0.0-rc.1) (2016-07-05)
# [1.0.0-rc](https://github.com/vuejs/vuex/compare/v0.8.2...v1.0.0-rc) (2016-07-01)
## [0.8.2](https://github.com/vuejs/vuex/compare/v0.8.1...v0.8.2) (2016-06-28)
## [0.8.1](https://github.com/vuejs/vuex/compare/v0.8.0...v0.8.1) (2016-06-28)
# [0.8.0](https://github.com/vuejs/vuex/compare/v0.7.1...v0.8.0) (2016-06-23)
## [0.7.1](https://github.com/vuejs/vuex/compare/v0.7.0...v0.7.1) (2016-06-22)
# [0.7.0](https://github.com/vuejs/vuex/compare/v0.6.3...v0.7.0) (2016-06-21)
## [0.6.3](https://github.com/vuejs/vuex/compare/v0.6.2...v0.6.3) (2016-04-23)
## [0.6.2](https://github.com/vuejs/vuex/compare/v0.6.1...v0.6.2) (2016-03-08)
## [0.6.1](https://github.com/vuejs/vuex/compare/v0.6.0...v0.6.1) (2016-03-07)
# [0.6.0](https://github.com/vuejs/vuex/compare/v0.5.1...v0.6.0) (2016-03-07)
## [0.5.1](https://github.com/vuejs/vuex/compare/v0.5.0...v0.5.1) (2016-03-04)
# [0.5.0](https://github.com/vuejs/vuex/compare/v0.4.2...v0.5.0) (2016-03-04)
## [0.4.2](https://github.com/vuejs/vuex/compare/v0.4.1...v0.4.2) (2016-03-02)
## [0.4.1](https://github.com/vuejs/vuex/compare/v0.4.0...v0.4.1) (2016-03-01)
# [0.4.0](https://github.com/vuejs/vuex/compare/v0.3.0...v0.4.0) (2016-03-01)
# [0.3.0](https://github.com/vuejs/vuex/compare/4a22523b8cf4a1954ec95a0083ddef6c085f4905...v0.3.0) (2016-02-16)
### Bug Fixes
* **api:** fix typo ([4a22523](https://github.com/vuejs/vuex/commit/4a22523b8cf4a1954ec95a0083ddef6c085f4905))
* **forms:** fix typo ([50094a6](https://github.com/vuejs/vuex/commit/50094a604f32d00ceb784a3fbf07c82c502faca2))

21
barter-app-main/barter-app/node_modules/vuex/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015-present Evan You
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

59
barter-app-main/barter-app/node_modules/vuex/README.md generated vendored Normal file
View File

@@ -0,0 +1,59 @@
# Vuex
[![npm](https://img.shields.io/npm/v/vuex.svg)](https://npmjs.com/package/vuex)
[![ci status](https://circleci.com/gh/vuejs/vuex/tree/dev.png?style=shield)](https://circleci.com/gh/vuejs/vuex)
---
:fire: **HEADS UP!** You're currently looking at Vuex 3 branch. If you're looking for Vuex 4, [please check out `4.0` branch](https://github.com/vuejs/vuex/tree/4.0).
---
Vuex is a state management pattern + library for Vue.js applications. It serves as a centralized store for all the components in an application, with rules ensuring that the state can only be mutated in a predictable fashion. It also integrates with Vue's official [devtools extension](https://github.com/vuejs/vue-devtools) to provide advanced features such as zero-config time-travel debugging and state snapshot export / import.
Learn more about Vuex at "[What is Vuex?](https://vuex.vuejs.org/)", or get started by looking into [full documentation](http://vuex.vuejs.org/).
## Documentation
To check out docs, visit [vuex.vuejs.org](https://vuex.vuejs.org/).
## Examples
- [Counter](https://github.com/vuejs/vuex/tree/dev/examples/counter)
- [Counter with Hot Reload](https://github.com/vuejs/vuex/tree/dev/examples/counter-hot)
- [TodoMVC](https://github.com/vuejs/vuex/tree/dev/examples/todomvc)
- [Flux Chat](https://github.com/vuejs/vuex/tree/dev/examples/chat)
- [Shopping Cart](https://github.com/vuejs/vuex/tree/dev/examples/shopping-cart)
Running the examples:
```bash
$ npm install
$ npm run dev # serve examples at localhost:8080
```
## Questions
For questions and support please use the [Discord chat server](https://chat.vuejs.org) or [the official forum](http://forum.vuejs.org). The issue list of this repo is **exclusively** for bug reports and feature requests.
## Issues
Please make sure to read the [Issue Reporting Checklist](https://github.com/vuejs/vuex/blob/dev/.github/contributing.md#issue-reporting-guidelines) before opening an issue. Issues not conforming to the guidelines may be closed immediately.
## Changelog
Detailed changes for each release are documented in the [release notes](https://github.com/vuejs/vuex/releases).
## Stay In Touch
For latest releases and announcements, follow on Twitter: [@vuejs](https://twitter.com/vuejs).
## Contribution
Please make sure to read the [Contributing Guide](https://github.com/vuejs/vuex/blob/dev/.github/contributing.md) before making a pull request.
## License
[MIT](http://opensource.org/licenses/MIT)
Copyright (c) 2015-present Evan You

View File

@@ -0,0 +1,155 @@
/*!
* vuex v3.6.2
* (c) 2021 Evan You
* @license MIT
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Vuex = factory());
}(this, (function () { 'use strict';
/**
* Get the first item that pass the test
* by second argument function
*
* @param {Array} list
* @param {Function} f
* @return {*}
*/
function find (list, f) {
return list.filter(f)[0]
}
/**
* Deep copy the given object considering circular structure.
* This function caches all nested objects and its copies.
* If it detects circular structure, use cached copy to avoid infinite loop.
*
* @param {*} obj
* @param {Array<Object>} cache
* @return {*}
*/
function deepCopy (obj, cache) {
if ( cache === void 0 ) cache = [];
// just return if obj is immutable value
if (obj === null || typeof obj !== 'object') {
return obj
}
// if obj is hit, it is in circular structure
var hit = find(cache, function (c) { return c.original === obj; });
if (hit) {
return hit.copy
}
var copy = Array.isArray(obj) ? [] : {};
// put the copy into cache at first
// because we want to refer it in recursive deepCopy
cache.push({
original: obj,
copy: copy
});
Object.keys(obj).forEach(function (key) {
copy[key] = deepCopy(obj[key], cache);
});
return copy
}
// Credits: borrowed code from fcomb/redux-logger
function createLogger (ref) {
if ( ref === void 0 ) ref = {};
var collapsed = ref.collapsed; if ( collapsed === void 0 ) collapsed = true;
var filter = ref.filter; if ( filter === void 0 ) filter = function (mutation, stateBefore, stateAfter) { return true; };
var transformer = ref.transformer; if ( transformer === void 0 ) transformer = function (state) { return state; };
var mutationTransformer = ref.mutationTransformer; if ( mutationTransformer === void 0 ) mutationTransformer = function (mut) { return mut; };
var actionFilter = ref.actionFilter; if ( actionFilter === void 0 ) actionFilter = function (action, state) { return true; };
var actionTransformer = ref.actionTransformer; if ( actionTransformer === void 0 ) actionTransformer = function (act) { return act; };
var logMutations = ref.logMutations; if ( logMutations === void 0 ) logMutations = true;
var logActions = ref.logActions; if ( logActions === void 0 ) logActions = true;
var logger = ref.logger; if ( logger === void 0 ) logger = console;
return function (store) {
var prevState = deepCopy(store.state);
if (typeof logger === 'undefined') {
return
}
if (logMutations) {
store.subscribe(function (mutation, state) {
var nextState = deepCopy(state);
if (filter(mutation, prevState, nextState)) {
var formattedTime = getFormattedTime();
var formattedMutation = mutationTransformer(mutation);
var message = "mutation " + (mutation.type) + formattedTime;
startMessage(logger, message, collapsed);
logger.log('%c prev state', 'color: #9E9E9E; font-weight: bold', transformer(prevState));
logger.log('%c mutation', 'color: #03A9F4; font-weight: bold', formattedMutation);
logger.log('%c next state', 'color: #4CAF50; font-weight: bold', transformer(nextState));
endMessage(logger);
}
prevState = nextState;
});
}
if (logActions) {
store.subscribeAction(function (action, state) {
if (actionFilter(action, state)) {
var formattedTime = getFormattedTime();
var formattedAction = actionTransformer(action);
var message = "action " + (action.type) + formattedTime;
startMessage(logger, message, collapsed);
logger.log('%c action', 'color: #03A9F4; font-weight: bold', formattedAction);
endMessage(logger);
}
});
}
}
}
function startMessage (logger, message, collapsed) {
var startMessage = collapsed
? logger.groupCollapsed
: logger.group;
// render
try {
startMessage.call(logger, message);
} catch (e) {
logger.log(message);
}
}
function endMessage (logger) {
try {
logger.groupEnd();
} catch (e) {
logger.log('—— log end ——');
}
}
function getFormattedTime () {
var time = new Date();
return (" @ " + (pad(time.getHours(), 2)) + ":" + (pad(time.getMinutes(), 2)) + ":" + (pad(time.getSeconds(), 2)) + "." + (pad(time.getMilliseconds(), 3)))
}
function repeat (str, times) {
return (new Array(times + 1)).join(str)
}
function pad (num, maxLength) {
return repeat('0', maxLength - num.toString().length) + num
}
return createLogger;
})));

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

1250
barter-app-main/barter-app/node_modules/vuex/dist/vuex.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,26 @@
import Vuex from '../dist/vuex.common.js'
const {
Store,
install,
version,
mapState,
mapMutations,
mapGetters,
mapActions,
createNamespacedHelpers,
createLogger
} = Vuex
export {
Vuex as default,
Store,
install,
version,
mapState,
mapMutations,
mapGetters,
mapActions,
createNamespacedHelpers,
createLogger
}

View File

@@ -0,0 +1,124 @@
{
"_from": "vuex",
"_id": "vuex@3.6.2",
"_inBundle": false,
"_integrity": "sha512-ETW44IqCgBpVomy520DT5jf8n0zoCac+sxWnn+hMe/CzaSejb/eVw2YToiXYX+Ex/AuHHia28vWTq4goAexFbw==",
"_location": "/vuex",
"_phantomChildren": {},
"_requested": {
"type": "tag",
"registry": true,
"raw": "vuex",
"name": "vuex",
"escapedName": "vuex",
"rawSpec": "",
"saveSpec": null,
"fetchSpec": "latest"
},
"_requiredBy": [
"#USER",
"/"
],
"_resolved": "https://registry.npmjs.org/vuex/-/vuex-3.6.2.tgz",
"_shasum": "236bc086a870c3ae79946f107f16de59d5895e71",
"_spec": "vuex",
"_where": "/Users/WebTmm/Desktop/barter-app",
"author": {
"name": "Evan You"
},
"bugs": {
"url": "https://github.com/vuejs/vuex/issues"
},
"bundleDependencies": false,
"deprecated": false,
"description": "state management for Vue.js",
"devDependencies": {
"@babel/core": "^7.12.10",
"@babel/preset-env": "^7.12.11",
"@rollup/plugin-buble": "^0.21.3",
"@rollup/plugin-commonjs": "^11.1.0",
"@rollup/plugin-node-resolve": "^7.1.3",
"@rollup/plugin-replace": "^2.3.2",
"@types/node": "^13.13.5",
"@vuepress/theme-vue": "^1.8.0",
"babel-jest": "^26.6.3",
"babel-loader": "^8.2.2",
"brotli": "^1.3.2",
"chalk": "^4.0.0",
"conventional-changelog-cli": "^2.1.1",
"cross-env": "^5.2.0",
"css-loader": "^2.1.0",
"enquirer": "^2.3.5",
"eslint": "^6.8.0",
"eslint-plugin-vue-libs": "^4.0.0",
"execa": "^5.0.0",
"express": "^4.17.1",
"jest": "^26.6.3",
"puppeteer": "^4.0.0",
"regenerator-runtime": "^0.13.5",
"rollup": "^2.38.0",
"rollup-plugin-terser": "^5.3.0",
"semver": "^7.3.2",
"start-server-and-test": "^1.11.7",
"todomvc-app-css": "^2.3.0",
"typescript": "^3.8.3",
"vue": "2.5.22",
"vue-loader": "15.2.1",
"vue-server-renderer": "2.5.22",
"vue-template-compiler": "2.5.22",
"vuepress": "^1.8.0",
"webpack": "^4.43.0",
"webpack-dev-middleware": "^3.7.2",
"webpack-hot-middleware": "^2.25.0"
},
"exports": {
".": {
"module": "./dist/vuex.esm.js",
"require": "./dist/vuex.common.js",
"import": "./dist/vuex.mjs"
},
"./": "./"
},
"files": [
"dist",
"types/index.d.ts",
"types/helpers.d.ts",
"types/logger.d.ts",
"types/vue.d.ts"
],
"homepage": "https://github.com/vuejs/vuex#readme",
"jsdelivr": "dist/vuex.js",
"license": "MIT",
"main": "dist/vuex.common.js",
"module": "dist/vuex.esm.js",
"name": "vuex",
"peerDependencies": {
"vue": "^2.0.0"
},
"repository": {
"type": "git",
"url": "git+https://github.com/vuejs/vuex.git"
},
"scripts": {
"build": "npm run build:main && npm run build:logger",
"build:logger": "node scripts/build-logger.js",
"build:main": "node scripts/build-main.js",
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s",
"coverage": "jest --testPathIgnorePatterns test/e2e --coverage",
"dev": "node examples/server.js",
"docs": "vuepress dev docs",
"docs:build": "vuepress build docs",
"lint": "eslint src test",
"release": "node scripts/release.js",
"test": "npm run lint && npm run test:types && npm run test:unit && npm run test:ssr && npm run test:e2e && npm run test:esm",
"test:e2e": "start-server-and-test dev http://localhost:8080 \"jest --testPathIgnorePatterns test/unit\"",
"test:esm": "node test/esm/esm-test.js",
"test:ssr": "cross-env VUE_ENV=server jest --testPathIgnorePatterns test/e2e",
"test:types": "tsc -p types/test",
"test:unit": "jest --testPathIgnorePatterns test/e2e"
},
"sideEffects": false,
"typings": "types/index.d.ts",
"unpkg": "dist/vuex.js",
"version": "3.6.2"
}

View File

@@ -0,0 +1,86 @@
import Vue from 'vue';
import { Dispatch, Commit } from './index';
type Computed = () => any;
type InlineComputed<T extends Function> = T extends (...args: any[]) => infer R ? () => R : never
type MutationMethod = (...args: any[]) => void;
type ActionMethod = (...args: any[]) => Promise<any>;
type InlineMethod<T extends (fn: any, ...args: any[]) => any> = T extends (fn: any, ...args: infer Args) => infer R ? (...args: Args) => R : never
type CustomVue = Vue & Record<string, any>;
interface Mapper<R> {
<Key extends string>(map: Key[]): { [K in Key]: R };
<Map extends Record<string, string>>(map: Map): { [K in keyof Map]: R };
}
interface MapperWithNamespace<R> {
<Key extends string>(namespace: string, map: Key[]): { [K in Key]: R };
<Map extends Record<string, string>>(namespace: string, map: Map): { [K in keyof Map]: R };
}
interface MapperForState {
<S, Map extends Record<string, (this: CustomVue, state: S, getters: any) => any> = {}>(
map: Map
): { [K in keyof Map]: InlineComputed<Map[K]> };
}
interface MapperForStateWithNamespace {
<S, Map extends Record<string, (this: CustomVue, state: S, getters: any) => any> = {}>(
namespace: string,
map: Map
): { [K in keyof Map]: InlineComputed<Map[K]> };
}
interface MapperForAction {
<Map extends Record<string, (this: CustomVue, dispatch: Dispatch, ...args: any[]) => any>>(
map: Map
): { [K in keyof Map]: InlineMethod<Map[K]> };
}
interface MapperForActionWithNamespace {
<Map extends Record<string, (this: CustomVue, dispatch: Dispatch, ...args: any[]) => any>>(
namespace: string,
map: Map
): { [K in keyof Map]: InlineMethod<Map[K]> };
}
interface MapperForMutation {
<Map extends Record<string, (this: CustomVue, commit: Commit, ...args: any[]) => any>>(
map: Map
): { [K in keyof Map]: InlineMethod<Map[K]> };
}
interface MapperForMutationWithNamespace {
<Map extends Record<string, (this: CustomVue, commit: Commit, ...args: any[]) => any>>(
namespace: string,
map: Map
): { [K in keyof Map]: InlineMethod<Map[K]> };
}
interface NamespacedMappers {
mapState: Mapper<Computed> & MapperForState;
mapMutations: Mapper<MutationMethod> & MapperForMutation;
mapGetters: Mapper<Computed>;
mapActions: Mapper<ActionMethod> & MapperForAction;
}
export declare const mapState: Mapper<Computed>
& MapperWithNamespace<Computed>
& MapperForState
& MapperForStateWithNamespace;
export declare const mapMutations: Mapper<MutationMethod>
& MapperWithNamespace<MutationMethod>
& MapperForMutation
& MapperForMutationWithNamespace;
export declare const mapGetters: Mapper<Computed>
& MapperWithNamespace<Computed>;
export declare const mapActions: Mapper<ActionMethod>
& MapperWithNamespace<ActionMethod>
& MapperForAction
& MapperForActionWithNamespace;
export declare function createNamespacedHelpers(namespace: string): NamespacedMappers;

View File

@@ -0,0 +1,164 @@
import _Vue, { WatchOptions } from "vue";
// augment typings of Vue.js
import "./vue";
import { mapState, mapMutations, mapGetters, mapActions, createNamespacedHelpers } from "./helpers";
import createLogger from "./logger";
export * from "./helpers";
export * from "./logger";
export declare class Store<S> {
constructor(options: StoreOptions<S>);
readonly state: S;
readonly getters: any;
replaceState(state: S): void;
dispatch: Dispatch;
commit: Commit;
subscribe<P extends MutationPayload>(fn: (mutation: P, state: S) => any, options?: SubscribeOptions): () => void;
subscribeAction<P extends ActionPayload>(fn: SubscribeActionOptions<P, S>, options?: SubscribeOptions): () => void;
watch<T>(getter: (state: S, getters: any) => T, cb: (value: T, oldValue: T) => void, options?: WatchOptions): () => void;
registerModule<T>(path: string, module: Module<T, S>, options?: ModuleOptions): void;
registerModule<T>(path: string[], module: Module<T, S>, options?: ModuleOptions): void;
unregisterModule(path: string): void;
unregisterModule(path: string[]): void;
hasModule(path: string): boolean;
hasModule(path: string[]): boolean;
hotUpdate(options: {
actions?: ActionTree<S, S>;
mutations?: MutationTree<S>;
getters?: GetterTree<S, S>;
modules?: ModuleTree<S>;
}): void;
}
export declare function install(Vue: typeof _Vue): void;
export interface Dispatch {
(type: string, payload?: any, options?: DispatchOptions): Promise<any>;
<P extends Payload>(payloadWithType: P, options?: DispatchOptions): Promise<any>;
}
export interface Commit {
(type: string, payload?: any, options?: CommitOptions): void;
<P extends Payload>(payloadWithType: P, options?: CommitOptions): void;
}
export interface ActionContext<S, R> {
dispatch: Dispatch;
commit: Commit;
state: S;
getters: any;
rootState: R;
rootGetters: any;
}
export interface Payload {
type: string;
}
export interface MutationPayload extends Payload {
payload: any;
}
export interface ActionPayload extends Payload {
payload: any;
}
export interface SubscribeOptions {
prepend?: boolean
}
export type ActionSubscriber<P, S> = (action: P, state: S) => any;
export type ActionErrorSubscriber<P, S> = (action: P, state: S, error: Error) => any;
export interface ActionSubscribersObject<P, S> {
before?: ActionSubscriber<P, S>;
after?: ActionSubscriber<P, S>;
error?: ActionErrorSubscriber<P, S>;
}
export type SubscribeActionOptions<P, S> = ActionSubscriber<P, S> | ActionSubscribersObject<P, S>;
export interface DispatchOptions {
root?: boolean;
}
export interface CommitOptions {
silent?: boolean;
root?: boolean;
}
export interface StoreOptions<S> {
state?: S | (() => S);
getters?: GetterTree<S, S>;
actions?: ActionTree<S, S>;
mutations?: MutationTree<S>;
modules?: ModuleTree<S>;
plugins?: Plugin<S>[];
strict?: boolean;
devtools?: boolean;
}
export type ActionHandler<S, R> = (this: Store<R>, injectee: ActionContext<S, R>, payload?: any) => any;
export interface ActionObject<S, R> {
root?: boolean;
handler: ActionHandler<S, R>;
}
export type Getter<S, R> = (state: S, getters: any, rootState: R, rootGetters: any) => any;
export type Action<S, R> = ActionHandler<S, R> | ActionObject<S, R>;
export type Mutation<S> = (state: S, payload?: any) => any;
export type Plugin<S> = (store: Store<S>) => any;
export interface Module<S, R> {
namespaced?: boolean;
state?: S | (() => S);
getters?: GetterTree<S, R>;
actions?: ActionTree<S, R>;
mutations?: MutationTree<S>;
modules?: ModuleTree<R>;
}
export interface ModuleOptions {
preserveState?: boolean;
}
export interface GetterTree<S, R> {
[key: string]: Getter<S, R>;
}
export interface ActionTree<S, R> {
[key: string]: Action<S, R>;
}
export interface MutationTree<S> {
[key: string]: Mutation<S>;
}
export interface ModuleTree<R> {
[key: string]: Module<any, R>;
}
export { createLogger }
declare const _default: {
Store: typeof Store;
install: typeof install;
mapState: typeof mapState,
mapMutations: typeof mapMutations,
mapGetters: typeof mapGetters,
mapActions: typeof mapActions,
createNamespacedHelpers: typeof createNamespacedHelpers,
createLogger: typeof createLogger
};
export default _default;

View File

@@ -0,0 +1,20 @@
import { Payload, Plugin } from "./index";
interface Logger extends Partial<Pick<Console, 'groupCollapsed' | 'group' | 'groupEnd'>> {
log(message: string, color: string, payload: any): void;
log(message: string): void;
}
export interface LoggerOption<S> {
collapsed?: boolean;
filter?: <P extends Payload>(mutation: P, stateBefore: S, stateAfter: S) => boolean;
transformer?: (state: S) => any;
mutationTransformer?: <P extends Payload>(mutation: P) => any;
actionFilter?: <P extends Payload>(action: P, state: S) => boolean;
actionTransformer?: <P extends Payload>(action: P) => any;
logMutations?: boolean;
logActions?: boolean;
logger?: Logger;
}
export default function createLogger<S>(option?: LoggerOption<S>): Plugin<S>;

View File

@@ -0,0 +1,18 @@
/**
* Extends interfaces in Vue.js
*/
import Vue, { ComponentOptions } from "vue";
import { Store } from "./index";
declare module "vue/types/options" {
interface ComponentOptions<V extends Vue> {
store?: Store<any>;
}
}
declare module "vue/types/vue" {
interface Vue {
$store: Store<any>;
}
}

View File

@@ -0,0 +1,21 @@
{
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"uni-read-pages": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/uni-read-pages/-/uni-read-pages-1.0.5.tgz",
"integrity": "sha512-GkrrZ0LX0vn9R5k6RKEi0Ez3Q3e2vUpjXQ8Z6/K/d28KudI9ajqgt8WEjQFlG5EPm1K6uTArN8LlqmZTEixDUA=="
},
"uni-simple-router": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/uni-simple-router/-/uni-simple-router-2.0.6.tgz",
"integrity": "sha512-n5gepoT3QcBrvVLeTCY/DjUjC4m1hNzwFlOa1VlCsww/4/34MGfvegpN9OWR8jOxxtUPKGHLAG0yODL/ITCwbw=="
},
"vuex": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/vuex/-/vuex-3.6.2.tgz",
"integrity": "sha512-ETW44IqCgBpVomy520DT5jf8n0zoCac+sxWnn+hMe/CzaSejb/eVw2YToiXYX+Ex/AuHHia28vWTq4goAexFbw=="
}
}
}

View File

@@ -0,0 +1,7 @@
{
"dependencies": {
"uni-read-pages": "^1.0.5",
"uni-simple-router": "^2.0.6",
"vuex": "^3.6.2"
}
}

View File

@@ -0,0 +1,366 @@
{
"pages": [
{
"path": "pages/equity/index",
"name": "Equity",
"style":{
"navigationStyle":"custom",
"navigationBarTextStyle":"white"
}
},{
"path": "pages/market/index",
"name": "Market",
"style": {
"navigationBarTitleText":"转让市场",
"titleNView": {
"backgroundColor": "#FFFFFF",
"buttons": [
{
"text": "成交历史",
"fontSize": "14",
"width": "80",
"color": "#555555"
}
]
}
}
},{
"path": "pages/store/index",
"name": "Store",
"style": {
"navigationStyle":"custom",
"navigationBarTitleText":"企业工具",
"navigationBarTextStyle":"white",
"navigationBarBackgroundColor":"#e93340"
}
},{
"path": "pages/property/index",
"name": "Property",
"style": {
"navigationBarTitleText": "",
"navigationStyle":"custom",
"navigationBarTextStyle":"white"
}
},{
"path" : "pages/goods/details",
"name" : "goodsDetails",
"style": {
"navigationBarTitleText":"",
"titleNView": {
"backgroundColor": "#FFFFFF",
"type": "transparent",
"buttons": [
{
"text": "分享",
"fontSize": "12",
"color": "#555555"
}
]
}
}
},{
"path": "pages/login/login",
"name": "Login",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#FFFFFF",
"disableScroll": true
}
},{
"path" : "pages/company/registered",
"name" : "Registered",
"style":{
"navigationBarTitleText": ""
}
},{
"path" : "pages/company/prompt",
"name" : "Prompt",
"style":{
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#FFFFFF",
"disableScroll": true,
"titleNView": {
"backgroundColor": "#FFFFFF",
"buttons": [
{
"text": "先逛一逛",
"fontSize": "14",
"width": "80",
"color": "#555555"
}
]
}
}
},{
"path": "pages/vip/index",
"name": "Vip",
"style": {
"navigationBarTitleText": "会员",
"navigationBarBackgroundColor":"#1f1b1c",
"navigationBarTextStyle":"white",
"backgroundColor":"#fefaef"
}
}
,{
"path" : "pages/equity/search",
"name" : "Search",
"style":{
"navigationBarTitleText": "搜索"
}
},{
"path" : "pages/market/logs",
"name" : "marketLogs",
"style" :{
"navigationBarTitleText": "成交历史"
}
},{
"path" : "pages/order/buy",
"name" : "Buy",
"style" :{
"navigationBarTitleText": "确认订单",
"navigationBarBackgroundColor":"#FFFFFF"
}
},{
"path" : "pages/order/index",
"name" : "Order",
"style" :{
"navigationBarTitleText": "订单管理"
}
},{
"path" : "pages/order/details",
"name" : "OrderDetails",
"style" :{
"navigationBarTitleText": "订单详情"
}
},{
"path" : "pages/order/sales",
"name" : "Sales",
"style":{
"navigationBarTitleText": "售后"
}
},{
"path" : "pages/goods/lists",
"name" : "goodsList",
"style" :{
"navigationBarTitleText": "商品列表",
"navigationBarBackgroundColor":"#FFFFFF"
}
},{
"path" : "pages/company/approve",
"name" : "Approve",
"style" :{
"navigationBarTitleText": ""
}
},{
"path" : "pages/store/visitors",
"name" : "Visitors",
"style" : {
"navigationBarTitleText": "访客统计",
"navigationBarBackgroundColor":"#FFFFFF"
}
},{
"path" : "pages/store/customer",
"name" : "Customer",
"style" : {
"navigationBarTitleText": "成交客户",
"navigationBarBackgroundColor":"#FFFFFF"
}
},{
"path" : "pages/store/basics",
"name" : "Basics",
"style" :{
"navigationBarTitleText": "基础信息",
"app-plus":{
"titleNView": {
"backgroundColor": "#FFFFFF",
"buttons": [
{
"text": "保存",
"fontSize": "16",
"width": "80",
"color": "#e93340"
}
]
}
}
}
},{
"path" : "pages/employees/list",
"name" : "Employees",
"style" : {
"navigationBarTitleText": "员工",
"app-plus":{
"titleNView": {
"backgroundColor": "#FFFFFF",
"buttons": [
{
"text": "添加",
"fontSize": "14",
"width": "80",
"color": "#e93340"
}
]
}
}
}
},{
"path" : "pages/employees/add",
"name" : "addEmployees",
"style":{
"navigationBarTitleText": "添加员工",
"navigationBarBackgroundColor":"#FFFFFF"
}
},{
"path" : "pages/goods/management",
"name" : "GoodsMag",
"style" :{
"navigationBarTitleText": "产品权证",
"app-plus":{
"titleNView": {
"backgroundColor": "#FFFFFF",
"buttons": [
{
"text": "发布",
"fontSize": "16",
"width": "80",
"color": "#e93340"
}
]
}
}
}
},{
"path" : "pages/goods/add",
"name" : "GoodsMagAdd",
"style":{
"navigationBarTitleText": "发布权证",
"navigationBarBackgroundColor":"#FFFFFF"
}
},{
"path" : "pages/coupons/index",
"name" : "Coupons",
"style": {
"navigationBarTitleText": "优惠券"
}
},{
"path" : "pages/coupons/management",
"name" : "CouponsMag",
"style": {
"navigationBarTitleText":"优惠券管理",
"titleNView": {
"backgroundColor": "#FFFFFF",
"buttons": [
{
"text": "发布",
"fontSize": "14",
"width": "80",
"color": "#e93340"
}
]
}
}
},{
"path" : "pages/coupons/add",
"name" : "couponsAdd",
"style":{
"navigationBarTitleText": "发布优惠券",
"navigationBarBackgroundColor":"#FFFFFF"
}
},{
"path" : "pages/coupons/selectGoods",
"name" : "selectGoods",
"style":{
"navigationBarTitleText": "选择产品",
"navigationBarBackgroundColor":"#FFFFFF"
}
},{
"path" : "pages/coupons/magDetails",
"name" : "magDetails",
"style": {
"navigationBarTitleText": "优惠券详情"
}
},{
"path" : "pages/verification/index",
"name" : "Verification",
"style": {
"navigationBarTitleText": "扫码验证"
}
},{
"path" : "pages/verification/details",
"name" : "VerificationDetails",
"style": {
"navigationBarTitleText": "核销券详情"
}
},{
"path" : "pages/shop/lists",
"name" : "shopLists",
"style": {
"navigationBarTitleText":"部门/门店",
"navigationBarBackgroundColor": "#FFFFFF",
"titleNView": {
"buttons": [
{
"text": "创建",
"fontSize": "14",
"width": "80",
"color": "#e93340"
}
]
}
}
},{
"path" : "pages/shop/create",
"name" : "shopCreate",
"style":{
"navigationBarTitleText": "创建门店/部门",
"navigationBarBackgroundColor": "#FFFFFF"
}
}
],
"tabBar": {
"color": "#bababa",
"selectedColor": "#e93340",
"backgroundColor": "#FFFFFF",
"borderStyle": "white",
"list": [{
"pagePath": "pages/equity/index",
"text": "通证权易",
"iconPath": "static/tabBar/tabBar_icon_00.png",
"selectedIconPath": "static/tabBar/tabBar_show_00.png"
}, {
"pagePath": "pages/market/index",
"text": "转让市场",
"iconPath": "static/tabBar/tabBar_icon_01.png",
"selectedIconPath": "static/tabBar/tabBar_show_01.png"
}, {
"pagePath": "pages/store/index",
"text": "企业工具",
"iconPath": "static/tabBar/tabBar_icon_02.png",
"selectedIconPath": "static/tabBar/tabBar_show_02.png"
}, {
"pagePath": "pages/property/index",
"text": "我的资产",
"iconPath": "static/tabBar/tabBar_icon_03.png",
"selectedIconPath": "static/tabBar/tabBar_show_03.png"
}]
},
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "易货",
"navigationBarBackgroundColor": "#f5f5f5",
"backgroundColor": "#f5f5f5"
},
"easycom": {
"nv": "@/uni_modules/pyh-nv/components/pyh-nv/pyh-nv.vue"
},
"condition" : { //模式配置,仅开发期间生效
"current": 0, //当前激活的模式(list 的索引项)
"list": [
{
"name": "", //模式名称
"path": "", //启动页面,必选
"query": "" //启动参数在页面的onLoad函数里面得到
}
]
}
}

View File

@@ -0,0 +1,324 @@
<template>
<view class="content">
<view class="header">
<view class="title">企业认证</view>
<view class="subtitle">请如实填写认证信息快速审核开店</view>
</view>
<view class="white-box">
<view class="inputs logo">
<label>企业LOGO</label>
<image class="logo-cover" :src="logo || require('@/static/icons/add-icon.png')" @click="updImg('logo')" mode="aspectFill"></image>
</view>
<view class="inputs">
<label>企业名称</label>
<input type="text" v-model="name" placeholder="输入企业名称" />
</view>
<view class="inputs">
<label>企业行业</label>
<picker v-if="industry.length > 0" :range="industry" :value="industryIndex" range-key="title" @change="changePicker" data-type="industry">
<view class="picker-text nowrap">
{{industry[industryIndex].title}}
<uni-icons class="picker-icon" type="arrowdown"></uni-icons>
</view>
</picker>
</view>
<view class="inputs">
<label>经营类目</label>
<view class="picker-text" @click="opnePopup">
选择经营类目
<uni-icons class="picker-icon" type="arrowdown"></uni-icons>
</view>
</view>
<view class="inputs">
<label>法人姓名</label>
<input type="text" v-model="corporate" placeholder="输入法人姓名" />
</view>
<view class="inputs">
<label>法人身份证</label>
<input type="text" v-model="identity" placeholder="输入法人身份证" />
</view>
<view class="inputs">
<label>机构代码</label>
<input type="text" v-model="org" placeholder="输入企业组织机构代码" />
</view>
<view class="inputs logo">
<label>营业执照</label>
<image class="license-cover" :src="license || require('@/static/icons/add-icon.png')" @click="updImg('license')" mode="aspectFill"></image>
</view>
<view class="btns">
<button type="default" size="default" @click="submitApplies">提交认证</button>
</view>
</view>
<!-- 经营类目 -->
<uni-popup ref="categoryPopup">
<view class="category-popup">
<view class="header">
<view class="title">经营类目</view>
<view class="subtitle">请选择经营类目</view>
</view>
<view class="category-flex">
<view class="category-flex-item" :class="{'show' : item.check}" v-for="(item, index) in category" :key="index" @click="item.check = !item.check">{{item.name}}</view>
</view>
<view class="btns">
<button type="default" size="default" @click="affirmCategory">确定</button>
</view>
</view>
</uni-popup>
</view>
</template>
<script>
import { appliesCreate, applies, appliesInfo } from '@/apis/interfaces/company'
import { uploads } from '@/apis/interfaces/uploading'
export default {
data() {
return {
formType : "",
name : "",
corporate : "",
identity : "",
org : "",
logo : "",
license : "",
industry : [],
industryIndex: 0,
reason : '',
category : [],
categorys : []
};
},
created(){
// 读取配置信息
appliesCreate().then(res=>{
console.log(res)
this.industry = res.industries
this.formType = this.$Route.query.formType
this.name = res.info.name
this.industryIndex = res.industries.findIndex(val => val.industry_id === res.info.industry.industry_id) || 0
this.category = res.info.categories
if(this.formType === 'put'){
appliesInfo().then(formValue => {
this.corporate = formValue.certification.name
this.identity = formValue.certification.idcard
this.org = formValue.certification.code
this.logo = formValue.cover
this.license = formValue.certification.license
}).catch(valueErr => {
uni.showToast({
title: valueErr.message,
icon : 'none'
})
})
}
}).catch(err =>{
uni.showToast({
title: err.message,
icon : 'none'
})
})
},
methods:{
// 选择经营类目
opnePopup(){
this.$refs.categoryPopup.open('bottom')
},
// 确认选择类目
affirmCategory(){
this.categorys = []
for(let val of this.category){
if(val.check){
this.categorys.push(val.category_id)
}
}
this.$refs.categoryPopup.close()
},
// 提交信息
submitApplies(){
let method = this.formType === 'put' ? 'PUT' : 'POST'
applies({
name : this.name,
cover : this.logo,
license : this.license,
user_name : this.corporate,
id_card : this.identity,
code : this.org,
industry_id : this.industry[this.industryIndex].industry_id,
categories : this.categorys
}, method).then(res => {
uni.showModal({
title : '提示',
content : '您的企业认证信息已提交审核需3-7个工作日请耐心等待',
showCancel : false,
confirmText : '确认',
success : resModal => {
this.$Router.back()
}
})
}).catch(err => {
uni.showToast({
title: err.message,
icon : 'none'
})
})
},
// 选择器
changePicker(e){
this.industryIndex = e.detail.value
},
// 上传图片
updImg(type){
uni.chooseImage({
count : 1,
success : path => {
uploads([{
uri : path.tempFilePaths[0]
}]).then(res => {
this[type] = res.url[0]
}).catch(err => {
uni.showToast({
title: err.message,
icon : 'none'
})
})
}
})
}
}
}
</script>
<style lang="scss" scoped>
// 内容
.content{
.header{
height: 15vh;
padding-bottom: $padding * 2;
box-sizing: border-box;
@extend .vertical;
.title{
text-align: center;
font-size: $title-size + 14;
font-weight: bold;
line-height: 90rpx;
}
.subtitle{
font-size: $title-size-m;
color: $text-gray;
text-align: center;
}
}
.white-box{
background-color: white;
border-radius: $radius $radius 0 0;
min-height: 85vh;
padding: $padding $padding * 2 $padding * 2;
box-sizing: border-box;
.inputs{
position: relative;
margin-top: $margin;
background: white;
border-bottom: solid 1rpx $border-color;
padding-left: 200rpx;
line-height: 90rpx;
min-height: 90rpx;
label{
position: absolute;
top: 0;
left: 0;
width: 200rpx;
font-size: $title-size;
}
input{
height: 90rpx;
line-height: 90rpx;
font-size: $title-size;
}
.picker-text{
position: relative;
padding-right: 90rpx;
.picker-icon{
position: absolute;
right: 0;
top: 0;
}
}
}
.logo{
min-height: 98rpx;
padding-bottom: $padding;
.logo-cover{
position: absolute;
right: 0;
top: 0;
width: 98rpx;
height: 98rpx;
background: $border-color-lg;
border-radius: 50%;
}
.license-cover{
@extend .logo-cover;
border-radius: 0;
width: 131rpx;
}
}
.btns{
padding-top: $padding * 2;
button{
background: $text-price;
border-radius: 0;
height: 90rpx;
line-height: 90rpx;
font-size: $title-size;
color: white;
font-weight: bold;
&::after{
border: none;
}
}
}
}
// 经营类目
.category-popup{
background: #F5F5F5;
padding: 0 $padding * 2 $padding * 2 $padding * 2;
.header{
padding-bottom: $padding;
}
.category-flex{
margin: 0 -10rpx;
display: flex;
flex-wrap: wrap;
.category-flex-item{
margin: 10rpx;
background: white;
width: calc(33.33% - 20rpx);
line-height: 90rpx;
text-align: center;
font-size: $title-size-m;
@extend .nowrap;
&.show{
color: white;
background-color: $text-price;
}
}
}
.btns{
padding-top: $padding * 2;
button{
background: $text-price;
border-radius: 0;
height: 90rpx;
line-height: 90rpx;
font-size: $title-size;
color: white;
font-weight: bold;
&::after{
border: none;
}
}
}
}
}
</style>

View File

@@ -0,0 +1,62 @@
<template>
<view class="content">
<image class="cover" src="@/static/dev/guide_cover_00.png" mode="widthFix"></image>
<view class="title">恭喜您已注册成功</view>
<view class="sub-title">开通会员认证企业信息立即获得授信易货额即可开始易货之旅</view>
<button class="vip-button" type="default" @click="$Router.push({name: 'Vip'})">开通会员</button>
</view>
</template>
<script>
export default {
data() {
return {
};
},
onNavigationBarButtonTap(e){
this.$Router.pushTab({name: "Equity"})
},
}
</script>
<style lang="scss" scoped>
.content{
padding: $padding * 2;
background: white;
height: 100vh;
width: 100vw;
box-sizing: border-box;
@extend .vertical;
text-align: center;
.cover{
width: 46vw;
margin-bottom: 10vh;
}
.title{
font-size: $title-size + 14;
font-weight: bold;
line-height: 90rpx;
}
.sub-title{
font-size: $title-size-m;
color: $text-gray;
text-align: center;
}
.vip-button{
margin-top: 10vh;
background: $text-price;
border-radius: 0;
height: 90rpx;
line-height: 90rpx;
font-size: $title-size;
color: white;
font-weight: bold;
&::after{
border: none;
}
}
}
</style>

View File

@@ -0,0 +1,147 @@
<template>
<view class="content">
<view class="header">
<view class="title">企业注册</view>
<view class="subtitle">填写企业基础行业获取企业/商家权益</view>
</view>
<view class="white-box">
<view class="inputs">
<label>企业名称</label>
<input type="text" v-model="name" placeholder="输入企业名称" />
</view>
<view class="inputs">
<label>企业行业</label>
<picker v-if="industry.length > 0" :range="industry" :value="industryIndex" range-key="title" @change="changePicker" data-type="industry">
<view class="picker-text nowrap">
{{industry[industryIndex].title}}
<uni-icons class="picker-icon" type="arrowdown"></uni-icons>
</view>
</picker>
</view>
<view class="btns">
<button type="default" size="default" @click="next">下一步</button>
</view>
</view>
</view>
</template>
<script>
import { createConfig, inits } from '@/apis/interfaces/company'
export default {
data() {
return {
name : "",
industry : [],
industryIndex: 0
};
},
created(){
createConfig().then(res=>{
this.industry = res
})
},
methods:{
// 提交信息
next(){
inits({
name : this.name,
industry_id : this.industry[this.industryIndex].industry_id
}).then(res => {
this.$Router.push({name: 'Prompt'})
}).catch(err => {
uni.showToast({
title: err.message,
icon : 'none'
})
})
},
//选择器
changePicker(e){
let changeType = e.target.dataset.type,
changeVlae = e.detail.value
switch(changeType){
case 'type':
this.typeIndex = changeVlae
break
case 'industry':
this.industryIndex = changeVlae
break
}
}
}
}
</script>
<style lang="scss" scoped>
.content{
.header{
height: 20vh;
@extend .vertical;
.title{
text-align: center;
font-size: $title-size + 14;
font-weight: bold;
line-height: 90rpx;
}
.subtitle{
font-size: $title-size-m;
color: $text-gray;
text-align: center;
}
}
.white-box{
background-color: white;
border-radius: $radius $radius 0 0;
min-height: 80vh;
padding: $padding * 2;
box-sizing: border-box;
.inputs{
position: relative;
margin-top: $margin;
background: white;
border-bottom: solid 1rpx $border-color;
padding-left: 200rpx;
line-height: 90rpx;
min-height: 90rpx;
label{
position: absolute;
top: 0;
left: 0;
width: 200rpx;
font-size: $title-size;
}
input{
height: 90rpx;
line-height: 90rpx;
font-size: $title-size;
}
.picker-text{
position: relative;
padding-right: 90rpx;
.picker-icon{
position: absolute;
right: 0;
top: 0;
}
}
}
.btns{
padding-top: $padding * 2;
button{
background: $text-price;
border-radius: 0;
height: 90rpx;
line-height: 90rpx;
font-size: $title-size;
color: white;
font-weight: bold;
&::after{
border: none;
}
}
}
}
}
</style>

View File

@@ -0,0 +1,356 @@
<template>
<view>
<view class="coupons-preview">
<view class="item cover" @click="updCover">
<image class="cover-img" v-if="cover != ''" :src="cover" mode="aspectFill" />
<image class="cover-img" v-else src="@/static/icons/add-icon.png" mode="aspectFill" />
</view>
<view class="item mian">
<view class="title nowrap">{{couponsTitle || '优惠券标题'}}</view>
<view class="time nowrap">{{datePickerValue.length == 0 ? '优惠券有效期': datePickerValue[0] + ' 至 ' + datePickerValue[1]}}</view>
<view class="goods nowrap">全店通用</view>
</view>
</view>
<view class="add-info">
<view class="inputs">
<label class="input-label">券类型</label>
<picker :range="types" range-key="text" :value="typeIndex" data-type="typeIndex" @change="changePicker">
<view class="input-text">{{types[typeIndex].text}}<uni-icons class="picker-icon" type="arrowdown" size="14" /></view>
</picker>
</view>
<view class="inputs">
<label class="input-label">券名称</label>
<input type="text" v-model="couponsTitle" placeholder="输入优惠券名称" />
</view>
<view class="inputs">
<label class="input-label">发券数量</label>
<input type="number" v-model="quantity" placeholder="输入优惠券发放量" />
</view>
<view class="inputs">
<label class="input-label">每人限领</label>
<input type="number" v-model="personQuantity" placeholder="输入每人限领数, 0为不限制" />
</view>
<block v-if="types[typeIndex].id == 2">
<view class="inputs">
<label class="input-label"></label>
<input type="digit" v-model="full" placeholder="输入券最低消费金额" />
</view>
<view class="inputs">
<label class="input-label"></label>
<input type="digit" v-model="price" placeholder="输入券优惠金额" />
</view>
</block>
<view class="inputs">
<label class="input-label">使用渠道</label>
<view class="input-checkbox">
<radio-group @change="radioChange">
<label class="input-checkbox-item">
<radio color="#c82626" value="1" checked/>
<text>线上</text>
</label>
<label class="input-checkbox-item">
<radio color="#c82626" value="2"/>
<text>线下</text>
</label>
</radio-group>
</view>
</view>
<view class="inputs">
<label class="input-label">券有效期</label>
<view class="input-text" @click="showDatePicker = true">{{datePickerValue.length == 0 ? '选择优惠券有效期': datePickerValue[0] + ' ' + datePickerValue[1]}}</view>
<tn-date-picker :show="showDatePicker" :monthNum="12" color="#c82626" :showTips="true" beginText="开始日期" endText="结束日期" @confirm="confirmDatePicker" @cancel="showDatePicker = false"/>
</view>
<view class="inputs">
<label class="input-label">设为推荐</label>
<picker :range="recommend" range-key="text" :value="recommendIndex" data-type="recommendIndex" @change="changePicker">
<view class="input-text">{{recommend[recommendIndex].text}}<uni-icons class="picker-icon" type="arrowdown" size="14" /></view>
</picker>
</view>
<view class="inputs">
<label class="input-label">产品限制</label>
<picker :range="product" range-key="text" :value="productIndex" data-type="productIndex" @change="changePicker">
<view class="input-text">{{product[productIndex].text}}<uni-icons class="picker-icon" type="arrowdown" size="14" /></view>
</picker>
</view>
</view>
<view class="add-textarea">
<label>使用规则</label>
<textarea :maxlength="-1" v-model="description" placeholder="输入使用规则说明" />
</view>
<view class="add-btn ios-bottom">
<button type="default" @click="updComponent">{{product[productIndex].type === 'all' ? '发布优惠券': '选择关联商品'}}</button>
</view>
</view>
</template>
<script>
import TnDatePicker from "@/components/tn-datepicker/tn-datepicker";
import { uploads } from '@/apis/interfaces/uploading'
import { pushCoupons } from '@/apis/interfaces/coupons'
export default {
components:{
TnDatePicker
},
data() {
return {
typeIndex : 0,
types : [
{ id: 1, text: '服务券' },
{ id: 2, text: '代金券' },
{ id: 3, text: '提货券' },
],
recommendIndex : 0,
recommend : [
{ type: '0', text: '不推荐' },
{ type: '1', text: '推荐' }
],
productIndex : 0,
product : [
{ type: 'all', text: '全部商品通用' },
{ type: 'part', text: '部分商品可用' }
],
showDatePicker : false,
datePickerValue : [],
cover : '',
useWay : 1,
couponsTitle : '',
quantity : '',
personQuantity : '',
full : '',
price : '',
description : ''
};
},
methods:{
// 使用渠道
radioChange(e){
this.useWay = e.detail.value
},
// 选择
changePicker(e){
this[e.target.dataset.type] = e.detail.value
},
// 日期
confirmDatePicker(e){
this.datePickerValue = e.value
this.showDatePicker = false
},
// 上传优惠券封面
updCover(){
uni.chooseImage({
crop: {width: 300, height: 300},
success: path=> {
uploads([{
uri : path.tempFilePaths[0]
}]).then(res => {
this.cover = res.url[0]
}).catch(err => {
uni.showToast({
title: err.message,
icon : 'none'
})
})
}
})
},
// 发布优惠券
updComponent(){
let recommendVlue = this.recommend[this.recommendIndex].type === 1 ? 2 : ''
let valuss = {
title : this.couponsTitle,
cover : this.cover,
type : this.types[this.typeIndex].id,
full : this.full || 0,
price : this.price || 0,
quantity : this.quantity,
use_way : this.useWay,
person_quantity : this.personQuantity,
start_at : this.datePickerValue[0],
end_at : this.datePickerValue[1],
channel : this.product[this.productIndex].type,
description : this.description,
position : [1, recommendVlue]
}
pushCoupons(valuss).then(res => {
uni.showModal({
title : '提示',
content : res.message,
confirmText : res.linkGoods ? '关联商品' : '确定',
confirmColor: '#c82626',
cancelText : '稍后',
cancelColor : '#555555',
showCancel : res.linkGoods,
success : modalRes => {
if(modalRes.confirm && res.linkGoods){
this.$Router.replace({name: 'selectGoods', params:{couponId: res.coupon_id}})
return
}
this.$Router.back()
}
})
}).catch(err => {
uni.showToast({
title: err.message,
icon : 'none'
})
})
}
}
}
</script>
<style lang="scss">
.add-btn{
padding: $padding;
button{
height: 90rpx;
line-height: 90rpx;
background: $text-price;
color: white;
font-size: $title-size;
font-weight: bold;
border-radius: 0;
&::after{
border: none;
}
}
}
// 优惠券详情
.add-textarea{
background: white;
margin-top: $margin;
padding: $padding $padding $padding ($padding + 180rpx);
label{
position: absolute;
left: $padding;
line-height: 40rpx;
height: 40rpx;
}
textarea{
width: 100%;
padding: 0;
margin: 0;
line-height: 40rpx;
height: 200rpx;
}
}
// 优惠券配置
.add-info{
background-color: white;
padding: 0 $padding;
.inputs{
position: relative;
padding-left: 180rpx;
&::after{
position: absolute;
height: 1rpx;
content: " ";
background: $border-color;
left: 0;
right: -$padding;
bottom: 0;
}
&:last-child::after{
display: none;
}
.input-label{
position: absolute;
left: 0;
line-height: 90rpx;
height: 90rpx;
}
.input-text,
input{
height: 90rpx;
line-height: 90rpx;
font-size: $title-size-lg;
}
.input-text{
position: relative;
padding-right: 90rpx;
.picker-icon{
position: absolute;
right: 0;
}
}
.input-checkbox{
min-height: 90rpx;
display: flex;
.input-checkbox-item{
line-height: 90rpx;
margin-right: $margin;
radio{
transform:scale(0.8);
margin-right: -15rpx;
}
text{
padding: 0 ($padding/2);
line-height: 50rpx;
display: inline-block;
font-size: $title-size-lg;
}
}
}
}
}
// 发布预览
.coupons-preview{
background: white;
margin: $margin;
border-radius: $radius/2;
display: flex;
justify-content: space-between;
flex-wrap: wrap;
padding: $padding 0;
.item{
position: relative;
padding: 0 $padding;
}
.cover{
position: relative;
border-right: dashed 3rpx $border-color;
width: 148rpx;
text-align: center;
.cover-img{
width: 148rpx;
height: 148rpx;
border-radius: $radius/2;
vertical-align: top;
background: $border-color-lg;
border:solid 1rpx $border-color;
box-sizing: border-box;
}
&::after,&::before{
position: absolute;
width: 30rpx;
height: 30rpx;
background: #f8f8f8;
content: " ";
right: -16rpx;
border-radius: 50%;
}
&::after{
top: -($padding + 15);
}
&::before{
bottom: -($padding + 15);
}
}
.mian{
justify-content: center;
width: calc(100% - 148rpx - #{$padding*2});
box-sizing: border-box;
@extend .vertical;
.title{
font-size: $title-size-lg;
line-height: 50rpx;
}
.time, .goods{
color: $text-gray-m;
font-size: $title-size-m;
line-height: 40rpx;
}
}
}
</style>

View File

@@ -0,0 +1,19 @@
<template>
<view>
优惠券
</view>
</template>
<script>
export default {
data() {
return {
};
}
}
</script>
<style lang="scss">
</style>

View File

@@ -0,0 +1,244 @@
<template>
<view class="content" v-if="!isLoding">
<view class="details">
<view class="header">
<view class="info">
<image class="info-cover" :src="details.cover" mode="aspectFill"></image>
<view class="info-title">{{details.title}}</view>
</view>
<view class="info-item">
<view class="info-item-title">优惠券类型</view>
<view class="info-item-text">{{details.type.text}}</view>
</view>
<view class="info-item">
<view class="info-item-title">上架状态</view>
<view class="info-item-text">{{details.status.text}}</view>
</view>
<view class="info-item">
<view class="info-item-title">使用渠道</view>
<view class="info-item-text">{{details.use_way}}</view>
</view>
<view class="info-item" v-if="details.type.value === 2">
<view class="info-item-title">满减</view>
<view class="info-item-text">{{details.price}}{{details.full}}</view>
</view>
<view class="info-item">
<view class="info-item-title">券发放量</view>
<view class="info-item-text">{{details.quantity}}</view>
</view>
<view class="info-item">
<view class="info-item-title">已发放量</view>
<view class="info-item-text">{{details.grant_quantity}}</view>
</view>
<view class="info-item">
<view class="info-item-title">每人限领</view>
<view class="info-item-text">{{details.person_quantity == 0 ? '不限制': details.person_quantity}}</view>
</view>
<view class="info-item">
<view class="info-item-title">券有效期()</view>
<view class="info-item-text">{{details.start_at}}</view>
</view>
<view class="info-item">
<view class="info-item-title">券有效期()</view>
<view class="info-item-text">{{details.end_at}}</view>
</view>
<view class="info-item">
<view class="info-item-title">创建时间</view>
<view class="info-item-text">{{details.created_at}}</view>
</view>
</view>
<view class="mian">
<block v-if="details.goods.length > 0">
<view class="mian-title">适用权证</view>
<view class="mian-goods">
<view class="item" v-for="(item, index) in details.goods" :key="index">
<view class="item-title nowrap">{{item.name}}</view>
<view class="item-price nowrap">{{item.price}}</view>
</view>
</view>
</block>
<view class="mian-title">使用说明</view>
<view class="mian-text">
<text>{{details.description || '-'}}</text>
</view>
</view>
</view>
<view class="ios-bottom"></view>
<!-- footer -->
<view class="footer">
<view class="footer-flex">
<view class="item" @click="putStatus">{{details.status.value == 4 ? '上架': '下架'}}</view>
<view class="item" @click="$Router.push({name: 'selectGoods', params: {couponId: details.coupon_id}})">关联商品</view>
</view>
<view class="ios-bottom"></view>
</view>
</view>
</template>
<script>
import { magCouponsInfo, magCouponsStatus } from '@/apis/interfaces/coupons'
export default {
data() {
return {
isLoding: true,
details : {}
};
},
onShow() {
magCouponsInfo(this.$Route.query.couponId).then(res => {
this.isLoding = false
this.details = res
})
},
methods:{
// 上下架
putStatus(){
magCouponsStatus(this.details.coupon_id).then(res => {
uni.showToast({
title: res,
icon : 'none'
})
this.$set(this.details, 'status', this.details.status.value == 4 ? {'value': 2,'text': "上架"} : {'value': 4,'text': "下架"})
}).catch(err => {
uni.showToast({
title: err.message,
icon : 'none'
})
})
}
}
}
</script>
<style lang="scss">
// content
.content{
padding-bottom: $padding + 90;
}
// footer
.footer{
box-shadow: 0 0 4rpx 4rpx rgba($color: #000000, $alpha: .02);
background: white;
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: ($padding/2) $padding;
.footer-flex{
display: flex;
justify-content: space-between;
.item{
line-height: 70rpx;
width: 50%;
text-align: center;
color: $text-price;
font-size: $title-size-lg;
font-weight: bold;
border-right: solid 1rpx $border-color;
&:last-child{
border: none;
}
}
}
}
.details{
margin: $margin;
background: white;
border-radius: $radius;
// 优惠券信息
.header{
position: relative;
border-bottom: dashed 2rpx $border-color;
padding: $padding;
&::after,&::before{
position: absolute;
width: 30rpx;
height: 30rpx;
background: #f8f8f8;
content: " ";
bottom: -16rpx;
border-radius: 50%;
}
&::after{
left: -16rpx;
}
&::before{
right: -16rpx;
}
.info{
padding: $padding 0;
text-align: center;
.info-cover{
width: 128rpx;
height: 128rpx;
border-radius: 50%;
vertical-align: top;
}
.info-title{
padding-top: $padding;
text-align: center;
font-weight: bold;
font-size: $title-size;
}
}
.info-item{
position: relative;
padding-left: 200rpx;
min-height: 60rpx;
font-size: $title-size-lg;
padding-bottom: $padding/3;
&:last-child{
padding-bottom: 0;
}
.info-item-title{
position: absolute;
left: 0;
top: 0;
line-height: 50rpx;
color: $text-color;
}
.info-item-text{
line-height: 50rpx;
color: $text-gray;
text-align: right;
@extend .nowrap;
}
}
}
// 优惠券介绍
.mian{
padding: $padding;
.mian-title{
font-size: $title-size-lg;
font-weight: bold;
line-height: 50rpx;
color: $text-color;
}
.mian-text{
font-size: $title-size-m;
color: $text-gray;
padding-top: $padding/3;
}
.mian-goods{
padding-bottom: $padding;
.item{
display: flex;
justify-content: space-between;
line-height: 60rpx;
font-size: $title-size-m;
padding-top: $padding/3;
.item-title{
width: 75%;
color: $text-gray;
}
.item-price{
width: 25%;
color: $text-price;
font-weight: bold;
text-align: right;
}
}
}
}
}
</style>

View File

@@ -0,0 +1,204 @@
<template>
<view>
<view class="tabs">
<view class="item" :class="{'show': listType == ''}" @click="onTabs('')">全部</view>
<view class="item" :class="{'show': listType == '1'}" @click="onTabs('1')">服务券</view>
<view class="item" :class="{'show': listType == '2'}" @click="onTabs('2')">代金券</view>
<view class="item" :class="{'show': listType == '3'}" @click="onTabs('3')">提货券</view>
</view>
<block v-if="coupons.length > 0">
<view class="coupons">
<view class="coupons-flex" v-for="(item, index) in coupons" :key="index" @click="$Router.push({name: 'magDetails', params: {couponId: item.coupon_id}})">
<view class="item cover">
<image class="cover-img" :src="item.cover" mode="aspectFill" />
</view>
<view class="item mian">
<view class="title nowrap">{{item.title}}</view>
<view class="time nowrap">{{item.start_at}}{{item.end_at}}</view>
<view class="tags nowrap">
<text>{{item.status.text}}</text>
<text>{{item.type.text}}</text>
<text>{{item.channel.text}}</text>
</view>
</view>
<view class="arrowright">
<uni-icons type="arrowright" size="16" color="#999"></uni-icons>
</view>
</view>
</view>
</block>
<block v-else>
<view class="list-null">
<image class="icon" src="@/static/icons/approve-icon.png" mode="widthFix"></image>
<view class="sub-title">暂无相关优惠券</view>
</view>
</block>
</view>
</template>
<script>
import { toolsCoupons } from '@/apis/interfaces/coupons'
export default {
data() {
return {
listType: '',
coupons : [],
pages : {}
};
},
onShow() {
this.getCoupons()
},
methods:{
// 选择类型
onTabs(value){
if(value == this.listType) return
this.listType = value
this.getCoupons()
},
// 数据列表
getCoupons(){
toolsCoupons({
type: this.listType
}).then(res => {
this.coupons = res.lists.data
this.pages = res.lists.page
}).catch(err => {
uni.showToast({
title: err.message,
icon : 'none'
})
})
}
},
onNavigationBarButtonTap() {
this.$Router.push({name: 'couponsAdd'})
}
}
</script>
<style lang="scss" scoped>
// tabs
.tabs{
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 99;
display: flex;
justify-content: space-around;
background: white;
padding: 15rpx 0;
font-size: $title-size-lg;
color: $text-gray;
.item{
height: 60rpx;
line-height: 60rpx;
&.show{
color: $text-price;
border-bottom: solid 4rpx $text-price;
}
}
}
// 空提示
.list-null{
width: 100vw;
height: 100vh;
padding-bottom: 20vh;
box-sizing: border-box;
background: white;
text-align: center;
@extend .vertical;
.sub-title{
color: $text-gray;
font-size: $title-size-m;
}
.icon{
width: 288rpx;
}
}
// 订单管理
.coupons{
padding-top: 90rpx;
@extend .ios-bottom;
.coupons-flex{
position: relative;
background: white;
margin: $margin;
border-radius: $radius/2;
display: flex;
justify-content: space-between;
flex-wrap: wrap;
padding: $padding 70rpx $padding 0;
.item{
position: relative;
padding: 0 $padding;
}
.arrowright{
position: absolute;
right: $margin;
top: 0;
bottom: 0;
@extend .vertical
}
.cover{
position: relative;
border-right: dashed 3rpx $border-color;
width: 148rpx;
text-align: center;
.cover-img{
width: 148rpx;
height: 148rpx;
border-radius: $radius/2;
vertical-align: top;
background: $border-color-lg;
border:solid 1rpx $border-color;
box-sizing: border-box;
}
&::after,&::before{
position: absolute;
width: 30rpx;
height: 30rpx;
background: #f8f8f8;
content: " ";
right: -16rpx;
border-radius: 50%;
}
&::after{
top: -($padding + 15);
}
&::before{
bottom: -($padding + 15);
}
}
.mian{
justify-content: center;
width: calc(100% - 148rpx - #{$padding*2});
box-sizing: border-box;
@extend .vertical;
.title{
font-size: $title-size-lg;
line-height: 50rpx;
font-weight: bold;
}
.time, .tags{
color: $text-gray-m;
font-size: $title-size-m;
line-height: 40rpx;
}
.tags{
margin-top: 10rpx;
text{
background: $border-color-lg;
color: $text-gray;
padding: 0 ($padding/2);
margin-right: ($margin/2);
&:last-child{
margin-right: 0;
}
}
}
}
}
}
</style>

View File

@@ -0,0 +1,250 @@
<template>
<view>
<!-- 商品列表 -->
<view class="lists">
<view class="goods-item" v-for="(item, index) in goods" :key="index">
<checkbox class="checkbox" :checked="item.isSelect" @click="onSelect(index)"/>
<view class="mian">
<image class="cover" :src="item.cover" mode="aspectFill" />
<view class="title">{{item.title}}</view>
<view class="subtitle">{{item.description}}</view>
<view class="mian-flex">
<view class="price"><text></text>{{item.price}}</view>
<view class="inventory">权证剩余{{item.stock}}</view>
</view>
</view>
</view>
<view class="ios-bottom"></view>
</view>
<!-- footer -->
<view class="footer">
<view class="footer-flex">
<view class="flex-checkbox">
<checkbox class="checkbox" :checked="allSelect" @click="onAllSelect"/>
<label for="all">
<view class="text">全选</view>
<view class="sub-text">已选{{selectGoods.length}}</view>
</label>
</view>
<view class="flex-button" @click="setGoods">确定</view>
</view>
<view class="ios-bottom"></view>
</view>
</view>
</template>
<script>
import { couponsGoods, couponsAddgoods } from '@/apis/interfaces/coupons'
export default {
data() {
return {
goods : [],
selectGoods : [],
allSelect : false
};
},
created() {
couponsGoods(this.$Route.query.couponId).then(res => {
this.goods = res
this.selectNumber()
}).catch(err => {
uni.showToast({
title: err.message,
icon : 'none'
})
})
},
methods:{
// 全选产品
onAllSelect(){
for(let val of this.goods){
val.isSelect = !this.allSelect
}
this.selectNumber()
},
// 选择产品
onSelect(index){
const goodsItem = this.goods[index]
goodsItem.isSelect = !goodsItem.isSelect
this.$set(this.goods, index, goodsItem)
this.selectNumber()
},
// 计算产品数量
selectNumber(){
let selectArr = [];
for(let val of this.goods){
if(val.isSelect) selectArr.push(val.goods_sku_id)
}
this.selectGoods = selectArr
if(selectArr.length == this.goods.length && this.goods.length != 0) this.allSelect = true
else this.allSelect = false
},
// 添加设置产品
setGoods(){
if(this.selectGoods.length <= 0){
uni.showToast({
title: '请选择优惠券关联商品',
icon : 'none'
})
return
}
couponsAddgoods(this.$Route.query.couponId, {
goodsable_ids: this.selectGoods
}).then(res => {
uni.showModal({
title : '提示',
content : res,
showCancel : false,
success : modalRes => {
if(modalRes.confirm) this.$Router.back()
}
})
}).catch(err => {
uni.showToast({
title: err.message,
icon : 'none'
})
})
}
}
}
</script>
<style lang="scss">
// 列表
.lists{
padding-bottom: ($padding + 10) + 70;
.goods-item{
background: white;
padding: $padding $padding $padding ($padding + 70);
position: relative;
border-bottom: solid 1rpx $border-color;
.checkbox{
left: $padding;
}
.mian{
position: relative;
padding-left: $padding + 168;
min-height: 168rpx;
.cover{
position: absolute;
left: 0;
top: 0;
width: 168rpx;
height: 168rpx;
}
.title{
font-size: $title-size-lg;
line-height: 40rpx;
height: 48rpx;
@extend .nowrap;
}
.subtitle{
color: $text-gray;
font-size: $title-size-m;
height: 80rpx;
line-height: 40rpx;
margin-bottom: 8rpx;
@extend .nowrap;
}
.mian-flex{
display: flex;
justify-content: space-between;
.price{
width: 50%;
font-weight: bold;
color: $text-price;
font-size: $title-size;
height: 40rpx;
line-height: 40rpx;
@extend .nowrap;
}
.inventory{
width: 50%;
text-align: right;
font-size: $title-size-sm;
color: $text-gray;
height: 40rpx;
line-height: 40rpx;
@extend .nowrap;
}
}
}
}
}
// footer
.footer{
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: white;
padding: ($padding - 10) $padding;
z-index: 99;
box-shadow: 0 0 4rpx 4rpx rgba($color: #000000, $alpha: .02);
.footer-flex{
display: flex;
justify-content: space-between;
align-items: center;
.flex-checkbox{
position: relative;
width: 50%;
padding-right: $padding;
padding-left: 70rpx;
box-sizing: border-box;
.text{
font-size: $title-size-lg;
font-weight: bold;
line-height: 40rpx;
color: $text-color;
}
.sub-text{
font-size: $title-size-sm;
color: $text-gray;
line-height: 40rpx;
@extend .nowrap;
}
}
.flex-button{
background: $text-price;
color: white;
width: 50%;
text-align: center;
line-height: 80rpx;
border-radius: $radius/2;
font-size: $title-size;
font-weight: bold;
}
}
}
// checkbox
.checkbox{
position: absolute;
left: 0;
top: 50%;
margin-top: -28rpx;
.uni-checkbox-input{
border: 1px solid $border-color;
border-radius: 50%;
width: 46rpx;
height: 46rpx;
box-sizing:border-box;
}
.uni-checkbox-input.uni-checkbox-input-checked{
border: none;
background: $text-price;
}
.uni-checkbox-input.uni-checkbox-input-checked::before{
border-radius: 50%;
width: 40rpx;
height: 40rpx;
line-height: 40rpx;
text-align: center;
font-size: 24rpx;
color:#fff;
background: transparent;
transform:translate(-50%, -50%) scale(1);
-webkit-transform:translate(-50%, -50%) scale(1);
}
}
</style>

View File

@@ -0,0 +1,246 @@
<template>
<view class="content" v-if="!loging">
<!-- 基础信息 -->
<view class="info-card">
<view class="cover">
<view class="cover-add vertical" @click="updCover">
<block v-if="cover != ''">
<image :src="cover" mode="aspectFill"></image>
</block>
<block v-else>
<image class="cover-default" src="@/static/icons/add-icon.png" mode="widthFix"></image>
<view>员工寸照</view>
</block>
</view>
</view>
<view class="info-text">
<view class="info-inputs">
<input type="text" v-model="name" placeholder="姓名"/>
</view>
<view class="info-inputs">
<input type="number" v-model="phone" placeholder="手机号码"/>
</view>
<view class="info-inputs">
<input type="text" v-model="job" placeholder="职业"/>
</view>
<view class="info-inputs">
<picker :range="section" range-key="name" @change="pickerChange">
<view class="picker-text">
{{section[sectionIndex].name}}
<uni-icons class="icon" type="arrowdown" color="#555"></uni-icons>
</view>
</picker>
</view>
</view>
</view>
<!-- 权限设置 -->
<view class="title">权限设置</view>
<view class="jurisdiction">
<checkbox-group @change="permissionChange">
<view class="item" v-for="(item, index) in permissions" :key="index">
<label>
<view class="item-title">{{item.title}}</view>
<view class="item-info">{{item.description}}</view>
<checkbox class="item-checkbox" color="#c82626" :value="item.permission_id" />
</label>
</view>
</checkbox-group>
</view>
<!-- 按钮 -->
<view class="add-btns">
<button size="default" @click="onAddEmployees">确认添加</button>
</view>
</view>
</template>
<script>
import { employeesConfig, addEmployees } from '@/apis/interfaces/employees'
import { uploads } from '@/apis/interfaces/uploading'
export default {
data() {
return {
loging : true,
section : [],
permissions : [],
permissionIds: [],
sectionIndex : 0,
cover : '',
name : '',
phone : '',
job : ''
};
},
created() {
employeesConfig().then(res => {
this.section = res.store
this.permissions = res.permissions
this.loging = false
})
},
methods:{
// 选择部门
pickerChange(e){
this.sectionIndex = e.detail.value
},
// 权限选择
permissionChange(e){
this.permissionIds = e.detail.value
},
// 上传照片
updCover(){
uni.chooseImage({
crop: {width: 229, height: 320},
success: path=> {
uploads([{
uri : path.tempFilePaths[0]
}]).then(res => {
this.cover = res.url[0]
}).catch(err => {
uni.showToast({
title: err.message,
icon : 'none'
})
})
}
})
},
// 添加员工
onAddEmployees(){
addEmployees({
name : this.name,
mobileNo : this.phone,
job : this.job,
cover : this.cover,
position : 1,
order : 0,
store_id : this.section[this.sectionIndex].store_id,
permission : this.permissionIds
}).then(res => {
uni.showModal({
title : '提示',
content : res,
success : () => {
this.$Router.back()
}
})
}).catch(err => {
uni.showToast({
title: err.message,
icon : 'none'
})
})
}
}
}
</script>
<style lang="scss" scoped>
.content{
min-height: 100vh;
@extend .ios-bottom;
.title{
padding: ($padding/2) $padding;
color: $text-gray;
}
.jurisdiction{
background: white;
.item{
position: relative;
padding: $padding 150rpx $padding $padding;
&::after{
position: absolute;
left: $padding;
right: 0;
bottom: 0;
height: 1rpx;
content: " ";
background: $border-color;
}
&:last-child::after{
display: none;
}
.item-checkbox{
position: absolute;
right: $padding;
top: 50%;
height: 40rpx;
width: 40rpx;
margin-top: -27rpx;
}
.item-title{
font-size: $title-size;
padding-bottom: $margin/3;
}
.item-info{
font-size: $title-size-m;
color: $text-gray;
}
}
}
// 基础信息
.info-card{
background: white;
padding: $padding;
position: relative;
min-height: 238rpx;
.cover{
position: absolute;
top: $padding;
left: $padding;
background: #f8f8f8;
width: 229rpx;
height: 320rpx;
.cover-add{
position: absolute;
width: 100%;
height: 100%;
text-align: center;
image{
width: 229rpx;
height: 320rpx;
}
image.cover-default{
width: 128rpx;
}
color: $text-gray-m;
font-size: $title-size-m;
}
}
.info-text{
padding-left: $padding + 229;
.info-inputs{
height: 80rpx;
line-height: 80rpx;
border-bottom: solid 1rpx $border-color;
input{
height: 80rpx;
}
.picker-text{
position: relative;
padding-right: 80rpx;
.icon{
position: absolute;
right: 0;
top: 0;
}
}
}
}
}
// 添加按钮
.add-btns{
padding: $padding;
button[size='default']{
height: 90rpx;
line-height: 90rpx;
padding: 0;
margin: 0;
background: $text-price;
font-size: $title-size;
font-weight: bold;
color: white;
border-radius: 0;
}
}
}
</style>

View File

@@ -0,0 +1,114 @@
<template>
<view class="ios-bottom">
<uni-collapse>
<uni-collapse-item :show-animation="true" :open="true">
<template v-slot:title>
<view class="collapse-title">技术部</view>
</template>
<view class="employees-item" v-for="(item, index) in 10" :key="index">
<view class="cover">
<block v-if="index === 4"></block>
<block v-else>
<image class="cover-img" src="@/static/dev/good_cover_00.jpg" mode="aspectFill"></image>
</block>
</view>
<view class="content">
<view class="nickname nowrap">唐明明</view>
<view class="job nowrap">web前端开发</view>
</view>
</view>
</uni-collapse-item>
<uni-collapse-item :show-animation="true">
<template v-slot:title>
<view class="collapse-title">市场部</view>
</template>
<view class="employees-item" v-for="(item, index) in 5" :key="index">
<view class="cover">
<block v-if="index === 4"></block>
<block v-else>
<image class="cover-img" src="@/static/dev/good_cover_00.jpg" mode="aspectFill"></image>
</block>
</view>
<view class="content">
<view class="nickname">唐明明</view>
<view class="job">web前端开发</view>
</view>
</view>
</uni-collapse-item>
</uni-collapse>
</view>
</template>
<script>
import { employees } from '@/apis/interfaces/employees'
export default {
data() {
return {};
},
onShow(){
employees().then(res => {
console.log(res)
})
},
onNavigationBarButtonTap() {
this.$Router.push({name: 'addEmployees'})
}
};
</script>
<style lang="scss" scoped>
.collapse-title{
padding: 0 $padding;
line-height: 90rpx;
}
.employees-item {
background: white;
padding: ($padding - 10) $padding;
position: relative;
&::before {
position: absolute;
bottom: 0;
left: $padding + 98;
right: 0;
content: ' ';
height: 1rpx;
background: $border-color;
}
&:last-child::before {
display: none;
}
.cover {
position: absolute;
top: $padding - 10;
left: $padding;
background: $text-price;
color: white;
height: 78rpx;
width: 78rpx;
line-height: 78rpx;
text-align: center;
border-radius: 50%;
overflow: hidden;
.cover-img {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
}
}
.content {
height: 78rpx;
padding-left: 98rpx;
.nickname{
line-height: 48rpx;
font-size: $title-size;
}
.job{
line-height: 30rpx;
font-size: $title-size-sm;
color: $text-gray-m;
}
}
}
</style>

View File

@@ -0,0 +1,875 @@
<template>
<view>
<!-- 状态栏 -->
<nv :config="nvConfig" @nvTabTap="onNvTab" @nvBtnTap="onRightBtn"></nv>
<block v-if="tabIndex === 1">
<!-- 推荐商家 -->
<view class="block-title">
<view class="title">
推荐商家
</view>
</view>
<scroll-view scroll-x class="recommended">
<block v-for="(item, index) in recommendBus" :key="index">
<view class="item-box">
<image
class="item-cover"
:src="item.cover"
mode="aspectFill"
/>
<view class="item-vip">
<view class="item-vip-text">{{item.level.name}}</view>
<view class="item-vip-tips">企业</view>
</view>
<view class="item-title nowrap">{{item.name || '-'}}</view>
<view class="item-trade nowrap">行业{{item.industry.title}}</view>
<view class="item-bar">
<view class="item-bar-color">
<view class="item-bar-strip" :style="{width: item.process + '%'}">
<text class="item-bar-per">{{item.process}}%</text>
</view>
</view>
<image class="item-bar-strip-img" src="../../static/icons/equity_arrow_up.png" mode="aspectFill"></image>
</view>
<view class="item-url">
<view class="item-credit"><image class="item-credit-img" src="../../static/icons/equity_arrow_icon.png" mode="aspectFill"></image>信用值 {{item.integrity}}</view>
<view class="item-btn" @click="onOpenWechat(item)">进店<image src="../../static/icons/equity_arrow_right.png" mode="aspectFill"></image></view>
</view>
</view>
</block>
</scroll-view>
<!-- 热易商家 -->
<view class="block-title">
<view class="title">
热易商家
</view>
</view>
<swiper class="hot-swiper">
<swiper-item v-for="(item, index) in hotBus" :key="index">
<view class="hot-box" @click="onOpenWechat(item)">
<image
class="cover"
:src="item.cover"
mode="aspectFill"
/>
<view class="hot-vip">{{item.level.name}}</view>
<view class="hot-content">
<view class="hot-title nowrap">{{item.name || '-'}}</view>
<view class="hot-credit">信誉值 {{item.integrity}}</view>
<view class="hot-trade nowrap">所属行业{{item.industry.title}}</view>
<view class="hot-warrant">
<view class="hot-warrant-text nowrap">
权证数量{{item.goodsCount}}
</view>
<view class="hot-bar">
<view class="hot-bar-color">
<view class="hot-bar-strip" :style="{width: item.process + '%'}"></view>
</view>
<view class="hot-bar-per">{{item.process}}</view>
</view>
</view>
<!-- <view class="credibility">
<uni-rate
:readonly="true"
color="#ddd"
active-color="#e93340"
:value="item.star"
:size="14"
/>
</view>
<view class="trading nowrap">累计交易次</view> -->
</view>
<view class="hot-tool">
<view class="hot-deal">
交易量<text class="hot-deal-number">{{item.saleCount || 0}}</text>
</view>
</view>
</view>
</swiper-item>
</swiper>
<!-- 行业分类 -->
<scroll-view class="industry-tabs" scroll-x>
<view class="industry-item" :class="{'show':index === industryIndex}" v-for="(item, index) in industryBus" :key="index" @click="onBusIndustry(index)">{{item.title}}</view>
</scroll-view>
<!-- 商家 -->
<industry-list :list="busList" @on-industry="onOpenWechat"/>
</block>
<!-- 易货商城 -->
<block v-if="tabIndex === 0">
<view class="header-back">
<!-- 轮播图 -->
<view class="banner">
<swiper class="banner-swiper" indicator-color="#e93340" indicator-active-color="#f8f8f8" indicator-dots autoplay>
<swiper-item v-for="(item, index) in banners" :key="index">
<image class="cover" :src="item.cover" mode="aspectFill"></image>
</swiper-item>
</swiper>
</view>
<!-- 分类 -->
<view class="classify">
<view class="classify-item" v-for="(item, index) in classify" :key="index" @click="onClassify">
<image class="cover" :src="item.cover" mode="aspectFill"></image>
<view class="title">{{item.name}}</view>
</view>
<view class="classify-item">
<image class="cover" src="../../static/icons/equity_nav.png" mode="aspectFill"></image>
<view class="title">查看全部</view>
</view>
</view>
</view>
<!-- 每日推荐 -->
<view class="block-title">
<view class="title">
每日推荐<text>小易精选 推荐好物</text>
</view>
</view>
<view class="goods-push" v-if="JSON.stringify(position) != '{}'">
<view class="itme item-mian" @click="onGoods(position.one)">
<image class="cover" :src="position.one.cover" mode="widthFix"></image>
<view class="title">{{position.one.name}}</view>
<view class="price"><text>¥</text>{{position.one.original_price}}</view>
</view>
<view class="itme">
<view class="itme-list" v-for="(item, index) in position.two" :key="index" @click="onGoods(item)">
<image class="cover" :src="item.cover" mode="aspectFill"></image>
<view class="title">{{item.name}}</view>
<view class="price"><text>¥</text>{{item.original_price}}</view>
</view>
</view>
</view>
<!-- 优惠券 -->
<view class="block-title">
<view class="title">
限时抢购<text>海量商家优惠券</text>
</view>
<navigator class="more" url="#">查看更多</navigator>
</view>
<view class="coupons" v-if="coupons.length > 0">
<view class="coupons-item" v-for="(item, index) in coupons" :key="index">
<view class="content">
<view class="coupons-title">
<view class="coupons-title-tips" v-if="item.type">
<!-- value == 1服务券 value == 2代金券 value == 3提货券 -->
<image v-if="item.type.value == '1'" class="coupons-title-icon" src="../../static/icons/equity_coupons_01.png" mode="aspectFill"></image>
<image v-else-if="item.type.value == '2'" class="coupons-title-icon" src="../../static/icons/equity_coupons_02.png" mode="aspectFill"></image>
<image v-else-if="item.type.value == '3'" class="coupons-title-icon" src="../../static/icons/equity_coupons_03.png" mode="aspectFill"></image>
</view>
<view class="coupons-title-name">
{{item.title}}
</view>
</view>
<view class="sun-text">{{item.title}}</view>
</view>
<view class="logo">
<image class="logo-img" :src="item.cover" mode="aspectFill"></image>
</view>
<view class="btn">立即领取</view>
</view>
</view>
<!-- 优选商品 -->
<goods-list :list="goods" priceType="CNY" @on-goods="onGoods" />
</block>
</view>
</template>
<script>
import { companies, companiesList } from '@/apis/interfaces/company'
import { mall, list } from '@/apis/interfaces/goods'
import goodsList from '@/components/goods-list/goods-list'
import industryList from '@/components/industry-list/industry-list'
export default{
comments:{
goodsList,
industryList
},
data() {
return {
tabIndex : 0,
nvConfig : {
tabArr: [
{title:'易货商城', active:true},
{title:'企业广场'}
],
color: '#FFF',
hideback: true,
bgColor: '#e93340',
btn: [{
icon: '/static/icons/search-icon.png',
style: 'paddingRight: 20rpx;'
}]
},
// 易货部分
banners : [],
classify : [],
coupons : [],
position : {},
goods : [],
pages : {},
// 广场部分
industryIndex: 0,
recommendBus : [],
hotBus : [],
industryBus : [],
busList : [],
busPages : {}
};
},
created() {
this.getMall()
},
methods:{
// tab
onNvTab(e){
let tabIndex = e.index
for(let i in this.nvConfig.tabArr){
if(i == tabIndex) this.nvConfig.tabArr[i].active = true
else this.nvConfig.tabArr[i].active = false
}
this.tabIndex = tabIndex
if(tabIndex == 1 && this.recommendBus.length <= 0) this.getCompanies()
},
// 搜索
onRightBtn(e){
switch(e.index){
case 0:
this.$Router.push({name: 'Search'})
break
}
},
// 企业广场
getCompanies(){
companies().then(res=>{
this.recommendBus = res.positions
this.hotBus = res.hot
this.industryBus = [{title: '全部', industry_id: ''}, ...res.industries]
this.getCompaniesList()
}).catch(err => {
uni.showToast({
title: err.message,
icon : 'none'
})
})
},
// 企业广场行业
onBusIndustry(index){
this.industryIndex = index
this.getCompaniesList()
},
// 企业列表
getCompaniesList(){
companiesList({
industry_id: this.industryBus[this.industryIndex].industry_id
}).then(res => {
this.busList = res.data
this.busPages = res.pages
})
},
// 易货首页
getMall(){
mall().then(res => {
console.log(res.coupons)
this.classify = res.categories.slice(0, 9)
this.banners = res.banners
this.coupons = res.coupons
this.position = res.positions
}).catch(err => {
uni.showToast({
title: err.message,
icon : 'none'
})
})
this.getGoods()
},
// 商品列表
getGoods(){
list().then(res => {
this.goods = res.data
this.pages = res.page
})
},
// 商品详情
onGoods(e){
this.$Router.push({name: 'goodsDetails', params: {id: e.goods_id}})
},
// 易货分类
onClassify(){
this.$Router.push({name: 'goodsList'})
},
// 打开微信小程序
onOpenWechat(e){
plus.share.getServices(res => {
let sweixin = null;
for(let val of res){
if(val.id === 'weixin'){
sweixin = val
}
}
if(sweixin != null){
sweixin.launchMiniProgram({
id : e.original_id,
path: 'pages/index/index?scene=' + e.company_id,
})
}else{
uni.showToast({
title: '当前环境不支持打开微信小程序',
icon : 'none'
})
}
})
}
}
}
</script>
<style lang="scss" scoped>
// 易货
.header-back{
background-image: linear-gradient(to bottom, white , #f8f8f8);
padding-top: $padding;
// 易货轮播
.banner{
position: relative;
background: white;
margin: 0 $margin;
border-radius: $radius/2;
padding-top: calc(50% - #{$margin * 2});
overflow: hidden;
.banner-swiper{
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
.cover{
width: 100%;
height: 100%;
border-radius: $radius/2;
}
}
}
// 分类
.classify{
display: flex;
margin: $margin/2;
flex-wrap: wrap;
.classify-item{
margin: $margin/2;
width: calc(20% - #{$margin});
text-align: center;
.cover{
width: 98rpx;
height: 98rpx;
vertical-align: top;
margin-bottom: $margin/2;
}
.title{
line-height: 40rpx;
font-size: $title-size-sm;
color: $text-gray;
}
}
}
}
// 每日推荐
.goods-push{
margin: 0 $margin;
background: white;
border-radius: $radius/2;
display: flex;
.itme{
width: 50%;
}
.item-mian{
width: 50%;
padding: $padding/2;
height: 450rpx;
box-sizing: border-box;
text-align: center;
border-right: solid 1rpx $border-color;
@extend .vertical;
.title{
font-weight: bold;
font-size: $title-size;
@extend .nowrap;
line-height: 50rpx;
}
.price{
color: $text-price;
font-size: $title-size;
font-weight: bold;
line-height: 50rpx;
text{
font-size: 80%;
margin-right: 10rpx;
}
}
.cover{
margin-bottom: 30rpx;
width: 220rpx;
height: 220rpx;
vertical-align: top;
}
}
.itme-list{
position: relative;
padding: 25rpx $padding/2;
padding-left: 150rpx;
height: 150rpx;
box-sizing: border-box;
.title{
font-size: $title-size-lg;
@extend .nowrap;
line-height: 60rpx;
}
.price{
color: $text-price;
font-size: $title-size;
font-weight: bold;
line-height: 40rpx;
text{
font-size: 80%;
margin-right: 10rpx;
}
}
.cover{
position: absolute;
left: $padding/2;
top: $padding/2;
width: calc(150rpx - #{$padding});
height: calc(150rpx - #{$padding});
}
}
}
// 优惠券
.coupons{
display: flex;
flex-wrap: wrap;
margin: -10rpx ($margin - 10rpx);
.coupons-item{
background: white;
width: calc(50% - 20rpx);
margin: 10rpx;
border-radius: $radius/2;
padding: $padding - 10;
box-sizing: border-box;
// display: flex;
align-items: center;
justify-content: space-between;
.content{
width: 100%;
.coupons-title{
font-size: $title-size-lg;
font-weight: 600;
margin-bottom: 10rpx;
line-height: 54rpx;
display: flex;
@extend .nowrap;
.coupons-title-tips {
width: 50rpx;
height: 50rpx;
border-radius: 50%;
overflow: hidden;
background-color: #f5f5f5;
text-align: center;
margin-right: 10rpx;
.coupons-title-icon {
width: 28rpx;
height: 28rpx;
margin: 11rpx;
}
}
}
.sun-text{
font-size: $title-size-sm;
color: $text-gray;
line-height: 54rpx;
@extend .nowrap;
}
.btn{
color: $text-price;
border:solid 1rpx $text-price;
display: inline-block;
font-size: $title-size-sm;
padding: 0 ($padding/2);
height: 45rpx;
line-height: 45rpx;
border-radius: 22rpx;
}
}
.logo{
width: 100%;
padding-top: 70%;
border-radius: 10rpx;
overflow: hidden;
position: relative;
.logo-img {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
}
}
.btn {
background-color: #faf2dd;
border-radius: 8rpx;
margin-top: 20rpx;
color: #fd5f3c;
text-align: center;
line-height: 64rpx;
font-size: 28rpx;
font-weight: 600;
}
}
}
// 行业分类
.industry-tabs{
white-space:nowrap;
.industry-item{
margin-left: $margin;
display: inline-block;
line-height: 50rpx;
font-size: $title-size-lg;
color: $text-gray;
&:last-child{
margin-right: $margin;
}
&.show{
color: $text-price;
font-size: $title-size;
font-weight: bold;
}
}
}
// 热易商家
.hot-swiper{
margin: 0 $margin $margin $margin;
height: 240rpx;
.hot-box{
position: absolute;
height: 100%;
width: 100%;
border-radius: ($radius/2);
background: white;
padding: $padding;
box-sizing: border-box;
.hot-vip {
position: absolute;
top: $padding;
left: $padding;
background-color: #191919;
color: #f3c8a8;
font-size: 24rpx;
line-height: 32rpx;
padding: 0 8rpx;
border-radius: 6rpx 6rpx 6rpx 0;
text-transform: uppercase;
}
.hot-tool {
position: absolute;
top: 30rpx;
right: $padding;;
.hot-deal {
width: 120rpx;
height: 120rpx;
text-align: center;
box-shadow: 0 0 14rpx rgba(260, 60, 80, .9);
background: linear-gradient(to bottom, #e1293f, #f85d31);
color: #FFFFFF;
font-size: 26rpx;
border-radius: 50%;
padding-top: 25rpx;
box-sizing: border-box;
font-size: 24rpx;
.hot-deal-number {
font-size: 28rpx;
font-weight: 600;
display: block;
}
}
}
.cover{
position: absolute;
left: $padding;
top: $padding;
width: calc(198rpx - #{$padding * 2});
height: calc(198rpx - #{$padding * 2});
}
.hot-content{
padding-left: calc(198rpx - #{$padding});
.hot-title{
font-weight: bold;
font-size: $title-size-lg;
line-height: 40rpx;
height: 40rpx;
width: 60%;
}
.hot-credit {
font-size: 24rpx;
display: inline-block;
font-weight: normal;
color: #ec652f;
border: 2rpx solid #ec652f;
line-height: 36rpx;
border-radius: 6rpx;
padding: 0 6rpx;
margin-top: 10rpx;
}
.hot-trade,
.hot-warrant{
font-size: 24rpx;
color: #999;
padding-top: 10rpx;
}
.hot-trade {
width: 60%;
}
.hot-warrant {
display: flex;
.hot-warrant-text {
width: 50%;
}
.hot-bar {
display: flex;
width: 50%;
margin-top: 4rpx;
.hot-bar-color{
background: #ebebeb;
border-left: 2rpx solid transparent;
border-right: 2rpx solid transparent;
width: calc(100% - 50rpx);
border-radius: 60rpx;
margin: 6rpx 10rpx 0 0;
height: 18rpx;
.hot-bar-strip {
background-color: #ff8562;
border: 1px solid #ff8562;
border-radius: 10px;
box-shadow: 1vw 3vh 10vh rgba(168, 7, 7, 0.8);
background-size: 3em 3em;
background-image: linear-gradient(-45deg, transparent 0em, transparent 0.8em, #ec3950 0.9em, #ec3950 2.1em, transparent 2.1em, transparent 2.9em, #ec3950 3.1em);
height: 14rpx;
border-radius: 60rpx;
position: relative;
&::after {
content: '';
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 1;
height: 100%;
background-image: linear-gradient(to bottom, #db152e, rgba(168, 7, 7, 0.6) 15%, transparent 60%, #db152e);
border-radius: 20rpx;
}
}
}
.hot-bar-per {
font-size: 20rpx;
color: rgba(0, 0, 0, 0.8);
transform: scale(.8);
}
}
}
.credibility{
height: calc(108rpx - #{$padding * 2});
font-size: $title-size-m;
color: $text-gray;
}
.trading{
line-height: 40rpx;
font-size: $title-size-sm;
color: $text-gray;
}
}
.btn{
position: absolute;
top: 50%;
right: $padding;
font-size: $title-size-m;
background-color: $text-price;
color: white;
line-height: 50rpx;
width: 100rpx;
text-align: center;
margin-top: -25rpx;
}
}
}
// 推荐商家
.recommended{
white-space:nowrap;
.item-box{
position: relative;
display: inline-block;
background: white;
width: 370rpx;
margin-left: $margin;
border-radius: ($radius/2);
padding: ($padding + $padding/2) $padding - 10;
box-sizing: border-box;
text-align: center;
&:last-child{
margin-right: $margin;
}
.item-cover{
background: #eee;
width: 120rpx;
height: 120rpx;
vertical-align: top;
border-radius: $radius / 2;
}
.item-vip {
position: absolute;
left: 0;;
top: 20rpx;
background-color: #f3ceb3;
font-size: 24rpx;
line-height: 38rpx;
border-radius: 0 6rpx 6rpx 0;
display: flex;
.item-vip-text {
background-color: #191919;
color: #f3c8a8;
padding: 0 6rpx;
text-transform: uppercase;
}
.item-vip-tips {
padding: 0 6rpx;
}
}
.item-title{
margin: 20rpx 0 10rpx;
}
.item-trade {
font-size: 28rpx;
color: #999999;
}
.item-bar {
display: flex;
margin: 10rpx 0 20rpx;
.item-bar-color {
background: #ebebeb;
border-left: 2rpx solid transparent;
border-right: 2rpx solid transparent;
width: calc(100% - 50rpx);
border-radius: 60rpx;
margin: 4rpx 10rpx 0 0;
.item-bar-strip {
background-color: #f3c8a8;
border: 2rpx solid #f3c8a8;
border-radius: 20rpx;
box-shadow: 1vw 3vh 10vh rgba(168, 7, 7, 0.8);
background-size: 3em 3em;
background-image: linear-gradient(-45deg, transparent 0em, transparent 0.8em, #ee883d 0.9em, #ee883d 2.1em, transparent 2.1em, transparent 2.9em, #ee883d 3.1em);
animation: warning-animation 1.4s infinite linear;
height: 14rpx;
border-radius: 60rpx;
position: relative;
.item-bar-per {
position: absolute;
right: 0;
top: -6rpx;
height: 100%;
font-size: 20rpx;
color: rgba(0, 0, 0, 0.8);
text-shadow: 0 1px rgba(255, 255, 255, 0.4);
transform: scale(.8);
}
&::after {
content: '';
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 1;
height: 100%;
background-image: linear-gradient(to bottom, #ee883d, rgba(168, 7, 7, 0.6) 15%, transparent 60%, #ee883d);
border-radius: 10px;
}
@keyframes warning-animation {
0% {
background-position: 0 0;
}
100% {
background-position: 3em 0;
}
}
}
}
.item-bar-strip-img {
width: 24rpx;
height: 24rpx;
animation: bounce-up 1.5s linear infinite;
}
@keyframes bounce-up {
25% {transform: translateY(2rpx);}
50%, 100% {transform: translateY(0);}
75% {transform: translateY(-2rpx);}
}
}
.item-url {
background: linear-gradient(to right, #e1293f, #f85d31);
color: #FFFFFF;
border-radius: 10rpx;
display: flex;
line-height: 52rpx;
padding: 10rpx;
box-sizing: border-box;
font-size: 28rpx;
.item-credit {
font-size: 24rpx;
border-radius: 6rpx;
background-color: #FFFFFF;
color: #e1293f;
flex: 1;
display: flex;
text-align: left;
padding: 0 10rpx;
box-sizing: border-box;
.item-credit-img {
width: 28rpx;
height: 28rpx;
margin: 12rpx 8rpx 0 0;
}
}
.item-btn{
padding-left: 20rpx;
box-sizing: border-box;
display: flex;
image {
width: 32rpx;
height: 32rpx;
margin-top: 12rpx;
}
}
}
}
}
// 模块标题
.block-title{
padding: $padding;
display: flex;
justify-content: space-between;
.title{
font-weight: bold;
text{
padding-left: $padding/2;
font-weight: normal;
font-size: $title-size-m;
color: $text-gray;
}
}
.more{
font-size: $title-size-m;
font-weight: normal;
color: $text-gray;
}
}
</style>

View File

@@ -0,0 +1,22 @@
<template>
<view>
搜索
</view>
</template>
<script>
export default {
data() {
return {
}
},
methods: {
}
}
</script>
<style>
</style>

View File

@@ -0,0 +1,275 @@
<template>
<view class="content">
<!-- 商品图片 -->
<view class="form-block">
<view class="form-upd">
<view class="form-title">商品轮播图<text>首图为产品封面</text></view>
<view class="form-imgs">
<view class="item" v-for="(item, index) in cover" :key="index">
<image class="item-cover" src="@/static/dev/good_cover_00.jpg" mode="aspectFill"></image>
</view>
<view class="item item-add">
<image class="item-cover" src="@/static/icons/add-icon.png" mode="aspectFill"></image>
</view>
</view>
</view>
</view>
<!-- 商品基本信息 -->
<view class="form-block">
<view class="form-box inputs-flex">
<label class="form-label">标题</label>
<input type="text" v-model="name" placeholder="输入商品标题"/>
</view>
<view class="form-box inputs-flex">
<label class="form-label">商品描述</label>
<input type="text" v-model="description" placeholder="输入商品描述"/>
</view>
<view class="form-box picker-flex">
<label class="form-label">产品详情</label>
<view class="picker-text">
<uni-icons class="picker-icon" type="arrowright" color="#999"></uni-icons>
</view>
</view>
</view>
<!-- 商品价格 -->
<view class="form-block">
<view class="form-box inputs-flex input-unit">
<label class="form-label">划线价</label>
<input type="digit" v-model="original_price" placeholder="0.00"/>
<text class="units">/</text>
</view>
<view class="form-box inputs-flex input-unit">
<label class="form-label">市场价格</label>
<input type="digit" v-model="skus_cost" placeholder="0.00"/>
<text class="units">/</text>
</view>
<view class="form-box inputs-flex input-unit">
<label class="form-label">销售价格</label>
<input type="digit" v-model="skus_price" placeholder="0.00"/>
<text class="units">/</text>
</view>
<view class="form-box inputs-flex input-unit">
<label class="form-label">资产</label>
<input type="digit" v-model="skus_assets" placeholder="0.00"/>
<text class="units">/</text>
</view>
<view class="form-box inputs-flex input-unit">
<label class="form-label">分销佣金</label>
<input type="digit" v-model="skus_charge" placeholder="0.00"/>
<text class="units">/</text>
</view>
</view>
<!-- 商品详情介绍 -->
<view class="form-block">
<view class="form-box inputs-flex">
<label class="form-label">易货起购数量</label>
<input type="number" v-model="skus_number" placeholder="输入易货起购数量"/>
</view>
<view class="form-box inputs-flex">
<label class="form-label">商品库存</label>
<input type="number" v-model="skus_stock" placeholder="输入商品库存"/>
</view>
</view>
<!-- 售后服务 -->
<view class="form-block">
<view class="form-box picker-flex">
<label class="form-label">分类</label>
<picker mode="selector">
<view class="picker-text">
请选择分类
<uni-icons class="picker-icon" type="arrowright" color="#999"></uni-icons>
</view>
</picker>
</view>
<view class="form-box picker-flex">
<label class="form-label">支付方式</label>
<picker mode="selector">
<view class="picker-text">
请选择支付方式
<uni-icons class="picker-icon" type="arrowright" color="#999"></uni-icons>
</view>
</picker>
</view>
<view class="form-box picker-flex">
<label class="form-label">允许售后</label>
<picker mode="selector">
<view class="picker-text">
<uni-icons class="picker-icon" type="arrowright" color="#999"></uni-icons>
</view>
</picker>
</view>
<view class="form-box picker-flex">
<label class="form-label">可选服务</label>
<view class="picker-text">
<uni-icons class="picker-icon" type="arrowright" color="#999"></uni-icons>
</view>
</view>
</view>
<!-- 安全区 -->
<view class="ios-bottom"></view>
<!-- footer -->
<view class="footer">
<button class="footer-btn" type="default">发布</button>
<view class="ios-bottom"></view>
</view>
</view>
</template>
<script>
import { managesGoodsCreate } from '@/apis/interfaces/goods'
import { uploads } from '@/apis/interfaces/uploading'
export default {
data() {
return {
cover : [],
name : '',
description : '',
original_price : '',
skus_cost : '',
skus_price : '',
skus_assets : '',
skus_charge : '',
skus_number : '',
skus_stock : '',
};
},
methods: {
}
}
</script>
<style lang="scss" scoped>
.content{
padding-bottom: 150rpx;
}
// 表单
.form-block{
background: white;
margin-top: $margin - 10;
.form-box{
position: relative;
padding-left: 240rpx;
padding-right: $padding;
font-size: $title-size-lg;
min-height: 80rpx;
&::after{
position: absolute;
bottom: 0;
left: $margin;
right: 0;
height: 1rpx;
content: " ";
background: $border-color;
}
&:last-child::after{
display: none;
}
.form-label{
position: absolute;
left: $margin;
line-height: 80rpx;
top: 0;
width: calc(240rpx - #{$margin});
}
}
.inputs-flex{
input{
height: 80rpx;
line-height: 80rpx;
}
}
.input-unit{
padding-right: 200rpx;
.units{
position: absolute;
right: 0;
top: 0;
line-height: 80rpx;
height: 80rpx;
width: 200rpx;
padding-right: $padding;
text-align: right;
box-sizing: border-box;
}
}
.picker-flex{
.picker-text{
position: relative;
line-height: 80rpx;
min-height: 80rpx;
padding-right: 80rpx;
@extend .nowrap;
.picker-icon{
right: 0;
position: absolute;
}
}
}
.form-upd{
.form-title{
font-size: $title-size-lg;
line-height: 80rpx;
padding: 0 $padding;
text{
font-size: 80%;
color: $text-gray;
}
}
.form-imgs{
margin-top: -($margin/3);
padding: 0 20rpx 20rpx;
display: flex;
flex-wrap: wrap;
.item{
width: calc(20% - 14rpx);
padding-top: calc(20% - 14rpx);
margin: 7rpx;
position: relative;
.item-cover{
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
}
.item-add{
border: dashed 2rpx $border-color;
box-sizing: border-box;
.item-cover{
top: calc(15% - 2rpx);
left: calc(15% - 2rpx);
width: 70%;
height: 70%;
}
}
}
}
}
// 发布
.footer{
background: white;
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: 20rpx $padding;
box-shadow: 0 0 4rpx 4rpx rgba($color: #000000, $alpha: .02);
z-index: 99;
.footer-btn{
border: none;
border-radius: 0;
background: $text-price;
height: 90rpx;
line-height: 90rpx;
font-weight: bold;
font-size: $title-size;
color: white;
&::after{
border: none;
}
}
}
</style>

Some files were not shown because too many files have changed in this diff Show More