员工管理穿梭框改造:显示全部机床+状态色点(绿在线/灰离线/红停用)+图例说明

main
haoliang 1 week ago
parent 8a18fdc998
commit e91fc70ca7

@ -78,14 +78,16 @@
|------|------|------|--------|------|------| |------|------|------|--------|------|------|
| 工号 | el-input maxlength=50 | 是 | - | 必填+唯一性(新增时) | - | | 工号 | el-input maxlength=50 | 是 | - | 必填+唯一性(新增时) | - |
| 姓名 | el-input maxlength=50 | 是 | - | 必填 | - | | 姓名 | el-input maxlength=50 | 是 | - | 必填 | - |
| 绑定机床 | el-transfer 穿梭框 | 否 | 空 | - | 左侧显示可选机床,右侧显示已绑定机床 | | 绑定机床 | el-transfer 穿梭框 | 否 | 空 | - | 左侧显示全部机床(带状态色点),右侧显示已绑定机床 |
说明: 说明:
- 弹窗中不设"状态"字段,新增默认启用 - 弹窗中不设"状态"字段,新增默认启用
- 绑定机床使用穿梭框el-transfer左侧列出可选机床右侧显示已绑定机床 - 绑定机床使用穿梭框el-transfer左侧列出全部机床右侧显示已绑定机床
- 穿梭框数据查询cnc_machine中未绑定其他工人的机床uk_machine约束确保一对一编辑时含当前已绑定的 - 穿梭框数据源调用机床列表API`/admin/machine`)获取全部机床,不按状态筛选
- 机床不论在线/离线状态均可绑定(绑定关系不受机床在线状态限制) - 每项机床名称前显示状态色点:🟢在线(绿色)、⚪离线(灰色)、🔴停用(红色)
- 穿梭框每项显示格式:`device_code (机床名称) - 车间` - 穿梭框下方显示图例:绿色=在线 | 灰色=离线 | 红色=停用
- 机床不论在线/离线/停用状态均可绑定(绑定关系不受机床状态限制)
- 穿梭框每项显示格式:`[状态点] 机床名称 (device_code) - 车间`
### 7. 状态机 ### 7. 状态机
@ -123,7 +125,7 @@
| 编辑工人 | §3.6 #4 | 编辑弹窗保存 | | 编辑工人 | §3.6 #4 | 编辑弹窗保存 |
| 删除工人 | §3.6 #5 | 删除操作 | | 删除工人 | §3.6 #5 | 删除操作 |
| 启停工人 | §3.6 #6 | 启停切换 | | 启停工人 | §3.6 #6 | 启停切换 |
| 可绑定机床 | §3.6 #10 | 弹窗中机床下拉 | | 机床列表 | §3.6 机床列表 | 弹窗中穿梭框数据源(全部机床,含在线/离线/停用状态) |
#### 数据结构 #### 数据结构
@ -145,16 +147,20 @@ Response
{ "code": 0, "message": "success", "data": { "id": 4, "name": "赵六" }} { "code": 0, "message": "success", "data": { "id": 4, "name": "赵六" }}
``` ```
**可绑定机床§3.6 #10** **机床列表(穿梭框数据源**
Response Response
```json ```json
{ "code": 0, "data": { "items": [ { "code": 0, "data": { "items": [
{ "id": 5, "name": "东-2.5", "deviceCode": "siemens_2.5", "workshopName": "B栋" }, { "id": 1, "name": "西-1.8", "deviceCode": "fanake_1.8", "workshopName": "A栋", "isOnline": 1, "isEnabled": 1 },
{ "id": 8, "name": "北-4.1", "deviceCode": "fanake_4.1", "workshopName": "C栋" } { "id": 2, "name": "西-1.10", "deviceCode": "fanake_1.10", "workshopName": "A栋", "isOnline": 1, "isEnabled": 1 },
]}} { "id": 3, "name": "东-2.0", "deviceCode": "fanake_2.0", "workshopName": "B栋", "isOnline": 0, "isEnabled": 1 },
{ "id": 4, "name": "东-2.5", "deviceCode": "siemens_2.5", "workshopName": "B栋", "isOnline": 0, "isEnabled": 0 }
], "total": 4, "page": 1, "pageSize": 999 }}
``` ```
> 调用机床列表API`GET /admin/machine?pageSize=999`获取全部机床。前端根据isOnline和isEnabled渲染状态色点。
**编辑工人§3.6 #4/ 删除工人§3.6 #5/ 启停工人§3.6 #6** **编辑工人§3.6 #4/ 删除工人§3.6 #5/ 启停工人§3.6 #6**
Response Response
@ -170,4 +176,4 @@ Response
|---------|---------|------| |---------|---------|------|
| 工人列表(含分页) | cnc_worker | 主表需JOIN绑定机床数+机床名称 | | 工人列表(含分页) | cnc_worker | 主表需JOIN绑定机床数+机床名称 |
| 工号唯一性校验 | cnc_worker.uk_code | 新增时实时校验 | | 工号唯一性校验 | cnc_worker.uk_code | 新增时实时校验 |
| 可绑定机床下拉 | cnc_machine LEFT JOIN cnc_worker_machine | 查未绑定工人(not exists in cnc_worker_machine)的机床 | | 可绑定机床下拉 | cnc_machine | 调用机床列表API获取全部机床前端按isOnline/isEnabled渲染状态色点 |

