node-webkit File Dialog

本文探讨了在多次选择相同目录时,文件选择器的onchange事件不触发的问题,并提供了一种解决方案,通过重新设置输入框的值为空确保事件始终被触发。

1. 多次选择相同的路径时,chooser.onchange 事件不触发

参考链接

Choosing the same directory multiple times

Once the chooser has been used to select a directory (or the same file path + file name), the change event will not fire again since the value of the input is already set with the same value. For example, if a user is expected to select a directory to export a file to, if they choose the same directory multiple times, the change event will one fire once. This is only an issue if the selected value may potentially be the same multiple times, i.e. in the case of selecting an output directory.

In this case, re-setting the value of the input can ensure that the change event will always fire in subsequent selections.

jQuery

<script>
  function chooseFile(name) {  var chooser = $(name);  chooser.change(function(evt) {  console.log($(this).val());  // Reset the selected value to empty ('')  $(this).val('');  });   chooser.trigger('click');  }  chooseFile('#fileDialog'); </script>

 

转载于:https://www.cnblogs.com/JosephLiao/p/4538165.html

<div data-v-4e45b24b="" role="tree" aria-label="目录树结构" aria-multiselectable="false">,<div data-v-6b10262b="" role="tree" class="el-tree">,Certain ARIA roles must contain particular children: Required ARIA children role not present: group, treeitem.这是我的报错,请根据报错修改我的代码<template> <div class="dir-tree-container"> <!-- 目录树结构:严格遵循 tree → group → treeitem 刚性层级 --> <div role="tree" aria-label="目录树结构" aria-multiselectable="false"> <!-- 根目录组:必须包含 treeitem --> <div role="group" aria-label="根目录组" aria-roledescription="根目录集合"> <!-- 加载状态 --> <template v-if="loading"> <div role="treeitem" aria-level="1" aria-setsize="1" aria-posinset="1" aria-selected="false" aria-disabled="true" class="empty-treeitem" > 加载中... </div> </template> <!-- 根节点:有数据时渲染 --> <template v-else-if="actualTreeData.length > 0"> <tree-node v-for="(node, index) in actualTreeData" :key="node.id" :node-data="node" :current-level="1" :node-index="index" :sibling-total="actualTreeData.length" @node-select="handleNodeSelect" @add-node="handleAdd" @edit-node="handleEdit" @delete-node="handleDelete" /> </template> <!-- 根节点空状态:强制 treeitem 角色完整 --> <div v-else role="treeitem" aria-level="1" aria-setsize="1" aria-posinset="1" aria-selected="false" aria-disabled="true" aria-label="根目录为空" class="empty-treeitem" > 暂无目录,请点击"新增"创建 </div> </div> </div> <!-- 对话框:保持原有功能 --> <el-dialog :visible.sync="dialogVisible" :title="isEdit ? '编辑目录' : '新增目录'" width="30%" > <el-form :model="form" label-width="80px" ref="formRef" :rules="formRules"> <el-form-item label="目录名称" prop="name"> <el-input v-model="form.name" :placeholder="isEdit ? '请输入新的目录名称' : '请输入目录名称'" :title="isEdit ? '编辑目录名称' : '输入新目录名称'" aria-label="目录名称输入框" aria-required="true" /> </el-form-item> <el-form-item label="父级目录" v-if="!isRootAdd"> <el-select v-model="form.parentId" placeholder="请选择父级目录(必选)" title="选择父级目录" aria-label="父级目录选择框" aria-required="true" > <el-option v-for="item in actualTreeData" :key="item.id" :label="item.name" :value="item.id" :disabled="isEdit && form.id === item.id" :aria-label="'父级目录选项:' + item.name" /> <el-option v-if="actualTreeData.length === 0" value="" label="暂无可用父级目录" disabled /> </el-select> </el-form-item> </el-form> <span slot="footer" class="dialog-footer"> <el-button @click="dialogVisible = false" title="取消操作" aria-label="取消按钮" > 取消 </el-button> <el-button type="primary" @click="confirmSubmit" :title="isEdit ? '确认编辑' : '确认新增'" aria-label="确认提交按钮" > 确认 </el-button> </span> </el-dialog> </div> </template> <script> import { getDirectories, createDirectory, updateDirectory, deleteDirectory } from '@/api/data'; export default { name: "DirTree", components: { TreeNode: { name: 'TreeNode', props: { nodeData: { type: Object, required: true, validator: value => value && typeof value === 'object' && value.id !== undefined && value.name !== undefined }, currentLevel: { type: Number, required: true, default: 1 }, nodeIndex: { type: Number, required: true, default: 0 }, siblingTotal: { type: Number, required: true, default: 1 } }, template: ` <!-- 父节点:treeitem 必须绑定子组 ID --> <div role="treeitem" :aria-level="currentLevel" :aria-expanded="nodeData.children.length > 0" <!-- 直接判断子节点数量 --> :aria-setsize="siblingTotal" :aria-posinset="nodeIndex + 1" :aria-label="'目录节点:' + nodeData.name" :aria-owns="\`children-group-\${nodeData.id}\`" <!-- 强制绑定子组 ID --> :aria-selected="false" <!-- 明确选中状态 --> @click="$emit('node-select', nodeData)" class="tree-item" :title="'点击选择目录:' + nodeData.name" > <span class="node-name">{{ nodeData.name }}</span> <div class="node-actions" role="group" aria-label="节点操作按钮组"> <el-button type="primary" size="mini" @click.stop="$emit('add-node', nodeData)" title="新增子目录" aria-label="新增子目录按钮" >新增</el-button> <el-button type="success" size="mini" @click.stop="$emit('edit-node', nodeData)" title="编辑当前目录" aria-label="编辑目录按钮" >编辑</el-button> <el-button type="danger" size="mini" @click.stop="$emit('delete-node', nodeData)" title="删除当前目录" aria-label="删除目录按钮" >删除</el-button> </div> </div> <!-- 子组:无条件渲染(已强制补充 children 数组),必须包含 treeitem --> <div :id="\`children-group-\${nodeData.id}\`" <!-- 与父节点 aria-owns 对应 --> role="group" aria-label="\`\${nodeData.name}\` 的子目录组" aria-roledescription="子目录集合" class="children-container" > <!-- 子节点有数据 --> <template v-if="nodeData.children.length > 0"> <tree-node v-for="(child, idx) in nodeData.children" :key="child.id" :node-data="child" :current-level="currentLevel + 1" :node-index="idx" :sibling-total="nodeData.children.length" @node-select="$emit('node-select', $event)" @add-node="$emit('add-node', $event)" @edit-node="$emit('edit-node', $event)" @delete-node="$emit('delete-node', $event)" /> </template> <!-- 子节点空状态:强制 treeitem 角色 --> <div v-else role="treeitem" :aria-level="currentLevel + 1" aria-setsize="1" aria-posinset="1" aria-selected="false" aria-disabled="true" aria-label="\`\${nodeData.name}\` 暂无子目录" class="empty-treeitem" > 暂无子目录 </div> </div> ` } }, data() { return { dirTree: [], dialogVisible: false, isEdit: false, isRootAdd: false, form: { id: "", name: "", parentId: null }, selectedNode: null, formRules: { name: [{ required: true, message: "请输入目录名称", trigger: "blur" }], }, loading: false }; }, computed: { actualTreeData() { if (!this.dirTree || typeof this.dirTree !== 'object') return []; // 强制处理数据:确保每个节点都有 children 数组 const formatData = (data) => { return data.map(node => ({ ...node, children: node.children && Array.isArray(node.children) ? formatData(node.children) : [] })); }; if (this.dirTree.data && Array.isArray(this.dirTree.data)) return formatData(this.dirTree.data); if (Array.isArray(this.dirTree)) return formatData(this.dirTree); return []; } }, created() { this.fetchDirTree(); }, methods: { async fetchDirTree() { this.loading = true; try { const parentId = 0; const res = await getDirectories(parentId); this.dirTree = res; // 数据格式化交给 computed 处理 } catch (error) { console.error('获取目录树失败:', error); this.$message.error('加载目录失败,请检查接口后重试'); } finally { this.loading = false; } }, handleNodeSelect(data) { this.selectedNode = data; }, handleAdd(parentData) { const realParent = parentData || this.selectedNode; this.isEdit = false; this.isRootAdd = !realParent; this.form = { id: "", name: "", parentId: realParent ? realParent.id : null, children: [] // 新增节点默认带空 children }; this.dialogVisible = true; }, handleEdit(data) { this.isEdit = true; // 编辑时确保 children 存在 this.form = JSON.parse(JSON.stringify({ ...data, children: data.children || [] })); this.dialogVisible = true; }, async handleDelete(data) { await this.$confirm('确定删除【' + data.name + '】吗?', "警告") .then(async () => { try { await deleteDirectory(data.id); await this.fetchDirTree(); this.$message.success("删除成功"); } catch (error) { console.error("删除目录失败:", error); this.$message.error("删除失败,请重试"); } }) .catch(() => { this.$message.info("已取消删除"); }); }, async confirmSubmit() { await this.$refs.formRef.validate(async (valid) => { if (!valid) return; try { if (this.isEdit) { await updateDirectory(this.form.id, this.form); } else { await createDirectory(this.form); } await this.fetchDirTree(); this.dialogVisible = false; this.$message.success(this.isEdit ? "编辑成功" : "新增成功"); } catch (error) { console.error('提交失败:', error); this.$message.error(this.isEdit ? "编辑失败" : "新增失败"); } }); } }, }; </script> <style scoped> .dir-tree-container { padding: 10px; height: 100%; background: #fff; } ::v-deep .tree-item { border-radius: 4px; transition: background 0.2s; margin: 4px 0; -webkit-appearance: none; -moz-appearance: none; appearance: none; } ::v-deep .tree-item:hover { background: #f5f7fa; } ::v-deep .node-actions button { padding: 2px 8px; font-size: 12px; -webkit-appearance: none; -moz-appearance: none; appearance: none; } ::v-deep .node-name { -webkit-user-select: none; -moz-user-select: none; user-select: none; -ms-user-select: none; } .tree-item { display: flex; justify-content: space-between; align-items: center; padding: 4px 8px; cursor: pointer; -webkit-user-select: none; -moz-user-select: none; user-select: none; } .node-name { flex: 1; } .node-actions { display: none; gap: 4px; } .tree-item:hover .node-actions { display: flex; } .children-container { padding-left: 24px; -webkit-user-select: none; -moz-user-select: none; user-select: none; } .empty-treeitem { padding: 4px 8px; color: #999; font-style: italic; margin: 4px 0; } </style>这是dirtree,<template> <div class="dashboard"> <h2>欢迎回来,{{ $store.state.userInfo?.username || '用户' }}!</h2> <!-- 树形目录区域 --> <el-tree :data="directoryTree" :props="{ label: 'name', children: 'children' }" node-key="id" @node-click="handleNodeClick" @node-contextmenu="handleNodeContextMenu" default-expand-all > <template #default="{ node }"> <span class="custom-tree-node"> {{ node.label }} <div class="tree-node-operations"> <el-button type="text" size="mini" @click="handleAdd(node)">新增</el-button> <el-button type="text" size="mini" @click="handleEdit(node)">编辑</el-button> <el-button type="text" size="mini" @click="handleDelete(node)">删除</el-button> </div> </span> </template> </el-tree> <!-- 操作区域 --> <div style="margin: 20px 0; display: flex; align-items: center;"> <el-upload class="upload-excel" action="#" :auto-upload="false" :on-change="handleExcelChange" accept=".xlsx,.xls" :show-file-list="false" > <el-button type="primary" icon="el-icon-upload2">导入Excel表格</el-button> </el-upload> <el-button type="success" @click="handleAddData">添加目录</el-button> <el-input v-model="searchKey" placeholder="输入关键词搜索目录" style="width: 250px; margin-left: 10px;" @keyup.enter="handleSearch" ></el-input> <el-button type="primary" style="margin-left: 10px;" @click="handleSearch">查找</el-button> <el-button type="default" style="margin-left: 10px;" @click="handleClearSearch">清空搜索</el-button> </div> <!-- 新增/编辑目录弹窗 --> <el-dialog :visible.sync="dialogVisible" :title="isEdit ? '编辑目录' : '新增目录'"> <el-form :model="form" :rules="rules" ref="formRef" label-width="80px"> <el-form-item label="目录名称" prop="name"> <el-input v-model="form.name" /> </el-form-item> <el-form-item label="父级目录" prop="parentId" v-if="!isRoot"> <el-select v-model="form.parentId" placeholder="请选择父级目录"> <el-option v-for="item in directoryOptions" :key="item.id" :label="item.name" :value="item.id" /> </el-select> </el-form-item> </el-form> <template #footer> <el-button @click="dialogVisible = false">取消</el-button> <el-button type="primary" @click="submitForm">确认</el-button> </template> </el-dialog> </div> </template> <script> import { getDirectories, createDirectory, updateDirectory, deleteDirectory, getAllDirectories, importExcel } from '@/api/data'; import { mapState } from 'vuex'; import * as XLSX from 'xlsx'; export default { name: 'HomeDashboard', computed: { ...mapState(['userInfo']) }, data() { return { directoryTree: [], // 树形目录数据 directoryOptions: [], // 所有目录(用于父级选择下拉) dialogVisible: false, // 弹窗显隐 isEdit: false, // 是否为编辑模式 isRoot: false, // 是否为根目录 form: { name: '', parentId: 0, id: '' }, // 表单数据 rules: { name: [{ required: true, message: '请输入目录名称', trigger: 'blur' }] }, searchKey: '' // 搜索关键词 }; }, mounted() { this.loadDirectories(); if (!XLSX || typeof XLSX.read !== 'function') { this.$message.error('XLSX库加载失败,请重新安装依赖'); } }, methods: { // 加载目录数据(树形+扁平)- 新增日志调试 async loadDirectories() { try { console.log('🔍 开始加载目录数据'); console.log('📌 请求1:加载根目录子目录,parentId=0'); console.log('📌 请求2:加载所有目录(用于下拉选择)'); // 同时发起两个请求并打印响应 const [treeRes, allRes] = await Promise.all([ getDirectories(0).then(res => { console.log('✅ 根目录子目录请求成功:', { 状态码: res.status, 响应数据: res.data }); return res; }), getAllDirectories().then(res => { console.log('✅ 所有目录请求成功:', { 状态码: res.status, 响应数据: res.data }); return res; }) ]); // 赋值并打印最终数据格式 this.directoryTree = treeRes.data; this.directoryOptions = allRes.data; console.log('📊 目录树最终数据(el-tree渲染用):', this.directoryTree); console.log('📊 下拉选择最终数据:', this.directoryOptions); // 数据格式校验提示 if (!Array.isArray(this.directoryTree)) { console.warn('⚠️ 目录树数据不是数组格式,el-tree可能无法渲染'); } } catch (error) { // 错误详情打印 console.error('❌ 目录加载失败,详细错误:'); console.error('错误名称:', error.name); console.error('错误消息:', error.message); if (error.response) { console.error('响应状态码:', error.response.status); console.error('响应数据:', error.response.data); console.error('请求URL:', error.response.config.url); // 根据状态码给出针对性提示 const status = error.response.status; let errMsg = '目录加载失败:'; if (status === 401) errMsg += '未登录或登录已过期,请重新登录'; else if (status === 404) errMsg += '接口路径错误,请检查前后端路径是否一致'; else if (status === 500) errMsg += '服务器内部错误,请联系后端排查'; else errMsg += error.response.data?.message || '未知错误'; this.$message.error(errMsg); } else if (error.request) { console.error('⚠️ 无响应:请求已发送,但未收到后端回复'); this.$message.error('目录加载失败:网络异常或后端服务未启动'); } else { console.error('⚠️ 请求准备失败:', error); this.$message.error('目录加载失败:请求参数或配置错误'); } } }, // 节点点击事件 handleNodeClick(node) { console.log('点击目录:', node.label); }, // 右键/新增目录 handleAdd(node) { this.isEdit = false; this.isRoot = !node; this.form = { name: '', parentId: node ? node.id : 0, id: '' }; this.dialogVisible = true; }, // 编辑目录 handleEdit(node) { this.isEdit = true; this.isRoot = false; this.form = { ...node.data }; this.dialogVisible = true; }, // 删除目录 async handleDelete(node) { await this.$confirm('确定删除该目录吗?删除后子目录也会被删除', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }); await deleteDirectory(node.data.id); this.$message.success('删除成功'); this.loadDirectories(); }, // 提交表单(新增/编辑) async submitForm() { await this.$refs.formRef.validate(); if (this.isEdit) { await updateDirectory(this.form.id, this.form); this.$message.success('编辑成功'); } else { await createDirectory(this.form); this.$message.success('新增成功'); } this.dialogVisible = false; this.loadDirectories(); }, // Excel导入处理 handleExcelChange(file) { // 保持原Excel导入逻辑(需后端支持Excel导入目录的接口) importExcel(file.raw, this.form.parentId || 0).then(() => { this.$message.success('Excel导入成功'); this.loadDirectories(); }).catch(err => { this.$message.error('Excel导入失败:' + err.message); }); }, // 搜索目录 handleSearch() { if (!this.searchKey.trim()) { this.loadDirectories(); this.$message.info('请输入搜索关键词'); return; } const key = this.searchKey.trim().toLowerCase(); // 模拟前端搜索(实际可通过后端接口筛选) this.directoryTree = this.directoryTree.map(node => ({ ...node, children: node.children?.filter(child => child.name.toLowerCase().includes(key) ) })).filter(node => node.name.toLowerCase().includes(key) || (node.children && node.children.length > 0) ); this.$message.success(`找到 ${this.countNodes(this.directoryTree)} 条匹配结果`); }, // 统计节点数(辅助搜索提示) countNodes(tree) { return tree.reduce((total, node) => { total++; if (node.children && node.children.length > 0) { total += this.countNodes(node.children); } return total; }, 0); }, // 清空搜索 handleClearSearch() { this.searchKey = ''; this.loadDirectories(); this.$message.info('已清空搜索条件'); }, // 手动添加目录(快捷方式) handleAddData() { this.handleAdd(null); // 新增根目录 } } }; </script> <style scoped> .dashboard { padding: 20px; } .custom-tree-node { display: flex; justify-content: space-between; align-items: center; width: 100%; } .tree-node-operations { display: none; } .custom-tree-node:hover .tree-node-operations { display: flex; } .upload-excel { margin-right: 10px; } </style>这是HomeDashboard,import request from '@/utils/request'; // 目录管理接口 - 对接后端 // 示例:@/api/data.js 中目录接口的正确路径 // 修改后(正确) export const getDirectories = (parentId) => { // 改名:与组件导入的名称一致 return request({ url: `/api/directories/parent/${parentId}`, method: 'GET' }); }; export function createDirectory(data) { const requestData = { name: data.name, parentId: data.parentId || 0, creator: localStorage.getItem('username') || 'admin' // 模拟创建人,实际需从登录态获取 }; return request({ url: '/api/directories', method: 'post', data: requestData, }); } export function updateDirectory(id, data) { const requestData = { name: data.name, parentId: data.parentId || 0, lastEditor: localStorage.getItem('username') || 'admin' // 模拟编辑人 }; return request({ url: `/api/directories/${id}`, method: 'put', data: requestData, }); } export function deleteDirectory(id) { return request({ url: `/api/directories/${id}`, method: 'delete', }); } export function getAllDirectories() { return request({ url: '/api/directories/all', method: 'get', }); } // 登录接口(保持不变) export function login(data) { return request.post('/auth/login', data); } // Excel导入接口(保持不变) export function importExcel(file, directoryId) { const formData = new FormData(); formData.append('file', file); formData.append('directoryId', directoryId); return request.post('/excel/import', formData, { headers: { 'Authorization': localStorage.getItem('token') } }); }这是data
11-06
同样的解释对下面代码,尽可能详细且解释语法:<template> <div class="body"> <div class="annotation_Table"> <div class="contentBox"> <el-tabs v-model="activeName" @tab-click="handleClick"> <el-tab-pane label="任务" name="task"></el-tab-pane> <el-tab-pane label="工程" name="engineering"></el-tab-pane> </el-tabs> </div> <div> <div class="treeSelect" v-if="activeName == 'task'"> <el-input size="small" placeholder="请输入内容" suffix-icon="el-icon-search" v-model="taskSearch"> </el-input> </div> <div class="treeSelect" v-else> <el-input size="small" placeholder="请输入内容" suffix-icon="el-icon-search" v-model="engineeringSearch"> </el-input> </div> <!-- <el-tree--> <!-- :data="tree_data"--> <!-- show-checkbox--> <!-- node-key="id"--> <!-- @check="check"--> <!-- :default-expand-all="true"--> <!-- :props="defaultProps"--> <!-- @node-click="handleNodeClick"--> <!-- >--> <!-- </el-tree>--> <!-- 修改:树节点:default-expand-all="true"展开改为false闭合 --> <el-tree class="treeLine" :data="tree_data" show-checkbox :node-key="activeName == 'engineering'?'projectId':'taskId'" @check="check" :default-expand-all="false" :props="defaultProps" @node-click="handleNodeClick" ref="treeRefImg" :highlight-current="true" filterable :key="treeKey" :filter-node-method="filterNode"> </el-tree> </div> </div> <div class="image_lazy_right body-tabel"> <div class="top-form" style="position: relative" @keyup.enter="getPicFun()"> <div class="select-box"> <label>专业</label> <el-select @change="taskMajorChange" filterable placeholder="专业" clearable v-model="params.taskMajor"> <el-option v-for="item in MajorList" :key="item.majorType" :label="item.majorTypeName" :value="item.majorType"></el-option> </el-select> </div> <div class="select-box"> <label>电压等级</label> <el-select @change="dydjListChange" filterable placeholder="电压等级" clearable v-model="params.dydj"> <el-option v-for="item in dydjList" :key="item.typeValue" :label="item.typeName" :value="item.typeValue"></el-option> </el-select> </div> <div class="select-box"> <label>工程名称</label> <el-select class="selectCss" clearable filterable @change="gcxxListChange" v-model="params.lineName" placeholder="工程名称"> <el-option v-for="item in gcxxList" :key="item.basicId" :label="item.basicName" :value="item.basicId"></el-option> </el-select> </div> <div class="select-box"> <label>杆塔/间隔名称</label> <!-- 新增@change="handleGtbhChange"杆塔和工程树联动 --> <el-select multiple collapse-tags class="selectCss selectCssList" clearable filterable v-model="gtbhList" placeholder="杆塔/间隔名称" @change="handleGtbhChange"> <el-option v-for="item in gcgtList" :key="item.keyId" :label="item.towerName" :value="item.keyId"></el-option> </el-select> </div> <div class="date-picker-box"> <label>上传时间</label> <el-date-picker v-model="timeValue" type="daterange" value-format="yyyy-MM-dd" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期"> </el-date-picker> </div> <div class="date-picker-box"> <label>处理时间</label> <el-date-picker v-model="timeValue1" type="daterange" value-format="yyyy-MM-dd" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期"> </el-date-picker> </div> <div class="select-box"> <label>是否有问题</label> <el-select class="selectCss" clearable filterable v-model="params.isMark" placeholder="是否有问题"> <el-option label="否" value="0"></el-option> <el-option label="是" value="1"></el-option> </el-select> </div> <div class="select-box"> <label>图片类型</label> <el-select class="selectCss" clearable filterable v-model="params.picType" placeholder="图片类型"> <el-option label="可见光巡检照片" value="1"></el-option> <el-option label="红外巡检照片" value="2"></el-option> </el-select> </div> <div class="select-box"> <label>数据来源</label> <el-select class="selectCss" clearable filterable v-model="params.uploadtype" placeholder="数据来源"> <el-option label="app上传" value="1"></el-option> <el-option label="离线上传" value="2"></el-option> </el-select> </div> <div class="select-box"> <label>是否归档</label> <el-select class="selectCss" clearable filterable v-model="params.isArc" placeholder="是否归档"> <el-option label="否" value="0"></el-option> <el-option label="是" value="1"></el-option> </el-select> </div> <div class="select-box"> <label>处理状态</label> <el-select class="selectCss" clearable filterable v-model="params.handleFlag" placeholder="处理状态"> <el-option label="已处理" value="1"></el-option> <el-option label="未处理" value="2"></el-option> </el-select> </div> <div class="input-box"> <label>处理人</label> <el-input v-model="params.handleUser" placeholder="处理人"></el-input> </div> <!-- <div class="select-box">--> <!-- <label>有无问题</label>--> <!-- <el-select--> <!-- class="selectCss"--> <!-- clearable--> <!-- filterable--> <!-- v-model="params.handleFlag"--> <!-- placeholder="有无问题"--> <!-- >--> <!-- <el-option label="有" value="1"></el-option>--> <!-- <el-option label="无" value="2"></el-option>--> <!-- </el-select>--> <!-- </div>--> <div class="button-box"> <el-button v-if="this.$menu.getMenuBus('查询')" v-on:click="getPicFun">查询</el-button> </div> <div class="button-box" style="position: absolute;right: 0px;bottom: -1px;"> <el-button v-if="this.$menu.getMenuBus('批量下载')" :loading="loading" v-on:click="downloadC()">批量下载</el-button> <el-button v-if="this.$menu.getMenuBus('导出')" v-on:click="exportDronePic()">导出</el-button> <el-button v-if="this.$menu.getMenuBus('数据统计')" v-on:click="getWorkCount()">数据统计</el-button> </div> </div> <div class="demo-image__lazy"> <div class="imgBox"> <div class="imgList" v-for="(item,index) in tableData"> <el-image v-on:click="viewimg(item,index)" :key="item.sourceUrl" :src="getImgUrl(item)"> </el-image> <!-- :preview-src-list="srcList"--> <!-- :initial-index="listIndex"--> <div class="typeNeme">{{ item.taskType }}</div> <div class="typeGD">{{ item.isArcName }}</div> <div class="isWenTi">{{ item.handleName }} 发现 {{ item.defectNum?item.defectNum:0 }} 个问题</div> <div class="imgText"> <li style="cursor: pointer;" v-on:click="modifyTest(item)"><span style="border-bottom: 1px solid #ffffff">{{ item.picName ? item.picName : "" }}</span></li> <!-- <li>--> <!-- <span>名称:</span--> <!-- >{{--> <!-- (item.lineName ? item.lineName : "") +--> <!-- "-" +--> <!-- (item.towerName ? item.towerName : "")--> <!-- }}--> <!-- </li>--> <li><span>采集时间:</span>{{ parseTime(item.photoTime) }}</li> <li class="imgTextLi" v-on:click="modifyTest(item)">标注 >></li> </div> </div> </div> </div> <div class="pageBox"> 共 {{ total }} 条数据 <el-pagination background :current-page.sync="page.pageNum" layout="prev, pager, next, jumper, sizes" :total="total" @size-change="handleSizeChange" @current-change="handleCurrentChange"> </el-pagination> </div> </div> <manually-annotated ref="examineInitMap" @getPicFun="getPicFun" @pre="pre" @next="next" v-if="dialogUploadObj.isShow" :dialogUploadObj="dialogUploadObj" :AnnotatedRow="AnnotatedRow" :imageObj="imageObj"></manually-annotated> <view-img @closeImgBox="closeImgBox" v-if="view.isShow" :view="view"></view-img> <!-- 隐藏原本代码 --> <!-- <data-statistics v-if="dataStatisticsObj.isShow" :dataStatisticsObj="dataStatisticsObj"></data-statistics> --> <el-dialog title="数据统计" :visible.sync="dataStatisticsObj.isShow" :modal-append-to-body="false" :append-to-body="false" :close-on-click-modal="false" top="12vh" width="70%" class="dialogBox data-statistics-dialog" custom-class="data-statistics-dialog"> <!-- 新增:查询按钮 --> <div class="button-box"> <el-button v-on:click="getWorkCount">查询</el-button> </div> <!-- 新增:任务名称、工程名称、处理时间三个筛选 --> <div class="top-form"> <div class="input-box"> <label>任务名称</label> <el-input placeholder="请输入任务名称" v-model="params.taskName" clearable></el-input> </div> <div class="select-box"> <label>工程名称</label> <el-select class="selectCss" clearable filterable v-model="params.lineName" placeholder="工程名称"> <el-option v-for="item in gcxxList" :key="item.basicId" :label="item.basicName" :value="item.basicId"></el-option> </el-select> </div> <div class="date-picker-box"> <label>处理时间</label> <el-date-picker v-model="timeValue1" type="daterange" value-format="yyyy-MM-dd" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期"></el-date-picker> </div> </div> <div class="button-box" style="margin-bottom: 5px; height: 30px;"> <el-row :gutter="3"> <el-col v-for="(tag, index) in filterTags" :key="index" :span="1.5"> <el-tag color="#0C53CF" closable @close="removeFilterTag(index)"> {{ tag.label }}: {{ tag.value }} </el-tag> </el-col> </el-row> </div> <div class="body-tabel"> <div class="body-tabels"> <!-- 新增:auto-resize自动调整大小 --> <vxe-table :data="dataStatisticsObj.tableData" style="width: 100%; height: 100%" border ref="table" :filter-config="{ remote: true }" @filter-change="handleFilterChange" :custom-config="{ placement: 'top-left', storage: true }" :column-config="columnConfig, columnDragConfig" :toolbar-config="toolbarConfig" auto-resize > <vxe-column title="序号" type="seq" align="center" :width="80 * scale"></vxe-column> <vxe-column field="handleUser" align="center" title="标注人员" show-header-overflow show-overflow="title" show-footer-overflow :filters="[{ data: '' }]" sortable> <template #filter="{ column }"> <FuzzySearch ref="fuzzySearch" type="input" @reset="handleReset" @filter="(value) => handleColumnFilter(column, value)" label="标注人员" @changeTab="changeTab" /> </template> </vxe-column> <vxe-column field="towerNum" align="center" title="杆塔数量" show-header-overflow show-overflow="title" show-footer-overflow :filters="[{ data: '' }]" sortable> <template #filter="{ column }"> <FuzzySearch ref="fuzzySearch" type="input" @reset="handleReset" @filter="(value) => handleColumnFilter(column, value)" label="杆塔数量" @changeTab="changeTab" /> </template> </vxe-column> <vxe-column field="markNum" align="center" title="缺陷数据" show-header-overflow show-overflow="title" show-footer-overflow :filters="[{ data: '' }]" sortable> <template #filter="{ column }"> <FuzzySearch ref="fuzzySearch" type="input" @reset="handleReset" @filter="(value) => handleColumnFilter(column, value)" label="缺陷标注" @changeTab="changeTab" /> </template> </vxe-column> <vxe-column field="allNum" align="center" title="处理数据" show-header-overflow show-overflow="title" show-footer-overflow :filters="[{ data: '' }]" sortable> <template #filter="{ column }"> <FuzzySearch ref="fuzzySearch" type="input" @reset="handleReset" @filter="(value) => handleColumnFilter(column, value)" label="处理数据" @changeTab="changeTab" /> </template> </vxe-column> </vxe-table> <!-- 新增分页 --> <!-- 添加分页 --> <div class="pageBox"> 共 {{ dataStatisticsObj.total }} 条数据 <el-pagination background :page-sizes="[10, 20, 50, 100]" :page-size="dataStatisticsObj.page.pageSize" :total="dataStatisticsObj.total" :current-page="dataStatisticsObj.page.pageNum" layout="prev, pager, next, jumper, sizes" @current-change="handleCurrentChangeStats" @size-change="handleSizeChangeStats" > </el-pagination> </div> </div> </div> </el-dialog> </div> </template> <script> import { getProject, getProjectTower, } from "@/api/jobManagement/jobManagementUrl"; import { getCommonReadFile } from '@/api/common' // import dataStatistics from './dataStatistics' //隐藏 import { deletePic, downloadPic, exportDronePic, getPicFun, getSampleEngineerTree, getSampleTaskTree, getWorkCount } from '@/api/dataManageMent' import moment from "moment"; import { getMajorType, getSysDictionary } from "@/api/publicUrl"; import viewImg from "./viewImg"; import manuallyAnnotated from "./manuallyAnnotated"; import { requestParams, resetAddress } from '@/api/url' import { Loading } from 'element-ui' import FuzzySearch from "@/views/FuzzySearch.vue" export default { name: "annotationTable", components: { viewImg, manuallyAnnotated, // dataStatistics, //隐藏 FuzzySearch }, data() { return { view: { isShow: false, obj: {}, }, activeName: "task", treeKey: 0, loading: false, tree_data: [], listIndex: 0, srcList: [], defaultProps: { children: "child", label: "title", }, uro: require("../../../../../assets/images/operation/example.jpg"), gtbhList:[], params: { gtbh: "", //杆塔id companyId: "", //单位id lineName: "", //线路id dydj: "", //电压等级 taskMajor: "", //专业 startTime: "", //开始时间 endTime: "", //结束时间 handleStartTime:'', handleEndTime:'', isMark: "", //是否标注 isArc: "", //是否标注 picType: "", //图片类型 uploadtype: "", //数据来源 taskIds: "", //任务id projectIds: "", //工程id handleFlag: '2', // handleUser: '', // taskName: "", // 新增任务名称参数 }, AnnotatedRow: {}, timeValue: [], timeValue1: [], tableData: [], imageObj: {}, MajorList: [], dydjList: [], gcxxList: [],//左侧工程树与右侧工程名称下拉框联动(左侧树有选中节点时下拉框显示选中的内容,如果没有选中的显示初始化查询的所有工程名称) gcxxListAll: [],//初始化查询的所有工程名称 gcgtList: [], page: { pageNum: 1, pageSize: 10, }, total: 0, upDialogObj: { isShow: false, title: "上传", width: "50%", top: "11vh", "modal-append-to-body": false, "close-on-click-modal": false, "append-to-body": false, }, dataStatisticsObj:{ isShow: false, tableData: [], // 新增:以下 total: 0, page: { pageNum: 1, pageSize: 10 }, }, dialogUploadObj: { isShow: false, title: "图像标注", width: "99%", // width: "96%", // top: "11vh", top: "1vh", "modal-append-to-body": false, "close-on-click-modal": false, "append-to-body": true, }, taskSearch:'',//任务搜索 engineeringSearch:'',//工程搜索 scale: document.documentElement.clientWidth / 1920, // 新增:从子组件移入 // 新增数据统计表格的筛选条件 columnFilters: { handleUser: '', towerNum: '', markNum: '', allNum: '' }, //新增:当前筛选条件 filters: {}, // 新增:字段设置的工具栏配置项 toolbarConfig: { custom: true, // 启用自定义列功能 customStorage: true, // 启用自定义列的本地存储 }, // 相关配置项 columnConfig: { drag: true, // 启用表头左右拖拽排序功能 resizable: true, // 启用拖拽列宽功能 }, columnDragConfig: { trigger: 'cell', showIcon: false, showGuidesStatus: true }, filterTags: [], // 新增:tag标签用于存储筛选条件的标签 }; }, watch: { taskSearch(val) { this.$refs.treeRefImg.filter(val); }, engineeringSearch(val) { this.$refs.treeRefImg.filter(val); }, // 新增: dataStatisticsObj: { handler(newVal) { // 从子组件移入的watch }, deep: true, immediate: true } }, mounted() { this.getPicFun(); this.getMajorType(); this.getSysDictionary(0); this.handleClick(); this.getProject() // 新增:连接vxe-toolbar和vxe-table const $table = this.$refs.table; const $toolbar = this.$refs.toolbarRef; if ($table && $toolbar) { $table.connect($toolbar); } }, methods: { filterNode(value, data) { if (!value) return true; if(!data.title) return true; return data.title.indexOf(value) !== -1; }, handleNodeClick(val){ var _this = this; _this.params.projectIds = ""; _this.params.taskIds = ""; _this.params.lineName = ""; var data = this.$refs.treeRefImg.getCheckedNodes(); var Index = data.findIndex((item) => { return item == val; }); if(Index== -1){ data.push(val) }else { data.splice(Index, 1) } if (_this.activeName == "task") { data.forEach((res) => { if(res.taskId){ _this.params.taskIds += res.taskId + ","; } }); } else { if(data.length > 0){ let arr = [] data.forEach((res) => { if(res.projectId){ if(res.projectId) _this.params.projectIds += res.projectId + ","; } arr.push({basicld:res.projectId,basicName:res.projectName}) }); this.gcxxList = arr }else{ _this.gcxxList = _this.gcxxListAll; } } _this.getPicFun(); this.$refs.treeRefImg.setCheckedNodes(data); }, // handleNodeClick(data){ // var _this = this; // _this.params.projectIds = ""; // _this.params.taskIds = ""; // if (_this.activeName == "task") { // _this.params.taskIds = data.taskId || ''; // } else { // _this.params.projectIds += data.projectId || ""; // } // _this.getPicFun(); // // }, // 隐藏原代码 // check(val, node) { // var _this = this; // _this.params.projectIds = ""; // _this.params.taskIds = ""; // if (_this.activeName == "task") { // node.checkedNodes.forEach((res) => { // _this.params.taskIds += res.taskId + ","; // }); // } else { // if(node.checkedNodes && node.checkedNodes.length > 0){ // let arr = [] // node.checkedNodes.forEach((res) => { // _this.params.projectIds += res.projectId + ","; // arr.push({basicld:res.projectId,basicName:res.projectName}) // }); // this.gcxxList = arr // this.params.lineName = '' // }else{ // this.gcxxList = this.gcxxListAll // this.params.lineName = '' // } // } // _this.getPicFun(); // }, // 修改以上代码 // 修改check方法,处理树节点选中时的联动 check(val, node) {debugger var _this = this; _this.params.projectIds = ""; _this.params.taskIds = ""; _this.params.lineName = ""; // 清空工程名称选择 _this.gtbhList = []; // 清空杆塔选择 if (_this.activeName == "task") { node.checkedNodes.forEach((res) => { _this.params.taskIds += res.taskId + ","; }); } else { // 工程树逻辑 let projectIds = []; let projectNames = []; let arr = [] node.checkedNodes.forEach((res) => { if (res.projectId) { projectIds.push(res.projectId); projectNames.push(res.projectName); _this.params.projectIds += res.projectId + ","; } arr.push({basicld:res.projectId,basicName:res.projectName}) }); this.gcxxList = arr // 更新工程名称下拉框选项 if (projectIds.length > 0) { // 新增:如果选中了工程节点,则只显示选中的工程 // _this.gcxxList = projectIds.map((id, index) => ({ // basicId: id, // basicName: projectNames[index] // })); // 自动查询第一个选中工程的杆塔数据 if (projectIds[0]) { this.getProjectTower(projectIds[0]); } } else { // 新增:如果没有选中任何工程节点,则显示所有工程 _this.gcxxList = _this.gcxxListAll; } } _this.getPicFun(); }, getImgUrl(obj) { // url = requestParams.dronePic+url obj.fileType = obj.fileType ? obj.fileType : 0; var URL= obj.sourceThumbUrl?obj.sourceThumbUrl:obj.sourceUrl var url = requestParams.ImgPathOss + "/uavMap/data/readFile?filePath=" + URL + "&fileType=" + obj.fileType; // console.log(url.replaceAll('#','%23')) // // getCommonReadFile({fileType:obj.fileType,filePath:obj.urlPath,ossPath:obj.urlPath}).then(res=>{ // let url1 = window.URL.createObjectURL(res); // console.log(url1) // }) return resetAddress(url); // return url1.toString(); }, handleClick() { var _this = this; _this.engineeringSearch = '' _this.taskSearch = '' this.gcxxList = this.gcxxListAll if (_this.activeName == "task") { getSampleTaskTree().then((res) => { _this.tree_data = res.data; }); } else { getSampleEngineerTree().then((res) => { _this.tree_data = res.data; }); } _this.treeKey+=1 }, pre() { if (this.page.pageNum == 1) { } else { this.page.pageNum--; this.getPicFun(); } }, next(type) { if(type){ this.page.pageNum = 1; }else{ this.page.pageNum = this.page.pageNum += 1; } this.getPicFun(); }, closeImgBox() { this.view.isShow = false; }, viewimg(row,index) { // this.listIndex = index+1 var Index = this.tableData.findIndex((item) => { return item.keyId == row.keyId; }); this.view.listIndex = Index; this.view.obj = row; this.view.obj.urlPath = row.sourceUrl this.view.isShow = true; }, modifyTest(row) { this.AnnotatedRow = row; this.dialogUploadObj.isShow = true; }, taskMajorChange(val) { // this.MajorList.filter((item, index) => { // return item.majorType == val; // }); }, dydjListChange(val) { let arr = this.dydjList.filter((item, index) => { return item.typeValue == val; }); this.getProject(arr[0]); }, getProject(val) { var _this = this; getProject({ voltageName: val?val.typeName:'', majorType: _this.params.taskMajor?_this.params.taskMajor:'0', voltageLevel: val?val.typeValue:'', }).then((res) => { _this.gcxxListAll = res.data; _this.gcxxList = res.data; }); }, // 原代码隐藏 // gcxxListChange(val) { // this.getProjectTower(val); // // var list = this.gcxxList.filter(item => { // // return item.basicId == val // // }); // }, // 修改以上代码 修改工程名称变化处理方法 gcxxListChange(val) { if (val) { this.getProjectTower(val); } else { this.gcgtList = []; // 清空杆塔列表 this.gtbhList = []; // 清空杆塔选择 } this.getPicFun(); }, // 新增:杆塔选择变化处理方法 handleGtbhChange(val) { this.getPicFun(); }, // 隐藏原代码 // getProjectTower(val) { // var _this = this; // getProjectTower({ keyId: val, majorType: _this.params.taskMajor }).then( // (res) => { // _this.gcgtList = res.data; // } // ); // }, // 新增:修改以上代码:获取杆塔数据方法 getProjectTower(val) { var _this = this; getProjectTower({ keyId: val, majorType: _this.params.taskMajor }).then((res) => { _this.gcgtList = res.data; // 可优化:自动选中第一个杆塔(可选) // if (res.data.length > 0) { // _this.gtbhList = [res.data[0].keyId]; // } }).catch(() => { _this.gcgtList = []; }); }, downloadC(row) { let that = this; let str = "", type = ".zip"; let parmas = {}; that.params.startTime = that.timeValue ? that.timeValue[0] : ""; that.params.endTime = that.timeValue ? that.timeValue[1] : ""; that.params.handleStartTime = that.timeValue1 ? that.timeValue1[0] : ""; that.params.handleEndTime = that.timeValue1 ? that.timeValue1[1] : ""; that.params.gtbh = that.gtbhList.join(",") that.loading = true downloadPic({ ...that.params }).then((data) => { if (!data) { return; } let time = new Date(); let url = window.URL.createObjectURL(new Blob([data])); let link = window.document.createElement("a"); link.style.display = "none"; link.href = url; link.setAttribute("download", moment(time).format("YYYYMMDD") + type); document.body.appendChild(link); link.click(); //释放内存 window.URL.revokeObjectURL(link.href); that.loading = false }).catch(e=>{ that.loading = false }); return; if (row) { str += row.keyId + ","; str = str.slice(0, str.length - 1); parmas = { keyId: str, type: 1, }; type = ".zip"; that.params.startTime = that.timeValue ? that.timeValue[0] : ""; that.params.endTime = that.timeValue ? that.timeValue[1] : ""; that.params.handleStartTime = that.timeValue1 ? that.timeValue1[0] : ""; that.params.handleEndTime = that.timeValue1 ? that.timeValue1[1] : ""; downloadPic({ ...that.params }).then((data) => { if (!data) { return; } let time = new Date(); let url = window.URL.createObjectURL(new Blob([data])); let link = window.document.createElement("a"); link.style.display = "none"; link.href = url; link.setAttribute("download", moment(time).format("YYYYMMDD") + type); document.body.appendChild(link); link.click(); //释放内存 window.URL.revokeObjectURL(link.href); }); } }, deletePic(val) { var _this = this; deletePic({ keyId: val.keyId, fileUrl: val.sourceUrl }).then((res) => { if (res.code == 200) { _this.$message({ message: "删除成功", type: "success", }); } else { _this.$message({ message: "删除失败", type: "error", }); } _this.getPicFun(); }); }, getFile(row) { // debugger let url = process.env.VUE_APP_BASE_API + "/uavMap/data/readFile?filePath=" + row.filePath + "&fileType=" + row.fileType; window.open(resetAddress(url)); }, getSysDictionary(sum) { var _this = this; getSysDictionary({ parentId: sum }).then((res) => { if (sum == 0) { _this.dydjList = res.data; } }); }, getMajorType() { var _this = this; getMajorType({}).then((res) => { _this.MajorList = res.data; }); }, getPicFun() { let instance = Loading.service({ text: "正在查询数据,请稍候", spinner: "el-icon-loading", background: "rgba(0, 0, 0, 0.7)", }); var _this = this; _this.params.startTime = _this.timeValue ? _this.timeValue[0] : ""; _this.params.endTime = _this.timeValue ? _this.timeValue[1] : ""; _this.params.handleStartTime = _this.timeValue1 ? _this.timeValue1[0] : ""; _this.params.handleEndTime = _this.timeValue1 ? _this.timeValue1[1] : ""; _this.params.gtbh = _this.gtbhList.join(",") $(".v-modal").remove(); getPicFun({ ..._this.page, ..._this.params }).then((res) => { _this.tableData = res.data.rows; _this.view.srcList = []; _this.tableData.forEach((item,index)=>{ var url = requestParams.ImgPathOss +"/uavMap/data/readFile?filePath="+item.sourceUrl+"&fileType="+item.fileType; _this.view.srcList.push(resetAddress(url)) }) // _this.tableData = _this.tableData.concat(res.data.rows); _this.total = res.data.total; _this.imageObj = res.data; _this.imageObj["pageNum"] = _this.page.pageNum; _this.srcList = [] // _this.tableData.forEach(obj=>{ // getCommonReadFile({fileType:obj.fileType,filePath:obj.sourceUrl,ossPath:obj.sourceUrl}).then(res=>{ // let url1 = window.URL.createObjectURL(res); // _this.srcList.push(url1) // console.log(_this.srcList) // }) // }) instance.close(); _this.$refs.examineInitMap.preCG(_this.tableData); }).catch(e=>{ instance.close(); }); }, // 新增分页选择器大小 handleSizeChange(val) { this.page.pageSize = val; //更新每页显示条数 this.getPicFun(); //重新获取数据 }, handleCurrentChange(val) { this.page.pageNum = val; this.getPicFun(); }, // 新增分页 handleCurrentChangeStats(val) { this.dataStatisticsObj.page.pageNum = val; this.getWorkCount(); }, // 数据统计表格的每页条数变化 handleSizeChangeStats(val) { this.dataStatisticsObj.page.pageSize = val; this.getWorkCount(); }, getWorkCount(){ var _this = this; // 设置处理时间参数 // _this.params.handleStartTime = _this.timeValue1 ? _this.timeValue1[0] : ""; // _this.params.handleEndTime = _this.timeValue1 ? _this.timeValue1[1] : ""; // 合并表单查询条件和列筛选条件 let queryParams = { ...this.params, ...this.columnFilters, pageNum: this.dataStatisticsObj.page.pageNum, pageSize: this.dataStatisticsObj.page.pageSize, }; // 暂时隐藏不可删除 _this.params.startTime = _this.timeValue ? _this.timeValue[0] : ""; _this.params.endTime = _this.timeValue ? _this.timeValue[1] : ""; _this.params.handleStartTime = _this.timeValue1 ? _this.timeValue1[0] : ""; _this.params.handleEndTime = _this.timeValue1 ? _this.timeValue1[1] : ""; _this.params.gtbh = _this.gtbhList.join(",") _this.dataStatisticsObj.isShow = true getWorkCount(queryParams).then(res =>{ _this.dataStatisticsObj.tableData = res.data.rows || res.data; // 根据后端返回结构调整, _this.dataStatisticsObj.total = res.data.total || res.data.length; // 根据后端返回结构调整; }) }, exportDronePic() { var _this = this; _this.params.startTime = _this.timeValue ? _this.timeValue[0] : ""; _this.params.endTime = _this.timeValue ? _this.timeValue[1] : ""; _this.params.handleStartTime = _this.timeValue1 ? _this.timeValue1[0] : ""; _this.params.handleEndTime = _this.timeValue1 ? _this.timeValue1[1] : ""; _this.params.gtbh = _this.gtbhList.join(",") exportDronePic({ ..._this.params }).then((data) => { let time = new Date(); let url = window.URL.createObjectURL(new Blob([data])); let link = window.document.createElement("a"); link.style.display = "none"; link.href = url; link.setAttribute( "download", "样本库列表" + this.parseTime(new Date(), "{y}{m}{d}{h}{i}{s}") + ".xlsx" ); document.body.appendChild(link); link.click(); //释放内存 window.URL.revokeObjectURL(link.href); }); }, // 数据统计表格的列筛选 handleColumnFilter(column, value) { // 清除每个重复展示的筛选条件 this.filterTags = this.filterTags.filter(tag => tag.prop !== column.property); // 映射列字段到查询字段 const fieldMap = { handleUser: 'handleUser', towerNum: 'towerNum', markNum: 'markNum', allNum: 'allNum' }; if (value || value == 0) { let displayValue = value; // 将筛选条件添加到 filterTags的el-tag标签中 this.filterTags.push({ label: column.title, value: Array.isArray(displayValue) ? displayValue.join(', ') : displayValue, prop: column.property, }); // 更新对应的查询字段 const queryField = fieldMap[column.property] || column.property; this.columnFilters[column.property] = value; this.params[queryField] = value; } else { // 清除筛选条件 const queryField = fieldMap[column.property] || column.property; delete this.columnFilters[column.property]; } this.handleFilterChange({ filters: this.filters }); }, // 移除数据统计表格的筛选标签 removeFilterTag(index) { const removedTag = this.filterTags.splice(index, 1)[0]; // 映射列字段到查询字段 const fieldMap = { companyName: 'companyName', taskName: 'taskName', workDescribe: 'workDescribe', implementTypeName: 'implementType', taskTypeName: 'taskType', planStateName: 'planState', createUser: 'createUser' }; // 清除对应的查询条件 const queryField = fieldMap[removedTag.prop] || removedTag.prop; delete this.params[queryField]; delete this.columnFilters[removedTag.prop]; this.getWorkCount(); }, // 筛选变化事件 handleFilterChange({ column }) {}, // 重置筛选条件 handleReset() { this.filters = {}; this.getWorkCount(); }, changeTab() { this.$nextTick(() => { this.$refs.table.closeFilter(); this.$refs.table.clearFilter(); }); } }, }; </script> <style lang="scss" scoped> .pageBox { display: flex; justify-content: flex-end; align-items: center; color: rgba($color: #fff, $alpha: 0.6); margin-top: 16px; letter-spacing: 1px; ::v-deep .el-pagination { .el-pager li, .btn-prev, .btn-next { width: 34px; height: 34px; color: #fff; font-size: 16px; background: rgba($color: #0045c0, $alpha: 0.2); border: 1px solid rgba($color: #005cff, $alpha: 0.9); line-height: 34px; padding: 0; } .el-pager li.active { background: #005cff; } .el-pagination__jump { color: rgba($color: #fff, $alpha: 0.6); margin-left: 20px; .el-pagination__editor { margin: 0 5px; .el-input__inner { background: rgba($color: #0045c0, $alpha: 0.4); height: 34px; color: #fff; border: 0; } } } .el-pagination__sizes { color: #ffffff !important; .el-input__inner { height: 34px; font-size: 16px; background: rgba($color: #0045c0, $alpha: 0.2); border: 1px solid rgba($color: #005cff, $alpha: 0.9); color: #ffffff !important; } } } } // 新增:引入定义好的样式文件 @import "@/assets/styles/vxeClass.scss"; .body { width: calc(100% - 60px); height: calc(100% - 190px); position: fixed; left: 20px; top: 150px; display: flex; //flex-direction: column; flex-direction: initial; .annotation_Table { width: 20%; height: 100%; //background: #f0f8ff21; .contentBox { width: 100%; flex: 1; //margin-top: 20px; margin-bottom: 20px; ::v-deep .el-tabs { .el-tabs__header { margin: 0; } .el-tabs__nav { width: 100%; display: flex; .el-tabs__item { flex: 1; height: 32px; padding: 0; display: flex; justify-content: center; align-items: center; font-size: 14px; color: #fff; letter-spacing: 1px; background: rgba($color: #000c23, $alpha: 0.3); } .is-active { background: rgba($color: #469bff, $alpha: 0.4); } } .el-tabs__nav-wrap::after { height: 4px; background: rgba($color: #469bff, $alpha: 0.4); } .el-tabs__active-bar { height: 4px; background: #469bff; } ::v-deep .el-tabs__active-bar { width: 200px !important; transform: translateX(0); } } } ::v-deep .el-tree { background: transparent; color: #fff; overflow: auto; height: 586px; //原本675px flex: 1; .el-icon-caret-right:before { //display: none; } >.el-tree-node { min-width: 100%; display: inline-block; } //.el-tree-node__expand-icon { // width: 14px; // height: 14px; // background: url("../../../../../../assets/images/operation/add.png") no-repeat; // background-size: 100% 100%; // // margin-left: -25px; //} .custom-tree-node { margin-left: 14px; } //.el-tree-node__expand-icon.expanded { // transform: rotateY(0); // background: url("../../../../../../assets/images/operation/reduce.png") // no-repeat; // background-size: 100% 100%; //} .el-tree-node__expand-icon.is-leaf { //display: none; } .el-tree-node__content { // margin-left: 42px; } .el-tree-node__content:hover { background: rgba($color: #469bff, $alpha: 0.3); } .el-tree-node:focus>.el-tree-node__content { background: rgba($color: #469bff, $alpha: 0.8); } } ::v-deep .el-tree--highlight-current .el-tree-node.is-current>.el-tree-node__content { background: rgba($color: #469bff, $alpha: 0.8); } } .image_lazy_right { width: 80%; height: 100%; //background: rgba(0, 137, 255, 0.13); .date-picker-box { width: 360px; } .select-box { width: 360px; } .input-box { width: 360px; } .demo-image__lazy { display: flex; height: 65%; overflow-y: auto; } .imgBox { display: flex; flex-wrap: wrap; .imgList { position: relative; width: 275px; margin: 0px 10px 0px 10px; overflow: hidden; .el-image { width: 100%; height: 165px; } } .typeNeme { position: absolute; top: 0px; background: #ffb802c7; padding: 2px 25px 2px 10px; border-bottom-right-radius: 19px; color: white; font-size: 13px; } .isWenTi { position: absolute; top: 145px; background: #000000a6; padding: 2px 15px 2px 10px; /* border-bottom-right-radius: 19px; */ color: white; font-size: 13px; } .typeGD { position: absolute; top: 12px; right: -27px; background: #00a3ff; padding: 2px 32px; -webkit-transform: rotate(45deg); transform: rotate(45deg); color: #ffffff; font-size: 13px; } .imgText { color: #ffffff; position: relative; margin-bottom: 10px; font-size: 12px; .imgTextLi { position: absolute; right: 10px; bottom: 0px; color: #00a3ff; cursor: pointer; } } } } .btn { width: 80px; height: 28px; cursor: pointer; display: flex; align-items: center; justify-content: center; } .audit { color: #07ffe1; background: rgba(0, 92, 255, 0.08); border-radius: 2px 2px 2px 2px; border: 1px solid; border-image: linear-gradient(180deg, rgba(7, 255, 225, 0.8), rgba(7, 255, 225, 0.24)) 1 1; margin-right: 8px; img { width: 14px; height: 14px; margin-right: 4px; } } .delete { color: #ff4e02; background: rgba(0, 92, 255, 0.08); border-radius: 2px 2px 2px 2px; border: 1px solid; border-image: linear-gradient(180deg, rgba(255, 78, 2, 0.8), rgba(255, 78, 2, 0.24)) 1 1; img { width: 14px; height: 14px; margin-right: 4px; } } .examine { color: #469bff; background: rgba(0, 92, 255, 0.08); border-radius: 2px 2px 2px 2px; border: 1px solid; border-image: linear-gradient(180deg, rgba(0, 92, 255, 0.8), rgba(0, 92, 255, 0.24)) 1 1; margin-right: 8px; img { width: 14px; height: 14px; margin-right: 4px; } } .downloadC { color: #e6a23c; background: rgba(0, 92, 255, 0.08); border-radius: 2px 2px 2px 2px; border: 1px solid; border-image: linear-gradient(180deg, rgba(236, 162, 60, 0.8), rgba(236, 162, 60, 0.24)) 1 1; margin-right: 8px; img { width: 14px; height: 14px; margin-right: 4px; } } // 新增:从子组件移入的样式 //修改后的样式 .data-statistics-dialog { ::v-deep .el-dialog { height: 70vh !important; // 使用视口高度单位 display: flex; flex-direction: column; .el-dialog__body { flex: 1; padding: 20px; overflow: hidden; display: flex; flex-direction: column; } } .body-tabel { flex: 1; display: flex; flex-direction: column; overflow: hidden; .body-tabels { flex: 1; overflow: hidden; overflow-y: auto; display: flex; flex-direction: column; .vxe-table { flex: 1; display: flex; flex-direction: column; overflow: hidden; overflow-y: auto; .vxe-table--body-wrapper { flex: 1; overflow: auto; } } } } } } </style>
09-09
<!-- 工艺模板 --> <template> <el-container v-loading="loading"> <!-- 工具栏 --> <el-header> <!-- 左侧工具栏 --> <!-- <div class="left-panel"> <el-button type="primary" icon="el-icon-plus" @click="edit()">新增</el-button> </div> --> <!-- 右侧工具栏 --> <div class="right-panel"> <div class="right-panel-search"> <el-input v-model="procedureData.name" placeholder="名称" clearable /> <el-button icon="el-icon-refresh" @click="refreshProcedure">查看所有</el-button> <el-button type="primary" icon="el-icon-search" @click="getProcedure(0)">搜索</el-button> <el-button type="primary" icon="el-icon-plus" @click="editProcedure()">新增</el-button> </div> </div> </el-header> <!-- 列表 --> <el-main class="nopadding"> <scTable :data="procedureData.data" style="width: 100%" row-key="id" :getList="getProcedure" hidePagination :header-cell-style="{ background: '#F1F3F0', color: '#7B7571' }"> <el-table-column label="ID" prop="id" width="50"></el-table-column> <el-table-column label="编号" prop="rule_number"></el-table-column> <el-table-column label="工艺名称" prop="name"></el-table-column> <el-table-column label="操作人" prop="user_name"></el-table-column> <el-table-column label="创建时间" prop="create_time"></el-table-column> <el-table-column label="操作" fixed="right" width="150"> <template #default="scope"> <el-button plain type="primary" size="small" @click="editProcedure(scope.row)">编辑</el-button> <el-button plain type="primary" size="small" @click="delProcedure(scope.row)">删除</el-button> </template> </el-table-column> <template #pagination> <el-pagination layout="prev, pager, next, total, sizes" :current-page="procedureData.current_page" :page-size="procedureData.per_page" :total="procedureData.total" :page-sizes="[20, 50, 100]" @size-change=" (num) => { procedureData.per_page = num; getProcedure(1); } " @current-change="getProcedure" /> </template> </scTable> </el-main> <!-- 工艺工序新增编辑模板 --> <el-dialog v-model="ditVisibleGy" :close-on-click-modal="false" width="80%" :fullscreen="isFullscreen" class="dialogGxgy"> <!-- 自定义标题栏 --> <template #title> <div class="dialogtitle"> <h4 style="font-size: 17px;">{{ procedureForm.id ? '编辑工艺模板' : '新增工艺模板' }}</h4> <div class="dialog-icons"> <!-- 全屏图标 --> <el-icon @click="toggleFullscreen" class="fullscreen-icon"> <component :is="isFullscreen ? ZoomOut : FullScreen" /> </el-icon> </div> </div> </template> <el-header> <div class="gybs"> <div class="gyheader"> <h4>模板名称:</h4> <el-input v-model="procedureForm.name" placeholder="请输入模板名称" style="width: 200px" /> </div> <!-- <el-upload class="sc-file-select__upload" :action="action" multiple :show-file-list="false" :accept="accept" :before-upload="uploadBefore" :on-success="uploadSuccess" :on-error="uploadError" :headers="uploadHeaders" :data="uploadData"> <el-button type="primary" icon="el-icon-upload">上传图纸</el-button> </el-upload> --> <!-- 图片列表,横向滚动 --> </div> </el-header> <el-container v-loading="gymbLoading"> <el-aside width="250px" height="400px"> <el-card class="card_gy"> <!-- 拖拽排序 --> <div class="tzpx"> <fcDraggable v-model="itemsgy" :sort="true" handle=".icon-drag" itemKey="gongxumingcheng" direction="vertical" :animation="300" @end="onDragEnd"> <template #item="{ element, index }"> <div class="slx"> <div class="sortable-item" @click="selectItem(element)" :class="{ 'selected': selectedItem === element }"> <div class="item_left"> <i class="fc-icon icon-drag"></i> </div> <div class="item_content"> {{ element.gongxumingcheng }} </div> <div class="item_right"> <i class="fc-icon icon-delete" @click.stop="removeField(index)"></i> </div> </div> </div> </template> </fcDraggable> <el-empty description="暂无工序" :image-size="120" v-if="itemsgy.length === 0" /> </div> <el-button-group class="btgp"> <el-button type="primary" @click="addProcedure">添加</el-button> <el-button type="primary" @click="changeGx">选择工序</el-button> </el-button-group> </el-card> </el-aside> <el-container class="" v-loading="bomLoading" v-if="itemsgy.length > 0"> <el-header> <H3 class="ht">工序参数</H3> </el-header> <div class="formbody"> <div class="gyform" v-if="isFormFilled"> <form-create v-model:api="api" :rule="formRule" :option="formCreateOptions" /> </div> <el-empty description="请先选择工序" v-show="!isFormFilled" /> <div class="gycs" v-if="itemsgy.length > 1"> <div class="dowt"> <H3>工艺路线图</H3> </div> <div class="route"> <el-steps :active="itemsgy.length" :space="150" align-center finish-status="primary" process-status="primary"> <el-step v-for="(item, index) in itemsgy" :key="index" :title="item.gongxumingcheng" /> </el-steps> </div> </div> </div> </el-container> <div v-else style="width: 100%;"> <el-empty description="请先选择工序模板" /> </div> <el-aside width="200px" class="lf_aside"> <div class="image-preview-list"> <el-scrollbar height="400px"> <el-upload class="uploadTz" :action="action" multiple :show-file-list="false" :on-preview="handlePictureCardPreview" :before-upload="uploadBefore" :on-success="uploadSuccess" :on-error="uploadError" :headers="uploadHeaders" :data="uploadData" > <el-button type="primary">上传图纸</el-button> </el-upload> <div class="upbox"> <div class="wdg" v-for="(item, index) in fileList" :key="index"> <div class="wg_lf" @click="handlePictureCardPreview(item)"> <el-icon size="18"> <DocumentRemove /> </el-icon> <el-tooltip class="box-item" effect="dark" :content="item.original_name" placement="top"> <div class="weds">{{ item.original_name }}</div> </el-tooltip> </div> <div> <el-button type="danger" :icon="Delete" circle size="small" @click="delImg(index)"/> </div> </div> </div> <el-dialog v-model="dialogVisibleImg"> <img w-full :src="dialogImageUrl" alt="Preview Image" /> </el-dialog> </el-scrollbar> </div> </el-aside> </el-container> <template #footer> <span class="dialog-footer"> <el-button @click="ditVisibleGy = false">关闭</el-button> <el-button type="primary" @click="submitProcesses" :loading="gyloading"> 保存工艺模板 </el-button> </span> </template> </el-dialog> <!-- 选择工序弹窗 --> <el-dialog v-model="dialogVisiblexzgx" title="选择工序" width="60%"> <el-container> <el-aside width="200px"> <el-tree ref="dic" class="menu" node-key="id" :data="gxtypeList" :props="{ label: 'name', }" :highlight-current="true" :expand-on-click-node="false" :filter-node-method="dicFilterNode" @node-click="selectgxClass"> <template #default="{ node, data }"> <span class="custom-tree-node"> <span class="label">{{ node.label }}</span> <span class="code">{{ data.code }}</span> </span> </template> </el-tree> </el-aside> <el-main v-loading="tableloading"> <el-header v-if="processesList.data.length > 0"> <div class="right-panel"> <el-input v-model="processesList.name" placeholder="标题" class="titlese" /> <el-button icon="el-icon-refresh" @click="refresprocessesList">查看所有</el-button> <el-button type="primary" icon="el-icon-search" @click="getTemplate(1)"></el-button> </div> </el-header> <el-main> <scTable :data="processesList.data" ref="tableRef" style="width: 100%" row-key="id" :getList="getitem" @row-click="handleRowClick" @selection-change="handleSelectionChange"> <el-table-column type="selection" width="50"></el-table-column> <el-table-column prop="id" label="ID" width="50" /> <el-table-column prop="gongxumingcheng" label="工序名称" fixed="right" /> <el-table-column prop="user_name" label="创建人" fixed="right" /> <el-table-column prop="create_time" label="创建时间" fixed="right" /> <template #pagination> <el-pagination layout="prev, pager, next, total, sizes" :current-page="processesList.current_page" :page-size="processesList.per_page" :total="processesList.total" :page-sizes="[20, 50, 100]" @size-change=" (num) => { processesList.per_page = num; getTemplate(1); } " @current-change="getTemplate" /> </template> </scTable> </el-main> </el-main> </el-container> <template #footer> <div class="dialog-footer"> <el-button @click="dialogVisiblexzgx = false">取消</el-button> <el-button type="primary" @click="selectProcess" :loading="selectProcessLoading"> 确定 </el-button> </div> </template> </el-dialog> <!-- 新增工序 公共页面弹窗新增 --> <el-dialog v-model="ditVisibleProcedure" :close-on-click-modal="false" width="70%" title="工序新增"> <form-create v-model:api="api" :rule="formRuleAdd" :option="formCreateOptions" /> </el-dialog> </el-container> </template> <script setup> import { ref, watch, reactive, nextTick } from "vue"; import request from "@/utils/httpRequest"; import { ElMessage,ElMessageBox } from "element-plus"; import fcDraggable from "vuedraggable"; import formCreate from "@form-create/element-ui"; import { getFormRuleDescription } from "@/components/formCreate/src/utils/index"; // js工具 import utils from "./components/utils"; import tool from "@/utils/tool"; import config from "@/config" import { DocumentRemove, Delete } from "@element-plus/icons-vue"; // 页面loading let loading = ref(false); //上传地址 const token = tool.cookie.get("TOKEN"); const action = ref(config.BASEURL + "/system/upload/upload"); const fileList = ref([]); //上传文件列表 const dialogVisibleImg = ref(false) const dialogImageUrl = ref('') //设置请求头 const uploadHeaders = ref({ token: token, // 添加 token 到 headers }); // 设置额外字段 const uploadData = ref({ is_annex: 1, // 添加额外字段 is_annex group_id: 0, }); // 列表数据 const procedureData = ref({ current_page: 1, per_page: 10, total: 0, data: [], name: '',//名称查询 }) // #region 定义工艺模板数据字段 const itemsgy = ref([])//工序列表 const formRule = ref([])//formcreate 组件 const selectedItem = ref({})// 记录当前选中工序的索引 const processLoading = ref(false)//loading const isEditor = ref(false)//用于判断是否走编辑接口 const ditVisibleGy = ref(false)//工艺模板弹窗 const formCreateOptions = ref({});//表单组件参数 //工艺form const procedureForm = ref({ good_id: '',//绑定产品id name: '',//工艺名称 // type_id: 0,//工艺分类 goods_name: '',//绑定产品名称 item: ''//工序节点 }); // #endregion // #region 数据列表 function refreshProcedure() { procedureData.value.name = '' getProcedure() } async function getProcedure(page = 1) { if (page) { procedureData.value.current_page = page; } try { loading.value = true; let data = await request.post("/system/workmanship.workmanship_template/index", { page: procedureData.value.current_page, limit: procedureData.value.per_page, name: procedureData.value.name }); procedureData.value.current_page = data.data.current_page procedureData.value.total = data.data.total procedureData.value.data = data.data.data loading.value = false; } catch (error) { loading.value = false; } } getProcedure() // #endregion // #region 新增编辑 //点击新增 编辑 async function editProcedure(row) { itemsgy.value = [] formRule.value = [] selectedItem.value = {} fileList.value = [] if (row) { try { loading.value = true; let data = await request.post("/system/workmanship.workmanship_template/read", { id: row.id }); loading.value = false if( data.data.data.file){ fileList.value = data.data.data.file } procedureForm.value = { // goods_id: data.data.data.goods_id,//绑定产品id name: data.data.data.name,//工艺名称 // type_id: '',//工艺分类 // goods_name: data.data.data.goods_name,//绑定产品名称 id: row.id } itemsgy.value = data.data.data.item for (const item of itemsgy.value) { try { let data = await request.post("/system/workmanship.workmanship_template/getItem", { id: item.id }); //value是组件的值 let content = formCreate.parseJson(data.data.form.content); const value = utils.decodeFormat(content, data.data.item, 2); item.options = value item.option = content //options是页面参数配置 formCreate.parseJson // const options = (data.data.form.options); const options = formCreate.parseJson(data.data.form.options) formCreateOptions.value = options formCreateOptions.value.submitBtn.show = false loading.value = false } catch (error) { loading.value = false; } } isEditor.value = true processLoading.value = false } catch (error) { processLoading.value = false } } else { itemsgy.value = [] procedureForm.value = { name: '',//工艺名称 // type_id: '',//工艺分类 goods_name: 24,//绑定产品名称 item: '',//工序节点 id: '' } isEditor.value = false } ditVisibleGy.value = true; } // #endregion // #region 获取工序分类 const dialogVisiblexzgx = ref(false); const gymbLoading = ref(false) const gxcclist = ref([])//存储当前选中的工序列表 const tableRef = ref(null); // 绑定 scTable 组件 const gxId = ref('')//工序id // 打开工序选择弹窗,并获取工序类型数据= function changeGx() { getgxtype() } // 获取工序模板分类数据(工序类别) async function getgxtype() { try { gymbLoading.value = true let data = await request.post("/system/dictionary/getChildListByCode", { code: 'gongxu_type' }); gxtypeList.value = data.data gxcclist.value = [] dialogVisiblexzgx.value = true; tableRef.value.clearSelection(); // 清空选中 gymbLoading.value = false; } catch (error) { gymbLoading.value = false; } } const gxtypeList = ref([])//已选中的工序 列表多选 //选择工序分类 function selectgxClass(row) { gxId.value = row.value getTemplate(1) } // #endregion // #region 获取工序模板 const tableloading = ref(false) const processesList = ref({ current_page: 1, per_page: 10, total: 0, data: [], name: '' })//工序模板列表 //工序form表单 const formGy = reactive({ workmanship_id: '',//工艺id name: '',//工序名称 piece_type: '',//计件方式 dept: '',//报工部门 process_quota: '',//工序定额1启用2停用 price_type: '',//计酬方式1计件2计工时 process_price: 0,//工序单价 deduction: 0,//不合格扣款 sort: 0,//排序 }); const bomLoading = ref(false) // 根据工序分类 ID 获取对应的工序列表 async function getTemplate(page = 1) { tableloading.value = true if (page) { processesList.value.current_page = page; } let data = await request.post("/system/processes.processes_template/index", { gongxufenlei_ids: gxId.value, limit: processesList.value.per_page, page: processesList.value.current_page, name: processesList.value.name }); tableloading.value = false processesList.value.current_page = data.data.current_page processesList.value.total = data.data.total // 更新工序列表 processesList.value.data = data.data.data nextTick(() => { recoverSelection(); // 拉完数据恢复勾选 }); } // 监听工序表格的多选事件,并更新已选中的工序数据 function handleSelectionChange(selection) { // gxcclist.value = selection // ⚡ selection 是当前页面选中的 // 我们需要同步到 gxcclist 里面,保证全局勾选 // 先删除本页数据在 gxcclist 里面的 const pageIds = processesList.value.data.map(item => item.id); gxcclist.value = gxcclist.value.filter(item => !pageIds.includes(item.id)); // 再把最新的 selection 加进去 gxcclist.value = [...gxcclist.value, ...selection]; } // 恢复勾选 function recoverSelection() { if (!tableRef.value) return; const tableData = processesList.value.data tableData.forEach((row) => { const isSelected = gxcclist.value.some(item => item.id === row.id) || itemsgy.value.some(item => item.id === row.id); tableRef.value.toggleRowSelection(row, isSelected); }); } // 是否有数据 const isFormFilled = ref(false); function handleRowClick(row) { tableRef.value.toggleRowSelection(row); } // 监听 formGy 变化 watch( formGy, (newVal) => { isFormFilled.value = Object.values(newVal).some( (value) => value !== "" && value !== null && value !== undefined ); }, { deep: true } ); const selectProcessLoading = ref(false)//loading 按钮 // 确认选择工序,并关闭弹窗 async function selectProcess() { itemsgy.value = gxcclist.value refreshGx() dialogVisiblexzgx.value = false } // #endregion //#region 刷新工序展示列表最新数据 async function refreshGx() { for (const item of itemsgy.value) { let form = {} const res = await request.post("/system/crud/read", { crud_id: 105, id: item.id }); // 格式化表单数据 const value = utils.decodeFormat( formCreate.parseJson(res.data.form.content), res.data.data, 2 ); // 表单渲染规则 form = value; const options = formCreate.parseJson(res.data.form.options); //默认给子表加上一条 form.forEach((item) => { if (item.field) { if (item.field.includes("sub_")) { if (item.value.length === 0) { item.value = [{}]; } } } }); // 编辑时删除默认数据,转而使用表单数据 // delete options.form; // 获取所有数据组件参数 const formData = formCreate.parseJson( formCreate.toJson(getFormRuleDescription(form)) ); formCreateOptions.value = options formCreateOptions.value.submitBtn.show = false // 整理格式 const postData = utils.formatForm(formData); // const psData = formData // **根据 id 匹配 itemsgy.value 数组的元素,并存入新字段 `options`** const targetItem = itemsgy.value.find(el => el.id === item.id); if (targetItem) { targetItem.options = postData; // 存入 options 字段 targetItem.option = form } } } // 记录用户点击的工序项(用于高亮显示) async function selectItem(item) { bomLoading.value = true selectedItem.value = item; // 更新选中状态 formRule.value = item.option;//选中 赋给表单 isFormFilled.value = true bomLoading.value = false } //删除对应已选工序模板 function removeField(index) { itemsgy.value.splice(index, 1); } // #endregion //#region 新增工序 弹出公共页面的新增 const ditVisibleProcedure = ref(false) const api = ref({}) const formRuleAdd = ref([]);//用于新增模板 async function addProcedure() { let res = await request.post("/system/crud/getCrudForm", { crud_id: 105 }); // 表单渲染规则 formRuleAdd.value = formCreate.parseJson(res.data.content) // 表单配置项 formCreateOptions.value = { onSubmit: async () => { // 表单验证通过后的提交方法 // 获取所有数据组件参数 let formData = formCreate.parseJson( formCreate.toJson(getFormRuleDescription(formRuleAdd.value)) ); // 整理格式 let postData = utils.formatForm(formData); await request.post("/system/crud/add", { crud_id: 105, form_data: formCreate.toJson(postData) }); ElMessage({ type: "success", message: "操作成功", }); ditVisibleProcedure.value = false }, ...formCreate.parseJson(res.data.options) }; ditVisibleProcedure.value = true } // #endregion const imageUrls = ref('') //#region 提交新增工艺 const gyloading = ref(false)//保存按钮loading async function submitProcesses() { if (gyloading.value) { return } // ✅ 防止双击或重复点击 if (!procedureForm.value.name) { ElMessage.error('请先填写工艺名称') return; } let newArray = []; // 先定义 newArray,确保在整个作用域内可访问 if (itemsgy.value.length > 0) { // 处理 itemsgy.value 数组,将 options 转换为 option 并删除 options 字段 newArray = itemsgy.value.map(item => { // 获取所有数据组件参数 let formData = formCreate.parseJson( formCreate.toJson(getFormRuleDescription(item.option)) ); const newItem = { ...item, option: utils.formatForm(formData)// 将 options 字段格式化后存入 option }; delete newItem.options;// 删除原 options 字段 return newItem;// 返回处理后的新对象 }) } if (fileList.value.length > 0) { // 提取所有上传图片的 URL imageUrls.value = fileList.value.map(file => file.full_path).join(','); } try { // 发送 POST 请求,将数据提交到后端接口 // let data = await request.post("/system/workmanship.workmanship_template/add", ); gymbLoading.value = true gyloading.value = true let url = procedureForm.value.id ? "/system/workmanship.workmanship_template/update" : "/system/workmanship.workmanship_template/add"; // 尝试提交 let data = await request.post(url, { name: procedureForm.value.name, // 工艺模板名称 // goods_id: procedureForm.value.goods_id, // 关联的商品 ID goods_name: procedureForm.value.goods_name, // 关联的商品名称 item: JSON.stringify(newArray), // 处理后的工艺项数据,转换为 JSON 字符串 id: procedureForm.value.id, file: imageUrls.value }); if (data.code === 1) { ElMessage({ message: '操作成功', type: 'success', }) await getProcedure() gymbLoading.value = false gyloading.value = false ditVisibleGy.value = false } } catch (error) { // positionLoading.value = false; } } // #endregion //#region 上传附件 // 上传之前的处理 const uploadBefore = (file) => { const isImage = file.type.startsWith('image/'); const isSizeOk = file.size / 1024 / 1024 < 20; // 限制文件大小最大为 5MB if (!isImage) { ElMessage.error('只能上传图片文件!') return false; } if (!isSizeOk) { ElMessage.error('图片大小不能超过 5MB!') return false; } gymbLoading.value = true; // 开启 loading return true; }; // 预览图片 const handlePictureCardPreview = (file) => { dialogImageUrl.value = file.full_path; // 设置预览的图片地址 dialogVisibleImg.value = true; // 显示对话框 }; //上传成功的回调 const uploadSuccess = (response) => { fileList.value.push({ original_name: response.data.original_name, path: response.data.fullurl, // 带域名的完整路径 full_path: response.data.url // 相对路径 }) ElMessage({ type: "success", message: "操作成功", }); gymbLoading.value = false }; // 上传失败的回调 const uploadError = (error, file) => { console.error("上传失败:", error, file); }; // 处理文件删除 // const handleRemove = (file, files) => { // fileList.value = files; // 更新文件列表 // ElMessage.info(`已删除文件: ${file.name}`); // }; //移除上传图纸 function delImg (index){ fileList.value.splice(index, 1); } // #endregion //#region 删除工艺模板 async function delProcedure(row) { ElMessageBox.confirm("确定要删除吗?这可能导致该数据无法找回", "警告", { confirmButtonText: "确定", cancelButtonText: "取消", type: "warning", }) .then(async () => { try { loading.value = true; await request.post("/system/workmanship.workmanship_template/delete", { id: row.id, }); ElMessage({ type: "success", message: "操作成功", }); getProcedure(); } catch (error) { loading.value = false; } }) .catch(() => { }); } // #endregion </script> <style scoped> .item { cursor: pointer; float: left; background: #fff; width: 100px; height: 100px; line-height: 100px; text-align: center; margin: 0 15px 15px 0; border: 1px solid #e6e6e6; display: block; transition: all 0.3s; /* 添加过渡效果 */ } /* 选中时的样式 */ .selected { border-color: #409eff; /* 选中时的边框颜色 */ background-color: #e6f7ff; /* 选中时的背景颜色 */ } .cwxzlist { display: flex; } .changeCheckbox { display: block; margin-bottom: 20px; } .addwarehouse { margin-right: 10px; } :deep(.dialogGxgy .el-dialog__headerbtn) { height: 57px !important; } .dialogtitle { display: flex; justify-content: space-between; align-items: center; } .fullscreen-icon { cursor: pointer; font-size: 18px; color: #606266; transition: color 0.2s; } .fullscreen-icon:hover { color: #409eff; } .dialog-icons { display: flex; align-items: center; gap: 10px; } .gybs { display: flex; align-items: center; width: 100%; } .gyheader { margin-right: 20px; display: flex; align-items: center; width: 24%; } .card_gy { margin: 10px; min-height: 500px; position: relative; } ::v-deep.card_gy .el-card__body { width: 100%; } .tzpx { height: 400px; overflow-y: scroll; scrollbar-width: none; /* Firefox 隐藏滚动条 */ } .slx { display: flex; } .sortable-item { width: 100%; padding: 15px 20px; background: #ffffff; border: 1px solid #e4e7ed; border-radius: 8px; cursor: move; margin-bottom: 12px; transition: all 0.3s ease; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); display: flex; align-items: center; justify-content: center; font-size: 14px; color: #333; } .sortable-item:hover { background-color: #f5f5f5; border-color: #dcdfe6; color: #409eff; } .sortable-item:active { transform: scale(0.98); } .sortable-item .drag-handle { cursor: move; font-size: 18px; color: #999; margin-right: 12px; } .sortable-item .drag-handle:hover { color: #409eff; } .selected { background: #409eff; /* 选中状态变蓝色 */ color: white; font-weight: bold; } .item_left { margin-right: 8px; } .item_content { flex: 1; /* 占据中间可用空间 */ overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } .item_right { margin-left: 8px; margin-left: auto; /* 自动将右侧图标推到最右 */ cursor: pointer; } .btgp { display: flex; justify-content: center; position: absolute; bottom: 3%; left: 50%; transform: translateX(-50%); } .gyform { padding: 10px; } .dowt { display: flex; justify-content: space-between; align-items: center; border-top: 1px solid #e4e7ed; border-bottom: 1px solid #e4e7ed; padding: 12px 15px; } .route { margin-top: 30px; } .image-preview-list { width: 100%; text-align: center; padding-top: 30px; /* overflow-x: scroll; */ /* overflow: scroll; */ } .image-preview-list::-webkit-scrollbar { display: none; /* Safari 和 Chrome 隐藏滚动条 */ } /* ::v-deep .el-upload-list { display: flex; } */ .lf_aside { border-left: 1px solid #e4e7ed; display: flex; justify-content: center; } .upbox { width: 100%; display: flex; /* flex-wrap: wrap; */ justify-content: center; flex-direction: column-reverse; align-items: center; } .wdg { width: 90%; border: 1px solid #e4e7ed; height: 50px; border-radius: 5px; display: flex; align-items: center; padding: 0 10px; justify-content: space-between; margin-top: 10px; } .wg_lf { display: flex; align-items: center; cursor: pointer; } .weds { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; width: 80px; display: flex; margin-left: 5px; } </style> 这是我的代码 我的代码现在 handleSelectionChange 勾选列表有问题 如果我勾选多个 然后重新获取列表就会加载不上已勾选的状态
08-20
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值