filter_page.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. '''
  4. @File : filter_page.py
  5. @Time : 2024/11/21 14:55:10
  6. @Author : dulip3ng
  7. @Version : 1.0
  8. @Desc : None
  9. '''
  10. import re
  11. from playwright.sync_api import Page, expect
  12. from common.field_type import FieldType
  13. from common.condition_row import ConditionRow
  14. from pages.base_page import BasePage
  15. class FilterPage(BasePage):
  16. """
  17. 过滤弹出框页面模型,适用于普通过滤弹出框,如在列表中点击过滤后弹出的过滤框
  18. """
  19. def __init__(self, page:Page, locator):
  20. super().__init__(page)
  21. self.locator = locator
  22. def check_all_org(self):
  23. """
  24. 勾选所有组织复选框,如果已经是勾选状态则不操作
  25. **用法:**
  26. FilterPage.check_all_org()
  27. :return:
  28. """
  29. # 多数情况下勾选所有组织是过滤界面第一个操作,偶发点击失效情况,加入2s等待时间确保稳定性
  30. self.page.wait_for_load_state("networkidle")
  31. locator_click = self.page.locator(self.locator.ALL_ORG_LOC)
  32. locator_get_state = self.page.locator(self.locator.ALL_ORG_GET_CHECK_STATE_LOC)
  33. try:
  34. expect(locator_get_state).to_be_checked(timeout=5000)
  35. except AssertionError:
  36. locator_click.click()
  37. def uncheck_all_org(self):
  38. """
  39. 取消勾选所有组织复选框,如果已经未勾选状态则不操作
  40. **用法:**
  41. FilterPage.uncheck_all_org()
  42. :return:
  43. """
  44. # 多数情况下勾选所有组织是过滤界面第一个操作,偶发点击失效情况,加入2s等待时间确保稳定性
  45. self.page.wait_for_load_state("networkidle")
  46. locator_click = self.page.locator(self.locator.ALL_ORG_LOC)
  47. # 打开过滤框后过滤框渲染完成才能加载到准确的所有组织勾选状态,
  48. # 增加1s等待时间,确保勾选状态准确加载
  49. self.page.wait_for_timeout(1000)
  50. locator_get_state = self.page.locator(self.locator.ALL_ORG_GET_CHECK_STATE_LOC)
  51. if locator_get_state.is_checked():
  52. locator_click.click()
  53. def check_org(self, *orgs:str):
  54. """
  55. 根据给定组织编码或名称,勾选组织
  56. **用法:**
  57. FilterPage.check_org("100.1")
  58. FilterPage.check_org("100.1", "鲁泰纺织股份")
  59. :param orgs: 组织编码或名称,可传多个,模糊匹配
  60. :return:
  61. """
  62. self.page.locator(self.locator.ORG_ARROW_LOC).click()
  63. for org in orgs:
  64. self.page.locator(self.locator.ORG_MULTI_ITEM_ARGS_LOC % org).click()
  65. self.page.locator(self.locator.ORG_SELECT_OK_BTN_LOC).click()
  66. def check_entity(self, *entitys_name:str):
  67. """
  68. 根据给定实体,勾选实体
  69. **用法:**
  70. FilterPage.check_entity("明细信息")
  71. FilterPage.check_entity("明细信息", "财务信息")
  72. **注意:**
  73. 未进行勾选状态判断,未勾选状态下调用一次为勾选,再次调用为取消勾选
  74. :param entitys_name: 实体名称
  75. :return:
  76. """
  77. for entity in entitys_name:
  78. self.page.locator(self.locator.ENTITY_CHECKBOX_ARGS_LOC % entity).click()
  79. def click_tool_bar_button(self, button_name:str):
  80. """
  81. 点击过滤框页面菜单行按钮
  82. **用法:**
  83. FilterPage.click_tool_bar_button("删除全部")
  84. :param button_name: 按钮名称
  85. :return:
  86. """
  87. self.page.locator(self.locator.TOOL_BAR_BTN_ARGS_LOC % button_name).click()
  88. def set_condition(self, row:int, condition_name:str):
  89. """
  90. 按行设置过滤条件项
  91. **用法:**
  92. FilterPage.set_condition(1, "基本信息-衬衣订单号")
  93. **注意:**
  94. 部分单据中存在相同字段名在不同页签的情况,此时模糊匹配无法找到正确选项值,推荐使用条件项全称精确匹配
  95. :param row: 行号,从1开始
  96. :param condition_name: 条件项全称
  97. :return:
  98. """
  99. self.click_tool_bar_button("新增行")
  100. locator = self.page.locator(self.locator.FILTER_CONDITION_ARGS_LOC % ((row - 1) * 35))
  101. locator.clear()
  102. locator.fill(condition_name)
  103. # 条件项赋值后默认不弹出选项框,输入空格再删除,以便弹出选项框进行选择
  104. locator.press_sequentially(" ")
  105. locator.press("Backspace")
  106. self.page.locator(self.locator.CONDITION_ITEM_ARGS_LOC % condition_name).click()
  107. def set_operator(self, row:int, operator:str):
  108. """
  109. 设置过滤条件操作符号
  110. **用法:**
  111. FilterPage.set_operator(1, "包含")
  112. :param row: 行号
  113. :param operator: 操作符名称
  114. :return:
  115. """
  116. self.page.locator(self.locator.COMPARE_TYPE_ARGS_LOC % ((row - 1) * 35)).click()
  117. self.page.locator(self.locator.COMPARE_ITEM_ARGS_LOC % operator).click()
  118. def _set_text(self, row:int, value:str):
  119. """
  120. 条件值字段,文本字段类型赋值
  121. **用法:**
  122. FilterPage.set_text(1, "内销")
  123. 使用FilterPage.set_condition_value()方法代替,此方法无需区分字段类型
  124. :param row: 行号,从1开始
  125. :param value: 条件值
  126. :return:
  127. """
  128. locator = self.page.locator(self.locator.CONDITION_TEXT_FIELD_ARGS_LOC % ((row - 1) * 35))
  129. locator.clear()
  130. locator.fill(value)
  131. def _set_date(self, row:int, value:str):
  132. """
  133. 条件值字段,日期字段类型赋值
  134. **用法:**
  135. FilterPage.set_date(1, "2024-11-25")
  136. 使用FilterPage.set_condition_value()方法代替,此方法无需区分字段类型
  137. :param row: 行号,从1开始
  138. :param value: 条件值,格式"2024-11-25"
  139. :return:
  140. """
  141. locator = self.page.locator(self.locator.CONDITION_DATE_FIELD_ARGS_LOC % ((row - 1) * 35))
  142. locator.clear()
  143. locator.fill(value)
  144. self._click_blank()
  145. def _set_select(self, row:int, value:str):
  146. """
  147. 条件值字段,下拉列表字段类型赋值
  148. **用法:**
  149. FilterPage.set_select(1, "创建")
  150. 使用FilterPage.set_condition_value()方法代替,此方法无需区分字段类型
  151. :param row: 行号,从1开始
  152. :param value: 条件值
  153. :return:
  154. """
  155. self.page.locator(self.locator.CONDITION_SELECT_FIELD_ARGS_LOC % ((row - 1) * 35)).click()
  156. self.page.locator(self.locator.CONDITION_SELECT_ITEM_ARGS_LOC % value).click()
  157. def _set_base(self, row:int, value:str):
  158. """
  159. 条件值字段,基础资料字段类型赋值
  160. **用法:**
  161. FilterPage.set_base(1, "P010001")
  162. 使用FilterPage.set_condition_value()方法代替,此方法无需区分字段类型
  163. :param row: 行号,从1开始
  164. :param value: 条件值
  165. :return:
  166. """
  167. # CONDITION_BASE_FIELD_ARGS_LOC input元素可操作性检查不通过,CONDITION_BASE_FIELD_SPAN_ARGS_LOC span劫持,这里直接对span进行点击
  168. self.page.locator(self.locator.CONDITION_BASE_FIELD_SPAN_ARGS_LOC % ((row - 1) * 35)).click()
  169. locator = self.page.locator(self.locator.CONDITION_BASE_FIELD_ARGS_LOC % ((row - 1) * 35))
  170. locator.clear()
  171. locator.fill(value)
  172. locator.press_sequentially(" ")
  173. locator.press("Backspace")
  174. self.page.locator(self.locator.CONDITION_BASE_ITEM_ARGS_LOC % value).nth(0).click()
  175. def _set_text_area(self, row:int, value:str):
  176. """
  177. 条件值字段,大文本字段类型赋值
  178. **用法:**
  179. FilterPage.set_text_area(1, "大文本字段值1234567890")
  180. 使用FilterPage.set_condition_value()方法代替,此方法无需区分字段类型
  181. :param row: 行号,从1开始
  182. :param value: 条件值
  183. :return:
  184. """
  185. locator = self.page.locator(self.locator.CONDITION_TEXTAREA_ARGS_LOC % ((row - 1) * 35))
  186. locator.clear()
  187. locator.fill(value)
  188. def set_logic(self, row:int, value:str):
  189. """
  190. 设置逻辑符号,如并且
  191. **用法:**
  192. FilterPage.set_logic(1, "并且")
  193. :param row: 行号,从1开始
  194. :param value: 逻辑符号,取并且,或者
  195. :return:
  196. """
  197. self.page.locator(self.locator.LOGIC_ARGS_LOC % ((row - 1) * 35)).click()
  198. self.page.locator(self.locator.COMPARE_ITEM_ARGS_LOC % value).click()
  199. def _parse_condition_value_field_type(self, row:int) -> FieldType:
  200. """
  201. 根据条件项元素属性,自动解析条件值项字段类型
  202. **用法:**
  203. FilterPage._parse_condition_value_field_type(1)
  204. **注意:**
  205. 1.金蝶大版本升级后,判断依据可能会调整
  206. 2.当前支持v8.2
  207. :param row: 行号,从1开始
  208. :return:
  209. """
  210. data_role = self.page.locator(self.locator.PARSE_CONDITION_VALUE_FIELD_TYPE_ARGS_LOC % ((row - 1) * 35)).get_attribute("data-attdata")
  211. pattern = "xtype\":\"([a-zA-Z0-9]+)"
  212. match_strs = re.findall(pattern, data_role)
  213. if match_strs:
  214. match match_strs[0]:
  215. case "textfield":
  216. return FieldType.TEXT
  217. case "bosf7field":
  218. return FieldType.BASE
  219. case "kdeditcombo" | "kdcombo":
  220. return FieldType.SELECT
  221. case "kdmulticombo":
  222. return FieldType.MULTISELECT
  223. case "kdtextarea" | "kdlangtextarea" | "kdtabpage":
  224. return FieldType.TEXTAREA
  225. case "datefield":
  226. return FieldType.DATE
  227. return FieldType.UNKNOWN
  228. def set_condition_value(self, row:int, value:str, field_type: FieldType = None):
  229. """
  230. 条件值字段赋值,不给定字段类型时自动解析字段类型,推荐不给定,方便统一调用形式
  231. **用法:**
  232. FilterPage.set_condition_value(1, "26024001")
  233. FilterPage.set_condition_value(1, "26024001", FieldType.TEXT)
  234. :param row: 行号,从1开始
  235. :param value: 条件值
  236. :param field_type: 条件值字段类型,可选
  237. :return:
  238. """
  239. if field_type is None:
  240. field_type = self._parse_condition_value_field_type(row)
  241. match field_type:
  242. case FieldType.TEXT:
  243. self._set_text(row, value)
  244. case FieldType.SELECT:
  245. self._set_select(row, value)
  246. case FieldType.BASE:
  247. self._set_base(row, value)
  248. case FieldType.DATE:
  249. self._set_date(row, value)
  250. case FieldType.TEXTAREA:
  251. self._set_text_area(row, value)
  252. case _:
  253. raise TypeError("未匹配到过滤页面过滤条件输入框字段类型。")
  254. def click_ok_button(self):
  255. """
  256. 点击确定,开始过滤
  257. **用法:**
  258. FilterPage.click_ok_button()
  259. :return:
  260. """
  261. self.page.locator(self.locator.FILTER_OK_BUTTON_LOC).click()
  262. self.page.wait_for_load_state("networkidle")
  263. def set_condition_row(self, row:int, condition_name:str, condition_value:str=None, operator:str=None, logic:str=None):
  264. """
  265. 设置一行过滤条件
  266. **用法:**
  267. FilterPage.set_condition_row(1, "创建日期", operator="今天")
  268. FilterPage.set_condition_row(1, "衬衣订单号", "26024001")
  269. FilterPage.set_condition_row(1, "衬衣订单号", "26024", "包含")
  270. FilterPage.set_condition_row(1, "衬衣订单号", "26024", "包含", "或者")
  271. :param row: 行号,从1,开始
  272. :param condition_name: 过滤条件名
  273. :param condition_value: 过滤条件值,可选
  274. :param operator: 操作符,可选
  275. :param logic: 逻辑符,可选
  276. :return:
  277. """
  278. self.set_condition(row, condition_name)
  279. if operator:
  280. self.set_operator(row, operator)
  281. if condition_value:
  282. self.set_condition_value(row, condition_value)
  283. if logic:
  284. self.set_logic(row, logic)
  285. def filter(self, *condition_row: ConditionRow):
  286. """
  287. 根据条件对象进行过滤,默认勾选全部组织
  288. **用法:**
  289. cond1 = ConditionRow(1, "基本信息-衬衣订单号", "123456", "包含", "或者")
  290. cond2 = ConditionRow(2, "单据状态", "已审核")
  291. FilterPage.filter(cond1, cond2)
  292. :param condition_row: 条件对象,ConditionRow(row:int, condition_name:str, condition_value:str=None, operator:str=None, logic:str=None), 可传多个
  293. :return:
  294. """
  295. self.check_all_org()
  296. self.click_tool_bar_button("全部删除")
  297. self.page.wait_for_timeout(1000)
  298. for cr in condition_row:
  299. self.set_condition_row(cr.row, cr.condition_name, cr.condition_value, cr.operator, cr.logic)
  300. self.click_ok_button()