Commit 4db7a0b1 authored by cellee's avatar cellee

预览修复,支持多种格式在线预览,封装小区详情预览组件,修改合同预览,新增账号接口对接完成。

Signed-off-by: cellee's avatarcellee <893264950@qq.com>
parent bf32777f
import React, { useState, useEffect } from 'react';
import { connect } from 'umi';
import { Avatar, Modal, Button, Spin, Image, Pagination } from 'antd';
import {} from '@ant-design/icons';
import { RA } from '@/utils/method';
// icon
import IconPdf from '@/assets/icon-pdf.png';
import IconImg from '@/assets/icon-img.png';
import IconNone from '@/assets/logo_icon_bg.png';
import PDF from 'react-pdf-js';
const PreView = (props: any) => {
const module = 'CellList';
const { CellList, dispatch, OpenUrl, loading } = props;
const [Icon, setIcon] = useState(null as any); // 图标显示哪一个控制
const [ModalTip, setModalTip] = useState(false); // 弹窗控制
const [ModalInfo, setModalInfo] = useState(null as any); // 弹窗信息
//pdf 页码内容
const [numPages, setNumPages] = useState(1 as any); // 总页码数
const [pageNumber, setPageNumber] = useState(1); // 当前页码数
// 图标显示判断
useEffect(() => {
console.log(OpenUrl);
if (OpenUrl) {
OpenUrl.match(/\.([^\.]+)$/)[1].toLowerCase() == 'pdf' ? setIcon(IconPdf) : setIcon(IconImg);
} else {
setIcon(IconNone);
}
}, [OpenUrl]);
// 监听是否获取到地址以及发生改变
useEffect(() => {
if (CellList.imgUrl != null) {
let info = {
name: CellList.imgUrl.name,
type: CellList.imgUrl.name.match(/\.([^\.]+)$/)[1].toLowerCase(),
url: CellList.imgUrl.url,
};
setModalInfo(info);
// openDows(imgUrl.url, imgUrl.name);
}
}, [CellList.imgUrl]);
//点击预览
const opens = () => {
setModalTip(true);
let msg = {
// userToken: '',
// type: ans.substr(ans.lastIndexOf('.') + 1),
type: 'tosCommunityFileService',
fileName: OpenUrl,
extends: CellList.detailData.rows.residentialName,
};
RA(47, msg, module, dispatch);
};
// 隐藏弹窗
const handleCancel = () => {
setModalTip(false);
};
// pdf 总页数
const onDocumentLoadSuccess = (pages: any) => {
setNumPages(pages);
};
// 切换pdf 总数
const onChangePage = (page: any) => {
setPageNumber(page);
};
return (
<>
{/* 文件 Icon */}
<Avatar
shape="square"
style={{
color: '#f56a00',
backgroundColor: '#F2F6FC',
border: '1px solid #F2F6FC',
borderRadius: 4,
}}
size={80}
src={Icon}
/>
{/* 文件名 */}
<span style={{ display: 'inline-block', margin: '0 15px', minWidth: 200 }}>
{OpenUrl.length > 0 ? OpenUrl : 'No Attachment!'}
</span>
{/* 文件操作 */}
{OpenUrl.length > 0 ? (
<Button type="link" onClick={opens}>
Browse
</Button>
) : (
''
)}
{/* 文件弹窗 */}
<Modal
visible={ModalTip}
width={650}
style={{ textAlign: 'center' }}
footer={null}
title={OpenUrl}
onCancel={handleCancel}
>
<Spin spinning={loading}>
{ModalInfo != null && ModalInfo.type == 'pdf' ? (
<>
<PDF
file={ModalInfo ? ModalInfo.url : ''}
page={pageNumber}
onDocumentComplete={onDocumentLoadSuccess}
/>
<Pagination
style={{ textAlign: 'center', marginTop: 10, display: 'block' }}
simple
defaultCurrent={pageNumber}
total={numPages * 10}
onChange={onChangePage}
/>
</>
) : (
<Image
src={ModalInfo ? ModalInfo.url : ''}
preview={false}
style={{ margin: '0 auto', textAlign: 'center' }}
/>
)}
</Spin>
</Modal>
</>
);
};
function PreViewPorps(state: any) {
const { CellList } = state;
const loading = state.loading.models.CellList;
return {
CellList,
loading,
};
}
export default connect(PreViewPorps)(PreView);
......@@ -13,6 +13,8 @@ export default {
DataSave: null,
DataSaveDetail: null,
playload: {},
// 图片列表
FileImg: null,
},
reducers: {
......@@ -31,6 +33,11 @@ export default {
returnPath(state, { playload }) {
return { ...state, playload };
},
// 详情
FileListImg(state, { FileImg }) {
return { ...state, FileImg };
},
},
effects: {
......@@ -39,9 +46,6 @@ export default {
console.log('开始请求');
const resp = yield call(service.RA, playload);
console.log(resp);
if (resp.code == 500 || resp.error_code != '0000') {
// window.location.href = '/500';
}
if (resp.error_code != '0000') {
console.log('请求错误码:' + '(' + playload.index + ')' + resp.error_code);
console.log(playload);
......@@ -69,6 +73,12 @@ export default {
}, 1000);
}
break;
case 47:
{
var FileImg = resp.data;
yield put({ type: 'FileListImg', FileImg });
}
break;
}
}
},
......@@ -93,6 +103,12 @@ export default {
*Paths({ playload }, { put }) {
yield put({ type: 'returnPath', playload });
},
// 清掉图片列表
*FileImgs({ playload }, { put }) {
let FileImg = null;
yield put({ type: 'FileListImg', FileImg });
},
},
subscriptions: {
......
import React, { useState, useEffect } from 'react';
import { Form, Input, Button, Pagination, message } from 'antd';
import { Form, Input, Button, Pagination, message, Modal } from 'antd';
import { connect, history } from 'umi';
import { SearchOutlined, ClearOutlined, PlusOutlined } from '@ant-design/icons';
import ProTable from '@ant-design/pro-table';
......@@ -30,6 +30,8 @@ const Account = (props: any) => {
// 拉取数据的条件存储
const [term, setTerm] = useState({} as any);
// 关闭账号
const [over, setOver] = useState(false);
// 表单标识
const [form] = Form.useForm();
......@@ -43,7 +45,7 @@ const Account = (props: any) => {
},
{
title: 'Grade',
dataIndex: 'tosuserLevel',
dataIndex: 'tosUserLevel',
key: 'userLevel',
render: (text: any) => (
<span>
......@@ -60,12 +62,17 @@ const Account = (props: any) => {
title: 'Status',
dataIndex: 'userStatus',
key: 'userStatus',
render: (text: any) => <span>{text == 0 ? '启用' : '禁用'}</span>,
render: (text: any) => <span>{text != 1 ? '启用' : '禁用'}</span>,
},
{
title: 'Created By',
dataIndex: 'tosUserServiceCell',
key: 'createAccount',
width: 280,
ellipsis: true,
// render: (text: any) => {
// <span>{text == 0 ? '启用' : '禁用'}</span>
// },
},
{
title: 'Creation Time',
......@@ -113,7 +120,9 @@ const Account = (props: any) => {
};
// 停用账号
const lockS = (item: any) => {};
const lockS = (item: any) => {
setOver(true);
};
// 表头单搜索
const onFinishContract = (value: any) => {
......@@ -137,6 +146,14 @@ const Account = (props: any) => {
RA(51, t);
};
// 弹窗
const handleOk = (e: any) => {
setOver(false);
};
const handleCancel = (e: any) => {
setOver(false);
};
return (
<>
<div className="contop">
......@@ -214,6 +231,13 @@ const Account = (props: any) => {
/>
</div>
</div>
{/* 确认关闭账号 */}
<Modal title="Basic Modal" visible={over} onOk={handleOk} onCancel={handleCancel}>
<p>Some contents...</p>
<p>Some contents...</p>
<p>Some contents...</p>
</Modal>
</>
);
};
......
......@@ -20,24 +20,25 @@ import { AccountTip } from '@/utils/tip';
import SelectCommunity from '@/components/SelectCommunity';
import { RA } from '@/utils/method';
import moment from 'moment';
const Account = (props: any) => {
const module = 'Account';
const { dispatch, Data, DataSave, DataSaveDetail, Result, loading, CommunityList } = props;
const RA = (index: any, values: any) => {
dispatch({ type: 'Account/RA', playload: { index: index, body: values } });
};
// 拉取数据的条件存储
const [term, setTerm] = useState({} as any);
// 小区列表
const [comList, setCommunityList] = useState(CommunityList as any);
// 数据
useEffect(() => {
if (CommunityList != null) {
setCommunityList(CommunityList);
}
}, [CommunityList]);
// const [comList, setCommunityList] = useState(CommunityList as any);
// // 数据
// useEffect(() => {
// if (CommunityList != null) {
// setCommunityList(CommunityList);
// }
// }, [CommunityList]);
// 权限列表
const treeData = zhCnFaci;
......@@ -48,16 +49,34 @@ const Account = (props: any) => {
const [autoExpandParent, setAutoExpandParent] = useState<boolean>(true); // 树形菜单展开关闭
// 单选 二级还是三级管理员
const [values, setvalues] = useState(1); // 树形菜单展开关闭
const [values, setvalues] = useState(2); // 树形菜单展开关闭
// 表单标识
const [form] = Form.useForm();
const formRef = useRef(null);
// 表头单搜索
// 保存提交
const onFinishContract = (value: any) => {
value.tosUserServiceCellList = value.community.value; // 管辖小区
value.tosUserEmail = value.tosUserName; // 邮箱就是账号
value.tosUserLevel = values; //级别
value.creatorName = getCookie('name'); //新建者账号
value.creatorId = getCookie('id'); //新建者ID
delete value.community;
console.log(value);
console.log(value.community);
// 另传权限
let obj = {
userName: value.tosUserName,
userPassword: value.tosUserPwd,
permissionArray: checkedKeys.sort((n1, n2) => {
return parseInt(n1) - parseInt(n2);
}),
};
console.log(obj);
RA(38, value, module, dispatch); // 信息上传
RA(42, obj, module, dispatch); // 权限上传
};
//goToReturn
const goToReturn = () => {};
......@@ -124,17 +143,17 @@ const Account = (props: any) => {
>
<Descriptions column={{ xs: 1, sm: 2, md: 3 }}>
<Descriptions.Item>
<Form.Item name="basic" label="管理员姓名" rules={AccountTip[0]}>
<Form.Item name="tosAccountName" label="管理员姓名" rules={AccountTip[0]}>
<Input placeholder="6 Postcode" className="input" />
</Form.Item>
</Descriptions.Item>
<Descriptions.Item>
<Form.Item name="basic" label="联系方式">
<Form.Item name="tosUserPhone" label="联系方式">
<Input placeholder="6 Postcode" className="input" />
</Form.Item>
</Descriptions.Item>
<Descriptions.Item>
<Form.Item name="basic" label="所属公司">
<Form.Item name="tosUserToCompany" label="所属公司">
<Input placeholder="6 Postcode" className="input" />
</Form.Item>
</Descriptions.Item>
......@@ -142,12 +161,12 @@ const Account = (props: any) => {
<Descriptions column={{ xs: 1, sm: 2, md: 3 }}>
<Descriptions.Item>
<Form.Item name="basic" label="账号ID">
<Form.Item name="tosUserName" label="账号ID">
<Input placeholder="6 Postcode" className="input" />
</Form.Item>
</Descriptions.Item>
<Descriptions.Item>
<Form.Item name="basic" label="登录密码">
<Form.Item name="tosUserPwd" label="登录密码">
<Input placeholder="6 Postcode" className="input" />
</Form.Item>
</Descriptions.Item>
......@@ -163,12 +182,12 @@ const Account = (props: any) => {
</div>
<div className="label">
<Radio.Group defaultValue={values} onChange={onRadio}>
<Radio style={radioStyle} value={1}>
<Radio style={radioStyle} value={2}>
二级管理员
</Radio>
<Radio style={radioStyle} value={2}>
<Radio style={radioStyle} value={3}>
三级管理员
<Input placeholder="三级管理员" style={{ width: 160, marginLeft: 10 }} />
{/* <Input placeholder="三级管理员" style={{ width: 160, marginLeft: 10 }} /> */}
</Radio>
</Radio.Group>
</div>
......
......@@ -105,7 +105,7 @@ const Adds = (props: any) => {
} else {
console.log('新建');
}
}, [1]);
}, [Data]);
// 返回上传格式 和 地址
function backUpload(str: string, index: number) {
......@@ -120,7 +120,7 @@ const Adds = (props: any) => {
// 上传设置
const uploads = {
name: 'file',
accept: '.doc,.docx,.jpg,.png,.pdf',
accept: '.jpg,.png,.pdf',
action: '/tos/image/upload',
data: { imageType: 'tosCreateCommunity', extends: codename },
};
......
......@@ -16,6 +16,8 @@ import img from '@/assets/logo_icon_color.png';
import imgs from '@/assets/logo_icon_bg.png';
import hfor from '@/assets/h5.png';
import PreView from '@/components/PreView';
import moment from 'moment';
import Axios from 'axios';
......@@ -37,12 +39,13 @@ const Detail = (props: any) => {
}
}, [1]);
useEffect(() => {
if (imgUrl != null) {
// console.log('变化了'); 开始下载
openDows(imgUrl.url, imgUrl.name);
}
}, [imgUrl]);
// 下载
// useEffect(() => {
// if (imgUrl != null) {
// // console.log('变化了'); 开始下载
// openDows(imgUrl.url, imgUrl.name);
// }
// }, [imgUrl]);
useEffect(() => {
if (overCom != null) {
......@@ -211,53 +214,17 @@ const Detail = (props: any) => {
<div className="list-item">
<div className="item">
<label>Louba Timetable:</label>
<Avatar
shape="square"
style={{ color: '#f56a00', backgroundColor: '#F2F6FC' }}
size={64}
src={detailData.rows.balouscheduleUrl ? img : imgs}
/>
{detailData.rows.balouscheduleUrl ? (
<Button type="link" onClick={() => opens(detailData.rows.balouscheduleUrl)}>
Browse
</Button>
) : (
''
)}
<PreView OpenUrl={detailData.rows.balouscheduleUrl}></PreView>
</div>
{/* ---------- */}
<div className="item">
<label>Property Guide:</label>
<Avatar
shape="square"
style={{ color: '#f56a00', backgroundColor: '#F2F6FC' }}
size={64}
src={detailData.rows.serviceGuideUrl ? img : imgs}
/>
{detailData.rows.serviceGuideUrl ? (
<Button type="link" onClick={() => opens(detailData.rows.serviceGuideUrl)}>
Browse
</Button>
) : (
''
)}
<PreView OpenUrl={detailData.rows.serviceGuideUrl}></PreView>
</div>
{/* ---------- */}
<div className="item">
<label>Shelf Life Service:</label>
<Avatar
shape="square"
style={{ color: '#f56a00', backgroundColor: '#F2F6FC' }}
size={64}
src={detailData.rows.lifeServiceUrl ? img : imgs}
/>
{detailData.rows.lifeServiceUrl ? (
<Button type="link" onClick={() => opens(detailData.rows.lifeServiceUrl)}>
Browse
</Button>
) : (
''
)}
<PreView OpenUrl={detailData.rows.lifeServiceUrl}></PreView>
</div>
</div>
</div>
......
......@@ -86,7 +86,7 @@ li {
.item {
font-size: 14px;
line-height: 35px;
margin-bottom: 10px;
margin-bottom: 15px;
label {
display: inline-block;
min-width: 160px;
......
......@@ -92,7 +92,7 @@ const Facility = (props: any) => {
};
useEffect(() => {
if (sourceData != null) {
setExtend(DataSave.communityName);
setCtyName(DataSave.communityName);
setSoltTime(sourceData.reservationQuantumTime); // 设置时间
// 中断
......
......@@ -174,7 +174,7 @@ const VisitorRecord = (props: any) => {
onFinish={onSubmitForm}
>
<Form.Item name="inviterName">
<Input allowClear placeholder="Please input Visitor Name." style={{ width: 240 }} />
<Input allowClear placeholder="Please input Invitees Name." style={{ width: 240 }} />
</Form.Item>
<Form.Item name="showTime">
<DatePicker format="YYYY-MM-DD" placeholder="Select Date" />
......
......@@ -174,7 +174,7 @@ const VisitorRecord = (props: any) => {
onFinish={onSubmitForm}
>
<Form.Item name="inviterName">
<Input allowClear placeholder="Please input Visitor Name." style={{ width: 240 }} />
<Input allowClear placeholder="Please input Invitees Name." style={{ width: 240 }} />
</Form.Item>
<Form.Item name="showTime">
<DatePicker format="YYYY-MM-DD" placeholder="Select Date" />
......
......@@ -34,8 +34,6 @@ const Contract = (props: any) => {
// 跳转
const Jump = (record: any, Jump: String) => {
console.log(record);
console.log(Jump);
dispatch({
type: 'ContractModel/getMove',
payload: {
......@@ -43,6 +41,9 @@ const Contract = (props: any) => {
record: record,
},
});
// 清掉图片列表
dispatch({ type: 'Contract/FileImgs' });
// if(record == 0){
// history.push(location.pathname + '/' + Jump)
// }
......@@ -270,10 +271,9 @@ const Contract = (props: any) => {
};
const mapStateToProps = ({ ContractModel, loading }: { ContractModel: any; loading: Loading }) => {
console.log(loading);
return {
ContractModel,
userListLoading: loading.models.ContractModel,
userListLoading: loading.models.ContractModel || false,
};
};
......
import React, { useState, useEffect, useRef } from 'react';
import styles from './ContractContent.less';
import { Input, Form, message, Upload, Button, DatePicker, Space, Modal } from 'antd';
import {
Input,
Form,
message,
Upload,
Button,
DatePicker,
Pagination,
Modal,
Spin,
Image,
} from 'antd';
import { PlusOutlined, LeftOutlined } from '@ant-design/icons';
import { Link, useIntl, connect, Dispatch, Loading } from 'umi';
......@@ -18,8 +29,11 @@ import locale from 'antd/es/date-picker/locale/en_US';
import SearchOptionsCommnity from '@/components/SearchOptions/SearchOptionsCommnity';
import { tipList } from '@/utils/tip';
import FileViewer from 'react-file-viewer';
import PDF from 'react-pdf-js';
const ContractContent = (props: any) => {
const { ContractModel, dispatch } = props;
const { ContractModel, dispatch, FileImg, loading } = props;
const { RangePicker } = DatePicker; // 日期组件
const [form] = Form.useForm(); // 表单
const RA = (index: any, values: any) => {
......@@ -36,30 +50,29 @@ const ContractContent = (props: any) => {
const [comtyName, setComtyName] = useState(null); // 小区名字
const [tipTime, setTipTime] = useState(['previous month', 'two months'] as any); //提示时间
const [tipModal, settipModal] = useState(false); //附件弹窗
const [fileInfo, setfileInfo] = useState(null as any); //附件内容
//pdf 页码内容
const [numPages, setNumPages] = useState(1 as any); // 总页码数
const [pageNumber, setPageNumber] = useState(1); // 当前页码数
useEffect(() => {
// 如果是添加传来没有值的时候 就清空 否则 赋值给表单
if (ContractModel.record === -1) {
// 返回列表
console.log('没数据');
} else if (ContractModel.record === 0) {
form.resetFields();
} else {
// 发起请求
RA(47, ContractModel.record.contractFileName);
let arr = ContractModel.record.contractFileName.split(',');
let obj = new Array();
for (var i in arr) {
let a = {
uid: i,
name: arr[i],
status: 'done',
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.pdf',
let objData = {
type: 'tosContractPreview',
fileName: ContractModel.record.contractFileName,
extends: ContractModel.record.communityName,
};
obj.push(a);
}
RA(47, objData);
setFileList([...obj]);
// 打开禁止
setUploadUp(false);
// 提示时间
let a1 = moment(ContractModel.record.contractValidEndDate)
......@@ -70,6 +83,8 @@ const ContractContent = (props: any) => {
.format('YYYY-MM-DD');
setTipTime([a1, a2]);
// 给到上传绑定
setComtyName(ContractModel.record.communityName);
// 表单内容
form.setFieldsValue({
...ContractModel.record,
......@@ -79,7 +94,27 @@ const ContractContent = (props: any) => {
],
});
}
}, [1]);
}, [ContractModel]);
// 监听图片列表
useEffect(() => {
if (FileImg != null) {
let obj = new Array();
for (var i in FileImg) {
let a = {
uid: i,
name: FileImg[i].fileName,
status: 'done',
type: FileImg[i].fileName.match(/\.([^\.]+)$/)[1].toLowerCase(),
url: FileImg[i].fileUrl,
};
obj.push(a);
}
setFileList([...obj]);
} else {
setFileList([]);
}
}, [FileImg]);
// 返回
const goToReturn = () => {
......@@ -91,20 +126,18 @@ const ContractContent = (props: any) => {
const onFinish = (values: any) => {
values.contractValidStartDate = values.time[0].format('YYYY-MM-DD');
values.contractValidEndDate = values.time[1].format('YYYY-MM-DD');
let file = fileList;
let data = new Array();
for (let i = 0; i < file.length; i++) {
for (let i = 0; i < fileList.length; i++) {
data.push(fileList[i].name);
}
values.contractFileNameList = data;
if (ContractModel.record.id === undefined) {
// 添加
RA(27, values);
} else {
// 修改
message.error('数据接口对接中');
// 编辑
if (ContractModel.record.id != undefined) {
values.id = ContractModel.record.id;
}
RA(27, values);
// RA(27, values)
};
......@@ -148,16 +181,30 @@ const ContractContent = (props: any) => {
};
//点击预览
const onPreviews = () => {
console.log('预览');
const onPreviews = (file: any) => {
console.log(file);
setfileInfo(file); // 设置选择的文件
settipModal(true);
};
// 关闭预览
// 关闭预览弹窗
const handleCancel = () => {
settipModal(false);
};
// pdf 总页数
const onDocumentLoadSuccess = (pages: any) => {
setNumPages(pages);
};
// 切换pdf 总数
const onChangePage = (page: any) => {
setPageNumber(page);
};
return (
<>
<Spin spinning={loading}>
<div className={styles.base}>
{/* 头部组件 */}
<div className={styles.box}>
......@@ -301,7 +348,8 @@ const ContractContent = (props: any) => {
expire !
</p>
<p style={{ marginBottom: 0 }}>
<span style={{ color: 'red' }}>*</span> If the time has passed, it will not be sent
<span style={{ color: 'red' }}>*</span> If the time has passed, it will not be
sent
</p>
{/* <span style={{color:'#f00'}}>&nbsp;{stateTime}</span> */}
</div>
......@@ -309,7 +357,7 @@ const ContractContent = (props: any) => {
<div>
<LINE />
<Button type="primary" htmlType="submit">
<Button type="primary" htmlType="submit" loading={loading}>
Submit
</Button>
</div>
......@@ -320,22 +368,56 @@ const ContractContent = (props: any) => {
<Modal
title="File Preview"
visible={tipModal}
// centered={true}
width={800}
onOk={handleCancel}
onCancel={handleCancel}
style={{ textAlign: 'center' }}
footer={null}
>
<p>预览接口调整中。。。</p>
<p>预览接口调整中。。。</p>
<p>预览接口调整中。。。</p>
{fileInfo != null ? (
fileInfo.type == 'pdf' ? (
// pdf 换一种
<>
<PDF
file={fileInfo.url}
page={pageNumber}
onDocumentComplete={onDocumentLoadSuccess}
/>
<Pagination
style={{ textAlign: 'center', marginTop: 10, display: 'block' }}
simple
defaultCurrent={pageNumber}
total={numPages * 10}
onChange={onChangePage}
/>
</>
) : fileInfo.type == 'jpg' || fileInfo.type == 'png' ? (
// 图片用指定格式
<Image
src={fileInfo.url}
preview={false}
style={{ margin: '0 auto', textAlign: 'center' }}
/>
) : (
<FileViewer fileType={fileInfo.type} filePath={fileInfo.url} />
)
) : (
''
)}
</Modal>
</div>
</Spin>
</>
);
};
function mapStateToProps({ ContractModel }: { ContractModel: any }) {
function mapStateToProps(state: any) {
const { ContractModel, Contract } = state;
const FileImg = Contract.FileImg;
const loading = state.loading.models.Contract || false;
return {
ContractModel,
FileImg,
loading,
};
}
export default connect(mapStateToProps)(ContractContent);
......@@ -21,7 +21,6 @@ export const zhCnFaci = [
children: [
{ title: '查看物业费', key: '9' },
{ title: '编辑物业费', key: '10' },
{ title: '0-0-1-2', key: '11' },
],
},
],
......
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