af-mobile-client-vue3 1.4.92 → 1.4.94

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "af-mobile-client-vue3",
3
3
  "type": "module",
4
- "version": "1.4.92",
4
+ "version": "1.4.94",
5
5
  "packageManager": "pnpm@10.13.1",
6
6
  "description": "Vue + Vite component lib",
7
7
  "engines": {
@@ -0,0 +1,158 @@
1
+ import { runLogic } from '@af-mobile-client-vue3/services/api/common'
2
+ import { isWechat } from '@af-mobile-client-vue3/utils/platform-auth'
3
+ import DrivingConfig from '@af-mobile-client-vue3/views/component/Driving/index.vue'
4
+ import { createApp } from 'vue'
5
+
6
+ /**
7
+ * 功能角色配置接口
8
+ * 用于定义某个功能基于用户角色的启用条件
9
+ */
10
+ export interface FeatureRoleConfig {
11
+ /** 是否启用该功能的总开关 */
12
+ enabled: boolean
13
+
14
+ /** 命中即禁用(最高优先级)- 如果用户拥有这些角色之一,则禁用功能 */
15
+ denyRoles?: string[]
16
+
17
+ /** 必须具备的角色(全部满足)- 用户必须拥有所有这些角色才能启用功能 */
18
+ mustRoles?: string[]
19
+
20
+ /** 至少命中一个即可 - 用户至少拥有这些角色之一即可启用功能 */
21
+ maybeRoles?: string[]
22
+
23
+ /** 字段说明,不参与逻辑 - 用于文档或注释 */
24
+ __comment?: Record<string, string>
25
+ }
26
+ /**
27
+ * 根据角色配置判断功能是否可用
28
+ * 该函数根据提供的角色配置和用户角色字符串,判断用户是否可以启用某个功能
29
+ * 逻辑优先级:总开关 > 禁止角色 > 必须角色 > 可选角色
30
+ * @param config 功能角色配置对象
31
+ * @param roleStr 用户角色字符串(逗号分隔的角色列表)
32
+ * @returns 如果功能可用返回 true,否则返回 false
33
+ */
34
+ export function canEnableFeatureByRoleStr(
35
+ config: FeatureRoleConfig,
36
+ roleStr: string,
37
+ ): boolean {
38
+ // 如果配置为空或未定义,直接返回 false
39
+ if (config === undefined || config === null) {
40
+ return false
41
+ }
42
+
43
+ // 1️⃣ 总开关 - 如果功能未启用,直接返回 false
44
+ if (!config?.enabled) {
45
+ return false
46
+ }
47
+
48
+ // 2️⃣ 将角色字符串转成数组(去空格并过滤空值)
49
+ const userRoles = roleStr
50
+ .split(',')
51
+ .map(r => r.trim())
52
+ .filter(Boolean)
53
+
54
+ // 3️⃣ 禁止角色检查(最高优先级)- 如果用户拥有任何禁止角色,则禁用功能
55
+ if (config.denyRoles?.some(role => userRoles.includes(role))) {
56
+ return false
57
+ }
58
+
59
+ // 4️⃣ 必须角色检查(必须全部满足)- 用户必须拥有所有必须角色
60
+ if (
61
+ config.mustRoles?.length
62
+ && !config.mustRoles.every(role => userRoles.includes(role))
63
+ ) {
64
+ return false
65
+ }
66
+
67
+ // 5️⃣ 可选角色检查(至少命中一个)- 用户至少拥有一个可选角色
68
+ if (
69
+ config.maybeRoles?.length
70
+ && !config.maybeRoles.some(role => userRoles.includes(role))
71
+ ) {
72
+ return false
73
+ }
74
+
75
+ // 所有条件满足,返回 true
76
+ return true
77
+ }
78
+
79
+ /**
80
+ * 处理驾驶配置提交的回调函数
81
+ * 当用户在弹窗中提交驾驶配置数据时,调用此函数保存数据并更新用户状态
82
+ * @param data 用户提交的驾驶配置数据
83
+ * @param userState
84
+ */
85
+ export async function handleDrivingConfigSubmit(data: any, userState: any) {
86
+ // console.warn('>>> handleDrivingConfigSubmit', data, userState)
87
+ // 调用后端逻辑保存用户驾驶信息
88
+ await runLogic('saveUserDrivingInfoLogic', { ...data, userId: userState.getUserInfo().id }, 'af-linepatrol')
89
+ // 获取当前用户信息
90
+ const currentUserInfo = userState.getUserInfo()
91
+ // 更新用户信息,将驾驶信息合并到用户状态中
92
+ userState.setUserInfo({ ...currentUserInfo, drivingInfo: data })
93
+ }
94
+
95
+ /**
96
+ * 检查并初始化驾驶配置
97
+ * 该函数检查当前环境和用户角色是否支持驾驶功能,如果支持且用户未配置驾驶信息,则打开配置弹窗
98
+ * 主要用于应用启动时或需要时初始化驾驶相关设置
99
+ */
100
+ export async function checkAndInitDrivingConfig(userState: any) {
101
+ // 检查环境条件:不是微信环境且兼容模式不是OA
102
+ if (!isWechat() && import.meta.env.VITE_APP_COMPATIBLE !== 'OA') {
103
+ // console.warn('>>> userState', userState) // 调试日志
104
+ // 获取驾驶配置(角色权限等)
105
+ const drivingConfig: any = await runLogic('getLiuliConfiguration', { configName: 'DrivingConfig' }, 'af-linepatrol')
106
+ // 根据配置和用户角色判断是否启用驾驶功能
107
+ const isDriving = canEnableFeatureByRoleStr(drivingConfig, userState.getUserInfo().rolestr || userState.getUserInfo().rolestr.rolesnames || '')
108
+ if (isDriving) {
109
+ // 获取用户的驾驶信息
110
+ const drivingInfoRes: any = await runLogic('getUserDrivingInfoLogic', { userId: userState.getUserInfo().id }, 'af-linepatrol')
111
+ const drivingInfo = JSON.parse(drivingInfoRes.value)
112
+ // console.warn('>>>drivingInfoRes', drivingInfoRes) // 调试日志
113
+ // console.warn('>>>drivingInfo', drivingInfo) // 调试日志
114
+ // 如果驾驶信息为空,说明用户未配置,需要打开配置弹窗
115
+ if (drivingInfo === null || Object.keys(drivingInfo).length === 0) {
116
+ // 打开配置弹窗,并传入提交处理函数
117
+ openDrivingConfigPopup()
118
+ }
119
+ else {
120
+ // 如果用户已配置驾驶信息,则更新用户状态
121
+ userState.setUserInfo({ ...userState.getUserInfo(), drivingInfo })
122
+ // console.warn('用户已配置驾驶信息,则更新用户状态>>>userinfo', userState.getUserInfo())
123
+ }
124
+ }
125
+ }
126
+ }
127
+
128
+ /**
129
+ * 打开驾驶配置弹窗
130
+ * 该函数动态创建并显示驾驶配置弹窗组件,监听用户的提交和关闭事件
131
+ * 当用户提交数据时,调用传入的回调函数;当用户关闭弹窗时,卸载组件
132
+ */
133
+ export function openDrivingConfigPopup() {
134
+ // 创建Vue应用实例,传入弹窗组件和事件监听器
135
+ const app = createApp(DrivingConfig, {
136
+ 'visible': true, // 设置弹窗初始显示
137
+ // 监听自定义事件:onSubmit(data, userState)
138
+ // 对应组件中 emit('onSubmit', data, userState)
139
+ 'onOnSubmit': async (data: any, userState: any) => {
140
+ // 监听提交事件,调用回调函数并卸载组件
141
+ await handleDrivingConfigSubmit(data, userState)
142
+ app.unmount()
143
+ },
144
+ // 监听 v-model:visible 对应的 update:visible 事件
145
+ 'onUpdate:visible': (visible: boolean) => {
146
+ // 监听弹窗显示状态变化,如果隐藏则卸载组件
147
+ if (!visible) {
148
+ app.unmount()
149
+ }
150
+ },
151
+ })
152
+ // 创建临时DOM元素作为挂载点
153
+ const div = document.createElement('div')
154
+ // 将临时元素添加到页面body中
155
+ document.body.appendChild(div)
156
+ // 将Vue应用挂载到临时元素上
157
+ app.mount(div)
158
+ }
@@ -0,0 +1,127 @@
1
+ <script setup lang="ts">
2
+ import XForm from '@af-mobile-client-vue3/components/data/XForm/index.vue'
3
+ import { runLogic } from '@af-mobile-client-vue3/services/api/common'
4
+ import { useUserStore } from '@af-mobile-client-vue3/stores'
5
+ import { showToast, Button as VanButton } from 'vant'
6
+ import { onMounted, ref } from 'vue'
7
+ // 定义 props 和 emits
8
+ const props = defineProps<{
9
+ visible: boolean
10
+ }>()
11
+ const emit = defineEmits<{
12
+ (event: 'update:visible', value: boolean): void
13
+ (event: 'onSubmit', value: any, userState: any): void
14
+ }>()
15
+ const userState = useUserStore()
16
+ const drivingFrom = ref()
17
+ const showLogoutConfirm = ref(false)
18
+ onMounted(() => {
19
+ // console.log('>>>onMounted', props)
20
+ showLogoutConfirm.value = props.visible
21
+ runLogic('getLiuliConfiguration', {
22
+ configName: 'DrivingConfigForm',
23
+ }, 'af-linepatrol')
24
+ .then((DrivingConfigForm) => {
25
+ // console.log('>>>res', DrivingConfigForm)
26
+ const initParams = {
27
+ formItems: DrivingConfigForm,
28
+ }
29
+ if ((userState.getUserInfo()?.drivingInfo?.drivingMode || null) === null) {
30
+ drivingFrom.value?.init(initParams)
31
+ }
32
+ else {
33
+ drivingFrom.value?.init({
34
+ ...initParams,
35
+ formData: userState.getUserInfo()?.drivingInfo,
36
+ })
37
+ }
38
+ })
39
+ .catch((err) => {
40
+ console.error('>>>err', err)
41
+ showToast('获取配置失败')
42
+ })
43
+ })
44
+ function onsubmit(data: any) {
45
+ // console.warn('>>>onsubmit', data, userState)
46
+ emit('onSubmit', data, userState)
47
+ // emit('update:visible', false)
48
+ }
49
+ function updateModalVisible(visible = false) {
50
+ showLogoutConfirm.value = visible
51
+ emit('update:visible', visible)
52
+ }
53
+
54
+ async function handleSubmitClick() {
55
+ // 调用 XForm 对外暴露的 submit 方法(如果存在)
56
+ const res = await drivingFrom.value.asyncSubmit()
57
+ onsubmit(res?.realForm)
58
+ }
59
+ function handleCloseClick() {
60
+ updateModalVisible(false)
61
+ }
62
+ defineExpose({ updateModalVisible })
63
+ </script>
64
+
65
+ <template>
66
+ <div v-if="showLogoutConfirm" class="driving-popup-overlay">
67
+ <div class="driving-popup-container">
68
+ <XForm
69
+ ref="drivingFrom"
70
+ service-name:="af-linepatrol"
71
+ :is-group-form="true"
72
+ :submit-button="false"
73
+ />
74
+ <div class="driving-popup-footer">
75
+ <VanButton
76
+ class="close-btn"
77
+
78
+ plain block
79
+ @click="handleCloseClick"
80
+ >
81
+ 取消
82
+ </VanButton>
83
+ <VanButton
84
+ class="submit-btn"
85
+ type="primary"
86
+ block
87
+ @click="handleSubmitClick"
88
+ >
89
+ 确认修改
90
+ </VanButton>
91
+ </div>
92
+ </div>
93
+ </div>
94
+ </template>
95
+
96
+ <style scoped lang="less">
97
+ .driving-popup-overlay {
98
+ position: fixed;
99
+ inset: 0;
100
+ z-index: 100;
101
+ display: flex;
102
+ align-items: center;
103
+ justify-content: center;
104
+ background-color: rgba(0, 0, 0, 0.5);
105
+ }
106
+
107
+ .driving-popup-container {
108
+ width: 90%;
109
+ max-width: 400px;
110
+ max-height: 80vh;
111
+ padding: 16px;
112
+ overflow-y: auto;
113
+ border-radius: 12px;
114
+ background-color: #fff;
115
+ }
116
+
117
+ .driving-popup-footer {
118
+ display: flex;
119
+ gap: 8px;
120
+ padding-top: 16px;
121
+ }
122
+
123
+ .driving-popup-footer .submit-btn,
124
+ .driving-popup-footer .close-btn {
125
+ flex: 1;
126
+ }
127
+ </style>
@@ -7,6 +7,7 @@ import { getUserPermissions } from '@af-mobile-client-vue3/services/api/search'
7
7
  import { useSettingStore } from '@af-mobile-client-vue3/stores/modules/setting'
8
8
  import { useUserStore } from '@af-mobile-client-vue3/stores/modules/user'
9
9
  import { UserType } from '@af-mobile-client-vue3/types/auth'
10
+ import { checkAndInitDrivingConfig } from '@af-mobile-client-vue3/utils/driving/drivingUtils'
10
11
  import { isWechat } from '@af-mobile-client-vue3/utils/platform-auth'
11
12
  import { funcToRouter, loadRoutes } from '@af-mobile-client-vue3/utils/routerUtil'
12
13
  import { secureStorageBatchWrite, secureStorageRead } from '@af-mobile-client-vue3/utils/secureStorage'
@@ -223,6 +224,8 @@ async function afterGeneral(result, token = '') {
223
224
 
224
225
  // 每次重新登录时,清除indexedDB缓存
225
226
  // indexedDB.clear()
227
+ // 如果不是微信和OA 进行超速相关判断
228
+ checkAndInitDrivingConfig(userState)
226
229
  }
227
230
  async function wxLoginFun() {
228
231
  await localStorage.setItem('wechatLoginPending', 'true')
@@ -8,12 +8,14 @@ import { Dialog as vanDialog, Icon as vanIcon, Sticky as VanSticky,
8
8
  } from 'vant'
9
9
  import { computed, onMounted, onUnmounted, ref } from 'vue'
10
10
  import { useRouter } from 'vue-router'
11
+ import { openDrivingConfigPopup } from '~root/src/utils/driving/drivingUtils'
11
12
  import ModifyPassword from './comm/ModifyPassword.vue'
12
13
 
13
14
  const router = useRouter()
14
15
  const username = useUserStore().getUserInfo().name
15
16
  const fullnames = useUserStore().getLogin().f.resources.fullnames
16
17
  const roleName = useUserStore().getLogin().f.resources.f_role_name
18
+ const userInfo = useUserStore().getUserInfo()
17
19
 
18
20
  // 修改密码弹窗显示状态
19
21
  const showModifyPassword = ref(false)
@@ -250,6 +252,13 @@ const webMobileConfig = useSettingStore().getSetting()
250
252
  </div>
251
253
  <van-icon name="arrow" class="menu-arrow" />
252
254
  </div>
255
+ <div v-if="userInfo?.drivingInfo !== undefined && userInfo?.drivingInfo !== null" class="menu-item" @click="openDrivingConfigPopup">
256
+ <div class="menu-item-content">
257
+ <van-icon name="logistics" class="menu-icon" />
258
+ <span class="menu-text">修改行驶方式</span>
259
+ </div>
260
+ <van-icon name="arrow" class="menu-arrow" />
261
+ </div>
253
262
  <div class="menu-item" @click="exit_login">
254
263
  <div class="menu-item-content">
255
264
  <van-icon name="sign" class="menu-icon logout-icon" />
@@ -14,7 +14,7 @@ const userId = computed(() => useRoute().query.userId?.toString() || '')
14
14
  :config-name="configName"
15
15
  :service-name="serviceName"
16
16
  :fix-query-form="{
17
- info_f_userinfo_id: userId,
17
+ twb_f_userinfo_id: userId,
18
18
  }"
19
19
  />
20
20
  </template>