Commit 41b3e2f2 by DaiJiezhang

Merge branch 'feat/device-image-and-wecom-crud' into 'master'

完善企微新增手机号信息

See merge request !12
parents 870deb9d 25564ae2
package com.xyw.console.asset.controller;
import com.xyw.console.asset.exception.PhoneAssetNotFoundException;
import com.xyw.console.asset.exception.PhoneAssetValidationException;
import com.xyw.console.common.ApiResponse;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/** 文件用途(白话):把手机号码管理的业务异常转换成前端可读的统一 JSON 错误。 */
@RestControllerAdvice(assignableTypes = PhoneAssetController.class)
public class PhoneAssetExceptionHandler {
/** 代码作用(白话):把手机号格式、ICCID 和实名人必填等校验失败转为 400。关联文件:PhoneAssetService.java。关联逻辑(调用链/数据流):Service 校验异常 -> Advice -> ApiResponse -> 弹窗提示。 */
@ExceptionHandler(PhoneAssetValidationException.class) @ResponseStatus(HttpStatus.BAD_REQUEST)
public ApiResponse<Void> validation(PhoneAssetValidationException exception) { return ApiResponse.error(400, exception.getMessage()); }
/** 代码作用(白话):把不存在的手机号码管理转为 404。关联文件:PhoneAssetService.java。关联逻辑(调用链/数据流):Service 查无记录 -> Advice -> ApiResponse -> 前端提示。 */
@ExceptionHandler(PhoneAssetNotFoundException.class) @ResponseStatus(HttpStatus.NOT_FOUND)
public ApiResponse<Void> notFound(PhoneAssetNotFoundException exception) { return ApiResponse.error(404, exception.getMessage()); }
}
......@@ -57,6 +57,10 @@ public class WecomAccountController {
@GetMapping("/lookups/phone-assets")
public ApiResponse<List<PhoneAssetLookupResponse>> phoneAssets(@RequestParam(defaultValue = "") String keyword) { permissions.require(PagePermissionService.WECOM, "READ"); return ApiResponse.success(service.searchPhoneAssets(keyword)); }
/** 代码作用(白话):查询注册手机号在手机号码管理中是否已存在,供企微表单决定是否展开新增手机号区块。关联文件:WecomAccountService.java、WecomAccountView.js。关联逻辑(调用链/数据流):手机号输入 -> GET phone-exists -> 精确查询 -> 布尔结果。 */
@GetMapping("/lookups/phone-exists")
public ApiResponse<Boolean> phoneExists(@RequestParam(defaultValue = "") String phoneNumber) { permissions.require(PagePermissionService.WECOM, "READ"); return ApiResponse.success(service.phoneExists(phoneNumber)); }
/** 代码作用(白话):按名称搜索可关联的有效设备,供表单下拉远程查询。关联文件:DeviceAssetLookupResponse.java、WecomAccountService.java。关联逻辑(调用链/数据流):下拉输入 -> GET lookup -> 设备选项。 */
@GetMapping("/lookups/devices")
public ApiResponse<List<DeviceAssetLookupResponse>> devices(@RequestParam(defaultValue = "") String keyword) { permissions.require(PagePermissionService.WECOM, "READ"); return ApiResponse.success(service.searchDevices(keyword)); }
......
......@@ -2,7 +2,7 @@ package com.xyw.console.asset.dto;
import jakarta.validation.constraints.NotBlank;
/** ???????????????????????? Controller ??????????? */
/** 代码作用(白话):限定新增和编辑时浏览器可以提交的企业微信资产字段,并携带注册手机号为新增号码时所需的手机号信息。关联文件:WecomAccountController.java、WecomAccountService.java。关联逻辑(调用链/数据流):弹窗表单 -> DTO -> Service 写入允许修改的实体字段。 */
public record WecomAccountSaveRequest(
@NotBlank String wecomName,
String wecomAlias,
......@@ -13,5 +13,8 @@ public record WecomAccountSaveRequest(
String realNameOwnerStatus,
String gender,
Long deviceId,
Long operatorPersonId) {
Long operatorPersonId,
String cardType,
String iccid,
String phoneRealNameOwner) {
}
......@@ -69,7 +69,9 @@ public class PhoneAssetService {
/** 代码作用(白话):读取一条仍有效的资产,供编辑和删除共用。关联文件:PhoneAssetMapper.java、PhoneAssetExceptionHandler.java。关联逻辑(调用链/数据流):Service 查询 -> 无记录 -> 404 异常。 */
private PhoneAssetEntity requireActiveEntity(Long id){ PhoneAssetEntity entity=mapper.selectOne(new LambdaQueryWrapper<PhoneAssetEntity>().eq(PhoneAssetEntity::getDeleteTime,0L).eq(PhoneAssetEntity::getId,id)); if(entity==null) throw new PhoneAssetNotFoundException("手机号码管理不存在或已删除"); return entity; }
/** 代码作用(白话):把表单允许提交的字段写入实体,并统一处理手机号和默认使用状态。关联文件:PhoneAssetSaveRequest.java、PhoneAssetEntity.java。关联逻辑(调用链/数据流):弹窗表单 -> DTO -> Entity -> Mapper 保存。 */
private void applyEditableFields(PhoneAssetEntity entity, PhoneAssetSaveRequest request){ entity.setPhoneNumber(normalizePhoneNumber(request.phoneNumber())); entity.setCardType(request.cardType()); entity.setIccid(request.iccid()); entity.setRealNameOwner(request.realNameOwner()); entity.setManagementType(request.managementType()); entity.setDisposalStatus(request.disposalStatus()==null||request.disposalStatus().isBlank()?"正常使用":request.disposalStatus()); entity.setDeviceId(request.deviceId()); }
private void applyEditableFields(PhoneAssetEntity entity, PhoneAssetSaveRequest request){ validateRequiredIdentityFields(request); entity.setPhoneNumber(normalizePhoneNumber(request.phoneNumber())); entity.setCardType(request.cardType()); entity.setIccid(request.iccid()); entity.setRealNameOwner(request.realNameOwner()); entity.setManagementType(request.managementType()); entity.setDisposalStatus(request.disposalStatus()==null||request.disposalStatus().isBlank()?"正常使用":request.disposalStatus()); entity.setDeviceId(request.deviceId()); }
/** 代码作用(白话):运营商为虚拟号码时实名人和 ICCID 可不填,其余运营商必须填写。关联文件:PhoneAssetSaveRequest.java、PhoneAssetValidationException.java。关联逻辑(调用链/数据流):DTO -> 校验 -> 失败抛 400 -> 前端保留弹窗并提示。 */
private void validateRequiredIdentityFields(PhoneAssetSaveRequest request){ if("虚拟号码".equals(request.cardType())) return; if(!hasText(request.iccid())) throw new PhoneAssetValidationException("ICCID 不能为空"); if(!hasText(request.realNameOwner())) throw new PhoneAssetValidationException("实名人不能为空"); }
/** 代码作用(白话):按已确认规则清理手机号并验证其最终格式。关联文件:PhoneAssetSaveRequest.java、PhoneAssetExceptionHandler.java。关联逻辑(调用链/数据流):前端输入 -> 去空格和 +86 -> 格式失败返回 400。 */
private String normalizePhoneNumber(String value){ String normalized=value==null?"":value.trim(); if(normalized.startsWith("+86")) normalized=normalized.substring(3); if(!normalized.matches("\\d{11}")) throw new PhoneAssetValidationException("手机号必须是 11 位数字"); return normalized; }
/** 代码作用(白话):只挑选列表需要展示的字段,并按传入的设备名映射补上关联设备名称。关联文件:PhoneAssetResponse.java、PhoneAssetEntity.java。关联逻辑(调用链/数据流):实体 + 设备名映射 -> DTO -> ApiResponse -> 表格。 */
......
......@@ -84,7 +84,7 @@ public class WecomAccountService {
PhoneAssetEntity phone = phoneAssetMapper.selectOne(new LambdaQueryWrapper<PhoneAssetEntity>().eq(PhoneAssetEntity::getDeleteTime, 0L).eq(PhoneAssetEntity::getPhoneNumber, phoneNumber));
boolean created = phone == null;
LocalDateTime now = LocalDateTime.now();
if (created) { phone = new PhoneAssetEntity(); phone.setPhoneNumber(phoneNumber); phone.setNumberType("EXTERNAL"); phone.setSourceAssetType("WECOM"); phone.setDisposalStatus("正常使用"); phone.setCreateTime(now); phone.setUpdateTime(now); phone.setDeleteTime(0L); phone.setLinkedWecomAccounts("[]"); phone.setLinkedWechatAccounts("[]"); phone.setLinkedDouyinAccounts("[]"); phone.setLinkedDomainAccounts("[]"); phone.setLinkedMerchants("[]"); if (phoneAssetMapper.insert(phone) != 1) throw new IllegalStateException("手机号码管理新增失败"); }
if (created) { phone = new PhoneAssetEntity(); phone.setPhoneNumber(phoneNumber); phone.setNumberType("EXTERNAL"); phone.setSourceAssetType("WECOM"); applyPhoneIdentity(phone, request); phone.setDisposalStatus("正常使用"); phone.setCreateTime(now); phone.setUpdateTime(now); phone.setDeleteTime(0L); phone.setLinkedWecomAccounts("[]"); phone.setLinkedWechatAccounts("[]"); phone.setLinkedDouyinAccounts("[]"); phone.setLinkedDomainAccounts("[]"); phone.setLinkedMerchants("[]"); if (phoneAssetMapper.insert(phone) != 1) throw new IllegalStateException("手机号码管理新增失败"); }
WecomAccountEntity entity = new WecomAccountEntity(); applyEditableFields(entity, request); entity.setPhoneAssetId(phone.getId()); entity.setPhoneLinkMode(created ? "CREATED" : "EXISTING"); entity.setCreateTime(now); entity.setUpdateTime(now); entity.setDeleteTime(0L); if (wecomAccountMapper.insert(entity) != 1) throw new IllegalStateException("企业微信资产新增失败"); if (created) { phone.setSourceAssetId(entity.getId()); phoneAssetMapper.updateById(phone); } return toResponse(entity, Map.of(), Map.of(phone.getId(), phone.getPhoneNumber()), Map.of(), Map.of());
}
......@@ -104,7 +104,7 @@ public class WecomAccountService {
LocalDateTime now = LocalDateTime.now();
// 号码没变就不动关联,避免把 EXISTING 误改成 CREATED、或把还在用的号码当成旧号清理掉。
if (!phoneNumber.equals(currentPhoneNumber(previousPhoneAssetId))) {
PhoneAssetEntity phone = attachPhone(phoneNumber, entity.getId(), now);
PhoneAssetEntity phone = attachPhone(phoneNumber, entity.getId(), now, request);
entity.setPhoneAssetId(phone.getId());
entity.setPhoneLinkMode(isCreatedFor(phone, entity.getId()) ? "CREATED" : "EXISTING");
releaseAutoCreatedPhone(previousPhoneAssetId, previousLinkMode, entity.getId(), now);
......@@ -149,11 +149,11 @@ public class WecomAccountService {
* 关联文件:PhoneAssetEntity.java、PhoneAssetMapper.java。
* 关联逻辑(调用链/数据流):号码 -> 查已有 -> 命中则复用,未命中则新建 EXTERNAL/WECOM 记录。
*/
private PhoneAssetEntity attachPhone(String phoneNumber, Long wecomAccountId, LocalDateTime now) {
private PhoneAssetEntity attachPhone(String phoneNumber, Long wecomAccountId, LocalDateTime now, WecomAccountSaveRequest request) {
PhoneAssetEntity phone = phoneAssetMapper.selectOne(new LambdaQueryWrapper<PhoneAssetEntity>().eq(PhoneAssetEntity::getDeleteTime, 0L).eq(PhoneAssetEntity::getPhoneNumber, phoneNumber));
if (phone != null) return phone;
phone = new PhoneAssetEntity();
phone.setPhoneNumber(phoneNumber); phone.setNumberType("EXTERNAL"); phone.setSourceAssetType("WECOM"); phone.setSourceAssetId(wecomAccountId); phone.setDisposalStatus("正常使用");
phone.setPhoneNumber(phoneNumber); phone.setNumberType("EXTERNAL"); phone.setSourceAssetType("WECOM"); phone.setSourceAssetId(wecomAccountId); applyPhoneIdentity(phone, request); phone.setDisposalStatus("正常使用");
phone.setCreateTime(now); phone.setUpdateTime(now); phone.setDeleteTime(0L);
phone.setLinkedWecomAccounts("[]"); phone.setLinkedWechatAccounts("[]"); phone.setLinkedDouyinAccounts("[]"); phone.setLinkedDomainAccounts("[]"); phone.setLinkedMerchants("[]");
if (phoneAssetMapper.insert(phone) != 1) throw new IllegalStateException("手机号码管理新增失败");
......@@ -201,6 +201,21 @@ public class WecomAccountService {
entity.setGender(request.gender()); entity.setDeviceId(request.deviceId()); entity.setOperatorPersonId(request.operatorPersonId());
}
/** 代码作用(白话):企微为新增注册手机号自动建号时,把运营商、ICCID 和手机号实名人写入号码资产,并套用与手机号码管理一致的必填规则(运营商为虚拟号码时 ICCID 和手机号实名人可不填)。关联文件:PhoneAssetEntity.java、PhoneAssetValidationException.java。关联逻辑(调用链/数据流):企微建号分支 -> 字段写入 -> 校验失败抛 400 -> 前端提示。 */
private void applyPhoneIdentity(PhoneAssetEntity phone, WecomAccountSaveRequest request) {
phone.setCardType(request.cardType());
phone.setIccid(request.iccid());
phone.setRealNameOwner(request.phoneRealNameOwner());
if ("虚拟号码".equals(request.cardType())) return;
if (!hasText(request.iccid())) throw new PhoneAssetValidationException("ICCID 不能为空");
if (!hasText(request.phoneRealNameOwner())) throw new PhoneAssetValidationException("手机号实名人不能为空");
}
/** 代码作用(白话):判断指定号码在手机号码管理中是否存在(未删除),供企微表单决定是否展开新增手机号区块。关联文件:WecomAccountController.java、PhoneAssetMapper.java。关联逻辑(调用链/数据流):手机号输入 -> GET phone-exists -> 精确查询 -> 布尔结果。 */
public boolean phoneExists(String phoneNumber) {
return phoneAssetMapper.selectCount(new LambdaQueryWrapper<PhoneAssetEntity>().eq(PhoneAssetEntity::getDeleteTime, 0L).eq(PhoneAssetEntity::getPhoneNumber, phoneNumber)) > 0;
}
/** 代码作用(白话):确认选中的关联设备仍然有效,阻止伪造或已删除的设备 ID 写入。关联文件:AssetDeviceMapper.java、WecomAccountSaveRequest.java。关联逻辑(调用链/数据流):表单 deviceId -> 有效性查询 -> 参数错误或继续保存。 */
private void requireActiveDevice(Long deviceId) {
if (deviceId == null) return;
......
......@@ -36,7 +36,7 @@ class PhoneAssetControllerTest {
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new PhoneAssetController(new PhoneAssetService(mapper, mock(AssetDeviceMapper.class)), mock(PagePermissionService.class))).build();
mockMvc.perform(post("/api/phone-assets").contentType(MediaType.APPLICATION_JSON)
.content("{\"phoneNumber\":\"13812345678\",\"realNameOwner\":\"张三\"}"))
.content("{\"phoneNumber\":\"13812345678\",\"cardType\":\"移动\",\"iccid\":\"测试 ICCID\",\"realNameOwner\":\"张三\"}"))
.andExpect(status().isOk());
}
......@@ -55,7 +55,7 @@ class PhoneAssetControllerTest {
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new PhoneAssetController(new PhoneAssetService(mapper, mock(AssetDeviceMapper.class)), mock(PagePermissionService.class))).build();
mockMvc.perform(put("/api/phone-assets/1").contentType(MediaType.APPLICATION_JSON)
.content("{\"phoneNumber\":\"13912345678\",\"realNameOwner\":\"李四\"}"))
.content("{\"phoneNumber\":\"13912345678\",\"cardType\":\"移动\",\"iccid\":\"测试 ICCID\",\"realNameOwner\":\"李四\"}"))
.andExpect(status().isOk());
}
......
......@@ -60,7 +60,7 @@ class WecomAccountControllerTest {
.when(permissions).require(PagePermissionService.WECOM, "EDIT");
WecomAccountController controller = new WecomAccountController(service, permissions);
org.junit.jupiter.api.Assertions.assertThrows(org.springframework.security.access.AccessDeniedException.class,
() -> controller.update(1L, new com.xyw.console.asset.dto.WecomAccountSaveRequest("名称", null, null, null, "13812345678", null, null, null, null, null)));
() -> controller.update(1L, new com.xyw.console.asset.dto.WecomAccountSaveRequest("名称", null, null, null, "13812345678", null, null, null, null, null, null, null, null)));
org.junit.jupiter.api.Assertions.assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> controller.delete(1L));
org.mockito.Mockito.verifyNoInteractions(service);
}
......
......@@ -193,7 +193,7 @@ class WecomAccountServiceTest {
void createsWithAnExistingPhoneAsset() {
WecomAccountMapper wecomMapper = mock(WecomAccountMapper.class); PhoneAssetMapper phoneMapper = mock(PhoneAssetMapper.class);
PhoneAssetEntity phone = phoneAsset(); when(phoneMapper.selectOne(any())).thenReturn(phone); when(wecomMapper.insert(any(WecomAccountEntity.class))).thenReturn(1);
WecomAccountResponse result = service(wecomMapper, mock(CompanyProfileMapper.class), phoneMapper, mock(AssetDeviceMapper.class), mock(CompanyPersonMapper.class)).create(new WecomAccountSaveRequest("测试企微", "别名", null, null, "13812345678", null, null, null, null, null));
WecomAccountResponse result = service(wecomMapper, mock(CompanyProfileMapper.class), phoneMapper, mock(AssetDeviceMapper.class), mock(CompanyPersonMapper.class)).create(new WecomAccountSaveRequest("测试企微", "别名", null, null, "13812345678", null, null, null, null, null, null, null, null));
assertEquals(20L, result.phoneAssetId()); assertEquals("EXISTING", result.phoneLinkMode());
}
......@@ -202,7 +202,7 @@ class WecomAccountServiceTest {
void createsAnExternalPhoneAssetForANewNumber() {
WecomAccountMapper wecomMapper = mock(WecomAccountMapper.class); PhoneAssetMapper phoneMapper = mock(PhoneAssetMapper.class);
when(phoneMapper.selectOne(any())).thenReturn(null); when(phoneMapper.insert(any(PhoneAssetEntity.class))).thenAnswer(call -> { ((PhoneAssetEntity) call.getArgument(0)).setId(70L); return 1; }); when(wecomMapper.insert(any(WecomAccountEntity.class))).thenAnswer(call -> { ((WecomAccountEntity) call.getArgument(0)).setId(80L); return 1; });
WecomAccountResponse result = service(wecomMapper, mock(CompanyProfileMapper.class), phoneMapper, mock(AssetDeviceMapper.class), mock(CompanyPersonMapper.class)).create(new WecomAccountSaveRequest("测试企微", null, null, null, "13912345678", null, null, null, null, null));
WecomAccountResponse result = service(wecomMapper, mock(CompanyProfileMapper.class), phoneMapper, mock(AssetDeviceMapper.class), mock(CompanyPersonMapper.class)).create(new WecomAccountSaveRequest("测试企微", null, null, null, "13912345678", null, null, null, null, null, "移动", "测试 ICCID", "测试实名人"));
assertEquals("CREATED", result.phoneLinkMode()); verify(phoneMapper).updateById(any(PhoneAssetEntity.class));
}
......@@ -221,7 +221,7 @@ class WecomAccountServiceTest {
WecomAccountService service = service(wecomMapper, companyProfileMapper, phoneMapper, mock(AssetDeviceMapper.class), mock(CompanyPersonMapper.class));
IllegalArgumentException error = assertThrows(IllegalArgumentException.class, () -> service.create(
new WecomAccountSaveRequest("测试企微", null, null, 99L, "13812345678", null, null, null, null, null)));
new WecomAccountSaveRequest("测试企微", null, null, 99L, "13812345678", null, null, null, null, null, null, null, null)));
assertEquals("注册主体不存在或已删除", error.getMessage());
}
......@@ -363,6 +363,6 @@ class WecomAccountServiceTest {
/** 代码作用(白话):构造一份只关心手机号的编辑表单。关联文件:WecomAccountSaveRequest.java。关联逻辑(调用链/数据流):测试 -> DTO -> Service.update。 */
private WecomAccountSaveRequest saveRequest(String phoneNumber) {
return new WecomAccountSaveRequest("编辑后的企微", "别名", "account", null, phoneNumber, "张三", "在职", "男", null, null);
return new WecomAccountSaveRequest("编辑后的企微", "别名", "account", null, phoneNumber, "张三", "在职", "男", null, null, "移动", "测试 ICCID", "测试实名人");
}
}
......@@ -222,6 +222,15 @@ export default {
* 关联逻辑(调用链/数据流):确认保存 -> POST/PUT -> 成功提示 -> dialogVisible=false -> loadPage。
*/
async function submitForm() {
// 运营商为虚拟号码时实名人/ICCID 可不填,其余运营商必须填写
if (form.cardType !== '虚拟号码' && (!form.iccid || !form.iccid.trim())) {
ElMessage.warning('请填写 ICCID');
return;
}
if (form.cardType !== '虚拟号码' && (!form.realNameOwner || !form.realNameOwner.trim())) {
ElMessage.warning('请填写实名人');
return;
}
saving.value = true;
try {
const payload = { ...form, phoneNumber: normalizePhoneNumber(form.phoneNumber) };
......@@ -331,11 +340,11 @@ export default {
<template #suffix><span class="phone-asset-modal__character-count" aria-live="polite">{{ form.phoneNumber.length }}/11</span></template>
</el-input>
</el-form-item>
<el-form-item class="phone-asset-modal__form-row" label="实名人"><el-input v-model="form.realNameOwner" maxlength="20" placeholder="请输入实名人" /></el-form-item>
<el-form-item class="phone-asset-modal__form-row" label="实名人" :required="form.cardType !== '虚拟号码'"><el-input v-model="form.realNameOwner" maxlength="20" placeholder="请输入实名人" /></el-form-item>
<el-form-item class="phone-asset-modal__form-row" label="运营商">
<el-select v-model="form.cardType" clearable placeholder="请选择" popper-class="phone-asset-modal__select-popper"><el-option label="移动" value="移动" /><el-option label="电信" value="电信" /><el-option label="联通" value="联通" /><el-option label="电销卡「联通」" value="电销卡「联通」" /><el-option label="广电" value="广电" /><el-option label="虚拟号码" value="虚拟号码" /></el-select>
</el-form-item>
<el-form-item class="phone-asset-modal__form-row" label="ICCID">
<el-form-item class="phone-asset-modal__form-row" label="ICCID" :required="form.cardType !== '虚拟号码'">
<el-input v-model="form.iccid" class="phone-asset-modal__count-input" maxlength="20" autocomplete="off" placeholder="请输入 ICCID" @input="limitIccid" @paste="handleIccidPaste">
<template #suffix><span class="phone-asset-modal__character-count" aria-live="polite">{{ form.iccid.length }}/20</span></template>
</el-input>
......
......@@ -32,6 +32,9 @@ export function searchCompanyProfiles(keyword) { return request(`/api/wecom-acco
/** Code purpose (plain language): searches existing phone assets for the registration-phone input. Related files: WecomAccountView.js, WecomAccountController.java. Data flow: autocomplete -> GET lookup -> selectable numbers. */
export function searchPhoneAssets(keyword) { return request(`/api/wecom-accounts/lookups/phone-assets?keyword=${encodeURIComponent(keyword || '')}`); }
/** 代码作用(白话):查询注册手机号在手机号码管理中是否已存在。关联文件:WecomAccountView.js、WecomAccountController.java。关联逻辑(调用链/数据流):手机号输入 -> GET phone-exists -> 布尔结果 -> 决定是否展开新增手机号区块。 */
export function checkPhoneExists(phoneNumber) { return request(`/api/wecom-accounts/lookups/phone-exists?phoneNumber=${encodeURIComponent(phoneNumber || '')}`); }
/** Code purpose (plain language): searches optional WeCom owners. Related files: WecomAccountView.js, WecomAccountController.java. Data flow: remote select -> GET lookup -> options. */
export function searchCompanyPersons(keyword) { return request(`/api/wecom-accounts/lookups/company-persons?keyword=${encodeURIComponent(keyword || '')}`); }
......
......@@ -51,8 +51,10 @@ body { margin: 0; }
}
.phone-asset-modal {
width: min(560px, 100%) !important;
max-height: calc(100vh - 48px);
height: min(680px, calc(100vh - 48px));
margin: 0 !important;
display: flex;
flex-direction: column;
overflow: hidden;
border: 1px solid rgba(228, 228, 231, 0.9);
border-radius: 12px;
......@@ -96,15 +98,44 @@ body { margin: 0; }
height: 15px;
}
.phone-asset-modal .el-dialog__body {
max-height: calc(100vh - 206px);
flex: 1 1 auto;
min-height: 0;
padding: 4px 24px 8px;
overflow-y: auto;
}
/* 弹窗正文的滚动条缩窄并去掉轨道色,使其融入表单背景。 */
@media (min-width: 601px) {
.phone-asset-modal .el-dialog__body {
scrollbar-width: thin;
scrollbar-color: #d4d4d8 transparent;
}
.phone-asset-modal .el-dialog__body::-webkit-scrollbar {
width: 6px;
}
.phone-asset-modal .el-dialog__body::-webkit-scrollbar-track {
background: transparent;
}
.phone-asset-modal .el-dialog__body::-webkit-scrollbar-thumb {
border-radius: 999px;
background: #d4d4d8;
}
.phone-asset-modal .el-dialog__body::-webkit-scrollbar-thumb:hover {
background: #a1a1aa;
}
}
.phone-asset-modal__form {
display: grid;
gap: 14px;
}
/* 企微新增注册手机号为系统不存在的号码时,"新增手机号"区块的分隔标题 */
.phone-asset-modal__form-divider {
margin: 4px 0 0;
padding-top: 12px;
border-top: 1px dashed #e4e4e7;
color: #52525b;
font-size: 13px;
font-weight: 600;
}
.phone-asset-modal__form-row.el-form-item {
display: grid;
grid-template-columns: 96px minmax(0, 1fr);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment