feat(后台分组): 分组筛选与分组列仅超管可见,非超管一律隐藏
管理员/普通用户数据已由后端裁剪到本人可访问分组,分组维度对其无意义; 账号归属分组是超管职责,故只有超管需要按分组筛选与区分。 - 隐藏分组筛选+分组列:品牌数据库、查询ASIN、最低价ASIN、去重总数据、 店铺管理、用户密钥 - 仅隐藏分组展示:店铺数据记录(筛选框+卡片行)、图片视频任务(所属分组行)、 撞款检测(明细抽屉分组列+卡片分组标签) - 弹窗分组选择保留展示但锁定:非超管仅1个可访问分组时自动选中并置灰 (查询/最低价ASIN 另联动加载该分组店铺),多分组不锁以免限制用户 - 空数据行 colspan 随列显隐动态化 - 新增 tests/align-group-visibility.test.ts 回归守卫(5 条)
This commit is contained in:
@@ -18,6 +18,8 @@ import type { ShopCredential, ShopGroupOption, ShopSummary } from './shop-dto.ts
|
||||
import OldPagination from '@/components/OldPagination.vue'
|
||||
|
||||
const session = useAdminSessionStore()
|
||||
/** 仅超管需要分组维度:非超管数据由后端裁剪到本人分组,分组筛选/列一律不展示。 */
|
||||
const isSuperAdmin = computed(() => session.isSuperAdmin)
|
||||
const loading = ref(false)
|
||||
const rows = ref<ShopSummary[]>([])
|
||||
const total = ref(0)
|
||||
@@ -42,6 +44,11 @@ const revealedPasswordCache = ref<Record<number, string>>({})
|
||||
const revealedRowId = ref<number | null>(null)
|
||||
|
||||
const groupOptions = computed(() => groups.value)
|
||||
/** 非超管仅有一个可访问分组时,弹窗分组选择锁定为该组(保留展示、不可改)。 */
|
||||
const lockedGroupId = computed<number | null>(() => {
|
||||
if (isSuperAdmin.value) return null
|
||||
return groups.value.length === 1 ? groups.value[0].id : null
|
||||
})
|
||||
|
||||
function shownPassword(row: ShopSummary): string {
|
||||
if (revealedRowId.value !== row.id) return row.passwordMasked || '******'
|
||||
@@ -123,13 +130,14 @@ function goJump() {
|
||||
function openCreate() {
|
||||
editingId.value = null
|
||||
Object.assign(form, emptyShopManageForm())
|
||||
if (lockedGroupId.value != null) form.groupId = lockedGroupId.value
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
function openEdit(row: ShopSummary) {
|
||||
editingId.value = row.id
|
||||
Object.assign(form, {
|
||||
groupId: row.groupId,
|
||||
groupId: row.groupId ?? lockedGroupId.value,
|
||||
shopName: row.shopName,
|
||||
mallName: row.mallName,
|
||||
znUsername: row.znUsername,
|
||||
@@ -214,7 +222,7 @@ onMounted(() => {
|
||||
</div>
|
||||
|
||||
<div class="form-row shop-filter-row">
|
||||
<div class="form-group" style="min-width: 180px">
|
||||
<div v-if="isSuperAdmin" class="form-group" style="min-width: 180px">
|
||||
<label>分组</label>
|
||||
<el-select v-model="filter.groupId" class="admin-filter" filterable clearable placeholder="全部分组">
|
||||
<el-option v-for="group in groupOptions" :key="group.id" :label="group.groupName" :value="group.id" />
|
||||
@@ -232,7 +240,7 @@ onMounted(() => {
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 58px">序号</th>
|
||||
<th style="width: 120px">分组</th>
|
||||
<th v-if="isSuperAdmin" style="width: 120px">分组</th>
|
||||
<th style="width: 150px">店铺名</th>
|
||||
<th style="width: 130px">店铺商城名</th>
|
||||
<th style="width: 130px">自动化账号</th>
|
||||
@@ -247,7 +255,7 @@ onMounted(() => {
|
||||
<template v-if="rows.length">
|
||||
<tr v-for="(row, index) in rows" :key="row.id">
|
||||
<td>{{ (page - 1) * pageSize + index + 1 }}</td>
|
||||
<td data-text :title="row.groupName || '—'">{{ row.groupName || '—' }}</td>
|
||||
<td v-if="isSuperAdmin" data-text :title="row.groupName || '—'">{{ row.groupName || '—' }}</td>
|
||||
<td :title="row.shopName">{{ row.shopName }}</td>
|
||||
<td :title="row.mallName || '—'">{{ row.mallName || '—' }}</td>
|
||||
<td :title="row.znUsername || '—'">{{ row.znUsername || '—' }}</td>
|
||||
@@ -273,10 +281,10 @@ onMounted(() => {
|
||||
</tr>
|
||||
</template>
|
||||
<tr v-else-if="loading">
|
||||
<td colspan="10" class="empty-tip">加载中...</td>
|
||||
<td :colspan="isSuperAdmin ? 10 : 9" class="empty-tip">加载中...</td>
|
||||
</tr>
|
||||
<tr v-else>
|
||||
<td colspan="10" class="empty-tip">暂无店铺</td>
|
||||
<td :colspan="isSuperAdmin ? 10 : 9" class="empty-tip">暂无店铺</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -287,7 +295,7 @@ onMounted(() => {
|
||||
<el-dialog v-model="dialogVisible" :title="editingId == null ? '新增店铺' : '编辑店铺'" width="600px">
|
||||
<el-form label-width="120px">
|
||||
<el-form-item label="所属分组" required>
|
||||
<el-select filterable v-model="form.groupId" placeholder="选择分组">
|
||||
<el-select filterable v-model="form.groupId" placeholder="选择分组" :disabled="lockedGroupId != null">
|
||||
<el-option v-for="group in groupOptions" :key="group.id" :label="group.groupName" :value="group.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
@@ -316,7 +324,7 @@ onMounted(() => {
|
||||
<el-dialog v-model="credentialVisible" title="店铺明文凭据" width="560px">
|
||||
<el-descriptions v-if="credential" :column="1" border>
|
||||
<el-descriptions-item label="店铺名">{{ credential.shopName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="分组">{{ credential.groupName || '—' }}</el-descriptions-item>
|
||||
<el-descriptions-item v-if="isSuperAdmin" label="分组">{{ credential.groupName || '—' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="店铺商城名">{{ credential.mallName || '—' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="账号">{{ credential.account }}</el-descriptions-item>
|
||||
<el-descriptions-item label="密码">
|
||||
|
||||
Reference in New Issue
Block a user