焊接首页
@@ -3,5 +3,13 @@ import {
|
|||||||
post
|
post
|
||||||
} from '@/utils/request.js'
|
} from '@/utils/request.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录接口
|
||||||
|
*/
|
||||||
|
export const reqLogin = (data) => post('user/postLoginOn', data)
|
||||||
|
|
||||||
export const reqLogin = (data) => post('user/postLoginOn', data)
|
/**
|
||||||
|
* 请求控制权限
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const reqMenuPowerList = (userId,projectId)=>get(`User/getMenuPowerList?userId=${userId}&projectId=${projectId}`)
|
||||||
@@ -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}`)
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import {
|
||||||
|
get,
|
||||||
|
post
|
||||||
|
} from '@/utils/request.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取焊接首页数据
|
||||||
|
*/
|
||||||
|
export const reqHJIndexData = (projectId)=>get(`HJGLIndex/GetJGLIndexItem?projectId=${projectId}`)
|
||||||
@@ -26,6 +26,8 @@
|
|||||||
|
|
||||||
// 首页
|
// 首页
|
||||||
.home {
|
.home {
|
||||||
|
padding-top: 80px;
|
||||||
|
padding-bottom: 60rpx;
|
||||||
.u-border-bottom:after {
|
.u-border-bottom:after {
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
@@ -40,7 +42,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&-icons {
|
&-icons {
|
||||||
height: 160rpx;
|
position: fixed;
|
||||||
|
top: 95px;
|
||||||
|
height: 80px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background-color: $u-type-primary;
|
background-color: $u-type-primary;
|
||||||
display: flex;
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
@font-face {
|
@font-face {
|
||||||
font-family: "nbd-icon"; /* Project id 5193086 */
|
font-family: "nbd-icon"; /* Project id 5193086 */
|
||||||
src: url('//at.alicdn.com/t/c/font_5193086_zgg3m1cv22e.woff2?t=1781003671405') format('woff2'),
|
src: url('//at.alicdn.com/t/c/font_5193086_dyc00qt3bz.woff2?t=1781056075220') format('woff2'),
|
||||||
url('//at.alicdn.com/t/c/font_5193086_zgg3m1cv22e.woff?t=1781003671405') format('woff'),
|
url('//at.alicdn.com/t/c/font_5193086_dyc00qt3bz.woff?t=1781056075220') format('woff'),
|
||||||
url('//at.alicdn.com/t/c/font_5193086_zgg3m1cv22e.ttf?t=1781003671405') format('truetype');
|
url('//at.alicdn.com/t/c/font_5193086_dyc00qt3bz.ttf?t=1781056075220') format('truetype');
|
||||||
}
|
}
|
||||||
|
|
||||||
.nbd-icon {
|
.nbd-icon {
|
||||||
@@ -13,6 +13,26 @@
|
|||||||
-moz-osx-font-smoothing: grayscale;
|
-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 {
|
.nbd-icon-daiban:before {
|
||||||
content: "\e612";
|
content: "\e612";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,9 +12,13 @@
|
|||||||
</view>
|
</view>
|
||||||
<scroll-view scroll-y="true" class="project-select-inner">
|
<scroll-view scroll-y="true" class="project-select-inner">
|
||||||
<view class="project-select-inner-wrap">
|
<view class="project-select-inner-wrap">
|
||||||
<view class="li" v-for="(item,idx) in props.list" :key="idx"
|
<view class="li" v-for="(item,idx) in filteredList" :key="idx"
|
||||||
:class="selectObj[props.valueKey] == item[props.valueKey]?'active':''" @click="handerChangList(item)">{{item[props.labelKey]}}
|
:class="selectObj && selectObj[props.valueKey] == item[props.valueKey] ? 'active' : ''"
|
||||||
|
@click="handerChangList(item)"
|
||||||
|
>
|
||||||
|
{{ item[props.labelKey] }}
|
||||||
</view>
|
</view>
|
||||||
|
<view v-if="filteredList.length === 0" class="empty-tip">暂无匹配项目</view>
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
</view>
|
</view>
|
||||||
@@ -24,7 +28,8 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import {
|
import {
|
||||||
ref,
|
ref,
|
||||||
watch
|
watch,
|
||||||
|
computed
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -61,6 +66,14 @@
|
|||||||
|
|
||||||
const selectObj = ref(null)
|
const selectObj = ref(null)
|
||||||
|
|
||||||
|
// 过滤后的列表
|
||||||
|
const filteredList = computed(() => {
|
||||||
|
if (!props.searchKey) return props.list
|
||||||
|
return props.list.filter(item =>
|
||||||
|
item[props.labelKey]?.includes(props.searchKey)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
watch(() => props.modelValue, (val) => {
|
watch(() => props.modelValue, (val) => {
|
||||||
visible.value = val
|
visible.value = val
|
||||||
if(val){
|
if(val){
|
||||||
@@ -68,7 +81,7 @@
|
|||||||
if (selectedItem) {
|
if (selectedItem) {
|
||||||
selectObj.value = selectedItem
|
selectObj.value = selectedItem
|
||||||
} else {
|
} else {
|
||||||
selectObj.value = {}
|
selectObj.value = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -91,4 +104,60 @@
|
|||||||
const handleSearchKey = (val) => {
|
const handleSearchKey = (val) => {
|
||||||
emit('searchChange', val)
|
emit('searchChange', val)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.project-select {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 24rpx 24rpx 0 0;
|
||||||
|
|
||||||
|
.project-select-btns {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 24rpx 32rpx;
|
||||||
|
border-bottom: 1rpx solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-select-filter {
|
||||||
|
padding: 16rpx 32rpx;
|
||||||
|
border-bottom: 1rpx solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-select-inner {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
.project-select-inner-wrap {
|
||||||
|
padding: 0 32rpx 32rpx;
|
||||||
|
|
||||||
|
.li {
|
||||||
|
padding: 28rpx 0;
|
||||||
|
border-bottom: 1rpx solid #f5f6f8;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333;
|
||||||
|
transition: all 0.2s;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
color: $u-type-primary;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-tip {
|
||||||
|
text-align: center;
|
||||||
|
padding: 80rpx 0;
|
||||||
|
color: #999;
|
||||||
|
font-size: 26rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,325 @@
|
|||||||
|
<template>
|
||||||
|
<u-popup v-model="localVisible" mode="bottom" :height="height">
|
||||||
|
<view class="nbd-select">
|
||||||
|
<!-- 标题栏 -->
|
||||||
|
<view class="nbd-select-header" v-if="title">
|
||||||
|
<text class="nbd-select-title">{{ title }}</text>
|
||||||
|
<text class="nbd-select-tip" v-if="tip">{{ tip }}</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 搜索栏 -->
|
||||||
|
<view class="nbd-select-filter" v-if="showSearch">
|
||||||
|
<u-search
|
||||||
|
:placeholder="searchPlaceholder"
|
||||||
|
:height="72"
|
||||||
|
v-model="searchKey"
|
||||||
|
:show-action="false"
|
||||||
|
@change="handleSearch"
|
||||||
|
@clear="handleSearch"
|
||||||
|
></u-search>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 列表内容 -->
|
||||||
|
<scroll-view scroll-y="true" class="nbd-select-inner">
|
||||||
|
<view class="nbd-select-inner-wrap">
|
||||||
|
<!-- 空状态 -->
|
||||||
|
<view class="nbd-select-empty" v-if="filteredList.length === 0">
|
||||||
|
<u-empty mode="list" text="暂无数据"></u-empty>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 列表项 -->
|
||||||
|
<view
|
||||||
|
v-for="(item, idx) in filteredList"
|
||||||
|
:key="idx"
|
||||||
|
class="nbd-select-item"
|
||||||
|
:class="{ active: isSelected(item) }"
|
||||||
|
@click="handleItemClick(item)"
|
||||||
|
>
|
||||||
|
<text class="nbd-select-item-text">{{ item[labelKey] }}</text>
|
||||||
|
<view v-if="multiple" class="nbd-select-checkbox">
|
||||||
|
<u-icon
|
||||||
|
:name="isSelected(item) ? 'checkbox-mark' : 'square'"
|
||||||
|
:size="36"
|
||||||
|
:color="isSelected(item) ? $u.type.primary : '#dcdee0'"
|
||||||
|
></u-icon>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
|
||||||
|
<!-- 底部按钮 -->
|
||||||
|
<view class="nbd-select-footer">
|
||||||
|
<u-button class="nbd-select-btn-cancel" @click="handleCancel">
|
||||||
|
{{ cancelText }}
|
||||||
|
</u-button>
|
||||||
|
<u-button class="nbd-select-btn-confirm" type="primary" @click="handleConfirm">
|
||||||
|
{{ confirmText }}
|
||||||
|
</u-button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</u-popup>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed, watch } from 'vue'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
// 弹窗显示状态
|
||||||
|
modelValue: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
// 弹窗高度
|
||||||
|
height: {
|
||||||
|
type: String,
|
||||||
|
default: '70%'
|
||||||
|
},
|
||||||
|
// 标题
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
// 标题提示
|
||||||
|
tip: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
// 数据列表
|
||||||
|
list: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
// 选中项(支持数组,多选时使用)
|
||||||
|
model: {
|
||||||
|
type: [Object, Array, String, Number],
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
// 是否多选
|
||||||
|
multiple: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
// 显示搜索框
|
||||||
|
showSearch: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
// 搜索占位符
|
||||||
|
searchPlaceholder: {
|
||||||
|
type: String,
|
||||||
|
default: '请输入关键词搜索'
|
||||||
|
},
|
||||||
|
// 字段名配置
|
||||||
|
labelKey: {
|
||||||
|
type: String,
|
||||||
|
default: 'label'
|
||||||
|
},
|
||||||
|
valueKey: {
|
||||||
|
type: String,
|
||||||
|
default: 'value'
|
||||||
|
},
|
||||||
|
// 自定义搜索字段(默认搜索 labelKey)
|
||||||
|
searchKeyField: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
// 按钮文字
|
||||||
|
cancelText: {
|
||||||
|
type: String,
|
||||||
|
default: '取消'
|
||||||
|
},
|
||||||
|
confirmText: {
|
||||||
|
type: String,
|
||||||
|
default: '确定'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits([
|
||||||
|
'update:modelValue',
|
||||||
|
'update:model',
|
||||||
|
'cancel',
|
||||||
|
'confirm',
|
||||||
|
'searchChange',
|
||||||
|
'change'
|
||||||
|
])
|
||||||
|
|
||||||
|
const localVisible = ref(false)
|
||||||
|
const searchKey = ref('')
|
||||||
|
|
||||||
|
// 当前选中的项(多选时为数组)
|
||||||
|
const selectedItems = ref(props.multiple ? (props.model || []) : (props.model || null))
|
||||||
|
|
||||||
|
// 监听外部 model 变化
|
||||||
|
watch(() => props.model, (newVal) => {
|
||||||
|
selectedItems.value = props.multiple ? (newVal || []) : newVal
|
||||||
|
}, { deep: true })
|
||||||
|
|
||||||
|
// 弹窗状态同步
|
||||||
|
watch(() => props.modelValue, (val) => {
|
||||||
|
localVisible.value = val
|
||||||
|
if (val) {
|
||||||
|
// 打开时重置搜索
|
||||||
|
searchKey.value = ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(localVisible, (val) => {
|
||||||
|
emit('update:modelValue', val)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 过滤后的列表
|
||||||
|
const filteredList = computed(() => {
|
||||||
|
if (!searchKey.value) return props.list
|
||||||
|
|
||||||
|
const keyword = searchKey.value.toLowerCase()
|
||||||
|
const searchField = props.searchKeyField || props.labelKey
|
||||||
|
|
||||||
|
return props.list.filter(item => {
|
||||||
|
const value = item[searchField]
|
||||||
|
if (value === null || value === undefined) return false
|
||||||
|
return String(value).toLowerCase().includes(keyword)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// 判断是否选中
|
||||||
|
const isSelected = (item) => {
|
||||||
|
if (props.multiple) {
|
||||||
|
return selectedItems.value.some(
|
||||||
|
selected => selected[props.valueKey] === item[props.valueKey]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (selectedItems.value && typeof selectedItems.value === 'object') {
|
||||||
|
return selectedItems.value[props.valueKey] === item[props.valueKey]
|
||||||
|
}
|
||||||
|
return selectedItems.value === item[props.valueKey]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 点击列表项
|
||||||
|
const handleItemClick = (item) => {
|
||||||
|
if (props.multiple) {
|
||||||
|
const index = selectedItems.value.findIndex(
|
||||||
|
selected => selected[props.valueKey] === item[props.valueKey]
|
||||||
|
)
|
||||||
|
if (index > -1) {
|
||||||
|
selectedItems.value.splice(index, 1)
|
||||||
|
} else {
|
||||||
|
selectedItems.value.push(item)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
selectedItems.value = item
|
||||||
|
}
|
||||||
|
emit('change', selectedItems.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索
|
||||||
|
const handleSearch = (val) => {
|
||||||
|
emit('searchChange', val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消
|
||||||
|
const handleCancel = () => {
|
||||||
|
emit('cancel')
|
||||||
|
localVisible.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确认
|
||||||
|
const handleConfirm = () => {
|
||||||
|
emit('confirm', selectedItems.value)
|
||||||
|
emit('update:model', selectedItems.value)
|
||||||
|
localVisible.value = false
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.nbd-select {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 24rpx 24rpx 0 0;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&-header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 24rpx 32rpx 20rpx;
|
||||||
|
border-bottom: 1rpx solid #f0f0f0;
|
||||||
|
|
||||||
|
.nbd-select-title {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nbd-select-tip {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-filter {
|
||||||
|
padding: 16rpx 32rpx;
|
||||||
|
border-bottom: 1rpx solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-inner {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 0 32rpx;
|
||||||
|
|
||||||
|
&-wrap {
|
||||||
|
padding: 16rpx 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-empty {
|
||||||
|
padding: 80rpx 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 28rpx 0;
|
||||||
|
border-bottom: 1rpx solid #f5f6f8;
|
||||||
|
transition: background 0.2s;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active .nbd-select-item-text {
|
||||||
|
color: $u-type-primary;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nbd-select-item-text {
|
||||||
|
flex: 1;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nbd-select-checkbox {
|
||||||
|
margin-left: 16rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-footer {
|
||||||
|
display: flex;
|
||||||
|
gap: 24rpx;
|
||||||
|
padding: 20rpx 32rpx 40rpx;
|
||||||
|
|
||||||
|
.nbd-select-btn-cancel,
|
||||||
|
.nbd-select-btn-confirm {
|
||||||
|
flex: 1;
|
||||||
|
height: 80rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
border-radius: 40rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -20,8 +20,47 @@
|
|||||||
"navigationBarTitleText": "首页",
|
"navigationBarTitleText": "首页",
|
||||||
"navigationStyle": "custom"
|
"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": {
|
"globalStyle": {
|
||||||
"navigationBarTextStyle": "white",
|
"navigationBarTextStyle": "white",
|
||||||
"navigationBarTitleText": "施工管理",
|
"navigationBarTitleText": "施工管理",
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
</view>
|
</view>
|
||||||
</u-navbar>
|
</u-navbar>
|
||||||
<view class="home-icons">
|
<view class="home-icons">
|
||||||
|
|
||||||
<view class="home-icon" @click="handleScan">
|
<view class="home-icon" @click="handleScan">
|
||||||
<u-icon name="saoyisao" custom-prefix="nbd-icon" :size="68" color="#ffffff" />
|
<u-icon name="saoyisao" custom-prefix="nbd-icon" :size="68" color="#ffffff" />
|
||||||
<text>扫一扫</text>
|
<text>扫一扫</text>
|
||||||
@@ -18,48 +18,112 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="home-hj">
|
<view class="home-hj">
|
||||||
<view class="hj-title">项目管道实时预制率</view>
|
|
||||||
<view class="hj-content">
|
<view class="hj-content">
|
||||||
<view class="hj-row">
|
<view class="hj-row">
|
||||||
<view class="hj-cell">
|
<view class="hj-cell">
|
||||||
<text class="hj-label">工厂焊接一次合格率</text>
|
<text class="hj-label">工厂焊接一次合格率</text>
|
||||||
<text class="hj-value">99.5%</text>
|
<text class="hj-value">{{hjIndexData?.GCFirstPassRate}}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="hj-cell">
|
<view class="hj-cell">
|
||||||
<text class="hj-label">现场焊接一次合格率</text>
|
<text class="hj-label">现场焊接一次合格率</text>
|
||||||
<text class="hj-value">0%</text>
|
<text class="hj-value">{{hjIndexData?.XCFirstPassRate}}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="hj-row">
|
<view class="hj-row">
|
||||||
<view class="hj-cell">
|
<view class="hj-cell">
|
||||||
<text class="hj-label">工厂预制进度</text>
|
<text class="hj-label">工厂预制进度</text>
|
||||||
<text class="hj-value hj-sub">49229.500/286639.000 DIN</text>
|
<text class="hj-value hj-sub">{{hjIndexData?.GCProgress}}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="hj-cell">
|
<view class="hj-cell">
|
||||||
<text class="hj-label">现场焊接进度</text>
|
<text class="hj-label">现场焊接进度</text>
|
||||||
<text class="hj-value hj-sub">5906.500/84951.000 DIN</text>
|
<text class="hj-value hj-sub">{{hjIndexData?.XCProgress}}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="hj-row">
|
<view class="hj-row">
|
||||||
<view class="hj-cell">
|
<view class="hj-cell">
|
||||||
<text class="hj-label">工厂焊工功效</text>
|
<text class="hj-label">工厂焊工功效</text>
|
||||||
<text class="hj-value">252 DIN/天</text>
|
<text class="hj-value">{{hjIndexData?.GCWelderEfficacy}}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="hj-cell">
|
<view class="hj-cell">
|
||||||
<text class="hj-label">现场焊接功效</text>
|
<text class="hj-label">现场焊接功效</text>
|
||||||
<text class="hj-value">95 DIN/天</text>
|
<text class="hj-value">{{hjIndexData?.XCWelderEfficacy}}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="hj-footer">
|
<view class="hj-footer">
|
||||||
<text class="hj-footer-label">项目管道实时预制率 (包含工厂和现场的预制口)</text>
|
<text class="hj-footer-label">项目管道实时预制率 (包含工厂和现场的预制口)</text>
|
||||||
<text class="hj-footer-value">13%</text>
|
<text class="hj-footer-value">{{hjIndexData?.PipePrefabricationRate}}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<!-- 快捷应用 -->
|
||||||
|
<view class="section-card home-quick">
|
||||||
|
<view class="section-title">
|
||||||
|
<view class="title-left">
|
||||||
|
<text class="title-line"></text>
|
||||||
|
<text>快捷应用</text>
|
||||||
|
</view>
|
||||||
|
<u-icon class="edit-btn" name="edit-pen" :size="32" color="#999" @click="openEditQuick" />
|
||||||
|
</view>
|
||||||
|
<view class="section-grid">
|
||||||
|
<view class="grid-item" v-for="(item, idx) in quickApps" :key="idx" @click="handleSkip(item)">
|
||||||
|
<view class="icon-box">
|
||||||
|
<u-icon :name="item.icon" :size="56" :custom-prefix='item.prefix'
|
||||||
|
:color="pastelColors[idx % 6]" />
|
||||||
|
</view>
|
||||||
|
<text class="item-label">{{ item.name }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- 业务查询 -->
|
||||||
|
<view class="section-card home-query" v-if="queryApps&&queryApps.length>0">
|
||||||
|
<view class="section-title">
|
||||||
|
<view class="title-left">
|
||||||
|
<text class="title-line"></text>
|
||||||
|
<text>业务查询</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="section-grid">
|
||||||
|
<view class="grid-item" v-for="(item, idx) in queryApps" :key="idx" @click="handleSkip(item)">
|
||||||
|
<view class="icon-box">
|
||||||
|
<u-icon :name="item.icon" :custom-prefix='item.prefix' :size="56" :color="item.color" />
|
||||||
|
</view>
|
||||||
|
<text class="item-label">{{ item.name }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 编辑快捷应用弹窗 -->
|
||||||
|
<u-popup v-model="showEditQuick" mode="bottom" height="70%">
|
||||||
|
<view class="edit-quick">
|
||||||
|
<view class="edit-title">
|
||||||
|
<text>选择快捷应用</text>
|
||||||
|
</view>
|
||||||
|
<view class="edit-grid">
|
||||||
|
<view v-for="(item, idx) in editCache" :key="idx" class="edit-item"
|
||||||
|
:class="{ active: item.selected }" @click="toggleSelect(item)">
|
||||||
|
<view class="icon-box">
|
||||||
|
<u-icon :name="item.icon" :size="48" :custom-prefix='item.prefix'
|
||||||
|
:color="item.selected ? pastelColors[idx % 6] : '#ccc'" />
|
||||||
|
</view>
|
||||||
|
<text class="edit-label">{{ item.name }}</text>
|
||||||
|
<view v-if="item.selected" class="selected-mark">
|
||||||
|
<u-icon name="checkmark" :size="24" color="#fff" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="edit-btns">
|
||||||
|
<u-button size="medium" class="btn-cancel" @click="showEditQuick = false">取消</u-button>
|
||||||
|
<u-button size="medium" class="btn-confirm" type="primary" @click="handleConfirmEdit">确定</u-button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</u-popup>
|
||||||
<nbd-select-project v-model="showProject" :select-id="currentProject?.ProjectId" :list="projectList"
|
<nbd-select-project v-model="showProject" :select-id="currentProject?.ProjectId" :list="projectList"
|
||||||
label-key="ProjectName" value-key="ProjectId" @confirm="handleConfirmProject" />
|
label-key="ProjectName" value-key="ProjectId" :search-key="projectNameKeyword"
|
||||||
|
@search-change="handleSearchChange" @confirm="handleConfirmProject" />
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -73,13 +137,18 @@
|
|||||||
ref
|
ref
|
||||||
} from 'vue'
|
} from 'vue'
|
||||||
import {
|
import {
|
||||||
useUserStore
|
useUserStore,
|
||||||
|
useMenuStore,
|
||||||
|
useLargeStore
|
||||||
} from '@/store';
|
} from '@/store';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
storeToRefs
|
storeToRefs
|
||||||
} from 'pinia';
|
} from 'pinia';
|
||||||
|
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
|
const menuStore = useMenuStore()
|
||||||
|
const largeStore = useLargeStore()
|
||||||
const {
|
const {
|
||||||
setProjectList,
|
setProjectList,
|
||||||
setCurrentProject
|
setCurrentProject
|
||||||
@@ -89,12 +158,52 @@
|
|||||||
projectList,
|
projectList,
|
||||||
currentProject
|
currentProject
|
||||||
} = storeToRefs(userStore)
|
} = storeToRefs(userStore)
|
||||||
|
const {
|
||||||
|
quickApps,
|
||||||
|
queryApps,
|
||||||
|
editCache
|
||||||
|
} = storeToRefs(menuStore)
|
||||||
|
const {
|
||||||
|
hjIndexData
|
||||||
|
} = storeToRefs(largeStore)
|
||||||
|
const {
|
||||||
|
pastelColors
|
||||||
|
} = menuStore
|
||||||
const navbarBg = reactive({
|
const navbarBg = reactive({
|
||||||
backgroundColor: "var(--u-type-primary)"
|
backgroundColor: "var(--u-type-primary)"
|
||||||
})
|
})
|
||||||
|
const projectNameKeyword = ref('')
|
||||||
const showProject = ref(false)
|
const showProject = ref(false)
|
||||||
|
const showEditQuick = ref(false)
|
||||||
const currentProjectName = computed(()=>currentProject.value?.ProjectName||'')
|
|
||||||
|
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 = () => {
|
const handleShowProjectSelect = () => {
|
||||||
reqProjectByUserId(userInfo.value.PersonId).then(res => {
|
reqProjectByUserId(userInfo.value.PersonId).then(res => {
|
||||||
@@ -105,24 +214,30 @@
|
|||||||
/**
|
/**
|
||||||
* 项目列表点击确认
|
* 项目列表点击确认
|
||||||
*/
|
*/
|
||||||
const handleConfirmProject = (item)=>{
|
const handleConfirmProject = (item) => {
|
||||||
setCurrentProject(item)
|
setCurrentProject(item)
|
||||||
showProject.value = false
|
showProject.value = false
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* 项目按名称搜索
|
||||||
|
*/
|
||||||
|
const handleSearchChange = (keyword) => {
|
||||||
|
projectNameKeyword.value = keyword
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 扫一扫
|
* 扫一扫
|
||||||
*/
|
*/
|
||||||
const handleScan = ()=>{
|
const handleScan = () => {
|
||||||
uni.scanCode({
|
uni.scanCode({
|
||||||
onlyFromCamera: true,
|
onlyFromCamera: true,
|
||||||
success (res) {
|
success(res) {
|
||||||
console.log('条码类型:' + res.scanType);
|
console.log('条码类型:' + res.scanType);
|
||||||
console.log('条码内容:' + res.result);
|
console.log('条码内容:' + res.result);
|
||||||
},
|
},
|
||||||
fail(err) {
|
fail(err) {
|
||||||
console.log("扫码失败了====>",err)
|
console.log("扫码失败了====>", err)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -0,0 +1,277 @@
|
|||||||
|
<template>
|
||||||
|
<view class="mine">
|
||||||
|
<!-- 用户信息卡片 -->
|
||||||
|
<view class="user-card">
|
||||||
|
<view class="user-info">
|
||||||
|
<u-avatar :size="120" icon="account" bgColor="#e0e7ff"></u-avatar>
|
||||||
|
<view class="user-detail">
|
||||||
|
<text class="user-name">{{ userInfo?.PersonName || '未登录' }}</text>
|
||||||
|
<text class="user-account">账号:{{ userInfo?.Account || '-' }}</text>
|
||||||
|
<text class="user-company">{{ userInfo?.CompanyName || '暂无公司信息' }}</text>
|
||||||
|
<text class="user-phone">{{ userInfo?.Telephone || '暂无手机号' }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<u-button v-if="!token" size="large" shape="circle" @click="goLogin">
|
||||||
|
立即登录
|
||||||
|
</u-button>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 项目信息 -->
|
||||||
|
<view class="project-card" v-if="currentProject?.ProjectId">
|
||||||
|
<view class="card-title">
|
||||||
|
<u-icon name="home" :size="36" color="#2979ff"></u-icon>
|
||||||
|
<text>当前项目</text>
|
||||||
|
</view>
|
||||||
|
<view class="project-info">
|
||||||
|
<text class="project-name">{{ currentProject.ProjectName }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 功能菜单 -->
|
||||||
|
<view class="menu-list">
|
||||||
|
<view class="menu-item" @click="handleMenuClick('profile')">
|
||||||
|
<view class="menu-left">
|
||||||
|
<u-icon name="setting" :size="40" color="#2979ff"></u-icon>
|
||||||
|
<text>个人设置</text>
|
||||||
|
</view>
|
||||||
|
<u-icon name="arrow-right" :size="28" color="#ccc"></u-icon>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="menu-item" @click="handleMenuClick('password')">
|
||||||
|
<view class="menu-left">
|
||||||
|
<u-icon name="lock" :size="40" color="#2979ff"></u-icon>
|
||||||
|
<text>修改密码</text>
|
||||||
|
</view>
|
||||||
|
<u-icon name="arrow-right" :size="28" color="#ccc"></u-icon>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="menu-item" @click="handleMenuClick('about')">
|
||||||
|
<view class="menu-left">
|
||||||
|
<u-icon name="info-circle" :size="40" color="#2979ff"></u-icon>
|
||||||
|
<text>关于我们</text>
|
||||||
|
</view>
|
||||||
|
<u-icon name="arrow-right" :size="28" color="#ccc"></u-icon>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 退出登录 -->
|
||||||
|
<view class="logout-btn" v-if="token">
|
||||||
|
<u-button text="退出登录" @click="handleLogout"></u-button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {
|
||||||
|
computed
|
||||||
|
} from 'vue'
|
||||||
|
import {
|
||||||
|
useUserStore
|
||||||
|
} from '@/store'
|
||||||
|
import {
|
||||||
|
storeToRefs
|
||||||
|
} from 'pinia'
|
||||||
|
|
||||||
|
const userStore = useUserStore()
|
||||||
|
const {
|
||||||
|
logout
|
||||||
|
} = userStore
|
||||||
|
const {
|
||||||
|
userInfo,
|
||||||
|
token,
|
||||||
|
currentProject
|
||||||
|
} = storeToRefs(userStore)
|
||||||
|
|
||||||
|
// 跳转登录
|
||||||
|
const goLogin = () => {
|
||||||
|
uni.reLaunch({
|
||||||
|
url: '/pages/login/index'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换项目
|
||||||
|
const handleSwitchProject = () => {
|
||||||
|
// TODO: 打开项目选择弹窗
|
||||||
|
uni.showToast({
|
||||||
|
title: '项目选择功能开发中',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 菜单点击
|
||||||
|
const handleMenuClick = (type) => {
|
||||||
|
const menuMap = {
|
||||||
|
profile: '个人设置',
|
||||||
|
password: '修改密码',
|
||||||
|
about: '关于我们'
|
||||||
|
}
|
||||||
|
uni.showToast({
|
||||||
|
title: `${menuMap[type]}功能开发中`,
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 退出登录
|
||||||
|
const handleLogout = () => {
|
||||||
|
uni.showModal({
|
||||||
|
title: '提示',
|
||||||
|
content: '确定要退出登录吗?',
|
||||||
|
success: (res) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
logout()
|
||||||
|
uni.reLaunch({
|
||||||
|
url: '/pages/login/index'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.mine {
|
||||||
|
min-height: 100vh;
|
||||||
|
background: #f5f6f8;
|
||||||
|
padding: 32rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用户信息卡片
|
||||||
|
.user-card {
|
||||||
|
background: linear-gradient(135deg, #2979ff 0%, #6ba7ff 100%);
|
||||||
|
border-radius: 24rpx;
|
||||||
|
padding: 40rpx 32rpx;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
color: #ffffff;
|
||||||
|
|
||||||
|
.user-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
margin-bottom: 32rpx;
|
||||||
|
|
||||||
|
.user-detail {
|
||||||
|
margin-left: 24rpx;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
.user-name {
|
||||||
|
font-size: 36rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-account,
|
||||||
|
.user-company,
|
||||||
|
.user-phone {
|
||||||
|
font-size: 24rpx;
|
||||||
|
opacity: 0.85;
|
||||||
|
margin-bottom: 4rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-account {
|
||||||
|
color: rgba(255, 255, 255, 0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-company {
|
||||||
|
color: rgba(255, 255, 255, 0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-phone {
|
||||||
|
color: rgba(255, 255, 255, 0.8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.u-button {
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
border: none;
|
||||||
|
color: #ffffff;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 项目信息卡片
|
||||||
|
.project-card {
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 24rpx;
|
||||||
|
padding: 32rpx;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 30rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
|
||||||
|
.u-icon {
|
||||||
|
margin-right: 12rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.project-name {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #666;
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 功能菜单
|
||||||
|
.menu-list {
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 24rpx;
|
||||||
|
padding: 0 32rpx;
|
||||||
|
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
|
||||||
|
|
||||||
|
.menu-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 32rpx 0;
|
||||||
|
border-bottom: 1rpx solid #f0f0f0;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.u-icon {
|
||||||
|
margin-right: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 退出登录
|
||||||
|
.logout-btn {
|
||||||
|
margin-top: 48rpx;
|
||||||
|
|
||||||
|
.u-button {
|
||||||
|
height: 88rpx;
|
||||||
|
font-size: 30rpx;
|
||||||
|
border-radius: 44rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,371 @@
|
|||||||
|
<template>
|
||||||
|
<view class="page">
|
||||||
|
<!-- 头部过滤区域 -->
|
||||||
|
<view class="filter-header">
|
||||||
|
<view class="filter-row">
|
||||||
|
<!-- 下拉过滤 1 -->
|
||||||
|
<u-dropdown v-model="filter1Value" :options="filter1Options" @change="handleFilter1Change"></u-dropdown>
|
||||||
|
|
||||||
|
<!-- 下拉过滤 2 -->
|
||||||
|
<u-dropdown v-model="filter2Value" :options="filter2Options" @change="handleFilter2Change"></u-dropdown>
|
||||||
|
|
||||||
|
<!-- 刷新按钮 -->
|
||||||
|
<view class="refresh-btn" @click="handleRefresh">
|
||||||
|
<u-icon name="reload" :size="36" :class="{ rotating: loading }"></u-icon>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 列表内容 -->
|
||||||
|
<scroll-view
|
||||||
|
class="list-scroll"
|
||||||
|
scroll-y
|
||||||
|
:scroll-into-view="scrollIntoView"
|
||||||
|
@scrolltolower="loadMore"
|
||||||
|
refresher-enabled
|
||||||
|
:refresher-triggered="refreshing"
|
||||||
|
@refresh="handleRefresh"
|
||||||
|
>
|
||||||
|
<!-- 列表项 -->
|
||||||
|
<view class="list-wrap">
|
||||||
|
<view
|
||||||
|
v-for="(item, index) in listData"
|
||||||
|
:key="item.id || index"
|
||||||
|
class="list-item"
|
||||||
|
@click="handleItemClick(item)"
|
||||||
|
>
|
||||||
|
<view class="item-header">
|
||||||
|
<text class="item-title">{{ item.name || '未命名' }}</text>
|
||||||
|
<text class="item-status" :class="item.status">{{ item.statusText || '' }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="item-content">
|
||||||
|
<text class="item-desc">{{ item.description || '' }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="item-footer">
|
||||||
|
<text class="item-time">{{ item.createTime || '' }}</text>
|
||||||
|
<text class="item-author">{{ item.creator || '' }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 空状态 -->
|
||||||
|
<u-empty
|
||||||
|
v-if="!loading && listData.length === 0"
|
||||||
|
mode="list"
|
||||||
|
text="暂无数据"
|
||||||
|
></u-empty>
|
||||||
|
|
||||||
|
<!-- 加载提示 -->
|
||||||
|
<view v-if="loading && listData.length > 0" class="loading-more">
|
||||||
|
<u-loading mode="circle" size="32"></u-loading>
|
||||||
|
<text>加载中...</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 没有更多 -->
|
||||||
|
<view v-if="!loading && !hasMore && listData.length > 0" class="no-more">
|
||||||
|
<text>— 没有更多了 —</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, reactive, onMounted } from 'vue'
|
||||||
|
|
||||||
|
// ===== 过滤条件 =====
|
||||||
|
const filter1Value = ref('')
|
||||||
|
const filter2Value = ref('')
|
||||||
|
|
||||||
|
// 过滤选项 1
|
||||||
|
const filter1Options = ref([
|
||||||
|
{ label: '全部类型', value: '' },
|
||||||
|
{ label: '类型 A', value: 'type_a' },
|
||||||
|
{ label: '类型 B', value: 'type_b' },
|
||||||
|
{ label: '类型 C', value: 'type_c' }
|
||||||
|
])
|
||||||
|
|
||||||
|
// 过滤选项 2
|
||||||
|
const filter2Options = ref([
|
||||||
|
{ label: '全部状态', value: '' },
|
||||||
|
{ label: '进行中', value: 'pending' },
|
||||||
|
{ label: '已完成', value: 'completed' },
|
||||||
|
{ label: '已取消', value: 'cancelled' }
|
||||||
|
])
|
||||||
|
|
||||||
|
// ===== 列表数据 =====
|
||||||
|
const listData = ref([])
|
||||||
|
const loading = ref(false)
|
||||||
|
const refreshing = ref(false)
|
||||||
|
const hasMore = ref(true)
|
||||||
|
const currentPage = ref(1)
|
||||||
|
const pageSize = 20
|
||||||
|
const scrollIntoView = ref('')
|
||||||
|
|
||||||
|
// ===== 筛选条件变更 =====
|
||||||
|
const filterParams = reactive({
|
||||||
|
filter1: '',
|
||||||
|
filter2: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
// 过滤 1 变化
|
||||||
|
const handleFilter1Change = (value) => {
|
||||||
|
filterParams.filter1 = value
|
||||||
|
resetAndLoad()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 过滤 2 变化
|
||||||
|
const handleFilter2Change = (value) => {
|
||||||
|
filterParams.filter2 = value
|
||||||
|
resetAndLoad()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 数据加载 =====
|
||||||
|
|
||||||
|
// 加载数据
|
||||||
|
const fetchData = async (isRefresh = false) => {
|
||||||
|
if (loading.value || (!isRefresh && !hasMore.value)) return
|
||||||
|
|
||||||
|
loading.value = true
|
||||||
|
if (isRefresh) {
|
||||||
|
refreshing.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// TODO: 替换为实际 API 调用
|
||||||
|
// const res = await api.getList({
|
||||||
|
// page: currentPage.value,
|
||||||
|
// pageSize,
|
||||||
|
// ...filterParams
|
||||||
|
// })
|
||||||
|
|
||||||
|
// 模拟数据
|
||||||
|
const mockData = await mockApiFetch({
|
||||||
|
page: currentPage.value,
|
||||||
|
pageSize,
|
||||||
|
...filterParams
|
||||||
|
})
|
||||||
|
|
||||||
|
if (isRefresh) {
|
||||||
|
listData.value = mockData.list
|
||||||
|
} else {
|
||||||
|
listData.value = [...listData.value, ...mockData.list]
|
||||||
|
}
|
||||||
|
|
||||||
|
hasMore.value = mockData.hasMore
|
||||||
|
currentPage.value++
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载数据失败:', error)
|
||||||
|
uni.showToast({
|
||||||
|
title: '加载失败,请重试',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
refreshing.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置并重新加载
|
||||||
|
const resetAndLoad = () => {
|
||||||
|
currentPage.value = 1
|
||||||
|
hasMore.value = true
|
||||||
|
listData.value = []
|
||||||
|
fetchData(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下拉刷新
|
||||||
|
const handleRefresh = () => {
|
||||||
|
resetAndLoad()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上拉加载更多
|
||||||
|
const loadMore = () => {
|
||||||
|
if (!loading.value && hasMore.value) {
|
||||||
|
fetchData(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 列表项点击
|
||||||
|
const handleItemClick = (item) => {
|
||||||
|
console.log('点击列表项:', item)
|
||||||
|
// TODO: 跳转详情
|
||||||
|
// uni.navigateTo({ url: `/pages/xxx/detail?id=${item.id}` })
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 模拟 API(实际使用时删除)=====
|
||||||
|
const mockApiFetch = async (params) => {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
const start = (params.page - 1) * params.pageSize
|
||||||
|
const end = start + params.pageSize
|
||||||
|
const hasMore = end < 100 // 模拟总共 100 条数据
|
||||||
|
|
||||||
|
const list = Array.from({ length: hasMore ? params.pageSize : (100 - start) }, (_, i) => ({
|
||||||
|
id: start + i + 1,
|
||||||
|
name: `项目 ${start + i + 1}`,
|
||||||
|
description: '这是一个测试项目的描述信息,可能包含更多内容',
|
||||||
|
status: ['pending', 'completed', 'cancelled'][Math.floor(Math.random() * 3)],
|
||||||
|
statusText: ['进行中', '已完成', '已取消'][Math.floor(Math.random() * 3)],
|
||||||
|
createTime: '2024-01-01 12:00:00',
|
||||||
|
creator: `用户${Math.floor(Math.random() * 10) + 1}`
|
||||||
|
}))
|
||||||
|
|
||||||
|
resolve({
|
||||||
|
list,
|
||||||
|
hasMore
|
||||||
|
})
|
||||||
|
}, 500)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 生命周期 =====
|
||||||
|
onMounted(() => {
|
||||||
|
fetchData(true)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.page {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100vh;
|
||||||
|
background: #f5f6f8;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 头部过滤区域
|
||||||
|
.filter-header {
|
||||||
|
background: #ffffff;
|
||||||
|
padding: 20rpx 24rpx;
|
||||||
|
border-bottom: 1rpx solid #f0f0f0;
|
||||||
|
|
||||||
|
.filter-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.refresh-btn {
|
||||||
|
margin-left: auto;
|
||||||
|
padding: 8rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.rotating {
|
||||||
|
animation: rotate 1s linear infinite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes rotate {
|
||||||
|
from {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 列表滚动区域
|
||||||
|
.list-scroll {
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-wrap {
|
||||||
|
padding: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 列表项
|
||||||
|
.list-item {
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
padding: 28rpx;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
|
||||||
|
.item-title {
|
||||||
|
font-size: 30rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-status {
|
||||||
|
font-size: 22rpx;
|
||||||
|
padding: 6rpx 16rpx;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
background: #f0f0f0;
|
||||||
|
color: #666;
|
||||||
|
|
||||||
|
&.pending {
|
||||||
|
background: rgba(255, 167, 38, 0.1);
|
||||||
|
color: #ffa726;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.completed {
|
||||||
|
background: rgba(76, 175, 80, 0.1);
|
||||||
|
color: #4caf50;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.cancelled {
|
||||||
|
background: rgba(244, 67, 54, 0.1);
|
||||||
|
color: #f44336;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-content {
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
|
||||||
|
.item-desc {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #666;
|
||||||
|
line-height: 1.6;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.item-time,
|
||||||
|
.item-author {
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载中
|
||||||
|
.loading-more {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 32rpx 0;
|
||||||
|
color: #999;
|
||||||
|
font-size: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 没有更多
|
||||||
|
.no-more {
|
||||||
|
text-align: center;
|
||||||
|
padding: 32rpx 0;
|
||||||
|
color: #ccc;
|
||||||
|
font-size: 24rpx;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
@@ -8,4 +8,6 @@ export function setupStore(app) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export * from './modules/user'
|
export * from './modules/user'
|
||||||
|
export * from './modules/menu'
|
||||||
|
export * from './modules/large'
|
||||||
export default pinia
|
export default pinia
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -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: [],
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -2,12 +2,13 @@ import {
|
|||||||
defineStore
|
defineStore
|
||||||
} from 'pinia'
|
} from 'pinia'
|
||||||
import {
|
import {
|
||||||
ref
|
ref,
|
||||||
|
watch
|
||||||
} from 'vue'
|
} from 'vue'
|
||||||
import {
|
import {
|
||||||
reqLogin
|
reqLogin
|
||||||
} from '@/api/auth.js'
|
} from '@/api/auth.js'
|
||||||
|
import { useMenuStore, useLargeStore } from '@/store'
|
||||||
export const useUserStore = defineStore('user', () => {
|
export const useUserStore = defineStore('user', () => {
|
||||||
const token = ref("")
|
const token = ref("")
|
||||||
const userInfo = ref(null)
|
const userInfo = ref(null)
|
||||||
@@ -20,6 +21,20 @@ export const useUserStore = defineStore('user', () => {
|
|||||||
const projectList = ref([])
|
const projectList = ref([])
|
||||||
const currentProject = ref(null)
|
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 = () => {
|
const logout = () => {
|
||||||
token.value = ""
|
token.value = ""
|
||||||
userInfo.value = null
|
userInfo.value = null
|
||||||
loginForm.value = {
|
// loginForm.value = {
|
||||||
account: '',
|
// account: '',
|
||||||
password: '',
|
// password: '',
|
||||||
Telephone: '',
|
// Telephone: '',
|
||||||
}
|
// }
|
||||||
currentProject.value = null
|
currentProject.value = null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,7 +94,8 @@ export const useUserStore = defineStore('user', () => {
|
|||||||
projectList,
|
projectList,
|
||||||
currentProject,
|
currentProject,
|
||||||
setProjectList,
|
setProjectList,
|
||||||
setCurrentProject
|
setCurrentProject,
|
||||||
|
logout
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
persist: {
|
persist: {
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
]
|
||||||