Browse Source

init

dev_xrcc
jiazhipeng 4 months ago
parent
commit
6530033fa0
  1. 3
      .gitignore
  2. 17
      App.vue
  3. 58
      components/Search.vue
  4. 101
      components/SearchHeader.vue
  5. 99
      components/TitleHeader.vue
  6. 398
      components/addressAdd.vue
  7. 469
      components/cartData.vue
  8. 265
      components/contactAdd.vue
  9. 260
      components/recommend.vue
  10. 12
      components/recommend.vue.rej
  11. 22
      main.js
  12. 42
      mixins/myMixins.js
  13. 18
      package-lock.json
  14. 20
      package.json
  15. 166
      pages.json
  16. 650
      pages/index/index.vue
  17. 258
      pages/login/login.vue
  18. 47
      pages/stratIndex.vue
  19. 659
      pages/user/user.vue
  20. 107
      static/css/base.css
  21. BIN
      static/images/home.png
  22. BIN
      static/images/mine.png
  23. BIN
      static/images/selectHome.png
  24. BIN
      static/images/selectMine.png
  25. 306
      static/js/CommonFunction.js
  26. 196
      static/js/mmmm-image-tools/index.js
  27. 11
      static/js/mmmm-image-tools/package.json
  28. 81
      static/js/request.js
  29. 426
      static/js/weapp-qrcode.js
  30. 12
      store/index.js
  31. 69
      store/modules/user.js
  32. 8
      subPackages/index.vue
  33. 147
      subPackages/letter/detail.vue
  34. 151
      subPackages/letter/index.vue
  35. 1651
      subPackages/order/detail.vue
  36. 912
      subPackages/order/gwcOrder.vue
  37. 702
      subPackages/order/trades.vue
  38. 115
      subPackages/shiguang/index.vue
  39. 790
      subPackages/techan/detail.vue
  40. 820
      subPackages/techan/index.vue
  41. 1281
      subPackages/techan/order.vue
  42. 253
      subPackages/techan/selfPickUpPoint.vue
  43. 89
      subPackages/user/changeNickname.vue
  44. 556
      subPackages/user/gwc.vue
  45. 387
      subPackages/user/myAddressAdd.vue
  46. 58
      subPackages/user/privacy.vue
  47. 30
      subPackages/user/privacyInfo.vue
  48. 407
      subPackages/user/profile.vue
  49. 339
      subPackages/user/travelerList.vue
  50. 59
      subPackages/video/video.vue
  51. 28
      subPackages/webPage/webPage.vue
  52. 2
      uni.scss
  53. 31
      uni_modules/uni-badge/changelog.md
  54. 268
      uni_modules/uni-badge/components/uni-badge/uni-badge.vue
  55. 85
      uni_modules/uni-badge/package.json
  56. 10
      uni_modules/uni-badge/readme.md
  57. 20
      uni_modules/uni-calendar/changelog.md
  58. 546
      uni_modules/uni-calendar/components/uni-calendar/calendar.js
  59. 12
      uni_modules/uni-calendar/components/uni-calendar/i18n/en.json
  60. 8
      uni_modules/uni-calendar/components/uni-calendar/i18n/index.js
  61. 12
      uni_modules/uni-calendar/components/uni-calendar/i18n/zh-Hans.json
  62. 12
      uni_modules/uni-calendar/components/uni-calendar/i18n/zh-Hant.json
  63. 188
      uni_modules/uni-calendar/components/uni-calendar/uni-calendar-item.vue
  64. 562
      uni_modules/uni-calendar/components/uni-calendar/uni-calendar.vue
  65. 350
      uni_modules/uni-calendar/components/uni-calendar/util.js
  66. 85
      uni_modules/uni-calendar/package.json
  67. 103
      uni_modules/uni-calendar/readme.md
  68. 26
      uni_modules/uni-card/changelog.md
  69. 270
      uni_modules/uni-card/components/uni-card/uni-card.vue
  70. 90
      uni_modules/uni-card/package.json
  71. 12
      uni_modules/uni-card/readme.md
  72. 36
      uni_modules/uni-collapse/changelog.md
  73. 402
      uni_modules/uni-collapse/components/uni-collapse-item/uni-collapse-item.vue
  74. 147
      uni_modules/uni-collapse/components/uni-collapse/uni-collapse.vue
  75. 89
      uni_modules/uni-collapse/package.json
  76. 12
      uni_modules/uni-collapse/readme.md
  77. 15
      uni_modules/uni-combox/changelog.md
  78. 275
      uni_modules/uni-combox/components/uni-combox/uni-combox.vue
  79. 90
      uni_modules/uni-combox/package.json
  80. 11
      uni_modules/uni-combox/readme.md
  81. 24
      uni_modules/uni-countdown/changelog.md
  82. 6
      uni_modules/uni-countdown/components/uni-countdown/i18n/en.json
  83. 8
      uni_modules/uni-countdown/components/uni-countdown/i18n/index.js
  84. 6
      uni_modules/uni-countdown/components/uni-countdown/i18n/zh-Hans.json
  85. 6
      uni_modules/uni-countdown/components/uni-countdown/i18n/zh-Hant.json
  86. 271
      uni_modules/uni-countdown/components/uni-countdown/uni-countdown.vue
  87. 86
      uni_modules/uni-countdown/package.json
  88. 10
      uni_modules/uni-countdown/readme.md
  89. 45
      uni_modules/uni-data-checkbox/changelog.md
  90. 821
      uni_modules/uni-data-checkbox/components/uni-data-checkbox/uni-data-checkbox.vue
  91. 84
      uni_modules/uni-data-checkbox/package.json
  92. 18
      uni_modules/uni-data-checkbox/readme.md
  93. 68
      uni_modules/uni-data-picker/changelog.md
  94. 45
      uni_modules/uni-data-picker/components/uni-data-picker/keypress.js
  95. 554
      uni_modules/uni-data-picker/components/uni-data-picker/uni-data-picker.vue
  96. 563
      uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-picker.js
  97. 335
      uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-pickerview.vue
  98. 90
      uni_modules/uni-data-picker/package.json
  99. 22
      uni_modules/uni-data-picker/readme.md
  100. 25
      uni_modules/uni-data-select/changelog.md

3
.gitignore

@ -0,0 +1,3 @@
/.hbuilderx/
/unpackage/
/node_modules/

17
App.vue

@ -1,6 +1,7 @@
<script> <script>
export default { export default {
onLaunch: function() { onLaunch: function() {
console.warn('当前组件仅支持 uni_modules 目录结构 ,请升级 HBuilderX 到 3.1.0 版本以上!')
console.log('App Launch') console.log('App Launch')
}, },
onShow: function() { onShow: function() {
@ -12,6 +13,20 @@
} }
</script> </script>
<style> <style lang="scss">
/*每个页面公共css */ /*每个页面公共css */
@import '@/uni_modules/uni-scss/index.scss';
@import "@/static/css/base.css";
/* #ifndef APP-NVUE */
//
page {
background-color: #f5f5f5;
}
/* #endif */
.example-info {
font-size: 14px;
color: #333;
padding: 10px;
}
</style> </style>

58
components/Search.vue

@ -0,0 +1,58 @@
<template>
<view>
<view class="searchBox flex-between">
<view class="inputBox">
<img src="https://static.ticket.sz-trip.com/taihu/images/scenic/searchGray.png" alt="">
<view>
搜索你想要的产品
</view>
</view>
<view class="btn">
搜索
</view>
</view>
</view>
</template>
<script>
</script>
<style scoped lang="scss">
.searchBox{
width: 697rpx;
height: 67rpx;
background: rgba(242, 242, 242, .7);
border-radius: 33rpx;
margin: auto;
padding: 0 7rpx 0 29rpx;
box-sizing: border-box;
.inputBox{
display: flex;
align-items: center;
font-size: 31rpx;
font-family: PingFang SC;
font-weight: 400;
color: #999999;
img{
width: 28rpx;
height: 30rpx;
margin-right: 19rpx;
}
}
.btn{
width: 107rpx;
height: 53rpx;
background: #00D7ED;
border-radius: 27rpx;
text-align: center;
line-height: 53rpx;
font-size: 28rpx;
font-family: PingFang SC;
font-weight: 400;
color: #FFFFFF;
}
}
</style>

101
components/SearchHeader.vue

@ -0,0 +1,101 @@
<template>
<div class="title-header-box search" :style="{height:`${height}px`}">
<div class="title-header"
:style="{height:`${height}px`,top:`${height+padHeight}px`,backgroundColor:bgColor || '#FFFFFF'}">
<div class="search-box" :style="{backgroundColor:bgColor ? '#F2F2F2' : '#F2F2F2'}">
<img src="https://static.ticket.sz-trip.com/taizhou/images/search.png" alt="">
<input type="text" class="input" placeholder="请输入想要的产品或服务" v-model="keywords" @confirm="search" />
<div class="btn" @click="search()">搜索</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "SearchHeader",
props: ['bgColor'],
data() {
return {
keywords: '',
padHeight: 0,
height: 88,
}
},
methods: {
search() {
uni.$emit('search', {
keywords: this.keywords
})
},
},
created() {
// #ifdef MP-WEIXIN
let systemInfo = uni.getSystemInfoSync(),
rect = uni.getMenuButtonBoundingClientRect();
this.height = (rect.top - systemInfo.statusBarHeight) * 2 + rect.height
this.padHeight = systemInfo.statusBarHeight
// #endif
// #ifdef APP-PLUS
let systemInfo = uni.getSystemInfoSync()
this.height = systemInfo.statusBarHeight*1.5
this.padHeight = systemInfo.statusBarHeight
// #endif
}
}
</script>
<style scoped>
.title-header-box {
padding: 16rpx 0;
}
.title-header {
padding: 16rpx 0;
width: 100%;
position: fixed;
left: 0;
right: 0;
top: 0;
margin: auto;
z-index: 1000;
}
.search-box {
width: 697rpx;
height: 68rpx;
margin: 0 auto;
border-radius: 60rpx;
padding: 6rpx 7rpx 6rpx 26rpx;
font-size: 26rpx;
display: flex;
align-items: center;
box-sizing: border-box;
background: #F2F2F2;
}
.search-box img {
width: 28rpx;
height: 30rpx;
}
.search-box .input {
flex: 1;
border: none;
outline: none;
background: none;
color: #000000;
margin: 0 20rpx;
background-color: #F2F2F2;
}
.search-box .btn {
/* background-color: #FFC825; */
border-radius: 27rpx;
font-size: 28rpx;
color: #FFFFFF;
padding: 10rpx 26rpx;
background-color: #00AAFF;
}
</style>

99
components/TitleHeader.vue

@ -0,0 +1,99 @@
<template>
<view class="title-header-box" :style="{height:`${height+padHeight}px`}">
<view class="title-header" :style="{paddingTop:`${padHeight}px`,height:`${height}px`}">
<view class="left" @click="goBack" v-if="title !== '太湖'"><i class="iconfont">&#xe64b;</i></view>
<view class="center">{{title ? title.substr(0,10) : '太湖'}}</view>
<view class="right"></view>
</view>
</view>
</template>
<script>
export default {
name: "SearchHeader",
props: ['title', 'icon'],
watch: {
'title'(newVal, oldVal) {
this.title = newVal
}
},
data() {
return {
keywords: '',
padHeight: 0,
height: 88
}
},
methods: {
search() {
uni.$emit('search', {
keywords: this.keywords
})
},
},
created() {
let systemInfo = uni.getSystemInfoSync(),
rect = uni.getMenuButtonBoundingClientRect();
let height = (rect.top - systemInfo.statusBarHeight) * 2 + rect.height
this.height = height
this.padHeight = systemInfo.statusBarHeight
this.right = (systemInfo.screenWidth - rect.right) + rect.width
}
}
</script>
<style scoped>
.title-header-box {
background-color: #F8F8F8;
color: #000000;
}
.title-header {
/* background-color: #FFC825; */
background: white;
display: flex;
justify-content: center;
align-items: center;
height: 88rpx;
position: fixed;
left: 0;
right: 0;
top: 0;
z-index: 1000;
padding-top: 40rpx;
}
.title-header .left {
padding-left: 30rpx;
width: 58rpx;
display: flex;
position: absolute;
left: 0;
}
.title-header .search-box {
width: 502rpx;
height: 58rpx;
border-radius: 29rpx;
background: #F0F0F0;
padding: 0 26rpx;
font-size: 26rpx;
display: flex;
align-items: center;
}
.title-header .search-box .iconfont {
flex-shrink: 0;
color: #999999;
font-size: 30rpx;
margin-right: 10;
}
.title-header .search-box .input {
flex: 1;
border: none;
outline: none;
background: none;
color: #666;
}
</style>

398
components/addressAdd.vue

@ -0,0 +1,398 @@
<template>
<view class="bg">
<view class="list-forms">
<view class="list-item">
<view class="list-item-title">姓名</view>
<view class="list-item-input"><input type="text" v-model="username" placeholder="请输入姓名" /></view>
</view>
<view class="list-item">
<view class="list-item-title">手机号</view>
<view class="list-item-input"><input type="number" v-model="mobile" placeholder="请输入手机号" maxlength="11"
/></view>
</view>
<view class="list-item" style="position: relative;">
<view class="list-item-title">选择地区</view>
<view class="list-item-input3" style="flex: 1;">
<picker mode="multiSelector" :range="newProvinceDataList" range-key="name" @change="changeArea" @columnchange="pickerColumnchange"
style="position: relative;z-index: 2;">
<input type="text" readonly style="font-size: 35rpx;position: relative;z-index: -1;"
v-model="citySeld" disabled="true" placeholder="请选择地区"/>
</picker>
</view>
</view>
<view class="list-item">
<view class="list-item-title">详细地址</view>
<view class="list-item-input"><input type="text" v-model="detailAddr" placeholder="请输入详细地址" /></view>
</view>
<view class="list-item" style="border-bottom: 0;">
<view class="list-item-title">设为默认</view>
<view class="list-item-switch">
<switch :checked="idDefault" @change="switchChange" color="#74A5AA"/>
</view>
</view>
<!-- <view class="list-item-btn">
<view class="list-item-post" @click="postSave()">保存</view>
</view> -->
</view>
</view>
</template>
<script>
import District from 'ydui-district/dist/jd_province_city_area_id';
export default {
data() {
return {
username: '',
mobile: '',
citySeld: '',
detailAddr: '',
idDefault: false,
title: '新增收货地址',
show: false,
district: District, //
ready: false,
province: null,
city: null,
area: '',
provinceId: null,
cityId: null,
areaId: null,
columns: [],
id: '',
newProvinceDataList:[
[],[],[]
],
multiIndex: [0, 0, 0],
}
},
onLoad(option){
},
methods: {
init(option) {
this.id = null
this.username = ''
this.mobile = ''
this.citySeld = ''
this.detailAddr = ''
this.idDefault = false
this.province = null
this.city = null
this.area = ''
this.provinceId = null
this.cityId = null
this.areaId = null
this.multiIndex = [0, 0, 0]
this.id = option.id
if (option.id > 0) {
this.title = '编辑收货地址'
this.getDetail()
}
else {
this.getSeldCityList()
}
},
switchChange(e){
this.idDefault = e.detail.value
},
changeArea(e){
//
this.multiIndex = e.detail.value;
this.citySeld = this.newProvinceDataList[0][this.multiIndex[0]].name + this.newProvinceDataList[1][this.multiIndex[1]].name + this.newProvinceDataList[2][this.multiIndex[2]].name
this.provinceId = this.newProvinceDataList[0][this.multiIndex[0]].id
this.cityId = this.newProvinceDataList[1][this.multiIndex[1]].id
this.areaId = this.newProvinceDataList[2][this.multiIndex[2]].id
},
getSeldCityList() {
if (this.columns.length>0) {
return
}
let that = this
that.Post({}, '/api/areas/getAll').then(res => {
if (res.code === 200) {
var data = res.data;
var result = {};
for (var i = 0; i < data.length; i++) {
var item = data[i];
if (item.parent_id == 0) {
continue;
}
//
if (item.parent_id == "1") {
result[item.id.toString()] = {};
result[item.id.toString()].children = []
result[item.id.toString()].name = item.name;
result[item.id.toString()].id = item.id;
} else if (result[item.parent_id.toString()]) {
//
var t = {
id: item.id,
name: item.name,
children: []
}
result[item.parent_id.toString()].children.push(t)
} else {
//
var k = {
id: item.id,
name: item.name
}
for (var j = 0; j < result[item.parent_id.toString().substr(0, 2) + "0000"].children
.length; j++) {
if (result[item.parent_id.toString().substr(0, 2) + "0000"].children[j].id == item
.parent_id) {
result[item.parent_id.toString().substr(0, 2) + "0000"].children[j].children.push(k)
}
}
}
}
var r = [];
//ObjectArray
for (var i in result) {
r.push(result[i]);
}
//
that.district = r;
let arr = []
let arr1 = []
let arr2 = []
that.district.forEach(item => {
arr.push(item)
})
that.district[0].children.forEach(item => {
arr1.push(item)
})
that.district[0].children[0].children.forEach(item => {
arr2.push(item)
})
that.columns = arr
//
that.ready = true;
console.log(this.columns)
for(let i=0; i<this.columns.length; i++){
this.newProvinceDataList[0].push({name:this.columns[i].name,id:this.columns[i].id});
}
console.log(this.columns[0].children)
for(let i=0; i<this.columns[0].children.length; i++){
this.newProvinceDataList[1].push({name:this.columns[0].children[i].name,id:this.columns[0].children[i].id});
}
for(let i=0; i<this.columns[0].children[0].children.length; i++){
this.newProvinceDataList[2].push({name:this.columns[0].children[0].children[i].name,id:this.columns[0].children[0].children[i].id});
}
}
})
},
//
pickerColumnchange(e){
//
// console.log(e.detail.column);
//
// console.log(e.detail.value)
//
if(e.detail.column === 0){
this.multiIndex[0] = e.detail.value
// console.log('');
// this.newProvinceDataList[1] = [];
this.newProvinceDataList[1] = this.columns[this.multiIndex[0]].children.map((item,index)=>{
// console.log(item)
return item
})
// console.log(this.multiIndex)
if(this.columns[this.multiIndex[0]].children.length === 1){
this.newProvinceDataList[2] = this.columns[this.multiIndex[0]].children[0].children.map((item,index)=>{
// console.log(item)
return item
})
}else{
this.newProvinceDataList[2] = this.columns[this.multiIndex[0]].children[this.multiIndex[1]].children.map((item,index)=>{
// console.log(item)
return item
})
}
//
this.multiIndex.splice(1, 1, 0)
this.multiIndex.splice(2, 1, 0)
}
//
if(e.detail.column === 1){
this.multiIndex[1] = e.detail.value
// console.log('');
// console.log(this.multiIndex)
this.newProvinceDataList[2] = this.columns[this.multiIndex[0]].children[this.multiIndex[1]].children.map((item,index)=>{
// console.log(item)
return item
})
//
this.multiIndex.splice(2, 1, 0)
}
//
if(e.detail.column === 2){
this.multiIndex[2] = e.detail.value
// console.log('')
// console.log(this.multiIndex)
}
},
getDetail() {
this.Post({
id: this.id
}, "/api/user/contactDetail").then(res => {
res = res.data;
if (res && res.id > 0) {
this.username = res.name
this.mobile = res.tel
this.idDefault = res.is_default == 1 ? true : false
this.provinceId = res.province_id
this.cityId = res.city_id
this.areaId = res.district_id
this.citySeld = res.province_text + '' + res.city_text + '' + res.district_text
this.detailAddr = res.detail_addr;
this.getSeldCityList();
}
})
},
async postSave() {
this.username = this.username.trim()
this.mobile = this.mobile.trim()
this.detailAddr = this.detailAddr.trim()
if (this.username.length < 1) {
uni.showToast({
title: '请输入姓名',
icon: 'none'
})
return
}
if (this.username.length > 6) {
uni.showToast({
title: '姓名最多6个字',
icon: 'none'
})
return
}
if (!this.IsTel(this.mobile)) {
uni.showToast({
title: '请输入正确的手机号',
icon: 'none'
})
return
}
if (this.citySeld.length < 1) {
uni.showToast({
title: '请选择地区',
icon: 'none'
})
return
}
if (this.detailAddr.length < 2) {
uni.showToast({
title: '请输入具体地址',
icon: 'none'
})
return
}
let res = await this.Post({
name: this.username,
tel: this.mobile,
is_default: this.idDefault ? '1' : '0',
province_id: this.provinceId,
city_id: this.cityId,
district_id: this.areaId,
detail_addr: this.detailAddr,
id: this.id || null
},'/api/user/' + (this.id > 0 ? 'edit' : 'add') + 'Consignee')
if(res.code == '1'){
// uni.setStorageSync('addressNow',JSON.stringify(res.data))
uni.showModal({
title: '提示',
content: this.id>0?'编辑成功':'添加成功',
showCancel: false,
})
}else{
uni.showModal({
title: '提示',
content: res.msg,
showCancel: false,
})
}
return {...res,data: {id: this.id}}
}
}
}
</script>
<style scoped>
.bg {
width: 100%;
height: 100%;
}
.list-forms {
display: flex;
flex-direction: column;
box-sizing: content-box
}
.list-item {
display: flex;
border-bottom: 1rpx solid #D8D8D8;
padding: 30rpx 0;
height: 60rpx;
align-items: center;
box-sizing: content-box
}
.list-item-title {
width: 150rpx;
font-size: 31rpx;
margin-right: 20rpx;
}
.list-item-input {
flex: 1;
}
.list-item-input input {
width: 100%;
height: 100%;
padding: 0;
margin: 0;
border: 0;
background-color: transparent;
line-height: 31rpx;
font-size: 31rpx;
}
.list-item-input input::placeholder {
font-size: 26rpx;
}
.list-item-switch {
display: flex;
flex: 1;
justify-content: flex-end;
}
.list-item-btn {
display: flex;
justify-content: center;
margin-top: 20rpx;
}
.list-item-post {
display: flex;
color: #FFFFFF;
font-size: 36rpx;
width: 697rpx;
height: 73rpx;
background: linear-gradient(90deg, #F84A56, #FF9834);
border-radius: 37rpx;
justify-content: center;
align-items: center;
margin-top: 725rpx;
}
</style>

469
components/cartData.vue

@ -0,0 +1,469 @@
<template>
<view class="bg">
<uni-popup ref="popup" type="bottom" border-radius="10px 10px 0 0"
:safe-area="true" @change="popChange">
<view class="cart-container" v-if="showCart">
<view class="header-container">
<view class="select-area flex flex-items-center" @click.stop="selectAllGoods">
<view class="select-cycle" v-show="!selectAll"></view>
<view class="select-cycle selected" v-show="selectAll">
<image :src="showImg('/uploads/20250513/515c0699b759568421d6419fbb9befb7.png')">
</view>
<view style="padding-left: 26rpx;" >全选</view>
</view>
<view class="delete-area flex flex-items-center" @click.stop="clearAllGoods">
<image :src="showImg('/uploads/20250513/a98a9f06410e6a8d3c9d398cd4b7e9f0.png')"></image>
<view style="padding-left: 8rpx;" >清空</view>
</view>
</view>
<view class="content-container">
<view class="content-item" v-for="(item,i) in cartData" :key="i" :model="cartData">
<view class="flex flex-items-center" @tap.stop="setItemSelect(item)">
<view class="select-cycle" v-show="!item.isSelected"></view>
<view class="select-cycle selected" v-show="item.isSelected">
<image :src="showImg('/uploads/20250513/515c0699b759568421d6419fbb9befb7.png')">
</view>
<view style="padding-left: 26rpx;flex:1">
<view class="commodity box" >
<image class="img" :src="showImg(item.specifications_image)" mode="aspectFill"></image>
<view class="title goods-text-area">
<view class="commodity-info">
<view class="text-overflowRows" style="font-weight: bold;">{{ item.good_name }}</view>
<view class="text-overflowRows" style="font-size: 27rpx;color: #999999;padding-top: 10rpx;">{{ item.Specifications_name }}</view>
</view>
<view class="flex flex-between">
<view class="commodity-price">
<view style="font-size: 40rpx;">{{item.Specifications_money/100}}</view>
</view>
<view class="add-num-area">
<view :class="['ctrl',item.num>1?'':'disabled']" @click.stop="addBuyNum(item,-1,i)" >-</view>
<view>{{item.num}}</view>
<view :class="['ctrl']" @click.stop="addBuyNum(item,1,i)">+</view>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
<view style="width: 1rpx;height: 50rpx;"></view>
</view>
</view>
</uni-popup>
<slot name="content"></slot>
</view>
</template>
<script>
export default {
name: "cartData",
data() {
return {
cartData: [],
showCart: false,
selectAll: false,
canOpenpop: true,
allPrice: {allPrice:0, iNum:0, fNum:'00'},
}
},
props:{
paramData: {
type: Object,
},
},
mounted() {
this.canOpenpop = true
this.refreshData()
uni.$on("updateDataByConnect",this.getDataByConnect)
},
beforeUnmount () {
console.log('触发off')
uni.$off("updateDataByConnect",this.getDataByConnect)
},
beforeDestroy () {
console.log('触发off')
uni.$off("updateDataByConnect",this.getDataByConnect)
},
methods: {
calNum () {
let res = 0
this.cartData.forEach(v=>{
res += v.num
})
return res
},
closePopup() {
this.$refs.popup.close()
this.paramData.showCart = this.showCart
this.$emit('changeParamData', this.paramData)
},
openPop(){
this.$refs.popup.open()
this.paramData.showCart = this.showCart
this.$emit('changeParamData', this.paramData)
},
popChange (e) {
this.showCart = e.show
this.paramData.showCart = this.showCart
this.$emit('changeParamData', this.paramData)
},
getDataByConnect(data) {
if (data.msgType == "updateCartDataInfo") {
this.refreshData(data)
}
},
refreshData (data) {
let selectedData = []
try {
selectedData = JSON.parse(uni.getStorageSync('cartDataInfo'));
} catch(e) {
selectedData = []
}
if (Array.isArray(data) && data.length>0) {
this.cartData = data
this.setAllSelect()
} else {
this.Post({noForceLogin: true},'/api/shopping/getShoppingList').then(res => {
if (res && res.data) {
let tempData = new Array();
(res.data.merchant||[]).forEach(v=>{
tempData = tempData.concat(v.data || [])
})
this.cartData = tempData.map(v=>{return {...v, isSelected:selectedData.includes(v.specifications_id)}})
this.setAllSelect()
}
})
}
},
//
setItemSelect(item) {
item.isSelected = !item.isSelected
// uni.setStorageSync('cartDataInfo', JSON.stringify(this.cartData));
this.setAllSelect()
},
selectAllGoods(){
let goods = this.cartData
if (this.selectAll) {
goods.forEach(v => v.isSelected = false)
this.selectAll = false
} else {
goods.forEach(v => v.isSelected = true)
this.selectAll = true
}
this.setAllSelect()
},
clearAllGoods(){
let _this = this
Promise.all(this.cartData.map(v=>{
return _this.Post({s_id: v.id},'/api/shopping/delShopping')
})).finally(res =>{
this.cartData = []
this.setAllSelect()
})
},
setAllSelect() {
let goods = this.cartData
if(goods.length>0&& goods.every(v => v.isSelected)){
this.selectAll = true
} else {
this.selectAll = false
}
let selectedData = goods.filter(v=>v.isSelected).map(v=>v.specifications_id)
uni.setStorageSync('cartDataInfo', JSON.stringify(selectedData));
this.calAllPrice()
},
addBuyNum(item, num,index){
if (num == -1 && item.num == 1) {
this.Post({s_id: item.id},'/api/shopping/delShopping').then(res =>{
this.cartData.splice(index,1)
this.setAllSelect()
})
} else {
let numData = item.num + num
this.Post({s_id: item.id, num: numData},'/api/shopping/updateShopping').then(res =>{
item.num += num
this.setAllSelect()
})
}
},
calAllPrice () {
let selectedGoods = this.cartData.filter(v=>v.isSelected)
let totalPrice = 0
selectedGoods.forEach(v=>{
totalPrice += v.Specifications_money/100 * v.num
})
totalPrice = totalPrice.toFixed(2)
console.log(totalPrice)
this.allPrice = {allPrice: totalPrice, iNum: totalPrice.split('.')[0], fNum: totalPrice.split('.')[1]}
this.paramData.allPrice = totalPrice
this.paramData.iNum = this.allPrice.iNum
this.paramData.fNum = this.allPrice.fNum
this.paramData.num = this.calNum()
this.$emit('changeParamData', this.paramData)
},
goCartOrder () {
if(this.cartData.every(v => !v.isSelected)){
uni.showToast({
title:'请先选中要购买的商品',
icon:'none',
})
return
}
// let merchant_ids = this.cartData.filter(v=>v.isSelected).map(v=>v.merchant_id)
// if (merchant_ids.some(v=>v!=merchant_ids[0])) {
// uni.showToast({
// title:'',
// icon:'none',
// })
// return
// }
let orderData = this.cartData.filter(v=>v.isSelected).map(v=>{
return {
goodsInfo: {
image: v.specifications_image,
title:v.good_name,
merchant_name: v.merchant_name,
},
skuInfo: {
title:v.Specifications_name,
buyNum:v.num,
money: v.Specifications_money,
id: v.specifications_id,
},
}
})
let orderInfo = {
is_post: 1, //
goods: orderData,
post: 0
}
uni.setStorageSync('teChanOrder', JSON.stringify(orderInfo)); //
// uni.setStorageSync('teChanInfo', JSON.stringify(this.info)); //
uni.navigateTo({
url: '/subPackages/techan/order'
});
},
},
}
</script>
<style scoped lang="scss">
*{
box-sizing: border-box;
}
.bg{
width: 100%;
height: 100%;
}
.cart-container{
display: flex;
flex-direction: column;
background-color: white;
height: 933rpx;
border-radius: 20rpx 20rpx 0rpx 0rpx;
display: flex;
flex-direction: column;
position: absolute;
width: 100%;
bottom: 0;
z-index: 20;
.header-container{
display: flex;
align-items: center;
justify-content: space-between;
padding: 40rpx 26rpx;
.select-area{
font-family: PingFang SC;
font-weight: bold;
font-size: 37rpx;
color: #000000;
}
.delete-area{
font-family: PingFang SC;
font-weight: bold;
font-size: 27rpx;
color: #999999;
image{
width: 26rpx;
height: 26rpx;
}
}
}
}
.content-container{
flex: 1;
height: 10rpx;
overflow-y: auto;
padding:0 26rpx 26rpx;
.content-item{
margin-bottom: 48rpx;
}
.commodity {
display: flex;
.add-num-area{
display: flex;
justify-content: space-between;
align-items: center;
width: 160rpx;
image{
width: 49rpx;
height: 49rpx;
}
}
.goods-text-area{
display: flex;
flex-direction: column;
justify-content: space-between;
}
.commodity-price{
font-weight: 500;
font-size: 24rpx;
color: #F84A56;
display: flex;
align-items: baseline;
}
.commodity-info{
font-family: PingFangSC;
font-weight: 500;
font-size: 32rpx;
color: #2C2C2C;
}
// align-items: center;
.img {
width: 217rpx;
height: 179rpx;
border-radius: 13rpx;
flex-shrink: 0;
}
.title {
flex: 1;
margin-left: 20rpx;
font-size: 32rpx;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #000000;
.price-list {
display: flex;
margin-top: 18rpx;
align-items: center;
.price-r {
font-size: 32rpx;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #fc5109;
&:before {
content: '¥';
display: inline-block;
color: #fc5109;
font-size: 24rpx;
}
}
.price-g {
font-size: 24rpx;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #b5bcc9;
text-decoration: line-through;
margin-left: 10rpx;
}
}
}
.num-box {
display: flex;
align-items: center;
margin-left: 20rpx;
width: 160rpx;
justify-content: space-between;
.num {
text-align: center;
width: 50rpx;
}
.ctrl {
width: 46rpx;
height: 46rpx;
}
}
}
}
.select-cycle{
width: 40rpx;
height: 40rpx;
border-radius: 50%;
border: 1px solid #999999;
image{
width: 100%;
height: 100%;
}
}
.select-cycle.selected {
border: none;
image{
width: 40rpx;
height: 40rpx;
}
}
.ctrl {
width: 47rpx;
height: 47rpx;
background: #74A5AA;
border-radius: 50%;
font-family: PingFang SC;
font-weight: 400;
font-size: 34rpx;
color: #FFFFFF;
line-height: 47rpx;
text-align: center;
}
.ctrl.disabled{
background: #E8E8E8;
color: #999999;
}
</style>

265
components/contactAdd.vue

@ -0,0 +1,265 @@
<template>
<view class="bg">
<view class="list-forms">
<view class="list-item">
<view class="list-item-title">姓名</view>
<view class="list-item-input"><input type="text" v-model="username" placeholder="请输入姓名" /></view>
</view>
<view class="list-item">
<view class="list-item-title">证件类型</view>
<view class="list-item-input">
<picker mode="selector" :range="idcardTypeList" range-key="title" @change="changeIdType">
<input type="text" readonly style="font-size: 35rpx;position: relative;"
v-model="idcardTypeValue" disabled="true" placeholder="请选择"/>
</picker>
</view>
</view>
<view class="list-item">
<view class="list-item-title">证件号</view>
<view class="list-item-input"><input type="text" v-model="idNumber" placeholder="请输入正确的证件号" /></view>
</view>
<view class="list-item">
<view class="list-item-title">联系电话</view>
<view class="list-item-input"><input type="number" v-model="mobile" placeholder="请输入手机号" maxlength="11" /></view>
</view>
<view class="list-item">
<view class="list-item-title">设为默认</view>
<view class="list-item-switch">
<switch :checked="idDefault" @change="switchChange" color="#515150"/>
</view>
</view>
</view>
<!-- <view class="btn" @click="submit">保存</view> -->
</view>
</template>
<script>
export default {
data() {
return {
username: '',
mobile: '',
idNumber: '',
idcardTypeValue: '身份证',
show: false,
idcardTypeList: [],
document_type: 'DAM01001',
id: '',
type: '',
idDefault: false,
}
},
methods: {
init (data) {
this.username = ''
this.mobile= ''
this.idNumber= ''
this.idcardTypeValue= '身份证'
this.show= false
this.idcardTypeList= []
this.document_type= 'DAM01001'
this.id= ''
this.type=''
this.idDefault= false
this.type = 'add'
this.getIdcardTypeList()
if (data && data.id) {
this.id = data.id
this.type = 'edit'
this.getDetail()
}
},
switchChange(e){
this.idDefault = e.detail.value
},
getDetail() {
this.Post({
id: this.id
}, '/api/user/contactDetail').then(res => {
if (res.code === 1) {
res = res.data
if (res && res.id > 0) {
this.username = res.name
this.mobile = res.tel
this.idNumber = res.id_number
this.idDefault = res.is_default == 1 ? true : false
this.document_type = res.document_type
this.idcardTypeValue = res.document_type_text
}
}
})
},
//
getIdcardTypeList() {
this.Post({}, '/api/user/getIdcardTypeList').then(res => {
this.idcardTypeList = res.data
})
},
//
changeIdType(e) {
this.idcardTypeValue = this.idcardTypeList[e.detail.value].title
this.document_type = this.idcardTypeList[e.detail.value].document_type
},
//
async submit() {
this.username = this.username.trim()
this.mobile = this.mobile.trim()
this.idNumber = this.idNumber.trim()
if (this.username.length < 1) {
uni.showToast({
title: '请输入姓名',
icon: 'none'
})
return
}
if (!this.IsTel(this.mobile)) {
uni.showToast({
title: '请输入正确的手机号',
icon: 'none'
})
return
}
if (this.idcardTypeValue == '身份证') {
if (!this.idCardNumber(this.idNumber)) {
uni.showToast({
title: '请输入正确的身份证',
icon: 'none'
})
return
}
} else if (this.idcardTypeValue == '护照') {
if (!this.passportValid(this.idNumber)) {
uni.showToast({
title: '请输入正确的护照',
icon: 'none'
})
return
}
} else if (this.idcardTypeValue == '台胞证') {
if (!this.taiwanValid(this.idNumber)) {
uni.showToast({
title: '请输入正确的台胞证',
icon: 'none'
})
return
}
} else if (this.idcardTypeValue == '港澳通行证') {
if (!this.gangaoValid(this.idNumber)) {
uni.showToast({
title: '请输入正确的港澳通行证',
icon: 'none'
})
return
}
} else if (this.idcardTypeValue == '外国人永久居留证') {
if (!this.foreignerValid(this.idNumber)) {
uni.showToast({
title: '请输入正确的外国人永久居留证',
icon: 'none'
})
return
}
} else if (this.idcardTypeValue == '军官证') {
if (!this.officerValid(this.idNumber)) {
uni.showToast({
title: '请输入正确的军官证',
icon: 'none'
})
return
}
}
let res = await this.Post({
id: this.type == 'edit'?this.id:'',
id_number: this.idNumber,
name: this.username,
tel: this.mobile,
document_type: this.document_type,
is_default: this.idDefault ? '1' : '0',
}, this.type=='edit'?'/api/user/editContact':'/api/user/addContact')
if (res.code == 1) {
uni.showModal({
title: '提示',
content: '操作成功',
showCancel: false,
})
}else{
uni.showModal({
title: '提示',
content: res.msg,
showCancel: false,
})
}
return {...res,data: {id: this.id}}
}
}
}
</script>
<style scoped lang="scss">
.bg {
width: 100%;
height: 100%;
position: relative;
}
.list-forms {
padding: 0 33rpx;
.list-item {
display: flex;
border-bottom: 1rpx solid #D8D8D8;
padding: 30rpx 0;
height: 60rpx;
box-sizing: content-box;
align-items: center;
.list-item-title {
font-size: 34rpx;
margin-right: 20rpx;
width: 200rpx;
}
.list-item-input {
flex: 1;
input {
width: 100%;
height: 100%;
padding: 0;
margin: 0;
border: 0;
background-color: transparent;
line-height: 34rpx;
font-size: 34rpx;
text-align: left;
}
input::placeholder {
font-size: 26rpx;
}
}
}
}
.btn {
width: 697rpx;
height: 80rpx;
background: #515150;
border-radius: 40rpx;
text-align: center;
line-height: 80rpx;
font-size: 36rpx;
font-family: PingFang SC;
font-weight: 500;
color: #FFFFFF;
position: absolute;
bottom: 53rpx;
left: 27rpx;
}
</style>

260
components/recommend.vue

@ -0,0 +1,260 @@
<template>
<view class="bg" :style="{backgroundColor:bgColor || '#F5F5F5'}">
<view class="titles">为您推荐</view>
<view class="goodBox" v-if="type == 0 && (list && list.length>0)">
<view>
<view class="goodItem" v-for="(item,index) in leftList" :key="item.id" @click="gotoDetail(item.id)">
<image :src="showImg(item.image)" mode="widthFix"></image>
<view class="goodContent">
<view class="title">
{{item.title}}
</view>
<view class="tags" v-if="item.label">
<view v-for="(tagItem,tagIndex) in item.label.split(',').slice(0,2)" :key="tagIndex" class="text-overflow">
{{tagItem}}
</view>
</view>
<view class="price">{{showPrice(item.price)}}</view>
</view>
</view>
</view>
<view>
<view class="goodItem" v-for="(item,index) in rightList" :key="item.id" @click="gotoDetail(item.id)">
<image :src="showImg(item.image)" mode="widthFix"></image>
<view class="goodContent">
<view class="title">
{{item.title}}
</view>
<view class="tags" v-if="item.label">
<view v-for="(tagItem,tagIndex) in item.label.split(',').slice(0,2)" :key="tagIndex" class="text-overflow">
{{tagItem}}
</view>
</view>
<view class="price">{{showPrice(item.price)}}</view>
</view>
</view>
</view>
</view>
<view class="goodBox" v-if="type == 1 && (list && list.length>0)">
<view>
<view class="goodItem" v-for="(item,index) in leftList" :key="item.id" @click="gotoDetail(item.goods.id)">
<image :src="showImg(item.goods.image)" mode="widthFix"></image>
<view class="goodContent">
<view class="title">
{{item.goods.title}}
</view>
<view class="tags" v-if="item.goods.label">
<view v-for="(tagItem,tagIndex) in item.goods.goods_new_tag.split(',').slice(0,2)" :key="tagIndex" class="text-overflow">
{{tagItem}}
</view>
</view>
<view class="price">{{showPrice(item.goods.price)}}</view>
</view>
</view>
</view>
<view>
<view class="goodItem" v-for="(item,index) in rightList" :key="item.id" @click="gotoDetail(item.goods.id)">
<image :src="showImg(item.goods.image)" mode="widthFix"></image>
<view class="goodContent">
<view class="title">
{{item.goods.title}}
</view>
<view class="tags" v-if="item.goods.label">
<view v-for="(tagItem,tagIndex) in item.goods.goods_new_tag.split(',').slice(0,2)" :key="tagIndex" class="text-overflow">
{{tagItem}}
</view>
</view>
<view class="price">{{showPrice(item.goods.price)}}</view>
</view>
</view>
</view>
</view>
<view class="finished-text" v-if="finished">没有更多数据了</view>
</view>
</template>
<script>
import Base from "@/components/Base";
export default{
extends: Base,
// type:0,scenic_type_id:1,511宿12;type:1,,tag_id:6=,7=
props: ['scenic_type_id','tag_id','type','bgColor','exclude_id'],
data(){
return {
list: [],
leftList: [],
rightList: [],
finished: false
}
},
mounted() {
this.getList()
},
methods: {
getList(){
if(this.type == 0){
this.Post({
method: 'POST',
scenic_type_id: this.scenic_type_id,
exclude_id: this.exclude_id,
offset: this.list.length,
limit: 6
},'/api/scenic/getScenicByType').then(res => {
res.data.forEach((item,index)=>{
if(index%2 == 0){
this.leftList.push(item)
}else{
this.rightList.push(item)
}
})
this.list = [...this.list, ...res.data];
if (res.data.length < 6) {
this.finished = true
}
})
}else{
this.Post({
method: 'POST',
type_id: 5,
exclude_id: this.exclude_id,
offset: this.list.length,
limit: 6,
tag_id: this.tag_id
},'/api/tag/getGoodsByTagId').then(res => {
res.data.forEach((item,index)=>{
if(index%2 == 0){
this.leftList.push(item)
}else{
this.rightList.push(item)
}
})
this.list = [...this.list, ...res.data];
if (res.data.length < 6) {
this.finished = true
}
})
}
},
gotoDetail(id){
if(this.scenic_type_id == 1){
uni.navigateTo({
url: '/subPackages/details/scenicDetails?id='+id
})
}else if(this.scenic_type_id == 5){
uni.navigateTo({
url: '/subPackages1/hotel/hotelDetails?id='+id
})
}else if(this.scenic_type_id == 11){
}else if(this.scenic_type_id == 12){
uni.navigateTo({
url: '/subPackages/details/eateryDetails?id='+id
})
}else if(this.tag_id == 6){
uni.navigateTo({
url: '/subPackages/details/goodDetails?type=1&id='+id
})
}else if(this.tag_id == 7){
uni.navigateTo({
url: '/subPackages/details/goodDetails?type=0&id='+id
})
}
}
},
onReachBottom() {
setTimeout(() => {
if (!this.finished) this.getList()
},1000)
}
}
</script>
<style scoped lang="scss">
.bg{
padding: 0 27rpx;
box-sizing: border-box;
}
.titles{
font-size: 36rpx;
font-family: PingFang SC;
font-weight: 500;
color: #000000;
margin-bottom: 30rpx;
}
.goodBox{
display: flex;
justify-content: space-between;
.goodItem{
margin-bottom: 28rpx;
image{
width: 340rpx;
border-radius: 20rpx 20rpx 0px 0px;
box-shadow: 0px 0px 41rpx 0px rgba(51,51,51,0.1);
display: block;
}
.goodContent{
width: 340rpx;
height: 150rpx;
background: #FFFFFF;
border-radius: 0 0 20rpx 20rpx;
padding: 13rpx;
box-sizing: border-box;
box-shadow: 0px 0px 20rpx 0px rgba(51,51,51,0.1);
display: flex;
flex-direction: column;
justify-content: space-between;
.title{
width: 286rpx;
font-size: 27rpx;
font-family: PingFang SC;
font-weight: 500;
color: #000000;
overflow-x: hidden;
overflow-y: inherit;
white-space: nowrap;
}
.tags{
display: flex;
view{
max-width: 115rpx;
padding: 0 10rpx;
height: 30rpx;
border: 1rpx solid #00D7ED;
border-radius: 7rpx;
font-size: 23rpx;
font-family: PingFang SC;
font-weight: 400;
color: #00D7ED;
text-align: center;
line-height: 30rpx;
margin-right: 12rpx;
}
}
.price{
font-size: 32rpx;
font-family: PingFang SC;
font-weight: 500;
color: #FF2D3B;
text-align: right;
}
.price::before{
content: "¥";
font-size: 20rpx;
}
.price::after{
content: "起";
font-size: 20rpx;
color: #8D8D8D;
}
}
}
}
</style>

12
components/recommend.vue.rej

@ -0,0 +1,12 @@
diff a/components/recommend.vue b/components/recommend.vue (rejected hunks)
@@ -142,9 +142,7 @@
url: '/subPackages/details/scenicDetails?id='+id
})
}else if(this.scenic_type_id == 5){
- uni.navigateTo({
- url: '/subPackages1/hotel/hotelDetails?id='+id
- })
+
}else if(this.scenic_type_id == 11){
}else if(this.scenic_type_id == 12){

22
main.js

@ -1,18 +1,32 @@
import App from './App'
// #ifndef VUE3 // #ifndef VUE3
import Vue from 'vue' import Vue from 'vue'
import './uni.promisify.adaptor' import App from './App'
import store from './store'
import '@/static/js/request.js'
import '@/static/js/CommonFunction.js'
import {myMixins} from '@/mixins/myMixins.js'
Vue.mixin(myMixins)
Vue.config.productionTip = false Vue.config.productionTip = false
// 去除生产环境console
if (uni.getSystemInfoSync().platform !== "devtools") {
console.log = () => {}
}
App.mpType = 'app' App.mpType = 'app'
const app = new Vue({ const app = new Vue({
...App store: store,
...App
}) })
app.$mount() app.$mount()
// #endif // #endif
// #ifdef VUE3 // #ifdef VUE3
import { createSSRApp } from 'vue' import { createSSRApp } from 'vue'
import App from './App.vue'
export function createApp() { export function createApp() {
const app = createSSRApp(App) const app = createSSRApp(App)
return { return {

42
mixins/myMixins.js

@ -0,0 +1,42 @@
export const myMixins ={
onLoad(option) {
console.log('option',option)
if(option && option.wechat_qrcode){
uni.setStorageSync('wechat_qrcode',option.wechat_qrcode)
}
// 分享
uni.showShareMenu({
withShareTicket: true,
//设置下方的Menus菜单,才能够让发送给朋友与分享到朋友圈两个按钮可以点击
menus: ["shareAppMessage", "shareTimeline"]
})
},
// 分享到朋友圈
onShareTimeline() {
return {
title: 'CGC-ICH',
type: 0,
summary: "",
imageUrl: "https://cgc.js-dyyj.com/uploads/20250619/172a730c88bd8894dee1e64c703795eb.jpg"
}
},
onShareAppMessage() {
const pages = getCurrentPages(); // 获取加载的页面
const view = pages[pages.length - 1]; // 获取当前页面的对象
let url = '';
if (view.options.url) {
url = view.options.url;
} else {
url = uni.getStorageSync('webUrl');
}
console.log(111,url,`${view.route}?url=${url}`)
return {
title: 'CGC-ICH', // 分享的名称
path: `${view.route}?url=${url}`, // 将 url 作为参数传递
imageUrl: "https://cgc.js-dyyj.com/uploads/20250619/172a730c88bd8894dee1e64c703795eb.jpg",
mpId: 'wx9d68934300b1fe90' // 此处配置微信小程序的 AppId
};
}
}

18
package-lock.json

@ -0,0 +1,18 @@
{
"name": "cgc_wechat",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"moment": {
"version": "2.30.1",
"resolved": "https://registry.npmmirror.com/moment/-/moment-2.30.1.tgz",
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how=="
},
"ydui-district": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/ydui-district/-/ydui-district-1.1.0.tgz",
"integrity": "sha512-MBhvfaR5Gkn6MUmEnrH1A7IFB5igALuDgtIF+gz3dRwNwW9+KOmih7z+xZFfGluMsEbWaT7C3lWOckYsLZQnFg=="
}
}
}

20
package.json

@ -0,0 +1,20 @@
{
"name": "cgc_wechat",
"version": "1.0.0",
"description": "",
"main": "main.js",
"dependencies": {
"moment": "^2.30.1",
"ydui-district": "^1.1.0"
},
"devDependencies": {},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "http://123.60.98.226:3000/chenkainan/cgc_WeChat.git"
},
"author": "",
"license": "ISC"
}

166
pages.json

@ -1,12 +1,176 @@
{ {
"pages": [ //pageshttps://uniapp.dcloud.io/collocation/pages "pages": [ //pageshttps://uniapp.dcloud.io/collocation/pages
{
"path": "pages/stratIndex",
"style": {
"navigationStyle": "custom"
}
},
{ {
"path": "pages/index/index", "path": "pages/index/index",
"style": { "style": {
"navigationBarTitleText": "uni-app" "navigationStyle": "custom"
// "navigationBarTitleText": "CGC-ICH"
}
},
{
"path": "pages/user/user",
"style": {
"navigationBarTitleText": "我的",
"navigationStyle": "custom"
}
},
{
"path": "pages/login/login",
"style": {
// "navigationBarTitleText": "CGC-ICH"
"navigationStyle": "custom"
} }
} }
], ],
"subPackages": [{
"root": "subPackages",
"pages": [
{
"path": "index",
"style": {
"navigationBarTitleText": "CGC-ICH"
}
},
{
"path" : "user/privacy",
"style" : {
"navigationBarTitleText" : "详情"
}
},
{
"path" : "user/privacyInfo",
"style" : {
"navigationBarTitleText" : "详情"
}
},
{
"path" : "order/trades",
"style" : {
"navigationBarTitleText" : "全部订单"
}
},
{
"path" : "order/detail",
"style" : {
"navigationBarTitleText" : "订单详情",
"navigationStyle": "custom"
}
},
{
"path" : "user/travelerList",
"style" : {
"navigationBarTitleText" : "收货地址"
}
},
{
"path" : "user/myAddressAdd",
"style" : {
"navigationBarTitleText" : "收货地址"
}
},
{
"path" : "user/gwc",
"style" : {
"navigationBarTitleText" : "购物车"
}
},
{
"path" : "order/gwcOrder",
"style" : {
"navigationBarTitleText" : "下单"
}
},
{
"path": "letter/index",
"style": {
"navigationBarTitleText": "非遗手札"
}
},
{
"path": "letter/detail",
"style": {
"navigationBarTitleText": "文章详情"
}
},
{
"path": "techan/index",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom"
}
},
{
"path": "techan/detail",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "techan/order",
"style": {
"navigationBarTitleText": "确认订单"
}
},
{
"path": "shiguang/index",
"style": {
"navigationBarTitleText": "非遗拾光"
}
},
{
"path": "user/profile",
"style": {
"navigationBarTitleText": "个人信息"
}
},
{
"path" : "user/changeNickname",
"style" : {
"navigationBarTitleText" : "修改昵称"
}
},
{
"path" : "webPage/webPage",
"style" : {
"navigationBarTitleText" : ""
}
},
{
"path" : "video/video",
"style" : {
"navigationBarTitleText" : ""
}
}
]
}],
"tabBar": {
"color": "#333333",
"selectedColor": "#6CA5AA",
"borderStyle": "black",
"backgroundColor": "#ffffff",
"fontSize": "24rpx",
"height": "100rpx",
"iconWidth": "40rpx",
"list": [{
"pagePath": "pages/index/index",
"iconPath": "/static/images/home.png",
"selectedIconPath": "/static/images/selectHome.png",
"text": "首页"
},
{
"pagePath": "pages/user/user",
"iconPath": "/static/images/mine.png",
"selectedIconPath": "/static/images/selectMine.png",
"text": "我的"
}
]
},
"globalStyle": { "globalStyle": {
"navigationBarTextStyle": "black", "navigationBarTextStyle": "black",
"navigationBarTitleText": "uni-app", "navigationBarTitleText": "uni-app",

650
pages/index/index.vue

@ -1,9 +1,117 @@
<template> <template>
<view class="content"> <view class="content" style="overflow-x: auto;">
<image class="logo" src="/static/logo.png"></image> <view class="search-header" :style="{'height': height+'px','padding-top':statusBarHeight+'px'}">
<view class="text-area"> <view class="title">CGC-ICH</view>
<text class="title">{{title}}</text> <view class="subtitle">大运河非物质文化遗产</view>
</view> </view>
<div :style="{'height':height+'px','flex-shrink':0}"></div>
<!-- 头部banner -->
<view class="top-box">
<!-- 搜索 -->
<!-- <view class="search-box">
<image src="https://static.ticket.sz-trip.com/cgc/images/index/search.png" mode=""></image>
<span>|</span>
搜一搜您想要的
</view> -->
<swiper class="top-banner" :circular="true" :interval="6000"
:duration="800" :indicator-dots="false" :autoplay="true" v-if="topBanner && topBanner.length > 0">
<swiper-item v-for="(item, index) in topBanner" :key="index" @click.stop="gotoUrlNew(item)">
<image class="top-banner" :src="showImg(item.head_img)" mode="aspectFill" lazy-load="true"></image>
</swiper-item>
</swiper>
</view>
<!-- 金刚区 -->
<view class="menu-box flex-between">
<view class="menu-left flex-around">
<view class="" v-for="(item,index) in menuList" :key="index" @click="gotoPath(item.path)">
<image :src="showImg(`https://static.ticket.sz-trip.com/cgc/images/indexs/${index + 1}.png`)" class="topImg" mode="heightFix"></image>
<image :src="showImg(`https://static.ticket.sz-trip.com/cgc/images/indexs/${index + 1}s.png`)" class="titleImg" mode="heightFix"></image>
</view>
</view>
<!-- <view class="menu-right flex-around">
<swiper class="menu-right" :circular="true" :interval="6000"
:duration="800" :indicator-dots="false" :autoplay="true" v-if="smallBanner">
<swiper-item v-for="(item, index) in smallBanner" :key="index" @click.stop="gotoUrlNew(item)">
<image class="menu-right" :src="showImg(item.head_img)" mode="aspectFill" lazy-load="true"></image>
</swiper-item>
</swiper>
</view> -->
</view>
<!-- 非遗手札 -->
<view v-if="handwrittenList && handwrittenList.length > 0">
<view class="title-box flex-between">
<image src="https://cgc.js-dyyj.com/uploads/20250619/d0cfd5cd7253d8ce6a747842a40be020.png" mode="heightFix"></image>
<image src="https://static.ticket.sz-trip.com/cgc/images/index/more.png" mode="" @click="gotoPath('/subPackages/letter/index')"></image>
</view>
<view class="handwritten-box flex-between">
<image :src="showImg(item.image)" mode="aspectFill" v-for="(item,index) in handwrittenList" :key="index"
@click="viewDetail(item)"></image>
</view>
</view>
<!-- 非遗时光 -->
<view v-if="shiguang && shiguang.image">
<view class="title-box flex-between">
<image src="https://cgc.js-dyyj.com/uploads/20250619/b10ed22a2e5b2187d88352869575fb16.png" mode="heightFix"></image>
<image src="https://static.ticket.sz-trip.com/cgc/images/index/more.png" mode="" @click="gotoPath('/subPackages/shiguang/index')"></image>
</view>
<view class="time-box" :style="{backgroundImage: 'url('+showImg(shiguang.image)+')'}" @click="gotoVideo(shiguang)">
<image src="https://static.ticket.sz-trip.com/cgc/images/index/play.png" class="play"></image>
</view>
</view>
<!-- 底部推荐 -->
<view class="hot-type flex-around">
<view v-for="(item,index) in hotType" :key="index" :class="{'hot-active': index == hotIndex}" @click="changeType(index)">
{{item.title}}
</view>
</view>
<view class="hot-box" v-if="hotIndex==0">
<view class="hot-column" v-for="(column, index) in 2" :key="index">
<view v-if="indexs%2==1&&column==0" v-for="(item,indexs) in hotList" :key="indexs" class="hot-item" @click="gotoHotDetail(item)">
<view class="image-container">
<image :src="showImg(item.goods.image)" mode="widthFix" class="hot-img" lazy-load="true"></image>
</view>
<view class="hot-content">
<view class="title text-overflowRows">{{item.title || item.goods.title}}</view>
</view>
</view>
<view v-if="indexs%2==0&&column==1" v-for="(item,indexs) in hotList" :key="indexs" class="hot-item" @click="gotoHotDetail(item)">
<view class="image-container">
<image :src="showImg(item.goods.image)" mode="widthFix" class="hot-img" lazy-load="true"></image>
</view>
<view class="hot-content">
<view class="title text-overflowRows">{{item.title || item.goods.title}}</view>
</view>
</view>
</view>
</view>
<view class="hot-box" v-else>
<view class="hot-column" v-for="(column, index) in 2" :key="index">
<view v-if="indexs%2==1&&column==0" v-for="(item,indexs) in hotList1" :key="indexs" class="hot-item" @click="gotoHotDetail(item)">
<view class="image-container">
<image :src="showImg(item.goods.image)" mode="widthFix" class="hot-img" lazy-load="true"></image>
</view>
<view class="hot-content">
<view class="title text-overflowRows">{{item.title || item.goods.title}}</view>
</view>
</view>
<view v-if="indexs%2==0&&column==1" v-for="(item,indexs) in hotList1" :key="indexs" class="hot-item" @click="gotoHotDetail(item)">
<view class="image-container">
<image :src="showImg(item.goods.image)" mode="widthFix" class="hot-img" lazy-load="true"></image>
</view>
<view class="hot-content">
<view class="title text-overflowRows">{{item.title || item.goods.title}}</view>
</view>
</view>
</view>
</view>
</view> </view>
</template> </template>
@ -11,42 +119,528 @@
export default { export default {
data() { data() {
return { return {
title: 'Hello' //
height: 0,
statusBarHeight: 0,
topBanner: [],
smallBanner: [],
menuList: [
{
path: '/subPackages/techan/index?idIndex=0',
image: '',
},
{
path: '/subPackages/techan/index?idIndex=1',
image: '',
},
{
path: '/subPackages/techan/index?idIndex=2',
image: '',
},
{
path: '/subPackages/techan/index?idIndex=3',
image: '',
},
{
path: '/subPackages/techan/index?idIndex=4',
image: '',
},
{
path: '/subPackages/techan/index?idIndex=5',
image: '',
}
],
handwrittenList: [],
shiguang: {},
hotType: [
{
title: '好物推荐',
id: '41'
},
{
title: '苏州非遗',
id: '41'
}
],
hotIndex: 0,
hotList: [],
hotList1: [],
isLoading: false,
limit: 4,
} }
}, },
onLoad() { onLoad() {
},
onReady() {
//
// if(!uni.getStorageSync('location')) {
// this.getLocation()
// }
this.initRectInfo()
this.getList()
this.initHotList()
this.changeType(0)
},
onReachBottom() {
// setTimeout(() => {
// // if (!this.isLoading) this.getHotList();
// this.getHotList()
// }, 1000);
}, },
methods: { methods: {
viewDetail(item) {
if (item.url) {
uni.navigateTo({
url:"/subPackages/webPage/webPage?url="+encodeURIComponent(item.url)
})
return
}
uni.navigateTo({
url:'/subPackages/letter/detail?id='+item.id
})
},
initRectInfo () {
const sysInfo = uni.getSystemInfoSync()
this.statusBarHeight = sysInfo.statusBarHeight
//
this.height = sysInfo.statusBarHeight + 40
},
getList() {
//
this.Post({
type_id: 3,
position: 1,
}, '/api/adv/getAdv').then(res => {
if(res.data) {
this.topBanner = res.data;
}
});
//
this.Post({
type_id: 3,
position: 2,
}, '/api/adv/getAdv').then(res => {
if(res.data) {
this.smallBanner = res.data;
this.screenPng = res.data[0]
}
});
//
this.Post({
type_id: 1,
offset: 0,
limit: 1
}, '/api/article/getArticleByType').then(res => {
if(res.data) {
this.handwrittenList = res.data
}
})
//
this.Post({
type_id: 2,
offset: 0,
limit: 1
}, '/api/article/getArticleByType').then(res => {
if(res.data) {
this.shiguang = res.data[0]
}
})
},
// 234
gotoUrlNew(item) {
console.log(item);
let that = this;
let url = '';
switch (item.jump_type) {
case 0:
break;
case 2:
uni.navigateTo({
url: item.tdata
});
break;
case 3:
uni.navigateTo({
url: '/subPackages/webPage/webPage?url=' + item.tdata
});
break;
case 4:
uni.navigateToMiniProgram({
appId: item.tdata.appid, // appid
path: item.tdata.page, //
envVersion: 'release',
success: res => {
//
console.log('打开成功', res);
},
fail: err => {
console.log(err);
}
});
break;
default:
break;
}
},
//
// getColumnItems(columnIndex) {
// const columnItems = [];
// this.hotList.forEach((item, index) => {
// // switch (this.hotIndex) {
// // case 1:
// // item.src = this.showImg(item.image);
// // break;
// // default:
// // item.src = this.showImg(item.goods.image);
// // }
// item.src = this.showImg(item.goods.image);
// if (index % 2 === columnIndex) {
// columnItems.push(item);
// }
// });
// return columnItems;
// },
//
changeType(index) {
this.hotIndex = index
// this.isLoading = false
// if (index==0) {
// this.hotList = this.hotList1
// }
// this.getHotList()
},
initHotList () {
this.hotList = []
this.hotList1 = []
this.Post({
tag_id: 51,
offset: this.hotList.length,
limit: 100,
},'/api/tag/getGoodsByTagId').then(res => {
if(res.data.length < this.limit) this.isLoading = true
this.hotList = this.hotList.concat(res.data)
})
this.Post({
tag_id: 52,
offset: this.hotList1.length,
limit: 100,
},'/api/tag/getGoodsByTagId').then(res => {
if(res.data.length < this.limit) this.isLoading = true
this.hotList1 = this.hotList1.concat(res.data)
})
},
getHotList() {
if(this.hotIndex == 1) {
// this.Post({
// type_id: 3,
// offset: this.hotList.length,
// limit: this.limit
// },'/api/Article/getArticleByType').then(res => {
// if(res) {
// if(res.data.length < this.limit) this.isLoading = true
// this.hotList = this.hotList.concat(res.data)
// }
// })
this.Post({
tag_id: 52,
offset: this.hotList1.length,
limit: this.limit,
},'/api/tag/getGoodsByTagId').then(res => {
if(res.data.length < this.limit) this.isLoading = true
this.hotList1 = this.hotList.concat(res.data)
})
}else {
this.Post({
tag_id: 51,
offset: this.hotList.length,
limit: this.limit,
},'/api/tag/getGoodsByTagId').then(res => {
if(res.data.length < this.limit) this.isLoading = true
this.hotList = this.hotList.concat(res.data)
})
}
},
gotoVideo(item) {
uni.navigateTo({
url: '/subPackages/video/video?item=' + encodeURIComponent(JSON.stringify(item))
})
},
gotoHotDetail(item) {
uni.navigateTo({
url: '/subPackages/techan/detail?id=' + item.goods_id
})
// if(this.hotIndex == 1) {
// if(item.genre == 'video') {
// //
// this.gotoVideo(item)
// }else {
// //
// uni.navigateTo({
// url: '/subPackages/letter/detail?id=' + item.id
// })
// }
// }else {
// uni.navigateTo({
// url: '/subPackages/techan/detail?id=' + item.goods_id
// })
// }
}
} }
} }
</script> </script>
<style> <style lang="scss" scoped>
.search-header{
width: 100%;
position: fixed;
top: 0;
left: 0;
right: 0;
background: white;
text-align: center;
z-index:99;
.title{
font-size: 30rpx
}
.subtitle{
font-size: 22rpx;
}
}
.content { .content {
display: flex; min-height: 100vh;
flex-direction: column; background: #F9F0EA;
align-items: center; padding: 21rpx 0 50rpx;
justify-content: center;
} }
.logo { .top-box {
height: 200rpx; width: 684rpx;
width: 200rpx; height: 451rpx;
margin-top: 200rpx; border-radius: 30rpx;
margin-left: auto; overflow: hidden;
margin-right: auto; position: relative;
margin-bottom: 50rpx; margin: auto;
.top-banner {
width: 100%;
height: 100%;
}
.search-box {
width: 632rpx;
height: 40rpx;
background: #FFFFFF;
border-radius: 20rpx;
position: absolute;
z-index: 1000;
top: 31rpx;
left: 26rpx;
display: flex;
align-items: center;
font-weight: 500;
font-size: 24rpx;
color: #7B7C7D;
padding-left: 21rpx;
image {
width: 29rpx;
height: 28rpx;
}
span {
padding: 0 15rpx;
}
}
} }
.text-area { .menu-box {
display: flex; width: 100%;
justify-content: center; padding: 54rpx 33rpx 0;
overflow: hidden;
box-sizing: border-box;
.menu-left {
// flex-wrap: wrap;
width: 100%;
display: flex;
justify-content: space-between;
&>view {
flex: 1;
flex-shrink: 0;
display: flex;
flex-direction: column;
align-items: center;
}
.topImg {
height: 84rpx;
}
.titleImg {
height: 21rpx;
margin-top: 19rpx;
}
}
.menu-right {
width: 254rpx;
height: 323rpx;
border-radius: 20rpx;
}
} }
.title { .title-box {
font-size: 36rpx; margin: 60rpx 44rpx 21rpx 50rpx;
color: #8f8f94; align-items: flex-end;
&>image {
width: 350rpx;
height: 32rpx;
}
&>image:last-child {
width: 59rpx;
height: 20rpx;
}
}
.handwritten-box {
margin: 0 46rpx;
flex-wrap: wrap;
image {
width: 100%;
height: 181rpx;
border-radius: 10rpx;
}
// &>image:nth-child(n+3) {
// margin-top: 13rpx;
// }
}
.time-box {
background-size: 100% 100%;
width: 657rpx;
height: 370rpx;
border-radius: 30rpx;
margin: 23rpx auto 0;
position: relative;
.play {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
width: 90rpx;
height: 90rpx;
}
}
.hot-type {
height: 122rpx;
font-weight: bold;
font-size: 32rpx;
color: #B5B5B6;
&>view {
position: relative;
}
&>view::after {
content: "";
position: absolute;
left: 0;
right: 0;
bottom: -10rpx;
background-color: #B5B5B6;
margin: 0 auto;
width: 32rpx;
height: 4rpx;
}
.hot-active {
color: #000000;
}
.hot-active::after {
background-color: #000000;
}
}
.hot-box {
margin: 0 48rpx;
display: flex;
justify-content: space-between;
.hot-column {
width: 319rpx;
display: flex;
flex-direction: column;
}
.hot-item {
width: 319rpx;
background: #FFFFFF;
border-radius: 20rpx;
overflow: hidden;
margin-bottom: 13rpx;
.image-container {
position: relative;
.hot-img {
width: 319rpx;
}
.play-img {
width: 66.67rpx;
height: 66.67rpx;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
.hot-content {
padding: 15rpx;
.title {
font-weight: bold;
font-size: 26rpx;
color: #000000;
}
.subtitle {
margin-top: 20rpx;
font-weight: 400;
font-size: 24rpx;
color: #000000;
display: flex;
align-items: center;
image {
width: 48rpx;
height: 48rpx;
border-radius: 50%;
display: block;
margin-right: 15rpx;
}
}
}
}
} }
</style> </style>

258
pages/login/login.vue

@ -0,0 +1,258 @@
<template>
<view class="content" style="overflow-x: auto;">
<view class="search-header" :style="{'height': height+'px','padding-top':statusBarHeight+'px'}">
<view class="title">CGC-ICH</view>
<view class="subtitle">大运河非物质文化遗产</view>
</view>
<view :style="{'height':height+'px','flex-shrink':0}"></view>
<div style="padding-top: 88rpx;">
<div class="login-tip">CGC-ICH 申请获得</div>
<div class="login-tip2">以下权限</div>
<div class="login-tip-box">
<text>获得你的公开信息昵称头像地区及性别</text>
</div>
<div class="btn-box">
<button bindtap="cancel" type="default" @click="redirectIndex">取消</button>
<button type="primary" @click="getUserInfo">同意</button>
</div>
<div class="flex-center article-box">
<radio-group @change="toggleAgreement">
<radio value="1" :checked="isAgreed" style="transform:scale(0.7)"></radio>
</radio-group>
<div>同意<text @click="gotoInfo">用户服务协议隐私政策</text></div>
</div>
<uni-popup ref="popup" type="bottom" background-color="#fff">
<button type="default" open-type="getPhoneNumber" @getphonenumber="handlePhoneNumber" style="width: 100%;height: 12vh;line-height: 12vh;">点击授权手机号</button>
</uni-popup>
</div>
</view>
</template>
<script>
export default {
name: "login",
data() {
return {
//
height: 0,
statusBarHeight: 0,
isAgreed: false,
};
},
onLoad(options) {
this.initRectInfo()
//
if (options.needAuth === '1') {
setTimeout(() => {
this.$refs.popup.open('bottom');
}, 400);
}
},
methods: {
initRectInfo () {
const sysInfo = uni.getSystemInfoSync()
this.statusBarHeight = sysInfo.statusBarHeight
//
this.height = sysInfo.statusBarHeight + 40
},
//
handlePhoneNumber(e) {
if (e.detail.errMsg === "getPhoneNumber:ok") { //
this.$refs.popup.close();
this.Post({
code: e.detail.code,
encryptedData: e.detail.encryptedData,
iv: e.detail.iv,
token: uni.getStorageSync('token1')
}, '/api/mini_program/bindPhoneNumber')
.then(res => {
this.$store.commit('changeUserInfo', res.data.userinfo);
this.navigateBasedOnPath();
})
.catch(error => {
console.error('绑定手机号失败:', error);
uni.showToast({
title: '绑定手机号失败,请稍后重试',
icon: 'none'
});
});
} else {
console.error('获取手机号失败:', e.detail.errMsg);
uni.showToast({
title: '获取手机号失败,请稍后重试',
icon: 'none'
});
}
},
//
gotoInfo() {
uni.navigateTo({
url: '/subPackages/user/privacy'
});
},
//
toggleAgreement() {
this.isAgreed = !this.isAgreed;
},
//
redirectIndex() {
uni.switchTab({
url: '/pages/index/index',
});
},
//
getUserInfo() {
if (!this.isAgreed) {
uni.showToast({
title: '请先勾选同意《用户服务协议》、《隐私政策》',
icon: 'none'
});
return;
}
uni.login({
provider: 'weixin',
success: (loginRes) => {
uni.getUserInfo({
withCredentials: true,
success: (res) => {
this.Post({
code: loginRes.code,
userInfo: res.userInfo,
encryptedData: res.encryptedData,
iv: res.iv,
wechat_qrcode: uni.getStorageSync('wechat_qrcode') || ''
}, '/api/mini_program/login')
.then(resTwo => {
this.$store.commit('changeUserInfo', resTwo.data.userinfo);
if (resTwo.data.userinfo.mobile) {
this.navigateBasedOnPath();
} else {
uni.setStorageSync('token1', resTwo.data.userinfo.token);
this.$refs.popup.open('bottom');
}
})
.catch(error => {
console.error('登录失败:', error);
uni.showToast({
title: '登录失败,请稍后重试',
icon: 'none'
});
});
},
fail: (err) => {
console.error('获取用户信息失败:', err);
uni.showToast({
title: '获取用户信息失败,请稍后重试',
icon: 'none'
});
}
});
},
fail: (err) => {
console.error('微信登录失败:', err);
uni.showToast({
title: '微信登录失败,请稍后重试',
icon: 'none'
});
}
});
},
//
navigateBasedOnPath() {
if (this.$store.state.user.toPath.includes('user/user')) {
uni.switchTab({
url: this.$store.state.user.toPath
});
} else {
uni.navigateBack({});
}
}
},
};
</script>
<style scoped lang="scss">
.search-header{
width: 100%;
position: fixed;
top: 0;
left: 0;
right: 0;
background: white;
text-align: center;
z-index:99;
.title{
font-size: 30rpx
}
.subtitle{
font-size: 22rpx;
}
}
.content {
min-height: 100vh;
}
.login-tip {
font-size: 28rpx;
margin: 0 60rpx;
margin-top: 40rpx;
}
.login-tip2 {
font-size: 44rpx;
margin: 0rpx 60rpx;
margin-top: 20rpx;
font-weight: 400;
}
.login-tip-box {
display: flex;
align-items: flex-start;
margin: 0 60rpx;
font-size: 28rpx;
margin-top: 40rpx;
line-height: 40rpx;
}
.login-tip-box .icon-gou1 {
line-height: 80rpx;
margin-top: -20rpx;
margin-right: 30rpx;
color: #666;
}
.btn-box {
display: flex;
position: absolute;
bottom: 100rpx;
left: 0;
right: 0;
}
.btn-box button {
width: 400rpx;
}
.article-box {
position: absolute;
left: 0;
right: 0;
justify-content: center;
font-size: 24rpx;
color: #1aad19;
bottom: 40rpx;
}
.article-box .iconfont {
margin-right: 10rpx;
font-size: 26rpx;
}
.article-box text {
border-bottom: 1px solid;
}
</style>

47
pages/stratIndex.vue

@ -0,0 +1,47 @@
<template>
<view class="bg" @click="goIndex()">
<image :src="showImg(screenPng)"></image>
</view>
</template>
<script>
export default {
data() {
return {
screenPng: ""
}
},
onLoad() {
},
onReady () {
//
this.Post({
type_id: 3,
position: 5,
}, '/api/adv/getAdv').then(res => {
if(res.data && res.data.length>0) {
this.screenPng = res.data[0].head_img
}
});
},
methods: {
goIndex () {
uni.switchTab({
url:"/pages/index/index"
})
},
}
}
</script>
<style lang="scss" scoped>
.bg{
height: 100vh;
image{
width: 100%;
height: 100%;
}
}
</style>

659
pages/user/user.vue

@ -0,0 +1,659 @@
<template>
<view class="bg">
<view class="title">我的</view>
<view class="topBox" @click="gotoProfile">
<view class="avatar-box flex-center">
<image :src="showImg(userInfo.avatar)" mode="aspectFill" class="headImg" v-if="userInfo.avatar"></image>
<image src="https://changshu.js-dyyj.com/uploads/20250326/516242619f0772bee371a60684618c01.png" mode="aspectFill"
class="headImg" v-else></image>
</view>
<view class="username" v-if="userInfo.nickname">{{userInfo.nickname}}</view>
<view class="username" v-else>请登录/注册 ></view>
<view class="personalCenter flex-center" v-if="userInfo.nickname">
个人中心
</view>
</view>
<view class="orderBox">
<navigator :url="'/subPackages/order/trades'" class="moreBox flex-between">
我的订单
<span class="flex-between">查看更多 <img
src="https://static.ticket.sz-trip.com/cgc/images/user/rightIcon.png" alt=""></span>
</navigator>
<view class="flex-around" style="margin-top: 20rpx;">
<view class="orderItem" v-for="(item,index) in orderList" :key="index" @click="goTrades(item)">
<img :src="item.src" alt="">
<view>{{item.title}}</view>
</view>
</view>
<!-- 待付款轮播 -->
<!-- <swiper class="my-swipe" :autoplay="3000" indicator-color="white" v-if="dfkList && dfkList.length>0" circular>
<swiper-item v-for="(item,index) in dfkList" :key="item.id">
<div class="dfkBox" @click="goToOrderDetail(item)">
<image :src="showImg(item.order_child[0].specifications_image)" mode="aspectFill"></image>
<div class="contentBox">
<div style="width:300rpx;">
<div style="font-size: 27rpx;margin-bottom: 10rpx;">等待付款 </div>
<div style="display: flex;color: #8A8A8A;font-size: 27rpx;">剩余时间<uni-countdown class="countdown" @timeup="timeup(index)" :show-day="false" :hour="differTimeList[index].slice(0,2)" :minute="differTimeList[index].slice(3,5)" :second="differTimeList[index].slice(6,8)"/></div>
</div>
<div class="orderBtn" @click.stop="setOrderId(item.order_id)">去支付</div>
</div>
</div>
</swiper-item>
</swiper> -->
</view>
<view class="cygj">
<view class="cyItem" style="font-weight: bold;font-size: 31rpx;color: #333333;">
常用工具
</view>
<view class="cyItem flex-between" v-for="(item,index) in cyList" :key="index"
@click="gotoUrl(item,index)" v-if="item.isShow">
<view class="flex-center">
<img :src="item.src" class="headIcon">
{{item.title}}
</view>
<img src="https://static.ticket.sz-trip.com/yandu/images/user/rightIcon-gray.png" class="rightIcon">
</view>
<!-- <button id="contact" class="cyItem flex-between" open-type="contact" bindcontact="handleContact" session-from="sessionFrom">
<view class="flex-center">
<img src="https://static.ticket.sz-trip.com/dongtai/images/user/zxkf.png" class="headIcon">
在线客服
</view>
<img src="https://static.ticket.sz-trip.com/dongtai/images/user/rightIcon-gray.png" class="rightIcon">
</button> -->
</view>
<!-- 旅游咨询弹框 -->
<uni-popup ref="popup" type="center">
<view class="consult-popup">
即将拨打客服电话
<view class="consult-subtitle">服务时间周一至周五<br>9:00-12:0013:00-18:00</view>
<view class="consult-btns">
<view @click="$refs.popup.close()">取消</view>
<view @click="clickPhone('0515-69186109')">确定</view>
<!-- <view>
<button class="confirm" open-type="contact">确定</button>
</view> -->
</view>
</view>
</uni-popup>
</view>
</template>
<script>
import moment from "moment";
export default {
data() {
return {
dfkList: [],
differTimeList: [],
nowDateTime: '', //
userInfo: {},
orderList: [{
src: 'https://static.ticket.sz-trip.com/cgc/images/user/dfk.png',
title: '待付款',
status: 'WAIT_PAYMENT'
},
{
src: 'https://static.ticket.sz-trip.com/cgc/images/user/dfh.png',
title: '待使用/发货',
status: 'PAYMENT_SUCCESSFULLY'
},
{
src: 'https://static.ticket.sz-trip.com/cgc/images/user/dsh.png',
title: '待收货',
status: 'POST'
},
// {
// src: 'https://changshu.js-dyyj.com/uploads/20250326/3f13d3a10dd0f88e764e3ddf1157c108.png',
// title: '',
// status: 'WAIT_COMMENT'
// },
{
src: 'https://static.ticket.sz-trip.com/cgc/images/user/tk.png',
title: '退款/售后',
status: 'WAIT_REFUND,REFUND_SUCCESS,REFUND_REFUSAL,REFUND_ERROR,REFUND_PART'
},
],
cyList: [
{
src: 'https://static.ticket.sz-trip.com/cgc/images/user/gwc.png',
title: '购物车',
path: '/subPackages/user/gwc',
isShow: true
},
{
src: 'https://static.ticket.sz-trip.com/cgc/images/user/kfdh.png',
title: '客服电话',
path: '',
isShow: true
},
{
src: 'https://static.ticket.sz-trip.com/cgc/images/user/shdz.png',
title: '收货地址',
path: '/subPackages/user/travelerList',
isShow: true
},
{
src: 'https://static.ticket.sz-trip.com/cgc/images/user/ysgl.png',
title: '隐私管理',
path: '/subPackages/user/privacy',
isShow: true
},
// {
// src: 'https://changshu.js-dyyj.com/uploads/20250326/3e977f62b6cbfeec5a17d945b96b8c8c.png',
// title: '',
// path: '/subPackages/service/service',
// isShow: true
// },
],
}
},
onShow() {
this.userInfo = (uni.getStorageSync('userInfo') && JSON.parse(uni.getStorageSync('userInfo'))) || this.$store.state.user.userInfo || {}
console.log(this.userInfo)
// this.dfkList = []
// this.nowDateTime = parseInt(new Date().getTime() / 1000)
// this.Post({}, "/api/user/userInfo").then((res) => {
// if (res.data) {
// this.userInfo = res.data;
// // this.getDfk()
// //
// this.Post({},'/api/merchants/is_merchant').then(res => {
// this.cyList[6].isShow = res.data
// })
// }
// });
},
methods: {
//
gotoProfile() {
// token
if(this.userInfo.token) {
uni.navigateTo({
url: '/subPackages/user/profile'
})
}else {
uni.navigateTo({
url: '/pages/login/login'
})
}
},
timeup(index) {
// return this.dfkList.splice(index,1)
},
setOrderId(id) {
let that = this;
that.orderId = id;
that.Post({
order_id: id,
type: "miniprogram",
platform: 'miniprogram'
},
'/api/pay/unify'
).then(res => {
if (res.data) {
uni.requestPayment({
nonceStr: res.data.nonceStr,
package: res.data.package,
paySign: res.data.paySign,
signType: res.data.signType,
timeStamp: res.data.timeStamp
});
}
});
},
//
getDfk() {
this.differTimeList = []
let params = {
offset: this.dfkList.length,
limit: 10,
status: 'WAIT_PAYMENT',
}
this.Post(params, '/api/order/orderList').then(res => {
this.isLoading = false
if (res) {
this.dfkList = [...this.dfkList, ...res.data]
this.dfkList.forEach(item => {
// ,-differTimeList
let del;
if (moment(item.close_time).diff(moment()) > 0) {
del = moment.utc(moment(item.close_time).diff(moment())).format('HH:mm:ss')
} else {
del = '00:00:00'
}
this.differTimeList.push(del)
})
console.log(this.differTimeList);
console.log('this.differTimeList:' + this.differTimeList[0].slice(0, 2))
}
})
},
goToOrderDetail(item) {
uni.navigateTo({
url: '/subPackages/order/detail?id=' + item.order_id
});
},
getChild(list) {
let arr = []
for (let i = 0; i < list.length; i++) {
if (list[i].product_model == "ticket") {
console.log(list[i]);
arr.push(list[i])
break
}
}
console.log(arr);
if (arr.length > 0) {
return arr[0]
} else {
return list[0]
}
},
goCoupon() {
uni.navigateTo({
url: "/subPackages/user/coupon",
});
},
goKeFu() {
uni.navigateTo({
url: "/subPackages/publicservices/ServiceOnline",
});
},
// open(){
// this.$refs.popup.open('center')
// },
gotoUrl(item, index) {
if (item.title == "客服电话") {
this.$refs.popup.open()
return;
}
uni.navigateTo({
url: item.path
})
},
qidai() {
uni.showToast({
title: '功能建设中...',
icon: 'none'
})
},
goTrades(item) {
if (item) {
let title = item.title
if (title == "待使用/发货") title="待使用"
uni.navigateTo({
url: "/subPackages/order/trades?type=" + title,
});
} else {
uni.navigateTo({
url: "/subPackages/order/trades",
});
}
},
//
isGz(item) {
this.$refs.pop.openPop(
'https://yjks.oss-cn-shanghai.aliyuncs.com/uploads/20230517/db9eb60e0abfea8be1075b406fefe551.jpg');
// this.Post({}, '/api/wechat/getSubcribeInfo').then(res => {
// if (res.data) {
// uni.navigateTo({
// url:'/subPackages/webPage/webPage?url='+'https://mp.weixin.qq.com/mp/profile_ext?action=home&__biz=MzU2NjQwNTYxNg==#wechat_redirect'
// })
// } else {
// console.log(this.$refs.pop);
// }
// });
},
}
}
</script>
<style scoped lang="scss">
view{
box-sizing: border-box;
}
::v-deep .uni-countdown {
font-size: 20px !important;
::v-deep .uni-countdown__splitor {
font-size: 20px !important;
}
}
.bg {
min-height: 100vh;
overflow-x: hidden;
background: url('https://static.ticket.sz-trip.com/cgc/images/user/bg.png') no-repeat;
background-size: 100% auto;
background-color: #F7F7F7;
padding-bottom: 100rpx;
}
.title {
font-weight: bold;
font-size: 36rpx;
color: #333333;
position: absolute;
top: 110rpx;
left: 50%;
transform: translate(-50%, 0);
}
.topBox {
width: 750rpx;
height: 373rpx;
padding: 100rpx 0 0 26rpx;
box-sizing: border-box;
display: flex;
margin-top: 90rpx;
.avatar-box {
width: 120rpx;
height: 120rpx;
background: #FFFFFF;
border-radius: 50%;
overflow: hidden;
}
.headImg {
width: 120rpx;
height: 120rpx;
border-radius: 50%;
}
.username {
margin: 40rpx 0 0 28rpx;
font-weight: 500;
font-size: 40rpx;
color: #000000;
}
.personalCenter {
width: 165rpx;
height: 59rpx;
background: rgba(24, 135, 145, 1);
border-radius: 29rpx 0rpx 0rpx 29rpx;
margin: 40rpx 0 0 auto;
font-weight: bold;
font-size: 28rpx;
color: #FFFFFF;
}
}
.orderBox {
width: 697rpx;
background: #FFFFFF;
box-shadow: 0rpx 0rpx 23rpx 0rpx rgba(80, 80, 80, 0.12);
border-radius: 20rpx;
margin: -48rpx auto 0;
padding-bottom: 30.6rpx;
.moreBox {
height: 84rpx;
margin: auto;
font-size: 31rpx;
font-family: PingFang SC;
font-weight: bold;
color: #000000;
padding-left: 26rpx;
span {
padding-right: 18rpx;
font-size: 27rpx;
font-family: PingFang SC;
font-weight: 500;
box-sizing: border-box;
img {
width: 12rpx;
height: 21rpx;
margin-left: 12rpx;
}
}
}
.orderItem {
font-size: 27rpx;
font-family: PingFang SC;
font-weight: 500;
color: #000000;
width: 25%;
text-align: center;
img {
width: 80rpx;
height: 80rpx;
}
}
}
.lxwm {
width: 696rpx;
height: 447rpx;
background: #FFFFFF;
box-shadow: 0px 4rpx 12rpx 0px rgba(150, 149, 149, 0.3);
border-radius: 20rpx;
margin: auto;
padding: 27rpx 19rpx 0 19rpx;
font-size: 31rpx;
font-family: PingFang SC;
font-weight: bold;
color: #000000;
.midBox {
padding: 26rpx 43rpx 21rpx 44rpx;
box-sizing: border-box;
img {
width: 265rpx;
height: 252rpx;
border-radius: 15rpx;
}
}
.botBox {
padding: 0 30rpx 0 51rpx;
font-size: 27rpx;
font-family: PingFang SC;
font-weight: bold;
color: #000000;
}
}
.cygj {
width: 697rpx;
background: #FFFFFF;
border-radius: 20rpx;
margin: 30rpx auto 0;
padding: 0 27rpx;
font-size: 28rpx;
font-family: PingFang SC;
font-weight: 500;
color: #000000;
box-sizing: border-box;
.cyItem {
height: 106rpx;
display: flex;
align-items: center;
border-bottom: 1rpx solid #D8D8D8;
.headIcon {
width: 42rpx;
height: 42rpx;
margin-right: 15rpx;
}
.rightIcon {
width: 20rpx;
height: 20rpx;
}
}
.cyItem:last-child {
border: none;
}
}
.my-swipe {
// width: 100%;
margin: 0 30rpx;
margin-top: 37.3rpx;
}
swiper {
height: 111rpx !important;
}
.dfkBox {
width: 100%;
height: 111rpx;
background: #F7F7F7;
margin: 0 auto 30.64rpx;
// padding: 0.25rem;
display: flex;
}
.dfkBox image {
width: 137rpx;
height: 111rpx;
border-radius: 13rpx;
flex-shrink: 0;
// margin-right: 16.7rpx;
}
.dfkBox .contentBox {
padding-left: 5rpx;
padding-right: 5rpx;
height: 111rpx;
width: 100%;
display: flex;
justify-content: space-around;
align-items: center;
}
// .van-count-down{
// font-size: 0.39rem;
// font-family: PingFang SC;
// font-weight: 400;
// color: #FB6E4D;
// }
.orderBtn {
width: 152rpx;
height: 56rpx;
background: linear-gradient(270deg, #FC5109, #FDC43A);
;
border-radius: 28rpx;
text-align: center;
line-height: 56rpx;
font-size: 27rpx;
font-family: PingFang SC;
font-weight: 400;
color: #FFFFFF;
// margin-left: 50rpx;
// margin-right: 20rpx;
}
#contact {
-webkit-tap-highlight-color: transparent;
background-color: rgba(0, 0, 0, 0);
border-radius: 0;
box-sizing: border-box;
color: transparent;
cursor: pointer;
overflow: hidden;
padding: 0 27rpx;
position: relative;
text-align: center;
text-decoration: none;
border: transparent 0px solid;
display: flex;
align-items: center;
font-size: 28rpx;
font-family: PingFang SC;
font-weight: 500;
color: #000000;
}
button::after {
border: none;
background-color: rgba(0, 0, 0, 0);
}
.more {
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, .5);
position: fixed;
top: 0;
left: 0;
display: flex;
flex-direction: column;
justify-content: space-around;
align-items: center;
image {
width: 646rpx;
height: 959rpx;
}
img {
width: 80rpx;
height: 80rpx;
}
}
.consult-popup {
width: 487rpx;
height: 367rpx;
background: #F0F0F0;
border-radius: 20rpx;
padding: 65rpx 0 23rpx;
font-weight: bold;
font-size: 32rpx;
color: #000000;
text-align: center;
.consult-subtitle {
font-weight: 500;
font-size: 27rpx;
color: #333333;
margin-top: 43rpx;
}
.consult-btns {
display: flex;
margin-top: 75rpx;
view {
width: 50%;
font-weight: bold;
font-size: 32rpx;
color: #00AEA0;
line-height: 54rpx;
}
view:first-child {
border-right: 1rpx solid #D8D8D8;
color: #000000;
}
}
}
.confirm{
height: 58rpx;
line-height: 58rpx;
background: none;
font-weight: bold;
font-size: 32rpx;
color: #00AEA0;
}
</style>

107
static/css/base.css

@ -0,0 +1,107 @@
* {
padding: 0;
margin: 0;
}
a {
text-decoration: none;
}
input {
outline: none;
border: none;
}
view {
box-sizing: border-box;
}
.flex-between {
display: flex;
justify-content: space-between;
align-items: center;
}
.flex-center {
display: flex;
justify-content: center;
align-items: center;
}
.flex-around {
display: flex;
justify-content: space-around;
align-items: center;
}
.flex-start {
display: flex;
justify-content: flex-start;
align-items: center;
}
.flex-column {
display: flex;
flex-direction: column;
}
/*单行隐藏*/
.text-overflow {
overflow-x: hidden;
overflow-y: inherit;
text-overflow: ellipsis;
white-space: nowrap;
}
/* 两行隐藏 */
.text-overflowRows {
overflow: hidden;
text-overflow: ellipsis;
-webkit-line-clamp: 2;
word-break: break-all;
display: -webkit-box;
-webkit-box-orient: vertical;
}
.finished-text {
text-align: center;
font-size: 24upx;
padding: 30upx 0;
color: #999999;
}
.flex{
display: flex;
}
.flex-shrink-0{
flex-shrink: 0;
}
.flex-1{
flex: 1;
}
.flex-wrap{
flex-wrap: wrap;
}
.w-full{
width: 100%;
}
.w-1rpx{
width: 1rpx;
}
.h-1rpx{
height: 1rpx;
}
.relative{
position: relative;
}
.absolute{
position: absolute;
}
.flex-items-center{
align-items: center;
}
.no-scrollbar::-webkit-scrollbar{
display: none;
}
.font-bold{
font-weight: bold;
}

BIN
static/images/home.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

BIN
static/images/mine.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

BIN
static/images/selectHome.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

BIN
static/images/selectMine.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

306
static/js/CommonFunction.js

@ -0,0 +1,306 @@
// 获取路径参数
import Vue from "vue";
// 格式化富文本
Vue.prototype.formateRichText = str => {
if (!str) return "";
var reg = new RegExp("<img", "g");
str = str.replace(reg, "<img class='sz-xcx-fwb-img' width='100%'")
reg = new RegExp("<IMG", "g");
str = str.replace(reg, "<img class='sz-xcx-fwb-img' width='100%'")
reg = new RegExp("&nbsp;", "g");
str = str.replace(reg, '<span style="width: 8rpx;display: inline-block;"></span>')
reg = new RegExp("section", "g");
str = str.replace(reg, 'div');
reg = new RegExp("↵", "g");
str = str.replace(reg, '<br />');
return str;
}
// 获取路径参数
Vue.prototype.getUrlPara = url => {
let arrUrl = url.split("?");
let para = arrUrl[1];
return para ? para.split('&') : false;
}
// 中文姓名规则
Vue.prototype.idChinaName = (val) => {
var pattern = /^[\u4E00-\u9FA5]{2,4}$/
return pattern.test(val);
}
// 身份证验证规则
Vue.prototype.idCardNumber = (val) => {
var pattern = /^\d{17}(\d{1}|[X|x])$/
return pattern.test(val);
}
// 护照验证正则
Vue.prototype.passportValid = (val) => {
return /^([a-zA-z]|[0-9]){5,17}$/.test(val);
}
// 台胞证正则
Vue.prototype.taiwanValid = (val) => {
return /^\d{8}|^[a-zA-Z0-9]{10}|^\d{18}$/.test(val);
}
// 港澳通行证正则
Vue.prototype.gangaoValid = (val) => {
return /^([A-Z]\d{6,10}(\(\w{1}\))?)$/.test(val);
}
// 外国人永久居留证正则
Vue.prototype.foreignerValid = (val) => {
return /(^[A-Za-z]{3})([0-9]{12}$)/.test(val);
}
// 军官证正则
Vue.prototype.officerValid = (val) => {
return /^[\u4E00-\u9FA5](字第)([0-9a-zA-Z]{4,8})(号?)$/.test(val);
}
// 邮箱验证正则
Vue.prototype.emailValid = (val) => {
return /^[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/.test(val)
}
//判断电话号码格式
Vue.prototype.IsTel = tel => {
var pattern = /^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/;
return pattern.test(tel);
}
//判断澳门电话号码格式
Vue.prototype.IsTelMacau = tel => {
var pattern = /^\d{8}$/;
return pattern.test(tel);
}
//验证码格式
Vue.prototype.IsCode = code => {
var pattern = /^\d{6}$/;
return pattern.test(code);
}
// 图片显示判断
Vue.prototype.showImg = img => {
if (!img) return;
let timeStamp = new Date().getTime()
if (img.indexOf('https://') != -1 || img.indexOf('http://') != -1) {
return img+"?timestamp="+timeStamp;
} else {
return Vue.prototype.NEWAPIURL + img +"?timestamp="+timeStamp;
}
}
// 获取经纬度
Vue.prototype.getLocation = function(callback) {
return
// 检查权限状态
uni.getSetting({
success: (res) => {
if (res.authSetting['scope.userLocation'] === false) {
// 已拒绝权限,引导用户去设置
uni.showModal({
title: '权限申请',
content: '需要位置权限才能获取当前城市',
confirmText: '去设置',
success: (res) => {
if (res.confirm) {
uni.openSetting();
} else {
callback && callback(null, {
errMsg: '用户拒绝授权'
});
}
}
});
} else {
// 未授权或已授权,请求位置
this._requestLocation(callback);
}
},
fail: (err) => {
console.error('获取权限状态失败:', err);
callback && callback(null, err);
}
});
};
// 私有方法:请求位置
Vue.prototype._requestLocation = function(callback) {
// 直接获取位置(微信小程序不需要先调用startLocationUpdate)
uni.getLocation({
type: 'wgs84', // 坐标系类型
success: (res) => {
console.log('获取位置成功:', res);
const location = {
lat: res.latitude,
lon: res.longitude
};
uni.setStorageSync('location', location);
callback && callback(location);
},
fail: (err) => {
console.error('获取位置失败:', err);
if (err.errMsg.includes('auth deny')) {
// 权限被拒绝,引导用户去设置
uni.showModal({
title: '权限申请',
content: '需要位置权限才能获取当前城市',
confirmText: '去设置',
success: (res) => {
if (res.confirm) {
uni.openSetting();
}
}
});
}
callback && callback(null, err);
}
});
};
// 路由页面跳转
Vue.prototype.gotoPath = path => {
uni.navigateTo({
url: path
})
}
// 返回上一页
Vue.prototype.goBack = () => {
getCurrentPages().length > 1 ? uni.navigateBack({}) : uni.switchTab({
url: '/pages/index/index'
})
}
// 打开地图
Vue.prototype.openLocation = (lat, lon) => {
uni.openLocation({
latitude: Number(lat),
longitude: Number(lon),
success: function() {
console.log('success');
}
});
}
// 拨打电话
Vue.prototype.clickPhone = (phone) => {
uni.makePhoneCall({
phoneNumber: phone
})
}
//周几
Vue.prototype.ShowDateDay = day => {
let stateTxt = "";
switch (day) {
case 0:
stateTxt = '周日'
break;
case 1:
stateTxt = '周一'
break;
case 2:
stateTxt = '周二'
break;
case 3:
stateTxt = '周三'
break;
case 4:
stateTxt = '周四'
break;
case 5:
stateTxt = '周五'
break;
case 6:
stateTxt = '周六'
break;
}
return stateTxt
}
Date.prototype.Format = function(fmt) { //author: meizz
var o = {
"Y+": this.getFullYear(), //月份
"M+": this.getMonth() + 1, //月份
"d+": this.getDate(), //日
"h+": this.getHours(), //小时
"m+": this.getMinutes(), //分
"s+": this.getSeconds(), //秒
"q+": Math.floor((this.getMonth() + 3) / 3), //季度
"S": this.getMilliseconds() //毫秒
};
if (/(y+)/.test(fmt))
fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
for (var k in o)
if (new RegExp("(" + k + ")").test(fmt))
fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k])
.length)));
return fmt;
}
// 是否接收订阅消息
Vue.prototype.getSubscribeMessage = () => {
const templateIds = [
// 退款成功通知
'hRZoiEES2BWtKb6Xgsnn8khLQH9un5j_11qu0bwlhfE',
// 订单核销通知
// '7D-JP7o0nQ_NiQk2w8mBs8jdT1_7ofvyBN-G9NLY2Zk',
// 订单支付成功通知
// '6cHez9KDlCDp1_nWUlUSV7qEaahIQWmYVlOCE-J6ODQ',
// 出票失败提醒
// 'G-N85zK2gPwgTRZWQrtHZo_-5TFcdAqBxSk4qsqcvVc',
// 出票结果通知
'YyTCUIYBnrj9CyKks8cOjNX_Rk8a4yVdswMP-zXVbhc'
]
uni.requestSubscribeMessage({
tmplIds: templateIds,
complete(res) {
uni.navigateTo({
url: '/subPackages/order/trades'
})
}
})
}
// 金刚区头图
Vue.prototype.getHeadImg = id => {
return Vue.prototype.Post({id: id},'/api/article/getArticleById').then(res => {
return res.data.image
});
}
// 判断是否去外部链接
Vue.prototype.goOtherDetail = (item,type) => {
if(item.link_type == 1) {
// 外部小程序
uni.navigateToMiniProgram({
shortLink: item.ext_link
})
return true
}else if(item.link_type == 2){
// 外部H5
uni.navigateTo({
url: '/subPackages/webPage/webPage?url=' + item.ext_link
});
return true
}
}
// 根据类型判断详情页
Vue.prototype.goDetailByType= function(item){
let res = this.goOtherDetail(item)
if (res) {
return
}
uni.navigateTo({
url: `/subPackages/techan/detail?id=?${item.id}`
})
}

196
static/js/mmmm-image-tools/index.js

@ -0,0 +1,196 @@
function getLocalFilePath(path) {
if (path.indexOf('_www') === 0 || path.indexOf('_doc') === 0 || path.indexOf('_documents') === 0 || path.indexOf('_downloads') === 0) {
return path
}
if (path.indexOf('file://') === 0) {
return path
}
if (path.indexOf('/storage/emulated/0/') === 0) {
return path
}
if (path.indexOf('/') === 0) {
var localFilePath = plus.io.convertAbsoluteFileSystem(path)
if (localFilePath !== path) {
return localFilePath
} else {
path = path.substr(1)
}
}
return '_www/' + path
}
function dataUrlToBase64(str) {
var array = str.split(',')
return array[array.length - 1]
}
var index = 0
function getNewFileId() {
return Date.now() + String(index++)
}
function biggerThan(v1, v2) {
var v1Array = v1.split('.')
var v2Array = v2.split('.')
var update = false
for (var index = 0; index < v2Array.length; index++) {
var diff = v1Array[index] - v2Array[index]
if (diff !== 0) {
update = diff > 0
break
}
}
return update
}
export function pathToBase64(path) {
return new Promise(function(resolve, reject) {
if (typeof window === 'object' && 'document' in window) {
if (typeof FileReader === 'function') {
var xhr = new XMLHttpRequest()
xhr.open('GET', path, true)
xhr.responseType = 'blob'
xhr.onload = function() {
if (this.status === 200) {
let fileReader = new FileReader()
fileReader.onload = function(e) {
resolve(e.target.result)
}
fileReader.onerror = reject
fileReader.readAsDataURL(this.response)
}
}
xhr.onerror = reject
xhr.send()
return
}
var canvas = document.createElement('canvas')
var c2x = canvas.getContext('2d')
var img = new Image
img.onload = function() {
canvas.width = img.width
canvas.height = img.height
c2x.drawImage(img, 0, 0)
resolve(canvas.toDataURL())
canvas.height = canvas.width = 0
}
img.onerror = reject
img.src = path
return
}
if (typeof plus === 'object') {
plus.io.resolveLocalFileSystemURL(getLocalFilePath(path), function(entry) {
entry.file(function(file) {
var fileReader = new plus.io.FileReader()
fileReader.onload = function(data) {
resolve(data.target.result)
}
fileReader.onerror = function(error) {
reject(error)
}
fileReader.readAsDataURL(file)
}, function(error) {
reject(error)
})
}, function(error) {
reject(error)
})
return
}
if (typeof wx === 'object' && wx.canIUse('getFileSystemManager')) {
wx.getFileSystemManager().readFile({
filePath: path,
encoding: 'base64',
success: function(res) {
resolve('data:image/png;base64,' + res.data)
},
fail: function(error) {
reject(error)
}
})
return
}
reject(new Error('not support'))
})
}
export function base64ToPath(base64) {
return new Promise(function(resolve, reject) {
if (typeof window === 'object' && 'document' in window) {
base64 = base64.split(',')
var type = base64[0].match(/:(.*?);/)[1]
var str = atob(base64[1])
var n = str.length
var array = new Uint8Array(n)
while (n--) {
array[n] = str.charCodeAt(n)
}
return resolve((window.URL || window.webkitURL).createObjectURL(new Blob([array], { type: type })))
}
var extName = base64.split(',')[0].match(/data\:\S+\/(\S+);/)
if (extName) {
extName = extName[1]
} else {
reject(new Error('base64 error'))
}
var fileName = getNewFileId() + '.' + extName
if (typeof plus === 'object') {
var basePath = '_doc'
var dirPath = 'uniapp_temp'
var filePath = basePath + '/' + dirPath + '/' + fileName
if (!biggerThan(plus.os.name === 'Android' ? '1.9.9.80627' : '1.9.9.80472', plus.runtime.innerVersion)) {
plus.io.resolveLocalFileSystemURL(basePath, function(entry) {
entry.getDirectory(dirPath, {
create: true,
exclusive: false,
}, function(entry) {
entry.getFile(fileName, {
create: true,
exclusive: false,
}, function(entry) {
entry.createWriter(function(writer) {
writer.onwrite = function() {
resolve(filePath)
}
writer.onerror = reject
writer.seek(0)
writer.writeAsBinary(dataUrlToBase64(base64))
}, reject)
}, reject)
}, reject)
}, reject)
return
}
var bitmap = new plus.nativeObj.Bitmap(fileName)
bitmap.loadBase64Data(base64, function() {
bitmap.save(filePath, {}, function() {
bitmap.clear()
resolve(filePath)
}, function(error) {
bitmap.clear()
reject(error)
})
}, function(error) {
bitmap.clear()
reject(error)
})
return
}
if (typeof wx === 'object' && wx.canIUse('getFileSystemManager')) {
var filePath = wx.env.USER_DATA_PATH + '/' + fileName
wx.getFileSystemManager().writeFile({
filePath: filePath,
data: dataUrlToBase64(base64),
encoding: 'base64',
success: function() {
resolve(filePath)
},
fail: function(error) {
reject(error)
}
})
return
}
reject(new Error('not support'))
})
}

11
static/js/mmmm-image-tools/package.json

@ -0,0 +1,11 @@
{
"id": "mmmm-image-tools",
"name": "image-tools",
"version": "1.4.0",
"description": "图像转换工具,可用于图像和base64的转换",
"keywords": [
"base64",
"保存",
"图像"
]
}

81
static/js/request.js

@ -0,0 +1,81 @@
import Vue from 'vue';
import store from '@/store';
// 定义 API URL
const DEV_API_URL = 'https://cgc.js-dyyj.com';
const PROD_API_URL = 'https://cgc.js-dyyj.com';
const NEWAPIURL = process.env.NODE_ENV === 'development' ? DEV_API_URL : PROD_API_URL;
// 获取token
const getToken = () => {
const userInfoFromStorage = uni.getStorageSync('userInfo');
if (userInfoFromStorage) {
const userInfo = JSON.parse(userInfoFromStorage);
if (userInfo.token) {
return userInfo.token;
}
}
return store.state.user.userInfo.token;
};
// 定义错误处理函数 noForceLogin 不强制登录
const handleError = (res, reject, noForceLogin) => {
if (res.data?.code === 401) {
if (noForceLogin) {
reject(res)
return
}
store.commit('changeLoginPath');
}
setTimeout(() => {
uni.showToast({
title: res.data?.msg || res.msg,
icon: 'none'
});
reject(res);
}, 0);
};
// 挂载到 Vue 原型上
Vue.prototype.NEWAPIURL = NEWAPIURL;
// #ifdef H5
Vue.prototype.NEWAPIURL = '/api';
// #endif
Vue.prototype.Post = (params = {}, apiurl) => {
const token = getToken();
if (token) {
params.token = token;
}
return new Promise((resolve, reject) => {
uni.showLoading({
title: '加载中'
});
uni.request({
method: params.method || 'GET',
url: Vue.prototype.NEWAPIURL + apiurl,
data: params,
header: {
'content-type': 'application/json',
'token': token || ''
},
success: (res) => {
console.log('success', res.data);
uni.hideLoading()
if (res.data.code === 200 || res.data.code === 1) {
resolve(res.data);
} else {
handleError(res, reject);
}
},
fail: (err) => {
console.log('err', err);
uni.hideLoading()
handleError(err, reject, params.noForceLogin);
}
});
});
};
export default NEWAPIURL;

426
static/js/weapp-qrcode.js

@ -0,0 +1,426 @@
// Core code comes from https://github.com/davidshimjs/qrcodejs
var QRCode;
(function () {
/**
* Get the type by string length
*
* @private
* @param {String} sText
* @param {Number} nCorrectLevel
* @return {Number} type
*/
function _getTypeNumber(sText, nCorrectLevel) {
var nType = 1;
var length = _getUTF8Length(sText);
for (var i = 0, len = QRCodeLimitLength.length; i <= len; i++) {
var nLimit = 0;
switch (nCorrectLevel) {
case QRErrorCorrectLevel.L:
nLimit = QRCodeLimitLength[i][0];
break;
case QRErrorCorrectLevel.M:
nLimit = QRCodeLimitLength[i][1];
break;
case QRErrorCorrectLevel.Q:
nLimit = QRCodeLimitLength[i][2];
break;
case QRErrorCorrectLevel.H:
nLimit = QRCodeLimitLength[i][3];
break;
}
if (length <= nLimit) {
break;
} else {
nType++;
}
}
if (nType > QRCodeLimitLength.length) {
throw new Error("Too long data");
}
return nType;
}
function _getUTF8Length(sText) {
var replacedText = encodeURI(sText).toString().replace(/\%[0-9a-fA-F]{2}/g, 'a');
return replacedText.length + (replacedText.length != sText ? 3 : 0);
}
function QR8bitByte(data) {
this.mode = QRMode.MODE_8BIT_BYTE;
this.data = data;
this.parsedData = [];
// Added to support UTF-8 Characters
for (var i = 0, l = this.data.length; i < l; i++) {
var byteArray = [];
var code = this.data.charCodeAt(i);
if (code > 0x10000) {
byteArray[0] = 0xF0 | ((code & 0x1C0000) >>> 18);
byteArray[1] = 0x80 | ((code & 0x3F000) >>> 12);
byteArray[2] = 0x80 | ((code & 0xFC0) >>> 6);
byteArray[3] = 0x80 | (code & 0x3F);
} else if (code > 0x800) {
byteArray[0] = 0xE0 | ((code & 0xF000) >>> 12);
byteArray[1] = 0x80 | ((code & 0xFC0) >>> 6);
byteArray[2] = 0x80 | (code & 0x3F);
} else if (code > 0x80) {
byteArray[0] = 0xC0 | ((code & 0x7C0) >>> 6);
byteArray[1] = 0x80 | (code & 0x3F);
} else {
byteArray[0] = code;
}
this.parsedData.push(byteArray);
}
this.parsedData = Array.prototype.concat.apply([], this.parsedData);
if (this.parsedData.length != this.data.length) {
this.parsedData.unshift(191);
this.parsedData.unshift(187);
this.parsedData.unshift(239);
}
}
QR8bitByte.prototype = {
getLength: function (buffer) {
return this.parsedData.length;
},
write: function (buffer) {
for (var i = 0, l = this.parsedData.length; i < l; i++) {
buffer.put(this.parsedData[i], 8);
}
}
};
// QRCodeModel
function QRCodeModel(typeNumber, errorCorrectLevel) {
this.typeNumber = typeNumber;
this.errorCorrectLevel = errorCorrectLevel;
this.modules = null;
this.moduleCount = 0;
this.dataCache = null;
this.dataList = [];
}
QRCodeModel.prototype = {
addData: function (data) { var newData = new QR8bitByte(data); this.dataList.push(newData); this.dataCache = null; }, isDark: function (row, col) {
if (row < 0 || this.moduleCount <= row || col < 0 || this.moduleCount <= col) { throw new Error(row + "," + col); }
return this.modules[row][col];
}, getModuleCount: function () { return this.moduleCount; }, make: function () { this.makeImpl(false, this.getBestMaskPattern()); }, makeImpl: function (test, maskPattern) {
this.moduleCount = this.typeNumber * 4 + 17; this.modules = new Array(this.moduleCount); for (var row = 0; row < this.moduleCount; row++) { this.modules[row] = new Array(this.moduleCount); for (var col = 0; col < this.moduleCount; col++) { this.modules[row][col] = null; } }
this.setupPositionProbePattern(0, 0); this.setupPositionProbePattern(this.moduleCount - 7, 0); this.setupPositionProbePattern(0, this.moduleCount - 7); this.setupPositionAdjustPattern(); this.setupTimingPattern(); this.setupTypeInfo(test, maskPattern); if (this.typeNumber >= 7) { this.setupTypeNumber(test); }
if (this.dataCache == null) { this.dataCache = QRCodeModel.createData(this.typeNumber, this.errorCorrectLevel, this.dataList); }
this.mapData(this.dataCache, maskPattern);
}, setupPositionProbePattern: function (row, col) { for (var r = -1; r <= 7; r++) { if (row + r <= -1 || this.moduleCount <= row + r) continue; for (var c = -1; c <= 7; c++) { if (col + c <= -1 || this.moduleCount <= col + c) continue; if ((0 <= r && r <= 6 && (c == 0 || c == 6)) || (0 <= c && c <= 6 && (r == 0 || r == 6)) || (2 <= r && r <= 4 && 2 <= c && c <= 4)) { this.modules[row + r][col + c] = true; } else { this.modules[row + r][col + c] = false; } } } }, getBestMaskPattern: function () {
var minLostPoint = 0; var pattern = 0; for (var i = 0; i < 8; i++) { this.makeImpl(true, i); var lostPoint = QRUtil.getLostPoint(this); if (i == 0 || minLostPoint > lostPoint) { minLostPoint = lostPoint; pattern = i; } }
return pattern;
}, createMovieClip: function (target_mc, instance_name, depth) {
var qr_mc = target_mc.createEmptyMovieClip(instance_name, depth); var cs = 1; this.make(); for (var row = 0; row < this.modules.length; row++) { var y = row * cs; for (var col = 0; col < this.modules[row].length; col++) { var x = col * cs; var dark = this.modules[row][col]; if (dark) { qr_mc.beginFill(0, 100); qr_mc.moveTo(x, y); qr_mc.lineTo(x + cs, y); qr_mc.lineTo(x + cs, y + cs); qr_mc.lineTo(x, y + cs); qr_mc.endFill(); } } }
return qr_mc;
}, setupTimingPattern: function () {
for (var r = 8; r < this.moduleCount - 8; r++) {
if (this.modules[r][6] != null) { continue; }
this.modules[r][6] = (r % 2 == 0);
}
for (var c = 8; c < this.moduleCount - 8; c++) {
if (this.modules[6][c] != null) { continue; }
this.modules[6][c] = (c % 2 == 0);
}
}, setupPositionAdjustPattern: function () {
var pos = QRUtil.getPatternPosition(this.typeNumber); for (var i = 0; i < pos.length; i++) {
for (var j = 0; j < pos.length; j++) {
var row = pos[i]; var col = pos[j]; if (this.modules[row][col] != null) { continue; }
for (var r = -2; r <= 2; r++) { for (var c = -2; c <= 2; c++) { if (r == -2 || r == 2 || c == -2 || c == 2 || (r == 0 && c == 0)) { this.modules[row + r][col + c] = true; } else { this.modules[row + r][col + c] = false; } } }
}
}
}, setupTypeNumber: function (test) {
var bits = QRUtil.getBCHTypeNumber(this.typeNumber); for (var i = 0; i < 18; i++) { var mod = (!test && ((bits >> i) & 1) == 1); this.modules[Math.floor(i / 3)][i % 3 + this.moduleCount - 8 - 3] = mod; }
for (var i = 0; i < 18; i++) { var mod = (!test && ((bits >> i) & 1) == 1); this.modules[i % 3 + this.moduleCount - 8 - 3][Math.floor(i / 3)] = mod; }
}, setupTypeInfo: function (test, maskPattern) {
var data = (this.errorCorrectLevel << 3) | maskPattern; var bits = QRUtil.getBCHTypeInfo(data); for (var i = 0; i < 15; i++) { var mod = (!test && ((bits >> i) & 1) == 1); if (i < 6) { this.modules[i][8] = mod; } else if (i < 8) { this.modules[i + 1][8] = mod; } else { this.modules[this.moduleCount - 15 + i][8] = mod; } }
for (var i = 0; i < 15; i++) { var mod = (!test && ((bits >> i) & 1) == 1); if (i < 8) { this.modules[8][this.moduleCount - i - 1] = mod; } else if (i < 9) { this.modules[8][15 - i - 1 + 1] = mod; } else { this.modules[8][15 - i - 1] = mod; } }
this.modules[this.moduleCount - 8][8] = (!test);
}, mapData: function (data, maskPattern) {
var inc = -1; var row = this.moduleCount - 1; var bitIndex = 7; var byteIndex = 0; for (var col = this.moduleCount - 1; col > 0; col -= 2) {
if (col == 6) col--; while (true) {
for (var c = 0; c < 2; c++) {
if (this.modules[row][col - c] == null) {
var dark = false; if (byteIndex < data.length) { dark = (((data[byteIndex] >>> bitIndex) & 1) == 1); }
var mask = QRUtil.getMask(maskPattern, row, col - c); if (mask) { dark = !dark; }
this.modules[row][col - c] = dark; bitIndex--; if (bitIndex == -1) { byteIndex++; bitIndex = 7; }
}
}
row += inc; if (row < 0 || this.moduleCount <= row) { row -= inc; inc = -inc; break; }
}
}
}
};
QRCodeModel.PAD0 = 0xEC;
QRCodeModel.PAD1 = 0x11;
QRCodeModel.createData = function (typeNumber, errorCorrectLevel, dataList) {
var rsBlocks = QRRSBlock.getRSBlocks(typeNumber, errorCorrectLevel); var buffer = new QRBitBuffer(); for (var i = 0; i < dataList.length; i++) { var data = dataList[i]; buffer.put(data.mode, 4); buffer.put(data.getLength(), QRUtil.getLengthInBits(data.mode, typeNumber)); data.write(buffer); }
var totalDataCount = 0; for (var i = 0; i < rsBlocks.length; i++) { totalDataCount += rsBlocks[i].dataCount; }
if (buffer.getLengthInBits() > totalDataCount * 8) {
throw new Error("code length overflow. ("
+ buffer.getLengthInBits()
+ ">"
+ totalDataCount * 8
+ ")");
}
if (buffer.getLengthInBits() + 4 <= totalDataCount * 8) { buffer.put(0, 4); }
while (buffer.getLengthInBits() % 8 != 0) { buffer.putBit(false); }
while (true) {
if (buffer.getLengthInBits() >= totalDataCount * 8) { break; }
buffer.put(QRCodeModel.PAD0, 8); if (buffer.getLengthInBits() >= totalDataCount * 8) { break; }
buffer.put(QRCodeModel.PAD1, 8);
}
return QRCodeModel.createBytes(buffer, rsBlocks);
};
QRCodeModel.createBytes = function (buffer, rsBlocks) {
var offset = 0; var maxDcCount = 0; var maxEcCount = 0; var dcdata = new Array(rsBlocks.length); var ecdata = new Array(rsBlocks.length); for (var r = 0; r < rsBlocks.length; r++) {
var dcCount = rsBlocks[r].dataCount; var ecCount = rsBlocks[r].totalCount - dcCount; maxDcCount = Math.max(maxDcCount, dcCount); maxEcCount = Math.max(maxEcCount, ecCount); dcdata[r] = new Array(dcCount); for (var i = 0; i < dcdata[r].length; i++) { dcdata[r][i] = 0xff & buffer.buffer[i + offset]; }
offset += dcCount; var rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount); var rawPoly = new QRPolynomial(dcdata[r], rsPoly.getLength() - 1); var modPoly = rawPoly.mod(rsPoly); ecdata[r] = new Array(rsPoly.getLength() - 1); for (var i = 0; i < ecdata[r].length; i++) { var modIndex = i + modPoly.getLength() - ecdata[r].length; ecdata[r][i] = (modIndex >= 0) ? modPoly.get(modIndex) : 0; }
}
var totalCodeCount = 0; for (var i = 0; i < rsBlocks.length; i++) { totalCodeCount += rsBlocks[i].totalCount; }
var data = new Array(totalCodeCount); var index = 0; for (var i = 0; i < maxDcCount; i++) { for (var r = 0; r < rsBlocks.length; r++) { if (i < dcdata[r].length) { data[index++] = dcdata[r][i]; } } }
for (var i = 0; i < maxEcCount; i++) { for (var r = 0; r < rsBlocks.length; r++) { if (i < ecdata[r].length) { data[index++] = ecdata[r][i]; } } }
return data;
};
var QRMode = { MODE_NUMBER: 1 << 0, MODE_ALPHA_NUM: 1 << 1, MODE_8BIT_BYTE: 1 << 2, MODE_KANJI: 1 << 3 };
var QRErrorCorrectLevel = { L: 1, M: 0, Q: 3, H: 2 };
var QRMaskPattern = { PATTERN000: 0, PATTERN001: 1, PATTERN010: 2, PATTERN011: 3, PATTERN100: 4, PATTERN101: 5, PATTERN110: 6, PATTERN111: 7 };
var QRUtil = {
PATTERN_POSITION_TABLE: [[], [6, 18], [6, 22], [6, 26], [6, 30], [6, 34], [6, 22, 38], [6, 24, 42], [6, 26, 46], [6, 28, 50], [6, 30, 54], [6, 32, 58], [6, 34, 62], [6, 26, 46, 66], [6, 26, 48, 70], [6, 26, 50, 74], [6, 30, 54, 78], [6, 30, 56, 82], [6, 30, 58, 86], [6, 34, 62, 90], [6, 28, 50, 72, 94], [6, 26, 50, 74, 98], [6, 30, 54, 78, 102], [6, 28, 54, 80, 106], [6, 32, 58, 84, 110], [6, 30, 58, 86, 114], [6, 34, 62, 90, 118], [6, 26, 50, 74, 98, 122], [6, 30, 54, 78, 102, 126], [6, 26, 52, 78, 104, 130], [6, 30, 56, 82, 108, 134], [6, 34, 60, 86, 112, 138], [6, 30, 58, 86, 114, 142], [6, 34, 62, 90, 118, 146], [6, 30, 54, 78, 102, 126, 150], [6, 24, 50, 76, 102, 128, 154], [6, 28, 54, 80, 106, 132, 158], [6, 32, 58, 84, 110, 136, 162], [6, 26, 54, 82, 110, 138, 166], [6, 30, 58, 86, 114, 142, 170]], G15: (1 << 10) | (1 << 8) | (1 << 5) | (1 << 4) | (1 << 2) | (1 << 1) | (1 << 0), G18: (1 << 12) | (1 << 11) | (1 << 10) | (1 << 9) | (1 << 8) | (1 << 5) | (1 << 2) | (1 << 0), G15_MASK: (1 << 14) | (1 << 12) | (1 << 10) | (1 << 4) | (1 << 1), getBCHTypeInfo: function (data) {
var d = data << 10; while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15) >= 0) { d ^= (QRUtil.G15 << (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15))); }
return ((data << 10) | d) ^ QRUtil.G15_MASK;
}, getBCHTypeNumber: function (data) {
var d = data << 12; while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18) >= 0) { d ^= (QRUtil.G18 << (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18))); }
return (data << 12) | d;
}, getBCHDigit: function (data) {
var digit = 0; while (data != 0) { digit++; data >>>= 1; }
return digit;
}, getPatternPosition: function (typeNumber) { return QRUtil.PATTERN_POSITION_TABLE[typeNumber - 1]; }, getMask: function (maskPattern, i, j) { switch (maskPattern) { case QRMaskPattern.PATTERN000: return (i + j) % 2 == 0; case QRMaskPattern.PATTERN001: return i % 2 == 0; case QRMaskPattern.PATTERN010: return j % 3 == 0; case QRMaskPattern.PATTERN011: return (i + j) % 3 == 0; case QRMaskPattern.PATTERN100: return (Math.floor(i / 2) + Math.floor(j / 3)) % 2 == 0; case QRMaskPattern.PATTERN101: return (i * j) % 2 + (i * j) % 3 == 0; case QRMaskPattern.PATTERN110: return ((i * j) % 2 + (i * j) % 3) % 2 == 0; case QRMaskPattern.PATTERN111: return ((i * j) % 3 + (i + j) % 2) % 2 == 0; default: throw new Error("bad maskPattern:" + maskPattern); } }, getErrorCorrectPolynomial: function (errorCorrectLength) {
var a = new QRPolynomial([1], 0); for (var i = 0; i < errorCorrectLength; i++) { a = a.multiply(new QRPolynomial([1, QRMath.gexp(i)], 0)); }
return a;
}, getLengthInBits: function (mode, type) { if (1 <= type && type < 10) { switch (mode) { case QRMode.MODE_NUMBER: return 10; case QRMode.MODE_ALPHA_NUM: return 9; case QRMode.MODE_8BIT_BYTE: return 8; case QRMode.MODE_KANJI: return 8; default: throw new Error("mode:" + mode); } } else if (type < 27) { switch (mode) { case QRMode.MODE_NUMBER: return 12; case QRMode.MODE_ALPHA_NUM: return 11; case QRMode.MODE_8BIT_BYTE: return 16; case QRMode.MODE_KANJI: return 10; default: throw new Error("mode:" + mode); } } else if (type < 41) { switch (mode) { case QRMode.MODE_NUMBER: return 14; case QRMode.MODE_ALPHA_NUM: return 13; case QRMode.MODE_8BIT_BYTE: return 16; case QRMode.MODE_KANJI: return 12; default: throw new Error("mode:" + mode); } } else { throw new Error("type:" + type); } }, getLostPoint: function (qrCode) {
var moduleCount = qrCode.getModuleCount(); var lostPoint = 0; for (var row = 0; row < moduleCount; row++) {
for (var col = 0; col < moduleCount; col++) {
var sameCount = 0; var dark = qrCode.isDark(row, col); for (var r = -1; r <= 1; r++) {
if (row + r < 0 || moduleCount <= row + r) { continue; }
for (var c = -1; c <= 1; c++) {
if (col + c < 0 || moduleCount <= col + c) { continue; }
if (r == 0 && c == 0) { continue; }
if (dark == qrCode.isDark(row + r, col + c)) { sameCount++; }
}
}
if (sameCount > 5) { lostPoint += (3 + sameCount - 5); }
}
}
for (var row = 0; row < moduleCount - 1; row++) { for (var col = 0; col < moduleCount - 1; col++) { var count = 0; if (qrCode.isDark(row, col)) count++; if (qrCode.isDark(row + 1, col)) count++; if (qrCode.isDark(row, col + 1)) count++; if (qrCode.isDark(row + 1, col + 1)) count++; if (count == 0 || count == 4) { lostPoint += 3; } } }
for (var row = 0; row < moduleCount; row++) { for (var col = 0; col < moduleCount - 6; col++) { if (qrCode.isDark(row, col) && !qrCode.isDark(row, col + 1) && qrCode.isDark(row, col + 2) && qrCode.isDark(row, col + 3) && qrCode.isDark(row, col + 4) && !qrCode.isDark(row, col + 5) && qrCode.isDark(row, col + 6)) { lostPoint += 40; } } }
for (var col = 0; col < moduleCount; col++) { for (var row = 0; row < moduleCount - 6; row++) { if (qrCode.isDark(row, col) && !qrCode.isDark(row + 1, col) && qrCode.isDark(row + 2, col) && qrCode.isDark(row + 3, col) && qrCode.isDark(row + 4, col) && !qrCode.isDark(row + 5, col) && qrCode.isDark(row + 6, col)) { lostPoint += 40; } } }
var darkCount = 0; for (var col = 0; col < moduleCount; col++) { for (var row = 0; row < moduleCount; row++) { if (qrCode.isDark(row, col)) { darkCount++; } } }
var ratio = Math.abs(100 * darkCount / moduleCount / moduleCount - 50) / 5; lostPoint += ratio * 10; return lostPoint;
}
};
var QRMath = {
glog: function (n) {
if (n < 1) { throw new Error("glog(" + n + ")"); }
return QRMath.LOG_TABLE[n];
}, gexp: function (n) {
while (n < 0) { n += 255; }
while (n >= 256) { n -= 255; }
return QRMath.EXP_TABLE[n];
}, EXP_TABLE: new Array(256), LOG_TABLE: new Array(256)
}; for (var i = 0; i < 8; i++) { QRMath.EXP_TABLE[i] = 1 << i; }
for (var i = 8; i < 256; i++) { QRMath.EXP_TABLE[i] = QRMath.EXP_TABLE[i - 4] ^ QRMath.EXP_TABLE[i - 5] ^ QRMath.EXP_TABLE[i - 6] ^ QRMath.EXP_TABLE[i - 8]; }
for (var i = 0; i < 255; i++) { QRMath.LOG_TABLE[QRMath.EXP_TABLE[i]] = i; }
function QRPolynomial(num, shift) {
if (num.length == undefined) { throw new Error(num.length + "/" + shift); }
var offset = 0; while (offset < num.length && num[offset] == 0) { offset++; }
this.num = new Array(num.length - offset + shift); for (var i = 0; i < num.length - offset; i++) { this.num[i] = num[i + offset]; }
}
QRPolynomial.prototype = {
get: function (index) { return this.num[index]; }, getLength: function () { return this.num.length; }, multiply: function (e) {
var num = new Array(this.getLength() + e.getLength() - 1); for (var i = 0; i < this.getLength(); i++) { for (var j = 0; j < e.getLength(); j++) { num[i + j] ^= QRMath.gexp(QRMath.glog(this.get(i)) + QRMath.glog(e.get(j))); } }
return new QRPolynomial(num, 0);
}, mod: function (e) {
if (this.getLength() - e.getLength() < 0) { return this; }
var ratio = QRMath.glog(this.get(0)) - QRMath.glog(e.get(0)); var num = new Array(this.getLength()); for (var i = 0; i < this.getLength(); i++) { num[i] = this.get(i); }
for (var i = 0; i < e.getLength(); i++) { num[i] ^= QRMath.gexp(QRMath.glog(e.get(i)) + ratio); }
return new QRPolynomial(num, 0).mod(e);
}
};
function QRRSBlock(totalCount, dataCount) { this.totalCount = totalCount; this.dataCount = dataCount; }
QRRSBlock.RS_BLOCK_TABLE = [[1, 26, 19], [1, 26, 16], [1, 26, 13], [1, 26, 9], [1, 44, 34], [1, 44, 28], [1, 44, 22], [1, 44, 16], [1, 70, 55], [1, 70, 44], [2, 35, 17], [2, 35, 13], [1, 100, 80], [2, 50, 32], [2, 50, 24], [4, 25, 9], [1, 134, 108], [2, 67, 43], [2, 33, 15, 2, 34, 16], [2, 33, 11, 2, 34, 12], [2, 86, 68], [4, 43, 27], [4, 43, 19], [4, 43, 15], [2, 98, 78], [4, 49, 31], [2, 32, 14, 4, 33, 15], [4, 39, 13, 1, 40, 14], [2, 121, 97], [2, 60, 38, 2, 61, 39], [4, 40, 18, 2, 41, 19], [4, 40, 14, 2, 41, 15], [2, 146, 116], [3, 58, 36, 2, 59, 37], [4, 36, 16, 4, 37, 17], [4, 36, 12, 4, 37, 13], [2, 86, 68, 2, 87, 69], [4, 69, 43, 1, 70, 44], [6, 43, 19, 2, 44, 20], [6, 43, 15, 2, 44, 16], [4, 101, 81], [1, 80, 50, 4, 81, 51], [4, 50, 22, 4, 51, 23], [3, 36, 12, 8, 37, 13], [2, 116, 92, 2, 117, 93], [6, 58, 36, 2, 59, 37], [4, 46, 20, 6, 47, 21], [7, 42, 14, 4, 43, 15], [4, 133, 107], [8, 59, 37, 1, 60, 38], [8, 44, 20, 4, 45, 21], [12, 33, 11, 4, 34, 12], [3, 145, 115, 1, 146, 116], [4, 64, 40, 5, 65, 41], [11, 36, 16, 5, 37, 17], [11, 36, 12, 5, 37, 13], [5, 109, 87, 1, 110, 88], [5, 65, 41, 5, 66, 42], [5, 54, 24, 7, 55, 25], [11, 36, 12], [5, 122, 98, 1, 123, 99], [7, 73, 45, 3, 74, 46], [15, 43, 19, 2, 44, 20], [3, 45, 15, 13, 46, 16], [1, 135, 107, 5, 136, 108], [10, 74, 46, 1, 75, 47], [1, 50, 22, 15, 51, 23], [2, 42, 14, 17, 43, 15], [5, 150, 120, 1, 151, 121], [9, 69, 43, 4, 70, 44], [17, 50, 22, 1, 51, 23], [2, 42, 14, 19, 43, 15], [3, 141, 113, 4, 142, 114], [3, 70, 44, 11, 71, 45], [17, 47, 21, 4, 48, 22], [9, 39, 13, 16, 40, 14], [3, 135, 107, 5, 136, 108], [3, 67, 41, 13, 68, 42], [15, 54, 24, 5, 55, 25], [15, 43, 15, 10, 44, 16], [4, 144, 116, 4, 145, 117], [17, 68, 42], [17, 50, 22, 6, 51, 23], [19, 46, 16, 6, 47, 17], [2, 139, 111, 7, 140, 112], [17, 74, 46], [7, 54, 24, 16, 55, 25], [34, 37, 13], [4, 151, 121, 5, 152, 122], [4, 75, 47, 14, 76, 48], [11, 54, 24, 14, 55, 25], [16, 45, 15, 14, 46, 16], [6, 147, 117, 4, 148, 118], [6, 73, 45, 14, 74, 46], [11, 54, 24, 16, 55, 25], [30, 46, 16, 2, 47, 17], [8, 132, 106, 4, 133, 107], [8, 75, 47, 13, 76, 48], [7, 54, 24, 22, 55, 25], [22, 45, 15, 13, 46, 16], [10, 142, 114, 2, 143, 115], [19, 74, 46, 4, 75, 47], [28, 50, 22, 6, 51, 23], [33, 46, 16, 4, 47, 17], [8, 152, 122, 4, 153, 123], [22, 73, 45, 3, 74, 46], [8, 53, 23, 26, 54, 24], [12, 45, 15, 28, 46, 16], [3, 147, 117, 10, 148, 118], [3, 73, 45, 23, 74, 46], [4, 54, 24, 31, 55, 25], [11, 45, 15, 31, 46, 16], [7, 146, 116, 7, 147, 117], [21, 73, 45, 7, 74, 46], [1, 53, 23, 37, 54, 24], [19, 45, 15, 26, 46, 16], [5, 145, 115, 10, 146, 116], [19, 75, 47, 10, 76, 48], [15, 54, 24, 25, 55, 25], [23, 45, 15, 25, 46, 16], [13, 145, 115, 3, 146, 116], [2, 74, 46, 29, 75, 47], [42, 54, 24, 1, 55, 25], [23, 45, 15, 28, 46, 16], [17, 145, 115], [10, 74, 46, 23, 75, 47], [10, 54, 24, 35, 55, 25], [19, 45, 15, 35, 46, 16], [17, 145, 115, 1, 146, 116], [14, 74, 46, 21, 75, 47], [29, 54, 24, 19, 55, 25], [11, 45, 15, 46, 46, 16], [13, 145, 115, 6, 146, 116], [14, 74, 46, 23, 75, 47], [44, 54, 24, 7, 55, 25], [59, 46, 16, 1, 47, 17], [12, 151, 121, 7, 152, 122], [12, 75, 47, 26, 76, 48], [39, 54, 24, 14, 55, 25], [22, 45, 15, 41, 46, 16], [6, 151, 121, 14, 152, 122], [6, 75, 47, 34, 76, 48], [46, 54, 24, 10, 55, 25], [2, 45, 15, 64, 46, 16], [17, 152, 122, 4, 153, 123], [29, 74, 46, 14, 75, 47], [49, 54, 24, 10, 55, 25], [24, 45, 15, 46, 46, 16], [4, 152, 122, 18, 153, 123], [13, 74, 46, 32, 75, 47], [48, 54, 24, 14, 55, 25], [42, 45, 15, 32, 46, 16], [20, 147, 117, 4, 148, 118], [40, 75, 47, 7, 76, 48], [43, 54, 24, 22, 55, 25], [10, 45, 15, 67, 46, 16], [19, 148, 118, 6, 149, 119], [18, 75, 47, 31, 76, 48], [34, 54, 24, 34, 55, 25], [20, 45, 15, 61, 46, 16]];
QRRSBlock.getRSBlocks = function (typeNumber, errorCorrectLevel) {
var rsBlock = QRRSBlock.getRsBlockTable(typeNumber, errorCorrectLevel); if (rsBlock == undefined) { throw new Error("bad rs block @ typeNumber:" + typeNumber + "/errorCorrectLevel:" + errorCorrectLevel); }
var length = rsBlock.length / 3; var list = []; for (var i = 0; i < length; i++) { var count = rsBlock[i * 3 + 0]; var totalCount = rsBlock[i * 3 + 1]; var dataCount = rsBlock[i * 3 + 2]; for (var j = 0; j < count; j++) { list.push(new QRRSBlock(totalCount, dataCount)); } }
return list;
};
QRRSBlock.getRsBlockTable = function (typeNumber, errorCorrectLevel) { switch (errorCorrectLevel) { case QRErrorCorrectLevel.L: return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 0]; case QRErrorCorrectLevel.M: return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 1]; case QRErrorCorrectLevel.Q: return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 2]; case QRErrorCorrectLevel.H: return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 3]; default: return undefined; } };
function QRBitBuffer() { this.buffer = []; this.length = 0; }
QRBitBuffer.prototype = {
get: function (index) { var bufIndex = Math.floor(index / 8); return ((this.buffer[bufIndex] >>> (7 - index % 8)) & 1) == 1; }, put: function (num, length) { for (var i = 0; i < length; i++) { this.putBit(((num >>> (length - i - 1)) & 1) == 1); } }, getLengthInBits: function () { return this.length; }, putBit: function (bit) {
var bufIndex = Math.floor(this.length / 8); if (this.buffer.length <= bufIndex) { this.buffer.push(0); }
if (bit) { this.buffer[bufIndex] |= (0x80 >>> (this.length % 8)); }
this.length++;
}
};
var QRCodeLimitLength = [[17, 14, 11, 7], [32, 26, 20, 14], [53, 42, 32, 24], [78, 62, 46, 34], [106, 84, 60, 44], [134, 106, 74, 58], [154, 122, 86, 64], [192, 152, 108, 84], [230, 180, 130, 98], [271, 213, 151, 119], [321, 251, 177, 137], [367, 287, 203, 155], [425, 331, 241, 177], [458, 362, 258, 194], [520, 412, 292, 220], [586, 450, 322, 250], [644, 504, 364, 280], [718, 560, 394, 310], [792, 624, 442, 338], [858, 666, 482, 382], [929, 711, 509, 403], [1003, 779, 565, 439], [1091, 857, 611, 461], [1171, 911, 661, 511], [1273, 997, 715, 535], [1367, 1059, 751, 593], [1465, 1125, 805, 625], [1528, 1190, 868, 658], [1628, 1264, 908, 698], [1732, 1370, 982, 742], [1840, 1452, 1030, 790], [1952, 1538, 1112, 842], [2068, 1628, 1168, 898], [2188, 1722, 1228, 958], [2303, 1809, 1283, 983], [2431, 1911, 1351, 1051], [2563, 1989, 1423, 1093], [2699, 2099, 1499, 1139], [2809, 2213, 1579, 1219], [2953, 2331, 1663, 1273]];
// QRCode object
QRCode = function (canvasId, vOption) {
this._htOption = {
width: 256,
height: 256,
typeNumber: 4,
colorDark: "#000000",
colorLight: "#ffffff",
correctLevel: QRErrorCorrectLevel.H
};
if (typeof vOption === 'string') {
vOption = {
text: vOption
};
}
// Overwrites options
if (vOption) {
for (var i in vOption) {
this._htOption[i] = vOption[i];
}
}
this._oQRCode = null;
this.canvasId = canvasId
if (this._htOption.text && this.canvasId) {
this.makeCode(this._htOption.text);
}
};
QRCode.prototype.makeCode = function (sText) {
this._oQRCode = new QRCodeModel(_getTypeNumber(sText, this._htOption.correctLevel), this._htOption.correctLevel);
this._oQRCode.addData(sText);
this._oQRCode.make();
this.makeImage();
};
QRCode.prototype.makeImage = function () {
var _oContext
if (this._htOption.usingIn) {
_oContext = wx.createCanvasContext(this.canvasId, this._htOption.usingIn)
}
else {
_oContext = wx.createCanvasContext(this.canvasId)
}
var _htOption = this._htOption;
var oQRCode = this._oQRCode
var nCount = oQRCode.getModuleCount();
var nWidth = _htOption.padding ? (_htOption.width - 2 * _htOption.padding) / nCount : _htOption.width / nCount;
var nHeight = _htOption.padding ? (_htOption.height - 2 * _htOption.padding) / nCount : _htOption.height / nCount;
var nRoundedHeight = Math.round(nHeight);
var nRoundedWidth = Math.round(nWidth);
if (_htOption.image && _htOption.image != '') {
_oContext.drawImage(_htOption.image, 0, 0, _htOption.width, _htOption.height)
}
_oContext.setFillStyle('#fff')
_oContext.fillRect(0, 0, _htOption.width, _htOption.height)
_oContext.save()
for (var row = 0; row < nCount; row++) {
for (var col = 0; col < nCount; col++) {
var bIsDark = oQRCode.isDark(row, col);
var nLeft = _htOption.padding ? col * nWidth + _htOption.padding : col * nWidth;
var nTop = _htOption.padding ? row * nHeight + _htOption.padding : row * nHeight;
_oContext.setStrokeStyle(bIsDark ? _htOption.colorDark : _htOption.colorLight)
// _oContext.setStrokeStyle('red')
_oContext.setLineWidth(1)
_oContext.setFillStyle(bIsDark ? _htOption.colorDark : _htOption.colorLight)
// _oContext.setFillStyle('red')
// if (bIsDark) {
_oContext.fillRect(nLeft, nTop, nWidth, nHeight);
// }
// 안티 앨리어싱 방지 처리
// if (bIsDark) {
_oContext.strokeRect(
Math.floor(nLeft) + 0.5,
Math.floor(nTop) + 0.5,
nRoundedHeight
);
_oContext.strokeRect(
Math.ceil(nLeft) - 0.5,
Math.ceil(nTop) - 0.5,
nRoundedWidth,
nRoundedHeight
);
// }
// _oContext.fillRect(
// Math.floor(nLeft) + 0.5,
// Math.floor(nTop) + 0.5,
// nRoundedWidth,
// nRoundedHeight
// );
// _oContext.fillRect(
// Math.ceil(nLeft) - 0.5,
// Math.ceil(nTop) - 0.5,
// nRoundedWidth,
// nRoundedHeight
// );
// _oContext.clearRect(
// Math.floor(nLeft) + 0.5,
// Math.floor(nTop) + 0.5,
// nRoundedWidth,
// nRoundedHeight
// );
// _oContext.clearRect(
// Math.ceil(nLeft) - 0.5,
// Math.ceil(nTop) - 0.5,
// nRoundedWidth,
// nRoundedHeight
// );
}
}
_oContext.draw(false, () => {
setTimeout(() => {
this.exportImage()
}, 800)
})
};
// 保存为图片,将临时路径传给回调
QRCode.prototype.exportImage = function (callback) {
if (this._htOption.callback && typeof this._htOption.callback === 'function') {
wx.canvasToTempFilePath({
x: 0,
y: 0,
width: this._htOption.width,
height: this._htOption.height,
destWidth: this._htOption.width,
destHeight: this._htOption.height,
canvasId: this.canvasId,
success: (res) => {
this._htOption.callback({path: res.tempFilePath})
}
})
}
}
QRCode.CorrectLevel = QRErrorCorrectLevel;
})();
module.exports = QRCode

12
store/index.js

@ -0,0 +1,12 @@
import Vue from 'vue'
import Vuex from 'vuex'
import user from "./modules/user"
Vue.use(Vuex);
export default new Vuex.Store({
modules: {
user: user,
}
})

69
store/modules/user.js

@ -0,0 +1,69 @@
export default {
// 类似 vue 的 data
state: {
location: {
lat: '',
lon: ''
},
userInfo: {
token: ""
}, //保存用户登录信息,
toPath: "", //要跳转过去的页面,
products: "",//订单数据
linkProducts: "",//用来支付的订单数据
coupon: "",//景点下单选择优惠券
eshoppingCart: [],//自提产品购物车下单数据
sshoppingCart: [],//邮寄产品购物车下单数据
meetRoomReserve: {
date: null, //使用日期信息
coupon: null, // 优惠券信息
people: null, //预定人信息
} // 会议预定信息
},
// 类似 vue 里的 mothods(同步方法)
mutations: {
//改变用户信息,自动保存到本地的COOKIE
changeUserInfo(state, data) {
state.userInfo = data;
uni.setStorageSync('userInfo', JSON.stringify(data))
},
changeLoationInfo(state, data) {
uni.setStorageSync('locationInfo', JSON.stringify(data))
state.location = data;
},
changeLoginPath(state) {
var pages = getCurrentPages() //获取加载的页面
var currentPage = pages[pages.length - 1] //获取当前页面的对象
state.toPath = currentPage.$page.fullPath
if(state.toPath == '/pages/login/login') return;
// 登录
uni.navigateTo({
url: '/pages/login/login'
})
},
//订单数据
changeOrderInfo(state, data) {
state.products = data
},
//获取支付订单数据
changelinkProducts(state, data) {
state.linkProducts = data
},
// 选择优惠券
choseCoupon(state, data) {
state.coupon = data
},
// 自提产品购物车下单
changeOrderECart(state, data){
state.eshoppingCart = data
},
// 邮寄产品购物车下单
changeOrderSCart(state, data){
state.sshoppingCart = data
},
//meetRoomReserve修改会议预定信息
changeMeetRoomReserve(state, data){
state.meetRoomReserve = data
}
}
}

8
subPackages/index.vue

@ -0,0 +1,8 @@
<template>
</template>
<script>
</script>
<style>
</style>

147
subPackages/letter/detail.vue

@ -0,0 +1,147 @@
<template>
<view class="bg">
<swiper class="swiper" :autoplay="true" :interval="3000" :duration="1000" circular indicator-dots indicator-color="rgba(255,255,255,.5)"
indicator-active-color="#fff" v-if="detail.images">
<swiper-item v-for="(item, index) in detail.images.split(',')" :key="item.id">
<view class="swiper-item">
<image class="item-img" :src="showImg(item)" mode="aspectFill"></image>
</view>
</swiper-item>
</swiper>
<view class="topBox">
<view class="title text-overflowRows">{{detail.title}}</view>
<view class="flex-between">
<view class="name">
<image :src="showImg(detail.author_img)" mode=""></image>
{{detail.author}}
</view>
<view class="other-info">
<image @click="likeAricle(detail)" v-if="detail.is_star" :src="showImg('/uploads/20250513/00b5680ca4b62479a59b40786979e3e2.png')" mode=""></image>
<image @click="likeAricle(detail)" v-else :src="showImg('/uploads/20250513/99e8d59b9692ae73b3204a6cec79611b.png')" mode=""></image>
<text style="padding-right: 36rpx;">{{handleNum(detail.star)}}</text>
<image :src="showImg('/uploads/20250513/71acef9a784095e9ba17c88580506705.png')" mode=""></image>
{{handleNum(detail.view)}}
</view>
</view>
</view>
<view class="content" v-html="formateRichText(detail.content)"></view>
</view>
</template>
<script>
export default {
data() {
return {
detail: {}
}
},
onLoad(option) {
this.Post({id: option.id},'/api/article/getArticleById').then(res => {
if (res.data.flag == 0) {
uni.showToast({title: '文章不存在或已下架',icon: 'none'})
setTimeout(() => {this.goBack()}, 2000)
}
this.detail = res.data;
});
},
methods: {
likeAricle (item) {
this.Post({article_id: item.id},'/api/article/setArticleStar').then(res => {
if (res.code == 200 || res.code == 1) {
this.detail.is_star = !this.detail.is_star
if (this.detail.is_star) {
this.detail.star++
} else {
this.detail.star--
}
}
});
},
handleNum (num) {
if (num<=0) { return 0 }
if (num>=1000) {
return (num/1000).toFixed(1)+'k'
}
return num
},
},
}
</script>
<style lang="scss" scoped>
.bg {
width: 750rpx;
min-height: 100vh;
background: #FFFFFF;
padding-bottom: 100rpx;
}
.swiper {
height: 408rpx;
.swiper-item {
width: 100%;
height: 408rpx;
.item-img {
width: 750rpx;
height: 408rpx;
}
}
}
.topBox {
width: 100%;
padding: 28rpx 32rpx;
.title {
font-weight: 500;
font-size: 32rpx;
color: #000000;
}
.flex-between{
padding: 28rpx 0;
border-bottom: 2rpx solid #F7F7F7;
}
.name {
font-weight: 400;
font-size: 24rpx;
color: #000000;
display: flex;
align-items: center;
image {
width: 48rpx;
height: 48rpx;
// border-radius: 50%;
margin-right: 12rpx;
}
}
.other-info{
display: flex;
align-items: center;
font-weight: 400;
font-size: 24rpx;
color: #848484;
image{
width: 32rpx;
height: 32rpx;
margin-right: 8rpx;
}
}
}
.content {
width: 100%;
background: #FFFFFF;
padding:0 28rpx;
}
</style>

151
subPackages/letter/index.vue

@ -0,0 +1,151 @@
<template>
<view class="bg">
<img :src="showImg(headImg)" class="topImg" />
<div class="article-container">
<view @click="viewDetail(item)" v-for="(item,index) in list" :key="index" class="item">
<image class="img" :src="showImg(item.image)" mode="aspectFill"></image>
<view class="content flex-column">
<view class="title text-overflowRows">{{item.title}}</view>
<view class="text-overflow" style="font-size: 24rpx;color: #848484;">{{item.subtitle}}</view>
<view class="subtitle flex-between">
<view style="color: #000000;">
<image style="width: 48rpx;height:48rpx" :src="showImg(item.author_img)" mode=""></image>
{{item.author}}
</view>
<view>
<image @click="likeAricle(item)" v-if="item.is_like" :src="showImg('/uploads/20250513/00b5680ca4b62479a59b40786979e3e2.png')" mode=""></image>
<image @click="likeAricle(item)" v-else :src="showImg('/uploads/20250513/99e8d59b9692ae73b3204a6cec79611b.png')" mode=""></image>
<text style="padding-right: 36rpx;">{{handleNum(item.star)}}</text>
<image :src="showImg('/uploads/20250513/71acef9a784095e9ba17c88580506705.png')" mode=""></image>
{{handleNum(item.view)}}
</view>
</view>
</view>
</view>
</div>
</view>
</template>
<script>
export default {
data() {
return {
list: [],
finished: false,
headImg: ''
}
},
onReady() {
this.getArticleByType()
this.getHeadImg(10212).then(res => {this.headImg = res})
},
methods: {
//
getArticleByType() {
this.Post({
type_id: 1,
offset: this.list.length,
limit: 10
},'/api/Article/getArticleByType').then(res => {
if (res.data.length < 10) {
this.finished = true
}
this.list = [...this.list, ...res.data]
})
},
likeAricle (item) {
},
viewDetail(item) {
if (item.url) {
uni.navigateTo({
url:"/subPackages/webPage/webPage?url="+encodeURIComponent(item.url)
})
return
}
uni.navigateTo({
url:'/subPackages/letter/detail?id='+item.id
})
},
handleNum (num) {
if (num<=0) { return 0 }
if (num>=1000) {
return (num/1000).toFixed(1)+'k'
}
return num
},
},
//
onReachBottom() {
setTimeout(() => {
if (!this.finished) this.getArticleByType();
}, 1000);
}
}
</script>
<style lang="scss" scoped>
.bg {
width: 750rpx;
min-height: 100vh;
background: #FFFFFF;
padding-bottom: 50rpx;
}
.topImg {
width: 100%;
height: 380rpx;
}
.article-container{
margin-top: -22rpx;
width: 100%;
background: #FFFFFF;
border-radius: 24rpx ;
position: relative;
padding: 32rpx;
}
.item {
width: 100%;
height: 198rpx;
margin-bottom:30rpx;
display: flex;
.img {
width: 208rpx;
height: 198rpx;
border-radius: 24rpx;
flex-shrink: 0;
}
.content {
width: 370rpx;
flex: 1;
height: 198rpx;
padding: 12rpx 0 0 24rpx;
justify-content: space-between;
.title {
font-weight: 400;
font-size: 28rpx;
color: #000000;
}
.subtitle {
font-weight: 500;
font-size: 27rpx;
color: #848484;
image {
width: 32rpx;
height: 32rpx;
margin-right: 6rpx;
vertical-align: middle;
}
}
}
}
</style>

1651
subPackages/order/detail.vue

File diff suppressed because it is too large

912
subPackages/order/gwcOrder.vue

@ -0,0 +1,912 @@
<template>
<view class="bg" v-if="info">
<view class="address box" @click="changeAddressPopup('open', '', key)" v-if="!contacts && info[0].is_post == 1">
<!-- <image src="https://kunshan.xmainc.com/uploads/20230105/870e4ce1d5a661986d8a54e9f3c2b0b3.png" mode="aspectFill"></image> -->
<view class="text">+ 添加邮寄地址</view>
</view>
<view class="contacts box" v-if="contacts && info[0].is_post == 1">
<view class="contacts-left">
<view class="name-phone">
<view class="name">{{ contacts.name }}</view>
<view class="phone">{{ contacts.tel }}</view>
</view>
<view class="adds text-overflowRows">{{ contacts.province_text + contacts.city_text + contacts.district_text + contacts.detail_addr }}</view>
</view>
<image @click="changeAddressPopup('open', '', key)" src="https://static.ticket.sz-trip.com/taizhou/images/detail/edit.png" mode="aspectFill"></image>
</view>
<view class="commodity box">
<view class="merchant-name">
{{info[0].merchant_name}}
</view>
<view style="margin-top: 20rpx;" class="" v-for="(item,index) in info" :key="item.id">
<view class="goods">
<image class="img" :src="showImg(item.specifications_image)" mode=""></image>
<view class="" style="display: flex;flex-direction: column;justify-content: space-between;">
<view class="">
{{item.good_name}}
</view>
<!-- <view>
<span class="info-tags" v-for="(itemT,indexT) in item.goods_new_tag.split(',').splice(0,2)" :key="indexT">{{itemT}}</span>
</view> -->
</view>
</view>
<view class="sku-info">
<view class="title">
<view class="text-overflowRows" style="font-size: 31rpx;font-weight: 500;color: #000;">{{ item.specifications_name }}</view>
<view class="price-list">
<view class="price-r">{{ item.Specifications_money / 100 }}</view>
<!-- <view class="price-g">¥{{ info.skuInfo.price / 100 }}</view> -->
<view class="price-g" v-if="item.post_money">{{ item.post_money / 100 }}</view>
</view>
</view>
<view class="num-box">
<view class="ctrl" @click="reduce(item)">-</view>
<view class="num">{{item.num}}</view>
<!-- <input class="num" :disabled="true" type="text" v-model="item.num" @input="rge($event)" @blur="setV()" /> -->
<view class="ctrl" @click="plus(item)">+</view>
</view>
</view>
</view>
</view>
<view class="person-info box" v-if="info[0].is_post == 0 && info[0].is_card == 1">
<view class="person-title">购买信息</view>
<view class="p_name line flex">
<view class="left">用户姓名</view>
<input class="input" type="text" placeholder="请输入姓名" v-model="reserve_name" />
</view>
<view class="p_id line flex">
<view class="left">身份证号</view>
<input class="input" type="text" placeholder="请输入身份证号" v-model="reserve_idcard" />
</view>
<view class="p_tel flex">
<view class="left">手机号码</view>
<input class="input" type="text" placeholder="接受预定信息的号码" v-model="reserve_phone" />
</view>
</view>
<!-- 优惠 -->
<view class="youhui">
<view class="" style="font-size: 32rpx;font-weight: bold;color: #000;">优惠</view>
<view class="youhui-price">
<view class="" style="color: #4D526C;font-weight: 500;">
活动促销
</view>
<view class="" style="color: #FC5109;font-weight: bold;">
-{{delPrice}}
</view>
</view>
</view>
<!-- <view class="remark">
<view class="">备注:</view>
<input type="text" placeholder="订单备注" v-model="remark" maxlength="50"/>
</view> -->
<!-- <view class="" style="font-size: 24rpx;font-weight: 500;color: #FC5109;margin-left: 26.7rpx;margin-top: 27.33rpx;">
*温馨提示本商品不支持退换货
</view> -->
<view class="btn-list">
<view class="price-box">
<view class="text">合计:</view>
<view class="price">{{ (allPrice + post)/100}}</view>
<!-- <view class="price">{{ getAllPrice() - disPrice + (post / 100) }}</view> -->
<view class="post" v-if="post">含邮费:¥{{ post / 100 }}</view>
</view>
<view class="btn" @click="order()">立即购买</view>
</view>
<!-- 选择收货地址弹窗 -->
<uni-popup ref="addressPopup" type="bottom" backgroundColor="#F4F4F4">
<view class="people-popup">
<view class="top-box">
<view class="top flex-between">
<text class="text-overflow" @click="changeAddressPopup('close')">取消</text>
<text class="confirm" @click="changeAddressPopup('close', 'confirm')">确定</text>
</view>
</view>
<navigator url="/subPackages/user/myAddressAdd" class="button">添加收货地址</navigator>
<view class="popup-list" v-if="addressList.length > 0">
<view class="popup-item" v-for="(item, index) in addressList" :key="index" @click="seldThisAddress(item)">
<view class="item-top flex-between">
<view>
<view class="name flex-start">
{{ item.name }}
<text>{{ item.tel }}</text>
<text class="tag" v-if="item.is_default == 1">默认</text>
</view>
<view class="subtitle">{{ item.address }}</view>
</view>
<navigator :url="`/subPackages/user/myAddressAdd?id=${item.id}`">
<img src="https://static.ticket.sz-trip.com/taizhou/images/detail/edit.png" alt="" />
</navigator>
</view>
</view>
</view>
</view>
</uni-popup>
</view>
</template>
<script>
export default {
data() {
return {
contacts: null,
info: null,
num: 1,
post: 0,
flag: true,
addressList: [],
reserve_name: '',
reserve_idcard: '',
reserve_phone: '',
remark: '',
allPrice: 0, //
discounts:null,
disPrice: {}, //
delPrice:0
// disPrice: 0
};
},
onLoad() {
this.info = this.$store.state.user.sshoppingCart
console.log('info',this.info);
this.info.forEach(item => {
this.getDiscount(item)
})
if (!this.info) {
uni.navigateBack();
}
console.log('----***info***------',this.info);
},
onShow() {
this.getContacts();
this.getAllAddressList();
// this.getAllPrice()
// this.getTotal()
},
methods: {
//
getAllPrice() {
this.info.forEach(item => {
if ((item.Specifications_money * item.num) - (this.disPrice[item.specifications_id]*100) <= 0) {
item.price1 = this.disPrice[item.specifications_id]
item.price = 0
} else{
item.price1 = this.disPrice[item.specifications_id]
let price = (item.Specifications_money * item.num) - (this.disPrice[item.specifications_id]*100)
item.price = +price.toFixed(2)
}
})
let allPrice = 0
let delPrice = 0
this.info.forEach(item => {
delPrice += item.price1
allPrice += item.price
})
this.delPrice = delPrice
console.log('delPrice',this.delPrice);
this.allPrice = allPrice
},
getContacts() {
if (this.info[0].is_post == 0) {
return;
}
this.Post({}, '/api/user/getDefaultConsignee').then(res => {
if (res) {
this.contacts = res.data;
this.getPost();
}
});
},
getPost() {
if (this.info[0].is_post == 0 || !this.contacts) {
return;
}
console.log(this.info,55555555,this.contacts);
this.flag = false;
let data = this.info.map((item) => {
return { specifications_id:item.specifications_id, num: item.num, consignee_id: this.contacts.id }
})
// let data = [{ specifications_id: this.info.skuInfo.id, num: this.num, consignee_id: this.contacts.id }];
console.log(data);
this.Post({ data:JSON.stringify(data)}, '/api/order/getNewPost').then(res => {
if (res) {
this.post = 0
res.data.forEach((item)=> {
this.post += item.post_money
const postItem = this.info.find(i => i.specifications_id == item.specifications_id)
if(postItem) postItem.post_money = item.post_money
})
this.flag = true;
}
});
},
rge(val) {
this.$nextTick(() => {
this.num = val.detail.value.replace(/^(0+)|[^\d]+/g, '');
});
},
setV() {
if (!this.num) {
this.$nextTick(() => {
this.num = 1;
if (this.flag) {
this.getPost();
}
});
} else {
if (this.flag) {
this.getPost();
}
}
},
plus(item) {
// this.num = Number(this.num);
this.$nextTick(() => {
item.num += 1;
this.getDiscount(item)
// this.disPrice = this.calculateTotalPrice(this.discounts,this.num)
// console.log(this.disPrice);
if (this.flag) {
this.getPost();
}
});
},
reduce(item) {
if (item.num > 1) {
this.$nextTick(() => {
item.num -= 1;
this.getDiscount(item)
// this.disPrice = this.calculateTotalPrice(item.discounts,item.num)
console.log(this.disPrice);
if (this.flag) {
this.getPost();
}
});
}
// this.num = Number(this.num);
// if (this.num > 1) {
// this.$nextTick(() => {
// this.num -= 1;
// this.disPrice = this.calculateTotalPrice(this.discounts,this.num)
// console.log(this.disPrice);
// if (this.flag) {
// this.getPost();
// }
// });
// }
},
//
getDiscount(item) {
this.Post({ specifications_id: item.specifications_id }, '/api/goods/getSpeNumDiscount').then(res => {
this.discounts = res.data.discount_rule;
if (this.discounts) {
let disPrice_item = this.calculateTotalPrice(this.discounts,item.num)
this.disPrice[item.specifications_id] = +disPrice_item
} else {
this.disPrice[item.specifications_id] = 0
}
this.getAllPrice()
});
},
//
calculateTotalPrice(discounts, selectedQuantity) {
if (discounts) {
let totalPrice = 0;
for (const discount of discounts) {
if (selectedQuantity >= discount.num) {
totalPrice = discount.money;
}
}
return totalPrice;
} else {
return 0;
}
},
//
changeAddressPopup(type, confirm, index) {
if (type == 'open') {
this.$refs.addressPopup.open('bottom');
}
else this.$refs.addressPopup.close();
this.$forceUpdate();
},
//
getAllAddressList() {
this.Post({}, '/api/user/consigneeList').then(res => {
if (res.code === 200) this.addressList = res.data;
});
},
//
seldThisAddress(item) {
if (!this.contacts) this.contacts = {};
Object.assign(this.contacts, item);
if (this.flag) {
this.getPost();
}
this.$refs.addressPopup.close();
this.$forceUpdate();
},
//
order() {
if (this.info[0].is_post == 1 && !this.contacts) {
uni.showToast({
title: '请选择收货地址',
icon: 'none'
});
return;
}
if (this.info[0].is_post == 0 && this.info[0].is_card == 1) {
if (!this.reserve_name) {
uni.showToast({
title: '请输入姓名',
icon: 'none'
});
return;
}
if (!this.reserve_phone) {
uni.showToast({
title: '请输入电话',
icon: 'none'
});
return;
}
if (!this.reserve_idcard) {
uni.showToast({
title: '请输入身份证',
icon: 'none'
});
return;
}
}
// let goods = [];
let goods = this.info.map((item) => {
return { specifications_id:item.specifications_id, num: item.num, consignee_id: item.is_post == 1 ? this.contacts.id : null }
})
// let goodsItem = {
// specifications_id: this.info.skuInfo.id,
// num: this.num,
// consignee_id: this.info.skuInfo.is_post == 1 ? this.contacts.id : null
// };
// goods.push(goodsItem);
let data = {
goods: goods,
coupon: null,
remark: this.remark,
is_post: this.info[0].is_post,
reserve_name: this.reserve_name,
reserve_phone: this.reserve_phone,
reserve_idcard: this.reserve_idcard
};
this.Post(
{
method: 'POST',
data: JSON.stringify(data)
},
'/api/order/place'
).then(resT => {
if (resT.code == 200) {
this.Post(
{
order_id: resT.data.order_id,
type: "miniprogram",
// #ifdef MP-WEIXIN
platform: 'miniprogram',
// #endif
// #ifdef H5
platform: 'JSAPI',
// #endif
},
'/api/pay/unify'
).then(res => {
if (res.data) {
// #ifdef MP-WEIXIN
uni.requestPayment({
nonceStr: res.data.nonceStr,
package: res.data.package,
paySign: res.data.paySign,
signType: res.data.signType,
timeStamp: res.data.timeStamp,
complete() {
},
success(){
uni.showLoading({
title:'加载中...'
})
setTimeout(()=>{
uni.hideLoading()
uni.navigateTo({
url: '/subPackages/order/detail?id='+resT.data.order_id
});
},2000)
}
});
// #endif
// #ifdef H5
WeixinJSBridge.invoke('getBrandWCPayRequest', {
appId: res.data.appId,
timeStamp: res.data.timeStamp,
nonceStr: res.data.nonceStr,
package: res.data.package,
signType: res.data.signType,
paySign: res.data.paySign
}, function(Twores) {
if (Twores.err_msg === 'get_brand_wcpay_request:ok') {
uni.showLoading({
title:'加载中...'
})
setTimeout(()=>{
uni.hideLoading()
uni.navigateTo({
url: '/subPackages/order/detail?id='+resT.data.order_id
});
},2000)
} else {
}
});
// #endif
}
});
}
});
}
}
};
</script>
<style lang="scss" scoped>
.bg {
min-height: 100vh;
overflow-x: hidden;
background: #f2f4f7;
padding-bottom: 200rpx;
}
view {
box-sizing: border-box;
}
.box {
width: 710rpx;
min-height: 100rpx;
padding: 30rpx;
background: #ffffff;
border-radius: 16rpx;
margin: 0 auto;
margin-top: 20rpx;
}
.address {
display: flex;
align-items: center;
justify-content: center;
image {
width: 32rpx;
height: 32rpx;
}
.text {
font-size: 28rpx;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #6c7a94;
margin-left: 18rpx;
}
}
.commodity {
// display: flex;
// align-items: center;
.img {
width: 140rpx;
height: 140rpx;
background: #f2f4f7;
border-radius: 10rpx;
margin-right: 18rpx;
}
.title {
width: 300rpx;
margin-left: 20rpx;
font-size: 30rpx;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #000000;
.price-list {
width: 600rpx;
display: flex;
justify-content: space-between;
margin-top: 18rpx;
align-items: center;
.price-r {
font-size: 32rpx;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 500;
color: #FC5109;
&:before {
content: '小计:';
display: inline-block;
color: #000;
font-size: 24rpx;
}
}
.price-g {
font-size: 32rpx;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 500;
color: #FC5109;
&:before {
content: '运费:';
display: inline-block;
color: #000;
font-size: 24rpx;
}
}
}
}
.num-box {
display: flex;
align-items: center;
margin-left: 20rpx;
width: 200rpx;
justify-content: space-between;
.num {
text-align: center;
width: 50rpx;
}
.ctrl {
width: 70rpx;
height: 60rpx;
line-height: 60rpx;
text-align: center;
font-weight: bold;
font-size: 36rpx;
}
}
}
.btn-list {
width: 750rpx;
height: 166rpx;
background: #ffffff;
box-shadow: 0rpx -3rpx 9rpx 1rpx rgba(227, 229, 232, 0.5);
display: flex;
position: fixed;
bottom: 0;
padding: 20rpx 50rpx;
align-items: center;
justify-content: space-between;
.btn {
width: 250rpx;
height: 80rpx;
background: #60989E;
border-radius: 44rpx;
text-align: center;
line-height: 80rpx;
font-size: 32rpx;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #ffffff;
}
.price-box {
display: flex;
align-items: baseline;
.text {
font-size: 28rpx;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #393b3e;
}
.price {
margin-left: 15rpx;
font-size: 48rpx;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #FC5109;
&:before {
content: '¥';
display: inline-block;
color: #FC5109;
font-size: 24rpx;
}
}
.post {
margin-left: 15rpx;
color: #FC5109;
font-size: 24rpx;
}
}
}
.contacts {
display: flex;
align-items: center;
justify-content: space-between;
image {
width: 36rpx;
height: 36rpx;
}
.contacts-left {
.name-phone {
display: flex;
align-items: center;
.name {
font-size: 32rpx;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #222222;
}
.phone {
margin-left: 27rpx;
font-size: 28rpx;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #303030;
}
}
.adds {
font-size: 28rpx;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #6c7a94;
margin-top: 20rpx;
max-width: 500rpx;
}
}
}
.people-popup {
padding: 26rpx;
min-height: 800rpx;
.top-box {
height: 80rpx;
.top {
position: fixed;
left: 0;
right: 0;
color: #000;
height: 80rpx;
font-size: 0;
overflow: hidden;
padding: 0 26rpx;
text {
text-align: left;
font-size: 30rpx;
font-weight: 400;
color: #000000;
}
.confirm {
font-weight: 400;
color: #000000;
}
}
}
.popup-list {
height: 666rpx;
overflow: scroll;
.popup-item {
border-radius: 12rpx;
padding: 0 20rpx;
margin-top: 24rpx;
font-size: 24rpx;
color: #333333;
font-weight: 400;
background-color: #ffffff;
.item-top {
padding: 32rpx;
img {
color: #666666;
width: 40rpx;
height: 40rpx;
}
.name {
font-size: 30rpx;
color: #000000;
overflow: hidden;
text {
color: #999999;
}
.tag {
width: 70rpx;
height: 32rpx;
background: #00d7ed;
border-radius: 7rpx;
line-height: 32rpx;
text-align: center;
font-size: 21rpx;
font-family: PingFang SC;
font-weight: 500;
color: #ffffff;
}
}
.com-flex-start {
margin: 0 0 30rpx;
}
.subtitle {
font-weight: 400;
flex: 1;
text-align: left;
margin-top: 33rpx;
.mobile {
margin-bottom: 36rpx;
}
}
.status {
width: 40rpx;
height: 40rpx;
line-height: 40rpx;
border-radius: 50%;
text-align: center;
box-sizing: border-box;
img {
width: 27rpx;
height: 21rpx;
}
}
.statuss {
background: linear-gradient(90deg, #fa2b66, #ff9834);
border: none;
}
.noSelect {
border: 1rpx solid #999999;
}
}
// .item-site {
// color: #666666;
// display: flex;
// align-items: center;
// padding: 36rpx 0;
// view {
// width: 23rpx;
// height: 23rpx;
// margin-right: 10rpx;
// border: 1rpx solid #999999;
// border-radius: 50%;
// view {
// width: 8rpx;
// height: 8rpx;
// background: #000000;
// border-radius: 50%;
// margin: auto;
// }
// }
// }
}
}
.button {
font-size: 30rpx;
font-weight: 400;
color: #000000;
text-align: center;
width: 100%;
height: 80rpx;
line-height: 80rpx;
background-color: #ffffff;
border-radius: 60rpx;
}
}
.person-info {
padding: 30rpx 30rpx 15rpx 30rpx;
background: #fff;
margin-top: 30rpx;
border-radius: 16rpx;
}
.person-title {
font-size: 32rpx;
font-weight: bold;
color: #000;
}
.line {
border-bottom: 1px solid #e3e5e8;
}
.flex {
display: flex;
align-items: center;
}
.left {
width: 140rpx;
font-size: 28rpx;
font-weight: 500;
color: #4d526c;
height: 104rpx;
line-height: 104rpx;
}
.input {
font-size: 28rpx;
font-weight: 400;
}
.remark {
padding: 30rpx;
display: flex;
align-items: center;
width: 710rpx;
min-height: 150rpx;
background: #ffffff;
border-radius: 16rpx;
margin: 0 auto;
margin-top: 30rpx;
view {
color: #4d526c;
font-size: 28rpx;
}
input {
margin-left: 64rpx;
width: 500rpx;
font-size: 28rpx;
}
}
.info-tags {
padding: 8rpx 7rpx;
text-align: center;
line-height: 35rpx;
font-size: 20rpx;
font-weight: 500;
}
.info-tags:first-child {
background: rgba(252,81,9,.08);
color: #FC5109;
}
.info-tags:last-child {
background: rgba(73,143,239,.1);
color: #498FEF;
}
.merchant-name {
margin-bottom: 24rpx;
}
.goods {
display: flex;
}
.sku-info {
display: flex;
margin-top: 26rpx;
align-items: baseline;
justify-content: space-between;
border-bottom: 1rpx solid #ccc;
padding-bottom: 25.3rpx;
}
.sku-info:last-child {
border:none !important;
}
.youhui {
width: 710rpx;
margin: 0 auto;
margin-top: 22rpx;
padding: 32.67rpx 19.33rpx 32.67rpx 24rpx;
background: #FFFFFF;
border-radius: 13rpx;
}
.youhui-price {
margin-top: 52.67rpx;
display: flex;
justify-content: space-between;
margin-left: .67rpx;
font-size: 27rpx;
}
</style>

702
subPackages/order/trades.vue

@ -0,0 +1,702 @@
<template>
<view class="bg">
<view class="top-bg">
<view class="search-box">
<view class="left">
<image src="https://static.ticket.sz-trip.com/yandu/images/eventCalendar/search.png"
mode="aspectFill"></image>
<input v-model="keywords" type="text" placeholder="请输入关键字" @confirm="search()" />
</view>
<!-- <view class="btn" @click="search()">搜索</view> -->
</view>
<view class="common-box">
<view class="common-types com-flex-tao">
<view @click="setType(index)" v-for="(item, index) in typeList" :key="item.id"
:class="['common-type', typeIndex == index ? 'active' : '']">
{{ item.name }}
</view>
</view>
</view>
</view>
<view class="list-common-empty" v-if="list.length == 0">
<img :src="showImg('/uploads/20221201/8f97261b8eddffcc55342eca0ed0249c.png')" />
<p class="list-common-empty-tip">暂无订单~</p>
</view>
<view class="trade-list" v-if="list.length > 0">
<view v-for="(item, key) in list" :key="item.id" class="trade-items" v-if="showItem(item)"
@click="() => choseType(item)">
<view class="trade-item-head">
<view class="trade-item-head-tid">订单号:{{ item.f_order_id }}</view>
<view class="trade-item-head-state">{{ item.f_status_text }}</view>
</view>
<view class="trade-item-pros">
<view class="trade-item-pro" v-for="(pro, proIndex) in item.data" :key="pro.child_id">
<view class="trade-item-pro-img" v-if="pro.specifications_image">
<image :src="showImg(pro.specifications_image)" mode="aspectFill"></image>
</view>
<view style="flex: 1;">
<view class="trade-item-pro-title">{{ pro.goods_title }}</view>
<view class="trade-item-pro-subtitle">{{ pro.specifications_name }}</view>
</view>
<view class="trade-item-pro-price">
<view class="trade-item-pro-price-pri">{{ pro.pay_money / 100 }}</view>
<view class="trade-item-pro-num">x{{ pro.num }}</view>
</view>
</view>
</view>
<view class="trade-item-info">
合计
<text>{{ item.f_pay_money / 100 }}</text>
</view>
<view class="trade-item-btns">
<view @click.stop="() => refund(item.f_order_id, key)" v-if="item.f_status == 'PAYMENT_SUCCESSFULLY'">
申请退款</view>
<view @click.stop="() => closeOrder(item.f_order_id, item)" v-if="item.f_status == 'WAIT_PAYMENT'">关闭订单
</view>
<view @click.stop="()=>confirmpost(item.f_order_id, key)" v-if="item.data[0].status=='POST'">确认收货</view>
<view class="pay-btn" @click.stop="()=>setOrderId(item.f_order_id, item)" v-if="item.f_status == 'WAIT_PAYMENT'">
立即支付</view>
</view>
</view>
</view>
<!-- <view v-if="list.length === 0 && finished" class="noDate">
<view>暂无订单</view>
</view> -->
</view>
</template>
<script>
export default {
name: 'Trades',
data() {
return {
finished: false,
list: [],
typeList: [{
id: 'ALL',
name: '全部'
},
{
id: 'WAIT_PAYMENT',
name: '待付款'
},
{
id: 'PAYMENT_SUCCESSFULLY',
name: '待使用'
},
{
// id: 'PAYMENT_SUCCESSFULLY',
id: "WAIT_DELIVER",
name: '待发货'
},
{
id: 'POST',
name: '待收货'
},
// {
// id: 'WAIT_COMMENT',
// name: ''
// },
{
id: 'WAIT_REFUND,REFUND_SUCCESS,REFUND_REFUSAL,REFUND_ERROR,REFUND_PART',
name: '退款/售后'
}
],
typeIndex: 0,
ajaxFlag: true,
keywords: '',
orderId: null,
dateRange: [],
type: ''
};
},
onLoad(options) {
console.log(options);
if (options.type) this.typeIndex = this.typeList.findIndex(vm => vm.name === options.type);
uni.$on("updateDataByConnect", this.getDataByConnect)
},
onShow() {
this.getList();
},
onUnload() {
uni.$off("updateDataByConnect", this.getDataByConnect)
},
onReachBottom() {
if (this.finished) return false;
this.getList();
},
methods: {
getDataByConnect(data) {
if (data.msgType == "updateOrderTrades") {
this.list = [];
this.finished = false;
this.getList()
}
},
emptyFunc() {
},
showItem(item) {
let flag = true;
// if (this.typeIndex == 2 && item.order_child[0] && !item.order_child[0].consignee) flag = false
return flag;
},
onReload() {
this.list = [];
this.finished = false;
this.getList();
},
setType(index) {
this.typeIndex = index;
this.onReload();
},
setOrderId(id, item) {
console.log(id, item)
let that = this;
that.orderId = id;
that.Post({
order_id: id,
type: "miniprogram",
platform: 'miniprogram'
},
'/api/pay/unify'
).then(res => {
if (res.data) {
uni.requestPayment({
nonceStr: res.data.nonceStr,
package: res.data.package,
paySign: res.data.paySign,
signType: res.data.signType,
timeStamp: res.data.timeStamp,
success: () => {
const templateIds = [
// 退
'hRZoiEES2BWtKb6Xgsnn8khLQH9un5j_11qu0bwlhfE',
//
'YyTCUIYBnrj9CyKks8cOjNX_Rk8a4yVdswMP-zXVbhc'
]
uni.requestSubscribeMessage({
tmplIds: templateIds,
complete (res) {
that.list = [];
that.finished = false;
that.getList()
}
})
}
});
}
});
},
//
confirmpost(id, index) {
let that = this;
uni.showModal({
title: '提示',
content: '是否确认收货?',
success: successRes => {
if (successRes.confirm) {
that.Post({
order_id: id
},
'/api/order/confirmPost'
).then(res => {
if (res.code == 200) {
// list[index].order_child.map(item => {
// item.status = 'WAIT_COMMENT';
// });
// list[index].status = 'WAIT_COMMENT';
// list[index].postFlag = false;
// that.list = list;
uni.showToast({
title: '操作成功'
});
// that.$forceUpdate();
that.list = [];
that.finished = false;
that.getList();
}
});
}
}
});
},
//
closeOrder(id, index) {
console.log(id, index)
let that = this;
uni.showModal({
title: '提示',
content: '是否关闭订单?',
success: successRes => {
if (successRes.confirm) {
that.Post({
order_id: id
},
'/api/order/closeOrder'
).then(res => {
if (res.code == 200) {
uni.showToast({
title: '关闭成功',
icon: 'success'
});
that.list = [];
that.finished = false;
that.getList();
}
});
}
}
});
},
//
deletOrder(id) {
let that = this;
uni.showModal({
title: '提示',
content: '是否删除订单?',
success: successRes => {
if (successRes.confirm) {
that.Post({
order_id: id
},
'/api/order/delOrder'
).then(res => {
if (res.code == 200) {
uni.showToast({
title: '删除成功',
icon: 'success'
});
that.list = [];
that.finished = false;
that.getList();
}
});
}
}
});
},
// 退
refund(id, index) {
let that = this;
uni.showModal({
title: '提示',
content: '是否申请退款?',
success: successRes => {
if (successRes.confirm) {
that.Post({
order_id: id
},
'/api/order/applyRefund'
).then(res => {
if (res.code == 200) {
uni.showToast({
title: '申请成功',
icon: 'success'
});
that.onReload();
}
});
}
}
});
},
search(e) {
this.list = [];
this.getList();
// if (e.keyCode == 13) {
// //
// this.keywords = e.target.value;
// }
},
getList() {
let data = {
status: this.typeList[this.typeIndex].id == 'ALL' ? '' : this.typeList[this.typeIndex].true_id ||
this.typeList[this.typeIndex].id,
offset: this.list.length,
limit: 5,
name: this.keywords,
type: this.typeList[this.typeIndex].name == '待使用' ? 1 : (this.typeList[this.typeIndex].name ==
'待发货' ? 2 : '')
};
this.Post(data, '/api/order/orderList').then(res => {
let arr = res.data;
arr.forEach(item => {
console.log(item)
item.order_child.forEach((itemX, index) => {
if (index > 0) {
item.order_child[0].data = [...item.order_child[0].data, ...itemX
.data
];
} else {
itemX.f_order_id = item.order_id;
itemX.f_status_text = item.status_text;
itemX.f_status = item.status;
itemX.f_pay_money = item.pay_money;
console.log('1',itemX);
this.list.push(itemX);
}
});
});
if (res.data.length < 5) {
this.finished = true;
}
});
},
UpdateOrder(id) {
this.ajaxFlag = false;
let list = this.list;
this.Post({
order_id: id
},
'/api/order/orderDetail'
).then(res => {
this.ajaxFlag = true;
list.map(item => {
if (item.order_id == id) {
item = res.data;
}
});
this.list = list;
});
},
choseType(item) {
uni.navigateTo({
url: '/subPackages/order/detail?id=' + item.f_order_id
});
},
}
};
</script>
<style scoped lang="scss">
view {
box-sizing: border-box;
}
.common-box {
height: 90rpx;
}
.common-types {
background: white;
height: 90rpx;
font-weight: 500;
font-size: 31rpx;
color: #333333;
z-index: 10;
margin: auto;
color: #666;
overflow-x: scroll;
overflow-y: hidden;
padding: 0 27rpx;
}
.common-types::-webkit-scrollbar {
width: 0rpx;
height: 0;
display: none;
}
.common-type {
flex-shrink: 0;
margin: 0 26rpx;
line-height: 90rpx;
height: 90rpx;
position: relative;
}
.common-type.active {
font-weight: bold;
font-size: 31rpx;
color: #000000;
}
.common-type.active:after {
display: block;
width: 60%;
font-size: 0;
content: '1';
margin: auto;
position: absolute;
left: 0;
right: 0;
bottom: 1rpx;
height: 4rpx;
background: #74A5AA;
border-radius: 2rpx;
}
.bg {
min-height: 100vh;
background-color: #f7f7f7;
}
.noDate {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-top: 200rpx;
}
.noDate img {
width: 514rpx;
height: auto;
}
.noDate view {
font-size: 24rpx;
color: #777777;
}
.trade-list {
padding: 28rpx 26rpx;
}
.trade-items {
background-color: white;
margin-bottom: 28rpx;
border-radius: 20rpx;
}
.trade-item-head {
display: flex;
justify-content: space-between;
padding: 28rpx 20rpx;
border-bottom: 1rpx solid #d8d8d8;
}
.trade-item-head-tid {
font-size: 24rpx;
display: flex;
align-items: center;
color: #666666;
}
.trade-item-head-state {
font-size: 27rpx;
font-family: PingFang SC;
font-weight: bold;
color: #FC5109;
}
.trade-item-head-name {
display: flex;
align-items: center;
font-size: 31rpx;
font-family: PingFang SC;
font-weight: bold;
color: #333333;
image {
width: 33rpx;
height: 31rpx;
}
view {
margin-left: 13rpx;
}
}
.trade-item-pros {
display: flex;
/* background-color: #F2F2F2; */
flex-direction: column;
}
.trade-item-pro {
display: flex;
padding: 20rpx;
justify-content: space-between;
}
.trade-item-pro-img {
display: flex;
justify-content: center;
align-items: center;
}
.trade-item-pro-img image {
width: 180rpx;
height: 180rpx;
border-radius: 10rpx;
}
.trade-item-pro-price {
display: flex;
flex-direction: column;
}
.trade-item-pro-title {
text-align: left;
flex: 1;
padding: 0 20rpx;
font-size: 28rpx;
}
.trade-item-pro-subtitle {
text-align: left;
flex: 1;
padding: 0 20rpx;
font-weight: 500;
font-size: 24rpx;
color: #888888;
margin-top: 20rpx;
}
.trade-item-pro-price view {
display: flex;
flex-wrap: nowrap;
text-wrap: none;
white-space: nowrap;
justify-content: flex-end;
}
.trade-item-pro-price-pri {
font-size: 27rpx;
color: #C3282E;
font-weight: 500;
color: #333333;
}
.trade-item-pro-num {
font-size: 24rpx;
color: #666666;
margin-top: 24rpx;
}
.trade-item-info {
font-size: 28rpx;
display: flex;
justify-content: flex-end;
align-items: center;
background-color: white;
padding: 0rpx 20rpx;
margin-top: -6rpx;
/* border-bottom: 1px solid #B6B6B6; */
}
.trade-item-info text {
font-size: 36rpx;
font-weight: bold;
color: rgba(238, 0, 0, 1);
}
.trade-item-btns {
display: flex;
flex-direction: row;
justify-content: flex-end;
align-items: center;
padding: 20rpx;
}
.trade-item-btns view {
margin-left: 20rpx;
background: rgba(237, 237, 237, 0);
border: 1rpx solid #999999;
border-radius: 27rpx;
padding: 8rpx 16rpx;
font-size: 27rpx;
font-family: PingFang SC;
font-weight: 500;
color: #333333;
}
.trade-item-btns .pay-btn {
color: #FFFFFF;
background: #74A5AA;
border: none;
padding: 10rpx 16rpx;
}
.comment-btn {
width: 100rpx;
text-align: center;
line-height: 40rpx;
border-radius: 20rpx;
border: 1px solid #999999;
color: #333333;
justify-content: center !important;
font-size: 24rpx;
margin-top: 16rpx;
}
.list-common-empty {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 50vh;
}
.list-common-empty img {
width: 483rpx;
height: 254rpx;
}
.list-common-empty-tip {
margin-top: 61rpx;
font-size: 29rpx;
font-family: PingFang SC;
font-weight: 500;
color: #8599b5;
}
.com-flex-tao {
display: flex;
justify-content: space-between;
align-items: center;
}
.search-box {
width: 697rpx;
height: 67rpx;
background: #f2f2f2;
border-radius: 33rpx;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 8rpx 0 28rpx;
margin: 0 auto;
margin-bottom: 20rpx;
.left {
display: flex;
align-items: center;
image {
width: 28rpx;
height: 30rpx;
}
input {
margin-left: 20rpx;
font-size: 31rpx;
font-weight: 400;
color: #333;
width: 450rpx;
}
}
.btn {
width: 107rpx;
height: 53rpx;
background: #71B580;
border-radius: 27rpx;
font-size: 28rpx;
font-weight: 400;
color: #ffffff;
line-height: 53rpx;
text-align: center;
}
}
.top-bg {
background: #fff;
padding-top: 20rpx;
}
</style>

115
subPackages/shiguang/index.vue

@ -0,0 +1,115 @@
<template>
<view class="bg">
<image :src="showImg(headImg)" mode="aspectFill" class="topImg"></image>
<view class="box">
<view v-for="(item,index) in list" :key="index" class="item" @click="gotoVideo(item)">
<view style="display: flex;align-items: center;">
<image :src="showImg(item.author_img)" mode="aspectFill" class="profile"></image>
<view class="name">{{item.author}}</view>
</view>
<view class="title">
{{item.title}}
</view>
<view class="video-box" :style="{backgroundImage: 'url('+showImg(item.image)+')'}">
<image src="https://static.ticket.sz-trip.com/cgc/images/index/plays.png" mode="" class="play"></image>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
list: [],
headImg: '',
}
},
onReady() {
this.getHeadImg(10213).then(res => {this.headImg = res})
this.Post({
type_id: 2,
offset: 0,
limit: 100
}, '/api/article/getArticleByType').then(res => {
if(res.data) {
this.list = res.data
}
})
},
methods: {
gotoVideo(item) {
uni.navigateTo({
url: '/subPackages/video/video?item=' + encodeURIComponent(JSON.stringify(item))
})
},
}
}
</script>
<style lang="scss" scoped>
.bg {
min-height: 100vh;
background: #FFFFFF;
padding-bottom: 100rpx;
}
.topImg {
width: 750rpx;
height: 380rpx;
}
.box {
margin-top: -24rpx;
border-radius: 24rpx 24rpx 0 0;
padding: 0 32rpx;
.item {
padding: 32rpx 0;
.profile {
width: 48rpx;
height: 48rpx;
border-radius: 50%;
margin-right: 16rpx;
}
.name {
font-weight: 500;
font-size: 28rpx;
color: #000000;
}
.title {
font-weight: 400;
font-size: 28rpx;
color: #000000;
margin-top: 24rpx;
}
.video-box {
width: 686rpx;
height: 372rpx;
border-radius: 32rpx;
margin-top: 20rpx;
background-size: 100% 100%;
position: relative;
.play {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
width: 56rpx;
height: 56rpx;
}
}
}
.item:nth-child(n+2) {
border-top: 2rpx solid rgba(0,0,0,0.05);
}
}
</style>

790
subPackages/techan/detail.vue

@ -0,0 +1,790 @@
<template>
<view class="bg" id="bg" v-if="info">
<view class="swipe-box">
<swiper class="swiper" :indicator-dots="false" :autoplay="false" :interval="3000" :duration="1000" circular>
<swiper-item v-if="info && info.videourl">
<video
:src="showImg(info.videourl)"
id="detailVideo"
:poster="showImg(info.image)"
@play="play"
@error="videoErrorCallback"
controls
style="width: 100%;height: 100%;"
object-fit="cover"
></video>
</swiper-item>
<swiper-item v-for="(item, index) in info.list_images.split(',')" :key="item.id">
<view class="swiper-item">
<image class="item-img" :src="showImg(item)" mode="aspectFill"></image>
</view>
</swiper-item>
</swiper>
<view class="swiper-item-num">{{ info.list_images.split(',').length }}</view>
</view>
<view class="w-full relative" style="padding: 26rpx 0;top: -52rpx;">
<view class="price-box ">
<view class="price-zan">
<view class="price">
<view class="present-price">{{ info.money / 100 }}</view>
</view>
</view>
<view class="tag no-scrollbar" v-if="info.goods_new_tag">
<view class="tag-item" v-for="(item, index) in info.goods_new_tag.split(',')" :key="index">
{{ item }}
</view>
</view>
<view class="title">{{ info.title }}</view>
</view>
<view class="sku-box" v-if="sku.length>0">
<view class="sku-item" v-for="(item,i) in sku" :key="i">
<image :src="showImg(item.image)"></image>
<view class="sku-content">
<view class="flex-1">{{item.title}}</view>
<view class="present-price">{{item.money/100}}</view>
</view>
</view>
</view>
<view class="notice" v-if="info.reserve_content">
<view class="title">购买须知</view>
<view class="rich-text" v-html="formateRichText(info.reserve_content)"></view>
</view>
<view class="notice" >
<view class="title">商品须知</view>
<view class="rich-text" v-html="formateRichText(info.special_content)"></view>
</view>
</view>
<template v-if="!popShow">
<view class="btn-list" v-if="!paramData.showCart">
<view class="left-box">
<view class="img-box" slot="content">
<uni-badge class="uni-badge-left-margin" :text="paramData.num" absolute="rightTop" :offset="[5, 5]" size="small"
:custom-style="{background:'#D90F01',color:'#ffffff',border:'1px solid #ffffff'}">
<image @click.stop="showCartClick" :src="showImg('/uploads/20250513/29fd015f8c779f08e53d1016d3643c58.png')" mode="aspectFill"
style="width: 60rpx;height: 50rpx;"></image>
</uni-badge>
<text class="text">购物车</text>
</view>
</view>
<view class="btn-post">
<view class="left-btn-buy" @click="openPop(true)">加入购物车</view>
<view class="right-btn-buy" @click="openPop(false)">立即购买</view>
</view>
</view>
<view class="btn-list" v-else>
<view class="left-box">
<view class="img-box" slot="content">
<uni-badge class="uni-badge-left-margin" :text="paramData.num" absolute="rightTop" :offset="[5, 5]" size="small"
:custom-style="{background:'#D90F01',color:'#ffffff',border:'1px solid #ffffff'}">
<image @click.stop="showCartClick" :src="showImg('/uploads/20250513/29fd015f8c779f08e53d1016d3643c58.png')" mode="aspectFill"
style="width: 60rpx;height: 50rpx;"></image>
</uni-badge>
<text class="text">购物车</text>
</view>
<view class="bottom-price">
<text style="color: #000000;">总计</text>
<view class="bottom-price-yuan">{{paramData.iNum}}</view>
<view>.{{paramData.fNum}}</view>
</view>
</view>
<view class="btn-buy" @click="goCartOrder">
去结算
</view>
</view>
</template>
<cartDataVue ref="cartDataVueRef" :paramData="paramData" @changeParamData="changeParamData" :key="new Date().getTime()"></cartDataVue>
<uni-popup ref="popup" type="bottom" @change="changPopShow"
:safe-area="false" style="position: relative;z-index: 50;">
<view class="popup-content" v-if="sku.length>0">
<view @click="closePopup" style="padding: 31rpx 0 0 639rpx;width: 50rpx;height: 80rpx;">
<uni-icons type="closeempty" size="24"></uni-icons>
</view>
<view class="bottom-productImg">
<img :src="showImg(sku[productIndex].image)" alt="">
<view class="right-content">
<view class="bottom-productPrice com-price">{{(sku[productIndex].money||0)/100}}</view>
<view class="bottom-content text-overflow">已选择{{sku[productIndex].title}}</view>
</view>
</view>
<view>
<view class="sp">
规格
</view>
<view style="display: flex;align-items: center;justify-content: space-between;flex-wrap: wrap;">
<view style="position:relative;" v-for="(botItem,botIndex) in sku" :key="botIndex">
<view :class="['botProduct','text-overflow',{'noStore':botItem.store==0},{'botProducts':productIndex==botIndex}]"
@click="changeProduct(botItem,botIndex)">
{{botItem.title}}
</view>
<view class="noStore-text" v-if="botItem.store==0">
不可购买
</view>
<!-- <view class="noStore-text" v-else-if="is_post==1&&botItem.is_post!=1">不可加购</view> -->
</view>
</view>
</view>
<view class="buy-num com-flex-tao">
数量
<view class="number-btn">
<view>
<text @click="delNumber">-</text>
</view>
<view style="width: 96rpx;height: 69rpx;margin: 0 14rpx;">{{ buyNum }}</view>
<view>
<text @click="addNumber">+</text>
</view>
</view>
</view>
</view>
<!-- <view style="height: 100rpx;"></view> -->
<view class="btn-box">
<view class="buy-btn" @click="openPop(addCartOrOrder)">
{{addCartOrOrder?'加入购物车':'立即购买'}}
</view>
</view>
</uni-popup>
</view>
</template>
<script>
import cartDataVue from '../../components/cartData.vue'
export default {
components: {cartDataVue},
data() {
return {
id: null,
info: null,
sku: [],
productIndex: 0,
is_post: "1",
isCollect: false,
showLength: 0,
buyNum: 1,
popShow: false,
paramData: {allPrice: 0,iNum:0, fNum:'00', showCart: false, num: 0},
addCartOrOrder: null, //true false
};
},
onLoad(option) {
this.id = option.id;
this.getInfo();
this.getSpecificationsByGoodsId();
},
methods: {
changPopShow (e) {
this.popShow = e.show
},
getInfo() {
this.Post({
goods_id: this.id
},
'/api/goods/getGoodDetail'
).then(res => {
if (res.data.flag == 0) {
setTimeout(() => {
uni.showToast({
title: '商品不存在或已下架',
icon: 'none'
})
}, 0)
setTimeout(() => {
this.goBack()
}, 2000)
}
this.info = res.data;
// this.isCollect = this.info.is_collect;
if (this.info.title) {
uni.setNavigationBarTitle({
title: this.info.title
})
}
});
},
getSpecificationsByGoodsId() {
this.Post({
goods_id: this.id
},
'/api/goods/getSpecificationsByGoodsId'
).then(res => {
if (res) {
this.sku = res.data || [];
// todo
// this.is_post = "2"
// if (this.sku.length>0 && this.sku.some(v=>v.is_post == 1)) {
// this.is_post = "1"
// }
}
});
},
//
collect() {
this.Post({
type: 5,
id: this.id
},
'/api/scenic/collect'
).then(res => {
if (res) {
uni.showToast({
title: res.msg,
icon: 'none'
});
this.isCollect = !this.isCollect
}
});
},
//
addNumber() {
this.buyNum += 1;
},
delNumber() {
if (this.buyNum <= 1) {
return;
}
this.buyNum -= 1;
},
closePopup() {
this.$refs.popup.close()
},
// false true
openPop(flag) {
if (!this.sku||this.sku.length<=0) {
uni.showToast({
title:'暂无可选规格',
icon:'none'
})
return
}
if (!this.popShow) {
this.$refs.popup.open()
this.addCartOrOrder = flag
} else {
if (flag) {
this.addToCart()
} else {
this.order()
}
}
},
order(item) {
let goods = this.sku[this.productIndex]
goods.buyNum = this.buyNum
let orderInfo = {
// is_post: goods.is_post || "2",
is_post: 1,
goods: [{goodsInfo: this.info, skuInfo: goods }],
post: 0,
}
uni.setStorageSync('teChanOrder', JSON.stringify(orderInfo)); //
// uni.setStorageSync('teChanInfo', JSON.stringify(this.info)); //
uni.navigateTo({
url: '/subPackages/techan/order'
});
},
addToCart () {
let goods = this.sku[this.productIndex]
goods.buyNum = this.buyNum
let goodsInfo = {goodsInfo:this.info, skuInfo: goods, isSelected: true}
this.Post({good_id: this.info.id, specifications_id: goods.id,num: this.buyNum },
'/api/shopping/addShopping').then(res => {
if (res) {
let selectedData = []
try {
selectedData = JSON.parse(uni.getStorageSync('cartDataInfo'));
} catch(e) {
selectedData = []
}
let currentGoods = selectedData.find(v =>v==goods.id)
if (!currentGoods) {
selectedData.push(goods.id)
}
uni.setStorageSync('cartDataInfo', JSON.stringify(selectedData));
uni.$emit("updateDataByConnect", {msgType:'updateCartDataInfo',data:null})
this.closePopup()
// this.$refs.cartDataVueRef.openPop()
}
});
},
goUser() {
uni.switchTab({
url: '/pages/index/user'
})
},
changeProduct(item,index) {
if (item.store==0) {
uni.showToast({
title:"库存不足!",
icon:'none'
})
return
}
this.productIndex = index
},
//
changeParamData (data) {
for(let key in this.paramData) {
this.paramData[key] = data[key]
}
},
showCartClick () {
if (this.paramData.showCart) {
this.$refs.cartDataVueRef.closePopup()
} else {
this.$refs.cartDataVueRef.openPop()
}
},
goCartOrder () {
this.$refs.cartDataVueRef.goCartOrder()
},
},
onReachBottom() {
}
};
</script>
<style lang="scss" scoped>
.bg {
min-height: 100vh;
overflow-x: hidden;
background: #f2f4f7;
padding-bottom: 210rpx;
}
view {
box-sizing: border-box;
}
.swipe-box {
height: 750rpx;
position: relative;
.swiper-item-num {
width: 90rpx;
height: 40rpx;
background: rgba(0, 0, 0, 0.5);
border-radius: 20rpx;
font-size: 24rpx;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #ffffff;
text-align: center;
line-height: 40rpx;
position: absolute;
right: 30rpx;
bottom: 50rpx;
}
}
.swiper {
height: 750rpx;
position: relative;
.swiper-item {
width: 100%;
height: 750rpx;
.item-img {
width: 750rpx;
height: 750rpx;
}
}
}
.price-box {
width: 100%;
background: #ffffff;
border-radius: 20rpx 20rpx 0 0;
padding: 24rpx 30rpx;
.price-zan {
display: flex;
align-items: center;
justify-content: space-between;
.price {
display: flex;
}
}
.tag {
margin:24rpx 0 15rpx 0;
display: flex;
align-items: center;
flex-wrap: nowrap;
overflow-x: auto;
.tag-item {
margin-right: 10rpx;
padding: 0rpx 15rpx;
flex-shrink: 0;
font-weight: 500;
font-size: 23rpx;
color: #60989E;
height: 37rpx;
line-height: 37rpx;
background: rgba(96, 152, 158, 0.2);
border-radius: 4rpx;
}
}
.title {
margin-top: 22rpx;
font-family: PingFang;
font-weight: bold;
font-size: 31rpx;
color: #000000;
}
}
.present-price {
font-size: 42rpx;
font-family: PingFang SC;
font-weight: 500;
color: #FC5109;
&:before {
content: '¥';
display: inline-block;
font-size: 26rpx;
}
&:after {
content: '起';
display: inline-block;
font-size: 24rpx;
font-family: PingFangSC;
color: #B5BCC9;
}
}
.notice {
width: 700rpx;
margin: 25rpx auto 0;
background: #ffffff;
border-radius: 20rpx;
padding: 40rpx 25rpx;
.title{
font-weight: bold;
font-size: 36rpx;
color: #000000;
margin-bottom: 30rpx;
}
}
.btn-list {
position: fixed;
z-index: 9999;
bottom: 0;
width: 750rpx;
height: 180rpx;
background: #FFFFFF;
box-shadow: 0rpx 0rpx 16rpx 0rpx rgba(6, 0, 1, 0.1);
display: flex;
justify-content: space-between;
padding: 30rpx 25rpx 0 50rpx;
.left-box {
display: flex;
align-items: flex-start;
.bottom-price{
display: flex;
align-items: baseline;
font-family: PingFang SC;
font-weight: bold;
font-size: 27rpx;
color: #D70000;
padding-top: 20rpx;
.bottom-price-yuan{
font-size: 40rpx;
}
}
.bottom-detail-icon{
font-size: 24rpx;
display: flex;
align-items: center;
}
.img-box {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-right: 50rpx;
image {
width: 48rpx;
height: 48rpx;
}
.text {
font-weight: 500;
font-size: 22rpx;
color: #4D526C;
}
}
}
.btn-buy {
width: 240rpx;
height: 78rpx;
background: #74A5AA;
border-radius: 40rpx;
text-align: center;
line-height: 78rpx;
font-size: 32rpx;
font-family: PingFangSC;
font-weight: 500;
color: #FFFFFF;
}
}
.popup-content {
background-color: white;
padding: 0rpx 39rpx 51rpx 39rpx;
height: auto;
border-radius: 20rpx 20rpx 0 0;
.bottom-productImg {
display: flex;
margin-bottom: 23rpx;
img {
width: 218rpx;
height: 179rpx;
background: #666666;
border-radius: 13rpx;
}
}
.right-content {
margin: 10rpx 0 0 41rpx;
}
.bottom-productPrice {
font-size: 40rpx;
color: #FC524B;
&:before {
content: "¥";
font-size: 26rpx;
}
}
.bottom-content {
width: 331rpx;
font-size: 27rpx;
font-family: PingFang SC;
font-weight: 400;
color: #666666;
}
}
.botProduct {
width: 320rpx;
// height: 78rpx;
border-radius: 13rpx;
background-color: #EFEFEF;
font-size: 29rpx;
font-family: PingFang SC;
font-weight: 400;
color: #333333;
line-height: 78rpx;
text-align: center;
margin-bottom: 25rpx;
display: inline-block;
position: relative;
padding: 0 40rpx;
}
.noStore{
background-color: rgba(239, 239, 239, 1);
color: rgba(153, 153, 153, 1);
}
.noStore-text{
width: 113rpx;
height: 43rpx;
background: #C0C0C0;
border-radius: 7rpx 0rpx 7rpx 0rpx;
text-align: center;
line-height: 43rpx;
position: absolute;
right: -14rpx;
top: -20rpx;
font-size: 23rpx;
font-family: PingFangSC;
font-weight: 400;
color: #FFFFFF;
}
.botProducts {
// border: 1rpx solid #00AAFF;
// background-color: rgba(254, 180, 25, 1);
background: #74A5AA;
color: #FFFFFF;
}
.buy-num {
font-size: 29rpx;
font-family: PingFang SC;
font-weight: 400;
color: #333333;
// border-top: 1rpx solid #CCCCCC;
padding: 39rpx 0;
display: flex;
align-items: center;
justify-content: space-between;
.number-btn {
display: flex;
}
}
.buy-num .number-btn view {
display: flex;
justify-content: center;
align-items: center;
width: 69rpx;
height: 69rpx;
border: 1rpx solid #CCCCCC;
border-radius: 7rpx;
}
.buy-num .number-btn>view text {
font-size: 46rpx;
}
.buy-btn {
width: 670rpx;
height: 78rpx;
text-align: center;
line-height: 78rpx;
background: #74A5AA;
border-radius: 40rpx;
font-size: 34rpx;
font-family: PingFangSC;
font-weight: 500;
color: #FFFFFF;
}
.btn-box {
width: 750rpx;
height: 151rpx;
background: #FFFFFF;
box-shadow: 0rpx 0rpx 16rpx 0rpx rgba(6, 0, 1, 0.1);
display: flex;
align-items: center;
justify-content: center;
}
.sp {
width: 100%;
height: 30rpx;
font-size: 32rpx;
font-family: PingFangSC;
font-weight: 400;
color: #060001;
line-height: 30rpx;
border-top: solid 2rpx #ccc;
margin: 60rpx 0;
padding-top: 30rpx;
}
.btn-post{
font-weight: 500;
font-size: 32rpx;
color: #60989E;
line-height: 77rpx;
text-align: center;
display: flex;
.left-btn-buy{
width: 186rpx;
height: 77rpx;
color: #60989E;
border-radius: 39rpx 0rpx 0rpx 39rpx;
background: rgba(116, 165, 170, 0.3);
}
.right-btn-buy{
width: 187rpx;
height: 77rpx;
background: #74A5AA;
border-radius: 0rpx 39rpx 39rpx 0rpx;
color: white;
}
}
.sku-box{
width: 700rpx;
margin: 25rpx auto 0;
background: #FFFFFF;
border-radius: 20rpx;
.sku-item{
// border-radius: 20rpx;
width: 100%;
padding: 14rpx;
display: flex;
border-bottom: 1px solid #D8D8D8;
.sku-content{
display: flex;
padding-left: 16rpx;
flex: 1;
width: 1rpx;
}
image{
width: 160rpx;
height: 160rpx;
border-radius: 13rpx;
flex-shrink: 0;
}
.flex-1{
font-weight: 500;
font-size: 29rpx;
color: #000000;
height: 160rpx;
padding-right: 75rpx;
overflow: hidden;
}
.present-price{
display: flex;
align-items: center;
}
.present-price::after{
content: '';
}
}
.sku-item:last-of-type{
border: none;
}
}
</style>

820
subPackages/techan/index.vue

@ -0,0 +1,820 @@
<template>
<view class="bg">
<view class="search-header" :style="{'height': height+'px','padding-top':statusBarHeight+'px'}">
<uni-icons type="left" size="20" @click="goBack" style="flex-shrink: 0;"></uni-icons>
<view class="input-model">
<uni-icons style="height: 1.4rem;line-height: 1.4rem;" type="search" size="15" color="#ABAAAD" ></uni-icons>
<input class="input-text" placeholder="请输入商品名称" v-model="searchText" @confirm="search(false)" @keyup.enter="search(false)"></text>
</view>
</view>
<div :style="{'height':height+'px','flex-shrink':0}"></div>
<!-- 正常产品 -->
<template v-if="searchText.trim().length<=0">
<img :src="showImg(headImg)" class="topImg" />
<view class="goods-container">
<scroll-view class="left-container no-scrollbar" :scroll-y="true">
<view :class="['type-item',typeIndex==i?'active':'']"
v-for="(item,i) in typeParam" :key="i" @click="changeType(item,i)">
{{item.title}}
<view class="active-type-bar" v-if="typeIndex==i"></view>
</view>
<view style="height: 148rpx;width: 1rpx;"></view>
</scroll-view>
<scroll-view class="right-container no-scrollbar" :scroll-y="true" @scrolltolower="scrollLow()">
<view class="item" v-for="item in list" :key="item.goods.id" @click="viewDetail(item.goods)">
<image class="item-img" :src="showImg(item.goods.image)" mode=""></image>
<view class="content">
<view class="title text-overflowRows">{{item.goods.title}}</view>
<view class="bottom">
<view class="price">
{{item.goods.money/100}}
</view>
<view class="buy-cart" @click.stop="showOrderCart(item.goods)">+</view>
</view>
</view>
</view>
<view style="height: 148rpx;width: 1rpx;"></view>
</scroll-view>
</view>
</template>
<!-- 搜索 -->
<view v-else style="flex: 1;">
<view v-if="searchList.length<=0" class="flex-center" style="width: 100%;height: 100%;flex-direction: column;">
<image style="width: 328rpx;height: 450.67rpx;" :src="showImg('/uploads/20250514/0e5f4c089ba2f3a86d3c6f9d8d818d5b.png')"></image>
<view style="font-weight: 500;font-size: 28rpx;color: #666666;
text-align: center;margin-top: 67rpx;padding-bottom: 300rpx;">暂无搜索结果</view>
</view>
<view style="padding:0 26rpx;background: #FFFFFF;min-height: 100%;" v-else>
<view class="flex" style="justify-content: space-between;">
<view class="search-result" >
<view v-for="(item,i) in searchList" >
<view v-if="i%2==0" class="search-item" :key="i" @click="viewDetail(item.goods)">
<image :src="showImg(item.goods.image)" mode="widthFix"></image>
<view class="search-container">
<view class="title text-overflowRows">{{item.goods.title}}</view>
<view class="flex-between" style="padding-top: 20rpx;">
<view>
<text class="price">{{item.goods.money/100}}</text>
<text class="oldPrice">{{item.goods.price/100}}</text>
</view>
<view class="buy-cart" @click.stop="showOrderCart(item.goods)">+</view>
</view>
</view>
</view>
</view>
</view>
<view class="search-result" >
<view v-for="(item,i) in searchList">
<view class="search-item" v-if="i%2==1" :key="i" @click="viewDetail(item.goods)">
<image :src="showImg(item.goods.image)" mode="widthFix"></image>
<view class="search-container">
<view class="title text-overflowRows">{{item.goods.title}}</view>
<view class="flex-between" style="padding-top: 20rpx;">
<view>
<text class="price">{{item.goods.money/100}}</text>
<text class="oldPrice">{{item.goods.price/100}}</text>
</view>
<view class="buy-cart" @click.stop="showOrderCart(item.goods)">+</view>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
<view style="height: 148rpx;width: 1rpx;"></view>
</view>
<!-- 购物车 -->
<view class="btn-bottom" >
<cartDataVue ref="cartDataVueRef" :paramData="paramData" @changeParamData="changeParamData" style="width: 100%;height: 100%;">
<template class="btn-list" slot="content">
<view class="left-box">
<uni-badge class="uni-badge-left-margin" :text="paramData.num" absolute="rightTop" :offset="[5, 5]" size="small"
:custom-style="{background:'#D90F01',color:'#ffffff',border:'1px solid #ffffff'}">
<view class="img-box" @click.stop="showCartClick">
<image :src="showImg('/uploads/20250513/cc33cde475ce45c274cb304e20805aa1.png')" mode="aspectFill"></image>
</view>
</uni-badge>
<view class="bottom-price">
<text style="color: #000000;">总计</text>
<view class="bottom-price-yuan">{{paramData.iNum}}</view>
<view>.{{paramData.fNum}}</view>
</view>
<view class="bottom-detail-icon" @click.stop="showCartClick">
明细
<view v-if="!paramData.showCart" style="transform: rotate(90deg);"><uni-icons color="#0B898E" type="left" size="15"></uni-icons></view>
<view v-else style="transform: rotate(-90deg);"><uni-icons color="#0B898E" type="left" size="15"></uni-icons></view>
</view>
</view>
<view class="btn-buy" @click="goCartOrder">
去结算
</view>
</template>
</cartDataVue>
</view>
<uni-popup ref="popup" type="bottom" :safe-area="true">
<view class="popup-content" v-if="sku.length>0">
<view @click="closePopup" style="padding: 31rpx 0 0 639rpx;width: 50rpx;height: 80rpx;">
<uni-icons type="closeempty" size="24"></uni-icons>
</view>
<view class="bottom-productImg">
<img :src="showImg(sku[productIndex].image)" alt="">
<view class="right-content">
<view class="bottom-productPrice com-price">{{(sku[productIndex].money||0)/100}}</view>
<view class="bottom-content text-overflow">已选择{{sku[productIndex].title}}</view>
</view>
</view>
<view>
<view style="display: flex;align-items: center;justify-content: space-between;flex-wrap: wrap;">
<view style="position:relative;" v-for="(botItem,botIndex) in sku" :key="botIndex">
<view :class="['botProduct','text-overflow',{'noStore':botItem.store==0},{'botProducts':productIndex==botIndex}]"
@click="changeProduct(botItem,botIndex)">
{{botItem.title}}
</view>
<view class="noStore-text" v-if="botItem.store==0">
不可购买
</view>
<!-- <view class="noStore-text" v-else-if="botItem.is_post!=1">不可加购</view> -->
</view>
</view>
</view>
<view class="buy-num com-flex-tao">
数量
<view class="number-btn">
<view>
<text @click="delNumber">-</text>
</view>
<view style="width: 96rpx;height: 69rpx;margin: 0 14rpx;">{{ buyNum }}</view>
<view>
<text @click="addNumber">+</text>
</view>
</view>
</view>
</view>
<view class="placeholder-content">
<view style="height: 100rpx;"></view>
<view class="btn-cover">
<view class="btn" @click.stop="order">加入购物车</view>
</view>
</view>
</uni-popup>
</view>
</template>
<script>
import cartDataVue from '../../components/cartData.vue'
export default {
components: {cartDataVue},
data() {
return {
//
height: 0,
statusBarHeight: 0,
headImg: "",
typeParam: [
{id: 45, title: '味 | WEI'},
{id: 46, title: '服 | FU'},
{id: 47, title: '器 | QI'},
{id: 48, title: '香 | XIANG'},
{id: 49, title: '饰 | SHI'},
{id: 50, title: '艺 | YI'},
],
typeIndex: 0,
list:[],
finished: false,
paramData: {allPrice: 0,iNum:0, fNum:'00', showCart: false, num: 0},
currentGoods: {},
sku: [],
productIndex: 0,
buyNum: 1,
cartDataVueShow: false,
searchText: '',
searchList: [],
searchFinish: false,
}
},
onLoad(options) {
this.getHeadImg(10211).then(res => {this.headImg = res})
if (options.idIndex) {
try {
this.typeIndex = Number(options.idIndex)
} catch(e) {
this.typeIndex = 0
}
}
},
onReady() {
this.initRectInfo()
this.getList()
},
methods: {
initRectInfo () {
const sysInfo = uni.getSystemInfoSync()
this.statusBarHeight = sysInfo.statusBarHeight
//
this.height = sysInfo.statusBarHeight + 40
},
changeType (item, i) {
if (this.typeIndex!== i) {
this.typeIndex=i
this.finished = false
this.list = []
this.getList()
}
},
//
getList(){
this.Post({
tag_id: this.typeParam[this.typeIndex].id,
offset: this.list.length,
limit: 10,
},'/api/tag/getGoodsByTagId').then(res => {
this.list = [...this.list, ...res.data];
if (res.data.length < 10) {
this.finished = true
}
})
},
scrollLow () {
if (!this.finished) {
this.getList()
}
},
viewDetail(item) {
// this.goOtherDetail(item)
if(!item.link_type) {
uni.navigateTo({
url: '/subPackages/techan/detail?id=' + item.id
})
}
},
showCartClick () {
if (this.paramData.showCart) {
this.$refs.cartDataVueRef.closePopup()
} else {
this.$refs.cartDataVueRef.openPop()
}
},
changeParamData (data) {
for(let key in this.paramData) {
this.paramData[key] = data[key]
}
},
//
showOrderCart (item) {
console.log(item)
this.sku = []
this.productIndex = 0
this.buyNum = 1
this.currentGoods = JSON.parse(JSON.stringify(item))
this.getSpecificationsByGoodsId(item.id)
},
changeProduct(item,index) {
if (item.store==0) {
uni.showToast({
title:"库存不足!",
icon:'none'
})
return
}
this.productIndex = index
},
getSpecificationsByGoodsId(goods_id) {
this.Post({goods_id: goods_id},'/api/goods/getSpecificationsByGoodsId'
).then(res => {
if (res) {
this.sku = res.data;
if (!this.sku||this.sku.length<=0) {
uni.showToast({
title:'暂无可选规格',
icon:'none'
})
return
}
this.openPop()
}
});
},
//
addNumber() {
this.buyNum += 1;
},
delNumber() {
if (this.buyNum <= 1) {
return;
}
this.buyNum -= 1;
},
closePopup() {
this.$refs.popup.close()
},
openPop(){
this.$refs.popup.open()
},
order() {
let goods = this.sku[this.productIndex]
if(goods.is_post!=1) {
uni.showToast({
title: '此规格不支持加入购物车',
icon: 'none'
})
return
}
goods.buyNum = this.buyNum
let goodsInfo = {goodsInfo:this.currentGoods, skuInfo: goods, isSelected: true}
this.Post({good_id: this.currentGoods.id, specifications_id: goods.id,num: this.buyNum },
'/api/shopping/addShopping').then(res => {
if (res) {
let selectedData = []
try {
selectedData = JSON.parse(uni.getStorageSync('cartDataInfo'));
} catch(e) {
selectedData = []
}
let currentGoods = selectedData.find(v =>v==goods.id)
if (!currentGoods) {
selectedData.push(goods.id)
}
uni.setStorageSync('cartDataInfo', JSON.stringify(selectedData));
uni.$emit("updateDataByConnect", {msgType:'updateCartDataInfo',data:null})
this.closePopup()
// this.$refs.cartDataVueRef.openPop()
}
});
},
goCartOrder () {
this.$refs.cartDataVueRef.goCartOrder()
},
//
search (isMore) {
if (this.searchText.trim().length<=0) {
uni.showToast({
title:'请输入搜索关键字',
icon:'none'
})
return
}
if (!isMore) {
this.searchList = []
this.searchFinish = false
}
this.Post({
name: this.searchText.trim(),
offset: this.searchList.length,
// type: 'pgoods',
limit: 10,
}, '/api/search/search').then(res => {
let resData = (res.data || []).map(v=> {return {id: v.id, goods: v.search_data}})
console.log(resData)
this.searchList = [...this.searchList, ...resData];
this.searchFinish = false
console.log(resData, this.searchList)
if (res.data.length<10) {
this.searchFinish = true
}
})
},
},
onReachBottom() {
if (!this.searchFinish) {
this.search(true)
}
console.log(1)
}
}
</script>
<style lang="scss" scoped>
view {
box-sizing: border-box;
}
.search-header{
width: 100%;
position: fixed;
top: 0;
left: 0;
right: 0;
background: white;
display: flex;
align-items: center;
padding-left: 10rpx;
padding-right: 120px;
.input-model{
flex: 1;
width: 1rpx;
height: 64rpx;
background: #FFFFFF;
border-radius: 55rpx;
border: 1rpx solid #20898D;
margin-left: 12rpx;
display: flex;
align-items: center;
font-weight: 400;
font-size: 24rpx;
color: #ABAAAD;
padding-left: 24rpx;
}
.input-text{
height:19px;
padding-left: 14rpx;
font-weight: 400;
font-size: 24rpx;
color: #030000;
}
}
.bg {
height: 100vh;
padding-bottom: 26rpx;
display: flex;
flex-direction: column;
background: #F8FAFB;
}
.topImg {
width: 750rpx;
height: 344rpx;
flex-shrink: 0;
}
.goods-container{
height: 1rpx;
flex: 1;
display: flex;
}
.left-container{
width: 214rpx;
flex-shrink: 0;
.type-item{
font-weight: 400;
font-size: 24rpx;
color: #000000;
text-align: center;
width: 216rpx;
height: 120rpx;
line-height: 120rpx;
position: relative;
.active-type-bar{
position: absolute;
left: 0;
top: 0;
bottom: 0;
height: 100%;
width: 12rpx;
background: #74A5AA;
}
}
.type-item.active{
background: linear-gradient( 90deg, rgba(32,137,141,0.4) 0%, rgba(32,137,141,0) 100%);
font-weight: 600;
font-size: 28rpx;
}
}
.right-container{
flex: 1;
width: 1rpx;
background: white;
padding-top: 32rpx;
.item {
width: 100%;
padding:0 32rpx 32rpx 32rpx;
display: flex;
.item-img {
width: 148rpx;
height: 148rpx;
border-radius: 15rpx;
flex-shrink: 0;
}
.content {
height: 148rpx;
padding-left: 24rpx;
display: flex;
flex-direction: column;
justify-content: space-between;
flex: 1;
}
.title {
font-weight: 500;
font-size: 28rpx;
color: #000000;
width: 100%;
}
.bottom {
display: flex;
justify-content: space-between;
align-items: center;
.price {
font-size: 40rpx;
color: #FA0000;
font-weight: 400;
}
.price::before {
content: '¥';
font-size: 24rpx;
}
}
}
}
.buy-cart{
width: 44rpx;
height: 44rpx;
color: #FFFFFF;
background: #74A5AA;
line-height: 44rpx;
font-size: 30rpx;
text-align: center;
border-radius: 50%;
}
.btn-list {
z-index: 99;
position: fixed;
bottom: 0;
width: 750rpx;
height: 148rpx;
background: #FFFFFF;
box-shadow: 0rpx 0rpx 16rpx 0rpx rgba(6, 0, 1, 0.1);
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 26rpx;
.left-box {
display: flex;
align-items: center;
.bottom-price{
display: flex;
align-items: baseline;
font-family: PingFang SC;
font-weight: bold;
font-size: 27rpx;
color: #D70000;
padding: 0 12rpx;
.bottom-price-yuan{
font-size: 40rpx;
}
}
.bottom-detail-icon{
font-size: 24rpx;
display: flex;
align-items: center;
color: #0B898E;
}
.img-box {
image {
width: 78rpx;
height: 78rpx;
}
}
}
.btn-buy {
width: 240rpx;
height: 78rpx;
background: #74A5AA;
border-radius: 40rpx;
text-align: center;
line-height: 78rpx;
font-size: 32rpx;
font-family: PingFangSC;
font-weight: 500;
color: #FFFFFF;
}
}
//
.popup-content {
background-color: white;
padding: 0rpx 39rpx 51rpx 39rpx;
height: auto;
border-radius: 20rpx 20rpx 0 0;
.bottom-productImg {
display: flex;
margin-bottom: 48rpx;
}
.bottom-productImg img {
width: 218rpx;
height: 179rpx;
background: #666666;
border-radius: 13rpx;
}
.right-content {
margin: 10rpx 0 0 41rpx;
}
.bottom-productPrice {
font-size: 40rpx;
color: #C3282E;
&:before {
content: "¥";
font-size: 26rpx;
}
}
.bottom-content {
width: 331rpx;
font-size: 27rpx;
font-family: PingFang SC;
font-weight: 400;
color: #666666;
}
.botProduct {
width: 320rpx;
// height: 78rpx;
border-radius: 13rpx;
background-color: #EFEFEF;
font-size: 29rpx;
font-family: PingFang SC;
font-weight: 400;
color: #333333;
line-height: 78rpx;
text-align: center;
margin-bottom: 25rpx;
display: inline-block;
position: relative;
padding: 0 40rpx;
}
.noStore{
background-color: rgba(239, 239, 239, 1);
color: rgba(153, 153, 153, 1);
}
.noStore-text{
width: 113rpx;
height: 43rpx;
background: #C0C0C0;
border-radius: 7rpx 0rpx 7rpx 0rpx;
text-align: center;
line-height: 43rpx;
position: absolute;
right: -14rpx;
top: -20rpx;
font-size: 23rpx;
font-family: PingFangSC;
font-weight: 400;
color: #FFFFFF;
}
.botProducts {
background: #74A5AA;
color: #FFFFFF;
}
.buy-num {
font-size: 29rpx;
font-family: PingFang SC;
font-weight: 400;
color: #333333;
// border-top: 1rpx solid #CCCCCC;
padding: 39rpx 0;
display: flex;
align-items: center;
justify-content: space-between;
border-top: 1px solid #CCCCCC;
}
.buy-num .number-btn {
display: flex;
}
.buy-num .number-btn view {
display: flex;
justify-content: center;
align-items: center;
width: 69rpx;
height: 69rpx;
border: 1rpx solid #CCCCCC;
border-radius: 7rpx;
}
.buy-num .number-btn>view text {
font-size: 46rpx;
}
.btn-box {
width: 750rpx;
height: 151rpx;
background: #FFFFFF;
box-shadow: 0rpx 0rpx 16rpx 0rpx rgba(6, 0, 1, 0.1);
display: flex;
align-items: center;
justify-content: center;
}
}
.placeholder-content{
background: white;
position: relative;
.btn-cover{
z-index: 200;
position: fixed;
bottom: 0;
width: 750rpx;
height: 148rpx;
background: #FFFFFF;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0rpx 0rpx 16rpx 0rpx rgba(6,0,1,0.1);
.btn{
width: 670rpx;
height: 78rpx;
text-align: center;
line-height: 78rpx;
background: #74A5AA;
border-radius: 40rpx;
font-size: 34rpx;
font-family: PingFangSC;
font-weight: 500;
color: #FFFFFF;
}
}
}
.search-result{
display: flex;
flex-direction: column;
width: 100%;
flex: 1;
flex-shrink: 0;
.search-item{
width: 337rpx;
box-shadow: 0rpx 0rpx 9rpx 0rpx rgba(153,153,153,0.33);
border-radius: 13rpx;
flex-shrink: 0;
margin-bottom: 20rpx;
height: fit-content;
image{
width: 100%;
border-radius: 13rpx;
}
.search-container{
padding: 20rpx;
.title{
font-weight: 500;
font-size: 27rpx;
color: #000000;
}
.price{
font-weight: 400;
font-size: 33rpx;
color: #D70000;
}
.price:before{
content: "¥";
font-size: 23rpx;
}
.oldPrice{
font-weight: 400;
font-size: 21rpx;
color: #999999;
text-decoration-line: line-through;
padding-left: 12rpx;
}
}
}
}
</style>

1281
subPackages/techan/order.vue

File diff suppressed because it is too large

253
subPackages/techan/selfPickUpPoint.vue

@ -0,0 +1,253 @@
<template>
<view class="bg">
<view :class="['item-bg',selectItem.id==item.id?'active':'']" v-for="(item,index) in list" :key="index" @click="selectPoint(item)">
<view class="item">
<view class="item-point-title">
<view class="name text-overflow flex-shrink-0">{{item.extract_name}}</view>
<view class="addressStr">
<!-- <view class="flex-shrink-0"></view> -->
<view class="text-overflowRows">地址{{item.detail_addr}}</view>
</view>
</view>
<view class="item-point-guide" @click.stop="goMap(item)">
<view>
<image :src="showImg('/uploads/20241104/8ff7aa0225c9e4fb86df1a9cb229c932.png')" mode="aspectFill" class="mapPoint"></image>
</view>
<view>去这里</view>
</view>
</view>
</view>
<view class="no-data" v-if="list.length==0">
<image src="https://static.ticket.sz-trip.com/dongtai/images/user/noAddress.png" mode="aspectFill" class="no-address"></image>
<view class="">
暂无自提点地址
</view>
</view>
<!-- <view class="btn-bottom">
<view class="addBox" @click.stop="confirmPoint">
确定
</view>
</view> -->
</view>
</template>
<script>
export default {
data() {
return {
load: false,
pending: false,
list: [],
pickupId: null,
goodsId: null,
selectItem: {},
};
},
onLoad(options) {
this.pickupId = options.pickupId || null
this.goodsId = options.goodsId
},
onShow(options) {
this.getList()
},
methods: {
getList() {
if (this.pending) {
return
}
this.pending = true
// this.getLocation()
let param = {
goods_id: this.goodsId,
offset: this.list.length,
limit: 10,
lon: uni.getStorageSync('location').lon || '',
lat: uni.getStorageSync('location').lat || '',
}
this.Post(param, "/api/extract/getMerchantExtractListByGoodsIdNew").then(res => {
if (res) {
this.list = [...this.list, ...res.data];
if (res.data.length < 10) {
this.load = true;
}
if (this.pickupId && Array.isArray(this.list)) {
let selectItem = this.list.find(v=>v.id==this.pickupId)
if (selectItem) {
this.selectItem = selectItem
}
}
}
this.pending = false
})
},
selectPoint (item) {
this.selectItem = item
// selectItem
uni.$emit("updateDataByConnect", {msgType:'updatePickUpPoint',data:this.selectItem})
uni.navigateBack()
},
goMap (item) {
uni.openLocation({
latitude: Number(item.lat),
longitude: Number(item.lon),
name: item.extract_name,
address: item.detail_addr,
success: function () {
console.log('success');
}
});
},
confirmPoint () {
if(!this.selectItem.id) {
uni.showToast({icon: "none",title: "请先选择自提点"})
return;
}
// selectItem
uni.$emit("updateDataByConnect", {msgType:'updatePickUpPoint',data:this.selectItem})
uni.navigateBack()
}
},
onReachBottom() {
setTimeout(() => {
if (!this.load) {
this.getList()
}
}, 1000);
}
}
</script>
<style lang="scss" scoped>
view {
box-sizing: border-box;
font-family: PingFang SC;
}
.bg {
position: relative;
background: #F7F7F7;
min-height: 100vh;
padding-bottom: 170rpx;
}
.flex-shrink-0{
flex-shrink: 0;
}
.item-bg{
width: 697rpx;
height: 160rpx;
background: #FFFFFF;
border-radius: 13rpx;
margin: 0 auto;
margin-bottom: 28rpx;
padding: 2rpx;
}
.item {
padding: 24rpx;
padding-right: 0;
display: flex;
background: #FFFFFF;
border-radius: 13rpx;
width: 100%;
height: 100%;
.item-point-title{
flex: 1;
width: 10rpx;
padding-right: 78rpx;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.item-point-guide{
width:140rpx;
flex-shrink: 0;
border-left: 1px solid #D8D8D8;
color: #515150;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: 24rpx;
}
.mapPoint{
width: 33rpx;
height: 33rpx;
}
}
.item-bg.active{
background: #515150;
}
.name {
display: flex;
font-size: 31rpx;
font-weight: bold;
color: #333333;
height: 42rpx;
}
.addressStr{
display: flex;
font-size: 27rpx;
color: #999999;
padding-top: 10rpx;
}
.no-data {
width: 100%;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
view:nth-child(2) {
font-size: 30rpx;
font-family: PingFang SC;
font-weight: 500;
color: #333333;
margin-top: 25rpx;
}
image {
width: 160rpx;
height: 160rpx;
}
padding-bottom: 400rpx;
}
.btn-bottom{
position: fixed;
bottom: 0;
width: 750rpx;
height: 150rpx;
background: #FFFFFF;
box-shadow: 0rpx 0rpx 16rpx 0rpx rgba(6, 0, 1, 0.1);
display: flex;
justify-content: space-between;
padding: 30rpx 50rpx 30rpx 50rpx;
.addBox{
margin: 0 auto;
width: 697rpx;
height: 80rpx;
background: linear-gradient(90deg, #F84A56, #FF9834);
border-radius: 40rpx;
font-size: 36rpx;
font-family: PingFang SC;
font-weight: 500;
color: #FFFFFF;
line-height: 80rpx;
text-align: center;
}
}
</style>

89
subPackages/user/changeNickname.vue

@ -0,0 +1,89 @@
<template>
<view class="bg">
<view class="nickname-box">
<input v-model="nickname" type="text" placeholder="请输入内容" />
</view>
<view class="btn" @click="save">保存</view>
</view>
</template>
<script>
export default {
name: "changeNickname",
data: function() {
return {
nickname: ""
}
},
methods: {
save: function() {
if (!this.nickname) {
uni.showToast({
title: '请输入昵称',
icon: 'none'
})
return;
}
this.Post({
nickname: this.nickname
}, '/api/user/profile').then(res => {
console.log(res)
if (res.code == 1 || res.code == 200) {
uni.showModal({
title: '提示',
content: '保存成功!',
success: res => {
if (res.confirm) {
this.goBack()
}
}
})
}
})
}
}
}
</script>
<style scoped>
.bg {
min-height: 100vh;
padding-top: 50rpx;
}
.nickname-box {
display: flex;
padding: 10rpx 0 30rpx;
margin: 0 30rpx;
align-items: center;
background: white;
margin-bottom: 100rpx;
font-size: 30rpx;
height: 70rpx;
border-bottom: 1rpx solid #D8D8D8;
}
.nickname-box span {
flex-shrink: 0;
}
.nickname-box input {
flex: 1;
font-size: 30rpx;
display: block;
}
.btn {
color: black;
margin: 0 auto;
line-height: 80rpx;
position: relative;
font-size: 34rpx;
text-align: center;
width: 333rpx;
height: 80rpx;
background: #C3282E;
border-radius: 40rpx;
color: #FFFFFF;
}
</style>

556
subPackages/user/gwc.vue

@ -0,0 +1,556 @@
<template>
<view class="bg">
<view class="guanli" v-if="list && list.length>0" @click="guanli">{{operate}}</view>
<view v-for="(item,index) in list" :key="index">
<view class="goodBox" v-if="item.data && item.data.length > 0 ">
<view class="title" v-if="item.data.some(v => v.flag == 1)">{{item.merchant_name}} ></view>
<view class="goodItem" v-for="(goodItem,goodIndex) in item.data" :key="goodIndex"
v-if="goodItem.flag != 0">
<view class="noSelect" v-if="!goodItem.is_select" @click="changeSelect(goodItem)"></view>
<view class="selectBox flex-around" v-else @click="changeSelect(goodItem)">
<img src="https://static.ticket.sz-trip.com/cgc/images/order/dui.png">
</view>
<image :src="showImg(goodItem.specifications_image)" mode="aspectFill"
@click="goDetails(goodItem.good_id)"></image>
<view class="contentBox">
<view class="title text-overflow">{{goodItem.good_name}}</view>
<view class="subTitle text-overflow">{{goodItem.Specifications_name}}</view>
<view class="priceBox flex-between">
<view class="price">{{goodItem.Specifications_money / 100}}</view>
<view class="flex-between" style="width: 165rpx;font-size: 29rpx;">
<view :class="['jian', goodItem.num == 1 ? 'zhihui' : '']" @click="delNum(goodItem)">-
</view>
<view>{{goodItem.num}}</view>
<view class="jia" @click="addNum(goodItem)">+</view>
</view>
</view>
</view>
</view>
</view>
</view>
<view class="loseGood" v-if="loseList && loseList.length>0">
<view class="title flex-between">失效商品<span @click="delLoseGood('',1)">清空失效商品</span></view>
<view class="goodItem" v-for="(goodItem,goodIndex) in loseList" :key="goodIndex">
<view class="noSelect"></view>
<image :src="showImg(goodItem.specifications_image)" mode="aspectFill"></image>
<view class="contentBox">
<view class="title text-overflow">{{goodItem.good_name}}</view>
<view class="subTitle text-overflow">{{goodItem.Specifications_name}}</view>
<view class="priceBox flex-between">
<view class="price">{{goodItem.Specifications_money / 100}}</view>
<view class="btn" @click="delLoseGood(goodItem.id,0)">删除</view>
</view>
</view>
</view>
</view>
<!-- 预定按钮 -->
<view class="orderBox flex-between">
<view class="priceBox">
<!-- <view class="selectBox flex-around" v-if="allSelect" @click="changeAllSelect">
<img src="https://static.ticket.sz-trip.com/cgc/images/order/dui.png" alt="">
</view>
<view class="noSelect" v-else @click="changeAllSelect"></view>全选 -->
<span v-if="operate == '管理'">在线付:</span>
<p v-if="operate == '管理'">{{getAllPrice()}}</p>
</view>
<view class="btn" @click="order" v-if="operate == '管理'">提交订单</view>
<view class="btn" @click="delGood" v-else>删除</view>
</view>
<view class="noProduct" v-if="list.length == 0 && loseList.length == 0">
暂无商品
</view>
</view>
</template>
<script>
export default {
data() {
return {
operate: '管理',
list: [],
loseList: [],
allSelect: false,
}
},
onShow() {
this.getShoppingList()
this.allSelect = false
},
methods: {
getShoppingList() {
this.Post({
is_post: 1,
// lon: this.$store.state.user.location.lon || '120.63132',
// lat: this.$store.state.user.location.lat || '31.30227'
}, '/api/shopping/getShoppingList').then(res => {
this.list = res.data.merchant
this.loseList = []
this.list.forEach(item => {
item.data.forEach(items => {
if (items.flag === 0) {
this.loseList.push(items)
}
})
})
console.log('打印', this.loseList);
})
},
//
guanli() {
if (this.operate == '管理') {
this.operate = '退出管理'
} else {
this.operate = '管理'
}
console.log(this.operate)
},
delNum(item) {
if (item.num <= 1) {
item.num = 1
uni.showToast({
title: '已经最少了,不能再减了',
icon: 'none'
})
} else {
item.num -= 1
this.updateCart(item)
}
},
addNum(item) {
item.num += 1
this.updateCart(item)
},
updateCart(item) {
let that = this
that.Post({
s_id: item.id,
num: item.num
}, '/api/shopping/updateShopping').then(function(res) {
if (res) {
// that.$toast.clear()
}
})
},
//
changeSelect(item) {
item.is_select = !item.is_select
this.list.forEach(item => {
let checkAll = item.data.findIndex(items => {
return !items.is_select
})
if (checkAll == -1) {
this.allSelect = true
} else {
this.allSelect = false
}
})
this.$forceUpdate()
},
//
goDetails(id) {
uni.navigateTo({
url: '/subPackages/techan/detail?id=' + id
});
},
//
changeAllSelect() {
this.allSelect = !this.allSelect
this.list.forEach(item => {
item.data.forEach(items => {
if (this.allSelect) items.is_select = true
else items.is_select = false
})
})
},
//
delLoseGood(id, index) {
if (index == 0) {
this.Post({
s_id: id
}, '/api/shopping/delShopping').then(res => {
this.getShoppingList()
this.$forceUpdate()
})
} else {
let ids = []
this.loseList.forEach(item => {
ids.push(item.id)
})
this.Post({
s_id: ids.toString()
}, '/api/shopping/delShopping').then(res => {
this.getShoppingList()
this.$forceUpdate()
})
}
},
//
delGood() {
let ids = []
this.list.forEach(item => {
item.data.forEach(items => {
if (items.is_select == true) {
ids.push(items.id)
}
})
})
this.Post({
s_id: ids.toString()
}, '/api/shopping/delShopping').then(res => {
this.getShoppingList()
this.$forceUpdate()
})
},
//
getAllPrice() {
let allPrice = 0
this.list.forEach(item => {
item.data.forEach(items => {
if (items.is_select == true) {
allPrice += items.Specifications_money * items.num
}
})
})
return (allPrice / 100).toFixed(2)
},
//
order() {
let list = []
let ids = []
this.list.forEach(item => {
item.data.forEach((items, index) => {
if (items.is_select == true) {
ids.push(items.merchant_id)
list.push(items)
}
})
})
// if (Array.from(new Set(ids)).length > 1) {
// uni.showToast({
// title: '',
// icon: 'none'
// })
// return;
// }
if (list.length == 0) {
uni.showToast({
title: '请选择商品',
icon: 'none'
})
return;
}
console.log('list参数', list);
this.$store.commit("changeOrderSCart", list)
// this.gotoPath('/subPackages/order/eCartOrder')
this.gotoPath('/subPackages/order/gwcOrder')
// this.gotoPath('/subPackages/goods/goodsOrder')
}
}
}
</script>
<style scoped lang="scss">
.bg {
width: 100%;
min-height: 100vh;
padding: 0 27rpx 200rpx;
box-sizing: border-box;
background-color: #F8F8F8;
}
.guanli {
height: 76rpx;
text-align: right;
font-size: 32rpx;
font-family: PingFang SC;
font-weight: 400;
color: #000000;
line-height: 76rpx;
}
.goodBox {
width: 697rpx;
height: auto;
background: #FFFFFF;
border-radius: 20rpx;
margin: 0 auto 27rpx;
padding: 25rpx 20rpx;
box-sizing: border-box;
.title {
font-size: 35rpx;
font-family: PingFang SC;
font-weight: 500;
color: #000000;
}
.goodItem {
width: 100%;
height: 173rpx;
margin-top: 46rpx;
display: flex;
.noSelect {
width: 37rpx;
height: 37rpx;
border-radius: 50%;
border: 2rpx solid #666666;
box-sizing: border-box;
margin-right: 25rpx;
margin-top: 68rpx;
}
.selectBox {
width: 37rpx;
height: 37rpx;
// background: linear-gradient(90deg, #FA2B66, #FF9834);
// border-radius: 50%;
margin-right: 25rpx;
margin-top: 68rpx;
img {
width: 100%;
height: 100%;
}
}
image {
width: 173rpx;
height: 173rpx;
border-radius: 13rpx;
}
.contentBox {
width: 400rpx;
height: 173rpx;
margin-left: 23rpx;
.title {
font-size: 31rpx;
font-family: PingFang SC;
font-weight: 500;
color: #000000;
}
.subTitle {
font-size: 27rpx;
font-family: PingFang SC;
font-weight: 400;
color: #7C7C7C;
margin-top: 13rpx;
}
.priceBox {
margin-top: 35rpx;
.price {
font-size: 40rpx;
font-family: PingFang SC;
font-weight: 500;
color: #FF2D3B;
}
.price:before {
content: "¥";
font-size: 27rpx;
}
.jian,
.jia {
width: 47rpx;
height: 47rpx;
background: #6CA5AA;
border-radius: 50%;
text-align: center;
color: #FFFFFF;
line-height: 47rpx;
}
}
}
}
}
.loseGood {
width: 697rpx;
height: auto;
background: #FFFFFF;
border-radius: 20rpx;
margin: 0 auto 27rpx;
padding: 25rpx 20rpx;
box-sizing: border-box;
.title {
font-size: 35rpx;
font-family: PingFang SC;
font-weight: 500;
color: #666666;
span {
font-size: 32rpx;
font-family: PingFang SC;
font-weight: 400;
color: #2791F7;
}
}
.goodItem {
width: 100%;
height: 173rpx;
margin-top: 46rpx;
display: flex;
color: #999999;
.noSelect {
width: 37rpx;
height: 37rpx;
border-radius: 50%;
border: 2rpx solid #CCCCCC;
background: #F1F1F1;
box-sizing: border-box;
margin-right: 25rpx;
margin-top: 68rpx;
}
image {
width: 173rpx;
height: 173rpx;
border-radius: 13rpx;
margin-right: 23rpx;
}
.contentBox {
width: 421rpx;
height: 173rpx;
.title {
font-size: 31rpx;
font-family: PingFang SC;
font-weight: 500;
}
.subTitle {
font-size: 27rpx;
font-family: PingFang SC;
font-weight: 400;
}
.priceBox {
margin-top: 35rpx;
.price {
font-size: 40rpx;
font-family: PingFang SC;
font-weight: 500;
}
.price:before {
content: "¥";
font-size: 27rpx;
}
.btn {
width: 125rpx;
height: 53rpx;
background: #999999;
border-radius: 13rpx;
text-align: center;
line-height: 53rpx;
font-size: 29rpx;
font-family: PingFang SC;
font-weight: 500;
color: #FFFFFF;
}
}
}
}
}
.noProduct {
width: 100%;
height: 300rpx;
text-align: center;
line-height: 300rpx;
color: #999999;
font-size: 32rpx;
}
.orderBox {
width: 750rpx;
height: 151rpx;
background: #FFFFFF;
box-shadow: 0px 0px 16rpx 0px rgba(6, 0, 1, 0.1);
padding: 0 27rpx;
box-sizing: border-box;
position: fixed;
left: 0;
bottom: 0;
.priceBox {
display: flex;
font-size: 27rpx;
font-family: PingFang SC;
font-weight: 400;
color: #666666;
span {
font-size: 27rpx;
font-family: PingFang SC;
font-weight: 400;
color: #333333;
margin-left: 20rpx;
}
p {
font-size: 36rpx;
font-family: PingFang SC;
font-weight: 500;
color: #FF2D3B;
margin-top: -5rpx;
}
.noSelect {
width: 41rpx;
height: 41rpx;
border-radius: 50%;
border: 2rpx solid #666666;
box-sizing: border-box;
margin-right: 10rpx;
}
.selectBox {
width: 41rpx;
height: 41rpx;
// background: linear-gradient(90deg, #FA2B66, #FF9834);
border-radius: 50%;
margin-right: 10rpx;
img {
width: 100%;
height: 100%;
}
}
}
.btn {
width: 219rpx;
height: 77rpx;
background: #6CA5AA;
border-radius: 39rpx;
text-align: center;
line-height: 77rpx;
font-size: 33rpx;
font-family: PingFang SC;
font-weight: 500;
color: #FFFFFF;
}
}
.zhihui {
background: #E8E8E8 !important;
color: #999999 !important;
}
</style>

387
subPackages/user/myAddressAdd.vue

@ -0,0 +1,387 @@
<template>
<view class="bg">
<view class="list-forms">
<view class="list-item">
<view class="list-item-title">姓名:</view>
<view class="list-item-input"><input type="text" v-model="username" placeholder="请输入姓名" /></view>
</view>
<view class="list-item">
<view class="list-item-title">手机号:</view>
<view class="list-item-input"><input type="number" v-model="mobile" placeholder="请输入手机号" maxlength="11"
/></view>
</view>
<view class="list-item" style="position: relative;">
<view class="list-item-title">选择地区:</view>
<view class="list-item-input" style="flex: 1;">
<picker mode="multiSelector" :range="newProvinceDataList" range-key="name" @change="changeArea" @columnchange="pickerColumnchange"
style="position: relative;z-index: 2;">
<input type="text" readonly style="text-align: right;font-size: 33rpx;position: relative;z-index: -1;"
v-model="citySeld" disabled="true" placeholder="请选择地区"/>
</picker>
</view>
</view>
<view class="list-item">
<view class="list-item-title">详细地址:</view>
<view class="list-item-input"><input type="text" v-model="detailAddr" placeholder="请输入详细地址" /></view>
</view>
<view class="list-item">
<view class="list-item-title">设为默认:</view>
<view class="list-item-switch">
<switch :checked="idDefault" @change="switchChange" color="#6CA5AA"/>
</view>
</view>
<view class="list-item-btn">
<view class="list-item-post" @click="postSave()">保存</view>
</view>
</view>
</view>
</template>
<script>
import District from 'ydui-district/dist/jd_province_city_area_id';
export default {
data() {
return {
username: '',
mobile: '',
citySeld: '',
detailAddr: '',
idDefault: false,
title: '新增收货地址',
show: false,
district: District, //
ready: false,
province: null,
city: null,
area: '',
provinceId: null,
cityId: null,
areaId: null,
columns: [],
id: '',
newProvinceDataList:[
[],[],[]
],
multiIndex: [0, 0, 0],
}
},
onLoad(option){
this.id = option.id
if (option.id > 0) {
this.title = '编辑收货地址'
this.getDetail()
}
else {
this.getSeldCityList()
}
},
methods: {
switchChange(e){
this.idDefault = e.detail.value
},
changeArea(e){
//
this.multiIndex = e.detail.value;
this.citySeld = this.newProvinceDataList[0][this.multiIndex[0]].name + this.newProvinceDataList[1][this.multiIndex[1]].name + this.newProvinceDataList[2][this.multiIndex[2]].name
this.provinceId = this.newProvinceDataList[0][this.multiIndex[0]].id
this.cityId = this.newProvinceDataList[1][this.multiIndex[1]].id
this.areaId = this.newProvinceDataList[2][this.multiIndex[2]].id
},
getSeldCityList() {
let that = this
that.Post({}, '/api/areas/getAll').then(res => {
if (res) {
var data = res.data;
var result = {};
for (var i = 0; i < data.length; i++) {
var item = data[i];
if (item.parent_id == 0) {
continue;
}
//
if (item.parent_id == "1") {
result[item.id.toString()] = {};
result[item.id.toString()].children = []
result[item.id.toString()].name = item.name;
result[item.id.toString()].id = item.id;
} else if (result[item.parent_id.toString()]) {
//
var t = {
id: item.id,
name: item.name,
children: []
}
result[item.parent_id.toString()].children.push(t)
} else {
//
var k = {
id: item.id,
name: item.name
}
for (var j = 0; j < result[item.parent_id.toString().substr(0, 2) + "0000"].children
.length; j++) {
if (result[item.parent_id.toString().substr(0, 2) + "0000"].children[j].id == item
.parent_id) {
result[item.parent_id.toString().substr(0, 2) + "0000"].children[j].children.push(k)
}
}
}
}
var r = [];
//ObjectArray
for (var i in result) {
r.push(result[i]);
}
//
that.district = r;
let arr = []
let arr1 = []
let arr2 = []
that.district.forEach(item => {
arr.push(item)
})
that.district[0].children.forEach(item => {
arr1.push(item)
})
that.district[0].children[0].children.forEach(item => {
arr2.push(item)
})
that.columns = arr
//
that.ready = true;
console.log(this.columns)
for(let i=0; i<this.columns.length; i++){
this.newProvinceDataList[0].push({name:this.columns[i].name,id:this.columns[i].id});
}
console.log(this.columns[0].children)
for(let i=0; i<this.columns[0].children.length; i++){
this.newProvinceDataList[1].push({name:this.columns[0].children[i].name,id:this.columns[0].children[i].id});
}
for(let i=0; i<this.columns[0].children[0].children.length; i++){
this.newProvinceDataList[2].push({name:this.columns[0].children[0].children[i].name,id:this.columns[0].children[0].children[i].id});
}
}
})
},
//
pickerColumnchange(e){
//
// console.log(e.detail.column);
//
// console.log(e.detail.value)
//
if(e.detail.column === 0){
this.multiIndex[0] = e.detail.value
// console.log('');
// this.newProvinceDataList[1] = [];
this.newProvinceDataList[1] = this.columns[this.multiIndex[0]].children.map((item,index)=>{
// console.log(item)
return item
})
// console.log(this.multiIndex)
if(this.columns[this.multiIndex[0]].children.length === 1){
this.newProvinceDataList[2] = this.columns[this.multiIndex[0]].children[0].children.map((item,index)=>{
// console.log(item)
return item
})
}else{
this.newProvinceDataList[2] = this.columns[this.multiIndex[0]].children[this.multiIndex[1]].children.map((item,index)=>{
// console.log(item)
return item
})
}
//
this.multiIndex.splice(1, 1, 0)
this.multiIndex.splice(2, 1, 0)
}
//
if(e.detail.column === 1){
this.multiIndex[1] = e.detail.value
// console.log('');
// console.log(this.multiIndex)
this.newProvinceDataList[2] = this.columns[this.multiIndex[0]].children[this.multiIndex[1]].children.map((item,index)=>{
// console.log(item)
return item
})
//
this.multiIndex.splice(2, 1, 0)
}
//
if(e.detail.column === 2){
this.multiIndex[2] = e.detail.value
// console.log('')
// console.log(this.multiIndex)
}
},
getDetail() {
this.Post({
id: this.id
}, "/api/user/contactDetail").then(res => {
res = res.data;
if (res && res.id > 0) {
this.username = res.name
this.mobile = res.tel
this.idDefault = res.is_default == 1 ? true : false
this.provinceId = res.province_id
this.cityId = res.city_id
this.areaId = res.district_id
this.citySeld = res.province_text + '' + res.city_text + '' + res.district_text
this.detailAddr = res.detail_addr;
this.getSeldCityList();
}
})
},
postSave() {
this.username = this.username.trim()
this.mobile = this.mobile.trim()
this.detailAddr = this.detailAddr.trim()
if (this.username.length < 1) {
uni.showToast({
title: '请输入姓名',
icon: 'none'
})
return
}
if (this.username.length > 6) {
uni.showToast({
title: '姓名最多6个字',
icon: 'none'
})
return
}
if (!this.IsTel(this.mobile)) {
uni.showToast({
title: '请输入正确的手机号',
icon: 'none'
})
return
}
if (this.citySeld.length < 1) {
uni.showToast({
title: '请选择地区',
icon: 'none'
})
return
}
if (this.detailAddr.length < 2) {
uni.showToast({
title: '请输入具体地址',
icon: 'none'
})
return
}
this.Post({
name: this.username,
tel: this.mobile,
is_default: this.idDefault ? '1' : '0',
province_id: this.provinceId,
city_id: this.cityId,
district_id: this.areaId,
detail_addr: this.detailAddr,
id: this.id || null
},'/api/user/' + (this.id > 0 ? 'edit' : 'add') + 'Consignee').then(res => {
if(res){
uni.setStorageSync('addressNow',JSON.stringify(res.data))
uni.showModal({
title: '提示',
content: this.id>0?'编辑成功':'添加成功',
showCancel: false,
success: res => {
if(res.confirm){
uni.navigateBack({})
}
}
})
}else{
uni.showModal({
title: '提示',
content: res.msg,
showCancel: false,
success: res => {
}
})
}
})
}
}
}
</script>
<style scoped>
.bg {
min-height: 100vh;
background-color: white;
display: flex;
flex-direction: column;
}
.list-forms {
display: flex;
flex-direction: column;
border-top: 1rpx solid #D8D8D8;
padding: 20rpx 40rpx;
box-sizing: content-box
}
.list-item {
display: flex;
border-bottom: 1rpx solid #D8D8D8;
padding: 30rpx 0;
height: 60rpx;
align-items: center;
box-sizing: content-box
}
.list-item-title {
width: 150rpx;
font-size: 31rpx;
margin-right: 20rpx;
}
.list-item-input {
flex: 1;
}
.list-item-input input {
width: 100%;
height: 100%;
padding: 0;
margin: 0;
border: 0;
background-color: transparent;
line-height: 34rpx;
font-size: 31rpx;
text-align: right;
font-weight: 500;
}
.list-item-input input::placeholder {
font-size: 26rpx;
}
.list-item-switch {
display: flex;
flex: 1;
justify-content: flex-end;
}
.list-item-btn {
display: flex;
justify-content: center;
margin-top: 20rpx;
}
.list-item-post {
display: flex;
color: #FFFFFF;
font-size: 36rpx;
width: 697rpx;
height: 73rpx;
background: #6CA5AA;
border-radius: 37rpx;
justify-content: center;
align-items: center;
margin-top: 650rpx;
}
</style>

58
subPackages/user/privacy.vue

@ -0,0 +1,58 @@
<template>
<view class="bg">
<view class="list">
<view class="item" v-for="(item,index) in list" :key="item.id" @click="goInfo(item)">
{{item.name}}
</view>
</view>
</view>
</template>
<script>
export default{
data(){
return {
list:[{name:'用户协议',id:'10001'},{name:'隐私协议',id:'9999'},{name:'个人信息收集清单',id:'10202 '}],
}
},
onShow() {
// this.Post({
// id: 9999
// },'/api/article/getArticleById').then(res => {
// this.content = res.data.content
// })
},
methods:{
goInfo(item){
uni.navigateTo({
url:'/subPackages/user/privacyInfo?id='+item.id
})
}
}
}
</script>
<style scoped lang="scss">
view{
box-sizing: border-box;
}
.list{
width: 698rpx;
background: #FFFFFF;
box-shadow: 0px 4rpx 12rpx 0px rgba(150,149,149,0.3);
border-radius: 18rpx;
margin: 0 auto;
margin-top: 20rpx;
padding:0 20rpx ;
}
.item{
width: 100%;
height: 93rpx;
border-bottom: 1rpx solid #EEEFF7;
font-size: 28rpx;
font-family: PingFang SC;
font-weight: 500;
color: #333333;
line-height: 93rpx;
}
</style>

30
subPackages/user/privacyInfo.vue

@ -0,0 +1,30 @@
<template>
<view class="bg">
<view class="text" v-html="formateRichText(content)">
</view>
</view>
</template>
<script>
export default {
data() {
return {
content:''
};
},
onLoad(option){
this.Post({
id:option.id
},'/api/article/getArticleById').then(res => {
this.content = res.data.content
})
}
}
</script>
<style lang="scss">
.text{
padding: 20rpx;
}
</style>

407
subPackages/user/profile.vue

@ -0,0 +1,407 @@
<template>
<view v-if="info">
<view class="user-other-info">
<div class="info-avatar-top">
<span>头像</span>
<view @click="uploadImg()">
<image :src="showImg(info.avatar)" mode="aspectFill"
style="width: 80rpx;height: 80rpx;border-radius: 50%;"></image>
</view>
</div>
<navigator url="/subPackages/user/changeNickname" tag="view" class="userinfo-item">
<span>昵称</span>
<view>{{nickname}}</view>
</navigator>
<!-- <view class="userinfo-item" @click="showSexSelect = true">
<span>性别</span>
<view @click="$refs.popup.open()">{{gender == 1 ? '' : (gender == 2 ? '' : '保密')}}</view>
</view> -->
<view class="userinfo-item">
<span>手机号</span>
<view>{{info.mobile}}</view>
</view>
<!-- <view class="userinfo-item">
<span>生日</span>
<picker mode="date" :value="date" :start="startDate" :end="endDate" @change="bindDateChange">
<view class="uni-input">{{birthday}}</view>
</picker>
</view>
<navigator url="/subPackages/user/logout" tag="view" class="userinfo-item">
<span>注销账号</span>
<i>注销后账号无法恢复请谨慎操作</i>
</navigator> -->
<!-- <view class="btn-tao" @click="submit">保存</view> -->
</view>
<!-- 性别弹框 -->
<uni-popup ref="popup" type="bottom">
<view class="popup-box">
<view class="popup-item flex-center" v-for="(item,index) in sexes" :key="index" @click="changesex(index)">{{item.text}}</view>
<view class="popup-items flex-center" @click="$refs.popup.close()">取消</view>
</view>
</uni-popup>
</view>
</template>
<script>
import {pathToBase64} from "@/static/js/mmmm-image-tools/index.js"
export default {
name: "Profile",
data() {
const currentDate = this.getDate({
format: true
})
return {
date: currentDate,
info: null,
showSexSelect: false,
sexes: [{
value: '1',
text: '男'
},
{
value: '2',
text: '女'
},
{
value: '0',
text: '保密'
}
],
today: null,
showCropper: false,
nickname: '',
gender: '',
birthday: '',
email: '',
fileList1: [],
startDate: '1900-1-1',
endDate: '2050-1-1'
}
},
onShow() {
console.log(this.$store.state.user.userInfo,uni.getStorageSync('userInfo'))
this.getList()
},
computed: {
// startDate() {
// return this.getDate('start');
// },
// endDate() {
// return this.getDate('end');
// }
},
methods: {
getFile(e) {
console.log(e)
},
getList() {
let today = new Date();
today = today.getFullYear() + "/" + (today.getMonth() + 1) + "/" + today.getDate();
this.today = today;
this.Post({}, "/api/user/userInfo").then(res => {
if (!res.data.birthday) {
let date = new Date();
res.data.birthday = date.getFullYear() + "/" + (date.getMonth() + 1) + "/" + date
.getDate();
}
this.info = res.data;
this.nickname = this.info.nickname
this.email = this.info.email
this.birthday = this.info.birthday
this.gender = this.info.gender
this.info.token = JSON.parse(uni.getStorageSync('userInfo')).token || this.$store.state.user.userInfo.token
console.log(this.info)
this.$store.commit('changeUserInfo', this.info)
})
},
uploadImg() {
uni.chooseImage({
success: (chooseImageRes) => {
const tempFilePaths = chooseImageRes.tempFilePaths;
// // #ifdef MP-WEIXIN
// uni.getFileSystemManager().readFile({
// filePath: tempFilePaths[0],
// encoding: 'base64',
// success: res => {
// this.Post({
// method: 'POST',
// base64: 'data:image/png;base64,' + res.data
// }, '/api/common/base64').then(res => {
// if (res.data) {
// this.Post({
// avatar: res.data
// }, '/api/user/profile').then(res => {
// uni.showModal({
// title: '',
// content: res.msg,
// showCancel: false,
// success: res => {
// if (res.confirm) {
// this.getList()
// }
// }
// })
// })
// }
// })
// }
// })
// // #endif
pathToBase64(tempFilePaths[0]).then(base64 => {
this.Post({
method: 'POST',
base64: base64
}, '/api/common/base64').then(res => {
if (res.data) {
this.Post({
avatar: res.data
}, '/api/user/profile').then(res => {
uni.showModal({
title: '提示',
content: res.msg,
showCancel: false,
success: res => {
if (res.confirm) {
this.getList()
}
}
})
})
}
})
})
}
});
},
//
bindDateChange: function(e) {
this.birthday = e.detail.value
},
getDate(type) {
const date = new Date();
let year = date.getFullYear();
let month = date.getMonth() + 1;
let day = date.getDate();
if (type === 'start') {
year = year - 60;
} else if (type === 'end') {
year = year + 2;
}
month = month > 9 ? month : '0' + month;
day = day > 9 ? day : '0' + day;
return `${year}/${month}/${day}`;
},
changesex(index) {
this.gender = this.sexes[index].value
this.$refs.popup.close()
},
submit() {
uni.showModal({
title: '提示',
content: '确认修改您的信息?',
success: res => {
if (res.confirm) {
this.Post({
nickname: this.nickname,
gender: this.gender,
birthday: this.birthday
}, '/api/user/profile').then(res => {
console.log(res)
if (res.code == 1) {
uni.showModal({
title: '提示',
content: res.msg,
showCancel: false,
success: res => {
if (res.confirm) {
this.getList()
}
}
})
}
})
}
}
})
},
}
}
</script>
<style scoped lang="scss">
view {
box-sizing: content-box;
}
.info-avatar-top {
display: flex;
justify-content: space-between;
font-size: 30rpx;
border-bottom: 1rpx solid #D8D8D8;
padding: 40rpx 0;
height: 48rpx;
color: #333;
align-items: center;
}
.info-avatar-top view{
display: flex;
align-items: center;
}
.info-avatar-top view:after{
content: "";
width: 20rpx;
height: 20rpx;
margin-left: 6rpx;
background-image: url('https://static.ticket.sz-trip.com/tongli/images/user/rightIcon-gray.png');
background-size: 100% 100%;
}
.info-avatar-top img {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
margin-right: 10rpx;
}
.change-avatar-btn {
color: #FFF;
width: 220rpx;
margin: 0 auto;
line-height: 70rpx;
border-radius: 20rpx;
background: #4C93FF;
position: relative;
font-size: 34rpx;
}
.change-avatar-btn input {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
opacity: 0;
}
.user-other-info {
margin: 30rpx;
}
.userinfo-item {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 30rpx;
border-bottom: 1rpx solid #D8D8D8;
padding: 40rpx 30rpx 40rpx 0;
height: 48rpx;
color: #333;
position: relative;
}
.info-avatar-top span {
font-weight: 500;
font-size: 31rpx;
flex-shrink: 0;
}
.userinfo-item span {
font-weight: 500;
font-size: 31rpx;
flex-shrink: 0;
color: #000;
}
.userinfo-item i {
font-weight: 500;
font-size: 24rpx;
color: #999999;
}
.userinfo-item {
& view::after {
content: "";
width: 20rpx;
height: 20rpx;
margin-left: 6rpx;
background-image: url('https://static.ticket.sz-trip.com/tongli/images/user/rightIcon-gray.png');
background-size: 100% 100%;
position: absolute;
right: 0;
margin: auto;
top: 0;
bottom: 0;
}
}
.birthday-box {
text-align: right;
}
.cropper {
width: auto;
height: 100%;
}
.cropper-content {
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
z-index: 1000;
}
.dialog-footer .change-avatar-btn {
position: fixed;
text-align: center;
bottom: 80rpx;
left: 50%;
margin-left: -110rpx;
}
.btn-tao {
text-align: center;
font-size: 30rpx;
width: 697rpx;
height: 80rpx;
background: #C3282E;
border-radius: 40rpx;
line-height: 80rpx;
color: #FFFFFF;
position: fixed;
left: 26rpx;
bottom: 100rpx;
}
.popup-box {
border-radius: 20rpx 20rpx 0rpx 0rpx;
background: #fff;
overflow: hidden;
.popup-item {
width: 697rpx;
height: 99rpx;
font-weight: 500;
font-size: 31rpx;
color: #12293C;
margin: auto;
}
.popup-item:nth-child(2) {
border: none;
border-bottom: 1rpx solid #D8D8D8;
border-top: 1rpx solid #D8D8D8;
}
.popup-items {
width: 100%;
height: 99rpx;
font-weight: 500;
font-size: 31rpx;
color: #12293C;
border-top: 13rpx solid #F2F2F2;
}
}
</style>

339
subPackages/user/travelerList.vue

@ -0,0 +1,339 @@
<template>
<view class="bg">
<!-- <view class="cancat-nav flex-around">
<view :class="['concat-nav-item',showType===0?'active':'']" @click="showType=0">
<view>出行人信息</view>
<view v-if="showType===0" class="active-bar"></view>
</view>
<view :class="['concat-nav-item',showType===1?'active':'']" @click="showType=1">
<view>收货地址</view>
<view v-if="showType===1" class="active-bar"></view>
</view>
</view> -->
<!-- 出行人信息 -->
<view v-if="showType===0">
<view class="item" v-for="(item,index) in travelList" :key="index">
<view class="name">
<view class="">
{{item.name}}
</view>
<view class="">
{{item.tel}}
<span v-if="item.is_default==1">默认</span>
</view>
</view>
<view class="idcard">
<view class="">
{{item.document_type_text}}:
</view>
<view class="">
{{item.id_number}}
</view>
</view>
<view class="item-btn">
<view class="choice">
<!-- <image src="https://static.ticket.sz-trip.com/yandu/images/user/dui.png" mode="aspectFill"
v-if="item.is_default==1">
</image>
<view class="yuan" v-else @click.stop="defaultC(item)">
</view>
<view class="">
默认出行人
</view> -->
</view>
<view class="btn-list">
<view class="btn-item" @click.stop="edit(item.id)">
修改
</view>
<view class="btn-item" @click.stop="delet(item)">
删除
</view>
</view>
</view>
</view>
</view>
<!-- 收货地址 -->
<view v-else>
<view class="item" v-for="(item,index) in addressList" :key="index">
<view class="name">
<view>
{{item.name}}
</view>
<view class="">
{{item.tel}}
<span v-if="item.is_default==1">默认</span>
</view>
</view>
<view class="idcard">
<view class="">
收货地址:
</view>
<view class="text-overflow">
{{item.address}}
</view>
</view>
<view class="item-btn" style="justify-content: flex-end;">
<view class="btn-list">
<view class="btn-item" @click.stop="edit(item.id)">
修改
</view>
<view class="btn-item" @click.stop="delet(item)">
删除
</view>
</view>
</view>
</view>
</view>
<view class="btn-box">
<navigator :url="showType ? '/subPackages/user/myAddressAdd' : '/subPackages/user/myContactsAdd'" class="btn">
{{showType ? '新增收货地址' : '添加联系人'}}
</navigator>
</view>
</view>
</template>
<script>
export default {
name: "travelerList",
data() {
return {
showType: 1,
travelList: [],
addressList: []
};
},
onShow() {
this.init()
},
methods: {
init () {
//
// this.Post({}, "/api/user/contactList").then(res => {
// if (res) this.travelList = res.data
// })
//
this.Post({}, '/api/user/consigneeList').then(res => {
if(res) this.addressList = res.data
})
},
delet(item) {
let that = this
uni.showModal({
title: '提示',
content: '确定要删除该出行人吗?',
success: function(res) {
if (res.confirm) {
that.Post({
id: item.id
}, "/api/user/delDetail").then(res => {
if (res) {
uni.showToast({
icon: "none",
title: res.msg
})
}
that.init()
})
} else if (res.cancel) {
console.log('用户点击取消');
}
}
});
},
defaultC(item) {
this.Post({
id: item.id,
is_default: 1
}, "/api/user/editContact").then(res => {
if (res.code == 1) {
this.travelList.forEach(i => i.is_default = 0)
item.is_default = !item.is_default
}
})
},
//
edit(id){
let url = ''
if(this.showType) {
url = '/subPackages/user/myAddressAdd?id='+id
}else {
url = "/subPackages/user/myContactsAdd?type=edit&id="+id
}
uni.navigateTo({
url: url
});
}
}
}
</script>
<style lang="scss" scoped>
.bg {
position: relative;
background: #F7F7F7;
height: 100vh;
overflow-x: hidden;
padding-bottom: 200rpx;
}
.cancat-nav{
width: 100%;
display: flex;
height: 112rpx;
flex-shrink: 0;
background-color: #fff;
.concat-nav-item{
ffont-weight: 500;
font-size: 35rpx;
color: #000000;
height: 100%;
text-align: center;
position: relative;
padding-top: 35rpx;
}
.concat-nav-item.active{
color: #000000;
}
.active-bar{
width: 73rpx;
height: 7rpx;
background: #515150;
border-radius: 3rpx;
margin: auto;
position: absolute;
bottom: 0;
left: 0;
right: 0;
}
}
.item {
width: 697rpx;
background: #FFFFFF;
border-radius: 13rpx;
margin: 28rpx auto 0;
padding: 26rpx;
.name,
.idcard {
display: flex;
font-size: 31rpx;
font-weight: bold;
color: #333333;
}
.name {
view:last-child{
margin-left: 40rpx;
}
span {
padding: 3rpx 9rpx;
background: #515150;
border-radius: 7rpx;
font-weight: bold;
font-size: 24rpx;
color: #FFFFFF;
margin-left: 15rpx;
}
}
.idcard {
margin-top: 26rpx;
view:last-child {
margin-left: 14rpx;
font-weight: 400;
max-width: 492rpx;
}
}
.item-btn {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 40rpx;
.choice {
display: flex;
align-items: center;
view:last-child {
margin-left: 20rpx;
font-size: 27rpx;
font-weight: 500;
color: #333333;
}
image {
width: 40rpx;
height: 40rpx;
}
.yuan {
width: 40rpx;
height: 40rpx;
background: rgba(255, 200, 37, 0);
border: 1rpx solid #999999;
border-radius: 20rpx;
}
}
.btn-list {
display: flex;
align-items: center;
view {
width: 134rpx;
height: 54rpx;
background: #FFFFFF;
border: 1px solid #999999;
border-radius: 27rpx;
font-size: 27rpx;
font-weight: 500;
color: #333333;
line-height: 52rpx;
text-align: center;
}
view:last-child {
margin-left: 14rpx;
}
}
}
}
.btn-box {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
height: 200rpx;
background-color: #fff;
.btn {
width: 697rpx;
line-height: 80rpx;
background: #6CA5AA;
border-radius: 37rpx;
font-weight: 500;
font-size: 36rpx;
color: #fff;
text-align: center;
position: fixed;
bottom: 53rpx;
left: 26.67rpx;
}
}
</style>

59
subPackages/video/video.vue

@ -0,0 +1,59 @@
<template>
<view class="bg">
<video :src="showImg(src)" controls class="myVideo" :poster="poster"></video>
<view class="title">
{{title}}
</view>
</view>
</template>
<script>
export default {
data() {
return {
src: '',
poster: '',
title: ''
}
},
onLoad(option) {
const item = JSON.parse(decodeURIComponent(option.item));
this.src = item.video_file
this.poster = item.image
this.title = item.title
},
onReady() {
uni.setNavigationBarTitle({
title: this.title
})
}
}
</script>
<style scoped lang="scss">
.bg {
width: 100%;
height: 100vh;
position: relative;
}
.myVideo {
width: 100%;
height: 100vh;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
}
.title {
width: 699rpx;
font-family: PingFang SC;
font-weight: 500;
font-size: 29rpx;
color: #FFFFFF;
position: fixed;
left: 28rpx;
bottom: 140rpx;
}
</style>

28
subPackages/webPage/webPage.vue

@ -0,0 +1,28 @@
<template>
<web-view :src="url"></web-view>
</template>
<script>
export default {
data() {
return {
url: ''
}
},
onLoad(option) {
if (option.url) {
this.url = decodeURIComponent(option.url)
}else{
this.url = uni.getStorageSync('webUrl')
}
console.log(this.url)
// 使web-view
}
}
</script>
<style lang="scss" scoped>
</style>

2
uni.scss

@ -41,7 +41,7 @@ $uni-border-color:#c8c7cc;
/* 文字尺寸 */ /* 文字尺寸 */
$uni-font-size-sm:12px; $uni-font-size-sm:12px;
$uni-font-size-base:14px; $uni-font-size-base:14px;
$uni-font-size-lg:16px; $uni-font-size-lg:16;
/* 图片尺寸 */ /* 图片尺寸 */
$uni-img-size-sm:20px; $uni-img-size-sm:20px;

31
uni_modules/uni-badge/changelog.md

@ -0,0 +1,31 @@
## 1.2.1(2022-09-05)
- 修复 当 text 超过 max-num 时,badge 的宽度计算是根据 text 的长度计算,更改为 css 计算实际展示宽度,详见:[https://ask.dcloud.net.cn/question/150473](https://ask.dcloud.net.cn/question/150473)
## 1.2.0(2021-11-19)
- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-badge](https://uniapp.dcloud.io/component/uniui/uni-badge)
## 1.1.7(2021-11-08)
- 优化 升级ui
- 修改 size 属性默认值调整为 small
- 修改 type 属性,默认值调整为 error,info 替换 default
## 1.1.6(2021-09-22)
- 修复 在字节小程序上样式不生效的 bug
## 1.1.5(2021-07-30)
- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 1.1.4(2021-07-29)
- 修复 去掉 nvue 不支持css 的 align-self 属性,nvue 下不暂支持 absolute 属性
## 1.1.3(2021-06-24)
- 优化 示例项目
## 1.1.1(2021-05-12)
- 新增 组件示例地址
## 1.1.0(2021-05-12)
- 新增 uni-badge 的 absolute 属性,支持定位
- 新增 uni-badge 的 offset 属性,支持定位偏移
- 新增 uni-badge 的 is-dot 属性,支持仅显示有一个小点
- 新增 uni-badge 的 max-num 属性,支持自定义封顶的数字值,超过 99 显示99+
- 优化 uni-badge 属性 custom-style, 支持以对象形式自定义样式
## 1.0.7(2021-05-07)
- 修复 uni-badge 在 App 端,数字小于10时不是圆形的bug
- 修复 uni-badge 在父元素不是 flex 布局时,宽度缩小的bug
- 新增 uni-badge 属性 custom-style, 支持自定义样式
## 1.0.6(2021-02-04)
- 调整为uni_modules目录规范

268
uni_modules/uni-badge/components/uni-badge/uni-badge.vue

@ -0,0 +1,268 @@
<template>
<view class="uni-badge--x">
<slot />
<text v-if="text" :class="classNames" :style="[positionStyle, customStyle, dotStyle]"
class="uni-badge" @click="onClick()">{{displayValue}}</text>
</view>
</template>
<script>
/**
* Badge 数字角标
* @description 数字角标一般和其它控件列表9宫格等配合使用用于进行数量提示默认为实心灰色背景
* @tutorial https://ext.dcloud.net.cn/plugin?id=21
* @property {String} text 角标内容
* @property {String} size = [normal|small] 角标内容
* @property {String} type = [info|primary|success|warning|error] 颜色类型
* @value info 灰色
* @value primary 蓝色
* @value success 绿色
* @value warning 黄色
* @value error 红色
* @property {String} inverted = [true|false] 是否无需背景颜色
* @property {Number} maxNum 展示封顶的数字值超过 99 显示 99+
* @property {String} absolute = [rightTop|rightBottom|leftBottom|leftTop] 开启绝对定位, 角标将定位到其包裹的标签的四角上
* @value rightTop 右上
* @value rightBottom 右下
* @value leftTop 左上
* @value leftBottom 左下
* @property {Array[number]} offset 距定位角中心点的偏移量只有存在 absolute 属性时有效例如[-10, -10] 表示向外偏移 10px[10, 10] 表示向 absolute 指定的内偏移 10px
* @property {String} isDot = [true|false] 是否显示为一个小点
* @event {Function} click 点击 Badge 触发事件
* @example <uni-badge text="1"></uni-badge>
*/
export default {
name: 'UniBadge',
emits: ['click'],
props: {
type: {
type: String,
default: 'error'
},
inverted: {
type: Boolean,
default: false
},
isDot: {
type: Boolean,
default: false
},
maxNum: {
type: Number,
default: 99
},
absolute: {
type: String,
default: ''
},
offset: {
type: Array,
default () {
return [0, 0]
}
},
text: {
type: [String, Number],
default: ''
},
size: {
type: String,
default: 'small'
},
customStyle: {
type: Object,
default () {
return {}
}
}
},
data() {
return {};
},
computed: {
width() {
return String(this.text).length * 8 + 12
},
classNames() {
const {
inverted,
type,
size,
absolute
} = this
return [
inverted ? 'uni-badge--' + type + '-inverted' : '',
'uni-badge--' + type,
'uni-badge--' + size,
absolute ? 'uni-badge--absolute' : ''
].join(' ')
},
positionStyle() {
if (!this.absolute) return {}
let w = this.width / 2,
h = 10
if (this.isDot) {
w = 5
h = 5
}
const x = `${- w + this.offset[0]}px`
const y = `${- h + this.offset[1]}px`
const whiteList = {
rightTop: {
right: x,
top: y
},
rightBottom: {
right: x,
bottom: y
},
leftBottom: {
left: x,
bottom: y
},
leftTop: {
left: x,
top: y
}
}
const match = whiteList[this.absolute]
return match ? match : whiteList['rightTop']
},
dotStyle() {
if (!this.isDot) return {}
return {
width: '10px',
minWidth: '0',
height: '10px',
padding: '0',
borderRadius: '10px'
}
},
displayValue() {
const {
isDot,
text,
maxNum
} = this
return isDot ? '' : (Number(text) > maxNum ? `${maxNum}+` : text)
}
},
methods: {
onClick() {
this.$emit('click');
}
}
};
</script>
<style lang="scss" >
$uni-primary: #2979ff !default;
$uni-success: #4cd964 !default;
$uni-warning: #f0ad4e !default;
$uni-error: #dd524d !default;
$uni-info: #909399 !default;
$bage-size: 12px;
$bage-small: scale(0.8);
.uni-badge--x {
/* #ifdef APP-NVUE */
// align-self: flex-start;
/* #endif */
/* #ifndef APP-NVUE */
display: inline-block;
/* #endif */
position: relative;
}
.uni-badge--absolute {
position: absolute;
}
.uni-badge--small {
transform: $bage-small;
transform-origin: center center;
}
.uni-badge {
/* #ifndef APP-NVUE */
display: flex;
overflow: hidden;
box-sizing: border-box;
/* #endif */
justify-content: center;
flex-direction: row;
height: 20px;
min-width: 20px;
padding: 0 4px;
line-height: 18px;
color: #fff;
border-radius: 100px;
background-color: $uni-info;
background-color: transparent;
border: 1px solid #fff;
text-align: center;
font-family: 'Helvetica Neue', Helvetica, sans-serif;
font-feature-settings: "tnum";
font-size: $bage-size;
/* #ifdef H5 */
z-index: 999;
cursor: pointer;
/* #endif */
&--info {
color: #fff;
background-color: $uni-info;
}
&--primary {
background-color: $uni-primary;
}
&--success {
background-color: $uni-success;
}
&--warning {
background-color: $uni-warning;
}
&--error {
background-color: $uni-error;
}
&--inverted {
padding: 0 5px 0 0;
color: $uni-info;
}
&--info-inverted {
color: $uni-info;
background-color: transparent;
}
&--primary-inverted {
color: $uni-primary;
background-color: transparent;
}
&--success-inverted {
color: $uni-success;
background-color: transparent;
}
&--warning-inverted {
color: $uni-warning;
background-color: transparent;
}
&--error-inverted {
color: $uni-error;
background-color: transparent;
}
}
</style>

85
uni_modules/uni-badge/package.json

@ -0,0 +1,85 @@
{
"id": "uni-badge",
"displayName": "uni-badge 数字角标",
"version": "1.2.1",
"description": "数字角标(徽章)组件,在元素周围展示消息提醒,一般用于列表、九宫格、按钮等地方。",
"keywords": [
"",
"badge",
"uni-ui",
"uniui",
"数字角标",
"徽章"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
"type": "component-vue"
},
"uni_modules": {
"dependencies": ["uni-scss"],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "y",
"联盟": "y"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

10
uni_modules/uni-badge/readme.md

@ -0,0 +1,10 @@
## Badge 数字角标
> **组件名:uni-badge**
> 代码块: `uBadge`
数字角标一般和其它控件(列表、9宫格等)配合使用,用于进行数量提示,默认为实心灰色背景,
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-badge)
#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839

20
uni_modules/uni-calendar/changelog.md

@ -0,0 +1,20 @@
## 1.4.7(2022-09-16)
- 可以使用 uni-scss 控制主题色
## 1.4.6(2022-09-08)
- fix: 表头年月切换,导致改变当前日期为选择月1号,且未触发change事件
## 1.4.5(2022-02-25)
- 修复 条件编译 nvue 不支持的 css 样式
## 1.4.4(2022-02-25)
- 修复 条件编译 nvue 不支持的 css 样式
## 1.4.3(2021-09-22)
- 修复 startDate、 endDate 属性失效的 bug
## 1.4.2(2021-08-24)
- 新增 支持国际化
## 1.4.1(2021-08-05)
- 修复 弹出层被 tabbar 遮盖 bug
## 1.4.0(2021-07-30)
- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 1.3.16(2021-05-12)
- 新增 组件示例地址
## 1.3.15(2021-02-04)
- 调整为uni_modules目录规范

546
uni_modules/uni-calendar/components/uni-calendar/calendar.js

@ -0,0 +1,546 @@
/**
* @1900-2100区间内的公历农历互转
* @charset UTF-8
* @github https://github.com/jjonline/calendar.js
* @Author Jea杨(JJonline@JJonline.Cn)
* @Time 2014-7-21
* @Time 2016-8-13 Fixed 2033hexAttribution Annals
* @Time 2016-9-25 Fixed lunar LeapMonth Param Bug
* @Time 2017-7-24 Fixed use getTerm Func Param Error.use solar year,NOT lunar year
* @Version 1.0.3
* @公历转农历calendar.solar2lunar(1987,11,01); //[you can ignore params of prefix 0]
* @农历转公历calendar.lunar2solar(1987,09,10); //[you can ignore params of prefix 0]
*/
/* eslint-disable */
var calendar = {
/**
* 农历1900-2100的润大小信息表
* @Array Of Property
* @return Hex
*/
lunarInfo: [0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2, // 1900-1909
0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977, // 1910-1919
0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, // 1920-1929
0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950, // 1930-1939
0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, // 1940-1949
0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0, // 1950-1959
0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, // 1960-1969
0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6, // 1970-1979
0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570, // 1980-1989
0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x05ac0, 0x0ab60, 0x096d5, 0x092e0, // 1990-1999
0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5, // 2000-2009
0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, // 2010-2019
0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530, // 2020-2029
0x05aa0, 0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, // 2030-2039
0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0, // 2040-2049
/** Add By JJonline@JJonline.Cn**/
0x14b63, 0x09370, 0x049f8, 0x04970, 0x064b0, 0x168a6, 0x0ea50, 0x06b20, 0x1a6c4, 0x0aae0, // 2050-2059
0x0a2e0, 0x0d2e3, 0x0c960, 0x0d557, 0x0d4a0, 0x0da50, 0x05d55, 0x056a0, 0x0a6d0, 0x055d4, // 2060-2069
0x052d0, 0x0a9b8, 0x0a950, 0x0b4a0, 0x0b6a6, 0x0ad50, 0x055a0, 0x0aba4, 0x0a5b0, 0x052b0, // 2070-2079
0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160, // 2080-2089
0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252, // 2090-2099
0x0d520], // 2100
/**
* 公历每个月份的天数普通表
* @Array Of Property
* @return Number
*/
solarMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
/**
* 天干地支之天干速查表
* @Array Of Property trans["甲","乙","丙","丁","戊","己","庚","辛","壬","癸"]
* @return Cn string
*/
Gan: ['\u7532', '\u4e59', '\u4e19', '\u4e01', '\u620a', '\u5df1', '\u5e9a', '\u8f9b', '\u58ec', '\u7678'],
/**
* 天干地支之地支速查表
* @Array Of Property
* @trans["子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥"]
* @return Cn string
*/
Zhi: ['\u5b50', '\u4e11', '\u5bc5', '\u536f', '\u8fb0', '\u5df3', '\u5348', '\u672a', '\u7533', '\u9149', '\u620c', '\u4ea5'],
/**
* 天干地支之地支速查表<=>生肖
* @Array Of Property
* @trans["鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪"]
* @return Cn string
*/
Animals: ['\u9f20', '\u725b', '\u864e', '\u5154', '\u9f99', '\u86c7', '\u9a6c', '\u7f8a', '\u7334', '\u9e21', '\u72d7', '\u732a'],
/**
* 24节气速查表
* @Array Of Property
* @trans["小寒","大寒","立春","雨水","惊蛰","春分","清明","谷雨","立夏","小满","芒种","夏至","小暑","大暑","立秋","处暑","白露","秋分","寒露","霜降","立冬","小雪","大雪","冬至"]
* @return Cn string
*/
solarTerm: ['\u5c0f\u5bd2', '\u5927\u5bd2', '\u7acb\u6625', '\u96e8\u6c34', '\u60ca\u86f0', '\u6625\u5206', '\u6e05\u660e', '\u8c37\u96e8', '\u7acb\u590f', '\u5c0f\u6ee1', '\u8292\u79cd', '\u590f\u81f3', '\u5c0f\u6691', '\u5927\u6691', '\u7acb\u79cb', '\u5904\u6691', '\u767d\u9732', '\u79cb\u5206', '\u5bd2\u9732', '\u971c\u964d', '\u7acb\u51ac', '\u5c0f\u96ea', '\u5927\u96ea', '\u51ac\u81f3'],
/**
* 1900-2100各年的24节气日期速查表
* @Array Of Property
* @return 0x string For splice
*/
sTermInfo: ['9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f',
'97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
'97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa',
'97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f',
'b027097bd097c36b0b6fc9274c91aa', '9778397bd19801ec9210c965cc920e', '97b6b97bd19801ec95f8c965cc920f',
'97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2', '9778397bd197c36c9210c9274c91aa',
'97b6b97bd19801ec95f8c965cc920e', '97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2',
'9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec95f8c965cc920e', '97bcf97c3598082c95f8e1cfcc920f',
'97bd097bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e',
'97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722',
'9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f',
'97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
'97bcf97c359801ec95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd097bd07f595b0b6fc920fb0722',
'9778397bd097c36b0b6fc9210c8dc2', '9778397bd19801ec9210c9274c920e', '97b6b97bd19801ec95f8c965cc920f',
'97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e',
'97b6b97bd19801ec95f8c965cc920f', '97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2',
'9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bd07f1487f595b0b0bc920fb0722',
'7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
'97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
'9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f531b0b0bb0b6fb0722',
'7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
'97bcf7f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b97bd19801ec9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
'9778397bd097c36b0b6fc9210c91aa', '97b6b97bd197c36c9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722',
'7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e',
'97b6b7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2',
'9778397bd097c36b0b70c9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722',
'7f0e397bd097c35b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721',
'7f0e27f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
'9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
'7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721',
'7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b7f0e47f531b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
'9778397bd097c36b0b6fc9210c91aa', '97b6b7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
'7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '977837f0e37f149b0723b0787b0721',
'7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c35b0b6fc9210c8dc2',
'977837f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722',
'7f0e397bd097c35b0b6fc9210c8dc2', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
'7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '977837f0e37f14998082b0787b06bd',
'7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
'977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
'7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
'7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd',
'7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
'977837f0e37f14998082b0723b06bd', '7f07e7f0e37f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
'7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b0721',
'7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f595b0b0bb0b6fb0722', '7f0e37f0e37f14898082b0723b02d5',
'7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f531b0b0bb0b6fb0722',
'7f0e37f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
'7f0e37f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd',
'7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35',
'7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
'7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f149b0723b0787b0721',
'7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0723b06bd',
'7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', '7f0e37f0e366aa89801eb072297c35',
'7ec967f0e37f14998082b0723b06bd', '7f07e7f0e37f14998083b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
'7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14898082b0723b02d5', '7f07e7f0e37f14998082b0787b0721',
'7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66aa89801e9808297c35', '665f67f0e37f14898082b0723b02d5',
'7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66a449801e9808297c35',
'665f67f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
'7f0e36665b66a449801e9808297c35', '665f67f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd',
'7f07e7f0e47f531b0723b0b6fb0721', '7f0e26665b66a449801e9808297c35', '665f67f0e37f1489801eb072297c35',
'7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722'],
/**
* 数字转中文速查表
* @Array Of Property
* @trans ['日','一','二','三','四','五','六','七','八','九','十']
* @return Cn string
*/
nStr1: ['\u65e5', '\u4e00', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341'],
/**
* 日期转农历称呼速查表
* @Array Of Property
* @trans ['初','十','廿','卅']
* @return Cn string
*/
nStr2: ['\u521d', '\u5341', '\u5eff', '\u5345'],
/**
* 月份转农历称呼速查表
* @Array Of Property
* @trans ['正','一','二','三','四','五','六','七','八','九','十','冬','腊']
* @return Cn string
*/
nStr3: ['\u6b63', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341', '\u51ac', '\u814a'],
/**
* 返回农历y年一整年的总天数
* @param lunar Year
* @return Number
* @eg:var count = calendar.lYearDays(1987) ;//count=387
*/
lYearDays: function (y) {
var i; var sum = 348
for (i = 0x8000; i > 0x8; i >>= 1) { sum += (this.lunarInfo[y - 1900] & i) ? 1 : 0 }
return (sum + this.leapDays(y))
},
/**
* 返回农历y年闰月是哪个月若y年没有闰月 则返回0
* @param lunar Year
* @return Number (0-12)
* @eg:var leapMonth = calendar.leapMonth(1987) ;//leapMonth=6
*/
leapMonth: function (y) { // 闰字编码 \u95f0
return (this.lunarInfo[y - 1900] & 0xf)
},
/**
* 返回农历y年闰月的天数 若该年没有闰月则返回0
* @param lunar Year
* @return Number (02930)
* @eg:var leapMonthDay = calendar.leapDays(1987) ;//leapMonthDay=29
*/
leapDays: function (y) {
if (this.leapMonth(y)) {
return ((this.lunarInfo[y - 1900] & 0x10000) ? 30 : 29)
}
return (0)
},
/**
* 返回农历y年m月非闰月的总天数计算m为闰月时的天数请使用leapDays方法
* @param lunar Year
* @return Number (-12930)
* @eg:var MonthDay = calendar.monthDays(1987,9) ;//MonthDay=29
*/
monthDays: function (y, m) {
if (m > 12 || m < 1) { return -1 }// 月份参数从1至12,参数错误返回-1
return ((this.lunarInfo[y - 1900] & (0x10000 >> m)) ? 30 : 29)
},
/**
* 返回公历(!)y年m月的天数
* @param solar Year
* @return Number (-128293031)
* @eg:var solarMonthDay = calendar.leapDays(1987) ;//solarMonthDay=30
*/
solarDays: function (y, m) {
if (m > 12 || m < 1) { return -1 } // 若参数错误 返回-1
var ms = m - 1
if (ms == 1) { // 2月份的闰平规律测算后确认返回28或29
return (((y % 4 == 0) && (y % 100 != 0) || (y % 400 == 0)) ? 29 : 28)
} else {
return (this.solarMonth[ms])
}
},
/**
* 农历年份转换为干支纪年
* @param lYear 农历年的年份数
* @return Cn string
*/
toGanZhiYear: function (lYear) {
var ganKey = (lYear - 3) % 10
var zhiKey = (lYear - 3) % 12
if (ganKey == 0) ganKey = 10// 如果余数为0则为最后一个天干
if (zhiKey == 0) zhiKey = 12// 如果余数为0则为最后一个地支
return this.Gan[ganKey - 1] + this.Zhi[zhiKey - 1]
},
/**
* 公历月日判断所属星座
* @param cMonth [description]
* @param cDay [description]
* @return Cn string
*/
toAstro: function (cMonth, cDay) {
var s = '\u9b54\u7faf\u6c34\u74f6\u53cc\u9c7c\u767d\u7f8a\u91d1\u725b\u53cc\u5b50\u5de8\u87f9\u72ee\u5b50\u5904\u5973\u5929\u79e4\u5929\u874e\u5c04\u624b\u9b54\u7faf'
var arr = [20, 19, 21, 21, 21, 22, 23, 23, 23, 23, 22, 22]
return s.substr(cMonth * 2 - (cDay < arr[cMonth - 1] ? 2 : 0), 2) + '\u5ea7'// 座
},
/**
* 传入offset偏移量返回干支
* @param offset 相对甲子的偏移量
* @return Cn string
*/
toGanZhi: function (offset) {
return this.Gan[offset % 10] + this.Zhi[offset % 12]
},
/**
* 传入公历(!)y年获得该年第n个节气的公历日期
* @param y公历年(1900-2100)n二十四节气中的第几个节气(1~24)从n=1(小寒)算起
* @return day Number
* @eg:var _24 = calendar.getTerm(1987,3) ;//_24=4;意即1987年2月4日立春
*/
getTerm: function (y, n) {
if (y < 1900 || y > 2100) { return -1 }
if (n < 1 || n > 24) { return -1 }
var _table = this.sTermInfo[y - 1900]
var _info = [
parseInt('0x' + _table.substr(0, 5)).toString(),
parseInt('0x' + _table.substr(5, 5)).toString(),
parseInt('0x' + _table.substr(10, 5)).toString(),
parseInt('0x' + _table.substr(15, 5)).toString(),
parseInt('0x' + _table.substr(20, 5)).toString(),
parseInt('0x' + _table.substr(25, 5)).toString()
]
var _calday = [
_info[0].substr(0, 1),
_info[0].substr(1, 2),
_info[0].substr(3, 1),
_info[0].substr(4, 2),
_info[1].substr(0, 1),
_info[1].substr(1, 2),
_info[1].substr(3, 1),
_info[1].substr(4, 2),
_info[2].substr(0, 1),
_info[2].substr(1, 2),
_info[2].substr(3, 1),
_info[2].substr(4, 2),
_info[3].substr(0, 1),
_info[3].substr(1, 2),
_info[3].substr(3, 1),
_info[3].substr(4, 2),
_info[4].substr(0, 1),
_info[4].substr(1, 2),
_info[4].substr(3, 1),
_info[4].substr(4, 2),
_info[5].substr(0, 1),
_info[5].substr(1, 2),
_info[5].substr(3, 1),
_info[5].substr(4, 2)
]
return parseInt(_calday[n - 1])
},
/**
* 传入农历数字月份返回汉语通俗表示法
* @param lunar month
* @return Cn string
* @eg:var cnMonth = calendar.toChinaMonth(12) ;//cnMonth='腊月'
*/
toChinaMonth: function (m) { // 月 => \u6708
if (m > 12 || m < 1) { return -1 } // 若参数错误 返回-1
var s = this.nStr3[m - 1]
s += '\u6708'// 加上月字
return s
},
/**
* 传入农历日期数字返回汉字表示法
* @param lunar day
* @return Cn string
* @eg:var cnDay = calendar.toChinaDay(21) ;//cnMonth='廿一'
*/
toChinaDay: function (d) { // 日 => \u65e5
var s
switch (d) {
case 10:
s = '\u521d\u5341'; break
case 20:
s = '\u4e8c\u5341'; break
break
case 30:
s = '\u4e09\u5341'; break
break
default :
s = this.nStr2[Math.floor(d / 10)]
s += this.nStr1[d % 10]
}
return (s)
},
/**
* 年份转生肖[!仅能大致转换] => 精确划分生肖分界线是立春
* @param y year
* @return Cn string
* @eg:var animal = calendar.getAnimal(1987) ;//animal='兔'
*/
getAnimal: function (y) {
return this.Animals[(y - 4) % 12]
},
/**
* 传入阳历年月日获得详细的公历农历object信息 <=>JSON
* @param y solar year
* @param m solar month
* @param d solar day
* @return JSON object
* @eg:console.log(calendar.solar2lunar(1987,11,01));
*/
solar2lunar: function (y, m, d) { // 参数区间1900.1.31~2100.12.31
// 年份限定、上限
if (y < 1900 || y > 2100) {
return -1// undefined转换为数字变为NaN
}
// 公历传参最下限
if (y == 1900 && m == 1 && d < 31) {
return -1
}
// 未传参 获得当天
if (!y) {
var objDate = new Date()
} else {
var objDate = new Date(y, parseInt(m) - 1, d)
}
var i; var leap = 0; var temp = 0
// 修正ymd参数
var y = objDate.getFullYear()
var m = objDate.getMonth() + 1
var d = objDate.getDate()
var offset = (Date.UTC(objDate.getFullYear(), objDate.getMonth(), objDate.getDate()) - Date.UTC(1900, 0, 31)) / 86400000
for (i = 1900; i < 2101 && offset > 0; i++) {
temp = this.lYearDays(i)
offset -= temp
}
if (offset < 0) {
offset += temp; i--
}
// 是否今天
var isTodayObj = new Date()
var isToday = false
if (isTodayObj.getFullYear() == y && isTodayObj.getMonth() + 1 == m && isTodayObj.getDate() == d) {
isToday = true
}
// 星期几
var nWeek = objDate.getDay()
var cWeek = this.nStr1[nWeek]
// 数字表示周几顺应天朝周一开始的惯例
if (nWeek == 0) {
nWeek = 7
}
// 农历年
var year = i
var leap = this.leapMonth(i) // 闰哪个月
var isLeap = false
// 效验闰月
for (i = 1; i < 13 && offset > 0; i++) {
// 闰月
if (leap > 0 && i == (leap + 1) && isLeap == false) {
--i
isLeap = true; temp = this.leapDays(year) // 计算农历闰月天数
} else {
temp = this.monthDays(year, i)// 计算农历普通月天数
}
// 解除闰月
if (isLeap == true && i == (leap + 1)) { isLeap = false }
offset -= temp
}
// 闰月导致数组下标重叠取反
if (offset == 0 && leap > 0 && i == leap + 1) {
if (isLeap) {
isLeap = false
} else {
isLeap = true; --i
}
}
if (offset < 0) {
offset += temp; --i
}
// 农历月
var month = i
// 农历日
var day = offset + 1
// 天干地支处理
var sm = m - 1
var gzY = this.toGanZhiYear(year)
// 当月的两个节气
// bugfix-2017-7-24 11:03:38 use lunar Year Param `y` Not `year`
var firstNode = this.getTerm(y, (m * 2 - 1))// 返回当月「节」为几日开始
var secondNode = this.getTerm(y, (m * 2))// 返回当月「节」为几日开始
// 依据12节气修正干支月
var gzM = this.toGanZhi((y - 1900) * 12 + m + 11)
if (d >= firstNode) {
gzM = this.toGanZhi((y - 1900) * 12 + m + 12)
}
// 传入的日期的节气与否
var isTerm = false
var Term = null
if (firstNode == d) {
isTerm = true
Term = this.solarTerm[m * 2 - 2]
}
if (secondNode == d) {
isTerm = true
Term = this.solarTerm[m * 2 - 1]
}
// 日柱 当月一日与 1900/1/1 相差天数
var dayCyclical = Date.UTC(y, sm, 1, 0, 0, 0, 0) / 86400000 + 25567 + 10
var gzD = this.toGanZhi(dayCyclical + d - 1)
// 该日期所属的星座
var astro = this.toAstro(m, d)
return { 'lYear': year, 'lMonth': month, 'lDay': day, 'Animal': this.getAnimal(year), 'IMonthCn': (isLeap ? '\u95f0' : '') + this.toChinaMonth(month), 'IDayCn': this.toChinaDay(day), 'cYear': y, 'cMonth': m, 'cDay': d, 'gzYear': gzY, 'gzMonth': gzM, 'gzDay': gzD, 'isToday': isToday, 'isLeap': isLeap, 'nWeek': nWeek, 'ncWeek': '\u661f\u671f' + cWeek, 'isTerm': isTerm, 'Term': Term, 'astro': astro }
},
/**
* 传入农历年月日以及传入的月份是否闰月获得详细的公历农历object信息 <=>JSON
* @param y lunar year
* @param m lunar month
* @param d lunar day
* @param isLeapMonth lunar month is leap or not.[如果是农历闰月第四个参数赋值true即可]
* @return JSON object
* @eg:console.log(calendar.lunar2solar(1987,9,10));
*/
lunar2solar: function (y, m, d, isLeapMonth) { // 参数区间1900.1.31~2100.12.1
var isLeapMonth = !!isLeapMonth
var leapOffset = 0
var leapMonth = this.leapMonth(y)
var leapDay = this.leapDays(y)
if (isLeapMonth && (leapMonth != m)) { return -1 }// 传参要求计算该闰月公历 但该年得出的闰月与传参的月份并不同
if (y == 2100 && m == 12 && d > 1 || y == 1900 && m == 1 && d < 31) { return -1 }// 超出了最大极限值
var day = this.monthDays(y, m)
var _day = day
// bugFix 2016-9-25
// if month is leap, _day use leapDays method
if (isLeapMonth) {
_day = this.leapDays(y, m)
}
if (y < 1900 || y > 2100 || d > _day) { return -1 }// 参数合法性效验
// 计算农历的时间差
var offset = 0
for (var i = 1900; i < y; i++) {
offset += this.lYearDays(i)
}
var leap = 0; var isAdd = false
for (var i = 1; i < m; i++) {
leap = this.leapMonth(y)
if (!isAdd) { // 处理闰月
if (leap <= i && leap > 0) {
offset += this.leapDays(y); isAdd = true
}
}
offset += this.monthDays(y, i)
}
// 转换闰月农历 需补充该年闰月的前一个月的时差
if (isLeapMonth) { offset += day }
// 1900年农历正月一日的公历时间为1900年1月30日0时0分0秒(该时间也是本农历的最开始起始点)
var stmap = Date.UTC(1900, 1, 30, 0, 0, 0)
var calObj = new Date((offset + d - 31) * 86400000 + stmap)
var cY = calObj.getUTCFullYear()
var cM = calObj.getUTCMonth() + 1
var cD = calObj.getUTCDate()
return this.solar2lunar(cY, cM, cD)
}
}
export default calendar

12
uni_modules/uni-calendar/components/uni-calendar/i18n/en.json

@ -0,0 +1,12 @@
{
"uni-calender.ok": "ok",
"uni-calender.cancel": "cancel",
"uni-calender.today": "today",
"uni-calender.MON": "MON",
"uni-calender.TUE": "TUE",
"uni-calender.WED": "WED",
"uni-calender.THU": "THU",
"uni-calender.FRI": "FRI",
"uni-calender.SAT": "SAT",
"uni-calender.SUN": "SUN"
}

8
uni_modules/uni-calendar/components/uni-calendar/i18n/index.js

@ -0,0 +1,8 @@
import en from './en.json'
import zhHans from './zh-Hans.json'
import zhHant from './zh-Hant.json'
export default {
en,
'zh-Hans': zhHans,
'zh-Hant': zhHant
}

12
uni_modules/uni-calendar/components/uni-calendar/i18n/zh-Hans.json

@ -0,0 +1,12 @@
{
"uni-calender.ok": "确定",
"uni-calender.cancel": "取消",
"uni-calender.today": "今日",
"uni-calender.SUN": "日",
"uni-calender.MON": "一",
"uni-calender.TUE": "二",
"uni-calender.WED": "三",
"uni-calender.THU": "四",
"uni-calender.FRI": "五",
"uni-calender.SAT": "六"
}

12
uni_modules/uni-calendar/components/uni-calendar/i18n/zh-Hant.json

@ -0,0 +1,12 @@
{
"uni-calender.ok": "確定",
"uni-calender.cancel": "取消",
"uni-calender.today": "今日",
"uni-calender.SUN": "日",
"uni-calender.MON": "一",
"uni-calender.TUE": "二",
"uni-calender.WED": "三",
"uni-calender.THU": "四",
"uni-calender.FRI": "五",
"uni-calender.SAT": "六"
}

188
uni_modules/uni-calendar/components/uni-calendar/uni-calendar-item.vue

@ -0,0 +1,188 @@
<template>
<view class="uni-calendar-item__weeks-box" :class="{
'uni-calendar-item--disable':weeks.disable,
'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
'uni-calendar-item--checked':(calendar.fullDate === weeks.fullDate && !weeks.isDay) ,
'uni-calendar-item--before-checked':weeks.beforeMultiple,
'uni-calendar-item--multiple': weeks.multiple,
'uni-calendar-item--after-checked':weeks.afterMultiple,
}"
@click="choiceDate(weeks)">
<view class="uni-calendar-item__weeks-box-item">
<text v-if="selected&&weeks.extraInfo" class="uni-calendar-item__weeks-box-circle"></text>
<text class="uni-calendar-item__weeks-box-text" :class="{
'uni-calendar-item--isDay-text': weeks.isDay,
'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
'uni-calendar-item--before-checked':weeks.beforeMultiple,
'uni-calendar-item--multiple': weeks.multiple,
'uni-calendar-item--after-checked':weeks.afterMultiple,
'uni-calendar-item--disable':weeks.disable,
}">{{weeks.date}}</text>
<text v-if="!lunar&&!weeks.extraInfo && weeks.isDay" class="uni-calendar-item__weeks-lunar-text" :class="{
'uni-calendar-item--isDay-text':weeks.isDay,
'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
'uni-calendar-item--before-checked':weeks.beforeMultiple,
'uni-calendar-item--multiple': weeks.multiple,
'uni-calendar-item--after-checked':weeks.afterMultiple,
}">{{todayText}}</text>
<text v-if="lunar&&!weeks.extraInfo" class="uni-calendar-item__weeks-lunar-text" :class="{
'uni-calendar-item--isDay-text':weeks.isDay,
'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
'uni-calendar-item--before-checked':weeks.beforeMultiple,
'uni-calendar-item--multiple': weeks.multiple,
'uni-calendar-item--after-checked':weeks.afterMultiple,
'uni-calendar-item--disable':weeks.disable,
}">{{weeks.isDay ? todayText : (weeks.lunar.IDayCn === '初一'?weeks.lunar.IMonthCn:weeks.lunar.IDayCn)}}</text>
<text v-if="weeks.extraInfo&&weeks.extraInfo.info" class="uni-calendar-item__weeks-lunar-text" :class="{
'uni-calendar-item--extra':weeks.extraInfo.info,
'uni-calendar-item--isDay-text':weeks.isDay,
'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
'uni-calendar-item--before-checked':weeks.beforeMultiple,
'uni-calendar-item--multiple': weeks.multiple,
'uni-calendar-item--after-checked':weeks.afterMultiple,
'uni-calendar-item--disable':weeks.disable,
}">{{weeks.extraInfo.info}}</text>
</view>
</view>
</template>
<script>
import {
initVueI18n
} from '@dcloudio/uni-i18n'
import messages from './i18n/index.js'
const { t } = initVueI18n(messages)
export default {
emits:['change'],
props: {
weeks: {
type: Object,
default () {
return {}
}
},
calendar: {
type: Object,
default: () => {
return {}
}
},
selected: {
type: Array,
default: () => {
return []
}
},
lunar: {
type: Boolean,
default: false
}
},
computed: {
todayText() {
return t("uni-calender.today")
},
},
methods: {
choiceDate(weeks) {
this.$emit('change', weeks)
}
}
}
</script>
<style lang="scss" scoped>
$uni-font-size-base:14px;
$uni-text-color:#333;
$uni-font-size-sm:12px;
$uni-color-error: #e43d33;
$uni-opacity-disabled: 0.3;
$uni-text-color-disable:#c0c0c0;
$uni-primary: #2979ff !default;
.uni-calendar-item__weeks-box {
flex: 1;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: center;
align-items: center;
}
.uni-calendar-item__weeks-box-text {
font-size: $uni-font-size-base;
color: $uni-text-color;
}
.uni-calendar-item__weeks-lunar-text {
font-size: $uni-font-size-sm;
color: $uni-text-color;
}
.uni-calendar-item__weeks-box-item {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: center;
align-items: center;
width: 100rpx;
height: 100rpx;
}
.uni-calendar-item__weeks-box-circle {
position: absolute;
top: 5px;
right: 5px;
width: 8px;
height: 8px;
border-radius: 8px;
background-color: $uni-color-error;
}
.uni-calendar-item--disable {
background-color: rgba(249, 249, 249, $uni-opacity-disabled);
color: $uni-text-color-disable;
}
.uni-calendar-item--isDay-text {
color: $uni-primary;
}
.uni-calendar-item--isDay {
background-color: $uni-primary;
opacity: 0.8;
color: #fff;
}
.uni-calendar-item--extra {
color: $uni-color-error;
opacity: 0.8;
}
.uni-calendar-item--checked {
background-color: $uni-primary;
color: #fff;
opacity: 0.8;
}
.uni-calendar-item--multiple {
background-color: $uni-primary;
color: #fff;
opacity: 0.8;
}
.uni-calendar-item--before-checked {
background-color: #ff5a5f;
color: #fff;
}
.uni-calendar-item--after-checked {
background-color: #ff5a5f;
color: #fff;
}
</style>

562
uni_modules/uni-calendar/components/uni-calendar/uni-calendar.vue

@ -0,0 +1,562 @@
<template>
<view class="uni-calendar">
<view v-if="!insert&&show" class="uni-calendar__mask" :class="{'uni-calendar--mask-show':aniMaskShow}" @click="clean"></view>
<view v-if="insert || show" class="uni-calendar__content" :class="{'uni-calendar--fixed':!insert,'uni-calendar--ani-show':aniMaskShow}">
<view v-if="!insert" class="uni-calendar__header uni-calendar--fixed-top">
<view class="uni-calendar__header-btn-box" @click="close">
<text class="uni-calendar__header-text uni-calendar--fixed-width">{{cancelText}}</text>
</view>
<view class="uni-calendar__header-btn-box" @click="confirm">
<text class="uni-calendar__header-text uni-calendar--fixed-width">{{okText}}</text>
</view>
</view>
<view class="uni-calendar__header">
<view class="uni-calendar__header-btn-box" @click.stop="pre">
<view class="uni-calendar__header-btn uni-calendar--left"></view>
</view>
<picker mode="date" :value="date" fields="month" @change="bindDateChange">
<text class="uni-calendar__header-text">{{ (nowDate.year||'') +' / '+( nowDate.month||'')}}</text>
</picker>
<view class="uni-calendar__header-btn-box" @click.stop="next">
<view class="uni-calendar__header-btn uni-calendar--right"></view>
</view>
<text class="uni-calendar__backtoday" @click="backtoday">{{todayText}}</text>
</view>
<view class="uni-calendar__box">
<view v-if="showMonth" class="uni-calendar__box-bg">
<text class="uni-calendar__box-bg-text">{{nowDate.month}}</text>
</view>
<view class="uni-calendar__weeks">
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text">{{SUNText}}</text>
</view>
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text">{{monText}}</text>
</view>
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text">{{TUEText}}</text>
</view>
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text">{{WEDText}}</text>
</view>
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text">{{THUText}}</text>
</view>
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text">{{FRIText}}</text>
</view>
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text">{{SATText}}</text>
</view>
</view>
<view class="uni-calendar__weeks" v-for="(item,weekIndex) in weeks" :key="weekIndex">
<view class="uni-calendar__weeks-item" v-for="(weeks,weeksIndex) in item" :key="weeksIndex">
<calendar-item class="uni-calendar-item--hook" :weeks="weeks" :calendar="calendar" :selected="selected" :lunar="lunar" @change="choiceDate"></calendar-item>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import Calendar from './util.js';
import calendarItem from './uni-calendar-item.vue'
import {
initVueI18n
} from '@dcloudio/uni-i18n'
import messages from './i18n/index.js'
const { t } = initVueI18n(messages)
/**
* Calendar 日历
* @description 日历组件可以查看日期选择任意范围内的日期打点操作常用场景如酒店日期预订火车机票选择购买日期上下班打卡等
* @tutorial https://ext.dcloud.net.cn/plugin?id=56
* @property {String} date 自定义当前时间默认为今天
* @property {Boolean} lunar 显示农历
* @property {String} startDate 日期选择范围-开始日期
* @property {String} endDate 日期选择范围-结束日期
* @property {Boolean} range 范围选择
* @property {Boolean} insert = [true|false] 插入模式,默认为false
* @value true 弹窗模式
* @value false 插入模式
* @property {Boolean} clearDate = [true|false] 弹窗模式是否清空上次选择内容
* @property {Array} selected 打点期待格式[{date: '2019-06-27', info: '签到', data: { custom: '自定义信息', name: '自定义消息头',xxx:xxx... }}]
* @property {Boolean} showMonth 是否选择月份为背景
* @event {Function} change 日期改变`insert :ture` 时生效
* @event {Function} confirm 确认选择`insert :false` 时生效
* @event {Function} monthSwitch 切换月份时触发
* @example <uni-calendar :insert="true":lunar="true" :start-date="'2019-3-2'":end-date="'2019-5-20'"@change="change" />
*/
export default {
components: {
calendarItem
},
emits:['close','confirm','change','monthSwitch'],
props: {
date: {
type: String,
default: ''
},
selected: {
type: Array,
default () {
return []
}
},
lunar: {
type: Boolean,
default: false
},
startDate: {
type: String,
default: ''
},
endDate: {
type: String,
default: ''
},
range: {
type: Boolean,
default: false
},
insert: {
type: Boolean,
default: true
},
showMonth: {
type: Boolean,
default: true
},
clearDate: {
type: Boolean,
default: true
}
},
data() {
return {
show: false,
weeks: [],
calendar: {},
nowDate: '',
aniMaskShow: false
}
},
computed:{
/**
* for i18n
*/
okText() {
return t("uni-calender.ok")
},
cancelText() {
return t("uni-calender.cancel")
},
todayText() {
return t("uni-calender.today")
},
monText() {
return t("uni-calender.MON")
},
TUEText() {
return t("uni-calender.TUE")
},
WEDText() {
return t("uni-calender.WED")
},
THUText() {
return t("uni-calender.THU")
},
FRIText() {
return t("uni-calender.FRI")
},
SATText() {
return t("uni-calender.SAT")
},
SUNText() {
return t("uni-calender.SUN")
},
},
watch: {
date(newVal) {
// this.cale.setDate(newVal)
this.init(newVal)
},
startDate(val){
this.cale.resetSatrtDate(val)
this.cale.setDate(this.nowDate.fullDate)
this.weeks = this.cale.weeks
},
endDate(val){
this.cale.resetEndDate(val)
this.cale.setDate(this.nowDate.fullDate)
this.weeks = this.cale.weeks
},
selected(newVal) {
this.cale.setSelectInfo(this.nowDate.fullDate, newVal)
this.weeks = this.cale.weeks
}
},
created() {
//
this.cale = new Calendar({
// date: new Date(),
selected: this.selected,
startDate: this.startDate,
endDate: this.endDate,
range: this.range,
})
//
// this.cale.setDate(this.date)
this.init(this.date)
// this.setDay
},
methods: {
// 穿
clean() {},
bindDateChange(e) {
const value = e.detail.value + '-1'
console.log(this.cale.getDate(value));
this.setDate(value)
},
/**
* 初始化日期显示
* @param {Object} date
*/
init(date) {
this.cale.setDate(date)
this.weeks = this.cale.weeks
this.nowDate = this.calendar = this.cale.getInfo(date)
},
/**
* 打开日历弹窗
*/
open() {
//
if (this.clearDate && !this.insert) {
this.cale.cleanMultipleStatus()
// this.cale.setDate(this.date)
this.init(this.date)
}
this.show = true
this.$nextTick(() => {
setTimeout(() => {
this.aniMaskShow = true
}, 50)
})
},
/**
* 关闭日历弹窗
*/
close() {
this.aniMaskShow = false
this.$nextTick(() => {
setTimeout(() => {
this.show = false
this.$emit('close')
}, 300)
})
},
/**
* 确认按钮
*/
confirm() {
this.setEmit('confirm')
this.close()
},
/**
* 变化触发
*/
change() {
if (!this.insert) return
this.setEmit('change')
},
/**
* 选择月份触发
*/
monthSwitch() {
let {
year,
month
} = this.nowDate
this.$emit('monthSwitch', {
year,
month: Number(month)
})
},
/**
* 派发事件
* @param {Object} name
*/
setEmit(name) {
let {
year,
month,
date,
fullDate,
lunar,
extraInfo
} = this.calendar
this.$emit(name, {
range: this.cale.multipleStatus,
year,
month,
date,
fulldate: fullDate,
lunar,
extraInfo: extraInfo || {}
})
},
/**
* 选择天触发
* @param {Object} weeks
*/
choiceDate(weeks) {
if (weeks.disable) return
this.calendar = weeks
//
this.cale.setMultiple(this.calendar.fullDate)
this.weeks = this.cale.weeks
this.change()
},
/**
* 回到今天
*/
backtoday() {
console.log(this.cale.getDate(new Date()).fullDate);
let date = this.cale.getDate(new Date()).fullDate
// this.cale.setDate(date)
this.init(date)
this.change()
},
/**
* 上个月
*/
pre() {
const preDate = this.cale.getDate(this.nowDate.fullDate, -1, 'month').fullDate
this.setDate(preDate)
this.monthSwitch()
},
/**
* 下个月
*/
next() {
const nextDate = this.cale.getDate(this.nowDate.fullDate, +1, 'month').fullDate
this.setDate(nextDate)
this.monthSwitch()
},
/**
* 设置日期
* @param {Object} date
*/
setDate(date) {
this.cale.setDate(date)
this.weeks = this.cale.weeks
this.nowDate = this.cale.getInfo(date)
}
}
}
</script>
<style lang="scss" scoped>
$uni-bg-color-mask: rgba($color: #000000, $alpha: 0.4);
$uni-border-color: #EDEDED;
$uni-text-color: #333;
$uni-bg-color-hover:#f1f1f1;
$uni-font-size-base:14px;
$uni-text-color-placeholder: #808080;
$uni-color-subtitle: #555555;
$uni-text-color-grey:#999;
.uni-calendar {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
}
.uni-calendar__mask {
position: fixed;
bottom: 0;
top: 0;
left: 0;
right: 0;
background-color: $uni-bg-color-mask;
transition-property: opacity;
transition-duration: 0.3s;
opacity: 0;
/* #ifndef APP-NVUE */
z-index: 99;
/* #endif */
}
.uni-calendar--mask-show {
opacity: 1
}
.uni-calendar--fixed {
position: fixed;
/* #ifdef APP-NVUE */
bottom: 0;
/* #endif */
left: 0;
right: 0;
transition-property: transform;
transition-duration: 0.3s;
transform: translateY(460px);
/* #ifndef APP-NVUE */
bottom: calc(var(--window-bottom));
z-index: 99;
/* #endif */
}
.uni-calendar--ani-show {
transform: translateY(0);
}
.uni-calendar__content {
background-color: #fff;
}
.uni-calendar__header {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: center;
align-items: center;
height: 50px;
border-bottom-color: $uni-border-color;
border-bottom-style: solid;
border-bottom-width: 1px;
}
.uni-calendar--fixed-top {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: space-between;
border-top-color: $uni-border-color;
border-top-style: solid;
border-top-width: 1px;
}
.uni-calendar--fixed-width {
width: 50px;
// padding: 0 15px;
}
.uni-calendar__backtoday {
position: absolute;
right: 0;
top: 25rpx;
padding: 0 5px;
padding-left: 10px;
height: 25px;
line-height: 25px;
font-size: 12px;
border-top-left-radius: 25px;
border-bottom-left-radius: 25px;
color: $uni-text-color;
background-color: $uni-bg-color-hover;
}
.uni-calendar__header-text {
text-align: center;
width: 100px;
font-size: $uni-font-size-base;
color: $uni-text-color;
}
.uni-calendar__header-btn-box {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: center;
width: 50px;
height: 50px;
}
.uni-calendar__header-btn {
width: 10px;
height: 10px;
border-left-color: $uni-text-color-placeholder;
border-left-style: solid;
border-left-width: 2px;
border-top-color: $uni-color-subtitle;
border-top-style: solid;
border-top-width: 2px;
}
.uni-calendar--left {
transform: rotate(-45deg);
}
.uni-calendar--right {
transform: rotate(135deg);
}
.uni-calendar__weeks {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
}
.uni-calendar__weeks-item {
flex: 1;
}
.uni-calendar__weeks-day {
flex: 1;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: center;
align-items: center;
height: 45px;
border-bottom-color: #F5F5F5;
border-bottom-style: solid;
border-bottom-width: 1px;
}
.uni-calendar__weeks-day-text {
font-size: 14px;
}
.uni-calendar__box {
position: relative;
}
.uni-calendar__box-bg {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
justify-content: center;
align-items: center;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.uni-calendar__box-bg-text {
font-size: 200px;
font-weight: bold;
color: $uni-text-color-grey;
opacity: 0.1;
text-align: center;
/* #ifndef APP-NVUE */
line-height: 1;
/* #endif */
}
</style>

350
uni_modules/uni-calendar/components/uni-calendar/util.js

@ -0,0 +1,350 @@
import CALENDAR from './calendar.js'
class Calendar {
constructor({
date,
selected,
startDate,
endDate,
range
} = {}) {
// 当前日期
this.date = this.getDate(new Date()) // 当前初入日期
// 打点信息
this.selected = selected || [];
// 范围开始
this.startDate = startDate
// 范围结束
this.endDate = endDate
this.range = range
// 多选状态
this.cleanMultipleStatus()
// 每周日期
this.weeks = {}
// this._getWeek(this.date.fullDate)
}
/**
* 设置日期
* @param {Object} date
*/
setDate(date) {
this.selectDate = this.getDate(date)
this._getWeek(this.selectDate.fullDate)
}
/**
* 清理多选状态
*/
cleanMultipleStatus() {
this.multipleStatus = {
before: '',
after: '',
data: []
}
}
/**
* 重置开始日期
*/
resetSatrtDate(startDate) {
// 范围开始
this.startDate = startDate
}
/**
* 重置结束日期
*/
resetEndDate(endDate) {
// 范围结束
this.endDate = endDate
}
/**
* 获取任意时间
*/
getDate(date, AddDayCount = 0, str = 'day') {
if (!date) {
date = new Date()
}
if (typeof date !== 'object') {
date = date.replace(/-/g, '/')
}
const dd = new Date(date)
switch (str) {
case 'day':
dd.setDate(dd.getDate() + AddDayCount) // 获取AddDayCount天后的日期
break
case 'month':
if (dd.getDate() === 31) {
dd.setDate(dd.getDate() + AddDayCount)
} else {
dd.setMonth(dd.getMonth() + AddDayCount) // 获取AddDayCount天后的日期
}
break
case 'year':
dd.setFullYear(dd.getFullYear() + AddDayCount) // 获取AddDayCount天后的日期
break
}
const y = dd.getFullYear()
const m = dd.getMonth() + 1 < 10 ? '0' + (dd.getMonth() + 1) : dd.getMonth() + 1 // 获取当前月份的日期,不足10补0
const d = dd.getDate() < 10 ? '0' + dd.getDate() : dd.getDate() // 获取当前几号,不足10补0
return {
fullDate: y + '-' + m + '-' + d,
year: y,
month: m,
date: d,
day: dd.getDay()
}
}
/**
* 获取上月剩余天数
*/
_getLastMonthDays(firstDay, full) {
let dateArr = []
for (let i = firstDay; i > 0; i--) {
const beforeDate = new Date(full.year, full.month - 1, -i + 1).getDate()
dateArr.push({
date: beforeDate,
month: full.month - 1,
lunar: this.getlunar(full.year, full.month - 1, beforeDate),
disable: true
})
}
return dateArr
}
/**
* 获取本月天数
*/
_currentMonthDys(dateData, full) {
let dateArr = []
let fullDate = this.date.fullDate
for (let i = 1; i <= dateData; i++) {
let nowDate = full.year + '-' + (full.month < 10 ?
full.month : full.month) + '-' + (i < 10 ?
'0' + i : i)
// 是否今天
let isDay = fullDate === nowDate
// 获取打点信息
let info = this.selected && this.selected.find((item) => {
if (this.dateEqual(nowDate, item.date)) {
return item
}
})
// 日期禁用
let disableBefore = true
let disableAfter = true
if (this.startDate) {
// let dateCompBefore = this.dateCompare(this.startDate, fullDate)
// disableBefore = this.dateCompare(dateCompBefore ? this.startDate : fullDate, nowDate)
disableBefore = this.dateCompare(this.startDate, nowDate)
}
if (this.endDate) {
// let dateCompAfter = this.dateCompare(fullDate, this.endDate)
// disableAfter = this.dateCompare(nowDate, dateCompAfter ? this.endDate : fullDate)
disableAfter = this.dateCompare(nowDate, this.endDate)
}
let multiples = this.multipleStatus.data
let checked = false
let multiplesStatus = -1
if (this.range) {
if (multiples) {
multiplesStatus = multiples.findIndex((item) => {
return this.dateEqual(item, nowDate)
})
}
if (multiplesStatus !== -1) {
checked = true
}
}
let data = {
fullDate: nowDate,
year: full.year,
date: i,
multiple: this.range ? checked : false,
beforeMultiple: this.dateEqual(this.multipleStatus.before, nowDate),
afterMultiple: this.dateEqual(this.multipleStatus.after, nowDate),
month: full.month,
lunar: this.getlunar(full.year, full.month, i),
disable: !(disableBefore && disableAfter),
isDay
}
if (info) {
data.extraInfo = info
}
dateArr.push(data)
}
return dateArr
}
/**
* 获取下月天数
*/
_getNextMonthDays(surplus, full) {
let dateArr = []
for (let i = 1; i < surplus + 1; i++) {
dateArr.push({
date: i,
month: Number(full.month) + 1,
lunar: this.getlunar(full.year, Number(full.month) + 1, i),
disable: true
})
}
return dateArr
}
/**
* 获取当前日期详情
* @param {Object} date
*/
getInfo(date) {
if (!date) {
date = new Date()
}
const dateInfo = this.canlender.find(item => item.fullDate === this.getDate(date).fullDate)
return dateInfo
}
/**
* 比较时间大小
*/
dateCompare(startDate, endDate) {
// 计算截止时间
startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
// 计算详细项的截止时间
endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
if (startDate <= endDate) {
return true
} else {
return false
}
}
/**
* 比较时间是否相等
*/
dateEqual(before, after) {
// 计算截止时间
before = new Date(before.replace('-', '/').replace('-', '/'))
// 计算详细项的截止时间
after = new Date(after.replace('-', '/').replace('-', '/'))
if (before.getTime() - after.getTime() === 0) {
return true
} else {
return false
}
}
/**
* 获取日期范围内所有日期
* @param {Object} begin
* @param {Object} end
*/
geDateAll(begin, end) {
var arr = []
var ab = begin.split('-')
var ae = end.split('-')
var db = new Date()
db.setFullYear(ab[0], ab[1] - 1, ab[2])
var de = new Date()
de.setFullYear(ae[0], ae[1] - 1, ae[2])
var unixDb = db.getTime() - 24 * 60 * 60 * 1000
var unixDe = de.getTime() - 24 * 60 * 60 * 1000
for (var k = unixDb; k <= unixDe;) {
k = k + 24 * 60 * 60 * 1000
arr.push(this.getDate(new Date(parseInt(k))).fullDate)
}
return arr
}
/**
* 计算阴历日期显示
*/
getlunar(year, month, date) {
return CALENDAR.solar2lunar(year, month, date)
}
/**
* 设置打点
*/
setSelectInfo(data, value) {
this.selected = value
this._getWeek(data)
}
/**
* 获取多选状态
*/
setMultiple(fullDate) {
let {
before,
after
} = this.multipleStatus
if (!this.range) return
if (before && after) {
this.multipleStatus.before = ''
this.multipleStatus.after = ''
this.multipleStatus.data = []
} else {
if (!before) {
this.multipleStatus.before = fullDate
} else {
this.multipleStatus.after = fullDate
if (this.dateCompare(this.multipleStatus.before, this.multipleStatus.after)) {
this.multipleStatus.data = this.geDateAll(this.multipleStatus.before, this.multipleStatus.after);
} else {
this.multipleStatus.data = this.geDateAll(this.multipleStatus.after, this.multipleStatus.before);
}
}
}
this._getWeek(fullDate)
}
/**
* 获取每周数据
* @param {Object} dateData
*/
_getWeek(dateData) {
const {
year,
month
} = this.getDate(dateData)
let firstDay = new Date(year, month - 1, 1).getDay()
let currentDay = new Date(year, month, 0).getDate()
let dates = {
lastMonthDays: this._getLastMonthDays(firstDay, this.getDate(dateData)), // 上个月末尾几天
currentMonthDys: this._currentMonthDys(currentDay, this.getDate(dateData)), // 本月天数
nextMonthDays: [], // 下个月开始几天
weeks: []
}
let canlender = []
const surplus = 42 - (dates.lastMonthDays.length + dates.currentMonthDys.length)
dates.nextMonthDays = this._getNextMonthDays(surplus, this.getDate(dateData))
canlender = canlender.concat(dates.lastMonthDays, dates.currentMonthDys, dates.nextMonthDays)
let weeks = {}
// 拼接数组 上个月开始几天 + 本月天数+ 下个月开始几天
for (let i = 0; i < canlender.length; i++) {
if (i % 7 === 0) {
weeks[parseInt(i / 7)] = new Array(7)
}
weeks[parseInt(i / 7)][i % 7] = canlender[i]
}
this.canlender = canlender
this.weeks = weeks
}
//静态方法
// static init(date) {
// if (!this.instance) {
// this.instance = new Calendar(date);
// }
// return this.instance;
// }
}
export default Calendar

85
uni_modules/uni-calendar/package.json

@ -0,0 +1,85 @@
{
"id": "uni-calendar",
"displayName": "uni-calendar 日历",
"version": "1.4.7",
"description": "日历组件",
"keywords": [
"uni-ui",
"uniui",
"日历",
"",
"打卡",
"日历选择"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
"type": "component-vue"
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

103
uni_modules/uni-calendar/readme.md

@ -0,0 +1,103 @@
## Calendar 日历
> **组件名:uni-calendar**
> 代码块: `uCalendar`
日历组件
> **注意事项**
> 为了避免错误使用,给大家带来不好的开发体验,请在使用组件前仔细阅读下面的注意事项,可以帮你避免一些错误。
> - 本组件农历转换使用的js是 [@1900-2100区间内的公历、农历互转](https://github.com/jjonline/calendar.js)
> - 仅支持自定义组件模式
> - `date`属性传入的应该是一个 String ,如: 2019-06-27 ,而不是 new Date()
> - 通过 `insert` 属性来确定当前的事件是 @change 还是 @confirm 。理应合并为一个事件,但是为了区分模式,现使用两个事件,这里需要注意
> - 弹窗模式下无法阻止后面的元素滚动,如有需要阻止,请在弹窗弹出后,手动设置滚动元素为不可滚动
### 安装方式
本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。
如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55)
### 基本用法
在 ``template`` 中使用组件
```html
<view>
<uni-calendar
:insert="true"
:lunar="true"
:start-date="'2019-3-2'"
:end-date="'2019-5-20'"
@change="change"
/>
</view>
```
### 通过方法打开日历
需要设置 `insert``false`
```html
<view>
<uni-calendar
ref="calendar"
:insert="false"
@confirm="confirm"
/>
<button @click="open">打开日历</button>
</view>
```
```javascript
export default {
data() {
return {};
},
methods: {
open(){
this.$refs.calendar.open();
},
confirm(e) {
console.log(e);
}
}
};
```
## API
### Calendar Props
| 属性名 | 类型 | 默认值| 说明 |
| | |
| date | String |- | 自定义当前时间,默认为今天 |
| lunar | Boolean | false | 显示农历 |
| startDate | String |- | 日期选择范围-开始日期 |
| endDate | String |- | 日期选择范围-结束日期 |
| range | Boolean | false | 范围选择 |
| insert | Boolean | false | 插入模式,可选值,ture:插入模式;false:弹窗模式;默认为插入模式 |
|clearDate |Boolean |true |弹窗模式是否清空上次选择内容 |
| selected | Array |- | 打点,期待格式[{date: '2019-06-27', info: '签到', data: { custom: '自定义信息', name: '自定义消息头',xxx:xxx... }}] |
|showMonth | Boolean | true | 是否显示月份为背景 |
### Calendar Events
| 事件名 | 说明 |返回值|
| | | |
| open | 弹出日历组件,`insert :false` 时生效|- |
## 组件示例
点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/calendar/calendar](https://hellouniapp.dcloud.net.cn/pages/extUI/calendar/calendar)

26
uni_modules/uni-card/changelog.md

@ -0,0 +1,26 @@
## 1.3.1(2021-12-20)
- 修复 在vue页面下略缩图显示不正常的bug
## 1.3.0(2021-11-19)
- 重构插槽的用法 ,header 替换为 title
- 新增 actions 插槽
- 新增 cover 封面图属性和插槽
- 新增 padding 内容默认内边距离
- 新增 margin 卡片默认外边距离
- 新增 spacing 卡片默认内边距
- 新增 shadow 卡片阴影属性
- 取消 mode 属性,可使用组合插槽代替
- 取消 note 属性 ,使用actions插槽代替
- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-card](https://uniapp.dcloud.io/component/uniui/uni-card)
## 1.2.1(2021-07-30)
- 优化 vue3下事件警告的问题
## 1.2.0(2021-07-13)
- 组件兼容 vue3,如何创建vue3项目详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 1.1.8(2021-07-01)
- 优化 图文卡片无图片加载时,提供占位图标
- 新增 header 插槽,自定义卡片头部( 图文卡片 mode="style" 时,不支持)
- 修复 thumbnail 不存在仍然占位的 bug
## 1.1.7(2021-05-12)
- 新增 组件示例地址
## 1.1.6(2021-02-04)
- 调整为uni_modules目录规范

270
uni_modules/uni-card/components/uni-card/uni-card.vue

@ -0,0 +1,270 @@
<template>
<view class="uni-card" :class="{ 'uni-card--full': isFull, 'uni-card--shadow': isShadow,'uni-card--border':border}"
:style="{'margin':isFull?0:margin,'padding':spacing,'box-shadow':isShadow?shadow:''}">
<!-- 封面 -->
<slot name="cover">
<view v-if="cover" class="uni-card__cover">
<image class="uni-card__cover-image" mode="widthFix" @click="onClick('cover')" :src="cover"></image>
</view>
</slot>
<slot name="title">
<view v-if="title || extra" class="uni-card__header">
<!-- 卡片标题 -->
<view class="uni-card__header-box" @click="onClick('title')">
<view v-if="thumbnail" class="uni-card__header-avatar">
<image class="uni-card__header-avatar-image" :src="thumbnail" mode="aspectFit" />
</view>
<view class="uni-card__header-content">
<text class="uni-card__header-content-title uni-ellipsis">{{ title }}</text>
<text v-if="title&&subTitle"
class="uni-card__header-content-subtitle uni-ellipsis">{{ subTitle }}</text>
</view>
</view>
<view class="uni-card__header-extra" @click="onClick('extra')">
<text class="uni-card__header-extra-text">{{ extra }}</text>
</view>
</view>
</slot>
<!-- 卡片内容 -->
<view class="uni-card__content" :style="{padding:padding}" @click="onClick('content')">
<slot></slot>
</view>
<view class="uni-card__actions" @click="onClick('actions')">
<slot name="actions"></slot>
</view>
</view>
</template>
<script>
/**
* Card 卡片
* @description 卡片视图组件
* @tutorial https://ext.dcloud.net.cn/plugin?id=22
* @property {String} title 标题文字
* @property {String} subTitle 副标题
* @property {Number} padding 内容内边距
* @property {Number} margin 卡片外边距
* @property {Number} spacing 卡片内边距
* @property {String} extra 标题额外信息
* @property {String} cover 封面图本地路径需要引入
* @property {String} thumbnail 标题左侧缩略图
* @property {Boolean} is-full = [true | false] 卡片内容是否通栏 true 时将去除padding值
* @property {Boolean} is-shadow = [true | false] 卡片内容是否开启阴影
* @property {String} shadow 卡片阴影
* @property {Boolean} border 卡片边框
* @event {Function} click 点击 Card 触发事件
*/
export default {
name: 'UniCard',
emits: ['click'],
props: {
title: {
type: String,
default: ''
},
subTitle: {
type: String,
default: ''
},
padding: {
type: String,
default: '10px'
},
margin: {
type: String,
default: '15px'
},
spacing: {
type: String,
default: '0 10px'
},
extra: {
type: String,
default: ''
},
cover: {
type: String,
default: ''
},
thumbnail: {
type: String,
default: ''
},
isFull: {
//
type: Boolean,
default: false
},
isShadow: {
//
type: Boolean,
default: true
},
shadow: {
type: String,
default: '0px 0px 3px 1px rgba(0, 0, 0, 0.08)'
},
border: {
type: Boolean,
default: true
}
},
methods: {
onClick(type) {
this.$emit('click', type)
}
}
}
</script>
<style lang="scss">
$uni-border-3: #EBEEF5 !default;
$uni-shadow-base:0 0px 6px 1px rgba($color: #a5a5a5, $alpha: 0.2) !default;
$uni-main-color: #3a3a3a !default;
$uni-base-color: #6a6a6a !default;
$uni-secondary-color: #909399 !default;
$uni-spacing-sm: 8px !default;
$uni-border-color:$uni-border-3;
$uni-shadow: $uni-shadow-base;
$uni-card-title: 15px;
$uni-cart-title-color:$uni-main-color;
$uni-card-subtitle: 12px;
$uni-cart-subtitle-color:$uni-secondary-color;
$uni-card-spacing: 10px;
$uni-card-content-color: $uni-base-color;
.uni-card {
margin: $uni-card-spacing;
padding: 0 $uni-spacing-sm;
border-radius: 4px;
overflow: hidden;
font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, SimSun, sans-serif;
background-color: #fff;
flex: 1;
.uni-card__cover {
position: relative;
margin-top: $uni-card-spacing;
flex-direction: row;
overflow: hidden;
border-radius: 4px;
.uni-card__cover-image {
flex: 1;
// width: 100%;
/* #ifndef APP-PLUS */
vertical-align: middle;
/* #endif */
}
}
.uni-card__header {
display: flex;
border-bottom: 1px $uni-border-color solid;
flex-direction: row;
align-items: center;
padding: $uni-card-spacing;
overflow: hidden;
.uni-card__header-box {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex: 1;
flex-direction: row;
align-items: center;
overflow: hidden;
}
.uni-card__header-avatar {
width: 40px;
height: 40px;
overflow: hidden;
border-radius: 5px;
margin-right: $uni-card-spacing;
.uni-card__header-avatar-image {
flex: 1;
width: 40px;
height: 40px;
}
}
.uni-card__header-content {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: center;
flex: 1;
// height: 40px;
overflow: hidden;
.uni-card__header-content-title {
font-size: $uni-card-title;
color: $uni-cart-title-color;
// line-height: 22px;
}
.uni-card__header-content-subtitle {
font-size: $uni-card-subtitle;
margin-top: 5px;
color: $uni-cart-subtitle-color;
}
}
.uni-card__header-extra {
line-height: 12px;
.uni-card__header-extra-text {
font-size: 12px;
color: $uni-cart-subtitle-color;
}
}
}
.uni-card__content {
padding: $uni-card-spacing;
font-size: 14px;
color: $uni-card-content-color;
line-height: 22px;
}
.uni-card__actions {
font-size: 12px;
}
}
.uni-card--border {
border: 1px solid $uni-border-color;
}
.uni-card--shadow {
position: relative;
/* #ifndef APP-NVUE */
box-shadow: $uni-shadow;
/* #endif */
}
.uni-card--full {
margin: 0;
border-left-width: 0;
border-left-width: 0;
border-radius: 0;
}
/* #ifndef APP-NVUE */
.uni-card--full:after {
border-radius: 0;
}
/* #endif */
.uni-ellipsis {
/* #ifndef APP-NVUE */
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
/* #endif */
/* #ifdef APP-NVUE */
lines: 1;
/* #endif */
}
</style>

90
uni_modules/uni-card/package.json

@ -0,0 +1,90 @@
{
"id": "uni-card",
"displayName": "uni-card 卡片",
"version": "1.3.1",
"description": "Card 组件,提供常见的卡片样式。",
"keywords": [
"uni-ui",
"uniui",
"card",
"",
"卡片"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"category": [
"前端组件",
"通用组件"
],
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
},
"uni_modules": {
"dependencies": [
"uni-icons",
"uni-scss"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

12
uni_modules/uni-card/readme.md

@ -0,0 +1,12 @@
## Card 卡片
> **组件名:uni-card**
> 代码块: `uCard`
卡片视图组件。
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-card)
#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839

36
uni_modules/uni-collapse/changelog.md

@ -0,0 +1,36 @@
## 1.4.3(2022-01-25)
- 修复 初始化的时候 ,open 属性失效的bug
## 1.4.2(2022-01-21)
- 修复 微信小程序resize后组件收起的bug
## 1.4.1(2021-11-22)
- 修复 vue3中个别scss变量无法找到的问题
## 1.4.0(2021-11-19)
- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-collapse](https://uniapp.dcloud.io/component/uniui/uni-collapse)
## 1.3.3(2021-08-17)
- 优化 show-arrow 属性默认为true
## 1.3.2(2021-08-17)
- 新增 show-arrow 属性,控制是否显示右侧箭头
## 1.3.1(2021-07-30)
- 优化 vue3下小程序事件警告的问题
## 1.3.0(2021-07-30)
- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 1.2.2(2021-07-21)
- 修复 由1.2.0版本引起的 change 事件返回 undefined 的Bug
## 1.2.1(2021-07-21)
- 优化 组件示例
## 1.2.0(2021-07-21)
- 新增 组件折叠动画
- 新增 value\v-model 属性 ,动态修改面板折叠状态
- 新增 title 插槽 ,可定义面板标题
- 新增 border 属性 ,显示隐藏面板内容分隔线
- 新增 title-border 属性 ,显示隐藏面板标题分隔线
- 修复 resize 方法失效的Bug
- 修复 change 事件返回参数不正确的Bug
- 优化 H5、App 平台自动更具内容更新高度,无需调用 reszie() 方法
## 1.1.7(2021-05-12)
- 新增 组件示例地址
## 1.1.6(2021-02-05)
- 优化 组件引用关系,通过uni_modules引用组件
## 1.1.5(2021-02-05)
- 调整为uni_modules目录规范

402
uni_modules/uni-collapse/components/uni-collapse-item/uni-collapse-item.vue

@ -0,0 +1,402 @@
<template>
<view class="uni-collapse-item">
<!-- onClick(!isOpen) -->
<view @click="onClick(!isOpen)" class="uni-collapse-item__title"
:class="{'is-open':isOpen &&titleBorder === 'auto' ,'uni-collapse-item-border':titleBorder !== 'none'}">
<view class="uni-collapse-item__title-wrap">
<slot name="title">
<view class="uni-collapse-item__title-box" :class="{'is-disabled':disabled}">
<image v-if="thumb" :src="thumb" class="uni-collapse-item__title-img" />
<text class="uni-collapse-item__title-text">{{ title }}</text>
</view>
</slot>
</view>
<view v-if="showArrow"
:class="{ 'uni-collapse-item__title-arrow-active': isOpen, 'uni-collapse-item--animation': showAnimation === true }"
class="uni-collapse-item__title-arrow">
<uni-icons :color="disabled?'#ddd':'#bbb'" size="14" type="bottom" />
</view>
</view>
<view class="uni-collapse-item__wrap" :class="{'is--transition':showAnimation}"
:style="{height: (isOpen?height:0) +'px'}">
<view :id="elId" ref="collapse--hook" class="uni-collapse-item__wrap-content"
:class="{open:isheight,'uni-collapse-item--border':border&&isOpen}">
<slot></slot>
</view>
</view>
</view>
</template>
<script>
// #ifdef APP-NVUE
const dom = weex.requireModule('dom')
// #endif
/**
* CollapseItem 折叠面板子组件
* @description 折叠面板子组件
* @property {String} title 标题文字
* @property {String} thumb 标题左侧缩略图
* @property {String} name 唯一标志符
* @property {Boolean} open = [true|false] 是否展开组件
* @property {Boolean} titleBorder = [true|false] 是否显示标题分隔线
* @property {Boolean} border = [true|false] 是否显示分隔线
* @property {Boolean} disabled = [true|false] 是否展开面板
* @property {Boolean} showAnimation = [true|false] 开启动画
* @property {Boolean} showArrow = [true|false] 是否显示右侧箭头
*/
export default {
name: 'uniCollapseItem',
props: {
//
title: {
type: String,
default: ''
},
name: {
type: [Number, String],
default: ''
},
//
disabled: {
type: Boolean,
default: false
},
// #ifdef APP-PLUS
// ,app
showAnimation: {
type: Boolean,
default: false
},
// #endif
// #ifndef APP-PLUS
//
showAnimation: {
type: Boolean,
default: true
},
// #endif
//
open: {
type: Boolean,
default: false
},
//
thumb: {
type: String,
default: ''
},
// 线
titleBorder: {
type: String,
default: 'auto'
},
border: {
type: Boolean,
default: true
},
showArrow: {
type: Boolean,
default: true
}
},
data() {
// TODO IDbug
const elId = `Uni_${Math.ceil(Math.random() * 10e5).toString(36)}`
return {
isOpen: false,
isheight: null,
height: 0,
elId,
nameSync: 0
}
},
watch: {
open(val) {
this.isOpen = val
this.onClick(val, 'init')
}
},
updated(e) {
this.$nextTick(() => {
this.init(true)
})
},
created() {
this.collapse = this.getCollapse()
this.oldHeight = 0
this.onClick(this.open, 'init')
},
// #ifndef VUE3
// TODO vue2
destroyed() {
if (this.__isUnmounted) return
this.uninstall()
},
// #endif
// #ifdef VUE3
// TODO vue3
unmounted() {
this.__isUnmounted = true
this.uninstall()
},
// #endif
mounted() {
if (!this.collapse) return
if (this.name !== '') {
this.nameSync = this.name
} else {
this.nameSync = this.collapse.childrens.length + ''
}
if (this.collapse.names.indexOf(this.nameSync) === -1) {
this.collapse.names.push(this.nameSync)
} else {
console.warn(`name 值 ${this.nameSync} 重复`);
}
if (this.collapse.childrens.indexOf(this) === -1) {
this.collapse.childrens.push(this)
}
this.init()
},
methods: {
init(type) {
// #ifndef APP-NVUE
this.getCollapseHeight(type)
// #endif
// #ifdef APP-NVUE
this.getNvueHwight(type)
// #endif
},
uninstall() {
if (this.collapse) {
this.collapse.childrens.forEach((item, index) => {
if (item === this) {
this.collapse.childrens.splice(index, 1)
}
})
this.collapse.names.forEach((item, index) => {
if (item === this.nameSync) {
this.collapse.names.splice(index, 1)
}
})
}
},
onClick(isOpen, type) {
if (this.disabled) return
this.isOpen = isOpen
if (this.isOpen && this.collapse) {
this.collapse.setAccordion(this)
}
if (type !== 'init') {
this.collapse.onChange(isOpen, this)
}
},
getCollapseHeight(type, index = 0) {
const views = uni.createSelectorQuery().in(this)
views
.select(`#${this.elId}`)
.fields({
size: true
}, data => {
// TODO
if (index >= 10) return
if (!data) {
index++
this.getCollapseHeight(false, index)
return
}
// #ifdef APP-NVUE
this.height = data.height + 1
// #endif
// #ifndef APP-NVUE
this.height = data.height
// #endif
this.isheight = true
if (type) return
this.onClick(this.isOpen, 'init')
})
.exec()
},
getNvueHwight(type) {
const result = dom.getComponentRect(this.$refs['collapse--hook'], option => {
if (option && option.result && option.size) {
// #ifdef APP-NVUE
this.height = option.size.height + 1
// #endif
// #ifndef APP-NVUE
this.height = option.size.height
// #endif
this.isheight = true
if (type) return
this.onClick(this.open, 'init')
}
})
},
/**
* 获取父元素实例
*/
getCollapse(name = 'uniCollapse') {
let parent = this.$parent;
let parentName = parent.$options.name;
while (parentName !== name) {
parent = parent.$parent;
if (!parent) return false;
parentName = parent.$options.name;
}
return parent;
}
}
}
</script>
<style lang="scss">
.uni-collapse-item {
/* #ifndef APP-NVUE */
box-sizing: border-box;
/* #endif */
&__title {
/* #ifndef APP-NVUE */
display: flex;
width: 100%;
box-sizing: border-box;
/* #endif */
flex-direction: row;
align-items: center;
transition: border-bottom-color .3s;
// transition-property: border-bottom-color;
// transition-duration: 5s;
&-wrap {
width: 100%;
flex: 1;
}
&-box {
padding: 0 15px;
/* #ifndef APP-NVUE */
display: flex;
width: 100%;
box-sizing: border-box;
/* #endif */
flex-direction: row;
justify-content: space-between;
align-items: center;
height: 48px;
line-height: 48px;
background-color: #fff;
color: #303133;
font-size: 13px;
font-weight: 500;
/* #ifdef H5 */
cursor: pointer;
outline: none;
/* #endif */
&.is-disabled {
.uni-collapse-item__title-text {
color: #999;
}
}
}
&.uni-collapse-item-border {
border-bottom: 1px solid #ebeef5;
}
&.is-open {
border-bottom-color: transparent;
}
&-img {
height: 22px;
width: 22px;
margin-right: 10px;
}
&-text {
flex: 1;
font-size: 14px;
/* #ifndef APP-NVUE */
white-space: nowrap;
color: inherit;
/* #endif */
/* #ifdef APP-NVUE */
lines: 1;
/* #endif */
overflow: hidden;
text-overflow: ellipsis;
}
&-arrow {
/* #ifndef APP-NVUE */
display: flex;
box-sizing: border-box;
/* #endif */
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
margin-right: 10px;
transform: rotate(0deg);
&-active {
transform: rotate(-180deg);
}
}
}
&__wrap {
/* #ifndef APP-NVUE */
will-change: height;
box-sizing: border-box;
/* #endif */
background-color: #fff;
overflow: hidden;
position: relative;
height: 0;
&.is--transition {
// transition: all 0.3s;
transition-property: height, border-bottom-width;
transition-duration: 0.3s;
/* #ifndef APP-NVUE */
will-change: height;
/* #endif */
}
&-content {
position: absolute;
font-size: 13px;
color: #303133;
// transition: height 0.3s;
border-bottom-color: transparent;
border-bottom-style: solid;
border-bottom-width: 0;
&.uni-collapse-item--border {
border-bottom-width: 1px;
border-bottom-color: red;
border-bottom-color: #ebeef5;
}
&.open {
position: relative;
}
}
}
&--animation {
transition-property: transform;
transition-duration: 0.3s;
transition-timing-function: ease;
}
}
</style>

147
uni_modules/uni-collapse/components/uni-collapse/uni-collapse.vue

@ -0,0 +1,147 @@
<template>
<view class="uni-collapse">
<slot />
</view>
</template>
<script>
/**
* Collapse 折叠面板
* @description 展示可以折叠 / 展开的内容区域
* @tutorial https://ext.dcloud.net.cn/plugin?id=23
* @property {String|Array} value 当前激活面板改变时触发(如果是手风琴模式参数类型为string否则为array)
* @property {Boolean} accordion = [true|false] 是否开启手风琴效果是否开启手风琴效果
* @event {Function} change 切换面板时触发如果是手风琴模式返回类型为string否则为array
*/
export default {
name: 'uniCollapse',
emits:['change','activeItem','input','update:modelValue'],
props: {
value: {
type: [String, Array],
default: ''
},
modelValue: {
type: [String, Array],
default: ''
},
accordion: {
//
type: [Boolean, String],
default: false
},
},
data() {
return {}
},
computed: {
// TODO vue2 vue3
dataValue() {
let value = (typeof this.value === 'string' && this.value === '') ||
(Array.isArray(this.value) && this.value.length === 0)
let modelValue = (typeof this.modelValue === 'string' && this.modelValue === '') ||
(Array.isArray(this.modelValue) && this.modelValue.length === 0)
if (value) {
return this.modelValue
}
if (modelValue) {
return this.value
}
return this.value
}
},
watch: {
dataValue(val) {
this.setOpen(val)
}
},
created() {
this.childrens = []
this.names = []
},
mounted() {
this.$nextTick(()=>{
this.setOpen(this.dataValue)
})
},
methods: {
setOpen(val) {
let str = typeof val === 'string'
let arr = Array.isArray(val)
this.childrens.forEach((vm, index) => {
if (str) {
if (val === vm.nameSync) {
if (!this.accordion) {
console.warn('accordion 属性为 false ,v-model 类型应该为 array')
return
}
vm.isOpen = true
}
}
if (arr) {
val.forEach(v => {
if (v === vm.nameSync) {
if (this.accordion) {
console.warn('accordion 属性为 true ,v-model 类型应该为 string')
return
}
vm.isOpen = true
}
})
}
})
this.emit(val)
},
setAccordion(self) {
if (!this.accordion) return
this.childrens.forEach((vm, index) => {
if (self !== vm) {
vm.isOpen = false
}
})
},
resize() {
this.childrens.forEach((vm, index) => {
// #ifndef APP-NVUE
vm.getCollapseHeight()
// #endif
// #ifdef APP-NVUE
vm.getNvueHwight()
// #endif
})
},
onChange(isOpen, self) {
let activeItem = []
if (this.accordion) {
activeItem = isOpen ? self.nameSync : ''
} else {
this.childrens.forEach((vm, index) => {
if (vm.isOpen) {
activeItem.push(vm.nameSync)
}
})
}
this.$emit('change', activeItem)
this.emit(activeItem)
},
emit(val){
this.$emit('input', val)
this.$emit('update:modelValue', val)
}
}
}
</script>
<style lang="scss" >
.uni-collapse {
/* #ifndef APP-NVUE */
width: 100%;
display: flex;
/* #endif */
/* #ifdef APP-NVUE */
flex: 1;
/* #endif */
flex-direction: column;
background-color: #fff;
}
</style>

89
uni_modules/uni-collapse/package.json

@ -0,0 +1,89 @@
{
"id": "uni-collapse",
"displayName": "uni-collapse 折叠面板",
"version": "1.4.3",
"description": "Collapse 组件,可以折叠 / 展开的内容区域。",
"keywords": [
"uni-ui",
"折叠",
"折叠面板",
"手风琴"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"category": [
"前端组件",
"通用组件"
],
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
},
"uni_modules": {
"dependencies": [
"uni-scss",
"uni-icons"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

12
uni_modules/uni-collapse/readme.md

@ -0,0 +1,12 @@
## Collapse 折叠面板
> **组件名:uni-collapse**
> 代码块: `uCollapse`
> 关联组件:`uni-collapse-item`、`uni-icons`。
折叠面板用来折叠/显示过长的内容或者是列表。通常是在多内容分类项使用,折叠不重要的内容,显示重要内容。点击可以展开折叠部分。
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-collapse)
#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839

15
uni_modules/uni-combox/changelog.md

@ -0,0 +1,15 @@
## 1.0.1(2021-11-23)
- 优化 label、label-width 属性
## 1.0.0(2021-11-19)
- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-combox](https://uniapp.dcloud.io/component/uniui/uni-combox)
## 0.1.0(2021-07-30)
- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 0.0.6(2021-05-12)
- 新增 组件示例地址
## 0.0.5(2021-04-21)
- 优化 添加依赖 uni-icons, 导入后自动下载依赖
## 0.0.4(2021-02-05)
- 优化 组件引用关系,通过uni_modules引用组件
## 0.0.3(2021-02-04)
- 调整为uni_modules目录规范

275
uni_modules/uni-combox/components/uni-combox/uni-combox.vue

@ -0,0 +1,275 @@
<template>
<view class="uni-combox" :class="border ? '' : 'uni-combox__no-border'">
<view v-if="label" class="uni-combox__label" :style="labelStyle">
<text>{{label}}</text>
</view>
<view class="uni-combox__input-box">
<input class="uni-combox__input" type="text" :placeholder="placeholder"
placeholder-class="uni-combox__input-plac" v-model="inputVal" @input="onInput" @focus="onFocus"
@blur="onBlur" />
<uni-icons :type="showSelector? 'top' : 'bottom'" size="14" color="#999" @click="toggleSelector">
</uni-icons>
</view>
<view class="uni-combox__selector" v-if="showSelector">
<view class="uni-popper__arrow"></view>
<scroll-view scroll-y="true" class="uni-combox__selector-scroll">
<view class="uni-combox__selector-empty" v-if="filterCandidatesLength === 0">
<text>{{emptyTips}}</text>
</view>
<view class="uni-combox__selector-item" v-for="(item,index) in filterCandidates" :key="index"
@click="onSelectorClick(index)">
<text>{{item}}</text>
</view>
</scroll-view>
</view>
</view>
</template>
<script>
/**
* Combox 组合输入框
* @description 组合输入框一般用于既可以输入也可以选择的场景
* @tutorial https://ext.dcloud.net.cn/plugin?id=1261
* @property {String} label 左侧文字
* @property {String} labelWidth 左侧内容宽度
* @property {String} placeholder 输入框占位符
* @property {Array} candidates 候选项列表
* @property {String} emptyTips 筛选结果为空时显示的文字
* @property {String} value 组合框的值
*/
export default {
name: 'uniCombox',
emits: ['input', 'update:modelValue'],
props: {
border: {
type: Boolean,
default: true
},
label: {
type: String,
default: ''
},
labelWidth: {
type: String,
default: 'auto'
},
placeholder: {
type: String,
default: ''
},
candidates: {
type: Array,
default () {
return []
}
},
emptyTips: {
type: String,
default: '无匹配项'
},
// #ifndef VUE3
value: {
type: [String, Number],
default: ''
},
// #endif
// #ifdef VUE3
modelValue: {
type: [String, Number],
default: ''
},
// #endif
},
data() {
return {
showSelector: false,
inputVal: ''
}
},
computed: {
labelStyle() {
if (this.labelWidth === 'auto') {
return ""
}
return `width: ${this.labelWidth}`
},
filterCandidates() {
return this.candidates.filter((item) => {
return item.toString().indexOf(this.inputVal) > -1
})
},
filterCandidatesLength() {
return this.filterCandidates.length
}
},
watch: {
// #ifndef VUE3
value: {
handler(newVal) {
this.inputVal = newVal
},
immediate: true
},
// #endif
// #ifdef VUE3
modelValue: {
handler(newVal) {
this.inputVal = newVal
},
immediate: true
},
// #endif
},
methods: {
toggleSelector() {
this.showSelector = !this.showSelector
},
onFocus() {
this.showSelector = true
},
onBlur() {
setTimeout(() => {
this.showSelector = false
}, 153)
},
onSelectorClick(index) {
this.inputVal = this.filterCandidates[index]
this.showSelector = false
this.$emit('input', this.inputVal)
this.$emit('update:modelValue', this.inputVal)
},
onInput() {
setTimeout(() => {
this.$emit('input', this.inputVal)
this.$emit('update:modelValue', this.inputVal)
})
}
}
}
</script>
<style lang="scss" scoped>
.uni-combox {
font-size: 14px;
border: 1px solid #DCDFE6;
border-radius: 4px;
padding: 6px 10px;
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
// height: 40px;
flex-direction: row;
align-items: center;
// border-bottom: solid 1px #DDDDDD;
}
.uni-combox__label {
font-size: 16px;
line-height: 22px;
padding-right: 10px;
color: #999999;
}
.uni-combox__input-box {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex: 1;
flex-direction: row;
align-items: center;
}
.uni-combox__input {
flex: 1;
font-size: 14px;
height: 22px;
line-height: 22px;
}
.uni-combox__input-plac {
font-size: 14px;
color: #999;
}
.uni-combox__selector {
/* #ifndef APP-NVUE */
box-sizing: border-box;
/* #endif */
position: absolute;
top: calc(100% + 12px);
left: 0;
width: 100%;
background-color: #FFFFFF;
border: 1px solid #EBEEF5;
border-radius: 6px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
z-index: 2;
padding: 4px 0;
}
.uni-combox__selector-scroll {
/* #ifndef APP-NVUE */
max-height: 200px;
box-sizing: border-box;
/* #endif */
}
.uni-combox__selector-empty,
.uni-combox__selector-item {
/* #ifndef APP-NVUE */
display: flex;
cursor: pointer;
/* #endif */
line-height: 36px;
font-size: 14px;
text-align: center;
// border-bottom: solid 1px #DDDDDD;
padding: 0px 10px;
}
.uni-combox__selector-item:hover {
background-color: #f9f9f9;
}
.uni-combox__selector-empty:last-child,
.uni-combox__selector-item:last-child {
/* #ifndef APP-NVUE */
border-bottom: none;
/* #endif */
}
// picker
.uni-popper__arrow,
.uni-popper__arrow::after {
position: absolute;
display: block;
width: 0;
height: 0;
border-color: transparent;
border-style: solid;
border-width: 6px;
}
.uni-popper__arrow {
filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03));
top: -6px;
left: 10%;
margin-right: 3px;
border-top-width: 0;
border-bottom-color: #EBEEF5;
}
.uni-popper__arrow::after {
content: " ";
top: 1px;
margin-left: -6px;
border-top-width: 0;
border-bottom-color: #fff;
}
.uni-combox__no-border {
border: none;
}
</style>

90
uni_modules/uni-combox/package.json

@ -0,0 +1,90 @@
{
"id": "uni-combox",
"displayName": "uni-combox 组合框",
"version": "1.0.1",
"description": "可以选择也可以输入的表单项 ",
"keywords": [
"uni-ui",
"uniui",
"combox",
"组合框",
"select"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"category": [
"前端组件",
"通用组件"
],
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
},
"uni_modules": {
"dependencies": [
"uni-scss",
"uni-icons"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "n"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

11
uni_modules/uni-combox/readme.md

@ -0,0 +1,11 @@
## Combox 组合框
> **组件名:uni-combox**
> 代码块: `uCombox`
组合框组件。
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-combox)
#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839

24
uni_modules/uni-countdown/changelog.md

@ -0,0 +1,24 @@
## 1.2.2(2022-01-19)
- 修复 在微信小程序中样式不生效的bug
## 1.2.1(2022-01-18)
- 新增 update 方法 ,在动态更新时间后,刷新组件
## 1.2.0(2021-11-19)
- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-countdown](https://uniapp.dcloud.io/component/uniui/uni-countdown)
## 1.1.3(2021-10-18)
- 重构
- 新增 font-size 支持自定义字体大小
## 1.1.2(2021-08-24)
- 新增 支持国际化
## 1.1.1(2021-07-30)
- 优化 vue3下小程序事件警告的问题
## 1.1.0(2021-07-30)
- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 1.0.5(2021-06-18)
- 修复 uni-countdown 重复赋值跳两秒的 bug
## 1.0.4(2021-05-12)
- 新增 组件示例地址
## 1.0.3(2021-05-08)
- 修复 uni-countdown 不能控制倒计时的 bug
## 1.0.2(2021-02-04)
- 调整为uni_modules目录规范

6
uni_modules/uni-countdown/components/uni-countdown/i18n/en.json

@ -0,0 +1,6 @@
{
"uni-countdown.day": "day",
"uni-countdown.h": "h",
"uni-countdown.m": "m",
"uni-countdown.s": "s"
}

8
uni_modules/uni-countdown/components/uni-countdown/i18n/index.js

@ -0,0 +1,8 @@
import en from './en.json'
import zhHans from './zh-Hans.json'
import zhHant from './zh-Hant.json'
export default {
en,
'zh-Hans': zhHans,
'zh-Hant': zhHant
}

6
uni_modules/uni-countdown/components/uni-countdown/i18n/zh-Hans.json

@ -0,0 +1,6 @@
{
"uni-countdown.day": "天",
"uni-countdown.h": "时",
"uni-countdown.m": "分",
"uni-countdown.s": "秒"
}

6
uni_modules/uni-countdown/components/uni-countdown/i18n/zh-Hant.json

@ -0,0 +1,6 @@
{
"uni-countdown.day": "天",
"uni-countdown.h": "時",
"uni-countdown.m": "分",
"uni-countdown.s": "秒"
}

271
uni_modules/uni-countdown/components/uni-countdown/uni-countdown.vue

@ -0,0 +1,271 @@
<template>
<view class="uni-countdown">
<text v-if="showDay" :style="[timeStyle]" class="uni-countdown__number">{{ d }}</text>
<text v-if="showDay" :style="[splitorStyle]" class="uni-countdown__splitor">{{dayText}}</text>
<text :style="[timeStyle]" class="uni-countdown__number">{{ h }}</text>
<text :style="[splitorStyle]" class="uni-countdown__splitor">{{ showColon ? ':' : hourText }}</text>
<text :style="[timeStyle]" class="uni-countdown__number">{{ i }}</text>
<text :style="[splitorStyle]" class="uni-countdown__splitor">{{ showColon ? ':' : minuteText }}</text>
<text :style="[timeStyle]" class="uni-countdown__number">{{ s }}</text>
<text v-if="!showColon" :style="[splitorStyle]" class="uni-countdown__splitor">{{secondText}}</text>
</view>
</template>
<script>
import {
initVueI18n
} from '@dcloudio/uni-i18n'
import messages from './i18n/index.js'
const {
t
} = initVueI18n(messages)
/**
* Countdown 倒计时
* @description 倒计时组件
* @tutorial https://ext.dcloud.net.cn/plugin?id=25
* @property {String} backgroundColor 背景色
* @property {String} color 文字颜色
* @property {Number} day 天数
* @property {Number} hour 小时
* @property {Number} minute 分钟
* @property {Number} second
* @property {Number} timestamp 时间戳
* @property {Boolean} showDay = [true|false] 是否显示天数
* @property {Boolean} show-colon = [true|false] 是否以冒号为分隔符
* @property {String} splitorColor 分割符号颜色
* @event {Function} timeup 倒计时时间到触发事件
* @example <uni-countdown :day="1" :hour="1" :minute="12" :second="40"></uni-countdown>
*/
export default {
name: 'UniCountdown',
emits: ['timeup'],
props: {
showDay: {
type: Boolean,
default: true
},
showColon: {
type: Boolean,
default: true
},
start: {
type: Boolean,
default: true
},
backgroundColor: {
type: String,
default: ''
},
color: {
type: String,
default: '#333'
},
fontSize: {
type: Number,
default: 14
},
splitorColor: {
type: String,
default: '#333'
},
day: {
type: Number,
default: 0
},
hour: {
type: Number,
default: 0
},
minute: {
type: Number,
default: 0
},
second: {
type: Number,
default: 0
},
timestamp: {
type: Number,
default: 0
}
},
data() {
return {
timer: null,
syncFlag: false,
d: '00',
h: '00',
i: '00',
s: '00',
leftTime: 0,
seconds: 0
}
},
computed: {
dayText() {
return t("uni-countdown.day")
},
hourText(val) {
return t("uni-countdown.h")
},
minuteText(val) {
return t("uni-countdown.m")
},
secondText(val) {
return t("uni-countdown.s")
},
timeStyle() {
const {
color,
backgroundColor,
fontSize
} = this
return {
color,
backgroundColor,
fontSize: `${fontSize}px`,
width: `${fontSize * 22 / 14}px`, // 14px
lineHeight: `${fontSize * 20 / 14}px`,
borderRadius: `${fontSize * 3 / 14}px`,
}
},
splitorStyle() {
const { splitorColor, fontSize, backgroundColor } = this
return {
color: splitorColor,
fontSize: `${fontSize * 12 / 14}px`,
margin: backgroundColor ? `${fontSize * 4 / 14}px` : ''
}
}
},
watch: {
day(val) {
this.changeFlag()
},
hour(val) {
this.changeFlag()
},
minute(val) {
this.changeFlag()
},
second(val) {
this.changeFlag()
},
start: {
immediate: true,
handler(newVal, oldVal) {
if (newVal) {
this.startData();
} else {
if (!oldVal) return
clearInterval(this.timer)
}
}
}
},
created: function(e) {
this.seconds = this.toSeconds(this.timestamp, this.day, this.hour, this.minute, this.second)
this.countDown()
},
// #ifndef VUE3
destroyed() {
clearInterval(this.timer)
},
// #endif
// #ifdef VUE3
unmounted() {
clearInterval(this.timer)
},
// #endif
methods: {
toSeconds(timestamp, day, hours, minutes, seconds) {
if (timestamp) {
return timestamp - parseInt(new Date().getTime() / 1000, 10)
}
return day * 60 * 60 * 24 + hours * 60 * 60 + minutes * 60 + seconds
},
timeUp() {
clearInterval(this.timer)
this.$emit('timeup')
},
countDown() {
let seconds = this.seconds
let [day, hour, minute, second] = [0, 0, 0, 0]
if (seconds > 0) {
day = Math.floor(seconds / (60 * 60 * 24))
hour = Math.floor(seconds / (60 * 60)) - (day * 24)
minute = Math.floor(seconds / 60) - (day * 24 * 60) - (hour * 60)
second = Math.floor(seconds) - (day * 24 * 60 * 60) - (hour * 60 * 60) - (minute * 60)
} else {
this.timeUp()
}
if (day < 10) {
day = '0' + day
}
if (hour < 10) {
hour = '0' + hour
}
if (minute < 10) {
minute = '0' + minute
}
if (second < 10) {
second = '0' + second
}
this.d = day
this.h = hour
this.i = minute
this.s = second
},
startData() {
this.seconds = this.toSeconds(this.timestamp, this.day, this.hour, this.minute, this.second)
if (this.seconds <= 0) {
this.seconds = this.toSeconds(0, 0, 0, 0, 0)
this.countDown()
return
}
clearInterval(this.timer)
this.countDown()
this.timer = setInterval(() => {
this.seconds--
if (this.seconds < 0) {
this.timeUp()
return
}
this.countDown()
}, 1000)
},
update(){
this.startData();
},
changeFlag() {
if (!this.syncFlag) {
this.seconds = this.toSeconds(this.timestamp, this.day, this.hour, this.minute, this.second)
this.startData();
this.syncFlag = true;
}
}
}
}
</script>
<style lang="scss" scoped>
$font-size: 14px;
.uni-countdown {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
&__splitor {
margin: 0 2px;
font-size: $font-size;
color: #333;
}
&__number {
border-radius: 3px;
text-align: center;
font-size: $font-size;
}
}
</style>

86
uni_modules/uni-countdown/package.json

@ -0,0 +1,86 @@
{
"id": "uni-countdown",
"displayName": "uni-countdown 倒计时",
"version": "1.2.2",
"description": "CountDown 倒计时组件",
"keywords": [
"uni-ui",
"uniui",
"countdown",
"倒计时"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"category": [
"前端组件",
"通用组件"
],
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
},
"uni_modules": {
"dependencies": ["uni-scss"],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

10
uni_modules/uni-countdown/readme.md

@ -0,0 +1,10 @@
## CountDown 倒计时
> **组件名:uni-countdown**
> 代码块: `uCountDown`
倒计时组件。
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-countdown)
#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839

45
uni_modules/uni-data-checkbox/changelog.md

@ -0,0 +1,45 @@
## 1.0.3(2022-09-16)
- 可以使用 uni-scss 控制主题色
## 1.0.2(2022-06-30)
- 优化 在 uni-forms 中的依赖注入方式
## 1.0.1(2022-02-07)
- 修复 multiple 为 true 时,v-model 的值为 null 报错的 bug
## 1.0.0(2021-11-19)
- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-data-checkbox](https://uniapp.dcloud.io/component/uniui/uni-data-checkbox)
## 0.2.5(2021-08-23)
- 修复 在uni-forms中 modelValue 中不存在当前字段,当前字段必填写也不参与校验的问题
## 0.2.4(2021-08-17)
- 修复 单选 list 模式下 ,icon 为 left 时,选中图标不显示的问题
## 0.2.3(2021-08-11)
- 修复 在 uni-forms 中重置表单,错误信息无法清除的问题
## 0.2.2(2021-07-30)
- 优化 在uni-forms组件,与label不对齐的问题
## 0.2.1(2021-07-27)
- 修复 单选默认值为0不能选中的Bug
## 0.2.0(2021-07-13)
- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 0.1.11(2021-07-06)
- 优化 删除无用日志
## 0.1.10(2021-07-05)
- 修复 由 0.1.9 引起的非 nvue 端图标不显示的问题
## 0.1.9(2021-07-05)
- 修复 nvue 黑框样式问题
## 0.1.8(2021-06-28)
- 修复 selectedTextColor 属性不生效的Bug
## 0.1.7(2021-06-02)
- 新增 map 属性,可以方便映射text/value属性
## 0.1.6(2021-05-26)
- 修复 不关联服务空间的情况下组件报错的Bug
## 0.1.5(2021-05-12)
- 新增 组件示例地址
## 0.1.4(2021-04-09)
- 修复 nvue 下无法选中的问题
## 0.1.3(2021-03-22)
- 新增 disabled属性
## 0.1.2(2021-02-24)
- 优化 默认颜色显示
## 0.1.1(2021-02-24)
- 新增 支持nvue
## 0.1.0(2021-02-18)
- “暂无数据”显示居中

821
uni_modules/uni-data-checkbox/components/uni-data-checkbox/uni-data-checkbox.vue

@ -0,0 +1,821 @@
<template>
<view class="uni-data-checklist" :style="{'margin-top':isTop+'px'}">
<template v-if="!isLocal">
<view class="uni-data-loading">
<uni-load-more v-if="!mixinDatacomErrorMessage" status="loading" iconType="snow" :iconSize="18" :content-text="contentText"></uni-load-more>
<text v-else>{{mixinDatacomErrorMessage}}</text>
</view>
</template>
<template v-else>
<checkbox-group v-if="multiple" class="checklist-group" :class="{'is-list':mode==='list' || wrap}" @change="chagne">
<label class="checklist-box" :class="['is--'+mode,item.selected?'is-checked':'',(disabled || !!item.disabled)?'is-disable':'',index!==0&&mode==='list'?'is-list-border':'']"
:style="item.styleBackgroud" v-for="(item,index) in dataList" :key="index">
<checkbox class="hidden" hidden :disabled="disabled || !!item.disabled" :value="item[map.value]+''" :checked="item.selected" />
<view v-if="(mode !=='tag' && mode !== 'list') || ( mode === 'list' && icon === 'left')" class="checkbox__inner" :style="item.styleIcon">
<view class="checkbox__inner-icon"></view>
</view>
<view class="checklist-content" :class="{'list-content':mode === 'list' && icon ==='left'}">
<text class="checklist-text" :style="item.styleIconText">{{item[map.text]}}</text>
<view v-if="mode === 'list' && icon === 'right'" class="checkobx__list" :style="item.styleBackgroud"></view>
</view>
</label>
</checkbox-group>
<radio-group v-else class="checklist-group" :class="{'is-list':mode==='list','is-wrap':wrap}" @change="chagne">
<!-- -->
<label class="checklist-box" :class="['is--'+mode,item.selected?'is-checked':'',(disabled || !!item.disabled)?'is-disable':'',index!==0&&mode==='list'?'is-list-border':'']"
:style="item.styleBackgroud" v-for="(item,index) in dataList" :key="index">
<radio class="hidden" hidden :disabled="disabled || item.disabled" :value="item[map.value]+''" :checked="item.selected" />
<view v-if="(mode !=='tag' && mode !== 'list') || ( mode === 'list' && icon === 'left')" class="radio__inner"
:style="item.styleBackgroud">
<view class="radio__inner-icon" :style="item.styleIcon"></view>
</view>
<view class="checklist-content" :class="{'list-content':mode === 'list' && icon ==='left'}">
<text class="checklist-text" :style="item.styleIconText">{{item[map.text]}}</text>
<view v-if="mode === 'list' && icon === 'right'" :style="item.styleRightIcon" class="checkobx__list"></view>
</view>
</label>
</radio-group>
</template>
</view>
</template>
<script>
/**
* DataChecklist 数据选择器
* @description 通过数据渲染 checkbox radio
* @tutorial https://ext.dcloud.net.cn/plugin?id=xxx
* @property {String} mode = [default| list | button | tag] 显示模式
* @value default 默认横排模式
* @value list 列表模式
* @value button 按钮模式
* @value tag 标签模式
* @property {Boolean} multiple = [true|false] 是否多选
* @property {Array|String|Number} value 默认值
* @property {Array} localdata 本地数据 格式 [{text:'',value:''}]
* @property {Number|String} min 最小选择个数 multiple为true时生效
* @property {Number|String} max 最大选择个数 multiple为true时生效
* @property {Boolean} wrap 是否换行显示
* @property {String} icon = [left|right] list 列表模式下icon显示位置
* @property {Boolean} selectedColor 选中颜色
* @property {Boolean} emptyText 没有数据时显示的文字 本地数据无效
* @property {Boolean} selectedTextColor 选中文本颜色如不填写则自动显示
* @property {Object} map 字段映射 默认 map={text:'text',value:'value'}
* @value left 左侧显示
* @value right 右侧显示
* @event {Function} change 选中发生变化触发
*/
export default {
name: 'uniDataChecklist',
mixins: [uniCloud.mixinDatacom || {}],
emits:['input','update:modelValue','change'],
props: {
mode: {
type: String,
default: 'default'
},
multiple: {
type: Boolean,
default: false
},
value: {
type: [Array, String, Number],
default () {
return ''
}
},
// TODO vue3
modelValue: {
type: [Array, String, Number],
default() {
return '';
}
},
localdata: {
type: Array,
default () {
return []
}
},
min: {
type: [Number, String],
default: ''
},
max: {
type: [Number, String],
default: ''
},
wrap: {
type: Boolean,
default: false
},
icon: {
type: String,
default: 'left'
},
selectedColor: {
type: String,
default: ''
},
selectedTextColor: {
type: String,
default: ''
},
emptyText:{
type: String,
default: '暂无数据'
},
disabled:{
type: Boolean,
default: false
},
map:{
type: Object,
default(){
return {
text:'text',
value:'value'
}
}
}
},
watch: {
localdata: {
handler(newVal) {
this.range = newVal
this.dataList = this.getDataList(this.getSelectedValue(newVal))
},
deep: true
},
mixinDatacomResData(newVal) {
this.range = newVal
this.dataList = this.getDataList(this.getSelectedValue(newVal))
},
value(newVal) {
this.dataList = this.getDataList(newVal)
// fix by mehaotian is_reset uni-forms
// if(!this.is_reset){
// this.is_reset = false
// this.formItem && this.formItem.setValue(newVal)
// }
},
modelValue(newVal) {
this.dataList = this.getDataList(newVal);
// if(!this.is_reset){
// this.is_reset = false
// this.formItem && this.formItem.setValue(newVal)
// }
}
},
data() {
return {
dataList: [],
range: [],
contentText: {
contentdown: '查看更多',
contentrefresh: '加载中',
contentnomore: '没有更多'
},
isLocal:true,
styles: {
selectedColor: '#2979ff',
selectedTextColor: '#666',
},
isTop:0
};
},
computed:{
dataValue(){
if(this.value === '')return this.modelValue
if(this.modelValue === '') return this.value
return this.value
}
},
created() {
// this.form = this.getForm('uniForms')
// this.formItem = this.getForm('uniFormsItem')
// this.formItem && this.formItem.setValue(this.value)
// if (this.formItem) {
// this.isTop = 6
// if (this.formItem.name) {
// // name,formData
// if(!this.is_reset){
// this.is_reset = false
// this.formItem.setValue(this.dataValue)
// }
// this.rename = this.formItem.name
// this.form.inputChildrens.push(this)
// }
// }
if (this.localdata && this.localdata.length !== 0) {
this.isLocal = true
this.range = this.localdata
this.dataList = this.getDataList(this.getSelectedValue(this.range))
} else {
if (this.collection) {
this.isLocal = false
this.loadData()
}
}
},
methods: {
loadData() {
this.mixinDatacomGet().then(res=>{
this.mixinDatacomResData = res.result.data
if(this.mixinDatacomResData.length === 0){
this.isLocal = false
this.mixinDatacomErrorMessage = this.emptyText
}else{
this.isLocal = true
}
}).catch(err=>{
this.mixinDatacomErrorMessage = err.message
})
},
/**
* 获取父元素实例
*/
getForm(name = 'uniForms') {
let parent = this.$parent;
let parentName = parent.$options.name;
while (parentName !== name) {
parent = parent.$parent;
if (!parent) return false
parentName = parent.$options.name;
}
return parent;
},
chagne(e) {
const values = e.detail.value
let detail = {
value: [],
data: []
}
if (this.multiple) {
this.range.forEach(item => {
if (values.includes(item[this.map.value] + '')) {
detail.value.push(item[this.map.value])
detail.data.push(item)
}
})
} else {
const range = this.range.find(item => (item[this.map.value] + '') === values)
if (range) {
detail = {
value: range[this.map.value],
data: range
}
}
}
// this.formItem && this.formItem.setValue(detail.value)
// TODO vue2
this.$emit('input', detail.value);
// // TOTO vue3
this.$emit('update:modelValue', detail.value);
this.$emit('change', {
detail
})
if (this.multiple) {
// v-model
// if (this.value.length === 0) {
this.dataList = this.getDataList(detail.value, true)
// }
} else {
this.dataList = this.getDataList(detail.value)
}
},
/**
* 获取渲染的新数组
* @param {Object} value 选中内容
*/
getDataList(value) {
//
let dataList = JSON.parse(JSON.stringify(this.range))
let list = []
if (this.multiple) {
if (!Array.isArray(value)) {
value = []
}
}
dataList.forEach((item, index) => {
item.disabled = item.disable || item.disabled || false
if (this.multiple) {
if (value.length > 0) {
let have = value.find(val => val === item[this.map.value])
item.selected = have !== undefined
} else {
item.selected = false
}
} else {
item.selected = value === item[this.map.value]
}
list.push(item)
})
return this.setRange(list)
},
/**
* 处理最大最小值
* @param {Object} list
*/
setRange(list) {
let selectList = list.filter(item => item.selected)
let min = Number(this.min) || 0
let max = Number(this.max) || ''
list.forEach((item, index) => {
if (this.multiple) {
if (selectList.length <= min) {
let have = selectList.find(val => val[this.map.value] === item[this.map.value])
if (have !== undefined) {
item.disabled = true
}
}
if (selectList.length >= max && max !== '') {
let have = selectList.find(val => val[this.map.value] === item[this.map.value])
if (have === undefined) {
item.disabled = true
}
}
}
this.setStyles(item, index)
list[index] = item
})
return list
},
/**
* 设置 class
* @param {Object} item
* @param {Object} index
*/
setStyles(item, index) {
//
item.styleBackgroud = this.setStyleBackgroud(item)
item.styleIcon = this.setStyleIcon(item)
item.styleIconText = this.setStyleIconText(item)
item.styleRightIcon = this.setStyleRightIcon(item)
},
/**
* 获取选中值
* @param {Object} range
*/
getSelectedValue(range) {
if (!this.multiple) return this.dataValue
let selectedArr = []
range.forEach((item) => {
if (item.selected) {
selectedArr.push(item[this.map.value])
}
})
return this.dataValue.length > 0 ? this.dataValue : selectedArr
},
/**
* 设置背景样式
*/
setStyleBackgroud(item) {
let styles = {}
let selectedColor = this.selectedColor?this.selectedColor:'#2979ff'
if (this.selectedColor) {
if (this.mode !== 'list') {
styles['border-color'] = item.selected?selectedColor:'#DCDFE6'
}
if (this.mode === 'tag') {
styles['background-color'] = item.selected? selectedColor:'#f5f5f5'
}
}
let classles = ''
for (let i in styles) {
classles += `${i}:${styles[i]};`
}
return classles
},
setStyleIcon(item) {
let styles = {}
let classles = ''
if (this.selectedColor) {
let selectedColor = this.selectedColor?this.selectedColor:'#2979ff'
styles['background-color'] = item.selected?selectedColor:'#fff'
styles['border-color'] = item.selected?selectedColor:'#DCDFE6'
if(!item.selected && item.disabled){
styles['background-color'] = '#F2F6FC'
styles['border-color'] = item.selected?selectedColor:'#DCDFE6'
}
}
for (let i in styles) {
classles += `${i}:${styles[i]};`
}
return classles
},
setStyleIconText(item) {
let styles = {}
let classles = ''
if (this.selectedColor) {
let selectedColor = this.selectedColor?this.selectedColor:'#2979ff'
if (this.mode === 'tag') {
styles.color = item.selected?(this.selectedTextColor?this.selectedTextColor:'#fff'):'#666'
} else {
styles.color = item.selected?(this.selectedTextColor?this.selectedTextColor:selectedColor):'#666'
}
if(!item.selected && item.disabled){
styles.color = '#999'
}
}
for (let i in styles) {
classles += `${i}:${styles[i]};`
}
return classles
},
setStyleRightIcon(item) {
let styles = {}
let classles = ''
if (this.mode === 'list') {
styles['border-color'] = item.selected?this.styles.selectedColor:'#DCDFE6'
}
for (let i in styles) {
classles += `${i}:${styles[i]};`
}
return classles
}
}
}
</script>
<style lang="scss">
$uni-primary: #2979ff !default;
$border-color: #DCDFE6;
$disable:0.4;
@mixin flex {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
}
.uni-data-loading {
@include flex;
flex-direction: row;
justify-content: center;
align-items: center;
height: 36px;
padding-left: 10px;
color: #999;
}
.uni-data-checklist {
position: relative;
z-index: 0;
flex: 1;
//
.checklist-group {
@include flex;
flex-direction: row;
flex-wrap: wrap;
&.is-list {
flex-direction: column;
}
.checklist-box {
@include flex;
flex-direction: row;
align-items: center;
position: relative;
margin: 5px 0;
margin-right: 25px;
.hidden {
position: absolute;
opacity: 0;
}
//
.checklist-content {
@include flex;
flex: 1;
flex-direction: row;
align-items: center;
justify-content: space-between;
.checklist-text {
font-size: 14px;
color: #666;
margin-left: 5px;
line-height: 14px;
}
.checkobx__list {
border-right-width: 1px;
border-right-color: #007aff;
border-right-style: solid;
border-bottom-width:1px;
border-bottom-color: #007aff;
border-bottom-style: solid;
height: 12px;
width: 6px;
left: -5px;
transform-origin: center;
transform: rotate(45deg);
opacity: 0;
}
}
//
.checkbox__inner {
/* #ifndef APP-NVUE */
flex-shrink: 0;
box-sizing: border-box;
/* #endif */
position: relative;
width: 16px;
height: 16px;
border: 1px solid $border-color;
border-radius: 4px;
background-color: #fff;
z-index: 1;
.checkbox__inner-icon {
position: absolute;
/* #ifdef APP-NVUE */
top: 2px;
/* #endif */
/* #ifndef APP-NVUE */
top: 1px;
/* #endif */
left: 5px;
height: 8px;
width: 4px;
border-right-width: 1px;
border-right-color: #fff;
border-right-style: solid;
border-bottom-width:1px ;
border-bottom-color: #fff;
border-bottom-style: solid;
opacity: 0;
transform-origin: center;
transform: rotate(40deg);
}
}
//
.radio__inner {
@include flex;
/* #ifndef APP-NVUE */
flex-shrink: 0;
box-sizing: border-box;
/* #endif */
justify-content: center;
align-items: center;
position: relative;
width: 16px;
height: 16px;
border: 1px solid $border-color;
border-radius: 16px;
background-color: #fff;
z-index: 1;
.radio__inner-icon {
width: 8px;
height: 8px;
border-radius: 10px;
opacity: 0;
}
}
//
&.is--default {
//
&.is-disable {
/* #ifdef H5 */
cursor: not-allowed;
/* #endif */
.checkbox__inner {
background-color: #F2F6FC;
border-color: $border-color;
/* #ifdef H5 */
cursor: not-allowed;
/* #endif */
}
.radio__inner {
background-color: #F2F6FC;
border-color: $border-color;
}
.checklist-text {
color: #999;
}
}
//
&.is-checked {
.checkbox__inner {
border-color: $uni-primary;
background-color: $uni-primary;
.checkbox__inner-icon {
opacity: 1;
transform: rotate(45deg);
}
}
.radio__inner {
border-color: $uni-primary;
.radio__inner-icon {
opacity: 1;
background-color: $uni-primary;
}
}
.checklist-text {
color: $uni-primary;
}
//
&.is-disable {
.checkbox__inner {
opacity: $disable;
}
.checklist-text {
opacity: $disable;
}
.radio__inner {
opacity: $disable;
}
}
}
}
//
&.is--button {
margin-right: 10px;
padding: 5px 10px;
border: 1px $border-color solid;
border-radius: 3px;
transition: border-color 0.2s;
//
&.is-disable {
/* #ifdef H5 */
cursor: not-allowed;
/* #endif */
border: 1px #eee solid;
opacity: $disable;
.checkbox__inner {
background-color: #F2F6FC;
border-color: $border-color;
/* #ifdef H5 */
cursor: not-allowed;
/* #endif */
}
.radio__inner {
background-color: #F2F6FC;
border-color: $border-color;
/* #ifdef H5 */
cursor: not-allowed;
/* #endif */
}
.checklist-text {
color: #999;
}
}
&.is-checked {
border-color: $uni-primary;
.checkbox__inner {
border-color: $uni-primary;
background-color: $uni-primary;
.checkbox__inner-icon {
opacity: 1;
transform: rotate(45deg);
}
}
.radio__inner {
border-color: $uni-primary;
.radio__inner-icon {
opacity: 1;
background-color: $uni-primary;
}
}
.checklist-text {
color: $uni-primary;
}
//
&.is-disable {
opacity: $disable;
}
}
}
//
&.is--tag {
margin-right: 10px;
padding: 5px 10px;
border: 1px $border-color solid;
border-radius: 3px;
background-color: #f5f5f5;
.checklist-text {
margin: 0;
color: #666;
}
//
&.is-disable {
/* #ifdef H5 */
cursor: not-allowed;
/* #endif */
opacity: $disable;
}
&.is-checked {
background-color: $uni-primary;
border-color: $uni-primary;
.checklist-text {
color: #fff;
}
}
}
//
&.is--list {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
padding: 10px 15px;
padding-left: 0;
margin: 0;
&.is-list-border {
border-top: 1px #eee solid;
}
//
&.is-disable {
/* #ifdef H5 */
cursor: not-allowed;
/* #endif */
.checkbox__inner {
background-color: #F2F6FC;
border-color: $border-color;
/* #ifdef H5 */
cursor: not-allowed;
/* #endif */
}
.checklist-text {
color: #999;
}
}
&.is-checked {
.checkbox__inner {
border-color: $uni-primary;
background-color: $uni-primary;
.checkbox__inner-icon {
opacity: 1;
transform: rotate(45deg);
}
}
.radio__inner {
.radio__inner-icon {
opacity: 1;
}
}
.checklist-text {
color: $uni-primary;
}
.checklist-content {
.checkobx__list {
opacity: 1;
border-color: $uni-primary;
}
}
//
&.is-disable {
.checkbox__inner {
opacity: $disable;
}
.checklist-text {
opacity: $disable;
}
}
}
}
}
}
}
</style>

84
uni_modules/uni-data-checkbox/package.json

@ -0,0 +1,84 @@
{
"id": "uni-data-checkbox",
"displayName": "uni-data-checkbox 数据选择器",
"version": "1.0.3",
"description": "通过数据驱动的单选框和复选框",
"keywords": [
"uni-ui",
"checkbox",
"单选",
"多选",
"单选多选"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": "^3.1.1"
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
"type": "component-vue"
},
"uni_modules": {
"dependencies": ["uni-load-more","uni-scss"],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

18
uni_modules/uni-data-checkbox/readme.md

@ -0,0 +1,18 @@
## DataCheckbox 数据驱动的单选复选框
> **组件名:uni-data-checkbox**
> 代码块: `uDataCheckbox`
本组件是基于uni-app基础组件checkbox的封装。本组件要解决问题包括:
1. 数据绑定型组件:给本组件绑定一个data,会自动渲染一组候选内容。再以往,开发者需要编写不少代码实现类似功能
2. 自动的表单校验:组件绑定了data,且符合[uni-forms](https://ext.dcloud.net.cn/plugin?id=2773)组件的表单校验规范,搭配使用会自动实现表单校验
3. 本组件合并了单选多选
4. 本组件有若干风格选择,如普通的单选多选框、并列button风格、tag风格。开发者可以快速选择需要的风格。但作为一个封装组件,样式代码虽然不用自己写了,却会牺牲一定的样式自定义性
在uniCloud开发中,`DB Schema`中配置了enum枚举等类型后,在web控制台的[自动生成表单](https://uniapp.dcloud.io/uniCloud/schema?id=autocode)功能中,会自动生成``uni-data-checkbox``组件并绑定好data
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-data-checkbox)
#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839

68
uni_modules/uni-data-picker/changelog.md

@ -0,0 +1,68 @@
## 1.0.9(2022-11-30)
- 修复 v-for 为使用 key 值控制台 warning
## 1.0.8(2022-09-16)
- 可以使用 uni-scss 控制主题色
## 1.0.7(2022-07-06)
- 优化 pc端图标位置不正确的问题
## 1.0.6(2022-07-05)
- 优化 显示样式
## 1.0.5(2022-07-04)
- 修复 uni-data-picker 在 uni-forms-item 中宽度不正确的bug
## 1.0.4(2022-04-19)
- 修复 字节小程序 本地数据无法选择下一级的Bug
## 1.0.3(2022-02-25)
- 修复 nvue 不支持的 v-show 的 bug
## 1.0.2(2022-02-25)
- 修复 条件编译 nvue 不支持的 css 样式
## 1.0.1(2021-11-23)
- 修复 由上个版本引发的map、v-model等属性不生效的bug
## 1.0.0(2021-11-19)
- 优化 组件 UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-data-picker](https://uniapp.dcloud.io/component/uniui/uni-data-picker)
## 0.4.9(2021-10-28)
- 修复 VUE2 v-model 概率无效的 bug
## 0.4.8(2021-10-27)
- 修复 v-model 概率无效的 bug
## 0.4.7(2021-10-25)
- 新增 属性 spaceInfo 服务空间配置 HBuilderX 3.2.11+
- 修复 树型 uniCloud 数据类型为 int 时报错的 bug
## 0.4.6(2021-10-19)
- 修复 非 VUE3 v-model 为 0 时无法选中的 bug
## 0.4.5(2021-09-26)
- 新增 清除已选项的功能(通过 clearIcon 属性配置是否显示按钮),同时提供 clear 方法以供调用,二者等效
- 修复 readonly 为 true 时报错的 bug
## 0.4.4(2021-09-26)
- 修复 上一版本造成的 map 属性失效的 bug
- 新增 ellipsis 属性,支持配置 tab 选项长度过长时是否自动省略
## 0.4.3(2021-09-24)
- 修复 某些情况下级联未触发的 bug
## 0.4.2(2021-09-23)
- 新增 提供 show 和 hide 方法,开发者可以通过 ref 调用
- 新增 选项内容过长自动添加省略号
## 0.4.1(2021-09-15)
- 新增 map 属性 字段映射,将 text/value 映射到数据中的其他字段
## 0.4.0(2021-07-13)
- 组件兼容 vue3,如何创建 vue3 项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 0.3.5(2021-06-04)
- 修复 无法加载云端数据的问题
## 0.3.4(2021-05-28)
- 修复 v-model 无效问题
- 修复 loaddata 为空数据组时加载时间过长问题
- 修复 上个版本引出的本地数据无法选择带有 children 的 2 级节点
## 0.3.3(2021-05-12)
- 新增 组件示例地址
## 0.3.2(2021-04-22)
- 修复 非树形数据有 where 属性查询报错的问题
## 0.3.1(2021-04-15)
- 修复 本地数据概率无法回显时问题
## 0.3.0(2021-04-07)
- 新增 支持云端非树形表结构数据
- 修复 根节点 parent_field 字段等于 null 时选择界面错乱问题
## 0.2.0(2021-03-15)
- 修复 nodeclick、popupopened、popupclosed 事件无法触发的问题
## 0.1.9(2021-03-09)
- 修复 微信小程序某些情况下无法选择的问题
## 0.1.8(2021-02-05)
- 优化 部分样式在 nvue 上的兼容表现
## 0.1.7(2021-02-05)
- 调整为 uni_modules 目录规范

45
uni_modules/uni-data-picker/components/uni-data-picker/keypress.js

@ -0,0 +1,45 @@
// #ifdef H5
export default {
name: 'Keypress',
props: {
disable: {
type: Boolean,
default: false
}
},
mounted () {
const keyNames = {
esc: ['Esc', 'Escape'],
tab: 'Tab',
enter: 'Enter',
space: [' ', 'Spacebar'],
up: ['Up', 'ArrowUp'],
left: ['Left', 'ArrowLeft'],
right: ['Right', 'ArrowRight'],
down: ['Down', 'ArrowDown'],
delete: ['Backspace', 'Delete', 'Del']
}
const listener = ($event) => {
if (this.disable) {
return
}
const keyName = Object.keys(keyNames).find(key => {
const keyName = $event.key
const value = keyNames[key]
return value === keyName || (Array.isArray(value) && value.includes(keyName))
})
if (keyName) {
// 避免和其他按键事件冲突
setTimeout(() => {
this.$emit(keyName, {})
}, 0)
}
}
document.addEventListener('keyup', listener)
this.$once('hook:beforeDestroy', () => {
document.removeEventListener('keyup', listener)
})
},
render: () => {}
}
// #endif

554
uni_modules/uni-data-picker/components/uni-data-picker/uni-data-picker.vue

@ -0,0 +1,554 @@
<template>
<view class="uni-data-tree">
<view class="uni-data-tree-input" @click="handleInput">
<slot :options="options" :data="inputSelected" :error="errorMessage">
<view class="input-value" :class="{'input-value-border': border}">
<text v-if="errorMessage" class="selected-area error-text">{{errorMessage}}</text>
<view v-else-if="loading && !isOpened" class="selected-area">
<uni-load-more class="load-more" :contentText="loadMore" status="loading"></uni-load-more>
</view>
<scroll-view v-else-if="inputSelected.length" class="selected-area" scroll-x="true">
<view class="selected-list">
<view class="selected-item" v-for="(item,index) in inputSelected" :key="index">
<text class="text-color">{{item.text}}</text><text v-if="index<inputSelected.length-1"
class="input-split-line">{{split}}</text>
</view>
</view>
</scroll-view>
<text v-else class="selected-area placeholder">{{placeholder}}</text>
<view v-if="clearIcon && !readonly && inputSelected.length" class="icon-clear"
@click.stop="clear">
<uni-icons type="clear" color="#c0c4cc" size="24"></uni-icons>
</view>
<view class="arrow-area" v-if="(!clearIcon || !inputSelected.length) && !readonly ">
<view class="input-arrow"></view>
</view>
</view>
</slot>
</view>
<view class="uni-data-tree-cover" v-if="isOpened" @click="handleClose"></view>
<view class="uni-data-tree-dialog" v-if="isOpened">
<view class="uni-popper__arrow"></view>
<view class="dialog-caption">
<view class="title-area">
<text class="dialog-title">{{popupTitle}}</text>
</view>
<view class="dialog-close" @click="handleClose">
<view class="dialog-close-plus" data-id="close"></view>
<view class="dialog-close-plus dialog-close-rotate" data-id="close"></view>
</view>
</view>
<data-picker-view class="picker-view" ref="pickerView" v-model="dataValue" :localdata="localdata"
:preload="preload" :collection="collection" :field="field" :orderby="orderby" :where="where"
:step-searh="stepSearh" :self-field="selfField" :parent-field="parentField" :managed-mode="true"
:map="map" :ellipsis="ellipsis" @change="onchange" @datachange="ondatachange" @nodeclick="onnodeclick">
</data-picker-view>
</view>
</view>
</template>
<script>
import dataPicker from "../uni-data-pickerview/uni-data-picker.js"
import DataPickerView from "../uni-data-pickerview/uni-data-pickerview.vue"
/**
* DataPicker 级联选择
* @description 支持单列和多列级联选择列数没有限制如果屏幕显示不全顶部tab区域会左右滚动
* @tutorial https://ext.dcloud.net.cn/plugin?id=3796
* @property {String} popup-title 弹出窗口标题
* @property {Array} localdata 本地数据参考
* @property {Boolean} border = [true|false] 是否有边框
* @property {Boolean} readonly = [true|false] 是否仅读
* @property {Boolean} preload = [true|false] 是否预加载数据
* @value true 开启预加载数据点击弹出窗口后显示已加载数据
* @value false 关闭预加载数据点击弹出窗口后开始加载数据
* @property {Boolean} step-searh = [true|false] 是否分布查询
* @value true 启用分布查询仅查询当前选中节点
* @value false 关闭分布查询一次查询出所有数据
* @property {String|DBFieldString} self-field 分布查询当前字段名称
* @property {String|DBFieldString} parent-field 分布查询父字段名称
* @property {String|DBCollectionString} collection 表名
* @property {String|DBFieldString} field 查询字段多个字段用 `,` 分割
* @property {String} orderby 排序字段及正序倒叙设置
* @property {String|JQLString} where 查询条件
* @event {Function} popupshow 弹出的选择窗口打开时触发此事件
* @event {Function} popuphide 弹出的选择窗口关闭时触发此事件
*/
export default {
name: 'UniDataPicker',
emits: ['popupopened', 'popupclosed', 'nodeclick', 'input', 'change', 'update:modelValue'],
mixins: [dataPicker],
components: {
DataPickerView
},
props: {
options: {
type: [Object, Array],
default () {
return {}
}
},
popupTitle: {
type: String,
default: '请选择'
},
placeholder: {
type: String,
default: '请选择'
},
heightMobile: {
type: String,
default: ''
},
readonly: {
type: Boolean,
default: false
},
clearIcon: {
type: Boolean,
default: true
},
border: {
type: Boolean,
default: true
},
split: {
type: String,
default: '/'
},
ellipsis: {
type: Boolean,
default: true
}
},
data() {
return {
isOpened: false,
inputSelected: []
}
},
created() {
this.form = this.getForm('uniForms')
this.formItem = this.getForm('uniFormsItem')
if (this.formItem) {
if (this.formItem.name) {
this.rename = this.formItem.name
this.form.inputChildrens.push(this)
}
}
this.$nextTick(() => {
this.load()
})
},
methods: {
clear() {
this.inputSelected.splice(0)
this._dispatchEvent([])
},
onPropsChange() {
this._treeData = []
this.selectedIndex = 0
this.load()
},
load() {
if (this.readonly) {
this._processReadonly(this.localdata, this.dataValue)
return
}
if (this.isLocaldata) {
this.loadData()
this.inputSelected = this.selected.slice(0)
} else if (!this.parentField && !this.selfField && this.hasValue) {
this.getNodeData(() => {
this.inputSelected = this.selected.slice(0)
})
} else if (this.hasValue) {
this.getTreePath(() => {
this.inputSelected = this.selected.slice(0)
})
}
},
getForm(name = 'uniForms') {
let parent = this.$parent;
let parentName = parent.$options.name;
while (parentName !== name) {
parent = parent.$parent;
if (!parent) return false;
parentName = parent.$options.name;
}
return parent;
},
show() {
this.isOpened = true
setTimeout(() => {
this.$refs.pickerView.updateData({
treeData: this._treeData,
selected: this.selected,
selectedIndex: this.selectedIndex
})
}, 200)
this.$emit('popupopened')
},
hide() {
this.isOpened = false
this.$emit('popupclosed')
},
handleInput() {
if (this.readonly) {
return
}
this.show()
},
handleClose(e) {
this.hide()
},
onnodeclick(e) {
this.$emit('nodeclick', e)
},
ondatachange(e) {
this._treeData = this.$refs.pickerView._treeData
},
onchange(e) {
this.hide()
this.$nextTick(() => {
this.inputSelected = e;
})
this._dispatchEvent(e)
},
_processReadonly(dataList, value) {
var isTree = dataList.findIndex((item) => {
return item.children
})
if (isTree > -1) {
let inputValue
if (Array.isArray(value)) {
inputValue = value[value.length - 1]
if (typeof inputValue === 'object' && inputValue.value) {
inputValue = inputValue.value
}
} else {
inputValue = value
}
this.inputSelected = this._findNodePath(inputValue, this.localdata)
return
}
if (!this.hasValue) {
this.inputSelected = []
return
}
let result = []
for (let i = 0; i < value.length; i++) {
var val = value[i]
var item = dataList.find((v) => {
return v.value == val
})
if (item) {
result.push(item)
}
}
if (result.length) {
this.inputSelected = result
}
},
_filterForArray(data, valueArray) {
var result = []
for (let i = 0; i < valueArray.length; i++) {
var value = valueArray[i]
var found = data.find((item) => {
return item.value == value
})
if (found) {
result.push(found)
}
}
return result
},
_dispatchEvent(selected) {
let item = {}
if (selected.length) {
var value = new Array(selected.length)
for (var i = 0; i < selected.length; i++) {
value[i] = selected[i].value
}
item = selected[selected.length - 1]
} else {
item.value = ''
}
if (this.formItem) {
this.formItem.setValue(item.value)
}
this.$emit('input', item.value)
this.$emit('update:modelValue', item.value)
this.$emit('change', {
detail: {
value: selected
}
})
}
}
}
</script>
<style >
.uni-data-tree {
flex: 1;
position: relative;
font-size: 14px;
}
.error-text {
color: #DD524D;
}
.input-value {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
flex-wrap: nowrap;
font-size: 14px;
/* line-height: 35px; */
padding: 0 10px;
padding-right: 5px;
overflow: hidden;
height: 35px;
/* #ifndef APP-NVUE */
box-sizing: border-box;
/* #endif */
}
.input-value-border {
border: 1px solid #e5e5e5;
border-radius: 5px;
}
.selected-area {
flex: 1;
overflow: hidden;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
}
.load-more {
/* #ifndef APP-NVUE */
margin-right: auto;
/* #endif */
/* #ifdef APP-NVUE */
width: 40px;
/* #endif */
}
.selected-list {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
flex-wrap: nowrap;
/* padding: 0 5px; */
}
.selected-item {
flex-direction: row;
/* padding: 0 1px; */
/* #ifndef APP-NVUE */
white-space: nowrap;
/* #endif */
}
.text-color {
color: #333;
}
.placeholder {
color: grey;
font-size: 12px;
}
.input-split-line {
opacity: .5;
}
.arrow-area {
position: relative;
width: 20px;
/* #ifndef APP-NVUE */
margin-bottom: 5px;
margin-left: auto;
display: flex;
/* #endif */
justify-content: center;
transform: rotate(-45deg);
transform-origin: center;
}
.input-arrow {
width: 7px;
height: 7px;
border-left: 1px solid #999;
border-bottom: 1px solid #999;
}
.uni-data-tree-cover {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, .4);
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
z-index: 100;
}
.uni-data-tree-dialog {
position: fixed;
left: 0;
top: 20%;
right: 0;
bottom: 0;
background-color: #FFFFFF;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
z-index: 102;
overflow: hidden;
/* #ifdef APP-NVUE */
width: 750rpx;
/* #endif */
}
.dialog-caption {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
/* border-bottom: 1px solid #f0f0f0; */
}
.title-area {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
align-items: center;
/* #ifndef APP-NVUE */
margin: auto;
/* #endif */
padding: 0 10px;
}
.dialog-title {
/* font-weight: bold; */
line-height: 44px;
}
.dialog-close {
position: absolute;
top: 0;
right: 0;
bottom: 0;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
padding: 0 15px;
}
.dialog-close-plus {
width: 16px;
height: 2px;
background-color: #666;
border-radius: 2px;
transform: rotate(45deg);
}
.dialog-close-rotate {
position: absolute;
transform: rotate(-45deg);
}
.picker-view {
flex: 1;
overflow: hidden;
}
.icon-clear {
display: flex;
align-items: center;
}
/* #ifdef H5 */
@media all and (min-width: 768px) {
.uni-data-tree-cover {
background-color: transparent;
}
.uni-data-tree-dialog {
position: absolute;
top: 55px;
height: auto;
min-height: 400px;
max-height: 50vh;
background-color: #fff;
border: 1px solid #EBEEF5;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
border-radius: 4px;
overflow: unset;
}
.dialog-caption {
display: none;
}
.icon-clear {
/* margin-right: 5px; */
}
}
/* #endif */
/* picker 弹出层通用的指示小三角, todo:扩展至上下左右方向定位 */
/* #ifndef APP-NVUE */
.uni-popper__arrow,
.uni-popper__arrow::after {
position: absolute;
display: block;
width: 0;
height: 0;
border-color: transparent;
border-style: solid;
border-width: 6px;
}
.uni-popper__arrow {
filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03));
top: -6px;
left: 10%;
margin-right: 3px;
border-top-width: 0;
border-bottom-color: #EBEEF5;
}
.uni-popper__arrow::after {
content: " ";
top: 1px;
margin-left: -6px;
border-top-width: 0;
border-bottom-color: #fff;
}
/* #endif */
</style>

563
uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-picker.js

@ -0,0 +1,563 @@
export default {
props: {
localdata: {
type: [Array, Object],
default () {
return []
}
},
spaceInfo: {
type: Object,
default () {
return {}
}
},
collection: {
type: String,
default: ''
},
action: {
type: String,
default: ''
},
field: {
type: String,
default: ''
},
orderby: {
type: String,
default: ''
},
where: {
type: [String, Object],
default: ''
},
pageData: {
type: String,
default: 'add'
},
pageCurrent: {
type: Number,
default: 1
},
pageSize: {
type: Number,
default: 20
},
getcount: {
type: [Boolean, String],
default: false
},
getone: {
type: [Boolean, String],
default: false
},
gettree: {
type: [Boolean, String],
default: false
},
manual: {
type: Boolean,
default: false
},
value: {
type: [Array, String, Number],
default () {
return []
}
},
modelValue: {
type: [Array, String, Number],
default () {
return []
}
},
preload: {
type: Boolean,
default: false
},
stepSearh: {
type: Boolean,
default: true
},
selfField: {
type: String,
default: ''
},
parentField: {
type: String,
default: ''
},
multiple: {
type: Boolean,
default: false
},
map: {
type: Object,
default() {
return {
text: "text",
value: "value"
}
}
}
},
data() {
return {
loading: false,
errorMessage: '',
loadMore: {
contentdown: '',
contentrefresh: '',
contentnomore: ''
},
dataList: [],
selected: [],
selectedIndex: 0,
page: {
current: this.pageCurrent,
size: this.pageSize,
count: 0
}
}
},
computed: {
isLocaldata() {
return !this.collection.length
},
postField() {
let fields = [this.field];
if (this.parentField) {
fields.push(`${this.parentField} as parent_value`);
}
return fields.join(',');
},
dataValue() {
let isModelValue = Array.isArray(this.modelValue) ? (this.modelValue.length > 0) : (this.modelValue !== null || this.modelValue !== undefined)
return isModelValue ? this.modelValue : this.value
},
hasValue() {
if (typeof this.dataValue === 'number') {
return true
}
return (this.dataValue != null) && (this.dataValue.length > 0)
}
},
created() {
this.$watch(() => {
var al = [];
['pageCurrent',
'pageSize',
'spaceInfo',
'value',
'modelValue',
'localdata',
'collection',
'action',
'field',
'orderby',
'where',
'getont',
'getcount',
'gettree'
].forEach(key => {
al.push(this[key])
});
return al
}, (newValue, oldValue) => {
let needReset = false
for (let i = 2; i < newValue.length; i++) {
if (newValue[i] != oldValue[i]) {
needReset = true
break
}
}
if (newValue[0] != oldValue[0]) {
this.page.current = this.pageCurrent
}
this.page.size = this.pageSize
this.onPropsChange()
})
this._treeData = []
},
methods: {
onPropsChange() {
this._treeData = []
},
getCommand(options = {}) {
/* eslint-disable no-undef */
let db = uniCloud.database(this.spaceInfo)
const action = options.action || this.action
if (action) {
db = db.action(action)
}
const collection = options.collection || this.collection
db = db.collection(collection)
const where = options.where || this.where
if (!(!where || !Object.keys(where).length)) {
db = db.where(where)
}
const field = options.field || this.field
if (field) {
db = db.field(field)
}
const orderby = options.orderby || this.orderby
if (orderby) {
db = db.orderBy(orderby)
}
const current = options.pageCurrent !== undefined ? options.pageCurrent : this.page.current
const size = options.pageSize !== undefined ? options.pageSize : this.page.size
const getCount = options.getcount !== undefined ? options.getcount : this.getcount
const getTree = options.gettree !== undefined ? options.gettree : this.gettree
const getOptions = {
getCount,
getTree
}
if (options.getTreePath) {
getOptions.getTreePath = options.getTreePath
}
db = db.skip(size * (current - 1)).limit(size).get(getOptions)
return db
},
getNodeData(callback) {
if (this.loading) {
return
}
this.loading = true
this.getCommand({
field: this.postField,
where: this._pathWhere()
}).then((res) => {
this.loading = false
this.selected = res.result.data
callback && callback()
}).catch((err) => {
this.loading = false
this.errorMessage = err
})
},
getTreePath(callback) {
if (this.loading) {
return
}
this.loading = true
this.getCommand({
field: this.postField,
getTreePath: {
startWith: `${this.selfField}=='${this.dataValue}'`
}
}).then((res) => {
this.loading = false
let treePath = []
this._extractTreePath(res.result.data, treePath)
this.selected = treePath
callback && callback()
}).catch((err) => {
this.loading = false
this.errorMessage = err
})
},
loadData() {
if (this.isLocaldata) {
this._processLocalData()
return
}
if (this.dataValue != null) {
this._loadNodeData((data) => {
this._treeData = data
this._updateBindData()
this._updateSelected()
})
return
}
if (this.stepSearh) {
this._loadNodeData((data) => {
this._treeData = data
this._updateBindData()
})
} else {
this._loadAllData((data) => {
this._treeData = []
this._extractTree(data, this._treeData, null)
this._updateBindData()
})
}
},
_loadAllData(callback) {
if (this.loading) {
return
}
this.loading = true
this.getCommand({
field: this.postField,
gettree: true,
startwith: `${this.selfField}=='${this.dataValue}'`
}).then((res) => {
this.loading = false
callback(res.result.data)
this.onDataChange()
}).catch((err) => {
this.loading = false
this.errorMessage = err
})
},
_loadNodeData(callback, pw) {
if (this.loading) {
return
}
this.loading = true
this.getCommand({
field: this.postField,
where: pw || this._postWhere(),
pageSize: 500
}).then((res) => {
this.loading = false
callback(res.result.data)
this.onDataChange()
}).catch((err) => {
this.loading = false
this.errorMessage = err
})
},
_pathWhere() {
let result = []
let where_field = this._getParentNameByField();
if (where_field) {
result.push(`${where_field} == '${this.dataValue}'`)
}
if (this.where) {
return `(${this.where}) && (${result.join(' || ')})`
}
return result.join(' || ')
},
_postWhere() {
let result = []
let selected = this.selected
let parentField = this.parentField
if (parentField) {
result.push(`${parentField} == null || ${parentField} == ""`)
}
if (selected.length) {
for (var i = 0; i < selected.length - 1; i++) {
result.push(`${parentField} == '${selected[i].value}'`)
}
}
let where = []
if (this.where) {
where.push(`(${this.where})`)
}
if (result.length) {
where.push(`(${result.join(' || ')})`)
}
return where.join(' && ')
},
_nodeWhere() {
let result = []
let selected = this.selected
if (selected.length) {
result.push(`${this.parentField} == '${selected[selected.length - 1].value}'`)
}
if (this.where) {
return `(${this.where}) && (${result.join(' || ')})`
}
return result.join(' || ')
},
_getParentNameByField() {
const fields = this.field.split(',');
let where_field = null;
for (let i = 0; i < fields.length; i++) {
const items = fields[i].split('as');
if (items.length < 2) {
continue;
}
if (items[1].trim() === 'value') {
where_field = items[0].trim();
break;
}
}
return where_field
},
_isTreeView() {
return (this.parentField && this.selfField)
},
_updateSelected() {
var dl = this.dataList
var sl = this.selected
let textField = this.map.text
let valueField = this.map.value
for (var i = 0; i < sl.length; i++) {
var value = sl[i].value
var dl2 = dl[i]
for (var j = 0; j < dl2.length; j++) {
var item2 = dl2[j]
if (item2[valueField] === value) {
sl[i].text = item2[textField]
break
}
}
}
},
_updateBindData(node) {
const {
dataList,
hasNodes
} = this._filterData(this._treeData, this.selected)
let isleaf = this._stepSearh === false && !hasNodes
if (node) {
node.isleaf = isleaf
}
this.dataList = dataList
this.selectedIndex = dataList.length - 1
if (!isleaf && this.selected.length < dataList.length) {
this.selected.push({
value: null,
text: "请选择"
})
}
return {
isleaf,
hasNodes
}
},
_filterData(data, paths) {
let dataList = []
let hasNodes = true
dataList.push(data.filter((item) => {
return (item.parent_value === null || item.parent_value === undefined || item.parent_value === '')
}))
for (let i = 0; i < paths.length; i++) {
var value = paths[i].value
var nodes = data.filter((item) => {
return item.parent_value === value
})
if (nodes.length) {
dataList.push(nodes)
} else {
hasNodes = false
}
}
return {
dataList,
hasNodes
}
},
_extractTree(nodes, result, parent_value) {
let list = result || []
let valueField = this.map.value
for (let i = 0; i < nodes.length; i++) {
let node = nodes[i]
let child = {}
for (let key in node) {
if (key !== 'children') {
child[key] = node[key]
}
}
if (parent_value !== null && parent_value !== undefined && parent_value !== '') {
child.parent_value = parent_value
}
result.push(child)
let children = node.children
if (children) {
this._extractTree(children, result, node[valueField])
}
}
},
_extractTreePath(nodes, result) {
let list = result || []
for (let i = 0; i < nodes.length; i++) {
let node = nodes[i]
let child = {}
for (let key in node) {
if (key !== 'children') {
child[key] = node[key]
}
}
result.push(child)
let children = node.children
if (children) {
this._extractTreePath(children, result)
}
}
},
_findNodePath(key, nodes, path = []) {
let textField = this.map.text
let valueField = this.map.value
for (let i = 0; i < nodes.length; i++) {
let node = nodes[i]
let children = node.children
let text = node[textField]
let value = node[valueField]
path.push({
value,
text
})
if (value === key) {
return path
}
if (children) {
const p = this._findNodePath(key, children, path)
if (p.length) {
return p
}
}
path.pop()
}
return []
},
_processLocalData() {
this._treeData = []
this._extractTree(this.localdata, this._treeData)
var inputValue = this.dataValue
if (inputValue === undefined) {
return
}
if (Array.isArray(inputValue)) {
inputValue = inputValue[inputValue.length - 1]
if (typeof inputValue === 'object' && inputValue[this.map.value]) {
inputValue = inputValue[this.map.value]
}
}
this.selected = this._findNodePath(inputValue, this.localdata)
}
}
}

335
uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-pickerview.vue

@ -0,0 +1,335 @@
<template>
<view class="uni-data-pickerview">
<scroll-view class="selected-area" scroll-x="true" scroll-y="false" :show-scrollbar="false">
<view class="selected-list">
<template v-for="(item,index) in selected">
<view class="selected-item"
:class="{'selected-item-active':index==selectedIndex, 'selected-item-text-overflow': ellipsis}"
v-if="item.text" @click="handleSelect(index)">
<text class="">{{item.text}}</text>
</view>
</template>
</view>
</scroll-view>
<view class="tab-c">
<template v-for="(child, i) in dataList" >
<scroll-view class="list" :key="i" v-if="i==selectedIndex" :scroll-y="true">
<view class="item" :class="{'is-disabled': !!item.disable}" v-for="(item, j) in child" :key="j"
@click="handleNodeClick(item, i, j)">
<text class="item-text item-text-overflow">{{item[map.text]}}</text>
<view class="check" v-if="selected.length > i && item[map.value] == selected[i].value"></view>
</view>
</scroll-view>
</template>
<view class="loading-cover" v-if="loading">
<uni-load-more class="load-more" :contentText="loadMore" status="loading"></uni-load-more>
</view>
<view class="error-message" v-if="errorMessage">
<text class="error-text">{{errorMessage}}</text>
</view>
</view>
</view>
</template>
<script>
import dataPicker from "./uni-data-picker.js"
/**
* DataPickerview
* @description uni-data-pickerview
* @tutorial https://ext.dcloud.net.cn/plugin?id=3796
* @property {Array} localdata 本地数据参考
* @property {Boolean} step-searh = [true|false] 是否分布查询
* @value true 启用分布查询仅查询当前选中节点
* @value false 关闭分布查询一次查询出所有数据
* @property {String|DBFieldString} self-field 分布查询当前字段名称
* @property {String|DBFieldString} parent-field 分布查询父字段名称
* @property {String|DBCollectionString} collection 表名
* @property {String|DBFieldString} field 查询字段多个字段用 `,` 分割
* @property {String} orderby 排序字段及正序倒叙设置
* @property {String|JQLString} where 查询条件
*/
export default {
name: 'UniDataPickerView',
emits: ['nodeclick', 'change', 'datachange', 'update:modelValue'],
mixins: [dataPicker],
props: {
managedMode: {
type: Boolean,
default: false
},
ellipsis: {
type: Boolean,
default: true
}
},
data() {
return {}
},
created() {
if (this.managedMode) {
return
}
this.$nextTick(() => {
this.load()
})
},
methods: {
onPropsChange() {
this._treeData = []
this.selectedIndex = 0
this.load()
},
load() {
if (this.isLocaldata) {
this.loadData()
} else if (this.dataValue.length) {
this.getTreePath((res) => {
this.loadData()
})
}
},
handleSelect(index) {
this.selectedIndex = index
},
handleNodeClick(item, i, j) {
if (item.disable) {
return
}
const node = this.dataList[i][j]
const text = node[this.map.text]
const value = node[this.map.value]
if (i < this.selected.length - 1) {
this.selected.splice(i, this.selected.length - i)
this.selected.push({
text,
value
})
} else if (i === this.selected.length - 1) {
this.selected.splice(i, 1, {
text,
value
})
}
if (node.isleaf) {
this.onSelectedChange(node, node.isleaf)
return
}
const {
isleaf,
hasNodes
} = this._updateBindData()
if (!this._isTreeView() && !hasNodes) {
this.onSelectedChange(node, true)
return
}
if (this.isLocaldata && (!hasNodes || isleaf)) {
this.onSelectedChange(node, true)
return
}
if (!isleaf && !hasNodes) {
this._loadNodeData((data) => {
if (!data.length) {
node.isleaf = true
} else {
this._treeData.push(...data)
this._updateBindData(node)
}
this.onSelectedChange(node, node.isleaf)
}, this._nodeWhere())
return
}
this.onSelectedChange(node, false)
},
updateData(data) {
this._treeData = data.treeData
this.selected = data.selected
if (!this._treeData.length) {
this.loadData()
} else {
//this.selected = data.selected
this._updateBindData()
}
},
onDataChange() {
this.$emit('datachange')
},
onSelectedChange(node, isleaf) {
if (isleaf) {
this._dispatchEvent()
}
if (node) {
this.$emit('nodeclick', node)
}
},
_dispatchEvent() {
this.$emit('change', this.selected.slice(0))
}
}
}
</script>
<style lang="scss">
$uni-primary: #007aff !default;
.uni-data-pickerview {
flex: 1;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
overflow: hidden;
height: 100%;
}
.error-text {
color: #DD524D;
}
.loading-cover {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
background-color: rgba(255, 255, 255, .5);
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
align-items: center;
z-index: 1001;
}
.load-more {
/* #ifndef APP-NVUE */
margin: auto;
/* #endif */
}
.error-message {
background-color: #fff;
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
padding: 15px;
opacity: .9;
z-index: 102;
}
/* #ifdef APP-NVUE */
.selected-area {
width: 750rpx;
}
/* #endif */
.selected-list {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
flex-wrap: nowrap;
padding: 0 5px;
border-bottom: 1px solid #f8f8f8;
}
.selected-item {
margin-left: 10px;
margin-right: 10px;
padding: 12px 0;
text-align: center;
/* #ifndef APP-NVUE */
white-space: nowrap;
/* #endif */
}
.selected-item-text-overflow {
width: 168px;
/* fix nvue */
overflow: hidden;
/* #ifndef APP-NVUE */
width: 6em;
white-space: nowrap;
text-overflow: ellipsis;
-o-text-overflow: ellipsis;
/* #endif */
}
.selected-item-active {
border-bottom: 2px solid $uni-primary;
}
.selected-item-text {
color: $uni-primary;
}
.tab-c {
position: relative;
flex: 1;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
overflow: hidden;
}
.list {
flex: 1;
}
.item {
padding: 12px 15px;
/* border-bottom: 1px solid #f0f0f0; */
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: space-between;
}
.is-disabled {
opacity: .5;
}
.item-text {
/* flex: 1; */
color: #333333;
}
.item-text-overflow {
width: 280px;
/* fix nvue */
overflow: hidden;
/* #ifndef APP-NVUE */
width: 20em;
white-space: nowrap;
text-overflow: ellipsis;
-o-text-overflow: ellipsis;
/* #endif */
}
.check {
margin-right: 5px;
border: 2px solid $uni-primary;
border-left: 0;
border-top: 0;
height: 12px;
width: 6px;
transform-origin: center;
/* #ifndef APP-NVUE */
transition: all 0.3s;
/* #endif */
transform: rotate(45deg);
}
</style>

90
uni_modules/uni-data-picker/package.json

@ -0,0 +1,90 @@
{
"id": "uni-data-picker",
"displayName": "uni-data-picker 数据驱动的picker选择器",
"version": "1.0.9",
"description": "单列、多列级联选择器,常用于省市区城市选择、公司部门选择、多级分类等场景",
"keywords": [
"uni-ui",
"uniui",
"picker",
"级联",
"省市区",
""
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
"type": "component-vue"
},
"uni_modules": {
"dependencies": [
"uni-load-more",
"uni-icons",
"uni-scss"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "u"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

22
uni_modules/uni-data-picker/readme.md

@ -0,0 +1,22 @@
## DataPicker 级联选择
> **组件名:uni-data-picker**
> 代码块: `uDataPicker`
> 关联组件:`uni-data-pickerview`、`uni-load-more`。
`<uni-data-picker>` 是一个选择类[datacom组件](https://uniapp.dcloud.net.cn/component/datacom)。
支持单列、和多列级联选择。列数没有限制,如果屏幕显示不全,顶部tab区域会左右滚动。
候选数据支持一次性加载完毕,也支持懒加载,比如示例图中,选择了“北京”后,动态加载北京的区县数据。
`<uni-data-picker>` 组件尤其适用于地址选择、分类选择等选择类。
`<uni-data-picker>` 支持本地数据、云端静态数据(json),uniCloud云数据库数据。
`<uni-data-picker>` 可以通过JQL直连uniCloud云数据库,配套[DB Schema](https://uniapp.dcloud.net.cn/uniCloud/schema),可在schema2code中自动生成前端页面,还支持服务器端校验。
在uniCloud数据表中新建表“uni-id-address”和“opendb-city-china”,这2个表的schema自带foreignKey关联。在“uni-id-address”表的表结构页面使用schema2code生成前端页面,会自动生成地址管理的维护页面,自动从“opendb-city-china”表包含的中国所有省市区信息里选择地址。
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-data-picker)
#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839

25
uni_modules/uni-data-select/changelog.md

@ -0,0 +1,25 @@
## 1.0.1(2022-12-06)
- 修复 当where变化时,数据不会自动更新的问题
## 0.1.9(2022-09-05)
- 修复 微信小程序下拉框出现后选择会点击到蒙板后面的输入框
## 0.1.8(2022-08-29)
- 修复 点击的位置不准确
## 0.1.7(2022-08-12)
- 新增 支持 disabled 属性
## 0.1.6(2022-07-06)
- 修复 pc端宽度异常的bug
## 0.1.5
- 修复 pc端宽度异常的bug
## 0.1.4(2022-07-05)
- 优化 显示样式
## 0.1.3(2022-06-02)
- 修复 localdata 赋值不生效的 bug
- 新增 支持 uni.scss 修改颜色
- 新增 支持选项禁用(数据选项设置 disabled: true 即禁用)
## 0.1.2(2022-05-08)
- 修复 当 value 为 0 时选择不生效的 bug
## 0.1.1(2022-05-07)
- 新增 记住上次的选项(仅 collection 存在时有效)
## 0.1.0(2022-04-22)
- 初始化

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

Loading…
Cancel
Save