项目接口调试完成

main
wangjy 2024-10-10 17:57:26 +08:00
parent 60cb68401f
commit b5f23e89d8
11 changed files with 553 additions and 221 deletions

View File

@ -1,6 +1,6 @@
VITE_BASE_URL = "./"
VITE_BASE_NAME = "external"
VITE_APP_PROXYURL = 'http://183.230.174.15:8088/external'
VITE_APP_REQUESTURL = '/external'
VITE_APP_ROUTERURL = '/external/'
VITE_BASE_NAME = "pms-front-test"
VITE_APP_PROXYURL = 'http://192.168.124.202:8080'
VITE_APP_REQUESTURL = '/pms-front-test'
VITE_APP_ROUTERURL = '/pms-front-test/'

View File

@ -1,5 +1,5 @@
VITE_BASE_URL = '/pms-front-test'
VITE_BASE_NAME = "pms-front-test"
VITE_APP_PROXYURL = 'http://183.230.174.15:8093/aiops-external'
VITE_APP_PROXYURL = 'http://192.168.124.202:8080'
VITE_APP_REQUESTURL = '/pms-front-test'
VITE_APP_ROUTERURL = '/pms-front-test/'

View File

@ -27,6 +27,13 @@
</el-button>
</div>
</template>
<template v-else-if="column.type === 'status'">
<span v-if="column.callback" v-html="column.callback(scope.row[column.prop], scope.row)">
</span>
<span v-else>
{{ scope.row[column.prop] }}
</span>
</template>
<template v-else>
{{ scope.row[column.prop] }}
</template>
@ -160,4 +167,30 @@ onUnmounted(() => {
display: flex;
justify-content: flex-end;
}
:deep(.el-scrollbar__wrap--hidden-default){
min-height: 100px;
}
:deep(.el-table) {
--el-table-text-align: center;
}
:deep(.el-table th) {
text-align: center;
}
:deep(.el-table .cell) {
text-align: center;
}
/* 如果操作列需要特殊处理,可以添加以下样式 */
:deep(.operation-column .cell) {
text-align: center;
display: flex;
justify-content: center;
}
:deep(.el-table__inner-wrapper::before){
display: none;
}
</style>

View File

@ -108,7 +108,7 @@ export const asyncRoutes = [
name: 'worklogList',
authKey: 'Workorder',
meta: {
title: '日志列表',
title: '工作日志',
},
component: () => import('@/views/workLog/list.vue'),
},

View File

@ -25,22 +25,10 @@ export const useUserStore = defineStore('user', {
async login(params) {
try {
const res = await loginApi.login(params)
const roleType = res.data.user.roles
.filter(item => item.roleType)
.map(per => per.roleType)
.join(',')
// setPerssionsTabbar(roleType)
const roleKey = res.data.user.roles
.filter(item => item.roleKey)
.map(per => per.roleKey)
.join(',')
sessionStorage.setItem('roleType', $utils.encryptByDES.CBC(roleType))
sessionStorage.setItem('roleKey', $utils.encryptByDES.CBC(roleKey))
const token = $utils.encryptByMD5(`123456`)
setToken(token)
await this.updateUserInfo(res.data)
setToken(res.token)
await this.updateUserInfo()
ElMessage.success({ message: '登录成功' })
const permissions = [...res.data.resourceKeys]
const permissions = [...this.userInfo.permissions]
// , 'WorkOrderError', 'workOrderDestroy', 'workOrderReserver'
sessionStorage.setItem('permissions', $utils.encryptByDES.CBC(JSON.stringify(permissions)))
this.updateAsyncRoutes()
@ -56,8 +44,8 @@ export const useUserStore = defineStore('user', {
removeToken()
router.push('/')
},
async updateUserInfo(data) {
const userInfo = data.user
async updateUserInfo() {
const userInfo = await loginApi.getUserInfo()
sessionStorage.setItem('userInfo', $utils.encryptByDES.CBC(JSON.stringify(userInfo)))
this.userInfo = userInfo
},
@ -67,8 +55,8 @@ export const useUserStore = defineStore('user', {
updateAsyncRoutes() {
// 权限时处理异步路由
let sessionPermissions = JSON.parse($utils.decryptByDES.CBC(sessionStorage.getItem('permissions')))
// console.log(asyncRoutes, sessionPermissions, 'asyncRoutes, sessionPermissions')
let result = filterAsyncRoutes(asyncRoutes, sessionPermissions)
// let result = filterAsyncRoutes(asyncRoutes, sessionPermissions)
let result = asyncRoutes
result = editRedirect(result)
// console.log(result, 'result')

View File

@ -1,14 +1,37 @@
import request from '@/utils/request'
//登录板块api
export const loginApi = {
// 登录
login: data => request('/login', 'post', data),
login: data => request('/login', 'json', data),
// 退出登录
loginOut: () => request('/aiops-api/logout', 'get'),
//修改密码
editPassword: data => request('/aiops-api/auth/user/updatePwd', 'post', data),
// 获取验证码
getCode: () => request('/captchaImage', 'captchaImage', {}, {
SHOW_LOADING: false, headers: {
'Content-Type': 'application/json'
}
}),
getUserInfo: () => request('/getInfo', 'get'),
}
// 项目板块
export const projectApi = {
// 查询项目列表
listProject: query => request('/business/project/list', 'get', query),
deleteProject: id => request(`/business/project/${id}`, 'delete'),
addProject: data => request('/business/project/add', 'json', data),
updateProject: data => request('/business/project/update', 'json', data),
getProjectCode: () => request('/business/project/getCode', 'get'),
getProjectDetail: id => request(`/business/project/info/${id}`, 'get'),
// 项目成员
getProjectUser: id => request(`/business/project/${id}`, 'get'),
updateProjectUser: data => request('/business/project/team', 'json', data),
deleteProjectUser: id => request(`/business/project/team/${id}`, 'delete'),
}
// 用户板块
export const useUserApi = () => {
@ -16,6 +39,7 @@ export const useUserApi = () => {
// 系统板块
export const systemApi = {
getUserList: data => request('/aiops-api/auth/user/list', 'post', data),
getDictData: dictCode => request('/system/dict/data/type/' + dictCode, 'get'),
}
// 系统定时任务
export const systemJobApi = {

View File

@ -17,7 +17,7 @@ const service = axios.create({
service.interceptors.request.use(
config => {
if (getToken()) {
config.headers['token'] = getToken()
config.headers['Authorization'] = 'Bearer ' + getToken()
}
return config
},
@ -105,6 +105,9 @@ const doRequest = (url, method = 'GET', params, opts, callback) => {
method = 'POST'
opts.headers['Content-Type'] = 'application/json'
opts.responseType = 'blob'
}else if (/captchaImage/i.test(method)) {
method = 'get'
opts.headers['Content-Type'] = 'application/json'
}
service({
url: url,
@ -156,7 +159,7 @@ const doRequest = (url, method = 'GET', params, opts, callback) => {
opts.SHOW_LOADING && loadingInstance && loadingInstance.close()
return
}
if (res.code === 0) {
if (res.code === 200) {
resolve(res) //
// resolve(res.data)
} else if (res.code === 401) {
@ -175,7 +178,6 @@ const doRequest = (url, method = 'GET', params, opts, callback) => {
opts.SHOW_LOADING && loadingInstance && loadingInstance.close()
})
.catch(err => {
console.log('ssss')
opts.SHOW_LOADING && loadingInstance && loadingInstance.close()
if (err.code === 'ERR_CANCELED') return
ElMessage.error({

View File

@ -73,6 +73,7 @@
import { ElMessage } from 'element-plus'
import { reactive, ref } from 'vue'
import { useRouter } from 'vue-router'
import { loginApi } from '@/utils/api'
import { useUserStore } from '@/stores/user'
import { storeToRefs } from 'pinia'
const userStore = useUserStore()
@ -82,23 +83,14 @@ const login = reactive({
pwd: '',
validateCode: '',
})
const codeSrc = ref(
process.env.NODE_ENV === 'production'
? `${window.location.origin}${
import.meta.env.VITE_APP_REQUESTURL
}/captcha/captchaImage?type=math&&t=${Math.random()}`
: `${import.meta.env.VITE_APP_REQUESTURL}/captcha/captchaImage?type=math`,
) //${window.location.origin}
const codeSrc = ref()
const uuid = ref()
const updateCode = async () => {
codeSrc.value =
process.env.NODE_ENV === 'production'
? `${window.location.origin}${
import.meta.env.VITE_APP_REQUESTURL
}/captcha/captchaImage?type=math&&t=${Math.random()}`
: `${import.meta.env.VITE_APP_REQUESTURL}/captcha/captchaImage?type=math&&t=${Math.random()}` //char math
const res = await loginApi.getCode()
codeSrc.value = 'data:image/png;base64,' + res.img
uuid.value = res.uuid
}
// getCode()
const confirmLogin = async () => {
if (!login.account) {
ElMessage.error({ message: `用户名不能为空!` })
@ -113,9 +105,10 @@ const confirmLogin = async () => {
return
}
const params = {
password: $utils.encryptByMD5(`${login.account}${login.pwd}`),
password: login.pwd,
username: login.account,
validateCode: login.validateCode,
code: login.validateCode,
uuid: uuid.value,
}
try {
await userStore.login(params)
@ -127,6 +120,7 @@ const confirmLogin = async () => {
const usershadow = ref(false)
const shadow = ref(false)
const codeShadow = ref(false)
updateCode()
</script>
<style lang="scss">
.login {

View File

@ -1,25 +1,29 @@
<template>
<div class="project-management">
<el-form :model="formData" :rules="rules" label-width="120px" class="custom-form">
<el-form ref="formRef" :model="formData" :rules="rules" label-width="120px" class="custom-form">
<el-row :gutter="24">
<el-col :span="24">
<el-form-item label="项目名称">
<el-select v-model="formData.projectName" placeholder="请选择项目名称" class="full-width longInput">
<el-option v-for="item in projectOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="项目编号">
<el-input v-model="formData.projectCode" class="full-width" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="项目负责人">
<el-form-item label="项目名称" prop="projectName">
<el-input
v-model="formData.projectManager"
v-model="formData.projectName"
placeholder="请输入项目名称"
:disabled="isEditing"
class="full-width longInput"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="项目编码" prop="projectCode">
<el-input v-model="formData.projectCode" class="full-width" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="项目负责人" prop="projectLeader">
<el-input
v-model="formData.projectLeaderName"
placeholder="选择项目负责人"
readonly
:disabled="isEditing"
@click="openProjectManagerSelect"
/>
</el-form-item>
@ -27,36 +31,58 @@
</el-row>
<el-row :gutter="24">
<el-col :span="12">
<el-form-item label="项目状态">
<el-select v-model="formData.projectStatus" placeholder="请选择项目状态" class="full-width">
<el-option label="进行中" value="ongoing" />
<el-option label="已完成" value="completed" />
<el-option label="已暂停" value="paused" />
<el-form-item label="项目状态" prop="projectState">
<el-select
v-model="formData.projectState"
placeholder="请选择项目状态"
:disabled="isEditing"
class="full-width"
>
<el-option label="未启动" value="0" />
<el-option label="进行中" value="1" />
<el-option label="已完成" value="2" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="预计工时">
<el-input type="number" v-model="formData.budgetDays" :min="0" class="full-width" />
<el-form-item label="预算天数" prop="budgetDate">
<el-input v-model.number="formData.budgetDate" :min="0" :disabled="isEditing" class="full-width" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="24">
<el-col :span="12">
<el-form-item label="项目开始时间">
<el-date-picker v-model="formData.startDate" type="date" placeholder="选择日期" class="full-width" />
<el-form-item label="开始日期" prop="startDate">
<el-date-picker
v-model="formData.startDate"
type="date"
placeholder="选择开始日期"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
class="full-width"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="项目结束时间">
<el-date-picker v-model="formData.endDate" type="date" placeholder="选择日期" class="full-width" />
<el-form-item label="结束日期" prop="endDate">
<el-date-picker
v-model="formData.endDate"
type="date"
placeholder="选择结束日期"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
class="full-width"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div class="form-actions jcc">
<el-button type="primary" @click="saveProject"></el-button>
<el-button @click="cancelEdit"></el-button>
</div>
<div class="table-actions">
<el-button type="primary" :icon="Plus" @click="editUser"></el-button>
<el-button type="primary" :icon="Plus" @click="addUser"></el-button>
</div>
<CustomTable
@ -65,31 +91,47 @@
:total="total"
:show-selection="false"
:show-index="true"
:show-pagination="false"
:table-height="tableHeight"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
>
<template #name="{ row }">
<el-input
v-model="row.name"
v-if="row.isNew"
v-model="row.userName"
placeholder="选择人员"
readonly
@click="openSelectUser(row)"
/>
<span v-else>{{ row.userName }}</span>
</template>
<template #role="{ row }">
<el-select
v-model="row.role"
placeholder="请选择职位"
@change="handleRoleChange(row)"
<template #post="{ row }">
<el-select
v-if="row.isEditing || row.isNew"
v-model="row.postId"
placeholder="请选择职位"
@change="handlePostChange(row)"
>
<el-option v-for="role in roleOptions" :key="role" :label="role" :value="role" />
<el-option v-for="post in postOptions" :key="post" :label="post.dictLabel" :value="post.dictValue" />
</el-select>
<span v-else>{{ postOptions.find(post => post.dictValue === row.postId)?.dictLabel }}</span>
</template>
<template #operation="{ row }">
<div class="operation-buttons">
<el-button text type="primary" @click="showTimesheet(row)"></el-button>
<el-button text type="danger" @click="confirmDelete(row)"></el-button>
<template v-if="row.isNew">
<el-button text type="primary" @click="confirmAddUser(row)"></el-button>
<el-button text type="danger" @click="cancelAddUser(row)"></el-button>
</template>
<template v-else-if="row.isEditing">
<el-button text type="primary" @click="saveUserEdit(row)"></el-button>
<el-button text type="info" @click="cancelUserEdit(row)"></el-button>
</template>
<template v-else>
<el-button text type="primary" @click="editUser(row)"></el-button>
<el-button text type="primary" @click="showTimesheet(row)"></el-button>
<el-button text type="danger" @click="confirmDelete(row)"></el-button>
</template>
</div>
</template>
</CustomTable>
@ -114,13 +156,9 @@
/>
<!-- 删除确认对话框 -->
<el-dialog
v-model="deleteDialogVisible"
title="确认删除"
width="30%"
>
<el-dialog v-model="deleteDialogVisible" title="确认删除" width="30%">
<div class="delete-confirm">
<img src="@/assets/warning.png" alt="警告" class="warning-icon">
<img src="@/assets/warning.png" alt="警告" class="warning-icon" />
<p>确定要删除该成员吗此操作不可逆</p>
</div>
<template #footer>
@ -134,42 +172,41 @@
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { ref, reactive, onMounted, computed, watch } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import CustomTable from '@/components/table.vue'
import { Plus } from '@element-plus/icons-vue'
import SelectUser from '@/components/SelectUser.vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { projectApi, systemApi } from '@/utils/api'
const router = useRouter()
const route = useRoute()
const formRef = ref(null)
const isEditing = ref(true)
const isEditing = computed(() => !!formData.projectId)
const showSelectUser = ref(false)
const showProjectManagerSelect = ref(false)
const formData = reactive({
projectId: null,
projectName: '',
projectCode: '',
projectManager: '',
budgetDays: 0,
projectStatus: '',
projectLeaderName: '',
projectLeader: '',
projectState: '',
startDate: '',
endDate: '',
budgetDate: 0,
state: 0,
})
const roleOptions = ['开发', '设计', '测试', '产品', '项目经理']
const postOptions = ref([]) //
const columns = [
{ prop: 'name', label: '人员姓名', slot: 'name' },
{ prop: 'role', label: '项目职位', slot: 'role' },
{ prop: 'workdays', label: '累计人天数' },
{
prop: 'operation',
label: '操作',
width: '200',
fixed: 'right',
className: 'operation-column',
},
{ prop: 'name', label: '姓名' },
{ prop: 'post', label: '项目职位' },
{ prop: 'workdays', label: '工作天数' },
{ prop: 'operation', label: '操作', width: '250px' },
]
const tableData = ref([])
@ -178,10 +215,31 @@ const currentEditingRow = ref(null)
const currentSelectedUser = ref([])
const projectManagerSelectedUser = ref([])
//
watch([() => formData.startDate, () => formData.endDate], updateProjectState, { immediate: true })
function updateProjectState() {
const now = new Date().getTime()
const start = formData.startDate ? new Date(formData.startDate).getTime() : null
const end = formData.endDate ? new Date(formData.endDate).getTime() : null
if (start && end) {
if (now < start) {
formData.projectState = '0' //
} else if (now >= start && now <= end) {
formData.projectState = '1' //
} else {
formData.projectState = '2' //
}
}
}
const validateDates = (rule, value, callback) => {
if (formData.startDate && formData.endDate) {
if (new Date(formData.startDate) > new Date(formData.endDate)) {
callback(new Error('开始时间不能大于结束时间'))
const start = new Date(formData.startDate)
const end = new Date(formData.endDate)
if (start > end) {
callback(new Error('开始日期不能晚于结束日期'))
} else {
callback()
}
@ -191,38 +249,140 @@ const validateDates = (rule, value, callback) => {
}
const rules = {
projectName: [
{ required: true, message: '请输入项目名称', trigger: 'blur' },
{ max: 200, message: '项目名称不能超过200个字符', trigger: 'blur' },
],
projectCode: [
{ required: true, message: '请输入项目编码', trigger: 'blur' },
{ max: 50, message: '项目编码不能超过50个字符', trigger: 'blur' },
],
projectLeader: [{ required: true, message: '请选择项目负责人', trigger: 'change' }],
projectState: [{ required: true, message: '请选择项目状态', trigger: 'change' }],
startDate: [
{ required: true, message: '请选择开始时间', trigger: 'change' },
{ validator: validateDates, trigger: 'change' }
{ required: true, message: '请选择开始日期', trigger: 'change' },
{ validator: validateDates, trigger: 'change' },
],
endDate: [
{ required: true, message: '请选择结束时间', trigger: 'change' },
{ validator: validateDates, trigger: 'change' }
]
{ required: true, message: '请选择结束日期', trigger: 'change' },
{ validator: validateDates, trigger: 'change' },
],
budgetDate: [
{ required: true, message: '请输入预算天数', trigger: 'blur' },
{ type: 'number', min: 1, message: '预算天数必须大于0', trigger: 'blur' },
],
}
const editUser = () => {
const saveProject = async () => {
if (!formRef.value) return
try {
await formRef.value.validate()
//
const projectDataToSave = {
...formData,
startDate: formData.startDate ? new Date(formData.startDate).getTime() : null,
endDate: formData.endDate ? new Date(formData.endDate).getTime() : null,
}
if (!formData.projectId) {
const res = await projectApi.addProject(projectDataToSave)
formData.projectId = res.data.projectId
} else {
await projectApi.updateProject(projectDataToSave)
}
ElMessage.success('项目保存成功')
//
} catch (error) {
if (error instanceof Error) {
//
console.error('表单验证失败', error)
ElMessage.error('请检查表单填写是否正确')
} else {
// API
console.error('保存项目失败', error)
ElMessage.error('保存项目失败')
}
}
}
const cancelEdit = () => {
router.back()
}
const addUser = () => {
if (!formData.projectId) {
ElMessage.warning('请先保存项目信息后再添加用户')
return
}
const newUser = {
id: Date.now(),
name: '',
role: '',
post: '',
postId: '',
workdays: 0,
userId: null
userId: null,
isNew: true,
}
tableData.value.push(newUser)
tableData.value.unshift(newUser)
total.value = tableData.value.length
}
const openSelectUser = (row) => {
const handlePostChange = row => {
//
}
const confirmAddUser = async row => {
if (!formData.projectId) {
ElMessage.warning('请先保存项目信息后再添加用户')
return
}
if (!row.userId || !row.postId) {
ElMessage.warning('请选择用户并指定职位')
return
}
try {
let params = {
projectId: formData.projectId,
userId: row.userId,
postId: row.postId,
}
if (row.teamId) params.teamId = row.teamId
await projectApi.updateProjectUser(params)
row.isNew = false
ElMessage.success('成员添加成功')
getProjectUser()
} catch (error) {
console.error('添加成员失败', error)
ElMessage.error('添加成员失败')
}
}
const cancelAddUser = row => {
const index = tableData.value.findIndex(item => item.id === row.id)
if (index !== -1) {
tableData.value.splice(index, 1)
total.value = tableData.value.length
}
}
const openSelectUser = row => {
currentEditingRow.value = row
currentSelectedUser.value = row.userId ? [{ id: row.userId, name: row.name }] : []
showSelectUser.value = true
}
const handleUserConfirm = (users) => {
if (users.length > 0) {
const selectedUser = users[0]
currentEditingRow.value.name = selectedUser.name
const handleUserConfirm = selectedUsers => {
if (!formData.projectId) {
ElMessage.warning('请先保存项目信息后再选择用户')
return
}
if (selectedUsers.length > 0) {
const selectedUser = selectedUsers[0]
currentEditingRow.value.userName = selectedUser.name
currentEditingRow.value.userId = selectedUser.id
} else {
currentEditingRow.value.name = ''
@ -238,20 +398,20 @@ const closeSelectUser = () => {
}
const openProjectManagerSelect = () => {
projectManagerSelectedUser.value = formData.projectManager
? [{ id: formData.projectManagerId, name: formData.projectManager }]
projectManagerSelectedUser.value = formData.projectLeader
? [{ id: formData.projectLeaderId, name: formData.projectLeader }]
: []
showProjectManagerSelect.value = true
}
const handleProjectManagerConfirm = (users) => {
const handleProjectManagerConfirm = users => {
if (users.length > 0) {
const selectedUser = users[0]
formData.projectManager = selectedUser.name
formData.projectManagerId = selectedUser.id
formData.projectLeaderName = selectedUser.name
formData.projectLeader = selectedUser.id
} else {
formData.projectManager = ''
formData.projectManagerId = null
formData.projectLeaderName = ''
formData.projectLeader = null
}
showProjectManagerSelect.value = false
}
@ -268,65 +428,122 @@ const handleCurrentChange = val => {
console.log('当前页:', val)
}
const handleRoleChange = (row) => {
}
const showTimesheet = (row) => {
const showTimesheet = row => {
router.push({
name: 'workLog',
params: { userId: row.userId },
query: { userName: row.name }
path: '/worklog/list',
query: { userId: row.userId, projectId: formData.projectId },
})
}
const deleteDialogVisible = ref(false)
const userToDelete = ref(null)
const confirmDelete = (row) => {
userToDelete.value = row
deleteDialogVisible.value = true
const confirmDelete = row => {
ElMessageBox.confirm('确定要删除该成员吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
deleteUser(row)
})
.catch(() => {
//
})
}
const handleDelete = () => {
const index = tableData.value.findIndex(item => item.id === userToDelete.value.id)
if (index !== -1) {
tableData.value.splice(index, 1)
total.value = tableData.value.length
ElMessage.success('已删除该成员')
deleteDialogVisible.value = false
userToDelete.value = null
const deleteUser = async row => {
try {
await projectApi.deleteProjectUser(row.teamId)
ElMessage.success('成员删除成功')
getProjectUser()
} catch (error) {
console.error('删除成员失败', error)
ElMessage.error('删除成员失败')
}
}
const tableHeight = ref(400)
onMounted(() => {
const projectData = route.params.projectData || route.query.projectData
if (projectData) {
Object.assign(formData, projectData)
} else if (route.query.id) {
fetchProjectData(route.query.id)
}
})
//
const getProjectCode = async () => {
const res = await projectApi.getProjectCode()
formData.projectCode = res.data
}
const fetchProjectData = async id => {
try {
const projectData = {
id: id,
projectName: `项目${id}`,
projectCode: `XM00${id}`,
projectManager: '张三',
budgetDays: 30,
projectStatus: 'ongoing',
startDate: '2024-08-31',
endDate: '2024-09-30',
}
Object.assign(formData, projectData)
const response = await projectApi.getProjectDetail(id)
const projectData = response.data
//
formData.startDate = projectData.startDate ? new Date(projectData.startDate).toISOString().split('T')[0] : ''
formData.endDate = projectData.endDate ? new Date(projectData.endDate).toISOString().split('T')[0] : ''
//
Object.assign(formData, {
...projectData,
startDate: formData.startDate,
endDate: formData.endDate,
})
updateProjectState() //
} catch (error) {
console.error('获取项目数据失败', error)
ElMessage.error('获取项目数据失败')
}
}
//
const getDictData = async () => {
const res = await systemApi.getDictData('business_positions')
postOptions.value = res.data
}
//
const getProjectUser = async () => {
await getDictData()
const res = await projectApi.getProjectUser(formData.projectId)
tableData.value = res.data
}
const editUser = row => {
row.isEditing = true
row.originalPost = row.postId // 便
}
const saveUserEdit = async row => {
if (!formData.projectId) {
ElMessage.warning('请先保存项目信息')
return
}
try {
await projectApi.updateProjectUser({
projectId: formData.projectId,
userId: row.userId,
postId: row.postId,
teamId: row.teamId,
})
row.isEditing = false
delete row.originalPost
ElMessage.success('用户信息更新成功')
} catch (error) {
console.error('更新用户信息失败', error)
ElMessage.error('更新用户信息失败')
}
}
const cancelUserEdit = row => {
row.postId = row.originalPost
row.isEditing = false
delete row.originalPost
}
onMounted(() => {
const projectId = route.params.id || route.query.id
formData.projectId = projectId
if (projectId) {
fetchProjectData(projectId)
getProjectUser()
} else {
updateProjectState() //
getProjectCode()
}
})
</script>
<style scoped>
@ -341,6 +558,7 @@ const fetchProjectData = async id => {
}
.custom-form {
width: 80%;
margin-bottom: 20px;
}
.form-container {
width: 100%;
@ -388,8 +606,16 @@ const fetchProjectData = async id => {
}
.table-actions {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
align-self: flex-start;
}
.form-actions {
display: flex;
gap: 10px;
}
:deep(.el-table) {
@ -447,4 +673,4 @@ const fetchProjectData = async id => {
height: 64px;
margin-bottom: 16px;
}
</style>
</style>

View File

@ -6,16 +6,19 @@
<el-input v-model="searchForm.projectName" placeholder="项目名称" />
</el-form-item>
<el-form-item label="负责人" class="form-item">
<el-select v-model="searchForm.manager" placeholder="负责人">
<el-option label="张三" value="张三" />
<el-option label="李四" value="李四" />
</el-select>
<el-input
v-model="searchForm.projectLeaderName"
placeholder="负责人"
readonly
@click="openUserSelectDialog"
>
</el-input>
</el-form-item>
<el-form-item label="项目状态" class="form-item">
<el-select v-model="searchForm.status" placeholder="项目状态">
<el-option label="待启动" value="待启动" />
<el-option label="进行中" value="进行中" />
<el-option label="已完成" value="已完成" />
<el-select v-model="searchForm.projectState" placeholder="项目状态">
<el-option label="未启动" value="0" />
<el-option label="进行中" value="1" />
<el-option label="已完成" value="2" />
</el-select>
</el-form-item>
<el-form-item class="search-buttons">
@ -64,6 +67,15 @@
</div>
</template>
</el-dialog>
<!-- 用户选择弹框 -->
<SelectUser
v-model:dialogVisible="userSelectDialogVisible"
:multiSelect="false"
:currentSelectedUser="currentSelectedUser"
@close="handleUserSelectClose"
@confirm="handleUserConfirm"
/>
</div>
</template>
@ -71,26 +83,68 @@
import { ref, reactive, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import CustomTable from '@/components/table.vue'
import SelectUser from '@/components/selectUser.vue'
import { WarningFilled } from '@element-plus/icons-vue'
import { projectApi } from '@/utils/api'
const router = useRouter()
const searchForm = reactive({
projectName: '',
manager: '',
status: '',
projectLeaderName: '',
projectLeader: '',
projectState: '',
})
const columns = [
{ prop: 'projectName', label: '项目名称' },
{ prop: 'projectCode', label: '项目编号' },
{ prop: 'manager', label: '负责人' },
{ prop: 'budgetDays', label: '预计工时(天)' },
{ prop: 'startDate', label: '开始时间' },
{ prop: 'endDate', label: '结束时间' },
{ prop: 'status', label: '项目状态' },
{ prop: 'memberCount', label: '参与项目人数' },
{ prop: 'createdBy', label: '项目创建人' },
{ prop: 'projectLeaderName', label: '负责人' },
{ prop: 'budgetDate', label: '预计工时(天)' },
{
prop: 'startDate',
label: '开始时间',
type: 'status',
callback: (data, row) => {
return data.split(' ')[0]
},
},
{
prop: 'endDate',
label: '结束时间',
type: 'status',
callback: (data, row) => {
return data.split(' ')[0]
},
},
{
prop: 'projectState',
label: '项目状态',
type: 'status',
callback: (value, row) => {
let status = '未知'
let color = ''
switch (value) {
case '0':
status = '未启动'
color = '#909399' //
break
case '1':
status = '进行中'
color = '#409EFF' //
break
case '2':
status = '已完成'
color = '#67C23A' // 绿
break
}
return `<span style="color: ${color}">${status}</span>`
},
},
{ prop: 'teamNum', label: '参与项目人数' },
{ prop: 'createByName', label: '项目创建人' },
{
prop: 'operation',
label: '操作',
@ -105,6 +159,35 @@ const total = ref(0)
const deleteDialogVisible = ref(false)
const currentDeleteItem = ref(null)
const pageNum = ref(1)
const pageSize = ref(10)
//
const userSelectDialogVisible = ref(false)
const currentSelectedUser = ref([])
const openUserSelectDialog = () => {
userSelectDialogVisible.value = true
}
const handleUserSelectClose = () => {
userSelectDialogVisible.value = false
}
const handleUserConfirm = (selectedUsers) => {
if (selectedUsers.length > 0) {
const selectedUser = selectedUsers[0]
searchForm.projectLeaderName = selectedUser.name
searchForm.projectLeader = selectedUser.id
currentSelectedUser.value = [selectedUser]
} else {
searchForm.projectLeaderName = ''
searchForm.projectLeader = ''
currentSelectedUser.value = []
}
userSelectDialogVisible.value = false
}
const onSearch = () => {
//
console.log('Search with:', searchForm)
@ -125,7 +208,7 @@ const addProject = () => {
const handleEdit = row => {
router.push({
path: '/project/detail',
query: { id: row.id },
query: { id: row.projectId },
state: { projectData: row },
})
}
@ -140,42 +223,35 @@ const handleCloseDeleteDialog = () => {
currentDeleteItem.value = null
}
const confirmDelete = () => {
const confirmDelete = async () => {
//
console.log('Delete project:', currentDeleteItem.value)
await projectApi.deleteProject(currentDeleteItem.value.projectId)
deleteDialogVisible.value = false
currentDeleteItem.value = null
ElMessage.success('删除成功')
fetchProjectList()
}
const handleSizeChange = val => {
console.log(`每页 ${val}`)
pageSize.value = val
pageNum.value = 1 //
fetchProjectList()
}
const handleCurrentChange = val => {
console.log(`当前页: ${val}`)
pageNum.value = val
fetchProjectList()
}
const fetchProjectList = () => {
const fetchProjectList = async () => {
// 使API
const fakeData = Array(20)
.fill(null)
.map((_, index) => ({
id: index + 1,
projectName: `项目${index + 1}`,
projectCode: `XM00${index + 1}`,
manager: ['张三', '李四', '王五'][index % 3],
budgetDays: Math.floor(Math.random() * 100) + 10,
startDate: '2024-08-31',
endDate: '2024-09-15',
status: ['待启动', '进行中', '已完成'][index % 3],
memberCount: Math.floor(Math.random() * 10) + 3,
createdBy: '张三',
}))
tableData.value = fakeData
total.value = fakeData.length
const res = await projectApi.listProject({
...searchForm,
pageNum: pageNum.value,
pageSize: pageSize.value,
})
tableData.value = res.rows
total.value = res.total
}
onMounted(() => {
@ -278,4 +354,4 @@ onMounted(() => {
left: 50%;
transform: translate(-50%, -50%);
}
</style>
</style>

View File

@ -60,17 +60,6 @@ export default ({ mode }) => {
},
// 设置代理
proxy: {
'/upload': {
target: 'http://111.10.228.245:8088/external',
changeOrigin: true,
secure: false,
rewrite: path => path.replace('/upload/', '/upload/'),
},
'/aiopsfile': {
target: 'http://111.10.228.245:8088/external',
changeOrigin: true,
secure: false,
},
[env.VITE_APP_REQUESTURL]: {
target: env.VITE_APP_PROXYURL,