feat(ft546): add WCS operations console

This commit is contained in:
zgw
2026-08-11 19:25:29 +08:00
parent c80acf0d99
commit ceba465675
20 changed files with 1313 additions and 18 deletions

View File

@@ -0,0 +1,5 @@
import request from '@/utils/request'
export function listDevices(query) {
return request({ url: '/wcs/devices', method: 'get', params: query })
}

View File

@@ -0,0 +1,21 @@
import request from '@/utils/request'
export function listOrders(query) {
return request({ url: '/wcs/orders', method: 'get', params: query })
}
export function suspendOrder(orderId, reason) {
return request({
url: `/wcs/orders/${orderId}/suspend`,
method: 'post',
data: { reason }
})
}
export function resumeOrder(orderId) {
return request({ url: `/wcs/orders/${orderId}/resume`, method: 'post' })
}
export function cancelOrder(orderId) {
return request({ url: `/wcs/orders/${orderId}/cancel`, method: 'post' })
}

View File

@@ -0,0 +1,21 @@
import request from '@/utils/request'
export function listRecoveryCases(query) {
return request({ url: '/wcs/recovery/cases', method: 'get', params: query })
}
export function confirmRecovery(caseId, data) {
return request({
url: `/wcs/recovery/cases/${caseId}/confirm`,
method: 'post',
data
})
}
export function abortRecovery(caseId, reason) {
return request({
url: `/wcs/recovery/cases/${caseId}/abort`,
method: 'post',
data: { reason }
})
}

View File

@@ -0,0 +1,9 @@
import request from '@/utils/request'
export function listResources(query) {
return request({
url: '/wcs/traffic/resources',
method: 'get',
params: query
})
}

View File

