mirror of
https://github.com/hpd840321/craftlabs-authorization-sdk.git
synced 2026-06-09 01:50:30 +08:00
feat(m1): add project stakeholder CRUD
This commit is contained in:
+98
@@ -0,0 +1,98 @@
|
||||
package cn.craftlabs.platform.api.persistence.project;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
@TableName("platform_project_stakeholder")
|
||||
public class PlatformProjectStakeholder {
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
@TableField("project_id")
|
||||
private Long projectId;
|
||||
|
||||
@TableField("contact_name")
|
||||
private String contactName;
|
||||
|
||||
@TableField("contact_role")
|
||||
private String contactRole;
|
||||
|
||||
private String phone;
|
||||
|
||||
private String email;
|
||||
|
||||
@TableField("is_internal")
|
||||
private Boolean isInternal;
|
||||
|
||||
@TableField("created_at")
|
||||
private OffsetDateTime createdAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getProjectId() {
|
||||
return projectId;
|
||||
}
|
||||
|
||||
public void setProjectId(Long projectId) {
|
||||
this.projectId = projectId;
|
||||
}
|
||||
|
||||
public String getContactName() {
|
||||
return contactName;
|
||||
}
|
||||
|
||||
public void setContactName(String contactName) {
|
||||
this.contactName = contactName;
|
||||
}
|
||||
|
||||
public String getContactRole() {
|
||||
return contactRole;
|
||||
}
|
||||
|
||||
public void setContactRole(String contactRole) {
|
||||
this.contactRole = contactRole;
|
||||
}
|
||||
|
||||
public String getPhone() {
|
||||
return phone;
|
||||
}
|
||||
|
||||
public void setPhone(String phone) {
|
||||
this.phone = phone;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public Boolean getIsInternal() {
|
||||
return isInternal;
|
||||
}
|
||||
|
||||
public void setIsInternal(Boolean isInternal) {
|
||||
this.isInternal = isInternal;
|
||||
}
|
||||
|
||||
public OffsetDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(OffsetDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package cn.craftlabs.platform.api.persistence.project;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface PlatformProjectStakeholderMapper extends BaseMapper<PlatformProjectStakeholder> {}
|
||||
+33
-1
@@ -4,6 +4,8 @@ import cn.craftlabs.platform.api.service.ProjectService;
|
||||
import cn.craftlabs.platform.api.web.dto.PageResponse;
|
||||
import cn.craftlabs.platform.api.web.dto.ProjectRequest;
|
||||
import cn.craftlabs.platform.api.web.dto.ProjectResponse;
|
||||
import cn.craftlabs.platform.api.web.dto.StakeholderRequest;
|
||||
import cn.craftlabs.platform.api.web.dto.StakeholderResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.Min;
|
||||
@@ -20,7 +22,8 @@ import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/** 项目 API。{@code DELETE /{id}} 为<strong>物理删除</strong>。 */
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/projects")
|
||||
@Validated
|
||||
@@ -62,4 +65,33 @@ public class ProjectController {
|
||||
public void delete(@PathVariable("id") long id) {
|
||||
projectService.delete(id);
|
||||
}
|
||||
|
||||
@GetMapping("/{projectId}/stakeholders")
|
||||
public List<StakeholderResponse> listStakeholders(@PathVariable("projectId") long projectId) {
|
||||
return projectService.listStakeholders(projectId);
|
||||
}
|
||||
|
||||
@PostMapping("/{projectId}/stakeholders")
|
||||
@ResponseStatus(HttpStatus.CREATED)
|
||||
public StakeholderResponse addStakeholder(
|
||||
@PathVariable("projectId") long projectId,
|
||||
@Valid @RequestBody StakeholderRequest request) {
|
||||
return projectService.addStakeholder(projectId, request);
|
||||
}
|
||||
|
||||
@PutMapping("/{projectId}/stakeholders/{id}")
|
||||
public StakeholderResponse updateStakeholder(
|
||||
@PathVariable("projectId") long projectId,
|
||||
@PathVariable("id") long id,
|
||||
@Valid @RequestBody StakeholderRequest request) {
|
||||
return projectService.updateStakeholder(id, request);
|
||||
}
|
||||
|
||||
@DeleteMapping("/{projectId}/stakeholders/{id}")
|
||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||
public void deleteStakeholder(
|
||||
@PathVariable("projectId") long projectId,
|
||||
@PathVariable("id") long id) {
|
||||
projectService.deleteStakeholder(id);
|
||||
}
|
||||
}
|
||||
|
||||
+75
-1
@@ -2,9 +2,13 @@ package cn.craftlabs.platform.api.service;
|
||||
|
||||
import cn.craftlabs.platform.api.persistence.project.PlatformProject;
|
||||
import cn.craftlabs.platform.api.persistence.project.PlatformProjectMapper;
|
||||
import cn.craftlabs.platform.api.persistence.project.PlatformProjectStakeholder;
|
||||
import cn.craftlabs.platform.api.persistence.project.PlatformProjectStakeholderMapper;
|
||||
import cn.craftlabs.platform.api.web.dto.PageResponse;
|
||||
import cn.craftlabs.platform.api.web.dto.ProjectRequest;
|
||||
import cn.craftlabs.platform.api.web.dto.ProjectResponse;
|
||||
import cn.craftlabs.platform.api.web.dto.StakeholderRequest;
|
||||
import cn.craftlabs.platform.api.web.dto.StakeholderResponse;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
@@ -26,10 +30,12 @@ public class ProjectService {
|
||||
private static final String DEFAULT_PHASE = "PLANNING";
|
||||
|
||||
private final PlatformProjectMapper projectMapper;
|
||||
private final PlatformProjectStakeholderMapper stakeholderMapper;
|
||||
private final CustomerService customerService;
|
||||
|
||||
public ProjectService(PlatformProjectMapper projectMapper, CustomerService customerService) {
|
||||
public ProjectService(PlatformProjectMapper projectMapper, PlatformProjectStakeholderMapper stakeholderMapper, CustomerService customerService) {
|
||||
this.projectMapper = projectMapper;
|
||||
this.stakeholderMapper = stakeholderMapper;
|
||||
this.customerService = customerService;
|
||||
}
|
||||
|
||||
@@ -101,6 +107,74 @@ public class ProjectService {
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public List<StakeholderResponse> listStakeholders(long projectId) {
|
||||
LambdaQueryWrapper<PlatformProjectStakeholder> q =
|
||||
Wrappers.lambdaQuery(PlatformProjectStakeholder.class)
|
||||
.eq(PlatformProjectStakeholder::getProjectId, projectId)
|
||||
.orderByAsc(PlatformProjectStakeholder::getId);
|
||||
return stakeholderMapper.selectList(q).stream()
|
||||
.map(this::toStakeholderResponse)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public StakeholderResponse addStakeholder(long projectId, StakeholderRequest request) {
|
||||
requireProjectExists(projectId);
|
||||
PlatformProjectStakeholder s = new PlatformProjectStakeholder();
|
||||
s.setProjectId(projectId);
|
||||
s.setContactName(request.getContactName().trim());
|
||||
s.setContactRole(request.getContactRole());
|
||||
s.setPhone(request.getPhone());
|
||||
s.setEmail(request.getEmail());
|
||||
s.setIsInternal(request.getIsInternal() != null && request.getIsInternal());
|
||||
s.setCreatedAt(OffsetDateTime.now(ZoneOffset.UTC));
|
||||
stakeholderMapper.insert(s);
|
||||
return toStakeholderResponse(s);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public StakeholderResponse updateStakeholder(long stakeholderId, StakeholderRequest request) {
|
||||
PlatformProjectStakeholder s = stakeholderMapper.selectById(stakeholderId);
|
||||
if (s == null) {
|
||||
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "stakeholder not found");
|
||||
}
|
||||
s.setContactName(request.getContactName().trim());
|
||||
s.setContactRole(request.getContactRole());
|
||||
s.setPhone(request.getPhone());
|
||||
s.setEmail(request.getEmail());
|
||||
s.setIsInternal(request.getIsInternal() != null && request.getIsInternal());
|
||||
stakeholderMapper.updateById(s);
|
||||
return toStakeholderResponse(s);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void deleteStakeholder(long stakeholderId) {
|
||||
int rows = stakeholderMapper.deleteById(stakeholderId);
|
||||
if (rows == 0) {
|
||||
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "stakeholder not found");
|
||||
}
|
||||
}
|
||||
|
||||
private void requireProjectExists(long projectId) {
|
||||
if (projectMapper.selectById(projectId) == null) {
|
||||
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "project not found");
|
||||
}
|
||||
}
|
||||
|
||||
private StakeholderResponse toStakeholderResponse(PlatformProjectStakeholder s) {
|
||||
StakeholderResponse r = new StakeholderResponse();
|
||||
r.setId(s.getId());
|
||||
r.setProjectId(s.getProjectId());
|
||||
r.setContactName(s.getContactName());
|
||||
r.setContactRole(s.getContactRole());
|
||||
r.setPhone(s.getPhone());
|
||||
r.setEmail(s.getEmail());
|
||||
r.setIsInternal(s.getIsInternal());
|
||||
r.setCreatedAt(s.getCreatedAt());
|
||||
return r;
|
||||
}
|
||||
|
||||
private String resolvePhase(String phase) {
|
||||
return StringUtils.hasText(phase) ? phase.trim() : DEFAULT_PHASE;
|
||||
}
|
||||
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
package cn.craftlabs.platform.api.web.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
public class StakeholderRequest {
|
||||
|
||||
@NotBlank
|
||||
@Size(max = 128)
|
||||
private String contactName;
|
||||
|
||||
@Size(max = 64)
|
||||
private String contactRole;
|
||||
|
||||
@Size(max = 32)
|
||||
private String phone;
|
||||
|
||||
@Size(max = 128)
|
||||
private String email;
|
||||
|
||||
private Boolean isInternal;
|
||||
|
||||
public String getContactName() {
|
||||
return contactName;
|
||||
}
|
||||
|
||||
public void setContactName(String contactName) {
|
||||
this.contactName = contactName;
|
||||
}
|
||||
|
||||
public String getContactRole() {
|
||||
return contactRole;
|
||||
}
|
||||
|
||||
public void setContactRole(String contactRole) {
|
||||
this.contactRole = contactRole;
|
||||
}
|
||||
|
||||
public String getPhone() {
|
||||
return phone;
|
||||
}
|
||||
|
||||
public void setPhone(String phone) {
|
||||
this.phone = phone;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public Boolean getIsInternal() {
|
||||
return isInternal;
|
||||
}
|
||||
|
||||
public void setIsInternal(Boolean isInternal) {
|
||||
this.isInternal = isInternal;
|
||||
}
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
package cn.craftlabs.platform.api.web.dto;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
public class StakeholderResponse {
|
||||
|
||||
private Long id;
|
||||
private Long projectId;
|
||||
private String contactName;
|
||||
private String contactRole;
|
||||
private String phone;
|
||||
private String email;
|
||||
private Boolean isInternal;
|
||||
private OffsetDateTime createdAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getProjectId() {
|
||||
return projectId;
|
||||
}
|
||||
|
||||
public void setProjectId(Long projectId) {
|
||||
this.projectId = projectId;
|
||||
}
|
||||
|
||||
public String getContactName() {
|
||||
return contactName;
|
||||
}
|
||||
|
||||
public void setContactName(String contactName) {
|
||||
this.contactName = contactName;
|
||||
}
|
||||
|
||||
public String getContactRole() {
|
||||
return contactRole;
|
||||
}
|
||||
|
||||
public void setContactRole(String contactRole) {
|
||||
this.contactRole = contactRole;
|
||||
}
|
||||
|
||||
public String getPhone() {
|
||||
return phone;
|
||||
}
|
||||
|
||||
public void setPhone(String phone) {
|
||||
this.phone = phone;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public Boolean getIsInternal() {
|
||||
return isInternal;
|
||||
}
|
||||
|
||||
public void setIsInternal(Boolean isInternal) {
|
||||
this.isInternal = isInternal;
|
||||
}
|
||||
|
||||
public OffsetDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(OffsetDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
CREATE TABLE platform_project_stakeholder (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
project_id BIGINT NOT NULL REFERENCES platform_project(id),
|
||||
contact_name VARCHAR(128) NOT NULL,
|
||||
contact_role VARCHAR(64),
|
||||
phone VARCHAR(32),
|
||||
email VARCHAR(128),
|
||||
is_internal BOOLEAN DEFAULT FALSE,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
CREATE INDEX idx_stakeholder_project ON platform_project_stakeholder(project_id);
|
||||
@@ -437,3 +437,16 @@ export function updateJsonTemplate(id, body) {
|
||||
export function deleteJsonTemplate(id) {
|
||||
return axios.delete(`/api/v1/integration/json-templates/${id}`);
|
||||
}
|
||||
|
||||
export function listStakeholders(projectId) {
|
||||
return axios.get(`/api/v1/projects/${projectId}/stakeholders`);
|
||||
}
|
||||
export function addStakeholder(projectId, body) {
|
||||
return axios.post(`/api/v1/projects/${projectId}/stakeholders`, body);
|
||||
}
|
||||
export function updateStakeholder(projectId, id, body) {
|
||||
return axios.put(`/api/v1/projects/${projectId}/stakeholders/${id}`, body);
|
||||
}
|
||||
export function deleteStakeholder(projectId, id) {
|
||||
return axios.delete(`/api/v1/projects/${projectId}/stakeholders/${id}`);
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
/>
|
||||
</el-select>
|
||||
<el-button type="primary" :loading="loading" @click="load">查询</el-button>
|
||||
<el-button type="success" @click="openCreate">新建项目</el-button>
|
||||
<el-button v-permission="'project:rw'" type="success" @click="openCreate">新建项目</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -38,10 +38,11 @@
|
||||
{{ phaseLabel(row.phase) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="160" fixed="right">
|
||||
<el-table-column label="操作" width="220" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" link @click="openEdit(row)">编辑</el-button>
|
||||
<el-button type="danger" link @click="onDelete(row)">删除</el-button>
|
||||
<el-button type="primary" link @click="openStakeholderDialog(row)">干系人</el-button>
|
||||
<el-button v-permission="'project:rw'" type="primary" link @click="openEdit(row)">编辑</el-button>
|
||||
<el-button v-permission="'project:delete'" type="danger" link @click="onDelete(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -100,6 +101,50 @@
|
||||
<el-button type="primary" :loading="saving" @click="submit">保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<el-dialog v-model="stakeholderVisible" :title="stakeholderTitle" width="700px" destroy-on-close>
|
||||
<el-button type="primary" size="small" style="margin-bottom: 12px" @click="openStakeholderAdd">添加干系人</el-button>
|
||||
<el-table :data="stakeholderRows" stripe style="width: 100%">
|
||||
<el-table-column prop="contactName" label="姓名" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column prop="contactRole" label="角色" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column prop="phone" label="电话" min-width="130" />
|
||||
<el-table-column prop="email" label="邮箱" min-width="160" show-overflow-tooltip />
|
||||
<el-table-column label="内部人员" width="90">
|
||||
<template #default="{ row }">
|
||||
{{ row.isInternal ? '是' : '否' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="140" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" link @click="openStakeholderEdit(row)">编辑</el-button>
|
||||
<el-button type="danger" link @click="onStakeholderDelete(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="stakeholderFormVisible" :title="stakeholderFormTitle" width="500px" destroy-on-close @closed="resetStakeholderForm">
|
||||
<el-form ref="stakeholderFormRef" :model="stakeholderForm" :rules="stakeholderRules" label-width="100px">
|
||||
<el-form-item label="姓名" prop="contactName">
|
||||
<el-input v-model="stakeholderForm.contactName" maxlength="128" placeholder="请输入姓名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="角色" prop="contactRole">
|
||||
<el-input v-model="stakeholderForm.contactRole" maxlength="64" placeholder="请输入角色" />
|
||||
</el-form-item>
|
||||
<el-form-item label="电话" prop="phone">
|
||||
<el-input v-model="stakeholderForm.phone" maxlength="32" placeholder="请输入电话" />
|
||||
</el-form-item>
|
||||
<el-form-item label="邮箱" prop="email">
|
||||
<el-input v-model="stakeholderForm.email" maxlength="128" placeholder="请输入邮箱" />
|
||||
</el-form-item>
|
||||
<el-form-item label="内部人员">
|
||||
<el-switch v-model="stakeholderForm.isInternal" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="stakeholderFormVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="stakeholderSaving" @click="submitStakeholder">保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
@@ -114,6 +159,10 @@ import {
|
||||
updateProject,
|
||||
deleteProject,
|
||||
getProjectPhaseDictionary,
|
||||
listStakeholders,
|
||||
addStakeholder,
|
||||
updateStakeholder,
|
||||
deleteStakeholder,
|
||||
} from "../api/platform";
|
||||
import { apiErrorMessage } from "../utils/apiErrorMessage";
|
||||
|
||||
@@ -151,6 +200,128 @@ const rules = {
|
||||
|
||||
const dialogTitle = computed(() => (editingId.value ? "编辑项目" : "新建项目"));
|
||||
|
||||
// —— 干系人 ——————————————————————————————————————————
|
||||
const stakeholderVisible = ref(false);
|
||||
const stakeholderFormVisible = ref(false);
|
||||
const stakeholderSaving = ref(false);
|
||||
const stakeholderRows = ref([]);
|
||||
const stakeholderProjectId = ref(null);
|
||||
const stakeholderEditingId = ref(null);
|
||||
const stakeholderFormRef = ref(null);
|
||||
const stakeholderForm = reactive({
|
||||
contactName: "",
|
||||
contactRole: "",
|
||||
phone: "",
|
||||
email: "",
|
||||
isInternal: false,
|
||||
});
|
||||
|
||||
const stakeholderRules = {
|
||||
contactName: [{ required: true, message: "请输入姓名", trigger: "blur" }],
|
||||
};
|
||||
|
||||
const stakeholderTitle = computed(() => {
|
||||
const p = rows.value.find((r) => r.id === stakeholderProjectId.value);
|
||||
return `干系人 - ${p?.name ?? stakeholderProjectId.value}`;
|
||||
});
|
||||
|
||||
const stakeholderFormTitle = computed(() =>
|
||||
stakeholderEditingId.value ? "编辑干系人" : "添加干系人"
|
||||
);
|
||||
|
||||
function openStakeholderDialog(row) {
|
||||
stakeholderProjectId.value = row.id;
|
||||
stakeholderVisible.value = true;
|
||||
loadStakeholders();
|
||||
}
|
||||
|
||||
async function loadStakeholders() {
|
||||
try {
|
||||
const { data } = await listStakeholders(stakeholderProjectId.value);
|
||||
stakeholderRows.value = Array.isArray(data) ? data : [];
|
||||
} catch (e) {
|
||||
ElMessage.error(apiErrorMessage(e, "加载干系人列表失败"));
|
||||
stakeholderRows.value = [];
|
||||
}
|
||||
}
|
||||
|
||||
function openStakeholderAdd() {
|
||||
stakeholderEditingId.value = null;
|
||||
resetStakeholderForm();
|
||||
stakeholderFormVisible.value = true;
|
||||
}
|
||||
|
||||
function openStakeholderEdit(row) {
|
||||
stakeholderEditingId.value = row.id;
|
||||
stakeholderForm.contactName = row.contactName ?? "";
|
||||
stakeholderForm.contactRole = row.contactRole ?? "";
|
||||
stakeholderForm.phone = row.phone ?? "";
|
||||
stakeholderForm.email = row.email ?? "";
|
||||
stakeholderForm.isInternal = row.isInternal ?? false;
|
||||
stakeholderFormVisible.value = true;
|
||||
}
|
||||
|
||||
function resetStakeholderForm() {
|
||||
stakeholderForm.contactName = "";
|
||||
stakeholderForm.contactRole = "";
|
||||
stakeholderForm.phone = "";
|
||||
stakeholderForm.email = "";
|
||||
stakeholderForm.isInternal = false;
|
||||
stakeholderFormRef.value?.resetFields?.();
|
||||
}
|
||||
|
||||
async function submitStakeholder() {
|
||||
const f = stakeholderFormRef.value;
|
||||
if (!f) return;
|
||||
try {
|
||||
await f.validate();
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
stakeholderSaving.value = true;
|
||||
const payload = {
|
||||
contactName: stakeholderForm.contactName.trim(),
|
||||
contactRole: stakeholderForm.contactRole || null,
|
||||
phone: stakeholderForm.phone || null,
|
||||
email: stakeholderForm.email || null,
|
||||
isInternal: stakeholderForm.isInternal,
|
||||
};
|
||||
try {
|
||||
const pid = stakeholderProjectId.value;
|
||||
if (stakeholderEditingId.value != null) {
|
||||
await updateStakeholder(pid, stakeholderEditingId.value, payload);
|
||||
ElMessage.success("已保存");
|
||||
} else {
|
||||
await addStakeholder(pid, payload);
|
||||
ElMessage.success("已添加");
|
||||
}
|
||||
stakeholderFormVisible.value = false;
|
||||
await loadStakeholders();
|
||||
} catch (e) {
|
||||
ElMessage.error(apiErrorMessage(e, "保存失败"));
|
||||
} finally {
|
||||
stakeholderSaving.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function onStakeholderDelete(row) {
|
||||
ElMessageBox.confirm(`确定删除干系人「${row.contactName || row.id}」吗?`, "提示", {
|
||||
type: "warning",
|
||||
confirmButtonText: "删除",
|
||||
cancelButtonText: "取消",
|
||||
})
|
||||
.then(async () => {
|
||||
try {
|
||||
await deleteStakeholder(stakeholderProjectId.value, row.id);
|
||||
ElMessage.success("已删除");
|
||||
await loadStakeholders();
|
||||
} catch (e) {
|
||||
ElMessage.error(apiErrorMessage(e, "删除失败"));
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
const customerMap = computed(() => {
|
||||
const m = new Map();
|
||||
for (const c of customerOptions.value) {
|
||||
|
||||
Reference in New Issue
Block a user