first commit

This commit is contained in:
Mauve
2026-09-08 17:20:27 +08:00
commit a7f2e0ca01
752 changed files with 111259 additions and 0 deletions
+597
View File
@@ -0,0 +1,597 @@
<template>
<view>
<!-- #ifdef APP-PLUS -->
<simple-watermark ref="watermark"></simple-watermark>
<!-- #endif -->
<mgmHeader ref="headerRef" />
<view class="hz-page">
<u-sticky customStyle="border-bottom:0.5px solid #f0f0f0;" bgColor="#fff">
<u-tabs :scrollable="false" :list="tabList" lineWidth="50" @change="changeTopTab" itemStyle="width:50%; height: 40px"
:activeStyle="{color: '#C39A4B'}" line-color="#C39A4B" ></u-tabs>
</u-sticky>
<!-- 搜索栏 -->
<view class="hz-spacing hz-mr-t-20 hz-flex-row">
<u-search :placeholder="searchHolder" v-model="searchInputStr" :showAction="false" bgColor="#ffffff"
borderColor="gray" style="padding-right: 8px;" @search="toSearch()"></u-search>
<view class="">
<u-icon size="28px" @click="openFilter()" :name="filterIconPath" style="margin-top: 3px;"></u-icon>
</view>
</view>
<!-- 卡片 -->
<view class="hz-mr-t-30">
<uni-card
style="padding:12px 12px 0px 12px;margin-bottom:24rpx;position:relative;box-shadow: rgba(0, 0, 0, 0.08) 0px 0px 0px 0px;" class="hz-mr-t-20 hz-font-base">
<!-- 卡片上方视图 -->
<view class="hz-flex-row hz-mr-b-20 " style="border-bottom:0.5px solid #a3a3a3;padding-bottom: 10px;">
<view>
<u-checkbox-group
v-model="checkboxValue"
placement="column"
@change="checkboxChange"
>
<u-checkbox
labelColor="#000000"
activeColor="#C39A4B"
v-for="(item, index) in checkboxList"
:key="index"
:label="item.name"
:name="item.id"
>
</u-checkbox>
</u-checkbox-group>
</view>
<!-- 卡片日期 -->
<span class="hz-font-sm" style="color:#999;margin-left: auto;">
2025-12-30 18:35
</span>
</view>
<!-- 卡片 Player Info 视图 -->
<view class="hz-flex-row" style="border-bottom:0.5px solid #a3a3a3;padding-bottom: 10px;">
<!-- 卡片左侧信息 -->
<view class="hz-font-color-black" style="line-height:160%; flex: 1; overflow: hidden; min-width: 0;">
<!-- 用户信息 -->
<view class="hz-font-md hz-mr-b-20" style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
11001960 / Chan, Tai Man / Supreme
</view>
<!-- Gaming Data -->
<view class="hz-mr-b-20 hz-flex-row">
<view>
<view>C/I:2025-01-01</view>
<view>C/I:2025-01-03</view>
</view>
<view style="margin-left: auto;">
<view>MGM Macau</view>
<view>GR,1Room</view>
</view>
</view>
<view class="hz-flex-row">
<view>
<view>Current Trip Theo</view>
<view>Last 2 Days Theo</view>
</view>
<view style="margin-left: auto;">
<view style="margin-left: auto;">900,732</view>
<view style="margin-left: auto;">110,227</view>
</view>
</view>
<view class="">R3 Theo /TPD /Actual win</view>
<view class="">3,456,789/456,785/-479,887</view>
<view class="">R6 Theo /TPD /Actual win</view>
<view class="">3,456,789/456,785/-479,887</view>
</view>
<!-- 卡片右侧按钮 -->
<view v-if="this.currentIndex===0" class="" style="width: 70px; flex-shrink: 0;">
<view class="hz-bg-primary hz-font-color-white roomCount hz-mr-t-30" style="text-align: center;" @click="handleRoomCountClick">Room Count</view>
</view>
<view v-else class="" style="width: 70px; flex-shrink: 0;">
<view class="roomCount hz-mr-t-30 hz-font-color-black"
style="border: 0.5px #C39A4B solid; text-align: center;">Approved</view>
</view>
</view>
<!-- 卡片 booking 视图 -->
<view class="hz-font-sm hz-font-color-black" style="border-bottom:0.5px solid #a3a3a3;padding-bottom: 6px;">
<view style="font-weight: bolder;">
Continuous / Overlap Booking(C/I to C/O)
</view>
<view class="">
02 Jan to 04 Jan MGM C-S2K | Conf#fzfsesr1
</view>
</view>
<!-- 卡片 Waitlist 视图 -->
<view v-show="currentIndex===0" class="hz-font-sm hz-font-color-black" style="border-bottom:0.5px solid #a3a3a3;padding-bottom: 6px;">
<view style="font-weight: bolder;">
Waitlist
</view>
<view class="">
02 Jan to 03 Jan MGM-GR
</view>
</view>
<!-- 卡片 Remark 视图 -->
<view class="hz-font-sm hz-font-color-black" style="border-bottom:0.5px solid #a3a3a3;padding-bottom: 6px;">
<view style="font-weight: bolder;">
Remarks
</view>
<view>
Wynn big guest
</view>
</view>
<!-- 卡片 Sharer 视图 -->
<view class="hz-font-sm hz-font-color-black" style="border-bottom:0.5px solid #a3a3a3;padding-bottom: 6px;">
<view style="font-weight: bolder;">
Sharer
</view>
<view>
11001983 Corso Wong
</view>
</view>
<!-- 卡片 Request Reason 视图 -->
<view class="hz-font-sm hz-font-color-black" style="margin-bottom: 5px;">
<view style="font-weight: bolder;">
Request Reason
</view>
<view>
HP
</view>
</view>
<!-- 按钮视图 -->
<view v-show="currentIndex===0" class="hz-flex-row hz-font-color-primary" style="border-top:0.5px solid #a3a3a3;">
<view class="actionButton" style="border-bottom-left-radius: 8px;" @click="handleApprovalClick">Approval</view>
<view class="actionButton" style="border-left: 0.5px solid #a3a3a3; border-right: 0.5px solid #a3a3a3;" @click="handleRejectClick">Reject</view>
<view class="actionButton" style="border-bottom-right-radius: 8px;" @click="handleDelegateClick">Delegate</view>
</view>
</uni-card>
<uni-card
style="padding:12px 12px 0px 12px;margin-bottom:24rpx;position:relative;box-shadow: rgba(0, 0, 0, 0.08) 0px 0px 0px 0px;" class="hz-mr-t-20 hz-font-base">
<!-- 卡片上方视图 -->
<view class="hz-flex-row hz-mr-b-20 " style="border-bottom:0.5px solid #a3a3a3;padding-bottom: 10px;">
<view>
<u-checkbox-group
v-model="checkboxValue"
placement="column"
@change="checkboxChange"
>
<u-checkbox
labelColor="#000000"
activeColor="#C39A4B"
v-for="(item, index) in checkboxList"
:key="index"
:label="item.name"
:name="item.id"
>
</u-checkbox>
</u-checkbox-group>
</view>
<!-- 卡片日期 -->
<span class="hz-font-sm" style="color:#999;margin-left: auto;">
2025-12-30 18:35
</span>
</view>
<!-- 卡片 Player Info 视图 -->
<view class="hz-flex-row" style="border-bottom:0.5px solid #a3a3a3;padding-bottom: 10px;">
<!-- 卡片左侧信息 -->
<view class="hz-font-color-black" style="line-height:160%; flex: 1; overflow: hidden; min-width: 0;">
<!-- 用户信息 -->
<view class="hz-font-md hz-mr-b-20" style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
11001960 / Chan, Tai Man / Supreme
</view>
<!-- Gaming Data -->
<view class="hz-mr-b-20 hz-flex-row">
<view>
<view>C/I:2025-01-01</view>
<view>C/I:2025-01-03</view>
</view>
<view style="margin-left: auto;">
<view>MGM Macau</view>
<view>GR,1Room</view>
</view>
</view>
<view class="hz-flex-row">
<view>
<view>Current Trip Theo</view>
<view>Last 2 Days Theo</view>
</view>
<view style="margin-left: auto;">
<view style="margin-left: auto;">900,732</view>
<view style="margin-left: auto;">110,227</view>
</view>
</view>
<view class="">R3 Theo /TPD /Actual win</view>
<view class="">3,456,789/456,785/-479,887</view>
<view class="">R6 Theo /TPD /Actual win</view>
<view class="">3,456,789/456,785/-479,887</view>
</view>
<!-- 卡片右侧按钮 -->
<view v-if="this.currentIndex===0" class="" style="width: 70px; flex-shrink: 0;">
<view class="hz-bg-primary hz-font-color-white roomCount hz-mr-t-30" style="text-align: center;" @click="handleRoomCountClick">Room Count</view>
</view>
<view v-else class="" style="width: 70px; flex-shrink: 0;">
<view class="roomCount hz-mr-t-30 hz-font-color-black"
style="border: 0.5px #C39A4B solid; text-align: center;">Approved</view>
</view>
</view>
<!-- 卡片 booking 视图 -->
<view class="hz-font-sm hz-font-color-black" style="border-bottom:0.5px solid #a3a3a3;padding-bottom: 6px;">
<view style="font-weight: bolder;">
Continuous / Overlap Booking(C/I to C/O)
</view>
<view class="">
02 Jan to 04 Jan MGM C-S2K | Conf#fzfsesr1
</view>
</view>
<!-- 卡片 Waitlist 视图 -->
<view v-show="currentIndex===0" class="hz-font-sm hz-font-color-black" style="border-bottom:0.5px solid #a3a3a3;padding-bottom: 6px;">
<view style="font-weight: bolder;">
Waitlist
</view>
<view class="">
02 Jan to 03 Jan MGM-GR
</view>
</view>
<!-- 卡片 Remark 视图 -->
<view class="hz-font-sm hz-font-color-black" style="border-bottom:0.5px solid #a3a3a3;padding-bottom: 6px;">
<view style="font-weight: bolder;">
Remarks
</view>
<view>
Wynn big guest
</view>
</view>
<!-- 卡片 Sharer 视图 -->
<view class="hz-font-sm hz-font-color-black" style="border-bottom:0.5px solid #a3a3a3;padding-bottom: 6px;">
<view style="font-weight: bolder;">
Sharer
</view>
<view>
11001983 Corso Wong
</view>
</view>
<!-- 卡片 Request Reason 视图 -->
<view class="hz-font-sm hz-font-color-black" style="margin-bottom: 5px;">
<view style="font-weight: bolder;">
Request Reason
</view>
<view>
HP
</view>
</view>
<!-- 按钮视图 -->
<view v-show="currentIndex===0" class="hz-flex-row hz-font-color-primary" style="border-top:0.5px solid #a3a3a3;">
<view class="actionButton" style="border-bottom-left-radius: 8px;" @click="handleApprovalClick">Approval</view>
<view class="actionButton" style="border-left: 0.5px solid #a3a3a3; border-right: 0.5px solid #a3a3a3;" @click="handleRejectClick">Reject</view>
<view class="actionButton" style="border-bottom-right-radius: 8px;" @click="handleDelegateClick">Delegate</view>
</view>
</uni-card>
</view>
</view>
<!-- 点击按钮弹窗内容 -->
<u-popup
:safeAreaInsetBottom="true"
:safeAreaInsetTop="true"
:mode="'center'"
:round="12"
:show="showMsgBox"
customStyle="width:94%; padding-top: 30px"
:closeable="!showDirBtn"
@close="closePopup()"
>
<view v-if="confirmType!=='DELEGATE'">
<view class="hz-flex-row" style="justify-content:center;align-items:center;width:100%;">
<image :src="iconPath" style="width:64px;height:64px;"></image>
</view>
<view class="hz-font-xl hz-font-bold hz-text-center" style="margin-top:20px;margin-bottom: 20px;margin-left: 15px;margin-right: 15px;white-space:pre-wrap;">
<rich-text v-html="popMsgStr" style="line-height:26px;"></rich-text>
</view>
<view @click="callConfirmFunction()" class="hz-btn hz-font-color-white hz-font-lg hz-font-bold hz-text-center"
style="width: 90%;margin: 0 auto;margin-bottom: 15px;">
{{$t("common.confirm").toUpperCase()}}
</view>
<view @click="closePopup()" class="hz-btn hz-font-color-primary hz-font-lg hz-font-bold hz-text-center"
style="width: 90%;margin: 0 auto;margin-bottom: 30px;background-color: white;border: 0.5px solid;">
{{$t("common.cancel").toUpperCase()}}
</view>
</view>
<view v-else>
<view class="hz-flex-row" style="margin: 30px 20px;">
<view class="hz-font-bold" style="align-self: center;margin-right: 10px;">
Delegate to
</view>
<view style="flex: 1;">
<view @click="showPicker = true" style="border: 0.5px solid #a3a3a3; padding: 8px; border-radius: 4px;">
{{ selectedDelegate || 'Select approver' }}
</view>
<u-picker :show="showPicker" :columns="delegateColumns" @confirm="handlePickerConfirm" @cancel="showPicker = false" :cancelText = 'cancelStr'
:confirmText = 'okStr' confirmColor = '#C39A4B' :closeOnClickOverlay = "true"></u-picker>
</view>
</view>
<view class="hz-mr-b-30">
<view @click="confirmDelegate()" class="hz-btn hz-font-color-white hz-font-lg hz-font-bold hz-text-center" style="width: 90%;margin: 0 auto;margin-bottom: 15px;">
{{$t("common.confirm").toUpperCase()}}
</view>
<view @click="closePopup()" class="hz-btn hz-font-color-primary hz-font-lg hz-font-bold hz-text-center"
style="width: 90%;margin: 0 auto;margin-bottom: 30px;background-color: white;border: 0.5px solid;">
{{$t("common.cancel").toUpperCase()}}
</view>
</view>
</view>
</u-popup>
<u-calendar closeOnClickOverlay :confirmText="confirmText" :confirmDisabledText="confirmText" color="#C39A4B"
title="" :startText="startText" :endText="endText" :show="showCalendar" :allowSameDay="true"
mode="range" @close="cancelDate" @confirm="confirmDate" :showMark="false">
</u-calendar>
<!-- 悬浮栏 - 显示选中的记录数量 -->
<view v-if="checkboxValue.length > 0" class="floating-bar">
<view class="floating-content">
<text class="selected-count">{{ checkboxValue.length }} selected</text>
<view class="floating-actions">
<view @click="handleBatchApprove" class="action-btn approve-btn">Approve</view>
<view @click="handleBatchReject" class="action-btn reject-btn">Reject</view>
</view>
</view>
</view>
</view>
</template>
<script>
import mgmHeader from "@/components/header/header.vue";
// #ifdef APP-PLUS
import SimpleWatermark from '@/components/simple-watermark/simple-watermark.vue'
// #endif
export default {
components: {
mgmHeader, // 注册组件
},
data() {
return {
title: "Approval",
currentIndex: 0,
sliderPosition: 0,
filterIconPath:"../../static/images/filter_inactive.png",
cancelStr:this.$t('common.cancel'),
okStr:this.$t('common.ok'),
searchHolder:this.$t('approval.searchStr'),
searchInputStr:'',
keyword:'',
checkboxValue:[],
confirmType:'',
userData:'',
showMsgBox:false,
showDirBtn:false,
iconPath:'',
popMsgStr:'',
showPicker:false,
confirmText:this.$t('common.confirm'),
startText:this.$t('common.start'),
endText:this.$t('common.end'),
showCalendar:false,
delegateColumns:[
[
{text: 'Mooney', value: '105868'},
{text: 'Kelly', value: '130211'},
{text: 'Eva', value: '100712'}
]
],
selectedDelegate:'',
checkboxList: [{
name: 'Request from Annie Chan',
disabled: false,
id:1
}
],
tabList: [{
name: this.$t('approval.pendingApproval'),
}, {
name: this.$t('approval.allRequest'),
}]
};
},
mounted() {},
onReady() {
// 页面首次渲染完成后,延迟触发水印更新
setTimeout(() => {
const watermarkStore = require('@/store/watermark.js');
if (watermarkStore && watermarkStore.updateWatermark) {
watermarkStore.updateWatermark(true);
}
}, 1200);
},
onShow() {
this.userData = uni.getStorageSync("userData");
if (this.userData !== null && this.userData !== undefined){
this.showTabbarByPermission(this.userData);
}
},
methods: {
click(item) {
console.log('item', item);
},
openFilter() {
this.showCalendar = true;
},
toSearch(){
console.log(this.searchInputStr);
console.log("toSearch");
},
checkboxChange(n) {
console.log('change', n);
},
handleRoomCountClick() {
this.navTo('/pages/roomCount/roomCount',0,false)
},
handleApprovalClick() {
this.navTo('/pages/playerDetail/playerDetail',0,false)
},
handleRejectClick() {
this.iconPath = '../../static/images/ico_err.png';
this.popMsgStr = this.$t("approval.submitReject") ;
this.showMsgBox = true; // 显示整个弹窗
this.confirmType = "REJECT";
},
handleDelegateClick() {
this.showMsgBox = true; // 显示整个弹窗
this.confirmType = "DELEGATE";
},
handlePickerConfirm(e) {
const selectedItem = e.value[0];
if (selectedItem) {
this.selectedDelegate = selectedItem.text;
}
this.showPicker = false;
},
changeTopTab(index) {
this.currentIndex = index.index;
},
confirmApproval(){
console.log("Approval");
this.showMsgBox = false;
},
confirmReject(){
console.log("Reject");
this.showMsgBox = false;
},
confirmDelegate(){
console.log("Delegate");
this.showMsgBox = false;
},
closePopup(){
this.showMsgBox = false;
},
handleBatchApprove(){
console.log('Batch approve:', this.checkboxValue);
this.iconPath = '../../static/images/ico_warn.png';
this.popMsgStr = this.$t("approval.submitBatchApproval") ;
this.showMsgBox = true; // 显示整个弹窗
this.confirmType = "BATCHAPPROVAL";
},
handleBatchReject(){
console.log('Batch reject:', this.checkboxValue);
this.iconPath = '../../static/images/ico_err.png';
this.popMsgStr = this.$t("approval.submitBatchReject") ;
this.showMsgBox = true; // 显示整个弹窗
this.confirmType = "BATCHREJECT";
},
cancelDate(){
this.showCalendar = false;
},
confirmDate(){
this.showCalendar = false;
},
confirmBatchApproval(){
console.log("batch app");
this.showMsgBox = false;
this.checkboxValue = [];
},
confirmBatchReject(){
console.log("batch rej");
this.showMsgBox = false;
this.checkboxValue = [];
},
callConfirmFunction(){
if(this.confirmType === "REJECT"){
this.confirmReject();
}
if(this.confirmType === "DELEGATE"){
this.confirmDelegate()
}
if(this.confirmType === "BATCHREJECT"){
this.confirmBatchReject();
}
if(this.confirmType ==="BATCHAPPROVAL"){
this.confirmBatchApproval();
}
}
},
};
</script>
<style lang="scss" scoped>
@import '../../static/css/custom.scss';
.roomCount{
border-radius: 20px;
font-size: 10px;
padding: 3px;
margin-left: auto;
}
.actionButton{
width: 33%;
text-align: center;
padding: 8px 0;
transition: background-color 0.2s ease;
}
.actionButton:active{
background-color: #cccccc;
}
.border-bt-sld{
border-bottom:0.5px;
color: #a3a3a3;
padding-bottom: 10px
}
.floating-bar{
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: #ffffff;
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
z-index: 999;
padding: 12px 16px;
border-top: 0.5px solid #a3a3a3;
}
.floating-content{
display: flex;
justify-content: space-between;
align-items: center;
}
.selected-count{
font-size: 16px;
font-weight: bold;
color: #333;
}
.floating-actions{
display: flex;
gap: 12px;
}
.action-btn{
padding: 8px 20px;
border-radius: 20px;
font-size: 14px;
font-weight: bold;
text-align: center;
}
.approve-btn{
background-color: #C39A4B;
color: #ffffff;
}
.reject-btn{
background-color: #C39A4B;
color: #ffffff;
}
::v-deep .uni-card {
margin: 10px;
padding: 0 8px;
border-radius: 12px;
overflow: hidden;
background-color: #fff;
flex: 1;
}
::v-deep .uni-card--border {
border: 0.5px solid #ccc;
}
::v-deep .uni-card--shadow {
position: relative;
box-shadow: 0 0px 0px 0px rgba(165, 165, 165, 0.2);
}
</style>
+81
View File
@@ -0,0 +1,81 @@
<template>
<view class="container">
<web-view :src="url"></web-view>
</view>
</template>
<script>
export default {
data() {
return {
//url:"https://login.microsoftonline.com/common/oauth2/v2.0/authorize?response_type=code&client_id=b4ba794c-cd0d-4033-924e-ace80b93d045&redirect_uri=https://hostcrmapp.mgm.mo/sso_callback/login&scope=openid%20profile%20email&response_mode=query",
//url:"https://adserver.willtechmacau.com/adfs/oauth2/authorize?response_type=code&client_id=0fd2d7dd-35f5-47b5-a1e2-edc13e12a5cd&redirect_uri=https://hostcrmapp.mgm.mo/sso_callback/login&scope=openid&state=STATE",
url:"https://adfs.mgm.mo/adfs/ls/IdpInitiatedSignOn?loginToRp="+encodeURIComponent('mgm_mobilecrm_app'),
intervalId:null,
newUrl:'',
siteData:null
}
},
onLoad(options) {
if (options && options.url) {
this.url = options.url;
}
this.listenUrl()
},
onShow() {
//console.log("azureLogin.vue:onShow()");
/*setTimeout(()=> {
this.checkArguments();
}, 10000);*/
this.siteData = uni.getStorageSync("siteData");
},
methods: {
listenUrl() {
var that = this;
var count = 0;
this.intervalId = setInterval(() => {
var currentUrl = that.getCurrentUrl();
that.newUrl = currentUrl;
var code = that.getCurrentQueryString(currentUrl, 'SAMLResponse');
//var code = that.getCurrentQueryString(currentUrl, 'code');
//uni.$u.toast(that.newUrl,10000);
//console.log('获取到的code:', code)
if(currentUrl.indexOf('/login')!=-1){
if (code){
clearInterval(that.intervalId);
uni.redirectTo({
url:'/pages/login/login?code='+encodeURIComponent(code)
})
}
}
}, 1000);
},
getCurrentQueryString(url, name) {
var queryString = url.substring(url.indexOf('?'));
var reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)', 'i');
var r = queryString.substr(1).match(reg);
if (r != null) {
return unescape(r[2]);
}
return null;
},
getCurrentUrl(){
var pages = getCurrentPages();
var page = pages[pages.length - 1];
var currentWebview = page.$getAppWebview();
var url = currentWebview.children()[0].getURL();
console.log("=======当前地址======", url);
return url;
}
}
}
</script>
<style>
</style>
+84
View File
@@ -0,0 +1,84 @@
<template>
<view class="container">
<web-view :src="url"></web-view>
</view>
</template>
<script>
export default {
data() {
return {
//url:"https://login.microsoftonline.com/common/oauth2/v2.0/authorize?response_type=code&client_id=b4ba794c-cd0d-4033-924e-ace80b93d045&redirect_uri=https://hostcrmapp.mgm.mo/sso_callback/login&scope=openid%20profile%20email&response_mode=query",
//url:"https://adserver.willtechmacau.com/adfs/ls/?wa=wsignout1.0&wtrealm=hostcrmapp&wreply=https://hostcrmapp.mgm.mo/sso_callback/logout",
url:"https://adfs.mgm.mo/adfs/ls/?wa=wsignout1.0&wtrealm=mgm_mobilecrm_app&wreply=https://hostcrmapp.mgm.mo/sso_callback/logout",
//url:"",
intervalId:null,
newUrl:'',
siteData:null
}
},
onLoad(options) {
if (options && options.url) {
this.url = options.url;
}
this.listenUrl()
},
onShow() {
//console.log("azureLogin.vue:onShow()");
/*setTimeout(()=> {
this.checkArguments();
}, 10000);*/
this.siteData = uni.getStorageSync("siteData");
},
methods: {
listenUrl() {
var that = this;
var count = 0;
this.intervalId = setInterval(() => {
var currentUrl = that.getCurrentUrl();
that.newUrl = currentUrl;
//var code = that.getCurrentQueryString(currentUrl, 'SAMLResponse');
//uni.$u.toast(that.newUrl,10000);
//console.log('获取到的code:', code)
if(currentUrl.indexOf('/logout')!=-1){
clearInterval(that.intervalId);
//uni.removeStorageSync("adfs_cookie");
uni.removeStorageSync("userData");
uni.removeStorageSync("token");
uni.removeStorageSync("idToken");
uni.removeStorageSync("clientId");
uni.redirectTo({
url:'/pages/login/login'
});
}
}, 1000);
},
getCurrentQueryString(url, name) {
var queryString = url.substring(url.indexOf('?'));
var reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)', 'i');
var r = queryString.substr(1).match(reg);
if (r != null) {
return unescape(r[2]);
}
return null;
},
getCurrentUrl(){
var pages = getCurrentPages();
var page = pages[pages.length - 1];
var currentWebview = page.$getAppWebview();
var url = currentWebview.children()[0].getURL();
console.log("=======当前地址======", url);
return url;
}
}
}
</script>
<style>
</style>
+311
View File
@@ -0,0 +1,311 @@
<template>
<view>
<view>
<mgmHeader ref="headerRef" />
<view v-if="showDashboardView">
<view class="hz-font-bolder hz-font-l hz-font-color-black hz-text-right"
style="margin-top:36rpx;margin-right:36rpx;">
{{$t('dashboard.dataAsOf')}} {{dataAsOf}}
</view>
<view class="hz-font-bolder hz-font-xl hz-font-color-black hz-text-left"
style="margin-top:36rpx;margin-left:36rpx;">
{{$t('dashboard.achievement')}}
</view>
<uni-card>
<view v-for="(record,index) in dashboardData" :key="index" v-show="getShowAchievement(record)"
style="margin-top:12px;">
<view class="hz-flex-row">
<view style="width:37%">
<view class="hz-font-color-dark hz-font-base">{{record.itemName}}</view>
</view>
<view v-if="record.targetValue!==0&&record.targetValue!==null&&record.targetValue!==undefined" class="hz-flex-row"
style="width:63%;justify-content: flex-end;align-items: center;align-items: center;">
<view class="hz-font-color-dark hz-font-12px" style="white-space: nowrap;">{{getTargetValue(record)}}</view>
<view class="hz-card-level" style="margin-left:4px;">LV{{record.level}}</view>
</view>
</view>
<view style="margin-top:3px;" v-if="record.targetValue!==0&&record.targetValue!==null&&record.targetValue!==undefined">
<u-line-progress :percentage="getPercentValue(record)" activeColor="#C39A4B"
height="16"></u-line-progress>
</view>
<view style="margin-top:3px;" v-else>
<u-line-progress :percentage="0" activeColor="#C39A4B" height="16"><span
style="font-size:10px;padding-left: 10px;">{{getCurrentValue(record)}}</span></u-line-progress>
</view>
</view>
<view v-show="userSegment =='IM'">
<view class="im-view-outside">
<view class="im-view-inside" style="margin-right:5px;">
<view class="im-text-gold">{{formatNumber(MFValue)}}</view>
<view class="hz-font-color-dark hz-font-base">MF Turnover</view>
</view>
<view class="im-view-inside" style="margin-left:5px;">
<view class="im-text-gold">{{formatNumber(IMValue)}}</view>
<view class="hz-font-color-dark hz-font-base">IM Turnover</view>
</view>
</view>
<view class="im-view-outside">
<view class="im-view-inside" style="margin-right:5px;">
<view class="im-text-gold">{{formatNumber(NFROValue)}}</view>
<view class="hz-font-color-dark hz-font-base">NFRO Turnover</view>
</view>
<view class="im-view-inside" style="margin-left:5px;">
<view class="im-text-gold">{{formatNumber(MRIValue)}}</view>
<view class="hz-font-color-dark hz-font -base">MRI Turnover</view>
</view>
</view>
</view>
</uni-card>
<view class="hz-font-bolder hz-font-xl hz-font-color-black hz-text-left"
style="margin-top:36rpx;margin-left:36rpx;">
{{$t('dashboard.kpi')}}
</view>
<uni-card>
<view v-for="(record,index) in dashboardData" :key="index" v-show="getShowKPI(record)"
style="margin-top:12px;">
<view class="hz-flex-row">
<view style="width:85%">
<view class="hz-font-color-dark hz-font-base">{{record.itemName}}</view>
</view>
<view class="hz-flex-row"
style="width:15%;justify-content: flex-end;align-items: center;align-items: center;">
<view class="hz-font-color-dark hz-font-12px">{{getTargetValue(record)}}</view>
</view>
</view>
<view style="margin-top:3px;">
<u-line-progress :percentage="getPercentValue(record)" activeColor="#C39A4B"
height="16"><span
style="font-size:10px;margin-right:6px;padding-left: 10px;">{{percentFormat(record.currentValue)}}</span></u-line-progress>
</view>
</view>
</uni-card>
<u-modal :content="msgStr" :show="showMsg" :confirmText="okText" :showCancelButton="false"
closeOnClickOverlay @confirm="showMsg=false" confirmColor="#C39A4B"
@close="showMsg=false"></u-modal>
</view>
</view>
<view v-if="!showDashboardView">
<view class="hz-flex-row" style="justify-content: center;margin-top:80px;">
<image src="/static/images/datanull.png" style="width:160px;height:160px;"></image>
</view>
<view class="hz-flex-row hz-font-bolder hz-font-md" style="justify-content: center;margin:20px;">
{{dataNull}}
</view>
</view>
</view>
</template>
<script>
import mgmHeader from '@/components/header/header.vue' // 引入头部组件
import {
getTodoDashboard
} from '../../api/todoApi';
import {
logout
} from '../../api/commonApi';
import {
formatDate
} from '../../common/stringUtils';
export default {
components: {
mgmHeader // 注册组件
},
data() {
return {
deviceData: null,
siteData: null,
msgStr: null,
showMsg: false,
okText: this.$t("common.ok"),
dataNull:this.$t("common.dataNotFound"),
userData: null,
dashboardData: [],
dataAsOf:"",
hasTargetValue:false,
MFValue: '0',
NFROValue: '0',
IMValue: '0',
MRIValue: '0',
showDashboardView: false,
userSegment: null
}
},
onLoad(opt) {
// 初始化界面
this.initUI();
},
onShow() {
this.playerData = uni.getStorageSync("playerData");
this.getTodoDashboardByuserId();
this.$nextTick(() => {
const header = this.$refs.headerRef;
if(header) {
header.updateUserData();
}
});
if (this.userData !== null && this.userData !== undefined)
this.showTabbarByPermission(this.userData);
},
methods: {
formatNumber(num) {
return num?.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") || "0";
},
async getTodoDashboardByuserId() {
uni.showLoading({
title: this.$t("common.loading")
});
let requestBody = "";
try {
let params = {};
requestBody = JSON.stringify(params);
const res = await getTodoDashboard(params);
if (res.data != null && res.success == true) {
if(Array.isArray(res.data.records) && res.data.records.length > 0){
this.dashboardData = res.data.records;
this.showDashboardView = true;
this.userSegment = this.dashboardData[0].userSegment;
this.hasTargetValue = this.dashboardData[0].targetValue !== null ||this.dashboardData[0].targetValue !== undefined ? true :false;
}
if(this.userSegment == 'IM' && this.userSegment !== null){
for (let i = 0; i < this.dashboardData.length; i++) {
if (this.dashboardData[i].itemName == "MF Turnover") this.MFValue = this.dashboardData[i]
.currentValue;
if (this.dashboardData[i].itemName == "IM Turnover") this.IMValue = this.dashboardData[i]
.currentValue;
if (this.dashboardData[i].itemName == "NFRO Turnover") this.NFROValue = this.dashboardData[i]
.currentValue;
if (this.dashboardData[i].itemName == "MRI Turnover") this.MRIValue = this.dashboardData[i]
.currentValue;
}
}
for (let i = 0; i < this.dashboardData.length; i++) {
if (this.dashboardData[i].type == "DataAsOf"){
this.dataAsOf = this.dashboardData[i].itemName;
}
}
} else {
this.showDashboardView = false;
if(res.errorCode == 'NO_PERMISSION'){
this.dataNull = this.$t("todo.noPermission");
}else if(res.errorCode == 'INVOKE_TODODASHBOARD_API_ERROR'){
this.dataNull = this.$t("todo.invokeError");
}else {
if(res.errorCode.toUpperCase().indexOf('NO RECORD') != -1){
this.dataNull = this.$t("common.noRecord");
}else{
this.dataNull = this.$t("todo.returnError");
}
}
}
} catch (e) {
} finally {
uni.hideLoading();
}
},
initUI() {
this.deviceData = uni.getStorageSync("deviceData");
this.siteData = uni.getStorageSync("siteData");
this.userData = uni.getStorageSync("userData");
}
},
computed: {
getShowAchievement() {
return function(record) {
if (record.type == "Achievement" && record.itemName !=="MF Turnover" && record.itemName !=="IM Turnover"
&& record.itemName !=="NFRO Turnover" && record.itemName !=="MRI Turnover") {
return true;
} else {
return false;
}
};
},
getShowKPI() {
return function(record) {
if (record.type == "KPI") {
return true;
} else {
return false;
}
};
},
getTargetValue() {
return function(record) {
if (record.targetValue != null && record.currentValue != null && record.targetValue != 0) {
if (record.type == "KPI") {
if (record.targetValue < 1) {
return (record.targetValue * 100) + "%";
} else {
return this.formatNumber(record.targetValue);
}
} else {
const targetShow = this.formatNumber(record.currentValue) + " / " + this.formatNumber(
record.targetValue);
return targetShow;
}
} else {
return null;
}
};
},
getCurrentValue(){
return function(record){
let result = this.formatNumber(parseInt(record.currentValue));
return result;
}
},
getPercentValue() {
return function(record) {
if (record.targetValue != null && record.currentValue != null && record.targetValue != 0) {
if ((Math.floor(record.currentValue / record.targetValue * 100)) <= 100) {
return Math.floor(record.currentValue / record.targetValue * 100);
} else {
return 100;
}
} else {
return null;
}
}
},
percentFormat() {
return function(val) {
if (val < 1) {
return Math.floor((val * 100) )+"%";
} else {
return this.formatNumber(Math.floor(val));
}
}
}
},
}
</script>
<style>
.im-view-outside {
display: flex;
flex-direction: row;
height: 70px;
margin-top: 10px;
}
.im-view-inside {
width: 50%;
background-color: #f0f0f0;
border-radius: 8px;
height: 95%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
}
.im-text-gold {
color: #cfa34f;
}
</style>
+48
View File
@@ -0,0 +1,48 @@
<template>
<view>
<u-navbar
:safeAreaInsetTop="true"
:placeholder="true"
:title="title"
:fixed="true"
:autoBack="false"
leftIconColor="#C39A4B"
bgColor="#C39A4B"
titleStyle="font-weight:bold;color:#ffffff;"
>
</u-navbar>
<view class="hz-flex-row" style="justify-content: center;margin-top:40px;">
<image src="/static/images/err.png" style="width:80px;height:80px;"></image>
</view>
<view class="hz-flex-row hz-font-bolder hz-font-md hz-text-center" style="margin:20px;">
{{message}}
</view>
</view>
</template>
<script>
export default {
data() {
return {
// 界面变量
userName:'',
title: this.$t("common.jailBreakTitle"),
message:this.$t("common.jailBreakMessage"),
}
},
onLoad(prop) {
},
onShow(){
},
methods: {
},
computed:{
}
}
</script>
<style>
</style>
+235
View File
@@ -0,0 +1,235 @@
<template>
<view>
<u-navbar
:safeAreaInsetTop="true"
:placeholder="true"
:title="title"
:fixed="true"
:autoBack="false"
leftIconColor="#C39A4B"
bgColor="#C39A4B"
titleStyle="font-weight:bold;color:#ffffff;"
>
<view
@click="logout"
style="margin-top:2px;margin-right:3px;"
slot="right"
>
<image src="/static/images/ico_exit.png" style="width:24px;height:24px;"></image>
</view>
<view
v-show="canViewApproval"
@click="toNotification"
style="margin-top:2px;margin-right:3px; position: relative"
slot="left"
>
<image src="/static/images/160task01.png" style="width:24px;height:24px;"/>
<u-badge :value="countAp" type="error" :offset="[-8, -8]" :show="countAp > 0" :absolute="true"></u-badge>
</view>
</u-navbar>
<view class="hz-text-center" style="margin-top:120rpx;">
<image src="../../static/images/room_book.png" style="width:128rpx;height:128rpx;"></image>
</view>
<view class="hz-font-bolder hz-font-xl hz-text-center hz-font-color-primary">
{{$t('hotel.roombooking')}}
</view>
<view class="hz-font-bolder hz-font-xl hz-text-center hz-font-color-dark" style="margin-top:160rpx;">
{{$t('findPlayer.title')}}
</view>
<view style="margin-top:30rpx;margin-right:60rpx;margin-left:60rpx;">
<u--input
v-model="searchPlayerId"
:placeholder="playerIdStr"
prefixIcon="search"
prefixIconStyle="font-size: 26px;color:#C39A4B;"
customStyle="border-radius:22px;"
type="number"
:clearable="true"
:maxlength="8"
></u--input>
<view style="margin-top:30rpx;">
<view @click="getProfileByPlayerId" class="hz-btn hz-font-color-white hz-font-lg hz-font-bold hz-text-center" >{{$t("common.search").toUpperCase()}}</view>
</view>
</view>
<u-modal :content="msgStr" :show="showMsg" :confirmText="yesText" :showCancelButton="false" closeOnClickOverlay @confirm="confirmMsg" confirmColor="#C39A4B" @cancel="confirmMsg" @close="confirmMsg"></u-modal>
</view>
</template>
<script>
import { getApprovalCount } from '../../api/approvalApi.js';
import {playerLookup} from '../../api/mgmApi.js'
import mgmHeader from '@/components/header/header.vue'
export default {
components: {
mgmHeader // 注册组件
},
data() {
return {
// 数据变量
playerIdStr: this.$t("common.playerId"),
searchPlayerId: '',
userData:null,
deviceData:null,
siteData:null,
countAp: '',
// 定时器变量
approvalCountTimer: null,
// 页面激活态(避免残留定时器回调在非当前页继续请求)
isFindPlayerActive: false,
// 界面变量
showMsg:false,
title: '',
msgStr:'',
yesText:this.$t("common.ok"),
canViewApproval:false
}
},
onLoad() {
// 初始化界面
//uni.setLocale("en") //測試切換語言en or zh-Hant
this.initUI();
this.siteData = uni.getStorageSync("siteData");
},
onShow() {
this.isFindPlayerActive = true;
this.getApporvalCount();
this.userData = uni.getStorageSync("userData");
this.searchPlayerId = '';
this.playerIdStr=this.$t("common.playerId");
if(this.userData!==null && this.userData!==undefined){
this.title = this.userData.userName+','+this.userData.userId;
this.showTabbarByPermission(this.userData);
}
this.isLogin();
this.$nextTick(() => {
const header = this.$refs.headerRef;
if(header) {
header.updateUserData();
}
});
// 切回页面时,先停后启,避免存在残留定时器
this.stopApprovalCountTimer();
this.startApprovalCountTimer();
if(this.userData.canViewApproval!==undefined && this.userData.canViewApproval==true) this.canViewApproval = true;
},
onHide() {
this.isFindPlayerActive = false;
// 切到其他页面时立即停止定时器,避免后台持续调用
this.stopApprovalCountTimer();
},
onUnload() {
this.isFindPlayerActive = false;
// 页面销毁时兜底清理
this.stopApprovalCountTimer();
},
onReady(){
},
methods: {
startApprovalCountTimer() {
if (!this.approvalCountTimer) {
this.approvalCountTimer = setInterval(() => {
// 双保险:非当前页不请求
if (this.isFindPlayerActive) {
this.getApporvalCount();
}
}, 30000); // 30秒
}
},
stopApprovalCountTimer() {
if (this.approvalCountTimer) {
clearInterval(this.approvalCountTimer);
this.approvalCountTimer = null;
}
},
// 数据方法
async getProfileByPlayerId(){
if(!this.searchPlayerId){
uni.$u.toast(this.$t("findPlayer.alert"));
return;
}
uni.showLoading({
title: this.$t("common.loading")
});
try{
let params = {playerId:this.searchPlayerId,userId:this.userData.userId};
const res = await playerLookup(params);
if(res.success == true){
if(res.data !== null){
if(res.data.codedFlag==1){
uni.setStorageSync("playerData",res.data);
uni.hideLoading();
this.navTo("/booking/bookingRecord/bookingRecord",0,false);
}else{
this.msgStr = this.$t("findPlayer.noPermission");
this.showMsg = true;
}
}
}else{
this.msgStr = res.message;
this.showMsg = true;
}
}catch(e){
console.error(e);
uni.$u.toast(e.errMsg,5000);
var request = e.config!=undefined?JSON.stringify(e.config):"";
var response = e.data!=undefined?JSON.stringify(e.data):"";
await this.writeLog(this.userData.userId,"find_player","","[APP][findPlayer.vue][getProfileByPlayerId][Catch Exception]"+e.errMsg,request,response,1,false);
}finally{
uni.hideLoading();
}
},
async getApporvalCount(){
if (!this.isFindPlayerActive) return;
try{
let params = {};
const res = await getApprovalCount(params);
if(res.success == true){
if(res.data !== null){
this.countAp = res.data;
}
}
}catch(e){
console.error(e);
uni.$u.toast(e.errMsg,5000);
var request = e.config!=undefined?JSON.stringify(e.config):"";
var response = e.data!=undefined?JSON.stringify(e.data):"";
await this.writeLog(this.userData.userId,"find_player","","[APP][findPlayer.vue][getProfileByPlayerId][Catch Exception]"+e.errMsg,request,response,1,false);
}finally{
uni.hideLoading();
}
},
// 界面方法
confirmMsg(){
this.msgStr = '';
this.showMsg = false;
},
toNotification(){
this.navTo("/pages/notification/notification",0,false)
},
clearSearchInput(){
this.searchPlayerId = '';
uni.removeStorageSync("playerData");
},
initUI() {
this.deviceData = uni.getStorageSync("deviceData");
},
logoutWithADFSApp() {
var wv = plus.webview.create(ADFS_LOGOUT_URL,'IDP');
wv.show();
},
},
}
</script>
<style>
.u-border {
border-width: 0.5px !important;
border-color: #434343 !important;
border-style: solid;
height:30px;
}
</style>
+112
View File
@@ -0,0 +1,112 @@
<!--sso login-->
<template>
<view>
<view class="hz-text-center" style="padding-top:200rpx;">
<image src="../../static/images/logo.png" style="width:138rpx;height:140rpx;"></image>
</view>
<view class="hz-font-bolder hz-font-xl hz-text-center hz-font-color-primary">
{{$t('common.appName')}}
</view>
<view class="hz-font-bolder hz-font-xl hz-text-center hz-font-color-dark" style="margin-top:100rpx;">
{{$t('common.userLogin')}}
</view>
<view style="margin-top:30rpx;margin-right:60rpx;margin-left:60rpx;">
<u--input
v-model="userId"
placeholder="Login User ID"
prefixIcon="account"
prefixIconStyle="font-size: 20px;color:#C39A4B;"
customStyle="border-radius:22px;"
></u--input>
</view>
<view style="margin-top:30rpx;margin-right:60rpx;margin-left:60rpx;">
<u--input
v-model="password"
placeholder="Login Password"
prefixIcon="lock-open"
:password="true"
prefixIconStyle="font-size: 20px;color: #C39A4B"
customStyle="border-radius:22px;"
></u--input>
</view>
<view style="margin-top:30rpx;margin-right:60rpx;margin-left:60rpx;">
<view @click="internalLogin" class="hz-btn hz-font-color-white hz-font-lg hz-font-bold hz-text-center" >{{$t("common.loginsso")}}</view>
</view>
<u-modal :content="msgStr" :show="showMsg" :confirmText="yesText" closeOnClickOverlay @confirm="confirmMsg" confirmColor="#C39A4B" @cancel="cancelMsg" @close="cancelMsg"></u-modal>
</view>
</template>
<script>
import config from '../../common/config.js'
export default {
data() {
return {
// 数据变量
userId: '',
password:'',
// 界面变量
msgStr:'',
showMsg:false,
yesText:this.$t("common.ok"),
}
},
onLoad(options) {
if(options && options.errorCode){
if(options.errorCode=='FormLoginFailed'){
this.msgStr = this.$t("common.loginerror");
}
else if(options.errorCode=='MFALoginFailed'){
this.msgStr = this.$t("common.mfaerror");
}
else
{
this.msgStr = this.$t("common.loginfail");
}
this.showMsg = true;
}else{
this.showMsg = false;
}
},
onShow() {
},
methods: {
// 数据方法
async internalLogin(){
// 判断用户名密码是否为空
if(this.userId!='' && this.password!=''){
if(this.userId.indexOf('@')==-1){
this.userId = this.userId + '@mgm.mo';
}
uni.redirectTo({
url:'/pages/samlLogin/samlLogin?uid=' + encodeURIComponent(this.userId) + '&pwd=' + encodeURIComponent(this.password)
})
}
else{
this.msgStr = this.$t("common.inputUserNameAndPassword");
this.showMsg = true;
}
},
// 界面方法
initUI() {
},
confirmMsg(){
this.showMsg = false;
},
cancelMsg(){
this.showMsg = false;
}
},
}
</script>
<style>
.u-border {
border-width: 0.5px !important;
border-color: #434343 !important;
border-style: solid;
height:30px;
}
</style>
+408
View File
@@ -0,0 +1,408 @@
<template>
<view>
<view class="hz-text-center" style="padding-top:200rpx;">
<image src="../../static/images/logo.png" style="width:138rpx;height:140rpx;"></image>
</view>
<view class="hz-font-bolder hz-font-xl hz-text-center hz-font-color-primary">
{{$t('common.appName')}}
</view>
<view class="hz-font-bolder hz-font-xl hz-text-center hz-font-color-dark" style="margin-top:160rpx;">
{{$t('common.userLogin')}}
</view>
<view style="margin-top:30rpx;margin-right:60rpx;margin-left:60rpx;">
<view @click="goLogin" class="hz-btn hz-font-color-white hz-font-lg hz-font-bold hz-text-center" >{{$t("common.loginsso")}}</view>
<button type="primary" v-show="false" @click="checkVPNOpen">testVPNFunc</button>
<button type="primary" v-show="false" @click="checkInMacau">testLocationFunc</button>
<button type="primary" v-show="false" @click="testCfgDecrypt">testCfgDecrypt</button>
</view>
<view class="hz-font-sm hz-text-center" style="margin-top:30rpx;color:#999;">
<u-row>
<u-col span="6" customStyle="text-align:center;">
{{siteData.version}}
</u-col>
<u-col span="6" customStyle="text-align:center;">
<view v-if="showConnected" style="display:flex;flex-direction:row;align-items: center;justify-content:center;">
<view style="width:6px;height:6px;background-color: #009933;border-radius: 50%;"></view>
<view style="margin-left:3px;">{{$t('common.connected')}}</view>
</view>
<view v-if="!showConnected" style="display:flex;flex-direction:row;align-items: center;justify-content:center;">
<view style="width:6px;height:6px;background-color: #ff0000;border-radius: 50%;"></view>
<view style="margin-left:3px;">{{$t('common.disconnected')}}</view>
</view>
</u-col>
</u-row>
</view>
<!--<view style="margin-top:30rpx;margin-right:60rpx;margin-left:60rpx;">
<view @click="logout" class="hz-btn hz-font-color-white hz-font-lg hz-font-bold hz-text-center" >testlogout</view>
</view>-->
<!-- <u-modal :show="showCommitment" :customStyle="{height:contentHeight + 'px;overflow:auto;'}" :confirmText="confirmText" :showCancelButton="false" closeOnClickOverlay @confirm="checkInMacau" confirmColor="#C39A4B" @close="showCommitment=false">
<scroll-view scroll-y style="max-height: 400rpx; margin: 20rpx 0;">
<view class="hz-font-lg commit-content">{{commitmentText1 }}<p style="margin-top:20px;">{{commitmentText2}}</p></view>
</scroll-view>
</u-modal> -->
<view style="width: 100%;">
<u-popup :show="showCommitment" :round="10" mode="center" customStyle="margin:0rpx 40rpx;">
<view style="padding: 20px 22px 32px 22px;">
<h4 style="font-size: 18px; display: flex; justify-content: center; padding: 7px;">{{commitTitle}}</h4>
<view style="height:490px;font-size:13px;color:#333;">
<view style="margin-bottom: 24rpx;line-height: 1.7;">{{commitmentText1}}</view>
<view >{{commitmentText2}}</view>
</view>
<view >
<view @click="handleConfirm()" class="hz-btn hz-font-color-white hz-font-lg hz-font-bold hz-text-center" >{{confirmText}}</view>
</view>
</view>
</u-popup>
</view>
</view>
</template>
<script>
import {getSettings} from '../../api/commonApi.js'
import {getSSOTokenByCode,userSSOlogin,userOpenIdlogin} from '../../api/ssoApi.js'
import { userConfirm } from '../../api/todoApi.js';
import { getCfgSettingByKey } from '../../api/configApi.js';
import config from '../../common/config.js';
import { decryptCfgSettingValue } from '../../common/cfgSettingCrypto.js';
import dayjs from '../../uni_modules/uview-ui/libs/util/dayjs.js';
export default {
data() {
return {
// 数据变量
playerIdStr: this.$t("common.playerId"),
searchPlayerId: '',
userData:null,
deviceData:null,
siteData:null,
// 界面变量
title: '',
userName:'',
code:'',
commitTitle:this.$t("region.commitTitle"),
commitmentText1:this.$t("region.commitment1"),
commitmentText2:this.$t("region.commitment2"),
confirmText:this.$t("common.confirm"),
showConnected:false,
showCommitment:false,
contentHeight:600,
}
},
async onLoad(opt) {
await this.$onLaunched; // 必须等App.vue中的initCfg执行完才执行onload
// 初始化界面
//uni.setLocale("en") //測試切換語言en or zh-Hant
await this.initUI();
this.siteData = uni.getStorageSync("siteData");
if(opt!=undefined && opt!=null && opt.code!=undefined && opt.code!=''){
this.code = opt.code;
this.goLogin();
}else{
this.code = '';
}
await this.getSetting();
uni.onNetworkStatusChange(res => {
console.log("是否联网:", res.isConnected);
console.log("网络类型:", res.networkType);
this.getSetting();
});
},
onShow() {
this.siteData = uni.getStorageSync("siteData");
var userData = uni.getStorageSync("userData");
if(userData==undefined || userData==null || userData==""){
this.code='';
}
/*let idToken = uni.getStorageSync("idToken");
console.log("onshow idToken="+idToken);*/
},
methods: {
/** 调试:拉取配置并 GCM 解密 keyValues,结果见控制台(需把按钮 v-show 改为 true 或在控制台调用) */
async testCfgDecrypt(){
try{
console.log('[testCfgDecrypt] crypto.subtle:', !!(globalThis.crypto && globalThis.crypto.subtle));
const params = { keyName: 'MACAU_IP' };
const res = await getCfgSettingByKey(params);
const cfg = new config();
const cipher = res && res.data ? res.data.keyValues : '';
const plain = await decryptCfgSettingValue(cipher, cfg.allowKey);
console.log('[testCfgDecrypt] cipher:', cipher);
console.log('[testCfgDecrypt] plain:', plain);
}catch(e){
console.error('[testCfgDecrypt]', e);
}
},
async handleConfirm(){
const userData = uni.getStorageSync("userData");
const params = {
userId : userData.userId,
userName : userData.userName
};
const res = await userConfirm(params);
this.checkInMacau();
},
checkInMacau(){
this.showCommitment = false;
//用户commit之后 将userData传到后端写log
// #ifdef APP-PLUS
let siteData = uni.getStorageSync("siteData");
if(siteData.internalTag===true){
let userData= uni.getStorageSync("userData");
this.redirectByPermission(userData,true);
}
//只有外网才检查
if(siteData!==undefined && siteData!==null && siteData.internalTag===false)
{
this.checkLocationAndVPN(false);
}
// #endif
// #ifdef H5
let userData= uni.getStorageSync("userData");
this.redirectByPermission(userData,true);
// #endif
},
// 数据方法
async getSetting(){
uni.showLoading({
title: this.$t("common.loading")
});
let that = this;
// 获取是否mockSSO配置
await getSettings({}).then((res) => {
try{
if(res!=null && res.success == true){
if(res.data!=null){
const settings = res.data;
//console.log('SETTING');
//console.log(settings);
for(let i=0;i<settings.length;i++){
const setting = settings[i];
if(setting.settingKey == 'APP_MOCK_SSO'){
if(setting.settingValue == "1")
that.siteData.mockSSO = true;
else
that.siteData.mockSSO = false;
if(setting.internalNetwork == true)
that.siteData.internalTag = true;
else
that.siteData.internalTag = false;
}
else if(setting.settingKey == 'APP_MOCK_TPD'){
if(setting.settingValue == "1")
that.siteData.mockTPD = true;
else
that.siteData.mockTPD = false;
}
}
//that.siteData.mockSSO = true;
//that.siteData.internalTag = true;
that.showConnected = true;
}else{
that.showConnected = false;
}
uni.setStorageSync('siteData', that.siteData);
that.$isResolve();
}else{
that.showConnected = false;
}
}catch(e){
console.error(e);
uni.$u.toast(e.errMsg,5000);
that.writeLog("","login","","[APP][login.vue][getSetting][Parse Exception]"+e.errMsg,"","",1,false);
uni.hideLoading();
}
finally{
uni.hideLoading();
}
})
.catch(error => {
console.log(error)
if(error.errMsg.indexOf('-1')>0){
that.showConnected = false;
}
uni.$u.toast(error.errMsg,5000);
var request = error.config!=undefined?JSON.stringify(error.config):"";
var response = error.data!=undefined?JSON.stringify(error.data):"";
that.writeLog("","login","","[APP][login.vue][getSetting][Catch Exception]"+error.errMsg,request,response,1,false);
uni.hideLoading();
});
},
logout(){
uni.redirectTo({
url:'/pages/azureLogout/azureLogout'
//url:'/pages/adfs/adfs'
});
},
async getTokenByCodeMock(code){
uni.showLoading({
title: this.$t("common.loading")
});
try{
let params = {
code:code,
mockSSO:this.siteData.mockSSO,
};
let that = this;
const res = await getSSOTokenByCode(params);
if(res.success === true){
if(res !== null){
uni.setStorageSync("token",res.accessToken);
uni.setStorageSync("userData",res.userData);
uni.setStorageSync("clientId",res.clientId);
uni.setStorageSync("timeOut",dayjs().add(30,'day').unix());
uni.setStorageSync("tokenTime",dayjs().unix());
uni.setStorageSync("refreshToken",res.refreshToken);
// uni.setStorageSync("timeOut",dayjs().add(1,'day').unix());//For test
// uni.setStorageSync("timeOut",dayjs().add(30, 'minutes').unix());
//this.navTo('/pages/dashboard/dashboard',1,false);
// that.redirectByPermission(res.data.userData,true);
that.showCommitment = true;
}else{
uni.$u.toast(res.message,5000);
this.writeLog("","getTokenByCodeMock","","[APP][login.vue][getTokenByCodeMock]data is null;"+res.message,"","",1,false);
}
}else{
uni.$u.toast(res.message,5000);
this.writeLog("","getTokenByCodeMock","","[APP][login.vue][getTokenByCodeMock]return false;"+res.message,"","",1,false);
}
}catch(e){
// write exception log
console.error(e);
uni.$u.toast(e.errMsg,5000);
this.writeLog("","getTokenByCodeMock","","[APP][login.vue][getTokenByCodeMock][Catch Exception]"+e.errMsg,"","",1,false);
}finally{
uni.hideLoading();
}
},
async getTokenByCode(code){
uni.showLoading({
title: this.$t("common.loading")
});
try{
code = code.replaceAll(" ", "+");
let params = {};
params = {
SAMLResponse:code,
};
let that = this;
const res = await userSSOlogin(params);
if(res.success === true){
if(res !== null){
uni.setStorageSync("idToken",res.idToken);
uni.setStorageSync("token",res.token);
uni.setStorageSync("userData",res.userData);
uni.setStorageSync("clientId",res.clientId);
uni.setStorageSync("timeOut",dayjs().add(30,'day').unix());
uni.setStorageSync("tokenTime",dayjs().unix());
uni.setStorageSync("refreshToken",res.refreshToken);
that.showCommitment = true;
// uni.setStorageSync("timeOut",dayjs().add(30, 'minutes').unix());
// uni.setStorageSync("timeOut",dayjs().add(1,'day').unix());//For test
//根据权限进行页面跳转
//that.redirectByPermission(res.data.userData,true);
//this.navTo('/pages/dashboard/dashboard',1,false);
}else{
// write log正常不会没有返回
uni.$u.toast(res.message+" No response data.",5000);
}
}else{
// write error log
uni.$u.toast(res.message +" Server return false.",5000);
}
}catch(e){
// write exception log
console.error(e);
uni.$u.toast(e.errMsg,5000);
this.writeLog("","getTokenByCode","","[APP][login.vue][getTokenByCode][Catch Exception]"+e.errMsg,code,"",1,false);
}finally{
uni.hideLoading();
}
},
// 界面方法
async goLogin(){
await this.$onLaunched;
await this.getSetting();
const userData = uni.getStorageSync("userData");
let loginTag = false;
uni.setStorageSync("todoSwitch","true");
if(userData!==undefined && userData!==null && userData!==''){
loginTag = userData.loginTag;
}
//uni.$u.toast("LoginTag="+loginTag + ";mockSSO="+this.siteData.mockSSO);
if(loginTag===true){
this.redirectByPermission(userData);
}
// 模拟开启,没有code,默认进来的情况
if(this.siteData.mockSSO==true && this.code=='' && loginTag===false){
// 模拟SSO
uni.redirectTo({
url:'/pages/ssoLogin/ssoLogin'
})
}
// 模拟开启,有code,SSO返回code的情况
else if(this.siteData.mockSSO==true && this.code!==''){
this.getTokenByCodeMock(this.code);
}
// 真实SSO环境,没有code,默认进来的情况
else if(this.siteData.mockSSO==false && this.code=='' && loginTag===false)
{
if(this.siteData.internalTag==true)
{
console.log('INTERNAL LOGIN');
uni.redirectTo({
url:'/pages/idpLogin/idpLogin'
});
}
else
{
console.log('AZURE LOGIN');
uni.redirectTo({
url:'/pages/azureLogin/azureLogin'
});
}
}
// 真实SSO环境,有code,SSO返回code的情况
else if(this.siteData.mockSSO==false && this.code!=='')
{
this.getTokenByCode(this.code);
}
},
clearSearchInput(){
this.searchPlayerId = '';
uni.removeStorageSync("playerData");
},
initUI() {
this.deviceData = uni.getStorageSync("deviceData");
this.contentHeight = this.deviceData.windowHeight - 60;
},
},
}
</script>
<style>
.commit-content{
line-height: 1.5; /* 增加行间距提升可读性 */
color: #333; /* 深灰提升阅读舒适度 */
max-height: 500px;
overflow-y: auto!important;
}
.custom-scrollbar::-webkit-scrollbar {
width: 6px;
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 3px;
}
</style>
+235
View File
@@ -0,0 +1,235 @@
<template>
<view>
<u-navbar
:safeAreaInsetTop="true"
:placeholder="true"
:title="title"
:fixed="true"
:autoBack="true"
leftIconColor="#000000"
bgColor="#ffffff"
titleStyle="font-weight:bold;color:#000000;"
>
<view style="margin-top:2px;margin-right:3px;" slot="right">
<u-icon
v-if="hasSelectedDate"
@click="clearDateFilter"
size="28px" name="../../static/images/filter-clear.svg"
></u-icon>
</view>
</u-navbar>
<!-- 搜索栏 -->
<view class="hz-spacing hz-mr-t-20 hz-flex-row">
<u-search :placeholder="searchHolder" v-model="searchInputStr" :showAction="false" bgColor="#ffffff" :label="searchLabel"
borderColor="gray" style="padding-right: 8px" @search="searchApprovalByPlayerId()"></u-search>
<u-icon name="calendar" :size="45" :color="calendarIconColor" @click="showCalendar = true"></u-icon>
</view>
<view v-if="!showApprovalList">
<view class="hz-flex-row" style="justify-content: center;margin-top:80px;">
<image src="/static/images/datanull.png" style="width:160px;height:160px;"></image>
</view>
<view class="hz-flex-row hz-font-bolder hz-font-md" style="justify-content: center;margin:20px;">
{{dataNull}}
</view>
</view>
<view v-else>
<uni-card v-for="(approvalRecord,index) in approvalList" :key="index" @click="toDetail(approvalRecord.playerId)">
<view class="notification-content">
<view class="icon-container">
<image src="/static/images/task.png" class="task-icon"></image>
</view>
<view class="notification-info">
<view class="notification-title">
{{ $t("notification.pendingApprovalRequest") }}
</view>
<view class="notification-date">
{{approvalRecord.createdAt}}
</view>
</view>
<view class="notification-time">
{{getTimeAgo(approvalRecord.createdAt)}}
</view>
</view>
<view class="notification-detail">
{{approvalRecord.memberLastName}}, {{approvalRecord.playerId}} ({{approvalRecord.tier}}), {{approvalRecord.propertyName}}, {{approvalRecord.roomTypeCode}}, {{approvalRecord.checkinDate}} to {{approvalRecord.checkoutDate}}, {{approvalRecord.booker}}
</view>
</uni-card>
</view>
<u-calendar :show="showCalendar" @close="showCalendar = false" :confirmText="confirmText" :confirmDisabledText="confirmText"
color="#C39A4B" :allowSameDay="true" :startText="startText" :endText="endText" mode="range" :showMark="false"
title="" @confirm="handleFilterDate" :defaultDate="selectedDate">
</u-calendar>
</view>
</template>
<script>
import {getApprovalList} from '../../api/approvalApi.js'
export default {
data() {
return {
title:this.$t("notification.pendingApproval"),
searchInputStr:'',
userData:'',
approvalList:[],
showApprovalList:true,
dataNull:this.$t("notification.noData"),
searchHolder:this.$t("approval.searchStr"),
calendarIconColor: '#000000',
showCalendar: false,
confirmText: this.$t("common.confirm").toUpperCase(),
startText: this.$t("common.start"),
endText: this.$t("common.end"),
selectedDate: [],
searchLabel: this.$t("common.playerId")
}
},
onShow() {
this.userData = uni.getStorageSync("userData");
this.getApprovalList();
},
methods: {
handleFilterDate(e) {
this.selectedDate = e;
this.showCalendar = false;
if (Array.isArray(this.selectedDate) && this.selectedDate.length > 0) {
this.calendarIconColor = '#C39A4B';
} else {
this.calendarIconColor = '#000000';
}
this.getApprovalList();
},
clearDateFilter() {
this.selectedDate = [];
this.calendarIconColor = '#000000';
this.getApprovalList();
},
toDetail(playerId){
this.navTo("/pages/playerDetail/playerDetail?playerId="+playerId,0,false)
},
getTimeAgo(createdAt) {
if (!createdAt) return '';
const now = new Date();
const created = new Date(createdAt);
const diffMs = now - created;
const diffMins = Math.floor(diffMs / 60000);
const diffHours = Math.floor(diffMins / 60);
const diffDays = Math.floor(diffHours / 24);
if (diffMins < 1) return 'just now';
if (diffMins < 60) return diffMins + 'm';
if (diffHours < 24) return diffHours + 'h';
return diffDays + 'd';
},
async getApprovalList(){
uni.showLoading({
title: this.$t("common.loading")
});
try{
let params = {
userId: this.userData.userId,
playerId: this.searchInputStr,
rangeDate: this.selectedDate
};
const res = await getApprovalList(params);
if(res.success == true){
if(res.data !== null && res.data.length > 0){
this.approvalList = res.data;
// 提取所有 playerId 到新的 playerList 并去重
const playerIds = this.approvalList.map(item => item.playerId);
const uniquePlayerIds = [...new Set(playerIds)];
this.playerList = uniquePlayerIds.map(playerId => ({ playerId }));
uni.setStorageSync("approvalPlayerList",this.playerList);
this.showApprovalList = true;
} else {
this.showApprovalList = false;
this.dataNull = this.$t("notification.noData");
}
}else{
this.msgStr = res.message;
this.showMsg = true;
this.showApprovalList = false;
if(res.errorCode==="NO_PERMISSION") this.dataNull = this.$t("notification.noPermission");
}
}catch(e){
console.error(e);
uni.$u.toast(e.errMsg,5000);
var request = e.config!=undefined?JSON.stringify(e.config):"";
var response = e.data!=undefined?JSON.stringify(e.data):"";
await this.writeLog(this.userData.userId,"find_player","","[APP][findPlayer.vue][getProfileByPlayerId][Catch Exception]"+e.errMsg,request,response,1,false);
}finally{
uni.hideLoading();
}
},
// 搜索玩家審批請求
searchApprovalByPlayerId() {
this.getApprovalList();
}
},
computed: {
hasSelectedDate() {
return Array.isArray(this.selectedDate) && this.selectedDate.length > 0;
}
}
}
</script>
<style lang="scss" scoped>
.notification-content {
display: flex;
align-items: center;
position: relative;
}
.icon-container {
margin-right: 10px;
display: flex;
justify-content: center;
align-items: center;
}
.task-icon {
width: 32px;
height: 32px;
}
.notification-info {
flex: 1;
}
.notification-title {
font-weight: bold;
font-size: 14px;
color: #000000;
}
.notification-date {
font-weight: normal;
font-size: 12px;
color: #999;
}
.notification-time {
font-weight: normal;
font-size: 12px;
color: #C39A4B;
background-color: #FFF8E7;
padding: 2px 8px;
border-radius: 10px;
margin-left: auto;
}
.notification-detail {
font-weight: normal;
font-size: 12px;
color: #000000;
margin-top: 6px;
}
</style>
+986
View File
@@ -0,0 +1,986 @@
<template>
<view class="hz-page">
<!-- #ifdef APP-PLUS -->
<simple-watermark ref="watermark"></simple-watermark>
<!-- #endif -->
<u-navbar @leftClick="back()" :safeAreaInsetTop="true" :placeholder="true" :title="title" :fixed="true"
:autoBack="false" leftIconColor="#000000" bgColor="#fff" titleStyle="font-weight:bold;">
</u-navbar>
<view class="hz-spacing hz-bg-white" style="margin-top:8px;">
<!-- Player Info 卡片 -->
<view class="hz-card-profile hz-mr-b-30">
<view class="hz-flex-row">
<view style="width:60%">
<view class="hz-font-color-light hz-font-sm">{{$t("common.playerId")}}</view>
<view class="hz-font-bolder hz-font-lg">
{{bookingListData.length > 0 ? bookingListData[0].playerId : '-'}}</view>
</view>
<view style="width:40%">
<view class="hz-font-color-light hz-font-sm">{{$t("common.tier")}}</view>
<view class="hz-font-bolder hz-font-lg">
{{bookingListData.length > 0 ? bookingListData[0].tier : '-'}}</view>
</view>
</view>
<view class="hz-flex-row hz-mr-t-30">
<view style="width:100%">
<view class="hz-font-color-light hz-font-sm">{{$t("common.name")}}</view>
<view class="hz-font-bolder hz-font-lg">
{{bookingListData.length > 0 ? (bookingListData[0].memberTitle || '') + ' ' + (bookingListData[0].memberFirstName || '') + ' ' + (bookingListData[0].memberLastName || '') : '-'}}
</view>
</view>
</view>
</view>
<!-- Booking Info 卡片 -->
<view class="hz-pd-16 hz-font-bolder hz-font-md hz-card-extend"
style="margin-top:10px;align-items: center;">
<view class="hz-flex-row" style="align-items: center; width: 100%;">
<!-- 房间信息轮播 -->
<view style="flex: 1; margin-right: 10px; min-height: 80px;">
<!-- 房间信息轮播 -->
<view style="position: relative; height: 100px;" @touchstart="touchStart" @touchend="touchEnd">
<!-- 轮播内容 -->
<view v-for="(room, index) in roomList" :key="index"
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; transition: opacity 0.5s;"
:style="{ opacity: currentRoomIndex === index ? 1 : 0, zIndex: currentRoomIndex === index ? 1 : 0 }">
<view class="hz-flex-row" style="align-items: center;">
<!-- 多选框 -->
<view
style="width: 17px; height: 17px; display: flex; align-items: center; justify-content: center;;"
@click="toggleSelection(index)">
<view
style="width: 15px; height: 15px; border: 0.5px solid #C39A4B; transition: all 0.3s; display: flex; align-items: center; justify-content: center;border-radius: 25px;"
:style="{ backgroundColor: room.selected ? '#C39A4B' : '#ffffff' }">
<view v-if="room.selected"
style="color: white; font-size: 10px; font-weight: bold;">✔</view>
</view>
</view>
<!-- 房间信息 -->
<view style="flex: 1;margin-left: 10px;">
<!-- Edit 按钮 -->
<view @click="toEdit()" style="display:flex;justify-content:flex-end;width:100%;">
<image src="/static/images/edit_active.png"
style="width: 17px; height: 17px;" />
</view>
<view class="hz-flex-row" style="align-items: center;">
<image src="/static/images/ico_calendar.png"
style="width:16px;height:16px;"></image>
<span class="hz-font-bolder"
style="margin-left:4px;font-size: 14px;">{{room.date}}&nbsp; {{$t("common.to")}}&nbsp;
{{room.endDate}}</span>
</view>
<view class="hz-flex-row" style="margin-top:8px;">
<view class="hz-flex-row" style="align-items: center;">
<image src="/static/images/ico_room.png"
style="width:16px;height:16px;"></image>
<span class="hz-font-bolder"
style="margin-left:4px;font-size: 14px;">{{room.roomInfo}}</span>
</view>
</view>
</view>
</view>
</view>
<!-- 轮播指示器 -->
<view
style="position: absolute; bottom: 0; left: 0; width: 100%; display: flex; justify-content: center; gap: 8px;">
<view v-for="(room, index) in roomList" :key="index"
style="width: 6px; height: 6px; border-radius: 50%; transition: background-color 0.3s;"
:style="{ backgroundColor: currentRoomIndex === index ? '#C39A4B' : '#CCCCCC' }"
@click="currentRoomIndex = index"></view>
</view>
</view>
</view>
</view>
</view>
<!-- 玩家信息 -->
<view style="padding-left: 8px;padding-right: 8px;">
<view class="infoButton">
<view class="hz-flex-row dataMargin">
<view class="" style="width: 50%;font-weight: bolder">
Current Trip Theo
</view>
<view class="">
{{formatWithThousands(currentApprovalData.currentTripTheoWinAmount)}}
</view>
</view>
<view class="hz-flex-row dataMargin">
<view class="" style="width: 50%;font-weight: bolder">
Last 2 Days Theo
</view>
<view class="">
{{formatWithThousands(currentApprovalData.last2DayTheoWinAmount)}}
</view>
</view>
<view class="dataMargin">
<view class="hz-flex-row">
<view class="" style="width: 50%;font-weight: bolder">
R3 Theo/TPD/Actual win
</view>
</view>
<view class="hz-flex-row">
<view>
{{formatWithThousands(currentApprovalData.r3TheoWin)}} / {{formatWithThousands(currentApprovalData.r3TPD)}} / {{formatWithThousands(currentApprovalData.r3ActualWin)}}
</view>
</view>
</view>
<view class="hz-flex-row">
<view class="" style="width: 50%;font-weight: bolder">
R6 Theo/TPD/Actual win
</view>
</view>
<view class="hz-flex-row">
<view>
{{formatWithThousands(currentApprovalData.r6TheoWin)}} / {{formatWithThousands(currentApprovalData.r6TPD)}} / {{formatWithThousands(currentApprovalData.r6ActualWin)}}
</view>
</view>
</view>
<view class="infoButton">
<view style="font-weight: bolder">
Continuous / Overlap Booking (C/I to C/O)
</view>
<view>
{{currentApprovalData.overlapBooking}}
</view>
</view>
<!-- RoomCount -->
<view class="infoButton">
<view style="font-weight: bolder">
Room Count
</view>
<view>
{{ sortRoomCountByDate(currentApprovalData.roomCount) }}
</view>
</view>
<!-- Remark -->
<view class="infoButton">
<view style="font-weight: bolder">
Remark
</view>
<view class="">
{{roomList[currentRoomIndex] && roomList[currentRoomIndex].remark ? roomList[currentRoomIndex].remark : '-'}}
</view>
</view>
<view class="infoButton">
<view style="font-weight: bolder">
Sharer
</view>
<view class="">
{{roomList[currentRoomIndex] ? roomList[currentRoomIndex].sharer : '-'}}
</view>
</view>
<view class="infoButton">
<view style="font-weight: bolder">
Request Reason
</view>
<view class="">
{{ currentApprovalRouting.approvalReason}}
</view>
</view>
<view class="infoButton">
<view style="font-weight: bolder">
Routing Reason
</view>
<view class="">
{{currentApprovalRouting.routingReason}}
</view>
</view>
<view class="hz-mr-t-30">
<view class="hz-flex-row">
<view class="" style="width: 50%;font-weight: bolder">
Requested on
</view>
<view class="" style="font-weight: bolder">
Requested by
</view>
</view>
<view class="hz-flex-row">
<view class="" style="width: 50%;">
{{currentApprovalRouting.createdAt ? formatDate(currentApprovalRouting.createdAt) : '-'}}
</view>
<view class="" style="width: 50%;">
{{currentApprovalRouting.createdBy}}
</view>
</view>
</view>
</view>
</view>
<u-tabbar :fixed="true" :placeholder="true" :safeAreaInsetBottom="true" customStyle="z-index:100;height:58px;">
<view @click="toPreviousRecord()" class="book hz-flex-row hz-spacing"
:class="canPrevious==false? 'hz-disabled':''" style="width: 60%;">
<view class="hz-btn hz-font-color-primary hz-font-lg hz-font-bold hz-text-center"
style="background-color: #fff;border: #C39A4B solid 2px;display: flex;justify-content: center;align-items: center;">
<image src="/static/images/arrow_previous_active.png"
style="width: 25px; height: 25px;" />
</view>
</view>
<view @click="onTabApproveTap" class="book hz-flex-row hz-spacing"
:class="approvalLoading==true || roomList.filter(r => r.selected).length > 0? 'hz-disabled':''">
<view class="hz-btn hz-font-color-white hz-font-lg hz-font-bold hz-text-center">
{{$t("approval.approve")}}
</view>
</view>
<view @click="onTabRejectTap" class="book hz-flex-row hz-spacing"
:class="approvalLoading==true || roomList.filter(r => r.selected).length > 0? 'hz-disabled':''">
<view class="hz-btn hz-font-color-white hz-font-lg hz-font-bold hz-text-center">
{{$t("bookingDetail.reject")}}
</view>
</view>
<view @click="toNextRecord()" class="book hz-flex-row hz-spacing" :class="canNext==false? 'hz-disabled':''"
style="width: 60%">
<view class="hz-btn hz-font-color-primary hz-font-lg hz-font-bold hz-text-center"
style="background-color: #fff;border: #C39A4B solid 2px;display: flex;justify-content: center;align-items: center;">
<image src="/static/images/arrow_next_active.png"
style="width: 25px; height: 25px;" />
</view>
</view>
</u-tabbar>
<u-popup :safeAreaInsetBottom="true" :closeOnClickOverlay="false" :safeAreaInsetTop="true" :mode="'center'" :round="12" :show="showMsgBox"
customStyle="width:94%; padding-top: 30px" @close="closePopup()">
<view>
<view class="hz-flex-row" style="justify-content:center;align-items:center;width:100%;">
<image :src="iconPath" style="width:64px;height:64px;"></image>
</view>
<view class="hz-font-xl hz-font-bold hz-text-center"
style="margin-top:20px;margin-bottom: 20px;margin-left: 15px;margin-right: 15px;white-space:pre-wrap;">
<rich-text v-html="popMsgStr" style="line-height:26px;"></rich-text>
</view>
<view v-if="showSubmit">
<view @click="currentAction === 'approve' ? confirmApproval() : confirmReject()"
class="hz-btn hz-font-color-white hz-font-lg hz-font-bold hz-text-center"
style="width: 90%;margin: 0 auto;margin-bottom: 15px;">
{{$t("common.confirm").toUpperCase()}}
</view>
<view @click="closePopup()"
class="hz-btn hz-font-color-primary hz-font-lg hz-font-bold hz-text-center"
style="width: 90%;margin: 0 auto;margin-bottom: 30px;background-color: white;border: 0.5px solid;">
{{$t("common.cancel").toUpperCase()}}
</view>
</view>
<view v-else>
<view @click="backToNotification()"
class="hz-btn hz-font-color-white hz-font-lg hz-font-bold hz-text-center"
style="width: 90%;margin: 0 auto;margin-bottom: 15px;">
{{$t("approval.backToRecord").toUpperCase()}}
</view>
</view>
</view>
</u-popup>
<u-popup :safeAreaInsetBottom="true" :safeAreaInsetTop="true" :mode="'center'" :round="12"
:show="showReturnMsgBox">
<view :style="'width:'+ windowWidth + 'px;'" style="padding:20px;">
<view class="hz-flex-row" style="justify-content: center;">
<image :src="iconPath" style="width:64px;height:64px;"></image>
</view>
<view class="hz-font-md hz-font-bold hz-text-center" style="margin-top:20px;">
<rich-text v-html="msgStr" style="line-height:26px;"></rich-text>
</view>
<view @click="showReturnMsgBox = false"
class="hz-btn hz-font-color-white hz-font-lg hz-font-bold hz-text-center" style="margin-top:20px;">
{{buttonStr}}
</view>
</view>
</u-popup>
<u-calendar closeOnClickOverlay :confirmText="confirmText" :confirmDisabledText="confirmText" color="#C39A4B"
title="" :startText="startText" :endText="endText" :show="showCalendar" :allowSameDay="true" mode="range"
:minDate="selectAbleStart" :maxDate="selectAbleEnd" :defaultDate="[calendarStartDate, calendarEndDate]"
@close="cancelDate" @confirm="confirmDate" :showMark="false">
</u-calendar>
<!-- 悬浮栏 - 显示选中的记录数量 -->
<view v-if="roomList.filter(r => r.selected).length > 0" class="floating-bar">
<view class="floating-content">
<text class="selected-count">{{ roomList.filter(r => r.selected).length }} {{ $t("common.selected") }}</text>
<view class="floating-actions">
<view @click="handleBatchApprove" class="action-btn approve-btn">{{ $t("approval.batchApprove") }}</view>
<view @click="handleBatchReject" class="action-btn reject-btn">{{ $t("approval.batchReject") }}</view>
</view>
</view>
</view>
</view>
</template>
<script>
import {
batchSubmitApproval,
batchSubmitReject,
getApprovalData,
rejectApproval,
submitApproval
} from '../../api/approvalApi';
import {
times
} from '../../uni_modules/uview-ui/libs/function/digit';
// #ifdef APP-PLUS
import SimpleWatermark from '@/components/simple-watermark/simple-watermark.vue'
// #endif
export default {
components: {
// #ifdef APP-PLUS
SimpleWatermark
// #endif
},
data() {
return {
title: "Detail",
approvalLoading: false,
iconPath: '',
popMsgStr: '',
showMsgBox: false,
confirmText: this.$t('playerDetail.confirm'),
startText: this.$t('common.in'),
endText: this.$t('common.out'),
// 日历日期范围
calendarStartDate: '',
calendarEndDate: '',
selectAbleStart: '',
selectAbleEnd: '',
searchPlayerId: '',
showCalendar: false,
showSubmit: true,
deviceData: '',
canNext: false,
canPrevious: true,
// 玩家导航
currentPlayerIndex: 0,
approvalPlayerList: [],
showReturnMsgBox: false,
// 触摸事件
touchStartX: 0,
touchEndX: 0,
buttonStr: '',
msgStr: '',
windowWidth: 0,
// 房间信息轮播
currentRoomIndex: 0,
// 当前操作类型:approve 或 reject
currentAction: '',
bookingListData: [],
approvalGamingDataList: [],
approvalRoutingList: [],
roomList: []
}
},
computed: {
// 获取当前房间对应的 approvalGamingData
currentApprovalData() {
const currentRoom = this.roomList[this.currentRoomIndex];
if (currentRoom && currentRoom.id) {
const approvalData = this.approvalGamingDataList.find(
item => item.bookingId == currentRoom.id
);
return approvalData || {};
}
return {};
},
// 当前轮播房间对应的 cfgApprovalRouting(按 bookingId 匹配)
currentApprovalRouting() {
const currentRoom = this.roomList[this.currentRoomIndex];
if (!currentRoom || currentRoom.id == null) return {};
const row = this.approvalRoutingList.find(
item => item.bookingId == currentRoom.id
);
return row || {};
}
},
methods: {
formatWithThousands(value) {
if (value === null || value === undefined || value === '') return '-';
const str = String(value).trim();
// 已经是合法千分位格式(支持负数和小数)时,直接返回
if (/^-?\d{1,3}(,\d{3})*(\.\d+)?$/.test(str)) return str;
// 未格式化数字(或带逗号但格式不标准)时,去逗号后重新格式化
const normalized = str.replace(/,/g, '');
if (/^-?\d+(\.\d+)?$/.test(normalized)) {
const n = Number(normalized);
return n.toLocaleString('en-US');
}
return value;
},
// 将形如:2025-03-18 +12 (W +2) | 2025-03-16 + 16 ... 按日期升序排序
sortRoomCountByDate(roomCountValue) {
if (roomCountValue === null || roomCountValue === undefined || roomCountValue === '') return '-';
const input = String(roomCountValue).trim();
if (!input) return '-';
const dateRegex = /^(\d{4}-\d{2}-\d{2})/;
// 新格式:类似 2026-04-01(100,W+0),2026-04-03(100,W+0),...(逗号分隔,括号内部也包含逗号)
// 不能简单 split(','),提取每个完整的 “YYYY-MM-DD(... )” 片段再排序
const parts = input.match(/\d{4}-\d{2}-\d{2}\s*[^)]*\([^)]*\)/g);
if (!parts || parts.length <= 1) return input;
const enriched = parts.map((part) => {
const m = part.match(dateRegex);
const dateStr = m ? m[1] : '';
const time = dateStr ? Date.parse(dateStr) : Number.POSITIVE_INFINITY;
return { part, time };
});
enriched.sort((a, b) => {
if (a.time === b.time) return a.part.localeCompare(b.part);
return a.time - b.time;
});
// 新格式分段之间通常是逗号
return enriched.map(x => x.part).join(',');
},
back() {
this.navBack();
},
toEdit() {
// 获取当前轮播图显示的房间信息
const currentRoom = this.roomList[this.currentRoomIndex];
if (currentRoom) {
// 从 bookingListData 中获取原始日期范围作为可选范围
// 因为 roomList 中的日期可能已经被修改过
const originalBooking = this.bookingListData.find(item => item.id === currentRoom.id);
if (originalBooking) {
// 可选日期范围保持为原始日期
// 注意:selectAbleStart 和 selectAbleEnd 只在数据加载时设置,这里不再修改
// 但默认显示的日期应该是当前房间的当前日期(可能已经被修改过的)
this.calendarStartDate = currentRoom.checkinDate;
this.calendarEndDate = currentRoom.checkoutDate;
}
}
this.showCalendar = true;
},
showApproval() {
// 设置当前操作类型为 approve
this.currentAction = 'approve';
this.iconPath = '../../static/images/warn.png';
this.popMsgStr = this.$t("approval.submitApproval");
this.showMsgBox = true; // 显示整个弹窗
this.showSubmit = true;
},
// 底部 Approve:有选中记录或 loading 时不进入 confirmApproval(避免仅样式置灰仍触发)
onTabApproveTap() {
if (this.approvalLoading || this.roomList.some(r => r.selected)) return;
this.confirmApproval();
},
// 底部 Reject:同上
onTabRejectTap() {
if (this.approvalLoading || this.roomList.some(r => r.selected)) return;
this.confirmReject();
},
async confirmApproval() {
if (this.approvalLoading || this.roomList.some(r => r.selected)) return;
uni.showLoading({
title: this.$t("common.loading")
});
// 获取当前轮播图显示的房间信息
const currentRoom = this.roomList[this.currentRoomIndex];
if (currentRoom) {
}
try {
let params = {
bookingId: currentRoom.id,
checkinDate: currentRoom.checkinDate,
checkoutDate: currentRoom.checkoutDate
};
const res = await submitApproval(params);
this.showMsgBox = false;
if (res.success == true) {
this.iconPath = '../../static/images/ico_suc.png';
this.popMsgStr = this.$t("bookingConfirm.approvalSuccess");
this.showMsgBox = true; // 显示整个弹窗
this.showSubmit = false;
} else {
this.buttonStr = this.$t("common.ok");
this.iconPath = '../../static/images/ico_err.png';
this.msgStr = res.message!=null?res.message:this.$t("common.approvalFail");
this.url = '';
this.showReturnMsgBox = true;
}
} catch (e) {
console.error(e);
uni.$u.toast(e.errMsg, 5000);
var request = e.config != undefined ? JSON.stringify(e.config) : "";
var response = e.data != undefined ? JSON.stringify(e.data) : "";
await this.writeLog(this.userData.userId, "find_player", "",
"[APP][findPlayer.vue][getProfileByPlayerId][Catch Exception]" + e.errMsg, request,
response, 1, false);
} finally {
uni.hideLoading();
}
},
async getApprovalDetailData(playerId) {
uni.showLoading({
title: this.$t("common.loading")
});
try {
// 如果传入了 playerId,则使用传入的,否则使用 searchPlayerId
const targetPlayerId = playerId || this.searchPlayerId;
let params = {
playerId: targetPlayerId
};
const res = await getApprovalData(params);
if (res.success == true) {
if (res.data !== null) {
this.bookingListData = res.data.bookingsViewList;
this.approvalGamingDataList = res.data.approvalGamingData || [];
const routingList = res.data.cfgApprovalRoutingList;
if (Array.isArray(routingList) && routingList.length > 0) {
this.approvalRoutingList = routingList;
} else if (res.data.cfgApprovalRouting) {
this.approvalRoutingList = [res.data.cfgApprovalRouting];
} else {
this.approvalRoutingList = [];
}
// 将 bookingListData 转换为 roomList 格式
if (this.bookingListData && this.bookingListData.length > 0) {
this.roomList = this.bookingListData.map(item => {
// 格式化日期,添加星期和月份
const formatDate = (dateStr) => {
if (!dateStr) return '';
const date = new Date(dateStr);
const weekdays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
const weekday = weekdays[date.getDay()];
const year = date.getFullYear();
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const day = date.getDate().toString().padStart(2, '0');
return `(${weekday})${year}-${month}-${day}`;
};
return {
id: item.id,
bookingId: item.id, // 保存 bookingId 用于匹配 approvalGamingData
date: formatDate(item.checkinDate),
checkinDate: item.checkinDate, // 保存原始入住日期
endDate: formatDate(item.checkoutDate),
checkoutDate: item.checkoutDate, // 保存原始退房日期
roomInfo: `${item.propertyName || ''}, ${item.room || ''}`.trim(),
selected: false,
sharer: item.sharer || '-',
remark: item.remark
};
});
// 切换 player 后重置轮播索引,避免 new roomList 更短导致取不到数据
this.currentRoomIndex = 0;
// 数据加载完成后,获取当前房间的日期作为可选范围
if (this.roomList.length > 0) {
const currentRoom = this.roomList[this.currentRoomIndex];
if (currentRoom) {
this.selectAbleStart = currentRoom.checkinDate;
this.selectAbleEnd = currentRoom.checkoutDate;
}
}
}
// 更新当前玩家在列表中的位置
this.updateCurrentPlayerIndex(targetPlayerId);
uni.hideLoading();
}
} else {
this.msgStr = res.message;
this.showMsg = true;
}
} catch (e) {
console.error(e);
uni.$u.toast(e.errMsg, 5000);
var request = e.config != undefined ? JSON.stringify(e.config) : "";
var response = e.data != undefined ? JSON.stringify(e.data) : "";
await this.writeLog(this.userData.userId, "find_player", "",
"[APP][findPlayer.vue][getProfileByPlayerId][Catch Exception]" + e.errMsg, request,
response, 1, false);
} finally {
uni.hideLoading();
}
},
closePopup() {
this.showMsgBox = false;
},
cancelDate() {
this.showCalendar = false;
},
confirmDate(e) {
// e 是选择的日期数组,格式为 [开始日期, 结束日期]
if (e) {
const startDate = e[0];
const endDate = e[e.length - 1];
// 获取当前轮播图显示的房间信息
const currentRoom = this.roomList[this.currentRoomIndex];
if (currentRoom) {
// 更新原始日期字段
currentRoom.checkinDate = startDate;
currentRoom.checkoutDate = endDate;
// 更新显示用的格式化日期
// 这里需要重新格式化日期,保持与之前相同的格式
const formatDateForDisplay = (dateStr) => {
if (!dateStr) return '';
const date = new Date(dateStr);
const weekdays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
const weekday = weekdays[date.getDay()];
const year = date.getFullYear();
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const day = date.getDate().toString().padStart(2, '0');
return `(${weekday})${year}-${month}-${day}`;
};
const formatEndDateForDisplay = (dateStr) => {
if (!dateStr) return '';
const date = new Date(dateStr);
const weekdays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
const weekday = weekdays[date.getDay()];
const year = date.getFullYear();
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const day = date.getDate().toString().padStart(2, '0');
return `(${weekday})${year}-${month}-${day}`;
};
currentRoom.date = formatDateForDisplay(startDate);
currentRoom.endDate = formatEndDateForDisplay(endDate);
}
}
this.showCalendar = false;
},
// 日期格式化方法
formatDate(timestamp) {
if (!timestamp) return '-';
const date = new Date(timestamp);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
},
// 更新当前玩家在列表中的位置
updateCurrentPlayerIndex(playerId) {
if (this.approvalPlayerList && this.approvalPlayerList.length > 0) {
const currentIndex = this.approvalPlayerList.findIndex(item => item.playerId === playerId);
if (currentIndex !== -1) {
this.currentPlayerIndex = currentIndex;
// 更新 canPrevious 和 canNext
this.canPrevious = currentIndex > 0;
this.canNext = currentIndex < this.approvalPlayerList.length - 1;
}
}
},
backToNotification() {
this.navBack();
},
toPreviousRecord() {
if (this.canPrevious === true) {
// 导航到上一个玩家
const previousIndex = this.currentPlayerIndex - 1;
if (previousIndex >= 0 && this.approvalPlayerList[previousIndex]) {
const previousPlayerId = this.approvalPlayerList[previousIndex].playerId;
// 先重置轮播索引,避免切换过程中出现空白
this.currentRoomIndex = 0;
this.getApprovalDetailData(previousPlayerId);
}
} else {
}
},
toNextRecord() {
if (this.canNext === true) {
// 导航到下一个玩家
const nextIndex = this.currentPlayerIndex + 1;
if (nextIndex < this.approvalPlayerList.length && this.approvalPlayerList[nextIndex]) {
const nextPlayerId = this.approvalPlayerList[nextIndex].playerId;
// 先重置轮播索引,避免切换过程中出现空白
this.currentRoomIndex = 0;
this.getApprovalDetailData(nextPlayerId);
}
}
},
// 处理轮播图切换事件
handleSwiperChange(e) {
this.currentRoomIndex = e.detail.current;
this.updateSelectableDates();
},
// 触摸开始事件
touchStart(e) {
this.touchStartX = e.touches[0].clientX;
},
// 触摸结束事件
touchEnd(e) {
this.touchEndX = e.changedTouches[0].clientX;
this.handleSwipe();
},
// 处理滑动事件
handleSwipe() {
const swipeThreshold = 30; // 滑动阈值
// 向左滑动
if (this.touchStartX - this.touchEndX > swipeThreshold) {
this.currentRoomIndex = (this.currentRoomIndex + 1) % this.roomList.length;
this.updateSelectableDates();
}
// 向右滑动
else if (this.touchEndX - this.touchStartX > swipeThreshold) {
this.currentRoomIndex = (this.currentRoomIndex - 1 + this.roomList.length) % this.roomList.length;
this.updateSelectableDates();
}
},
// 更新可选日期范围
updateSelectableDates() {
const currentRoom = this.roomList[this.currentRoomIndex];
if (currentRoom) {
// 从 bookingListData 中获取原始日期范围作为可选范围
const originalBooking = this.bookingListData.find(item => item.id === currentRoom.id);
if (originalBooking) {
// 可选日期范围保持为原始日期
this.selectAbleStart = originalBooking.checkinDate;
this.selectAbleEnd = originalBooking.checkoutDate;
// 但默认显示的日期应该是当前房间的当前日期(可能已经被修改过的)
this.calendarStartDate = currentRoom.checkinDate;
this.calendarEndDate = currentRoom.checkoutDate;
}
}
},
// 切换房间选择状态
toggleSelection(index) {
this.roomList[index].selected = !this.roomList[index].selected;
},
showReject() {
// 设置当前操作类型为 reject
this.currentAction = 'reject';
this.iconPath = '../../static/images/ico_err.png';
this.popMsgStr = this.$t("approval.submitReject");
this.showMsgBox = true; // 显示整个弹窗
this.showSubmit = true;
},
async confirmReject() {
if (this.approvalLoading || this.roomList.some(r => r.selected)) return;
uni.showLoading({
title: this.$t("common.loading")
});
// 获取当前轮播图显示的房间信息
const currentRoom = this.roomList[this.currentRoomIndex];
try {
let params = {
bookingId: currentRoom.id
};
const res = await rejectApproval(params);
if (res.success == true) {
if (res.data !== null) {
this.iconPath = '../../static/images/ico_suc.png';
this.popMsgStr = this.$t("bookingConfirm.rejectSuccess");
this.showMsgBox = true; // 显示整个弹窗
this.showSubmit = false;
}
} else {
this.buttonStr = this.$t("common.ok");
this.iconPath = '../../static/images/ico_err.png';
this.msgStr = res.message!=null?res.message:this.$t("common.rejectFail");
this.url = '';
this.showReturnMsgBox = true;
}
} catch (e) {
console.error(e);
uni.$u.toast(e.errMsg, 5000);
var request = e.config != undefined ? JSON.stringify(e.config) : "";
var response = e.data != undefined ? JSON.stringify(e.data) : "";
await this.writeLog(this.userData.userId, "find_player", "",
"[APP][findPlayer.vue][getProfileByPlayerId][Catch Exception]" + e.errMsg, request,
response, 1, false);
} finally {
uni.hideLoading();
}
// 延时显示Success
setTimeout(() => {}, 1000);
},
// 批量批准
async handleBatchApprove() {
uni.showLoading({
title: this.$t("common.loading")
});
try {
const selectedRooms = this.roomList.filter(room => room.selected);
// 新建参数,包含每个房间的 RoomId、checkindate 和 checkoutdate
const roomInfoList = selectedRooms.map(room => ({
bookingId: room.id,
checkinDate: room.checkinDate,
checkoutDate: room.checkoutDate
}));
const params = {submitInfoList:roomInfoList};
const res = await batchSubmitApproval(params);
this.showMsgBox = false;
if (res.success == true) {
this.iconPath = '../../static/images/suc.png';
this.popMsgStr = this.$t("bookingConfirm.approvalSuccess");
this.showMsgBox = true; // 显示整个弹窗
this.showSubmit = false;
} else {
this.buttonStr = this.$t("common.ok");
this.iconPath = '../../static/images/ico_err.png';
this.msgStr = res.message!=null?res.message:this.$t("common.appro")
this.url = '';
this.showReturnMsgBox = true;
}
} catch (e) {
console.error(e);
uni.$u.toast(e.errMsg, 5000);
var request = e.config != undefined ? JSON.stringify(e.config) : "";
var response = e.data != undefined ? JSON.stringify(e.data) : "";
await this.writeLog(this.userData.userId, "batch_approval", "",
"[APP][playerDetail.vue][handleBatchApprove][Catch Exception]" + e.errMsg, request,
response, 1, false);
} finally {
uni.hideLoading();
}
this.roomList.forEach(room => room.selected = false);
},
// 批量拒绝
async handleBatchReject() {
uni.showLoading({
title: this.$t("common.loading")
});
try {
const selectedRooms = this.roomList.filter(room => room.selected);
// 新建参数,包含每个房间的 RoomId、checkindate 和 checkoutdate
const roomInfoList = selectedRooms.map(room => ({
bookingId: room.id
}));
const params = {submitInfoList:roomInfoList};
const res = await batchSubmitReject(params);
this.showMsgBox = false;
if (res.success == true) {
this.iconPath = '../../static/images/suc.png';
this.popMsgStr = this.$t("bookingConfirm.rejectSuccess");
this.showMsgBox = true; // 显示整个弹窗
this.showSubmit = false;
} else {
this.buttonStr = this.$t("common.ok");
this.iconPath = '../../static/images/ico_err.png';
this.msgStr = this.$t("common.rejectFail") +":"+ res.message;
this.url = '';
this.showReturnMsgBox = true;
}
} catch (e) {
console.error(e);
uni.$u.toast(e.errMsg, 5000);
var request = e.config != undefined ? JSON.stringify(e.config) : "";
var response = e.data != undefined ? JSON.stringify(e.data) : "";
await this.writeLog(this.userData.userId, "batch_approval", "",
"[APP][playerDetail.vue][handleBatchApprove][Catch Exception]" + e.errMsg, request,
response, 1, false);
} finally {
uni.hideLoading();
}
this.roomList.forEach(room => room.selected = false);
}
},
onShow() {
// 手动触发水印更新,确保水印在页面显示时正确显示
this.$nextTick(() => {
setTimeout(() => {
console.log('[TodoList] Manually triggering watermark update on show');
const watermarkStore = require('@/store/watermark.js');
if (watermarkStore && watermarkStore.updateWatermark) {
watermarkStore.updateWatermark(true);
}
}, 500);
});
},
onLoad(opt) {
this.deviceData = uni.getStorageSync("deviceData");
this.windowWidth = this.deviceData.windowWidth - 70;
this.searchPlayerId = opt.playerId;
this.approvalPlayerList = uni.getStorageSync("approvalPlayerList");
if (this.searchPlayerId != null) this.getApprovalDetailData()
},
onReady() {
// 页面首次渲染完成后,延迟触发水印更新
setTimeout(() => {
console.log('[TodoList] Triggering watermark update on ready');
const watermarkStore = require('@/store/watermark.js');
if (watermarkStore && watermarkStore.updateWatermark) {
watermarkStore.updateWatermark(true);
}
}, 1200);
}
}
</script>
<style scoped>
/* 悬浮栏样式 - 显示在tabbar上方 */
.floating-bar {
position: fixed;
bottom: 65px;
/* tabbar高度 */
left: 0;
right: 0;
background-color: #ffffff;
border-top: 1px solid #e4e7ed;
border-radius: 25px;
padding: 10px 15px;
margin: 0px 10px;
z-index: 99;
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05);
}
.floating-content {
display: flex;
justify-content: space-between;
align-items: center;
}
.selected-count {
font-size: 14px;
color: #606266;
font-weight: 500;
}
.floating-actions {
display: flex;
gap: 10px;
}
.action-btn {
padding: 8px 16px;
border-radius: 20px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
}
.approve-btn {
background-color: #C39A4B;
color: #ffffff;
}
.reject-btn {
background-color:#C39A4B;
color: #ffffff;
}
.infoButton {
border-bottom: 0.5px #999 solid;
padding-bottom: 6px;
margin-top: 6px;
}
.dataMargin {
margin-bottom: 3px;
}
</style>
+96
View File
@@ -0,0 +1,96 @@
<template>
<view>
<u-navbar
:safeAreaInsetTop="true"
:placeholder="true"
:title="title"
:fixed="true"
:autoBack="false"
leftIconColor="#C39A4B"
bgColor="#C39A4B"
titleStyle="font-weight:bold;color:#ffffff;"
>
<view
@click="logout()"
style="margin-top:2px;margin-right:3px;"
slot="right"
>
<image src="../../static/images/ico_exit.png" style="width:24px;height:24px;"></image>
</view>
</u-navbar>
<view class="hz-flex-row" style="justify-content: center;margin-top:40px;">
<image src="/static/images/location_fail.png" style="width:160px;height:160px;"></image>
</view>
<view class="hz-flex-row hz-font-bolder hz-font-md" style="margin:20px;">
{{message}}
</view>
<view class="hz-font-bolder hz-font-md" style="margin:20px;">
{{reson}}:{{resonMsg}}
</view>
<view class="hz-flex-row hz-spacing" style="justify-content: center;">
<view @click="checkLocationAndVPN(true)" class="hz-btn hz-bg-primary hz-font-lg hz-font-bold hz-font-color-white hz-text-center" style="width: 150px;">{{$t("region.retry")}}</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
// 数据变量
userData:null,
deviceData:null,
siteData:null,
// 界面变量
userName:'',
title: this.$t("region.title"),
message:this.$t("region.showMessage"),
reson: this.$t("region.reson"),
resonMsg:"",
}
},
onLoad(prop) {
// 初始化界面
//uni.setLocale("en") //測試切換語言en or zh-Hant
this.userData = uni.getStorageSync("userData");
this.siteData = uni.getStorageSync("siteData");
if(prop!==null && prop!==undefined && prop.type!==null && prop.type!==undefined && prop.type!==''){
if(prop.type == '1'){
this.resonMsg = this.$t("region.resonMsglocationErr")
}
if(prop.type == '2'){
this.resonMsg = this.$t("region.resonMsgVPNErr")
}
if(prop.type == '3'){
this.resonMsg = this.$t("region.resonMsgFunctionErr")
}
if(prop.type == '4'){
this.resonMsg = this.$t("region.resonMsgNotInMacau")
}
if(prop.type == '5'){
this.resonMsg = this.$t("region.resonMsgOpenVPN")
}
if(prop.type == '6'){
this.resonMsg = this.$t("region.resonMsgNotInMacauOpenVpn")
}
if(prop.type == '7'){
this.resonMsg = this.$t("region.resonMsgNotInMacauIP")
}
}
},
onShow(){
this.userData = uni.getStorageSync("userData");
this.userName = this.userData.userName + " (" + this.userData.userId+")";
},
methods: {
},
computed:{
}
}
</script>
<style>
</style>
+75
View File
@@ -0,0 +1,75 @@
<template>
<view class="hz-pd-16">
<u-navbar @leftClick="back()" :safeAreaInsetTop="true" :placeholder="true" :title="title" :fixed="true"
:autoBack="false" leftIconColor="#000000" bgColor="#C39A4B" titleStyle="font-weight:bold;color:#ffffff;">
</u-navbar>
<uni-card>
<view style="border-bottom: 0.3px #999 dashed;padding-bottom: 6px;">
<view style="">
<view class="hz-font-bolder hz-font-xl">
Macau
</view>
<view class="hz-font-base">
Grand(35),WL+0
</view>
<view class="hz-font-base">
X1(2),Alpha X1+1
</view>
<view class="hz-font-base">
Alpha X2+1,WL+0
</view>
<view class="hz-font-base">
Total(35);Overbook Limit(67)
</view>
</view>
</view>
<view style="border-bottom: 0.3px #999 dashed;padding-bottom: 6px;margin-top: 6px;">
<view>V1 +0,WL +0</view>
<view>V2 +4(V2M +1,V2 +3),WL +0</view>
<view>V3 +1,WL +0</view>
</view>
<view style="border-bottom: 0.3px #999 solid;padding-bottom: 6px;margin-top: 6px;">
<view>MO GTD Room:95/95; MO GTD Suite:5/5,WL:0</view>
<view>Ascott GTD Room:55/54; Ascott GTD Studio:46/46; WL:0</view>
<view>H6V GTD Room:10/10: WL:0</view>
<view>LMG Room: 10/10;WL:0</view>
</view>
<view style="border-bottom: 0.3px #999 dashed;padding-bottom: 6px;margin-top: 6px;">
<view class="hz-font-xl">MGM Cotai</view>
<view>Resort (119)</view>
<view>E1+5</view>
<view>R1 (5)</view>
<view>R2+2</view>
<view>Skylofts Room +0</view>
<view>Terry</view>
<view>Y1+1</view>
<view>Y2+0</view>
<view>Total (116); Overbook Limit (167)</view>
</view>
<view class="">
<view>S1 +1</view>
<view>S2 +6(S2M +4,S2K +2)</view>
<view>S3 +1</view>
</view>
</uni-card>
</view>
</template>
<script>
export default {
data() {
return {
title: "Room Count",
}
},
methods: {
back() {
this.navBack();
},
}
}
</script>
<style>
</style>
+92
View File
@@ -0,0 +1,92 @@
<!--sso login-->
<template>
<view class="container">
<web-view :src="url"></web-view>
</view>
</template>
<script>
export default {
data() {
return {
url:'',
intervalId:null,
newUrl:'',
siteData:null
}
},
onLoad(options) {
this.siteData = uni.getStorageSync("siteData");
if(options && options.uid && options.pwd){
let username = options.uid.replaceAll(" ", "+");
let password = options.pwd.replaceAll(" ", "+");
this.url = this.siteData.internalUrl + this.siteData.apiPath + "/app/sso/login_saml_with_mfa?username="+encodeURIComponent(username)+"&password="+encodeURIComponent(password);
}
if (options && options.url){
this.url = options.url;
}
this.listenUrl()
},
onShow() {
this.siteData = uni.getStorageSync("siteData");
},
methods: {
listenUrl() {
var that = this;
var count = 0;
this.intervalId = setInterval(() => {
var currentUrl = that.getCurrentUrl();
that.newUrl = currentUrl;
var code = that.getCurrentQueryString(currentUrl, 'SAMLResponse');
var errorCode = that.getCurrentQueryString(currentUrl, 'errorCode');
//uni.$u.toast('url1='+currentUrl);
if(currentUrl.indexOf('/sso_callback/login')!=-1){
//uni.$u.toast('url2='+currentUrl);
if (code){
//uni.$u.toast('get code');
clearInterval(that.intervalId);
uni.redirectTo({
url:'/pages/login/login?code='+encodeURIComponent(code)
})
}
else if (errorCode){
//uni.$u.toast('get errorcode');
clearInterval(that.intervalId);
uni.redirectTo({
url:'/pages/idpLogin/idpLogin?errorCode='+encodeURIComponent(errorCode)
})
}
}
}, 1000);
},
getCurrentQueryString(url, name) {
try{
var queryString = url.substring(url.indexOf('?'));
var reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)', 'i');
var r = queryString.substr(1).match(reg);
if (r != null) {
return unescape(r[2]);
}
return null;
}
catch(e){
uni.$u.toast('getCurrentQueryString exception:'+e.errMsg);
return null;
}
},
getCurrentUrl(){
var pages = getCurrentPages();
var page = pages[pages.length - 1];
var currentWebview = page.$getAppWebview();
var url = currentWebview.children()[0].getURL();
console.log("=======当前地址======", url);
return url;
}
}
}
</script>
<style>
</style>
+454
View File
@@ -0,0 +1,454 @@
<template>
<view>
<u-navbar
:safeAreaInsetTop="true"
:placeholder="true"
:title="title"
:fixed="true"
:autoBack="false"
leftIconColor="#C39A4B"
bgColor="#C39A4B"
titleStyle="font-weight:bold;color:#ffffff;"
>
<view @click="logout"
style="margin-top:2px;margin-right:3px;"
slot="right"
>
<image src="../../static/images/ico_exit.png" style="width:24px;height:24px;"></image>
</view>
</u-navbar>
<view v-if="show" style="margin-left:20px;margin-right:20px;margin-top:5px;border-bottom:1rpx solid #ddd;height:80rpx;line-height:90rpx;">
<u-row>
<u-col span="12" >
<view class="hz-flex-row">
<view class="hz-font-md" style="margin-left:16rpx;">{{$t("common.user")}}</view>
</view>
</u-col>
</u-row>
</view>
<view v-if="show" style="margin-left:20px;margin-right:20px;height:90rpx;line-height: 22px;padding-top: 8px;">
<u-row>
<u-col span="12" >
<view class="hz-flex-row">
<view class="hz-font-md" style="margin-left:16rpx;color:#666;">{{userName}}</view>
</view>
</u-col>
</u-row>
</view>
<!-- <view v-if="show" style="margin-left:20px;margin-right:20px;border-bottom:1rpx solid #ddd;height:90rpx;line-height:90rpx;">
<u-row>
<u-col span="4" >
<view class="hz-flex-row">
<view class="hz-font-md" style="margin-left:16rpx;">{{$t("common.block")}}</view>
</view>
</u-col>
<u-col span="8">
<view class="hz-flex-row">
<view class="hz-text-right hz-font-md" style="color:#666;margin-right:16rpx;width:100%;">{{userData.block}}</view>
</view>
</u-col>
</u-row>
</view> -->
<view v-if="show" style="margin-left:20px;margin-right:20px;border-bottom:1rpx solid #ddd;height:80rpx;line-height:90rpx;margin-top:2px">
<u-row>
<u-col span="12" >
<view class="hz-flex-row">
<view class="hz-font-md" style="margin-left:16rpx;">{{$t("common.department")}}</view>
</view>
</u-col>
</u-row>
</view>
<view v-if="show" style="margin-left:20px;margin-right:20px;height:90rpx;line-height: 22px;padding-top: 8px;">
<u-row>
<u-col span="12" >
<view class="hz-flex-row">
<view class="hz-font-md" style="margin-left:16rpx;color:#666;">{{userData.department}}</view>
</view>
</u-col>
</u-row>
</view>
<view v-if="show" style="margin-left:20px;margin-right:20px;border-bottom:1rpx solid #ddd;height:80rpx;line-height:90rpx;margin-top:2px">
<u-row>
<u-col span="12" >
<view class="hz-flex-row">
<view class="hz-font-md" style="margin-left:16rpx;">{{$t("common.division")}}</view>
</view>
</u-col>
</u-row>
</view>
<view v-if="show" style="margin-left:20px;margin-right:20px;height:90rpx;line-height: 22px;padding-top: 8px;">
<u-row>
<u-col span="12" >
<view class="hz-flex-row">
<view class="hz-font-md" style="margin-left:16rpx;color:#666;">{{userData.division}}</view>
</view>
</u-col>
</u-row>
</view>
<view v-if="show" style="margin-left:20px;margin-right:20px;border-bottom:1rpx solid #ddd;height:80rpx;line-height:90rpx;margin-top:2px">
<u-row>
<u-col span="12" >
<view class="hz-flex-row">
<view class="hz-font-md" style="margin-left:16rpx;">{{$t("common.section")}}</view>
</view>
</u-col>
</u-row>
</view>
<view v-if="show" style="margin-left:20px;margin-right:20px;height:90rpx;line-height: 22px;padding-top: 8px;">
<u-row>
<u-col span="12" >
<view class="hz-flex-row">
<view class="hz-font-md" style="margin-left:16rpx;color:#666;">{{userData.section}}</view>
</view>
</u-col>
</u-row>
</view>
<view v-if="show" style="margin-left:20px;margin-right:20px;border-bottom:1rpx solid #ddd;height:80rpx;line-height:90rpx;margin-top:2px">
<u-row>
<u-col span="12" >
<view class="hz-flex-row">
<view class="hz-font-md" style="margin-left:16rpx;">{{$t("common.language")}}</view>
</view>
</u-col>
</u-row>
</view>
<view v-if="show" @click="popupLang" style="margin-left:20px;margin-right:20px;height:80rpx;line-height: 27px;padding-top: 8px;">
<u-row>
<u-col span="12" >
<view class="hz-flex-row">
<view class="hz-font-md" style="margin-left:16rpx;color:#666;">{{lang}}</view>
<u-icon name="arrow-right" color="#666;" style="margin-left: auto;"></u-icon>
</view>
</u-col>
</u-row>
</view>
<view v-if="show" style="margin-left:20px;margin-right:20px;border-bottom:1rpx solid #ddd;height:80rpx;line-height:90rpx;">
<u-row>
<u-col span="12" >
<view class="hz-flex-row">
<view class="hz-font-md" style="margin-left:16rpx;">{{$t("common.version")}}</view>
</view>
</u-col>
</u-row>
</view>
<view v-if="show" @click="openDebugMode" style="margin-left:20px;margin-right:20px;height:80rpx;line-height: 27px;padding-top: 8px;">
<u-row>
<u-col span="12" >
<view class="hz-flex-row">
<view class="hz-font-md" style="margin-left:16rpx;color:#666;">{{siteData.version}}</view>
<u-icon name="arrow-right" color="#666;" style="margin-left: auto;"></u-icon>
</view>
</u-col>
</u-row>
</view>
<!-- <view v-if="show" style="margin-left:20px;margin-right:20px;border-bottom:1rpx solid #ddd;height:80rpx;line-height:90rpx;margin-top:2px">
<u-row>
<u-col span="12" >
<view class="hz-flex-row">
<view class="hz-font-md" style="margin-left:16rpx;">Delegate Approval Request</view>
</view>
</u-col>
</u-row>
</view>
<view v-if="show" @click="showDelegate()" style="margin-left:20px;margin-right:20px;height:80rpx;line-height: 27px;padding-top: 8px;">
<u-row>
<u-col span="12" >
<view class="hz-flex-row">
<view class="hz-font-md" style="margin-left:16rpx;color:#666;">{{userStatus}}</view>
<u-icon name="arrow-right" color="#666;" style="margin-left: auto;"></u-icon>
</view>
</u-col>
</u-row>
</view> -->
<u-picker closeOnClickOverlay :show="showPicker" :defaultIndex="currentLangIndex" :columns="langList"
@close="showPicker=false" :cancelText="cancelText" :confirmText="okText" confirmColor="#C39A4B"
@cancel="showPicker=false" @confirm="confirmLang" keyName="name">
</u-picker>
<!-- <u-popup
:safeAreaInsetBottom="true"
:safeAreaInsetTop="true"
:mode="'center'"
:round="12"
:show="showMsgBox"
customStyle="width:94%; padding-top: 30px"
:closeable="!showDirBtn"
@close="closePopup()"
>
<view>
<view class="hz-flex-row" style="margin: 30px 30px;">
<view style="width: 40%;">
Status
</view>
<view style=" display: flex; align-items: center;">
<view style="margin-right: 10px; font-size: 14px; color: #333;">{{ userStatus === 'ON' ? 'ON' : 'OFF' }}</view>
<u-switch v-model="userStatus" @change="handleStatusChange"
activeValue="ON" inactiveValue="OFF" activeColor="#C39A4B" inactiveColor="#999" size="30"></u-switch>
</view>
</view>
<view class="hz-flex-row" style="margin: 30px 30px;width: 100%;">
<view style="width: 30%; flex-shrink: 0;">
Delegate to
</view>
<view style="margin-left: auto; flex: 1;">
<lu-data-select placeholder="Select Other Approver" :options="selectData" v-model="selectedApprover"
:multiple="false" :filterable="true" @change="onchange" style="width: 78%;" :clearable="false"></lu-data-select>
</view>
</view>
<view class="hz-flex-row" style="margin: 30px 30px;width: 100%;">
<view style="width: 30%; flex-shrink: 0;">
Date
</view>
<view style="width: 55%;">
<uni-datetime-picker class="delegate-data-picker" v-model="range" type="daterange" :clearIcon="false" @maskClick="maskClick" />
</view>
</view>
<view @click="saveDelegate()" class="hz-btn hz-font-color-white hz-font-lg hz-font-bold hz-text-center"
style="width: 90%;margin: 0 auto;margin-bottom: 15px;">
{{$t("common.confirm").toUpperCase()}}
</view>
<view @click="closePopup()" class="hz-btn hz-font-color-primary hz-font-lg hz-font-bold hz-text-center"
style="width: 90%;margin: 0 auto;margin-bottom: 30px;background-color: white;border: 0.5px solid;">
{{$t("common.cancel").toUpperCase()}}
</view>
</view>
</u-popup> -->
</view>
</template>
<script>
import {logout} from '../../api/commonApi.js'
export default {
data() {
return {
// 数据变量
userData:null,
deviceData:null,
siteData:null,
langList:[
[{
name: 'English',
code: 'en'
}, {
name: '繁體中文',
code: 'zh-Hant'
}]
],
// 界面变量
show:false,
showPicker:false,
cancelText:this.$t("common.cancel"),
confirmText:this.$t("common.confirm").toUpperCase(),
okText:this.$t("common.ok"),
userName:'',
showMsgBox:false,
showDirBtn:false,
userStatus:'ON',
selectedApprover:"",
lang:',',
currentLangIndex:[0],
title: this.$t("setting.title"),
single: "2021-04-3",
today: new Date(),
range: [],
selectData:[{
value: '1',
label: 'Corso'
},
{
value: '2',
label: 'Mauve',
},
{
value: '3',
label: 'Calmi',
}
],
}
},
onLoad() {
// 初始化界面
//this.redirectByPermission(this.userData,false);
this.siteData = uni.getStorageSync("siteData");
this.show = true;
this.lang = this.mapLocale(uni.getLocale());
// 初始化 range 变量
this.range = [this.today, new Date(this.today.getTime() + 24 * 60 * 60 * 1000)];
},
onShow(){
//uni.setLocale("en") //測試切換語言en or zh-Hant
this.userData = uni.getStorageSync("userData");
this.userName = this.userData.userName + " (" + this.userData.userId+")";
// const userPoint = turf.point([113.560831,22.224397]);
// // console.log(userPoint,point)
// let fea = point.features
// for(let i =0;i<fea.length;i++){
// let co = fea[i];
// // console.log(co.geometry)
// for(let j = 0 ;j<co.geometry.coordinates.length;j++){
// let geo = co.geometry.coordinates[j];
// console.log(geo)
// let po = turf.polygon(geo[0]);
// // console.log(po)
// const isInside = turf.booleanPointInPolygon(userPoint, [geo]);
// console.log(isInside)
// }
// }
},
onReady(){
if (this.userData !== null && this.userData !== undefined)
this.showTabbarByPermission(this.userData);
},
methods: {
async logout(){
uni.showLoading({
title: this.$t("common.loading")
});
try{
let params = {};
const res = await logout(params);
if(res.success == true){
if(res.data !== null){
if(this.siteData.mockSSO==false&&!this.siteData.internalTag){
//this.logoutWithADFSApp()
uni.redirectTo({
url:'/pages/azureLogout/azureLogout'
});
}else{
uni.removeStorageSync("userData");
uni.removeStorageSync("token");
uni.removeStorageSync("idToken");
this.navTo('/pages/login/login',2,false);
}
}
}else{
if(this.siteData.mockSSO==false&&!this.siteData.internalTag){
//this.logoutWithADFSApp()
uni.redirectTo({
url:'/pages/azureLogout/azureLogout'
});
}else{
uni.removeStorageSync("userData");
uni.removeStorageSync("token");
uni.removeStorageSync("idToken");
this.navTo('/pages/login/login',2,false);
}
}
}catch(e){
console.error(e);
uni.$u.toast(e.errMsg,5000);
await this.writeLog(this.userData.userId,"logout","","[APP][setting.vue][logout][Catch Exception]"+e.errMsg,"","",1,false);
}finally{
uni.hideLoading();
}
},
popupLang(){
this.currentLangIndex = [];
for(let i=0;i<this.langList[0].length;i++){
if(this.langList[0][i].code === uni.getLocale()){
this.currentLangIndex.push(i);
break;
}
}
this.showPicker = true;
},
showDelegate() {
this.showMsgBox = true;
},
confirmLang(e){
uni.setLocale(e.value[0].code);
this.$i18n.locale = e.value[0].code;
this.lang = e.value[0].name;
this.title = this.$t("setting.title");
this.cancelText=this.$t("common.cancel");
this.okText=this.$t("common.ok");
// App 端 tabBar 文案不会自动跟随 i18n 刷新,需要手动更新
// #ifdef APP-PLUS
this.$nextTick(() => {
try {
// pages.json tabBar.list 顺序:
// 0 dashboard, 1 todo, 2 hotel(findPlayer), 3 setting
uni.setTabBarItem({ index: 0, text: this.$t("dashboard.title") });
uni.setTabBarItem({ index: 1, text: this.$t("todo.title") });
uni.setTabBarItem({ index: 2, text: this.$t("hotel.title") });
uni.setTabBarItem({ index: 3, text: this.$t("setting.title") });
} catch (err) {
// ignore
}
});
// #endif
this.showPicker = false;
},
openDebugMode(){
let count = uni.getStorageSync("countOfDebugMode");
if(count===undefined || count===''){
count = 0;
}
count++;
uni.setStorageSync("countOfDebugMode",count);
if(count === 5){
if(this.siteData.debugMode === false){
this.siteData.debugMode = true;
uni.$u.toast(this.$t("common.debugMode") + ": ON");
}
else{
this.siteData.debugMode = false;
uni.$u.toast(this.$t("common.debugMode") + ": OFF");
}
uni.setStorageSync("siteData",this.siteData);
uni.removeStorageSync("countOfDebugMode");
}
},
saveDelegate(){
this.showMsgBox = false;
},
closePopup(){
this.showMsgBox = false;
},
handleStatusChange(value){
this.userStatus = value;
},
onchange(value){
this.selectedApprover = value;
},
maskClick(){
console.log("date");
}
},
computed:{
mapLocale() {
return function (lang) {
if(lang=='zh-Hant'){
return '繁體中文';
}else{
return 'English';
}
}
},
}
}
</script>
<style lang="scss" scoped>
::v-deep .uni-calendar__content{
bottom: 0 !important;
}
::v-deep .uni-calendar .uni-calendar__content .uni-date-changed .uni-datetime-picker--btn {
background-color: #C39A4B !important;
}
::v-deep .uni-calendar-item__weeks-box .uni-calendar-item--checked {
background-color: #C39A4B !important;
}
::v-deep .uni-calendar-item--multiple .uni-calendar-item--before-checked {
background-color: #C39A4B !important;
}
::v-deep .uni-calendar-item--multiple .uni-calendar-item--after-checked {
background-color: #C39A4B !important;
}
</style>
+150
View File
@@ -0,0 +1,150 @@
<template>
<view>
<view class="hz-text-center" style="padding-top:200rpx;">
<image src="../../static/images/logo.png" style="width:138rpx;height:140rpx;"></image>
</view>
<view class="hz-font-bolder hz-font-xl hz-text-center hz-font-color-primary">
{{$t('common.appName')}}
</view>
<view class="hz-font-bolder hz-font-xl hz-text-center hz-font-color-dark" style="margin-top:160rpx;">
Mockup IDP Login
</view>
<view style="margin-top:30rpx;margin-right:60rpx;margin-left:60rpx;">
<u--input
v-model="userId"
placeholder="Login User ID"
prefixIcon="account"
prefixIconStyle="font-size: 20px;color:#C39A4B;"
customStyle="border-radius:22px;"
></u--input>
</view>
<view style="margin-top:30rpx;margin-right:60rpx;margin-left:60rpx;">
<u--input
v-model="password"
placeholder="Login Password"
prefixIcon="lock-open"
:password="true"
prefixIconStyle="font-size: 20px;color: #C39A4B"
customStyle="border-radius:22px;"
></u--input>
</view>
<!--<view style="margin-top:30rpx;margin-right:60rpx;margin-left:60rpx;">
<u-radio-group
v-model="propertyCode"
placement="row">
<u-radio activeColor="#C39A4B" label="Macau" name="MM" customStyle="width:50%;" @change="changeProperty"></u-radio>
<u-radio activeColor="#C39A4B" label="Cotai" name="MC" @change="changeProperty"></u-radio>
</u-radio-group>
</view>-->
<view style="margin-top:30rpx;margin-right:60rpx;margin-left:60rpx;">
<view @click="mockSSOLogin" class="hz-btn hz-font-color-white hz-font-lg hz-font-bold hz-text-center" >{{$t("common.loginsso")}}</view>
</view>
<u-modal :content="msgStr" :show="showMsg" :confirmText="yesText" closeOnClickOverlay @confirm="confirmMsg" confirmColor="#C39A4B" @cancel="cancelMsg" @close="cancelMsg"></u-modal>
</view>
</template>
<script>
import CryptoJS from 'crypto-js';
import {mockUserSSOLogin} from '../../api/ssoApi.js'
export default {
data() {
return {
// 数据变量
userId: '',
password:'',
propertyCode:'MM',
userData:null,
deviceData:null,
siteData:null,
// 界面变量
msgStr:'',
showMsg:false,
yesText:this.$t("common.ok"),
}
},
onLoad() {
// 初始化界面
this.initUI();
this.siteData = uni.getStorageSync("siteData");
this.userData = uni.getStorageSync("userData");
},
onShow() {
},
methods: {
// 加密方法
hashPassword(pwd){
if(!pwd){
return '';
}
return CryptoJS.SHA256(pwd).toString(CryptoJS.enc.Hex);
},
// 数据方法
async mockSSOLogin(){
uni.showLoading({
title: this.$t("common.loading")
});
try{
const encryptedPassword = this.hashPassword(this.password);
let params = {userId:this.userId, credential:encryptedPassword, propertyCode:this.propertyCode};
const res = await mockUserSSOLogin(params);
if(res.success === true){
if(res.data !== null){
uni.redirectTo({
url:'/pages/login/login?code=' + res.accessToken
})
}
}else if(res.errorCode!==undefined && res.errorCode == 'ERROR_LOCKED'){
this.msgStr = this.$t("common.userLocked");
this.showMsg = true;
} else if (res != null && res.code == 'ERROR_LOCKED') {
this.msgStr = this.$t("common.userLocked");
this.showMsg = true;
} else if (res != null && res.code == 'ERROR_IP_BANNED') {
this.msgStr = this.$t("common.ipLocked");
this.showMsg = true;
} else {
this.msgStr = 'Login failed.';
this.showMsg = true;
}
}catch(e){
this.writeLog(this.userId,"mockSSOLogin","","[APP][ssoLogin.vue][mockSSOLogin]catch error"+JSON.stringify(e),"","",1,false);
if(e.data !== undefined && e.data.errorCode !== undefined && e.data.errorCode !==""){
if(e.data.errorCode == 'ERROR_LOCKED'){
this.msgStr = this.$t("common.userLocked");
this.showMsg = true;
}
}else{
this.msgStr = "System Error";
this.showMsg = true;
}
}finally{
uni.hideLoading();
}
},
// 界面方法
changeProperty(e){
this.propertyCode = e;
},
initUI() {
this.deviceData = uni.getStorageSync("deviceData");
},
confirmMsg(){
this.showMsg = false;
},
cancelMsg(){
this.showMsg = false;
}
},
}
</script>
<style>
.u-border {
border-width: 0.5px !important;
border-color: #434343 !important;
border-style: solid;
height:30px;
}
</style>