日志基本完成

main
wangjy 2024-10-11 18:08:59 +08:00
parent b5f23e89d8
commit 29a27ab0aa
15 changed files with 253 additions and 213 deletions

View File

@ -1,5 +1,6 @@
VITE_BASE_URL = '/pms-front-test' VITE_BASE_URL = "./"
VITE_BASE_NAME = "pms-front-test" VITE_BASE_NAME = "dev"
VITE_APP_PROXYURL = 'http://192.168.124.202:8080' VITE_APP_PROXYURL = 'http://192.168.124.202:8080'
VITE_APP_REQUESTURL = '/pms-front-test' VITE_APP_REQUESTURL = '/prod-api'
VITE_APP_ROUTERURL = '/pms-front-test/' VITE_APP_ROUTERURL = '/dev/'

2
.gitignore vendored
View File

@ -9,7 +9,7 @@ lerna-debug.log*
node_modules node_modules
pms-front pms-front
pms-front-test dev
dist dist
dist-ssr dist-ssr
*.local *.local

Binary file not shown.

After

Width:  |  Height:  |  Size: 556 B

View File

@ -7,6 +7,7 @@
v-bind="$attrs" v-bind="$attrs"
@selection-change="handleSelectionChange" @selection-change="handleSelectionChange"
:border="border" :border="border"
:highlight-current-row="highligt"
> >
<el-table-column v-if="showSelection" type="selection" width="55" /> <el-table-column v-if="showSelection" type="selection" width="55" />
<el-table-column v-if="showIndex" type="index" width="50" label="序号" /> <el-table-column v-if="showIndex" type="index" width="50" label="序号" />
@ -28,8 +29,7 @@
</div> </div>
</template> </template>
<template v-else-if="column.type === 'status'"> <template v-else-if="column.type === 'status'">
<span v-if="column.callback" v-html="column.callback(scope.row[column.prop], scope.row)"> <span v-if="column.callback" v-html="column.callback(scope.row[column.prop], scope.row)"></span>
</span>
<span v-else> <span v-else>
{{ scope.row[column.prop] }} {{ scope.row[column.prop] }}
</span> </span>
@ -90,8 +90,8 @@ const props = defineProps({
default: () => [10, 20, 30, 50], default: () => [10, 20, 30, 50],
}, },
maxHeight: { maxHeight: {
type: Number, type: String,
default: null, default: '100%',
}, },
offsetHeight: { offsetHeight: {
type: Number, type: Number,
@ -101,6 +101,14 @@ const props = defineProps({
type: Boolean, type: Boolean,
default: false, default: false,
}, },
multiSelect: {
type: Boolean,
default: true,
},
highligt: {
type: Boolean,
default: true,
},
}) })
const emit = defineEmits(['selection-change', 'size-change', 'current-change', 'button-click']) const emit = defineEmits(['selection-change', 'size-change', 'current-change', 'button-click'])
@ -128,7 +136,7 @@ const handleCurrentChange = val => {
const updateTableHeight = () => { const updateTableHeight = () => {
nextTick(() => { nextTick(() => {
if (tableContainer.value && paginationContainer.value) { if (tableContainer.value) {
const parentElement = tableContainer.value.parentElement const parentElement = tableContainer.value.parentElement
const parentHeight = parentElement.clientHeight const parentHeight = parentElement.clientHeight
const tableTop = tableContainer.value.getBoundingClientRect().top - parentElement.getBoundingClientRect().top const tableTop = tableContainer.value.getBoundingClientRect().top - parentElement.getBoundingClientRect().top
@ -161,36 +169,38 @@ onUnmounted(() => {
width: 100%; width: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
flex: 1;
} }
.pagination-container { .pagination-container {
margin-top: 10px; margin-top: 10px;
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
} }
:deep(.el-scrollbar__wrap--hidden-default){ :deep(.el-scrollbar__wrap--hidden-default) {
min-height: 100px; min-height: 100px;
} }
:deep(.el-table) { :deep(.el-table) {
--el-table-text-align: center; --el-table-text-align: center;
} }
:deep(.el-table th) { :deep(.el-table th) {
text-align: center; text-align: center;
} }
:deep(.el-table .cell) { :deep(.el-table .cell) {
text-align: center; text-align: center;
} }
/* 如果操作列需要特殊处理,可以添加以下样式 */ /* 如果操作列需要特殊处理,可以添加以下样式 */
:deep(.operation-column .cell) { :deep(.operation-column .cell) {
text-align: center; text-align: center;
display: flex; display: flex;
justify-content: center; justify-content: center;
} }
:deep(.el-table__inner-wrapper::before){ :deep(.el-table__inner-wrapper::before) {
display: none;
display: none; }
:deep(.el-table-column--selection) {
text-align: center;
} }
</style> </style>

View File

@ -13,6 +13,7 @@
:show-selection="true" :show-selection="true"
:show-index="true" :show-index="true"
:table-height="tableHeight" :table-height="tableHeight"
:multiSelect="multiSelect"
@selection-change="handleSelectionChange" @selection-change="handleSelectionChange"
@size-change="handleSizeChange" @size-change="handleSizeChange"
@current-change="handleCurrentChange" @current-change="handleCurrentChange"
@ -38,7 +39,8 @@
<script setup> <script setup>
import { ref, reactive, computed, watch, onMounted, nextTick } from 'vue' import { ref, reactive, computed, watch, onMounted, nextTick } from 'vue'
import CustomTable from '@/components/table.vue' import CustomTable from '@/components/CustomTable.vue'
import { systemApi } from '@/utils/api'
const props = defineProps({ const props = defineProps({
multiSelect: { multiSelect: {
@ -59,18 +61,13 @@ const emit = defineEmits(['update:dialogVisible', 'close', 'confirm'])
const currentPage = ref(1) const currentPage = ref(1)
const pageSize = ref(10) const pageSize = ref(10)
const total = ref(100) const total = ref(0)
const selectedUsers = ref([]) const selectedUsers = ref([])
const currentDepartment = ref('') const currentDepartment = ref('')
const tableHeight = ref(350) // const tableHeight = ref(350) //
// //
const treeData = [ const treeData = ref([])
{
label: '运维团队',
children: [{ label: '外场人员' }, { label: '内场人员' }, { label: '外场临时人员' }],
},
]
const defaultProps = { const defaultProps = {
children: 'children', children: 'children',
@ -79,35 +76,16 @@ const defaultProps = {
// //
const columns = [ const columns = [
{ prop: 'name', label: '姓名' }, { prop: 'nickName', label: '姓名' },
{ prop: 'department', label: '部门' }, { prop: 'dept', label: '部门', type: 'status', callback: (data, row) => data?.deptName },
{ prop: 'role', label: '角色' }, { prop: 'roles', label: '角色', type: 'status', callback: (data, row) => data.map(ele => ele.roleName).join(',') },
] ]
//
const allUserData = reactive(
Array(100)
.fill(null)
.map((_, index) => ({
id: index + 1,
name: `张三${index + 1}`,
department: ['外场人员', '内场人员', '外场临时人员'][index % 3],
role: '客户经理',
})),
)
const userData = computed(() => { const userData = ref([])
let filteredData = allUserData
if (currentDepartment.value) {
filteredData = allUserData.filter(user => user.department === currentDepartment.value)
}
const start = (currentPage.value - 1) * pageSize.value
const end = start + pageSize.value
return filteredData.slice(start, end)
})
const handleNodeClick = data => { const handleNodeClick = data => {
currentDepartment.value = data.label currentDepartment.value = data.id
currentPage.value = 1 currentPage.value = 1
fetchUserList() fetchUserList()
} }
@ -165,7 +143,7 @@ watch(
if (customTableRef.value) { if (customTableRef.value) {
customTableRef.value.clearSelection() customTableRef.value.clearSelection()
newVal.forEach(user => { newVal.forEach(user => {
const row = allUserData.find(item => item.id === user.id) const row = userData.value.find(item => item.userId === user.userId)
if (row) { if (row) {
customTableRef.value.toggleRowSelection(row, true) customTableRef.value.toggleRowSelection(row, true)
} }
@ -183,7 +161,7 @@ onMounted(() => {
if (customTableRef.value && props.currentSelectedUser.length > 0) { if (customTableRef.value && props.currentSelectedUser.length > 0) {
customTableRef.value.clearSelection() customTableRef.value.clearSelection()
props.currentSelectedUser.forEach(user => { props.currentSelectedUser.forEach(user => {
const row = allUserData.find(item => item.id === user.id) const row = userData.value.find(item => item.userId === user.userId)
if (row) { if (row) {
customTableRef.value.toggleRowSelection(row, true) customTableRef.value.toggleRowSelection(row, true)
} }
@ -194,24 +172,23 @@ onMounted(() => {
const fetchUserList = async () => { const fetchUserList = async () => {
// API // API
// const response = await api.getUserList({ const response = await systemApi.getUserList({
// page: currentPage.value, pageNum: currentPage.value,
// pageSize: pageSize.value, pageSize: pageSize.value,
// department: currentDepartment.value deptId: currentDepartment.value,
// }) })
// allUserData = response.data.list userData.value = response.rows
// total.value = response.data.total total.value = response.total
}
// 使 const fetchTreeData = async () => {
total.value = allUserData.filter( const response = await systemApi.getDeptTree()
user => !currentDepartment.value || user.department === currentDepartment.value, treeData.value = response.data
).length
} }
// //
// onMounted(() => { onMounted(() => {
// fetchUserList() fetchTreeData()
// }) fetchUserList()
})
</script> </script>
<style scoped> <style scoped>
@ -251,4 +228,4 @@ const fetchUserList = async () => {
.dialog-footer .el-button:first-child { .dialog-footer .el-button:first-child {
margin-left: 0; margin-left: 0;
} }
</style> </style>

View File

@ -4,9 +4,9 @@ export const loginApi = {
// 登录 // 登录
login: data => request('/login', 'json', data), login: data => request('/login', 'json', data),
// 退出登录 // 退出登录
loginOut: () => request('/aiops-api/logout', 'get'), loginOut: () => request('/logout', 'get'),
//修改密码 //修改密码
editPassword: data => request('/aiops-api/auth/user/updatePwd', 'post', data), editPassword: data => request('/auth/user/updatePwd', 'post', data),
// 获取验证码 // 获取验证码
getCode: () => request('/captchaImage', 'captchaImage', {}, { getCode: () => request('/captchaImage', 'captchaImage', {}, {
SHOW_LOADING: false, headers: { SHOW_LOADING: false, headers: {
@ -30,7 +30,13 @@ export const projectApi = {
updateProjectUser: data => request('/business/project/team', 'json', data), updateProjectUser: data => request('/business/project/team', 'json', data),
deleteProjectUser: id => request(`/business/project/team/${id}`, 'delete'), deleteProjectUser: id => request(`/business/project/team/${id}`, 'delete'),
} }
// 工作日志
export const workLogApi = {
userProject: id => request(`/business/work/hour/project/${id}`, 'get'),
getLogData: data => request('/business/work/hour/calendar', 'json', data, { SHOW_LOADING: false }),
getLogDataDetail: data => request('/business/work/hour/getInfo', 'json', data, { SHOW_LOADING: false }),
addLog: data => request('/business/work/hour/add', 'json', data),
}
// 用户板块 // 用户板块
export const useUserApi = () => { export const useUserApi = () => {
@ -38,8 +44,9 @@ export const useUserApi = () => {
} }
// 系统板块 // 系统板块
export const systemApi = { export const systemApi = {
getUserList: data => request('/aiops-api/auth/user/list', 'post', data), getUserList: data => request('/system/user/list', 'get', data),
getDictData: dictCode => request('/system/dict/data/type/' + dictCode, 'get'), getDictData: dictCode => request('/system/dict/data/type/' + dictCode, 'get'),
getDeptTree: () => request('/system/user/deptTree', 'get'),
} }
// 系统定时任务 // 系统定时任务
export const systemJobApi = { export const systemJobApi = {

View File

@ -14,7 +14,7 @@
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="项目编码" prop="projectCode"> <el-form-item label="项目编码" prop="projectCode">
<el-input v-model="formData.projectCode" class="full-width" disabled /> <el-input v-model="formData.projectCode" class="full-width" :disabled="isEditing" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
@ -158,7 +158,7 @@
<!-- 删除确认对话框 --> <!-- 删除确认对话框 -->
<el-dialog v-model="deleteDialogVisible" title="确认删除" width="30%"> <el-dialog v-model="deleteDialogVisible" title="确认删除" width="30%">
<div class="delete-confirm"> <div class="delete-confirm">
<img src="@/assets/warning.png" alt="警告" class="warning-icon" /> <el-icon class="warning-icon"><WarningFilled /></el-icon>
<p>确定要删除该成员吗此操作不可逆</p> <p>确定要删除该成员吗此操作不可逆</p>
</div> </div>
<template #footer> <template #footer>
@ -174,8 +174,8 @@
<script setup> <script setup>
import { ref, reactive, onMounted, computed, watch } from 'vue' import { ref, reactive, onMounted, computed, watch } from 'vue'
import { useRouter, useRoute } from 'vue-router' import { useRouter, useRoute } from 'vue-router'
import CustomTable from '@/components/table.vue' import CustomTable from '@/components/CustomTable.vue'
import { Plus } from '@element-plus/icons-vue' import { Plus,WarningFilled } from '@element-plus/icons-vue'
import SelectUser from '@/components/SelectUser.vue' import SelectUser from '@/components/SelectUser.vue'
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import { projectApi, systemApi } from '@/utils/api' import { projectApi, systemApi } from '@/utils/api'
@ -382,8 +382,8 @@ const handleUserConfirm = selectedUsers => {
if (selectedUsers.length > 0) { if (selectedUsers.length > 0) {
const selectedUser = selectedUsers[0] const selectedUser = selectedUsers[0]
currentEditingRow.value.userName = selectedUser.name currentEditingRow.value.userName = selectedUser.nickName
currentEditingRow.value.userId = selectedUser.id currentEditingRow.value.userId = selectedUser.userId
} else { } else {
currentEditingRow.value.name = '' currentEditingRow.value.name = ''
currentEditingRow.value.userId = null currentEditingRow.value.userId = null
@ -407,8 +407,8 @@ const openProjectManagerSelect = () => {
const handleProjectManagerConfirm = users => { const handleProjectManagerConfirm = users => {
if (users.length > 0) { if (users.length > 0) {
const selectedUser = users[0] const selectedUser = users[0]
formData.projectLeaderName = selectedUser.name formData.projectLeaderName = selectedUser.nickName
formData.projectLeader = selectedUser.id formData.projectLeader = selectedUser.userId
} else { } else {
formData.projectLeaderName = '' formData.projectLeaderName = ''
formData.projectLeader = null formData.projectLeader = null

View File

@ -82,7 +82,7 @@
<script setup> <script setup>
import { ref, reactive, onMounted } from 'vue' import { ref, reactive, onMounted } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import CustomTable from '@/components/table.vue' import CustomTable from '@/components/CustomTable.vue'
import SelectUser from '@/components/selectUser.vue' import SelectUser from '@/components/selectUser.vue'
import { WarningFilled } from '@element-plus/icons-vue' import { WarningFilled } from '@element-plus/icons-vue'
import { projectApi } from '@/utils/api' import { projectApi } from '@/utils/api'
@ -177,8 +177,8 @@ const handleUserSelectClose = () => {
const handleUserConfirm = (selectedUsers) => { const handleUserConfirm = (selectedUsers) => {
if (selectedUsers.length > 0) { if (selectedUsers.length > 0) {
const selectedUser = selectedUsers[0] const selectedUser = selectedUsers[0]
searchForm.projectLeaderName = selectedUser.name searchForm.projectLeaderName = selectedUser.nickName
searchForm.projectLeader = selectedUser.id searchForm.projectLeader = selectedUser.userId
currentSelectedUser.value = [selectedUser] currentSelectedUser.value = [selectedUser]
} else { } else {
searchForm.projectLeaderName = '' searchForm.projectLeaderName = ''

View File

@ -45,7 +45,7 @@
<script setup> <script setup>
import { ref, computed, onMounted, onUnmounted, nextTick } from 'vue' import { ref, computed, onMounted, onUnmounted, nextTick } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import CustomTable from '@/components/table.vue' import CustomTable from '@/components/CustomTable.vue'
const router = useRouter() const router = useRouter()
@ -60,9 +60,9 @@ function getDefaultDateRange() {
} }
const fixedColumns = [ const fixedColumns = [
{ prop: 'name', label: '项目', width: 200, slot: 'name' }, { prop: 'name', label: '项目', slot: 'name' },
{ prop: 'presetDays', label: '预计工时\n', width: 150 }, { prop: 'presetDays', label: '预计工时\n', width: 100 },
{ prop: 'actualDays', label: '累计工时\n', width: 150 }, { prop: 'actualDays', label: '累计工时\n', width: 100 },
] ]
const scrollableColumns = computed(() => { const scrollableColumns = computed(() => {

View File

@ -60,7 +60,7 @@
<script setup> <script setup>
import { ref, computed, onMounted } from 'vue' import { ref, computed, onMounted } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import CustomTable from '@/components/table.vue' import CustomTable from '@/components/CustomTable.vue'
const router = useRouter() const router = useRouter()

View File

@ -58,7 +58,7 @@
<script setup> <script setup>
import { ref, computed, onMounted, onUnmounted, nextTick } from 'vue' import { ref, computed, onMounted, onUnmounted, nextTick } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import CustomTable from '@/components/table.vue' import CustomTable from '@/components/CustomTable.vue'
import SelectUser from '@/components/selectUser.vue' import SelectUser from '@/components/selectUser.vue'
const router = useRouter() const router = useRouter()

View File

@ -289,7 +289,7 @@ import { ElMessage, ElMessageBox } from 'element-plus'
import { Search } from '@element-plus/icons-vue' import { Search } from '@element-plus/icons-vue'
import { systemJobApi } from '@/utils/api.js' import { systemJobApi } from '@/utils/api.js'
import Crontab from '@/components/Crontab' import Crontab from '@/components/Crontab'
import Table from '@/components/table.vue' import Table from '@/components/CustomTable.vue'
import Card from '@/components/Card.vue' import Card from '@/components/Card.vue'
const dict = ref({ const dict = ref({

View File

@ -72,7 +72,7 @@ import EditPerson from './compontents/EditPerson.vue'
import { Search } from '@element-plus/icons-vue' import { Search } from '@element-plus/icons-vue'
import { systemApi } from '@/utils/api.js' import { systemApi } from '@/utils/api.js'
import { listTotree } from '@/utils/tree.js' import { listTotree } from '@/utils/tree.js'
import Table from '@/components/table.vue' import Table from '@/components/CustomTable.vue'
import Card from '@/components/Card.vue' import Card from '@/components/Card.vue'
const handleCheckChange = (data, checked, indeterminate) => { const handleCheckChange = (data, checked, indeterminate) => {

View File

@ -13,38 +13,40 @@
</el-button> </el-button>
</div> </div>
</div> </div>
<el-table <CustomTable
v-show="!isCollapsed" v-show="!isCollapsed"
:data="projectList" :columns="[
style="width: 100%" { prop: 'name', label: '项目', align: 'center' },
{ prop: 'workDay', label: '工时(天)', align: 'center' },
]"
:tableData="projectList"
@row-click="handleProjectClick" @row-click="handleProjectClick"
highlight-current-row
:show-summary="true" :show-summary="true"
:summary-method="getSummaries" :summary-method="getSummaries"
sum-text="合计工时h" sum-text="合计工时h"
> :showPagination="false"
<el-table-column prop="name" label="项目" align="center"></el-table-column> :highligt="true"
<el-table-column prop="workHours" label="工时(小时)" align="center"></el-table-column> ref="projectRef"
</el-table> />
</div> </div>
<!-- 右侧项目信息和日历 --> <!-- 右侧项目信息和日历 -->
<div class="project-info-calendar"> <div class="project-info-calendar">
<h2 class="mb20 textC">工作日志</h2> <h2 class="mb20 textC">工作日志</h2>
<!-- 项目信息表单 --> <!-- 项目信息表单 -->
<el-form :model="projectInfo" label-width="100px" disabled class="project-info-form"> <el-form :model="projectInfo" label-width="100px" disabled class="project-info-form log-form">
<el-form-item label="项目名称"> <el-form-item label="项目名称">
<el-input v-model="projectInfo.name"></el-input> <el-input v-model="projectInfo.projectName"></el-input>
</el-form-item> </el-form-item>
<el-row :gutter="20"> <el-row :gutter="20">
<el-col :span="12"> <el-col :span="12">
<el-form-item label="项目编号"> <el-form-item label="项目编号">
<el-input v-model="projectInfo.code"></el-input> <el-input v-model="projectInfo.projectCode"></el-input>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="工时填报人"> <el-form-item label="工时填报人">
<el-input v-model="projectInfo.reporter"></el-input> <el-input v-model="projectInfo.nickName"></el-input>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
@ -62,11 +64,11 @@
</el-row> </el-row>
</el-form> </el-form>
<!-- 选择器 --> <!-- <EFBFBD><EFBFBD><EFBFBD>选择器 -->
<!-- 日历视图 --> <!-- 日历视图 -->
<div class="calendar-view" v-if="currentProject"> <div class="calendar-view" v-if="projectInfo">
<el-calendar v-model="selectedDate" @input="handleMonthChange"> <el-calendar v-model="selectedDate" @input="updateDateColors">
<template #header="{ date }"> <template #header="{ date }">
<div class="calendar-header"> <div class="calendar-header">
<el-date-picker v-model="selectedDate" type="month" format="YYYY年MM月" :clearable="false" /> <el-date-picker v-model="selectedDate" type="month" format="YYYY年MM月" :clearable="false" />
@ -74,6 +76,8 @@
</template> </template>
<template #dateCell="{ data }"> <template #dateCell="{ data }">
<div <div
:key="data.day"
:id="data.day"
@click="openLogDialog(data)" @click="openLogDialog(data)"
:class="{ :class="{
'date-cell': true, 'date-cell': true,
@ -91,16 +95,16 @@
<!-- 工作日志对话框 --> <!-- 工作日志对话框 -->
<el-dialog v-model="logDialogVisible" title="工作日志" width="30%"> <el-dialog v-model="logDialogVisible" title="工作日志" width="30%">
<el-form :model="logForm" label-width="100px" class="log-form"> <el-form :model="logForm" label-width="120px" class="log-form">
<el-form-item label="日期"> <el-form-item label="工作时长(小时)">
<el-input v-model="logForm.date" disabled></el-input> <el-input v-model.number="logForm.workTime"></el-input>
</el-form-item> </el-form-item>
<el-form-item label="工作内容"> <el-form-item label="工作内容">
<el-input v-model="logForm.content" type="textarea" :rows="4"></el-input> <el-input v-model="logForm.workContent" type="textarea" :rows="4"></el-input>
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
<span class="dialog-footer"> <span class="dialog-footer" v-if="logForm.status == -1">
<el-button @click="logDialogVisible = false">取消</el-button> <el-button @click="logDialogVisible = false">取消</el-button>
<el-button type="primary" @click="saveWorkLog"></el-button> <el-button type="primary" @click="saveWorkLog"></el-button>
</span> </span>
@ -110,39 +114,67 @@
</template> </template>
<script setup> <script setup>
import { ref, computed, watch, onMounted } from 'vue' import { ref, computed, watch, onMounted, nextTick } from 'vue'
import { ArrowLeft, ArrowRight } from '@element-plus/icons-vue' import { ArrowLeft, ArrowRight } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import { workLogApi, projectApi } from '@/utils/api'
import CustomTable from '@/components/CustomTable.vue'
const isCollapsed = ref(false) const isCollapsed = ref(false)
const selectedDate = ref(new Date()) const selectedDate = ref(new Date())
const currentMonth = ref(new Date())
// //
const projectList = ref([ const projectList = ref([])
{ id: 1, name: '项目1', startDate: '2023-01-01', endDate: '2023-06-30', workHours: 120 },
{ id: 2, name: '项目2', startDate: '2023-03-15', endDate: '2023-12-31', workHours: 80 },
{ id: 3, name: '项目3', startDate: '2023-02-01', endDate: '2023-08-31', workHours: 160 },
])
// //
const currentProject = ref(null)
const toggleCollapse = () => { const toggleCollapse = () => {
isCollapsed.value = !isCollapsed.value isCollapsed.value = !isCollapsed.value
} }
const handleProjectClick = row => { const logData = ref([])
currentProject.value = row const handleProjectClick = async row => {
// const res = await projectApi.getProjectDetail(row.projectId)
} projectInfo.value.projectName = res.data.projectName
projectInfo.value.projectId = res.data.projectId
projectInfo.value.projectCode = res.data.projectCode
projectInfo.value.startDate = res.data.startDate
projectInfo.value.endDate = res.data.endDate.split(' ')[0] + ' 23:59:59'
const openLogDialog = data => { //
if (!currentProject.value) { selectedDate.value = new Date(projectInfo.value.startDate) //
//
const res2 = await workLogApi.getLogData({
projectId: projectInfo.value.projectId,
startDate: projectInfo.value.startDate,
endDate: projectInfo.value.endDate,
userId: projectInfo.value.userId,
})
logData.value = res2.data
initDateColor()
}
//
const initDateColor = () => {
logData.value.map(item => {
var ele = document.getElementById(item.date.split(' ')[0])
if (ele) {
if (item.state == -1) {
ele.style = 'background:#ecf5ff'
} else if (item.state == 0) {
ele.style = 'background:#409eff;color:#fff'
} else if (item.state == 1) {
ele.style = 'background:#f5dc58;color:#fff'
}
}
})
}
const openLogDialog = async data => {
if (!projectInfo.value) {
ElMessage.warning('请先选择一个项目') ElMessage.warning('请先选择一个项目')
return return
} }
handleProjectClick(projectInfo.value)
// 使 data.day data.date // 使 data.day data.date
const clickedDate = new Date(data.day) const clickedDate = new Date(data.day)
const currentDate = new Date() const currentDate = new Date()
@ -151,31 +183,48 @@ const openLogDialog = data => {
clickedDate.setUTCHours(0, 0, 0, 0) clickedDate.setUTCHours(0, 0, 0, 0)
currentDate.setUTCHours(0, 0, 0, 0) currentDate.setUTCHours(0, 0, 0, 0)
if (clickedDate.getTime() > currentDate.getTime()) { if (
clickedDate.getTime() > currentDate.getTime() &&
clickedDate.getTime() < new Date(projectInfo.value.endDate).getTime()
) {
ElMessage.warning('不可编辑未来日期') ElMessage.warning('不可编辑未来日期')
return return
} }
const date = new Date(data.day) const date = new Date(data.day)
const start = new Date(currentProject.value.startDate) const start = new Date(projectInfo.value.startDate)
const end = new Date(currentProject.value.endDate) const end = new Date(projectInfo.value.endDate)
// 使 getTime() // 使 getTime()
let flag = date.getTime() >= start.getTime() && date.getTime() <= end.getTime() let flag = date.getTime() >= start.getTime() && date.getTime() <= end.getTime()
console.log(currentProject.value.endDate, currentProject.value.startDate, data.day)
if (!flag) { if (!flag) {
ElMessage.warning('该日期不在项目范围内') ElMessage.warning('该日期不在项目范围内')
return return
} }
// //
logForm.value.loggerDate = data.day + ' 00:00:00'
let logTime = logData.value.find(ele => ele.date == logForm.value.loggerDate) || {}
logForm.value.status = logTime.state
if (logForm.value.status != -1) {
const res = await workLogApi.getLogDataDetail({
userId: projectInfo.value.userId,
projectId: projectInfo.value.projectId,
loggerDate: logForm.value.loggerDate,
})
logForm.value.workTime = res.data.workTime
logForm.value.workContent = res.data.workContent
} else {
logForm.value.workTime = ''
logForm.value.workContent = ''
}
logDialogVisible.value = true logDialogVisible.value = true
} }
const isInProjectRange = data => { const isInProjectRange = data => {
if (!currentProject.value) return fals if (!projectInfo.value) return false
const date = new Date(data.day) const date = new Date(data.day)
const start = new Date(currentProject.value.startDate) const start = new Date(projectInfo.value.startDate)
const end = new Date(currentProject.value.endDate) const end = new Date(projectInfo.value.endDate)
// 使 getTime() // 使 getTime()
return date.getTime() >= start.getTime() && date.getTime() <= end.getTime() return date.getTime() >= start.getTime() && date.getTime() <= end.getTime()
} }
@ -199,7 +248,7 @@ const getSummaries = param => {
sums[index] = values.reduce((prev, curr) => { sums[index] = values.reduce((prev, curr) => {
const value = Number(curr) const value = Number(curr)
if (!isNaN(value)) { if (!isNaN(value)) {
return prev + curr return prev + curr * 24
} else { } else {
return prev return prev
} }
@ -208,21 +257,18 @@ const getSummaries = param => {
sums[index] = 'N/A' sums[index] = 'N/A'
} }
}) })
if (sums[1] != 'N/A') sums[1] = Number(sums[1]).toFixed(2)
return sums return sums
} }
const logForm = ref({ const logForm = ref({
date: '', loggerDate: '',
content: '', workContent: '',
workTime: '',
status: -1,
}) })
// //
const projectInfo = ref({ const projectInfo = ref({})
name: '项目1',
code: 'XM5836383',
reporter: '张三',
startDate: '2024-6-15',
endDate: '2024-8-29',
})
// //
const logDialogVisible = ref(false) const logDialogVisible = ref(false)
@ -232,53 +278,52 @@ const workLog = ref({
}) })
// //
const saveWorkLog = () => { const saveWorkLog = async () => {
// //
console.log('保存工作日志', workLog.value) //
let state = new Date(logForm.value.loggerDate).getTime() == $moment().startOf('day').valueOf() ? 0 : 1
await workLogApi.addLog({
...logForm.value,
projectId: projectInfo.value.projectId,
state,
userId: projectInfo.value.userId,
})
logDialogVisible.value = false logDialogVisible.value = false
ElMessage.success('添加成功')
handleProjectClick(projectInfo.value)
} }
const handleMonthChange = date => {
currentMonth.value = date
}
const selectDate = type => {
const date = new Date(currentMonth.value)
switch (type) {
case 'prev-month':
date.setMonth(date.getMonth() - 1)
break
case 'today':
date = new Date()
break
case 'next-month':
date.setMonth(date.getMonth() + 1)
break
}
currentMonth.value = date
selectedDate.value = date
}
//
watch(currentProject, newProject => {
if (newProject) {
selectedDate.value = new Date(newProject.startDate)
currentMonth.value = new Date(newProject.startDate)
}
})
const route = useRoute() const route = useRoute()
//
const projectRef = ref(null)
const fetchUserProjects = async () => {
try {
const response = await workLogApi.userProject(projectInfo.value.userId)
projectList.value = response.data // <EFBFBD><EFBFBD><EFBFBD> projects
if (projectList.value.length) {
handleProjectClick(projectList.value[0])
projectRef.value.setCurrentRow(projectList.value[0])
}
} catch (error) {}
}
const updateDateColors = () => {
initDateColor() //
}
onMounted(() => { onMounted(() => {
const userId = route.params.userId if (route.params.userId) {
const userName = route.query.userName projectInfo.value.userId = route.params.userId
projectInfo.value.nickName = route.params.nickName
if (userId && userName) { } else {
console.log(`加载用户 ${userName}ID: ${userId})的工作日志`) let userInfo =
// sessionStorage.getItem('userInfo') !== 'undefined' && sessionStorage.getItem('userInfo')
? JSON.parse($utils.decryptByDES.CBC(sessionStorage.getItem('userInfo'))).user
: {}
projectInfo.value.userId = userInfo.userId
projectInfo.value.nickName = userInfo.nickName
} }
//
// ... ... fetchUserProjects()
}) })
</script> </script>
@ -295,6 +340,7 @@ onMounted(() => {
transition: all 0.3s; transition: all 0.3s;
overflow: hidden; overflow: hidden;
position: relative; position: relative;
height: 100%;
} }
.project-list.collapsed { .project-list.collapsed {
@ -311,6 +357,7 @@ onMounted(() => {
border-bottom: 1px solid #dcdfe6; border-bottom: 1px solid #dcdfe6;
position: relative; position: relative;
height: 40px; height: 40px;
flex: 1;
} }
.collapse-button-wrapper { .collapse-button-wrapper {
@ -347,30 +394,6 @@ onMounted(() => {
color: #606266; color: #606266;
} }
.project-list :deep(.el-table__body-wrapper) {
overflow-y: auto;
}
.project-list :deep(.el-table__footer-wrapper) {
position: sticky;
bottom: 0;
z-index: 1;
}
.project-list :deep(.el-table__footer) {
background-color: white;
}
.project-list :deep(.el-table__footer td) {
background-color: white !important;
font-weight: bold;
}
.project-list :deep(.el-table__body td),
.project-list :deep(.el-table__footer td) {
height: 60px; /* 增加单格度 */
}
.project-info-calendar { .project-info-calendar {
flex: 1; flex: 1;
padding: 20px; padding: 20px;
@ -402,11 +425,11 @@ onMounted(() => {
background-color: #ecf5ff; background-color: #ecf5ff;
} }
.calendar-view :deep(.out-range) { .calendar-view :deep(.out-range) {
background-color: rgba(0, 0, 0, 0.05); background-color: rgba(0, 0, 0, 0.05) !important;
} }
.calendar-view :deep(.disabled) { .calendar-view :deep(.disabled) {
background-color: #fff; background-color: #fff !important;
color: #c0c4cc; color: #c0c4cc;
cursor: not-allowed; cursor: not-allowed;
} }
@ -469,6 +492,10 @@ onMounted(() => {
.log-form :deep(.el-input__wrapper) { .log-form :deep(.el-input__wrapper) {
height: 50px; height: 50px;
width: 100% !important;
}
:deep(.el-date-editor--date) {
width: 100% !important;
} }
.log-form :deep(.el-input__inner) { .log-form :deep(.el-input__inner) {
@ -486,7 +513,7 @@ onMounted(() => {
/* 增加 el-form-item__label 的高度和行高 */ /* 增加 el-form-item__label 的高度和行高 */
:deep(.el-form-item__label) { :deep(.el-form-item__label) {
height: 42px; /* 默认通常是 32px所以增加 10px 变为 42px */ height: 42px; /* 默认通常是 32px所以增加 10px <EFBFBD><EFBFBD>变为 42px */
line-height: 42px; /* 行高与高度相同,确保文字垂直居中 */ line-height: 42px; /* 行高与高度相同,确保文字垂直居中 */
} }
@ -573,4 +600,24 @@ onMounted(() => {
.date-cell.disabled { .date-cell.disabled {
cursor: not-allowed; cursor: not-allowed;
} }
:deep(.el-table__footer-wrapper) {
background-color: #f5f7fa;
}
:deep(.el-table__footer td) {
background-color: #f5f7fa !important;
font-weight: bold;
color: #606266;
}
:deep(.el-table__row) {
cursor: pointer;
}
:deep(tr.el-table__row:hover .el-table__cell) {
background: #409eff !important;
color: #fff;
}
:deep(tr.el-table__row.current-row .el-table__cell) {
background-color: #409eff !important;
color: #fff;
}
</style> </style>

View File

@ -6,8 +6,6 @@ import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite' import Components from 'unplugin-vue-components/vite'
import vue from '@vitejs/plugin-vue' import vue from '@vitejs/plugin-vue'
import myPlugin from './public/js/zip' import myPlugin from './public/js/zip'
// console.log(env.VITE_BASE_URL, 'env.VITE_BASE_URL')
// https://vitejs.dev/config/
export default ({ mode }) => { export default ({ mode }) => {
const env = loadEnv(mode, process.cwd()) const env = loadEnv(mode, process.cwd())