0905
This commit is contained in:
@ -24,3 +24,11 @@ export const exportWord = (params) => {
|
||||
method: 'post'
|
||||
});
|
||||
};
|
||||
// 导出模版
|
||||
export const exportExcel = (params) => {
|
||||
return request({
|
||||
url: '/design/collect/exportExcel',
|
||||
method: 'post',
|
||||
params: params
|
||||
});
|
||||
};
|
||||
|
||||
@ -127,6 +127,17 @@ export const majorList = (params) => {
|
||||
params: params
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 获取人员列表
|
||||
* @param query
|
||||
*/
|
||||
export const copyUserList = (params) => {
|
||||
return request({
|
||||
url: '/design/volumeCatalog/copyUserList',
|
||||
method: 'get',
|
||||
params: params
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 获取二维码信息
|
||||
* @param query
|
||||
|
||||
@ -101,3 +101,11 @@ export function getProjectId() {
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
export const getFootNote = (data) => {
|
||||
return request({
|
||||
url: 'gps/equipmentSon/getList',
|
||||
method: 'get',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
@ -14,6 +14,18 @@ export const listCompany = (query?: CompanyQuery): AxiosPromise<CompanyVO[]> =>
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}; /**
|
||||
* 查询材料提供商
|
||||
* @param query
|
||||
* @returns {*}
|
||||
*/
|
||||
|
||||
export const supplierInputGet = (query?) => {
|
||||
return request({
|
||||
url: '/supplierInput/supplierInput/getList',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -75,3 +75,11 @@ export const inventoryList = (id: any) => {
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
//获取材料表信息
|
||||
export const getMaterialInfo = (id: any) => {
|
||||
return request({
|
||||
url: '/materials/materials/listByFormCode/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
27
src/api/materials/materialOutbound/index.ts
Normal file
27
src/api/materials/materialOutbound/index.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import request from '@/utils/request';
|
||||
|
||||
//获取出库材料得列表
|
||||
export const outboundMaterials = (query?: any) => {
|
||||
return request({
|
||||
url: '/materials/materials/listRelevancy',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
//新增出库
|
||||
export const addOutbound = (data?: any) => {
|
||||
return request({
|
||||
url: '/materials/materialsInventory',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
};
|
||||
|
||||
//获取材料列表
|
||||
export const getMaterialsList = (query?: any) => {
|
||||
return request({
|
||||
url: '/materials/materials/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
@ -33,7 +33,6 @@ export interface MaterialsUseRecordVO {
|
||||
* 备注
|
||||
*/
|
||||
remark: string;
|
||||
|
||||
}
|
||||
|
||||
export interface MaterialsUseRecordForm extends BaseEntity {
|
||||
@ -71,11 +70,9 @@ export interface MaterialsUseRecordForm extends BaseEntity {
|
||||
* 备注
|
||||
*/
|
||||
remark?: string;
|
||||
|
||||
}
|
||||
|
||||
export interface MaterialsUseRecordQuery extends PageQuery {
|
||||
|
||||
/**
|
||||
* 项目ID
|
||||
*/
|
||||
@ -101,11 +98,9 @@ export interface MaterialsUseRecordQuery extends PageQuery {
|
||||
*/
|
||||
residueNumber?: string | number;
|
||||
|
||||
/**
|
||||
* 日期范围参数
|
||||
*/
|
||||
params?: any;
|
||||
/**
|
||||
* 日期范围参数
|
||||
*/
|
||||
params?: any;
|
||||
materialsId?: string | number;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@ -203,3 +203,31 @@ export const importConstructionUserInfo = (file: string) => {
|
||||
data: { file }
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// 获取项目列表
|
||||
export const ProjectList = (query) => {
|
||||
return request({
|
||||
url: '/contractor/constructionUser/projectList',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
// 获取班组列表
|
||||
export const TeamList = (query) => {
|
||||
return request({
|
||||
url: '/contractor/constructionUser/teamList',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
// 班组分配
|
||||
export const TeamDistribution = (data) => {
|
||||
return request({
|
||||
url: '/contractor/constructionUser/addTeam',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
@ -182,6 +182,8 @@ export interface ConstructionUserVO {
|
||||
* 创建时间
|
||||
*/
|
||||
createTime: string;
|
||||
|
||||
sysUserId: string | number;
|
||||
}
|
||||
export interface skipType {
|
||||
/**
|
||||
|
||||
@ -195,3 +195,71 @@ export const changeProject = (id: string | number) => {
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 打卡规则
|
||||
* @param id
|
||||
*/
|
||||
export const attendanceRuleEdit = (data) => {
|
||||
return request({
|
||||
url: '/project/attendanceRule',
|
||||
method: 'put',
|
||||
data
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 打卡规则
|
||||
* @param id
|
||||
*/
|
||||
export const attendanceRuleAdd = (data) => {
|
||||
return request({
|
||||
url: '/project/attendanceRule',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 获取规则
|
||||
* @param id
|
||||
*/
|
||||
export const byProjectIdDetail = (id) => {
|
||||
return request({
|
||||
url: '/project/attendanceRule/byProjectId/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
// 新增项目打卡范围
|
||||
export const addAttendanceRange = (data) => {
|
||||
return request({
|
||||
url: '/project/projectPunchrange',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
};
|
||||
|
||||
// 删除项目打卡范围
|
||||
export const delAttendanceRange = (id) => {
|
||||
return request({
|
||||
url: '/project/projectPunchrange/' + id,
|
||||
method: 'delete'
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// 修改项目打卡范围
|
||||
export const updateAttendanceRange = (data) => {
|
||||
return request({
|
||||
url: '/project/projectPunchrange',
|
||||
method: 'put',
|
||||
data
|
||||
});
|
||||
};
|
||||
|
||||
// 查询项目打卡范围列表
|
||||
export const getAttendanceRangeList = (data) => {
|
||||
return request({
|
||||
url: '/project/projectPunchrange/list',
|
||||
method: 'get',
|
||||
params: data
|
||||
});
|
||||
};
|
||||
@ -72,3 +72,12 @@ export const delProjectTeam = (id: string | number | Array<string | number>) =>
|
||||
method: 'delete'
|
||||
});
|
||||
};
|
||||
|
||||
// 获取项目得打卡范围
|
||||
export const getProjectTeamClockIn = (params) => {
|
||||
return request({
|
||||
url: '/project/projectTeam/rangeList',
|
||||
method: 'get',
|
||||
params
|
||||
});
|
||||
};
|
||||
@ -55,6 +55,11 @@ export interface ProjectTeamForm extends BaseEntity {
|
||||
* 备注
|
||||
*/
|
||||
remark?: string;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
punchRangeList?: [];
|
||||
}
|
||||
|
||||
export interface ProjectTeamQuery extends PageQuery {
|
||||
|
||||
66
src/api/projectScreen/index.ts
Normal file
66
src/api/projectScreen/index.ts
Normal file
@ -0,0 +1,66 @@
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
|
||||
// 查询生项目天气
|
||||
export const getScreenWeather = (projectId: number | string) => {
|
||||
return request({
|
||||
url: '/project/big/screen/weather/' + projectId,
|
||||
method: 'get',
|
||||
});
|
||||
};
|
||||
|
||||
// 查询项目安全天数
|
||||
export const getScreenSafetyDay = (projectId: number | string) => {
|
||||
return request({
|
||||
url: '/project/big/screen/safetyDay/' + projectId,
|
||||
method: 'get',
|
||||
});
|
||||
};
|
||||
|
||||
// 查询项目公告
|
||||
export const getScreenNews = (projectId: number | string) => {
|
||||
return request({
|
||||
url: '/project/big/screen/news/' + projectId,
|
||||
method: 'get',
|
||||
});
|
||||
};
|
||||
|
||||
// 查询项目土地统计
|
||||
export const getScreenLand = (projectId: number | string) => {
|
||||
return request({
|
||||
url: '/project/big/screen/' + projectId,
|
||||
method: 'get',
|
||||
});
|
||||
};
|
||||
|
||||
// 查询项目形象进度
|
||||
export const getScreenImgProcess = (projectId: number | string) => {
|
||||
return request({
|
||||
url: '/project/big/screen/imageProgress/' + projectId,
|
||||
method: 'get',
|
||||
});
|
||||
};
|
||||
|
||||
// 查询项目人员情况
|
||||
export const getScreenPeople = (projectId: number | string) => {
|
||||
return request({
|
||||
url: '/project/big/screen/people/' + projectId,
|
||||
method: 'get',
|
||||
});
|
||||
};
|
||||
|
||||
// 查询项目AI安全巡检
|
||||
export const getScreenSafetyInspection = (projectId: number | string) => {
|
||||
return request({
|
||||
url: '/project/big/screen/safetyInspection/' + projectId,
|
||||
method: 'get',
|
||||
});
|
||||
};
|
||||
|
||||
// 查询项目概况
|
||||
export const getScreenGeneralize = (projectId: number | string) => {
|
||||
return request({
|
||||
url: '/project/big/screen/generalize/' + projectId,
|
||||
method: 'get',
|
||||
});
|
||||
};
|
||||
5
src/api/projectScreen/types.ts
Normal file
5
src/api/projectScreen/types.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export interface TableQuery extends PageQuery {
|
||||
tableName: string;
|
||||
tableComment: string;
|
||||
dataName: string;
|
||||
}
|
||||
@ -20,18 +20,20 @@ export const getMenu = (menuId: string | number): AxiosPromise<MenuVO> => {
|
||||
};
|
||||
|
||||
// 查询菜单下拉树结构
|
||||
export const treeselect = (): AxiosPromise<MenuTreeOption[]> => {
|
||||
export const treeselect = (params?: any): AxiosPromise<MenuTreeOption[]> => {
|
||||
return request({
|
||||
url: '/system/menu/treeselect',
|
||||
method: 'get'
|
||||
method: 'get',
|
||||
params
|
||||
});
|
||||
};
|
||||
|
||||
// 根据角色ID查询菜单下拉树结构
|
||||
export const roleMenuTreeselect = (roleId: string | number): AxiosPromise<RoleMenuTree> => {
|
||||
export const roleMenuTreeselect = (roleId: string | number, params?: any): AxiosPromise<RoleMenuTree> => {
|
||||
return request({
|
||||
url: '/system/menu/roleMenuTreeselect/' + roleId,
|
||||
method: 'get'
|
||||
method: 'get',
|
||||
params
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -147,10 +147,11 @@ export const authUserSelectAll = (data: any) => {
|
||||
});
|
||||
};
|
||||
// 根据角色ID查询部门树结构
|
||||
export const deptTreeSelect = (roleId: string | number): AxiosPromise<RoleDeptTree> => {
|
||||
export const deptTreeSelect = (roleId: string | number, params?) => {
|
||||
return request({
|
||||
url: '/system/role/deptTree/' + roleId,
|
||||
method: 'get'
|
||||
method: 'get',
|
||||
params
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
BIN
src/assets/projectLarge/center.png
Normal file
BIN
src/assets/projectLarge/center.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 248 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 652 B |
Binary file not shown.
|
Before Width: | Height: | Size: 665 B |
Binary file not shown.
|
Before Width: | Height: | Size: 104 KiB |
@ -26,11 +26,11 @@
|
||||
<el-tooltip effect="dark" placement="bottom">
|
||||
<ProjectSelector />
|
||||
</el-tooltip>
|
||||
<!-- <el-tooltip content="搜索" effect="dark" placement="bottom">
|
||||
<el-tooltip content="搜索" effect="dark" placement="bottom">
|
||||
<div class="right-menu-item hover-effect" @click="openSearchMenu">
|
||||
<svg-icon class-name="search-icon" icon-class="search" />
|
||||
</div>
|
||||
</el-tooltip> -->
|
||||
</el-tooltip>
|
||||
<!-- 消息 -->
|
||||
<el-tooltip :content="proxy.$t('navbar.message')" effect="dark" placement="bottom">
|
||||
<div>
|
||||
|
||||
@ -5,8 +5,6 @@ import { ProjectTeamVO } from '@/api/project/projectTeam/types';
|
||||
import useUserStore from '@/store/modules/user';
|
||||
export const getProjectTeam = async () => {
|
||||
const isPermission = useUserStore().permissions.some((item) => item == 'project:team:list');
|
||||
console.log(useUserStore().permissions);
|
||||
|
||||
if (!isPermission && useUserStore().permissions[0] != '*:*:*') return;
|
||||
|
||||
const { id } = $cache.local.getJSON('selectedProject');
|
||||
|
||||
@ -1,26 +1,27 @@
|
||||
<template>
|
||||
<div class="centerPage">
|
||||
<div class="topPage">
|
||||
<!-- 暂无 -->
|
||||
<div id="earth" style="width: 100%;height: 100%;"></div>
|
||||
</div>
|
||||
<div class="endPage">
|
||||
<Title title="AI安全巡检" :prefix="true" />
|
||||
|
||||
<div class="swiper">
|
||||
<div class="endPage" :class="{ 'slide-out-down': isHide }">
|
||||
<Title title="AI安全巡检">
|
||||
<img src="@/assets/projectLarge/robot.svg" alt="" height="20px" width="20px">
|
||||
</Title>
|
||||
<div class="swiper" v-if="inspectionList.length">
|
||||
<div class="arrow" :class="{ 'canUse': canLeft }" @click="swiperClick('left')">
|
||||
<el-icon size="16" color="skyblue">
|
||||
<el-icon size="16" :color="canLeft ? 'rgba(29, 214, 255, 1)' : 'rgba(29, 214, 255, 0.3)'">
|
||||
<ArrowLeft />
|
||||
</el-icon>
|
||||
</div>
|
||||
<div class="swiper_content" ref="swiperContent">
|
||||
<div class="swiper_item" v-for="(item, index) in swiperList" :key="index">
|
||||
<img src="@/assets/projectLarge/swiper.png" alt="" class="swiper_img">
|
||||
<div class="swiper_date">{{ item.date }}</div>
|
||||
<div class="swiper_tip">{{ item.tip }}</div>
|
||||
<div class="swiper_item" v-for="(item, i) in inspectionList" :key="i">
|
||||
<img :src="item.picture" alt="安全巡检" class="swiper_img">
|
||||
<div class="swiper_date">{{ item.createTime.slice(5) }}</div>
|
||||
<div class="swiper_tip">{{ item.label }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="arrow" :class="{ 'canUse': canRight }" @click="swiperClick('right')">
|
||||
<el-icon size="16">
|
||||
<el-icon size="16" :color="canRight ? 'rgba(29, 214, 255, 1)' : 'rgba(29, 214, 255, 0.3)'">
|
||||
<ArrowRight />
|
||||
</el-icon>
|
||||
</div>
|
||||
@ -29,31 +30,39 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue"
|
||||
<script setup>
|
||||
import { ref, onMounted, toRefs, getCurrentInstance } from "vue"
|
||||
import Title from './title.vue'
|
||||
import { ArrowLeft, ArrowRight } from '@element-plus/icons-vue'
|
||||
import { getScreenSafetyInspection } from '@/api/projectScreen'
|
||||
|
||||
const swiperList = ref([
|
||||
{ date: '03-18 15:00', tip: '未佩戴安全帽1' },
|
||||
{ date: '03-18 15:00', tip: '未佩戴安全帽2' },
|
||||
{ date: '03-18 15:00', tip: '未佩戴安全帽3' },
|
||||
{ date: '03-18 15:00', tip: '未佩戴安全帽4' },
|
||||
{ date: '03-18 15:00', tip: '未佩戴安全帽5' },
|
||||
{ date: '03-18 15:00', tip: '未佩戴安全帽6' },
|
||||
{ date: '03-18 15:00', tip: '未佩戴安全帽7' },
|
||||
{ date: '03-18 15:00', tip: '未佩戴安全帽8' },
|
||||
{ date: '03-18 15:00', tip: '未佩戴安全帽9' },
|
||||
{ date: '03-18 15:00', tip: '未佩戴安全帽10' },
|
||||
{ date: '03-18 15:00', tip: '未佩戴安全帽11' },
|
||||
{ date: '03-18 15:00', tip: '未佩戴安全帽12' },
|
||||
])
|
||||
const { proxy } = getCurrentInstance();
|
||||
const { violation_level_type } = toRefs(proxy?.useDict('violation_level_type'));
|
||||
|
||||
const swiperContent = ref<HTMLDivElement>()
|
||||
const props = defineProps({
|
||||
isHide: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
projectId: {
|
||||
type: String,
|
||||
default: ""
|
||||
}
|
||||
})
|
||||
|
||||
const inspectionList = ref([{
|
||||
id: "",
|
||||
label: "",
|
||||
picture: "",
|
||||
createTime: ""
|
||||
}])
|
||||
const swiperContent = ref()
|
||||
const swiperItemWidth = ref(100)
|
||||
const canLeft = ref(false)
|
||||
const canRight = ref(true)
|
||||
|
||||
const swiperClick = (direction: 'left' | 'right') => {
|
||||
const swiperClick = (direction) => {
|
||||
if (!swiperContent.value) return
|
||||
|
||||
if (direction === 'right') {
|
||||
if (swiperContent.value.scrollLeft >= swiperContent.value.scrollWidth - swiperContent.value.clientWidth) {
|
||||
@ -70,10 +79,79 @@ const swiperClick = (direction: 'left' | 'right') => {
|
||||
}
|
||||
swiperContent.value.scrollLeft -= swiperItemWidth.value
|
||||
}
|
||||
|
||||
// 更新箭头状态
|
||||
canLeft.value = swiperContent.value.scrollLeft > 0
|
||||
canRight.value = swiperContent.value.scrollLeft < swiperContent.value.scrollWidth - swiperContent.value.clientWidth
|
||||
}
|
||||
|
||||
const getInspectionList = async () => {
|
||||
const res = await getScreenSafetyInspection(props.projectId)
|
||||
const { code, data } = res
|
||||
if (code === 200) {
|
||||
data.map(item => {
|
||||
item.label = violation_level_type.value.find((i) => i.value === item.violationType)?.label
|
||||
})
|
||||
inspectionList.value = data
|
||||
}
|
||||
}
|
||||
// 创建地球
|
||||
const createEarth = () => {
|
||||
window.YJ.on({
|
||||
ws: true,
|
||||
// host: getIP(), //资源所在服务器地址
|
||||
// username: this.loginForm.username, //用户名 可以不登录(不填写用户名),不登录时无法加载服务端的数据
|
||||
// password: md5pass, //密码 生成方式:md5(用户名_密码)
|
||||
}).then((res) => {
|
||||
let earth = new YJ.YJEarth("earth");
|
||||
window.Earth1 = earth;
|
||||
YJ.Global.openRightClick(window.Earth1);
|
||||
YJ.Global.openLeftClick(window.Earth1);
|
||||
let view = {
|
||||
"position": {
|
||||
"lng": 102.03643298211526,
|
||||
"lat": 34.393586474501,
|
||||
"alt": 11298179.51993155
|
||||
},
|
||||
"orientation": {
|
||||
"heading": 360,
|
||||
"pitch": -89.94481747201486,
|
||||
"roll": 0
|
||||
}
|
||||
}
|
||||
loadBaseMap(earth.viewer)
|
||||
YJ.Global.CesiumContainer(window.Earth1, {
|
||||
compass: false, //罗盘
|
||||
});
|
||||
// YJ.Global.flyTo(earth, view);
|
||||
// YJ.Global.setDefaultView(earth.viewer, view)
|
||||
})
|
||||
}
|
||||
// 加载底图
|
||||
const loadBaseMap = (viewer) => {
|
||||
// 创建瓦片提供器
|
||||
const imageryProvider = new Cesium.UrlTemplateImageryProvider({
|
||||
url: 'https://webst01.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}',
|
||||
// 可选:设置瓦片的格式
|
||||
fileExtension: 'png',
|
||||
// 可选:设置瓦片的范围和级别
|
||||
minimumLevel: 0,
|
||||
maximumLevel: 18,
|
||||
// 可选:设置瓦片的投影(默认为Web Mercator)
|
||||
projection: Cesium.WebMercatorProjection,
|
||||
// 可选:如果瓦片服务需要跨域请求,设置请求头部
|
||||
credit: new Cesium.Credit('卫星图数据来源')
|
||||
});
|
||||
|
||||
// 添加图层到视图
|
||||
const layer = viewer.imageryLayers.addImageryProvider(imageryProvider);
|
||||
}
|
||||
onMounted(() => {
|
||||
swiperItemWidth.value = swiperContent.value.children[0].clientWidth + 20
|
||||
getInspectionList()
|
||||
createEarth()
|
||||
if (swiperContent.value && swiperContent.value.children.length > 0) {
|
||||
swiperItemWidth.value = swiperContent.value.children[0].clientWidth + 20
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
@ -82,24 +160,41 @@ onMounted(() => {
|
||||
.centerPage {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 50vw;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.topPage,
|
||||
.endPage {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
padding: 15px 0;
|
||||
border: 1px solid rgba(29, 214, 255, 0.1);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.topPage,
|
||||
.endPage {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
padding: 15px 0;
|
||||
border: 1px solid rgba(230, 247, 255, 0.1);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.topPage {
|
||||
flex: 1;
|
||||
margin-bottom: 23px;
|
||||
}
|
||||
.topPage {
|
||||
flex: 1;
|
||||
margin-bottom: 23px;
|
||||
transition: flex 0.5s ease;
|
||||
}
|
||||
|
||||
.endPage {
|
||||
max-height: 300px;
|
||||
opacity: 1;
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
|
||||
/* 向下滑出动画 */
|
||||
.slide-out-down {
|
||||
transform: translateY(100%);
|
||||
opacity: 0;
|
||||
max-height: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.swiper {
|
||||
@ -161,14 +256,20 @@ onMounted(() => {
|
||||
.arrow {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid skyblue;
|
||||
border: 1px solid rgba(29, 214, 255, 0.3);
|
||||
color: skyblue;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:canUse {
|
||||
color: #000 !important;
|
||||
&.canUse {
|
||||
border: 1px solid rgba(29, 214, 255, 1);
|
||||
}
|
||||
|
||||
&:hover:not(.canUse) {
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
</div>
|
||||
<div style="font-size: 12px; padding-left: 10px">安全生产天数:</div>
|
||||
<div class="header_left_text">
|
||||
1,235
|
||||
{{ safetyDay }}
|
||||
<span style="font-size: 12px">天</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -14,16 +14,18 @@
|
||||
<div>XXX智慧工地管理平台</div>
|
||||
<div>XXX Smart Construction Stic Management Dashboard</div>
|
||||
</div>
|
||||
<div class="right">
|
||||
<div class="header_right">
|
||||
<div class="top-bar">
|
||||
<!-- 左侧:天气图标 + 日期文字 -->
|
||||
<div class="left-section">
|
||||
<img src="@/assets/large/weather.png" alt="天气图标" />
|
||||
|
||||
<span>
|
||||
<span>多云 9°/18°</span>
|
||||
<span style="padding-left: 20px"> {{ week[date.week] }} ({{ date.ymd }})</span>
|
||||
</span>
|
||||
<div class="weather-list" @mouseenter="requestPause" @mouseleave="resumeScroll">
|
||||
<div v-for="(item, i) in weatherList" :key="i" class="weather-item"
|
||||
:style="{ transform: `translateY(-${offsetY}px)`, transition: transition }">
|
||||
<img :src="`../../../src/assets/images/${item.icon}.png`" alt="" />
|
||||
<div>{{ item.weather }}{{ item.tempMin }}°/{{ item.tempMax }}°</div>
|
||||
<div>{{ item.week }}({{ item.date }})</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 分割线 -->
|
||||
<div class="divider">
|
||||
@ -35,48 +37,155 @@
|
||||
<img src="@/assets/large/setting.png" alt="设置图标" />
|
||||
<span>管理系统</span>
|
||||
</div>
|
||||
<!-- 分割线 -->
|
||||
<div class="divider">
|
||||
<div class="top-block"></div>
|
||||
<div class="bottom-block"></div>
|
||||
</div>
|
||||
<!-- -->
|
||||
<div class="change" @click="emit('changePage')">
|
||||
<el-icon size="20" v-if="!isFull">
|
||||
<Expand />
|
||||
</el-icon>
|
||||
<el-icon size="20" v-else>
|
||||
<Fold />
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const week = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
|
||||
const date = ref({
|
||||
ymd: '',
|
||||
hms: '',
|
||||
week: 0
|
||||
import { ref, onMounted, onUnmounted } from 'vue';
|
||||
import { getScreenSafetyDay, getScreenWeather } from '@/api/projectScreen';
|
||||
|
||||
interface Weather {
|
||||
week: string;
|
||||
date: string;
|
||||
icon: string;
|
||||
weather: string;
|
||||
tempMax: string;
|
||||
tempMin: string;
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
projectId: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
isFull: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['changePage'])
|
||||
|
||||
const safetyDay = ref<number>(0);
|
||||
const weatherList = ref<Weather[]>([])
|
||||
const timer = ref<number | null>(0)
|
||||
const offsetY = ref<number>(0)
|
||||
const curIndex = ref(0)
|
||||
const transition = ref('transform 0.5s ease');
|
||||
const pendingPause = ref(false);
|
||||
|
||||
/**
|
||||
* 判断当前时间是白天/夜晚
|
||||
*/
|
||||
function judgeDayOrNight(sunRise: string, sunSet: string) {
|
||||
// 将 "HH:MM" 格式转为分钟数(便于计算)
|
||||
const timeToMinutes = (timeStr: any) => {
|
||||
const [hours, minutes] = timeStr.split(':').map(Number);
|
||||
return isNaN(hours) || isNaN(minutes) ? 0 : hours * 60 + minutes;
|
||||
};
|
||||
// 转换日出、日落时间为分钟数
|
||||
const sunRiseMinutes = timeToMinutes(sunRise);
|
||||
const sunSetMinutes = timeToMinutes(sunSet);
|
||||
// 获取当前时间并转为分钟数
|
||||
const now = new Date();
|
||||
const currentMinutes = now.getHours() * 60 + now.getMinutes();
|
||||
// true 白天 false 夜晚
|
||||
return currentMinutes >= sunRiseMinutes && currentMinutes <= sunSetMinutes
|
||||
? true
|
||||
: false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置天气周期滑动
|
||||
*/
|
||||
const setWeatherScroll = () => {
|
||||
curIndex.value += 1
|
||||
transition.value = 'transform 0.3s ease';
|
||||
offsetY.value = curIndex.value * 60
|
||||
|
||||
if (curIndex.value === weatherList.value.length - 1) {
|
||||
setTimeout(() => {
|
||||
transition.value = 'none';
|
||||
curIndex.value = 0;
|
||||
offsetY.value = 0;
|
||||
}, 350);
|
||||
}
|
||||
}
|
||||
|
||||
function startScroll() {
|
||||
if (timer.value) clearInterval(timer.value);
|
||||
timer.value = window.setInterval(setWeatherScroll, 5000);
|
||||
}
|
||||
|
||||
function requestPause() {
|
||||
if (timer.value) {
|
||||
clearInterval(timer.value)
|
||||
timer.value = null
|
||||
}
|
||||
pendingPause.value = true;
|
||||
}
|
||||
|
||||
function resumeScroll() {
|
||||
console.log('resumeScroll')
|
||||
pendingPause.value = false;
|
||||
startScroll();
|
||||
}
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
/**
|
||||
* 获取安全生产天数
|
||||
*/
|
||||
getScreenSafetyDay(props.projectId).then(res => {
|
||||
const { data, code } = res
|
||||
if (code === 200) {
|
||||
safetyDay.value = data.safetyDay;
|
||||
}
|
||||
})
|
||||
/**
|
||||
* 获取近三天天气
|
||||
*/
|
||||
getScreenWeather(props.projectId).then(res => {
|
||||
const { data, code } = res
|
||||
if (code === 200) {
|
||||
data.forEach(item => {
|
||||
if (judgeDayOrNight(item.sunRise, item.sunSet)) {
|
||||
item.weather = item.dayStatus
|
||||
item.icon = item.dayIcon
|
||||
} else {
|
||||
item.weather = item.nightStatus
|
||||
item.icon = item.nightIcon
|
||||
}
|
||||
})
|
||||
weatherList.value = data
|
||||
// 多添加第一项 实现无缝衔接
|
||||
weatherList.value = [...weatherList.value, weatherList.value[0]]
|
||||
startScroll()
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
const setTime = () => {
|
||||
let date1 = new Date();
|
||||
let year: any = date1.getFullYear();
|
||||
let month: any = date1.getMonth() + 1;
|
||||
let day: any = date1.getDate();
|
||||
let hours: any = date1.getHours();
|
||||
if (hours < 10) {
|
||||
hours = '0' + hours;
|
||||
}
|
||||
let minutes: any = date1.getMinutes();
|
||||
if (minutes < 10) {
|
||||
minutes = '0' + minutes;
|
||||
}
|
||||
let seconds: any = date1.getSeconds();
|
||||
if (seconds < 10) {
|
||||
seconds = '0' + seconds;
|
||||
}
|
||||
date.value.ymd = year + '-' + month + '-' + day;
|
||||
date.value.hms = hours + ':' + minutes + ':' + seconds;
|
||||
date.value.week = date1.getDay();
|
||||
};
|
||||
|
||||
// 添加定时器,每秒更新一次时间
|
||||
const timer = setInterval(setTime, 1000);
|
||||
|
||||
// 组件卸载时清除定时器
|
||||
onUnmounted(() => {
|
||||
clearInterval(timer);
|
||||
});
|
||||
if (timer.value) {
|
||||
clearInterval(timer.value)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@ -107,6 +216,12 @@ onUnmounted(() => {
|
||||
}
|
||||
}
|
||||
|
||||
.header_right {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.title {
|
||||
color: #fff;
|
||||
font-family: 'AlimamaShuHeiTi', sans-serif;
|
||||
@ -124,12 +239,6 @@ onUnmounted(() => {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.right {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
/* 顶部栏容器:Flex 水平布局 + 垂直居中 */
|
||||
.top-bar {
|
||||
width: 100%;
|
||||
@ -137,7 +246,6 @@ onUnmounted(() => {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
// background-color: #1e2128;
|
||||
color: #fff;
|
||||
padding: 8px 16px;
|
||||
font-size: 14px;
|
||||
@ -145,24 +253,37 @@ onUnmounted(() => {
|
||||
|
||||
/* 左侧区域(天气 + 日期):自身也用 Flex 水平排列,确保元素在一行 */
|
||||
.left-section {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
// margin-right: auto; /* 让右侧元素(管理系统)居右 */
|
||||
}
|
||||
|
||||
.left-section img {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin-right: 8px;
|
||||
/* 图标与文字间距 */
|
||||
.weather-list {
|
||||
height: 60px;
|
||||
overflow: hidden;
|
||||
|
||||
.weather-item {
|
||||
height: 60px;
|
||||
line-height: 60px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&>div:last-child {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 分割线(视觉分隔,可根据需求调整样式) */
|
||||
.divider {
|
||||
display: grid;
|
||||
grid-template-rows: 1fr 1fr;
|
||||
height: 100%;
|
||||
/* 根据需要调整高度 */
|
||||
gap: 2px;
|
||||
padding: 14px 10px;
|
||||
}
|
||||
|
||||
@ -195,4 +316,11 @@ onUnmounted(() => {
|
||||
margin-right: 6px;
|
||||
/* 图标与文字间距 */
|
||||
}
|
||||
|
||||
.change {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
margin-right: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -2,30 +2,53 @@
|
||||
<div class="leftPage">
|
||||
<div class="topPage">
|
||||
<Title title="项目公告" />
|
||||
|
||||
<div class="content">
|
||||
<div class="content_item" v-for="item in 6" :key="item">
|
||||
<div class="round">
|
||||
<div class="sub_round"></div>
|
||||
<div class="content_item" v-for="item in news" :key="item.id">
|
||||
<img src="@/assets/projectLarge/round.svg" alt="">
|
||||
<div class="ellipsis">
|
||||
{{ item.title }}
|
||||
<span @click="showNewsDetail(item)" style="color: rgba(138, 149, 165, 1);">{{ item.id === newId ? '关闭' :
|
||||
'查看' }}</span>
|
||||
</div>
|
||||
<div class="ellipsis">2025年6月23日 重庆市两江新区广场前期准备与审批完毕区广场前期准备与审批完毕前期准备与审批完毕区广场前期准备与审批完毕</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detailBox" :class="{ 'show': newId }">
|
||||
<!-- <div class="detail_title">{{ newDetail.title }}</div> -->
|
||||
<div class="detail_content" v-html="newDetail.content"></div>
|
||||
</div>
|
||||
|
||||
<div class="endPage">
|
||||
<Title title="人员情况" />
|
||||
|
||||
<div class="map">
|
||||
<img src="@/assets/projectLarge/map.svg" alt="">
|
||||
<!-- <div ref="mapChartRef"></div> -->
|
||||
</div>
|
||||
|
||||
<div class="attendance_tag">
|
||||
<div class="tag_item" v-for="(item, index) in tagList" :key="index">
|
||||
<div class="tag_item">
|
||||
<img src="@/assets/projectLarge/people.svg" alt="">
|
||||
<div class="tag_title">{{ item.title }}</div>
|
||||
<div class="tag_title">出勤人</div>
|
||||
<div class="tag_info">
|
||||
{{ item.number }}
|
||||
<span style="font-size: 14px;">{{ index === 2 ? '%' : '人' }}</span>
|
||||
{{ attendanceCount }}
|
||||
<span style="font-size: 14px;">人</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tag_item">
|
||||
<img src="@/assets/projectLarge/people.svg" alt="">
|
||||
<div class="tag_title">在岗人</div>
|
||||
<div class="tag_info">
|
||||
{{ peopleCount }}
|
||||
<span style="font-size: 14px;">人</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tag_item">
|
||||
<img src="@/assets/projectLarge/people.svg" alt="">
|
||||
<div class="tag_title">出勤率</div>
|
||||
<div class="tag_info">
|
||||
{{ attendanceRate }}
|
||||
<span style="font-size: 14px;">%</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -37,11 +60,12 @@
|
||||
<div class="attendance_item_title">出勤率</div>
|
||||
<div class="attendance_item_title">出勤时间</div>
|
||||
</div>
|
||||
<div v-for="item in list" :key="item.title" class="attendance_item">
|
||||
<div class="attendance_item_title">{{ item.title }}</div>
|
||||
<div class="attendance_item_number">{{ item.number }} <span class="subfont">人/{{ item.number }}</span></div>
|
||||
<div v-for="item in teamAttendanceList" :key="item.id" class="attendance_item">
|
||||
<div class="attendance_item_title">{{ item.teamName }}</div>
|
||||
<div class="attendance_item_number">{{ item.attendanceNumber }} <span class="subfont">人/{{ item.allNumber
|
||||
}}</span></div>
|
||||
<div class="attendance_item_rate">{{ item.attendanceRate }} %</div>
|
||||
<div class="attendance_item_date subfont">{{ item.date }}</div>
|
||||
<div class="attendance_item_date subfont">{{ item.attendanceTime }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -51,29 +75,101 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue"
|
||||
import Title from './title.vue'
|
||||
import { getScreenNews, getScreenPeople } from '@/api/projectScreen';
|
||||
import { mapOption } from './optionList'
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
const list = ref([
|
||||
{ title: '智慧系统运维', number: 30, attendanceRate: 100, date: '2025-08-05 08:10' },
|
||||
{ title: '智慧系统运维', number: 30, attendanceRate: 100, date: '2025-08-05 08:10' },
|
||||
{ title: '智慧系统运维', number: 30, attendanceRate: 100, date: '2025-08-05 08:10' },
|
||||
{ title: '智慧系统运维', number: 30, attendanceRate: 100, date: '2025-08-05 08:10' },
|
||||
{ title: '智慧系统运维', number: 30, attendanceRate: 100, date: '2025-08-05 08:10' },
|
||||
{ title: '智慧系统运维', number: 30, attendanceRate: 100, date: '2025-08-05 08:10' },
|
||||
const props = defineProps({
|
||||
projectId: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
|
||||
let mapChart = null
|
||||
const mapChartRef = ref<HTMLDivElement | null>(null);
|
||||
const news = ref([])
|
||||
const newDetail = ref({
|
||||
title: '',
|
||||
content: ''
|
||||
})
|
||||
const newId = ref('')
|
||||
const attendanceCount = ref(0)
|
||||
const attendanceRate = ref(0)
|
||||
const peopleCount = ref(0)
|
||||
const teamAttendanceList = ref([
|
||||
{ id: "", teamName: "", attendanceNumber: 0, allNumber: 0, attendanceRate: 0, attendanceTime: "" },
|
||||
])
|
||||
|
||||
const tagList = ref([
|
||||
{ title: '出勤人数', number: 259 },
|
||||
{ title: '在岗人数', number: 100 },
|
||||
{ title: '出勤率', number: 100 },
|
||||
])
|
||||
/**
|
||||
* 显示新闻详情
|
||||
*/
|
||||
const showNewsDetail = (item: any) => {
|
||||
if (newId.value === item.id) {
|
||||
newId.value = ''
|
||||
return
|
||||
}
|
||||
newDetail.value = item
|
||||
newId.value = item.id
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取项目人员出勤数据
|
||||
*/
|
||||
const getPeopleData = async () => {
|
||||
const res = await getScreenPeople(props.projectId);
|
||||
const { data, code } = res
|
||||
if (code === 200) {
|
||||
attendanceCount.value = data.attendanceCount
|
||||
attendanceRate.value = data.attendanceRate
|
||||
peopleCount.value = data.peopleCount
|
||||
teamAttendanceList.value = data.teamAttendanceList
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取项目新闻数据
|
||||
*/
|
||||
const getNewsData = async () => {
|
||||
const res = await getScreenNews(props.projectId);
|
||||
const { data, code } = res
|
||||
if (code === 200) {
|
||||
news.value = data
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化地图
|
||||
*/
|
||||
const initMapChart = () => {
|
||||
if (!mapChartRef.value) {
|
||||
return;
|
||||
}
|
||||
mapChart = echarts.init(mapChartRef.value);
|
||||
mapChart.setOption(mapOption);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// nextTick(() => {
|
||||
// initMapChart();
|
||||
// });
|
||||
getPeopleData()
|
||||
getNewsData()
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
// if (mapChart) {
|
||||
// mapChart.dispose();
|
||||
// mapChart = null;
|
||||
// }
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.leftPage {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: calc(25vw - 30px);
|
||||
margin: 0 15px;
|
||||
height: 100%;
|
||||
|
||||
.topPage,
|
||||
@ -115,11 +211,11 @@ const tagList = ref([
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
// position: relative;
|
||||
margin-bottom: 20px;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
color: rgba(230, 247, 255, 1);
|
||||
cursor: pointer;
|
||||
|
||||
.ellipsis {
|
||||
display: -webkit-box;
|
||||
@ -134,21 +230,8 @@ const tagList = ref([
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.round {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
img {
|
||||
margin-top: 3px;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
background: rgba(29, 214, 255, 0.3);
|
||||
|
||||
.sub_round {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: #1DD6FF;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -165,12 +248,13 @@ const tagList = ref([
|
||||
margin-top: 15px;
|
||||
|
||||
.tag_item {
|
||||
width: 28%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
border: 1px dashed rgba(29, 214, 255, 0.3);
|
||||
padding: 10px 25px;
|
||||
padding: 10px;
|
||||
|
||||
.tag_info {
|
||||
font-size: 20px;
|
||||
@ -201,4 +285,45 @@ const tagList = ref([
|
||||
.subfont {
|
||||
color: rgba(138, 149, 165, 1);
|
||||
}
|
||||
|
||||
.detailBox {
|
||||
position: absolute;
|
||||
left: 20vw;
|
||||
top: 0;
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
max-height: 500px;
|
||||
overflow-y: auto;
|
||||
padding: 0 15px;
|
||||
box-sizing: border-box;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 4px;
|
||||
transition: all 0.3s ease;
|
||||
opacity: 0;
|
||||
z-index: -1;
|
||||
|
||||
&.show {
|
||||
left: 25vw;
|
||||
opacity: 1;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.detailBox::before {
|
||||
content: '';
|
||||
/* 绝对定位相对于父元素 */
|
||||
position: absolute;
|
||||
/* 定位到左侧中间位置 */
|
||||
left: -10px;
|
||||
top: 50%;
|
||||
/* 垂直居中 */
|
||||
transform: translateY(-50%);
|
||||
/* 利用边框创建三角形 */
|
||||
border-width: 10px 10px 10px 0;
|
||||
border-style: solid;
|
||||
/* 三角形颜色与背景匹配,左侧边框透明 */
|
||||
border-color: transparent rgba(255, 255, 255, 0.2) transparent transparent;
|
||||
/* 确保三角形在内容下方 */
|
||||
z-index: -1;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -0,0 +1,153 @@
|
||||
export let pieOption = {
|
||||
// 定义中心文字
|
||||
graphic: [
|
||||
{
|
||||
type: 'text',
|
||||
left: 'center',
|
||||
top: '40%',
|
||||
style: {
|
||||
// 需要从接口替换
|
||||
text: '70%',
|
||||
fontSize: 24,
|
||||
fontWeight: 'bold',
|
||||
fill: '#fff'
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
left: 'center',
|
||||
top: '50%',
|
||||
style: {
|
||||
text: '总进度',
|
||||
fontSize: 14,
|
||||
fill: '#fff'
|
||||
}
|
||||
},
|
||||
],
|
||||
legend: {
|
||||
show: true,
|
||||
type: 'plain',
|
||||
bottom: 20,
|
||||
itemWidth: 12,
|
||||
itemHeight: 12,
|
||||
textStyle: {
|
||||
color: '#fff'
|
||||
}
|
||||
},
|
||||
series: {
|
||||
type: 'pie',
|
||||
data: [],
|
||||
radius: [50, 80],
|
||||
center: ['50%', '45%'],
|
||||
itemStyle: {
|
||||
borderColor: '#fff',
|
||||
borderWidth: 1
|
||||
},
|
||||
label: {
|
||||
alignTo: 'edge',
|
||||
formatter: '{name|{b}}\n{percent|{c} %}',
|
||||
minMargin: 10,
|
||||
edgeDistance: 20,
|
||||
lineHeight: 15,
|
||||
rich: {
|
||||
name: {
|
||||
fontSize: 12,
|
||||
color: '#fff'
|
||||
},
|
||||
percent: {
|
||||
fontSize: 12,
|
||||
color: '#fff'
|
||||
}
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
top: 'bottom'
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
export let barOption = {
|
||||
legend: {
|
||||
icon: 'rect',
|
||||
itemWidth: 12,
|
||||
itemHeight: 12,
|
||||
// 调整文字与图标间距
|
||||
data: ['计划流转面积', '已流转面积'],
|
||||
top: 0,
|
||||
right: 20,
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
}
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: ['地块1', '地块2', '地块3', '地块4', '地块5', '地块6'],
|
||||
axisLabel: {
|
||||
color: '#fff'
|
||||
},
|
||||
axisLine: {
|
||||
show: false
|
||||
},
|
||||
splitLine: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
name: '单位:m²',
|
||||
type: 'value',
|
||||
axisLabel: {
|
||||
formatter: '{value}'
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
bottom: 0, // 距离容器底部的距离
|
||||
containLabel: true // 确保坐标轴标签不被裁剪
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '计划流转面积',
|
||||
type: 'bar',
|
||||
data: [],
|
||||
barWidth: '20%',
|
||||
itemStyle: {
|
||||
color: 'rgb(29, 253, 253)'
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '已流转面积',
|
||||
type: 'bar',
|
||||
data: [],
|
||||
barWidth: '20%',
|
||||
itemStyle: {
|
||||
color: 'rgb(25, 181, 251)'
|
||||
},
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export let mapOption = {
|
||||
geo: {
|
||||
map: 'ch',
|
||||
roam: true,
|
||||
aspectScale: Math.cos((47 * Math.PI) / 180),
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'graph',
|
||||
coordinateSystem: 'geo',
|
||||
data: [
|
||||
{ name: 'a', value: [7.667821250000001, 46.791734269956265] },
|
||||
{ name: 'b', value: [7.404848750000001, 46.516308805996054] },
|
||||
{ name: 'c', value: [7.376673125000001, 46.24728858538375] },
|
||||
{ name: 'd', value: [8.015320625000001, 46.39460918238572] },
|
||||
{ name: 'e', value: [8.616400625, 46.7020608630855] },
|
||||
{ name: 'f', value: [8.869981250000002, 46.37539345234199] },
|
||||
{ name: 'g', value: [9.546196250000001, 46.58676648282309] },
|
||||
{ name: 'h', value: [9.311399375, 47.182454114178896] },
|
||||
{ name: 'i', value: [9.085994375000002, 47.55395822835779] },
|
||||
{ name: 'j', value: [8.653968125000002, 47.47709530818285] },
|
||||
{ name: 'k', value: [8.203158125000002, 47.44506909144329] }
|
||||
],
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
@ -2,21 +2,14 @@
|
||||
<div class="leftPage">
|
||||
<div class="topPage">
|
||||
<Title title="项目概况" />
|
||||
|
||||
<div class="content">
|
||||
<div class="content_item">项目名称:智慧生态工地社区开发项目</div>
|
||||
<div class="content_item">项目位置:贵州省贵阳市乌当区(具体地块编号:01-123-11)</div>
|
||||
<div class="content_item">占地面积:约10000亩</div>
|
||||
<div class="content_item"> 土地性质:城镇住宅用地(兼容商业用地,容积率≤2.5)</div>
|
||||
</div>
|
||||
<div class="content" v-html="generalize"></div>
|
||||
</div>
|
||||
<div class="endPage">
|
||||
<!-- 饼图容器 -->
|
||||
<Title title="形象进度" />
|
||||
|
||||
<div ref="pieChartRef" class="echart" />
|
||||
<!-- 折线图容器 -->
|
||||
<div ref="lineChartRef" class="echart" />
|
||||
<div class="chart_container">
|
||||
<div ref="pieChartRef" class="echart" />
|
||||
<div ref="lineChartRef" class="echart" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -25,149 +18,45 @@
|
||||
import { ref, onMounted, onUnmounted, nextTick } from "vue"
|
||||
import Title from './title.vue'
|
||||
import * as echarts from 'echarts';
|
||||
import { pieOption, barOption } from './optionList';
|
||||
import { getScreenLand, getScreenImgProcess, getScreenGeneralize } from '@/api/projectScreen';
|
||||
|
||||
const props = defineProps({
|
||||
projectId: {
|
||||
type: String,
|
||||
default: 0
|
||||
}
|
||||
})
|
||||
|
||||
const generalize = ref()
|
||||
// 饼图相关
|
||||
const pieChartRef = ref<HTMLDivElement | null>(null);
|
||||
let pieChart: any = null;
|
||||
|
||||
const totalPercent = ref(0)
|
||||
// 折线图相关
|
||||
const lineChartRef = ref<HTMLDivElement | null>(null);
|
||||
let lineChart: any = null;
|
||||
|
||||
// 土地数据 折线图
|
||||
const designAreaData = ref([])
|
||||
const transferAreaData = ref([])
|
||||
// 饼图数据
|
||||
const pieData = [
|
||||
{ name: '桩点浇筑', value: 13 },
|
||||
{ name: '水泥灌注', value: 7 },
|
||||
{ name: '箱变安装', value: 40 },
|
||||
{ name: '支架安装', value: 20 },
|
||||
{ name: '组件安装', value: 20 },
|
||||
{ label: 'areaPercentage', name: '厂区', value: 0 },
|
||||
{ label: 'roadPercentage', name: '道路', value: 0 },
|
||||
{ label: 'collectorLinePercentage', name: '集电线路', value: 0 },
|
||||
{ label: 'exportLinePercentage', name: '送出线路', value: 0 },
|
||||
{ label: 'substationPercentage', name: '升压站', value: 0 },
|
||||
{ label: 'boxTransformerPercentage', name: '箱变', value: 0 },
|
||||
]
|
||||
|
||||
// 折线图数据
|
||||
const barData = {
|
||||
xAxis: ['地块1', '地块2', '地块3', '地块4', '地块5', '地块6'],
|
||||
series: [
|
||||
{
|
||||
name: '计划流转面积',
|
||||
data: [70, 25, 45, 115, 70, 85]
|
||||
},
|
||||
{
|
||||
name: '已流转面积',
|
||||
data: [105, 30, 150, 65, 80, 200]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// 饼图配置
|
||||
const pieOption = {
|
||||
series: {
|
||||
type: 'pie',
|
||||
data: pieData,
|
||||
radius: [50, 80],
|
||||
itemStyle: {
|
||||
borderColor: '#fff',
|
||||
borderWidth: 1
|
||||
},
|
||||
label: {
|
||||
alignTo: 'edge',
|
||||
formatter: '{name|{b}}\n{percent|{c} %}',
|
||||
minMargin: 10,
|
||||
edgeDistance: 20,
|
||||
lineHeight: 15,
|
||||
rich: {
|
||||
name: {
|
||||
fontSize: 12,
|
||||
color: '#fff'
|
||||
},
|
||||
percent: {
|
||||
fontSize: 12,
|
||||
color: '#fff'
|
||||
}
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
top: 'bottom'
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
// 柱状图配置
|
||||
const barOption = {
|
||||
legend: {
|
||||
data: ['计划流转面积', '已流转面积'],
|
||||
top: 0
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: barData.xAxis
|
||||
},
|
||||
yAxis: {
|
||||
name: '单位:m²',
|
||||
type: 'value',
|
||||
axisLabel: {
|
||||
formatter: '{value}'
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'bar',
|
||||
data: [], // 空数据,仅用于承载markArea
|
||||
markArea: {
|
||||
silent: true, // 背景不响应交互
|
||||
data: (() => {
|
||||
const groupCount = 3; // 共3组(6个月 ÷ 2)
|
||||
const groupWidth = 1.8; // 每组背景宽度(覆盖2根柱子)
|
||||
const bgData = [];
|
||||
|
||||
for (let i = 0; i < groupCount; i++) {
|
||||
const startX = i * 2 - 0.9; // 每组起始位置
|
||||
const endX = startX + groupWidth; // 每组结束位置
|
||||
bgData.push([
|
||||
{ xAxis: startX, yAxis: 0 },
|
||||
{ xAxis: endX, yAxis: 100 }
|
||||
]);
|
||||
}
|
||||
return bgData;
|
||||
})(),
|
||||
itemStyle: {
|
||||
color: 'rgba(255, 255, 255, 0.05)',
|
||||
borderRadius: 4
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '计划流转面积',
|
||||
type: 'bar',
|
||||
data: barData.series[0].data,
|
||||
barWidth: 15, // 柱形宽度
|
||||
itemStyle: {
|
||||
color: 'rgb(29, 253, 253)'
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '已流转面积',
|
||||
type: 'bar',
|
||||
data: barData.series[1].data,
|
||||
barWidth: 15,
|
||||
itemStyle: {
|
||||
color: '#rgb(25, 181, 251)'
|
||||
},
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
// 初始化饼图
|
||||
const initPieChart = () => {
|
||||
if (!pieChartRef.value) {
|
||||
console.error('未找到饼图容器元素');
|
||||
return;
|
||||
}
|
||||
pieOption.series.data = pieData
|
||||
pieOption.graphic[0].style.text = totalPercent.value + '%'
|
||||
pieChart = echarts.init(pieChartRef.value, null, {
|
||||
renderer: 'canvas',
|
||||
useDirtyRect: false
|
||||
@ -181,6 +70,8 @@ const initLineChart = () => {
|
||||
console.error('未找到折线图容器元素');
|
||||
return;
|
||||
}
|
||||
barOption.series[0].data = designAreaData.value
|
||||
barOption.series[1].data = transferAreaData.value
|
||||
lineChart = echarts.init(lineChartRef.value, null, {
|
||||
renderer: 'canvas',
|
||||
useDirtyRect: false
|
||||
@ -194,11 +85,52 @@ const handleResize = () => {
|
||||
if (lineChart) lineChart.resize();
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取项目土地统计数据
|
||||
*/
|
||||
const getScreenLandData = async () => {
|
||||
const res = await getScreenLand(props.projectId);
|
||||
const { data, code } = res
|
||||
if (code === 200) {
|
||||
designAreaData.value = data.map((item: any) => Number(item.designArea))
|
||||
transferAreaData.value = data.map((item: any) => Number(item.transferArea))
|
||||
initLineChart();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取项目形象进度数据
|
||||
*/
|
||||
const getScreenImgProcessData = async () => {
|
||||
const res = await getScreenImgProcess(props.projectId);
|
||||
const { data, code } = res
|
||||
if (code === 200) {
|
||||
totalPercent.value = data.totalPercentage
|
||||
pieData.forEach((item: any) => {
|
||||
item.value = data[item.label]
|
||||
})
|
||||
initPieChart()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取项目概况数据
|
||||
*/
|
||||
const getScreenGeneralizeData = async () => {
|
||||
const res = await getScreenGeneralize(props.projectId);
|
||||
const { data, code } = res
|
||||
if (code === 200) {
|
||||
generalize.value = data
|
||||
}
|
||||
}
|
||||
|
||||
// 组件挂载时初始化图表
|
||||
onMounted(() => {
|
||||
getScreenLandData()
|
||||
getScreenImgProcessData()
|
||||
getScreenGeneralizeData()
|
||||
nextTick(() => {
|
||||
initPieChart();
|
||||
initLineChart();
|
||||
window.addEventListener('resize', handleResize);
|
||||
});
|
||||
});
|
||||
@ -221,8 +153,6 @@ onUnmounted(() => {
|
||||
.leftPage {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: calc(25vw - 30px);
|
||||
margin: 0 15px;
|
||||
height: 100%;
|
||||
|
||||
.topPage,
|
||||
@ -240,15 +170,38 @@ onUnmounted(() => {
|
||||
flex: 1;
|
||||
margin-top: 23px;
|
||||
|
||||
.echart {
|
||||
.chart_container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.echart {
|
||||
height: 48%;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
margin: 10px 35px;
|
||||
height: 100px;
|
||||
margin: 0 15px;
|
||||
padding: 0 10px;
|
||||
margin-top: 15px;
|
||||
box-sizing: border-box;
|
||||
overflow-y: auto;
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: rgba(204, 204, 204, 0.1);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: rgba(29, 214, 255, 0.78);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.content_item {
|
||||
font-size: 14px;
|
||||
@ -262,12 +215,6 @@ onUnmounted(() => {
|
||||
}
|
||||
}
|
||||
|
||||
.ellipse {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.subfont {
|
||||
color: rgba(138, 149, 165, 1);
|
||||
}
|
||||
|
||||
@ -4,8 +4,8 @@
|
||||
<img src="@/assets/projectLarge/section.svg" alt="">
|
||||
<img src="@/assets/projectLarge/border.svg" alt="">
|
||||
</div>
|
||||
<div v-if="prefix">
|
||||
<img src="@/assets/projectLarge/robot.svg" alt="" style="width: 20px; height: 20px;margin-right: 5px;">
|
||||
<div>
|
||||
<slot></slot>
|
||||
</div>
|
||||
<div>{{ title }}</div>
|
||||
</div>
|
||||
|
||||
@ -1,45 +1,108 @@
|
||||
<template>
|
||||
<div class="large-screen">
|
||||
<Header />
|
||||
<div class="large_screen">
|
||||
<Header :projectId="projectId" :isFull="isFull" @changePage="handleChangePage" />
|
||||
<div class="nav">
|
||||
<leftPage />
|
||||
<centerPage />
|
||||
<rightPage />
|
||||
<div class="nav_left" :style="{ left: isHideOther ? '-25vw' : '0' }">
|
||||
<leftPage :projectId="projectId" />
|
||||
</div>
|
||||
<div class="nav_center" :style="{ width: isFull ? '100%' : 'calc(50vw - 30px)' }">
|
||||
<centerPage :projectId="projectId" :isHide="isFull" />
|
||||
</div>
|
||||
<div class="nav_right" :style="{ right: isHideOther ? '-25vw' : '0' }">
|
||||
<rightPage :projectId="projectId" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import Header from './components/header.vue';
|
||||
import leftPage from './components/leftPage.vue';
|
||||
import centerPage from './components/centerPage.vue';
|
||||
import rightPage from './components/rightPage.vue';
|
||||
import { useUserStoreHook } from '@/store/modules/user';
|
||||
|
||||
const userStore = useUserStoreHook();
|
||||
const projectId = computed(() => userStore.selectedProject.id);
|
||||
const isFull = ref(false)
|
||||
const isHideOther = ref(false)
|
||||
|
||||
/**
|
||||
* 切换中心页面全屏
|
||||
*/
|
||||
const handleChangePage = () => {
|
||||
if (isFull.value) {
|
||||
isFull.value = false;
|
||||
setTimeout(() => {
|
||||
isHideOther.value = false;
|
||||
}, 500);
|
||||
} else {
|
||||
isFull.value = true;
|
||||
isHideOther.value = true;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.large-screen {
|
||||
.large_screen {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background: url('@/assets/large/bg.png') no-repeat;
|
||||
background-size: 100% 100%;
|
||||
background-attachment: fixed;
|
||||
background-color: rgba(4, 7, 17, 1);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: 15rpx;
|
||||
width: 100%;
|
||||
height: calc(100vh - 100px);
|
||||
position: relative;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
width: calc(100vw - 30px);
|
||||
height: calc(100vh - 90px);
|
||||
margin: 0 auto;
|
||||
box-sizing: border-box;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.nav_left,
|
||||
.nav_right {
|
||||
margin: 0 15px 15px 15px;
|
||||
position: absolute;
|
||||
width: calc(25vw - 15px);
|
||||
height: 100%;
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
|
||||
.nav_left {
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.nav_right {
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.nav_center {
|
||||
margin-bottom: 15px;
|
||||
height: 100%;
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
|
||||
/* 中间面板全屏动画 */
|
||||
.full-width {
|
||||
/* 取消flex增长,使用固定宽度 */
|
||||
width: calc(100vw - 30px);
|
||||
flex: none;
|
||||
}
|
||||
|
||||
.slide_left {
|
||||
left: -25vw;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.slide_right {
|
||||
right: -25vw;
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<el-form :model="queryForm" :inline="true">
|
||||
<el-form-item label="版本号" prop="versions">
|
||||
<el-select v-model="queryForm.versions" placeholder="选择版本号" @change="changeVersions">
|
||||
<el-option v-for="item in options" :key="item.id" :label="item.versions" :value="item.id" />
|
||||
<el-option v-for="item in options" :key="item.id" :label="item.versions" :value="item.versions" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="表名" prop="sheet">
|
||||
@ -58,9 +58,10 @@
|
||||
<el-card shadow="never" class="mb8">
|
||||
<el-table ref="tableRef" v-loading="loading" :data="tableData" row-key="id" border lazy default-expand-all>
|
||||
<el-table-column prop="num" label="编号" />
|
||||
<el-table-column prop="name" label="工程或费用名称" />
|
||||
<el-table-column prop="unit" label="单位" />
|
||||
<el-table-column prop="quantity" label="数量" />
|
||||
<el-table-column prop="name" label="工程或费用名称" />
|
||||
<el-table-column prop="unit" label="单位" align="center" />
|
||||
<el-table-column prop="quantity" label="数量" align="center" />
|
||||
<el-table-column prop="specification" label="规格" align="center" />
|
||||
<el-table-column prop="remark" label="单价" align="center">
|
||||
<template #default="scope">
|
||||
<span>{{ scope.row.unitPrice }}</span>
|
||||
@ -166,7 +167,8 @@ const getTableData = async () => {
|
||||
const params = {
|
||||
projectId: currentProject.value?.id,
|
||||
sheet: queryForm.value.sheet,
|
||||
versions: queryForm.value.versions
|
||||
versions: queryForm.value.versions,
|
||||
type: '1'
|
||||
};
|
||||
const res = await getTreeLimit(params);
|
||||
loading.value = false;
|
||||
@ -209,8 +211,6 @@ const tableRef = ref<any>();
|
||||
|
||||
const toggleExpandAll = () => {
|
||||
isExpandAll.value = !isExpandAll.value;
|
||||
console.log(isExpandAll.value);
|
||||
|
||||
tableData.value.forEach((row) => {
|
||||
tableRef.value.toggleRowExpansion(row, isExpandAll.value);
|
||||
});
|
||||
@ -248,7 +248,7 @@ const handleExport = () => {
|
||||
projectId: currentProject.value?.id,
|
||||
sheet: queryForm.value.sheet
|
||||
},
|
||||
`限价一览表${queryForm.value.sheet}.xlsx`
|
||||
`投标成本核算清单${queryForm.value.sheet}.xlsx`
|
||||
);
|
||||
};
|
||||
// 审核
|
||||
|
||||
@ -210,7 +210,8 @@ const getListTable = async () => {
|
||||
const res = await getTreeLimit({
|
||||
projectId: currentProject.value?.id,
|
||||
versions: form.value.versions,
|
||||
sheet: form.value.sheet
|
||||
sheet: form.value.sheet,
|
||||
type: '0'
|
||||
});
|
||||
if (res.code == 200) {
|
||||
tableData.value = res.data;
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<el-form :model="queryForm" :inline="true">
|
||||
<el-form-item label="版本号" prop="versions">
|
||||
<el-select v-model="queryForm.versions" placeholder="选择版本号" @change="changeVersions">
|
||||
<el-option v-for="item in options" :key="item.id" :label="item.versions" :value="item.id" />
|
||||
<el-option v-for="item in options" :key="item.id" :label="item.versions" :value="item.versions" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="表名" prop="sheet">
|
||||
@ -59,8 +59,9 @@
|
||||
<el-table ref="tableRef" v-loading="loading" :data="tableData" row-key="id" border lazy default-expand-all>
|
||||
<el-table-column prop="num" label="编号" />
|
||||
<el-table-column prop="name" label="工程或费用名称" />
|
||||
<el-table-column prop="unit" label="单位" />
|
||||
<el-table-column prop="quantity" label="数量">
|
||||
<el-table-column prop="unit" label="单位" align="center" />
|
||||
<el-table-column prop="specification" label="规格" align="center"/>
|
||||
<el-table-column prop="quantity" label="数量" align="center">
|
||||
<template #default="scope">
|
||||
{{ scope.row.children.length > 0 ? '' : scope.row.quantity }}
|
||||
</template>
|
||||
@ -184,7 +185,8 @@ const getTableData = async () => {
|
||||
const params = {
|
||||
projectId: currentProject.value?.id,
|
||||
sheet: queryForm.value.sheet,
|
||||
versions: queryForm.value.versions
|
||||
versions: queryForm.value.versions,
|
||||
type: '0'
|
||||
};
|
||||
const res = await getTreeLimit(params);
|
||||
loading.value = false;
|
||||
@ -288,7 +290,7 @@ const handleExport = () => {
|
||||
projectId: currentProject.value?.id,
|
||||
sheet: queryForm.value.sheet
|
||||
},
|
||||
`限价一览表${queryForm.value.sheet}.xlsx`
|
||||
`投标成本核算${queryForm.value.sheet}.xlsx`
|
||||
);
|
||||
};
|
||||
// 审核
|
||||
|
||||
@ -138,9 +138,14 @@
|
||||
<el-table-column prop="useQuantity" label="剩余量" align="center">
|
||||
<template #default="scope">
|
||||
{{
|
||||
(scope.row.quantity ? Number(scope.row.quantity) : 0) - (scope.row.useQuantity ? Number(scope.row.useQuantity) : 0) == 0
|
||||
(scope.row.quantity ? Number(scope.row.quantity) : 0) -
|
||||
(scope.row.useQuantity ? Number(scope.row.useQuantity) : 0) -
|
||||
(scope.row.selectNum ? Number(scope.row.selectNum) : 0) ==
|
||||
0
|
||||
? ''
|
||||
: (scope.row.quantity ? Number(scope.row.quantity) : 0) - (scope.row.useQuantity ? Number(scope.row.useQuantity) : 0)
|
||||
: (scope.row.quantity ? Number(scope.row.quantity) : 0) -
|
||||
(scope.row.selectNum ? Number(scope.row.selectNum) : 0) -
|
||||
(scope.row.useQuantity ? Number(scope.row.useQuantity) : 0)
|
||||
}}
|
||||
</template>
|
||||
</el-table-column>
|
||||
@ -149,12 +154,16 @@
|
||||
<el-table-column prop="price" label="总价" align="center">
|
||||
<template #default="scope">
|
||||
{{
|
||||
((scope.row.quantity ? Number(scope.row.quantity) : 0) - (scope.row.useQuantity ? Number(scope.row.useQuantity) : 0)) *
|
||||
((scope.row.quantity ? Number(scope.row.quantity) : 0) -
|
||||
(scope.row.useQuantity ? Number(scope.row.useQuantity) : 0) -
|
||||
(scope.row.selectNum ? Number(scope.row.selectNum) : 0)) *
|
||||
Number(scope.row.unitPrice) ==
|
||||
0
|
||||
? ''
|
||||
: (
|
||||
((scope.row.quantity ? Number(scope.row.quantity) : 0) - (scope.row.useQuantity ? Number(scope.row.useQuantity) : 0)) *
|
||||
((scope.row.quantity ? Number(scope.row.quantity) : 0) -
|
||||
(scope.row.useQuantity ? Number(scope.row.useQuantity) : 0) -
|
||||
(scope.row.selectNum ? Number(scope.row.selectNum) : 0)) *
|
||||
Number(scope.row.unitPrice)
|
||||
).toFixed(2)
|
||||
}}
|
||||
@ -328,18 +337,10 @@ const getVersionNums = async () => {
|
||||
getSheetName();
|
||||
} else {
|
||||
treeForm.value.versions = '';
|
||||
ElMessage({
|
||||
message: '获取版本号失败',
|
||||
type: 'warning'
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
ElMessage({
|
||||
message: '获取版本号失败',
|
||||
type: 'warning'
|
||||
});
|
||||
}
|
||||
};
|
||||
//获取表名
|
||||
@ -356,19 +357,11 @@ const getSheetName = async () => {
|
||||
treeForm.value.sheet = res.data[0];
|
||||
} else {
|
||||
treeForm.value.sheet = '';
|
||||
ElMessage({
|
||||
message: '获取表名失败',
|
||||
type: 'warning'
|
||||
});
|
||||
}
|
||||
getTreeList();
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
ElMessage({
|
||||
message: '获取表名失败',
|
||||
type: 'warning'
|
||||
});
|
||||
}
|
||||
};
|
||||
const handleSelection = (selection: any) => {
|
||||
|
||||
@ -60,8 +60,10 @@
|
||||
<el-table ref="tableRef" v-loading="loading" :data="tableData" row-key="id" border lazy default-expand-all>
|
||||
<el-table-column prop="num" label="编号" />
|
||||
<el-table-column prop="name" label="工程或费用名称" />
|
||||
<el-table-column prop="unit" label="单位" />
|
||||
<el-table-column prop="quantity" label="数量">
|
||||
<el-table-column prop="unit" label="单位" align="center" />
|
||||
<el-table-column prop="taxRate" label="税率" align="center" />
|
||||
<el-table-column prop="specification" label="规格" align="center" />
|
||||
<el-table-column prop="quantity" label="数量" align="center">
|
||||
<template #default="scope">
|
||||
{{ scope.row.children.length > 0 ? '' : scope.row.quantity }}
|
||||
</template>
|
||||
@ -84,7 +86,7 @@
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="price" label="总价" align="center">
|
||||
<el-table-column prop="price" label="总价" align="center" >
|
||||
<template #default="scope">
|
||||
<!-- {{ scope.row.children.length > 0 ? scope.row.children.reduce((sum, child) => sum + child.price, 0) : scope.row.price }} -->
|
||||
{{ scope.row.price != 0 ? Number(scope.row.price).toFixed(2) : null }}
|
||||
@ -124,7 +126,7 @@ const loading = ref(false);
|
||||
const options = ref<any[]>([]);
|
||||
const sheets = ref<any[]>([]);
|
||||
const tableData = ref<any[]>([]);
|
||||
const isExpandAll = ref(false);
|
||||
const isExpandAll = ref(true);
|
||||
const reviewStatus = ref('');
|
||||
const versionObj: any = ref({});
|
||||
const versionMap = new Map();
|
||||
@ -150,18 +152,10 @@ const getVersionNums = async () => {
|
||||
getSheetName();
|
||||
} else {
|
||||
queryForm.value.versions = '';
|
||||
ElMessage({
|
||||
message: '获取版本号失败',
|
||||
type: 'warning'
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
ElMessage({
|
||||
message: '获取版本号失败',
|
||||
type: 'warning'
|
||||
});
|
||||
}
|
||||
};
|
||||
//选择版本号
|
||||
@ -193,19 +187,11 @@ const getSheetName = async () => {
|
||||
queryForm.value.sheet = res.data[0];
|
||||
} else {
|
||||
queryForm.value.sheet = '';
|
||||
ElMessage({
|
||||
message: '获取表名失败',
|
||||
type: 'warning'
|
||||
});
|
||||
}
|
||||
getTableData();
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
ElMessage({
|
||||
message: '获取表名失败',
|
||||
type: 'warning'
|
||||
});
|
||||
}
|
||||
};
|
||||
//获取表格
|
||||
@ -323,7 +309,7 @@ const handleExport = () => {
|
||||
versions: queryForm.value.versions,
|
||||
type: '1'
|
||||
},
|
||||
`限价一览表${queryForm.value.sheet}.xlsx`
|
||||
`限价一览${queryForm.value.sheet}.xlsx`
|
||||
);
|
||||
};
|
||||
// 审批
|
||||
|
||||
@ -93,9 +93,7 @@
|
||||
</el-row>
|
||||
|
||||
<el-form-item label="附图">
|
||||
<file-Upload v-model="form.attachmentsImg" :file-type="['pdf', 'png', 'jpg', 'jpeg', 'gif', 'bmp']">
|
||||
<el-button type="primary">上传附件</el-button>
|
||||
</file-Upload>
|
||||
<file-Upload :fileSize="50" v-model="form.attachmentsImg" :file-type="['png', 'jpg', 'jpeg']"> </file-Upload>
|
||||
</el-form-item>
|
||||
<!-- 变更原因 -->
|
||||
<el-form-item label="变更原因">
|
||||
@ -114,7 +112,7 @@
|
||||
<el-input v-model="form.content" type="textarea" :rows="6" placeholder="请输入内容" />
|
||||
</el-form-item>
|
||||
<el-form-item label="附件" prop="attachments">
|
||||
<file-upload v-model="form.attachments" :limit="1" :file-type="['pdf']"></file-upload>
|
||||
<file-upload :fileSize="50" v-model="form.attachments" :limit="1" :file-type="['pdf']"></file-upload>
|
||||
</el-form-item>
|
||||
<el-form-item label="变更费用估算" prop="costEstimation">
|
||||
<el-input v-model="form.costEstimation" :rows="6" type="number" placeholder="请输入变更费用估算" />
|
||||
|
||||
@ -65,7 +65,7 @@
|
||||
>
|
||||
<el-row :gutter="8" class="items-top">
|
||||
<!-- 1. 专业选择(核心:统一所有角色的专业来源) -->
|
||||
<el-col :span="3" class="mb-4 sm:mb-0 pl-4" style="margin-top:8px;">
|
||||
<el-col :span="3" class="mb-4 sm:mb-0 pl-4" style="margin-top: 8px">
|
||||
<el-form-item
|
||||
:prop="`designers.${configIndex}.userMajor`"
|
||||
:rules="[
|
||||
@ -76,7 +76,8 @@
|
||||
label-width="60px"
|
||||
label="专业"
|
||||
>
|
||||
<el-select filterable
|
||||
<el-select
|
||||
filterable
|
||||
v-model="form.designers[configIndex].userMajor"
|
||||
placeholder="请选择专业"
|
||||
class="w-full transition-all duration-300 border-gray-300"
|
||||
@ -109,7 +110,8 @@
|
||||
label="设计"
|
||||
label-width="50px"
|
||||
>
|
||||
<el-select filterable
|
||||
<el-select
|
||||
filterable
|
||||
v-model="person.userId"
|
||||
placeholder="选择人员"
|
||||
class="w-full transition-all duration-300 border-gray-300"
|
||||
@ -165,7 +167,8 @@
|
||||
label="校审"
|
||||
label-width="50px"
|
||||
>
|
||||
<el-select filterable
|
||||
<el-select
|
||||
filterable
|
||||
v-model="person.userId"
|
||||
placeholder="选择人员"
|
||||
class="w-full transition-all duration-300 border-gray-300"
|
||||
@ -221,7 +224,8 @@
|
||||
label="审定"
|
||||
label-width="50px"
|
||||
>
|
||||
<el-select filterable
|
||||
<el-select
|
||||
filterable
|
||||
v-model="person.userId"
|
||||
placeholder="选择人员"
|
||||
class="w-full transition-all duration-300 border-gray-300"
|
||||
@ -277,7 +281,8 @@
|
||||
label="审核"
|
||||
label-width="50px"
|
||||
>
|
||||
<el-select filterable
|
||||
<el-select
|
||||
filterable
|
||||
v-model="person.userId"
|
||||
placeholder="选择人员"
|
||||
class="w-full transition-all duration-300 border-gray-300"
|
||||
@ -318,7 +323,7 @@
|
||||
</el-col>
|
||||
|
||||
<!-- 操作列 -->
|
||||
<el-col :span="2" class="text-right pr-4">
|
||||
<el-col :span="2" class="pr-4 mt-2 text-right">
|
||||
<el-button
|
||||
type="text"
|
||||
class="text-red-500 hover:text-red-700 transition-colors"
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
<el-card v-if="index < 3" shadow="always">
|
||||
<el-form :model="state.queryForm" :inline="true">
|
||||
<el-form-item label="版本号" prop="versions">
|
||||
<el-select v-model="state.queryForm.versions" placeholder="选择版本号">
|
||||
<el-select v-model="state.queryForm.versions" placeholder="选择版本号" @change="handleChangeVersion">
|
||||
<el-option v-for="item in state.options" :key="item.versions" :label="item.versions" :value="item.versions" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
@ -16,7 +16,7 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="openTable(index)">{{ isExpandAll ? '一键收起' : '一键展开' }}</el-button>
|
||||
<el-button type="primary" @click="openTable(index)">{{ isExpandAll ? '一键收起' : '一键展开' }}</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="success" @click="downloadTemplate(1)">下载模板</el-button>
|
||||
@ -62,15 +62,10 @@
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="state.versionsData.status == 'draft'">
|
||||
<el-button type="primary" con="edit" @click="clickApprovalSheet()">审核</el-button>
|
||||
<el-button type="primary" con="edit" @click="clickApprovalSheet()">审核</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="state.versionsData.status == 'waiting' || state.versionsData.status == 'finish'">
|
||||
<el-button
|
||||
icon="view"
|
||||
@click="lookApprovalFlow()"
|
||||
type="warning"
|
||||
>查看流程</el-button
|
||||
>
|
||||
<el-button icon="view" @click="lookApprovalFlow()" type="warning">查看流程</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
@ -171,6 +166,7 @@ const handleTabChange = (tab) => {
|
||||
onMounted(async () => {
|
||||
await getVersionNums();
|
||||
});
|
||||
|
||||
// 获取版本号
|
||||
async function getVersionNums(isSheet = true) {
|
||||
try {
|
||||
@ -247,10 +243,10 @@ async function handleSheetName() {
|
||||
|
||||
// 获取列表
|
||||
async function handleQueryList(isSheet = true) {
|
||||
if (isSheet && !state.queryForm.sheet) {
|
||||
console.warn('表名不存在,无法获取列表数据');
|
||||
return;
|
||||
}
|
||||
// if (isSheet && !state.queryForm) {
|
||||
// console.warn('表名不存在,无法获取列表数据');
|
||||
// return;
|
||||
// }
|
||||
|
||||
try {
|
||||
state.loading.list = true;
|
||||
@ -308,12 +304,12 @@ function handleChange(sheet) {
|
||||
function handleChangeVersion(versions) {
|
||||
state.queryForm.versions = versions;
|
||||
state.versionsData = state.options.find((e) => e.versions == versions);
|
||||
console.log('state.versionsData', state.versionsData);
|
||||
// console.log('state.versionsData', state.versionsData);
|
||||
state.sheets = [];
|
||||
handleQueryList();
|
||||
}
|
||||
// 在 openTable 方法中通过索引获取对应的表格实例
|
||||
function openTable( index) {
|
||||
function openTable(index) {
|
||||
isExpandAll.value = !isExpandAll.value;
|
||||
nextTick(() => {
|
||||
// 通过索引获取当前标签页的表格实例
|
||||
@ -358,12 +354,12 @@ function lookApprovalFlow(row) {
|
||||
const downloadTemplate = (type) => {
|
||||
// 导出模版文件
|
||||
try {
|
||||
let linkurl = '';
|
||||
let linkurl = '';
|
||||
let name = '';
|
||||
if (type==1) {
|
||||
if (type == 1) {
|
||||
linkurl = '/billOfQuantities.xlsx';
|
||||
name = '工程量清单模板.xlsx';
|
||||
}else{
|
||||
} else {
|
||||
linkurl = '/materialsEquipment.xlsx';
|
||||
name = '物资设备清单模板.xlsx';
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
<!-- 表单区域 -->
|
||||
<el-card class="rounded-lg shadow-sm bg-white border border-gray-100 transition-all hover:shadow-md overflow-hidden">
|
||||
<div class="p-4 bg-gradient-to-r from-blue-50 to-indigo-50 border-b border-gray-100">
|
||||
<h3 class="text-lg font-semibold text-gray-800">投标工程清单</h3>
|
||||
<h3 class="text-lg font-semibold text-gray-800">投标工程量清单</h3>
|
||||
</div>
|
||||
<div class="p-6">
|
||||
<el-form
|
||||
|
||||
@ -113,7 +113,7 @@ const approvalRecordRef = ref<InstanceType<typeof ApprovalRecord>>();
|
||||
//按钮组件
|
||||
const flowCodeOptions = [
|
||||
{
|
||||
value: currentProject.value?.id + '_materialsPlans',
|
||||
value: currentProject.value?.id + '_equipmentList',
|
||||
label: '物资设备清单审核'
|
||||
}
|
||||
];
|
||||
|
||||
@ -50,7 +50,7 @@
|
||||
<div class="p-4">
|
||||
<p class="text-gray-600 mb-4">请选择要启动的流程:</p>
|
||||
<el-select v-model="flowCode" placeholder="请选择流程" style="width: 100%">
|
||||
<el-option v-for="item in flowCodeOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
<el-option v-for="item in [flowCodeOptions[optionIndex]]" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</div>
|
||||
<template #footer>
|
||||
@ -98,6 +98,7 @@ const flowCodeOptions = ref([
|
||||
label: '资金设计变更审批'
|
||||
}
|
||||
]);
|
||||
const optionIndex = ref<number>(0);
|
||||
|
||||
const flowCode = ref<string>('');
|
||||
const status = ref<string>('');
|
||||
@ -272,12 +273,12 @@ const submit = async (status, data) => {
|
||||
} else {
|
||||
if ((form.value.status === 'draft' && (flowCode.value === '' || flowCode.value === null)) || routeParams.value.type === 'add') {
|
||||
if (form.value.costEstimation == '0') {
|
||||
flowCodeOptions.value = [flowCodeOptions.value[0]];
|
||||
optionIndex.value = 0;
|
||||
} else {
|
||||
console.log('🚀 ~ submit ~ flowCodeOptions.value:', flowCodeOptions.value[1]);
|
||||
flowCodeOptions.value = [flowCodeOptions.value[1]];
|
||||
optionIndex.value = 1;
|
||||
}
|
||||
flowCode.value = flowCodeOptions.value[0].value;
|
||||
flowCode.value = flowCodeOptions.value[optionIndex.value].value;
|
||||
dialogVisible.visible = true;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -49,7 +49,32 @@
|
||||
<!-- 资料文件区域 -->
|
||||
<div class="mb-8">
|
||||
<div class="flex items-center justify-between mb-5">
|
||||
<h3 class="text-lg font-semibold text-blue-700">资料文件清单</h3>
|
||||
<div style="display: flex; align-items: center">
|
||||
<h3 class="text-lg font-semibold text-blue-700" style="margin-right: 20px">资料文件清单</h3>
|
||||
<el-upload
|
||||
class="upload-excel"
|
||||
action="#"
|
||||
v-if="!form.id || form.status == 'draft'"
|
||||
ref="uploadRef"
|
||||
:auto-upload="false"
|
||||
:on-change="importTemplate"
|
||||
:show-file-list="false"
|
||||
:accept="'.xlsx,.xls'"
|
||||
:limit="1"
|
||||
>
|
||||
<el-button type="primary" icon="Upload">导入文件</el-button>
|
||||
</el-upload>
|
||||
<el-button
|
||||
v-if="!form.id || form.status == 'draft'"
|
||||
type="primary"
|
||||
style="margin-left: 20px"
|
||||
icon="Download"
|
||||
@click="exportTemplate"
|
||||
class="transition-all hover:bg-blue-600"
|
||||
>
|
||||
导出模版
|
||||
</el-button>
|
||||
</div>
|
||||
<el-button type="primary" size="small" @click="addDocumentItem" v-if="!disabledAll" icon="Plus" class="transition-all hover:bg-blue-600">
|
||||
添加资料
|
||||
</el-button>
|
||||
@ -180,11 +205,11 @@ import { ref, reactive, computed, onMounted, onUnmounted, watch, getCurrentInsta
|
||||
import { useUserStoreHook } from '@/store/modules/user';
|
||||
import { ElMessage, ElLoading, FormRules } from 'element-plus';
|
||||
import { systemUserList } from '@/api/design/appointment';
|
||||
import { collectBatch, byProjectId, exportWord } from '@/api/design/received';
|
||||
import { collectBatch, byProjectId, exportWord, exportExcel } from '@/api/design/received';
|
||||
import { getUser } from '@/api/system/user';
|
||||
import type { ComponentInternalInstance, ElFormInstance } from 'element-plus';
|
||||
import { getInfo } from '@/api/login';
|
||||
|
||||
import * as XLSX from 'xlsx';
|
||||
// 全局实例与状态管理
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const userStore = useUserStoreHook();
|
||||
@ -200,7 +225,7 @@ const documentsFormRef = ref<ElFormInstance>();
|
||||
const userList = ref<any[]>([]);
|
||||
const userMap = new Map<string, string>(); // 存储用户ID与昵称映射
|
||||
const disabledAll = ref(false); // 表单是否全部禁用
|
||||
|
||||
const uploadRef = ref<any>();
|
||||
// 表单核心数据
|
||||
const form = reactive({
|
||||
projectId: currentProject.value?.id,
|
||||
@ -445,7 +470,66 @@ const onLoad = async () => {
|
||||
console.error('文件导出错误:', error);
|
||||
}
|
||||
};
|
||||
const exportTemplate = async () => {
|
||||
// 导出模版
|
||||
proxy?.download(
|
||||
'design/collect/exportExcel',
|
||||
{
|
||||
deptId: userStore.deptId
|
||||
},
|
||||
`收资清单表格.xlsx`
|
||||
);
|
||||
};
|
||||
const importTemplate = async (files, fileList) => {
|
||||
// 导入表格数据
|
||||
const file = fileList[0].raw; // 获取原始文件对象
|
||||
const reader = new FileReader();
|
||||
let obj = {
|
||||
id: '编码',
|
||||
name: '人员',
|
||||
fliename: '目录名',
|
||||
remark: '备注'
|
||||
};
|
||||
reader.onload = (e) => {
|
||||
try {
|
||||
// 读取文件内容
|
||||
const data = new Uint8Array(e.target.result);
|
||||
// 解析Excel
|
||||
const workbook = XLSX.read(data, { type: 'array' });
|
||||
|
||||
// 获取第一个工作表名称
|
||||
const firstSheetName = workbook.SheetNames[0];
|
||||
// 获取第一个工作表内容
|
||||
const worksheet = workbook.Sheets[firstSheetName];
|
||||
|
||||
// 转换为JSON格式
|
||||
const jsonData = XLSX.utils.sheet_to_json(worksheet);
|
||||
|
||||
if (jsonData.length === 0) {
|
||||
ElMessage.info('Excel文件中没有数据');
|
||||
return;
|
||||
}
|
||||
let arr = [];
|
||||
// 判断form.documents 是否对象
|
||||
jsonData.forEach((item, index) => {
|
||||
if (item[obj.id]) {
|
||||
arr.push({
|
||||
id: Date.now() + index,
|
||||
catalogueName: item[obj.fliename],
|
||||
remark: item[obj.remark],
|
||||
userId: item[obj.id]
|
||||
});
|
||||
}
|
||||
});
|
||||
form.documents = arr;
|
||||
uploadRef.value.clearFiles();
|
||||
console.log(arr);
|
||||
} catch (err) {}
|
||||
};
|
||||
|
||||
// 以ArrayBuffer方式读取文件
|
||||
reader.readAsArrayBuffer(file);
|
||||
};
|
||||
/** 页面挂载初始化 */
|
||||
onMounted(() => {
|
||||
// 先获取当前用户信息,再获取部门用户列表,最后回显表单数据
|
||||
|
||||
@ -153,6 +153,11 @@
|
||||
<el-form-item v-if="uploadForm.type == '3'" label="蓝图" prop="fileIds">
|
||||
<file-upload :fileType="['pdf']" :isShowTip="false" :fileSize="100" v-model="uploadForm.fileIds"></file-upload>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="uploadForm.type == '3'" label="抄送人">
|
||||
<el-select multiple filterable clearable v-model="form.userIds" placeholder="请选择抄送人">
|
||||
<el-option :value="item.userId" v-for="item in userCoryList" :key="item.userId" :label="item.nickName + '-' + item.phonenumber" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="uploadForm.type == '1'" label="过程图纸" prop="cancellationIds">
|
||||
<file-upload :fileType="['pdf']" :isShowTip="false" :fileSize="100" v-model="uploadForm.cancellationIds"></file-upload>
|
||||
</el-form-item>
|
||||
@ -255,7 +260,8 @@ import {
|
||||
uploadVolumeFile,
|
||||
majorList,
|
||||
getVolumeCatafileList,
|
||||
volumeFileList
|
||||
volumeFileList,
|
||||
copyUserList
|
||||
} from '@/api/design/volumeCatalog';
|
||||
import { VolumeCatalogVO } from '@/api/design/volumeCatalog/types';
|
||||
import { useUserStoreHook } from '@/store/modules/user';
|
||||
@ -281,6 +287,7 @@ const uploadOpinionVisible = ref(false);
|
||||
const design = ref('');
|
||||
const total = ref(0);
|
||||
const dialogHistory = ref(false);
|
||||
const userCoryList = ref([]);
|
||||
const opinion = ref('');
|
||||
const updateRow = ref({
|
||||
opinion: []
|
||||
@ -416,7 +423,13 @@ const cancel = () => {
|
||||
reset();
|
||||
dialog.visible = false;
|
||||
};
|
||||
|
||||
// 获取人员列表
|
||||
const getDesignUserList = async () => {
|
||||
const res = await copyUserList({ projectId: currentProject.value?.id, userType: 2 });
|
||||
if (res.code === 200) {
|
||||
userCoryList.value = res.data;
|
||||
}
|
||||
};
|
||||
/** 表单重置 */
|
||||
const reset = () => {
|
||||
form.value = { ...initFormData };
|
||||
@ -546,7 +559,7 @@ const onSubmit = async () => {
|
||||
type: uploadForm.type
|
||||
};
|
||||
try {
|
||||
await uploadVolumeFile(obj);
|
||||
await uploadVolumeFile({ ...obj, userIds: form.value.userIds });
|
||||
proxy?.$modal.msgSuccess('文件上传成功');
|
||||
uploadVisible.value = false;
|
||||
await getList();
|
||||
@ -656,6 +669,7 @@ const handleAuditInfo = (row) => {
|
||||
// 审核图纸
|
||||
};
|
||||
onMounted(() => {
|
||||
getDesignUserList();
|
||||
getSpecialtyList();
|
||||
getList();
|
||||
});
|
||||
@ -666,6 +680,7 @@ const listeningProject = watch(
|
||||
(nid, oid) => {
|
||||
queryParams.value.projectId = nid;
|
||||
form.value.projectId = nid;
|
||||
getDesignUserList();
|
||||
getSpecialtyList();
|
||||
getList();
|
||||
}
|
||||
|
||||
@ -1,18 +1,185 @@
|
||||
<template>
|
||||
<div class="text-center py-10">
|
||||
<el-button type="primary" @click="handleGoBack">返回上一页</el-button>
|
||||
<div class="p5" style="width: 100%; height: calc(100vh - 84px)" v-loading="loading">
|
||||
<div id="TrajectoryEarth" style="width: 100%; height: 100%"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="EmptyPage" lang="ts">
|
||||
import { useRouter } from 'vue-router';
|
||||
<script setup name="equipmentGPS">
|
||||
import { ref, onMounted, onUnmounted } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { getFootNote } from '@/api/equipment/index';
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const loading = ref(true);
|
||||
let earthInstance = null;
|
||||
let data = [
|
||||
{
|
||||
'locLongitude': 106.45637808828741,
|
||||
'locLatitude': 29.5597535878972,
|
||||
'locAltitude': 0
|
||||
}
|
||||
];
|
||||
|
||||
// 返回上一页
|
||||
const handleGoBack = () => {
|
||||
router.go(-1);
|
||||
// 获取轨迹数据
|
||||
const getTrajectoryData = async () => {
|
||||
try {
|
||||
// 从URL参数中获取clientId、projectId和userId
|
||||
const { clientId, projectId, userId } = route.query;
|
||||
|
||||
if (!clientId || !projectId || !userId) {
|
||||
ElMessage.warning('缺少必要参数,请检查传入的参数');
|
||||
return;
|
||||
}
|
||||
|
||||
loading.value = true;
|
||||
const res = await getFootNote({ clientId, projectId, userId });
|
||||
|
||||
if (res && res.code === 200 && res.data && res.data.length > 0) {
|
||||
data = res.data;
|
||||
// 渲染轨迹
|
||||
if (earthInstance && earthInstance.viewer) {
|
||||
renderRange(data);
|
||||
}
|
||||
} else {
|
||||
ElMessage.warning('暂无轨迹数据');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取轨迹数据失败:', error);
|
||||
ElMessage.error('获取轨迹数据失败,请稍后重试');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 创建地球
|
||||
const createEarth = () => {
|
||||
if (!window.YJ) {
|
||||
ElMessage.error('YJ库未加载,请检查依赖');
|
||||
return;
|
||||
}
|
||||
|
||||
window.YJ.on({
|
||||
ws: true
|
||||
// host: getIP(), // 资源所在服务器地址
|
||||
// username: this.loginForm.username, // 用户名
|
||||
// password: md5pass, // 密码
|
||||
})
|
||||
.then((res) => {
|
||||
// 创建地球实例
|
||||
earthInstance = new YJ.YJEarth('TrajectoryEarth');
|
||||
window.Earth3 = earthInstance;
|
||||
|
||||
// 开启右键和左键点击事件
|
||||
YJ.Global.openRightClick(window.Earth3);
|
||||
YJ.Global.openLeftClick(window.Earth3);
|
||||
|
||||
// 设置初始视角
|
||||
const view = {
|
||||
position: {
|
||||
lng: 102.03643298211526,
|
||||
lat: 34.393586474501,
|
||||
alt: 11298179.51993155
|
||||
},
|
||||
orientation: {
|
||||
heading: 360,
|
||||
pitch: -89.94481747201486,
|
||||
roll: 0
|
||||
}
|
||||
};
|
||||
|
||||
YJ.Global.CesiumContainer(window.Earth3, {
|
||||
compass: false //罗盘
|
||||
});
|
||||
// 加载底图
|
||||
loadBaseMap(earthInstance.viewer);
|
||||
|
||||
// 可以取消注释以下代码来设置初始视角
|
||||
// YJ.Global.flyTo(earthInstance, view);
|
||||
// YJ.Global.setDefaultView(earthInstance.viewer, view)
|
||||
|
||||
// 地球创建完成后获取并渲染轨迹数据
|
||||
getTrajectoryData();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('初始化地球失败:', err);
|
||||
ElMessage.error('初始化地球失败,请稍后重试');
|
||||
});
|
||||
};
|
||||
|
||||
// 加载底图
|
||||
const loadBaseMap = (viewer) => {
|
||||
if (!viewer || !Cesium) {
|
||||
ElMessage.error('Cesium库未加载,请检查依赖');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 创建瓦片提供器
|
||||
const imageryProvider = new Cesium.UrlTemplateImageryProvider({
|
||||
url: 'https://webst01.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}',
|
||||
fileExtension: 'png',
|
||||
minimumLevel: 0,
|
||||
maximumLevel: 18,
|
||||
projection: Cesium.WebMercatorProjection,
|
||||
credit: new Cesium.Credit('卫星图数据来源')
|
||||
});
|
||||
|
||||
// 添加图层到视图
|
||||
viewer.imageryLayers.addImageryProvider(imageryProvider);
|
||||
} catch (err) {
|
||||
console.error('加载底图失败:', err);
|
||||
ElMessage.error('加载底图失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 渲染轨迹
|
||||
const renderRange = (data) => {
|
||||
if (!data || data.length === 0) {
|
||||
ElMessage.warning('无轨迹数据可渲染');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!earthInstance || !earthInstance.viewer) {
|
||||
ElMessage.error('地球实例未初始化');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const positions = data.map((point) => {
|
||||
return Cesium.Cartesian3.fromDegrees(point.locLongitude, point.locLatitude, point.locAltitude || 0);
|
||||
});
|
||||
|
||||
const entity = earthInstance.viewer.entities.add({
|
||||
polyline: {
|
||||
positions: positions,
|
||||
width: 5,
|
||||
material: Cesium.Color.RED,
|
||||
clampToGround: true
|
||||
}
|
||||
});
|
||||
|
||||
// 调整视角以适应轨迹
|
||||
earthInstance.viewer.flyTo(entity);
|
||||
} catch (err) {
|
||||
console.error('渲染轨迹失败:', err);
|
||||
ElMessage.error('渲染轨迹失败');
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
onMounted(() => {
|
||||
createEarth();
|
||||
});
|
||||
|
||||
//
|
||||
onUnmounted(() => {
|
||||
if (earthInstance) {
|
||||
earthInstance.destroy();
|
||||
earthInstance = null;
|
||||
window.Earth3 = null;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
<style></style>
|
||||
|
||||
@ -24,22 +24,28 @@
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:equipment:edit']"
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['gps:equipment:edit']"
|
||||
>修改</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:equipment:remove']"
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['gps:equipment:remove']"
|
||||
>删除</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.8">
|
||||
<el-button type="primary" plain icon="User" :disabled="single" @click="handleBindUser()" v-hasPermi="['system:equipment:bindUser']"
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="User"
|
||||
:disabled="single"
|
||||
@click="handleBindUser()"
|
||||
v-hasPermi="['gps:equipment:unbindManmachine', 'gps:equipment:bindManmachine']"
|
||||
>绑定用户</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="2">
|
||||
<el-button type="primary" plain @click="handleViewAll" v-hasPermi="['system:equipment:view']">{{ viewAllButtonText }}</el-button>
|
||||
<el-button type="primary" plain @click="handleViewAll">{{ viewAllButtonText }}</el-button>
|
||||
</el-col>
|
||||
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
@ -91,13 +97,20 @@
|
||||
type="primary"
|
||||
icon="User"
|
||||
@click="scope.row.type === 1 ? handleUnbindUser(scope.row) : handleBindUser(scope.row)"
|
||||
v-hasPermi="['gps:equipment:bindUser']"
|
||||
v-hasPermi="['gps:equipment:unbindManmachine', 'gps:equipment:bindManmachine']"
|
||||
>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
<!-- 新增:跳转空页面按钮 -->
|
||||
<el-tooltip content="足迹" placement="top">
|
||||
<el-button link type="primary" icon="Location" @click="handleGoToEmptyPage(scope.row.userId)"></el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
icon="Location"
|
||||
v-hasPermi="['gps:equipmentSon:getList']"
|
||||
@click="handleGoToEmptyPage(scope.row.userId, scope.row.projectId, scope.row.clientId)"
|
||||
:disabled="!scope.row.userId || !scope.row.projectId"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="历史记录" placement="top">
|
||||
<el-button link type="primary" icon="Clock" @click="handleOpenHistoryUser(scope.row.clientId, scope.row.userId)"> </el-button>
|
||||
@ -465,9 +478,14 @@ const handleViewAll = () => {
|
||||
getList();
|
||||
};
|
||||
|
||||
const handleGoToEmptyPage = (userId: any) => {
|
||||
const handleGoToEmptyPage = (userId: any, projectId: any, clientId: any) => {
|
||||
router.push({
|
||||
path: './equipmentGPS'
|
||||
path: './equipmentGPS',
|
||||
query: {
|
||||
userId: userId,
|
||||
projectId: projectId,
|
||||
clientId: clientId
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -63,7 +63,7 @@
|
||||
</el-tooltip>
|
||||
</span>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<!-- <el-col :span="1.5">
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
@ -73,7 +73,7 @@
|
||||
v-hasPermi="['formalities:formalitiesAreConsolidated:edit']"
|
||||
>修改</el-button
|
||||
>
|
||||
</el-col>
|
||||
</el-col> -->
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
</template>
|
||||
@ -81,7 +81,7 @@
|
||||
<el-table v-loading="loading" :data="formalitiesAreConsolidatedList" @selection-change="handleSelectionChange" row-key="id" default-expand-all>
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<!-- <el-table-column label="手续办理清单模板父级" align="center" prop="formalitiesPname" /> -->
|
||||
<el-table-column label="手续办理清单" align="center" prop="formalitiesName" />
|
||||
<el-table-column label="手续办理清单" align="left" prop="formalitiesName" />
|
||||
<el-table-column label="计划开始时间" align="center" prop="planTheStartTime" width="180">
|
||||
<template #default="scope">
|
||||
<span>{{ parseTime(scope.row.planTheStartTime, '{y}-{m}-{d}') }}</span>
|
||||
|
||||
90
src/views/materials/materialOutbound/component/outbound.vue
Normal file
90
src/views/materials/materialOutbound/component/outbound.vue
Normal file
@ -0,0 +1,90 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<el-table size="small" v-if="props.row.length !== 0" :data="props.row">
|
||||
<el-table-column label="材料名称" align="center" prop="materialsName" />
|
||||
<!-- <el-table-column label="材料数量" align="center" prop="quantityCount" /> -->
|
||||
<el-table-column label="剩余量" align="center" prop="residue">
|
||||
<template #default="scope">
|
||||
<span>{{ scope.row.residue }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="出库数量" align="center" prop="number">
|
||||
<template #default="scope">
|
||||
<el-input-number v-if="scope.row.type" v-model="scope.row.number" :controls="false" :min="0" :max="scope.row.residue" :precision="0" />
|
||||
<span v-else>{{ scope.row.number }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="交接单位" align="center" prop="recipient">
|
||||
<template #default="scope">
|
||||
<el-input v-if="scope.row.type" style="width: 150px" v-model="scope.row.recipient" />
|
||||
<span v-else>{{ scope.row.recipient }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="领用人" align="center" prop=" shipper">
|
||||
<template #default="scope">
|
||||
<el-input v-if="scope.row.type" style="width: 150px" v-model="scope.row.shipper" />
|
||||
<span v-else>{{ scope.row.shipper }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作人" align="center" prop="operator">
|
||||
<template #default="scope">
|
||||
<el-input v-if="scope.row.type" style="width: 150px" v-model="scope.row.operator" />
|
||||
<span v-else>{{ scope.row.operator }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="出库时间" align="center" prop="outPutTime" width="180" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" @click="handleConfirm(scope.row)" v-if="scope.row.type"> 确认 </el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useUserStoreHook } from '@/store/modules/user';
|
||||
import { addOutbound } from '@/api/materials/materialOutbound';
|
||||
|
||||
// 获取用户 store
|
||||
const userStore = useUserStoreHook();
|
||||
|
||||
// 从 store 中获取项目列表和当前选中的项目
|
||||
const currentProject = computed(() => userStore.selectedProject);
|
||||
const emit = defineEmits(['success']);
|
||||
const props = defineProps({
|
||||
row: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
});
|
||||
|
||||
const handleConfirm = async (row: any) => {
|
||||
if (row.number == 0 || !row.number) {
|
||||
ElMessage.error('请输入出库数量');
|
||||
return;
|
||||
}
|
||||
if (!row.shipper) {
|
||||
ElMessage.error('请输入领用人');
|
||||
return;
|
||||
}
|
||||
if (!row.operator) {
|
||||
ElMessage.error('请输入操作人');
|
||||
return;
|
||||
}
|
||||
console.log(row);
|
||||
|
||||
const params = {
|
||||
...row,
|
||||
materialsId: row.id,
|
||||
projectId: currentProject.value?.id,
|
||||
outPut: '1'
|
||||
};
|
||||
console.log(params);
|
||||
const data = await addOutbound(params);
|
||||
if (data.code === 200) {
|
||||
ElMessage.success('出库成功');
|
||||
emit('success');
|
||||
}
|
||||
};
|
||||
</script>
|
||||
148
src/views/materials/materialOutbound/index.vue
Normal file
148
src/views/materials/materialOutbound/index.vue
Normal file
@ -0,0 +1,148 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<el-form-item label="材料名称" prop="materialName">
|
||||
<el-input v-model="queryParams.materialName" placeholder="请输入材料名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<el-card shadow="never">
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="tableList"
|
||||
:row-key="
|
||||
(row) => {
|
||||
return row.id;
|
||||
}
|
||||
"
|
||||
@row-click="
|
||||
(row, column, event) => {
|
||||
// 阻止点击行时自动展开
|
||||
if (column.property) event.stopPropagation();
|
||||
}
|
||||
"
|
||||
:preserve-expanded-content="true"
|
||||
>
|
||||
<el-table-column type="expand">
|
||||
<template #default="{ row }">
|
||||
<outbound :row="row.children ?? []" @success="getTableList" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="序号" type="index" width="60" align="center" />
|
||||
<el-table-column label="材料名称" align="center" prop="materialsName" />
|
||||
<el-table-column label="规格" align="center" prop="typeSpecificationName" />
|
||||
<el-table-column label="计量单位" align="center" prop="weightId" />
|
||||
<el-table-column label="材料数量" align="center" prop="quantityCount" />
|
||||
<el-table-column label="剩余量" align="center" prop="residue" />
|
||||
<el-table-column label="操作人" align="center" prop="operator"> </el-table-column>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" icon="Plus" @click="handleoutbound(scope.row)"> 新增出库 </el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getTableList"
|
||||
/>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { outboundMaterials } from '@/api/materials/materialOutbound';
|
||||
import { useUserStoreHook } from '@/store/modules/user';
|
||||
import outbound from './component/outbound.vue';
|
||||
import { number } from 'vue-types';
|
||||
const { proxy } = getCurrentInstance() as any;
|
||||
const queryFormRef = ref();
|
||||
const queryParams = ref({
|
||||
materialName: '',
|
||||
pageNum: 1,
|
||||
pageSize: 10
|
||||
});
|
||||
const total = ref(0);
|
||||
const loading = ref(false);
|
||||
const tableList = ref([]);
|
||||
// 获取用户 store
|
||||
const userStore = useUserStoreHook();
|
||||
|
||||
// 从 store 中获取项目列表和当前选中的项目
|
||||
const currentProject = computed(() => userStore.selectedProject);
|
||||
//获取列表数据
|
||||
const getTableList = async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
const res = await outboundMaterials({ ...queryParams.value, projectId: currentProject.value?.id });
|
||||
if (res.code === 200) {
|
||||
loading.value = false;
|
||||
tableList.value = res.rows;
|
||||
total.value = res.data.total;
|
||||
}
|
||||
} catch (error) {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
getTableList();
|
||||
// 出入库
|
||||
const handleoutbound = (row: any) => {
|
||||
console.log(row);
|
||||
if (row.children == null) {
|
||||
row.children = [];
|
||||
}
|
||||
if (row.children.some((child) => child.type === 'add')) {
|
||||
ElMessage.warning('已经存在出库记录,不能重复添加');
|
||||
return;
|
||||
}
|
||||
row.children.push({
|
||||
type: 'add',
|
||||
id: row.id,
|
||||
number: 0,
|
||||
operator: '',
|
||||
shipper: '',
|
||||
recipient: '',
|
||||
residue: row.residue,
|
||||
materialsName: row.materialsName
|
||||
});
|
||||
// 手动触发展开行
|
||||
const table = document.querySelector('.el-table__body-wrapper');
|
||||
const rows = table?.querySelectorAll('.el-table__row');
|
||||
const rowEl = rows?.[Array.from(tableList.value).indexOf(row)];
|
||||
const expandBtn: any = rowEl?.querySelector('.el-table__expand-icon');
|
||||
// 如果行未展开,则点击展开按钮
|
||||
if (expandBtn && !expandBtn.classList.contains('el-table__expand-icon--expanded')) {
|
||||
expandBtn.click();
|
||||
}
|
||||
};
|
||||
//搜索
|
||||
const handleQuery = () => {
|
||||
queryParams.value.pageNum = 1;
|
||||
getTableList();
|
||||
};
|
||||
|
||||
//重置
|
||||
const resetQuery = () => {
|
||||
console.log(111111111);
|
||||
|
||||
queryFormRef.value?.resetFields();
|
||||
handleQuery();
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@ -1,13 +1,11 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter"
|
||||
:leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="auto">
|
||||
<el-form-item label="材料名称" prop="materialsName">
|
||||
<el-input v-model="queryParams.materialsName" placeholder="请输入材料名称" clearable
|
||||
@keyup.enter="handleQuery" />
|
||||
<el-input v-model="queryParams.materialsName" placeholder="请输入材料名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="材料提供商" prop="companyId">
|
||||
<el-select v-model="queryParams.companyId" clearable placeholder="全部">
|
||||
@ -27,22 +25,7 @@
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['materials:materials:add']"> 新增
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()"
|
||||
v-hasPermi="['materials:materials:edit']">修改
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()"
|
||||
v-hasPermi="['materials:materials:remove']">删除
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="warning" plain icon="Download" @click="handleExport"
|
||||
v-hasPermi="['materials:materials:export']">导出 </el-button>
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['materials:materials:add']"> 新增 </el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
@ -72,64 +55,81 @@
|
||||
<el-table-column fixed="right" label="操作" align="center" class-name="small-padding fixed-width" width="320">
|
||||
<template #default="scope">
|
||||
<el-space>
|
||||
<el-button link type="primary" icon="View" @click="handleShowDrawer(scope.row)"
|
||||
v-hasPermi="['materials:materials:query']">
|
||||
<el-button link type="primary" icon="View" @click="handleShowDrawer(scope.row)" v-hasPermi="['materials:materials:query']">
|
||||
详情
|
||||
</el-button>
|
||||
<el-button link type="success" icon="Edit" @click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['materials:materials:edit']"> 修改 </el-button>
|
||||
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)"
|
||||
v-hasPermi="['materials:materials:remove']">
|
||||
<el-button link type="success" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['materials:materials:edit']"> 修改 </el-button>
|
||||
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['materials:materials:remove']">
|
||||
删除
|
||||
</el-button>
|
||||
<el-button v-hasPermi="['materials:materialsInventory:edit']" link type="primary" icon="Plus"
|
||||
@click="handleAddMaterialsInventory(scope.row)"> 出入库 </el-button>
|
||||
<el-button
|
||||
v-hasPermi="['materials:materialsInventory:edit']"
|
||||
link
|
||||
type="primary"
|
||||
icon="Plus"
|
||||
@click="handleAddMaterialsInventory(scope.row)"
|
||||
>
|
||||
出入库
|
||||
</el-button>
|
||||
</el-space>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize" @pagination="getList" />
|
||||
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
|
||||
</el-card>
|
||||
<!-- 添加或修改材料名称对话框 -->
|
||||
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
|
||||
<el-dialog draggable :title="dialog.title" v-model="dialog.visible" width="800px" append-to-body>
|
||||
<el-form ref="materialsFormRef" :model="form" :rules="rules" label-width="120px">
|
||||
<el-form-item label="材料名称" prop="materialsName">
|
||||
<el-input v-model="form.materialsName" placeholder="请输入材料名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="规格型号名称" prop="typeSpecificationName">
|
||||
<el-input v-model="form.typeSpecificationName" placeholder="请输入规格型号名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="材料供应商" prop="companyId">
|
||||
<el-select v-model="form.companyId" clearable placeholder="请选择材料提供商">
|
||||
<el-option v-for="item in companyOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="使用部位" prop="usePart">
|
||||
<el-input v-model="form.usePart" placeholder="请输入使用部位" />
|
||||
</el-form-item>
|
||||
<el-form-item label="计量单位" prop="weightId">
|
||||
<el-input v-model="form.weightId" placeholder="请输入计量单位" />
|
||||
</el-form-item>
|
||||
<el-form-item label="预计材料数量" prop="quantityCount">
|
||||
<el-input v-model="form.quantityCount" placeholder="请输入预计材料数量" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
<el-form-item label="材料文件" prop="fileOssIdMap">
|
||||
<div :key="item.value" v-for="item in materials_file_type">
|
||||
<h3>{{ item.label }}</h3>
|
||||
<file-upload v-model="ossIdMap[item.value]" :limit="1" :file-size="50" :file-type="['pdf']"
|
||||
@update:model-value="
|
||||
(args) => {
|
||||
handleOssUpdate(args, item.value);
|
||||
}
|
||||
" />
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="材料名称" prop="materialsName"> <el-input v-model="form.materialsName" placeholder="请输入材料名称" /> </el-form-item
|
||||
></el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="规格型号名称" prop="typeSpecificationName">
|
||||
<el-input v-model="form.typeSpecificationName" placeholder="请输入规格型号名称" /> </el-form-item
|
||||
></el-col>
|
||||
<el-col :span="12"
|
||||
><el-form-item label="材料提供商" prop="companyId">
|
||||
<el-select v-model="form.companyId" clearable placeholder="请选择材料提供商">
|
||||
<el-option v-for="item in companyOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select> </el-form-item
|
||||
></el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="使用部位" prop="usePart"> <el-input v-model="form.usePart" placeholder="请输入使用部位" /> </el-form-item
|
||||
></el-col>
|
||||
<el-col :span="12"
|
||||
><el-form-item label="计量单位" prop="weightId"> <el-input v-model="form.weightId" placeholder="请输入计量单位" /> </el-form-item
|
||||
></el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="预计材料数量" prop="quantityCount">
|
||||
<el-input v-model="form.quantityCount" type="number" min="0" placeholder="请输入预计材料数量" /> </el-form-item
|
||||
></el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="备注" prop="remark"> <el-input type="textarea" v-model="form.remark" placeholder="请输入备注" /> </el-form-item
|
||||
></el-col>
|
||||
<el-col :span="12" :key="item.value" v-for="item in materials_file_type">
|
||||
<el-form-item label="材料文件" prop="fileOssIdMap">
|
||||
<div>
|
||||
<h3>{{ item.label }}</h3>
|
||||
<file-upload
|
||||
:isShowTip="false"
|
||||
v-model="ossIdMap[item.value]"
|
||||
:limit="1"
|
||||
:file-size="50"
|
||||
:file-type="['pdf']"
|
||||
@update:model-value="
|
||||
(args) => {
|
||||
handleOssUpdate(args, item.value);
|
||||
}
|
||||
"
|
||||
/>
|
||||
</div> </el-form-item
|
||||
></el-col>
|
||||
<el-col :span="24" style="color: rgb(237 70 61)">注意:请上传pdf格式文件</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
@ -137,8 +137,7 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<materials-inventory-add-dialog :materials-id="currentMaterialsId" :project-id="currentProject.id" ref="dialogRef"
|
||||
@submit="getList" />
|
||||
<materials-inventory-add-dialog :materials-id="currentMaterialsId" :project-id="currentProject.id" ref="dialogRef" @submit="getList" />
|
||||
<el-dialog title="材料详情" v-model="showDetailDrawer" width="700px">
|
||||
<materials-detail-drawer :materials-id="currentMaterialsId" />
|
||||
</el-dialog>
|
||||
@ -151,7 +150,7 @@ import { MaterialsForm, MaterialsQuery, MaterialsVO } from '@/api/materials/mate
|
||||
import { useUserStoreHook } from '@/store/modules/user';
|
||||
import MaterialsInventoryTable from '@/views/materials/materials/component/MaterialsInventoryTable.vue';
|
||||
import MaterialsInventoryAddDialog from '@/views/materials/materials/component/MaterialsInventoryAddDialog.vue';
|
||||
import { listCompany } from '@/api/materials/company';
|
||||
import { listCompany, supplierInputGet } from '@/api/materials/company';
|
||||
import { CompanyVO } from '@/api/materials/company/types';
|
||||
import MaterialsDetailDrawer from '@/views/materials/materials/component/MaterialsDetailDrawer.vue';
|
||||
|
||||
@ -227,14 +226,14 @@ const getList = async () => {
|
||||
/** 获取当前项目下的公司列表 */
|
||||
const getCompanyList = async () => {
|
||||
loading.value = true;
|
||||
const companyRes = await listCompany({
|
||||
pageNum: 1,
|
||||
pageSize: 1000,
|
||||
const companyRes = await supplierInputGet({
|
||||
projectId: currentProject.value?.id
|
||||
});
|
||||
companyOptions.value = companyRes.rows.map((company: CompanyVO) => ({
|
||||
console.log(companyRes);
|
||||
|
||||
companyOptions.value = companyRes.data.map((company) => ({
|
||||
value: company.id,
|
||||
label: company.companyName
|
||||
label: company.supplierName
|
||||
}));
|
||||
loading.value = false;
|
||||
};
|
||||
@ -363,6 +362,7 @@ const listeningProject = watch(
|
||||
(nid, oid) => {
|
||||
queryParams.value.projectId = nid;
|
||||
form.value.projectId = nid;
|
||||
getCompanyList();
|
||||
getList();
|
||||
}
|
||||
);
|
||||
|
||||
@ -73,7 +73,10 @@
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="表单编号" prop="formCode">
|
||||
<el-input v-model="form.formCode" placeholder="请输入表单编号" />
|
||||
<!-- <el-input v-model="form.formCode" placeholder="请输入表单编号" /> -->
|
||||
<el-select v-model="form.formCode" placeholder="请选择表单编号" @change="(value) => formCodeChange(value)">
|
||||
<el-option v-for="item in options" :key="item.formCode" :label="item.formCode" :value="item.formCode" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
@ -123,11 +126,11 @@
|
||||
<el-col :span="12">
|
||||
<el-form-item
|
||||
label="名称"
|
||||
:prop="`itemList.${index}.name`"
|
||||
:prop="`itemList.${index}.materialsId`"
|
||||
:rules="[{ required: true, message: '名称不能为空', trigger: 'blur' }]"
|
||||
>
|
||||
<el-select v-model="item.inventoryId" placeholder="请选择名称" @change="(value) => getNameChange(value, index, item)">
|
||||
<el-option v-for="opt in optionsName" :key="opt.id" :label="opt.materialsName" :value="opt.id" />
|
||||
<el-select v-model="item.materialsId" placeholder="请选择名称" @change="(value) => getNameChange(value, index, item)">
|
||||
<el-option v-for="opt in optionsName" :key="opt.id" :label="`${opt.materialsName}_${opt.createTime}`" :value="opt.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@ -168,13 +171,9 @@
|
||||
</el-col> -->
|
||||
<el-col :span="12">
|
||||
<el-form-item label="领取" :prop="`itemList.${index}.issuedQuantity`">
|
||||
<el-input
|
||||
v-model.number="item.issuedQuantity"
|
||||
disabled
|
||||
placeholder="请输入领取数量"
|
||||
@input="handleIssuedChange(index)"
|
||||
@blur="handleIssuedChange(index)"
|
||||
/>
|
||||
<el-select v-model="item.issuedQuantity" placeholder="请选择数量">
|
||||
<el-option v-for="opt in item.outList" :key="opt.id" :label="opt.number" :value="opt.number" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<!-- <el-col :span="12">
|
||||
@ -247,7 +246,8 @@ import {
|
||||
addMaterialIssue,
|
||||
updateMaterialIssue,
|
||||
inventoryList,
|
||||
getMaterialName
|
||||
getMaterialName,
|
||||
getMaterialInfo
|
||||
} from '@/api/materials/materialIssue';
|
||||
|
||||
import { MaterialIssueVO, MaterialIssueQuery, MaterialIssueForm } from '@/api/materials/materialIssue/types';
|
||||
@ -307,6 +307,7 @@ const getInitFormData = () => {
|
||||
itemList: [
|
||||
{
|
||||
id: undefined,
|
||||
|
||||
specification: undefined,
|
||||
unit: undefined,
|
||||
stockQuantity: undefined,
|
||||
@ -314,7 +315,8 @@ const getInitFormData = () => {
|
||||
remainingQuantity: undefined,
|
||||
name: undefined, // 数量验收的名称
|
||||
remark: undefined,
|
||||
materialsId: undefined
|
||||
materialsId: undefined,
|
||||
outList: []
|
||||
}
|
||||
]
|
||||
};
|
||||
@ -365,7 +367,7 @@ const computeMaterialName = () => {
|
||||
.map((item) => item.name.trim())
|
||||
.filter((name, index, self) => self.indexOf(name) === index); // 去重(如需保留重复则删除这行)
|
||||
|
||||
form.value.materialName = validNames.join(',');
|
||||
// form.value.materialName = validNames.join(',');
|
||||
};
|
||||
|
||||
/** 查询物料领料单列表 */
|
||||
@ -404,6 +406,7 @@ const getNameChange = (value, index, item) => {
|
||||
item.unit = selected.weightId;
|
||||
item.issuedQuantity = selected.number;
|
||||
item.stockQuantity = Number(selected.inventoryNumber) || 0;
|
||||
item.outList = selected.outList || [];
|
||||
// calculateRemaining(index); // 计算剩余数量
|
||||
}
|
||||
};
|
||||
@ -529,8 +532,27 @@ const handleAdd = () => {
|
||||
dialog.title = '添加物料领料单';
|
||||
// 新增时初始计算材料名称
|
||||
computeMaterialName();
|
||||
getFormData();
|
||||
};
|
||||
|
||||
const options = ref([]);
|
||||
//新增获取表单数据
|
||||
const getFormData = async () => {
|
||||
const res = await getMaterialInfo(currentProject.value.id);
|
||||
if (res.code == 200) {
|
||||
options.value = res.data;
|
||||
}
|
||||
};
|
||||
const formCodeChange = (value) => {
|
||||
console.log(value);
|
||||
const selected = options.value.find((opt) => opt.formCode === value);
|
||||
if (selected) {
|
||||
form.value.materialName = selected.materialName;
|
||||
form.value.orderingUnit = selected.orderingUnit;
|
||||
form.value.supplierUnit = selected.supplierUnit;
|
||||
optionsName.value = selected.materials;
|
||||
}
|
||||
};
|
||||
/** 修改按钮操作 */
|
||||
const handleUpdate = async (row?: MaterialIssueVO) => {
|
||||
reset();
|
||||
@ -597,6 +619,7 @@ const submitForm = () => {
|
||||
remainingQuantity: Number(item.remainingQuantity)
|
||||
}))
|
||||
};
|
||||
console.log('提交数据:', submitData);
|
||||
|
||||
if (form.value.id) {
|
||||
await updateMaterialIssue(submitData);
|
||||
@ -638,10 +661,13 @@ const addItem = () => {
|
||||
// 删除数量验收条目
|
||||
const removeItem = (index: number) => {
|
||||
if (form.value.itemList.length > 1) {
|
||||
console.log(111111);
|
||||
console.log(itemWatchStopFns.value[index]);
|
||||
|
||||
// 停止该条目的监听
|
||||
if (itemWatchStopFns.value[index]) {
|
||||
itemWatchStopFns.value[index]();
|
||||
}
|
||||
// if (itemWatchStopFns.value[index]) {
|
||||
// itemWatchStopFns.value[index]();
|
||||
// }
|
||||
form.value.itemList.splice(index, 1);
|
||||
itemWatchStopFns.value.splice(index, 1);
|
||||
// 删除后重新计算材料名称
|
||||
@ -682,7 +708,7 @@ watch(
|
||||
|
||||
onMounted(() => {
|
||||
getList();
|
||||
getName();
|
||||
// getName();
|
||||
});
|
||||
|
||||
// 监听项目id刷新数据
|
||||
@ -692,7 +718,7 @@ const listeningProject = watch(
|
||||
queryParams.value.projectId = nid;
|
||||
form.value.projectId = nid;
|
||||
getList();
|
||||
getName();
|
||||
// getName();
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@ -42,7 +42,7 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="2">领料单位</th>
|
||||
<td class="th-bg" colspan="2">{{ formData.placeholder }}</td>
|
||||
<td class="th-bg" colspan="2">{{ formData.issueUnit }}</td>
|
||||
<th colspan="2">保管单位</th>
|
||||
<td class="th-bg" colspan="2">{{ formData.storageUnit }}</td>
|
||||
</tr>
|
||||
|
||||
@ -1,14 +1,13 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter"
|
||||
:leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<el-form-item label="表单编号" prop="formCode">
|
||||
<el-input v-model="queryParams.formCode" placeholder="请输入表单编号" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="材料来源" prop="materialSource">
|
||||
<el-form-item label="材料来源" prop="materialSource">
|
||||
<el-select v-model="queryParams.materialSource" filterable placeholder="请选择材料来源">
|
||||
<el-option label="全部" value="0"></el-option>
|
||||
<el-option label="甲供材料" value="1"></el-option>
|
||||
@ -16,8 +15,7 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="材料名称" prop="materialName">
|
||||
<el-input v-model="queryParams.materialName" placeholder="请输入设备材料名称" clearable
|
||||
@keyup.enter="handleQuery" />
|
||||
<el-input v-model="queryParams.materialName" placeholder="请输入设备材料名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="订货单位" prop="orderingUnit">
|
||||
<el-input v-model="queryParams.orderingUnit" placeholder="请输入订货单位" clearable @keyup.enter="handleQuery" />
|
||||
@ -38,8 +36,7 @@
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd"
|
||||
v-hasPermi="['materials:materialReceive:add']">新增</el-button>
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['materials:materialReceive:add']">新增</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
@ -50,8 +47,9 @@
|
||||
<el-table-column label="表单编号" align="center" prop="formCode" />
|
||||
<el-table-column label="材料来源" align="center" prop="projectName">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.materialSource == '1' ? 'success' : 'warning'">{{ scope.row.materialSource == '1' ?
|
||||
'甲供材料' : '乙供材料' }}</el-tag>
|
||||
<el-tag :type="scope.row.materialSource == '1' ? 'success' : 'warning'">{{
|
||||
scope.row.materialSource == '1' ? '甲供材料' : '乙供材料'
|
||||
}}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="设备材料名称" align="center" prop="materialName" />
|
||||
@ -77,20 +75,28 @@
|
||||
<!-- <el-button link type="primary" icon="edit" @click="handleUpdate(scope.row)" v-hasPermi="['materials:materialReceive:edit']"
|
||||
>修改</el-button
|
||||
> -->
|
||||
<el-button link type="primary" icon="View" @click="handleView(scope.row)"
|
||||
v-hasPermi="['materials:materialReceive:query']">查看</el-button>
|
||||
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)"
|
||||
v-hasPermi="['materials:materialReceive:remove']">删除</el-button>
|
||||
<el-button link type="primary" icon="View" @click="handleView(scope.row)" v-hasPermi="['materials:materialReceive:query']"
|
||||
>查看</el-button
|
||||
>
|
||||
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['materials:materialReceive:remove']"
|
||||
>删除</el-button
|
||||
>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize" @pagination="getList" />
|
||||
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
|
||||
</el-card>
|
||||
|
||||
<!-- 添加或修改物料接收单对话框 -->
|
||||
<el-dialog :close-on-click-modal="false" :close-on-press-escape="false" draggable :title="dialog.title"
|
||||
v-model="dialog.visible" width="800px" append-to-body>
|
||||
<el-dialog
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
draggable
|
||||
:title="dialog.title"
|
||||
v-model="dialog.visible"
|
||||
width="800px"
|
||||
append-to-body
|
||||
>
|
||||
<el-form ref="materialReceiveFormRef" :model="form" :rules="rules" label-width="110px">
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
@ -108,10 +114,8 @@
|
||||
</el-col>
|
||||
<el-col v-if="form.materialSource == '2'" :span="12">
|
||||
<el-form-item label="采购单编号" prop="docId">
|
||||
<el-select @change="handleSelect" v-model="form.docId" filterable placeholder="请选择采购单"
|
||||
style="width: 100%">
|
||||
<el-option v-for="item in purchaseDocList" :key="item.id" :label="item.docCode"
|
||||
:value="item.id"></el-option>
|
||||
<el-select @change="handleSelect" v-model="form.docId" filterable placeholder="请选择采购单" style="width: 100%">
|
||||
<el-option v-for="item in purchaseDocList" :key="item.id" :label="item.docCode" :value="item.id"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@ -130,11 +134,20 @@
|
||||
<el-input disabled v-model="form.projectName" placeholder="请输入工程名称" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" :offset="0"
|
||||
><el-form-item label="材料名称" prop="materialName">
|
||||
<el-input v-model="form.materialName" placeholder="请输入设备材料名称" clearable /> </el-form-item
|
||||
></el-col>
|
||||
|
||||
<el-col :span="12" v-if="form.materialSource == '2'">
|
||||
<el-form-item label="合同编号" prop="contractName">
|
||||
<el-select v-model="form.contractName" filterable placeholder="请选择合同" style="width: 100%">
|
||||
<el-option v-for="item in contractNameList" :key="item.contractCode" :label="item.contractCode"
|
||||
:value="item.contractCode"></el-option>
|
||||
<el-option
|
||||
v-for="item in contractNameList"
|
||||
:key="item.contractCode"
|
||||
:label="item.contractCode"
|
||||
:value="item.contractCode"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@ -149,48 +162,59 @@
|
||||
<div class="detail">
|
||||
<div class="detail-header">
|
||||
<span>数量验收</span>
|
||||
<el-button type="primary" v-if="form.materialSource == '1'" link @click="addItem"
|
||||
icon="Plus">添加数量验收</el-button>
|
||||
<el-button type="primary" v-if="form.materialSource == '1'" link @click="addItem" icon="Plus">添加数量验收</el-button>
|
||||
</div>
|
||||
<div v-for="(item, index) in form.itemList" :key="item.id" class="detail-item">
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="名称" :prop="`itemList.${index}.name`"
|
||||
:rules="{ required: true, message: '名称不能为空', trigger: 'blur' }">
|
||||
<el-form-item label="名称" :prop="`itemList.${index}.name`" :rules="{ required: true, message: '名称不能为空', trigger: 'blur' }">
|
||||
<el-input :disabled="form.materialSource == '2'" v-model="item.name" placeholder="请输入名称" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="规格" :prop="`itemList.${index}.specification`"
|
||||
:rules="{ required: true, message: '规格不能为空', trigger: 'blur' }">
|
||||
<el-input :disabled="form.materialSource == '2'" v-model="item.specification"
|
||||
placeholder="请输入规格" />
|
||||
<el-form-item
|
||||
label="规格"
|
||||
:prop="`itemList.${index}.specification`"
|
||||
:rules="{ required: true, message: '规格不能为空', trigger: 'blur' }"
|
||||
>
|
||||
<el-input :disabled="form.materialSource == '2'" v-model="item.specification" placeholder="请输入规格" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="单位" :prop="`itemList.${index}.unit`"
|
||||
:rules="{ required: true, message: '单位不能为空', trigger: 'blur' }">
|
||||
<el-form-item label="单位" :prop="`itemList.${index}.unit`" :rules="{ required: true, message: '单位不能为空', trigger: 'blur' }">
|
||||
<el-input :disabled="form.materialSource == '2'" v-model="item.unit" placeholder="请输入单位" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="数量" :prop="`itemList.${index}.quantity`" :rules="rules.quantityRule"
|
||||
ref="quantityFormItemRefs[index]">
|
||||
<el-input :disabled="form.materialSource == '2'" type="number" v-model.number="item.quantity"
|
||||
placeholder="请输入数量" min="0" @input="handleQuantityInput(index)"
|
||||
@blur="handleQuantityBlur(index)" />
|
||||
<el-form-item label="数量" :prop="`itemList.${index}.quantity`" :rules="rules.quantityRule" ref="quantityFormItemRefs[index]">
|
||||
<el-input
|
||||
:disabled="form.materialSource == '2'"
|
||||
type="number"
|
||||
v-model.number="item.quantity"
|
||||
placeholder="请输入数量"
|
||||
min="0"
|
||||
@input="handleQuantityInput(index)"
|
||||
@blur="handleQuantityBlur(index)"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="验收" :prop="`itemList.${index}.acceptedQuantity`"
|
||||
:rules="rules.acceptedQuantityRule">
|
||||
<el-input type="number" v-model.number="item.acceptedQuantity" placeholder="请输入验收" min="0"
|
||||
@input="handleAcceptedInput(index)" />
|
||||
<el-form-item label="验收" :prop="`itemList.${index}.acceptedQuantity`" :rules="rules.acceptedQuantityRule">
|
||||
<el-input
|
||||
type="number"
|
||||
v-model.number="item.acceptedQuantity"
|
||||
placeholder="请输入验收"
|
||||
min="0"
|
||||
@input="handleAcceptedInput(index)"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="缺件" :prop="`itemList.${index}.shortageQuantity`"
|
||||
:rules="{ required: true, message: '缺件数量不能为空', trigger: 'blur' }">
|
||||
<el-form-item
|
||||
label="缺件"
|
||||
:prop="`itemList.${index}.shortageQuantity`"
|
||||
:rules="{ required: true, message: '缺件数量不能为空', trigger: 'blur' }"
|
||||
>
|
||||
<el-input type="number" min="0" v-model="item.shortageQuantity" placeholder="自动计算" readonly />
|
||||
<span class="tips">*自动计算(数量-验收数量)</span>
|
||||
</el-form-item>
|
||||
@ -212,26 +236,22 @@
|
||||
|
||||
<el-col :span="12">
|
||||
<el-form-item label="合格证文件" prop="certCountFileId">
|
||||
<file-upload :isShowTip="false" :fileType="['pdf', 'png', 'jpg', 'jpeg']"
|
||||
v-model="form.certCountFileId" />
|
||||
<file-upload :isShowTip="false" :fileType="['pdf', 'png', 'jpg', 'jpeg']" v-model="form.certCountFileId" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="出厂报告文件" prop="reportCountFileId">
|
||||
<file-upload :isShowTip="false" :fileType="['pdf', 'png', 'jpg', 'jpeg']"
|
||||
v-model="form.reportCountFileId" />
|
||||
<file-upload :isShowTip="false" :fileType="['pdf', 'png', 'jpg', 'jpeg']" v-model="form.reportCountFileId" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="技术资料文件" prop="techDocCountFileId">
|
||||
<file-upload :isShowTip="false" :fileType="['pdf', 'png', 'jpg', 'jpeg']"
|
||||
v-model="form.techDocCountFileId" />
|
||||
<file-upload :isShowTip="false" :fileType="['pdf', 'png', 'jpg', 'jpeg']" v-model="form.techDocCountFileId" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="厂家资质文件" prop="licenseCountFileId">
|
||||
<file-upload :isShowTip="false" :fileType="['pdf', 'png', 'jpg', 'jpeg']"
|
||||
v-model="form.licenseCountFileId" />
|
||||
<file-upload :isShowTip="false" :fileType="['pdf', 'png', 'jpg', 'jpeg']" v-model="form.licenseCountFileId" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
@ -412,7 +432,7 @@ const { queryParams, form, rules } = toRefs(data);
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
if(queryParams.value.materialSource=='0'){
|
||||
if (queryParams.value.materialSource == '0') {
|
||||
delete queryParams.value.materialSource;
|
||||
}
|
||||
const res = await listMaterialReceive(queryParams.value);
|
||||
@ -506,9 +526,9 @@ const handleUpdate = async (row?: MaterialReceiveVO) => {
|
||||
watchItemChanges(index);
|
||||
// 手动触发当前条目的数量、验收数量、缺件数量验证
|
||||
if (materialReceiveFormRef.value) {
|
||||
materialReceiveFormRef.value.validateField(`itemList.${index}.quantity`, () => { });
|
||||
materialReceiveFormRef.value.validateField(`itemList.${index}.acceptedQuantity`, () => { });
|
||||
materialReceiveFormRef.value.validateField(`itemList.${index}.shortageQuantity`, () => { });
|
||||
materialReceiveFormRef.value.validateField(`itemList.${index}.quantity`, () => {});
|
||||
materialReceiveFormRef.value.validateField(`itemList.${index}.acceptedQuantity`, () => {});
|
||||
materialReceiveFormRef.value.validateField(`itemList.${index}.shortageQuantity`, () => {});
|
||||
}
|
||||
});
|
||||
|
||||
@ -619,9 +639,9 @@ const handleAcceptedInput = (index: number) => {
|
||||
// 手动验证数量和验收数量字段
|
||||
const validateQuantityField = (index: number) => {
|
||||
if (materialReceiveFormRef.value) {
|
||||
materialReceiveFormRef.value.validateField(`itemList.${index}.quantity`, () => { });
|
||||
materialReceiveFormRef.value.validateField(`itemList.${index}.acceptedQuantity`, () => { });
|
||||
materialReceiveFormRef.value.validateField(`itemList.${index}.shortageQuantity`, () => { });
|
||||
materialReceiveFormRef.value.validateField(`itemList.${index}.quantity`, () => {});
|
||||
materialReceiveFormRef.value.validateField(`itemList.${index}.acceptedQuantity`, () => {});
|
||||
materialReceiveFormRef.value.validateField(`itemList.${index}.shortageQuantity`, () => {});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -1,76 +1,101 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> </transition>
|
||||
<el-card shadow="never">
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<el-form-item label="使用部位" prop="usePart">
|
||||
<el-input v-model="queryParams.usePart" placeholder="请输入使用部位" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
</template>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="4">
|
||||
<el-card shadow="never">
|
||||
<el-tree style="max-width: 600px" :data="TreeData" :props="defaultProps" @node-click="handleNodeClick" />
|
||||
</el-card>
|
||||
</el-col>
|
||||
|
||||
<!-- 外层表格:添加ref用于控制展开状态 -->
|
||||
<el-table ref="outerTableRef" v-loading="loading" :data="materialsUseInventoryList" @expand-change="handleExpandChange" border>
|
||||
<el-table-column type="expand">
|
||||
<template #default="props">
|
||||
<div style="margin-left: 60px">
|
||||
<el-table :data="materialsUseRecordList" border v-loading="loadingChild">
|
||||
<el-table-column label="序号" align="center" type="index" width="60" />
|
||||
<el-table-column label="使用数量" align="center" prop="useNumber" />
|
||||
<el-table-column label="剩余量" align="center" prop="residueNumber" />
|
||||
<el-table-column label="使用部位" align="center" prop="usePart" />
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
icon="delete"
|
||||
v-if="scope.row.ishow"
|
||||
@click="handleDelete(scope.row)"
|
||||
v-hasPermi="['materials:materialsUseRecord:remove']"
|
||||
>删除</el-button
|
||||
>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination
|
||||
v-show="totalChild > 0"
|
||||
:total="totalChild"
|
||||
v-model:page="queryParamsChild.pageNum"
|
||||
v-model:limit="queryParamsChild.pageSize"
|
||||
@pagination="getListChild"
|
||||
/>
|
||||
</div>
|
||||
<el-col :span="20">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> </transition>
|
||||
<el-card shadow="never">
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<el-form-item label="物资名称" prop="materialsName">
|
||||
<el-input v-model="queryParams.materialsName" placeholder="请输入物资名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="序号" align="center" type="index" width="60" />
|
||||
<el-table-column label="物资名称" align="center" prop="materialsName" />
|
||||
<el-table-column label="交接单位" align="center" prop="recipient" />
|
||||
<el-table-column label="计划数量" align="center" prop="quantityCount" />
|
||||
<el-table-column label="数量" align="center" prop="number" />
|
||||
<el-table-column label="出库人" align="center" prop="operator" />
|
||||
<el-table-column label="领用人" align="center" prop="shipper" />
|
||||
<el-table-column label="剩余量" align="center" prop="residue" />
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" icon="Plus" @click="handleAdd(scope.row)" v-hasPermi="['materials:materialsUseRecord:add']"
|
||||
>添加登记</el-button
|
||||
>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
|
||||
</el-card>
|
||||
<!-- 外层表格:使用expandedRowKeys控制展开状态 -->
|
||||
<el-table
|
||||
ref="outerTableRef"
|
||||
v-loading="loading"
|
||||
:data="materialsUseInventoryList"
|
||||
@expand-change="handleExpandChange"
|
||||
border
|
||||
:expanded-row-keys="expandedRowKeys"
|
||||
>
|
||||
<el-table-column type="expand">
|
||||
<template #default="props">
|
||||
<div style="margin-left: 60px">
|
||||
<!-- 子表格:使用当前行的独立数据 -->
|
||||
<el-table :data="getChildData(props.row.id)" border v-loading="getChildLoading(props.row.id)">
|
||||
<el-table-column label="序号" align="center" type="index" width="60" />
|
||||
<el-table-column label="使用数量" align="center" prop="useNumber" />
|
||||
<el-table-column label="剩余量" align="center" prop="residueNumber" />
|
||||
<el-table-column label="使用部位" align="center" prop="usePart" />
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
icon="delete"
|
||||
v-if="scope.row.ishow"
|
||||
@click="handleDelete(scope.row, props.row.id)"
|
||||
v-hasPermi="['materials:materialsUseRecord:remove']"
|
||||
>删除</el-button
|
||||
>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 子分页:使用当前行的独立分页参数 -->
|
||||
<pagination
|
||||
v-show="getChildTotal(props.row.id) > 0"
|
||||
:total="getChildTotal(props.row.id)"
|
||||
v-model:page="getChildQueryParams(props.row.id).pageNum"
|
||||
v-model:limit="getChildQueryParams(props.row.id).pageSize"
|
||||
@pagination="(page, limit) => handleChildPagination(props.row.id, page, limit)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="序号" align="center" type="index" width="60" />
|
||||
<el-table-column label="物资名称" align="center" prop="materialsName" />
|
||||
<el-table-column label="交接单位" align="center" prop="recipient" />
|
||||
<el-table-column label="计划数量" align="center" prop="quantityCount" />
|
||||
<el-table-column label="数量" align="center" prop="number" />
|
||||
<el-table-column label="出库人" align="center" prop="operator" />
|
||||
<el-table-column label="领用人" align="center" prop="shipper" />
|
||||
<el-table-column label="剩余量" align="center" prop="residue" />
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" icon="Plus" @click="handleAdd(scope.row)" v-hasPermi="['materials:materialsUseRecord:add']"
|
||||
>添加登记</el-button
|
||||
>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 添加或修改材料使用登记对话框 -->
|
||||
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
|
||||
<el-form ref="materialsUseRecordFormRef" :model="form" :rules="rules" label-width="80px">
|
||||
@ -104,39 +129,53 @@ import {
|
||||
updateMaterialsUseRecord
|
||||
} from '@/api/materials/materialsUseRecord';
|
||||
import { MaterialsUseRecordVO, MaterialsUseRecordQuery, MaterialsUseRecordForm } from '@/api/materials/materialsUseRecord/types';
|
||||
import { getCurrentInstance, ComponentInternalInstance, onMounted, ref, reactive, toRefs, computed } from 'vue';
|
||||
import { getCurrentInstance, ComponentInternalInstance, onMounted, ref, reactive, toRefs, computed, watch, WatchStopHandle } from 'vue';
|
||||
import { ElFormInstance, ElTable } from 'element-plus';
|
||||
import useUserStore from '@/store/modules/user';
|
||||
import { get } from 'lodash';
|
||||
import { getMaterialsList } from '@/api/materials/materialOutbound';
|
||||
|
||||
// 类型定义补充(若项目中无全局DialogOption类型需添加)
|
||||
// 类型定义补充
|
||||
interface DialogOption {
|
||||
visible: boolean;
|
||||
title: string;
|
||||
}
|
||||
|
||||
// 子列表状态接口:为每个父行存储独立数据
|
||||
interface ChildRowState {
|
||||
list: MaterialsUseRecordVO[];
|
||||
queryParams: MaterialsUseRecordQuery & {
|
||||
pageNum: number;
|
||||
pageSize: number;
|
||||
inventoryId: number;
|
||||
projectId?: number;
|
||||
};
|
||||
loading: boolean;
|
||||
total: number;
|
||||
}
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const userStore = useUserStore();
|
||||
const currentProject = computed(() => userStore.selectedProject);
|
||||
|
||||
// 核心数据响应式定义
|
||||
const materialsUseRecordList = ref<MaterialsUseRecordVO[]>([]);
|
||||
const materialsUseInventoryList = ref<any[]>([]); // 外层列表数据类型可根据实际接口返回调整
|
||||
const materialsUseInventoryList = ref<any[]>([]);
|
||||
const buttonLoading = ref(false);
|
||||
const loading = ref(true);
|
||||
const loadingChild = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref<Array<string | number>>([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
const totalChild = ref(0);
|
||||
const expandedRowKeys = ref<number[]>([]); // 控制展开行的ID集合
|
||||
|
||||
// 子列表状态管理:使用对象存储不同父行的子数据,key为inventoryId
|
||||
const childRowStates = ref<Record<number, ChildRowState>>({});
|
||||
|
||||
// 组件Ref定义
|
||||
const queryFormRef = ref<ElFormInstance>();
|
||||
const materialsUseRecordFormRef = ref<ElFormInstance>();
|
||||
const outerTableRef = ref<InstanceType<typeof ElTable> | undefined>(undefined); // 外层表格Ref(控制展开)
|
||||
const currentExpandInventoryId = ref<number | undefined>(undefined); // 存储当前展开行的inventoryId
|
||||
const outerTableRef = ref<InstanceType<typeof ElTable> | undefined>(undefined);
|
||||
const currentOperateInventoryId = ref<number | undefined>(undefined); // 当前操作的父行ID
|
||||
|
||||
// 对话框与表单数据
|
||||
const dialog = reactive<DialogOption>({
|
||||
@ -160,72 +199,159 @@ const data = reactive({
|
||||
pageSize: 10,
|
||||
projectId: currentProject.value?.id,
|
||||
outPut: 1,
|
||||
usePart: undefined // 补充usePart字段(与表单prop对应)
|
||||
} as MaterialsUseRecordQuery & { outPut?: number; usePart?: string },
|
||||
queryParamsChild: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
inventoryId: undefined,
|
||||
materialsName: undefined,
|
||||
usePart: undefined,
|
||||
useNumber: undefined,
|
||||
residueNumber: undefined
|
||||
} as MaterialsUseRecordQuery,
|
||||
materialsId: ''
|
||||
} as MaterialsUseRecordQuery & {
|
||||
outPut?: number;
|
||||
materialsName?: string;
|
||||
usePart?: string;
|
||||
},
|
||||
rules: {
|
||||
id: [{ required: true, message: '主键ID不能为空', trigger: 'blur' }],
|
||||
projectId: [{ required: true, message: '项目ID不能为空', trigger: 'blur' }],
|
||||
inventoryId: [{ required: true, message: '库存ID不能为空', trigger: 'blur' }],
|
||||
usePart: [{ required: true, message: '使用部位不能为空', trigger: 'blur' }],
|
||||
useNumber: [{ required: true, message: '使用数量不能为空', trigger: 'blur' }],
|
||||
residueNumber: [{ required: true, message: '剩余量不能为空', trigger: 'blur' }]
|
||||
useNumber: [{ required: true, message: '使用数量不能为空', trigger: 'blur' }]
|
||||
}
|
||||
});
|
||||
|
||||
const { queryParams, form, rules, queryParamsChild } = toRefs(data);
|
||||
const { form, rules } = toRefs(data);
|
||||
const queryParams = ref<typeof data.queryParams>({ ...data.queryParams });
|
||||
const TreeData: any = ref([]);
|
||||
//获取材料列表
|
||||
const getMaterialsListData = async () => {
|
||||
const res = await getMaterialsList({ projectId: currentProject.value?.id, pageNum: 1, pageSize: 999 });
|
||||
if (res.code === 200) {
|
||||
// 将数据转换为树形结构
|
||||
TreeData.value = res.rows.map((item) => ({
|
||||
...item,
|
||||
label: item.materialsName + '_' + item.createTime,
|
||||
children: []
|
||||
}));
|
||||
queryParams.value.materialsId = TreeData.value[0].id;
|
||||
getList();
|
||||
}
|
||||
};
|
||||
|
||||
/** 查询材料使用登记列表(外层列表) */
|
||||
const handleNodeClick = (data: any) => {
|
||||
console.log(data);
|
||||
queryParams.value.materialsId = data.id;
|
||||
getList();
|
||||
};
|
||||
|
||||
const defaultProps = {
|
||||
children: 'children',
|
||||
label: 'label'
|
||||
};
|
||||
|
||||
// ------------------------------ 子列表状态工具函数 ------------------------------
|
||||
/** 获取或初始化指定父行的子列表状态 */
|
||||
const getOrInitChildState = (inventoryId: number): ChildRowState => {
|
||||
if (!childRowStates.value[inventoryId]) {
|
||||
childRowStates.value[inventoryId] = {
|
||||
list: [],
|
||||
loading: false,
|
||||
total: 0,
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
inventoryId,
|
||||
projectId: currentProject.value?.id
|
||||
}
|
||||
};
|
||||
}
|
||||
return childRowStates.value[inventoryId];
|
||||
};
|
||||
|
||||
/** 获取指定父行的子列表数据 */
|
||||
const getChildData = (inventoryId: number): MaterialsUseRecordVO[] => {
|
||||
return getOrInitChildState(inventoryId).list;
|
||||
};
|
||||
|
||||
/** 获取指定父行的子列表加载状态 */
|
||||
const getChildLoading = (inventoryId: number): boolean => {
|
||||
return getOrInitChildState(inventoryId).loading;
|
||||
};
|
||||
|
||||
/** 获取指定父行的子列表总条数 */
|
||||
const getChildTotal = (inventoryId: number): number => {
|
||||
return getOrInitChildState(inventoryId).total;
|
||||
};
|
||||
|
||||
/** 获取指定父行的子分页参数 */
|
||||
const getChildQueryParams = (inventoryId: number) => {
|
||||
return getOrInitChildState(inventoryId).queryParams;
|
||||
};
|
||||
|
||||
// ------------------------------ 核心业务逻辑 ------------------------------
|
||||
/** 查询外层列表数据 */
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await listMaterialsUseInventory(queryParams.value);
|
||||
materialsUseInventoryList.value = res.rows;
|
||||
total.value = res.total;
|
||||
// 保持已展开行的状态
|
||||
if (expandedRowKeys.value.length > 0 && outerTableRef.value) {
|
||||
expandedRowKeys.value.forEach((id) => {
|
||||
const targetRow = materialsUseInventoryList.value.find((item) => item.id === id);
|
||||
targetRow && outerTableRef.value!.toggleRowExpansion(targetRow, true);
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
/** 处理外层表格展开/折叠:记录当前展开行的inventoryId */
|
||||
const handleExpandChange = (row: any) => {
|
||||
currentExpandInventoryId.value = row.id; // 记录展开行ID
|
||||
queryParamsChild.value.inventoryId = row.id;
|
||||
getListChild();
|
||||
/** 处理外层表格展开/折叠 */
|
||||
const handleExpandChange = async (row: any, expandedRows: any[]) => {
|
||||
const inventoryId = row.id;
|
||||
// 更新展开行ID集合
|
||||
expandedRowKeys.value = expandedRows.map((item) => item.id);
|
||||
|
||||
// 展开时加载子数据(仅首次加载)
|
||||
const childState = getOrInitChildState(inventoryId);
|
||||
if (expandedRows.some((item) => item.id === inventoryId) && childState.list.length === 0) {
|
||||
await getListChild(inventoryId);
|
||||
}
|
||||
};
|
||||
|
||||
/** 查询材料子级登记列表(内层列表) */
|
||||
const getListChild = async () => {
|
||||
loadingChild.value = true;
|
||||
/** 查询指定父行的子列表数据 */
|
||||
const getListChild = async (inventoryId: number) => {
|
||||
const childState = getOrInitChildState(inventoryId);
|
||||
childState.loading = true;
|
||||
|
||||
try {
|
||||
const res = await listMaterialsUseRecord(queryParamsChild.value);
|
||||
materialsUseRecordList.value = res.rows;
|
||||
const res = await listMaterialsUseRecord(childState.queryParams);
|
||||
childState.list = res.rows;
|
||||
// 控制首行删除按钮显示
|
||||
if (res.rows.length > 0) {
|
||||
materialsUseRecordList.value[0].ishow = true;
|
||||
if (childState.list.length > 0) {
|
||||
childState.list[0].ishow = true;
|
||||
}
|
||||
totalChild.value = res.total;
|
||||
childState.total = res.total;
|
||||
} finally {
|
||||
loadingChild.value = false;
|
||||
childState.loading = false;
|
||||
}
|
||||
};
|
||||
|
||||
/** 处理子列表分页变化 */
|
||||
const handleChildPagination = async (inventoryId: number, page: number, limit: number) => {
|
||||
const childState = getOrInitChildState(inventoryId);
|
||||
childState.queryParams.pageNum = page;
|
||||
childState.queryParams.pageSize = limit;
|
||||
await getListChild(inventoryId);
|
||||
};
|
||||
|
||||
/** 取消按钮 */
|
||||
const cancel = () => {
|
||||
reset();
|
||||
dialog.visible = false;
|
||||
currentOperateInventoryId.value = undefined;
|
||||
};
|
||||
|
||||
/** 表单重置 */
|
||||
const reset = () => {
|
||||
form.value = { ...initFormData };
|
||||
form.value = { ...initFormData, projectId: currentProject.value?.id };
|
||||
materialsUseRecordFormRef.value?.resetFields();
|
||||
};
|
||||
|
||||
@ -238,10 +364,16 @@ const handleQuery = () => {
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value?.resetFields();
|
||||
queryParams.value = {
|
||||
...data.queryParams,
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
projectId: currentProject.value?.id
|
||||
};
|
||||
handleQuery();
|
||||
};
|
||||
|
||||
/** 多选框选中数据(当前模板未使用,保留原逻辑) */
|
||||
/** 多选框选中数据处理 */
|
||||
const handleSelectionChange = (selection: MaterialsUseRecordVO[]) => {
|
||||
ids.value = selection.map((item) => item.id);
|
||||
single.value = selection.length !== 1;
|
||||
@ -252,76 +384,87 @@ const handleSelectionChange = (selection: MaterialsUseRecordVO[]) => {
|
||||
const handleAdd = async (row: any) => {
|
||||
reset();
|
||||
form.value.inventoryId = row.id;
|
||||
currentOperateInventoryId.value = row.id;
|
||||
dialog.visible = true;
|
||||
dialog.title = '添加材料使用登记';
|
||||
};
|
||||
|
||||
/** 修改按钮操作(当前模板未使用,保留原逻辑) */
|
||||
/** 修改按钮操作 */
|
||||
const handleUpdate = async (row?: MaterialsUseRecordVO) => {
|
||||
if (!row && ids.value.length === 0) return;
|
||||
|
||||
reset();
|
||||
const _id = row?.id || ids.value[0];
|
||||
const res = await getMaterialsUseRecord(_id);
|
||||
Object.assign(form.value, res.data);
|
||||
currentOperateInventoryId.value = form.value.inventoryId;
|
||||
dialog.visible = true;
|
||||
dialog.title = '修改材料使用登记';
|
||||
};
|
||||
|
||||
/** 提交按钮:核心优化逻辑(刷新外层列表+保留展开状态) */
|
||||
/** 提交表单操作 */
|
||||
const submitForm = () => {
|
||||
materialsUseRecordFormRef.value?.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
buttonLoading.value = true;
|
||||
try {
|
||||
// 1. 执行添加/修改接口请求
|
||||
await addMaterialsUseRecord(form.value);
|
||||
// 2. 刷新外层列表(确保外层数据同步,如剩余量)
|
||||
await getList();
|
||||
// 3. 刷新当前展开行的子列表
|
||||
await getListChild();
|
||||
// 4. 恢复展开状态:根据记录的inventoryId重新展开行
|
||||
if (currentExpandInventoryId.value && outerTableRef.value) {
|
||||
const targetRow = materialsUseInventoryList.value.find((item) => item.id === currentExpandInventoryId.value);
|
||||
if (targetRow) {
|
||||
outerTableRef.value.toggleRowExpansion(targetRow, true);
|
||||
}
|
||||
}
|
||||
proxy?.$modal.msgSuccess('操作成功');
|
||||
dialog.visible = false;
|
||||
} finally {
|
||||
buttonLoading.value = false;
|
||||
}
|
||||
if (!valid || !currentOperateInventoryId.value) return;
|
||||
|
||||
buttonLoading.value = true;
|
||||
try {
|
||||
// 执行添加或修改操作
|
||||
const apiFn = form.value.id ? updateMaterialsUseRecord : addMaterialsUseRecord;
|
||||
await apiFn(form.value);
|
||||
|
||||
// 刷新外层列表和当前操作行的子列表
|
||||
await getList();
|
||||
await getListChild(currentOperateInventoryId.value);
|
||||
|
||||
proxy?.$modal.msgSuccess('操作成功');
|
||||
dialog.visible = false;
|
||||
} finally {
|
||||
buttonLoading.value = false;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (row?: MaterialsUseRecordVO) => {
|
||||
const _ids = row?.id || ids.value;
|
||||
/** 删除操作 */
|
||||
const handleDelete = async (row: MaterialsUseRecordVO, inventoryId: number) => {
|
||||
try {
|
||||
await proxy?.$modal.confirm(`是否确认删除材料使用登记编号为"${_ids}"的数据项?`);
|
||||
await delMaterialsUseRecord(_ids);
|
||||
await proxy?.$modal.confirm(`是否确认删除该记录?`);
|
||||
await delMaterialsUseRecord(row.id);
|
||||
await getList();
|
||||
await getListChild();
|
||||
if (currentExpandInventoryId.value && outerTableRef.value) {
|
||||
const targetRow = materialsUseInventoryList.value.find((item) => item.id === currentExpandInventoryId.value);
|
||||
if (targetRow) {
|
||||
outerTableRef.value.toggleRowExpansion(targetRow, true);
|
||||
}
|
||||
}
|
||||
await getListChild(inventoryId);
|
||||
proxy?.$modal.msgSuccess('删除成功');
|
||||
await getListChild();
|
||||
} finally {
|
||||
loading.value = false;
|
||||
} catch (error) {
|
||||
// 取消删除时不做处理
|
||||
}
|
||||
};
|
||||
|
||||
/** 导出按钮操作(保留原逻辑) */
|
||||
/** 导出操作 */
|
||||
const handleExport = () => {
|
||||
proxy?.download('materials/materialsUseRecord/export', { ...queryParams.value }, `materialsUseRecord_${new Date().getTime()}.xlsx`);
|
||||
};
|
||||
|
||||
/** 页面挂载时加载外层列表 */
|
||||
// 监听项目ID变化刷新数据
|
||||
const listeningProject: WatchStopHandle = watch(
|
||||
() => currentProject.value?.id,
|
||||
(newId) => {
|
||||
queryParams.value.projectId = newId;
|
||||
form.value.projectId = newId;
|
||||
// 更新所有子列表的项目ID
|
||||
Object.values(childRowStates.value).forEach((state) => {
|
||||
state.queryParams.projectId = newId;
|
||||
});
|
||||
getList();
|
||||
}
|
||||
);
|
||||
|
||||
// 页面卸载时清理监听
|
||||
onUnmounted(() => {
|
||||
listeningProject();
|
||||
});
|
||||
|
||||
// 页面挂载时加载数据
|
||||
onMounted(() => {
|
||||
getList();
|
||||
// getList();
|
||||
getMaterialsListData();
|
||||
});
|
||||
</script>
|
||||
|
||||
619
src/views/materials/overallPlanMaterialSupply/index copy.vue
Normal file
619
src/views/materials/overallPlanMaterialSupply/index copy.vue
Normal file
@ -0,0 +1,619 @@
|
||||
<template>
|
||||
<div class="overall-plan-material-supply">
|
||||
<el-card shadow="always">
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-form :inline="true">
|
||||
<el-form-item v-if="state.masterData.status == 'draft'">
|
||||
<el-button type="primary" icon="edit" @click="clickApprovalSheet1()">审批</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="state.masterData.status == 'waiting' || state.masterData.status == 'finish'">
|
||||
<el-button icon="view" @click="lookApprovalFlow()" type="warning">查看流程</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<file-upload
|
||||
upload-url="/design/totalsupplyplan/import"
|
||||
v-model="file"
|
||||
:limit="1"
|
||||
:file-type="['xls', 'xlsx']"
|
||||
:on-upload-success="handleSuccess"
|
||||
>
|
||||
<el-button :disabled="state.masterData.status == 'finish'" type="primary" plain icon="upload">导入</el-button>
|
||||
</file-upload>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" plain icon="Download" @click="handleExport">导出</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button icon="Finished" @click="editApprovalSheet()" type="success" :disabled="state.masterData.status == 'finish'"
|
||||
>一键全部保存</el-button
|
||||
>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="toggleExpandAll">{{ isExpandAll ? '一键收起' : '一键展开' }}</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<right-toolbar @queryTable="getMasterDataList"></right-toolbar>
|
||||
</el-row>
|
||||
</template>
|
||||
</el-card>
|
||||
|
||||
<!-- 本地数据懒加载表格 -->
|
||||
<el-table
|
||||
:data="state.tableData"
|
||||
v-loading="state.loading.list"
|
||||
ref="tableRef"
|
||||
stripe
|
||||
style="width: 100%; margin-bottom: 20px; height: calc(100vh - 230px)"
|
||||
row-key="id"
|
||||
border
|
||||
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
|
||||
:lazy="true"
|
||||
:load="loadLocalChildNodes"
|
||||
@expand-change="handleExpandChange"
|
||||
>
|
||||
<el-table-column align="center" prop="num" label="编号" />
|
||||
<el-table-column prop="name" label="工程或费用名称" width="180" />
|
||||
<el-table-column prop="unit" label="单位" />
|
||||
<el-table-column prop="specification" label="规格型号" width="80" />
|
||||
<el-table-column prop="quantity" label="数量" width="100" />
|
||||
<el-table-column prop="batchNumber" label="批次号" width="200" />
|
||||
|
||||
<!-- 优化的输入框列 -->
|
||||
<el-table-column prop="brand" label="品牌">
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-model.lazy="row.brand"
|
||||
:disabled="state.masterData.status != 'draft'"
|
||||
v-if="!row.hasChildren"
|
||||
placeholder=""
|
||||
clearable
|
||||
:key="`brand-${row.id}`"
|
||||
@change="handleInputChange(row, 'brand')"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="texture" label="材质">
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-model.lazy="row.texture"
|
||||
:disabled="state.masterData.status != 'draft'"
|
||||
v-if="!row.hasChildren"
|
||||
placeholder=""
|
||||
clearable
|
||||
:key="`texture-${row.id}`"
|
||||
@change="handleInputChange(row, 'texture')"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="qualityStandard" label="质量标准">
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
:disabled="state.masterData.status != 'draft'"
|
||||
v-model.lazy="row.qualityStandard"
|
||||
v-if="!row.hasChildren"
|
||||
placeholder=""
|
||||
clearable
|
||||
:key="`qualityStandard-${row.id}`"
|
||||
@change="handleInputChange(row, 'qualityStandard')"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="partUsed" label="使用部位">
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
:disabled="state.masterData.status != 'draft'"
|
||||
v-model.lazy="row.partUsed"
|
||||
v-if="!row.hasChildren"
|
||||
placeholder=""
|
||||
clearable
|
||||
:key="`partUsed-${row.id}`"
|
||||
@change="handleInputChange(row, 'partUsed')"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="deliveryPoints" label="交货地点">
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
:disabled="state.masterData.status != 'draft'"
|
||||
v-model.lazy="row.deliveryPoints"
|
||||
v-if="!row.hasChildren"
|
||||
placeholder=""
|
||||
clearable
|
||||
:key="`deliveryPoints-${row.id}`"
|
||||
@change="handleInputChange(row, 'deliveryPoints')"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="预计使用日期" width="150">
|
||||
<template #default="{ row }">
|
||||
<el-date-picker
|
||||
v-model="row.dateService"
|
||||
v-if="!row.hasChildren"
|
||||
type="date"
|
||||
value-format="YYYY-MM-DD"
|
||||
:disabled="state.masterData.status != 'draft'"
|
||||
placeholder="请选择日期"
|
||||
:key="`dateService-${row.id}`"
|
||||
@change="handleInputChange(row, 'dateService')"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="remark" label="备注" />
|
||||
</el-table>
|
||||
|
||||
<!-- 编辑弹窗 -->
|
||||
<el-dialog v-model="visible" title="修改物料信息" :width="800" :close-on-click-modal="false" @close="handleClose">
|
||||
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="120px" class="space-y-4">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="批次号" prop="batchNumber">
|
||||
<el-input disabled v-model="formData.batchNumber" placeholder="请输入批次号" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="品牌" prop="brand">
|
||||
<el-input v-model="formData.brand" placeholder="请输入品牌" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="材质" prop="texture">
|
||||
<el-input v-model="formData.texture" placeholder="请输入材质" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="质量标准" prop="qualityStandard">
|
||||
<el-input v-model="formData.qualityStandard" placeholder="请输入质量标准" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="使用部位" prop="partUsed">
|
||||
<el-input v-model="formData.partUsed" placeholder="请输入使用部位" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="交货地点" prop="deliveryPoints">
|
||||
<el-input v-model="formData.deliveryPoints" placeholder="请输入交货地点" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="预计使用日期" prop="dateService">
|
||||
<el-date-picker v-model="formData.dateService" type="date" placeholder="选择预计使用日期" format="YYYY-MM-DD" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="formData.remark" placeholder="请输入备注信息" type="textarea" rows="3" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="handleClose">取消</el-button>
|
||||
<el-button type="primary" @click="handleSubmit">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="billofQuantities">
|
||||
import { ref, reactive, onMounted, computed, getCurrentInstance, watch, onUnmounted } from 'vue';
|
||||
import { ElMessage, ElLoading } from 'element-plus';
|
||||
import {
|
||||
obtainMasterDataList,
|
||||
totalsupplyplan,
|
||||
totalSupplyplanDetails,
|
||||
materialChangeSupplyplan,
|
||||
totalSupplyplanBatchEdit
|
||||
} from '@/api/materials/overallPlanMaterialSupply/index';
|
||||
import { useUserStoreHook } from '@/store/modules/user';
|
||||
|
||||
// 全局状态与实例获取
|
||||
const userStore = useUserStoreHook();
|
||||
const currentProject = computed(() => userStore.selectedProject);
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
// 基础状态管理
|
||||
const visible = ref(false);
|
||||
const formRef = ref(null);
|
||||
const tableRef = ref(null);
|
||||
const file = ref(null);
|
||||
const isExpandAll = ref(false);
|
||||
const expandRowKeys = ref([]);
|
||||
const editDataMap = ref(new Map()); // 用Map存储修改的数据,提高查询效率
|
||||
const loadingInstance = ref(null);
|
||||
const fullTreeData = ref([]); // 存储完整树形数据,用于本地懒加载
|
||||
|
||||
// 页面核心状态
|
||||
const state = reactive({
|
||||
tableData: [], // 初始只存放根节点
|
||||
queryForm: {
|
||||
projectId: currentProject.value?.id,
|
||||
versions: '',
|
||||
sheet: '',
|
||||
pageSize: 20,
|
||||
pageNum: 1
|
||||
},
|
||||
loading: {
|
||||
versions: false,
|
||||
sheets: false,
|
||||
list: false
|
||||
},
|
||||
masterData: {}
|
||||
});
|
||||
|
||||
// 表单数据
|
||||
const formData = reactive({
|
||||
batchNumber: '',
|
||||
brand: '',
|
||||
compileDate: '',
|
||||
dateService: '',
|
||||
deliveryPoints: '',
|
||||
id: undefined,
|
||||
name: '',
|
||||
num: '',
|
||||
partUsed: '',
|
||||
planNumber: '',
|
||||
projectId: undefined,
|
||||
qualityStandard: '',
|
||||
quantity: 0,
|
||||
remark: '',
|
||||
specification: '',
|
||||
status: '',
|
||||
texture: '',
|
||||
unit: ''
|
||||
});
|
||||
|
||||
// 表单验证规则
|
||||
const formRules = reactive({
|
||||
name: [
|
||||
{ required: true, message: '请输入名称', trigger: 'blur' },
|
||||
{ max: 100, message: '名称长度不能超过100个字符', trigger: 'blur' }
|
||||
],
|
||||
num: [
|
||||
{ required: true, message: '请输入编号', trigger: 'blur' },
|
||||
{ max: 50, message: '编号长度不能超过50个字符', trigger: 'blur' }
|
||||
],
|
||||
quantity: [
|
||||
{ required: true, message: '请输入数量', trigger: 'blur' },
|
||||
{ type: 'number', min: 0, message: '数量不能为负数', trigger: 'blur' }
|
||||
],
|
||||
compileDate: [{ required: true, message: '请选择编制日期', trigger: 'change' }]
|
||||
});
|
||||
|
||||
/**
|
||||
* 本地数据懒加载实现:从完整数据中筛选子节点
|
||||
*/
|
||||
const loadLocalChildNodes = (row, treeNode, resolve) => {
|
||||
// 避免重复加载
|
||||
if (row.children && row.children.length > 0) {
|
||||
resolve(row.children);
|
||||
return;
|
||||
}
|
||||
|
||||
// 显示加载状态
|
||||
loadingInstance.value = ElLoading.service({
|
||||
target: tableRef.value.$el,
|
||||
text: '加载子节点中...',
|
||||
background: 'rgba(255, 255, 255, 0.8)'
|
||||
});
|
||||
|
||||
try {
|
||||
// 从完整数据中筛选当前行的子节点(假设父ID对应子节点的pid)
|
||||
const childNodes = fullTreeData.value.filter((node) => node.pid === row.id);
|
||||
|
||||
// 标记子节点是否有子节点
|
||||
const formattedChildren = childNodes.map((node) => ({
|
||||
...node,
|
||||
hasChildren: fullTreeData.value.some((child) => child.pid === node.id)
|
||||
}));
|
||||
|
||||
// 缓存子节点到当前行
|
||||
row.children = formattedChildren;
|
||||
resolve(formattedChildren);
|
||||
} catch (error) {
|
||||
ElMessage.error(`加载子节点异常:${error.message}`);
|
||||
resolve([]);
|
||||
} finally {
|
||||
loadingInstance.value?.close();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 处理展开状态变化
|
||||
*/
|
||||
const handleExpandChange = (row, expanded) => {
|
||||
if (expanded) {
|
||||
if (!expandRowKeys.value.includes(row.id)) {
|
||||
expandRowKeys.value.push(row.id);
|
||||
}
|
||||
} else {
|
||||
expandRowKeys.value = expandRowKeys.value.filter((id) => id !== row.id);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 一键展开/收起
|
||||
*/
|
||||
const toggleExpandAll = async () => {
|
||||
isExpandAll.value = !isExpandAll.value;
|
||||
|
||||
if (isExpandAll.value) {
|
||||
loadingInstance.value = ElLoading.service({
|
||||
target: tableRef.value.$el,
|
||||
text: '正在加载所有节点...',
|
||||
background: 'rgba(255, 255, 255, 0.8)'
|
||||
});
|
||||
|
||||
try {
|
||||
// 递归加载所有层级子节点
|
||||
const loadAllChildren = (nodes) => {
|
||||
nodes.forEach((node) => {
|
||||
const children = fullTreeData.value.filter((child) => child.sid === node.id);
|
||||
const formattedChildren = children.map((child) => ({
|
||||
...child,
|
||||
hasChildren: fullTreeData.value.some((c) => c.sid === child.id)
|
||||
}));
|
||||
node.children = formattedChildren;
|
||||
if (formattedChildren.length > 0) {
|
||||
loadAllChildren(formattedChildren);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
loadAllChildren(state.tableData);
|
||||
// 展开所有节点
|
||||
state.tableData.forEach((row) => {
|
||||
tableRef.value.toggleRowExpansion(row, true);
|
||||
});
|
||||
ElMessage.success('所有节点已展开');
|
||||
} catch (error) {
|
||||
ElMessage.error(`一键展开失败:${error.message}`);
|
||||
} finally {
|
||||
loadingInstance.value?.close();
|
||||
}
|
||||
} else {
|
||||
// 收起所有节点
|
||||
state.tableData.forEach((row) => {
|
||||
tableRef.value.toggleRowExpansion(row, false);
|
||||
});
|
||||
expandRowKeys.value = [];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 处理输入框变化(优化性能)
|
||||
*/
|
||||
const handleInputChange = (row, field) => {
|
||||
// 只记录修改过的数据,避免全量对比
|
||||
if (!editDataMap.value.has(row.id)) {
|
||||
editDataMap.value.set(row.id, { ...row });
|
||||
}
|
||||
const storedData = editDataMap.value.get(row.id);
|
||||
storedData[field] = row[field];
|
||||
editDataMap.value.set(row.id, storedData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取主数据列表(核心优化:拆分根节点和完整数据)
|
||||
*/
|
||||
async function getMasterDataList() {
|
||||
try {
|
||||
state.loading.list = true;
|
||||
// 获取主数据
|
||||
const masterDataRes = await obtainMasterDataList({
|
||||
projectId: currentProject.value?.id
|
||||
});
|
||||
const { data: masterData } = masterDataRes;
|
||||
|
||||
if (!masterData[0]?.id) {
|
||||
state.tableData = [];
|
||||
fullTreeData.value = [];
|
||||
return;
|
||||
}
|
||||
|
||||
state.masterData = masterData[0];
|
||||
|
||||
// 获取完整供应计划数据
|
||||
const supplyPlanRes = await totalsupplyplan({ id: masterData[0].id });
|
||||
const allData = supplyPlanRes.rows || [];
|
||||
|
||||
// 存储完整数据到本地
|
||||
fullTreeData.value = [...allData];
|
||||
|
||||
// 初始只加载根节点(pid为空或0的节点)
|
||||
const rootNodes = allData.filter((item) => !item.pid || item.pid === 0);
|
||||
|
||||
// 标记根节点是否有子节点
|
||||
state.tableData = rootNodes.map((node) => ({
|
||||
...node,
|
||||
hasChildren: allData.some((child) => child.pid === node.id)
|
||||
}));
|
||||
|
||||
// 初始化可编辑数据映射
|
||||
editDataMap.value.clear();
|
||||
allData.forEach((item) => {
|
||||
if (!item.hasChildren) {
|
||||
// 只关注叶子节点
|
||||
editDataMap.value.set(item.id, { ...item });
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
ElMessage.error(`数据加载失败:${error.message}`);
|
||||
state.tableData = [];
|
||||
fullTreeData.value = [];
|
||||
} finally {
|
||||
state.loading.list = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取详情
|
||||
*/
|
||||
async function totalSupplyplanDetail(id) {
|
||||
try {
|
||||
const result = await totalSupplyplanDetails(id);
|
||||
if (result?.code === 200) {
|
||||
const detailData = result.data || {};
|
||||
|
||||
// 清空表单并赋值
|
||||
Object.keys(formData).forEach((key) => {
|
||||
formData[key] = undefined;
|
||||
});
|
||||
|
||||
// 处理日期格式
|
||||
const formatDate = (date) => {
|
||||
if (!date) return '';
|
||||
const d = typeof date === 'string' ? new Date(date) : date;
|
||||
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
|
||||
};
|
||||
|
||||
Object.assign(formData, {
|
||||
...detailData,
|
||||
compileDate: formatDate(detailData.compileDate),
|
||||
dateService: formatDate(detailData.dateService)
|
||||
});
|
||||
} else {
|
||||
ElMessage.error(`获取详情失败: ${result?.msg || '未知错误'}`);
|
||||
}
|
||||
} catch (err) {
|
||||
ElMessage.error(`接口请求失败: ${err.message}`);
|
||||
console.error('详情接口错误:', err);
|
||||
} finally {
|
||||
state.loading.list = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 一键保存修改
|
||||
*/
|
||||
const editApprovalSheet = async () => {
|
||||
state.loading.list = true;
|
||||
try {
|
||||
// 只提交修改过的数据(从Map中获取)
|
||||
const modifiedData = Array.from(editDataMap.value.values());
|
||||
if (modifiedData.length === 0) {
|
||||
ElMessage.info('没有数据需要修改');
|
||||
return;
|
||||
}
|
||||
|
||||
await totalSupplyplanBatchEdit(modifiedData);
|
||||
ElMessage.success('修改成功');
|
||||
getMasterDataList(); // 重新加载数据
|
||||
} catch (error) {
|
||||
ElMessage.error(`保存失败:${error.message}`);
|
||||
} finally {
|
||||
state.loading.list = false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 提交表单
|
||||
*/
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
await formRef.value.validate();
|
||||
await materialChangeSupplyplan(formData);
|
||||
ElMessage.success('修改成功');
|
||||
handleClose();
|
||||
getMasterDataList();
|
||||
} catch (error) {
|
||||
console.error('表单验证失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 导出数据
|
||||
*/
|
||||
const handleExport = async () => {
|
||||
proxy?.download('/design/totalsupplyplan/export', { projectId: currentProject.value?.id }, `物资供应总计划.xlsx`, true);
|
||||
};
|
||||
|
||||
/**
|
||||
* 关闭弹窗
|
||||
*/
|
||||
const handleClose = () => {
|
||||
visible.value = false;
|
||||
formRef.value?.resetFields();
|
||||
Object.keys(formData).forEach((key) => {
|
||||
formData[key] = undefined;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 审批
|
||||
*/
|
||||
function clickApprovalSheet1() {
|
||||
proxy.$tab.closePage(proxy.$route);
|
||||
proxy.$router.push({
|
||||
path: `/approval/overallPlanMaterialSupply/indexEdit`,
|
||||
query: { id: state.masterData.id, type: 'update' }
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 查看流程
|
||||
*/
|
||||
function lookApprovalFlow() {
|
||||
proxy.$router.push({
|
||||
path: `/approval/overallPlanMaterialSupply/indexEdit`,
|
||||
query: { id: state.masterData.id, type: 'view' }
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听项目变化
|
||||
*/
|
||||
const listeningProject = watch(
|
||||
() => currentProject.value?.id,
|
||||
(nid) => {
|
||||
if (nid) getMasterDataList();
|
||||
}
|
||||
);
|
||||
|
||||
// 导入成功处理
|
||||
const handleSuccess = () => {
|
||||
ElMessage.success('导入成功');
|
||||
getMasterDataList();
|
||||
};
|
||||
|
||||
// 生命周期
|
||||
onMounted(() => {
|
||||
if (currentProject.value?.id) {
|
||||
getMasterDataList();
|
||||
}
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
listeningProject();
|
||||
loadingInstance.value?.close();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.overall-plan-material-supply {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.space-y-4 > .el-row {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.space-y-4 > .el-row:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
</style>
|
||||
@ -1,6 +1,5 @@
|
||||
<template>
|
||||
<div class="overall-plan-material-supply">
|
||||
<!-- tabPosition="left" -->
|
||||
<el-card shadow="always">
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
@ -30,19 +29,28 @@
|
||||
>一键全部保存</el-button
|
||||
>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item>
|
||||
<el-button type="primary" @click="toggleExpandAll">{{ isExpandAll ? '一键收起' : '一键展开' }}</el-button>
|
||||
</el-form-item> -->
|
||||
</el-form>
|
||||
<right-toolbar @queryTable="getMasterDataList"></right-toolbar>
|
||||
</el-row>
|
||||
</template>
|
||||
</el-card>
|
||||
|
||||
<!-- 本地数据懒加载表格 -->
|
||||
<el-table
|
||||
:data="state.tableData"
|
||||
v-loading="state.loading.list"
|
||||
ref="tableRef"
|
||||
stripe
|
||||
style="width: 100%; margin-bottom: 20px; height: calc(100vh - 230px)"
|
||||
row-key="id"
|
||||
border
|
||||
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
|
||||
:lazy="true"
|
||||
:load="loadLocalChildNodes"
|
||||
@expand-change="handleExpandChange"
|
||||
>
|
||||
<el-table-column align="center" prop="num" label="编号" />
|
||||
<el-table-column prop="name" label="工程或费用名称" width="180" />
|
||||
@ -50,87 +58,95 @@
|
||||
<el-table-column prop="specification" label="规格型号" width="80" />
|
||||
<el-table-column prop="quantity" label="数量" width="100" />
|
||||
<el-table-column prop="batchNumber" label="批次号" width="200" />
|
||||
|
||||
<!-- 优化的输入框列 -->
|
||||
<el-table-column prop="brand" label="品牌">
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-model="row.brand"
|
||||
v-model.lazy="row.brand"
|
||||
:disabled="state.masterData.status != 'draft'"
|
||||
v-if="!(row.children && row.children.length > 0)"
|
||||
v-if="!row.hasChildren"
|
||||
placeholder=""
|
||||
clearable
|
||||
:key="`brand-${row.id}`"
|
||||
@change="handleInputChange(row, 'brand')"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="texture" label="材质">
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-model="row.texture"
|
||||
v-model.lazy="row.texture"
|
||||
:disabled="state.masterData.status != 'draft'"
|
||||
v-if="!(row.children && row.children.length > 0)"
|
||||
v-if="!row.hasChildren"
|
||||
placeholder=""
|
||||
clearable
|
||||
:key="`texture-${row.id}`"
|
||||
@change="handleInputChange(row, 'texture')"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="qualityStandard" label="质量标准">
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
:disabled="state.masterData.status != 'draft'"
|
||||
v-model="row.qualityStandard"
|
||||
v-if="!(row.children && row.children.length > 0)"
|
||||
v-model.lazy="row.qualityStandard"
|
||||
v-if="!row.hasChildren"
|
||||
placeholder=""
|
||||
clearable
|
||||
:key="`qualityStandard-${row.id}`"
|
||||
@change="handleInputChange(row, 'qualityStandard')"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="partUsed" label="使用部位">
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
:disabled="state.masterData.status != 'draft'"
|
||||
v-model="row.partUsed"
|
||||
v-if="!(row.children && row.children.length > 0)"
|
||||
v-model.lazy="row.partUsed"
|
||||
v-if="!row.hasChildren"
|
||||
placeholder=""
|
||||
clearable
|
||||
:key="`partUsed-${row.id}`"
|
||||
@change="handleInputChange(row, 'partUsed')"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="deliveryPoints" label="交货地点">
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
:disabled="state.masterData.status != 'draft'"
|
||||
v-model="row.deliveryPoints"
|
||||
v-if="!(row.children && row.children.length > 0)"
|
||||
v-model.lazy="row.deliveryPoints"
|
||||
v-if="!row.hasChildren"
|
||||
placeholder=""
|
||||
clearable
|
||||
:key="`deliveryPoints-${row.id}`"
|
||||
@change="handleInputChange(row, 'deliveryPoints')"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="预计使用日期" width="150">
|
||||
<template #default="{ row }">
|
||||
<el-date-picker
|
||||
v-model="row.dateService"
|
||||
v-if="!(row.children && row.children.length > 0)"
|
||||
v-if="!row.hasChildren"
|
||||
type="date"
|
||||
value-format="YYYY-MM-DD"
|
||||
:disabled="state.masterData.status != 'draft'"
|
||||
placeholder="请选择日期"
|
||||
:key="`dateService-${row.id}`"
|
||||
@change="handleInputChange(row, 'dateService')"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="remark" label="备注" />
|
||||
<!-- <el-table-column label="操作">
|
||||
<template #default="{ row }">
|
||||
<el-button
|
||||
:disabled="state.masterData.status == 'waiting' || state.masterData.status == 'finish'"
|
||||
type="primary"
|
||||
v-hasPermi="['design:totalsupplyplan:edit']"
|
||||
@click="editApprovalSheet(row)"
|
||||
>修改</el-button
|
||||
>
|
||||
</template>
|
||||
</el-table-column> -->
|
||||
</el-table>
|
||||
<!-- 编辑 -->
|
||||
<!-- 编辑弹窗 -->
|
||||
<el-dialog v-model="visible" title="修改物料信息" :width="800" :close-on-click-modal="false" @close="handleClose">
|
||||
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="120px" class="space-y-4">
|
||||
<el-row :gutter="20">
|
||||
@ -145,7 +161,6 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!-- 物料属性区域 -->
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="材质" prop="texture">
|
||||
@ -158,7 +173,6 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!-- 日期与状态区域 -->
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="使用部位" prop="partUsed">
|
||||
@ -171,7 +185,6 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!-- 其他信息区域 -->
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="预计使用日期" prop="dateService">
|
||||
@ -196,7 +209,8 @@
|
||||
</template>
|
||||
|
||||
<script setup name="billofQuantities">
|
||||
import { ref, reactive, onMounted, computed, getCurrentInstance } from 'vue';
|
||||
import { ref, reactive, onMounted, computed, getCurrentInstance, watch, onUnmounted } from 'vue';
|
||||
import { ElMessage, ElLoading } from 'element-plus';
|
||||
import {
|
||||
obtainMasterDataList,
|
||||
totalsupplyplan,
|
||||
@ -205,13 +219,26 @@ import {
|
||||
totalSupplyplanBatchEdit
|
||||
} from '@/api/materials/overallPlanMaterialSupply/index';
|
||||
import { useUserStoreHook } from '@/store/modules/user';
|
||||
|
||||
// 全局状态与实例获取
|
||||
const userStore = useUserStoreHook();
|
||||
const currentProject = computed(() => userStore.selectedProject);
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
// 基础状态管理
|
||||
const visible = ref(false);
|
||||
const formRef = ref(null);
|
||||
const tableRef = ref(null);
|
||||
const file = ref(null);
|
||||
const isExpandAll = ref(false);
|
||||
const expandRowKeys = ref([]);
|
||||
const editDataMap = ref(new Map()); // 用Map存储修改的数据,提高查询效率
|
||||
const loadingInstance = ref(null);
|
||||
const fullTreeData = ref([]); // 存储完整树形数据,用于本地懒加载
|
||||
|
||||
// 页面核心状态
|
||||
const state = reactive({
|
||||
tableData: [],
|
||||
tableData: [], // 初始只存放根节点
|
||||
queryForm: {
|
||||
projectId: currentProject.value?.id,
|
||||
versions: '',
|
||||
@ -224,9 +251,9 @@ const state = reactive({
|
||||
sheets: false,
|
||||
list: false
|
||||
},
|
||||
// 主id
|
||||
masterData: {}
|
||||
});
|
||||
|
||||
// 表单数据
|
||||
const formData = reactive({
|
||||
batchNumber: '',
|
||||
@ -248,6 +275,7 @@ const formData = reactive({
|
||||
texture: '',
|
||||
unit: ''
|
||||
});
|
||||
|
||||
// 表单验证规则
|
||||
const formRules = reactive({
|
||||
name: [
|
||||
@ -264,71 +292,190 @@ const formRules = reactive({
|
||||
],
|
||||
compileDate: [{ required: true, message: '请选择编制日期', trigger: 'change' }]
|
||||
});
|
||||
// 获取主表数据
|
||||
|
||||
/**
|
||||
* 本地数据懒加载实现:从完整数据中筛选子节点
|
||||
*/
|
||||
const loadLocalChildNodes = (row, treeNode, resolve) => {
|
||||
// 避免重复加载
|
||||
if (row.children && row.children.length > 0) {
|
||||
resolve(row.children);
|
||||
return;
|
||||
}
|
||||
|
||||
// 显示加载状态
|
||||
loadingInstance.value = ElLoading.service({
|
||||
target: tableRef.value.$el,
|
||||
text: '加载中...',
|
||||
background: 'rgba(255, 255, 255, 0.8)'
|
||||
});
|
||||
|
||||
try {
|
||||
// 从完整数据中筛选当前行的子节点(假设父ID对应子节点的pid)
|
||||
const childNodes = fullTreeData.value.filter((node) => node.pid === row.sid);
|
||||
|
||||
// 标记子节点是否有子节点
|
||||
const formattedChildren = childNodes.map((node) => ({
|
||||
...node,
|
||||
hasChildren: fullTreeData.value.some((child) => child.pid === node.sid)
|
||||
}));
|
||||
|
||||
// 缓存子节点到当前行
|
||||
row.children = formattedChildren;
|
||||
resolve(formattedChildren);
|
||||
} catch (error) {
|
||||
resolve([]);
|
||||
} finally {
|
||||
loadingInstance.value?.close();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 处理展开状态变化
|
||||
*/
|
||||
const handleExpandChange = (row, expanded) => {
|
||||
if (expanded) {
|
||||
if (!expandRowKeys.value.includes(row.id)) {
|
||||
expandRowKeys.value.push(row.id);
|
||||
}
|
||||
} else {
|
||||
expandRowKeys.value = expandRowKeys.value.filter((id) => id !== row.id);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 一键展开/收起
|
||||
*/
|
||||
const toggleExpandAll = async () => {
|
||||
isExpandAll.value = !isExpandAll.value;
|
||||
|
||||
if (isExpandAll.value) {
|
||||
loadingInstance.value = ElLoading.service({
|
||||
target: tableRef.value.$el,
|
||||
text: '正在加载...',
|
||||
background: 'rgba(255, 255, 255, 0.8)'
|
||||
});
|
||||
|
||||
try {
|
||||
// 递归加载所有层级子节点
|
||||
const loadAllChildren = (nodes) => {
|
||||
nodes.forEach((node) => {
|
||||
const children = fullTreeData.value.filter((child) => child.pid === node.sid);
|
||||
const formattedChildren = children.map((child) => ({
|
||||
...child,
|
||||
hasChildren: fullTreeData.value.some((c) => c.pid === child.sid)
|
||||
}));
|
||||
node.children = formattedChildren;
|
||||
if (formattedChildren.length > 0) {
|
||||
loadAllChildren(formattedChildren);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
loadAllChildren(state.tableData);
|
||||
// 展开所有节点
|
||||
state.tableData.forEach((row) => {
|
||||
tableRef.value.toggleRowExpansion(row, true);
|
||||
});
|
||||
ElMessage.success('所有节点已展开');
|
||||
} catch (error) {
|
||||
ElMessage.error(`一键展开失败:${error.message}`);
|
||||
} finally {
|
||||
loadingInstance.value?.close();
|
||||
}
|
||||
} else {
|
||||
// 收起所有节点
|
||||
state.tableData.forEach((row) => {
|
||||
tableRef.value.toggleRowExpansion(row, false);
|
||||
});
|
||||
expandRowKeys.value = [];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 处理输入框变化(优化性能)
|
||||
*/
|
||||
const handleInputChange = (row, field) => {
|
||||
// 只记录修改过的数据,避免全量对比
|
||||
if (!editDataMap.value.has(row.id)) {
|
||||
editDataMap.value.set(row.id, { ...row });
|
||||
}
|
||||
const storedData = editDataMap.value.get(row.id);
|
||||
storedData[field] = row[field];
|
||||
editDataMap.value.set(row.id, storedData);
|
||||
};
|
||||
/**
|
||||
* 获取主数据列表(核心优化:拆分根节点和完整数据)
|
||||
*/
|
||||
async function getMasterDataList() {
|
||||
try {
|
||||
// 获取主数据列表
|
||||
state.loading.list = true;
|
||||
// 获取主数据
|
||||
const masterDataRes = await obtainMasterDataList({
|
||||
projectId: currentProject.value?.id
|
||||
});
|
||||
|
||||
const { data: masterData } = masterDataRes;
|
||||
console.log('masterData', masterData);
|
||||
|
||||
if (!masterData[0].id) {
|
||||
console.warn('未获取到有效的主数据ID');
|
||||
if (!masterData[0]?.id) {
|
||||
state.tableData = [];
|
||||
fullTreeData.value = [];
|
||||
return;
|
||||
}
|
||||
state.masterData = masterData[0];
|
||||
|
||||
// 获取供应计划
|
||||
const supplyPlanRes = await totalsupplyplan({ id: masterData[0].id });
|
||||
|
||||
// 处理结果
|
||||
if (supplyPlanRes.list == null) {
|
||||
// state.tableData = supplyPlanRes.rows || [];
|
||||
state.tableData = proxy?.handleTree(supplyPlanRes.rows, 'sid', 'pid');
|
||||
console.log('state.tableData', state.tableData);
|
||||
} else {
|
||||
// 根据实际业务逻辑处理有list的情况
|
||||
state.tableData = [];
|
||||
}
|
||||
// 获取完整供应计划数据
|
||||
const supplyPlanRes = await totalsupplyplan({ id: masterData[0].id, projectId: currentProject.value?.id });
|
||||
const allData = supplyPlanRes.rows || [];
|
||||
// 存储完整数据到本地
|
||||
fullTreeData.value = [...allData];
|
||||
// 初始只加载根节点(pid为空或0的节点)
|
||||
const rootNodes = allData.filter((item) => !item.pid || item.pid == 0);
|
||||
// 标记根节点是否有子节点
|
||||
state.tableData = rootNodes.map((node) => ({
|
||||
...node,
|
||||
hasChildren: allData.some((child) => child.pid === node.sid)
|
||||
}));
|
||||
// 初始化可编辑数据映射
|
||||
editDataMap.value.clear();
|
||||
allData.forEach((item) => {
|
||||
if (!item.hasChildren) {
|
||||
// 只关注叶子节点
|
||||
editDataMap.value.set(item.id, { ...item });
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('获取主数据列表失败:', error);
|
||||
// 错误情况下给默认值,避免页面出错
|
||||
state.tableData = [];
|
||||
fullTreeData.value = [];
|
||||
} finally {
|
||||
state.loading.list = false;
|
||||
}
|
||||
}
|
||||
// 获取详情
|
||||
// 修改获取详情的方法
|
||||
|
||||
/**
|
||||
* 获取详情
|
||||
*/
|
||||
async function totalSupplyplanDetail(id) {
|
||||
try {
|
||||
const result = await totalSupplyplanDetails(id);
|
||||
if (result?.code === 200) {
|
||||
const detailData = result.data || {};
|
||||
// 1. 清空原有表单数据
|
||||
|
||||
// 清空表单并赋值
|
||||
Object.keys(formData).forEach((key) => {
|
||||
formData[key] = undefined;
|
||||
});
|
||||
// 2. 处理日期格式(假设接口返回的是Date对象或ISO字符串)
|
||||
|
||||
// 处理日期格式
|
||||
const formatDate = (date) => {
|
||||
if (!date) return '';
|
||||
// 若为字符串,先转为Date对象
|
||||
const d = typeof date === 'string' ? new Date(date) : date;
|
||||
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
|
||||
};
|
||||
// 3. 合并数据到formData(响应式赋值)
|
||||
|
||||
Object.assign(formData, {
|
||||
...detailData,
|
||||
// 单独处理日期字段,转为表单可识别的字符串格式
|
||||
compileDate: formatDate(detailData.compileDate),
|
||||
dateService: formatDate(detailData.dateService)
|
||||
});
|
||||
console.log('表单数据已更新:', formData);
|
||||
} else {
|
||||
ElMessage.error(`获取详情失败: ${result?.msg || '未知错误'}`);
|
||||
}
|
||||
@ -339,87 +486,113 @@ async function totalSupplyplanDetail(id) {
|
||||
state.loading.list = false;
|
||||
}
|
||||
}
|
||||
// 修改
|
||||
|
||||
/**
|
||||
* 一键保存修改
|
||||
*/
|
||||
const editApprovalSheet = async () => {
|
||||
state.loading.list = true;
|
||||
await totalSupplyplanBatchEdit(state.tableData);
|
||||
proxy.$modal.msgSuccess('修改成功');
|
||||
|
||||
state.loading.list = false;
|
||||
};
|
||||
// 提交表单
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
// 表单验证
|
||||
await formRef.value.validate();
|
||||
// 触发提交事件
|
||||
editMaterialSupply(formData);
|
||||
handleClose();
|
||||
// 只提交修改过的数据(从Map中获取)
|
||||
const modifiedData = Array.from(editDataMap.value.values());
|
||||
if (modifiedData.length === 0) {
|
||||
ElMessage.info('没有数据需要修改');
|
||||
return;
|
||||
}
|
||||
|
||||
await totalSupplyplanBatchEdit(modifiedData);
|
||||
ElMessage.success('修改成功');
|
||||
getMasterDataList(); // 重新加载数据
|
||||
} catch (error) {
|
||||
// 验证失败不提交
|
||||
console.error('表单验证失败:', error);
|
||||
return;
|
||||
ElMessage.error(`保存失败:${error.message}`);
|
||||
} finally {
|
||||
state.loading.list = false;
|
||||
}
|
||||
};
|
||||
// 修改物资
|
||||
function editMaterialSupply(formData) {
|
||||
materialChangeSupplyplan(formData).then((res) => {
|
||||
if (res.code === 200) {
|
||||
ElMessage.success('修改成功');
|
||||
getMasterDataList();
|
||||
} else {
|
||||
ElMessage.error('修改失败');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const handleExport = async () => {
|
||||
proxy?.download(
|
||||
'/design/totalsupplyplan/export',
|
||||
{
|
||||
projectId: currentProject.value?.id
|
||||
},
|
||||
`物资供应总计划.xlsx`,
|
||||
true
|
||||
);
|
||||
/**
|
||||
* 提交表单
|
||||
*/
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
await formRef.value.validate();
|
||||
await materialChangeSupplyplan(formData);
|
||||
ElMessage.success('修改成功');
|
||||
handleClose();
|
||||
getMasterDataList();
|
||||
} catch (error) {
|
||||
console.error('表单验证失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 关闭弹窗
|
||||
/**
|
||||
* 导出数据
|
||||
*/
|
||||
const handleExport = async () => {
|
||||
proxy?.download('/design/totalsupplyplan/export', { projectId: currentProject.value?.id }, `物资供应总计划.xlsx`, true);
|
||||
};
|
||||
|
||||
/**
|
||||
* 关闭弹窗
|
||||
*/
|
||||
const handleClose = () => {
|
||||
visible.value = false;
|
||||
// 清空表单数据
|
||||
formRef.value?.resetFields();
|
||||
Object.keys(formData).forEach((key) => {
|
||||
formData[key] = undefined;
|
||||
});
|
||||
// 重置表单验证状态
|
||||
formRef.value?.resetFields();
|
||||
};
|
||||
|
||||
// 审批
|
||||
/**
|
||||
* 审批
|
||||
*/
|
||||
function clickApprovalSheet1() {
|
||||
proxy.$tab.closePage(proxy.$route);
|
||||
proxy.$router.push({
|
||||
path: `/approval/overallPlanMaterialSupply/indexEdit`,
|
||||
query: {
|
||||
id: state.masterData.id,
|
||||
type: 'update'
|
||||
}
|
||||
query: { id: state.masterData.id, type: 'update' }
|
||||
});
|
||||
}
|
||||
// 审核流程
|
||||
|
||||
/**
|
||||
* 查看流程
|
||||
*/
|
||||
function lookApprovalFlow() {
|
||||
proxy.$router.push({
|
||||
path: `/approval/overallPlanMaterialSupply/indexEdit`,
|
||||
query: {
|
||||
id: state.masterData.id,
|
||||
type: 'view'
|
||||
}
|
||||
query: { id: state.masterData.id, type: 'view' }
|
||||
});
|
||||
}
|
||||
onMounted(() => {
|
||||
|
||||
/**
|
||||
* 监听项目变化
|
||||
*/
|
||||
const listeningProject = watch(
|
||||
() => currentProject.value?.id,
|
||||
(nid) => {
|
||||
if (nid) getMasterDataList();
|
||||
}
|
||||
);
|
||||
|
||||
// 导入成功处理
|
||||
const handleSuccess = () => {
|
||||
ElMessage.success('导入成功');
|
||||
getMasterDataList();
|
||||
};
|
||||
|
||||
// 生命周期
|
||||
onMounted(() => {
|
||||
if (currentProject.value?.id) {
|
||||
getMasterDataList();
|
||||
}
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
listeningProject();
|
||||
loadingInstance.value?.close();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.overall-plan-material-supply {
|
||||
padding: 20px;
|
||||
|
||||
@ -51,9 +51,10 @@
|
||||
size="small"
|
||||
icon="Plus"
|
||||
type="primary"
|
||||
v-if="scope.row.purchaseSubmission == '0'"
|
||||
link
|
||||
@click="handleAddSon(scope.row)"
|
||||
>添加</el-button
|
||||
>提交</el-button
|
||||
>
|
||||
<el-button type="primary" v-hasPermi="['cailiaoshebei:physicalsupply:edit']" size="small" icon="Edit" link @click="handleEdit(scope.row)"
|
||||
>修改</el-button
|
||||
@ -113,7 +114,7 @@
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="运算周期(天)" prop="executionCycle">
|
||||
<el-input v-model.number="formData.executionCycle" placeholder="请输入运算周期" type="number"></el-input>
|
||||
<el-input v-model.number="formData.executionCycle" min="0" placeholder="请输入运算周期" type="number"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@ -121,12 +122,12 @@
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="安装量" prop="installationQuantity">
|
||||
<el-input v-model="formData.installationQuantity" placeholder="请输入安装量"></el-input>
|
||||
<el-input v-model="formData.installationQuantity" min="0" type="number" placeholder="请输入安装量"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="安装比例" prop="installationRatio">
|
||||
<el-input v-model="formData.installationRatio" placeholder="请输入安装比例" suffix="%"></el-input>
|
||||
<el-input v-model="formData.installationRatio" min="0" type="number" placeholder="请输入安装比例" suffix="%"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@ -296,22 +297,7 @@ const handleSearch = () => {
|
||||
// 刷新数据
|
||||
const refreshData = () => {
|
||||
fetchData();
|
||||
ElMessage.success('数据已刷新');
|
||||
};
|
||||
|
||||
// 分页大小改变
|
||||
const handleSizeChange = (val) => {
|
||||
pageSize.value = val;
|
||||
currentPage.value = 1;
|
||||
fetchData();
|
||||
};
|
||||
|
||||
// 当前页改变
|
||||
const handleCurrentChange = (val) => {
|
||||
currentPage.value = val;
|
||||
fetchData();
|
||||
};
|
||||
|
||||
// 新增
|
||||
const handleAdd = () => {
|
||||
dialogType.value = 'add';
|
||||
@ -476,6 +462,17 @@ const jumpRouter = (row) => {
|
||||
}
|
||||
});
|
||||
};
|
||||
//监听项目id刷新数据
|
||||
const listeningProject = watch(
|
||||
() => currentProject.value?.id,
|
||||
(nid, oid) => {
|
||||
fetchData();
|
||||
}
|
||||
);
|
||||
|
||||
onUnmounted(() => {
|
||||
listeningProject();
|
||||
});
|
||||
// 初始化页面
|
||||
onMounted(() => {
|
||||
fetchData();
|
||||
|
||||
@ -30,9 +30,7 @@
|
||||
:row-class-name="tableRowClassName"
|
||||
>
|
||||
<!-- 基础信息列 -->
|
||||
<el-table-column prop="id" label="ID" width="180" align="center"></el-table-column>
|
||||
<el-table-column prop="batch" label="批次" align="center"></el-table-column>
|
||||
<el-table-column prop="physicalsupplyId" label="使用情况ID" width="180" align="center"></el-table-column>
|
||||
<!-- 时间相关列 -->
|
||||
<el-table-column prop="issuanceTime" label="联系单下达时间" min-width="160" align="center">
|
||||
<template #default="scope">
|
||||
|
||||
@ -36,7 +36,6 @@
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<el-table v-loading="loading" :data="monthPlanList">
|
||||
<el-table-column type="index" label="序号" width="55" align="center" />
|
||||
<el-table-column label="计划月份" align="center" prop="planMonth" />
|
||||
@ -82,7 +81,7 @@
|
||||
<el-form-item label="计划产值(元)" prop="planValue">
|
||||
<el-input v-model="form.planValue" placeholder="请输入计划产值" type="number" />
|
||||
</el-form-item>
|
||||
<el-form-item label="计划月份(元)" prop="planMonth">
|
||||
<el-form-item label="计划月份" prop="planMonth">
|
||||
<el-date-picker v-model="form.planMonth" type="month" value-format="YYYY-MM" placeholder="请选择计划月份" />
|
||||
</el-form-item>
|
||||
<el-form-item label="产值类型" prop="valueType">
|
||||
|
||||
@ -175,7 +175,7 @@ const getInfo = () => {
|
||||
form.value = res.data as any;
|
||||
|
||||
console.log('🚀 ~ getInfo ~ form.value:', form.value[0].projectId);
|
||||
|
||||
form.value[0].mid = form.value[0].id;
|
||||
form.value[0].id = form.value[0].projectId + '_' + form.value[0].planMonth;
|
||||
loading.value = false;
|
||||
buttonLoading.value = false;
|
||||
@ -231,13 +231,15 @@ const approvalVerifyOpen = async () => {
|
||||
// 图纸上传成功之后 开始提交
|
||||
const submit = async (status, data) => {
|
||||
form.value = data;
|
||||
console.log(form.value);
|
||||
|
||||
if (status === 'draft') {
|
||||
buttonLoading.value = false;
|
||||
proxy?.$modal.msgSuccess('暂存成功');
|
||||
proxy.$tab.closePage(proxy.$route);
|
||||
proxy.$router.go(-1);
|
||||
} else {
|
||||
const res = await isSubmit(data[1]?.id);
|
||||
const res = await isSubmit(data[0]?.mid);
|
||||
|
||||
if (!res.data) {
|
||||
proxy?.$modal.msgError('三种计划产值必须填写');
|
||||
|
||||
@ -86,13 +86,14 @@ const handleTabChange = (tab) => {
|
||||
activeTab.value = tab;
|
||||
data.queryParams.valueType = '1';
|
||||
// data.queryParams.month = '';
|
||||
getList();
|
||||
};
|
||||
|
||||
/** 查询项目总产值分配列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
|
||||
const res = await listOutTable(queryParams.value);
|
||||
const res = await listOutTable({ ...queryParams.value, type: activeTab.value });
|
||||
valueAllocationList.value = res.rows;
|
||||
total.value = res.total;
|
||||
loading.value = false;
|
||||
|
||||
@ -161,7 +161,7 @@
|
||||
:total="detailTotalWork"
|
||||
v-model:page="detailQueryParamsWork.pageNum"
|
||||
v-model:limit="detailQueryParamsWork.pageSize"
|
||||
@pagination="getPvModuleList"
|
||||
@pagination="getDailyBookList"
|
||||
layout="total, sizes, prev, pager, next"
|
||||
:isSmall="5"
|
||||
/>
|
||||
@ -436,10 +436,10 @@ const handleView = (row: any, obj: any) => {
|
||||
|
||||
// 获取已填日报
|
||||
const getDailyBookList = (doneTime: string) => {
|
||||
detialWordList.value = [];
|
||||
// detialWordList.value = [];
|
||||
getDailyBook({
|
||||
id: formDetail.value.id,
|
||||
...detailQueryParams.value
|
||||
...detailQueryParamsWork.value
|
||||
}).then((res: any) => {
|
||||
if (res.code === 200) {
|
||||
detialWordList.value = res.rows || [];
|
||||
|
||||
@ -298,6 +298,11 @@ const getList = async () => {
|
||||
const res = await getProjectSquare(currentProject.value?.id);
|
||||
if (res.data.length === 0) {
|
||||
proxy?.$modal.msgWarning('当前项目下没有方阵,请先创建方阵');
|
||||
matrixOptions.value = [];
|
||||
queryParams.value.projectId = '';
|
||||
form.value.projectId = '';
|
||||
tabList.value = [];
|
||||
activeTab.value = '';
|
||||
} else {
|
||||
let matrixList = res.data.map((item) => {
|
||||
return {
|
||||
@ -472,6 +477,7 @@ const matrixValue = ref<number | undefined>(matrixOptions.value.length > 0 ? mat
|
||||
const resetMatrix = () => {
|
||||
matrixValue.value = undefined;
|
||||
queryParams.value.matrixId = undefined;
|
||||
queryParams.value.projectId = undefined;
|
||||
matrixOptions.value = [];
|
||||
};
|
||||
|
||||
@ -491,8 +497,6 @@ onMounted(() => {
|
||||
const listeningProject = watch(
|
||||
() => currentProject.value?.id,
|
||||
(nid, oid) => {
|
||||
queryParams.value.projectId = nid;
|
||||
form.value.projectId = nid;
|
||||
resetMatrix();
|
||||
getList();
|
||||
}
|
||||
|
||||
@ -27,9 +27,9 @@
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['land:enterRoad:add']">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<!-- <el-col :span="1.5">
|
||||
<el-button type="success" plain icon="Plus" @click="downloadTemplate" v-hasPermi="['land:enterRoad:import']">模板下载</el-button>
|
||||
</el-col>
|
||||
</el-col> -->
|
||||
<el-col :span="1.5">
|
||||
<el-upload
|
||||
ref="uploadRef"
|
||||
|
||||
@ -36,9 +36,9 @@
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['land:landBlock:add']">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<!-- <el-col :span="1.5">
|
||||
<el-button type="success" plain icon="Plus" @click="downloadTemplate" v-hasPermi="['land:enterRoad:import']">模板下载</el-button>
|
||||
</el-col>
|
||||
</el-col> -->
|
||||
<el-col :span="1.5">
|
||||
<el-upload ref="uploadRef" class="upload-demo" :http-request="handleImport" :show-file-list="false">
|
||||
<template #trigger>
|
||||
@ -78,6 +78,7 @@
|
||||
</el-table>
|
||||
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
|
||||
</el-card>
|
||||
|
||||
<!-- 地块表单弹窗 -->
|
||||
<el-dialog draggable :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
|
||||
<el-form ref="landBlockFormRef" :model="form" :rules="rules" label-width="100px">
|
||||
@ -107,20 +108,22 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<!-- 关联方阵弹窗(核心修改区域) -->
|
||||
|
||||
<!-- 关联方阵弹窗(核心修复区域) -->
|
||||
<el-dialog draggable :title="dialogMatrix.title" v-model="dialogMatrix.visible" width="900px" append-to-body>
|
||||
<el-button type="primary" plain icon="Plus" @click="addUnitBoItem" style="margin-bottom: 15px">添加</el-button>
|
||||
<!-- 方阵表单:绑定unitBoList的索引,实现动态校验 -->
|
||||
<!-- 修复1:表单模型绑定formM(根模型),确保嵌套字段校验生效 -->
|
||||
<el-form ref="landBlockFormMatrixRef" :model="formM" label-width="100px">
|
||||
<el-row v-for="(item, i) of unitBoList" :key="i" class="mb-4">
|
||||
<!-- 方阵选择:必填校验 -->
|
||||
<!-- 修复2:循环formM.unitBoList(而非独立ref),保证数据双向绑定 -->
|
||||
<el-row v-for="(item, i) of formM.unitBoList" :key="i" class="mb-4">
|
||||
<!-- 方阵选择:修复校验规则(移除min:2,改为min:1,适配单层级选择) -->
|
||||
<el-col :span="8">
|
||||
<el-form-item
|
||||
label="方阵"
|
||||
:prop="`unitBoList[${i}].unitProjectId`"
|
||||
:rules="[
|
||||
{ required: true, message: '请选择方阵', trigger: 'change' },
|
||||
{ type: 'array', min: 2, message: '请选择完整的方阵层级', trigger: 'change' }
|
||||
{ type: 'array', min: 1, message: '请选择完整的方阵', trigger: 'change' }
|
||||
]"
|
||||
>
|
||||
<el-cascader
|
||||
@ -133,7 +136,7 @@
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<!-- 所属工区:必填校验 -->
|
||||
<!-- 所属工区:保留原有规则 -->
|
||||
<el-col :span="8">
|
||||
<el-form-item
|
||||
label="所属工区"
|
||||
@ -143,7 +146,7 @@
|
||||
<el-input v-model="item.unitProjectArea" placeholder="请输入所属工区" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<!-- 方阵状态:必填校验 -->
|
||||
<!-- 方阵状态:保留原有规则 -->
|
||||
<el-col :span="6">
|
||||
<el-form-item
|
||||
label="方阵状态"
|
||||
@ -153,8 +156,15 @@
|
||||
<el-input v-model="item.unitProjectStatus" placeholder="请输入方阵状态" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<!-- 删除按钮:禁用逻辑优化(至少保留1项) -->
|
||||
<el-col :span="1" style="margin-left: 15px; display: flex; align-items: flex-end">
|
||||
<el-button style="margin-bottom: 18px" type="danger" icon="Delete" @click="removeUnitBoItem(i)"></el-button>
|
||||
<el-button
|
||||
style="margin-bottom: 18px"
|
||||
type="danger"
|
||||
icon="Delete"
|
||||
@click="removeUnitBoItem(i)"
|
||||
:disabled="formM.unitBoList.length <= 1"
|
||||
></el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
@ -181,21 +191,35 @@ import {
|
||||
} from '@/api/system/landTransfer/landBlock';
|
||||
import { LandBlockVO, LandBlockQuery, LandBlockForm } from '@/api/system/landTransfer/landBlock/types';
|
||||
import { useUserStoreHook } from '@/store/modules/user';
|
||||
import { getCurrentInstance, ComponentInternalInstance, onMounted, onUnmounted, watch } from 'vue';
|
||||
import { ElFormInstance, ElMessage } from 'element-plus';
|
||||
import { getCurrentInstance, ComponentInternalInstance, onMounted, onUnmounted, watch, reactive, ref, toRefs, computed } from 'vue';
|
||||
import { ElFormInstance } from 'element-plus';
|
||||
|
||||
// 类型定义补充(避免any)
|
||||
// 类型定义补充
|
||||
interface DialogOption {
|
||||
visible: boolean;
|
||||
title: string;
|
||||
}
|
||||
|
||||
// 方阵级联选择器选项类型
|
||||
interface FangzhenOption {
|
||||
matrixId: string | number;
|
||||
name: string;
|
||||
children?: FangzhenOption[];
|
||||
}
|
||||
|
||||
// 方阵表单项类型
|
||||
interface UnitBoItem {
|
||||
unitProjectArea: string;
|
||||
unitProjectStatus: string;
|
||||
unitProjectId: (string | number)[]; // 级联选择值(数组)
|
||||
}
|
||||
|
||||
// 方阵表单根模型类型(关键:显式声明unitBoList)
|
||||
interface MatrixForm {
|
||||
landId?: string | number; // 关联的地块ID
|
||||
unitBoList: UnitBoItem[]; // 方阵列表
|
||||
}
|
||||
|
||||
// 基础实例与Store
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const userStore = useUserStoreHook();
|
||||
@ -203,14 +227,17 @@ const currentProject = computed(() => userStore.selectedProject);
|
||||
|
||||
// 响应式数据
|
||||
const landBlockList = ref<LandBlockVO[]>([]);
|
||||
const fangzhenList = ref<any[]>([]); // 方阵列表(实际项目建议定义具体类型)
|
||||
const fangzhenList = ref<FangzhenOption[]>([]);
|
||||
const buttonLoading = ref(false);
|
||||
const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const uploadRef = ref<any>(null); // upload组件ref
|
||||
const uploadRef = ref<any>(null);
|
||||
|
||||
// 方阵表单数据(核心修改:初始值为空,避免默认填充无效数据)
|
||||
const unitBoList = ref<UnitBoItem[]>([{ unitProjectArea: '', unitProjectStatus: '', unitProjectId: [] }]);
|
||||
// 方阵表单模型(核心修复:使用reactive并显式声明结构)
|
||||
const formM = ref<MatrixForm>({
|
||||
landId: undefined,
|
||||
unitBoList: [{ unitProjectArea: '', unitProjectStatus: '', unitProjectId: [] }]
|
||||
});
|
||||
|
||||
// 表格选择相关
|
||||
const ids = ref<Array<string | number>>([]);
|
||||
@ -243,7 +270,6 @@ const initFormData: LandBlockForm = {
|
||||
// 核心数据(含表单规则)
|
||||
const data = reactive({
|
||||
form: { ...initFormData },
|
||||
formM: { landId: undefined }, // 方阵关联表单(仅存地块ID)
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
@ -256,7 +282,7 @@ const data = reactive({
|
||||
farmerCount: undefined,
|
||||
params: {}
|
||||
},
|
||||
// 地块表单规则(原有规则保留)
|
||||
// 地块表单规则
|
||||
rules: {
|
||||
id: [{ required: true, message: '主键ID不能为空', trigger: 'blur' }],
|
||||
projectId: [{ required: true, message: '项目ID不能为空', trigger: 'blur' }],
|
||||
@ -265,7 +291,7 @@ const data = reactive({
|
||||
}
|
||||
});
|
||||
|
||||
const { queryParams, form, rules, formM } = toRefs(data);
|
||||
const { queryParams, form, rules } = toRefs(data);
|
||||
|
||||
/** 查询地块列表 */
|
||||
const getList = async () => {
|
||||
@ -381,10 +407,10 @@ const getfangzhenList = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await subMatrix(currentProject.value.id);
|
||||
// 处理方阵数据(级联选择需父子结构,此处保留原有逻辑)
|
||||
// 处理方阵数据(级联选择需父子结构)
|
||||
res.data.forEach((item: any) => {
|
||||
item.children?.forEach((item2: any) => {
|
||||
item2.matrixId = `${item2.name}_${item2.matrixId}`; // 拼接名称+ID,便于后续拆分
|
||||
item2.matrixId = `${item2.name}_${item2.matrixId}`;
|
||||
});
|
||||
});
|
||||
fangzhenList.value = res.data;
|
||||
@ -395,55 +421,62 @@ const getfangzhenList = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
/** 关联方阵(核心修改:打开弹窗前强制重置表单) */
|
||||
/** 关联方阵 */
|
||||
const handleView = async (row: LandBlockVO) => {
|
||||
if (!row?.id) return proxy?.$modal.msgWarning('请选择有效的地块');
|
||||
|
||||
// 1. 重置方阵表单(清空历史数据)
|
||||
console.log('🚀 ~ handleView ~ row:', row);
|
||||
// 重置方阵表单
|
||||
resetMatrix();
|
||||
// 2. 绑定当前地块ID
|
||||
// 绑定当前地块ID
|
||||
formM.value.landId = row.id;
|
||||
// 3. 打开弹窗
|
||||
// 打开弹窗
|
||||
dialogMatrix.visible = true;
|
||||
|
||||
dialogMatrix.title = `关联方阵(地块:${row.landName || row.landCode})`;
|
||||
};
|
||||
|
||||
/** 新增方阵表单项 */
|
||||
const addUnitBoItem = () => {
|
||||
unitBoList.value.push({
|
||||
formM.value.unitBoList.push({
|
||||
unitProjectArea: '',
|
||||
unitProjectStatus: '',
|
||||
unitProjectId: [] // 级联选择初始为空数组
|
||||
unitProjectId: []
|
||||
});
|
||||
// 重置校验状态
|
||||
landBlockFormMatrixRef.value?.clearValidate();
|
||||
};
|
||||
|
||||
/** 删除方阵表单项 */
|
||||
const removeUnitBoItem = (index: number) => {
|
||||
if (unitBoList.value.length <= 1) {
|
||||
if (formM.value.unitBoList.length <= 1) {
|
||||
return proxy?.$modal.msgWarning('至少保留一项方阵配置');
|
||||
}
|
||||
unitBoList.value.splice(index, 1);
|
||||
// 重置表单校验状态(避免删除后残留校验提示)
|
||||
formM.value.unitBoList.splice(index, 1);
|
||||
landBlockFormMatrixRef.value?.clearValidate();
|
||||
};
|
||||
|
||||
/** 提交方阵关联表单 */
|
||||
/** 提交方阵关联表单(核心修复:数据处理逻辑) */
|
||||
const submitFormMatrix = () => {
|
||||
landBlockFormMatrixRef.value?.validate(async (valid: boolean) => {
|
||||
if (!valid) return;
|
||||
if (!formM.value.landId) return proxy?.$modal.msgWarning('地块ID异常,请重新选择地块');
|
||||
|
||||
try {
|
||||
// 处理方阵数据(拆分名称+ID)
|
||||
const unitBoListParams = unitBoList.value.map((item) => {
|
||||
const [unitProjectName, unitProjectId] = item.unitProjectId[1]?.split('_') || [];
|
||||
if (!unitProjectId) throw new Error('方阵ID解析失败,请重新选择方阵');
|
||||
// 处理方阵数据(修复ID拆分逻辑)
|
||||
const unitBoListParams = formM.value.unitBoList.map((item) => {
|
||||
// 取级联选择的最后一层值
|
||||
const lastLevelValue = item.unitProjectId[item.unitProjectId.length - 1];
|
||||
if (!lastLevelValue) throw new Error('请选择完整的方阵');
|
||||
|
||||
// 拆分名称和ID
|
||||
const [unitProjectName, unitProjectId] = String(lastLevelValue).split('_');
|
||||
if (!unitProjectId) throw new Error('方阵ID解析失败,请重新选择');
|
||||
|
||||
return {
|
||||
unitProjectArea: item.unitProjectArea.trim(),
|
||||
unitProjectStatus: item.unitProjectStatus.trim(),
|
||||
unitProjectId: unitProjectId, // 纯ID(后端需要)
|
||||
unitProjectName: unitProjectName // 名称(可选,用于显示)
|
||||
unitProjectId: unitProjectId,
|
||||
unitProjectName: unitProjectName
|
||||
};
|
||||
});
|
||||
|
||||
@ -456,7 +489,7 @@ const submitFormMatrix = () => {
|
||||
if (res.code === 200) {
|
||||
proxy?.$modal.msgSuccess('关联方阵成功');
|
||||
dialogMatrix.visible = false;
|
||||
await getList(); // 刷新地块列表
|
||||
await getList();
|
||||
} else {
|
||||
proxy?.$modal.msgError(res.msg || '关联失败');
|
||||
}
|
||||
@ -472,17 +505,14 @@ const cancelMatrix = () => {
|
||||
dialogMatrix.visible = false;
|
||||
};
|
||||
|
||||
/** 方阵表单重置(核心修改:清空所有数据+重置校验) */
|
||||
/** 方阵表单重置 */
|
||||
const resetMatrix = () => {
|
||||
// 1. 清空地块ID
|
||||
formM.value.landId = undefined;
|
||||
// 2. 重置方阵列表(仅保留一个空项)
|
||||
unitBoList.value = [{ unitProjectArea: '', unitProjectStatus: '', unitProjectId: [] }];
|
||||
// 3. 重置表单校验状态
|
||||
if (landBlockFormMatrixRef.value) {
|
||||
landBlockFormMatrixRef.value.resetFields();
|
||||
landBlockFormMatrixRef.value.clearValidate();
|
||||
}
|
||||
formM.value.landId = undefined;
|
||||
formM.value.unitBoList = [{ unitProjectArea: '', unitProjectStatus: '', unitProjectId: [] }];
|
||||
};
|
||||
|
||||
/** 监听项目变化,刷新数据 */
|
||||
@ -491,11 +521,11 @@ const listeningProject = watch(
|
||||
(newId) => {
|
||||
if (newId) {
|
||||
queryParams.value.projectId = newId;
|
||||
getfangzhenList(); // 刷新方阵列表
|
||||
getList(); // 刷新地块列表
|
||||
getfangzhenList();
|
||||
getList();
|
||||
}
|
||||
},
|
||||
{ immediate: true } // 初始加载时执行一次
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
/** 导入Excel */
|
||||
@ -524,36 +554,34 @@ const handleImport = (options: { file: File }) => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
/** 导出模板 */
|
||||
const exportFile = () => {
|
||||
// 导出模版文件
|
||||
try {
|
||||
// 创建a标签
|
||||
const link = document.createElement('a');
|
||||
// 设置PDF文件路径 - 相对于public目录
|
||||
link.href = '/dikuai.xlsx';
|
||||
// 设置下载后的文件名
|
||||
link.download = '地块信息导入模版.xlsx';
|
||||
// 触发点击
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
// 清理
|
||||
document.body.removeChild(link);
|
||||
} catch (error) {
|
||||
alert('下载失败,请重试');
|
||||
}
|
||||
};
|
||||
|
||||
/** 下载导入模板 */
|
||||
const downloadTemplate = () => {
|
||||
try {
|
||||
const link = document.createElement('a');
|
||||
link.href = '/landBlock.xlsx'; // 模板路径(需确保public目录下存在该文件)
|
||||
link.download = '地块信息导入模板.xlsx'; // 下载后文件名
|
||||
link.href = '/landBlock.xlsx';
|
||||
link.download = '地块信息导入模板.xlsx';
|
||||
document.body.appendChild(link);
|
||||
link.click(); // 触发下载
|
||||
link.click();
|
||||
} catch (err) {
|
||||
proxy?.$modal.msgError('模板下载失败,请重试');
|
||||
} finally {
|
||||
document.body.removeChild(link); // 清理DOM
|
||||
const link = document.querySelector('a[download="地块信息导入模板.xlsx"]');
|
||||
if (link) document.body.removeChild(link);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -91,7 +91,9 @@
|
||||
<template #default="scope">
|
||||
<!-- 查看子项按钮 -->
|
||||
<el-tooltip content="查看子项" placement="top">
|
||||
<el-button link type="primary" @click="handleViewSons(scope.row)" v-hasPermi="['land:landTransferLedger:view']"> 查看子项 </el-button>
|
||||
<el-button link type="primary" @click="handleViewSons(scope.row)" v-hasPermi="['land:landTransferLedger:childrenList']">
|
||||
查看子项
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button link type="primary" @click="handleDelete(scope.row)" v-hasPermi="['land:landTransferLedger:remove']">删除</el-button>
|
||||
@ -147,7 +149,9 @@
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAddSon" v-hasPermi="['land:landTransferLedger:addSon']"> 新增子项 </el-button>
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAddSon" v-hasPermi="['land:landTransferLedger:childrenAdd']">
|
||||
新增子项
|
||||
</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
@ -144,13 +144,16 @@
|
||||
</el-table-column> -->
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180" />
|
||||
<el-table-column fixed="right" label="操作" align="center" class-name="small-padding fixed-width" width="400">
|
||||
<el-table-column fixed="right" label="操作" align="center" width="500">
|
||||
<template #default="scope">
|
||||
<el-space>
|
||||
<el-button link type="primary" icon="Edit" @click="handleCheckRules(scope.row)" v-hasPermi="['project:attendanceRule:byProjectId']"
|
||||
>打卡规则
|
||||
</el-button>
|
||||
<el-button link type="primary" icon="Edit" @click="handleScope(scope.row)" v-hasPermi="['project:project:edit']">打卡范围 </el-button>
|
||||
<el-button link type="primary" icon="FolderOpened" @click="handleShowUpload(scope.row)" v-hasPermi="['project:project:edit']"
|
||||
>导入安全协议书
|
||||
</el-button>
|
||||
|
||||
<el-button link type="success" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['project:project:edit']">修改 </el-button>
|
||||
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['project:project:remove']">删除 </el-button>
|
||||
<el-button link type="primary" icon="upload" @click="handleUpload(scope.row)" v-hasPermi="['project:project:saveTenderFile']"
|
||||
@ -160,7 +163,6 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
|
||||
</el-card>
|
||||
<!-- 添加或修改项目对话框 -->
|
||||
@ -253,78 +255,6 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<div class="block-box">
|
||||
<div class="">打卡设置</div>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12" :offset="0">
|
||||
<el-form-item label="打卡开始时间" prop="playCardStart" label-width="110px">
|
||||
<!-- <el-time-picker value-format="HH:mm" v-model="form.playCardStart" placeholder="请输入打卡开始时间" /> -->
|
||||
<el-time-select
|
||||
v-model="form.playCardStart"
|
||||
style="width: 100%"
|
||||
class="mr-4"
|
||||
placeholder="请输入打卡开始时间"
|
||||
value-format="HH:mm"
|
||||
start="00:00"
|
||||
step="00:15"
|
||||
end="23:59"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" :offset="0">
|
||||
<el-form-item label="打卡结束时间" prop="playCardEnd" label-width="110px">
|
||||
<!-- <el-time-picker value-format="HH:mm" v-model="form.playCardEnd" placeholder="请输入打卡结束时间" /> -->
|
||||
<el-time-select
|
||||
v-model="form.playCardEnd"
|
||||
style="width: 100%"
|
||||
:min-time="form.playCardStart"
|
||||
class="mr-4"
|
||||
placeholder="请输入打卡结束时间"
|
||||
value-format="HH:mm"
|
||||
start="00:00"
|
||||
step="00:15"
|
||||
end="23:59"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" :offset="0">
|
||||
<el-form-item label="打卡类型" prop="playCardStart" label-width="110px">
|
||||
<!-- <el-time-picker value-format="HH:mm" v-model="form.playCardStart" placeholder="请输入打卡开始时间" /> -->
|
||||
<el-time-select
|
||||
v-model="form.playCardStart"
|
||||
style="width: 100%"
|
||||
class="mr-4"
|
||||
placeholder="请输入打卡开始时间"
|
||||
value-format="HH:mm"
|
||||
start="00:00"
|
||||
step="00:15"
|
||||
end="23:59"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" :offset="0">
|
||||
<el-form-item label="工作日" prop="playCardEnd" label-width="110px">
|
||||
<!-- <el-time-picker value-format="HH:mm" v-model="form.playCardEnd" placeholder="请输入打卡结束时间" /> -->
|
||||
<el-time-select
|
||||
v-model="form.playCardEnd"
|
||||
style="width: 100%"
|
||||
:min-time="form.playCardStart"
|
||||
class="mr-4"
|
||||
placeholder="请输入打卡结束时间"
|
||||
value-format="HH:mm"
|
||||
start="00:00"
|
||||
step="00:15"
|
||||
end="23:59"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<!-- <el-col :span="24" :offset="0">
|
||||
<el-form-item label="安全协议书" prop="securityAgreement">
|
||||
<file-upload v-model="form.securityAgreement" :limit="1" :file-type="['pdf']" :file-size="50" />
|
||||
</el-form-item>
|
||||
</el-col> -->
|
||||
</el-row>
|
||||
</div>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
@ -342,13 +272,12 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- //选取项目地址弹窗 -->
|
||||
<el-dialog v-model="amapStatus" :title="form.projectName + '-获取经纬度'" width="80%">
|
||||
<el-dialog draggable v-model="amapStatus" :title="form.projectName + '-获取经纬度'" width="80%">
|
||||
<amap height="620px" @setLocation="setPoi"></amap>
|
||||
</el-dialog>
|
||||
<!-- 选取方阵地址 -->
|
||||
<el-dialog title="设置方阵" v-model="polygonStatus" width="1400px" :close-on-click-modal="false">
|
||||
<el-dialog draggable title="设置方阵" v-model="polygonStatus" width="1400px" :close-on-click-modal="false">
|
||||
<open-layers-map
|
||||
:project-id="projectId"
|
||||
:design-id="designId"
|
||||
@ -356,7 +285,7 @@
|
||||
@close="polygonStatus = false"
|
||||
></open-layers-map>
|
||||
</el-dialog>
|
||||
<el-dialog title="添加子项目" v-model="childProjectStatus" width="400">
|
||||
<el-dialog draggable title="添加子项目" v-model="childProjectStatus" width="400">
|
||||
<span>填写子项目名称</span>
|
||||
<el-input v-model="childProjectForm.projectName"></el-input>
|
||||
<template #footer>
|
||||
@ -366,7 +295,7 @@
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<el-dialog title="上传文件" v-model="fileVisble" width="400">
|
||||
<el-dialog draggable title="上传文件" v-model="fileVisble" width="400">
|
||||
<file-upload v-model="fileForm.tenderFiles" :limit="10" :file-type="['pdf']" :file-size="50" />
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
@ -375,6 +304,91 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<el-dialog draggable title="打卡规则" v-model="ruleFlag" width="800">
|
||||
<template #footer>
|
||||
<el-form ref="projectFormRef" :model="form" :rules="rules" label-width="100px">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12" :offset="0">
|
||||
<el-form-item label="上班时间" prop="clockInTime">
|
||||
<el-time-select
|
||||
v-model="form.clockInTime"
|
||||
style="width: 100%"
|
||||
class="mr-4"
|
||||
placeholder="请输入打卡开始时间"
|
||||
value-format="HH:mm"
|
||||
start="00:00"
|
||||
step="00:15"
|
||||
end="23:59"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" :offset="0">
|
||||
<el-form-item label="下班时间" prop="clockOutTime">
|
||||
<el-time-select
|
||||
v-model="form.clockOutTime"
|
||||
style="width: 100%"
|
||||
:min-time="form.clockInTime"
|
||||
class="mr-4"
|
||||
placeholder="请输入打卡结束时间"
|
||||
value-format="HH:mm"
|
||||
start="00:00"
|
||||
step="00:15"
|
||||
end="23:59"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" :offset="0">
|
||||
<el-form-item label="打卡类型" prop="type">
|
||||
<el-radio-group v-model="form.type">
|
||||
<el-radio value="1" size="large">无限制</el-radio>
|
||||
<el-radio value="2" size="large">范围内打卡</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="打卡类型" prop="weekday">
|
||||
<el-checkbox-group v-model="form.weekday" size="small">
|
||||
<el-checkbox label="星期一" value="1" />
|
||||
<el-checkbox label="星期二" value="2" />
|
||||
<el-checkbox label="星期三" value="3" />
|
||||
<el-checkbox label="星期四" value="4" />
|
||||
<el-checkbox label="星期五" value="5" />
|
||||
<el-checkbox label="星期六" value="6" />
|
||||
<el-checkbox label="星期日" value="7" />
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
</el-col> </el-row
|
||||
></el-form>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="ruleSubmit"> 提交</el-button>
|
||||
<el-button @click="ruleFlag = false">取消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<el-dialog draggable title="打卡范围" v-model="ScopeFlag" width="600">
|
||||
<div v-for="(item, i) of punchRangeList" :key="i" class="options_item">
|
||||
<el-row style="margin-bottom: 10px;">
|
||||
<el-col :span="1"> <el-color-picker v-model="item.punchColor" show-alpha /></el-col>
|
||||
<el-col :span="12"> <el-input v-model="item.punchName" placeholder="请输入打卡范围名称" class="ml-8" /></el-col>
|
||||
<el-col :span="10" style="text-align: right; margin-top: 5px">
|
||||
<el-button v-if="item.id" link type="primary" icon="view" @click="previewPunchRange(item)">预览</el-button>
|
||||
<el-button v-if="item.id" link type="primary" icon="delete" @click="handleScopeDel(item)">移除</el-button>
|
||||
<el-button v-if="!item.id" link type="primary" icon="plus" @click="addPunchRange(item)">添加</el-button>
|
||||
<el-button v-if="item.id" link type="primary" icon="download" @click="handleScopeEdit(item)">保存</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="scopeSubmit">确定</el-button>
|
||||
<el-button @click="ScopeFlag = false">取消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<CesiumEarthDialog ref="earthDialog" @send-data="handleEarthData"
|
||||
:position="position"
|
||||
:data="earthData"
|
||||
@close="handleEarthClose"></CesiumEarthDialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -382,17 +396,24 @@
|
||||
import {
|
||||
addChildProject,
|
||||
addProject,
|
||||
addProjectFacilities,
|
||||
addProjectPilePoint,
|
||||
addProjectSquare,
|
||||
delProject,
|
||||
uploadProjectFile,
|
||||
getProject,
|
||||
listProject,
|
||||
updateProject
|
||||
updateProject,
|
||||
attendanceRuleAdd,
|
||||
attendanceRuleEdit,
|
||||
byProjectIdDetail,
|
||||
getAttendanceRangeList,
|
||||
updateAttendanceRange,
|
||||
delAttendanceRange
|
||||
} from '@/api/project/project';
|
||||
import { ProjectForm, ProjectQuery, ProjectVO, childProjectQuery, locationType } from '@/api/project/project/types';
|
||||
import amap from '@/components/amap/index.vue';
|
||||
import CesiumEarthDialog from "./map.vue"
|
||||
import { useUserStoreHook } from '@/store/modules/user';
|
||||
const userStore = useUserStoreHook();
|
||||
const currentProject = computed(() => userStore.selectedProject);
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { sys_normal_disable, project_category_type, project_type, project_stage } = toRefs<any>(
|
||||
proxy?.useDict('sys_normal_disable', 'project_category_type', 'project_type', 'project_stage')
|
||||
@ -413,6 +434,22 @@ const polygonStatus = ref(false);
|
||||
const dxfFile = ref(null);
|
||||
const projectId = ref<string>('');
|
||||
const designId = ref<string>('');
|
||||
const ruleFlag = ref(false);
|
||||
const ScopeFlag = ref(false);
|
||||
const earthDialog = ref(null);
|
||||
const position = ref(''); //预览
|
||||
let earthData = reactive({
|
||||
projectId: '',
|
||||
punchName: '',
|
||||
punchColor: '#1983ff',
|
||||
punchRange: '',
|
||||
}); //传值给地图组件
|
||||
const punchRangeList = ref<any>([
|
||||
{
|
||||
punchName: '',
|
||||
punchColor: '#1983ff'
|
||||
}
|
||||
]);
|
||||
const childProjectForm = reactive<childProjectQuery>({
|
||||
projectName: '',
|
||||
pid: '',
|
||||
@ -432,7 +469,7 @@ const fileForm = ref({
|
||||
const jsonData = ref(null);
|
||||
const fullscreenLoading = ref(false);
|
||||
|
||||
const initFormData: ProjectForm = {
|
||||
const initFormData = {
|
||||
id: undefined,
|
||||
projectName: undefined,
|
||||
shortName: undefined,
|
||||
@ -451,13 +488,15 @@ const initFormData: ProjectForm = {
|
||||
lat: undefined,
|
||||
plan: undefined,
|
||||
onStreamTime: undefined,
|
||||
playCardStart: undefined,
|
||||
playCardEnd: undefined,
|
||||
clockInTime: undefined,
|
||||
clockOutTime: undefined,
|
||||
designTotal: undefined,
|
||||
securityAgreement: undefined,
|
||||
sort: 0,
|
||||
showHidden: undefined,
|
||||
isDelete: undefined
|
||||
isDelete: undefined,
|
||||
type: '1',
|
||||
weekday: []
|
||||
};
|
||||
const data = reactive<PageData<ProjectForm, ProjectQuery>>({
|
||||
form: { ...initFormData },
|
||||
@ -480,8 +519,6 @@ const data = reactive<PageData<ProjectForm, ProjectQuery>>({
|
||||
lat: undefined,
|
||||
plan: undefined,
|
||||
onStreamTime: undefined,
|
||||
playCardStart: undefined,
|
||||
playCardEnd: undefined,
|
||||
designTotal: undefined,
|
||||
securityAgreement: undefined,
|
||||
sort: undefined,
|
||||
@ -490,8 +527,8 @@ const data = reactive<PageData<ProjectForm, ProjectQuery>>({
|
||||
params: {}
|
||||
},
|
||||
rules: {
|
||||
playCardStart: [{ required: true, message: '打卡开始时间不能为空', trigger: 'blur' }],
|
||||
playCardEnd: [{ required: true, message: '打卡结束时间不能为空', trigger: 'blur' }],
|
||||
clockInTime: [{ required: true, message: '打卡开始时间不能为空', trigger: 'blur' }],
|
||||
clockOutTime: [{ required: true, message: '打卡结束时间不能为空', trigger: 'blur' }],
|
||||
projectName: [{ required: true, message: '项目名称不能为空', trigger: 'blur' }],
|
||||
shortName: [{ required: true, message: '项目简称不能为空', trigger: 'blur' }],
|
||||
principalPhone: [{ required: true, message: '负责人电话不能为空', trigger: 'blur' }],
|
||||
@ -692,7 +729,86 @@ const handleSetChild = async () => {
|
||||
}
|
||||
}
|
||||
};
|
||||
const handleScope = (row) => {
|
||||
console.log('row',row);
|
||||
// 打卡范围
|
||||
ScopeFlag.value = true;
|
||||
projectId.value = row.id;
|
||||
// 获取打卡范围列表
|
||||
getListScope(row.id);
|
||||
};
|
||||
// 获取打卡范围列表
|
||||
const getListScope = (id) => {
|
||||
punchRangeList.value = [{
|
||||
punchName: '',
|
||||
punchColor: '#1983ff'
|
||||
}];
|
||||
getAttendanceRangeList({ projectId: id, }).then((res) => {
|
||||
if (res.code === 200) {
|
||||
punchRangeList.value.unshift(...res.rows);
|
||||
}
|
||||
});
|
||||
}
|
||||
// 修改打卡范围
|
||||
const handleScopeEdit = (row) => {
|
||||
updateAttendanceRange(row).then((res) => {
|
||||
if (res.code === 200) {
|
||||
proxy.$modal.msgSuccess('修改成功');
|
||||
// ScopeFlag.value = false;
|
||||
getListScope(projectId.value);
|
||||
}
|
||||
});
|
||||
}
|
||||
// 删除打卡范围
|
||||
const handleScopeDel = (row) => {
|
||||
proxy.$modal.confirm('是否确认删除打卡范围?').then(() => {
|
||||
delAttendanceRange(row.id).then((res) => {
|
||||
if (res.code === 200) {
|
||||
proxy.$modal.msgSuccess('删除成功');
|
||||
getListScope(projectId.value);
|
||||
}
|
||||
});
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
|
||||
const scopeSubmit = () => {
|
||||
// 提交打卡范围
|
||||
};
|
||||
// 添加规则
|
||||
const handleCheckRules = async (row?: ProjectVO) => {
|
||||
reset();
|
||||
const _id = row?.id || ids.value[0];
|
||||
const res = await byProjectIdDetail(_id);
|
||||
if (res.data) {
|
||||
res.data.weekday = res.data.weekday.split(',');
|
||||
Object.assign(form.value, res.data);
|
||||
}
|
||||
projectId.value = row.id;
|
||||
ruleFlag.value = true;
|
||||
};
|
||||
const ruleSubmit = async () => {
|
||||
console.log(form.value);
|
||||
projectFormRef.value?.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
let obj = {
|
||||
weekday: form.value.weekday.join(','),
|
||||
projectId: projectId.value,
|
||||
id: projectId.value,
|
||||
clockInTime: form.value.clockInTime,
|
||||
clockOutTime: form.value.clockOutTime,
|
||||
type: form.value.type
|
||||
};
|
||||
if (form.value.id) {
|
||||
await attendanceRuleEdit(obj);
|
||||
} else {
|
||||
await attendanceRuleAdd(obj);
|
||||
}
|
||||
ruleFlag.value = false;
|
||||
await getList();
|
||||
}
|
||||
});
|
||||
};
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = () => {
|
||||
proxy?.download(
|
||||
@ -703,7 +819,36 @@ const handleExport = () => {
|
||||
`project_${new Date().getTime()}.xlsx`
|
||||
);
|
||||
};
|
||||
|
||||
// 打开绘制范围
|
||||
const addPunchRange = (item) => {
|
||||
earthData.punchName = item.punchName
|
||||
earthData.punchColor = item.punchColor
|
||||
earthData.projectId = projectId.value
|
||||
earthData.id = ''
|
||||
earthData.punchRange = ''
|
||||
if (earthData.punchName=='') {
|
||||
proxy.$modal.msgError('请先填写打卡范围名称');
|
||||
return
|
||||
}
|
||||
earthDialog.value.show()
|
||||
}
|
||||
// 接受绘制范围
|
||||
const handleEarthData = (data) => {
|
||||
ScopeFlag.value = false;
|
||||
}
|
||||
// 关闭绘制范围
|
||||
const handleEarthClose = () => {
|
||||
earthDialog.value.show = false
|
||||
}
|
||||
// 预览范围
|
||||
const previewPunchRange = (item)=>{
|
||||
earthData.id = item.id
|
||||
earthData.projectId = item.projectId
|
||||
earthData.punchName = item.punchName
|
||||
earthData.punchColor = item.punchColor
|
||||
earthData.punchRange = item.punchRange
|
||||
earthDialog.value.show()
|
||||
}
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
|
||||
305
src/views/project/project/map.vue
Normal file
305
src/views/project/project/map.vue
Normal file
@ -0,0 +1,305 @@
|
||||
<template>
|
||||
<el-dialog v-model="visible" title="地球可视化" :width="dialogWidth" :height="dialogHeight" :before-close="handleClose"
|
||||
destroy-on-close>
|
||||
<div v-loading="loading" id="earthMap" class="earth-container"></div>
|
||||
<template #footer>
|
||||
<el-button type="warning" @click="redraw">重新绘制</el-button>
|
||||
<el-button v-if="isDraw" type="success" @click="drawRange">绘制</el-button>
|
||||
<el-button type="primary" @click="handlesubmit">确定</el-button>
|
||||
<el-button type="danger" @click="handleClose">关闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, watch, defineEmits, defineProps,onUnmounted } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import {
|
||||
addAttendanceRange,
|
||||
updateAttendanceRange,
|
||||
} from '@/api/project/project';
|
||||
const emit = defineEmits(['send-data', 'close']);
|
||||
const props = defineProps({
|
||||
position: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => { }
|
||||
}
|
||||
});
|
||||
// 弹窗控制变量
|
||||
const visible = ref(false);
|
||||
const dialogWidth = ref('90%');
|
||||
const dialogHeight = ref('90%');
|
||||
let earthInstance = null;
|
||||
let earthContainer = ref(null);
|
||||
let loading = ref(true);
|
||||
let positions = ref([]);
|
||||
const entityObject = ref(null);
|
||||
const isDraw = ref(true);
|
||||
// 显示弹窗
|
||||
const show = () => {
|
||||
visible.value = true;
|
||||
console.log(props.data);
|
||||
if (props.data?.id) {
|
||||
console.log(231, props.data);
|
||||
isDraw.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 关闭弹窗
|
||||
const handleClose = () => {
|
||||
visible.value = false;
|
||||
// 清除地球实例
|
||||
if (earthInstance && earthInstance.destroy) {
|
||||
earthInstance.destroy();
|
||||
earthInstance = null;
|
||||
}
|
||||
// 清除全局变量引用
|
||||
if (window.Earth2) {
|
||||
delete window.Earth2;
|
||||
}
|
||||
};
|
||||
|
||||
// 监听弹窗显示状态,初始化地球
|
||||
watch(
|
||||
() => visible.value,
|
||||
(newVal) => {
|
||||
if (newVal && earthContainer.value) {
|
||||
createEarth();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 创建地球
|
||||
const createEarth = () => {
|
||||
if (!window.YJ) {
|
||||
ElMessage.error('YJ库未加载,请检查依赖');
|
||||
return;
|
||||
}
|
||||
|
||||
window.YJ.on({
|
||||
ws: true,
|
||||
// host: getIP(), // 资源所在服务器地址
|
||||
// username: this.loginForm.username, // 用户名
|
||||
// password: md5pass, // 密码
|
||||
}).then((res) => {
|
||||
if (!earthContainer.value) return;
|
||||
loading.value = false;
|
||||
// 创建地球实例
|
||||
earthInstance = new YJ.YJEarth(earthContainer.value);
|
||||
window.Earth2 = earthInstance;
|
||||
|
||||
// 开启右键和左键点击事件
|
||||
YJ.Global.openRightClick(window.Earth2);
|
||||
YJ.Global.openLeftClick(window.Earth2);
|
||||
|
||||
// 设置初始视角
|
||||
const view = {
|
||||
position: {
|
||||
lng: 102.03643298211526,
|
||||
lat: 34.393586474501,
|
||||
alt: 11298179.51993155
|
||||
},
|
||||
orientation: {
|
||||
heading: 360,
|
||||
pitch: -89.94481747201486,
|
||||
roll: 0
|
||||
}
|
||||
};
|
||||
|
||||
YJ.Global.CesiumContainer(window.Earth2, {
|
||||
compass: false, //罗盘
|
||||
});
|
||||
// 加载底图
|
||||
loadBaseMap(earthInstance.viewer);
|
||||
|
||||
// 可以取消注释以下代码来设置初始视角
|
||||
// YJ.Global.flyTo(earthInstance, view);
|
||||
// YJ.Global.setDefaultView(earthInstance.viewer, view)
|
||||
if (props.data.punchRange) {
|
||||
renderRange(JSON.parse(props.data.punchRange));
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('初始化地球失败:', err);
|
||||
ElMessage.error('初始化地球失败,请稍后重试');
|
||||
});
|
||||
};
|
||||
|
||||
// 加载底图
|
||||
const loadBaseMap = (viewer) => {
|
||||
if (!viewer || !Cesium) {
|
||||
ElMessage.error('Cesium库未加载,请检查依赖');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 创建瓦片提供器
|
||||
const imageryProvider = new Cesium.UrlTemplateImageryProvider({
|
||||
url: 'https://webst01.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}',
|
||||
fileExtension: 'png',
|
||||
minimumLevel: 0,
|
||||
maximumLevel: 18,
|
||||
projection: Cesium.WebMercatorProjection,
|
||||
credit: new Cesium.Credit('卫星图数据来源')
|
||||
});
|
||||
|
||||
// 添加图层到视图
|
||||
viewer.imageryLayers.addImageryProvider(imageryProvider);
|
||||
} catch (err) {
|
||||
console.error('加载底图失败:', err);
|
||||
ElMessage.error('加载底图失败');
|
||||
}
|
||||
};
|
||||
// 处理数据传递
|
||||
const handlesubmit = () => {
|
||||
if (!earthInstance || !earthInstance.viewer) {
|
||||
ElMessage.warning('地球实例未初始化,无法获取数据');
|
||||
return;
|
||||
}
|
||||
// 要传递的对象数据
|
||||
const dataToSend = {
|
||||
...props.data,
|
||||
punchRange: JSON.stringify(positions.value),
|
||||
};
|
||||
if (props.data.id) {
|
||||
updateAttendanceRange1(dataToSend)
|
||||
} else {
|
||||
addAttendanceRange1(dataToSend);
|
||||
}
|
||||
emit('send-data', dataToSend);
|
||||
};
|
||||
// 新增打卡范围接口
|
||||
const addAttendanceRange1 = (data) => {
|
||||
addAttendanceRange(data).then((res) => {
|
||||
if (res.code === 200) {
|
||||
ElMessage.success('新增打卡范围成功');
|
||||
handleClose();
|
||||
} else {
|
||||
ElMessage.error(res.msg);
|
||||
}
|
||||
}).catch((err) => {
|
||||
console.error('新增打卡范围失败:', err);
|
||||
ElMessage.error('新增打卡范围失败');
|
||||
});
|
||||
};
|
||||
// 修改打卡范围接口
|
||||
const updateAttendanceRange1 = (data) => {
|
||||
updateAttendanceRange(data).then((res) => {
|
||||
if (res.code === 200) {
|
||||
ElMessage.success('修改打卡范围成功');
|
||||
handleClose();
|
||||
} else {
|
||||
ElMessage.error(res.msg);
|
||||
}
|
||||
}).catch((err) => {
|
||||
console.error('修改打卡范围失败:', err);
|
||||
ElMessage.error('修改打卡范围失败');
|
||||
});
|
||||
};
|
||||
// 绘制范围
|
||||
const drawRange = () => {
|
||||
if (!earthInstance || !earthInstance.viewer) {
|
||||
ElMessage.warning('地球实例未初始化,无法绘制');
|
||||
return;
|
||||
}
|
||||
let draw = new YJ.Draw.DrawPolygon(window.Earth2);
|
||||
draw.start((err, params) => {
|
||||
if (err) {
|
||||
console.error('绘制失败:', err);
|
||||
ElMessage.error('绘制失败');
|
||||
return;
|
||||
}
|
||||
console.log('绘制成功:', params);
|
||||
positions.value = params;
|
||||
renderRange(params);
|
||||
});
|
||||
}
|
||||
// 渲染范围
|
||||
const renderRange = (params) => {
|
||||
let option = {
|
||||
id: 'Checkinrange',
|
||||
info: {
|
||||
type: "richText",
|
||||
text: "",
|
||||
hrefs: "",
|
||||
},
|
||||
positions: params,
|
||||
color: props.data.punchColor || "rgba(255,0,0,0.5)",
|
||||
line: {
|
||||
width: 3,
|
||||
color: "rgba(255,0,0,1)",
|
||||
},
|
||||
type: 0,
|
||||
show: true,
|
||||
}
|
||||
// PolygonObject
|
||||
let entity = new YJ.Obj.PolygonObject(
|
||||
window.Earth2,
|
||||
option
|
||||
);
|
||||
entity.flyTo();
|
||||
entityObject.value = entity
|
||||
}
|
||||
|
||||
|
||||
// 重新绘制
|
||||
const redraw = () => {
|
||||
if (entityObject.value) {
|
||||
entityObject.value.remove();
|
||||
}
|
||||
drawRange();
|
||||
}
|
||||
|
||||
// 组件挂载时设置容器ID
|
||||
onMounted(() => {
|
||||
if (!earthContainer.value) {
|
||||
earthContainer.value = 'earthMap'; // 设置与初始化代码中相同的ID
|
||||
}
|
||||
});
|
||||
|
||||
// 暴露显示方法给父组件
|
||||
defineExpose({
|
||||
show
|
||||
});
|
||||
|
||||
//
|
||||
onUnmounted(() => {
|
||||
if (earthInstance) {
|
||||
earthInstance.destroy();
|
||||
earthInstance = null;
|
||||
window.Earth2 = null;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.earth-container {
|
||||
width: 100%;
|
||||
height: 600px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
:deep(.el-dialog__body) {
|
||||
padding: 10px;
|
||||
height: calc(100% - 100px);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:deep(.el-dialog) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-height: 90vh;
|
||||
}
|
||||
|
||||
:deep(.el-dialog__content) {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
@ -80,6 +80,11 @@
|
||||
<el-option v-for="item in team_clock_type" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="打卡范围" prop="punchRangeList" v-if="form.isClockIn == 1">
|
||||
<el-select v-model="form.punchRangeList" multiple clearable placeholder="请选择打卡范围">
|
||||
<el-option v-for="item in projectTeamRangeList" :key="item.id" :label="item.punchName" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
|
||||
</el-form-item>
|
||||
@ -98,13 +103,15 @@
|
||||
</template>
|
||||
|
||||
<script setup name="ProjectTeam" lang="ts">
|
||||
import { addProjectTeam, delProjectTeam, getProjectTeam, listProjectTeam, updateProjectTeam } from '@/api/project/projectTeam';
|
||||
import { addProjectTeam, delProjectTeam, getProjectTeam, listProjectTeam, updateProjectTeam,getProjectTeamClockIn } from '@/api/project/projectTeam';
|
||||
import { ProjectTeamForm, ProjectTeamQuery, ProjectTeamVO } from '@/api/project/projectTeam/types';
|
||||
import { useUserStoreHook } from '@/store/modules/user';
|
||||
import UserListDialog from '@/views/project/projectTeam/component/UserListDialog.vue';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { team_clock_type } = toRefs<any>(proxy?.useDict('team_clock_type'));
|
||||
console.log(team_clock_type);
|
||||
|
||||
// 获取用户 store
|
||||
const userStore = useUserStoreHook();
|
||||
// 从 store 中获取项目列表和当前选中的项目
|
||||
@ -117,6 +124,7 @@ const ids = ref<Array<string | number>>([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
const projectTeamRangeList = ref([]);
|
||||
const currentRow = ref<ProjectTeamVO>({
|
||||
id: undefined,
|
||||
projectId: undefined,
|
||||
@ -140,7 +148,8 @@ const initFormData: ProjectTeamForm = {
|
||||
teamName: undefined,
|
||||
isClockIn: undefined,
|
||||
remark: undefined,
|
||||
peopleNumber: undefined
|
||||
peopleNumber: undefined,
|
||||
punchRangeList: undefined
|
||||
};
|
||||
const data = reactive<PageData<ProjectTeamForm, ProjectTeamQuery>>({
|
||||
form: { ...initFormData },
|
||||
@ -171,6 +180,14 @@ const getList = async () => {
|
||||
total.value = res.total;
|
||||
loading.value = false;
|
||||
};
|
||||
/** 获取该项目的打开范围 "*/
|
||||
const getClockIn = async () => {
|
||||
if(currentProject.value?.id){
|
||||
const res = await getProjectTeamClockIn({projectId:currentProject.value?.id});
|
||||
projectTeamRangeList.value = res.rows
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** 取消按钮 */
|
||||
const cancel = () => {
|
||||
@ -280,5 +297,6 @@ onUnmounted(() => {
|
||||
|
||||
onMounted(() => {
|
||||
getList();
|
||||
getClockIn();
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -5,6 +5,11 @@
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<el-form-item label="项目名称" prop="projectId">
|
||||
<el-select v-model="queryParams.projectId" clearable placeholder="全部">
|
||||
<el-option v-for="item in projectList" :key="item.value" :label="item.projectName" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="人员姓名" prop="userName">
|
||||
<el-input v-model="queryParams.userName" placeholder="请输入人员姓名" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
@ -156,6 +161,7 @@
|
||||
</el-button>
|
||||
<!-- <el-button link type="primary" icon="Switch" @click="handleToggle(scope.row)"> 切换人脸 </el-button> -->
|
||||
<el-button link type="primary" icon="Switch" @click="handleChange(scope.row)"> 人员迁移 </el-button>
|
||||
<el-button link type="primary" icon="Switch" @click="handleAssign(scope.row)"> 分配班组 </el-button>
|
||||
<el-button link type="primary" icon="ChatLineSquare" @click="handleExit(scope.row)"> 入退场记录 </el-button>
|
||||
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)"
|
||||
v-hasPermi="['contractor:constructionUser:remove']">
|
||||
@ -443,6 +449,30 @@
|
||||
</template>
|
||||
</el-calendar>
|
||||
</el-dialog>
|
||||
<el-dialog draggable :title="skipName + '-人员分配'" v-model="personnelAllocation" width="500px">
|
||||
<el-form-item label="所属项目" label-width="130px">
|
||||
<el-select v-model="personnelAllocationObject.projectId" @change="selectProject1" placeholder="请选择所属项目" style="width: 240px">
|
||||
<el-option v-for="item in projectList" :key="item.id" :label="item.projectName" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="岗位" label-width="130px">
|
||||
<el-select v-model="personnelAllocationObject.postId" placeholder="请选择岗位" style="width: 240px">
|
||||
<el-option v-for="item in user_post_type" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="班组" label-width="130px">
|
||||
<el-select v-model="personnelAllocationObject.teamId" :disabled="!personnelAllocationObject.projectId" placeholder="请选择分包单位"
|
||||
style="width: 240px">
|
||||
<el-option v-for="item in teamList" :key="item.id" :label="item.teamName" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="handlePersonnelAllocation">确认</el-button>
|
||||
<el-button @click="personnelAllocation = false"> 取消 </el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -462,7 +492,10 @@ import {
|
||||
getConstructionUserExit,
|
||||
dowloadConstructionUserTemplate,
|
||||
importConstructionUserInfo,
|
||||
listConstructionMonth
|
||||
listConstructionMonth,
|
||||
ProjectList,
|
||||
TeamList,
|
||||
TeamDistribution
|
||||
} from '@/api/project/constructionUser';
|
||||
import {
|
||||
ConstructionUserForm,
|
||||
@ -494,8 +527,8 @@ import { parseTime } from '@/utils/ruoyi';
|
||||
|
||||
const calendar = ref<CalendarInstance>();
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { type_of_work, user_sex_type, user_clock_type, user_file_type, user_status_type, wage_measure_unit_type } = toRefs<any>(
|
||||
proxy?.useDict('type_of_work', 'user_sex_type', 'user_clock_type', 'user_file_type', 'user_status_type', 'wage_measure_unit_type')
|
||||
const { type_of_work, user_sex_type, user_clock_type, user_file_type, user_status_type, wage_measure_unit_type,user_post_type } = toRefs<any>(
|
||||
proxy?.useDict('type_of_work', 'user_sex_type', 'user_clock_type', 'user_file_type', 'user_status_type', 'wage_measure_unit_type','user_post_type')
|
||||
);
|
||||
// 获取用户 store
|
||||
const userStore = useUserStoreHook();
|
||||
@ -511,6 +544,7 @@ const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
const skip = ref(false);
|
||||
const personnelAllocation = ref(false);
|
||||
const fileStatus = ref(false);
|
||||
const showFaceDrawer = ref(false);
|
||||
const statusDialog = ref(false);
|
||||
@ -531,6 +565,10 @@ const queryFormRef = ref<ElFormInstance>();
|
||||
const constructionUserFormRef = ref<ElFormInstance>();
|
||||
const skipName = ref('');
|
||||
const calendarList = ref<Array<AttendanceMonthVO>>([]);
|
||||
// 项目列表
|
||||
const projectList = ref([]);
|
||||
// 班组列表
|
||||
const teamList = ref([]);
|
||||
const dialog = reactive<DialogOption>({
|
||||
visible: false,
|
||||
title: '',
|
||||
@ -543,6 +581,14 @@ const skipObject: skipType = reactive({
|
||||
projectId: '',
|
||||
contractorId: ''
|
||||
});
|
||||
// 人员分配
|
||||
const personnelAllocationObject = reactive({
|
||||
memberId: null,
|
||||
projectId: '',
|
||||
teamId: '',
|
||||
postId: '',
|
||||
});
|
||||
|
||||
const contractorList = ref<Array<skipTeamType>>([]);
|
||||
//项目列表
|
||||
const skipOptions = ref<Array<skipOptionType>>([]);
|
||||
@ -661,6 +707,13 @@ const uploadPath = computed(() => {
|
||||
return list;
|
||||
});
|
||||
|
||||
// 获取项目列表
|
||||
const getProjectList = async () => {
|
||||
const res = await ProjectList({});
|
||||
projectList.value = res.rows;
|
||||
projectList.value.unshift({ id: '', projectName: '全部' });
|
||||
};
|
||||
|
||||
/** 返回文件上传状态 */
|
||||
const uploadStatusColor = computed(() => (str: string) => {
|
||||
switch (str) {
|
||||
@ -825,7 +878,7 @@ const reset = () => {
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.value.pageNum = 1;
|
||||
if (contractorOpt.value.length == 1) queryParams.value.contractorId = contractorOpt.value[0].value;
|
||||
// if (contractorOpt.value.length == 1) queryParams.value.contractorId = contractorOpt.value[0].value;
|
||||
getList();
|
||||
};
|
||||
|
||||
@ -1053,6 +1106,36 @@ const listeningProject = watch(
|
||||
getContractorList();
|
||||
}
|
||||
);
|
||||
// 分配班组
|
||||
const handleAssign = async (row: ConstructionUserVO) => {
|
||||
const _id = row?.id || ids.value[0];
|
||||
currentUserId.value = _id;
|
||||
personnelAllocationObject.memberId = row?.sysUserId;
|
||||
personnelAllocation.value = true;
|
||||
};
|
||||
// 选择项目1
|
||||
const selectProject1 = (e: any) => {
|
||||
// 请求班组
|
||||
getTeamList(personnelAllocationObject.projectId);
|
||||
|
||||
};
|
||||
const getTeamList = async (projectId) => {
|
||||
const res = await TeamList({
|
||||
projectId,
|
||||
pageNum: 1,
|
||||
pageSize: 100
|
||||
});
|
||||
teamList.value = res.rows;
|
||||
};
|
||||
// 人员分配
|
||||
const handlePersonnelAllocation = async () => {
|
||||
let res = await TeamDistribution(personnelAllocationObject);
|
||||
if (res.code == 200) {
|
||||
ElMessage.success(res.msg);
|
||||
personnelAllocation.value = false;
|
||||
getList();
|
||||
}
|
||||
};
|
||||
|
||||
onUnmounted(() => {
|
||||
listeningProject();
|
||||
@ -1060,6 +1143,7 @@ onUnmounted(() => {
|
||||
|
||||
onMounted(() => {
|
||||
getContractorList();
|
||||
getProjectList();
|
||||
});
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
|
||||
413
src/views/system/appMenu/index.vue
Normal file
413
src/views/system/appMenu/index.vue
Normal file
@ -0,0 +1,413 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<el-form-item label="菜单名称" prop="menuName">
|
||||
<el-input v-model="queryParams.menuName" placeholder="请输入菜单名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="菜单状态" clearable>
|
||||
<el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<el-card shadow="hover">
|
||||
<template #header>
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['system:menu:add']" type="primary" plain icon="Plus" @click="handleAdd()">新增 </el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="info" plain icon="Sort" @click="handleToggleExpandAll">展开/折叠</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<el-table
|
||||
ref="menuTableRef"
|
||||
v-loading="loading"
|
||||
:data="menuList"
|
||||
row-key="menuId"
|
||||
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
|
||||
:default-expand-all="isExpandAll"
|
||||
>
|
||||
<el-table-column prop="menuName" label="菜单名称" :show-overflow-tooltip="true" width="160"></el-table-column>
|
||||
<el-table-column prop="icon" label="图标" align="center" width="100">
|
||||
<template #default="scope">
|
||||
<svg-icon :icon-class="scope.row.icon" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="orderNum" label="排序" width="60"></el-table-column>
|
||||
<el-table-column prop="perms" label="权限标识" :show-overflow-tooltip="true"></el-table-column>
|
||||
<el-table-column prop="component" label="组件路径" :show-overflow-tooltip="true"></el-table-column>
|
||||
<el-table-column prop="status" label="状态" width="80">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="sys_normal_disable" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime">
|
||||
<template #default="scope">
|
||||
<span>{{ scope.row.createTime }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column fixed="right" label="操作" width="180">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="修改" placement="top">
|
||||
<el-button v-hasPermi="['system:menu:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="新增" placement="top">
|
||||
<el-button v-hasPermi="['system:menu:add']" link type="primary" icon="Plus" @click="handleAdd(scope.row)" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button v-hasPermi="['system:menu:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)" />
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<el-dialog v-model="dialog.visible" :title="dialog.title" destroy-on-close append-to-bod width="750px">
|
||||
<el-form ref="menuFormRef" :model="form" :rules="rules" label-width="100px">
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="上级菜单">
|
||||
<el-tree-select
|
||||
v-model="form.parentId"
|
||||
:data="menuOptions"
|
||||
:props="{ value: 'menuId', label: 'menuName', children: 'children' }"
|
||||
value-key="menuId"
|
||||
placeholder="选择上级菜单"
|
||||
check-strictly
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="菜单类型" prop="menuType">
|
||||
<el-radio-group v-model="form.menuType">
|
||||
<el-radio value="M">目录</el-radio>
|
||||
<el-radio value="C">菜单</el-radio>
|
||||
<el-radio value="F">按钮</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col v-if="form.menuType !== 'F'" :span="24">
|
||||
<el-form-item label="菜单图标" prop="icon">
|
||||
<!-- 图标选择器 -->
|
||||
<icon-select v-model="form.icon" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="菜单名称" prop="menuName">
|
||||
<el-input v-model="form.menuName" placeholder="请输入菜单名称" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="显示排序" prop="orderNum">
|
||||
<el-input-number v-model="form.orderNum" controls-position="right" :min="0" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col v-if="form.menuType !== 'F'" :span="12">
|
||||
<el-form-item>
|
||||
<template #label>
|
||||
<span>
|
||||
<el-tooltip content="选择是外链则路由地址需要以`http(s)://`开头" placement="top">
|
||||
<el-icon>
|
||||
<question-filled />
|
||||
</el-icon> </el-tooltip
|
||||
>是否外链
|
||||
</span>
|
||||
</template>
|
||||
<el-radio-group v-model="form.isFrame">
|
||||
<el-radio label="0">是</el-radio>
|
||||
<el-radio label="1">否</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col v-if="form.menuType !== 'F'" :span="12">
|
||||
<el-form-item prop="path">
|
||||
<template #label>
|
||||
<span>
|
||||
<el-tooltip content="访问的路由地址,如:`user`,如外网地址需内链访问则以`http(s)://`开头" placement="top">
|
||||
<el-icon>
|
||||
<question-filled />
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
路由地址
|
||||
</span>
|
||||
</template>
|
||||
<el-input v-model="form.path" placeholder="请输入路由地址" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col v-if="form.menuType === 'C'" :span="12">
|
||||
<el-form-item prop="component">
|
||||
<template #label>
|
||||
<span>
|
||||
<el-tooltip content="访问的组件路径,如:`system/user/index`,默认在`views`目录下" placement="top">
|
||||
<el-icon>
|
||||
<question-filled />
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
组件路径
|
||||
</span>
|
||||
</template>
|
||||
<el-input v-model="form.component" placeholder="请输入组件路径" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col v-if="form.menuType !== 'M'" :span="12">
|
||||
<el-form-item>
|
||||
<el-input v-model="form.perms" placeholder="请输入权限标识" maxlength="100" />
|
||||
<template #label>
|
||||
<span>
|
||||
<el-tooltip content="控制器中定义的权限字符,如:@SaCheckPermission('system:user:list')" placement="top">
|
||||
<el-icon>
|
||||
<question-filled />
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
权限字符
|
||||
</span>
|
||||
</template>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col v-if="form.menuType === 'C'" :span="12">
|
||||
<el-form-item>
|
||||
<el-input v-model="form.queryParam" placeholder="请输入路由参数" maxlength="255" />
|
||||
<template #label>
|
||||
<span>
|
||||
<el-tooltip content='访问路由的默认传递参数,如:`{"id": 1, "name": "ry"}`' placement="top">
|
||||
<el-icon>
|
||||
<question-filled />
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
路由参数
|
||||
</span>
|
||||
</template>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col v-if="form.menuType === 'C'" :span="12">
|
||||
<el-form-item>
|
||||
<template #label>
|
||||
<span>
|
||||
<el-tooltip content="选择是则会被`keep-alive`缓存,需要匹配组件的`name`和地址保持一致" placement="top">
|
||||
<el-icon>
|
||||
<question-filled />
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
是否缓存
|
||||
</span>
|
||||
</template>
|
||||
<el-radio-group v-model="form.isCache">
|
||||
<el-radio label="0">缓存</el-radio>
|
||||
<el-radio label="1">不缓存</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col v-if="form.menuType !== 'F'" :span="12">
|
||||
<el-form-item>
|
||||
<template #label>
|
||||
<span>
|
||||
<el-tooltip content="选择隐藏则路由将不会出现在侧边栏,但仍然可以访问" placement="top">
|
||||
<el-icon>
|
||||
<question-filled />
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
显示状态
|
||||
</span>
|
||||
</template>
|
||||
<el-radio-group v-model="form.visible">
|
||||
<el-radio v-for="dict in sys_show_hide" :key="dict.value" :label="dict.value">{{ dict.label }} </el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item>
|
||||
<template #label>
|
||||
<span>
|
||||
<el-tooltip content="选择停用则路由将不会出现在侧边栏,也不能被访问" placement="top">
|
||||
<el-icon>
|
||||
<question-filled />
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
菜单状态
|
||||
</span>
|
||||
</template>
|
||||
<el-radio-group v-model="form.status">
|
||||
<el-radio v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.value">
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="Menu" lang="ts">
|
||||
import { addMenu, delMenu, getMenu, listMenu, updateMenu } from '@/api/system/menu';
|
||||
import { MenuForm, MenuQuery, MenuVO } from '@/api/system/menu/types';
|
||||
import { MenuTypeEnum } from '@/enums/MenuTypeEnum';
|
||||
|
||||
interface MenuOptionsType {
|
||||
menuId: number;
|
||||
menuName: string;
|
||||
children: MenuOptionsType[] | undefined;
|
||||
}
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { sys_show_hide, sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_show_hide', 'sys_normal_disable'));
|
||||
|
||||
const menuList = ref<MenuVO[]>([]);
|
||||
const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const menuOptions = ref<MenuOptionsType[]>([]);
|
||||
const isExpandAll = ref(false);
|
||||
|
||||
const dialog = reactive<DialogOption>({
|
||||
visible: false,
|
||||
title: ''
|
||||
});
|
||||
|
||||
const queryFormRef = ref<ElFormInstance>();
|
||||
const menuFormRef = ref<ElFormInstance>();
|
||||
const initFormData = {
|
||||
path: '',
|
||||
menuId: undefined,
|
||||
parentId: 0,
|
||||
menuName: '',
|
||||
icon: '',
|
||||
menuType: MenuTypeEnum.M,
|
||||
orderNum: 1,
|
||||
isFrame: '1',
|
||||
isCache: '0',
|
||||
visible: '0',
|
||||
status: '0',
|
||||
menuSource: '2'
|
||||
};
|
||||
const data = reactive<PageData<MenuForm, MenuQuery>>({
|
||||
form: { ...initFormData },
|
||||
queryParams: {
|
||||
menuName: undefined,
|
||||
status: undefined,
|
||||
menuSource: 2
|
||||
},
|
||||
rules: {
|
||||
menuName: [{ required: true, message: '菜单名称不能为空', trigger: 'blur' }],
|
||||
orderNum: [{ required: true, message: '菜单顺序不能为空', trigger: 'blur' }],
|
||||
path: [{ required: true, message: '路由地址不能为空', trigger: 'blur' }]
|
||||
}
|
||||
});
|
||||
|
||||
const menuTableRef = ref<ElTableInstance>();
|
||||
|
||||
const { queryParams, form, rules } = toRefs<PageData<MenuForm, MenuQuery>>(data);
|
||||
/** 查询菜单列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
const res = await listMenu(queryParams.value);
|
||||
const data = proxy?.handleTree<MenuVO>(res.data, 'menuId');
|
||||
if (data) {
|
||||
menuList.value = data;
|
||||
}
|
||||
|
||||
loading.value = false;
|
||||
};
|
||||
/** 查询菜单下拉树结构 */
|
||||
const getTreeselect = async () => {
|
||||
menuOptions.value = [];
|
||||
const response = await listMenu();
|
||||
const menu: MenuOptionsType = { menuId: 0, menuName: '主类目', children: [] };
|
||||
menu.children = proxy?.handleTree<MenuOptionsType>(response.data, 'menuId');
|
||||
menuOptions.value.push(menu);
|
||||
};
|
||||
/** 取消按钮 */
|
||||
const cancel = () => {
|
||||
reset();
|
||||
dialog.visible = false;
|
||||
};
|
||||
/** 表单重置 */
|
||||
const reset = () => {
|
||||
form.value = { ...initFormData };
|
||||
menuFormRef.value?.resetFields();
|
||||
};
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
getList();
|
||||
};
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value?.resetFields();
|
||||
handleQuery();
|
||||
};
|
||||
/** 新增按钮操作 */
|
||||
const handleAdd = (row?: MenuVO) => {
|
||||
reset();
|
||||
getTreeselect();
|
||||
row && row.menuId ? (form.value.parentId = row.menuId) : (form.value.parentId = 0);
|
||||
dialog.visible = true;
|
||||
dialog.title = '添加菜单';
|
||||
};
|
||||
/** 展开/折叠操作 */
|
||||
const handleToggleExpandAll = () => {
|
||||
isExpandAll.value = !isExpandAll.value;
|
||||
toggleExpandAll(menuList.value, isExpandAll.value);
|
||||
};
|
||||
/** 展开/折叠所有 */
|
||||
const toggleExpandAll = (data: MenuVO[], status: boolean) => {
|
||||
data.forEach((item: MenuVO) => {
|
||||
menuTableRef.value?.toggleRowExpansion(item, status);
|
||||
if (item.children && item.children.length > 0) toggleExpandAll(item.children, status);
|
||||
});
|
||||
};
|
||||
/** 修改按钮操作 */
|
||||
const handleUpdate = async (row: MenuVO) => {
|
||||
reset();
|
||||
await getTreeselect();
|
||||
if (row.menuId) {
|
||||
const { data } = await getMenu(row.menuId);
|
||||
form.value = data;
|
||||
}
|
||||
dialog.visible = true;
|
||||
dialog.title = '修改菜单';
|
||||
};
|
||||
/** 提交按钮 */
|
||||
const submitForm = () => {
|
||||
menuFormRef.value?.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
form.value.menuId ? await updateMenu(form.value) : await addMenu(form.value);
|
||||
proxy?.$modal.msgSuccess('操作成功');
|
||||
dialog.visible = false;
|
||||
await getList();
|
||||
}
|
||||
});
|
||||
};
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (row: MenuVO) => {
|
||||
await proxy?.$modal.confirm('是否确认删除名称为"' + row.menuName + '"的数据项?');
|
||||
await delMenu(row.menuId);
|
||||
await getList();
|
||||
proxy?.$modal.msgSuccess('删除成功');
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
</script>
|
||||
160
src/views/system/appRole/authUser.vue
Normal file
160
src/views/system/appRole/authUser.vue
Normal file
@ -0,0 +1,160 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="search">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<el-form-item label="用户名称" prop="userName">
|
||||
<el-input v-model="queryParams.userName" placeholder="请输入用户名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号码" prop="phonenumber">
|
||||
<el-input v-model="queryParams.phonenumber" placeholder="请输入手机号码" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</transition>
|
||||
<el-card shadow="never">
|
||||
<template #header>
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['system:role:add']" type="primary" plain icon="Plus" @click="openSelectUser">添加用户</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['system:role:remove']" type="danger" plain icon="CircleClose" :disabled="multiple" @click="cancelAuthUserAll">
|
||||
批量取消授权
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="warning" plain icon="Close" @click="handleClose">关闭</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:show-search="showSearch" :search="true" @query-table="getList"></right-toolbar>
|
||||
</el-row>
|
||||
</template>
|
||||
<el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="用户名称" prop="userName" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="用户昵称" prop="nickName" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="邮箱" prop="email" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="手机" prop="phonenumber" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="状态" align="center" prop="status">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="sys_normal_disable" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template #default="scope">
|
||||
<span>{{ scope.row.createTime }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="取消授权" placement="top">
|
||||
<el-button v-hasPermi="['system:role:remove']" link type="primary" icon="CircleClose" @click="cancelAuthUser(scope.row)"> </el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
|
||||
<select-user ref="selectRef" :role-id="queryParams.roleId" @ok="handleQuery" />
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="AuthUser" lang="ts">
|
||||
import { allocatedUserList, authUserCancel, authUserCancelAll } from '@/api/system/role';
|
||||
import { UserQuery } from '@/api/system/user/types';
|
||||
import { UserVO } from '@/api/system/user/types';
|
||||
import SelectUser from './selectUser.vue';
|
||||
import { RouteLocationNormalized } from 'vue-router';
|
||||
|
||||
const route = useRoute();
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_normal_disable'));
|
||||
|
||||
const userList = ref<UserVO[]>([]);
|
||||
const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
const userIds = ref<Array<string | number>>([]);
|
||||
|
||||
const queryFormRef = ref<ElFormInstance>();
|
||||
const selectRef = ref<InstanceType<typeof SelectUser>>();
|
||||
|
||||
const queryParams = reactive<UserQuery>({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
roleId: route.params.roleId as string,
|
||||
userName: undefined,
|
||||
phonenumber: undefined
|
||||
});
|
||||
|
||||
/** 查询授权用户列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
const res = await allocatedUserList(queryParams);
|
||||
userList.value = res.rows;
|
||||
total.value = res.total;
|
||||
loading.value = false;
|
||||
};
|
||||
// 返回按钮
|
||||
const handleClose = () => {
|
||||
const obj: RouteLocationNormalized = {
|
||||
path: '/system/role',
|
||||
fullPath: '',
|
||||
hash: '',
|
||||
matched: [],
|
||||
meta: undefined,
|
||||
name: undefined,
|
||||
params: undefined,
|
||||
query: undefined,
|
||||
redirectedFrom: undefined
|
||||
};
|
||||
proxy?.$tab.closeOpenPage(obj);
|
||||
};
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNum = 1;
|
||||
getList();
|
||||
};
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value?.resetFields();
|
||||
handleQuery();
|
||||
};
|
||||
// 多选框选中数据
|
||||
const handleSelectionChange = (selection: UserVO[]) => {
|
||||
userIds.value = selection.map((item) => item.userId);
|
||||
multiple.value = !selection.length;
|
||||
};
|
||||
/** 打开授权用户表弹窗 */
|
||||
const openSelectUser = () => {
|
||||
selectRef.value?.show();
|
||||
};
|
||||
/** 取消授权按钮操作 */
|
||||
const cancelAuthUser = async (row: UserVO) => {
|
||||
await proxy?.$modal.confirm('确认要取消该用户"' + row.userName + '"角色吗?');
|
||||
await authUserCancel({ userId: row.userId, roleId: queryParams.roleId });
|
||||
await getList();
|
||||
proxy?.$modal.msgSuccess('取消授权成功');
|
||||
};
|
||||
/** 批量取消授权按钮操作 */
|
||||
const cancelAuthUserAll = async () => {
|
||||
const roleId = queryParams.roleId;
|
||||
const uIds = userIds.value.join(',');
|
||||
await proxy?.$modal.confirm('是否取消选中用户授权数据项?');
|
||||
await authUserCancelAll({ roleId: roleId, userIds: uIds });
|
||||
await getList();
|
||||
proxy?.$modal.msgSuccess('取消授权成功');
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
574
src/views/system/appRole/index.vue
Normal file
574
src/views/system/appRole/index.vue
Normal file
@ -0,0 +1,574 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<el-form-item label="角色名称" prop="roleName">
|
||||
<el-input v-model="queryParams.roleName" placeholder="请输入角色名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="权限字符" prop="roleKey">
|
||||
<el-input v-model="queryParams.roleKey" placeholder="请输入权限字符" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="角色状态" clearable>
|
||||
<el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery" v-hasPermi="['system:role:query']">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
<el-row :gutter="20">
|
||||
<!-- 部门树 -->
|
||||
<el-col :lg="4" :xs="24" style="">
|
||||
<el-card shadow="hover">
|
||||
<el-input v-model="deptName" placeholder="请输入部门名称" prefix-icon="Search" clearable />
|
||||
<el-tree
|
||||
ref="deptTreeRef"
|
||||
class="mt-2"
|
||||
node-key="id"
|
||||
:data="deptOptions"
|
||||
:props="{ label: 'label', children: 'children' }"
|
||||
:expand-on-click-node="false"
|
||||
:filter-node-method="filterNode"
|
||||
highlight-current
|
||||
default-expand-all
|
||||
@node-click="handleNodeClick"
|
||||
/>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :lg="20" :xs="24">
|
||||
<el-card shadow="hover">
|
||||
<template #header>
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['system:role:add']" type="primary" plain icon="Plus" @click="handleAdd()">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['system:role:edit']" type="success" plain :disabled="single" icon="Edit" @click="handleUpdate()"
|
||||
>修改</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['system:role:delete']" type="danger" plain :disabled="ids.length === 0" @click="handleDelete()"
|
||||
>删除</el-button
|
||||
>
|
||||
</el-col>
|
||||
<right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<el-table ref="roleTableRef" v-loading="loading" :data="roleList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column v-if="false" label="角色编号" prop="roleId" width="120" />
|
||||
<el-table-column label="角色名称" prop="roleName" :show-overflow-tooltip="true" width="150" />
|
||||
<el-table-column label="权限字符" prop="roleKey" :show-overflow-tooltip="true" width="200" />
|
||||
<el-table-column label="显示顺序" prop="roleSort" width="100" />
|
||||
<el-table-column label="状态" align="center" width="100">
|
||||
<template #default="scope">
|
||||
<el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime">
|
||||
<template #default="scope">
|
||||
<span>{{ proxy.parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column fixed="right" label="操作" width="180">
|
||||
<template #default="scope">
|
||||
<el-tooltip v-if="scope.row.roleId !== 1" content="修改" placement="top">
|
||||
<el-button v-hasPermi="['system:role:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip v-if="scope.row.roleId !== 1" content="删除" placement="top">
|
||||
<el-button v-hasPermi="['system:role:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip v-if="scope.row.roleId !== 1" content="数据权限" placement="top">
|
||||
<el-button v-hasPermi="['system:role:edit']" link type="primary" icon="CircleCheck" @click="handleDataScope(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip v-if="scope.row.roleId !== 1" content="分配用户" placement="top">
|
||||
<el-button v-hasPermi="['system:role:edit']" link type="primary" icon="User" @click="handleAuthUser(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-if="total > 0"
|
||||
v-model:total="total"
|
||||
v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
|
||||
<el-form ref="roleFormRef" :model="form" :rules="rules" label-width="110px">
|
||||
<el-form-item label="所属部门" prop="deptId">
|
||||
<el-cascader
|
||||
:options="deptOptions"
|
||||
v-model="form.deptId"
|
||||
placeholder="请选择所属部门"
|
||||
clearable
|
||||
filterable
|
||||
:show-all-levels="false"
|
||||
:props="{ value: 'id', emitPath: false, checkStrictly: true }"
|
||||
>
|
||||
</el-cascader>
|
||||
</el-form-item>
|
||||
<el-form-item label="角色名称" prop="roleName">
|
||||
<el-input v-model="form.roleName" placeholder="请输入角色名称" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="roleKey">
|
||||
<template #label>
|
||||
<span>
|
||||
<el-tooltip content="控制器中定义的权限字符,如:@SaCheckRole('admin')" placement="top">
|
||||
<el-icon><question-filled /></el-icon>
|
||||
</el-tooltip>
|
||||
权限字符
|
||||
</span>
|
||||
</template>
|
||||
<el-input v-model="form.roleKey" placeholder="请输入权限字符" />
|
||||
</el-form-item>
|
||||
<el-form-item label="角色顺序" prop="roleSort">
|
||||
<el-input-number v-model="form.roleSort" controls-position="right" :min="0" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-radio-group v-model="form.status">
|
||||
<el-radio v-for="dict in sys_normal_disable" :key="dict.value" :value="dict.value">{{ dict.label }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="菜单权限">
|
||||
<el-checkbox v-model="menuExpand" @change="handleCheckedTreeExpand(Boolean($event), 'menu')">展开/折叠</el-checkbox>
|
||||
<el-checkbox v-model="menuNodeAll" @change="handleCheckedTreeNodeAll($event, 'menu')">全选/全不选</el-checkbox>
|
||||
<el-checkbox v-model="form.menuCheckStrictly" @change="handleCheckedTreeConnect($event, 'menu')">父子联动</el-checkbox>
|
||||
<el-tree
|
||||
ref="menuRef"
|
||||
class="tree-border"
|
||||
:data="menuOptions"
|
||||
show-checkbox
|
||||
node-key="id"
|
||||
:check-strictly="!form.menuCheckStrictly"
|
||||
empty-text="加载中,请稍候"
|
||||
:props="{ label: 'label', children: 'children' }"
|
||||
></el-tree>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否为特殊角色">
|
||||
<el-switch v-model="form.isSpecial" active-value="1" inactive-value="0" active-text="是" inactive-text="否"> </el-switch>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 分配角色数据权限对话框 -->
|
||||
<el-dialog v-model="openDataScope" :title="dialog.title" width="500px" append-to-body>
|
||||
<el-form ref="dataScopeRef" :model="form" label-width="80px">
|
||||
<el-form-item label="角色名称">
|
||||
<el-input v-model="form.roleName" :disabled="true" />
|
||||
</el-form-item>
|
||||
<el-form-item label="权限字符">
|
||||
<el-input v-model="form.roleKey" :disabled="true" />
|
||||
</el-form-item>
|
||||
<el-form-item label="权限范围">
|
||||
<el-select v-model="form.dataScope" @change="dataScopeSelectChange">
|
||||
<el-option v-for="item in dataScopeOptions" :key="item.value" :label="item.label" :value="item.value"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-show="form.dataScope === '2'" label="数据权限">
|
||||
<el-checkbox v-model="deptExpand" @change="handleCheckedTreeExpand(Boolean($event), 'dept')">展开/折叠</el-checkbox>
|
||||
<el-checkbox v-model="deptNodeAll" @change="handleCheckedTreeNodeAll($event, 'dept')">全选/全不选</el-checkbox>
|
||||
<el-checkbox v-model="form.deptCheckStrictly" @change="handleCheckedTreeConnect($event, 'dept')">父子联动</el-checkbox>
|
||||
<el-tree
|
||||
ref="deptRef"
|
||||
class="tree-border"
|
||||
:data="deptOptions"
|
||||
show-checkbox
|
||||
default-expand-all
|
||||
node-key="id"
|
||||
:check-strictly="!form.deptCheckStrictly"
|
||||
empty-text="加载中,请稍候"
|
||||
:props="{ label: 'label', children: 'children' }"
|
||||
></el-tree>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submitDataScope">确 定</el-button>
|
||||
<el-button @click="cancelDataScope">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="Role" lang="ts">
|
||||
import { addRole, changeRoleStatus, dataScope, delRole, getRole, listRole, updateRole, deptTreeSelect } from '@/api/system/role';
|
||||
import { roleMenuTreeselect, treeselect as menuTreeselect } from '@/api/system/menu/index';
|
||||
import { RoleVO, RoleForm, RoleQuery, DeptTreeOption } from '@/api/system/role/types';
|
||||
import { MenuTreeOption, RoleMenuTree } from '@/api/system/menu/types';
|
||||
import api, { uploadCertList } from '@/api/system/user';
|
||||
import { DeptTreeVO, DeptVO } from '@/api/system/dept/types';
|
||||
|
||||
const router = useRouter();
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_normal_disable'));
|
||||
|
||||
const roleList = ref<RoleVO[]>();
|
||||
const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref<Array<string | number>>([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
|
||||
const menuOptions = ref<MenuTreeOption[]>([]);
|
||||
const menuExpand = ref(false);
|
||||
const menuNodeAll = ref(false);
|
||||
const deptExpand = ref(true);
|
||||
const deptNodeAll = ref(false);
|
||||
const deptOptions = ref<DeptTreeVO[]>([]);
|
||||
const enabledDeptOptions = ref<DeptTreeVO[]>([]);
|
||||
|
||||
const openDataScope = ref(false);
|
||||
const deptName = ref('');
|
||||
|
||||
/** 数据范围选项*/
|
||||
const dataScopeOptions = ref([
|
||||
{ value: '1', label: '全部数据权限' },
|
||||
{ value: '2', label: '自定数据权限' },
|
||||
{ value: '3', label: '本部门数据权限' },
|
||||
{ value: '4', label: '本部门及以下数据权限' },
|
||||
{ value: '5', label: '仅本人数据权限' },
|
||||
{ value: '6', label: '部门及以下或本人数据权限' }
|
||||
]);
|
||||
|
||||
const queryFormRef = ref<ElFormInstance>();
|
||||
const roleFormRef = ref<ElFormInstance>();
|
||||
const dataScopeRef = ref<ElFormInstance>();
|
||||
const menuRef = ref<ElTreeInstance>();
|
||||
const deptRef = ref<ElTreeInstance>();
|
||||
const deptTreeRef = ref<ElTreeInstance>();
|
||||
|
||||
const initForm = {
|
||||
roleId: undefined,
|
||||
roleSort: 1,
|
||||
status: '0',
|
||||
roleName: '',
|
||||
roleKey: '',
|
||||
menuCheckStrictly: true,
|
||||
deptCheckStrictly: true,
|
||||
remark: '',
|
||||
dataScope: '1',
|
||||
menuIds: [],
|
||||
deptId: '',
|
||||
isSpecial: null,
|
||||
deptIds: [],
|
||||
roleSource: '2'
|
||||
};
|
||||
|
||||
const data = reactive({
|
||||
form: { ...initForm },
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
roleName: '',
|
||||
roleKey: '',
|
||||
deptId: '',
|
||||
status: '',
|
||||
roleSource: '2'
|
||||
},
|
||||
rules: {
|
||||
roleName: [{ required: true, message: '角色名称不能为空', trigger: 'blur' }],
|
||||
roleKey: [{ required: true, message: '权限字符不能为空', trigger: 'blur' }],
|
||||
roleSort: [{ required: true, message: '角色顺序不能为空', trigger: 'blur' }]
|
||||
}
|
||||
});
|
||||
const { form, queryParams, rules } = toRefs(data);
|
||||
|
||||
const dialog = reactive<DialogOption>({
|
||||
visible: false,
|
||||
title: ''
|
||||
});
|
||||
|
||||
/** 通过条件过滤节点 */
|
||||
const filterNode = (value: string, data: any) => {
|
||||
if (!value) return true;
|
||||
return data.label.indexOf(value) !== -1;
|
||||
};
|
||||
|
||||
/** 节点单击事件 */
|
||||
const handleNodeClick = (data: DeptVO) => {
|
||||
queryParams.value.deptId = data.id as string;
|
||||
handleQuery();
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询角色列表
|
||||
*/
|
||||
const getList = () => {
|
||||
loading.value = true;
|
||||
listRole(proxy?.addDateRange(queryParams.value, dateRange.value)).then((res) => {
|
||||
roleList.value = res.rows;
|
||||
total.value = res.total;
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 搜索按钮操作
|
||||
*/
|
||||
const handleQuery = () => {
|
||||
queryParams.value.pageNum = 1;
|
||||
getList();
|
||||
};
|
||||
|
||||
/** 重置 */
|
||||
const resetQuery = () => {
|
||||
dateRange.value = ['', ''];
|
||||
queryFormRef.value?.resetFields();
|
||||
queryParams.value.pageNum = 1;
|
||||
queryParams.value.deptId = undefined;
|
||||
deptTreeRef.value?.setCurrentKey(undefined);
|
||||
handleQuery();
|
||||
};
|
||||
/**删除按钮操作 */
|
||||
const handleDelete = async (row?: RoleVO) => {
|
||||
const roleids = row?.roleId || ids.value;
|
||||
await proxy?.$modal.confirm('是否确认删除角色编号为' + roleids + '数据项目');
|
||||
await delRole(roleids);
|
||||
getList();
|
||||
proxy?.$modal.msgSuccess('删除成功');
|
||||
};
|
||||
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = () => {
|
||||
proxy?.download(
|
||||
'system/role/export',
|
||||
{
|
||||
...queryParams.value
|
||||
},
|
||||
`role_${new Date().getTime()}.xlsx`
|
||||
);
|
||||
};
|
||||
/** 多选框选中数据 */
|
||||
const handleSelectionChange = (selection: RoleVO[]) => {
|
||||
ids.value = selection.map((item: RoleVO) => item.roleId);
|
||||
single.value = selection.length != 1;
|
||||
multiple.value = !selection.length;
|
||||
};
|
||||
|
||||
/** 角色状态修改 */
|
||||
const handleStatusChange = async (row: RoleVO) => {
|
||||
let text = row.status === '0' ? '启用' : '停用';
|
||||
try {
|
||||
await proxy?.$modal.confirm('确认要"' + text + '""' + row.roleName + '"角色吗?');
|
||||
await changeRoleStatus(row.roleId, row.status);
|
||||
proxy?.$modal.msgSuccess(text + '成功');
|
||||
} catch {
|
||||
row.status = row.status === '0' ? '1' : '0';
|
||||
}
|
||||
};
|
||||
|
||||
/** 分配用户 */
|
||||
const handleAuthUser = (row: RoleVO) => {
|
||||
router.push('/system/role-auth/user/' + row.roleId);
|
||||
};
|
||||
|
||||
/** 查询菜单树结构 */
|
||||
const getMenuTreeselect = async () => {
|
||||
const res = await menuTreeselect({ menuSource: '2' });
|
||||
menuOptions.value = res.data;
|
||||
};
|
||||
/** 所有部门节点数据 */
|
||||
const getDeptAllCheckedKeys = (): any => {
|
||||
// 目前被选中的部门节点
|
||||
let checkedKeys = deptRef.value?.getCheckedKeys();
|
||||
// 半选中的部门节点
|
||||
let halfCheckedKeys = deptRef.value?.getHalfCheckedKeys();
|
||||
if (halfCheckedKeys) {
|
||||
checkedKeys?.unshift.apply(checkedKeys, halfCheckedKeys);
|
||||
}
|
||||
return checkedKeys;
|
||||
};
|
||||
/** 重置新增的表单以及其他数据 */
|
||||
const reset = () => {
|
||||
menuRef.value?.setCheckedKeys([]);
|
||||
menuExpand.value = false;
|
||||
menuNodeAll.value = false;
|
||||
deptExpand.value = true;
|
||||
deptNodeAll.value = false;
|
||||
form.value = { ...initForm };
|
||||
roleFormRef.value?.resetFields();
|
||||
};
|
||||
|
||||
/** 添加角色 */
|
||||
const handleAdd = () => {
|
||||
reset();
|
||||
getMenuTreeselect();
|
||||
dialog.visible = true;
|
||||
dialog.title = '添加角色';
|
||||
};
|
||||
/** 修改角色 */
|
||||
const handleUpdate = async (row?: RoleVO) => {
|
||||
reset();
|
||||
const roleId = row?.roleId || ids.value[0];
|
||||
const { data } = await getRole(roleId);
|
||||
Object.assign(form.value, data);
|
||||
form.value.roleSort = Number(form.value.roleSort);
|
||||
const res = await getRoleMenuTreeselect(roleId);
|
||||
dialog.title = '修改角色';
|
||||
dialog.visible = true;
|
||||
res.checkedKeys.forEach((v) => {
|
||||
nextTick(() => {
|
||||
menuRef.value?.setChecked(v, true, false);
|
||||
});
|
||||
});
|
||||
};
|
||||
/** 根据角色ID查询菜单树结构 */
|
||||
const getRoleMenuTreeselect = (roleId: string | number) => {
|
||||
return roleMenuTreeselect(roleId, { menuSource: '2' }).then((res): RoleMenuTree => {
|
||||
menuOptions.value = res.data.menus;
|
||||
return res.data;
|
||||
});
|
||||
};
|
||||
/** 根据角色ID查询部门树结构 */
|
||||
const getRoleDeptTreeSelect = async (roleId: string | number) => {
|
||||
const res = await deptTreeSelect(roleId, { roleSource: '2' });
|
||||
deptOptions.value = res.data.depts;
|
||||
return res.data;
|
||||
};
|
||||
/** 树权限(展开/折叠)*/
|
||||
const handleCheckedTreeExpand = (value: boolean, type: string) => {
|
||||
if (type == 'menu') {
|
||||
let treeList = menuOptions.value;
|
||||
for (let i = 0; i < treeList.length; i++) {
|
||||
if (menuRef.value) {
|
||||
menuRef.value.store.nodesMap[treeList[i].id].expanded = value;
|
||||
}
|
||||
}
|
||||
} else if (type == 'dept') {
|
||||
let treeList = deptOptions.value;
|
||||
for (let i = 0; i < treeList.length; i++) {
|
||||
if (deptRef.value) {
|
||||
deptRef.value.store.nodesMap[treeList[i].id].expanded = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
/** 树权限(全选/全不选) */
|
||||
const handleCheckedTreeNodeAll = (value: any, type: string) => {
|
||||
if (type == 'menu') {
|
||||
menuRef.value?.setCheckedNodes(value ? (menuOptions.value as any) : []);
|
||||
} else if (type == 'dept') {
|
||||
deptRef.value?.setCheckedNodes(value ? (deptOptions.value as any) : []);
|
||||
}
|
||||
};
|
||||
/** 树权限(父子联动) */
|
||||
const handleCheckedTreeConnect = (value: any, type: string) => {
|
||||
if (type == 'menu') {
|
||||
form.value.menuCheckStrictly = value;
|
||||
} else if (type == 'dept') {
|
||||
form.value.deptCheckStrictly = value;
|
||||
}
|
||||
};
|
||||
/** 所有菜单节点数据 */
|
||||
const getMenuAllCheckedKeys = (): any => {
|
||||
// 目前被选中的菜单节点
|
||||
let checkedKeys = menuRef.value?.getCheckedKeys();
|
||||
// 半选中的菜单节点
|
||||
let halfCheckedKeys = menuRef.value?.getHalfCheckedKeys();
|
||||
if (halfCheckedKeys) {
|
||||
checkedKeys?.unshift.apply(checkedKeys, halfCheckedKeys);
|
||||
}
|
||||
return checkedKeys;
|
||||
};
|
||||
/** 提交按钮 */
|
||||
const submitForm = () => {
|
||||
roleFormRef.value?.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
form.value.menuIds = getMenuAllCheckedKeys();
|
||||
form.value.roleId ? await updateRole(form.value) : await addRole(form.value);
|
||||
proxy?.$modal.msgSuccess('操作成功');
|
||||
dialog.visible = false;
|
||||
getList();
|
||||
}
|
||||
});
|
||||
};
|
||||
/** 取消按钮 */
|
||||
const cancel = () => {
|
||||
reset();
|
||||
dialog.visible = false;
|
||||
};
|
||||
/** 选择角色权限范围触发 */
|
||||
const dataScopeSelectChange = (value: string) => {
|
||||
if (value !== '2') {
|
||||
deptRef.value?.setCheckedKeys([]);
|
||||
}
|
||||
};
|
||||
/** 分配数据权限操作 */
|
||||
const handleDataScope = async (row: RoleVO) => {
|
||||
const response = await getRole(row.roleId);
|
||||
Object.assign(form.value, response.data);
|
||||
const res = await getRoleDeptTreeSelect(row.roleId);
|
||||
openDataScope.value = true;
|
||||
dialog.title = '分配数据权限';
|
||||
await nextTick(() => {
|
||||
deptRef.value?.setCheckedKeys(res.checkedKeys);
|
||||
});
|
||||
};
|
||||
/** 提交按钮(数据权限) */
|
||||
const submitDataScope = async () => {
|
||||
if (form.value.roleId) {
|
||||
form.value.deptIds = getDeptAllCheckedKeys();
|
||||
await dataScope(form.value);
|
||||
proxy?.$modal.msgSuccess('修改成功');
|
||||
openDataScope.value = false;
|
||||
getList();
|
||||
}
|
||||
};
|
||||
/** 取消按钮(数据权限)*/
|
||||
const cancelDataScope = () => {
|
||||
dataScopeRef.value?.resetFields();
|
||||
form.value = { ...initForm };
|
||||
openDataScope.value = false;
|
||||
};
|
||||
/** 查询部门下拉树结构 */
|
||||
const getDeptTree = async () => {
|
||||
const res = await api.deptTreeSelect({ isShow: '1' });
|
||||
deptOptions.value = res.data;
|
||||
enabledDeptOptions.value = filterDisabledDept(res.data);
|
||||
};
|
||||
|
||||
/** 过滤禁用的部门 */
|
||||
const filterDisabledDept = (deptList: DeptTreeVO[]) => {
|
||||
return deptList.filter((dept) => {
|
||||
if (dept.disabled) {
|
||||
return false;
|
||||
}
|
||||
if (dept.children && dept.children.length) {
|
||||
dept.children = filterDisabledDept(dept.children);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getDeptTree(); // 初始化部门数据
|
||||
getList();
|
||||
});
|
||||
</script>
|
||||
132
src/views/system/appRole/selectUser.vue
Normal file
132
src/views/system/appRole/selectUser.vue
Normal file
@ -0,0 +1,132 @@
|
||||
<template>
|
||||
<el-row>
|
||||
<el-dialog v-model="visible" title="选择用户" width="800px" top="5vh" append-to-body>
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<el-form-item label="用户名称" prop="userName">
|
||||
<el-input v-model="queryParams.userName" placeholder="请输入用户名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号码" prop="phonenumber">
|
||||
<el-input v-model="queryParams.phonenumber" placeholder="请输入手机号码" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-row>
|
||||
<el-table ref="tableRef" :data="userList" height="260px" @row-click="clickRow" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55"></el-table-column>
|
||||
<el-table-column label="用户名称" prop="userName" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="用户昵称" prop="nickName" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="邮箱" prop="email" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="手机" prop="phonenumber" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="状态" align="center" prop="status">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="sys_normal_disable" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template #default="scope">
|
||||
<span>{{ proxy.parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination v-if="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
|
||||
</el-row>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="handleSelectUser">确 定</el-button>
|
||||
<el-button @click="visible = false">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script setup name="SelectUser" lang="ts">
|
||||
import { authUserSelectAll, unallocatedUserList } from '@/api/system/role';
|
||||
import { UserVO } from '@/api/system/user/types';
|
||||
import { UserQuery } from '@/api/system/user/types';
|
||||
|
||||
const props = defineProps({
|
||||
roleId: {
|
||||
type: [Number, String],
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_normal_disable'));
|
||||
|
||||
const userList = ref<UserVO[]>([]);
|
||||
const visible = ref(false);
|
||||
const total = ref(0);
|
||||
const userIds = ref<Array<string | number>>([]);
|
||||
|
||||
const queryParams = reactive<UserQuery>({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
roleId: undefined,
|
||||
userName: undefined,
|
||||
phonenumber: undefined
|
||||
});
|
||||
|
||||
const tableRef = ref<ElTableInstance>();
|
||||
const queryFormRef = ref<ElFormInstance>();
|
||||
|
||||
const show = () => {
|
||||
queryParams.roleId = props.roleId;
|
||||
getList();
|
||||
visible.value = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* 选择行
|
||||
*/
|
||||
const clickRow = (row: any) => {
|
||||
// ele的bug
|
||||
tableRef.value?.toggleRowSelection(row, false);
|
||||
};
|
||||
/** 多选框选中数据 */
|
||||
const handleSelectionChange = (selection: UserVO[]) => {
|
||||
userIds.value = selection.map((item: UserVO) => item.userId);
|
||||
};
|
||||
|
||||
/** 查询数据 */
|
||||
const getList = async () => {
|
||||
const res = await unallocatedUserList(queryParams);
|
||||
userList.value = res.rows;
|
||||
total.value = res.total;
|
||||
};
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNum = 1;
|
||||
getList();
|
||||
};
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value?.resetFields();
|
||||
getList();
|
||||
};
|
||||
|
||||
const emit = defineEmits(['ok']);
|
||||
/**选择授权用户操作 */
|
||||
const handleSelectUser = async () => {
|
||||
const roleId = queryParams.roleId;
|
||||
const ids = userIds.value.join(',');
|
||||
if (ids == '') {
|
||||
proxy?.$modal.msgError('请选择要分配的用户');
|
||||
return;
|
||||
}
|
||||
await authUserSelectAll({ roleId, userIds: ids });
|
||||
proxy?.$modal.msgSuccess('分配成功');
|
||||
emit('ok');
|
||||
visible.value = false;
|
||||
};
|
||||
// 暴露
|
||||
defineExpose({
|
||||
show
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@ -34,6 +34,7 @@
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<!-- 关键修改:开启lazy模式,使用本地数据筛选子节点 -->
|
||||
<el-table
|
||||
ref="menuTableRef"
|
||||
v-loading="loading"
|
||||
@ -41,6 +42,9 @@
|
||||
row-key="menuId"
|
||||
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
|
||||
:default-expand-all="isExpandAll"
|
||||
lazy
|
||||
:load="loadChildren"
|
||||
:has-children="hasChildren"
|
||||
>
|
||||
<el-table-column prop="menuName" label="菜单名称" :show-overflow-tooltip="true" width="160"></el-table-column>
|
||||
<el-table-column prop="icon" label="图标" align="center" width="100">
|
||||
@ -77,7 +81,7 @@
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<el-dialog v-model="dialog.visible" :title="dialog.title" destroy-on-close append-to-bod width="750px">
|
||||
<el-dialog v-model="dialog.visible" :title="dialog.title" destroy-on-close append-to-body width="750px">
|
||||
<el-form ref="menuFormRef" :model="form" :rules="rules" label-width="100px">
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
@ -103,7 +107,6 @@
|
||||
</el-col>
|
||||
<el-col v-if="form.menuType !== 'F'" :span="24">
|
||||
<el-form-item label="菜单图标" prop="icon">
|
||||
<!-- 图标选择器 -->
|
||||
<icon-select v-model="form.icon" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@ -122,10 +125,9 @@
|
||||
<template #label>
|
||||
<span>
|
||||
<el-tooltip content="选择是外链则路由地址需要以`http(s)://`开头" placement="top">
|
||||
<el-icon>
|
||||
<question-filled />
|
||||
</el-icon> </el-tooltip
|
||||
>是否外链
|
||||
<el-icon><question-filled /></el-icon>
|
||||
</el-tooltip>
|
||||
是否外链
|
||||
</span>
|
||||
</template>
|
||||
<el-radio-group v-model="form.isFrame">
|
||||
@ -139,9 +141,7 @@
|
||||
<template #label>
|
||||
<span>
|
||||
<el-tooltip content="访问的路由地址,如:`user`,如外网地址需内链访问则以`http(s)://`开头" placement="top">
|
||||
<el-icon>
|
||||
<question-filled />
|
||||
</el-icon>
|
||||
<el-icon><question-filled /></el-icon>
|
||||
</el-tooltip>
|
||||
路由地址
|
||||
</span>
|
||||
@ -154,9 +154,7 @@
|
||||
<template #label>
|
||||
<span>
|
||||
<el-tooltip content="访问的组件路径,如:`system/user/index`,默认在`views`目录下" placement="top">
|
||||
<el-icon>
|
||||
<question-filled />
|
||||
</el-icon>
|
||||
<el-icon><question-filled /></el-icon>
|
||||
</el-tooltip>
|
||||
组件路径
|
||||
</span>
|
||||
@ -170,9 +168,7 @@
|
||||
<template #label>
|
||||
<span>
|
||||
<el-tooltip content="控制器中定义的权限字符,如:@SaCheckPermission('system:user:list')" placement="top">
|
||||
<el-icon>
|
||||
<question-filled />
|
||||
</el-icon>
|
||||
<el-icon><question-filled /></el-icon>
|
||||
</el-tooltip>
|
||||
权限字符
|
||||
</span>
|
||||
@ -185,9 +181,7 @@
|
||||
<template #label>
|
||||
<span>
|
||||
<el-tooltip content='访问路由的默认传递参数,如:`{"id": 1, "name": "ry"}`' placement="top">
|
||||
<el-icon>
|
||||
<question-filled />
|
||||
</el-icon>
|
||||
<el-icon><question-filled /></el-icon>
|
||||
</el-tooltip>
|
||||
路由参数
|
||||
</span>
|
||||
@ -199,9 +193,7 @@
|
||||
<template #label>
|
||||
<span>
|
||||
<el-tooltip content="选择是则会被`keep-alive`缓存,需要匹配组件的`name`和地址保持一致" placement="top">
|
||||
<el-icon>
|
||||
<question-filled />
|
||||
</el-icon>
|
||||
<el-icon><question-filled /></el-icon>
|
||||
</el-tooltip>
|
||||
是否缓存
|
||||
</span>
|
||||
@ -217,9 +209,7 @@
|
||||
<template #label>
|
||||
<span>
|
||||
<el-tooltip content="选择隐藏则路由将不会出现在侧边栏,但仍然可以访问" placement="top">
|
||||
<el-icon>
|
||||
<question-filled />
|
||||
</el-icon>
|
||||
<el-icon><question-filled /></el-icon>
|
||||
</el-tooltip>
|
||||
显示状态
|
||||
</span>
|
||||
@ -234,9 +224,7 @@
|
||||
<template #label>
|
||||
<span>
|
||||
<el-tooltip content="选择停用则路由将不会出现在侧边栏,也不能被访问" placement="top">
|
||||
<el-icon>
|
||||
<question-filled />
|
||||
</el-icon>
|
||||
<el-icon><question-filled /></el-icon>
|
||||
</el-tooltip>
|
||||
菜单状态
|
||||
</span>
|
||||
@ -264,6 +252,20 @@
|
||||
import { addMenu, delMenu, getMenu, listMenu, updateMenu } from '@/api/system/menu';
|
||||
import { MenuForm, MenuQuery, MenuVO } from '@/api/system/menu/types';
|
||||
import { MenuTypeEnum } from '@/enums/MenuTypeEnum';
|
||||
import { ElFormInstance, ElTableInstance } from 'element-plus';
|
||||
import { ComponentInternalInstance, getCurrentInstance, onMounted, ref, reactive, toRefs } from 'vue';
|
||||
|
||||
// 类型定义
|
||||
interface DialogOption {
|
||||
visible: boolean;
|
||||
title: string;
|
||||
}
|
||||
|
||||
interface PageData<T, U> {
|
||||
form: T;
|
||||
queryParams: U;
|
||||
rules: Record<string, any[]>;
|
||||
}
|
||||
|
||||
interface MenuOptionsType {
|
||||
menuId: number;
|
||||
@ -271,20 +273,29 @@ interface MenuOptionsType {
|
||||
children: MenuOptionsType[] | undefined;
|
||||
}
|
||||
|
||||
// 全局变量
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { sys_show_hide, sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_show_hide', 'sys_normal_disable'));
|
||||
|
||||
// 1. 存储完整数据的数组(一次性获取后本地保存)
|
||||
const fullMenuList = ref<MenuVO[]>([]);
|
||||
// 2. 子节点缓存(优化查询性能)
|
||||
const childrenCache = ref<Record<number, MenuVO[]>>({});
|
||||
|
||||
// 页面状态
|
||||
const menuList = ref<MenuVO[]>([]);
|
||||
const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const menuOptions = ref<MenuOptionsType[]>([]);
|
||||
const isExpandAll = ref(false);
|
||||
|
||||
// 弹窗状态
|
||||
const dialog = reactive<DialogOption>({
|
||||
visible: false,
|
||||
title: ''
|
||||
});
|
||||
|
||||
// 表单相关
|
||||
const queryFormRef = ref<ElFormInstance>();
|
||||
const menuFormRef = ref<ElFormInstance>();
|
||||
const initFormData = {
|
||||
@ -298,35 +309,114 @@ const initFormData = {
|
||||
isFrame: '1',
|
||||
isCache: '0',
|
||||
visible: '0',
|
||||
status: '0'
|
||||
status: '0',
|
||||
menuSource: 1
|
||||
};
|
||||
const data = reactive<PageData<MenuForm, MenuQuery>>({
|
||||
const data = reactive({
|
||||
form: { ...initFormData },
|
||||
queryParams: {
|
||||
menuName: undefined,
|
||||
status: undefined
|
||||
status: undefined,
|
||||
menuSource: 1
|
||||
},
|
||||
rules: {
|
||||
menuName: [{ required: true, message: '菜单名称不能为空', trigger: 'blur' }],
|
||||
orderNum: [{ required: true, message: '菜单顺序不能为空', trigger: 'blur' }],
|
||||
path: [{ required: true, message: '路由地址不能为空', trigger: 'blur' }]
|
||||
path: [
|
||||
{
|
||||
required: (() => data.form.menuType !== 'F') as any,
|
||||
message: '路由地址不能为空',
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
const menuTableRef = ref<ElTableInstance>();
|
||||
|
||||
const { queryParams, form, rules } = toRefs<PageData<MenuForm, MenuQuery>>(data);
|
||||
/** 查询菜单列表 */
|
||||
|
||||
/**
|
||||
* 初始化加载:一次性获取所有数据并处理
|
||||
* 仅在表格展示根节点,子节点通过懒加载从本地数据中筛选
|
||||
*/
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
const res = await listMenu(queryParams.value);
|
||||
const data = proxy?.handleTree<MenuVO>(res.data, 'menuId');
|
||||
if (data) {
|
||||
menuList.value = data;
|
||||
}
|
||||
try {
|
||||
// 1. 一次性获取所有数据
|
||||
const res = await listMenu(queryParams.value);
|
||||
fullMenuList.value = res.data || [];
|
||||
|
||||
loading.value = false;
|
||||
// 2. 构建缓存(提前计算所有节点的子节点)
|
||||
buildChildrenCache();
|
||||
console.log(11111);
|
||||
|
||||
// 3. 只展示根节点(parentId=0)
|
||||
menuList.value = filterChildren(0);
|
||||
} catch (err) {
|
||||
proxy?.$modal.msgError('加载菜单失败');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 构建子节点缓存
|
||||
* 一次性处理所有数据,建立父ID到子节点列表的映射
|
||||
*/
|
||||
const buildChildrenCache = () => {
|
||||
const cache: Record<number, MenuVO[]> = {};
|
||||
|
||||
// 初始化所有可能的父ID
|
||||
fullMenuList.value.forEach((item) => {
|
||||
if (!cache[item.parentId!]) {
|
||||
cache[item.parentId!] = [];
|
||||
}
|
||||
});
|
||||
// 为每个父ID添加子节点
|
||||
fullMenuList.value.forEach((item) => {
|
||||
if (cache[item.parentId!]) {
|
||||
// 标记节点是否有子节点(用于显示展开图标)
|
||||
const hasChildren = fullMenuList.value.some((child) => child.parentId == item.menuId);
|
||||
cache[item.parentId!].push({ ...item, hasChildren });
|
||||
}
|
||||
});
|
||||
// 按排序号排序子节点
|
||||
// Object.keys(cache).forEach((parentId) => {
|
||||
// cache[Number(parentId)].sort((a, b) => (a.orderNum || 0) - (b.orderNum || 0));
|
||||
// });
|
||||
|
||||
childrenCache.value = cache;
|
||||
};
|
||||
|
||||
/**
|
||||
* 从缓存中筛选指定父节点的子节点
|
||||
* @param parentId 父节点ID
|
||||
* @returns 子节点列表
|
||||
*/
|
||||
const filterChildren = (parentId: number): MenuVO[] => {
|
||||
return childrenCache.value[parentId] || [];
|
||||
};
|
||||
|
||||
/**
|
||||
* 懒加载子节点实现
|
||||
* 从本地缓存中获取子节点,不发起额外请求
|
||||
*/
|
||||
const loadChildren = async (row, treeNode: any, resolve: (data: MenuVO[]) => void) => {
|
||||
const parentId = row.menuId;
|
||||
console.log(childrenCache.value);
|
||||
// 从缓存获取子节点并返回
|
||||
resolve(filterChildren(parentId));
|
||||
};
|
||||
|
||||
/**
|
||||
* 判断节点是否有子节点
|
||||
* @param row 节点数据
|
||||
* @returns 是否有子节点
|
||||
*/
|
||||
const hasChildren = (row: MenuVO) => {
|
||||
return row.hasChildren || false;
|
||||
};
|
||||
|
||||
/** 查询菜单下拉树结构 */
|
||||
const getTreeselect = async () => {
|
||||
menuOptions.value = [];
|
||||
@ -335,11 +425,13 @@ const getTreeselect = async () => {
|
||||
menu.children = proxy?.handleTree<MenuOptionsType>(response.data, 'menuId');
|
||||
menuOptions.value.push(menu);
|
||||
};
|
||||
|
||||
/** 取消按钮 */
|
||||
const cancel = () => {
|
||||
reset();
|
||||
dialog.visible = false;
|
||||
};
|
||||
|
||||
/** 表单重置 */
|
||||
const reset = () => {
|
||||
form.value = { ...initFormData };
|
||||
@ -350,11 +442,13 @@ const reset = () => {
|
||||
const handleQuery = () => {
|
||||
getList();
|
||||
};
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value?.resetFields();
|
||||
handleQuery();
|
||||
};
|
||||
|
||||
/** 新增按钮操作 */
|
||||
const handleAdd = (row?: MenuVO) => {
|
||||
reset();
|
||||
@ -363,18 +457,24 @@ const handleAdd = (row?: MenuVO) => {
|
||||
dialog.visible = true;
|
||||
dialog.title = '添加菜单';
|
||||
};
|
||||
|
||||
/** 展开/折叠操作 */
|
||||
const handleToggleExpandAll = () => {
|
||||
isExpandAll.value = !isExpandAll.value;
|
||||
toggleExpandAll(menuList.value, isExpandAll.value);
|
||||
};
|
||||
/** 展开/折叠所有 */
|
||||
|
||||
/** 展开/折叠所有节点 */
|
||||
const toggleExpandAll = (data: MenuVO[], status: boolean) => {
|
||||
data.forEach((item: MenuVO) => {
|
||||
menuTableRef.value?.toggleRowExpansion(item, status);
|
||||
if (item.children && item.children.length > 0) toggleExpandAll(item.children, status);
|
||||
// 如果有子节点,递归处理
|
||||
if (item.hasChildren) {
|
||||
toggleExpandAll(filterChildren(item.menuId!), status);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/** 修改按钮操作 */
|
||||
const handleUpdate = async (row: MenuVO) => {
|
||||
reset();
|
||||
@ -386,25 +486,38 @@ const handleUpdate = async (row: MenuVO) => {
|
||||
dialog.visible = true;
|
||||
dialog.title = '修改菜单';
|
||||
};
|
||||
|
||||
/** 提交按钮 */
|
||||
const submitForm = () => {
|
||||
menuFormRef.value?.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
form.value.menuId ? await updateMenu(form.value) : await addMenu(form.value);
|
||||
// 保存数据到后端
|
||||
if (form.value.menuId) {
|
||||
await updateMenu(form.value);
|
||||
} else {
|
||||
await addMenu(form.value);
|
||||
}
|
||||
|
||||
proxy?.$modal.msgSuccess('操作成功');
|
||||
dialog.visible = false;
|
||||
|
||||
// 重新加载所有数据并重建缓存
|
||||
await getList();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (row: MenuVO) => {
|
||||
await proxy?.$modal.confirm('是否确认删除名称为"' + row.menuName + '"的数据项?');
|
||||
await delMenu(row.menuId);
|
||||
|
||||
// 重新加载所有数据并重建缓存
|
||||
await getList();
|
||||
proxy?.$modal.msgSuccess('删除成功');
|
||||
};
|
||||
|
||||
// 初始化加载所有数据
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter"
|
||||
:leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
@ -13,19 +12,11 @@
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="角色状态" clearable>
|
||||
<el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label"
|
||||
:value="dict.value" />
|
||||
<el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" style="width: 308px">
|
||||
<el-date-picker v-model="dateRange" value-format="YYYY-MM-DD HH:mm:ss" type="daterange"
|
||||
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"
|
||||
:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"></el-date-picker>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery"
|
||||
v-hasPermi="['system:role:query']">搜索</el-button>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery" v-hasPermi="['system:role:query']">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
@ -37,9 +28,18 @@
|
||||
<el-col :lg="4" :xs="24" style="">
|
||||
<el-card shadow="hover">
|
||||
<el-input v-model="deptName" placeholder="请输入部门名称" prefix-icon="Search" clearable />
|
||||
<el-tree ref="deptTreeRef" class="mt-2" node-key="id" :data="deptOptions"
|
||||
:props="{ label: 'label', children: 'children' }" :expand-on-click-node="false"
|
||||
:filter-node-method="filterNode" highlight-current default-expand-all @node-click="handleNodeClick" />
|
||||
<el-tree
|
||||
ref="deptTreeRef"
|
||||
class="mt-2"
|
||||
node-key="id"
|
||||
:data="deptOptions"
|
||||
:props="{ label: 'label', children: 'children' }"
|
||||
:expand-on-click-node="false"
|
||||
:filter-node-method="filterNode"
|
||||
highlight-current
|
||||
default-expand-all
|
||||
@node-click="handleNodeClick"
|
||||
/>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :lg="20" :xs="24">
|
||||
@ -47,25 +47,21 @@
|
||||
<template #header>
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['system:role:add']" type="primary" plain icon="Plus"
|
||||
@click="handleAdd()">新增</el-button>
|
||||
<el-button v-hasPermi="['system:role:add']" type="primary" plain icon="Plus" @click="handleAdd()">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['system:role:edit']" type="success" plain :disabled="single" icon="Edit"
|
||||
@click="handleUpdate()">修改</el-button>
|
||||
<el-button v-hasPermi="['system:role:edit']" type="success" plain :disabled="single" icon="Edit" @click="handleUpdate()"
|
||||
>修改</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['system:role:delete']" type="danger" plain :disabled="ids.length === 0"
|
||||
@click="handleDelete()">删除</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['system:role:export']" type="warning" plain icon="Download"
|
||||
@click="handleExport">导出</el-button>
|
||||
<el-button v-hasPermi="['system:role:delete']" type="danger" plain :disabled="ids.length === 0" @click="handleDelete()"
|
||||
>删除</el-button
|
||||
>
|
||||
</el-col>
|
||||
<right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<el-table ref="roleTableRef" v-loading="loading" :data="roleList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column v-if="false" label="角色编号" prop="roleId" width="120" />
|
||||
@ -74,8 +70,7 @@
|
||||
<el-table-column label="显示顺序" prop="roleSort" width="100" />
|
||||
<el-table-column label="状态" align="center" width="100">
|
||||
<template #default="scope">
|
||||
<el-switch v-model="scope.row.status" active-value="0" inactive-value="1"
|
||||
@change="handleStatusChange(scope.row)"></el-switch>
|
||||
<el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime">
|
||||
@ -87,27 +82,28 @@
|
||||
<el-table-column fixed="right" label="操作" width="180">
|
||||
<template #default="scope">
|
||||
<el-tooltip v-if="scope.row.roleId !== 1" content="修改" placement="top">
|
||||
<el-button v-hasPermi="['system:role:edit']" link type="primary" icon="Edit"
|
||||
@click="handleUpdate(scope.row)"></el-button>
|
||||
<el-button v-hasPermi="['system:role:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip v-if="scope.row.roleId !== 1" content="删除" placement="top">
|
||||
<el-button v-hasPermi="['system:role:remove']" link type="primary" icon="Delete"
|
||||
@click="handleDelete(scope.row)"></el-button>
|
||||
<el-button v-hasPermi="['system:role:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip v-if="scope.row.roleId !== 1" content="数据权限" placement="top">
|
||||
<el-button v-hasPermi="['system:role:edit']" link type="primary" icon="CircleCheck"
|
||||
@click="handleDataScope(scope.row)"></el-button>
|
||||
<el-button v-hasPermi="['system:role:edit']" link type="primary" icon="CircleCheck" @click="handleDataScope(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip v-if="scope.row.roleId !== 1" content="分配用户" placement="top">
|
||||
<el-button v-hasPermi="['system:role:edit']" link type="primary" icon="User"
|
||||
@click="handleAuthUser(scope.row)"></el-button>
|
||||
<el-button v-hasPermi="['system:role:edit']" link type="primary" icon="User" @click="handleAuthUser(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-if="total > 0" v-model:total="total" v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize" @pagination="getList" />
|
||||
<pagination
|
||||
v-if="total > 0"
|
||||
v-model:total="total"
|
||||
v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@ -115,8 +111,15 @@
|
||||
<el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
|
||||
<el-form ref="roleFormRef" :model="form" :rules="rules" label-width="110px">
|
||||
<el-form-item label="所属部门" prop="deptId">
|
||||
<el-cascader :options="deptOptions" v-model="form.deptId" placeholder="请选择所属部门" clearable filterable
|
||||
:show-all-levels="false" :props="{ value: 'id', emitPath: false, checkStrictly: true }" @change="">
|
||||
<el-cascader
|
||||
:options="deptOptions"
|
||||
v-model="form.deptId"
|
||||
placeholder="请选择所属部门"
|
||||
clearable
|
||||
filterable
|
||||
:show-all-levels="false"
|
||||
:props="{ value: 'id', emitPath: false, checkStrictly: true }"
|
||||
>
|
||||
</el-cascader>
|
||||
</el-form-item>
|
||||
<el-form-item label="角色名称" prop="roleName">
|
||||
@ -138,23 +141,26 @@
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-radio-group v-model="form.status">
|
||||
<el-radio v-for="dict in sys_normal_disable" :key="dict.value" :value="dict.value">{{ dict.label
|
||||
}}</el-radio>
|
||||
<el-radio v-for="dict in sys_normal_disable" :key="dict.value" :value="dict.value">{{ dict.label }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="菜单权限">
|
||||
<el-checkbox v-model="menuExpand"
|
||||
@change="handleCheckedTreeExpand(Boolean($event), 'menu')">展开/折叠</el-checkbox>
|
||||
<el-checkbox v-model="menuExpand" @change="handleCheckedTreeExpand(Boolean($event), 'menu')">展开/折叠</el-checkbox>
|
||||
<el-checkbox v-model="menuNodeAll" @change="handleCheckedTreeNodeAll($event, 'menu')">全选/全不选</el-checkbox>
|
||||
<el-checkbox v-model="form.menuCheckStrictly"
|
||||
@change="handleCheckedTreeConnect($event, 'menu')">父子联动</el-checkbox>
|
||||
<el-tree ref="menuRef" class="tree-border" :data="menuOptions" show-checkbox node-key="id"
|
||||
:check-strictly="!form.menuCheckStrictly" empty-text="加载中,请稍候"
|
||||
:props="{ label: 'label', children: 'children' }"></el-tree>
|
||||
<el-checkbox v-model="form.menuCheckStrictly" @change="handleCheckedTreeConnect($event, 'menu')">父子联动</el-checkbox>
|
||||
<el-tree
|
||||
ref="menuRef"
|
||||
class="tree-border"
|
||||
:data="menuOptions"
|
||||
show-checkbox
|
||||
node-key="id"
|
||||
:check-strictly="!form.menuCheckStrictly"
|
||||
empty-text="加载中,请稍候"
|
||||
:props="{ label: 'label', children: 'children' }"
|
||||
></el-tree>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否为特殊角色">
|
||||
<el-switch v-model="form.isSpecial" active-value="1" inactive-value="0" active-text="是" inactive-text="否">
|
||||
</el-switch>
|
||||
<el-switch v-model="form.isSpecial" active-value="1" inactive-value="0" active-text="是" inactive-text="否"> </el-switch>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容"></el-input>
|
||||
@ -179,19 +185,24 @@
|
||||
</el-form-item>
|
||||
<el-form-item label="权限范围">
|
||||
<el-select v-model="form.dataScope" @change="dataScopeSelectChange">
|
||||
<el-option v-for="item in dataScopeOptions" :key="item.value" :label="item.label"
|
||||
:value="item.value"></el-option>
|
||||
<el-option v-for="item in dataScopeOptions" :key="item.value" :label="item.label" :value="item.value"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-show="form.dataScope === '2'" label="数据权限">
|
||||
<el-checkbox v-model="deptExpand"
|
||||
@change="handleCheckedTreeExpand(Boolean($event), 'dept')">展开/折叠</el-checkbox>
|
||||
<el-checkbox v-model="deptExpand" @change="handleCheckedTreeExpand(Boolean($event), 'dept')">展开/折叠</el-checkbox>
|
||||
<el-checkbox v-model="deptNodeAll" @change="handleCheckedTreeNodeAll($event, 'dept')">全选/全不选</el-checkbox>
|
||||
<el-checkbox v-model="form.deptCheckStrictly"
|
||||
@change="handleCheckedTreeConnect($event, 'dept')">父子联动</el-checkbox>
|
||||
<el-tree ref="deptRef" class="tree-border" :data="deptOptions" show-checkbox default-expand-all node-key="id"
|
||||
:check-strictly="!form.deptCheckStrictly" empty-text="加载中,请稍候"
|
||||
:props="{ label: 'label', children: 'children' }"></el-tree>
|
||||
<el-checkbox v-model="form.deptCheckStrictly" @change="handleCheckedTreeConnect($event, 'dept')">父子联动</el-checkbox>
|
||||
<el-tree
|
||||
ref="deptRef"
|
||||
class="tree-border"
|
||||
:data="deptOptions"
|
||||
show-checkbox
|
||||
default-expand-all
|
||||
node-key="id"
|
||||
:check-strictly="!form.deptCheckStrictly"
|
||||
empty-text="加载中,请稍候"
|
||||
:props="{ label: 'label', children: 'children' }"
|
||||
></el-tree>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
@ -252,7 +263,7 @@ const menuRef = ref<ElTreeInstance>();
|
||||
const deptRef = ref<ElTreeInstance>();
|
||||
const deptTreeRef = ref<ElTreeInstance>();
|
||||
|
||||
const initForm: RoleForm = {
|
||||
const initForm = {
|
||||
roleId: undefined,
|
||||
roleSort: 1,
|
||||
status: '0',
|
||||
@ -265,10 +276,11 @@ const initForm: RoleForm = {
|
||||
menuIds: [],
|
||||
deptId: '',
|
||||
isSpecial: null,
|
||||
deptIds: []
|
||||
deptIds: [],
|
||||
roleSource: '1'
|
||||
};
|
||||
|
||||
const data = reactive<PageData<RoleForm, RoleQuery>>({
|
||||
const data = reactive({
|
||||
form: { ...initForm },
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
@ -276,7 +288,8 @@ const data = reactive<PageData<RoleForm, RoleQuery>>({
|
||||
roleName: '',
|
||||
roleKey: '',
|
||||
deptId: '',
|
||||
status: ''
|
||||
status: '',
|
||||
roleSource: '1'
|
||||
},
|
||||
rules: {
|
||||
roleName: [{ required: true, message: '角色名称不能为空', trigger: 'blur' }],
|
||||
@ -377,7 +390,7 @@ const handleAuthUser = (row: RoleVO) => {
|
||||
|
||||
/** 查询菜单树结构 */
|
||||
const getMenuTreeselect = async () => {
|
||||
const res = await menuTreeselect();
|
||||
const res = await menuTreeselect({ menuSource: '1' });
|
||||
menuOptions.value = res.data;
|
||||
};
|
||||
/** 所有部门节点数据 */
|
||||
@ -427,14 +440,14 @@ const handleUpdate = async (row?: RoleVO) => {
|
||||
};
|
||||
/** 根据角色ID查询菜单树结构 */
|
||||
const getRoleMenuTreeselect = (roleId: string | number) => {
|
||||
return roleMenuTreeselect(roleId).then((res): RoleMenuTree => {
|
||||
return roleMenuTreeselect(roleId, { menuSource: '1' }).then((res): RoleMenuTree => {
|
||||
menuOptions.value = res.data.menus;
|
||||
return res.data;
|
||||
});
|
||||
};
|
||||
/** 根据角色ID查询部门树结构 */
|
||||
const getRoleDeptTreeSelect = async (roleId: string | number) => {
|
||||
const res = await deptTreeSelect(roleId);
|
||||
const res = await deptTreeSelect(roleId, { roleSource: '1' });
|
||||
deptOptions.value = res.data.depts;
|
||||
return res.data;
|
||||
};
|
||||
|
||||
258
src/views/system/user/comm/editInfo.vue
Normal file
258
src/views/system/user/comm/editInfo.vue
Normal file
@ -0,0 +1,258 @@
|
||||
<template>
|
||||
<div class="p-2 editInfo">
|
||||
<el-form label-position="top" ref="userFormRef" :model="form" :rules="rules" label-width="80px" size="large">
|
||||
<el-row>
|
||||
<el-col :span="10">
|
||||
<el-form-item label="用户昵称" prop="nickName">
|
||||
<el-input v-model="form.nickName" placeholder="请输入用户昵称" maxlength="30" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="2"></el-col>
|
||||
<el-col :span="10">
|
||||
<el-form-item label="归属部门" prop="deptId">
|
||||
<el-tree-select
|
||||
v-model="form.deptId"
|
||||
:data="enabledDeptOptions"
|
||||
:props="{ value: 'id', label: 'label', children: 'children' }"
|
||||
value-key="id"
|
||||
placeholder="请选择归属部门"
|
||||
check-strictly
|
||||
@change="handleDeptChange"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="10">
|
||||
<el-form-item label="手机号码" prop="phonenumber">
|
||||
<el-input v-model="form.phonenumber" placeholder="请输入手机号码" maxlength="11" />
|
||||
</el-form-item> </el-col
|
||||
><el-col :span="2"></el-col>
|
||||
<el-col :span="10">
|
||||
<el-form-item label="邮箱" prop="email">
|
||||
<el-input v-model="form.email" placeholder="请输入邮箱" maxlength="50" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="10">
|
||||
<el-form-item v-if="form.userId == undefined" label="用户名称" prop="userName">
|
||||
<el-input v-model="form.userName" placeholder="请输入用户名称" maxlength="30" />
|
||||
</el-form-item> </el-col
|
||||
><el-col :span="2"></el-col>
|
||||
<el-col :span="10">
|
||||
<el-form-item v-if="form.userId == undefined" label="用户密码" prop="password">
|
||||
<el-input v-model="form.password" placeholder="请输入用户密码" type="password" maxlength="20" show-password />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="10">
|
||||
<el-form-item label="用户性别">
|
||||
<el-select v-model="form.sex" placeholder="请选择">
|
||||
<el-option v-for="dict in sys_user_sex" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
|
||||
</el-select>
|
||||
</el-form-item> </el-col
|
||||
><el-col :span="2"></el-col>
|
||||
<el-col :span="10">
|
||||
<el-form-item label="岗位">
|
||||
<el-select v-model="form.postIds" multiple placeholder="请选择">
|
||||
<el-option
|
||||
v-for="item in postOptions"
|
||||
:key="item.postId"
|
||||
:label="item.postName"
|
||||
:value="item.postId"
|
||||
:disabled="item.status == '1'"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item> </el-col
|
||||
><el-col :span="2"></el-col>
|
||||
<el-col :span="10">
|
||||
<el-form-item label="状态">
|
||||
<el-radio-group v-model="form.status">
|
||||
<el-radio v-for="dict in sys_normal_disable" :key="dict.value" :value="dict.value">{{ dict.label }} </el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="form.remark" type="textarea" :autosize="{ minRows: 3, maxRows: 3 }" placeholder="请输入内容"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<div class="box_submit">
|
||||
<el-button size="large" @click="cancel()">取 消</el-button>
|
||||
<el-button size="large" type="primary" @click="submitForm">确 定</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="Profile" lang="ts">
|
||||
import api from '@/api/system/user';
|
||||
import { listProject } from '@/api/project/project';
|
||||
import { getProjectByDeptId, getRoleList, optionselect } from '@/api/system/post';
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { sys_normal_disable, sys_user_sex } = toRefs<any>(proxy?.useDict('sys_normal_disable', 'sys_user_sex'));
|
||||
const enabledDeptOptions = ref([]);
|
||||
const deptOptions = ref([]);
|
||||
const projectOptions = ref<any[]>([]);
|
||||
const postOptions = ref([]);
|
||||
const roleOptions = ref([]);
|
||||
const userFormRef = ref<ElFormInstance>();
|
||||
const initFormData = {
|
||||
userId: undefined,
|
||||
deptId: undefined,
|
||||
userName: '',
|
||||
nickName: undefined,
|
||||
password: '',
|
||||
phonenumber: undefined,
|
||||
email: undefined,
|
||||
sex: undefined,
|
||||
projectRoles: [
|
||||
{
|
||||
projectId: '',
|
||||
roleIds: []
|
||||
}
|
||||
],
|
||||
status: '0',
|
||||
remark: '',
|
||||
postIds: [],
|
||||
filePath: undefined
|
||||
};
|
||||
|
||||
const initData = {
|
||||
form: { ...initFormData },
|
||||
rules: {
|
||||
userName: [
|
||||
{ required: true, message: '用户名称不能为空', trigger: 'blur' },
|
||||
{
|
||||
min: 2,
|
||||
max: 20,
|
||||
message: '用户名称长度必须介于 2 和 20 之间',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
nickName: [{ required: true, message: '用户昵称不能为空', trigger: 'blur' }],
|
||||
password: [
|
||||
{ required: true, message: '用户密码不能为空', trigger: 'blur' },
|
||||
{
|
||||
min: 5,
|
||||
max: 20,
|
||||
message: '用户密码长度必须介于 5 和 20 之间',
|
||||
trigger: 'blur'
|
||||
},
|
||||
{ pattern: /^[^<>"'|\\]+$/, message: '不能包含非法字符:< > " \' \\ |', trigger: 'blur' }
|
||||
],
|
||||
email: [
|
||||
{
|
||||
type: 'email',
|
||||
message: '请输入正确的邮箱地址',
|
||||
trigger: ['blur', 'change']
|
||||
}
|
||||
],
|
||||
phonenumber: [
|
||||
{ required: true, message: '请输入手机号码', trigger: 'blur' },
|
||||
{
|
||||
pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
|
||||
message: '请输入正确的手机号码',
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
const data = reactive(initData);
|
||||
const { form, rules } = toRefs(data);
|
||||
/** 查询部门下拉树结构 */
|
||||
const getDeptTree = async () => {
|
||||
const res = await api.deptTreeSelect({ isShow: '1' });
|
||||
deptOptions.value = res.data;
|
||||
enabledDeptOptions.value = filterDisabledDept(res.data);
|
||||
const projectList = await listProject();
|
||||
projectOptions.value = projectList.rows;
|
||||
};
|
||||
/** 过滤禁用的部门 */
|
||||
const filterDisabledDept = (deptList) => {
|
||||
return deptList.filter((dept) => {
|
||||
if (dept.disabled) {
|
||||
return false;
|
||||
}
|
||||
if (dept.children && dept.children.length) {
|
||||
dept.children = filterDisabledDept(dept.children);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
};
|
||||
async function handleDeptChange(value: number | string) {
|
||||
proxy?.$emit('setDeptId', value);
|
||||
const response = await optionselect(value);
|
||||
const roleList = await getRoleList(value);
|
||||
roleOptions.value = roleList.data;
|
||||
postOptions.value = response.data;
|
||||
form.value.postIds = [];
|
||||
form.value.projectRoles = [
|
||||
{
|
||||
projectId: '',
|
||||
roleIds: []
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
const submitForm = () => {
|
||||
userFormRef.value?.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
form.value.userId ? await api.updateUser(form.value) : await api.addUser(form.value);
|
||||
proxy?.$modal.msgSuccess('操作成功');
|
||||
proxy?.$emit('submit', false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const cancel = () => {
|
||||
proxy?.$emit('close', false);
|
||||
};
|
||||
/** 重置操作表单 */
|
||||
const reset = () => {
|
||||
form.value = { ...initFormData };
|
||||
form.value.projectRoles = [
|
||||
{
|
||||
projectId: '',
|
||||
roleIds: []
|
||||
}
|
||||
];
|
||||
|
||||
userFormRef.value?.resetFields();
|
||||
};
|
||||
// 打开弹框调用参数
|
||||
const open = async (row?: any) => {
|
||||
reset();
|
||||
if (row) {
|
||||
// 编辑
|
||||
const { data } = await api.getUser(row.userId);
|
||||
Object.assign(form.value, data.user);
|
||||
postOptions.value = data.posts;
|
||||
roleOptions.value = data.roles;
|
||||
form.value.postIds = data.postIds;
|
||||
form.value.projectRoles = data.projectRoles;
|
||||
form.value.password = '';
|
||||
const roleList = await getRoleList(form.value.deptId);
|
||||
roleOptions.value = roleList.data;
|
||||
} else {
|
||||
// 新增
|
||||
const { data } = await api.getUser();
|
||||
postOptions.value = data.posts;
|
||||
}
|
||||
};
|
||||
const getInfoForm = () => {
|
||||
return form.value;
|
||||
};
|
||||
onMounted(() => {
|
||||
getDeptTree();
|
||||
});
|
||||
defineExpose({ open, getInfoForm });
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.editInfo {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.box_submit {
|
||||
align-self: flex-end;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
486
src/views/system/user/comm/roleInfo.vue
Normal file
486
src/views/system/user/comm/roleInfo.vue
Normal file
@ -0,0 +1,486 @@
|
||||
<template>
|
||||
<div class="roleInfo">
|
||||
<div class="title_detail">
|
||||
<span>当前选定角色信息预览</span>
|
||||
<div style="margin-top: 10px">
|
||||
<el-table :data="roleList" border height="150">
|
||||
<el-table-column label="所属部门" align="center" prop="deptName" />
|
||||
<el-table-column label="关联项目" align="center" prop="projectName" />
|
||||
<el-table-column label="web端担任角色" align="center" prop="webRoles" />
|
||||
<el-table-column label="APP端担任角色" align="center" prop="appRoles" />
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="title_detail" style="margin-top: 10px">
|
||||
<span>选择或修改当前角色信息</span>
|
||||
<div style="margin-top: 10px" class="box_detail">
|
||||
<!-- 项目列表选择区 -->
|
||||
<div class="project_list">
|
||||
<span>关联项目模块</span>
|
||||
<div class="project-items">
|
||||
<div
|
||||
v-for="item in projectOptions"
|
||||
:key="item.id"
|
||||
class="project-item"
|
||||
:class="{ 'project-item-selected': isProjectSelected(item.id) }"
|
||||
@click="toggleProjectSelection(item)"
|
||||
>
|
||||
<div class="project-item-content">
|
||||
<el-checkbox v-model="item.checked" :value="item.id" class="project-checkbox" @change="handleProjectCheck(item, $event)" />
|
||||
<span class="project-name">{{ item.projectName }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 角色分配区 -->
|
||||
<div class="post_list">
|
||||
<div class="list_title">关联项目角色分配</div>
|
||||
<div v-if="selectedProjects.length === 0" class="no-selection">请从左侧选择项目进行角色分配</div>
|
||||
<div v-for="(project, index) in selectedProjects" :key="project.id" class="project-role-container">
|
||||
<div class="project-header">
|
||||
<span class="project-title">{{ project.projectName }}</span>
|
||||
<el-button type="text" class="remove-project" @click="removeProject(project.id)"> 移除 </el-button>
|
||||
</div>
|
||||
<div class="role-assignment">
|
||||
<div class="role-group">
|
||||
<label class="role-label">web端角色:</label>
|
||||
<el-checkbox-group v-model="project.webRoles" @change="updateRoleList">
|
||||
<el-checkbox v-for="role in allRoles" :key="role.roleId" :value="role.roleId">
|
||||
{{ role.roleName }}
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
<div class="role-group">
|
||||
<label class="role-label">APP端角色:</label>
|
||||
<el-checkbox-group v-model="project.appRoles" @change="updateRoleList">
|
||||
<el-checkbox v-for="role in AppRoles" :key="role.roleId" :value="role.roleId">
|
||||
{{ role.roleName }}
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box_submit">
|
||||
<el-button size="large" @click="cancel()">取 消</el-button>
|
||||
<el-button size="large" type="primary" @click="submitForm">保存角色信息</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="RoleProjectManagement" lang="ts">
|
||||
import { ref, reactive, toRefs, getCurrentInstance, ComponentInternalInstance, defineExpose, watch } from 'vue';
|
||||
import { ElFormInstance } from 'element-plus';
|
||||
import api from '@/api/system/user';
|
||||
import { listProject } from '@/api/project/project';
|
||||
import { getRoleList } from '@/api/system/post';
|
||||
|
||||
// 类型定义
|
||||
interface Project {
|
||||
id: number | string;
|
||||
projectName: string;
|
||||
checked: boolean;
|
||||
webRoles: string[];
|
||||
appRoles: string[];
|
||||
}
|
||||
|
||||
interface Role {
|
||||
roleId: number | string;
|
||||
roleName: string;
|
||||
roleSort?: number;
|
||||
}
|
||||
|
||||
interface RoleInfo {
|
||||
userNick: string;
|
||||
deptName: string;
|
||||
postName: string;
|
||||
projectName: string;
|
||||
webRoles: string;
|
||||
appRoles: string;
|
||||
}
|
||||
|
||||
// 组件实例
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
|
||||
// 响应式数据
|
||||
const projectOptions = ref<Project[]>([]);
|
||||
const selectedProjects = ref<Project[]>([]);
|
||||
const allRoles = ref<Role[]>([]); // web端角色
|
||||
const AppRoles = ref<Role[]>([]); // APP端角色
|
||||
const roleList = ref<RoleInfo[]>([]);
|
||||
// 表单初始数据
|
||||
const initFormData = {
|
||||
userId: undefined,
|
||||
deptId: undefined,
|
||||
userName: '',
|
||||
nickName: undefined,
|
||||
password: '',
|
||||
phonenumber: undefined,
|
||||
email: undefined,
|
||||
sex: undefined,
|
||||
projectRoles: [] as Array<{ projectId: number | string; webRoles: string[]; appRoles: string[] }>,
|
||||
status: '0',
|
||||
remark: '',
|
||||
postIds: [],
|
||||
filePath: undefined,
|
||||
deptName: '' // 新增部门名称字段
|
||||
};
|
||||
|
||||
const data = reactive({
|
||||
form: { ...initFormData }
|
||||
});
|
||||
const deptName = ref('');
|
||||
const { form } = toRefs(data);
|
||||
|
||||
// 核心方法:更新预览列表
|
||||
const updateRoleList = () => {
|
||||
if (selectedProjects.value.length === 0) {
|
||||
roleList.value = [];
|
||||
return;
|
||||
}
|
||||
roleList.value = selectedProjects.value.map((project) => {
|
||||
// 处理web端角色名称
|
||||
var webRoleNames = project.webRoles
|
||||
.map((roleId) => {
|
||||
const role = allRoles.value.find((r) => r.roleId === roleId);
|
||||
return role ? role.roleName : '';
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
||||
// 处理APP端角色名称
|
||||
var appRoleNames = project.appRoles
|
||||
.map((roleId) => {
|
||||
const role = AppRoles.value.find((r) => r.roleId === roleId);
|
||||
return role ? role.roleName : '';
|
||||
})
|
||||
.filter(Boolean);
|
||||
webRoleNames = [...new Set(webRoleNames)];
|
||||
appRoleNames = [...new Set(appRoleNames)];
|
||||
return {
|
||||
deptName: deptName.value,
|
||||
projectName: project.projectName,
|
||||
webRoles: webRoleNames.length > 0 ? webRoleNames.join(',') : '无',
|
||||
appRoles: appRoleNames.length > 0 ? appRoleNames.join(',') : '无'
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
// 监听已选项目变化,自动更新预览列表
|
||||
watch(
|
||||
selectedProjects,
|
||||
() => {
|
||||
updateRoleList();
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
// 检查项目是否被选中
|
||||
const isProjectSelected = (projectId: number | string) => {
|
||||
return selectedProjects.value.some((p) => p.id === projectId);
|
||||
};
|
||||
|
||||
// 切换项目选择状态
|
||||
const toggleProjectSelection = (project: Project) => {
|
||||
// handleProjectCheck(project, !project.checked);
|
||||
};
|
||||
|
||||
// 处理项目勾选状态变化
|
||||
const handleProjectCheck = (project: Project, checked: boolean) => {
|
||||
// project.checked = checked;
|
||||
const index = selectedProjects.value.findIndex((p) => p.id === project.id);
|
||||
|
||||
if (checked && index === -1) {
|
||||
// 添加选中的项目
|
||||
selectedProjects.value.push({
|
||||
...project,
|
||||
webRoles: [],
|
||||
appRoles: []
|
||||
});
|
||||
} else if (!checked && index !== -1) {
|
||||
// 移除取消选中的项目
|
||||
selectedProjects.value.splice(index, 1);
|
||||
}
|
||||
};
|
||||
|
||||
// 移除项目
|
||||
const removeProject = (projectId: number | string) => {
|
||||
// 更新选中项目列表
|
||||
selectedProjects.value = selectedProjects.value.filter((p) => p.id !== projectId);
|
||||
|
||||
// 更新项目选项的勾选状态
|
||||
const project = projectOptions.value.find((p) => p.id === projectId);
|
||||
if (project) {
|
||||
project.checked = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 提交表单
|
||||
const submitForm = async () => {
|
||||
// 整理项目角色数据
|
||||
if (form.value.projectRoles.length == 0) {
|
||||
proxy?.$modal.msgWarning('请选择项目角色');
|
||||
return;
|
||||
}
|
||||
form.value.projectRoles = selectedProjects.value.map((project) => ({
|
||||
projectId: project.id,
|
||||
roleIds: [...new Set(project.webRoles), ...new Set(project.appRoles)]
|
||||
}));
|
||||
// 提交数据
|
||||
try {
|
||||
if (form.value.userId) {
|
||||
await api.updateUser(form.value);
|
||||
} else {
|
||||
await api.addUser(form.value);
|
||||
}
|
||||
proxy?.$modal.msgSuccess('操作成功');
|
||||
proxy?.$emit('submit', form.value);
|
||||
cancel();
|
||||
} catch (error) {
|
||||
proxy?.$modal.msgError('操作失败,请重试');
|
||||
console.error('提交失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 取消操作
|
||||
const cancel = () => {
|
||||
resetForm();
|
||||
proxy?.$emit('close');
|
||||
};
|
||||
|
||||
// 重置表单
|
||||
const resetForm = () => {
|
||||
data.form = { ...initFormData };
|
||||
projectOptions.value.forEach((p) => (p.checked = false));
|
||||
selectedProjects.value = [];
|
||||
roleList.value = [];
|
||||
};
|
||||
|
||||
// 初始化数据
|
||||
const initData = async () => {
|
||||
try {
|
||||
// 获取项目列表
|
||||
const projectRes = await listProject();
|
||||
projectOptions.value = projectRes.rows.map((item: any) => ({
|
||||
id: item.id,
|
||||
projectName: item.projectName,
|
||||
checked: false
|
||||
}));
|
||||
} catch (error) {
|
||||
proxy?.$modal.msgError('数据加载失败');
|
||||
console.error('初始化数据失败:', error);
|
||||
}
|
||||
};
|
||||
// 打开弹窗时调用
|
||||
const open = async (row?: any, deptId?: any) => {
|
||||
resetForm();
|
||||
await initData();
|
||||
deptName.value = row.deptName;
|
||||
if (row) {
|
||||
try {
|
||||
if (!row.createTime) {
|
||||
form.value = { ...row };
|
||||
// 获取角色列表
|
||||
if (form.value.deptId) {
|
||||
deptId = form.value.deptId;
|
||||
}
|
||||
const roleRes = await getRoleList(deptId);
|
||||
allRoles.value = roleRes.data.filter((item: Role) => item.roleSource == '1');
|
||||
AppRoles.value = roleRes.data.filter((item: Role) => item.roleSource != '1');
|
||||
} else {
|
||||
// const { data } = await api.getUser(row.userId);
|
||||
const data = row;
|
||||
Object.assign(form.value, row);
|
||||
// 获取角色列表
|
||||
if (form.value.deptId) {
|
||||
deptId = form.value.deptId;
|
||||
}
|
||||
const roleRes = await getRoleList(deptId);
|
||||
// 区分web端和app端角色
|
||||
allRoles.value = roleRes.data.filter((item: Role) => item.roleSource == '1');
|
||||
AppRoles.value = roleRes.data.filter((item: Role) => item.roleSource != '1');
|
||||
// 加载项目角色数据
|
||||
if (data.projectRoles && data.projectRoles.length) {
|
||||
data.projectRoles.forEach((pr: any) => {
|
||||
const project = projectOptions.value.find((p) => p.id === pr.projectId);
|
||||
if (project) {
|
||||
project.checked = true;
|
||||
selectedProjects.value.push({
|
||||
...project,
|
||||
webRoles: pr.roleIds || [],
|
||||
appRoles: pr.roleIds || []
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
proxy?.$modal.msgError('加载用户数据失败');
|
||||
console.error('加载用户数据失败:', error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 暴露方法
|
||||
defineExpose({ open });
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.roleInfo {
|
||||
height: 100%;
|
||||
padding-bottom: 60px;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
|
||||
.title_detail {
|
||||
> span {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
display: inline-block;
|
||||
padding-bottom: 5px;
|
||||
border-bottom: 2px solid #409eff;
|
||||
}
|
||||
|
||||
.box_detail {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
margin-top: 15px;
|
||||
|
||||
> div {
|
||||
height: 350px;
|
||||
padding: 15px;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
overflow-y: auto;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.project_list {
|
||||
width: 320px;
|
||||
border: 1px solid #eee;
|
||||
|
||||
.project-items {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.project-item {
|
||||
padding: 10px 12px;
|
||||
margin-bottom: 8px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
border: 1px solid #eee;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.project-item-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.project-checkbox {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.project-name {
|
||||
flex: 1;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
.project-item-selected {
|
||||
background-color: #ecf5ff;
|
||||
border-color: #c6e2ff;
|
||||
}
|
||||
}
|
||||
|
||||
.post_list {
|
||||
flex: 1;
|
||||
border: 1px solid #eee;
|
||||
|
||||
.list_title {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.no-selection {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #909399;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.project-role-container {
|
||||
padding: 15px;
|
||||
margin-bottom: 15px;
|
||||
// background-color: #f9f9f9;
|
||||
border-radius: 4px;
|
||||
border: 1px solid rgb(229 231 235);
|
||||
|
||||
.project-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
|
||||
.project-title {
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.remove-project {
|
||||
color: #f56c6c;
|
||||
padding: 0 5px;
|
||||
|
||||
&:hover {
|
||||
color: #e4393c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.role-assignment {
|
||||
.role-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.role-label {
|
||||
display: inline-block;
|
||||
width: 110px;
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.el-checkbox-group {
|
||||
display: inline-block;
|
||||
margin-left: 10px;
|
||||
|
||||
.el-checkbox {
|
||||
margin-right: 15px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.box_submit {
|
||||
align-self: flex-end;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
788
src/views/system/user/index copy.vue
Normal file
788
src/views/system/user/index copy.vue
Normal file
@ -0,0 +1,788 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<el-row :gutter="20">
|
||||
<!-- 部门树 -->
|
||||
<el-col :lg="4" :xs="24" style="">
|
||||
<el-card shadow="hover">
|
||||
<el-input v-model="deptName" placeholder="请输入部门名称" prefix-icon="Search" clearable />
|
||||
<el-tree
|
||||
ref="deptTreeRef"
|
||||
class="mt-2"
|
||||
node-key="id"
|
||||
:data="deptOptions"
|
||||
:props="{ label: 'label', children: 'children' }"
|
||||
:expand-on-click-node="false"
|
||||
:filter-node-method="filterNode"
|
||||
highlight-current
|
||||
default-expand-all
|
||||
@node-click="handleNodeClick"
|
||||
/>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :lg="20" :xs="24">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<el-form-item label="用户名称" prop="userName">
|
||||
<el-input v-model="queryParams.userName" placeholder="请输入用户名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号码" prop="phonenumber">
|
||||
<el-input v-model="queryParams.phonenumber" placeholder="请输入手机号码" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="用户状态" clearable>
|
||||
<el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" style="width: 308px">
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery" @v-has-permi="['system:user:query']">搜索</el-button>
|
||||
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<el-card shadow="hover">
|
||||
<template #header>
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="1.5">
|
||||
<el-button v-has-permi="['system:user:add']" type="primary" plain icon="Plus" @click="handleAdd()"> 新增 </el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-has-permi="['system:user:edit']" type="success" plain :disabled="single" icon="Edit" @click="handleUpdate()">
|
||||
修改
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-has-permi="['system:user:remove']" type="danger" plain :disabled="multiple" icon="Delete" @click="handleDelete()">
|
||||
删除
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-dropdown class="mt-[1px]">
|
||||
<el-button plain type="info">
|
||||
更多
|
||||
<el-icon class="el-icon--right">
|
||||
<arrow-down />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item icon="Download" @click="importTemplate">下载模板</el-dropdown-item>
|
||||
<el-dropdown-item v-has-permi="['system:user:import']" icon="Top" @click="handleImport">导入数据 </el-dropdown-item>
|
||||
<el-dropdown-item v-has-permi="['system:user:export']" icon="Download" @click="handleExport"> 导出数据 </el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</el-col>
|
||||
<right-toolbar v-model:show-search="showSearch" :columns="columns" :search="true" @query-table="getList"></right-toolbar>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="50" align="center" />
|
||||
<el-table-column v-if="columns[0].visible" key="userId" label="用户编号" align="center" prop="userId" />
|
||||
<el-table-column v-if="columns[1].visible" key="userName" label="用户名称" align="center" prop="userName" :show-overflow-tooltip="true" />
|
||||
<el-table-column v-if="columns[2].visible" key="nickName" label="用户昵称" align="center" prop="nickName" :show-overflow-tooltip="true" />
|
||||
<el-table-column v-if="columns[3].visible" key="deptName" label="部门" align="center" prop="deptName" :show-overflow-tooltip="true" />
|
||||
<el-table-column v-if="columns[4].visible" key="phonenumber" label="手机号码" align="center" prop="phonenumber" width="120" />
|
||||
<el-table-column v-if="columns[5].visible" key="status" label="状态" align="center">
|
||||
<template #default="scope">
|
||||
<el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column v-if="columns[6].visible" label="创建时间" align="center" prop="createTime" width="160">
|
||||
<template #default="scope">
|
||||
<span>{{ scope.row.createTime }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="操作" fixed="right" width="230" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-tooltip v-if="scope.row.userId !== 1" content="修改" placement="top">
|
||||
<el-button v-hasPermi="['system:user:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip v-if="scope.row.userId !== 1" content="删除" placement="top">
|
||||
<el-button v-hasPermi="['system:user:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
|
||||
<el-tooltip v-if="scope.row.userId !== 1" content="重置密码" placement="top">
|
||||
<el-button v-hasPermi="['system:user:resetPwd']" link type="primary" icon="Key" @click="handleResetPwd(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
|
||||
<el-tooltip v-if="scope.row.userId !== 1" content="分配角色" placement="top">
|
||||
<el-button v-hasPermi="['system:user:edit']" link type="primary" icon="CircleCheck" @click="handleAuthRole(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip v-if="scope.row.userId !== 1" content="编辑关联项目" placement="top">
|
||||
<el-button v-hasPermi="['system:user:edit']" link type="primary" icon="Edit" @click="handleUpdateProject(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip v-if="scope.row.userId !== 1" content="上传证书目录" placement="top">
|
||||
<el-button v-hasPermi="['system:user:edit']" link type="primary" icon="Upload" @click="handleUploadCert(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-dialog v-model="shuttleVisible" title="编辑关联项目" width="auto" destroy-on-close>
|
||||
<shuttle-frame :userId="selectedUserId" @close="shuttleVisible = false" />
|
||||
</el-dialog>
|
||||
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
:total="total"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 添加或修改用户配置对话框 -->
|
||||
<el-dialog draggable ref="formDialogRef" v-model="dialog.visible" :title="dialog.title" width="600px" append-to-body @close="closeDialog">
|
||||
<el-form ref="userFormRef" :model="form" :rules="rules" label-width="80px">
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="用户昵称" prop="nickName">
|
||||
<el-input v-model="form.nickName" placeholder="请输入用户昵称" maxlength="30" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="归属部门" prop="deptId">
|
||||
<el-tree-select
|
||||
v-model="form.deptId"
|
||||
:data="enabledDeptOptions"
|
||||
:props="{ value: 'id', label: 'label', children: 'children' }"
|
||||
value-key="id"
|
||||
placeholder="请选择归属部门"
|
||||
check-strictly
|
||||
@change="handleDeptChange"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="手机号码" prop="phonenumber">
|
||||
<el-input v-model="form.phonenumber" placeholder="请输入手机号码" maxlength="11" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="邮箱" prop="email">
|
||||
<el-input v-model="form.email" placeholder="请输入邮箱" maxlength="50" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item v-if="form.userId == undefined" label="用户名称" prop="userName">
|
||||
<el-input v-model="form.userName" placeholder="请输入用户名称" maxlength="30" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item v-if="form.userId == undefined" label="用户密码" prop="password">
|
||||
<el-input v-model="form.password" placeholder="请输入用户密码" type="password" maxlength="20" show-password />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="用户性别">
|
||||
<el-select v-model="form.sex" placeholder="请选择">
|
||||
<el-option v-for="dict in sys_user_sex" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="岗位">
|
||||
<el-select v-model="form.postIds" multiple placeholder="请选择">
|
||||
<el-option
|
||||
v-for="item in postOptions"
|
||||
:key="item.postId"
|
||||
:label="item.postName"
|
||||
:value="item.postId"
|
||||
:disabled="item.status == '1'"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-row :gutter="20" v-for="(item, index) in form.projectRoles">
|
||||
<el-col :span="11" :offset="0">
|
||||
<el-form-item label="项目列表">
|
||||
<el-select v-model="item.projectId" placeholder="请选择">
|
||||
<el-option v-for="dict in projectOptions" :key="dict.id" :label="dict.shortName" :value="dict.id"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="11" :offset="0">
|
||||
<el-form-item label="角色">
|
||||
<el-select v-model="item.roleIds" filterable multiple placeholder="请选择">
|
||||
<el-option
|
||||
v-for="item in roleOptions"
|
||||
:key="item.roleId"
|
||||
:label="item.roleName"
|
||||
:value="item.roleId"
|
||||
:disabled="item.status == '1'"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="1" :offset="0">
|
||||
<el-button type="primary" circle icon="Plus" @click="handleAddProject" v-if="index == 0"></el-button>
|
||||
<el-button type="danger" circle icon="Delete" @click="delProject(index)" v-else></el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="状态">
|
||||
<el-radio-group v-model="form.status">
|
||||
<el-radio v-for="dict in sys_normal_disable" :key="dict.value" :value="dict.value">{{ dict.label }} </el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel()">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 用户导入对话框 -->
|
||||
<el-dialog draggable v-model="upload.open" :title="upload.title" width="400px" append-to-body>
|
||||
<el-upload
|
||||
ref="uploadRef"
|
||||
:limit="1"
|
||||
accept=".xlsx, .xls"
|
||||
:headers="upload.headers"
|
||||
:action="upload.url + '?updateSupport=' + upload.updateSupport"
|
||||
:disabled="upload.isUploading"
|
||||
:on-progress="handleFileUploadProgress"
|
||||
:on-success="handleFileSuccess"
|
||||
:auto-upload="false"
|
||||
drag
|
||||
>
|
||||
<el-icon class="el-icon--upload">
|
||||
<i-ep-upload-filled />
|
||||
</el-icon>
|
||||
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
||||
<template #tip>
|
||||
<div class="text-center el-upload__tip">
|
||||
<div class="el-upload__tip">
|
||||
<el-checkbox v-model="upload.updateSupport" />
|
||||
是否更新已经存在的用户数据
|
||||
</div>
|
||||
<span>仅允许导入xls、xlsx格式文件。</span>
|
||||
<el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline" @click="importTemplate">下载模板 </el-link>
|
||||
</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submitFileForm">确 定</el-button>
|
||||
<el-button @click="upload.open = false">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="上传证书目录" v-model="certDialog" width="30%" destroy-on-close>
|
||||
<!-- <File-upload v-model="fileUpload" :limit="5"></File-upload> -->
|
||||
<ImageUpload v-model="fileUpload" :limit="5"></ImageUpload>
|
||||
<template #footer>
|
||||
<span>
|
||||
<el-button @click="certDialog = false">取消</el-button>
|
||||
<el-button type="primary" @click="uploadCert">确定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="User" lang="ts">
|
||||
import api, { uploadCertList } from '@/api/system/user';
|
||||
import { UserForm, UserQuery, UserVO } from '@/api/system/user/types';
|
||||
import { DeptTreeVO, DeptVO } from '@/api/system/dept/types';
|
||||
import { RoleVO } from '@/api/system/role/types';
|
||||
import { PostVO } from '@/api/system/post/types';
|
||||
import { globalHeaders } from '@/utils/request';
|
||||
import { to } from 'await-to-js';
|
||||
import { getProjectByDeptId, getRoleList, optionselect } from '@/api/system/post';
|
||||
import ShuttleFrame from '../../project/projectRelevancy/component/ShuttleFrame.vue';
|
||||
import { listProject } from '@/api/project/project';
|
||||
import editInfo from './comm/editInfo.vue';
|
||||
const router = useRouter();
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { sys_normal_disable, sys_user_sex } = toRefs<any>(proxy?.useDict('sys_normal_disable', 'sys_user_sex'));
|
||||
const userList = ref<UserVO[]>();
|
||||
const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref<Array<number | string>>([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
|
||||
const deptName = ref('');
|
||||
const deptOptions = ref<DeptTreeVO[]>([]);
|
||||
const enabledDeptOptions = ref<DeptTreeVO[]>([]);
|
||||
const initPassword = ref<string>('');
|
||||
const postOptions = ref<PostVO[]>([]);
|
||||
const roleOptions = ref<RoleVO[]>([]);
|
||||
const projectOptions = ref<any[]>([]);
|
||||
|
||||
/*** 用户导入参数 */
|
||||
const upload = reactive<ImportOption>({
|
||||
// 是否显示弹出层(用户导入)
|
||||
open: false,
|
||||
// 弹出层标题(用户导入)
|
||||
title: '',
|
||||
// 是否禁用上传
|
||||
isUploading: false,
|
||||
// 是否更新已经存在的用户数据
|
||||
updateSupport: 0,
|
||||
// 设置上传的请求头部
|
||||
headers: globalHeaders(),
|
||||
// 上传的地址
|
||||
url: import.meta.env.VITE_APP_BASE_API + '/system/user/importData'
|
||||
});
|
||||
// 列显隐信息
|
||||
const columns = ref<FieldOption[]>([
|
||||
{ key: 0, label: `用户编号`, visible: false, children: [] },
|
||||
{ key: 1, label: `用户名称`, visible: true, children: [] },
|
||||
{ key: 2, label: `用户昵称`, visible: true, children: [] },
|
||||
{ key: 3, label: `部门`, visible: true, children: [] },
|
||||
{ key: 4, label: `手机号码`, visible: true, children: [] },
|
||||
{ key: 5, label: `状态`, visible: true, children: [] },
|
||||
{ key: 6, label: `创建时间`, visible: true, children: [] }
|
||||
]);
|
||||
|
||||
const deptTreeRef = ref<ElTreeInstance>();
|
||||
const queryFormRef = ref<ElFormInstance>();
|
||||
const userFormRef = ref<ElFormInstance>();
|
||||
const uploadRef = ref<ElUploadInstance>();
|
||||
const formDialogRef = ref<ElDialogInstance>();
|
||||
|
||||
const dialog = reactive<DialogOption>({
|
||||
visible: false,
|
||||
title: ''
|
||||
});
|
||||
|
||||
const initFormData: UserForm = {
|
||||
userId: undefined,
|
||||
deptId: undefined,
|
||||
userName: '',
|
||||
nickName: undefined,
|
||||
password: '',
|
||||
phonenumber: undefined,
|
||||
email: undefined,
|
||||
sex: undefined,
|
||||
projectRoles: [
|
||||
{
|
||||
projectId: '',
|
||||
roleIds: []
|
||||
}
|
||||
],
|
||||
status: '0',
|
||||
remark: '',
|
||||
postIds: [],
|
||||
filePath: undefined
|
||||
};
|
||||
|
||||
const initData: PageData<UserForm, UserQuery> = {
|
||||
form: { ...initFormData },
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
userName: '',
|
||||
phonenumber: '',
|
||||
status: '',
|
||||
deptId: '',
|
||||
roleId: ''
|
||||
},
|
||||
rules: {
|
||||
userName: [
|
||||
{ required: true, message: '用户名称不能为空', trigger: 'blur' },
|
||||
{
|
||||
min: 2,
|
||||
max: 20,
|
||||
message: '用户名称长度必须介于 2 和 20 之间',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
nickName: [{ required: true, message: '用户昵称不能为空', trigger: 'blur' }],
|
||||
password: [
|
||||
{ required: true, message: '用户密码不能为空', trigger: 'blur' },
|
||||
{
|
||||
min: 5,
|
||||
max: 20,
|
||||
message: '用户密码长度必须介于 5 和 20 之间',
|
||||
trigger: 'blur'
|
||||
},
|
||||
{ pattern: /^[^<>"'|\\]+$/, message: '不能包含非法字符:< > " \' \\ |', trigger: 'blur' }
|
||||
],
|
||||
email: [
|
||||
{
|
||||
type: 'email',
|
||||
message: '请输入正确的邮箱地址',
|
||||
trigger: ['blur', 'change']
|
||||
}
|
||||
],
|
||||
phonenumber: [
|
||||
{ required: true, message: '请输入手机号码', trigger: 'blur' },
|
||||
{
|
||||
pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
|
||||
message: '请输入正确的手机号码',
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
const data = reactive<PageData<UserForm, UserQuery>>(initData);
|
||||
|
||||
const { queryParams, form, rules } = toRefs<PageData<UserForm, UserQuery>>(data);
|
||||
|
||||
/** 通过条件过滤节点 */
|
||||
const filterNode = (value: string, data: any) => {
|
||||
if (!value) return true;
|
||||
return data.label.indexOf(value) !== -1;
|
||||
};
|
||||
/** 根据名称筛选部门树 */
|
||||
watchEffect(
|
||||
() => {
|
||||
deptTreeRef.value?.filter(deptName.value);
|
||||
},
|
||||
{
|
||||
flush: 'post' // watchEffect会在DOM挂载或者更新之前就会触发,此属性控制在DOM元素更新后运行
|
||||
}
|
||||
);
|
||||
|
||||
/** 查询用户列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
const res = await api.listUser(proxy?.addDateRange(queryParams.value, dateRange.value));
|
||||
loading.value = false;
|
||||
userList.value = res.rows;
|
||||
total.value = res.total;
|
||||
};
|
||||
|
||||
/** 查询部门下拉树结构 */
|
||||
const getDeptTree = async () => {
|
||||
const res = await api.deptTreeSelect({ isShow: '1' });
|
||||
deptOptions.value = res.data;
|
||||
enabledDeptOptions.value = filterDisabledDept(res.data);
|
||||
const projectList = await listProject();
|
||||
projectOptions.value = projectList.rows;
|
||||
};
|
||||
|
||||
/** 过滤禁用的部门 */
|
||||
const filterDisabledDept = (deptList: DeptTreeVO[]) => {
|
||||
return deptList.filter((dept) => {
|
||||
if (dept.disabled) {
|
||||
return false;
|
||||
}
|
||||
if (dept.children && dept.children.length) {
|
||||
dept.children = filterDisabledDept(dept.children);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
};
|
||||
|
||||
/** 节点单击事件 */
|
||||
const handleNodeClick = (data: DeptVO) => {
|
||||
queryParams.value.deptId = data.id;
|
||||
handleQuery();
|
||||
};
|
||||
|
||||
/** 部门选择变化 */
|
||||
const handleAddProject = () => {
|
||||
form.value.projectRoles.push({
|
||||
projectId: '',
|
||||
roleIds: []
|
||||
});
|
||||
};
|
||||
|
||||
/** 删除项目 */
|
||||
const delProject = (index: number) => {
|
||||
form.value.projectRoles.splice(index, 1);
|
||||
};
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.value.pageNum = 1;
|
||||
getList();
|
||||
};
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
dateRange.value = ['', ''];
|
||||
queryFormRef.value?.resetFields();
|
||||
queryParams.value.pageNum = 1;
|
||||
queryParams.value.deptId = undefined;
|
||||
deptTreeRef.value?.setCurrentKey(undefined);
|
||||
handleQuery();
|
||||
};
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (row?: UserVO) => {
|
||||
const userIds = row?.userId || ids.value;
|
||||
const [err] = await to(proxy?.$modal.confirm('是否确认删除用户编号为"' + userIds + '"的数据项?') as any);
|
||||
if (!err) {
|
||||
await api.delUser(userIds);
|
||||
await getList();
|
||||
proxy?.$modal.msgSuccess('删除成功');
|
||||
}
|
||||
};
|
||||
|
||||
/** 用户状态修改 */
|
||||
const handleStatusChange = async (row: UserVO) => {
|
||||
let text = row.status === '0' ? '启用' : '停用';
|
||||
try {
|
||||
await proxy?.$modal.confirm('确认要"' + text + '""' + row.userName + '"用户吗?');
|
||||
await api.changeUserStatus(row.userId, row.status);
|
||||
proxy?.$modal.msgSuccess(text + '成功');
|
||||
} catch (err) {
|
||||
row.status = row.status === '0' ? '1' : '0';
|
||||
}
|
||||
};
|
||||
/** 跳转角色分配 */
|
||||
const handleAuthRole = (row: UserVO) => {
|
||||
const userId = row.userId;
|
||||
router.push('/system/user-auth/role/' + userId);
|
||||
};
|
||||
|
||||
/** 重置密码按钮操作 */
|
||||
const handleResetPwd = async (row: UserVO) => {
|
||||
const [err, res] = await to(
|
||||
ElMessageBox.prompt('请输入"' + row.userName + '"的新密码', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
closeOnClickModal: false,
|
||||
inputPattern: /^.{5,20}$/,
|
||||
inputErrorMessage: '用户密码长度必须介于 5 和 20 之间',
|
||||
inputValidator: (value) => {
|
||||
if (/<|>|"|'|\||\\/.test(value)) {
|
||||
return '不能包含非法字符:< > " \' \\ |';
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
if (!err && res) {
|
||||
await api.resetUserPwd(row.userId, res.value);
|
||||
proxy?.$modal.msgSuccess('修改成功,新密码是:' + res.value);
|
||||
}
|
||||
};
|
||||
|
||||
/** 选择条数 */
|
||||
const handleSelectionChange = (selection: UserVO[]) => {
|
||||
ids.value = selection.map((item) => item.userId);
|
||||
single.value = selection.length != 1;
|
||||
multiple.value = !selection.length;
|
||||
};
|
||||
|
||||
/** 导入按钮操作 */
|
||||
const handleImport = () => {
|
||||
upload.title = '用户导入';
|
||||
upload.open = true;
|
||||
};
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = () => {
|
||||
proxy?.download(
|
||||
'system/user/export',
|
||||
{
|
||||
...queryParams.value
|
||||
},
|
||||
`user_${new Date().getTime()}.xlsx`
|
||||
);
|
||||
};
|
||||
/** 下载模板操作 */
|
||||
const importTemplate = () => {
|
||||
proxy?.download('system/user/importTemplate', {}, `user_template_${new Date().getTime()}.xlsx`);
|
||||
};
|
||||
|
||||
/**文件上传中处理 */
|
||||
const handleFileUploadProgress = () => {
|
||||
upload.isUploading = true;
|
||||
};
|
||||
/** 文件上传成功处理 */
|
||||
const handleFileSuccess = (response: any, file: UploadFile) => {
|
||||
upload.open = false;
|
||||
upload.isUploading = false;
|
||||
uploadRef.value?.handleRemove(file);
|
||||
ElMessageBox.alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + '</div>', '导入结果', {
|
||||
dangerouslyUseHTMLString: true
|
||||
});
|
||||
getList();
|
||||
};
|
||||
|
||||
/** 提交上传文件 */
|
||||
function submitFileForm() {
|
||||
uploadRef.value?.submit();
|
||||
}
|
||||
|
||||
/** 重置操作表单 */
|
||||
const reset = () => {
|
||||
form.value = { ...initFormData };
|
||||
form.value.projectRoles = [
|
||||
{
|
||||
projectId: '',
|
||||
roleIds: []
|
||||
}
|
||||
];
|
||||
|
||||
userFormRef.value?.resetFields();
|
||||
};
|
||||
/** 取消按钮 */
|
||||
const cancel = () => {
|
||||
dialog.visible = false;
|
||||
reset();
|
||||
};
|
||||
|
||||
/** 新增按钮操作 */
|
||||
const handleAdd = async () => {
|
||||
reset();
|
||||
const { data } = await api.getUser();
|
||||
dialog.visible = true;
|
||||
dialog.title = '新增用户';
|
||||
postOptions.value = data.posts;
|
||||
form.value.password = initPassword.value.toString();
|
||||
};
|
||||
|
||||
/** 修改按钮操作 */
|
||||
const handleUpdate = async (row?: UserForm) => {
|
||||
reset();
|
||||
const userId = row?.userId || ids.value[0];
|
||||
const { data } = await api.getUser(userId);
|
||||
dialog.visible = true;
|
||||
dialog.title = '修改用户';
|
||||
Object.assign(form.value, data.user);
|
||||
postOptions.value = data.posts;
|
||||
roleOptions.value = data.roles;
|
||||
form.value.postIds = data.postIds;
|
||||
form.value.projectRoles = data.projectRoles;
|
||||
form.value.password = '';
|
||||
const roleList = await getRoleList(form.value.deptId);
|
||||
|
||||
roleOptions.value = roleList.data;
|
||||
};
|
||||
|
||||
const validate = () => {
|
||||
for (let i = 0; i < form.value.projectRoles.length; i++) {
|
||||
const item = form.value.projectRoles[i];
|
||||
if (!item.projectId || item.projectId.length === 0) {
|
||||
proxy?.$modal.msgError(`第 ${i + 1} 行“项目列表”未填写`);
|
||||
return false; // 阻止提交
|
||||
}
|
||||
if (!item.roleIds || item.roleIds.length === 0) {
|
||||
proxy?.$modal.msgError(`第 ${i + 1} 行“角色”未填写`);
|
||||
return false; // 阻止提交
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/** 提交按钮 */
|
||||
const submitForm = () => {
|
||||
const isValid = validate();
|
||||
if (!isValid) return;
|
||||
userFormRef.value?.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
form.value.userId ? await api.updateUser(form.value) : await api.addUser(form.value);
|
||||
proxy?.$modal.msgSuccess('操作成功');
|
||||
dialog.visible = false;
|
||||
await getList();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 关闭用户弹窗
|
||||
*/
|
||||
const closeDialog = () => {
|
||||
dialog.visible = false;
|
||||
resetForm();
|
||||
};
|
||||
|
||||
/**
|
||||
* 重置表单
|
||||
*/
|
||||
const resetForm = () => {
|
||||
userFormRef.value?.resetFields();
|
||||
userFormRef.value?.clearValidate();
|
||||
|
||||
form.value.id = undefined;
|
||||
form.value.status = '1';
|
||||
};
|
||||
onMounted(() => {
|
||||
getDeptTree(); // 初始化部门数据
|
||||
getList(); // 初始化列表数据
|
||||
proxy?.getConfigKey('sys.user.initPassword').then((response) => {
|
||||
initPassword.value = response.data;
|
||||
});
|
||||
});
|
||||
|
||||
async function handleDeptChange(value: number | string) {
|
||||
const response = await optionselect(value);
|
||||
const roleList = await getRoleList(value);
|
||||
|
||||
roleOptions.value = roleList.data;
|
||||
postOptions.value = response.data;
|
||||
form.value.postIds = [];
|
||||
form.value.projectRoles = [
|
||||
{
|
||||
projectId: [],
|
||||
roleIds: []
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
const shuttleVisible = ref(false);
|
||||
const selectedUserId = ref<number>();
|
||||
const handleUpdateProject = (row) => {
|
||||
if (row) {
|
||||
selectedUserId.value = row.userId;
|
||||
shuttleVisible.value = true;
|
||||
}
|
||||
};
|
||||
|
||||
const certDialog = ref(false);
|
||||
const certId = ref<string | number>(undefined);
|
||||
const fileUpload = ref<string>();
|
||||
//上传证书
|
||||
const handleUploadCert = (row: UserVO) => {
|
||||
certId.value = row.userId;
|
||||
fileUpload.value = row.filePath;
|
||||
certDialog.value = true;
|
||||
};
|
||||
|
||||
const uploadCert = async () => {
|
||||
if (!fileUpload.value) {
|
||||
proxy?.$modal.msgError('请上传证书目录');
|
||||
return;
|
||||
}
|
||||
let res = await uploadCertList({
|
||||
userId: certId.value,
|
||||
fileId: fileUpload.value
|
||||
});
|
||||
console.log(res);
|
||||
|
||||
certDialog.value = false;
|
||||
proxy?.$modal.msgSuccess('上传证书目录成功');
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@ -5,14 +5,22 @@
|
||||
<el-col :lg="4" :xs="24" style="">
|
||||
<el-card shadow="hover">
|
||||
<el-input v-model="deptName" placeholder="请输入部门名称" prefix-icon="Search" clearable />
|
||||
<el-tree ref="deptTreeRef" class="mt-2" node-key="id" :data="deptOptions"
|
||||
:props="{ label: 'label', children: 'children' }" :expand-on-click-node="false"
|
||||
:filter-node-method="filterNode" highlight-current default-expand-all @node-click="handleNodeClick" />
|
||||
<el-tree
|
||||
ref="deptTreeRef"
|
||||
class="mt-2"
|
||||
node-key="id"
|
||||
:data="deptOptions"
|
||||
:props="{ label: 'label', children: 'children' }"
|
||||
:expand-on-click-node="false"
|
||||
:filter-node-method="filterNode"
|
||||
highlight-current
|
||||
default-expand-all
|
||||
@node-click="handleNodeClick"
|
||||
/>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :lg="20" :xs="24">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter"
|
||||
:leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
@ -20,24 +28,27 @@
|
||||
<el-input v-model="queryParams.userName" placeholder="请输入用户名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号码" prop="phonenumber">
|
||||
<el-input v-model="queryParams.phonenumber" placeholder="请输入手机号码" clearable
|
||||
@keyup.enter="handleQuery" />
|
||||
<el-input v-model="queryParams.phonenumber" placeholder="请输入手机号码" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="用户状态" clearable>
|
||||
<el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label"
|
||||
:value="dict.value" />
|
||||
<el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" style="width: 308px">
|
||||
<el-date-picker v-model="dateRange" value-format="YYYY-MM-DD HH:mm:ss" type="daterange"
|
||||
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"
|
||||
:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"></el-date-picker>
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery"
|
||||
@v-has-permi="['system:user:query']">搜索</el-button>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery" @v-has-permi="['system:user:query']">搜索</el-button>
|
||||
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
@ -50,18 +61,15 @@
|
||||
<template #header>
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="1.5">
|
||||
<el-button v-has-permi="['system:user:add']" type="primary" plain icon="Plus" @click="handleAdd()"> 新增
|
||||
</el-button>
|
||||
<el-button v-has-permi="['system:user:add']" type="primary" plain icon="Plus" @click="handleAdd()"> 新增 </el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-has-permi="['system:user:edit']" type="success" plain :disabled="single" icon="Edit"
|
||||
@click="handleUpdate()">
|
||||
<el-button v-has-permi="['system:user:edit']" type="success" plain :disabled="single" icon="Edit" @click="handleUpdate()">
|
||||
修改
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-has-permi="['system:user:remove']" type="danger" plain :disabled="multiple" icon="Delete"
|
||||
@click="handleDelete()">
|
||||
<el-button v-has-permi="['system:user:remove']" type="danger" plain :disabled="multiple" icon="Delete" @click="handleDelete()">
|
||||
删除
|
||||
</el-button>
|
||||
</el-col>
|
||||
@ -76,34 +84,26 @@
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item icon="Download" @click="importTemplate">下载模板</el-dropdown-item>
|
||||
<el-dropdown-item v-has-permi="['system:user:import']" icon="Top" @click="handleImport">导入数据
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item v-has-permi="['system:user:export']" icon="Download" @click="handleExport"> 导出数据
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item v-has-permi="['system:user:import']" icon="Top" @click="handleImport">导入数据 </el-dropdown-item>
|
||||
<el-dropdown-item v-has-permi="['system:user:export']" icon="Download" @click="handleExport"> 导出数据 </el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</el-col>
|
||||
<right-toolbar v-model:show-search="showSearch" :columns="columns" :search="true"
|
||||
@query-table="getList"></right-toolbar>
|
||||
<right-toolbar v-model:show-search="showSearch" :columns="columns" :search="true" @query-table="getList"></right-toolbar>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="50" align="center" />
|
||||
<el-table-column v-if="columns[0].visible" key="userId" label="用户编号" align="center" prop="userId" />
|
||||
<el-table-column v-if="columns[1].visible" key="userName" label="用户名称" align="center" prop="userName"
|
||||
:show-overflow-tooltip="true" />
|
||||
<el-table-column v-if="columns[2].visible" key="nickName" label="用户昵称" align="center" prop="nickName"
|
||||
:show-overflow-tooltip="true" />
|
||||
<el-table-column v-if="columns[3].visible" key="deptName" label="部门" align="center" prop="deptName"
|
||||
:show-overflow-tooltip="true" />
|
||||
<el-table-column v-if="columns[4].visible" key="phonenumber" label="手机号码" align="center" prop="phonenumber"
|
||||
width="120" />
|
||||
<el-table-column v-if="columns[1].visible" key="userName" label="用户名称" align="center" prop="userName" :show-overflow-tooltip="true" />
|
||||
<el-table-column v-if="columns[2].visible" key="nickName" label="用户昵称" align="center" prop="nickName" :show-overflow-tooltip="true" />
|
||||
<el-table-column v-if="columns[3].visible" key="deptName" label="部门" align="center" prop="deptName" :show-overflow-tooltip="true" />
|
||||
<el-table-column v-if="columns[4].visible" key="phonenumber" label="手机号码" align="center" prop="phonenumber" width="120" />
|
||||
<el-table-column v-if="columns[5].visible" key="status" label="状态" align="center">
|
||||
<template #default="scope">
|
||||
<el-switch v-model="scope.row.status" active-value="0" inactive-value="1"
|
||||
@change="handleStatusChange(scope.row)"></el-switch>
|
||||
<el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
@ -116,30 +116,24 @@
|
||||
<el-table-column label="操作" fixed="right" width="230" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-tooltip v-if="scope.row.userId !== 1" content="修改" placement="top">
|
||||
<el-button v-hasPermi="['system:user:edit']" link type="primary" icon="Edit"
|
||||
@click="handleUpdate(scope.row)"></el-button>
|
||||
<el-button v-hasPermi="['system:user:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip v-if="scope.row.userId !== 1" content="删除" placement="top">
|
||||
<el-button v-hasPermi="['system:user:remove']" link type="primary" icon="Delete"
|
||||
@click="handleDelete(scope.row)"></el-button>
|
||||
<el-button v-hasPermi="['system:user:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
|
||||
<el-tooltip v-if="scope.row.userId !== 1" content="重置密码" placement="top">
|
||||
<el-button v-hasPermi="['system:user:resetPwd']" link type="primary" icon="Key"
|
||||
@click="handleResetPwd(scope.row)"></el-button>
|
||||
<el-button v-hasPermi="['system:user:resetPwd']" link type="primary" icon="Key" @click="handleResetPwd(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
|
||||
<el-tooltip v-if="scope.row.userId !== 1" content="分配角色" placement="top">
|
||||
<el-button v-hasPermi="['system:user:edit']" link type="primary" icon="CircleCheck"
|
||||
@click="handleAuthRole(scope.row)"></el-button>
|
||||
<el-button v-hasPermi="['system:user:edit']" link type="primary" icon="CircleCheck" @click="handleAuthRole(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip v-if="scope.row.userId !== 1" content="编辑关联项目" placement="top">
|
||||
<el-button v-hasPermi="['system:user:edit']" link type="primary" icon="Edit"
|
||||
@click="handleUpdateProject(scope.row)"></el-button>
|
||||
<el-button v-hasPermi="['system:user:edit']" link type="primary" icon="Edit" @click="handleUpdateProject(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip v-if="scope.row.userId !== 1" content="上传证书目录" placement="top">
|
||||
<el-button v-hasPermi="['system:user:edit']" link type="primary" icon="Upload"
|
||||
@click="handleUploadCert(scope.row)"></el-button>
|
||||
<el-button v-hasPermi="['system:user:edit']" link type="primary" icon="Upload" @click="handleUploadCert(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@ -148,117 +142,53 @@
|
||||
<shuttle-frame :userId="selectedUserId" @close="shuttleVisible = false" />
|
||||
</el-dialog>
|
||||
|
||||
<pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize"
|
||||
:total="total" @pagination="getList" />
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
:total="total"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 添加或修改用户配置对话框 -->
|
||||
<el-dialog ref="formDialogRef" v-model="dialog.visible" :title="dialog.title" width="600px" append-to-body
|
||||
@close="closeDialog">
|
||||
<el-form ref="userFormRef" :model="form" :rules="rules" label-width="80px">
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="用户昵称" prop="nickName">
|
||||
<el-input v-model="form.nickName" placeholder="请输入用户昵称" maxlength="30" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="归属部门" prop="deptId">
|
||||
<el-tree-select v-model="form.deptId" :data="enabledDeptOptions"
|
||||
:props="{ value: 'id', label: 'label', children: 'children' }" value-key="id" placeholder="请选择归属部门"
|
||||
check-strictly @change="handleDeptChange" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="手机号码" prop="phonenumber">
|
||||
<el-input v-model="form.phonenumber" placeholder="请输入手机号码" maxlength="11" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="邮箱" prop="email">
|
||||
<el-input v-model="form.email" placeholder="请输入邮箱" maxlength="50" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item v-if="form.userId == undefined" label="用户名称" prop="userName">
|
||||
<el-input v-model="form.userName" placeholder="请输入用户名称" maxlength="30" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item v-if="form.userId == undefined" label="用户密码" prop="password">
|
||||
<el-input v-model="form.password" placeholder="请输入用户密码" type="password" maxlength="20" show-password />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="用户性别">
|
||||
<el-select v-model="form.sex" placeholder="请选择">
|
||||
<el-option v-for="dict in sys_user_sex" :key="dict.value" :label="dict.label"
|
||||
:value="dict.value"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="岗位">
|
||||
<el-select v-model="form.postIds" multiple placeholder="请选择">
|
||||
<el-option v-for="item in postOptions" :key="item.postId" :label="item.postName" :value="item.postId"
|
||||
:disabled="item.status == '1'"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-row :gutter="20" v-for="(item, index) in form.projectRoles">
|
||||
<el-col :span="11" :offset="0">
|
||||
<el-form-item label="项目列表">
|
||||
<el-select v-model="item.projectId" placeholder="请选择">
|
||||
<el-option v-for="dict in projectOptions" :key="dict.id" :label="dict.shortName"
|
||||
:value="dict.id"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="11" :offset="0">
|
||||
<el-form-item label="角色">
|
||||
<el-select v-model="item.roleIds" filterable multiple placeholder="请选择">
|
||||
<el-option v-for="item in roleOptions" :key="item.roleId" :label="item.roleName"
|
||||
:value="item.roleId" :disabled="item.status == '1'"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="1" :offset="0">
|
||||
<el-button type="primary" circle icon="Plus" @click="handleAddProject" v-if="index == 0"></el-button>
|
||||
<el-button type="danger" circle icon="Delete" @click="delProject(index)" v-else></el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="状态">
|
||||
<el-radio-group v-model="form.status">
|
||||
<el-radio v-for="dict in sys_normal_disable" :key="dict.value" :value="dict.value">{{ dict.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel()">取 消</el-button>
|
||||
<el-dialog draggable ref="formDialogRef" style="background: rgb(249,250,251);" v-model="dialog.visible" :title="dialog.title" width="1300px" append-to-body @close="closeDialog">
|
||||
<div class="boxDetial">
|
||||
<div class="tab_info">
|
||||
<div class="tab_item" @click="onTab(1)" :class="{ active: type == 1 }">
|
||||
<Avatar style="width: 1em; height: 1em; margin-right: 8px" :style="{color: type == 1 ? '#1890ff' : '#000'}" />
|
||||
<span>基本资料</span>
|
||||
</div>
|
||||
<div class="tab_item" @click="onTab(2)" :class="{ active: type == 2 }">
|
||||
<Key style="width: 1em; height: 1em; margin-right: 8px" :style="{color: type == 2 ? '#1890ff' : '#000'}" />
|
||||
<span>角色信息</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div class="tab_content" v-show="type == 1">
|
||||
<editInfo ref="editInfoRef" @close="dialog.visible = false" @submit="getList" @setDeptId="setDeptId"></editInfo>
|
||||
</div>
|
||||
<div class="tab_content" v-show="type == 2">
|
||||
<roleInfo ref="roleInfoRef" @close="dialog.visible = false" @submit="getList"></roleInfo>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 用户导入对话框 -->
|
||||
<el-dialog draggable v-model="upload.open" :title="upload.title" width="400px" append-to-body>
|
||||
<el-upload ref="uploadRef" :limit="1" accept=".xlsx, .xls" :headers="upload.headers"
|
||||
:action="upload.url + '?updateSupport=' + upload.updateSupport" :disabled="upload.isUploading"
|
||||
:on-progress="handleFileUploadProgress" :on-success="handleFileSuccess" :auto-upload="false" drag>
|
||||
<el-upload
|
||||
ref="uploadRef"
|
||||
:limit="1"
|
||||
accept=".xlsx, .xls"
|
||||
:headers="upload.headers"
|
||||
:action="upload.url + '?updateSupport=' + upload.updateSupport"
|
||||
:disabled="upload.isUploading"
|
||||
:on-progress="handleFileUploadProgress"
|
||||
:on-success="handleFileSuccess"
|
||||
:auto-upload="false"
|
||||
drag
|
||||
>
|
||||
<el-icon class="el-icon--upload">
|
||||
<i-ep-upload-filled />
|
||||
</el-icon>
|
||||
@ -270,8 +200,7 @@
|
||||
是否更新已经存在的用户数据
|
||||
</div>
|
||||
<span>仅允许导入xls、xlsx格式文件。</span>
|
||||
<el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline"
|
||||
@click="importTemplate">下载模板 </el-link>
|
||||
<el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline" @click="importTemplate">下载模板 </el-link>
|
||||
</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
@ -307,7 +236,9 @@ import { to } from 'await-to-js';
|
||||
import { getProjectByDeptId, getRoleList, optionselect } from '@/api/system/post';
|
||||
import ShuttleFrame from '../../project/projectRelevancy/component/ShuttleFrame.vue';
|
||||
import { listProject } from '@/api/project/project';
|
||||
|
||||
import editInfo from './comm/editInfo.vue';
|
||||
import roleInfo from './comm/roleInfo.vue';
|
||||
import { color } from 'echarts';
|
||||
const router = useRouter();
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { sys_normal_disable, sys_user_sex } = toRefs<any>(proxy?.useDict('sys_normal_disable', 'sys_user_sex'));
|
||||
@ -326,7 +257,8 @@ const initPassword = ref<string>('');
|
||||
const postOptions = ref<PostVO[]>([]);
|
||||
const roleOptions = ref<RoleVO[]>([]);
|
||||
const projectOptions = ref<any[]>([]);
|
||||
|
||||
const editInfoRef = ref<InstanceType<typeof editInfo> | null>(null);
|
||||
const roleInfoRef = ref<InstanceType<typeof roleInfo> | null>(null);
|
||||
/*** 用户导入参数 */
|
||||
const upload = reactive<ImportOption>({
|
||||
// 是否显示弹出层(用户导入)
|
||||
@ -358,7 +290,8 @@ const queryFormRef = ref<ElFormInstance>();
|
||||
const userFormRef = ref<ElFormInstance>();
|
||||
const uploadRef = ref<ElUploadInstance>();
|
||||
const formDialogRef = ref<ElDialogInstance>();
|
||||
|
||||
const deptIdRole = ref<number>();
|
||||
const type = ref(1);
|
||||
const dialog = reactive<DialogOption>({
|
||||
visible: false,
|
||||
title: ''
|
||||
@ -452,7 +385,9 @@ watchEffect(
|
||||
flush: 'post' // watchEffect会在DOM挂载或者更新之前就会触发,此属性控制在DOM元素更新后运行
|
||||
}
|
||||
);
|
||||
|
||||
const setDeptId = (deptId: number) => {
|
||||
deptIdRole.value = deptId;
|
||||
};
|
||||
/** 查询用户列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
@ -637,8 +572,12 @@ const cancel = () => {
|
||||
const handleAdd = async () => {
|
||||
reset();
|
||||
const { data } = await api.getUser();
|
||||
type.value = 1;
|
||||
dialog.visible = true;
|
||||
dialog.title = '新增用户';
|
||||
nextTick(() => {
|
||||
editInfoRef.value?.open();
|
||||
});
|
||||
postOptions.value = data.posts;
|
||||
form.value.password = initPassword.value.toString();
|
||||
};
|
||||
@ -646,19 +585,13 @@ const handleAdd = async () => {
|
||||
/** 修改按钮操作 */
|
||||
const handleUpdate = async (row?: UserForm) => {
|
||||
reset();
|
||||
const userId = row?.userId || ids.value[0];
|
||||
const { data } = await api.getUser(userId);
|
||||
dialog.visible = true;
|
||||
dialog.title = '修改用户';
|
||||
Object.assign(form.value, data.user);
|
||||
postOptions.value = data.posts;
|
||||
roleOptions.value = data.roles;
|
||||
form.value.postIds = data.postIds;
|
||||
form.value.projectRoles = data.projectRoles;
|
||||
form.value.password = '';
|
||||
const roleList = await getRoleList(form.value.deptId);
|
||||
|
||||
roleOptions.value = roleList.data;
|
||||
type.value = 1;
|
||||
form.value = row;
|
||||
nextTick(() => {
|
||||
editInfoRef.value?.open(row);
|
||||
});
|
||||
};
|
||||
|
||||
const validate = () => {
|
||||
@ -764,6 +697,58 @@ const uploadCert = async () => {
|
||||
certDialog.value = false;
|
||||
proxy?.$modal.msgSuccess('上传证书目录成功');
|
||||
};
|
||||
const onTab = (val: number) => {
|
||||
type.value = val;
|
||||
if (val == 2) {
|
||||
let obj = editInfoRef.value?.getInfoForm();
|
||||
form.value = obj;
|
||||
nextTick(() => {
|
||||
roleInfoRef.value?.open(form.value, deptIdRole.value);
|
||||
});
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
<style lang="scss" scoped>
|
||||
.boxDetial {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: start;
|
||||
height: 680px;
|
||||
gap: 20px;
|
||||
|
||||
.tab_info {
|
||||
height: 40%;
|
||||
width: 200px;
|
||||
background: #fff;
|
||||
padding: 30px 10px;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-radius: 0.5rem;
|
||||
font-size: 18px;
|
||||
.tab_item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 5px;
|
||||
margin-bottom: 10px;
|
||||
padding: 5px 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
.tab_item:hover {
|
||||
background-color: rgb(249, 250, 251);
|
||||
}
|
||||
.active {
|
||||
color: #1890ff;
|
||||
}
|
||||
}
|
||||
.tab_content {
|
||||
height: 100%;
|
||||
width: calc(100% - 200px);
|
||||
padding: 20px 10px;
|
||||
background: #fff;
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<el-option v-for="item in options" :key="item.versions" :label="item.versions" :value="item.versions" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="表名" prop="sheet">
|
||||
<el-form-item label="表名" prop="sheet" v-if="activeTab != '3'">
|
||||
<el-select v-model="queryForm.sheet" placeholder="选择表名" @change="changeSheet">
|
||||
<el-option v-for="item in sheets" :key="item" :label="item" :value="item" />
|
||||
</el-select>
|
||||
@ -58,11 +58,13 @@
|
||||
</el-card>
|
||||
</transition>
|
||||
<el-card shadow="never" class="mb8">
|
||||
<el-table ref="tableRef" v-loading="loading" :data="tableData" row-key="id" border lazy default-expand-all>
|
||||
<el-table ref="tableAllRef" v-loading="loading" :data="tableData" row-key="id" border lazy :expand-row-keys="expandRowKeys">
|
||||
<el-table-column prop="num" label="编号" />
|
||||
<el-table-column prop="name" label="工程或费用名称" />
|
||||
<el-table-column prop="unit" label="单位" />
|
||||
<el-table-column prop="quantity" label="数量">
|
||||
<el-table-column prop="unit" label="单位" align="center" />
|
||||
|
||||
<el-table-column prop="specification" label="规格" align="center" />
|
||||
<el-table-column prop="quantity" label="数量" align="center">
|
||||
<template #default="scope">
|
||||
{{ scope.row.children.length > 0 ? '' : scope.row.quantity }}
|
||||
</template>
|
||||
@ -90,6 +92,11 @@
|
||||
{{ scope.row.price != 0 ? Number(scope.row.price).toFixed(2) : null }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="taxRate" label="税率" width="100">
|
||||
<template #default="scope">
|
||||
{{ scope.row.taxRate !== false ? scope.row.taxRate : '' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="operate" label="操作" align="center">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
@ -111,64 +118,141 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch, onMounted, onUnmounted } from 'vue';
|
||||
import { getCurrentInstance, ComponentInternalInstance } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { useUserStoreHook } from '@/store/modules/user';
|
||||
import { obtainAllVersionNumbers, sheetList, getTableList, updatePrice, importExcelFile } from '@/api/tender/index';
|
||||
|
||||
// 类型定义
|
||||
interface QueryForm {
|
||||
versions: string;
|
||||
sheet: string;
|
||||
}
|
||||
|
||||
interface TableRow {
|
||||
id: string | number;
|
||||
num?: string;
|
||||
name?: string;
|
||||
unit?: string;
|
||||
taxRate?: string | number;
|
||||
specification?: string;
|
||||
quantity?: number;
|
||||
unitPrice?: number;
|
||||
price?: number;
|
||||
remark?: string;
|
||||
children?: TableRow[];
|
||||
}
|
||||
|
||||
interface VersionItem {
|
||||
versions: string;
|
||||
status: string;
|
||||
id?: string | number;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
// 实例与状态初始化
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const userStore = useUserStoreHook();
|
||||
const currentProject = computed(() => userStore.selectedProject);
|
||||
|
||||
// 标签页配置
|
||||
const tabList = [
|
||||
{
|
||||
label: '招采工程量清单',
|
||||
value: '2'
|
||||
},
|
||||
{
|
||||
label: '物资设备清单',
|
||||
value: '3'
|
||||
}
|
||||
{ label: '招采工程量清单', value: '2' },
|
||||
{ label: '物资设备清单', value: '3' }
|
||||
];
|
||||
const queryForm = ref({
|
||||
versions: '',
|
||||
sheet: ''
|
||||
});
|
||||
|
||||
const versionsData = ref<any>({});
|
||||
|
||||
// 响应式状态
|
||||
const queryForm = ref<QueryForm>({ versions: '', sheet: '' });
|
||||
const versionsData = ref<VersionItem>({});
|
||||
const activeTab = ref('2');
|
||||
const sheets = ref([]);
|
||||
const options = ref([]);
|
||||
const tableData = ref([]);
|
||||
const tableRef = ref();
|
||||
const isExpandAll = ref(false);
|
||||
const sheets = ref<string[]>([]);
|
||||
const options = ref<VersionItem[]>([]);
|
||||
const tableData = ref<TableRow[]>([]);
|
||||
const tableAllRef = ref<any>(null);
|
||||
const uploadRef = ref<any>(null);
|
||||
const isExpandAll = ref(true);
|
||||
const loading = ref(false);
|
||||
const versionMap = new Map();
|
||||
const versionMap = new Map<string, VersionItem>();
|
||||
const expandRowKeys = ref<string[] | number[]>([]); // 控制表格展开的行ID
|
||||
const modifyPrice = new Map<string | number, TableRow>();
|
||||
|
||||
// 切换tab
|
||||
// 切换标签页
|
||||
const handleTabChange = (tab: string) => {
|
||||
activeTab.value = tab;
|
||||
tableData.value = [];
|
||||
versionsData.value = {};
|
||||
isExpandAll.value = true;
|
||||
expandRowKeys.value = [];
|
||||
getVersionNums();
|
||||
};
|
||||
//切换版本
|
||||
const changeVersions = (value) => {
|
||||
versionsData.value = options.value.find((item) => item.versions == value);
|
||||
|
||||
// 切换版本号
|
||||
const changeVersions = (value: string) => {
|
||||
versionsData.value = options.value.find((item) => item.versions === value) || {};
|
||||
getSheetName();
|
||||
};
|
||||
//切换表格
|
||||
const changeSheet = (val: any) => {
|
||||
|
||||
// 切换表名
|
||||
const changeSheet = (val: string) => {
|
||||
getTableData();
|
||||
};
|
||||
//展开树
|
||||
const toggleExpandAll = () => {
|
||||
|
||||
// 一键展开/收起
|
||||
const toggleExpandAll = async () => {
|
||||
isExpandAll.value = !isExpandAll.value;
|
||||
console.log(isExpandAll.value);
|
||||
tableData.value.forEach((row) => {
|
||||
tableRef.value.toggleRowExpansion(row, isExpandAll.value);
|
||||
if (!tableData.value.length || !tableAllRef.value) return;
|
||||
|
||||
if (isExpandAll.value) {
|
||||
// 收集所有已加载节点的ID
|
||||
const allExpandIds = collectAllNodeIds(tableData.value);
|
||||
expandRowKeys.value = allExpandIds;
|
||||
|
||||
// 处理懒加载节点
|
||||
await loadAndExpandAllLazyNodes();
|
||||
} else {
|
||||
// 全部收起
|
||||
expandRowKeys.value = [];
|
||||
}
|
||||
};
|
||||
// 辅助函数:递归收集所有节点ID(包括子节点)
|
||||
const collectAllNodeIds = (nodes: TableRow[]): (string | number)[] => {
|
||||
let ids: (string | number)[] = [];
|
||||
nodes.forEach((node) => {
|
||||
ids.push(node.id);
|
||||
if (node.children && node.children.length > 0) {
|
||||
ids = [...ids, ...collectAllNodeIds(node.children)];
|
||||
}
|
||||
});
|
||||
return ids;
|
||||
};
|
||||
|
||||
//获取版本号
|
||||
// 辅助函数:加载并展开所有懒加载子节点
|
||||
const loadAndExpandAllLazyNodes = async () => {
|
||||
if (!tableAllRef.value) return;
|
||||
try {
|
||||
// 获取所有可能有子节点的父节点
|
||||
const parentNodes = tableData.value.filter((node) => node.hasChildren || (node.children && node.children.length === 0));
|
||||
if (!parentNodes.length) return;
|
||||
|
||||
// 逐个加载并展开子节点
|
||||
for (const parent of parentNodes) {
|
||||
if (tableAllRef.value.loadOrToggleRow) {
|
||||
await tableAllRef.value.loadOrToggleRow(parent);
|
||||
}
|
||||
await nextTick();
|
||||
// 递归展开子节点的子节点
|
||||
if (parent.children && parent.children.length > 0) {
|
||||
const childIds = collectAllNodeIds(parent.children);
|
||||
expandRowKeys.value = [...new Set([...expandRowKeys.value, ...childIds])];
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载并展开懒加载节点失败:', error);
|
||||
} finally {
|
||||
}
|
||||
};
|
||||
// 获取版本号列表
|
||||
const getVersionNums = async () => {
|
||||
try {
|
||||
const params = {
|
||||
@ -179,93 +263,126 @@ const getVersionNums = async () => {
|
||||
};
|
||||
|
||||
const res = await obtainAllVersionNumbers(params);
|
||||
if (res.code == 200) {
|
||||
if (res.code === 200) {
|
||||
options.value = res.data;
|
||||
if (res.data.length > 0) {
|
||||
res.data.forEach((item: any) => {
|
||||
versionMap.set(item.versions, item);
|
||||
});
|
||||
queryForm.value.versions = res.data[0].versions;
|
||||
versionsData.value = options.value.find((item) => item.versions == queryForm.value.versions);
|
||||
versionMap.clear();
|
||||
options.value.forEach((item) => versionMap.set(item.versions, item));
|
||||
|
||||
if (options.value.length > 0) {
|
||||
queryForm.value.versions = options.value[0].versions;
|
||||
versionsData.value = options.value[0];
|
||||
getSheetName();
|
||||
} else {
|
||||
queryForm.value.versions = '';
|
||||
// getSheetName();
|
||||
sheets.value = [];
|
||||
tableData.value = [];
|
||||
expandRowKeys.value = [];
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
console.error('获取版本号失败:', error);
|
||||
ElMessage.error('获取版本号失败,请刷新重试');
|
||||
}
|
||||
};
|
||||
//获取表名
|
||||
|
||||
// 获取表名列表
|
||||
const getSheetName = async () => {
|
||||
try {
|
||||
const params = {
|
||||
projectId: currentProject.value?.id,
|
||||
versions: queryForm.value.versions
|
||||
};
|
||||
|
||||
const res = await sheetList(params);
|
||||
if (res.code == 200) {
|
||||
if (res.code === 200) {
|
||||
sheets.value = res.data;
|
||||
if (res.data.length > 0) {
|
||||
queryForm.value.sheet = res.data[0];
|
||||
if (sheets.value.length > 0) {
|
||||
queryForm.value.sheet = sheets.value[0];
|
||||
} else {
|
||||
queryForm.value.sheet = '';
|
||||
tableData.value = [];
|
||||
expandRowKeys.value = [];
|
||||
}
|
||||
getTableData();
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
console.error('获取表名失败:', error);
|
||||
ElMessage.error('获取表名失败,请刷新重试');
|
||||
}
|
||||
};
|
||||
//获取表格数据
|
||||
|
||||
// 获取表格数据
|
||||
const getTableData = async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
|
||||
const params = {
|
||||
projectId: currentProject.value?.id,
|
||||
versions: queryForm.value.versions,
|
||||
sheet: queryForm.value.sheet,
|
||||
type: activeTab.value
|
||||
};
|
||||
|
||||
const res = await getTableList(params);
|
||||
if (res.code == 200) {
|
||||
if (res.code === 200) {
|
||||
tableData.value = res.data;
|
||||
if (isExpandAll.value) {
|
||||
// 展开全部
|
||||
isExpandAll.value = false;
|
||||
toggleExpandAll();
|
||||
}
|
||||
modifyPrice.clear();
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
console.error('获取表格数据失败:', error);
|
||||
ElMessage.error('获取表格数据失败,请刷新重试');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
//导入
|
||||
const importExcel = (options: any): any => {
|
||||
let formData = new FormData();
|
||||
|
||||
// 导入Excel
|
||||
const importExcel = (options: any): void => {
|
||||
if (!queryForm.value.versions || (activeTab.value !== '3' && !queryForm.value.sheet)) {
|
||||
ElMessage.warning('请先选择版本号和表名(物资设备清单无需选择表名)');
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('file', options.file);
|
||||
loading.value = true;
|
||||
|
||||
importExcelFile(
|
||||
{ projectId: currentProject.value?.id, sheet: queryForm.value.sheet, versions: queryForm.value.versions, type: activeTab.value },
|
||||
{
|
||||
projectId: currentProject.value?.id,
|
||||
sheet: queryForm.value.sheet,
|
||||
versions: queryForm.value.versions,
|
||||
type: activeTab.value
|
||||
},
|
||||
formData
|
||||
)
|
||||
.then((res) => {
|
||||
const { code } = res;
|
||||
if (code == 200) {
|
||||
proxy.$modal.msgSuccess(res.msg || '导入成功');
|
||||
if (res.code === 200) {
|
||||
proxy?.$modal.msgSuccess(res.msg || '导入成功');
|
||||
getTableData();
|
||||
} else {
|
||||
proxy.$modal.msgError(res.msg || '导入失败');
|
||||
proxy?.$modal.msgError(res.msg || '导入失败');
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
proxy.$modal.msgError(err.msg || '导入失败');
|
||||
proxy?.$modal.msgError(err.msg || '导入失败');
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
//导出
|
||||
|
||||
// 导出Excel
|
||||
const handleExport = () => {
|
||||
if (!queryForm.value.versions || (activeTab.value !== '3' && !queryForm.value.sheet)) {
|
||||
ElMessage.warning('请先选择版本号和表名(物资设备清单无需选择表名)');
|
||||
return;
|
||||
}
|
||||
|
||||
proxy?.download(
|
||||
'/tender/tenderPlanLimitList/export',
|
||||
{
|
||||
@ -274,90 +391,108 @@ const handleExport = () => {
|
||||
versions: queryForm.value.versions,
|
||||
type: activeTab.value
|
||||
},
|
||||
`招标一览表${queryForm.value.sheet}.xlsx`
|
||||
`招标一览_${queryForm.value.sheet || (activeTab.value === '3' ? '物资设备清单' : '')}_v${queryForm.value.versions}.xlsx`
|
||||
);
|
||||
};
|
||||
const modifyPrice = new Map();
|
||||
|
||||
const changePrice = (row: any) => {
|
||||
modifyPrice.set(row.id, row);
|
||||
// if (!row.unitPrice) {
|
||||
// modifyPrice.delete(row.id);
|
||||
// }
|
||||
};
|
||||
//修改单价
|
||||
const handleSave = (row?: any, type?: any) => {
|
||||
try {
|
||||
if (type == 'single') {
|
||||
loading.value = true;
|
||||
const list = [{ ...row, type: activeTab.value }];
|
||||
updatePrice(list).then((res) => {
|
||||
if (res.code == 200) {
|
||||
ElMessage({
|
||||
message: '修改成功',
|
||||
type: 'success'
|
||||
});
|
||||
getTableData();
|
||||
}
|
||||
});
|
||||
}
|
||||
if (type == 'all') {
|
||||
loading.value = true;
|
||||
const list = [];
|
||||
modifyPrice.forEach((item) => {
|
||||
list.push({ ...item, type: activeTab.value });
|
||||
});
|
||||
updatePrice(list).then((res) => {
|
||||
if (res.code == 200) {
|
||||
ElMessage({
|
||||
message: '修改成功',
|
||||
type: 'success'
|
||||
});
|
||||
getTableData();
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage({
|
||||
message: '修改失败',
|
||||
type: 'error'
|
||||
});
|
||||
} finally {
|
||||
loading.value = false;
|
||||
// 记录待修改价格的行
|
||||
const changePrice = (row: TableRow) => {
|
||||
if (row.id && row.unitPrice !== undefined) {
|
||||
modifyPrice.set(row.id, row);
|
||||
} else if (row.id) {
|
||||
modifyPrice.delete(row.id);
|
||||
}
|
||||
};
|
||||
|
||||
/** 审核按钮操作 */
|
||||
const handleAudit = async () => {
|
||||
let id = versionMap.get(queryForm.value.versions).id;
|
||||
console.log(id);
|
||||
if (activeTab.value == '2') {
|
||||
// 保存价格修改
|
||||
const handleSave = (row?: TableRow, type?: 'single' | 'all') => {
|
||||
if (versionsData.value.status !== 'draft') {
|
||||
ElMessage.warning('仅草稿状态可修改单价');
|
||||
return;
|
||||
}
|
||||
|
||||
let updateList: TableRow[] = [];
|
||||
if (type === 'single' && row?.id) {
|
||||
updateList = [{ ...row, type: activeTab.value }];
|
||||
} else if (type === 'all') {
|
||||
updateList = Array.from(modifyPrice.values()).map((item) => ({ ...item, type: activeTab.value }));
|
||||
if (updateList.length === 0) {
|
||||
ElMessage.warning('暂无待修改的单价数据');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
loading.value = true;
|
||||
updatePrice(updateList)
|
||||
.then((res) => {
|
||||
if (res.code === 200) {
|
||||
ElMessage.success('修改成功');
|
||||
getTableData();
|
||||
modifyPrice.clear();
|
||||
} else {
|
||||
ElMessage.error(res.msg || '修改失败');
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
ElMessage.error(err.msg || '修改失败');
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
// 审核/查看流程
|
||||
const handleAudit = () => {
|
||||
const versionItem = versionMap.get(queryForm.value.versions);
|
||||
if (!versionItem?.id) {
|
||||
ElMessage.warning('请先选择有效的版本号');
|
||||
return;
|
||||
}
|
||||
|
||||
if (activeTab.value === '2') {
|
||||
proxy?.$tab.openPage('/approval/tenderBidd/indexEdit', '招采工程量清单审核', {
|
||||
id: id,
|
||||
id: versionItem.id,
|
||||
type: 'update'
|
||||
});
|
||||
}
|
||||
if (activeTab.value == '3') {
|
||||
} else if (activeTab.value === '3') {
|
||||
proxy?.$tab.openPage('/approval/tenderBidd/indexEdit2', '物资设备清单审核', {
|
||||
id: id,
|
||||
id: versionItem.id,
|
||||
type: 'update'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
//监听项目id刷新数据
|
||||
// 监听项目切换
|
||||
const listeningProject = watch(
|
||||
() => currentProject.value?.id,
|
||||
(nid, oid) => {
|
||||
getVersionNums();
|
||||
(newId, oldId) => {
|
||||
if (newId && newId !== oldId) {
|
||||
getVersionNums();
|
||||
} else {
|
||||
tableData.value = [];
|
||||
options.value = [];
|
||||
sheets.value = [];
|
||||
queryForm.value = { versions: '', sheet: '' };
|
||||
versionsData.value = {};
|
||||
expandRowKeys.value = [];
|
||||
versionMap.clear();
|
||||
modifyPrice.clear();
|
||||
}
|
||||
}
|
||||
);
|
||||
onUnmounted(() => {
|
||||
listeningProject();
|
||||
});
|
||||
|
||||
// 生命周期钩子
|
||||
onMounted(() => {
|
||||
getVersionNums();
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
listeningProject();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
<style scoped lang="scss">
|
||||
.mb8 {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -191,9 +191,16 @@
|
||||
<el-table-column prop="useQuantity" label="剩余量" align="center">
|
||||
<template #default="scope">
|
||||
{{
|
||||
(scope.row.quantity ? Number(scope.row.quantity) : 0) - (scope.row.useQuantity ? Number(scope.row.useQuantity) : 0) == 0
|
||||
? ''
|
||||
: (scope.row.quantity ? Number(scope.row.quantity) : 0) - (scope.row.useQuantity ? Number(scope.row.useQuantity) : 0)
|
||||
(scope.row.quantity ? Number(scope.row.quantity) : 0) -
|
||||
(scope.row.useQuantity ? Number(scope.row.useQuantity) : 0) -
|
||||
(scope.row.selectNum ? Number(scope.row.selectNum) : 0) ==
|
||||
0
|
||||
? activeTab == 2
|
||||
? 0
|
||||
: ''
|
||||
: (scope.row.quantity ? Number(scope.row.quantity) : 0) -
|
||||
(scope.row.selectNum ? Number(scope.row.selectNum) : 0) -
|
||||
(scope.row.useQuantity ? Number(scope.row.useQuantity) : 0)
|
||||
}}
|
||||
</template>
|
||||
</el-table-column>
|
||||
@ -219,12 +226,16 @@
|
||||
<el-table-column prop="price" label="总价" align="center">
|
||||
<template #default="scope">
|
||||
{{
|
||||
((scope.row.quantity ? Number(scope.row.quantity) : 0) - (scope.row.useQuantity ? Number(scope.row.useQuantity) : 0)) *
|
||||
((scope.row.quantity ? Number(scope.row.quantity) : 0) -
|
||||
(scope.row.useQuantity ? Number(scope.row.useQuantity) : 0) -
|
||||
(scope.row.selectNum ? Number(scope.row.selectNum) : 0)) *
|
||||
Number(scope.row.unitPrice) ==
|
||||
0
|
||||
? ''
|
||||
: (
|
||||
((scope.row.quantity ? Number(scope.row.quantity) : 0) - (scope.row.useQuantity ? Number(scope.row.useQuantity) : 0)) *
|
||||
((scope.row.quantity ? Number(scope.row.quantity) : 0) -
|
||||
(scope.row.useQuantity ? Number(scope.row.useQuantity) : 0) -
|
||||
(scope.row.selectNum ? Number(scope.row.selectNum) : 0)) *
|
||||
Number(scope.row.unitPrice)
|
||||
).toFixed(2)
|
||||
}}
|
||||
@ -459,19 +470,11 @@ const getSheetName = async () => {
|
||||
treeForm.value.sheet = res.data[0];
|
||||
} else {
|
||||
treeForm.value.sheet = '';
|
||||
ElMessage({
|
||||
message: '获取表名失败',
|
||||
type: 'warning'
|
||||
});
|
||||
}
|
||||
getTreeList();
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
ElMessage({
|
||||
message: '获取表名失败',
|
||||
type: 'warning'
|
||||
});
|
||||
}
|
||||
};
|
||||
const handleSelection = (selection: any) => {
|
||||
@ -612,17 +615,21 @@ const changeBiddingTime = (value: any, row: any) => {
|
||||
};
|
||||
//修改合同金额
|
||||
const changeContractPrice = (value: any, row: any) => {
|
||||
updateTenderPlan({
|
||||
...row
|
||||
}).then((res) => {
|
||||
if (res.code == 200) {
|
||||
ElMessage({
|
||||
message: '修改成功',
|
||||
type: 'success'
|
||||
});
|
||||
getList();
|
||||
}
|
||||
});
|
||||
if (value <= Number(row.price)) {
|
||||
updateTenderPlan({
|
||||
...row
|
||||
}).then((res) => {
|
||||
if (res.code == 200) {
|
||||
ElMessage({
|
||||
message: '修改成功',
|
||||
type: 'success'
|
||||
});
|
||||
getList();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
ElMessage.error('合同金额不能大于限价金额');
|
||||
}
|
||||
};
|
||||
|
||||
//上传投标文件
|
||||
|
||||
@ -234,17 +234,17 @@
|
||||
<!-- 第十一行:注册人员数量(仅劳务类型显示) -->
|
||||
<el-row :gutter="20" class="mb-4" v-if="form.supplierType === '劳务'">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="一建建造师" prop="build1">
|
||||
<el-input v-model="form.build1" placeholder="请输入一建建造师数量" clearable />
|
||||
<el-form-item label="一建建造师" prop="firstBuildingNumber">
|
||||
<el-input v-model="form.firstBuildingNumber" placeholder="请输入一建建造师数量" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="二建建造师" prop="build2">
|
||||
<el-input v-model="form.build2" placeholder="请输入二建建造师数量" clearable />
|
||||
<el-form-item label="二建建造师" prop="secondBuildingNumber">
|
||||
<el-input v-model="form.secondBuildingNumber" placeholder="请输入二建建造师数量" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="注册造价工程师" prop="build3">
|
||||
<el-input v-model="form.build3" placeholder="请输入注册造价工程师数量" clearable />
|
||||
<el-form-item label="注册造价工程师" prop="registeredEngineerNumber">
|
||||
<el-input v-model="form.registeredEngineerNumber" placeholder="请输入注册造价工程师数量" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="其他(分别写)" prop="build4">
|
||||
<el-input v-model="form.build4" placeholder="请输入其他人员数量" clearable />
|
||||
<el-form-item label="其他(分别写)" prop="otherBuildingNumber">
|
||||
<el-input v-model="form.otherBuildingNumber" placeholder="请输入其他人员数量" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
@ -254,17 +254,17 @@
|
||||
<!-- 第十二行:职称人员数量(仅劳务类型显示) -->
|
||||
<el-row :gutter="20" class="mb-4" v-if="form.supplierType === '劳务'">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="高级工程师人数" prop="personnelNumber1">
|
||||
<el-input v-model="form.personnelNumber1" placeholder="请输高级工程师数量" clearable />
|
||||
<el-form-item label="高级工程师人数" prop="seniorEngineerNumber">
|
||||
<el-input v-model="form.seniorEngineerNumber" placeholder="请输高级工程师数量" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="工程师数量" prop="personnelNumber2">
|
||||
<el-input v-model="form.personnelNumber2" placeholder="请输入工程师数量" clearable />
|
||||
<el-form-item label="工程师数量" prop="engineerNumber">
|
||||
<el-input v-model="form.engineerNumber" placeholder="请输入工程师数量" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="助理工程师数量" prop="personnelNumber3">
|
||||
<el-input v-model="form.personnelNumber3" placeholder="请输入助理工程师数量" clearable />
|
||||
<el-form-item label="助理工程师数量" prop="assistantEngineerNumber">
|
||||
<el-input v-model="form.assistantEngineerNumber" placeholder="请输入助理工程师数量" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="其他人员数量" prop="personnelNumber4">
|
||||
<el-input v-model="form.personnelNumber4" placeholder="请输入其他人员数量" clearable />
|
||||
<el-form-item label="其他人员数量" prop="otherPersonnelNumber">
|
||||
<el-input v-model="form.otherPersonnelNumber" placeholder="请输入其他人员数量" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
@ -285,8 +285,8 @@
|
||||
:data="form"
|
||||
uploadUrl="/supplierInput/supplierInput"
|
||||
:limit="1"
|
||||
:onUploadSuccess="handleUploadSuccess"
|
||||
@handleChange="change"
|
||||
@handleChange="handleFileChange"
|
||||
@handleRemove="handleFileRemove"
|
||||
showFileList
|
||||
>
|
||||
<div><el-button type="primary">上传文件</el-button><br /></div>
|
||||
@ -309,7 +309,7 @@
|
||||
<script setup name="SupplierInput" lang="ts">
|
||||
import { ComponentInternalInstance, getCurrentInstance, onMounted, ref, reactive, toRefs, computed } from 'vue';
|
||||
import { ElFormInstance } from 'element-plus';
|
||||
import { listSupplierInput, getSupplierInput, delSupplierInput } from '@/api/supplierInput/supplierInput/index';
|
||||
import { listSupplierInput, getSupplierInput, delSupplierInput, updateSupplierInput } from '@/api/supplierInput/supplierInput/index';
|
||||
import { SupplierInputVO, SupplierInputQuery, SupplierInputForm, PageData, DialogOption } from '@/api/supplierInput/supplierInput/types';
|
||||
import Pagination from '@/components/Pagination/index.vue';
|
||||
import RightToolbar from '@/components/RightToolbar/index.vue';
|
||||
@ -369,14 +369,14 @@ const initFormData: any = {
|
||||
inputFile: undefined,
|
||||
// state: '0', // 新增默认待审核
|
||||
// 新增:用于表单输入的单独字段
|
||||
build1: undefined, // 一建建造师
|
||||
build2: undefined, // 二建建造师
|
||||
build3: undefined, // 注册造价工程师
|
||||
build4: undefined, // 其他注册人员
|
||||
personnelNumber1: undefined, // 高级工程师
|
||||
personnelNumber2: undefined, // 工程师
|
||||
personnelNumber3: undefined, // 助理工程师
|
||||
personnelNumber4: undefined // 其他职称人员
|
||||
firstBuildingNumber: undefined, // 一建建造师
|
||||
secondBuildingNumber: undefined, // 二建建造师
|
||||
registeredEngineerNumber: undefined, // 注册造价工程师
|
||||
otherBuildingNumber: undefined, // 其他注册人员
|
||||
seniorEngineerNumber: undefined, // 高级工程师
|
||||
engineerNumber: undefined, // 工程师
|
||||
assistantEngineerNumber: undefined, // 助理工程师
|
||||
otherPersonnelNumber: undefined // 其他职称人员
|
||||
};
|
||||
// 核心数据(表单+查询参数)
|
||||
const data = reactive<PageData<SupplierInputForm, SupplierInputQuery>>({
|
||||
@ -411,14 +411,14 @@ const rules = computed(() => {
|
||||
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号格式', trigger: 'blur' }
|
||||
],
|
||||
id: [{ required: true, message: 'ID不能为空', trigger: 'blur' }],
|
||||
build1: [{ required: true, message: '请输入一建建造师数量', trigger: 'change' }],
|
||||
build2: [{ required: true, message: '请输入二建建造师数量', trigger: 'change' }],
|
||||
build3: [{ required: true, message: '请输入注册造价工程师数量', trigger: 'change' }],
|
||||
build4: [{ required: true, message: '请输入其他数量', trigger: 'change' }],
|
||||
personnelNumber1: [{ required: true, message: '请输入高级工程师数量', trigger: 'change' }],
|
||||
personnelNumber2: [{ required: true, message: '请输入工程师数量', trigger: 'change' }],
|
||||
personnelNumber3: [{ required: true, message: '请输入助理工程师数量', trigger: 'change' }],
|
||||
personnelNumber4: [{ required: true, message: '请输入其他数量', trigger: 'change' }]
|
||||
firstBuildingNumber: [{ required: true, message: '请输入一建建造师数量', trigger: 'change' }],
|
||||
secondBuildingNumber: [{ required: true, message: '请输入二建建造师数量', trigger: 'change' }],
|
||||
registeredEngineerNumber: [{ required: true, message: '请输入注册造价工程师数量', trigger: 'change' }],
|
||||
otherBuildingNumber: [{ required: true, message: '请输入其他数量', trigger: 'change' }],
|
||||
seniorEngineerNumber: [{ required: true, message: '请输入高级工程师数量', trigger: 'change' }],
|
||||
engineerNumber: [{ required: true, message: '请输入工程师数量', trigger: 'change' }],
|
||||
assistantEngineerNumber: [{ required: true, message: '请输入助理工程师数量', trigger: 'change' }],
|
||||
otherPersonnelNumber: [{ required: true, message: '请输入其他数量', trigger: 'change' }]
|
||||
};
|
||||
|
||||
// 仅当类型为"劳务"时,添加安全生产许可证+人员数量校验
|
||||
@ -430,15 +430,15 @@ const rules = computed(() => {
|
||||
safeCodeData: [{ required: true, message: '请选择安全生产许可证发证日期', trigger: 'change' }],
|
||||
safeCertificateValidity: [{ required: true, message: '请选择安全生产许可证有效期', trigger: 'change' }],
|
||||
// 注册人员数量校验
|
||||
build1: [{ required: true, message: '请输入一建建造师数量', trigger: 'blur' }],
|
||||
build2: [{ required: true, message: '请输入二建建造师数量', trigger: 'blur' }],
|
||||
build3: [{ required: true, message: '请输入注册造价工程师数量', trigger: 'blur' }],
|
||||
build4: [{ required: true, message: '请输入其他注册人员数量', trigger: 'blur' }],
|
||||
firstBuildingNumber: [{ required: true, message: '请输入一建建造师数量', trigger: 'blur' }],
|
||||
secondBuildingNumber: [{ required: true, message: '请输入二建建造师数量', trigger: 'blur' }],
|
||||
registeredEngineerNumber: [{ required: true, message: '请输入注册造价工程师数量', trigger: 'blur' }],
|
||||
otherBuildingNumber: [{ required: true, message: '请输入其他注册人员数量', trigger: 'blur' }],
|
||||
// 职称人员数量校验
|
||||
personnelNumber1: [{ required: true, message: '请输入高级工程师数量', trigger: 'blur' }],
|
||||
personnelNumber2: [{ required: true, message: '请输入工程师数量', trigger: 'blur' }],
|
||||
personnelNumber3: [{ required: true, message: '请输入助理工程师数量', trigger: 'blur' }],
|
||||
personnelNumber4: [{ required: true, message: '请输入其他职称人员数量', trigger: 'blur' }]
|
||||
seniorEngineerNumber: [{ required: true, message: '请输入高级工程师数量', trigger: 'blur' }],
|
||||
engineerNumber: [{ required: true, message: '请输入工程师数量', trigger: 'blur' }],
|
||||
assistantEngineerNumber: [{ required: true, message: '请输入助理工程师数量', trigger: 'blur' }],
|
||||
otherPersonnelNumber: [{ required: true, message: '请输入其他职称人员数量', trigger: 'blur' }]
|
||||
};
|
||||
}
|
||||
|
||||
@ -455,22 +455,26 @@ const handleTypeChange = () => {
|
||||
form.value.registeredNumber = undefined;
|
||||
form.value.personnelNumber = undefined;
|
||||
// 清空表单单独字段
|
||||
form.value.build1 = form.value.build2 = form.value.build3 = form.value.build4 = undefined;
|
||||
form.value.personnelNumber1 = form.value.personnelNumber2 = form.value.personnelNumber3 = form.value.personnelNumber4 = undefined;
|
||||
form.value.firstBuildingNumber =
|
||||
form.value.secondBuildingNumber =
|
||||
form.value.registeredEngineerNumber =
|
||||
form.value.otherBuildingNumber =
|
||||
undefined;
|
||||
form.value.seniorEngineerNumber = form.value.engineerNumber = form.value.assistantEngineerNumber = form.value.otherPersonnelNumber = undefined;
|
||||
}
|
||||
// 重置隐藏字段的校验状态,避免错误提示残留
|
||||
supplierInputFormRef.value?.clearValidate([
|
||||
'safeCode',
|
||||
'safeCodeData',
|
||||
'safeCertificateValidity',
|
||||
'build1',
|
||||
'build2',
|
||||
'build3',
|
||||
'build4',
|
||||
'personnelNumber1',
|
||||
'personnelNumber2',
|
||||
'personnelNumber3',
|
||||
'personnelNumber4'
|
||||
'firstBuildingNumber',
|
||||
'secondBuildingNumber',
|
||||
'registeredEngineerNumber',
|
||||
'otherBuildingNumber',
|
||||
'seniorEngineerNumber',
|
||||
'engineerNumber',
|
||||
'assistantEngineerNumber',
|
||||
'otherPersonnelNumber'
|
||||
]);
|
||||
};
|
||||
|
||||
@ -517,18 +521,18 @@ const resetQuery = () => {
|
||||
const splitBackEndStrToForm = (resData: any) => {
|
||||
if (resData.registeredNumber) {
|
||||
const registeredArr = resData.registeredNumber.split(',');
|
||||
form.value.build1 = registeredArr[0] || undefined; // 一建建造师
|
||||
form.value.build2 = registeredArr[1] || undefined; // 二建建造师
|
||||
form.value.build3 = registeredArr[2] || undefined; // 注册造价工程师
|
||||
form.value.build4 = registeredArr[3] || undefined; // 其他注册人员
|
||||
form.value.firstBuildingNumber = registeredArr[0] || undefined; // 一建建造师
|
||||
form.value.secondBuildingNumber = registeredArr[1] || undefined; // 二建建造师
|
||||
form.value.registeredEngineerNumber = registeredArr[2] || undefined; // 注册造价工程师
|
||||
form.value.otherBuildingNumber = registeredArr[3] || undefined; // 其他注册人员
|
||||
}
|
||||
|
||||
if (resData.personnelNumber) {
|
||||
const personnelArr = resData.personnelNumber.split(',');
|
||||
form.value.personnelNumber1 = personnelArr[0] || undefined; // 高级工程师
|
||||
form.value.personnelNumber2 = personnelArr[1] || undefined; // 工程师
|
||||
form.value.personnelNumber3 = personnelArr[2] || undefined; // 助理工程师
|
||||
form.value.personnelNumber4 = personnelArr[3] || undefined; // 其他职称人员
|
||||
form.value.seniorEngineerNumber = personnelArr[0] || undefined; // 高级工程师
|
||||
form.value.engineerNumber = personnelArr[1] || undefined; // 工程师
|
||||
form.value.assistantEngineerNumber = personnelArr[2] || undefined; // 助理工程师
|
||||
form.value.otherPersonnelNumber = personnelArr[3] || undefined; // 其他职称人员
|
||||
}
|
||||
};
|
||||
/** 审核过程按钮操作 */
|
||||
@ -572,6 +576,7 @@ const handleAdd = () => {
|
||||
dialog.title = '添加供应商入库';
|
||||
};
|
||||
|
||||
const editFileId = ref('');
|
||||
const handleUpdate = async (row?: SupplierInputVO) => {
|
||||
reset();
|
||||
const _id = row?.id || ids.value[0];
|
||||
@ -579,7 +584,8 @@ const handleUpdate = async (row?: SupplierInputVO) => {
|
||||
|
||||
try {
|
||||
const res = await getSupplierInput(_id);
|
||||
const resData = res.data || {};
|
||||
const resData: any = res.data || {};
|
||||
editFileId.value = resData.fileId;
|
||||
// 1. 基础字段回显
|
||||
form.value = { ...form.value, ...resData, inputFile: '' };
|
||||
// 2. 核心修复:拆分后端拼接字符串到表单单独字段
|
||||
@ -594,32 +600,61 @@ const handleUpdate = async (row?: SupplierInputVO) => {
|
||||
}
|
||||
};
|
||||
|
||||
const fileStatus = ref(false);
|
||||
const updateFileStatus = ref(true);
|
||||
const isUpdateFile = ref(false); //记录是否在修改页面时是否有新上传的文件
|
||||
const handleFileChange = (file, fileList) => {
|
||||
if (form.value.id) {
|
||||
updateFileStatus.value = true;
|
||||
isUpdateFile.value = true; //记录是否在修改页面时是否有新上传的文件
|
||||
}
|
||||
fileStatus.value = true;
|
||||
};
|
||||
|
||||
const handleFileRemove = (file, fileList) => {
|
||||
if (form.value.id) {
|
||||
updateFileStatus.value = false;
|
||||
isUpdateFile.value = false; //记录是否在修改页面时是否有新上传的文件
|
||||
}
|
||||
|
||||
fileStatus.value = false;
|
||||
};
|
||||
/** 提交表单 */
|
||||
const submitForm = () => {
|
||||
supplierInputFormRef.value?.validate(async (valid: boolean) => {
|
||||
if (!valid) return;
|
||||
if (form.value.supplierType === '劳务') {
|
||||
form.value.registeredNumber = [form.value.build1, form.value.build2, form.value.build3, form.value.build4].join(',');
|
||||
form.value.registeredNumber = [
|
||||
form.value.firstBuildingNumber,
|
||||
form.value.secondBuildingNumber,
|
||||
form.value.registeredEngineerNumber,
|
||||
form.value.otherBuildingNumber
|
||||
].join(',');
|
||||
form.value.personnelNumber = [
|
||||
form.value.personnelNumber1,
|
||||
form.value.personnelNumber2,
|
||||
form.value.personnelNumber3,
|
||||
form.value.personnelNumber4
|
||||
form.value.seniorEngineerNumber,
|
||||
form.value.engineerNumber,
|
||||
form.value.assistantEngineerNumber,
|
||||
form.value.otherPersonnelNumber
|
||||
].join(',');
|
||||
}
|
||||
buttonLoading.value = true;
|
||||
try {
|
||||
if (fileUploadRef.value) {
|
||||
await fileUploadRef.value.submitUpload().then((res) => {
|
||||
console.log(res);
|
||||
if (res == 'noFile') {
|
||||
proxy?.$modal.msgError('请上传文件');
|
||||
return;
|
||||
}
|
||||
proxy?.$modal.msgSuccess('操作成功');
|
||||
dialog.visible = false;
|
||||
getList();
|
||||
});
|
||||
if (form.value.fileId === editFileId.value && !isUpdateFile.value) {
|
||||
console.log(1111111111);
|
||||
|
||||
editFormData();
|
||||
} else {
|
||||
fileUploadRef.value.submitUpload().then((res) => {
|
||||
if (res == 'noFile') {
|
||||
proxy?.$modal.msgError('请上传文件');
|
||||
return;
|
||||
}
|
||||
proxy?.$modal.msgSuccess('操作成功');
|
||||
dialog.visible = false;
|
||||
getList();
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
proxy?.$modal.msgError('提交失败,请重试');
|
||||
@ -628,7 +663,14 @@ const submitForm = () => {
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const editFormData = async () => {
|
||||
const res = await updateSupplierInput(form.value);
|
||||
if ((res.code = 200)) {
|
||||
proxy?.$modal.msgSuccess('操作成功');
|
||||
dialog.visible = false;
|
||||
getList();
|
||||
}
|
||||
};
|
||||
/** 删除操作 */
|
||||
const handleDelete = async (row?: SupplierInputVO) => {
|
||||
const _ids = row?.id || ids.value;
|
||||
|
||||
@ -104,8 +104,9 @@
|
||||
<el-col :span="12">
|
||||
<el-form-item label="近三年营业额" prop="pastThreeYears">
|
||||
<el-input v-model="form.pastThreeYears" placeholder="请输入近三年营业额" clearable />
|
||||
</el-form-item> </el-col
|
||||
><el-col :span="12">
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<!-- <el-col :span="12">
|
||||
<el-form-item label="生产许可证编号" prop="safeCode">
|
||||
<el-input v-model="form.safeCode" placeholder="请输入许可证编号" clearable />
|
||||
</el-form-item>
|
||||
@ -118,45 +119,45 @@
|
||||
<el-form-item label="生产许可证发证日期" prop="safeCertificateValidity">
|
||||
<el-date-picker v-model="form.safeCertificateValidity" type="date" placeholder="请选择发证日期" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-col> -->
|
||||
</el-row>
|
||||
<el-row class="mb-4" v-if="form.supplierType === '劳务'">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="一建建造师" prop="build1">
|
||||
<el-input v-model="form.build1" placeholder="请输入一建建造师数量" clearable />
|
||||
<el-form-item label="一建建造师" prop="firstBuildingNumber">
|
||||
<el-input v-model="form.firstBuildingNumber" placeholder="请输入一建建造师数量" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="二建建造师" prop="build2">
|
||||
<el-input v-model="form.build2" placeholder="请输入二建建造师数量" clearable />
|
||||
<el-form-item label="二建建造师" prop="secondBuildingNumber">
|
||||
<el-input v-model="form.secondBuildingNumber" placeholder="请输入二建建造师数量" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="其他(分别写)" prop="build4">
|
||||
<el-input v-model="form.build3" placeholder="请输入其他人员数量" clearable />
|
||||
<el-form-item label="其他(分别写)" prop="otherBuildingNumber">
|
||||
<el-input v-model="form.otherBuildingNumber" placeholder="请输入其他人员数量" clearable />
|
||||
</el-form-item> </el-col
|
||||
><el-col :span="12">
|
||||
<el-form-item label="注册造价工程师" prop="build3">
|
||||
<el-input v-model="form.build4" placeholder="请输入注册造价工程师数量" clearable />
|
||||
<el-form-item label="注册造价工程师" prop="registeredEngineerNumber">
|
||||
<el-input v-model="form.registeredEngineerNumber" placeholder="请输入注册造价工程师数量" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="24" class="mb-4" v-if="form.supplierType === '劳务'">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="高级工程师人数" prop="personnelNumber1">
|
||||
<el-input v-model="form.personnelNumber1" placeholder="请输高级工程师数量" clearable />
|
||||
<el-form-item label="高级工程师人数" prop="seniorEngineerNumber">
|
||||
<el-input v-model="form.seniorEngineerNumber" placeholder="请输高级工程师数量" clearable />
|
||||
</el-form-item> </el-col
|
||||
><el-col :span="12">
|
||||
<el-form-item label="工程师数量" prop="personnelNumber2">
|
||||
<el-input v-model="form.personnelNumber2" placeholder="请输入工程师数量" clearable />
|
||||
<el-form-item label="工程师数量" prop="engineerNumber">
|
||||
<el-input v-model="form.engineerNumber" placeholder="请输入工程师数量" clearable />
|
||||
</el-form-item> </el-col
|
||||
><el-col :span="12">
|
||||
<el-form-item label="助理工程师数量" prop="personnelNumber3">
|
||||
<el-input v-model="form.personnelNumber3" placeholder="请输入助理工程师数量" clearable />
|
||||
<el-form-item label="助理工程师数量" prop="assistantEngineerNumber">
|
||||
<el-input v-model="form.assistantEngineerNumber" placeholder="请输入助理工程师数量" clearable />
|
||||
</el-form-item> </el-col
|
||||
><el-col :span="12">
|
||||
<el-form-item label="其他人员数量" prop="personnelNumber4">
|
||||
<el-input v-model="form.personnelNumber4" placeholder="请输入其他人员数量" clearable />
|
||||
<el-form-item label="其他人员数量" prop="otherPersonnelNumber">
|
||||
<el-input v-model="form.otherPersonnelNumber" placeholder="请输入其他人员数量" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@ -270,14 +271,14 @@ const initFormData = {
|
||||
inputFile: undefined,
|
||||
state: '0', // 新增默认待审核
|
||||
// 新增:用于表单输入的单独字段
|
||||
build1: undefined, // 一建建造师
|
||||
build2: undefined, // 二建建造师
|
||||
build3: undefined, // 注册造价工程师
|
||||
build4: undefined, // 其他注册人员
|
||||
personnelNumber1: undefined, // 高级工程师
|
||||
personnelNumber2: undefined, // 工程师
|
||||
personnelNumber3: undefined, // 助理工程师
|
||||
personnelNumber4: undefined // 其他职称人员
|
||||
firstBuildingNumber: undefined, // 一建建造师
|
||||
secondBuildingNumber: undefined, // 二建建造师
|
||||
registeredEngineerNumber: undefined, // 注册造价工程师
|
||||
otherBuildingNumber: undefined, // 其他注册人员
|
||||
seniorEngineerNumber: undefined, // 高级工程师
|
||||
engineerNumber: undefined, // 工程师
|
||||
assistantEngineerNumber: undefined, // 助理工程师
|
||||
otherPersonnelNumber: undefined
|
||||
};
|
||||
const data = reactive<PageData<LeaveForm, LeaveQuery>>({
|
||||
form: { ...initFormData },
|
||||
@ -332,17 +333,27 @@ const getInfo = () => {
|
||||
buttonLoading.value = false;
|
||||
nextTick(async () => {
|
||||
const res = await getSupplierInput(routeParams.value.id);
|
||||
console.log(res, '------------------res');
|
||||
|
||||
Object.assign(form.value, res.data);
|
||||
form.value.registeredNumber = form.value.registeredNumber?.split(',');
|
||||
form.value.build1 = form.value.registeredNumber[0] || '';
|
||||
form.value.build2 = form.value.registeredNumber[1] || '';
|
||||
form.value.build3 = form.value.registeredNumber[2] || '';
|
||||
form.value.build4 = form.value.registeredNumber[3] || '';
|
||||
form.value.personnelNumber = form.value.personnelNumber?.split(',');
|
||||
form.value.personnelNumber1 = form.value.personnelNumber[0] || '';
|
||||
form.value.personnelNumber2 = form.value.personnelNumber[1] || '';
|
||||
form.value.personnelNumber3 = form.value.personnelNumber[2] || '';
|
||||
form.value.personnelNumber4 = form.value.personnelNumber[3] || '';
|
||||
// form.value.firstBuildingNumber=res.data.firstBuildingNumber, // 一建建造师
|
||||
// secondBuildingNumber: undefined, // 二建建造师
|
||||
// registeredEngineerNumber: undefined, // 注册造价工程师
|
||||
// otherBuildingNumber: undefined, // 其他注册人员
|
||||
// seniorEngineerNumber: undefined, // 高级工程师
|
||||
// engineerNumber: undefined, // 工程师
|
||||
// assistantEngineerNumber: undefined, // 助理工程师
|
||||
// otherPersonnelNumber: undefined
|
||||
// form.value.registeredNumber = form.value.registeredNumber?.split(',');
|
||||
// form.value.build1 = form.value.registeredNumber[0] || '';
|
||||
// form.value.build2 = form.value.registeredNumber[1] || '';
|
||||
// form.value.build3 = form.value.registeredNumber[2] || '';
|
||||
// form.value.build4 = form.value.registeredNumber[3] || '';
|
||||
// form.value.personnelNumber = form.value.personnelNumber?.split(',');
|
||||
// form.value.personnelNumber1 = form.value.personnelNumber[0] || '';
|
||||
// form.value.personnelNumber2 = form.value.personnelNumber[1] || '';
|
||||
// form.value.personnelNumber3 = form.value.personnelNumber[2] || '';
|
||||
// form.value.personnelNumber4 = form.value.personnelNumber[3] || '';
|
||||
|
||||
loading.value = false;
|
||||
buttonLoading.value = false;
|
||||
|
||||
Reference in New Issue
Block a user