feat(oa): add cc read filters batch mark and unread styles
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
ecdf73fe23
commit
06b1468bf1
3 files changed
+271
-15
No files matched your search
@@ -34,9 +34,10 @@
|
||||
class="oa-task-card__btn"
|
||||
:class="{
|
||||
'is-primary': !!act.primary,
|
||||
'is-danger': !!act.danger && !act.primary
|
||||
'is-danger': !!act.danger && !act.primary,
|
||||
'is-disabled': !!act.disabled
|
||||
}"
|
||||
@click.stop="$emit('action', act)"
|
||||
@click.stop="onActionClick(act)"
|
||||
>
|
||||
{{ act.label }}
|
||||
</view>
|
||||
@@ -63,6 +64,12 @@
|
||||
const t = String(this.statusTone || 'info').toLowerCase()
|
||||
return TONES.includes(t) ? t : 'info'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onActionClick(act) {
|
||||
if (!act || act.disabled) return
|
||||
this.$emit('action', act)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
+257
-13
@@ -19,6 +19,13 @@
|
||||
<text class="search-clear__x">×</text>
|
||||
</view>
|
||||
</view>
|
||||
<view
|
||||
class="manage-btn"
|
||||
:class="{ 'is-active': manageMode }"
|
||||
@click="toggleManage"
|
||||
>
|
||||
{{ manageMode ? '完成' : '管理' }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="toolbar-row toolbar-row--sub">
|
||||
<view class="search-box" :class="{ 'is-focus': initiatorFocused }">
|
||||
@@ -51,6 +58,17 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="manageMode && selectedIds.length" class="batch-bar">
|
||||
<text class="batch-bar__hint">已选 {{ selectedIds.length }} 项</text>
|
||||
<view
|
||||
class="batch-bar__btn"
|
||||
:class="{ 'is-disabled': batchReadDisabled }"
|
||||
@click="batchMarkRead"
|
||||
>
|
||||
批量已读
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="loading && !list.length" class="state state-loading">
|
||||
<view class="pulse-dots" aria-hidden="true">
|
||||
<view class="pulse-dot" />
|
||||
@@ -63,18 +81,34 @@
|
||||
<view v-else-if="!list.length" class="oa-task-list__empty">暂无抄送</view>
|
||||
|
||||
<view v-else class="oa-task-list">
|
||||
<OaTaskCard
|
||||
<view
|
||||
v-for="row in list"
|
||||
:key="row.id"
|
||||
:title="cardTitle(row)"
|
||||
:status-text="statusText(row.instanceStatus)"
|
||||
:status-tone="statusTone(row.instanceStatus)"
|
||||
:unread="!row.isRead"
|
||||
:meta-lines="metaLines(row)"
|
||||
:actions="cardActions"
|
||||
@open="openView(row)"
|
||||
@action="openView(row)"
|
||||
/>
|
||||
class="cc-row"
|
||||
:class="{ 'is-manage': manageMode, 'is-selected': isSelected(row.id) }"
|
||||
>
|
||||
<view
|
||||
v-if="manageMode"
|
||||
class="cc-check"
|
||||
@click.stop="toggleSelect(row.id)"
|
||||
>
|
||||
<view class="cc-check__box" :class="{ on: isSelected(row.id) }">
|
||||
<text v-if="isSelected(row.id)" class="cc-check__mark">✓</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="cc-card-wrap">
|
||||
<OaTaskCard
|
||||
:title="cardTitle(row)"
|
||||
:status-text="statusText(row.instanceStatus)"
|
||||
:status-tone="statusTone(row.instanceStatus)"
|
||||
:unread="!row.isRead"
|
||||
:meta-lines="metaLines(row)"
|
||||
:actions="cardActionsFor(row)"
|
||||
@open="onCardOpen(row)"
|
||||
@action="onCardAction($event, row)"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="loadingMore" class="oa-task-list__end">加载更多…</view>
|
||||
<view v-else-if="!hasMore" class="oa-task-list__end">已经到底了</view>
|
||||
</view>
|
||||
@@ -83,7 +117,11 @@
|
||||
|
||||
<script>
|
||||
import OaTaskCard from '../../../components/OaTaskCard.vue'
|
||||
import { fetchMarkOaCcTaskRead, fetchOaTasks } from '../../../utils/oa-api.js'
|
||||
import {
|
||||
fetchBatchMarkOaCcTasksRead,
|
||||
fetchMarkOaCcTaskRead,
|
||||
fetchOaTasks
|
||||
} from '../../../utils/oa-api.js'
|
||||
import { forceRelogin, getToken } from '../../../utils/auth.js'
|
||||
import { stashOaDetailTitle } from '../../../utils/oa-h5.js'
|
||||
|
||||
@@ -116,14 +154,26 @@
|
||||
loading: false,
|
||||
loadingMore: false,
|
||||
hasMore: true,
|
||||
manageMode: false,
|
||||
selectedIds: [],
|
||||
openingId: '',
|
||||
markingId: '',
|
||||
batchMarking: false,
|
||||
_searchTimer: null,
|
||||
readTabs: [
|
||||
{ value: 'unread', label: '未读' },
|
||||
{ value: 'read', label: '已读' },
|
||||
{ value: 'all', label: '全部' }
|
||||
],
|
||||
cardActions: [{ key: 'view', label: '查看', primary: true }]
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
batchReadDisabled() {
|
||||
const ids = this.selectedIds || []
|
||||
if (!ids.length) return true
|
||||
return !this.list.some(
|
||||
(r) => r && ids.indexOf(r.id) >= 0 && !r.isRead
|
||||
)
|
||||
}
|
||||
},
|
||||
onShow() {
|
||||
@@ -180,6 +230,20 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
cardActionsFor(row) {
|
||||
if (this.manageMode) return []
|
||||
return [
|
||||
{ key: 'view', label: '查看', primary: true },
|
||||
{
|
||||
key: 'read',
|
||||
label: '已读',
|
||||
disabled: !!(row && row.isRead)
|
||||
}
|
||||
]
|
||||
},
|
||||
isSelected(id) {
|
||||
return this.selectedIds.indexOf(id) >= 0
|
||||
},
|
||||
scheduleSearch() {
|
||||
if (this._searchTimer) clearTimeout(this._searchTimer)
|
||||
this._searchTimer = setTimeout(() => {
|
||||
@@ -219,6 +283,73 @@
|
||||
this.readStatus = value
|
||||
this.loadList(true)
|
||||
},
|
||||
toggleManage() {
|
||||
this.manageMode = !this.manageMode
|
||||
if (!this.manageMode) this.selectedIds = []
|
||||
},
|
||||
toggleSelect(id) {
|
||||
if (!id) return
|
||||
const i = this.selectedIds.indexOf(id)
|
||||
if (i >= 0) {
|
||||
this.selectedIds = this.selectedIds.filter((x) => x !== id)
|
||||
} else {
|
||||
this.selectedIds = this.selectedIds.concat([id])
|
||||
}
|
||||
},
|
||||
onCardOpen(row) {
|
||||
if (this.manageMode) {
|
||||
this.toggleSelect(row && row.id)
|
||||
return
|
||||
}
|
||||
this.openView(row)
|
||||
},
|
||||
onCardAction(act, row) {
|
||||
if (!act || !row) return
|
||||
if (act.key === 'view') this.openView(row)
|
||||
else if (act.key === 'read') this.markOneRead(row)
|
||||
},
|
||||
async markOneRead(row) {
|
||||
if (!row || row.isRead || this.markingId) return
|
||||
const taskId = String(row.id || '').trim()
|
||||
if (!taskId) return
|
||||
this.markingId = taskId
|
||||
try {
|
||||
await fetchMarkOaCcTaskRead(taskId)
|
||||
uni.showToast({ title: '已标记已读', icon: 'success' })
|
||||
await this.loadList(true)
|
||||
} catch (e) {
|
||||
uni.showToast({
|
||||
title: (e && e.message) || '标记已读失败',
|
||||
icon: 'none'
|
||||
})
|
||||
} finally {
|
||||
this.markingId = ''
|
||||
}
|
||||
},
|
||||
async batchMarkRead() {
|
||||
if (this.batchReadDisabled || this.batchMarking) return
|
||||
const selected = this.selectedIds || []
|
||||
const ids = this.list
|
||||
.filter((r) => r && selected.indexOf(r.id) >= 0 && !r.isRead)
|
||||
.map((r) => String(r.id || '').trim())
|
||||
.filter(Boolean)
|
||||
if (!ids.length) return
|
||||
this.batchMarking = true
|
||||
try {
|
||||
await fetchBatchMarkOaCcTasksRead(ids)
|
||||
uni.showToast({ title: '已标记已读', icon: 'success' })
|
||||
this.selectedIds = []
|
||||
this.manageMode = false
|
||||
await this.loadList(true)
|
||||
} catch (e) {
|
||||
uni.showToast({
|
||||
title: (e && e.message) || '批量已读失败',
|
||||
icon: 'none'
|
||||
})
|
||||
} finally {
|
||||
this.batchMarking = false
|
||||
}
|
||||
},
|
||||
async openView(row) {
|
||||
if (!row || this.openingId) return
|
||||
const instanceId = String(row.instanceId || '').trim()
|
||||
@@ -261,6 +392,7 @@
|
||||
this.page = 1
|
||||
this.hasMore = true
|
||||
this.loading = true
|
||||
this.selectedIds = []
|
||||
} else {
|
||||
this.loadingMore = true
|
||||
}
|
||||
@@ -400,6 +532,28 @@
|
||||
margin-top: -2rpx;
|
||||
}
|
||||
|
||||
.manage-btn {
|
||||
flex-shrink: 0;
|
||||
min-width: 96rpx;
|
||||
height: 72rpx;
|
||||
padding: 0 16rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #166534;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.manage-btn.is-active {
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.manage-btn:active {
|
||||
opacity: 0.72;
|
||||
}
|
||||
|
||||
.read-tabs {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -433,6 +587,96 @@
|
||||
opacity: 0.78;
|
||||
}
|
||||
|
||||
.batch-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16rpx;
|
||||
margin: 16rpx 24rpx 0;
|
||||
padding: 20rpx 24rpx;
|
||||
border-radius: 16rpx;
|
||||
background: #fff;
|
||||
border: 1rpx solid #d8f3e5;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.batch-bar__hint {
|
||||
font-size: 26rpx;
|
||||
color: #5f7a6d;
|
||||
}
|
||||
|
||||
.batch-bar__btn {
|
||||
min-height: 64rpx;
|
||||
padding: 0 28rpx;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 14rpx;
|
||||
font-size: 26rpx;
|
||||
font-weight: 600;
|
||||
color: #166534;
|
||||
background: #ecfdf5;
|
||||
border: 1rpx solid #a7f3d0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.batch-bar__btn.is-disabled {
|
||||
opacity: 0.45;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.batch-bar__btn:active {
|
||||
opacity: 0.78;
|
||||
}
|
||||
|
||||
.cc-row {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.cc-row.is-manage .cc-card-wrap {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.cc-row.is-selected .cc-card-wrap :deep(.oa-task-card) {
|
||||
border-color: rgba(34, 197, 94, 0.45);
|
||||
box-shadow: 0 8rpx 32rpx rgba(22, 163, 74, 0.12);
|
||||
}
|
||||
|
||||
.cc-check {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
padding: 40rpx 0 0 4rpx;
|
||||
margin-right: 12rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.cc-check__box {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
border-radius: 50%;
|
||||
border: 2rpx solid #cbd5e1;
|
||||
background: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.cc-check__box.on {
|
||||
border-color: #22c55e;
|
||||
background: #22c55e;
|
||||
}
|
||||
|
||||
.cc-check__mark {
|
||||
font-size: 22rpx;
|
||||
line-height: 1;
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.state {
|
||||
padding: 80rpx 24rpx;
|
||||
text-align: center;
|
||||
|
||||
@@ -166,3 +166,8 @@
|
||||
background: rgba(239, 68, 68, 0.06);
|
||||
border-color: rgba(239, 68, 68, 0.35);
|
||||
}
|
||||
|
||||
.oa-task-card__btn.is-disabled {
|
||||
opacity: 0.45;
|
||||
pointer-events: none;
|
||||
}
|
||||
Reference in new issue
Block a user