Hide/display columns or rows in an Excel document

本文介绍如何使用Excel对象模型通过VBA代码自动化地隐藏或显示Excel工作表中的行和列。此方法适用于外部Excel文件,并提供了示例代码来演示如何实现这一功能。

This solution applies to external Excel files, not to the QuickTest Professional data table.


You can use the Excel object model to automate hiding or displaying rows and columns in an Excel workshee


Example:
' Create the Excel object
Set ExcelObj = CreateObject("Excel.Application")
ExcelObj.Visible = true

' Open up the Excel file which has the rows or columns to be hidden.
ExcelObj.Workbooks.Open "C:\temp\Book1.xls"

' Get the first sheet
Set NewSheet = ExcelObj.Sheets.Item(1)

' Hide the first row and column

NewSheet.Cells(1,1).EntireRow.Hidden = True
NewSheet.Cells(1,1).EntireColumn.Hidden = True

' Save the Excel file
ExcelObj.ActiveWorkbook.Save

' Close the application and clear the object reference
ExcelObj.Quit
Set ExcelObj = Nothing

To hide columns or rows, set the Hidden property to True.
To display columns or rows, set the Hidden property to False.


<template> <div> <div class="search-title">查询条件</div> <div class="search-box"> <a-form-model :model="searchForm" layout="inline" ref="searchForm" class="searchForm"> <a-form-model-item label="题库名称" prop="title"> <a-input v-model="searchForm.title" placeholder="请输入题库名称" /> </a-form-model-item> <a-form-model-item class="searchButton"> <a-button type="primary" v-if="QX.read" @click="getSearch">查询</a-button> <a-button type="default" v-if="QX.read" @click="restSearch('searchForm')">重置</a-button> </a-form-model-item> </a-form-model> </div> <div class="table-operation"> <a-button type="primary" @click="addBank" icon="plus" v-if="QX.add">新增</a-button> </div> <a-table :columns="columns" :data-source="dataList" :pagination="false" :loading="loading" rowKey="questionBankId" :scroll="{ y: this.$getViewportSize().height - 300 }" > <span slot="action" slot-scope="text, record"> <a @click="editAuth(record)" v-if="QX.edit"><a-icon class="iconBtn" type="edit" />编辑</a> <a-divider v-if="QX.edit && QX.delete" type="vertical" /> <a-popconfirm title="确认是否删除?" ok-text="是" cancel-text="否" @confirm="removeBank(record)" > <a v-if="QX.delete"><a-icon class="iconBtn" type="delete" />删除</a> </a-popconfirm> </span> </a-table> <template slot="action" slot-scope="text, record, index"> <a @click="removeQuestion(index)">删除</a> </template> <a-pagination show-size-changer :total="totalPage" :current="pageIndex" :pageSize="pageSize" @showSizeChange="onShowSizeChange" @change="onChangePage" style="float: right; margin-top: 15px" /> <a-drawer :closable="true" :title="title" width="auto" :visible="visible" @close="visible = !visible" > <a-spin class="submitLoading" :spinning="submitLoading"> <a-form-model :model="form" :label-col="labelCol" :wrapper-col="wrapperCol" :rules="rules" ref="form" class="lay-drawer-form"> <a-row> <a-col :span="12"> <a-form-model-item label="题库名称" prop="title"> <a-input v-model="form.title" :maxLength="25" placeholder="请输入题库名称" style="width: 380px" /> </a-form-model-item> </a-col> <a-col :span="12"> <a-form-model-item label="关联岗位" prop="positionId"> <a-select v-model="form.positionId" style="width: 380px" placeholder="请选择岗位"> <a-select-option v-for="(label, value) in positionDict" :key="value" :value="value"> {{ label }} </a-select-option> </a-select> </a-form-model-item> </a-col> <a-col :span="12"> <a-form-model-item label="参与PK" prop="participateInPk"> <a-switch v-model="form.participateInPk" :checkedValue="1" :unCheckedValue="0" /> </a-form-model-item> </a-col> </a-row> </a-form-model> <!-- 新增题目区域 --> <div class="question-batch"> <div style="margin: 20px 0; display: flex; justify-content: space-between; align-items: center;"> <a-button type="primary" @click="downloadTemplate">下载模板</a-button> <div class="import-add-buttons"> <a-upload name="file" :showUploadList="false" :beforeUpload="beforeUpload" accept=".xlsx,.xls" :disabled="!importEnabled" > <a-button :disabled="!importEnabled"><a-icon type="upload" /> 导入题目</a-button> </a-upload> <a-button type="dashed" @click="addTopicVisible = true" :disabled="!addEnabled" > <a-icon type="plus" /> 添加题目 </a-button> </div> </div> <!-- 题目列表容器添加样式 --> <div class="topic-list-container"> <!-- 题目列表 --> <div class="topic-grid"> <!-- 题目编号和内容 --> <div v-for="(topic, index) in topicList" :key="topic.topicId" class="topic-item"> <!-- 题目编号和内容 --> <div class="topic-content"> <strong>{{ index + 1 }}. {{ topic.content }}</strong> </div> <!-- 选项列表 --> <div class="options" v-if="topic.topicType === 1"> <label v-for="option in getOptions(topic)" :key="option.key"> <input type="radio" :name="'topic' + topic.topicId" :value="option.key" :checked="topic.correctAnswer === option.key" /> <!-- @change="checkAnswer(topic, option.key)"--> {{ option.key }}. {{ option.value }} </label> </div> <!-- 判断题 --> <div v-if="topic.topicType === 2" class="options"> <label> <input type="radio" :name="'topic' + topic.topicId" value="正确" :checked="topic.correctAnswer === '正确'" /> <!-- @change="checkAnswer(topic, '正确')"--> 正确 </label> <label> <input type="radio" :name="'topic' + topic.topicId" value="错误" :checked="topic.correctAnswer === '错误'" /> <!-- @change="checkAnswer(topic, '错误')"--> 错误 </label> </div> <!-- 删除按钮 --> <div class="topic-delete"> <a @click="removeQuestion(topic.topicId)"> <a-icon type="delete" /> 删除 </a> </div> </div> </div> <!-- 分页组件移到题目列表区域外面 --> <div class="pagination-wrapper"> <a-pagination v-model="topicPageNum" :pageSize="topicPageSize" :total="totalTopicCount" @change="handleTopicPageChange" style="text-align: right;" /> </div> </div> </div> <div :style="{ position: 'absolute', right: 0, bottom: 0, width: '100%', borderTop: '1px solid #e9e9e9', padding: '8px 16px', background: '#fff', textAlign: 'right', zIndex: 1, }"> <a-button type="default" @click="visible = !visible" > 取消 </a-button> <a-button type="primary" @click="submitForm" > 确认 </a-button> </div> </a-spin> </a-drawer> <!-- 新增题目抽屉 --> <a-drawer title="新增题目" :visible="addTopicVisible" @close="addTopicVisible = false" width="500" > <a-form-model :model="addTopicForm" layout="vertical" :rules="rulesForAddTopic" ref="addTopicFormRef"> <!-- 题目类型 --> <a-form-model-item label="题目类型" prop="topicType"> <a-select v-model="addTopicForm.topicType" style="width: 100%"> <a-select-option :value="1">选择题</a-select-option> <a-select-option :value="2">判断题</a-select-option> </a-select> </a-form-model-item> <!-- 题目内容 --> <a-form-model-item label="题目内容" prop="content"> <a-input v-model="addTopicForm.content" placeholder="请输入题目内容" /> </a-form-model-item> <!-- 选择题选项 --> <div v-if="addTopicForm.topicType === 1"> <a-form-model-item label="选项A" prop="optionA"> <a-input v-model="addTopicForm.optionA" placeholder="请输入选项A内容" /> </a-form-model-item> <a-form-model-item label="选项B" prop="optionB"> <a-input v-model="addTopicForm.optionB" placeholder="请输入选项B内容" /> </a-form-model-item> <a-form-model-item label="选项C" prop="optionC"> <a-input v-model="addTopicForm.optionC" placeholder="请输入选项C内容" /> </a-form-model-item> <a-form-model-item label="选项D" prop="optionD"> <a-input v-model="addTopicForm.optionD" placeholder="请输入选项D内容" /> </a-form-model-item> <a-form-model-item label="正确答案" prop="correctAnswer"> <a-select v-model="addTopicForm.correctAnswer" style="width: 100%"> <a-select-option value="A">A</a-select-option> <a-select-option value="B">B</a-select-option> <a-select-option value="C">C</a-select-option> <a-select-option value="D">D</a-select-option> </a-select> </a-form-model-item> </div> <!-- 判断题选项 --> <div v-if="addTopicForm.topicType === 2"> <a-form-model-item label="正确答案" prop="correctAnswer"> <a-select v-model="addTopicForm.correctAnswer" style="width: 100%"> <a-select-option value="正确">正确</a-select-option> <a-select-option value="错误">错误</a-select-option> </a-select> </a-form-model-item> </div> <div class="drawer-footer"> <a-button @click="addTopicVisible = false">取消</a-button> <a-button type="primary" @click="saveNewTopic">保存</a-button> </div> </a-form-model> </a-drawer> </div> </template> <script> import { req, fileDownload } from '../../../api/axiosFun'; import preventBack from 'vue-prevent-browser-back'; export default { name: 'Bank', mixins: [preventBack], data() { return { QX: {}, topicQX: {}, topicList: [], totalTopicCount: 0, // 题目总数 topicPageNum: 1, // 当前页码 topicPageSize: 10, // 每页数量 addTopicVisible: false, addTopicForm: { content: '', // 题目内容 topicType: 1, // 题目类型:1=选择题,2=判断题 optionA: '', optionB: '', optionC: '', optionD: '', correctAnswer: '', }, disabled: false, checkedKeys: [], selectAuth: [], treeData: [], positionDict: {}, title: '', labelCol: { span: 4 }, wrapperCol: { span: 20 }, tableHeight: 0, expanded: false, // 筛选条件是否展开 form: { questionBankId: 0, bankCode: '', title: '', positionId: '', participateInPk: true, }, isEdit: false, // 是否是编辑状态 isAdd: false, // 是否是新增状态 importEnabled: false, // 导入题目按钮是否可用 - 默认为不可用 addEnabled: false, // 添加题目按钮是否可用 - 默认为不可用 rules: { positionId: [ { required: true, message: '请选择岗位', trigger: 'blur' }, ], title: [ { required: true, message: '请输入题库名称', trigger: 'blur' }, ], }, rulesForAddTopic: { content: [ { required: true, message: '请输入题目内容', trigger: ['blur', 'change'] }, ], topicType: [ { required: true, message: '请选择题目类型', trigger: 'change' }, ], optionA: [ { required: (rule, value) => this.addTopicForm.topicType === 1, message: '选择题必须输入选项A', trigger: ['blur', 'change'], }, ], optionB: [ { required: (rule, value) => this.addTopicForm.topicType === 1, message: '选择题必须输入选项B', trigger: ['blur', 'change'], }, ], optionC: [ { required: (rule, value) => this.addTopicForm.topicType === 1, message: '选择题必须输入选项C', trigger: ['blur', 'change'], }, ], optionD: [ { required: (rule, value) => this.addTopicForm.topicType === 1, message: '选择题必须输入选项D', trigger: ['blur', 'change'], }, ], correctAnswer: [ { required: true, message: '请选择正确答案', trigger: 'change' }, ], }, searchForm: { title: '', }, visible: false, dataList: [], columns, loading: false, submitLoading: false, pageIndex: 1, pageSize: 10, totalPage: 0, ops: { vuescroll: {}, scrollPanel: {}, rail: { keepShow: true, }, bar: { hoverStyle: true, onlyShowBarOnScroll: false, // 是否只有滚动的时候才显示滚动条 background: '#F5F5F5', // 滚动条颜色 opacity: 1, // 滚动条透明度 'overflow-x': 'hidden', }, }, }; }, watch: { 'addTopicForm.topicType': function (newVal) { // 当题目类型变化时,触发相关字段的验证 this.$nextTick(() => { if (this.$refs.addTopicFormRef) { // 验证选项字段 if (newVal === 1) { this.$refs.addTopicFormRef.validateFields(['optionA', 'optionB', 'optionC', 'optionD']); } else { // 清除非必填字段的验证状态 this.$refs.addTopicFormRef.clearValidate(['optionA', 'optionB', 'optionC', 'optionD']); } } }); }, visible(newVal, oldVal) { if (!newVal) { this.restForm('form'); this.form.questionBankId = 0; this.checkedKeys = []; this.selectAuth = []; this.treeData = []; // 重置状态标志 this.isEdit = false; this.isAdd = false; } else { // 当抽屉打开时强制更新按钮状态 this.$nextTick(() => { this.$forceUpdate(); }); } }, }, mounted() { this.actionTitle = '操作'; this.getDict('position').then(res => { const dictMap = {}; res.data.forEach(item => { dictMap[item.dicValue] = item.dicDisplayName; }); this.positionDict = dictMap; }); this.getBankList(); }, methods: { // 获取字典数据方法 getDict(type) { return req('get', `/dict/getDictItemByNo`, { dicNo: type }) .then((res) => { if (res.result === 'success') { return res; } throw new Error(res.message || '获取字典数据失败'); }) .catch((error) => { console.error(`获取${type}字典失败:`, error); throw error; }); }, /* 新增题库 */ addBank() { this.visible = true; this.disabled = false; this.title = '新增题库'; // 获取功能权限树 req('get', '/bank/getTree', {}).then((res) => { this.treeData = res.data; }); // 初始化题目列表为空 this.topicList = []; this.totalTopicCount = 0; this.topicPageNum = 1; this.isEdit = false; // 不是编辑状态 this.isAdd = true; // 设置为新增状态 // 在新增状态下禁用导入和添加功能 this.importEnabled = false; this.addEnabled = false; // 强制更新按钮状态 this.$nextTick(() => { this.$forceUpdate(); }); }, // 查询 getSearch() { this.pageIndex = 1; this.getBankList(); }, /* 重置查询 */ restSearch(form) { this.restForm(form); this.pageIndex = 1; this.getBankList(); }, /* 删除题库 */ removeBank(record) { this.loading = true; req('post', '/bank/removeBQuestionBank', { questionBankId: record.questionBankId, }).then((res) => { this.loading = false; if (res.result === 'success') { this.$message.success(res.message); this.getBankList(); } }); }, downloadTemplate() { fileDownload('get', '/topic/downloadTemplate', null).then((res) => { const blob = new Blob([res], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', }); const link = document.createElement('a'); link.href = window.URL.createObjectURL(blob); link.download = '题库模板.xlsx'; link.click(); window.URL.revokeObjectURL(link.href); }).catch(() => { this.$message.error('下载失败'); }); }, beforeUpload(file) { // 显示加载状态 this.loading = true; const formData = new FormData(); formData.append('file', file); formData.append('questionBankId', this.form.questionBankId); // 添加 questionBankId 字段 req('post', '/topic/import', formData, { headers: { 'Content-Type': 'multipart/form-data', }, }) .then((res) => { // 隐藏加载状态 this.loading = false; if (res.result === 'success') { // 确保在DOM更新后再刷新题目列表 this.$nextTick(() => { // 刷新题目列表 this.getTopicList(this.form.questionBankId, this.topicPageNum, this.topicPageSize) .then(() => { this.$message.success('导入成功'); }) .catch(() => { this.$message.error('刷新题目列表失败'); }); }); } else { this.$message.error(res.message || '导入失败'); } }) .catch((error) => { // 隐藏加载状态 this.loading = false; this.$message.error('导入失败,请重试'); console.error('导入题目出错:', error); }); return false; // 阻止默认上传行为 }, // 获取题库下的题目列表(返回Promise以便链式调用) getTopicList(questionBankId, pageNum = 1, pageSize = 10) { return new Promise((resolve, reject) => { req('post', '/topic/list', { questionBankId, page: pageNum, rows: pageSize, }).then((res) => { if (res.result === 'success') { this.topicQX = res.QX; if (!this.topicQX.edit && !this.topicQX.delete) { this.hideAction(); } else { this.topicList = res.data.map((item, index) => ({ index: (pageNum - 1) * pageSize + index + 1, content: item.content, optionA: item.optionA, optionB: item.optionB, optionC: item.optionC, optionD: item.optionD, topicId: item.topicId, correctAnswer: item.correctAnswer, topicType: item.topicType, })); } // 更新总条数用于分页组件 this.totalTopicCount = res.page.totalResult; resolve(res); } else { this.$message.error(res.message || '获取题目列表失败'); reject(res); } }).catch((error) => { this.$message.error('获取题目列表失败'); reject(error); }); }); }, handleTopicPageChange(page) { this.topicPageNum = page; this.getTopicList(this.form.questionBankId, page, this.topicPageSize); }, // 获取选择题的选项 getOptions(topic) { if (topic.topicType === 1) { // 选择题:返回 A/B/C/D 选项 const options = []; if (topic.optionA) options.push({ key: 'A', value: topic.optionA }); if (topic.optionB) options.push({ key: 'B', value: topic.optionB }); if (topic.optionC) options.push({ key: 'C', value: topic.optionC }); if (topic.optionD) options.push({ key: 'D', value: topic.optionD }); return options; } else if (topic.topicType === 2) { // 判断题:直接返回 正确/错误 return [ { key: '正确', value: '正确' }, { key: '错误', value: '错误' }, ]; } return []; }, checkAnswer(topic, selectedAnswer) { // 仅记录用户选择的答案,不进行是否正确的判断 this.$set(topic, 'userAnswer', selectedAnswer); }, resetAddTopicForm() { this.addTopicForm = { content: '', topicType: 1, optionA: '', optionB: '', optionC: '', optionD: '', correctAnswer: '', }; }, saveNewTopic() { this.$refs.addTopicFormRef.validate((valid, fields) => { if (!valid) { console.log('表单验证失败:', fields); // 找出第一个错误字段并聚焦 const firstErrorField = Object.keys(fields).find(key => fields[key]); if (firstErrorField && this.$refs.addTopicFormRef) { const formItem = this.$refs.addTopicFormRef.$children.find( child => child.prop === firstErrorField, ); if (formItem && formItem.$el) { const input = formItem.$el.querySelector('input, select, textarea'); if (input) input.focus(); } } return; } // 验证通过,处理保存逻辑 const newTopic = { ...this.addTopicForm }; // 发送请求保存题目 req('post', '/topic/add', { questionBankId: this.form.questionBankId, optionA: newTopic.optionA, optionB: newTopic.optionB, optionC: newTopic.optionC, optionD: newTopic.optionD, correctAnswer: newTopic.correctAnswer, content: newTopic.content, topicType: newTopic.topicType, }).then((res) => { if (res.result === 'success') { this.$message.success('题目添加成功'); this.addTopicVisible = false; this.getTopicList(this.form.questionBankId); // 刷新题目列表 } else { this.$message.error(res.message || '保存失败'); } this.resetAddTopicForm(); }).catch((err) => { this.$message.error('网络异常,请重试'); }); }); }, /* 删除题库下的题目 */ removeQuestion(topicId) { this.$confirm({ title: '确认删除该题目?', content: '删除后将无法恢复', okText: '是', cancelText: '否', onOk: () => { req('post', '/topic/removeBTopic', { topicId, questionBankId: this.form.questionBankId, }).then((res) => { if (res.result === 'success') { this.$message.success(res.message); // 刷新题目列表 this.getTopicList(this.form.questionBankId, this.topicPageNum, this.topicPageSize); } else { this.$message.error(res.message || '删除失败'); } }); }, }); }, editAuth(record) { this.loading = true; req('post', '/bank/getBQuestionBank', { questionBankId: record.questionBankId, }).then((res) => { this.loading = false; if (res.result === 'success') { this.visible = true; this.disabled = true; this.title = '修改题库'; this.isEdit = true; // 设置为编辑状态 this.isAdd = false; // 不是新增状态 // 在编辑状态下启用导入和添加功能 this.importEnabled = true; this.addEnabled = true; // 强制更新按钮状态 this.$nextTick(() => { this.$forceUpdate(); }); const bank = res.data; this.$nextTick(() => { this.form.questionBankId = bank.questionBankId; this.form.title = bank.title; this.form.participateInPk = Boolean(bank.participateInPk); this.form.positionId = bank.positionId; this.treeData = bank.treeData; this.checkedKeys = bank.auths; // 获取题目列表 this.topicPageNum = 1; this.getTopicList(bank.questionBankId, this.topicPageNum, this.topicPageSize); }); } }); }, /* 保存or修改题库信息 */ submitForm() { this.$refs.form.validate((valid) => { if (valid) { this.form.participateInPk = this.form.participateInPk ? 1 : 0; const url = this.form.questionBankId ? 'edit' : 'add'; const selectAuth = this.selectAuth; this.form.selectAuth = JSON.stringify(selectAuth); this.submitLoading = true; req('post', `/bank/${url}`, this.form).then((res) => { if (res.result === 'success') { this.visible = false; this.getBankList(); // 如果是新增题库且成功,获取题目列表 if (!this.form.questionBankId && res.data && res.data.questionBankId) { this.form.questionBankId = res.data.questionBankId; this.topicPageNum = 1; this.getTopicList(res.data.questionBankId, this.topicPageNum, this.topicPageSize); // 新增成功后启用导入和添加功能 this.importEnabled = true; this.addEnabled = true; } this.$message.success(res.message); // 重置新增/编辑状态 this.isEdit = false; this.isAdd = false; } this.submitLoading = false; }); } }); }, /* 重置表单 */ restForm(form) { this.$refs[form].resetFields(); }, /* 改变页数事件 */ onChangePage(page, pageSize) { this.pageIndex = page; this.getBankList(); }, /* 改变每页显示条数 */ onShowSizeChange(current, pageSize) { this.pageIndex = 1; this.pageSize = pageSize; this.getBankList(); }, /* 题库信息列表 */ getBankList() { this.loading = true; this.searchForm.page = this.pageIndex; this.searchForm.rows = this.pageSize; req('post', '/bank/list', this.searchForm) .then((res) => { if (res.result === 'success') { this.dataList = res.data; this.QX = res.QX; // 无权限隐藏操作列 if (!this.QX.edit && !this.QX.delete) { this.hideAction(); } else if (columns[columns.length - 1].title != '操作') { columns.push(actionShow); } this.totalPage = res.page.totalResult; } this.loading = false; }).catch((error) => { this.loading = false; }); }, /* 无所有行操作权限时,隐藏操作栏 */ hideAction() { if (columns[columns.length - 1].title == '操作') { columns.splice(columns.length - 1, 1); } }, /* 校验代号类型 */ validCode(value) { if (value.length > 20) { value = value.slice(0, 20); } for (let i = value.length - 1; i >= 0; i--) { const unicode = value.charCodeAt(i); if (unicode > 65280 && unicode < 65375) { value = value.substr(0, i); } } this.value = value; }, }, }; const actionShow = { title: '操作', width: '200px', hide: true, dataIndex: 'action', key: 'action', align: 'center', scopedSlots: { customRender: 'action' }, }; const columns = [ { title: '序号', width: '50px', align: 'center', customRender: (text, row, index) => index + 1, }, { title: '题库名称', align: 'center', dataIndex: 'title', key: 'title', width: '120px', }, { title: '涉及岗位', align: 'center', dataIndex: 'positionName', key: 'positionName', width: '110px', }, { title: '题目数量', dataIndex: 'topicCount', key: 'topicCount', align: 'center', width: '180px', }, { title: '操作', width: '150px', hide: true, dataIndex: 'action', key: 'action', align: 'center', scopedSlots: { customRender: 'action' }, }, ]; </script> <style scoped> .topic-list-container { max-height: calc(100vh - 400px); /* 根据实际布局调整最大高度 */ overflow-y: auto; } .pagination-wrapper { position: sticky; bottom: 0; left: 0; right: 0; background: #fff; padding: 10px; z-index: 1; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); margin-top: 15px; } .topic-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px; } .topic-item { border: 1px solid #e8e8e8; padding: 15px; border-radius: 4px; position: relative; /* 为绝对定位的子元素提供定位上下文 */ } .topic-content { font-size: 16px; margin-bottom: 10px; } .options label { display: block; margin: 5px 0; font-size: 14px; } .options input[type="radio"] { margin-right: 5px; } /* 按钮禁用状态样式优化 */ .ant-upload.ant-upload-disabled .ant-upload-list, .ant-btn[disabled] { cursor: not-allowed; opacity: 0.5; } .topic-delete { position: absolute; top: 10px; right: 10px; } </style> <style scoped> @import "../../../assets/css/maincss.css"; </style> 在编辑页面中选择某个题目的选项后,再次打开该题时,默认显示的是上次选择的选项而不是正确答案。修改此问题
07-02
<script setup lang="ts"> import { ref, reactive, watch, onMounted, computed, toRefs, onUnmounted } from 'vue' import { buildGridConfig,validateStringLength,isAdmin,formAllStringValueTrim } from 'util/common' import * as _data from './data' import * as dhtmlxe from 'util/dhtmlxe' import { MESSAGE_INFO,REG } from 'util/constant' import * as _url from '@/api/V93000/TesterGroup' import UploadFile from '@/components/Common/UploadFile.vue' import { ElMessage, ElMessageBox, type FormInstance } from 'element-plus' import _lodash from 'lodash' import {getDeviceListApi} from '@/api/V93000/DeviceManagement' import { load } from '@/components/Loading/index' import store from '@/store/index.js' import { SessionStorage, iso8601ToDateString, getPageDurationData } from '@/util/common' const props: any = defineProps({ testerSidebar:String, selectLink:{}, isManualSelect:Boolean }) //公共数据维护 const gtype = _data.gtype const rsource = {testerconfig:{hide:false,v:'tester',text:"Refer Tester Config"},device:{hide:false,v:'device',text:"Refer Device"},model:{hide:false,v:'modelfile',text:"Model File"}} const smt_platform = {smt7:{v:0,text:"SMT7(<7.10)"},smt710:{v:2,text:"SMT7(>=7.10)"},smt8:{v:1,text:"SMT8"}} const modeType = {relax:{v:1,text:"Strict Mode"},strict:{v:2,text:"Relax Mode"}} // 弹窗显示维护 const showRuleEditFlag = ref(false) const showGroupEditFlag = ref(false) const showRuleDetailFlag = ref(false) const showTester = ref(false) const refData = reactive({clearFileFlag:false,groupGridNumber:0 }) const formLabelWidth = '140px' const ruleColSpan=12 const selectTesterId = ref() const selectGroupId = ref() const getDetail = ref() // 为了避免多次渲染grid,全局grid维护 let groupGrid:any let groupTesterGrid:any let testerListGrid:any let gtGrid_g_id:Number = 0 //选中的grid id // rule 编辑弹窗的三个grid,编辑时选中rule_index 维护 let stesterGrid:any let sdeviceGrid:any let show_ruleGrid:any let showRuleDetailGrid:any let testerDetailGrid:any let testerDetailLayout:any const ruleFormRef = ref() const groupFormRef = ref() // 全局group 和 tester数据维护 let group_list:any, tester_list:any, edit_group_t:any // 弹窗的title let groupTitle:string = "Add Group" let ruleTitle:string = "Add Rule" let is_admin = isAdmin() let clickType = ref(false) const allTesterList = ref<any[]>([]) // 完整数据 let currentViewTids: number[] = [] const formGroup = reactive({ gname: '', id:0, gtype: gtype.auto, g_tids:[] }) const formRule = reactive({ id:0, gid:"0", status:1, name:"", rule_type:gtype.auto, source:rsource.testerconfig.v, is_strict:modeType.strict.v, smt:smt_platform.smt7.v, reference:[], rule:[], pogofile:"", modulefile:"" }) let selectedRuleList: any = null const commonRulesByGroup = reactive({ gname:[ { required: true, message: 'Please Input', trigger: 'blur' }, {pattern: REG.NORMAL_STRINT8, message: MESSAGE_INFO.VALIDATE_NORMAL_STRING7,trigger: ['blur']}, { max: 60,validator: validateStringLength, trigger: 'blur' } ], rname:[ { required: true, message: 'Please Input', trigger: 'blur' }, { max: 60,validator: validateStringLength, trigger: 'blur' } ] }) onMounted(() => { initStart() init() reflushGroupList() window.addEventListener('change', handleChange) }) onUnmounted(() => { props.selectLink = undefined document.removeEventListener('change', handleChange) }) watch( () => props.isManualSelect, (newValue, oldValue) => { props.isManualSelect = newValue } ) //跳转至Tab页面统计之前页面停留时长 const initStart = ()=> { //调用接口处理 store.dispatch('updatePageDurationData', getPageDurationData()) //记录当前页面 SessionStorage.set('lastFeature', 'tester-group-setup') SessionStorage.set('lastFeatureTime', iso8601ToDateString(new Date().toISOString())) } const init = async()=>{ let layout = new dhx.Layout("tester_group_layout", _data.TESTER_GROUP_LIST_LAYOUT) let currentColumns = _data.GROUP_LIST if ( !is_admin ) currentColumns = _data.GROUP_LIST.filter((item:any)=>{ return item.id != 'action'}) groupGrid = new dhx.TreeGrid(null, buildGridConfig(currentColumns,{autoWidth:true})) groupGrid.selection.setCell(groupGrid.data.getItem(groupGrid.data.getId(1)), groupGrid.config.columns[0]); layout.getCell('group').attach(groupGrid) if (props.selectLink && props.isManualSelect && clickType.value == false) { setTimeout(() => { selectGroupId.value = props.selectLink.group_id groupGrid.selection.setCell(props.selectLink.group_id) selectedRuleList = group_list[props.selectLink.group_id]?.rule_list if (selectedRuleList) { const ruleIds = Object.values(selectedRuleList).map((r: any) => r.id) selectTesterId.value = ruleIds } }, 500) } groupGrid.events.on("cellClick",function(row:any,column:any,e:any){ selectGroupId.value = row.gid if(row.rule_id == undefined){ const ruleIds = Object.values(group_list[row.g_id].rule_list) .map((r: any) => r.id) selectTesterId.value = ruleIds }else{ selectTesterId.value = [row.rule_id] } if (column.id == "status" && row.status != undefined) { let status = row.status == 1 ? 0 : 1 groupGrid.data.update(row.id, { status:status }) _url.changeRuleStatus({id:row.rule_id,status:status}).then(function(res){ if (res) { group_list[row.g_id]['rule_list'][row.r_index]['status'] = status group_list[row.g_id]['rule_list'][row.r_index]['tids'] = res.data reflushGroupTesterList() } }) } else if (column.id == "action") { btnOnClick(row,column,e) } const data = row?.rule_list const allTidsEmpty = Object.values(data).every(rule => Array.isArray(rule.tids) && rule.tids.length === 0 ) if (allTidsEmpty) { groupTesterGrid.data.removeAll() } let gid = row.g_id if (gtGrid_g_id == gid) return gtGrid_g_id = gid reflushGroupTesterList() }) groupTesterGrid = new dhx.Grid(null, buildGridConfig(_data.GROUP_TESTER_LIST,{autoWidth:true,autoHeight:true})) groupTesterGrid.columns = initGridColumnFunc(_data.GROUP_TESTER_LIST) layout.getCell("group_rule_tester_grid").attach(groupTesterGrid) dhtmlxe.initPagination(groupTesterGrid,layout,"group_rule_tester") initGridEvent() } const initGridColumnFunc = (columns: any) => { for (let key in columns) { if ("reference" == columns[key].id) { columns[key].htmlEnable = true columns[key].template = function (row,value) { const checked = value.count_current ? 'checked' : ''; return ` <div style="display:flex;align-items:center;margin-left:20px;"> <!-- 图标 --> <i class="dxi dxi-link-variant" style="cursor:pointer;font-size:16px;margin-right:8px;flex-shrink:0;" title="Show Detail"></i> <!-- 勾选框 --> <label class="dhx_checkbox dhx_cell-editor__checkbox "> <input type="checkbox" id="header_check" ${checked} class="dhx_checkbox__input dhx_checkbox--check-all"> <span class="dhx_checkbox__visual-input "></span> </label> </div>` } } } return columns } const initGridEvent = ()=>{ groupTesterGrid.events.on("cellClick", function (row: any, column: any, e: any) { if(column.id === "reference"){ const rules = Object.values(group_list[gtGrid_g_id]?.rule_list || {}) const referenceTesterIds: number[] = [] rules.forEach((r: any) => { if (Array.isArray(r.reference_tester)) { referenceTesterIds.push(...r.reference_tester.map(Number)) } }) const isCurrentlyReferenced = referenceTesterIds.includes(Number(row.id)) const currentRow = groupTesterGrid.data.getItem(row.id) if (currentRow && currentRow.count_current !== (isCurrentlyReferenced ? 1 : 0)) { groupTesterGrid.data.update(row.id, { count_current: isCurrentlyReferenced ? 1 : 0 }) } if(e.target.tagName.toLowerCase() == 'i'){ let params = { tid: row.id, rsource: "tester" } _url.getReferenceDetailApi(params).then((res)=>{ if(res && res.code == 200){ showTester.value = true getDetail.value = res.data.reference } }) } else if (e.target.tagName.toLowerCase() === 'input') { const checkbox = e.target as HTMLInputElement const testerId = row.id if (props.selectLink && props.isManualSelect && clickType.value == false) { setTimeout(() => { groupTesterGrid.data.forEach((item: any) => { groupTesterGrid.data.update(item.id, { count_current: 0 }) }) groupTesterGrid.data.update(testerId, { count_current: checkbox.checked ? 1 : 0 }) const rules = Object.values(group_list[props.selectLink.group_id]?.rule_list) rules.forEach((r: any) => { if (r.status === 1) r.reference_tester = [] }) }, 500) }else{ const rules = Object.values(group_list[gtGrid_g_id]?.rule_list) rules.forEach((r: any) => { if (r.status === 1) r.reference_tester = [] }) } const ruleIds = Array.isArray(selectTesterId.value) ? selectTesterId.value : [selectTesterId.value]; const rawValue = String(selectGroupId.value || ''); const group_id = rawValue.startsWith('rule_') ? rawValue.replace(/^rule_/, '') : rawValue; groupTesterGrid.data.forEach((item: any) => { groupTesterGrid.data.update(item.id, { count_current: 0 }) }) // 再点亮当前行 groupTesterGrid.data.update(testerId, { count_current: checkbox.checked ? 1 : 0 }) const data = { tester_id: testerId, is_reference: checkbox.checked ? 1 : 0, group_id } _url.saveReferenceApi(data).then(res => { groupTesterGrid.data.update(testerId, { reference_tester: testerId }) if (res && res.code === 200) { ElMessage.success(MESSAGE_INFO.CO_SAVE_SUCCESS) if (selectedRuleList) { const ruleList = group_list[gtGrid_g_id]?.rule_list ?? {} ruleIds.forEach((rId: number) => { for (const key in ruleList) { const rule = ruleList[key] if (rule.id === Number(rId)) { rule.reference_tester = checkbox.checked ? [testerId] : [] break } } }) } else { const ruleList = group_list[gtGrid_g_id]?.rule_list ?? {} ruleIds.forEach((rId: number) => { for (const key in ruleList) { const rule = ruleList[key] if (rule.id === Number(rId)) { rule.reference_tester = checkbox.checked ? [testerId] : [] break // 找到就停,避免无意义循环 } } }) syncCheckboxState() } groupTesterGrid.data.update(testerId, { count_current: checkbox.checked ? 1 : 0 }) } else { /* 失败回滚 UI */ checkbox.checked = !checkbox.checked const target = groupTesterGrid.data.getItem(testerId) if (target) groupTesterGrid.data.update(testerId, { count_current: checkbox.checked ? 1 : 0 }) } }) } }else{ } }) } const syncCheckboxState = () => { const rules = Object.values(group_list[gtGrid_g_id]?.rule_list || {}) const referenceTesterIds: number[] = [] rules.forEach((r: any) => { if (Array.isArray(r.reference_tester)) { referenceTesterIds.push(...r.reference_tester.map(Number)) } }) groupTesterGrid.data.forEach((row: any) => { const shouldBeChecked = referenceTesterIds.includes(Number(row.id)) if (row.count_current !== (shouldBeChecked ? 1 : 0)) { groupTesterGrid.data.update(row.id, { count_current: shouldBeChecked ? 1 : 0 }) } }) } const getFirstRule = (rulelist:any) =>{ for(let r_k in rulelist){ return rulelist[r_k] } } const getGroupTids = (rulelist:any) =>{ let tids:any = [] for(let r_k in rulelist){ if (rulelist[r_k]['status'] == 1) { tids = _lodash.concat(tids,rulelist[r_k]['tids']) } } return _lodash.uniq(tids) } const btnOnClick=(row:any,column:any,e:any)=>{ let key = e.target.getAttribute("key") clickType.value = true switch (key) { case "addrule": initRulePage('add',row) break case "editrule": initRulePage('edit',row) break case "delrule": initRulePage('rm',row) break case "editgroup": initGroupPage('edit') break case "delgroup": initGroupPage('rm') break case "detailrule": showRuleDetailFlag.value = true break } } const reflushGroupTesterList = () => { groupTesterGrid.data.removeAll() const expandedIds = groupGrid.getExpanded ? groupGrid.getExpanded() : [] const referenceTesterIds: number[] = [] if (!group_list[gtGrid_g_id]) return Object.values(group_list[gtGrid_g_id].rule_list).forEach((r: any) => { if (Array.isArray(r.reference_tester)) { referenceTesterIds.push(...r.reference_tester.map(String)) // 转成字符串 } }) const g_tids = getGroupTids(group_list[gtGrid_g_id].rule_list) if (!g_tids || g_tids.length === 0) return const rows: any[] = [] const rule_tester: any = {} if (group_list[gtGrid_g_id].gtype === gtype.auto) { for (const i in g_tids) { const tid = g_tids[i] rule_tester[tid] = [] for (const j in group_list[gtGrid_g_id].rule_list) { const temp_tids = group_list[gtGrid_g_id].rule_list[j]['tids'] if (temp_tids.indexOf(tid) !== -1) { rule_tester[tid].push(group_list[gtGrid_g_id].rule_list[j]['name']) } } } } for (const i in g_tids) { const t = tester_list[g_tids[i]] if (!t) continue; const rname = rule_tester[g_tids[i]] ? rule_tester[g_tids[i]].join("\n") : '' const checked = referenceTesterIds.includes(String(t.id)) ? 1 : 0 rows.push({ tid: t.tid, tname: t.tname, rname, id: t.id, count_current: checked, rule_list:t.rule_list }) } expandedIds.forEach(id => { if (groupGrid.data.getItem(id)) { groupGrid.expand(id) } }) groupTesterGrid.data.parse(rows) // groupTesterGrid.data.removeAll() // if (!group_list[gtGrid_g_id]) return; // const referenceTesterIds: number[] = [] // if (!group_list[gtGrid_g_id]) return // Object.values(group_list[gtGrid_g_id].rule_list).forEach((r: any) => { // if (Array.isArray(r.reference_tester)) { // referenceTesterIds.push(...r.reference_tester.map(String)) // 转成字符串 // } // }) // let g_tids = getGroupTids(group_list[gtGrid_g_id].rule_list) // if (!g_tids || g_tids.length == 0) return // let rows = [] // // 初始化每个tester 的rule // let rule_tester:any = {} // if ( group_list[gtGrid_g_id]['gtype'] == gtype.auto ) { // for (var i in g_tids){ // let tid = g_tids[i] // rule_tester[tid] = [] // for (var j in group_list[gtGrid_g_id].rule_list){ // var temp_tids = group_list[gtGrid_g_id].rule_list[j]['tids'] // if (temp_tids.indexOf(tid) != -1) { // rule_tester[tid].push(group_list[gtGrid_g_id].rule_list[j]['name']) // } // } // } // } // for (var i in g_tids){ // var t = tester_list[g_tids[i]]; // if (!t) continue; // const checked = referenceTesterIds.includes(String(t.id)) ? 1 : 0 // var rname = rule_tester[g_tids[i]] ? rule_tester[g_tids[i]].join("\n"):''; // rows.push({ // tid: t.tid, // tname: t.tname, // rname, // id: t.id, // count_current: checked, // rule_list:t.rule_list // }) // } // groupTesterGrid.data.parse(rows) } const reflushLinkList = (id:any) =>{ // groupTesterGrid.data.removeAll() if (!group_list[id]) return const referenceTesterIds: number[] = [] group_list = group_list Object.values(group_list[id].rule_list).forEach((r: any) => { if (Array.isArray(r.reference_tester)) { referenceTesterIds.push(...r.reference_tester.map(String)) // 转成字符串 } }) let g_tids = getGroupTids(group_list[id].rule_list) console.log(g_tids,"g_tids") if (!g_tids || g_tids.length == 0) return let rows = [] // 初始化每个tester 的rule let rule_tester:any = {} if ( group_list[id]['gtype'] == gtype.auto ) { for (var i in g_tids){ let tid = g_tids[i] rule_tester[tid] = [] for (var j in group_list[id].rule_list){ var temp_tids = group_list[id].rule_list[j]['tids'] if (temp_tids.indexOf(tid) != -1) { rule_tester[tid].push(group_list[id].rule_list[j]['name']) } } } } for (var i in g_tids){ var t = tester_list[g_tids[i]] if (!t) continue; var rname = rule_tester[g_tids[i]] ? rule_tester[g_tids[i]].join("\n"):'' const checked = referenceTesterIds.includes(String(t.id)) ? 1 : 0 console.log(referenceTesterIds.includes(String(t.id)),"referenceTesterIds") rows.push({tid:t['tid'],tname:t['tname'],rname,id:t['id'],count_current: checked}) } groupTesterGrid.data.parse(rows) initGridEvent() } const reflushGroupList =(gid?:any)=>{ load.show() _url.testerGroupInit().then(function(res:any){ load.hide() group_list = res.data.group_list tester_list = res.data.tester_list groupGrid.data.removeAll() groupTesterGrid.data.removeAll() if (props.selectLink && props.isManualSelect && clickType.value == false) { console.log(props.selectLink.group_id,"props.selectLink.group_id") setTimeout(() => { reflushLinkList(props.selectLink.group_id) }, 500) } gtGrid_g_id = 0 if ( !res ) { refData.groupGridNumber = 0 return; } let rows = [] for (var g_id in res.data.group_list){ var gdata = res.data.group_list[g_id] rows.push({g_id:g_id,id:g_id,gname:gdata['gname'],gtype:gdata['gtype'],gid:gdata['gid'],rule_list:gdata['rule_list']}) for(var r_k in gdata['rule_list']){ var rdata = gdata['rule_list'][r_k] if (rdata['rule_type'] != gtype.auto) continue rows.push({g_id:g_id,id:"rule_"+r_k,parent:g_id,gid:gdata['gid'], status:rdata['status'],rule_list:gdata['rule_list'], rname:rdata['name'],rule_id:rdata['id'],r_index:r_k, rtype:modeType.strict.v == rdata['is_strict'] ? modeType.strict.text : modeType.relax.text,count_current: 0,reference_tester: []}) } } refData.groupGridNumber = rows.length groupGrid.data.parse(rows) groupGrid.collapseAll() if (gid) { groupGrid.expand(gid) gtGrid_g_id = gid reflushGroupTesterList() } }) } // 使用elment标签画页面 // edit group code const initGroupPage = (action:String)=>{ emit("initGroupPage") if (action == "add"){ groupTitle = "Add Group" formGroup.id = 0 formGroup.gname = "" formGroup.gtype = gtype.auto formGroup.g_tids = [] showGroupEditFlag.value = true return } const selectedCell = groupGrid.selection.getCell(); if (!selectedCell) { ElMessage.warning(MESSAGE_INFO.TSG_SELECT_GROUP) return } let row = selectedCell.row if ( action == "edit") { let gdata = group_list[row.g_id] groupTitle = "Edit Group" formGroup.gname = gdata.gname formGroup.gtype = gdata.gtype formGroup.id = gdata.id formGroup.g_tids = getGroupTids(gdata.rule_list) showGroupEditFlag.value = true } else if ( action == "rm") { ElMessageBox.confirm(MESSAGE_INFO.TSG_DEL_CONFIRM,'Warning',{confirmButtonText: 'OK',cancelButtonText: 'Cancel',type: 'warning',cancelButtonClass:'confirm_cancel_button'}).then(function(){ _url.delGroup({id:row.g_id}).then(function(res){ if ( !res ) return; ElMessage.success(MESSAGE_INFO.CO_DEL_SUCCESS) reflushGroupList() }) }) } } const showEditGroupNewCallback = ()=>{ testerListGrid = new dhx.Grid("showGroupTester", buildGridConfig(_data.MANUAL_TESTER,{autoWidth:true,multiselection:true})) if (!edit_group_t) { edit_group_t = [] for(var t in tester_list){ var t_t = tester_list[t]; edit_group_t.push({id:t,tid:t_t['tid'],tname:t_t['tname'],site:t_t['site']}) } } testerListGrid.data.parse(edit_group_t) testerListGrid.events.on("cellClick",function(row:any,column:any){ if ( 'select' == column.id ) {dhtmlxe.selectOrUnselectCheckbox(testerListGrid)} }) // group 弹窗初始化 if (formGroup.id ){ // if (formGroup.gtype == gtype.auto) refreshGroupByRule(true); if (formGroup.gtype == gtype.manual) { testerListGrid.data.forEach(function(item:any) { if (formGroup.g_tids.indexOf(item.id) == -1) { item.select = 0 } else { item.select = 1 } }) } } else { testerListGrid.data.forEach(function(item:any) { item.select = 0 }) } } const saveGroupInfo = (el:any) =>{ if (!el) return; el.validate((valid:boolean) => { if (valid) { let formData = _lodash.cloneDeep(formGroup) if ( formData.gtype == gtype.manual ) { formData.g_tids = [] let initData = testerListGrid.data.getInitialData() for (let index = 0; index < initData.length; index++) { const item = initData[index] if (item.select) formData.g_tids.push(item.id) } } formData = formAllStringValueTrim(formData) _url.saveGroup(formData).then(function(res:any){ if ( !res ) return; if ( res.code != 200) return ElMessage.success(MESSAGE_INFO.CO_SAVE_SUCCESS) reflushGroupList(formGroup.id) showGroupEditFlag.value = false }) } }) } const initRulePage =(action:String,row:any)=>{ formRule.gid = row.g_id if (action == "add"){ ruleTitle = "Add Rule" formRule.id = 0 formRule.status = 1 formRule.name = "" formRule.source = rsource.testerconfig.v formRule.is_strict = modeType.strict.v formRule.smt = smt_platform.smt7.v formRule.reference = [] formRule.rule = [] showRuleEditFlag.value = true return } const selectedCell = groupGrid.selection.getCell(); if (!selectedCell) { ElMessage.warning(MESSAGE_INFO.TSG_SELECT_RULE) return } let rdata = group_list[row.g_id].rule_list[row.r_index] if ( action == "edit") { ruleTitle = "Edit Rule" formRule.id = rdata.id formRule.status = rdata.status formRule.name = rdata.name formRule.source = rdata.source formRule.is_strict = rdata.is_strict formRule.smt = rdata.smt formRule.reference = rdata.reference formRule.rule = rdata.rule showRuleEditFlag.value = true } else if ( action == "rm") { ElMessageBox.confirm(MESSAGE_INFO.TSG_DEL_RULE_CONFIRM,'Warning',{confirmButtonText: 'OK',cancelButtonText: 'Cancel',type: 'warning',cancelButtonClass:'confirm_cancel_button'}).then(function(){ _url.delRule({id:rdata.id}).then(function(res){ if ( !res ) return; ElMessage.success(MESSAGE_INFO.CO_DEL_SUCCESS) delete group_list[row.g_id].rule_list[row.r_index] groupGrid.data.remove(row.id) reflushGroupTesterList() }) }) } } const showEditRuleNewCallback = ()=>{ if (stesterGrid == undefined){ stesterGrid = new dhx.Grid("showRuleTester", buildGridConfig(_data.RULE_S_TESTER,{autoWidth:true})) for(var t in tester_list){ var t_t = tester_list[t]; if (t_t.snapshots_id == null) continue stesterGrid.data.add(t_t); } stesterGrid.events.on("cellClick",function(row,column,e){ flashRule({tid:row.id}); }) } if (sdeviceGrid == undefined) { sdeviceGrid = new dhx.Grid("showRuleDevice", buildGridConfig(_data.RULE_S_DEVICE,{autoWidth:true})) getDeviceListApi().then((res)=>{ if ( res && 200 == res.code ) { sdeviceGrid.data.parse(res.data.getdevicelist.data.data) } }) sdeviceGrid.events.on("cellClick",function(row,column,e){ flashRule({d_id:row.id,product_name:row.product_name}); }) } if (show_ruleGrid == undefined) show_ruleGrid = new dhx.Grid("ruleDetail", buildGridConfig(_data.RULE_DETAIL,{autoWidth:true})) if (formRule.name != ""){ getRuleShowParse(formRule.reference) } else { show_ruleGrid.data.removeAll() } refData.clearFileFlag = true } //tid:String, d_id:String, d_type:String, product_name:String const flashRule = (data:any)=>{ data.rsource = formRule.source show_ruleGrid.data.removeAll() formRule.reference = [] formRule.rule = [] load.show() _url.getRuleItems(data).then(function(res){ if (res && res.data) { if (data.d_id) { res.data.reference['pname'] = data.product_name; } formRule.reference = res.data.reference formRule.rule = res.data.rule getRuleShowParse(res.data.reference) } load.hide() }) } const getRuleShowParse = (items:any,bygrid?:any)=>{ if (items != null){ let data = [] if(items.tname) data.push({tname: items.tname}) if(items.pname) data.push({pname: items.pname}) if(items.smt) data.push({smt: items.smt}) for (let k in items.board){ let pogo_l = []; for (let key in items.board[k]['pogo']) pogo_l.push(items.board[k]['pogo'][key]['p']); data.push({b_name: items.board[k]['recipe_name'],cable: items.board[k]['cable'],pogo: pogo_l}) } if (bygrid) bygrid.data.parse(data) else show_ruleGrid.data.parse(data) } } const rtypeChange = (v:any)=>{ formRule.reference = [] formRule.rule = [] show_ruleGrid.data.removeAll() } const saveRuleInfo = (formEl)=>{ if (!formEl) return; formEl.validate((valid) => { if (valid) { if (!formRule.reference || formRule.reference.length == 0) { if (formRule.source == rsource.model.v) { ElMessage.error(MESSAGE_INFO.TSG_RULE_CHECK_MODULE) return } else if (formRule.source == rsource.testerconfig.v) { ElMessage.error(MESSAGE_INFO.TSG_RULE_CHECK_TESTER) return } else if (formRule.source == rsource.device.v) { ElMessage.error(MESSAGE_INFO.TSG_RULE_CHECK_DEVICE) return } ElMessage.error(MESSAGE_INFO.TSG_RULE_CHECK) return } _url.saveGroupRule(formRule).then(function(res){ if (res && res.code == 200){ reflushGroupList(formRule.gid) showRuleEditFlag.value = false } }) } }) } const showRuleDetailFlagCallback=()=>{ if (showRuleDetailGrid == undefined) showRuleDetailGrid = new dhx.Grid("showRuleDetail", buildGridConfig(_data.RULE_DETAIL,{autoWidth:true})) const selectedCell = groupGrid.selection.getCell(); if (!selectedCell) { ElMessage.warning(MESSAGE_INFO.TSG_SELECT_GROUP) return } let row = selectedCell.row getRuleShowParse(group_list[row.g_id]['rule_list'][row.r_index]['reference'],showRuleDetailGrid) } const iconClick =(e)=>{ if (e == "add") { initGroupPage('add') } } const onTesterDialogOpened = () => { testerDetail() getRuleShowParse(getDetail.value,testerDetailGrid) } const testerDetail = () =>{ if (null != testerDetailGrid) testerDetailGrid.destructor() if (null != testerDetailLayout) testerDetailLayout.destructor() testerDetailLayout = new dhx.Layout("tester_layout", { type: "none", rows: [{ id: "tester_grid" }], }) let column_config = dhtmlxe.mergeGridColumnConfig(_data.TESTERINFORMATION, "") let gridConfig = buildGridConfig(column_config, { autoWidth: true }) testerDetailGrid = new dhx.Grid(null, gridConfig) testerDetailLayout.getCell("tester_grid").attach(testerDetailGrid) } const handleChange = (e) => { if (!e.target.classList.contains('dhx_checkbox--check-all')) return; if (e.target.checked) { document.querySelectorAll('.dhx_checkbox--check-all').forEach(cb => { if (cb !== e.target) { cb.checked = false; } }); } } defineExpose({iconClick}) const emit = defineEmits(['initGroupPage']) </script> <template> <div> <div v-show="refData.groupGridNumber > 0" class="tester_setup_layout" id="tester_group_layout"></div> <div v-show="refData.groupGridNumber == 0" class="tester_setup_layout grid_head"> <span class="no_license">Please add a new tester group.</span> </div> <el-dialog width="600px" top="10vh" @open="showEditGroupNewCallback" v-model="showGroupEditFlag" :close-on-click-modal="false" :draggable="true" :title="groupTitle" destroy-on-close> <el-form ref="groupFormRef" :model="formGroup" class="demo-form-inline" label-position="left"> <el-input v-model="formGroup.id" v-show="false" /> <el-form-item label="Group Name" label-width="120px" :rules="commonRulesByGroup.gname" prop="gname"> <el-input v-model="formGroup.gname" clearable style="width: 420px;" /> </el-form-item> <el-form-item label="Group Type" label-width="120px" > <div class="form-item-row"> <el-select v-model="formGroup.gtype" :disabled="formGroup.id != 0"> <el-option label="Auto Grouping" value="AUTO" /> <el-option label="Manual Grouping" value="MANUAL" /> </el-select> </div> </el-form-item> </el-form> <el-row style="height: 450px;margin-top:10px" v-show="formGroup.gtype == gtype.manual"> <el-col :span="24"> <div style="height: 100%;" id="showGroupTester"></div> </el-col> </el-row> <template #footer center> <span class="dialog-footer"> <el-button @click="saveGroupInfo(groupFormRef)" type="primary" >Submit</el-button> <el-button @click="showGroupEditFlag = false">Cancel</el-button> </span> </template> </el-dialog> <el-dialog width="940px" top="10vh" @open="showEditRuleNewCallback" @close="()=>{ refData.clearFileFlag = false if (formRule.source == rsource.model.v) flashRule({rmfile:1,pogofile:formRule.pogofile,modulefile:formRule.modulefile}) }" v-model="showRuleEditFlag" :title="ruleTitle" :close-on-click-modal="false" :draggable="true"> <el-form :model="formRule" label-position="right" ref="ruleFormRef" class="rule-form" > <el-row> <el-col :span="ruleColSpan"> <el-form-item label="Rule name" :label-width="formLabelWidth" :rules="commonRulesByGroup.rname" prop="name"> <el-input v-model="formRule.name" /> </el-form-item> </el-col> <el-col :span="ruleColSpan"> <el-form-item label="Source" :label-width="formLabelWidth"> <el-select v-model="formRule.source" @change="rtypeChange" > <el-option v-for="(item, index) in rsource" :label="item.text" :value="item.v" v-show="item.hide == false" /> </el-select> </el-form-item> </el-col> </el-row> <el-row> <el-col :span="ruleColSpan"> <el-form-item label="Mode" :label-width="formLabelWidth"> <el-select v-model="formRule.is_strict"> <el-option v-for="(item, index) in modeType" :label="item.text" :value="item.v" /> </el-select> </el-form-item> </el-col> <el-col :span="ruleColSpan"> <el-form-item label="Smt Platform" :label-width="formLabelWidth" v-show="formRule.source == rsource.model.v"> <el-select v-model="formRule.smt" > <el-option v-for="(item, index) in smt_platform" :label="item.text" :value="item.v" /> </el-select> </el-form-item> </el-col> </el-row> <el-row> <el-col :span="ruleColSpan"> <el-form-item label="Model File" required :label-width="formLabelWidth" v-show="formRule.source == rsource.model.v"> <UploadFile source="TSG_MODULE_FILE" :clearFileFlag="refData.clearFileFlag" :onlyUpload="true" :dragFlag="false" @uploadFileCallBack="(res)=>{ formRule.modulefile = res.data flashRule({smt_platform:formRule.smt,pogofile:formRule.pogofile,modulefile:formRule.modulefile}) }"></UploadFile> </el-form-item> </el-col> <el-col :span="ruleColSpan"> <el-form-item label="Pogo Mapping File" :label-width="formLabelWidth" v-show="formRule.source == rsource.model.v && formRule.smt == smt_platform.smt7.v"> <UploadFile source="TSG_MODULE_FILE" :clearFileFlag="refData.clearFileFlag" :onlyUpload="true" :dragFlag="false" @uploadFileCallBack="(res)=>{ formRule.pogofile = res.data flashRule({smt_platform:formRule.smt,pogofile:formRule.pogofile,modulefile:formRule.modulefile}) }"></UploadFile> </el-form-item> </el-col> </el-row> <el-row class="win_grid" v-show="formRule.source == rsource.testerconfig.v"> <label class="el-form-item__label" style="width: 140px;">Select Tester</label> <div style="width:calc(100% - 140px); height: 100%;" id="showRuleTester"></div> </el-row> <el-row class="win_grid" v-show="formRule.source == rsource.device.v"> <label class="el-form-item__label" style="width: 140px;">Select Device</label> <div style="width:calc(100% - 140px); height: 100%;" id="showRuleDevice"></div> </el-row> <el-row class="win_grid" style="margin-top: 10px;"> <el-col :span="24"> <div style="height: 100%;" id="ruleDetail"></div> </el-col> </el-row> </el-form> <template #footer center> <span class="dialog-footer"> <el-button type="primary" @click="saveRuleInfo(ruleFormRef)">Submit</el-button> <el-button @click="showRuleEditFlag = false">Cancel</el-button> </span> </template> </el-dialog> <el-dialog width="940px" top="10vh" @open="showRuleDetailFlagCallback" :draggable="true" v-model="showRuleDetailFlag" title="Rule Detail"> <div style='height: 540px;' id="showRuleDetail"></div> </el-dialog> <el-dialog width="840px" :draggable="true" @opened="onTesterDialogOpened" v-model="showTester" title="Tester Detail"> <div style='height: 500px;' id="tester_layout"></div> </el-dialog> </div> </template> <style lang="scss" scoped> .dialog-footer{ display: flex; justify-content: center; align-items: center; } .win_grid{ height: 240px; } :deep(.rule-form .el-select){ width: 215px; } :deep(.rule-form .el-input){ width: 215px; } :deep(.form-item-row .el-input__wrapper) { width: 420px; } </style> 我这个怎么如果从别的页面跳过来,我选中,再次点击别的行切回来我就选中的变成未选中了在视觉上,
最新发布
10-14
<!DOCTYPE html> <html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro"> <head> <th:block th:include="include :: header('教师管理列表')"/> <th:block th:include="include :: layout-latest-css"/> <th:block th:include="include :: ztree-css"/> </head> <body class="gray-bg"> <div class="ui-layout-west"> <div class="box box-main"> <div class="box-header"> <div class="box-title"> <i class="fa icon-grid"></i> 组织机构 </div> <div class="box-tools pull-right"> <button type="button" class="btn btn-box-tool" id="btnExpand" title="展开" style="display:none;"><i class="fa fa-chevron-up"></i></button> <button type="button" class="btn btn-box-tool" id="btnCollapse" title="折叠"><i class="fa fa-chevron-down"></i></button> <button type="button" class="btn btn-box-tool" id="btnRefresh" title="刷新部门"><i class="fa fa-refresh"></i></button> </div> </div> <div class="ui-layout-content"> <div id="tree" class="ztree"></div> </div> </div> </div> <div class="ui-layout-center"> <div class="container-div"> <div class="row"> <div class="col-sm-12 search-collapse"> <form id="formId"> <div class="select-list"> <input type="hidden" id="schoolId" name="schoolId"> <input type="hidden" id="collegeId" name="collegeId"> <ul> <li> <input id="selectConditionOneInput" type="text" name="multipleOne" style="width: 200px;" placeholder="姓名|工号(账号)"/> </li> <li> <input id="selectConditionTwoInput" type="text" name="multipleTwo" style="width: 250px;" placeholder="邮箱|手机号|身份证|昵称"/> </li> <li> <label>账号:</label> <select style="width: 60px" name="state" th:with="type=${@dict.getType('accountStatus')}"> <option value="">所有</option> <option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}"></option> </select> </li> <li> <a class="btn btn-primary btn-rounded btn-sm" onclick="$.table.search()"><i class="fa fa-search"></i> 搜索</a> <a class="btn btn-warning btn-rounded btn-sm" onclick="$.form.reset()"><i class="fa fa-refresh"></i> 重置</a> </li> </ul> </div> </form> </div> <div class="btn-group-sm" id="toolbar" role="group"> <a class="btn btn-success disabled" id="addBtn" onclick="$.operate.add($('#collegeId').val())" shiro:hasPermission="system:teacher:add"> <i class="fa fa-plus"></i> 新增 </a> <a class="btn btn-danger multiple disabled" onclick="javascript:batchUpdatePwd()" shiro:hasPermission="system:teacher:batchUpdatePwd"> <i class="fa fa-sign-out"></i> 批量重置密码 </a> <a id="importBtn" class="btn btn-info disabled" onclick="importExcel()" shiro:hasPermission="system:student:export"> <i class="fa fa-upload"></i> 导入 </a> <!-- <a class="btn btn-warning" onclick="$.table.exportExcel()" shiro:hasPermission="system:teacher:export">--> <!-- <i class="fa fa-download"></i> 导出--> <!-- </a>--> <a class="btn btn-warning" onclick="$.table.exportExcelByChoose()" shiro:hasPermission="system:teacher:export"> <i class="fa fa-upload"></i> 导出 </a> <a href="/teacherTemplate.xlsx">点击下载导入模版</a> </div> <div class="col-sm-12 select-table table-striped"> <table id="bootstrap-table"></table> </div> </div> </div> </div> <th:block th:include=include :: footer”/> <th:block th:include=include :: layout-latest-js”/> <th:block th:include=include :: ztree-js”/> <script th:inline="javascript"> var editFlag = [[${@permission.hasPermi('system:teacher:edit')}]]; var removeFlag = [[${@permission.hasPermi('system:teacher:remove')}]]; var genderDatas = [[${@dict.getType('sys_user_sex')}]]; var stateDatas = [[${@dict.getType('accountStatus')}]]; var prefix = ctx + "system/teacher"; $(function () { var panehHidden = false; if ($(this).width() < 769) { panehHidden = true; } $('body').layout({initClosed: panehHidden, west__size: 185}); // 回到顶部绑定 if ($.fn.toTop !== undefined) { var opt = { win: $('.ui-layout-center'), doc: $('.ui-layout-center') }; $('#scroll-up').toTop(opt); } queryTeacherList(); queryOrganizationTree(); }); function queryTeacherList() { var options = { url: prefix + "/list", createUrl: prefix + "/add/{id}", updateUrl: prefix + "/edit/{id}", removeUrl: prefix + "/remove", exportUrl: prefix + "/export", exportByChooseUrl: prefix + "/export/choose", importUrl: prefix + "/importData", modalName: "教师", columns: [ { checkbox: true }, { field: 'id', title: '唯一id', visible: false }, { field: 'name', title: '姓名' }, { field: 'account', title: '账号(职工号)' }, { field: 'gender', title: '性别', formatter: function (value, row, index) { return $.table.selectDictLabel(genderDatas, value); }, width: 20 }, { field: 'nickName', title: '昵称' }, { field: 'email', title: '邮箱' }, // { // field: 'idNum', // title: '身份证件号', // width: 100 // }, { field: 'phone', title: '手机号', // width: 100 }, { field: 'educational', title: '学历' }, { field: 'degree', title: '学位' }, // { // field: 'birthday', // title: '生日' // }, { field: 'state', title: '账号状态', formatter: function (value, row, index) { return statusTools(row); }, width: 20 }, { field: 'collegeName', title: '所在学院' }, { field: 'lastLoginTime', title: '最后一次登录时间' }, { title: '操作', align: 'center', formatter: function (value, row, index) { var actions = []; var more = []; more.push("<a id='editBtn' rowid='" + row.id + "' class='btn btn-success btn-xs " + editFlag + "' href='javascript:void(0)' onclick='power(this)'><i class='fa fa-edit'></i>权限</a> "); more.push("<a id='editBtn' rowid='" + row.id + "' class='btn btn-success btn-xs " + editFlag + "' href='javascript:void(0)' onclick='edit(this)'><i class='fa fa-edit'></i>编辑</a> "); more.push("<a id='resetPasswordBtn' rowid='" + row.id + "' class='btn btn-success btn-xs" + editFlag + "' href='javascript:void(0)' onclick='resetPassword(this)'><i class='fa fa-edit'></i>重置密码</a> "); more.push("<a id='removeBtn' rowid='" + row.id + "' class='btn btn-danger btn-xs " + editFlag + "' href='javascript:void(0)' onclick='remove(this)'><i class='fa fa-edit'></i>删除</a> "); actions.push('<a id="' + 'operation' + row.id + '" rowid="' + row.id + '" rowname="' + row.name + '" rowaccount="' + row.account + '" tabindex="0" class="btn btn-info btn-xs" role="button" data-container="body" data-placement="left" data-toggle="popover" data-html="true" data-trigger="hover" data-content="' + more.join('') + '"><i class="fa fa-chevron-circle-right"></i>更多操作</a>'); return actions.join(''); }, width: 20 }] }; $.table.init(options); } /* 账号状态显示 */ function statusTools(row) { if (row.state == 0) { // 禁用 return '<i class=\"fa fa-toggle-off text-info fa-2x\" onclick="enable(\'' + row.id + '\')"></i> '; } else { // 启用 return '<i class=\"fa fa-toggle-on text-info fa-2x\" onclick="disable(\'' + row.id + '\')"></i> '; } } /* 账号状态-停用 */ function disable(id) { $.modal.confirm("确认要禁用账号吗?", function () { $.operate.post(prefix + "/changeState", {"id": id, "state": 0}); }) } /* 账号状态-启用 */ function enable(id) { $.modal.confirm("确认要启用账号吗?", function () { $.operate.post(prefix + "/changeState", {"id": id, "state": 1}); }) } /** * 组织树 */ function queryOrganizationTree() { var url = ctx + "system/organization/collegeTreeData"; var options = { url: url, expandLevel: 3, onClick: zOnClick }; $.tree.init(options); function zOnClick(event, treeId, treeNode) { if (treeNode.level == 0) { $("#addBtn").addClass("disabled"); $("#importBtn").addClass("disabled"); } else { $("#addBtn").removeClass("disabled"); $("#importBtn").removeClass("disabled"); } var treeObj = $.fn.zTree.getZTreeObj("tree"); var levelChildren = getLastLevelChildren(treeObj, treeNode); if (levelChildren.length > 0) { var collegeId = levelChildren[0].id; switch (treeNode.level) { case 0: $("#schoolId").val(null); $("#collegeId").val(null); break; case 1: $("#schoolId").val(treeNode.id); $("#collegeId").val(collegeId); break; case 2: $("#schoolId").val(null); $("#collegeId").val(treeNode.id); break; } }else { switch (treeNode.level) { case 0, 1: $.modal.alert("此大学下面还没有学院,不能添加教师") break; } } $.table.search(); } } function getLastLevelChildren(treeObj, node) { var level4Nodes = []; function findLevel4Children(currentNode, currentLevel) { if (currentLevel == 2) { level4Nodes = level4Nodes.concat(currentNode || []); } else if (currentNode.children) { currentNode.children.forEach(function (childNode) { findLevel4Children(childNode, currentLevel + 1); }); } } findLevel4Children(node, node.level); return level4Nodes; } $('#btnExpand').click(function () { $._tree.expandAll(true); $(this).hide(); $('#btnCollapse').show(); }); $('#btnCollapse').click(function () { $._tree.expandAll(false); $(this).hide(); $('#btnExpand').show(); }); $('#btnRefresh').click(function () { queryOrganizationTree(); }); /** * 重置密码 */ function resetPassword(btn) { $.modal.confirm("确定重置密码吗?", function () { $.modal.disable(); var url = prefix + "/resetPassword/" + $(btn).attr("rowid"); var data = {"id": $(btn).attr("rowid")}; var config = { url: url, type: "get", dataType: "json", data: "", beforeSend: function () { $.modal.loading("正在处理中,请稍候..."); }, success: function (data) { if (data.code == 0) { // 成功 $.modal.closeLoading(); $.modal.alert("密码已重置为:" + $("#operation" + $(btn).attr("rowid")).attr("rowaccount") + "@tea"); $.modal.disable(); } else { // 失败 $.modal.closeLoading(); $.modal.msgError(data.msg); $.modal.disable(); } } }; $.ajax(config) }); } /** * 删除 */ function remove(btn) { $.operate.remove($(btn).attr("rowid")); // $.table.search(); } /** * 编辑 */ function edit(btn) { $.operate.edit($(btn).attr("rowid")); } function power(btn) { let teacherId = $(btn).attr("rowid") let url = prefix + "/power/" + teacherId; $.modal.open("修改教师权限", url, '800', '400'); } /** * 回车查询 */ $("#selectConditionOneInput").keydown(function (e) { if (e.keyCode == 13) { $.table.search(); } }); $("#selectConditionTwoInput").keydown(function (e) { if (e.keyCode == 13) { $.table.search(); } }); /** * 文件上传 */ function importExcel(){ top.layer.open({ type: 1, area: [400 + 'px', 230 + 'px'], fix: false, //不固定 maxmin: true, shade: 0.3, title: '导入' + table.options.modalName + '数据', content: '<form enctype="multipart/form-data" class="mt20 mb10">\n' + ' <div class="col-xs-offset-1">\n' + ' <input type="file" id="file" name="file"/>\n' + ' <input class="hidden" type="text" id="importCollegeId" name="collegeId"/>\n' + ' <font color="red" class="pull-left mt10">\n' + ' 提示:仅允许导入“xls”或“xlsx”格式文件!\n' + ' </font>\n' + ' </div>\n' + ' </form>', btn: ['<i class="fa fa-check"></i> 导入', '<i class="fa fa-remove"></i> 取消'], // 弹层外区域关闭 shadeClose: true, btn1: function(index, layero){ var file = layero.find('#file').val(); layero.find('#importCollegeId').val($("#collegeId").val()); if (file == '' || (!$.common.endWith(file, '.xls') && !$.common.endWith(file, '.xlsx'))){ $.modal.msgWarning("请选择后缀为 “xls”或“xlsx”的文件。"); return false; } var index = top.layer.load(2, {shade: false}); $.modal.disable(); var formData = new FormData(layero.find('form')[0]); $.ajax({ url: table.options.importUrl, data: formData, cache: false, contentType: false, processData: false, type: 'POST', success: function (result) { if (result.code == web_status.SUCCESS) { $.modal.close(index); $.modal.closeAll(); $.modal.alertSuccess(result.msg); $.table.refresh(); } else if (result.code == web_status.WARNING) { $.modal.close(index); $.modal.enable(); $.modal.alertWarning(result.msg) } else { $.modal.close(index); $.modal.enable(); $.modal.alertError(result.msg); } }, complete: function () { layero.find('#file').val(''); } }); } }); } // 批量更新密码 function batchUpdatePwd() { var rows = $.table.selectColumns("id"); if (rows.length === 0) { $.modal.alertWarning("请选择用户"); return; } console.log(rows) //var initPwd = [[${@config.getKey('sys.user.initPassword')}]] layer.prompt({ formType: 0, title: '请输入重置密码', placeholder: '请输入重置密码', maxlength: 16, move: false, value: null //area: ['800px', '350px'] //自定义文本域宽高 }, function(value, index, elem){ if (value.length < 2){ $.modal.msgError("请输入正确格式的密码") return } $.modal.confirm("确认重置选中的"+ rows.length +"位教师密码?", function() { $.ajax({ type: 'patch', url: ctx + "system/teacher/resetPassword", dataType: 'json', data:{ ids:rows.join(), pwd: value }, success: function (result) { if (result.code === web_status.SUCCESS) { $.modal.msgSuccess("重置成功"); } else { $.modal.msgError("重置失败"); } }, error: function () { $.modal.msgError("重置失败"); } }); }); layer.close(index); }); } </script> </body> </html> 用vue语言重写这个页面,保持原有的功能和逻辑不变,不要使用jquery语法
07-10
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值