This commit is contained in:
笔记本里的永平
2025-07-16 11:06:09 +08:00
parent 1bb34b4932
commit b8b86598d8
2 changed files with 110 additions and 71 deletions

View File

@@ -162,3 +162,34 @@ class RequestCancelManager {
// 导出单例实例 // 导出单例实例
export const requestDeduplicator = new RequestDeduplicator(); export const requestDeduplicator = new RequestDeduplicator();
export const requestCancelManager = new RequestCancelManager(); export const requestCancelManager = new RequestCancelManager();
/**
* 通用文件上传方法(支持图片、文件)
* @param {File} file - 要上传的文件对象
* @param {string} [uploadUrl='/v1/attachment/upload'] - 上传接口地址
* @returns {Promise<string>} - 上传成功后返回文件url
*/
export async function uploadFile(file: File, uploadUrl: string = '/v1/attachment/upload'): Promise<string> {
const formData = new FormData();
formData.append('file', file);
const token = typeof window !== 'undefined' ? localStorage.getItem('token') : '';
const headers: Record<string, string> = {};
if (token) headers['Authorization'] = `Bearer ${token}`;
try {
const response = await fetch(uploadUrl, {
method: 'POST',
headers,
body: formData,
});
const res = await response.json();
if (res?.url) {
return res.url;
}
if (res?.data?.url) {
return res.data.url;
}
throw new Error(res?.msg || '文件上传失败');
} catch (e: any) {
throw new Error(e?.message || '文件上传失败');
}
}

View File

@@ -304,6 +304,7 @@ export function BasicSettings({
// 新增用于文件选择的ref // 新增用于文件选择的ref
const uploadInputRef = useRef<HTMLInputElement>(null); const uploadInputRef = useRef<HTMLInputElement>(null);
const uploadOrderInputRef = useRef<HTMLInputElement>(null);
// 更新电话获客设置 // 更新电话获客设置
const handlePhoneSettingsUpdate = () => { const handlePhoneSettingsUpdate = () => {
@@ -648,6 +649,9 @@ export function BasicSettings({
transition: "border 0.2s", transition: "border 0.2s",
textAlign: "center", textAlign: "center",
position: "relative", position: "relative",
height: 180 + 12, // 图片高度180+上下padding
overflow: "hidden",
minHeight: 192,
}} }}
onClick={() => handleMaterialSelect(material)} onClick={() => handleMaterialSelect(material)}
> >
@@ -675,40 +679,31 @@ export function BasicSettings({
</button> </button>
{/* 删除自定义海报按钮 */} {/* 删除自定义海报按钮 */}
{isCustom && ( {isCustom && (
<button <div
type="button"
style={{ style={{
position: "absolute", position: "absolute",
top: 8, top: 8,
right: 8, right: 8,
width: 24, width: 28,
height: 24, height: 28,
background: "rgba(0,0,0,0.5)", background: "rgba(0,0,0,0.5)",
border: "none", border: "none",
borderRadius: "50%", borderRadius: "50%",
padding: 0,
zIndex: 2, zIndex: 2,
cursor: "pointer", cursor: "pointer",
display: "flex", display: "flex",
alignItems: "center", alignItems: "center",
justifyContent: "center", justifyContent: "center",
lineHeight: 20,
color: "#ffffff",
}} }}
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
handleRemoveCustomPoster(material.id); handleRemoveCustomPoster(material.id);
}} }}
> >
<span ×
style={{ </div>
color: "#fff",
fontWeight: 700,
fontSize: 16,
lineHeight: "24px",
}}
>
×
</span>
</button>
)} )}
<img <img
src={material.preview} src={material.preview}
@@ -719,12 +714,14 @@ export function BasicSettings({
objectFit: "cover", objectFit: "cover",
borderRadius: 4, borderRadius: 4,
marginBottom: 0, marginBottom: 0,
display: "block",
}} }}
/> />
<div <div
style={{ style={{
position: "relative", position: "absolute",
top: "-32px", left: 0,
bottom: 0,
width: "100%", width: "100%",
background: "rgba(0,0,0,0.5)", background: "rgba(0,0,0,0.5)",
color: "#fff", color: "#fff",
@@ -733,6 +730,7 @@ export function BasicSettings({
borderBottomLeftRadius: 4, borderBottomLeftRadius: 4,
borderBottomRightRadius: 4, borderBottomRightRadius: 4,
textAlign: "center", textAlign: "center",
zIndex: 3,
}} }}
> >
{material.name} {material.name}
@@ -753,7 +751,7 @@ export function BasicSettings({
flexDirection: "column", flexDirection: "column",
alignItems: "center", alignItems: "center",
justifyContent: "center", justifyContent: "center",
height: 220, height: 190,
}} }}
onClick={() => uploadInputRef.current?.click()} onClick={() => uploadInputRef.current?.click()}
> >
@@ -801,62 +799,75 @@ export function BasicSettings({
index={0} index={0}
/> />
</div> </div>
{/* 订单导入区块 */} {/* 订单导入区块优化 */}
<div style={openOrder} className="my-4"> <div style={openOrder} className="my-4">
<Button theme="default" onClick={() => setIsImportDialogOpen(true)}> <div style={{ fontWeight: 500, marginBottom: 8 }}></div>
{importedTags.length <div style={{ display: "flex", gap: 12, marginBottom: 4 }}>
? `已导入订单(${importedTags.length}` <Button
: "导入订单"} type="button"
</Button> style={{ display: "flex", alignItems: "center", gap: 4 }}
<Dialog visible={isImportDialogOpen} onClose={handleImportDialogClose}> theme="default"
<div style={{ fontWeight: 600, fontSize: 16, marginBottom: 12 }}> onClick={handleDownloadTemplate}
>
</div> <span className="iconfont" style={{ fontSize: 18 }}>
<div style={{ marginBottom: 12 }}>
<Button </span>{" "}
theme="primary"
onClick={handleDownloadTemplate} </Button>
style={{ marginRight: 8 }} <Button
> type="button"
style={{ display: "flex", alignItems: "center", gap: 4 }}
</Button> theme="default"
onClick={() => uploadOrderInputRef.current?.click()}
>
<span className="iconfont" style={{ fontSize: 18 }}>
</span>{" "}
<input <input
ref={uploadOrderInputRef}
type="file" type="file"
accept=".csv" accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel"
style={{ display: "none" }}
onChange={handleFileImport} onChange={handleFileImport}
style={{ display: "inline-block" }}
/> />
</div> </Button>
{importedTags.length > 0 && ( </div>
<Table <div style={{ color: "#888", fontSize: 13, marginBottom: 8 }}>
columns={[ CSVExcel
{ colKey: "phone", title: "手机号" }, </div>
{ colKey: "wechat", title: "微信号" }, {/* 已导入数据表格可复用原有Table渲染 */}
{ colKey: "source", title: "来源" }, {importedTags.length > 0 && (
{ colKey: "orderAmount", title: "订单金额" }, <Table
{ colKey: "orderDate", title: "下单日期" }, columns={[
]} { colKey: "phone", title: "手机号" },
data={importedTags} { colKey: "wechat", title: "微信号" },
rowKey="phone" { colKey: "source", title: "来源" },
style={{ marginTop: 12 }} { colKey: "orderAmount", title: "订单金额" },
/> { colKey: "orderDate", title: "下单日期" },
)} ]}
</Dialog> data={importedTags}
rowKey="phone"
style={{ marginTop: 12 }}
/>
)}
</div> </div>
{/* 电话获客设置区块,仅在选择电话获客场景时显示 */} {/* 电话获客设置区块,仅在选择电话获客场景时显示 */}
{formData.scenario === "phone" && ( {formData.scenario === 5 && (
<div style={{ margin: "16px 0" }}> <div style={{ margin: "16px 0" }}>
<Button theme="default" onClick={() => setIsPhoneSettingsOpen(true)}> <div
style={{
</Button> background: "#f7f8fa",
<Dialog borderRadius: 10,
visible={isPhoneSettingsOpen} padding: 20,
onClose={handlePhoneSettingsDialogClose} boxShadow: "0 2px 8px rgba(0,0,0,0.03)",
marginBottom: 12,
}}
> >
<div style={{ fontWeight: 600, fontSize: 16, marginBottom: 12 }}> <div style={{ fontWeight: 600, fontSize: 16, marginBottom: 16 }}>
</div> </div>
<div style={{ display: "flex", flexDirection: "column", gap: 16 }}> <div style={{ display: "flex", flexDirection: "column", gap: 18 }}>
<div <div
style={{ style={{
display: "flex", display: "flex",
@@ -902,15 +913,12 @@ export function BasicSettings({
} }
/> />
</div> </div>
<Button block theme="primary" onClick={handlePhoneSettingsUpdate}>
</Button>
</div> </div>
</Dialog> </div>
</div> </div>
)} )}
{/* 微信群设置区块,仅在选择微信群场景时显示 */} {/* 微信群设置区块,仅在选择微信群场景时显示 */}
{formData.scenario === "weixinqun" && ( {formData.scenario === 7 && (
<div style={{ margin: "16px 0" }}> <div style={{ margin: "16px 0" }}>
<div style={{ marginBottom: 8 }}> <div style={{ marginBottom: 8 }}>
<Input <Input
@@ -932,7 +940,7 @@ export function BasicSettings({
</div> </div>
</div> </div>
)} )}
<Button block theme="primary" onClick={onNext}> <Button className="mt-4" block theme="primary" onClick={onNext}>
</Button> </Button>
</div> </div>