feat(admin): family-aware goods view - auto tags readonly, clean link names, config badges, locate highlight
This commit is contained in:
@@ -18,7 +18,7 @@ import { tagGroupsApi } from '@/api/tag-groups'
|
||||
import { originGoodsApi } from '@/api/origin-goods'
|
||||
import { syncApi } from '@/api/sync'
|
||||
import { sameFamily } from '@/utils/family-match'
|
||||
import { truncateToProcess } from '@/utils/origin-name'
|
||||
import { cleanLinkName, deriveLinkTagNames, truncateToProcess } from '@/utils/origin-name'
|
||||
import { productFamiliesApi } from '@/api/product-families'
|
||||
|
||||
const mode = ref<'category' | 'country' | 'global'>('category')
|
||||
@@ -182,7 +182,9 @@ function goodToNode(g: Good): any {
|
||||
const filteredGoods = computed(() => {
|
||||
let result = allGoods.value
|
||||
if (searchKeyword.value) {
|
||||
result = result.filter(g => g.goodName?.includes(searchKeyword.value))
|
||||
const kw = searchKeyword.value
|
||||
// 同时匹配原始链接名与解析后的「品名 型号」展示名
|
||||
result = result.filter(g => g.goodName?.includes(kw) || cleanLinkName(g.goodName).includes(kw))
|
||||
}
|
||||
if (selectedCountryIds.value.length) {
|
||||
result = result.filter(g => selectedCountryIds.value.includes(g.countryId))
|
||||
@@ -289,13 +291,32 @@ function buildRightTree(tree: OriginGoodsTreeResponse) {
|
||||
children: [...children, ...goods],
|
||||
}
|
||||
}
|
||||
rightTreeData.value = tree.tree.map(mapCat)
|
||||
const roots = tree.tree.map(mapCat)
|
||||
// 已配置数按族计入:链接成族(或已配置商品)即视为已配置,后序汇总到各级分类
|
||||
function fillConfiguredEff(nodes: any[]): number {
|
||||
let count = 0
|
||||
for (const n of nodes) {
|
||||
if (n.isOG) {
|
||||
n.configuredEff = n.configuredCount > 0 || n.familyId ? 1 : 0
|
||||
} else {
|
||||
n.configuredEff = fillConfiguredEff(n.children ?? [])
|
||||
}
|
||||
count += n.configuredEff
|
||||
}
|
||||
return count
|
||||
}
|
||||
fillConfiguredEff(roots)
|
||||
rightTreeData.value = roots
|
||||
}
|
||||
|
||||
function rightFilterNode(_value: string, data: any) {
|
||||
if (data.isOG) {
|
||||
if (showUnconfiguredOnly.value && data.configuredCount > 0) return false
|
||||
if (searchKeyword.value && !data.label.includes(searchKeyword.value)) return false
|
||||
// 「仅未配置」同样把族内链接视为已配置
|
||||
if (showUnconfiguredOnly.value && (data.configuredCount > 0 || data.familyId)) return false
|
||||
if (searchKeyword.value) {
|
||||
const kw = searchKeyword.value
|
||||
if (!data.label.includes(kw) && !cleanLinkName(data.label).includes(kw)) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
return true
|
||||
@@ -930,8 +951,17 @@ function locateInRightTree(originGoodId: string) {
|
||||
}, 250)
|
||||
}
|
||||
|
||||
/** 右侧定位到左侧后短暂闪烁的商品 id(用于行级高亮动画) */
|
||||
const locateFlashGoodId = ref<string>('')
|
||||
|
||||
function locateInLeftTree(originGoodId: string) {
|
||||
const good = allGoods.value.find(g => g.originGoodId === originGoodId)
|
||||
// 链接 → 商品:优先精确匹配该链接配置的商品,其次同族商品(族内任一链接都可定位)
|
||||
const ogNode = findOgNodeById(originGoodId)
|
||||
const familyId = ogNode?.familyId ?? null
|
||||
const good = allGoods.value.find(g => String(g.originGoodId) === String(originGoodId))
|
||||
?? (familyId
|
||||
? allGoods.value.find(g => g.originGood?.family?.familyId === familyId)
|
||||
: null)
|
||||
if (!good) {
|
||||
ElMessage.warning('该原产品尚未配置到官网')
|
||||
return
|
||||
@@ -958,6 +988,9 @@ function locateInLeftTree(originGoodId: string) {
|
||||
}
|
||||
setTimeout(() => {
|
||||
tree.setCurrentKey(targetKey)
|
||||
// 闪烁高亮定位行(2.4s 后自动消失)
|
||||
locateFlashGoodId.value = good.id
|
||||
setTimeout(() => { if (locateFlashGoodId.value === good.id) locateFlashGoodId.value = '' }, 2400)
|
||||
nextTick(() => {
|
||||
const el = document.querySelector('.gv-left .el-tree-node.is-current') as HTMLElement
|
||||
el?.scrollIntoView({ behavior: 'smooth', block: 'center' })
|
||||
@@ -1293,27 +1326,14 @@ const groupedTagOptions = computed(() => {
|
||||
return groups
|
||||
})
|
||||
|
||||
// ─── 族派生标签:物流/工艺/位置组由族自动同步,表单中只读 ───
|
||||
const isAutoTagGroup = (name: string) => /物流|工艺|位置/.test(name)
|
||||
// ─── 派生标签:物流/工艺/印花数量组由系统按链接名称自动生成,表单中只读 ───
|
||||
const isAutoTagGroup = (name: string) => /物流|工艺|位置|印花数量/.test(name)
|
||||
const autoTagGroupIds = computed(() =>
|
||||
new Set(allTagGroups.value.filter((g) => isAutoTagGroup(g.groupName)).map((g) => g.id)),
|
||||
)
|
||||
const configHasFamily = computed(() => Boolean((configOG.value as any)?.familyId))
|
||||
const configTagOptions = computed(() =>
|
||||
configHasFamily.value
|
||||
? groupedTagOptions.value.filter((g) => g.id === 'ungrouped' || !autoTagGroupIds.value.has(g.id as any))
|
||||
: groupedTagOptions.value,
|
||||
)
|
||||
const editTagOptions = computed(() =>
|
||||
editFamilyId.value
|
||||
? groupedTagOptions.value.filter((g) => g.id === 'ungrouped' || !autoTagGroupIds.value.has(g.id as any))
|
||||
: groupedTagOptions.value,
|
||||
)
|
||||
/** 当前商品的自动组标签(只读展示) */
|
||||
const editDerivedTags = computed(() =>
|
||||
editFamilyId.value
|
||||
? ((editGood.value as any)?.tags ?? []).filter((t: any) => autoTagGroupIds.value.has(t.tagGroupId))
|
||||
: [],
|
||||
/** 当前商品的自动组标签(只读展示,来自其链接名称的解析结果) */
|
||||
const editAutoTags = computed(() =>
|
||||
((editGood.value as any)?.tags ?? []).filter((t: any) => autoTagGroupIds.value.has(t.tagGroupId)),
|
||||
)
|
||||
|
||||
function toggleTagInSelection(tagId: string): void {
|
||||
@@ -1370,19 +1390,6 @@ async function quickCreateCountry(targetForm: () => void) {
|
||||
} catch {}
|
||||
}
|
||||
|
||||
async function quickCreateTag(targetForm: () => void) {
|
||||
try {
|
||||
const { value } = await ElMessageBox.prompt('请输入标签名称', '新增标签', {
|
||||
confirmButtonText: '新增', cancelButtonText: '取消', inputPlaceholder: '标签名称',
|
||||
})
|
||||
if (!value.trim()) return
|
||||
await tagsApi.createTag({ tagName: value.trim(), tagColor: '#ff6800' } as any)
|
||||
await reloadTags()
|
||||
targetForm()
|
||||
ElMessage.success('已创建并选中')
|
||||
} catch {}
|
||||
}
|
||||
|
||||
async function quickCreateTagGroup() {
|
||||
try {
|
||||
const { value } = await ElMessageBox.prompt('请输入分组名称', '新建分组', {
|
||||
@@ -1662,6 +1669,7 @@ onMounted(() => loadAll())
|
||||
:data="leftTreeData"
|
||||
:props="treeProps"
|
||||
node-key="id"
|
||||
highlight-current
|
||||
:draggable="mode === 'category'"
|
||||
:expand-on-click-node="true"
|
||||
@node-drag-end="onLeftTreeDragEnd"
|
||||
@@ -1684,7 +1692,12 @@ onMounted(() => loadAll())
|
||||
</span>
|
||||
</div>
|
||||
<!-- Good node: two-line with meta -->
|
||||
<div v-else-if="data.isGood" class="good-node" @click="openEdit(data.raw)">
|
||||
<div
|
||||
v-else-if="data.isGood"
|
||||
class="good-node"
|
||||
:class="{ 'good-node--flash': data.goodId === locateFlashGoodId }"
|
||||
@click="openEdit(data.raw)"
|
||||
>
|
||||
<el-image v-if="data.goodImage" :src="data.goodImage" fit="cover" class="good-thumb" />
|
||||
<div v-else class="good-thumb-placeholder" />
|
||||
<div class="good-body">
|
||||
@@ -1697,7 +1710,7 @@ onMounted(() => loadAll())
|
||||
<el-image v-if="data.goodImage" :src="data.goodImage" fit="cover" class="gt-img" />
|
||||
<div v-else class="gt-img gt-img-empty" />
|
||||
<div class="gt-title-area">
|
||||
<div class="gt-title">{{ data.goodName }}</div>
|
||||
<div class="gt-title">{{ cleanLinkName(data.goodName) }}</div>
|
||||
<div class="gt-country">{{ data.country }}</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1715,12 +1728,12 @@ onMounted(() => loadAll())
|
||||
</div>
|
||||
<div v-if="data.originGoodName" class="gt-row">
|
||||
<div class="gt-label">{{ data.isCustom ? '来源' : '原产品' }}</div>
|
||||
<div class="gt-val gt-val-ellipsis">{{ data.originGoodName }}</div>
|
||||
<div class="gt-val gt-val-ellipsis">{{ cleanLinkName(data.originGoodName) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<span class="good-name">{{ data.goodName }}</span>
|
||||
<span class="good-name" :title="data.goodName">{{ cleanLinkName(data.goodName) }}</span>
|
||||
<span v-if="data.mergedCount > 1" class="gv-merged-badge">×{{ data.mergedCount }}</span>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
@@ -1778,11 +1791,11 @@ onMounted(() => loadAll())
|
||||
<div v-if="data.isOG" class="og-node" draggable="true" @dragstart="onOGDragStart($event, data)">
|
||||
<el-image v-if="data.goodImage" :src="data.goodImage" fit="cover" class="og-thumb" />
|
||||
<div v-else class="og-thumb og-thumb-placeholder" />
|
||||
<span class="og-name">{{ data.label }}</span>
|
||||
<span class="og-name" :title="data.label">{{ cleanLinkName(data.label) }}</span>
|
||||
<span
|
||||
v-if="data.configuredCount > 0"
|
||||
v-if="data.familyId || data.configuredCount > 0"
|
||||
class="og-badge og-badge--ok"
|
||||
title="已配置 {{ data.configuredCount }} 国"
|
||||
:title="data.familyId ? `族 ${data.familyCode || ''} · ${data.familyName || ''}` : `已配置 ${data.configuredCount} 国`"
|
||||
>已配置{{ data.configuredCount > 1 ? ' ' + data.configuredCount : '' }}</span>
|
||||
<span
|
||||
v-else
|
||||
@@ -1805,12 +1818,12 @@ onMounted(() => loadAll())
|
||||
@click.stop="handleSyncOriginDetail(data)"
|
||||
/>
|
||||
<el-button
|
||||
v-if="data.configuredCount > 0"
|
||||
v-if="data.configuredCount > 0 || data.familyId"
|
||||
size="small" link :icon="Aim" title="定位到官网商品"
|
||||
@click.stop="locateInLeftTree(data.rawId)"
|
||||
/>
|
||||
<el-button
|
||||
v-if="!data.configuredCount"
|
||||
v-if="!data.configuredCount && !data.familyId"
|
||||
size="small" type="primary" link class="og-config-btn"
|
||||
@click.stop="openConfigFromRightTree(data)"
|
||||
>配置</el-button>
|
||||
@@ -1818,8 +1831,8 @@ onMounted(() => loadAll())
|
||||
<div v-else class="og-cat-node">
|
||||
<span>{{ data.label }}</span>
|
||||
<span v-if="data.totalCount" class="og-cat-count">
|
||||
<template v-if="data.configuredCount < data.totalCount">
|
||||
{{ data.configuredCount }}/{{ data.totalCount }}
|
||||
<template v-if="data.configuredEff < data.totalCount">
|
||||
{{ data.configuredEff }}/{{ data.totalCount }}
|
||||
</template>
|
||||
<template v-else>{{ data.totalCount }}</template>
|
||||
</span>
|
||||
@@ -1909,7 +1922,7 @@ onMounted(() => loadAll())
|
||||
<el-dialog v-model="configVisible" title="配置原产品" width="520px" destroy-on-close>
|
||||
<div v-if="configOG" class="config-og-info">
|
||||
<div>
|
||||
<div class="config-og-name">{{ configOG.goodName }}</div>
|
||||
<div class="config-og-name" :title="configOG.goodName">{{ cleanLinkName(configOG.goodName) }}</div>
|
||||
<div class="config-og-meta">SDS ID: {{ configOG.sdsGoodId }}<template v-if="configOG.goodPrice"> · 价格: ¥{{ configOG.goodPrice }}</template></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1929,16 +1942,16 @@ onMounted(() => loadAll())
|
||||
</el-form-item>
|
||||
<el-form-item v-if="configSiblings.length" label="合并同名">
|
||||
<div class="config-merge-box">
|
||||
<div class="config-merge-tip">勾选同分类下同名(不同工厂/仓库)原产品,合并为一个商品;主源决定价格与详情。</div>
|
||||
<div class="config-merge-tip">勾选同分类下同名(不同工厂/仓库)原产品,合并为一个商品;主链接决定价格与详情。</div>
|
||||
<div class="config-merge-primary">
|
||||
<el-radio-group v-model="configPrimaryId">
|
||||
<el-radio :value="String(configOG.rawId)">主源:{{ configOG.goodName }}</el-radio>
|
||||
<el-radio v-for="s in checkedSiblingNodes" :key="s.rawId" :value="String(s.rawId)">{{ s.goodName }}</el-radio>
|
||||
<el-radio :value="String(configOG.rawId)">主链接:{{ cleanLinkName(configOG.goodName) }}</el-radio>
|
||||
<el-radio v-for="s in checkedSiblingNodes" :key="s.rawId" :value="String(s.rawId)">{{ cleanLinkName(s.goodName) }}</el-radio>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<el-checkbox-group v-model="configChecked">
|
||||
<el-checkbox v-for="s in configSiblings" :key="s.rawId" :value="String(s.rawId)">
|
||||
{{ s.goodName }}<template v-if="s.goodPrice"> · ¥{{ s.goodPrice }}</template>
|
||||
{{ cleanLinkName(s.goodName) }}<template v-if="s.goodPrice"> · ¥{{ s.goodPrice }}</template>
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
@@ -1947,17 +1960,7 @@ onMounted(() => loadAll())
|
||||
<ImageUpload v-model="configForm.goodImage" label="上传图片" />
|
||||
</el-form-item>
|
||||
<el-form-item label="标签">
|
||||
<div class="select-inline">
|
||||
<el-select v-model="configForm.tagIds" multiple filterable placeholder="选择标签" style="flex:1">
|
||||
<el-option-group v-for="g in configTagOptions" :key="g.id" :label="g.label">
|
||||
<el-option v-for="t in g.tags" :key="t.id" :label="t.tagName" :value="t.id" />
|
||||
</el-option-group>
|
||||
</el-select>
|
||||
<el-button text :icon="Plus" @click="quickCreateTag(() => { const latest = allTags[allTags.length - 1]; if (latest) configForm.tagIds.push(latest.id) })" />
|
||||
</div>
|
||||
<div v-if="configHasFamily" class="derived-tags-note">
|
||||
物流 / 工艺 / 印刷位置标签由族成员自动生成,无需手动选择
|
||||
</div>
|
||||
<div class="derived-tags-note">标签无需手动选择:保存后由系统按链接名称自动解析(印花数量 / 工艺 / 物流)</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
@@ -2002,13 +2005,13 @@ onMounted(() => loadAll())
|
||||
</el-dialog>
|
||||
|
||||
<!-- Edit Good Modal (direct edit — no separate detail step) -->
|
||||
<el-dialog v-model="editVisible" :title="editGood?.goodName || '编辑商品'" width="900px" destroy-on-close>
|
||||
<el-dialog v-model="editVisible" :title="cleanLinkName(editGood?.goodName) || '编辑商品'" width="900px" destroy-on-close>
|
||||
<!-- Origin product reference -->
|
||||
<div v-if="editGood?.originGood" class="edit-og-ref">
|
||||
<el-image v-if="editGood.originGood.goodImage" :src="editGood.originGood.goodImage" fit="cover" class="edit-og-img" />
|
||||
<div class="edit-og-meta">
|
||||
<div class="edit-og-label">{{ editIsCustom ? '自定义商品' : '关联原产品' }}</div>
|
||||
<div class="edit-og-name">{{ editGood.originGood.goodName }}</div>
|
||||
<div class="edit-og-name" :title="editGood.originGood.goodName ?? undefined">{{ cleanLinkName(editGood.originGood.goodName) }}</div>
|
||||
<div class="edit-og-sub">{{ editIsCustom ? '自定义 ID' : 'SDS ID' }}: {{ editGood.originGood.sdsGoodId }}<template v-if="editGood.originGood.goodPrice"> · ¥{{ editGood.originGood.goodPrice }}</template></div>
|
||||
<div class="edit-og-status">
|
||||
<el-tag :type="editGood.originGood.hasDetail ? 'success' : 'warning'" size="small">
|
||||
@@ -2043,20 +2046,28 @@ onMounted(() => loadAll())
|
||||
<div class="edit-merged-list">
|
||||
<div class="edit-merged-item primary">
|
||||
<span class="edit-merged-tag">主</span>
|
||||
<span>{{ editGood?.originGood?.goodName }}</span>
|
||||
<span class="edit-merged-name" :title="editGood?.originGood?.goodName ?? undefined">{{ cleanLinkName(editGood?.originGood?.goodName) }}</span>
|
||||
<span v-if="deriveLinkTagNames(editGood?.originGood?.goodName).length" class="member-chips">
|
||||
<span v-for="c in deriveLinkTagNames(editGood?.originGood?.goodName)" :key="c" class="member-chip">{{ c }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div v-for="m in editFamilyMembers" :key="m.id" class="edit-merged-item">
|
||||
<span class="edit-merged-tag sub">族</span>
|
||||
<span>{{ m.goodName }}</span>
|
||||
<el-button size="small" link type="primary" @click="promoteFamilyPrimary(m)">设为主链接</el-button>
|
||||
<el-button size="small" link type="danger" @click="removeFamilyMember(m)">移除出族</el-button>
|
||||
<span class="edit-merged-name" :title="m.goodName">{{ cleanLinkName(m.goodName) }}</span>
|
||||
<span v-if="deriveLinkTagNames(m.goodName).length" class="member-chips">
|
||||
<span v-for="c in deriveLinkTagNames(m.goodName)" :key="c" class="member-chip">{{ c }}</span>
|
||||
</span>
|
||||
<div class="edit-merged-actions">
|
||||
<el-button size="small" link type="primary" @click="promoteFamilyPrimary(m)">设为主链接</el-button>
|
||||
<el-button size="small" link type="danger" @click="removeFamilyMember(m)">移除出族</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!editFamilyLoaded && editFamilyId" class="edit-family-loading">族成员加载中…</div>
|
||||
<div v-else-if="!editFamilyId" class="edit-family-loading">暂未成族:配置合并时自动成族</div>
|
||||
</div>
|
||||
<el-select v-model="editFamilyAddKw" filterable remote :remote-method="searchFamilyCandidates"
|
||||
placeholder="搜索原产品名称添加到族" clearable style="width:100%">
|
||||
<el-option v-for="c in editFamilyCandidates" :key="c.id" :label="c.goodName" :value="String(c.id)"
|
||||
<el-option v-for="c in editFamilyCandidates" :key="c.id" :label="cleanLinkName(c.goodName) || c.goodName" :value="String(c.id)"
|
||||
@click="addFamilyMember(c)" />
|
||||
</el-select>
|
||||
</div>
|
||||
@@ -2077,20 +2088,12 @@ onMounted(() => loadAll())
|
||||
<el-cascader v-model="editForm.cascaderCategory" :options="categoryCascader as any" :props="{ checkStrictly: true }" placeholder="请选择分类" @change="onEditCascaderChange" />
|
||||
</el-form-item>
|
||||
<el-form-item label="标签">
|
||||
<div class="select-inline">
|
||||
<el-select v-model="editForm.tagIds" multiple filterable placeholder="选择标签" style="flex:1">
|
||||
<el-option-group v-for="g in editTagOptions" :key="g.id" :label="g.label">
|
||||
<el-option v-for="t in g.tags" :key="t.id" :label="t.tagName" :value="t.id" />
|
||||
</el-option-group>
|
||||
</el-select>
|
||||
<el-button text :icon="Plus" @click="quickCreateTag(() => { const latest = allTags[allTags.length - 1]; if (latest) editForm.tagIds.push(latest.id) })" />
|
||||
</div>
|
||||
<div v-if="editFamilyId && editDerivedTags.length" class="derived-tags-row">
|
||||
<span class="derived-tags-label">族自动:</span>
|
||||
<el-tag v-for="t in editDerivedTags" :key="t.id" size="small" type="info" class="derived-tag">{{ t.tagName }}</el-tag>
|
||||
</div>
|
||||
<div v-else-if="editFamilyId" class="derived-tags-note">
|
||||
物流 / 工艺 / 印刷位置标签由族成员自动生成,无需手动选择
|
||||
<div class="derived-tags-row derived-tags-readonly">
|
||||
<template v-if="editAutoTags.length">
|
||||
<el-tag v-for="t in editAutoTags" :key="t.id" size="small" class="derived-tag">{{ t.tagName }}</el-tag>
|
||||
</template>
|
||||
<span v-else class="derived-tags-note">暂无自动标签</span>
|
||||
<span class="derived-tags-note">由链接名称自动解析(印花数量 / 工艺 / 物流),不可手动编辑</span>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="editIsCustom" label="基础价格">
|
||||
@@ -2530,14 +2533,35 @@ onMounted(() => loadAll())
|
||||
.edit-family-loading { color: var(--el-text-color-placeholder); font-size: 12px; padding: 4px 0; }
|
||||
.derived-tags-note { font-size: 12px; color: var(--el-text-color-secondary); margin-top: 4px; }
|
||||
.derived-tags-row { display: flex; align-items: center; flex-wrap: wrap; gap: 4px; margin-top: 4px; }
|
||||
.derived-tags-readonly { margin-top: 0; row-gap: 2px; }
|
||||
.derived-tags-label { font-size: 12px; color: var(--el-text-color-secondary); }
|
||||
.derived-tag { pointer-events: none; }
|
||||
/* 左树定位高亮:el-tree 当前节点底色 + 行级闪烁动画 */
|
||||
.gv-left :deep(.el-tree-node.is-current > .el-tree-node__content) {
|
||||
background: var(--el-color-primary-light-8);
|
||||
}
|
||||
.good-node--flash { animation: good-node-flash 1.2s ease-in-out 2; }
|
||||
@keyframes good-node-flash {
|
||||
0%, 100% { background: transparent; }
|
||||
50% { background: var(--el-color-primary-light-7); box-shadow: inset 0 0 0 1px var(--el-color-primary-light-5); }
|
||||
}
|
||||
.edit-merged-list { display: flex; flex-direction: column; gap: 6px; margin-bottom: 8px; }
|
||||
.edit-merged-item {
|
||||
display: flex; align-items: center; gap: 8px;
|
||||
padding: 6px 10px; background: #f5f7fa; border-radius: 6px; font-size: 13px;
|
||||
}
|
||||
.edit-merged-item .el-button { margin-left: auto; }
|
||||
.edit-merged-name {
|
||||
flex: 1; min-width: 0;
|
||||
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
||||
}
|
||||
/* 操作按钮固定右缘:只有 actions 容器吃 margin-left:auto,两个按钮间距固定 */
|
||||
.edit-merged-actions { margin-left: auto; flex-shrink: 0; display: flex; align-items: center; }
|
||||
.edit-merged-actions .el-button + .el-button { margin-left: 4px; }
|
||||
.member-chips { display: flex; align-items: center; gap: 4px; flex-shrink: 0; flex-wrap: nowrap; overflow: hidden; }
|
||||
.member-chip {
|
||||
font-size: 11px; line-height: 16px; padding: 1px 7px; border-radius: 8px;
|
||||
background: #ecf5ff; color: var(--el-color-primary); white-space: nowrap;
|
||||
}
|
||||
.edit-merged-tag {
|
||||
padding: 0 5px; border-radius: 4px; font-size: 11px; line-height: 18px;
|
||||
background: var(--el-color-primary); color: #fff; flex-shrink: 0;
|
||||
|
||||
Reference in New Issue
Block a user