feat(后台分组): 分组筛选与分组列仅超管可见,非超管一律隐藏
管理员/普通用户数据已由后端裁剪到本人可访问分组,分组维度对其无意义; 账号归属分组是超管职责,故只有超管需要按分组筛选与区分。 - 隐藏分组筛选+分组列:品牌数据库、查询ASIN、最低价ASIN、去重总数据、 店铺管理、用户密钥 - 仅隐藏分组展示:店铺数据记录(筛选框+卡片行)、图片视频任务(所属分组行)、 撞款检测(明细抽屉分组列+卡片分组标签) - 弹窗分组选择保留展示但锁定:非超管仅1个可访问分组时自动选中并置灰 (查询/最低价ASIN 另联动加载该分组店铺),多分组不锁以免限制用户 - 空数据行 colspan 随列显隐动态化 - 新增 tests/align-group-visibility.test.ts 回归守卫(5 条)
This commit is contained in:
@@ -16,6 +16,11 @@ import { isAllowedExcelImportFile } from './import-progress-model.ts'
|
||||
import { fetchShopManageGroups } from '../shop/shop-manage-api.ts'
|
||||
import type { ShopGroupOption } from '../shop/shop-dto.ts'
|
||||
import OldPagination from '@/components/OldPagination.vue'
|
||||
import { useAdminSessionStore } from '@/stores/admin-session'
|
||||
|
||||
const session = useAdminSessionStore()
|
||||
/** 仅超管需要分组维度:非超管数据由后端裁剪到本人分组,分组筛选/列一律不展示。 */
|
||||
const isSuperAdmin = computed(() => session.isSuperAdmin)
|
||||
|
||||
const loading = ref(false)
|
||||
const rows = ref<QueryAsinItem[]>([])
|
||||
@@ -23,6 +28,11 @@ const total = ref(0)
|
||||
const page = ref(1)
|
||||
const pageSize = ref(10)
|
||||
const groups = ref<ShopGroupOption[]>([])
|
||||
/** 非超管仅有一个可访问分组时,弹窗分组选择锁定为该组(保留展示、不可改)。 */
|
||||
const lockedGroupId = computed<number | null>(() => {
|
||||
if (isSuperAdmin.value) return null
|
||||
return groups.value.length === 1 ? groups.value[0].id : null
|
||||
})
|
||||
const filter = reactive<QueryAsinFilterState>(createQueryAsinFilterState())
|
||||
const jumpPage = ref('')
|
||||
const totalPages = computed(() => Math.max(1, Math.ceil(total.value / pageSize.value)))
|
||||
@@ -115,7 +125,7 @@ const creating = ref(false)
|
||||
const createAsinInputRef = ref<InstanceType<typeof import('element-plus').ElInput> | null>(null)
|
||||
|
||||
function openCreate(): void {
|
||||
createGroupId.value = null
|
||||
createGroupId.value = lockedGroupId.value
|
||||
createShopName.value = ''
|
||||
createCountry.value = ''
|
||||
createAsin.value = ''
|
||||
@@ -123,6 +133,8 @@ function openCreate(): void {
|
||||
createMsgOk.value = false
|
||||
shopNames.value = []
|
||||
createVisible.value = true
|
||||
// 非超管分组已锁定:直接联动加载该分组店铺,省去一次无意义的选择。
|
||||
if (createGroupId.value != null) void onCreateGroupChange()
|
||||
}
|
||||
|
||||
async function onCreateGroupChange(): Promise<void> {
|
||||
@@ -189,7 +201,7 @@ const importRunning = ref(false)
|
||||
const importProgress = ref('')
|
||||
|
||||
function resetImport(): void {
|
||||
importGroupId.value = null
|
||||
importGroupId.value = lockedGroupId.value
|
||||
importFile.value = null
|
||||
importProgress.value = ''
|
||||
importRunning.value = false
|
||||
@@ -348,7 +360,7 @@ onMounted(() => {
|
||||
</div>
|
||||
|
||||
<div class="form-row query-filter-row">
|
||||
<div class="form-group">
|
||||
<div v-if="isSuperAdmin" class="form-group">
|
||||
<label>分组</label>
|
||||
<el-select v-model="filter.groupId" class="admin-filter" filterable clearable placeholder="全部分组">
|
||||
<el-option v-for="group in groups" :key="group.id" :label="group.groupName" :value="group.id" />
|
||||
@@ -378,7 +390,7 @@ onMounted(() => {
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 6%">序号</th>
|
||||
<th style="width: 13%">分组</th>
|
||||
<th v-if="isSuperAdmin" style="width: 13%">分组</th>
|
||||
<th style="width: 15%">店铺名</th>
|
||||
<th style="width: 24%">ASIN</th>
|
||||
<th style="width: 12%">国家</th>
|
||||
@@ -390,7 +402,7 @@ onMounted(() => {
|
||||
<tr v-for="row in displayRows" :key="`${row.item.id}-${row.country}`">
|
||||
<template v-if="row.isFirst">
|
||||
<td :rowspan="row.rowspan">{{ row.rowNo }}</td>
|
||||
<td :rowspan="row.rowspan">{{ row.item.groupName || '—' }}</td>
|
||||
<td v-if="isSuperAdmin" :rowspan="row.rowspan">{{ row.item.groupName || '—' }}</td>
|
||||
<td :rowspan="row.rowspan">{{ row.item.shopName }}</td>
|
||||
</template>
|
||||
<td>
|
||||
@@ -409,10 +421,10 @@ onMounted(() => {
|
||||
</tr>
|
||||
</template>
|
||||
<tr v-else-if="loading">
|
||||
<td colspan="6" class="empty-tip">加载中...</td>
|
||||
<td :colspan="isSuperAdmin ? 6 : 5" class="empty-tip">加载中...</td>
|
||||
</tr>
|
||||
<tr v-else>
|
||||
<td colspan="6" class="empty-tip">暂无数据</td>
|
||||
<td :colspan="isSuperAdmin ? 6 : 5" class="empty-tip">暂无数据</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -423,7 +435,7 @@ onMounted(() => {
|
||||
<el-dialog v-model="createVisible" title="新增查询 ASIN" width="560px" :close-on-click-modal="false">
|
||||
<el-form label-position="top" @submit.prevent>
|
||||
<el-form-item label="分组">
|
||||
<el-select v-model="createGroupId" placeholder="请选择分组" filterable clearable style="width: 100%" @change="onCreateGroupChange">
|
||||
<el-select v-model="createGroupId" placeholder="请选择分组" filterable clearable style="width: 100%" :disabled="lockedGroupId != null" @change="onCreateGroupChange">
|
||||
<el-option v-for="group in groups" :key="group.id" :label="group.groupName" :value="group.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
@@ -475,7 +487,7 @@ onMounted(() => {
|
||||
<el-dialog v-model="importVisible" :title="importMode === 'add' ? '导入添加查询 ASIN' : '删除导入查询 ASIN'" width="520px">
|
||||
<el-form label-position="top">
|
||||
<el-form-item label="分组(Excel 未提供时的兜底,可选)">
|
||||
<el-select filterable v-model="importGroupId" placeholder="可不选" clearable style="width: 100%">
|
||||
<el-select filterable v-model="importGroupId" placeholder="可不选" clearable style="width: 100%" :disabled="lockedGroupId != null">
|
||||
<el-option v-for="group in groups" :key="group.id" :label="group.groupName" :value="group.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
Reference in New Issue
Block a user