项目接口调试完成
parent
60cb68401f
commit
b5f23e89d8
|
@ -1,6 +1,6 @@
|
||||||
VITE_BASE_URL = "./"
|
VITE_BASE_URL = "./"
|
||||||
VITE_BASE_NAME = "external"
|
VITE_BASE_NAME = "pms-front-test"
|
||||||
VITE_APP_PROXYURL = 'http://183.230.174.15:8088/external'
|
VITE_APP_PROXYURL = 'http://192.168.124.202:8080'
|
||||||
VITE_APP_REQUESTURL = '/external'
|
VITE_APP_REQUESTURL = '/pms-front-test'
|
||||||
VITE_APP_ROUTERURL = '/external/'
|
VITE_APP_ROUTERURL = '/pms-front-test/'
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
VITE_BASE_URL = '/pms-front-test'
|
VITE_BASE_URL = '/pms-front-test'
|
||||||
VITE_BASE_NAME = "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_REQUESTURL = '/pms-front-test'
|
||||||
VITE_APP_ROUTERURL = '/pms-front-test/'
|
VITE_APP_ROUTERURL = '/pms-front-test/'
|
|
@ -27,6 +27,13 @@
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</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>
|
<template v-else>
|
||||||
{{ scope.row[column.prop] }}
|
{{ scope.row[column.prop] }}
|
||||||
</template>
|
</template>
|
||||||
|
@ -160,4 +167,30 @@ onUnmounted(() => {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
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>
|
</style>
|
||||||
|
|
|
@ -108,7 +108,7 @@ export const asyncRoutes = [
|
||||||
name: 'worklogList',
|
name: 'worklogList',
|
||||||
authKey: 'Workorder',
|
authKey: 'Workorder',
|
||||||
meta: {
|
meta: {
|
||||||
title: '日志列表',
|
title: '工作日志',
|
||||||
},
|
},
|
||||||
component: () => import('@/views/workLog/list.vue'),
|
component: () => import('@/views/workLog/list.vue'),
|
||||||
},
|
},
|
||||||
|
|
|
@ -25,22 +25,10 @@ export const useUserStore = defineStore('user', {
|
||||||
async login(params) {
|
async login(params) {
|
||||||
try {
|
try {
|
||||||
const res = await loginApi.login(params)
|
const res = await loginApi.login(params)
|
||||||
const roleType = res.data.user.roles
|
setToken(res.token)
|
||||||
.filter(item => item.roleType)
|
await this.updateUserInfo()
|
||||||
.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)
|
|
||||||
ElMessage.success({ message: '登录成功' })
|
ElMessage.success({ message: '登录成功' })
|
||||||
const permissions = [...res.data.resourceKeys]
|
const permissions = [...this.userInfo.permissions]
|
||||||
// , 'WorkOrderError', 'workOrderDestroy', 'workOrderReserver'
|
// , 'WorkOrderError', 'workOrderDestroy', 'workOrderReserver'
|
||||||
sessionStorage.setItem('permissions', $utils.encryptByDES.CBC(JSON.stringify(permissions)))
|
sessionStorage.setItem('permissions', $utils.encryptByDES.CBC(JSON.stringify(permissions)))
|
||||||
this.updateAsyncRoutes()
|
this.updateAsyncRoutes()
|
||||||
|
@ -56,8 +44,8 @@ export const useUserStore = defineStore('user', {
|
||||||
removeToken()
|
removeToken()
|
||||||
router.push('/')
|
router.push('/')
|
||||||
},
|
},
|
||||||
async updateUserInfo(data) {
|
async updateUserInfo() {
|
||||||
const userInfo = data.user
|
const userInfo = await loginApi.getUserInfo()
|
||||||
sessionStorage.setItem('userInfo', $utils.encryptByDES.CBC(JSON.stringify(userInfo)))
|
sessionStorage.setItem('userInfo', $utils.encryptByDES.CBC(JSON.stringify(userInfo)))
|
||||||
this.userInfo = userInfo
|
this.userInfo = userInfo
|
||||||
},
|
},
|
||||||
|
@ -67,8 +55,8 @@ export const useUserStore = defineStore('user', {
|
||||||
updateAsyncRoutes() {
|
updateAsyncRoutes() {
|
||||||
// 权限时处理异步路由
|
// 权限时处理异步路由
|
||||||
let sessionPermissions = JSON.parse($utils.decryptByDES.CBC(sessionStorage.getItem('permissions')))
|
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)
|
result = editRedirect(result)
|
||||||
// console.log(result, 'result')
|
// console.log(result, 'result')
|
||||||
|
|
|
@ -1,14 +1,37 @@
|
||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
|
|
||||||
//登录板块api
|
//登录板块api
|
||||||
export const loginApi = {
|
export const loginApi = {
|
||||||
// 登录
|
// 登录
|
||||||
login: data => request('/login', 'post', data),
|
login: data => request('/login', 'json', data),
|
||||||
// 退出登录
|
// 退出登录
|
||||||
loginOut: () => request('/aiops-api/logout', 'get'),
|
loginOut: () => request('/aiops-api/logout', 'get'),
|
||||||
//修改密码
|
//修改密码
|
||||||
editPassword: data => request('/aiops-api/auth/user/updatePwd', 'post', data),
|
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 = () => {
|
export const useUserApi = () => {
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,6 +39,7 @@ export const useUserApi = () => {
|
||||||
// 系统板块
|
// 系统板块
|
||||||
export const systemApi = {
|
export const systemApi = {
|
||||||
getUserList: data => request('/aiops-api/auth/user/list', 'post', data),
|
getUserList: data => request('/aiops-api/auth/user/list', 'post', data),
|
||||||
|
getDictData: dictCode => request('/system/dict/data/type/' + dictCode, 'get'),
|
||||||
}
|
}
|
||||||
// 系统定时任务
|
// 系统定时任务
|
||||||
export const systemJobApi = {
|
export const systemJobApi = {
|
||||||
|
|
|
@ -17,7 +17,7 @@ const service = axios.create({
|
||||||
service.interceptors.request.use(
|
service.interceptors.request.use(
|
||||||
config => {
|
config => {
|
||||||
if (getToken()) {
|
if (getToken()) {
|
||||||
config.headers['token'] = getToken()
|
config.headers['Authorization'] = 'Bearer ' + getToken()
|
||||||
}
|
}
|
||||||
return config
|
return config
|
||||||
},
|
},
|
||||||
|
@ -105,6 +105,9 @@ const doRequest = (url, method = 'GET', params, opts, callback) => {
|
||||||
method = 'POST'
|
method = 'POST'
|
||||||
opts.headers['Content-Type'] = 'application/json'
|
opts.headers['Content-Type'] = 'application/json'
|
||||||
opts.responseType = 'blob'
|
opts.responseType = 'blob'
|
||||||
|
}else if (/captchaImage/i.test(method)) {
|
||||||
|
method = 'get'
|
||||||
|
opts.headers['Content-Type'] = 'application/json'
|
||||||
}
|
}
|
||||||
service({
|
service({
|
||||||
url: url,
|
url: url,
|
||||||
|
@ -156,7 +159,7 @@ const doRequest = (url, method = 'GET', params, opts, callback) => {
|
||||||
opts.SHOW_LOADING && loadingInstance && loadingInstance.close()
|
opts.SHOW_LOADING && loadingInstance && loadingInstance.close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (res.code === 0) {
|
if (res.code === 200) {
|
||||||
resolve(res) //
|
resolve(res) //
|
||||||
// resolve(res.data)
|
// resolve(res.data)
|
||||||
} else if (res.code === 401) {
|
} else if (res.code === 401) {
|
||||||
|
@ -175,7 +178,6 @@ const doRequest = (url, method = 'GET', params, opts, callback) => {
|
||||||
opts.SHOW_LOADING && loadingInstance && loadingInstance.close()
|
opts.SHOW_LOADING && loadingInstance && loadingInstance.close()
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.log('ssss')
|
|
||||||
opts.SHOW_LOADING && loadingInstance && loadingInstance.close()
|
opts.SHOW_LOADING && loadingInstance && loadingInstance.close()
|
||||||
if (err.code === 'ERR_CANCELED') return
|
if (err.code === 'ERR_CANCELED') return
|
||||||
ElMessage.error({
|
ElMessage.error({
|
||||||
|
|
|
@ -73,6 +73,7 @@
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { reactive, ref } from 'vue'
|
import { reactive, ref } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
|
import { loginApi } from '@/utils/api'
|
||||||
import { useUserStore } from '@/stores/user'
|
import { useUserStore } from '@/stores/user'
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
|
@ -82,23 +83,14 @@ const login = reactive({
|
||||||
pwd: '',
|
pwd: '',
|
||||||
validateCode: '',
|
validateCode: '',
|
||||||
})
|
})
|
||||||
const codeSrc = ref(
|
const codeSrc = ref()
|
||||||
process.env.NODE_ENV === 'production'
|
const uuid = ref()
|
||||||
? `${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 updateCode = async () => {
|
const updateCode = async () => {
|
||||||
codeSrc.value =
|
const res = await loginApi.getCode()
|
||||||
process.env.NODE_ENV === 'production'
|
codeSrc.value = 'data:image/png;base64,' + res.img
|
||||||
? `${window.location.origin}${
|
uuid.value = res.uuid
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// getCode()
|
|
||||||
const confirmLogin = async () => {
|
const confirmLogin = async () => {
|
||||||
if (!login.account) {
|
if (!login.account) {
|
||||||
ElMessage.error({ message: `用户名不能为空!` })
|
ElMessage.error({ message: `用户名不能为空!` })
|
||||||
|
@ -113,9 +105,10 @@ const confirmLogin = async () => {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const params = {
|
const params = {
|
||||||
password: $utils.encryptByMD5(`${login.account}${login.pwd}`),
|
password: login.pwd,
|
||||||
username: login.account,
|
username: login.account,
|
||||||
validateCode: login.validateCode,
|
code: login.validateCode,
|
||||||
|
uuid: uuid.value,
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await userStore.login(params)
|
await userStore.login(params)
|
||||||
|
@ -127,6 +120,7 @@ const confirmLogin = async () => {
|
||||||
const usershadow = ref(false)
|
const usershadow = ref(false)
|
||||||
const shadow = ref(false)
|
const shadow = ref(false)
|
||||||
const codeShadow = ref(false)
|
const codeShadow = ref(false)
|
||||||
|
updateCode()
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.login {
|
.login {
|
||||||
|
|
|
@ -1,25 +1,29 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="project-management">
|
<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-row :gutter="24">
|
||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
<el-form-item label="项目名称">
|
<el-form-item label="项目名称" prop="projectName">
|
||||||
<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-input
|
<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="选择项目负责人"
|
placeholder="选择项目负责人"
|
||||||
readonly
|
readonly
|
||||||
|
:disabled="isEditing"
|
||||||
@click="openProjectManagerSelect"
|
@click="openProjectManagerSelect"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
@ -27,36 +31,58 @@
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row :gutter="24">
|
<el-row :gutter="24">
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="项目状态">
|
<el-form-item label="项目状态" prop="projectState">
|
||||||
<el-select v-model="formData.projectStatus" placeholder="请选择项目状态" class="full-width">
|
<el-select
|
||||||
<el-option label="进行中" value="ongoing" />
|
v-model="formData.projectState"
|
||||||
<el-option label="已完成" value="completed" />
|
placeholder="请选择项目状态"
|
||||||
<el-option label="已暂停" value="paused" />
|
:disabled="isEditing"
|
||||||
|
class="full-width"
|
||||||
|
>
|
||||||
|
<el-option label="未启动" value="0" />
|
||||||
|
<el-option label="进行中" value="1" />
|
||||||
|
<el-option label="已完成" value="2" />
|
||||||
</el-select>
|
</el-select>
|
||||||
</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="预算天数" prop="budgetDate">
|
||||||
<el-input type="number" v-model="formData.budgetDays" :min="0" class="full-width" />
|
<el-input v-model.number="formData.budgetDate" :min="0" :disabled="isEditing" class="full-width" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row :gutter="24">
|
<el-row :gutter="24">
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="项目开始时间">
|
<el-form-item label="开始日期" prop="startDate">
|
||||||
<el-date-picker v-model="formData.startDate" type="date" placeholder="选择日期" class="full-width" />
|
<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-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="项目结束时间">
|
<el-form-item label="结束日期" prop="endDate">
|
||||||
<el-date-picker v-model="formData.endDate" type="date" placeholder="选择日期" class="full-width" />
|
<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-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</el-form>
|
</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">
|
<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>
|
</div>
|
||||||
|
|
||||||
<CustomTable
|
<CustomTable
|
||||||
|
@ -65,31 +91,47 @@
|
||||||
:total="total"
|
:total="total"
|
||||||
:show-selection="false"
|
:show-selection="false"
|
||||||
:show-index="true"
|
:show-index="true"
|
||||||
|
:show-pagination="false"
|
||||||
:table-height="tableHeight"
|
:table-height="tableHeight"
|
||||||
@size-change="handleSizeChange"
|
@size-change="handleSizeChange"
|
||||||
@current-change="handleCurrentChange"
|
@current-change="handleCurrentChange"
|
||||||
>
|
>
|
||||||
<template #name="{ row }">
|
<template #name="{ row }">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="row.name"
|
v-if="row.isNew"
|
||||||
|
v-model="row.userName"
|
||||||
placeholder="选择人员"
|
placeholder="选择人员"
|
||||||
readonly
|
readonly
|
||||||
@click="openSelectUser(row)"
|
@click="openSelectUser(row)"
|
||||||
/>
|
/>
|
||||||
|
<span v-else>{{ row.userName }}</span>
|
||||||
</template>
|
</template>
|
||||||
<template #role="{ row }">
|
<template #post="{ row }">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="row.role"
|
v-if="row.isEditing || row.isNew"
|
||||||
|
v-model="row.postId"
|
||||||
placeholder="请选择职位"
|
placeholder="请选择职位"
|
||||||
@change="handleRoleChange(row)"
|
@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>
|
</el-select>
|
||||||
|
<span v-else>{{ postOptions.find(post => post.dictValue === row.postId)?.dictLabel }}</span>
|
||||||
</template>
|
</template>
|
||||||
<template #operation="{ row }">
|
<template #operation="{ row }">
|
||||||
<div class="operation-buttons">
|
<div class="operation-buttons">
|
||||||
<el-button text type="primary" @click="showTimesheet(row)">工作日志</el-button>
|
<template v-if="row.isNew">
|
||||||
<el-button text type="danger" @click="confirmDelete(row)">删除</el-button>
|
<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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</CustomTable>
|
</CustomTable>
|
||||||
|
@ -114,13 +156,9 @@
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- 删除确认对话框 -->
|
<!-- 删除确认对话框 -->
|
||||||
<el-dialog
|
<el-dialog v-model="deleteDialogVisible" title="确认删除" width="30%">
|
||||||
v-model="deleteDialogVisible"
|
|
||||||
title="确认删除"
|
|
||||||
width="30%"
|
|
||||||
>
|
|
||||||
<div class="delete-confirm">
|
<div class="delete-confirm">
|
||||||
<img src="@/assets/warning.png" alt="警告" class="warning-icon">
|
<img src="@/assets/warning.png" alt="警告" class="warning-icon" />
|
||||||
<p>确定要删除该成员吗?此操作不可逆。</p>
|
<p>确定要删除该成员吗?此操作不可逆。</p>
|
||||||
</div>
|
</div>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
|
@ -134,42 +172,41 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, reactive, onMounted } 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/table.vue'
|
||||||
import { Plus } from '@element-plus/icons-vue'
|
import { Plus } 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'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
const formRef = ref(null)
|
||||||
|
|
||||||
const isEditing = ref(true)
|
const isEditing = computed(() => !!formData.projectId)
|
||||||
const showSelectUser = ref(false)
|
const showSelectUser = ref(false)
|
||||||
const showProjectManagerSelect = ref(false)
|
const showProjectManagerSelect = ref(false)
|
||||||
const formData = reactive({
|
const formData = reactive({
|
||||||
|
projectId: null,
|
||||||
projectName: '',
|
projectName: '',
|
||||||
projectCode: '',
|
projectCode: '',
|
||||||
projectManager: '',
|
projectLeaderName: '',
|
||||||
budgetDays: 0,
|
projectLeader: '',
|
||||||
projectStatus: '',
|
projectState: '',
|
||||||
startDate: '',
|
startDate: '',
|
||||||
endDate: '',
|
endDate: '',
|
||||||
|
budgetDate: 0,
|
||||||
|
state: 0,
|
||||||
})
|
})
|
||||||
|
|
||||||
const roleOptions = ['开发', '设计', '测试', '产品', '项目经理']
|
const postOptions = ref([]) // 根据实际情况调整
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{ prop: 'name', label: '人员姓名', slot: 'name' },
|
{ prop: 'name', label: '姓名' },
|
||||||
{ prop: 'role', label: '项目职位', slot: 'role' },
|
{ prop: 'post', label: '项目职位' },
|
||||||
{ prop: 'workdays', label: '累计人天数' },
|
{ prop: 'workdays', label: '工作天数' },
|
||||||
{
|
{ prop: 'operation', label: '操作', width: '250px' },
|
||||||
prop: 'operation',
|
|
||||||
label: '操作',
|
|
||||||
width: '200',
|
|
||||||
fixed: 'right',
|
|
||||||
className: 'operation-column',
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
|
|
||||||
const tableData = ref([])
|
const tableData = ref([])
|
||||||
|
@ -178,10 +215,31 @@ const currentEditingRow = ref(null)
|
||||||
const currentSelectedUser = ref([])
|
const currentSelectedUser = ref([])
|
||||||
const projectManagerSelectedUser = 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) => {
|
const validateDates = (rule, value, callback) => {
|
||||||
if (formData.startDate && formData.endDate) {
|
if (formData.startDate && formData.endDate) {
|
||||||
if (new Date(formData.startDate) > new Date(formData.endDate)) {
|
const start = new Date(formData.startDate)
|
||||||
callback(new Error('开始时间不能大于结束时间'))
|
const end = new Date(formData.endDate)
|
||||||
|
if (start > end) {
|
||||||
|
callback(new Error('开始日期不能晚于结束日期'))
|
||||||
} else {
|
} else {
|
||||||
callback()
|
callback()
|
||||||
}
|
}
|
||||||
|
@ -191,38 +249,140 @@ const validateDates = (rule, value, callback) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const rules = {
|
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: [
|
startDate: [
|
||||||
{ required: true, message: '请选择开始时间', trigger: 'change' },
|
{ required: true, message: '请选择开始日期', trigger: 'change' },
|
||||||
{ validator: validateDates, trigger: 'change' }
|
{ validator: validateDates, trigger: 'change' },
|
||||||
],
|
],
|
||||||
endDate: [
|
endDate: [
|
||||||
{ required: true, message: '请选择结束时间', trigger: 'change' },
|
{ required: true, message: '请选择结束日期', trigger: 'change' },
|
||||||
{ validator: validateDates, 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 = {
|
const newUser = {
|
||||||
id: Date.now(),
|
id: Date.now(),
|
||||||
name: '',
|
name: '',
|
||||||
role: '',
|
post: '',
|
||||||
|
postId: '',
|
||||||
workdays: 0,
|
workdays: 0,
|
||||||
userId: null
|
userId: null,
|
||||||
|
isNew: true,
|
||||||
}
|
}
|
||||||
tableData.value.push(newUser)
|
tableData.value.unshift(newUser)
|
||||||
total.value = tableData.value.length
|
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
|
currentEditingRow.value = row
|
||||||
currentSelectedUser.value = row.userId ? [{ id: row.userId, name: row.name }] : []
|
currentSelectedUser.value = row.userId ? [{ id: row.userId, name: row.name }] : []
|
||||||
showSelectUser.value = true
|
showSelectUser.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleUserConfirm = (users) => {
|
const handleUserConfirm = selectedUsers => {
|
||||||
if (users.length > 0) {
|
if (!formData.projectId) {
|
||||||
const selectedUser = users[0]
|
ElMessage.warning('请先保存项目信息后再选择用户')
|
||||||
currentEditingRow.value.name = selectedUser.name
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedUsers.length > 0) {
|
||||||
|
const selectedUser = selectedUsers[0]
|
||||||
|
currentEditingRow.value.userName = selectedUser.name
|
||||||
currentEditingRow.value.userId = selectedUser.id
|
currentEditingRow.value.userId = selectedUser.id
|
||||||
} else {
|
} else {
|
||||||
currentEditingRow.value.name = ''
|
currentEditingRow.value.name = ''
|
||||||
|
@ -238,20 +398,20 @@ const closeSelectUser = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const openProjectManagerSelect = () => {
|
const openProjectManagerSelect = () => {
|
||||||
projectManagerSelectedUser.value = formData.projectManager
|
projectManagerSelectedUser.value = formData.projectLeader
|
||||||
? [{ id: formData.projectManagerId, name: formData.projectManager }]
|
? [{ id: formData.projectLeaderId, name: formData.projectLeader }]
|
||||||
: []
|
: []
|
||||||
showProjectManagerSelect.value = true
|
showProjectManagerSelect.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleProjectManagerConfirm = (users) => {
|
const handleProjectManagerConfirm = users => {
|
||||||
if (users.length > 0) {
|
if (users.length > 0) {
|
||||||
const selectedUser = users[0]
|
const selectedUser = users[0]
|
||||||
formData.projectManager = selectedUser.name
|
formData.projectLeaderName = selectedUser.name
|
||||||
formData.projectManagerId = selectedUser.id
|
formData.projectLeader = selectedUser.id
|
||||||
} else {
|
} else {
|
||||||
formData.projectManager = ''
|
formData.projectLeaderName = ''
|
||||||
formData.projectManagerId = null
|
formData.projectLeader = null
|
||||||
}
|
}
|
||||||
showProjectManagerSelect.value = false
|
showProjectManagerSelect.value = false
|
||||||
}
|
}
|
||||||
|
@ -268,65 +428,122 @@ const handleCurrentChange = val => {
|
||||||
console.log('当前页:', val)
|
console.log('当前页:', val)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleRoleChange = (row) => {
|
const showTimesheet = row => {
|
||||||
}
|
|
||||||
|
|
||||||
const showTimesheet = (row) => {
|
|
||||||
router.push({
|
router.push({
|
||||||
name: 'workLog',
|
path: '/worklog/list',
|
||||||
params: { userId: row.userId },
|
query: { userId: row.userId, projectId: formData.projectId },
|
||||||
query: { userName: row.name }
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const deleteDialogVisible = ref(false)
|
const deleteDialogVisible = ref(false)
|
||||||
const userToDelete = ref(null)
|
|
||||||
|
|
||||||
const confirmDelete = (row) => {
|
const confirmDelete = row => {
|
||||||
userToDelete.value = row
|
ElMessageBox.confirm('确定要删除该成员吗?', '提示', {
|
||||||
deleteDialogVisible.value = true
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning',
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
deleteUser(row)
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
// 取消删除操作
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleDelete = () => {
|
const deleteUser = async row => {
|
||||||
const index = tableData.value.findIndex(item => item.id === userToDelete.value.id)
|
try {
|
||||||
if (index !== -1) {
|
await projectApi.deleteProjectUser(row.teamId)
|
||||||
tableData.value.splice(index, 1)
|
ElMessage.success('成员删除成功')
|
||||||
total.value = tableData.value.length
|
getProjectUser()
|
||||||
ElMessage.success('已删除该成员')
|
} catch (error) {
|
||||||
deleteDialogVisible.value = false
|
console.error('删除成员失败', error)
|
||||||
userToDelete.value = null
|
ElMessage.error('删除成员失败')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const tableHeight = ref(400)
|
const tableHeight = ref(400)
|
||||||
|
// 获取项目编码
|
||||||
onMounted(() => {
|
const getProjectCode = async () => {
|
||||||
const projectData = route.params.projectData || route.query.projectData
|
const res = await projectApi.getProjectCode()
|
||||||
if (projectData) {
|
formData.projectCode = res.data
|
||||||
Object.assign(formData, projectData)
|
}
|
||||||
} else if (route.query.id) {
|
|
||||||
fetchProjectData(route.query.id)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const fetchProjectData = async id => {
|
const fetchProjectData = async id => {
|
||||||
try {
|
try {
|
||||||
const projectData = {
|
const response = await projectApi.getProjectDetail(id)
|
||||||
id: id,
|
const projectData = response.data
|
||||||
projectName: `项目${id}`,
|
// 将时间戳转换为日期字符串
|
||||||
projectCode: `XM00${id}`,
|
formData.startDate = projectData.startDate ? new Date(projectData.startDate).toISOString().split('T')[0] : ''
|
||||||
projectManager: '张三',
|
formData.endDate = projectData.endDate ? new Date(projectData.endDate).toISOString().split('T')[0] : ''
|
||||||
budgetDays: 30,
|
// 复制其他字段
|
||||||
projectStatus: 'ongoing',
|
Object.assign(formData, {
|
||||||
startDate: '2024-08-31',
|
...projectData,
|
||||||
endDate: '2024-09-30',
|
startDate: formData.startDate,
|
||||||
}
|
endDate: formData.endDate,
|
||||||
|
})
|
||||||
Object.assign(formData, projectData)
|
updateProjectState() // 更新项目状态
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取项目数据失败', 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>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
@ -341,6 +558,7 @@ const fetchProjectData = async id => {
|
||||||
}
|
}
|
||||||
.custom-form {
|
.custom-form {
|
||||||
width: 80%;
|
width: 80%;
|
||||||
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
.form-container {
|
.form-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -388,8 +606,16 @@ const fetchProjectData = async id => {
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-actions {
|
.table-actions {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
align-self: flex-start;
|
}
|
||||||
|
|
||||||
|
.form-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.el-table) {
|
:deep(.el-table) {
|
||||||
|
|
|
@ -6,16 +6,19 @@
|
||||||
<el-input v-model="searchForm.projectName" placeholder="项目名称" />
|
<el-input v-model="searchForm.projectName" placeholder="项目名称" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="负责人" class="form-item">
|
<el-form-item label="负责人" class="form-item">
|
||||||
<el-select v-model="searchForm.manager" placeholder="负责人">
|
<el-input
|
||||||
<el-option label="张三" value="张三" />
|
v-model="searchForm.projectLeaderName"
|
||||||
<el-option label="李四" value="李四" />
|
placeholder="负责人"
|
||||||
</el-select>
|
readonly
|
||||||
|
@click="openUserSelectDialog"
|
||||||
|
>
|
||||||
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="项目状态" class="form-item">
|
<el-form-item label="项目状态" class="form-item">
|
||||||
<el-select v-model="searchForm.status" placeholder="项目状态">
|
<el-select v-model="searchForm.projectState" placeholder="项目状态">
|
||||||
<el-option label="待启动" value="待启动" />
|
<el-option label="未启动" value="0" />
|
||||||
<el-option label="进行中" value="进行中" />
|
<el-option label="进行中" value="1" />
|
||||||
<el-option label="已完成" value="已完成" />
|
<el-option label="已完成" value="2" />
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item class="search-buttons">
|
<el-form-item class="search-buttons">
|
||||||
|
@ -64,6 +67,15 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 用户选择弹框 -->
|
||||||
|
<SelectUser
|
||||||
|
v-model:dialogVisible="userSelectDialogVisible"
|
||||||
|
:multiSelect="false"
|
||||||
|
:currentSelectedUser="currentSelectedUser"
|
||||||
|
@close="handleUserSelectClose"
|
||||||
|
@confirm="handleUserConfirm"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -71,26 +83,68 @@
|
||||||
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/table.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'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
const searchForm = reactive({
|
const searchForm = reactive({
|
||||||
projectName: '',
|
projectName: '',
|
||||||
manager: '',
|
projectLeaderName: '',
|
||||||
status: '',
|
projectLeader: '',
|
||||||
|
projectState: '',
|
||||||
})
|
})
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{ prop: 'projectName', label: '项目名称' },
|
{ prop: 'projectName', label: '项目名称' },
|
||||||
{ prop: 'projectCode', label: '项目编号' },
|
{ prop: 'projectCode', label: '项目编号' },
|
||||||
{ prop: 'manager', label: '负责人' },
|
{ prop: 'projectLeaderName', label: '负责人' },
|
||||||
{ prop: 'budgetDays', label: '预计工时(天)' },
|
{ prop: 'budgetDate', label: '预计工时(天)' },
|
||||||
{ prop: 'startDate', label: '开始时间' },
|
{
|
||||||
{ prop: 'endDate', label: '结束时间' },
|
prop: 'startDate',
|
||||||
{ prop: 'status', label: '项目状态' },
|
label: '开始时间',
|
||||||
{ prop: 'memberCount', label: '参与项目人数' },
|
type: 'status',
|
||||||
{ prop: 'createdBy', label: '项目创建人' },
|
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',
|
prop: 'operation',
|
||||||
label: '操作',
|
label: '操作',
|
||||||
|
@ -105,6 +159,35 @@ const total = ref(0)
|
||||||
const deleteDialogVisible = ref(false)
|
const deleteDialogVisible = ref(false)
|
||||||
const currentDeleteItem = ref(null)
|
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 = () => {
|
const onSearch = () => {
|
||||||
// 实现搜索逻辑
|
// 实现搜索逻辑
|
||||||
console.log('Search with:', searchForm)
|
console.log('Search with:', searchForm)
|
||||||
|
@ -125,7 +208,7 @@ const addProject = () => {
|
||||||
const handleEdit = row => {
|
const handleEdit = row => {
|
||||||
router.push({
|
router.push({
|
||||||
path: '/project/detail',
|
path: '/project/detail',
|
||||||
query: { id: row.id },
|
query: { id: row.projectId },
|
||||||
state: { projectData: row },
|
state: { projectData: row },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -140,42 +223,35 @@ const handleCloseDeleteDialog = () => {
|
||||||
currentDeleteItem.value = null
|
currentDeleteItem.value = null
|
||||||
}
|
}
|
||||||
|
|
||||||
const confirmDelete = () => {
|
const confirmDelete = async () => {
|
||||||
// 实现删除逻辑
|
// 实现删除逻辑
|
||||||
console.log('Delete project:', currentDeleteItem.value)
|
await projectApi.deleteProject(currentDeleteItem.value.projectId)
|
||||||
deleteDialogVisible.value = false
|
deleteDialogVisible.value = false
|
||||||
currentDeleteItem.value = null
|
currentDeleteItem.value = null
|
||||||
|
ElMessage.success('删除成功')
|
||||||
fetchProjectList()
|
fetchProjectList()
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSizeChange = val => {
|
const handleSizeChange = val => {
|
||||||
console.log(`每页 ${val} 条`)
|
pageSize.value = val
|
||||||
|
pageNum.value = 1 // 重置为第一页
|
||||||
fetchProjectList()
|
fetchProjectList()
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleCurrentChange = val => {
|
const handleCurrentChange = val => {
|
||||||
console.log(`当前页: ${val}`)
|
pageNum.value = val
|
||||||
fetchProjectList()
|
fetchProjectList()
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchProjectList = () => {
|
const fetchProjectList = async () => {
|
||||||
// 这里使用假数据,实际应该调用API
|
// 这里使用假数据,实际应该调用API
|
||||||
const fakeData = Array(20)
|
const res = await projectApi.listProject({
|
||||||
.fill(null)
|
...searchForm,
|
||||||
.map((_, index) => ({
|
pageNum: pageNum.value,
|
||||||
id: index + 1,
|
pageSize: pageSize.value,
|
||||||
projectName: `项目${index + 1}`,
|
})
|
||||||
projectCode: `XM00${index + 1}`,
|
tableData.value = res.rows
|
||||||
manager: ['张三', '李四', '王五'][index % 3],
|
total.value = res.total
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
|
|
@ -60,17 +60,6 @@ export default ({ mode }) => {
|
||||||
},
|
},
|
||||||
// 设置代理
|
// 设置代理
|
||||||
proxy: {
|
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]: {
|
[env.VITE_APP_REQUESTURL]: {
|
||||||
target: env.VITE_APP_PROXYURL,
|
target: env.VITE_APP_PROXYURL,
|
||||||
|
|
Loading…
Reference in New Issue