Compare commits
10 Commits
d294fb1306
...
trunk
| Author | SHA1 | Date | |
|---|---|---|---|
| 89d2bcdf29 | |||
| ab9c034a72 | |||
| 17be93115d | |||
| b142751b69 | |||
| aabb89b5f5 | |||
| bab3770337 | |||
| b83a91e2f2 | |||
| 7464906605 | |||
| 5a193bd585 | |||
| da323690db |
@ -27,6 +27,7 @@ export enum AdminAPI {
|
|||||||
MATERIAL_UPDATE = '/api/bo/resume/update',
|
MATERIAL_UPDATE = '/api/bo/resume/update',
|
||||||
// 七牛
|
// 七牛
|
||||||
UPLOAD_FILE = '/api/backend/file/upload',
|
UPLOAD_FILE = '/api/backend/file/upload',
|
||||||
|
UPLOAD_FILE_WX = '/api/cityOperator/wxCp/uploadImg',
|
||||||
// 运营人员
|
// 运营人员
|
||||||
STAFF_LIST = '/api/staff/getStaffPageList',
|
STAFF_LIST = '/api/staff/getStaffPageList',
|
||||||
STAFF_ALL = '/api/staff/getAllStaff',
|
STAFF_ALL = '/api/staff/getAllStaff',
|
||||||
@ -42,4 +43,5 @@ export enum AdminAPI {
|
|||||||
GIVE_VIP = '/api/bo/giveVip/give',
|
GIVE_VIP = '/api/bo/giveVip/give',
|
||||||
// 产品列表
|
// 产品列表
|
||||||
PRODUCT_LIST = '/api/bo/product/listByType/{productType}',
|
PRODUCT_LIST = '/api/bo/product/listByType/{productType}',
|
||||||
|
GET_PRODUCT_SPEC = '/api/bo/product/getBySpecId/{productSpecId}'
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,9 +5,8 @@ import {
|
|||||||
ProFormInstance,
|
ProFormInstance,
|
||||||
ProFormSelect,
|
ProFormSelect,
|
||||||
ProFormText,
|
ProFormText,
|
||||||
ProFormDigit,
|
|
||||||
ProFormMoney,
|
|
||||||
ProFormUploadButton,
|
ProFormUploadButton,
|
||||||
|
ProFormDependency,
|
||||||
} from '@ant-design/pro-components';
|
} from '@ant-design/pro-components';
|
||||||
import { ModalForm, PageContainer, ProTable } from '@ant-design/pro-components';
|
import { ModalForm, PageContainer, ProTable } from '@ant-design/pro-components';
|
||||||
import '@umijs/max';
|
import '@umijs/max';
|
||||||
@ -19,8 +18,14 @@ import React, { useEffect, useMemo, useRef, useState } from 'react';
|
|||||||
|
|
||||||
import { CITY_OPTIONS } from '@/constants/city';
|
import { CITY_OPTIONS } from '@/constants/city';
|
||||||
import { TIME_FORMAT } from '@/constants/global';
|
import { TIME_FORMAT } from '@/constants/global';
|
||||||
import { uploadFile } from '@/services/file';
|
import { uploadFileWx } from '@/services/file';
|
||||||
import { deleteCityOperator, getAllStaffList, getCityOpratorList, updateCityOperator } from '@/services/list';
|
import {
|
||||||
|
deleteCityOperator,
|
||||||
|
getAllStaffList,
|
||||||
|
getCityOpratorList,
|
||||||
|
getProductList,
|
||||||
|
updateCityOperator,
|
||||||
|
} from '@/services/list';
|
||||||
|
|
||||||
const useStyles = createStyles(({ token }) => {
|
const useStyles = createStyles(({ token }) => {
|
||||||
return {
|
return {
|
||||||
@ -37,8 +42,13 @@ const useStyles = createStyles(({ token }) => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const calcPrice = (productSpecId: string, specList: API.ProductSpecListItem[]) => {
|
||||||
|
const product = specList.find(o => o.productSpecId === productSpecId);
|
||||||
|
return { price: product?.showPrice, originalPrice: product?.originalPrice, sendCount: product?.count };
|
||||||
|
};
|
||||||
const TableList: React.FC = () => {
|
const TableList: React.FC = () => {
|
||||||
const [updateModalOpen, handleUpdateModalOpen] = useState<boolean>(false);
|
const [updateModalOpen, handleUpdateModalOpen] = useState<boolean>(false);
|
||||||
|
const [specList, setSpecList] = useState<API.ProductSpecListItem[]>([]);
|
||||||
const [currentRow, setCurrentRow] = useState<API.CityOperatorListItem>();
|
const [currentRow, setCurrentRow] = useState<API.CityOperatorListItem>();
|
||||||
const actionRef = useRef<ActionType>();
|
const actionRef = useRef<ActionType>();
|
||||||
const formRef = useRef<ProFormInstance>();
|
const formRef = useRef<ProFormInstance>();
|
||||||
@ -47,6 +57,14 @@ const TableList: React.FC = () => {
|
|||||||
return staffOptions.find(it => it.isDefault) || staffOptions[0];
|
return staffOptions.find(it => it.isDefault) || staffOptions[0];
|
||||||
}, [staffOptions]);
|
}, [staffOptions]);
|
||||||
const { styles } = useStyles();
|
const { styles } = useStyles();
|
||||||
|
const specOptions = useMemo(
|
||||||
|
() =>
|
||||||
|
specList.map(it => ({
|
||||||
|
value: it.productSpecId,
|
||||||
|
label: it.title,
|
||||||
|
})),
|
||||||
|
[specList],
|
||||||
|
);
|
||||||
|
|
||||||
const getAllStaffOptions = async () => {
|
const getAllStaffOptions = async () => {
|
||||||
try {
|
try {
|
||||||
@ -63,17 +81,23 @@ const TableList: React.FC = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getAllSpectList = async () => {
|
||||||
|
const list = await getProductList('GROUP_BATCH_PUSH');
|
||||||
|
setSpecList(list);
|
||||||
|
};
|
||||||
|
|
||||||
const handleDelete = async (id: number) => {
|
const handleDelete = async (id: number) => {
|
||||||
await deleteCityOperator(id);
|
await deleteCityOperator(id);
|
||||||
message.success('操作成功');
|
message.success('操作成功');
|
||||||
actionRef.current?.reload();
|
actionRef.current?.reload();
|
||||||
};
|
};
|
||||||
const handleUpload = async (file: File) => {
|
const handleUpload = async (file: File) => {
|
||||||
const { url } = await uploadFile({ file, type: 'IMAGE' });
|
const url = await uploadFileWx({ file, id: currentRow?.id });
|
||||||
return url;
|
return url;
|
||||||
};
|
};
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getAllStaffOptions();
|
getAllStaffOptions();
|
||||||
|
getAllSpectList();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const columns: ProColumns<API.CityOperatorListItem>[] = [
|
const columns: ProColumns<API.CityOperatorListItem>[] = [
|
||||||
@ -121,7 +145,7 @@ const TableList: React.FC = () => {
|
|||||||
copyable: true,
|
copyable: true,
|
||||||
search: false,
|
search: false,
|
||||||
render(_dom, { contactQrCode }) {
|
render(_dom, { contactQrCode }) {
|
||||||
return <img className={styles.img} src={contactQrCode} alt="" />;
|
return contactQrCode ? <img className={styles.img} src={contactQrCode} alt="" /> : '-';
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -131,21 +155,22 @@ const TableList: React.FC = () => {
|
|||||||
copyable: true,
|
copyable: true,
|
||||||
search: false,
|
search: false,
|
||||||
render(_dom, { groupQrCode }) {
|
render(_dom, { groupQrCode }) {
|
||||||
return <img className={styles.img} src={groupQrCode} alt="" />;
|
return groupQrCode ? <img className={styles.img} src={groupQrCode} alt="" /> : '-';
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '可群发数量',
|
title: '可群发数量',
|
||||||
dataIndex: 'sendCount',
|
dataIndex: 'productSpecId',
|
||||||
valueType: 'textarea',
|
valueType: 'textarea',
|
||||||
search: false,
|
search: false,
|
||||||
|
renderText: productSpecId => calcPrice(productSpecId, specList).sendCount || '-',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '群代发价格',
|
title: '群代发价格',
|
||||||
dataIndex: 'price',
|
dataIndex: 'productSpecId',
|
||||||
valueType: 'textarea',
|
valueType: 'textarea',
|
||||||
search: false,
|
search: false,
|
||||||
renderText: cents => (cents ? (cents / 100).toFixed(0) : '-'),
|
renderText: productSpecId => calcPrice(productSpecId, specList).price || '-',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '操作',
|
title: '操作',
|
||||||
@ -158,12 +183,24 @@ const TableList: React.FC = () => {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleUpdateModalOpen(true);
|
handleUpdateModalOpen(true);
|
||||||
setCurrentRow(record);
|
setCurrentRow(record);
|
||||||
formRef.current?.setFieldsValue({
|
setTimeout(() => {
|
||||||
...record,
|
formRef.current?.setFieldsValue({
|
||||||
city: {
|
...record,
|
||||||
label: record.cityName,
|
city: {
|
||||||
value: record.cityCode,
|
label: record.cityName,
|
||||||
},
|
value: record.cityCode,
|
||||||
|
},
|
||||||
|
qrCode: record.groupQrCode
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
uid: `${record.id}_qrCode`,
|
||||||
|
name: `${record.cityName}_qrCode`,
|
||||||
|
status: 'done',
|
||||||
|
url: record.groupQrCode,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: undefined,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -214,9 +251,8 @@ const TableList: React.FC = () => {
|
|||||||
cityCode: formData.city.value,
|
cityCode: formData.city.value,
|
||||||
cityName: formData.city.label,
|
cityName: formData.city.label,
|
||||||
groupLink: formData.groupLink,
|
groupLink: formData.groupLink,
|
||||||
sendCount: formData.sendCount,
|
productSpecId: formData.productSpecId,
|
||||||
qroupQrCode: formData.qrCode[0].xhr.responseURL,
|
groupQrCode: formData.qrCode[0].xhr ? formData.qrCode[0].xhr.responseURL : formData.qrCode[0].url,
|
||||||
price: formData.price,
|
|
||||||
};
|
};
|
||||||
console.log('update confirm', formData, params);
|
console.log('update confirm', formData, params);
|
||||||
try {
|
try {
|
||||||
@ -245,7 +281,7 @@ const TableList: React.FC = () => {
|
|||||||
<ProFormText name="groupLink" label="进群链接" rules={[{ message: '请输入链接', type: 'url' }]} />
|
<ProFormText name="groupLink" label="进群链接" rules={[{ message: '请输入链接', type: 'url' }]} />
|
||||||
<ProFormUploadButton
|
<ProFormUploadButton
|
||||||
name="qrCode"
|
name="qrCode"
|
||||||
label="上传"
|
label="进群二维码"
|
||||||
max={1}
|
max={1}
|
||||||
accept="image/*"
|
accept="image/*"
|
||||||
rules={[{ required: true, message: '必填项' }]}
|
rules={[{ required: true, message: '必填项' }]}
|
||||||
@ -254,29 +290,22 @@ const TableList: React.FC = () => {
|
|||||||
}}
|
}}
|
||||||
action={handleUpload}
|
action={handleUpload}
|
||||||
/>
|
/>
|
||||||
<ProFormDigit name="sendCount" label="可群发数量" min={1} fieldProps={{ precision: 0 }} />
|
<ProFormSelect
|
||||||
<ProFormMoney
|
mode="single"
|
||||||
name="price"
|
name="productSpecId"
|
||||||
label="价格"
|
label="可群发数量"
|
||||||
min={0}
|
options={specOptions}
|
||||||
fieldProps={{
|
rules={[{ required: true, message: '必填项' }]}
|
||||||
precision: 0,
|
|
||||||
formatter: value => {
|
|
||||||
if (!value) return '0';
|
|
||||||
return `${Math.floor(value / 100)}`;
|
|
||||||
},
|
|
||||||
parser: value => {
|
|
||||||
if (!value) return 0;
|
|
||||||
const numValue = parseInt(value.toString().replace(/[^\d]/g, ''));
|
|
||||||
return numValue * 100;
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
transform={(value, namePath) => {
|
|
||||||
return {
|
|
||||||
[namePath]: value ? Math.round(Number(value)) : 0,
|
|
||||||
};
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
|
<ProFormDependency name={['productSpecId']}>
|
||||||
|
{({ productSpecId }) =>
|
||||||
|
productSpecId ? (
|
||||||
|
<div>
|
||||||
|
价格:{calcPrice(productSpecId, specList).price}(原价:{calcPrice(productSpecId, specList).originalPrice})
|
||||||
|
</div>
|
||||||
|
) : null
|
||||||
|
}
|
||||||
|
</ProFormDependency>
|
||||||
</ModalForm>
|
</ModalForm>
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
import type { ActionType, ProColumns, ProFormInstance } from '@ant-design/pro-components';
|
import type { ActionType, ProColumns, ProFormInstance } from '@ant-design/pro-components';
|
||||||
import { ModalForm, PageContainer, ProFormSelect, ProFormText, ProTable } from '@ant-design/pro-components';
|
import { ModalForm, PageContainer, ProFormSelect, ProFormText, ProTable } from '@ant-design/pro-components';
|
||||||
|
import { message, Typography } from 'antd';
|
||||||
import '@umijs/max';
|
import '@umijs/max';
|
||||||
import { Select } from 'antd';
|
import { Select } from 'antd';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import React, { useRef, useState } from 'react';
|
import React, { useCallback, useRef, useState } from 'react';
|
||||||
|
|
||||||
|
const { Text } = Typography;
|
||||||
import { TIME_FORMAT } from '@/constants/global';
|
import { TIME_FORMAT } from '@/constants/global';
|
||||||
import { getPublisherList, updatePublisherInfo } from '@/services/list';
|
import { getPublisherList, updatePublisherInfo } from '@/services/list';
|
||||||
|
|
||||||
@ -35,12 +37,37 @@ const PRIORITY_OPTIONS = [
|
|||||||
{ label: '正常处理', value: 2 },
|
{ label: '正常处理', value: 2 },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const COPY_TIMES = '__copy_times__';
|
||||||
|
const COPY_EXP = '__copy_exp__';
|
||||||
const TableList: React.FC = () => {
|
const TableList: React.FC = () => {
|
||||||
const [updateModalOpen, handleUpdateModalOpen] = useState<boolean>(false);
|
const [updateModalOpen, handleUpdateModalOpen] = useState<boolean>(false);
|
||||||
const [currentRow, setCurrentRow] = useState<API.PublisherListItem>();
|
const [currentRow, setCurrentRow] = useState<API.PublisherListItem>();
|
||||||
const actionRef = useRef<ActionType>();
|
const actionRef = useRef<ActionType>();
|
||||||
const formRef = useRef<ProFormInstance>();
|
const formRef = useRef<ProFormInstance>();
|
||||||
|
|
||||||
|
const handleCopy = useCallback(() => {
|
||||||
|
const storedTimes = localStorage.getItem(COPY_TIMES);
|
||||||
|
const copyExp = localStorage.getItem(COPY_EXP);
|
||||||
|
|
||||||
|
const now = Date.now();
|
||||||
|
let currentTimes = 0;
|
||||||
|
|
||||||
|
if (!copyExp || now >= parseInt(copyExp, 10)) {
|
||||||
|
currentTimes = 1;
|
||||||
|
|
||||||
|
const tomorrow = new Date();
|
||||||
|
tomorrow.setDate(tomorrow.getDate() + 1);
|
||||||
|
tomorrow.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
|
localStorage.setItem(COPY_EXP, tomorrow.getTime().toString());
|
||||||
|
} else {
|
||||||
|
currentTimes = parseInt(storedTimes || '0', 10) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
localStorage.setItem(COPY_TIMES, `${currentTimes}`);
|
||||||
|
message.success(`今天已复制${currentTimes}次`);
|
||||||
|
}, []);
|
||||||
|
|
||||||
const columns: ProColumns<API.PublisherListItem>[] = [
|
const columns: ProColumns<API.PublisherListItem>[] = [
|
||||||
{
|
{
|
||||||
title: '发布人昵称',
|
title: '发布人昵称',
|
||||||
@ -52,7 +79,17 @@ const TableList: React.FC = () => {
|
|||||||
title: '发布人ID',
|
title: '发布人ID',
|
||||||
dataIndex: 'blPublisherId',
|
dataIndex: 'blPublisherId',
|
||||||
valueType: 'textarea',
|
valueType: 'textarea',
|
||||||
copyable: true,
|
render(blPublisherId) {
|
||||||
|
return (
|
||||||
|
<Text
|
||||||
|
copyable={{
|
||||||
|
onCopy: handleCopy,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{blPublisherId}
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '优先级',
|
title: '优先级',
|
||||||
|
|||||||
@ -95,16 +95,18 @@ const TableList: React.FC = () => {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleUpdateModalOpen(true);
|
handleUpdateModalOpen(true);
|
||||||
setCurrentRow(record);
|
setCurrentRow(record);
|
||||||
formRef.current?.setFieldsValue({
|
setTimeout(() => {
|
||||||
...record,
|
formRef.current?.setFieldsValue({
|
||||||
qrCode: [
|
...record,
|
||||||
{
|
qrCode: [
|
||||||
uid: `${record.id}_qrCode`,
|
{
|
||||||
name: `${record.staffName}_qrCode`,
|
uid: `${record.id}_qrCode`,
|
||||||
status: 'done',
|
name: `${record.staffName}_qrCode`,
|
||||||
url: record.staffQrCode,
|
status: 'done',
|
||||||
},
|
url: record.staffQrCode,
|
||||||
],
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -155,7 +157,7 @@ const TableList: React.FC = () => {
|
|||||||
id: currentRow?.id,
|
id: currentRow?.id,
|
||||||
isDefault: currentRow?.isDefault || 0,
|
isDefault: currentRow?.isDefault || 0,
|
||||||
staffName: formData?.staffName,
|
staffName: formData?.staffName,
|
||||||
staffQrCode: formData.qrCode[0].xhr.responseURL,
|
staffQrCode: formData.qrCode[0].xhr ? formData.qrCode[0].xhr.responseURL : formData.qrCode[0].url,
|
||||||
};
|
};
|
||||||
console.log('update confirm', formData, params);
|
console.log('update confirm', formData, params);
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -11,3 +11,13 @@ export async function uploadFile(options: { file: File; type: 'VIDEO' | 'IMAGE'
|
|||||||
data: options,
|
data: options,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function uploadFileWx(options: { file: File; id?: number }): Promise<string> {
|
||||||
|
return request<string>(AdminAPI.UPLOAD_FILE_WX, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data',
|
||||||
|
},
|
||||||
|
data: options,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@ -327,3 +327,9 @@ export async function getProductList(productType: API.ProductType) {
|
|||||||
method: 'GET',
|
method: 'GET',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getProductSpec(productSpecId: API.ProductType) {
|
||||||
|
return request<API.ProductSpecListItem>(buildUrl(AdminAPI.GET_PRODUCT_SPEC, {productSpecId}), {
|
||||||
|
method: 'GET',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
9
src/services/typings.d.ts
vendored
9
src/services/typings.d.ts
vendored
@ -363,10 +363,9 @@ declare namespace API {
|
|||||||
staffId: number;
|
staffId: number;
|
||||||
cityCode: string;
|
cityCode: string;
|
||||||
groupLink: string;
|
groupLink: string;
|
||||||
sendCount: number;
|
productSpecId: string;
|
||||||
contactQrCode: string;
|
contactQrCode: string;
|
||||||
groupQrCode: string;
|
groupQrCode: string;
|
||||||
price: number;
|
|
||||||
id: number;
|
id: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -377,9 +376,8 @@ declare namespace API {
|
|||||||
cityName?: string;
|
cityName?: string;
|
||||||
cityCode: string;
|
cityCode: string;
|
||||||
groupLink: string;
|
groupLink: string;
|
||||||
qroupQrCode: string;
|
groupQrCode: string;
|
||||||
sendCount?: number;
|
productSpecId?: string;
|
||||||
price?: number;
|
|
||||||
created?: string;
|
created?: string;
|
||||||
updated?: string;
|
updated?: string;
|
||||||
}
|
}
|
||||||
@ -411,6 +409,7 @@ declare namespace API {
|
|||||||
productSpecId: string;
|
productSpecId: string;
|
||||||
productType: ProductType;
|
productType: ProductType;
|
||||||
productName: string;
|
productName: string;
|
||||||
|
count: number;
|
||||||
title: string;
|
title: string;
|
||||||
priceText: string;
|
priceText: string;
|
||||||
payPrice: number;
|
payPrice: number;
|
||||||
|
|||||||
Reference in New Issue
Block a user