feat: v3.4.12 ticket iteration

Refund rollback, winery T+3, multi withdraw, mini-user store detail, dev plan batch edit and WeCom dispatch, support ticket edit/attachments/batch status, package imageUrl.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-04 23:53:31 +08:00
parent 71f508e02b
commit 4372018c09
34 changed files with 1032 additions and 86 deletions
@@ -1,6 +1,7 @@
import { useState } from 'react';
import { useRef, useState } from 'react';
import { STORE_PACKAGE_MAX_COUNT } from '@dukang/shared-types';
import { emptyPackage, type PackageFormItem } from '../lib/storePackages';
import { uploadFileToOss } from '../lib/upload';
type Props = {
items: PackageFormItem[];
@@ -10,6 +11,19 @@ type Props = {
export default function ShopPackagesForm({ items, onChange, disabled }: Props) {
const [collapsed, setCollapsed] = useState<Record<number, boolean>>({});
const [uploadingIndex, setUploadingIndex] = useState<number | null>(null);
const fileRefs = useRef<Record<number, HTMLInputElement | null>>({});
async function pickPackageImage(index: number, file?: File | null) {
if (!file || disabled) return;
setUploadingIndex(index);
try {
const result = await uploadFileToOss(file, 'STORE_PACKAGE');
updateAt(index, { imageUrl: result.url });
} finally {
setUploadingIndex(null);
}
}
function updateAt(index: number, patch: Partial<PackageFormItem>) {
onChange(items.map((item, i) => (i === index ? { ...item, ...patch } : item)));
@@ -116,6 +130,36 @@ export default function ShopPackagesForm({ items, onChange, disabled }: Props) {
/>
</label>
<label className="shop-packages-field">
<span className="shop-packages-label"></span>
{item.imageUrl ? (
<img
src={item.imageUrl}
alt=""
style={{ width: '100%', maxHeight: 160, objectFit: 'cover', borderRadius: 8, marginBottom: 8 }}
/>
) : null}
<input
ref={(el) => {
fileRefs.current[index] = el;
}}
type="file"
accept="image/*"
hidden
disabled={disabled}
onChange={(e) => void pickPackageImage(index, e.target.files?.[0])}
/>
<button
type="button"
className="shop-packages-add"
style={{ marginTop: 0 }}
disabled={disabled || uploadingIndex === index}
onClick={() => fileRefs.current[index]?.click()}
>
{uploadingIndex === index ? '上传中…' : item.imageUrl ? '更换图片' : '上传图片'}
</button>
</label>
<label className="shop-packages-field">
<span className="shop-packages-label"></span>
<input
+6 -1
View File
@@ -10,6 +10,7 @@ export function emptyPackage(index = 0): PackageFormItem {
dishes: '',
usableTime: '',
otherNotes: '',
imageUrl: '',
sortOrder: index,
};
}
@@ -22,9 +23,13 @@ export function normalizePackageFormItems(raw: PackageFormItem[]): PackageFormIt
dishes: item.dishes.trim(),
usableTime: item.usableTime?.trim() || '',
otherNotes: item.otherNotes?.trim() || '',
imageUrl: item.imageUrl?.trim() || '',
sortOrder: index,
}))
.filter((item) => item.name || item.price || item.dishes || item.usableTime || item.otherNotes);
.filter(
(item) =>
item.name || item.price || item.dishes || item.usableTime || item.otherNotes || item.imageUrl,
);
}
export function validatePackageFormItems(items: PackageFormItem[]): string | null {
-2
View File
@@ -80,7 +80,6 @@ export default function WithdrawPage() {
const canApply =
!!summary?.isPrimary &&
summary.availableAmount > 0 &&
!summary.hasPendingRequest &&
summary.hasBankAccount &&
!submitting;
@@ -126,7 +125,6 @@ export default function WithdrawPage() {
info
</span>
¥{formatMoney(summary?.dailyLimit ?? 5000)}
{summary?.hasPendingRequest ? ' · 已有待审核申请' : ''}
{!summary?.hasBankAccount ? ' · 请先完善收款账户' : ''}
</p>
</section>