diff --git a/package.json b/package.json index a4b6e79..536398b 100644 --- a/package.json +++ b/package.json @@ -67,10 +67,12 @@ "vue-json-pretty": "2.4.0", "vue-print-nb": "^1.7.5", "vue-router": "4.4.5", + "vue-simple-uploader": "^1.0.3", "vue-types": "5.1.3", "vue3-print-nb": "^0.1.4", "vue3-scroll-seamless": "^1.0.6", - "vxe-table": "4.5.22" + "vxe-table": "4.5.22", + "xlsx": "^0.18.5" }, "devDependencies": { "@eslint/js": "9.15.0", diff --git a/src/api/menu.ts b/src/api/menu.ts index fc1d542..a65bb63 100644 --- a/src/api/menu.ts +++ b/src/api/menu.ts @@ -4,6 +4,8 @@ import { RouteRecordRaw } from 'vue-router'; // 获取路由 export function getRouters(id: string): AxiosPromise { + console.log('🚀 ~ getRouters ~ id:', id); + return request({ url: '/system/menu/getRouters/' + id, method: 'get' diff --git a/src/api/project/busSalaryDetails/index.ts b/src/api/project/busSalaryDetails/index.ts new file mode 100644 index 0000000..0b62b42 --- /dev/null +++ b/src/api/project/busSalaryDetails/index.ts @@ -0,0 +1,61 @@ +import request from '@/utils/request-go'; +// 查询员工工资考核记录列表 +export function listBusSalaryDetails(query: object) { + return request({ + url: '/zm/api/v1/system/busSalaryDetails/list', + method: 'get', + params: query + }); +} +// 查询员工工资考核记录详细 +export function getBusSalaryDetails(id: number) { + return request({ + url: '/zm/api/v1/system/busSalaryDetails/get', + method: 'get', + params: { + id: id.toString() + } + }); +} +// 新增员工工资考核记录 +export function addBusSalaryDetails(data: object) { + return request({ + url: '/zm/api/v1/system/busSalaryDetails/add', + method: 'post', + data: data + }); +} +// 修改员工工资考核记录 +export function updateBusSalaryDetails(data: object) { + return request({ + url: '/zm/api/v1/system/busSalaryDetails/edit', + method: 'put', + data: data + }); +} +// 删除员工工资考核记录 +export function delBusSalaryDetails(ids: number[]) { + return request({ + url: '/zm/api/v1/system/busSalaryDetails/delete', + method: 'delete', + data: { + ids: ids + } + }); +} +// 根据身份证获取当前考勤记录明细 +export function getByIdDetail(params) { + return request({ + url: '/zm/api/v1/system/busSalaryDetails/getByIdDetail', + method: 'get', + params + }); +} +// 工资条获取(可根据项目or班组or施工人员) +export function salarySheet(params) { + return request({ + url: '/zm/api/v1/system/busSalaryDetails/salarySheet', + method: 'get', + params + }); +} diff --git a/src/api/project/projectTeam/index.ts b/src/api/project/projectTeam/index.ts index bbf4dc1..a8540c0 100644 --- a/src/api/project/projectTeam/index.ts +++ b/src/api/project/projectTeam/index.ts @@ -75,3 +75,12 @@ export const delProjectTeam = (ids: string | number | Array) => params: { ids } }); }; + +// 根据项目或班组导出出勤记录(班组) +export function exportSalary(data: any) { + return request({ + url: '/zm/api/v1/system/busConstructionUser/exportSalary', + method: 'post', + data + }); +} diff --git a/src/api/safety/violationLevel/index.ts b/src/api/safety/violationLevel/index.ts index b20965c..7b2d4dd 100644 --- a/src/api/safety/violationLevel/index.ts +++ b/src/api/safety/violationLevel/index.ts @@ -57,7 +57,7 @@ export const updateViolationLevel = (data: ViolationLevelForm) => { * @param id */ export const delViolationLevel = (id: string | number | Array) => { - return request({ + return request1({ url: '/zm/api/v1/system/busViolationLevel/delete', method: 'delete', data: { ids: id } diff --git a/src/components/bigUploader/index.vue b/src/components/bigUploader/index.vue new file mode 100644 index 0000000..4529f63 --- /dev/null +++ b/src/components/bigUploader/index.vue @@ -0,0 +1,427 @@ + + + + + diff --git a/src/main.ts b/src/main.ts index 8197ba2..8cd9035 100644 --- a/src/main.ts +++ b/src/main.ts @@ -61,6 +61,7 @@ setLocal('dockSocketUrl', 'ws://58.17.134.85:9512/websocket'); ElDialog.props.closeOnClickModal.default = false;*/ // **main.js** import { vue3ScrollSeamless } from 'vue3-scroll-seamless'; +import uploader from 'vue-simple-uploader'; import bus from './utils/bus'; import $message from '@/plugins/modal'; @@ -74,6 +75,8 @@ app.use(print); app.use(i18n); app.use(VXETable); app.use(plugins); +app.use(uploader); + app.use(bus); app.component('vue3ScrollSeamless', vue3ScrollSeamless); // 自定义指令 diff --git a/src/store/modules/permission.ts b/src/store/modules/permission.ts index 7efcf2d..4678281 100644 --- a/src/store/modules/permission.ts +++ b/src/store/modules/permission.ts @@ -45,7 +45,9 @@ export const usePermissionStore = defineStore('permission', () => { sidebarRouters.value = routes; }; const generateRoutes = async (): Promise => { - const res = await getRouters(useUserStoreHook().selectedProject?.id || '0'); + const id = useUserStoreHook().selectedProject ? useUserStoreHook().selectedProject.id : '0'; + console.log('🚀 ~ generateRoutes ~ useUserStoreHook().selectedProject?.id:', id); + const res = await getRouters(id); const { data } = res; const sdata = JSON.parse(JSON.stringify(data)); const rdata = JSON.parse(JSON.stringify(data)); diff --git a/src/utils/auth.ts b/src/utils/auth.ts index 0b21b0b..33152c7 100644 --- a/src/utils/auth.ts +++ b/src/utils/auth.ts @@ -10,6 +10,10 @@ export const setToken = (access_token: string) => (tokenStorage.value = access_t export const removeToken = () => (tokenStorage.value = null); +export const getGoToken = () => { + return getLocal('goToken'); +}; + export const getDockSocketUrl = () => { return getLocal('dockSocketUrl'); }; diff --git a/src/utils/exportExcel.ts b/src/utils/exportExcel.ts new file mode 100644 index 0000000..ecd7a74 --- /dev/null +++ b/src/utils/exportExcel.ts @@ -0,0 +1,212 @@ +import { exportSalary } from '@/api/project/projectTeam'; +import { exportDataToExcel } from '@/utils/exportDataToExcel.js'; + +export function useExcelExport() { + // 数据整合函数 + const exportConfig = (obj, proxy, times) => { + // 如果导出前要处理数据,需要深克隆一份表格数据,然后进行处理 + let time = proxy.parseTime(new Date(times), '{y}年{m}月'); // 当前年月 + let header1 = ['建筑施工企业现场人员考勤表(' + time + ')']; + let projectName = '项目部名称:' + obj.projectName; + let teamName = '班组类别:' + obj.teamName; + let header2 = []; + let header3 = [projectName + ' ' + teamName]; + const header = ['序号', '姓名/日期', '身份证号']; + let columnsWidth = [6, 10, 26]; // 表格宽度 + let obj1 = { index: '', name: '', identity_card: '' }; + let fields = ['index', 'row', 'identity_card']; + + // 计算当前月天数 + let years = times.split('-')[0]; + let month = times.split('-')[1]; + let listLength = new Date(years, month, 0).getDate(); + + for (let index = 0; index < listLength; index++) { + obj1['day' + index] = ''; + fields.push('day' + index); + header.push(index + 1); + columnsWidth.push(4); + } + + header.push('合计'); + fields.push('sum'); + header.push('是否离场'); + fields.push('type'); + header.push('签字'); + fields.push('sign'); + + // 上方共用 + let data = []; + if (!(obj.team && obj.team.length)) return; + + obj.team.forEach((item, index) => { + let obj = { row: '', index: '', identity_card: '' }; + + for (let key in item.attendance) { + let j = parseInt(key.split('-')[2]) - 1; + obj['day' + j] = item.attendance[key]; + } + + obj.row = item.row; + obj.index = index + 1; + obj.identity_card = item.identity_card; + + let start = ''; // 从表格那一列哪一行开始 + let end = ''; // 从表格那一列哪一行结束 + let cols = 5 + index; // 行数 + start = 'D' + cols; + + // 判断当月天数 获取表格中最后一天的列标识 + if (listLength == 28) { + end = 'AE' + cols; + } else if (listLength == 29) { + end = 'AF' + cols; + } else if (listLength == 30) { + end = 'AG' + cols; + } else { + end = 'AH' + cols; + } + + obj.sum = { formula: 'SUM(' + start + ':' + end + ')', result: 0 }; + obj.sign = ''; + obj.type = item.type ? '离场' : '未离场'; + data.push(obj); + }); + + const merges = [ + // 单元格合并 + { row: 0, col: 0, rowspan: 1, colspan: listLength + 5 }, + { row: 2, col: 0, rowspan: 1, colspan: listLength + 5 }, + { row: obj.team.length + 5, col: 0, rowspan: 1, colspan: 3 }, + { row: obj.team.length + 5, col: 3, rowspan: 1, colspan: 10 }, + { row: obj.team.length + 5, col: 13, rowspan: 1, colspan: 10 }, + { row: obj.team.length + 5, col: 23, rowspan: 1, colspan: 10 } + ]; + + data.push({}); + // 表格尾部标题 + data.push({ + index: '制表人:', + day0: '班组负责人', + day10: '项目负责人', + day20: '制表日期' + }); + data.push({ + index: '注:', + row: '1、本考勤表必须按照每日实际工时进行填写;' + }); + data.push({ + row: '2、本考勤表作为工资计发的重要依据。' + }); + + const config = { + data, + fields, + headers: [header1, header2, header3, header], + merges, + attrs: [], + view: [], + columnsWidth, + sheetName: obj.teamName + }; + + // 设置全表单元格边框,居中布局 + config.attrs.push({ + rowStart: 3, + rowEnd: config.data.length - 1, + colStart: 0, + colEnd: config.fields.length - 1, + attr: { + alignment: { vertical: 'middle', horizontal: 'center' }, + border: { + top: { style: 'thin' }, + left: { style: 'thin' }, + bottom: { style: 'thin' }, + right: { style: 'thin' } + } + } + }); + + // 设置表头字体加粗 + config.attrs.push({ + rowStart: 0, + rowEnd: 0, + colStart: 0, + colEnd: config.fields.length - 1, + attr: { + alignment: { vertical: 'middle', horizontal: 'center' }, + font: { + bold: true, + size: '16' + } + } + }); + + config.attrs.push({ + rowStart: 2, + rowEnd: 2, + colStart: 0, + colEnd: config.fields.length - 1, + attr: { + alignment: { vertical: 'middle', horizontal: 'center' } + } + }); + + return config; + }; + + // 导出函数 + const exportExcel = async (obj, proxy, name = undefined) => { + try { + const res = await exportSalary(obj); + + if (res.code === 0 && res.data.AttendanceAllOne) { + let config = []; + let AttendanceAllOne = res.data.AttendanceAllOne; + + // 处理每个表格 + AttendanceAllOne.forEach((item) => { + let datas = exportConfig(item, proxy, obj.years); + if (datas) { + config.push(datas); + } + }); + + // 获取项目名 + let projectName = ''; + if (name) { + projectName = res.data.AttendanceAllOne[0].projectName + '-' + name; + } else { + projectName = res.data.AttendanceAllOne[0].projectName; + } + + let year = obj.years.split('-')[0]; + let month = obj.years.split('-')[1]; + + // 导出Excel + exportDataToExcel(config, `${projectName}-${year}年${month}月考勤表.xlsx`); + } else if (res.code === 0) { + ElMessage({ + message: '暂无考勤数据', + type: 'warning' + }); + } else { + ElMessage({ + message: res.message || '导出失败', + type: 'error' + }); + } + } catch (error) { + ElMessage({ + message: '导出过程出错', + type: 'error' + }); + console.error('Excel导出错误:', error); + } + }; + + return { + exportConfig, + exportExcel + }; +} diff --git a/src/views/design/technicalStandard/component/documentsDeails.vue b/src/views/design/technicalStandard/component/documentsDeails.vue index 99e9705..9fa82db 100644 --- a/src/views/design/technicalStandard/component/documentsDeails.vue +++ b/src/views/design/technicalStandard/component/documentsDeails.vue @@ -3,8 +3,8 @@
- - + +
diff --git a/src/views/design/technicalStandard/component/documentsEdit.vue b/src/views/design/technicalStandard/component/documentsEdit.vue index 3a0754b..d282d99 100644 --- a/src/views/design/technicalStandard/component/documentsEdit.vue +++ b/src/views/design/technicalStandard/component/documentsEdit.vue @@ -3,8 +3,8 @@
- - + +
diff --git a/src/views/project/attendance/index.vue b/src/views/project/attendance/index.vue index 6adf06c..0379006 100644 --- a/src/views/project/attendance/index.vue +++ b/src/views/project/attendance/index.vue @@ -47,6 +47,11 @@ --> + @@ -99,6 +104,16 @@ + + + + + + + + @@ -115,10 +130,12 @@ import { pcSelectBelowProjectOfPersonnel, getSysProjectTeamList, pcCollectDataFo const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { type_of_work } = toRefs(proxy?.useDict('clock_status_type', 'type_of_work')); import type { CalendarInstance } from 'element-plus'; +import { useExcelExport } from '@/utils/exportExcel'; // 获取用户 store const userStore = useUserStoreHook(); // 从 store 中获取项目列表和当前选中的项目 const currentProject = computed(() => userStore.selectedProject); +const exportDialogVisible = ref(false); const attendanceList = ref([]); const attendanceTwoWeekList = ref([]); @@ -138,7 +155,15 @@ const dialog = reactive({ title: '' }); const echartsOption = ref({}); -const initFormData: AttendanceForm = { +const exportForm = reactive({ + fuzzyQuery: undefined, + typeOfWork: undefined, + teamId: undefined, + projectId: currentProject.value.goId, + years: undefined, + dateStr: undefined +}); +const initFormData: any = { id: undefined, userId: undefined, facePic: undefined, @@ -157,26 +182,23 @@ const initFormData: AttendanceForm = { typeOfWork: undefined, teamId: undefined }; -const data = reactive>({ +const data = reactive>({ form: { ...initFormData }, queryParams: { pageNum: 1, pageSize: 10, - userName: undefined, dateStr: undefined, - clockStatus: undefined, - commuter: undefined, + fuzzyQuery: undefined, projectId: currentProject.value.goId, typeOfWork: undefined, - teamId: undefined, - params: {} + teamId: undefined }, rules: { id: [{ required: true, message: '主键id不能为空', trigger: 'blur' }], userId: [{ required: true, message: '人员id不能为空', trigger: 'blur' }], facePic: [{ required: true, message: '人脸照不能为空', trigger: 'blur' }], projectId: [{ required: true, message: '项目id不能为空', trigger: 'blur' }], - dateStr: [{ required: true, message: '打卡日期不能为空', trigger: 'blur' }], + years: [{ required: true, message: '导出时间不能为空', trigger: 'blur' }], clockStatus: [{ required: true, message: '1正常,2迟到,3早退,4缺勤,5补卡不能为空', trigger: 'change' }] } }); @@ -315,6 +337,14 @@ const handleDetails = async (row?: AttendanceVO) => { dialog.title = row?.userName || ''; }; +const onExport = () => { + exportDialogVisible.value = true; + exportForm.teamId = queryParams.value.teamId; + exportForm.projectId = queryParams.value.projectId as string; + exportForm.typeOfWork = queryParams.value.typeOfWork; + exportForm.fuzzyQuery = queryParams.value.fuzzyQuery; +}; + /** 提交按钮 */ const submitForm = () => { attendanceFormRef.value?.validate(async (valid: boolean) => { @@ -341,6 +371,18 @@ const init = () => { handleQuery(); }); }; +const formRef = ref(null); +const onSubmit = () => { + const formWrap = unref(formRef) as any; + if (!formWrap) return; + formWrap.validate((valid: boolean) => { + if (valid) { + exportForm.dateStr = exportForm.years; + console.log('🚀 ~ onSubmit ~ exportForm:', exportForm); + useExcelExport().exportExcel(exportForm, proxy, '考勤列表'); + } + }); +}; //监听项目id刷新数据 const listeningProject = watch( diff --git a/src/views/project/busSalaryDetails/component/add.vue b/src/views/project/busSalaryDetails/component/add.vue new file mode 100644 index 0000000..eb3be2d --- /dev/null +++ b/src/views/project/busSalaryDetails/component/add.vue @@ -0,0 +1,169 @@ + + + diff --git a/src/views/project/busSalaryDetails/component/detail.vue b/src/views/project/busSalaryDetails/component/detail.vue new file mode 100644 index 0000000..ae82797 --- /dev/null +++ b/src/views/project/busSalaryDetails/component/detail.vue @@ -0,0 +1,102 @@ + + + diff --git a/src/views/project/busSalaryDetails/component/edit.vue b/src/views/project/busSalaryDetails/component/edit.vue new file mode 100644 index 0000000..0d08744 --- /dev/null +++ b/src/views/project/busSalaryDetails/component/edit.vue @@ -0,0 +1,146 @@ + + + diff --git a/src/views/project/busSalaryDetails/component/model.ts b/src/views/project/busSalaryDetails/component/model.ts new file mode 100644 index 0000000..70b95a1 --- /dev/null +++ b/src/views/project/busSalaryDetails/component/model.ts @@ -0,0 +1,57 @@ +export interface BusSalaryDetailsTableColumns { + id:number + sfzNumber:string; // 身份证 + name:string; // 户名 + account:string; // 账户 + sumDuration:number; // 当月总时长 + salary:number; // 薪水(天) + dateOfIssue:string; // 发放年月 + lister:string; // 制表人 + createdAt:string; // 创建时间 +} + + +export interface BusSalaryDetailsInfoData { + id:number|undefined; // 主键ID + sfzNumber:string|undefined; // 身份证 + name:string|undefined; // 户名 + account:string|undefined; // 账户 + sumDuration:number|undefined; // 当月总时长 + salary:number|undefined; // 薪水(天) + projectId:number|undefined; // 项目id + teamId:number|undefined; // 班组id + projectName:string|undefined; // 项目名称 + teamName:string|undefined; // 班组名称 + dateOfIssue:string|undefined; // 发放年月 + lister:string|undefined; // 制表人 + createdAt:string|undefined; // 创建时间 + updatedAt:string|undefined; // 更新时间 + deletedAt:string|undefined; // 删除时间 +} + + +export interface BusSalaryDetailsTableDataState { + ids:any[]; + tableData: { + data: Array; + total: number; + loading: boolean; + param: { + pageNum: number; + pageSize: number; + id: number|undefined; + sfzNumber: string|undefined; + projectId: number|undefined; + teamId: number|undefined; + dateRange: string[]; + }; + }; +} + + +export interface BusSalaryDetailsEditState{ + loading:boolean; + isShowDialog: boolean; + formData:BusSalaryDetailsInfoData; + rules: object; +} \ No newline at end of file diff --git a/src/views/project/busSalaryDetails/index.vue b/src/views/project/busSalaryDetails/index.vue new file mode 100644 index 0000000..29e19e9 --- /dev/null +++ b/src/views/project/busSalaryDetails/index.vue @@ -0,0 +1,590 @@ + + + diff --git a/src/views/project/salaryExcel/component/documentsDetail/index.vue b/src/views/project/salaryExcel/component/documentsDetail/index.vue index 8252e60..3a64ee2 100644 --- a/src/views/project/salaryExcel/component/documentsDetail/index.vue +++ b/src/views/project/salaryExcel/component/documentsDetail/index.vue @@ -3,8 +3,8 @@
- - + +
diff --git a/src/views/quality/documentQualityMeeting/index.vue b/src/views/quality/documentQualityMeeting/index.vue index c983577..89db5d9 100644 --- a/src/views/quality/documentQualityMeeting/index.vue +++ b/src/views/quality/documentQualityMeeting/index.vue @@ -63,7 +63,7 @@ :preview-src-list="[item.filenPathCoding]" fit="cover" /> - + {{ item.name }}
@@ -86,6 +86,7 @@ +