js中没有contains()方法可以用indexOf("str") != -1代替,一个改变自定义checkbox状态的代码

本文介绍了一种使用JavaScript实现自定义复选框状态切换的方法。通过检查图片源中是否包含特定字符串来判断复选框的状态,并据此更新其显示状态。此方法利用了JavaScript中的indexOf方法来模拟contains方法的功能。
原文地址为: js中没有contains()方法可以用indexOf("str") != -1代替,一个改变自定义checkbox状态的代码

 js中没有contains()方法可以用indexOf("str") != -1代替

 例如一个改变自定义checkbox状态的代码如下:

 function changTurnonState(obj){

//alert(obj.src.indexOf("checked") != -1);
if(obj.src.indexOf("checked") != -1){
obj.src = "nocheck.png";
}else{
obj.src = "checked.png";
}
}
 

转载请注明本文地址: js中没有contains()方法可以用indexOf("str") != -1代替,一个改变自定义checkbox状态的代码
<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() const ids = 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 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 let selectTids = ref() 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 } ) let disable = ref() //跳转至Tab页面统计之前页面停留时长 const initStart = ()=> { //调用接口处理 store.dispatch('updatePageDurationData', getPageDurationData()) //记录当前页面 SessionStorage.set('lastFeature', 'tester-group-setup') SessionStorage.set('lastFeatureTime', iso8601ToDateString(new Date().toISOString())) } const isClickchange = ref() const curRefTesterId = ref() 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) { if (!res) return; /* 先更新本地状态 */ const rule = group_list[row.g_id]['rule_list'][row.r_index]; rule.status = status; rule.tids = res.data; /* 立即刷新列表(enable 后 status=1) */ reflushGroupTesterList(); /* 等 isClickchange 拿到后台返回的 testerId 再判断 */ setTimeout(() => { if(status == 1){ isClickchange.value = res.data[0]; } const referenceTesterIds: string[] = [ ...new Set( Object.values(group_list[gtGrid_g_id].rule_list) .filter((r: any) => Array.isArray(r.reference_tester)) .flatMap((r: any) => r.reference_tester.map(String)) ) ] if (referenceTesterIds.length && Number(referenceTesterIds[0]) == Number(isClickchange.value)) { const testerId = referenceTesterIds[0]; groupTesterGrid.data.update(testerId, { count_current: 0 }); const curRuleId = Number( Array.isArray(selectTesterId.value) ? selectTesterId.value[0] : selectTesterId.value ); for (const k in group_list[row.g_id].rule_list) { const r = group_list[row.g_id].rule_list[k] if (r.id === curRuleId) { r.reference_tester = [] break; } } } }, 0) } }) } else if (column.id == "action") { btnOnClick(row,column,e) } 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"){ 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 groupTesterGrid.data.forEach((item: any) => { groupTesterGrid.data.update(item.id, { count_current: 0 }) }) /* 2. 只点亮当前行 */ groupTesterGrid.data.update(testerId, { count_current: checkbox.checked ? 1 : 0 }); /* 3. 智能单选:只保留当前规则的 reference_tester,其余清空 */ const curRuleId = Number( Array.isArray(selectTesterId.value) ? selectTesterId.value[0] : selectTesterId.value ); for (const k in group_list[gtGrid_g_id].rule_list) { const rule = group_list[gtGrid_g_id].rule_list[k]; if (rule.id === curRuleId) { rule.reference_tester = checkbox.checked ? [testerId] : []; } else { rule.reference_tester = []; // 其他规则一律清空 } } const rawValue = String(selectGroupId.value || ''); const group_id = rawValue.startsWith('rule_') ? rawValue.replace(/^rule_/, '') : rawValue; const data = { tester_id: testerId, is_reference: checkbox.checked ? 1 : 0, group_id }; _url.saveReferenceApi(data).then(res => { if (res && res.code === 200) { ElMessage.success(MESSAGE_INFO.CO_SAVE_SUCCESS); } else { /* 失败回滚 */ checkbox.checked = !checkbox.checked; groupTesterGrid.data.update(testerId, { count_current: checkbox.checked ? 1 : 0 }); } }); } }else{ } }) } 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() : [] if (!group_list[gtGrid_g_id]) return const referenceTesterIds: string[] = [ ...new Set( Object.values(group_list[gtGrid_g_id].rule_list) .filter((r: any) => Array.isArray(r.reference_tester)) .flatMap((r: any) => r.reference_tester.map(String)) ) ] const g_tids = getGroupTids(group_list[gtGrid_g_id].rule_list) ids.value = g_tids 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[ids.value[i]] if (!t) continue const rname = rule_tester[ids.value[i]] ? rule_tester[ids.value[i]].join("\n") : '' let checked if(referenceTesterIds[0]==isClickchange.value){ checked = 0 }else{ 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) } const reflushLinkList = (id:any) =>{ 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) 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 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) { 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) const targetId = props.selectLink?.group_id; if (targetId) { const row = groupGrid.data.getItem(String(targetId)); if (row) { groupGrid.selection.setCell(row, groupGrid.config.columns[0]); gtGrid_g_id = Number(targetId); reflushGroupTesterList(); /* 回填选中的 rule */ selectedRuleList = group_list[targetId]?.rule_list; if (selectedRuleList) { const ruleIds = Object.values(selectedRuleList).map((r: any) => r.id); selectTesterId.value = ruleIds; } } } 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>这样写导致导致我的 }else{ 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 }) }我的 rname,显示多个的话,我disable某一个则就把所有的都取消全选了,我要实现的是一个的取消,多个的知道我所有的取消完在取消全选
11-04
import re import time import json import threading from selenium import webdriver from datetime import datetime, timedelta from selenium.webdriver.edge.service import Service as EdgeService from selenium.webdriver.edge.options import Options as EdgeOptions from selenium.webdriver.support.ui import WebDriverWait, Select from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By from apscheduler.schedulers.background import BackgroundScheduler from pytz import timezone import FreeSimpleGUI as sg BEIJING_TZ = timezone('Asia/Shanghai') CONFIG_FILE = 'browser_config.json' ELEMENT_TYPES = { "按钮": "button", "复选框": "checkbox", "单选按钮": "radio", "下拉框": "dropdown", "标签页": "tab", "时间": "time", "文本": "text" } WEEKDAYS = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"] SCHEDULER = None def load_config(): default_config = { "order": ["选项1", "选项2", "选项3", "选项4", "选项5", "选项6", "选项7"], "elements": { "target_url": "http://www.igs.gnsswhu.cn/index.php", "选项1": {"type": "标签页", "identifier": "OBS观测值"}, "选项2": {"type": "下拉框", "identifier": "IGS测站列表"}, "选项3": {"type": "复选框", "identifier": "ABMF00GLP"}, "选项4": {"type": "单选按钮", "identifier": "d文件"}, "选项5": {"type": "时间", "identifier": "开始时间"}, "选项6": {"type": "时间", "identifier": "结束时间"}, "选项7": {"type": "按钮", "identifier": "检索"}, }, "start_date": "", "end_date": "", "schedule": { "start_preset": "--", "end_preset": "--", "schedule_type": "--", "schedule_time": "00:00:00" }, "stay_duration": 60 } try: with open(CONFIG_FILE, 'r') as f: user_config = json.load(f) merged_config = default_config.copy() merged_config.update(user_config) # 用户配置覆盖默认 merged_config['order'] = user_config.get('order', default_config['order']) # 确保顺序正确 # 处理日期格式 if 'start_date' in merged_config and ' ' not in merged_config.get('start_date', ''): merged_config['start_date'] += " 00:00:00" if 'end_date' in merged_config and ' ' not in merged_config.get('end_date', ''): merged_config['end_date'] += " 00:00:00" return merged_config except: return default_config def save_config(values, current_order): try: elements = {} for label in current_order: elements[label] = { "type": values[f'-{label}_TYPE-'], "identifier": values[f'-{label}_ID-'] } if values[f'-{label}_TYPE-'] == "文本": elements[label]["text"] = values[f'-{label}_TEXT-'] def get_full_datetime(date_str, h, m, s): if not date_str: return "" # 格式化为两位数 h = f"{int(h):02d}" if h else "00" m = f"{int(m):02d}" if m else "00" s = f"{int(s):02d}" if s else "00" return f"{date_str} {h}:{m}:{s}" start_time = get_full_datetime(values['-START_DATE-'], values['-START_HOUR-'], values['-START_MINUTE-'], values['-START_SECOND-']) end_time = get_full_datetime(values['-END_DATE-'], values['-END_HOUR-'], values['-END_MINUTE-'], values['-END_SECOND-']) config = { "order": current_order, "elements": { "target_url": values['-URL-'], **elements }, "start_identifier": load_config().get('start_identifier', '开始时间'), "end_identifier": load_config().get('end_identifier', '结束时间'), "start_date": start_time, "end_date": end_time, "schedule": load_config().get('schedule', {}), "stay_duration": load_config().get('stay_duration', 60) } with open(CONFIG_FILE, 'w') as f: json.dump(config, f, ensure_ascii=False) return True except Exception as e: print(f"保存配置失败: {str(e)}") return False def create_element_row(label, element_type='', identifier='', text_value=''): return [ sg.Text(label, size=(12, 1)), sg.Combo( list(ELEMENT_TYPES.keys()), default_value=element_type, key=f'-{label}_TYPE-', size=(15, 1), enable_events=True ), sg.Input( identifier, key=f'-{label}_ID-', size=(20, 1) ), sg.Input( text_value, key=f'-{label}_TEXT-', size=(20, 1), visible=(element_type == "文本") ), sg.Button('↑', key=f'-{label}_UP-'), sg.Button('↓', key=f'-{label}_DOWN-'), sg.Button('-', key=f'-{label}_DEL-') ] def create_elements_column(current_order): config = load_config() elements = [] for label in current_order: element_config = config['elements'].get(label, {}) elements.append(create_element_row( label, element_config.get('type', ''), element_config.get('identifier', ''), element_config.get('text', '') )) return sg.Column( elements, scrollable=True, vertical_scroll_only=True, size=(800, 300), key='-ELEMENTS_COL-' ) def show_settings_window(parent_window): """显示停留时间设置窗口""" config = load_config() start_id = config.get('start_identifier', '开始时间') end_id = config.get('end_identifier', '结束时间') layout = [ [sg.Text('网页停留时间(秒):'), sg.Input(config.get('stay_duration', 60), key='-STAY_DURATION-')], [sg.HorizontalSeparator()], [sg.Text('开始时间字段标识:'), sg.Input(start_id, key='-START_ID-', size=15)], [sg.Text('结束时间字段标识:'), sg.Input(end_id, key='-END_ID-', size=15)], [sg.Button('保存'), sg.Button('取消')] ] window = sg.Window('设置', layout, modal=True) while True: event, values = window.read() if event in (None, '取消'): break if event == '保存': try: stay_duration = int(values['-STAY_DURATION-']) if stay_duration <= 0: raise ValueError("停留时间必须大于0") config['stay_duration'] = stay_duration config['start_identifier'] = values['-START_ID-'].strip() config['end_identifier'] = values['-END_ID-'].strip() with open(CONFIG_FILE, 'w') as f: json.dump(config, f, ensure_ascii=False) sg.popup('设置已保存!') break except Exception as e: sg.popup_error(f'输入无效: {str(e)}') window.close() def create_gui(): config = load_config() current_order = config["order"].copy() elements_config = config["elements"] schedule_config = config.get('schedule', {}) # 时间处理函数 def split_datetime(dt_str): if not dt_str: return "", "00", "00", "00" if ' ' in dt_str: date_part, time_part = dt_str.split(' ', 1) h, m, s = time_part.split(':')[:3] else: date_part, h, m, s = dt_str, '00', '00', '00' return date_part, h, m, s # 初始化日期时间 start_date, start_h, start_m, start_s = split_datetime(config.get('start_date', '')) end_date, end_h, end_m, end_s = split_datetime(config.get('end_date', '')) # 主布局 layout = [ [sg.Text('目标网址'), sg.Input(config['elements']['target_url'], key='-URL-')], [sg.HorizontalSeparator()], [create_elements_column(current_order)], [sg.Button('+ 添加选项', key='-ADD_OPTION-')], [sg.HorizontalSeparator()], # 开始时间选择 [sg.Frame('开始时间', [ [sg.CalendarButton('选择日期', target='-START_DATE-', format='%Y-%m-%d'), sg.Input(start_date, key='-START_DATE-', size=(12, 1)), sg.Spin([f"{i:02}" for i in range(24)], start_h, key='-START_HOUR-', size=3), sg.Text(':'), sg.Spin([f"{i:02}" for i in range(60)], start_m, key='-START_MINUTE-', size=3), sg.Text(':'), sg.Spin([f"{i:02}" for i in range(60)], start_s, key='-START_SECOND-', size=3)] ])], # 结束时间选择 [sg.Frame('结束时间', [ [sg.CalendarButton('选择日期', target='-END_DATE-', format='%Y-%m-%d'), sg.Input(end_date, key='-END_DATE-', size=(12, 1)), sg.Spin([f"{i:02}" for i in range(24)], end_h, key='-END_HOUR-', size=3), sg.Text(':'), sg.Spin([f"{i:02}" for i in range(60)], end_m, key='-END_MINUTE-', size=3), sg.Text(':'), sg.Spin([f"{i:02}" for i in range(60)], end_s, key='-END_SECOND-', size=3)] ])], # 控制按钮 [sg.HorizontalSeparator()], [sg.Button('运行', size=10), sg.Button('定时', key='-SCHEDULE-', size=10), sg.Button('保存配置', size=10), sg.Button('设置', size=10), sg.Button('退出', size=10)], [sg.Output(size=(90, 10), echo_stdout_stderr=True, key='-OUTPUT-')] ] window = sg.Window('浏览器自动化工具', layout, finalize=True) setup_scheduler(window) # 动态更新元素可见性 def update_element_visibility(): for label in current_order: element_type = values.get(f'-{label}_TYPE-', '') window[f'-{label}_TEXT-'].update(visible=(element_type == "文本")) # 主事件循环 while True: event, values = window.read() if event in (None, '退出'): break # 元素类型变化事件 if '_TYPE-' in event: update_element_visibility() # 添加新选项 if event == '-ADD_OPTION-': new_label = f'选项{len(current_order) + 1}' current_order.append(new_label) window.extend_layout(window['-ELEMENTS_COL-'], [create_element_row(new_label)]) window['-ELEMENTS_COL-'].contents_changed() # 删除选项 if '_DEL-' in event: label = event.split('_')[1] if label in current_order: current_order.remove(label) # 删除对应的UI元素 for element in window['-ELEMENTS_COL-'].Widget.winfo_children(): if f'_{label}_' in str(element): element.destroy() # 重新编号剩余选项 new_order = [f"选项{i + 1}" for i in range(len(current_order))] config_changes = {} for old, new in zip(current_order, new_order): config_changes[old] = new # 更新配置和当前顺序 current_order = new_order.copy() window['-ELEMENTS_COL-'].update(visible=False) window['-ELEMENTS_COL-'].update(visible=True) # 移动选项位置 if '_UP-' in event or '_DOWN-' in event: direction = -1 if '_UP-' in event else 1 label = event.split('_')[1] index = current_order.index(label) new_index = index + direction if 0 <= new_index < len(current_order): # 交换顺序 current_order.insert(new_index, current_order.pop(index)) # 重新排列UI元素 elements = [create_element_row(lbl) for lbl in current_order] window['-ELEMENTS_COL-'].update(visible=False) window['-ELEMENTS_COL-'].update(elements) window['-ELEMENTS_COL-'].contents_changed() window['-ELEMENTS_COL-'].update(visible=True) # 保存配置 if event == '保存配置': elements = {} for label in current_order: elements[label] = { "type": values[f'-{label}_TYPE-'], "identifier": values[f'-{label}_ID-'] } if values[f'-{label}_TYPE-'] == "文本": elements[label]["text"] = values[f'-{label}_TEXT-'] # 构建时间字符串 def build_time_str(date_part, h, m, s): return f"{date_part} {h}:{m}:{s}" if date_part else "" new_config = { "order": current_order, "elements": { "target_url": values['-URL-'], ** elements }, "start_date": build_time_str( values['-START_DATE-'], values['-START_HOUR-'], values['-START_MINUTE-'], values['-START_SECOND-'] ), "end_date": build_time_str( values['-END_DATE-'], values['-END_HOUR-'], values['-END_MINUTE-'], values['-END_SECOND-'] ), "schedule": config.get('schedule', {}), "stay_duration": config.get('stay_duration', 60) } try: with open(CONFIG_FILE, 'w') as f: json.dump(new_config, f, indent=4, ensure_ascii=False) sg.popup('配置保存成功!', title='保存结果') except Exception as e: sg.popup_error(f'保存失败: {str(e)}') # 运行自动化 if event == '运行': print("启动浏览器自动化...") threading.Thread( target=browser_automation, args=(values, current_order), daemon=True ).start() # 打开设置窗口 if event == '设置': show_settings_window(window) # 定时设置 if event == '-SCHEDULE-': show_schedule_settings(window, config) # 清理资源 window.close() if SCHEDULER and SCHEDULER.running: SCHEDULER.shutdown() def calculate_dynamic_date(preset): if not preset or preset == "--": return None week_type, weekday_str = preset[:1], preset[1:] week_offset = -1 if week_type == "上" else 0 try: weekday_index = WEEKDAYS.index(weekday_str) except ValueError: return None today = datetime.now(BEIJING_TZ) current_week_monday = today - timedelta(days=today.weekday()) target_week_monday = current_week_monday + timedelta(weeks=week_offset) return target_week_monday + timedelta(days=weekday_index) def show_schedule_settings(parent_window, config): schedule = config.get('schedule', {}) def split_preset_time(time_str): if time_str and re.match(r"\d{2}:\d{2}:\d{2}", time_str): return time_str.split(":") return ["00", "00", "00"] start_time_parts = split_preset_time(schedule.get('start_preset_time', "00:00:00")) end_time_parts = split_preset_time(schedule.get('end_preset_time', "00:00:00")) layout = [ [sg.Text('开始日期预设:'), sg.Combo(["--"] + [f"上{day}" for day in WEEKDAYS] + [f"本{day}" for day in WEEKDAYS], default_value=schedule.get('start_preset', '--'), key='-START_PRESET-', enable_events=True, size=(12, 1)), sg.Text("时间:"), sg.Spin([f"{i:02}" for i in range(24)], start_time_parts[0], key='-START_PRESET_H-', size=3), sg.Text(':'), sg.Spin([f"{i:02}" for i in range(60)], start_time_parts[1], key='-START_PRESET_M-', size=3), sg.Text(':'), sg.Spin([f"{i:02}" for i in range(60)], start_time_parts[2], key='-START_PRESET_S-', size=3), sg.Text("实际日期:", size=(10, 1)), sg.Text("", key='-REAL_START-', size=15)], [sg.Text('结束日期预设:'), sg.Combo(["--"] + [f"上{day}" for day in WEEKDAYS] + [f"本{day}" for day in WEEKDAYS], default_value=schedule.get('end_preset', '--'), key='-END_PRESET-', enable_events=True, size=(12, 1)), sg.Text("时间:"), sg.Spin([f"{i:02}" for i in range(24)], end_time_parts[0], key='-END_PRESET_H-', size=3), sg.Text(':'), sg.Spin([f"{i:02}" for i in range(60)], end_time_parts[1], key='-END_PRESET_M-', size=3), sg.Text(':'), sg.Spin([f"{i:02}" for i in range(60)], end_time_parts[2], key='-END_PRESET_S-', size=3), sg.Text("实际日期:", size=(10, 1)), sg.Text("", key='-REAL_END-', size=15)], [sg.Text('定时执行:'), sg.Combo(["--"] + WEEKDAYS, default_value=schedule.get('schedule_type', '--'), key='-SCHEDULE_TYPE-', size=(8, 1)), sg.Input(schedule.get('schedule_time', '00:00:00'), key='-SCHEDULE_TIME-', size=(8, 1), tooltip="格式: HH:MM:SS")], [sg.Button('保存'), sg.Button('清除定时'), sg.Button('取消')], ] window = sg.Window('定时设置', layout, finalize=True) def update_dates(): start_date = calculate_dynamic_date(window['-START_PRESET-'].get()) end_date = calculate_dynamic_date(window['-END_PRESET-'].get()) window['-REAL_START-'].update(start_date.strftime('%Y-%m-%d') if start_date else "") window['-REAL_END-'].update(end_date.strftime('%Y-%m-%d') if end_date else "") update_dates() while True: event, values = window.read(timeout=500) if event in (None, '取消'): break if event == '清除定时': window['-START_PRESET-'].update('--') window['-END_PRESET-'].update('--') window['-SCHEDULE_TYPE-'].update('--') window['-SCHEDULE_TIME-'].update('00:00:00') window['-REAL_START-'].update('') window['-REAL_END-'].update('') config['schedule'] = { "start_preset": "--", "end_preset": "--", "schedule_type": "--", "schedule_time": "00:00:00" } with open(CONFIG_FILE, 'w') as f: json.dump(config, f, ensure_ascii=False) global SCHEDULER if SCHEDULER and SCHEDULER.running: SCHEDULER.shutdown() SCHEDULER = None sg.popup("定时设置已重置") if event in ('-START_PRESET-', '-END_PRESET-', '__TIMEOUT__'): update_dates() if event == '保存': try: start_h = int(values['-START_PRESET_H-']) start_m = int(values['-START_PRESET_M-']) start_s = int(values['-START_PRESET_S-']) end_h = int(values['-END_PRESET_H-']) end_m = int(values['-END_PRESET_M-']) end_s = int(values['-END_PRESET_S-']) if not (0 <= start_h <= 23 and 0 <= end_h <= 23): raise ValueError("小时需在00-23之间") if not (0 <= start_m <= 59 and 0 <= end_m <= 59): raise ValueError("分钟需在00-59之间") if not (0 <= start_s <= 59 and 0 <= end_s <= 59): raise ValueError("秒数需在00-59之间") except ValueError as e: sg.popup_error(f"时间输入错误: {str(e)}") continue config['schedule']['start_preset_time'] = f"{values['-START_PRESET_H-']}:{values['-START_PRESET_M-']}:{values['-START_PRESET_S-']}" config['schedule']['end_preset_time'] = f"{values['-END_PRESET_H-']}:{values['-END_PRESET_M-']}:{values['-END_PRESET_S-']}" if values['-START_PRESET-'] != "--": parent_window['-START_HOUR-'].update(values['-START_PRESET_H-']) parent_window['-START_MINUTE-'].update(values['-START_PRESET_M-']) parent_window['-START_SECOND-'].update(values['-START_PRESET_S-']) if values['-END_PRESET-'] != "--": parent_window['-END_HOUR-'].update(values['-END_PRESET_H-']) parent_window['-END_MINUTE-'].update(values['-END_PRESET_M-']) parent_window['-END_SECOND-'].update(values['-END_PRESET_S-']) if not re.match(r'^([0-1]\d|2[0-3]):[0-5]\d:[0-5]\d$', values['-SCHEDULE_TIME-']): sg.popup_error("时间格式应为HH:MM:SS") continue start_preset_val = values['-START_PRESET-'] end_preset_val = values['-END_PRESET-'] calculated_start = calculate_dynamic_date(start_preset_val) calculated_end = calculate_dynamic_date(end_preset_val) if start_preset_val != "--" and calculated_start: date_part = calculated_start.strftime('%Y-%m-%d') # 更新主窗口的开始日期输入框 parent_window['-START_DATE-'].update(date_part) # 同时更新配置中的日期部分 config[ 'start_date'] = f"{date_part} {values['-START_PRESET_H-']}:{values['-START_PRESET_M-']}:{values['-START_PRESET_S-']}" if end_preset_val != "--" and calculated_end: date_part = calculated_end.strftime('%Y-%m-%d') # 更新主窗口的结束日期输入框 parent_window['-END_DATE-'].update(date_part) # 同时更新配置中的日期部分 config[ 'end_date'] = f"{date_part} {values['-END_PRESET_H-']}:{values['-END_PRESET_M-']}:{values['-END_PRESET_S-']}" config['schedule'] = { "start_preset": start_preset_val, "end_preset": end_preset_val, "schedule_type": values['-SCHEDULE_TYPE-'], "schedule_time": values['-SCHEDULE_TIME-'] } with open(CONFIG_FILE, 'w') as f: json.dump(config, f, ensure_ascii=False) setup_scheduler(parent_window) sg.popup("定时设置已保存!") window.close() def setup_scheduler(parent_window): global SCHEDULER if SCHEDULER and SCHEDULER.running: SCHEDULER.shutdown() config = load_config() schedule = config.get('schedule', {}) if schedule.get('schedule_type') == "--" or not schedule.get('schedule_time'): return SCHEDULER = BackgroundScheduler(timezone=BEIJING_TZ) def scheduled_task(): try: now = datetime.now(BEIJING_TZ) print(f"\n[{now.strftime('%Y-%m-%d %H:%M:%S')}] 定时任务启动") parent_window.write_event_value('执行定时任务', None) except Exception as e: print(f"定时任务异常: {str(e)}") try: h, m, s = map(int, schedule['schedule_time'].split(':')) weekday_index = WEEKDAYS.index(schedule['schedule_type']) SCHEDULER.add_job( scheduled_task, 'cron', day_of_week=weekday_index, hour=h, minute=m, second=s, misfire_grace_time=60 ) SCHEDULER.start() next_run = SCHEDULER.get_jobs()[0].next_run_time.astimezone(BEIJING_TZ) print(f"定时任务已激活,每周{WEEKDAYS[weekday_index]} {schedule['schedule_time']} 执行") print(f"下次执行时间: {next_run.strftime('%Y-%m-%d %H:%M:%S')}") except Exception as e: sg.popup_error(f"定时设置错误: {str(e)}") def browser_automation(values, current_order): try: if current_order is None: config = load_config() current_order = config["order"] config = load_config() STAY_DURATION = config.get('stay_duration', 60) schedule = config.get('schedule', {}) start_date = values['-START_DATE-'] start_time = f"{values['-START_HOUR-']}:{values['-START_MINUTE-']}:{values['-START_SECOND-']}" full_start = f"{start_date} {start_time}" if start_date else "" end_date = values['-END_DATE-'] end_time = f"{values['-END_HOUR-']}:{values['-END_MINUTE-']}:{values['-END_SECOND-']}" full_end = f"{end_date} {end_time}" if end_date else "" if not start_date or not end_date: print("错误:日期预设配置无效") return print(f"当前日期范围: {full_start} 至 {full_end}") service = EdgeService(r"C:\Program Files (x86)\Microsoft\Edge\Application\msedgedriver.exe") options = EdgeOptions() options.use_chromium = True driver = webdriver.Edge(service=service, options=options) modified_values = values.copy() modified_values['-FULL_START-'] = full_start modified_values['-FULL_END-'] = full_end driver.get(values['-URL-']) time.sleep(2) for label in current_order: element_config = config['elements'].get(label, {}) if element_config and element_config.get('identifier'): handle_element(driver, label, element_config, modified_values) time.sleep(0.5) print("自动化操作成功完成!") print(f"网页将保持打开状态{STAY_DURATION}秒...") time.sleep(STAY_DURATION) except Exception as e: print(f"执行过程中发生错误: {str(e)}") if 'driver' in locals(): driver.save_screenshot(f'error_{int(time.time())}.png') finally: if 'driver' in locals(): driver.quit() print("浏览器已关闭") def handle_element(driver, label, element_config, config): element_type = ELEMENT_TYPES[element_config['type']] identifier = element_config['identifier'] clean_identifier = identifier.replace(' ', '') schedule_config = config start_identifier = schedule_config.get('start_identifier', '开始时间') end_identifier = schedule_config.get('end_identifier', '结束时间') try: if element_type == 'button': xpath = f"//div[translate(normalize-space(), ' ', '') = '{clean_identifier}']" element = WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, xpath))) driver.execute_script("arguments[0].scrollIntoView({block: 'center'});", element) driver.execute_script("arguments[0].click();", element) elif element_type == 'checkbox': xpath = f"//span[translate(normalize-space(), ' ', '')='{clean_identifier}']/preceding-sibling::input[@type='checkbox']" checkbox = WebDriverWait(driver, 20).until(EC.presence_of_element_located((By.XPATH, xpath))) if not checkbox.is_selected(): driver.execute_script("arguments[0].click();", checkbox) elif element_type == 'radio': xpath = f"//input[@type='radio']/following-sibling::text()[contains(., '{identifier}')]/preceding::input[1]" radio = WebDriverWait(driver, 20).until(EC.presence_of_element_located((By.XPATH, xpath))) driver.execute_script("arguments[0].click();", radio) elif element_type == 'dropdown': xpath = f"//span[translate(normalize-space(), ' ', '')='{clean_identifier}']/following::select[1]" select_element = WebDriverWait(driver, 20).until(EC.presence_of_element_located((By.XPATH, xpath))) Select(select_element).select_by_index(0) elif element_type == 'tab': xpath = f"//div[contains(@class,'tab')]/span[translate(normalize-space(), ' ', '')='{clean_identifier}']" tab = WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, xpath))) driver.execute_script("arguments[0].click();", tab) time.sleep(1) elif element_type == 'time': xpath = f"//span[translate(normalize-space(), ' ', '')='{clean_identifier}']/following::input[1]" field = WebDriverWait(driver, 20).until(EC.presence_of_element_located((By.XPATH, xpath))) schedule_config = config.get('schedule', {}) start_id = schedule_config.get('start_identifier', '开始时间') end_id = schedule_config.get('end_identifier', '结束时间') if element_config['identifier'] == start_id: full_value = config.get('-FULL_START-', '') elif element_config['identifier'] == end_id: full_value = config.get('-FULL_END-', '') else: print(f"未配置的时间字段: {element_config['identifier']}") return attempts = [] if full_value: attempts.append(full_value) if ' ' in full_value: attempts.append(full_value.split(' ')[0]) attempts.append(full_value.split(' ')[1]) success = False for attempt in attempts: try: field.clear() field.send_keys(attempt) time.sleep(0.5) current_value = field.get_attribute('value') if current_value.strip() == attempt.strip(): success = True break except Exception as e: print(f"尝试输入 '{attempt}' 失败: {str(e)}") continue if not success: raise ValueError(f"无法输入日期时间: {full_value}") elif element_type == 'text': try: # 查找包含标识文本的元素 base_element = WebDriverWait(driver, 20).until( EC.presence_of_element_located( (By.XPATH, f"//*[contains(text(), '{identifier}')]")) ) # 查找最近的输入框(前、后或父级相邻) input_element = base_element.find_element(By.XPATH, "./following-sibling::input | " + "./preceding-sibling::input | " + "../following-sibling::input | " + "ancestor::div/following-sibling::input" ) # 清空并输入文本 input_element.clear() input_element.send_keys(element_config.get('text', '')) print(f"成功输入文本: {element_config['text']}") except Exception as e: print(f"查找输入框失败: {str(e)}") raise return print(f"成功处理: {label}") except Exception as e: print(f"处理 {label} 时出错: {str(e)}") raise if __name__ == "__main__": create_gui() 以上代码的删除功能键点击无效,点击上下移动界面闪退,修改
05-13
using Mds.Contracts.Customization; using Mds.Customization.Entities; using Mds.Customization.Repositories; using Mds.Enums.Customization; using Mds.Extention; using Mds.Master.Customer; using Mds.Master.Entities; using Mds.Som.ChargePriceAdjust; using Mds.Som.ChargePriceAdjustItem; using Mds.Som.Entities; using Mds.Toolkit; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using Minio.DataModel.Notification; using Modern.Toolkits.Dtos; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Net.Http; using System.Text.Json; using System.Text.RegularExpressions; using System.Threading.Tasks; using Volo.Abp; using Volo.Abp.Application.Dtos; using Volo.Abp.Domain.Entities; using Volo.Abp.Domain.Repositories; namespace Mds.Customization { //[Authorize] public class CustSearchAppService : MdsCustCrudAppService <SearchEntity, SearchDto, Dictionary<string, object>, Guid, PagedAndFilterResultRequestDto, CreateOrUpdateSearchDto, CreateOrUpdateSearchDto>, ICustSearchAppService { private readonly ILogger<CustSearchAppService> _logger; private readonly ISearchRepository _searchRepository; private readonly IRecordTypeRepository _recordTypeRepository; private readonly ICustRecordTypeFieldAppService _recordTypeFieldAppService; private readonly IRecordTypeFieldFilterSourceListRepository _recordFieldFilterSourceListRepository; private readonly ISearchAvailableFilterRepository _searchAvailableFilterRepository; private readonly ISearchCriteriaRepository _searchCriteriaRepository; private readonly ISearchResultRepository _searchResultRepository; private readonly IRecordTypeFieldRepository _recordTypeFieldRepository; public CustSearchAppService( IHttpClientFactory httpClientFactory, IRepository<SearchEntity, Guid> repository, ILogger<CustSearchAppService> logger, IHttpContextAccessor httpContextAccessor, ISearchRepository searchRepository, ISearchCriteriaRepository searchCriteriaRepository, ISearchAvailableFilterRepository searchAvailableFilterRepository, IRecordTypeRepository recordTypeRepository, IRecordTypeFieldRepository recordFieldRepository, IFieldRepository fieldRepository, ICustFieldAppService fieldAppService, ICustRecordTypeFieldAppService recordTypeFieldAppService, IRecordTypeFieldFilterSourceListRepository recordTypeFieldFilterSourceListRepository , ISearchResultRepository searchResultRepository, IRecordTypeFieldRepository recordTypeFieldRepository) : base(repository) { _logger = logger; _searchRepository = searchRepository; _recordTypeRepository = recordTypeRepository; _recordTypeFieldAppService = recordTypeFieldAppService; _recordFieldFilterSourceListRepository = recordTypeFieldFilterSourceListRepository; _searchAvailableFilterRepository = searchAvailableFilterRepository; _searchCriteriaRepository = searchCriteriaRepository; _searchResultRepository = searchResultRepository; _recordTypeFieldRepository = recordTypeFieldRepository; } public async override Task<SearchDto> GetAsync(Guid id) { var entity = await _searchRepository.FindAsync(id); //.GetAsync(id); if (entity == null) { var recordType = await _recordTypeRepository.GetAsync(id); entity = ObjectMapper.Map<RecordTypeEntity, SearchEntity>(recordType); } var dto = ObjectMapper.Map<SearchEntity, SearchDto>(entity); var fields = await _GetFieldDtosAsync(entity); foreach (var item in dto.SearchResults) { var field = fields.FirstOrDefault(x => x.Id == item.FieldId); item.FieldCustomId = field.CustomId; item.FieldName = field.Name; item.field = field; if (item.LinkFieldId.HasValue && item.LinkFieldId != Guid.Empty) { var linkField = fields.FirstOrDefault(x => x.Id == item.LinkFieldId); item.FieldCustomId = field.CustomId + "." + linkField.CustomId; item.FieldName = linkField.Name; item.field = Newtonsoft.Json.JsonConvert.DeserializeObject<RecordTypeFieldDto>(Newtonsoft.Json.JsonConvert.SerializeObject(linkField)); item.field.FieldCustomId = item.FieldCustomId; item.field.Name = item.FieldName; } item.RecordTypeId = entity.RecordTypeId; } var searchAvailableFilterDtos = await _GetSearchAvailableFilterDtos(fields, entity); dto.SearchAvailableFilters = searchAvailableFilterDtos; foreach (var item in dto.SearchAvailableFilters) { item.RecordTypeId = entity.RecordTypeId; } foreach (var item in dto.SearchCriterias) { item.RecordTypeId = entity.RecordTypeId; Guid filter = new Guid(); if (Guid.TryParse(item.Filter, out filter)) { var fieldInfo = fields.FirstOrDefault(o => o.Id == Guid.Parse(item.Filter)); item.FilterOptionLabelFields = fieldInfo.OptionLabelFields; item.FilterFieldListOrRecordTypeId = fieldInfo.FieldListOrRecordTypeId; } } return dto; } public async Task<object> GetSearchResultsAsync(Guid id, FilterSearchResultRequestDto input) { var search = await _searchRepository.FindAsync(id); RecordTypeEntity recordType = null; if (search == null) { recordType = await _recordTypeRepository.GetAsync(id); search = ObjectMapper.Map<RecordTypeEntity, SearchEntity>(recordType); } if (recordType == null) recordType = await _recordTypeRepository.GetAsync(search.RecordTypeId); var fieldDtos = await _GetFieldDtosAsync(search); var searchResultDtos = await _GetSearchResultDto(fieldDtos, search); var methdorNameUri = ReplaceMethodNamePlaceholders(recordType.MethodName, input); input.Filter = await _GetFilterAsync(input, search, recordType, searchResultDtos); var result = await _fetchRemoteDataAsync(recordType, input, methdorNameUri); List<dynamic> resultItems = result.Items; // CamelCaseHelper.ToCamelCaseList(result.Items); if (recordType.IsList) { List<ListLineDto> dyList = new List<ListLineDto>(); foreach (var item in resultItems) { JObject jObject = JObject.FromObject(item); var dto = jObject.ToObject<ListLineDto>(); dyList.Add(dto); } var pageResult = new PagedResultDto<ListLineDto>() { TotalCount = result.TotalCount, Items = dyList }; return pageResult; } ProvideAdditionalInfo(resultItems, searchResultDtos, 0); if (recordType.CustomId == "history") await RepalceHistoryValueAsync(resultItems, input.RecordTypeId.Value); if (recordType.CustomId == "record-type-field-filter-source-list") await ProvidefilterSourceListInfoAsync(resultItems,searchResultDtos); List<object> list = new List<object>(); // 1. 构建对象 var propertyTypes = GetPropertyTypes(searchResultDtos); var dyResult = ReflectionHelper.CreateDynamicType("dyResult", propertyTypes); foreach (var item in resultItems) { var newdy = Activator.CreateInstance(dyResult); var properties = dyResult.GetProperties(); foreach (var property in properties) { var val = item[property.Name] ?? item[ToPascalCase(property.Name)]; ReflectionHelper.SetPropertyValue(newdy, property.Name, val); } list.Add(newdy); } result.Items = list; return new PagedResultDto<dynamic>() { TotalCount = result.TotalCount, Items = result.Items }; } private static string ToPascalCase(string camelCase) { if (string.IsNullOrEmpty(camelCase)) return camelCase; return char.ToUpper(camelCase[0]) + camelCase.Substring(1); } public async Task<List<object>> ComplementAdditionalInfo(Guid id, List<dynamic> items) { var search = await _searchRepository.FindAsync(id); if (search == null) { var recordType = await _recordTypeRepository.GetAsync(id); search = ObjectMapper.Map<RecordTypeEntity, SearchEntity>(recordType); } var fieldDtos = await _GetFieldDtosAsync(search); var searchResultDtos = await _GetSearchResultDto(fieldDtos, search); ProvideAdditionalInfo(items, searchResultDtos, 0); List<object> list = new List<object>(); // 1. 构建对象 var propertyTypes = GetPropertyTypes(searchResultDtos); var dyResult = ReflectionHelper.CreateDynamicType("dyResult", propertyTypes); foreach (var item in items) { var newdy = Activator.CreateInstance(dyResult); var properties = dyResult.GetProperties(); foreach (var property in properties) { var val = item[property.Name]; ReflectionHelper.SetPropertyValue(newdy, property.Name, val); } list.Add(newdy); } return list; } public async Task<SearchDto> GetSearchResultsByCustomIdAsync(string customId) { //根据customId先取recordType var entity = await _recordTypeRepository.GetAsync(p => p.CustomId == customId); if (entity == null) { throw new UserFriendlyException(string.Format("未找到对应的RecodType")); } var result = await GetAsync(entity.Id); return result; } #region 私有方法 protected async override Task DeleteBeforeCheckAsync(Guid id) { var entity = await _searchRepository.GetAsync(id); if (entity.IsStandard) throw new BusinessException(MdsDomainErrorCodes.IsStandardDataNotCanDelete); } private async Task<AllResultDto<dynamic>> _fetchRemoteDataAsync(RecordTypeEntity recordType, FilterSearchResultRequestDto input, string methodName) // Search search, { var pageResult = new AllResultDto<dynamic>(); SetDefaultSorting(input, recordType); string serviceModule = recordType.ServiceModule; string recordTypeServiceName = recordType.ServiceName; //.EntityName; int maxResultCount = input.MaxResultCount == 1 ? 1000 : input.MaxResultCount; string strParams = string.Format("?Sorting={0}&SkipCount={1}&MaxResultCount={2}&IsInActive={3}&Filter={4}", input.Sorting, input.SkipCount, maxResultCount, input.IsInActive, input.Filter); var result = await GetDaprInvokeResultAsync<PagedResultDto<dynamic>>(HttpMethod.Get, recordType.DaprAppId, serviceModule, recordTypeServiceName, methodName, strParams); pageResult.TotalCount = result.TotalCount; pageResult.Items = result.Items.ToList(); if (input.MaxResultCount == 1) { var pageCount = Math.Ceiling((double)pageResult.TotalCount / maxResultCount); for (int i = 1; i < pageCount; i++) { int skipCount = 1000 * i; string strParams1 = string.Format("?Sorting={0}&SkipCount={1}&MaxResultCount={2}&IsInActive={3}&Filter={4}", input.Sorting, skipCount, maxResultCount, input.IsInActive, input.Filter); var result1 = await GetDaprInvokeResultAsync<PagedResultDto<dynamic>>(HttpMethod.Get, recordType.DaprAppId, serviceModule, recordType.ServiceName, methodName, strParams1); // .EntityName pageResult.Items.AddRange(result1.Items); } } return pageResult; } private static void SetDefaultSorting(FilterSearchResultRequestDto input, RecordTypeEntity recordType) { var hasInputSort = !string.IsNullOrWhiteSpace(input.Sorting) && recordType.RecordTypeFields.Any(f => f.FieldCustomId == input.Sorting.Split(" ")[0]); if (hasInputSort) return; if (recordType.IsList) { input.Sorting = "sort"; return; } var hasSort = recordType.RecordTypeFields.Any(f => f.FieldCustomId == "sort"); input.Sorting = hasSort ? "sort" : "id"; } private async Task RepalceHistoryValueAsync(List<dynamic> items, Guid recordTypeId) { var recordType = await _recordTypeRepository.GetAsync(recordTypeId); items.ForEach(item => { string propertyName = Convert.ToString(item["propertyName"]); var field = recordType.RecordTypeFields.Find(p => p.FieldCustomId.ToLower() == propertyName.ToLower()); if (field != null) { item["propertyName"] = field.Name; } }); } private async Task ProvidefilterSourceListInfoAsync(List<dynamic> items, List<SearchResultDto> dicSearchResult) { List<Guid> filterUsingFieldIds = new List<Guid>(); items.ForEach(item => { string filterUsing = Convert.ToString(item["filterUsingId"]); var id = filterUsing.Split('.').LastOrDefault(); if (id != null) filterUsingFieldIds.Add(Guid.Parse(id)); }); var fieldDtos = await _recordTypeFieldAppService.GetListByIdsAsync(filterUsingFieldIds.Distinct().ToList()); var listOrRecordTypeFieldDtos = fieldDtos.Where(p => p.FieldViewType == FieldViewType._13_ListOrRecordType).ToList(); foreach (var item in items) { string filterUsing = Convert.ToString(item["filterUsingId"]); var id = filterUsing.Split('.').LastOrDefault(); if (id == null) return; var fieldDto = listOrRecordTypeFieldDtos.FirstOrDefault(p => p.Id.ToString() == id); if (fieldDto == null) { item["value1_text"] = item["value1"]; item["value2_text"] = item["value2"]; } else if (!string.IsNullOrWhiteSpace(Convert.ToString(item["value1"]))) { string v1 = Convert.ToString(item["value1"]); string v2 = Convert.ToString(item["value2"]); var ids = new List<object> { v1, v2 }; var entities = await _GetEntitiesByIdsAsync(ids, fieldDto); var entityV1 = entities.FirstOrDefault(p => p.id == v1 || p.value == v1); if (entityV1 != null) { item["value1_text"] = GetFieldCustomIdText(entityV1, "[\"name\"]"); // "name" } var entityV2 = entities.FirstOrDefault(p => p.id == v2 || p.value == v2); if (entityV2 != null) { item["value2_text"] = GetFieldCustomIdText(entityV2, "[\"name\"]"); } } } } private static string ReplaceMethodNamePlaceholders(string methodName, FilterSearchResultRequestDto input) { if (string.IsNullOrWhiteSpace(methodName)) return null; Dictionary<string, string> placeholders = new Dictionary<string, string>(); if (!string.IsNullOrWhiteSpace(input.Filter) && input.Filter != "{}" && input.Filter != "[]") { var inputFilter = Newtonsoft.Json.Linq.JObject.Parse(input.Filter); foreach (var property in inputFilter.Properties()) { string propertyName = property.Name; string propertyValue = property.Value.ToString(); placeholders[propertyName] = propertyValue; } } // 使用正则表达式查找所有被大括号包围的占位符 var newMethodName = Regex.Replace(methodName, @"\{([^}]+)\}", match => { // 获取大括号内的内容(占位符) string placeholder = match.Groups[1].Value; // 尝试从字典中获取替换值 // 如果字典中存在该占位符,则返回替换值 // 如果不存在,则返回原始匹配(即保留大括号和占位符内容) if (placeholders.ContainsKey(placeholder)) { var val = placeholders[placeholder]; placeholders.Remove(placeholder); return val; } return match.Value; }); // 检查替换后的字符串中是否还有剩余的占位符 var matches = Regex.Matches(newMethodName, @"\{([^}]+)\}"); var remainingPlaceholders = matches.Cast<Match>().Select(m => m.Groups[1].Value).ToList(); if (remainingPlaceholders.Any()) throw new UserFriendlyException(string.Format("方法{0}中,缺少参数{1}", methodName, string.Join(",", remainingPlaceholders))); input.Filter = Newtonsoft.Json.JsonConvert.SerializeObject(placeholders); return newMethodName; } private async Task<string> _GetFilterAsync(FilterSearchResultRequestDto input, SearchEntity search, RecordTypeEntity recordType, List<SearchResultDto> searchResultDtos) { List<FilterModel> filters = new List<FilterModel>(); if (!string.IsNullOrWhiteSpace(input.Filter) && input.Filter != "{}" && input.Filter != "[]") { var inputFilter = Newtonsoft.Json.Linq.JObject.Parse(input.Filter); // 遍历对象的所有属性 // 1. 添加动态数据过滤 // 1.1 当属性里有Filter_RecordTypeFieldId(特定字段,根据id查找RecordTypeFieldFilterSourceListDto 列表)构建过滤条件; // 1.2 comparedField 使用参数传过来的值,替换RecordTypeFieldFilterSourceListDto中CompareField的值; // 1.3 构建过滤条件,并合并其他用户动态传递过来的参数 var hasFilterField = inputFilter.Properties().Any(p => p.Name == "filter_RecordTypeFieldId"); if (hasFilterField) { var recordTypeFieldId = inputFilter["filter_RecordTypeFieldId"].ToString(); var filterSourceList = await _recordFieldFilterSourceListRepository.GetListAsync(p => p.RecordTypeFieldId == Guid.Parse(recordTypeFieldId)); var filterSourceListDtos = ObjectMapper.Map<List<RecordTypeFieldFilterSourceListEntity>, List<RecordTypeFieldFilterSourceListDto>>(filterSourceList); filterSourceListDtos = filterSourceListDtos.OrderBy(p => p.Line).ToList(); filterSourceListDtos.ForEach(line => { if (line.CompareToField.HasValue) { var compareValue = inputFilter["filter_" + line.FilterUsing].ToString(); line.Value1 = compareValue.StartsWith("opt_") ? compareValue.Substring(compareValue.Split(" ")[0].Length + 1) : compareValue; if (line.Value1 == "undefined" || line.Value1 == "null") line.Value1 = Guid.Empty.ToString(); inputFilter.Remove("filter_" + line.FilterUsing); } }); var filterList1 = ObjectMapper.Map<List<RecordTypeFieldFilterSourceListDto>, List<FilterModel>>(filterSourceListDtos); filterList1[filterList1.Count - 1].LogicalOperator = LogicalOperator.And; filters = filterList1; inputFilter.Remove("filter_RecordTypeFieldId"); } foreach (var property in inputFilter.Properties()) { string propertyName = property.Name; // object propertyValue = property.Value; string propertyValue = property.Value.ToString(); if (string.IsNullOrWhiteSpace(propertyValue)) continue; int optType = propertyValue.StartsWith("opt_") ? Convert.ToInt32(propertyValue.Substring(4, propertyValue.Split(" ")[0].Length - 4)) : (int)OperatorType.Equal; propertyValue = propertyValue.StartsWith("opt_") ? propertyValue.Substring(propertyValue.Split(" ")[0].Length + 1) : propertyValue; string propertyValue2 = string.Empty; OperatorType operatorType = (OperatorType)optType; if (operatorType == OperatorType.Between) { var values = JsonSerializer.Deserialize<List<string>>(propertyValue); propertyValue = values[0]; propertyValue2 = values[1]; if (!string.IsNullOrWhiteSpace(propertyValue) && !string.IsNullOrWhiteSpace(propertyValue2)) { FilterModel filter1 = new FilterModel(); filter1.Not = false; filter1.OpenParens = 1; filter1.FilterUsing = propertyName; filter1.OperatorType = OperatorType.GreaterThanOrEqual; filter1.Value1 = propertyValue.ToString(); filter1.Value2 = string.Empty; filter1.CloseParens = 0; filter1.LogicalOperator = LogicalOperator.And; filter1.Line = -1; filters.Add(filter1); FilterModel filter2 = new FilterModel(); filter2.Not = false; filter2.OpenParens = 1; filter2.FilterUsing = propertyName; filter2.OperatorType = OperatorType.LessThanOrEqual; filter2.Value1 = propertyValue2.ToString(); filter2.Value2 = string.Empty; filter2.CloseParens = 0; filter2.LogicalOperator = LogicalOperator.And; filter2.Line = -1; filters.Add(filter2); } else if (string.IsNullOrWhiteSpace(propertyValue) && !string.IsNullOrWhiteSpace(propertyValue2)) { FilterModel filter2 = new FilterModel(); filter2.Not = false; filter2.OpenParens = 0; filter2.FilterUsing = propertyName; filter2.OperatorType = OperatorType.LessThanOrEqual; filter2.Value1 = propertyValue2.ToString(); filter2.Value2 = string.Empty; filter2.CloseParens = 0; filter2.LogicalOperator = LogicalOperator.And; filter2.Line = -1; filters.Add(filter2); } else if (!string.IsNullOrWhiteSpace(propertyValue) && string.IsNullOrWhiteSpace(propertyValue2)) { FilterModel filter1 = new FilterModel(); filter1.Not = false; filter1.OpenParens = 0; filter1.FilterUsing = propertyName; filter1.OperatorType = OperatorType.GreaterThanOrEqual; filter1.Value1 = propertyValue.ToString(); filter1.Value2 = string.Empty; filter1.CloseParens = 0; filter1.LogicalOperator = LogicalOperator.And; filter1.Line = -1; filters.Add(filter1); } } else if (propertyName.ToLower() == "keywords") { if (string.IsNullOrWhiteSpace(propertyValue) || propertyValue == "null") continue; var kvFilters = GetKeyWordsFilterModels(propertyValue, searchResultDtos); filters.AddRange(kvFilters); } else { FilterModel filter = new FilterModel(); filter.Not = false; filter.OpenParens = 0; filter.FilterUsing = propertyName; filter.OperatorType = (OperatorType)optType; // OperatorType.Equal; filter.Value1 = propertyValue.ToString(); filter.Value2 = propertyValue2; filter.LogicalOperator = LogicalOperator.And; filter.CloseParens = 0; filter.Line = -1; filters.Add(filter); } } } if (recordType.IsList) { FilterModel filter = new FilterModel(); filter.Not = false; filter.OpenParens = 0; filter.FilterUsing = "RecordTypeId"; filter.OperatorType = OperatorType.Equal; filter.Value1 = recordType.Id.ToString(); filter.Value2 = string.Empty; filter.LogicalOperator = LogicalOperator.And; filter.CloseParens = 0; filter.Line = -1; filters.Add(filter); } if (search.SearchCriterias != null && search.SearchCriterias.Count > 0) { // 子查询处理 foreach (var item in search.SearchCriterias) { FilterModel filterModel = new FilterModel(); filterModel.Not = item.Not; filterModel.OpenParens = item.OpenParens; filterModel.FilterUsing = item.Filter; filterModel.OperatorType = item.OperatorType; filterModel.Value1 = item.Value1; filterModel.Value2 = item.Value2; filterModel.LogicalOperator = item.LogicalOperator; filterModel.CloseParens = item.CloseParens; filterModel.Line = item.Line; filters.Add(filterModel); } await _ResetSubScopeFilterAsync(recordType, filters); } for (int i = 0; i < filters.Count; i++) { filters[i].Line = i; } var strFilter = filters.Count > 0 ? JsonSerializer.Serialize(filters) : string.Empty; return strFilter; } public async Task ProvideAdditionalInfo(List<dynamic> items, List<SearchResultDto> dicSearchResult, int level) { if (level > 5 || items.Count == 0) return; List<string> containCustomIdTexts = new List<string>(); var textSearchResult = dicSearchResult.Where(p => !string.IsNullOrWhiteSpace(p.FieldCustomId) && p.FieldCustomId.Contains("_text")).ToList(); if (textSearchResult.Count > 0) { containCustomIdTexts = textSearchResult.Select(p => p.FieldCustomId).ToList(); } var searchResultDtos = dicSearchResult.Where(p => (p.field.FieldViewType == FieldViewType._13_ListOrRecordType || p.field.FieldViewType == FieldViewType._23_Dynamic) && !containCustomIdTexts.Contains(p.FieldCustomId + "_text")).ToList(); if (searchResultDtos.Count == 0) return; foreach (var searchResultDto in searchResultDtos) { var fieldDto = searchResultDto.field; if (fieldDto.FieldViewType == FieldViewType._23_Dynamic) // 动态字段类型,其text文本,由服务传递,不在这里聚合 { bool flag = dicSearchResult.Any(p => p.FieldCustomId == searchResultDto.FieldCustomId + "_text"); if (!flag) { var searchResultDtoText = Newtonsoft.Json.JsonConvert.DeserializeObject<SearchResultDto>(Newtonsoft.Json.JsonConvert.SerializeObject(searchResultDto)); searchResultDtoText!.FieldCustomId = searchResultDto.FieldCustomId + "_text"; searchResultDtoText.field.FieldViewType = FieldViewType._7_FreeFormText; dicSearchResult.Add(searchResultDtoText); } continue; } if (fieldDto.FieldViewType != FieldViewType._13_ListOrRecordType) continue; //if (fieldDto.CustomId.ToLower() == "filterUsing".ToLower()) //{ // foreach (var item in items) // { // item[searchResultDto.FieldCustomId + "_text"] = GetFieldCustomIdText(item, searchResultDto.FieldCustomId); // item[searchResultDto.FieldCustomId]; // bool flag = dicSearchResult.Any(p => p.FieldCustomId == searchResultDto.FieldCustomId + "_text"); // if (!flag) // { // var searchResultDtoText = Newtonsoft.Json.JsonConvert.DeserializeObject<SearchResultDto>(Newtonsoft.Json.JsonConvert.SerializeObject(searchResultDto)); // searchResultDtoText.FieldCustomId = searchResultDto.FieldCustomId + "_text"; // searchResultDtoText.field.FieldViewType = FieldViewType._7_FreeFormText; // dicSearchResult.Add(searchResultDtoText); // } // } // continue; //} var ids = items.Select(p => p[searchResultDto.FieldCustomId]).Distinct().ToList(); ids.RemoveAll(p => p == null); if (ids.Count == 0) { continue; } var groupedLists = ids .Select((value, index) => new { Index = index, Value = value }) .GroupBy(x => x.Index / 10) .Select(g => g.Select(x => x.Value).ToList()) .ToList(); List<dynamic> resultItemlist = new List<dynamic>(); #region 并行 Task<List<dynamic>>[] taskArray = new Task<List<dynamic>>[groupedLists.Count]; for (var i = 0; i < groupedLists.Count; i++) { var groupIds = groupedLists[i]; taskArray[i] = _GetEntitiesByIdsAsync(groupIds, fieldDto); } // 等待所有任务完成 Task.WaitAll(taskArray); foreach (var task in taskArray) { var resultItems = task.Result; if (resultItems != null) resultItemlist.AddRange(resultItems); } #endregion foreach (var item in items) { string itemid = item[searchResultDto.FieldCustomId]; if (string.IsNullOrWhiteSpace(itemid)) continue; var resultItem = fieldDto.FieldListOrRecordTypeIsList ? resultItemlist.FirstOrDefault(p => p.value == itemid && p.recordTypeId == fieldDto.FieldListOrRecordTypeId) : resultItemlist.FirstOrDefault(p => string.Equals(Convert.ToString(p.id), itemid, StringComparison.OrdinalIgnoreCase)); if (string.IsNullOrWhiteSpace(item[searchResultDto.FieldCustomId + "_text"] as string)) { item[searchResultDto.FieldCustomId + "_text"] = resultItem == null ? "" : GetFieldCustomIdText(resultItem, searchResultDto.field.LabelFields); // resultItem.name; } //item[searchResultDto.FieldCustomId + "_text"] = resultItem == null ? "" : GetFieldCustomIdText(resultItem, searchResultDto.field.LabelFields); // resultItem.name; bool flag = dicSearchResult.Any(p => p.FieldCustomId == searchResultDto.FieldCustomId + "_text"); if (!flag) { var searchResultDtoText = Newtonsoft.Json.JsonConvert.DeserializeObject<SearchResultDto>(Newtonsoft.Json.JsonConvert.SerializeObject(searchResultDto)); searchResultDtoText.FieldCustomId = searchResultDto.FieldCustomId + "_text"; searchResultDtoText.field.FieldViewType = FieldViewType._7_FreeFormText; dicSearchResult.Add(searchResultDtoText); } var searchResults = dicSearchResult.FindAll(p => p.FieldId == fieldDto.Id && p.LinkFieldId != null && p.LinkFieldId != Guid.Empty); foreach (var searchResult in searchResults) { var linkField = searchResult.field; item[searchResult.FieldCustomId] = resultItem[linkField.CustomId]; } } } level++; ProvideAdditionalInfo(items, dicSearchResult, level); } private static string GetFieldCustomIdText(dynamic item, string? fieldName) { if (!string.IsNullOrEmpty(fieldName)) { var array = JArray.Parse(fieldName); fieldName = array[0].ToString(); } else { fieldName = "name"; } if (item is JToken jToken) { JObject? jObject = jToken as JObject; if (jObject == null) { return string.Empty; } foreach (var property in jObject) { if ( (property.Key== fieldName) && property.Value != null && property.Value.Type != JTokenType.Null) { return property.Value.ToString(); } } } return string.Empty; } private static List<FilterModel> GetKeyWordsFilterModels(string propertyValue, List<SearchResultDto> searchResultDtos) { List<FilterModel> filters = new List<FilterModel>(); List<string> keywordFields = new List<string>() { "name", "no", "code", "customId" }; List<string> keywordSearchResultDtos = searchResultDtos.Where(p => p.FieldCustomId.ToLower().EndsWith("name") || p.FieldCustomId.ToLower().EndsWith("no") || p.FieldCustomId.ToLower().EndsWith("code")).Select(p => p.FieldCustomId).ToList(); keywordFields = new HashSet<string>(keywordFields.Concat(keywordSearchResultDtos), StringComparer.OrdinalIgnoreCase).ToList(); for (int i = 0; i < keywordFields.Count; i++) { var item = keywordFields[i]; FilterModel filter = new FilterModel(); filter.Not = false; filter.OpenParens = 0; filter.FilterUsing = item; filter.OperatorType = OperatorType.Contains; filter.Value1 = propertyValue; filter.Value2 = string.Empty; filter.CloseParens = 0; filter.LogicalOperator = LogicalOperator.Or; filter.Line = -1; if (i == 0) filter.OpenParens = 1; if (i == keywordFields.Count - 1) { filter.LogicalOperator = LogicalOperator.And; filter.CloseParens = 1; } filters.Add(filter); } return filters; } private async Task<List<SearchResultDto>> _GetSearchResultDto(List<RecordTypeFieldDto> fieldDtos, SearchEntity search) { var recordType = await _recordTypeRepository.GetAsync(search.RecordTypeId); List<SearchResultDto> dicSearchResult = new List<SearchResultDto>(); foreach (var searchResult in search.SearchResults) { var searchResultDto = ObjectMapper.Map<SearchResultEntity, SearchResultDto>(searchResult); var fieldDto = fieldDtos.Find(p => p.Id == searchResult.FieldId); searchResultDto.FieldCustomId = fieldDto.CustomId; searchResultDto.field = fieldDto; if (fieldDto.FieldViewType != FieldViewType._13_ListOrRecordType) { dicSearchResult.Add(searchResultDto); continue; } if (searchResult.LinkFieldId == null) { var newFieldDto = await _UpdateFieldDtoAsync(recordType, fieldDto); searchResultDto.FieldCustomId = newFieldDto.CustomId; searchResultDto.field = newFieldDto; dicSearchResult.Add(searchResultDto); } else { var linkFieldDto = fieldDtos.Find(p => p.Id == searchResult.LinkFieldId); var newRecordType = await _recordTypeRepository.GetAsync(searchResultDto.field.FieldListOrRecordTypeId.Value); var newFieldDto = await _UpdateFieldDtoAsync(newRecordType, linkFieldDto); searchResultDto.FieldCustomId = string.Format("{0}.{1}", fieldDto.CustomId, newFieldDto.CustomId); searchResultDto.field = newFieldDto; dicSearchResult.Add(searchResultDto); var linkFieldSearchResultDto = dicSearchResult.Any(p => p.FieldId == searchResult.FieldId && (p.LinkFieldId == null || p.LinkFieldId == Guid.Empty)); var linkFieldSearchResult = search.SearchResults.Any(p => p.FieldId.Equals(searchResult.FieldId) && (p.LinkFieldId == null || p.LinkFieldId == Guid.Empty)); if (!linkFieldSearchResultDto && !linkFieldSearchResult) { var linkFieldSearchResultDto1 = Newtonsoft.Json.JsonConvert.DeserializeObject<SearchResultDto>(Newtonsoft.Json.JsonConvert.SerializeObject(searchResultDto)); var fieldDto1 = fieldDtos.Find(p => p.Id == linkFieldSearchResultDto1.FieldId); var newFieldDto1 = await _UpdateFieldDtoAsync(recordType, fieldDto1); linkFieldSearchResultDto1.FieldCustomId = newFieldDto1.CustomId; linkFieldSearchResultDto1.field = newFieldDto1; linkFieldSearchResultDto1.LinkFieldId = null; dicSearchResult.Add(linkFieldSearchResultDto1); } } } return dicSearchResult; } private async Task<List<SearchAvailableFilterDto>> _GetSearchAvailableFilterDtos(List<RecordTypeFieldDto> fieldDtos, SearchEntity search) { var recordType = await _recordTypeRepository.GetAsync(search.RecordTypeId); List<SearchAvailableFilterDto> dicSearchResult = new List<SearchAvailableFilterDto>(); // Dictionary<string, FieldDto>(); foreach (var searchAvailableFilter in search.SearchAvailableFilters) { var searchResultDto = ObjectMapper.Map<SearchAvailableFilterEntity, SearchAvailableFilterDto>(searchAvailableFilter); var fieldDto = fieldDtos.Find(p => p.Id == searchAvailableFilter.FieldId); if (fieldDto == null) continue; searchResultDto.FieldCustomId = fieldDto.CustomId; searchResultDto.Field = fieldDto; if (fieldDto.FieldViewType != FieldViewType._13_ListOrRecordType) { dicSearchResult.Add(searchResultDto); continue; } if (searchAvailableFilter.LinkFieldId == null) { var newFieldDto = await _UpdateFieldDtoAsync(recordType, fieldDto); searchResultDto.FieldCustomId = newFieldDto.CustomId; searchResultDto.Field = newFieldDto; dicSearchResult.Add(searchResultDto); } else { var linkFieldDto = fieldDtos.Find(p => p.Id == searchAvailableFilter.LinkFieldId); var newRecordType = await _recordTypeRepository.GetAsync(searchResultDto.Field.FieldListOrRecordTypeId.Value); var newFieldDto = await _UpdateFieldDtoAsync(newRecordType, linkFieldDto); searchResultDto.FieldCustomId = string.Format("{0}.{1}", fieldDto.CustomId, newFieldDto.CustomId); searchResultDto.Field = newFieldDto; dicSearchResult.Add(searchResultDto); var linkFieldSearchResultDto = dicSearchResult.Any(p => p.FieldId == searchAvailableFilter.FieldId && (p.LinkFieldId == null || p.LinkFieldId == Guid.Empty)); var linkFieldSearchResult = search.SearchResults.Any(p => p.FieldId.Equals(searchAvailableFilter.FieldId) && (p.LinkFieldId == null || p.LinkFieldId == Guid.Empty)); if (!linkFieldSearchResultDto && !linkFieldSearchResult) { var linkFieldSearchResultDto1 = Newtonsoft.Json.JsonConvert.DeserializeObject<SearchAvailableFilterDto>(Newtonsoft.Json.JsonConvert.SerializeObject(searchResultDto)); var fieldDto1 = fieldDtos.Find(p => p.Id == linkFieldSearchResultDto1.FieldId); var newFieldDto1 = await _UpdateFieldDtoAsync(recordType, fieldDto1); linkFieldSearchResultDto1.FieldCustomId = newFieldDto1.CustomId; linkFieldSearchResultDto1.Field = newFieldDto1; linkFieldSearchResultDto1.LinkFieldId = null; dicSearchResult.Add(linkFieldSearchResultDto1); } } } return dicSearchResult; } private async Task<RecordTypeFieldDto> _UpdateFieldDtoAsync(RecordTypeEntity recordType, RecordTypeFieldDto fieldDto) { var copyFieldDto = Newtonsoft.Json.JsonConvert.DeserializeObject<RecordTypeFieldDto>(Newtonsoft.Json.JsonConvert.SerializeObject(fieldDto)); if (copyFieldDto.FieldViewType != FieldViewType._13_ListOrRecordType) return copyFieldDto; var recordField = recordType.RecordTypeFields.Find(p => p.FieldId == fieldDto.Id); if (recordField != null && recordField.FieldListOrRecordTypeId.HasValue) { var fieldRecordType = await _recordTypeRepository.GetAsync(recordField.FieldListOrRecordTypeId.Value, false); copyFieldDto.RecordTypeId = recordField.FieldListOrRecordTypeId.Value; copyFieldDto.FieldListOrRecordTypeCustomId = fieldRecordType.CustomId; copyFieldDto.FieldListOrRecordTypeServiceName = fieldRecordType.ServiceName; copyFieldDto.FieldListOrRecordTypeEntityName = fieldRecordType.EntityName; copyFieldDto.FieldListOrRecordTypeIsList = fieldRecordType.IsList; copyFieldDto.FieldListOrRecordTypeName = fieldRecordType.Name; copyFieldDto.FieldListOrRecordTypeServiceModule = fieldRecordType.ServiceModule; } return copyFieldDto; } private async Task<List<dynamic>> _GetEntitiesByIdsAsync(List<object> ids, RecordTypeFieldDto fieldDto) { if (fieldDto.FieldViewType != FieldViewType._13_ListOrRecordType) return null; List<FilterModel> filters = new List<FilterModel>(); int line = 1; foreach (var id in ids) { if (fieldDto.FieldListOrRecordTypeIsList) { FilterModel filter = new FilterModel(); filter.Not = false; filter.OpenParens = 1; filter.FilterUsing = fieldDto.FieldListOrRecordTypeIsList ? "value" : "Id"; filter.OperatorType = OperatorType.Equal; filter.Value1 = id.ToString(); filter.Value2 = string.Empty; filter.LogicalOperator = LogicalOperator.And; filter.CloseParens = 0; filter.Line = line; filters.Add(filter); FilterModel filter1 = new FilterModel(); filter1.Not = false; filter1.OpenParens = 0; filter1.FilterUsing = "RecordTypeId"; filter1.OperatorType = OperatorType.Equal; filter1.Value1 = fieldDto.FieldListOrRecordTypeId.ToString(); filter1.Value2 = string.Empty; filter1.LogicalOperator = LogicalOperator.Or; filter1.CloseParens = 1; filter1.Line = line + 1; filters.Add(filter1); line = line + 2; } else { FilterModel filter = new FilterModel(); filter.Not = false; filter.OpenParens = 0; filter.FilterUsing = fieldDto.OptionValue; // fieldDto.FieldListOrRecordTypeIsList ? "value" : "Id"; filter.OperatorType = OperatorType.Equal; filter.Value1 = id.ToString(); filter.Value2 = string.Empty; filter.LogicalOperator = LogicalOperator.Or; filter.CloseParens = 0; filter.Line = line; filters.Add(filter); line = line + 1; } } var strFilter = filters.Count > 0 ? JsonSerializer.Serialize(filters) : string.Empty; //string recordTypeEntityName = fieldDto.FieldListOrRecordTypeEntityName; string recordTypeServiceName = fieldDto.FieldListOrRecordTypeServiceName ?? string.Empty; string strParams = string.Format("?SkipCount={0}&MaxResultCount=1000&Filter={1}", 0, strFilter); string methodName = fieldDto.FieldListOrRecordTypeMethodName ?? string.Empty; var result = await GetDaprInvokeResultAsync<PagedResultDto<dynamic>>(HttpMethod.Get, fieldDto.FieldListOrRecordTypeDaprAppId, fieldDto.FieldListOrRecordTypeServiceModule, recordTypeServiceName, methodName, strParams); // Todo: 暂不支持自定义MethodName的recordType return (List<dynamic>)result.Items; } private async Task<List<RecordTypeFieldDto>> _GetFieldDtosAsync(SearchEntity search) { var originFieldIds = search.SearchResults.Select(p => p.FieldId).Distinct().ToList(); var linkFieldIds = search.SearchResults.Where(p => p.LinkFieldId.HasValue).Select(p => p.LinkFieldId.Value).ToList(); var availableFilter = search.SearchAvailableFilters.Select(p => p.FieldId).Distinct().ToList(); var avaiLinkFieldIds = search.SearchAvailableFilters.Where(p => p.LinkFieldId.HasValue).Select(p => p.LinkFieldId.Value).ToList(); var filterFieldIds = search.SearchCriterias.Where(p => !string.IsNullOrEmpty(p.Filter)).Select(p => p.Filter).ToList(); List<Guid> fieldIds = new List<Guid>(); fieldIds.AddRange(originFieldIds); fieldIds.AddRange(linkFieldIds); fieldIds.AddRange(availableFilter); fieldIds.AddRange(avaiLinkFieldIds); foreach (var item in filterFieldIds) { Guid filter = new Guid() ; if (Guid.TryParse(item,out filter)) { fieldIds.Add(filter); } } fieldIds = fieldIds.Distinct().ToList(); var dtos = await _recordTypeFieldAppService.GetListByIdsAsync(fieldIds); return dtos; } private Dictionary<string, Type> GetPropertyTypes(List<SearchResultDto> searchResultDtos) { Dictionary<string, Type> propertyTypes = new Dictionary<string, Type>(); foreach (var searchResultDto in searchResultDtos) { if (propertyTypes.ContainsKey(searchResultDto.FieldCustomId)) continue; if (searchResultDto.field.FieldViewType == FieldViewType._13_ListOrRecordType) { propertyTypes.Add(searchResultDto.FieldCustomId, typeof(string)); } else if (searchResultDto.field.FieldViewType == FieldViewType._12_IntegerNumber) { if (searchResultDto.field.Mandatory) { propertyTypes.Add(searchResultDto.FieldCustomId, typeof(int)); } else { propertyTypes.Add(searchResultDto.FieldCustomId, typeof(int?)); } } else if (searchResultDto.field.FieldViewType == FieldViewType._1_Currency || searchResultDto.field.FieldViewType == FieldViewType._4_DecimalNumber || searchResultDto.field.FieldViewType == FieldViewType._16_Percent) { if(searchResultDto.field.Mandatory) { propertyTypes.Add(searchResultDto.FieldCustomId, typeof(decimal)); } else { propertyTypes.Add(searchResultDto.FieldCustomId, typeof(decimal?)); } } else if (searchResultDto.field.FieldViewType == FieldViewType._0_CheckBox) { if(searchResultDto.field.Mandatory) { propertyTypes.Add(searchResultDto.FieldCustomId, typeof(bool)); } else { propertyTypes.Add(searchResultDto.FieldCustomId, typeof(bool?)); } } else { propertyTypes.Add(searchResultDto.FieldCustomId, typeof(string)); } } return propertyTypes; } protected override void MapToEntity(CreateOrUpdateSearchDto updateInput, SearchEntity entity) { base.MapToEntity(updateInput, entity); //固定条件映射 var cearchCriteriaIds = updateInput.SearchCriterias.Select(p => p.Id).ToList(); entity.SearchCriterias.RemoveAll(u => !cearchCriteriaIds.Contains(u.Id)); foreach (var item in updateInput.SearchCriterias) { var oldEntity = entity.SearchCriterias.Find(p => p.Id == item.Id); if (oldEntity == null) { entity.SearchCriterias.Add(ObjectMapper.Map<CreateOrUpdateSearchCriteriaDto, SearchCriteriaEntity>(item)); } else { ObjectMapper.Map(item, oldEntity); } } //可选条件映射 var searchAvailableFiltersIds = updateInput.SearchAvailableFilters.Select(p => p.Id).ToList(); entity.SearchAvailableFilters.RemoveAll(u => !searchAvailableFiltersIds.Contains(u.Id)); foreach (var item in updateInput.SearchAvailableFilters) { var oldEntity = entity.SearchAvailableFilters.Find(p => p.Id == item.Id); if (oldEntity == null) { entity.SearchAvailableFilters.Add(ObjectMapper.Map<CreateOrUpdateSearchAvailableFilterDto, SearchAvailableFilterEntity>(item)); } else { ObjectMapper.Map(item, oldEntity); } } //结果集映射 var searchResultIds = updateInput.SearchResults.Select(p => p.Id).ToList(); entity.SearchResults.RemoveAll(u => !searchResultIds.Contains(u.Id)); foreach (var item in updateInput.SearchResults) { var oldEntity = entity.SearchResults.Find(p => p.Id == item.Id); if (oldEntity == null) { entity.SearchResults.Add(ObjectMapper.Map<CreateOrUpdateSearchResultDto, SearchResultEntity>(item)); } else { ObjectMapper.Map(item, oldEntity); } } //SetIdForGuids(entity); } protected override void SetIdForGuids(SearchEntity entity) { base.SetIdForGuids(entity); foreach (var item in entity.SearchAvailableFilters) { if (item.Id == Guid.Empty) EntityHelper.TrySetId(item, () => GuidGenerator.Create(), true); } foreach (var item in entity.SearchCriterias) { if (item.Id == Guid.Empty) EntityHelper.TrySetId(item, () => GuidGenerator.Create(), true); } foreach (var item in entity.SearchResults) { if (item.Id == Guid.Empty) EntityHelper.TrySetId(item, () => GuidGenerator.Create(), true); } } // 处理子(join表)查询 private async Task _ResetSubScopeFilterAsync(RecordTypeEntity recordType, List<FilterModel> filters) { var tasks = new List<Task>(); foreach (var filter in filters) { //var currFilter = filters[i]; var filterUsings = filter.FilterUsing.Split("."); if (filterUsings.Count() > 5) throw new UserFriendlyException("最多只能5级关联查询"); if (filterUsings.Count() == 1) continue; tasks.Add(_ResetSubScopeFilterAsync1(recordType.Id, filter)); } await Task.WhenAll(tasks); } private async Task _ResetSubScopeFilterAsync1(Guid recordTypeId, FilterModel currFilter) { var filterUsings = currFilter.FilterUsing.Split("."); Guid currRecordTypeId = recordTypeId; Stack<RecordTypeFieldDto> recordTypeFieldDtoStack = new Stack<RecordTypeFieldDto>(); for (int j = 0; j < filterUsings.Count(); j++) { var filter = filterUsings[j]; var recordTypeFieldDto = await _recordTypeFieldAppService.GetByCustomIdAsync(currRecordTypeId, filter); currRecordTypeId = recordTypeFieldDto.FieldListOrRecordTypeId.Value; recordTypeFieldDtoStack.Push(recordTypeFieldDto); } FilterSearchResultRequestDto filterSearchResultRequestDto = new FilterSearchResultRequestDto() { MaxResultCount = 1, SkipCount = 0, Sorting = "sort", Filter = "" }; var subFilter = Newtonsoft.Json.JsonConvert.DeserializeObject<FilterModel>(Newtonsoft.Json.JsonConvert.SerializeObject(currFilter)); var recordTypeFieldDtoS = recordTypeFieldDtoStack.Pop(); subFilter.FilterUsing = recordTypeFieldDtoS.CustomId; List<FilterModel> filterModels = new List<FilterModel>() { subFilter }; var strFilter = filterModels.Count > 0 ? JsonSerializer.Serialize(filterModels) : string.Empty; for (int j = 1; j < filterUsings.Count(); j++) { var recordType1 = await _recordTypeRepository.GetAsync(recordTypeFieldDtoS.RecordTypeId); // 查询远程数据列表 filterSearchResultRequestDto.Filter = strFilter; //string methodName = string.Format("api/{0}/{1}/{2}", recordType1.ServiceModule, recordType1.EntityName, strParams); var result = await _fetchRemoteDataAsync(recordType1, filterSearchResultRequestDto, null); // Todo: 暂不支持自定义MethodName的recordtype 作为子记录 List<Guid> ids = new List<Guid>(); foreach (var item in result.Items) { ids.Add(item.id); } recordTypeFieldDtoS = recordTypeFieldDtoStack.Pop(); subFilter.FilterUsing = recordTypeFieldDtoS.CustomId; subFilter.OperatorType = OperatorType.AnyOf; subFilter.Value1 = Newtonsoft.Json.JsonConvert.SerializeObject(ids); } currFilter.FilterUsing = recordTypeFieldDtoS.CustomId; currFilter.OperatorType = OperatorType.AnyOf; currFilter.Value1 = subFilter.Value1; } #endregion /// <summary> /// 根据recordTypeId查询视图列表,及明细 /// </summary> /// <param name="recordTypeId"></param> /// <returns></returns> public async Task<List<SearchDto>> GetListDetailsAsync(Guid recordTypeId) { List<SearchDto> result = []; var list = await _searchRepository.GetListAsync(o => o.RecordTypeId == recordTypeId); if (list.Any()) { var searchIds = list.Select(o => o.Id).ToList(); var searchAvailableFilters = await _searchAvailableFilterRepository.GetListAsync(o => searchIds.Contains(o.SearchId)); var searchResults = await _searchResultRepository.GetListAsync(o => searchIds.Contains(o.SearchId)); var searchCriterias = await _searchCriteriaRepository.GetListAsync(o => searchIds.Contains(o.SearchId)); //需要查询字段集合 var havingSelfileIds = new List<Guid>(); havingSelfileIds.AddRange(searchAvailableFilters.Select(o => o.FieldId).Distinct().ToList()); havingSelfileIds.AddRange(searchResults.Select(o => o.FieldId).Distinct().ToList()); havingSelfileIds.AddRange(searchAvailableFilters.Where(o => o.LinkFieldId.HasValue).Select(o => o.LinkFieldId.Value).Distinct().ToList()); havingSelfileIds = havingSelfileIds.Distinct().ToList(); var fieldDtos = await _recordTypeFieldAppService.GetListByIdsAsync(havingSelfileIds); foreach (var item in list) { item.SearchResults = searchResults.Where(o => o.SearchId == item.Id).OrderBy(o => o.Sort).ToList(); item.SearchAvailableFilters = searchAvailableFilters.Where(o => o.SearchId == item.Id).OrderBy(o => o.Sort).ToList(); item.SearchCriterias = searchCriterias.Where(o => o.SearchId == item.Id).OrderBy(o => o.Sort).ToList(); } result = ObjectMapper.Map<List<SearchEntity>, List<SearchDto>>(list); foreach (var item in result) { //可选条件字段信息赋值 if (item.SearchAvailableFilters.Any()) { foreach (var itemAvailableFilters in item.SearchAvailableFilters) { var fieldInfo = fieldDtos.FirstOrDefault(o => o.Id == itemAvailableFilters.FieldId); if (fieldInfo != null) { itemAvailableFilters.Field = fieldInfo; itemAvailableFilters.FieldCustomId = fieldInfo.FieldCustomId; } } } //结果集字段信息赋值 if (item.SearchResults.Any()) { foreach (var itemSearchResults in item.SearchResults) { var fieldInfo = fieldDtos.FirstOrDefault(o => o.Id == itemSearchResults.FieldId); if (fieldInfo != null) { itemSearchResults.field = fieldInfo; itemSearchResults.FieldCustomId = fieldInfo.FieldCustomId; itemSearchResults.FieldName = fieldInfo.Name; } } } } } return result; } } // 转换小驼峰方法 public static class CamelCaseHelper { // 主入口:List<dynamic> → List<dynamic> public static List<dynamic> ToCamelCaseList(List<dynamic> list) { var jArray = JArray.FromObject(list); // 转 JArray var camelJArray = (JArray)ToCamelCase(jArray); // 转 key return camelJArray.ToObject<List<dynamic>>(); // 转回 List<dynamic> } // 递归转换 JToken private static JToken ToCamelCase(JToken token) { if (token is JObject obj) { var newObj = new JObject(); foreach (var property in obj.Properties()) { string camelName = ToCamelCase(property.Name); newObj[camelName] = ToCamelCase(property.Value); } return newObj; } else if (token is JArray array) { var newArray = new JArray(); foreach (var item in array) { newArray.Add(ToCamelCase(item)); } return newArray; } else { return token.DeepClone(); } } // key 转 camelCase private static string ToCamelCase(string str) { if (string.IsNullOrEmpty(str)) return str; return char.ToLower(str[0], CultureInfo.InvariantCulture) + str.Substring(1); } } } 详细讲解
最新发布
11-27
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值