python-qlv-helper 0.2.0__py3-none-any.whl → 0.6.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {python_qlv_helper-0.2.0.dist-info → python_qlv_helper-0.6.0.dist-info}/METADATA +3 -2
- {python_qlv_helper-0.2.0.dist-info → python_qlv_helper-0.6.0.dist-info}/RECORD +17 -14
- qlv_helper/config/__init__.py +11 -0
- qlv_helper/config/custom_exception.py +35 -0
- qlv_helper/config/url_const.py +13 -0
- qlv_helper/controller/order_detail.py +412 -4
- qlv_helper/controller/order_table.py +17 -1
- qlv_helper/http/order_page.py +143 -42
- qlv_helper/http/order_table_page.py +30 -4
- qlv_helper/po/domestic_activity_order_page.py +2 -4
- qlv_helper/po/login_page.py +1 -1
- qlv_helper/po/main_page.py +3 -4
- qlv_helper/po/order_detail_page.py +275 -0
- qlv_helper/po/wechat_auth_page.py +1 -1
- qlv_helper/po/base_po.py +0 -40
- {python_qlv_helper-0.2.0.dist-info → python_qlv_helper-0.6.0.dist-info}/WHEEL +0 -0
- {python_qlv_helper-0.2.0.dist-info → python_qlv_helper-0.6.0.dist-info}/licenses/LICENSE +0 -0
- {python_qlv_helper-0.2.0.dist-info → python_qlv_helper-0.6.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
# ---------------------------------------------------------------------------------------------------------
|
|
4
|
+
# ProjectName: qlv-helper
|
|
5
|
+
# FileName: order_detail_page.py
|
|
6
|
+
# Description: 订单详情页面对象
|
|
7
|
+
# Author: ASUS
|
|
8
|
+
# CreateDate: 2025/12/16
|
|
9
|
+
# Copyright ©2011-2025. Hunan xxxxxxx Company limited. All rights reserved.
|
|
10
|
+
# ---------------------------------------------------------------------------------------------------------
|
|
11
|
+
"""
|
|
12
|
+
from typing import Dict, Any, List, Tuple
|
|
13
|
+
from playwright.async_api import Page, Locator
|
|
14
|
+
from playwright_helper.libs.base_po import BasePo
|
|
15
|
+
from qlv_helper.config.url_const import order_detail_url
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class OrderDetailPage(BasePo):
|
|
19
|
+
url: str = order_detail_url
|
|
20
|
+
__page: Page
|
|
21
|
+
|
|
22
|
+
def __init__(self, page: Page, url: str = order_detail_url) -> None:
|
|
23
|
+
super().__init__(page, url)
|
|
24
|
+
self.url = url
|
|
25
|
+
self.__page = page
|
|
26
|
+
|
|
27
|
+
async def get_order_lock_state_btn(self, timeout: float = 5.0) -> Tuple[str, Locator]:
|
|
28
|
+
"""
|
|
29
|
+
订单详情页面,获取订单锁单状态按钮
|
|
30
|
+
:param timeout: 超时时间(秒)
|
|
31
|
+
:return: (是否存在, 错误信息|元素对象)
|
|
32
|
+
"""
|
|
33
|
+
selector: str = '//div[@class="order_information "]//a[(@class="view_detail_red" and contains(text(), "强制解锁")) or (@class="view_detail_green" and (contains(text(), "锁定") or contains(text(), "解锁返回") or contains(text(), "操作")))]'
|
|
34
|
+
locator: Locator = await self.get_locator(selector=selector, timeout=timeout)
|
|
35
|
+
text: str = (await locator.inner_text()).strip()
|
|
36
|
+
return text, locator
|
|
37
|
+
|
|
38
|
+
async def get_out_ticket_platform_type_dropdown(self, timeout: float = 5.0) -> Locator:
|
|
39
|
+
"""
|
|
40
|
+
订单详情页面,采购信息栏,获取出票地类型下拉菜单按钮
|
|
41
|
+
:param timeout: 超时时间(秒)
|
|
42
|
+
:return: (是否存在, 错误信息|元素对象)
|
|
43
|
+
"""
|
|
44
|
+
selector: str = '//table[@id="PurchaseInfos"]//select[@id="OutTktPFTypeID"]'
|
|
45
|
+
return await self.get_locator(selector=selector, timeout=timeout)
|
|
46
|
+
|
|
47
|
+
async def get_out_ticket_platform_type_select_option(self, select_option: str, timeout: float = 5.0) -> Locator:
|
|
48
|
+
"""
|
|
49
|
+
订单详情页面,采购信息栏,获取出票地类型下拉菜单选项
|
|
50
|
+
:param select_option: 要获取的选项
|
|
51
|
+
:param timeout: 超时时间(秒)
|
|
52
|
+
:return: (是否存在, 错误信息|元素对象)
|
|
53
|
+
"""
|
|
54
|
+
selector: str = f'//table[@id="PurchaseInfos"]//select[@id="OutTktPFTypeID"]/option[contains(text(), "{select_option}")]'
|
|
55
|
+
return await self.get_locator(selector=selector, timeout=timeout)
|
|
56
|
+
|
|
57
|
+
async def get_out_ticket_platform_dropdown(self, timeout: float = 5.0) -> Locator:
|
|
58
|
+
"""
|
|
59
|
+
订单详情页面,采购信息栏,获取出票平台下拉菜单按钮
|
|
60
|
+
:param timeout: 超时时间(秒)
|
|
61
|
+
:return: (是否存在, 错误信息|元素对象)
|
|
62
|
+
"""
|
|
63
|
+
selector: str = '//table[@id="PurchaseInfos"]//select[@id="OutTktPF"]'
|
|
64
|
+
return await self.get_locator(selector=selector, timeout=timeout)
|
|
65
|
+
|
|
66
|
+
async def get_out_ticket_platform_select_option(self, select_option: str, timeout: float = 5.0) -> Locator:
|
|
67
|
+
"""
|
|
68
|
+
订单详情页面,采购信息栏,获取出票平台下拉菜单选项
|
|
69
|
+
:param select_option: 要获取的选项
|
|
70
|
+
:param timeout: 超时时间(秒)
|
|
71
|
+
:return: (是否存在, 错误信息|元素对象)
|
|
72
|
+
"""
|
|
73
|
+
selector: str = f'//table[@id="PurchaseInfos"]//select[@id="OutTktPF"]/option[contains(text(), "{select_option}")]'
|
|
74
|
+
return await self.get_locator(selector=selector, timeout=timeout)
|
|
75
|
+
|
|
76
|
+
async def get_out_ticket_account_dropdown(self, timeout: float = 5.0) -> Locator:
|
|
77
|
+
"""
|
|
78
|
+
订单详情页面,采购信息栏,获取出票账号下拉菜单按钮
|
|
79
|
+
:param timeout: 超时时间(秒)
|
|
80
|
+
:return: (是否存在, 错误信息|元素对象)
|
|
81
|
+
"""
|
|
82
|
+
selector: str = '//table[@id="PurchaseInfos"]//select[@id="AccountNumber"]'
|
|
83
|
+
return await self.get_locator(selector=selector, timeout=timeout)
|
|
84
|
+
|
|
85
|
+
async def get_out_ticket_account_select_option(self, select_option: str, timeout: float = 5.0) -> Locator:
|
|
86
|
+
"""
|
|
87
|
+
订单详情页面,采购信息栏,获取出票账号下拉菜单选项
|
|
88
|
+
:param select_option: 要获取的选项
|
|
89
|
+
:param timeout: 超时时间(秒)
|
|
90
|
+
:return: (是否存在, 错误信息|元素对象)
|
|
91
|
+
"""
|
|
92
|
+
selector: str = f'//table[@id="PurchaseInfos"]//select[@id="AccountNumber"]/option[contains(text(), "{select_option}")]'
|
|
93
|
+
return await self.get_locator(selector=selector, timeout=timeout)
|
|
94
|
+
|
|
95
|
+
async def get_purchase_account_type_dropdown(self, timeout: float = 5.0) -> Locator:
|
|
96
|
+
"""
|
|
97
|
+
订单详情页面,采购信息栏,获取采购账号类型下拉菜单按钮
|
|
98
|
+
:param timeout: 超时时间(秒)
|
|
99
|
+
:return: (是否存在, 错误信息|元素对象)
|
|
100
|
+
"""
|
|
101
|
+
selector: str = '//table[@id="PurchaseInfos"]//select[@id="TypeName"]'
|
|
102
|
+
return await self.get_locator(selector=selector, timeout=timeout)
|
|
103
|
+
|
|
104
|
+
async def get_purchase_account_type_select_option(self, select_option: str, timeout: float = 5.0) -> Locator:
|
|
105
|
+
"""
|
|
106
|
+
订单详情页面,采购信息栏,获取采购账号类型下拉菜单选项
|
|
107
|
+
:param select_option: 要获取的选项
|
|
108
|
+
:param timeout: 超时时间(秒)
|
|
109
|
+
:return: (是否存在, 错误信息|元素对象)
|
|
110
|
+
"""
|
|
111
|
+
selector: str = f'//table[@id="PurchaseInfos"]//select[@id="TypeName"]/option[contains(text(), "{select_option}")]'
|
|
112
|
+
return await self.get_locator(selector=selector, timeout=timeout)
|
|
113
|
+
|
|
114
|
+
async def get_purchase_account_dropdown(self, timeout: float = 5.0) -> Locator:
|
|
115
|
+
"""
|
|
116
|
+
订单详情页面,采购信息栏,获取采购账号下拉菜单按钮
|
|
117
|
+
:param timeout: 超时时间(秒)
|
|
118
|
+
:return: (是否存在, 错误信息|元素对象)
|
|
119
|
+
"""
|
|
120
|
+
selector: str = '//table[@id="PurchaseInfos"]//select[@id="PurchaseAccount"]'
|
|
121
|
+
return await self.get_locator(selector=selector, timeout=timeout)
|
|
122
|
+
|
|
123
|
+
async def get_purchase_account_select_option(self, select_option: str, timeout: float = 5.0) -> Locator:
|
|
124
|
+
"""
|
|
125
|
+
订单详情页面,采购信息栏,获取采购账号下拉菜单选项
|
|
126
|
+
:param select_option: 要获取的选项
|
|
127
|
+
:param timeout: 超时时间(秒)
|
|
128
|
+
:return: (是否存在, 错误信息|元素对象)
|
|
129
|
+
"""
|
|
130
|
+
selector: str = f'//table[@id="PurchaseInfos"]//select[@id="PurchaseAccount"]/option[contains(text(), "{select_option}")]'
|
|
131
|
+
return await self.get_locator(selector=selector, timeout=timeout)
|
|
132
|
+
|
|
133
|
+
async def get_purchase_amount_input(self, timeout: float = 5.0) -> Locator:
|
|
134
|
+
"""
|
|
135
|
+
订单详情页面,采购信息栏,获取采购金额输入框
|
|
136
|
+
:param timeout: 超时时间(秒)
|
|
137
|
+
:return: (是否存在, 错误信息|元素对象)
|
|
138
|
+
"""
|
|
139
|
+
selector: str = '//table[@id="PurchaseInfos"]//input[@id="TransactionAmount"]'
|
|
140
|
+
return await self.get_locator(selector=selector, timeout=timeout)
|
|
141
|
+
|
|
142
|
+
async def get_remark_input(self, timeout: float = 5.0) -> Locator:
|
|
143
|
+
"""
|
|
144
|
+
订单详情页面,采购信息栏,获取备注输入框,一般输入登录官网的"账号/密码"
|
|
145
|
+
:param timeout: 超时时间(秒)
|
|
146
|
+
:return: (是否存在, 错误信息|元素对象)
|
|
147
|
+
"""
|
|
148
|
+
selector: str = '//table[@id="PurchaseInfos"]//input[@id="Remark"]'
|
|
149
|
+
return await self.get_locator(selector=selector, timeout=timeout)
|
|
150
|
+
|
|
151
|
+
async def get_main_check_input(self, timeout: float = 5.0) -> Locator:
|
|
152
|
+
"""
|
|
153
|
+
订单详情页面,采购信息栏,获取对账标识输入框,一般输入虚拟卡的card_id
|
|
154
|
+
:param timeout: 超时时间(秒)
|
|
155
|
+
:return: (是否存在, 错误信息|元素对象)
|
|
156
|
+
"""
|
|
157
|
+
selector: str = '//table[@id="PurchaseInfos"]//input[@id="MainCheckNumber"]'
|
|
158
|
+
return await self.get_locator(selector=selector, timeout=timeout)
|
|
159
|
+
|
|
160
|
+
async def get_air_comp_order_id_input(self, timeout: float = 5.0) -> Locator:
|
|
161
|
+
"""
|
|
162
|
+
订单详情页面,采购信息栏,获取官网订单号输入框
|
|
163
|
+
:param timeout: 超时时间(秒)
|
|
164
|
+
:return: (是否存在, 错误信息|元素对象)
|
|
165
|
+
"""
|
|
166
|
+
selector: str = '//table[@id="PurchaseInfos"]//input[@id="AirCoOrderID"]'
|
|
167
|
+
return await self.get_locator(selector=selector, timeout=timeout)
|
|
168
|
+
|
|
169
|
+
async def get_procurement_info_save_btn(self, timeout: float = 5.0) -> Locator:
|
|
170
|
+
"""
|
|
171
|
+
订单详情页面,采购信息栏,获取【保存采购】按钮
|
|
172
|
+
:param timeout: 超时时间(秒)
|
|
173
|
+
:return: (是否存在, 错误信息|元素对象)
|
|
174
|
+
"""
|
|
175
|
+
selector: str = '//table[@id="PurchaseInfos"]//input[@id="submit"]'
|
|
176
|
+
return await self.get_locator(selector=selector, timeout=timeout)
|
|
177
|
+
|
|
178
|
+
async def get_passenger_itinerary_locators(self, timeout: float = 5.0) -> List[Dict[str, Any]]:
|
|
179
|
+
"""
|
|
180
|
+
订单详情页面,乘客信息栏,获取票号输入框
|
|
181
|
+
:param timeout:超时时间(秒)
|
|
182
|
+
:return: List[{
|
|
183
|
+
"username": str,
|
|
184
|
+
"id_number": str,
|
|
185
|
+
"locator": Locator # 指向 <input name="ticketNo">
|
|
186
|
+
}]
|
|
187
|
+
"""
|
|
188
|
+
results = list()
|
|
189
|
+
tbody_selector: str = '(//div[@class="order_information"]//table[@class="table table_border table_center"]/tbody)[3]'
|
|
190
|
+
tbody_locator: Locator = await self.get_locator(selector=tbody_selector, timeout=timeout)
|
|
191
|
+
# 只选真正的数据行(有 pid / ticketNo 的 tr)
|
|
192
|
+
rows = tbody_locator.locator("tr[pid]")
|
|
193
|
+
row_count = await rows.count()
|
|
194
|
+
for i in range(row_count):
|
|
195
|
+
row = rows.nth(i)
|
|
196
|
+
|
|
197
|
+
out_ticket_state: Locator = await self.get_sub_locator(
|
|
198
|
+
locator=row, selector='xpath=(.//td)[1]', timeout=timeout
|
|
199
|
+
)
|
|
200
|
+
out_ticket_state_text = (await out_ticket_state.inner_text()).strip()
|
|
201
|
+
if "已出票" in out_ticket_state_text:
|
|
202
|
+
continue
|
|
203
|
+
|
|
204
|
+
# 1️⃣ 用户名
|
|
205
|
+
name_locator = await self.get_sub_locator(
|
|
206
|
+
locator=row, selector='xpath=.//span[@name="pname"]', timeout=timeout
|
|
207
|
+
)
|
|
208
|
+
username = (await name_locator.first.inner_text()).strip()
|
|
209
|
+
|
|
210
|
+
# 2️⃣ 身份证号
|
|
211
|
+
# a 标签里:javascript:showIDNo('身份证号', ...)
|
|
212
|
+
id_link = await self.get_sub_locator(locator=row, selector='xpath=.//a[starts-with(@id, "IDNo_")]',
|
|
213
|
+
timeout=timeout)
|
|
214
|
+
|
|
215
|
+
href = await id_link.first.get_attribute("href")
|
|
216
|
+
# href 示例:
|
|
217
|
+
# javascript:showIDNo('320324198511254466','175687',...)
|
|
218
|
+
|
|
219
|
+
id_number = None
|
|
220
|
+
if href:
|
|
221
|
+
# 非正则版本,更安全
|
|
222
|
+
start = href.find("'") + 1
|
|
223
|
+
end = href.find("'", start)
|
|
224
|
+
id_number = href[start:end]
|
|
225
|
+
|
|
226
|
+
# 3️⃣ ticketNo 输入框 locator
|
|
227
|
+
ticket_input = await self.get_sub_locator(locator=row, selector='xpath=.//input[@name="ticketNo"]',
|
|
228
|
+
timeout=timeout)
|
|
229
|
+
|
|
230
|
+
# 防御性校验(可选)
|
|
231
|
+
if await ticket_input.count() == 0:
|
|
232
|
+
continue
|
|
233
|
+
|
|
234
|
+
results.append({
|
|
235
|
+
"username": username,
|
|
236
|
+
"id_number": id_number,
|
|
237
|
+
"locator": ticket_input # Playwright Locator 对象
|
|
238
|
+
})
|
|
239
|
+
return results
|
|
240
|
+
|
|
241
|
+
async def get_custom_remark_input(self, timeout: float = 5.0) -> Locator:
|
|
242
|
+
"""
|
|
243
|
+
订单详情页面,日志记录栏,获取自定义备注输入框
|
|
244
|
+
:param timeout: 超时时间(秒)
|
|
245
|
+
:return: (是否存在, 错误信息|元素对象)
|
|
246
|
+
"""
|
|
247
|
+
selector: str = '//input[@id="rmk"]'
|
|
248
|
+
return await self.get_locator(selector=selector, timeout=timeout)
|
|
249
|
+
|
|
250
|
+
async def get_custom_remark_save_btn(self, timeout: float = 5.0) -> Locator:
|
|
251
|
+
"""
|
|
252
|
+
订单详情页面,日志记录栏,获取【保存备注】按钮
|
|
253
|
+
:param timeout: 超时时间(秒)
|
|
254
|
+
:return: (是否存在, 错误信息|元素对象)
|
|
255
|
+
"""
|
|
256
|
+
selector: str = '//a[@href="javascript:fnSaveRemark()"]'
|
|
257
|
+
return await self.get_locator(selector=selector, timeout=timeout)
|
|
258
|
+
|
|
259
|
+
async def get_message_notice_dialog_confirm_btn(self, timeout: float = 5.0) -> Locator:
|
|
260
|
+
"""
|
|
261
|
+
订单详情页面,消息提醒弹框,获取【确认】按钮
|
|
262
|
+
:param timeout: 超时时间(秒)
|
|
263
|
+
:return: (是否存在, 错误信息|元素对象)
|
|
264
|
+
"""
|
|
265
|
+
selector: str = '//a[@id="alertMsg_btnConfirm"]/cite'
|
|
266
|
+
return await self.get_locator(selector=selector, timeout=timeout)
|
|
267
|
+
|
|
268
|
+
async def get_policy_input(self, timeout: float = 5.0) -> Locator:
|
|
269
|
+
"""
|
|
270
|
+
订单详情页面,获取政策代码输入框
|
|
271
|
+
:param timeout:
|
|
272
|
+
:return:
|
|
273
|
+
"""
|
|
274
|
+
selector: str = '//legend//input[@name="PolicyName"]'
|
|
275
|
+
return await self.get_locator(selector=selector, timeout=timeout)
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
import os
|
|
13
13
|
import asyncio
|
|
14
14
|
from typing import Tuple, Union
|
|
15
|
-
from
|
|
15
|
+
from playwright_helper.libs.base_po import BasePo
|
|
16
16
|
from qlv_helper.utils.file_handle import get_caller_dir
|
|
17
17
|
from qlv_helper.utils.windows_utils import gen_allow_btn_image, windows_on_click
|
|
18
18
|
from playwright.async_api import Page, TimeoutError as PlaywrightTimeoutError, Locator
|
qlv_helper/po/base_po.py
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
"""
|
|
3
|
-
# ---------------------------------------------------------------------------------------------------------
|
|
4
|
-
# ProjectName: qlv-helper
|
|
5
|
-
# FileName: base_po.py
|
|
6
|
-
# Description: po对象基础类
|
|
7
|
-
# Author: ASUS
|
|
8
|
-
# CreateDate: 2025/11/25
|
|
9
|
-
# Copyright ©2011-2025. Hunan xxxxxxx Company limited. All rights reserved.
|
|
10
|
-
# ---------------------------------------------------------------------------------------------------------
|
|
11
|
-
"""
|
|
12
|
-
from typing import List
|
|
13
|
-
from playwright.async_api import Page
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class BasePo(object):
|
|
17
|
-
__page: Page
|
|
18
|
-
|
|
19
|
-
def __init__(self, page: Page, url: str):
|
|
20
|
-
self.url = url
|
|
21
|
-
self.__page = page
|
|
22
|
-
if self.is_current_page() is False:
|
|
23
|
-
raise ValueError("page参数值无效")
|
|
24
|
-
|
|
25
|
-
def get_page(self) -> Page:
|
|
26
|
-
return self.__page
|
|
27
|
-
|
|
28
|
-
def is_current_page(self) -> bool:
|
|
29
|
-
url = self.__page.url.split("?")[0]
|
|
30
|
-
if isinstance(self.__page, Page) and url.endswith(self.url):
|
|
31
|
-
return True
|
|
32
|
-
else:
|
|
33
|
-
return False
|
|
34
|
-
|
|
35
|
-
def get_url_domain(self) -> str:
|
|
36
|
-
if isinstance(self.__page, Page):
|
|
37
|
-
page_slice: List[str] = self.__page.url.split("/")
|
|
38
|
-
return f"{page_slice[0]}://{page_slice[2]}"
|
|
39
|
-
else:
|
|
40
|
-
raise AttributeError("PO对象中的page属性未被初始化")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|