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
a3e29145
Commit
a3e29145
authored
Jul 01, 2026
by
DaiJiezhang
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Refine wechat list layout and image actions
parent
f7030db4
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
339 additions
and
60 deletions
+339
-60
app-v2.js
app-v2.js
+36
-6
record-adapters.js
src/modules/shared/record-adapters.js
+27
-15
wechat-api-client.js
src/modules/shared/wechat-api-client.js
+2
-0
wechat-list-runtime.js
src/modules/wechat/wechat-list-runtime.js
+98
-25
styles-v2.css
styles-v2.css
+141
-5
styles.css
styles.css
+35
-9
No files found.
app-v2.js
View file @
a3e29145
...
...
@@ -347,6 +347,23 @@ async function saveWechatRecord(payload) {
}
/**
* 代码作用(白话):在列表里删除企微手机图片时,先取最新记录再清空图片字段并复用统一保存接口;关联文件:F:/Project/xyw_console/src/modules/wechat/wechat-list-runtime.js、F:/Project/xyw_console/src/modules/shared/wechat-api-client.js;关联逻辑(调用链/消息链/数据流):列表删除图片按钮 -> removeWechatRecordImage() -> getWechatRecordById() -> saveWechatRecord() -> refreshWechatList()。
*/
async
function
removeWechatRecordImage
(
recordId
)
{
const
record
=
await
getWechatRecordById
(
recordId
);
if
(
!
record
)
{
throw
new
Error
(
'未找到对应企微记录'
);
}
const
payload
=
{
...
record
,
phone_image
:
''
,
phoneImage
:
''
,
'手机图片'
:
''
};
return
saveWechatRecord
(
payload
);
}
/**
* 代码作用(白话):在企微保存成功后刷新当前页面,保证列表、详情和弹窗看到的是同一份最新数据;关联文件:F:/Project/xyw_console/src/modules/wechat/wechat-add-dialog.js;关联逻辑(调用链/消息链/数据流):WechatAddDialogActions.afterSave() -> refreshWechatList() -> renderModule() -> 重新挂载 wechat 列表。
*/
async
function
refreshWechatList
()
{
...
...
@@ -477,24 +494,32 @@ function renderExternalRuntime(moduleKey) {
add
:
'新增资料'
,
reset
:
'重置'
,
realName
:
'姓名'
,
account
:
'账号'
,
alias
:
'别名'
,
department
:
'部门'
,
gender
:
'性别'
,
phone
:
'手机号'
,
wxId
:
'微信 ID
'
,
status
:
'状态
'
,
owner
:
'负责人
'
,
updatedAt
:
'更新时间
'
,
owner
:
'实名人
'
,
accountLocation
:
'账号位置
'
,
owner
Status
:
'实名状态
'
,
phoneImage
:
'手机图片
'
,
action
:
'操作'
,
view
:
'查看'
,
edit
:
'编辑'
,
replaceImage
:
'更换图片'
,
removeImage
:
'删除图片'
,
imageMissing
:
'暂无图片'
,
selected
:
'已选'
,
items
:
'项'
,
batchAction
:
'批量操作'
,
total
:
'共'
,
records
:
'条'
,
pageSuffix
:
' 条/页'
,
searchPlaceholder
:
'搜索姓名、
微信 ID、手机号或部门
'
,
searchPlaceholder
:
'搜索姓名、
账号、手机号、部门或实名人
'
,
loadFailed
:
'加载企微列表失败,请确认本地后端已启动'
,
openFailed
:
'打开企微详情失败,请稍后重试'
openFailed
:
'打开企微详情失败,请稍后重试'
,
imageRemoved
:
'已删除手机图片'
,
imageRemoveFailed
:
'删除手机图片失败,请稍后重试'
},
wechatTablePageSizes
:
[
10
,
20
,
50
,
100
],
mountState
:
moduleMountState
.
wechat
,
...
...
@@ -502,6 +527,7 @@ function renderExternalRuntime(moduleKey) {
loadWechatRows
,
openAddDialog
:
openWechatAddDialog
,
openDrawer
:
openWechatDrawer
,
removeRecordImage
:
removeWechatRecordImage
,
getVisiblePageNumbers
,
getWechatStatusDotClass
,
notifyError
:
showError
,
...
...
@@ -696,3 +722,7 @@ document.addEventListener('DOMContentLoaded', initPage);
src/modules/shared/record-adapters.js
View file @
a3e29145
...
...
@@ -12,33 +12,45 @@
const
ownerStatusRaw
=
source
.
ownerStatus
??
source
.
real_name_owner_status
??
source
.
realNameOwnerStatus
;
const
status
=
ownerStatusRaw
===
'abnormal'
||
ownerStatusRaw
===
'异常'
||
ownerStatusRaw
===
0
?
'异常'
:
'正常'
;
const
remark
=
source
.
remark
||
source
.
project
||
'暂无备注'
;
const
accountLocation
=
source
.
account_location
||
source
.
accountLocation
||
source
[
'账号位置'
]
||
''
;
const
phoneImage
=
source
.
phone_image
||
source
.
phoneImage
||
source
[
'手机图片'
]
||
''
;
const
departmentName
=
departmentMap
[
source
.
department
]
||
source
.
department
||
source
.
department_name
||
source
.
departmentName
||
''
;
const
realName
=
source
.
realName
||
source
.
real_name
||
''
;
const
aliasName
=
source
.
alias_name
||
source
.
alias
||
source
.
aliasName
||
''
;
const
account
=
source
.
account
||
source
.
wxId
||
''
;
const
phoneNumber
=
source
.
phone_number
||
source
.
phone
||
source
.
phoneNumber
||
''
;
const
ownerName
=
source
.
real_name_owner
||
source
.
owner
||
source
.
realNameOwner
||
''
;
return
{
...
source
,
realName
:
source
.
realName
||
source
.
real_name
||
''
,
real_name
:
source
.
real_name
||
source
.
realName
||
''
,
alias
:
source
.
alias
||
source
.
alias_name
||
source
.
aliasName
||
''
,
alias_name
:
source
.
alias_name
||
source
.
alias
||
source
.
aliasName
||
''
,
account
:
source
.
account
||
source
.
wxId
||
''
,
department
:
department
Map
[
source
.
department
]
||
source
.
department
||
source
.
department_name
||
source
.
department
Name
||
'未分配'
,
department_name
:
source
.
department_name
||
source
.
department
||
source
.
departmentName
||
''
,
phone
:
source
.
phone
||
source
.
phone_number
||
source
.
phoneNumber
||
''
,
phone_number
:
source
.
phone_number
||
source
.
phone
||
source
.
phoneNumber
||
''
,
owner
:
source
.
owner
||
source
.
real_name_owner
||
source
.
realNameOwner
||
''
,
real_name_owner
:
source
.
real_name_owner
||
source
.
owner
||
source
.
realNameOwner
||
''
,
realName
,
real_name
:
realName
,
alias
:
aliasName
,
alias_name
:
aliasName
,
account
,
department
:
departmentName
||
'未分配'
,
department_name
:
departmentName
,
phone
:
phoneNumber
,
phone_number
:
phoneNumber
,
owner
:
ownerName
,
real_name_owner
:
ownerName
,
ownerStatus
:
status
,
real_name_owner_status
:
status
===
'正常'
?
1
:
0
,
status
,
wxId
:
source
.
wxId
||
source
.
account
||
''
,
wxId
:
source
.
wxId
||
account
,
createdAt
:
source
.
createdAt
||
source
.
created_at
||
''
,
created_at
:
source
.
created_at
||
source
.
createdAt
||
''
,
updatedAt
:
source
.
updatedAt
||
source
.
updated_at
||
''
,
updated_at
:
source
.
updated_at
||
source
.
updatedAt
||
''
,
remark
,
project
:
source
.
project
||
remark
,
gender
:
source
.
gender
||
'男'
,
account_location
:
source
.
account_location
||
source
.
accountLocation
||
source
[
'账号位置'
]
||
''
,
phone_image
:
source
.
phone_image
||
source
.
phoneImage
||
source
[
'手机图片'
]
||
''
gender
:
source
.
gender
||
''
,
account_location
:
accountLocation
,
accountLocation
,
'账号位置'
:
accountLocation
,
phone_image
:
phoneImage
,
phoneImage
,
'手机图片'
:
phoneImage
};
}
...
...
src/modules/shared/wechat-api-client.js
View file @
a3e29145
(
function
attachWechatApiClient
()
{
// 当前企微资料接口对应测试数据库;后续切换到生产数据库(正式业务库)时,需要同步改成生产环境对应的后端配置。
const
BASE_URL
=
'http://localhost:8080/api/wechat-records'
;
/**
...
...
@@ -55,3 +56,4 @@
saveWechatRecord
};
})();
src/modules/wechat/wechat-list-runtime.js
View file @
a3e29145
(
function
attachWechatListRuntime
()
{
const
WECHAT_TABLE_COLUMN_WIDTHS
=
[
'48px'
,
'calc((100% - 48px) * 0.14)'
,
'calc((100% - 48px) * 0.12)'
,
'calc((100% - 48px) * 0.14)'
,
'calc((100% - 48px) * 0.16)'
,
'calc((100% - 48px) * 0.10)'
,
'calc((100% - 48px) * 0.12)'
,
'calc((100% - 48px) * 0.12)'
,
'calc((100% - 48px) * 0.10)'
'140px'
,
'140px'
,
'140px'
,
'140px'
,
'92px'
,
'132px'
,
'132px'
,
'180px'
,
'120px'
,
'156px'
,
'216px'
];
const
WECHAT_TABLE_MIN_WIDTH
=
'1636px'
;
const
WechatListTable
=
{
name
:
'WechatListTable'
,
...
...
@@ -21,6 +25,7 @@
loadWechatRows
:
{
type
:
Function
,
required
:
true
},
openAddDialog
:
{
type
:
Function
,
required
:
true
},
openDrawer
:
{
type
:
Function
,
required
:
true
},
removeRecordImage
:
{
type
:
Function
,
required
:
true
},
getVisiblePageNumbers
:
{
type
:
Function
,
required
:
true
},
getWechatStatusDotClass
:
{
type
:
Function
,
required
:
true
},
notifyError
:
{
type
:
Function
,
required
:
true
}
...
...
@@ -43,6 +48,23 @@
const
loading
=
ref
(
false
);
/**
* 代码作用(白话):把列表里可能为空的字段统一转换成稳定显示文案,避免扩列后出现大片空白;关联文件:F:/Project/xyw_console/src/modules/shared/record-adapters.js;关联逻辑(调用链/消息链/数据流):record 字段 -> displayValue() -> 列表单元格文本。
*/
function
displayValue
(
value
)
{
if
(
value
===
null
||
value
===
undefined
||
value
===
''
)
{
return
'-'
;
}
return
String
(
value
);
}
/**
* 代码作用(白话):从一条企微记录里读出手机图片链接,给缩略图、放大预览和删除动作共用;关联文件:F:/Project/xyw_console/src/modules/shared/record-adapters.js;关联逻辑(调用链/消息链/数据流):record -> getPhoneImageUrl() -> 图片单元格/删除图片动作。
*/
function
getPhoneImageUrl
(
record
)
{
return
record
.
phone_image
||
record
.
phoneImage
||
record
[
'手机图片'
]
||
''
;
}
/**
* 代码作用(白话):把分页和勾选状态同步回外层挂载状态,保证路由切走再切回来时还能恢复现场;关联文件:F:/Project/xyw_console/app-v2.js;关联逻辑(调用链/消息链/数据流):列表交互 -> syncMountState() -> mountState -> 下次重新挂载复用。
*/
function
syncMountState
()
{
...
...
@@ -100,9 +122,12 @@
const
query
=
searchQuery
.
value
.
toLowerCase
();
result
=
result
.
filter
((
record
)
=>
(
record
.
realName
&&
record
.
realName
.
toLowerCase
().
includes
(
query
))
||
(
record
.
account
&&
record
.
account
.
toLowerCase
().
includes
(
query
))
||
(
record
.
alias_name
&&
record
.
alias_name
.
toLowerCase
().
includes
(
query
))
||
(
record
.
phone
&&
record
.
phone
.
toLowerCase
().
includes
(
query
))
||
(
record
.
wxId
&&
record
.
wxId
.
toLowerCase
().
includes
(
query
))
||
(
record
.
department
&&
record
.
department
.
toLowerCase
().
includes
(
query
))
(
record
.
department
&&
record
.
department
.
toLowerCase
().
includes
(
query
))
||
(
record
.
real_name_owner
&&
record
.
real_name_owner
.
toLowerCase
().
includes
(
query
))
);
}
return
result
;
...
...
@@ -301,11 +326,36 @@
}
}
/**
* 代码作用(白话):从列表里直接打开当前记录的编辑弹窗,让用户在原有保存流程里更换图片链接;关联文件:F:/Project/xyw_console/app-v2.js、F:/Project/xyw_console/src/modules/wechat/wechat-add-dialog.js;关联逻辑(调用链/消息链/数据流):更换图片按钮 -> handleReplaceImage() -> openDrawer('edit') -> 编辑弹窗保存。
*/
async
function
handleReplaceImage
(
recordId
)
{
await
handleRowAction
(
'edit'
,
recordId
);
}
/**
* 代码作用(白话):从列表里删除手机图片链接并刷新全量记录,保证列表、详情和编辑弹窗看到同一份最新状态;关联文件:F:/Project/xyw_console/app-v2.js、F:/Project/xyw_console/src/modules/shared/wechat-api-client.js;关联逻辑(调用链/消息链/数据流):删除图片按钮 -> handleRemoveImage() -> removeRecordImage() -> loadAllRecords。
*/
async
function
handleRemoveImage
(
recordId
)
{
focusRow
(
recordId
);
loading
.
value
=
true
;
try
{
await
props
.
removeRecordImage
(
recordId
);
await
loadAllRecords
();
window
.
ElementPlus
?.
ElMessage
?.
success
?.(
props
.
wechatText
.
imageRemoved
);
}
catch
(
error
)
{
props
.
notifyError
(
error
.
message
||
props
.
wechatText
.
imageRemoveFailed
);
}
finally
{
loading
.
value
=
false
;
}
}
window
.
Vue
.
onMounted
(
loadAllRecords
);
syncMountState
();
return
{
WECHAT_TABLE_COLUMN_WIDTHS
,
WECHAT_TABLE_MIN_WIDTH
,
currentPage
,
pageSize
,
visibleRows
,
...
...
@@ -326,6 +376,10 @@
resetTableState
,
handleAdd
,
handleRowAction
,
handleReplaceImage
,
handleRemoveImage
,
displayValue
,
getPhoneImageUrl
,
filterDate
,
searchQuery
,
filterStatus
,
...
...
@@ -383,7 +437,7 @@
</div>
</div>
<div class="table-wrap table-wrap-refined" v-loading="loading">
<table class="xtable domain-table domain-table-refined">
<table class="xtable domain-table domain-table-refined
wechat-list-table" :style="{ minWidth: WECHAT_TABLE_MIN_WIDTH }
">
<colgroup>
<col v-for="width in WECHAT_TABLE_COLUMN_WIDTHS" :key="width" :style="{ width }" />
</colgroup>
...
...
@@ -391,12 +445,15 @@
<tr>
<th class="th-selector"><input type="checkbox" :checked="allVisibleChecked" @change="toggleAllVisible($event.target.checked)" /></th>
<th>{{ wechatText.realName }}</th>
<th>{{ wechatText.account }}</th>
<th>{{ wechatText.alias }}</th>
<th>{{ wechatText.department }}</th>
<th>{{ wechatText.gender }}</th>
<th>{{ wechatText.phone }}</th>
<th>{{ wechatText.wxId }}</th>
<th>{{ wechatText.status }}</th>
<th>{{ wechatText.owner }}</th>
<th>{{ wechatText.updatedAt }}</th>
<th>{{ wechatText.accountLocation }}</th>
<th>{{ wechatText.ownerStatus }}</th>
<th>{{ wechatText.phoneImage }}</th>
<th class="th-action">{{ wechatText.action }}</th>
</tr>
</thead>
...
...
@@ -404,34 +461,50 @@
<tr v-for="row in visibleRows" :key="row.id" :class="{ selected: isRowSelected(row.id) }" @click="focusRow(row.id)">
<td class="td-selector"><input type="checkbox" :checked="isRowChecked(row.id)" @click.stop @change="toggleRowChecked(row.id, $event.target.checked)" /></td>
<td class="td-domain">
<a href="#" class="domain-link" @click.prevent="focusRow(row.id)">{{ row.realName }}</a>
<div class="domain-inline-meta">{{ row.alias || row.alias_name || '-' }}</div>
<a href="#" class="domain-link" @click.prevent="focusRow(row.id)">{{ displayValue(row.realName || row.real_name) }}</a>
</td>
<td>{{ displayValue(row.account || row.wxId) }}</td>
<td>{{ displayValue(row.alias_name || row.alias) }}</td>
<td>
<div class="date-stack">
<span>{{
row.department
}}</span>
<div class="cell-sub">{{
row.project || row.remark || '暂无备注'
}}</div>
<span>{{
displayValue(row.department || row.department_name)
}}</span>
<div class="cell-sub">{{
displayValue(row.project || row.remark || '暂无备注')
}}</div>
</div>
</td>
<td>{{ row.phone }}</td>
<td>{{ row.wxId }}</td>
<td>{{ displayValue(row.gender) }}</td>
<td>{{ displayValue(row.phone || row.phone_number) }}</td>
<td>{{ displayValue(row.owner || row.real_name_owner) }}</td>
<td class="wechat-location-cell">{{ displayValue(row.account_location || row.accountLocation || row['账号位置']) }}</td>
<td>
<span class="status-chip-wrap">
<span class="status-dot" :class="getStatusClass(row.status)"></span>
<span>{{
row.status
}}</span>
<span class="status-dot" :class="getStatusClass(row.
ownerStatus || row.
status)"></span>
<span>{{
displayValue(row.ownerStatus || row.status)
}}</span>
</span>
</td>
<td>{{ row.owner }}</td>
<td>{{ row.updatedAt || '-' }}</td>
<td>
<div v-if="getPhoneImageUrl(row)" class="wechat-image-cell">
<el-image
class="wechat-image-thumb"
:src="getPhoneImageUrl(row)"
:preview-src-list="[getPhoneImageUrl(row)]"
fit="cover"
preview-teleported
@click.stop
></el-image>
</div>
<span v-else class="wechat-image-empty">{{ wechatText.imageMissing }}</span>
</td>
<td class="td-action">
<div class="action-cluster">
<div class="action-cluster
wechat-action-cluster
">
<button class="action-pill action-pill-primary" type="button" @click.stop="handleRowAction('view', row.id)">{{ wechatText.view }}</button>
<button class="action-pill" type="button" @click.stop="handleRowAction('edit', row.id)">{{ wechatText.edit }}</button>
<button class="action-pill" type="button" @click.stop="handleReplaceImage(row.id)">{{ wechatText.replaceImage }}</button>
<button class="action-pill action-pill-danger" type="button" :disabled="!getPhoneImageUrl(row)" @click.stop="handleRemoveImage(row.id)">{{ wechatText.removeImage }}</button>
</div>
</td>
</tr>
<tr v-if="!visibleRows.length">
<td colspan="
9
" style="padding: 24px; text-align: center; color: #9ca3af;">暂无企微资料</td>
<td colspan="
12
" style="padding: 24px; text-align: center; color: #9ca3af;">暂无企微资料</td>
</tr>
</tbody>
</table>
...
...
styles-v2.css
View file @
a3e29145
/* Import original styles first */
/* Import original styles first */
@import
url('styles.css')
;
/* Overrides for right side replication */
...
...
@@ -306,16 +306,16 @@
}
.fselect
:after
{
content
:
"
▾"
;
content
:
"
鈻?
;
color: #9ca3af;
}
/* Custom icon for el-select to match batch action button icon
▾
*/
/* Custom icon for el-select to match batch action button icon
鈻?
*/
.custom-fselect .el-input__suffix-inner > .el-icon > svg {
display: none !important;
}
.custom-fselect .el-input__suffix-inner > .el-icon::before {
content
:
"
▾"
;
content: "
鈻?
;
color
:
#9ca3af
;
font-size
:
14px
;
display
:
flex
;
...
...
@@ -1003,7 +1003,143 @@
fill
:
currentColor
!important
;
}
/*
消除原来的旧外边距干扰
*/
/*
娑堥櫎鍘熸潵鐨勬棫澶栬竟璺濆共鎵?
*/
.domain-mode
.action-dots
{
margin
:
0
;
}
.wechat-list-table
{
table-layout
:
fixed
;
}
.table-wrap-refined
{
overflow-x
:
auto
;
overflow-y
:
hidden
;
}
.wechat-location-cell
{
white-space
:
normal
;
word-break
:
break-word
;
}
.wechat-image-cell
{
display
:
flex
;
align-items
:
center
;
}
.wechat-image-thumb
{
width
:
84px
;
height
:
84px
;
border-radius
:
10px
;
overflow
:
hidden
;
border
:
1px
solid
#dbe4f0
;
background
:
#f8fafc
;
}
.wechat-image-thumb
.el-image__inner
{
width
:
100%
;
height
:
100%
;
object-fit
:
cover
;
}
.wechat-image-empty
{
color
:
#94a3b8
;
font-size
:
12px
;
}
.wechat-action-cluster
{
flex-wrap
:
wrap
;
justify-content
:
flex-start
;
gap
:
6px
;
}
.action-pill-danger
{
color
:
#dc2626
;
}
.action-pill-danger
:hover:not
(
:disabled
)
{
background
:
#fef2f2
;
color
:
#b91c1c
;
}
.action-pill
:disabled
{
opacity
:
0.45
;
cursor
:
not-allowed
;
}
.table-area-refined
{
flex
:
1
1
auto
;
min-height
:
0
;
height
:
100%
;
}
.table-toolbar-refined
{
flex
:
0
0
auto
;
}
.table-wrap-refined
{
flex
:
1
1
auto
;
min-height
:
0
;
height
:
0
;
overflow-x
:
auto
;
overflow-y
:
auto
;
}
#wechatListRoot
,
#phoneListRoot
{
min-height
:
0
;
}
#vue-router-app
,
.legacy-wrapper
{
display
:
flex
;
flex
:
1
1
auto
;
flex-direction
:
column
;
min-height
:
0
;
height
:
100%
;
}
#pageHeader
{
flex
:
0
0
auto
;
}
#pageBody
{
display
:
flex
!important
;
flex
:
1
1
auto
;
flex-direction
:
column
;
min-height
:
0
;
overflow
:
hidden
;
}
#wechatListRoot
,
#phoneListRoot
,
#overviewListRoot
{
flex
:
1
1
auto
;
min-height
:
0
;
height
:
100%
;
}
#wechatListRoot
[
style
*=
'display: block'
],
#phoneListRoot
[
style
*=
'display: block'
],
#overviewListRoot
[
style
*=
'display: block'
]
{
display
:
flex
!important
;
flex-direction
:
column
;
}
.table-area-refined
{
flex
:
1
1
auto
!important
;
min-height
:
0
!important
;
height
:
100%
!important
;
}
.table-wrap-refined
{
flex
:
1
1
auto
!important
;
min-height
:
0
!important
;
height
:
0
!important
;
overflow-x
:
auto
!important
;
overflow-y
:
auto
!important
;
}
styles.css
View file @
a3e29145
:root
{
:root
{
--page-bg
:
#f5f7fb
;
--surface
:
#ffffff
;
--surface-soft
:
#fafcff
;
...
...
@@ -2922,14 +2922,38 @@ button {
/* Professional admin dashboard visual override */
.shell-main
{
background
:
linear-gradient
(
180deg
,
#f7f9fc
0%
,
#eff4f9
100%
);
}
.shell-header
{
padding
:
28px
28px
20px
;
border-bottom
:
1px
solid
rgba
(
15
,
23
,
42
,
0.06
);
background
:
rgba
(
255
,
255
,
255
,
0.92
);
}
.workspace-heading
{
display
:
flex
;
align-items
:
flex-start
;
justify-content
:
space-between
;
gap
:
24px
;
}
.heading-copy
{
display
:
grid
;
gap
:
12px
;
max-width
:
760px
;
}
.legacy-wrapper
{
display
:
flex
;
flex
:
1
1
auto
;
flex-direction
:
column
;
min-height
:
0
;
height
:
100%
;
}
#pageHeader
{
flex
:
0
0
auto
;
}
#pageBody
{
flex
:
1
1
auto
;
min-height
:
0
;
}
#pageBody
>
#wechatListRoot
,
#pageBody
>
#phoneListRoot
,
#pageBody
>
#overviewListRoot
{
height
:
100%
;
min-height
:
0
;
}
.shell-header
{
padding
:
20px
28px
16px
;
border-bottom
:
1px
solid
rgba
(
15
,
23
,
42
,
0.06
);
background
:
rgba
(
255
,
255
,
255
,
0.92
);
}
.workspace-heading
{
display
:
flex
;
align-items
:
flex-start
;
justify-content
:
space-between
;
gap
:
20px
;
flex-wrap
:
wrap
;
min-height
:
88px
;
}
.heading-copy
{
display
:
grid
;
gap
:
8px
;
flex
:
1
1
420px
;
min-width
:
0
;
max-width
:
760px
;
align-content
:
start
;
}
.heading-caption-row
{
display
:
flex
;
align-items
:
center
;
gap
:
10px
;
flex-wrap
:
wrap
;
}
.heading-caption
{
margin
:
0
;
font-size
:
12px
;
font-weight
:
700
;
letter-spacing
:
0.08em
;
text-transform
:
uppercase
;
color
:
#6b7a90
;
}
.heading-badge
{
display
:
inline-flex
;
align-items
:
center
;
height
:
28px
;
padding
:
0
12px
;
border-radius
:
999px
;
background
:
#eef4ff
;
color
:
#2554cf
;
font-size
:
12px
;
font-weight
:
700
;
}
.page-title
{
margin
:
0
;
font-size
:
3
2px
;
line-height
:
1.1
;
font-weight
:
800
;
color
:
#0f172a
;
}
.page-summary
{
margin
:
0
;
color
:
#667085
;
font-size
:
14px
;
line-height
:
1.
7
;
}
.page-title
{
margin
:
0
;
font-size
:
3
0px
;
line-height
:
1.08
;
font-weight
:
800
;
color
:
#0f172a
;
}
.page-summary
{
margin
:
0
;
color
:
#667085
;
font-size
:
14px
;
line-height
:
1.
5
;
}
.admin-summary-pills
{
display
:
flex
;
flex-wrap
:
wrap
;
gap
:
10px
;
}
.status-pill
{
display
:
inline-flex
;
align-items
:
center
;
gap
:
8px
;
height
:
34px
;
padding
:
0
12px
;
border-radius
:
999px
;
border
:
1px
solid
rgba
(
148
,
163
,
184
,
0.18
);
background
:
#fff
;
box-shadow
:
0
10px
24px
rgba
(
15
,
23
,
42
,
0.04
);
font-size
:
13px
;
color
:
#475467
;
}
.status-pill
strong
{
color
:
#0f172a
;
}
...
...
@@ -2937,7 +2961,7 @@ button {
.status-pill.green
{
background
:
#edfdf3
;
color
:
#177245
;
}
.status-pill.orange
{
background
:
#fff6ea
;
color
:
#c76a00
;
}
.status-pill.red
{
background
:
#fff1f3
;
color
:
#d92d20
;
}
.header-tools
{
display
:
flex
;
flex
:
0
0
460px
;
flex-direction
:
column
;
gap
:
14px
;
}
.header-tools
{
display
:
flex
;
flex
:
1
1
360px
;
min-width
:
280px
;
max-width
:
460px
;
margin-left
:
auto
;
flex-direction
:
column
;
gap
:
14px
;
}
.header-search
{
display
:
flex
;
align-items
:
center
;
gap
:
10px
;
width
:
100%
;
min-width
:
0
;
height
:
52px
;
padding
:
0
16px
;
border
:
1px
solid
rgba
(
148
,
163
,
184
,
0.24
);
border-radius
:
16px
;
background
:
rgba
(
255
,
255
,
255
,
0.98
);
box-shadow
:
0
12px
30px
rgba
(
15
,
23
,
42
,
0.05
);
}
.header-search
::before
{
content
:
none
;
}
.header-search-icon
{
display
:
inline-flex
;
align-items
:
center
;
justify-content
:
center
;
width
:
28px
;
height
:
28px
;
border-radius
:
50%
;
background
:
#eef4ff
;
color
:
#2554cf
;
font-size
:
14px
;
}
...
...
@@ -2946,7 +2970,7 @@ button {
.header-button
,
.header-button-primary
,
.toolbar-button
,
.footer-button
,
.footer-button-primary
,
.preview-link
{
height
:
42px
;
padding
:
0
16px
;
border-radius
:
14px
;
font-weight
:
700
;
}
.header-button
,
.toolbar-button
,
.footer-button
,
.preview-link
{
border
:
1px
solid
rgba
(
148
,
163
,
184
,
0.2
);
background
:
#fff
;
box-shadow
:
0
8px
22px
rgba
(
15
,
23
,
42
,
0.05
);
}
.header-button-primary
,
.footer-button-primary
{
border
:
1px
solid
#2563eb
;
background
:
linear-gradient
(
180deg
,
#3b82f6
0%
,
#2563eb
100%
);
color
:
#fff
;
box-shadow
:
0
14px
28px
rgba
(
37
,
99
,
235
,
0.24
);
}
.shell-body
{
gap
:
20px
;
padding
:
20px
28px
28
px
;
background
:
transparent
;
}
.shell-body
{
display
:
flex
;
flex
:
1
1
auto
;
flex-direction
:
column
;
gap
:
16px
;
min-height
:
0
;
overflow
:
hidden
;
padding
:
16px
28px
20
px
;
background
:
transparent
;
}
.stats-grid
{
gap
:
16px
;
}
.metric-card-premium
{
position
:
relative
;
display
:
flex
;
flex-direction
:
column
;
gap
:
16px
;
min-height
:
190px
;
padding
:
20px
22px
;
border-radius
:
22px
;
border
:
1px
solid
rgba
(
226
,
232
,
240
,
0.92
);
box-shadow
:
0
18px
42px
rgba
(
15
,
23
,
42
,
0.06
);
overflow
:
hidden
;
}
.metric-card-premium
[
data-tone
=
"blue"
]
{
background
:
linear-gradient
(
180deg
,
#eef5ff
0%
,
#ffffff
100%
);
}
...
...
@@ -3034,7 +3058,7 @@ button {
.timeline-time
{
color
:
#98a2b3
;
font-size
:
12px
;
}
.detail-footer
{
display
:
flex
;
gap
:
10px
;
}
.detail-footer
>
*
{
flex
:
1
;
}
@media
(
max-width
:
1360px
)
{
.workspace-heading
,
.table-head-v3
,
.filter-toolbar
,
.preview-head-v3
{
flex-direction
:
column
;
align-items
:
stretch
;
}
.header-tools
{
flex
:
1
1
auto
;
}
.table-selection-chip
{
min-width
:
0
;
}
}
@media
(
max-width
:
1360px
)
{
.workspace-heading
,
.table-head-v3
,
.filter-toolbar
,
.preview-head-v3
{
flex-direction
:
column
;
align-items
:
stretch
;
}
.header-tools
{
flex
:
1
1
auto
;
min-width
:
0
;
max-width
:
none
;
margin-left
:
0
;
}
.table-selection-chip
{
min-width
:
0
;
}
}
@media
(
max-width
:
1180px
)
{
.shell-header
,
.shell-body
{
padding-left
:
18px
;
padding-right
:
18px
;
}
.page-title
{
font-size
:
28px
;
}
.preview-row-v3
{
grid-template-columns
:
32px
minmax
(
0
,
1
fr
);
}
.preview-link
{
grid-column
:
2
;
justify-self
:
start
;
}
}
@media
(
max-width
:
840px
)
{
.shell-header
,
.shell-body
{
padding-left
:
14px
;
padding-right
:
14px
;
}
.page-title
{
font-size
:
24px
;
}
.detail-grid
{
grid-template-columns
:
1
fr
;
}
}
@media
(
min-width
:
1280px
)
{
...
...
@@ -3047,3 +3071,5 @@ button {
.workspace
:not
(
.sidebar-collapsed
)
.nav-link
,
.workspace
:not
(
.sidebar-collapsed
)
.nav-toggle-button
{
justify-content
:
flex-start
;
gap
:
12px
;
padding
:
0
14px
;
}
}
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