@ -48,11 +48,25 @@
<el-transfer <el-transfer
v-model="form.machineIds" v-model="form.machineIds"
:data="availableMachines" :data="availableMachines"
:titles="['可选机床', '已绑定']" :titles="['全部机床', '已绑定']"
:props="{ key: 'id', label: 'label' }" :props="{ key: 'id', label: 'label' }"
filterable filterable
filter-placeholder="搜索机床" filter-placeholder="搜索机床"
/> >
<template #default="{ option }">
<span :class="['machine-transfer-item', getStatusClass(option)]">
<span class="status-dot"></span>
{{ option.name }}
<template v-if="option.deviceCode"> ({{ option.deviceCode }})</template>
<template v-if="option.workshopName"> - {{ option.workshopName }}</template>
</span>
</template>
</el-transfer>
<div class="transfer-legend">
<span><span class="status-dot online"></span>在线</span>
<span><span class="status-dot offline"></span>离线</span>
<span><span class="status-dot disabled"></span>停用</span>
</div>
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer><el-button @click="dialogVisible=false"></el-button><el-button type="primary" :loading="submitting" @click="handleSubmit"></el-button></template> <template #footer><el-button @click="dialogVisible=false"></el-button><el-button type="primary" :loading="submitting" @click="handleSubmit"></el-button></template>
@ -72,7 +86,8 @@ const loading=ref(false);const tableData=ref<Worker[]>([]);const selectedRows=re
// //
const pagination=ref<{ currentPage: number; pageSize: number; total: number }>({ currentPage: 1, pageSize: 20, total: 0 }) const pagination=ref<{ currentPage: number; pageSize: number; total: number }>({ currentPage: 1, pageSize: 20, total: 0 })
const dialogVisible=ref(false);const submitting=ref(false);const editingId=ref<number|null>(null) const dialogVisible=ref(false);const submitting=ref(false);const editingId=ref<number|null>(null)
const availableMachines=ref<{ id: number; label: string; name: string }[]>([]) interface MachineItem { id: number; name: string; label: string; deviceCode?: string; workshopName?: string; isOnline: number; isEnabled: number }
const availableMachines=ref<MachineItem[]>([])
const query=reactive({isEnabled:undefined as number | undefined,keyword:''}) const query=reactive({isEnabled:undefined as number | undefined,keyword:''})
const form=reactive({code:'',name:'',machineIds:[] as number[]}) const form=reactive({code:'',name:'',machineIds:[] as number[]})
const workerForm=ref<FormInstance | null>(null) const workerForm=ref<FormInstance | null>(null)
@ -100,8 +115,38 @@ function handleEdit(row: Worker){editingId.value=row.id;Object.assign(form,{code
async function handleSubmit(){submitting.value=true;try{const ok = await (workerForm.value?.validate ? new Promise<boolean>((resolve)=>workerForm.value!.validate((valid:boolean)=>resolve(valid))) : Promise.resolve(true)); if(!ok){return} await request[editingId.value?'put':'post'](editingId.value?`/admin/worker/${editingId.value}`:'/admin/worker',{...form});ElMessage.success('保存成功');dialogVisible.value=false;loadData()}finally{submitting.value=false}} async function handleSubmit(){submitting.value=true;try{const ok = await (workerForm.value?.validate ? new Promise<boolean>((resolve)=>workerForm.value!.validate((valid:boolean)=>resolve(valid))) : Promise.resolve(true)); if(!ok){return} await request[editingId.value?'put':'post'](editingId.value?`/admin/worker/${editingId.value}`:'/admin/worker',{...form});ElMessage.success('保存成功');dialogVisible.value=false;loadData()}finally{submitting.value=false}}
async function handleDelete(row:any){await ElMessageBox.confirm('确定删除【'+row.name+'】?此操作不可恢复。','提示',{type:'warning'});await request.delete(`/admin/worker/${row.id}`);ElMessage.success('已删除');loadData()} async function handleDelete(row:any){await ElMessageBox.confirm('确定删除【'+row.name+'】?此操作不可恢复。','提示',{type:'warning'});await request.delete(`/admin/worker/${row.id}`);ElMessage.success('已删除');loadData()}
async function batchStatus(isEnabled:number){await ElMessageBox.confirm('确定对选中的'+selectedRows.value.length+'项操作?','提示',{type:'warning'});for(const id of selectedRows.value.map((r:any)=>r.id)){await request.put(`/admin/worker/${id}/toggle`,{isEnabled})};ElMessage.success('操作成功');loadData()} async function batchStatus(isEnabled:number){await ElMessageBox.confirm('确定对选中的'+selectedRows.value.length+'项操作?','提示',{type:'warning'});for(const id of selectedRows.value.map((r:any)=>r.id)){await request.put(`/admin/worker/${id}/toggle`,{isEnabled})};ElMessage.success('操作成功');loadData()}
async function loadDrops(){try{const r: ApiResponse<{ items: Array<{ id: number; name: string; deviceCode?: string; workshopName?: string }> }> = await request.get('/admin/worker/available-machines'); availableMachines.value = (r.data?.items ?? []).map(m => ({ id: m.id, name: m.name, label: `${m.deviceCode || m.name} (${m.name})${m.workshopName ? ' - ' + m.workshopName : ''}` }))}catch{/* 接口不可用时保持为空,不影响其他功能 */}} async function loadDrops(){try{const r: ApiResponse<{ items: MachineItem[] }> = await request.get('/admin/machine',{params:{pageSize:999}}); availableMachines.value = (r.data?.items ?? []).map(m => ({ id: m.id, name: m.name, label: m.name, deviceCode: m.deviceCode, workshopName: m.workshopName, isOnline: m.isOnline, isEnabled: m.isEnabled }))}catch{/* 接口不可用时保持为空,不影响其他功能 */}}
/** 根据机床在线/启用状态返回样式类 */
function getStatusClass(m: MachineItem): string {
if (!m.isEnabled) return 'status-disabled'
if (m.isOnline) return 'status-online'
return 'status-offline'
}
function handlePageChange(page:number){pagination.value.currentPage=page;loadData()} function handlePageChange(page:number){pagination.value.currentPage=page;loadData()}
function handleSizeChange(size:number){pagination.value.pageSize=size;pagination.value.currentPage=1;loadData()} function handleSizeChange(size:number){pagination.value.pageSize=size;pagination.value.currentPage=1;loadData()}
onMounted(()=>{loadData();loadDrops()}) onMounted(()=>{loadData();loadDrops()})
</script> </script>
<style scoped>
/* 穿梭框状态色点 */
.status-dot {
display: inline-block;
width: 8px;
height: 8px;
border-radius: 50%;
margin-right: 6px;
vertical-align: middle;
}
.status-online .status-dot { background-color: #67c23a; }
.status-offline .status-dot { background-color: #c0c4cc; }
.status-disabled .status-dot { background-color: #f56c6c; }
/* 图例 */
.transfer-legend {
display: flex;
gap: 16px;
margin-top: 8px;
font-size: 12px;
color: #909399;
align-items: center;
}
.transfer-legend .status-dot { margin-right: 4px; }
</style>

Loading…
Cancel
Save