diff --git a/api/auth.js b/api/auth.js index e3c852f..6192427 100644 --- a/api/auth.js +++ b/api/auth.js @@ -3,5 +3,13 @@ import { post } from '@/utils/request.js' +/** + * 登录接口 + */ +export const reqLogin = (data) => post('user/postLoginOn', data) -export const reqLogin = (data) => post('user/postLoginOn', data) \ No newline at end of file +/** + * 请求控制权限 + */ + +export const reqMenuPowerList = (userId,projectId)=>get(`User/getMenuPowerList?userId=${userId}&projectId=${projectId}`) \ No newline at end of file diff --git a/api/base.js b/api/base.js new file mode 100644 index 0000000..d05d4e8 --- /dev/null +++ b/api/base.js @@ -0,0 +1,11 @@ +import { + get, + post +} from '@/utils/request.js' + +// BaseInfo/getProjectWorkArea?projectId=0792b983-4de2-44d5-adba-361b7fc99a97& + +/** + * 请求单位工程 + */ +export const reqProjectWorkArea = (projectId)=>get(`BaseInfo/getProjectWorkArea?projectId=${projectId}`) \ No newline at end of file diff --git a/api/hj.js b/api/hj.js new file mode 100644 index 0000000..d2f9bc3 --- /dev/null +++ b/api/hj.js @@ -0,0 +1,9 @@ +import { + get, + post +} from '@/utils/request.js' + +/** + * 获取焊接首页数据 + */ +export const reqHJIndexData = (projectId)=>get(`HJGLIndex/GetJGLIndexItem?projectId=${projectId}`) \ No newline at end of file diff --git a/assets/css/page.scss b/assets/css/page.scss index 843b307..e2ea56b 100644 --- a/assets/css/page.scss +++ b/assets/css/page.scss @@ -26,6 +26,8 @@ // 首页 .home { + padding-top: 80px; + padding-bottom: 60rpx; .u-border-bottom:after { border: none; } @@ -40,7 +42,9 @@ } &-icons { - height: 160rpx; + position: fixed; + top: 95px; + height: 80px; width: 100%; background-color: $u-type-primary; display: flex; @@ -145,4 +149,206 @@ } } } + + // 快捷应用 & 业务查询公共样式 + .section-card { + margin: 20rpx; + background: #fff; + border-radius: 16rpx; + box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.08); + padding: 24rpx; + + .section-title { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 24rpx; + font-size: 28rpx; + font-weight: 600; + color: #333; + + .title-left { + display: flex; + align-items: center; + } + + .title-line { + display: inline-block; + width: 8rpx; + height: 28rpx; + background: $u-type-primary; + border-radius: 4rpx; + margin-right: 16rpx; + } + + .edit-btn { + padding: 8rpx; + opacity: 0.8; + &:active { + opacity: 1; + } + } + } + + .section-grid { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 24rpx 16rpx; + overflow-x: hidden; + } + + .grid-item { + display: flex; + flex-direction: column; + align-items: center; + padding: 16rpx 8rpx; + border-radius: 12rpx; + transition: background 0.2s; + min-width: 0; + + &:active { + background: #f5f6f8; + } + + .icon-box { + width: 100rpx; + height: 100rpx; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + } + + .item-label { + margin-top: 12rpx; + font-size: 22rpx; + color: #666; + text-align: center; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + width: 100%; + } + } + } + + // 编辑快捷应用弹窗样式 + .edit-quick { + padding: 32rpx 32rpx 0 32rpx; + height: 100%; + display: flex; + flex-direction: column; + box-sizing: border-box; + + .edit-title { + display: flex; + align-items: center; + justify-content: space-between; + font-size: 28rpx; + font-weight: 600; + color: #333; + padding-bottom: 24rpx; + border-bottom: 1rpx solid #f0f0f0; + + .edit-tip { + font-size: 24rpx; + color: #999; + font-weight: 400; + } + } + + .edit-grid { + width: 100%; + overflow-y: scroll; + display: grid; + grid-template-columns: repeat(4, calc(25% - 20rpx)); + gap: 20rpx; + padding: 24rpx 0; + min-height: 0; + box-sizing: border-box; + } + + .edit-item { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + width: 100%; + height: 160rpx; + background: #f8f9fa; + border-radius: 16rpx; + position: relative; + transition: all 0.25s; + box-sizing: border-box; + + &.active { + background: rgba($color: #4a7df5, $alpha: 0.08); + border: 2rpx solid $u-type-primary; + } + + .icon-box { + width: 96rpx; + height: 96rpx; + display: flex; + align-items: center; + justify-content: center; + } + + .edit-label { + margin-top: 14rpx; + font-size: 22rpx; + color: #666; + text-align: center; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + width: 100%; + } + + .selected-mark { + position: absolute; + top: 6rpx; + right: 6rpx; + width: 40rpx; + height: 40rpx; + border-radius: 50%; + background: $u-type-primary; + display: flex; + align-items: center; + justify-content: center; + box-shadow: 0 4rpx 12rpx rgba($color: $u-type-primary, $alpha: 0.3); + } + } + + .edit-btns { + display: flex; + gap: 24rpx; + padding-top: 20rpx; + padding-bottom: 10rpx; + position: absolute; + bottom: 10px;left:0;right:0; + .u-size-medium { + width: 40%; + } + .btn-cancel, + .btn-confirm { + height: 88rpx; + font-size: 30rpx; + border: none; + &::after { + border: none; + } + + } + + .btn-cancel { + background: #f5f6f8; + color: #666; + } + + .btn-confirm { + box-shadow: 0 4rpx 16rpx rgba($color: $u-type-primary, $alpha: 0.25); + } + } + } } \ No newline at end of file diff --git a/assets/icon/nbd-icon.css b/assets/icon/nbd-icon.css index 681d5ae..703c758 100644 --- a/assets/icon/nbd-icon.css +++ b/assets/icon/nbd-icon.css @@ -1,8 +1,8 @@ @font-face { font-family: "nbd-icon"; /* Project id 5193086 */ - src: url('//at.alicdn.com/t/c/font_5193086_zgg3m1cv22e.woff2?t=1781003671405') format('woff2'), - url('//at.alicdn.com/t/c/font_5193086_zgg3m1cv22e.woff?t=1781003671405') format('woff'), - url('//at.alicdn.com/t/c/font_5193086_zgg3m1cv22e.ttf?t=1781003671405') format('truetype'); + src: url('//at.alicdn.com/t/c/font_5193086_dyc00qt3bz.woff2?t=1781056075220') format('woff2'), + url('//at.alicdn.com/t/c/font_5193086_dyc00qt3bz.woff?t=1781056075220') format('woff'), + url('//at.alicdn.com/t/c/font_5193086_dyc00qt3bz.ttf?t=1781056075220') format('truetype'); } .nbd-icon { @@ -13,6 +13,26 @@ -moz-osx-font-smoothing: grayscale; } +.nbd-icon-a-facheguanli:before { + content: "\e683"; +} + +.nbd-icon-yuzhijian:before { + content: "\e647"; +} + +.nbd-icon-baozhuangguanli:before { + content: "\e60e"; +} + +.nbd-icon-diankouguanli:before { + content: "\e60f"; +} + +.nbd-icon-shiyabao:before { + content: "\e657"; +} + .nbd-icon-daiban:before { content: "\e612"; } diff --git a/components/nbd-select-project.vue b/components/nbd-select-project.vue index 62e7048..7308452 100644 --- a/components/nbd-select-project.vue +++ b/components/nbd-select-project.vue @@ -12,9 +12,13 @@ - {{item[props.labelKey]}} + + {{ item[props.labelKey] }} + 暂无匹配项目 @@ -24,7 +28,8 @@ \ No newline at end of file + + + \ No newline at end of file diff --git a/components/nbd-select.vue b/components/nbd-select.vue new file mode 100644 index 0000000..f014e7c --- /dev/null +++ b/components/nbd-select.vue @@ -0,0 +1,325 @@ + + + + + diff --git a/pages.json b/pages.json index e1fa50f..c3d5b98 100644 --- a/pages.json +++ b/pages.json @@ -20,8 +20,47 @@ "navigationBarTitleText": "首页", "navigationStyle": "custom" } + }, + { + "path": "pages/mine/index", + "style": { + "navigationBarTitleText": "我的" + } } ], + "subPackages": [ + { + "root": "pipe", + "pages": [ + { + "path": "diankou/list", + "style": { + "navigationBarTitleText": "点口管理" + } + } + ] + } + ], + "tabBar": { + "color": "#cbcbcb", + "backgroundColor": "#2979ff", + "selectedColor": "#ffffff", + "borderStyle": "black", + "list": [{ + "text": "首页", + "pagePath": "pages/index/index", + "iconPath": "/static/tabs/home.png", + "selectedIconPath": "/static/tabs/home-active.png" + }, + { + "text": "个人中心", + "pagePath": "pages/mine/index", + "iconPath": "static/tabs/mine.png", + "selectedIconPath": "static/tabs/mine-active.png" + } + ], + "position": "bottom" + }, "globalStyle": { "navigationBarTextStyle": "white", "navigationBarTitleText": "施工管理", diff --git a/pages/index/index.vue b/pages/index/index.vue index a9c98b6..f6b5d46 100644 --- a/pages/index/index.vue +++ b/pages/index/index.vue @@ -7,7 +7,7 @@ - + 扫一扫 @@ -18,48 +18,112 @@ - 项目管道实时预制率 工厂焊接一次合格率 - 99.5% + {{hjIndexData?.GCFirstPassRate}} 现场焊接一次合格率 - 0% + {{hjIndexData?.XCFirstPassRate}} 工厂预制进度 - 49229.500/286639.000 DIN + {{hjIndexData?.GCProgress}} 现场焊接进度 - 5906.500/84951.000 DIN + {{hjIndexData?.XCProgress}} 工厂焊工功效 - 252 DIN/天 + {{hjIndexData?.GCWelderEfficacy}} 现场焊接功效 - 95 DIN/天 + {{hjIndexData?.XCWelderEfficacy}} 项目管道实时预制率 (包含工厂和现场的预制口) - 13% + {{hjIndexData?.PipePrefabricationRate}} - + + + + + + 快捷应用 + + + + + + + + + {{ item.name }} + + + + + + + + + + + + 业务查询 + + + + + + + + {{ item.name }} + + + + + + + + + 选择快捷应用 + + + + + + + {{ item.name }} + + + + + + + 取消 + 确定 + + + + label-key="ProjectName" value-key="ProjectId" :search-key="projectNameKeyword" + @search-change="handleSearchChange" @confirm="handleConfirmProject" /> @@ -73,13 +137,18 @@ ref } from 'vue' import { - useUserStore + useUserStore, + useMenuStore, + useLargeStore } from '@/store'; + import { storeToRefs } from 'pinia'; const userStore = useUserStore() + const menuStore = useMenuStore() + const largeStore = useLargeStore() const { setProjectList, setCurrentProject @@ -89,12 +158,52 @@ projectList, currentProject } = storeToRefs(userStore) + const { + quickApps, + queryApps, + editCache + } = storeToRefs(menuStore) + const { + hjIndexData + } = storeToRefs(largeStore) + const { + pastelColors + } = menuStore const navbarBg = reactive({ backgroundColor: "var(--u-type-primary)" }) + const projectNameKeyword = ref('') const showProject = ref(false) - - const currentProjectName = computed(()=>currentProject.value?.ProjectName||'') + const showEditQuick = ref(false) + + const currentProjectName = computed(() => currentProject.value?.ProjectName || '') + + const handleSkip= (item) => { + uni.navigateTo({ + url: item.path + }) + } + + // 打开编辑弹窗 + const openEditQuick = () => { + menuStore.openEdit() + showEditQuick.value = true + } + + // 切换选择状态 + const toggleSelect = (item) => { + menuStore.toggleSelect(item) + } + + // 确认编辑 + const handleConfirmEdit = () => { + menuStore.confirmEdit() + showEditQuick.value = false + uni.showToast({ + title: '已更新快捷应用', + icon: 'success' + }) + } const handleShowProjectSelect = () => { reqProjectByUserId(userInfo.value.PersonId).then(res => { @@ -105,24 +214,30 @@ /** * 项目列表点击确认 */ - const handleConfirmProject = (item)=>{ + const handleConfirmProject = (item) => { setCurrentProject(item) showProject.value = false } - + /** + * 项目按名称搜索 + */ + const handleSearchChange = (keyword) => { + projectNameKeyword.value = keyword + } + /** * 扫一扫 */ - const handleScan = ()=>{ + const handleScan = () => { uni.scanCode({ onlyFromCamera: true, - success (res) { + success(res) { console.log('条码类型:' + res.scanType); console.log('条码内容:' + res.result); }, fail(err) { - console.log("扫码失败了====>",err) + console.log("扫码失败了====>", err) } }); } - + \ No newline at end of file diff --git a/pages/mine/index.vue b/pages/mine/index.vue new file mode 100644 index 0000000..ee5832e --- /dev/null +++ b/pages/mine/index.vue @@ -0,0 +1,277 @@ + + + + + \ No newline at end of file diff --git a/pipe/diankou/list.vue b/pipe/diankou/list.vue new file mode 100644 index 0000000..d999a42 --- /dev/null +++ b/pipe/diankou/list.vue @@ -0,0 +1,371 @@ + + + + + diff --git a/static/tabs/about-active.png b/static/tabs/about-active.png new file mode 100644 index 0000000..da56d0f Binary files /dev/null and b/static/tabs/about-active.png differ diff --git a/static/tabs/about.png b/static/tabs/about.png new file mode 100644 index 0000000..1b9e1aa Binary files /dev/null and b/static/tabs/about.png differ diff --git a/static/tabs/application-active.png b/static/tabs/application-active.png new file mode 100644 index 0000000..b5cfc45 Binary files /dev/null and b/static/tabs/application-active.png differ diff --git a/static/tabs/application.png b/static/tabs/application.png new file mode 100644 index 0000000..06e3e3d Binary files /dev/null and b/static/tabs/application.png differ diff --git a/static/tabs/home-active.png b/static/tabs/home-active.png new file mode 100644 index 0000000..c2abb17 Binary files /dev/null and b/static/tabs/home-active.png differ diff --git a/static/tabs/home.png b/static/tabs/home.png new file mode 100644 index 0000000..2dc56d5 Binary files /dev/null and b/static/tabs/home.png differ diff --git a/static/tabs/mine-active.png b/static/tabs/mine-active.png new file mode 100644 index 0000000..a0fab65 Binary files /dev/null and b/static/tabs/mine-active.png differ diff --git a/static/tabs/mine.png b/static/tabs/mine.png new file mode 100644 index 0000000..53dfbf3 Binary files /dev/null and b/static/tabs/mine.png differ diff --git a/store/index.js b/store/index.js index 6eb7d4e..3ae962f 100644 --- a/store/index.js +++ b/store/index.js @@ -8,4 +8,6 @@ export function setupStore(app) { } export * from './modules/user' +export * from './modules/menu' +export * from './modules/large' export default pinia diff --git a/store/modules/large.js b/store/modules/large.js new file mode 100644 index 0000000..faa76dc --- /dev/null +++ b/store/modules/large.js @@ -0,0 +1,25 @@ +import { + defineStore +} from 'pinia' +import { + ref, + computed +} from 'vue' +import {reqHJIndexData} from '@/api/hj.js' + +export const useLargeStore = defineStore('large', () => { + const hjIndexData = ref(null) + + /** + * 获取焊接首页数据 + */ + const fetchHJIndex = (projectId)=>{ + reqHJIndexData(projectId).then(res=>{ + hjIndexData.value = res.data + }) + } + return { + hjIndexData, + fetchHJIndex + } +}) diff --git a/store/modules/menu.js b/store/modules/menu.js new file mode 100644 index 0000000..1391eb8 --- /dev/null +++ b/store/modules/menu.js @@ -0,0 +1,102 @@ +import { + defineStore +} from 'pinia' +import { + ref, + computed +} from 'vue' +import {pastelColors,defaultApps} from '@/utils/constant.js' +import {reqMenuPowerList} from '@/api/auth.js' + +export const useMenuStore = defineStore('menu', () => { + // 应用列表(默认配置 + 持久化) + const appsList = ref([...defaultApps]) + // 编辑时的临时缓存 + const editCache = ref([]) + // 菜单的权限列表(后端返回的有权限的菜单 code 数组) + const menuProwerList = ref([]) + + // 快捷应用列表(已选择的) + const quickApps = computed(() => appsList.value.filter(app => app.selected&&hasMenuPermission(app.menuId))) + + // 业务查询列表(未选择的) + const queryApps = computed(() => appsList.value.filter(app => !app.selected&&hasMenuPermission(app.menuId))) + + /** + * 打开编辑弹窗(备份当前状态) + */ + const openEdit = () => { + editCache.value = appsList.value.filter(app => hasMenuPermission(app.menuId)) + } + + /** + * 切换应用选择状态 + */ + const toggleSelect = (item) => { + item.selected = !item.selected + } + + /** + * 确认编辑(保存更改) + */ + const confirmEdit = () => { + appsList.value = editCache.value.map(app => ({ ...app })) + } + + /** + * 请求菜单权限列表 + */ + const fetchMenuPowerList = async (userId, projectId) => { + if (!userId || !projectId) return + try { + const res = await reqMenuPowerList(userId, projectId) + menuProwerList.value = res.data || [] + } catch (err) { + console.error('获取菜单权限失败:', err) + menuProwerList.value = [] + } + } + + /** + * 判断菜单是否有权限打开 + * @param {string} menuCode - 菜单编码/标识 + * @returns {boolean} 是否有权限 + */ + const hasMenuPermission = (menuCode) => { + if (!menuCode) return true + return menuProwerList.value.includes(menuCode) + } + + /** + * 根据权限过滤快捷应用 + */ + const quickAppsWithPermission = computed(() => { + if (menuProwerList.value.length === 0) return quickApps.value + return quickApps.value.filter(app => { + return hasMenuPermission(app.permissionCode) + }) + }) + + return { + appsList, + editCache, + quickApps, + quickAppsWithPermission, + queryApps, + menuProwerList, + pastelColors, + openEdit, + toggleSelect, + confirmEdit, + fetchMenuPowerList, + hasMenuPermission + } +}, { + persist: { + enabled: true, + strategies: [{ + key: 'menu', + paths: [], + }] + }, +}) diff --git a/store/modules/user.js b/store/modules/user.js index 2a29055..f6cb338 100644 --- a/store/modules/user.js +++ b/store/modules/user.js @@ -2,12 +2,13 @@ import { defineStore } from 'pinia' import { - ref + ref, + watch } from 'vue' import { reqLogin } from '@/api/auth.js' - +import { useMenuStore, useLargeStore } from '@/store' export const useUserStore = defineStore('user', () => { const token = ref("") const userInfo = ref(null) @@ -20,6 +21,20 @@ export const useUserStore = defineStore('user', () => { const projectList = ref([]) const currentProject = ref(null) + // 监听 currentProject 变化,获取菜单权限 + watch(() => currentProject.value?.ProjectId, (newId) => { + if (newId) { + // 项目变化 请求菜单权限 + const menuStore = useMenuStore() + const userId = userInfo.value?.PersonId + menuStore.fetchMenuPowerList(userId, newId) + // 项目变化 请求焊接首页数据 + const largeStore = useLargeStore() + largeStore.fetchHJIndex(newId) + + } + }, { immediate: true }) + /** * 登出 @@ -27,11 +42,11 @@ export const useUserStore = defineStore('user', () => { const logout = () => { token.value = "" userInfo.value = null - loginForm.value = { - account: '', - password: '', - Telephone: '', - } + // loginForm.value = { + // account: '', + // password: '', + // Telephone: '', + // } currentProject.value = null } @@ -79,7 +94,8 @@ export const useUserStore = defineStore('user', () => { projectList, currentProject, setProjectList, - setCurrentProject + setCurrentProject, + logout } }, { persist: { diff --git a/utils/constant.js b/utils/constant.js new file mode 100644 index 0000000..f12539d --- /dev/null +++ b/utils/constant.js @@ -0,0 +1,49 @@ +// 淡雅配色方案(马卡龙色系) +export const pastelColors = [ + '#7eb6ff', // 淡蓝 + '#ffb787', // 淡橙 + '#8fd6a8', // 淡绿 + '#b99bf8', // 淡紫 + '#ff8fa3', // 淡粉 + '#80deea', // 青色 + '#ffd56b', // 淡黄 +] + +// 默认应用配置 +export const defaultApps = [{ + name: '点口管理', + icon: 'diankouguanli', + menuId: '3ACE25CE-C5CE-4CEC-AD27-0D5CF1DF2F01', + path: "/pipe/diankou/list", + prefix: 'nbd-icon', + selected: true + }, + { + name: '试压管理', + icon: 'shiyabao', + menuId: '55976B16-2C33-406E-B514-2FE42D031071', + prefix: 'nbd-icon', + selected: true + }, + { + name: '包装管理', + icon: 'baozhuangguanli', + menuId: "25DED954-10C9-47CC-99F2-C44FDE9E0A81", + prefix: 'nbd-icon', + selected: true + }, + { + name: '发货管理', + icon: 'a-facheguanli', + menuId: "EEC0D060-C15E-4D25-B015-C2B91F735DAC", + prefix: 'nbd-icon', + selected: true + }, + { + name: '预制组件管理', + icon: 'yuzhijian', + menuId: "8255554C-0A92-4C7B-BF19-779AF0220A8C", + prefix: 'nbd-icon', + selected: true + } +] \ No newline at end of file