@@ -223,10 +270,19 @@ function TaskDetail({ task, close, onConfirmRefund }) {
请确认已按用户上传的收款码完成付款。确认后,该任务将归入“已完成”,此操作不可撤销。
+
{loading ? (
@@ -590,9 +654,10 @@ export default function TaskBoardPage({ api }) {
{task.product_name}{maskPhone(task.phone)}
{task.task_type_label}
{task.qa_count}
-
{task.pending_review_count} / {task.completed_count}
+
{task.completed_count}
{task.status_label}
{task.creator_name}{formatTime(task.created_at)}
+
{task.refund_screenshot_count ? "已上传" : "--"}
{task.can_cancel && (
@@ -614,9 +679,10 @@ export default function TaskBoardPage({ api }) {
{child.product_name}{maskPhone(child.phone)}
提问任务
{child.qa_count}
- {child.pending_review_count} / {child.completed_count}
+ {child.completed_count}
{child.status_label}
{child.creator_name}{formatTime(child.created_at)}
+ {child.refund_proof_uploaded ? "已上传" : child.refund_endpoint ? "待上传" : "--"}
))}
diff --git a/frontend/src/admin.css b/frontend/src/admin.css
index ce6285b..9044a6a 100644
--- a/frontend/src/admin.css
+++ b/frontend/src/admin.css
@@ -11,6 +11,12 @@
.ask-exhausted-card p{max-width:330px;margin:0;color:#71859e;font-size:13px;line-height:1.8}
.ask-exhausted-card small{margin:22px 0 14px;color:#91a1b5}
.ask-exhausted-card button{width:100%;padding:12px;border-radius:10px;background:#1764e8;color:#fff;font-weight:700;box-shadow:0 7px 16px rgba(23,100,232,.2)}
+.ask-invalid-card{display:flex;align-items:center;flex-direction:column;width:min(420px,calc(100% - 32px));margin:90px auto 24px;padding:34px 24px;border:1px solid #b8d0f3;border-radius:20px;background:linear-gradient(180deg,#f8fbff,#fff);text-align:center;box-shadow:0 12px 32px rgba(26,70,135,.1)}
+.ask-invalid-icon{display:grid;place-items:center;width:72px;height:72px;margin-bottom:14px;border:1px solid #8bb4ef;border-radius:22px;background:#eaf3ff;color:#2864e8}
+.ask-invalid-card>em{padding:5px 12px;border-radius:999px;background:#eaf2ff;color:#2864e8;font-size:11px;font-style:normal;font-weight:700}
+.ask-invalid-card h1{margin:18px 0 8px;color:#102744;font-size:24px}
+.ask-invalid-card p{max-width:330px;margin:0;color:#71859e;font-size:13px;line-height:1.8}
+.ask-invalid-card button{width:100%;margin-top:22px;padding:12px;border-radius:10px;background:#1764e8;color:#fff;font-weight:700;box-shadow:0 7px 16px rgba(23,100,232,.2)}
.ask-claim-summary{margin:12px 16px;padding:14px 16px;border:1px solid #b9d4ff;border-radius:14px;background:linear-gradient(135deg,#f5f9ff,#eef5ff);color:#194b91}.ask-claim-summary>div:first-child{display:flex;align-items:center;justify-content:space-between;gap:10px}.ask-claim-summary strong{font-size:13px}.ask-claim-summary span,.ask-claim-summary small{color:#7188a5;font-size:11px}.ask-claim-summary>b{display:block;margin-top:8px;color:#1764e8;font-size:18px}.ask-claim-progress{height:7px;margin:8px 0;border-radius:9px;background:#dce8f8;overflow:hidden}.ask-claim-progress i{display:block;height:100%;border-radius:inherit;background:linear-gradient(90deg,#2166ef,#5b46ee)}
.ask-question-list>button.used{border-color:#d7e1ec;background:#f3f6fa;color:#7d8fa5;cursor:not-allowed;box-shadow:none}.ask-question-list>button.used i{border-color:#d0dbe7;background:#e6edf5;color:#8ca0b6}.ask-question-list>button em{margin-left:auto;padding:4px 7px;border-radius:8px;background:#e6edf5;color:#7288a2;font-size:10px;font-style:normal;white-space:nowrap}
.ask-status-page{padding:14px 16px 80px}.ask-status-page .ask-history-tabs{margin-top:0}.ask-status-page .ask-history-item{box-shadow:0 2px 7px rgba(32,62,102,.05)}
@@ -1867,7 +1873,7 @@
.board-table-head,
.board-table-row {
display: grid;
- grid-template-columns: 34px 1.05fr 1.05fr 1.9fr 0.75fr 0.55fr 0.75fr 0.75fr 1fr minmax(150px, 1.2fr);
+ grid-template-columns: 34px 1.05fr 1.05fr 1.9fr 0.75fr 0.55fr 0.75fr 0.75fr 1fr 0.75fr minmax(150px, 1.2fr);
align-items: center;
gap: 10px;
padding: 13px 14px;
@@ -1936,6 +1942,11 @@
.board-child-row .board-actions button {
color: #2864e8;
}
+.board-refund-proof {
+ color: #12965b;
+ font-size: 11px;
+ white-space: nowrap;
+}
.board-select-cell {
display: grid;
place-items: center;
@@ -2180,7 +2191,7 @@
.task-detail-table .head,
.task-detail-table .row {
display: grid;
- grid-template-columns: 45px 2fr 1fr 0.7fr;
+ grid-template-columns: 45px 2fr 1fr 0.7fr 0.9fr;
gap: 10px;
align-items: center;
padding: 11px;
@@ -2190,6 +2201,29 @@
color: #647b98;
font-size: 11px;
}
+.task-detail-table-body {
+ display: grid;
+ grid-template-columns: 45px 2fr 1fr 0.7fr 0.9fr;
+}
+.task-detail-cell {
+ display: grid;
+ min-width: 0;
+ align-content: center;
+ gap: 5px;
+ padding: 11px;
+ color: #536b88;
+ font-size: 11px;
+}
+.task-detail-cell.row-border {
+ border-top: 1px solid #edf1f6;
+}
+.task-detail-cell b {
+ color: #203c5e;
+}
+.task-detail-cell small {
+ color: #8094ac;
+ line-height: 1.5;
+}
.task-detail-table .row {
border-top: 1px solid #edf1f6;
color: #536b88;
@@ -2211,6 +2245,73 @@
text-align: center;
color: #8a9cb1;
}
+.task-detail-refund-proof button {
+ display: grid;
+ gap: 4px;
+ padding: 0;
+ background: transparent;
+ color: #12965b;
+ font-size: 10px;
+}
+.task-detail-refund-proof img {
+ width: 48px;
+ height: 48px;
+ object-fit: cover;
+ border-radius: 6px;
+}
+.task-detail-refund-proof-merged {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ min-width: 0;
+ padding: 11px;
+ border-left: 1px solid #edf1f6;
+ color: #8094ac;
+ font-size: 10px;
+ text-align: center;
+}
+.ask-refund-proof-card {
+ margin-top: 12px;
+ padding: 14px;
+ border: 1px solid #bfe8d1;
+ border-radius: 12px;
+ background: #f4fff8;
+}
+.ask-refund-proof-card > header {
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: 10px;
+ color: #168451;
+ font-size: 12px;
+}
+.ask-refund-proof-card > div,
+.refund-proof-user > div {
+ display: flex;
+ gap: 9px;
+ flex-wrap: wrap;
+}
+.ask-refund-proof-card figure,
+.refund-proof-user figure {
+ margin: 0;
+ overflow: hidden;
+ border: 1px solid #d3e8da;
+ border-radius: 8px;
+ background: #fff;
+}
+.ask-refund-proof-card img,
+.refund-proof-user img {
+ display: block;
+ width: 96px;
+ height: 96px;
+ object-fit: cover;
+}
+.ask-refund-proof-card figcaption,
+.refund-proof-user figcaption {
+ padding: 5px;
+ color: #5d816b;
+ font-size: 10px;
+ text-align: center;
+}
.task-detail-evidence {
display: grid;
grid-template-columns: 1.5fr 1fr;
@@ -2325,6 +2426,25 @@
font-size: 12px;
line-height: 1.75;
}
+.refund-proof-upload {
+ display: grid;
+ gap: 7px;
+ margin-top: 16px;
+ padding: 12px;
+ border: 1px dashed #b8cbe2;
+ border-radius: 10px;
+ color: #294767;
+ text-align: left;
+}
+.refund-proof-upload input {
+ width: 100%;
+}
+.refund-proof-upload small {
+ overflow: hidden;
+ color: #8195ad;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
.refund-confirm-modal > div {
display: grid;
grid-template-columns: 1fr 1.35fr;
@@ -2389,7 +2509,8 @@
overflow-x: auto;
}
.task-detail-table .head,
- .task-detail-table .row {
+ .task-detail-table .row,
+ .task-detail-table-body {
min-width: 620px;
}
.task-detail-evidence {
@@ -4655,3 +4776,117 @@ body img {
bottom: 14px;
}
}
+
+.ask-invalid-product-link {
+ display: flex;
+ align-items: center;
+ padding: 8px;
+ border-radius: 8px;
+ background: #fff0f3;
+ color: #c93652;
+ font-size: 10px;
+ white-space: nowrap;
+}
+
+/* Public mobile portals: prevent fixed action bars, long filenames and modal
+ content from creating a horizontal overflow on narrow DingTalk webviews. */
+@media (max-width: 700px) {
+ .ask-user-app,
+ .answer-v2-app,
+ .ask-user-app * ,
+ .answer-v2-app * {
+ min-width: 0;
+ }
+
+ .ask-user-app input,
+ .ask-user-app textarea,
+ .ask-user-app select,
+ .answer-v2-app input,
+ .answer-v2-app textarea,
+ .answer-v2-app select {
+ max-width: 100%;
+ }
+
+ .ask-user-sticky {
+ gap: 8px;
+ width: 100%;
+ padding-inline: 10px;
+ }
+
+ .ask-user-sticky span {
+ min-width: 0;
+ flex: 1 1 auto;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+
+ .ask-user-sticky button {
+ flex: 0 1 180px;
+ width: min(180px, 46vw);
+ min-width: 0;
+ white-space: nowrap;
+ }
+
+ .ask-product-card > footer,
+ .ask-payment-file {
+ min-width: 0;
+ flex-wrap: wrap;
+ }
+
+ .ask-product-card > footer code,
+ .ask-payment-file span {
+ min-width: 0;
+ max-width: 100%;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+
+ .ask-product-card > footer code {
+ flex: 1 1 100%;
+ }
+
+ .ask-payment-file span {
+ flex: 1 1 100%;
+ }
+
+ .ask-submit-backdrop {
+ align-items: end;
+ padding: 8px;
+ }
+
+ .ask-submit-modal {
+ width: 100%;
+ max-height: calc(100dvh - 16px);
+ overflow: auto;
+ border-radius: 16px;
+ }
+
+ .ask-submit-modal > footer {
+ grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
+ padding: 14px;
+ }
+
+ .ask-submit-modal > footer button {
+ min-width: 0;
+ white-space: nowrap;
+ }
+
+ .ask-invalid-card,
+ .ask-exhausted-card {
+ width: calc(100% - 24px);
+ margin: 56px auto 24px;
+ padding: 28px 18px;
+ }
+
+ .ask-invalid-card h1,
+ .ask-exhausted-card h1 {
+ font-size: 21px;
+ }
+}
+.ask-audit-card>span.refund-proof-badge{width:auto;min-width:60px;height:38px;padding:0 14px;border-radius:12px;font-size:13px;font-weight:700}
+.ask-audit-card>div.ask-audit-refund-proof{display:flex;align-items:center;justify-content:center;gap:8px;width:auto;min-height:60px;margin:0;padding:0;border:0;background:transparent;text-align:center}
+.ask-audit-refund-proof figure{margin:0;text-align:center}
+.ask-audit-refund-proof img{display:block;width:78px;height:78px;object-fit:cover;border:1px solid #67ca98;border-radius:12px;background:#fff}
+.ask-audit-refund-proof figcaption{margin-top:4px;color:#168451;font-size:10px}
diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx
index 4f6a519..d0aed8b 100644
--- a/frontend/src/main.jsx
+++ b/frontend/src/main.jsx
@@ -90,7 +90,10 @@ function LoginPage({ onLogin }) {
const autoLoginStarted = useRef(false);
const query = new URLSearchParams(window.location.search);
const getDingTalkBridge = () => window.dd || window.DingTalkPC || window.dingtalk;
- const inDingtalkWorkbench = /DingTalk|AliApp\(DT/i.test(navigator.userAgent || "") || query.get("from") === "dingtalk" || Boolean(getDingTalkBridge());
+ // The DingTalk SDK is loaded on every page, including a normal browser.
+ // Do not treat the mere presence of `window.dd` as proof that we are inside
+ // the DingTalk workbench, otherwise local/browser users lose password login.
+ const inDingtalkWorkbench = /DingTalk|AliApp\(DT/i.test(navigator.userAgent || "") || query.get("from") === "dingtalk";
function loadDingTalkSdk() {
if (getDingTalkBridge()) return Promise.resolve(getDingTalkBridge());
return new Promise((resolve, reject) => {