feat(ops): kb doc edit, validation auto-tickets, support batch ops

Add knowledge document GET/PUT and admin edit UI; auto-create support tickets on client 400 validation errors across user/shop/partner apps; batch create tasks and publish from support tickets; restore mini-user store env single-column layout.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-05 00:08:40 +08:00
parent 16c386f165
commit 02d89e6385
18 changed files with 677 additions and 24 deletions
+7
View File
@@ -3,6 +3,7 @@ import { ClientApp } from '@dukang/shared-types';
import { forceReloadAfterAccountMerge } from './auth-nav';
import { resetStoresSessionBootstrap } from './stores-session';
import { resetHomeCatalogBootstrap } from './home-catalog-session';
import { reportClientValidationError } from './client-error';
function resolveApiBase(): string {
const origin =
@@ -113,6 +114,12 @@ export async function request<T = unknown>(path: string, options: ReqOptions = {
throw new Error('接口不可达,请确认 API 服务已启动');
}
if (status >= 400 || body.code !== 0) {
if (status === 400 || body?.code === 400) {
reportClientValidationError({
message: body?.message || `请求失败(${status})`,
apiPath: path,
});
}
throw new Error(body?.message || `请求失败(${status})`);
}
return (res.data as { data: T }).data;
+12
View File
@@ -6,6 +6,7 @@ export type ClientErrorCategory =
| 'js_error'
| 'unhandled_rejection'
| 'api_error'
| 'validation_error'
| 'network'
| 'render'
| 'bridge'
@@ -57,6 +58,17 @@ export function reportClientError(payload: ClientErrorPayload): void {
}).catch(() => {});
}
/** 上报 API 400 验证错误(失败静默) */
export function reportClientValidationError(input: { message: string; apiPath?: string }): void {
if (input.apiPath?.includes('/common/client-errors')) return;
reportClientError({
level: 'warn',
category: 'validation_error',
message: input.message,
extra: { status: 400, url: input.apiPath },
});
}
let installed = false;
/** 安装小程序/H5 全局未捕获错误钩子(幂等) */
+4 -4
View File
@@ -175,15 +175,14 @@
}
.store-detail-env-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
display: flex;
flex-direction: column;
gap: 12px;
}
.store-detail-env-item {
position: relative;
width: 100%;
aspect-ratio: 4 / 3;
border-radius: var(--radius-md, 8px);
overflow: hidden;
background: var(--color-surface-container);
@@ -192,8 +191,9 @@
.store-detail-env-img {
width: 100%;
height: 100%;
height: auto;
display: block;
vertical-align: top;
}
.store-detail-bar {