提交
This commit is contained in:
1 parent
9345cf4ad8
commit
c3bd2c00ae
51 files changed
+14486
-15765
No files matched your search
@@ -115,7 +115,9 @@ export interface PlanListParams {
|
||||
executionStatus?: string
|
||||
createdFrom?: string
|
||||
createdTo?: string
|
||||
/** 筛选区间开始:与计划有效期(起始~截止)有交集 */
|
||||
startDateFrom?: string
|
||||
/** 筛选区间结束:与计划有效期(起始~截止)有交集 */
|
||||
startDateTo?: string
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@ export interface DutySlotItem {
|
||||
shiftLabel?: string
|
||||
capacity: number
|
||||
taken?: number
|
||||
/** 班次工作安排 */
|
||||
remark?: string | null
|
||||
assignments: DutyAssignmentItem[]
|
||||
}
|
||||
|
||||
@@ -47,6 +49,7 @@ export function saveDutyDay(data: {
|
||||
slots: Array<{
|
||||
shiftType: ShiftType
|
||||
capacity: number
|
||||
remark?: string | null
|
||||
assignments: DutyAssignmentItem[]
|
||||
}>
|
||||
linkStatus?: LinkStatus
|
||||
|
||||
@@ -12,6 +12,7 @@ export interface PublicDutySlot {
|
||||
shiftLabel?: string
|
||||
capacity: number
|
||||
taken: number
|
||||
remark?: string | null
|
||||
assignments: Array<{ id?: string; source: string; personName: string; userId?: string }>
|
||||
}
|
||||
|
||||
|
||||
@@ -405,10 +405,17 @@
|
||||
loadBxAttachments
|
||||
} from '@/utils/bx/attachment'
|
||||
import { invoicesToLinkedAttachmentFiles } from '@/utils/bx/invoice-link-files'
|
||||
import { matchesApplyDateRange, type ApplyDateRange } from '@/utils/bx/apply-date-filter'
|
||||
import { buildApplyDateRangeQuery, type ApplyDateRange } from '@/utils/bx/apply-date-filter'
|
||||
import { usePagedTable } from '@/hooks/bx/usePagedTable'
|
||||
import { useApplicantOptions } from '@/hooks/bx/useApplicantOptions'
|
||||
|
||||
const STATUS_LABEL_TO_CODE: Record<string, string> = {
|
||||
待提交: 'draft',
|
||||
审批中: 'pending',
|
||||
审批通过: 'approved',
|
||||
审批驳回: 'rejected'
|
||||
}
|
||||
|
||||
type DialogKey = 'viewDetail'
|
||||
|
||||
const loading = ref(false)
|
||||
@@ -464,7 +471,7 @@
|
||||
const filterForm = reactive({
|
||||
code: '',
|
||||
bizType: '' as string,
|
||||
applicantId: undefined as number | undefined,
|
||||
applicantId: undefined as string | number | undefined,
|
||||
status: '' as string,
|
||||
applyDateRange: null as ApplyDateRange,
|
||||
amountRange: '',
|
||||
@@ -474,7 +481,7 @@
|
||||
const appliedFilter = reactive({
|
||||
code: '',
|
||||
bizType: '' as string,
|
||||
applicantId: undefined as number | undefined,
|
||||
applicantId: undefined as string | number | undefined,
|
||||
status: '' as string,
|
||||
applyDateRange: null as ApplyDateRange,
|
||||
amountRange: '',
|
||||
@@ -485,6 +492,7 @@
|
||||
Object.assign(appliedFilter, { ...filterForm })
|
||||
}
|
||||
|
||||
/** 接口已支持的条件在 loadData 中下发;此处仅处理合并列表后的本地条件 */
|
||||
function filterMergedRows(rows: any[]) {
|
||||
let list = [...rows]
|
||||
const quickKw = appliedQuickSearch.value.trim().toLowerCase()
|
||||
@@ -502,32 +510,11 @@
|
||||
.includes(quickKw)
|
||||
)
|
||||
}
|
||||
const code = appliedFilter.code.trim()
|
||||
if (code) list = list.filter((r: any) => String(r.applyCode || '').includes(code))
|
||||
if (appliedFilter.bizType && appliedFilter.bizType !== '全部') {
|
||||
list = list.filter((r: any) => r.bizType === appliedFilter.bizType)
|
||||
}
|
||||
if (appliedFilter.applicantId != null) {
|
||||
list = list.filter((r: any) => Number(r.applicantId) === Number(appliedFilter.applicantId))
|
||||
}
|
||||
if (appliedFilter.applyDateRange?.[0] || appliedFilter.applyDateRange?.[1]) {
|
||||
list = list.filter((r: any) =>
|
||||
matchesApplyDateRange(r.applyTime, appliedFilter.applyDateRange)
|
||||
)
|
||||
}
|
||||
if (appliedFilter.status && appliedFilter.status !== '全部') {
|
||||
if (appliedFilter.status === '已发放') {
|
||||
list = list.filter((r: any) => ['issued', 'paid', 'offset'].includes(r.status))
|
||||
} else {
|
||||
const map: Record<string, string> = {
|
||||
待提交: 'draft',
|
||||
审批中: 'pending',
|
||||
审批通过: 'approved',
|
||||
审批驳回: 'rejected'
|
||||
}
|
||||
const want = map[appliedFilter.status]
|
||||
if (want) list = list.filter((r: any) => r.status === want)
|
||||
}
|
||||
if (appliedFilter.status === '已发放') {
|
||||
list = list.filter((r: any) => ['issued', 'paid', 'offset'].includes(r.status))
|
||||
}
|
||||
if (appliedFilter.currentNode && appliedFilter.currentNode !== '全部') {
|
||||
list = list.filter((r: any) => (r.currentNode || '') === appliedFilter.currentNode)
|
||||
@@ -548,6 +535,21 @@
|
||||
return list
|
||||
}
|
||||
|
||||
function buildApiQuery() {
|
||||
const q: Record<string, string | number> = {}
|
||||
const code = appliedFilter.code.trim()
|
||||
if (code) q.keyword = code
|
||||
if (appliedFilter.applicantId != null && appliedFilter.applicantId !== '') {
|
||||
q.applicantId = String(appliedFilter.applicantId)
|
||||
}
|
||||
if (appliedFilter.status && appliedFilter.status !== '全部' && appliedFilter.status !== '已发放') {
|
||||
const statusCode = STATUS_LABEL_TO_CODE[appliedFilter.status]
|
||||
if (statusCode) q.status = statusCode
|
||||
}
|
||||
buildApplyDateRangeQuery(appliedFilter.applyDateRange, q)
|
||||
return q
|
||||
}
|
||||
|
||||
const filteredMergedRows = computed(() => filterMergedRows(mergedRows.value))
|
||||
|
||||
const pager = usePagedTable(() => filteredMergedRows.value, { initialPageSize: 20 })
|
||||
@@ -641,29 +643,44 @@
|
||||
'已发放'
|
||||
]
|
||||
|
||||
function mapAdvanceRow(r: any) {
|
||||
return {
|
||||
...r,
|
||||
bizType: '备用金',
|
||||
title: r.reason,
|
||||
applyCode: r.applyCode || r.code,
|
||||
applicantName: r.applicantName || r.user,
|
||||
applicantId: r.applicantId
|
||||
}
|
||||
}
|
||||
|
||||
function mapReimburseRow(r: any) {
|
||||
return {
|
||||
...r,
|
||||
bizType: '报销单',
|
||||
title: r.title,
|
||||
applyCode: r.applyCode || r.code,
|
||||
applicantName: r.applicantName || r.user,
|
||||
applicantId: r.applicantId
|
||||
}
|
||||
}
|
||||
|
||||
async function loadData() {
|
||||
loading.value = true
|
||||
try {
|
||||
const [advRes, reimRes]: any[] = await Promise.all([
|
||||
advanceApi.list({ page: 1, pageSize: 999 }),
|
||||
reimburseApi.list({ page: 1, pageSize: 999 })
|
||||
const listParams = { page: 1, pageSize: 9999, ...buildApiQuery() }
|
||||
const [advRes, reimRes] = await Promise.allSettled([
|
||||
advanceApi.list(listParams),
|
||||
reimburseApi.list(listParams)
|
||||
])
|
||||
const advRows = (advRes.data?.rows || []).map((r: any) => ({
|
||||
...r,
|
||||
bizType: '备用金',
|
||||
title: r.reason,
|
||||
applyCode: r.applyCode || r.code,
|
||||
applicantName: r.applicantName || r.user,
|
||||
applicantId: r.applicantId
|
||||
}))
|
||||
const reimRows = (reimRes.data?.rows || []).map((r: any) => ({
|
||||
...r,
|
||||
bizType: '报销单',
|
||||
title: r.title,
|
||||
applyCode: r.applyCode || r.code,
|
||||
applicantName: r.applicantName || r.user,
|
||||
applicantId: r.applicantId
|
||||
}))
|
||||
const advRows =
|
||||
advRes.status === 'fulfilled'
|
||||
? ((advRes.value as any).data?.rows || []).map(mapAdvanceRow)
|
||||
: []
|
||||
const reimRows =
|
||||
reimRes.status === 'fulfilled'
|
||||
? ((reimRes.value as any).data?.rows || []).map(mapReimburseRow)
|
||||
: []
|
||||
mergedRows.value = [...advRows, ...reimRows]
|
||||
} catch {
|
||||
// error handled by interceptor
|
||||
@@ -677,6 +694,7 @@
|
||||
appliedQuickSearch.value = ''
|
||||
syncAppliedFilter()
|
||||
pager.currentPage.value = 1
|
||||
loadData()
|
||||
}
|
||||
|
||||
useDbInstantSearch(
|
||||
@@ -714,6 +732,7 @@
|
||||
currentNode: ''
|
||||
})
|
||||
pager.currentPage.value = 1
|
||||
loadData()
|
||||
}
|
||||
|
||||
function handleQuickSearch() {
|
||||
|
||||
@@ -758,7 +758,7 @@
|
||||
|
||||
const handleDateRangeChange = () => {
|
||||
activeDateShortcut.value = ''
|
||||
currentPage.value = 1
|
||||
handleSearch()
|
||||
}
|
||||
|
||||
const resetFormData = () => {
|
||||
|
||||
@@ -110,6 +110,7 @@
|
||||
:key="ci"
|
||||
class="chip"
|
||||
:class="chip.cls"
|
||||
:title="chip.title || chip.text"
|
||||
>
|
||||
{{ chip.text }}
|
||||
</span>
|
||||
@@ -179,7 +180,7 @@
|
||||
v-model="dialogVisible"
|
||||
class="dialog-form-layout duty-day-dialog"
|
||||
:title="`配置 · ${editingDate}`"
|
||||
width="640px"
|
||||
width="880px"
|
||||
align-center
|
||||
destroy-on-close
|
||||
:close-on-click-modal="false"
|
||||
@@ -198,23 +199,35 @@
|
||||
</div>
|
||||
</div>
|
||||
<template v-if="slot.enabled">
|
||||
<el-form-item label="直接安排(值班人员)">
|
||||
<el-select
|
||||
v-model="slot.pendingUserId"
|
||||
filterable
|
||||
clearable
|
||||
placeholder="选择员工后添加"
|
||||
style="width: 100%"
|
||||
@change="(v) => addAssign(slot, String(v || ''))"
|
||||
>
|
||||
<el-option
|
||||
v-for="u in userOptions"
|
||||
:key="u.value"
|
||||
:label="u.label"
|
||||
:value="u.value"
|
||||
<div class="form-grid-2 slot-assign-row">
|
||||
<el-form-item label="值班安排">
|
||||
<el-select
|
||||
v-model="slot.pendingUserId"
|
||||
filterable
|
||||
clearable
|
||||
placeholder="选择员工后添加"
|
||||
style="width: 100%"
|
||||
@change="(v) => addAssign(slot, String(v || ''))"
|
||||
>
|
||||
<el-option
|
||||
v-for="u in userOptions"
|
||||
:key="u.value"
|
||||
:label="u.label"
|
||||
:value="u.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="工作安排">
|
||||
<el-input
|
||||
v-model="slot.remark"
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
maxlength="500"
|
||||
show-word-limit
|
||||
placeholder="可选,填写本班次工作安排"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div class="person-tags">
|
||||
<el-tag
|
||||
v-for="(a, ai) in slot.assignments"
|
||||
@@ -320,6 +333,7 @@
|
||||
shiftType: ShiftType
|
||||
enabled: boolean
|
||||
capacity: number
|
||||
remark: string
|
||||
pendingUserId: string
|
||||
assignments: DutySlotItem['assignments']
|
||||
}
|
||||
@@ -329,6 +343,7 @@
|
||||
shiftType: t,
|
||||
enabled: false,
|
||||
capacity: 1,
|
||||
remark: '',
|
||||
pendingUserId: '',
|
||||
assignments: []
|
||||
}))
|
||||
@@ -473,7 +488,7 @@
|
||||
dateStr: string
|
||||
isToday: boolean
|
||||
summary?: DutyDayItem
|
||||
chips: Array<{ text: string; cls: string }>
|
||||
chips: Array<{ text: string; cls: string; title?: string }>
|
||||
}> = []
|
||||
|
||||
const prevDays = new Date(y, m - 1, 0).getDate()
|
||||
@@ -484,7 +499,7 @@
|
||||
for (let d = 1; d <= daysInMonth; d++) {
|
||||
const dateStr = toDateStr(y, m, d)
|
||||
const summary = dayMap.value.get(dateStr)
|
||||
const chips: Array<{ text: string; cls: string }> = []
|
||||
const chips: Array<{ text: string; cls: string; title?: string }> = []
|
||||
if (summary?.slots?.length) {
|
||||
for (const s of summary.slots) {
|
||||
if (shiftFilter && s.shiftType !== shiftFilter) continue
|
||||
@@ -494,6 +509,7 @@
|
||||
if (filterKey && !matched.length) continue
|
||||
const names = matched.map((a) => a.personName).filter(Boolean)
|
||||
const taken = s.taken ?? (s.assignments || []).length
|
||||
const remark = (s.remark || '').trim()
|
||||
let text: string
|
||||
if (filterKey) {
|
||||
text = `${s.shiftLabel || SHIFT_LABELS[s.shiftType]} ${names.join('、')}`
|
||||
@@ -502,7 +518,14 @@
|
||||
} else {
|
||||
text = `${s.shiftLabel || SHIFT_LABELS[s.shiftType]} ${taken}/${s.capacity}`
|
||||
}
|
||||
chips.push({ text, cls: SHIFT_CLS[s.shiftType] || 'am' })
|
||||
if (remark) {
|
||||
text = `${text} · ${remark}`
|
||||
}
|
||||
chips.push({
|
||||
text,
|
||||
cls: SHIFT_CLS[s.shiftType] || 'am',
|
||||
title: remark || undefined
|
||||
})
|
||||
}
|
||||
if (!hasFilter && !chips.length) {
|
||||
chips.push({ text: '未排班 · 点击配置', cls: 'empty' })
|
||||
@@ -598,6 +621,7 @@
|
||||
const found = detail?.slots?.find((s) => s.shiftType === fs.shiftType)
|
||||
fs.enabled = !!found
|
||||
fs.capacity = found?.capacity || 1
|
||||
fs.remark = (found?.remark || '').trim()
|
||||
fs.pendingUserId = ''
|
||||
fs.assignments = found ? [...(found.assignments || [])] : []
|
||||
}
|
||||
@@ -665,6 +689,7 @@
|
||||
.map((s) => ({
|
||||
shiftType: s.shiftType,
|
||||
capacity: s.capacity,
|
||||
remark: s.remark.trim() || null,
|
||||
assignments: s.assignments
|
||||
.filter((a) => a.source === 'assign')
|
||||
.map((a) => ({
|
||||
@@ -1147,6 +1172,12 @@
|
||||
justify-content: space-between;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.slot-assign-row {
|
||||
margin-bottom: 4px;
|
||||
:deep(.el-form-item) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
.cap-field {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
|
||||
@@ -11,6 +11,15 @@
|
||||
<span>收藏</span>
|
||||
<span class="n">{{ favoriteList.length }}</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="portal-home-aside__item"
|
||||
:class="{ 'is-on': isTodoView }"
|
||||
@click="activeModule = TODO_KEY"
|
||||
>
|
||||
<span>待办列表</span>
|
||||
<span class="n">{{ formatPendingCount(todoTotalSum) }}</span>
|
||||
</button>
|
||||
<button
|
||||
v-for="mod in modules"
|
||||
:key="mod.modulePath"
|
||||
@@ -77,6 +86,44 @@
|
||||
<p v-else class="portal-home-empty">暂无收藏,可在各模块菜单卡片上点星标添加</p>
|
||||
</section>
|
||||
|
||||
<section v-else-if="isTodoView" class="portal-home-block portal-home-block--flat">
|
||||
<div v-if="todoList.length" class="portal-home-grid">
|
||||
<button
|
||||
v-for="card in todoList"
|
||||
:key="'todo-' + card.path"
|
||||
type="button"
|
||||
class="portal-home-card"
|
||||
@click="openCard(card.path)"
|
||||
>
|
||||
<span
|
||||
class="portal-home-card__pending"
|
||||
:aria-label="`${pendingCountFor(card)} 条待审批`"
|
||||
>
|
||||
{{ formatPendingCount(pendingCountFor(card)) }}
|
||||
</span>
|
||||
<span class="portal-home-card__badge" aria-hidden="true">
|
||||
{{ titleAbbr(card.title) }}
|
||||
</span>
|
||||
<span class="portal-home-card__body">
|
||||
<span class="portal-home-card__title">{{ card.title }}</span>
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
class="portal-home-card__star"
|
||||
:class="{ 'is-on': favoritePathSet.has(card.path) }"
|
||||
:aria-label="favoritePathSet.has(card.path) ? '取消收藏' : '加入收藏'"
|
||||
@click.stop="onToggleStar(card.path)"
|
||||
>
|
||||
<ElIcon>
|
||||
<StarFilled v-if="favoritePathSet.has(card.path)" />
|
||||
<Star v-else />
|
||||
</ElIcon>
|
||||
</button>
|
||||
</button>
|
||||
</div>
|
||||
<p v-else class="portal-home-empty">暂无待办</p>
|
||||
</section>
|
||||
|
||||
<template v-else>
|
||||
<section
|
||||
v-for="(group, gIdx) in activeGroups"
|
||||
@@ -156,6 +203,7 @@
|
||||
defineOptions({ name: 'PortalHome' })
|
||||
|
||||
const FAV_KEY = '__favorites__'
|
||||
const TODO_KEY = '__todos__'
|
||||
/** CRM 报价/合同审批:侧栏角标体系尚未覆盖,首页单独拉取 */
|
||||
const PENDING_CARD_RULES: Array<{
|
||||
key: 'quote' | 'contract'
|
||||
@@ -194,9 +242,10 @@
|
||||
const favoritePathSet = computed(() => new Set(favItems.value.map((i) => i.path)))
|
||||
|
||||
const isFavView = computed(() => activeModule.value === FAV_KEY)
|
||||
const isTodoView = computed(() => activeModule.value === TODO_KEY)
|
||||
|
||||
const activeSection = computed(() => {
|
||||
if (isFavView.value) return undefined
|
||||
if (isFavView.value || isTodoView.value) return undefined
|
||||
return modules.value.find((m) => m.modulePath === activeModule.value)
|
||||
})
|
||||
|
||||
@@ -204,48 +253,6 @@
|
||||
|
||||
const activeCards = computed(() => activeSection.value?.cards || [])
|
||||
|
||||
const panelTitle = computed(() => {
|
||||
if (isFavView.value) return '收藏'
|
||||
return activeSection.value?.moduleTitle || ''
|
||||
})
|
||||
|
||||
const panelDesc = computed(() =>
|
||||
isFavView.value ? '可拖拽调整顺序,星标取消收藏' : '按二级菜单分组展示,点击卡片进入功能'
|
||||
)
|
||||
|
||||
const panelCount = computed(() =>
|
||||
isFavView.value ? favoriteList.value.length : activeCards.value.length
|
||||
)
|
||||
|
||||
const titleAbbr = (title: string) => {
|
||||
const t = String(title || '').trim()
|
||||
if (!t) return '?'
|
||||
return t.slice(0, 1)
|
||||
}
|
||||
|
||||
const ensureActiveModule = () => {
|
||||
if (activeModule.value === FAV_KEY) return
|
||||
const list = modules.value
|
||||
if (!list.length) {
|
||||
activeModule.value = FAV_KEY
|
||||
return
|
||||
}
|
||||
if (!list.some((m) => m.modulePath === activeModule.value)) {
|
||||
activeModule.value = FAV_KEY
|
||||
}
|
||||
}
|
||||
|
||||
const syncFavoriteList = () => {
|
||||
favoriteList.value = favItems.value.map(
|
||||
(i) => findHomeCardByPath(modules.value, i.path) || { path: i.path, title: i.path }
|
||||
)
|
||||
}
|
||||
|
||||
const resolveCurrentUserId = (): string => {
|
||||
const info = (userStore.info || {}) as { id?: string | number; userId?: string | number }
|
||||
return String(info.id || info.userId || '')
|
||||
}
|
||||
|
||||
const resolveBadgeMenuCode = (card: PortalHomeCardItem) => {
|
||||
const code = String(card.menuCode || '').trim()
|
||||
if (code && MENU_BADGE_CODE_SET.has(code)) return code
|
||||
@@ -276,6 +283,84 @@
|
||||
|
||||
const formatPendingCount = (n: number) => (n > 99 ? '99+' : String(n))
|
||||
|
||||
const allHomeCards = computed(() => {
|
||||
const seen = new Set<string>()
|
||||
const out: PortalHomeCardItem[] = []
|
||||
for (const sec of modules.value) {
|
||||
for (const card of sec.cards) {
|
||||
if (seen.has(card.path)) continue
|
||||
seen.add(card.path)
|
||||
out.push(card)
|
||||
}
|
||||
}
|
||||
return out
|
||||
})
|
||||
|
||||
const todoEntries = computed(() => {
|
||||
void badgeCounts.value
|
||||
void badgeLoaded.value
|
||||
void pendingCounts.value
|
||||
return allHomeCards.value
|
||||
.map((card) => ({ card, count: pendingCountFor(card) }))
|
||||
.filter((x) => x.count > 0)
|
||||
.sort(
|
||||
(a, b) => b.count - a.count || String(a.card.title).localeCompare(String(b.card.title), 'zh')
|
||||
)
|
||||
})
|
||||
|
||||
const todoList = computed(() => todoEntries.value.map((x) => x.card))
|
||||
|
||||
const todoTotalSum = computed(() =>
|
||||
todoEntries.value.reduce((sum, x) => sum + x.count, 0)
|
||||
)
|
||||
|
||||
const panelTitle = computed(() => {
|
||||
if (isFavView.value) return '收藏'
|
||||
if (isTodoView.value) return '待办列表'
|
||||
return activeSection.value?.moduleTitle || ''
|
||||
})
|
||||
|
||||
const panelDesc = computed(() => {
|
||||
if (isFavView.value) return '可拖拽调整顺序,星标取消收藏'
|
||||
if (isTodoView.value) return '汇总当前有待办的菜单,按数量从多到少排列'
|
||||
return '按二级菜单分组展示,点击卡片进入功能'
|
||||
})
|
||||
|
||||
const panelCount = computed(() => {
|
||||
if (isFavView.value) return favoriteList.value.length
|
||||
if (isTodoView.value) return todoList.value.length
|
||||
return activeCards.value.length
|
||||
})
|
||||
|
||||
const titleAbbr = (title: string) => {
|
||||
const t = String(title || '').trim()
|
||||
if (!t) return '?'
|
||||
return t.slice(0, 1)
|
||||
}
|
||||
|
||||
const ensureActiveModule = () => {
|
||||
if (activeModule.value === FAV_KEY || activeModule.value === TODO_KEY) return
|
||||
const list = modules.value
|
||||
if (!list.length) {
|
||||
activeModule.value = FAV_KEY
|
||||
return
|
||||
}
|
||||
if (!list.some((m) => m.modulePath === activeModule.value)) {
|
||||
activeModule.value = FAV_KEY
|
||||
}
|
||||
}
|
||||
|
||||
const syncFavoriteList = () => {
|
||||
favoriteList.value = favItems.value.map(
|
||||
(i) => findHomeCardByPath(modules.value, i.path) || { path: i.path, title: i.path }
|
||||
)
|
||||
}
|
||||
|
||||
const resolveCurrentUserId = (): string => {
|
||||
const info = (userStore.info || {}) as { id?: string | number; userId?: string | number }
|
||||
return String(info.id || info.userId || '')
|
||||
}
|
||||
|
||||
const loadCrmPendingCounts = async () => {
|
||||
const userId = resolveCurrentUserId()
|
||||
if (!userId) {
|
||||
|
||||
@@ -59,6 +59,9 @@
|
||||
<span>名额</span>
|
||||
<strong>{{ slot.taken }} / {{ slot.capacity }}</strong>
|
||||
</div>
|
||||
<p v-if="slot.remark" class="slot-remark">
|
||||
工作安排:<strong>{{ slot.remark }}</strong>
|
||||
</p>
|
||||
<div class="bar"><i :style="{ width: barWidth(slot) }" :class="slot.shiftType"></i></div>
|
||||
<div class="people">
|
||||
<span v-for="(a, i) in slot.assignments" :key="i" class="person">{{ a.personName }}</span>
|
||||
@@ -376,6 +379,18 @@
|
||||
font-size: 15px;
|
||||
color: #1f2a26;
|
||||
}
|
||||
.slot-remark {
|
||||
margin: 8px 0 0;
|
||||
font-size: 13px;
|
||||
line-height: 1.45;
|
||||
color: #6b7b74;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
.slot-remark strong {
|
||||
font-weight: 600;
|
||||
color: #1f2a26;
|
||||
}
|
||||
.bar {
|
||||
height: 7px;
|
||||
margin-top: 8px;
|
||||
|
||||
Reference in new issue
Block a user