@@ -0,0 +1,61 @@
<template>
<div class="app-container wcs-page">
<div class="wcs-page__bar"><h2>WCS 设备</h2><el-switch v-model="autoRefresh" active-text="自动刷新" /></div>
<el-form ref="queryForm" :model="queryParams" size="small" :inline="true">
<el-form-item label="命令状态" prop="state"><el-select v-model="queryParams.state" clearable placeholder="全部状态"><el-option v-for="state in commandStates" :key="state" :label="state" :value="state" /></el-select></el-form-item>
<el-form-item><el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">查询</el-button><el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button></el-form-item>
</el-form>
<el-table v-loading="loading" :data="rows" stripe @row-click="openDetail">
<el-table-column label="设备 ID" prop="deviceId" min-width="130" show-overflow-tooltip />
<el-table-column label="类型" prop="deviceType" width="120" />
<el-table-column label="连接" prop="connected" width="90" align="center"><template slot-scope="scope"><el-tag size="mini" :type="scope.row.connected ? 'success' : 'danger'">{{ scope.row.connected ? '在线' : '离线' }}</el-tag></template></el-table-column>
<el-table-column label="当前位置" prop="currentSite" min-width="130" />
<el-table-column label="命令状态" prop="commandState" width="130" align="center" />
<el-table-column label="当前命令" prop="currentCommandKey" min-width="180" show-overflow-tooltip />
<el-table-column label="活动告警" prop="activeAlarmCode" min-width="140"><template slot-scope="scope"><span :class="{ 'alarm-text': scope.row.activeAlarmCode }">{{ scope.row.activeAlarmCode || '-' }}</span></template></el-table-column>
<el-table-column label="更新时间" prop="updatedAt" width="180"><template slot-scope="scope">{{ parseTime(scope.row.updatedAt) }}</template></el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" />
<el-drawer title="设备详情" :visible.sync="detailOpen" size="420px"><dl v-if="current" class="wcs-detail"><template v-for="item in details"><dt :key="item.key + '-label'">{{ item.label }}</dt><dd :key="item.key">{{ current[item.key] === null || current[item.key] === undefined ? '-' : current[item.key] }}</dd></template></dl></el-drawer>
</div>
</template>
<script>
import { listDevices } from '@/api/wcs/device'
export default {
name: 'WcsDevice',
data() {
return {
loading: false, autoRefresh: true, timer: null, rows: [], total: 0, current: null, detailOpen: false,
queryParams: { pageNum: 1, pageSize: 20, state: null },
commandStates: ['CREATED', 'SENT', 'ACKNOWLEDGED', 'RUNNING', 'COMPLETED', 'FAILED', 'UNKNOWN'],
details: [
{ key: 'deviceId', label: '设备 ID' }, { key: 'deviceType', label: '类型' },
{ key: 'connected', label: '连接' }, { key: 'currentSite', label: '当前位置' },
{ key: 'currentCommandKey', label: '当前命令' }, { key: 'commandState', label: '命令状态' },
{ key: 'activeAlarmCode', label: '活动告警' }, { key: 'recoveryCaseId', label: '恢复案例' },
{ key: 'updatedAt', label: '更新时间' }
]
}
},
created() { this.getList() },
mounted() { this.timer = window.setInterval(() => { if (this.autoRefresh) this.getList(true) }, 5000) },
beforeDestroy() { window.clearInterval(this.timer) },
methods: {
getList(silent) { if (!silent) this.loading = true; return listDevices(this.queryParams).then(r => { this.rows = r.rows || []; this.total = r.total || 0 }).finally(() => { this.loading = false }) },
handleQuery() { this.queryParams.pageNum = 1; this.getList() },
resetQuery() { this.resetForm('queryForm'); this.handleQuery() },
openDetail(row) { this.current = row; this.detailOpen = true }
}
}
</script>
<style scoped>
.wcs-page__bar { display:flex; align-items:center; justify-content:space-between; min-height:58px; margin-bottom:16px; padding-bottom:12px; border-bottom:1px solid #dcdfe6; }
.wcs-page__bar h2 { margin:0; color:#17202a; font-size:20px; font-weight:600; letter-spacing:0; }
.alarm-text { color:#c0362c; font-weight:600; }
.wcs-detail { display:grid; grid-template-columns:110px minmax(0, 1fr); gap:0; margin:0 24px; border-top:1px solid #ebeef5; }
.wcs-detail dt,.wcs-detail dd { margin:0; padding:12px 8px; border-bottom:1px solid #ebeef5; overflow-wrap:anywhere; }
.wcs-detail dt { color:#667085; background:#f7f8fa; }.wcs-detail dd { color:#17202a; }
</style>

View File

@@ -0,0 +1,141 @@
<template>
<div class="app-container wcs-page">
<div class="wcs-page__bar">
<div>
<h2>WCS 订单</h2>
<span class="wcs-page__stamp">{{ refreshStamp || '-' }}</span>
</div>
<el-switch v-model="autoRefresh" active-text="自动刷新" />
</div>
<el-form ref="queryForm" :model="queryParams" size="small" :inline="true">
<el-form-item label="订单状态" prop="state">
<el-select v-model="queryParams.state" clearable placeholder="全部状态">
<el-option v-for="state in orderStates" :key="state" :label="state" :value="state" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">查询</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-table v-loading="loading" :data="rows" stripe @row-click="openDetail">
<el-table-column label="WMS 任务号" prop="externalRequestId" min-width="150" show-overflow-tooltip />
<el-table-column label="订单 ID" prop="orderId" min-width="180" show-overflow-tooltip />
<el-table-column label="订单状态" prop="orderState" width="140" align="center">
<template slot-scope="scope"><el-tag size="mini" :type="stateType(scope.row.orderState)">{{ scope.row.orderState || '-' }}</el-tag></template>
</el-table-column>
<el-table-column label="步骤" prop="stepId" min-width="130" show-overflow-tooltip />
<el-table-column label="设备" prop="deviceId" width="110" />
<el-table-column label="命令状态" prop="commandState" width="130" align="center" />
<el-table-column label="更新时间" prop="updatedAt" width="180">
<template slot-scope="scope">{{ parseTime(scope.row.updatedAt) }}</template>
</el-table-column>
<el-table-column label="操作" width="230" fixed="right" align="center">
<template slot-scope="scope">
<el-button v-if="canSuspend(scope.row)" type="text" icon="el-icon-video-pause" v-hasPermi="['wcs:order:control']" @click.stop="openSuspend(scope.row)">暂停</el-button>
<el-button v-if="scope.row.orderState === 'SUSPENDED'" type="text" icon="el-icon-video-play" v-hasPermi="['wcs:order:control']" @click.stop="resume(scope.row)">恢复</el-button>
<el-button v-if="canCancel(scope.row)" type="text" class="danger-action" icon="el-icon-circle-close" v-hasPermi="['wcs:order:control']" @click.stop="cancel(scope.row)">取消</el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" />
<el-dialog title="暂停订单" :visible.sync="suspendOpen" width="460px" append-to-body>
<el-form ref="suspendForm" :model="suspendForm" :rules="suspendRules" label-width="80px">
<el-form-item label="原因" prop="reason"><el-input v-model="suspendForm.reason" type="textarea" :rows="3" maxlength="200" show-word-limit /></el-form-item>
</el-form>
<div slot="footer"><el-button @click="suspendOpen = false">取消</el-button><el-button type="primary" @click="submitSuspend">确认暂停</el-button></div>
</el-dialog>
<el-drawer title="订单详情" :visible.sync="detailOpen" size="420px">
<dl v-if="current" class="wcs-detail">
<template v-for="item in orderDetails"><dt :key="item.key + '-label'">{{ item.label }}</dt><dd :key="item.key">{{ current[item.key] || '-' }}</dd></template>
</dl>
</el-drawer>
</div>
</template>
<script>
import { listOrders, suspendOrder, resumeOrder, cancelOrder } from '@/api/wcs/order'
export default {
name: 'WcsOrder',
data() {
return {
loading: false,
autoRefresh: true,
timer: null,
refreshStamp: '',
rows: [],
total: 0,
current: null,
detailOpen: false,
suspendOpen: false,
suspendForm: { orderId: null, reason: '' },
suspendRules: { reason: [{ required: true, message: '请输入暂停原因', trigger: 'blur' }] },
queryParams: { pageNum: 1, pageSize: 20, state: null },
orderStates: ['ACCEPTED', 'PLANNED', 'EXECUTING', 'SUSPENDED', 'CANCEL_REQUESTED', 'COMPLETED', 'FAILED', 'CANCELLED'],
orderDetails: [
{ key: 'externalRequestId', label: 'WMS 任务号' }, { key: 'orderId', label: '订单 ID' },
{ key: 'orderState', label: '订单状态' }, { key: 'workflowId', label: '工作流' },
{ key: 'stepId', label: '当前步骤' }, { key: 'deviceId', label: '设备' },
{ key: 'currentCommandKey', label: '命令键' }, { key: 'commandState', label: '命令状态' },
{ key: 'recoveryCaseId', label: '恢复案例' }, { key: 'updatedAt', label: '更新时间' }
]
}
},
created() { this.getList() },
mounted() { this.timer = window.setInterval(() => { if (this.autoRefresh) this.getList(true) }, 5000) },
beforeDestroy() { window.clearInterval(this.timer) },
methods: {
getList(silent) {
if (!silent) this.loading = true
return listOrders(this.queryParams).then(response => {
this.rows = response.rows || []
this.total = response.total || 0
this.refreshStamp = this.parseTime(new Date())
}).finally(() => { this.loading = false })
},
handleQuery() { this.queryParams.pageNum = 1; this.getList() },
resetQuery() { this.resetForm('queryForm'); this.handleQuery() },
openDetail(row) { this.current = row; this.detailOpen = true },
stateType(state) {
if (state === 'COMPLETED') return 'success'
if (state === 'FAILED' || state === 'CANCELLED') return 'danger'
if (state === 'SUSPENDED' || state === 'CANCEL_REQUESTED') return 'warning'
return ''
},
canSuspend(row) { return ['ACCEPTED', 'PLANNED', 'EXECUTING'].includes(row.orderState) },
canCancel(row) { return ['ACCEPTED', 'PLANNED', 'CANCEL_REQUESTED'].includes(row.orderState) },
openSuspend(row) { this.suspendForm = { orderId: row.orderId, reason: '' }; this.suspendOpen = true },
submitSuspend() {
this.$refs.suspendForm.validate(valid => {
if (!valid) return
suspendOrder(this.suspendForm.orderId, this.suspendForm.reason).then(() => {
this.suspendOpen = false; this.$modal.msgSuccess('订单已暂停'); this.getList()
})
})
},
resume(row) { resumeOrder(row.orderId).then(() => { this.$modal.msgSuccess('订单已恢复'); this.getList() }) },
cancel(row) {
this.$modal.confirm(`确认取消 WMS 任务 ${row.externalRequestId || row.orderId}?`).then(() => cancelOrder(row.orderId)).then(() => {
this.$modal.msgSuccess('订单已取消'); this.getList()
}).catch(() => {})
}
}
}
</script>
<style scoped>
.wcs-page__bar { display:flex; align-items:center; justify-content:space-between; min-height:58px; margin-bottom:16px; padding-bottom:12px; border-bottom:1px solid #dcdfe6; }
.wcs-page__bar h2 { margin:0 0 5px; color:#17202a; font-size:20px; font-weight:600; letter-spacing:0; }
.wcs-page__stamp { color:#7b8794; font-size:12px; }
.danger-action { color:#d14343; }
.wcs-detail { display:grid; grid-template-columns:110px minmax(0, 1fr); gap:0; margin:0 24px; border-top:1px solid #ebeef5; }
.wcs-detail dt,.wcs-detail dd { margin:0; padding:12px 8px; border-bottom:1px solid #ebeef5; overflow-wrap:anywhere; }
.wcs-detail dt { color:#667085; background:#f7f8fa; }
.wcs-detail dd { color:#17202a; }
</style>

View File

@@ -0,0 +1,102 @@
<template>
<div class="app-container wcs-page">
<div class="wcs-page__bar"><h2>恢复处置</h2><el-switch v-model="autoRefresh" active-text="自动刷新" /></div>
<el-form ref="queryForm" :model="queryParams" size="small" :inline="true">
<el-form-item label="案例状态" prop="state"><el-select v-model="queryParams.state" placeholder="案例状态"><el-option v-for="state in recoveryStates" :key="state" :label="state" :value="state" /></el-select></el-form-item>
<el-form-item><el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">查询</el-button><el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button></el-form-item>
</el-form>
<el-table v-loading="loading" :data="rows" stripe @row-click="openDetail">
<el-table-column label="案例 ID" prop="caseId" min-width="150" show-overflow-tooltip />
<el-table-column label="设备" prop="deviceId" width="110" />
<el-table-column label="命令键" prop="commandKey" min-width="180" show-overflow-tooltip />
<el-table-column label="状态" prop="state" width="120" align="center"><template slot-scope="scope"><el-tag size="mini" :type="recoveryType(scope.row.state)">{{ scope.row.state }}</el-tag></template></el-table-column>
<el-table-column label="原因" prop="reason" min-width="210" show-overflow-tooltip />
<el-table-column label="创建时间" prop="createdAt" width="180"><template slot-scope="scope">{{ parseTime(scope.row.createdAt) }}</template></el-table-column>
<el-table-column label="操作" width="170" fixed="right" align="center"><template slot-scope="scope"><template v-if="scope.row.state === 'OPEN'"><el-button type="text" icon="el-icon-circle-check" v-hasPermi="['wcs:recovery:confirm']" @click.stop="openConfirm(scope.row)">确认事实</el-button><el-button type="text" class="danger-action" icon="el-icon-circle-close" v-hasPermi="['wcs:recovery:confirm']" @click.stop="openAbort(scope.row)">中止</el-button></template></template></el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" />
<el-dialog title="确认现场事实" :visible.sync="confirmOpen" width="520px" append-to-body>
<el-form ref="confirmForm" :model="confirmForm" :rules="confirmRules" label-width="90px">
<el-form-item label="当前位置" prop="currentSite"><el-input v-model="confirmForm.currentSite" /></el-form-item>
<el-form-item label="任务号" prop="taskNumber"><el-input v-model="confirmForm.taskNumber" /></el-form-item>
<el-form-item label="设备模式" prop="deviceMode"><el-select v-model="confirmForm.deviceMode"><el-option label="AUTO" value="AUTO" /><el-option label="MANUAL" value="MANUAL" /></el-select></el-form-item>
<el-form-item label="载货状态" prop="loaded"><el-switch v-model="confirmForm.loaded" active-text="有货" inactive-text="空载" /></el-form-item>
<el-form-item label="确认原因" prop="reason"><el-input v-model="confirmForm.reason" type="textarea" :rows="3" maxlength="200" show-word-limit /></el-form-item>
</el-form>
<div slot="footer"><el-button @click="confirmOpen = false">取消</el-button><el-button type="primary" @click="submitConfirm">提交事实</el-button></div>
</el-dialog>
<el-dialog title="中止恢复案例" :visible.sync="abortOpen" width="460px" append-to-body>
<el-form ref="abortForm" :model="abortForm" :rules="abortRules" label-width="80px"><el-form-item label="原因" prop="reason"><el-input v-model="abortForm.reason" type="textarea" :rows="3" maxlength="200" show-word-limit /></el-form-item></el-form>
<div slot="footer"><el-button @click="abortOpen = false">取消</el-button><el-button type="danger" @click="submitAbort">确认中止</el-button></div>
</el-dialog>
<el-drawer title="恢复案例详情" :visible.sync="detailOpen" size="420px"><dl v-if="current" class="wcs-detail"><template v-for="item in details"><dt :key="item.key + '-label'">{{ item.label }}</dt><dd :key="item.key">{{ current[item.key] || '-' }}</dd></template></dl></el-drawer>
</div>
</template>
<script>
import { listRecoveryCases, confirmRecovery, abortRecovery } from '@/api/wcs/recovery'
export default {
name: 'WcsRecovery',
data() {
return {
loading: false, autoRefresh: true, timer: null, rows: [], total: 0, current: null, detailOpen: false,
confirmOpen: false, abortOpen: false,
queryParams: { pageNum: 1, pageSize: 20, state: 'OPEN' },
recoveryStates: ['OPEN', 'CONFIRMED', 'ABORTED'],
confirmForm: { caseId: null, currentSite: '', taskNumber: '', deviceMode: 'AUTO', loaded: false, reason: '' },
abortForm: { caseId: null, reason: '' },
confirmRules: {
currentSite: [{ required: true, message: '请输入当前位置', trigger: 'blur' }],
taskNumber: [{ required: true, message: '请输入任务号', trigger: 'blur' }],
deviceMode: [{ required: true, message: '请选择设备模式', trigger: 'change' }],
reason: [{ required: true, message: '请输入确认原因', trigger: 'blur' }]
},
abortRules: { reason: [{ required: true, message: '请输入中止原因', trigger: 'blur' }] },
details: [
{ key: 'caseId', label: '案例 ID' }, { key: 'deviceId', label: '设备' },
{ key: 'commandKey', label: '命令键' }, { key: 'state', label: '状态' },
{ key: 'reason', label: '原因' }, { key: 'createdAt', label: '创建时间' },
{ key: 'resolvedBy', label: '处置人' }, { key: 'resolvedAt', label: '处置时间' }
]
}
},
created() { this.getList() },
mounted() { this.timer = window.setInterval(() => { if (this.autoRefresh) this.getList(true) }, 5000) },
beforeDestroy() { window.clearInterval(this.timer) },
methods: {
getList(silent) { if (!silent) this.loading = true; return listRecoveryCases(this.queryParams).then(r => { this.rows = r.rows || []; this.total = r.total || 0 }).finally(() => { this.loading = false }) },
handleQuery() { this.queryParams.pageNum = 1; this.getList() },
resetQuery() { this.resetForm('queryForm'); this.queryParams.state = 'OPEN'; this.handleQuery() },
openDetail(row) { this.current = row; this.detailOpen = true },
recoveryType(state) { if (state === 'CONFIRMED') return 'success'; if (state === 'ABORTED') return 'danger'; return 'warning' },
openConfirm(row) { this.confirmForm = { caseId: row.caseId, currentSite: '', taskNumber: '', deviceMode: 'AUTO', loaded: false, reason: '' }; this.confirmOpen = true },
submitConfirm() {
this.$refs.confirmForm.validate(valid => {
if (!valid) return
const data = { ...this.confirmForm }; delete data.caseId
confirmRecovery(this.confirmForm.caseId, data).then(() => { this.confirmOpen = false; this.$modal.msgSuccess('现场事实已确认'); this.getList() })
})
},
openAbort(row) { this.abortForm = { caseId: row.caseId, reason: '' }; this.abortOpen = true },
submitAbort() {
this.$refs.abortForm.validate(valid => {
if (!valid) return
abortRecovery(this.abortForm.caseId, this.abortForm.reason).then(() => { this.abortOpen = false; this.$modal.msgSuccess('恢复案例已中止'); this.getList() })
})
}
}
}
</script>
<style scoped>
.wcs-page__bar { display:flex; align-items:center; justify-content:space-between; min-height:58px; margin-bottom:16px; padding-bottom:12px; border-bottom:1px solid #dcdfe6; }
.wcs-page__bar h2 { margin:0; color:#17202a; font-size:20px; font-weight:600; letter-spacing:0; }
.danger-action { color:#d14343; }
.wcs-detail { display:grid; grid-template-columns:110px minmax(0, 1fr); gap:0; margin:0 24px; border-top:1px solid #ebeef5; }
.wcs-detail dt,.wcs-detail dd { margin:0; padding:12px 8px; border-bottom:1px solid #ebeef5; overflow-wrap:anywhere; }
.wcs-detail dt { color:#667085; background:#f7f8fa; }.wcs-detail dd { color:#17202a; }
</style>

View File

@@ -0,0 +1,63 @@
<template>
<div class="app-container wcs-page">
<div class="wcs-page__bar"><h2>交通资源</h2><el-switch v-model="autoRefresh" active-text="自动刷新" /></div>
<el-form ref="queryForm" :model="queryParams" size="small" :inline="true">
<el-form-item label="占用状态" prop="state"><el-select v-model="queryParams.state" clearable placeholder="全部状态"><el-option v-for="state in resourceStates" :key="state" :label="state" :value="state" /></el-select></el-form-item>
<el-form-item><el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">查询</el-button><el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button></el-form-item>
</el-form>
<el-table v-loading="loading" :data="rows" stripe @row-click="openDetail">
<el-table-column label="资源键" prop="resourceKey" min-width="210" show-overflow-tooltip />
<el-table-column label="类型" prop="resourceType" width="100" />
<el-table-column label="容量" prop="capacity" width="80" align="right" />
<el-table-column label="保护数" prop="protectedCount" width="90" align="right" />
<el-table-column label="持有者" prop="holder" width="110" show-overflow-tooltip />
<el-table-column label="状态" prop="reservationState" width="130" align="center"><template slot-scope="scope"><el-tag size="mini" :type="resourceType(scope.row.reservationState)">{{ scope.row.reservationState }}</el-tag></template></el-table-column>
<el-table-column label="释放证据" prop="releaseEvidenceId" min-width="160" show-overflow-tooltip />
<el-table-column label="更新时间" prop="updatedAt" width="180"><template slot-scope="scope">{{ parseTime(scope.row.updatedAt) }}</template></el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" />
<el-drawer title="资源详情" :visible.sync="detailOpen" size="420px"><dl v-if="current" class="wcs-detail"><template v-for="item in details"><dt :key="item.key + '-label'">{{ item.label }}</dt><dd :key="item.key">{{ detailValue(current[item.key]) }}</dd></template></dl></el-drawer>
</div>
</template>
<script>
import { listResources } from '@/api/wcs/traffic'
export default {
name: 'WcsTraffic',
data() {
return {
loading: false, autoRefresh: true, timer: null, rows: [], total: 0, current: null, detailOpen: false,
queryParams: { pageNum: 1, pageSize: 20, state: null },
resourceStates: ['FREE', 'RESERVED', 'OCCUPIED', 'RELEASING', 'UNKNOWN'],
details: [
{ key: 'resourceKey', label: '资源键' }, { key: 'resourceType', label: '类型' },
{ key: 'capacity', label: '容量' }, { key: 'protectedCount', label: '保护数' },
{ key: 'holder', label: '持有者' }, { key: 'reservationState', label: '状态' },
{ key: 'orderId', label: '订单' }, { key: 'stepId', label: '步骤' },
{ key: 'vehicleId', label: '车辆' }, { key: 'fencingToken', label: '围栏令牌' },
{ key: 'releaseEvidenceId', label: '释放证据' }, { key: 'updatedAt', label: '更新时间' }
]
}
},
created() { this.getList() },
mounted() { this.timer = window.setInterval(() => { if (this.autoRefresh) this.getList(true) }, 5000) },
beforeDestroy() { window.clearInterval(this.timer) },
methods: {
getList(silent) { if (!silent) this.loading = true; return listResources(this.queryParams).then(r => { this.rows = r.rows || []; this.total = r.total || 0 }).finally(() => { this.loading = false }) },
handleQuery() { this.queryParams.pageNum = 1; this.getList() },
resetQuery() { this.resetForm('queryForm'); this.handleQuery() },
openDetail(row) { this.current = row; this.detailOpen = true },
detailValue(value) { return value === null || value === undefined || value === '' ? '-' : value },
resourceType(state) { if (state === 'FREE') return 'success'; if (state === 'UNKNOWN') return 'danger'; if (state === 'RESERVED' || state === 'RELEASING') return 'warning'; return '' }
}
}
</script>
<style scoped>
.wcs-page__bar { display:flex; align-items:center; justify-content:space-between; min-height:58px; margin-bottom:16px; padding-bottom:12px; border-bottom:1px solid #dcdfe6; }
.wcs-page__bar h2 { margin:0; color:#17202a; font-size:20px; font-weight:600; letter-spacing:0; }
.wcs-detail { display:grid; grid-template-columns:110px minmax(0, 1fr); gap:0; margin:0 24px; border-top:1px solid #ebeef5; }
.wcs-detail dt,.wcs-detail dd { margin:0; padding:12px 8px; border-bottom:1px solid #ebeef5; overflow-wrap:anywhere; }
.wcs-detail dt { color:#667085; background:#f7f8fa; }.wcs-detail dd { color:#17202a; }
</style>