Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
X
xyw_asset_console
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
1
Merge Requests
1
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
DaiJiezhang
xyw_asset_console
Commits
45443f00
Commit
45443f00
authored
Aug 01, 2026
by
DaiJiezhang
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: refine phone asset list management
parent
ec930cf8
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
95 additions
and
90 deletions
+95
-90
PhoneAssetPageQuery.java
...n/java/com/xyw/console/asset/dto/PhoneAssetPageQuery.java
+15
-5
PhoneAssetService.java
...java/com/xyw/console/asset/service/PhoneAssetService.java
+29
-16
PhoneAssetView.js
frontend/src/modules/phone/PhoneAssetView.js
+34
-67
app.css
frontend/src/styles/app.css
+17
-2
No files found.
backend/src/main/java/com/xyw/console/asset/dto/PhoneAssetPageQuery.java
View file @
45443f00
package
com
.
xyw
.
console
.
asset
.
dto
;
package
com
.
xyw
.
console
.
asset
.
dto
;
import
jakarta.validation.constraints.Max
;
import
jakarta.validation.constraints.Max
;
import
jakarta.validation.constraints.Min
;
import
jakarta.validation.constraints.Min
;
public
record
PhoneAssetPageQuery
(
@Min
(
1
)
Integer
page
,
@Min
(
1
)
@Max
(
100
)
Integer
size
,
String
phoneNumber
,
String
iccid
)
{
/** 代码作用(白话):在调用方未传分页参数时提供安全默认值。关联文件:PhoneAssetController.java。关联逻辑(调用链/数据流):HTTP 查询参数 -> 默认值 -> Service Page。 */
/** 文件用途(白话):接收手机号资产列表的分页和筛选条件,不改变新增、编辑接口的数据结构。 */
public
int
resolvedPage
(){
return
page
==
null
?
1
:
page
;
}
public
record
PhoneAssetPageQuery
(
/** 代码作用(白话):限制每页返回数量,防止一次取走过多数据。关联文件:PhoneAssetService.java。关联逻辑(调用链/数据流):HTTP 查询参数 -> 默认值 -> Mapper limit。 */
@Min
(
1
)
Integer
page
,
public
int
resolvedSize
(){
return
size
==
null
?
20
:
size
;
}
@Min
(
1
)
@Max
(
100
)
Integer
size
,
String
phoneNumber
,
String
iccid
,
String
realNameOwner
,
String
disposalStatus
)
{
/** 代码作用(白话):在调用方未传页码时提供安全默认值。关联文件:PhoneAssetController.java。关联逻辑(调用链/数据流):HTTP 查询参数 -> 默认页码 -> Service 分页查询。 */
public
int
resolvedPage
()
{
return
page
==
null
?
1
:
page
;
}
/** 代码作用(白话):限制每页返回数量,防止一次取走过多数据。关联文件:PhoneAssetService.java。关联逻辑(调用链/数据流):HTTP 查询参数 -> 默认页大小 -> Mapper limit。 */
public
int
resolvedSize
()
{
return
size
==
null
?
20
:
size
;
}
}
}
\ No newline at end of file
backend/src/main/java/com/xyw/console/asset/service/PhoneAssetService.java
View file @
45443f00
package
com
.
xyw
.
console
.
asset
.
service
;
package
com
.
xyw
.
console
.
asset
.
service
;
import
com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper
;
import
com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper
;
import
com.baomidou.mybatisplus.extension.plugins.pagination.Page
;
import
com.baomidou.mybatisplus.extension.plugins.pagination.Page
;
import
com.xyw.console.asset.dto.*
;
import
com.xyw.console.asset.dto.*
;
...
@@ -15,29 +16,41 @@ public class PhoneAssetService {
...
@@ -15,29 +16,41 @@ public class PhoneAssetService {
private
final
PhoneAssetMapper
mapper
;
private
final
PhoneAssetMapper
mapper
;
/** 代码作用(白话):接收手机号资产表的数据库访问入口。关联文件:PhoneAssetMapper.java、PhoneAssetController.java。关联逻辑(调用链/数据流):Controller -> Service -> Mapper -> as_phone_asset。 */
/** 代码作用(白话):接收手机号资产表的数据库访问入口。关联文件:PhoneAssetMapper.java、PhoneAssetController.java。关联逻辑(调用链/数据流):Controller -> Service -> Mapper -> as_phone_asset。 */
public
PhoneAssetService
(
PhoneAssetMapper
mapper
)
{
this
.
mapper
=
mapper
;
}
public
PhoneAssetService
(
PhoneAssetMapper
mapper
)
{
this
.
mapper
=
mapper
;
}
/** 代码作用(白话):
查询未删除手机号资产并转换成前端列表字段。关联文件:PhoneAssetController.java、PhoneAssetMapper.java。关联逻辑(调用链/数据流):GET 请求 -> page -> Mapper -> as_phone_asset
-> Response。 */
/** 代码作用(白话):
按分页、手机号片段、ICCID、实名人和使用状态读取未删除资产。关联文件:PhoneAssetPageQuery.java、PhoneAssetController.java。关联逻辑(调用链/数据流):GET 参数 -> 查询条件 -> Mapper
-> Response。 */
public
PhoneAssetPageResponse
page
(
PhoneAssetPageQuery
query
)
{
public
PhoneAssetPageResponse
page
(
PhoneAssetPageQuery
query
)
{
Page
<
PhoneAssetEntity
>
page
=
mapper
.
selectPage
(
new
Page
<>(
query
.
resolvedPage
(),
query
.
resolvedSize
()),
activeQuery
(
).
eq
(
query
.
phoneNumber
()!=
null
&&!
query
.
phoneNumber
().
isBlank
(),
PhoneAssetEntity:
:
getPhoneNumber
,
query
.
phoneNumber
()).
eq
(
query
.
iccid
()!=
null
&&!
query
.
iccid
().
isBlank
(),
PhoneAssetEntity:
:
getIccid
,
query
.
iccid
()).
orderByDesc
(
PhoneAssetEntity:
:
getId
));
Page
<
PhoneAssetEntity
>
page
=
mapper
.
selectPage
(
new
Page
<>(
query
.
resolvedPage
(),
query
.
resolvedSize
()),
activeQuery
(
query
));
List
<
PhoneAssetResponse
>
records
=
page
.
getRecords
().
stream
().
map
(
this
::
toResponse
).
toList
();
List
<
PhoneAssetResponse
>
records
=
page
.
getRecords
().
stream
().
map
(
this
::
toResponse
).
toList
();
return
new
PhoneAssetPageResponse
(
records
,
page
.
getTotal
(),
query
.
resolvedPage
(),
query
.
resolvedSize
());
return
new
PhoneAssetPageResponse
(
records
,
page
.
getTotal
(),
query
.
resolvedPage
(),
query
.
resolvedSize
());
}
}
/** 代码作用(白话):新增手机号资产并初始化审计字段和内部关联快照。关联文件:PhoneAssetSaveRequest.java、PhoneAssetMapper.java、PhoneAssetController.java。关联逻辑(调用链/数据流):POST 请求 -> DTO -> Service.create -> Mapper.insert -> as_phone_asset。 */
/** 代码作用(白话):组合现有资产筛选条件,保证旧的完整号码与 ICCID 参数继续可用。关联文件:PhoneAssetPageQuery.java、PhoneAssetEntity.java。关联逻辑(调用链/数据流):Query DTO -> LambdaQueryWrapper -> SQL where 条件。 */
public
PhoneAssetResponse
create
(
PhoneAssetSaveRequest
request
)
{
private
LambdaQueryWrapper
<
PhoneAssetEntity
>
activeQuery
(
PhoneAssetPageQuery
query
)
{
PhoneAssetEntity
entity
=
new
PhoneAssetEntity
();
applyEditableFields
(
entity
,
request
);
LocalDateTime
now
=
LocalDateTime
.
now
();
entity
.
setCreateTime
(
now
);
entity
.
setUpdateTime
(
now
);
entity
.
setDeleteTime
(
0L
);
entity
.
setLinkedWecomAccounts
(
"[]"
);
entity
.
setLinkedWechatAccounts
(
"[]"
);
entity
.
setLinkedDouyinAccounts
(
"[]"
);
entity
.
setLinkedDomainAccounts
(
"[]"
);
entity
.
setLinkedMerchants
(
"[]"
);
if
(
mapper
.
insert
(
entity
)!=
1
)
throw
new
IllegalStateException
(
"手机号资产新增失败"
);
return
toResponse
(
entity
);
LambdaQueryWrapper
<
PhoneAssetEntity
>
wrapper
=
new
LambdaQueryWrapper
<
PhoneAssetEntity
>().
eq
(
PhoneAssetEntity:
:
getDeleteTime
,
0L
);
}
applyPhoneNumberFilter
(
wrapper
,
query
.
phoneNumber
());
/** 代码作用(白话):更新有效资产的用户可写字段。关联文件:PhoneAssetSaveRequest.java、PhoneAssetMapper.java、PhoneAssetController.java。关联逻辑(调用链/数据流):PUT 请求 -> Service 查找 -> Mapper.updateById -> Response。 */
wrapper
.
eq
(
hasText
(
query
.
iccid
()),
PhoneAssetEntity:
:
getIccid
,
query
.
iccid
())
public
PhoneAssetResponse
update
(
Long
id
,
PhoneAssetSaveRequest
request
)
{
.
like
(
hasText
(
query
.
realNameOwner
()),
PhoneAssetEntity:
:
getRealNameOwner
,
query
.
realNameOwner
())
PhoneAssetEntity
entity
=
requireActiveEntity
(
id
);
applyEditableFields
(
entity
,
request
);
entity
.
setUpdateTime
(
LocalDateTime
.
now
());
if
(
mapper
.
updateById
(
entity
)!=
1
)
throw
new
PhoneAssetNotFoundException
(
"手机号资产不存在或已删除"
);
return
toResponse
(
entity
);
.
eq
(
hasText
(
query
.
disposalStatus
()),
PhoneAssetEntity:
:
getDisposalStatus
,
query
.
disposalStatus
())
.
orderByDesc
(
PhoneAssetEntity:
:
getId
);
return
wrapper
;
}
}
/** 代码作用(白话):将无关联检查的资产标记为删除,供 Controller 删除接口调用。关联文件:PhoneAssetController.java、PhoneAssetMapper.java。关联逻辑(调用链/数据流):DELETE 请求 -> Service -> Mapper.updateById -> delete_time 更新。 */
/** 代码作用(白话):按 3、4、7、11 位手机号规则追加查询条件,其余长度保持原有精确查询。关联文件:PhoneAssetPageQuery.java、PhoneAssetView.js。关联逻辑(调用链/数据流):搜索输入 -> phoneNumber 参数 -> 前缀/后缀/精确 SQL 匹配。 */
public
void
softDelete
(
Long
id
)
{
private
void
applyPhoneNumberFilter
(
LambdaQueryWrapper
<
PhoneAssetEntity
>
wrapper
,
String
phoneNumber
)
{
PhoneAssetEntity
entity
=
requireActiveEntity
(
id
);
entity
.
setDeleteTime
(
System
.
currentTimeMillis
());
entity
.
setUpdateTime
(
LocalDateTime
.
now
());
if
(
mapper
.
updateById
(
entity
)!=
1
)
throw
new
PhoneAssetNotFoundException
(
"手机号资产不存在或已删除"
);
if
(!
hasText
(
phoneNumber
))
return
;
if
(
phoneNumber
.
length
()
==
3
)
wrapper
.
likeRight
(
PhoneAssetEntity:
:
getPhoneNumber
,
phoneNumber
);
else
if
(
phoneNumber
.
length
()
==
4
)
wrapper
.
likeLeft
(
PhoneAssetEntity:
:
getPhoneNumber
,
phoneNumber
);
else
if
(
phoneNumber
.
length
()
==
7
)
wrapper
.
likeRight
(
PhoneAssetEntity:
:
getPhoneNumber
,
phoneNumber
.
substring
(
0
,
3
)).
likeLeft
(
PhoneAssetEntity:
:
getPhoneNumber
,
phoneNumber
.
substring
(
3
));
else
wrapper
.
eq
(
PhoneAssetEntity:
:
getPhoneNumber
,
phoneNumber
);
}
}
/** 代码作用(白话):构造只包含未删除资产的查询条件。关联文件:PhoneAssetEntity.java、PhoneAssetMapper.java。关联逻辑(调用链/数据流):Service 查询 -> delete_time = 0 -> Mapper。 */
/** 代码作用(白话):判断筛选文本是否有内容,避免空字符串参与数据库筛选。关联文件:PhoneAssetPageQuery.java、PhoneAssetService.java。关联逻辑(调用链/数据流):请求参数 -> hasText -> 是否追加条件。 */
private
LambdaQueryWrapper
<
PhoneAssetEntity
>
activeQuery
(){
return
new
LambdaQueryWrapper
<
PhoneAssetEntity
>().
eq
(
PhoneAssetEntity:
:
getDeleteTime
,
0L
);
}
private
boolean
hasText
(
String
value
)
{
return
value
!=
null
&&
!
value
.
isBlank
();
}
/** 代码作用(白话):新增手机号资产并初始化审计字段和内部关联快照。关联文件:PhoneAssetSaveRequest.java、PhoneAssetMapper.java、PhoneAssetController.java。关联逻辑(调用链/数据流):POST 请求 -> DTO -> Service.create -> Mapper.insert -> as_phone_asset。 */
public
PhoneAssetResponse
create
(
PhoneAssetSaveRequest
request
)
{
PhoneAssetEntity
entity
=
new
PhoneAssetEntity
();
applyEditableFields
(
entity
,
request
);
LocalDateTime
now
=
LocalDateTime
.
now
();
entity
.
setCreateTime
(
now
);
entity
.
setUpdateTime
(
now
);
entity
.
setDeleteTime
(
0L
);
entity
.
setLinkedWecomAccounts
(
"[]"
);
entity
.
setLinkedWechatAccounts
(
"[]"
);
entity
.
setLinkedDouyinAccounts
(
"[]"
);
entity
.
setLinkedDomainAccounts
(
"[]"
);
entity
.
setLinkedMerchants
(
"[]"
);
if
(
mapper
.
insert
(
entity
)!=
1
)
throw
new
IllegalStateException
(
"手机号资产新增失败"
);
return
toResponse
(
entity
);
}
/** 代码作用(白话):更新有效资产的用户可写字段。关联文件:PhoneAssetSaveRequest.java、PhoneAssetMapper.java、PhoneAssetController.java。关联逻辑(调用链/数据流):PUT 请求 -> Service 查找 -> Mapper.updateById -> Response。 */
public
PhoneAssetResponse
update
(
Long
id
,
PhoneAssetSaveRequest
request
)
{
PhoneAssetEntity
entity
=
requireActiveEntity
(
id
);
applyEditableFields
(
entity
,
request
);
entity
.
setUpdateTime
(
LocalDateTime
.
now
());
if
(
mapper
.
updateById
(
entity
)!=
1
)
throw
new
PhoneAssetNotFoundException
(
"手机号资产不存在或已删除"
);
return
toResponse
(
entity
);
}
/** 代码作用(白话):将资产标记为删除,供 Controller 删除接口调用。关联文件:PhoneAssetController.java、PhoneAssetMapper.java。关联逻辑(调用链/数据流):DELETE 请求 -> Service -> Mapper.updateById。 */
public
void
softDelete
(
Long
id
)
{
PhoneAssetEntity
entity
=
requireActiveEntity
(
id
);
entity
.
setDeleteTime
(
System
.
currentTimeMillis
());
entity
.
setUpdateTime
(
LocalDateTime
.
now
());
if
(
mapper
.
updateById
(
entity
)!=
1
)
throw
new
PhoneAssetNotFoundException
(
"手机号资产不存在或已删除"
);
}
/** 代码作用(白话):读取一条仍有效的资产,供编辑和删除共用。关联文件:PhoneAssetMapper.java、PhoneAssetExceptionHandler.java。关联逻辑(调用链/数据流):Service 查询 -> 无记录 -> 404 异常。 */
/** 代码作用(白话):读取一条仍有效的资产,供编辑和删除共用。关联文件:PhoneAssetMapper.java、PhoneAssetExceptionHandler.java。关联逻辑(调用链/数据流):Service 查询 -> 无记录 -> 404 异常。 */
private
PhoneAssetEntity
requireActiveEntity
(
Long
id
){
PhoneAssetEntity
entity
=
mapper
.
selectOne
(
activeQuery
(
).
eq
(
PhoneAssetEntity:
:
getId
,
id
));
if
(
entity
==
null
)
throw
new
PhoneAssetNotFoundException
(
"手机号资产不存在或已删除"
);
return
entity
;
}
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 保存。 */
/** 代码作用(白话):把表单允许提交的字段写入实体,并统一处理手机号和默认
使用
状态。关联文件: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
){
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
());
}
/** 代码作用(白话):按已确认规则清理手机号并验证其最终格式。关联文件:PhoneAssetSaveRequest.java、PhoneAssetExceptionHandler.java。关联逻辑(调用链/数据流):前端输入 -> 去空格和 +86 -> 格式失败返回 400。 */
/** 代码作用(白话):按已确认规则清理手机号并验证其最终格式。关联文件: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
;
}
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
;
}
...
...
frontend/src/modules/phone/PhoneAssetView.js
View file @
45443f00
import
{
nextTick
,
onMounted
,
reactive
,
ref
}
from
'vue/dist/vue.esm-bundler.js'
;
import
{
nextTick
,
onMounted
,
reactive
,
ref
}
from
'vue/dist/vue.esm-bundler.js'
;
import
{
ElMessage
,
ElMessageBox
}
from
'element-plus'
;
import
{
ElMessage
,
ElMessageBox
}
from
'element-plus'
;
import
zhCn
from
'element-plus/es/locale/lang/zh-cn.mjs'
;
import
{
createPhoneAsset
,
deletePhoneAsset
,
listPhoneAssets
,
updatePhoneAsset
}
from
'./phone-api-client.js'
;
import
{
createPhoneAsset
,
deletePhoneAsset
,
listPhoneAssets
,
updatePhoneAsset
}
from
'./phone-api-client.js'
;
/**
/**
...
@@ -14,13 +15,16 @@ export default {
...
@@ -14,13 +15,16 @@ export default {
* 关联逻辑(调用链/数据流):路由进入 -> setup 初始化 -> 用户操作 -> 接口调用或弹窗状态变化。
* 关联逻辑(调用链/数据流):路由进入 -> setup 初始化 -> 用户操作 -> 接口调用或弹窗状态变化。
*/
*/
setup
()
{
setup
()
{
const
elementLocale
=
zhCn
;
const
loading
=
ref
(
false
);
const
loading
=
ref
(
false
);
const
saving
=
ref
(
false
);
const
saving
=
ref
(
false
);
const
records
=
ref
([]);
const
records
=
ref
([]);
const
total
=
ref
(
0
);
const
total
=
ref
(
0
);
const
dialogVisible
=
ref
(
false
);
const
dialogVisible
=
ref
(
false
);
const
editingId
=
ref
(
null
);
const
editingId
=
ref
(
null
);
const
filters
=
reactive
({
page
:
1
,
size
:
20
,
phoneNumber
:
''
,
iccid
:
''
});
const
filters
=
reactive
({
page
:
1
,
size
:
20
,
phoneNumber
:
''
,
iccid
:
''
,
realNameOwner
:
''
,
disposalStatus
:
''
});
let
searchTimer
=
null
;
let
latestRequest
=
0
;
const
form
=
reactive
({
const
form
=
reactive
({
phoneNumber
:
''
,
phoneNumber
:
''
,
cardType
:
''
,
cardType
:
''
,
...
@@ -37,15 +41,17 @@ export default {
...
@@ -37,15 +41,17 @@ export default {
* 关联逻辑(调用链/数据流):查询/分页/保存成功 -> listPhoneAssets -> GET 接口 -> records 与 total。
* 关联逻辑(调用链/数据流):查询/分页/保存成功 -> listPhoneAssets -> GET 接口 -> records 与 total。
*/
*/
async
function
loadPage
()
{
async
function
loadPage
()
{
const
requestId
=
++
latestRequest
;
loading
.
value
=
true
;
loading
.
value
=
true
;
try
{
try
{
const
result
=
await
listPhoneAssets
(
filters
);
const
result
=
await
listPhoneAssets
(
filters
);
if
(
requestId
!==
latestRequest
)
return
;
records
.
value
=
result
.
records
;
records
.
value
=
result
.
records
;
total
.
value
=
result
.
total
;
total
.
value
=
result
.
total
;
}
catch
(
error
)
{
}
catch
(
error
)
{
ElMessage
.
error
(
error
.
message
);
if
(
requestId
===
latestRequest
)
ElMessage
.
error
(
error
.
message
);
}
finally
{
}
finally
{
loading
.
value
=
false
;
if
(
requestId
===
latestRequest
)
loading
.
value
=
false
;
}
}
}
}
...
@@ -215,35 +221,20 @@ export default {
...
@@ -215,35 +221,20 @@ export default {
}
}
}
}
/**
/** 代码作用(白话):停止输入约 300 毫秒后自动查询,避免连续输入反复请求。关联文件:PhoneAssetView.js、phone-api-client.js。关联逻辑(调用链/数据流):输入事件 -> 防抖计时 -> loadPage -> GET 接口。 */
* 代码作用(白话):按当前筛选值从第一页重新查询。
function
scheduleSearch
()
{
window
.
clearTimeout
(
searchTimer
);
searchTimer
=
window
.
setTimeout
(()
=>
{
filters
.
page
=
1
;
loadPage
();
},
300
);
}
* 关联文件:PhoneAssetView.js、phone-api-client.js。
/** 代码作用(白话):把列表手机号筛选值限定为 11 位数字,粘贴内容也按相同规则处理。关联文件:PhoneAssetView.js、PhoneAssetService.java。关联逻辑(调用链/数据流):手机号输入 -> limitSearchPhone -> scheduleSearch -> 后端号码匹配。 */
* 关联逻辑(调用链/数据流):查询按钮或表单提交 -> page=1 -> loadPage -> GET 接口。
function
limitSearchPhone
(
value
)
{
filters
.
phoneNumber
=
String
(
value
||
''
).
replace
(
/
\D
/g
,
''
).
slice
(
0
,
11
);
scheduleSearch
();
}
*/
/** 代码作用(白话):按 Enter 时立刻执行当前筛选。关联文件:PhoneAssetView.js、phone-api-client.js。关联逻辑(调用链/数据流):Enter -> 清除计时 -> page=1 -> loadPage。 */
function
submitSearch
()
{
function
submitSearch
()
{
window
.
clearTimeout
(
searchTimer
);
filters
.
page
=
1
;
loadPage
();
}
filters
.
page
=
1
;
/** 代码作用(白话):切换使用状态后马上查询,不等待输入防抖。关联文件:PhoneAssetView.js、phone-api-client.js。关联逻辑(调用链/数据流):状态 change -> page=1 -> loadPage。 */
loadPage
();
function
changeStatus
()
{
submitSearch
();
}
}
/** 代码作用(白话):清空所有筛选条件并恢复第一页列表。关联文件:PhoneAssetView.js、phone-api-client.js。关联逻辑(调用链/数据流):重置 -> filters 默认值 -> loadPage。 */
function
resetSearch
()
{
window
.
clearTimeout
(
searchTimer
);
Object
.
assign
(
filters
,
{
page
:
1
,
size
:
20
,
phoneNumber
:
''
,
iccid
:
''
,
realNameOwner
:
''
,
disposalStatus
:
''
});
loadPage
();
}
/**
/** 代码作用(白话):切换页码并读取对应数据。关联文件:PhoneAssetView.js、phone-api-client.js。关联逻辑(调用链/数据流):分页器 -> filters.page -> loadPage。 */
* 代码作用(白话):清空筛选条件并重新加载默认列表。
function
changePage
(
page
)
{
filters
.
page
=
page
;
loadPage
();
}
* 关联文件:PhoneAssetView.js、phone-api-client.js。
/** 代码作用(白话):切换每页条数后回到第一页查询。关联文件:PhoneAssetView.js、phone-api-client.js。关联逻辑(调用链/数据流):每页条数 -> size/page -> loadPage。 */
* 关联逻辑(调用链/数据流):重置按钮 -> filters 默认值 -> loadPage -> GET 接口。
function
changePageSize
(
size
)
{
filters
.
size
=
size
;
filters
.
page
=
1
;
loadPage
();
}
*/
function
resetSearch
()
{
Object
.
assign
(
filters
,
{
page
:
1
,
size
:
20
,
phoneNumber
:
''
,
iccid
:
''
});
loadPage
();
}
/**
* 代码作用(白话):切换页码并读取该页的数据。
* 关联文件:PhoneAssetView.js、phone-api-client.js。
* 关联逻辑(调用链/数据流):分页器 -> filters.page -> loadPage -> GET 接口。
*/
function
changePage
(
page
)
{
filters
.
page
=
page
;
loadPage
();
}
/**
/**
* 代码作用(白话):页面首次显示时请求手机号资产列表。
* 代码作用(白话):页面首次显示时请求手机号资产列表。
...
@@ -256,15 +247,19 @@ export default {
...
@@ -256,15 +247,19 @@ export default {
return
{
return
{
changePage
,
changePage
,
changePageSize
,
changeStatus
,
confirmDelete
,
confirmDelete
,
dialogVisible
,
dialogVisible
,
editingId
,
editingId
,
elementLocale
,
filters
,
filters
,
form
,
form
,
handleIccidPaste
,
handleIccidPaste
,
handlePhonePaste
,
handlePhonePaste
,
limitIccid
,
limitIccid
,
limitPhoneNumber
,
limitPhoneNumber
,
limitSearchPhone
,
loading
,
loading
,
openCreate
,
openCreate
,
openEdit
,
openEdit
,
...
@@ -272,46 +267,17 @@ export default {
...
@@ -272,46 +267,17 @@ export default {
resetDialogScroll
,
resetDialogScroll
,
resetSearch
,
resetSearch
,
saving
,
saving
,
scheduleSearch
,
submitForm
,
submitForm
,
submitSearch
,
submitSearch
,
total
total
};
};
},
},
template
:
`
template
:
`
<section class="phone-asset-page">
<el-config-provider :locale="elementLocale"><section class="phone-asset-page phone-asset-list-page">
<header class="page-header">
<header class="phone-asset-list-page__header"><h2>手机号资产</h2><el-button class="phone-asset-list-page__add" type="primary" @click="openCreate">新增手机号资产</el-button></header>
<div>
<section class="phone-asset-list-page__panel phone-asset-list-page__search" aria-label="筛选手机号资产"><el-form class="phone-asset-list-page__filters" @submit.prevent="submitSearch"><el-input v-model="filters.phoneNumber" maxlength="11" inputmode="numeric" placeholder="手机号前3位、后4位或完整号码" @input="limitSearchPhone" @keydown.enter.prevent="submitSearch" /><el-input v-model="filters.iccid" maxlength="20" placeholder="请输入 ICCID" @input="scheduleSearch" @keydown.enter.prevent="submitSearch" /><el-input v-model="filters.realNameOwner" placeholder="请输入实名人" @input="scheduleSearch" @keydown.enter.prevent="submitSearch" /><el-select v-model="filters.disposalStatus" placeholder="全部状态" clearable @change="changeStatus"><el-option label="全部状态" value="" /><el-option label="正常使用" value="正常使用" /><el-option label="闲置" value="闲置" /><el-option label="停机" value="停机" /><el-option label="已注销" value="已注销" /></el-select><el-button @click="resetSearch">重置</el-button></el-form></section>
<p class="eyebrow">手机号资产</p>
<section class="phone-asset-list-page__panel phone-asset-list-page__table"><header class="phone-asset-list-page__table-header"><h3>资产列表</h3><span>共 {{ total }} 条</span></header><el-table v-loading="loading" :data="records" empty-text="暂无匹配数据" class="phone-asset-list-page__grid"><el-table-column prop="phoneNumber" label="手机号" min-width="150" show-overflow-tooltip /><el-table-column prop="cardType" label="运营商" min-width="110" show-overflow-tooltip /><el-table-column prop="iccid" label="ICCID" min-width="220" show-overflow-tooltip /><el-table-column prop="realNameOwner" label="实名人" min-width="150" show-overflow-tooltip /><el-table-column prop="managementType" label="管理模式" min-width="120" show-overflow-tooltip /><el-table-column label="使用状态" min-width="130"><template #default="{ row }"><span class="phone-asset-list-page__status"><i :class="['phone-asset-list-page__status-dot', row.disposalStatus]"></i>{{ row.disposalStatus || '未设置' }}</span></template></el-table-column><el-table-column prop="deviceId" label="关联设备(ID)" min-width="150" show-overflow-tooltip /><el-table-column label="操作" width="120"><template #default="{ row }"><span class="phone-asset-list-page__actions"><el-button link @click="openEdit(row)">编辑</el-button><el-button link type="danger" @click="confirmDelete(row)">删除</el-button></span></template></el-table-column></el-table><footer v-if="total" class="phone-asset-list-page__pagination"><span>共 {{ total }} 条</span><el-pagination layout="sizes, prev, pager, next, jumper" :current-page="filters.page" :page-size="filters.size" :page-sizes="[5,10,20,50]" :total="total" @current-change="changePage" @size-change="changePageSize" /></footer></section>
<h2>手机号资产</h2>
<p>管理手机号卡的基础信息与关联状态。</p>
</div>
<el-button type="primary" @click="openCreate">新增手机号资产</el-button>
</header>
<article class="reference-card">
<el-form inline @submit.prevent="submitSearch">
<el-form-item label="手机号"><el-input v-model="filters.phoneNumber" maxlength="11" clearable /></el-form-item>
<el-form-item label="ICCID"><el-input v-model="filters.iccid" clearable /></el-form-item>
<el-form-item><el-button type="primary" @click="submitSearch">查询</el-button><el-button @click="resetSearch">重置</el-button></el-form-item>
</el-form>
<el-table v-loading="loading" :data="records" empty-text="暂无数据">
<el-table-column prop="phoneNumber" label="手机号" min-width="140" />
<el-table-column prop="cardType" label="卡类型" min-width="120" />
<el-table-column prop="iccid" label="ICCID" min-width="180" />
<el-table-column prop="realNameOwner" label="实名归属" min-width="140" />
<el-table-column prop="managementType" label="管理方式" min-width="120" />
<el-table-column prop="disposalStatus" label="处置状态" min-width="120" />
<el-table-column prop="deviceId" label="关联设备 ID" min-width="130" />
<el-table-column label="操作" width="150">
<template #default="{ row }">
<el-button link type="primary" @click="openEdit(row)">编辑</el-button>
<el-button link type="danger" @click="confirmDelete(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination v-if="total" background layout="total, prev, pager, next" :current-page="filters.page" :page-size="filters.size" :total="total" @current-change="changePage" />
</article>
<el-dialog v-model="dialogVisible" class="phone-asset-modal" modal-class="phone-asset-modal-mask" :title="editingId === null ? '新增手机号资产' : '编辑手机号资产'" width="560px" @opened="resetDialogScroll">
<el-dialog v-model="dialogVisible" class="phone-asset-modal" modal-class="phone-asset-modal-mask" :title="editingId === null ? '新增手机号资产' : '编辑手机号资产'" width="560px" @opened="resetDialogScroll">
<el-form class="phone-asset-modal__form" label-width="96px">
<el-form class="phone-asset-modal__form" label-width="96px">
<el-form-item class="phone-asset-modal__form-row" label="手机号" required>
<el-form-item class="phone-asset-modal__form-row" label="手机号" required>
...
@@ -337,6 +303,6 @@ export default {
...
@@ -337,6 +303,6 @@ export default {
<el-button type="primary" :loading="saving" @click="submitForm">确认保存</el-button>
<el-button type="primary" :loading="saving" @click="submitForm">确认保存</el-button>
</template>
</template>
</el-dialog>
</el-dialog>
</section>
</section>
</el-config-provider>
`
`
};
};
\ No newline at end of file
frontend/src/styles/app.css
View file @
45443f00
...
@@ -257,4 +257,19 @@ body { margin: 0; }
...
@@ -257,4 +257,19 @@ body { margin: 0; }
color
:
#18181b
;
color
:
#18181b
;
font-weight
:
550
;
font-weight
:
550
;
background
:
#f4f4f5
;
background
:
#f4f4f5
;
}
}
\ No newline at end of file
/* 手机号资产列表:参考 mobile-number-asset-management-v4.html,仅作用于当前列表页。 */
.phone-asset-list-page
{
width
:
100%
;
max-width
:
1680px
;
margin
:
0
auto
;
padding
:
34px
4px
56px
;
}
.phone-asset-list-page__header
{
display
:
flex
;
align-items
:
center
;
justify-content
:
space-between
;
gap
:
24px
;
margin-bottom
:
24px
;
}
.phone-asset-list-page__header
h2
{
margin
:
0
;
color
:
#111113
;
font-size
:
29px
;
font-weight
:
700
;
line-height
:
1.25
;
letter-spacing
:
-.035em
;
}
.phone-asset-list-page__add.el-button
{
height
:
38px
;
padding
:
0
16px
;
border-color
:
#18181b
;
border-radius
:
6px
;
background
:
#18181b
;
font-size
:
14px
;
font-weight
:
600
;
}
.phone-asset-list-page__panel
{
border
:
1px
solid
#e5e5e8
;
border-radius
:
12px
;
background
:
#fff
;
box-shadow
:
0
10px
30px
rgba
(
15
,
23
,
42
,
.05
);
}
.phone-asset-list-page__search
{
margin-bottom
:
16px
;
padding
:
20px
22px
;
}
.phone-asset-list-page__filters
{
display
:
flex
;
align-items
:
center
;
flex-wrap
:
wrap
;
gap
:
12px
;
}
.phone-asset-list-page__filters
.el-input
{
width
:
210px
;
}
.phone-asset-list-page__filters
.el-input
:nth-child
(
2
)
{
width
:
240px
}
.phone-asset-list-page__filters
.el-input
:nth-child
(
3
)
{
width
:
190px
}
.phone-asset-list-page__filters
.el-select
{
width
:
160px
}
.phone-asset-list-page__filters
.el-input__wrapper
,
.phone-asset-list-page__filters
.el-select__wrapper
{
min-height
:
38px
;
border-radius
:
6px
;
box-shadow
:
0
0
0
1px
#e5e5e8
inset
;
}
.phone-asset-list-page__filters
.el-button
{
height
:
38px
;
border-radius
:
6px
;
color
:
#52525b
;
border-color
:
#e5e5e8
}
.phone-asset-list-page__filters
.el-input__wrapper
:hover
,
.phone-asset-list-page__filters
.el-select__wrapper
:hover
{
box-shadow
:
0
0
0
1px
#d4d4d8
inset
}
.phone-asset-list-page__table
{
overflow
:
hidden
;
}
.phone-asset-list-page__table-header
{
display
:
flex
;
align-items
:
center
;
gap
:
9px
;
min-height
:
62px
;
padding
:
0
22px
;
border-bottom
:
1px
solid
#e5e5e8
}
.phone-asset-list-page__table-header
h3
{
margin
:
0
;
color
:
#18181b
;
font-size
:
15px
;
font-weight
:
650
}
.phone-asset-list-page__table-header
span
{
color
:
#8b8b93
;
font-size
:
13px
}
.phone-asset-list-page__grid
.el-table__header
th
.el-table__cell
{
height
:
46px
;
background
:
#fafafa
;
color
:
#71717a
;
font-size
:
13px
;
font-weight
:
600
}
.phone-asset-list-page__grid
.el-table__cell
{
height
:
56px
;
color
:
#3f3f46
;
font-size
:
14px
}
.phone-asset-list-page__grid
.el-table__row
:hover
>
td
.el-table__cell
{
background
:
#fafafa
}
.phone-asset-list-page__status
{
display
:
inline-flex
;
align-items
:
center
;
gap
:
8px
;
white-space
:
nowrap
}
.phone-asset-list-page__status-dot
{
width
:
7px
;
height
:
7px
;
border-radius
:
50%
;
background
:
#a1a1aa
;
box-shadow
:
0
0
0
3px
rgba
(
161
,
161
,
170
,
.12
)}
.phone-asset-list-page__status-dot
.
正常使用
{
background
:
#2f9e68
;
box-shadow
:
0
0
0
3px
rgba
(
47
,
158
,
104
,
.1
)}
.phone-asset-list-page__status-dot
.
闲置
,
.phone-asset-list-page__status-dot
.
停机
{
background
:
#d28b24
;
box-shadow
:
0
0
0
3px
rgba
(
210
,
139
,
36
,
.11
)}
.phone-asset-list-page__actions
{
display
:
inline-flex
;
gap
:
14px
}
.phone-asset-list-page__actions
.el-button
{
padding
:
0
;
color
:
#3f3f46
;
font-size
:
13px
}
.phone-asset-list-page__actions
.el-button--danger
{
color
:
#e5484d
}
.phone-asset-list-page__pagination
{
display
:
flex
;
align-items
:
center
;
justify-content
:
space-between
;
gap
:
20px
;
min-height
:
66px
;
padding
:
12px
22px
;
border-top
:
1px
solid
#e5e5e8
;
color
:
#71717a
;
font-size
:
13px
}
.phone-asset-list-page__pagination
.el-pagination
{
justify-content
:
flex-end
}
.phone-asset-list-page__pagination
.el-pager
li
,
.phone-asset-list-page__pagination
.btn-prev
,
.phone-asset-list-page__pagination
.btn-next
{
min-width
:
34px
;
height
:
34px
;
border
:
1px
solid
#e5e5e8
;
border-radius
:
6px
;
background
:
#fff
}
.phone-asset-list-page__pagination
.el-pager
li
.is-active
{
background
:
#18181b
;
color
:
#fff
}
@media
(
max-width
:
900px
){
.phone-asset-list-page
{
padding
:
26px
0
40px
}
.phone-asset-list-page__pagination
{
align-items
:
flex-start
;
flex-direction
:
column
}
.phone-asset-list-page__pagination
.el-pagination
{
width
:
100%
;
justify-content
:
space-between
}}
@media
(
max-width
:
640px
){
.phone-asset-list-page
{
padding
:
20px
0
32px
}
.phone-asset-list-page__header
{
align-items
:
stretch
;
flex-direction
:
column
;
margin-bottom
:
18px
}
.phone-asset-list-page__add.el-button
{
width
:
100%
}
.phone-asset-list-page__header
h2
{
font-size
:
25px
}
.phone-asset-list-page__search
{
padding
:
18px
}
.phone-asset-list-page__filters
,
.phone-asset-list-page__filters
.el-input
,
.phone-asset-list-page__filters
.el-select
,
.phone-asset-list-page__filters
.el-button
{
width
:
100%
!important
}
.phone-asset-list-page__table-header
,
.phone-asset-list-page__pagination
{
padding-left
:
18px
;
padding-right
:
18px
}
.phone-asset-list-page__pagination
.el-pagination__jump
{
display
:
none
}}
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment