doc: 处理冲突
This commit is contained in:
commit
06633c3a80
@ -11,7 +11,7 @@
|
|||||||
:model-value="memberOf(role).robotId"
|
:model-value="memberOf(role).robotId"
|
||||||
clearable
|
clearable
|
||||||
filterable
|
filterable
|
||||||
:loading="robotLoading"
|
:loading="robotLoading || autoAssigning"
|
||||||
placeholder="请选择机器人"
|
placeholder="请选择机器人"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
@change="(robotId) => changeRobot(role, robotId)"
|
@change="(robotId) => changeRobot(role, robotId)"
|
||||||
@ -23,7 +23,7 @@
|
|||||||
<el-select
|
<el-select
|
||||||
v-model="memberOf(role).devices[slot.slotKey]"
|
v-model="memberOf(role).devices[slot.slotKey]"
|
||||||
filterable
|
filterable
|
||||||
:loading="loadingRoleKey === role.roleKey"
|
:loading="autoAssigning || loadingRoleKey === role.roleKey"
|
||||||
:disabled="!memberOf(role).robotId"
|
:disabled="!memberOf(role).robotId"
|
||||||
:placeholder="memberOf(role).robotId ? '请选择设备' : '请先选择机器人'"
|
:placeholder="memberOf(role).robotId ? '请选择设备' : '请先选择机器人'"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
@ -59,9 +59,11 @@ const assignments = ref([])
|
|||||||
const robotOptions = ref([])
|
const robotOptions = ref([])
|
||||||
const devicesByRole = reactive({})
|
const devicesByRole = reactive({})
|
||||||
const robotLoading = ref(false)
|
const robotLoading = ref(false)
|
||||||
|
const autoAssigning = ref(false)
|
||||||
const loadingRoleKey = ref('')
|
const loadingRoleKey = ref('')
|
||||||
const robotLoadError = ref('')
|
const robotLoadError = ref('')
|
||||||
const deviceLoadErrors = reactive({})
|
const deviceLoadErrors = reactive({})
|
||||||
|
const onlineDevicesByRobot = new Map()
|
||||||
const loadError = computed(() => robotLoadError.value || Object.values(deviceLoadErrors).find(Boolean) || '')
|
const loadError = computed(() => robotLoadError.value || Object.values(deviceLoadErrors).find(Boolean) || '')
|
||||||
const clone = (value) => JSON.parse(JSON.stringify(value || []))
|
const clone = (value) => JSON.parse(JSON.stringify(value || []))
|
||||||
|
|
||||||
@ -74,10 +76,10 @@ const loadRobots = async () => {
|
|||||||
robotLoadError.value = ''
|
robotLoadError.value = ''
|
||||||
try {
|
try {
|
||||||
const response = await getRobotList({
|
const response = await getRobotList({
|
||||||
|
...props.robotQuery,
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
pageSize: 1000,
|
pageSize: 1000,
|
||||||
connectStatus: '1',
|
connectStatus: '1',
|
||||||
...props.robotQuery,
|
|
||||||
})
|
})
|
||||||
robotOptions.value = (response.rows || []).map((robot) => ({
|
robotOptions.value = (response.rows || []).map((robot) => ({
|
||||||
label: `${robot.robotName || robot.robotId} (${robot.robotId})`,
|
label: `${robot.robotName || robot.robotId} (${robot.robotId})`,
|
||||||
@ -98,6 +100,96 @@ const robotOptionsOf = (role) => {
|
|||||||
return robotOptions.value.filter((robot) => String(robot.currentMapId || '') === String(props.requiredMapId))
|
return robotOptions.value.filter((robot) => String(robot.currentMapId || '') === String(props.requiredMapId))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getOnlineDevices = async (robotId) => {
|
||||||
|
if (!onlineDevicesByRobot.has(robotId)) {
|
||||||
|
const request = getRobotDevicesByRobotId(robotId).then((response) => (
|
||||||
|
Array.isArray(response.data) ? response.data : []
|
||||||
|
))
|
||||||
|
onlineDevicesByRobot.set(robotId, request)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return await onlineDevicesByRobot.get(robotId)
|
||||||
|
} catch (error) {
|
||||||
|
onlineDevicesByRobot.delete(robotId)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const itemScopeOf = (role) => role.itemId == null
|
||||||
|
? 'task'
|
||||||
|
: `${role.itemId}:${role.itemOccurrence || 1}`
|
||||||
|
|
||||||
|
const planRoleDevices = (role, robotId, devices, committedUses, preferredDevices = {}) => {
|
||||||
|
const plannedUses = new Map(committedUses)
|
||||||
|
const selectedDevices = {}
|
||||||
|
for (const slot of role.deviceSlots || []) {
|
||||||
|
const matchingDevices = devices.filter((device) => (
|
||||||
|
device?.deviceId
|
||||||
|
&& (slot.deviceKind == null || Number(device.deviceKind) === Number(slot.deviceKind))
|
||||||
|
))
|
||||||
|
const preferredId = preferredDevices[slot.slotKey]
|
||||||
|
const orderedDevices = preferredId
|
||||||
|
? [
|
||||||
|
...matchingDevices.filter((device) => device.deviceId === preferredId),
|
||||||
|
...matchingDevices.filter((device) => device.deviceId !== preferredId),
|
||||||
|
]
|
||||||
|
: matchingDevices
|
||||||
|
const selected = orderedDevices.find((device) => {
|
||||||
|
const physicalKey = `${itemScopeOf(role)}:${robotId}:${device.deviceId}`
|
||||||
|
const existingUse = plannedUses.get(physicalKey)
|
||||||
|
return !existingUse || (slot.allowReuse && existingUse.allowReuse)
|
||||||
|
})
|
||||||
|
if (!selected && slot.required) return null
|
||||||
|
selectedDevices[slot.slotKey] = selected?.deviceId || ''
|
||||||
|
if (selected) {
|
||||||
|
plannedUses.set(`${itemScopeOf(role)}:${robotId}:${selected.deviceId}`, {
|
||||||
|
allowReuse: slot.allowReuse === true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { selectedDevices, plannedUses }
|
||||||
|
}
|
||||||
|
|
||||||
|
const autoAssignRoles = async () => {
|
||||||
|
let committedUses = new Map()
|
||||||
|
autoAssigning.value = true
|
||||||
|
try {
|
||||||
|
for (const role of props.roles) {
|
||||||
|
const member = memberOf(role)
|
||||||
|
const candidates = robotOptionsOf(role)
|
||||||
|
let match = null
|
||||||
|
for (const robot of candidates) {
|
||||||
|
try {
|
||||||
|
const devices = (role.deviceSlots || []).length
|
||||||
|
? await getOnlineDevices(robot.value)
|
||||||
|
: []
|
||||||
|
const preferredDevices = robot.value === member.robotId ? member.devices : {}
|
||||||
|
const plan = planRoleDevices(role, robot.value, devices, committedUses, preferredDevices)
|
||||||
|
if (plan) {
|
||||||
|
match = { robot, devices, ...plan }
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// A failed device query makes this robot ineligible for automatic assignment.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (match) {
|
||||||
|
member.robotId = match.robot.value
|
||||||
|
member.devices = match.selectedDevices
|
||||||
|
devicesByRole[role.roleKey] = match.devices
|
||||||
|
committedUses = match.plannedUses
|
||||||
|
} else {
|
||||||
|
member.robotId = ''
|
||||||
|
member.devices = Object.fromEntries((role.deviceSlots || []).map((slot) => [slot.slotKey, '']))
|
||||||
|
devicesByRole[role.roleKey] = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
emitValue()
|
||||||
|
} finally {
|
||||||
|
autoAssigning.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const optionsOf = (role, slot) => {
|
const optionsOf = (role, slot) => {
|
||||||
const member = memberOf(role)
|
const member = memberOf(role)
|
||||||
const unavailableDeviceIds = new Set((role.deviceSlots || [])
|
const unavailableDeviceIds = new Set((role.deviceSlots || [])
|
||||||
@ -122,12 +214,11 @@ const loadDevices = async (role, preserve = true) => {
|
|||||||
loadingRoleKey.value = role.roleKey
|
loadingRoleKey.value = role.roleKey
|
||||||
delete deviceLoadErrors[role.roleKey]
|
delete deviceLoadErrors[role.roleKey]
|
||||||
try {
|
try {
|
||||||
const response = await getRobotDevicesByRobotId(member.robotId)
|
devicesByRole[role.roleKey] = await getOnlineDevices(member.robotId)
|
||||||
devicesByRole[role.roleKey] = Array.isArray(response.data) ? response.data : []
|
|
||||||
;(role.deviceSlots || []).forEach((slot) => {
|
;(role.deviceSlots || []).forEach((slot) => {
|
||||||
const options = optionsOf(role, slot)
|
const options = optionsOf(role, slot)
|
||||||
if (!preserve || !options.some((option) => option.value === member.devices[slot.slotKey])) {
|
if (!preserve || !options.some((option) => option.value === member.devices[slot.slotKey])) {
|
||||||
member.devices[slot.slotKey] = options.length === 1 ? options[0].value : ''
|
member.devices[slot.slotKey] = options[0]?.value || ''
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
emitValue()
|
emitValue()
|
||||||
@ -148,6 +239,8 @@ const changeRobot = async (role, robotId) => {
|
|||||||
|
|
||||||
const initialize = async () => {
|
const initialize = async () => {
|
||||||
Object.keys(deviceLoadErrors).forEach((roleKey) => delete deviceLoadErrors[roleKey])
|
Object.keys(deviceLoadErrors).forEach((roleKey) => delete deviceLoadErrors[roleKey])
|
||||||
|
Object.keys(devicesByRole).forEach((roleKey) => delete devicesByRole[roleKey])
|
||||||
|
onlineDevicesByRobot.clear()
|
||||||
const existing = clone(props.modelValue)
|
const existing = clone(props.modelValue)
|
||||||
assignments.value = props.roles.map((role) => {
|
assignments.value = props.roles.map((role) => {
|
||||||
const existingAssignment = existing.find((item) => item.roleKey === role.roleKey)
|
const existingAssignment = existing.find((item) => item.roleKey === role.roleKey)
|
||||||
@ -170,10 +263,11 @@ const initialize = async () => {
|
|||||||
})
|
})
|
||||||
emitValue()
|
emitValue()
|
||||||
await loadRobots()
|
await loadRobots()
|
||||||
await Promise.all(props.roles.filter((role) => memberOf(role).robotId).map((role) => loadDevices(role)))
|
await autoAssignRoles()
|
||||||
}
|
}
|
||||||
|
|
||||||
const validate = () => {
|
const validate = () => {
|
||||||
|
if (robotLoading.value || autoAssigning.value) return '正在自动匹配在线机器人和设备,请稍候'
|
||||||
const physicalDeviceUses = new Map()
|
const physicalDeviceUses = new Map()
|
||||||
for (const role of props.roles) {
|
for (const role of props.roles) {
|
||||||
const member = memberOf(role)
|
const member = memberOf(role)
|
||||||
|
|||||||
@ -1,8 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<div v-if="list.length" class="step-current">
|
||||||
|
<span class="step-current__count">{{ currentStepText }}</span>
|
||||||
|
<span class="step-current__name">{{ currentStep?.waypointName || '未命名点位' }}</span>
|
||||||
|
</div>
|
||||||
<el-scrollbar ref="scrollbarRef" style="width: 100%;" class="step-scrollbar">
|
<el-scrollbar ref="scrollbarRef" style="width: 100%;" class="step-scrollbar">
|
||||||
<div class="step-wrapper" ref="stepWrapperRef">
|
<div class="step-wrapper" ref="stepWrapperRef">
|
||||||
<el-steps :active="activeStep" finish-status="success">
|
<el-steps :active="normalizedActiveStep" finish-status="success" process-status="process">
|
||||||
<el-step v-for="item in list" :key="value" :title="item.waypointName" :description="item.remark" />
|
<el-step v-for="(item, index) in list" :key="item.id || item.waypointId || item.detectItemId || index"
|
||||||
|
:title="item.waypointName" :description="item.remark" />
|
||||||
</el-steps>
|
</el-steps>
|
||||||
</div>
|
</div>
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
@ -22,6 +27,13 @@ const props = defineProps({
|
|||||||
const scrollbarRef = ref(null)
|
const scrollbarRef = ref(null)
|
||||||
const stepWrapperRef = ref(null)
|
const stepWrapperRef = ref(null)
|
||||||
|
|
||||||
|
const currentStepIndex = computed(() => Math.min(Math.max(props.activeStep, 0), Math.max(props.list.length - 1, 0)))
|
||||||
|
const currentStep = computed(() => props.list[currentStepIndex.value])
|
||||||
|
const normalizedActiveStep = computed(() => Math.min(Math.max(props.activeStep, 0), props.list.length))
|
||||||
|
const currentStepText = computed(() => props.activeStep >= props.list.length
|
||||||
|
? `已完成 ${props.list.length}/${props.list.length}`
|
||||||
|
: `当前 ${currentStepIndex.value + 1}/${props.list.length}`)
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 滚动到当前激活的步骤
|
* 滚动到当前激活的步骤
|
||||||
@ -37,52 +49,69 @@ const scrollToActiveStep = () => {
|
|||||||
|
|
||||||
// 获取所有 step 元素
|
// 获取所有 step 元素
|
||||||
const steps = stepWrapperRef.value?.querySelectorAll('.el-step')
|
const steps = stepWrapperRef.value?.querySelectorAll('.el-step')
|
||||||
if (!steps || !steps[props.activeStep]) return
|
if (!steps || !steps[currentStepIndex.value]) return
|
||||||
|
|
||||||
// 计算激活 step 的位置
|
// 计算激活 step 的位置
|
||||||
const activeEl = steps[props.activeStep]
|
const activeEl = steps[currentStepIndex.value]
|
||||||
const stepLeft = activeEl.offsetLeft
|
const stepLeft = activeEl.offsetLeft
|
||||||
const stepWidth = activeEl.offsetWidth
|
const stepWidth = activeEl.offsetWidth
|
||||||
|
|
||||||
// 让激活 step 居中显示
|
// 让激活 step 居中显示
|
||||||
const scrollLeft = stepLeft - viewWidth / 2 + stepWidth / 2
|
const scrollLeft = stepLeft - viewWidth / 2 + stepWidth / 2
|
||||||
console.log('滚动位置:', scrollLeft)
|
|
||||||
// 使用 el-scrollbar 提供的方法滚动
|
|
||||||
scrollbarRef.value.setScrollLeft(Math.max(0, scrollLeft))
|
scrollbarRef.value.setScrollLeft(Math.max(0, scrollLeft))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
setTimeout(() => {
|
watch(
|
||||||
scrollToActiveStep()
|
() => [props.activeStep, props.list.length],
|
||||||
}, 1000)
|
scrollToActiveStep,
|
||||||
|
{ immediate: true, flush: 'post' }
|
||||||
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.step-scrollbar {
|
.step-current {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
min-width: 0;
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-current__count {
|
||||||
|
flex: none;
|
||||||
|
color: #409eff;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-current__name {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-scrollbar {
|
||||||
|
margin-top: 4px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
||||||
.step-wrapper {
|
.step-wrapper {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
/* 让wrapper宽度由内容决定 */
|
width: max-content;
|
||||||
width: 100%;
|
min-width: 100%;
|
||||||
/* 至少和容器一样宽 */
|
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
/* 防止 flex 项换行 */
|
|
||||||
|
|
||||||
margin: 10px 0 20px 0;
|
margin: 10px 0 20px 0;
|
||||||
|
|
||||||
:deep(.el-steps) {
|
:deep(.el-steps) {
|
||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
width: calc(100% - 150px);
|
width: max-content;
|
||||||
|
min-width: 100%;
|
||||||
|
|
||||||
.el-step {
|
.el-step {
|
||||||
flex-shrink: 0 !important;
|
flex: 0 0 180px !important;
|
||||||
/* 禁止步骤项被压缩 */
|
min-width: 180px;
|
||||||
min-width: 150px;
|
|
||||||
/* 每个步骤的最小宽度,按需调整 */
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,8 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<div v-if="list.length" class="step-current">
|
||||||
|
<span class="step-current__count">{{ currentStepText }}</span>
|
||||||
|
<span class="step-current__name">{{ currentStep?.caseName || '未命名检测项' }}</span>
|
||||||
|
</div>
|
||||||
<el-scrollbar ref="scrollbarRef" style="width: 100%;" class="step-scrollbar">
|
<el-scrollbar ref="scrollbarRef" style="width: 100%;" class="step-scrollbar">
|
||||||
<div class="step-wrapper" ref="stepWrapperRef">
|
<div class="step-wrapper" ref="stepWrapperRef">
|
||||||
<el-steps :active="activeStep" finish-status="success">
|
<el-steps :active="normalizedActiveStep" finish-status="success" process-status="process">
|
||||||
<el-step v-for="item in list" :key="value" :title="item.caseName" :description="item.remark" />
|
<el-step v-for="(item, index) in list" :key="item.id || item.testCaseId || item.detectItemId || index"
|
||||||
|
:title="item.caseName" :description="item.remark" />
|
||||||
</el-steps>
|
</el-steps>
|
||||||
</div>
|
</div>
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
@ -22,6 +27,13 @@ const props = defineProps({
|
|||||||
const scrollbarRef = ref(null)
|
const scrollbarRef = ref(null)
|
||||||
const stepWrapperRef = ref(null)
|
const stepWrapperRef = ref(null)
|
||||||
|
|
||||||
|
const currentStepIndex = computed(() => Math.min(Math.max(props.activeStep, 0), Math.max(props.list.length - 1, 0)))
|
||||||
|
const currentStep = computed(() => props.list[currentStepIndex.value])
|
||||||
|
const normalizedActiveStep = computed(() => Math.min(Math.max(props.activeStep, 0), props.list.length))
|
||||||
|
const currentStepText = computed(() => props.activeStep >= props.list.length
|
||||||
|
? `已完成 ${props.list.length}/${props.list.length}`
|
||||||
|
: `当前 ${currentStepIndex.value + 1}/${props.list.length}`)
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 滚动到当前激活的步骤
|
* 滚动到当前激活的步骤
|
||||||
@ -37,52 +49,69 @@ const scrollToActiveStep = () => {
|
|||||||
|
|
||||||
// 获取所有 step 元素
|
// 获取所有 step 元素
|
||||||
const steps = stepWrapperRef.value?.querySelectorAll('.el-step')
|
const steps = stepWrapperRef.value?.querySelectorAll('.el-step')
|
||||||
if (!steps || !steps[props.activeStep]) return
|
if (!steps || !steps[currentStepIndex.value]) return
|
||||||
|
|
||||||
// 计算激活 step 的位置
|
// 计算激活 step 的位置
|
||||||
const activeEl = steps[props.activeStep]
|
const activeEl = steps[currentStepIndex.value]
|
||||||
const stepLeft = activeEl.offsetLeft
|
const stepLeft = activeEl.offsetLeft
|
||||||
const stepWidth = activeEl.offsetWidth
|
const stepWidth = activeEl.offsetWidth
|
||||||
|
|
||||||
// 让激活 step 居中显示
|
// 让激活 step 居中显示
|
||||||
const scrollLeft = stepLeft - viewWidth / 2 + stepWidth / 2
|
const scrollLeft = stepLeft - viewWidth / 2 + stepWidth / 2
|
||||||
console.log('滚动位置:', scrollLeft)
|
|
||||||
// 使用 el-scrollbar 提供的方法滚动
|
|
||||||
scrollbarRef.value.setScrollLeft(Math.max(0, scrollLeft))
|
scrollbarRef.value.setScrollLeft(Math.max(0, scrollLeft))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
setTimeout(() => {
|
watch(
|
||||||
scrollToActiveStep()
|
() => [props.activeStep, props.list.length],
|
||||||
}, 1000)
|
scrollToActiveStep,
|
||||||
|
{ immediate: true, flush: 'post' }
|
||||||
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.step-scrollbar {
|
.step-current {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
min-width: 0;
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-current__count {
|
||||||
|
flex: none;
|
||||||
|
color: #409eff;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-current__name {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-scrollbar {
|
||||||
|
margin-top: 4px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
||||||
.step-wrapper {
|
.step-wrapper {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
/* 让wrapper宽度由内容决定 */
|
width: max-content;
|
||||||
width: 100%;
|
min-width: 100%;
|
||||||
/* 至少和容器一样宽 */
|
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
/* 防止 flex 项换行 */
|
|
||||||
|
|
||||||
margin: 10px 0 20px 0;
|
margin: 10px 0 20px 0;
|
||||||
|
|
||||||
:deep(.el-steps) {
|
:deep(.el-steps) {
|
||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
width: calc(100% - 150px);
|
width: max-content;
|
||||||
|
min-width: 100%;
|
||||||
|
|
||||||
.el-step {
|
.el-step {
|
||||||
flex-shrink: 0 !important;
|
flex: 0 0 180px !important;
|
||||||
/* 禁止步骤项被压缩 */
|
min-width: 180px;
|
||||||
min-width: 150px;
|
|
||||||
/* 每个步骤的最小宽度,按需调整 */
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user