Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 447934d5ff | |||
| 6f4ef68db4 | |||
| aebb3d744e | |||
| 3d29ecbf1e | |||
| e6d1838682 | |||
| 4991720abb | |||
| 11f2624889 | |||
| 55ceed2b2e | |||
| 88977f36cd | |||
| e98bdcee49 |
@ -11,16 +11,16 @@
|
|||||||
*/
|
*/
|
||||||
export default {
|
export default {
|
||||||
// 如果需要自定义本地开发服务器 请取消注释按需调整
|
// 如果需要自定义本地开发服务器 请取消注释按需调整
|
||||||
// dev: {
|
dev: {
|
||||||
// localhost:8000/api/** -> https://preview.pro.ant.design/api/**
|
// localhost:8000/api/** -> https://preview.pro.ant.design/api/**
|
||||||
// '/api/': {
|
'/api/': {
|
||||||
// // 要代理的地址
|
// 要代理的地址
|
||||||
// target: 'http://10.147.18.100:8082',
|
target: 'https://dev.neighbourhood.cn',
|
||||||
// // 配置了这个可以从 http 代理到 https
|
// 配置了这个可以从 http 代理到 https
|
||||||
// // 依赖 origin 的功能可能需要这个,比如 cookie
|
// 依赖 origin 的功能可能需要这个,比如 cookie
|
||||||
// changeOrigin: true,
|
changeOrigin: true,
|
||||||
// },
|
},
|
||||||
// },
|
},
|
||||||
mako: {},
|
mako: {},
|
||||||
/**
|
/**
|
||||||
* @name 详细的代理配置
|
* @name 详细的代理配置
|
||||||
|
|||||||
@ -11,6 +11,8 @@ export default [
|
|||||||
{ name: '用户管理', icon: 'table', path: '/list/anchor', component: './table-list/anchor' },
|
{ name: '用户管理', icon: 'table', path: '/list/anchor', component: './table-list/anchor' },
|
||||||
{ name: '主播报单管理', icon: 'table', path: '/list/declaration', component: './table-list/declaration' },
|
{ name: '主播报单管理', icon: 'table', path: '/list/declaration', component: './table-list/declaration' },
|
||||||
{ name: '模卡管理', icon: 'table', path: '/list/resume', component: './table-list/material' },
|
{ name: '模卡管理', icon: 'table', path: '/list/resume', component: './table-list/material' },
|
||||||
|
{ name: '运营二维码管理', icon: 'table', path: '/list/staff-qrcode', component: './table-list/staff-qrcode' },
|
||||||
|
{ name: '城市运营管理', icon: 'table', path: '/list/city-operation', component: './table-list/city-operation' },
|
||||||
{ path: '/', redirect: '/list/job' },
|
{ path: '/', redirect: '/list/job' },
|
||||||
{ path: '*', layout: false, component: './404' },
|
{ path: '*', layout: false, component: './404' },
|
||||||
];
|
];
|
||||||
|
|||||||
@ -9,7 +9,7 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "5000:5000"
|
- "5000:5000"
|
||||||
environment:
|
environment:
|
||||||
- BASE_URL=http://192.168.60.191:8082
|
- BASE_URL=http://192.168.60.120:8082
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|||||||
@ -117,7 +117,7 @@
|
|||||||
<ul>
|
<ul>
|
||||||
<li>用户违反本协议约定的,平台可采取封号等方式限制用户使用,且所付服务费用不予退还。</li>
|
<li>用户违反本协议约定的,平台可采取封号等方式限制用户使用,且所付服务费用不予退还。</li>
|
||||||
<li>用户违反本协议约定,给平台或其他用户造成损失的,应承担赔偿责任。</li>
|
<li>用户违反本协议约定,给平台或其他用户造成损失的,应承担赔偿责任。</li>
|
||||||
<li>用户对平台服务不满,在未违背平台用户协议的情况下,可在付款后 3 日内申请退款,平台将在扣除已使用服务标准金额后,进行退款。超出 3 日,不予退款。</li>
|
<li>用户对平台服务不满,在未违背平台用户协议的情况下,可在付款后 3 日内申请退款,平台将在扣除已使用服务标准金额(每日7元,不足一日按一日算)后,进行退款。超出 3 日,不予退款。</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>退款申请渠道:<b>xiaochengxu@neighbourhood.com.cn</b></p>
|
<p>退款申请渠道:<b>xiaochengxu@neighbourhood.com.cn</b></p>
|
||||||
|
|
||||||
|
|||||||
@ -20,8 +20,6 @@ const genJobList = (current: number, pageSize: number) => {
|
|||||||
publisher: '甲子',
|
publisher: '甲子',
|
||||||
blPublisherId: `${index}-blPublisherId`,
|
blPublisherId: `${index}-blPublisherId`,
|
||||||
publisherAcctNo: `${index}-publisherAcctNo`,
|
publisherAcctNo: `${index}-publisherAcctNo`,
|
||||||
phone: `${index}-phone`,
|
|
||||||
acctNo: `${index}-acctNo`,
|
|
||||||
blGroupId: `${index}-blGroupId`,
|
blGroupId: `${index}-blGroupId`,
|
||||||
imGroupId: `${index}-imGroupId`,
|
imGroupId: `${index}-imGroupId`,
|
||||||
imGroupNick: '杭州主播群3',
|
imGroupNick: '杭州主播群3',
|
||||||
|
|||||||
@ -96,5 +96,6 @@
|
|||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12.0.0"
|
"node": ">=12.0.0"
|
||||||
}
|
},
|
||||||
|
"packageManager": "pnpm@10.12.4+sha512.5ea8b0deed94ed68691c9bad4c955492705c5eeb8a87ef86bc62c74a26b037b08ff9570f108b2e4dbd1dd1a9186fea925e527f141c648e85af45631074680184"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,4 +25,16 @@ export enum AdminAPI {
|
|||||||
// 模卡
|
// 模卡
|
||||||
MATERIAL_LIST = '/api/bo/resume/list',
|
MATERIAL_LIST = '/api/bo/resume/list',
|
||||||
MATERIAL_UPDATE = '/api/bo/resume/update',
|
MATERIAL_UPDATE = '/api/bo/resume/update',
|
||||||
|
// 七牛
|
||||||
|
UPLOAD_FILE = '/api/backend/file/upload',
|
||||||
|
// 运营人员
|
||||||
|
STAFF_LIST = '/api/staff/getStaffPageList',
|
||||||
|
STAFF_ALL = '/api/staff/getAllStaff',
|
||||||
|
STAFF_UPDATE = '/api/staff/addOrEdit',
|
||||||
|
STAFF_DELETE = '/api/staff/{id}',
|
||||||
|
STAFF_SET_DEFAULT = '/api/staff/setAsDefault/{id}',
|
||||||
|
// 城市运营
|
||||||
|
CITY_OPERATOR_LIST = '/api/cityOperator/getStaffPageList',
|
||||||
|
CITY_OPERATOR_UPDATE = '/api/cityOperator/addOrEdit',
|
||||||
|
CITY_OPERATOR_DELETE = '/api/cityOperator/{id}',
|
||||||
}
|
}
|
||||||
|
|||||||
247
src/pages/table-list/city-operation/index.tsx
Normal file
247
src/pages/table-list/city-operation/index.tsx
Normal file
@ -0,0 +1,247 @@
|
|||||||
|
import { PlusOutlined } from '@ant-design/icons';
|
||||||
|
import {
|
||||||
|
ActionType,
|
||||||
|
ProColumns,
|
||||||
|
ProFormInstance,
|
||||||
|
ProFormSelect,
|
||||||
|
ProFormText,
|
||||||
|
ProFormDigit,
|
||||||
|
ProFormMoney,
|
||||||
|
} from '@ant-design/pro-components';
|
||||||
|
import { ModalForm, PageContainer, ProTable } from '@ant-design/pro-components';
|
||||||
|
import '@umijs/max';
|
||||||
|
import { Button, message } from 'antd';
|
||||||
|
import { Select } from 'antd';
|
||||||
|
import { createStyles } from 'antd-style';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
|
|
||||||
|
import { CITY_OPTIONS } from '@/constants/city';
|
||||||
|
import { TIME_FORMAT } from '@/constants/global';
|
||||||
|
import { deleteCityOperator, getAllStaffList, getCityOpratorList, updateCityOperator } from '@/services/list';
|
||||||
|
|
||||||
|
const useStyles = createStyles(({ token }) => {
|
||||||
|
return {
|
||||||
|
img: {
|
||||||
|
width: 100,
|
||||||
|
height: 100,
|
||||||
|
},
|
||||||
|
delete: {
|
||||||
|
color: token.colorError,
|
||||||
|
},
|
||||||
|
button: {
|
||||||
|
marginRight: '8px',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const TableList: React.FC = () => {
|
||||||
|
const [updateModalOpen, handleUpdateModalOpen] = useState<boolean>(false);
|
||||||
|
const [currentRow, setCurrentRow] = useState<API.CityOperatorListItem>();
|
||||||
|
const actionRef = useRef<ActionType>();
|
||||||
|
const formRef = useRef<ProFormInstance>();
|
||||||
|
const [staffOptions, setStaffOptions] = useState<Array<{ label: string; value: number; isDefault: boolean }>>([]);
|
||||||
|
const defaultStaff = useMemo(() => {
|
||||||
|
return staffOptions.find(it => it.isDefault) || staffOptions[0];
|
||||||
|
}, [staffOptions]);
|
||||||
|
const { styles } = useStyles();
|
||||||
|
|
||||||
|
const getAllStaffOptions = async () => {
|
||||||
|
try {
|
||||||
|
const results = await getAllStaffList();
|
||||||
|
setStaffOptions(
|
||||||
|
results.map(it => ({
|
||||||
|
label: `${it.staffName}${it.isDefault ? ' (默认)' : ''}`,
|
||||||
|
isDefault: Boolean(it.isDefault),
|
||||||
|
value: it.id,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
} finally {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDelete = async (id: number) => {
|
||||||
|
await deleteCityOperator(id);
|
||||||
|
message.success('操作成功');
|
||||||
|
actionRef.current?.reload();
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getAllStaffOptions();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const columns: ProColumns<API.CityOperatorListItem>[] = [
|
||||||
|
{
|
||||||
|
title: '城市',
|
||||||
|
dataIndex: 'cityCode',
|
||||||
|
valueType: 'textarea',
|
||||||
|
render: (_text, record) => record.cityName,
|
||||||
|
renderFormItem() {
|
||||||
|
return (
|
||||||
|
<Select
|
||||||
|
showSearch
|
||||||
|
allowClear
|
||||||
|
options={CITY_OPTIONS}
|
||||||
|
filterOption={(input, option) => (option?.label ?? '').toLowerCase().includes(input.toLowerCase())}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '运营',
|
||||||
|
dataIndex: 'staffName',
|
||||||
|
valueType: 'textarea',
|
||||||
|
search: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '创建时间',
|
||||||
|
dataIndex: 'created',
|
||||||
|
valueType: 'dateTime',
|
||||||
|
renderText(created: string) {
|
||||||
|
return dayjs(created).format(TIME_FORMAT);
|
||||||
|
},
|
||||||
|
search: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '进群链接',
|
||||||
|
dataIndex: 'groupLink',
|
||||||
|
valueType: 'textarea',
|
||||||
|
search: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '可群发数量',
|
||||||
|
dataIndex: 'sendCount',
|
||||||
|
valueType: 'textarea',
|
||||||
|
search: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '群代发价格',
|
||||||
|
dataIndex: 'price',
|
||||||
|
valueType: 'textarea',
|
||||||
|
search: false,
|
||||||
|
renderText: cents => (cents ? (cents / 100).toFixed(0) : '-'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
valueType: 'option',
|
||||||
|
render: (_, record) => (
|
||||||
|
<>
|
||||||
|
<a
|
||||||
|
key="config"
|
||||||
|
className={styles.button}
|
||||||
|
onClick={() => {
|
||||||
|
handleUpdateModalOpen(true);
|
||||||
|
setCurrentRow(record);
|
||||||
|
formRef.current?.setFieldsValue({
|
||||||
|
...record,
|
||||||
|
city: {
|
||||||
|
label: record.cityName,
|
||||||
|
value: record.cityCode,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
修改
|
||||||
|
</a>
|
||||||
|
<a key="delete" className={styles.delete} onClick={() => handleDelete(record.id)}>
|
||||||
|
删除
|
||||||
|
</a>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
return (
|
||||||
|
<PageContainer>
|
||||||
|
<ProTable<API.CityOperatorListItem, API.PageParams>
|
||||||
|
headerTitle="查询表格"
|
||||||
|
actionRef={actionRef}
|
||||||
|
rowKey="key"
|
||||||
|
search={{ labelWidth: 120, collapsed: false, collapseRender: false }}
|
||||||
|
request={getCityOpratorList}
|
||||||
|
columns={columns}
|
||||||
|
toolBarRender={() => [
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
key="add"
|
||||||
|
icon={<PlusOutlined />}
|
||||||
|
onClick={() => {
|
||||||
|
handleUpdateModalOpen(true);
|
||||||
|
setCurrentRow(undefined);
|
||||||
|
formRef.current?.resetFields();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
新增
|
||||||
|
</Button>,
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<ModalForm
|
||||||
|
title={`${currentRow ? '修改' : '新增'}城市`}
|
||||||
|
width="400px"
|
||||||
|
formRef={formRef}
|
||||||
|
initialValues={{ staffId: defaultStaff?.value }}
|
||||||
|
open={updateModalOpen}
|
||||||
|
onOpenChange={handleUpdateModalOpen}
|
||||||
|
onFinish={async formData => {
|
||||||
|
const params: API.UpdateCityOperator = {
|
||||||
|
id: currentRow?.id,
|
||||||
|
staffId: formData.staffId,
|
||||||
|
cityCode: formData.city.value,
|
||||||
|
cityName: formData.city.label,
|
||||||
|
groupLink: formData.groupLink,
|
||||||
|
sendCount: formData.sendCount,
|
||||||
|
price: formData.price,
|
||||||
|
};
|
||||||
|
console.log('update confirm', formData, params);
|
||||||
|
try {
|
||||||
|
await updateCityOperator(params);
|
||||||
|
actionRef.current?.reload();
|
||||||
|
formRef.current?.resetFields();
|
||||||
|
} catch (e) {}
|
||||||
|
handleUpdateModalOpen(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ProFormSelect.SearchSelect
|
||||||
|
name="city"
|
||||||
|
mode="single"
|
||||||
|
label="所属城市"
|
||||||
|
options={CITY_OPTIONS}
|
||||||
|
rules={[{ required: true, message: '必填项' }]}
|
||||||
|
/>
|
||||||
|
<ProFormSelect
|
||||||
|
mode="single"
|
||||||
|
name="staffId"
|
||||||
|
showSearch
|
||||||
|
label="运营"
|
||||||
|
options={staffOptions}
|
||||||
|
rules={[{ required: true, message: '必填项' }]}
|
||||||
|
/>
|
||||||
|
<ProFormText name="groupLink" label="进群链接" rules={[{ message: '请输入链接', type: 'url' }]} />
|
||||||
|
<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,
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</ModalForm>
|
||||||
|
</PageContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default TableList;
|
||||||
@ -74,20 +74,6 @@ const TableList: React.FC = () => {
|
|||||||
search: false,
|
search: false,
|
||||||
copyable: true,
|
copyable: true,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: '通告微信号',
|
|
||||||
dataIndex: 'acctNo',
|
|
||||||
valueType: 'textarea',
|
|
||||||
search: false,
|
|
||||||
copyable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '通告手机号',
|
|
||||||
dataIndex: 'phone',
|
|
||||||
valueType: 'textarea',
|
|
||||||
search: false,
|
|
||||||
copyable: true,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: '发布人昵称',
|
title: '发布人昵称',
|
||||||
dataIndex: 'publisher',
|
dataIndex: 'publisher',
|
||||||
@ -106,6 +92,18 @@ const TableList: React.FC = () => {
|
|||||||
valueType: 'textarea',
|
valueType: 'textarea',
|
||||||
copyable: true,
|
copyable: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: '微信号',
|
||||||
|
dataIndex: 'acctNo',
|
||||||
|
valueType: 'textarea',
|
||||||
|
copyable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '手机号',
|
||||||
|
dataIndex: 'phone',
|
||||||
|
valueType: 'textarea',
|
||||||
|
copyable: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: '发布群数量',
|
title: '发布群数量',
|
||||||
dataIndex: 'relateGroupCount',
|
dataIndex: 'relateGroupCount',
|
||||||
|
|||||||
@ -30,6 +30,11 @@ const HAS_DECLARE_OPTIONS = [
|
|||||||
{ label: '无报单', value: false },
|
{ label: '无报单', value: false },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const PRIORITY_OPTIONS = [
|
||||||
|
{ label: '优先处理', value: 1 },
|
||||||
|
{ label: '正常处理', value: 2 },
|
||||||
|
];
|
||||||
|
|
||||||
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>();
|
||||||
@ -49,6 +54,16 @@ const TableList: React.FC = () => {
|
|||||||
valueType: 'textarea',
|
valueType: 'textarea',
|
||||||
copyable: true,
|
copyable: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: '优先级',
|
||||||
|
dataIndex: 'priority',
|
||||||
|
renderText(status: number) {
|
||||||
|
return PRIORITY_OPTIONS.find(option => option.value === status)?.label;
|
||||||
|
},
|
||||||
|
renderFormItem() {
|
||||||
|
return <Select showSearch allowClear options={PRIORITY_OPTIONS} />;
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: '账号状态',
|
title: '账号状态',
|
||||||
dataIndex: 'status',
|
dataIndex: 'status',
|
||||||
|
|||||||
185
src/pages/table-list/staff-qrcode/index.tsx
Normal file
185
src/pages/table-list/staff-qrcode/index.tsx
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
import { PlusOutlined } from '@ant-design/icons';
|
||||||
|
import { ActionType, ProColumns, ProFormInstance, ProFormText, ProFormUploadButton } from '@ant-design/pro-components';
|
||||||
|
import { ModalForm, PageContainer, ProTable } from '@ant-design/pro-components';
|
||||||
|
import '@umijs/max';
|
||||||
|
import { Button, message } from 'antd';
|
||||||
|
import { createStyles } from 'antd-style';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import React, { useRef, useState } from 'react';
|
||||||
|
|
||||||
|
import { TIME_FORMAT } from '@/constants/global';
|
||||||
|
import { uploadFile } from '@/services/file';
|
||||||
|
import { deleteStaff, getStaffList, setDefaultStaff, updateStaff } from '@/services/list';
|
||||||
|
|
||||||
|
const useStyles = createStyles(({ token }) => {
|
||||||
|
return {
|
||||||
|
img: {
|
||||||
|
width: 100,
|
||||||
|
height: 100,
|
||||||
|
},
|
||||||
|
delete: {
|
||||||
|
color: token.colorError,
|
||||||
|
},
|
||||||
|
button: {
|
||||||
|
marginRight: '8px',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const TableList: React.FC = () => {
|
||||||
|
const [updateModalOpen, handleUpdateModalOpen] = useState<boolean>(false);
|
||||||
|
const [currentRow, setCurrentRow] = useState<API.StaffListItem>();
|
||||||
|
const actionRef = useRef<ActionType>();
|
||||||
|
const formRef = useRef<ProFormInstance>();
|
||||||
|
const { styles } = useStyles();
|
||||||
|
|
||||||
|
const handleDefault = async (id: number) => {
|
||||||
|
await setDefaultStaff(id);
|
||||||
|
actionRef.current?.reload();
|
||||||
|
message.success('操作成功');
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDelete = async (id: number) => {
|
||||||
|
await deleteStaff(id);
|
||||||
|
actionRef.current?.reload();
|
||||||
|
message.success('操作成功');
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleUpload = async (file: File) => {
|
||||||
|
const { url } = await uploadFile({ file, type: 'IMAGE' });
|
||||||
|
return url;
|
||||||
|
};
|
||||||
|
|
||||||
|
const columns: ProColumns<API.StaffListItem>[] = [
|
||||||
|
{
|
||||||
|
title: '昵称',
|
||||||
|
dataIndex: 'staffName',
|
||||||
|
valueType: 'textarea',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '二维码',
|
||||||
|
dataIndex: 'staffQrCode',
|
||||||
|
valueType: 'textarea',
|
||||||
|
copyable: true,
|
||||||
|
search: false,
|
||||||
|
render(_dom, { staffQrCode }) {
|
||||||
|
return <img className={styles.img} src={staffQrCode} alt="" />;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '默认',
|
||||||
|
dataIndex: 'isDefault',
|
||||||
|
valueType: 'textarea',
|
||||||
|
search: false,
|
||||||
|
renderText(isDefault) {
|
||||||
|
return isDefault ? '是' : '否';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '添加时间',
|
||||||
|
dataIndex: 'created',
|
||||||
|
valueType: 'dateTime',
|
||||||
|
renderText(created: string) {
|
||||||
|
return dayjs(created).format(TIME_FORMAT);
|
||||||
|
},
|
||||||
|
search: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
valueType: 'option',
|
||||||
|
render: (_, record) => (
|
||||||
|
<>
|
||||||
|
<a
|
||||||
|
key="config"
|
||||||
|
className={styles.button}
|
||||||
|
onClick={() => {
|
||||||
|
handleUpdateModalOpen(true);
|
||||||
|
setCurrentRow(record);
|
||||||
|
formRef.current?.setFieldsValue({
|
||||||
|
...record,
|
||||||
|
qrCode: [
|
||||||
|
{
|
||||||
|
uid: `${record.id}_qrCode`,
|
||||||
|
name: `${record.staffName}_qrCode`,
|
||||||
|
status: 'done',
|
||||||
|
url: record.staffQrCode,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
修改
|
||||||
|
</a>
|
||||||
|
<a key="default" className={styles.button} onClick={() => handleDefault(record.id)}>
|
||||||
|
设为默认
|
||||||
|
</a>
|
||||||
|
<a key="delete" className={styles.delete} onClick={() => handleDelete(record.id)}>
|
||||||
|
删除
|
||||||
|
</a>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
return (
|
||||||
|
<PageContainer>
|
||||||
|
<ProTable<API.StaffListItem, API.PageParams>
|
||||||
|
headerTitle="查询表格"
|
||||||
|
actionRef={actionRef}
|
||||||
|
rowKey="key"
|
||||||
|
search={{ labelWidth: 120, collapsed: false, collapseRender: false }}
|
||||||
|
request={getStaffList}
|
||||||
|
columns={columns}
|
||||||
|
toolBarRender={() => [
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
key="add"
|
||||||
|
icon={<PlusOutlined />}
|
||||||
|
onClick={() => {
|
||||||
|
handleUpdateModalOpen(true);
|
||||||
|
setCurrentRow(undefined);
|
||||||
|
formRef.current?.resetFields();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
新增
|
||||||
|
</Button>,
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<ModalForm
|
||||||
|
title={`${currentRow ? '修改' : '新增'}运营`}
|
||||||
|
width="400px"
|
||||||
|
formRef={formRef}
|
||||||
|
open={updateModalOpen}
|
||||||
|
onOpenChange={handleUpdateModalOpen}
|
||||||
|
onFinish={async formData => {
|
||||||
|
const params: API.UpdateStaffParams = {
|
||||||
|
id: currentRow?.id,
|
||||||
|
isDefault: currentRow?.isDefault || 0,
|
||||||
|
staffName: formData?.staffName,
|
||||||
|
staffQrCode: formData.qrCode[0].xhr.responseURL,
|
||||||
|
};
|
||||||
|
console.log('update confirm', formData, params);
|
||||||
|
try {
|
||||||
|
await updateStaff(params);
|
||||||
|
actionRef.current?.reload();
|
||||||
|
formRef.current?.resetFields();
|
||||||
|
} catch (e) {}
|
||||||
|
handleUpdateModalOpen(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ProFormText name="staffName" label="昵称" rules={[{ required: true, message: '必填项' }]} />
|
||||||
|
<ProFormUploadButton
|
||||||
|
name="qrCode"
|
||||||
|
label="上传"
|
||||||
|
max={1}
|
||||||
|
accept="image/*"
|
||||||
|
rules={[{ required: true, message: '必填项' }]}
|
||||||
|
fieldProps={{
|
||||||
|
name: 'file',
|
||||||
|
}}
|
||||||
|
action={handleUpload}
|
||||||
|
/>
|
||||||
|
</ModalForm>
|
||||||
|
</PageContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default TableList;
|
||||||
@ -7,14 +7,11 @@ import { clearToken, getToken, gotoLogin } from '@/utils/login';
|
|||||||
|
|
||||||
import { IRequestResponse } from './types/http';
|
import { IRequestResponse } from './types/http';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name 全局请求配置
|
* @name 全局请求配置
|
||||||
* @doc https://umijs.org/docs/max/request#配置
|
* @doc https://umijs.org/docs/max/request#配置
|
||||||
*/
|
*/
|
||||||
export const requestConfig: RequestConfig = {
|
export const requestConfig: RequestConfig = {
|
||||||
|
|
||||||
|
|
||||||
baseURL: (window.ENV?.BASE_URL || 'https://neighbourhood.cn') as string,
|
baseURL: (window.ENV?.BASE_URL || 'https://neighbourhood.cn') as string,
|
||||||
// 错误处理: umi@3 的错误处理方案。
|
// 错误处理: umi@3 的错误处理方案。
|
||||||
errorConfig: {
|
errorConfig: {
|
||||||
|
|||||||
13
src/services/file.ts
Normal file
13
src/services/file.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { request } from '@@/exports';
|
||||||
|
|
||||||
|
import { AdminAPI } from '@/constants/api';
|
||||||
|
|
||||||
|
export async function uploadFile(options: { file: File; type: 'VIDEO' | 'IMAGE' }): Promise<API.UploadFile> {
|
||||||
|
return request<API.UploadFile>(AdminAPI.UPLOAD_FILE, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data',
|
||||||
|
},
|
||||||
|
data: options,
|
||||||
|
});
|
||||||
|
}
|
||||||
@ -4,6 +4,7 @@ import { AdminAPI } from '@/constants/api';
|
|||||||
import { EmployType, JobType } from '@/constants/job';
|
import { EmployType, JobType } from '@/constants/job';
|
||||||
import { request } from '@umijs/max';
|
import { request } from '@umijs/max';
|
||||||
import { SortOrder } from 'antd/es/table/interface';
|
import { SortOrder } from 'antd/es/table/interface';
|
||||||
|
import { buildUrl } from '@/services/utils';
|
||||||
|
|
||||||
function transformPageParams(params: API.PageParams & Record<string, any>) {
|
function transformPageParams(params: API.PageParams & Record<string, any>) {
|
||||||
params.page = params.current;
|
params.page = params.current;
|
||||||
@ -16,6 +17,10 @@ function transformPageParams(params: API.PageParams & Record<string, any>) {
|
|||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function camelToSnakeCase(str: string) {
|
||||||
|
return str.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, '$1_$2').toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
function transformSort(sort: Record<string, SortOrder>) {
|
function transformSort(sort: Record<string, SortOrder>) {
|
||||||
if (!sort) {
|
if (!sort) {
|
||||||
return {};
|
return {};
|
||||||
@ -25,7 +30,7 @@ function transformSort(sort: Record<string, SortOrder>) {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
const asc = sort[sortField] === 'ascend';
|
const asc = sort[sortField] === 'ascend';
|
||||||
return { sortField, asc };
|
return { sortField: camelToSnakeCase(sortField), asc };
|
||||||
}
|
}
|
||||||
|
|
||||||
function sortTableList<T extends { created: number; updated: number }>(
|
function sortTableList<T extends { created: number; updated: number }>(
|
||||||
@ -43,9 +48,12 @@ function sortTableList<T extends { created: number; updated: number }>(
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getJobList(params: API.PageParams & Partial<API.JobListItem>, options?: {
|
export async function getJobList(
|
||||||
[key: string]: any
|
params: API.PageParams & Partial<API.JobListItem>,
|
||||||
}) {
|
options?: {
|
||||||
|
[key: string]: any;
|
||||||
|
},
|
||||||
|
) {
|
||||||
if (!params.category) {
|
if (!params.category) {
|
||||||
params.category = JobType.All;
|
params.category = JobType.All;
|
||||||
}
|
}
|
||||||
@ -222,3 +230,71 @@ export async function updateDeclarationInfo(options: API.UpdateDeclarationParams
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getStaffList(
|
||||||
|
params: API.PageParams & Partial<API.StaffListItem>,
|
||||||
|
options?: { [key: string]: any },
|
||||||
|
) {
|
||||||
|
const result = await request<API.TableList<API.StaffListItem>>(AdminAPI.STAFF_LIST, {
|
||||||
|
method: 'GET',
|
||||||
|
params: {
|
||||||
|
...transformPageParams(params),
|
||||||
|
...(options || {}),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getAllStaffList() {
|
||||||
|
return await request<API.StaffListItem[]>(AdminAPI.STAFF_ALL, { method: 'GET' });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateStaff(options: API.UpdateStaffParams) {
|
||||||
|
return request(AdminAPI.STAFF_UPDATE, {
|
||||||
|
method: 'POST',
|
||||||
|
data: {
|
||||||
|
...(options || {}),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteStaff(id: number) {
|
||||||
|
return request(buildUrl(AdminAPI.STAFF_DELETE, { id }), {
|
||||||
|
method: 'DELETE',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function setDefaultStaff(id: number) {
|
||||||
|
return request(buildUrl(AdminAPI.STAFF_SET_DEFAULT, { id }), {
|
||||||
|
method: 'PUT',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getCityOpratorList(
|
||||||
|
params: API.PageParams & Partial<API.CityOperatorListItem>,
|
||||||
|
options?: { [key: string]: any },
|
||||||
|
) {
|
||||||
|
const result = await request<API.TableList<API.CityOperatorListItem>>(AdminAPI.CITY_OPERATOR_LIST, {
|
||||||
|
method: 'GET',
|
||||||
|
params: {
|
||||||
|
...transformPageParams(params),
|
||||||
|
...(options || {}),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateCityOperator(options: API.UpdateCityOperator) {
|
||||||
|
return request<API.GroupListItem>(AdminAPI.CITY_OPERATOR_UPDATE, {
|
||||||
|
method: 'POST',
|
||||||
|
data: {
|
||||||
|
...(options || {}),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteCityOperator(id: number) {
|
||||||
|
return request(buildUrl(AdminAPI.CITY_OPERATOR_DELETE, { id }), {
|
||||||
|
method: 'DELETE',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
62
src/services/typings.d.ts
vendored
62
src/services/typings.d.ts
vendored
@ -66,10 +66,6 @@ declare namespace API {
|
|||||||
robotImNick: string;
|
robotImNick: string;
|
||||||
// 机器人微信号
|
// 机器人微信号
|
||||||
robotImNo: string;
|
robotImNo: string;
|
||||||
// 通告微信号
|
|
||||||
acctNo
|
|
||||||
// 通告手机号
|
|
||||||
phone
|
|
||||||
// 通告发布群数量
|
// 通告发布群数量
|
||||||
relateGroupCount: number;
|
relateGroupCount: number;
|
||||||
// 创建时间,时间戳
|
// 创建时间,时间戳
|
||||||
@ -78,6 +74,14 @@ declare namespace API {
|
|||||||
updated: string;
|
updated: string;
|
||||||
// 是否禁用,默认为 false
|
// 是否禁用,默认为 false
|
||||||
disable: boolean;
|
disable: boolean;
|
||||||
|
/**
|
||||||
|
* 通告手机号
|
||||||
|
*/
|
||||||
|
phone: string;
|
||||||
|
/**
|
||||||
|
* 通告微信号
|
||||||
|
*/
|
||||||
|
acctNo: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface GroupListItem {
|
interface GroupListItem {
|
||||||
@ -326,4 +330,54 @@ declare namespace API {
|
|||||||
type UpdateDeclarationParams = Pick<DeclarationListItem, 'id' | 'weComStatus'>;
|
type UpdateDeclarationParams = Pick<DeclarationListItem, 'id' | 'weComStatus'>;
|
||||||
|
|
||||||
type UpdateMaterialParams = Pick<MaterialListItem, 'id'> & { adminOpen: number };
|
type UpdateMaterialParams = Pick<MaterialListItem, 'id'> & { adminOpen: number };
|
||||||
|
|
||||||
|
interface UploadFile {
|
||||||
|
id: string;
|
||||||
|
url: string;
|
||||||
|
coverUrl: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface StaffListItem {
|
||||||
|
id: number;
|
||||||
|
staffName: string;
|
||||||
|
staffQrCode: string;
|
||||||
|
isDefault: 1 | 0;
|
||||||
|
created: string;
|
||||||
|
updated: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UpdateStaffParams {
|
||||||
|
id?: number;
|
||||||
|
staffName: string;
|
||||||
|
staffQrCode: string;
|
||||||
|
isDefault: 1 | 0;
|
||||||
|
created?: string;
|
||||||
|
updated?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CityOperatorListItem {
|
||||||
|
cityName: string;
|
||||||
|
staffName: string;
|
||||||
|
created: string;
|
||||||
|
updated: string;
|
||||||
|
staffId: number;
|
||||||
|
cityCode: string;
|
||||||
|
groupLink: string;
|
||||||
|
sendCount: number;
|
||||||
|
price: number;
|
||||||
|
id: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UpdateCityOperator {
|
||||||
|
id?: number;
|
||||||
|
staffId: number;
|
||||||
|
staffName?: string;
|
||||||
|
cityName?: string;
|
||||||
|
cityCode: string;
|
||||||
|
groupLink: string;
|
||||||
|
sendCount?: number;
|
||||||
|
price?: number;
|
||||||
|
created?: string;
|
||||||
|
updated?: string;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
5
src/services/utils.ts
Normal file
5
src/services/utils.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export function buildUrl(url: string, params: Record<string, string | number>): string {
|
||||||
|
return Object.entries(params).reduce((result, [key, value]) => {
|
||||||
|
return result.replace(new RegExp(`\\{${key}\\}`, 'g'), String(value));
|
||||||
|
}, url);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user