feat:添加扫码器/手机设备判断

master
黄伟杰 3 weeks ago
parent fb08eed1d4
commit 60c45a2af0

@ -2,6 +2,7 @@
import { getToken } from '@/utils/auth' import { getToken } from '@/utils/auth'
import { initializeLocale, translateLiteral } from '@/locales' import { initializeLocale, translateLiteral } from '@/locales'
import useUserStore from '@/store/modules/user' import useUserStore from '@/store/modules/user'
import { initializeTerminalType } from '@/utils/terminal'
let wrapped = false let wrapped = false
@ -48,6 +49,7 @@ function wrapUniTextApi() {
export default { export default {
onLaunch: function () { onLaunch: function () {
initializeLocale() initializeLocale()
initializeTerminalType()
wrapUniTextApi() wrapUniTextApi()
if (getToken()) { if (getToken()) {
useUserStore().getInfo().catch(() => {}) useUserStore().getInfo().catch(() => {})

@ -1,4 +1,5 @@
import request from '@/utils/request' import request from '@/utils/request'
import { getTerminalType } from '@/utils/terminal'
const permissionInfoCache = new Map() const permissionInfoCache = new Map()
const permissionInfoPending = new Map() const permissionInfoPending = new Map()
@ -12,16 +13,35 @@ function normalizePermissionParams(params = {}) {
function createPermissionCacheKey(params = {}) { function createPermissionCacheKey(params = {}) {
const normalizedParams = normalizePermissionParams(params) const normalizedParams = normalizePermissionParams(params)
const terminalType = getTerminalType()
return JSON.stringify( return JSON.stringify(
Object.keys(normalizedParams) Object.keys({ ...normalizedParams, terminalType })
.sort() .sort()
.reduce((result, key) => { .reduce((result, key) => {
result[key] = normalizedParams[key] result[key] = key === 'terminalType' ? terminalType : normalizedParams[key]
return result return result
}, {}) }, {})
) )
} }
function filterPermissionMenus(menus, terminalType) {
if (!Array.isArray(menus)) {
return []
}
return menus.filter((menu) => {
if (!menu || typeof menu !== 'object') {
return false
}
if (menu.terminalType === null || typeof menu.terminalType === 'undefined' || menu.terminalType === '') {
return true
}
return Number(menu.terminalType) === Number(terminalType)
})
}
export function clearPermissionInfoCache() { export function clearPermissionInfoCache() {
permissionInfoCache.clear() permissionInfoCache.clear()
permissionInfoPending.clear() permissionInfoPending.clear()
@ -62,6 +82,7 @@ export function register(data) {
function getPermissionInfo(params = {}) { function getPermissionInfo(params = {}) {
const normalizedParams = normalizePermissionParams(params) const normalizedParams = normalizePermissionParams(params)
const terminalType = getTerminalType()
const cacheKey = createPermissionCacheKey(normalizedParams) const cacheKey = createPermissionCacheKey(normalizedParams)
if (permissionInfoCache.has(cacheKey)) { if (permissionInfoCache.has(cacheKey)) {
@ -80,8 +101,15 @@ function getPermissionInfo(params = {}) {
const wrappedPromise = requestPromise const wrappedPromise = requestPromise
.then((res) => { .then((res) => {
permissionInfoCache.set(cacheKey, res) const nextRes = {
return res ...res,
data: {
...res?.data,
menus: filterPermissionMenus(res?.data?.menus, terminalType)
}
}
permissionInfoCache.set(cacheKey, nextRes)
return nextRes
}) })
.finally(() => { .finally(() => {
permissionInfoPending.delete(cacheKey) permissionInfoPending.delete(cacheKey)

@ -4,10 +4,12 @@
:fixed="true" :placeholder="true" :border="false" @change="handleChange" zIndex="1000"> :fixed="true" :placeholder="true" :border="false" @change="handleChange" zIndex="1000">
<up-tabbar-item v-for="(item, index) in tabList" :key="index" :text="item.text" :name="index"> <up-tabbar-item v-for="(item, index) in tabList" :key="index" :text="item.text" :name="index">
<template #active-icon> <template #active-icon>
<image :src="item.selectedIcon" class="tabbar-icon" mode="widthFix" /> <uni-icons v-if="item.iconType === 'uni-icons'" :type="item.iconName" size="24" :color="activeColor" />
<image v-else :src="item.selectedIcon" class="tabbar-icon" mode="widthFix" />
</template> </template>
<template #inactive-icon> <template #inactive-icon>
<image :src="item.icon" class="tabbar-icon" mode="widthFix" /> <uni-icons v-if="item.iconType === 'uni-icons'" :type="item.iconName" size="24" :color="inactiveColor" />
<image v-else :src="item.icon" class="tabbar-icon" mode="widthFix" />
</template> </template>
</up-tabbar-item> </up-tabbar-item>
</up-tabbar> </up-tabbar>
@ -61,6 +63,14 @@ function normalizeRoute(path = '') {
return String(path || '').trim().replace(/^\/+/, '') return String(path || '').trim().replace(/^\/+/, '')
} }
function isUniIcon(icon) {
return String(icon || '').startsWith('uni-icons:')
}
function getUniIconName(icon) {
return String(icon || '').replace(/^uni-icons:/, '').trim()
}
function resolveTabIcons(path, index) { function resolveTabIcons(path, index) {
const route = normalizeRoute(path) const route = normalizeRoute(path)
if (route === 'pages/index') { if (route === 'pages/index') {
@ -84,6 +94,23 @@ function resolveTabIcons(path, index) {
return dynamicTabIconList[index % dynamicTabIconList.length] return dynamicTabIconList[index % dynamicTabIconList.length]
} }
function resolveTabIconMeta(menu, index) {
if (isUniIcon(menu?.icon)) {
return {
iconType: 'uni-icons',
iconName: getUniIconName(menu.icon)
}
}
const icons = resolveTabIcons(menu?.path, index)
return {
iconType: 'image',
iconName: '',
icon: icons.icon,
selectedIcon: icons.selectedIcon
}
}
function getCurrentActiveIndex() { function getCurrentActiveIndex() {
const pages = getCurrentPages() const pages = getCurrentPages()
if (pages && pages.length > 0) { if (pages && pages.length > 0) {
@ -97,11 +124,13 @@ function getCurrentActiveIndex() {
const tabList = computed(() => { const tabList = computed(() => {
const dynamicTabList = getTabBarMenus(menus.value) const dynamicTabList = getTabBarMenus(menus.value)
.map((menu, index) => { .map((menu, index) => {
const icons = resolveTabIcons(menu.path, index) const iconMeta = resolveTabIconMeta(menu, index)
return { return {
text: String(menu.name || menu.enName || '').trim() || `菜单${index + 1}`, text: String(menu.name || menu.enName || '').trim() || `菜单${index + 1}`,
icon: icons.icon, icon: iconMeta.icon,
selectedIcon: icons.selectedIcon, selectedIcon: iconMeta.selectedIcon,
iconType: iconMeta.iconType,
iconName: iconMeta.iconName,
path: menu.path path: menu.path
} }
}) })
@ -112,6 +141,8 @@ const tabList = computed(() => {
text: t('nav.mine'), text: t('nav.mine'),
icon: mineIcon, icon: mineIcon,
selectedIcon: mineSelectedIcon, selectedIcon: mineSelectedIcon,
iconType: 'image',
iconName: '',
path: '/pages/mine' path: '/pages/mine'
} }
] ]

@ -514,10 +514,16 @@ export default {
setting: { setting: {
language: 'System Language', language: 'System Language',
currentLanguage: 'Current: {language}', currentLanguage: 'Current: {language}',
terminalMode: 'Terminal Mode',
currentTerminal: 'Current Terminal: {terminal}',
switchingTerminal: 'Switching terminal...',
terminalSwitched: 'Switched to {terminal}',
switchLanguage: 'Switch Language', switchLanguage: 'Switch Language',
checkUpdate: 'Check Updates', checkUpdate: 'Check Updates',
cleanCache: 'Clear Cache', cleanCache: 'Clear Cache',
logout: 'Log Out', logout: 'Log Out',
mobile: 'Mobile',
scanner: 'Scanner',
zhCN: 'Chinese', zhCN: 'Chinese',
enUS: 'English' enUS: 'English'
}, },

@ -514,10 +514,16 @@ export default {
setting: { setting: {
language: '系统语言', language: '系统语言',
currentLanguage: '当前语言:{language}', currentLanguage: '当前语言:{language}',
terminalMode: '终端模式',
currentTerminal: '当前终端:{terminal}',
switchingTerminal: '正在切换终端...',
terminalSwitched: '已切换到{terminal}',
switchLanguage: '切换语言', switchLanguage: '切换语言',
checkUpdate: '检查更新', checkUpdate: '检查更新',
cleanCache: '清理缓存', cleanCache: '清理缓存',
logout: '退出登录', logout: '退出登录',
mobile: '手机',
scanner: '扫码器',
zhCN: '中文', zhCN: '中文',
enUS: '英文' enUS: '英文'
}, },

@ -15,6 +15,13 @@
<view>{{ t('mine.changePassword') }}</view> <view>{{ t('mine.changePassword') }}</view>
</view> </view>
</view> </view>
<view class="list-cell list-cell-arrow" @click="handleTerminalChange">
<view class="menu-item-box">
<view class="iconfont icon-phone menu-icon"></view>
<view>{{ t('setting.terminalMode') }}</view>
</view>
<view class="text-grey">{{ t('setting.currentTerminal', { terminal: currentTerminalLabel }) }}</view>
</view>
<view class="list-cell list-cell-arrow" @click="handleToUpgrade"> <view class="list-cell list-cell-arrow" @click="handleToUpgrade">
<view class="menu-item-box"> <view class="menu-item-box">
<view class="iconfont icon-refresh menu-icon"></view> <view class="iconfont icon-refresh menu-icon"></view>
@ -51,6 +58,7 @@ import { useI18n } from 'vue-i18n'
import useUserStore from '@/store/modules/user' import useUserStore from '@/store/modules/user'
import { getCurrentLocale, setLocale } from '@/locales' import { getCurrentLocale, setLocale } from '@/locales'
import NavBar from '@/components/common/NavBar.vue' import NavBar from '@/components/common/NavBar.vue'
import { getTerminalType, setTerminalType, TERMINAL_TYPE_MOBILE, TERMINAL_TYPE_SCANNER } from '@/utils/terminal'
const userStore = useUserStore() const userStore = useUserStore()
const { t } = useI18n() const { t } = useI18n()
@ -59,7 +67,9 @@ const pageTitle = computed(() => t('nav.setting'))
const windowHeight = ref(uni.getSystemInfoSync().windowHeight); const windowHeight = ref(uni.getSystemInfoSync().windowHeight);
const popup = ref(null); const popup = ref(null);
const currentLocale = ref(getCurrentLocale()) const currentLocale = ref(getCurrentLocale())
const currentTerminalType = ref(getTerminalType())
const currentLanguageLabel = computed(() => currentLocale.value === 'en-US' ? t('setting.enUS') : t('setting.zhCN')) const currentLanguageLabel = computed(() => currentLocale.value === 'en-US' ? t('setting.enUS') : t('setting.zhCN'))
const currentTerminalLabel = computed(() => currentTerminalType.value === TERMINAL_TYPE_SCANNER ? t('setting.scanner') : t('setting.mobile'))
function handleToPwd() { function handleToPwd() {
uni.navigateTo({ uni.navigateTo({
@ -99,6 +109,46 @@ function handleLanguageChange() {
} }
}) })
} }
function handleTerminalChange() {
uni.showActionSheet({
itemList: [t('setting.mobile'), t('setting.scanner')],
success: async ({ tapIndex }) => {
const nextTerminalType = tapIndex === 1 ? TERMINAL_TYPE_SCANNER : TERMINAL_TYPE_MOBILE
if (nextTerminalType === currentTerminalType.value) {
return
}
const previousTerminalType = currentTerminalType.value
setTerminalType(nextTerminalType)
currentTerminalType.value = nextTerminalType
uni.showLoading({
title: t('setting.switchingTerminal')
})
try {
await userStore.refreshPermissionInfo()
uni.showToast({
title: t('setting.terminalSwitched', { terminal: currentTerminalLabel.value }),
icon: 'none',
duration: 1000
})
} catch (error) {
setTerminalType(previousTerminalType)
currentTerminalType.value = previousTerminalType
await userStore.refreshPermissionInfo().catch(() => {})
uni.showToast({
title: t('common.saveFailed'),
icon: 'none',
duration: 1000
})
} finally {
uni.hideLoading()
}
}
})
}
function handleLogout() { function handleLogout() {
popup.value.open(); popup.value.open();
}; };

@ -1,5 +1,6 @@
import { login, logout, getInfo, clearPermissionInfoCache } from "@/api/login"; import { login, logout, getInfo, clearPermissionInfoCache } from "@/api/login";
import { getToken, setToken, removeToken } from "@/utils/auth"; import { getToken, setToken, removeToken } from "@/utils/auth";
import { removeTerminalType } from "@/utils/terminal";
import defAva from "@/static/images/profile.jpg"; import defAva from "@/static/images/profile.jpg";
import { defineStore } from "pinia"; import { defineStore } from "pinia";
import { initAllDict } from "@/utils/dict"; import { initAllDict } from "@/utils/dict";
@ -76,12 +77,17 @@ const useUserStore = defineStore("user", {
}); });
}); });
}, },
refreshPermissionInfo() {
clearPermissionInfoCache();
return this.getInfo();
},
// 退出系统 // 退出系统
logOut() { logOut() {
return new Promise<null>((resolve, reject) => { return new Promise<null>((resolve, reject) => {
logout() logout()
.then(() => { .then(() => {
clearPermissionInfoCache(); clearPermissionInfoCache();
removeTerminalType();
this.token = ""; this.token = "";
this.roles = []; this.roles = [];
this.permissions = []; this.permissions = [];

@ -216,6 +216,10 @@ export function buildPageModules(tabMenu) {
} }
export function resolveMenuUrl(menu) { export function resolveMenuUrl(menu) {
if (String(menu?.path || '').trim() === '/' || String(menu?.component || '').trim() === '/') {
return '/pages/index'
}
const directRoute = getDirectRoute(menu?.path) || getDirectRoute(menu?.component) const directRoute = getDirectRoute(menu?.path) || getDirectRoute(menu?.component)
if (directRoute) { if (directRoute) {
return directRoute return directRoute

@ -0,0 +1,46 @@
const TerminalTypeKey = 'App-Terminal-Type'
export const TERMINAL_TYPE_MOBILE = 1
export const TERMINAL_TYPE_SCANNER = 2
function normalizeTerminalType(value: unknown) {
return Number(value) === TERMINAL_TYPE_SCANNER ? TERMINAL_TYPE_SCANNER : TERMINAL_TYPE_MOBILE
}
export function detectTerminalType() {
const systemInfo = uni.getSystemInfoSync()
const width = Number(systemInfo.windowWidth || systemInfo.screenWidth || 0)
const height = Number(systemInfo.windowHeight || systemInfo.screenHeight || 0)
if (width === 480 && height === 800) {
return TERMINAL_TYPE_SCANNER
}
return TERMINAL_TYPE_MOBILE
}
export function getTerminalType() {
const storedValue = uni.getStorageSync(TerminalTypeKey)
if (storedValue === '' || storedValue === null || typeof storedValue === 'undefined') {
return TERMINAL_TYPE_MOBILE
}
return normalizeTerminalType(storedValue)
}
export function setTerminalType(value: unknown) {
const terminalType = normalizeTerminalType(value)
uni.setStorageSync(TerminalTypeKey, terminalType)
return terminalType
}
export function removeTerminalType() {
uni.removeStorageSync(TerminalTypeKey)
}
export function initializeTerminalType() {
const storedValue = uni.getStorageSync(TerminalTypeKey)
if (storedValue === '' || storedValue === null || typeof storedValue === 'undefined') {
return setTerminalType(detectTerminalType())
}
return normalizeTerminalType(storedValue)
}
Loading…
Cancel
Save