This commit is contained in:
zlwu committed 2026-08-09 21:07:28 +08:00
1 parent b45ce2c32c
commit a5210a6a9e
30 files changed
+11989 -5917

No files matched your search

@@ -358,6 +358,7 @@
v-for="pick in selectedInvoicePicks"
:key="pick.key"
class="invoice-selected-card"
:style="{ '--invoice-group-color': invoiceGroupColor(pick.invoiceId) }"
>
<button
type="button"
@@ -388,7 +389,7 @@
</span>
<div class="invoice-selected-card__footer">
<span class="invoice-selected-card__amount">
{{ formatMoneyYuan(pick.amount) }} 元
整票 {{ formatMoneyYuan(pick.amount) }} 元
</span>
<el-icon
class="invoice-selected-card__download"
@@ -761,6 +762,25 @@ const walletBalance = ref(0)
const editingId = ref<number | null>(null)
const titleAutoLinked = ref(true)
/** 同一发票共用边框色(与 BxInvoiceSelectDialog / bx-mobile 一致) */
const INVOICE_GROUP_COLORS = [
'#0891b2',
'#7c3aed',
'#d97706',
'#db2777',
'#2563eb',
'#059669',
'#ea580c',
'#4f46e5'
]
function invoiceGroupColor(invoiceId: string | number) {
const s = String(invoiceId || '')
let hash = 0
for (let i = 0; i < s.length; i++) hash = (hash * 31 + s.charCodeAt(i)) >>> 0
return INVOICE_GROUP_COLORS[hash % INVOICE_GROUP_COLORS.length]
}
/** 关联发票固定展示顺序:上传/创建时间从新到旧,同时间按附件 ID */
function sortInvoicePicksByFixedOrder<T extends { createTime?: string; attachmentId?: string | number; id?: string | number; key?: string }>(
items: T[]
@@ -1384,7 +1404,7 @@ function viewDetailSummary({ columns, data }: { columns: any[]; data: any[] }) {
.invoice-selected-card {
position: relative;
width: 96px;
border: 1px solid var(--border, #d9e8e2);
border: 2px solid var(--invoice-group-color, var(--border, #d9e8e2));
border-radius: 6px;
background: var(--surface, #fff);
overflow: hidden;
+1 -1
View File
@@ -20,7 +20,7 @@
const detailDrawerRef = ref<InstanceType<typeof DetailDrawer>>()
function goBack() {
router.push({ path: '/system/clue' })
router.push({ path: '/system/temp/clue' })
}
onMounted(() => {
+1 -1
View File
@@ -2236,7 +2236,7 @@ const handleViewCustomer = () => {
/** 查看商机 */
const handleViewOpportunity = () => {
router.push({
path: '/system/opportunity/detail',
path: '/system/temp/opportunity/detail',
query: { id: contract.value?.opportunityId }
});
};
@@ -23,7 +23,7 @@
const opportunityId = ref<string>('')
function goBack() {
router.push({ path: '/system/opportunity' })
router.push({ path: '/system/temp/opportunity' })
}
onMounted(() => {
@@ -1029,7 +1029,7 @@
/** 切换视图模式 */
const switchView = (mode: 'list' | 'kanban') => {
if (mode === 'kanban') {
router.push('/system/opportunity/kanban')
router.push('/system/temp/opportunity/kanban')
} else {
viewMode.value = mode
}
@@ -1037,7 +1037,7 @@
/** 跳转到漏斗分析 */
const goToFunnel = () => {
router.push('/system/opportunity/funnel')
router.push('/system/temp/opportunity/funnel')
}
watchDebounced(
@@ -185,13 +185,13 @@ const resetQuery = () => {
}
const handleViewDetail = (id: number) => {
router.push({ path: '/system/opportunity/detail', query: { id } })
router.push({ path: '/system/temp/opportunity/detail', query: { id } })
}
const handleCardAction = (command: string, opp: OpportunityVO) => {
switch (command) {
case 'edit':
router.push({ path: '/system/opportunity', query: { id: opp.id, action: 'edit' } })
router.push({ path: '/system/temp/opportunity', query: { id: opp.id, action: 'edit' } })
break
case 'stage':
changeStageDialogRef.value?.open(opp.id)
@@ -239,7 +239,7 @@ let winRateChart: echarts.ECharts | null = null
let ownerChart: echarts.ECharts | null = null
const goBack = () => {
router.push('/system/opportunity')
router.push('/system/temp/opportunity')
}
const handleQuery = () => {
+79 -48
View File
@@ -78,49 +78,62 @@
<p v-else class="portal-home-empty">暂无收藏,可在各模块菜单卡片上点星标添加</p>
</section>
<section v-else class="portal-home-block">
<div class="portal-home-grid">
<button
v-for="card in activeCards"
:key="card.path"
type="button"
class="portal-home-card"
@click="openCard(card.path)"
>
<span
v-if="pendingCountFor(card) > 0"
class="portal-home-card__pending"
:aria-label="`${pendingCountFor(card)} 条待审批`"
>
{{ formatPendingCount(pendingCountFor(card)) }}
</span>
<span
class="portal-home-card__badge"
:data-tone="toneIndex(activeModule)"
aria-hidden="true"
>
{{ titleAbbr(card.title) }}
</span>
<span class="portal-home-card__body">
<span class="portal-home-card__title">{{ card.title }}</span>
<span class="portal-home-card__sub">{{ panelTitle }}</span>
</span>
<template v-else>
<section
v-for="(group, gIdx) in activeGroups"
:key="group.groupPath || `flat-${gIdx}`"
class="portal-home-block"
:class="{ 'portal-home-block--flat': group.hideTitle }"
>
<div v-if="!group.hideTitle && group.groupTitle" class="portal-home-block__head">
<h3 class="title-bar-heading">{{ group.groupTitle }}</h3>
<span class="portal-home-block__tip">{{ group.cards.length }} 项</span>
</div>
<div class="portal-home-grid">
<button
v-for="card in group.cards"
:key="card.path"
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)"
class="portal-home-card"
@click="openCard(card.path)"
>
<ElIcon>
<StarFilled v-if="favoritePathSet.has(card.path)" />
<Star v-else />
</ElIcon>
<span
v-if="pendingCountFor(card) > 0"
class="portal-home-card__pending"
:aria-label="`${pendingCountFor(card)} 条待审批`"
>
{{ formatPendingCount(pendingCountFor(card)) }}
</span>
<span
class="portal-home-card__badge"
:data-tone="toneIndex(group.groupPath || activeModule)"
aria-hidden="true"
>
{{ titleAbbr(card.title) }}
</span>
<span class="portal-home-card__body">
<span class="portal-home-card__title">{{ card.title }}</span>
<span class="portal-home-card__sub">{{
group.hideTitle || !group.groupTitle ? panelTitle : group.groupTitle
}}</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>
</button>
</div>
<p v-if="!activeCards.length" class="portal-home-empty">该模块暂无可用菜单</p>
</section>
</div>
</section>
<p v-if="!activeGroups.length" class="portal-home-empty">该模块暂无可用菜单</p>
</template>
</div>
</div>
@@ -190,19 +203,22 @@
const isFavView = computed(() => activeModule.value === FAV_KEY)
const activeCards = computed(() => {
if (isFavView.value) return []
const sec = modules.value.find((m) => m.modulePath === activeModule.value)
return sec?.cards || []
const activeSection = computed(() => {
if (isFavView.value) return undefined
return modules.value.find((m) => m.modulePath === activeModule.value)
})
const activeGroups = computed(() => activeSection.value?.groups || [])
const activeCards = computed(() => activeSection.value?.cards || [])
const panelTitle = computed(() => {
if (isFavView.value) return '收藏'
return modules.value.find((m) => m.modulePath === activeModule.value)?.moduleTitle || ''
return activeSection.value?.moduleTitle || ''
})
const panelDesc = computed(() =>
isFavView.value ? '可拖拽调整顺序,星标取消收藏' : '点击卡片进入功能,星标加入收藏'
isFavView.value ? '可拖拽调整顺序,星标取消收藏' : '按二级菜单分组展示,点击卡片进入功能'
)
const panelCount = computed(() =>
@@ -217,6 +233,11 @@
const favSubTitle = (path: string) => {
for (const sec of modules.value) {
for (const group of sec.groups || []) {
if (!group.cards.some((c) => c.path === path)) continue
if (group.hideTitle || !group.groupTitle) return sec.moduleTitle
return `${sec.moduleTitle} / ${group.groupTitle}`
}
if (sec.cards.some((c) => c.path === path)) return sec.moduleTitle
}
return '收藏入口'
@@ -494,15 +515,23 @@
}
.portal-home-block {
margin: 0 18px 18px;
margin: 14px 18px 8px;
&:last-child {
&:last-of-type {
margin-bottom: 4px;
}
&--fav {
margin-top: 14px;
}
&--flat {
margin-top: 14px;
.portal-home-grid {
margin-top: 0;
}
}
}
.portal-home-block__head {
@@ -510,11 +539,13 @@
flex-wrap: wrap;
gap: 8px;
align-items: center;
justify-content: space-between;
}
.portal-home-block__tip {
font-size: 12px;
font-weight: 400;
font-weight: 500;
font-variant-numeric: tabular-nums;
color: var(--mint-text-muted, #94a3b8);
}
+1 -1
View File
@@ -408,7 +408,7 @@
// 返回列表
const goBack = () => {
router.push('/system/user')
router.push('/system/info/user')
}
// 加载数据