权限:移除业务物理删除入口
This commit is contained in:
@@ -17,21 +17,18 @@ const expectedRequests = {
|
||||
{ url: "'/master/strain/' + strainId", method: 'get' },
|
||||
{ url: "'/master/strain'", method: 'post' },
|
||||
{ url: "'/master/strain'", method: 'put' },
|
||||
{ url: "'/master/strain/' + strainIds", method: 'delete' }
|
||||
],
|
||||
'src/api/master/customer.js': [
|
||||
{ url: "'/master/customer/list'", method: 'get' },
|
||||
{ url: "'/master/customer/' + customerId", method: 'get' },
|
||||
{ url: "'/master/customer'", method: 'post' },
|
||||
{ url: "'/master/customer'", method: 'put' },
|
||||
{ url: "'/master/customer/' + customerIds", method: 'delete' }
|
||||
],
|
||||
'src/api/master/site.js': [
|
||||
{ url: "'/master/site/list'", method: 'get' },
|
||||
{ url: "'/master/site/' + siteId", method: 'get' },
|
||||
{ url: "'/master/site'", method: 'post' },
|
||||
{ url: "'/master/site'", method: 'put' },
|
||||
{ url: "'/master/site/' + siteIds", method: 'delete' },
|
||||
{ url: "'/master/site/' + siteId + '/users'", method: 'get' },
|
||||
{ url: "'/master/site/' + siteId + '/users'", method: 'put' }
|
||||
],
|
||||
@@ -40,14 +37,12 @@ const expectedRequests = {
|
||||
{ url: "'/master/line/' + lineId", method: 'get' },
|
||||
{ url: "'/master/line'", method: 'post' },
|
||||
{ url: "'/master/line'", method: 'put' },
|
||||
{ url: "'/master/line/' + lineIds", method: 'delete' }
|
||||
],
|
||||
'src/api/master/warehouse.js': [
|
||||
{ url: "'/master/warehouse/list'", method: 'get' },
|
||||
{ url: "'/master/warehouse/' + warehouseId", method: 'get' },
|
||||
{ url: "'/master/warehouse'", method: 'post' },
|
||||
{ url: "'/master/warehouse'", method: 'put' },
|
||||
{ url: "'/master/warehouse/' + warehouseIds", method: 'delete' },
|
||||
{ url: "'/master/warehouse/' + warehouseId + '/users'", method: 'get' },
|
||||
{ url: "'/master/warehouse/' + warehouseId + '/users'", method: 'put' }
|
||||
],
|
||||
@@ -57,7 +52,6 @@ const expectedRequests = {
|
||||
{ url: "'/master/pallet/' + palletId + '/qrcode'", method: 'get' },
|
||||
{ url: "'/master/pallet'", method: 'post' },
|
||||
{ url: "'/master/pallet'", method: 'put' },
|
||||
{ url: "'/master/pallet/' + palletIds", method: 'delete' }
|
||||
],
|
||||
'src/api/business/inbound.js': [
|
||||
{ url: "'/business/inbound/list'", method: 'get' },
|
||||
@@ -66,7 +60,6 @@ const expectedRequests = {
|
||||
{ url: "'/business/inbound/' + inboundId", method: 'get' },
|
||||
{ url: "'/business/inbound'", method: 'post' },
|
||||
{ url: "'/business/inbound'", method: 'put' },
|
||||
{ url: "'/business/inbound/' + inboundIds", method: 'delete' },
|
||||
{ url: "'/business/inbound/' + inboundId + '/confirm'", method: 'put' }
|
||||
],
|
||||
'src/api/business/filling.js': [
|
||||
@@ -84,7 +77,6 @@ const expectedRequests = {
|
||||
{ url: "'/business/shipment/' + shipmentId", method: 'get' },
|
||||
{ url: "'/business/shipment'", method: 'post' },
|
||||
{ url: "'/business/shipment'", method: 'put' },
|
||||
{ url: "'/business/shipment/' + shipmentIds", method: 'delete' },
|
||||
{ url: "'/business/shipment/' + shipmentId + '/confirm'", method: 'put' }
|
||||
],
|
||||
'src/api/business/usage.js': [
|
||||
@@ -134,43 +126,43 @@ const expectedReportExports = {
|
||||
const masterPageContracts = {
|
||||
strain: {
|
||||
relativePath: 'src/views/master/strain/index.vue',
|
||||
apiNames: ['addStrain', 'delStrain', 'getStrain', 'listStrain', 'updateStrain'],
|
||||
permissions: ['add', 'edit', 'list', 'query', 'remove'],
|
||||
apiNames: ['addStrain', 'getStrain', 'listStrain', 'updateStrain'],
|
||||
permissions: ['add', 'edit', 'list', 'query'],
|
||||
addPayloadKeys: ['remark', 'status', 'strainCode', 'strainName'],
|
||||
editPayloadKeys: ['remark', 'status', 'strainCode', 'strainId', 'strainName']
|
||||
},
|
||||
customer: {
|
||||
relativePath: 'src/views/master/customer/index.vue',
|
||||
apiNames: ['addCustomer', 'delCustomer', 'getCustomer', 'listCustomer', 'updateCustomer'],
|
||||
permissions: ['add', 'edit', 'list', 'query', 'remove'],
|
||||
apiNames: ['addCustomer', 'getCustomer', 'listCustomer', 'updateCustomer'],
|
||||
permissions: ['add', 'edit', 'list', 'query'],
|
||||
addPayloadKeys: ['customerCode', 'customerName', 'remark', 'status'],
|
||||
editPayloadKeys: ['customerCode', 'customerId', 'customerName', 'remark', 'status']
|
||||
},
|
||||
site: {
|
||||
relativePath: 'src/views/master/site/index.vue',
|
||||
apiNames: ['addSite', 'delSite', 'getSite', 'listSite', 'updateSite'],
|
||||
permissions: ['add', 'assign', 'edit', 'list', 'query', 'remove'],
|
||||
apiNames: ['addSite', 'getSite', 'listSite', 'updateSite'],
|
||||
permissions: ['add', 'assign', 'edit', 'list', 'query'],
|
||||
addPayloadKeys: ['address', 'customerId', 'remark', 'siteCode', 'siteName', 'status'],
|
||||
editPayloadKeys: ['address', 'customerId', 'remark', 'siteCode', 'siteId', 'siteName', 'status']
|
||||
},
|
||||
line: {
|
||||
relativePath: 'src/views/master/line/index.vue',
|
||||
apiNames: ['addLine', 'delLine', 'getLine', 'listLine', 'updateLine'],
|
||||
permissions: ['add', 'edit', 'list', 'query', 'remove'],
|
||||
apiNames: ['addLine', 'getLine', 'listLine', 'updateLine'],
|
||||
permissions: ['add', 'edit', 'list', 'query'],
|
||||
addPayloadKeys: ['lineCode', 'lineName', 'remark', 'siteId', 'status'],
|
||||
editPayloadKeys: ['lineCode', 'lineId', 'lineName', 'remark', 'siteId', 'status']
|
||||
},
|
||||
warehouse: {
|
||||
relativePath: 'src/views/master/warehouse/index.vue',
|
||||
apiNames: ['addWarehouse', 'delWarehouse', 'getWarehouse', 'listWarehouse', 'updateWarehouse'],
|
||||
permissions: ['add', 'assign', 'edit', 'list', 'query', 'remove'],
|
||||
apiNames: ['addWarehouse', 'getWarehouse', 'listWarehouse', 'updateWarehouse'],
|
||||
permissions: ['add', 'assign', 'edit', 'list', 'query'],
|
||||
addPayloadKeys: ['address', 'remark', 'status', 'warehouseCode', 'warehouseName'],
|
||||
editPayloadKeys: ['address', 'remark', 'status', 'warehouseCode', 'warehouseId', 'warehouseName']
|
||||
},
|
||||
pallet: {
|
||||
relativePath: 'src/views/master/pallet/index.vue',
|
||||
apiNames: ['addPallet', 'delPallet', 'getPallet', 'getPalletQrcode', 'listPallet', 'updatePallet'],
|
||||
permissions: ['add', 'edit', 'list', 'print', 'query', 'remove'],
|
||||
apiNames: ['addPallet', 'getPallet', 'getPalletQrcode', 'listPallet', 'updatePallet'],
|
||||
permissions: ['add', 'edit', 'list', 'print', 'query'],
|
||||
addPayloadKeys: ['capacityPackages', 'palletCode', 'remark'],
|
||||
editPayloadKeys: ['assetStatus', 'capacityPackages', 'palletId', 'remark']
|
||||
}
|
||||
@@ -182,14 +174,13 @@ const businessPageContracts = {
|
||||
apiNames: [
|
||||
'addInbound',
|
||||
'confirmInbound',
|
||||
'delInbound',
|
||||
'getInbound',
|
||||
'getInboundWarehouseOptions',
|
||||
'listInbound',
|
||||
'scanInboundPallet',
|
||||
'updateInbound'
|
||||
],
|
||||
permissions: ['add', 'confirm', 'edit', 'list', 'query', 'remove'],
|
||||
permissions: ['add', 'confirm', 'edit', 'list', 'query'],
|
||||
payloads: {
|
||||
addInbound: ['palletIds', 'warehouseId'],
|
||||
updateInbound: ['inboundId', 'palletIds', 'warehouseId'],
|
||||
@@ -217,7 +208,6 @@ const businessPageContracts = {
|
||||
apiNames: [
|
||||
'addShipment',
|
||||
'confirmShipment',
|
||||
'delShipment',
|
||||
'getShipment',
|
||||
'getShipmentSiteOptions',
|
||||
'getShipmentWarehouseOptions',
|
||||
@@ -225,7 +215,7 @@ const businessPageContracts = {
|
||||
'scanShipmentPallet',
|
||||
'updateShipment'
|
||||
],
|
||||
permissions: ['add', 'confirm', 'edit', 'list', 'query', 'remove'],
|
||||
permissions: ['add', 'confirm', 'edit', 'list', 'query'],
|
||||
payloads: {
|
||||
addShipment: ['palletIds', 'sourceWarehouseId', 'targetSiteId'],
|
||||
updateShipment: ['palletIds', 'shipmentId', 'sourceWarehouseId', 'targetSiteId'],
|
||||
@@ -361,47 +351,40 @@ const expectedButtonPermissions = [
|
||||
'master:strain:query',
|
||||
'master:strain:add',
|
||||
'master:strain:edit',
|
||||
'master:strain:remove',
|
||||
'master:customer:query',
|
||||
'master:customer:add',
|
||||
'master:customer:edit',
|
||||
'master:customer:remove',
|
||||
'master:site:query',
|
||||
'master:site:add',
|
||||
'master:site:edit',
|
||||
'master:site:remove',
|
||||
'master:site:assign',
|
||||
'master:line:query',
|
||||
'master:line:add',
|
||||
'master:line:edit',
|
||||
'master:line:remove',
|
||||
'master:warehouse:query',
|
||||
'master:warehouse:add',
|
||||
'master:warehouse:edit',
|
||||
'master:warehouse:remove',
|
||||
'master:warehouse:assign',
|
||||
'master:pallet:query',
|
||||
'master:pallet:add',
|
||||
'master:pallet:edit',
|
||||
'master:pallet:remove',
|
||||
'master:pallet:print',
|
||||
'business:inbound:query',
|
||||
'business:inbound:add',
|
||||
'business:inbound:edit',
|
||||
'business:inbound:remove',
|
||||
'business:inbound:confirm',
|
||||
'business:filling:query',
|
||||
'business:filling:add',
|
||||
'business:shipment:query',
|
||||
'business:shipment:add',
|
||||
'business:shipment:edit',
|
||||
'business:shipment:remove',
|
||||
'business:shipment:confirm',
|
||||
'business:usage:query',
|
||||
'business:usage:add',
|
||||
'business:return:query',
|
||||
'business:return:request',
|
||||
'business:return:confirm'
|
||||
'business:return:confirm',
|
||||
'system:testbatch:adopt'
|
||||
]
|
||||
|
||||
const expectedPermissions = [...expectedPagePermissions, ...expectedButtonPermissions].sort()
|
||||
@@ -536,7 +519,7 @@ function parseStaticPermissionArray(expression) {
|
||||
assert.ok(index === ranges.length - 1, '权限门禁数组禁止稀疏元素,仅允许单个尾逗号')
|
||||
continue
|
||||
}
|
||||
const literal = /^(['"])((?:master|business|report):[a-z]+:[a-z]+)\1$/.exec(item)
|
||||
const literal = /^(['"])((?:master|business|report|system):[a-z]+:[a-z]+)\1$/.exec(item)
|
||||
assert.ok(literal, '权限门禁数组禁止动态表达式或非法字面量')
|
||||
permissions.push(literal[2])
|
||||
}
|
||||
@@ -1147,8 +1130,8 @@ test('窄屏模式控件仍保留正式退出与测试批次切换入口', async
|
||||
|
||||
test('17 个页面的真实权限门禁逐项匹配独立硬编码契约', async () => {
|
||||
assert.equal(expectedPagePermissions.length, 17)
|
||||
assert.equal(expectedButtonPermissions.length, 44)
|
||||
assert.equal(new Set(expectedPermissions).size, 61)
|
||||
assert.equal(expectedButtonPermissions.length, 37)
|
||||
assert.equal(new Set(expectedPermissions).size, 54)
|
||||
const relativePaths = [
|
||||
...Object.values(masterPageContracts),
|
||||
...Object.values(businessPageContracts),
|
||||
@@ -1450,7 +1433,7 @@ test('首次入库确认失败后取消重试不丢弃原幂等键', async () =>
|
||||
|
||||
test('17 个 API 文件逐一匹配硬编码请求契约', async (t) => {
|
||||
assert.equal(Object.keys(expectedRequests).length, 17)
|
||||
assert.equal(Object.values(expectedRequests).flat().length, 74)
|
||||
assert.equal(Object.values(expectedRequests).flat().length, 66)
|
||||
|
||||
for (const [relativePath, expected] of Object.entries(expectedRequests)) {
|
||||
await t.test(relativePath, async () => {
|
||||
@@ -1647,7 +1630,6 @@ test('六个基础资料页面逐页满足 API、权限、分页和安全删除
|
||||
assertButtonsUsePermission(source, 'submitCreate', `${permissionPrefix}:add`)
|
||||
assertButtonsUsePermission(source, 'handleUpdate', `${permissionPrefix}:edit`)
|
||||
assertButtonsUsePermission(source, 'submitUpdate', `${permissionPrefix}:edit`)
|
||||
assertButtonsUsePermission(source, 'handleDelete', `${permissionPrefix}:remove`)
|
||||
|
||||
for (const button of extractButtonBlocks(source, 'handleUpdate')) {
|
||||
assert.match(
|
||||
@@ -1708,20 +1690,10 @@ test('六个基础资料页面逐页满足 API、权限、分页和安全删除
|
||||
assert.doesNotMatch(selectionColumn, /:selectable=/)
|
||||
assert.doesNotMatch(source, /function isRowSelectable\(/)
|
||||
assert.match(source, /const selectedIds = ref\(\[\]\)/)
|
||||
assert.match(source, /const removableIds = ref\(\[\]\)/)
|
||||
const selectionSource = source.slice(source.indexOf('function handleSelectionChange('), source.indexOf('function handleAdd('))
|
||||
assert.match(selectionSource, new RegExp(`selectedIds\\.value = selection\\.map\\(\\(item\\) => item\\.${idKey}\\)`))
|
||||
assert.match(selectionSource, new RegExp(`removableIds\\.value = selection\\s*\\.filter\\(\\(item\\) => item\\.canRemove === true\\)\\s*\\.map\\(\\(item\\) => item\\.${idKey}\\)`))
|
||||
assert.match(selectionSource, /single\.value = selectedIds\.value\.length !== 1/)
|
||||
assert.match(selectionSource, /multiple\.value = removableIds\.value\.length === 0/)
|
||||
assert.match(detailSource, new RegExp(`const ${idKey} = row\\.${idKey} \\|\\| selectedIds\\.value\\[0\\]`))
|
||||
assert.match(source, new RegExp(`const targetIds = row\\?\\.${idKey} \\? \\(row\\.canRemove === true \\? \\[row\\.${idKey}\\] : \\[\\]\\) : removableIds\\.value`))
|
||||
const rowDeleteButtons = extractButtonBlocks(source, 'handleDelete')
|
||||
.filter((button) => button.includes('handleDelete(scope.row)'))
|
||||
assert.ok(rowDeleteButtons.length > 0, `${resource} 缺少行删除按钮`)
|
||||
for (const button of rowDeleteButtons) {
|
||||
assert.match(button, /v-if="scope\.row\.canRemove === true"/)
|
||||
}
|
||||
const rowUpdateButtons = extractButtonBlocks(source, 'handleUpdate')
|
||||
.filter((button) => button.includes('handleUpdate(scope.row)'))
|
||||
assert.ok(rowUpdateButtons.length > 0, `${resource} 缺少行修改按钮`)
|
||||
@@ -1980,7 +1952,6 @@ test('入库页面只允许 DRAFT 变更并以 authoritative 详情和独立确
|
||||
assertButtonsUsePermission(source, 'handleAdd', 'business:inbound:add')
|
||||
assertButtonsUsePermission(source, 'submitInbound', 'business:inbound:add')
|
||||
assertButtonsUsePermission(source, 'handleUpdate', 'business:inbound:edit')
|
||||
assertButtonsUsePermission(source, 'handleDelete', 'business:inbound:remove')
|
||||
assertButtonsUsePermission(source, 'handleConfirm', 'business:inbound:confirm')
|
||||
assert.match(source, /proxy\.\$auth\.hasPermiAnd\(\['business:inbound:query', 'business:inbound:add'\]\)/)
|
||||
assert.match(source, /proxy\.\$auth\.hasPermiAnd\(\['business:inbound:query', 'business:inbound:edit'\]\)/)
|
||||
@@ -1999,9 +1970,8 @@ test('入库页面只允许 DRAFT 变更并以 authoritative 详情和独立确
|
||||
/^function handleAdd\(\)\s*\{\s*if \(!canAddInbound\.value \|\| operationPending\.value \|\| optionLoading\.value \|\| detailLoading\.value \|\| open\.value\) return/
|
||||
)
|
||||
assert.match(source, /scope\.row\.status === ['"]DRAFT['"][\s\S]*?handleUpdate\(scope\.row\)/)
|
||||
assert.match(source, /scope\.row\.status === ['"]DRAFT['"][\s\S]*?handleDelete\(scope\.row\)/)
|
||||
assert.match(source, /scope\.row\.status === ['"]DRAFT['"][\s\S]*?handleConfirm\(scope\.row\)/)
|
||||
for (const functionName of ['handleUpdate', 'handleDelete', 'handleConfirm']) {
|
||||
for (const functionName of ['handleUpdate', 'handleConfirm']) {
|
||||
assert.match(extractNamedFunction(source, functionName), /status !== ['"]DRAFT['"]/)
|
||||
}
|
||||
|
||||
@@ -2041,11 +2011,6 @@ test('入库页面只允许 DRAFT 变更并以 authoritative 详情和独立确
|
||||
)
|
||||
assert.match(submitSource, /if \(!valid\) \{\s*submitLoading\.value = false\s*return/)
|
||||
assert.match(submitSource, /\.finally\(\(\) => \{\s*submitLoading\.value = false/)
|
||||
const deleteSource = extractNamedFunction(source, 'handleDelete')
|
||||
assert.ok(
|
||||
deleteSource.indexOf('deleteLoading.value = true') < deleteSource.indexOf('proxy.$modal.confirm('),
|
||||
'入库删除必须在打开二次确认框前加锁'
|
||||
)
|
||||
|
||||
assert.deepEqual(extractSecondCallObjectKeys(source, 'scanInboundPallet'), ['warehouseId'])
|
||||
const scanSource = extractNamedFunction(source, 'handleInboundScan')
|
||||
@@ -2187,7 +2152,6 @@ test('发货页面只允许 DRAFT 变更、展示只读余量并复用确认幂
|
||||
assertButtonsUsePermission(source, 'handleAdd', 'business:shipment:add')
|
||||
assertButtonsUsePermission(source, 'submitShipment', 'business:shipment:add')
|
||||
assertButtonsUsePermission(source, 'handleUpdate', 'business:shipment:edit')
|
||||
assertButtonsUsePermission(source, 'handleDelete', 'business:shipment:remove')
|
||||
assertButtonsUsePermission(source, 'handleConfirm', 'business:shipment:confirm')
|
||||
assert.match(source, /proxy\.\$auth\.hasPermiAnd\(\['business:shipment:query', 'business:shipment:add'\]\)/)
|
||||
assert.match(source, /proxy\.\$auth\.hasPermiAnd\(\['business:shipment:query', 'business:shipment:edit'\]\)/)
|
||||
@@ -2206,9 +2170,8 @@ test('发货页面只允许 DRAFT 变更、展示只读余量并复用确认幂
|
||||
/^function handleAdd\(\)\s*\{\s*if \(!canAddShipment\.value \|\| operationPending\.value \|\| optionLoading\.value \|\| detailLoading\.value \|\| open\.value\) return/
|
||||
)
|
||||
assert.match(source, /scope\.row\.status === ['"]DRAFT['"][\s\S]*?handleUpdate\(scope\.row\)/)
|
||||
assert.match(source, /scope\.row\.status === ['"]DRAFT['"][\s\S]*?handleDelete\(scope\.row\)/)
|
||||
assert.match(source, /scope\.row\.status === ['"]DRAFT['"][\s\S]*?handleConfirm\(scope\.row\)/)
|
||||
for (const functionName of ['handleUpdate', 'handleDelete', 'handleConfirm']) {
|
||||
for (const functionName of ['handleUpdate', 'handleConfirm']) {
|
||||
assert.match(extractNamedFunction(source, functionName), /status !== ['"]DRAFT['"]/)
|
||||
}
|
||||
|
||||
@@ -2250,11 +2213,6 @@ test('发货页面只允许 DRAFT 变更、展示只读余量并复用确认幂
|
||||
)
|
||||
assert.match(submitSource, /if \(!valid\) \{\s*submitLoading\.value = false\s*return/)
|
||||
assert.match(submitSource, /\.finally\(\(\) => \{\s*submitLoading\.value = false/)
|
||||
const deleteSource = extractNamedFunction(source, 'handleDelete')
|
||||
assert.ok(
|
||||
deleteSource.indexOf('deleteLoading.value = true') < deleteSource.indexOf('proxy.$modal.confirm('),
|
||||
'发货删除必须在打开二次确认框前加锁'
|
||||
)
|
||||
|
||||
assert.deepEqual(extractSecondCallObjectKeys(source, 'scanShipmentPallet'), ['sourceWarehouseId', 'targetSiteId'])
|
||||
assert.match(source, /label="[^"]*只读[^"]*"[^>]*prop="remainingPackages"|prop="remainingPackages"[\s\S]*?不可修改/)
|
||||
@@ -2534,6 +2492,7 @@ test('测试批次管理与旧数据纳入遵守 root、权限和预览提交契
|
||||
assert.match(source, /useUserStore\(\)/, `${rootType} 必须读取 admin 可见性`)
|
||||
assert.match(source, /useTestBatchStore\(\)/, `${rootType} 必须读取测试模式`)
|
||||
assert.match(source, /userStore\.admin === true\s*&&\s*!testBatchStore\.isTestMode/, `${rootType} 纳入入口必须只在管理员正式模式显示`)
|
||||
assert.match(source, /v-hasPermi="\['system:testbatch:adopt'\]"/, `${rootType} 纳入入口必须校验批次纳入权限`)
|
||||
assert.match(source, new RegExp(`<test-batch-adoption-dialog\\b[\\s\\S]*?:root-type="['\"]${rootType}['\"][\\s\\S]*?:resource-ids="selectedIds"`))
|
||||
assert.match(source, new RegExp(`selectedIds\\.value\\s*=\\s*selection\\.map\\(\\(item\\)\\s*=>\\s*item\\.${contract.id}\\)`), `${rootType} 必须传递正整数根 ID`)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user