Commit 8a3968c6 authored by GuanHua's avatar GuanHua

merge

parents d1825df3 17c0bcfc
......@@ -26,8 +26,8 @@
"dependencies": {
"@ant-design/icons": "^4.2.1",
"@ant-design/pro-layout": "^5.0.16",
"@formily/antd": "^1.2.10",
"@formily/antd-components": "^1.2.10",
"@formily/antd": "^1.2.11",
"@formily/antd-components": "^1.2.11",
"@umijs/hooks": "^1.9.3",
"@umijs/preset-react": "1.x",
"@umijs/test": "^3.2.0",
......
import React from 'react';
import SchemaForm, {
IAntdSchemaFormProps, createVirtualBox, registerVirtualBox,
IAntdSchemaFormProps, createVirtualBox, registerVirtualBox, Schema, SchemaField, FormButtonGroup, Reset, createControllerBox,
} from '@formily/antd';
import { Button, Space, Row, Col } from 'antd';
import styled from 'styled-components'
......@@ -18,6 +18,7 @@ import CustomRelevance from './components/CustomRelevance';
import Children from './components/Children';
import CircleBox from './components/CircleBox';
import './index.less'
import { Input } from '@formily/antd-components';
export interface NiceFormProps extends IAntdSchemaFormProps {}
......@@ -42,6 +43,37 @@ registerVirtualBox('flex-layout', (_props) => {
</RowLayout>
)
})
registerVirtualBox('empty-layout', (_props) => {
const { children, props } = _props
return (
<div>{children}</div>
)
})
const SchemaFormButtonGroup = createVirtualBox('schemaButtonGroup', FormButtonGroup)
const SchemaButton = createVirtualBox('schemaButton', Button)
const SchemaSubmit = createVirtualBox('schemaSubmit', Submit)
const SchemaReset = createVirtualBox('schemaReset', Reset)
export const FlexBox = createVirtualBox('flexBox', props => <Row {...props}/>)
// 该组件用于schema中嵌套表单, 不过控制台会出现警告
const schemaLayout = createControllerBox("schemaLayout", (_props) => {
const { schema } = _props;
const componentProps = schema.getExtendsComponentProps();
const { properties } = schema.toJSON();
const nestedSchema = new Schema({
type: "object",
properties
});
// const { visible, title, onCancel, footer, ...others } = componentProps;
return (
<NiceForm>
<SchemaField schema={nestedSchema}></SchemaField>
</NiceForm>
);
});
const NiceForm: React.FC<NiceFormProps> = props => {
const { children, components, ...reset } = props;
const customComponents = {
......@@ -57,7 +89,9 @@ const NiceForm: React.FC<NiceFormProps> = props => {
CustomRegistryPhone,
CustomRelevance,
Children,
CircleBox
CircleBox,
SchemaFormButtonGroup,
FlexBox
};
const defineComponents = Object.assign(customComponents, components);
......
......@@ -71,6 +71,7 @@ export const useLinkEnumEffect = (childKey, transformFn?) => {
type: 'value:linkage',
resolve: ({ origin, target }, { setFieldState, getFieldState }) => {
getFieldState(origin, state => {
console.log('origin', origin, 'state', state)
const { originData = [] } = state
setFieldState(target, targetState => {
if (state.value === undefined) {
......
import React, { useState, useEffect } from 'react';
import { Modal, Result, Progress, Button } from 'antd';
import { Modal, Result, Progress, Upload, Button } from 'antd';
import { FileExcelOutlined } from '@ant-design/icons';
import styles from './index.less';
import { PublicApi } from '@/services/api';
......@@ -8,6 +8,7 @@ interface Uploader {
visibleModal: boolean;
fileText: string;
onCancel: Function;
uploadUrl?: string;
}
let timeChange: any;
......@@ -15,6 +16,8 @@ let timeChange: any;
const UploadModal: React.FC<Uploader> = props => {
const [modalTitle, setModalTitle] = useState('导入');
const [modalStep, setModalStep] = useState(0);
const [file, setFile] = useState();
const [fileList, setFileList] = useState([]);
const downLoadTemplate = () => {};
......@@ -143,8 +146,18 @@ const UploadModal: React.FC<Uploader> = props => {
/>
);
const beforeUpload = file => {
let extension = file.name.split('.')[1];
if (['xlsx', 'xls'].includes[extension]) {
setTimeout(() => {
setExceptionCheck(true);
}, 250);
}
return false;
};
// 上传
const handleUpload = (type: string, step: number = 0) => {
const handleUpload = (type: string, file?: any, step: number = 0) => {
let title = '';
switch (type) {
case 'continue':
......@@ -162,6 +175,7 @@ const UploadModal: React.FC<Uploader> = props => {
}
setModalStep(step);
setModalTitle(title);
if (step === 1) return false;
};
const exportErrorLog = () => {};
......@@ -190,13 +204,20 @@ const UploadModal: React.FC<Uploader> = props => {
icon={<FileExcelOutlined />}
title={step0Description}
extra={
<Button
style={{ width: '100%' }}
type="primary"
onClick={() => handleUpload('upload')}
>
上传
</Button>
// <Upload
// name="file"
// action=""
// fileList={fileList}
// beforeUpload={file => beforeUpload(file)}
// customRequest={file => console.log(file)}
// >
// <Button type="primary">导入</Button>
// </Upload>
<>
<Button type="primary" onClick={() => handleUpload('upload')}>
导入
</Button>
</>
}
/>
</>
......
import { createFormActions, FormEffectHooks, FormPath } from '@formily/antd'
import { useLinkageUtils } from '@/utils/formEffectUtils'
const { onFormInit$ } = FormEffectHooks
/**
* @description 处理异步请求的下拉选择
* @param name 待处理的表单路径
* @param service 触发的异步函数, 需返回一个{label: any, value: any}形式的数组
*/
export const useAsyncInitSelect = (name: string[], service?: () => Promise<any>) => {
const { dispatch, setFieldState } = createFormActions()
const linkage = useLinkageUtils()
onFormInit$().subscribe(() => {
setFieldState(name, state => {
FormPath.setIn(state, 'props.x-props.hasFeedback', true)
})
name.forEach(v => linkage.loaded(v))
service().then(res => {
console.log(res)
name.forEach(v => {
linkage.loaded(v)
linkage.enum(v, res[v])
})
//请求结束可以dispatch一个自定义事件收尾,方便后续针对该事件做联动
dispatch('requestAsyncSelect', {
name,
payload: res
})
}).catch(err => {
// linkage.loaded(name)
// linkage.enum(name, [])
})
})
}
\ No newline at end of file
import React, { Component } from 'react';
import { Link } from 'umi'
import SchemaForm, { SchemaField, Schema, createControllerBox } from '@formily/antd';
import { Modal } from 'antd';
class Index extends Component<{}, {}> {
render() {
......
This diff is collapsed.
import { ISchema } from '@formily/antd';
import { FORM_FILTER_PATH } from '@/formSchema/const';
export const auditSchema: ISchema = {
type: 'object',
properties: {
megaLayout: {
type: 'object',
'x-component': 'mega-layout',
properties: {
topLayout: {
type: 'object',
'x-component': 'mega-layout',
'x-component-props': {
grid: true,
},
properties: {
ctl: {
type: 'object',
'x-component': 'Children',
'x-component-props': {
children: '{{controllerBtns}}',
},
},
search: {
type: 'string',
'x-component': 'Search',
'x-mega-props': {},
'x-component-props': {
placeholder: '搜索',
},
},
},
},
[FORM_FILTER_PATH]: {
type: 'object',
'x-component': 'flex-layout',
'x-component-props': {
rowStyle: {
flexWrap: 'nowrap',
},
colStyle: {
marginLeft: 20,
},
},
properties: {
memberType: {
type: 'string',
'x-component-props': {
placeholder: '请选择',
defaultValue: 0,
},
enum: [],
},
roleId: {
type: 'string',
'x-component-props': {
placeholder: '请选择',
defaultValue: 0,
},
enum: [],
},
level: {
type: 'string',
'x-component-props': {
placeholder: '请选择',
defaultValue: 0,
},
enum: [],
},
source: {
type: 'string',
'x-component-props': {
placeholder: '请选择',
defaultValue: 0,
},
enum: [],
},
timeRange: {
type: 'string',
'x-component-props': {
placeholder: '请选择',
defaultValue: 0,
},
enum: [
{ label: '时间范围(全部)', value: 0 },
{ label: '今天', value: 1 },
{ label: '一周内', value: 2 },
{ label: '一个月内', value: 3 },
{ label: '三个月内', value: 4 },
{ label: '六个月内', value: 5 },
{ label: '一年内', value: 6 },
{ label: '一年前', value: 7 },
],
},
submit: {
'x-component': 'Submit',
'x-mega-props': {
span: 1,
},
'x-component-props': {
children: '查询',
},
},
},
},
},
},
},
};
......@@ -2,24 +2,12 @@ import React, { useState, useEffect, useRef, ReactNode } from 'react';
import { history } from 'umi';
import { Tabs, Badge, Button, Card, Row, Col, message, Upload } from 'antd';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import {
ContainerOutlined,
PlusOutlined,
SaveOutlined,
} from '@ant-design/icons';
import { SaveOutlined } from '@ant-design/icons';
import { createFormActions } from '@formily/antd';
import { StandardTable } from 'god';
import { ColumnType } from 'antd/lib/table/interface';
import {
IFormFilter,
IButtonFilter,
} from 'god/dist/src/standard-table/TableController';
import ReutrnEle from '@/components/ReturnEle';
import styles from './index.less';
import NiceForm from '@/components/NiceForm';
import { initDetailSchema } from './schema';
import style from './index.less';
import { PublicApi } from '@/services/api';
const { TabPane } = Tabs;
......@@ -27,19 +15,7 @@ const { TabPane } = Tabs;
const addSchemaAction = createFormActions();
const addMember: React.FC<any> = props => {
const ref = useRef({});
const selectList: any = [
{
label: '',
// (
// <>
// <img src={ChinaImg} style={{ width: 24, height: 17 }} /> +86
// </>
// )
value: '1',
},
];
const ref = useRef<any>({});
/* 会员类型、会员角色、会员等级、注册手机号选项 */
const [memberItems, setMemberItems] = useState<any>({});
......
This diff is collapsed.
......@@ -2,57 +2,103 @@ import React from 'react';
import { ISchema } from '@formily/antd';
import { FORM_FILTER_PATH } from '@/formSchema/const';
export const maintianSchema: ISchema = {
export const importSchema: ISchema = {
type: 'object',
properties: {
megaLayout: {
type: 'object',
'x-component': 'mega-layout',
properties: {
search: {
type: 'string',
'x-component': 'Search',
'x-mega-props': {},
topLayout: {
type: 'object',
'x-component': 'mega-layout',
'x-component-props': {
placeholder: '请输入仓位名称',
grid: true,
},
properties: {
ctl: {
type: 'object',
'x-component': 'Children',
'x-component-props': {
children: '{{controllerBtns}}',
},
},
search: {
type: 'string',
'x-component': 'Search',
'x-mega-props': {},
'x-component-props': {
placeholder: '搜索',
},
},
},
},
[FORM_FILTER_PATH]: {
type: 'object',
'x-component': 'mega-layout',
visible: false,
'x-component': 'flex-layout',
'x-component-props': {
inline: true,
rowStyle: {
flexWrap: 'nowrap',
},
colStyle: {
marginLeft: 20,
},
},
properties: {
productName: {
memberType: {
type: 'string',
'x-component-props': {
placeholder: '商品名称',
placeholder: '请选择',
defaultValue: 0,
},
enum: [],
},
productId: {
roleId: {
type: 'string',
'x-component-props': {
placeholder: '商品ID',
placeholder: '请选择',
defaultValue: 0,
},
enum: [],
},
category: {
level: {
type: 'string',
'x-component-props': {
placeholder: '请选择品类',
placeholder: '请选择',
defaultValue: 0,
},
enum: [],
},
brand: {
source: {
type: 'string',
'x-component-props': {
placeholder: '请选择品牌',
placeholder: '请选择',
defaultValue: 0,
},
enum: [],
},
timeRange: {
type: 'string',
'x-component-props': {
placeholder: '请选择',
defaultValue: 0,
},
enum: [
{ label: '时间范围(全部)', value: 0 },
{ label: '今天', value: 1 },
{ label: '一周内', value: 2 },
{ label: '一个月内', value: 3 },
{ label: '三个月内', value: 4 },
{ label: '六个月内', value: 5 },
{ label: '一年内', value: 6 },
{ label: '一年前', value: 7 },
],
},
submit: {
'x-component': 'Submit',
'x-mega-props': {
span: 1,
},
'x-component-props': {
children: '查询',
},
......@@ -64,8 +110,6 @@ export const maintianSchema: ISchema = {
},
};
const registryPhone = <></>;
const getCompnentValue = (elements: any) => {
let components = {};
for (let item of elements) {
......
......@@ -44,7 +44,6 @@
.subCol {
width : 100%;
text-align: right;
.select {
width : 160px;
......
This diff is collapsed.
import { ISchema } from '@formily/antd';
import { FORM_FILTER_PATH } from '@/formSchema/const';
export const maintianSchema: ISchema = {
type: 'object',
properties: {
megaLayout: {
type: 'object',
'x-component': 'mega-layout',
properties: {
search: {
type: 'string',
'x-component': 'Search',
'x-component-props': {
placeholder: '搜索',
align: 'flex-left',
},
},
[FORM_FILTER_PATH]: {
type: 'object',
'x-component': 'mega-layout',
'x-component-props': {
grid: true,
full: true,
autoRow: true,
columns: 6,
},
properties: {
memberType: {
type: 'string',
'x-component-props': {
placeholder: '请选择',
defaultValue: 0,
},
enum: [],
},
roleId: {
type: 'string',
'x-component-props': {
placeholder: '请选择',
defaultValue: 0,
},
enum: [],
},
level: {
type: 'string',
'x-component-props': {
placeholder: '请选择',
defaultValue: 0,
},
enum: [],
},
source: {
type: 'string',
'x-component-props': {
placeholder: '请选择',
defaultValue: 0,
},
enum: [],
},
timeRange: {
type: 'string',
'x-component-props': {
placeholder: '请选择',
defaultValue: 0,
},
enum: [
{ label: '时间范围(全部)', value: 0 },
{ label: '今天', value: 1 },
{ label: '一周内', value: 2 },
{ label: '一个月内', value: 3 },
{ label: '三个月内', value: 4 },
{ label: '六个月内', value: 5 },
{ label: '一年内', value: 6 },
{ label: '一年前', value: 7 },
],
},
submit: {
'x-component': 'Submit',
'x-mega-props': {
span: 1,
},
'x-component-props': {
children: '查询',
},
},
},
},
},
},
},
};
......@@ -14,7 +14,7 @@ import ReutrnEle from '@/components/ReturnEle';
import './index.less'
import NiceForm from '@/components/NiceForm'
import { repositDetailSchema } from './schema'
import { createFormActions, ISchema } from '@formily/antd'
import { createFormActions, ISchema, FormEffectHooks } from '@formily/antd'
import EyePreview from '@/components/EyePreview'
import { findItemAndDelete } from '@/utils'
import { PublicApi } from '@/services/api'
......@@ -32,14 +32,6 @@ const fetchProductList = async (params) => {
return res.data
}
const fetchWarehouseAll = async () => {
const { data } = await PublicApi.getWarehouseWarehouseAll()
return data.map(v => ({
value: v.id,
label: v.name
}))
}
const addSchemaAction = createFormActions()
const AddRepository:React.FC<{}> = (props) => {
......@@ -52,6 +44,17 @@ const AddRepository:React.FC<{}> = (props) => {
const initValue = useInitValue(PublicApi.getWarehouseFreightSpaceDetails)
const [visibleChannelMember, setVisibleChannelMember] = useState(false)
const [visibleChannelRroduct, setVisibleChannelRroduct] = useState(false)
// 获取到的所有仓库, 用于选中后获得仓库库存
const repositRef = useRef<any>({})
const fetchWarehouseAll = async () => {
const { data } = await PublicApi.getWarehouseWarehouseAll()
repositRef.current = data
return data.map(v => ({
value: v.id,
label: v.name
}))
}
const [memberRowSelection, memberRowCtl] = useRowSelectionTable()
const [productRowSelection, productRowCtl] = useRowSelectionTable({type: 'radio'})
......@@ -236,6 +239,13 @@ const AddRepository:React.FC<{}> = (props) => {
}}
effects={() => {
useAsyncSelect('warehouseId', fetchWarehouseAll)
FormEffectHooks.onFieldValueChange$('warehouseId').subscribe(state => {
console.log(state.value)
addSchemaAction.setFieldState('inventory', targetState => {
const data = repositRef.current
targetState.props['x-component-props'].max = Array.isArray(data) ? data.find(v => v.id === state.value)?.principal : 200
})
})
}}
onSubmit={handleSubmit}
actions={addSchemaAction}
......
This diff is collapsed.
......@@ -149,7 +149,7 @@ const Repositories: React.FC<{}> = () => {
const handleAdjust = (record: any) => {
history.push(
`/memberCenter/commodityAbility/repositories/adjustRepository?id=${record.key}`,
`/memberCenter/commodityAbility/repositories/adjustRepository?id=${record.id}`,
);
};
......
This diff is collapsed.
......@@ -12,26 +12,34 @@ import { useStateFilterSearchLinkageEffect } from '@/formSchema/effects/useFilte
import { FORM_FILTER_PATH } from '@/formSchema/const';
import { billsSchema } from './schema';
import UploadModal from '@/components/UploadModal';
import { useAsyncSelect } from '@/formSchema/effects/useAsyncSelect';
import { PublicApi } from '@/services/api';
const formActions = createFormActions();
// 模拟请求
const fetchData = async (params: any) => {
const res = await PublicApi.getWarehouseInvoicesList(params);
return res.data;
};
const Bills: React.FC<{}> = () => {
const ref = useRef<any>({});
const [selectedRowKeys, setSelectedRowKeys] = useState<Array<string>>([]);
const [visibleModal, setVisibleModal] = useState(false);
const [moreVisible, setMoreVisible] = useState(false);
const [searchKey, setSearchKey] = useState({
invoicesNo: '',
invoicesType: '',
invoicesAbstract: '',
memberName: '',
inventoryId: '',
startTransactionTime: '',
endTransactionTime: '',
orderNo: '',
state: '',
});
// 获取单据类型
const fetchInvoicesType = async () => {
const { data } = await PublicApi.getWarehouseInvoicesTypeAll();
return data.map(v => ({ label: v.name, value: v.id }));
};
// 获取对应仓库
const fetchInventory = async () => {
const { data } = await PublicApi.getWarehouseWarehouseAll();
return data.map(v => ({ label: v.name, value: v.id }));
};
const menu = (
<Menu onClick={e => handleBatchDel(e)}>
......@@ -108,8 +116,8 @@ const Bills: React.FC<{}> = () => {
dataIndex: 'state',
key: 'state',
filters: [
{ text: 'Male', value: 'male' },
{ text: 'Female', value: 'female' },
{ text: '未审核', value: 1 },
{ text: '已审核', value: 2 },
],
filterMultiple: false,
render: (text: any, record: any) => (
......@@ -149,21 +157,6 @@ const Bills: React.FC<{}> = () => {
},
];
// 模拟请求
const fetchData = (params: any) => {
return new Promise((resolve, reject) => {
PublicApi.getWarehouseInvoicesList({
...searchKey,
current: params.current,
pageSize: params.pageSize,
}).then(res => {
resolve(res.data);
});
});
};
const handleAdd = () => {};
const rowSelection = {
selectedRowKeys: selectedRowKeys,
onChange: (selectedRowKeys: any, selectedRows: any) => {},
......@@ -213,9 +206,11 @@ const Bills: React.FC<{}> = () => {
useStateFilterSearchLinkageEffect(
$,
actions,
'search',
'invoicesNo',
FORM_FILTER_PATH,
);
useAsyncSelect('invoicesType', fetchInvoicesType);
useAsyncSelect('inventory', fetchInventory);
}}
schema={billsSchema}
/>
......@@ -223,7 +218,7 @@ const Bills: React.FC<{}> = () => {
/>
<UploadModal
visibleModal={visibleModal}
fileText="会员资料"
fileText="单据资料"
onCancel={() => setVisibleModal(false)}
/>
</Card>
......
......@@ -22,33 +22,28 @@ export const billsSchema: ISchema = {
children: '{{controllerBtns}}',
},
},
search: {
invoicesNo: {
type: 'string',
'x-component': 'Search',
'x-mega-props': {},
'x-component-props': {
placeholder: '请输入仓位名称',
placeholder: '搜索',
},
},
},
},
[FORM_FILTER_PATH]: {
type: 'object',
'x-component': 'mega-layout',
visible: false,
'x-component': 'flex-layout',
'x-component-props': {
grid: true,
full: true,
autoRow: true,
columns: 8,
rowStyle: {
flexWrap: 'nowrap',
},
colStyle: {
marginLeft: 20,
},
},
properties: {
invoicesNo: {
type: 'string',
'x-component-props': {
placeholder: '单据号',
},
},
invoicesAbstract: {
type: 'string',
'x-component-props': {
......@@ -81,19 +76,29 @@ export const billsSchema: ISchema = {
},
enum: [],
},
state: {
transactionTime: {
type: 'string',
'x-component-props': {
placeholder: '请选择单据状态',
placeholder: '请选择交易时间',
},
enum: [],
enum: [
{ label: '今天', value: 1 },
{ label: '一周内', value: 2 },
{ label: '一个月内', value: 3 },
{ label: '三个月内', value: 4 },
{ label: '六个月内', value: 5 },
{ label: '一年内', value: 6 },
{ label: '一年前', value: 7 },
],
},
time: {
type: 'string',
submit: {
'x-component': 'Submit',
'x-mega-props': {
span: 1,
},
'x-component-props': {
placeholder: '请选择交易时间',
children: '查询',
},
enum: [],
},
},
},
......
import React, { useState } from 'react'
import { history } from 'umi'
import { Form, Row, Col, Input, Button } from 'antd'
import {
UserOutlined,
......@@ -7,6 +8,7 @@ import {
} from '@ant-design/icons';
import styles from '../index.less'
import { PublicApi } from '@/services/api';
import { setAuth } from '@/utils/auth';
const LoginWrap: React.FC = () => {
const [validFrame, setValidFrame] = useState(false)
......@@ -15,7 +17,13 @@ const LoginWrap: React.FC = () => {
const finish = (value:any) => {
console.log(value)
PublicApi.postMemberLogin(value).then(res => {
console.log(res)
const { data } = res
setAuth({
memberId: data.memberId,
userId: data.userId,
token: data.token
})
history.push('/')
})
}
......
export interface AuthInfo {
userId: number,
memberId: number,
token: string
}
export const setAuth = (info: AuthInfo) => {
window.localStorage.setItem('auth', JSON.stringify(info))
}
export const getAuth = () => {
try {
return JSON.parse(window.localStorage.getItem('auth'))
} catch (error) {
return {}
}
}
......@@ -2,6 +2,7 @@ import { extend, ResponseError, OnionOptions, RequestOptionsInit, ResponseInterc
import responseCode from '@/constants/responseCode'
import { IRequestError, IRequestSuccess } from '..';
import { message } from 'antd'
import { getAuth } from './auth';
export type CtlType = 'none' | 'message'
// 根前缀请求路径
......@@ -49,7 +50,9 @@ const errorHandler = (error: ResponseError): IRequestError => {
}
const defaultHeaders = {
'Content-Type': 'Application/json'
'Content-Type': 'Application/json',
// 能力中心特定标识, 不可修改
source: '1'
}
/**
......@@ -64,10 +67,20 @@ const baseRequest = extend({
// 请求拦截器
baseRequest.interceptors.request.use((url: string, options: RequestOptionsInit): { url: string, options: RequestOptionsInit } => {
// 判断是否有权限
const loginAfterHeaders = getAuth()
const headers = {
...options.headers,
...loginAfterHeaders
}
return {
// 前缀如果已经带上api, 跳过自动补前缀
url: url.startsWith('/api') ? url : basePrefix + url,
options,
options: {
...options,
headers
},
};
});
......
......@@ -8,6 +8,7 @@ const tokens = [
'3a46198c5b97ac7147e5b07ad2dff5ac5c93c1afed47e1911961db87149e6ebf', // 商户会员管理服务
'efe99e20ed1375dc0db3e809e4fc7692f42ecebaf60cd77e65c50ed65d6ba6c4', // 商品服务
'7ec923520215c7e2f771867cb4d29cafbf823daf0fb2d3d9fa70b57a523c8bfb', // 店铺服务
'c789e0e56ee8a8cc2fbd85f930eb2928c58fc1014583c6643acf29cff954da49', // 支付服务
]
const genMap = (tokens) => {
return tokens.map(v => {
......
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