import Vue from 'vue' import Vuex from 'vuex' import { getEnums, getMyEffectiveContracts, getMyPendingSignContracts, orderSearchedMePage } from '@/api/index.js' import { normalizeBookingList, parseOrderListResponse, pickCurrentBooking, pickCurrentStayOrder } from '@/utils/booking-helper' import { getBuiltInEnums } from '@/utils/enum-store' Vue.use(Vuex) let lifeData = {} // 尝试获取本地是否存在lifeData变量,第一次启动时不存在 try { lifeData = uni.getStorageSync('lifeData') } catch(e) { } // 标记需要永久存储的变量,在每次启动时取出,在 state 中的变量名 let saveStateKeys = ['vuex_user', 'vuex_identity', 'vuex_locale', 'vuex_checkin_status', 'vuex_current_order'] const normalizeIdentityValue = (value) => { const map = { resident: '1', counselor: '2', 'dorm-manager': '3', social: '0' } if (value === undefined || value === null || value === '') return '' const normalized = map[value] || value return String(normalized) } // 保存变量到本地存储 const saveLifeData = function(key, value) { // 判断变量是否在存储数组中 if (saveStateKeys.indexOf(key) != -1) { // 获取本地存储的lifeData对象,将变量添加到对象中 let tmpLifeData = uni.getStorageSync('lifeData') // 第一次启动时不存在,则放一个空对象 tmpLifeData = tmpLifeData ? tmpLifeData : {}, tmpLifeData[key] = value // 将变量再次放回本地存储中 uni.setStorageSync('lifeData', tmpLifeData) } } function parseContractList(response) { const payload = response && response.data ? response.data : response if (Array.isArray(payload)) { return payload } if (payload && Array.isArray(payload.data)) { return payload.data } if (payload && payload.data && Array.isArray(payload.data.data)) { return payload.data.data } return [] } function logRequestError(name, error) { console.error(`[store] ${name} failed`, error) return [] } function resolveOrderNo(order = {}) { const safeOrder = order || {} const candidates = [safeOrder.orderNo, safeOrder.no, safeOrder.code, safeOrder.OrderNo] for (let index = 0; index < candidates.length; index += 1) { const value = candidates[index] if (value === undefined || value === null) { continue } const text = String(value).trim() if (text) { return text } } return '' } let lastFetchStatusTime = 0 let fetchUserGlobalStatusPromise = null let globalEnumsPromise = null let lastFetchEnumsTime = 0 const GLOBAL_ENUMS_TTL = 10 * 60 * 1000 const store = new Vuex.Store({ state: { // 如果上面从本地获取的lifeData对象下有对应的属性,就赋值给state中对应的变量 // 加上vuex_前缀,是防止变量名冲突,也让人一目了然 vuex_user: lifeData.vuex_user ? lifeData.vuex_user : {}, vuex_identity: normalizeIdentityValue(lifeData.vuex_identity), vuex_checkin_status: lifeData.vuex_checkin_status !== undefined ? lifeData.vuex_checkin_status : false, vuex_locale: lifeData.vuex_locale ? lifeData.vuex_locale : 'zh-Hans', // app版本 vuex_version: "1.0.0", // 是否使用自定义导航栏 vuex_custom_nav_bar: true, // 状态栏高度 vuex_status_bar_height: 0, // 自定义导航栏的高度 vuex_custom_bar_height: 0, vuex_pending_contract_count: 0, vuex_pending_contracts: [], vuex_effective_contracts: [], vuex_valid_orders: [], vuex_current_order: lifeData.vuex_current_order ? lifeData.vuex_current_order : null, vuex_enums: getBuiltInEnums(lifeData.vuex_locale || 'zh-Hans') }, mutations: { $tStore(state, payload) { // 判断是否多层调用,state中为对象存在的情况,例如user.info.score = 1 let nameArr = payload.name.split('.') let saveKey = '' let len = nameArr.length if (len >= 2) { let obj = state[nameArr[0]] for (let i= 1; i < len - 1; i++) { obj = obj[nameArr[i]] } obj[nameArr[len - 1]] = payload.value saveKey = nameArr[0] } else { // 单层级变量 state[payload.name] = payload.name === 'vuex_identity' ? normalizeIdentityValue(payload.value) : payload.value saveKey = payload.name } // 保存变量到本地中 saveLifeData(saveKey, state[saveKey]) } }, actions: { fetchUserGlobalStatus({ state, commit }, options = {}) { const force = !!(options && options.force) const language = (options && options.language) || 'zh' const currentUser = state.vuex_user || {} const userCode = currentUser.code || currentUser.userCode || '' const cachedOrders = Array.isArray(state.vuex_valid_orders) ? state.vuex_valid_orders : [] const cachedPendingContracts = Array.isArray(state.vuex_pending_contracts) ? state.vuex_pending_contracts : [] const cachedEffectiveContracts = Array.isArray(state.vuex_effective_contracts) ? state.vuex_effective_contracts : [] const cachedCurrentOrder = state.vuex_current_order || pickCurrentStayOrder(cachedOrders) const cachedCurrentBooking = currentUser.currentBooking || pickCurrentBooking(cachedOrders) || null const cachedResult = { pendingContracts: cachedPendingContracts, effectiveContracts: cachedEffectiveContracts, validOrders: cachedOrders, currentOrder: cachedCurrentOrder || null, currentBooking: cachedCurrentBooking, hasCheckedIn: !!cachedCurrentOrder } if (!userCode) return Promise.resolve(cachedResult) const now = Date.now() if (!force && fetchUserGlobalStatusPromise) { return fetchUserGlobalStatusPromise } if (!force && now - lastFetchStatusTime < 1000) { return Promise.resolve(cachedResult) } lastFetchStatusTime = now fetchUserGlobalStatusPromise = Promise.all([ orderSearchedMePage({ PageIndex: 1, PageSize: 100, IsExport: false }, language).catch((error) => logRequestError('orderSearchedMePage', error)) ]).then(([orderResponse]) => { const validOrders = normalizeBookingList(parseOrderListResponse(orderResponse), language) const currentOrder = pickCurrentStayOrder(validOrders) const currentOrderNo = resolveOrderNo(currentOrder) return Promise.all([ Promise.resolve(validOrders), Promise.resolve(currentOrder), currentOrderNo ? getMyPendingSignContracts({ OrderNo: currentOrderNo, ProviderType: 'WechatMiniProgram' }, language).catch((error) => logRequestError('getMyPendingSignContracts', error)) : Promise.resolve([]), currentOrderNo ? getMyEffectiveContracts({ OrderNo: currentOrderNo, ProviderType: 'WechatMiniProgram' }, language).catch((error) => logRequestError('getMyEffectiveContracts', error)) : Promise.resolve([]) ]) }).then(([validOrders, currentOrder, pendingContractsResponse, effectiveContractsResponse]) => { const pendingContracts = parseContractList(pendingContractsResponse) const effectiveContracts = parseContractList(effectiveContractsResponse) const currentBooking = pickCurrentBooking(validOrders) const nextUser = { ...currentUser, hasCheckedIn: !!currentOrder, currentOrder: currentOrder || null, currentBooking: currentBooking || null, bookingStatus: currentBooking ? (currentBooking.status || currentBooking.statusKey || 'none') : 'none', bookingRejectReason: currentBooking ? (currentBooking.rejectReasonText || currentBooking.rejectReason || '') : '' } commit('$tStore', { name: 'vuex_pending_contracts', value: pendingContracts }) commit('$tStore', { name: 'vuex_pending_contract_count', value: pendingContracts.length }) commit('$tStore', { name: 'vuex_effective_contracts', value: effectiveContracts }) commit('$tStore', { name: 'vuex_valid_orders', value: validOrders }) commit('$tStore', { name: 'vuex_checkin_status', value: !!currentOrder }) commit('$tStore', { name: 'vuex_current_order', value: currentOrder || null }) commit('$tStore', { name: 'vuex_user', value: nextUser }) return { pendingContracts, effectiveContracts, validOrders, currentOrder, currentBooking, hasCheckedIn: !!currentOrder } }).catch((error) => { console.error('[store] fetchUserGlobalStatus failed', error) return cachedResult }).finally(() => { fetchUserGlobalStatusPromise = null }) return fetchUserGlobalStatusPromise }, fetchGlobalEnums({ state, commit }, options = {}) { const force = !!(options && options.force) const hasEnums = state.vuex_enums && Object.keys(state.vuex_enums).length > 0 const now = Date.now() if (!force && hasEnums && now - lastFetchEnumsTime < GLOBAL_ENUMS_TTL) { return Promise.resolve(state.vuex_enums) } if (!force && globalEnumsPromise) { return globalEnumsPromise } const language = (options && options.language) || state.vuex_locale globalEnumsPromise = getEnums({}, language) .then((res) => { const data = res && res.data ? res.data : res const enumsMap = getBuiltInEnums(language) if (Array.isArray(data)) { data.forEach(item => { enumsMap[item.code] = item.data || [] }) } commit('$tStore', { name: 'vuex_enums', value: enumsMap }) lastFetchEnumsTime = Date.now() return enumsMap }) .catch(() => { return hasEnums ? state.vuex_enums : getBuiltInEnums(language) }) .finally(() => { globalEnumsPromise = null }) return globalEnumsPromise } } , getters: { enumOptions: (state) => (code) => { return state.vuex_enums[code] || [] }, enumLabel: (state) => (code, key) => { const options = state.vuex_enums[code] || [] const option = options.find(item => String(item.key) === String(key)) return option ? option.value : key } } }) export default store