|
|
@@ -1,6 +1,6 @@
|
|
|
<template>
|
|
|
|
|
|
- <el-tabs v-model="topsTab" type="card" class="top_tabs">
|
|
|
+ <el-tabs v-model="topsTab" type="card" class="top_tabs" :disabled="isSortMode">
|
|
|
<el-tab-pane label="执行左脚程序" name="left">
|
|
|
</el-tab-pane>
|
|
|
<el-tab-pane label="执行右脚程序" name="right"></el-tab-pane>
|
|
|
@@ -11,19 +11,47 @@
|
|
|
:class="item.id === activeTab.id ? 'active' : ''"
|
|
|
v-for="item in tabs" :key="item.id"
|
|
|
@click="toggleTab(item)" v-log="{ describe: { action: '点击切换动作Tab', tabName: item.mode_name, tabId: item.id } }"
|
|
|
+ :style="{ cursor: isSortMode ? 'not-allowed' : 'pointer', opacity: isSortMode ? 0.5 : 1 }"
|
|
|
>{{item.mode_name}}</div>
|
|
|
</div>
|
|
|
<div class="form-table">
|
|
|
+ <div v-if="isSortMode" class="sort-tip">
|
|
|
+ <el-icon><Warning /></el-icon>
|
|
|
+ <span>排序模式:请拖拽行进行排序,完成后点击"保存排序"</span>
|
|
|
+ </div>
|
|
|
<div class="btnBox">
|
|
|
- <div class="primary-btn" @click="addRow" v-log="{ describe: { action: '点击新增一行' } }">新增一行</div>
|
|
|
- <div class="primary-btn" @click="resetConfig" v-log="{ describe: { action: '点击重新初始化', tab: topsTab } }">重新初始化</div>
|
|
|
- <div class="primary-btn" @click="reName" v-log="{ describe: { action: '点击重命名配置' } }">重命名配置</div>
|
|
|
- <el-radio-group style="margin-left: 10px" v-model="selectID" @click="changeSelectId(activeTab.id)" v-log="{ describe: { action: '点击切换执行配置', tabId: activeTab.id } }">
|
|
|
+ <div class="primary-btn" @click="addRow" v-log="{ describe: { action: '点击新增一行' } }" :class="{ disabled: isSortMode }" :style="{ opacity: isSortMode ? 0.5 : 1, cursor: isSortMode ? 'not-allowed' : 'pointer' }">新增一行</div>
|
|
|
+ <div class="primary-btn" @click="resetConfig" v-log="{ describe: { action: '点击重新初始化', tab: topsTab } }" :class="{ disabled: isSortMode }" :style="{ opacity: isSortMode ? 0.5 : 1, cursor: isSortMode ? 'not-allowed' : 'pointer' }">重新初始化</div>
|
|
|
+ <div class="primary-btn" @click="reName" v-log="{ describe: { action: '点击重命名配置' } }" :class="{ disabled: isSortMode }" :style="{ opacity: isSortMode ? 0.5 : 1, cursor: isSortMode ? 'not-allowed' : 'pointer' }">重命名配置</div>
|
|
|
+ <div class="primary-btn" @click="toggleSortMode" v-log="{ describe: { action: isSortMode ? '点击保存排序' : '点击排序' } }">
|
|
|
+ {{ isSortMode ? '保存排序' : '排序' }}
|
|
|
+ </div>
|
|
|
+ <div v-if="isSortMode" class="normal-btn" @click="cancelSort" v-log="{ describe: { action: '点击取消排序' } }">
|
|
|
+ 取消排序
|
|
|
+ </div>
|
|
|
+ <el-radio-group style="margin-left: 10px" v-model="selectID" @click="changeSelectId(activeTab.id)" v-log="{ describe: { action: '点击切换执行配置', tabId: activeTab.id } }" :disabled="isSortMode">
|
|
|
<el-radio :label="activeTab.id">切换成执行配置</el-radio>
|
|
|
</el-radio-group>
|
|
|
</div>
|
|
|
- <el-table max-height="700" :data="tableData" style="width: 100%" border>
|
|
|
-<!-- <el-table-column prop="id" label="id" />-->
|
|
|
+ <el-table
|
|
|
+ max-height="700"
|
|
|
+ :data="tableData"
|
|
|
+ style="width: 100%"
|
|
|
+ border
|
|
|
+ row-key="id"
|
|
|
+ :row-class-name="getRowClassName"
|
|
|
+ ref="tableRef"
|
|
|
+ >
|
|
|
+ <el-table-column prop="sort" label="排序" width="80" v-if="isSortMode">
|
|
|
+ <template #default="scope">
|
|
|
+ <div class="sort-content">
|
|
|
+ <span class="sort-number">{{ scope.row.sort }}</span>
|
|
|
+ <div class="sort-handle">
|
|
|
+ <el-icon><Rank /></el-icon>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
<el-table-column prop="action_name" label="步骤" >
|
|
|
<template #default="scope">
|
|
|
{{ scope.row.action_name }}
|
|
|
@@ -63,7 +91,9 @@
|
|
|
<script setup lang="ts">
|
|
|
import { ref, defineProps, defineEmits , watch,onMounted, reactive,onBeforeUnmount } from 'vue'
|
|
|
import EditDialog from "./EditDialog.vue";
|
|
|
-import { ElMessage, ElMessageBox } from 'element-plus';import client from "@/stores/modules/client";
|
|
|
+import { ElMessage, ElMessageBox } from 'element-plus';
|
|
|
+import { Rank, Warning } from '@element-plus/icons-vue';
|
|
|
+import client from "@/stores/modules/client";
|
|
|
import icpList from '@/utils/ipc';
|
|
|
const clientStore = client();
|
|
|
import socket from "@/stores/modules/socket";
|
|
|
@@ -80,10 +110,18 @@ const activeTab = ref({}); // 当前激活的标签页
|
|
|
const tabs = ref([]); // 所有标签页
|
|
|
const editId = ref(0); // 当前编辑行的索引
|
|
|
const selectID = ref(0) //当前默认的ID
|
|
|
+const isSortMode = ref(false); // 是否处于排序模式
|
|
|
+const originalTableData = ref([]); // 保存原始数据用于排序
|
|
|
+const tableRef = ref(null); // 表格引用
|
|
|
+let dragEventHandlers = null; // 拖拽事件处理器
|
|
|
|
|
|
|
|
|
onBeforeUnmount(()=>{
|
|
|
window.removeEventListener('beforeunload', handleBeforeUnload);
|
|
|
+ // 清理排序模式
|
|
|
+ if (isSortMode.value) {
|
|
|
+ exitSortMode();
|
|
|
+ }
|
|
|
})
|
|
|
|
|
|
onMounted(()=>{
|
|
|
@@ -106,7 +144,9 @@ const handleBeforeUnload = (e)=>{
|
|
|
* 监听topsTab变化,获取对应标签页的设备配置列表。
|
|
|
*/
|
|
|
watch(() => topsTab.value, (newTab) => {
|
|
|
- getTopList();
|
|
|
+ if (!isSortMode.value) {
|
|
|
+ getTopList();
|
|
|
+ }
|
|
|
});
|
|
|
|
|
|
const getTopList = ()=>{
|
|
|
@@ -140,6 +180,8 @@ const getTopList = ()=>{
|
|
|
|
|
|
//切换tab
|
|
|
const toggleTab = (item) => {
|
|
|
+ if (isSortMode.value) return; // 排序模式下禁用
|
|
|
+
|
|
|
activeTab.value = item
|
|
|
getList()
|
|
|
};
|
|
|
@@ -150,6 +192,11 @@ const calibrationId = ref(null) //校准位
|
|
|
* 获取设备配置列表。
|
|
|
*/
|
|
|
const getList = () => {
|
|
|
+ // 如果正在排序模式,先退出
|
|
|
+ if (isSortMode.value) {
|
|
|
+ exitSortMode();
|
|
|
+ }
|
|
|
+
|
|
|
clientStore.ipc.removeAllListeners(icpList.setting.getDeviceConfigList);
|
|
|
let params = {
|
|
|
tab_id: activeTab.value.id
|
|
|
@@ -177,6 +224,7 @@ const getList = () => {
|
|
|
|
|
|
|
|
|
const changeSelectId = (id)=>{
|
|
|
+ if (isSortMode.value) return; // 排序模式下禁用
|
|
|
if(id === selectID.value) return;
|
|
|
clientStore.ipc.removeAllListeners(icpList.setting.updateLeftRightConfig);
|
|
|
let params = {
|
|
|
@@ -205,6 +253,8 @@ const changeSelectId = (id)=>{
|
|
|
* @param {number} index - 当前行的索引
|
|
|
*/
|
|
|
const editRow = (row, index) => {
|
|
|
+ if (isSortMode.value) return; // 排序模式下禁用
|
|
|
+
|
|
|
addRowData.value = {}
|
|
|
dialogVisible.value = true;
|
|
|
editId.value = row.id
|
|
|
@@ -216,6 +266,8 @@ const editRow = (row, index) => {
|
|
|
* @param {number} index - 当前行的索引
|
|
|
*/
|
|
|
const deleteRow = (row, index) => {
|
|
|
+ if (isSortMode.value) return; // 排序模式下禁用
|
|
|
+
|
|
|
ElMessageBox.confirm('确定删除该步骤吗?', '提示', {
|
|
|
confirmButtonText: '确定',
|
|
|
cancelButtonText: '取消',
|
|
|
@@ -242,6 +294,8 @@ const deleteRow = (row, index) => {
|
|
|
* 重置设备配置。
|
|
|
*/
|
|
|
const resetConfig = () => {
|
|
|
+ if (isSortMode.value) return; // 排序模式下禁用
|
|
|
+
|
|
|
console.log(activeTab.value);
|
|
|
ElMessageBox.confirm(`确定初始化${activeTab.value.mode_name}吗?`, '提示', {
|
|
|
confirmButtonText: '确定',
|
|
|
@@ -267,6 +321,8 @@ const resetConfig = () => {
|
|
|
};
|
|
|
|
|
|
const reName = ()=>{
|
|
|
+ if (isSortMode.value) return; // 排序模式下禁用
|
|
|
+
|
|
|
ElMessageBox.prompt('', '重命名配置', {
|
|
|
confirmButtonText: '保存',
|
|
|
cancelButtonText: '取消',
|
|
|
@@ -296,7 +352,7 @@ const reName = ()=>{
|
|
|
ElMessage.success('重命名成功');
|
|
|
} else if(result.mssg){
|
|
|
ElMessage.error(result.mssg);
|
|
|
- }else {
|
|
|
+ }else {
|
|
|
ElMessage.error('重命名失败');
|
|
|
}
|
|
|
clientStore.ipc.removeAllListeners(icpList.setting.updateTabName);
|
|
|
@@ -308,6 +364,8 @@ const reName = ()=>{
|
|
|
* 新增一行配置。
|
|
|
*/
|
|
|
const addRow = () => {
|
|
|
+ if (isSortMode.value) return; // 排序模式下禁用
|
|
|
+
|
|
|
editId.value = -1
|
|
|
let length = Number(tableData.value.length)+1
|
|
|
addRowData.value = {
|
|
|
@@ -329,6 +387,203 @@ const addRow = () => {
|
|
|
editTitle.value = '新增步骤';
|
|
|
};
|
|
|
|
|
|
+/**
|
|
|
+ * 切换排序模式
|
|
|
+ */
|
|
|
+const toggleSortMode = () => {
|
|
|
+ if (isSortMode.value) {
|
|
|
+ // 保存排序
|
|
|
+ saveSortOrder();
|
|
|
+ } else {
|
|
|
+ // 进入排序模式
|
|
|
+ enterSortMode();
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * 取消排序
|
|
|
+ */
|
|
|
+const cancelSort = () => {
|
|
|
+ exitSortMode();
|
|
|
+ ElMessage.info('已取消排序');
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * 进入排序模式
|
|
|
+ */
|
|
|
+const enterSortMode = () => {
|
|
|
+ isSortMode.value = true;
|
|
|
+ // 保存原始数据
|
|
|
+ originalTableData.value = JSON.parse(JSON.stringify(tableData.value));
|
|
|
+ // 为每行添加排序值
|
|
|
+ tableData.value.forEach((item, index) => {
|
|
|
+ item.sort = index + 1;
|
|
|
+ });
|
|
|
+
|
|
|
+ // 等待DOM更新后初始化Sortable
|
|
|
+ setTimeout(() => {
|
|
|
+ initSortable();
|
|
|
+ }, 100);
|
|
|
+
|
|
|
+ ElMessage.info('请拖拽行进行排序,完成后点击"保存排序"');
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * 初始化拖拽排序
|
|
|
+ */
|
|
|
+const initSortable = () => {
|
|
|
+ if (!tableRef.value) return;
|
|
|
+
|
|
|
+ const tbody = tableRef.value.$el.querySelector('.el-table__body-wrapper tbody');
|
|
|
+ if (!tbody) return;
|
|
|
+
|
|
|
+ // 使用原生拖拽API实现排序
|
|
|
+ let draggedRow = null;
|
|
|
+ let draggedIndex = -1;
|
|
|
+
|
|
|
+ // 创建事件处理器
|
|
|
+ dragEventHandlers = {
|
|
|
+ handleDragStart: (e) => {
|
|
|
+ if (!isSortMode.value) return;
|
|
|
+ draggedRow = e.target.closest('tr');
|
|
|
+ if (draggedRow) {
|
|
|
+ e.dataTransfer.effectAllowed = 'move';
|
|
|
+ draggedRow.style.opacity = '0.5';
|
|
|
+ const rows = Array.from(tbody.querySelectorAll('tr'));
|
|
|
+ draggedIndex = rows.indexOf(draggedRow);
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ handleDragEnd: (e) => {
|
|
|
+ if (draggedRow) {
|
|
|
+ draggedRow.style.opacity = '';
|
|
|
+ draggedRow = null;
|
|
|
+ draggedIndex = -1;
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ handleDragOver: (e) => {
|
|
|
+ if (!isSortMode.value || !draggedRow) return;
|
|
|
+ e.preventDefault();
|
|
|
+ e.dataTransfer.dropEffect = 'move';
|
|
|
+ },
|
|
|
+
|
|
|
+ handleDrop: (e) => {
|
|
|
+ if (!isSortMode.value || !draggedRow) return;
|
|
|
+ e.preventDefault();
|
|
|
+
|
|
|
+ const dropRow = e.target.closest('tr');
|
|
|
+ if (!dropRow || dropRow === draggedRow) return;
|
|
|
+
|
|
|
+ // 获取目标行的索引
|
|
|
+ const rows = Array.from(tbody.querySelectorAll('tr'));
|
|
|
+ const dropIndex = rows.indexOf(dropRow);
|
|
|
+
|
|
|
+ if (draggedIndex !== -1 && dropIndex !== -1 && draggedIndex !== dropIndex) {
|
|
|
+ // 重新排序数据
|
|
|
+ const newData = [...tableData.value];
|
|
|
+ const [draggedItem] = newData.splice(draggedIndex, 1);
|
|
|
+ newData.splice(dropIndex, 0, draggedItem);
|
|
|
+
|
|
|
+ // 更新排序值
|
|
|
+ newData.forEach((item, index) => {
|
|
|
+ item.sort = index + 1;
|
|
|
+ });
|
|
|
+
|
|
|
+ tableData.value = newData;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 添加事件监听器
|
|
|
+ tbody.addEventListener('dragstart', dragEventHandlers.handleDragStart);
|
|
|
+ tbody.addEventListener('dragend', dragEventHandlers.handleDragEnd);
|
|
|
+ tbody.addEventListener('dragover', dragEventHandlers.handleDragOver);
|
|
|
+ tbody.addEventListener('drop', dragEventHandlers.handleDrop);
|
|
|
+
|
|
|
+ // 为每行添加拖拽属性
|
|
|
+ const rows = tbody.querySelectorAll('tr');
|
|
|
+ rows.forEach(row => {
|
|
|
+ row.draggable = isSortMode.value;
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * 保存排序
|
|
|
+ */
|
|
|
+const saveSortOrder = () => {
|
|
|
+ // 准备排序数据
|
|
|
+ const sortData = tableData.value.map((item, index) => ({
|
|
|
+ id: item.id,
|
|
|
+ sort: index + 1
|
|
|
+ }));
|
|
|
+
|
|
|
+ console.log("sort_data",{
|
|
|
+ tab_id: activeTab.value.id,
|
|
|
+ sort_data: sortData
|
|
|
+ })
|
|
|
+ // 调用后端接口保存排序
|
|
|
+ clientStore.ipc.removeAllListeners(icpList.setting.updateDeviceConfigSort);
|
|
|
+ clientStore.ipc.send(icpList.setting.updateDeviceConfigSort, {
|
|
|
+ tab_id: activeTab.value.id,
|
|
|
+ sort_data: sortData
|
|
|
+ });
|
|
|
+
|
|
|
+ clientStore.ipc.on(icpList.setting.updateDeviceConfigSort, (event, result) => {
|
|
|
+ if (result.code == 0) {
|
|
|
+ ElMessage.success('排序保存成功');
|
|
|
+ isSortMode.value = false;
|
|
|
+ // 重新获取列表以更新数据
|
|
|
+ getList();
|
|
|
+ } else if (result.mssg) {
|
|
|
+ ElMessage.error(result.mssg);
|
|
|
+ } else {
|
|
|
+ ElMessage.error('排序保存失败');
|
|
|
+ // 保存失败时退出排序模式
|
|
|
+ exitSortMode();
|
|
|
+ }
|
|
|
+ clientStore.ipc.removeAllListeners(icpList.setting.updateDeviceConfigSort);
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * 获取行类名
|
|
|
+ */
|
|
|
+const getRowClassName = ({ row, rowIndex }) => {
|
|
|
+ if (isSortMode.value) {
|
|
|
+ return 'sortable-row';
|
|
|
+ }
|
|
|
+ return '';
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * 退出排序模式
|
|
|
+ */
|
|
|
+const exitSortMode = () => {
|
|
|
+ isSortMode.value = false;
|
|
|
+ // 恢复原始数据
|
|
|
+ tableData.value = JSON.parse(JSON.stringify(originalTableData.value));
|
|
|
+
|
|
|
+ // 移除拖拽事件监听器和属性
|
|
|
+ if (tableRef.value && dragEventHandlers) {
|
|
|
+ const tbody = tableRef.value.$el.querySelector('.el-table__body-wrapper tbody');
|
|
|
+ if (tbody) {
|
|
|
+ // 移除事件监听器
|
|
|
+ tbody.removeEventListener('dragstart', dragEventHandlers.handleDragStart);
|
|
|
+ tbody.removeEventListener('dragend', dragEventHandlers.handleDragEnd);
|
|
|
+ tbody.removeEventListener('dragover', dragEventHandlers.handleDragOver);
|
|
|
+ tbody.removeEventListener('drop', dragEventHandlers.handleDrop);
|
|
|
+
|
|
|
+ // 移除拖拽属性
|
|
|
+ const rows = tbody.querySelectorAll('tr');
|
|
|
+ rows.forEach(row => {
|
|
|
+ row.draggable = false;
|
|
|
+ });
|
|
|
+ }
|
|
|
+ dragEventHandlers = null;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
@@ -410,6 +665,71 @@ const addRow = () => {
|
|
|
.cursor-pointer{
|
|
|
cursor: pointer;
|
|
|
}
|
|
|
+
|
|
|
+ // 排序相关样式
|
|
|
+ .sort-content {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ height: 100%;
|
|
|
+
|
|
|
+ .sort-number {
|
|
|
+ font-weight: bold;
|
|
|
+ color: #2957FF;
|
|
|
+ font-size: 14px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .sort-handle {
|
|
|
+ cursor: move;
|
|
|
+ color: #909399;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ color: #2957FF;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep(.sortable-row) {
|
|
|
+ cursor: move;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ background-color: #f5f7fa !important;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep(.el-table__row.sortable-row) {
|
|
|
+ transition: all 0.3s ease;
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep(.el-table__body-wrapper tbody tr) {
|
|
|
+ &.sortable-row {
|
|
|
+ cursor: move;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ background-color: #f5f7fa !important;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .disabled {
|
|
|
+ pointer-events: none;
|
|
|
+ }
|
|
|
+
|
|
|
+ .sort-tip {
|
|
|
+ background: #EAF3FF;
|
|
|
+ border: 1px solid #CBE1FF;
|
|
|
+ border-radius: 4px;
|
|
|
+ padding: 8px 12px;
|
|
|
+ margin-bottom: 12px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+ color: #2957FF;
|
|
|
+ font-size: 14px;
|
|
|
+ }
|
|
|
}
|
|
|
.editDialog{
|
|
|
.el-dialog__body{
|