feat(after-sale): 支持撤销售后工单并接入人机验证
This commit is contained in:
@@ -85,6 +85,8 @@ export const permissionsSchemas = {
|
||||
{ label: '每日签到', value: 'signin' },
|
||||
{ label: '前台投稿', value: 'front_submit' },
|
||||
{ label: '打赏用户', value: 'tip_user' },
|
||||
{ label: '申请售后', value: 'after_sale_apply' },
|
||||
{ label: '撤销售后', value: 'after_sale_cancel' },
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
+89
-10
@@ -6496,7 +6496,8 @@ body.aksrt-modal-runtime-open {
|
||||
底色走 --main-radius / --main-bg-color;移动端由 --bottom 底部抽屉接管。 */
|
||||
.aksrt-after-sale-apply-modal:not(.aksrt-modal-runtime--bottom) .aksrt-modal-runtime__dialog,
|
||||
.aksrt-after-sale-detail-modal:not(.aksrt-modal-runtime--bottom) .aksrt-modal-runtime__dialog,
|
||||
.aksrt-after-sale-expired-modal:not(.aksrt-modal-runtime--bottom) .aksrt-modal-runtime__dialog {
|
||||
.aksrt-after-sale-expired-modal:not(.aksrt-modal-runtime--bottom) .aksrt-modal-runtime__dialog,
|
||||
.aksrt-after-sale-withdraw-modal:not(.aksrt-modal-runtime--bottom) .aksrt-modal-runtime__dialog {
|
||||
width: min(400px, calc(100vw - 32px));
|
||||
border-radius: var(--main-radius, 8px);
|
||||
background: var(--main-bg-color, #fff);
|
||||
@@ -6504,7 +6505,8 @@ body.aksrt-modal-runtime-open {
|
||||
|
||||
.aksrt-after-sale-apply-modal .aksrt-modal-runtime__skeleton,
|
||||
.aksrt-after-sale-detail-modal .aksrt-modal-runtime__skeleton,
|
||||
.aksrt-after-sale-expired-modal .aksrt-modal-runtime__skeleton {
|
||||
.aksrt-after-sale-expired-modal .aksrt-modal-runtime__skeleton,
|
||||
.aksrt-after-sale-withdraw-modal .aksrt-modal-runtime__skeleton {
|
||||
padding: 24px 18px;
|
||||
}
|
||||
|
||||
@@ -6513,9 +6515,13 @@ body.aksrt-modal-runtime-open {
|
||||
.aksrt-after-sale-apply-modal .modal-colorful-header,
|
||||
.aksrt-after-sale-detail-modal .modal-colorful-header,
|
||||
.aksrt-after-sale-expired-modal .modal-colorful-header,
|
||||
.aksrt-after-sale-withdraw-modal .modal-colorful-header,
|
||||
.aksrt-order-detail-modal .modal-colorful-header,
|
||||
.aksrt-after-sale-apply-modal .colorful-bg,
|
||||
.aksrt-after-sale-detail-modal .colorful-bg,
|
||||
.aksrt-after-sale-expired-modal .colorful-bg {
|
||||
.aksrt-after-sale-expired-modal .colorful-bg,
|
||||
.aksrt-after-sale-withdraw-modal .colorful-bg,
|
||||
.aksrt-order-detail-modal .colorful-bg {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
color: var(--this-color);
|
||||
@@ -6525,7 +6531,9 @@ body.aksrt-modal-runtime-open {
|
||||
|
||||
.aksrt-after-sale-apply-modal .modal-colorful-header,
|
||||
.aksrt-after-sale-detail-modal .modal-colorful-header,
|
||||
.aksrt-after-sale-expired-modal .modal-colorful-header {
|
||||
.aksrt-after-sale-expired-modal .modal-colorful-header,
|
||||
.aksrt-after-sale-withdraw-modal .modal-colorful-header,
|
||||
.aksrt-order-detail-modal .modal-colorful-header {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
@@ -6539,13 +6547,17 @@ body.aksrt-modal-runtime-open {
|
||||
|
||||
.aksrt-after-sale-apply-modal .modal-colorful-header + *,
|
||||
.aksrt-after-sale-detail-modal .modal-colorful-header + *,
|
||||
.aksrt-after-sale-expired-modal .modal-colorful-header + * {
|
||||
.aksrt-after-sale-expired-modal .modal-colorful-header + *,
|
||||
.aksrt-after-sale-withdraw-modal .modal-colorful-header + *,
|
||||
.aksrt-order-detail-modal .modal-colorful-header + * {
|
||||
margin-top: 120px;
|
||||
}
|
||||
|
||||
.aksrt-after-sale-apply-modal .modal-colorful-header > .close,
|
||||
.aksrt-after-sale-detail-modal .modal-colorful-header > .close,
|
||||
.aksrt-after-sale-expired-modal .modal-colorful-header > .close {
|
||||
.aksrt-after-sale-expired-modal .modal-colorful-header > .close,
|
||||
.aksrt-after-sale-withdraw-modal .modal-colorful-header > .close,
|
||||
.aksrt-order-detail-modal .modal-colorful-header > .close {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
@@ -6562,8 +6574,14 @@ body.aksrt-modal-runtime-open {
|
||||
.aksrt-after-sale-detail-modal .colorful-bg > .colorful-make::before,
|
||||
.aksrt-after-sale-detail-modal .colorful-bg > .colorful-make::after,
|
||||
.aksrt-after-sale-expired-modal .colorful-bg > .colorful-make,
|
||||
.aksrt-after-sale-withdraw-modal .colorful-bg > .colorful-make,
|
||||
.aksrt-order-detail-modal .colorful-bg > .colorful-make,
|
||||
.aksrt-after-sale-expired-modal .colorful-bg > .colorful-make::before,
|
||||
.aksrt-after-sale-expired-modal .colorful-bg > .colorful-make::after {
|
||||
.aksrt-after-sale-withdraw-modal .colorful-bg > .colorful-make::before,
|
||||
.aksrt-order-detail-modal .colorful-bg > .colorful-make::before,
|
||||
.aksrt-after-sale-expired-modal .colorful-bg > .colorful-make::after,
|
||||
.aksrt-after-sale-withdraw-modal .colorful-bg > .colorful-make::after,
|
||||
.aksrt-order-detail-modal .colorful-bg > .colorful-make::after {
|
||||
position: absolute;
|
||||
color: var(--this-color);
|
||||
background: var(--this-bg);
|
||||
@@ -6573,7 +6591,9 @@ body.aksrt-modal-runtime-open {
|
||||
|
||||
.aksrt-after-sale-apply-modal .colorful-bg > .colorful-make,
|
||||
.aksrt-after-sale-detail-modal .colorful-bg > .colorful-make,
|
||||
.aksrt-after-sale-expired-modal .colorful-bg > .colorful-make {
|
||||
.aksrt-after-sale-expired-modal .colorful-bg > .colorful-make,
|
||||
.aksrt-after-sale-withdraw-modal .colorful-bg > .colorful-make,
|
||||
.aksrt-order-detail-modal .colorful-bg > .colorful-make {
|
||||
top: 60%;
|
||||
bottom: 0;
|
||||
width: 323px;
|
||||
@@ -6583,7 +6603,9 @@ body.aksrt-modal-runtime-open {
|
||||
|
||||
.aksrt-after-sale-apply-modal .colorful-bg > .colorful-make::before,
|
||||
.aksrt-after-sale-detail-modal .colorful-bg > .colorful-make::before,
|
||||
.aksrt-after-sale-expired-modal .colorful-bg > .colorful-make::before {
|
||||
.aksrt-after-sale-expired-modal .colorful-bg > .colorful-make::before,
|
||||
.aksrt-after-sale-withdraw-modal .colorful-bg > .colorful-make::before,
|
||||
.aksrt-order-detail-modal .colorful-bg > .colorful-make::before {
|
||||
top: -325px;
|
||||
right: -558px;
|
||||
bottom: 0;
|
||||
@@ -6595,7 +6617,9 @@ body.aksrt-modal-runtime-open {
|
||||
|
||||
.aksrt-after-sale-apply-modal .colorful-bg > .colorful-make::after,
|
||||
.aksrt-after-sale-detail-modal .colorful-bg > .colorful-make::after,
|
||||
.aksrt-after-sale-expired-modal .colorful-bg > .colorful-make::after {
|
||||
.aksrt-after-sale-expired-modal .colorful-bg > .colorful-make::after,
|
||||
.aksrt-after-sale-withdraw-modal .colorful-bg > .colorful-make::after,
|
||||
.aksrt-order-detail-modal .colorful-bg > .colorful-make::after {
|
||||
top: -170px;
|
||||
bottom: 0;
|
||||
left: -262px;
|
||||
@@ -6669,6 +6693,61 @@ body.aksrt-modal-runtime-open {
|
||||
.aksrt-after-sale-status.is-rejected,
|
||||
.aksrt-after-sale-status.is-cancelled { color: var(--color-error, #ef4444); }
|
||||
|
||||
/* 状态行右侧单元格:状态徽章 + 「撤销售后」入口(仅待审核工单渲染)。 */
|
||||
.aksrt-after-sale-detail__statusCell { display: inline-flex; align-items: center; gap: 10px; }
|
||||
.aksrt-after-sale-withdraw { display: inline-flex; align-items: center; gap: 3px; padding: 0; border: 0; background: transparent; color: var(--modal-danger, #ef4444); font-size: 12px; font-weight: 600; line-height: 1.4; cursor: pointer; transition: color .15s ease; }
|
||||
.aksrt-after-sale-withdraw .fa { font-size: 12px; }
|
||||
.aksrt-after-sale-withdraw:hover { color: color-mix(in srgb, var(--modal-danger) 78%, var(--modal-ink)); }
|
||||
|
||||
/* 售后提示弹窗(撤销售后确认)正文。 */
|
||||
.aksrt-after-sale-withdraw__body { margin-bottom: 16px; color: var(--color-ink-2, #4e5358); line-height: 1.7; }
|
||||
|
||||
/* 售后弹窗底部按钮:全宽平铺贴底(负 margin 抵消 __content 内边距)+ 分隔线,
|
||||
子按钮去独立背景/边框/圆角(文字色区分主次),覆盖上方全局按钮网格的胶囊观感;
|
||||
仅在售后弹窗家族生效,其他 runtime 弹窗仍走全局网格样式。 */
|
||||
.aksrt-after-sale-apply-modal .but-average.modal-buts,
|
||||
.aksrt-after-sale-detail-modal .but-average.modal-buts,
|
||||
.aksrt-after-sale-expired-modal .but-average.modal-buts,
|
||||
.aksrt-after-sale-withdraw-modal .but-average.modal-buts {
|
||||
gap: 0;
|
||||
margin: 16px -18px -16px;
|
||||
padding: 12px 18px;
|
||||
border-top: 1px solid var(--modal-line-soft, rgba(50, 50, 50, 0.06));
|
||||
background: var(--main-bg-color, #fff);
|
||||
}
|
||||
|
||||
.aksrt-after-sale-apply-modal .but-average.modal-buts > .but,
|
||||
.aksrt-after-sale-detail-modal .but-average.modal-buts > .but,
|
||||
.aksrt-after-sale-expired-modal .but-average.modal-buts > .but,
|
||||
.aksrt-after-sale-withdraw-modal .but-average.modal-buts > .but {
|
||||
min-height: 38px;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
background: transparent;
|
||||
color: var(--modal-muted, #777);
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
--this-bg: transparent;
|
||||
--this-border: transparent;
|
||||
--this-shadow: none;
|
||||
}
|
||||
|
||||
.aksrt-after-sale-apply-modal .but-average.modal-buts > .but.c-blue,
|
||||
.aksrt-after-sale-detail-modal .but-average.modal-buts > .but.c-blue,
|
||||
.aksrt-after-sale-expired-modal .but-average.modal-buts > .but.c-blue,
|
||||
.aksrt-after-sale-withdraw-modal .but-average.modal-buts > .but.c-blue {
|
||||
background: transparent;
|
||||
border: 0;
|
||||
color: var(--modal-primary, #f04494);
|
||||
}
|
||||
|
||||
/* 单按钮 footer 占满整行(全局网格是 1fr 1fr,单按钮会只占一半)。 */
|
||||
.aksrt-after-sale-apply-modal .but-average.modal-buts > .but:only-child,
|
||||
.aksrt-after-sale-detail-modal .but-average.modal-buts > .but:only-child,
|
||||
.aksrt-after-sale-expired-modal .but-average.modal-buts > .but:only-child,
|
||||
.aksrt-after-sale-withdraw-modal .but-average.modal-buts > .but:only-child { grid-column: 1 / -1; }
|
||||
|
||||
/* source: assets/css/frontend/notyf.css */
|
||||
/**
|
||||
* legacy-compatible frontend notifications.
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
@@ -619,7 +619,8 @@ body.aksrt-modal-runtime-open {
|
||||
底色走 --main-radius / --main-bg-color;移动端由 --bottom 底部抽屉接管。 */
|
||||
.aksrt-after-sale-apply-modal:not(.aksrt-modal-runtime--bottom) .aksrt-modal-runtime__dialog,
|
||||
.aksrt-after-sale-detail-modal:not(.aksrt-modal-runtime--bottom) .aksrt-modal-runtime__dialog,
|
||||
.aksrt-after-sale-expired-modal:not(.aksrt-modal-runtime--bottom) .aksrt-modal-runtime__dialog {
|
||||
.aksrt-after-sale-expired-modal:not(.aksrt-modal-runtime--bottom) .aksrt-modal-runtime__dialog,
|
||||
.aksrt-after-sale-withdraw-modal:not(.aksrt-modal-runtime--bottom) .aksrt-modal-runtime__dialog {
|
||||
width: min(400px, calc(100vw - 32px));
|
||||
border-radius: var(--main-radius, 8px);
|
||||
background: var(--main-bg-color, #fff);
|
||||
@@ -627,7 +628,8 @@ body.aksrt-modal-runtime-open {
|
||||
|
||||
.aksrt-after-sale-apply-modal .aksrt-modal-runtime__skeleton,
|
||||
.aksrt-after-sale-detail-modal .aksrt-modal-runtime__skeleton,
|
||||
.aksrt-after-sale-expired-modal .aksrt-modal-runtime__skeleton {
|
||||
.aksrt-after-sale-expired-modal .aksrt-modal-runtime__skeleton,
|
||||
.aksrt-after-sale-withdraw-modal .aksrt-modal-runtime__skeleton {
|
||||
padding: 24px 18px;
|
||||
}
|
||||
|
||||
@@ -636,10 +638,12 @@ body.aksrt-modal-runtime-open {
|
||||
.aksrt-after-sale-apply-modal .modal-colorful-header,
|
||||
.aksrt-after-sale-detail-modal .modal-colorful-header,
|
||||
.aksrt-after-sale-expired-modal .modal-colorful-header,
|
||||
.aksrt-after-sale-withdraw-modal .modal-colorful-header,
|
||||
.aksrt-order-detail-modal .modal-colorful-header,
|
||||
.aksrt-after-sale-apply-modal .colorful-bg,
|
||||
.aksrt-after-sale-detail-modal .colorful-bg,
|
||||
.aksrt-after-sale-expired-modal .colorful-bg,
|
||||
.aksrt-after-sale-withdraw-modal .colorful-bg,
|
||||
.aksrt-order-detail-modal .colorful-bg {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
@@ -651,6 +655,7 @@ body.aksrt-modal-runtime-open {
|
||||
.aksrt-after-sale-apply-modal .modal-colorful-header,
|
||||
.aksrt-after-sale-detail-modal .modal-colorful-header,
|
||||
.aksrt-after-sale-expired-modal .modal-colorful-header,
|
||||
.aksrt-after-sale-withdraw-modal .modal-colorful-header,
|
||||
.aksrt-order-detail-modal .modal-colorful-header {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
@@ -666,6 +671,7 @@ body.aksrt-modal-runtime-open {
|
||||
.aksrt-after-sale-apply-modal .modal-colorful-header + *,
|
||||
.aksrt-after-sale-detail-modal .modal-colorful-header + *,
|
||||
.aksrt-after-sale-expired-modal .modal-colorful-header + *,
|
||||
.aksrt-after-sale-withdraw-modal .modal-colorful-header + *,
|
||||
.aksrt-order-detail-modal .modal-colorful-header + * {
|
||||
margin-top: 120px;
|
||||
}
|
||||
@@ -673,6 +679,7 @@ body.aksrt-modal-runtime-open {
|
||||
.aksrt-after-sale-apply-modal .modal-colorful-header > .close,
|
||||
.aksrt-after-sale-detail-modal .modal-colorful-header > .close,
|
||||
.aksrt-after-sale-expired-modal .modal-colorful-header > .close,
|
||||
.aksrt-after-sale-withdraw-modal .modal-colorful-header > .close,
|
||||
.aksrt-order-detail-modal .modal-colorful-header > .close {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
@@ -690,10 +697,13 @@ body.aksrt-modal-runtime-open {
|
||||
.aksrt-after-sale-detail-modal .colorful-bg > .colorful-make::before,
|
||||
.aksrt-after-sale-detail-modal .colorful-bg > .colorful-make::after,
|
||||
.aksrt-after-sale-expired-modal .colorful-bg > .colorful-make,
|
||||
.aksrt-after-sale-withdraw-modal .colorful-bg > .colorful-make,
|
||||
.aksrt-order-detail-modal .colorful-bg > .colorful-make,
|
||||
.aksrt-after-sale-expired-modal .colorful-bg > .colorful-make::before,
|
||||
.aksrt-after-sale-withdraw-modal .colorful-bg > .colorful-make::before,
|
||||
.aksrt-order-detail-modal .colorful-bg > .colorful-make::before,
|
||||
.aksrt-after-sale-expired-modal .colorful-bg > .colorful-make::after,
|
||||
.aksrt-after-sale-withdraw-modal .colorful-bg > .colorful-make::after,
|
||||
.aksrt-order-detail-modal .colorful-bg > .colorful-make::after {
|
||||
position: absolute;
|
||||
color: var(--this-color);
|
||||
@@ -705,6 +715,7 @@ body.aksrt-modal-runtime-open {
|
||||
.aksrt-after-sale-apply-modal .colorful-bg > .colorful-make,
|
||||
.aksrt-after-sale-detail-modal .colorful-bg > .colorful-make,
|
||||
.aksrt-after-sale-expired-modal .colorful-bg > .colorful-make,
|
||||
.aksrt-after-sale-withdraw-modal .colorful-bg > .colorful-make,
|
||||
.aksrt-order-detail-modal .colorful-bg > .colorful-make {
|
||||
top: 60%;
|
||||
bottom: 0;
|
||||
@@ -716,6 +727,7 @@ body.aksrt-modal-runtime-open {
|
||||
.aksrt-after-sale-apply-modal .colorful-bg > .colorful-make::before,
|
||||
.aksrt-after-sale-detail-modal .colorful-bg > .colorful-make::before,
|
||||
.aksrt-after-sale-expired-modal .colorful-bg > .colorful-make::before,
|
||||
.aksrt-after-sale-withdraw-modal .colorful-bg > .colorful-make::before,
|
||||
.aksrt-order-detail-modal .colorful-bg > .colorful-make::before {
|
||||
top: -325px;
|
||||
right: -558px;
|
||||
@@ -729,6 +741,7 @@ body.aksrt-modal-runtime-open {
|
||||
.aksrt-after-sale-apply-modal .colorful-bg > .colorful-make::after,
|
||||
.aksrt-after-sale-detail-modal .colorful-bg > .colorful-make::after,
|
||||
.aksrt-after-sale-expired-modal .colorful-bg > .colorful-make::after,
|
||||
.aksrt-after-sale-withdraw-modal .colorful-bg > .colorful-make::after,
|
||||
.aksrt-order-detail-modal .colorful-bg > .colorful-make::after {
|
||||
top: -170px;
|
||||
bottom: 0;
|
||||
@@ -802,3 +815,58 @@ body.aksrt-modal-runtime-open {
|
||||
.aksrt-after-sale-status.is-completed { color: var(--color-success, #10b981); }
|
||||
.aksrt-after-sale-status.is-rejected,
|
||||
.aksrt-after-sale-status.is-cancelled { color: var(--color-error, #ef4444); }
|
||||
|
||||
/* 状态行右侧单元格:状态徽章 + 「撤销售后」入口(仅待审核工单渲染)。 */
|
||||
.aksrt-after-sale-detail__statusCell { display: inline-flex; align-items: center; gap: 10px; }
|
||||
.aksrt-after-sale-withdraw { display: inline-flex; align-items: center; gap: 3px; padding: 0; border: 0; background: transparent; color: var(--modal-danger, #ef4444); font-size: 12px; font-weight: 600; line-height: 1.4; cursor: pointer; transition: color .15s ease; }
|
||||
.aksrt-after-sale-withdraw .fa { font-size: 12px; }
|
||||
.aksrt-after-sale-withdraw:hover { color: color-mix(in srgb, var(--modal-danger) 78%, var(--modal-ink)); }
|
||||
|
||||
/* 售后提示弹窗(撤销售后确认)正文。 */
|
||||
.aksrt-after-sale-withdraw__body { margin-bottom: 16px; color: var(--color-ink-2, #4e5358); line-height: 1.7; }
|
||||
|
||||
/* 售后弹窗底部按钮:全宽平铺贴底(负 margin 抵消 __content 内边距)+ 分隔线,
|
||||
子按钮去独立背景/边框/圆角(文字色区分主次),覆盖上方全局按钮网格的胶囊观感;
|
||||
仅在售后弹窗家族生效,其他 runtime 弹窗仍走全局网格样式。 */
|
||||
.aksrt-after-sale-apply-modal .but-average.modal-buts,
|
||||
.aksrt-after-sale-detail-modal .but-average.modal-buts,
|
||||
.aksrt-after-sale-expired-modal .but-average.modal-buts,
|
||||
.aksrt-after-sale-withdraw-modal .but-average.modal-buts {
|
||||
gap: 0;
|
||||
margin: 16px -18px -16px;
|
||||
padding: 12px 18px;
|
||||
border-top: 1px solid var(--modal-line-soft, rgba(50, 50, 50, 0.06));
|
||||
background: var(--main-bg-color, #fff);
|
||||
}
|
||||
|
||||
.aksrt-after-sale-apply-modal .but-average.modal-buts > .but,
|
||||
.aksrt-after-sale-detail-modal .but-average.modal-buts > .but,
|
||||
.aksrt-after-sale-expired-modal .but-average.modal-buts > .but,
|
||||
.aksrt-after-sale-withdraw-modal .but-average.modal-buts > .but {
|
||||
min-height: 38px;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
background: transparent;
|
||||
color: var(--modal-muted, #777);
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
--this-bg: transparent;
|
||||
--this-border: transparent;
|
||||
--this-shadow: none;
|
||||
}
|
||||
|
||||
.aksrt-after-sale-apply-modal .but-average.modal-buts > .but.c-blue,
|
||||
.aksrt-after-sale-detail-modal .but-average.modal-buts > .but.c-blue,
|
||||
.aksrt-after-sale-expired-modal .but-average.modal-buts > .but.c-blue,
|
||||
.aksrt-after-sale-withdraw-modal .but-average.modal-buts > .but.c-blue {
|
||||
background: transparent;
|
||||
border: 0;
|
||||
color: var(--modal-primary, #f04494);
|
||||
}
|
||||
|
||||
/* 单按钮 footer 占满整行(全局网格是 1fr 1fr,单按钮会只占一半)。 */
|
||||
.aksrt-after-sale-apply-modal .but-average.modal-buts > .but:only-child,
|
||||
.aksrt-after-sale-detail-modal .but-average.modal-buts > .but:only-child,
|
||||
.aksrt-after-sale-expired-modal .but-average.modal-buts > .but:only-child,
|
||||
.aksrt-after-sale-withdraw-modal .but-average.modal-buts > .but:only-child { grid-column: 1 / -1; }
|
||||
|
||||
@@ -114,6 +114,10 @@
|
||||
border-radius: var(--main-radius, 8px);
|
||||
background: var(--main-bg-color, #fff);
|
||||
}
|
||||
/* 滚动发生在 dialog 本体(modal-runtime 的 overflow:auto),内容超高时右缘会露出
|
||||
滚动栏;与售后/申诉弹窗口径一致:完全隐藏滚动条(含细滚动条),滚动行为不变。 */
|
||||
.aksrt-order-detail-modal .aksrt-modal-runtime__dialog { -ms-overflow-style: none; scrollbar-width: none; }
|
||||
.aksrt-order-detail-modal .aksrt-modal-runtime__dialog::-webkit-scrollbar { display: none; }
|
||||
.aksrt-order-detail-modal .aksrt-modal-runtime__content { display: flex; flex-direction: column; gap: 18px; padding: 16px 18px 0; }
|
||||
|
||||
.aksrt-order-detail__skeleton { display: grid; gap: 12px; padding: 4px 0; }
|
||||
|
||||
@@ -4,9 +4,12 @@
|
||||
* 与「禁封/举报」链路同构(report.js / user-ban.js):
|
||||
* - [data-aksrt-after-sale-apply] → 申请售后弹窗(超时效则改为超时效提示弹窗)
|
||||
* - [data-aksrt-after-sale-detail] → 售后详细弹窗(可再次申请 / 不可再申请提示)
|
||||
* - [data-aksrt-after-sale-withdraw] → 撤销售后二次确认弹窗(仅待审核工单),确认后 DELETE 工单
|
||||
* 两个入口的 data-* 属性携带订单与售后状态载荷(同 report.js 的 data-aksrt-report),
|
||||
* 点击即同步弹窗,不做前置请求(避免首次点击无反馈)。
|
||||
* - 提交走 REST /aksrt/v1/orders/{order_number}/after-sale
|
||||
* - 撤销走 DELETE 同端点(仅待审核工单,后端置为已取消)
|
||||
* - 提交与撤销前先过人机验证(after_sale_apply / after_sale_cancel,场景未启用自动放行)
|
||||
* - 举证图片走 /orders/{order_number}/after-sale/upload-image(复用评论图片上传校验)
|
||||
*
|
||||
* 按钮形态由订单视图模型的 after_sale 状态决定(UserOrders::after_sale_state):
|
||||
@@ -178,8 +181,13 @@
|
||||
|
||||
html += '<div class="aksrt-after-sale-detail__statusRow">' +
|
||||
'<span class="muted-2-color em09">售后状态</span>' +
|
||||
'<span class="aksrt-after-sale-detail__statusCell">' +
|
||||
'<span class="badg p2-10 aksrt-after-sale-status is-' + escapeHtml(status) + '">' + escapeHtml(STATUS_LABELS[status] || status) + '</span>' +
|
||||
'</div>';
|
||||
// 仅「待审核」可撤销(后端 cancel 仅放行 pending):入口随状态徽章渲染。
|
||||
(status === 'pending'
|
||||
? '<button type="button" class="aksrt-after-sale-withdraw" data-aksrt-after-sale-withdraw="' + escapeHtml((order && order.order_number) || '') + '">撤销售后<i class="fa fas fa-angle-right" aria-hidden="true"></i></button>'
|
||||
: '') +
|
||||
'</span></div>';
|
||||
|
||||
html += '<div class="aksrt-after-sale-detail__rows">';
|
||||
html += detailRow('申请时间', String(latest.created_at || ''));
|
||||
@@ -238,6 +246,19 @@
|
||||
return html;
|
||||
}
|
||||
|
||||
// 撤销售后二次确认弹窗:待审核工单入口点入,确认后 DELETE 售后工单。
|
||||
function buildWithdrawHtml(orderNumber) {
|
||||
var html = '<div class="aksrt-after-sale-withdraw-modal">';
|
||||
html += colorfulHeader('撤销售后', 'fa-undo-alt');
|
||||
html += '<div class="aksrt-after-sale-withdraw__body em09">撤销后本次售后申请将关闭,订单恢复为可再次申请售后的状态。确定撤销吗?</div>';
|
||||
html += '<div class="but-average modal-buts">' +
|
||||
'<button type="button" class="but" data-aksrt-modal-close>取消</button>' +
|
||||
'<button type="button" class="but c-blue" data-aksrt-after-sale-withdraw-confirm="' + escapeHtml(orderNumber) + '">确认撤销</button>' +
|
||||
'</div>';
|
||||
html += '</div>';
|
||||
return html;
|
||||
}
|
||||
|
||||
function openModal(options) {
|
||||
if (!window.AksrtModalRuntime || typeof window.AksrtModalRuntime.open !== 'function') {
|
||||
notyf('售后组件加载失败,请刷新页面后重试', 'danger');
|
||||
@@ -277,6 +298,15 @@
|
||||
});
|
||||
}
|
||||
|
||||
function openWithdraw(orderNumber) {
|
||||
if (!orderNumber) return;
|
||||
return openModal({
|
||||
html: buildWithdrawHtml(orderNumber),
|
||||
className: 'aksrt-after-sale-withdraw-modal',
|
||||
kind: 'after-sale-withdraw',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析按钮 data-* 上的售后载荷(与举报链路 data-aksrt-report 同模式):
|
||||
* 入口数据随列表一起渲染,点击即可同步弹窗,不做前置请求。
|
||||
@@ -407,6 +437,22 @@
|
||||
return parts.join('\n');
|
||||
}
|
||||
|
||||
function clearToast(id) {
|
||||
var toast = document.getElementById(id);
|
||||
if (toast) toast.remove();
|
||||
}
|
||||
|
||||
// 提交前的人机验证:场景未启用时无挂载点,run 直接 resolve {};
|
||||
// 用户中途取消验证时 run resolve null,此时还原按钮并中止提交。
|
||||
function runCaptcha(scene) {
|
||||
if (!window.AKSRTCaptcha || typeof window.AKSRTCaptcha.run !== 'function') {
|
||||
return Promise.resolve({});
|
||||
}
|
||||
return window.AKSRTCaptcha.run(scene).then(function (payload) {
|
||||
return payload === null ? Promise.reject({ cancelled: true }) : (payload || {});
|
||||
});
|
||||
}
|
||||
|
||||
function submitApply(shell) {
|
||||
var form = shell.querySelector('[data-aksrt-after-sale-order]');
|
||||
var orderNumber = form ? form.getAttribute('data-aksrt-after-sale-order') : '';
|
||||
@@ -427,19 +473,23 @@
|
||||
if (submitBtn) submitBtn.disabled = true;
|
||||
notyf('正在提交售后申请…', 'load', 3000, 'aksrt-after-sale-submit');
|
||||
|
||||
var restoreButton = function () {
|
||||
if (submitBtn) submitBtn.disabled = false;
|
||||
};
|
||||
var sendApply = function (captchaPayload) {
|
||||
window.fetch(API_ROOT + '/orders/' + encodeURIComponent(orderNumber) + '/after-sale', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-WP-Nonce': NONCE,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
body: JSON.stringify(Object.assign({
|
||||
reason: composeReason(shell),
|
||||
reason_label: selectedReasonLabel(shell),
|
||||
materials: collectMaterials(shell),
|
||||
description: reasonText,
|
||||
images: collectImages(shell),
|
||||
}),
|
||||
}, captchaPayload)),
|
||||
})
|
||||
.then(function (res) { return res.json(); })
|
||||
.then(function (result) {
|
||||
@@ -450,17 +500,69 @@
|
||||
window.AKSRTOrderCenter.refresh(orderNumber);
|
||||
}
|
||||
} else {
|
||||
if (submitBtn) submitBtn.disabled = false;
|
||||
restoreButton();
|
||||
notyf((result && result.message) || '售后申请提交失败', 'danger');
|
||||
}
|
||||
})
|
||||
.catch(function () {
|
||||
if (submitBtn) submitBtn.disabled = false;
|
||||
restoreButton();
|
||||
notyf('网络错误,请重试', 'danger');
|
||||
})
|
||||
.then(function () {
|
||||
var toast = document.getElementById('aksrt-after-sale-submit');
|
||||
if (toast) toast.remove();
|
||||
.then(function () { clearToast('aksrt-after-sale-submit'); });
|
||||
};
|
||||
|
||||
runCaptcha('after_sale_apply').then(sendApply).catch(function (error) {
|
||||
restoreButton();
|
||||
clearToast('aksrt-after-sale-submit');
|
||||
if (error && error.cancelled) return;
|
||||
notyf('人机验证失败,请重试', 'danger');
|
||||
});
|
||||
}
|
||||
|
||||
// 撤销售后:待审核工单置为已取消(DELETE 售后工单),成功后刷新订单列表与统计。
|
||||
function submitWithdraw(shell, button) {
|
||||
var orderNumber = button.getAttribute('data-aksrt-after-sale-withdraw-confirm') || '';
|
||||
if (!orderNumber || button.disabled) return;
|
||||
button.disabled = true;
|
||||
notyf('正在撤销售后申请…', 'load', 3000, 'aksrt-after-sale-withdraw');
|
||||
|
||||
var restoreButton = function () {
|
||||
button.disabled = false;
|
||||
};
|
||||
var sendWithdraw = function (captchaPayload) {
|
||||
window.fetch(API_ROOT + '/orders/' + encodeURIComponent(orderNumber) + '/after-sale', {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-WP-Nonce': NONCE,
|
||||
},
|
||||
body: JSON.stringify(captchaPayload || {}),
|
||||
})
|
||||
.then(function (res) { return res.json(); })
|
||||
.then(function (result) {
|
||||
if (result && result.success) {
|
||||
window.AksrtModalRuntime.close('withdraw');
|
||||
notyf('售后申请已撤销', 'success');
|
||||
if (window.AKSRTOrderCenter && typeof window.AKSRTOrderCenter.refresh === 'function') {
|
||||
window.AKSRTOrderCenter.refresh(orderNumber);
|
||||
}
|
||||
} else {
|
||||
restoreButton();
|
||||
notyf((result && result.message) || '撤销失败,请重试', 'danger');
|
||||
}
|
||||
})
|
||||
.catch(function () {
|
||||
restoreButton();
|
||||
notyf('网络错误,请重试', 'danger');
|
||||
})
|
||||
.then(function () { clearToast('aksrt-after-sale-withdraw'); });
|
||||
};
|
||||
|
||||
runCaptcha('after_sale_cancel').then(sendWithdraw).catch(function (error) {
|
||||
restoreButton();
|
||||
clearToast('aksrt-after-sale-withdraw');
|
||||
if (error && error.cancelled) return;
|
||||
notyf('人机验证失败,请重试', 'danger');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -509,6 +611,21 @@
|
||||
var payload = parsePayload(reapplyBtn.getAttribute('data-aksrt-after-sale-payload'));
|
||||
window.AksrtModalRuntime.close('replace');
|
||||
openApplyFlow(payload);
|
||||
return;
|
||||
}
|
||||
|
||||
// 撤销售后:入口在售后详细弹窗状态行(仅待审核),点击先弹二次确认。
|
||||
var withdrawBtn = event.target.closest('[data-aksrt-after-sale-withdraw]');
|
||||
if (withdrawBtn) {
|
||||
event.preventDefault();
|
||||
openWithdraw(withdrawBtn.getAttribute('data-aksrt-after-sale-withdraw') || '');
|
||||
return;
|
||||
}
|
||||
|
||||
var withdrawConfirm = event.target.closest('[data-aksrt-after-sale-withdraw-confirm]');
|
||||
if (withdrawConfirm) {
|
||||
event.preventDefault();
|
||||
submitWithdraw(shell, withdrawConfirm);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -237,7 +237,6 @@
|
||||
}));
|
||||
html += '<div class="aksrt-order-detail__after-sale-foot">';
|
||||
html += '<button class="btn-ghost" type="button" data-aksrt-after-sale-detail="' + recordPayload + '">查看详细</button>';
|
||||
if (asStatus === "pending") html += '<button class="btn-ghost" type="button" data-aksrt-after-sale-cancel="' + escapeHtml(order.order_number) + '">取消申请</button>';
|
||||
html += "</div>";
|
||||
html += "</li>";
|
||||
});
|
||||
@@ -662,25 +661,7 @@
|
||||
state.detailReplacementSession = null;
|
||||
};
|
||||
state.onDocumentClick = function (event) {
|
||||
var afterSaleCancel = closest(event.target, "[data-aksrt-after-sale-cancel]");
|
||||
if (afterSaleCancel && !event.__aksrtAfterSaleCancelHandled) {
|
||||
var afterSaleNumber = afterSaleCancel.getAttribute("data-aksrt-after-sale-cancel") || "";
|
||||
if (activeDetail(state, afterSaleNumber)) {
|
||||
event.__aksrtAfterSaleCancelHandled = true;
|
||||
event.preventDefault();
|
||||
afterSaleCancel.disabled = true;
|
||||
request(orderPath(afterSaleNumber, "/after-sale"), undefined, "DELETE", window.AksrtRequest.createIdempotencyKey()).then(function () {
|
||||
notyf("售后申请已取消", "success");
|
||||
invalidateOrder(afterSaleNumber);
|
||||
openDetail(state, afterSaleNumber);
|
||||
refreshView(state);
|
||||
}).catch(function (error) {
|
||||
afterSaleCancel.disabled = false;
|
||||
notyf(errorMessage(error, "取消申请失败"), "danger");
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 取消/撤销售后已移入售后详细弹窗(order-after-sale.js 的「撤销售后」入口)。
|
||||
if (!state.detailSession || window.AksrtModalRuntime.getActive() !== state.detailSession.shell) return;
|
||||
var copy = closest(event.target, "[data-aksrt-order-copy]");
|
||||
if (copy) {
|
||||
|
||||
@@ -150,4 +150,13 @@ $status_options = [
|
||||
<div class="aksrt-orders-page__empty" data-aksrt-order-list><i class="fa fas fa-file-alt" aria-hidden="true"></i><h3><?php esc_html_e('还没有购买记录', 'aksrt'); ?></h3><p><?php esc_html_e('购买内容或开通权益后,订单会出现在这里。', 'aksrt'); ?></p></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php
|
||||
// 售后人机验证挂载点(申请 / 撤销):场景未启用时 aksrt_render_captcha 无输出,
|
||||
// 前端 order-after-sale.js 以挂载点存在与否决定是否先过验证。
|
||||
if (function_exists('aksrt_render_captcha')) {
|
||||
aksrt_render_captcha('after_sale_apply');
|
||||
aksrt_render_captcha('after_sale_cancel');
|
||||
}
|
||||
?>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -375,6 +375,8 @@ function aksrt_captcha_scenes(): array {
|
||||
'signin' => __('每日签到', 'aksrt'),
|
||||
'front_submit' => __('前台投稿', 'aksrt'),
|
||||
'tip_user' => __('打赏用户', 'aksrt'),
|
||||
'after_sale_apply' => __('申请售后', 'aksrt'),
|
||||
'after_sale_cancel' => __('撤销售后', 'aksrt'),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -81,6 +81,8 @@ class SchemaPermissions {
|
||||
['label' => '每日签到', 'value' => 'signin'],
|
||||
['label' => '前台投稿', 'value' => 'front_submit'],
|
||||
['label' => '打赏用户', 'value' => 'tip_user'],
|
||||
['label' => '申请售后', 'value' => 'after_sale_apply'],
|
||||
['label' => '撤销售后', 'value' => 'after_sale_cancel'],
|
||||
]],
|
||||
]],
|
||||
['key' => 'providers', 'title' => '服务商配置', 'dependsOn' => ['key' => 'enabled', 'value' => true], 'fields' => $provider_fields],
|
||||
|
||||
@@ -93,6 +93,10 @@ final class AfterSaleService {
|
||||
}
|
||||
|
||||
public function rest_submit(\WP_REST_Request $request) {
|
||||
$captcha = $this->require_captcha($request, 'after_sale_apply');
|
||||
if (is_wp_error($captcha)) {
|
||||
return $captcha;
|
||||
}
|
||||
$images = $request->get_param('images');
|
||||
$materials = $request->get_param('materials');
|
||||
$result = $this->create(
|
||||
@@ -116,10 +120,23 @@ final class AfterSaleService {
|
||||
}
|
||||
|
||||
public function rest_cancel(\WP_REST_Request $request) {
|
||||
$captcha = $this->require_captcha($request, 'after_sale_cancel');
|
||||
if (is_wp_error($captcha)) {
|
||||
return $captcha;
|
||||
}
|
||||
$result = $this->cancel_for_user(get_current_user_id(), (string) $request->get_param('order_number'));
|
||||
return is_wp_error($result) ? $result : rest_ensure_response(['success' => true, 'data' => $result]);
|
||||
}
|
||||
|
||||
/** 人机验证(申请售后 / 撤销售后):场景未启用时 aksrt_require_captcha 直接放行。 */
|
||||
private function require_captcha(\WP_REST_Request $request, string $scene) {
|
||||
if (!function_exists('aksrt_require_captcha')) {
|
||||
return true;
|
||||
}
|
||||
$params = $request->get_json_params();
|
||||
return aksrt_require_captcha($scene, is_array($params) ? $params : []);
|
||||
}
|
||||
|
||||
/** @param array<string,mixed> $apply_form */
|
||||
public function create(int $user_id, string $order_number, string $reason, array $images = [], array $apply_form = []) {
|
||||
$reason = sanitize_textarea_field($reason);
|
||||
|
||||
@@ -257,10 +257,11 @@ test('order view model exposes the after-sale button matrix', () => {
|
||||
test('frontend after-sale modals are standalone and config-driven', () => {
|
||||
const js = read('assets/js/order-after-sale.js')
|
||||
|
||||
// 三个独立弹窗 + modal-mini 几何 + 蓝色炫彩标题区
|
||||
// 四个独立弹窗 + modal-mini 几何 + 蓝色炫彩标题区
|
||||
assert.match(js, /className: 'aksrt-after-sale-apply-modal'/)
|
||||
assert.match(js, /className: 'aksrt-after-sale-detail-modal'/)
|
||||
assert.match(js, /className: 'aksrt-after-sale-expired-modal'/)
|
||||
assert.match(js, /className: 'aksrt-after-sale-withdraw-modal'/)
|
||||
assert.match(js, /modal-colorful-header colorful-bg jb-blue/)
|
||||
assert.match(js, /AksrtModalRuntime\.open\(\{/)
|
||||
|
||||
@@ -307,6 +308,21 @@ test('frontend after-sale modals are standalone and config-driven', () => {
|
||||
assert.match(js, /data-aksrt-modal-close>我知道了</)
|
||||
assert.match(js, /data-aksrt-after-sale-reapply data-aksrt-after-sale-payload="/)
|
||||
|
||||
// 撤销售后:入口仅随待审核状态渲染,点击先过二次确认弹窗,确认后 DELETE 工单
|
||||
assert.match(js, /status === 'pending'[\s\S]*?data-aksrt-after-sale-withdraw="' \+ escapeHtml\(\(order && order\.order_number\) \|\| ''\)/)
|
||||
assert.match(js, /buildWithdrawHtml\(orderNumber\)/)
|
||||
assert.match(js, /className: 'aksrt-after-sale-withdraw-modal'/)
|
||||
assert.match(js, /data-aksrt-after-sale-withdraw-confirm="' \+ escapeHtml\(orderNumber\) \+ '"/)
|
||||
assert.match(js, /method: 'DELETE'/)
|
||||
assert.match(js, /AksrtModalRuntime\.close\('withdraw'\)/)
|
||||
assert.match(js, /notyf\('售后申请已撤销', 'success'\)/)
|
||||
|
||||
// 人机验证:提交与撤销前先过验证(场景未启用时挂载点缺失,run 直接放行)
|
||||
assert.match(js, /function runCaptcha\(scene\)/)
|
||||
assert.match(js, /runCaptcha\('after_sale_apply'\)/)
|
||||
assert.match(js, /runCaptcha\('after_sale_cancel'\)/)
|
||||
assert.match(js, /AKSRTCaptcha\.run\(scene\)/)
|
||||
|
||||
// 提交后关闭弹窗并刷新订单列表
|
||||
assert.match(js, /AKSRTOrderCenter\.refresh\(orderNumber\)/)
|
||||
assert.match(js, /window\.AksrtModalRuntime\.close\('submit'\)/)
|
||||
@@ -343,9 +359,12 @@ test('order center drops the embedded after-sale form and exposes refresh()', ()
|
||||
assert.match(js, /data-aksrt-after-sale-detail="/)
|
||||
assert.match(js, /data-aksrt-after-sale-apply="/)
|
||||
|
||||
// 售后记录区与取消申请保留在订单详情弹窗
|
||||
// 售后记录区保留在订单详情弹窗:记录卡片带申请时间,「查看详细」逐条入口;
|
||||
// 取消申请按钮与处理器已移除(撤销入口移入售后详细弹窗)。
|
||||
assert.match(js, /售后记录/)
|
||||
assert.match(js, /data-aksrt-after-sale-cancel/)
|
||||
assert.match(js, /<time>" \+ escapeHtml\(item\.created_at\)/)
|
||||
assert.ok(!js.includes('data-aksrt-after-sale-cancel'), 'cancel entry moved into the after-sale detail modal')
|
||||
assert.ok(!js.includes('取消申请'), 'order detail must not render the legacy cancel button')
|
||||
|
||||
// refresh 供售后弹窗提交后刷新列表与统计
|
||||
assert.match(js, /function refresh\(orderNumber\) \{/)
|
||||
@@ -373,14 +392,30 @@ test('after-sale assets, template buttons and runtime data are wired', () => {
|
||||
assert.match(template, /esc_html_e\('售后详细', 'aksrt'\)/)
|
||||
assert.ok(!template.includes('can_after_sale'), 'template must not use the removed can_after_sale flag')
|
||||
|
||||
// 售后人机验证挂载点(申请 / 撤销):场景未启用时 aksrt_render_captcha 无输出
|
||||
assert.match(template, /function_exists\('aksrt_render_captcha'\)/)
|
||||
assert.match(template, /aksrt_render_captcha\('after_sale_apply'\)/)
|
||||
assert.match(template, /aksrt_render_captcha\('after_sale_cancel'\)/)
|
||||
|
||||
const css = read('assets/css/frontend/modal-runtime.css')
|
||||
assert.match(css, /\.aksrt-after-sale-apply-modal:not\(\.aksrt-modal-runtime--bottom\) \.aksrt-modal-runtime__dialog/)
|
||||
assert.match(css, /\.aksrt-after-sale-detail-modal:not\(\.aksrt-modal-runtime--bottom\) \.aksrt-modal-runtime__dialog/)
|
||||
assert.match(css, /\.aksrt-after-sale-expired-modal:not\(\.aksrt-modal-runtime--bottom\) \.aksrt-modal-runtime__dialog/)
|
||||
assert.match(css, /\.aksrt-after-sale-withdraw-modal:not\(\.aksrt-modal-runtime--bottom\) \.aksrt-modal-runtime__dialog/)
|
||||
assert.match(css, /width: min\(400px, calc\(100vw - 32px\)\)/)
|
||||
assert.match(css, /\.aksrt-after-sale-apply-modal \.modal-colorful-header/)
|
||||
assert.match(css, /\.aksrt-after-sale-apply-modal \.colorful-bg > \.colorful-make/)
|
||||
|
||||
// 弹窗底部按钮:四个售后弹窗统一全宽平铺贴底,子按钮去独立胶囊观感(覆盖全局按钮网格)
|
||||
for (const modal of ['apply', 'detail', 'expired', 'withdraw']) {
|
||||
assert.match(css, new RegExp(`\\.aksrt-after-sale-${modal}-modal \\.but-average\\.modal-buts[\\s\\S]*?margin: 16px -18px -16px`))
|
||||
assert.match(css, new RegExp(`\\.aksrt-after-sale-${modal}-modal \\.but-average\\.modal-buts > \\.but[\\s\\S]*?border-radius: 0`))
|
||||
}
|
||||
assert.match(css, /\.but:only-child \{ grid-column: 1 \/ -1; \}/)
|
||||
// 撤销售后入口:待审核状态行内的文字式入口按钮
|
||||
assert.match(css, /\.aksrt-after-sale-detail__statusCell/)
|
||||
assert.match(css, /\.aksrt-after-sale-withdraw \{[^}]*background: transparent/)
|
||||
|
||||
const field = read('assets/admin/src/components/form/AksField.vue')
|
||||
assert.match(field, /import draggable from 'vuedraggable'/)
|
||||
assert.match(field, /v-model="repeaterDragList"/)
|
||||
|
||||
@@ -61,3 +61,28 @@ test('legacy public SMS-code AJAX surface is removed while SMS service remains',
|
||||
assert.doesNotMatch(sms, /function ajax_send_code\s*\(/);
|
||||
assert.match(sms, /public function send_code\(string \$phone, string \$code\): array/);
|
||||
});
|
||||
|
||||
test('after-sale submit and withdraw endpoints verify captcha once per request', () => {
|
||||
const service = read('src/Payment/AfterSaleService.php');
|
||||
|
||||
// 两个新场景已在场景白名单与后台「启用场景」勾选列表同步注册
|
||||
const scenes = read('inc/modules/captcha.php');
|
||||
assert.match(scenes, /'after_sale_apply'\s*=> __\('申请售后', 'aksrt'\)/);
|
||||
assert.match(scenes, /'after_sale_cancel'\s*=> __\('撤销售后', 'aksrt'\)/);
|
||||
const permissions = read('src/Admin/Schema/SchemaPermissions.php');
|
||||
assert.match(permissions, /'label' => '申请售后', 'value' => 'after_sale_apply'/);
|
||||
assert.match(permissions, /'label' => '撤销售后', 'value' => 'after_sale_cancel'/);
|
||||
const adminSchema = read('assets/admin/src/schema/sections/permissions/index.js');
|
||||
assert.match(adminSchema, /label: '申请售后', value: 'after_sale_apply'/);
|
||||
assert.match(adminSchema, /label: '撤销售后', value: 'after_sale_cancel'/);
|
||||
|
||||
// REST 提交 / 撤销各过一次场景校验(统一入口 helper 内恰一次 aksrt_require_captcha);
|
||||
// 场景未启用时 aksrt_require_captcha 直接放行
|
||||
const submit = service.slice(service.indexOf('public function rest_submit'), service.indexOf('public function rest_list'));
|
||||
const cancel = service.slice(service.indexOf('public function rest_cancel'), service.indexOf('public function create'));
|
||||
assert.match(submit, /require_captcha\(\$request, 'after_sale_apply'\)/);
|
||||
assert.match(cancel, /require_captcha\(\$request, 'after_sale_cancel'\)/);
|
||||
assert.equal((service.match(/require_captcha\(\$request, '/g) || []).length, 2);
|
||||
assert.equal((service.match(/aksrt_require_captcha\(/g) || []).length, 1);
|
||||
assert.match(service, /function_exists\('aksrt_require_captcha'\)/);
|
||||
});
|
||||
|
||||
@@ -592,6 +592,10 @@ test('profile routes load versioned leaf styles and settings dialogs use the sha
|
||||
assert.match(orderCenterJs, /data-aksrt-order-filter/);
|
||||
assert.match(orderCenterJs, /data-aksrt-order-page/);
|
||||
assert.doesNotMatch(orderCenterJs, /new URL\(window\.location\.href\)/);
|
||||
// 订单详情弹窗滚动发生在 runtime dialog 本体:滚动条必须完全隐藏(含细滚动条),滚动行为不变。
|
||||
assert.match(ordersCss, /\.aksrt-order-detail-modal \.aksrt-modal-runtime__dialog\s*\{[^}]*scrollbar-width:\s*none/);
|
||||
assert.match(ordersCss, /\.aksrt-order-detail-modal \.aksrt-modal-runtime__dialog::-webkit-scrollbar\s*\{\s*display:\s*none;\s*\}/);
|
||||
assert.doesNotMatch(ordersCss, /aksrt-order-detail-modal[^{}]*mini-scrollbar/);
|
||||
assert.doesNotMatch(userCenterJs, /\['order_status', 'order_type', 'order_number', 'order_page'\]/);
|
||||
assert.match(userCenterJs, /initialTab === 'orders'[\s\S]*?window\.history\.replaceState/);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user