This commit is contained in:
liuyuan
2025-06-10 17:52:06 +08:00
parent 3b86ad9ed8
commit a26581fd81
96 changed files with 11316 additions and 367 deletions

View File

@@ -2,11 +2,11 @@ let baseUrl = "";
let socketUrl = "";
if (process.env.NODE_ENV === 'development') {
// 开发环境
//baseUrl = "http://192.168.110.100:9200/pb/"; // 张川川
baseUrl = "https://api.nuttyreading.com/"; //线上正式
baseUrl = "http://192.168.110.100:9200/pb/"; // 张川川
//baseUrl = "https://api.nuttyreading.com/"; //线上正式
} else if (process.env.NODE_ENV === 'production') {
//baseUrl = "http://192.168.110.100:9200/pb/"; // 张川川
baseUrl = "https://api.nuttyreading.com/"; //线上正式
baseUrl = "http://192.168.110.100:9200/pb/"; // 张川川
//baseUrl = "https://api.nuttyreading.com/"; //线上正式
}
const courtConfig = {
//微信公众号APPID

View File

@@ -1,9 +1,6 @@
import $http from '@/config/requestConfig'
import store from '@/store';
import base from '@/config/baseUrl';
import {
getAppWxLatLon
} from '@/plugins/utils';
// #ifdef H5
import {
getLatLonH5,
@@ -231,9 +228,6 @@ export const getLatLon = function(tip) {
// #ifdef H5
getLatLonH5(successProcess, errProcess);
// #endif
// #ifndef H5
getAppWxLatLon(successProcess, errProcess);
// #endif
});
}

View File

@@ -25,8 +25,8 @@ import { judgeLogin } from '@/config/login';
Vue.prototype.judgeLogin = judgeLogin;
Vue.prototype.isShowHtml = store.state.loadingShow;
Vue.prototype.$baseUrl = "https://api.nuttyreading.com/"
//Vue.prototype.$baseUrl = "http://192.168.110.100:9200/pb/"
//Vue.prototype.$baseUrl = "https://api.nuttyreading.com/"
Vue.prototype.$baseUrl = "http://192.168.110.100:9200/pb/"
//判断手机型号
uni.getSystemInfo({
@@ -42,6 +42,8 @@ uni.getSystemInfo({
}
})
import commonGoodsNav from '@/pages/component/commonComponents/goodsNav.vue'
Vue.component('common-goods-nav', commonGoodsNav);
import commonList from '@/pages/component/commonComponents/list.vue'
Vue.component('common-list', commonList);

View File

@@ -49,7 +49,9 @@
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>",
"<uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"/>",
"<uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\"/>"
],
"abiFilters" : [ "armeabi-v7a", "arm64-v8a", "x86" ],
"minSdkVersion" : 23,

View File

@@ -11,6 +11,17 @@
}
}
},
{
"path": "pages/user/login",
"style": {
"navigationBarTitleText": "登录",
"app-plus": {
"bounce": "none",
"titleNView": false,
"popGesture": "none"
}
}
},
{
"path": "pages/my/index",
"style": {
@@ -35,17 +46,6 @@
}
}
},
{
"path": "pages/user/login",
"style": {
"navigationBarTitleText": "登录",
"app-plus": {
"bounce": "none",
"titleNView": false,
"popGesture": "none"
}
}
},
{
"path": "pages/user/forget",
"style": {
@@ -177,6 +177,102 @@
}
}
},
{
"path": "pages/folder/index",
"style": {
"navigationBarTitleText": "我的病历夹",
"enablePullDownRefresh": false, // 禁止下拉刷新
"app-plus": {
"bounce": "none",
"titleNView": false,
"popGesture": "none"
}
}
},
{
"path": "pages/folder/patient",
"style": {
"navigationBarTitleText": "患者列表",
"enablePullDownRefresh": false, // 禁止下拉刷新
"app-plus": {
"bounce": "none",
"titleNView": false,
"popGesture": "none"
}
}
},
{
"path": "pages/wallet/recharge",
"style": {
"navigationBarTitleText": "充值",
"enablePullDownRefresh": false, // 禁止下拉刷新
"app-plus": {
"bounce": "none",
"titleNView": false,
"popGesture": "none"
}
}
},
{
"path": "pages/wallet/account",
"style": {
"navigationBarTitleText": "我的账户",
"enablePullDownRefresh": false, // 禁止下拉刷新
"app-plus": {
"bounce": "none",
"titleNView": false,
"popGesture": "none"
}
}
},
{
"path": "pages/wallet/points",
"style": {
"navigationBarTitleText": "我的积分",
"enablePullDownRefresh": false, // 禁止下拉刷新
"app-plus": {
"bounce": "none",
"titleNView": false,
"popGesture": "none"
}
}
},
{
"path": "pages/vip/index",
"style": {
"navigationBarTitleText": "VIP办理",
"enablePullDownRefresh": false, // 禁止下拉刷新
"app-plus": {
"bounce": "none",
"titleNView": false,
"popGesture": "none"
}
}
},
{
"path": "pages/order/index",
"style": {
"navigationBarTitleText": "我的订单",
"enablePullDownRefresh": false, // 禁止下拉刷新
"app-plus": {
"bounce": "none",
"titleNView": false,
"popGesture": "none"
}
}
},
{
"path": "pages/order/infor",
"style": {
"navigationBarTitleText": "订单详情",
"enablePullDownRefresh": false, // 禁止下拉刷新
"app-plus": {
"bounce": "none",
"titleNView": false,
"popGesture": "none"
}
}
},

View File

@@ -0,0 +1,97 @@
<template>
<view class="goods_nav">
<view class="left">
<view v-for="(v,i) in iconList" class="icon_item" v-if="iconList.length>0">
<u-icon :name="v.icon" :color="v.infoColor" size="22" v-if="v.iconType" style="margin:0 auto"
@click="clickIcon(v)"></u-icon>
<uni-icons :type="v.icon" size="22" :color="v.infoColor" style="margin:0 auto" v-else> </uni-icons>
<view :style="`color:${v.infoColor};`">{{ v.text }}</view>
</view>
<slot name="leftSlot"></slot>
</view>
<view class="right">
<view class="button" v-for="(v,i) in customButton"
:style="`background:${v.backgroundColor} !important;color:${v.color};width:${v.width}`"
@click="submit(v)">
{{ v.text }}
</view>
</view>
<slot name="bottomSlot"></slot>
</view>
</template>
<script>
import $http from '@/config/requestConfig.js';
import {
mapState
} from 'vuex';
export default {
props: ['iconList', 'customButton'],
data() {
return {
}
},
computed: {
...mapState(['userInfo']),
},
methods: {
submit(v) {
this.$emit('submit', v)
},
clickIcon(v) {
this.$emit('clickIcon', v)
},
},
onBackPress() {
// #ifdef APP-PLUS
plus.key.hideSoftKeybord();
// #endif
},
}
</script>
<style lang="scss" scoped>
.goods_nav {
background-color: #fff;
width: 100%;
padding: 20rpx 30rpx;
overflow: hidden;
display: flex;
align-items: center;
justify-content: space-between;
.left {
height: 100%;
float: left;
display: flex;
align-items: center;
}
}
.richDetail {
width: 100%;
height: 100%;
}
.button {
float: right;
width: 240rpx;
border-radius: 100rpx;
line-height: 70rpx;
text-align: center;
background: linear-gradient(90deg, rgb(254, 96, 53), rgb(239, 18, 36));
color: rgb(255, 255, 255);
}
.icon_item {
display: flex;
justify-content: center;
flex-direction: column;
}
</style>

View File

@@ -24,7 +24,7 @@
<u-empty v-else-if="noDataIcon && isLoadingHide" :mode="noDataIcon"
:icon="`http://cdn.uviewui.com/uview/empty/${noDataIcon}.png`">
</u-empty>
<u-divider style="width: 100%;" v-else-if="!isOrderList" text="暂无数据"></u-divider>
<text class="null_text" v-else-if="!isOrderList">暂无数据</text>
</view>
</view>
</template>
@@ -126,4 +126,12 @@
.list_item:last-child {
border-bottom: none !important;
}
.null_text{
display: block;
text-align: center;
font-size: 30rpx;
color: #999;
padding-top: 150rpx;
padding-bottom: 50rpx;
}
</style>

View File

