Commit 582fde65 authored by guang.wu's avatar guang.wu

Merge branch 'master' of git.quantgroup.cn:ui/merchant-manage-ui into feat/test

parents 8cdb816f 02e874cc
...@@ -14,7 +14,7 @@ module.exports = { ...@@ -14,7 +14,7 @@ module.exports = {
'@typescript-eslint/no-unused-vars': ['off'], '@typescript-eslint/no-unused-vars': ['off'],
'import/no-unresolved': 0, 'import/no-unresolved': 0,
'import/extensions': 0, 'import/extensions': 0,
'no-unused-expressions': ['error', { allowShortCircuit: true }], 'no-unused-expressions': ['off'],
'template-curly-spacing': 'off', 'template-curly-spacing': 'off',
}, },
}; };
...@@ -20,6 +20,7 @@ yarn-error.log ...@@ -20,6 +20,7 @@ yarn-error.log
yarn.lock yarn.lock
*bak *bak
.vscode .vscode
.scannerwork
# visual studio code # visual studio code
.history .history
......
...@@ -48,7 +48,6 @@ const plugins = [ ...@@ -48,7 +48,6 @@ const plugins = [
}, },
], ],
]; ];
export default { export default {
antd: {}, antd: {},
dva: { dva: {
...@@ -62,8 +61,11 @@ export default { ...@@ -62,8 +61,11 @@ export default {
targets: { targets: {
ie: 11, ie: 11,
}, },
// devtool: process.env.SENTRY_ENV === 'test' ? false : 'hidden-source-map', // devtool: process.env.SENTRY_ENV === 'prod' ? 'hidden-source-map' : false,
devtool: false, // devtool: 'source-map',
// uglifyJSOptions: {
// sourceMap: true
// },
// umi routes: https://umijs.org/zh/guide/router.html // umi routes: https://umijs.org/zh/guide/router.html
routes: [ routes: [
{ {
...@@ -286,6 +288,18 @@ export default { ...@@ -286,6 +288,18 @@ export default {
name: 'businessInfo', name: 'businessInfo',
component: './businessManage/info', component: './businessManage/info',
}, },
{
title: '商户管理后台-品牌管理',
path: '/brandManage',
name: 'brandManage',
component: './BrandManage',
},
{
title: '商户管理后台-自营商品供货价更新',
path: '/supplyPriceUpdate',
name: 'supplyPriceUpdate',
component: './GoodsManage/SupplyPriceUpdate',
},
...groupMealRoute, ...groupMealRoute,
{ {
component: './404', component: './404',
...@@ -341,7 +355,11 @@ export default { ...@@ -341,7 +355,11 @@ export default {
}, },
}, },
}, },
chunks: ['antdesigns', 'vendors', 'mapvgl', 'umi'],
chainWebpack: webpackPlugin, chainWebpack: webpackPlugin,
nodeModulesTransform: {
type: 'none',
},
/* /*
proxy: { proxy: {
'/server/api/': { '/server/api/': {
......
...@@ -4,28 +4,9 @@ ...@@ -4,28 +4,9 @@
/* eslint-disable import/no-extraneous-dependencies */ /* eslint-disable import/no-extraneous-dependencies */
import ThemeColorReplacer from 'webpack-theme-color-replacer'; import ThemeColorReplacer from 'webpack-theme-color-replacer';
import generate from '@ant-design/colors/lib/generate'; import generate from '@ant-design/colors/lib/generate';
import path from 'path'; import webpack from 'webpack';
const SentryPlugin = require('@qg/sentry-webpack-plugin'); const SentryPlugin = require('@qg/sentry-webpack-plugin');
const SentryConfig = require('../.sentryclirc'); const SentryConfig = require('../.sentryclirc');
function getModulePackageName(module) {
if (!module.context) return null;
const nodeModulesPath = path.join(__dirname, '../node_modules/');
if (module.context.substring(0, nodeModulesPath.length) !== nodeModulesPath) {
return null;
}
const moduleRelativePath = module.context.substring(nodeModulesPath.length);
const [moduleDirName] = moduleRelativePath.split(path.sep);
let packageName = moduleDirName; // handle tree shaking
if (packageName && packageName.match('^_')) {
// eslint-disable-next-line prefer-destructuring
packageName = packageName.match(/^_(@?[^@]+)/)[1];
}
return packageName;
}
export default config => { export default config => {
if (process.env.NODE_ENV === 'production') { if (process.env.NODE_ENV === 'production') {
...@@ -61,44 +42,44 @@ export default config => { ...@@ -61,44 +42,44 @@ export default config => {
}, },
]); ]);
} // optimize chunks } // optimize chunks
if (process.env.SENTRY_ENV === 'prod') {
config.plugin('source-map-dev-tool-plugin').use(webpack.SourceMapDevToolPlugin, [
{
exclude: /(antdesigns|vendors|mapvgl).*/, // 以下文件排除不生成
filename: '[file].map[query]', // 指定map文件名称
},
]);
}
config.optimization // share the same chunks across different modules config.optimization // share the same chunks across different modules
.runtimeChunk(false)
.splitChunks({ .splitChunks({
chunks: 'async', chunks: 'all', //async异步代码分割 initial同步代码分割 all同步异步分割都开启
name: 'vendors', automaticNameDelimiter: '.',
maxInitialRequests: Infinity, name: true,
minSize: 0, minSize: 30000, // 引入的文件大于30kb才进行分割
minChunks: 1, // 模块至少使用次数
cacheGroups: { cacheGroups: {
// 分离antd系代码
antdesigns: {
name: 'antdesigns',
chunks: 'all',
test: /[\\/]node_modules[\\/](@antv|antd|@ant-design|rc-*|[\\@]ctrl[\\/]tinycolor|tinycolor2)/,
priority: 10,
},
// 分离公共类库
vendors: { vendors: {
test: module => { name: 'vendors',
const packageName = getModulePackageName(module) || ''; chunks: 'all',
test: /([\\/]node_modules[\\/](lodash|moment|react|dva|postcss|mapbox-gl|date-fns|sentry|react-sortablejs|sortablejs))|([\\/]src[\\/]utils[\\/]qiniu\.min\.js)/,
if (packageName) { priority: 10,
return [ enforce: true,
'bizcharts', },
'gg-editor', // 分离geo库
'g6', mapvgl: {
'@antv', name: 'mapvgl',
'gg-editor-core', chunks: 'all',
'bizcharts-plugin-slider', test: /[\\/]node_modules[\\/](mapvgl|react-bmapgl|react-amap)/,
].includes(packageName); priority: 11,
}
return false;
},
name(module) {
const packageName = getModulePackageName(module);
if (packageName) {
if (['bizcharts', '@antv_data-set'].indexOf(packageName) >= 0) {
return 'viz'; // visualization package
}
}
return 'misc';
},
}, },
}, },
}); });
......
...@@ -3,8 +3,8 @@ ...@@ -3,8 +3,8 @@
*/ */
// 从tob进入的判断接口前缀 // 从tob进入的判断接口前缀
const getUrlParams = name => { const getUrlParams = name => {
const regArg = new RegExp(`(^|&)${name}=([^&]*)(&|$)`); const regArg = RegExp(`(^|&)${name}=([^&]*)(&|$)`);
const r = window.location.search.substring(1).match(regArg); const r = regArg.exec(window.location.search.substring(1));
if (r != null) return decodeURIComponent(r[2]); if (r != null) return decodeURIComponent(r[2]);
return null; return null;
}; };
......
This diff is collapsed.
...@@ -73,6 +73,7 @@ ...@@ -73,6 +73,7 @@
"path-to-regexp": "^3.1.0", "path-to-regexp": "^3.1.0",
"pubsub-js": "^1.9.4", "pubsub-js": "^1.9.4",
"qs": "^6.9.0", "qs": "^6.9.0",
"rc-field-form": "^1.33.0",
"react": "^16.14.0", "react": "^16.14.0",
"react-amap": "^1.2.8", "react-amap": "^1.2.8",
"react-bmapgl": "^0.2.17", "react-bmapgl": "^0.2.17",
...@@ -127,7 +128,8 @@ ...@@ -127,7 +128,8 @@
"node-fetch": "^2.6.0", "node-fetch": "^2.6.0",
"prettier": "^1.17.1", "prettier": "^1.17.1",
"pro-download": "1.0.1", "pro-download": "1.0.1",
"stylelint": "^10.1.0" "stylelint": "^10.1.0",
"webpack": "^4.46.0"
}, },
"engines": { "engines": {
"node": ">=10.0.0" "node": ">=10.0.0"
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
import * as Sentry from '@sentry/react'; import * as Sentry from '@sentry/react';
import localStorage from '@/utils/localStorage'; import localStorage from '@/utils/localStorage';
if (process.env.SENTRY_ENV !== 'test' && process.env.NODE_ENV === 'production') { if (process.env.SENTRY_ENV !== 'prod' && process.env.NODE_ENV === 'production') {
try { try {
Sentry.init({ Sentry.init({
dsn: 'https://b3f60c62e1234e26a5b851b9f26fba07@sentry.q-gp.com/34', dsn: 'https://b3f60c62e1234e26a5b851b9f26fba07@sentry.q-gp.com/34',
......
...@@ -24,7 +24,7 @@ const CustomTree = forwardRef(props => { ...@@ -24,7 +24,7 @@ const CustomTree = forwardRef(props => {
const findArr = childrens => { const findArr = childrens => {
childrens.forEach(node => { childrens.forEach(node => {
if (node.children && node.children.length) { if (node.children && node.children.length) {
node.visibleChildren && arr.push(initChildrenStatus(node.children, node.checked)); node.visibleChildren && arr.push(initChildrenStatus(node.children));
findArr(node.children); findArr(node.children);
} }
}); });
...@@ -69,7 +69,7 @@ const CustomTree = forwardRef(props => { ...@@ -69,7 +69,7 @@ const CustomTree = forwardRef(props => {
} }
}); });
}; };
props.value && props.value.length && checkfn(props.value, json); props.value && props.value.length && checkfn(props.value);
}; };
// 格式化数据 // 格式化数据
const filterData = (arr, parentChecked) => { const filterData = (arr, parentChecked) => {
...@@ -247,7 +247,7 @@ const CustomTree = forwardRef(props => { ...@@ -247,7 +247,7 @@ const CustomTree = forwardRef(props => {
await getSyncLoadChildrens(datas, ckey, isChecked); await getSyncLoadChildrens(datas, ckey, isChecked);
} }
changeChecked(datas, ckey, isChecked); changeChecked(datas, ckey, isChecked);
const values = getCheckTreeValue(datas, isChecked); const values = getCheckTreeValue(datas);
if (level === 1) { if (level === 1) {
onVisibleChildren('', false); onVisibleChildren('', false);
} }
......
import { DownloadOutlined } from '@ant-design/icons';
import { Modal, Button, Empty, Pagination } from 'antd';
import React, { useState, useEffect } from 'react';
import { unstable_batchedUpdates } from 'react-dom';
import styles from './index.less';
import { apiUploadResult } from './service';
const ImportListModal = props => {
const { visible } = props;
const [data, setData] = useState([]);
const [total, setTotal] = useState(0);
const [pageInfo, setPageInfo] = useState({
pageNo: 1,
pageSize: 2,
});
const getImportData = async params => {
const res = await apiUploadResult({
type: props.type,
...params,
});
if (res?.data) {
unstable_batchedUpdates(() => {
setData(res.data.records);
setTotal(res.data.total);
});
}
};
const onChangePage = (pageNo, pageSize) => {
const obj = { pageNo, pageSize };
setPageInfo(obj);
getImportData(obj);
};
const onDownload = url => {
window.location.href = url;
};
useEffect(() => {
getImportData(pageInfo);
}, []);
return (
<Modal
title={
<div>
查看导入记录<span className={styles.subTitle}>(仅展示近半年记录)</span>
</div>
}
open={visible}
onCancel={props.onCancel}
width="800px"
footer={false}
>
{data.length ? (
<div>
<div className={styles.tableBody}>
{data.map(item => (
<div className={styles.card} key={item.id}>
<div>
<div className={styles.top}>
<span>导入数据:{item.total}</span>
<span>成功数:{item.successCount}</span>
<span>失败数:{item.failedCount}</span>
</div>
<div>
<span>导入时间:{item.createdAt}</span>
<span>操作人:{item.createdBy}</span>
</div>
</div>
<div className={styles.errWrappper}>
{item.failedCount > 0 && (
<Button type="primary" onClick={() => onDownload(item.failedFileUrl)}>
<DownloadOutlined />
下载错误数据
</Button>
)}
{item.status && +item.status.code === 3 && (
<div className={styles.errorMessage}>{item.failedMessage}</div>
)}
</div>
</div>
))}
</div>
<div className={styles.pagewrapper}>
<Pagination
total={total}
showTotal={e => `总共 ${e} 条数据`}
pageSize={pageInfo.pageSize}
defaultCurrent={pageInfo.pageNo}
onChange={onChangePage}
onShowSizeChange={onChangePage}
showQuickJumper
// showSizeChanger
/>
</div>
</div>
) : (
<Empty />
)}
</Modal>
);
};
export default ImportListModal;
// 导入配置
export const ImportConfig = {
// 竞价商品批量选品
binding: {
title: '自营商品供货价库存批量更新',
type: 6,
tempPath: '',
limitNum: 5000,
hideDownload: true,
tip: 'SKU',
},
};
import React, { useState, useEffect } from 'react';
import { Form } from '@ant-design/compatible';
import { Modal, Button, Upload, notification } from 'antd';
import styles from './index.less';
import { apiImportGoods } from './service';
import ImportListModal from './ImportListModal';
import { ImportConfig } from './config';
const ImportGoods = React.memo(props => {
const [importFile, setImportFile] = useState([]);
const [loading, setLoading] = useState(false);
const [visibleRecord, setVisibleRecord] = useState(false);
const [config, setConfig] = useState({});
// 关闭弹窗
const onCancel = () => {
props.onHide();
};
// 查看导入记录
const onShowRecord = () => {
setVisibleRecord(true);
};
// 下载模板
const onDownTemplate = () => {
if (config.tempPath) {
window.location.href = config.tempPath;
} else if (props.onDownload) {
props.onDownload(config);
}
};
// 导入
const onImport = async () => {
if (!importFile.length) {
notification.error({
message: '请选择导入文件',
});
return;
}
setLoading(true);
const result = await apiImportGoods(importFile[0], config.type);
setLoading(false);
if (result?.success) {
setImportFile([]);
notification.success({
message:
'已操作上传,稍后上传成功后,请在操作历史中,查看上传结果,如有未上传成功的数据,请根据返回原因,及时处理,修改后重新上传。',
});
}
};
// 导入按钮配置属性
const uploadFileAttr = {
name: 'file',
maxCount: 1,
fileList: importFile,
async customRequest(info) {
setImportFile([info.file]);
return false;
},
accept: '.xlsx',
showUploadList: true,
onRemove: () => {
setImportFile([]);
},
};
useEffect(() => {
if (props.visible) {
setConfig(ImportConfig[props.importType]);
}
}, [props.visible]);
return (
<>
<Modal
title={config.title}
open={props.visible}
onCancel={onCancel}
footer={[
<Button type="link" onClick={onShowRecord} key="btnlook">
查看导入记录
</Button>,
!config.hideDownload && (
<Button type="link" onClick={onDownTemplate} key="btndown">
下载模板
</Button>
),
<Button onClick={onCancel} key="btncancel">
关闭
</Button>,
<Button type="primary" loading={loading} onClick={onImport} key="btnimprot">
导入
</Button>,
]}
>
<Form>
<Form.Item label="导入文件">
<Upload {...uploadFileAttr}>
<Button type="link" key="btnsel">
选择文件
</Button>
</Upload>
<div className={styles.textDesc}>
<div>1、仅支持按商品{config.tip}导入,纵向排列</div>
<div>2、支持Excel格式文件,导入数量限制{config.limitNum}</div>
</div>
</Form.Item>
</Form>
</Modal>
{visibleRecord && (
<ImportListModal
visible={visibleRecord}
type={config.type}
onCancel={() => setVisibleRecord(false)}
/>
)}
</>
);
});
export default Form.create()(ImportGoods);
.card {
display: flex;
justify-content: space-between;
margin-bottom: 20px;
padding: 20px;
background-color: #fafafa;
span {
display: inline-block;
padding-right: 15px;
}
}
.top {
margin-bottom: 15px;
font-weight: 600;
font-size: 16px;
}
.subTitle {
color: #333;
font-weight: normal;
font-size: 14px;
}
.tableBody {
min-height: 244px;
}
.pagewrapper {
text-align: right;
}
.errWrappper {
display: flex;
align-items: flex-end;
justify-content: flex-end;
}
.errorMessage {
max-width: 300px;
}
.textDesc {
color: #999;
line-height: 32px;
}
import request from '@/utils/request';
import config from '@/../config/env.config';
import qs from 'qs';
const { goodsApi } = config;
// 商品导入接口
export async function apiImportGoods(file, type) {
const params = new FormData();
params.append('file', file);
params.append('type', type);
const res = await request.post('/api/merchants/importFile/excel', {
data: params,
prefix: goodsApi,
});
return res;
}
// 分页查询上传文件结果
export async function apiUploadResult(params) {
const data = await request.get(
`/api/merchants/importFile/info/page${qs.stringify(params, { addQueryPrefix: true })}`,
{
prefix: goodsApi,
},
);
return data;
}
import {
DeleteOutlined,
EyeOutlined,
PlusOutlined,
LoadingOutlined,
FileFilled,
} from '@ant-design/icons';
import { Modal, Upload, notification, Spin } from 'antd';
import React, { useState, useEffect, useRef, forwardRef } from 'react';
import lodash from 'lodash';
import { apiFileUpload } from './service.js';
import { ReactSortable } from 'react-sortablejs';
import styles from './style.less';
import { checkIsImagePath } from '@/utils/utils';
const MAX_FILE_SIZE = 5;
const UNIT = 1024 * 1024;
const UploadButton = (
<div>
<PlusOutlined />
<div style={{ marginTop: 8 }}>上传图片</div>
</div>
);
const renderFile = item => {
if (item.status === 'done') {
if (checkIsImagePath(item.url)) {
return <img width="100%" key={item.uid} src={item.url} alt="" />;
}
return (
<div className={styles.imgLoading}>
<a target="_blank" href={item.url} rel="noopener noreferrer">
<FileFilled />
</a>
</div>
);
}
return (
<div className={styles.imgLoading}>
<LoadingOutlined />
<div className={styles.imgLoadingText}>文件上传中</div>
</div>
);
};
const UploadImage = forwardRef((props, ref) => {
const {
name = `${Date.now()}`,
limit = null,
multiple = true,
disabled,
uploadParams,
value,
width = 0,
height = 0,
canUploadFile = false, // 是否可以上传图片之外的文件
accept = ['jpg', 'jpeg', 'png'],
} = props;
const [uploadLoading, setUploadLoading] = useState(false);
const [previewVisible, setPreviewVisible] = useState(false);
const [previewImage, setPreviewImage] = useState('');
const [previewTitle, setPreviewTitle] = useState('');
const [fileList, setFileList] = useState([]);
const [activeImgIndex, setActiveImgIndex] = useState(null);
const fileListRef = useRef([]);
useEffect(() => {
const newPictures = (value || []).map((url, ind) => ({
url,
name: url,
uid: `${ind}`,
status: !url ? '' : 'done',
}));
fileListRef.current = [...newPictures];
setFileList([...newPictures]);
}, [value]);
const handleCancel = () => setPreviewVisible(false);
const handlePreview = async file => {
if (checkIsImagePath(file.url)) {
setPreviewImage(file.url);
setPreviewVisible(true);
setPreviewTitle(file.name || file.url.substring(file.url.lastIndexOf('/') + 1));
} else if (file.url) {
window.open(file.url);
}
};
const bundleChange = imgFile => {
const imgList = imgFile.map(item => item.url);
props.onChange(imgList);
};
const handleRemove = file => {
const freshFiles = fileList?.filter(ele => ele.uid !== file.uid);
bundleChange(freshFiles);
};
const checkFile = file =>
new Promise(resolve => {
const curType = file.name.substr(file.name.lastIndexOf('.') + 1).toLowerCase();
if (!canUploadFile && !accept.includes(curType)) {
notification.open({
message: file.name,
description: `文件格式须为${accept.join('')}!`,
});
return resolve(null);
}
if (file.size > MAX_FILE_SIZE * UNIT) {
notification.open({
message: file.name,
description: `单个文件大小不能超过${MAX_FILE_SIZE}M!`,
});
return resolve(null);
}
return resolve(file);
});
const imageLoading = (file, ret) => ({
uid: file.uid,
status: 'done',
name: file.name,
url: ret,
});
const getImageSize = async file =>
new Promise((resolve, reject) => {
const fileObj = file;
// 获取上传的图片的宽高
const reader = new FileReader();
reader.readAsDataURL(fileObj);
reader.onload = evt => {
const replaceSrc = evt.target.result;
const imageObj = new Image();
imageObj.src = replaceSrc;
imageObj.onload = () => {
const { width: widthPx, height: heightPx } = imageObj;
file.width = widthPx;
file.height = heightPx;
resolve(file);
};
};
reader.onerror = error => reject(error);
});
const validateImageSize = async files => {
const fileAll = files.map(item => getImageSize(item));
const checkFiles = (await Promise.all(fileAll)).filter(item => item !== null);
const checkSize = checkFiles.some(
item => (item.width !== width && width !== 0) || (item.height !== height && height !== 0),
);
if (checkSize) {
notification.warning({ message: '图片尺寸不正确' });
return false;
}
return true;
};
const defaultBeforeUpload = lodash.debounce(
(file, fileArray) =>
// 文件显示
new Promise(async () => {
if (limit && fileListRef.current.length + fileArray.length > limit) {
Modal.warning({
maskClosable: true,
title: '超出上传个数',
});
return Upload.LIST_IGNORE;
}
if (width !== 0 || height !== 0) {
const checkSize = await validateImageSize(fileArray);
if (!checkSize) {
return Upload.LIST_IGNORE;
}
}
const fileAll = fileArray.map(item => checkFile(item));
const checkFiles = (await Promise.all(fileAll)).filter(item => item !== null);
try {
if (checkFiles.length) {
setUploadLoading(true);
const res = await apiFileUpload(checkFiles);
console.log('res', res);
if (res) {
const proFiles = (res || []).map((urlItem, urlIndex) =>
imageLoading(checkFiles[urlIndex], urlItem.data),
);
// const imagList = await Promise.all(proFiles);
const newFiles = [...fileListRef.current, ...proFiles];
bundleChange(newFiles);
} else {
notification.warning({
message: '警告',
description: res.msg,
});
}
setUploadLoading(false);
}
} catch (error) {
console.log('error', error);
setUploadLoading(false);
Modal.warning({
maskClosable: true,
title: '上传失败,请重新尝试!',
});
}
return null;
}),
);
return (
<Spin tip="正在上传..." spinning={uploadLoading} delay={100}>
<div>
{fileList.length > 0 && (
<ReactSortable animation={300} list={fileList} setList={list => bundleChange(list)}>
{fileList.map((item, index) => (
<div
key={item.uid}
className={styles.sortImg}
onMouseEnter={() => setActiveImgIndex(index)}
onMouseLeave={() => setActiveImgIndex(null)}
>
<div className={styles.imgBox}>{renderFile(item)}</div>
{activeImgIndex === index && (
<div className={styles.mask} key={item.uid}>
<EyeOutlined className={styles.maskIcon} onClick={() => handlePreview(item)} />
<DeleteOutlined
className={styles.maskIcon}
onClick={() => handleRemove(item)}
/>
</div>
)}
</div>
))}
</ReactSortable>
)}
</div>
<Upload
{...uploadParams}
disabled={Boolean(disabled)}
multiple={multiple}
name={name}
customRequest={() => {}}
listType="picture-card"
beforeUpload={defaultBeforeUpload}
fileList={fileList}
onPreview={handlePreview}
onRemove={handleRemove}
showUploadList={false}
>
{limit !== null && fileList.length >= limit ? null : UploadButton}
</Upload>
<Modal open={previewVisible} title={previewTitle} footer={null} onCancel={handleCancel}>
<img alt="example" style={{ width: '100%' }} src={previewImage} />
</Modal>
</Spin>
);
});
export default UploadImage;
import request from '@/utils/request';
import config from '@/../config/env.config';
import UUID from '@/utils/uuid';
const { goodsApi } = config;
// export const merchantUpload = async files => {
// const params = new FormData();
// files.forEach(file => params.append('file', file));
// const data = await request.post('/image/api/merchant/upload', {
// prefix: goodsApi,
// data: params,
// });
// return data;
// };
export const apiFileUpload = async files => {
const data = await Promise.all(
files.map(async file => {
const params = new FormData();
params.append('file', file);
const uuid = UUID.createUUID();
if (file.name) {
const arr = file.name.split('.');
params.append('fileName', `${uuid}.${arr[arr.length - 1]}`);
}
const res = await request.post(`/api/merchants/images/uploadFile?id=${uuid}`, {
prefix: goodsApi,
data: params,
});
return res;
}),
);
console.log('data', data);
return data;
};
.sortImg {
position: relative;
display: inline-block;
width: 105px;
height: 105px;
margin-right: 10px;
margin-bottom: 10px;
padding: 9px;
border: 1px solid #ddd;
border-radius: 6px;
}
.mask {
position: absolute;
top: 9px;
right: 9px;
bottom: 9px;
left: 9px;
padding-top: 27%;
text-align: center;
background: #000;
opacity: 0.5;
}
.maskIcon {
margin-right: 5px;
color: #efefef !important;
font-size: 16px;
cursor: pointer;
}
.imgBox {
position: relative;
width: 100%;
height: 100%;
overflow: hidden;
}
...@@ -11,7 +11,6 @@ import { connect } from 'dva'; ...@@ -11,7 +11,6 @@ import { connect } from 'dva';
import { Icon as LegacyIcon } from '@ant-design/compatible'; import { Icon as LegacyIcon } from '@ant-design/compatible';
import { Result, Button, Layout, Menu } from 'antd'; import { Result, Button, Layout, Menu } from 'antd';
import Authorized from '@/utils/Authorized'; import Authorized from '@/utils/Authorized';
import localStorage from '@/utils/localStorage';
import RightContent from '@/components/GlobalHeader/RightContent'; import RightContent from '@/components/GlobalHeader/RightContent';
import MessageReminder from '@/components/MessageReminder'; import MessageReminder from '@/components/MessageReminder';
import { getAuthorityFromRouter, getUrlSearchParams, getToken } from '@/utils/utils'; import { getAuthorityFromRouter, getUrlSearchParams, getToken } from '@/utils/utils';
...@@ -229,6 +228,7 @@ const BasicLayout = props => { ...@@ -229,6 +228,7 @@ const BasicLayout = props => {
} }
if (isTob && searchPrams.token) { if (isTob && searchPrams.token) {
console.log('window.parent :>> ', window.parent); console.log('window.parent :>> ', window.parent);
window.wp = window.parent;
return tobLayout(); return tobLayout();
} }
return merchantLayout(); return merchantLayout();
......
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { Row, Col } from 'antd'; import { Row, Col } from 'antd';
import { Link } from 'umi'; import { Link } from 'umi';
// import { PageHeaderWrapper } from '@ant-design/pro-layout';
// eslint-disable-next-line import/no-extraneous-dependencies
import { FileTextOutlined } from '@ant-design/icons'; import { FileTextOutlined } from '@ant-design/icons';
import style from './styles.less'; import style from './styles.less';
import { getPendingNum } from './service'; import { getPendingNum } from './service';
import { connect } from 'dva'; import { connect } from 'dva';
import { import { AFTER_SALE_ORDER, PENDING_DELIVERY_ORDER } from '@/../config/permission.config';
AFTER_SALE_ORDER,
CANCEL_BILL_MANAGE,
PENDING_DELIVERY_ORDER,
} from '@/../config/permission.config';
const Admin = props => { const Admin = props => {
const [pendingNum, setPendingNum] = useState({}); const [pendingNum, setPendingNum] = useState({});
const showAfterSaleList = props.permissions[AFTER_SALE_ORDER.LIST]; const showAfterSaleList = props.permissions[AFTER_SALE_ORDER.LIST];
const showCancelBillList = props.permissions[CANCEL_BILL_MANAGE.LIST];
const showPendingDeliveryOrderList = props.permissions[PENDING_DELIVERY_ORDER.LIST]; const showPendingDeliveryOrderList = props.permissions[PENDING_DELIVERY_ORDER.LIST];
const qurey = async () => { const query = async () => {
const res = await getPendingNum(); const res = await getPendingNum();
if (res && res.data) { setPendingNum(res?.data || {});
setPendingNum(res.data);
}
}; };
useEffect(() => { useEffect(() => {
qurey(); query();
}, []); }, []);
return ( return (
......
/**
* 数据模型转换-接口获取数据转换为表单数据
*/
export const transformVOToFormData = data => {
const params = { ...data };
if (params.authorizationUrl) {
params.authorizationUrl = params.authorizationUrl.split(',');
}
if (params.horizontalLogo) {
params.horizontalLogo = [params.horizontalLogo];
}
if (params.logo) {
params.logo = [params.logo];
}
if (params.qualifyUrl) {
params.qualifyUrl = params.qualifyUrl.split(',');
}
return params;
};
/*
* 表单数据转换-表单数据转换为接口数据
*/
export const transformFormDataToDTO = res => {
const params = { ...res };
if (params.authorizationUrl?.length) {
params.authorizationUrl = params.authorizationUrl.join(',');
}
if (params.horizontalLogo?.length) {
params.horizontalLogo = params.horizontalLogo.join(',');
}
if (params.logo?.length) {
params.logo = params.logo.join(',');
}
if (params.qualifyUrl?.length) {
params.qualifyUrl = params.qualifyUrl.join(',');
}
return params;
};
import React from 'react';
import { BetaSchemaForm } from '@ant-design/pro-components';
import { notification } from 'antd';
import { brandInfoColumn } from '../staticData.js';
import { layout } from '@/utils/bll';
import { apiBrandDetail, apiCreateBrand, apiEditBrand, apiAppendQualifyBrand } from '../services';
import { transformVOToFormData, transformFormDataToDTO } from '../bll.js';
/**
* 品牌信息组件
*/
const BrandInfo = props => {
const refForm = React.useRef();
const { actionStatus, brandId } = props;
const closeModal = v => {
refForm?.current?.resetFields?.();
!v && props.onClose(false);
};
const getAPI = () => {
switch (actionStatus) {
case 'edit':
return apiEditBrand;
case 'supplement':
return apiAppendQualifyBrand;
default:
return apiCreateBrand;
}
};
const submitForm = async values => {
const params = transformFormDataToDTO(values);
params.brandId = brandId;
params.id = brandId;
const api = getAPI();
const res = await api(params);
if (res?.success) {
notification.success({
message: '提交成功',
});
props.onClose(true);
}
};
const getInfo = () =>
new Promise(resolve => {
setTimeout(async () => {
if (brandId) {
const res = await apiBrandDetail({ brandId });
if (res?.data) {
const data = transformVOToFormData(res.data);
resolve(data);
}
}
resolve({});
}, 200);
});
const config = { actionStatus };
return (
props.visible && (
<BetaSchemaForm
layoutType="ModalForm"
title="品牌信息"
open={props.visible}
width="800px"
modalProps={{
maskClosable: true,
destroyOnClose: true,
}}
request={getInfo}
formRef={refForm}
onOpenChange={closeModal}
layout="horizontal"
{...layout}
onFinish={submitForm}
columns={brandInfoColumn(config)}
/>
)
);
};
export default BrandInfo;
import React, { useState, useRef } from 'react';
import { ProTable } from '@ant-design/pro-components';
import { Button } from 'antd';
import { PlusOutlined } from '@ant-design/icons';
import { apiBrandList } from './services';
import { brandColumn, brandActionAdd } from './staticData.js';
import utilStyle from '@/utils/utils.less';
import BrandInfo from './components/BrandInfo.jsx';
/**
* 品牌管理列表
*/
const BrandManage = () => {
const refTable = useRef();
const [id, setId] = useState('');
const [visible, setVisible] = useState(false);
const [actionStatus, setActionStatus] = useState(brandActionAdd);
// 编辑品牌
const onAction = (record, status) => {
setId(record.brandId);
setActionStatus(status);
setVisible(!0);
};
const onClose = refresh => {
setVisible(false);
refresh && refTable.current?.reload();
};
return (
<>
<ProTable
actionRef={refTable}
search={{
collapsed: false,
span: 8,
className: utilStyle.formSearch,
collapseRender: () => null,
}}
columns={brandColumn({
refTable,
onAction,
})}
request={apiBrandList}
toolBarRender={() => [
<Button
key="add"
icon={<PlusOutlined />}
type="primary"
onClick={() => {
setId('');
setActionStatus(brandActionAdd);
setVisible(!0);
}}
>
添加
</Button>,
]}
rowKey={r => r.brandId}
bordered
options={false}
/>
<BrandInfo visible={visible} actionStatus={actionStatus} brandId={id} onClose={onClose} />
</>
);
};
export default BrandManage;
import qs from 'qs';
import request from '@/utils/request';
import config from '@/../config/env.config';
const { kdspApi } = config;
/**
* 分页查询所有品牌列表
* http://yapi.quantgroups.com/project/389/interface/api/66404
*/
export async function apiBrandList(params) {
const param = {
...params,
pageNo: params.current,
};
const res = await request.get(`/api/merchants/brands/list?${qs.stringify(param)}`, {
prefix: kdspApi,
});
if (res?.data) {
return {
total: res.data.total,
data: res.data.records,
};
}
return {
total: 0,
data: [],
};
}
/**
* 添加品牌
* http://yapi.quantgroups.com/project/389/interface/api/66389
* */
export async function apiCreateBrand(data) {
return request.post('/api/merchants/brands/add', {
data,
prefix: kdspApi,
});
}
/**
* 编辑品牌
* http://yapi.quantgroups.com/project/389/interface/api/66394
*/
export async function apiEditBrand(data) {
return request.post('/api/merchants/brands/edit', {
data,
prefix: kdspApi,
});
}
/**
* 补充资质
* http://yapi.quantgroups.com/project/389/interface/api/66399
*/
export async function apiAppendQualifyBrand(data) {
return request.post('/api/merchants/brands/qualify/add', {
data,
prefix: kdspApi,
});
}
/**
* 查询品牌信息
* http://yapi.quantgroups.com/project/389/interface/api/66409
* */
export async function apiBrandDetail(params) {
return request.get(`/api/merchants/brands/detail?${qs.stringify(params)}`, {
prefix: kdspApi,
});
}
import React from 'react';
import { Button, Popover } from 'antd';
import UploadImage from '@/components/UploadImg/index.jsx';
// 品牌审核状态
/**
* @description: 品牌审核状态 1-待审核
*/
export const auditStatusWait = 1;
/**
* @description: 品牌审核状态 2-审核通过
*/
export const auditStatusPass = 2;
/**
* @description: 品牌审核状态 3-审核拒绝
*/
export const auditStatusReject = 3;
/**
* @description: 品牌审核状态枚举 0-无 1-待审核,2-审核通过,3-审核拒绝
*/
export const brandStatusEnum = {
// 0: '-',
[auditStatusWait]: '待审核',
[auditStatusPass]: '审核通过',
[auditStatusReject]: '驳回',
};
/**
* @description: 品牌审核 2审核通过 3驳回
*/
export const brandAuditEnum = {
[auditStatusPass]: '审核通过',
[auditStatusReject]: '驳回',
};
// 操作状态 查看、修改、添加、补充资质
/**
* @description: 品牌操作状态 查看
*/
export const brandActionFind = 'find';
/**
* @description: 品牌操作状态 修改
*/
export const brandActionEdit = 'edit';
/**
* @description: 品牌操作状态 添加
*/
export const brandActionAdd = 'add';
/**
* @description: 品牌操作状态 补充资质
*/
export const brandActionSupplement = 'supplement';
/**
* @description: 列表基础字段
*/
export const brandBaseColumn = [
{
title: '品牌名称',
dataIndex: 'name',
key: 'name',
align: 'center',
},
{
title: '中文名称',
dataIndex: 'chineseName',
key: 'chineseName',
hideInSearch: true,
align: 'center',
},
{
title: '英文名称',
key: 'englishName',
dataIndex: 'englishName',
hideInSearch: true,
align: 'center',
},
{
title: '品牌缩写或别称',
key: 'alias',
dataIndex: 'alias',
hideInSearch: true,
align: 'center',
},
];
// 品牌列表字段
export const brandColumn = config => {
const { onAction } = config;
return [
...brandBaseColumn,
{
title: '审核状态',
key: 'status',
dataIndex: 'status',
align: 'center',
valueEnum: brandStatusEnum,
render: (_, r) => {
const { status } = r;
return status === auditStatusReject ? (
<Popover content={r.rejectReason} title="驳回原因" trigger="hover">
<Button type="link" danger>
驳回
</Button>
</Popover>
) : (
brandStatusEnum[status]
);
},
},
{
title: '操作',
hideInSearch: true,
dataIndex: 'option',
align: 'center',
width: 200,
render: (_, r) => [
(r.modifiable && (
<Button key="check" type="primary" onClick={() => onAction(r, 'edit')}>
修改
</Button>
)) ||
'',
([null, auditStatusReject].includes(r.status) && !r.modifiable && (
<Button key="supplement" type="primary" ghost onClick={() => onAction(r, 'supplement')}>
补充资质
</Button>
)) ||
'',
],
},
];
};
// 品牌信息字段
export const brandInfoColumn = config => {
const { actionStatus } = config;
const disabled = brandActionSupplement === actionStatus;
const baseInfo = [
{
title: '资质证书',
dataIndex: 'qualifyUrl',
formItemProps: {
rules: [{ required: true, message: '请选择资质证书' }],
},
renderFormItem: () => (
<UploadImage limit={30} accept={['jpg', 'jpeg', 'png', 'zip', 'rar', 'pdf']} />
),
},
{
title: '授权证书',
dataIndex: 'authorizationUrl',
formItemProps: {
rules: [{ required: true, message: '请选择授权证书' }],
},
renderFormItem: () => (
<UploadImage limit={30} accept={['jpg', 'jpeg', 'png', 'zip', 'rar', 'pdf']} />
),
},
{
title: '品牌名称',
dataIndex: 'name',
maxLength: 50,
fieldProps: {
disabled,
},
formItemProps: {
rules: disabled ? [] : [{ required: true, message: '请输入品牌名称' }],
},
},
{
title: '中文名称',
dataIndex: 'chineseName',
maxLength: 50,
fieldProps: {
disabled,
},
},
{
title: '英文名称',
dataIndex: 'englishName',
maxLength: 100,
fieldProps: {
disabled,
},
},
{
title: '品牌缩写或别称',
dataIndex: 'alias',
maxLength: 50,
formItemProps: {
rules: disabled ? [] : [{ required: true, message: '请输入品牌缩写或别称' }],
},
fieldProps: {
disabled,
},
},
{
title: '长方形LOGO上传',
dataIndex: 'horizontalLogo',
formItemProps: {
rules: disabled ? [] : [{ required: true, message: '请上传长方形LOGO上传' }],
extra: (
<div>
<div>尺寸要求:219*72</div>
<div>
素材要求:1、透明底;2、上下左右最少留白2px,具体以保证整体大小一致,视觉平衡为准
</div>
</div>
),
},
fieldProps: {
disabled,
},
renderFormItem: () => <UploadImage limit={1} disabled={disabled} width={219} height={72} />,
},
{
title: 'LOGO上传',
dataIndex: 'logo',
formItemProps: {
rules: disabled ? [] : [{ required: true, message: '请上传LOGO' }],
extra: <span>尺寸要求:192*192</span>,
},
fieldProps: {
disabled,
},
renderFormItem: () => <UploadImage limit={1} disabled={disabled} width={192} height={192} />,
},
];
return baseInfo;
};
...@@ -8,21 +8,19 @@ const { Item } = Form; ...@@ -8,21 +8,19 @@ const { Item } = Form;
const BlacklistModal = ({ visible, onClose, list, enterpriseId, selectedRows }) => { const BlacklistModal = ({ visible, onClose, list, enterpriseId, selectedRows }) => {
const [form] = Form.useForm(); const [form] = Form.useForm();
const handleSave = () => { const handleSave = async () => {
form.validateFields().then(values => { const values = await form.validateFields();
const params = { const params = {
enterpriseId, enterpriseId,
ids: list, ids: list,
isBlack: 1, isBlack: 1,
balanceBackFlag: 1, balanceBackFlag: 1,
}; };
apiStaffBlack(params).then(res => { const res = await apiStaffBlack(params);
if (res.businessCode === '0000') { if (res?.businessCode === '0000') {
message.success('设置成功'); message.success('设置成功');
onClose(true, 'blacklist'); onClose(true, 'blacklist');
} }
});
});
}; };
return ( return (
......
...@@ -46,20 +46,19 @@ const DepartmentModal = ({ visible, onClose, enterpriseList }) => { ...@@ -46,20 +46,19 @@ const DepartmentModal = ({ visible, onClose, enterpriseList }) => {
file: values.file, file: values.file,
}; };
const res = await apiDepartmentExcel(params); const res = await apiDepartmentExcel(params);
if (res.businessCode === '0000') { if (res?.businessCode === '0000') {
message.success('保存成功'); message.success('保存成功');
handleCancel(true); handleCancel(true);
} }
}; };
const handleSave = () => { const handleSave = async () => {
form.validateFields().then(values => { const values = await form.validateFields();
if (importMode) { if (importMode) {
getDepartmentExcel(values); getDepartmentExcel(values);
return; return;
} }
getDepartmentSave(values); getDepartmentSave(values);
});
}; };
return ( return (
......
...@@ -21,22 +21,21 @@ const NewEmployeeModal = props => { ...@@ -21,22 +21,21 @@ const NewEmployeeModal = props => {
setImportMode(false); setImportMode(false);
onClose(val, 'newEmployee'); onClose(val, 'newEmployee');
}; };
const handleSave = () => { const handleSave = async () => {
form.validateFields().then(async values => { const values = await form.validateFields();
if (importMode) { if (importMode) {
const res = await apiStaffExcel(values); const res = await apiStaffExcel(values);
if (res.businessCode === '0000') { if (res?.businessCode === '0000') {
message.success('上传成功'); message.success('上传成功');
handleCancel(true);
}
return;
}
const res = await apiStaffSave(values);
if (res.businessCode === '0000') {
message.success('保存成功');
handleCancel(true); handleCancel(true);
} }
}); return;
}
const res = await apiStaffSave(values);
if (res?.businessCode === '0000') {
message.success('保存成功');
handleCancel(true);
}
}; };
const validatePhone = (_, value) => { const validatePhone = (_, value) => {
...@@ -51,7 +50,7 @@ const NewEmployeeModal = props => { ...@@ -51,7 +50,7 @@ const NewEmployeeModal = props => {
// 部门查询 // 部门查询
const getDepartmentList = async id => { const getDepartmentList = async id => {
const res = await apiDepartmentList({ data: { enterpriseId: id }, page: 1, size: 10000 }); const res = await apiDepartmentList({ data: { enterpriseId: id }, page: 1, size: 10000 });
if (res.businessCode === '0000' && res.data?.records?.length) { if (res?.businessCode === '0000' && res.data?.records?.length) {
const list = res.data.records; const list = res.data.records;
const optionData = list.map(item => ({ const optionData = list.map(item => ({
value: item.id, value: item.id,
......
...@@ -53,7 +53,7 @@ const ViewDepartmentModal = ({ visible, onClose, enterpriseList }) => { ...@@ -53,7 +53,7 @@ const ViewDepartmentModal = ({ visible, onClose, enterpriseList }) => {
name: values.name, name: values.name,
}; };
const res = await apiDepartmentUpdate(params); const res = await apiDepartmentUpdate(params);
if (res.businessCode === '0000') { if (res?.businessCode === '0000') {
message.success('修改成功'); message.success('修改成功');
setNameVisible(false); setNameVisible(false);
getDepartmentList({ data: { enterpriseId }, ...pageInfo }); getDepartmentList({ data: { enterpriseId }, ...pageInfo });
......
...@@ -94,7 +94,7 @@ const StoreManagement = () => { ...@@ -94,7 +94,7 @@ const StoreManagement = () => {
// 部门查询 // 部门查询
const getDepartmentList = async id => { const getDepartmentList = async id => {
const res = await apiDepartmentList({ data: { enterpriseId: id }, page: 1, size: 10000 }); const res = await apiDepartmentList({ data: { enterpriseId: id }, page: 1, size: 10000 });
if (res.businessCode === '0000' && res.data?.records?.length) { if (res?.businessCode === '0000' && res.data?.records?.length) {
const list = res.data.records; const list = res.data.records;
const optionData = list.map(item => ({ const optionData = list.map(item => ({
value: item.id, value: item.id,
...@@ -140,7 +140,7 @@ const StoreManagement = () => { ...@@ -140,7 +140,7 @@ const StoreManagement = () => {
// 企业查询 // 企业查询
const getEnterpriseList = async () => { const getEnterpriseList = async () => {
const res = await apiEnterpriseList(); const res = await apiEnterpriseList();
if (res.businessCode === '0000' && res.data?.records?.length) { if (res?.businessCode === '0000' && res.data?.records?.length) {
const list = res.data.records; const list = res.data.records;
const firstOption = list[0].id; const firstOption = list[0].id;
const optionData = list.map(item => ({ const optionData = list.map(item => ({
...@@ -169,7 +169,7 @@ const StoreManagement = () => { ...@@ -169,7 +169,7 @@ const StoreManagement = () => {
// 删除 // 删除
const deleteEmployee = async id => { const deleteEmployee = async id => {
const res = await apiStaffDelete({ id, enterpriseId: searchForm.enterpriseId, state: 0 }); const res = await apiStaffDelete({ id, enterpriseId: searchForm.enterpriseId, state: 0 });
if (res.businessCode === '0000') { if (res?.businessCode === '0000') {
message.success('删除成功!'); message.success('删除成功!');
shopList({ ...page, data: searchForm }); shopList({ ...page, data: searchForm });
} }
...@@ -219,7 +219,7 @@ const StoreManagement = () => { ...@@ -219,7 +219,7 @@ const StoreManagement = () => {
const goDetails = async ({ staffNo }) => { const goDetails = async ({ staffNo }) => {
setRechargeDetailsVisible(true); setRechargeDetailsVisible(true);
const res = await apiGenerateLogList({ staffNo, enterpriseId: searchForm.enterpriseId }); const res = await apiGenerateLogList({ staffNo, enterpriseId: searchForm.enterpriseId });
if (res.businessCode === '0000') { if (res?.businessCode === '0000') {
const list = res.data; const list = res.data;
setGenerateLog(list); setGenerateLog(list);
} }
...@@ -230,7 +230,7 @@ const StoreManagement = () => { ...@@ -230,7 +230,7 @@ const StoreManagement = () => {
isLimit: checked ? 1 : 0, isLimit: checked ? 1 : 0,
enterpriseId: searchForm.enterpriseId, enterpriseId: searchForm.enterpriseId,
}); });
if (res.businessCode === '0000') { if (res?.businessCode === '0000') {
message.success('设置成功'); message.success('设置成功');
shopList({ ...page, data: searchForm }); shopList({ ...page, data: searchForm });
} }
......
import { Form, Button, Input, Select, notification, Cascader, InputNumber } from 'antd';
import React, { Component } from 'react';
import { SwapRightOutlined } from '@ant-design/icons';
import { connect } from 'dva';
import ImportGoodsModal from '@/components/ImportGoodsModal';
import styles from '../../style.less';
import { apiDownBiddingTemplate, apiUploadGoodsFile } from '../../service';
const FormItem = Form.Item;
const { Option } = Select;
@connect(({ goodsManage, menu }) => ({
goodsManage,
permissions: menu.permissions,
}))
class goodsManage extends Component {
formRef = React.createRef();
state = {
productType: null,
visibleImport: false,
};
componentDidMount() {
this.props.onRef(this);
this.handleSearch();
}
getFieldsValue() {
const form = this.formRef.current;
return form.getFieldsValue();
}
handleSearch = () => {
this.props.handleSearch(1);
};
// 下载模版
onDownload = () => {
const form = this.formRef.current;
const values = form.getFieldsValue();
apiDownBiddingTemplate(values);
};
valueMin = value => {
const { getFieldValue, setFieldsValue } = this.formRef.current;
const minVal = getFieldValue('supplyPriceMin');
if (minVal && minVal > value) {
setFieldsValue({ supplyPriceMax: minVal });
}
};
onReset = () => {
const form = this.formRef.current;
form.resetFields();
this.props.onReset();
this.props.changeProductType(1);
this.setState({
productType: 1,
});
};
addSpu = () => {
this.props.addSpu();
};
setArea = (isAll, type) => {
this.props.setArea(isAll, type);
};
// 验证是否可以修改库存
checkEnableUpdateStock = () => {
if (this.props.selectNum) {
this.props.checkStock();
} else {
notification.info({ message: '请选择' });
}
};
onChangeProductType = (v = null) => {
const form = this.formRef.current;
form.setFieldsValue({
skuId: '',
skuName: '',
thirdSkuNo: '',
productCategoryId: null,
state: null,
supplyPriceMin: null,
supplyPriceMax: null,
productType: v,
});
this.props.changeProductType(v);
this.setState({
productType: v,
});
};
onShowImport = importType => {
this.setState({
visibleImport: true,
importType,
});
};
onHideImport = () => {
this.setState({
visibleImport: false,
});
};
// 上传文件设置
uploadConfig = () => {
const that = this;
return {
name: 'file',
async customRequest(info) {
that.setState({
loading: true,
});
const result = await apiUploadGoodsFile(info.file);
if (result.businessCode === '0000') {
notification.success({ message: '导入成功' });
}
that.setState({
loading: false,
});
},
accept: '.xlsx',
showUploadList: false,
};
};
render() {
const { treeData } = this.props;
const selectW = { width: 250 };
const iptNumWidth = { width: 118 };
return (
<div>
<Form
ref={this.formRef}
name="horizontal_login"
initialValues={{ productType: 1 }}
layout="inline"
className={styles.searchForm}
>
<FormItem label="SKU编码" name="skuId">
<InputNumber placeholder="请输入SKU编码" max={99999999999999999} style={selectW} />
</FormItem>
<FormItem label="商品名称" name="skuName">
<Input placeholder="请输入商品名称" allowClear style={selectW} />
</FormItem>
<FormItem label="商品类型" name="productType">
<Select
style={selectW}
placeholder="请选择商品类型"
onChange={this.onChangeProductType}
>
<Option value={1}>实体商品</Option>
<Option value={4}>服务类商品</Option>
<Option value={5}>外卖商品</Option>
</Select>
</FormItem>
<FormItem label="类目" name="productCategoryId">
<Cascader
placeholder="请选择类目"
style={selectW}
showSearch
changeOnSelect
fieldNames={{ label: 'name', value: 'id', children: 'children' }}
options={treeData}
/>
</FormItem>
{this.state.productType !== 5 && (
<>
<FormItem label="供货价区间">
<FormItem name="supplyPriceMin" className={styles.iptNumRight} noStyle>
<InputNumber placeholder="请输入" min={0} max={999999999} style={iptNumWidth} />
</FormItem>
<span>
<SwapRightOutlined />
</span>
<FormItem name="supplyPriceMax" className={styles.iptNumRight} noStyle>
<InputNumber
style={iptNumWidth}
min={0}
max={999999999}
placeholder="请输入"
onChange={this.valueMin}
/>
</FormItem>
</FormItem>
<FormItem name="thirdSkuNo" label="第三方SKU编码">
<Input placeholder="请输入第三方SKU编码" allowClear style={selectW} />
</FormItem>
</>
)}
<FormItem className={styles.queryBtn}>
<Button onClick={() => this.handleSearch()} type="primary" className={styles.button}>
查询
</Button>
<Button onClick={() => this.onReset()} className={styles.button}>
重置
</Button>
<Button
type="primary"
className={styles.button}
onClick={() => this.onShowImport('binding')}
>
导入商品
</Button>
<Button onClick={() => this.onDownload()} type="link" className={styles.button}>
下载模板
</Button>
</FormItem>
</Form>
{this.state.visibleImport && (
<ImportGoodsModal
visible={this.state.visibleImport}
importType={this.state.importType}
onHide={this.onHideImport}
/>
)}
</div>
);
}
}
export default goodsManage;
import React, { useState, createContext, useEffect } from 'react';
import { Modal, Table, Form } from 'antd';
import { column } from '../staticdata';
import styles from '../style.less';
import { apiProductBiddingUpdate } from '../../service';
/**
* 更新供货价
* * */
const UpdatePriceStock = options => {
const { visible, skuData, productId, spuName } = options;
const [loading, setLoading] = useState(false);
const [specArr, setSpecArr] = useState([]); // 规格列
const [form] = Form.useForm();
const [errorMsg, setErrorMsg] = useState([]); // 错误信息
const onSubmit = async () => {
const value = await form.validateFields();
if (!value?.data?.length) return;
const params = {
productId,
skus: value.data,
};
setLoading(true);
setErrorMsg([]);
const res = await apiProductBiddingUpdate(params);
setLoading(false);
if (res?.success) {
options.refresh();
options.onCancel();
} else {
const msg = res?.msg || '修改失败';
setErrorMsg(msg.split(';'));
}
};
const EditableContext = createContext(null);
useEffect(() => {
if (options.visible) {
let firstSpecTitle = '';
let secondSpecTitle = '';
const arr = [];
skuData.forEach(item => {
if (item.firstSpec && item.firstSpecValue) {
firstSpecTitle = item.firstSpec;
}
if (item.secondSpec && item.secondSpecValue) {
secondSpecTitle = item.secondSpec;
}
});
if (firstSpecTitle) {
arr.push({
title: firstSpecTitle,
width: 135,
align: 'center',
dataIndex: 'firstSpecValue',
});
}
if (secondSpecTitle) {
arr.push({
title: secondSpecTitle,
width: 135,
align: 'center',
dataIndex: 'secondSpecValue',
});
}
setSpecArr(arr);
}
}, [visible]);
return (
<Modal
title="修改供货价"
open={visible}
onCancel={options.onCancel}
onOk={onSubmit}
confirmLoading={loading}
width={900}
className={styles.priceStockTable}
destroyOnClose
>
<div className={styles.modalTitle}>SPU名称:{spuName}</div>
<Form form={form} scrollToFirstError component={false}>
<EditableContext.Provider value={form}>
<Table
className={styles.priceStockTable}
rowKey="id"
columns={column.call(this, specArr)}
dataSource={skuData}
pagination={false}
/>
</EditableContext.Provider>
</Form>
{errorMsg && (
<div className={styles.errorMsg}>
{errorMsg.map(item => (
<div key={item}>{item}</div>
))}
</div>
)}
</Modal>
);
};
export default UpdatePriceStock;
import { Form } from '@ant-design/compatible';
import '@ant-design/compatible/assets/index.css';
import { Card, Pagination, Table, notification, Drawer, Spin } from 'antd';
import React, { Component } from 'react';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import { connect } from 'dva';
import styles from '../style.less';
import { apiCategoryListType, apiProductBiddingInfo } from '../service';
import { JDSHOPID } from '../staticdata';
import { columnManage } from './staticdata';
import SearchForm from './components/SearchForm';
import UpdatePriceStock from './components/UpdatePriceStock';
import Takeaway from '../Takeaway';
import { GOOD_MANAGE } from '@/../config/permission.config';
import LocalStroage from '@/utils/localStorage';
import configApi from '@/../config/env.config';
@connect(({ SupplyPrice, menu }) => ({
SupplyPrice,
permissions: menu.permissions,
}))
class supplyPriceUpdate extends Component {
state = {
pageNo: 1,
loading: false,
categoryTree: [],
pageSize: 20,
previewVisible: false,
createloading: false,
productType: 1, // 商品类型
searchValue: {}, // 搜索条件
refresh: '', // 外卖刷新
visibleUpdatePrice: false, // 修改价格弹窗
skuPriceStockList: [], // 修改价格弹窗数据
spuId: '',
spuName: '',
};
currentLog = null;
supplierId = null;
shopList = [];
canEditable = false;
componentDidMount() {
this.props.SupplyPrice.tableData = {};
this.categoryListByType(this.state.productType);
}
handleSearch = page => {
const searchValue = this.searchForm.getFieldsValue() || {};
this.setState({ searchValue });
if (searchValue.productType !== 5) {
const currentPage = this.state.pageNo;
this.setState(
{
pageNo: page || currentPage,
loading: true,
},
() => {
const { dispatch } = this.props;
const { pageSize, pageNo } = this.state;
dispatch({
type: 'SupplyPrice/getList',
payload: {
pageNo,
pageSize,
...searchValue,
},
}).finally(() => {
this.setState({
loading: false,
});
});
},
);
} else {
this.setState({
refresh: new Date().getTime(),
searchValue,
});
}
};
onPageChange = page => {
this.handleSearch(page);
};
audit = skuId => {
this.setState({
previewVisible: true,
src: `${configApi.prologueDomain}/goods/${skuId}?h=0&token=${LocalStroage.get(
'token',
)}&hideReport=1&time=${Date.now()}`,
});
};
onPageSizeChange = (current, size) => {
this.setState(
{
pageSize: size,
},
() => this.handleSearch(),
);
};
onReset = () => {
this.setState({
pageNo: 1,
pageSize: 20,
});
this.handleSearch();
};
onLoad = error => {
if (!error) {
notification.success({ message: '操作成功' });
this.handleSearch();
}
};
filterShopList = (list = [], isEdit) =>
list.filter(item => isEdit || !JDSHOPID.includes(item.id));
categoryListByType = async type => {
try {
const { data: categoryTree } = await apiCategoryListType(type);
if (!categoryTree) return;
this.setState({ categoryTree });
} catch (e) {
console.log(e);
}
};
changeProductType = e => {
this.setState({
productType: e || 1,
});
this.categoryListByType(e);
if (e !== 5) {
this.handleSearch(1);
}
};
// 显示更新供货价弹窗
serviceVisbleChange = async row => {
try {
const { data } = await apiProductBiddingInfo({
productId: row.spuId,
});
if (!data) return;
this.setState({
spuId: row.spuId,
spuName: data.productName,
skuPriceStockList: data.itemList,
visibleUpdatePrice: true,
});
} catch (e) {
console.log(e);
}
};
render() {
const {
SupplyPrice: { tableData = {} },
permissions,
} = this.props;
const { pageNo, pageSize } = this.state;
this.canEditable = permissions[GOOD_MANAGE.EDITABLE];
return (
<PageHeaderWrapper>
<Spin spinning={this.state.createloading}>
<Card>
<SearchForm
handleSearch={this.handleSearch}
onReset={this.onReset}
onLoad={this.onLoad}
onRef={ref => {
this.searchForm = ref;
}}
treeData={this.state.categoryTree}
shopList={this.shopList}
checkStock={this.checkEnableUpdateStock}
changeProductType={this.changeProductType}
setArea={(isALL, type) => this.setArea(isALL, type)}
/>
</Card>
{this.state.productType === 5 ? (
<Takeaway
handleEdit={this.handleTakeawayEdit}
searchValue={this.state.searchValue}
permissions={permissions}
refresh={this.state.refresh}
/>
) : (
<>
<Spin spinning={this.state.loading}>
<Table
dataSource={tableData?.records}
bordered
columns={columnManage.call(this)}
rowKey={record => record.skuId}
pagination={false}
className={styles.tabletop}
scroll={{ x: '100%', y: 500 }}
/>
</Spin>
<br />
{tableData && (
<Pagination
style={{ marginBottom: 10 }}
onChange={this.onPageChange}
total={tableData.total}
showTotal={total => `共${total}条`}
current={pageNo}
pageSize={pageSize}
showSizeChanger
onShowSizeChange={this.onPageSizeChange}
/>
)}
<Drawer
open={this.state.previewVisible}
width="450"
onClose={() => {
this.setState({ previewVisible: false });
}}
title="商品预览"
bodyStyle={{ height: '90%' }}
>
<iframe
src={this.state.src}
frameBorder="0"
height="100%"
width="375"
title="商品预览"
></iframe>
</Drawer>
</>
)}
</Spin>
{this.state.visibleUpdatePrice && (
<UpdatePriceStock
visible={this.state.visibleUpdatePrice}
onCancel={() => {
this.setState({ visibleUpdatePrice: false });
}}
skuData={this.state.skuPriceStockList}
productId={this.state.spuId}
spuName={this.state.spuName}
refresh={this.handleSearch}
/>
)}
</PageHeaderWrapper>
);
}
}
export default Form.create()(supplyPriceUpdate);
import * as api from '../service';
const Model = {
namespace: 'SupplyPrice',
state: {
tableData: {},
shopList: [],
statusList: [],
cid1List: [],
cid2List: [],
cid3List: [],
treeData: [],
},
effects: {
*getList({ payload }, { call, put }) {
const params = payload;
const productCategoryId = payload?.productCategoryId || [];
params.productCategoryId =
(productCategoryId.length && productCategoryId[productCategoryId.length - 1]) || '';
const res = yield call(api.apiBiddingList, params);
if (res && !res.data) return;
yield put({
type: 'saveData',
payload: {
tableData: res.data,
},
});
},
*categoryList({ payload }, { call, put }) {
const [data] = yield call(api.categoryList, payload.value);
if (!data) return;
yield put({
type: 'saveCategory',
payload: {
[payload.categoryNum]: data,
},
});
},
},
reducers: {
saveData(state, action) {
const data = action.payload;
return { ...state, ...data };
},
dataList(state, action) {
const data = action.payload;
return { ...state, ...data };
},
saveCategory(state, action) {
const data = action.payload;
return { ...state, ...data };
},
},
};
export default Model;
import React from 'react';
import { Input, Form, InputNumber, Button, notification } from 'antd';
import { isIntegerNotMust, isCheckPriceTwoDecimal } from '@/utils/validator';
import styles from './style.less';
export function column(specArr = []) {
return [
{
title: 'SKU编码',
dataIndex: 'id',
width: 200,
align: 'center',
},
...specArr,
{
title: '供货价',
dataIndex: 'supplyPrice',
width: 150,
align: 'center',
render: (_, row, index) => (
<div>
<Form.Item
label=""
key="supplyPrice"
name={['data', index, 'supplyPrice']}
initialValue={row.supplyPrice || 0}
rules={[
{ required: true, message: '请输入供货价!' },
{ validator: isCheckPriceTwoDecimal },
]}
>
<Input allowClear />
</Form.Item>
</div>
),
},
{
title: '市场价',
width: 135,
align: 'center',
dataIndex: 'marketPrice',
render: (_, row, index) => (
<div>
<span>{row.marketPrice}</span>
<div className={styles.hidden}>
<Form.Item label="" key="id" name={['data', index, 'skuId']} initialValue={row.id}>
<input />
</Form.Item>
</div>
</div>
),
},
{
title: '库存',
width: 80,
dataIndex: 'stock',
align: 'center',
render: (_, row, index) => (
<div>
<Form.Item
label=""
key="stock"
name={['data', index, 'stock']}
initialValue={row.stock || 0}
rules={[{ required: true, message: '请输入库存!' }, { validator: isIntegerNotMust }]}
>
<InputNumber min={0} max={500} />
</Form.Item>
</div>
),
},
];
}
export function columnManage() {
return [
{
title: 'SKU编码',
dataIndex: 'skuId',
width: 200,
align: 'center',
},
{
title: 'SKU商品名称',
align: 'center',
dataIndex: 'skuName',
},
{
title: '市场价',
dataIndex: 'marketPrice',
width: 160,
align: 'center',
sorter: (a, b) => a.marketPrice - b.marketPrice,
render: (_, row) => <div>{(row.marketPrice || 0).toFixed(2)}</div>,
},
{
title: '供货价',
dataIndex: 'supplyPrice',
width: 160,
align: 'center',
sorter: (a, b) => a.supplyPrice - b.supplyPrice,
render: (_, row) => (
<div>{(row.supplyPrice && (row.supplyPrice || 0).toFixed(2)) || '-'}</div>
),
},
{
title: '库存',
dataIndex: 'stock',
width: 160,
align: 'center',
render: (_, row) => <div>{row.stock || '-'}</div>,
},
{
title: '操作',
dataIndex: 'action',
width: 150,
align: 'center',
render: (_, row) => (
<div className={styles.actionBtn}>
{this.canEditable && (row.state === 4 || (row.state >= 5 && row.updateState !== 1)) && (
<Button
key="edit"
type="primary"
size="small"
className={styles.button}
onClick={() => {
this.serviceVisbleChange(row);
}}
>
修改
</Button>
)}
<Button
key="viewP"
type="primary"
size="small"
className={styles.button}
onClick={() => this.audit(row.skuId)}
>
预览
</Button>
</div>
),
},
];
}
.priceStockTable {
:global(.ant-modal-body) {
padding: 0 15px !important;
}
:global(.ant-table-cell) {
padding: 5px 0 0 0 !important;
line-height: 32px !important;
vertical-align: top !important;
}
}
.modalTitle {
height: 40px;
line-height: 40px;
}
.hidden {
width: 0;
height: 0;
margin: 0;
padding: 0;
overflow: hidden;
}
.button {
margin: 0 5px;
}
.errorMsg {
padding: 10px 25px;
color: red;
}
...@@ -4,7 +4,6 @@ import { Card, Pagination, Table, notification, Drawer, Spin, Button, Modal } fr ...@@ -4,7 +4,6 @@ import { Card, Pagination, Table, notification, Drawer, Spin, Button, Modal } fr
import React, { Component } from 'react'; import React, { Component } from 'react';
import { PageHeaderWrapper } from '@ant-design/pro-layout'; import { PageHeaderWrapper } from '@ant-design/pro-layout';
import { connect } from 'dva'; import { connect } from 'dva';
import { getToken } from '@/utils/utils';
import styles from './style.less'; import styles from './style.less';
import UpdateStock from './UpdateStock'; import UpdateStock from './UpdateStock';
...@@ -29,6 +28,8 @@ import DraftModal from './DraftModal'; ...@@ -29,6 +28,8 @@ import DraftModal from './DraftModal';
import Takeaway from './Takeaway'; import Takeaway from './Takeaway';
import { GOOD_MANAGE } from '@/../config/permission.config'; import { GOOD_MANAGE } from '@/../config/permission.config';
import LocalStroage from '@/utils/localStorage';
import configApi from '@/../config/env.config';
@connect(({ goodsManage, menu }) => ({ @connect(({ goodsManage, menu }) => ({
goodsManage, goodsManage,
...@@ -124,6 +125,15 @@ class goodsManage extends Component { ...@@ -124,6 +125,15 @@ class goodsManage extends Component {
this.handleSearch(page); this.handleSearch(page);
}; };
audit = skuId => {
this.setState({
previewVisible: true,
src: `${configApi.prologueDomain}/goods/${skuId}?h=0&token=${LocalStroage.get(
'token',
)}&hideReport=1&time=${Date.now()}`,
});
};
onPageSizeChange = (current, size) => { onPageSizeChange = (current, size) => {
this.setState( this.setState(
{ {
......
...@@ -17,12 +17,12 @@ const Model = { ...@@ -17,12 +17,12 @@ const Model = {
const productCategoryId = payload?.productCategoryId || []; const productCategoryId = payload?.productCategoryId || [];
params.productCategoryId = params.productCategoryId =
(productCategoryId.length && productCategoryId[productCategoryId.length - 1]) || ''; (productCategoryId.length && productCategoryId[productCategoryId.length - 1]) || '';
const { data } = yield call(api.searchList, params); const res = yield call(api.searchList, params);
if (!data) return; if (res && !res.data) return;
yield put({ yield put({
type: 'saveData', type: 'saveData',
payload: { payload: {
tableData: data, tableData: res.data,
}, },
}); });
}, },
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
import request from '@/utils/request'; import request from '@/utils/request';
import config from '../../../config/env.config'; import config from '../../../config/env.config';
import { stringify } from 'qs'; import { stringify } from 'qs';
import { saveAs } from 'file-saver';
import { format } from 'date-fns';
import _ from 'lodash'; import _ from 'lodash';
const { goodsApi, kdspApi } = config; const { goodsApi, kdspApi } = config;
...@@ -387,3 +389,71 @@ export async function apiProductStock(data) { ...@@ -387,3 +389,71 @@ export async function apiProductStock(data) {
params: data, params: data,
}); });
} }
/**
* 获取竞价详情
* yApi: http://yapi.quantgroups.com/project/389/interface/api/67169
* * */
export async function apiProductBiddingInfo(params) {
return request.get('/api/merchants/products/bidding/detail', {
prefix: goodsApi,
params,
});
}
/**
* 供应商更新商品供货价库存
* yApi: http://yapi.quantgroups.com/project/389/interface/api/67139
* * */
export async function apiProductBiddingUpdate(params) {
return request.post('/api/merchants/products/bidding/edit', {
prefix: goodsApi,
data: params,
duration: null,
notTip: true,
});
}
/**
* 可竞价sku列表
* yApi: http://yapi.quantgroups.com/project/389/interface/api/67164
* * */
export async function apiBiddingList(params) {
return request.post('/api/merchants/products/bidding/page', {
prefix: goodsApi,
data: stringify(params),
headers,
});
}
/**
* 导入竞价商品信息
* yApi: http://yapi.quantgroups.com/project/389/interface/api/45896
* * */
export async function apiUploadGoodsFile(file) {
const params = new FormData();
params.append('file', file);
params.append('type', 6);
const data = await request.post('/api/merchants/importFile/excel', {
data: params,
prefix: goodsApi,
});
return data;
}
/**
* 下载竞价商品模版
* yApi: http://yapi.quantgroups.com/project/389/interface/api/67269
* * */
export async function apiDownBiddingTemplate(params) {
const productCategoryId = params?.productCategoryId || [];
params.productCategoryId =
(productCategoryId.length && productCategoryId[productCategoryId.length - 1]) || '';
const res = await request.post('/api/merchants/products/bidding-template/export', {
data: stringify(_.omitBy(params, v => v === undefined && v === null && v === '')),
headers,
prefix: goodsApi,
responseType: 'arrayBuffer',
});
const blob = new Blob([res]);
saveAs(blob, `自营商品供货价更新表-${format(new Date(), 'yyyy-MM-dd')}.xlsx`);
}
...@@ -30,9 +30,13 @@ class toExamine extends Component { ...@@ -30,9 +30,13 @@ class toExamine extends Component {
if (err) { if (err) {
return; return;
} }
if (!values?.fileList[0]?.url) {
notification.error({ message: '请上传发票错误,请重新上传' });
return;
}
const params = { const params = {
filePath: values?.fileList[0].url, filePath: values?.fileList[0].url,
fileName: values?.fileList[0].name, fileName: values?.fileList[0]?.name || '',
id, id,
}; };
const data = await uploadBill(params); const data = await uploadBill(params);
...@@ -55,7 +59,7 @@ class toExamine extends Component { ...@@ -55,7 +59,7 @@ class toExamine extends Component {
<Modal <Modal
width={400} width={400}
title={status === 1 ? '上传发票' : '查看发票'} title={status === 1 ? '上传发票' : '查看发票'}
visible={visible} open={visible}
onCancel={() => this.handleCancel()} onCancel={() => this.handleCancel()}
footer={status === 1 ? renderModal.call(this) : detailsModal.call(this, filePath)} footer={status === 1 ? renderModal.call(this) : detailsModal.call(this, filePath)}
className={styles.textArea} className={styles.textArea}
......
...@@ -62,6 +62,10 @@ class PicturesWall extends React.Component { ...@@ -62,6 +62,10 @@ class PicturesWall extends React.Component {
const vm = this; const vm = this;
// eslint-disable-next-line new-cap // eslint-disable-next-line new-cap
const data = `${UUID.createUUID()}.${suffix}`; const data = `${UUID.createUUID()}.${suffix}`;
if (!token) {
message.error('上传失败,请刷新页面重试!');
return;
}
const observable = qiniu.upload(file, data, token); const observable = qiniu.upload(file, data, token);
const observer = { const observer = {
next() { next() {
...@@ -121,7 +125,9 @@ class PicturesWall extends React.Component { ...@@ -121,7 +125,9 @@ class PicturesWall extends React.Component {
customRequest={this.customRequest} customRequest={this.customRequest}
listType="text" listType="text"
fileList={fileList} fileList={fileList}
disabled={status !== 1}
onRemove={status === 1 ? this.clearFileList : ''} onRemove={status === 1 ? this.clearFileList : ''}
accept=".pdf,.doc,.docx,.zip,.rar,.png,.jpeg"
> >
{max && fileList.length >= max ? null : uploadButton} {max && fileList.length >= max ? null : uploadButton}
</Upload> </Upload>
......
import { Upload, message, notification } from 'antd';
import React, { useEffect, useState } from 'react';
import { PlusOutlined } from '@ant-design/icons';
import config from '../../../../config/env.config';
import UUID from '../../../utils/uuid';
import { qiniuToken } from '@/services/qiniu';
const qiniu = require('@/utils/qiniu.min.js');
const { qiniuHost } = config;
const CustomUpload = props => {
const { limit = 1 } = props;
const [fileList, setFileList] = useState([]);
const [token, setToken] = useState(null);
const customRequest = ({ file, onError, onSuccess }) => {
let suffix = '';
if (file.name) {
const index = file.name.lastIndexOf('.');
suffix = file.name.substr(index + 1, file.name.length - 1);
const types = ['pdf', 'doc', 'docx', 'zip', 'rar', 'png', 'jpeg', 'jpg'];
if (!types.includes(suffix)) {
message.error('文件格式错误!');
return;
}
}
if (fileList.length >= limit) {
message.warning(`最多上传${limit}个!`);
return;
}
// eslint-disable-next-line new-cap
const data = `${UUID.createUUID()}.${suffix}`;
if (!token) {
message.error('上传失败,请刷新页面重试!');
return;
}
const observable = qiniu.upload(file, data, token);
const observer = {
next() {
// ...
},
error(t) {
if (t.code === 614) {
notification.error({
message: '文件名重复,请更换文件名。',
});
}
onError(file);
// ...
},
complete(res) {
const comFile = file;
const url = `${qiniuHost}/${res.key}`;
comFile.url = url;
const list = fileList;
list.push({
url: file.url,
name: file.name,
uid: list.length,
status: 'done',
});
setFileList([...list]);
props.onChange(list);
onSuccess(comFile);
},
};
observable.subscribe(observer); // 上传开始
};
const onRemove = item => {
const arr = (fileList || []).filter(file => file.uid !== item.uid);
setTimeout(() => {
props.onChange(arr);
}, 100);
};
const getToken = async () => {
setToken(await qiniuToken());
};
useEffect(() => {
if (props.value instanceof Array) {
console.log('initFileList(props.value) :>> ', JSON.stringify(initFileList(props.value)));
setFileList(initFileList(props.value));
}
}, [props.value]);
useEffect(() => {
getToken();
}, []);
return (
<Upload
onChange={props.onChange}
customRequest={customRequest}
listType="picture-card"
fileList={fileList}
onRemove={onRemove}
accept=".pdf,.doc,.docx,.zip,.rar,.png,.jpeg,.jpg"
>
<div>
<PlusOutlined />
<div style={{ marginTop: 8 }}>上传文件</div>
</div>
</Upload>
);
};
export default CustomUpload;
function initFileList(fileList) {
const fileLists = (fileList || []).map((item, index) => ({
url: item.url,
name: item.name ? item.name : '发票',
uid: index,
status: 'done',
}));
return fileLists;
}
import React, { useRef, useEffect } from 'react';
import { Modal, Form, Upload, Input } from 'antd';
import { apiMerchantView } from '../service';
import { getFileData } from '@/utils/bll';
const formItemLayout = {
labelCol: { span: 4 },
wrapperCol: { span: 19 },
};
/**
* 查看发票
* */
const InvoiceListModal = props => {
const refForm = useRef();
const { id } = props;
const getDetail = async () => {
const res = await apiMerchantView(id);
if (res && res.data) {
const { invoiceFiles = [], refuseReason } = res.data;
// 发票列表
const invoiceList = [];
// 补录发票列表
const subInvoiceList = [];
invoiceFiles.forEach(item => {
if (item.supplementary) {
subInvoiceList.push(item);
} else {
invoiceList.push(item);
}
});
const params = {
fileList: getFileData(invoiceList, 'filePath', 'fileName'),
other: refuseReason,
sFileList: getFileData(subInvoiceList, 'filePath', 'fileName'),
sOther: '',
};
refForm.current?.setFieldsValue?.(params);
}
};
useEffect(() => {
getDetail();
}, []);
return (
<Modal width={800} title="查看发票" open footer={null} onCancel={() => props.onCancel(false)}>
<Form ref={refForm} {...formItemLayout}>
<Form.Item label="发票" name="fileList" valuePropName="fileList">
<Upload disabled listType="picture-card" />
</Form.Item>
<Form.Item label="发票备注" name="other">
<Input disabled />
</Form.Item>
<Form.Item label="补充发票" name="sFileList" valuePropName="fileList">
<Upload disabled listType="picture-card" />
</Form.Item>
<Form.Item label="补充发票备注" name="sOther">
<Input disabled />
</Form.Item>
</Form>
</Modal>
);
};
export default InvoiceListModal;
import React, { useEffect } from 'react';
import { layout } from '@/utils/bll';
import { BetaSchemaForm } from '@ant-design/pro-components';
import { paymentInfoColumn } from '../data';
const PaymentBill = props => {
const refForm = React.useRef();
const closeModal = v => {
!v && props.onClose(false);
};
const onSubmit = async v => {
// 关闭
closeModal();
};
const getInfo = async () => {
const info = props.info || {};
let images = []; // info.paySuccessImage;
if (info.paySuccessImages?.length) {
images = info.paySuccessImages.map((item, i) => ({
url: item,
uid: i,
status: 'done',
name: '',
}));
}
const params = {
images,
comment: info.payComment || '',
};
refForm.current?.setFieldsValue?.(params);
const res = refForm.current?.getFieldsValue();
console.log(res);
};
useEffect(() => {
if (props.visible) {
getInfo();
}
}, [props.visible]);
return (
props.visible && (
<BetaSchemaForm
layoutType="ModalForm"
title="查看付款单"
open={props.visible}
width="600px"
modalProps={{
maskClosable: false,
destroyOnClose: true,
okText: () => <></>,
cancelText: '关闭',
}}
formRef={refForm}
onOpenChange={closeModal}
layout="horizontal"
{...layout}
onFinish={onSubmit}
columns={paymentInfoColumn(props.status)}
/>
)
);
};
export default PaymentBill;
import React, { useState, useRef } from 'react';
import { Modal, notification, Form } from 'antd';
import Upload from './CustomUpload';
import { formItemLayout } from '../data';
import { apiUploadSupplementaryBill } from '../service';
/**
* 上传发票弹窗
* */
const UploadInvoiceModal = props => {
const { id } = props;
const refForm = useRef();
const onConfirm = async () => {
const res = await refForm.current?.validateFields?.();
if (res?.fileList?.length) {
const { fileList } = res;
const params = {
filePath: fileList[0].url,
fileName: fileList[0]?.name || '',
id,
};
const data = await apiUploadSupplementaryBill(params);
if (data.businessCode === '0000') {
notification.success({ message: '上传成功' });
props.onCancel(true);
}
}
};
return (
<Modal width={500} title="补充发票" open onOk={onConfirm} onCancel={props.onCancel}>
<Form ref={refForm} {...formItemLayout}>
<Form.Item
label="上传发票"
name="fileList"
rules={[{ required: true }]}
extra="支持格式:pdf | doc | docx | png | jpeg | jpg | zip | rar"
>
<Upload limit={1} />
</Form.Item>
</Form>
</Modal>
);
};
export default UploadInvoiceModal;
import React from 'react'; import React from 'react';
import { Button } from 'antd'; import { Button, Upload } from 'antd';
import { UploadOutlined } from '@ant-design/icons';
import styles from './style.less'; import styles from './style.less';
export const payStateEnum = { export const payStateEnum = {
...@@ -8,6 +9,7 @@ export const payStateEnum = { ...@@ -8,6 +9,7 @@ export const payStateEnum = {
3: { text: '已通过' }, 3: { text: '已通过' },
4: { text: '待打款' }, 4: { text: '待打款' },
5: { text: '已打款' }, 5: { text: '已打款' },
6: { text: '打款中' },
}; };
export const orderStateNum = { export const orderStateNum = {
...@@ -17,6 +19,13 @@ export const orderStateNum = { ...@@ -17,6 +19,13 @@ export const orderStateNum = {
4: { text: '已通过' }, 4: { text: '已通过' },
}; };
export const dateStateEnum = {
1: { text: '一周' },
2: { text: '半个月' },
3: { text: '一个月' },
4: { text: '日结' },
};
export const redInvoiceState = { export const redInvoiceState = {
1: { text: '', actionValue: 2 }, 1: { text: '', actionValue: 2 },
2: { text: '', actionValue: 1 }, 2: { text: '', actionValue: 1 },
...@@ -130,6 +139,41 @@ export function columns(res, pages) { ...@@ -130,6 +139,41 @@ export function columns(res, pages) {
</div> </div>
), ),
}, },
{
title: () => (
<div style={{ textAlign: 'center' }}>
<div>当前结算周期</div>
<div style={{ color: '#f00' }}>(供应商维度)</div>
</div>
),
dataIndex: 'billPeriod',
key: 'billPeriod',
width: '150px',
hideInSearch: true,
valueEnum: dateStateEnum,
align: 'center',
},
{
title: () => (
<div style={{ textAlign: 'center' }}>
<div>拖欠金额</div>
<div style={{ color: '#f00' }}>(供应商维度)</div>
</div>
),
dataIndex: 'arrearsAmount',
key: 'arrearsAmount',
width: '130px',
hideInSearch: true,
align: 'center',
},
{
title: '实付金额',
dataIndex: 'paidAmount',
key: 'paidAmount',
width: '100px',
hideInSearch: true,
align: 'center',
},
{ {
title: '是否缺少红字发票', title: '是否缺少红字发票',
dataIndex: 'redInvoiceState', dataIndex: 'redInvoiceState',
...@@ -156,6 +200,7 @@ export function columns(res, pages) { ...@@ -156,6 +200,7 @@ export function columns(res, pages) {
dataIndex: 'option', dataIndex: 'option',
// valueType: 'option', // valueType: 'option',
key: 'option', key: 'option',
width: '220px',
align: 'center', align: 'center',
fixed: 'right', fixed: 'right',
render: (_, row) => ( render: (_, row) => (
...@@ -170,19 +215,25 @@ export function columns(res, pages) { ...@@ -170,19 +215,25 @@ export function columns(res, pages) {
> >
申请结算 申请结算
</Button> </Button>
<Button {row.billPeriod === 4 && row.blueInvoiceState === 4 ? (
type="primary" <Button type="primary" className={styles.button} onClick={() => res.edit(11, row)}>
disabled={ 补充发票
!( </Button>
(row.blueInvoiceState === 1 || row.blueInvoiceState === 3) && ) : (
row.payState === 3 <Button
) type="primary"
} disabled={
className={styles.button} !(
onClick={() => res.edit(1, row)} (row.blueInvoiceState === 1 || row.blueInvoiceState === 3) &&
> row.payState === 3
上传发票 )
</Button> }
className={styles.button}
onClick={() => res.edit(1, row)}
>
上传发票
</Button>
)}
</> </>
) : ( ) : (
'' ''
...@@ -198,6 +249,14 @@ export function columns(res, pages) { ...@@ -198,6 +249,14 @@ export function columns(res, pages) {
<Button type="primary" className={styles.button} onClick={() => res.edit(3, row)}> <Button type="primary" className={styles.button} onClick={() => res.edit(3, row)}>
下载账单 下载账单
</Button> </Button>
<Button
type="primary"
className={styles.btnMax}
disabled={row.payState !== 5}
onClick={() => res.onPaymentOrder(1, row)}
>
查看付款凭证
</Button>
</div> </div>
), ),
}, },
...@@ -205,3 +264,90 @@ export function columns(res, pages) { ...@@ -205,3 +264,90 @@ export function columns(res, pages) {
} }
export const toolBarRender = onExport => () => []; export const toolBarRender = onExport => () => [];
// 上传文件
export function uploadPropsFn({
keyName,
limit = 1,
maxSize = 1024 * 1024 * 5,
maxSizeMsg = '文件大小不能超过5M!',
form,
disabled,
accept = '.png,.bmp,.gif,.jpeg,.jpg',
}) {
const config = {
maxSize,
disabled,
multiple: limit > 1,
listType: 'picture-card',
showUploadList: true,
accept,
onRemove: e => {
const fileValue = form.getFieldValue(keyName) || [];
if (fileValue.fileList) {
const files = fileValue.fileList.filter(item => item.uid !== e.uid);
form.setFieldValue(keyName, files);
} else {
const files = fileValue.filter(item => item.uid !== e.uid);
form.setFieldValue(keyName, files);
}
},
};
return config;
}
/**
* 付款单弹窗
* status:1查看 0上传
* * */
export const paymentInfoColumn = status => [
{
title: '付款单',
dataIndex: 'images',
key: 'images',
formItemProps: {
rules: [{ required: true, message: '请上传付款单' }],
},
fieldProps: {
disabled: status === 1,
limit: 1,
accept: '.png,.bmp,.gif,.jpeg,.jpg,.pdf',
},
renderFormItem: (s, c, form) => {
let fileList = form.getFieldValue('images') || [];
if (fileList.fileList) {
fileList = fileList.fileList || [];
}
return (
<Upload
{...uploadPropsFn.call(this, {
keyName: 'images',
form,
accept: '.png,.bmp,.gif,.jpeg,.jpg,.pdf',
})}
fileList={fileList}
disabled={status === 1}
>
<UploadOutlined /> 上传文件
</Upload>
);
},
},
{
title: '备注',
dataIndex: 'comment',
key: 'comment',
align: 'center',
valueType: 'textarea',
fieldProps: {
disabled: status === 1,
maxLength: 200,
autoSize: { minRows: 2, maxRows: 6 },
},
},
];
export const formItemLayout = {
labelCol: { span: 6 },
wrapperCol: { span: 18 },
};
...@@ -8,6 +8,9 @@ import { saveAs } from 'file-saver'; ...@@ -8,6 +8,9 @@ import { saveAs } from 'file-saver';
import { format } from 'date-fns'; import { format } from 'date-fns';
import moment from 'moment'; import moment from 'moment';
import { connect } from 'dva'; import { connect } from 'dva';
import PaymentBillModal from './components/PaymentBillModal';
import UploadInvoiceModal from './components/UploadInvoiceModal';
import InvoiceListModal from './components/InvoiceListModal';
import { PAYMENT_MANAGE } from '@/../config/permission.config'; import { PAYMENT_MANAGE } from '@/../config/permission.config';
import ToExamineModal from './ToExamine'; import ToExamineModal from './ToExamine';
import { columns, toolBarRender } from './data'; import { columns, toolBarRender } from './data';
...@@ -16,7 +19,7 @@ import { ...@@ -16,7 +19,7 @@ import {
selfPaymentExport, selfPaymentExport,
selfPaymentexportDetail, selfPaymentexportDetail,
settlement, settlement,
merchantView, apiMerchantView,
} from './service'; } from './service';
const { confirm } = Modal; const { confirm } = Modal;
...@@ -31,6 +34,11 @@ const PaymentMange = props => { ...@@ -31,6 +34,11 @@ const PaymentMange = props => {
const [status, setstatus] = useState(1); const [status, setstatus] = useState(1);
const [pages, setpages] = useState({}); const [pages, setpages] = useState({});
const [toExamineModalVisibel, setToExamineModalVisibel] = useState(false); const [toExamineModalVisibel, setToExamineModalVisibel] = useState(false);
const [visiblePaymentBill, setVisiblePaymentBill] = useState(false);
const [paymentStatus, setPaymentStatus] = useState(0);
const [supplierInfo, setSupplierInfo] = useState({});
const [visibleUploadInvoice, setVisibleUploadInvoice] = useState(false);
const [visibleInvoiceList, setVisibleInvoiceList] = useState(false);
const reload = () => { const reload = () => {
if (actionRef.current) { if (actionRef.current) {
...@@ -71,13 +79,14 @@ const PaymentMange = props => { ...@@ -71,13 +79,14 @@ const PaymentMange = props => {
}; };
// 查看发票 // 查看发票
const invoiceDetail = async params => { const invoiceDetail = async params => {
const data = await merchantView(params); const data = await apiMerchantView(params);
if (data.businessCode === '0000') { if (data.businessCode === '0000') {
settoExamineData({ ...data.data }); settoExamineData({ ...data.data });
setToExamineModalVisibel(true); // setToExamineModalVisibel(true);
setVisibleInvoiceList(true);
} }
}; };
// 0申请结算 1上传发票 2查看发票 3下载账单付款单明细 // 0申请结算 1上传发票 11补充发票 2查看发票 3下载账单付款单明细
const edit = async (status, { id }) => { const edit = async (status, { id }) => {
if (status === 0) { if (status === 0) {
showConfirm(id); showConfirm(id);
...@@ -86,10 +95,14 @@ const PaymentMange = props => { ...@@ -86,10 +95,14 @@ const PaymentMange = props => {
settoExamineData({ id }); settoExamineData({ id });
setToExamineModalVisibel(true); setToExamineModalVisibel(true);
} else if (status === 2) { } else if (status === 2) {
setstatus(status); settoExamineData({ id });
invoiceDetail(id); setVisibleInvoiceList(true);
} else if (status === 3) { } else if (status === 3) {
exportDetail(id); exportDetail(id);
} else if (status === 11) {
console.log('id :>> ', id);
settoExamineData({ id });
setVisibleUploadInvoice(true);
} }
}; };
...@@ -99,6 +112,13 @@ const PaymentMange = props => { ...@@ -99,6 +112,13 @@ const PaymentMange = props => {
} }
setToExamineModalVisibel(false); setToExamineModalVisibel(false);
}; };
const onCloseUploadInvoice = v => {
setVisibleUploadInvoice(false);
if (v === true) {
reload();
}
};
const checkedTime = ({ dateTimeRange }) => { const checkedTime = ({ dateTimeRange }) => {
const startTimeStr = moment(dateTimeRange?.[0]).format('YYYY-MM-DD'); const startTimeStr = moment(dateTimeRange?.[0]).format('YYYY-MM-DD');
const endTimeStr = moment(dateTimeRange?.[1]).format('YYYY-MM-DD'); const endTimeStr = moment(dateTimeRange?.[1]).format('YYYY-MM-DD');
...@@ -124,11 +144,18 @@ const PaymentMange = props => { ...@@ -124,11 +144,18 @@ const PaymentMange = props => {
} }
setloading(false); setloading(false);
}; };
// 上传付款单和查看付款单
const onPaymentOrder = (status, row) => {
setPaymentStatus(status);
setSupplierInfo(row);
setVisiblePaymentBill(!0);
};
const onToolBarRender = toolBarRender(); const onToolBarRender = toolBarRender();
const res = { const res = {
edit, edit,
onPaymentOrder,
canEditable, canEditable,
}; };
...@@ -199,7 +226,23 @@ const PaymentMange = props => { ...@@ -199,7 +226,23 @@ const PaymentMange = props => {
onColse(); onColse();
}} }}
/> />
{visiblePaymentBill && (
<PaymentBillModal
visible={visiblePaymentBill}
status={paymentStatus}
info={supplierInfo}
onRefresh={() => reload()}
onClose={() => setVisiblePaymentBill(false)}
/>
)}
</PageHeaderWrapper> </PageHeaderWrapper>
{visibleUploadInvoice && (
<UploadInvoiceModal onCancel={onCloseUploadInvoice} id={toExamineData.id} />
)}
{visibleInvoiceList && (
<InvoiceListModal onCancel={setVisibleInvoiceList} id={toExamineData.id} />
)}
</Spin> </Spin>
); );
}; };
......
...@@ -90,9 +90,23 @@ export async function uploadBill(params) { ...@@ -90,9 +90,23 @@ export async function uploadBill(params) {
}); });
} }
// 查看发票 /**
export async function merchantView(params) { * 补录发票
return request.get(`/selfPaymentBill/merchant/view/${params}`, { * yApi: http://yapi.quantgroups.com/project/549/interface/api/69004
* */
export async function apiUploadSupplementaryBill(params) {
return request.post('/selfPaymentBill/merchant/supplementary/upload/bill', {
prefix: querysApi,
data: params,
});
}
/**
* 查看发票
* yApi: http://yapi.quantgroups.com/project/549/interface/api/35872
* */
export async function apiMerchantView(id) {
return request.get(`/selfPaymentBill/merchant/view/${id}`, {
prefix: querysApi, prefix: querysApi,
}); });
} }
.button { .button {
display: block; width: 88px;
margin: 0 auto 10px; margin: 0 auto 10px;
padding: 0;
text-align: center;
}
.button:nth-child(even) {
margin-left: 10px;
}
.btnMax {
width: auto;
padding: 4px 15px;
} }
.uploadButton { .uploadButton {
......
...@@ -92,7 +92,7 @@ ...@@ -92,7 +92,7 @@
} }
} }
.pullImage { .pullImage {
position: absolute; position: absolute !important;
top: 30px; top: 30px;
right: 40px; right: 40px;
} }
......
...@@ -147,7 +147,7 @@ const FormAttr = forwardRef((props, ref) => { ...@@ -147,7 +147,7 @@ const FormAttr = forwardRef((props, ref) => {
if (typeof values[item] === 'string') { if (typeof values[item] === 'string') {
obj.productAttributeApplyValueList = JSON.parse(values[item]); obj.productAttributeApplyValueList = JSON.parse(values[item]);
} else { } else {
obj.productAttributeApplyValueList = values[item].map(v => JSON.parse(v)); obj.productAttributeApplyValueList = values[item]?.map(v => JSON.parse(v)) || [];
} }
return obj; return obj;
}); });
......
...@@ -22,7 +22,7 @@ import { formItemLayout } from '../config'; ...@@ -22,7 +22,7 @@ import { formItemLayout } from '../config';
import { ServiceContext } from '../context'; import { ServiceContext } from '../context';
import { debounce } from '@/utils/utils'; import { debounce } from '@/utils/utils';
import UploadCropImage from './UploadCropImage'; import UploadCropImage from './UploadCropImage';
import { apiShopIds, apiQueryShopList, getByProductType } from '../service'; import { apiShopIds, apiQueryShopList, apiServiceByCategory } from '../service';
import GroupInfo from '../../GoodsManage/Takeaway/components/GroupInfo'; import GroupInfo from '../../GoodsManage/Takeaway/components/GroupInfo';
const CreateSelectOption = optionList => const CreateSelectOption = optionList =>
...@@ -58,6 +58,8 @@ const FormInformationBasic = forwardRef((props, ref) => { ...@@ -58,6 +58,8 @@ const FormInformationBasic = forwardRef((props, ref) => {
const [isEditTakeaway, setIsEditTakeaway] = useState(false); const [isEditTakeaway, setIsEditTakeaway] = useState(false);
const [isModalOpen, setIsModalOpen] = useState(false); const [isModalOpen, setIsModalOpen] = useState(false);
const [shopIdSource, setShopIdSource] = useState(''); const [shopIdSource, setShopIdSource] = useState('');
const [serviceList, setServiceList] = useState({}); // 品类下的服务承诺
const [specServiceList, setSpecServiceList] = useState([]); // 服务承诺列表
const onCheck = async () => { const onCheck = async () => {
try { try {
...@@ -82,6 +84,25 @@ const FormInformationBasic = forwardRef((props, ref) => { ...@@ -82,6 +84,25 @@ const FormInformationBasic = forwardRef((props, ref) => {
} }
}; };
// 获取品类下的服务承诺
const getServiceList = async ids => {
const id = ids[ids.length - 1];
const res = await apiServiceByCategory(id);
if (res?.data?.length > 0) {
const obj = {};
res.data.forEach(item => {
obj[item.productServiceId] = item.required;
});
setServiceList(obj);
}
};
const onCategoryChange = ids => {
props.onCategoryChange(ids);
getServiceList(ids);
};
// 自定义加入菜单 // 自定义加入菜单
const showModal = () => { const showModal = () => {
if (shopIdSource || editData?.productRefShopId) { if (shopIdSource || editData?.productRefShopId) {
...@@ -185,6 +206,7 @@ const FormInformationBasic = forwardRef((props, ref) => { ...@@ -185,6 +206,7 @@ const FormInformationBasic = forwardRef((props, ref) => {
if (editData?.commonImageList?.length) { if (editData?.commonImageList?.length) {
onTakeawayImageList(editData?.commonImageList); onTakeawayImageList(editData?.commonImageList);
} }
getServiceList(editData.categoryId);
}, [customer.isEdit, editData]); }, [customer.isEdit, editData]);
useEffect(() => { useEffect(() => {
setShopList([]); setShopList([]);
...@@ -200,6 +222,25 @@ const FormInformationBasic = forwardRef((props, ref) => { ...@@ -200,6 +222,25 @@ const FormInformationBasic = forwardRef((props, ref) => {
}; };
form.setFieldsValue(temp); form.setFieldsValue(temp);
}, [groupShopData]); }, [groupShopData]);
useEffect(() => {
// 品类下的设置的服务承诺不可选 required true默认选中 false默认不选中
if (specListData.length > 0) {
const defaultChecked = new Set(editData?.[specListData[0].specId] || []);
const arr = specListData[0].specValues.map(item => {
if (serviceList[item.value]) {
defaultChecked.add(item.value);
}
return {
...item,
disabled: typeof serviceList[item.value] === 'boolean',
};
});
setSpecServiceList(arr);
form.setFieldsValue({ [specListData[0].specId]: Array.from(defaultChecked) });
}
}, [serviceList, specListData]);
return ( return (
<Form <Form
{...formItemLayout} {...formItemLayout}
...@@ -275,7 +316,7 @@ const FormInformationBasic = forwardRef((props, ref) => { ...@@ -275,7 +316,7 @@ const FormInformationBasic = forwardRef((props, ref) => {
disabled={customer.isEdit && customer.isNormal} disabled={customer.isEdit && customer.isNormal}
showSearch={{ filter: filterCategoryOptions }} showSearch={{ filter: filterCategoryOptions }}
fieldNames={{ label: 'name', value: 'id', children: 'children' }} fieldNames={{ label: 'name', value: 'id', children: 'children' }}
onChange={props.onCategoryChange} onChange={onCategoryChange}
options={newCategoryList[customer.productType]} options={newCategoryList[customer.productType]}
/> />
</Form.Item> </Form.Item>
...@@ -308,7 +349,6 @@ const FormInformationBasic = forwardRef((props, ref) => { ...@@ -308,7 +349,6 @@ const FormInformationBasic = forwardRef((props, ref) => {
name="brandId" name="brandId"
label="商品品牌" label="商品品牌"
rules={[{ required: true, message: '请选择商品品牌' }]} rules={[{ required: true, message: '请选择商品品牌' }]}
extra="若需新增品牌请联系业务员"
> >
<Select <Select
showSearch showSearch
...@@ -352,7 +392,7 @@ const FormInformationBasic = forwardRef((props, ref) => { ...@@ -352,7 +392,7 @@ const FormInformationBasic = forwardRef((props, ref) => {
!customer.isCard && !customer.isCard &&
specListData.map((item, index) => ( specListData.map((item, index) => (
<Form.Item name={item.specId} key={item.specId} label={item.specName}> <Form.Item name={item.specId} key={item.specId} label={item.specName}>
<Checkbox.Group options={item.specValues} /> <Checkbox.Group options={specServiceList} />
</Form.Item> </Form.Item>
))} ))}
......
...@@ -989,7 +989,7 @@ const TakeawayGoodsInfo = forwardRef((props, ref) => { ...@@ -989,7 +989,7 @@ const TakeawayGoodsInfo = forwardRef((props, ref) => {
singularSpu[0]?.specs[0]?.unit && singularSpu[0]?.specs[0]?.unit &&
singularSpu.map((item, index) => ( singularSpu.map((item, index) => (
<div className={styles.specsSingularBetween}> <div className={styles.specsSingularBetween}>
<Form.Item label={calcLabelName(item, 'singular')}> <Form.Item label={calcLabelName(item)}>
<div className={styles.specsSingularBetween}> <div className={styles.specsSingularBetween}>
<span className={styles.repertoryLimit}> <span className={styles.repertoryLimit}>
{item?.specs[0]?.productStock}/{item?.specs[0]?.maxStock} {item?.specs[0]?.productStock}/{item?.specs[0]?.maxStock}
......
...@@ -355,7 +355,7 @@ const ServiceGoods = options => { ...@@ -355,7 +355,7 @@ const ServiceGoods = options => {
setIsUseCache(true); setIsUseCache(true);
setVisibleCacheEdit(false); setVisibleCacheEdit(false);
onSetData(SourceData); onSetData(SourceData);
onValuesChange(SourceData, !0); onValuesChange(SourceData);
// 外卖类型--- // 外卖类型---
if (SourceData && SourceData?.type === 5) { if (SourceData && SourceData?.type === 5) {
setTakeawayEditData(SourceData); setTakeawayEditData(SourceData);
......
...@@ -35,6 +35,10 @@ export const merchantBrandList = () => ...@@ -35,6 +35,10 @@ export const merchantBrandList = () =>
request.post('/product/brand/api/merchant/list', { request.post('/product/brand/api/merchant/list', {
prefix: goodsApi, prefix: goodsApi,
}); });
// export const merchantBrandList = () =>
// request.post('/api/merchants/brands/getAll', {
// prefix: goodsApi,
// });
// 获取规格列表 // 获取规格列表
export const merchantSpecList = () => export const merchantSpecList = () =>
...@@ -172,3 +176,15 @@ export const apiGetShopDetail = data => ...@@ -172,3 +176,15 @@ export const apiGetShopDetail = data =>
'Content-Type': 'application/x-www-form-urlencoded', 'Content-Type': 'application/x-www-form-urlencoded',
}, },
}); });
/**
* 获取类目关联服务
* yApi地址: http://yapi.quantgroups.com/project/389/interface/api/69189
* */
export const apiServiceByCategory = (categoryId, refIdType = 1) =>
request.get(
`/api/merchants/products/service/config/list?refId=${categoryId}&refIdType=${refIdType}`,
{
prefix: goodsApi,
},
);
...@@ -4,7 +4,8 @@ import { Button } from 'antd'; ...@@ -4,7 +4,8 @@ import { Button } from 'antd';
export const dateStateEnum = { export const dateStateEnum = {
1: { text: '一周', maxlength: 8 }, 1: { text: '一周', maxlength: 8 },
2: { text: '半个月', maxlength: 4 }, 2: { text: '半个月', maxlength: 4 },
3: { text: ' 一个月', maxlength: 2 }, 3: { text: '一个月', maxlength: 2 },
4: { text: '日结', maxlength: 10 },
}; };
export const orderStateNum = { export const orderStateNum = {
......
...@@ -66,7 +66,7 @@ const AddModal = props => { ...@@ -66,7 +66,7 @@ const AddModal = props => {
} }
/> />
</Form.Item> </Form.Item>
<Form.Item label="餐类型"> <Form.Item label="餐类型">
<span>到店</span> <span>到店</span>
</Form.Item> </Form.Item>
</Form> </Form>
......
...@@ -45,7 +45,7 @@ export const columns = props => [ ...@@ -45,7 +45,7 @@ export const columns = props => [
align: 'center', align: 'center',
}, },
{ {
title: '类型', title: '类型',
key: 'mealType', key: 'mealType',
dataIndex: 'mealType', dataIndex: 'mealType',
align: 'center', align: 'center',
......
...@@ -42,7 +42,7 @@ const EditRepastModal = props => { ...@@ -42,7 +42,7 @@ const EditRepastModal = props => {
return ( return (
<> <>
<Modal <Modal
title="餐类型" title="餐类型"
onOk={onOk} onOk={onOk}
visible={editVisible} visible={editVisible}
onCancel={() => handleCancel(false)} onCancel={() => handleCancel(false)}
......
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { Checkbox, Space, message, Modal, notification } from 'antd'; import { Checkbox, Space, message, Modal, notification, Button } from 'antd';
import { weekOptions } from '../staticData/goods'; import { weekOptions } from '../staticData/goods';
import { apiMealInfoUpdate } from '../service'; import { apiMealInfoUpdate, apiCheckInfo } from '../service';
import style from '../style/index.less';
const SaleDateModal = props => { const SaleDateModal = props => {
const [value, setValue] = useState([]); const [value, setValue] = useState([]);
const [loading, setLoading] = useState(false);
const [checkAll, setCheckAll] = useState(false);
// 关闭弹窗 // 关闭弹窗
const handleCancel = () => { const handleCancel = () => {
...@@ -13,6 +16,7 @@ const SaleDateModal = props => { ...@@ -13,6 +16,7 @@ const SaleDateModal = props => {
const onChangeWeek = e => { const onChangeWeek = e => {
setValue(e); setValue(e);
setCheckAll(e?.length === 7);
}; };
// 提交 // 提交
...@@ -21,12 +25,35 @@ const SaleDateModal = props => { ...@@ -21,12 +25,35 @@ const SaleDateModal = props => {
message.error('请选择可售日期'); message.error('请选择可售日期');
return; return;
} }
if (props.productType === 4) {
const data = [];
props.dataSource.forEach(item => {
const obj = {
saleDateList: item.saleDate,
tabCateList: item.tabCate?.map(v => ({ tabId: v })),
};
if (props.item.skuId === item.skuId && item.tabCate) {
obj.saleDateList = value;
data.push(obj);
} else if (item.saleDate?.length && item.tabCate?.length) {
data.push(obj);
}
});
setLoading(true);
const res = await apiCheckInfo(data);
setLoading(false);
if (!res || !res.success) {
return;
}
}
if (props.id) { if (props.id) {
const params = { const params = {
id: props.id, id: props.id,
saleDateList: value, saleDateList: value,
}; };
setLoading(true);
await apiMealInfoUpdate(params); await apiMealInfoUpdate(params);
setLoading(false);
notification.success({ message: '保存成功' }); notification.success({ message: '保存成功' });
} }
handleCancel(); handleCancel();
...@@ -51,20 +78,45 @@ const SaleDateModal = props => { ...@@ -51,20 +78,45 @@ const SaleDateModal = props => {
return `${item}`; return `${item}`;
}); });
} }
setCheckAll(v.length === 7);
setValue(v); setValue(v);
} }
}, [props.visible]); }, [props.visible]);
// 全选事件
const onCheckAll = e => {
if (e.target.checked) {
setValue(Object.keys(weekOptions).map(w => `${w}`));
} else {
setValue([]);
}
setCheckAll(e.target.checked);
};
// 弹窗底部
const footerComponent = [
<div key="footer" className={style.modalFooters}>
<Checkbox checked={checkAll} onChange={onCheckAll}>
全选
</Checkbox>
<div>
<Button onClick={handleCancel}> 取消 </Button>
<Button type="primary" loading={loading} onClick={handleConfirm}>
{' '}
保存{' '}
</Button>
</div>
</div>,
];
return ( return (
<Modal <Modal
title={props.title || '设置可售日期'} title={props.title || '设置可售日期'}
open={props.visible} open={props.visible}
destroyOnClose destroyOnClose
maskClosable={false} maskClosable={false}
width="200px" width="300px"
okText="保存" footer={footerComponent}
onOk={handleConfirm}
onCancel={handleCancel}
> >
<Checkbox.Group onChange={onChangeWeek} value={value}> <Checkbox.Group onChange={onChangeWeek} value={value}>
<Space direction="vertical"> <Space direction="vertical">
......
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { Checkbox, Space, Modal, notification, message } from 'antd'; import { Checkbox, Space, Modal, notification, message } from 'antd';
import { mealColumn } from '../staticData/goods'; import { mealColumn } from '../staticData/goods';
import { apiMealInfoUpdate, apiEnterpriseInfo } from '../service'; import { apiMealInfoUpdate, apiEnterpriseInfo, apiCheckInfo } from '../service';
const SaleDateModal = props => { const SaleDateModal = props => {
const [value, setValue] = useState([]); const [value, setValue] = useState([]);
...@@ -23,25 +23,52 @@ const SaleDateModal = props => { ...@@ -23,25 +23,52 @@ const SaleDateModal = props => {
message.error('请选择餐段'); message.error('请选择餐段');
return; return;
} }
const arr = value.sort((x, y) => x - y);
if (props.productType === 4) {
const data = [];
props.dataSource.forEach(item => {
const obj = {
saleDateList: item.saleDate,
tabCateList: item.tabCate?.map(v => ({ tabId: v })),
};
if (props.item.skuId === item.skuId && item.saleDate) {
obj.tabCateList = arr.map(v => ({ tabId: v }));
data.push(obj);
} else if (item.saleDate?.length && item.tabCate?.length) {
data.push(obj);
}
});
const res = await apiCheckInfo(data);
if (!res || !res.success) {
return;
}
}
if (props.id) { if (props.id) {
const params = { const params = {
id: props.id, id: props.id,
tabIds: value, tabIds: arr,
}; };
await apiMealInfoUpdate(params); await apiMealInfoUpdate(params);
notification.success({ message: '保存成功' }); notification.success({ message: '保存成功' });
} }
handleCancel(); handleCancel();
props.handleRefresh(value); props.handleRefresh(arr);
}; };
// 获取店铺餐段通过企业ID // 获取店铺餐段通过企业ID
const getEnterpriseMealColumn = async () => { const getEnterpriseMealColumn = async valueList => {
setLoading(true); setLoading(true);
const res = await apiEnterpriseInfo(props.enterpriseID); const res = await apiEnterpriseInfo(props.enterpriseID);
setLoading(false); setLoading(false);
if (res && res.data && res.data.mealTimePeriod && res.data.mealTimePeriod.length) { if (res && res.data && res.data.mealTimePeriod && res.data.mealTimePeriod.length) {
setTabCateList(res.data.mealTimePeriod.map(item => `${item.mealPeriodType}`)); const arr = res.data.mealTimePeriod.map(item => `${item.mealPeriodType}`);
setTabCateList(arr);
// 餐段的值需在企业配置的可选餐段之内
const v = [];
valueList.forEach(t => {
arr.includes(t) && v.push(t);
});
setValue(v);
} }
}; };
...@@ -65,14 +92,14 @@ const SaleDateModal = props => { ...@@ -65,14 +92,14 @@ const SaleDateModal = props => {
} }
setValue(v); setValue(v);
if (props.enterpriseID) { if (props.enterpriseID) {
getEnterpriseMealColumn(); getEnterpriseMealColumn(v);
} }
} }
}, [props.visible]); }, [props.visible]);
return ( return (
<Modal <Modal
title={props.title || '修改可售餐段'} title={props.title || '设置可售餐段'}
open={props.visible} open={props.visible}
destroyOnClose destroyOnClose
maskClosable={false} maskClosable={false}
......
...@@ -51,28 +51,30 @@ const SaleDateModal = props => { ...@@ -51,28 +51,30 @@ const SaleDateModal = props => {
api = apiSelVirtualGoodsList; api = apiSelVirtualGoodsList;
} }
const res = await api(data); const res = await api(data);
console.log('res :>> ', res); const arr =
if (res && res.data && res.data.records) { res?.data?.records?.map(item => {
res.data.records.forEach(item => {
item.enterprisePrice = item.salePrice; item.enterprisePrice = item.salePrice;
}); return item;
setDataSource(res.data.records); }) || [];
setTotal(res.data.total); setDataSource(arr);
} setTotal(res?.data?.total || 0);
setLoading(false); setLoading(false);
}; };
// 点击搜索 // 点击搜索
const onSearch = () => { const onSearch = () => {
setSearchKeyword(searchName); setSearchKeyword(searchName);
searchList({ [searchType === '1' ? 'name' : 'skuId']: searchName }); setPage(1);
searchList({ [searchType === '1' ? 'name' : 'skuId']: searchName, current: 1 });
}; };
// 切换店铺 // 切换店铺
const onChangeShop = v => { const onChangeShop = v => {
setShopId(v); setShopId(v);
setPage(1);
searchList({ searchList({
shopId: v, shopId: v,
current: 1,
}); });
setSelectedRowKeys([]); setSelectedRowKeys([]);
setSelectedRows([]); setSelectedRows([]);
...@@ -231,6 +233,7 @@ const SaleDateModal = props => { ...@@ -231,6 +233,7 @@ const SaleDateModal = props => {
useEffect(() => { useEffect(() => {
if (+props.productType === 5) { if (+props.productType === 5) {
setShopId(null);
getShopList(); getShopList();
} else { } else {
setShopId(props.shopID); setShopId(props.shopID);
......
...@@ -7,7 +7,7 @@ export const getEnterpriseList = async (param = {}) => { ...@@ -7,7 +7,7 @@ export const getEnterpriseList = async (param = {}) => {
size: 10000, size: 10000,
data: param, data: param,
}); });
if (res && res.data && res.data.records) { if (res?.data?.records?.length) {
const data = res.data.records; const data = res.data.records;
const arr = data.map(item => ({ const arr = data.map(item => ({
label: item.name, label: item.name,
......
...@@ -171,3 +171,13 @@ export async function apiEnterpriseInfo(id) { ...@@ -171,3 +171,13 @@ export async function apiEnterpriseInfo(id) {
prefix: roleApi, prefix: roleApi,
}); });
} }
/**
* 企业团餐->虚拟商品 校验是否可修改餐段和可售日期
* http://yapi.quantgroups.com/project/389/interface/api/65674
*/
export async function apiCheckInfo(data) {
return request.post(`${apiPrefix}/product/enterprise/virtual/addParamCheck`, {
data,
prefix: roleApi,
});
}
...@@ -65,7 +65,7 @@ export const takeawayGoodsColumn = options => { ...@@ -65,7 +65,7 @@ export const takeawayGoodsColumn = options => {
hideInTable: true, hideInTable: true,
valueType: 'select', valueType: 'select',
fieldProps: { fieldProps: {
value: +enterpriseId, value: enterpriseId ? +enterpriseId : null,
showSearch: true, showSearch: true,
filterOption: (v, option) => (option?.label ?? '').toLowerCase().includes(v.toLowerCase()), filterOption: (v, option) => (option?.label ?? '').toLowerCase().includes(v.toLowerCase()),
onChange: onChangeEnterprise, onChange: onChangeEnterprise,
...@@ -144,7 +144,15 @@ export const takeawayGoodsColumn = options => { ...@@ -144,7 +144,15 @@ export const takeawayGoodsColumn = options => {
hideInSearch: true, hideInSearch: true,
render: (_, record) => ( render: (_, record) => (
<Space> <Space>
{_ && _.length ? <span>{_.map(item => item.tabName).join('/')}</span> : <span>-</span>} {_ && _.length ? (
<span>
{_.sort((x, y) => x.tabId - y.tabId)
.map(item => item.tabName)
.join('/')}
</span>
) : (
<span>-</span>
)}
<span <span
className={style.columnBtnEdit} className={style.columnBtnEdit}
onClick={() => { onClick={() => {
...@@ -380,7 +388,11 @@ export const GoodsInfoColumn = options => { ...@@ -380,7 +388,11 @@ export const GoodsInfoColumn = options => {
render: (_, record) => ( render: (_, record) => (
<Space> <Space>
{_ && _.length ? ( {_ && _.length ? (
<span key="1">{_.map(item => mealColumn[item]).join('/')}</span> <span key="1">
{_.sort((x, y) => x - y)
.map(item => mealColumn[item])
.join('/')}
</span>
) : ( ) : (
<span key="2" className={style.columnTip}> <span key="2" className={style.columnTip}>
请配置 请配置
...@@ -484,7 +496,7 @@ export const virtualGoodsColumn = options => { ...@@ -484,7 +496,7 @@ export const virtualGoodsColumn = options => {
hideInTable: true, hideInTable: true,
valueType: 'select', valueType: 'select',
fieldProps: { fieldProps: {
value: +enterpriseId, value: enterpriseId ? +enterpriseId : null,
showSearch: true, showSearch: true,
filterOption: (v, option) => (option?.label ?? '').toLowerCase().includes(v.toLowerCase()), filterOption: (v, option) => (option?.label ?? '').toLowerCase().includes(v.toLowerCase()),
onChange: onChangeEnterprise, onChange: onChangeEnterprise,
...@@ -552,7 +564,15 @@ export const virtualGoodsColumn = options => { ...@@ -552,7 +564,15 @@ export const virtualGoodsColumn = options => {
align: 'center', align: 'center',
hideInSearch: true, hideInSearch: true,
render: (_, record) => ( render: (_, record) => (
<Space>{_ && _.length && <span>{_.map(item => item.tabName).join('/')}</span>}</Space> <Space>
{_ && _.length && (
<span>
{_.sort((x, y) => x.tabId - y.tabId)
.map(item => item.tabName)
.join('/')}
</span>
)}
</Space>
), ),
}, },
{ {
......
...@@ -75,3 +75,8 @@ ...@@ -75,3 +75,8 @@
.columnBtnEdit { .columnBtnEdit {
cursor: pointer; cursor: pointer;
} }
.modalFooters {
display: flex;
align-items: center;
justify-content: space-between;
}
...@@ -27,18 +27,22 @@ const TakeawayGoods = () => { ...@@ -27,18 +27,22 @@ const TakeawayGoods = () => {
const [visibleSaleSection, setVisibleSaleSection] = useState(false); // 可售餐段弹窗 const [visibleSaleSection, setVisibleSaleSection] = useState(false); // 可售餐段弹窗
const [visiblePrice, setVisiblePrice] = useState(false); // 修改企业商品价格弹窗 const [visiblePrice, setVisiblePrice] = useState(false); // 修改企业商品价格弹窗
const [visibleSort, setVisibleSort] = useState(false); // 商品排序弹窗 const [visibleSort, setVisibleSort] = useState(false); // 商品排序弹窗
const [enterprises, setEnterprises] = useState({}); // 企业列表 const [enterprises, setEnterprises] = useState(null); // 企业列表
const [activeKey, setActiveKey] = useState(''); // 自提点ID const [activeKey, setActiveKey] = useState(''); // 自提点ID
const [enterpriseId, setEnterpriseId] = useState(); // 企业ID const [enterpriseId, setEnterpriseId] = useState(''); // 企业ID
const [pickSelfList, setPickSelfList] = useState([]); // 取餐点列表 const [pickSelfList, setPickSelfList] = useState([]); // 取餐点列表
const [recordID, setRecordID] = useState(''); // 编辑的记录ID const [recordID, setRecordID] = useState(''); // 编辑的记录ID
const [editItem, setEditItem] = useState({}); // 编辑的记录信息 const [editItem, setEditItem] = useState({}); // 编辑的记录信息
// 刷新列表 // 刷新列表
const onRefresh = () => { const onRefresh = isReset => {
if (pageLoaded) { if (!pageLoaded) {
refTable.current.reloadAndRest(); return;
// refTable.current.reload(); }
if (isReset) {
refTable?.current?.reloadAndRest();
} else {
refTable?.current?.reload();
} }
}; };
...@@ -108,24 +112,17 @@ const TakeawayGoods = () => { ...@@ -108,24 +112,17 @@ const TakeawayGoods = () => {
// 根据企业ID获取取餐点 // 根据企业ID获取取餐点
const getPickSelf = async id => { const getPickSelf = async id => {
const res = await apiSelPickSelfList({ enterpriseId: id }); const res = await apiSelPickSelfList({ enterpriseId: id });
if (res && res.data && res.data.length) { const arr =
setPickSelfList( res?.data?.map(item => ({
res.data.map(item => ({ key: `${item.pickSelfId}`,
key: `${item.pickSelfId}`, label: <span>{item.pickSelfName}</span>,
label: <span>{item.pickSelfName}</span>, })) || [];
})), setPickSelfList(arr);
); const [obj] = res?.data || [];
const pid = res.data[0].pickSelfId; const pid = `${obj?.pickSelfId || ''}`;
setActiveKey(`${pid}`); setActiveKey(`${pid}`);
if (pageLoaded) { if (pageLoaded) {
onRefresh(); onRefresh(true);
}
} else {
setPickSelfList([]);
setActiveKey('');
if (pageLoaded) {
onRefresh();
}
} }
}; };
...@@ -144,8 +141,10 @@ const TakeawayGoods = () => { ...@@ -144,8 +141,10 @@ const TakeawayGoods = () => {
setEnterprises(obj.list); setEnterprises(obj.list);
setEnterpriseId(`${obj.id}`); setEnterpriseId(`${obj.id}`);
await getPickSelf(obj.id); await getPickSelf(obj.id);
setPageLoaded(true); } else if (!enterpriseId) {
notification.error({ message: '未找到企业' });
} }
setPageLoaded(true);
}; };
useEffect(() => { useEffect(() => {
...@@ -167,6 +166,14 @@ const TakeawayGoods = () => { ...@@ -167,6 +166,14 @@ const TakeawayGoods = () => {
onRefresh, onRefresh,
}; };
let request = () => ({
data: [],
total: 0,
});
if (enterpriseId) {
request = params => searchList({ ...params });
}
return ( return (
<div className={utilStyle.formPageBox}> <div className={utilStyle.formPageBox}>
{pageLoaded && ( {pageLoaded && (
...@@ -180,12 +187,12 @@ const TakeawayGoods = () => { ...@@ -180,12 +187,12 @@ const TakeawayGoods = () => {
actionRef={refTable} actionRef={refTable}
tableClassName={utilStyle.formTable} tableClassName={utilStyle.formTable}
columns={takeawayGoodsColumn(options)} columns={takeawayGoodsColumn(options)}
request={params => searchList({ ...params })} request={request}
rowKey={r => r.id} rowKey={r => r.id}
expandIconColumnIndex={10}
bordered bordered
options={false} options={false}
form={{ initialValues: { enterpriseId } }} form={{ initialValues: { enterpriseId } }}
scroll={{ x: 1300 }}
toolbar={{ toolbar={{
menu: { menu: {
type: 'tab', type: 'tab',
...@@ -193,7 +200,7 @@ const TakeawayGoods = () => { ...@@ -193,7 +200,7 @@ const TakeawayGoods = () => {
items: pickSelfList, items: pickSelfList,
onChange: key => { onChange: key => {
setActiveKey(`${key}`); setActiveKey(`${key}`);
onRefresh(); onRefresh(true);
}, },
}, },
actions: [ actions: [
......
...@@ -14,16 +14,15 @@ const VirtualGoods = () => { ...@@ -14,16 +14,15 @@ const VirtualGoods = () => {
const history = useHistory(); const history = useHistory();
const refTable = useRef(); const refTable = useRef();
const [pageLoaded, setPageLoaded] = useState(false); const [pageLoaded, setPageLoaded] = useState(false);
const [enterprises, setEnterprises] = useState({}); // 企业列表 const [enterprises, setEnterprises] = useState(null); // 企业列表
const [enterpriseId, setEnterpriseId] = useState(); const [enterpriseId, setEnterpriseId] = useState(''); // 选中企业ID
// 刷新列表 // 刷新列表
// const onRefresh = () => { const onRefresh = () => {
// if (pageLoaded) { if (pageLoaded) {
// refTable.current.reloadAndRest(); refTable?.current?.reloadAndRest();
// // refTable.current.reload(); }
// } };
// };
// 搜索商品列表 // 搜索商品列表
const searchList = async params => { const searchList = async params => {
...@@ -80,6 +79,7 @@ const VirtualGoods = () => { ...@@ -80,6 +79,7 @@ const VirtualGoods = () => {
// 改变企业 // 改变企业
const onChangeEnterprise = v => { const onChangeEnterprise = v => {
setEnterpriseId(v); setEnterpriseId(v);
onRefresh();
}; };
// 获取企业列表 // 获取企业列表
...@@ -90,8 +90,10 @@ const VirtualGoods = () => { ...@@ -90,8 +90,10 @@ const VirtualGoods = () => {
if (obj.list && Object.keys(obj.list).length) { if (obj.list && Object.keys(obj.list).length) {
setEnterprises(obj.list); setEnterprises(obj.list);
setEnterpriseId(`${obj.id}`); setEnterpriseId(`${obj.id}`);
setPageLoaded(true); } else if (!enterpriseId) {
notification.error({ message: '未找到企业' });
} }
setPageLoaded(true);
}; };
useEffect(() => { useEffect(() => {
...@@ -105,6 +107,14 @@ const VirtualGoods = () => { ...@@ -105,6 +107,14 @@ const VirtualGoods = () => {
onChangeEnterprise, onChangeEnterprise,
}; };
let request = () => ({
data: [],
total: 0,
});
if (enterpriseId) {
request = params => searchList({ ...params });
}
return ( return (
<div className={utilStyle.formPageBox}> <div className={utilStyle.formPageBox}>
{pageLoaded && ( {pageLoaded && (
...@@ -118,12 +128,11 @@ const VirtualGoods = () => { ...@@ -118,12 +128,11 @@ const VirtualGoods = () => {
actionRef={refTable} actionRef={refTable}
tableClassName={utilStyle.formTable} tableClassName={utilStyle.formTable}
columns={virtualGoodsColumn(options)} columns={virtualGoodsColumn(options)}
request={params => searchList({ ...params })} request={request}
rowKey={r => r.id} rowKey={r => r.id}
expandIconColumnIndex={10}
bordered bordered
options={false} options={false}
form={{ initialValues: { enterpriseId } }} scroll={{ x: 1300 }}
toolbar={{ toolbar={{
actions: [ actions: [
<Button key="1" icon={<PlusOutlined />} type="primary" onClick={toAdd}> <Button key="1" icon={<PlusOutlined />} type="primary" onClick={toAdd}>
......
...@@ -22,7 +22,7 @@ const VirtualGoodsInfo = props => { ...@@ -22,7 +22,7 @@ const VirtualGoodsInfo = props => {
const [visiblePrice, setVisiblePrice] = useState(false); // 修改企业商品价格弹窗 const [visiblePrice, setVisiblePrice] = useState(false); // 修改企业商品价格弹窗
const [dataSource, setDataSource] = useState([]); const [dataSource, setDataSource] = useState([]);
const [shopList, setShopList] = useState({}); // 店铺列表 const [shopList, setShopList] = useState({}); // 店铺列表
const [sleShopID, setSelShopID] = useState(''); // 选中的店铺ID const [sleShopID, setSelShopID] = useState(null); // 选中的店铺ID
const [sleShopName, setSelShopName] = useState(''); // 选中的店铺名称 const [sleShopName, setSelShopName] = useState(''); // 选中的店铺名称
const [selectedRowKeys, setSelectedRowKeys] = useState([]); const [selectedRowKeys, setSelectedRowKeys] = useState([]);
const [editItem, setEditItem] = useState({}); // 编辑信息 const [editItem, setEditItem] = useState({}); // 编辑信息
...@@ -137,6 +137,7 @@ const VirtualGoodsInfo = props => { ...@@ -137,6 +137,7 @@ const VirtualGoodsInfo = props => {
setSelectedRowKeys(keys); setSelectedRowKeys(keys);
} else { } else {
setDataSource([]); setDataSource([]);
setSelectedRowKeys([]);
} }
}; };
...@@ -237,6 +238,8 @@ const VirtualGoodsInfo = props => { ...@@ -237,6 +238,8 @@ const VirtualGoodsInfo = props => {
visible={visibleSaleDate} visible={visibleSaleDate}
item={editItem} item={editItem}
type="saleDate" type="saleDate"
productType={4}
dataSource={dataSource}
handleRefresh={v => refreshList('saleDate', v)} handleRefresh={v => refreshList('saleDate', v)}
handleClose={() => setVisibleSaleDate(false)} handleClose={() => setVisibleSaleDate(false)}
/> />
...@@ -248,6 +251,8 @@ const VirtualGoodsInfo = props => { ...@@ -248,6 +251,8 @@ const VirtualGoodsInfo = props => {
item={editItem} item={editItem}
type="tabCate" type="tabCate"
enterpriseID={id} enterpriseID={id}
productType={4}
dataSource={dataSource}
handleRefresh={v => refreshList('tabCate', v)} handleRefresh={v => refreshList('tabCate', v)}
handleClose={() => setVisibleSaleSection(false)} handleClose={() => setVisibleSaleSection(false)}
/> />
......
...@@ -48,54 +48,56 @@ export async function getInfo() { ...@@ -48,54 +48,56 @@ export async function getInfo() {
const data = await apiBusinessDetail(); const data = await apiBusinessDetail();
let settlementType = 1; let settlementType = 1;
const res = data.data; const res = data.data;
if (res) { console.log('res :>> ', res);
if (res.facilities) { if (!res) {
res.customList = res.facilities.customList || []; return;
res.selfList = res.facilities.selfList || []; }
} if (res.facilities) {
res.signDateType = res.signDateType ? [res.signDateType] : []; res.customList = res.facilities.customList || [];
res.accountOpenPermitImage = res.accountOpenPermitImage res.selfList = res.facilities.selfList || [];
? [{ uid: 0, url: res.accountOpenPermitImage }] }
: []; res.signDateType = res.signDateType ? [res.signDateType] : [];
res.businessLicenseImage = res.businessLicenseImage res.accountOpenPermitImage = res.accountOpenPermitImage
? [{ uid: 0, url: res.businessLicenseImage }] ? [{ uid: 0, url: res.accountOpenPermitImage }]
: []; : [];
res.idCardEmblemImage = res.idCardEmblemImage ? [{ uid: 0, url: res.idCardEmblemImage }] : []; res.businessLicenseImage = res.businessLicenseImage
res.idCardPortraitImage = res.idCardPortraitImage ? [{ uid: 0, url: res.businessLicenseImage }]
? [{ uid: 0, url: res.idCardPortraitImage }] : [];
: []; res.idCardEmblemImage = res.idCardEmblemImage ? [{ uid: 0, url: res.idCardEmblemImage }] : [];
res.primaryImage = res.primaryImage ? [{ uid: 0, url: res.primaryImage }] : []; res.idCardPortraitImage = res.idCardPortraitImage
res.sealImage = res.sealImage ? [{ uid: 0, url: res.sealImage }] : []; ? [{ uid: 0, url: res.idCardPortraitImage }]
res.brandCertificate = res.brandCertificate ? [{ uid: 0, url: res.brandCertificate }] : []; : [];
// 非同名结算授权文件 res.primaryImage = res.primaryImage ? [{ uid: 0, url: res.primaryImage }] : [];
res.differentNameAuthorizationImage = res.differentNameAuthorizationImage res.sealImage = res.sealImage ? [{ uid: 0, url: res.sealImage }] : [];
? [{ uid: 0, url: res.brandCertificate }] res.brandCertificate = res.brandCertificate ? [{ uid: 0, url: res.brandCertificate }] : [];
: []; // 非同名结算授权文件
// 户口本本人页 res.differentNameAuthorizationImage = res.differentNameAuthorizationImage
res.householdRegisterImage = res.householdRegisterImage ? [{ uid: 0, url: res.brandCertificate }]
? [{ uid: 0, url: res.householdRegisterImage }] : [];
: []; // 户口本本人页
// 工商局 res.householdRegisterImage = res.householdRegisterImage
res.icbProofImage = res.icbProofImage ? [{ uid: 0, url: res.icbProofImage }] : []; ? [{ uid: 0, url: res.householdRegisterImage }]
if (res.categoryQualificateImage && res.categoryQualificateImage.length) { : [];
res.categoryQualificateImage = res.categoryQualificateImage.map((item, index) => ({ // 工商局
uid: index, res.icbProofImage = res.icbProofImage ? [{ uid: 0, url: res.icbProofImage }] : [];
url: item, if (res.categoryQualificateImage && res.categoryQualificateImage.length) {
})); res.categoryQualificateImage = res.categoryQualificateImage.map((item, index) => ({
} uid: index,
if (res.otherImage && res.otherImage.length) { url: item,
res.otherImage = res.otherImage.map((item, index) => ({ }));
uid: index, }
url: item, if (res.otherImage && res.otherImage.length) {
})); res.otherImage = res.otherImage.map((item, index) => ({
} uid: index,
res.addr = [res.provinceId, res.cityId, res.countyId]; url: item,
if (res.townId) { }));
res.addr.push(res.townId); }
} res.addr = [res.provinceId, res.cityId, res.countyId];
if (settlementType !== null) { if (res.townId) {
settlementType = +res.settlementType; res.addr.push(res.townId);
} }
if (settlementType !== null) {
settlementType = +res.settlementType;
} }
// 身份证有效期处理 // 身份证有效期处理
let checkboxDisabled = false; let checkboxDisabled = false;
...@@ -266,8 +268,8 @@ export function renderServiceTags() { ...@@ -266,8 +268,8 @@ export function renderServiceTags() {
that.state.businessInfo.customList.map(tag => { that.state.businessInfo.customList.map(tag => {
const isLongTag = tag.length > 20; const isLongTag = tag.length > 20;
const tagElem = ( const tagElem = (
// closable 二期放开 // 二期放开
<Tag key={tag} onClose={() => handleClose.call(that, tag)}> <Tag key={tag} closable onClose={() => handleClose.call(that, tag)}>
{isLongTag ? `${tag.slice(0, 20)}...` : tag} {isLongTag ? `${tag.slice(0, 20)}...` : tag}
</Tag> </Tag>
); );
...@@ -293,14 +295,14 @@ export function renderServiceTags() { ...@@ -293,14 +295,14 @@ export function renderServiceTags() {
/> />
)} )}
{/* 二期放开 */} {/* 二期放开 */}
{/* {!that.state.tagVisible && ( {!that.state.tagVisible && (
<Tag <Tag
onClick={() => showInput.call(that)} onClick={() => showInput.call(that)}
style={{ background: '#fff', borderStyle: 'dashed' }} style={{ background: '#fff', borderStyle: 'dashed' }}
> >
<PlusOutlined /> 新增 <PlusOutlined /> 新增
</Tag> </Tag>
)} */} )}
</div> </div>
); );
} }
......
import React, { Component } from 'react'; import React, { Component } from 'react';
import { history } from 'umi';
import moment from 'moment'; import moment from 'moment';
import { ArrowRightOutlined, UploadOutlined } from '@ant-design/icons'; import { ArrowRightOutlined, UploadOutlined } from '@ant-design/icons';
import { Form } from '@ant-design/compatible'; import { Form } from '@ant-design/compatible';
...@@ -31,7 +30,7 @@ import { ...@@ -31,7 +30,7 @@ import {
getServiceFacility, getServiceFacility,
businessModel, businessModel,
getInfo, getInfo,
getBankList, // getBankList,
businessTypeDesc, businessTypeDesc,
signDateTypeList, signDateTypeList,
legalPersonList, legalPersonList,
...@@ -62,7 +61,7 @@ class BusinessInfo extends Component { ...@@ -62,7 +61,7 @@ class BusinessInfo extends Component {
areaAddr: [], // 四级地址列表 areaAddr: [], // 四级地址列表
categoryList: [], // 主要类目 categoryList: [], // 主要类目
serviceFacilitys: [], // 服务设施 serviceFacilitys: [], // 服务设施
bankList: [], // 开户行 // bankList: [], // 开户行
settlementType: 1, settlementType: 1,
// loading: false, // loading: false,
visibleLoading: false, visibleLoading: false,
...@@ -70,14 +69,14 @@ class BusinessInfo extends Component { ...@@ -70,14 +69,14 @@ class BusinessInfo extends Component {
mainCategoryId: null, mainCategoryId: null,
companyNamedis: false, // 公司名称 companyNamedis: false, // 公司名称
companyType: null, // 企业类型 companyType: null, // 企业类型
disabled: true, // 二期放开 disabled: false, // 二期放开
}; };
async componentDidMount() { async componentDidMount() {
await getInfo.call(this); await getInfo.call(this);
getAreaAddr.call(this); getAreaAddr.call(this);
getServiceFacility.call(this); getServiceFacility.call(this);
getBankList.call(this); // getBankList.call(this);
} }
// 主营类目 // 主营类目
...@@ -203,7 +202,11 @@ class BusinessInfo extends Component { ...@@ -203,7 +202,11 @@ class BusinessInfo extends Component {
''; '';
obj.householdRegisterImage = obj.householdRegisterImage =
(imgs?.householdRegisterImage?.length && imgs?.householdRegisterImage[0]?.url) || ''; (imgs?.householdRegisterImage?.length && imgs?.householdRegisterImage[0]?.url) || '';
obj.icbProofImage = (imgs?.icbProofImage?.length && imgs?.icbProofImage[0]?.url) || ''; if (!this.state.companyNamedis) {
obj.icbProofImage = (imgs?.icbProofImage?.length && imgs?.icbProofImage[0]?.url) || '';
} else {
delete obj.icbProofImage;
}
obj.applySource = 1; obj.applySource = 1;
obj.signDateType = 1; obj.signDateType = 1;
...@@ -582,9 +585,11 @@ class BusinessInfo extends Component { ...@@ -582,9 +585,11 @@ class BusinessInfo extends Component {
<Row> <Row>
<Col span={4} style={{ marginTop: '4px' }}> <Col span={4} style={{ marginTop: '4px' }}>
{/* 二期放开 */} {/* 二期放开 */}
{/* {businessInfo.companyName && ( {businessInfo.companyName && (
<Button onClick={this.setCompanyName}>修改</Button> <Button onClick={this.setCompanyName}>
)} */} {this.state.companyNamedis ? '修改' : '取消'}
</Button>
)}
</Col> </Col>
<Col span={12}> <Col span={12}>
<FormItem label="公司名称" labelCol={{ span: 8 }}> <FormItem label="公司名称" labelCol={{ span: 8 }}>
...@@ -991,23 +996,9 @@ class BusinessInfo extends Component { ...@@ -991,23 +996,9 @@ class BusinessInfo extends Component {
<Col span={12}> <Col span={12}>
<FormItem label="开户行" labelCol={{ span: 8 }}> <FormItem label="开户行" labelCol={{ span: 8 }}>
{getFieldDecorator('accountBankName', { {getFieldDecorator('accountBankName', {
rules: [{ required: true, message: '选择开户行!' }], rules: [{ required: true, message: '输入开户行!' }],
initialValue: businessInfo.accountBankName, initialValue: businessInfo.accountBankName,
})( })(<Input maxLength={100} disabled={disabled} />)}
<Select
showSearch
filterOption={(input, option) =>
(option?.value ?? '').toLowerCase().includes(input.toLowerCase())
}
disabled={disabled}
>
{this.state.bankList.map(item => (
<Option value={item.bankName} key={item.bankName}>
{item.bankName}
</Option>
))}
</Select>,
)}
</FormItem> </FormItem>
</Col> </Col>
<Col span={12}> <Col span={12}>
...@@ -1080,23 +1071,9 @@ class BusinessInfo extends Component { ...@@ -1080,23 +1071,9 @@ class BusinessInfo extends Component {
<Col span={12}> <Col span={12}>
<FormItem label="开户行" labelCol={{ span: 8 }}> <FormItem label="开户行" labelCol={{ span: 8 }}>
{getFieldDecorator('accountBankName', { {getFieldDecorator('accountBankName', {
rules: [{ required: true, message: '选择开户行!' }], rules: [{ required: true, message: '输入开户行!' }],
initialValue: businessInfo.accountBankName, initialValue: businessInfo.accountBankName,
})( })(<Input maxLength={100} disabled={disabled} />)}
<Select
showSearch
filterOption={(input, option) =>
(option?.value ?? '').toLowerCase().includes(input.toLowerCase())
}
disabled={disabled}
>
{this.state.bankList.map(item => (
<Option value={item.bankName} key={item.bankName}>
{item.bankName}
</Option>
))}
</Select>,
)}
</FormItem> </FormItem>
</Col> </Col>
<Col span={12}> <Col span={12}>
...@@ -1146,23 +1123,9 @@ class BusinessInfo extends Component { ...@@ -1146,23 +1123,9 @@ class BusinessInfo extends Component {
<Col span={12}> <Col span={12}>
<FormItem label="开户行" labelCol={{ span: 8 }}> <FormItem label="开户行" labelCol={{ span: 8 }}>
{getFieldDecorator('accountBankName', { {getFieldDecorator('accountBankName', {
rules: [{ required: true, message: '选择开户行!' }], rules: [{ required: true, message: '输入开户行!' }],
initialValue: businessInfo.accountBankName, initialValue: businessInfo.accountBankName,
})( })(<Input maxLength={100} disabled={disabled} />)}
<Select
showSearch
filterOption={(input, option) =>
(option?.value ?? '').toLowerCase().includes(input.toLowerCase())
}
disabled={disabled}
>
{this.state.bankList.map(item => (
<Option value={item.bankName} key={item.bankName}>
{item.bankName}
</Option>
))}
</Select>,
)}
</FormItem> </FormItem>
</Col> </Col>
<Col span={12}> <Col span={12}>
...@@ -1194,14 +1157,14 @@ class BusinessInfo extends Component { ...@@ -1194,14 +1157,14 @@ class BusinessInfo extends Component {
</Card> </Card>
)} )}
{/* 临时注释二期放开 */} {/* 临时注释二期放开 */}
{/* <div className={styles.formBtns}> <div className={styles.formBtns}>
<Button type="primary" size="large" htmlType="submit" loading={this.state.loading}> <Button type="primary" size="large" htmlType="submit" loading={this.state.loading}>
修改并提交 修改并提交
</Button> </Button>
<Button size="large" onClick={this.onCancel}> <Button size="large" onClick={this.onCancel}>
取消 取消
</Button> </Button>
</div> */} </div>
</Form> </Form>
{this.state.visibleLoading && ( {this.state.visibleLoading && (
<div className={styles.spinBox}> <div className={styles.spinBox}>
......
...@@ -50,7 +50,7 @@ export const apiRecognize = params => ...@@ -50,7 +50,7 @@ export const apiRecognize = params =>
// 编辑商户信息 // 编辑商户信息
export const apiEditStoreInfo = params => export const apiEditStoreInfo = params =>
request.post('/api/merchants/suppliers/edit', { prefix: kdspApi, data: params }); request.post('/api/merchants/suppliers/info/edit', { prefix: kdspApi, data: params });
// 查询商户详情 // 查询商户详情
export const apiBusinessDetail = businessId => export const apiBusinessDetail = businessId =>
......
import React from 'react';
import { Table } from 'antd';
import style from './style.less';
const GoodsRemark = props => {
const columns = [
{
title: '商品',
dataIndex: 'skuName',
width: 350,
render: (value, record) => {
const { skuName, imageUrl } = record;
return (
<div className={style.popoverGoods}>
<img src={imageUrl} alt="" className={style['popoverGoods-img']} />
<span>{skuName}</span>
</div>
);
},
},
{
title: '备注',
dataIndex: 'content',
width: 150,
},
{
title: '操作时间',
dataIndex: 'time',
width: 150,
},
];
return (
<div>
<Table columns={columns} dataSource={props.dataSource} pagination={false} />
</div>
);
};
export default GoodsRemark;
.popoverGoods {
display: flex;
align-items: center;
&-img {
width: 50px;
height: 50px;
margin-right: 10px;
}
}
...@@ -10,7 +10,7 @@ const FormItem = Form.Item; ...@@ -10,7 +10,7 @@ const FormItem = Form.Item;
const { Option } = Select; const { Option } = Select;
const LogisticsForm = props => { const LogisticsForm = props => {
const { modalVisible, onCancel, companys = [], skuList, onSubmit } = props; const { modalVisible, onCancel, company = [], skuList, onSubmit } = props;
const { getFieldDecorator } = props.form; const { getFieldDecorator } = props.form;
const [result, setResult] = useState(() => props.value); const [result, setResult] = useState(() => props.value);
const formData = async (formDataList, fieldsValue, suffixes) => { const formData = async (formDataList, fieldsValue, suffixes) => {
...@@ -127,14 +127,14 @@ const LogisticsForm = props => { ...@@ -127,14 +127,14 @@ const LogisticsForm = props => {
], ],
})( })(
<Select showSearch placeholder="请选择物流公司"> <Select showSearch placeholder="请选择物流公司">
{companys.map(item => ( {company?.map(item => (
<Option <Option
value={`${item.expressCompanyCode}-${item.expressCompanyName}`} value={`${item.expressCompanyCode}-${item.expressCompanyName}`}
key={item.expressCompanyCode} key={item.expressCompanyCode}
> >
{item.expressCompanyName} {item.expressCompanyName}
</Option> </Option>
))} )) || []}
</Select>, </Select>,
)} )}
</FormItem> </FormItem>
......
...@@ -248,51 +248,54 @@ const UpdateStatusModal = (props, ref) => { ...@@ -248,51 +248,54 @@ const UpdateStatusModal = (props, ref) => {
const onOk = async () => { const onOk = async () => {
const submitApi = oldPackageList.length === 0 ? apiDeliveriesAdd : apiDeliveriesEdit; const submitApi = oldPackageList.length === 0 ? apiDeliveriesAdd : apiDeliveriesEdit;
formRef.current.form.validateFields().then(async values => { formRef.current.form
const packageList = onFilterParams(values.packageList); .validateFields()
.then(async values => {
const packageList = onFilterParams(values.packageList);
// 对比修改差异 // 对比修改差异
oldPackageList.forEach((item, index) => { oldPackageList.forEach((item, index) => {
if (!packageList[index]) return; if (!packageList[index]) return;
const updateAttr = () => { const updateAttr = () => {
packageList[index].preExpressCompanyCode = item.expressCompanyCode; packageList[index].preExpressCompanyCode = item.expressCompanyCode;
packageList[index].preExpressCompanyName = item.expressCompanyName; packageList[index].preExpressCompanyName = item.expressCompanyName;
packageList[index].preExpressNo = item.expressNo; packageList[index].preExpressNo = item.expressNo;
}; };
// eslint-disable-next-line no-restricted-syntax
// for (const key in item) {
// if (typeof item[key] !== 'object' && item[key] !== packageList[index][key]) {
// console.log('修改了物流信息');
// updateAttr();
// break;
// } else if (
// typeof item[key] === 'object' &&
// JSON.stringify(item[key]) !== JSON.stringify(packageList[index][key])
// ) {
// console.log('修改了选择的商品');
// updateAttr();
// break;
// }
// }
updateAttr();
});
const params = { // eslint-disable-next-line no-restricted-syntax
orderNo: record.orderNo, // for (const key in item) {
packageList, // if (typeof item[key] !== 'object' && item[key] !== packageList[index][key]) {
}; // console.log('修改了物流信息');
setConfirmLoading(true); // updateAttr();
const res = await submitApi(params); // break;
setConfirmLoading(false); // } else if (
if (res.code === '0000' && res.businessCode === '0000') { // typeof item[key] === 'object' &&
notification.success({ // JSON.stringify(item[key]) !== JSON.stringify(packageList[index][key])
message: '提交成功', // ) {
// console.log('修改了选择的商品');
// updateAttr();
// break;
// }
// }
updateAttr();
}); });
onCancel();
actionRef.current.reload(); const params = {
} orderNo: record.orderNo,
}); packageList,
};
setConfirmLoading(true);
const res = await submitApi(params);
setConfirmLoading(false);
if (res?.code === '0000' && res?.businessCode === '0000') {
notification.success({
message: '提交成功',
});
onCancel();
actionRef.current.reload();
}
})
.catch(err => {});
}; };
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
......
...@@ -12,6 +12,7 @@ import LogisticsForm from './components/LogisticsForm'; ...@@ -12,6 +12,7 @@ import LogisticsForm from './components/LogisticsForm';
import PopoverDom from './components/PreviewImage'; import PopoverDom from './components/PreviewImage';
import LogisticsCom from './components/LogisticsCom'; import LogisticsCom from './components/LogisticsCom';
import DelayDeliverGoods from './components/DelayDeliverGoods'; import DelayDeliverGoods from './components/DelayDeliverGoods';
import GoodsRemark from '../components/GoodsRemark';
import MultiLogisticsModal from './components/MultiLogisticsModal'; import MultiLogisticsModal from './components/MultiLogisticsModal';
import { import {
...@@ -228,7 +229,7 @@ const TableList = props => { ...@@ -228,7 +229,7 @@ const TableList = props => {
key: 'orderNotice', key: 'orderNotice',
width: 150, width: 150,
render: (_, record) => { render: (_, record) => {
const { updateAddressList, platformRemark } = record; const { updateAddressList, platformRemark, platformRemarkList } = record;
// let addressData; // let addressData;
// if (updateAddressList) { // if (updateAddressList) {
// addressData = updateAddressList.pop(); // addressData = updateAddressList.pop();
...@@ -251,13 +252,11 @@ const TableList = props => { ...@@ -251,13 +252,11 @@ const TableList = props => {
) : ( ) : (
'' ''
)} )}
{platformRemark ? ( {platformRemarkList && platformRemarkList.length ? (
<Popover <Popover
placement="top" placement="top"
title="平台备注" title="平台备注"
content={ content={<GoodsRemark dataSource={platformRemarkList} />}
<PopoverNotice time={platformRemark?.time} content={platformRemark?.content} />
}
trigger="hover" trigger="hover"
> >
<Button block type="warning"> <Button block type="warning">
...@@ -591,7 +590,7 @@ const TableList = props => { ...@@ -591,7 +590,7 @@ const TableList = props => {
<LogisticsForm <LogisticsForm
onSubmit={reload} onSubmit={reload}
skuList={skuList} skuList={skuList}
companys={companys} company={companys}
onCancel={() => handleModalVisible(false)} onCancel={() => handleModalVisible(false)}
modalVisible={LogisticsModalVisible} modalVisible={LogisticsModalVisible}
value={LogisticsData} value={LogisticsData}
......
...@@ -48,13 +48,13 @@ ...@@ -48,13 +48,13 @@
} }
} }
.btnWarning { .btnWarning {
background-color: rgb(247, 143, 74); background-color: rgb(247, 143, 74) !important;
border-color: rgb(247, 143, 74); border-color: rgb(247, 143, 74) !important;
} }
.btnWarning:hover, .btnWarning:hover,
.btnWarning:focus { .btnWarning:focus {
background-color: rgb(253, 168, 111); background-color: rgb(253, 168, 111) !important;
border-color: rgb(253, 168, 111); border-color: rgb(253, 168, 111) !important;
} }
.tableList { .tableList {
......
...@@ -57,7 +57,7 @@ const DetailModal = (props, ref) => { ...@@ -57,7 +57,7 @@ const DetailModal = (props, ref) => {
{ {
title: '数量', title: '数量',
dataIndex: 'count', dataIndex: 'count',
width: 150, width: 100,
}, },
{ {
title: '小计', title: '小计',
...@@ -68,6 +68,11 @@ const DetailModal = (props, ref) => { ...@@ -68,6 +68,11 @@ const DetailModal = (props, ref) => {
return (supplyPrice * count).toFixed(2); return (supplyPrice * count).toFixed(2);
}, },
}, },
{
title: '商品备注',
dataIndex: 'remark',
width: 150,
},
{ {
title: '售后状态', title: '售后状态',
width: 150, width: 150,
......
import UUID from '@/utils/uuid';
export const layout = {
labelCol: { span: 6 },
wrapperCol: { span: 16 },
};
/**
* 文件路径数组转Upload fileList 列表值
* * */
export const getFileData = (arr = [], urlKey, nameKey) =>
arr.map(item => {
let url = item;
let name = item;
if (urlKey) {
url = item[urlKey];
}
if (nameKey) {
name = item[nameKey];
}
return {
url,
name,
uid: UUID.createUUID(),
status: 'done',
};
});
/* eslint-disable class-methods-use-this */
class IframeBridge {
constructor() {
console.info('use iframeBridge');
this.mountEventBus();
}
run(data, callbackName) {
return new Promise(resolve => {
if (!data.event) {
resolve(false);
return;
}
console.log('childIframe', data);
window.parent.postMessage(data, '*');
!callbackName && resolve(true);
});
}
mountEventBus() {
window.addEventListener('message', info => {
// 来源页面地址
console.log('addEventListenerCallBack', info);
});
}
// async closeBrowser() {
// return await this.run({ event: 'closeBrowser' });
// }
// async getToken() {
// return await this.run({ event: 'getToken' }, 'getTokenSuccess');
// }
// async openNewUrl(data: RunData = {}) {
// if (!data.event) {
// data.event = 'openNewUrl';
// }
// return await this.run(data);
// }
}
export default IframeBridge;
...@@ -9,9 +9,10 @@ import { notification } from 'antd'; ...@@ -9,9 +9,10 @@ import { notification } from 'antd';
import { getUrlSearchParams, getToken } from './utils'; import { getUrlSearchParams, getToken } from './utils';
import localStorage from './localStorage'; import localStorage from './localStorage';
import config from '../../config/env.config'; import config from '../../config/env.config';
import IframeBridge from './iframeBridge';
let isRefreshing = true; let isRefreshing = true;
let subscriber = []; const subscriber = [];
const codeMessage = { const codeMessage = {
200: '服务器成功返回请求的数据。', 200: '服务器成功返回请求的数据。',
...@@ -54,10 +55,12 @@ const request = extend({ ...@@ -54,10 +55,12 @@ const request = extend({
const addSubscriber = callback => subscriber.push(callback); const addSubscriber = callback => subscriber.push(callback);
const wait = async seconds => new Promise(resolve => setTimeout(resolve, seconds)); const wait = async seconds => new Promise(resolve => setTimeout(resolve, seconds));
const onAccessTokenFetched = () => { const onAccessTokenFetched = () => {
console.log('subscriber :>> ', subscriber);
subscriber.forEach(callback => callback()); subscriber.forEach(callback => callback());
subscriber = []; // subscriber = [];
}; };
const refreshRequest = async (url, options) => { const refreshRequest = async (url, options) => {
console.log('url, options :>> ', url, options);
const promise = new Promise(resolve => addSubscriber(() => resolve(request(url, options)))); const promise = new Promise(resolve => addSubscriber(() => resolve(request(url, options))));
if (isRefreshing) { if (isRefreshing) {
isRefreshing = false; isRefreshing = false;
...@@ -66,6 +69,7 @@ const refreshRequest = async (url, options) => { ...@@ -66,6 +69,7 @@ const refreshRequest = async (url, options) => {
if (data.code === 2000) { if (data.code === 2000) {
localStorage.set('token', data.data.accessToken); localStorage.set('token', data.data.accessToken);
} }
console.log('1111111 :>> ', subscriber.length);
onAccessTokenFetched(); onAccessTokenFetched();
isRefreshing = true; isRefreshing = true;
} }
...@@ -114,7 +118,7 @@ request.interceptors.response.use(async (response, options) => { ...@@ -114,7 +118,7 @@ request.interceptors.response.use(async (response, options) => {
}); });
} }
// token过期 // token过期
const url = response.url.split(config.api)[1]; const url = response.url.replace(options.prefix, '');
return refreshRequest(url, options); return refreshRequest(url, options);
} }
if (data.code === 4011) { if (data.code === 4011) {
...@@ -125,15 +129,17 @@ request.interceptors.response.use(async (response, options) => { ...@@ -125,15 +129,17 @@ request.interceptors.response.use(async (response, options) => {
notification.warning({ notification.warning({
message: data.msg, message: data.msg,
}); });
if (isTob) { if (!isTob) {
localStorage.remove('tobToken');
} else {
window.location.href = loginPath; window.location.href = loginPath;
} else {
const iframeBridgeModel = new IframeBridge();
iframeBridgeModel.run({ event: 'toLogin' });
} }
} }
if (data.businessCode && data.businessCode !== '0000' && !options.notTip) { if (data.businessCode && data.businessCode !== '0000' && !options.notTip) {
notification.warning({ notification.warning({
message: data.detail || data.msg || '操作失败', message: data.detail || data.msg || '操作失败',
duration: typeof options.duration === 'undefined' ? 4.5 : options.duration,
}); });
} }
return response; return response;
......
...@@ -3,7 +3,6 @@ import { parse } from 'querystring'; ...@@ -3,7 +3,6 @@ import { parse } from 'querystring';
import pathRegexp from 'path-to-regexp'; import pathRegexp from 'path-to-regexp';
import moment from 'moment'; import moment from 'moment';
import localStorage from './localStorage'; import localStorage from './localStorage';
import config from '@/../config/env.config';
/* eslint no-useless-escape:0 import/prefer-default-export:0 */ /* eslint no-useless-escape:0 import/prefer-default-export:0 */
const reg = /(((^https?:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)$/; const reg = /(((^https?:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)$/;
...@@ -183,8 +182,8 @@ export const getErrorMessage = err => { ...@@ -183,8 +182,8 @@ export const getErrorMessage = err => {
// 获取地址栏参数,name:参数名称 // 获取地址栏参数,name:参数名称
export const getUrlParams = name => { export const getUrlParams = name => {
const regArg = new RegExp(`(^|&)${name}=([^&]*)(&|$)`); const regArg = RegExp(`(^|&)${name}=([^&]*)(&|$)`);
const r = window.location.search.substring(1).match(regArg); const r = regArg.exec(window.location.search.substring(1));
if (r != null) return decodeURIComponent(r[2]); if (r != null) return decodeURIComponent(r[2]);
return null; return null;
}; };
...@@ -224,3 +223,6 @@ export const getToUrlQuery = () => { ...@@ -224,3 +223,6 @@ export const getToUrlQuery = () => {
} }
return {}; return {};
}; };
// 判断是否图片类型
export const checkIsImagePath = name => /\.(gif|jpg|jpeg|png)$/i.test(name.toLocaleLowerCase());
...@@ -62,4 +62,12 @@ ...@@ -62,4 +62,12 @@
.formPageBox { .formPageBox {
box-sizing: border-box; box-sizing: border-box;
padding: 20px; padding: 20px;
:global {
.ant-pro-query-filter-actions {
.ant-form-item-label {
width: 0 !important;
min-width: 0 !important;
}
}
}
} }
...@@ -193,21 +193,17 @@ export const isNumberSection = (rule, value, callback) => { ...@@ -193,21 +193,17 @@ export const isNumberSection = (rule, value, callback) => {
// 验证是否整数,非必填 // 验证是否整数,非必填
export function isIntegerNotMust(rule, value, callback) { export function isIntegerNotMust(rule, value, callback) {
if (!value) { if (!value) {
callback(); return Promise.resolve();
} }
setTimeout(() => { if (!Number(value)) {
if (!Number(value)) { return Promise.reject(new Error('请输入正整数'));
callback(new Error('请输入正整数')); }
} else { const re = /^[0-9]*[1-9][0-9]*$/;
const re = /^[0-9]*[1-9][0-9]*$/; const rsCheck = re.test(value);
const rsCheck = re.test(value); if (!rsCheck) {
if (!rsCheck) { return Promise.reject(new Error('请输入正整数'));
callback(new Error('请输入正整数')); }
} else { return Promise.resolve();
callback();
}
}
}, 1000);
} }
// 验证是否是[0-1]的小数 // 验证是否是[0-1]的小数
...@@ -456,7 +452,7 @@ export const getDefaultRule = data => { ...@@ -456,7 +452,7 @@ export const getDefaultRule = data => {
// 输入空格校验 // 输入空格校验
export const validateSpaces = (rule, value, callback) => { export const validateSpaces = (rule, value, callback) => {
// 输入不能包括空格 // 输入不能包括空格
if (value && value.includes(' ')) { if (value?.includes(' ')) {
// eslint-disable-next-line prefer-promise-reject-errors // eslint-disable-next-line prefer-promise-reject-errors
return Promise.reject('输入不能为空格'); return Promise.reject('输入不能为空格');
} }
......
...@@ -12,7 +12,6 @@ class Socket extends EventEmitter { ...@@ -12,7 +12,6 @@ class Socket extends EventEmitter {
this.taskRemindInterval = null; this.taskRemindInterval = null;
this.connected = false; this.connected = false;
this.waitingSendData = []; this.waitingSendData = [];
this.reconnectCount = 0;
this.heartBeatTimer = null; this.heartBeatTimer = null;
return this; return this;
} }
...@@ -117,7 +116,9 @@ class Socket extends EventEmitter { ...@@ -117,7 +116,9 @@ class Socket extends EventEmitter {
return; return;
} }
const sendValue = typeof value === 'string' ? value : JSON.stringify(value); const sendValue = typeof value === 'string' ? value : JSON.stringify(value);
this.socket.send(sendValue); if (this.socket.readyState === this.socket.OPEN) {
this.socket.send(sendValue);
}
} }
}; };
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment