Compare commits
108
Commits
397cf0c88e
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c7dbbfda16 | ||
|
|
fd78b504a6 | ||
|
|
a26fc1a4a7 | ||
|
|
5d31c3658b | ||
|
|
5f9a346960 | ||
|
|
7180e176eb | ||
|
|
e3a852cb43 | ||
|
|
0a51901807 | ||
|
|
8a879df4b4 | ||
|
|
18c7f7386d | ||
|
|
b5cc11ddd3 | ||
|
|
4a22470f38 | ||
|
|
6cdd8b90db | ||
|
|
cbc427e41c | ||
|
|
45a9478790 | ||
|
|
c6ce198c31 | ||
|
|
f4fe516224 | ||
|
|
80772749b2 | ||
|
|
0fa310e422 | ||
|
|
761a8fdaa4 | ||
|
|
ca69dfbe4b | ||
|
|
4042df6762 | ||
|
|
4e3a30b863 | ||
|
|
2831de027c | ||
|
|
08e3ba22c5 | ||
|
|
40f5ddf4a5 | ||
|
|
1fa822ed72 | ||
|
|
89848ba321 | ||
|
|
7fb1200528 | ||
|
|
b833a00d65 | ||
|
|
e8e9c84cdf | ||
|
|
1e99b3a082 | ||
|
|
efcc7d9603 | ||
|
|
54f8132cee | ||
|
|
ddbfac11c3 | ||
|
|
1de18aa228 | ||
|
|
799471090f | ||
|
|
ae9a961a42 | ||
|
|
db9ca707ae | ||
|
|
a7b2a673a9 | ||
|
|
d06ee397cf | ||
|
|
034744959f | ||
|
|
cf6df28e8c | ||
|
|
00d0ec8a9b | ||
|
|
56c645a75c | ||
|
|
8025b2abab | ||
|
|
c86aa03f01 | ||
|
|
22104881f1 | ||
|
|
f4c8df1950 | ||
|
|
1785e9309e | ||
|
|
da3659412d | ||
|
|
51367c48c9 | ||
|
|
41079458dc | ||
|
|
60531b339e | ||
|
|
9788e723a2 | ||
|
|
91726b87c5 | ||
|
|
6b37f0899d | ||
|
|
f4b1ef932d | ||
|
|
76e619b85d | ||
|
|
2cfc225b55 | ||
|
|
2cfa4bdc53 | ||
|
|
9e0a5d2512 | ||
|
|
589092ec62 | ||
|
|
1255ce8aa5 | ||
|
|
4dca51b0a3 | ||
|
|
8d9ad1ca51 | ||
|
|
e7cebcb00c | ||
|
|
dcade02e47 | ||
|
|
f7d44b9c27 | ||
|
|
d1a4f610d3 | ||
|
|
6b238c2d33 | ||
|
|
7c46a3487a | ||
|
|
38d13d33ce | ||
|
|
5d2d8cc9ff | ||
|
|
5af6f8c906 | ||
|
|
1e134f236c | ||
|
|
4816054b96 | ||
|
|
ea320f4e54 | ||
|
|
d44cd1516a | ||
|
|
2a1f4a2405 | ||
|
|
2532aa055c | ||
|
|
b16601baa6 | ||
|
|
8b1e9bcb41 | ||
|
|
dd9b2984b2 | ||
|
|
65b2d269c5 | ||
|
|
570e47698c | ||
|
|
71136a03a6 | ||
|
|
053063dc88 | ||
|
|
4703b22d25 | ||
|
|
df4d95993a | ||
|
|
36cda41b14 | ||
|
|
9e78b974a2 | ||
|
|
bdd2ba278e | ||
|
|
70eeb19867 | ||
|
|
ae08b222af | ||
|
|
b884a8f6fc | ||
|
|
215b3298d0 | ||
|
|
7ce2ad8d8e | ||
|
|
1ad67cf0dd | ||
|
|
d827caed33 | ||
|
|
1556ea2ec7 | ||
|
|
d40f65de65 | ||
|
|
c711c53959 | ||
|
|
ad39805b83 | ||
|
|
b8f6257bf1 | ||
|
|
8a38bafabe | ||
|
|
dc17a06548 | ||
|
|
a5b24e54b2 |
@@ -0,0 +1,12 @@
|
||||
|
||||
changed 1 package, and audited 42 packages in 5s
|
||||
|
||||
7 packages are looking for funding
|
||||
run `npm fund` for details
|
||||
|
||||
3 high severity vulnerabilities
|
||||
|
||||
Some issues need review, and may require choosing
|
||||
a different dependency.
|
||||
|
||||
Run `npm audit` for details.
|
||||
@@ -1,53 +1,264 @@
|
||||
<script>
|
||||
export default {
|
||||
onLaunch: function() {
|
||||
console.log('App Launch')
|
||||
},
|
||||
onShow: function() {
|
||||
console.log('App Show')
|
||||
},
|
||||
onHide: function() {
|
||||
console.log('App Hide')
|
||||
}
|
||||
}
|
||||
import version from '@/utils/version.js'
|
||||
export default {
|
||||
globalData: {
|
||||
plAd: true,
|
||||
dotNumber: {},
|
||||
apiHost: "https://dev-app.igandan.com/app",
|
||||
//BASE_URL='https://dev-app.igandan.com/app'
|
||||
},
|
||||
onLaunch(options) {
|
||||
//uni.setStorageSync('apiHost', 'value')
|
||||
console.log("打印options");
|
||||
console.log(options);
|
||||
if (
|
||||
options &&
|
||||
options.referrerInfo.extraData &&
|
||||
options.referrerInfo.extraData.token
|
||||
) {
|
||||
uni.setStorageSync(
|
||||
"AUTH_TOKEN_App",
|
||||
options.referrerInfo.extraData.token
|
||||
);
|
||||
uni.setStorageSync("userInfo", options.referrerInfo.extraData.userInfo);
|
||||
if(process.env.UNI_PLATFORM =="mp-weixin"){
|
||||
uni.setStorageSync("app_version",version);
|
||||
}else{
|
||||
let app_version=options.referrerInfo.extraData.app_version?options.referrerInfo.extraData.app_version:version;
|
||||
uni.setStorageSync("app_version",app_version);
|
||||
};
|
||||
|
||||
//getApp({allowDefault: true}).globalData.apiHost = options.referrerInfo.extraData.apiHost;
|
||||
}
|
||||
if (
|
||||
options &&
|
||||
options.referrerInfo.extraData &&
|
||||
options.referrerInfo.extraData.targetPath
|
||||
) {
|
||||
let src=options.referrerInfo.extraData.targetPath
|
||||
|
||||
if(src.indexOf('downLoadVideo') > -1 ){
|
||||
uni.navigateTo({
|
||||
url: src+'?from=mine',
|
||||
});
|
||||
}else{
|
||||
uni.navigateTo({
|
||||
url: src,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
console.log("app传递登录信息onLaunch-------------------");
|
||||
try {
|
||||
// #ifdef APP
|
||||
uni.onNativeEventReceive((event, data) => {
|
||||
console.log("event", event);
|
||||
console.log("data", data);
|
||||
if (event.indexOf("clearToken") > -1) {
|
||||
uni.setStorageSync("AUTH_TOKEN_App", "");
|
||||
plus.runtime.quit();
|
||||
}
|
||||
if(event.indexOf("goPage") > -1){
|
||||
if(data.indexOf('downLoadVideo') > -1){
|
||||
uni.navigateTo({
|
||||
url: data+'?from=mine'
|
||||
});
|
||||
}else{
|
||||
uni.navigateTo({
|
||||
url: data,
|
||||
});
|
||||
}
|
||||
}
|
||||
if(event.indexOf('paySuccess') > -1){
|
||||
console.log('app.vue监听到成功支付');
|
||||
if(data.type=='GandanFile'){
|
||||
uni.$emit('paySuccess',{
|
||||
type:'GandanFile',
|
||||
});
|
||||
}else if(data.type=='BuyPoint'){
|
||||
uni.navigateBack();
|
||||
}
|
||||
}
|
||||
});
|
||||
// #endif
|
||||
} catch (error) {
|
||||
console.log("app传递登录信息error-------------------");
|
||||
console.log(error);
|
||||
}
|
||||
},
|
||||
onShow() {
|
||||
try {
|
||||
// #ifdef APP
|
||||
let checkTokenCallBackFlag = false;
|
||||
console.log("执行onshow");
|
||||
|
||||
uni.sendNativeEvent(
|
||||
"checkToken",
|
||||
{
|
||||
msg: "checkToken",
|
||||
},
|
||||
(ret) => {
|
||||
checkTokenCallBackFlag = true;
|
||||
console.log("check回调");
|
||||
console.log(ret);
|
||||
if (ret.code == 0) {
|
||||
uni.showModal({
|
||||
title: "提示",
|
||||
showCancel: false,
|
||||
content: "您已退出登录!",
|
||||
success: function (res) {
|
||||
if (res.confirm) {
|
||||
uni.sendNativeEvent(
|
||||
"goTabbarPage",
|
||||
{
|
||||
msg: "home",
|
||||
},
|
||||
(ret) => {
|
||||
console.log(ret);
|
||||
}
|
||||
);
|
||||
plus.runtime.quit();
|
||||
} else if (res.cancel) {
|
||||
console.log("用户点击取消");
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// setTimeout(function(){
|
||||
// console.log("执行 setTimeout")
|
||||
// if(!checkTokenCallBackFlag){
|
||||
// uni.showModal({
|
||||
// title: '提示',
|
||||
// showCancel: false,
|
||||
// content: '您已退出登录',
|
||||
// success: function (res) {
|
||||
// if (res.confirm) {
|
||||
// uni.sendNativeEvent('goTabbarPage', {
|
||||
// msg: 'home'
|
||||
// }, ret => {
|
||||
// console.log(ret);
|
||||
// })
|
||||
// } else if (res.cancel) {
|
||||
// console.log('用户点击取消');
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// }, 1000)
|
||||
|
||||
|
||||
let main = plus.android.runtimeMainActivity();
|
||||
//为了防止快速点按返回键导致程序退出重写quit方法改为隐藏至后台
|
||||
plus.runtime.quit = function () {
|
||||
main.moveTaskToBack(false);
|
||||
};
|
||||
//重写toast方法如果内容为 ‘再按一次退出应用’ 就隐藏应用,其他正常toast
|
||||
plus.nativeUI.toast = function (str) {
|
||||
if (str == "再按一次退出应用") {
|
||||
main.moveTaskToBack(false);
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: str,
|
||||
icon: "none",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// uni.onNativeEventReceive((event, data) => {
|
||||
// console.log("app传递登录信息onshow-------------------");
|
||||
// console.log("event", event);
|
||||
// console.log("data", data);
|
||||
// if (event == "loginInfo") {
|
||||
// uni.setStorageSync("DEV_AUTH_YX_TOKEN_App", data.token);
|
||||
// uni.setStorageSync("userInfo", data.userInfo);
|
||||
// }else if(event == "showRedDot"){
|
||||
// console.log("showRedDot");
|
||||
// console.log(data);
|
||||
// getApp().globalData.dotNumber=data;
|
||||
// }
|
||||
// });
|
||||
// #endif
|
||||
} catch (error) {
|
||||
console.log("error");
|
||||
console.log(error);
|
||||
}
|
||||
},
|
||||
onHide() {},
|
||||
methods: {},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
/*每个页面公共css */
|
||||
@import '@/uni_modules/uni-scss/index.scss';
|
||||
/* #ifndef APP-NVUE */
|
||||
@import '@/static/customicons.css';
|
||||
// 设置整个项目的背景色
|
||||
page {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
<template>
|
||||
<GlobalDialog />
|
||||
</template>
|
||||
|
||||
/* #endif */
|
||||
.uni-nav-bar-text{
|
||||
font-weight: bold;
|
||||
font-size: 34rpx!important;
|
||||
}
|
||||
.example-info {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
padding: 10px;
|
||||
}
|
||||
.twoline{
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.oneline{
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.uni-navbar .uniui-left{
|
||||
font-weight: bold;
|
||||
font-size: 50rpx!important;
|
||||
}
|
||||
::-webkit-scrollbar { display: none; }
|
||||
</style>
|
||||
<style lang="scss">
|
||||
/*每个页面公共css */
|
||||
@import "@/uni_modules/uni-scss/index.scss";
|
||||
/* #ifndef APP-NVUE */
|
||||
@import "@/static/customicons.css";
|
||||
|
||||
// 设置整个项目的背景色
|
||||
page {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.uni-navbar__header-btns-right {
|
||||
min-width: 120rpx;
|
||||
width: auto !important;
|
||||
}
|
||||
|
||||
/* #endif */
|
||||
.uni-nav-bar-text {
|
||||
|
||||
font-size: 40rpx !important;
|
||||
}
|
||||
|
||||
.example-info {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.twoline {
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.oneline {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.uni-navbar .uniui-left {
|
||||
font-weight: bold;
|
||||
font-size: 50rpx !important;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.status_bar {
|
||||
height: var(--status-bar-height);
|
||||
background-color: #eeeeee;
|
||||
}
|
||||
|
||||
.navbox {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: calc(var(--status-bar-height) + 44px);
|
||||
z-index: 9999;
|
||||
}
|
||||
.share-content .share-item:last-child{
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"prompt" : "none"
|
||||
}
|
||||
+343
-5
@@ -1,4 +1,5 @@
|
||||
import {request} from '@/utils/request.js'
|
||||
|
||||
const api = {
|
||||
|
||||
expertWxLogin(jscode){
|
||||
@@ -119,11 +120,11 @@ const api = {
|
||||
},
|
||||
// 积分支付列表
|
||||
bonusPointsPayList(data) {
|
||||
return request('/expertAPI/bonusPointsPayList', data, 'post', false);
|
||||
return request('/expertAPI/bonusPointsPayList', data, 'post', true);
|
||||
},
|
||||
// 积分列表
|
||||
bonusPointsList(data) {
|
||||
return request('/expertAPI/bonusPointsList', data, 'post', false);
|
||||
return request('/expertAPI/bonusPointsList', data, 'post', true);
|
||||
},
|
||||
|
||||
getWelfarePage(data) {
|
||||
@@ -281,9 +282,9 @@ const api = {
|
||||
return request('/expertAPI/appMesageRead', data, 'post', false);
|
||||
},
|
||||
|
||||
bankCardList(data){
|
||||
return request('/pingExpertV2/bankCardList', data, 'post', false);
|
||||
},
|
||||
// bankCardList(data){
|
||||
// return request('/pingExpertV2/bankCardList', data, 'post', false);
|
||||
// },
|
||||
|
||||
identificationBankCardNew(data){
|
||||
return request('/pingExpertV2/identificationBankCardNew', data, 'post', false);
|
||||
@@ -300,7 +301,17 @@ const api = {
|
||||
smsSend(data){
|
||||
return request('/expertAPI/smsSend', data, 'post', false);
|
||||
},
|
||||
pointTicketlist(data){
|
||||
return request('/expertAPI/pointTicketlist', data, 'post', false);
|
||||
},
|
||||
|
||||
pointTicketExchange(data){
|
||||
return request('/expertAPI/updateStatus', data, 'post', false);
|
||||
},
|
||||
|
||||
addFeedBack(data){
|
||||
return request('/expertAPI/addFeedBack', data, 'post', false);
|
||||
},
|
||||
// 肝胆新闻相关API
|
||||
// 顶部轮播
|
||||
newsRollNew(data){
|
||||
@@ -325,9 +336,15 @@ const api = {
|
||||
applyList(data){
|
||||
return request('/expertAPI/applyList', data, 'post', false);
|
||||
},
|
||||
patientListByGBKPage(data){
|
||||
return request('/expertAPI/patientListByGBKPage', data, 'post', false);
|
||||
},
|
||||
patientListByGBK(data){
|
||||
return request('/expertAPI/patientListByGBK', data, 'post', false);
|
||||
},
|
||||
patientListByGBKU(data){
|
||||
return request('/expertAPI/patientListByGBKU', data, 'post', false);
|
||||
},
|
||||
followUpList(data){
|
||||
return request('/expertAPI/followUpList', data, 'post', false);
|
||||
},
|
||||
@@ -394,6 +411,327 @@ const api = {
|
||||
polularScienceArticleListIndexNew(data){
|
||||
return request('/expertAPI/polularScienceArticleListIndexNew', data, 'post', false);
|
||||
},
|
||||
|
||||
bankCardList(data){
|
||||
return request('/expertPay/bankCardList', data, 'post', false);
|
||||
},
|
||||
|
||||
deleteBankCard(data){
|
||||
return request('/expertPay/deleteBankCard', data, 'post', false);
|
||||
},
|
||||
getIncomeTax(data){
|
||||
return request('/expertPay/getIncomeTax', data, 'post', false);
|
||||
},
|
||||
createWithdrawal(data){
|
||||
return request('/expertPay/createWithdrawal', data, 'post', false);
|
||||
},
|
||||
|
||||
meetingListBySearch(data){
|
||||
return request('/expertAPI/meetingListBySearch', data, 'post', false);
|
||||
},
|
||||
getExpertByUuid(data){
|
||||
return request('/expertAPI/getExpertByUuid', data, 'post', false);
|
||||
},
|
||||
getUserIcon(data){
|
||||
return request('/expertAPI/getUserIcon', data, 'post', false);
|
||||
},
|
||||
addUserIcon(data){
|
||||
return request('/expertAPI/addUserIcon', data, 'post', false);
|
||||
},
|
||||
getDiseaseList(data){
|
||||
return request('/expertAPI/disease', data, 'post', false);
|
||||
},
|
||||
getPositionList(data){
|
||||
return request('/expertAPI/positionList', data, 'post', false);
|
||||
},
|
||||
getOfficeList(data){
|
||||
return request('/expertAPI/officeList', data, 'post', false);
|
||||
},
|
||||
newConsultList(data){
|
||||
return request('/expertAPI/newConsultList', data, 'post', false);
|
||||
},
|
||||
consultListHis(data){
|
||||
return request('/expertAPI/consultListHis', data, 'post', false);
|
||||
},
|
||||
listNewInterrogation(data){
|
||||
return request('/expertAPI/listNewInterrogation', data, 'post', true);
|
||||
},
|
||||
listMyAnsweredInterrogation(data){
|
||||
return request('/expertAPI/listMyAnsweredInterrogation', data, 'post', true);
|
||||
},
|
||||
getInterrogation(data){
|
||||
return request('/expertAPI/getInterrogation', data, 'post', false);
|
||||
},
|
||||
interrogationPatientInfo(data){
|
||||
return request('/expertAPI/InterrogationPatientInfo', data, 'post', false);
|
||||
},
|
||||
updateInterrogationAnswer(data){
|
||||
return request('/expertAPI/updateInterrogationAnswerU', data, 'post', true);
|
||||
},
|
||||
addInterrogationAnswer(data){
|
||||
return request('/expertAPI/addInterrogationAnswerU', data, 'post', true);
|
||||
},
|
||||
videoDetail(data){
|
||||
return request('/expertAPI/videoDetail', data, 'post', false);
|
||||
},
|
||||
addVideoWatchRecord(data){
|
||||
return request('/expertAPI/addVideoWatchRecord', data, 'post', false);
|
||||
},
|
||||
videoCommentListV2(data){
|
||||
return request('/expertAPI/videoCommentListV2', data, 'post', false);
|
||||
},
|
||||
isVideoDownloadRecord(data){
|
||||
return request('/expertAPI/isVideoDownloadRecord', data, 'post', false);
|
||||
},
|
||||
getWelfareNum(data){
|
||||
return request('/expertAPI/getWelfareNum', data, 'post', false);
|
||||
},
|
||||
payVideoDownload(data){
|
||||
return request('/expertAPI/payVideoDownload', data, 'post', false);
|
||||
},
|
||||
addCommentV2(data){
|
||||
return request('/expertAPI/addCommentV2', data, 'post', false);
|
||||
},
|
||||
collection(data){
|
||||
return request('/expertAPI/collection', data, 'post', false);
|
||||
},
|
||||
discollection(data){
|
||||
return request('/expertAPI/discollection', data, 'post', false);
|
||||
},
|
||||
meetingV2Video(data){
|
||||
return request('/expertAPI/meetingV2Video', data, 'post', false);
|
||||
},
|
||||
listGroupSendMsg(data){
|
||||
return request('/expertAPI/listGroupSendMsg', data, 'post', false);
|
||||
},
|
||||
addGroupSendMsg4YunXin(data){
|
||||
return request('/expertAPI/addGroupSendMsg4YunXin', data, 'post', false);
|
||||
},
|
||||
quickReplyList(data){
|
||||
return request('/expertAPI/QuickReplyList', data, 'post', false);
|
||||
},
|
||||
addQuickReply(data){
|
||||
return request('/expertAPI/addQuickReply', data, 'post', false);
|
||||
},
|
||||
deleteQuickReply(data){
|
||||
return request('/expertAPI/deleteQuickReply', data, 'post', false);
|
||||
},
|
||||
listOutPatient(data){
|
||||
return request('/expertAPI/listOutPatient', data, 'post', false);
|
||||
},
|
||||
stopOutPatientList(data){
|
||||
return request('/expertAPI/stopOutPatientList', data, 'post', false);
|
||||
},
|
||||
listWorkPlace(data){
|
||||
return request('/expertAPI/listWorkPlace', data, 'post', false);
|
||||
},
|
||||
addOutPatient(data){
|
||||
return request('/expertAPI/addOutPatient', data, 'post', false);
|
||||
},
|
||||
updateOutPatient(data){
|
||||
return request('/expertAPI/updateOutPatientA', data, 'post', false);
|
||||
},
|
||||
deleteOutPatient(data){
|
||||
return request('/expertAPI/deleteOutPatient', data, 'post', false);
|
||||
},
|
||||
addWorkPlace(data){
|
||||
return request('/expertAPI/addWorkPlace', data, 'post', false);
|
||||
},
|
||||
updateWorkPlace(data){
|
||||
return request('/expertAPI/editWorkPlace', data, 'post', false);
|
||||
},
|
||||
deleteWorkPlace(data){
|
||||
return request('/expertAPI/deleteWorkPlace', data, 'post', false);
|
||||
},
|
||||
hospitalList(data){
|
||||
return request('/expertAPI/hospitalList', data, 'post', false);
|
||||
},
|
||||
addStopPatient(data){
|
||||
return request('/expertAPI/addStopOutPatient', data, 'post', false);
|
||||
},
|
||||
deleteStopPatient(data){
|
||||
return request('/expertAPI/deleteStopOutPatient', data, 'post', false);
|
||||
},
|
||||
addOutPatientA(data){
|
||||
return request('/expertAPI/addOutPatientA', data, 'post', false);
|
||||
},
|
||||
delGroupSendMsg(data){
|
||||
return request('/expertAPI/delGroupSendMsg', data, 'post', false);
|
||||
},
|
||||
patientDetail(data){
|
||||
return request('/expertAPI/patientDetail', data, 'post', false);
|
||||
},
|
||||
toAddNickname(data){
|
||||
return request('/expertAPI/toAddNickname', data, 'post', false);
|
||||
},
|
||||
caseDetail(data){
|
||||
return request('/expertAPI/caseDetail', data, 'post', false);
|
||||
},
|
||||
conditionRecordList(data){
|
||||
return request('/expertAPI/conditionRecordList', data, 'post', false);
|
||||
},
|
||||
addConditionRecord(data){
|
||||
return request('/expertAPI/addConditionRecord', data, 'post', false);
|
||||
},
|
||||
updateConditionRecord(data){
|
||||
return request('/expertAPI/upConditionRecord', data, 'post', false);
|
||||
},
|
||||
deleteConditionRecord(data){
|
||||
return request('/expertAPI/delConditionRecord', data, 'post', false);
|
||||
},
|
||||
consultDetail(data){
|
||||
return request('/expertAPI/consultDetail', data, 'post', false);
|
||||
},
|
||||
isConsultIng(data){
|
||||
return request('/expertAPI/isConsultIng', data, 'post', false);
|
||||
},
|
||||
resConsult(data){
|
||||
return request('/expertAPI/resConsult', data, 'post', false);
|
||||
},
|
||||
countConsult(data){
|
||||
return request('/expertAPI/countConsult', data, 'post', false);
|
||||
},
|
||||
closeConsult(data){
|
||||
return request('/expertAPI/closeConsult', data, 'post', false);
|
||||
},
|
||||
checkUser(data){
|
||||
return request('/expertAPI/checkUser', data, 'post', false);
|
||||
},
|
||||
ganDanFileDetials(data){
|
||||
return request('/expertAPI/ganDanFileDetials', data, 'post', false);
|
||||
},
|
||||
createGanDanFileOrder(data){
|
||||
return request('/expertPay/createGanDanFileOrder', data, 'post', false);
|
||||
},
|
||||
getOrderStatus(data){
|
||||
return request('/expertPay/getOrderStatus', data, 'post', false);
|
||||
},
|
||||
|
||||
getOrderStatus(data){
|
||||
return request('/expertPay/getOrderStatus', data, 'post', false);
|
||||
},
|
||||
getBalance(data){
|
||||
return request('/expertPay/getBalance', data, 'post', false);
|
||||
},
|
||||
payGanDanFile(data){
|
||||
return request('/expertPay/payGanDanFileOrder', data, 'post', false);
|
||||
},
|
||||
// discollection(data){
|
||||
// return request('/expert/discollection', data, 'post', false);
|
||||
// },
|
||||
// collection(data){
|
||||
// return request('/expert/collection', data, 'post', false);
|
||||
// },
|
||||
getTypeUuidByName(data){
|
||||
return request('/expertAPI/getTypeUuidByName', data, 'post', false);
|
||||
},
|
||||
videoByTypeNew(data){
|
||||
return request('/expertAPI/videoByTypeNew', data, 'post', false);
|
||||
},
|
||||
meetingHistoryList(data){
|
||||
return request('/expertAPI/meetingHistoryList', data, 'post', false);
|
||||
},
|
||||
|
||||
// 扫码登录相关API
|
||||
// 确认扫码登录
|
||||
confirmScanLogin(data){
|
||||
return request('/expertAPI/confirmScanLogin', data, 'post', false);
|
||||
},
|
||||
|
||||
// 取消扫码登录
|
||||
cancelScanLogin(data){
|
||||
return request('/expertAPI/cancelScanLogin', data, 'post', false);
|
||||
},
|
||||
|
||||
// 获取扫码登录状态
|
||||
getScanLoginStatus(data){
|
||||
return request('/expertAPI/getScanLoginStatus', data, 'post', false);
|
||||
},
|
||||
gethonorDetail(data){
|
||||
return request('/expertAPI/gethonorDetail', data, 'post', false);
|
||||
},
|
||||
checkVersion(data){
|
||||
return request('/expertAPI/checkVersion', data, 'post', false);
|
||||
},
|
||||
getIsAccount(data){
|
||||
return request('/expertAPI/getIsAccount', data, 'post', false);
|
||||
},
|
||||
patientVideoByJingHuaNew(data){
|
||||
return request('/expertAPI/patientVideoByJingHuaNew', data, 'post', false);
|
||||
},
|
||||
useWelfareNum(data){
|
||||
return request('/expertAPI/useWelfareNum', data, 'post', false);
|
||||
},
|
||||
deleteComment(data){
|
||||
return request('/expertAPI/deleteComment', data, 'post', false);
|
||||
},
|
||||
downloadGanDanFile(data){
|
||||
return request('/expertAPI/downloadGanDanFile', data, 'post', false,'application/json',{},'arraybuffer');
|
||||
},
|
||||
downloadGanDanFileV2(data){
|
||||
return request('/expertAPI/downloadGanDanFileV2', data, 'post', false);
|
||||
},
|
||||
feedBack(data){
|
||||
return request('/expertAPI/addFeedBack', data, 'post', false);
|
||||
},
|
||||
getPointUnitPrice(data){
|
||||
return request('/expertAPI/getPointUnitPrice', data, 'post', false);
|
||||
},
|
||||
welfareList(data){
|
||||
return request('/expertAPI/welfareList', data, 'post', false);
|
||||
},
|
||||
|
||||
getUGoods(data){
|
||||
return request('/expertAPI/getGoodsDetailU', data, 'post', false);
|
||||
},
|
||||
createGoodsOrder(data){
|
||||
return request('/expertAPI/createGoodsOrder', data, 'post', false);
|
||||
},
|
||||
useWelfareNum(data){
|
||||
return request('/expertAPI/useWelfareNum', data, 'post', false);
|
||||
},
|
||||
createBointsOrder(data){
|
||||
return request('/expertPay/createBointsOrder', data, 'post', false);
|
||||
},
|
||||
receiveWelfare(data){
|
||||
return request('/expertAPI/receiveWelfare', data, 'post', false);
|
||||
},
|
||||
getKePuCollection(data){
|
||||
return request('/expertAPI/getKePuCollection', data, 'post', false);
|
||||
},
|
||||
getRead(data){
|
||||
return request('/expertAPI/read', data, 'post', false);
|
||||
},
|
||||
getOutRead(data){
|
||||
return request('/expertAPI/outread', data, 'post', false);
|
||||
},
|
||||
getAgree(data){
|
||||
return request('/expertAPI/agree', data, 'post', false);
|
||||
},
|
||||
getDisagree(data){
|
||||
return request('/expertAPI/disagree', data, 'post', false);
|
||||
},
|
||||
getCollect(data){
|
||||
return request('/expertAPI/collection', data, 'post', false);
|
||||
},
|
||||
getDiscollect(data){
|
||||
return request('/expertAPI/discollection', data, 'post', false);
|
||||
},
|
||||
getGanDanFileOrder(data){
|
||||
return request('/expertPay/getGanDanFileOrder', data, 'post', false);
|
||||
},
|
||||
publishOutPatient(data){
|
||||
return request('/expertAPI/addGroupSendMsg4YunXin', data, 'post', false);
|
||||
},
|
||||
polularScienceArticleListIndexNew(data){
|
||||
return request('/expertAPI/polularScienceArticleListIndexNew', data, 'post', false);
|
||||
},
|
||||
getNewsDetail(data){
|
||||
return request('/expertAPI/newsDetial', data, 'post', false);
|
||||
},
|
||||
|
||||
//https://dev-app.igandan.com/app/expertPay/getGanDanFileOrder
|
||||
|
||||
}
|
||||
|
||||
export default api
|
||||
+16
-2
@@ -23,11 +23,25 @@ const goods_api = {
|
||||
createGoodsOrder(data) {
|
||||
return request('/expertAPI/createGoodsOrder', data, 'post', true);
|
||||
},
|
||||
|
||||
payGoodsOrder(data) {
|
||||
return request('/expertAPI/payGoodsOrder', data, 'post', true);
|
||||
},
|
||||
getGoodsOrderList(data) {
|
||||
return request('/expertAPI/goodsOrderList', data, 'post', true);
|
||||
},
|
||||
getUpanWelfareCount(data){
|
||||
return request('/expertAPI/getUpanWelfareCount', data, 'post', false);
|
||||
},
|
||||
getGoodsOrderDetail(data) {
|
||||
return request('/expertAPI/getGoodsOrder', data, 'post', true);
|
||||
},
|
||||
getOrderTrack(data) {
|
||||
return request('/expertAPI/getOrderTrack', data, 'post', true);
|
||||
},
|
||||
cancelGoodsOrder(data) {
|
||||
return request('/expertAPI/cancleGoodsOrder', data, 'post', true);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
export default goods_api;
|
||||
@@ -0,0 +1,31 @@
|
||||
import {request} from '@/utils/request.js'
|
||||
const my_api = {
|
||||
isBoundWechat() {
|
||||
return request('/expertAPI/isBoundWechatByUuid', {}, 'post', true);
|
||||
},
|
||||
|
||||
unBindWechat(data) {
|
||||
return request('/expertAPI/delWechatExpert', data, 'post', true);
|
||||
},
|
||||
|
||||
bindWechat(data) {
|
||||
return request('/expertAPI/addWechatExpert', data, 'post', true);
|
||||
},
|
||||
|
||||
expertWxLogin(data,header) {
|
||||
return request('/expertAPI/wechatLogin', data, 'post', true,'application/json',header);
|
||||
},
|
||||
|
||||
checkVersion(data) {
|
||||
return request('/expertAPI/checkVersion', data, 'post', true);
|
||||
},
|
||||
|
||||
startpage() {
|
||||
return request('/expertAPI/startpage', {}, 'post', true);
|
||||
},
|
||||
addUniappCid(data) {
|
||||
return request('/expertAPI/addUniappCid', data, 'post', true);
|
||||
}
|
||||
}
|
||||
|
||||
export default my_api;
|
||||
@@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<span
|
||||
class="appellation"
|
||||
:style="{ color: color, fontSize: fontSize + 'px' }"
|
||||
>{{ appellation }}</span
|
||||
>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { autorun } from 'mobx'
|
||||
import { onUnmounted, ref, withDefaults } from 'vue'
|
||||
|
||||
const appellation = ref('')
|
||||
|
||||
const { account, teamId, ignoreAlias, nickFromMsg } = withDefaults(
|
||||
defineProps<{
|
||||
account: string
|
||||
teamId?: string
|
||||
ignoreAlias?: boolean
|
||||
nickFromMsg?: string
|
||||
color?: string
|
||||
fontSize?: number
|
||||
}>(),
|
||||
{
|
||||
teamId: undefined,
|
||||
ignoreAlias: false,
|
||||
nickFromMsg: undefined,
|
||||
color: '#333',
|
||||
fontSize: 16,
|
||||
}
|
||||
)
|
||||
|
||||
const uninstallAppellationWatch = autorun(() => {
|
||||
appellation.value = uni.$UIKitStore.uiStore.getAppellation({
|
||||
account,
|
||||
teamId,
|
||||
ignoreAlias,
|
||||
nickFromMsg,
|
||||
})
|
||||
})
|
||||
onUnmounted(() => {
|
||||
uninstallAppellationWatch()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.appellation {
|
||||
color: #333;
|
||||
font-size: 16px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,193 @@
|
||||
<template>
|
||||
<div
|
||||
class="avatar"
|
||||
:style="{ width: avatarSize + 'px', height: avatarSize + 'px' }"
|
||||
@click="handleAvatarClick"
|
||||
@longpress="longpress"
|
||||
@touchend="touchend"
|
||||
>
|
||||
|
||||
<!-- 使用遮罩层避免android长按头像会出现保存图片的弹窗 -->
|
||||
<div class="img-mask"></div>
|
||||
<image
|
||||
:lazy-load="true"
|
||||
class="avatar-img"
|
||||
v-if="avatarUrl"
|
||||
:src="docUrl+avatarUrl.replace('null','')"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
<image
|
||||
:lazy-load="true"
|
||||
class="avatar-img"
|
||||
v-else-if="userInfo.uuid && account==userInfo.uuid.toLowerCase()"
|
||||
:src="docUrl+userInfo.photo"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
<div class="avatar-name-wrapper" :style="{ backgroundColor: color }">
|
||||
<div class="avatar-name-text" :style="{ fontSize: fontSize + 'px' }">
|
||||
{{ appellation }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { customNavigateTo, customRedirectTo } from '@/utils/im/customNavigate'
|
||||
import { autorun } from 'mobx'
|
||||
import { ref, computed, onUnmounted, withDefaults } from 'vue'
|
||||
import docUrl from "@/utils/docUrl"
|
||||
import { onShow,onLoad,onReady } from "@dcloudio/uni-app";
|
||||
import { V2NIMUser } from 'nim-web-sdk-ng/dist/v2/NIM_UNIAPP_SDK/V2NIMUserService'
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
account: string
|
||||
teamId?: string
|
||||
avatar?: string
|
||||
size?: string
|
||||
gotoUserCard?: boolean
|
||||
fontSize?: string
|
||||
isRedirect?: boolean
|
||||
}>(),
|
||||
{
|
||||
teamId: '',
|
||||
avatar: '',
|
||||
size: '',
|
||||
gotoUserCard: false,
|
||||
fontSize: '',
|
||||
isRedirect: false,
|
||||
}
|
||||
)
|
||||
const userInfo=ref({
|
||||
uuid:''
|
||||
})
|
||||
const $emit = defineEmits(['onLongpress'])
|
||||
|
||||
const avatarSize = props.size || 42
|
||||
const user = ref<V2NIMUser>()
|
||||
let isLongPress = false // uniapp 长按事件也会触发点击事件,此时需要处理
|
||||
|
||||
const appellation = ref<string>('')
|
||||
|
||||
const appellationWatch = autorun(() => {
|
||||
appellation.value = uni.$UIKitStore.uiStore
|
||||
.getAppellation({
|
||||
account: props.account,
|
||||
ignoreAlias: false,
|
||||
})
|
||||
?.slice(0, 2)
|
||||
})
|
||||
|
||||
const userInfoWatch = autorun(() => {
|
||||
uni.$UIKitStore?.userStore?.getUserActive(props.account).then((data) => {
|
||||
user.value = data
|
||||
})
|
||||
})
|
||||
|
||||
const avatarUrl = computed(() => {
|
||||
user.value = uni.$UIKitStore?.userStore?.users?.get(props.account)
|
||||
return props.avatar || user.value?.avatar
|
||||
})
|
||||
|
||||
const key = `__yx_avatar_color_${props.account}__`
|
||||
let color = uni.getStorageSync(key)
|
||||
if (!color) {
|
||||
const colorMap: { [key: number]: string } = {
|
||||
0: '#60CFA7',
|
||||
1: '#53C3F3',
|
||||
2: '#537FF4',
|
||||
3: '#854FE2',
|
||||
4: '#BE65D9',
|
||||
5: '#E9749D',
|
||||
6: '#F9B751',
|
||||
}
|
||||
const _color = colorMap[Math.floor(Math.random() * 7)]
|
||||
uni.setStorageSync(key, _color)
|
||||
color = _color
|
||||
}
|
||||
|
||||
const handleAvatarClick = () => {
|
||||
if (props.gotoUserCard && !isLongPress) {
|
||||
if (props.isRedirect) {
|
||||
if (props.account === uni.$UIKitStore?.userStore?.myUserInfo.accountId) {
|
||||
customRedirectTo({
|
||||
url: `/pages/User/my-detail/index`,
|
||||
})
|
||||
} else {
|
||||
customRedirectTo({
|
||||
url: `/pages/User/friend/index?account=${props.account}`,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if (props.account === uni.$UIKitStore?.userStore?.myUserInfo.accountId) {
|
||||
customNavigateTo({
|
||||
url: `/pages/User/my-detail/index`,
|
||||
})
|
||||
} else {
|
||||
customNavigateTo({
|
||||
url: `/pages/User/friend/index?account=${props.account}`,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const longpress = () => {
|
||||
isLongPress = true
|
||||
$emit('onLongpress')
|
||||
}
|
||||
|
||||
const touchend = () => {
|
||||
const timeOut = setTimeout(() => {
|
||||
isLongPress = false
|
||||
clearTimeout(timeOut)
|
||||
}, 200)
|
||||
}
|
||||
onShow(()=>{
|
||||
userInfo.value=uni.getStorageSync('userInfo');
|
||||
})
|
||||
onReady(()=>{
|
||||
userInfo.value=uni.getStorageSync('userInfo');
|
||||
})
|
||||
onUnmounted(() => {
|
||||
userInfoWatch()
|
||||
appellationWatch()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.avatar {
|
||||
overflow: hidden;
|
||||
border-radius: 50%;
|
||||
flex-shrink: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.img-mask {
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.avatar-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.avatar-name-wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.avatar-name-text {
|
||||
color: #fff;
|
||||
size: 14px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="dot" v-if="dot" :style="customStyle"></div>
|
||||
<div class="badge" v-else-if="text" :style="customStyle">{{ text }}</div>
|
||||
<div class="hidden">{{ props.num }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { StyleValue, computed, withDefaults } from 'vue'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
num: number
|
||||
max?: number
|
||||
dot?: boolean
|
||||
customStyle?: StyleValue
|
||||
}>(),
|
||||
{
|
||||
max: 99,
|
||||
dot: false,
|
||||
}
|
||||
)
|
||||
const max = props.max || 99
|
||||
const text = computed(() => {
|
||||
return props.num > 0 ? (props.num > max ? `${max}+` : props.num + '') : ''
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.dot {
|
||||
background-color: #ff4d4f;
|
||||
color: #fff;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 5px;
|
||||
box-sizing: border-box;
|
||||
z-index: 99;
|
||||
}
|
||||
|
||||
.badge {
|
||||
background-color: #ff4d4f;
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
min-width: 20px;
|
||||
height: 20px;
|
||||
line-height: 19px;
|
||||
border-radius: 10px;
|
||||
padding: 0 5px;
|
||||
box-sizing: border-box;
|
||||
text-align: center;
|
||||
z-index: 99;
|
||||
position: relative;
|
||||
}
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<div class="empty-wrapper">
|
||||
<image
|
||||
class="empty-img"
|
||||
src="https://yx-web-nosdn.netease.im/common/e0f58096f06c18cdd101f2614e6afb09/empty.png"
|
||||
/>
|
||||
<div class="empty-text">{{ text }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
text?: string
|
||||
}>(),
|
||||
{
|
||||
text: '',
|
||||
}
|
||||
)
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.empty-wrapper {
|
||||
margin: 75px 10px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
.empty-img {
|
||||
display: block;
|
||||
width: 125px;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
display: block;
|
||||
color: #a6adb6;
|
||||
margin: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,139 @@
|
||||
<template>
|
||||
<div>
|
||||
<div :class="inputClass">
|
||||
<slot name="addonBefore" />
|
||||
<input
|
||||
class="input"
|
||||
:type="type"
|
||||
:value="inputValue"
|
||||
@input="handleInput"
|
||||
:focus="inputFocus"
|
||||
@focus="handleFocus"
|
||||
@blur="handleBlur"
|
||||
:placeholder="placeholder"
|
||||
:maxlength="maxlength"
|
||||
/>
|
||||
<div class="clear-icon" @tap="clearInput()">
|
||||
<icon v-show="modelValue && allowClear" type="clear" size="16" />
|
||||
</div>
|
||||
<slot name="addonAfter" />
|
||||
</div>
|
||||
|
||||
<div v-if="inputError && rule" class="error-tips">{{ rule.message }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed } from 'vue'
|
||||
const $emit = defineEmits([
|
||||
'update:modelValue',
|
||||
'input',
|
||||
'focus',
|
||||
'blur',
|
||||
'clear',
|
||||
])
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
className?: string
|
||||
type?: string
|
||||
modelValue?: string
|
||||
placeholder?: string
|
||||
allowClear?: boolean
|
||||
rule?: any
|
||||
maxlength?: number
|
||||
}>(),
|
||||
{
|
||||
className: '',
|
||||
type: 'text',
|
||||
modelValue: '',
|
||||
placeholder: '',
|
||||
allowClear: false,
|
||||
rule: null,
|
||||
maxlength: 140,
|
||||
}
|
||||
)
|
||||
|
||||
const inputFocus = ref(false)
|
||||
const inputError = ref(false)
|
||||
// const inputKey = ref(0);
|
||||
|
||||
const inputClass = computed(() => {
|
||||
return [
|
||||
props.className,
|
||||
'form-input-item',
|
||||
{ focus: inputFocus.value, error: inputError.value },
|
||||
]
|
||||
})
|
||||
|
||||
const inputValue = computed(() => {
|
||||
return props.modelValue || ''
|
||||
})
|
||||
|
||||
const handleBlur = (event: any) => {
|
||||
inputFocus.value = false
|
||||
if (props.rule && props.rule.trigger === 'blur') {
|
||||
inputError.value = !props.rule.reg.test(props.modelValue || '')
|
||||
}
|
||||
$emit('blur', event)
|
||||
}
|
||||
|
||||
const handleFocus = (event: any) => {
|
||||
inputFocus.value = true
|
||||
$emit('blur', event)
|
||||
}
|
||||
|
||||
const handleInput = (event: any) => {
|
||||
if (!(props.maxlength && event.detail.value.length > props.maxlength)) {
|
||||
$emit('update:modelValue', event.detail.value)
|
||||
$emit('input', event.detail.value)
|
||||
}
|
||||
// 强制刷新input
|
||||
// inputKey.value++;
|
||||
}
|
||||
|
||||
const clearInput = () => {
|
||||
$emit('update:modelValue', null)
|
||||
$emit('input', null)
|
||||
$emit('clear', '')
|
||||
|
||||
inputFocus.value = true
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
$primary-color: #337eff;
|
||||
$error-color: #f56c6c;
|
||||
|
||||
.form-input-item {
|
||||
border-bottom: 1px solid #dcdfe5;
|
||||
padding: 10px 10px 5px 0px;
|
||||
display: flex;
|
||||
height: 44px;
|
||||
align-items: center;
|
||||
|
||||
&.focus {
|
||||
border-color: $primary-color;
|
||||
}
|
||||
|
||||
&.error {
|
||||
border-color: $error-color;
|
||||
}
|
||||
}
|
||||
|
||||
.input {
|
||||
flex: 1;
|
||||
height: 30px;
|
||||
border: none;
|
||||
outline: none;
|
||||
}
|
||||
.clear-icon {
|
||||
width: 40px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.error-tips {
|
||||
color: $error-color;
|
||||
font-size: 12px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,169 @@
|
||||
<template>
|
||||
<view v-show="visible" class="gd-mask" :class="alignClass" :style="maskStyle">
|
||||
<view
|
||||
class="gd-dialog"
|
||||
@touchstart="onTouchStart"
|
||||
@touchmove="onTouchMove"
|
||||
@touchend="onTouchEnd"
|
||||
>
|
||||
<view class="gd-title">{{ title }}</view>
|
||||
<scroll-view class="gd-content" scroll-y>
|
||||
<text class="gd-text">{{ content }}</text>
|
||||
</scroll-view>
|
||||
<!-- <view class="gd-footer">
|
||||
<view class="gd-btn gd-confirm" @click="handleConfirm">{{ confirmText }}</view>
|
||||
</view> -->
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, onBeforeUnmount, computed } from 'vue'
|
||||
|
||||
const visible = ref(false)
|
||||
const title = ref('消息通知')
|
||||
const content = ref('')
|
||||
const confirmText = ref('我知道了')
|
||||
const position = ref('center') // 'center' | 'top'
|
||||
const offsetTop = ref('20rpx')
|
||||
const autoHideTimer = ref(null)
|
||||
|
||||
const startY = ref(0)
|
||||
const hasSwipedUp = ref(false)
|
||||
|
||||
const emit = defineEmits(['confirm'])
|
||||
|
||||
function handleConfirm(){
|
||||
visible.value = false
|
||||
emit('confirm')
|
||||
}
|
||||
|
||||
function show(payload = {}){
|
||||
title.value = payload.title || '消息通知'
|
||||
content.value = payload.content || ''
|
||||
confirmText.value = payload.confirmText || '我知道了'
|
||||
position.value = payload.position === 'top' ? 'top' : 'center'
|
||||
offsetTop.value = payload.offsetTop || '20rpx'
|
||||
visible.value = true
|
||||
// 自动 5 秒后消失
|
||||
if (autoHideTimer.value) clearTimeout(autoHideTimer.value)
|
||||
autoHideTimer.value = setTimeout(() => {
|
||||
hide()
|
||||
}, 5000)
|
||||
}
|
||||
|
||||
function hide(){
|
||||
visible.value = false
|
||||
if (autoHideTimer.value) {
|
||||
clearTimeout(autoHideTimer.value)
|
||||
autoHideTimer.value = null
|
||||
}
|
||||
}
|
||||
|
||||
function onTouchStart(e){
|
||||
const touch = e.changedTouches ? e.changedTouches[0] : (e.touches ? e.touches[0] : null)
|
||||
if (!touch) return
|
||||
startY.value = touch.clientY || touch.pageY || 0
|
||||
hasSwipedUp.value = false
|
||||
}
|
||||
|
||||
function onTouchMove(e){
|
||||
const touch = e.changedTouches ? e.changedTouches[0] : (e.touches ? e.touches[0] : null)
|
||||
if (!touch) return
|
||||
const currentY = touch.clientY || touch.pageY || 0
|
||||
const deltaY = currentY - startY.value
|
||||
if (deltaY < -60) {
|
||||
hasSwipedUp.value = true
|
||||
}
|
||||
}
|
||||
|
||||
function onTouchEnd(){
|
||||
if (hasSwipedUp.value) {
|
||||
hide()
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
console.log('[GlobalDialog] mounted')
|
||||
uni.$on('global-dialog:show', show)
|
||||
uni.$on('global-dialog:hide', hide)
|
||||
// 便于全局手动触发测试
|
||||
// @ts-ignore
|
||||
uni.$showGlobalDialog = (payload = {}) => {
|
||||
console.log('[GlobalDialog] uni.$showGlobalDialog called', payload)
|
||||
uni.$emit('global-dialog:show', payload)
|
||||
}
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
uni.$off('global-dialog:show', show)
|
||||
uni.$off('global-dialog:hide', hide)
|
||||
})
|
||||
|
||||
const alignClass = computed(() => position.value === 'top' ? 'is-top' : 'is-center')
|
||||
const maskStyle = computed(() => position.value === 'top' ? { paddingTop: offsetTop.value } : {})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.gd-mask{
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
// 非模态:容器不截获事件
|
||||
pointer-events: none;
|
||||
}
|
||||
.gd-mask.is-top{
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
}
|
||||
.gd-dialog{
|
||||
width: 600rpx;
|
||||
max-width: 90%;
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
// 允许对话框自己接收事件(容器为非模态)
|
||||
pointer-events: auto;
|
||||
// 轻微上滑消失的过渡体验
|
||||
transition: transform 0.2s ease, opacity 0.2s ease;
|
||||
}
|
||||
.gd-title{
|
||||
padding: 28rpx 32rpx 12rpx;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #111;
|
||||
text-align: left;
|
||||
}
|
||||
.gd-content{
|
||||
max-height: 420rpx;
|
||||
padding: 0 32rpx 24rpx;
|
||||
}
|
||||
.gd-text{
|
||||
font-size: 28rpx;
|
||||
line-height: 1.7;
|
||||
color: #333;
|
||||
}
|
||||
.gd-footer{
|
||||
padding: 16rpx 24rpx 24rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.gd-btn{
|
||||
min-width: 200rpx;
|
||||
padding: 20rpx 24rpx;
|
||||
text-align: center;
|
||||
border-radius: 12rpx;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
.gd-confirm{
|
||||
background: #2979ff;
|
||||
color: #fff;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,373 @@
|
||||
<template>
|
||||
<view :class="className" :style="iconStyle">
|
||||
<!-- #ifdef APP-PLUS -->
|
||||
<image
|
||||
:src="iconUrl"
|
||||
:style="{
|
||||
width: (width || size) + 'px',
|
||||
height: (height || size) + 'px',
|
||||
}"
|
||||
class="icon"
|
||||
/>
|
||||
<!-- #endif -->
|
||||
<!-- #ifndef APP-PLUS -->
|
||||
<img
|
||||
:src="iconUrl"
|
||||
:style="{
|
||||
width: (width || size) + 'px',
|
||||
height: (height || size) + 'px',
|
||||
}"
|
||||
class="icon"
|
||||
/>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, withDefaults } from 'vue'
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
type: string
|
||||
size?: number
|
||||
width?: number
|
||||
height?: number
|
||||
iconClassName?: string
|
||||
iconStyle?: any
|
||||
}>(),
|
||||
{
|
||||
size: 16,
|
||||
}
|
||||
)
|
||||
const urlMap: any = {
|
||||
'icon-a-1':
|
||||
'https://yx-web-nosdn.netease.im/common/7c2e2c6ff08f4ed60f3ca7d5ab6d38ac/icon-a-1.png',
|
||||
'icon-a-2':
|
||||
'https://yx-web-nosdn.netease.im/common/bb61c05013350e980b3acf77a00aa7b7/icon-a-2.png',
|
||||
'icon-a-3':
|
||||
'https://yx-web-nosdn.netease.im/common/bfe80222635ec3f07e41b2819d002a4c/icon-a-3.png',
|
||||
'icon-a-4':
|
||||
'https://yx-web-nosdn.netease.im/common/e6dd252af94a4716edc191f7d3812042/icon-a-4.png',
|
||||
'icon-a-5':
|
||||
'https://yx-web-nosdn.netease.im/common/4e01e93f3e795d8aeb140e924d8b1206/icon-a-5.png',
|
||||
'icon-a-6':
|
||||
'https://yx-web-nosdn.netease.im/common/a1fa1174a588a984845ba871b1f1e3b0/icon-a-6.png',
|
||||
'icon-a-7':
|
||||
'https://yx-web-nosdn.netease.im/common/e0e30470561cf90446ec31c6d5b206a5/icon-a-7.png',
|
||||
'icon-a-8':
|
||||
'https://yx-web-nosdn.netease.im/common/e44ceea2c764bfbe773e61483ae857eb/icon-a-8.png',
|
||||
'icon-a-9':
|
||||
'https://yx-web-nosdn.netease.im/common/f4e5b303cb77d9ce67a0baf4efaa1125/icon-a-9.png',
|
||||
'icon-a-10':
|
||||
'https://yx-web-nosdn.netease.im/common/28e46e6cc468c4ff6f44532bd6b7f464/icon-a-10.png',
|
||||
'icon-a-11':
|
||||
'https://yx-web-nosdn.netease.im/common/f265ea5a782cebabae08a079045caf2c/icon-a-11.png',
|
||||
'icon-a-12':
|
||||
'https://yx-web-nosdn.netease.im/common/20043e6f8d619ab7bca69f1a0d5a0e56/icon-a-12.png',
|
||||
'icon-a-13':
|
||||
'https://yx-web-nosdn.netease.im/common/c2b2d52a97eea83efb10c9bbc34dc104/icon-a-13.png',
|
||||
'icon-a-14':
|
||||
'https://yx-web-nosdn.netease.im/common/37486551a8bd31e3446ae862cddd3067/icon-a-14.png',
|
||||
'icon-a-15':
|
||||
'https://yx-web-nosdn.netease.im/common/e383cc57dcb7a903887031ed0d341fef/icon-a-15.png',
|
||||
'icon-a-16':
|
||||
'https://yx-web-nosdn.netease.im/common/73e2f9507f8aac7238896a3f1ee8305a/icon-a-16.png',
|
||||
'icon-a-17':
|
||||
'https://yx-web-nosdn.netease.im/common/51a80e8e81a8aa531d1c68e6f79f7d58/icon-a-17.png',
|
||||
'icon-a-18':
|
||||
'https://yx-web-nosdn.netease.im/common/20f8c6153ba0f51c038e04f2fbedf767/icon-a-18.png',
|
||||
'icon-a-19':
|
||||
'https://yx-web-nosdn.netease.im/common/47d420b5251ca7b9ddc564b42564eb01/icon-a-19.png',
|
||||
'icon-a-20':
|
||||
'https://yx-web-nosdn.netease.im/common/8fad6b9427b49f5bfd1a8ec0f8a77fac/icon-a-20.png',
|
||||
'icon-a-21':
|
||||
'https://yx-web-nosdn.netease.im/common/6fa17035135cebe4ca1512b454fa38c2/icon-a-21.png',
|
||||
'icon-a-22':
|
||||
'https://yx-web-nosdn.netease.im/common/12189563eea22258b847cf900dcbc4d4/icon-a-22.png',
|
||||
'icon-a-23':
|
||||
'https://yx-web-nosdn.netease.im/common/3b7dcfe14788bd7d8c2da8aad6c24baa/icon-a-23.png',
|
||||
'icon-a-24':
|
||||
'https://yx-web-nosdn.netease.im/common/6bb0879272c8a3c73524f3b728a93c10/icon-a-24.png',
|
||||
'icon-a-25':
|
||||
'https://yx-web-nosdn.netease.im/common/1f93dac2bdef1f91ca2f7302065954d5/icon-a-25.png',
|
||||
'icon-a-26':
|
||||
'https://yx-web-nosdn.netease.im/common/aa3c8e63ee6c6d605f4606a8094f2094/icon-a-26.png',
|
||||
'icon-a-27':
|
||||
'https://yx-web-nosdn.netease.im/common/000447b3172d70d1b739a170c377eae2/icon-a-27.png',
|
||||
'icon-a-28':
|
||||
'https://yx-web-nosdn.netease.im/common/27c3b8d1acb6f234a7591c2358b549bf/icon-a-28.png',
|
||||
'icon-a-29':
|
||||
'https://yx-web-nosdn.netease.im/common/fbae68926f43367022d0b0c95c15adcf/icon-a-29.png',
|
||||
'icon-a-30':
|
||||
'https://yx-web-nosdn.netease.im/common/61c0b6c12bc6ad2b7e0511d843b03265/icon-a-30.png',
|
||||
'icon-a-31':
|
||||
'https://yx-web-nosdn.netease.im/common/202e7abeaecf651f6da3f7f695ef8752/icon-a-31.png',
|
||||
'icon-a-32':
|
||||
'https://yx-web-nosdn.netease.im/common/6ef4958d39866165212c1b60bc7c6a55/icon-a-32.png',
|
||||
'icon-a-33':
|
||||
'https://yx-web-nosdn.netease.im/common/18f8b53d57a6b47c3876b6938f2f57b9/icon-a-33.png',
|
||||
'icon-a-34':
|
||||
'https://yx-web-nosdn.netease.im/common/84d7491e6d5a6d53848b6a3d12a739b2/icon-a-34.png',
|
||||
'icon-a-35':
|
||||
'https://yx-web-nosdn.netease.im/common/f03fe01986e9e25f3faf1a17c0ef78c8/icon-a-35.png',
|
||||
'icon-a-36':
|
||||
'https://yx-web-nosdn.netease.im/common/4617253245617bcd38ed7165bdb592d8/icon-a-36.png',
|
||||
'icon-a-37':
|
||||
'https://yx-web-nosdn.netease.im/common/9168b11333f59b0ff948003dedb24c84/icon-a-37.png',
|
||||
'icon-a-38':
|
||||
'https://yx-web-nosdn.netease.im/common/1a3cd5d62e92895dbab3d356112b5079/icon-a-38.png',
|
||||
'icon-a-39':
|
||||
'https://yx-web-nosdn.netease.im/common/b465db737c737ab4308c9272a00a1fdf/icon-a-39.png',
|
||||
'icon-a-40':
|
||||
'https://yx-web-nosdn.netease.im/common/6bca766a4904e10934ea2f2450c341b5/icon-a-40.png',
|
||||
'icon-a-41':
|
||||
'https://yx-web-nosdn.netease.im/common/12d5055618b619b568342f0619cacf0c/icon-a-41.png',
|
||||
'icon-a-42':
|
||||
'https://yx-web-nosdn.netease.im/common/8851ccd815375180bd7f8728ead1cdf1/icon-a-42.png',
|
||||
'icon-a-43':
|
||||
'https://yx-web-nosdn.netease.im/common/1ac7cacc9d0063d165f8e2bc3020bfff/icon-a-43.png',
|
||||
'icon-a-44':
|
||||
'https://yx-web-nosdn.netease.im/common/5735260c7e240beaabc360365e6d60c5/icon-a-44.png',
|
||||
'icon-a-45':
|
||||
'https://yx-web-nosdn.netease.im/common/07372c06915a7d0a977a4f042522e2a7/icon-a-45.png',
|
||||
'icon-a-46':
|
||||
'https://yx-web-nosdn.netease.im/common/40c5b2b5769166b51c968942f4abed95/icon-a-46.png',
|
||||
'icon-a-47':
|
||||
'https://yx-web-nosdn.netease.im/common/9334c3ec8c8d54a9a3271a3536ff7c62/icon-a-47.png',
|
||||
'icon-a-48':
|
||||
'https://yx-web-nosdn.netease.im/common/5630ab646533dd6de7e3c1accc3d2ca1/icon-a-48.png',
|
||||
'icon-a-49':
|
||||
'https://yx-web-nosdn.netease.im/common/f2412538fa0da38549c23fd44b25bdfb/icon-a-49.png',
|
||||
'icon-a-50':
|
||||
'https://yx-web-nosdn.netease.im/common/1f28ffd1413aa3fcce1c17aff3694d41/icon-a-50.png',
|
||||
'icon-a-51':
|
||||
'https://yx-web-nosdn.netease.im/common/a99b57b586a85c23cbd8ed65d1a16765/icon-a-51.png',
|
||||
'icon-a-52':
|
||||
'https://yx-web-nosdn.netease.im/common/ce6f64bbe45a42108fd1b1a7b1dae606/icon-a-52.png',
|
||||
'icon-a-53':
|
||||
'https://yx-web-nosdn.netease.im/common/ba5dc56bf8f550da8c2dc1c94e2ee7fb/icon-a-53.png',
|
||||
'icon-a-54':
|
||||
'https://yx-web-nosdn.netease.im/common/81f5b3b693d9133030c013f0f21462ab/icon-a-54.png',
|
||||
'icon-a-55':
|
||||
'https://yx-web-nosdn.netease.im/common/758ed38dea207d60855969689a5dd68b/icon-a-55.png',
|
||||
'icon-a-56':
|
||||
'https://yx-web-nosdn.netease.im/common/a20bee2372cbe399191af3d76a5aad31/icon-a-56.png',
|
||||
'icon-a-57':
|
||||
'https://yx-web-nosdn.netease.im/common/a17fbdbc64cf4b55be377f18ed81879a/icon-a-57.png',
|
||||
'icon-a-58':
|
||||
'https://yx-web-nosdn.netease.im/common/784b31cda55da76d8278a5bd53c2c7e4/icon-a-58.png',
|
||||
'icon-a-59':
|
||||
'https://yx-web-nosdn.netease.im/common/c716afbc25b8809dfb9ca80a09051f6f/icon-a-59.png',
|
||||
'icon-a-60':
|
||||
'https://yx-web-nosdn.netease.im/common/54de44be20c5d21ffad08176b24560a0/icon-a-60.png',
|
||||
'icon-a-61':
|
||||
'https://yx-web-nosdn.netease.im/common/0628fd4507e2a3455028f376d8d5d80a/icon-a-61.png',
|
||||
'icon-a-62':
|
||||
'https://yx-web-nosdn.netease.im/common/fa336dc373d5e395a1e9a541dc9953d2/icon-a-62.png',
|
||||
'icon-a-63':
|
||||
'https://yx-web-nosdn.netease.im/common/868ee8c664b32b34d554d02fc406ab70/icon-a-63.png',
|
||||
'icon-a-64':
|
||||
'https://yx-web-nosdn.netease.im/common/4e33aadf3ca3b918e73ef07e21eb96ac/icon-a-64.png',
|
||||
'icon-a-65':
|
||||
'https://yx-web-nosdn.netease.im/common/c6e5563811d94c82426036b9f96d6de5/icon-a-65.png',
|
||||
'icon-a-66':
|
||||
'https://yx-web-nosdn.netease.im/common/8baa1f43b4e523e524c54f3340ef21cb/icon-a-66.png',
|
||||
'icon-a-67':
|
||||
'https://yx-web-nosdn.netease.im/common/307c3426dccf1252b5967956bcdcf58a/icon-a-67.png',
|
||||
'icon-a-68':
|
||||
'https://yx-web-nosdn.netease.im/common/196f62aa8e8a38bbbcd818ad42729714/icon-a-68.png',
|
||||
'icon-a-70':
|
||||
'https://yx-web-nosdn.netease.im/common/fb54482390faf9d8d9d607d7e3ab691f/icon-a-70.png',
|
||||
'icon-a-Frame7':
|
||||
'https://yx-web-nosdn.netease.im/common/c5c3d0ee0a4000736827cedfd0610172/icon-a-Frame7.png',
|
||||
'icon-a-Frame8':
|
||||
'https://yx-web-nosdn.netease.im/common/9f77f834a99c29ed5c4b4b54b7ade468/icon-a-Frame8.png',
|
||||
'icon-addition':
|
||||
'https://yx-web-nosdn.netease.im/common/6302fcf17e9c4ac65b392553aaccf9b1/icon-addition.png',
|
||||
'icon-biaoqing':
|
||||
'https://yx-web-nosdn.netease.im/common/1a98df356ed629193e50bea570e02a53/icon-biaoqing.png',
|
||||
'icon-chehui':
|
||||
'https://yx-web-nosdn.netease.im/common/958cb6797f69bdfbf5441da25ed997c3/icon-chehui.png',
|
||||
'icon-chuangjianqunzu':
|
||||
'https://yx-web-nosdn.netease.im/common/7b00839704359b7c10f32af7cc8b5911/icon-chuangjianqunzu.png',
|
||||
'icon-computed':
|
||||
'https://yx-web-nosdn.netease.im/common/352cd94c93347f01936b50bb1487b5a4/icon-computed.png',
|
||||
'icon-erfenzhiyiyidu':
|
||||
'https://yx-web-nosdn.netease.im/common/aa6b529567ee6aeb801f34f228600b04/icon-erfenzhiyiyidu.png',
|
||||
'icon-Excel':
|
||||
'https://yx-web-nosdn.netease.im/common/a9793bbb8a7237e9e92f57ad1b469baf/icon-Excel.png',
|
||||
'icon-fasong':
|
||||
'https://yx-web-nosdn.netease.im/common/f8d855dde4840989b4c88e904e1c23bf/icon-fasong.png',
|
||||
'icon-fuzhi1':
|
||||
'https://yx-web-nosdn.netease.im/common/9e0ba675eb4548bffe19da823fb712e3/icon-fuzhi1.png',
|
||||
'icon-guanbi':
|
||||
'https://yx-web-nosdn.netease.im/common/2d07f146ecb4b616632f0fcfdd02b5be/icon-guanbi.png',
|
||||
'icon-guanbi1':
|
||||
'https://yx-web-nosdn.netease.im/common/51bad0b424c2462df8213883e211fe07/icon-guanbi1.png',
|
||||
'icon-guanyu':
|
||||
'https://yx-web-nosdn.netease.im/common/24d2344c49b551d5a605b3e5e3e6f6da/icon-guanyu.png',
|
||||
'icon-huifu':
|
||||
'https://yx-web-nosdn.netease.im/common/153c273cb7b075fc0c37487655c5cbfc/icon-huifu.png',
|
||||
'icon-im-xuanzhong':
|
||||
'https://yx-web-nosdn.netease.im/common/468aaecf148e23c84b821835021dfee1/icon-im-xuanzhong.png',
|
||||
'icon-jiantou':
|
||||
'https://yx-web-nosdn.netease.im/common/c9bb28670c41e208b1d9bcedd6de88a1/icon-jiantou.png',
|
||||
'icon-jiaruqunzu':
|
||||
'https://yx-web-nosdn.netease.im/common/a27ba47b93131b7c52d829b7012fa966/icon-jiaruqunzu.png',
|
||||
'icon-kefu':
|
||||
'https://yx-web-nosdn.netease.im/common/4ddc1b9557e6672b0b45fb8fb992c992/icon-kefu.png',
|
||||
'icon-lahei':
|
||||
'https://yx-web-nosdn.netease.im/common/e9054279ca62db944ef6e5a76687a93a/icon-lahei.png',
|
||||
'icon-lishixiaoxi':
|
||||
'https://yx-web-nosdn.netease.im/common/77888d982110f3c709a3ff4fd24d48dd/icon-lishixiaoxi.png',
|
||||
'icon-More':
|
||||
'https://yx-web-nosdn.netease.im/common/137ad07f3245dc220d5546433db63786/icon-More.png',
|
||||
'icon-PPT':
|
||||
'https://yx-web-nosdn.netease.im/common/1991f8d57d1d432fcd930e4049403958/icon-PPT.png',
|
||||
'icon-qita':
|
||||
'https://yx-web-nosdn.netease.im/common/c6f687c792ef029bef1e59ccce986922/icon-qita.png',
|
||||
'icon-quxiaoxiaoximiandarao':
|
||||
'https://yx-web-nosdn.netease.im/common/f6f54973789b69939c57ae94a38e8d25/icon-quxiaoxiaoximiandarao.png',
|
||||
'icon-quxiaozhiding':
|
||||
'https://yx-web-nosdn.netease.im/common/ad2d451e8f1ee36ec9cec6787d6074c0/icon-quxiaozhiding.png',
|
||||
'icon-RAR1':
|
||||
'https://yx-web-nosdn.netease.im/common/27a743e5696c1ca4416b75c5dde0252c/icon-RAR1.png',
|
||||
'icon-shanchu':
|
||||
'https://yx-web-nosdn.netease.im/common/d96f9c0113af4d86dd4e5ecf2e49711b/icon-shanchu.png',
|
||||
'icon-shandiao':
|
||||
'https://yx-web-nosdn.netease.im/common/14eea1edc7801449a30700a3c9d604c6/icon-shandiao.png',
|
||||
'icon-shezhi':
|
||||
'https://yx-web-nosdn.netease.im/common/698d483a7aacade68ea976f727184e1e/setting.png',
|
||||
'icon-shezhi1':
|
||||
'https://yx-web-nosdn.netease.im/common/eced271ac35864b7c716262f1a37217e/icon-shezhi1.png',
|
||||
'icon-shipin':
|
||||
'https://yx-web-nosdn.netease.im/common/455c333219318f3b96f748d6753eda4a/icon-shipin.png',
|
||||
'icon-shipin8':
|
||||
'https://yx-web-nosdn.netease.im/common/73fededbe5b97dec0246e438d33ae614/icon-shipin8.png',
|
||||
'icon-shipinyuyin':
|
||||
'https://yx-web-nosdn.netease.im/common/09b16fe9a2b824ded4162f25333e44e1/icon-shipinyuyin.png',
|
||||
'icon-sifenzhisanyidu':
|
||||
'https://yx-web-nosdn.netease.im/common/b1234a4255d3187fa9a767bfed8f4d96/icon-sifenzhisanyidu.png',
|
||||
'icon-sifenzhiyiyidu':
|
||||
'https://yx-web-nosdn.netease.im/common/11c7a7e79c57eda20c5ca9d98b0d7e8a/icon-sifenzhiyiyidu.png',
|
||||
'icon-sousuo':
|
||||
'https://yx-web-nosdn.netease.im/common/2ccfdfa640c72167f228e7d76068e5f5/icon-sousuo.png',
|
||||
'icon-team':
|
||||
'https://yx-web-nosdn.netease.im/common/140e8fbd1cc2df4c878acb17405471f7/icon-team.png',
|
||||
'icon-zuojiantou':
|
||||
'https://yx-web-nosdn.netease.im/common/9ab796030ac24a126dedc60fd60613ce/icon-zuojiantou.png',
|
||||
'icon-zhuanfa':
|
||||
'https://yx-web-nosdn.netease.im/common/163a3343b0262a76d72d00bbbb4f8ac9/icon-zhuanfa.png',
|
||||
'icon-zhongyingwen':
|
||||
'https://yx-web-nosdn.netease.im/common/80a7dbae8c0207f1896e63746ac1a18a/icon-zhongyingwen.png',
|
||||
'icon-zhankai':
|
||||
'https://yx-web-nosdn.netease.im/common/d4f655bff9e278ea732adb6a5317bbca/icon-zhankai.png',
|
||||
'icon-yinle':
|
||||
'https://yx-web-nosdn.netease.im/common/98a2a366c7e06ab1b1b6ddf9b0c01d73/icon-yinle.png',
|
||||
'icon-yidu':
|
||||
'https://yx-web-nosdn.netease.im/common/d17d4aa7866faca55a6a1180c1e15bf6/icon-yidu.png',
|
||||
'icon-yanzheng':
|
||||
'https://yx-web-nosdn.netease.im/common/5a0c2769626284ff646298a7ef1f66c2/icon-yanzheng.png',
|
||||
'icon-xiaoxizhiding':
|
||||
'https://yx-web-nosdn.netease.im/common/cef2e824e603dde3d333d128434f90c1/icon-xiaoxizhiding.png',
|
||||
'icon-xiaoximiandarao':
|
||||
'https://yx-web-nosdn.netease.im/common/1c92731bb3fa91fa3fc5ff45bf9e4dbe/icon-xiaoximiandarao.png',
|
||||
'icon-Word':
|
||||
'https://yx-web-nosdn.netease.im/common/af0e4fa22c4b30a263a7f534b0504c23/icon-Word.png',
|
||||
'icon-wenjian':
|
||||
'https://yx-web-nosdn.netease.im/common/d3b36fc953447f9d9630f3b73aaa6ef1/icon-wenjian.png',
|
||||
'icon-weizhiwenjian':
|
||||
'https://yx-web-nosdn.netease.im/common/d51b39a07b3b482ab3b50a4b068588c6/icon-weizhiwenjian.png',
|
||||
'icon-weidu':
|
||||
'https://yx-web-nosdn.netease.im/common/5d50477d2afa387a59a67e30fcdceabd/icon-weidu.png',
|
||||
'icon-tupian2':
|
||||
'https://yx-web-nosdn.netease.im/common/51eb954ad971eb6890d0934858f950aa/icon-tupian2.png',
|
||||
'icon-tupian1':
|
||||
'https://yx-web-nosdn.netease.im/common/0737f1e187aa250d5090f38925672485/icon-tupian1.png',
|
||||
'icon-tupian':
|
||||
'https://yx-web-nosdn.netease.im/common/aa93aa9ffd0197b9a961455506f75078/icon-tupian.png',
|
||||
'icon-tuigejian':
|
||||
'https://yx-web-nosdn.netease.im/common/7bca7dffd1f8c3cd66b8ede7f176e4a8/icon-tuigejian.png',
|
||||
'icon-tuichudenglu':
|
||||
'https://yx-web-nosdn.netease.im/common/ce42192020620522a46763c758951b76/icon-tuichudenglu.png',
|
||||
'icon-touxiang5':
|
||||
'https://yx-web-nosdn.netease.im/common/769a3ba0615b3157d6b493fa5d2352c4/icon-touxiang5.png',
|
||||
'icon-touxiang4':
|
||||
'https://yx-web-nosdn.netease.im/common/c6ea2b6557913d2fe8017b68eb688515/icon-touxiang4.png',
|
||||
'icon-touxiang3':
|
||||
'https://yx-web-nosdn.netease.im/common/89bbfa21ce6d43fda25a8c4121284db6/icon-touxiang3.png',
|
||||
'icon-touxiang2':
|
||||
'https://yx-web-nosdn.netease.im/common/e2e3fdafb9201a0b693b36514eb378ae/icon-touxiang2.png',
|
||||
'icon-touxiang1':
|
||||
'https://yx-web-nosdn.netease.im/common/4e639e380f246e804b2f0f115f84215f/icon-touxiang1.png',
|
||||
'icon-tongxunlu-xuanzhong':
|
||||
'https://yx-web-nosdn.netease.im/common/f49a558e193325d185223f55b65711bf/icon-tongxunlu-xuanzhong.png',
|
||||
'icon-tongxunlu-weixuanzhong':
|
||||
'https://yx-web-nosdn.netease.im/common/e33c24318c1faeb73c79fa1b0b1c9c53/icon-tongxunlu-weixuanzhong.png',
|
||||
'icon-tianjiahaoyou':
|
||||
'https://yx-web-nosdn.netease.im/common/c5f19ef12df64f466bba0a611cf224d5/icon-tianjiahaoyou.png',
|
||||
'icon-tianjiaanniu':
|
||||
'https://yx-web-nosdn.netease.im/common/181feb34fc6324198f4d9d887a8759e1/icon-tianjiaanniu.png',
|
||||
'icon-team2':
|
||||
'https://yx-web-nosdn.netease.im/common/f9d8ea13b9b5d769f75e7f01edcab1df/icon-team2.png',
|
||||
'icon-lahei2':
|
||||
'https://yx-web-nosdn.netease.im/common/1ee2a3bffb33b81727583189a2562658/icon-lahei2.png',
|
||||
'icon-yuyin1':
|
||||
'https://yx-web-nosdn.netease.im/common/2c785931baec5a9b00cd5f59f81f4482/语音1.png',
|
||||
'icon-yuyin2':
|
||||
'https://yx-web-nosdn.netease.im/common/20043a54056311986c867edf93f51f62/语音2.png',
|
||||
'icon-yuyin3':
|
||||
'https://yx-web-nosdn.netease.im/common/786476a9bd129f6a7b027621f9818883/语音3.png',
|
||||
'icon-yuyin8':
|
||||
'https://yx-web-nosdn.netease.im/common/40d631410c18983f6ee0ca880976c2e9/icon-yuyin8.png',
|
||||
'icon-audio':
|
||||
'https://yx-web-nosdn.netease.im/common/27c4c8b528fac12d3f79bb3154be87d4/audio1.png',
|
||||
'audio-btn':
|
||||
'https://yx-web-nosdn.netease.im/common/abfd3577b9d29bb6d3445979336a5770/Vector.png',
|
||||
'audio-btn-selected':
|
||||
'https://yx-web-nosdn.netease.im/common/7f17c648e9b63f40b91832664668ac7a/Frame.png',
|
||||
'send-more':
|
||||
'https://yx-web-nosdn.netease.im/common/270ebff9ad75056b857c21f40b55d72d/send-more.png',
|
||||
'icon-paishe':
|
||||
'https://yx-web-nosdn.netease.im/common/be9638b843a70f307ecb6803ffe5775c/paishe.png',
|
||||
'icon-shipin2':
|
||||
'https://yx-web-nosdn.netease.im/common/3865bf597f9f5ca03b2b222ca07344e1/icon-shipin2.png',
|
||||
'icon-audio-call':
|
||||
'https://yx-web-nosdn.netease.im/common/99438364d757b51e7e36c18d254e70e7/icon-audio-call.png',
|
||||
'icon-video-call':
|
||||
'https://yx-web-nosdn.netease.im/common/ed7c85a59de3e247d10ecfc684b05226/icon-video-call.png',
|
||||
'icon-read':
|
||||
'https://yx-web-nosdn.netease.im/common/271c53e493cfd6ea98c51650d8b39b79/read.png',
|
||||
'icon-file':
|
||||
'https://yx-web-nosdn.netease.im/common/90485f277f50fc081970ded8772ec7c5/file.png',
|
||||
'icon-pin':
|
||||
'https://yx-web-nosdn.netease.im/common/6eb4fafcca008d2e93e90311696d6b96/black-pin.png',
|
||||
'icon-green-pin':
|
||||
'https://yx-web-nosdn.netease.im/common/4a1f15eff2f53563c4f1cf6ecde82d2c/green-pin.png',
|
||||
'choose-picture':
|
||||
'https://yx-web-nosdn.netease.im/common/97b3ca79a589d5753cbc0e8e8ec09501/choose-picture.png',
|
||||
'icon-collection':
|
||||
'https://yx-web-nosdn.netease.im/common/aa1bad3410009dea83d34d513dcd20f3/add-collection.png',
|
||||
'blue-collection':
|
||||
'https://yx-web-nosdn.netease.im/common/fb3836a8731b57720fcfdd3b589b3d5f/collection.png',
|
||||
}
|
||||
|
||||
//以上链接访问有频率控制,建议您将静态资源放到您服务器上,然后修改上面的链接即可
|
||||
|
||||
const iconUrl = computed(() => {
|
||||
return urlMap[props.type]
|
||||
})
|
||||
|
||||
const className = `${props.iconClassName || ''} icon-wrapper`
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.icon-wrapper {
|
||||
display: inline-block;
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
.icon {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,105 @@
|
||||
<template>
|
||||
<div class="wrapper">
|
||||
<template v-for="item in textArr" :key="item.key">
|
||||
<template v-if="item.type === 'text'" class="msg-reply-text">
|
||||
{{ item.value }}
|
||||
</template>
|
||||
<template class="icon" v-else-if="item.type === 'emoji'">
|
||||
<Icon
|
||||
:type="EMOJI_ICON_MAP_CONFIG[item.value]"
|
||||
:size="14"
|
||||
:iconStyle="{
|
||||
margin: '3px',
|
||||
verticalAlign: 'bottom',
|
||||
display: 'inline-block',
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import Icon from './Icon.vue'
|
||||
import { EMOJI_ICON_MAP_CONFIG, emojiRegExp } from '@/utils/im/emoji'
|
||||
import { computed, withDefaults } from 'vue'
|
||||
|
||||
const props = withDefaults(defineProps<{ text: string }>(), {})
|
||||
|
||||
// 筛选出文本和表情
|
||||
const parseText = (text: string) => {
|
||||
if (!text) return []
|
||||
const regexEmoji = emojiRegExp
|
||||
const matches: {
|
||||
type: 'emoji' | 'text'
|
||||
value: string
|
||||
index: number
|
||||
}[] = []
|
||||
let match
|
||||
|
||||
while ((match = regexEmoji.exec(text)) !== null) {
|
||||
matches.push({
|
||||
type: 'emoji',
|
||||
value: match[0],
|
||||
index: match.index,
|
||||
})
|
||||
const fillText = ' '.repeat(match[0].length)
|
||||
text = text.replace(match[0], fillText)
|
||||
}
|
||||
|
||||
text = text.replace(regexEmoji, ' ')
|
||||
|
||||
if (text) {
|
||||
text
|
||||
.split(' ')
|
||||
.filter((item) => item.trim())
|
||||
.map((item) => {
|
||||
const index = text?.indexOf(item)
|
||||
matches.push({
|
||||
type: 'text',
|
||||
value: item,
|
||||
index,
|
||||
})
|
||||
const fillText = ' '.repeat(item.length)
|
||||
text = text.replace(item, fillText)
|
||||
})
|
||||
}
|
||||
|
||||
return matches
|
||||
.sort((a, b) => a.index - b.index)
|
||||
.map((item, index) => {
|
||||
return {
|
||||
...item,
|
||||
key: index + item.type,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const textArr = computed(() => {
|
||||
return parseText(props.text)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.wrapper {
|
||||
flex: 1;
|
||||
font-size: 13px;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.msg-reply-text {
|
||||
font-size: 13px !important;
|
||||
height: 18px;
|
||||
line-height: 18px;
|
||||
width: 100%;
|
||||
display: inline;
|
||||
}
|
||||
.ellipsis {
|
||||
display: inline-block;
|
||||
flex-basis: 25px;
|
||||
font-size: 13px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,112 @@
|
||||
<template>
|
||||
<div class="modal" v-if="visible">
|
||||
<div class="mask" @tap="handleMaskClick"></div>
|
||||
<div class="content">
|
||||
<div class="title">{{ title }}</div>
|
||||
<slot></slot>
|
||||
<div class="buttons">
|
||||
<div class="button cancel" @tap="handleCancelClick">
|
||||
{{ cancelText }}
|
||||
</div>
|
||||
<div class="button confirm" @tap="handleConfirmClick">
|
||||
{{ confirmText }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
title: string
|
||||
confirmText: string
|
||||
cancelText: string
|
||||
visible: boolean
|
||||
}>(),
|
||||
{}
|
||||
)
|
||||
|
||||
const emit = defineEmits(['confirm', 'cancel'])
|
||||
|
||||
const handleMaskClick = () => {
|
||||
emit('cancel')
|
||||
}
|
||||
|
||||
const handleConfirmClick = () => {
|
||||
emit('confirm')
|
||||
}
|
||||
|
||||
const handleCancelClick = () => {
|
||||
emit('cancel')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.modal {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 8px;
|
||||
z-index: 99999999999;
|
||||
}
|
||||
|
||||
.mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
touch-action: none;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.content {
|
||||
width: 276px;
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
z-index: 99999;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
text-align: left;
|
||||
margin: 16px 0 0 16px;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 52px;
|
||||
border-top: 1px solid #e1e6e8;
|
||||
position: relative;
|
||||
bottom: 0;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.cancel {
|
||||
color: #666666;
|
||||
height: 52px;
|
||||
line-height: 52px;
|
||||
border-right: 1px solid #e1e6e8;
|
||||
}
|
||||
|
||||
.confirm {
|
||||
color: #337eff;
|
||||
}
|
||||
|
||||
.button {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,94 @@
|
||||
<template>
|
||||
<!-- domId 是要截图的容器 id -->
|
||||
<view class="my-html2canvas" :prop="domId" :change:prop="h2cRender.watchDomId">
|
||||
<!-- 用于触发 renderjs 截图 -->
|
||||
<text :prop="expOver" :change:prop="h2cRender.watchExpOver"></text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
domId: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
expOver: 0
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 业务层调用这个方法开始截图
|
||||
h2cRenderDom() {
|
||||
// #ifdef H5
|
||||
// H5 直接在当前环境(有 DOM)里跑 renderDom
|
||||
this.$emit('renderDomInH5')
|
||||
// #endif
|
||||
|
||||
// #ifndef H5
|
||||
// 非 H5(APP 等)通过 expOver 触发 renderjs 中的截图逻辑
|
||||
this.expOver++
|
||||
// #endif
|
||||
},
|
||||
// renderjs 截图完成后会 callMethod 回调到这里
|
||||
renderOver(base64) {
|
||||
this.$emit('renderOver', base64)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- renderjs 只能内联 -->
|
||||
<script module="h2cRender" lang="renderjs">
|
||||
import html2canvas from 'html2canvas'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
domIdValue: ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async renderDom() {
|
||||
try {
|
||||
const el = document.getElementById(this.domIdValue)
|
||||
if (!el) {
|
||||
console.error('[MyHtml2canvas] dom 未找到,请检查 domId:', this.domIdValue)
|
||||
return
|
||||
}
|
||||
const canvas = await html2canvas(el, {
|
||||
width: el.offsetWidth,
|
||||
height: el.offsetHeight,
|
||||
x: 0,
|
||||
y: 0,
|
||||
useCORS: true,
|
||||
scale: 2
|
||||
})
|
||||
const base64 = canvas.toDataURL('image/png', 1)
|
||||
this.$ownerInstance.callMethod('renderOver', base64)
|
||||
} catch (err) {
|
||||
console.error('[MyHtml2canvas] html2canvas 失败:', err && err.message)
|
||||
}
|
||||
},
|
||||
// script 部分通过 :prop 传入 domId
|
||||
watchDomId(newVal) {
|
||||
this.domIdValue = newVal
|
||||
},
|
||||
// expOver 变化时触发渲染
|
||||
watchExpOver(newVal) {
|
||||
if (newVal !== 0) {
|
||||
this.renderDom()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.my-html2canvas {
|
||||
position: relative;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
<template>
|
||||
<!-- 样式兼容微信小程序 -->
|
||||
<div>
|
||||
<div
|
||||
class="nav-bar-wrapper"
|
||||
:style="{
|
||||
backgroundColor: backgroundColor || '#ffffff',
|
||||
backgroundImage: `url(${title})`,
|
||||
height: isWeixinApp ? '55px' : '40px',
|
||||
alignItems: isWeixinApp ? 'flex-end' : 'center',
|
||||
}"
|
||||
>
|
||||
<slot v-if="showLeft" name="left"></slot>
|
||||
<div v-else @tap="back">
|
||||
<Icon type="icon-zuojiantou" :size="22"></Icon>
|
||||
</div>
|
||||
<div class="title-container">
|
||||
<div class="title">{{ title }}</div>
|
||||
<div class="subTitle" v-if="subTitle">{{ subTitle }}</div>
|
||||
<slot name="icon"></slot>
|
||||
</div>
|
||||
<div>
|
||||
<slot name="right"></slot>
|
||||
</div>
|
||||
</div>
|
||||
<div :class="isWeixinApp ? 'block-wx' : 'block'"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { getUniPlatform } from '../utils'
|
||||
import Icon from './Icon.vue'
|
||||
import { withDefaults } from 'vue'
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
title: string
|
||||
subTitle?: string
|
||||
backgroundColor?: string
|
||||
showLeft?: boolean
|
||||
}>(),
|
||||
{
|
||||
subTitle: '',
|
||||
backgroundColor: '',
|
||||
}
|
||||
)
|
||||
|
||||
const isWeixinApp = getUniPlatform() === 'mp-weixin'
|
||||
|
||||
const back = () => {
|
||||
uni.navigateBack({
|
||||
delta: 1,
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../pages/styles/common.scss';
|
||||
|
||||
.nav-bar-wrapper {
|
||||
position: fixed;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: var(--status-bar-height) 10px 5px 10px;
|
||||
z-index: 9999;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
|
||||
.title-container {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 230px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.title {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.subTitle {
|
||||
white-space: nowrap;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.block {
|
||||
width: 100%;
|
||||
height: calc(45px + var(--status-bar-height));
|
||||
}
|
||||
|
||||
.block-wx {
|
||||
width: 100%;
|
||||
height: calc(55px + var(--status-bar-height));
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,73 @@
|
||||
<template>
|
||||
<div v-if="!isConnected && text" class="network-alert">
|
||||
{{ text }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { autorun } from 'mobx'
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
import { t } from '@/utils/im/i18n'
|
||||
import { V2NIMConst } from '@/utils/im/nim'
|
||||
|
||||
const isConnected = ref(true)
|
||||
const text = ref(t('connectingText'))
|
||||
// uni.onNetworkStatusChange((res) => {
|
||||
// if (!res.isConnected) {
|
||||
// isConnected.value = false;
|
||||
// text.value = t('offlineText');
|
||||
// } else {
|
||||
// text.value = t('connectingText');
|
||||
// }
|
||||
// });
|
||||
|
||||
onMounted(() => {
|
||||
if (
|
||||
uni.$UIKitStore?.connectStore?.connectStatus ===
|
||||
V2NIMConst.V2NIMConnectStatus.V2NIM_CONNECT_STATUS_CONNECTED
|
||||
) {
|
||||
isConnected.value = true
|
||||
} else if (
|
||||
uni.$UIKitStore?.connectStore?.connectStatus ===
|
||||
V2NIMConst.V2NIMConnectStatus.V2NIM_CONNECT_STATUS_DISCONNECTED
|
||||
) {
|
||||
isConnected.value = false
|
||||
text.value = t('offlineText')
|
||||
} else {
|
||||
isConnected.value = false
|
||||
text.value = t('connectingText')
|
||||
}
|
||||
})
|
||||
|
||||
const uninstallConnectWatch = autorun(() => {
|
||||
if (
|
||||
uni.$UIKitStore?.connectStore?.connectStatus ===
|
||||
V2NIMConst.V2NIMConnectStatus.V2NIM_CONNECT_STATUS_CONNECTED
|
||||
) {
|
||||
isConnected.value = true
|
||||
} else if (
|
||||
uni.$UIKitStore?.connectStore?.connectStatus ===
|
||||
V2NIMConst.V2NIMConnectStatus.V2NIM_CONNECT_STATUS_DISCONNECTED
|
||||
) {
|
||||
isConnected.value = false
|
||||
text.value = t('offlineText')
|
||||
} else {
|
||||
isConnected.value = false
|
||||
text.value = t('connectingText')
|
||||
}
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
uninstallConnectWatch()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.network-alert {
|
||||
font-size: 14px;
|
||||
background: #fee3e6;
|
||||
color: #fc596a;
|
||||
text-align: center;
|
||||
padding: 8px 0;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,164 @@
|
||||
<template>
|
||||
<div v-if="personList.length > 0" class="friend-select-wrapper">
|
||||
<div class="member-wrapper">
|
||||
<radio-group v-if="radio" @change="checkboxChange">
|
||||
<div
|
||||
class="member-item"
|
||||
v-for="item in personList"
|
||||
:key="item.accountId"
|
||||
>
|
||||
<radio
|
||||
class="checkbox"
|
||||
:value="item.accountId"
|
||||
:checked="item.checked"
|
||||
:disabled="
|
||||
item.disabled ||
|
||||
(selectAccount.length >= max &&
|
||||
!selectAccount.includes(item.accountId))
|
||||
"
|
||||
/>
|
||||
<Avatar class="user-avatar" size="36" :account="item.accountId" />
|
||||
<div class="user-name">
|
||||
<Appellation :account="item.accountId" :teamId="item.teamId" />
|
||||
</div>
|
||||
</div>
|
||||
</radio-group>
|
||||
<checkbox-group v-else @change="checkboxChange">
|
||||
<div
|
||||
class="member-item"
|
||||
v-for="item in personList"
|
||||
:key="item.accountId"
|
||||
>
|
||||
<checkbox
|
||||
class="checkbox"
|
||||
:value="item.accountId"
|
||||
:checked="item.checked"
|
||||
:disabled="
|
||||
item.disabled ||
|
||||
(selectAccount.length >= max &&
|
||||
!selectAccount.includes(item.accountId))
|
||||
"
|
||||
/>
|
||||
<Avatar class="user-avatar" size="36" :account="item.accountId" />
|
||||
<div class="user-name">
|
||||
<Appellation :account="item.accountId" :teamId="item.teamId" />
|
||||
</div>
|
||||
</div>
|
||||
</checkbox-group>
|
||||
</div>
|
||||
<div
|
||||
:style="{ border: '1px solid #ccc' }"
|
||||
v-if="!!showBtn"
|
||||
@tap="onBtnClick"
|
||||
class="ok-btn"
|
||||
>
|
||||
{{ btnText || t('okText') }}
|
||||
</div>
|
||||
</div>
|
||||
<Empty v-else :text="t('noFriendText')"></Empty>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import Avatar from './Avatar.vue'
|
||||
import Appellation from './Appellation.vue'
|
||||
import Empty from './Empty.vue'
|
||||
import { t } from '../utils/i18n'
|
||||
import { events } from '../utils/constants'
|
||||
import { ref, onMounted } from 'vue'
|
||||
|
||||
export type PersonSelectItem = {
|
||||
accountId: string
|
||||
teamId?: string
|
||||
disabled?: boolean
|
||||
checked?: boolean
|
||||
}
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
personList: PersonSelectItem[]
|
||||
showBtn?: boolean
|
||||
btnText?: string
|
||||
radio?: boolean
|
||||
max?: number
|
||||
}>(),
|
||||
{
|
||||
showBtn: true,
|
||||
btnText: '',
|
||||
radio: false,
|
||||
max: Number.MAX_SAFE_INTEGER,
|
||||
}
|
||||
)
|
||||
|
||||
const $emit = defineEmits<{
|
||||
(event: 'checkboxChange', selectList: string | string[]): void
|
||||
(event: 'onBtnClick'): void
|
||||
}>()
|
||||
|
||||
const onBtnClick = () => {
|
||||
uni.$emit(events.FRIEND_SELECT)
|
||||
$emit('onBtnClick')
|
||||
}
|
||||
|
||||
const checkboxChange = (event: any) => {
|
||||
const value = event.detail.value
|
||||
selectAccount.value = value
|
||||
$emit('checkboxChange', value)
|
||||
}
|
||||
|
||||
const selectAccount = ref<string[]>([])
|
||||
|
||||
onMounted(() => {
|
||||
selectAccount.value = props.personList
|
||||
.filter((item) => item.checked)
|
||||
.map((item) => item.accountId)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../pages/styles/common.scss';
|
||||
|
||||
.friend-select-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.member-wrapper {
|
||||
padding-top: 10px;
|
||||
display: flex;
|
||||
max-height: 80vh;
|
||||
overflow-y: auto;
|
||||
|
||||
.member-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 8px 20px;
|
||||
// width: 100vw;
|
||||
|
||||
.user-avatar {
|
||||
margin: 0 14px;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
max-width: 70%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: #333;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.ok-btn {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.ok-btn-mp {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,353 @@
|
||||
<template>
|
||||
<view
|
||||
class="zb-tooltip"
|
||||
:style="{
|
||||
'--theme-bg-color': color,
|
||||
}"
|
||||
>
|
||||
<view class="zb_tooltip_content" @longpress.stop="handleClick">
|
||||
<slot></slot>
|
||||
<view
|
||||
class="zb_tooltip__mask"
|
||||
@longpress.stop
|
||||
v-show="isShow"
|
||||
@touchstart="close"
|
||||
></view>
|
||||
<view
|
||||
class="zb_tooltip__popper"
|
||||
@tap.stop="() => {}"
|
||||
:style="[
|
||||
style,
|
||||
{
|
||||
visibility: isShow ? 'visible' : 'hidden',
|
||||
color: color === 'white' ? '' : '#fff',
|
||||
boxShadow:
|
||||
color === 'white'
|
||||
? '0 3px 6px -4px #0000001f, 0 6px 16px #00000014, 0 9px 28px 8px #0000000d'
|
||||
: '',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<slot name="content">{{ content }}</slot>
|
||||
<!-- <view
|
||||
class="zb_popper__icon"
|
||||
:style="[arrowStyle]"
|
||||
:class="[
|
||||
{
|
||||
zb_popper__up: placement.indexOf('bottom') === 0,
|
||||
zb_popper__arrow: placement.indexOf('top') === 0,
|
||||
zb_popper__right: placement.indexOf('right') === 0,
|
||||
zb_popper__left: placement.indexOf('left') === 0,
|
||||
},
|
||||
]"
|
||||
>
|
||||
</view> -->
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Tooltip',
|
||||
props: {
|
||||
visible: Boolean,
|
||||
align: Boolean,
|
||||
color: {
|
||||
type: String,
|
||||
default: '#303133',
|
||||
},
|
||||
// placement: {
|
||||
// type: String,
|
||||
// default: 'top',
|
||||
// },
|
||||
content: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
isShow: this.visible,
|
||||
title: 'Hello',
|
||||
arrowLeft: 0,
|
||||
query: null,
|
||||
style: {},
|
||||
arrowStyle: {},
|
||||
placement: 'top',
|
||||
}
|
||||
},
|
||||
onLoad() {},
|
||||
watch: {
|
||||
isShow: {
|
||||
handler(val) {
|
||||
this.$emit('update:visible', val)
|
||||
},
|
||||
immediate: true,
|
||||
},
|
||||
visible: {
|
||||
handler(val) {
|
||||
if (val) {
|
||||
this.$nextTick(() => {
|
||||
this.getPosition()
|
||||
})
|
||||
}
|
||||
this.isShow = val
|
||||
},
|
||||
immediate: true,
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
// #ifdef H5
|
||||
window.addEventListener('click', () => {
|
||||
this.isShow = false
|
||||
})
|
||||
// #endif
|
||||
this.getPosition()
|
||||
},
|
||||
methods: {
|
||||
close() {
|
||||
this.isShow = false
|
||||
},
|
||||
fixedWrap() {
|
||||
this.isShow = false
|
||||
},
|
||||
async handleClick() {
|
||||
if (this.isShow) {
|
||||
return (this.isShow = false)
|
||||
}
|
||||
await this.getPosition()
|
||||
this.isShow = true
|
||||
},
|
||||
getPosition() {
|
||||
return new Promise((resolve) => {
|
||||
uni
|
||||
.createSelectorQuery()
|
||||
.in(this)
|
||||
.selectAll('.zb_tooltip_content,.zb_tooltip__popper')
|
||||
.boundingClientRect(async (data) => {
|
||||
let { left, bottom, right, top, width, height } = data[0] || {}
|
||||
let obj1 = data[1] || {}
|
||||
let objStyle = {}
|
||||
let objStyle1 = {}
|
||||
if (top <= 300) {
|
||||
this.placement = 'bottom'
|
||||
} else {
|
||||
this.placement = 'top'
|
||||
}
|
||||
switch (this.placement) {
|
||||
case 'top':
|
||||
// if (obj1.width > width) {
|
||||
// objStyle.left = `-${(obj1.width - width + 120) / 2}px`
|
||||
// } else {
|
||||
// objStyle.left = `${Math.abs(obj1.width - width) / 2}px`
|
||||
// }
|
||||
if (this.align) {
|
||||
objStyle.left = '-100px'
|
||||
if (width < 90) {
|
||||
objStyle.left = '-200px'
|
||||
}
|
||||
} else {
|
||||
objStyle.left = '50px'
|
||||
}
|
||||
objStyle.bottom = `${height + 8}px`
|
||||
// objStyle.left = '30%'
|
||||
// objStyle1.left = obj1.width - 6 + 'px'
|
||||
break
|
||||
case 'bottom':
|
||||
// if (obj1.width > width) {
|
||||
// objStyle.left = `-${(obj1.width - width) / 2}px`
|
||||
// } else {
|
||||
// objStyle.left = `${Math.abs(obj1.width - width) / 2}px`
|
||||
// }
|
||||
if (this.align) {
|
||||
objStyle.left = '-100px'
|
||||
if (width < 100) {
|
||||
objStyle.left = '-200px'
|
||||
}
|
||||
} else {
|
||||
objStyle.left = '50px'
|
||||
}
|
||||
// objStyle.left = `-${obj1.width - width - 100}px`
|
||||
objStyle.top = `${height + 8}px`
|
||||
// objStyle1.left = obj1.width / 2 - 6 + 'px'
|
||||
break
|
||||
// case 'top-start':
|
||||
// objStyle.left = `0px`
|
||||
// objStyle.bottom = `${height + 8}px`
|
||||
// break;
|
||||
// case 'top-end':
|
||||
// objStyle.right = `0px`
|
||||
// objStyle.bottom = `${height + 8}px`
|
||||
// objStyle1.right = `8px`
|
||||
// break;
|
||||
// case 'bottom-start':
|
||||
// objStyle.left = `0px`
|
||||
// objStyle.top = `${height + 8}px`
|
||||
// objStyle1.left = `8px`
|
||||
// break;
|
||||
|
||||
// case 'bottom-end':
|
||||
// objStyle.right = `0px`
|
||||
// objStyle.top = `${height + 8}px`
|
||||
// objStyle1.right = `8px`
|
||||
// break;
|
||||
|
||||
// case 'right':
|
||||
// objStyle.left = `${width + 8}px`
|
||||
// if (obj1.height > height) {
|
||||
// objStyle.top = `-${(obj1.height - height) / 2}px`
|
||||
// } else {
|
||||
// objStyle.top = `${Math.abs((obj1.height - height) / 2)}px`
|
||||
// }
|
||||
|
||||
// objStyle1.top = `${obj1.height / 2 - 6}px`
|
||||
// break;
|
||||
// case 'right-start':
|
||||
// objStyle.left = `${width + 8}px`
|
||||
// objStyle.top = `0px`
|
||||
// objStyle1.top = `8px`
|
||||
// break;
|
||||
|
||||
// case 'right-end':
|
||||
// objStyle.left = `${width + 8}px`
|
||||
// objStyle.bottom = `0px`
|
||||
// objStyle1.bottom = `8px`
|
||||
// break;
|
||||
|
||||
// case 'left':
|
||||
// objStyle.right = `${width + 8}px`
|
||||
|
||||
// if (obj1.height > height) {
|
||||
// objStyle.top = `-${(obj1.height - height) / 2}px`
|
||||
// } else {
|
||||
// objStyle.top = `${Math.abs((obj1.height - height) / 2)}px`
|
||||
// }
|
||||
|
||||
// objStyle1.top = `${obj1.height / 2 - 6}px`
|
||||
// break;
|
||||
|
||||
// case 'left-start':
|
||||
// objStyle.right = `${width + 8}px`
|
||||
// objStyle.top = `0px`
|
||||
// objStyle1.top = `8px`
|
||||
// break;
|
||||
|
||||
// case 'left-end':
|
||||
// objStyle.right = `${width + 8}px`
|
||||
// objStyle.bottom = `0px`
|
||||
// objStyle1.bottom = `8px`
|
||||
// break;
|
||||
}
|
||||
this.style = objStyle
|
||||
// 三角形箭头
|
||||
this.arrowStyle = objStyle1
|
||||
resolve()
|
||||
})
|
||||
.exec()
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
$theme-bg-color: var(--theme-bg-color);
|
||||
|
||||
.zb-tooltip {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.zb_tooltip_content {
|
||||
height: 100%;
|
||||
/* float: left; */
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
|
||||
// display: flex;
|
||||
// flex-direction: row;
|
||||
// align-items: center;
|
||||
/* overflow: hidden; */
|
||||
}
|
||||
|
||||
.zb_tooltip__popper {
|
||||
/* transform-origin: center top; */
|
||||
background: $theme-bg-color;
|
||||
|
||||
visibility: hidden;
|
||||
// color:'#fff';
|
||||
position: absolute;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
padding: 10px;
|
||||
min-width: 10px;
|
||||
word-wrap: break-word;
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
z-index: 99;
|
||||
}
|
||||
|
||||
.zb_tooltip__mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
background-color: rgba(256, 256, 256, 0);
|
||||
z-index: 8;
|
||||
}
|
||||
|
||||
.zb_popper__icon {
|
||||
width: 0;
|
||||
height: 0;
|
||||
z-index: 9;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.zb_popper__arrow {
|
||||
bottom: -5px;
|
||||
/* transform-origin: center top; */
|
||||
border-left: 6px solid transparent;
|
||||
border-right: 6px solid transparent;
|
||||
border-top: 6px solid $theme-bg-color;
|
||||
}
|
||||
|
||||
.zb_popper__right {
|
||||
border-top: 6px solid transparent;
|
||||
border-bottom: 6px solid transparent;
|
||||
border-right: 6px solid $theme-bg-color;
|
||||
left: -5px;
|
||||
}
|
||||
|
||||
.zb_popper__left {
|
||||
border-top: 6px solid transparent;
|
||||
border-bottom: 6px solid transparent;
|
||||
border-left: 6px solid $theme-bg-color;
|
||||
right: -5px;
|
||||
}
|
||||
|
||||
.zb_popper__up {
|
||||
border-left: 6px solid transparent;
|
||||
border-right: 6px solid transparent;
|
||||
border-bottom: 6px solid $theme-bg-color;
|
||||
top: -5px;
|
||||
}
|
||||
|
||||
.fixed {
|
||||
position: absolute;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
pointer-events: auto;
|
||||
background: red;
|
||||
z-index: -1;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,117 @@
|
||||
<template>
|
||||
<div class="user-wrapper">
|
||||
<div class="avatar-wrapper">
|
||||
<Avatar v-if="props.account" size="70" :account="props.account"></Avatar>
|
||||
</div>
|
||||
<div class="account-wrapper">
|
||||
<div v-if="alias">
|
||||
<div class="main">{{ alias }}</div>
|
||||
<div class="deputy">{{ t('name') }}:{{ nick || account }}</div>
|
||||
</div>
|
||||
<div v-else class="main">{{ nick || account }}</div>
|
||||
<div class="deputy">
|
||||
{{ t('accountText') }}:{{ props.account }}
|
||||
<div @tap.stop="copyAccount">
|
||||
<Icon
|
||||
class="copy-icon"
|
||||
type="icon-fuzhi1"
|
||||
color="#A6ADB6"
|
||||
:size="20"
|
||||
></Icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import Avatar from './Avatar.vue'
|
||||
import Icon from './Icon.vue'
|
||||
import { onUnmounted, ref } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { autorun } from 'mobx'
|
||||
import { t } from '../utils/i18n'
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
account?: string
|
||||
nick?: string
|
||||
}>(),
|
||||
{
|
||||
account: '',
|
||||
nick: '',
|
||||
}
|
||||
)
|
||||
const alias = ref<string>()
|
||||
let uninstallFriendsWatch = () => {}
|
||||
|
||||
onLoad((props) => {
|
||||
let account = props ? props.account : ''
|
||||
uninstallFriendsWatch = autorun(() => {
|
||||
const friend = { ...uni.$UIKitStore.friendStore.friends.get(account) }
|
||||
alias.value = friend ? friend.alias : ''
|
||||
})
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
uninstallFriendsWatch()
|
||||
})
|
||||
|
||||
const copyAccount = () => {
|
||||
uni.setClipboardData({
|
||||
data: props.account,
|
||||
showToast: false,
|
||||
success: () => {
|
||||
uni.showToast({
|
||||
title: t('copySuccessText'),
|
||||
icon: 'none',
|
||||
})
|
||||
},
|
||||
fail: () => {
|
||||
uni.showToast({
|
||||
title: t('copyFailText'),
|
||||
icon: 'none',
|
||||
})
|
||||
},
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../pages/styles/common.scss';
|
||||
|
||||
.user-wrapper {
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
height: 140px;
|
||||
align-items: center;
|
||||
|
||||
.avatar-wrapper {
|
||||
margin: 0 15px;
|
||||
flex: 0 0 70px;
|
||||
}
|
||||
|
||||
.account-wrapper {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
padding-right: 40px;
|
||||
|
||||
.main {
|
||||
font-size: 20px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.deputy {
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.copy-icon {
|
||||
margin-left: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<view>
|
||||
<!-- 当父组件传递的prop属性数据发生变化的时候 ,生成海报图片-->
|
||||
<view :prop="domId" :change:prop="html2canvas.emitData" class="html2canvas">
|
||||
<slot></slot>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { base64ToPath } from '@/utils/image-tools.js';
|
||||
export default {
|
||||
props: {
|
||||
domId: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
mounted:function(){
|
||||
console.log("组件加载完成");
|
||||
},
|
||||
|
||||
methods: {
|
||||
// 接收renderjs发回的数据
|
||||
async receiveRenderData(imgData) {
|
||||
let posterHeight=imgData.height;
|
||||
let posterWidth=imgData.width;
|
||||
let imgPath =await base64ToPath(imgData.imgVal, '.jpeg');
|
||||
this.$emit('renderFinish', {"posterPath":imgPath,"posterHeight":posterHeight,"posterWidth":posterWidth});
|
||||
uni.hideLoading();
|
||||
},
|
||||
showLoading() {
|
||||
uni.showLoading({
|
||||
title: "正在生成导出",
|
||||
icon: "none",
|
||||
mask: true,
|
||||
duration: 3000
|
||||
})
|
||||
},
|
||||
hideLoading() {
|
||||
uni.hideLoading();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<script module="html2canvas" lang="renderjs">
|
||||
import html2canvas from 'html2canvas';
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
mounted:function(){
|
||||
console.log("视图层渲染完成");
|
||||
},
|
||||
methods: {
|
||||
// 发送数据到逻辑层,下面这里最好是加一个settimeout延迟,因为主界面数据还没加载渲染完成
|
||||
emitData(domId) {
|
||||
this.$ownerInstance.callMethod('showLoading', true);
|
||||
const dom = document.getElementById('poster');
|
||||
console.log("dom",dom);
|
||||
let width= dom.offsetWidth;
|
||||
let height=dom.offsetHeight;
|
||||
let windowHeight= uni.getSystemInfoSync().windowHeight;
|
||||
let windowWidth=uni.getSystemInfoSync().windowWidth;
|
||||
html2canvas(dom, {
|
||||
width:width,
|
||||
height:height,
|
||||
//scale:2.5, // 缩放倍数
|
||||
scrollY: 0, // html2canvas默认绘制视图内的页面,需要把scrollY,scrollX设置为0
|
||||
scrollX: 0,
|
||||
x:0,
|
||||
y:0,
|
||||
windowWidth:windowWidth,
|
||||
windowHeight:windowHeight,
|
||||
useCORS: true, //支持跨域,但好像没什么用
|
||||
}).then((canvas) => {
|
||||
let imgVal= canvas.toDataURL('image/png');
|
||||
this.$ownerInstance.callMethod('receiveRenderData',{imgVal:imgVal,height:height,width:width});
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<view>
|
||||
<!-- 当父组件传递的prop属性数据发生变化的时候 ,生成海报图片-->
|
||||
<view :prop="domId" :change:prop="html2canvas.emitData" class="html2canvas">
|
||||
<slot></slot>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { base64ToPath } from '@/utils/image-tools.js';
|
||||
export default {
|
||||
props: {
|
||||
domId: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
mounted:function(){
|
||||
console.log("组件加载完成");
|
||||
},
|
||||
|
||||
methods: {
|
||||
// 接收renderjs发回的数据
|
||||
async receiveRenderData(imgData) {
|
||||
let posterHeight=imgData.height;
|
||||
let posterWidth=imgData.width;
|
||||
let imgPath =await base64ToPath(imgData.imgVal, '.jpeg');
|
||||
this.$emit('renderFinish', {"posterPath":imgPath,"posterHeight":posterHeight,"posterWidth":posterWidth});
|
||||
uni.hideLoading();
|
||||
},
|
||||
showLoading() {
|
||||
uni.showLoading({
|
||||
title: "正在生成导出",
|
||||
icon: "none",
|
||||
mask: true,
|
||||
duration: 3000
|
||||
})
|
||||
},
|
||||
hideLoading() {
|
||||
uni.hideLoading();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<script module="html2canvas" lang="renderjs">
|
||||
import html2canvas from 'html2canvas';
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
mounted:function(){
|
||||
console.log("视图层渲染完成");
|
||||
},
|
||||
methods: {
|
||||
// 发送数据到逻辑层,下面这里最好是加一个settimeout延迟,因为主界面数据还没加载渲染完成
|
||||
emitData(domId) {
|
||||
this.$ownerInstance.callMethod('showLoading', true);
|
||||
const dom = document.getElementById('poster');
|
||||
console.log("dom",dom);
|
||||
let width= dom.offsetWidth;
|
||||
let height=dom.offsetHeight;
|
||||
let windowHeight= uni.getSystemInfoSync().windowHeight;
|
||||
let windowWidth=uni.getSystemInfoSync().windowWidth;
|
||||
html2canvas(dom, {
|
||||
width:width,
|
||||
height:height,
|
||||
//scale:2.5, // 缩放倍数
|
||||
scrollY: 0, // html2canvas默认绘制视图内的页面,需要把scrollY,scrollX设置为0
|
||||
scrollX: 0,
|
||||
x:0,
|
||||
y:0,
|
||||
windowWidth:windowWidth,
|
||||
windowHeight:windowHeight,
|
||||
useCORS: true, //支持跨域,但好像没什么用
|
||||
}).then((canvas) => {
|
||||
let imgVal= canvas.toDataURL('image/png');
|
||||
this.$ownerInstance.callMethod('receiveRenderData',{imgVal:imgVal,height:height,width:width});
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
</style>
|
||||
@@ -3,7 +3,7 @@
|
||||
<view class="dialog-container" @click.stop>
|
||||
<!-- 弹窗标题 -->
|
||||
<view class="dialog-header">
|
||||
<text class="dialog-title">{{ title }}</text>
|
||||
<text class="dialog-title" :style="{ color: titleColor }">{{ title }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 弹窗内容 -->
|
||||
@@ -42,13 +42,13 @@
|
||||
|
||||
@click="oncancel"
|
||||
>
|
||||
<text class="btn-text">取消</text>
|
||||
<text class="btn-text">{{ cancelText }}</text>
|
||||
</view>
|
||||
<view
|
||||
class="dialog-btn confirm"
|
||||
@click="onconfirm"
|
||||
>
|
||||
<text class="btn-text">确定</text>
|
||||
<text class="btn-text">{{ confirmText }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -63,6 +63,10 @@
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
titleColor: {
|
||||
type: String,
|
||||
default: '#8B2316'
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: '温馨提示'
|
||||
@@ -130,6 +134,8 @@
|
||||
}
|
||||
|
||||
.dialog-container {
|
||||
position: relative;
|
||||
z-index:100;
|
||||
background-color: #ffffff;
|
||||
border-radius: 20rpx;
|
||||
width: 92%;
|
||||
|
||||
@@ -1,23 +1,85 @@
|
||||
<template>
|
||||
<view class="emptybox">
|
||||
<up-image :src="emptyImg" width="176rpx" height="204rpx" ></up-image>
|
||||
<text class="empty-text">暂无数据</text>
|
||||
<view class="empty-image-wrap" :style="imageWrapStyle">
|
||||
<up-image
|
||||
:src="emptyImage || emptyImg"
|
||||
:width="renderWidth"
|
||||
:height="renderHeight"
|
||||
:mode="imgMode"
|
||||
></up-image>
|
||||
</view>
|
||||
<text class="empty-text">{{ emptyDesc }}</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { onShow } from "@dcloudio/uni-app";
|
||||
import { computed } from 'vue';
|
||||
import emptyImg from "@/static/icon_empty.png"
|
||||
const props = defineProps({
|
||||
emptyImage: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
imgWidth: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
},
|
||||
imgHeight: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
},
|
||||
emptyDesc: {
|
||||
type: String,
|
||||
default: '暂无数据'
|
||||
}
|
||||
})
|
||||
|
||||
const normalizeSize = (size) => {
|
||||
if (size === '' || size === null || size === undefined) return '';
|
||||
// 直传字符串(如 50%、206rpx、120px),数字交给 up-image 按组件规则处理
|
||||
return typeof size === 'string' ? size.trim() : size;
|
||||
};
|
||||
const isPercentSize = (size) => typeof size === 'string' && size.endsWith('%');
|
||||
|
||||
const imgWidth = computed(() => {
|
||||
const width = normalizeSize(props.imgWidth);
|
||||
const height = normalizeSize(props.imgHeight);
|
||||
if (width !== '') return width;
|
||||
if (height === '') return '206rpx';
|
||||
return '';
|
||||
});
|
||||
const imgHeight = computed(() => normalizeSize(props.imgHeight));
|
||||
const renderWidth = computed(() => (isPercentSize(imgWidth.value) ? '100%' : imgWidth.value));
|
||||
const renderHeight = computed(() => (isPercentSize(imgHeight.value) ? '100%' : imgHeight.value));
|
||||
const imageWrapStyle = computed(() => {
|
||||
const style = {};
|
||||
if (isPercentSize(imgWidth.value)) style.width = imgWidth.value;
|
||||
if (isPercentSize(imgHeight.value)) style.height = imgHeight.value;
|
||||
return style;
|
||||
});
|
||||
const imgMode = computed(() => {
|
||||
// 只传宽/高时按比例缩放,避免图片被拉伸
|
||||
if (imgWidth.value && !imgHeight.value) return 'widthFix';
|
||||
if (!imgWidth.value && imgHeight.value) return 'heightFix';
|
||||
return 'aspectFit';
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.emptybox{
|
||||
width:100%;
|
||||
height:800rpx;
|
||||
min-width:600rpx;
|
||||
display: flex;
|
||||
margin:0 auto;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
.empty-image-wrap{
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.empty-text{
|
||||
font-size: 30rpx;
|
||||
color:#999;
|
||||
|
||||
@@ -1,21 +1,30 @@
|
||||
<template>
|
||||
<!-- 顶部导航栏 -->
|
||||
<uni-nav-bar
|
||||
left-icon="left"
|
||||
<view class="navbox">
|
||||
<view class="status_bar"></view>
|
||||
<uni-nav-bar
|
||||
:left-icon="showLeft?'left':''"
|
||||
:title="title"
|
||||
@clickLeft="goBack"
|
||||
fixed
|
||||
color="#8B2316"
|
||||
height="140rpx"
|
||||
:border="false"
|
||||
backgroundColor="#eeeeee"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { onShow } from "@dcloudio/uni-app";
|
||||
cosnt props=defineProps({
|
||||
const props=defineProps({
|
||||
showLeft:{
|
||||
type:Boolean,
|
||||
default:true
|
||||
},
|
||||
noticeBack:{
|
||||
type:Boolean,
|
||||
default:false
|
||||
},
|
||||
title:{
|
||||
type:String,
|
||||
default:''
|
||||
@@ -29,10 +38,21 @@
|
||||
url: '/pages/index/index'
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.navbox{
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height:(var(--status-bar-height)+44px);
|
||||
z-index: 9999;
|
||||
}
|
||||
.status_bar{
|
||||
height: var(--status-bar-height);
|
||||
background-color: #eeeeee;
|
||||
}
|
||||
</style>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* @file 拖动进度提示组件
|
||||
*/
|
||||
<template>
|
||||
<view class="plv-player-drag-tips">
|
||||
<view class="plv-player-drag-tips__box">
|
||||
<image class="plv-player-drag-tips__direction-img" :src="imgSrc"
|
||||
@error="imageError">
|
||||
</image>
|
||||
<text class="plv-player-drag-tips__time">
|
||||
{{changeTimeFormat}} / {{durationFormat}}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import utils from '@/common/util.js';
|
||||
import backImg from '@/static/skin/back.png';
|
||||
import forwardImg from '@/static/skin/forward.png'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
currentTime: {
|
||||
type: Number
|
||||
},
|
||||
|
||||
duration: {
|
||||
type: Number
|
||||
},
|
||||
|
||||
changeTime: {
|
||||
type: Number
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
isForward: false
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
watch: {
|
||||
changeTime(newValue, oldValue){
|
||||
this.isForward = newValue > oldValue;
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
timeFormatType() {
|
||||
this.duration >= 3600 ? 2 : 1;
|
||||
},
|
||||
|
||||
changeTimeFormat() {
|
||||
return utils.formatTimeByDuration(this.changeTime, this.timeFormatType);
|
||||
},
|
||||
|
||||
durationFormat() {
|
||||
return utils.formatTimeByDuration(this.duration, this.timeFormatType)
|
||||
},
|
||||
|
||||
imgSrc() {
|
||||
return this.isForward ? forwardImg : backImg;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.plv-player-drag-tips {
|
||||
width: 250rpx;
|
||||
height: 160rpx;
|
||||
background: rgba(0,0,0,.7);
|
||||
border-radius: 10rpx;
|
||||
}
|
||||
|
||||
.plv-player-drag-tips__box {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.plv-player-drag-tips__direction-img {
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
}
|
||||
|
||||
.plv-player-drag-tips__time {
|
||||
font-size: 24rpx;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,271 @@
|
||||
<template>
|
||||
<view class="plv-player-gesture"
|
||||
@touchstart="touchstart"
|
||||
@touchmove="touchmove"
|
||||
@longpress="handleLongPress"
|
||||
@touchcancel="handleTouchCancel"
|
||||
@touchend="touchend">
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
// 屏幕宽度(物理像素)
|
||||
holeWidth: Number,
|
||||
// 视频总时长
|
||||
duration: Number,
|
||||
// 视频当前播放时间
|
||||
currentTime: Number
|
||||
},
|
||||
|
||||
computed: {
|
||||
halfWidth() {
|
||||
return parseInt(this.holeWidth / 2);
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
// 是否在左区域按下手势
|
||||
downLeft: false,
|
||||
// 滑动方向
|
||||
swipeDir: null
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.addHandler();
|
||||
},
|
||||
|
||||
methods:{
|
||||
addHandler() {
|
||||
// 增加父容器左右滑动监听
|
||||
let finalTime = -1;
|
||||
// 计算左右滑动seek进度
|
||||
const countTime = (position) => {
|
||||
const { holeWidth, duration, currentTime } = this;
|
||||
const precent = Math.abs(position) / holeWidth;
|
||||
const nowTime = currentTime || 0;
|
||||
const constSecond = duration < 60 ? duration : 60;
|
||||
let plusTime = parseInt(Math.abs(precent * constSecond));
|
||||
if (position < 0) plusTime = plusTime * -1;
|
||||
finalTime = nowTime + plusTime;
|
||||
finalTime = this.limit(finalTime, 0, duration);
|
||||
return finalTime;
|
||||
};
|
||||
|
||||
this.addGesture((type, position) => {
|
||||
switch (type) {
|
||||
// 下滑
|
||||
case -1:
|
||||
// console.log('//下滑');
|
||||
this.$emit('onGestureEvent', {
|
||||
type: this.downLeft ? 'LEFT_DOWN' : 'RIGHT_DOWN',
|
||||
position
|
||||
});
|
||||
break;
|
||||
// 上滑
|
||||
case -2:
|
||||
// console.log('//上滑');
|
||||
this.$emit('onGestureEvent', {
|
||||
type: this.downLeft ? 'LEFT_UP' : 'RIGHT_UP',
|
||||
position
|
||||
});
|
||||
break;
|
||||
// 左滑
|
||||
case 0:
|
||||
// console.log('//左滑');
|
||||
this.swipeDir = type;
|
||||
countTime(position);
|
||||
this.$emit('onGestureEvent', {
|
||||
type: 'SEEK_TIME_UPDATE',
|
||||
finalTime
|
||||
});
|
||||
break;
|
||||
// 右滑
|
||||
case 1:
|
||||
// console.log('//右滑');
|
||||
this.swipeDir = type;
|
||||
countTime(position);
|
||||
this.$emit('onGestureEvent', {
|
||||
type: 'SEEK_TIME_UPDATE',
|
||||
finalTime
|
||||
});
|
||||
break;
|
||||
// touchend for swipe
|
||||
case 3:
|
||||
if (this.swipeDir > -1) {
|
||||
this.$emit('onGestureEvent', {
|
||||
type: this.swipeDir ? 'SWIPE_RIGHT' : 'SWIPE_LEFT'
|
||||
});
|
||||
this.swipeDir = -1;
|
||||
}
|
||||
|
||||
if (finalTime > -1) {
|
||||
this.$emit('onGestureSeekTo', finalTime);
|
||||
finalTime = -1;
|
||||
}
|
||||
break;
|
||||
// doubleTap
|
||||
case 4:
|
||||
// console.log('>>>> 双击');
|
||||
this.$emit('onGestureEvent', {
|
||||
type: 'DOUBLE_CLICK'
|
||||
});
|
||||
break;
|
||||
// tap
|
||||
case 5:
|
||||
// console.log('>>>> 单击');
|
||||
this.handleClick();
|
||||
break;
|
||||
// longTap
|
||||
case 6:
|
||||
break;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
addGesture(callback) {
|
||||
const { options } = this;
|
||||
let startX, startY, moveEndX, moveEndY, X, Y, position, hasType;
|
||||
let T, lastTap, isJustTouch = true;
|
||||
|
||||
|
||||
const excuteCb = (type) => {
|
||||
// type 手势类型 -2 上滑 -1 下滑 0 左滑 1 右滑 2 just touch 3 touchend 4 doubleTap 5 tap 6 longTap
|
||||
if (callback && typeof callback === 'function')
|
||||
callback(type, position);
|
||||
};
|
||||
|
||||
const YPlus = 20;
|
||||
const YReduce = YPlus * -1;
|
||||
const XPlus = 50;
|
||||
const XReduce = XPlus * -1;
|
||||
|
||||
const isXchange = () => {
|
||||
if (X > XPlus || X < XReduce) {
|
||||
position = X;
|
||||
hasType = 'X';
|
||||
isJustTouch = false;
|
||||
}
|
||||
|
||||
if (X > XPlus) excuteCb(1);
|
||||
// 左滑
|
||||
else if (X < XReduce) excuteCb(0);
|
||||
};
|
||||
|
||||
const isYchange = () => {
|
||||
if (Y > YPlus || Y < YReduce) {
|
||||
position = Y;
|
||||
hasType = 'Y';
|
||||
isJustTouch = false;
|
||||
}
|
||||
|
||||
if (Y > YPlus) excuteCb(-1);
|
||||
// 上滑
|
||||
else if (Y < YReduce) excuteCb(-2);
|
||||
};
|
||||
|
||||
const countDirect = () => {
|
||||
if (hasType) {
|
||||
hasType === 'X' ? isXchange() : isYchange();
|
||||
return;
|
||||
}
|
||||
isXchange();
|
||||
isYchange();
|
||||
};
|
||||
|
||||
this.handleTouch = (type, e) => {
|
||||
switch(type) {
|
||||
case 'start':
|
||||
startX = e.changedTouches[0].pageX;
|
||||
startY = e.changedTouches[0].pageY;
|
||||
T = Date.now();
|
||||
hasType = '';
|
||||
isJustTouch = true;
|
||||
this.downLeft = startX < this.halfWidth ? true : false;
|
||||
|
||||
break;
|
||||
case 'move':
|
||||
moveEndX = e.changedTouches[0].pageX;
|
||||
moveEndY = e.changedTouches[0].pageY;
|
||||
X = moveEndX - startX;
|
||||
Y = moveEndY - startY;
|
||||
countDirect();
|
||||
break;
|
||||
case 'end':
|
||||
if (isJustTouch)
|
||||
if (lastTap && Date.now() - lastTap <= 300)
|
||||
excuteCb(4);
|
||||
else if (Date.now() - T < 1000)
|
||||
excuteCb(5);
|
||||
else
|
||||
excuteCb(6);
|
||||
else
|
||||
excuteCb(3);
|
||||
|
||||
lastTap = Date.now();
|
||||
isJustTouch = true;
|
||||
break;
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
handleClick() {
|
||||
this.$emit('onGestureClick');
|
||||
},
|
||||
|
||||
touchstart(e) {
|
||||
this.handleTouch('start', e);
|
||||
},
|
||||
|
||||
touchmove(e) {
|
||||
this.handleTouch('move', e);
|
||||
},
|
||||
|
||||
touchend(e) {
|
||||
this.handleTouch('end', e);
|
||||
this.$emit('onTouchEnd');
|
||||
},
|
||||
|
||||
handleTouchCancel() {
|
||||
this.$emit('onTouchCancel');
|
||||
},
|
||||
|
||||
handleLongPress() {
|
||||
this.$emit('onLongPress');
|
||||
},
|
||||
|
||||
/**
|
||||
* 控制最小值,最大值
|
||||
* @param num
|
||||
* @param min
|
||||
* @param max
|
||||
* @returns {*}
|
||||
*/
|
||||
limit(num, min, max) {
|
||||
if (num < min) return min;
|
||||
if (num > max) return max;
|
||||
return num;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.plv-player-gesture {
|
||||
flex: 1;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.plv-player-gesture__left {
|
||||
flex: 5;
|
||||
}
|
||||
|
||||
.plv-player-gesture__right {
|
||||
flex: 5;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<view v-if="isShow" class="plv-player-icon">
|
||||
<image class="plv-player-icon__img" :src="imgUrl"></image>
|
||||
<text class="plv-player-icon__text">{{ precent + '%' }}</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
imgUrl: {
|
||||
type: 'string',
|
||||
required: true
|
||||
},
|
||||
precent: {
|
||||
type: 'Number',
|
||||
default: 100,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
precent(num) {
|
||||
if (!this.isShow) this.isShow = true;
|
||||
this.startClock();
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
isShow: false
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
startClock() {
|
||||
this.clearClock();
|
||||
|
||||
this.clock = setTimeout(() => {
|
||||
this.isShow = false;
|
||||
}, 1500);
|
||||
},
|
||||
|
||||
clearClock() {
|
||||
if (this.clock) clearTimeout(this.clock);
|
||||
this.clock = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.plv-player-icon {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.plv-player-icon__img {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
}
|
||||
|
||||
.plv-player-icon__text {
|
||||
color: #FFFFFF;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
</style>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,92 @@
|
||||
<template>
|
||||
<view v-if="isShow" class="plv-player-volume-wrap">
|
||||
<image class="plv-player-volume__img" :src="value === 0 ? muteImgUrl : imgUrl "></image>
|
||||
<view class="plv-player-volume__progress">
|
||||
<view class="plv-player-volume__progress__played" :style="'flex:' + value"></view>
|
||||
<view class="plv-player-volume__progress_surplus" :style="'flex:' + valueLeft"></view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
imgUrl: {
|
||||
type: 'string',
|
||||
required: true
|
||||
},
|
||||
muteImgUrl: {
|
||||
type: 'string',
|
||||
required: true
|
||||
},
|
||||
value: {
|
||||
type: 'Number',
|
||||
default: 0.5,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
value(num) {
|
||||
if (!this.isShow) this.isShow = true;
|
||||
this.startClock();
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
valueLeft() {
|
||||
return 1 - this.value;
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
isShow: false
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
startClock() {
|
||||
this.clearClock();
|
||||
|
||||
this.clock = setTimeout(() => {
|
||||
this.isShow = false;
|
||||
}, 1500);
|
||||
},
|
||||
|
||||
clearClock() {
|
||||
if (this.clock) clearTimeout(this.clock);
|
||||
this.clock = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.plv-player-volume-wrap {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.plv-player-volume__img {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
|
||||
.plv-player-volume__progress {
|
||||
width: 260rpx;
|
||||
height: 8rpx;
|
||||
flex-direction: row;
|
||||
background-color: #09BB07;
|
||||
}
|
||||
|
||||
.plv-player-volume__progress__played {
|
||||
background-color: #007AFF;
|
||||
}
|
||||
|
||||
.plv-player-volume__progress_surplus {
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,855 @@
|
||||
/**
|
||||
* 图片编辑器-手势监听
|
||||
* 1. 支持编译到app-vue(uni-app 2.5.5及以上版本)、H5上
|
||||
*/
|
||||
/** 图片偏移量 */
|
||||
var offset = { x: 0, y: 0 };
|
||||
/** 图片缩放比例 */
|
||||
var scale = 1;
|
||||
/** 图片最小缩放比例 */
|
||||
var minScale = 1;
|
||||
/** 图片旋转角度 */
|
||||
var rotate = 0;
|
||||
/** 触摸点 */
|
||||
var touches = [];
|
||||
/** 图片布局信息 */
|
||||
var img = {};
|
||||
/** 系统信息 */
|
||||
var sys = {};
|
||||
/** 裁剪区域布局信息 */
|
||||
var area = {};
|
||||
/** 触摸行为类型 */
|
||||
var touchType = '';
|
||||
/** 操作角的位置 */
|
||||
var activeAngle = 0;
|
||||
/** 裁剪区域布局信息偏移量 */
|
||||
var areaOffset = { left: 0, right: 0, top: 0, bottom: 0 };
|
||||
/** 元素ID */
|
||||
var elIds = {
|
||||
'imageStyles': 'crop-image',
|
||||
'maskStylesList': 'crop-mask-block',
|
||||
'borderStyles': 'crop-border',
|
||||
'circleBoxStyles': 'crop-circle-box',
|
||||
'circleStyles': 'crop-circle',
|
||||
'gridStylesList': 'crop-grid',
|
||||
'angleStylesList': 'crop-angle',
|
||||
}
|
||||
/** 记录上次初始化时间戳,排除APP重复更新 */
|
||||
var timestamp = 0;
|
||||
/** vue3 renderjs 条件编译无效,以此方式区别 APP 和 H5 */
|
||||
// #ifdef H5
|
||||
var platform = 'H5';
|
||||
// #endif
|
||||
// #ifdef APP
|
||||
var platform = 'APP';
|
||||
// #endif
|
||||
/** 容错值 */
|
||||
var fault = 0.000001;
|
||||
/**
|
||||
* 获取a、b两数中的最小正数
|
||||
* @param a
|
||||
* @param b
|
||||
*/
|
||||
function minimum(a, b) {
|
||||
if (a > 0 && b < 0) return a;
|
||||
if (a < 0 && b > 0) return b;
|
||||
if (a > 0 && b > 0) return Math.min(a, b);
|
||||
return 0;
|
||||
}
|
||||
/**
|
||||
* 在容错访问内获取n近似值
|
||||
* @param n
|
||||
*/
|
||||
function num(n) {
|
||||
var m = parseFloat((n).toFixed(6));
|
||||
return m === fault || m === -fault ? 0 : m;
|
||||
}
|
||||
/**
|
||||
* 比较a值在容错值范围内是否等于b值
|
||||
* @param a
|
||||
* @param b
|
||||
*/
|
||||
function equalsByFault(a, b) {
|
||||
return Math.abs(a - b) <= fault;
|
||||
}
|
||||
/**
|
||||
* 比较a值在容错值范围内是否小于b值
|
||||
* @param a
|
||||
* @param b
|
||||
*/
|
||||
function lessThanByFault(a, b) {
|
||||
var c = a - b;
|
||||
return c < 0 ? c < -fault : c < fault;
|
||||
}
|
||||
/**
|
||||
* 验证并获取有效最大值
|
||||
* @param v
|
||||
* @param max
|
||||
* @param isInclude
|
||||
* @param x
|
||||
* @param y
|
||||
* @param rate
|
||||
* @returns
|
||||
*/
|
||||
function validMax(v, max, isInclude, x, y, rate) {
|
||||
if(typeof max === 'number') {
|
||||
if(isInclude && equalsByFault(max, y)) { // 宽高不等时,x轴用y轴值要做等比例转换
|
||||
var n = num(max * rate);
|
||||
if (n <= x) return n; // 转化后值在x轴最大值范围内
|
||||
return x; // 转化后值超出x轴最大值范围则用最大值
|
||||
}
|
||||
return max;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
/**
|
||||
* 样式对象转字符串
|
||||
* @param {Object} style 样式对象
|
||||
*/
|
||||
function styleToString(style) {
|
||||
if(typeof style === 'string') return style;
|
||||
var str = '';
|
||||
for (let k in style) {
|
||||
str += k + ':' + style[k] + ';';
|
||||
}
|
||||
return str;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param {Object} instance 页面实例对象
|
||||
* @param {Object} key 要修改样式的key
|
||||
* @param {Object|Array} style 样式
|
||||
*/
|
||||
function setStyle(instance, key, style) {
|
||||
// console.log('setStyle', instance, key, JSON.stringify(style))
|
||||
// #ifdef APP-PLUS
|
||||
if(platform === 'APP') {
|
||||
if(Object.prototype.toString.call(style) === '[object Array]') {
|
||||
for (var i = 0, len = style.length; i < len; i++) {
|
||||
var el = window.document.getElementById(elIds[key] + '-' + (i + 1));
|
||||
el && (el.style = styleToString(style[i]));
|
||||
}
|
||||
} else {
|
||||
var el = window.document.getElementById(elIds[key]);
|
||||
el && (el.style = styleToString(style));
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
if(platform === 'H5') instance[key] = style;
|
||||
// #endif
|
||||
}
|
||||
/**
|
||||
* 触发页面实例指定方法
|
||||
* @param {Object} instance 页面实例对象
|
||||
* @param {Object} name 方法名称
|
||||
* @param {Object} obj 传递参数
|
||||
*/
|
||||
function callMethod(instance, name, obj) {
|
||||
// #ifdef APP-PLUS
|
||||
if(platform === 'APP') instance.callMethod(name, obj);
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
if(platform === 'H5') instance[name](obj);
|
||||
// #endif
|
||||
}
|
||||
/**
|
||||
* 计算两点间距
|
||||
* @param {Object} touches 触摸点信息
|
||||
*/
|
||||
function getDistanceByTouches(touches) {
|
||||
// 根据勾股定理求两点间距离
|
||||
var a = touches[1].pageX - touches[0].pageX;
|
||||
var b = touches[1].pageY - touches[0].pageY;
|
||||
var c = Math.sqrt(Math.pow(a, 2) + Math.pow(b, 2));
|
||||
// 求两点间的中点坐标
|
||||
// 1. a、b可能为负值
|
||||
// 2. 在求a、b时,如用touches[1]减touches[0],则求中点坐标也得用touches[1]减a/2、b/2
|
||||
// 3. 同理,在求a、b时,也可用touches[0]减touches[1],则求中点坐标也得用touches[0]减a/2、b/2
|
||||
var x = touches[1].pageX - a / 2;
|
||||
var y = touches[1].pageY - b / 2;
|
||||
return { c, x, y };
|
||||
};
|
||||
|
||||
/**
|
||||
* 修正取值
|
||||
* @param {Object} a
|
||||
* @param {Object} b
|
||||
* @param {Object} c
|
||||
* @param {Object} reverse 是否反向
|
||||
*/
|
||||
function correctValue(a, b, c, reverse) {
|
||||
return num(reverse ? Math.max(Math.min(a, b), c) : Math.min(Math.max(a, b), c));
|
||||
}
|
||||
|
||||
/**
|
||||
* 旋转90°或270°时检查边界:限制 x、y 拖动范围,禁止滑出边界
|
||||
* @param {Object} e 点坐标
|
||||
* @param {Object} xReverse x是否反向
|
||||
* @param {Object} yReverse y是否反向
|
||||
*/
|
||||
function checkRotateRange(e, xReverse, yReverse) {
|
||||
var o = num((img.height - img.width) / 2); // 宽高差值一半
|
||||
return {
|
||||
x: correctValue(e.x, -img.height + o + area.width + area.left, area.left + o, xReverse),
|
||||
y: correctValue(e.y, -img.width - o + area.height + area.top, area.top - o, yReverse)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查边界:限制 x、y 拖动范围,禁止滑出边界
|
||||
* @param {Object} e 点坐标
|
||||
*/
|
||||
function checkRange(e) {
|
||||
var r = rotate / 90 % 2;
|
||||
if(r === 1) { // 因图片宽高可能不等,翻转 90° 或 270° 后图片宽高需反着计算,且左右和上下边界要根据差值做偏移
|
||||
if (area.width === area.height) {
|
||||
return checkRotateRange(e, img.height < area.height, img.width < area.width);
|
||||
}
|
||||
var isInclude = img.height < area.width && img.width < area.height; // 图片是否包含在裁剪区域内
|
||||
if (img.width < area.height || img.height < area.width) {
|
||||
if (area.width < area.height && img.width < img.height) {
|
||||
return isInclude
|
||||
? checkRotateRange(e, area.width < area.height, area.width < area.height)
|
||||
: checkRotateRange(e, false, true);
|
||||
}
|
||||
if (area.height < area.width && img.height < img.width) {
|
||||
return isInclude
|
||||
? checkRotateRange(e, area.height < area.width, area.height < area.width)
|
||||
: checkRotateRange(e, true, false);
|
||||
}
|
||||
}
|
||||
if (img.height >= area.width && img.width >= area.height) {
|
||||
return checkRotateRange(e, false, false);
|
||||
}
|
||||
if (isInclude) {
|
||||
return area.height < area.width
|
||||
? checkRotateRange(e, true, true)
|
||||
: checkRotateRange(e, area.width < area.height, area.width < area.height);
|
||||
}
|
||||
if (img.height < area.width && !img.width < area.height) {
|
||||
return checkRotateRange(e, true, false);
|
||||
}
|
||||
if (!img.height < area.width && img.width < area.height) {
|
||||
return checkRotateRange(e, false, true);
|
||||
}
|
||||
return checkRotateRange(e, img.height < area.height, img.width < area.width);
|
||||
}
|
||||
return {
|
||||
x: correctValue(e.x, -img.width + area.width + area.left, area.left, img.width < area.width),
|
||||
y: correctValue(e.y, -img.height + area.height + area.top, area.top, img.height < area.height)
|
||||
}
|
||||
};
|
||||
/**
|
||||
* 变更图片布局信息
|
||||
* @param {Object} e 布局信息
|
||||
*/
|
||||
function changeImageRect(e) {
|
||||
// console.log('changeImageRect', e)
|
||||
offset.x += e.x || 0;
|
||||
offset.y += e.y || 0;
|
||||
if(e.check && area.checkRange) { // 检查边界
|
||||
var point = checkRange(offset);
|
||||
if(offset.x !== point.x || offset.y !== point.y) {
|
||||
offset = point;
|
||||
}
|
||||
}
|
||||
|
||||
// 因频繁修改 width/height 会造成大量的内存消耗,改为scale
|
||||
// e.instance.imageStyles = {
|
||||
// width: img.width + 'px',
|
||||
// height: img.height + 'px',
|
||||
// transform: 'translate(' + (offset.x + ox) + 'px, ' + (offset.y + ox) + 'px) rotate(' + rotate +'deg)'
|
||||
// };
|
||||
var ox = (img.width - img.oldWidth) / 2;
|
||||
var oy = (img.height - img.oldHeight) / 2;
|
||||
// e.instance.imageStyles = {
|
||||
// width: img.oldWidth + 'px',
|
||||
// height: img.oldHeight + 'px',
|
||||
// transform: 'translate(' + (offset.x + ox) + 'px, ' + (offset.y + oy) + 'px) rotate(' + rotate +'deg) scale(' + scale + ')'
|
||||
// };
|
||||
setStyle(e.instance, 'imageStyles', {
|
||||
width: img.oldWidth + 'px',
|
||||
height: img.oldHeight + 'px',
|
||||
transform: (img.gpu ? 'translateZ(0) ' : '') + 'translate(' + (offset.x + ox) + 'px, ' + (offset.y + oy) + 'px' + ') rotate(' + rotate +'deg) scale(' + scale + ')'
|
||||
});
|
||||
callMethod(e.instance, 'dataChange', {
|
||||
width: img.width,
|
||||
height: img.height,
|
||||
x: offset.x,
|
||||
y: offset.y,
|
||||
rotate: rotate
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 变更裁剪区域布局信息
|
||||
* @param {Object} e 布局信息
|
||||
*/
|
||||
function changeAreaRect(e) {
|
||||
// console.log('changeAreaRect', e)
|
||||
// 变更蒙版样式
|
||||
setStyle(e.instance, 'maskStylesList', [
|
||||
{
|
||||
left: 0,
|
||||
width: (area.left + areaOffset.left) + 'px',
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
'z-index': area.zIndex + 2
|
||||
},
|
||||
{
|
||||
left: (area.right + areaOffset.right) + 'px',
|
||||
right: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
'z-index': area.zIndex + 2
|
||||
},
|
||||
{
|
||||
left: (area.left + areaOffset.left) + 'px',
|
||||
width: (area.width + areaOffset.right - areaOffset.left) + 'px',
|
||||
top: 0,
|
||||
height: (area.top + areaOffset.top) + 'px',
|
||||
'z-index': area.zIndex + 2
|
||||
},
|
||||
{
|
||||
left: (area.left + areaOffset.left) + 'px',
|
||||
width: (area.width + areaOffset.right - areaOffset.left) + 'px',
|
||||
top: (area.bottom + areaOffset.bottom) + 'px',
|
||||
// height: (area.top - areaOffset.bottom + sys.offsetBottom) + 'px',
|
||||
bottom: 0,
|
||||
'z-index': area.zIndex + 2
|
||||
}
|
||||
]);
|
||||
// 变更边框样式
|
||||
if(area.showBorder) {
|
||||
setStyle(e.instance, 'borderStyles', {
|
||||
left: (area.left + areaOffset.left) + 'px',
|
||||
top: (area.top + areaOffset.top) + 'px',
|
||||
width: (area.width + areaOffset.right - areaOffset.left) + 'px',
|
||||
height: (area.height + areaOffset.bottom - areaOffset.top) + 'px',
|
||||
'z-index': area.zIndex + 3
|
||||
});
|
||||
}
|
||||
|
||||
// 变更参考线样式
|
||||
if(area.showGrid) {
|
||||
setStyle(e.instance, 'gridStylesList', [
|
||||
{
|
||||
'border-width': '1px 0 0 0',
|
||||
left: (area.left + areaOffset.left) + 'px',
|
||||
right: (area.right + areaOffset.right) + 'px',
|
||||
top: (area.top + areaOffset.top + (area.height + areaOffset.bottom - areaOffset.top) / 3 - 0.5) + 'px',
|
||||
width: (area.width + areaOffset.right - areaOffset.left) + 'px',
|
||||
'z-index': area.zIndex + 3
|
||||
},
|
||||
{
|
||||
'border-width': '1px 0 0 0',
|
||||
left: (area.left + areaOffset.left) + 'px',
|
||||
right: (area.right + areaOffset.right) + 'px',
|
||||
top: (area.top + areaOffset.top + (area.height + areaOffset.bottom - areaOffset.top) * 2 / 3 - 0.5) + 'px',
|
||||
width: (area.width + areaOffset.right - areaOffset.left) + 'px',
|
||||
'z-index': area.zIndex + 3
|
||||
},
|
||||
{
|
||||
'border-width': '0 1px 0 0',
|
||||
top: (area.top + areaOffset.top) + 'px',
|
||||
bottom: (area.bottom + areaOffset.bottom) + 'px',
|
||||
left: (area.left + areaOffset.left + (area.width + areaOffset.right - areaOffset.left) / 3 - 0.5) + 'px',
|
||||
height: (area.height + areaOffset.bottom - areaOffset.top) + 'px',
|
||||
'z-index': area.zIndex + 3
|
||||
},
|
||||
{
|
||||
'border-width': '0 1px 0 0',
|
||||
top: (area.top + areaOffset.top) + 'px',
|
||||
bottom: (area.bottom + areaOffset.bottom) + 'px',
|
||||
left: (area.left + areaOffset.left + (area.width + areaOffset.right - areaOffset.left) * 2 / 3 - 0.5) + 'px',
|
||||
height: (area.height + areaOffset.bottom - areaOffset.top) + 'px',
|
||||
'z-index': area.zIndex + 3
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
// 变更四个伸缩角样式
|
||||
if(area.showAngle) {
|
||||
setStyle(e.instance, 'angleStylesList', [
|
||||
{
|
||||
'border-width': area.angleBorderWidth + 'px 0 0 ' + area.angleBorderWidth + 'px',
|
||||
left: (area.left + areaOffset.left - area.angleBorderWidth) + 'px',
|
||||
top: (area.top + areaOffset.top - area.angleBorderWidth) + 'px',
|
||||
'z-index': area.zIndex + 3
|
||||
},
|
||||
{
|
||||
'border-width': area.angleBorderWidth + 'px ' + area.angleBorderWidth + 'px 0 0',
|
||||
left: (area.right + areaOffset.right - area.angleSize) + 'px',
|
||||
top: (area.top + areaOffset.top - area.angleBorderWidth) + 'px',
|
||||
'z-index': area.zIndex + 3
|
||||
},
|
||||
{
|
||||
'border-width': '0 0 ' + area.angleBorderWidth + 'px ' + area.angleBorderWidth + 'px',
|
||||
left: (area.left + areaOffset.left - area.angleBorderWidth) + 'px',
|
||||
top: (area.bottom + areaOffset.bottom - area.angleSize) + 'px',
|
||||
'z-index': area.zIndex + 3
|
||||
},
|
||||
{
|
||||
'border-width': '0 ' + area.angleBorderWidth + 'px ' + area.angleBorderWidth + 'px 0',
|
||||
left: (area.right + areaOffset.right - area.angleSize) + 'px',
|
||||
top: (area.bottom + areaOffset.bottom - area.angleSize) + 'px',
|
||||
'z-index': area.zIndex + 3
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
// 变更圆角样式
|
||||
if(area.radius > 0) {
|
||||
var radius = area.radius;
|
||||
if(area.width === area.height && area.radius >= area.width / 2) { // 圆形
|
||||
radius = (area.width / 2);
|
||||
} else { // 圆角矩形
|
||||
if(area.width !== area.height) { // 限制圆角半径不能超过短边的一半
|
||||
radius = Math.min(area.width / 2, area.height / 2, radius);
|
||||
}
|
||||
}
|
||||
setStyle(e.instance, 'circleBoxStyles', {
|
||||
left: (area.left + areaOffset.left) + 'px',
|
||||
top: (area.top + areaOffset.top) + 'px',
|
||||
width: (area.width + areaOffset.right - areaOffset.left) + 'px',
|
||||
height: (area.height + areaOffset.bottom - areaOffset.top) + 'px',
|
||||
'z-index': area.zIndex + 2
|
||||
});
|
||||
setStyle(e.instance, 'circleStyles', {
|
||||
'box-shadow': '0 0 0 ' + Math.max(area.width, area.height) + 'px rgba(51, 51, 51, 0.8)',
|
||||
'border-radius': radius + 'px'
|
||||
});
|
||||
}
|
||||
};
|
||||
/**
|
||||
* 缩放图片
|
||||
* @param {Object} e 布局信息
|
||||
*/
|
||||
function scaleImage(e) {
|
||||
// console.log('scaleImage', e)
|
||||
var last = scale;
|
||||
scale = Math.min(Math.max(e.scale + scale, minScale), img.maxScale);
|
||||
if(last !== scale) {
|
||||
img.width = num(img.oldWidth * scale);
|
||||
img.height = num(img.oldHeight * scale);
|
||||
// 参考问题:有一个长4000px、宽4000px的四方形ABCD,A点的坐标固定在(-2000,-2000),
|
||||
// 该四边形上有一个点E,坐标为(-100,-300),将该四方形复制一份并缩小到90%后,
|
||||
// 新四边形的A点坐标为多少时可使新四边形的E点与原四边形的E点重合?
|
||||
// 预期效果:从图中选取某点(参照物)为中心点进行缩放,缩放时无论图像怎么变化,该点位置始终固定不变
|
||||
// 计算方法:以相同起点先计算缩放前后两点间的距离,再加上原图像偏移量即可
|
||||
e.x = num((e.x - offset.x) * (1 - scale / last));
|
||||
e.y = num((e.y - offset.y) * (1 - scale / last));
|
||||
changeImageRect(e);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
/**
|
||||
* 获取触摸点在哪个角
|
||||
* @param {number} x 触摸点x轴坐标
|
||||
* @param {number} y 触摸点y轴坐标
|
||||
* @return {number} 角的位置:0=无;1=左上;2=右上;3=左下;4=右下;
|
||||
*/
|
||||
function getToucheAngle(x, y) {
|
||||
// console.log('getToucheAngle', x, y, JSON.stringify(area))
|
||||
var o = area.angleBorderWidth; // 需扩大触发范围则把 o 值加大即可
|
||||
var oy = sys.navigation ? 0 : sys.windowTop;
|
||||
if(y >= area.top - o + oy && y <= area.top + area.angleSize + o + oy) {
|
||||
if(x >= area.left - o && x <= area.left + area.angleSize + o) {
|
||||
return 1; // 左上角
|
||||
} else if(x >= area.right - area.angleSize - o && x <= area.right + o) {
|
||||
return 2; // 右上角
|
||||
}
|
||||
} else if(y >= area.bottom - area.angleSize - o + oy && y <= area.bottom + o + oy) {
|
||||
if(x >= area.left - o && x <= area.left + area.angleSize + o) {
|
||||
return 3; // 左下角
|
||||
} else if(x >= area.right - area.angleSize - o && x <= area.right + o) {
|
||||
return 4; // 右下角
|
||||
}
|
||||
}
|
||||
return 0; // 无触摸到角
|
||||
};
|
||||
/**
|
||||
* 重置数据
|
||||
*/
|
||||
function resetData() {
|
||||
offset = { x: 0, y: 0 };
|
||||
scale = 1;
|
||||
minScale = img.minScale;
|
||||
rotate = 0;
|
||||
};
|
||||
function getTouchs(touches) {
|
||||
var result = [];
|
||||
var len = touches ? touches.length : 0
|
||||
for (var i = 0; i < len; i++) {
|
||||
result[i] = {
|
||||
pageX: touches[i].pageX,
|
||||
// h5无标题栏时,窗口顶部距离仍为标题栏高度,且触摸点y轴坐标还是有标题栏的值,即减去标题栏高度的值
|
||||
pageY: touches[i].pageY + sys.windowTop
|
||||
};
|
||||
}
|
||||
return result;
|
||||
};
|
||||
var mouseEvent = false;
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
imageStyles: {},
|
||||
maskStylesList: [{}, {}, {}, {}],
|
||||
borderStyles: {},
|
||||
gridStylesList: [{}, {}, {}, {}],
|
||||
angleStylesList: [{}, {}, {}, {}],
|
||||
circleBoxStyles: {},
|
||||
circleStyles: {}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// 监听 PC 端鼠标滚轮
|
||||
// #ifdef H5
|
||||
platform === 'H5' && window.addEventListener('mousewheel', async (e) => {
|
||||
var touchs = getTouchs([e])
|
||||
img.src && scaleImage({
|
||||
instance: await this.getInstance(),
|
||||
check: true,
|
||||
// 鼠标向上滚动时,deltaY 固定 -100,鼠标向下滚动时,deltaY 固定 100
|
||||
scale: e.deltaY > 0 ? -0.05 : 0.05,
|
||||
x: touchs[0].pageX,
|
||||
y: touchs[0].pageY
|
||||
});
|
||||
});
|
||||
// #endif
|
||||
},
|
||||
// #ifdef H5
|
||||
mounted() {
|
||||
platform === 'H5' && this.initH5Events();
|
||||
},
|
||||
// #endif
|
||||
setPlatform(p) {
|
||||
platform = p;
|
||||
},
|
||||
methods: {
|
||||
// #ifdef H5
|
||||
getTouchEvent(e) {
|
||||
e.touches = [
|
||||
{ pageX: e.pageX, pageY: e.pageY }
|
||||
];
|
||||
return e;
|
||||
},
|
||||
initH5Events() {
|
||||
const preview = document.getElementById('pic-preview');
|
||||
preview?.addEventListener('mousedown', (e, ev) => {
|
||||
mouseEvent = true;
|
||||
this.touchstart(this.getTouchEvent(e));
|
||||
});
|
||||
preview?.addEventListener('mousemove', (e) => {
|
||||
if (!mouseEvent) return;
|
||||
this.touchmove(this.getTouchEvent(e));
|
||||
});
|
||||
preview?.addEventListener('mouseup', (e) => {
|
||||
mouseEvent = false;
|
||||
this.touchend(this.getTouchEvent(e))
|
||||
});
|
||||
preview?.addEventListener('mouseleave', (e) => {
|
||||
mouseEvent = false;
|
||||
this.touchend(this.getTouchEvent(e))
|
||||
});
|
||||
},
|
||||
// #endif
|
||||
async getInstance() {
|
||||
// #ifdef APP-PLUS
|
||||
if(platform === 'APP')
|
||||
return this.$ownerInstance
|
||||
? Promise.resolve(this.$ownerInstance)
|
||||
: new Promise((resolve) => {
|
||||
setTimeout(async () => {
|
||||
resolve(await this.getInstance());
|
||||
});
|
||||
});
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
if(platform === 'H5')
|
||||
return Promise.resolve(this);
|
||||
// #endif
|
||||
},
|
||||
/**
|
||||
* 初始化:观察数据变更
|
||||
* @param {Object} newVal 新数据
|
||||
* @param {Object} oldVal 旧数据
|
||||
* @param {Object} o 组件实例对象
|
||||
*/
|
||||
initObserver: async function(newVal, oldVal, o, i) {
|
||||
// console.log('initObserver', newVal, oldVal, o, i)
|
||||
if(newVal && (!img.src || timestamp !== newVal.timestamp)) {
|
||||
timestamp = newVal.timestamp;
|
||||
img = newVal.img;
|
||||
sys = newVal.sys;
|
||||
area = newVal.area;
|
||||
minScale = img.minScale;
|
||||
resetData();
|
||||
const instance = await this.getInstance()
|
||||
img.src && changeImageRect({
|
||||
instance,
|
||||
x: (sys.windowWidth - img.width) / 2,
|
||||
y: (sys.windowHeight + sys.windowTop - sys.offsetBottom - img.height) / 2
|
||||
});
|
||||
changeAreaRect({
|
||||
instance
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 鼠标滚轮滚动
|
||||
* @param {Object} e 事件对象
|
||||
* @param {Object} o 组件实例对象
|
||||
*/
|
||||
mousewheel: function(e, o) {
|
||||
// h5平台 wheel 事件无法判断滚轮滑动方向,需使用 mousewheel
|
||||
},
|
||||
/**
|
||||
* 触摸开始
|
||||
* @param {Object} e 事件对象
|
||||
* @param {Object} o 组件实例对象
|
||||
*/
|
||||
touchstart: function(e, o) {
|
||||
if(!img.src) return;
|
||||
touches = getTouchs(e.touches);
|
||||
activeAngle = area.showAngle ? getToucheAngle(touches[0].pageX, touches[0].pageY) : 0;
|
||||
if(touches.length === 1 && activeAngle !== 0) {
|
||||
touchType = 'stretch'; // 伸缩裁剪区域
|
||||
} else {
|
||||
touchType = '';
|
||||
}
|
||||
// console.log('touchstart', e, activeAngle)
|
||||
},
|
||||
/**
|
||||
* 触摸移动
|
||||
* @param {Object} e 事件对象
|
||||
* @param {Object} o 组件实例对象
|
||||
*/
|
||||
touchmove: async function(e, o) {
|
||||
if(!img.src) return;
|
||||
// console.log('touchmove', e, o)
|
||||
e.touches = getTouchs(e.touches);
|
||||
if(touchType === 'stretch') { // 触摸四个角进行拉伸
|
||||
var point = e.touches[0];
|
||||
var start = touches[0];
|
||||
var x = point.pageX - start.pageX;
|
||||
var y = point.pageY - start.pageY;
|
||||
if(x !== 0 || y !== 0) {
|
||||
var maxX = num(area.width * (1 - area.minScale));
|
||||
var maxY = num(area.height * (1 - area.minScale));
|
||||
// console.log(x, y, maxX, maxY, offset, area)
|
||||
touches[0] = point;
|
||||
var r = rotate / 90 % 2;
|
||||
var m = r === 1 ? num((img.height - img.width) / 2) : 0; // 宽高差值一半
|
||||
var xCompare = r === 1 ? lessThanByFault(img.height, area.width) : lessThanByFault(img.width, area.width);
|
||||
var yCompare = r === 1 ? lessThanByFault(img.width, area.height) : lessThanByFault(img.height, area.height)
|
||||
var isInclude = xCompare && yCompare;
|
||||
var isIntersect = area.checkRange && (xCompare || yCompare); // 图片是否包含在裁剪区域内
|
||||
var isReverse = !isInclude || num((offset.x - area.left) / area.width) <= num((offset.y - area.top) / area.height) || (area.width > area.height && img.width < img.height && r === 1);
|
||||
switch(activeAngle) {
|
||||
case 1: // 左上角
|
||||
x = num(x + areaOffset.left);
|
||||
y = num(y + areaOffset.top);
|
||||
if(x >= 0 && y >= 0) { // 有效滑动
|
||||
var t = num(offset.y + m - area.top);
|
||||
var l = num(offset.x - m - area.left);
|
||||
// && (offset.x + img.width < area.right || offset.y + img.height < area.bottom)
|
||||
var max = isIntersect && ((l >= 0) || (t >= 0))
|
||||
? minimum(t, l)
|
||||
: false;
|
||||
if(x > y && isReverse) { // 以x轴滑动距离为缩放基准
|
||||
maxX = validMax(maxX, max, isInclude, l, t, area.width / area.height);
|
||||
if(x > maxX) x = maxX;
|
||||
y = num(x * area.height / area.width);
|
||||
} else { // 以y轴滑动距离为缩放基准
|
||||
maxY = validMax(maxY, max, isInclude, t, l, area.height / area.width);
|
||||
if(y > maxY) y = maxY;
|
||||
x = num(y * area.width / area.height);
|
||||
}
|
||||
areaOffset.left = x;
|
||||
areaOffset.top = y;
|
||||
}
|
||||
break;
|
||||
case 2: // 右上角
|
||||
x = num(x + areaOffset.right);
|
||||
y = num(y + areaOffset.top);
|
||||
if(x <= 0 && y >= 0) { // 有效滑动
|
||||
var w = (r === 1 ? img.height : img.width);
|
||||
var t = num(offset.y + m - area.top);
|
||||
var l = num(area.right + m - offset.x - w);
|
||||
var max = isIntersect && ((t >= 0) || (l >= 0))
|
||||
? minimum(t, l)
|
||||
: false;
|
||||
if(-x > y && isReverse) { // 以x轴滑动距离为缩放基准
|
||||
maxX = validMax(maxX, max, isInclude, l, t, area.width / area.height);
|
||||
if(-x > maxX) x = -maxX;
|
||||
y = num(-x * area.height / area.width);
|
||||
} else { // 以y轴滑动距离为缩放基准
|
||||
maxY = validMax(maxY, max, isInclude, t, l, area.height / area.width);
|
||||
if(y > maxY) y = maxY;
|
||||
x = num(-y * area.width / area.height);
|
||||
}
|
||||
areaOffset.right = x;
|
||||
areaOffset.top = y;
|
||||
}
|
||||
break;
|
||||
case 3: // 左下角
|
||||
x += num(x + areaOffset.left);
|
||||
y += num(y + areaOffset.bottom);
|
||||
if(x >= 0 && y <= 0) { // 有效滑动
|
||||
var w = (r === 1 ? img.width : img.height);
|
||||
var t = num(area.bottom - m - offset.y - w);
|
||||
var l = num(offset.x - m - area.left);
|
||||
var max = isIntersect && ((l >= 0) || (t >= 0))
|
||||
? minimum(t, l)
|
||||
: false;
|
||||
if(x > -y && isReverse) { // 以x轴滑动距离为缩放基准
|
||||
maxX = validMax(maxX, max, isInclude, l, t, area.width / area.height);
|
||||
if(x > maxX) x = maxX;
|
||||
y = num(-x * area.height / area.width);
|
||||
} else { // 以y轴滑动距离为缩放基准
|
||||
maxY = validMax(maxY, max, isInclude, t, l, area.height / area.width);
|
||||
if(-y > maxY) y = -maxY;
|
||||
x = num(-y * area.width / area.height);
|
||||
}
|
||||
areaOffset.left = x;
|
||||
areaOffset.bottom = y;
|
||||
}
|
||||
break;
|
||||
case 4: // 右下角
|
||||
x = num(x + areaOffset.right);
|
||||
y = num(y + areaOffset.bottom);
|
||||
if(x <= 0 && y <= 0) { // 有效滑动
|
||||
var w = (r === 1 ? img.height : img.width);
|
||||
var h = (r === 1 ? img.width : img.height);
|
||||
var t = num(area.bottom - offset.y - h - m);
|
||||
var l = num(area.right + m - offset.x - w);
|
||||
var max = isIntersect && ((l >= 0) || (t >= 0))
|
||||
? minimum(t, l)
|
||||
: false;
|
||||
if(-x > -y && isReverse) { // 以x轴滑动距离为缩放基准
|
||||
maxX = validMax(maxX, max, isInclude, l, t, area.width / area.height);
|
||||
if(-x > maxX) x = -maxX;
|
||||
y = num(x * area.height / area.width);
|
||||
} else { // 以y轴滑动距离为缩放基准
|
||||
maxY = validMax(maxY, max, isInclude, t, l, area.height / area.width);
|
||||
if(-y > maxY) y = -maxY;
|
||||
x = num(y * area.width / area.height);
|
||||
}
|
||||
areaOffset.right = x;
|
||||
areaOffset.bottom = y;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// console.log(x, y, JSON.stringify(areaOffset))
|
||||
changeAreaRect({
|
||||
instance: await this.getInstance(),
|
||||
});
|
||||
// this.draw();
|
||||
}
|
||||
} else if (e.touches.length == 2) { // 双点触摸缩放
|
||||
var start = getDistanceByTouches(touches);
|
||||
var end = getDistanceByTouches(e.touches);
|
||||
scaleImage({
|
||||
instance: await this.getInstance(),
|
||||
check: !area.bounce,
|
||||
scale: (end.c - start.c) / 100,
|
||||
x: end.x,
|
||||
y: end.y
|
||||
});
|
||||
touchType = 'scale';
|
||||
} else if(touchType === 'scale') {// 从双点触摸变成单点触摸 / 从缩放变成拖动
|
||||
touchType = 'move';
|
||||
} else {
|
||||
changeImageRect({
|
||||
instance: await this.getInstance(),
|
||||
check: !area.bounce,
|
||||
x: e.touches[0].pageX - touches[0].pageX,
|
||||
y: e.touches[0].pageY - touches[0].pageY
|
||||
});
|
||||
touchType = 'move';
|
||||
}
|
||||
touches = e.touches;
|
||||
},
|
||||
/**
|
||||
* 触摸结束
|
||||
* @param {Object} e 事件对象
|
||||
* @param {Object} o 组件实例对象
|
||||
*/
|
||||
touchend: async function(e, o) {
|
||||
if(!img.src) return;
|
||||
if(touchType === 'stretch') { // 拉伸裁剪区域的四个角缩放
|
||||
// 裁剪区域宽度被缩放到多少
|
||||
var left = areaOffset.left;
|
||||
var right = areaOffset.right;
|
||||
var top = areaOffset.top;
|
||||
var bottom = areaOffset.bottom;
|
||||
var w = area.width + right - left;
|
||||
var h = area.height + bottom - top;
|
||||
// 图像放大倍数
|
||||
var p = scale * (area.width / w) - scale;
|
||||
// 复原裁剪区域
|
||||
areaOffset = { left: 0, right: 0, top: 0, bottom: 0 };
|
||||
changeAreaRect({
|
||||
instance: await this.getInstance(),
|
||||
});
|
||||
scaleImage({
|
||||
instance: await this.getInstance(),
|
||||
scale: p,
|
||||
x: area.left + left + (1 === activeAngle || 3 === activeAngle ? w : 0),
|
||||
y: area.top + top + (1 === activeAngle || 2 === activeAngle ? h : 0)
|
||||
});
|
||||
} else if (area.bounce) { // 检查边界并矫正,实现拖动到边界时有回弹效果
|
||||
changeImageRect({
|
||||
instance: await this.getInstance(),
|
||||
check: true
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 顺时针翻转图片90°
|
||||
* @param {Object} e 事件对象
|
||||
* @param {Object} o 组件实例对象
|
||||
*/
|
||||
rotateImage: async function(r) {
|
||||
rotate = (rotate + (r || 90)) % 360;
|
||||
|
||||
if(img.minScale >= 1 && area.checkRange) {
|
||||
// 因图片宽高可能不等,翻转后图片宽高需足够填满裁剪区域
|
||||
minScale = 1;
|
||||
if(img.width < area.height) {
|
||||
minScale = area.height / img.oldWidth;
|
||||
} else if(img.height < area.width) {
|
||||
minScale = area.width / img.oldHeight;
|
||||
}
|
||||
if(minScale !== 1) {
|
||||
scaleImage({
|
||||
instance: await this.getInstance(),
|
||||
scale: minScale - scale,
|
||||
x: sys.windowWidth / 2,
|
||||
y: (sys.windowHeight - sys.offsetBottom) / 2
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 由于拖动画布后会导致图片位置偏移,翻转时的旋转中心点需是图片区域+偏移区域的中心点
|
||||
// 翻转x轴中心点 = (超出裁剪区域右侧的图片宽度 - 超出裁剪区域左侧的图片宽度) / 2
|
||||
// 翻转y轴中心点 = (超出裁剪区域下方的图片宽度 - 超出裁剪区域上方的图片宽度) / 2
|
||||
var ox = ((offset.x + img.width - area.right) - (area.left - offset.x)) / 2;
|
||||
var oy = ((offset.y + img.height - area.bottom) - (area.top - offset.y)) / 2;
|
||||
changeImageRect({
|
||||
instance: await this.getInstance(),
|
||||
check: true,
|
||||
x: -ox - oy,
|
||||
y: -oy + ox
|
||||
});
|
||||
},
|
||||
rotateImage90: function() {
|
||||
this.rotateImage(90)
|
||||
},
|
||||
rotateImage270: function() {
|
||||
this.rotateImage(270)
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,746 @@
|
||||
<template>
|
||||
<view class="image-cropper" :style="{ zIndex }" @wheel="cropper.mousewheel">
|
||||
<canvas v-if="use2d" type="2d" id="imgCanvas" class="img-canvas" :style="{
|
||||
width: `${canvansWidth}px`,
|
||||
height: `${canvansHeight}px`
|
||||
}"></canvas>
|
||||
<canvas v-else id="imgCanvas" canvas-id="imgCanvas" class="img-canvas" :style="{
|
||||
width: `${canvansWidth}px`,
|
||||
height: `${canvansHeight}px`
|
||||
}"></canvas>
|
||||
<view id="pic-preview" class="pic-preview" :change:init="cropper.initObserver" :init="initData" @touchstart="cropper.touchstart" @touchmove="cropper.touchmove" @touchend="cropper.touchend">
|
||||
<image v-if="imgSrc" id="crop-image" class="crop-image" :style="cropper.imageStyles" :src="imgSrc" webp></image>
|
||||
<view v-for="(item, index) in maskList" :key="item.id" :id="item.id" class="crop-mask-block" :style="cropper.maskStylesList[index]"></view>
|
||||
<view v-if="showBorder" id="crop-border" class="crop-border" :style="cropper.borderStyles"></view>
|
||||
<view v-if="radius > 0" id="crop-circle-box" class="crop-circle-box" :style="cropper.circleBoxStyles">
|
||||
<view class="crop-circle" id="crop-circle" :style="cropper.circleStyles"></view>
|
||||
</view>
|
||||
<block v-if="showGrid">
|
||||
<view v-for="(item, index) in gridList" :key="item.id" :id="item.id" class="crop-grid" :style="cropper.gridStylesList[index]"></view>
|
||||
</block>
|
||||
<block v-if="showAngle">
|
||||
<view v-for="(item, index) in angleList" :key="item.id" :id="item.id" class="crop-angle" :style="cropper.angleStylesList[index]">
|
||||
<view :style="[{
|
||||
width: `${angleSize}px`,
|
||||
height: `${angleSize}px`
|
||||
}]"></view>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
<slot />
|
||||
<view class="fixed-bottom safe-area-inset-bottom" :style="{ zIndex: initData.area.zIndex + 99 }">
|
||||
<view v-if="(rotatable || reverseRotatable) && !!imgSrc" class="action-bar">
|
||||
<view v-if="reverseRotatable" class="rotate-icon" @click="cropper.rotateImage270"></view>
|
||||
<view v-if="rotatable" class="rotate-icon is-reverse" @click="cropper.rotateImage90"></view>
|
||||
</view>
|
||||
<view v-if="!choosable" class="choose-btn" @click="cropClick">确定</view>
|
||||
<block v-else-if="!!imgSrc">
|
||||
<view class="rechoose" @click="chooseImage">重选</view>
|
||||
<button class="button" size="mini" @click="cropClick">确定</button>
|
||||
</block>
|
||||
<view v-else class="choose-btn" @click="chooseImage">选择图片</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<!-- #ifdef APP-VUE -->
|
||||
<script module="cropper" lang="renderjs">
|
||||
import cropper from './qf-image-cropper.render.js';
|
||||
// vue3 app renderjs中条件编译无效
|
||||
cropper.setPlatform('APP');
|
||||
export default {
|
||||
mixins: [ cropper ]
|
||||
}
|
||||
</script>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef H5 -->
|
||||
<script module="cropper" lang="renderjs">
|
||||
import cropper from './qf-image-cropper.render.js';
|
||||
export default {
|
||||
mixins: [ cropper ]
|
||||
}
|
||||
</script>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef MP-WEIXIN || MP-QQ -->
|
||||
<script module="cropper" lang="wxs" src="./qf-image-cropper.wxs"></script>
|
||||
<!-- #endif -->
|
||||
<script>
|
||||
/** 裁剪区域最大宽高所占屏幕宽度百分比 */
|
||||
const AREA_SIZE = 75;
|
||||
/** 图片默认宽高 */
|
||||
const IMG_SIZE = 300;
|
||||
|
||||
export default {
|
||||
name:"qf-image-cropper",
|
||||
// #ifdef MP-WEIXIN
|
||||
options: {
|
||||
// 表示启用样式隔离,在自定义组件内外,使用 class 指定的样式将不会相互影响
|
||||
styleIsolation: "isolated"
|
||||
},
|
||||
// #endif
|
||||
props: {
|
||||
/** 图片资源地址 */
|
||||
src: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
/** 裁剪宽度,有些平台或设备对于canvas的尺寸有限制,过大可能会导致无法正常绘制 */
|
||||
width: {
|
||||
type: Number,
|
||||
default: IMG_SIZE
|
||||
},
|
||||
/** 裁剪高度,有些平台或设备对于canvas的尺寸有限制,过大可能会导致无法正常绘制 */
|
||||
height: {
|
||||
type: Number,
|
||||
default: IMG_SIZE
|
||||
},
|
||||
/** 是否绘制裁剪区域边框 */
|
||||
showBorder: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
/** 是否绘制裁剪区域网格参考线 */
|
||||
showGrid: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
/** 是否展示四个支持伸缩的角 */
|
||||
showAngle: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
/** 裁剪区域最小缩放倍数 */
|
||||
areaScale: {
|
||||
type: Number,
|
||||
default: 0.3
|
||||
},
|
||||
/** 图片最小缩放倍数 */
|
||||
minScale: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
/** 图片最大缩放倍数 */
|
||||
maxScale: {
|
||||
type: Number,
|
||||
default: 5
|
||||
},
|
||||
/** 检查图片位置是否超出裁剪边界,如果超出则会矫正位置 */
|
||||
checkRange: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
/** 生成图片背景色:如果裁剪区域没有完全包含在图片中时,不设置该属性生成图片存在一定的透明块 */
|
||||
backgroundColor: {
|
||||
type: String
|
||||
},
|
||||
/** 是否有回弹效果:当 checkRange 为 true 时有效,拖动时可以拖出边界,释放时会弹回边界 */
|
||||
bounce: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
/** 是否支持翻转 */
|
||||
rotatable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
/** 是否支持逆向翻转 */
|
||||
reverseRotatable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
/** 是否支持从本地选择素材 */
|
||||
choosable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
/** 是否开启硬件加速,图片缩放过程中如果出现元素的“留影”或“重影”效果,可通过该方式解决或减轻这一问题 */
|
||||
gpu: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
/** 四个角尺寸,单位px */
|
||||
angleSize: {
|
||||
type: Number,
|
||||
default: 20
|
||||
},
|
||||
/** 四个角边框宽度,单位px */
|
||||
angleBorderWidth: {
|
||||
type: Number,
|
||||
default: 2
|
||||
},
|
||||
zIndex: {
|
||||
type: [Number, String]
|
||||
},
|
||||
/** 裁剪图片圆角半径,单位px */
|
||||
radius: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
/** 生成文件的类型,只支持 'jpg' 或 'png'。默认为 'png' */
|
||||
fileType: {
|
||||
type: String,
|
||||
default: 'png'
|
||||
},
|
||||
/**
|
||||
* 图片从绘制到生成所需时间,单位ms
|
||||
* 微信小程序平台使用 `Canvas 2D` 绘制时有效
|
||||
* 如绘制大图或出现裁剪图片空白等情况应适当调大该值,因 `Canvas 2d` 采用同步绘制,需自己把控绘制完成时间
|
||||
*/
|
||||
delay: {
|
||||
type: Number,
|
||||
default: 1000
|
||||
},
|
||||
// #ifdef H5
|
||||
/**
|
||||
* 页面是否是原生标题栏
|
||||
* H5平台当 showAngle 为 true 时,使用插件的页面在 `page.json` 中配置了 "navigationStyle": "custom" 时,必须将此值设为 false ,否则四个可拉伸角的触发位置会有偏差。
|
||||
* 注:因H5平台的窗口高度是包含标题栏的,而屏幕触摸点的坐标是不包含的
|
||||
*/
|
||||
navigation: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
// #endif
|
||||
},
|
||||
emits: ["crop"],
|
||||
data() {
|
||||
return {
|
||||
// 用不同 id 使 v-for key 不重复
|
||||
maskList: [
|
||||
{ id: 'crop-mask-block-1' },
|
||||
{ id: 'crop-mask-block-2' },
|
||||
{ id: 'crop-mask-block-3' },
|
||||
{ id: 'crop-mask-block-4' },
|
||||
],
|
||||
gridList: [
|
||||
{ id: 'crop-grid-1' },
|
||||
{ id: 'crop-grid-2' },
|
||||
{ id: 'crop-grid-3' },
|
||||
{ id: 'crop-grid-4' },
|
||||
],
|
||||
angleList: [
|
||||
{ id: 'crop-angle-1' },
|
||||
{ id: 'crop-angle-2' },
|
||||
{ id: 'crop-angle-3' },
|
||||
{ id: 'crop-angle-4' },
|
||||
],
|
||||
/** 本地缓存的图片路径 */
|
||||
imgSrc: '',
|
||||
/** 图片的裁剪宽度 */
|
||||
imgWidth: IMG_SIZE,
|
||||
/** 图片的裁剪高度 */
|
||||
imgHeight: IMG_SIZE,
|
||||
/** 裁剪区域最大宽度所占屏幕宽度百分比 */
|
||||
widthPercent: AREA_SIZE,
|
||||
/** 裁剪区域最大高度所占屏幕宽度百分比 */
|
||||
heightPercent: AREA_SIZE,
|
||||
/** 裁剪区域布局信息 */
|
||||
area: {},
|
||||
/** 未被缩放过的图片宽 */
|
||||
oldWidth: 0,
|
||||
/** 未被缩放过的图片高 */
|
||||
oldHeight: 0,
|
||||
/** 系统信息 */
|
||||
sys: uni.getSystemInfoSync(),
|
||||
scaleWidth: 0,
|
||||
scaleHeight: 0,
|
||||
rotate: 0,
|
||||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
use2d: false,
|
||||
canvansWidth: 0,
|
||||
canvansHeight: 0,
|
||||
// imageStyles: {},
|
||||
// maskStylesList: [{}, {}, {}, {}],
|
||||
// borderStyles: {},
|
||||
// gridStylesList: [{}, {}, {}, {}],
|
||||
// angleStylesList: [{}, {}, {}, {}],
|
||||
// circleBoxStyles: {},
|
||||
// circleStyles: {},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
initData() {
|
||||
// console.log('initData')
|
||||
return {
|
||||
timestamp: new Date().getTime(),
|
||||
area: {
|
||||
...this.area,
|
||||
bounce: this.bounce,
|
||||
showBorder: this.showBorder,
|
||||
showGrid: this.showGrid,
|
||||
showAngle: this.showAngle,
|
||||
angleSize: this.angleSize,
|
||||
angleBorderWidth: this.angleBorderWidth,
|
||||
minScale: this.areaScale,
|
||||
widthPercent: this.widthPercent,
|
||||
heightPercent: this.heightPercent,
|
||||
radius: this.radius,
|
||||
checkRange: this.checkRange,
|
||||
zIndex: +this.zIndex || 0,
|
||||
},
|
||||
sys: this.sys,
|
||||
img: {
|
||||
minScale: this.minScale,
|
||||
maxScale: this.maxScale,
|
||||
src: this.imgSrc,
|
||||
width: this.oldWidth,
|
||||
height: this.oldHeight,
|
||||
oldWidth: this.oldWidth,
|
||||
oldHeight: this.oldHeight,
|
||||
gpu: this.gpu,
|
||||
}
|
||||
}
|
||||
},
|
||||
imgProps() {
|
||||
return {
|
||||
width: this.width,
|
||||
height: this.height,
|
||||
src: this.src,
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
imgProps: {
|
||||
handler(val, oldVal) {
|
||||
// 自定义裁剪尺,示例如下:
|
||||
this.imgWidth = Number(val.width) || IMG_SIZE;
|
||||
this.imgHeight = Number(val.height) || IMG_SIZE;
|
||||
let use2d = true;
|
||||
// #ifndef MP-WEIXIN
|
||||
use2d = false;
|
||||
// #endif
|
||||
// if(use2d && (this.imgWidth > 1365 || this.imgHeight > 1365)) {
|
||||
// use2d = false;
|
||||
// }
|
||||
let canvansWidth = this.imgWidth;
|
||||
let canvansHeight = this.imgHeight;
|
||||
let size = Math.max(canvansWidth, canvansHeight)
|
||||
let scalc = 1;
|
||||
if(size > 1365) {
|
||||
scalc = 1365 / size;
|
||||
}
|
||||
this.canvansWidth = canvansWidth * scalc;
|
||||
this.canvansHeight = canvansHeight * scalc;
|
||||
this.use2d = use2d;
|
||||
this.initArea();
|
||||
const src = val.src || this.imgSrc;
|
||||
src && this.initImage(src, oldVal === undefined);
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
/** 提供给wxs调用,用来接收图片变更数据 */
|
||||
dataChange(e) {
|
||||
// console.log('dataChange', e)
|
||||
this.scaleWidth = e.width;
|
||||
this.scaleHeight = e.height;
|
||||
this.rotate = e.rotate;
|
||||
this.offsetX = e.x;
|
||||
this.offsetY = e.y;
|
||||
},
|
||||
/** 初始化裁剪区域布局信息 */
|
||||
initArea() {
|
||||
// 底部操作栏高度 = 底部底部操作栏内容高度 + 设备底部安全区域高度
|
||||
this.sys.offsetBottom = uni.upx2px(100) + this.sys.safeAreaInsets.bottom;
|
||||
// #ifndef H5
|
||||
this.sys.windowTop = 0;
|
||||
this.sys.navigation = true;
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
// h5平台的窗口高度是包含标题栏的
|
||||
this.sys.windowTop = this.sys.windowTop || 44;
|
||||
this.sys.navigation = this.navigation;
|
||||
// #endif
|
||||
let wp = this.widthPercent;
|
||||
let hp = this.heightPercent;
|
||||
if (this.imgWidth > this.imgHeight) {
|
||||
hp = hp * this.imgHeight / this.imgWidth;
|
||||
} else if (this.imgWidth < this.imgHeight) {
|
||||
wp = wp * this.imgWidth / this.imgHeight;
|
||||
}
|
||||
const size = this.sys.windowWidth > this.sys.windowHeight ? this.sys.windowHeight : this.sys.windowWidth;
|
||||
const width = size * wp / 100;
|
||||
const height = size * hp / 100;
|
||||
const left = (this.sys.windowWidth - width) / 2;
|
||||
const right = left + width;
|
||||
const top = (this.sys.windowHeight + this.sys.windowTop - this.sys.offsetBottom - height) / 2;
|
||||
const bottom = this.sys.windowHeight + this.sys.windowTop - this.sys.offsetBottom - top;
|
||||
this.area = { width, height, left, right, top, bottom };
|
||||
this.scaleWidth = width;
|
||||
this.scaleHeight = height;
|
||||
},
|
||||
/** 从本地选取图片 */
|
||||
chooseImage(options) {
|
||||
// #ifdef MP-WEIXIN || MP-JD
|
||||
if(uni.chooseMedia) {
|
||||
uni.chooseMedia({
|
||||
...options,
|
||||
count: 1,
|
||||
mediaType: ['image'],
|
||||
success: (res) => {
|
||||
this.resetData();
|
||||
this.initImage(res.tempFiles[0].tempFilePath);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
// #endif
|
||||
uni.chooseImage({
|
||||
...options,
|
||||
count: 1,
|
||||
success: (res) => {
|
||||
this.resetData();
|
||||
this.initImage(res.tempFiles[0].path);
|
||||
}
|
||||
});
|
||||
},
|
||||
/** 重置数据 */
|
||||
resetData() {
|
||||
this.imgSrc = '';
|
||||
this.rotate = 0;
|
||||
this.offsetX = 0;
|
||||
this.offsetY = 0;
|
||||
this.initArea();
|
||||
},
|
||||
/**
|
||||
* 初始化图片信息
|
||||
* @param {String} url 图片链接
|
||||
*/
|
||||
initImage(url, isFirst) {
|
||||
uni.getImageInfo({
|
||||
src: url,
|
||||
success: async (res) => {
|
||||
if (isFirst && this.src === url) await (new Promise((resolve) => setTimeout(resolve, 50)));
|
||||
this.imgSrc = res.path;
|
||||
let scale = res.width / res.height;
|
||||
let areaScale = this.area.width / this.area.height;
|
||||
if (scale > 1) { // 横向图片
|
||||
if (scale >= areaScale) { // 图片宽不小于目标宽,则高固定,宽自适应
|
||||
this.scaleWidth = (this.scaleHeight / res.height) * this.scaleWidth * (res.width / this.scaleWidth);
|
||||
} else { // 否则宽固定、高自适应
|
||||
this.scaleHeight = res.height * this.scaleWidth / res.width;
|
||||
}
|
||||
} else { // 纵向图片
|
||||
if (scale <= areaScale) { // 图片高不小于目标高,宽固定,高自适应
|
||||
this.scaleHeight = (this.scaleWidth / res.width) * this.scaleHeight / (this.scaleHeight / res.height);
|
||||
} else { // 否则高固定,宽自适应
|
||||
this.scaleWidth = res.width * this.scaleHeight / res.height;
|
||||
}
|
||||
}
|
||||
// 记录原始宽高,为缩放比列做限制
|
||||
this.oldWidth = +this.scaleWidth.toFixed(2);
|
||||
this.oldHeight = +this.scaleHeight.toFixed(2);
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error(err)
|
||||
}
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 剪切图片圆角
|
||||
* @param {Object} ctx canvas 的绘图上下文对象
|
||||
* @param {Number} radius 圆角半径
|
||||
* @param {Number} scale 生成图片的实际尺寸与截取区域比
|
||||
* @param {Function} drawImage 执行剪切时所调用的绘图方法,入参为是否执行了剪切
|
||||
*/
|
||||
drawClipImage(ctx, radius, scale, drawImage) {
|
||||
if(radius > 0) {
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
const w = this.canvansWidth;
|
||||
const h = this.canvansHeight;
|
||||
if(w === h && radius >= w / 2) { // 圆形
|
||||
ctx.arc(w / 2, h / 2, w / 2, 0, 2 * Math.PI);
|
||||
} else { // 圆角矩形
|
||||
if(w !== h) { // 限制圆角半径不能超过短边的一半
|
||||
radius = Math.min(w / 2, h / 2, radius);
|
||||
// radius = Math.min(Math.max(w, h) / 2, radius);
|
||||
}
|
||||
ctx.moveTo(radius, 0);
|
||||
ctx.arcTo(w, 0, w, h, radius);
|
||||
ctx.arcTo(w, h, 0, h, radius);
|
||||
ctx.arcTo(0, h, 0, 0, radius);
|
||||
ctx.arcTo(0, 0, w, 0, radius);
|
||||
ctx.closePath();
|
||||
}
|
||||
ctx.clip();
|
||||
drawImage && drawImage(true);
|
||||
ctx.restore();
|
||||
} else {
|
||||
drawImage && drawImage(false);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 旋转图片
|
||||
* @param {Object} ctx canvas 的绘图上下文对象
|
||||
* @param {Number} rotate 旋转角度
|
||||
* @param {Number} scale 生成图片的实际尺寸与截取区域比
|
||||
*/
|
||||
drawRotateImage(ctx, rotate, scale) {
|
||||
if(rotate !== 0) {
|
||||
// 1. 以图片中心点为旋转中心点
|
||||
const x = this.scaleWidth * scale / 2;
|
||||
const y = this.scaleHeight * scale / 2;
|
||||
ctx.translate(x, y);
|
||||
// 2. 旋转画布
|
||||
ctx.rotate(rotate * Math.PI / 180);
|
||||
// 3. 旋转完画布后恢复设置旋转中心时所做的偏移
|
||||
ctx.translate(-x, -y);
|
||||
}
|
||||
},
|
||||
drawImage(ctx, image, callback) {
|
||||
// 生成图片的实际尺寸与截取区域比
|
||||
const scale = this.canvansWidth / this.area.width;
|
||||
if(this.backgroundColor) {
|
||||
if(ctx.setFillStyle) ctx.setFillStyle(this.backgroundColor);
|
||||
else ctx.fillStyle = this.backgroundColor;
|
||||
ctx.fillRect(0, 0, this.canvansWidth, this.canvansHeight);
|
||||
}
|
||||
this.drawClipImage(ctx, this.radius, scale, () => {
|
||||
this.drawRotateImage(ctx, this.rotate, scale);
|
||||
const r = this.rotate / 90;
|
||||
ctx.drawImage(
|
||||
image,
|
||||
[
|
||||
(this.offsetX - this.area.left),
|
||||
(this.offsetY - this.area.top),
|
||||
-(this.offsetX - this.area.left),
|
||||
-(this.offsetY - this.area.top)
|
||||
][r] * scale,
|
||||
[
|
||||
(this.offsetY - this.area.top),
|
||||
-(this.offsetX - this.area.left),
|
||||
-(this.offsetY - this.area.top),
|
||||
(this.offsetX - this.area.left)
|
||||
][r] * scale,
|
||||
this.scaleWidth * scale,
|
||||
this.scaleHeight * scale
|
||||
);
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 绘图
|
||||
* @param {Object} canvas
|
||||
* @param {Object} ctx canvas 的绘图上下文对象
|
||||
* @param {String} src 图片路径
|
||||
* @param {Function} callback 开始绘制时回调
|
||||
*/
|
||||
draw2DImage(canvas, ctx, src, callback) {
|
||||
// console.log('draw2DImage', canvas, ctx, src, callback)
|
||||
if(canvas) {
|
||||
const image = canvas.createImage();
|
||||
image.onload = () => {
|
||||
this.drawImage(ctx, image);
|
||||
// 如果觉得`生成时间过长`或`出现生成图片空白`可尝试调整延迟时间
|
||||
callback && setTimeout(callback, this.delay);
|
||||
};
|
||||
image.onerror = (err) => {
|
||||
console.error(err)
|
||||
uni.hideLoading();
|
||||
};
|
||||
image.src = src;
|
||||
} else {
|
||||
this.drawImage(ctx, src);
|
||||
setTimeout(() => {
|
||||
ctx.draw(false, callback);
|
||||
}, 200);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 画布转图片到本地缓存
|
||||
* @param {Object} canvas
|
||||
* @param {String} canvasId
|
||||
*/
|
||||
canvasToTempFilePath(canvas, canvasId) {
|
||||
// console.log('canvasToTempFilePath', canvas, canvasId)
|
||||
uni.canvasToTempFilePath({
|
||||
canvas,
|
||||
canvasId,
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: this.canvansWidth,
|
||||
height: this.canvansHeight,
|
||||
destWidth: this.imgWidth, // 必要,保证生成图片宽度不受设备分辨率影响
|
||||
destHeight: this.imgHeight, // 必要,保证生成图片高度不受设备分辨率影响
|
||||
fileType: this.fileType, // 目标文件的类型,默认png
|
||||
success: (res) => {
|
||||
// 生成的图片临时文件路径
|
||||
this.handleImage(res.tempFilePath);
|
||||
},
|
||||
fail: (err) => {
|
||||
uni.hideLoading();
|
||||
uni.showToast({ title: '裁剪失败,生成图片异常!', icon: 'none' });
|
||||
}
|
||||
}, this);
|
||||
},
|
||||
/** 确认裁剪 */
|
||||
cropClick() {
|
||||
uni.showLoading({ title: '裁剪中...', mask: true });
|
||||
if(!this.use2d) {
|
||||
const ctx = uni.createCanvasContext('imgCanvas', this);
|
||||
ctx.clearRect(0, 0, this.canvansWidth, this.canvansHeight);
|
||||
this.draw2DImage(null, ctx, this.imgSrc, () => {
|
||||
this.canvasToTempFilePath(null, 'imgCanvas');
|
||||
});
|
||||
return;
|
||||
}
|
||||
// #ifdef MP-WEIXIN
|
||||
const query = uni.createSelectorQuery().in(this);
|
||||
query.select('#imgCanvas')
|
||||
.fields({ node: true, size: true })
|
||||
.exec((res) => {
|
||||
const canvas = res[0].node;
|
||||
|
||||
const dpr = uni.getSystemInfoSync().pixelRatio;
|
||||
canvas.width = res[0].width * dpr;
|
||||
canvas.height = res[0].height * dpr;
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.scale(dpr, dpr);
|
||||
ctx.clearRect(0, 0, this.canvansWidth, this.canvansHeight);
|
||||
|
||||
this.draw2DImage(canvas, ctx, this.imgSrc, () => {
|
||||
this.canvasToTempFilePath(canvas);
|
||||
});
|
||||
});
|
||||
// #endif
|
||||
},
|
||||
handleImage(tempFilePath){
|
||||
// 在H5平台下,tempFilePath 为 base64
|
||||
// console.log(tempFilePath)
|
||||
uni.hideLoading();
|
||||
this.$emit('crop', { tempFilePath });
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
$uni-bg-color-grey:#666;
|
||||
$uni-color-primary:#333;
|
||||
$uni-spacing-row-lg:20rpx;
|
||||
.image-cropper {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #000;
|
||||
.img-canvas {
|
||||
position: absolute !important;
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
.pic-preview {
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
position: relative;
|
||||
|
||||
.crop-mask-block {
|
||||
background-color: rgba(51, 51, 51, 0.8);
|
||||
z-index: 2;
|
||||
position: fixed;
|
||||
box-sizing: border-box;
|
||||
pointer-events: none;
|
||||
}
|
||||
.crop-circle-box {
|
||||
position: fixed;
|
||||
box-sizing: border-box;
|
||||
z-index: 2;
|
||||
pointer-events: none;
|
||||
overflow: hidden;
|
||||
.crop-circle {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
.crop-image {
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border-radius: 0 !important;
|
||||
display: block !important;
|
||||
backface-visibility: hidden;
|
||||
}
|
||||
.crop-border {
|
||||
position: fixed;
|
||||
border: 1px solid #fff;
|
||||
box-sizing: border-box;
|
||||
z-index: 3;
|
||||
pointer-events: none;
|
||||
}
|
||||
.crop-grid {
|
||||
position: fixed;
|
||||
z-index: 3;
|
||||
border-style: dashed;
|
||||
border-color: #fff;
|
||||
pointer-events: none;
|
||||
opacity: 0.5;
|
||||
}
|
||||
.crop-angle {
|
||||
position: fixed;
|
||||
z-index: 3;
|
||||
border-style: solid;
|
||||
border-color: #fff;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.fixed-bottom {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 99;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
background-color: $uni-bg-color-grey;
|
||||
|
||||
.action-bar {
|
||||
position: absolute;
|
||||
top: -90rpx;
|
||||
left: 10rpx;
|
||||
display: flex;
|
||||
.rotate-icon {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAAXNSR0IArs4c6QAABCFJREFUaEPtml3IpVMUx3//ko/ChTIyiGFSMyhllI8bc4F85yuNC2FCqLmQC1+FZORiEkUMNW7UjKjJULgxV+NzSkxDhEkZgwsyigv119J63p7zvOc8z37OmXdOb51dz82711r7/99r7bXXXucVi3xokeNnRqCvB20fDmwAlgK/5bcD+FTSr33tHXQP2H4MeHQE0A+B5yRtLiUyDQJrgVc6AAaBpyV93kXkoBMIQLbfBS5NcK8BRwDXNcD+AdwnaVMbiWkRCPBBohpxHuK7M7865sclRdgNHVMhkF6IMIpwirFEUhzo8M7lwIvASTXEqyVtH8ZgagQSbOzsDknv18HZXpHn5IL8+94IOUm7miSmSqAttjPdbgGuTrnNktYsGgLpoYuAD2qg1zRTbG8P2D4SOC6/Q7vSHPALsE/S7wWy80RsPw/ckxMfSTq/LtRJwPbxwF3ASiCUTxwHCPAnEBfVF8AWSTtL7Ng+LfWOTfmlkn6udFsJ5K15R6a4kvX6yGyUFBvTOWzHXXFzCt4g6c1OArYj9iIGh43YgR+BvztXh1PSa4cMkd0jaVmXDduPAE+k3HpJD7cSGFKvfAc8FQUX8IOk/V2L1udtB/hTgdOBW4Aba/M7Ja1qs2f7euCNlHlZUlx4/495IWQ7Jl+qGbxX0gt9AHfJ2o6zFBVoNVrDKe+F3Sm8VdK1bQQ+A85JgXckXdkFaJx527cC9TpnVdvBtl3h2iapuhsGPdBw1b9xnUvaNw7AEh3bnwDnpuwGSfeP0rN9NvAMELXRXFkxEEK2nwQeSiOtRVQJwC4Z29cAW1Nuu6TVXTrN+SaBt4ErUug2Sa/2NdhH3vZy4NvU2S/p6D768w5xI3WOrAD7LtISFpGdIhVXKfaYvjd20wP13L9M0p4DBbaFRKToSLExVkr6qs+aIwlI6iwz+izUQqC+ab29PiMwqRcmPXczD8w8MFj1zg7xXEqbpdHCw7FgWSjafZL+KcQxtpjteCeflwYulFR/J3TabSslVkj6utPChAK2f6q9uZdLitKieLQRuExSvX9ZbLRUMFs09efpUZL+KtUfVo1GW/umNHC3pOhRLtiwfSbwZS6wV9IJfRdreuBBYH0a2STp9r4G+8jbXgc8mzoDT8VSO00ClwDv1ZR7XyylC4ec7ejaLUmdsV6Aw7oSbwFXpdFdks7qA6pU1na0aR6owgeIR/1cx63UzjAC0YXYVjMQHlkn6ZtSo21ytuPZGKFagQ/xsXZ/3iGuFrYdjafXG0DiQMeBi47c9/GV3BO247UV38n5o0UAP6xmu7jFOGxjRr66On5NPBDOCBsDTapxjHY1dyOcolNXnYlx1himE53p2PmNkxosevfavhg4Izt2k7TXPwZ2S6p6QZPin/2rwcQ7OKmBohCadJGF1P8PG6aaQBKVX/8AAAAASUVORK5CYII=');
|
||||
background-size: 60% 60%;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
&.is-reverse {
|
||||
transform: rotateY(180deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.rechoose {
|
||||
color: $uni-color-primary;
|
||||
padding: 0 $uni-spacing-row-lg;
|
||||
line-height: 100rpx;
|
||||
}
|
||||
|
||||
.choose-btn {
|
||||
color: $uni-color-primary;
|
||||
text-align: center;
|
||||
line-height: 100rpx;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.button {
|
||||
margin: auto $uni-spacing-row-lg auto auto;
|
||||
background-color: $uni-color-primary;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.safe-area-inset-bottom {
|
||||
padding-bottom: 0;
|
||||
padding-bottom: constant(safe-area-inset-bottom); // 兼容 IOS<11.2
|
||||
padding-bottom: env(safe-area-inset-bottom); // 兼容 IOS>=11.2
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,727 @@
|
||||
/**
|
||||
* 图片编辑器-手势监听
|
||||
* 1. wxs 暂不支持 es6 语法
|
||||
* 2. 支持编译到微信小程序、QQ小程序、app-vue、H5上(uni-app 2.2.5及以上版本)
|
||||
*/
|
||||
/** 图片偏移量 */
|
||||
var offset = { x: 0, y: 0 };
|
||||
/** 图片缩放比例 */
|
||||
var scale = 1;
|
||||
/** 图片最小缩放比例 */
|
||||
var minScale = 1;
|
||||
/** 图片旋转角度 */
|
||||
var rotate = 0;
|
||||
/** 触摸点 */
|
||||
var touches = [];
|
||||
/** 图片布局信息 */
|
||||
var img = {};
|
||||
/** 系统信息 */
|
||||
var sys = {};
|
||||
/** 裁剪区域布局信息 */
|
||||
var area = {};
|
||||
/** 触摸行为类型 */
|
||||
var touchType = '';
|
||||
/** 操作角的位置 */
|
||||
var activeAngle = 0;
|
||||
/** 裁剪区域布局信息偏移量 */
|
||||
var areaOffset = { left: 0, right: 0, top: 0, bottom: 0 };
|
||||
/** 容错值 */
|
||||
var fault = 0.000001;
|
||||
/**
|
||||
* 获取a、b两数中的最小正数
|
||||
* @param a
|
||||
* @param b
|
||||
*/
|
||||
function minimum(a, b) {
|
||||
if (a > 0 && b < 0) return a;
|
||||
if (a < 0 && b > 0) return b;
|
||||
if (a > 0 && b > 0) return Math.min(a, b);
|
||||
return 0;
|
||||
}
|
||||
/**
|
||||
* 在容错访问内获取n近似值
|
||||
* @param n
|
||||
*/
|
||||
function num(n) {
|
||||
var m = parseFloat((n).toFixed(6));
|
||||
return m === fault || m === -fault ? 0 : m;
|
||||
}
|
||||
/**
|
||||
* 比较a值在容错值范围内是否等于b值
|
||||
* @param a
|
||||
* @param b
|
||||
*/
|
||||
function equalsByFault(a, b) {
|
||||
return Math.abs(a - b) <= fault;
|
||||
}
|
||||
/**
|
||||
* 比较a值在容错值范围内是否小于b值
|
||||
* @param a
|
||||
* @param b
|
||||
*/
|
||||
function lessThanByFault(a, b) {
|
||||
var c = a - b;
|
||||
return c < 0 ? c < -fault : c < fault;
|
||||
}
|
||||
/**
|
||||
* 验证并获取有效最大值
|
||||
* @param v
|
||||
* @param max
|
||||
* @param isInclude
|
||||
* @param x
|
||||
* @param y
|
||||
* @param rate
|
||||
* @returns
|
||||
*/
|
||||
function validMax(v, max, isInclude, x, y, rate) {
|
||||
if(typeof max === 'number') {
|
||||
if(isInclude && equalsByFault(max, y)) { // 宽高不等时,x轴用y轴值要做等比例转换
|
||||
var n = num(max * rate);
|
||||
if (n <= x) return n; // 转化后值在x轴最大值范围内
|
||||
return x; // 转化后值超出x轴最大值范围则用最大值
|
||||
}
|
||||
return max;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
/**
|
||||
* 计算两点间距
|
||||
* @param {Object} touches 触摸点信息
|
||||
*/
|
||||
function getDistanceByTouches(touches) {
|
||||
// 根据勾股定理求两点间距离
|
||||
var a = touches[1].pageX - touches[0].pageX;
|
||||
var b = touches[1].pageY - touches[0].pageY;
|
||||
var c = Math.sqrt(Math.pow(a, 2) + Math.pow(b, 2));
|
||||
// 求两点间的中点坐标
|
||||
// 1. a、b可能为负值
|
||||
// 2. 在求a、b时,如用touches[1]减touches[0],则求中点坐标也得用touches[1]减a/2、b/2
|
||||
// 3. 同理,在求a、b时,也可用touches[0]减touches[1],则求中点坐标也得用touches[0]减a/2、b/2
|
||||
var x = touches[1].pageX - a / 2;
|
||||
var y = touches[1].pageY - b / 2;
|
||||
return { c, x, y };
|
||||
};
|
||||
/**
|
||||
* 修正取值
|
||||
* @param {Object} a
|
||||
* @param {Object} b
|
||||
* @param {Object} c
|
||||
* @param {Object} reverse 是否反向
|
||||
*/
|
||||
function correctValue(a, b, c, reverse) {
|
||||
return num(reverse ? Math.max(Math.min(a, b), c) : Math.min(Math.max(a, b), c));
|
||||
}
|
||||
|
||||
/**
|
||||
* 旋转90°或270°时检查边界:限制 x、y 拖动范围,禁止滑出边界
|
||||
* @param {Object} e 点坐标
|
||||
* @param {Object} xReverse x是否反向
|
||||
* @param {Object} yReverse y是否反向
|
||||
*/
|
||||
function checkRotateRange(e, xReverse, yReverse) {
|
||||
var o = num((img.height - img.width) / 2); // 宽高差值一半
|
||||
return {
|
||||
x: correctValue(e.x, -img.height + o + area.width + area.left, area.left + o, xReverse),
|
||||
y: correctValue(e.y, -img.width - o + area.height + area.top, area.top - o, yReverse)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查边界:限制 x、y 拖动范围,禁止滑出边界
|
||||
* @param {Object} e 点坐标
|
||||
*/
|
||||
function checkRange(e) {
|
||||
var r = rotate / 90 % 2;
|
||||
if(r === 1) { // 因图片宽高可能不等,翻转 90° 或 270° 后图片宽高需反着计算,且左右和上下边界要根据差值做偏移
|
||||
if (area.width === area.height) {
|
||||
return checkRotateRange(e, img.height < area.height, img.width < area.width);
|
||||
}
|
||||
var isInclude = img.height < area.width && img.width < area.height; // 图片是否包含在裁剪区域内
|
||||
if (img.width < area.height || img.height < area.width) {
|
||||
if (area.width < area.height && img.width < img.height) {
|
||||
return isInclude
|
||||
? checkRotateRange(e, area.width < area.height, area.width < area.height)
|
||||
: checkRotateRange(e, false, true);
|
||||
}
|
||||
if (area.height < area.width && img.height < img.width) {
|
||||
return isInclude
|
||||
? checkRotateRange(e, area.height < area.width, area.height < area.width)
|
||||
: checkRotateRange(e, true, false);
|
||||
}
|
||||
}
|
||||
if (img.height >= area.width && img.width >= area.height) {
|
||||
return checkRotateRange(e, false, false);
|
||||
}
|
||||
if (isInclude) {
|
||||
return area.height < area.width
|
||||
? checkRotateRange(e, true, true)
|
||||
: checkRotateRange(e, area.width < area.height, area.width < area.height);
|
||||
}
|
||||
if (img.height < area.width && !img.width < area.height) {
|
||||
return checkRotateRange(e, true, false);
|
||||
}
|
||||
if (!img.height < area.width && img.width < area.height) {
|
||||
return checkRotateRange(e, false, true);
|
||||
}
|
||||
return checkRotateRange(e, img.height < area.height, img.width < area.width);
|
||||
}
|
||||
return {
|
||||
x: correctValue(e.x, -img.width + area.width + area.left, area.left, img.width < area.width),
|
||||
y: correctValue(e.y, -img.height + area.height + area.top, area.top, img.height < area.height)
|
||||
};
|
||||
};
|
||||
/**
|
||||
* 变更图片布局信息
|
||||
* @param {Object} e 布局信息
|
||||
*/
|
||||
function changeImageRect(e) {
|
||||
offset.x += e.x || 0;
|
||||
offset.y += e.y || 0;
|
||||
var image = e.instance.selectComponent('.crop-image');
|
||||
if(e.check && area.checkRange) { // 检查边界
|
||||
var point = checkRange(offset);
|
||||
if(offset.x !== point.x || offset.y !== point.y) {
|
||||
offset = point;
|
||||
}
|
||||
}
|
||||
// image.setStyle({
|
||||
// width: img.width + 'px',
|
||||
// height: img.height + 'px',
|
||||
// transform: 'translate(' + offset.x + 'px, ' + offset.y + 'px) rotate(' + rotate +'deg)'
|
||||
// });
|
||||
var ox = (img.width - img.oldWidth) / 2;
|
||||
var oy = (img.height - img.oldHeight) / 2;
|
||||
image.setStyle({
|
||||
width: img.oldWidth + 'px',
|
||||
height: img.oldHeight + 'px',
|
||||
transform: (img.gpu ? 'translateZ(0) ' : '') + 'translate(' + (offset.x + ox) + 'px, ' + (offset.y + oy) + 'px) rotate(' + rotate +'deg) scale(' + scale + ')'
|
||||
});
|
||||
|
||||
e.instance.callMethod('dataChange', {
|
||||
width: img.width,
|
||||
height: img.height,
|
||||
x: offset.x,
|
||||
y: offset.y,
|
||||
rotate: rotate
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 变更裁剪区域布局信息
|
||||
* @param {Object} e 布局信息
|
||||
*/
|
||||
function changeAreaRect(e) {
|
||||
// 变更蒙版样式
|
||||
var masks = e.instance.selectAllComponents('.crop-mask-block');
|
||||
var maskStyles = [
|
||||
{
|
||||
left: 0,
|
||||
width: (area.left + areaOffset.left) + 'px',
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
'z-index': area.zIndex + 2
|
||||
},
|
||||
{
|
||||
left: (area.right + areaOffset.right) + 'px',
|
||||
right: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
'z-index': area.zIndex + 2
|
||||
},
|
||||
{
|
||||
left: (area.left + areaOffset.left) + 'px',
|
||||
width: (area.width + areaOffset.right - areaOffset.left) + 'px',
|
||||
top: 0,
|
||||
height: (area.top + areaOffset.top) + 'px',
|
||||
'z-index': area.zIndex + 2
|
||||
},
|
||||
{
|
||||
left: (area.left + areaOffset.left) + 'px',
|
||||
width: (area.width + areaOffset.right - areaOffset.left) + 'px',
|
||||
top: (area.bottom + areaOffset.bottom) + 'px',
|
||||
// height: (area.top - areaOffset.bottom + sys.offsetBottom) + 'px',
|
||||
bottom: 0,
|
||||
'z-index': area.zIndex + 2
|
||||
}
|
||||
];
|
||||
var len = masks.length;
|
||||
for (var i = 0; i < len; i++) {
|
||||
masks[i].setStyle(maskStyles[i]);
|
||||
}
|
||||
|
||||
// 变更边框样式
|
||||
if(area.showBorder) {
|
||||
var border = e.instance.selectComponent('.crop-border');
|
||||
border.setStyle({
|
||||
left: (area.left + areaOffset.left) + 'px',
|
||||
top: (area.top + areaOffset.top) + 'px',
|
||||
width: (area.width + areaOffset.right - areaOffset.left) + 'px',
|
||||
height: (area.height + areaOffset.bottom - areaOffset.top) + 'px',
|
||||
'z-index': area.zIndex + 3
|
||||
});
|
||||
}
|
||||
|
||||
// 变更参考线样式
|
||||
if(area.showGrid) {
|
||||
var grids = e.instance.selectAllComponents('.crop-grid');
|
||||
var gridStyles = [
|
||||
{
|
||||
'border-width': '1px 0 0 0',
|
||||
left: (area.left + areaOffset.left) + 'px',
|
||||
right: (area.right + areaOffset.right) + 'px',
|
||||
top: (area.top + areaOffset.top + (area.height + areaOffset.bottom - areaOffset.top) / 3 - 0.5) + 'px',
|
||||
width: (area.width + areaOffset.right - areaOffset.left) + 'px',
|
||||
'z-index': area.zIndex + 3
|
||||
},
|
||||
{
|
||||
'border-width': '1px 0 0 0',
|
||||
left: (area.left + areaOffset.left) + 'px',
|
||||
right: (area.right + areaOffset.right) + 'px',
|
||||
top: (area.top + areaOffset.top + (area.height + areaOffset.bottom - areaOffset.top) * 2 / 3 - 0.5) + 'px',
|
||||
width: (area.width + areaOffset.right - areaOffset.left) + 'px',
|
||||
'z-index': area.zIndex + 3
|
||||
},
|
||||
{
|
||||
'border-width': '0 1px 0 0',
|
||||
top: (area.top + areaOffset.top) + 'px',
|
||||
bottom: (area.bottom + areaOffset.bottom) + 'px',
|
||||
left: (area.left + areaOffset.left + (area.width + areaOffset.right - areaOffset.left) / 3 - 0.5) + 'px',
|
||||
height: (area.height + areaOffset.bottom - areaOffset.top) + 'px',
|
||||
'z-index': area.zIndex + 3
|
||||
},
|
||||
{
|
||||
'border-width': '0 1px 0 0',
|
||||
top: (area.top + areaOffset.top) + 'px',
|
||||
bottom: (area.bottom + areaOffset.bottom) + 'px',
|
||||
left: (area.left + areaOffset.left + (area.width + areaOffset.right - areaOffset.left) * 2 / 3 - 0.5) + 'px',
|
||||
height: (area.height + areaOffset.bottom - areaOffset.top) + 'px',
|
||||
'z-index': area.zIndex + 3
|
||||
}
|
||||
];
|
||||
var len = grids.length;
|
||||
for (var i = 0; i < len; i++) {
|
||||
grids[i].setStyle(gridStyles[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// 变更四个伸缩角样式
|
||||
if(area.showAngle) {
|
||||
var angles = e.instance.selectAllComponents('.crop-angle');
|
||||
var angleStyles = [
|
||||
{
|
||||
'border-width': area.angleBorderWidth + 'px 0 0 ' + area.angleBorderWidth + 'px',
|
||||
left: (area.left + areaOffset.left - area.angleBorderWidth) + 'px',
|
||||
top: (area.top + areaOffset.top - area.angleBorderWidth) + 'px',
|
||||
'z-index': area.zIndex + 3
|
||||
},
|
||||
{
|
||||
'border-width': area.angleBorderWidth + 'px ' + area.angleBorderWidth + 'px 0 0',
|
||||
left: (area.right + areaOffset.right - area.angleSize) + 'px',
|
||||
top: (area.top + areaOffset.top - area.angleBorderWidth) + 'px',
|
||||
'z-index': area.zIndex + 3
|
||||
},
|
||||
{
|
||||
'border-width': '0 0 ' + area.angleBorderWidth + 'px ' + area.angleBorderWidth + 'px',
|
||||
left: (area.left + areaOffset.left - area.angleBorderWidth) + 'px',
|
||||
top: (area.bottom + areaOffset.bottom - area.angleSize) + 'px',
|
||||
'z-index': area.zIndex + 3
|
||||
},
|
||||
{
|
||||
'border-width': '0 ' + area.angleBorderWidth + 'px ' + area.angleBorderWidth + 'px 0',
|
||||
left: (area.right + areaOffset.right - area.angleSize) + 'px',
|
||||
top: (area.bottom + areaOffset.bottom - area.angleSize) + 'px',
|
||||
'z-index': area.zIndex + 3
|
||||
}
|
||||
];
|
||||
var len = angles.length;
|
||||
for (var i = 0; i < len; i++) {
|
||||
angles[i].setStyle(angleStyles[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// 变更圆角样式
|
||||
if(area.radius > 0) {
|
||||
var circleBox = e.instance.selectComponent('.crop-circle-box');
|
||||
var circle = e.instance.selectComponent('.crop-circle');
|
||||
var radius = area.radius;
|
||||
if(area.width === area.height && area.radius >= area.width / 2) { // 圆形
|
||||
radius = (area.width / 2);
|
||||
} else { // 圆角矩形
|
||||
if(area.width !== area.height) { // 限制圆角半径不能超过短边的一半
|
||||
radius = Math.min(area.width / 2, area.height / 2, radius);
|
||||
}
|
||||
}
|
||||
circleBox.setStyle({
|
||||
left: (area.left + areaOffset.left) + 'px',
|
||||
top: (area.top + areaOffset.top) + 'px',
|
||||
width: (area.width + areaOffset.right - areaOffset.left) + 'px',
|
||||
height: (area.height + areaOffset.bottom - areaOffset.top) + 'px',
|
||||
'z-index': area.zIndex + 2
|
||||
});
|
||||
circle.setStyle({
|
||||
'box-shadow': '0 0 0 ' + Math.max(area.width, area.height) + 'px rgba(51, 51, 51, 0.8)',
|
||||
'border-radius': radius + 'px'
|
||||
});
|
||||
}
|
||||
};
|
||||
/**
|
||||
* 缩放图片
|
||||
* @param {Object} e 布局信息
|
||||
*/
|
||||
function scaleImage(e) {
|
||||
var last = scale;
|
||||
scale = Math.min(Math.max(e.scale + scale, minScale), img.maxScale);
|
||||
if(last !== scale) {
|
||||
img.width = num(img.oldWidth * scale);
|
||||
img.height = num(img.oldHeight * scale);
|
||||
// 参考问题:有一个长4000px、宽4000px的四方形ABCD,A点的坐标固定在(-2000,-2000),
|
||||
// 该四边形上有一个点E,坐标为(-100,-300),将该四方形复制一份并缩小到90%后,
|
||||
// 新四边形的A点坐标为多少时可使新四边形的E点与原四边形的E点重合?
|
||||
// 预期效果:从图中选取某点(参照物)为中心点进行缩放,缩放时无论图像怎么变化,该点位置始终固定不变
|
||||
// 计算方法:以相同起点先计算缩放前后两点间的距离,再加上原图像偏移量即可
|
||||
e.x = num((e.x - offset.x) * (1 - scale / last));
|
||||
e.y = num((e.y - offset.y) * (1 - scale / last));
|
||||
changeImageRect(e);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
/**
|
||||
* 获取触摸点在哪个角
|
||||
* @param {number} x 触摸点x轴坐标
|
||||
* @param {number} y 触摸点y轴坐标
|
||||
* @return {number} 角的位置:0=无;1=左上;2=右上;3=左下;4=右下;
|
||||
*/
|
||||
function getToucheAngle(x, y) {
|
||||
// console.log('getToucheAngle', x, y, JSON.stringify(area))
|
||||
var o = area.angleBorderWidth; // 需扩大触发范围则把 o 值加大即可
|
||||
if(y >= area.top - o && y <= area.top + area.angleSize + o) {
|
||||
if(x >= area.left - o && x <= area.left + area.angleSize + o) {
|
||||
return 1; // 左上角
|
||||
} else if(x >= area.right - area.angleSize - o && x <= area.right + o) {
|
||||
return 2; // 右上角
|
||||
}
|
||||
} else if(y >= area.bottom - area.angleSize - o && y <= area.bottom + o) {
|
||||
if(x >= area.left - o && x <= area.left + area.angleSize + o) {
|
||||
return 3; // 左下角
|
||||
} else if(x >= area.right - area.angleSize - o && x <= area.right + o) {
|
||||
return 4; // 右下角
|
||||
}
|
||||
}
|
||||
return 0; // 无触摸到角
|
||||
};
|
||||
/**
|
||||
* 重置数据
|
||||
*/
|
||||
function resetData() {
|
||||
offset = { x: 0, y: 0 };
|
||||
scale = 1;
|
||||
minScale = img.minScale;
|
||||
rotate = 0;
|
||||
};
|
||||
/**
|
||||
* 顺时针翻转图片90°
|
||||
* @param {Object} e 事件对象
|
||||
* @param {Object} o 组件实例对象
|
||||
*/
|
||||
function rotateImage(e, o, r) {
|
||||
rotate = (rotate + r) % 360;
|
||||
if(img.minScale >= 1 && area.checkRange) {
|
||||
// 因图片宽高可能不等,翻转后图片宽高需足够填满裁剪区域
|
||||
minScale = 1;
|
||||
if(img.width < area.height) {
|
||||
minScale = area.height / img.oldWidth;
|
||||
} else if(img.height < area.width) {
|
||||
minScale = area.width / img.oldHeight;
|
||||
}
|
||||
if(minScale !== 1) {
|
||||
scaleImage({
|
||||
instance: o,
|
||||
scale: minScale - scale,
|
||||
x: sys.windowWidth / 2,
|
||||
y: (sys.windowHeight - sys.offsetBottom) / 2
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 由于拖动画布后会导致图片位置偏移,翻转时的旋转中心点需是图片区域+偏移区域的中心点
|
||||
// 翻转x轴中心点 = (超出裁剪区域右侧的图片宽度 - 超出裁剪区域左侧的图片宽度) / 2
|
||||
// 翻转y轴中心点 = (超出裁剪区域下方的图片宽度 - 超出裁剪区域上方的图片宽度) / 2
|
||||
var ox = ((offset.x + img.width - area.right) - (area.left - offset.x)) / 2;
|
||||
var oy = ((offset.y + img.height - area.bottom) - (area.top - offset.y)) / 2;
|
||||
changeImageRect({
|
||||
instance: o,
|
||||
check: true,
|
||||
x: -ox - oy,
|
||||
y: -oy + ox
|
||||
});
|
||||
};
|
||||
module.exports = {
|
||||
/**
|
||||
* 初始化:观察数据变更
|
||||
* @param {Object} newVal 新数据
|
||||
* @param {Object} oldVal 旧数据
|
||||
* @param {Object} o 组件实例对象
|
||||
*/
|
||||
initObserver: function(newVal, oldVal, o, i) {
|
||||
if(newVal) {
|
||||
img = newVal.img;
|
||||
sys = newVal.sys;
|
||||
area = newVal.area;
|
||||
minScale = img.minScale;
|
||||
resetData();
|
||||
img.src && changeImageRect({
|
||||
instance: o,
|
||||
x: (sys.windowWidth - img.width) / 2,
|
||||
y: (sys.windowHeight - sys.offsetBottom - img.height) / 2
|
||||
});
|
||||
changeAreaRect({
|
||||
instance: o
|
||||
});
|
||||
// console.log('initRect', JSON.stringify(newVal))
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 鼠标滚轮滚动
|
||||
* @param {Object} e 事件对象
|
||||
* @param {Object} o 组件实例对象
|
||||
*/
|
||||
mousewheel: function(e, o) {
|
||||
if(!img.src) return;
|
||||
scaleImage({
|
||||
instance: o,
|
||||
check: true,
|
||||
// 鼠标向上滚动时,deltaY 固定 -100,鼠标向下滚动时,deltaY 固定 100
|
||||
scale: e.detail.deltaY > 0 ? -0.05 : 0.05,
|
||||
x: e.touches[0].pageX,
|
||||
y: e.touches[0].pageY
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 触摸开始
|
||||
* @param {Object} e 事件对象
|
||||
* @param {Object} o 组件实例对象
|
||||
*/
|
||||
touchstart: function(e, o) {
|
||||
if(!img.src) return;
|
||||
touches = e.touches;
|
||||
activeAngle = area.showAngle ? getToucheAngle(touches[0].pageX, touches[0].pageY) : 0;
|
||||
if(touches.length === 1 && activeAngle !== 0) {
|
||||
touchType = 'stretch'; // 伸缩裁剪区域
|
||||
} else {
|
||||
touchType = '';
|
||||
}
|
||||
// console.log('touchstart', JSON.stringify(e), activeAngle)
|
||||
},
|
||||
/**
|
||||
* 触摸移动
|
||||
* @param {Object} e 事件对象
|
||||
* @param {Object} o 组件实例对象
|
||||
*/
|
||||
touchmove: function(e, o) {
|
||||
if(!img.src) return;
|
||||
// console.log('touchmove', JSON.stringify(e), JSON.stringify(o))
|
||||
if(touchType === 'stretch') { // 触摸四个角进行拉伸
|
||||
var point = e.touches[0];
|
||||
var start = touches[0];
|
||||
var x = point.pageX - start.pageX;
|
||||
var y = point.pageY - start.pageY;
|
||||
if(x !== 0 || y !== 0) {
|
||||
var maxX = num(area.width * (1 - area.minScale));
|
||||
var maxY = num(area.height * (1 - area.minScale));
|
||||
// console.log(x, y, maxX, maxY, offset, area)
|
||||
touches[0] = point;
|
||||
var r = rotate / 90 % 2;
|
||||
var m = r === 1 ? num((img.height - img.width) / 2) : 0; // 宽高差值一半
|
||||
var xCompare = r === 1 ? lessThanByFault(img.height, area.width) : lessThanByFault(img.width, area.width);
|
||||
var yCompare = r === 1 ? lessThanByFault(img.width, area.height) : lessThanByFault(img.height, area.height)
|
||||
var isInclude = xCompare && yCompare;
|
||||
var isIntersect = area.checkRange && (xCompare || yCompare); // 图片是否包含在裁剪区域内
|
||||
var isReverse = !isInclude || num((offset.x - area.left) / area.width) <= num((offset.y - area.top) / area.height) || (area.width > area.height && img.width < img.height && r === 1);
|
||||
switch(activeAngle) {
|
||||
case 1: // 左上角
|
||||
x = num(x + areaOffset.left);
|
||||
y = num(y + areaOffset.top);
|
||||
if(x >= 0 && y >= 0) { // 有效滑动
|
||||
var t = num(offset.y + m - area.top);
|
||||
var l = num(offset.x - m - area.left);
|
||||
// && (offset.x + img.width < area.right || offset.y + img.height < area.bottom)
|
||||
var max = isIntersect && ((l >= 0) || (t >= 0))
|
||||
? minimum(t, l)
|
||||
: false;
|
||||
if(x > y && isReverse) { // 以x轴滑动距离为缩放基准
|
||||
maxX = validMax(maxX, max, isInclude, l, t, area.width / area.height);
|
||||
if(x > maxX) x = maxX;
|
||||
y = num(x * area.height / area.width);
|
||||
} else { // 以y轴滑动距离为缩放基准
|
||||
maxY = validMax(maxY, max, isInclude, t, l, area.height / area.width);
|
||||
if(y > maxY) y = maxY;
|
||||
x = num(y * area.width / area.height);
|
||||
}
|
||||
areaOffset.left = x;
|
||||
areaOffset.top = y;
|
||||
}
|
||||
break;
|
||||
case 2: // 右上角
|
||||
x = num(x + areaOffset.right);
|
||||
y = num(y + areaOffset.top);
|
||||
if(x <= 0 && y >= 0) { // 有效滑动
|
||||
var w = (r === 1 ? img.height : img.width);
|
||||
var t = num(offset.y + m - area.top);
|
||||
var l = num(area.right + m - offset.x - w);
|
||||
var max = isIntersect && ((t >= 0) || (l >= 0))
|
||||
? minimum(t, l)
|
||||
: false;
|
||||
// var max = isInclude && ((offset.x > 0 && offset.x + img.width <= area.right) || (offset.y > 0 && offset.y >= area.top))
|
||||
// ? minimum(offset.y - area.top, area.right - offset.x - img.width)
|
||||
// : false;
|
||||
// console.log(offset.x, offset.y, img.width, img.height, area.top, area.right, m, max)
|
||||
// console.log(offset.y + m - area.top, area.right + m - offset.x - w)
|
||||
if(-x > y && isReverse) { // 以x轴滑动距离为缩放基准
|
||||
maxX = validMax(maxX, max, isInclude, l, t, area.width / area.height);
|
||||
if(-x > maxX) x = -maxX;
|
||||
y = num(-x * area.height / area.width);
|
||||
} else { // 以y轴滑动距离为缩放基准
|
||||
maxY = validMax(maxY, max, isInclude, t, l, area.height / area.width);
|
||||
if(y > maxY) y = maxY;
|
||||
x = num(-y * area.width / area.height);
|
||||
}
|
||||
areaOffset.right = x;
|
||||
areaOffset.top = y;
|
||||
}
|
||||
break;
|
||||
case 3: // 左下角
|
||||
x += num(x + areaOffset.left);
|
||||
y += num(y + areaOffset.bottom);
|
||||
if(x >= 0 && y <= 0) { // 有效滑动
|
||||
var w = (r === 1 ? img.width : img.height);
|
||||
var t = num(area.bottom - m - offset.y - w);
|
||||
var l = num(offset.x - m - area.left);
|
||||
var max = isIntersect && ((l >= 0) || (t >= 0))
|
||||
? minimum(t, l)
|
||||
: false;
|
||||
if(x > -y && isReverse) { // 以x轴滑动距离为缩放基准
|
||||
maxX = validMax(maxX, max, isInclude, l, t, area.width / area.height);
|
||||
if(x > maxX) x = maxX;
|
||||
y = num(-x * area.height / area.width);
|
||||
} else { // 以y轴滑动距离为缩放基准
|
||||
maxY = validMax(maxY, max, isInclude, t, l, area.height / area.width);
|
||||
if(-y > maxY) y = -maxY;
|
||||
x = num(-y * area.width / area.height);
|
||||
}
|
||||
areaOffset.left = x;
|
||||
areaOffset.bottom = y;
|
||||
}
|
||||
break;
|
||||
case 4: // 右下角
|
||||
x = num(x + areaOffset.right);
|
||||
y = num(y + areaOffset.bottom);
|
||||
if(x <= 0 && y <= 0) { // 有效滑动
|
||||
var w = (r === 1 ? img.height : img.width);
|
||||
var h = (r === 1 ? img.width : img.height);
|
||||
var t = num(area.bottom - offset.y - h - m);
|
||||
var l = num(area.right + m - offset.x - w);
|
||||
var max = isIntersect && ((l >= 0) || (t >= 0))
|
||||
? minimum(t, l)
|
||||
: false;
|
||||
if(-x > -y && isReverse) { // 以x轴滑动距离为缩放基准
|
||||
maxX = validMax(maxX, max, isInclude, l, t, area.width / area.height);
|
||||
if(-x > maxX) x = -maxX;
|
||||
y = num(x * area.height / area.width);
|
||||
} else { // 以y轴滑动距离为缩放基准
|
||||
maxY = validMax(maxY, max, isInclude, t, l, area.height / area.width);
|
||||
if(-y > maxY) y = -maxY;
|
||||
x = num(y * area.width / area.height);
|
||||
}
|
||||
areaOffset.right = x;
|
||||
areaOffset.bottom = y;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// console.log(x, y, JSON.stringify(areaOffset))
|
||||
changeAreaRect({
|
||||
instance: o,
|
||||
});
|
||||
// this.draw();
|
||||
}
|
||||
} else if (e.touches.length == 2) { // 双点触摸缩放
|
||||
var start = getDistanceByTouches(touches);
|
||||
var end = getDistanceByTouches(e.touches);
|
||||
scaleImage({
|
||||
instance: o,
|
||||
check: !area.bounce,
|
||||
scale: (end.c - start.c) / 100,
|
||||
x: end.x,
|
||||
y: end.y
|
||||
});
|
||||
touchType = 'scale';
|
||||
} else if(touchType === 'scale') {// 从双点触摸变成单点触摸 / 从缩放变成拖动
|
||||
touchType = 'move';
|
||||
} else {
|
||||
changeImageRect({
|
||||
instance: o,
|
||||
check: !area.bounce,
|
||||
x: e.touches[0].pageX - touches[0].pageX,
|
||||
y: e.touches[0].pageY - touches[0].pageY
|
||||
});
|
||||
touchType = 'move';
|
||||
}
|
||||
touches = e.touches;
|
||||
},
|
||||
/**
|
||||
* 触摸结束
|
||||
* @param {Object} e 事件对象
|
||||
* @param {Object} o 组件实例对象
|
||||
*/
|
||||
touchend: function(e, o) {
|
||||
if(!img.src) return;
|
||||
if(touchType === 'stretch') { // 拉伸裁剪区域的四个角缩放
|
||||
// 裁剪区域宽度被缩放到多少
|
||||
var left = areaOffset.left;
|
||||
var right = areaOffset.right;
|
||||
var top = areaOffset.top;
|
||||
var bottom = areaOffset.bottom;
|
||||
var w = area.width + right - left;
|
||||
var h = area.height + bottom - top;
|
||||
// 图像放大倍数
|
||||
var p = scale * (area.width / w) - scale;
|
||||
// 复原裁剪区域
|
||||
areaOffset = { left: 0, right: 0, top: 0, bottom: 0 };
|
||||
changeAreaRect({
|
||||
instance: o,
|
||||
});
|
||||
scaleImage({
|
||||
instance: o,
|
||||
scale: p,
|
||||
x: area.left + left + (1 === activeAngle || 3 === activeAngle ? w : 0),
|
||||
y: area.top + top + (1 === activeAngle || 2 === activeAngle ? h : 0)
|
||||
});
|
||||
} else if (area.bounce) { // 检查边界并矫正,实现拖动到边界时有回弹效果
|
||||
changeImageRect({
|
||||
instance: o,
|
||||
check: true
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 顺时针翻转图片90°
|
||||
* @param {Object} e 事件对象
|
||||
* @param {Object} o 组件实例对象
|
||||
*/
|
||||
rotateImage: function(e, o) {
|
||||
rotateImage(e, o, 90);
|
||||
},
|
||||
rotateImage90: function(e, o) {
|
||||
rotateImage(e, o, 90)
|
||||
},
|
||||
rotateImage270: function(e, o) {
|
||||
rotateImage(e, o, 270)
|
||||
},
|
||||
// 此处只用于对齐其他平台端的样式参数,防止异常,无作用
|
||||
imageStyles: '',
|
||||
maskStylesList: ['', '', '', ''],
|
||||
borderStyles: '',
|
||||
gridStylesList: ['', '', '', ''],
|
||||
angleStylesList: ['', '', '', ''],
|
||||
circleBoxStyles: '',
|
||||
circleStyles: '',
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
v-for="(item, index) in tabList"
|
||||
:key="index"
|
||||
@click="switchTab(index, item)"
|
||||
:class="{ active: currentTab === index }"
|
||||
:class="{ active: 2 === index }"
|
||||
>
|
||||
<!-- 图标容器 -->
|
||||
<view class="icon-container">
|
||||
@@ -33,11 +33,13 @@
|
||||
<!-- <view class="active-indicator" v-if="currentTab === index"></view> -->
|
||||
</view>
|
||||
</view>
|
||||
<GlobalDialog />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, nextTick } from 'vue';
|
||||
import { onShow, onLoad } from "@dcloudio/uni-app";
|
||||
import GlobalDialog from "@/components/GlobalDialog.vue"
|
||||
import home from '@/static/home_nor.png'
|
||||
import homeOn from '@/static/home_sel.png'
|
||||
import classroom from "@/static/classroom.png"
|
||||
@@ -70,7 +72,7 @@
|
||||
activeColor: '#007aff',
|
||||
badge: 0,
|
||||
showRedDot: false,
|
||||
pagePath: '/pages/index/index'
|
||||
pagePath: '/pages/index/index?from=push'
|
||||
},
|
||||
{
|
||||
icon: classroom,
|
||||
@@ -119,35 +121,56 @@
|
||||
if (currentTab.value === index) return;
|
||||
|
||||
// 更新当前tab
|
||||
currentTab.value = index;
|
||||
|
||||
// 清除红点
|
||||
if (item.showRedDot) {
|
||||
item.showRedDot = false;
|
||||
//currentTab.value = index;
|
||||
let msg="home"
|
||||
if(index == 0){
|
||||
msg="home"
|
||||
}else if(index == 1){
|
||||
msg="patientClass"
|
||||
}else if(index == 2){
|
||||
msg="live"
|
||||
}else if(index == 3){
|
||||
msg="education"
|
||||
}else if(index == 4){
|
||||
msg="mine"
|
||||
}
|
||||
|
||||
try{
|
||||
uni.sendNativeEvent('goTabbarPage', {
|
||||
msg: msg
|
||||
},ret => {
|
||||
|
||||
console.log(ret);
|
||||
})
|
||||
}catch(e){
|
||||
console.log(e);
|
||||
}
|
||||
plus.runtime.quit()
|
||||
// 清除红点
|
||||
// if (item.showRedDot) {
|
||||
// item.showRedDot = false;
|
||||
// }
|
||||
|
||||
// 页面跳转
|
||||
uni.switchTab({
|
||||
url: item.pagePath,
|
||||
fail: () => {
|
||||
// 如果页面不存在,使用navigateTo
|
||||
uni.navigateTo({
|
||||
url: item.pagePath,
|
||||
fail: () => {
|
||||
uni.showToast({
|
||||
title: '页面开发中',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
// uni.redirectTo({
|
||||
// url: item.pagePath,
|
||||
// fail: () => {
|
||||
// // 如果页面不存在,使用navigateTo
|
||||
// uni.navigateTo({
|
||||
// url: item.pagePath,
|
||||
// fail: () => {
|
||||
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
|
||||
// 触发父组件事件
|
||||
emit('tabChange', {
|
||||
index,
|
||||
item
|
||||
});
|
||||
// emit('tabChange', {
|
||||
// index,
|
||||
// item
|
||||
// });
|
||||
|
||||
};
|
||||
|
||||
// 设置徽章数量
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
<template>
|
||||
<view class="top-tip" v-if="visible">
|
||||
<view class="title">{{ title }}</view>
|
||||
<view class="content">
|
||||
{{ content }}
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const visible = ref(false)
|
||||
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
content: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
|
||||
const open = () => {
|
||||
visible.value = true
|
||||
}
|
||||
const close = () => {
|
||||
visible.value = false
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
open,
|
||||
close
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.top-tip{
|
||||
position: fixed;
|
||||
top: 180rpx;
|
||||
left: 40rpx;
|
||||
right:40rpx;
|
||||
padding: 20rpx;
|
||||
border-radius: 16rpx;
|
||||
background-color: rgba(0, 0, 0, 0.8);
|
||||
z-index: 99999999999;
|
||||
.title{
|
||||
font-size: 32rpx;
|
||||
color: #fff;
|
||||
text-align: left;
|
||||
|
||||
}
|
||||
.content{
|
||||
font-size: 28rpx;
|
||||
color: #fff;
|
||||
text-align: left;
|
||||
margin-top: 25rpx;
|
||||
line-height: 1.5;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,150 @@
|
||||
<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,
|
||||
showToast: false,
|
||||
})
|
||||
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>
|
||||
@@ -0,0 +1,49 @@
|
||||
// #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: () => {
|
||||
return null
|
||||
},
|
||||
}
|
||||
// #endif
|
||||
@@ -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>
|
||||
+143
@@ -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>
|
||||
@@ -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: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/d684ae40-50be-11eb-8ff1-d5dcf8779628.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>
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"uni-popup.cancel": "取消",
|
||||
"uni-popup.ok": "确定",
|
||||
"uni-popup.placeholder": "请输入",
|
||||
"uni-popup.title": "提示",
|
||||
"uni-popup.shareTitle": "分享到"
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"uni-popup.cancel": "取消",
|
||||
"uni-popup.ok": "確定",
|
||||
"uni-popup.placeholder": "請輸入",
|
||||
"uni-popup.title": "提示",
|
||||
"uni-popup.shareTitle": "分享到"
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// #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: () => {
|
||||
return null
|
||||
},
|
||||
}
|
||||
// #endif
|
||||
@@ -0,0 +1,23 @@
|
||||
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
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,511 @@
|
||||
<template>
|
||||
<view
|
||||
v-if="showPopup"
|
||||
class="uni-popup"
|
||||
:class="[popupstyle, isDesktop ? 'fixforpc-z-index' : '']"
|
||||
>
|
||||
<view @touchstart="touchstart">
|
||||
<UniTransition
|
||||
key="1"
|
||||
v-if="maskShow"
|
||||
name="mask"
|
||||
mode-class="fade"
|
||||
:styles="maskClass"
|
||||
:duration="duration"
|
||||
:show="showTrans"
|
||||
@click="onTap"
|
||||
/>
|
||||
<UniTransition
|
||||
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>
|
||||
</UniTransition>
|
||||
</view>
|
||||
<!-- #ifdef H5 -->
|
||||
<keypress v-if="maskShow" @esc="onTap" />
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// #ifdef H5
|
||||
import keypress from './keypress.js'
|
||||
// #endif
|
||||
import UniTransition from '../../../uni-transition/components/uni-transition/uni-transition.vue'
|
||||
/**
|
||||
* 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: {
|
||||
UniTransition,
|
||||
// #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: 999999;
|
||||
|
||||
/* #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;
|
||||
z-index: 999;
|
||||
|
||||
/* 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>
|
||||
@@ -0,0 +1,15 @@
|
||||
## 1.0.3(2022-01-21)
|
||||
|
||||
- 优化 组件示例
|
||||
|
||||
## 1.0.2(2021-11-22)
|
||||
|
||||
- 修复 / 符号在 vue 不同版本兼容问题引起的报错问题
|
||||
|
||||
## 1.0.1(2021-11-22)
|
||||
|
||||
- 修复 vue3 中 scss 语法兼容问题
|
||||
|
||||
## 1.0.0(2021-11-18)
|
||||
|
||||
- init
|
||||
@@ -0,0 +1 @@
|
||||
@import './styles/index.scss';
|
||||
@@ -0,0 +1,82 @@
|
||||
{
|
||||
"id": "uni-scss",
|
||||
"displayName": "uni-scss 辅助样式",
|
||||
"version": "1.0.3",
|
||||
"description": "uni-sass是uni-ui提供的一套全局样式 ,通过一些简单的类名和sass变量,实现简单的页面布局操作,比如颜色、边距、圆角等。",
|
||||
"keywords": [
|
||||
"uni-scss",
|
||||
"uni-ui",
|
||||
"辅助样式"
|
||||
],
|
||||
"repository": "https://github.com/dcloudio/uni-ui",
|
||||
"engines": {
|
||||
"HBuilderX": "^3.1.0"
|
||||
},
|
||||
"dcloudext": {
|
||||
"category": [
|
||||
"JS SDK",
|
||||
"通用 SDK"
|
||||
],
|
||||
"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": [],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "y",
|
||||
"aliyun": "y"
|
||||
},
|
||||
"client": {
|
||||
"App": {
|
||||
"app-vue": "y",
|
||||
"app-nvue": "u"
|
||||
},
|
||||
"H5-mobile": {
|
||||
"Safari": "y",
|
||||
"Android Browser": "y",
|
||||
"微信浏览器(Android)": "y",
|
||||
"QQ浏览器(Android)": "y"
|
||||
},
|
||||
"H5-pc": {
|
||||
"Chrome": "y",
|
||||
"IE": "y",
|
||||
"Edge": "y",
|
||||
"Firefox": "y",
|
||||
"Safari": "y"
|
||||
},
|
||||
"小程序": {
|
||||
"微信": "y",
|
||||
"阿里": "y",
|
||||
"百度": "y",
|
||||
"字节跳动": "y",
|
||||
"QQ": "y"
|
||||
},
|
||||
"快应用": {
|
||||
"华为": "n",
|
||||
"联盟": "n"
|
||||
},
|
||||
"Vue": {
|
||||
"vue2": "y",
|
||||
"vue3": "y"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
`uni-sass` 是 `uni-ui`提供的一套全局样式 ,通过一些简单的类名和`sass`变量,实现简单的页面布局操作,比如颜色、边距、圆角等。
|
||||
|
||||
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-sass)
|
||||
|
||||
#### 如使用过程中有任何问题,或者您对 uni-ui 有一些好的建议,欢迎加入 uni-ui 交流群:871950839
|
||||
@@ -0,0 +1,7 @@
|
||||
@import './setting/_variables.scss';
|
||||
@import './setting/_border.scss';
|
||||
@import './setting/_color.scss';
|
||||
@import './setting/_space.scss';
|
||||
@import './setting/_radius.scss';
|
||||
@import './setting/_text.scss';
|
||||
@import './setting/_styles.scss';
|
||||
@@ -0,0 +1,3 @@
|
||||
.uni-border {
|
||||
border: 1px $uni-border-1 solid;
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
|
||||
// TODO 暂时不需要 class ,需要用户使用变量实现 ,如果使用类名其实并不推荐
|
||||
// @mixin get-styles($k,$c) {
|
||||
// @if $k == size or $k == weight{
|
||||
// font-#{$k}:#{$c}
|
||||
// }@else{
|
||||
// #{$k}:#{$c}
|
||||
// }
|
||||
// }
|
||||
$uni-ui-color:(
|
||||
// 主色
|
||||
primary: $uni-primary,
|
||||
primary-disable: $uni-primary-disable,
|
||||
primary-light: $uni-primary-light,
|
||||
// 辅助色
|
||||
success: $uni-success,
|
||||
success-disable: $uni-success-disable,
|
||||
success-light: $uni-success-light,
|
||||
warning: $uni-warning,
|
||||
warning-disable: $uni-warning-disable,
|
||||
warning-light: $uni-warning-light,
|
||||
error: $uni-error,
|
||||
error-disable: $uni-error-disable,
|
||||
error-light: $uni-error-light,
|
||||
info: $uni-info,
|
||||
info-disable: $uni-info-disable,
|
||||
info-light: $uni-info-light,
|
||||
// 中性色
|
||||
main-color: $uni-main-color,
|
||||
base-color: $uni-base-color,
|
||||
secondary-color: $uni-secondary-color,
|
||||
extra-color: $uni-extra-color,
|
||||
// 背景色
|
||||
bg-color: $uni-bg-color,
|
||||
// 边框颜色
|
||||
border-1: $uni-border-1,
|
||||
border-2: $uni-border-2,
|
||||
border-3: $uni-border-3,
|
||||
border-4: $uni-border-4,
|
||||
// 黑色
|
||||
black:$uni-black,
|
||||
// 白色
|
||||
white:$uni-white,
|
||||
// 透明
|
||||
transparent:$uni-transparent
|
||||
) !default;
|
||||
@each $key, $child in $uni-ui-color {
|
||||
.uni-#{"" + $key} {
|
||||
color: $child;
|
||||
}
|
||||
.uni-#{"" + $key}-bg {
|
||||
background-color: $child;
|
||||
}
|
||||
}
|
||||
.uni-shadow-sm {
|
||||
box-shadow: $uni-shadow-sm;
|
||||
}
|
||||
.uni-shadow-base {
|
||||
box-shadow: $uni-shadow-base;
|
||||
}
|
||||
.uni-shadow-lg {
|
||||
box-shadow: $uni-shadow-lg;
|
||||
}
|
||||
.uni-mask {
|
||||
background-color:$uni-mask;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
@mixin radius($r,$d:null ,$important: false){
|
||||
$radius-value:map-get($uni-radius, $r) if($important, !important, null);
|
||||
// Key exists within the $uni-radius variable
|
||||
@if (map-has-key($uni-radius, $r) and $d){
|
||||
@if $d == t {
|
||||
border-top-left-radius:$radius-value;
|
||||
border-top-right-radius:$radius-value;
|
||||
}@else if $d == r {
|
||||
border-top-right-radius:$radius-value;
|
||||
border-bottom-right-radius:$radius-value;
|
||||
}@else if $d == b {
|
||||
border-bottom-left-radius:$radius-value;
|
||||
border-bottom-right-radius:$radius-value;
|
||||
}@else if $d == l {
|
||||
border-top-left-radius:$radius-value;
|
||||
border-bottom-left-radius:$radius-value;
|
||||
}@else if $d == tl {
|
||||
border-top-left-radius:$radius-value;
|
||||
}@else if $d == tr {
|
||||
border-top-right-radius:$radius-value;
|
||||
}@else if $d == br {
|
||||
border-bottom-right-radius:$radius-value;
|
||||
}@else if $d == bl {
|
||||
border-bottom-left-radius:$radius-value;
|
||||
}
|
||||
}@else{
|
||||
border-radius:$radius-value;
|
||||
}
|
||||
}
|
||||
|
||||
@each $key, $child in $uni-radius {
|
||||
@if($key){
|
||||
.uni-radius-#{"" + $key} {
|
||||
@include radius($key)
|
||||
}
|
||||
}@else{
|
||||
.uni-radius {
|
||||
@include radius($key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@each $direction in t, r, b, l,tl, tr, br, bl {
|
||||
@each $key, $child in $uni-radius {
|
||||
@if($key){
|
||||
.uni-radius-#{"" + $direction}-#{"" + $key} {
|
||||
@include radius($key,$direction,false)
|
||||
}
|
||||
}@else{
|
||||
.uni-radius-#{$direction} {
|
||||
@include radius($key,$direction,false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
|
||||
@mixin fn($space,$direction,$size,$n) {
|
||||
@if $n {
|
||||
#{$space}-#{$direction}: #{$size*$uni-space-root}px
|
||||
} @else {
|
||||
#{$space}-#{$direction}: #{-$size*$uni-space-root}px
|
||||
}
|
||||
}
|
||||
@mixin get-styles($direction,$i,$space,$n){
|
||||
@if $direction == t {
|
||||
@include fn($space, top,$i,$n);
|
||||
}
|
||||
@if $direction == r {
|
||||
@include fn($space, right,$i,$n);
|
||||
}
|
||||
@if $direction == b {
|
||||
@include fn($space, bottom,$i,$n);
|
||||
}
|
||||
@if $direction == l {
|
||||
@include fn($space, left,$i,$n);
|
||||
}
|
||||
@if $direction == x {
|
||||
@include fn($space, left,$i,$n);
|
||||
@include fn($space, right,$i,$n);
|
||||
}
|
||||
@if $direction == y {
|
||||
@include fn($space, top,$i,$n);
|
||||
@include fn($space, bottom,$i,$n);
|
||||
}
|
||||
@if $direction == a {
|
||||
@if $n {
|
||||
#{$space}:#{$i*$uni-space-root}px;
|
||||
} @else {
|
||||
#{$space}:#{-$i*$uni-space-root}px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@each $orientation in m,p {
|
||||
$space: margin;
|
||||
@if $orientation == m {
|
||||
$space: margin;
|
||||
} @else {
|
||||
$space: padding;
|
||||
}
|
||||
@for $i from 0 through 16 {
|
||||
@each $direction in t, r, b, l, x, y, a {
|
||||
.uni-#{$orientation}#{$direction}-#{$i} {
|
||||
@include get-styles($direction,$i,$space,true);
|
||||
}
|
||||
.uni-#{$orientation}#{$direction}-n#{$i} {
|
||||
@include get-styles($direction,$i,$space,false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
/* #ifndef APP-NVUE */
|
||||
|
||||
$-color-white:#fff;
|
||||
$-color-black:#000;
|
||||
@mixin base-style($color) {
|
||||
color: #fff;
|
||||
background-color: $color;
|
||||
border-color: mix($-color-black, $color, 8%);
|
||||
&:not([hover-class]):active {
|
||||
background: mix($-color-black, $color, 10%);
|
||||
border-color: mix($-color-black, $color, 20%);
|
||||
color: $-color-white;
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
@mixin is-color($color) {
|
||||
@include base-style($color);
|
||||
&[loading] {
|
||||
@include base-style($color);
|
||||
&::before {
|
||||
margin-right:5px;
|
||||
}
|
||||
}
|
||||
&[disabled] {
|
||||
&,
|
||||
&[loading],
|
||||
&:not([hover-class]):active {
|
||||
color: $-color-white;
|
||||
border-color: mix(darken($color,10%), $-color-white);
|
||||
background-color: mix($color, $-color-white);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@mixin base-plain-style($color) {
|
||||
color:$color;
|
||||
background-color: mix($-color-white, $color, 90%);
|
||||
border-color: mix($-color-white, $color, 70%);
|
||||
&:not([hover-class]):active {
|
||||
background: mix($-color-white, $color, 80%);
|
||||
color: $color;
|
||||
outline: none;
|
||||
border-color: mix($-color-white, $color, 50%);
|
||||
}
|
||||
}
|
||||
@mixin is-plain($color){
|
||||
&[plain] {
|
||||
@include base-plain-style($color);
|
||||
&[loading] {
|
||||
@include base-plain-style($color);
|
||||
&::before {
|
||||
margin-right:5px;
|
||||
}
|
||||
}
|
||||
&[disabled] {
|
||||
&,
|
||||
&:active {
|
||||
color: mix($-color-white, $color, 40%);
|
||||
background-color: mix($-color-white, $color, 90%);
|
||||
border-color: mix($-color-white, $color, 80%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.uni-btn {
|
||||
margin: 5px;
|
||||
color: #393939;
|
||||
border:1px solid #ccc;
|
||||
font-size: 16px;
|
||||
font-weight: 200;
|
||||
background-color: #F9F9F9;
|
||||
// TODO 暂时处理边框隐藏一边的问题
|
||||
overflow: visible;
|
||||
&::after{
|
||||
border: none;
|
||||
}
|
||||
|
||||
&:not([type]),&[type=default] {
|
||||
color: #999;
|
||||
&[loading] {
|
||||
background: none;
|
||||
&::before {
|
||||
margin-right:5px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
&[disabled]{
|
||||
color: mix($-color-white, #999, 60%);
|
||||
&,
|
||||
&[loading],
|
||||
&:active {
|
||||
color: mix($-color-white, #999, 60%);
|
||||
background-color: mix($-color-white,$-color-black , 98%);
|
||||
border-color: mix($-color-white, #999, 85%);
|
||||
}
|
||||
}
|
||||
|
||||
&[plain] {
|
||||
color: #999;
|
||||
background: none;
|
||||
border-color: $uni-border-1;
|
||||
&:not([hover-class]):active {
|
||||
background: none;
|
||||
color: mix($-color-white, $-color-black, 80%);
|
||||
border-color: mix($-color-white, $-color-black, 90%);
|
||||
outline: none;
|
||||
}
|
||||
&[disabled]{
|
||||
&,
|
||||
&[loading],
|
||||
&:active {
|
||||
background: none;
|
||||
color: mix($-color-white, #999, 60%);
|
||||
border-color: mix($-color-white, #999, 85%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:not([hover-class]):active {
|
||||
color: mix($-color-white, $-color-black, 50%);
|
||||
}
|
||||
|
||||
&[size=mini] {
|
||||
font-size: 16px;
|
||||
font-weight: 200;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
&.uni-btn-small {
|
||||
font-size: 14px;
|
||||
}
|
||||
&.uni-btn-mini {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
&.uni-btn-radius {
|
||||
border-radius: 999px;
|
||||
}
|
||||
&[type=primary] {
|
||||
@include is-color($uni-primary);
|
||||
@include is-plain($uni-primary)
|
||||
}
|
||||
&[type=success] {
|
||||
@include is-color($uni-success);
|
||||
@include is-plain($uni-success)
|
||||
}
|
||||
&[type=error] {
|
||||
@include is-color($uni-error);
|
||||
@include is-plain($uni-error)
|
||||
}
|
||||
&[type=warning] {
|
||||
@include is-color($uni-warning);
|
||||
@include is-plain($uni-warning)
|
||||
}
|
||||
&[type=info] {
|
||||
@include is-color($uni-info);
|
||||
@include is-plain($uni-info)
|
||||
}
|
||||
}
|
||||
/* #endif */
|
||||
@@ -0,0 +1,24 @@
|
||||
@mixin get-styles($k,$c) {
|
||||
@if $k == size or $k == weight{
|
||||
font-#{$k}:#{$c}
|
||||
}@else{
|
||||
#{$k}:#{$c}
|
||||
}
|
||||
}
|
||||
|
||||
@each $key, $child in $uni-headings {
|
||||
/* #ifndef APP-NVUE */
|
||||
.uni-#{$key} {
|
||||
@each $k, $c in $child {
|
||||
@include get-styles($k,$c)
|
||||
}
|
||||
}
|
||||
/* #endif */
|
||||
/* #ifdef APP-NVUE */
|
||||
.container .uni-#{$key} {
|
||||
@each $k, $c in $child {
|
||||
@include get-styles($k,$c)
|
||||
}
|
||||
}
|
||||
/* #endif */
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
// @use "sass:math";
|
||||
@import '../tools/functions.scss';
|
||||
// 间距基础倍数
|
||||
$uni-space-root: 2 !default;
|
||||
// 边框半径默认值
|
||||
$uni-radius-root:5px !default;
|
||||
$uni-radius: () !default;
|
||||
// 边框半径断点
|
||||
$uni-radius: map-deep-merge(
|
||||
(
|
||||
0: 0,
|
||||
// TODO 当前版本暂时不支持 sm 属性
|
||||
// 'sm': math.div($uni-radius-root, 2),
|
||||
null: $uni-radius-root,
|
||||
'lg': $uni-radius-root * 2,
|
||||
'xl': $uni-radius-root * 6,
|
||||
'pill': 9999px,
|
||||
'circle': 50%
|
||||
),
|
||||
$uni-radius
|
||||
);
|
||||
// 字体家族
|
||||
$body-font-family: 'Roboto', sans-serif !default;
|
||||
// 文本
|
||||
$heading-font-family: $body-font-family !default;
|
||||
$uni-headings: () !default;
|
||||
$letterSpacing: -0.01562em;
|
||||
$uni-headings: map-deep-merge(
|
||||
(
|
||||
'h1': (
|
||||
size: 32px,
|
||||
weight: 300,
|
||||
line-height: 50px,
|
||||
// letter-spacing:-0.01562em
|
||||
),
|
||||
'h2': (
|
||||
size: 28px,
|
||||
weight: 300,
|
||||
line-height: 40px,
|
||||
// letter-spacing: -0.00833em
|
||||
),
|
||||
'h3': (
|
||||
size: 24px,
|
||||
weight: 400,
|
||||
line-height: 32px,
|
||||
// letter-spacing: normal
|
||||
),
|
||||
'h4': (
|
||||
size: 20px,
|
||||
weight: 400,
|
||||
line-height: 30px,
|
||||
// letter-spacing: 0.00735em
|
||||
),
|
||||
'h5': (
|
||||
size: 16px,
|
||||
weight: 400,
|
||||
line-height: 24px,
|
||||
// letter-spacing: normal
|
||||
),
|
||||
'h6': (
|
||||
size: 14px,
|
||||
weight: 500,
|
||||
line-height: 18px,
|
||||
// letter-spacing: 0.0125em
|
||||
),
|
||||
'subtitle': (
|
||||
size: 12px,
|
||||
weight: 400,
|
||||
line-height: 20px,
|
||||
// letter-spacing: 0.00937em
|
||||
),
|
||||
'body': (
|
||||
font-size: 14px,
|
||||
font-weight: 400,
|
||||
line-height: 22px,
|
||||
// letter-spacing: 0.03125em
|
||||
),
|
||||
'caption': (
|
||||
'size': 12px,
|
||||
'weight': 400,
|
||||
'line-height': 20px,
|
||||
// 'letter-spacing': 0.03333em,
|
||||
// 'text-transform': false
|
||||
)
|
||||
),
|
||||
$uni-headings
|
||||
);
|
||||
|
||||
|
||||
|
||||
// 主色
|
||||
$uni-primary: #2979ff !default;
|
||||
$uni-primary-disable:lighten($uni-primary,20%) !default;
|
||||
$uni-primary-light: lighten($uni-primary,25%) !default;
|
||||
|
||||
// 辅助色
|
||||
// 除了主色外的场景色,需要在不同的场景中使用(例如危险色表示危险的操作)。
|
||||
$uni-success: #18bc37 !default;
|
||||
$uni-success-disable:lighten($uni-success,20%) !default;
|
||||
$uni-success-light: lighten($uni-success,25%) !default;
|
||||
|
||||
$uni-warning: #f3a73f !default;
|
||||
$uni-warning-disable:lighten($uni-warning,20%) !default;
|
||||
$uni-warning-light: lighten($uni-warning,25%) !default;
|
||||
|
||||
$uni-error: #e43d33 !default;
|
||||
$uni-error-disable:lighten($uni-error,20%) !default;
|
||||
$uni-error-light: lighten($uni-error,25%) !default;
|
||||
|
||||
$uni-info: #8f939c !default;
|
||||
$uni-info-disable:lighten($uni-info,20%) !default;
|
||||
$uni-info-light: lighten($uni-info,25%) !default;
|
||||
|
||||
// 中性色
|
||||
// 中性色用于文本、背景和边框颜色。通过运用不同的中性色,来表现层次结构。
|
||||
$uni-main-color: #3a3a3a !default; // 主要文字
|
||||
$uni-base-color: #6a6a6a !default; // 常规文字
|
||||
$uni-secondary-color: #909399 !default; // 次要文字
|
||||
$uni-extra-color: #c7c7c7 !default; // 辅助说明
|
||||
|
||||
// 边框颜色
|
||||
$uni-border-1: #F0F0F0 !default;
|
||||
$uni-border-2: #EDEDED !default;
|
||||
$uni-border-3: #DCDCDC !default;
|
||||
$uni-border-4: #B9B9B9 !default;
|
||||
|
||||
// 常规色
|
||||
$uni-black: #000000 !default;
|
||||
$uni-white: #ffffff !default;
|
||||
$uni-transparent: rgba($color: #000000, $alpha: 0) !default;
|
||||
|
||||
// 背景色
|
||||
$uni-bg-color: #f7f7f7 !default;
|
||||
|
||||
/* 水平间距 */
|
||||
$uni-spacing-sm: 8px !default;
|
||||
$uni-spacing-base: 15px !default;
|
||||
$uni-spacing-lg: 30px !default;
|
||||
|
||||
// 阴影
|
||||
$uni-shadow-sm:0 0 5px rgba($color: #d8d8d8, $alpha: 0.5) !default;
|
||||
$uni-shadow-base:0 1px 8px 1px rgba($color: #a5a5a5, $alpha: 0.2) !default;
|
||||
$uni-shadow-lg:0px 1px 10px 2px rgba($color: #a5a4a4, $alpha: 0.5) !default;
|
||||
|
||||
// 蒙版
|
||||
$uni-mask: rgba($color: #000000, $alpha: 0.4) !default;
|
||||
@@ -0,0 +1,19 @@
|
||||
// 合并 map
|
||||
@function map-deep-merge($parent-map, $child-map){
|
||||
$result: $parent-map;
|
||||
@each $key, $child in $child-map {
|
||||
$parent-has-key: map-has-key($result, $key);
|
||||
$parent-value: map-get($result, $key);
|
||||
$parent-type: type-of($parent-value);
|
||||
$child-type: type-of($child);
|
||||
$parent-is-map: $parent-type == map;
|
||||
$child-is-map: $child-type == map;
|
||||
|
||||
@if (not $parent-has-key) or ($parent-type != $child-type) or (not ($parent-is-map and $child-is-map)){
|
||||
$result: map-merge($result, ( $key: $child ));
|
||||
}@else {
|
||||
$result: map-merge($result, ( $key: map-deep-merge($parent-value, $child) ));
|
||||
}
|
||||
}
|
||||
@return $result;
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
// 间距基础倍数
|
||||
$uni-space-root: 2;
|
||||
// 边框半径默认值
|
||||
$uni-radius-root:5px;
|
||||
// 主色
|
||||
$uni-primary: #2979ff;
|
||||
// 辅助色
|
||||
$uni-success: #4cd964;
|
||||
// 警告色
|
||||
$uni-warning: #f0ad4e;
|
||||
// 错误色
|
||||
$uni-error: #dd524d;
|
||||
// 描述色
|
||||
$uni-info: #909399;
|
||||
// 中性色
|
||||
$uni-main-color: #303133;
|
||||
$uni-base-color: #606266;
|
||||
$uni-secondary-color: #909399;
|
||||
$uni-extra-color: #C0C4CC;
|
||||
// 背景色
|
||||
$uni-bg-color: #f5f5f5;
|
||||
// 边框颜色
|
||||
$uni-border-1: #DCDFE6;
|
||||
$uni-border-2: #E4E7ED;
|
||||
$uni-border-3: #EBEEF5;
|
||||
$uni-border-4: #F2F6FC;
|
||||
|
||||
// 常规色
|
||||
$uni-black: #000000;
|
||||
$uni-white: #ffffff;
|
||||
$uni-transparent: rgba($color: #000000, $alpha: 0);
|
||||
@@ -0,0 +1,62 @@
|
||||
@import './styles/setting/_variables.scss';
|
||||
// 间距基础倍数
|
||||
$uni-space-root: 2;
|
||||
// 边框半径默认值
|
||||
$uni-radius-root:5px;
|
||||
|
||||
// 主色
|
||||
$uni-primary: #2979ff;
|
||||
$uni-primary-disable:mix(#fff,$uni-primary,50%);
|
||||
$uni-primary-light: mix(#fff,$uni-primary,80%);
|
||||
|
||||
// 辅助色
|
||||
// 除了主色外的场景色,需要在不同的场景中使用(例如危险色表示危险的操作)。
|
||||
$uni-success: #18bc37;
|
||||
$uni-success-disable:mix(#fff,$uni-success,50%);
|
||||
$uni-success-light: mix(#fff,$uni-success,80%);
|
||||
|
||||
$uni-warning: #f3a73f;
|
||||
$uni-warning-disable:mix(#fff,$uni-warning,50%);
|
||||
$uni-warning-light: mix(#fff,$uni-warning,80%);
|
||||
|
||||
$uni-error: #e43d33;
|
||||
$uni-error-disable:mix(#fff,$uni-error,50%);
|
||||
$uni-error-light: mix(#fff,$uni-error,80%);
|
||||
|
||||
$uni-info: #8f939c;
|
||||
$uni-info-disable:mix(#fff,$uni-info,50%);
|
||||
$uni-info-light: mix(#fff,$uni-info,80%);
|
||||
|
||||
// 中性色
|
||||
// 中性色用于文本、背景和边框颜色。通过运用不同的中性色,来表现层次结构。
|
||||
$uni-main-color: #3a3a3a; // 主要文字
|
||||
$uni-base-color: #6a6a6a; // 常规文字
|
||||
$uni-secondary-color: #909399; // 次要文字
|
||||
$uni-extra-color: #c7c7c7; // 辅助说明
|
||||
|
||||
// 边框颜色
|
||||
$uni-border-1: #F0F0F0;
|
||||
$uni-border-2: #EDEDED;
|
||||
$uni-border-3: #DCDCDC;
|
||||
$uni-border-4: #B9B9B9;
|
||||
|
||||
// 常规色
|
||||
$uni-black: #000000;
|
||||
$uni-white: #ffffff;
|
||||
$uni-transparent: rgba($color: #000000, $alpha: 0);
|
||||
|
||||
// 背景色
|
||||
$uni-bg-color: #f7f7f7;
|
||||
|
||||
/* 水平间距 */
|
||||
$uni-spacing-sm: 8px;
|
||||
$uni-spacing-base: 15px;
|
||||
$uni-spacing-lg: 30px;
|
||||
|
||||
// 阴影
|
||||
$uni-shadow-sm:0 0 5px rgba($color: #d8d8d8, $alpha: 0.5);
|
||||
$uni-shadow-base:0 1px 8px 1px rgba($color: #a5a5a5, $alpha: 0.2);
|
||||
$uni-shadow-lg:0px 1px 10px 2px rgba($color: #a5a4a4, $alpha: 0.5);
|
||||
|
||||
// 蒙版
|
||||
$uni-mask: rgba($color: #000000, $alpha: 0.4);
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
// 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)
|
||||
}
|
||||
+286
@@ -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>
|
||||
@@ -77,7 +77,7 @@
|
||||
this.videoVid = event.detail.value;
|
||||
},
|
||||
|
||||
setVid() {
|
||||
setVid(vid) {
|
||||
if(!this.videoVid) {
|
||||
uni.showToast({
|
||||
title: "请输入视频Vid",
|
||||
@@ -87,7 +87,7 @@
|
||||
}
|
||||
const { vodPlayer } = this;
|
||||
vodPlayer.setVid({
|
||||
vid:this.videoVid,
|
||||
vid:this.videoVid || vid,
|
||||
level:0
|
||||
}, (ret) => {
|
||||
this.text = JSON.stringify(ret);
|
||||
|
||||
@@ -0,0 +1,273 @@
|
||||
/**
|
||||
* 本模块封装了Android、iOS的应用权限判断、打开应用权限设置界面、以及位置系统服务是否开启
|
||||
*/
|
||||
|
||||
var isIos
|
||||
// #ifdef APP-PLUS
|
||||
isIos = (plus.os.name == "iOS")
|
||||
// #endif
|
||||
|
||||
// 判断推送权限是否开启
|
||||
function judgeIosPermissionPush() {
|
||||
var result = false;
|
||||
var UIApplication = plus.ios.import("UIApplication");
|
||||
var app = UIApplication.sharedApplication();
|
||||
var enabledTypes = 0;
|
||||
if (app.currentUserNotificationSettings) {
|
||||
var settings = app.currentUserNotificationSettings();
|
||||
enabledTypes = settings.plusGetAttribute("types");
|
||||
console.log("enabledTypes1:" + enabledTypes);
|
||||
if (enabledTypes == 0) {
|
||||
console.log("推送权限没有开启");
|
||||
} else {
|
||||
result = true;
|
||||
console.log("已经开启推送功能!")
|
||||
}
|
||||
plus.ios.deleteObject(settings);
|
||||
} else {
|
||||
enabledTypes = app.enabledRemoteNotificationTypes();
|
||||
if (enabledTypes == 0) {
|
||||
console.log("推送权限没有开启!");
|
||||
} else {
|
||||
result = true;
|
||||
console.log("已经开启推送功能!")
|
||||
}
|
||||
console.log("enabledTypes2:" + enabledTypes);
|
||||
}
|
||||
plus.ios.deleteObject(app);
|
||||
plus.ios.deleteObject(UIApplication);
|
||||
return result;
|
||||
}
|
||||
|
||||
// 判断定位权限是否开启
|
||||
function judgeIosPermissionLocation() {
|
||||
var result = false;
|
||||
var cllocationManger = plus.ios.import("CLLocationManager");
|
||||
var status = cllocationManger.authorizationStatus();
|
||||
result = (status != 2)
|
||||
console.log("定位权限开启:" + result);
|
||||
// 以下代码判断了手机设备的定位是否关闭,推荐另行使用方法 checkSystemEnableLocation
|
||||
/* var enable = cllocationManger.locationServicesEnabled();
|
||||
var status = cllocationManger.authorizationStatus();
|
||||
console.log("enable:" + enable);
|
||||
console.log("status:" + status);
|
||||
if (enable && status != 2) {
|
||||
result = true;
|
||||
console.log("手机定位服务已开启且已授予定位权限");
|
||||
} else {
|
||||
console.log("手机系统的定位没有打开或未给予定位权限");
|
||||
} */
|
||||
plus.ios.deleteObject(cllocationManger);
|
||||
return result;
|
||||
}
|
||||
|
||||
// 判断麦克风权限是否开启
|
||||
function judgeIosPermissionRecord() {
|
||||
var result = false;
|
||||
var avaudiosession = plus.ios.import("AVAudioSession");
|
||||
var avaudio = avaudiosession.sharedInstance();
|
||||
var permissionStatus = avaudio.recordPermission();
|
||||
console.log("permissionStatus:" + permissionStatus);
|
||||
if (permissionStatus == 1684369017 || permissionStatus == 1970168948) {
|
||||
console.log("麦克风权限没有开启");
|
||||
} else {
|
||||
result = true;
|
||||
console.log("麦克风权限已经开启");
|
||||
}
|
||||
plus.ios.deleteObject(avaudiosession);
|
||||
return result;
|
||||
}
|
||||
|
||||
// 判断相机权限是否开启
|
||||
function judgeIosPermissionCamera() {
|
||||
var result = false;
|
||||
var AVCaptureDevice = plus.ios.import("AVCaptureDevice");
|
||||
var authStatus = AVCaptureDevice.authorizationStatusForMediaType('vide');
|
||||
console.log("authStatus:" + authStatus);
|
||||
if (authStatus == 3) {
|
||||
result = true;
|
||||
console.log("相机权限已经开启");
|
||||
} else {
|
||||
console.log("相机权限没有开启");
|
||||
}
|
||||
plus.ios.deleteObject(AVCaptureDevice);
|
||||
return result;
|
||||
}
|
||||
|
||||
// 判断相册权限是否开启
|
||||
function judgeIosPermissionPhotoLibrary() {
|
||||
var result = false;
|
||||
var PHPhotoLibrary = plus.ios.import("PHPhotoLibrary");
|
||||
var authStatus = PHPhotoLibrary.authorizationStatus();
|
||||
console.log("authStatus:" + authStatus);
|
||||
if (authStatus == 3) {
|
||||
result = true;
|
||||
console.log("相册权限已经开启");
|
||||
} else {
|
||||
console.log("相册权限没有开启");
|
||||
}
|
||||
plus.ios.deleteObject(PHPhotoLibrary);
|
||||
return result;
|
||||
}
|
||||
|
||||
// 判断通讯录权限是否开启
|
||||
function judgeIosPermissionContact() {
|
||||
var result = false;
|
||||
var CNContactStore = plus.ios.import("CNContactStore");
|
||||
var cnAuthStatus = CNContactStore.authorizationStatusForEntityType(0);
|
||||
if (cnAuthStatus == 3) {
|
||||
result = true;
|
||||
console.log("通讯录权限已经开启");
|
||||
} else {
|
||||
console.log("通讯录权限没有开启");
|
||||
}
|
||||
plus.ios.deleteObject(CNContactStore);
|
||||
return result;
|
||||
}
|
||||
|
||||
// 判断日历权限是否开启
|
||||
function judgeIosPermissionCalendar() {
|
||||
var result = false;
|
||||
var EKEventStore = plus.ios.import("EKEventStore");
|
||||
var ekAuthStatus = EKEventStore.authorizationStatusForEntityType(0);
|
||||
if (ekAuthStatus == 3) {
|
||||
result = true;
|
||||
console.log("日历权限已经开启");
|
||||
} else {
|
||||
console.log("日历权限没有开启");
|
||||
}
|
||||
plus.ios.deleteObject(EKEventStore);
|
||||
return result;
|
||||
}
|
||||
|
||||
// 判断备忘录权限是否开启
|
||||
function judgeIosPermissionMemo() {
|
||||
var result = false;
|
||||
var EKEventStore = plus.ios.import("EKEventStore");
|
||||
var ekAuthStatus = EKEventStore.authorizationStatusForEntityType(1);
|
||||
if (ekAuthStatus == 3) {
|
||||
result = true;
|
||||
console.log("备忘录权限已经开启");
|
||||
} else {
|
||||
console.log("备忘录权限没有开启");
|
||||
}
|
||||
plus.ios.deleteObject(EKEventStore);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Android权限查询
|
||||
function requestAndroidPermission(permissionID) {
|
||||
return new Promise((resolve, reject) => {
|
||||
plus.android.requestPermissions(
|
||||
[permissionID], // 理论上支持多个权限同时查询,但实际上本函数封装只处理了一个权限的情况。有需要的可自行扩展封装
|
||||
function(resultObj) {
|
||||
var result = 0;
|
||||
for (var i = 0; i < resultObj.granted.length; i++) {
|
||||
var grantedPermission = resultObj.granted[i];
|
||||
console.log('已获取的权限:' + grantedPermission);
|
||||
result = 1
|
||||
}
|
||||
for (var i = 0; i < resultObj.deniedPresent.length; i++) {
|
||||
var deniedPresentPermission = resultObj.deniedPresent[i];
|
||||
console.log('拒绝本次申请的权限:' + deniedPresentPermission);
|
||||
result = 0
|
||||
}
|
||||
for (var i = 0; i < resultObj.deniedAlways.length; i++) {
|
||||
var deniedAlwaysPermission = resultObj.deniedAlways[i];
|
||||
console.log('永久拒绝申请的权限:' + deniedAlwaysPermission);
|
||||
result = -1
|
||||
}
|
||||
resolve(result);
|
||||
// 若所需权限被拒绝,则打开APP设置界面,可以在APP设置界面打开相应权限
|
||||
// if (result != 1) {
|
||||
// gotoAppPermissionSetting()
|
||||
// }
|
||||
},
|
||||
function(error) {
|
||||
console.log('申请权限错误:' + error.code + " = " + error.message);
|
||||
resolve({
|
||||
code: error.code,
|
||||
message: error.message
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// 使用一个方法,根据参数判断权限
|
||||
function judgeIosPermission(permissionID) {
|
||||
if (permissionID == "location") {
|
||||
return judgeIosPermissionLocation()
|
||||
} else if (permissionID == "camera") {
|
||||
return judgeIosPermissionCamera()
|
||||
} else if (permissionID == "photoLibrary") {
|
||||
return judgeIosPermissionPhotoLibrary()
|
||||
} else if (permissionID == "record") {
|
||||
return judgeIosPermissionRecord()
|
||||
} else if (permissionID == "push") {
|
||||
return judgeIosPermissionPush()
|
||||
} else if (permissionID == "contact") {
|
||||
return judgeIosPermissionContact()
|
||||
} else if (permissionID == "calendar") {
|
||||
return judgeIosPermissionCalendar()
|
||||
} else if (permissionID == "memo") {
|
||||
return judgeIosPermissionMemo()
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 跳转到**应用**的权限页面
|
||||
function gotoAppPermissionSetting() {
|
||||
if (isIos) {
|
||||
var UIApplication = plus.ios.import("UIApplication");
|
||||
var application2 = UIApplication.sharedApplication();
|
||||
var NSURL2 = plus.ios.import("NSURL");
|
||||
// var setting2 = NSURL2.URLWithString("prefs:root=LOCATION_SERVICES");
|
||||
var setting2 = NSURL2.URLWithString("app-settings:");
|
||||
application2.openURL(setting2);
|
||||
|
||||
plus.ios.deleteObject(setting2);
|
||||
plus.ios.deleteObject(NSURL2);
|
||||
plus.ios.deleteObject(application2);
|
||||
} else {
|
||||
// console.log(plus.device.vendor);
|
||||
var Intent = plus.android.importClass("android.content.Intent");
|
||||
var Settings = plus.android.importClass("android.provider.Settings");
|
||||
var Uri = plus.android.importClass("android.net.Uri");
|
||||
var mainActivity = plus.android.runtimeMainActivity();
|
||||
var intent = new Intent();
|
||||
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
|
||||
var uri = Uri.fromParts("package", mainActivity.getPackageName(), null);
|
||||
intent.setData(uri);
|
||||
mainActivity.startActivity(intent);
|
||||
}
|
||||
}
|
||||
|
||||
// 检查系统的设备服务是否开启
|
||||
// var checkSystemEnableLocation = async function () {
|
||||
function checkSystemEnableLocation() {
|
||||
if (isIos) {
|
||||
var result = false;
|
||||
var cllocationManger = plus.ios.import("CLLocationManager");
|
||||
var result = cllocationManger.locationServicesEnabled();
|
||||
console.log("系统定位开启:" + result);
|
||||
plus.ios.deleteObject(cllocationManger);
|
||||
return result;
|
||||
} else {
|
||||
var context = plus.android.importClass("android.content.Context");
|
||||
var locationManager = plus.android.importClass("android.location.LocationManager");
|
||||
var main = plus.android.runtimeMainActivity();
|
||||
var mainSvr = main.getSystemService(context.LOCATION_SERVICE);
|
||||
var result = mainSvr.isProviderEnabled(locationManager.GPS_PROVIDER);
|
||||
console.log("系统定位开启:" + result);
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
// module.exports = {
|
||||
// judgeIosPermission: judgeIosPermission,
|
||||
// requestAndroidPermission: requestAndroidPermission,
|
||||
// checkSystemEnableLocation: checkSystemEnableLocation,
|
||||
// gotoAppPermissionSetting: gotoAppPermissionSetting
|
||||
// }
|
||||
export default {judgeIosPermission,requestAndroidPermission,checkSystemEnableLocation,gotoAppPermissionSetting}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"messageText": "Message",
|
||||
"cantactsText": "Cantact",
|
||||
"mineText": "Me"
|
||||
}
|
||||
+367
@@ -0,0 +1,367 @@
|
||||
// en.js
|
||||
export default {
|
||||
appText: 'NIM',
|
||||
messageText: 'Message',
|
||||
contactText: 'Contacts',
|
||||
mineText: 'Me',
|
||||
logoutText: 'Log out',
|
||||
commsEaseText: 'About NIM',
|
||||
logoutConfirmText: 'Are you sure you want to log out?',
|
||||
avatarText: 'Avatar',
|
||||
accountText: 'Account',
|
||||
name: 'Nickname',
|
||||
genderText: 'Gender',
|
||||
remark: 'remark',
|
||||
mobile: 'Phone',
|
||||
email: 'Email',
|
||||
sign: 'Signature',
|
||||
man: 'Male',
|
||||
woman: 'Female',
|
||||
unknow: 'Unknown',
|
||||
birthText: 'Birthday',
|
||||
userOnlineText: 'Online',
|
||||
userOfflineText: 'Offline',
|
||||
updateAvatarText: 'Update Avatar',
|
||||
updateNameText: 'Update Name',
|
||||
|
||||
FriendPageText: 'Friend Card',
|
||||
startConversationText: 'Start Chat',
|
||||
addText: 'Add',
|
||||
applyFriendSuccessText: 'Request Sent',
|
||||
applyFriendFailText: 'Request Failed',
|
||||
addFriendText: 'Add Friend',
|
||||
addFriendFailText: 'Add Friend Failed',
|
||||
deleteFriendText: 'Delete Friend',
|
||||
deleteFriendConfirmText: 'Are you sure you want to delete this friend?',
|
||||
deleteFriendSuccessText: 'Delete Friend Success',
|
||||
deleteFriendFailText: 'Delete Friend Failed',
|
||||
setBlackFailText: 'Add to blacklist failed',
|
||||
removeBlackFailText: 'Remove from blacklist failed',
|
||||
removeBlackSuccessText: 'Remove from blacklist success',
|
||||
addPermissionText:
|
||||
'Please add relevant permissions in the system settings page',
|
||||
FailAvatarText: 'Avatar upload failed',
|
||||
chooseDefaultImage: 'Choose default image',
|
||||
chatButtonText: 'Go to Chat',
|
||||
gotoChatFailText: 'Go to chat failed',
|
||||
chatWithFriendText: 'Chat',
|
||||
okText: 'OK',
|
||||
updateText: 'Update',
|
||||
cancelText: 'Cancel',
|
||||
friendSelectText: 'Select Friend',
|
||||
telErrorText: 'Invalid phone number format',
|
||||
emailErrorText: 'Invalid email format',
|
||||
createButtonText: 'Create',
|
||||
createP2PText: 'Start Chat',
|
||||
createTeamText: 'Create Group',
|
||||
addTeamMemberText: 'Add Group Member',
|
||||
discussionMemberText: '讨论组成员',
|
||||
addTeamMemberFailText: 'Add Group Member Failed',
|
||||
teamTitlePlaceholder: 'Enter Group Name',
|
||||
maxSelectedText: 'You can only select up to 200 friends',
|
||||
createTeamSuccessText: 'Create Group Success',
|
||||
createTeamFailedText: 'Create Group Failed',
|
||||
PersonalPageText: 'PersonalCard',
|
||||
deleteSessionText: 'Delete',
|
||||
sessionMuteText: 'Mute Notifications',
|
||||
sessionMuteFailText: 'Mute Notifications Failed',
|
||||
sessionUnMuteFailText: 'Unmute Notifications Failed',
|
||||
stickTopText: 'Pin Chat',
|
||||
deleteStickTopText: 'Unpin Chat',
|
||||
addStickTopText: 'Pin',
|
||||
deleteSessionFailText: 'Delete Conversation Failed',
|
||||
deleteStickTopFailText: 'Unpin Chat Failed',
|
||||
addStickTopFailText: 'Pin Message Failed',
|
||||
teamMemberText: 'Group Members',
|
||||
saveText: 'Save',
|
||||
setText: 'Settings',
|
||||
failText: 'Failed',
|
||||
saveSuccessText: 'Save Success',
|
||||
saveFailedText: 'Save Failed',
|
||||
searchTeamPlaceholder: 'Enter Group Name',
|
||||
teamTitle: 'Group Name',
|
||||
discussionTitle: 'Discussion Group Name',
|
||||
teamInfoText: 'Group Information',
|
||||
discussionInfoText: 'Discussion Group Information',
|
||||
teamAvatar: 'Group Avatar',
|
||||
discussionAvatarText: 'Discussion Group Avatar',
|
||||
networkError: 'Current network error',
|
||||
searchFailText: 'Search Failed',
|
||||
offlineText: 'Network is unavailable, please check network settings.',
|
||||
connectingText:
|
||||
'The current IM connection has been disconnected and is reconnecting…',
|
||||
teamMenuText: 'My Groups',
|
||||
teamChooseText: 'My Group Chats',
|
||||
chooseText: 'Choose',
|
||||
friendSelect: 'Please select a contact',
|
||||
getHistoryMsgFailedText: 'Failed to get message history',
|
||||
deleteText: 'Delete',
|
||||
recallText: 'Recall',
|
||||
copyText: 'Copy',
|
||||
forwardText: 'Forward',
|
||||
forwardComment: 'forward comment',
|
||||
replyText: 'Reply',
|
||||
replyNotFindText: 'Message has recalled or deleted',
|
||||
forwardToTeamText: 'Forward to Group',
|
||||
forwardToFriendText: 'Forward to Contact',
|
||||
sessionRecordText: 'Conversation Record',
|
||||
chatInputPlaceHolder: 'What would you like to say?',
|
||||
enableV2CloudConversationText: 'Enable V2 Cloud Conversation',
|
||||
SwitchToEnglishText: 'Switch to English',
|
||||
pinText: 'Mark',
|
||||
pinFailedText: 'Mark Failed',
|
||||
unpinText: 'Unmark',
|
||||
unpinFailedText: 'Unmark Failed',
|
||||
noPinListText: 'No Mark',
|
||||
collectionText: 'Collection',
|
||||
deleteCollectionText: 'Delete collection',
|
||||
addCollectionSuccessText: 'Add collection success',
|
||||
addCollectionFailedText: 'Add collection Failed',
|
||||
deleteCollectionFailedText: 'Delete collection Failed',
|
||||
noCollectionsText: 'No collection',
|
||||
teamBannedText: 'Message Banned',
|
||||
you: 'You',
|
||||
pinThisText: 'Marked this message',
|
||||
delete: 'Delete this message',
|
||||
recall: 'Recalled a message',
|
||||
recall2: 'This message has been recalled',
|
||||
recall3: 'Recall this message',
|
||||
recallMsgFailText: 'Recall Message Failed',
|
||||
reeditText: 'Edit',
|
||||
loadingMoreText: 'Loading…',
|
||||
noMoreText: 'No more',
|
||||
deleteMsgSuccessText: 'Delete Success',
|
||||
deleteMsgFailText: 'Delete Failed',
|
||||
conversationEmptyText: 'No conversation',
|
||||
teamOwner: 'Group Owner',
|
||||
teamManager: 'Group Manager',
|
||||
teamEmptyText: 'NoGroup Chats',
|
||||
blacklistEmptyText: 'No Blocked Contacts',
|
||||
blacklistSubTitle: 'You will not get messages from anyone on the list. ',
|
||||
validEmptyText: 'No Verification Messages',
|
||||
dismissTeamText: 'Dismiss Group',
|
||||
dismissTeamConfirmText: 'Are you sure you want to dismiss this group?',
|
||||
leaveTeamTitle: 'Leave Group',
|
||||
leaveDiscussionTitle: 'Leave Discussion Group',
|
||||
leaveTeamConfirmText: 'Are you sure you want to leave this group?',
|
||||
leaveTeamSuccessText: 'You have left this group',
|
||||
leaveTeamFailedText: 'Failed to leave this group',
|
||||
leaveDiscussionSuccessText: 'Successfully left the discussion group',
|
||||
leaveDiscussionFailedText: 'Failed to leave the discussion group',
|
||||
dismissTeamSuccessText: 'Dismiss Group Success',
|
||||
dismissTeamFailedText: 'Dismiss Group Failed',
|
||||
updateTeamSuccessText: 'Update Success',
|
||||
updateTeamFailedText: 'Update Failed',
|
||||
teamTitleConfirmText: 'Group name cannot be empty',
|
||||
teamIntroConfirmText: 'Group introduction cannot be empty',
|
||||
aliasConfirmText: 'Alias cannot be empty',
|
||||
sendImageFailedText: 'Failed to send Image',
|
||||
sendVideoFailedText: 'Failed to send Video',
|
||||
sendAudioFailedText: 'Failed to send Audio',
|
||||
sendFileFailedText: 'Failed to send File',
|
||||
sendMsgFailedText: 'Failed to send Message',
|
||||
weekText: 'Weeks ago',
|
||||
dayText: 'Days ago',
|
||||
hourText: 'Hours ago',
|
||||
minuteText: 'Minutes ago',
|
||||
nowText: 'Just now',
|
||||
sendText: 'Send',
|
||||
sendToText: 'Send to',
|
||||
textMsgText: 'Text Message',
|
||||
audioMsgText: 'Audio Message',
|
||||
videoMsgText: 'Video Message',
|
||||
fileMsgText: 'File Message',
|
||||
callMsgText: 'Call Message',
|
||||
geoMsgText: 'Location Message',
|
||||
imgMsgText: 'Image Message',
|
||||
notiMsgText: 'Notification Message',
|
||||
robotMsgText: 'Robot Message',
|
||||
tipMsgText: 'Tip Message',
|
||||
customMsgText: 'Custom Message',
|
||||
unknowMsgText: 'Unknown Message',
|
||||
noFriendText: 'No Friends',
|
||||
onDismissTeamText: 'This group has been dismissed',
|
||||
onRemoveTeamText: 'You have left the group',
|
||||
selectSessionFailText: 'Failed to select conversation',
|
||||
noExistUser: 'This user does not exist',
|
||||
enterAccount: 'Enter account',
|
||||
validMsgText: 'Verification Message',
|
||||
blacklistText: 'Blocked Contacts',
|
||||
applyTeamText: 'Apply to Join Group',
|
||||
acceptResultText: 'Accepted',
|
||||
rejectResultText: 'Rejected',
|
||||
rejectTeamInviteText: 'Rejected group invitation',
|
||||
beRejectResultText: 'Rejected friend request',
|
||||
passResultText: 'Accepted friend request',
|
||||
acceptedText: 'This request has been accepted',
|
||||
acceptFailedText: 'Failed to accept this request',
|
||||
rejectedText: 'This request has been rejected',
|
||||
rejectFailedText: 'Failed to reject this request',
|
||||
passFriendAskText:
|
||||
"I have accepted your friend request, let's start chatting!",
|
||||
applyFriendText: 'Add you as a friend',
|
||||
rejectText: 'Reject',
|
||||
acceptText: 'Accept',
|
||||
inviteTeamText: 'Invite to Group',
|
||||
addBlacklist: 'Block',
|
||||
removeBlacklist: 'Unblock',
|
||||
forwardSuccessText: 'Forward Success',
|
||||
forwardFailText: 'Forward Failed',
|
||||
getMessageFailed: 'Fail to get message',
|
||||
getForwardMessageFailed: 'Fail to get forward message',
|
||||
sendFailWithInBlackText:
|
||||
'The other party has blocked you, message sending failed',
|
||||
sendFailWithDeleteText:
|
||||
'The relationship between both parties has been terminated, please reapply for contact',
|
||||
friendVerificationText: 'Friend Verification',
|
||||
teamAll: 'all',
|
||||
chooseMentionText: 'Choose Mention',
|
||||
someoneText: 'someone',
|
||||
meText: 'mention',
|
||||
teamMutePlaceholder: 'The current group owner has set the group to mute',
|
||||
viewMoreText: 'View More',
|
||||
resendMsgFailText: 'Resend Failed',
|
||||
audioBtnText: 'Press and hold to speak',
|
||||
audioRemindText: 'Release to send',
|
||||
audioErrorText: 'Recording failed',
|
||||
videoPlayText: 'Video Play',
|
||||
voiceCallText: 'Voice Call',
|
||||
videoCallText: 'Video Call',
|
||||
callDurationText: 'call duration',
|
||||
callCancelText: 'canceled',
|
||||
callRejectedText: 'rejected',
|
||||
callTimeoutText: 'Missed call',
|
||||
callBusyText: 'busy line',
|
||||
callFailedText: 'call failed',
|
||||
wxAppFileCopyText: 'Please paste the URL in the mobile browser.',
|
||||
shootText: 'shoot',
|
||||
albumText: 'album',
|
||||
msgReadPageTitleText: 'Message Reading Status',
|
||||
readText: 'Read',
|
||||
unreadText: 'Unread',
|
||||
fileText: 'file',
|
||||
allUnReadText: 'All Unread',
|
||||
msgRecallTimeErrorText: 'Message recall time exceeded',
|
||||
allReadText: 'All Read',
|
||||
searchText: 'Search',
|
||||
searchTitleText: 'Search',
|
||||
friendText: 'Friend',
|
||||
teamText: 'Group',
|
||||
discussionText: 'Discussion Group',
|
||||
searchResultNullText: 'User does not exist',
|
||||
conversationSendFailText: '[Send Failed]',
|
||||
conversationNotificationText: '[Notification]',
|
||||
fileMsgTitleText: '[File Message]',
|
||||
collectionFromText: 'Collection from',
|
||||
copySuccessText: 'Copy Success',
|
||||
openUrlText: 'Copied. Open in browser.',
|
||||
teamManagerText: 'Team Manager',
|
||||
teamManagerSettingText: 'Team Manager Setting',
|
||||
updateTeamInfoText: 'Who is allowed to edit group details',
|
||||
teamOwnerAndManagerText: 'TeamOwnerAndManager',
|
||||
updateTeamInviteText: 'Who is allowed to invite group members',
|
||||
updateTeamAtText: 'Who is allowed to @',
|
||||
addMemberText: 'Add Member',
|
||||
removeText: 'remove',
|
||||
|
||||
securityTipText:
|
||||
'For test only. Beware of money transfer, lottery winnings & strange call scams.',
|
||||
createDiscussionText: 'Create Discussion',
|
||||
|
||||
// emoji 不能随便填,要用固定 key
|
||||
Laugh: '[Laugh]',
|
||||
Happy: '[Happy]',
|
||||
Sexy: '[Sexy]',
|
||||
Cool: '[Cool]',
|
||||
Mischievous: '[Mischievous]',
|
||||
Kiss: '[Kiss]',
|
||||
Spit: '[Spit]',
|
||||
Squint: '[Squint]',
|
||||
Cute: '[Cute]',
|
||||
Grimace: '[Grimace]',
|
||||
Snicker: '[Snicker]',
|
||||
Joy: '[Joy]',
|
||||
Ecstasy: '[Ecstasy]',
|
||||
Surprise: '[Surprise]',
|
||||
Tears: '[Tears]',
|
||||
Sweat: '[Sweat]',
|
||||
Angle: '[Angle]',
|
||||
Funny: '[Funny]',
|
||||
Awkward: '[Awkward]',
|
||||
Thrill: '[Thrill]',
|
||||
Cry: '[Cry]',
|
||||
Fretting: '[Fretting]',
|
||||
Terrorist: '[Terrorist]',
|
||||
Halo: '[Halo]',
|
||||
Shame: '[Shame]',
|
||||
Sleep: '[Sleep]',
|
||||
Tired: '[Tired]',
|
||||
Mask: '[Mask]',
|
||||
ok: '[ok]',
|
||||
AllRight: '[All right]',
|
||||
Despise: '[Despise]',
|
||||
Uncomfortable: '[Uncomfortable]',
|
||||
Disdain: '[Disdain]',
|
||||
ill: '[ill]',
|
||||
Mad: '[Mad]',
|
||||
Ghost: '[Ghost]',
|
||||
Angry: '[Angry]',
|
||||
Unhappy: '[Unhappy]',
|
||||
Frown: '[Frown]',
|
||||
Broken: '[Broken]',
|
||||
Beckoning: '[Beckoning]',
|
||||
Ok: '[Ok]',
|
||||
Low: '[Low]',
|
||||
Nice: '[Nice]',
|
||||
Applause: '[Applause]',
|
||||
GoodJob: '[Good job]',
|
||||
Hit: '[Hit]',
|
||||
Please: '[Please]',
|
||||
Bye: '[Bye]',
|
||||
First: '[First]',
|
||||
Fist: '[Fist]',
|
||||
GiveMeFive: '[Give me five]',
|
||||
Knife: '[Knife]',
|
||||
Hi: '[Hi]',
|
||||
No: '[No]',
|
||||
Hold: '[Hold]',
|
||||
Think: '[Think]',
|
||||
Pig: '[Pig]',
|
||||
NoListen: '[No listen]',
|
||||
NoLook: '[No look]',
|
||||
NoWords: '[No words]',
|
||||
Monkey: '[Monkey]',
|
||||
Bomb: '[Bomb]',
|
||||
Cloud: '[Cloud]',
|
||||
Rocket: '[Rocket]',
|
||||
Ambulance: '[Ambulance]',
|
||||
Poop: '[Poop]',
|
||||
// 错误码及提示内容
|
||||
// 通用错误码
|
||||
190001: 'internal error',
|
||||
190002: 'illegal state',
|
||||
191001: 'misuse',
|
||||
191002: 'cancelled',
|
||||
191003: 'callback failed',
|
||||
191004: 'invalid parameter',
|
||||
191005: 'timeout',
|
||||
191006: 'resource not exist',
|
||||
191007: 'resource already exist',
|
||||
// 连接相关
|
||||
192001: 'connect failed',
|
||||
192002: 'connect timeout',
|
||||
192003: 'disconnected',
|
||||
192004: 'protocol timeout',
|
||||
192005: 'protocol send failed',
|
||||
192006: 'request failed',
|
||||
// 消息相关
|
||||
107319: 'PIN limit exceeded',
|
||||
107320: 'PIN not exist',
|
||||
107322: 'PIN already exist',
|
||||
107327: 'PIN function disabled',
|
||||
189301: 'collection limit exceeded',
|
||||
189302: 'collection not exist',
|
||||
189449: 'collection concurrent operation failed',
|
||||
// 群相关
|
||||
109432: 'no permission',
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"messageText": "消息",
|
||||
"cantactsText": "通讯录",
|
||||
"mineText": "我的"
|
||||
}
|
||||
@@ -0,0 +1,407 @@
|
||||
// zn.js
|
||||
export default {
|
||||
appText: '云信IM',
|
||||
messageText: '消息',
|
||||
contactText: '通讯录',
|
||||
mineText: '我的',
|
||||
logoutText: '退出登录',
|
||||
commsEaseText: '关于云信',
|
||||
logoutConfirmText: '确定退出登录吗?',
|
||||
avatarText: '头像',
|
||||
updateAvatarText: '修改头像',
|
||||
updateNameText: '修改名称',
|
||||
accountText: '账号',
|
||||
name: '昵称',
|
||||
genderText: '性别',
|
||||
remarkText: '备注名',
|
||||
mobile: '手机',
|
||||
email: '邮箱',
|
||||
sign: '签名',
|
||||
man: '男',
|
||||
woman: '女',
|
||||
unknow: '未知',
|
||||
birthText: '生日',
|
||||
userOnlineText: '在线',
|
||||
userOfflineText: '离线',
|
||||
FriendPageText: '好友名片',
|
||||
startConversationText: '发起单聊',
|
||||
addText: '添加',
|
||||
removeText: '移除',
|
||||
addTeamManagerSuccessText: '添加管理员成功',
|
||||
addTeamManagerFailText: '添加管理员失败',
|
||||
confirmRemoveText: '是否移除?',
|
||||
removeManagerExplain: '移除后该成员将无管理权限',
|
||||
removeMemberExplain: '将移除该成员',
|
||||
removeSuccessText: '移除成功',
|
||||
removeFailText: '移除失败',
|
||||
userNotInTeam: '该用户不在群里',
|
||||
noTeamManager: '暂无群管理员',
|
||||
noTeamMember: '暂无群成员',
|
||||
applyFriendSuccessText: '好友申请已发送',
|
||||
applyFriendFailText: '发送申请失败',
|
||||
addFriendText: '添加好友',
|
||||
addMemberText: '添加成员',
|
||||
addFriendFailText: '添加好友失败',
|
||||
deleteFriendText: '删除好友',
|
||||
deleteFriendConfirmText: '是否确认删除好友',
|
||||
deleteFriendSuccessText: '删除好友成功',
|
||||
deleteFriendFailText: '删除好友失败',
|
||||
setBlackFailText: '加入黑名单失败',
|
||||
removeBlackFailText: '解除黑名单失败',
|
||||
removeBlackSuccessText: '解除黑名单成功',
|
||||
addPermissionText: '请在系统设置页添加相关权限',
|
||||
FailAvatarText: '上传头像失败',
|
||||
chooseDefaultImage: '选择默认图标',
|
||||
chatButtonText: '去聊天',
|
||||
gotoChatFailText: '去聊天失败',
|
||||
chatWithFriendText: '聊天',
|
||||
okText: '确定',
|
||||
updateText: '修改',
|
||||
cancelText: '取消',
|
||||
friendSelectText: '选择好友',
|
||||
telErrorText: '手机格式不正确',
|
||||
emailErrorText: '邮箱格式不正确',
|
||||
createButtonText: '创建',
|
||||
createP2PText: '发起单聊',
|
||||
createTeamText: '创建群聊',
|
||||
addTeamMemberText: '添加群成员',
|
||||
addTeamMemberFailText: '添加群成员失败',
|
||||
teamTitlePlaceholder: '请输入群名称',
|
||||
maxSelectedText: '最多只能选择200位好友',
|
||||
createTeamSuccessText: '创建群组成功',
|
||||
createDiscussionSuccessText: '创建讨论组成功',
|
||||
createTeamFailedText: '创建群组失败',
|
||||
createDiscussionFailedText: '创建讨论组失败',
|
||||
PersonalPageText: '个人名片',
|
||||
deleteSessionText: '删除会话',
|
||||
sessionMuteText: '开启消息提醒',
|
||||
sessionMuteFailText: '开启消息提醒失败',
|
||||
sessionUnMuteFailText: '关闭消息提醒失败',
|
||||
stickTopText: '聊天置顶',
|
||||
deleteStickTopText: '取消置顶',
|
||||
addStickTopText: '置顶消息',
|
||||
deleteSessionFailText: '删除会话失败',
|
||||
deleteStickTopFailText: '取消置顶失败',
|
||||
addStickTopFailText: '置顶消息失败',
|
||||
teamMemberText: '群成员',
|
||||
discussionMemberText: '讨论组成员',
|
||||
teamManagerText: '群管理',
|
||||
teamManagerSettingText: '管理管理员',
|
||||
updateTeamInfoText: '谁可以编辑群信息',
|
||||
updateTeamInviteText: '谁可以邀请新成员',
|
||||
updateTeamAtText: '谁可以@所有人',
|
||||
transformTeam: '转让群主',
|
||||
transformTeamContent: '确认转让群主给',
|
||||
doubt: '吗?',
|
||||
transformTeamSuccessText: '转让群主成功',
|
||||
transformTeamFailedText: '转让群主失败',
|
||||
transformTeamText: '选择转让成员',
|
||||
teamBannedText: '群禁言',
|
||||
saveText: '保存',
|
||||
setText: '设置',
|
||||
failText: '失败',
|
||||
saveSuccessText: '保存成功',
|
||||
saveFailedText: '保存失败',
|
||||
searchTeamPlaceholder: '请输入群名称',
|
||||
nickInTeamEmptyText: '请输入群昵称',
|
||||
teamTitle: '群名称',
|
||||
discussionTitle: '讨论组名称',
|
||||
teamInfoText: '群信息',
|
||||
discussionInfoText: '讨论组信息',
|
||||
teamIntro: '群介绍',
|
||||
teamAvatar: '群头像',
|
||||
discussionAvatarText: '讨论组头像',
|
||||
networkError: '当前网络错误',
|
||||
searchFailText: '查找失败',
|
||||
offlineText: '当前网络不可用,请检查你当前网络设置。',
|
||||
connectingText: '当前IM连接已断开,正在重连...',
|
||||
teamMenuText: '我的群组',
|
||||
teamChooseText: '我的群聊',
|
||||
chooseText: '选择',
|
||||
friendSelect: '请选择联系人',
|
||||
pleaseSelectMember: '请选择成员',
|
||||
teamMemebrSelect: '人员选择',
|
||||
getHistoryMsgFailedText: '获取失败',
|
||||
deleteText: '删除',
|
||||
recallText: '撤回',
|
||||
copyText: '复制',
|
||||
forwardText: '转发',
|
||||
forwardComment: '留言',
|
||||
forwardSuccessText: '转发成功',
|
||||
forwardFailedText: '转发失败',
|
||||
getMessageFailed: '获取消息失败',
|
||||
replyText: '回复',
|
||||
replyNotFindText: '该消息已撤回或删除',
|
||||
msgRecallTimeErrorText: '已超过时间无法撤回',
|
||||
enableV2CloudConversationText: '是否开启云端会话',
|
||||
SwitchToEnglish: '切换语言为英文',
|
||||
copySuccessText: '复制成功',
|
||||
|
||||
forwardToTeamText: '转发到群组',
|
||||
forwardToFriendText: '转发到个人',
|
||||
sessionRecordText: '的会话记录',
|
||||
chatInputPlaceHolder: '说点什么呢~',
|
||||
pinText: '标记',
|
||||
pinFailedText: '标记失败',
|
||||
unpinText: '取消标记',
|
||||
unpinFailedText: '取消标记失败',
|
||||
noPinListText: '暂无标记消息',
|
||||
collectionText: '收藏',
|
||||
deleteCollectionText: '删除收藏',
|
||||
addCollectionSuccessText: '收藏成功',
|
||||
addCollectionFailedText: '添加收藏失败',
|
||||
deleteCollectionFailedText: '删除收藏失败',
|
||||
noCollectionsText: '暂无收藏',
|
||||
you: '你',
|
||||
pinThisText: '标记了这条消息',
|
||||
delete: '删除此消息',
|
||||
recall: '撤回了一条消息',
|
||||
recall2: '此消息已撤回',
|
||||
recall3: '撤回此消息',
|
||||
recallMsgFailText: '撤回消息失败',
|
||||
reeditText: '重新编辑',
|
||||
loadingMoreText: '加载更多……',
|
||||
noMoreText: '没有更多了',
|
||||
deleteMsgSuccessText: '删除成功',
|
||||
deleteMsgFailText: '删除失败',
|
||||
conversationEmptyText: '暂无会话',
|
||||
teamOwner: '群主',
|
||||
teamManager: '群管理员',
|
||||
manager: '管理员',
|
||||
teamEmptyText: '暂无群聊',
|
||||
blacklistEmptyText: '暂无黑名单',
|
||||
blacklistSubTitle: '你不会收到列表中任何联系人的消息',
|
||||
validEmptyText: '暂无验证消息',
|
||||
dismissTeamText: '解散群组',
|
||||
dismissTeamConfirmText: '是否确认解散该群组',
|
||||
leaveTeamTitle: '退出群聊',
|
||||
leaveDiscussionTitle: '退出讨论组',
|
||||
leaveTeamConfirmText: '是否确认退出该群组',
|
||||
leaveDiscussionConfirmText: '是否确认退出该讨论组',
|
||||
leaveTeamSuccessText: '已成功退出此群',
|
||||
leaveDiscussionSuccessText: '已成功退出此讨论组',
|
||||
leaveTeamFailedText: '退出此群失败',
|
||||
leaveDiscussionFailedText: '退出此讨论组失败',
|
||||
dismissTeamSuccessText: '群解散成功',
|
||||
dismissTeamFailedText: '群解散失败',
|
||||
updateTeamSuccessText: '修改成功',
|
||||
updateTeamFailedText: '修改失败',
|
||||
teamTitleConfirmText: '群名称不能为空',
|
||||
teamIntroConfirmText: '群介绍不能为空',
|
||||
aliasConfirmText: '备注名不能为空',
|
||||
sendImageFailedText: '发送图片失败',
|
||||
sendVideoFailedText: '发送视频失败',
|
||||
sendAudioFailedText: '发送音频失败',
|
||||
sendFileFailedText: '发送文件失败',
|
||||
sendMsgFailedText: '发送消息失败',
|
||||
weekText: '周前',
|
||||
dayText: '天前',
|
||||
hourText: '小时前',
|
||||
minuteText: '分钟前',
|
||||
nowText: '刚刚',
|
||||
sendText: '发送',
|
||||
sendToText: '发送给',
|
||||
textMsgText: '文本消息',
|
||||
audioMsgText: '音频消息',
|
||||
videoMsgText: '视频消息',
|
||||
fileMsgText: '文件消息',
|
||||
callMsgText: '话单消息',
|
||||
geoMsgText: '地理位置消息',
|
||||
imgMsgText: '图片消息',
|
||||
notiMsgText: '通知消息',
|
||||
robotMsgText: '机器消息',
|
||||
tipMsgText: '提示消息',
|
||||
customMsgText: '自定义消息',
|
||||
unknowMsgText: '未知消息体',
|
||||
noFriendText: '暂无好友',
|
||||
onDismissTeamText: '该群聊已解散',
|
||||
onRemoveTeamText: '您已离开群组',
|
||||
selectSessionFailText: '选择会话失败',
|
||||
noExistUser: '该用户不存在',
|
||||
enterAccount: '请输入账号',
|
||||
validMsgText: '验证消息',
|
||||
blacklistText: '黑名单',
|
||||
applyTeamText: '申请入群',
|
||||
acceptResultText: '已同意',
|
||||
rejectResultText: '已拒绝',
|
||||
rejectTeamInviteText: '拒绝了群邀请',
|
||||
beRejectResultText: '拒绝了好友申请',
|
||||
passResultText: '通过了好友申请',
|
||||
acceptedText: '已同意该申请',
|
||||
acceptFailedText: '同意该申请失败',
|
||||
rejectedText: '已拒绝该申请',
|
||||
rejectFailedText: '拒绝该申请失败',
|
||||
passFriendAskText: '我已经同意了你的申请,现在开始聊天吧~',
|
||||
applyFriendText: '添加您为好友',
|
||||
rejectText: '拒绝',
|
||||
acceptText: '同意',
|
||||
inviteTeamText: '邀请入群',
|
||||
addBlacklist: '加入黑名单',
|
||||
removeBlacklist: '解除',
|
||||
teamAll: '所有人',
|
||||
sendFailWithInBlackText: '对方已将你拉黑,消息发送失败',
|
||||
sendFailWithDeleteText: '双方关系已解除,如需沟通,请申请',
|
||||
friendVerificationText: '好友验证',
|
||||
chooseMentionText: '选择提醒',
|
||||
someoneText: '有人',
|
||||
meText: '我',
|
||||
teamMutePlaceholder: '当前群主已经设置为群禁言',
|
||||
noPermission: '您暂无权限操作',
|
||||
muteAllTeamFailedText: '全员禁言失败',
|
||||
unmuteAllTeamFailedText: '解除全员禁言失败',
|
||||
|
||||
nickInTeam: '我在群里的昵称',
|
||||
viewMoreText: '查看更多',
|
||||
resendMsgFailText: '重发失败',
|
||||
audioBtnText: '按住说话',
|
||||
audioRemindText: '松开发送',
|
||||
audioErrorText: '录音失败,请检查是否开启权限',
|
||||
videoPlayText: '视频播放',
|
||||
updateTeamAvatar: '更新了群头像',
|
||||
updateTeamName: '更新群名称为',
|
||||
updateTeamIntro: '更新了群介绍',
|
||||
updateTeamInviteMode: '更新了群权限“邀请他人权限”为',
|
||||
updateTeamUpdateTeamMode: '更新了群权限“群资料修改权限”为',
|
||||
updateAllowAt: '更新了“@所有人权限”为',
|
||||
updateTeamMute: '更新了“群禁言”为',
|
||||
onlyTeamOwner: '仅群主',
|
||||
teamOwnerAndManagerText: '群主和管理员',
|
||||
closeText: '关闭',
|
||||
openText: '开启',
|
||||
joinTeamText: '加入群组',
|
||||
leaveTeamText: '离开了群组',
|
||||
beRemoveTeamText: '被移出群组',
|
||||
beAddTeamManagersText: '被任命为管理员',
|
||||
beRemoveTeamManagersText: '被移除管理员',
|
||||
newGroupOwnerText: '成为新群主',
|
||||
callDurationText: '通话时长',
|
||||
callCancelText: '已取消',
|
||||
callRejectedText: '已拒绝',
|
||||
callTimeoutText: '超时未接听',
|
||||
callBusyText: '忙线未接听',
|
||||
callFailedText: '呼叫失败',
|
||||
wxAppFileCopyText: '已自动复制网址,请在手机浏览器里粘贴该网址',
|
||||
shootText: '拍摄',
|
||||
albumText: '相册',
|
||||
voiceCallText: '语音通话',
|
||||
videoCallText: '视频通话',
|
||||
msgReadPageTitleText: '消息阅读状态',
|
||||
readText: '已读',
|
||||
unreadText: '未读',
|
||||
fileText: '文件',
|
||||
allUnReadText: '全部成员未读',
|
||||
allReadText: '全部成员已读',
|
||||
searchText: '请输入你要搜索的关键字',
|
||||
searchTitleText: '搜索',
|
||||
friendText: '好友',
|
||||
teamText: '群组',
|
||||
discussionText: '讨论组',
|
||||
searchResultNullText: '该用户不存在',
|
||||
conversationSendFailText: '[发送失败]',
|
||||
conversationNotificationText: '[通知消息]',
|
||||
getForwardMessageFailed: '该消息已撤回或删除',
|
||||
fileMsgTitleText: '[文件消息]',
|
||||
collectionFromText: '来自',
|
||||
openUrlText: '已复制链接,请在浏览器打开链接',
|
||||
SwitchToEnglishText: '是否切换为英文',
|
||||
securityTipText:
|
||||
'仅用于体验云信IM 产品功能,请勿轻信汇款、中奖等涉及钱款的信息,勿轻易拨打陌生电话,谨防上当受骗。',
|
||||
createDiscussionText: '创建讨论组',
|
||||
|
||||
// emoji 不能随便填,要用固定 key
|
||||
Laugh: '[大笑]',
|
||||
Happy: '[开心]',
|
||||
Sexy: '[色]',
|
||||
Cool: '[酷]',
|
||||
Mischievous: '[奸笑]',
|
||||
Kiss: '[亲]',
|
||||
Spit: '[伸舌头]',
|
||||
Squint: '[眯眼]',
|
||||
Cute: '[可爱]',
|
||||
Grimace: '[鬼脸]',
|
||||
Snicker: '[偷笑]',
|
||||
Joy: '[喜悦]',
|
||||
Ecstasy: '[狂喜]',
|
||||
Surprise: '[惊讶]',
|
||||
Tears: '[流泪]',
|
||||
Sweat: '[流汗]',
|
||||
Angle: '[天使]',
|
||||
Funny: '[笑哭]',
|
||||
Awkward: '[尴尬]',
|
||||
Thrill: '[惊恐]',
|
||||
Cry: '[大哭]',
|
||||
Fretting: '[烦躁]',
|
||||
Terrorist: '[恐怖]',
|
||||
Halo: '[两眼冒星]',
|
||||
Shame: '[害羞]',
|
||||
Sleep: '[睡着]',
|
||||
Tired: '[冒星]',
|
||||
Mask: '[口罩]',
|
||||
ok: '[OK]',
|
||||
AllRight: '[好吧]',
|
||||
Despise: '[鄙视]',
|
||||
Uncomfortable: '[难受]',
|
||||
Disdain: '[不屑]',
|
||||
ill: '[不舒服]',
|
||||
Mad: '[愤怒]',
|
||||
Ghost: '[鬼怪]',
|
||||
Angry: '[发怒]',
|
||||
Unhappy: '[不高兴]',
|
||||
Frown: '[皱眉]',
|
||||
Broken: '[心碎]',
|
||||
Beckoning: '[心动]',
|
||||
Ok: '[好的]',
|
||||
Low: '[低级]',
|
||||
Nice: '[赞]',
|
||||
Applause: '[鼓掌]',
|
||||
GoodJob: '[给力]',
|
||||
Hit: '[打你]',
|
||||
Please: '[阿弥陀佛]',
|
||||
Bye: '[拜拜]',
|
||||
First: '[第一]',
|
||||
Fist: '[拳头]',
|
||||
GiveMeFive: '[手掌]',
|
||||
Knife: '[剪刀]',
|
||||
Hi: '[招手]',
|
||||
No: '[不要]',
|
||||
Hold: '[举着]',
|
||||
Think: '[思考]',
|
||||
Pig: '[猪头]',
|
||||
NoListen: '[不听]',
|
||||
NoLook: '[不看]',
|
||||
NoWords: '[不说]',
|
||||
Monkey: '[猴子]',
|
||||
Bomb: '[炸弹]',
|
||||
Cloud: '[筋斗云]',
|
||||
Rocket: '[火箭]',
|
||||
Ambulance: '[救护车]',
|
||||
Poop: '[便便]',
|
||||
// 错误码及提示内容
|
||||
// 通用错误码
|
||||
190001: '内部错误',
|
||||
190002: '非法状态',
|
||||
191001: '使用姿势错误',
|
||||
191002: '操作取消',
|
||||
191003: '回调失败',
|
||||
191004: '参数错误',
|
||||
191005: '请求超时',
|
||||
191006: '资源不存在',
|
||||
191007: '资源已存在',
|
||||
// 连接相关
|
||||
192001: '连接错误',
|
||||
192002: '连接超时',
|
||||
192003: '连接断开',
|
||||
192004: '协议超时',
|
||||
192005: '协议发送失败',
|
||||
192006: '请求失败',
|
||||
// 消息相关
|
||||
107319: 'PIN 数量超限',
|
||||
107320: 'PIN 消息不存在',
|
||||
107322: '重复 PIN',
|
||||
107327: '未开启 PIN 功能',
|
||||
189301: '收藏数量超限',
|
||||
189302: '收藏不存在',
|
||||
189449: '并发操作收藏失败',
|
||||
// 群相关
|
||||
109432: '没有权限',
|
||||
}
|
||||
@@ -1,5 +1,9 @@
|
||||
|
||||
// ES6模块化引入
|
||||
//import VConsole from 'vconsole'
|
||||
import App from './App'
|
||||
import uviewPlus, { setConfig } from 'uview-plus'
|
||||
//const vConsole = new VConsole();
|
||||
// #ifndef VUE3
|
||||
import Vue from 'vue'
|
||||
import './uni.promisify.adaptor'
|
||||
|
||||
+127
-10
@@ -1,12 +1,13 @@
|
||||
{
|
||||
"name" : "uniapp",
|
||||
"appid" : "__UNI__C3DFBD4",
|
||||
"name" : "肝胆相照专家版",
|
||||
"appid" : "__UNI__89F511F",
|
||||
"description" : "",
|
||||
"versionName" : "1.0.0",
|
||||
"versionCode" : "100",
|
||||
"versionName" : "4.2.7",
|
||||
"versionCode" : 427,
|
||||
"transformPx" : false,
|
||||
"app-plus" : {
|
||||
/* 5+App特有相关 */
|
||||
"runmode" : "liberate",
|
||||
"usingComponents" : true,
|
||||
"nvueCompiler" : "uni-app",
|
||||
"nvueStyleCompiler" : "uni-app",
|
||||
@@ -16,14 +17,28 @@
|
||||
"autoclose" : true,
|
||||
"delay" : 0
|
||||
},
|
||||
"compatible" : {
|
||||
"ignoreVersion" : true
|
||||
},
|
||||
"useragent" : {
|
||||
"value" : "gdxz",
|
||||
"concatenate" : true
|
||||
},
|
||||
"modules" : {
|
||||
"Share" : {},
|
||||
"Webview-x5" : {},
|
||||
"UIWebview" : {},
|
||||
"VideoPlayer" : {},
|
||||
"OAuth" : {},
|
||||
"Barcode" : {},
|
||||
"Camera" : {},
|
||||
"Payment" : {}
|
||||
},
|
||||
/* 模块配置 */
|
||||
"distribute" : {
|
||||
/* 应用发布信息 */
|
||||
"android" : {
|
||||
"hasTaskAffinity" : true,
|
||||
/* android打包配置 */
|
||||
"permissions" : [
|
||||
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
|
||||
@@ -42,18 +57,31 @@
|
||||
"<uses-feature android:name=\"android.hardware.camera\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
|
||||
],
|
||||
"abiFilters" : [ "armeabi-v7a", "arm64-v8a", "x86" ]
|
||||
"abiFilters" : [ "armeabi-v7a", "arm64-v8a", "x86" ],
|
||||
"minSdkVersion" : 21,
|
||||
"targetSdkVersion" : 30
|
||||
},
|
||||
"ios" : {
|
||||
"dSYMs" : false
|
||||
"dSYMs" : false,
|
||||
"idfa" : false,
|
||||
"urlschemewhitelist" : [ "weixin", "wechat" ],
|
||||
"permission" : {
|
||||
"NSPhotoLibraryUsageDescription" : "需要访问相册以选择图片",
|
||||
"NSCameraUsageDescription" : "需要访问相机以拍照"
|
||||
}
|
||||
},
|
||||
/* ios打包配置 */
|
||||
"sdkConfigs" : {
|
||||
"oauth" : {
|
||||
"univerify" : {},
|
||||
"weixin" : {
|
||||
"appid" : "wxbf3658f5e674667c",
|
||||
"UniversalLinks" : "https://doc.igandan.com/gdxzExpert/"
|
||||
},
|
||||
"univerify" : {},
|
||||
"sina" : {
|
||||
"appkey" : "2071931330",
|
||||
"redirect_uri" : "http://sns.whalecloud.com/sina2/callback",
|
||||
"UniversalLinks" : ""
|
||||
}
|
||||
},
|
||||
"payment" : {
|
||||
@@ -62,23 +90,110 @@
|
||||
"appid" : "wxbf3658f5e674667c",
|
||||
"UniversalLinks" : "https://doc.igandan.com/gdxzExpert/"
|
||||
}
|
||||
},
|
||||
"ad" : {},
|
||||
"share" : {
|
||||
"weixin" : {
|
||||
"appid" : "wxbf3658f5e674667c",
|
||||
"UniversalLinks" : "https://doc.igandan.com/gdxzExpert/"
|
||||
},
|
||||
"sina" : {
|
||||
"appkey" : "2071931330",
|
||||
"redirect_uri" : "http://sns.whalecloud.com/sina2/callback",
|
||||
"UniversalLinks" : "https://doc.igandan.com/gdxzExpert/"
|
||||
}
|
||||
},
|
||||
"speech" : {},
|
||||
"push" : {}
|
||||
},
|
||||
"splashscreen" : {
|
||||
"androidStyle" : "common",
|
||||
"useOriginalMsgbox" : false,
|
||||
"iosStyle" : "common"
|
||||
},
|
||||
"icons" : {
|
||||
"android" : {
|
||||
"hdpi" : "D:/uniapp/专家版图标/72.png",
|
||||
"xhdpi" : "D:/uniapp/专家版图标/96.png",
|
||||
"xxhdpi" : "D:/uniapp/专家版图标/144.png",
|
||||
"xxxhdpi" : "D:/uniapp/专家版图标/192.png"
|
||||
},
|
||||
"ios" : {
|
||||
"appstore" : "D:/uniapp/专家版图标/1024.png",
|
||||
"iphone" : {
|
||||
"app@2x" : "D:/uniapp/专家版图标/120.png",
|
||||
"app@3x" : "D:/uniapp/专家版图标/180.png",
|
||||
"spotlight@2x" : "D:/uniapp/专家版图标/80.png",
|
||||
"spotlight@3x" : "D:/uniapp/专家版图标/120.png",
|
||||
"settings@2x" : "D:/uniapp/专家版图标/58.png",
|
||||
"settings@3x" : "D:/uniapp/专家版图标/87.png",
|
||||
"notification@2x" : "D:/uniapp/专家版图标/40.png",
|
||||
"notification@3x" : "D:/uniapp/专家版图标/60.png"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"nativePlugins" : {
|
||||
"NIMUniPlugin" : {
|
||||
"PUSH_VIVO_APPID" : "100034326",
|
||||
"PUSH_VIVO_APPKEY" : "e789634c1f76c8f184b1def1101ce824",
|
||||
"PUSH_HONOR_APPID" : "900782424",
|
||||
"__plugin_info__" : {
|
||||
"name" : "NIMUniPlugin",
|
||||
"description" : "uni消息推送插件",
|
||||
"platforms" : "Android,iOS",
|
||||
"url" : "",
|
||||
"android_package_name" : "",
|
||||
"ios_bundle_id" : "",
|
||||
"isCloud" : false,
|
||||
"bought" : -1,
|
||||
"pid" : "",
|
||||
"parameters" : {
|
||||
"PUSH_VIVO_APPID" : {
|
||||
"des" : "厂商VIVO-app_id",
|
||||
"key" : "com.vivo.push.app_id",
|
||||
"value" : ""
|
||||
},
|
||||
"PUSH_VIVO_APPKEY" : {
|
||||
"des" : "厂商VIVO-app_key",
|
||||
"key" : "com.vivo.push.api_key",
|
||||
"value" : ""
|
||||
},
|
||||
"PUSH_HONOR_APPID" : {
|
||||
"des" : "厂商HONOR-app_id",
|
||||
"key" : "com.hihonor.push.app_id",
|
||||
"value" : ""
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"PLV-MediaPlayer" : {
|
||||
"__plugin_info__" : {
|
||||
"name" : "Polyv播放器插件",
|
||||
"description" : "该插件封装了保利威底层播放器,是云直播和云点播插件必须的附属插件",
|
||||
"platforms" : "Android,iOS",
|
||||
"url" : "https://ext.dcloud.net.cn/plugin?id=4798",
|
||||
"android_package_name" : "",
|
||||
"ios_bundle_id" : "com.domainname.apppolyvios",
|
||||
"android_package_name" : "cn.shangyu.gdxzExpert",
|
||||
"ios_bundle_id" : "cn.shangyu.gdxzExport",
|
||||
"isCloud" : true,
|
||||
"bought" : 1,
|
||||
"pid" : "4798",
|
||||
"parameters" : {}
|
||||
}
|
||||
},
|
||||
"PLV-VodUniPlugin" : {
|
||||
"__plugin_info__" : {
|
||||
"name" : "polyv保利威视频云点播插件",
|
||||
"description" : "polyv保利威视频云点播软件组件,支持聊天弹幕、视频加密、多码率、去广告等,还支持视频多维数据统计,适用于教学录播、产品介绍片、视频门户等",
|
||||
"platforms" : "Android,iOS",
|
||||
"url" : "https://ext.dcloud.net.cn/plugin?id=2802",
|
||||
"android_package_name" : "cn.shangyu.gdxzExpert",
|
||||
"ios_bundle_id" : "cn.shangyu.gdxzExport",
|
||||
"isCloud" : true,
|
||||
"bought" : 1,
|
||||
"pid" : "2802",
|
||||
"parameters" : {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -96,5 +211,7 @@
|
||||
"vueVersion" : "3",
|
||||
"uniStatistics" : {
|
||||
"version" : "2"
|
||||
}
|
||||
},
|
||||
"fallbackLocale" : "zh-Hans",
|
||||
"locale" : "zh-Hans"
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
{"agcgw":{"url":"connect-drcn.dbankcloud.cn","backurl":"connect-drcn.hispace.hicloud.com","websocketurl":"connect-ws-drcn.hispace.dbankcloud.cn","websocketbackurl":"connect-ws-drcn.hispace.dbankcloud.com"},"agcgw_all":{"SG":"connect-dra.dbankcloud.cn","SG_back":"connect-dra.hispace.hicloud.com","CN":"connect-drcn.dbankcloud.cn","CN_back":"connect-drcn.hispace.hicloud.com","RU":"connect-drru.hispace.dbankcloud.ru","RU_back":"connect-drru.hispace.dbankcloud.cn","DE":"connect-dre.dbankcloud.cn","DE_back":"connect-dre.hispace.hicloud.com"},"websocketgw_all":{"SG":"connect-ws-dra.hispace.dbankcloud.cn","SG_back":"connect-ws-dra.hispace.dbankcloud.com","CN":"connect-ws-drcn.hispace.dbankcloud.cn","CN_back":"connect-ws-drcn.hispace.dbankcloud.com","RU":"connect-ws-drru.hispace.dbankcloud.ru","RU_back":"connect-ws-drru.hispace.dbankcloud.cn","DE":"connect-ws-dre.hispace.dbankcloud.cn","DE_back":"connect-ws-dre.hispace.dbankcloud.com"},"client":{"cp_id":"890086000102014306","product_id":"9105163812218585677","client_id":"178572405214545024","client_secret":"B90774CD63B8A6AB5C2C587DC9D7C4C666EF53A19DBA70C4D549C6302ED6CFBE","project_id":"9105163812218585677","app_id":"10526652","api_key":"CV58Raxnpwbfazn6iLPkESUy7KoiUzZ5iTv8lFg/WuexwYZceAK6F9BjRf0HVmT+039dGKt5yecdkRdQ4FCHsmLZ1Zvh","package_name":"cn.shangyu.gdxzExpert"},"oauth_client":{"client_id":"10526652","client_type":1},"app_info":{"app_id":"10526652","package_name":"cn.shangyu.gdxzExpert"},"service":{"analytics":{"collector_url":"datacollector-drcn.dt.hicloud.com,datacollector-drcn.dt.dbankcloud.cn","collector_url_cn":"datacollector-drcn.dt.hicloud.com,datacollector-drcn.dt.dbankcloud.cn","collector_url_de":"datacollector-dre.dt.hicloud.com,datacollector-dre.dt.dbankcloud.cn","collector_url_ru":"datacollector-drru.dt.dbankcloud.ru,datacollector-drru.dt.hicloud.com","collector_url_sg":"datacollector-dra.dt.hicloud.com,datacollector-dra.dt.dbankcloud.cn","resource_id":"p1","channel_id":""},"ml":{"mlservice_url":"ml-api-drcn.ai.dbankcloud.com,ml-api-drcn.ai.dbankcloud.cn"},"cloudstorage":{"storage_url":"https://agc-storage-drcn.platform.dbankcloud.cn","storage_url_ru":"https://agc-storage-drru.cloud.huawei.ru","storage_url_sg":"https://ops-dra.agcstorage.link","storage_url_de":"https://ops-dre.agcstorage.link","storage_url_cn":"https://agc-storage-drcn.platform.dbankcloud.cn","storage_url_ru_back":"https://agc-storage-drru.cloud.huawei.ru","storage_url_sg_back":"https://agc-storage-dra.cloud.huawei.asia","storage_url_de_back":"https://agc-storage-dre.cloud.huawei.eu","storage_url_cn_back":"https://agc-storage-drcn.cloud.huawei.com.cn"},"search":{"url":"https://search-drcn.cloud.huawei.com"},"edukit":{"edu_url":"edukit.cloud.huawei.com.cn","dh_url":"edukit.cloud.huawei.com.cn"}},"region":"CN","configuration_version":"3.0","appInfos":[{"package_name":"cn.shangyu.gdxzExpert","client":{"app_id":"10526652"},"app_info":{"package_name":"cn.shangyu.gdxzExpert","app_id":"10526652"},"oauth_client":{"client_type":1,"client_id":"10526652"}}]}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"developer_id":"109999967097",
|
||||
"app_id":"900782424",
|
||||
"package_name":"cn.shangyu.gdxzExpert",
|
||||
"version":"1.0"
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,101 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>files</key>
|
||||
<dict>
|
||||
<key>Info.plist</key>
|
||||
<data>
|
||||
MxazSmkOgOArGHG40O8L+mZ4ULU=
|
||||
</data>
|
||||
</dict>
|
||||
<key>files2</key>
|
||||
<dict/>
|
||||
<key>rules</key>
|
||||
<dict>
|
||||
<key>^.*</key>
|
||||
<true/>
|
||||
<key>^.*\.lproj/</key>
|
||||
<dict>
|
||||
<key>optional</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1000</real>
|
||||
</dict>
|
||||
<key>^.*\.lproj/locversion.plist$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1100</real>
|
||||
</dict>
|
||||
<key>^Base\.lproj/</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>1010</real>
|
||||
</dict>
|
||||
<key>^version.plist$</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>rules2</key>
|
||||
<dict>
|
||||
<key>.*\.dSYM($|/)</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>11</real>
|
||||
</dict>
|
||||
<key>^(.*/)?\.DS_Store$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>2000</real>
|
||||
</dict>
|
||||
<key>^.*</key>
|
||||
<true/>
|
||||
<key>^.*\.lproj/</key>
|
||||
<dict>
|
||||
<key>optional</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1000</real>
|
||||
</dict>
|
||||
<key>^.*\.lproj/locversion.plist$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1100</real>
|
||||
</dict>
|
||||
<key>^Base\.lproj/</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>1010</real>
|
||||
</dict>
|
||||
<key>^Info\.plist$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^PkgInfo$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^embedded\.provisionprofile$</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^version\.plist$</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
Binary file not shown.
@@ -0,0 +1,70 @@
|
||||
{
|
||||
"name": "NIMUniPlugin",
|
||||
"id": "NIMUniPlugin",
|
||||
"version": "1.1.3",
|
||||
"git": "3ff8678",
|
||||
"buildAt": "7/21/2025, 10:17:00 AM",
|
||||
"description": "uni消息推送插件",
|
||||
"_dp_type": "nativeplugin",
|
||||
"_dp_nativeplugin": {
|
||||
"android": {
|
||||
"plugins": [
|
||||
{
|
||||
"type": "module",
|
||||
"name": "NIMUniPlugin-PluginModule",
|
||||
"class": "com.netease.nimlib.uniapp.push.PushModule"
|
||||
}
|
||||
],
|
||||
"hooksClass": "com.netease.nimlib.uniapp.push.PushModule",
|
||||
"integrateType": "aar",
|
||||
"dependencies": [
|
||||
"com.huawei.hms:push:6.9.0.300",
|
||||
"com.meizu.flyme.internet:push-internal:4.2.3",
|
||||
"com.google.android.gms:play-services-base:18.2.0",
|
||||
"com.google.firebase:firebase-messaging:23.1.2",
|
||||
"com.google.firebase:firebase-core:21.1.1",
|
||||
"com.google.code.gson:gson:2.6.2",
|
||||
"commons-codec:commons-codec:1.6",
|
||||
"com.netease.nimlib:basesdk:9.16.1",
|
||||
"com.netease.nimlib:push:9.16.1"
|
||||
],
|
||||
"compileOptions": {
|
||||
"sourceCompatibility": "1.8",
|
||||
"targetCompatibility": "1.8"
|
||||
},
|
||||
"abis": [
|
||||
"armeabi-v7a",
|
||||
"arm64-v8a",
|
||||
"x86"
|
||||
],
|
||||
"minSdkVersion": "21",
|
||||
"useAndroidX": true,
|
||||
"parameters": {
|
||||
"PUSH_VIVO_APPID": {
|
||||
"des": "厂商VIVO-app_id",
|
||||
"key": "com.vivo.push.app_id"
|
||||
},
|
||||
"PUSH_VIVO_APPKEY": {
|
||||
"des": "厂商VIVO-app_key",
|
||||
"key": "com.vivo.push.api_key"
|
||||
},
|
||||
"PUSH_HONOR_APPID": {
|
||||
"des": "厂商HONOR-app_id",
|
||||
"key": "com.hihonor.push.app_id"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ios": {
|
||||
"plugins": [
|
||||
{
|
||||
"type": "module",
|
||||
"name": "NIMUniPlugin-PluginModule",
|
||||
"class": "NIMPluginModule"
|
||||
}
|
||||
],
|
||||
"hooksClass": "NIMPluginProxy",
|
||||
"integrateType": "framework",
|
||||
"deploymentTarget": "9.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
-1
@@ -8,11 +8,20 @@
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"@xkit-yx/im-store-v2": "^0.8.3",
|
||||
"@xkit-yx/utils": "^0.7.2",
|
||||
"crypto-js": "^4.2.0",
|
||||
"dayjs": "^1.11.18",
|
||||
"html2canvas": "^1.4.1",
|
||||
"image-tools": "^1.4.0",
|
||||
"js-base64": "^3.7.8",
|
||||
"js-md5": "^0.8.3",
|
||||
"mobx": "^6.6.1",
|
||||
"nim-web-sdk-ng": "^10.9.50",
|
||||
"pinyin": "^4.0.0",
|
||||
"uview-plus": "^3.4.73"
|
||||
"uni-packing-wgt": "^1.2.3",
|
||||
"uniapp-video-player": "^1.3.0",
|
||||
"uview-plus": "^3.4.73",
|
||||
"vconsole": "^3.15.1"
|
||||
}
|
||||
}
|
||||
|
||||
+1675
-863
File diff suppressed because it is too large
Load Diff
+197
@@ -0,0 +1,197 @@
|
||||
<template>
|
||||
<view class="ad-container" v-if="adData.path">
|
||||
<!-- 广告图片 -->
|
||||
<image
|
||||
class="ad-image"
|
||||
:src="adData.path"
|
||||
mode="aspectFill"
|
||||
@click="handleAdClick"
|
||||
></image>
|
||||
|
||||
<!-- 右上角倒计时跳过按钮 -->
|
||||
<view class="skip-button" @click="skipAd">
|
||||
<text class="skip-text">跳过 {{countdown}}s</text>
|
||||
</view>
|
||||
|
||||
<!-- 底部点击跳转按钮 -->
|
||||
<view class="bottom-button" v-if="adData.url" @click="handleAdClick">
|
||||
<text class="button-text">点击查看详情</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 加载状态 -->
|
||||
<view class="loading-container" v-else>
|
||||
<text class="loading-text">加载中...</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, nextTick, computed, onUnmounted } from 'vue';
|
||||
import { onShow, onLoad, onPageScroll } from "@dcloudio/uni-app";
|
||||
import my_api from '@/api/my_api.js'
|
||||
import docUrl from "@/utils/docUrl.js";
|
||||
const app = getApp()
|
||||
// 广告数据
|
||||
const adData = ref({
|
||||
path: '',
|
||||
url: ''
|
||||
});
|
||||
|
||||
// 倒计时
|
||||
const countdown = ref(5);
|
||||
let timer = null;
|
||||
|
||||
// 页面加载
|
||||
onLoad(() => {
|
||||
console.log('开屏广告页面加载');
|
||||
getStartpage();
|
||||
});
|
||||
|
||||
// 获取开屏广告数据
|
||||
const getStartpage = () => {
|
||||
my_api.startpage().then(res => {
|
||||
console.log('开屏广告数据:', res);
|
||||
if (res && res.data) {
|
||||
adData.value = {
|
||||
path: docUrl + res.data.path || '',
|
||||
url: res.data.url || ''
|
||||
};
|
||||
|
||||
// 如果有广告图片,开始倒计时
|
||||
if (res.data.path) {
|
||||
|
||||
startCountdown();
|
||||
|
||||
} else {
|
||||
// 没有广告,直接跳转到首页
|
||||
navigateToHome();
|
||||
}
|
||||
} else {
|
||||
// 接口返回异常,直接跳转到首页
|
||||
navigateToHome();
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error('获取开屏广告失败:', err);
|
||||
// 接口调用失败,直接跳转到首页
|
||||
navigateToHome();
|
||||
});
|
||||
}
|
||||
|
||||
// 开始倒计时
|
||||
const startCountdown = () => {
|
||||
timer = setInterval(() => {
|
||||
countdown.value--;
|
||||
if (countdown.value <= 0) {
|
||||
clearInterval(timer);
|
||||
if(app.globalData.plAd){
|
||||
navigateToHome();
|
||||
}
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// 跳过广告
|
||||
const skipAd = () => {
|
||||
clearInterval(timer);
|
||||
navigateToHome();
|
||||
}
|
||||
|
||||
// 点击广告
|
||||
const handleAdClick = () => {
|
||||
if (adData.value.url) {
|
||||
// 有跳转链接,跳转到指定页面
|
||||
uni.navigateTo({
|
||||
url: '/pages/webview/webview?url=' + adData.value.url
|
||||
});
|
||||
} else {
|
||||
// 没有跳转链接,直接跳转到首页
|
||||
navigateToHome();
|
||||
}
|
||||
}
|
||||
|
||||
// 跳转到首页
|
||||
const navigateToHome = () => {
|
||||
uni.redirectTo({
|
||||
url: '/pages/index/index'
|
||||
});
|
||||
}
|
||||
|
||||
// 页面卸载时清理定时器
|
||||
onUnmounted(() => {
|
||||
if (timer) {
|
||||
clearInterval(timer);
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.ad-container {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-color: #000;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.ad-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
|
||||
.skip-button {
|
||||
position: absolute;
|
||||
top: 60rpx;
|
||||
right: 30rpx;
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
border-radius: 40rpx;
|
||||
padding: 16rpx 24rpx;
|
||||
z-index: 10000;
|
||||
}
|
||||
|
||||
.skip-text {
|
||||
color: #fff;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.bottom-button {
|
||||
position: absolute;
|
||||
bottom: 100rpx;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-radius: 50rpx;
|
||||
padding: 24rpx 60rpx;
|
||||
box-shadow: 0 8rpx 32rpx rgba(102, 126, 234, 0.3);
|
||||
z-index: 10000;
|
||||
}
|
||||
|
||||
.button-text {
|
||||
color: #fff;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.loading-container {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
color: #666;
|
||||
font-size: 32rpx;
|
||||
}
|
||||
</style>
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<uni-nav-bar title="继续教育" fixed color="#8B2316" height="140rpx" :border="false" backgroundColor="#eeeeee"></uni-nav-bar>
|
||||
<navBar :title="'继续教育'"></navBar>
|
||||
<view class="page">
|
||||
<!-- 课程列表 -->
|
||||
<view class="course-list">
|
||||
@@ -103,21 +103,22 @@
|
||||
import xueyuanImg from "@/static/ganbingxueyuan.png"
|
||||
import newerImg from "@/static/xinshoujiaocheng.png"
|
||||
import navTo from '@/utils/navTo.js';
|
||||
import navBar from "@/components/navBar/navBar.vue"
|
||||
// 方法
|
||||
// 方法
|
||||
const goToCourse = (courseType) => {
|
||||
console.log('进入课程:', courseType);
|
||||
|
||||
|
||||
// 根据课程类型进行不同的处理
|
||||
switch(courseType) {
|
||||
case 'course':
|
||||
navTo({
|
||||
url: '/pages_course/index/index'
|
||||
uni.sendNativeEvent('goCourse',{
|
||||
msg: 'course'
|
||||
})
|
||||
break;
|
||||
case 'video':
|
||||
navTo({
|
||||
url: '/pages_app/video/video'
|
||||
url: '/pages_app/video/video?from=education'
|
||||
})
|
||||
break;
|
||||
case 'ppt':
|
||||
@@ -166,9 +167,8 @@ $padding-small: 10px;
|
||||
|
||||
.page {
|
||||
background-color: $gray-bg;
|
||||
min-height: calc(100vh - 140rpx);
|
||||
|
||||
padding-bottom: 120rpx;
|
||||
min-height: calc(100vh - var(--status-bar-height) - 44px);
|
||||
margin-top:calc(var(--status-bar-height) + 44px);
|
||||
}
|
||||
|
||||
// 课程列表
|
||||
|
||||
+1044
-565
File diff suppressed because it is too large
Load Diff
+1469
-897
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<navBar :showLeft="false" :title="'加载中...'"></navBar>
|
||||
<view class="loading-page"></view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted,ref} from 'vue';
|
||||
import { onLoad,onUnload,onHide,onShow } from '@dcloudio/uni-app';
|
||||
import navBar from '@/components/navBar/navBar.vue';
|
||||
const timer=ref(null);
|
||||
const count=ref(0);
|
||||
const countDown=()=>{
|
||||
if(timer.value){
|
||||
return
|
||||
}
|
||||
timer.value=setInterval(()=>{
|
||||
count.value++;
|
||||
if(count.value>=8){
|
||||
clearInterval(timer.value);
|
||||
plus.runtime.quit();
|
||||
}
|
||||
},1000);
|
||||
}
|
||||
onShow(() => {
|
||||
uni.showLoading({
|
||||
title: '加载中...',
|
||||
mask: true
|
||||
});
|
||||
countDown()
|
||||
});
|
||||
|
||||
onUnload(() => {
|
||||
uni.hideLoading();
|
||||
if(timer.value){
|
||||
clearInterval(timer.value);
|
||||
}
|
||||
});
|
||||
onHide(() => {
|
||||
uni.hideLoading();
|
||||
if(timer.value){
|
||||
clearInterval(timer.value);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.loading-page{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #fff;
|
||||
}
|
||||
</style>
|
||||
+65
-14
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<uni-nav-bar title="肝胆会议" fixed color="#8B2316" height="140rpx" :border="false" backgroundColor="#eeeeee"></uni-nav-bar>
|
||||
<navBar :title="'肝胆会议'"></navBar>
|
||||
<view class="page">
|
||||
<!-- 筛选标签栏 -->
|
||||
<view class="filter-bar">
|
||||
@@ -146,6 +146,7 @@
|
||||
import timeImg from "@/static/play_long.png"
|
||||
import api from '@/api/api';
|
||||
import docUrl from '@/utils/docUrl';
|
||||
import navBar from "@/components/navBar/navBar.vue"
|
||||
// 弹窗状态
|
||||
const isTimePopupShow = ref(false);
|
||||
const isLocationPopupShow = ref(false);
|
||||
@@ -168,30 +169,37 @@
|
||||
const generateMonthList = () => {
|
||||
const months = [];
|
||||
const currentDate = new Date();
|
||||
const currentMonth = currentDate.getMonth() + 1; // 当前月份 (1-12)
|
||||
const currentYear = currentDate.getFullYear();
|
||||
|
||||
// 添加"所有"选项
|
||||
months.push({ value: 'all', label: '所有' });
|
||||
|
||||
// 从当前月份开始,往后生成12个月
|
||||
for (let i = 0; i < 12; i++) {
|
||||
const targetDate = new Date(currentDate.getFullYear(), currentDate.getMonth() + i, 1);
|
||||
const month = targetDate.getMonth() + 1;
|
||||
const year = targetDate.getFullYear();
|
||||
|
||||
// 格式化月份标签
|
||||
// 从当前月份到12月
|
||||
for (let month = currentMonth; month <= 12; month++) {
|
||||
let monthLabel = '';
|
||||
if (i === 0) {
|
||||
if (month === currentMonth) {
|
||||
monthLabel = `${month}月(本月)`;
|
||||
} else if (i === 1) {
|
||||
} else if (month === currentMonth + 1) {
|
||||
monthLabel = `${month}月(下月)`;
|
||||
} else {
|
||||
monthLabel = `${month}月`;
|
||||
}
|
||||
|
||||
months.push({
|
||||
value: month.toString(), // 只传月份数字
|
||||
value: month.toString(),
|
||||
label: monthLabel,
|
||||
year: year,
|
||||
year: currentYear,
|
||||
month: month
|
||||
});
|
||||
}
|
||||
|
||||
// 从1月到当前月份(不包括当前月份,避免重复)
|
||||
for (let month = 1; month < currentMonth; month++) {
|
||||
months.push({
|
||||
value: month.toString(),
|
||||
label: `${month}月`,
|
||||
year: currentYear + 1, // 下一年的月份
|
||||
month: month
|
||||
});
|
||||
}
|
||||
@@ -400,7 +408,7 @@
|
||||
meetingListBySearchU(filters).then(() => {
|
||||
uni.showToast({
|
||||
title: '刷新成功',
|
||||
icon: 'success',
|
||||
icon: 'none',
|
||||
duration: 1500
|
||||
});
|
||||
});
|
||||
@@ -754,7 +762,50 @@
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
// 格式化日期函数,包含年月
|
||||
const formatDate = (date, format = 'YYYY-MM') => {
|
||||
if (!date) return '';
|
||||
|
||||
// 如果传入的是字符串,转换为Date对象
|
||||
const dateObj = typeof date === 'string' ? new Date(date) : date;
|
||||
|
||||
// 检查日期是否有效
|
||||
if (isNaN(dateObj.getTime())) {
|
||||
console.error('无效的日期:', date);
|
||||
return '';
|
||||
}
|
||||
|
||||
const year = dateObj.getFullYear();
|
||||
const month = (dateObj.getMonth() + 1).toString().padStart(2, '0');
|
||||
const day = dateObj.getDate().toString().padStart(2, '0');
|
||||
const hours = dateObj.getHours().toString().padStart(2, '0');
|
||||
const minutes = dateObj.getMinutes().toString().padStart(2, '0');
|
||||
const seconds = dateObj.getSeconds().toString().padStart(2, '0');
|
||||
|
||||
// 根据格式参数返回不同格式
|
||||
switch (format) {
|
||||
case 'YYYY-MM':
|
||||
return `${year}-${month}`;
|
||||
case 'YYYY-MM-DD':
|
||||
return `${year}-${month}-${day}`;
|
||||
case 'YYYY-MM-DD HH:mm':
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}`;
|
||||
case 'YYYY-MM-DD HH:mm:ss':
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||||
case 'YYYY年MM月':
|
||||
return `${year}年${month}月`;
|
||||
case 'YYYY年MM月DD日':
|
||||
return `${year}年${month}月${day}日`;
|
||||
case 'MM月':
|
||||
return `${month}月`;
|
||||
case 'MM月DD日':
|
||||
return `${month}月${day}日`;
|
||||
default:
|
||||
return `${year}-${month}`;
|
||||
}
|
||||
};
|
||||
|
||||
onShow(() => {
|
||||
// 生成月份列表
|
||||
generateMonthList();
|
||||
// 加载会议列表
|
||||
|
||||
+593
-51
@@ -1,6 +1,5 @@
|
||||
<template>
|
||||
<uni-nav-bar title="我的" fixed color="#8B2316" height="140rpx" :border="false"
|
||||
backgroundColor="#eeeeee"></uni-nav-bar>
|
||||
<navBar :title="'我的'" :showLeft="false"></navBar>
|
||||
<view class="page">
|
||||
|
||||
|
||||
@@ -8,8 +7,22 @@
|
||||
<view class="user-card"
|
||||
:style="{'background-image': `url(${myInfoBackGround || '/static/big_background_my.png'})`, 'background-size':'cover','background-repeat':'no-repeat'}">
|
||||
<view class="user-info">
|
||||
<image class="avatar" :src="avatar" mode="aspectFill"></image>
|
||||
<text class="username">{{username}}</text>
|
||||
<image class="avatar" :src="avatar" mode="aspectFill" @click="goToPage('myInfo')"></image>
|
||||
<view class="user-info-content">
|
||||
<text class="username">{{username}}</text>
|
||||
<scroll-view class="honor_box" scroll-x="true" show-scrollbar="false">
|
||||
<view class="honor_wrap">
|
||||
<view class="honor_item" v-if="ranking > 0">
|
||||
<image class="honor_image" :src="suifangImg" mode="aspectFill"></image>
|
||||
<text class="honor_text">随访达人|排名{{ranking}}</text>
|
||||
</view>
|
||||
<view class="honor_item" v-for="item in honor_list" :key="item.id" @click="getHonorDetail(item.id)">
|
||||
<image class="honor_image" :src="rongyu" mode="aspectFill"></image>
|
||||
<text class="honor_text">{{ item.nick_name }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 统计数据 -->
|
||||
@@ -27,9 +40,12 @@
|
||||
<text class="stats-label">患者送花数</text>
|
||||
</view>
|
||||
<view class="divder"></view>
|
||||
<view class="signbox">
|
||||
<view class="signbox" v-if="sign_in == 0" @click="onSignClick">
|
||||
<view class="sign">签到</view>
|
||||
</view>
|
||||
<view class="signbox_1" v-else @click="goToPage('pointMall')">
|
||||
<view class="sign">{{ formatToChineseUnit(totalPoints) }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -54,7 +70,7 @@
|
||||
<up-image :src="sfImg" width="48rpx" height="48rpx"></up-image>
|
||||
<text>随访二维码</text>
|
||||
</view>
|
||||
<view class="service-item" @click="goToPage('qrcode')">
|
||||
<view class="service-item" @click="goToPage('plan')">
|
||||
<up-image :src="czjhImg" width="48rpx" height="48rpx"></up-image>
|
||||
<text>出诊计划</text>
|
||||
</view>
|
||||
@@ -88,9 +104,9 @@
|
||||
<!-- 账户明细 -->
|
||||
<view class="section">
|
||||
<view class="section-title">账户明细</view>
|
||||
<up-scroll-list indicatorActiveColor="#8B2316" indicatorWidth="40rpx" indicatorBarWidth="20rpx"
|
||||
<up-scroll-list :indicator="isAccount" indicatorActiveColor="#8B2316" indicatorWidth="40rpx" indicatorBarWidth="20rpx"
|
||||
class="account-grid">
|
||||
<view class="account-item" @click="goToPage('myAccount')">
|
||||
<view class="account-item" @click="goToPage('myAccount')" v-if="isAccount">
|
||||
<up-image :src="wdzzImg" width="48rpx" height="48rpx"></up-image>
|
||||
<text>我的账户</text>
|
||||
</view>
|
||||
@@ -102,11 +118,11 @@
|
||||
<up-image :src="wdflImg" width="48rpx" height="48rpx"></up-image>
|
||||
<text>我的福利</text>
|
||||
</view>
|
||||
<view class="account-item" @click="goToPage('myFlower')">
|
||||
<view class="account-item" @click="goToPage('myFlower')" v-if="isAccount">
|
||||
<up-image :src="wdxhImg" width="48rpx" height="48rpx"></up-image>
|
||||
<text>我的鲜花</text>
|
||||
</view>
|
||||
<view class="account-item" @click="goToPage('pptDetail')">
|
||||
<view class="account-item" @click="goToPage('pptDetail')" v-if="isAccount">
|
||||
<up-image :src="kjmxImg" width="48rpx" height="48rpx"></up-image>
|
||||
<text>课件明细</text>
|
||||
</view>
|
||||
@@ -117,28 +133,31 @@
|
||||
</up-scroll-list>
|
||||
</view>
|
||||
|
||||
<!-- #ifdef APP -->
|
||||
<!-- 常用操作 -->
|
||||
<view class="section">
|
||||
<view class="section-title">常用操作</view>
|
||||
<view class="operation-grid">
|
||||
<view class="operation-item" @click="goToPage('wechatUnbind')">
|
||||
<up-image :src="wxjbImg" width="48rpx" height="48rpx"></up-image>
|
||||
<text>微信解绑</text>
|
||||
<text v-if="isBoundWechat">微信解绑</text>
|
||||
<text v-else>微信绑定</text>
|
||||
</view>
|
||||
<view class="operation-item" @click="goToPage('changePhone')">
|
||||
<up-image :src="ghsjhImg" width="48rpx" height="48rpx"></up-image>
|
||||
<text>更换手机号</text>
|
||||
</view>
|
||||
<view class="operation-item" @click="goToPage('notifications')">
|
||||
<view class="operation-item" @click="switchPushPermissions">
|
||||
<up-image :src="tzykImg" width="48rpx" height="48rpx"></up-image>
|
||||
<text>通知已开</text>
|
||||
<text>通知已{{notice_on?'开':'关'}}</text>
|
||||
</view>
|
||||
<view class="operation-item" @click="showVersion">
|
||||
<up-image :src="versionImg" width="48rpx" height="48rpx"></up-image>
|
||||
<text>V4.1.4</text>
|
||||
<up-image :src="hasNewVersion?versionImgOn:versionImg" width="48rpx" height="48rpx"></up-image>
|
||||
<text>{{hasNewVersion?'有新版本':'V'+version}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
<view class="setbox">
|
||||
<!-- 福利卡兑换 -->
|
||||
<view class="sectioncell benefit-section">
|
||||
@@ -159,7 +178,7 @@
|
||||
|
||||
<!-- 常用银行卡 -->
|
||||
<view class="sectioncell bank-section">
|
||||
<view class="bank-item" @click="goToPage('bankCard')">
|
||||
<view class="bank-item" @click="goToPage('bankCard')" v-if="isAccount">
|
||||
<view class="bank-icon">
|
||||
<up-image :src="cyyhk" width="48rpx" height="48rpx"></up-image>
|
||||
</view>
|
||||
@@ -178,10 +197,12 @@
|
||||
<view class="settings-icon">
|
||||
<up-image :src="settingImg" width="48rpx" height="48rpx"></up-image>
|
||||
</view>
|
||||
<view class="settings-content">
|
||||
<view class="settings-content flex-row" >
|
||||
<text class="settings-title">设置与帮助</text>
|
||||
<text v-if="hasNewVersion && isIos" class="new-version">有新版本</text>
|
||||
</view>
|
||||
<view class="settings-arrow">
|
||||
|
||||
<uni-icons type="forward" size="16" color="#ccc"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
@@ -199,16 +220,69 @@
|
||||
<!-- 底部导航栏 -->
|
||||
<CustomTabbar></CustomTabbar>
|
||||
</view>
|
||||
<unidialog :visible="hasSign" content="今日已签到,每天只能签到一次。<br>请明日继续哦~" @close="hasSign=false" :showCancel="true" cancelText="关闭"></unidialog>
|
||||
<up-overlay :show="showSign" >
|
||||
<view class="signwrap">
|
||||
<view class="signbox">
|
||||
<view class="close" @click="showSign=false"></view>
|
||||
<view class="signbg">
|
||||
<up-image :src="signImg" width="604rpx" height="964rpx" ></up-image>
|
||||
</view>
|
||||
<view class="signcontent">
|
||||
<view class="day">今天是我们相识的第{{signInfo.gdxzday}}天</view>
|
||||
<view class="signtotal">本周共签到{{signInfo.totalDay}}次</view>
|
||||
<view class="signcontinue">已经连续签到{{signInfo.continuous_day}}天</view>
|
||||
<view class="tip">连续签到获取更多积分</view>
|
||||
<view class="news" @click.stop="goNews">
|
||||
{{signInfo.news.summary}}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</up-overlay>
|
||||
<up-overlay :show="showHonor" >
|
||||
<view class="honorwrap">
|
||||
<view class="honorbox">
|
||||
<image :src="docUrl+honnor_detail.img" class="honor_image"></image>
|
||||
<uni-icons type="close" class="close_honor" size="24" color="#fff" @click="showHonor=false"></uni-icons>
|
||||
<view class="content">
|
||||
<view class="content_title">
|
||||
{{honnor_detail.title}}
|
||||
</view>
|
||||
<view class="row">
|
||||
<view class="row_item" v-for="item in honnor_detail.honorlist" :key="item.id">
|
||||
{{ item.nick_name }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="content_content">
|
||||
{{honnor_detail.note}}
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</up-overlay>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import unidialog from '@/components/dialog/dialog.vue'
|
||||
import signImg from "@/static/sign_in_bng_big.png"
|
||||
import CustomTabbar from '@/components/tabBar/tabBar.vue';
|
||||
import navBar from "@/components/navBar/navBar.vue"
|
||||
import {
|
||||
ref
|
||||
ref,reactive
|
||||
} from 'vue';
|
||||
import {
|
||||
onShow
|
||||
} from "@dcloudio/uni-app";
|
||||
const showHonor=ref(false);
|
||||
import docUrl from "@/utils/docUrl.js"
|
||||
import honnorExpert from "@/static/zixunexpert.png"
|
||||
import honnorYingxiong from "@/static/yingxiongbanglog.png"
|
||||
import rongyu from "@/static/yxb.png"
|
||||
import version from '@/utils/version.js'
|
||||
import hzshImg from "@/static/hzsh.png"
|
||||
import hzfzImg from "@/static/hzfz.png"
|
||||
import qfxxImg from "@/static/qfxz.png"
|
||||
@@ -229,18 +303,27 @@
|
||||
import ghsjhImg from "@/static/ghsjh.png"
|
||||
import tzykImg from "@/static/xxtx.png"
|
||||
import versionImg from "@/static/fxxbb.png"
|
||||
|
||||
|
||||
import versionImgOn from "@/static/fxbb.png"
|
||||
import jifenImg from "@/static/point_buy.png"
|
||||
import suifangImg from "@/static/suifang.png"
|
||||
import fulicard from "@/static/fulicard.png"
|
||||
import fpgl from "@/static/fpgl.png"
|
||||
import cyyhk from "@/static/cyhhk.png"
|
||||
import settingImg from "@/static/setting.png"
|
||||
import api from '@/api/api.js'
|
||||
import my_api from '@/api/my_api.js'
|
||||
import navTo from "@/utils/navTo"
|
||||
import linkUrl from "@/utils/docUrl"
|
||||
|
||||
// 响应式数据
|
||||
const isLargeFont = ref(false);
|
||||
|
||||
const hasSign = ref(false)
|
||||
const showSign = ref(false)
|
||||
const signInfo=reactive({
|
||||
news:{
|
||||
summary:''
|
||||
}
|
||||
})
|
||||
// 新增:我的信息数据
|
||||
const avatar = ref('')
|
||||
const username = ref('')
|
||||
@@ -248,7 +331,46 @@
|
||||
const consultCount = ref(0)
|
||||
const flowerCount = ref(0)
|
||||
const myInfoBackGround = ref('')
|
||||
|
||||
const totalPoints = ref(0)
|
||||
const sign_in = ref(0)
|
||||
const honor_list = ref([])
|
||||
const ranking = ref(0)
|
||||
|
||||
const notice_on = ref(false)
|
||||
const hasNewVersion = ref(false)
|
||||
const checkVersion=()=>{
|
||||
api.checkVersion().then(res=>{
|
||||
if(res.code==80002){
|
||||
hasNewVersion.value=true
|
||||
}
|
||||
})
|
||||
}
|
||||
const isAccount = ref(false);
|
||||
const getIsAccount=()=>{
|
||||
api.getIsAccount().then(res=>{
|
||||
if(res.code==1){
|
||||
isAccount.value=res.is_account==0?false:true;
|
||||
}
|
||||
})
|
||||
}
|
||||
const goNews=()=>{
|
||||
let url=docUrl+signInfo.news.path;
|
||||
// #ifdef H5
|
||||
window.open(url, '_blank');
|
||||
// #endif
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
plus.runtime.openURL(url);
|
||||
// #endif
|
||||
|
||||
// #ifdef MP
|
||||
const encoded = encodeURIComponent(url);
|
||||
navTo({
|
||||
url: `/pages_app/webview/webview?url=${encoded}`
|
||||
});
|
||||
// #endif
|
||||
}
|
||||
|
||||
// 从storage获取用户信息
|
||||
const getUserInfoFromStorage = () => {
|
||||
try {
|
||||
@@ -263,35 +385,81 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 签到点击事件
|
||||
const onSignClick = async () => {
|
||||
|
||||
try {
|
||||
// 调用添加积分接口
|
||||
const res = await api.addBonusPointsN({
|
||||
score_type:1
|
||||
});
|
||||
|
||||
if (res && res.code ==1) {
|
||||
// 签到成功
|
||||
sign_in.value = 1;
|
||||
uni.showToast({
|
||||
title: '签到成功,获得'+res.bonuspoints+'积分',
|
||||
icon: 'none'
|
||||
});
|
||||
Object.assign(signInfo,res);
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: res?.msg || '签到失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('签到失败:', error);
|
||||
uni.showToast({
|
||||
title: '签到失败,请重试',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const fetchMyInfo = async () => {
|
||||
try {
|
||||
const res = await api.getMyInfo({})
|
||||
if (res && res.code === 200 && res.data) {
|
||||
const data = res.data
|
||||
myInfoBackGround.value = data.myInfoBackGround;
|
||||
followupCount.value = data.follow_patient_num || data.relationPatientNum || 0
|
||||
consultCount.value = data.consult_total || data.consultCount || 0
|
||||
flowerCount.value = data.give_flower_num || data.flowerCount || 0
|
||||
myInfoBackGround.value = data.myInfoBackGround;
|
||||
followupCount.value = data.expert_apply_num || 0
|
||||
consultCount.value = data.consult_total_num || 0
|
||||
flowerCount.value = data.ping_flowewr_num || 0
|
||||
totalPoints.value = data.total_points || 0
|
||||
sign_in.value = data.sign_in || 0
|
||||
honor_list.value = data.honor_list || []
|
||||
ranking.value = data.ranking || 0
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('获取我的信息失败', e)
|
||||
}
|
||||
}
|
||||
|
||||
// 数字格式化为中文单位(万/亿)
|
||||
const formatToChineseUnit = (value, fractionDigits = 1) => {
|
||||
if (value == null || value === '' || isNaN(Number(value))) return value;
|
||||
const num = Number(value);
|
||||
const sign = num < 0 ? '-' : '';
|
||||
const abs = Math.abs(num);
|
||||
|
||||
if (abs < 10000) {
|
||||
return sign + String(abs);
|
||||
}
|
||||
if (abs < 100000000) {
|
||||
const v = (abs / 10000).toFixed(fractionDigits);
|
||||
return sign + (v.indexOf('.') > -1 ? v.replace(/\.0+$/, '').replace(/(\.\d*?)0+$/, '$1') : v) + '万';
|
||||
}
|
||||
const v = (abs / 100000000).toFixed(fractionDigits);
|
||||
return sign + (v.indexOf('.') > -1 ? v.replace(/\.0+$/, '').replace(/(\.\d*?)0+$/, '$1') : v) + '亿';
|
||||
}
|
||||
|
||||
// 方法
|
||||
const toggleFontSize = () => {
|
||||
isLargeFont.value = !isLargeFont.value;
|
||||
console.log('切换字体大小:', isLargeFont.value ? '大字版' : '普通版');
|
||||
};
|
||||
|
||||
const checkin = () => {
|
||||
console.log('签到');
|
||||
uni.showToast({
|
||||
title: '签到成功',
|
||||
icon: 'success'
|
||||
});
|
||||
};
|
||||
|
||||
const goToPage = (page) => {
|
||||
console.log('跳转到页面:', page);
|
||||
let url="";
|
||||
@@ -304,11 +472,17 @@
|
||||
url="/pages_app/patientGroup/patientGroup"
|
||||
break;
|
||||
case 'groupMessage':
|
||||
url="/pages_app/groupMessage/groupMessage"
|
||||
url="/pages_chat/groupMessage/groupMessage"
|
||||
break;
|
||||
case 'qrcode':
|
||||
url="/pages_app/myCode/myCode"
|
||||
break;
|
||||
case 'myInfo':
|
||||
url="/pages_app/personInfo/personInfo"
|
||||
break;
|
||||
case 'plan':
|
||||
url="/pages_chat/outPatient/outPatient"
|
||||
break;
|
||||
case 'myVideos':
|
||||
url="/pages_app/myVideo/myVideo"
|
||||
break;
|
||||
@@ -327,6 +501,9 @@
|
||||
case 'myPoints':
|
||||
url="/pages_app/myPoint/myPoint"
|
||||
break;
|
||||
case 'pointMall':
|
||||
url="/pages_goods/pointMall/pointMall"
|
||||
break;
|
||||
case 'myBenefits':
|
||||
url="/pages_app/myWelfare/myWelfare"
|
||||
break;
|
||||
@@ -346,7 +523,7 @@
|
||||
url="/pages_app/changeMobile/changeMobile"
|
||||
break;
|
||||
case 'benefitExchange':
|
||||
url="/pages_app/myWelfareCard/myWelfareCard"
|
||||
url="/pages_app/myWelfareCard/myWelfareCard?from=my"
|
||||
break;
|
||||
case 'bankCard':
|
||||
url="/pages_app/idcardAuth/bankCardList"
|
||||
@@ -355,8 +532,9 @@
|
||||
url="/pages_app/setting/setting"
|
||||
break;
|
||||
default:
|
||||
const push_clientid = uni.getStorageSync("push_clientid")
|
||||
uni.showToast({
|
||||
title: '功能开发中',
|
||||
title: 'push_clientid: '+push_clientid,
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
@@ -365,6 +543,131 @@
|
||||
})
|
||||
};
|
||||
|
||||
// 判断通知权限是否开启
|
||||
const isNoticeOn = () => {
|
||||
// #ifdef APP-PLUS
|
||||
if (plus.os.name == "Android") {
|
||||
var main = plus.android.runtimeMainActivity();
|
||||
var NotificationManagerCompat = plus.android.importClass("android.support.v4.app.NotificationManagerCompat");
|
||||
if (NotificationManagerCompat == null) {
|
||||
NotificationManagerCompat = plus.android.importClass("androidx.core.app.NotificationManagerCompat");
|
||||
}
|
||||
var areNotificationsEnabled = NotificationManagerCompat.from(main).areNotificationsEnabled();
|
||||
notice_on.value = areNotificationsEnabled
|
||||
return areNotificationsEnabled;
|
||||
} else if (plus.os.name == "iOS") {
|
||||
var isIosOn = undefined;
|
||||
var types = 0;
|
||||
var app = plus.ios.invoke("UIApplication", "sharedApplication");
|
||||
var settings = plus.ios.invoke(app, "currentUserNotificationSettings");
|
||||
if (settings) {
|
||||
types = settings.plusGetAttribute("types");
|
||||
plus.ios.deleteObject(settings);
|
||||
} else {
|
||||
types = plus.ios.invoke(app, "enabledRemoteNotificationTypes");
|
||||
}
|
||||
plus.ios.deleteObject(app);
|
||||
isIosOn = 0 != types;
|
||||
notice_on.value = isIosOn
|
||||
return isIosOn;
|
||||
}
|
||||
// #endif
|
||||
}
|
||||
|
||||
// 前往系统设置页面开启或关闭通知权限
|
||||
const switchPushPermissions = () => {
|
||||
const push_clientid = uni.getStorageSync("push_clientid")
|
||||
// #ifdef APP-PLUS
|
||||
let title = isNoticeOn() ? "通知权限关闭提醒" : "通知权限开启提醒";
|
||||
let content = isNoticeOn()
|
||||
? "通知权限已开启,是否前往设置关闭?"+push_clientid
|
||||
: "您还没有开启通知权限,无法接受到消息通知,是否前往设置?"+push_clientid;
|
||||
// 打开安卓系统设置页面
|
||||
let openAndroidSetting = () => {
|
||||
var main = plus.android.runtimeMainActivity();
|
||||
var pkName = main.getPackageName();
|
||||
var uid = main.getApplicationInfo().plusGetAttribute("uid");
|
||||
var Intent = plus.android.importClass("android.content.Intent");
|
||||
var Build = plus.android.importClass("android.os.Build");
|
||||
//android 8.0引导
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
var intent = new Intent("android.settings.APP_NOTIFICATION_SETTINGS");
|
||||
intent.putExtra("android.provider.extra.APP_PACKAGE", pkName);
|
||||
} else if (Build.VERSION.SDK_INT >= 21) {
|
||||
//android 5.0-7.0
|
||||
var intent = new Intent("android.settings.APP_NOTIFICATION_SETTINGS");
|
||||
intent.putExtra("app_package", pkName);
|
||||
intent.putExtra("app_uid", uid);
|
||||
} else {
|
||||
//(<21)其他--跳转到该应用管理的详情页
|
||||
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
|
||||
var uri = Uri.fromParts("package", mainActivity.getPackageName(), null);
|
||||
intent.setData(uri);
|
||||
}
|
||||
// 跳转到该应用的系统通知设置页
|
||||
main.startActivity(intent);
|
||||
};
|
||||
// 打开苹果系统设置页面
|
||||
let openIOSSetting = () => {
|
||||
var app = plus.ios.invoke("UIApplication", "sharedApplication");
|
||||
var setting = plus.ios.invoke("NSURL", "URLWithString:", "app-settings:");
|
||||
plus.ios.invoke(app, "openURL:", setting);
|
||||
plus.ios.deleteObject(setting);
|
||||
plus.ios.deleteObject(app);
|
||||
// ios18跳转不过去的解决方案
|
||||
// 方案一:使用uni.openAppAuthorizeSetting(),文档地址:https://uniapp.dcloud.net.cn/api/system/openappauthorizesetting.html
|
||||
// 方案二:待验证,AI生成;来源评论区:iOS 18以后,openURL:完全被弃用,需要用openURL:options:completionHandler:替代。
|
||||
// var app = plus.ios.invoke("UIApplication", "sharedApplication");
|
||||
// var url;
|
||||
// // 获取安全 URL(iOS 8+)
|
||||
// if (plus.ios.invoke("UIApplication", "respondsToSelector:", "openSettingsURLString")) {
|
||||
// var urlString = plus.ios.invoke("UIApplication", "openSettingsURLString");
|
||||
// url = plus.ios.invoke("NSURL", "URLWithString:", urlString);
|
||||
// } else {
|
||||
// url = plus.ios.invoke("NSURL", "URLWithString:", "app-settings:");
|
||||
// }
|
||||
// // 判断新 API 可用性(iOS 10+)
|
||||
// var supportsNewAPI = plus.ios.invoke(app, "respondsToSelector:", "openURL:options:completionHandler:");
|
||||
// if (supportsNewAPI) {
|
||||
// var options = plus.ios.newObject("NSDictionary");
|
||||
// var completion = plus.ios.newBlock(function(success) {
|
||||
// console.log("跳转结果:" + success);
|
||||
// if (!success) {
|
||||
// plus.nativeUI.toast("无法打开设置,请手动前往");
|
||||
// }
|
||||
// }, "v@:B");
|
||||
// plus.ios.invoke(app, "openURL:options:completionHandler:", url, options, completion);
|
||||
// plus.ios.deleteObject(completion);
|
||||
// } else {
|
||||
// if (plus.ios.invoke(app, "canOpenURL:", url)) {
|
||||
// plus.ios.invoke(app, "openURL:", url);
|
||||
// } else {
|
||||
// throw new Error("当前系统不支持跳转设置");
|
||||
// }
|
||||
// }
|
||||
// plus.ios.deleteObject(url);
|
||||
// plus.ios.deleteObject(app);
|
||||
};
|
||||
// 弹窗提醒开通或关闭权限,点击确认后,跳转到系统设置页面进行设置
|
||||
uni.showModal({
|
||||
title: title,
|
||||
content: content,
|
||||
showCancel: true,
|
||||
confirmColor: "#ff903f",
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
if (plus.os.name == "Android") {
|
||||
openAndroidSetting();
|
||||
} else if (plus.os.name == "iOS") {
|
||||
openIOSSetting();
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
// #endif
|
||||
}
|
||||
|
||||
|
||||
const showYearSummary = () => {
|
||||
console.log('显示年度总结');
|
||||
uni.showToast({
|
||||
@@ -376,11 +679,20 @@
|
||||
const showVersion = () => {
|
||||
uni.showModal({
|
||||
title: '版本信息',
|
||||
content: '当前版本:V4.1.4',
|
||||
content: '当前版本:V'+version,
|
||||
showCancel: false
|
||||
});
|
||||
};
|
||||
|
||||
const honnor_detail=ref({});
|
||||
const getHonorDetail=async(id)=>{
|
||||
const res = await api.gethonorDetail({
|
||||
id:id
|
||||
})
|
||||
if (res.code == 1 ) {
|
||||
honnor_detail.value = res.data;
|
||||
showHonor.value = true;
|
||||
}
|
||||
}
|
||||
const onStatsClick = (key) => {
|
||||
let url = '';
|
||||
switch (key) {
|
||||
@@ -405,10 +717,30 @@
|
||||
})
|
||||
}
|
||||
|
||||
const getIsBoundWechat = async () => {
|
||||
const res = await my_api.isBoundWechat()
|
||||
console.log(res)
|
||||
if (res.code === 200 && res.data) {
|
||||
isBoundWechat.value = res.data
|
||||
}
|
||||
}
|
||||
const isBoundWechat = ref(false);
|
||||
const isIos = ref(false);
|
||||
onShow(() => {
|
||||
console.log('我的页面显示');
|
||||
getUserInfoFromStorage() // 先从storage获取用户信息
|
||||
fetchMyInfo() // 然后从API获取最新数据
|
||||
getIsBoundWechat()
|
||||
isNoticeOn();
|
||||
checkVersion();
|
||||
getIsAccount();
|
||||
let systemInfo = uni.getSystemInfoSync();
|
||||
console.log(systemInfo)
|
||||
if(systemInfo.osName == 'ios'){
|
||||
isIos.value = true;
|
||||
}else{
|
||||
isIos.value = false;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -432,12 +764,151 @@
|
||||
|
||||
.page {
|
||||
background-color: $gray-bg;
|
||||
height: calc(100vh - 140rpx);
|
||||
margin-top:calc(var(--status-bar-height) + 44px);
|
||||
height: calc(100vh - var(--status-bar-height) - 44px);
|
||||
overflow-y: scroll;
|
||||
padding-bottom: 100rpx;
|
||||
}
|
||||
|
||||
|
||||
.flex-row{
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.new-version{
|
||||
background:#8B2316;
|
||||
color:#fff;
|
||||
font-size: 24rpx;
|
||||
margin-right: 10rpx;
|
||||
padding: 8rpx 20rpx;
|
||||
border-radius: 30rpx;
|
||||
}
|
||||
.honorwrap{
|
||||
width:100%;
|
||||
height:100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.honorbox{
|
||||
|
||||
width:80%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
min-height:200rpx;
|
||||
background:linear-gradient(to bottom, #54049b, #da4ee2);
|
||||
border-radius: 20rpx;
|
||||
padding: 20rpx;
|
||||
.row{
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr); /* 创建两列,每列等宽 */
|
||||
gap: 20px;
|
||||
.row_item{
|
||||
font-size: 26rpx;
|
||||
color: #da4ee2;
|
||||
margin-right: 20rpx;
|
||||
text-align: center;
|
||||
padding: 10rpx 20rpx;
|
||||
background:#fff;
|
||||
white-space: nowrap;
|
||||
border-radius: 10rpx;
|
||||
border:1rpx solid #da4ee2;
|
||||
}
|
||||
}
|
||||
.close_honor{
|
||||
position: absolute;
|
||||
top:20rpx;
|
||||
right:20rpx;
|
||||
}
|
||||
.honor_image{
|
||||
width:269rpx;
|
||||
height:213rpx;
|
||||
position: absolute;
|
||||
top:-76rpx;
|
||||
left:50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
.content{
|
||||
margin-top:73rpx;
|
||||
background:#fff;
|
||||
border-radius: 5rpx;
|
||||
padding: 20rpx;
|
||||
.content_title{
|
||||
font-weight: bold;
|
||||
padding: 35rpx 0 20rpx;
|
||||
text-align: center;
|
||||
font-size: 36rpx;
|
||||
color: #da4ee2;
|
||||
}
|
||||
.content_content{
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
margin-top: 20rpx;
|
||||
line-height: 1.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.signwrap{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height:100%;
|
||||
.signbox{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
z-index:0;
|
||||
.close{
|
||||
position:absolute;
|
||||
right:0;
|
||||
height:60rpx;
|
||||
width:60rpx;
|
||||
opacity: 0;
|
||||
background:#fff;
|
||||
z-index:2;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.signwrap .signcontent{
|
||||
width:100%;
|
||||
top:0;
|
||||
position: absolute;
|
||||
z-index:1;
|
||||
}
|
||||
.signwrap .signcontent .day{
|
||||
margin-top: 384rpx;
|
||||
text-align: center;
|
||||
}
|
||||
.signwrap .signtotal{
|
||||
margin-top: 30rpx;
|
||||
text-align: center;
|
||||
font-size: 30rpx;
|
||||
}
|
||||
.signwrap .signcontinue{
|
||||
font-size: 30rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.signwrap .signcontent .tip{
|
||||
margin-top: 40rpx;
|
||||
color:red;
|
||||
font-size: 28rpx;
|
||||
text-align: center;
|
||||
}
|
||||
.signwrap .signcontent .news{
|
||||
margin: 196rpx 60rpx 0;
|
||||
height: 116rpx;
|
||||
font-size: 30rpx;
|
||||
}
|
||||
.signwrap .signbg{
|
||||
width:604rpx;
|
||||
height:964rpx;
|
||||
}
|
||||
// 用户信息卡片
|
||||
.user-card {
|
||||
|
||||
@@ -459,14 +930,65 @@
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 10rpx;
|
||||
|
||||
}
|
||||
|
||||
.username {
|
||||
color: $white;
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
.user-info-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 10rpx;
|
||||
margin-left: 10rpx;
|
||||
|
||||
.username {
|
||||
color: $white;
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.honor_box {
|
||||
width: 500rpx;
|
||||
/* 根据设计可调整宽度,使内容可横向滚动 */
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.honor_wrap {
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 10rpx;
|
||||
}
|
||||
|
||||
.honor_item {
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 10rpx;
|
||||
flex: 0 0 auto;
|
||||
padding: 6rpx 12rpx;
|
||||
border: 2rpx solid rgba(255, 255, 255, 0.6);
|
||||
border-radius: 24rpx;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
.honor_image {
|
||||
width: 24rpx;
|
||||
height: 24rpx;
|
||||
}
|
||||
|
||||
.honor_text {
|
||||
font-size: 24rpx;
|
||||
color: $white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.bar_rank{
|
||||
padding:0 6rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
.stats-section {
|
||||
@@ -491,7 +1013,7 @@
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 54rpx;
|
||||
width: 160rpx;
|
||||
width: 150rpx;
|
||||
height: 56rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -508,6 +1030,27 @@
|
||||
}
|
||||
}
|
||||
|
||||
.signbox_1 {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 54rpx;
|
||||
width: 160rpx;
|
||||
height: 54rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: 1rpx solid #fc564a;
|
||||
border-right: none;
|
||||
border-radius: 30rpx 0 0 30rpx;
|
||||
background: #fff url("@/static/qd_bg.9.png")no-repeat 0 0;
|
||||
background-size: 57rpx 56rpx;
|
||||
|
||||
.sign {
|
||||
margin-left: 60rpx;
|
||||
font-size: 28rpx;
|
||||
color: #fc564a;
|
||||
}
|
||||
}
|
||||
|
||||
.stats-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -540,8 +1083,7 @@
|
||||
gap: 8rpx;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 通用section样式
|
||||
.section {
|
||||
background-color: $white;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<uni-nav-bar title="患教学堂" fixed color="#8B2316" height="140rpx" :border="false" backgroundColor="#eeeeee"></uni-nav-bar>
|
||||
<navBar :title="'患教学堂'" :showLeft="false"></navBar>
|
||||
<view class="page">
|
||||
<!-- 顶部标题栏 -->
|
||||
|
||||
@@ -10,12 +10,13 @@
|
||||
<view class="tab-item" :class="{ active: activeTab === 0 }" @click="switchTab(0)">
|
||||
患教文库
|
||||
</view>
|
||||
<view class="bar"></view>
|
||||
<view class="tab-item" :class="{ active: activeTab === 1 }" @click="switchTab(1)">
|
||||
患教视频
|
||||
</view>
|
||||
<view class="tab-item" :class="{ active: activeTab === 2 }" @click="switchTab(2)">
|
||||
<!-- <view class="tab-item" :class="{ active: activeTab === 2 }" @click="switchTab(2)">
|
||||
常见问题
|
||||
</view>
|
||||
</view> -->
|
||||
</view>
|
||||
|
||||
<!-- 筛选栏 -->
|
||||
@@ -51,7 +52,7 @@
|
||||
:class="{ active: tag.selected }"
|
||||
@click="toggleTag(index)"
|
||||
>
|
||||
{{ tag.DES }}
|
||||
{{ tag.NAME }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -74,20 +75,20 @@
|
||||
:lower-threshold="100"
|
||||
>
|
||||
<view class="article-item" v-for="(article, index) in articleList" :key="article.uuid || index" @click="goToDetail(article)">
|
||||
<image class="article-image" :src="article.image" mode="aspectFill"></image>
|
||||
<image class="article-image" :src="article.image"></image>
|
||||
<view class="article-content">
|
||||
<view class="article-title">{{ article.title }}</view>
|
||||
<view class="article-meta">
|
||||
<view class="date-tag" v-if="article.isToday">今日</view>
|
||||
<view class="date" v-else>{{ article.date }}</view>
|
||||
<view class="stats">
|
||||
<view class="stat-item">
|
||||
<view class="stat-item readnum">
|
||||
<uni-icons type="eye" size="12" color="#999"></uni-icons>
|
||||
<text>{{ article.views }}</text>
|
||||
<text>{{ formatNumber(article.views) }}</text>
|
||||
</view>
|
||||
<view class="stat-item">
|
||||
<uni-icons type="hand-up" size="13" color="#999"></uni-icons>
|
||||
<text>{{ article.likes }}</text>
|
||||
<text>{{ formatNumber(article.likes) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -107,8 +108,9 @@
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view class="empty-state" v-if="articleList.length === 0 && !loading">
|
||||
<uni-icons type="article" size="80" color="#cccccc"></uni-icons>
|
||||
<text class="empty-text">暂无文章</text>
|
||||
<empty :emptyDesc="'暂无患教信息'"></empty>
|
||||
<!-- <uni-icons type="article" size="80" color="#cccccc"></uni-icons>
|
||||
<text class="empty-text">暂无文章</text> -->
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
@@ -120,7 +122,7 @@
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { onShow } from "@dcloudio/uni-app";
|
||||
import {onShow,onLoad,onBackPress } from "@dcloudio/uni-app";
|
||||
import CustomTabbar from '@/components/tabBar/tabBar.vue';
|
||||
import upImg from "@/static/cb_up.png"
|
||||
import downImg from "@/static/cb_up.png"
|
||||
@@ -129,9 +131,24 @@
|
||||
import api from "@/api/api.js"
|
||||
import docUrl from '@/utils/docUrl.js';
|
||||
import navTo from '@/utils/navTo.js';
|
||||
|
||||
import navBar from "@/components/navBar/navBar.vue"
|
||||
import formatNumber from '@/utils/formatNumber.js';
|
||||
const type=ref(1)
|
||||
|
||||
onBackPress(() => {
|
||||
plus.runtime.quit();
|
||||
return true;
|
||||
});
|
||||
onLoad(() => {
|
||||
uni.sendNativeEvent("portraitReport",{
|
||||
msg:{
|
||||
event_nickname: "list_page",
|
||||
page_type: "患教文库",
|
||||
},
|
||||
},
|
||||
(ret) => {
|
||||
console.log(ret);
|
||||
});
|
||||
})
|
||||
// 分页相关状态
|
||||
const currentPage = ref(1);
|
||||
const pageSize = ref(10);
|
||||
@@ -147,7 +164,8 @@
|
||||
// 重置分页状态
|
||||
currentPage.value = 1;
|
||||
hasMore.value = true;
|
||||
polularScienceArticleListByKeywordsNew(true);
|
||||
articleList.value = [];
|
||||
polularScienceArticleListByKeywordsNew(false);
|
||||
};
|
||||
const loadGuideTags = async () => {
|
||||
try {
|
||||
@@ -195,9 +213,9 @@
|
||||
// 判断是否还有更多数据
|
||||
hasMore.value = newData.length >= pageSize.value;
|
||||
|
||||
if (!isRefresh) {
|
||||
currentPage.value++;
|
||||
}
|
||||
// if (!isRefresh) {
|
||||
// currentPage.value++;
|
||||
// }
|
||||
|
||||
console.log('文章数据加载成功:', articleList.value);
|
||||
}
|
||||
@@ -240,9 +258,18 @@
|
||||
|
||||
// 格式化日期
|
||||
const formatDate = (date) => {
|
||||
const today = new Date();
|
||||
const year = date.getFullYear();
|
||||
const currentYear = today.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
return `${month}-${day}`;
|
||||
|
||||
// 如果年份是今年,只显示月日,否则显示年月日
|
||||
if (year === currentYear) {
|
||||
return `${month}-${day}`;
|
||||
} else {
|
||||
return `${year}-${month}-${day}`;
|
||||
}
|
||||
};
|
||||
// 筛选标签数据
|
||||
const filterTags = ref([]);
|
||||
@@ -255,12 +282,12 @@
|
||||
|
||||
if(index==1){
|
||||
navTo({
|
||||
url: '/pages_app/patientVideo/patientVideo'
|
||||
url: '/pages_app/patientVideo/patientVideo?from=patientClass'
|
||||
})
|
||||
}else if(index==2){
|
||||
let url=encodeURIComponent('https://wx.igandan.com/wxPatient/index.htm#/problem?link=share&fromtype=doctor')
|
||||
navTo({
|
||||
url: '/pages_app/webview/webview?url='+url
|
||||
url: '/pages_app/webview/webview?url='+url+'&title='+'常见问题'
|
||||
})
|
||||
}
|
||||
};
|
||||
@@ -270,15 +297,15 @@
|
||||
// 跳转到详情页
|
||||
if (article.path) {
|
||||
uni.navigateTo({
|
||||
url: `/pages_app/webview/webview?url=${encodeURIComponent(docUrl+article.path)}&title=${encodeURIComponent(article.title)}`
|
||||
url: `/pages_app/webview/webviewClass?url=${encodeURIComponent(docUrl+article.path+'?fromtype=doctor')}&title=患教详情&uuid=${article.uuid}&sharetitle=${article.title}&summary=${article.summary}&type=huanjiao&imgPath=${encodeURIComponent(article.image)}&tags=${article.tags}`
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 跳转到搜索页面
|
||||
const goToSearch = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages_app/search/search'
|
||||
uni.sendNativeEvent('goHomeSearch',{
|
||||
msg: 'patientClass'
|
||||
});
|
||||
};
|
||||
|
||||
@@ -288,7 +315,7 @@
|
||||
await polularScienceArticleListByKeywordsNew(true);
|
||||
uni.showToast({
|
||||
title: '刷新成功',
|
||||
icon: 'success',
|
||||
icon: 'none',
|
||||
duration: 1500
|
||||
});
|
||||
};
|
||||
@@ -296,6 +323,7 @@
|
||||
// 上拉加载更多
|
||||
const onLoadMore = async () => {
|
||||
if (!hasMore.value || loading.value) return;
|
||||
currentPage.value++
|
||||
await polularScienceArticleListByKeywordsNew(false);
|
||||
};
|
||||
|
||||
@@ -319,7 +347,19 @@
|
||||
};
|
||||
|
||||
const toggleTag = (index) => {
|
||||
filterTags.value[index].selected = !filterTags.value[index].selected;
|
||||
const tag = filterTags.value[index];
|
||||
// 若当前为未选中状态,且已选满3个,则禁止再选
|
||||
if (!tag.selected) {
|
||||
const selectedCount = filterTags.value.filter(t => t.selected).length;
|
||||
if (selectedCount >= 3) {
|
||||
uni.showToast({
|
||||
title: '最多选择3个标签',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
tag.selected = !tag.selected;
|
||||
};
|
||||
|
||||
const resetFilter = () => {
|
||||
@@ -333,25 +373,26 @@
|
||||
// 检查是否有选中的标签
|
||||
const hasSelected = filterTags.value.some(tag => tag.selected);
|
||||
isFilterActive.value = hasSelected;
|
||||
|
||||
let words='';
|
||||
// 执行筛选逻辑
|
||||
if (hasSelected) {
|
||||
const selectedTags = filterTags.value.filter(tag => tag.selected).map(tag => tag.DES);
|
||||
const selectedTags = filterTags.value.filter(tag => tag.selected).map(tag => tag.NAME);
|
||||
console.log('选中的标签:', selectedTags);
|
||||
keywords.value = selectedTags.join(',');
|
||||
words = selectedTags.join(',');
|
||||
} else {
|
||||
keywords.value = '';
|
||||
words = '';
|
||||
}
|
||||
|
||||
keywords.value = words;
|
||||
// 重置分页状态
|
||||
currentPage.value = 1;
|
||||
hasMore.value = true;
|
||||
|
||||
articleList.value = [];
|
||||
showFilter.value = false;
|
||||
polularScienceArticleListByKeywordsNew(true);
|
||||
polularScienceArticleListByKeywordsNew(false);
|
||||
};
|
||||
|
||||
onShow(() => {
|
||||
|
||||
polularScienceArticleListByKeywordsNew(true);
|
||||
loadGuideTags();
|
||||
});
|
||||
@@ -369,14 +410,14 @@ $gray-dark: #666;
|
||||
$text-color: #333;
|
||||
|
||||
// 尺寸变量
|
||||
$border-radius: 8px;
|
||||
$border-radius-small: 6px;
|
||||
$padding: 15px;
|
||||
$padding-small: 10px;
|
||||
$border-radius: 16rpx;
|
||||
$border-radius-small: 12rpx;
|
||||
$padding: 30rpx;
|
||||
$padding-small: 20rpx;
|
||||
|
||||
.page {
|
||||
background-color: $gray-bg;
|
||||
height: calc(100vh - 140rpx);
|
||||
height: calc(100vh - 180rpx);
|
||||
display: flex;
|
||||
overflow-y: hidden;
|
||||
flex-direction: column;
|
||||
@@ -385,19 +426,25 @@ $padding-small: 10px;
|
||||
.top{
|
||||
width:100%;
|
||||
position: fixed;
|
||||
top:140rpx;
|
||||
top:calc(var(--status-bar-height) + 44px);
|
||||
}
|
||||
// 选项卡
|
||||
.tabs {
|
||||
background-color: $white;
|
||||
display: flex;
|
||||
border-bottom: 1px solid $gray-light;
|
||||
|
||||
align-items: center;
|
||||
border-bottom: 2rpx solid $gray-light;
|
||||
.bar{
|
||||
width: 2rpx;
|
||||
height: 98rpx;
|
||||
background-color: $gray-light;
|
||||
|
||||
}
|
||||
.tab-item {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
padding: $padding 0;
|
||||
font-size: 16px;
|
||||
font-size: 32rpx;
|
||||
color: $gray-dark;
|
||||
position: relative;
|
||||
|
||||
@@ -411,8 +458,8 @@ $padding-small: 10px;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 30px;
|
||||
height: 2px;
|
||||
width: 60rpx;
|
||||
height: 4rpx;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -425,25 +472,25 @@ $padding-small: 10px;
|
||||
padding: $padding-small $padding;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid $gray-light;
|
||||
border-bottom: 2rpx solid $gray-light;
|
||||
|
||||
.search-box {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
gap: 10rpx;
|
||||
|
||||
.search-text {
|
||||
font-size: 14px;
|
||||
font-size: 28rpx;
|
||||
color: $gray-medium;
|
||||
}
|
||||
}
|
||||
|
||||
.divider {
|
||||
width: 1px;
|
||||
height: 16px;
|
||||
background-color: $gray-medium;
|
||||
width: 2rpx;
|
||||
height: 32rpx;
|
||||
background-color: $gray-light;
|
||||
margin: 0 $padding;
|
||||
}
|
||||
|
||||
@@ -452,8 +499,8 @@ $padding-small: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 3px;
|
||||
font-size: 14px;
|
||||
gap: 6rpx;
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
@@ -466,23 +513,24 @@ $padding-small: 10px;
|
||||
// 文章列表
|
||||
.article-list {
|
||||
position: fixed;
|
||||
height:calc(100vh - 450rpx);
|
||||
top: 342rpx;
|
||||
// height:calc(100vh - var(--status-bar-height) - 44px - 207rpx);
|
||||
top: calc(var(--status-bar-height) + 44px + 188rpx);
|
||||
|
||||
padding-bottom: 130rpx;
|
||||
bottom: 128rpx;
|
||||
.article-item {
|
||||
background-color: $white;
|
||||
margin-bottom: $padding-small;
|
||||
border-radius: $border-radius;
|
||||
padding: $padding;
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
|
||||
|
||||
.article-image {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
width: 260rpx;
|
||||
height: 160rpx;
|
||||
border-radius: $border-radius-small;
|
||||
flex-shrink: 0;
|
||||
margin-right: 24rpx;
|
||||
}
|
||||
|
||||
.article-content {
|
||||
@@ -492,7 +540,7 @@ $padding-small: 10px;
|
||||
justify-content: space-between;
|
||||
|
||||
.article-title {
|
||||
font-size: 16px;
|
||||
font-size: 32rpx;
|
||||
color: $text-color;
|
||||
line-height: 1.4;
|
||||
margin-bottom: $padding-small;
|
||||
@@ -506,32 +554,40 @@ $padding-small: 10px;
|
||||
|
||||
.article-meta {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.date-tag {
|
||||
background-color: $primary-color;
|
||||
color: $white;
|
||||
font-size: 12px;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
font-size: 24rpx;
|
||||
padding: 4rpx 12rpx;
|
||||
border-radius: 6rpx;
|
||||
}
|
||||
|
||||
.date {
|
||||
font-size: 12px;
|
||||
width: 125rpx;
|
||||
font-size: 24rpx;
|
||||
color: $gray-medium;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.stats {
|
||||
display: flex;
|
||||
gap: $padding;
|
||||
|
||||
margin-left: 50rpx;
|
||||
.readnum{
|
||||
width:100rpx;
|
||||
}
|
||||
.stat-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
font-size: 12px;
|
||||
gap: 10rpx;
|
||||
|
||||
font-size: 24rpx;
|
||||
color: $gray-medium;
|
||||
text{
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -591,8 +647,8 @@ $padding-small: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
font-size: 12px;
|
||||
gap: 4rpx;
|
||||
font-size: 24rpx;
|
||||
color: $gray-medium;
|
||||
|
||||
&.active {
|
||||
@@ -600,7 +656,7 @@ $padding-small: 10px;
|
||||
}
|
||||
|
||||
text {
|
||||
font-size: 10px;
|
||||
font-size: 20rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -619,7 +675,7 @@ $padding-small: 10px;
|
||||
z-index: 9999;
|
||||
|
||||
.filter-content {
|
||||
margin-top:329rpx;
|
||||
margin-top:calc(var(--status-bar-height) + 44px + 188rpx);;
|
||||
|
||||
background-color: $white;
|
||||
// border-radius: 20rpx 20rpx 0 0;
|
||||
@@ -630,17 +686,21 @@ $padding-small: 10px;
|
||||
.filter-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20rpx;
|
||||
margin-bottom: 60rpx;
|
||||
max-height: 65vh;
|
||||
overflow-y: auto;
|
||||
|
||||
.tag-item {
|
||||
background-color: #f8f8f8;
|
||||
color: $gray-dark;
|
||||
padding: 8rpx 24rpx;
|
||||
padding: 8rpx 0rpx;
|
||||
width:152rpx;
|
||||
margin:8rpx;
|
||||
text-align:center;
|
||||
border-radius: 30rpx;
|
||||
font-size: 26rpx;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
border: 2rpx solid #efefef;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user