@@ -1,6 +1,6 @@
<template>
<view class="content">
<z-nav-bar title="名医精彩"></z-nav-bar>
<z-nav-bar title="名医精彩" bgColor="#5188e5" fontColor="#fff"></z-nav-bar>
<z-navigation></z-navigation>
</view>
</template>
@@ -18,7 +18,7 @@ export default {
uni.hideTabBar();
},
onShow() {
uni.removeStorageSync('homeParams');
},
methods: {
//获取数据

451
pages/folder/index.vue Normal file
View File

@@ -0,0 +1,451 @@
<template>
<view class="content">
<z-nav-bar title="我的病历夹" bgColor="#5188e5" fontColor="#fff">
<template v-slot:right>
<view class="top_right" @tap="createFolder">
<uni-icons type="folder-add" size="17" color="#fff"></uni-icons>
<text>创建</text>
</view>
</template>
</z-nav-bar>
<view class="list_wrap">
<view v-if="showMask" class="mask" @click="closeMenu"></view>
<view class="list_content" v-if="list&&list.length>0">
<view class="list_item" v-for="(item, index) in list" :key="index"
@click="goToList(item.id, item.folderName)"
@longpress="handleLongPress(index)"
@touchstart="handleTouchStart(index)"
@touchend="handleTouchEnd"
:class="activeIndex==index?'active':''">
<view class="item_data">
<text class="item_name">{{item.folderName}}</text>
<text class="item_time">{{item.createTime}}</text>
</view>
<!-- 长按弹窗菜单 -->
<view class="action-menu" v-if="activeIndex === index && showMenu">
<view class="menu-item" @tap.stop="renameItem(item.folderName, item.id)">
<text>重命名</text><uni-icons type="compose" size="18"></uni-icons>
</view>
<view class="menu-item" @tap.stop="deleteItem(item)">
<text style=" color: red;">删除</text><uni-icons type="trash" size="18" color="red"></uni-icons>
</view>
</view>
</view>
</view>
<text class="null_text" v-else>{{null_text}}</text>
</view>
<uni-popup ref="edit_folder" class="folder_popup">
<view class="popup-content">
<text class="edit_folder_name">重命名</text>
<view class="edit_folder_input">
<input type="text" v-model="folderName" placeholder="请输入病历夹名称" placeholder-class="custom-placeholder" />
</view>
<button class="edit_folder_btn" @click="update">保存</button>
</view>
</uni-popup>
<uni-popup ref="add_folder" class="folder_popup">
<view class="popup-content">
<text class="add_folder_name">创建病历夹</text>
<view class="add_folder_input">
<input type="text" v-model="addFolderName" placeholder="请输入病历夹名称" placeholder-class="custom-placeholder" />
</view>
<button class="add_folder_btn" @click="addFolder">保存</button>
</view>
</uni-popup>
</view>
</template>
<script>
import $http from "@/config/requestConfig.js";
export default {
data() {
return {
id: null,
list: [],
null_text: '',
folderName: '',
activeIndex: -1,
showMenu: false, //是否显示菜单
showMask: false, //是否显示遮罩
addFolderName: '',
isLongPress: false,
touchStartTime: 0,
}
},
onLoad() {
uni.hideTabBar();
},
onShow() {
this.getData();
},
methods: {
//获取数据
getData() {
uni.showLoading({
title: '加载中'
})
this.$http.request({
url: 'taihumed/aiRecordFolder/getRecordFolders',
method: "POST",
data: {
folderName: '',
patientName: ''
},
header: {
"Content-Type": "application/json",
},
})
.then(res=> {
if(res.code==0){
uni.hideLoading();
if(res.list&&res.list.length>0){
this.list = res.list;
}else{
this.list = [];
this.null_text = '暂无数据';
}
}
});
},
//长按操作
handleLongPress(index) {
this.activeIndex = index;
this.showMenu = true;
this.showMask = true;
//标记长按模式
this.isLongPress = true;
},
//关闭菜单
closeMenu() {
this.showMenu = false;
this.showMask = false;
this.activeIndex = -1;
},
//重命名
renameItem(name, id) {
this.id = id; //编辑的id
this.folderName = name;
this.$refs.edit_folder.open('center');
this.closeMenu();
},
//保存
update(){
if(!this.folderName){
this.$commonJS.showToast("请输入病历夹名称");
return
}
uni.showLoading({
title: '正在保存'
})
this.$http.request({
url: 'taihumed/aiRecordFolder/updateRecordFolder',
method: "POST",
data: {
id: this.id,
folderName: this.folderName,
sort: 0
},
header: {
"Content-Type": "application/json",
},
})
.then(res=> {
if(res.code==0){
uni.hideLoading();
uni.showToast({
title: '保存成功',
icon: 'success'
})
this.$refs.edit_folder.close();
this.getData();
}
});
},
//删除
deleteItem(data) {
this.closeMenu();
uni.showModal({
title: '提示',
content: '确定删除['+ data.folderName +']病历夹?',
success: (res) => {
if (res.confirm) {
uni.showLoading({
title: '正在删除'
})
this.$http.request({
url: 'taihumed/aiRecordFolder/delRecordFolder',
method: "POST",
data: {
id: data.id
},
header: {
"Content-Type": "application/json",
},
})
.then(res=> {
if(res.code==0){
uni.hideLoading();
uni.showToast({
title: '删除成功',
icon: 'success'
})
this.getData();
}
});
}
}
});
},
//创建
createFolder(){
this.$refs.add_folder.open();
this.addFolderName = '';
},
//保存
addFolder(){
if(!this.addFolderName){
this.$commonJS.showToast("请输入病历夹名称");
return
}
uni.showLoading({
title: '正在创建中'
})
this.$http.request({
url: 'taihumed/aiRecordFolder/addRecordFolder',
method: "POST",
data: {
folderName: this.addFolderName,
sort: 0
},
header: {
"Content-Type": "application/json",
},
})
.then(res=> {
if(res.code==0){
uni.hideLoading();
uni.showToast({
title: '创建成功',
icon: 'success'
})
this.$refs.add_folder.close();
this.getData();
}
})
},
//跳转患者列表页
goToList(id, name){
if (this.isLongPress) {
this.isLongPress = false; //重置状态
}
this.onPageJump("/pages/folder/patient?id="+id+"&name="+name);
},
//触摸开始(防抖)
handleTouchStart(index) {
this.isLongPress = false;
},
//触摸结束
handleTouchEnd() {
const touchDuration = Date.now() - this.touchStartTime;
//如果触摸时间超过300ms认为是长按不触发点击
if (touchDuration > 300) {
this.isLongPress = true;
}
},
onPageJump(url) {
uni.navigateTo({
url: url,
});
},
},
}
</script>
<style lang="scss" scoped>
@import '@/static/mixin.scss';
.content{
height: 100%;
overflow: auto;
background-color: #fff;
}
.top_right{
display: flex;
align-items: center;
margin-right: 30rpx;
text{
font-size: 28rpx;
color: #fff;
padding-left: 2rpx;
}
}
.list_content{
margin: 20rpx 30rpx;
border-radius: 10rpx;
background-color: #fff;
padding: 20rpx 30rpx;
box-shadow: 0px 0px 10px 0px #a7bbe4;
.list_item{
position: relative;
transition: background-color 0.2s;
.action-menu{
padding: 15rpx;
width: 320rpx;
position: absolute;
top: 75rpx;
left: 0;
z-index: 100;
background: #fff;
border-radius: 8rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
overflow: hidden;
.menu-item{
padding: 20rpx;
border-bottom: 1rpx solid #f5f5f5;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 28rpx;
line-height: 36rpx;
}
.menu-item:last-child{
border-bottom: none;
}
.menu-item:active{
background-color: #f5f5f5;
}
}
.item_data{
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
font-size: 30rpx;
line-height: 50rpx;
padding: 20rpx 0;
border-bottom: 1rpx solid #e5dcdc;
.item_name{
width: 60%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.item_time{
color: #999;
font-size: 24rpx;
}
}
}
.list_item:last-child .item_data{
border-bottom: none;
}
}
.null_text{
display: block;
text-align: center;
font-size: 30rpx;
color: #999;
padding-top: 150rpx;
}
.edit_folder_name{
font-size: 30rpx;
line-height: 45rpx;
color: #5188e5;
}
.edit_folder_input{
margin-top: 20rpx;
input{
height: 70rpx;
line-height: 70rpx;
padding: 0 20rpx;
font-size: 26rpx;
color: #303030;
border-radius: 10rpx;
border: 1rpx solid #ddd;
box-sizing: border-box;
}
.custom-placeholder{
font-size: 26rpx;
}
}
.edit_folder_btn{
width: 50%;
margin: 25rpx auto 0;
background: #5188e5;
border-radius: 50rpx;
font-size: 26rpx;
color: #fff;
line-height: 70rpx;
}
.folder_popup{
.popup-content{
padding: 30rpx;
width: 550rpx;
background: #fff;
border-radius: 10rpx;
}
}
.mask{
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 99;
}
.add_folder_name{
font-size: 30rpx;
line-height: 45rpx;
color: #5188e5;
}
.add_folder_input{
margin-top: 20rpx;
input{
height: 70rpx;
line-height: 70rpx;
padding: 0 20rpx;
font-size: 26rpx;
color: #303030;
border-radius: 10rpx;
border: 1rpx solid #ddd;
box-sizing: border-box;
}
.custom-placeholder{
font-size: 26rpx;
}
}
.add_folder_btn{
width: 50%;
margin: 25rpx auto 0;
background: #5188e5;
border-radius: 50rpx;
font-size: 26rpx;
color: #fff;
line-height: 70rpx;
}
</style>

504
pages/folder/patient.vue Normal file
View File

@@ -0,0 +1,504 @@
<template>
<view class="content">
<z-nav-bar :title="name+'患者列表'" bgColor="#5188e5" fontColor="#fff"></z-nav-bar>
<view class="list_wrap">
<view class="list_content" v-if="list&&list.length>0">
<view v-if="showMask" class="mask" @click="closeMenu"></view>
<view class="list_item" v-for="(item, index) in list" :key="index"
@click="clickRecord(item, index)"
@longpress="handleLongPress(index)"
@touchstart="handleTouchStart(index)"
@touchend="handleTouchEnd">
<view class="item_img">
<image src="../../static/icon/icon_hz.png"></image>
<text>{{item.patientName}}</text>
</view>
<view class="item_content">
<view>西医诊断<text>{{item.diagnosis}}</text></view>
<view>助手类型<text>{{item.chatAssistantName}}</text></view>
<view>创建时间<text>{{item.createTime}}</text></view>
</view>
<!-- 长按弹窗菜单 -->
<view class="action-menu" v-if="activeIndex === index && showMenu">
<view class="menu-item" @tap.stop="renameItem(item)">
<text>重命名</text><uni-icons type="compose" size="18"></uni-icons>
</view>
<view class="menu-item" @tap.stop="moveItem(item)">
<text>移出到</text><uni-icons type="redo" size="18"></uni-icons>
</view>
<view class="menu-item" @tap.stop="deleteItem(item)">
<text style=" color: red;">删除</text><uni-icons type="trash" size="18" color="red"></uni-icons>
</view>
</view>
</view>
</view>
<text class="null_text" v-else>{{null_text}}</text>
</view>
<uni-popup ref="edit_folder" class="folder_popup">
<view class="popup-content">
<text class="edit_folder_name">重命名</text>
<view class="edit_folder_input">
<input type="text" v-model="patientName" placeholder="请输入患者名称" placeholder-class="custom-placeholder" />
</view>
<button class="edit_folder_btn" @click="update">保存</button>
</view>
</uni-popup>
<uni-popup ref="move_folder" class="folder_popup">
<view class="popup-content">
<text class="edit_folder_name">移出到病历夹</text>
<view class="edit_folder_list" style=" margin-top: 20rpx;">
<scroll-view scroll-y class="popup-scroll">
<view class="item_select" v-for="(item, index) in folderList" :key="index" @click="handleRadioClick(item)">
<radio :value="String(item.id)"
:checked="selectedItem.id === item.id" color="#5188e5"
:disabled="item.here === 1"
:style="item.here === 1 ? 'opacity: 0.8; pointer-events: none;' : ''"
/>
<text :style="item.here === 1 ? 'color: #999;' : ''">{{ item.folderName }}</text>
</view>
</scroll-view>
</view>
<button class="edit_folder_btn" @click="confirm">确定</button>
</view>
</uni-popup>
</view>
</template>
<script>
import $http from "@/config/requestConfig.js";
export default {
data() {
return {
id: null,
name: '',
list: [],
null_text: '',
activeIndex: -1,
showMenu: false, //是否显示菜单
showMask: false, //是否显示遮罩
patientId: null,
patientName: '',
chatAssistantId: '',
chatId: '',
folderList: [], //病历夹列表数据
selectedItem: {},
patientData: {}, //患者数据
isLongPress: false,
touchStartTime: 0,
}
},
onLoad(e) {
uni.hideTabBar();
this.id = e.id;
this.name = e.name;
},
onShow() {
this.getData();
},
methods: {
//病历夹获取数据
getFolderList(assistantId,chatId) {
uni.showLoading({
title: '加载中'
})
this.$http.request({
url: 'taihumed/aiRecordFolder/getRecordFolders',
method: "POST",
data: {
assistantId: assistantId,
chatId: chatId,
folderName: '',
patientName: ''
},
header: {
"Content-Type": "application/json",
},
})
.then(res=> {
if(res.code==0){
uni.hideLoading();
if(res.list&&res.list.length>0){
this.folderList = res.list;
}
}
});
},
//获取数据
getData() {
uni.showLoading({
title: '加载中'
})
this.$http.request({
url: 'taihumed/aiRecordFolder/getRecordFolderChats',
method: "POST",
data: {
folderId: this.id,
patientName: ''
},
header: {
"Content-Type": "application/json",
},
})
.then(res=> {
if(res.code==0){
uni.hideLoading();
if(res.list&&res.list.length>0){
this.list = res.list;
}else{
this.list = [];
this.null_text = '暂无数据';
}
}
});
},
//长按操作
handleLongPress(index) {
this.activeIndex = index;
this.showMenu = true;
this.showMask = true;
//标记长按模式
this.isLongPress = true;
},
//重命名
renameItem(data) {
this.chatAssistantId = data.chatAssistantId;
this.chatId = data.chatId;
this.patientName = data.name;
this.$refs.edit_folder.open('center');
this.closeMenu();
},
//保存
update(){
if(!this.patientName){
this.$commonJS.showToast("请输入患者名称");
return
}
uni.showLoading({
title: '正在保存'
})
this.$http.request({
url: 'taihumed/aiRecordFolder/updateRecordFolderChat',
method: "POST",
data: {
chatAssistantId: this.chatAssistantId,
chatId: this.chatId,
patientName: this.patientName,
},
header: {
"Content-Type": "application/json",
},
})
.then(res=> {
if(res.code==0){
uni.hideLoading();
uni.showToast({
title: '保存成功',
icon: 'success'
})
this.$refs.edit_folder.close();
this.getData();
}
});
},
//删除
deleteItem(data) {
this.closeMenu();
uni.showModal({
title: '提示',
content: '确定移除患者['+ data.patientName +']',
success: (res) => {
if (res.confirm) {
uni.showLoading({
title: '正在操作'
})
this.$http.request({
url: 'taihumed/aiRecordFolder/delRecordFolderChat',
method: "POST",
data: {
id: data.id
},
header: {
"Content-Type": "application/json",
},
})
.then(res=> {
if(res.code==0){
uni.hideLoading();
uni.showToast({
title: '操作成功',
icon: 'success'
})
this.getData();
}
});
}
}
});
},
//移入到其他病历夹
moveItem(data){
this.closeMenu();
this.$refs.move_folder.open('center');
console.log('患者数据:',data)
this.patientData = data; //移入需要数据
this.getFolderList(data.chatAssistantId, data.chatId);
},
//radio点击事件
handleRadioClick(data){
if (data.here === 1) return; //在此病历夹的禁止点击
this.selectedItem = data;
},
//移入-确定
confirm(){
//先操作移出
this.$http.request({
url: 'taihumed/aiRecordFolder/delRecordFolderChat',
method: "POST",
data: {
id: this.patientData.id
},
header: {
"Content-Type": "application/json",
},
})
.then(res=> {
if(res.code==0){
//再操作移入
this.joinFolder();
}
});
},
//移入
joinFolder(){
uni.showLoading({
title: '正在操作中'
})
this.$http.request({
url: 'taihumed/aiRecordFolder/addRecordFolderChat',
method: "POST",
data: {
folderId: this.selectedItem.id,
patientName: this.patientData.patientName,
chatAssistantId: this.patientData.chatAssistantId,
chatId: this.patientData.chatId
},
header: {
"Content-Type": "application/json",
},
})
.then(res=> {
if(res.code==0){
uni.hideLoading();
uni.showToast({
title: '操作成功',
icon: 'success'
})
this.$refs.move_folder.close();
this.getData();
}
});
},
//点击会话跳转到首页记录
clickRecord(item, index){
if (this.isLongPress) {
this.isLongPress = false; //重置状态
}
uni.setStorageSync('homeParams', { data: item, index: index, type: 'patient' });
uni.switchTab({
url: '/pages/home/index'
});
},
//触摸开始(防抖)
handleTouchStart(index) {
this.isLongPress = false;
},
//触摸结束
handleTouchEnd() {
const touchDuration = Date.now() - this.touchStartTime;
//如果触摸时间超过300ms认为是长按不触发点击
if (touchDuration > 300) {
this.isLongPress = true;
}
},
//关闭菜单
closeMenu() {
this.showMenu = false;
this.showMask = false;
this.activeIndex = -1;
},
},
}
</script>
<style lang="scss" scoped>
@import '@/static/mixin.scss';
.content{
height: 100%;
overflow: auto;
background-color: #fff;
}
.list_content{
margin: 20rpx 30rpx;
.list_item{
border: 1rpx solid $themeColor;
border-radius: 15rpx;
margin-bottom: 20rpx;
padding: 25rpx;
position: relative;
.item_img{
display: flex;
align-items: center;
image{
width: 80rpx;
height: 80rpx;
}
text{
font-size: 30rpx;
color: #333;
padding-left: 20rpx;
}
}
.action-menu{
padding: 15rpx;
width: 320rpx;
position: absolute;
top: 120rpx;
left: 25rpx;
z-index: 100;
background: #fff;
border-radius: 8rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
overflow: hidden;
.menu-item{
padding: 20rpx;
border-bottom: 1rpx solid #f5f5f5;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 28rpx;
line-height: 36rpx;
}
.menu-item:last-child{
border-bottom: none;
}
.menu-item:active{
background-color: #f5f5f5;
}
}
}
}
.item_content{
margin-top: 20rpx;
view{
display: flex;
align-items: center;
font-size: 28rpx;
color: #333;
line-height: 46rpx;
text{
color: #999;
width: 78%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
.null_text{
display: block;
text-align: center;
font-size: 30rpx;
color: #999;
padding-top: 150rpx;
}
.mask{
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 99;
}
.folder_popup{
.popup-content{
padding: 30rpx;
width: 550rpx;
background: #fff;
border-radius: 10rpx;
}
}
.edit_folder_name{
font-size: 30rpx;
line-height: 45rpx;
color: #5188e5;
}
.edit_folder_input{
margin-top: 20rpx;
input{
height: 70rpx;
line-height: 70rpx;
padding: 0 20rpx;
font-size: 26rpx;
color: #303030;
border-radius: 10rpx;
border: 1rpx solid #ddd;
box-sizing: border-box;
}
.custom-placeholder{
font-size: 26rpx;
}
}
.edit_folder_btn{
width: 50%;
margin: 25rpx auto 0;
background: #5188e5;
border-radius: 50rpx;
font-size: 26rpx;
color: #fff;
line-height: 70rpx;
}
.popup-scroll{
max-height: 325rpx;
.item_select{
display: flex;
align-items: center;
margin: 10rpx 0;
radio{
transform: scale(0.6);
/deep/.uni-radio-input{
}
}
text{
font-size: 28rpx;
margin-left: -8rpx;
}
}
}
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -20,7 +20,7 @@
太湖云医是一款以吴雄志老师独有的一以贯之的中医学针灸学与心学学术体系把精准医学思想引入传统医学将中西医有机整合医学与心理学有机整合同时将心理学与东方心学及国学的有机融合为患者及医生提供现代医学中医学与心理学的治疗预防及康复指导意见的一款app
</view>
<view style="text-align: right; padding-right: 20rpx; padding-top: 30rpx;">
<uni-link href="https://soulspace.taihumed.com/privacy.html" text="隐私政策"></uni-link>
<uni-link href="https://www.taimed.cn/privacy.html" text="隐私政策"></uni-link>
</view>
</view>
</template>

View File

@@ -22,6 +22,66 @@
<br clear="both" />
</view>
<view class="modal_vip" v-if="$platform=='android'">
<template>
<view class="chong_zhi boxShadow box_fillet vip_box">
<view class="noVip">
<view style="display: flex; align-items: center;">
<view class="zhanghu" style="text-align: left">
<image class="vip_image" src="@/static/icon/vip.png" mode="aspectFit"></image>
<text style="color: #fff; font-size: 60rpx; padding-left: 10rpx;" v-if="aiVipLog==null">VIP</text>
</view>
<view class="vip_block" v-if="aiVipLog!=null">
<view class="vip_infor_item">
<text v-if="aiVipLog.type==1">月度{{aiVipLog.count}}</text>
<text v-if="aiVipLog.type==2">季度{{aiVipLog.count}}</text>
<text>有效期到 {{aiVipLog.endTime.split(' ')[0]}})</text>
</view>
</view>
<view v-else class="vip_null">办理VIP畅享更多专属权益</view>
</view>
<view class="vip_btn" @click="onPageJump('/pages/vip/index?flag='+flag)">
<button v-if="flag==1">去升级</button>
<button v-if="flag==0">去办理</button>
</view>
</view>
</view>
</template>
</view>
<view class="recharge_block">
<view class="chong_zhi boxShadow box_fillet chongzhi_box">
<view class="chong_list">
<view class="left">
<view class="chong_list_item" @click="onPageJump('/pages/wallet/account')">
<view class="pay_item_img">
天医币
<u-icon @click.native.stop="openInfo()"
name="question-circle"
style="float: right; margin: 0 6rpx">
</u-icon>
</view>
<view class="text" style="line-height:30rpx; display: flex">
{{userMes.peanutCoin ? userMes.peanutCoin : 0}}
</view>
</view>
<view class="chong_list_item" @click="onPageJump('/pages/wallet/points')" v-if="$platform=='android'">
<view class="pay_item_img">
积分
</view>
<view class="text" style="line-height:30rpx">{{userMes.jf ? userMes.jf : 0}}</view>
</view>
</view>
<view class="chong_list_item" style="flex-direction:initial;margin-right:0;">
<view class="chong_btn" @click="onPageJump('/pages/wallet/recharge')"> </view>
</view>
</view>
</view>
</view>
<view class="list_box">
<view class="xiugai boxShadow box_fillet">
<common-list :dataList="pageList" @hancleClick="handleClickTab" label="name">
@@ -33,6 +93,28 @@
</view>
</view>
</view>
<u-popup :show="infoShow" mode="center" round="6" backgroundColor="#fff">
<view class="popup_box">
<view class="title">温馨提示</view>
<view class="content">
<view class="center">
&nbsp;&nbsp;&nbsp;&nbsp;天医币仅为我平台支付使用币种
一人民币=一天医币仅为了方便用户支付使用<br />天医币可以用于在我平台支付书籍或课程使用
<br />天医币这个名称是为适应我们平台的定位属性所起名称与区块链虚拟货币无任何关系
</view>
<view class="bottom">
<view class="button_box" style=" align-items: center; justify-content: center;">
<u-button style="width: 200rpx; float: center" :plain="true" text="知道啦" color="#9b9b9b"
size="small" @click="infoShow = false"></u-button>
</view>
</view>
</view>
</view>
</u-popup>
<z-navigation></z-navigation>
</view>
</template>
@@ -48,11 +130,21 @@ export default {
return {
userMes: {},
pageList: [
{
name: "我的病历夹",
url: "/pages/folder/index",
type: "pageJump",
},
{
name: "会话记录",
url: "/pages/my/recordsList",
type: "pageJump",
},
{
name: "我的订单",
url: "/pages/order/index",
type: "pageJump"
},
{
name: "个人资料",
url: "/pages/my/persData",
@@ -68,7 +160,10 @@ export default {
url: "/pages/user/workOrder",
type: "pageJump"
}
]
],
infoShow: false, // 显示电子书相关
aiVipLog: null,
flag: null
};
},
computed: {
@@ -80,8 +175,8 @@ export default {
},
//页面显示
onShow() {
uni.removeStorageSync('homeParams');
this.getData();
this.getVipData();
},
//方法
methods: {
@@ -105,7 +200,10 @@ export default {
break;
}
},
//点击天医币显示弹窗
openInfo() {
this.infoShow = true;
},
//获取个人信息
getData() {
if (this.userInfo.id != undefined) {
@@ -114,6 +212,23 @@ export default {
});
}
},
//获取数据
getVipData() {
this.$http.request({
url: 'taihumed/aiVip/getUserAiVip',
method: "POST",
data: {},
header: {
"Content-Type": "application/json",
},
})
.then(res=> {
if(res.code==0){
this.aiVipLog = res.aiVipLog;
this.flag = res.flag;
}
});
},
onPageJump(url) {
uni.navigateTo({
url: url,
@@ -257,17 +372,21 @@ export default {
.chong_list_item {
width: auto;
height: 100rpx;
margin-right: 60rpx;
text-align: center;
display: inline-block;
color: #294a97;
display: flex;
align-items: center;
flex-direction: column;
justify-content: space-between;
.text {
font-size: 30rpx;
display: block;
font-weight: 700;
color: #294a97 !important;
color: $themeColor;
padding-top: 20rpx;
}
}
@@ -275,7 +394,8 @@ export default {
font-size: 26rpx;
border-radius: 50rpx;
color: #fffbf6;
padding: 8rpx 32rpx;
line-height: 40rpx;
padding: 10rpx 30rpx;
background: $themeBgColor;
}
}
@@ -335,7 +455,7 @@ export default {
width: calc(100%);
height: 100%;
float: left;
padding: 20rpx 30rpx;
padding: 30rpx;
background: rgba(255, 255, 255, 0.65);
border-radius: 10rpx;
box-shadow: 0px 0px 5px 1px rgba(0, 82, 79, 0.1);
@@ -351,6 +471,9 @@ export default {
right: 30rpx;
}
}
.vip_block{
padding-left: 10rpx;
}
.mine_box {
width: 100%;
@@ -360,23 +483,17 @@ export default {
}
.list_box {
padding: 40rpx 0;
padding: 20rpx 0;
}
.pay_item_img {
display: flex;
color: #333;
align-items: center;
padding: 0;
box-sizing: border-box;
font-weight: bold;
margin: 0 auto;
margin-bottom: 10rpx;
color: #000;
}
.popup_box {
padding-bottom: 20rpx;
width: 88vw;
width: 80vw;
overflow: hidden;
position: relative;
height: auto;
@@ -386,15 +503,15 @@ export default {
font-weight: normal;
font-size: 46rpx;
color: $themeColor;
padding: 20rpx;
padding: 30rpx;
border-top-left-radius: 6px;
border-top-right-radius: 6px;
}
.content {
font-size: 26rpx;
font-size: 28rpx;
letter-spacing: 0.15rpx;
padding: 20rpx;
padding: 10rpx 30rpx 30rpx;
color: #3f3f3f;
.top {
@@ -407,8 +524,8 @@ export default {
.bottom {
width: 100%;
margin-top: 60rpx;
margin-top: 40rpx;
margin-bottom: 20rpx;
font-size: 24rpx;
line-height: 26rpx;
color: #b0b0b0;
@@ -432,8 +549,8 @@ export default {
}
.modal_vip{
margin-top: 50rpx;
padding: 20rpx 20rpx 0;
margin-top: 40rpx;
padding: 20rpx 30rpx 0;
height: auto;
display: flex;
align-items: center;
@@ -472,10 +589,8 @@ export default {
margin-top: 6rpx;
font-weight: normal;
font-size: 28rpx;
color: #000000;
// line-height: 46rpx;
color: #000;
text-align: center;
// color: #fff;
}
}
}
@@ -740,25 +855,51 @@ export default {
.vip_infor_item text{
color: #fff;
}
.vip_btn{
}
.vip_btn button{
background: none;
border: 2rpx solid #294a97;
border: 1rpx solid #5188e5;
border-radius: 40rpx;
font-size: 24rpx;
width: 82rpx;
line-height: 42rpx;
color: #294a97;
font-weight: bold;
color: #5188e5;
padding: 0 10rpx;
}
.vip_null{
color: #fff;
font-size: 26rpx;
line-height: 34rpx;
padding-left: 30rpx;
}
.expired{
opacity: 0.55;
}
.recharge_block{
padding: 20rpx 30rpx 0;
height: auto;
display: flex;
align-items: center;
justify-content: space-between;
}
.vip_box {
padding: 25rpx 0;
float: right;
position: relative;
width: 100%;
height: auto;
border-radius: 20rpx !important;
background: linear-gradient(90deg, #5188e5 0%, #accafb 80%);
.PM_font {
color: #476379;
}
.zhanghu {
font-size: 80rpx;
text-align: center;
line-height: 100%;
margin-bottom: 0;
}
}
</style>

View File

@@ -55,6 +55,7 @@ export default {
if(res.list&&res.list.length>0){
this.list = res.list;
}else{
this.list = [];
this.null_text = '暂无数据';
}
}

324
pages/order/index.vue Normal file
View File

@@ -0,0 +1,324 @@
<template>
<view class="content">
<z-nav-bar title="我的订单" bgColor="#5188e5" fontColor="#fff"></z-nav-bar>
<view class="order_block">
<view class="order_list" v-if="list.length>0">
<view class="order_item" v-for="(item, index) in list" :key="index" @click="goToDetail(item)">
<view class="order_orderSn">
{{item.orderSn}}
<u-tag @click="handleCopy(item.orderSn, '订单编号')" text="复制" plain style="float: right" size="mini" type="success"></u-tag>
</view>
<view class="item_top">
<text class="orderstatus" v-show="item.orderStatus == 0">未付款</text>
<text class="orderstatus" v-show="item.orderStatus == 1">待发货</text>
<text class="orderstatus" v-show="item.orderStatus == 2">已发货</text>
<text class="orderstatus" v-show="item.orderStatus == 3">交易成功</text>
<text class="orderstatus" v-show="item.orderStatus == 4">交易失败</text>
<text class="orderstatus" v-show="item.orderStatus == 5">已过期</text>
</view>
<view class="order_infor" v-if="item.orderType=='aiVip'||item.orderType=='upgradeAiVip'">
<view class="left">
<image src="../../static/icon/order_vip.png" mode="aspectFill"></image>
<text>{{item.aiBuyConfig.title}}<span>{{item.aiBuyConfig.count}}</span><span v-if="item.orderType=='upgradeAiVip'">VIP升级</span></text>
</view>
<view class="right">
<text v-if="item.orderType=='upgradeAiVip'&&item.districtMoney>0">{{Number(item.aiBuyConfig.fee)-Number(item.districtMoney)}}</text>
<text v-else>{{item.aiBuyConfig.fee}}</text>
</view>
</view>
<view class="order_price">实付款
<view class="left">
<text v-if="item.realMoney && item.realMoney > 0">
{{ item.realMoney }}
</text>
<text v-if="item.realMoney==0&&item.jfDeduction==0">
0
</text>
<text v-if="item.realMoney > 0 && item.jfDeduction > 0">
+
</text>
<text v-if="item.jfDeduction > 0">{{ item.jfDeduction }} 积分</text>
</view>
</view>
<text class="order_time">下单时间{{item.createTime}}</text>
<view class="order_statusbtn">
<text v-if="item.orderStatus == 0" @click.stop="goPay(item)">继续付款</text>
<text v-if="item.orderStatus == 3">申请售后</text>
</view>
</view>
</view>
<text class="null_text" v-else>{{null_text}}</text>
</view>
</view>
</template>
<script>
import $http from "@/config/requestConfig.js";
import { mapState, mapMutations } from "vuex";
import {setPay, setPayAssign, setWXPay} from "@/config/utils";
export default {
data() {
return {
list: [],
pagination: {
page: 1, //页码
limit: 20, //每页显示
total: 0, //总条数
},
null_text: '',
}
},
onLoad() {
this.getData();
},
onShow() {
},
computed: {
...mapState(["userInfo"]),
},
methods: {
//获取数据
getData() {
uni.showLoading({
title: '加载中'
})
var params = {
userId: this.userInfo.id,
come: '4',
orderStatus: 3,
...this.pagination
}
this.$http.request({
url: 'common/buyOrder/commonBuyOrderList',
method: "POST",
data: params,
header: {
"Content-Type": "application/json",
},
})
.then(res=> {
uni.hideLoading();
if(res.code==0){
if (res.data.records&&res.data.records.length>0) {
this.list = res.data.records;
}else{
this.list = [];
this.null_text = '暂无数据';
}
}
});
},
//复制
handleCopy(value, title) {
this.$commonJS.handleCopy(value, title);
},
//跳转
goToDetail(val){
uni.navigateTo({
url: "/pages/order/infor?orderId=" +
val.orderId +
"&orderType=" +
val.orderStatus +
"&orderSn=" +
val.orderSn,
});
},
//继续支付
goPay(payItem) {
if (payItem.paymentMethod == 2) {
console.log("阿里支付");
setPay({
typePay: "alipay",
subject: "order",
totalAmount: payItem.realMoney,
type: 2,
relevanceoid: payItem.orderSn,
customerId: this.userInfo.id,
},
(res) => {
if (res.success) {
uni.showToast({
title: "支付成功",
});
} else {
uni.showToast({
title: "支付失败",
icon: "none"
});
}
}
);
} else if (payItem.paymentMethod == 1) {
console.log("微信支付");
if (this.isAndorid == false) {
uni.showModal({
title: "提示",
content: "很抱歉,苹果系统暂不支持微信支付",
showCancel: false,
});
return false;
} else {
let data1 = {
orderSn: payItem.orderSn,
buyOrderId: null,
totalAmount: payItem.realMoney,
};
setWXPay(data1, (res) => {
if (res.success) {
uni.showToast({
title: "支付成功",
});
} else {
if (res.data.errMsg.indexOf("User canceled") != -1) {
uni.showToast({
title: "用户取消支付",
icon: "none"
});
} else {
uni.showToast({
title: "支付失败",
icon: "none"
});
}
}
});
}
} else if (payItem.paymentMethod == 3) {
// 苹果充值
console.log("苹果二次支付");
if (this.isAndorid) {
uni.showModal({
title: "提示",
showCancel: false,
content: "很抱歉,当前订单属于苹果系统内购订单,安卓系统无法完成支付操作,您可切换到苹果系统进行支付,也可以取消该订单,并重新下单",
});
} else {
console.log("进行苹果支付");
}
}
},
},
}
</script>
<style lang="scss" scoped>
@import '@/static/mixin.scss';
.content{
height: 100%;
overflow: auto;
background-color: #fff;
}
.order_block{
padding-bottom: 20rpx;
}
.order_list{
margin: 20rpx 30rpx;
border-radius: 10rpx;
background-color: #fff;
.order_item{
display: block;
width: 100%;
padding: 20rpx 30rpx;
box-shadow: 0px 0px 10px 0px #a7bbe4;
border-radius: 10rpx;
margin-bottom: 20rpx;
position: relative;
}
}
.order_orderSn{
font-size: 28rpx;
color: #999;
position: relative;
text{
}
}
.item_top{
position: absolute;
top: 75rpx;
right: 30rpx;
font-size: 24rpx;
font-weight: normal;
color: red;
}
.order_infor{
margin-top: 40rpx;
display: flex;
align-items: center;
justify-content: space-between;
.left{
display: flex;
align-items: center;
image{
width: 85rpx;
height: 85rpx;
}
text{
padding-left: 30rpx;
font-size: 30rpx;
font-weight: bold;
color: #333;
span{
color: red;
font-size: 28rpx;
}
}
}
}
.order_time{
display: block;
margin-top: 20rpx;
font-size: 26rpx;
color: #999;
}
.order_price{
margin-top: 30rpx;
line-height: 50rpx;
display: flex;
align-items: center;
justify-content: flex-end;
font-size: 28rpx;
.left{
padding-left: 10rpx;
font-size: 30rpx;
color: #333;
font-weight: bold;
}
}
.order_statusbtn{
position: absolute;
bottom: 20rpx;
right: 30rpx;
padding: 0 15rpx;
line-height: 45rpx;
font-size: 24rpx;
color: #999;
border: 1px solid #999;
border-radius: 30rpx;
}
.null_text{
display: block;
text-align: center;
font-size: 30rpx;
color: #999;
padding-top: 150rpx;
}
</style>

906
pages/order/infor.vue Normal file
View File

@@ -0,0 +1,906 @@
<template>
<view class="content">
<z-nav-bar title="订单详情" bgColor="#5188e5" fontColor="#fff"></z-nav-bar>
<view class="orderList" v-if="consigneeShow">
<view class="orderItem">
<view class="" style="position: relative; height: 60rpx">
<text
class="orderState orderState0"
v-if="orderContet.orderStatus == 0">待支付</text>
<text
class="orderState orderState1"
v-if="orderContet.orderStatus == 1">待发货</text>
<text
class="orderState orderState2"
v-if="orderContet.orderStatus == 2">待收货</text>
<text
class="orderState orderState3"
v-if="orderContet.orderStatus == 3">已完成</text>
<text
class="orderState orderState5"
v-if="orderContet.orderStatus == 5">已超时</text>
</view>
<view class="orderContent" v-if="orderContet.orderType == 'aiVip'||orderContet.orderType == 'upgradeAiVip'" style=" display: flex; align-items: center; justify-content: center;">
<image src="/static/icon/order_vip.png" mode="aspectFill" style="width: 100rpx; height: 100rpx;"></image>
<view class="itemJian">
<view class="orderTitle" style="line-height: 46rpx; margin-bottom: 0;">
{{ orderContet.aiBuyConfig.title }}<span style=" color: red;">{{ orderContet.aiBuyConfig.count }}<span v-if="orderContet.orderType=='upgradeAiVip'">VIP升级</span></span>
</view>
</view>
</view>
<view
class="orderContent" v-else-if="orderContet.orderType == 'point'">
<image
src="/static/icon/pay_3.png"
mode="aspectFill"
style="width: 100rpx; height: 100rpx"
></image>
<view class="itemJian">
<view class="orderTitle" style="line-height: 100rpx">
<text>充值 {{ orderContet.bookBuyConfigEntity.money }}天医币</text>
</view>
</view>
</view>
<view class="order_item">
<view class="orderallpri">
<span style="color: #666; margin-right: 10rpx; float: left">商品总价 :
</span>
<span></span>
<text v-if="orderContet.orderType=='upgradeAiVip'&&orderContet.districtMoney>0">{{Number(orderContet.aiBuyConfig.fee)-Number(orderContet.districtMoney)}}</text>
<text v-else>{{ orderContet.orderMoney }}</text>
</view>
<view class="orderReal" v-if="orderContet.jfDeduction > 0">
<span style="color: #666; margin-right: 10rpx; float: left">积分抵扣:</span>
<span style="color: red" v-if="orderContet.jfDeduction > 0">-</span>
<span style="color: red"> {{ orderContet.jfDeduction }}</span>
</view>
<view
class="orderReal"
v-if="orderContet.paymentMethod == 4 && orderContet.realMoney > 0"
>
<span style="color: #666; margin-right: 10rpx; float: left">天医币抵扣:</span>
<span style="color: red">-</span>
<span style="color: red">{{ orderContet.realMoney }}</span>
</view>
<view class="orderReal" v-if="orderContet.orderStatus != 0">
<span style="color: #666; margin-right: 10rpx; float: left">
实付款:
</span>
<b v-if="orderContet.orderType == 'point'" style="color: red"> {{ orderContet.bookBuyConfigEntity.realMoney }}</b>
<b style="color: red" v-else>
<template v-if="orderContet.realMoney">
{{ orderContet.realMoney }}
<text style="font-size: 24rpx;margin-left: 10rpx;">
<span
style=""
v-if="orderContet.paymentMethod == 1"
>微信支付</span
>
<span
style=""
v-if="orderContet.paymentMethod == 2"
>支付宝支付</span
>
<span
style=""
v-if="orderContet.paymentMethod == 3"
>苹果支付</span
>
<span
style=""
v-if="orderContet.paymentMethod == 4"
>
天医币支付
</span>
</text>
</template>
<template v-if="orderContet.realMoney == 0&&orderContet.jfDeduction==0">0</template>
<text
style="margin: 0 4rpx"
v-if="
orderContet.realMoney > 0 && orderContet.jfDeduction > 0
"
>
+
</text>
<text v-if="orderContet.jfDeduction > 0"
>{{ orderContet.jfDeduction }} 积分</text
>
</b>
</view>
<view class="orderReal" v-if="orderContet.remark">
<span style="color: #666; margin-right: 10rpx; float: left">
备注 :
</span>
<text style="color: #666; line-height: 36rpx; display: inline-block; max-width: 80%;">{{ orderContet.remark }}</text>
</view>
</view>
<view class="order_item">
<view class="orderYunf">
<span style="color: #666; float: left">订单编号 : </span>
<text style="font-size: 24rpx; color: #666">{{ orderContet.orderSn }}
</text>
<u-tag
@click="handleCopy(orderContet.orderSn, '订单编号')"
borderColor="#5188e5"
color="#5188e5"
text="复制"
plain
style="float: right; margin-left: 15rpx;"
size="mini"
type="success"
></u-tag>
</view>
<view class="orderReal" style=" width: 100%;
display: flex;
align-items: center;
justify-content: space-between;">
<span style="color: #666; margin-right: 10rpx; float: left">
创建时间 :
</span>
<text style="font-size: 24rpx; color: #666">{{orderContet.createTime}}</text>
</view>
<view
class="orderReal"
v-if=" orderContet.orderStatus >= 1 && orderContet.orderStatus != 5 &&orderContet.paymentDate">
<span style="color: #666; margin-right: 10rpx; float: left">
付款时间 :
</span>
<text style="font-size: 24rpx; color: #666">{{orderContet.paymentDate}}</text>
</view>
</view>
</view>
</view>
<view class="goods_nav_box">
<common-goods-nav
:iconList="iconList"
:customButton="customButton"
@submit="goBuyJie"
@clickIcon="clickIcon"
>
<!-- leftSlot -->
<template slot="leftSlot" slot-scope="slotProps">
<view
class="price_box order_bottom_box"
v-if="orderContet.orderStatus == 0"
>
<text class="price">
合计:
<text class="total">¥{{ orderContet.realMoney }}</text>
</text>
</view>
</template>
</common-goods-nav>
</view>
</view>
</template>
<script>
import $http from "@/config/requestConfig.js";
import { setPay, setPayAssign, setWXPay } from "@/config/utils";
import { mapState } from "vuex";
export default {
data() {
return {
iconList: [
{
text: "联系客服",
iconType: 1,
icon: "server-fill",
infoColor: "#666",
},
],
userRecordid: null, // 用户的评价状态
playData: {},
title: "Hello",
logisticsData: [], // 快递信息列表
titleStat: "",
orderID: 0,
orderSn: "",
orderType: "",
orderContet: {},
sheetList: [], // 面单数据
consigneeShow: false,
customButton: [],
};
},
onLoad(e) {
this.windowWidth = uni.getSystemInfoSync().windowWidth;
this.orderID = e.orderId;
this.orderType = e.orderType;
this.orderSn = e.orderSn;
},
onHide() {
this.sheetList = [];
},
onShow() {
this.getOrderList();
},
computed: {
...mapState(["userInfo"]),
},
methods: {
async goBuyJie(data) {
if (data.text == "继续付款") {
this.goPay(this.orderContet);
} else if (data.text == "取消订单") {
this.canceOrder();
}
},
//联系客服
async clickIcon(data) {
if (data.text == "联系客服") {
this.kefu();
}
},
//联系客服
kefu() {
uni.showModal({
title: "提示",
content: "微信号yilujiankangkefu",
});
},
//复制
handleCopy(value, title) {
this.$commonJS.handleCopy(value, title);
},
onPageJump(item) {
uni.navigateTo({
url: "./deliverDetail?objId=" + item,
});
},
//获取订单详情
getOrderList() {
this.$http
.request({
url: "common/buyOrder/commonOrderDetail",
method: "POST",
data: {
orderId: this.orderID,
},
header: {
"Content-Type": "application/json",
},
})
.then((res) => {
this.customButton = [];
this.orderContet = res.data.buyOrder;
this.consigneeShow = true;
if (
this.orderContet.orderStatus == 2 &&
this.sheetList.length > 0 &&
this.orderContet.orderStatus != 5
) {
this.customButton.push({
width: "160rpx",
text: "查看物流",
color: "#333",
backgroundColor: "#f0f0f0",
color: "#fff",
});
}
if (this.orderContet.orderStatus == 2) {
this.customButton.push({
width: "160rpx",
text: "确认收货",
color: "#fff",
});
}
if (this.orderContet.orderStatus == 0) {
this.customButton.push({
width: "160rpx",
text: "继续付款",
});
}
if (this.orderContet.orderStatus == 0) {
this.customButton.push({
width: "160rpx",
text: "取消订单",
color: "#333",
backgroundColor: "#f0f0f0",
});
}
if (this.orderContet.orderStatus == 0) {
this.titleStat = "待支付";
} else if (this.orderContet.orderStatus == 1) {
this.titleStat = "待发货";
} else if (this.orderContet.orderStatus == 2) {
this.titleStat = "待收货";
} else if (this.orderContet.orderStatus == 3) {
this.titleStat = "已完成";
}
if (
this.orderContet.orderStatus >= 2 &&
this.orderContet.orderType == "order" &&
this.orderContet.expressOrders
) {
this.sheetList = this.orderContet.expressOrders;
} else {
this.sheetList = [];
}
console.log(this.orderContet, "订单详情");
});
},
//取消订单
canceOrder() {
uni.showModal({
title: "提示",
content: "确定要取消订单吗?",
confirmText: "取消订单",
cancelText: "考虑一下",
confirmColor: "#c96713",
cancelColor: "#555",
success: (res) => {
if (res.confirm) {
this.$http
.post(
"book/buyOrder/appDelete?orderId=" + this.orderContet.orderId
)
.then((res) => {
uni.showToast({
icon: "none",
title: "取消订单成功",
});
uni.switchTab({
url: "./orderList",
});
});
}
},
});
},
//支付
goPay(payItem) {
if (payItem.paymentMethod == 2) {
console.log("阿里支付");
setPay(
{
typePay: "alipay",
subject: "order",
totalAmount: payItem.realMoney,
type: 2,
relevanceoid: payItem.orderSn,
customerId: this.userInfo.id,
},
(res) => {
if (res.success) {
uni.showToast({
title: "支付成功",
});
} else {
uni.showToast({
title: "支付失败",
icon: "none"
});
}
this.getOrderList();
}
);
} else if (payItem.paymentMethod == 1) {
console.log("微信支付");
if (this.isAndorid == false) {
uni.showModal({
title: "提示",
content: "很抱歉,苹果系统暂不支持微信支付",
showCancel: false,
});
return false;
} else {
let data1 = {
orderSn: payItem.orderSn,
buyOrderId: null,
totalAmount: payItem.realMoney,
};
console.log(data1, "data1");
setWXPay(data1, (res) => {
if (res.success) {
uni.showToast({
title: "支付成功",
});
} else {
console.log(res);
if (res.data.errMsg.indexOf("User canceled") != -1) {
uni.showToast({
title: "用户取消支付",
icon: "none"
});
} else {
uni.showToast({
title: "支付失败",
icon: "none"
});
}
}
});
}
} else if (payItem.paymentMethod == 3) {
// 苹果充值
console.log("苹果二次支付");
if (this.isAndorid) {
uni.showModal({
title: "提示",
showCancel: false,
content:
"很抱歉,当前订单属于苹果系统内购订单,安卓系统无法完成支付操作,您可切换到苹果系统进行支付,也可以取消该订单,并重新下单",
});
} else {
this.iphonepay(payItem);
}
}
},
},
};
</script>
<style lang="scss" scoped>
@import "@/static/mixin.scss";
@import "@/static/common.scss";
view,uni-view {
font-size: 28rpx;
}
.expresslist {
font-size: 28rpx;
color: #666;
.title {
text-align: center;
}
.item {
padding: 30rpx;
border-bottom: 1px solid #eee;
}
}
.orderState {
color: #fff;
position: absolute;
left: 0;
top: 0rpx;
padding: 6rpx 10rpx;
border-radius: 0 24rpx 24rpx 0;
font-size: 26rpx;
// font-weight: bold;
color: #fff;
}
.orderState0 {
background-color: #e6a23c;
}
.orderState1 {
background-color: #409eff;
}
.orderState2 {
background-color: #f56c6c;
}
.orderState3 {
background-color: #5188e5;
}
.orderState5 {
background-color: #787878;
}
.guoqi {
font-size: 28rpx;
align-items: center;
color: red;
float: right;
line-height: 40rpx;
}
.star {
display: inline-block;
width: 20px;
height: 20px;
margin-right: 10rpx;
}
.mb30 {
margin-bottom: 30rpx;
overflow: hidden;
}
.tanchu {
padding: 40rpx 30rpx 40rpx 30rpx;
position: relative;
.dp_title {
font-size: 32rpx;
margin-bottom: 50rpx;
color: #555;
text-align: center;
font-weight: bold;
}
.dp_add {
position: absolute;
top: 40rpx;
right: 30rpx;
font-size: 22rpx;
background-color: #fd6004;
color: #fff;
border-radius: 10rpx;
padding: 5rpx 10rpx;
.u-icon {
display: inline-block;
margin-right: 5rpx;
}
}
.addressItem {
border: 2px dashed #d9d9d9;
border-radius: 10rpx;
width: 100%;
display: flex;
padding: 20rpx 10rpx;
margin: 25rpx 0 0 0;
align-items: center;
background-color: #fff;
.addrContent {
margin-left: 40rpx;
flex: 1;
.addrContentTop {
display: flex;
align-items: flex-end;
margin: 0 0 15rpx 0;
position: relative;
.userName {
font-size: 35rpx;
font-weight: bold;
margin-right: 30rpx;
}
.userTel {
font-size: 25rpx;
color: #888;
}
.userMoren {
border: 1px solid #fd6004;
color: #fd6004;
padding: 3rpx 10rpx;
font-size: 22rpx;
border-radius: 10rpx;
margin: 0 0 0 20rpx;
}
.chooseCheck {
position: absolute;
top: 3rpx;
right: 6rpx;
}
}
.addrContentBottom {
font-size: 32rpx;
}
}
}
.addressItem.addItem_style {
border-color: #fd6004;
}
.youhuiItem {
border: 1px solid #d9d9d9;
border-radius: 10rpx;
width: 100%;
display: flex;
padding: 20rpx 10rpx;
margin: 25rpx 0 0 0;
align-items: center;
background-color: #fff;
font-size: 30rpx;
}
.youhuiItem > view {
float: left;
}
.youhuiItem.youItem_style {
border-color: #fd6004;
}
}
.opShou {
border-color: $uni-color-success !important;
}
.adDefault {
padding: 30rpx 50rpx 30rpx 100rpx;
background-color: #fff;
border-top: 1px solid #eee;
border-bottom: 2px dashed #b2e9d7;
margin: 0 0 5rpx 0;
position: relative;
.defalTop {
.userName {
font-size: 35rpx;
font-weight: bold;
margin-right: 30rpx;
}
.userTel {
font-size: 25rpx;
color: #888;
}
}
.defalBottom {
.userAddress {
font-size: 32rpx;
line-height: 40rpx;
}
}
.defalLeft {
position: absolute;
left: 30rpx;
top: 55rpx;
}
}
.orderList {
margin: 30rpx;
box-shadow: 0px 0px 10px 0px #a7bbe4;
border-radius: 10rpx;
.orderItem {
padding: 30rpx 0rpx;
background-color: #fff;
border-radius: 10rpx;
margin-bottom: 30rpx;
.orderContent {
padding: 20rpx;
margin-bottom: 20rpx;
image {
width: 150rpx;
height: 180rpx;
margin-right: 20rpx;
float: left;
}
.goods_info {
float: left;
width: calc(100% - 180rpx) !important;
justify-content: space-between;
}
.itemJian {
width: 100%;
justify-content: space-between;
.orderTitle {
font-weight: bold;
font-size: 30rpx;
margin: 0 0 20rpx 0;
float: left;
width: 540rpx;
}
.orderPrice {
font-size: 28rpx;
float: right;
width: 60rpx;
text-align: right;
line-height: 40rpx;
}
}
}
.orderallpri {
text-align: right;
margin: 10rpx 0;
font-size: 28rpx;
}
.orderReal {
text-align: right;
font-size: 28rpx;
margin: 10rpx 0;
overflow: hidden;
}
.orderYunf {
text-align: right;
font-size: 28rpx;
}
.orderOper {
text-align: right;
margin: 40rpx 20rpx 0 0;
view {
margin-left: 20rpx;
padding: 10rpx 0;
display: inline-block;
width: 160rpx;
font-size: 25rpx;
text-align: center;
}
.opFix {
color: #555;
border: 1px solid #ddd;
border-radius: 30rpx;
}
.opCan {
color: #c96713;
border: 1px solid #eba00b;
border-radius: 30rpx;
}
.kefu {
float: left;
}
}
}
}
.copyCode {
display: inline-block;
margin-left: 20rpx;
}
.deliverCntent {
padding: 32rpx;
position: relative;
background-color: #fff;
margin-bottom: 20rpx;
border-top: 1px solid #eee;
font-size: 28rpx;
}
.flexbox {
display: flex;
}
.img_icon {
padding-right: 5px;
}
.moreBtnF {
align-items: center;
padding: 30rpx;
height: 100%;
position: absolute;
right: 0;
top: 0;
z-index: 1;
background-color: rgba(255, 255, 255, 0.9);
}
.wuliu {
.time {
color: #888;
}
.moreBtn {
display: inline-block;
width: 50px;
}
}
.address_box {
background-color: #fff;
display: flex;
align-items: center;
.order_top {
background-color: #fff;
}
.user_info {
width: 100%;
float: left;
display: flex;
align-items: center;
.name {
color: #838282;
margin-right: 10rpx;
}
.tel {
color: #838282;
}
}
.curriulum_title {
width: auto;
font-size: 30rpx;
font-weight: 700;
float: left;
margin-right: 10rpx;
color: #333;
line-height: 40rpx;
letter-spacing: 2rpx;
}
.normal_box {
margin-left: 5rpx;
width: calc(100% - 120rpx);
}
.goods_box {
padding: 20rpx 20rpx;
display: flex;
align-items: center;
}
.rightArrow {
margin-left: 40rpx;
width: 40rpx;
height: 40rpx;
}
}
.feng {
background-color: #fafafa;
margin: 0rpx 22rpx 0 0;
height: 140rpx !important;
width: 140rpx !important;
float: left;
border-radius: 14rpx;
// border: 1rpx solid #e9e9e9;
}
.booknameleft {
font-size: 32rpx;
width: calc(100% - 100rpx);
color: #070707;
letter-spacing: 0.5rpx;
font-weight: 600;
}
.order_item {
padding: 20rpx 20rpx;
border-top: 1px solid #eee;
}
/deep/.goods_nav_box {
.left {
margin-top: -10rpx;
width: auto;
height: auto;
position: relative;
// overflow: hidden;
}
}
.delisted {
background-color: red;
color: #fff;
// padding: 2rpx 4rpx;
position: absolute;
border-radius: 4rpx;
font-size: 22rpx;
width: 100rpx;
text-align: center;
height: 40rpx;
line-height: 40rpx;
left: calc((100% - 100rpx) / 2);
top: calc((100% - 40rpx) / 2);
}
.order_bottom_box {
margin-left: 20rpx;
.number {
font-size: 28rpx;
margin-right: 15rpx;
margin-top: 5rpx;
}
.price {
.total {
font-size: 38rpx;
color: red;
}
}
}
.vip_year{
padding-left: 20rpx;
color: #f5342b;
font-size: 30rpx;
}
</style>

View File

@@ -30,6 +30,10 @@
<text>预约信息</text>
<span style="white-space: pre-line;">{{taihuTalent.reservation.replace('电话预约:', '电话预约:\n')}}</span>
</view>
<view class="taihu_common">
<text>所属地域</text>
{{taihuTalent.region}}
</view>
<view class="taihu_common">
<text>太湖证书</text>
<view class="certificate-list" v-if="certificates.length>0">

View File

@@ -1,7 +1,67 @@
<template>
<view class="content">
<z-nav-bar title="太湖英才" bgColor="#5188e5" fontColor="#fff"></z-nav-bar>
<view class="talents_list">
<z-nav-bar title="太湖英才" bgColor="#5188e5" fontColor="#fff" :backState="2000"></z-nav-bar>
<view class="talents_module" :style="`top: ${45 + statusBarHeight}px;`">
<view class="talents_tab">
<view class="tab_item" @click="toggleFilter('city')">
地区
<text :class="['arrow', activeFilter === 'city' ? 'up' : 'down']"></text>
</view>
<view class="tab_item" @click="toggleFilter('department')">
科室
<text :class="['arrow', activeFilter === 'department' ? 'up' : 'down']"></text>
</view>
</view>
<!-- 弹窗 -->
<view class="talents_tan" v-if="activeFilter">
<view class="tan_item tan_city" v-if="activeFilter=='city'">
<scroll-view scroll-y="true" class="city_scroll scroll_left" :class="!cityStatus?'width50':''" :scroll-into-view="'country_' + selectedPath[0]">
<view class="city_item"
:class="countryIndex==index?'active':''"
v-for="(item,index) in country" :key="index" :id="'country_' + item.areaId" @click="click_country(item,index)">
<text>{{item.title}}</text>
</view>
</scroll-view>
<scroll-view scroll-y="true" class="city_scroll scroll_center" :class="!cityStatus?'width50':''" :scroll-into-view="'province_' + selectedPath[1]">
<view class="city_item"
:class="provinceIndex==index?'active':''"
v-for="(item,index) in province" :key="index" :id="'province_' + item.provId" @click="click_province(item,index)">
<text>{{item.provName}}</text>
</view>
</scroll-view>
<scroll-view scroll-y="true" class="city_scroll scroll_right" v-if="cityStatus" :scroll-into-view="'city_' + selectedPath[2]">
<view class="city_item"
:class="cityIndex==index?'active':''"
v-for="(item,index) in city" :key="index" :id="'city_' + item.cityId" @click="click_city(item,index)">
<text>{{item.cityName}}</text>
</view>
</scroll-view>
</view>
<view class="tan_item tan_department" v-if="activeFilter=='department'">
<view class="department_item"
:class="departmentIndex==index?'department_active':''"
v-for="(item,index) in chatAssistants" :key="index" @click="click_department(item.name,index)">
<text>{{item.name}}</text>
</view>
</view>
</view>
<view class="name_search" v-if="!activeFilter">
<uni-easyinput
v-model="name"
prefixIcon="search"
placeholder="按姓名搜索"
placeholderClass="name-placeholder"
class="center-input"
/>
<button @click="getData()">查询</button>
</view>
</view>
<!-- 遮罩层 -->
<view v-if="activeFilter" class="overlay" @click="closeFilter"></view>
<view class="talents_list" v-if="list&&list.length>0">
<view class="talents_item" v-for="(item,index) in list" :key="index" @click="goToDetail(item.id)">
<image :src="item.icon" class="item_image" mode="aspectFit"></image>
<view class="item_right">
@@ -13,6 +73,7 @@
</view>
</view>
</view>
<text class="null_text" v-else>{{null_text}}</text>
<z-navigation></z-navigation>
</view>
</template>
@@ -23,15 +84,35 @@ export default {
data() {
return {
name: '',
region: '',
list: []
region: '', //地区
department: '', //科室
list: [],
null_text: '',
departmentIndex: null,
activeFilter: '',
chatAssistants: [], //科室数据
country: [], //国家数据
province: [], //省份数据
city: [], //城市数据
countryIndex: 0,
provinceIndex: null,
cityIndex: null,
countryTitle: '',
provinceTitle: '',
cityTitle: '',
//城市模块是否显示
cityStatus: false,
selectedPath: [], //存储选中路径
}
},
onLoad() {
uni.hideTabBar();
this.getAllBaseArea();
this.getChatAssistants();
},
onShow() {
uni.removeStorageSync('homeParams');
this.getData();
},
methods: {
@@ -45,7 +126,8 @@ export default {
method: "POST",
data: {
name: this.name,
region: this.region
region: this.region,
department: this.department
},
header: {
"Content-Type": "application/json",
@@ -55,15 +137,165 @@ export default {
uni.hideLoading();
if (res.list&&res.list.length>0) {
this.list = res.list;
}else{
this.list = [];
this.null_text = '暂无数据';
}
});
},
//获取科室数据
getChatAssistants() {
this.$http.request({
url: 'common/ragFlowApi/getChatAssistants',
method: "POST",
data: {},
header: {
"Content-Type": "application/json",
},
})
.then(res=> {
if (res.list&&res.list.length>0) {
this.chatAssistants = res.list;
}
})
},
//获取国家
getAllBaseArea() {
this.$http.request({
url: 'common/baseArea/getAllBaseArea',
method: "POST",
data: {},
header: {
"Content-Type": "application/json",
},
})
.then(res=> {
if (res.baseAreas&&res.baseAreas.length>0) {
this.country = res.baseAreas;
//如果第一位是中国默认请求
if(this.country[0].code=='86'){
this.countryTitle = this.country[0].title;
this.getProvinceList();
}
}
})
},
//获取中国省份
getProvinceList() {
this.$http.request({
url: 'common/province/getProvinceList',
method: "POST",
data: {},
header: {
"Content-Type": "application/json",
},
})
.then(res=> {
if (res.provinceList&&res.provinceList.length>0) {
this.province = res.provinceList;
}
})
},
//获取城市
getCityList(provId) {
this.$http.request({
url: 'common/province/getCityList?provId='+provId,
method: "POST",
data: {},
header: {
"Content-Type": "application/json",
},
})
.then(res=> {
if (res.prov&&res.prov.length>0) {
this.city = res.prov;
this.cityStatus = true;
}
})
},
//点击国家
click_country(data, index){
this.provinceIndex = null;
this.cityIndex = null;
this.city = [];
this.region = '';
this.selectedPath = [data.areaId];
//只有中国有二级,如果没有二级则为海外国家
if(data.code!='86'){
this.cityStatus = false;
if(this.countryTitle == data.title){
this.countryIndex = null;
this.provinceIndex = null;
}else{
this.province = [];
this.countryIndex = index;
this.countryTitle = data.title;
this.region = data.title;
}
this.activeFilter = '';
this.getData();
}else{
this.countryIndex = index;
this.countryTitle = data.title;
this.getProvinceList();
}
console.log(this.countryIndex, this.countryTitle)
},
//点击省份
click_province(data, index){
this.provinceIndex = index;
this.cityIndex = null;
this.provinceTitle = data.provName;
this.selectedPath = [this.selectedPath[0], data.provId];
console.log(this.provinceTitle)
this.getCityList(data.provId);
},
//点击城市
click_city(data, index){
this.selectedPath = [this.selectedPath[0], this.selectedPath[1], data.cityId];
if(this.cityTitle == data.cityName){
this.cityIndex = null;
this.cityTitle = '';
this.region = '';
}else{
this.cityIndex = index;
this.cityTitle = data.cityName;
this.region = this.provinceTitle + this.cityTitle;
}
this.activeFilter = '';
this.getData();
console.log(this.region)
},
//详情
goToDetail(id){
uni.navigateTo({
url: '/pages/talents/detail?id='+id,
});
},
//点击
toggleFilter(type) {
this.activeFilter = this.activeFilter === type ? '' : type;
},
//关闭
closeFilter() {
this.activeFilter = '';
},
//点击科室类别
click_department(name, index){
if(this.department==name){
this.departmentIndex = null;
this.department = ''
}else{
this.departmentIndex = index;
this.department = name;
}
this.activeFilter = '';
this.getData();
}
},
}
</script>
@@ -76,7 +308,8 @@ export default {
background-color: #fff;
}
.talents_list{
margin: 20rpx 30rpx;
margin: 185rpx 30rpx 20rpx;
padding-bottom: 120rpx;
}
.talents_item{
border: 1rpx solid $themeColor;
@@ -121,4 +354,162 @@ export default {
overflow: hidden;
text-overflow: ellipsis;
}
.null_text{
display: block;
text-align: center;
font-size: 30rpx;
color: #999;
padding-top: 300rpx;
}
.talents_module{
width: 100%;
position: fixed;
z-index: 999;
top: 0;
left: 0;
}
.talents_tab {
width: 100%;
height: 80rpx;
display: flex;
justify-content: space-around;
background: #f3f3f3;
}
.tab_item {
display: flex;
align-items: center;
}
.arrow {
display: inline-block;
width: 0;
height: 0;
border-left: 10rpx solid transparent;
border-right: 10rpx solid transparent;
margin-left: 10rpx;
}
.arrow.down {
border-top: 10rpx solid #666;
}
.arrow.up {
border-bottom: 10rpx solid #666;
}
.overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
z-index: 9;
}
.talents_tan{
width: 100%;
background: #fff;
font-size: 28rpx;
.tan_department{
display: flex;
flex-wrap: wrap;
padding-bottom: 1rpx;
.department_item{
display: flex;
justify-content: center;
width: 50%;
height: 85rpx;
line-height: 85rpx;
border-bottom: 1rpx solid #f3f3f3;
border-right: 1rpx solid #f3f3f3;
text{
color: #333;
}
}
.department_item:nth-of-type(2n){
border-right: 0;
}
.department_item:nth-last-child(-n+2){
border-bottom: 0;
}
.department_active{
text{
color: $themeColor;
font-weight: bold;
}
}
}
.tan_city{
display: flex;
align-items: center;
.city_scroll{
width: 33.3%;
height: 395rpx;
}
.scroll_left,.scroll_center{
border-right: 1rpx solid #f3f3f3;
box-sizing: border-box;
}
.width50{
width: 50% !important;
}
}
}
.city_item{
padding-left: 20rpx;
line-height: 78rpx;
border-bottom: 1rpx solid #f3f3f3;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.city_item:last-child{
border-bottom: 0;
}
.active{
text{
color: $themeColor;
font-weight: bold;
}
}
.name_search{
background-color: #fff;
padding: 20rpx 30rpx;
display: flex;
align-items: center;
/deep/.is-input-border{
background-color: #f3f3f3;
border-radius: 50rpx;
height: 60rpx;
line-height: 30rpx;
padding: 15rpx;
font-size: 28rpx;
color: #666;
}
/deep/.uni-easyinput__content-input{
}
.name-placeholder{
font-size: 28rpx;
text-align: center;
color: #666;
}
button{
background-color: $themeBgColor;
font-size: 26rpx;
line-height: 36rpx;
border-radius: 15rpx;
color: #fff;
padding: 5rpx 20rpx;
margin-left: 15rpx;
}
}
</style>

View File

@@ -73,8 +73,7 @@
<span class="highlight" @click="yszcShow = true">隐私协议</span>
</view>
<view class="btn_box">
<button @click="onSubmit" v-if="btnShow"> </button>
<button v-else> </button>
<button @click="onSubmit"> </button>
</view>
<view class="loginHelp" v-if="submitClickNum > 0">
<text>登录遇到问题</text><text class="link"
@@ -129,10 +128,8 @@ export default {
codeText: "获取验证码",
//验证码已发
readonly: false,
btnShow: true,
agree: false,
isIos: true,
isWeixin: true,
system: 13,
clearTime: null,
HealthOpen: false,
@@ -168,10 +165,6 @@ export default {
this.isIos = plus.os.name == "iOS";
let systemInfo = uni.getSystemInfoSync();
this.system = parseFloat(systemInfo["system"].replace(/[a-zA-Z]/g, ""));
this.isWeixin = plus.runtime.isApplicationExist({
pname: "com.tencent.mm",
action: "weixin://",
});
this.type = 2000;
this.brand = 3000;
@@ -205,19 +198,6 @@ export default {
this.loginForm.quCode = this.quCodeList[0].value;
this.brand = val;
},
closeMusic() {
this.$music.setCloseBgm(); // 关闭音频
uni.setStorage({
key: "playVisible",
data: false,
success: function() {
console.log("success");
},
});
this.setUserInfo({
playVisible: false,
});
},
// 获取国家区域编码
getCountyCode() {
this.$http
@@ -270,13 +250,6 @@ export default {
quChange(e) {
console.log(e, "e");
},
// 修改密码
resetPassWord() {
uni.navigateTo({
url: "",
});
},
onPageJump(url, name) {
if(name){
uni.navigateTo({
@@ -380,24 +353,6 @@ export default {
});
}
},
loginAPP() {
this.$http.post(this.urlList.apiLogin, this.appForm).then((res) => {
this.$store.commit("setUserInfo", res.obj);
uni.setStorageSync("token", res.obj.token);
uni.setStorageSync("customerOid", res.obj.customerOid);
this.setUserInfo(res.obj);
uni.showToast({
title: "登录成功",
duration: 1000,
});
setTimeout(() => {
uni.reLaunch({
url: "/pages/home/index",
});
}, 1000);
});
return;
},
// 手机密码登录
async onSubmit() {
if (!this.agree) {
@@ -514,188 +469,7 @@ export default {
});
}
},
// 一路健康APP登录进入
onSubmit_Health() {
if (!this.health_phone) {
this.$commonJS.showToast("请输入一路健康账号");
return;
}
if (!this.health_password) {
this.$commonJS.showToast("请输入密码");
return;
}
let healthData = {};
healthData.phone = this.health_phone;
healthData.password = this.health_password;
this.$http.post("book/user/getEverhealthInfo", healthData).then((res) => {
this.setHealthMes(res.everhealthInfo);
if (res.userInfo) {
res.userInfo.token = res.token.token;
this.setUserInfo(res.userInfo);
uni.showToast({
title: "登录成功",
});
setTimeout(() => {
uni.reLaunch({
url: "/pages/home/index"
});
}, 500);
} else {
setTimeout(() => {
uni.navigateTo({
url: "",
});
}, 600);
uni.showToast({
title: "账号验证成功",
duration: 600,
});
}
});
},
// 一路健康APP登录
onHealthLogin() {
if (!this.agree) {
this.$commonJS.showToast("请先同意《用户协议》和《隐私协议》");
return;
}
this.HealthOpen = true;
},
// 微信APP登录
onWxAppLogin() {
uni.login({
provider: "weixin",
success: (res) => {
uni.getUserInfo({
success: (info) => {
this.userInfo = info.userInfo;
if (res.authResult.openid && res.authResult.unionid) {
this.$http
.post("api/open/v1/login", {
wxAppOpenId: res.authResult.openid,
unionid: res.authResult.unionid,
nickname: this.userInfo.nickName,
headImg: this.userInfo.avatarUrl,
})
.then((data) => {
this.setUserInfo({
openId: res.authResult.openid,
unionid: res.authResult.unionid,
...data,
});
if (data.thirdLoginSuccess) {
socket.init();
uni.showToast({
title: "登录成功",
});
setTimeout(() => {
uni.reLaunch({
url: "/pages/home/index"
});
}, 500);
} else {
uni.showModal({
title: "提示",
content: "您还未绑定手机号,请先绑定~",
confirmText: "去绑定",
cancelText: "再逛会",
success: (res) => {
if (res.confirm) {
uni.redirectTo({
url: "",
});
}
},
});
}
});
} else {
uni.showToast({
title: "点击无效,请再次点击",
icon: "none",
});
}
},
fail: () => {
console.log("未授权");
},
});
},
fail(err) {
console.log(err);
},
});
},
// 苹果登录
onAppleLogin() {
uni.login({
provider: "apple",
success: (loginRes) => {
uni.getUserInfo({
provider: "apple",
success: (userInfoRes) => {
if (userInfoRes.userInfo.identityToken) {
this.$http
.post("api/open/v1/ios_login", {
identityToken: userInfoRes.userInfo.identityToken,
})
.then((data) => {
this.setUserInfo(data);
if (data.thirdLoginSuccess) {
socket.init();
uni.showToast({
title: "登录成功",
duration: 2000,
});
setTimeout(() => {
uni.reLaunch({
url: "/pages/home/index"
});
}, 500);
} else {
uni.showModal({
title: "提示",
content: "您还未绑定手机号,请先绑定~",
confirmText: "去绑定",
cancelText: "再逛会",
success: (res) => {
if (res.confirm) {
uni.redirectTo({
url: "",
});
}
},
});
}
});
}
},
});
},
fail: (err) => {
uni.showToast({
title: "登录失败",
icon: "none",
});
},
});
},
},
//页面隐藏
onHide() {},
//页面卸载
onUnload() {},
//页面下来刷新
onPullDownRefresh() {},
//页面上拉触底
onReachBottom() {},
};
</script>
<style lang="scss" scoped>

1043
pages/vip/index.vue Normal file

File diff suppressed because it is too large Load Diff

494
pages/wallet/account.vue Normal file
View File

@@ -0,0 +1,494 @@
<template>
<view class="wallet_wrap">
<z-nav-bar title="我的账户" bgColor="#5188e5" fontColor="#fff"></z-nav-bar>
<u-alert
type="warning"
size="20rpx"
:title="goBuyTitle"
:show-icon="true"
>
</u-alert>
<view class="ACTable">
<view class="ACTable_block">
<view>
<view
class="AC_chong PM_font"
@click="onPageJump('/pages/wallet/recharge')"
>立即充值
<u-icon
name="arrow-right"
color="#fff"
size="16"
class="rightArrow"
></u-icon>
</view>
</view>
<view>
<view class="AC_con">
<common-list
:dataList="MoneyRecord"
isCondition="true"
isNoIcon="true"
label="orderType"
@hancleClick="goClick"
>
<template slot="labelSlot" slot-scope="slotProps">
<view class="label_content AC_List">
<view style=" display: flex; align-items: center; justify-content: space-between;">
<view class="left">
<view class="title" v-if="slotProps.row.orderType=='购买商品'&&slotProps.row.productName">{{ slotProps.row.orderType }} <br/> {{ slotProps.row.productName }}</view>
<view class="title" v-else>{{ slotProps.row.orderType }}</view>
</view>
<view class="right Hot">
<text v-if="slotProps.row.changeAmount > 0">+</text>
<text>{{ slotProps.row.changeAmount }}</text>
</view>
</view>
<view class="AC_mark" v-if="slotProps.row.remark">{{slotProps.row.remark}}</view>
<view class="AC_time">{{ slotProps.row.createTime }}</view>
</view>
</template>
</common-list>
</view>
</view>
</view>
<view>
<u-back-top
:scroll-top="scrollTop"
bottom="60"
:customStyle="bgiStyle"
:iconStyle="iconStyle"
>
</u-back-top>
</view>
</view>
</view>
</template>
<script>
import $http from "@/config/requestConfig.js";
import { mapState } from "vuex";
export default {
data() {
return {
goBuyTitle:'【天医币】仅为我平台支付使用币种 。一人民币=一天医币,仅为了方便用户支付使用。【天医币】可以用于在我平台支付书籍或课程使用。【天医币】这个名称是为适应我们平台的定位属性,所起名称。与区块链虚拟货币无任何关系。',
playData: {},
options: {},
urlList: {
list: "common/transactionDetails/getTransactionDetailsList",
},
cardList: [],
couponListTab: 0,
MoneyRecord: [],
userMes: {},
scrollTop: 0,
status: 3,
totalPage: 0,
totalCount: 0,
tab_muJian: 0,
bgiStyle: {
background: "#7dc1f0",
},
iconStyle: {
fontSize: "40rpx",
fontWeight: "bold",
color: "#fff",
},
};
},
// 返回顶部
onPageScroll(e) {
this.scrollTop = e.scrollTop;
},
//第一次加载
onLoad(options) {
this.options = options;
},
computed: {
...mapState(["userInfo"]),
},
//页面显示
onShow() {
this.getData();
},
//方法
methods: {
//列表跳转到详情
goClick(data){
if(data.relationId){
uni.navigateTo({
url: "/pages/order/infor?orderId=" + data.relationId
});
}
},
//获取数据
getData() {
var data = {
userId: this.userInfo.id,
};
if (!this.iosHide) {
this.tab_muJian = 1;
}
//用户详情
if (this.userInfo.id != undefined) {
this.$http.post("book/user/info/" + this.userInfo.id).then((res) => {
this.userMes = res.user;
});
}
uni.showLoading({
title: '加载中'
})
$http.request({
url: this.urlList.list,
method: "POST",
data,
header: {
"Content-Type": "application/json",
},
})
.then((res) => {
uni.hideLoading();
this.MoneyRecord = res.transactionDetailsList;
});
},
// 跳转
onPageJump(url) {
uni.navigateTo({
url: url,
});
},
},
};
</script>
<style lang="scss" scoped>
@import "@/static/mixin.scss";
.wallet_wrap{
background-color: #eff5f8;
height: 100%;
}
.ACTable_block{
padding: 0 30rpx;
}
.ACTable {
margin-top: 20rpx;
.AC_mes {
width: 100%;
height: 400rpx;
padding: 160rpx 160rpx 100rpx 120rpx;
box-shadow: 0 0px 10px 1px #d3d1d133;
border-radius: 15rpx;
margin-bottom: 40rpx;
position: relative;
.wallet_title {
color: #fff;
font-size: 62rpx;
line-height: 80rpx;
font-weight: 600;
text-align: center;
}
.wallet_number {
color: #fff;
font-size: 70rpx;
line-height: 100rpx;
font-weight: 500;
text-align: center;
}
}
.AC_con {
background-color: #fff;
overflow: hidden;
box-shadow: 0 0px 10px 1px #d3d1d133;
border-radius: 0 0 15rpx 15rpx;
font-size: 30rpx;
.AC_jilu {
font-size: 42rpx;
text-align: left;
color: #294a97;
padding: 30rpx 20rpx 20rpx;
}
.AC_List {
overflow: hidden;
.left {
width: calc(100% - 140rpx) !important;
font-weight: 700;
float: left;
color: #333;
font-size: 32rpx;
line-height: 60rpx;
.title{
line-height: 48rpx;
}
}
.right {
display: block;
float: right;
text-align: right;
font-size: 32rpx;
font-weight: 700;
color: #333;
}
.AC_title {
font-size: 32rpx;
margin-bottom: 20rpx;
view {
float: right;
font-size: 34rpx;
font-weight: bold;
}
}
.AC_mark {
width: 100%;
font-size: 28rpx;
margin-top: 20rpx;
color: #888;
}
.AC_time {
color: #bababa;
font-size: 28rpx;
}
}
}
.couponList {
view {
display: inline-block;
padding: 0 0 25rpx 0;
margin: 40rpx 0 40rpx 0;
width: 33%;
text-align: center;
font-size: 30rpx;
}
.couStyle {
border-bottom: 5rpx solid #54a966;
color: #54a966;
font-weight: bold;
}
}
.card {
width: 100%;
overflow: hidden;
margin-bottom: 30rpx;
padding-left: 10rpx;
position: relative;
}
.card > view {
background: #fff;
border-radius: 5rpx;
}
.card .dot-left,
.card .dot-right {
display: block;
width: 12rpx;
height: 12rpx;
border-radius: 50%;
background: #f5f5f5;
position: absolute;
z-index: 999;
}
.card .dot-left {
bottom: -6rpx;
left: -6rpx;
}
.card .dot-right {
bottom: -6rpx;
right: -6rpx;
}
.card .page-group {
position: absolute;
top: 10rpx;
left: -2rpx;
width: 100%;
max-width: 200rpx;
.fold-page {
display: block;
width: 10rpx;
height: 8rpx;
background: #54a966;
transform: skewY(-40deg);
position: absolute;
top: -5rpx;
left: -8rpx;
z-index: 0;
}
.page {
position: absolute;
z-index: 1;
display: block;
padding: 5rpx 20rpx 3rpx 20rpx;
height: 40rpx;
line-height: 40rpx;
background: linear-gradient(137deg, #54a966 0%, #0d5e1e 100%);
border-radius: 0 20rpx 20rpx 0;
color: #fff;
text-align: center;
font-size: 24rpx;
overflow: hidden;
left: -8rpx;
}
}
.card .page-group.grey {
.fold-page {
background: #c6c6c6;
}
.page {
background: linear-gradient(137deg, #c6c6c6 0%, #999595 100%);
}
}
.card .content {
width: 100%;
height: 180rpx;
border-bottom: 1rpx dotted #f5f5f5;
position: relative;
z-index: 2;
}
.card .content .coupon-detail {
display: flex;
padding: 0 15rpx 0 20rpx;
}
.card .content .coupon-detail > view {
height: 130rpx;
display: flex;
align-items: center;
}
.card .content .coupon-detail > view:first-child {
color: #54a966;
padding-top: 100rpx;
width: 30%;
}
.card .content .coupon-detail > view.grey {
color: #c6c6c6;
}
.card .content .coupon-detail > view:first-child > span:first-child {
font-size: 30rpx;
margin: 0 10rpx 0 0;
}
.card .content .coupon-detail > view:first-child > span:last-child {
font-size: 70rpx;
}
.card .content .coupon-detail > view:last-child > view {
color: #54a966;
border: 1rpx solid #54a966;
border-radius: 50rpx;
font-size: 12px;
line-height: 25px;
width: 150rpx;
height: 50rpx;
margin: 100rpx 0 0 5rpx;
text-align: center;
}
.card .coupon-detail > view:nth-child(2) {
flex-direction: column;
padding-top: 60rpx;
width: 40%;
}
.card .coupon-detail > view:nth-child(2) > view {
width: 100%;
}
.card .coupon-detail > view:nth-child(2) > view:first-child {
color: #333;
font-weight: bold;
font-size: 25rpx;
margin: 0 0 10rpx 0;
}
.card .coupon-detail > view:nth-child(2) > view:last-child {
font-size: 12px;
color: #adadad;
margin-top: 5rpx;
}
.card .coupon-detail > view:nth-child(2) > view:last-child > view {
transform: scale(0.8);
margin-left: -14rpx;
}
.card {
.footer {
color: #999;
font-size: 12px;
padding: 30rpx 15rpx 30rpx 30rpx;
}
.ribbon {
width: 160rpx;
height: 40rpx;
background: #54a966;
position: absolute;
right: -40rpx;
top: 25rpx;
transform: rotateZ(45deg);
text-align: center;
color: #fff;
font-size: 20rpx;
line-height: 44rpx;
}
.ribbon.grey {
background: #c6c6c6;
}
}
}
.Hot {
color: $themeColor !important;
}
.AC_chong {
display: flex;
align-items: center;
justify-content: space-between;
background: $themeBgColor;
color: #fff;
border-top-left-radius: 15rpx;
border-top-right-radius: 15rpx;
padding: 20rpx 20rpx;
font-size: 34rpx;
text-align: right;
}
.rightArrow {
width: 40rpx;
height: 40rpx;
float: right;
margin-left: 10rpx;
// position: absolute;
// right: 30rpx;
// top: 20rpx;
}
/deep/.u-alert__content__title
{
// background-color: #f0f0f0;
font-size: 24rpx !important;
// color: #f4511a !important;
line-height: 28rpx !important;
}
</style>

435
pages/wallet/points.vue Normal file
View File

@@ -0,0 +1,435 @@
<template>
<view class="wallet_wrap">
<z-nav-bar title="我的积分" bgColor="#5188e5" fontColor="#fff"></z-nav-bar>
<view class="ACTable"><view>
<view class="AC_con">
<common-list
:dataList="MoneyRecord"
isCondition="true"
isNoIcon="true"
label="orderType"
>
<template slot="labelSlot" slot-scope="slotProps">
<view class="label_content AC_List" @click="slotProps.row.relationId?goClick(slotProps.row.relationId):''">
<view class="point_box">
<view class="title"><view class="AC_time">{{slotProps.row.createTime}}</view></view>
<view class="Hot">
<text v-if="slotProps.row.changeAmount > 0">+</text>
<text>{{slotProps.row.changeAmount}}</text>
</view>
</view>
<view class="AC_mark" v-if="slotProps.row.remark">{{slotProps.row.remark}}</view>
</view>
</template>
</common-list>
</view>
</view>
<view style="padding-bottom: 20rpx">
<u-back-top
:scroll-top="scrollTop"
bottom="60"
:customStyle="bgiStyle"
:iconStyle="iconStyle"
>
</u-back-top>
</view>
</view>
</view>
</template>
<script>
import $http from "@/config/requestConfig.js";
import { mapState } from "vuex";
export default {
data() {
return {
playData: {},
platform: null,
urlList: {
list: "common/jfTransactionDetails/getJfTransactionDetailsList",
},
cardList: [],
couponListTab: 0,
MoneyRecord: [],
userMes: {},
RecordScreen: {
userid: "",
page: 1,
limit: 5,
},
scrollTop: 0,
status: 3,
totalPage: 0,
totalCount: 0,
tab_muJian: 0,
bgiStyle: {
background: "#54a966",
},
iconStyle: {
fontSize: "40rpx",
fontWeight: "bold",
color: "#54a966",
},
};
},
// 返回顶部
onPageScroll(e) {
this.scrollTop = e.scrollTop;
},
//第一次加载
onLoad(e) {
},
computed: {
...mapState(["userInfo"]),
},
//页面显示
onShow() {
this.getData();
},
//方法
methods: {
//列表进入详情
goClick(id){
uni.navigateTo({
url: "/pages/order/infor?orderId=" + id
});
},
//获取数据
getData() {
var data = {
userId: this.userInfo.id,
};
if (!this.iosHide) {
this.tab_muJian = 1;
}
//用户详情
if (this.userInfo.id != undefined) {
this.$http.post("book/user/info/" + this.userInfo.id).then((res) => {
this.userMes = res.user;
});
}
uni.showLoading({
title: '加载中'
})
$http.request({
url: this.urlList.list,
method: "POST",
data,
header: {
"Content-Type": "application/json",
},
})
.then((res) => {
uni.hideLoading();
this.MoneyRecord = res.transactionDetailsList;
});
},
},
};
</script>
<style lang="scss" scoped>
@import "@/static/mixin.scss";
.wallet_wrap{
background-color: #eff5f8;
height: 100%;
overflow: auto;
}
.ACTable {
margin: 30rpx;
border-radius: 15rpx;
background: #fff;
.AC_mes {
width: 100%;
height: 300rpx;
padding: 40rpx 20rpx;
box-shadow: 0 0px 10px 1px #d3d1d133;
border-radius: 15rpx;
margin-bottom: 10rpx;
position: relative;
.wallet_title {
color: $themeColor;
font-size: 40rpx;
line-height: 60rpx;
font-weight: 500 !important;
margin-bottom: 20rpx;
}
.wallet_number {
color: $themeColor;
font-size: 80rpx;
line-height: 80rpx;
font-weight: bold;
}
}
.AC_con {
overflow: hidden;
font-size: 30rpx;
padding-bottom: 30rpx;
.AC_jilu {
font-size: 42rpx;
text-align: left;
color: #3d75bf;
padding: 10rpx 20rpx 40rpx;
}
/deep/.list_item {
padding: 30rpx !important;
}
}
.couponList {
view {
display: inline-block;
padding: 0 0 25rpx 0;
margin: 40rpx 0 40rpx 0;
width: 33%;
text-align: center;
font-size: 30rpx;
}
.couStyle {
border-bottom: 5rpx solid #54a966;
color: #54a966;
font-weight: bold;
}
}
.card {
width: 100%;
overflow: hidden;
margin-bottom: 30rpx;
padding-left: 10rpx;
position: relative;
}
.card > view {
background: #fff;
border-radius: 5rpx;
}
.card .dot-left,
.card .dot-right {
display: block;
width: 12rpx;
height: 12rpx;
border-radius: 50%;
background: #f5f5f5;
position: absolute;
z-index: 999;
}
.card .dot-left {
bottom: -6rpx;
left: -6rpx;
}
.card .dot-right {
bottom: -6rpx;
right: -6rpx;
}
.card .page-group {
position: absolute;
top: 10rpx;
left: -2rpx;
width: 100%;
max-width: 200rpx;
.fold-page {
display: block;
width: 10rpx;
height: 8rpx;
background: #54a966;
transform: skewY(-40deg);
position: absolute;
top: -5rpx;
left: -8rpx;
z-index: 0;
}
.page {
position: absolute;
z-index: 1;
display: block;
padding: 5rpx 20rpx 3rpx 20rpx;
height: 40rpx;
line-height: 40rpx;
background: linear-gradient(137deg, #54a966 0%, #0d5e1e 100%);
border-radius: 0 20rpx 20rpx 0;
color: #fff;
text-align: center;
font-size: 24rpx;
overflow: hidden;
left: -8rpx;
}
}
.card .page-group.grey {
.fold-page {
background: #c6c6c6;
}
.page {
background: linear-gradient(137deg, #c6c6c6 0%, #999595 100%);
}
}
.card .content {
width: 100%;
height: 180rpx;
border-bottom: 1rpx dotted #f5f5f5;
position: relative;
z-index: 2;
}
.card .content .coupon-detail {
display: flex;
padding: 0 15rpx 0 20rpx;
}
.card .content .coupon-detail > view {
height: 130rpx;
display: flex;
align-items: center;
}
.card .content .coupon-detail > view:first-child {
color: #54a966;
padding-top: 100rpx;
width: 30%;
}
.card .content .coupon-detail > view.grey {
color: #c6c6c6;
}
.card .content .coupon-detail > view:first-child > span:first-child {
font-size: 30rpx;
margin: 0 10rpx 0 0;
}
.card .content .coupon-detail > view:first-child > span:last-child {
font-size: 70rpx;
}
.card .content .coupon-detail > view:last-child > view {
color: #54a966;
border: 1rpx solid #54a966;
border-radius: 50rpx;
font-size: 12px;
line-height: 25px;
width: 150rpx;
height: 50rpx;
margin: 100rpx 0 0 5rpx;
text-align: center;
}
.card .coupon-detail > view:nth-child(2) {
flex-direction: column;
padding-top: 60rpx;
width: 40%;
}
.card .coupon-detail > view:nth-child(2) > view {
width: 100%;
}
.card .coupon-detail > view:nth-child(2) > view:first-child {
color: #333;
font-weight: bold;
font-size: 25rpx;
margin: 0 0 10rpx 0;
}
.card .coupon-detail > view:nth-child(2) > view:last-child {
font-size: 12px;
color: #adadad;
margin-top: 5rpx;
}
.card .coupon-detail > view:nth-child(2) > view:last-child > view {
transform: scale(0.8);
margin-left: -14rpx;
}
.card {
.footer {
color: #999;
font-size: 12px;
padding: 30rpx 15rpx 30rpx 30rpx;
}
.ribbon {
width: 160rpx;
height: 40rpx;
background: #54a966;
position: absolute;
right: -40rpx;
top: 25rpx;
transform: rotateZ(45deg);
text-align: center;
color: #fff;
font-size: 20rpx;
line-height: 44rpx;
}
.ribbon.grey {
background: #c6c6c6;
}
}
}
.Hot {
color: $themeColor !important;
font-size: 32rpx;
font-weight: bold;
}
.AC_chong {
display: flex;
align-items: center;
justify-content: space-between;
background-image: linear-gradient(90deg, #258feb 0%, #73ee9c 100%);
color: #fff;
border-top-left-radius: 15rpx;
border-top-right-radius: 15rpx;
padding: 20rpx 20rpx;
font-size: 36rpx;
font-weight: 800;
text-align: right;
}
.rightArrow {
width: 40rpx;
height: 40rpx;
float: right;
margin-left: 10rpx;
}
.point_box{
display: flex;
align-items: center;
justify-content: space-between;
padding: 10rpx 0;
color: #333;
}
.title{
font-size: 32rpx;
font-weight: bold;
}
.AC_mark {
width: 100%;
display: block;
font-size: 28rpx;
color: #343434;
}
.AC_time {
color: #909090;
font-size: 28rpx;
padding-top: 10rpx;
}
</style>

786
pages/wallet/recharge.vue Normal file
View File

@@ -0,0 +1,786 @@
<template>
<view class="commonPageBox">
<z-nav-bar title="充值" bgColor="#5188e5" fontColor="#fff"></z-nav-bar>
<view>
<view class="cha_jine">
<view class="cj_title PM_font">充值金额</view>
<view class="cj_xiang">
<view v-for="(item, index) in cjList" @click="chosPric(item)" :class="
stepsCj.priceTypeId == item.priceTypeId
? 'Tab_cj cj_price'
: 'cj_price'
">
<view class="pr_jg">{{ item.realMoney }}</view>
<view class="pr_yl">{{ item.money }} 天医币</view>
<view class="pr_lj">限时特惠</view>
</view>
<br clear="both" />
</view>
</view>
<view class="cha_fangsh" v-if="isAndroid">
<view class="cf_title PM_font">支付方式</view>
<view class="cf_radio">
<u-radio-group v-model="payType">
<view style="width: 100%">
<view v-for="(item, index) in paylist" @click="choseType(item.id)"
:class="payType == item.id ? 'Tab_xf cf_xuanx' : 'cf_xuanx'">
<image class="pay_item_img" :src="item.imgUrl" mode="aspectFil">
</image>
{{ item.title }}
<u-radio :key="index" activeColor="#5188e5" :name="item.id"
style="float: right; margin-top: 5rpx"></u-radio>
</view>
</view>
</u-radio-group>
</view>
</view>
<view class="agree_wo flexbox" style="float: left; display: flex">
<radio-group class="agree">
<view v-for="(item, index) in argee" :key="index">
<radio class="agreeRadio" :value="item.id" :checked="item.id == radioValue" color="#5188e5"
@click="radioCheck(index)"></radio>
</view>
</radio-group>
<view>* 我已阅读并同意<span class="highlight" @click="showXieyi">增值服务协议</span></view>
</view>
<view class="char_btn">
<view @click="goToPay">立即充值</view>
</view>
</view>
<!-- 充值协议 -->
<u-popup :show="xieyiShow" :round="10" @close="xieyiShow = false">
<view class="tanchu">
<view class="dp_title">{{ xieyi.title }}</view>
<view style="max-height: 1000rpx; overflow-y: scroll">
<view v-html="xieyi.content"></view>
</view>
</view>
</u-popup>
</view>
</template>
<script>
import $http from "@/config/requestConfig.js";
import { checkIapOrder } from "@/store/modules/common.js";
import {
mapState,
mapMutations
} from "vuex";
import {
setPay,
setPayAssign,
setWXPay
} from "@/config/utils";
export default {
data() {
return {
playData: {},
options: {},
xieyi: {
title: "",
content: "",
},
chargeOrderSn: "", // 通过query传过来的orderSN二次支付情况
xieyiShow: false,
stepsCj: {},
cjList: [],
argee: [{value: false, id: "1"}], // 同意权限
radioValue: "",
orderSn: "", // 订单sn
productid: "",
isAndroid: true, // 是否为安卓环境
payType: null,
paylist: [{
title: "支付宝",
id: 2,
imgUrl: require("@/static/icon/pay_1.png"),
},
{
title: "微信",
id: 1,
imgUrl: require("@/static/icon/pay_2.png"),
},
],
transaction: {
//成功回调
},
urlList: {
list: "common/bookBuyConfig/getBookBuyConfigList",
},
};
},
//第一次加载
onLoad(options) {
this.options = options;
if (this.$platform == "ios") {
this.payType = 3;
} else {
this.payType = 1;
}
this.chargeOrderSn = options.orderSn;
this.getDevName();
},
//页面显示
onShow() {
},
computed: {
...mapState(["userInfo"]),
},
//方法
methods: {
...mapMutations(["setUserInfo"]),
// 关闭交易订单
finishTransaction(trans) {
this.iapChannel.finishTransaction(
trans,
(success) => {
console.log("关闭订单成功");
this.setUserInfo({
restoreFlag: false,
});
},
(fail) => {
console.log("关闭订单失败");
}
);
},
async showXieyi() {
var data = await this.$commonJS.getAgreement(114);
if (data.content) {
data.content = data.content.replace(
/<h5>/g,
'<view style="font-weight: bold;font-size: 32rpx;margin-top: 20rpx;margin-bottom: 20rpx;">'
);
data.content = data.content.replace(/<\/h5>/g, "</view>");
}
this.xieyi = data;
this.xieyiShow = true;
},
//勾选用户协议
radioCheck(index) {
this.argee.forEach((item) => {
item.isCheck = false;
});
if (this.radioValue == this.argee[index].id) {
this.radioValue = null;
} else {
this.radioValue = this.argee[index].id;
}
},
getProvider() {
return new Promise((resolve, reject) => {
uni.getProvider({
service: 'payment',
success: (res) => {
const iapChannel = res.providers.find((channel) => {
return (channel.id === 'appleiap')
})
resolve(iapChannel);
// 如果 iapChannel 为 null说明当前包没有包含iap支付模块。注意HBuilder基座不包含 iap 通道
}
});
})
},
requestPayment(orderInfo) {
let that = this
return new Promise((resolve, reject) => {
uni.requestPayment({
provider: 'appleiap',
orderInfo: orderInfo,
success: (res) => {
that.iapCheck(res);
resolve(res);
},
fail: (err) => {
uni.hideLoading()
that.restoreComplateRequest()
if (err.code == 2) {
uni.showToast({
title: '取消支付,内购订单即将关闭',
icon: 'none'
})
} else {
uni.showToast({
title: '支付失败,内购订单即将关闭',
icon: 'none'
})
}
reject(err);
}
});
})
},
// 查询未关闭iap订单
async restoreComplateRequest() {
let that = this;
await this.iapChannel.restoreCompletedTransactions({
manualFinishTransaction: true
}, function(results) {
// results 格式为数组存放恢复的IAP商品交易信息对象 IAPTransaction通用需将返回的支付凭证传给后端进行二次认证
that.ComplateRequestArr = results
console.log('未完成订单数组共有:', that.ComplateRequestArr.length)
if (results && results.length > 0) {
results.map((item, index) => {
that.finishTransaction(item);
});
}
});
},
async iphonepay() {
const that = this;
uni.showLoading({
title: "检测支付环境"
})
this.iapChannel = await this.getProvider()
if (this.iapChannel) {
await this.restoreComplateRequest();
await this.requestOrder();
} else {
uni.hideLoading()
uni.showToast({
title: '不支持内购支付',
icon: 'none'
})
}
},
requestOrder() {
uni.showLoading({
title: "获取商品信息",
mask: true,
});
const that = this;
let IAPOrders = [];
IAPOrders.push('t' + that.stepsCj.priceTypeId);
// 新建订单
uni.showLoading({
title: "正在创建订单",
mask: true,
});
that.iapChannel.requestProduct(
IAPOrders,
function(event) {
for (var index in event) {
var OrderItem = event[index];
that.topay(OrderItem.productid);
}
},
function(erroemsg) {
uni.showToast({
title: "商品获取失败",
icon: "none",
});
}
);
},
async topay(id) {
const that = this;
uni.showLoading({
title: "正在支付",
mask: true,
});
let orderInfo = {
productid: id,
username: that.orderSn, // 用户标识/订单标识
quantity: 1,
manualFinishTransaction: true, // 3.5.1+ 支持,设置此参数后需要开发者主动关闭订单,参见下面的关闭订单方法 finishTransaction()
}
that.transaction = await that.requestPayment(orderInfo)
},
iapCheck(result) {
let that = this;
let data = {
transactionId: result.transactionIdentifier, // 支付交易id
customerOid: that.userInfo.id,
productId: result.payment.productid.slice(1), // 产品id
orderId: result.payment.username, // 系统订单号
receiptData: result.transactionReceipt, // 苹果返回收据
// sandBox: true, //测试数据
// body: that.stepsCj.priceTypeId //充值类型id
};
$http
.request({
url: "Ipa/veri",
method: "POST",
data,
header: {
"Content-Type": "application/json",
},
})
.then((res) => {
if (res.code == 0) {
that.transaction = null
uni.hideLoading()
uni.showToast({
title: '充值成功!',
icon: 'success'
})
//服务器验证票据有效后在客户端关闭订单 (iapChannel.finishTransaction)
that.finishTransaction(result);
setTimeout(() => {
uni.switchTab({
url: '/pages/my/index'
})
}, 2000);
}
})
.catch((e) => {
uni.hideLoading()
that.finishTransaction(result);
uni.showModal({
title: "提示",
showCancel: false,
content: "支付验证失败请稍后重启app如不能解决您的问题可联系官方客服",
success: function(res) {
if (res.confirm) {
console.log("用户点击确定");
}
},
});
}, )
},
//获取使用环境
getDevName() {
if (uni.getSystemInfoSync().platform === "android") {
this.isAndroid = true;
} else {
this.isAndroid = false;
}
this.getData();
},
//获取充值金额
getData() {
if (this.isAndroid) {
this.getAndorList();
} else {
this.getAppleList();
}
},
// 安卓充值列表
getAndorList() {
uni.showLoading({
title: '加载中'
})
var data = {
type: "point",
qudao: "Android",
};
$http.request({
url: this.urlList.list,
method: "POST",
data,
header: {
"Content-Type": "application/json",
},
})
.then((res) => {
uni.hideLoading();
this.cjList = res.bookBuyConfigList;
this.stepsCj = res.bookBuyConfigList[0];
});
},
// 苹果充值列表
getAppleList() {
var data = {
type: "point",
qudao: "IOS",
};
$http.request({
url: this.urlList.list,
method: "POST",
data,
header: {
"Content-Type": "application/json",
},
})
.then((res) => {
this.cjList = res.bookBuyConfigList;
this.stepsCj = res.bookBuyConfigList[0];
});
},
// 点击充值金额
chosPric(e) {
this.stepsCj = e;
},
// 选择支付方式1
choseType(e) {
let that = this;
that.payType = e;
},
// 充值
goToPay() {
this.kaiChar();
},
// 正常充值
kaiChar() {
if (!this.payType) {
uni.showToast({
title: "请勾选支付方式",
icon: "none",
});
return false;
}
//常规充值
if (this.radioValue == "1") {
uni.showLoading({
title: "支付中,请勿离开",
icon: "loading",
});
let that = this;
let data = {
userId: that.userInfo.id, //下单人ID
userPhone: that.userInfo.tel, //收货人手机号
paymentMethod: that.payType, //2支付宝1微信3ios内购
orderMoney: that.stepsCj.money * 1, //订单金额
districtMoney: 0, //优惠金额
realMoney: that.stepsCj.money * 1, //实收金额
appName: "xlkj",
come: "3",
productId: that.stepsCj.priceTypeId, // 充值的类型id
};
$http.request({
url: "book/buyOrder/rechargeSave",
method: "POST",
data,
header: {
"Content-Type": "application/json",
},
})
.then((res) => {
that.orderSn = res.orderSn;
uni.hideLoading();
if (res.code == 0) {
if (that.payType == 2) {
setPay({
typePay: "alipay",
subject: "point",
totalAmount: that.stepsCj.money,
type: that.payType,
relevanceoid: res.orderSn,
body: that.stepsCj.priceTypeId,
},
(res) => {
if (res.success) {
uni.showToast({
title: "支付成功",
});
setTimeout(() => {
uni.navigateTo({
url: "/pages/wallet/account?source=recharge",
});
}, 2000);
} else {
uni.showToast({
title: "支付失败",
icon: "none"
});
}
}
);
} else if (that.payType == 1) {
// 微信支付
that.orderSn = res.orderSn;
let data1 = {
orderSn: res.orderSn,
buyOrderId: that.stepsCj.priceTypeId,
totalAmount: that.stepsCj.money,
};
setWXPay(data1, (res) => {
if (res.success) {
uni.showToast({
title: "支付成功",
});
setTimeout(() => {
uni.navigateTo({
url: "/pages/wallet/account?source=recharge",
});
}, 2000);
} else {
console.log(res);
if (res.data.errMsg.indexOf("User canceled") != -1) {
uni.showToast({
title: "用户取消支付",
icon: "none"
});
} else {
uni.showToast({
title: "支付失败",
icon: "none"
});
}
}
});
} else if (that.payType == 3) {
that.iphonepay();
}
}
});
} else {
uni.showToast({
title: "请勾选 已阅读增值服务协议",
icon: "none",
});
return false;
}
},
// 跳转
onPageJump(url) {
uni.navigateTo({
url: url,
});
},
},
};
</script>
<style lang="scss" scoped>
@import "@/static/mixin.scss";
.tanchu {
padding: 40rpx 30rpx 40rpx 30rpx;
position: relative;
.dp_title {
font-size: 32rpx;
margin-bottom: 50rpx;
color: #555;
text-align: center;
font-weight: bold;
}
.dp_add {
position: absolute;
top: 40rpx;
right: 30rpx;
font-size: 22rpx;
background-color: #fd6004;
color: #fff;
border-radius: 10rpx;
padding: 5rpx 10rpx;
.u-icon {
display: inline-block;
margin-right: 5rpx;
}
}
.addressItem {
border: 2px dashed #d9d9d9;
border-radius: 10rpx;
width: 100%;
display: flex;
padding: 20rpx 10rpx;
margin: 25rpx 0 0 0;
align-items: center;
background-color: #fff;
.addrContent {
margin-left: 40rpx;
flex: 1;
.addrContentTop {
display: flex;
align-items: flex-end;
margin: 0 0 15rpx 0;
position: relative;
.userName {
font-size: 35rpx;
font-weight: bold;
margin-right: 30rpx;
}
.userTel {
font-size: 25rpx;
color: #888;
}
.userMoren {
border: 1px solid #fd6004;
color: #fd6004;
padding: 3rpx 10rpx;
font-size: 22rpx;
border-radius: 10rpx;
margin: 0 0 0 20rpx;
}
.chooseCheck {
position: absolute;
top: 3rpx;
right: 6rpx;
}
}
.addrContentBottom {
font-size: 32rpx;
}
}
}
.addressItem.addItem_style {
border-color: #fd6004;
}
.youhuiItem {
border: 1px solid #d9d9d9;
border-radius: 10rpx;
width: 100%;
display: flex;
padding: 20rpx 10rpx;
margin: 25rpx 0 0 0;
align-items: center;
background-color: #fff;
font-size: 30rpx;
}
.youhuiItem>view {
float: left;
}
.youhuiItem.youItem_style {
border-color: #fd6004;
}
}
.agreeRadio {
zoom: 0.8;
}
.cha_jine {
padding: 60rpx 30rpx 40rpx;
.cj_title {
font-size: 46rpx;
color: #294a97;
}
.cj_xiang {
margin-top: 40rpx;
.cj_price {
box-shadow: 0 0 20rpx 0 #0000001a;
float: left;
width: 47%;
margin: 0 5% 30rpx 0;
text-align: center;
padding: 20rpx;
border-radius: 15rpx;
position: relative;
color: #2d2d2d;
.pr_jg {
font-size: 45rpx;
margin: 20rpx 0 10rpx 0;
font-weight: bold;
}
.pr_yl {
font-size: 26rpx;
color: #575555;
}
.pr_lj {
background-image: linear-gradient(180deg, #5188e5 0%, #abcbfb 100%);
color: #fff;
position: absolute;
top: -20rpx;
right: -20rpx;
z-index: 999;
font-size: 24rpx;
line-height: 20px;
padding: 5rpx 10rpx;
border-top-left-radius: 20rpx;
border-bottom-right-radius: 20rpx;
}
}
.cj_price:nth-child(2n) {
margin-right: 0;
}
.Tab_cj {
box-shadow: 0px 0px 5px 0px $themeColor !important;
color: $themeColor;
.pr_yl{
color: $themeColor;
}
}
}
}
.highlight {
color: $themeColor;
}
.cha_fangsh {
padding: 40rpx 30rpx;
.cf_title {
font-size: 46rpx;
color: #294a97;
}
.cf_radio {
margin-top: 20rpx;
.cf_xuanx {
font-size: 32rpx;
padding: 20rpx 0;
margin-bottom: 20rpx;
border-bottom: 1px solid #ededed;
image {
width: 40rpx;
height: 40rpx;
display: inline-block;
margin-right: 20rpx;
vertical-align: bottom;
}
}
}
}
.agree_wo {
color: #aaa;
font-size: 25rpx;
margin-top: 30rpx;
padding: 30rpx;
padding-bottom: 180rpx;
}
.char_btn {
position: fixed;
left: 0;
right: 0;
bottom: 30rpx;
view {
background: $themeBgColor;
color: #fff;
width: 90%;
margin: 0 auto;
text-align: center;
font-size: 30rpx;
padding: 20rpx 0;
border-radius: 50rpx;
}
}
.pay_item_img {
width: 50rpx;
height: 50rpx;
float: left;
margin-right: 20rpx;
}
.agree {
width: auto !important;
}
.commonPageBox {
background-color: #fff !important;
}
</style>

View File

@@ -1,6 +1,6 @@
<template>
<view class="content">
<z-nav-bar title="吴门医述"></z-nav-bar>
<z-nav-bar title="吴门医述" bgColor="#5188e5" fontColor="#fff"></z-nav-bar>
<z-navigation></z-navigation>
</view>
</template>

View File

@@ -2,6 +2,13 @@
$themeColor: #5188e5;
$themeBgColor: #fff !important;
@font-face {
font-family: 'PangMenZhengDaobiaoTiTiMianFeiBan';
src: url('@/static/font/PangMenZhengDaoBiaoTiTiMianFeiBan.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'MicrosoftYaHei';
font-weight: normal;
@@ -88,6 +95,10 @@ $themeBgColor: #fff !important;
box-shadow: 0px 0px 3px 0px rgba(0, 82, 79, 0.2) !important;
}
.PM_font {
font-family: PangMenZhengDaoBiaoTiTiMianFeiBan;
}
.bg_color {
background: rgba(125, 193, 240, 0.1);
}

Binary file not shown.

BIN
static/icon/icon_add.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 426 B

BIN
static/icon/icon_folder.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 610 B

BIN
static/icon/icon_hz.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
static/icon/jifen.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
static/icon/order_vip.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
static/icon/pay_1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 952 B

BIN
static/icon/pay_2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 893 B

BIN
static/icon/pay_3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
static/icon/vip.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

1
taimed

Submodule taimed deleted from 23cb7534ac

View File

@@ -0,0 +1,97 @@
## 1.1.92023-04-11
- 修复 vue3 下 keyboardheightchange 事件报错的bug
## 1.1.82023-03-29
- 优化 trim 属性默认值
## 1.1.72023-03-29
- 新增 cursor-spacing 属性
## 1.1.62023-01-28
- 新增 keyboardheightchange 事件,可监听键盘高度变化
## 1.1.52022-11-29
- 优化 主题样式
## 1.1.42022-10-27
- 修复 props 中背景颜色无默认值的bug
## 1.1.02022-06-30
- 新增 在 uni-forms 1.4.0 中使用可以在 blur 时校验内容
- 新增 clear 事件,点击右侧叉号图标触发
- 新增 change 事件 ,仅在输入框失去焦点或用户按下回车时触发
- 优化 组件样式,组件获取焦点时高亮显示,图标颜色调整等
## 1.0.52022-06-07
- 优化 clearable 显示策略
## 1.0.42022-06-07
- 优化 clearable 显示策略
## 1.0.32022-05-20
- 修复 关闭图标某些情况下无法取消的 bug
## 1.0.22022-04-12
- 修复 默认值不生效的 bug
## 1.0.12022-04-02
- 修复 value 不能为 0 的 bug
## 1.0.02021-11-19
- 优化 组件 UI并提供设计资源详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-easyinput](https://uniapp.dcloud.io/component/uniui/uni-easyinput)
## 0.1.42021-08-20
- 修复 在 uni-forms 的动态表单中默认值校验不通过的 bug
## 0.1.32021-08-11
- 修复 在 uni-forms 中重置表单,错误信息无法清除的问题
## 0.1.22021-07-30
- 优化 vue3 下事件警告的问题
## 0.1.1
- 优化 errorMessage 属性支持 Boolean 类型
## 0.1.02021-07-13
- 组件兼容 vue3如何创建 vue3 项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 0.0.162021-06-29
- 修复 confirmType 属性(仅 type="text" 生效)导致多行文本框无法换行的 bug
## 0.0.152021-06-21
- 修复 passwordIcon 属性拼写错误的 bug
## 0.0.142021-06-18
- 新增 passwordIcon 属性,当 type=password 时是否显示小眼睛图标
- 修复 confirmType 属性不生效的问题
## 0.0.132021-06-04
- 修复 disabled 状态可清出内容的 bug
## 0.0.122021-05-12
- 新增 组件示例地址
## 0.0.112021-05-07
- 修复 input-border 属性不生效的问题
## 0.0.102021-04-30
- 修复 ios 遮挡文字、显示一半的问题
## 0.0.92021-02-05
- 调整为 uni_modules 目录规范
- 优化 兼容 nvue 页面

View File

@@ -0,0 +1,56 @@
/**
* @desc 函数防抖
* @param func 目标函数
* @param wait 延迟执行毫秒数
* @param immediate true - 立即执行, false - 延迟执行
*/
export const debounce = function(func, wait = 1000, immediate = true) {
let timer;
console.log(1);
return function() {
console.log(123);
let context = this,
args = arguments;
if (timer) clearTimeout(timer);
if (immediate) {
let callNow = !timer;
timer = setTimeout(() => {
timer = null;
}, wait);
if (callNow) func.apply(context, args);
} else {
timer = setTimeout(() => {
func.apply(context, args);
}, wait)
}
}
}
/**
* @desc 函数节流
* @param func 函数
* @param wait 延迟执行毫秒数
* @param type 1 使用表时间戳,在时间段开始的时候触发 2 使用表定时器,在时间段结束的时候触发
*/
export const throttle = (func, wait = 1000, type = 1) => {
let previous = 0;
let timeout;
return function() {
let context = this;
let args = arguments;
if (type === 1) {
let now = Date.now();
if (now - previous > wait) {
func.apply(context, args);
previous = now;
}
} else if (type === 2) {
if (!timeout) {
timeout = setTimeout(() => {
timeout = null;
func.apply(context, args)
}, wait)
}
}
}
}

View File

@@ -0,0 +1,657 @@
<template>
<view class="uni-easyinput" :class="{ 'uni-easyinput-error': msg }" :style="boxStyle">
<view class="uni-easyinput__content" :class="inputContentClass" :style="inputContentStyle">
<uni-icons v-if="prefixIcon" class="content-clear-icon" :type="prefixIcon" color="#c0c4cc" @click="onClickIcon('prefix')" size="22"></uni-icons>
<textarea
v-if="type === 'textarea'"
class="uni-easyinput__content-textarea"
:class="{ 'input-padding': inputBorder }"
:name="name"
:value="val"
:placeholder="placeholder"
:placeholderStyle="placeholderStyle"
:disabled="disabled"
placeholder-class="uni-easyinput__placeholder-class"
:maxlength="inputMaxlength"
:focus="focused"
:autoHeight="autoHeight"
:cursor-spacing="cursorSpacing"
@input="onInput"
@blur="_Blur"
@focus="_Focus"
@confirm="onConfirm"
@keyboardheightchange="onkeyboardheightchange"
></textarea>
<input
v-else
:type="type === 'password' ? 'text' : type"
class="uni-easyinput__content-input"
:style="inputStyle"
:name="name"
:value="val"
:password="!showPassword && type === 'password'"
:placeholder="placeholder"
:placeholderStyle="placeholderStyle"
placeholder-class="uni-easyinput__placeholder-class"
:disabled="disabled"
:maxlength="inputMaxlength"
:focus="focused"
:confirmType="confirmType"
:cursor-spacing="cursorSpacing"
@focus="_Focus"
@blur="_Blur"
@input="onInput"
@confirm="onConfirm"
@keyboardheightchange="onkeyboardheightchange"
/>
<template v-if="type === 'password' && passwordIcon">
<!-- 开启密码时显示小眼睛 -->
<uni-icons
v-if="isVal"
class="content-clear-icon"
:class="{ 'is-textarea-icon': type === 'textarea' }"
:type="showPassword ? 'eye-slash-filled' : 'eye-filled'"
:size="22"
:color="focusShow ? primaryColor : '#c0c4cc'"
@click="onEyes"
></uni-icons>
</template>
<template v-else-if="suffixIcon">
<uni-icons v-if="suffixIcon" class="content-clear-icon" :type="suffixIcon" color="#c0c4cc" @click="onClickIcon('suffix')" size="22"></uni-icons>
</template>
<template v-else>
<uni-icons
v-if="clearable && isVal && !disabled && type !== 'textarea'"
class="content-clear-icon"
:class="{ 'is-textarea-icon': type === 'textarea' }"
type="clear"
:size="clearSize"
:color="msg ? '#dd524d' : focusShow ? primaryColor : '#c0c4cc'"
@click="onClear"
></uni-icons>
</template>
<slot name="right"></slot>
</view>
</view>
</template>
<script>
/**
* Easyinput 输入框
* @description 此组件可以实现表单的输入与校验,包括 "text" 和 "textarea" 类型。
* @tutorial https://ext.dcloud.net.cn/plugin?id=3455
* @property {String} value 输入内容
* @property {String } type 输入框的类型默认text password/text/textarea/..
* @value text 文本输入键盘
* @value textarea 多行文本输入键盘
* @value password 密码输入键盘
* @value number 数字输入键盘注意iOS上app-vue弹出的数字键盘并非9宫格方式
* @value idcard 身份证输入键盘信、支付宝、百度、QQ小程序
* @value digit 带小数点的数字键盘 App的nvue页面、微信、支付宝、百度、头条、QQ小程序支持
* @property {Boolean} clearable 是否显示右侧清空内容的图标控件点击可清空输入框内容默认true
* @property {Boolean} autoHeight 是否自动增高输入区域type为textarea时有效默认true
* @property {String } placeholder 输入框的提示文字
* @property {String } placeholderStyle placeholder的样式(内联样式,字符串),如"color: #ddd"
* @property {Boolean} focus 是否自动获得焦点默认false
* @property {Boolean} disabled 是否禁用默认false
* @property {Number } maxlength 最大输入长度,设置为 -1 的时候不限制最大长度默认140
* @property {String } confirmType 设置键盘右下角按钮的文字仅在type="text"时生效默认done
* @property {Number } clearSize 清除图标的大小单位px默认15
* @property {String} prefixIcon 输入框头部图标
* @property {String} suffixIcon 输入框尾部图标
* @property {String} primaryColor 设置主题色(默认#2979ff
* @property {Boolean} trim 是否自动去除两端的空格
* @property {Boolean} cursorSpacing 指定光标与键盘的距离,单位 px
* @value both 去除两端空格
* @value left 去除左侧空格
* @value right 去除右侧空格
* @value start 去除左侧空格
* @value end 去除右侧空格
* @value all 去除全部空格
* @value none 不去除空格
* @property {Boolean} inputBorder 是否显示input输入框的边框默认true
* @property {Boolean} passwordIcon type=password时是否显示小眼睛图标
* @property {Object} styles 自定义颜色
* @event {Function} input 输入框内容发生变化时触发
* @event {Function} focus 输入框获得焦点时触发
* @event {Function} blur 输入框失去焦点时触发
* @event {Function} confirm 点击完成按钮时触发
* @event {Function} iconClick 点击图标时触发
* @example <uni-easyinput v-model="mobile"></uni-easyinput>
*/
function obj2strClass(obj) {
let classess = '';
for (let key in obj) {
const val = obj[key];
if (val) {
classess += `${key} `;
}
}
return classess;
}
function obj2strStyle(obj) {
let style = '';
for (let key in obj) {
const val = obj[key];
style += `${key}:${val};`;
}
return style;
}
export default {
name: 'uni-easyinput',
emits: ['click', 'iconClick', 'update:modelValue', 'input', 'focus', 'blur', 'confirm', 'clear', 'eyes', 'change', 'keyboardheightchange'],
model: {
prop: 'modelValue',
event: 'update:modelValue'
},
options: {
virtualHost: true
},
inject: {
form: {
from: 'uniForm',
default: null
},
formItem: {
from: 'uniFormItem',
default: null
}
},
props: {
name: String,
value: [Number, String],
modelValue: [Number, String],
type: {
type: String,
default: 'text'
},
clearable: {
type: Boolean,
default: true
},
autoHeight: {
type: Boolean,
default: false
},
placeholder: {
type: String,
default: ' '
},
placeholderStyle: String,
focus: {
type: Boolean,
default: false
},
disabled: {
type: Boolean,
default: false
},
maxlength: {
type: [Number, String],
default: 140
},
confirmType: {
type: String,
default: 'done'
},
clearSize: {
type: [Number, String],
default: 24
},
inputBorder: {
type: Boolean,
default: true
},
prefixIcon: {
type: String,
default: ''
},
suffixIcon: {
type: String,
default: ''
},
trim: {
type: [Boolean, String],
default: false
},
cursorSpacing: {
type: Number,
default: 0
},
passwordIcon: {
type: Boolean,
default: true
},
primaryColor: {
type: String,
default: '#2979ff'
},
styles: {
type: Object,
default() {
return {
color: '#333',
backgroundColor: '#fff',
disableColor: '#F7F6F6',
borderColor: '#e5e5e5'
};
}
},
errorMessage: {
type: [String, Boolean],
default: ''
}
},
data() {
return {
focused: false,
val: '',
showMsg: '',
border: false,
isFirstBorder: false,
showClearIcon: false,
showPassword: false,
focusShow: false,
localMsg: '',
isEnter: false // 用于判断当前是否是使用回车操作
};
},
computed: {
// 输入框内是否有值
isVal() {
const val = this.val;
// fixed by mehaotian 处理值为0的情况字符串0不在处理范围
if (val || val === 0) {
return true;
}
return false;
},
msg() {
// console.log('computed', this.form, this.formItem);
// if (this.form) {
// return this.errorMessage || this.formItem.errMsg;
// }
// TODO 处理头条 formItem 中 errMsg 不更新的问题
return this.localMsg || this.errorMessage;
},
// 因为uniapp的input组件的maxlength组件必须要数值这里转为数值用户可以传入字符串数值
inputMaxlength() {
return Number(this.maxlength);
},
// 处理外层样式的style
boxStyle() {
return `color:${this.inputBorder && this.msg ? '#e43d33' : this.styles.color};`;
},
// input 内容的类和样式处理
inputContentClass() {
return obj2strClass({
'is-input-border': this.inputBorder,
'is-input-error-border': this.inputBorder && this.msg,
'is-textarea': this.type === 'textarea',
'is-disabled': this.disabled,
'is-focused': this.focusShow
});
},
inputContentStyle() {
const focusColor = this.focusShow ? this.primaryColor : this.styles.borderColor;
const borderColor = this.inputBorder && this.msg ? '#dd524d' : focusColor;
return obj2strStyle({
'border-color': borderColor || '#e5e5e5',
'background-color': this.disabled ? this.styles.disableColor : this.styles.backgroundColor
});
},
// input右侧样式
inputStyle() {
const paddingRight = this.type === 'password' || this.clearable || this.prefixIcon ? '' : '10px';
return obj2strStyle({
'padding-right': paddingRight,
'padding-left': this.prefixIcon ? '' : '10px'
});
}
},
watch: {
value(newVal) {
this.val = newVal;
},
modelValue(newVal) {
this.val = newVal;
},
focus(newVal) {
this.$nextTick(() => {
this.focused = this.focus;
this.focusShow = this.focus;
});
}
},
created() {
this.init();
// TODO 处理头条vue3 computed 不监听 inject 更改的问题formItem.errMsg
if (this.form && this.formItem) {
this.$watch('formItem.errMsg', newVal => {
this.localMsg = newVal;
});
}
},
mounted() {
this.$nextTick(() => {
this.focused = this.focus;
this.focusShow = this.focus;
});
},
methods: {
/**
* 初始化变量值
*/
init() {
if (this.value || this.value === 0) {
this.val = this.value;
} else if (this.modelValue || this.modelValue === 0 || this.modelValue === '') {
this.val = this.modelValue;
} else {
this.val = null;
}
},
/**
* 点击图标时触发
* @param {Object} type
*/
onClickIcon(type) {
this.$emit('iconClick', type);
},
/**
* 显示隐藏内容,密码框时生效
*/
onEyes() {
this.showPassword = !this.showPassword;
this.$emit('eyes', this.showPassword);
},
/**
* 输入时触发
* @param {Object} event
*/
onInput(event) {
let value = event.detail.value;
// 判断是否去除空格
if (this.trim) {
if (typeof this.trim === 'boolean' && this.trim) {
value = this.trimStr(value);
}
if (typeof this.trim === 'string') {
value = this.trimStr(value, this.trim);
}
}
if (this.errMsg) this.errMsg = '';
this.val = value;
// TODO 兼容 vue2
this.$emit('input', value);
// TODO 兼容 vue3
this.$emit('update:modelValue', value);
},
/**
* 外部调用方法
* 获取焦点时触发
* @param {Object} event
*/
onFocus() {
this.$nextTick(() => {
this.focused = true;
});
this.$emit('focus', null);
},
_Focus(event) {
this.focusShow = true;
this.$emit('focus', event);
},
/**
* 外部调用方法
* 失去焦点时触发
* @param {Object} event
*/
onBlur() {
this.focused = false;
this.$emit('focus', null);
},
_Blur(event) {
let value = event.detail.value;
this.focusShow = false;
this.$emit('blur', event);
// 根据类型返回值在event中获取的值理论上讲都是string
if (this.isEnter === false) {
this.$emit('change', this.val);
}
// 失去焦点时参与表单校验
if (this.form && this.formItem) {
const { validateTrigger } = this.form;
if (validateTrigger === 'blur') {
this.formItem.onFieldChange();
}
}
},
/**
* 按下键盘的发送键
* @param {Object} e
*/
onConfirm(e) {
this.$emit('confirm', this.val);
this.isEnter = true;
this.$emit('change', this.val);
this.$nextTick(() => {
this.isEnter = false;
});
},
/**
* 清理内容
* @param {Object} event
*/
onClear(event) {
this.val = '';
// TODO 兼容 vue2
this.$emit('input', '');
// TODO 兼容 vue2
// TODO 兼容 vue3
this.$emit('update:modelValue', '');
// 点击叉号触发
this.$emit('clear');
},
/**
* 键盘高度发生变化的时候触发此事件
* 兼容性微信小程序2.7.0+、App 3.1.0+
* @param {Object} event
*/
onkeyboardheightchange(event) {
this.$emit("keyboardheightchange",event);
},
/**
* 去除空格
*/
trimStr(str, pos = 'both') {
if (pos === 'both') {
return str.trim();
} else if (pos === 'left') {
return str.trimLeft();
} else if (pos === 'right') {
return str.trimRight();
} else if (pos === 'start') {
return str.trimStart();
} else if (pos === 'end') {
return str.trimEnd();
} else if (pos === 'all') {
return str.replace(/\s+/g, '');
} else if (pos === 'none') {
return str;
}
return str;
}
}
};
</script>
<style lang="scss">
$uni-error: #e43d33;
$uni-border-1: #dcdfe6 !default;
.uni-easyinput {
/* #ifndef APP-NVUE */
width: 100%;
/* #endif */
flex: 1;
position: relative;
text-align: left;
color: #333;
font-size: 14px;
}
.uni-easyinput__content {
flex: 1;
/* #ifndef APP-NVUE */
width: 100%;
display: flex;
box-sizing: border-box;
// min-height: 36px;
/* #endif */
flex-direction: row;
align-items: center;
// 处理border动画刚开始显示黑色的问题
border-color: #fff;
transition-property: border-color;
transition-duration: 0.3s;
}
.uni-easyinput__content-input {
/* #ifndef APP-NVUE */
width: auto;
/* #endif */
position: relative;
overflow: hidden;
flex: 1;
line-height: 1;
font-size: 14px;
height: 35px;
// min-height: 36px;
}
.uni-easyinput__placeholder-class {
color: #999;
font-size: 12px;
// font-weight: 200;
}
.is-textarea {
align-items: flex-start;
}
.is-textarea-icon {
margin-top: 5px;
}
.uni-easyinput__content-textarea {
position: relative;
overflow: hidden;
flex: 1;
line-height: 1.5;
font-size: 14px;
margin: 6px;
margin-left: 0;
height: 80px;
min-height: 80px;
/* #ifndef APP-NVUE */
min-height: 80px;
width: auto;
/* #endif */
}
.input-padding {
padding-left: 10px;
}
.content-clear-icon {
padding: 0 5px;
}
.label-icon {
margin-right: 5px;
margin-top: -1px;
}
// 显示边框
.is-input-border {
/* #ifndef APP-NVUE */
display: flex;
box-sizing: border-box;
/* #endif */
flex-direction: row;
align-items: center;
border: 1px solid $uni-border-1;
border-radius: 4px;
/* #ifdef MP-ALIPAY */
overflow: hidden;
/* #endif */
}
.uni-error-message {
position: absolute;
bottom: -17px;
left: 0;
line-height: 12px;
color: $uni-error;
font-size: 12px;
text-align: left;
}
.uni-error-msg--boeder {
position: relative;
bottom: 0;
line-height: 22px;
}
.is-input-error-border {
border-color: $uni-error;
.uni-easyinput__placeholder-class {
color: mix(#fff, $uni-error, 50%);
}
}
.uni-easyinput--border {
margin-bottom: 0;
padding: 10px 15px;
// padding-bottom: 0;
border-top: 1px #eee solid;
}
.uni-easyinput-error {
padding-bottom: 0;
}
.is-first-border {
/* #ifndef APP-NVUE */
border: none;
/* #endif */
/* #ifdef APP-NVUE */
border-width: 0;
/* #endif */
}
.is-disabled {
background-color: #f7f6f6;
color: #d5d5d5;
.uni-easyinput__placeholder-class {
color: #d5d5d5;
font-size: 12px;
}
}
</style>

View File

@@ -0,0 +1,87 @@
{
"id": "uni-easyinput",
"displayName": "uni-easyinput 增强输入框",
"version": "1.1.9",
"description": "Easyinput 组件是对原生input组件的增强",
"keywords": [
"uni-ui",
"uniui",
"input",
"uni-easyinput",
"输入框"
],
"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",
"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"
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
### Easyinput 增强输入框
> **组件名uni-easyinput**
> 代码块: `uEasyinput`
easyinput 组件是对原生input组件的增强 ,是专门为配合表单组件[uni-forms](https://ext.dcloud.net.cn/plugin?id=2773)而设计的easyinput 内置了边框,图标等,同时包含 input 所有功能
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-easyinput)
#### 如使用过程中有任何问题或者您对uni-ui有一些好的建议欢迎加入 uni-ui 交流群871950839

View File

@@ -0,0 +1,17 @@
## 1.0.02021-11-19
- 优化 组件UI并提供设计资源详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-link](https://uniapp.dcloud.io/component/uniui/uni-link)
## 1.1.72021-11-08
## 0.0.72021-09-03
- 修复 在 nvue 下不显示的 bug
## 0.0.62021-07-30
- 新增 支持自定义插槽
## 0.0.52021-06-21
- 新增 download 属性H5平台下载文件名
## 0.0.42021-05-12
- 新增 组件示例地址
## 0.0.32021-03-09
- 新增 href 属性支持 tel:|mailto:
## 0.0.22021-02-05
- 调整为uni_modules目录规范

View File

@@ -0,0 +1,128 @@
<template>
<a v-if="isShowA" class="uni-link" :href="href"
:class="{'uni-link--withline':showUnderLine===true||showUnderLine==='true'}"
:style="{color,fontSize:fontSize+'px'}" :download="download">
<slot>{{text}}</slot>
</a>
<!-- #ifndef APP-NVUE -->
<text v-else class="uni-link" :class="{'uni-link--withline':showUnderLine===true||showUnderLine==='true'}"
:style="{color,fontSize:fontSize+'px'}" @click="openURL">
<slot>{{text}}</slot>
</text>
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
<text v-else class="uni-link" :class="{'uni-link--withline':showUnderLine===true||showUnderLine==='true'}"
:style="{color,fontSize:fontSize+'px'}" @click="openURL">
{{text}}
</text>
<!-- #endif -->
</template>
<script>
/**
* Link 外部网页超链接组件
* @description uni-link是一个外部网页超链接组件在小程序内复制url在app内打开外部浏览器在h5端打开新网页
* @tutorial https://ext.dcloud.net.cn/plugin?id=1182
* @property {String} href 点击后打开的外部网页url
* @property {String} text 显示的文字
* @property {String} downlaod H5平台下载文件名
* @property {Boolean} showUnderLine 是否显示下划线
* @property {String} copyTips 在小程序端复制链接时显示的提示语
* @property {String} color 链接文字颜色
* @property {String} fontSize 链接文字大小
* @example * <uni-link href="https://ext.dcloud.net.cn" text="https://ext.dcloud.net.cn"></uni-link>
*/
export default {
name: 'uniLink',
props: {
href: {
type: String,
default: ''
},
text: {
type: String,
default: ''
},
download: {
type: String,
default: ''
},
showUnderLine: {
type: [Boolean, String],
default: true
},
copyTips: {
type: String,
default: '已自动复制网址,请在手机浏览器里粘贴该网址'
},
color: {
type: String,
default: '#999999'
},
fontSize: {
type: [Number, String],
default: 14
}
},
computed: {
isShowA() {
// #ifdef H5
this._isH5 = true;
// #endif
if ((this.isMail() || this.isTel()) && this._isH5 === true) {
return true;
}
return false;
}
},
created() {
this._isH5 = null;
},
methods: {
isMail() {
return this.href.startsWith('mailto:');
},
isTel() {
return this.href.startsWith('tel:');
},
openURL() {
// #ifdef APP-PLUS
if (this.isTel()) {
this.makePhoneCall(this.href.replace('tel:', ''));
} else {
plus.runtime.openURL(this.href);
}
// #endif
// #ifdef H5
window.open(this.href)
// #endif
// #ifdef MP
uni.setClipboardData({
data: this.href
});
uni.showModal({
content: this.copyTips,
showCancel: false
});
// #endif
},
makePhoneCall(phoneNumber) {
uni.makePhoneCall({
phoneNumber
})
}
}
}
</script>
<style>
/* #ifndef APP-NVUE */
.uni-link {
cursor: pointer;
}
/* #endif */
.uni-link--withline {
text-decoration: underline;
}
</style>

View File

@@ -0,0 +1,87 @@
{
"id": "uni-link",
"displayName": "uni-link 超链接",
"version": "1.0.0",
"description": "uni-link是一个外部网页超链接组件在小程序内复制url在app内打开外部浏览器在h5端打",
"keywords": [
"uni-ui",
"uniui",
"link",
"超链接",
""
],
"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"
},
"快应用": {
"华为": "y",
"联盟": "y"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
## Link 链接
> **组件名uni-link**
> 代码块: `uLink`
uni-link是一个外部网页超链接组件在小程序内复制url在app内打开外部浏览器在h5端打开新网页。
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-link)
#### 如使用过程中有任何问题或者您对uni-ui有一些好的建议欢迎加入 uni-ui 交流群871950839

View File

@@ -0,0 +1,68 @@
## 1.8.32023-04-17
- 修复 uni-popup 重复打开时的 bug
## 1.8.22023-02-02
- uni-popup-dialog 组件新增 inputType 属性
## 1.8.12022-12-01
- 修复 nvue 下 v-show 报错
## 1.8.02022-11-29
- 优化 主题样式
## 1.7.92022-04-02
- 修复 弹出层内部无法滚动的bug
## 1.7.82022-03-28
- 修复 小程序中高度错误的bug
## 1.7.72022-03-17
- 修复 快速调用open出现问题的Bug
## 1.7.62022-02-14
- 修复 safeArea 属性不能设置为false的bug
## 1.7.52022-01-19
- 修复 isMaskClick 失效的bug
## 1.7.42022-01-19
- 新增 cancelText \ confirmText 属性 ,可自定义文本
- 新增 maskBackgroundColor 属性 ,可以修改蒙版颜色
- 优化 maskClick属性 更新为 isMaskClick ,解决微信小程序警告的问题
## 1.7.32022-01-13
- 修复 设置 safeArea 属性不生效的bug
## 1.7.22021-11-26
- 优化 组件示例
## 1.7.12021-11-26
- 修复 vuedoc 文字错误
## 1.7.02021-11-19
- 优化 组件UI并提供设计资源详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-popup](https://uniapp.dcloud.io/component/uniui/uni-popup)
## 1.6.22021-08-24
- 新增 支持国际化
## 1.6.12021-07-30
- 优化 vue3下事件警告的问题
## 1.6.02021-07-13
- 组件兼容 vue3如何创建vue3项目详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 1.5.02021-06-23
- 新增 mask-click 遮罩层点击事件
## 1.4.52021-06-22
- 修复 nvue 平台中间弹出后点击内容再点击遮罩无法关闭的Bug
## 1.4.42021-06-18
- 修复 H5平台中间弹出后点击内容再点击遮罩无法关闭的Bug
## 1.4.32021-06-08
- 修复 错误的 watch 字段
- 修复 safeArea 属性不生效的问题
- 修复 点击内容再点击遮罩无法关闭的Bug
## 1.4.22021-05-12
- 新增 组件示例地址
## 1.4.12021-04-29
- 修复 组件内放置 input 、textarea 组件,无法聚焦的问题
## 1.4.0 2021-04-29
- 新增 type 属性的 left\right 值,支持左右弹出
- 新增 open(String:type) 方法参数 ,可以省略 type 属性 ,直接传入类型打开指定弹窗
- 新增 backgroundColor 属性,可定义主窗口背景色,默认不显示背景色
- 新增 safeArea 属性,是否适配底部安全区
- 修复 App\h5\微信小程序底部安全区占位不对的Bug
- 修复 App 端弹出等待的Bug
- 优化 提升低配设备性能,优化动画卡顿问题
- 优化 更简单的组件自定义方式
## 1.2.92021-02-05
- 优化 组件引用关系通过uni_modules引用组件
## 1.2.82021-02-05
- 调整为uni_modules目录规范
## 1.2.72021-02-05
- 调整为uni_modules目录规范
- 新增 支持 PC 端
- 新增 uni-popup-message 、uni-popup-dialog扩展组件支持 PC 端

View File

@@ -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

View File

@@ -0,0 +1,275 @@
<template>
<view class="uni-popup-dialog">
<view class="uni-dialog-title">
<text class="uni-dialog-title-text" :class="['uni-popup__'+dialogType]">{{titleText}}</text>
</view>
<view v-if="mode === 'base'" class="uni-dialog-content">
<slot>
<text class="uni-dialog-content-text">{{content}}</text>
</slot>
</view>
<view v-else class="uni-dialog-content">
<slot>
<input class="uni-dialog-input" v-model="val" :type="inputType" :placeholder="placeholderText" :focus="focus" >
</slot>
</view>
<view class="uni-dialog-button-group">
<view class="uni-dialog-button" @click="closeDialog">
<text class="uni-dialog-button-text">{{closeText}}</text>
</view>
<view class="uni-dialog-button uni-border-left" @click="onOk">
<text class="uni-dialog-button-text uni-button-color">{{okText}}</text>
</view>
</view>
</view>
</template>
<script>
import popup from '../uni-popup/popup.js'
import {
initVueI18n
} from '@dcloudio/uni-i18n'
import messages from '../uni-popup/i18n/index.js'
const { t } = initVueI18n(messages)
/**
* PopUp 弹出层-对话框样式
* @description 弹出层-对话框样式
* @tutorial https://ext.dcloud.net.cn/plugin?id=329
* @property {String} value input 模式下的默认值
* @property {String} placeholder input 模式下输入提示
* @property {String} type = [success|warning|info|error] 主题样式
* @value success 成功
* @value warning 提示
* @value info 消息
* @value error 错误
* @property {String} mode = [base|input] 模式、
* @value base 基础对话框
* @value input 可输入对话框
* @property {String} content 对话框内容
* @property {Boolean} beforeClose 是否拦截取消事件
* @event {Function} confirm 点击确认按钮触发
* @event {Function} close 点击取消按钮触发
*/
export default {
name: "uniPopupDialog",
mixins: [popup],
emits:['confirm','close'],
props: {
inputType:{
type: String,
default: 'text'
},
value: {
type: [String, Number],
default: ''
},
placeholder: {
type: [String, Number],
default: ''
},
type: {
type: String,
default: 'error'
},
mode: {
type: String,
default: 'base'
},
title: {
type: String,
default: ''
},
content: {
type: String,
default: ''
},
beforeClose: {
type: Boolean,
default: false
},
cancelText:{
type: String,
default: ''
},
confirmText:{
type: String,
default: ''
}
},
data() {
return {
dialogType: 'error',
focus: false,
val: ""
}
},
computed: {
okText() {
return this.confirmText || t("uni-popup.ok")
},
closeText() {
return this.cancelText || t("uni-popup.cancel")
},
placeholderText() {
return this.placeholder || t("uni-popup.placeholder")
},
titleText() {
return this.title || t("uni-popup.title")
}
},
watch: {
type(val) {
this.dialogType = val
},
mode(val) {
if (val === 'input') {
this.dialogType = 'info'
}
},
value(val) {
this.val = val
}
},
created() {
// 对话框遮罩不可点击
this.popup.disableMask()
// this.popup.closeMask()
if (this.mode === 'input') {
this.dialogType = 'info'
this.val = this.value
} else {
this.dialogType = this.type
}
},
mounted() {
this.focus = true
},
methods: {
/**
* 点击确认按钮
*/
onOk() {
if (this.mode === 'input'){
this.$emit('confirm', this.val)
}else{
this.$emit('confirm')
}
if(this.beforeClose) return
this.popup.close()
},
/**
* 点击取消按钮
*/
closeDialog() {
this.$emit('close')
if(this.beforeClose) return
this.popup.close()
},
close(){
this.popup.close()
}
}
}
</script>
<style lang="scss" >
.uni-popup-dialog {
width: 300px;
border-radius: 11px;
background-color: #fff;
}
.uni-dialog-title {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: center;
padding-top: 25px;
}
.uni-dialog-title-text {
font-size: 16px;
font-weight: 500;
}
.uni-dialog-content {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: center;
align-items: center;
padding: 20px;
}
.uni-dialog-content-text {
font-size: 14px;
color: #6C6C6C;
}
.uni-dialog-button-group {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
border-top-color: #f5f5f5;
border-top-style: solid;
border-top-width: 1px;
}
.uni-dialog-button {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex: 1;
flex-direction: row;
justify-content: center;
align-items: center;
height: 45px;
}
.uni-border-left {
border-left-color: #f0f0f0;
border-left-style: solid;
border-left-width: 1px;
}
.uni-dialog-button-text {
font-size: 16px;
color: #333;
}
.uni-button-color {
color: #007aff;
}
.uni-dialog-input {
flex: 1;
font-size: 14px;
border: 1px #eee solid;
height: 40px;
padding: 0 10px;
border-radius: 5px;
color: #555;
}
.uni-popup__success {
color: #4cd964;
}
.uni-popup__warn {
color: #f0ad4e;
}
.uni-popup__error {
color: #dd524d;
}
.uni-popup__info {
color: #909399;
}
</style>

View File

@@ -0,0 +1,143 @@
<template>
<view class="uni-popup-message">
<view class="uni-popup-message__box fixforpc-width" :class="'uni-popup__'+type">
<slot>
<text class="uni-popup-message-text" :class="'uni-popup__'+type+'-text'">{{message}}</text>
</slot>
</view>
</view>
</template>
<script>
import popup from '../uni-popup/popup.js'
/**
* PopUp 弹出层-消息提示
* @description 弹出层-消息提示
* @tutorial https://ext.dcloud.net.cn/plugin?id=329
* @property {String} type = [success|warning|info|error] 主题样式
* @value success 成功
* @value warning 提示
* @value info 消息
* @value error 错误
* @property {String} message 消息提示文字
* @property {String} duration 显示时间,设置为 0 则不会自动关闭
*/
export default {
name: 'uniPopupMessage',
mixins:[popup],
props: {
/**
* 主题 success/warning/info/error 默认 success
*/
type: {
type: String,
default: 'success'
},
/**
* 消息文字
*/
message: {
type: String,
default: ''
},
/**
* 显示时间,设置为 0 则不会自动关闭
*/
duration: {
type: Number,
default: 3000
},
maskShow:{
type:Boolean,
default:false
}
},
data() {
return {}
},
created() {
this.popup.maskShow = this.maskShow
this.popup.messageChild = this
},
methods: {
timerClose(){
if(this.duration === 0) return
clearTimeout(this.timer)
this.timer = setTimeout(()=>{
this.popup.close()
},this.duration)
}
}
}
</script>
<style lang="scss" >
.uni-popup-message {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: center;
}
.uni-popup-message__box {
background-color: #e1f3d8;
padding: 10px 15px;
border-color: #eee;
border-style: solid;
border-width: 1px;
flex: 1;
}
@media screen and (min-width: 500px) {
.fixforpc-width {
margin-top: 20px;
border-radius: 4px;
flex: none;
min-width: 380px;
/* #ifndef APP-NVUE */
max-width: 50%;
/* #endif */
/* #ifdef APP-NVUE */
max-width: 500px;
/* #endif */
}
}
.uni-popup-message-text {
font-size: 14px;
padding: 0;
}
.uni-popup__success {
background-color: #e1f3d8;
}
.uni-popup__success-text {
color: #67C23A;
}
.uni-popup__warn {
background-color: #faecd8;
}
.uni-popup__warn-text {
color: #E6A23C;
}
.uni-popup__error {
background-color: #fde2e2;
}
.uni-popup__error-text {
color: #F56C6C;
}
.uni-popup__info {
background-color: #F2F6FC;
}
.uni-popup__info-text {
color: #909399;
}
</style>

View File

@@ -0,0 +1,187 @@
<template>
<view class="uni-popup-share">
<view class="uni-share-title"><text class="uni-share-title-text">{{shareTitleText}}</text></view>
<view class="uni-share-content">
<view class="uni-share-content-box">
<view class="uni-share-content-item" v-for="(item,index) in bottomData" :key="index" @click.stop="select(item,index)">
<image class="uni-share-image" :src="item.icon" mode="aspectFill"></image>
<text class="uni-share-text">{{item.text}}</text>
</view>
</view>
</view>
<view class="uni-share-button-box">
<button class="uni-share-button" @click="close">{{cancelText}}</button>
</view>
</view>
</template>
<script>
import popup from '../uni-popup/popup.js'
import {
initVueI18n
} from '@dcloudio/uni-i18n'
import messages from '../uni-popup/i18n/index.js'
const { t } = initVueI18n(messages)
export default {
name: 'UniPopupShare',
mixins:[popup],
emits:['select'],
props: {
title: {
type: String,
default: ''
},
beforeClose: {
type: Boolean,
default: false
}
},
data() {
return {
bottomData: [{
text: '微信消息',
icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/c2b17470-50be-11eb-b680-7980c8a877b8.png',
name: 'wx'
},
{
text: '朋友圈',
icon: 'http://ehh-public-01.oss-cn-beijing.aliyuncs.com/image/20240626131115.png',
name: 'wx'
},
// {
// text: 'QQ',
// icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/e7a79520-50be-11eb-b997-9918a5dda011.png',
// name: 'qq'
// },
// {
// text: '新浪',
// icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/0dacdbe0-50bf-11eb-8ff1-d5dcf8779628.png',
// name: 'sina'
// },
// {
// text: '百度',
// icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/1ec6e920-50bf-11eb-8a36-ebb87efcf8c0.png',
// name: 'copy'
// },
// {
// text: '其他',
// icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/2e0fdfe0-50bf-11eb-b997-9918a5dda011.png',
// name: 'more'
// }
]
}
},
created() {},
computed: {
cancelText() {
return t("uni-popup.cancel")
},
shareTitleText() {
return this.title || t("uni-popup.shareTitle")
}
},
methods: {
/**
* 选择内容
*/
select(item, index) {
this.$emit('select', {
item,
index
})
this.close()
},
/**
* 关闭窗口
*/
close() {
if(this.beforeClose) return
this.popup.close()
}
}
}
</script>
<style lang="scss" >
.uni-popup-share {
background-color: #fff;
border-top-left-radius: 11px;
border-top-right-radius: 11px;
}
.uni-share-title {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: center;
height: 40px;
}
.uni-share-title-text {
font-size: 14px;
color: #666;
}
.uni-share-content {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: center;
padding-top: 10px;
}
.uni-share-content-box {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
flex-wrap: wrap;
width: 360px;
}
.uni-share-content-item {
width: 90px;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: center;
padding: 10px 0;
align-items: center;
}
.uni-share-content-item:active {
background-color: #f5f5f5;
}
.uni-share-image {
width: 30px;
height: 30px;
}
.uni-share-text {
margin-top: 10px;
font-size: 14px;
color: #3B4144;
}
.uni-share-button-box {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
padding: 10px 15px;
}
.uni-share-button {
flex: 1;
border-radius: 50px;
color: #666;
font-size: 16px;
}
.uni-share-button::after {
border-radius: 50px;
}
</style>

View File

@@ -0,0 +1,7 @@
{
"uni-popup.cancel": "cancel",
"uni-popup.ok": "ok",
"uni-popup.placeholder": "pleace enter",
"uni-popup.title": "Hint",
"uni-popup.shareTitle": "Share to"
}

View File

@@ -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
}

View File

@@ -0,0 +1,7 @@
{
"uni-popup.cancel": "取消",
"uni-popup.ok": "确定",
"uni-popup.placeholder": "请输入",
"uni-popup.title": "提示",
"uni-popup.shareTitle": "分享到"
}

View File

@@ -0,0 +1,7 @@
{
"uni-popup.cancel": "取消",
"uni-popup.ok": "確定",
"uni-popup.placeholder": "請輸入",
"uni-popup.title": "提示",
"uni-popup.shareTitle": "分享到"
}

View File

@@ -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

View File

@@ -0,0 +1,26 @@
export default {
data() {
return {
}
},
created(){
this.popup = this.getParent()
},
methods:{
/**
* 获取父元素实例
*/
getParent(name = 'uniPopup') {
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;
},
}
}

View File

@@ -0,0 +1,473 @@
<template>
<view v-if="showPopup" class="uni-popup" :class="[popupstyle, isDesktop ? 'fixforpc-z-index' : '']">
<view @touchstart="touchstart">
<uni-transition key="1" v-if="maskShow" name="mask" mode-class="fade" :styles="maskClass"
:duration="duration" :show="showTrans" @click="onTap" />
<uni-transition key="2" :mode-class="ani" name="content" :styles="transClass" :duration="duration"
:show="showTrans" @click="onTap">
<view class="uni-popup__wrapper" :style="{ backgroundColor: bg }" :class="[popupstyle]" @click="clear">
<slot />
</view>
</uni-transition>
</view>
<!-- #ifdef H5 -->
<keypress v-if="maskShow" @esc="onTap" />
<!-- #endif -->
</view>
</template>
<script>
// #ifdef H5
import keypress from './keypress.js'
// #endif
/**
* PopUp 弹出层
* @description 弹出层组件,为了解决遮罩弹层的问题
* @tutorial https://ext.dcloud.net.cn/plugin?id=329
* @property {String} type = [top|center|bottom|left|right|message|dialog|share] 弹出方式
* @value top 顶部弹出
* @value center 中间弹出
* @value bottom 底部弹出
* @value left 左侧弹出
* @value right 右侧弹出
* @value message 消息提示
* @value dialog 对话框
* @value share 底部分享示例
* @property {Boolean} animation = [true|false] 是否开启动画
* @property {Boolean} maskClick = [true|false] 蒙版点击是否关闭弹窗(废弃)
* @property {Boolean} isMaskClick = [true|false] 蒙版点击是否关闭弹窗
* @property {String} backgroundColor 主窗口背景色
* @property {String} maskBackgroundColor 蒙版颜色
* @property {Boolean} safeArea 是否适配底部安全区
* @event {Function} change 打开关闭弹窗触发e={show: false}
* @event {Function} maskClick 点击遮罩触发
*/
export default {
name: 'uniPopup',
components: {
// #ifdef H5
keypress
// #endif
},
emits: ['change', 'maskClick'],
props: {
// 开启动画
animation: {
type: Boolean,
default: true
},
// 弹出层类型可选值top: 顶部弹出层bottom底部弹出层center全屏弹出层
// message: 消息提示 ; dialog : 对话框
type: {
type: String,
default: 'center'
},
// maskClick
isMaskClick: {
type: Boolean,
default: null
},
// TODO 2 个版本后废弃属性 ,使用 isMaskClick
maskClick: {
type: Boolean,
default: null
},
backgroundColor: {
type: String,
default: 'none'
},
safeArea: {
type: Boolean,
default: true
},
maskBackgroundColor: {
type: String,
default: 'rgba(0, 0, 0, 0.4)'
},
},
watch: {
/**
* 监听type类型
*/
type: {
handler: function(type) {
if (!this.config[type]) return
this[this.config[type]](true)
},
immediate: true
},
isDesktop: {
handler: function(newVal) {
if (!this.config[newVal]) return
this[this.config[this.type]](true)
},
immediate: true
},
/**
* 监听遮罩是否可点击
* @param {Object} val
*/
maskClick: {
handler: function(val) {
this.mkclick = val
},
immediate: true
},
isMaskClick: {
handler: function(val) {
this.mkclick = val
},
immediate: true
},
// H5 下禁止底部滚动
showPopup(show) {
// #ifdef H5
// fix by mehaotian 处理 h5 滚动穿透的问题
document.getElementsByTagName('body')[0].style.overflow = show ? 'hidden' : 'visible'
// #endif
}
},
data() {
return {
duration: 300,
ani: [],
showPopup: false,
showTrans: false,
popupWidth: 0,
popupHeight: 0,
config: {
top: 'top',
bottom: 'bottom',
center: 'center',
left: 'left',
right: 'right',
message: 'top',
dialog: 'center',
share: 'bottom'
},
maskClass: {
position: 'fixed',
bottom: 0,
top: 0,
left: 0,
right: 0,
backgroundColor: 'rgba(0, 0, 0, 0.4)'
},
transClass: {
position: 'fixed',
left: 0,
right: 0
},
maskShow: true,
mkclick: true,
popupstyle: this.isDesktop ? 'fixforpc-top' : 'top'
}
},
computed: {
isDesktop() {
return this.popupWidth >= 500 && this.popupHeight >= 500
},
bg() {
if (this.backgroundColor === '' || this.backgroundColor === 'none') {
return 'transparent'
}
return this.backgroundColor
}
},
mounted() {
const fixSize = () => {
const {
windowWidth,
windowHeight,
windowTop,
safeArea,
screenHeight,
safeAreaInsets
} = uni.getSystemInfoSync()
this.popupWidth = windowWidth
this.popupHeight = windowHeight + (windowTop || 0)
// TODO fix by mehaotian 是否适配底部安全区 ,目前微信ios 、和 app ios 计算有差异,需要框架修复
if (safeArea && this.safeArea) {
// #ifdef MP-WEIXIN
this.safeAreaInsets = screenHeight - safeArea.bottom
// #endif
// #ifndef MP-WEIXIN
this.safeAreaInsets = safeAreaInsets.bottom
// #endif
} else {
this.safeAreaInsets = 0
}
}
fixSize()
// #ifdef H5
// window.addEventListener('resize', fixSize)
// this.$once('hook:beforeDestroy', () => {
// window.removeEventListener('resize', fixSize)
// })
// #endif
},
// #ifndef VUE3
// TODO vue2
destroyed() {
this.setH5Visible()
},
// #endif
// #ifdef VUE3
// TODO vue3
unmounted() {
this.setH5Visible()
},
// #endif
created() {
// this.mkclick = this.isMaskClick || this.maskClick
if (this.isMaskClick === null && this.maskClick === null) {
this.mkclick = true
} else {
this.mkclick = this.isMaskClick !== null ? this.isMaskClick : this.maskClick
}
if (this.animation) {
this.duration = 300
} else {
this.duration = 0
}
// TODO 处理 message 组件生命周期异常的问题
this.messageChild = null
// TODO 解决头条冒泡的问题
this.clearPropagation = false
this.maskClass.backgroundColor = this.maskBackgroundColor
},
methods: {
setH5Visible() {
// #ifdef H5
// fix by mehaotian 处理 h5 滚动穿透的问题
document.getElementsByTagName('body')[0].style.overflow = 'visible'
// #endif
},
/**
* 公用方法,不显示遮罩层
*/
closeMask() {
this.maskShow = false
},
/**
* 公用方法,遮罩层禁止点击
*/
disableMask() {
this.mkclick = false
},
// TODO nvue 取消冒泡
clear(e) {
// #ifndef APP-NVUE
e.stopPropagation()
// #endif
this.clearPropagation = true
},
open(direction) {
// fix by mehaotian 处理快速打开关闭的情况
if (this.showPopup) {
return
}
let innerType = ['top', 'center', 'bottom', 'left', 'right', 'message', 'dialog', 'share']
if (!(direction && innerType.indexOf(direction) !== -1)) {
direction = this.type
}
if (!this.config[direction]) {
console.error('缺少类型:', direction)
return
}
this[this.config[direction]]()
this.$emit('change', {
show: true,
type: direction
})
},
close(type) {
this.showTrans = false
this.$emit('change', {
show: false,
type: this.type
})
clearTimeout(this.timer)
// // 自定义关闭事件
// this.customOpen && this.customClose()
this.timer = setTimeout(() => {
this.showPopup = false
}, 300)
},
// TODO 处理冒泡事件,头条的冒泡事件有问题 ,先这样兼容
touchstart() {
this.clearPropagation = false
},
onTap() {
if (this.clearPropagation) {
// fix by mehaotian 兼容 nvue
this.clearPropagation = false
return
}
this.$emit('maskClick')
if (!this.mkclick) return
this.close()
},
/**
* 顶部弹出样式处理
*/
top(type) {
this.popupstyle = this.isDesktop ? 'fixforpc-top' : 'top'
this.ani = ['slide-top']
this.transClass = {
position: 'fixed',
left: 0,
right: 0,
backgroundColor: this.bg
}
// TODO 兼容 type 属性 ,后续会废弃
if (type) return
this.showPopup = true
this.showTrans = true
this.$nextTick(() => {
if (this.messageChild && this.type === 'message') {
this.messageChild.timerClose()
}
})
},
/**
* 底部弹出样式处理
*/
bottom(type) {
this.popupstyle = 'bottom'
this.ani = ['slide-bottom']
this.transClass = {
position: 'fixed',
left: 0,
right: 0,
bottom: 0,
paddingBottom: this.safeAreaInsets + 'px',
backgroundColor: this.bg
}
// TODO 兼容 type 属性 ,后续会废弃
if (type) return
this.showPopup = true
this.showTrans = true
},
/**
* 中间弹出样式处理
*/
center(type) {
this.popupstyle = 'center'
this.ani = ['zoom-out', 'fade']
this.transClass = {
position: 'fixed',
/* #ifndef APP-NVUE */
display: 'flex',
flexDirection: 'column',
/* #endif */
bottom: 0,
left: 0,
right: 0,
top: 0,
justifyContent: 'center',
alignItems: 'center'
}
// TODO 兼容 type 属性 ,后续会废弃
if (type) return
this.showPopup = true
this.showTrans = true
},
left(type) {
this.popupstyle = 'left'
this.ani = ['slide-left']
this.transClass = {
position: 'fixed',
left: 0,
bottom: 0,
top: 0,
backgroundColor: this.bg,
/* #ifndef APP-NVUE */
display: 'flex',
flexDirection: 'column'
/* #endif */
}
// TODO 兼容 type 属性 ,后续会废弃
if (type) return
this.showPopup = true
this.showTrans = true
},
right(type) {
this.popupstyle = 'right'
this.ani = ['slide-right']
this.transClass = {
position: 'fixed',
bottom: 0,
right: 0,
top: 0,
backgroundColor: this.bg,
/* #ifndef APP-NVUE */
display: 'flex',
flexDirection: 'column'
/* #endif */
}
// TODO 兼容 type 属性 ,后续会废弃
if (type) return
this.showPopup = true
this.showTrans = true
}
}
}
</script>
<style lang="scss">
.uni-popup {
position: fixed;
/* #ifndef APP-NVUE */
z-index: 99;
/* #endif */
&.top,
&.left,
&.right {
/* #ifdef H5 */
top: var(--window-top);
/* #endif */
/* #ifndef H5 */
top: 0;
/* #endif */
}
.uni-popup__wrapper {
/* #ifndef APP-NVUE */
display: block;
/* #endif */
position: relative;
/* iphonex 等安全区设置,底部安全区适配 */
/* #ifndef APP-NVUE */
// padding-bottom: constant(safe-area-inset-bottom);
// padding-bottom: env(safe-area-inset-bottom);
/* #endif */
&.left,
&.right {
/* #ifdef H5 */
padding-top: var(--window-top);
/* #endif */
/* #ifndef H5 */
padding-top: 0;
/* #endif */
flex: 1;
}
}
}
.fixforpc-z-index {
/* #ifndef APP-NVUE */
z-index: 999;
/* #endif */
}
.fixforpc-top {
top: 0;
}
</style>

View File

@@ -0,0 +1,87 @@
{
"id": "uni-popup",
"displayName": "uni-popup 弹出层",
"version": "1.8.3",
"description": " Popup 组件,提供常用的弹层",
"keywords": [
"uni-ui",
"弹出层",
"弹窗",
"popup",
"弹框"
],
"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",
"uni-transition"
],
"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"
}
}
}
}
}

View File

@@ -0,0 +1,17 @@
## Popup 弹出层
> **组件名uni-popup**
> 代码块: `uPopup`
> 关联组件:`uni-transition`
弹出层组件,在应用中弹出一个消息提示窗口、提示框等
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-popup)
#### 如使用过程中有任何问题或者您对uni-ui有一些好的建议欢迎加入 uni-ui 交流群871950839

View File

@@ -0,0 +1,22 @@
## 1.3.22023-05-04
- 修复 NVUE 平台报错的问题
## 1.3.12021-11-23
- 修复 init 方法初始化问题
## 1.3.02021-11-19
- 优化 组件UI并提供设计资源详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-transition](https://uniapp.dcloud.io/component/uniui/uni-transition)
## 1.2.12021-09-27
- 修复 init 方法不生效的 Bug
## 1.2.02021-07-30
- 组件兼容 vue3如何创建 vue3 项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 1.1.12021-05-12
- 新增 示例地址
- 修复 示例项目缺少组件的 Bug
## 1.1.02021-04-22
- 新增 通过方法自定义动画
- 新增 custom-class 非 NVUE 平台支持自定义 class 定制样式
- 优化 动画触发逻辑,使动画更流畅
- 优化 支持单独的动画类型
- 优化 文档示例
## 1.0.22021-02-05
- 调整为 uni_modules 目录规范

View File

@@ -0,0 +1,131 @@
// const defaultOption = {
// duration: 300,
// timingFunction: 'linear',
// delay: 0,
// transformOrigin: '50% 50% 0'
// }
// #ifdef APP-NVUE
const nvueAnimation = uni.requireNativePlugin('animation')
// #endif
class MPAnimation {
constructor(options, _this) {
this.options = options
// 在iOS10+QQ小程序平台下传给原生的对象一定是个普通对象而不是Proxy对象否则会报parameter should be Object instead of ProxyObject的错误
this.animation = uni.createAnimation({
...options
})
this.currentStepAnimates = {}
this.next = 0
this.$ = _this
}
_nvuePushAnimates(type, args) {
let aniObj = this.currentStepAnimates[this.next]
let styles = {}
if (!aniObj) {
styles = {
styles: {},
config: {}
}
} else {
styles = aniObj
}
if (animateTypes1.includes(type)) {
if (!styles.styles.transform) {
styles.styles.transform = ''
}
let unit = ''
if(type === 'rotate'){
unit = 'deg'
}
styles.styles.transform += `${type}(${args+unit}) `
} else {
styles.styles[type] = `${args}`
}
this.currentStepAnimates[this.next] = styles
}
_animateRun(styles = {}, config = {}) {
let ref = this.$.$refs['ani'].ref
if (!ref) return
return new Promise((resolve, reject) => {
nvueAnimation.transition(ref, {
styles,
...config
}, res => {
resolve()
})
})
}
_nvueNextAnimate(animates, step = 0, fn) {
let obj = animates[step]
if (obj) {
let {
styles,
config
} = obj
this._animateRun(styles, config).then(() => {
step += 1
this._nvueNextAnimate(animates, step, fn)
})
} else {
this.currentStepAnimates = {}
typeof fn === 'function' && fn()
this.isEnd = true
}
}
step(config = {}) {
// #ifndef APP-NVUE
this.animation.step(config)
// #endif
// #ifdef APP-NVUE
this.currentStepAnimates[this.next].config = Object.assign({}, this.options, config)
this.currentStepAnimates[this.next].styles.transformOrigin = this.currentStepAnimates[this.next].config.transformOrigin
this.next++
// #endif
return this
}
run(fn) {
// #ifndef APP-NVUE
this.$.animationData = this.animation.export()
this.$.timer = setTimeout(() => {
typeof fn === 'function' && fn()
}, this.$.durationTime)
// #endif
// #ifdef APP-NVUE
this.isEnd = false
let ref = this.$.$refs['ani'] && this.$.$refs['ani'].ref
if(!ref) return
this._nvueNextAnimate(this.currentStepAnimates, 0, fn)
this.next = 0
// #endif
}
}
const animateTypes1 = ['matrix', 'matrix3d', 'rotate', 'rotate3d', 'rotateX', 'rotateY', 'rotateZ', 'scale', 'scale3d',
'scaleX', 'scaleY', 'scaleZ', 'skew', 'skewX', 'skewY', 'translate', 'translate3d', 'translateX', 'translateY',
'translateZ'
]
const animateTypes2 = ['opacity', 'backgroundColor']
const animateTypes3 = ['width', 'height', 'left', 'right', 'top', 'bottom']
animateTypes1.concat(animateTypes2, animateTypes3).forEach(type => {
MPAnimation.prototype[type] = function(...args) {
// #ifndef APP-NVUE
this.animation[type](...args)
// #endif
// #ifdef APP-NVUE
this._nvuePushAnimates(type, args)
// #endif
return this
}
})
export function createAnimation(option, _this) {
if(!_this) return
clearTimeout(_this.timer)
return new MPAnimation(option, _this)
}

View File

@@ -0,0 +1,286 @@
<template>
<!-- #ifndef APP-NVUE -->
<view v-show="isShow" ref="ani" :animation="animationData" :class="customClass" :style="transformStyles" @click="onClick"><slot></slot></view>
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
<view v-if="isShow" ref="ani" :animation="animationData" :class="customClass" :style="transformStyles" @click="onClick"><slot></slot></view>
<!-- #endif -->
</template>
<script>
import { createAnimation } from './createAnimation'
/**
* Transition 过渡动画
* @description 简单过渡动画组件
* @tutorial https://ext.dcloud.net.cn/plugin?id=985
* @property {Boolean} show = [false|true] 控制组件显示或隐藏
* @property {Array|String} modeClass = [fade|slide-top|slide-right|slide-bottom|slide-left|zoom-in|zoom-out] 过渡动画类型
* @value fade 渐隐渐出过渡
* @value slide-top 由上至下过渡
* @value slide-right 由右至左过渡
* @value slide-bottom 由下至上过渡
* @value slide-left 由左至右过渡
* @value zoom-in 由小到大过渡
* @value zoom-out 由大到小过渡
* @property {Number} duration 过渡动画持续时间
* @property {Object} styles 组件样式,同 css 样式,注意带’-‘连接符的属性需要使用小驼峰写法如:`backgroundColor:red`
*/
export default {
name: 'uniTransition',
emits:['click','change'],
props: {
show: {
type: Boolean,
default: false
},
modeClass: {
type: [Array, String],
default() {
return 'fade'
}
},
duration: {
type: Number,
default: 300
},
styles: {
type: Object,
default() {
return {}
}
},
customClass:{
type: String,
default: ''
},
onceRender:{
type:Boolean,
default:false
},
},
data() {
return {
isShow: false,
transform: '',
opacity: 1,
animationData: {},
durationTime: 300,
config: {}
}
},
watch: {
show: {
handler(newVal) {
if (newVal) {
this.open()
} else {
// 避免上来就执行 close,导致动画错乱
if (this.isShow) {
this.close()
}
}
},
immediate: true
}
},
computed: {
// 生成样式数据
stylesObject() {
let styles = {
...this.styles,
'transition-duration': this.duration / 1000 + 's'
}
let transform = ''
for (let i in styles) {
let line = this.toLine(i)
transform += line + ':' + styles[i] + ';'
}
return transform
},
// 初始化动画条件
transformStyles() {
return 'transform:' + this.transform + ';' + 'opacity:' + this.opacity + ';' + this.stylesObject
}
},
created() {
// 动画默认配置
this.config = {
duration: this.duration,
timingFunction: 'ease',
transformOrigin: '50% 50%',
delay: 0
}
this.durationTime = this.duration
},
methods: {
/**
* ref 触发 初始化动画
*/
init(obj = {}) {
if (obj.duration) {
this.durationTime = obj.duration
}
this.animation = createAnimation(Object.assign(this.config, obj),this)
},
/**
* 点击组件触发回调
*/
onClick() {
this.$emit('click', {
detail: this.isShow
})
},
/**
* ref 触发 动画分组
* @param {Object} obj
*/
step(obj, config = {}) {
if (!this.animation) return
for (let i in obj) {
try {
if(typeof obj[i] === 'object'){
this.animation[i](...obj[i])
}else{
this.animation[i](obj[i])
}
} catch (e) {
console.error(`方法 ${i} 不存在`)
}
}
this.animation.step(config)
return this
},
/**
* ref 触发 执行动画
*/
run(fn) {
if (!this.animation) return
this.animation.run(fn)
},
// 开始过度动画
open() {
clearTimeout(this.timer)
this.transform = ''
this.isShow = true
let { opacity, transform } = this.styleInit(false)
if (typeof opacity !== 'undefined') {
this.opacity = opacity
}
this.transform = transform
// 确保动态样式已经生效后,执行动画,如果不加 nextTick ,会导致 wx 动画执行异常
this.$nextTick(() => {
// TODO 定时器保证动画完全执行,目前有些问题,后面会取消定时器
this.timer = setTimeout(() => {
this.animation = createAnimation(this.config, this)
this.tranfromInit(false).step()
this.animation.run()
this.$emit('change', {
detail: this.isShow
})
}, 20)
})
},
// 关闭过度动画
close(type) {
if (!this.animation) return
this.tranfromInit(true)
.step()
.run(() => {
this.isShow = false
this.animationData = null
this.animation = null
let { opacity, transform } = this.styleInit(false)
this.opacity = opacity || 1
this.transform = transform
this.$emit('change', {
detail: this.isShow
})
})
},
// 处理动画开始前的默认样式
styleInit(type) {
let styles = {
transform: ''
}
let buildStyle = (type, mode) => {
if (mode === 'fade') {
styles.opacity = this.animationType(type)[mode]
} else {
styles.transform += this.animationType(type)[mode] + ' '
}
}
if (typeof this.modeClass === 'string') {
buildStyle(type, this.modeClass)
} else {
this.modeClass.forEach(mode => {
buildStyle(type, mode)
})
}
return styles
},
// 处理内置组合动画
tranfromInit(type) {
let buildTranfrom = (type, mode) => {
let aniNum = null
if (mode === 'fade') {
aniNum = type ? 0 : 1
} else {
aniNum = type ? '-100%' : '0'
if (mode === 'zoom-in') {
aniNum = type ? 0.8 : 1
}
if (mode === 'zoom-out') {
aniNum = type ? 1.2 : 1
}
if (mode === 'slide-right') {
aniNum = type ? '100%' : '0'
}
if (mode === 'slide-bottom') {
aniNum = type ? '100%' : '0'
}
}
this.animation[this.animationMode()[mode]](aniNum)
}
if (typeof this.modeClass === 'string') {
buildTranfrom(type, this.modeClass)
} else {
this.modeClass.forEach(mode => {
buildTranfrom(type, mode)
})
}
return this.animation
},
animationType(type) {
return {
fade: type ? 1 : 0,
'slide-top': `translateY(${type ? '0' : '-100%'})`,
'slide-right': `translateX(${type ? '0' : '100%'})`,
'slide-bottom': `translateY(${type ? '0' : '100%'})`,
'slide-left': `translateX(${type ? '0' : '-100%'})`,
'zoom-in': `scaleX(${type ? 1 : 0.8}) scaleY(${type ? 1 : 0.8})`,
'zoom-out': `scaleX(${type ? 1 : 1.2}) scaleY(${type ? 1 : 1.2})`
}
},
// 内置动画类型与实际动画对应字典
animationMode() {
return {
fade: 'opacity',
'slide-top': 'translateY',
'slide-right': 'translateX',
'slide-bottom': 'translateY',
'slide-left': 'translateX',
'zoom-in': 'scale',
'zoom-out': 'scale'
}
},
// 驼峰转中横线
toLine(name) {
return name.replace(/([A-Z])/g, '-$1').toLowerCase()
}
}
}
</script>
<style></style>

View File

@@ -0,0 +1,84 @@
{
"id": "uni-transition",
"displayName": "uni-transition 过渡动画",
"version": "1.3.2",
"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": ["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"
}
}
}
}
}

View File

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

View File

@@ -0,0 +1,51 @@
## 0.6.02023-02-24
- 修复 升级中心安卓应用市场不显示的Bug
## 0.5.12022-07-06
- 修复 上版带出云函数不存在的Bug
- 升级 uni-admin 大于等于 1.9.0 务必更新至此版本。uni-admin 版本小于 1.9.0 请不要更新,历史版本在 Gitee 有发行版。后续 uni-admin 会集成升级中心
## 0.5.02022-07-05
- 修复 版本列表默认显示全部版本的Bug
- 升级 uni-admin 1.9.0 务必更新至此版本。uni-admin 版本小于 1.9.0 请不要更新,后续 uni-admin 会集成升级中心
## 0.4.22021-12-07
- 更新 优化 list 页面显示,修复 list 页面报错
## 0.4.12021-12-01
- 修复 0.4.0版本带出来,发布新版时 appid、name 不会自动填充的Bug
## 0.4.02021-11-26
- 更新 升级中心移除应用管理现在由uni-admin接管。旧版本若没有应用管理请做升级处理
## 0.3.02021-11-18
- 兼容 uni-admin 新版内置 $request 函数改动
## 0.2.22021-09-06
- 解决 opendb-app-list表对应的schema名称冲突的问题
## 0.2.12021-09-03
- 修复 一个在添加菜单时报错createdate不与默认值匹配 的Bug
## 0.2.02021-08-25
- 兼容vue3.0
## 0.1.92021-08-13
- 更新 uni-forms使用validate校验字段
- 修复 报错dirty_data、create_date在数据库中并不存在
## 0.1.82021-08-09
- 修复 默认配置项配置错误
## 0.1.72021-08-09
- 移除测试时配置项
## 0.1.62021-08-09
- 修复 修改版本信息时,上传时间丢失问题
## 0.1.52021-07-21
- 更新 :value.sync 改为 :value 和 @update:value
## 0.1.42021-07-13
- 修复 uni-easyinput去除输入字符长度限制
- 更新文档 关于 uni-id缺少配置信息 错误。请查看安装指引第13条
## 0.1.32021-06-15
- 修复 wgt更新某些情况下获取数据错误
## 0.1.22021-06-04
- 修复 上传包时根据平台筛选文件
- 更新 文档
## 0.1.12021-05-18
- 更新uni-table中uni-tr组件的selectable属性为disabled
## 0.1.02021-04-07
- 更新版本对比函数 compare
## 0.0.62021-04-01
- 调整db_init.json
## 0.0.52021-03-25
- 调整为uni_modules目录
- 升级中心后台管理系统拆分为 Admin 后台管理 和 前台检查更新uni-upgrade-center-app

View File

@@ -0,0 +1,91 @@
{
"id": "uni-upgrade-center",
"displayName": "升级中心 uni-upgrade-center - Admin",
"version": "0.6.0",
"description": "uni升级中心 - 后台管理系统",
"keywords": [
"uniCloud",
"admin",
"update",
"升级",
"wgt"
],
"repository": "https://gitee.com/dcloud/uni-upgrade-center/tree/master/uni_modules/uni-upgrade-center",
"engines": {
"HBuilderX": "^3.3.10"
},
"dcloudext": {
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "插件不采集任何数据",
"permissions": "无"
},
"npmurl": "",
"type": "unicloud-admin"
},
"uni_modules": {
"dependencies": [
"uni-data-checkbox",
"uni-data-picker",
"uni-dateformat",
"uni-easyinput",
"uni-file-picker",
"uni-forms",
"uni-icons",
"uni-pagination",
"uni-table"
],
"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"
}
}
}
}
}

View File

@@ -0,0 +1,52 @@
<template>
<view style="position: relative;">
<uni-icons @mouseenter.native="mouseenter" @mouseleave.native="showStableInfo = false"
style="padding:0 10px;color: #a8a8a8;cursor: pointer;" type="info" />
<view v-if="showStableInfo" class="show-stable" :style="{top:`${top}px`,left:`${left}px`}">
<text>{{content}}</text>
</view>
</view>
</template>
<script>
export default {
props: {
content: String,
top: {
type: [Number, String],
default: -60
},
left: {
type: [Number, String],
default: -100
}
},
data() {
return {
showStableInfo: false,
arrowStyle: {}
}
},
methods: {
mouseenter(e) {
this.showStableInfo = true
}
}
}
</script>
<style lang="scss" scoped>
$main_color: #fff;
$main_back_color: #303133;
.show-stable {
width: 200px;
position: absolute;
padding: 5px 10px;
background-color: $main_back_color;
color: $main_color;
border-radius: 4px;
border: 1px solid #e9e9eb;
z-index: 99999;
}
</style>

View File

@@ -0,0 +1,187 @@
import {
validator,
enumConverter
} from '@/js_sdk/validator/opendb-app-versions.js';
const platform_iOS = 'iOS';
const platform_Android = 'Android';
const db = uniCloud.database();
function getValidator(fields) {
let reuslt = {}
for (let key in validator) {
if (fields.includes(key)) {
reuslt[key] = validator[key]
}
}
return reuslt
}
export const fields =
'appid,name,title,contents,platform,type,version,min_uni_version,url,stable_publish,is_silently,is_mandatory,create_date,store_list'
export default {
data() {
return {
labelWidth: '80px',
enableiOSWgt: true, // 是否开启iOS的wgt更新
silentlyContent: '静默更新App升级时会在后台下载wgt包并自行安装。新功能在下次启动App时生效',
mandatoryContent: '强制更新App升级弹出框不可取消',
stablePublishContent: '同时只可有一个线上发行版,线上发行不可更设为下线。\n未上线可以设为上线发行并自动替换当前线上发行版',
stablePublishContent2: '使用本包替换当前线上发行版',
uploadFileContent: '可下载安装包地址。上传文件到云存储自动填写,也可以手动填写',
minUniVersionContent: '上次使用新Api或打包新模块的App版本',
priorityContent: '检查更新时按照优先级从大到小依次尝试跳转商店。如果都跳转失败则会打开浏览器使用下载链接下载apk安装包',
latestStableData: [], // 库中最新已上线版
appFileList: null, // 上传包
type_valuetotext: enumConverter.type_valuetotext,
preUrl: '',
formData: {
"appid": "",
"name": "",
"title": "",
"contents": "",
"platform": [],
"store_list": [],
"type": "",
"version": "",
"min_uni_version": "",
"url": "",
"stable_publish": false,
"create_date": null
},
formOptions: {
"platform_localdata": [{
"value": "Android",
"text": "安卓"
},
{
"value": "iOS",
"text": "苹果"
}
],
"type_localdata": [{
"value": "native_app",
"text": "原生App安装包"
},
{
"value": "wgt",
"text": "App资源包"
}
]
},
rules: {
...getValidator([
"appid", "contents", "platform", "type",
"version", "min_uni_version", "url", "stable_publish",
"title", "name", "is_silently", "is_mandatory", "store_list"
])
}
}
},
onReady() {
this.$refs.form.setRules(this.rules)
},
computed: {
isWGT() {
return this.formData.type === 'wgt'
},
isiOS() {
return !this.isWGT ? this.formData.platform.includes(platform_iOS) : false;
},
hasPackage() {
return this.appFileList && !!Object.keys(this.appFileList).length
},
fileExtname() {
return this.isWGT ? ['wgt'] : ['apk']
},
platformLocaldata() {
return !this.isWGT ? this.formOptions.platform_localdata : this.enableiOSWgt ? this.formOptions
.platform_localdata : [this.formOptions.platform_localdata[0]]
},
uni_platform() {
return (this.isiOS ? platform_iOS : platform_Android).toLocaleLowerCase()
}
},
methods: {
getStoreList(appid) {
return db.collection('opendb-app-list')
.where({
appid
})
.get()
.then(res => {
const data = res.result.data[0]
return data.store_list || []
})
},
packageUploadSuccess(res) {
uni.showToast({
icon: 'success',
title: '上传成功',
duration: 800
})
this.preUrl = this.formData.url
this.formData.url = res.tempFilePaths[0]
},
deleteFile(fileList) {
return this.$request('deleteFile', {
fileList
}, {
functionName: 'uni-upgrade-center'
})
},
async packageDelete(res) {
if (!this.hasPackage) return;
let [deleteRes] = await this.deleteFile([res.tempFilePath])
if (deleteRes.success) {
uni.showToast({
icon: 'success',
title: '删除成功',
duration: 800
})
this.formData.url = this.preUrl
this.$refs.form.clearValidate('url')
}
},
selectFile() {
if (this.hasPackage) {
uni.showToast({
icon: 'none',
title: '只可上传一个文件,请删除已上传后重试',
duration: 1000
});
}
},
createCenterRecord(value) {
return {
...value,
uni_platform: this.uni_platform,
create_env: 'upgrade-center'
}
},
createCenterQuery({
appid
}) {
return {
appid,
create_env: 'upgrade-center'
}
},
createStatQuery({
appid,
type,
version,
uni_platform
}) {
return {
appid,
type,
version,
uni_platform: uni_platform ? uni_platform : this.uni_platform,
create_env: 'uni-stat',
stable_publish: false
}
}
}
}

View File

@@ -0,0 +1,26 @@
// 判断arr是否为一个数组返回一个bool值
function isArray(arr) {
return Object.prototype.toString.call(arr) === '[object Array]';
}
// 深度克隆
export function deepClone(obj) {
// 对常见的“非”值,直接返回原来值
if ([null, undefined, NaN, false].includes(obj)) return obj;
if (typeof obj !== "object" && typeof obj !== 'function') {
//原始类型直接返回
return obj;
}
var o = isArray(obj) ? [] : {};
for (let i in obj) {
if (obj.hasOwnProperty(i)) {
o[i] = typeof obj[i] === "object" ? deepClone(obj[i]) : obj[i];
}
}
return o;
}
export const appListDbName = 'opendb-app-list'
export const appVersionListDbName = 'opendb-app-versions'
// 版本列表默认显示应用Appid
export const defaultDisplayApp = ''

View File

@@ -0,0 +1,412 @@
<template>
<view class="uni-container">
<view class="uni-header">
<view class="uni-group">
<view class="uni-title">包类型</view>
<view class="uni-sub-title">{{type_valuetotext[formData.type]}}</view>
</view>
</view>
<uni-forms ref="form" :value="formData" validateTrigger="bind" :labelWidth="labelWidth">
<uni-forms-item name="appid" label="AppID" required>
<uni-easyinput :disabled="true" v-model="formData.appid" trim="both" />
</uni-forms-item>
<uni-forms-item name="name" label="应用名称">
<uni-easyinput :disabled="true" v-model="formData.name" trim="both" />
</uni-forms-item>
<uni-forms-item name="title" label="更新标题">
<uni-easyinput placeholder="更新标题" v-model="formData.title" />
</uni-forms-item>
<uni-forms-item name="contents" label="更新内容" required>
<textarea auto-height style="box-sizing: content-box;"
@input="binddata('contents', $event.detail.value)" class="uni-textarea-border"
:value="formData.contents" @update:value="val => formData.contents = val"></textarea>
</uni-forms-item>
<uni-forms-item name="platform" label="平台" required>
<uni-data-checkbox :multiple="isWGT" v-model="formData.platform" :localdata="platformLocaldata" />
</uni-forms-item>
<uni-forms-item name="version" label="版本号" required>
<uni-easyinput v-model="formData.version" placeholder="当前包版本号,必须大于当前线上发行版本号" />
</uni-forms-item>
<uni-forms-item v-if="isWGT" key="min_uni_version" name="min_uni_version" label="原生App最低版本"
:required="isWGT">
<uni-easyinput placeholder="原生App最低版本" v-model="formData.min_uni_version" />
<show-info :content="minUniVersionContent"></show-info>
</uni-forms-item>
<uni-forms-item v-if="!isiOS" label="上传apk包">
<uni-file-picker v-model="appFileList" :file-extname="fileExtname" :disabled="hasPackage"
returnType="object" file-mediatype="all" limit="1" @success="packageUploadSuccess"
@delete="packageDelete">
<view class="flex">
<button type="primary" size="mini" @click="selectFile" style="margin: 0;">选择文件</button>
<text
style="padding: 10px;font-size: 12px;color: #666;">上传apk到当前服务空间的云存储中上传成功后会自动使用云存储地址填充下载链接</text>
</view>
</uni-file-picker>
<text v-if="hasPackage"
style="padding-left: 20px;color: #a8a8a8;">{{Number(appFileList.size / 1024 / 1024).toFixed(2)}}M</text>
</uni-forms-item>
<uni-forms-item key="url" name="url" :label="isiOS ? 'AppStore' : '下载链接'" required>
<uni-easyinput placeholder="链接" v-model="formData.url" :maxlength="-1" />
<!-- <show-info :top="-80" :content="uploadFileContent"></show-info> -->
</uni-forms-item>
<uni-forms-item v-if="!isiOS && !isWGT && formData.store_list.length" label="Android应用市场" labelWidth="125px"
key="store_list" name="store_list">
<view style="flex: 1;">
<view v-for="(item) in formData.store_list" :key="item.id">
<uni-card style="margin: 0px 0px 20px 0px;">
<view style="display: flex;">
<checkbox-group style="user-select: none;"
@change="({detail:{value}}) => {item.enable = !!value.length}">
<label class="title_padding">
<checkbox value="scheme" :checked="item.enable" />
<text>是否启用</text>
</label>
</checkbox-group>
<!-- <view style="padding-left: 10px;">
<button type="warn" size="mini"
@click="formData.store_list.splice(index,1)">删除</button>
</view> -->
</view>
<uni-forms-item label="商店名称">
<uni-easyinput disabled v-model="item.name" trim="both"></uni-easyinput>
</uni-forms-item>
<uni-forms-item label="Scheme">
<uni-easyinput disabled v-model="item.scheme" trim="both"></uni-easyinput>
</uni-forms-item>
<uni-forms-item label="优先级">
<uni-easyinput v-model="item.priority" type="number"></uni-easyinput>
<show-info :top="-100" :left="-180" :content="priorityContent"></show-info>
</uni-forms-item>
</uni-card>
</view>
</view>
</uni-forms-item>
<uni-forms-item v-if="isWGT" key="is_silently" name="is_silently" label="静默更新">
<switch @change="binddata('is_silently', $event.detail.value)" :checked="formData.is_silently" />
<show-info :top="-80" :content="silentlyContent"></show-info>
</uni-forms-item>
<uni-forms-item v-if="!isiOS" key="is_mandatory" name="is_mandatory" label="强制更新">
<switch @change="binddata('is_mandatory', $event.detail.value)" :checked="formData.is_mandatory" />
<show-info :content="mandatoryContent"></show-info>
</uni-forms-item>
<uni-forms-item name="stable_publish" label="上线发行">
<switch @change="binddata('stable_publish', $event.detail.value)" :checked="formData.stable_publish" />
<show-info :top="-40" :content="stablePublishContent2"></show-info>
</uni-forms-item>
<uni-forms-item v-show="false" name="type" label="安装包类型">
<uni-data-checkbox v-model="formData.type" :localdata="formOptions.type_localdata" />
</uni-forms-item>
<view class="uni-button-group">
<button type="primary" class="uni-button" style="width: 100px;" @click="submit">发布</button>
<button type="warn" class="uni-button" style="width: 100px;margin-left: 15px;" @click="back">取消</button>
</view>
</uni-forms>
</view>
</template>
<script>
import {
validator,
enumConverter
} from '@/js_sdk/validator/opendb-app-versions.js';
import addAndDetail, {
fields
} from '../mixin/version_add_detail_mixin.js';
import {
appVersionListDbName
} from '../utils.js';
const db = uniCloud.database();
const dbCmd = db.command;
const dbCollectionName = appVersionListDbName;
const platform_iOS = 'iOS';
const platform_Android = 'Android';
/**
* 对比版本号,如需要,请自行修改判断规则
* 支持比对 ("3.0.0.0.0.1.0.1", "3.0.0.0.0.1") ("3.0.0.1", "3.0") ("3.1.1", "3.1.1.1") 之类的
* @param {Object} v1
* @param {Object} v2
* v1 > v2 return 1
* v1 < v2 return -1
* v1 == v2 return 0
*/
function compare(v1 = '0', v2 = '0') {
v1 = String(v1).split('.')
v2 = String(v2).split('.')
const minVersionLens = Math.min(v1.length, v2.length);
let result = 0;
for (let i = 0; i < minVersionLens; i++) {
const curV1 = Number(v1[i])
const curV2 = Number(v2[i])
if (curV1 > curV2) {
result = 1
break;
} else if (curV1 < curV2) {
result = -1
break;
}
}
if (result === 0 && (v1.length !== v2.length)) {
const v1BiggerThenv2 = v1.length > v2.length;
const maxLensVersion = v1BiggerThenv2 ? v1 : v2;
for (let i = minVersionLens; i < maxLensVersion.length; i++) {
const curVersion = Number(maxLensVersion[i])
if (curVersion > 0) {
v1BiggerThenv2 ? result = 1 : result = -1
break;
}
}
}
return result;
}
export default {
mixins: [addAndDetail],
data() {
return {
latestVersion: '0.0.0',
lastVersionId: ''
}
},
async onLoad({
appid,
name,
type
}) {
if (appid && type && name) {
const store_list = await this.getStoreList(appid)
this.formData = {
...this.formData,
...{
appid,
name,
type,
store_list
}
}
this.latestStableData = await this.getDetail(appid, type)
// 如果有数据否则为发布第一版默认为Android
if (!this.isWGT && this.latestStableData.length) {
this.setFormData(platform_Android)
}
// 如果是wgt ,则需要将 min_uni_version 设为必填
if (this.isWGT) {
this.rules.min_uni_version.rules.push({
"required": true
})
}
}
},
watch: {
isiOS(val) {
if (!val && this.hasPackage) {
this.formData.url = this.appFileList.url
return;
}
this.formData.url = ''
},
"formData.platform"(val) {
this.setFormData(val)
}
},
methods: {
setFormData(os) {
uni.showLoading({
mask: true
})
// 每次需初始化 版本 与 id ,因为可能是新增第一版
this.latestVersion = '0.0.0';
this.lastVersionId = ''
const data = this.getData(this.latestStableData, os)[0]
if (data) {
const {
_id,
version,
name,
platform,
min_uni_version,
url
} = data
this.lastVersionId = _id
this.latestVersion = version;
this.formData.name = name
// 如果不是wgt则需要删除 min_uni_version 字段
if (!this.isWGT) {
delete this.formData.min_uni_version;
this.formData.platform = platform[0]
// iOS需要带出上一版本的AppStore链接
if (this.isiOS) {
this.formData.url = url;
}
} else {
this.formData.min_uni_version = min_uni_version
// this.formData.platform = [os]
}
} else if (this.isWGT) {
this.formData.min_uni_version = ''
}
uni.hideLoading()
},
/**
* 触发表单提交
*/
submit() {
uni.showLoading({
mask: true
})
this.$refs.form.validate(['store_list']).then((res) => {
if (compare(this.latestVersion, res.version) >= 0) {
uni.showModal({
content: `版本号必须大于当前已上线版本(${this.latestVersion}`,
showCancel: false
})
throw new Error('版本号必须大于已上线版本(${this.latestVersion}');
}
// 如果不是 wgt 更新,则需将 platform 字段还原为 array
if (!this.isWGT) {
res.platform = [res.platform]
}
if (this.isiOS || this.isWGT) delete res.store_list;
if (res.store_list) {
res.store_list.forEach(item => {
item.priority = parseFloat(item.priority)
})
}
this.submitForm(res)
}).catch((errors) => {
uni.hideLoading()
})
},
async submitForm(value) {
value = this.createCenterRecord(value)
const collectionDB = db.collection(dbCollectionName)
// uni-stat 会创建这些字段 appid
let recordCreateByUniStat = []
if (!this.isWGT) {
recordCreateByUniStat = await this.getDetail(value.appid, value.type, this.createStatQuery(value))
}
let dbOperate
if (!recordCreateByUniStat.length) {
dbOperate = collectionDB.add(value)
} else {
value.create_date = Date.now()
dbOperate = collectionDB.doc(recordCreateByUniStat[0]._id).update(value)
}
// 使用 clientDB 提交数据
dbOperate.then(async (res) => {
// 如果新增版本为上线发行,且之前有该平台的上线发行,则自动将上一版设为下线
if (value.stable_publish && this.lastVersionId) {
await collectionDB.doc(this.lastVersionId).update({
stable_publish: false
})
}
uni.showToast({
title: '新增成功'
})
this.getOpenerEventChannel().emit('refreshData')
setTimeout(() => uni.navigateBack(), 500)
}).catch((err) => {
uni.showModal({
content: err.message || '请求服务失败',
showCancel: false
})
}).finally(() => {
uni.hideLoading()
})
},
/**
* 获取表单数据
* @param {Object} id
*/
getDetail(appid, type, args = {}) {
uni.showLoading({
mask: true
})
return db.collection(dbCollectionName)
.where(
Object.assign({
appid,
type,
stable_publish: true
}, args)
)
.field(fields)
.get()
.then((res) => res.result.data)
.catch((err) => {
uni.showModal({
content: err.message || '请求服务失败',
showCancel: false
})
}).finally(() => {
uni.hideLoading()
})
},
getData(data = [], platform) {
if (typeof platform === 'string') {
return data.filter(item => item.platform.includes(platform))
} else {
return data.filter(item => item.platform.toString() === platform.toString())
}
},
back() {
uni.showModal({
title: '取消发布',
content: this.hasPackage ? '将会删除已上传的包' : undefined,
success: res => {
if (res.confirm) {
// 若已上传包但取消发布,则自动将包删除
if (this.hasPackage) {
this.deleteFile([this.appFileList.url])
}
uni.navigateBack()
}
}
});
}
}
}
</script>
<style lang="scss">
::v-deep .uni-forms-item__content {
display: flex;
align-items: center;
}
.uni-button-group {
& button {
margin-left: 15px;
}
& button:first-child {
margin-left: 0px;
}
}
.title_padding {
padding-bottom: 15px;
display: block;
}
::v-deep .uni-file-picker__files {
max-width: 100%;
}
</style>

View File

@@ -0,0 +1,337 @@
<template>
<view class="uni-container">
<view class="uni-header">
<view class="uni-group">
<view class="uni-title">包类型</view>
<view class="uni-sub-title" style="display: flex;justify-content: center;align-items: center;">
{{type_valuetotext[formData.type]}}
</view>
</view>
<view v-if="!isStable" class="uni-group">
<button class="uni-button" type="warn" size="mini" @click="deletePackage">删除</button>
</view>
</view>
<uni-forms ref="form" :value="formData" validateTrigger="bind" :labelWidth="labelWidth">
<uni-forms-item name="appid" label="AppID" required>
<uni-easyinput :disabled="true" v-model="formData.appid" trim="both" />
</uni-forms-item>
<uni-forms-item name="name" label="应用名称">
<uni-easyinput :disabled="true" v-model="formData.name" trim="both" />
</uni-forms-item>
<uni-forms-item name="title" label="更新标题">
<uni-easyinput :disabled="detailsState" placeholder="更新标题" v-model="formData.title" />
</uni-forms-item>
<uni-forms-item name="contents" label="更新内容" required>
<textarea auto-height style="box-sizing: content-box;" :disabled="detailsState"
@input="binddata('contents', $event.detail.value)" class="uni-textarea-border"
:value="formData.contents" @update:value="val => formData.contents = val"></textarea>
</uni-forms-item>
<uni-forms-item name="platform" label="平台" required>
<uni-data-checkbox :disabled="true" :multiple="true" v-model="formData.platform"
:localdata="platformLocaldata" />
</uni-forms-item>
<uni-forms-item name="version" label="版本号" required>
<uni-easyinput :disabled="true" v-model="formData.version" placeholder="当前包版本号,必须大于当前已上线版本号" />
</uni-forms-item>
<uni-forms-item v-if="isWGT" key="min_uni_version" name="min_uni_version" label="原生App最低版本"
:required="isWGT">
<uni-easyinput :disabled="detailsState" placeholder="原生App最低版本" v-model="formData.min_uni_version" />
<show-info :content="minUniVersionContent"></show-info>
</uni-forms-item>
<uni-forms-item v-if="!isiOS && !detailsState" label="上传apk包">
<uni-file-picker v-model="appFileList" :file-extname="fileExtname" :disabled="hasPackage"
returnType="object" file-mediatype="all" limit="1" @success="packageUploadSuccess"
@delete="packageDelete">
<button type="primary" size="mini" @click="selectFile">选择文件</button>
</uni-file-picker>
<text v-if="hasPackage"
style="padding-left: 20px;color: #a8a8a8;">{{Number(appFileList.size / 1024 / 1024).toFixed(2)}}M</text>
</uni-forms-item>
<uni-forms-item key="url" name="url" :label="isiOS ? 'AppStore' : '下载链接'" required>
<uni-easyinput :disabled="detailsState" placeholder="下载链接" v-model="formData.url" :maxlength="-1" />
<!-- <show-info :top="-80" :content="uploadFileContent"></show-info> -->
</uni-forms-item>
<uni-forms-item v-if="!isiOS && !isWGT && formData.store_list.length" label="Android应用市场" key="store_list"
name="store_list" labelWidth="120">
<view style="flex: 1;">
<view v-for="(item,index) in formData.store_list" :key="item.id">
<uni-card style="margin: 0px 0px 20px 0px;">
<view style="display: flex;">
<checkbox-group style="user-select: none;"
@change="({detail:{value}}) => {item.enable = !!value.length}">
<label class="title_padding">
<checkbox :disabled="detailsState" value="scheme" :checked="item.enable" />
<text>是否启用</text>
</label>
</checkbox-group>
</view>
<uni-forms-item label="商店名称">
<uni-easyinput disabled v-model="item.name" trim="both"></uni-easyinput>
</uni-forms-item>
<uni-forms-item label="Scheme">
<uni-easyinput disabled v-model="item.scheme" trim="both"></uni-easyinput>
</uni-forms-item>
<uni-forms-item label="优先级">
<uni-easyinput :disabled="detailsState" v-model="item.priority" type="number">
</uni-easyinput>
<show-info :top="-100" :left="-180" :content="priorityContent"></show-info>
</uni-forms-item>
</uni-card>
</view>
</view>
</uni-forms-item>
<uni-forms-item v-if="isWGT" key="is_silently" name="is_silently" label="静默更新">
<switch :disabled="detailsState"
@change="binddata('is_silently', $event.detail.value),formData.is_silently=$event.detail.value"
:checked="formData.is_silently" />
<show-info :top="-80" :content="silentlyContent"></show-info>
</uni-forms-item>
<uni-forms-item v-if="!isiOS" key="is_mandatory" name="is_mandatory" label="强制更新">
<switch :disabled="detailsState"
@change="binddata('is_mandatory', $event.detail.value),formData.is_mandatory=$event.detail.value"
:checked="formData.is_mandatory" />
<show-info width="230" :top="-30" :content="mandatoryContent"></show-info>
</uni-forms-item>
<uni-forms-item name="stable_publish" label="上线发行">
<switch :disabled="detailsState || isStable"
@change="binddata('stable_publish', $event.detail.value),formData.stable_publish=$event.detail.value"
:checked="formData.stable_publish" />
<show-info v-if="isStable" :top="-50" width="350" :content="stablePublishContent"></show-info>
<show-info v-else :top="-40" :content="stablePublishContent2"></show-info>
</uni-forms-item>
<uni-forms-item name="create_date" label="上传时间">
<uni-dateformat format="yyyy-MM-dd hh:mm:ss" :date="formData.create_date" :threshold="[0, 0]" />
</uni-forms-item>
<uni-forms-item v-show="false" name="type" label="安装包类型">
<uni-data-checkbox v-model="formData.type" :localdata="formOptions.type_localdata" />
</uni-forms-item>
<view class="uni-button-group">
<button type="primary" class="uni-button" style="width: 100px;" @click="detailsState = false"
v-if="detailsState">修改</button>
<button type="primary" class="uni-button" style="width: 100px;" @click="submit"
v-if="!detailsState">提交</button>
<button type="warn" class="uni-button" style="width: 100px;" @click="cancelEdit"
v-if="!detailsState">取消</button>
<navigator open-type="navigateBack" style="margin-left: 15px;">
<button class="uni-button" style="width: 100px;">返回</button>
</navigator>
</view>
</uni-forms>
</view>
</template>
<script>
import {
validator,
enumConverter
} from '@/js_sdk/validator/opendb-app-versions.js';
import addAndDetail, {
fields
} from '../mixin/version_add_detail_mixin.js'
import {
deepClone,
appVersionListDbName
} from '../utils.js'
const db = uniCloud.database();
const dbCmd = db.command;
const dbCollectionName = appVersionListDbName;
const platform_iOS = 'iOS';
const platform_Android = 'Android';
function getValidator(fields) {
let reuslt = {}
for (let key in validator) {
if (fields.includes(key)) {
reuslt[key] = validator[key]
}
}
return reuslt
}
export default {
mixins: [addAndDetail],
data() {
return {
showStableInfo: false,
isStable: true, // 是否是线上发行版
originalData: {}, // 原始数据,用于恢复状态
detailsState: true // 查看状态
}
},
async onLoad(e) {
const id = e.id
this.formDataId = id
await this.getDetail(id)
this.isStable = this.formData.stable_publish;
this.latestStableData = await this.getLatestVersion();
if (this.isWGT) {
this.rules.min_uni_version.rules.push({
"required": true
})
}
},
methods: {
/**
* 触发表单提交
*/
submit() {
uni.showLoading({
mask: true
})
this.$refs.form.validate(['store_list']).then((res) => {
if (res.store_list) {
res.store_list.forEach(item => {
item.priority = parseFloat(item.priority)
})
}
this.submitForm(res)
}).catch((errors) => {
uni.hideLoading()
})
},
async submitForm(value) {
const collectionDB = db.collection(dbCollectionName)
// 使用 clientDB 提交数据
collectionDB.doc(this.formDataId).update(value).then(async (res) => {
// 如果不是线上发行版,则在设置为上线发行时,需将之前的已上线版设为下线
if (!this.isStable && value.stable_publish === true && this.latestStableData) {
await collectionDB.doc(this.latestStableData._id).update({
stable_publish: false
})
}
uni.showToast({
title: '修改成功'
})
this.getOpenerEventChannel().emit('refreshData')
setTimeout(() => uni.navigateBack(), 500)
}).catch((err) => {
uni.showModal({
content: err.message || '请求服务失败',
showCancel: false
})
}).finally(() => {
uni.hideLoading()
})
},
/**
* 获取表单数据
* @param {Object} id
*/
getDetail(id) {
uni.showLoading({
mask: true
})
return db.collection(dbCollectionName)
.doc(id)
.field(fields)
.get()
.then((res) => {
const data = res.result.data[0]
if (data) {
if (!data.store_list) data.store_list = []
this.formData = data
this.originalData = deepClone(this.formData)
}
}).catch((err) => {
uni.showModal({
content: err.message || '请求服务失败',
showCancel: false
})
}).finally(() => {
uni.hideLoading()
})
},
deletePackage() {
uni.showModal({
title: '提示',
content: '是否删除该版本',
success: res => {
if (res.confirm) {
uni.showLoading({
mask: true
})
db.collection(dbCollectionName).doc(this.formDataId).remove()
.then(() => {
uni.showToast({
title: '删除成功'
})
this.getOpenerEventChannel().emit('refreshData')
setTimeout(() => uni.navigateBack(), 500)
}).catch((err) => {
uni.showModal({
content: err.message || '请求服务失败',
showCancel: false
})
}).finally(() => {
uni.hideLoading()
})
}
}
});
},
async getLatestVersion() {
const where = {
appid: this.formData.appid,
type: this.formData.type,
stable_publish: true
};
if (!this.isWGT) {
where.platform = this.formData.platform[0]
}
const latestStableData = await db.collection(dbCollectionName).where(where).get()
return latestStableData.result.data.find(item => item.platform.toString() === this.formData.platform
.toString());
},
cancelEdit() {
let content = '';
!this.isiOS && this.hasPackage ? content += '\n将会删除已上传的包' : '';
uni.showModal({
title: '取消修改',
content,
success: res => {
if (res.confirm) {
this.formData = deepClone(this.originalData)
this.detailsState = true
if (this.hasPackage) {
this.deleteFile([this.appFileList.url])
}
}
}
});
}
}
}
</script>
<style lang="scss">
.show-stable-info {
position: absolute;
left: 165px;
padding: 5px 10px;
background-color: #f4f4f5;
color: #909399;
border-radius: 4px;
border: 1px solid #e9e9eb;
}
::v-deep .uni-forms-item__content {
display: flex;
align-items: center;
}
.uni-button-group {
& button {
margin-left: 15px;
}
& button:first-child {
margin-left: 0px;
}
}
</style>

View File

@@ -0,0 +1,368 @@
<template>
<view class="main">
<view v-if="loaded">
<view class="uni-header">
<view class="uni-group">
<view class="uni-sub-title">当前应用</view>
<view class="uni-title app-list">
<picker @change="(e) => showAppIndex = e.detail.value" :value="showAppIndex"
:range="appNameList">
<view class="uni-input" style="font-size: 14px;">
{{appNameList[showAppIndex]}}
<uni-icons type="bottom"></uni-icons>
</view>
</picker>
</view>
</view>
<view class="uni-group">
<input class="uni-search" type="text" v-model="query" @confirm="search" placeholder="请输入搜索内容" />
<button class="uni-button" type="default" size="mini" @click="search">搜索</button>
<button class="uni-button publish" type="primary" size="mini" @click="publish">发布新版</button>
<button class="uni-button" type="warn" size="mini" :disabled="!selectedIndexs.length"
@click="delTable">批量删除</button>
</view>
</view>
<view class="uni-container">
<unicloud-db ref="udb" :collection="appVersionListDbName"
field="store_list,appid,contents,platform,type,version,min_uni_version,url,stable_publish,create_date,title,name"
:where="where" page-data="replace" :orderby="orderby" :getcount="true" :page-size="options.pageSize"
:page-current="options.pageCurrent" v-slot:default="{data,pagination,loading,error,options}"
:options="options">
<uni-table style="overflow-y: hidden;" :loading="loading" :emptyText="error.message || '没有更多数据'"
border stripe type="selection" @selection-change="selectionChange">
<uni-tr>
<uni-th align="center">AppID</uni-th>
<uni-th align="center">更新标题</uni-th>
<uni-th align="center">安装包类型</uni-th>
<uni-th align="center">平台</uni-th>
<uni-th align="center">已上架应用市场</uni-th>
<uni-th align="center">版本号</uni-th>
<uni-th align="center">安装包状态</uni-th>
<uni-th align="center">上传时间</uni-th>
<uni-th align="center">操作</uni-th>
</uni-tr>
<uni-tr v-for="(item,index) in data" :key="index" :disabled="item.stable_publish">
<uni-td align="center"> {{item.appid}} </uni-td>
<uni-td align="center"> {{item.title || '-'}} </uni-td>
<uni-td align="center">
<text :style="{
padding: '5px 8px',
backgroundColor: item.type === 'wgt' ? '#f0f9eb' : '#ecf5ff',
color: item.type === 'wgt' ? '#67c23a' : '#409eff',
border: `1px solid ${item.type === 'wgt' ? '#e1f3d8' : '#d9ecff'}`,
borderRadius: '4px'
}">{{options.type_valuetotext[item.type]}}</text>
</uni-td>
<uni-td align="center">
<uni-data-picker :localdata="options.platform_valuetotext" :value="item.platform"
:border="false" :readonly="true" split="," />
</uni-td>
<uni-td align="center">
<text>{{store_list_key(item.store_list)}}</text>
</uni-td>
<uni-td align="center"> {{item.version}} </uni-td>
<uni-td align="center"> {{item.stable_publish == true ? '已上线' : '已下线'}} </uni-td>
<uni-td align="center">
<uni-dateformat format="yyyy-MM-dd hh:mm:ss" :date="item.create_date"
:threshold="[0, 0]" />
</uni-td>
<uni-td align="center">
<!-- <view class="uni-group"> -->
<button @click="navigateTo('./detail?id='+item._id, false)" class="uni-button"
size="mini" type="primary">详情</button>
<!-- <button @click="confirmDelete(item._id)" class="uni-button" size="mini" type="warn">删除</button> -->
<!-- </view> -->
</uni-td>
</uni-tr>
</uni-table>
<view class="uni-pagination-box">
<uni-pagination show-icon :page-size="pagination.size" v-model="pagination.current"
:total="pagination.count" @change="onPageChanged" />
</view>
</unicloud-db>
</view>
</view>
<view v-else class="page-loading" :style="containerTop">
<i class="uni-icon_toast uni-loading"></i>
</view>
</view>
</template>
<script>
import {
enumConverter
} from '@/js_sdk/validator/opendb-app-versions.js';
import {
appListDbName,
appVersionListDbName,
defaultDisplayApp
} from '../utils.js'
import {
mapState
} from 'vuex'
const db = uniCloud.database()
const dbCmd = db.command
// 表查询配置
const dbOrderBy = 'stable_publish desc,create_date desc' // 排序字段
const dbSearchFields = ['name', 'title', 'stable_publish', 'type'] // 模糊搜索字段,支持模糊搜索的字段列表
// 分页配置
const pageSize = 20
const pageCurrent = 1
const appidKey = '__app_version_appid'
const nameKey = '__app_version_name'
function getScreenHeight() {
return document.documentElement ? document.documentElement.clientHeight : window.innerHeight;
}
function createListQuery(condition = {}) {
return {
create_env: dbCmd.neq("uni-stat"),
...condition
}
}
export default {
data() {
return {
backButtonHover: false,
appVersionListDbName,
currentAppid: '',
currentAppName: '',
query: '',
where: '',
orderby: dbOrderBy,
selectedIndexs: [],
options: {
pageSize,
pageCurrent,
...enumConverter
},
imageStyles: {
width: 64,
height: 64
},
loaded: false,
containerTop: {},
appList: [],
showAppIndex: 0
}
},
async onLoad({
appid
}) {
await this.getAppList()
if (!this.appList.length) {
this.showModalToAppManager()
return
}
this.loaded = true
this.appList.forEach((item, index) => {
if (item.appid === appid || defaultDisplayApp) {
this.showAppIndex = index
}
})
this.setAppInfo(this.showAppIndex)
this.where = createListQuery({
appid: this.currentAppid
})
},
computed: {
...mapState('app', ['appid']),
appNameList() {
return this.appList.map(item => item.name)
}
},
watch: {
showAppIndex(val) {
this.setAppInfo(val)
this.where = createListQuery({
appid: this.currentAppid
})
}
},
onReady() {
this.containerTop.height = `${getScreenHeight()}px`
},
methods: {
setAppInfo(index) {
this.currentAppid = this.appList[index].appid
this.currentAppName = this.appList[index].name
},
navigateBack() {
uni.navigateBack()
},
getWhere() {
const query = this.query.trim()
if (!query) {
return ''
}
const queryRe = new RegExp(query, 'i')
return dbSearchFields.map(name => queryRe + '.test(' + name + ')').join(' || ')
},
search() {
const newWhere = this.getWhere()
const isSameWhere = newWhere === this.where
this.where = newWhere
if (this.where) {
this.where = `(${this.where}) && `
}
this.where += `${new RegExp(this.currentAppid, 'i')}.test(appid)`
if (isSameWhere) { // 相同条件时,手动强制刷新
this.loadData()
}
},
loadData(clear = true) {
this.$refs.udb.loadData({
clear
})
},
onPageChanged(e) {
this.$refs.udb.loadData({
current: e.current
})
},
navigateTo(url, clear) {
// clear 表示刷新列表时是否清除页码true 表示刷新并回到列表第 1 页,默认为 true
uni.navigateTo({
url,
events: {
refreshData: () => {
this.loadData(clear)
}
}
})
},
// 多选处理
selectedItems() {
var dataList = this.$refs.udb.dataList
return this.selectedIndexs.map(i => dataList[i]._id)
},
// 批量删除
delTable() {
this.$refs.udb.remove(this.selectedItems())
},
// 多选
selectionChange(e) {
this.selectedIndexs = e.detail.index
},
confirmDelete(id) {
this.$refs.udb.remove(id)
},
publish(e) {
// #ifdef H5
const {
top,
left,
width,
height
} = document.querySelector('.uni-button.publish').getBoundingClientRect()
// #endif
const platforms = Object.keys(this.options.type_valuetotext)
uni.showActionSheet({
itemList: Object.values(this.options.type_valuetotext),
// #ifdef H5
popover: {
top: top + height,
left,
width
},
// #endif
success: async (res) => {
this.navigateTo(
`./add?appid=${this.currentAppid}&name=${this.currentAppName}&type=${platforms[res.tapIndex]}`
)
}
});
},
async getAppList() {
try {
const {
result
} = await db.collection(appListDbName).get()
if (result && result.data && result.data.length > 0) {
this.appList = result.data.filter(item => item.appid !== this.appid)
} else {
this.showModalToAppManager()
}
} catch (e) {
const arr = ['TOKEN_INVALID_TOKEN_EXPIRED', 'TOKEN_INVALID_ANONYMOUS_USER']
if (arr.indexOf(e.code) === -1)
this.showModalToAppManager()
}
},
showModalToAppManager() {
let timer = null
let second = 3
function jump() {
uni.navigateTo({
url: '/pages/system/app/list'
})
clearInterval(timer)
}
timer = setInterval(() => {
if (--second <= 0) {
jump()
}
}, 1000)
uni.showModal({
title: '请先添加应用',
content: '即将跳转至应用管理……',
showCancel: false,
confirmText: '立即跳转',
success: (res) => jump()
})
},
store_list_key(store_list) {
const arr = store_list ? store_list.filter(item => item.enable) : []
return arr.length ?
arr.sort((a, b) => b.priority - a.priority)
.map(item => item.name).join(',') :
'-'
}
}
}
</script>
<style lang="scss">
.page-loading {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
flex: 1;
i {
$icon-size: 80rpx;
width: $icon-size;
height: $icon-size;
}
}
page,
page .main,
.page-loading {
height: 100%;
}
.app-list {
display: flex;
justify-content: space-between;
align-items: center;
padding: 5px 10px;
border-radius: 4px;
border: 1px solid #2e76ba;
color: #3A8EE6;
uni-text {
margin-left: 10px;
}
}
</style>

View File

@@ -0,0 +1,233 @@
## uni-admin 1.9.3+ 已内置,此插件不再维护 [点击查看文档](https://uniapp.dcloud.net.cn/uniCloud/upgrade-center.html)
- `uni-admin < 1.9.0`:请前往 [Gitee](https://gitee.com/dcloud/uni-upgrade-center/releases) 下载 `tag v0.4.2` 版本使用
-`1.9.0 <= uni-admin < 1.9.2` :请前往 [Gitee](https://gitee.com/dcloud/uni-upgrade-center/releases) 下载 `tag v0.5.1` 版本使用
- `uni-admin >= 1.9.3` uni-admin 已内置 升级中心,直接使用即可 [详情](https://uniapp.dcloud.io/uniCloud/admin.html#app-manager)。并且云函数 `upgrade-center` 废弃,使用 `uni-upgrade-center` 云函数。
# uni-upgrade-center - Admin
### 概述
> 统一管理App及App在`Android`、`iOS`平台上`App安装包`和`wgt资源包`的发布升级
> 本插件为uni升级中心后台管理系统客户端检查更新插件请点击查看 [uni-upgrade-center-app](https://ext.dcloud.net.cn/plugin?id=4542)
### 基于uniCloud的App升级中心本插件具有如下特征
- 云端基于uniCloud云函数实现
- 数据库遵循opendb规范
- 遵循uni-Admin框架规范可直接导入uni-admin项目中
- 支持App整包升级及wgt资源包升级
## 升级中心解决了什么问题?
升级中心是一款uni-admin插件负责App版本更新业务。包含后台管理界面、更新检查逻辑App内只要调用弹出提示即可。
升级中心有以下功能点:
- 应用管理对App的信息记录和应用版本管理
- 版本管理可以发布新版也可方便直观的对当前App历史版本以及线上发行版本进行查看、编辑和删除操作
- 版本发布信息管理,包括 更新标题,更新内容,版本号,静默更新,强制更新,灵活上线发行 的设置和修改
- 原生App安装包发布Apk更新用于App的整包更新可设置是否强制更新
- wgt资源包发布wgt更新用于App的热更新可设置是否强制更新静默更新
- App管理列表及App版本记录列表搜索
只需导入插件,初始化数据库即可拥有上述功能。
您也可以自己修改逻辑自定义数据库字段,和随意定制 UI 样式。
## 安装指引
1. 使用`HBuilderX 3.1.0+`,因为要使用到`uni_modules`
2. 使用已有`uniCloud-admin`项目或新建项目:`打开HBuilderX` -> `文件` -> `新建` -> `项目` -> `uni-app` 选择 `uniCloud admin`模板,键入一个名字,确定
3. 鼠标右键选择`关联云服务空间``运行云服务空间初始化向导`
3. 在插件市场打开本插件页面,在右侧点击`使用 HBuilderX 导入插件`,选择 `uniCloud admin` 项目点击确定
4. 等待下载安装完毕。由于本插件依赖一些uni-ui插件下载完成后会显示合并插件页面自行选择即可
5. 找到`/uni_modules/uni-upgrade-center/uniCloud/cloudfunctions/upgrade-center`,右键上传部署
7.`pages.json`中添加页面路径
```json
//此结构与uniCloud admin中的pages.json结构一致
{
"pages": [
// ……其他页面配置
{
"path": "uni_modules/uni-upgrade-center/pages/version/list",
"style": {
"navigationBarTitleText": "版本列表"
}
}, {
"path": "uni_modules/uni-upgrade-center/pages/version/add",
"style": {
"navigationBarTitleText": "新版发布"
}
}, {
"path": "uni_modules/uni-upgrade-center/pages/version/detail",
"style": {
"navigationBarTitleText": "版本信息查看"
}
}
]
}
```
8.`manifest.json -> 源码视图`中添加以下配置:
```js
"networkTimeout":{
"uploadFile":1200000 //ms 如果不配置,上传大文件可能会超时
}
```
9. 运行项目到`Chrome`
10. 添加菜单
- `vue2`
运行起来uniCloud admin菜单管理模块会自动读取`/uni_modules/uni-upgrade-center/menu.json`文件中的菜单配置,生成【待添加菜单】,选中升级中心,点击`添加选中的菜单`即可
<div align="center">
<img src="https://vkceyugu.cdn.bspapp.com/VKCEYUGU-a90b5f95-90ba-4d30-a6a7-cd4d057327db/16dc338e-7d5b-4290-98a9-adb7f0c23754.png" width="800"></img>
</div>
- `vue3`
可将 `/uni_modules/uni-upgrade-center/menu.json` 拷贝至 `uniCloud/database/db_init.json` 中的 `opendb-admin-menus` 节点下,并右键初始化数据库即可。
11. 添加成功后,就可以在左侧的菜单栏中找到`升级中心`菜单
<div align="center">
<img src="https://vkceyugu.cdn.bspapp.com/VKCEYUGU-a90b5f95-90ba-4d30-a6a7-cd4d057327db/fcf04804-0c4c-4342-9a8b-dfc273dfc83c.png" width="300"></img>
</div>
12. 在进入`升级中心`之前:
1. 需要到`uni-admin`的`应用管理`中添加一个应用,才可以在`升级中心`中发布对应应用的版本。
2. 当你有多个应用时,可以在`/uni_modules/uni-upgrade-center/pages/utils.js`中修改`defaultDisplayApp`字段来设置默认显示应用的`appid`。
3. 如果不设置或设置应用不存在则默认从数据库中查出来的第一个应用。
13. 由于插件依赖的uni-ui的一些组件建议右键`/uni_modules/uni-upgrade-center`安装一下第三方依赖,否则可能会出现一些问题
14. 运行在`uniCloud`,由于本插件使用了`clientDB`,因此可能需要配置一下`uni-config-center插件`关于`uni-id`的配置信息。如提示`公用模块uni-id缺少配置信息`请这样做:
1. 点击[uni-config-center](https://ext.dcloud.net.cn/plugin?id=4425)导入插件
2. 在`/uniCloud/cloudfunctions/common/uni-config-center/`下创建`uni-id`文件夹,文件夹内创建`config.json`文件。
3. 点击[config.json默认配置](https://uniapp.dcloud.net.cn/uniCloud/uni-id?id=start)。将内容拷贝至`config.json`中。**注:一定要把注释去除!**
## 使用指南
### 升级中心
#### 应用列表
1. 点击菜单 `应用管理`,这里展示你所添加的 App点击右上角 `新增` 可以新增一个 App
<div align="center">
<img src="https://vkceyugu.cdn.bspapp.com/VKCEYUGU-a90b5f95-90ba-4d30-a6a7-cd4d057327db/7f85aa6a-eff3-4cc6-bb32-9feaaeaf97d0.png" width="400"></img>
</div>
2. 将App的信息都填写完善后你可以在列表的操作列进行`修改`应用信息或者`删除`该应用。
**Tips**
- 删除应用会把该应用的所有版本记录同时删除
#### 版本管理
1. 在版本管理list的右上角点击`发布新版`,可以发布`原生App安装包`和`wgt资源包`。在左上角点击`下拉列表`,可以切换展示应用。
<div align="center">
<img src="https://vkceyugu.cdn.bspapp.com/VKCEYUGU-a90b5f95-90ba-4d30-a6a7-cd4d057327db/442e84e7-e7f3-4d27-9c98-45568e5db835.png" width="800"></img>
</div>
- #### 发布原生App安装包
1. 在上传安装包界面填写此次发版信息
<div align="center" >
<img src="https://vkceyugu.cdn.bspapp.com/VKCEYUGU-a90b5f95-90ba-4d30-a6a7-cd4d057327db/67932ae3-1a7a-4f21-9849-ba3bcc500c36.png" width="400"></img>
</div>
2. `包地址`
- 可以选择手动上传一个文件到 `云存储`,会自动将地址填入该项
- 也可以手动填写一个地址,就可以不用再上传文件
- 如果是发布`苹果`版本,包地址则为 应用在`AppStore的链接`
3. `强制更新`
- 如果使用强制更新App端接收到该字段后App升级弹出框不可取消
4. `上线发行`
- 可设置当前包是否上线发行,只有已上线才会进行更新检测
- 同时只可有一个线上发行版,线上发行不可更设为下线。未上线可以设为上线发行并自动替换当前线上发行版
- 修改当前包为上线发行,自动替换当前线上发行版
**注:版本号请填写以`.`分隔字符串,例如:`0.0.1`**
- #### 发布wgt资源包
1. 大部分配置与发布 `原生App安装包` 一致
<div align="center">
<img src="https://vkceyugu.cdn.bspapp.com/VKCEYUGU-a90b5f95-90ba-4d30-a6a7-cd4d057327db/ec916cde-0d0e-4bf3-a735-643ea2a45b74.png" width="400"></img>
</div>
2. `原生App最低版本`
- 上次使用新Api或打包新模块的App版本
- 如果此次打包wgt使用了`新的api`或者打包了`新的模块`,则在发布 `wgt资源包` 的时候,将此版本更新为本次版本
- 如果已有正式版`wgt资源包`,则本次新增会自动带出
2. `静默更新`
- App升级时会在后台下载wgt包并自行安装。新功能在下次启动App时生效
- **静默更新后不重启应用,可能会导致正在访问的应用的页面数据错乱,请谨慎使用!**
**注:版本号请填写以`.`分隔字符串,例如:`0.0.1`**
- #### 发布完成页面
<div align="center">
<img src="https://vkceyugu.cdn.bspapp.com/VKCEYUGU-a90b5f95-90ba-4d30-a6a7-cd4d057327db/c5470d8c-cc37-4b41-8d56-6d50f8daac62.png" width="800"></img>
</div>
**Tips**
1. `pages/system/upgradecenter/version/add.vue`中有版本对比函数compare
- 使用多段式版本格式(如:"3.0.0.0.0.1.0.1", "3.0.0.0.0.1")。如果不满足对比规则,请自行修改。
## 项目代码说明
### uniCloud 数据表
数据表基于 [openDB](https://gitee.com/dcloud/opendb/tree/master) 规范,它约定了一个标准用户表的表名和字段定义,并且基于 nosql 的特性,可以由开发者自行扩展字段。
本项目用到了 2 个表:
- opendb-app-listapp管理列表。记录应用的 appid、name、description 用于展示。[详见](https://gitee.com/dcloud/opendb/tree/master/collection/opendb-app-list)
- opendb-app-versions应用版本管理表。记录管理应用的版本信息。[详见](https://gitee.com/dcloud/opendb/tree/master/collection/opendb-app-versions)
### 前端页面
点击`升级中心`,会进入应用管理列表,在这里你可以新增应用,或者在`应用详情`中查看、修改或删除一个已经录入的应用。
在应用管理列表中点击某个应用的`版本管理`,进入该应用的所有版本记录。列表排序为:先排序已上线版本,剩下已下线版本根据创建时间排列。
在应用版本列表中点击`详情`,即可进入该版本的信息详情中查看、修改或删除该记录。
**Tips**
- 升级中心设计之初就支持iOS的wgt更新
- iOS的wgt更新肯定是违反apple政策的注意事项
- 审核期间请不要弹窗升级
- 升级完后尽量不要自行重启
- 尽量使用静默更新
- 可以通过以下修改支持iOS的wgt更新
> \uni_modules\uni-upgrade-center\pages\mixin\version_add_detail_mixin.js
>
> 将 `data` 中的 `enableiOSWgt: false` 中 改为 `enableiOSWgt: true`
**常见问题**
- 以下问题可以通过升级插件版本解决:
- createdate不与默认值匹配
- ["create_date"]在数据库中并不存在
- 提交的字段["dirty_data"]在数据库中并不存在
- 集合[opendb-app-list]对应的schema内存在错误详细信息opendb-app-list表对应的schema名称冲突这是什么意思呢
- 没有/找不到 [opendb-app-list] 集合/表。**解决方案:**升级 admin 至 1.6.0+ 即可
- 测试时发布了高版本的包,测试完了发布包提示需要大于版本号 (x.x.x)。**解决方案:**直接在控制台修改数据库

View File

@@ -0,0 +1,10 @@
// 在本文件中可配置云数据库初始化数据格式见https://uniapp.dcloud.io/uniCloud/hellodb?id=db-init
// 编写完毕后对本文件点右键,可按配置规则创建表和添加数据
{
}

View File

@@ -1,8 +1,8 @@
var isReady=false;var onReadyCallbacks=[];
var isServiceReady=false;var onServiceReadyCallbacks=[];
var __uniConfig = {"pages":["pages/home/index","pages/my/index","pages/my/recordsList","pages/user/login","pages/user/forget","pages/user/workOrder","pages/my/index","pages/my/set","pages/my/persData","pages/my/aboutUs","pages/talents/index","pages/talents/detail","pages/talents/certificateUrl","pages/doctors/index","pages/wumen/index","uni_modules/uni-upgrade-center-app/pages/upgrade-popup"],"window":{"navigationBarTextStyle":"black","navigationBarTitleText":"uni-app","navigationBarBackgroundColor":"#F8F8F8","backgroundColor":"#F8F8F8"},"tabBar":{"color":"#333","selectedColor":"#5188e5","borderStyle":"black","backgroundColor":"#fff","list":[{"pagePath":"pages/home/index","text":"智慧医疗"},{"pagePath":"pages/talents/index","text":"太湖英才"},{"pagePath":"pages/my/index","text":"我的"}]},"darkmode":false,"nvueCompiler":"uni-app","nvueStyleCompiler":"uni-app","renderer":"auto","splashscreen":{"alwaysShowBeforeRender":false,"autoclose":true},"appname":"太湖云医","compilerVersion":"4.45","entryPagePath":"pages/home/index","networkTimeout":{"request":60000,"connectSocket":60000,"uploadFile":60000,"downloadFile":60000}};
var __uniRoutes = [{"path":"/pages/home/index","meta":{"isQuit":true,"isTabBar":true},"window":{"navigationBarTitleText":"首页","bounce":"none","titleNView":false}},{"path":"/pages/my/index","meta":{"isQuit":true,"isTabBar":true},"window":{"navigationBarTitleText":"我的","enablePullDownRefresh":false,"bounce":"none","titleNView":false}},{"path":"/pages/my/recordsList","meta":{},"window":{"navigationBarTitleText":"历史记录","enablePullDownRefresh":false,"bounce":"none","titleNView":false}},{"path":"/pages/user/login","meta":{},"window":{"navigationBarTitleText":"登录","bounce":"none","titleNView":false}},{"path":"/pages/user/forget","meta":{},"window":{"navigationBarTitleText":"忘记密码","bounce":"none","titleNView":false}},{"path":"/pages/user/workOrder","meta":{},"window":{"navigationBarTitleText":"问题反馈","enablePullDownRefresh":false,"bounce":"none","titleNView":false}},{"path":"/pages/my/set","meta":{},"window":{"navigationBarTitleText":"设置","enablePullDownRefresh":false,"bounce":"none","titleNView":false}},{"path":"/pages/my/persData","meta":{},"window":{"navigationBarTitleText":"个人资料","enablePullDownRefresh":false,"bounce":"none","titleNView":false}},{"path":"/pages/my/aboutUs","meta":{},"window":{"navigationBarTitleText":"关于我们","enablePullDownRefresh":false,"bounce":"none","titleNView":false}},{"path":"/pages/talents/index","meta":{"isQuit":true,"isTabBar":true},"window":{"navigationBarTitleText":"太湖英才","enablePullDownRefresh":false,"bounce":"none","titleNView":false}},{"path":"/pages/talents/detail","meta":{},"window":{"navigationBarTitleText":"医生主页","enablePullDownRefresh":false,"bounce":"none","titleNView":false}},{"path":"/pages/talents/certificateUrl","meta":{},"window":{"navigationBarTitleText":"医生证书","enablePullDownRefresh":false,"bounce":"none","titleNView":false}},{"path":"/pages/doctors/index","meta":{},"window":{"navigationBarTitleText":"名医精彩","enablePullDownRefresh":false,"bounce":"none","titleNView":false}},{"path":"/pages/wumen/index","meta":{},"window":{"navigationBarTitleText":"吴门医述","enablePullDownRefresh":false,"bounce":"none","titleNView":false}},{"path":"/uni_modules/uni-upgrade-center-app/pages/upgrade-popup","meta":{},"window":{"disableScroll":true,"animationDuration":200,"animationType":"fade-in","background":"transparent","backgroundColorTop":"transparent","scrollIndicator":false,"titleNView":false}}];
var __uniConfig = {"pages":["pages/home/index","pages/home/test","pages/user/login","pages/my/index","pages/my/recordsList","pages/user/forget","pages/user/workOrder","pages/my/index","pages/my/set","pages/my/persData","pages/my/aboutUs","pages/talents/index","pages/talents/detail","pages/talents/certificateUrl","pages/doctors/index","pages/wumen/index","pages/folder/index","pages/folder/patient","pages/wallet/recharge","pages/wallet/account","pages/wallet/points","pages/vip/index","pages/order/index","pages/order/infor","uni_modules/uni-upgrade-center-app/pages/upgrade-popup"],"window":{"navigationBarTextStyle":"black","navigationBarTitleText":"uni-app","navigationBarBackgroundColor":"#F8F8F8","backgroundColor":"#F8F8F8"},"tabBar":{"color":"#333","selectedColor":"#5188e5","borderStyle":"black","backgroundColor":"#fff","list":[{"pagePath":"pages/home/index","text":"智慧医疗"},{"pagePath":"pages/talents/index","text":"太湖英才"},{"pagePath":"pages/my/index","text":"我的"}]},"darkmode":false,"nvueCompiler":"uni-app","nvueStyleCompiler":"uni-app","renderer":"auto","splashscreen":{"alwaysShowBeforeRender":false,"autoclose":true},"appname":"太湖云医","compilerVersion":"4.45","entryPagePath":"pages/home/index","networkTimeout":{"request":60000,"connectSocket":60000,"uploadFile":60000,"downloadFile":60000}};
var __uniRoutes = [{"path":"/pages/home/index","meta":{"isQuit":true,"isTabBar":true},"window":{"navigationBarTitleText":"首页","bounce":"none","titleNView":false}},{"path":"/pages/home/test","meta":{},"window":{"navigationBarTitleText":"首页","bounce":"none","titleNView":false}},{"path":"/pages/user/login","meta":{},"window":{"navigationBarTitleText":"登录","bounce":"none","titleNView":false}},{"path":"/pages/my/index","meta":{"isQuit":true,"isTabBar":true},"window":{"navigationBarTitleText":"我的","enablePullDownRefresh":false,"bounce":"none","titleNView":false}},{"path":"/pages/my/recordsList","meta":{},"window":{"navigationBarTitleText":"历史记录","enablePullDownRefresh":false,"bounce":"none","titleNView":false}},{"path":"/pages/user/forget","meta":{},"window":{"navigationBarTitleText":"忘记密码","bounce":"none","titleNView":false}},{"path":"/pages/user/workOrder","meta":{},"window":{"navigationBarTitleText":"问题反馈","enablePullDownRefresh":false,"bounce":"none","titleNView":false}},{"path":"/pages/my/set","meta":{},"window":{"navigationBarTitleText":"设置","enablePullDownRefresh":false,"bounce":"none","titleNView":false}},{"path":"/pages/my/persData","meta":{},"window":{"navigationBarTitleText":"个人资料","enablePullDownRefresh":false,"bounce":"none","titleNView":false}},{"path":"/pages/my/aboutUs","meta":{},"window":{"navigationBarTitleText":"关于我们","enablePullDownRefresh":false,"bounce":"none","titleNView":false}},{"path":"/pages/talents/index","meta":{"isQuit":true,"isTabBar":true},"window":{"navigationBarTitleText":"太湖英才","enablePullDownRefresh":false,"bounce":"none","titleNView":false}},{"path":"/pages/talents/detail","meta":{},"window":{"navigationBarTitleText":"医生主页","enablePullDownRefresh":false,"bounce":"none","titleNView":false}},{"path":"/pages/talents/certificateUrl","meta":{},"window":{"navigationBarTitleText":"医生证书","enablePullDownRefresh":false,"bounce":"none","titleNView":false}},{"path":"/pages/doctors/index","meta":{},"window":{"navigationBarTitleText":"名医精彩","enablePullDownRefresh":false,"bounce":"none","titleNView":false}},{"path":"/pages/wumen/index","meta":{},"window":{"navigationBarTitleText":"吴门医述","enablePullDownRefresh":false,"bounce":"none","titleNView":false}},{"path":"/pages/folder/index","meta":{},"window":{"navigationBarTitleText":"我的病历夹","enablePullDownRefresh":false,"bounce":"none","titleNView":false}},{"path":"/pages/folder/patient","meta":{},"window":{"navigationBarTitleText":"患者列表","enablePullDownRefresh":false,"bounce":"none","titleNView":false}},{"path":"/pages/wallet/recharge","meta":{},"window":{"navigationBarTitleText":"充值","enablePullDownRefresh":false,"bounce":"none","titleNView":false}},{"path":"/pages/wallet/account","meta":{},"window":{"navigationBarTitleText":"我的账户","enablePullDownRefresh":false,"bounce":"none","titleNView":false}},{"path":"/pages/wallet/points","meta":{},"window":{"navigationBarTitleText":"我的积分","enablePullDownRefresh":false,"bounce":"none","titleNView":false}},{"path":"/pages/vip/index","meta":{},"window":{"navigationBarTitleText":"VIP办理","enablePullDownRefresh":false,"bounce":"none","titleNView":false}},{"path":"/pages/order/index","meta":{},"window":{"navigationBarTitleText":"我的订单","enablePullDownRefresh":false,"bounce":"none","titleNView":false}},{"path":"/pages/order/infor","meta":{},"window":{"navigationBarTitleText":"订单详情","enablePullDownRefresh":false,"bounce":"none","titleNView":false}},{"path":"/uni_modules/uni-upgrade-center-app/pages/upgrade-popup","meta":{},"window":{"disableScroll":true,"animationDuration":200,"animationType":"fade-in","background":"transparent","backgroundColorTop":"transparent","scrollIndicator":false,"titleNView":false}}];
__uniConfig.onReady=function(callback){if(__uniConfig.ready){callback()}else{onReadyCallbacks.push(callback)}};Object.defineProperty(__uniConfig,"ready",{get:function(){return isReady},set:function(val){isReady=val;if(!isReady){return}const callbacks=onReadyCallbacks.slice(0);onReadyCallbacks.length=0;callbacks.forEach(function(callback){callback()})}});
__uniConfig.onServiceReady=function(callback){if(__uniConfig.serviceReady){callback()}else{onServiceReadyCallbacks.push(callback)}};Object.defineProperty(__uniConfig,"serviceReady",{get:function(){return isServiceReady},set:function(val){isServiceReady=val;if(!isServiceReady){return}const callbacks=onServiceReadyCallbacks.slice(0);onServiceReadyCallbacks.length=0;callbacks.forEach(function(callback){callback()})}});
service.register("uni-app-config",{create(a,b,c){if(!__uniConfig.viewport){var d=b.weex.config.env.scale,e=b.weex.config.env.deviceWidth,f=Math.ceil(e/d);Object.assign(__uniConfig,{viewport:f,defaultFontSize:Math.round(f/20)})}return{instance:{__uniConfig:__uniConfig,__uniRoutes:__uniRoutes,global:void 0,window:void 0,document:void 0,frames:void 0,self:void 0,location:void 0,navigator:void 0,localStorage:void 0,history:void 0,Caches:void 0,screen:void 0,alert:void 0,confirm:void 0,prompt:void 0,fetch:void 0,XMLHttpRequest:void 0,WebSocket:void 0,webkit:void 0,print:void 0}}}});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -2,6 +2,13 @@
$themeColor: #5188e5;
$themeBgColor: #fff !important;
@font-face {
font-family: 'PangMenZhengDaobiaoTiTiMianFeiBan';
src: url('@/static/font/PangMenZhengDaoBiaoTiTiMianFeiBan.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'MicrosoftYaHei';
font-weight: normal;
@@ -88,6 +95,10 @@ $themeBgColor: #fff !important;
box-shadow: 0px 0px 3px 0px rgba(0, 82, 79, 0.2) !important;
}
.PM_font {
font-family: PangMenZhengDaoBiaoTiTiMianFeiBan;
}
.bg_color {
background: rgba(125, 193, 240, 0.1);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 199 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 720 B

After

Width:  |  Height:  |  Size: 902 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB