docs(test): 更新电表箱装配业务回归测试文档
- 将文档从代码审计任务转换为端到端业务回归测试链路 - 重新组织文档结构为链路总览、示例数据基线和详细步骤 - 添加完整的业务场景清单涵盖WMS/MES/工位终端三大系统 - 定义统一的测试数据包括产品型号、物料清单、工位派工等 - 提供详细的步骤断言和数量等式验证方法 - 建立贯穿全链路的数据流向和状态变更追踪体系
This commit is contained in:
@@ -20,16 +20,6 @@ const props = defineProps({
|
||||
// 帮助文档按菜单各自独立
|
||||
const helpData = computed(() => (props.mode === 'material' ? helpMaterial : helpZone))
|
||||
|
||||
// 默认近3个月起始日期
|
||||
function defaultStart() {
|
||||
const d = new Date()
|
||||
d.setMonth(d.getMonth() - 3)
|
||||
const y = d.getFullYear()
|
||||
const m = String(d.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(d.getDate()).padStart(2, '0')
|
||||
return `${y}-${m}-${day}`
|
||||
}
|
||||
|
||||
/* ---------- 区域维护(库位四级:区域/货架/层/位置号 4 个独立 Tab) ---------- */
|
||||
const zoneLevels = [
|
||||
{ level: 1, name: '区域', codeLabel: '区域编码', nameLabel: '区域名称' },
|
||||
@@ -54,7 +44,7 @@ const regionOptions = ref([])
|
||||
const f2Shelf = ref([]); const f3Shelf = ref([]); const f3Layer = ref([])
|
||||
const f4Shelf = ref([]); const f4Layer = ref([])
|
||||
const cShelf = ref([]); const cLayer = ref([])
|
||||
const bShelf = ref([])
|
||||
const bShelf = ref([]); const bLayer = ref([])
|
||||
const lbShelf = ref([])
|
||||
|
||||
async function picker(level, params) {
|
||||
@@ -71,6 +61,9 @@ async function loadLayerOpts(target, zoneCode, shelfNo) {
|
||||
target.value = (zoneCode && shelfNo) ? await picker(3, { zoneCode, shelfNo }) : []
|
||||
}
|
||||
|
||||
// 下拉框展开即刷新上级选项(方案2):新建区域/货架后无需切页或重进菜单即可选到最新数据。
|
||||
function onDropdownOpen(visible, loader) { if (visible) loader() }
|
||||
|
||||
async function loadNodes(level) {
|
||||
const s = nodeState[level]
|
||||
s.loading = true
|
||||
@@ -97,6 +90,8 @@ async function loadNodes(level) {
|
||||
// 进入某 Tab:首次加载列表,并确保上级下拉选项就绪
|
||||
async function onZoneTabChange(level) {
|
||||
activeZoneTab.value = level
|
||||
// 切 tab 刷新区域下拉(方案1):在「区域」Tab 新建后切到货架/层/位置号,区域下拉立即是最新。
|
||||
await loadRegions()
|
||||
const f = nodeState[level].filter
|
||||
if (level >= 2 && f.zoneCode) await loadShelfOpts(level === 2 ? f2Shelf : level === 3 ? f3Shelf : f4Shelf, f.zoneCode)
|
||||
if ((level === 3 || level === 4) && f.zoneCode && f.shelfNo) {
|
||||
@@ -140,24 +135,22 @@ const nodeSaving = ref(false)
|
||||
|
||||
function openNodeCreate(level) {
|
||||
nodeEditing.value = false
|
||||
cShelf.value = []; cLayer.value = []
|
||||
Object.assign(nodeForm, { level, id: 0, code: '', name: '', remark: '', status: '启用', regionCode: '', shelfCode: '', layerCode: '' })
|
||||
nodeDialog.value = true
|
||||
}
|
||||
function openNodeEdit(row) {
|
||||
async function openNodeEdit(row) {
|
||||
nodeEditing.value = true
|
||||
const f = { level: row.level, id: row.id, code: row.code, name: row.name || row.zoneName || '', remark: row.description || '', status: row.status || '启用', regionCode: '', shelfCode: '', layerCode: '' }
|
||||
if (row.level === 2) f.regionCode = row.zoneCode
|
||||
if (row.level === 3) { f.regionCode = row.zoneCode; f.shelfCode = row.shelfNo }
|
||||
if (row.level === 4) { f.regionCode = row.zoneCode; f.shelfCode = row.shelfNo; f.layerCode = row.layerNo }
|
||||
Object.assign(nodeForm, f)
|
||||
// 编辑态必须预加载上级下拉选项:否则「所属货架 / 所属层」无选项、且当前值无法回显(用户报的编辑无下拉数据)
|
||||
if (row.level >= 3) await loadShelfOpts(cShelf, f.regionCode)
|
||||
if (row.level === 4) await loadLayerOpts(cLayer, f.regionCode, f.shelfCode)
|
||||
nodeDialog.value = true
|
||||
}
|
||||
function nodeParentCode() {
|
||||
if (nodeForm.level === 1) return ''
|
||||
if (nodeForm.level === 2) return nodeForm.regionCode
|
||||
if (nodeForm.level === 3) return nodeForm.shelfCode
|
||||
return nodeForm.layerCode || nodeForm.shelfCode
|
||||
}
|
||||
async function onNodeRegionChange() {
|
||||
nodeForm.shelfCode = ''
|
||||
nodeForm.layerCode = ''
|
||||
@@ -171,9 +164,10 @@ async function onNodeShelfChange() {
|
||||
async function saveNode() {
|
||||
if (!nodeForm.code) { ElMessage.warning('编码必填'); return }
|
||||
if (nodeForm.level === 1 && !nodeForm.name) { ElMessage.warning('区域名称必填'); return }
|
||||
if (nodeForm.level === 2 && !nodeForm.regionCode) { ElMessage.warning('请选择所属区域'); return }
|
||||
if (nodeForm.level === 3 && !nodeForm.shelfCode) { ElMessage.warning('请选择所属货架'); return }
|
||||
if (nodeForm.level === 4 && !nodeForm.shelfCode) { ElMessage.warning('请选择所属货架'); return }
|
||||
// 父级必须不能为空(逐级必选):货架必选区域;层必选区域+货架;位置号必选区域+货架+层
|
||||
if (nodeForm.level >= 2 && !nodeForm.regionCode) { ElMessage.warning('请选择所属区域'); return }
|
||||
if (nodeForm.level >= 3 && !nodeForm.shelfCode) { ElMessage.warning('请选择所属货架'); return }
|
||||
if (nodeForm.level === 4 && !nodeForm.layerCode) { ElMessage.warning('请选择所属层'); return }
|
||||
const okSave = await confirmAction(
|
||||
nodeEditing.value ? `确认保存【${nodeForm.code}】的修改?` : `确认新增【${nodeForm.code}${nodeForm.name ? ' ' + nodeForm.name : ''}】?`,
|
||||
nodeEditing.value ? '编辑确认' : '新增确认'
|
||||
@@ -187,13 +181,16 @@ async function saveNode() {
|
||||
} else {
|
||||
await request.post('/zone/node/create', {
|
||||
level: nodeForm.level, code: nodeForm.code, name: nodeForm.name,
|
||||
parentCode: nodeParentCode(), remark: nodeForm.remark
|
||||
zoneCode: nodeForm.regionCode, shelfNo: nodeForm.shelfCode, layerNo: nodeForm.layerCode,
|
||||
remark: nodeForm.remark
|
||||
})
|
||||
ElMessage.success('新增成功')
|
||||
}
|
||||
nodeDialog.value = false
|
||||
zoneTabLoaded[nodeForm.level] = false
|
||||
await loadNodes(nodeForm.level)
|
||||
// 新建/修改区域后立刻刷新区域下拉,保证本页及其他 Tab 马上能选到新区域。
|
||||
if (nodeForm.level === 1) await loadRegions()
|
||||
} finally {
|
||||
nodeSaving.value = false
|
||||
}
|
||||
@@ -201,22 +198,31 @@ async function saveNode() {
|
||||
|
||||
/* ---------- 区域维护:位置号批量生成 ---------- */
|
||||
const batchDialog = ref(false)
|
||||
const batchForm = reactive({ zoneCode: '', shelfNo: '', layerStart: 1, layerEnd: 1, posStart: 1, posEnd: 1 })
|
||||
const batchForm = reactive({ zoneCode: '', shelfNo: '', layerNo: '', posStart: 1, posEnd: 1 })
|
||||
const batchPreview = ref([])
|
||||
const batchPreviewing = ref(false)
|
||||
const batchSubmitting = ref(false)
|
||||
|
||||
function openBatchGen() {
|
||||
Object.assign(batchForm, { zoneCode: '', shelfNo: '', layerStart: 1, layerEnd: 1, posStart: 1, posEnd: 1 })
|
||||
Object.assign(batchForm, { zoneCode: '', shelfNo: '', layerNo: '', posStart: 1, posEnd: 1 })
|
||||
bShelf.value = []; bLayer.value = []
|
||||
batchPreview.value = []
|
||||
batchDialog.value = true
|
||||
}
|
||||
async function onBatchRegionChange() {
|
||||
batchForm.shelfNo = ''
|
||||
batchForm.layerNo = ''
|
||||
bLayer.value = []
|
||||
await loadShelfOpts(bShelf, batchForm.zoneCode)
|
||||
}
|
||||
// 选定货架后加载该货架下已存在的层(只能选具体层,批量生成只生成位置、不创建父级层)
|
||||
async function onBatchShelfChange() {
|
||||
batchForm.layerNo = ''
|
||||
await loadLayerOpts(bLayer, batchForm.zoneCode, batchForm.shelfNo)
|
||||
}
|
||||
async function previewBatch() {
|
||||
if (!batchForm.zoneCode || !batchForm.shelfNo) { ElMessage.warning('请选择区域与货架'); return }
|
||||
if (!batchForm.layerNo) { ElMessage.warning('请选择具体的层'); return }
|
||||
batchPreviewing.value = true
|
||||
try {
|
||||
const data = await request.post('/zone/batch-generate', { ...batchForm, preview: true })
|
||||
@@ -286,7 +292,7 @@ const materials = ref([])
|
||||
const materialTotal = ref(0)
|
||||
const materialLoading = ref(false)
|
||||
const matFilter = reactive({
|
||||
materialCode: '', name: '', spec: '', manageMode: '', itemType: '', startDate: defaultStart(), endDate: ''
|
||||
materialCode: '', name: '', spec: '', manageMode: '', itemType: '', startDate: '', endDate: ''
|
||||
})
|
||||
const matPage = reactive({ current: 1, size: 10 })
|
||||
|
||||
@@ -573,7 +579,7 @@ async function onMaterialFile(e) {
|
||||
<el-tab-pane :name="2" label="货架">
|
||||
<el-form inline style="margin-bottom:14px" @submit.prevent="searchNodes(2)">
|
||||
<el-form-item label="区域">
|
||||
<el-select v-model="nodeState[2].filter.zoneCode" placeholder="全部区域" clearable filterable style="width:160px" @change="onFilterRegion(2)">
|
||||
<el-select v-model="nodeState[2].filter.zoneCode" placeholder="全部区域" clearable filterable style="width:160px" @change="onFilterRegion(2)" @visible-change="(v) => onDropdownOpen(v, loadRegions)">
|
||||
<el-option v-for="r in regionOptions" :key="r.code" :value="r.code" :label="r.code + (r.zoneName ? ' '+r.zoneName : '')" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
@@ -624,12 +630,12 @@ async function onMaterialFile(e) {
|
||||
<el-tab-pane :name="3" label="层">
|
||||
<el-form inline style="margin-bottom:14px" @submit.prevent="searchNodes(3)">
|
||||
<el-form-item label="区域">
|
||||
<el-select v-model="nodeState[3].filter.zoneCode" placeholder="全部区域" clearable filterable style="width:150px" @change="onFilterRegion(3)">
|
||||
<el-select v-model="nodeState[3].filter.zoneCode" placeholder="全部区域" clearable filterable style="width:150px" @change="onFilterRegion(3)" @visible-change="(v) => onDropdownOpen(v, loadRegions)">
|
||||
<el-option v-for="r in regionOptions" :key="r.code" :value="r.code" :label="r.code + (r.zoneName ? ' '+r.zoneName : '')" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="货架">
|
||||
<el-select v-model="nodeState[3].filter.shelfNo" placeholder="全部货架" clearable filterable style="width:130px" @change="onFilterShelf(3)">
|
||||
<el-select v-model="nodeState[3].filter.shelfNo" placeholder="全部货架" clearable filterable style="width:130px" @change="onFilterShelf(3)" @visible-change="(v) => onDropdownOpen(v, () => loadShelfOpts(f3Shelf, nodeState[3].filter.zoneCode))">
|
||||
<el-option v-for="s in f3Shelf" :key="s.code" :value="s.code" :label="s.code" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
@@ -674,17 +680,17 @@ async function onMaterialFile(e) {
|
||||
<el-tab-pane :name="4" label="位置号">
|
||||
<el-form inline style="margin-bottom:14px" @submit.prevent="searchNodes(4)">
|
||||
<el-form-item label="区域">
|
||||
<el-select v-model="nodeState[4].filter.zoneCode" placeholder="全部区域" clearable filterable style="width:150px" @change="onFilterRegion(4)">
|
||||
<el-select v-model="nodeState[4].filter.zoneCode" placeholder="全部区域" clearable filterable style="width:150px" @change="onFilterRegion(4)" @visible-change="(v) => onDropdownOpen(v, loadRegions)">
|
||||
<el-option v-for="r in regionOptions" :key="r.code" :value="r.code" :label="r.code + (r.zoneName ? ' '+r.zoneName : '')" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="货架">
|
||||
<el-select v-model="nodeState[4].filter.shelfNo" placeholder="全部货架" clearable filterable style="width:130px" @change="onFilterShelf(4)">
|
||||
<el-select v-model="nodeState[4].filter.shelfNo" placeholder="全部货架" clearable filterable style="width:130px" @change="onFilterShelf(4)" @visible-change="(v) => onDropdownOpen(v, () => loadShelfOpts(f4Shelf, nodeState[4].filter.zoneCode))">
|
||||
<el-option v-for="s in f4Shelf" :key="s.code" :value="s.code" :label="s.code" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="层">
|
||||
<el-select v-model="nodeState[4].filter.layerNo" placeholder="全部层" clearable filterable style="width:110px" @change="searchNodes(4)">
|
||||
<el-select v-model="nodeState[4].filter.layerNo" placeholder="全部层" clearable filterable style="width:110px" @change="searchNodes(4)" @visible-change="(v) => onDropdownOpen(v, () => loadLayerOpts(f4Layer, nodeState[4].filter.zoneCode, nodeState[4].filter.shelfNo))">
|
||||
<el-option v-for="l in f4Layer" :key="l.code" :value="l.code" :label="'层 '+l.code" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
@@ -730,25 +736,24 @@ async function onMaterialFile(e) {
|
||||
<!-- 新增/编辑节点弹窗 -->
|
||||
<el-dialog v-model="nodeDialog" :title="(nodeEditing ? '编辑' : '新增') + (zoneLevels.find(z=>z.level===nodeForm.level)?.name || '')" width="560px" append-to-body destroy-on-close>
|
||||
<el-form label-width="90px">
|
||||
<el-form-item :label="zoneLevels.find(z=>z.level===nodeForm.level)?.codeLabel" required>
|
||||
<el-input v-model="nodeForm.code" :placeholder="zoneLevels.find(z=>z.level===nodeForm.level)?.codeLabel" :disabled="nodeEditing && nodeForm.level===1" clearable
|
||||
@input="nodeForm.code = (nodeForm.code || '').toUpperCase()" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="nodeForm.level === 2 || nodeForm.level === 3 || nodeForm.level === 4" label="所属区域" required>
|
||||
<el-select v-model="nodeForm.regionCode" placeholder="请选择区域" filterable style="width:100%" @change="onNodeRegionChange">
|
||||
<el-select v-model="nodeForm.regionCode" placeholder="请选择区域" filterable style="width:100%" @change="onNodeRegionChange" @visible-change="(v) => onDropdownOpen(v, loadRegions)">
|
||||
<el-option v-for="r in regionOptions" :key="r.code" :value="r.code" :label="r.code + (r.zoneName ? ' '+r.zoneName : '')" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="nodeForm.level === 3 || nodeForm.level === 4" label="所属货架" required>
|
||||
<el-select v-model="nodeForm.shelfCode" placeholder="请选择货架" filterable :disabled="!nodeForm.regionCode" style="width:100%" @change="onNodeShelfChange">
|
||||
<el-select v-model="nodeForm.shelfCode" placeholder="请选择货架" filterable :disabled="!nodeForm.regionCode" style="width:100%" @change="onNodeShelfChange" @visible-change="(v) => onDropdownOpen(v, () => loadShelfOpts(cShelf, nodeForm.regionCode))">
|
||||
<el-option v-for="s in cShelf" :key="s.code" :value="s.code" :label="s.code" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="nodeForm.level === 4" label="所属层">
|
||||
<el-select v-model="nodeForm.layerCode" placeholder="选填(留空=货架默认位置)" filterable :disabled="!nodeForm.shelfCode" style="width:100%">
|
||||
<el-form-item v-if="nodeForm.level === 4" label="所属层" required>
|
||||
<el-select v-model="nodeForm.layerCode" placeholder="请选择层" filterable :disabled="!nodeForm.shelfCode" style="width:100%" @visible-change="(v) => onDropdownOpen(v, () => loadLayerOpts(cLayer, nodeForm.regionCode, nodeForm.shelfCode))">
|
||||
<el-option v-for="l in cLayer" :key="l.code" :value="l.code" :label="'层 '+l.code" />
|
||||
</el-select>
|
||||
<div style="color:#909399;font-size:12px;margin-top:4px">留空则作为该货架的默认位置(无层)</div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="zoneLevels.find(z=>z.level===nodeForm.level)?.codeLabel" required>
|
||||
<el-input v-model="nodeForm.code" :placeholder="zoneLevels.find(z=>z.level===nodeForm.level)?.codeLabel" :disabled="nodeEditing && nodeForm.level===1" clearable
|
||||
@input="nodeForm.code = (nodeForm.code || '').toUpperCase()" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="zoneLevels.find(z=>z.level===nodeForm.level)?.nameLabel">
|
||||
<el-input v-model="nodeForm.name" :placeholder="(zoneLevels.find(z=>z.level===nodeForm.level)?.nameLabel || '') + '(可空)'" clearable />
|
||||
@@ -773,19 +778,20 @@ async function onMaterialFile(e) {
|
||||
<el-dialog v-model="batchDialog" title="批量生成位置号" width="720px" append-to-body destroy-on-close>
|
||||
<el-form label-width="90px">
|
||||
<el-form-item label="区域" required>
|
||||
<el-select v-model="batchForm.zoneCode" placeholder="请选择区域" filterable style="width:100%" @change="onBatchRegionChange">
|
||||
<el-select v-model="batchForm.zoneCode" placeholder="请选择区域" filterable style="width:100%" @change="onBatchRegionChange" @visible-change="(v) => onDropdownOpen(v, loadRegions)">
|
||||
<el-option v-for="r in regionOptions" :key="r.code" :value="r.code" :label="r.code + (r.zoneName ? ' '+r.zoneName : '')" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="货架" required>
|
||||
<el-select v-model="batchForm.shelfNo" placeholder="请选择货架" filterable :disabled="!batchForm.zoneCode" style="width:100%">
|
||||
<el-select v-model="batchForm.shelfNo" placeholder="请选择货架" filterable :disabled="!batchForm.zoneCode" style="width:100%" @change="onBatchShelfChange" @visible-change="(v) => onDropdownOpen(v, () => loadShelfOpts(bShelf, batchForm.zoneCode))">
|
||||
<el-option v-for="s in bShelf" :key="s.code" :value="s.code" :label="s.code" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="层范围">
|
||||
<el-input-number v-model="batchForm.layerStart" :min="0" :step="1" step-strictly controls-position="right" style="width:120px" />
|
||||
<span style="margin:0 8px">至</span>
|
||||
<el-input-number v-model="batchForm.layerEnd" :min="0" :step="1" step-strictly controls-position="right" style="width:120px" />
|
||||
<el-form-item label="层" required>
|
||||
<el-select v-model="batchForm.layerNo" placeholder="请选择具体的层" filterable :disabled="!batchForm.shelfNo" style="width:100%" @visible-change="(v) => onDropdownOpen(v, () => loadLayerOpts(bLayer, batchForm.zoneCode, batchForm.shelfNo))">
|
||||
<el-option v-for="l in bLayer" :key="l.code" :value="l.code" :label="'层 '+l.code" />
|
||||
</el-select>
|
||||
<div style="color:#909399;font-size:12px;margin-top:4px">只能选择已存在的层;批量生成只在该层下生成位置号,不会创建父级层。</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="位置范围">
|
||||
<el-input-number v-model="batchForm.posStart" :min="0" :step="1" step-strictly controls-position="right" style="width:120px" />
|
||||
@@ -814,12 +820,12 @@ async function onMaterialFile(e) {
|
||||
<el-dialog v-model="layerBatchDialog" title="批量生成层" width="640px" append-to-body destroy-on-close>
|
||||
<el-form label-width="90px">
|
||||
<el-form-item label="区域" required>
|
||||
<el-select v-model="layerBatchForm.zoneCode" placeholder="请选择区域" filterable style="width:100%" @change="onLayerBatchRegionChange">
|
||||
<el-select v-model="layerBatchForm.zoneCode" placeholder="请选择区域" filterable style="width:100%" @change="onLayerBatchRegionChange" @visible-change="(v) => onDropdownOpen(v, loadRegions)">
|
||||
<el-option v-for="r in regionOptions" :key="r.code" :value="r.code" :label="r.code + (r.zoneName ? ' '+r.zoneName : '')" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="货架" required>
|
||||
<el-select v-model="layerBatchForm.shelfNo" placeholder="请选择货架" filterable :disabled="!layerBatchForm.zoneCode" style="width:100%">
|
||||
<el-select v-model="layerBatchForm.shelfNo" placeholder="请选择货架" filterable :disabled="!layerBatchForm.zoneCode" style="width:100%" @visible-change="(v) => onDropdownOpen(v, () => loadShelfOpts(lbShelf, layerBatchForm.zoneCode))">
|
||||
<el-option v-for="s in lbShelf" :key="s.code" :value="s.code" :label="s.code" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
@@ -887,7 +893,7 @@ async function onMaterialFile(e) {
|
||||
<el-button v-if="can('material:create')" type="success" @click="openMatCreate">新增物料</el-button>
|
||||
<el-button v-if="can('material:import')" type="warning" :loading="matImporting" @click="pickMaterialFile">导入</el-button>
|
||||
<el-button v-if="can('material:export')" :loading="matExporting" @click="exportMaterials">导出</el-button>
|
||||
<el-tooltip v-if="can('material:import')" content="Excel 模板(首行表头):图号 | 名称 | 规格 | 单位 | 类型(结构件/电气件) | 说明(除「说明」外均为必填)">
|
||||
<el-tooltip v-if="can('material:import')" content="Excel 首行表头按名称识别(列顺序可变、可含多余列):图号 | 名称 | 规格 | 单位 | 类型(结构件/电气件) | 品类(原材料/半成品/成品/其他,可选) | 说明(除「说明」「品类」外均为必填)">
|
||||
<el-link type="info" :underline="false" style="margin-left:4px">导入说明</el-link>
|
||||
</el-tooltip>
|
||||
<input ref="matFileRef" type="file" accept=".xlsx,.xls" style="display:none" @change="onMaterialFile" />
|
||||
|
||||
Reference in New Issue
Block a user