This commit is contained in:
29
utils/expiry-reminder.js
Normal file
29
utils/expiry-reminder.js
Normal file
@@ -0,0 +1,29 @@
|
||||
export const getShanghaiDay = (date = new Date()) => {
|
||||
const parts = new Intl.DateTimeFormat('en-CA', {
|
||||
timeZone: 'Asia/Shanghai', year: 'numeric', month: '2-digit', day: '2-digit'
|
||||
}).formatToParts(date);
|
||||
const part = (type) => parts.find(item => item.type === type)?.value || '';
|
||||
return `${part('year')}-${part('month')}-${part('day')}`;
|
||||
};
|
||||
|
||||
export const normalizeExpiryReminder = (reminder) => {
|
||||
if (!reminder || typeof reminder !== 'object') return null;
|
||||
const days = reminder.remaining_days;
|
||||
if (days === null || days === undefined || days === '' ||
|
||||
!Number.isInteger(Number(days)) || Number(days) < 0 || Number(days) > 15 ||
|
||||
!['red', 'purple', 'pink'].includes(reminder.level)) return null;
|
||||
return { ...reminder, remaining_days: Number(days) };
|
||||
};
|
||||
|
||||
export const formatShanghaiExpiry = (value) => {
|
||||
if (!value) return '-';
|
||||
const text = String(value);
|
||||
// An offset-free server date is already a Shanghai wall-clock value.
|
||||
if (!/(Z|[+-]\d{2}:?\d{2})$/i.test(text)) return text.replace('T', ' ').slice(0, 19);
|
||||
const date = new Date(text);
|
||||
if (!Number.isFinite(date.getTime())) return '-';
|
||||
return new Intl.DateTimeFormat('zh-CN', {
|
||||
timeZone: 'Asia/Shanghai', year: 'numeric', month: '2-digit', day: '2-digit',
|
||||
hour: '2-digit', minute: '2-digit', hourCycle: 'h23'
|
||||
}).format(date);
|
||||
};
|
||||
@@ -1,25 +1,26 @@
|
||||
const PHONE_BIND_STATUS_KEY = 'phoneBindStatus';
|
||||
|
||||
const readStatuses = () => {
|
||||
const statuses = uni.getStorageSync(PHONE_BIND_STATUS_KEY);
|
||||
return statuses && typeof statuses === 'object' && !Array.isArray(statuses) ? statuses : {};
|
||||
// A customer phone or an old cache does not prove an asset association exists.
|
||||
export const optionalBoolean = (value) => {
|
||||
if (value === true || value === 1) return true;
|
||||
if (value === false || value === 0) return false;
|
||||
return null;
|
||||
};
|
||||
|
||||
export const getPhoneBindStatus = (identifier) => {
|
||||
if (!identifier) return null;
|
||||
|
||||
const status = readStatuses()[identifier];
|
||||
return status && status.bound ? status : null;
|
||||
export const getPhoneBindingMode = (customerPhone, assetPhoneBound) => {
|
||||
if (customerPhone === null || assetPhoneBound === null) return 'unknown';
|
||||
if (!customerPhone) return assetPhoneBound ? 'customer' : 'customer-asset';
|
||||
return assetPhoneBound ? 'none' : 'asset';
|
||||
};
|
||||
|
||||
export const setPhoneBindStatus = (identifier, phone) => {
|
||||
if (!identifier) return;
|
||||
|
||||
const statuses = readStatuses();
|
||||
statuses[identifier] = {
|
||||
bound: true,
|
||||
phone: phone || '',
|
||||
updatedAt: Date.now()
|
||||
};
|
||||
uni.setStorageSync(PHONE_BIND_STATUS_KEY, statuses);
|
||||
export const getBindingSuccessState = (state, phone, mode) => {
|
||||
if (mode === 'customer-asset') {
|
||||
return { customerPhone: phone, assetPhoneBound: true, assetPhoneIsOwn: true, needBindPhone: false };
|
||||
}
|
||||
if (mode === 'asset') {
|
||||
return { assetPhoneBound: true, assetPhoneIsOwn: phone === state.customerPhone, needBindPhone: false };
|
||||
}
|
||||
if (mode === 'customer') {
|
||||
return { customerPhone: phone, assetPhoneIsOwn: null, needBindPhone: false };
|
||||
}
|
||||
// An ambiguous success response does not identify which phone was changed.
|
||||
return { needBindPhone: false };
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { BASE_URL } from './env.js';
|
||||
import { useUserStore } from '@/store/user.js';
|
||||
|
||||
const showErrorToast = (message) => {
|
||||
uni.showToast({
|
||||
@@ -26,12 +27,14 @@ const request = (options) => {
|
||||
const { code, msg, data } = responseData;
|
||||
|
||||
if (res.statusCode === 401) {
|
||||
uni.removeStorageSync('token');
|
||||
uni.removeStorageSync('identifier');
|
||||
showErrorToast(msg || '登录已过期,请重新登录');
|
||||
setTimeout(() => {
|
||||
uni.reLaunch({ url: '/pages/login/login' });
|
||||
}, 2000);
|
||||
// A late response from an old login must not clear a new session.
|
||||
if (token === (uni.getStorageSync('token') || '')) {
|
||||
useUserStore().clearUser();
|
||||
showErrorToast(msg || responseData.message || '登录已过期,请重新登录');
|
||||
setTimeout(() => {
|
||||
if (!uni.getStorageSync('token')) uni.reLaunch({ url: '/pages/login/login' });
|
||||
}, 2000);
|
||||
}
|
||||
reject(responseData);
|
||||
return;
|
||||
}
|
||||
@@ -42,7 +45,7 @@ const request = (options) => {
|
||||
}
|
||||
|
||||
if (options.showError !== false) {
|
||||
showErrorToast(msg);
|
||||
showErrorToast(msg || responseData.message);
|
||||
}
|
||||
reject(responseData);
|
||||
},
|
||||
|
||||
9
utils/session-context.js
Normal file
9
utils/session-context.js
Normal file
@@ -0,0 +1,9 @@
|
||||
export const getSessionContext = () => ({
|
||||
token: uni.getStorageSync('token') || '',
|
||||
identifier: uni.getStorageSync('identifier') || ''
|
||||
});
|
||||
|
||||
export const isCurrentSession = (context) => {
|
||||
const current = getSessionContext();
|
||||
return !!context.token && context.token === current.token && context.identifier === current.identifier;
|
||||
};
|
||||
Reference in New Issue
Block a user