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',
|
||||
// 七牛
|
||||
UPLOAD_FILE = '/api/backend/file/upload',
|
||||
UPLOAD_FILE_WX = '/api/cityOperator/wxCp/uploadImg',
|
||||
// 运营人员
|
||||
STAFF_LIST = '/api/staff/getStaffPageList',
|
||||
STAFF_ALL = '/api/staff/getAllStaff',
|
||||
@ -42,4 +43,5 @@ export enum AdminAPI {
|
||||
GIVE_VIP = '/api/bo/giveVip/give',
|
||||
// 产品列表
|
||||
PRODUCT_LIST = '/api/bo/product/listByType/{productType}',
|
||||
GET_PRODUCT_SPEC = '/api/bo/product/getBySpecId/{productSpecId}'
|
||||
}
|
||||
|
||||
@ -5,9 +5,8 @@ import {
|
||||
ProFormInstance,
|
||||
ProFormSelect,
|
||||
ProFormText,
|
||||
ProFormDigit,
|
||||
ProFormMoney,
|
||||
ProFormUploadButton,
|
||||
ProFormDependency,
|
||||
} from '@ant-design/pro-components';
|
||||
import { ModalForm, PageContainer, ProTable } from '@ant-design/pro-components';
|
||||
import '@umijs/max';
|
||||
@ -19,8 +18,14 @@ import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||
|
||||
import { CITY_OPTIONS } from '@/constants/city';
|
||||
import { TIME_FORMAT } from '@/constants/global';
|
||||
import { uploadFile } from '@/services/file';
|
||||
import { deleteCityOperator, getAllStaffList, getCityOpratorList, updateCityOperator } from '@/services/list';
|
||||
import { uploadFileWx } from '@/services/file';
|
||||
import {
|
||||
deleteCityOperator,
|
||||
getAllStaffList,
|
||||
getCityOpratorList,
|
||||
getProductList,
|
||||
updateCityOperator,
|
||||
} from '@/services/list';
|
||||
|
||||
const useStyles = createStyles(({ token }) => {
|
||||
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 [updateModalOpen, handleUpdateModalOpen] = useState<boolean>(false);
|
||||
const [specList, setSpecList] = useState<API.ProductSpecListItem[]>([]);
|
||||
const [currentRow, setCurrentRow] = useState<API.CityOperatorListItem>();
|
||||
const actionRef = useRef<ActionType>();
|
||||
const formRef = useRef<ProFormInstance>();
|
||||
@ -47,6 +57,14 @@ const TableList: React.FC = () => {
|
||||
return staffOptions.find(it => it.isDefault) || staffOptions[0];
|
||||
}, [staffOptions]);
|
||||
const { styles } = useStyles();
|
||||
const specOptions = useMemo(
|
||||
() =>
|
||||
specList.map(it => ({
|
||||
value: it.productSpecId,
|
||||
label: it.title,
|
||||
})),
|
||||
[specList],
|
||||
);
|
||||
|
||||
const getAllStaffOptions = async () => {
|
||||
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) => {
|
||||
await deleteCityOperator(id);
|
||||
message.success('操作成功');
|
||||
actionRef.current?.reload();
|
||||
};
|
||||
const handleUpload = async (file: File) => {
|
||||
const { url } = await uploadFile({ file, type: 'IMAGE' });
|
||||
const url = await uploadFileWx({ file, id: currentRow?.id });
|
||||
return url;
|
||||
};
|
||||
useEffect(() => {
|
||||
getAllStaffOptions();
|
||||
getAllSpectList();
|
||||
}, []);
|
||||
|
||||
const columns: ProColumns<API.CityOperatorListItem>[] = [
|
||||
@ -121,7 +145,7 @@ const TableList: React.FC = () => {
|
||||
copyable: true,
|
||||
search: false,
|
||||
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,
|
||||
search: false,
|
||||
render(_dom, { groupQrCode }) {
|
||||
return <img className={styles.img} src={groupQrCode} alt="" />;
|
||||
return groupQrCode ? <img className={styles.img} src={groupQrCode} alt="" /> : '-';
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '可群发数量',
|
||||
dataIndex: 'sendCount',
|
||||
dataIndex: 'productSpecId',
|
||||
valueType: 'textarea',
|
||||
search: false,
|
||||
renderText: productSpecId => calcPrice(productSpecId, specList).sendCount || '-',
|
||||
},
|
||||
{
|
||||
title: '群代发价格',
|
||||
dataIndex: 'price',
|
||||
dataIndex: 'productSpecId',
|
||||
valueType: 'textarea',
|
||||
search: false,
|
||||
renderText: cents => (cents ? (cents / 100).toFixed(0) : '-'),
|
||||
renderText: productSpecId => calcPrice(productSpecId, specList).price || '-',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
@ -158,12 +183,24 @@ const TableList: React.FC = () => {
|
||||
onClick={() => {
|
||||
handleUpdateModalOpen(true);
|
||||
setCurrentRow(record);
|
||||
formRef.current?.setFieldsValue({
|
||||
...record,
|
||||
city: {
|
||||
label: record.cityName,
|
||||
value: record.cityCode,
|
||||
},
|
||||
setTimeout(() => {
|
||||
formRef.current?.setFieldsValue({
|
||||
...record,
|
||||
city: {
|
||||
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,
|
||||
cityName: formData.city.label,
|
||||
groupLink: formData.groupLink,
|
||||
sendCount: formData.sendCount,
|
||||
qroupQrCode: formData.qrCode[0].xhr.responseURL,
|
||||
price: formData.price,
|
||||
productSpecId: formData.productSpecId,
|
||||
groupQrCode: formData.qrCode[0].xhr ? formData.qrCode[0].xhr.responseURL : formData.qrCode[0].url,
|
||||
};
|
||||
console.log('update confirm', formData, params);
|
||||
try {
|
||||
@ -245,7 +281,7 @@ const TableList: React.FC = () => {
|
||||
<ProFormText name="groupLink" label="进群链接" rules={[{ message: '请输入链接', type: 'url' }]} />
|
||||
<ProFormUploadButton
|
||||
name="qrCode"
|
||||
label="上传"
|
||||
label="进群二维码"
|
||||
max={1}
|
||||
accept="image/*"
|
||||
rules={[{ required: true, message: '必填项' }]}
|
||||
@ -254,29 +290,22 @@ const TableList: React.FC = () => {
|
||||
}}
|
||||
action={handleUpload}
|
||||
/>
|
||||
<ProFormDigit name="sendCount" label="可群发数量" min={1} fieldProps={{ precision: 0 }} />
|
||||
<ProFormMoney
|
||||
name="price"
|
||||
label="价格"
|
||||
min={0}
|
||||
fieldProps={{
|
||||
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,
|
||||
};
|
||||
}}
|
||||
<ProFormSelect
|
||||
mode="single"
|
||||
name="productSpecId"
|
||||
label="可群发数量"
|
||||
options={specOptions}
|
||||
rules={[{ required: true, message: '必填项' }]}
|
||||
/>
|
||||
<ProFormDependency name={['productSpecId']}>
|
||||
{({ productSpecId }) =>
|
||||
productSpecId ? (
|
||||
<div>
|
||||
价格:{calcPrice(productSpecId, specList).price}(原价:{calcPrice(productSpecId, specList).originalPrice})
|
||||
</div>
|
||||
) : null
|
||||
}
|
||||
</ProFormDependency>
|
||||
</ModalForm>
|
||||
</PageContainer>
|
||||
);
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
import type { ActionType, ProColumns, ProFormInstance } 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 { Select } from 'antd';
|
||||
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 { getPublisherList, updatePublisherInfo } from '@/services/list';
|
||||
|
||||
@ -35,12 +37,37 @@ const PRIORITY_OPTIONS = [
|
||||
{ label: '正常处理', value: 2 },
|
||||
];
|
||||
|
||||
const COPY_TIMES = '__copy_times__';
|
||||
const COPY_EXP = '__copy_exp__';
|
||||
const TableList: React.FC = () => {
|
||||
const [updateModalOpen, handleUpdateModalOpen] = useState<boolean>(false);
|
||||
const [currentRow, setCurrentRow] = useState<API.PublisherListItem>();
|
||||
const actionRef = useRef<ActionType>();
|
||||
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>[] = [
|
||||
{
|
||||
title: '发布人昵称',
|
||||
@ -52,7 +79,17 @@ const TableList: React.FC = () => {
|
||||
title: '发布人ID',
|
||||
dataIndex: 'blPublisherId',
|
||||
valueType: 'textarea',
|
||||
copyable: true,
|
||||
render(blPublisherId) {
|
||||
return (
|
||||
<Text
|
||||
copyable={{
|
||||
onCopy: handleCopy,
|
||||
}}
|
||||
>
|
||||
{blPublisherId}
|
||||
</Text>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '优先级',
|
||||
|
||||
@ -95,16 +95,18 @@ const TableList: React.FC = () => {
|
||||
onClick={() => {
|
||||
handleUpdateModalOpen(true);
|
||||
setCurrentRow(record);
|
||||
formRef.current?.setFieldsValue({
|
||||
...record,
|
||||
qrCode: [
|
||||
{
|
||||
uid: `${record.id}_qrCode`,
|
||||
name: `${record.staffName}_qrCode`,
|
||||
status: 'done',
|
||||
url: record.staffQrCode,
|
||||
},
|
||||
],
|
||||
setTimeout(() => {
|
||||
formRef.current?.setFieldsValue({
|
||||
...record,
|
||||
qrCode: [
|
||||
{
|
||||
uid: `${record.id}_qrCode`,
|
||||
name: `${record.staffName}_qrCode`,
|
||||
status: 'done',
|
||||
url: record.staffQrCode,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
}}
|
||||
>
|
||||
@ -155,7 +157,7 @@ const TableList: React.FC = () => {
|
||||
id: currentRow?.id,
|
||||
isDefault: currentRow?.isDefault || 0,
|
||||
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);
|
||||
try {
|
||||
|
||||
@ -11,3 +11,13 @@ export async function uploadFile(options: { file: File; type: 'VIDEO' | 'IMAGE'
|
||||
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',
|
||||
});
|
||||
}
|
||||
|
||||
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;
|
||||
cityCode: string;
|
||||
groupLink: string;
|
||||
sendCount: number;
|
||||
productSpecId: string;
|
||||
contactQrCode: string;
|
||||
groupQrCode: string;
|
||||
price: number;
|
||||
id: number;
|
||||
}
|
||||
|
||||
@ -377,9 +376,8 @@ declare namespace API {
|
||||
cityName?: string;
|
||||
cityCode: string;
|
||||
groupLink: string;
|
||||
qroupQrCode: string;
|
||||
sendCount?: number;
|
||||
price?: number;
|
||||
groupQrCode: string;
|
||||
productSpecId?: string;
|
||||
created?: string;
|
||||
updated?: string;
|
||||
}
|
||||
@ -411,6 +409,7 @@ declare namespace API {
|
||||
productSpecId: string;
|
||||
productType: ProductType;
|
||||
productName: string;
|
||||
count: number;
|
||||
title: string;
|
||||
priceText: string;
|
||||
payPrice: number;
|
||||
|
||||
Reference in New Issue
Block a user