Commit a776c57b authored by 前端-李俊鑫's avatar 前端-李俊鑫

内容管理-初步

parent b1a84a91
......@@ -132,4 +132,4 @@
"ssh2": "^0.8.9",
"util": "^0.12.3"
}
}
}
\ No newline at end of file
import React, { useState } from 'react';
import { PageHeaderWrapper } from "@ant-design/pro-layout";
import ReutrnEle from '@/components/ReturnEle';
import { usePageStatus } from '@/hooks/usePageStatus';
import { history, Prompt } from 'umi';
import { Card } from 'antd';
import { SchemaForm, createFormActions, FormButtonGroup, Submit, FormEffectHooks } from '@formily/antd'
import advertisementInfoSchema from './schema/advertisementInfoSchema';
import { Input, Select, Button } from 'antd';
// import CustomUpload from '@/components/NiceForm/components/CustomUpload';
import { PublicApi } from '@/services/api';
import { useInitialValues } from '../hooks/useInitialValues';
import CustomUpload from '../components/WrapCustomUpload';
import useCustomValidator from '../hooks/useValidator'
import { sortedList, ADVERTISE_WEB_COLUMN_TYPE, ADVERTISE_APP_COLUMN_TYPE} from '../utils/utils';
enum ChannelEnum {
WEB = 1,
APP = 2,
}
const WEB_COLUMN_TYPE = Object.keys(ADVERTISE_WEB_COLUMN_TYPE).map((item) => {
return {
label: ADVERTISE_WEB_COLUMN_TYPE[item],
value: parseInt(item)
}
})
const APP_COLUMN_TYPE = Object.keys(ADVERTISE_APP_COLUMN_TYPE).map((item) => {
return {
label: ADVERTISE_APP_COLUMN_TYPE[item],
value: parseInt(item)
}
})
const actions = createFormActions();
const { onFieldValueChange$ } = FormEffectHooks;
const AdvertisementInfo = () => {
useCustomValidator()
const { id, preview } = usePageStatus();
const initialValues = useInitialValues({id:id}, PublicApi.getManageContentAdvertGet);
const [submitLoading, setSubmitLoading ] = useState(false);
const [unsaved, setUnsaved] = useState(true);
const isEdit = id && !preview;
const isAdd = !id && !preview;
const isView = id && preview;
const handleSubmit = (value) => {
console.log(value)
// const { title, columnType, sort, link, imageUrl} = value;
const serviceActions = isAdd
? PublicApi.postManageContentAdvertAdd
: PublicApi.postManageContentAdvertUpdate
let tempData = value;
const postData = isAdd ? tempData : {...tempData, id};
setSubmitLoading(true);
setUnsaved(false);
serviceActions(postData).then((data) => {
setSubmitLoading(false);
if(data.code === 1000) {
history.push('/content/advertisement')
}
})
}
const formEffects = () => () => {
onFieldValueChange$('channel').subscribe(fieldState => {
const isActive = fieldState.active;
let options: {label: string, value: number| string}[] = []
if(fieldState.value === ChannelEnum.WEB) {
options = WEB_COLUMN_TYPE
} else {
options = APP_COLUMN_TYPE
}
actions.setFieldState('columnType', (state) => {
state.props["x-component-props"]["options"] = options;
if(isActive) {
state.value = "";
}
})
})
}
const handleCancel = () => {
history.push('/content/advertisement')
}
return (
<PageHeaderWrapper
onBack={() => history.goBack()}
backIcon={<ReutrnEle description="返回" />}
title={isAdd ? '新建广告' : isEdit ? '编辑广告' : '查看广告'}
>
<Card>
<SchemaForm
schema={advertisementInfoSchema}
actions={actions}
components={{
Input, Select, Submit, CustomUpload
}}
initialValues={initialValues?.data}
onSubmit={handleSubmit}
editable={isAdd || isEdit}
effects={formEffects()}
expressionScope={{
label: (
<div>
{
isAdd || isEdit
? <span style={{color: '#ff4d4f'}}>* </span>
: null
}
广告图片
</div>
)
}}
>
{
isAdd || isEdit
? (
<FormButtonGroup offset={3}>
<Submit loading={submitLoading}>提交</Submit>
<Button onClick={handleCancel}>取消</Button>
</FormButtonGroup>
)
: <></>
}
</SchemaForm>
<Prompt when={unsaved && (isAdd || isEdit)} message="内容未保存,确定离开?"></Prompt>
</Card>
</PageHeaderWrapper>
)
}
export default AdvertisementInfo
import React, { useEffect, useState } from 'react';
import { FilterTable } from '../components/FilterTable';
import { Card, Input, Button, Table, Dropdown, Menu, Select, Space, Popconfirm, Modal } from 'antd';
import { createVirtualBox, createFormActions, FormEffectHooks, createEffectHook } from '@formily/antd';
import { history, Link } from 'umi';
import { DownOutlined, UpOutlined } from '@ant-design/icons';
import { timeRange } from '@/utils/index';
import { PublicApi } from '@/services/api';
import TablePagination from '../components/TablePagination';
import { tagColorStyle, getTableDataSource, setFormStatus } from '../utils/utils';
import { merge } from 'rxjs';
import CustomSearch from '../components/CustomSearch';
const { onFormInit$, onFieldValueChange$ } = FormEffectHooks
const { Search } = Input;
const SchemaButton = createVirtualBox('button', Button);
const SchemaTable = createVirtualBox('SchemaTable', Table);
const SchemaDropDown = createVirtualBox('SchemaDropDown', Dropdown.Button);
const actions = createFormActions();
import advertisementSchema from './schema';
const getData = async (params: any) => {
const res = await PublicApi.getManageContentAdvertPage(params);
return res.data
}
const Advertisement = () => {
useEffect(() => {
const params = {
current: 1,
pageSize: 10
}
getTableDataSource(actions, params, getData);
}, [])
const advertisementEffects = () => () => {
onFormInit$().subscribe(() => {
actions.setFieldState('FILTERS', state => {
state.visible = false;
})
})
merge(
onFieldValueChange$('status'),
onFieldValueChange$('time'),
).subscribe(
fieldState => {
if(fieldState.value != null) {
handleSearch({})
}
}
)
onFieldValueChange$('pagination').subscribe((state) => {
handleSearch({...state.value})
})
}
const handleSearch = async (params) => {
const title = actions.getFieldValue('search');
const status = actions.getFieldValue('status'); // 状态
const time = actions.getFieldValue('time');
const { st, et } = timeRange(time);
const postData = {
title: title || '',
status: status != 0 ? status : '',
startTime: st || null,
endTime: et || null,
current: 1,
pageSize: 10,
...params,
}
getTableDataSource(actions, postData, getData);
}
const handleDelete = (id) => {
Modal.confirm({
title: '确定要执行这个操作吗',
onOk: () => {
PublicApi.postManageContentAdvertDelete({id: id})
.then((data) => {
const paginationValue = actions.getFieldValue('pagination');
handleSearch({...paginationValue})
})
},
})
}
// 修改状态
const handleUpdateStatus = (id, status) => {
// 该方法是上下架 所以 enableStatus 无用,随意传
PublicApi.postManageContentAdvertUpdateStatus({id: id, shelfStatus: status, enableStatus: 0})
.then((data) => {
const paginationValue = actions.getFieldValue('pagination');
handleSearch({...paginationValue})
});
}
// 重设页码
const resetPagination = (params) => {
const paginationValue = actions.getFieldValue('pagination');
actions.setFieldValue('pagination', {
...paginationValue,
...params
})
}
return (
<Card>
<FilterTable
schema={advertisementSchema}
components={{
CustomSearch,
// SchemaFlexRowLayout,
// SchemaFlexColumnLayout,
SchemaDropDown,
Select,
Table,
TablePagination
}}
actions={actions}
expressionScope={{
goToCreate: () => {
history.push(`/content/advertisement/add`)
},
reset: () => {
actions.setFieldValue('search');
actions.setFieldValue('status'); // 状态
actions.setFieldValue('time');
resetPagination({current: 1})
handleSearch({});
},
search: (value) => {
resetPagination({current: 1})
handleSearch({title: value, current: 1 });
},
renderStatus: (text, record) => {
const STATUSMAP = {
"1": "待上架",
"2": "已上架",
"3": "已下架"
}
return (
<span style={{...tagColorStyle[record.status], padding: '3px 5px'}}>
{STATUSMAP[record.status]}
</span>
)
},
toggleFilters: () => {
actions.setFieldState('FILTERS', state => {
const visible = !state.visible;
state.visible = visible;
actions.setFieldState('HIGHT_FILTER_BTN', (state) => {
//@ts-ignore
state.props['x-component-props'].children = (
<div>高级搜索 {visible ? <UpOutlined /> : <DownOutlined /> }</div>
)
})
});
},
renderOperation: (val, record) => {
const status = ["", "上架", "下架", "上架"];
const canModify = [1, 3]
const menu = (
<Menu>
<Menu.Item>
<Link to={`/content/advertisement/detail?id=${record.id}`}>
编辑
</Link>
</Menu.Item>
<Menu.Item onClick={() => handleDelete(record.id)}>
<a>
删除
</a>
</Menu.Item>
</Menu>
)
return (
<Space>
{/* 这里反向操作, 上架的对应的是下架, 待上架,下架对应的是上架 */}
<Popconfirm
title="确定要执行这个操作吗"
onConfirm={() => handleUpdateStatus(record.id, status[record.status] == '上架' ? 2 : 3)}
okText="是"
cancelText="否"
>
<a href="#">{status[record.status]}</a>
</Popconfirm>
{/* // 只有待上架, 已下架架才有 修改和删除 */}
{
canModify.includes(record.status)
? (
<Dropdown overlay={menu}>
<a>
更多 <DownOutlined />
</a>
</Dropdown>
)
: null
}
</Space>
)
},
}}
effects={advertisementEffects()}
>
</FilterTable>
</Card>
)
}
export default Advertisement;
import { sortedList, } from '../../utils/utils';
const sortListOptions = sortedList(1, 6);
const schema = {
type: 'object',
properties: {
layout: {
type: 'object',
'x-component': 'mega-layout',
'x-component-props': {
labelCol: 3,
wrapperCol: 10,
labelAlign: 'left'
},
properties: {
title: {
type: 'string',
title: '标题',
'x-component': 'Input',
'x-component-props': {
placeholder: '最长60个字符,30个汉字'
},
"x-rules": [
{
"required": true,
"message": "最长60个字符,30个汉字"
},
{
limitByte: true, // 自定义校验规则
maxByte: 60,
}
]
},
channel: {
title: '投放渠道',
type: 'string',
"x-component": 'Select',
'x-component-props': {
options: [
{ label: 'Web', value: 1 },
{ label: 'App', value: 2 },
],
},
"x-rules": [{
"required": true,
"message": "请选择投放渠道"
}],
},
columnType: {
title: '栏目',
type: 'string',
'x-component': 'Select',
'x-component-props': {
options: []
},
"x-rules": [{
"required": true,
"message": "请选择栏目"
}],
},
sort: {
title: '广告排序',
type: 'string',
'x-component': 'Select',
'x-component-props': {
options: sortListOptions
},
"x-rules": [{
"required": true,
"message": "请选择广告排序"
}],
},
link: {
title: '跳转链接',
type: 'string',
'x-component': 'Input',
"x-rules": [
{
limitByte: true, // 自定义校验规则
maxByte: 100,
}
]
},
imageUrl: {
type: "object",
title: "{{label}}",
name: "imageUrl",
"x-component": "CustomUpload",
"x-component-props": {
size: '无',
fileMaxSize: 300,
},
"x-rules": {
"required": true,
"message": "请上传图片"
},
},
}
}
}
}
export default schema
import EyePreview from '@/components/EyePreview';
import { DownOutlined } from '@ant-design/icons';
import { TimeList } from '../../statusList';
import moment from 'moment';
import React from 'react';
import {ADVERTISE_WEB_COLUMN_TYPE, ADVERTISE_APP_COLUMN_TYPE} from '../../utils/utils';
const ALL_TYPE = Object.assign({}, ADVERTISE_WEB_COLUMN_TYPE, ADVERTISE_APP_COLUMN_TYPE);
const CustomTimeList = [{label: '全部', value: 0}].concat(TimeList.slice(1));
const columns = [
{title: 'ID', dataIndex: 'id'},
{ title: '标题',
dataIndex: 'title',
render: (text: string, record: any) => (
<EyePreview
url={`/content/advertisement/detail?id=${record.id}&preview=1`}
>
{text}
</EyePreview>
)
},
{
title: '栏目', dataIndex: 'columnType',
render: (text, record) => {
return (
<div>{ALL_TYPE[text]}</div>
)
},
},
{
title: '发布时间',
dataIndex: 'createTime',
render: (text) => (
moment(text).format('YYYY-MM-DD HH:mm:ss')
)
},
{title: '状态', dataIndex: 'status', render: "{{renderStatus}}"},
{title: '操作', render: "{{renderOperation}}"}
];
/**
* 广告管理列表也 schemat
*/
const advertisementSchema = {
type: 'object',
properties: {
layout: {
type: 'object',
// 'x-component': 'mega-layout',
'x-component': 'CustomFlexRowLayout',
'x-component-props': {
justify: 'space-between',
align: 'center'
},
properties: {
'left-layout': {
type: 'object',
name: 'left-layout',
'x-component': 'CustomFlexRowLayout',
'x-component-props': {
justify: 'start',
align: 'center'
},
properties: {
createBtn: {
type: "object",
name: "createBtn",
"x-component": "button",
"x-component-props": {
"onClick": "{{goToCreate}}",
"children": "新建",
"type": 'primary',
style: {
width: '112px',
margin: '0 0 15px 0'
}
}
},
}
},
'right-layout': {
type: 'object',
name: 'rigth-layout',
"x-component": 'CustomFlexColumnLayout',
properties: {
controllers: {
type: 'object',
name: 'controllers',
'x-component': 'CustomFlexRowLayout',
'x-component-props': {
justify: 'end',
},
properties: {
search: {
type: 'string',
name: 'name',
'x-component': 'CustomSearch',
'x-component-props': {
placeholder: "请填写标题名称",
"onSearch": "{{search}}",
}
},
'HIGHT_FILTER_BTN': {
type: 'string',
name: 'HIGHT_FILTER_BTN',
'x-component': 'button',
'x-component-props': {
"children": (
<div>高级搜索 <DownOutlined /></div>
),
"onClick": "{{toggleFilters}}",
style: {
margin: '0 15px'
}
}
},
reset: {
type: 'string',
name: 'reset',
"x-component": "button",
"x-component-props": {
"onClick": "{{reset}}",
"children": "重置",
}
},
}
},
'FILTERS': {
type: 'object',
name: 'FILTERS',
'x-component': 'CustomFlexRowLayout',
'x-component-props': {
justify: 'end'
},
properties: {
status: {
name: 'status',
type: 'string',
'x-component': 'Select',
'x-component-props': {
options: [
{label: '状态(全部)', value: '0'},
{label: '待上架', value: '1'},
{label: '已上架',value: '2'},
{label: '已下架',value: '3'},
],
defaultValue: '0',
placeholder: '请选择状态',
style: {
width: '160px',
margin: '0 15px'
}
}
},
time: {
name: 'time',
type: 'string',
'x-component': 'Select',
'x-component-props': {
placeholder: '发布时间(全部)',
options: CustomTimeList,
style: {
width: '160px',
}
}
}
}
}
}
}
}
},
"table": {
"key": "table",
"type": "object",
"name": "table",
"x-component": "Table",
"x-component-props": {
"columns": columns,
"rowKey": "id",
pagination: false,
}
},
pagination: {
type: 'object',
'x-component': "TablePagination",
'x-style': {
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-end'
},
'x-component-props': {
showQuickJumper: true,
pageSize: 10,
size: 'small'
}
}
}
}
export default advertisementSchema
import React, { useState, useEffect } from 'react';
import { PageHeaderWrapper } from "@ant-design/pro-layout";
import ReutrnEle from '@/components/ReturnEle';
import { usePageStatus } from '@/hooks/usePageStatus';
import { history, Prompt } from 'umi';
import { Card, Button } from 'antd';
import { SchemaForm, createFormActions, FormButtonGroup, Submit } from '@formily/antd'
import announceInfoSchema from './schema/announceInfoSchema';
import { Input, Select } from 'antd';
import CustomUpload from '@/components/NiceForm/components/CustomUpload';
import CustomEditor from '../components/CustomEditor';
import { PublicApi } from '@/services/api';
import { useInitialValues } from '../hooks/useInitialValues';
import CustomCheckbox from '../components/CustomCheckbox';
import BraftEditor from 'braft-editor';
import { setFormStatus } from '../utils/utils';
import useCustomValidator from '../hooks/useValidator'
const actions = createFormActions();
const AdvertisementInfo = () => {
useCustomValidator();
const { id, preview } = usePageStatus();
const initialValues: any = useInitialValues({id:id}, PublicApi.getManageContentNoticeGet);
const [submitLoading, setSubmitLoading ] = useState(false);
const [unsaved, setUnsaved] = useState(true);
const isEdit = id && !preview;
const isAdd = !id && !preview;
const isView = id && preview;
useEffect(() => {
const data = initialValues?.data || {}
const content = data?.content;
if(content) {
const editorState = BraftEditor.createEditorState(content);
actions.setFieldValue('layout.contentLayout.content', editorState);
}
setFormStatus(actions, 'layout.contentLayout.content', 'readOnly', isView);
}, [initialValues])
const handleSubmit = (value) => {
console.log(value)
const {content, top, ...rest} = value;
const editorContent = content.toHTML();
// const { title, columnType, sort, link, imageUrl} = value;
const serviceActions = isAdd
? PublicApi.postManageContentNoticeAdd
: PublicApi.postManageContentNoticeUpdate
let tempData = {...rest, content: editorContent, top: top ? 1 : 0};
const postData = isAdd ? tempData : {...tempData, id};
setSubmitLoading(true)
setUnsaved(false)
serviceActions(postData).then((data) => {
setSubmitLoading(false);
if(data.code === 1000) {
history.push('/content/announcements')
}
})
}
const handleCancel = () => {
history.push('/content/announcements')
}
return (
<PageHeaderWrapper
onBack={() => history.goBack()}
backIcon={<ReutrnEle description="返回" />}
title={isAdd ? '新建公告' : isEdit ? '编辑公告' : '查看公告'}
>
<Card>
<SchemaForm
schema={announceInfoSchema}
actions={actions}
components={{
Input, Select, Submit, CustomUpload, CustomEditor, CustomCheckbox
}}
initialValues={initialValues?.data}
onSubmit={handleSubmit}
editable={isAdd || isEdit}
expressionScope={{
label: (
<div>
{
isAdd || isEdit
? <span style={{color: '#ff4d4f'}}>* </span>
: null
}
栏目
</div>
)
}}
>
{
isAdd || isEdit
? (
<FormButtonGroup offset={3}>
<Submit loading={submitLoading}>提交</Submit>
<Button onClick={handleCancel}>取消</Button>
</FormButtonGroup>
)
: <></>
}
</SchemaForm>
<Prompt when={unsaved && (isAdd || isEdit)} message="您还有未保存的内容,是否确定要离开?"></Prompt>
</Card>
</PageHeaderWrapper>
)
}
export default AdvertisementInfo
import React, { useEffect, useState } from 'react';
import { FilterTable, SchemaFlexRowLayout, SchemaFlexColumnLayout } from '../components/FilterTable';
import { Card, Input, Button, Table, Dropdown, Menu, Select, Space, Popconfirm, Modal } from 'antd';
import { createVirtualBox, createFormActions, FormEffectHooks, createEffectHook } from '@formily/antd';
import { history, Link } from 'umi';
import { DownOutlined, DeleteOutlined, UpOutlined } from '@ant-design/icons';
import { timeRange } from '@/utils/index';
import { PublicApi } from '@/services/api';
import { tagColorStyle, getTableDataSource } from '../utils/utils';
import { merge } from 'rxjs'
import TablePagination from '../components/TablePagination';
import CustomSearch from '../components/CustomSearch';
const { onFormInit$, onFieldValueChange$, onFieldChange$ } = FormEffectHooks
const { Search } = Input;
const SchemaButton = createVirtualBox('button', Button);
const SchemaTable = createVirtualBox('SchemaTable', Table);
const SchemaDropDown = createVirtualBox('SchemaDropDown', Dropdown.Button);
const actions = createFormActions();
import advertisementSchema from './schema';
const getData = async (params: any) => {
const res = await PublicApi.getManageContentNoticePage(params);
return res.data
}
const Announcements = () => {
useEffect(() => {
const params = {
current: 1,
pageSize: 10
}
getTableDataSource(actions, params, getData);
}, [])
const announcementEffects = () => () => {
onFormInit$().subscribe(() => {
actions.setFieldState('FILTERS', state => {
state.visible = false;
})
})
merge(
onFieldValueChange$('columnType'),
onFieldValueChange$('status'),
onFieldValueChange$('time'),
).subscribe(
fieldState => {
if(fieldState.active && fieldState.value != null) {
handleSearch({})
}
}
)
// 页码发生改变
onFieldValueChange$('pagination').subscribe((state) => {
handleSearch({...state.value})
})
}
const handleSearch = async (params) => {
const title = actions.getFieldValue('search');
const columnType = actions.getFieldValue('columnType');
const status = actions.getFieldValue('status'); // 状态
const time = actions.getFieldValue('time');
const { st, et } = timeRange(time);
const postData = {
title: title || '',
columnType: columnType != 0 ? columnType : '',
status: status != 0 ? status : '',
startTime: st,
endTime: et,
current: 1,
pageSize: 10,
...params
}
getTableDataSource(actions, postData, getData);
}
const handleDelete = (id) => {
Modal.confirm({
title: '确定要执行这个操作?',
onOk: () => {
PublicApi.postManageContentNoticeDelete({id: id})
.then((data) => {
const paginationValue = actions.getFieldValue('pagination');
handleSearch({...paginationValue})
})
}
})
}
// 修改状态
const handleUpdateStatus = (id, status) => {
// 该方法是上下架 所以 enableStatus 无用,随意传
PublicApi.postManageContentNoticeUpdateStatus({id: id, shelfStatus: status, enableStatus: 0})
.then((data) => {
const paginationValue = actions.getFieldValue('pagination');
handleSearch({...paginationValue})
});
}
// 重设页码
const resetPagination = (params) => {
const paginationValue = actions.getFieldValue('pagination');
actions.setFieldValue('pagination', {
...paginationValue,
...params
})
}
return (
<Card>
<FilterTable
schema={advertisementSchema}
components={{
CustomSearch,
// SchemaFlexRowLayout,
// SchemaFlexColumnLayout,
SchemaDropDown,
Select,
Table,
TablePagination
}}
actions={actions}
expressionScope={{
goToCreate: () => {
history.push(`/content/announcements/add`)
},
reset: () => {
actions.setFieldValue('search');
actions.setFieldValue('columnType');
actions.setFieldValue('status'); // 状态
actions.setFieldValue('time');
resetPagination({current: 1})
handleSearch({current: 1})
},
search: (value) => {
resetPagination({current: 1})
handleSearch({title: value, current: 1 });
},
// eslint-disable-next-line react/display-name
renderStatus: (text, record) => {
const STATUSMAP = {
"1": "待上架",
"2": "已上架",
"3": "已下架"
}
return (
<span style={{...tagColorStyle[record.status], padding: '3px 5px'}}>
{STATUSMAP[record.status]}
</span>
)
},
toggleFilters: () => {
actions.setFieldState('FILTERS', state => {
const visible = !state.visible;
state.visible = visible;
actions.setFieldState('HIGHT_FILTER_BTN', (state) => {
//@ts-ignore
state.props['x-component-props'].children = (
<div>高级搜索 {visible ? <UpOutlined /> : <DownOutlined /> }</div>
)
})
});
},
// eslint-disable-next-line react/display-name
renderOperation: (val, record) => {
const status = ["", "上架", "下架", "上架"];
const canModify = [1, 3]
const menu = (
<Menu>
<Menu.Item>
<Link to={`/content/announcements/detail?id=${record.id}`}>
编辑
</Link>
</Menu.Item>
<Menu.Item onClick={() => handleDelete(record.id)}>
<a>
删除
</a>
</Menu.Item>
</Menu>
)
return (
<Space>
{/* 这里反向操作, 上架的对应的是下架, 待上架,下架对应的是上架 */}
<Popconfirm
title="确定要执行这个操作吗"
onConfirm={() => handleUpdateStatus(record.id, status[record.status] == '上架' ? 2 : 3)}
okText="是"
cancelText="否"
>
<a href="#">{status[record.status]}</a>
</Popconfirm>
{/* // 只有待上架, 已下架架才有 修改和删除 */}
{
canModify.includes(record.status)
? (
<Dropdown overlay={menu}>
<a>
更多 <DownOutlined />
</a>
</Dropdown>
)
: null
}
</Space>
)
},
}}
effects={announcementEffects()}
>
</FilterTable>
</Card>
)
}
export default Announcements;
import { ANNOUNCE_COLUMN_TYPE, transfer2Options } from '../../utils/utils';
const columnsList = transfer2Options(ANNOUNCE_COLUMN_TYPE);
const schema = {
type: 'object',
properties: {
layout: {
type: 'object',
'x-component': 'mega-layout',
'x-component-props': {
labelCol: 3,
wrapperCol: 10,
labelAlign: 'left'
},
properties: {
title: {
type: 'string',
title: '标题',
'x-component': 'Input',
'x-component-props': {
placeholder: '最长60个字符,30个汉字'
},
"x-rules": [
{
"required": true,
"message": "最长60个字符,30个汉字"
},
{
limitByte: true, // 自定义校验规则
maxByte: 60,
}
]
},
columnTypeLayout: {
type: 'object',
'x-component': 'mega-layout',
"x-component-props": {
"label": "{{label}}",
wrapperCol: 24, //
grid: true,
columns: 6,
autoRow: false,
layoutProps: {
"wrapperCol": 16,
},
style: {
marginBottom: 0
},
},
properties: {
columnType: {
name: 'columnType',
type: 'string',
'x-component': 'Select',
'x-mega-props': {
span: 2
},
'x-component-props': {
style: {
width: '95%'
},
options: columnsList,
},
"x-rules": [{
"required": true,
"message": "请选择栏目"
}],
},
top: {
name: 'top',
type: 'string',
'x-component': 'CustomCheckbox',
'x-mega-props': {
span: 1
},
'x-component-props': {
children: '置顶',
}
}
}
},
contentLayout: {
'x-component': 'mega-layout',
"x-component-props": {
layoutProps: {
"wrapperCol": 21,
},
wrapperCol: 23,
},
properties: {
content: {
type: "string",
name: 'content',
title: '内容',
"x-component": 'CustomEditor',
"x-component-parent-props": {
style: {
border: '1px solid #DCDFE6'
}
},
"x-rules": {
"required": true,
"message": "请输入内容"
},
"x-component-props": {
contentStyle: {
height: 256,
},
// onChange: "{{editorChange}}",
excludeControls: [
'letter-spacing',
'line-height',
'clear',
'headings',
'list-ol',
'list-ul',
'remove-styles',
'superscript',
'subscript',
'hr',
],
media: {
// 如果要允许上传视频的话,需要重写uploadFn, https://www.yuque.com/braft-editor/be/gz44tn
accepts: {
video: false,
audio: false,
}
}
},
}
}
}
}
}
}
}
export default schema
import EyePreview from '@/components/EyePreview';
import { DownOutlined } from '@ant-design/icons';
import { TimeList } from '../../statusList';
import moment from 'moment';
import React from 'react';
import { ANNOUNCE_COLUMN_TYPE, transfer2Options } from '../../utils/utils';
const ALL = [{label: '栏目(全部)', value: 0}]
const COLUMNSOPTIONS = ALL.concat(transfer2Options(ANNOUNCE_COLUMN_TYPE));
const columns = [
{title: 'ID', dataIndex: 'id'},
{
title: '栏目', dataIndex: 'columnType',
render: (text, record) => {
return (
<div>{ANNOUNCE_COLUMN_TYPE[text]}</div>
)
}
},
{ title: '标题',
dataIndex: 'title',
render: (text: string, record: any) => (
<EyePreview
url={`/content/announcements/detail?id=${record.id}&preview=1`}
>
{text}
</EyePreview>
)
},
{
title: '发布时间',
dataIndex: 'createTime',
render: (text) => (
moment(text).format('YYYY-MM-DD HH:mm:ss')
)
},
{title: '状态', dataIndex: 'status', render: "{{renderStatus}}"},
{title: '操作', render: "{{renderOperation}}"}
];
/**
* 公告管理列表也 schemat
*/
const announcementSchema = {
type: 'object',
properties: {
layout: {
type: 'object',
// 'x-component': 'mega-layout',
'x-component': 'CustomFlexRowLayout',
'x-component-props': {
justify: 'space-between',
align: 'center'
},
properties: {
'left-layout': {
type: 'object',
name: 'left-layout',
'x-component': 'CustomFlexRowLayout',
'x-component-props': {
justify: 'start',
align: 'center'
},
properties: {
createBtn: {
type: "object",
name: "createBtn",
"x-component": "button",
"x-component-props": {
"onClick": "{{goToCreate}}",
"children": "新建",
"type": 'primary',
style: {
width: '112px',
margin: '0 0 15px 0'
}
}
},
}
},
'right-layout': {
type: 'object',
name: 'rigth-layout',
"x-component": 'CustomFlexColumnLayout',
properties: {
controllers: {
type: 'object',
name: 'controllers',
'x-component': 'CustomFlexRowLayout',
'x-component-props': {
justify: 'end',
},
properties: {
search: {
type: 'string',
name: 'name',
'x-component': 'CustomSearch',
'x-component-props': {
placeholder: "请填写标题名称",
"onSearch": "{{search}}",
}
},
'HIGHT_FILTER_BTN': {
type: 'string',
name: 'HIGHT_FILTER_BTN',
'x-component': 'button',
'x-component-props': {
"children": (
<div>高级搜索 <DownOutlined /></div>
),
"onClick": "{{toggleFilters}}",
style: {
margin: '0 15px'
}
}
},
reset: {
type: 'string',
name: 'reset',
"x-component": "button",
"x-component-props": {
"onClick": "{{reset}}",
"children": "重置",
}
},
}
},
'FILTERS': {
type: 'object',
name: 'FILTERS',
'x-component': 'CustomFlexRowLayout',
'x-component-props': {
justify: 'end'
},
properties: {
columnType: {
type: 'string',
'x-component': 'Select',
'x-component-props': {
style: {
width: '160px'
},
options: COLUMNSOPTIONS,
defaultValue: 0,
}
},
status: {
name: 'status',
type: 'string',
'x-component': 'Select',
'x-component-props': {
options: [
{label: '状态(全部)', value: '0'},
{label: '待上架', value: '1'},
{label: '已上架',value: '2'},
{label: '已下架',value: '3'},
],
defaultValue: '0',
placeholder: '请选择状态',
style: {
width: '160px',
margin: '0 15px'
}
}
},
time: {
name: 'time',
type: 'string',
'x-component': 'Select',
'x-component-props': {
placeholder: '发布时间(全部)',
options: TimeList,
style: {
width: '160px',
}
}
}
}
}
}
}
}
},
"table": {
"key": "table",
"type": "object",
"name": "table",
"x-component": "Table",
"x-component-props": {
"columns": columns,
"rowKey": "id",
pagination: false,
// "pagination": {
// showQuickJumper: true,
// size: "small",
// "onChange": "{{paginationChange}}",
// },
// "rowSelection": "{{rowSelection}}"
}
},
pagination: {
type: 'object',
'x-component': "TablePagination",
'x-style': {
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-end'
},
'x-component-props': {
showQuickJumper: true,
pageSize: 10,
size: 'small'
}
}
}
}
export default announcementSchema
.page {
display: flex;
flex-direction: row;
justify-content: space-between;
height: 100%;
overflow: hidden;
.tree {
min-width: 488px;
height: 100%;
margin-right: 16px;
}
.editPanel {
flex: 1;
background-color: #fff;
padding: 16px;
border-radius: 8px;
}
}
import React, { useState, useEffect, useMemo } from 'react'
import { Row, Col, Popconfirm, Button, Card, Tooltip } from 'antd';
import Children from '@/components/NiceForm/components/Children';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import TabTree, { useTreeActions, createTreeActions } from '@/components/TabTree';
import SchemaForm, { createFormActions, LifeCycleTypes, FormEffectHooks, FormButtonGroup } from '@formily/antd';
import { Checkbox } from '@formily/antd-components'
import { classSchema } from './schema'
import { PublicApi } from '@/services/api';
import { useTreeTabs } from '@/hooks/useTreeTabs';
import NiceForm from '@/components/NiceForm';
import { GetManageContentCategoryAllResponse } from '@/services/PlatformApi/id8545';
import styles from './index.less';
import { BorderOuterOutlined, QuestionCircleOutlined } from '@ant-design/icons';
const { ON_FORM_INPUT_CHANGE } = LifeCycleTypes
enum FormState {
FREE, // 空闲状态
EDIT, // 编辑状态
ADD, // 新增状态
}
type TreeDataType = {
id: number,
title: string,
children: TreeDataType[],
key: string,
level: number,
parentId: number,
}
/**
* 递归改变数据结构
* @param treeData
* @param level
* @returns
*/
const transferTreeData = (treeData: GetManageContentCategoryAllResponse, level: string, checkedKeys): TreeDataType[] => {
let res: TreeDataType[] = [];
for(let i = 0; i < treeData.length; i++) {
const item = treeData[i];
const key = level + item.id
let result: TreeDataType = {
id: item.id,
title: item.name,
key: key,
level: item.level,
parentId: item.parentId,
children: [],
}
if(item.status) {
checkedKeys.push(key);
}
if(item.list.length !== 0) {
result.children = transferTreeData(item.list as GetManageContentCategoryAllResponse, key + "-", checkedKeys);
}
res.push(result)
}
return res
}
const formActions = createFormActions()
const treeActions = createTreeActions()
const fetchClassTreeData = async (params?) => {
const res = await PublicApi.getManageContentCategoryAll();
return res
}
const ClassProperty: React.FC<{}> = () => {
const {
treeStatus,
setTreeStatus,
treeData,
setIsEditForm, //是否编辑状态
nodeRecord,
setNodeRecord,
handleSelect,
getTreeMaps,
setTreeMaps,
resetMenu,
toolsRender,
handleDeleteMenu
} = useTreeTabs({
treeActions,
formActions,
deleteMenu: PublicApi.postManageContentCategoryDelete,
fetchMenuData: fetchClassTreeData,
fetchItemDetailData: PublicApi.getManageContentCategoryGet
})
const tempCheckedKeys = []
const transferTreeDatarRes: TreeDataType[] = useMemo(() => transferTreeData(treeData, "", tempCheckedKeys), [treeData])
// const checkedKeys = useMemo(() => tempCheckedKeys, [transferTreeDatarRes])
const formInitValue = (nodeRecord && treeStatus === FormState.EDIT) ? getTreeMaps(nodeRecord.key) : {}
const formValue = formInitValue ? {...formInitValue, status: [formInitValue.status]} : {}
const is3Level = useMemo(() => {
if (!nodeRecord || nodeRecord.parentId === 0) {
return 1;
}
// 通脱nodeRecord 的parentId 和 _key 判断创建节点还是子节点
const { parentId, _key } = nodeRecord;
const splitRes = _key.split("-");
let flag = splitRes.length;
let i = 1;
for ( ; i <= splitRes.length; i++) {
if (splitRes[i - 1] === parentId?.toString()) {
console.log(i);
flag = i + 1;
break;
}
}
return flag
}, [nodeRecord])
const onFinish = async (values: {id: number, parentId: number, name: string, describe: string, status: string[], level: number}) => {
const _key = nodeRecord?._key?.split("-");
const isEdit = treeStatus === FormState.EDIT;
let parentId = nodeRecord?.parentId || 0;
if (nodeRecord && !isEdit && is3Level === 4) {
parentId = _key[1]
}
const service = !isEdit ? PublicApi.postManageContentCategoryAdd : PublicApi.postManageContentCategoryUpdate;
let postData: any = {
name: values.name,
describe: values.describe,
status: values.status?.[0] || 0,
level: is3Level === 4 ? 3 : is3Level,
parentId: parentId,
}
if (isEdit) {
postData = {
...postData,
parentId: values.parentId,
id: values.id,
level: values.level
}
}
const { code, data } = await service(postData);
if (code === 1000) {
resetMenu()
setTreeStatus(FormState.FREE)
setNodeRecord(null)
}
}
const clickSelect = (key, node) => {
handleSelect(key, node)
// flag = false
}
useEffect(() => {
// 这里有个奇怪的地方,不知道为什么要医疗nodeRecord
if (treeStatus === FormState.ADD && is3Level === 3 || is3Level === 4) {
formActions.setFieldValue("level", 3);
}
}, [treeStatus, nodeRecord, is3Level])
return (
<div className={styles.page}>
<div className={styles.tree}>
<Card>
<h3 className="mb-30">选择要编辑的项目</h3>
{
treeData && treeData.length > 0
? <TabTree
fetchData = {params => fetchClassTreeData(params)}
treeData={transferTreeDatarRes}
toolsRender={toolsRender}
actions={treeActions}
handleSelect={(key, node) => clickSelect(key, node)}
customKey="id"
/>
:
<Button block type='primary' onClick={() => handleSelect()}>暂无菜单, 开始新增</Button>
}
</Card>
</div>
<div className={styles.editPanel}>
{
treeStatus !== FormState.FREE && <>
<h3 className="commonPanelTitle mb-30">{treeStatus === FormState.ADD ? '新增' : '编辑'}</h3>
<NiceForm
value={formValue}
components={{
Checkbox,
CheckboxGroup: Checkbox.Group
}}
name='classForm'
onSubmit={onFinish}
actions={formActions}
effects={($, action)=> {}}
schema={classSchema}
expressionScope={{
showWarn: (
<Tooltip placement="topLeft" title={"只能同时对7个一级分类下的第三级分类设置推荐分类,且每个一级分类下最多只允许设置2个推荐分类"}>
<span style={{marginTop: '-12px', width: '50px'}}>
<QuestionCircleOutlined />
</span>
</Tooltip>
)
}}
>
<FormButtonGroup>
<Button htmlType='submit' type="primary" >
保存
</Button>
<Popconfirm title="确定要删除吗?" okText="是" cancelText="否" onConfirm={handleDeleteMenu}>
{
treeStatus !== FormState.ADD && <Button >
删除
</Button>
}
</Popconfirm>
</FormButtonGroup>
</NiceForm>
</>
}
</div>
</div>
)
}
export default ClassProperty
import { ISchema } from '@formily/antd';
export const classSchema: ISchema = {
type: 'object',
properties: {
megaLayout: {
type: 'object',
"x-component": "mega-layout",
"x-component-props": {
grid: true,
columns: 16,
labelAlign: 'top'
},
properties: {
noField1: {
type: 'object',
"x-component": 'mega-layout',
"x-component-props": {
full: true,
"wrapperWidth": 507,
},
"x-mega-props": {
span: 1
},
properties: {
name: {
type: 'string',
title: '分类名称',
required: true,
"x-component-props": {
placeholder: '请输入品类名称'
},
"x-rules": [
{
limitByte: true,
maxByte: 16
}
]
},
describe: {
type: 'textarea',
title: '类型',
required: true,
"x-component-props": {
placeholder: '最多100个字符,50个汉字'
},
"x-rules": [
{
limitByte: true,
maxByte: 100
}
]
},
level: {
type: 'string',
visible: false,
'x-linkages': [
{
type: 'value:visible',
target: '*(inlineLayout)',
condition: '{{$value === 3}}'
},
]
},
inlineLayout: {
type: 'object',
"x-component": "mega-layout",
"x-component-props": {
inline: true,
},
properties: {
status: {
title: '',
'x-component': 'CheckboxGroup',
enum: [
{ label: '推荐分类', value: 1 },
],
"x-mega-props": {
"addonAfter": "{{showWarn}}",
// wrapperWidth: 130
},
},
}
}
// status1: {
// title: '',
// 'x-component': 'Children',
// "x-component-props": {
// "children": "{{renderCheckBox()}}"
// }
// }
}
},
}
}
}
}
import React, { useState, useEffect } from 'react';
import { SchemaForm,Submit, FormButtonGroup, Reset } from '@formily/antd';
import { Card, Select, Input, Button } from 'antd';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import ReutrnEle from '@/components/ReturnEle';
import { usePageStatus } from '@/hooks/usePageStatus';
import { history, Prompt } from 'umi';
import { PublicApi } from '@/services/api';
import useCustomValidator from '../hooks/useValidator'
interface IOption {
value: number|string,
label: number|string
}
const sortedList = (() => {
let res: IOption[] = []
for(let i = 1; i <= 30; i++ ) {
let data: IOption = {
label: i,
value: i
}
res.push(data);
}
return res
})()
const schema = {
type: 'object',
properties: {
layout: {
name: 'layout',
type: 'object',
'x-component': 'mega-layout',
'x-component-props': {
"labelCol": 3,
"wrapperCol": 10,
"labelAlign": "left"
},
properties: {
name: {
name: 'name',
title: '栏目名称',
'x-component': 'Input',
"required": true,
"x-rules": [
{
"required": true,
"message": "最长20个字符,10个汉字"
},
{
limitByte: true, // 自定义校验规则
maxByte: 20,
}
]
},
type: {
title: '栏目分类',
'x-component': 'Select',
"x-component-props": {
options: [
{ label: '市场行情', value: 1 },
{ label: '资讯', value: 2 },
]
},
"required": true,
"x-rules": [
{
"required": true,
"message": "请选择栏目分类"
},
]
},
sort: {
name: 'sort',
title: '栏目排序',
'x-component': 'Select',
"required": true,
"x-rules": {
"required": true,
"message": "请选择栏目排序"
},
"x-component-props": {
options: sortedList
}
},
}
}
}
}
const useInitialValues = (id) => {
const [state, setState] = useState({})
useEffect(() => {
if(id) {
PublicApi.getManageContentColumnGet({id: id})
.then((data) => {
setState(data);
})
}
}, [id])
return state
}
const ColumnInfo = () => {
useCustomValidator();
const { id, preview } = usePageStatus();
const [ submitLoading, setSubmitLoading ] = useState(false);
const initialValues = useInitialValues(id);
const [unsaved, setUnsaved] = useState(true);
const isEdit = id && !preview;
const isAdd = !id && !preview;
const handleSubmit = async (value: { name: string, sort: number, type: number }) => {
const { name, sort, type} = value;
const serviceActions = isAdd
? PublicApi.postManageContentColumnAdd
: PublicApi.postManageContentColumnUpdate
const postData = {id, name, sort, type};
setSubmitLoading(true)
setUnsaved(false)
try {
const { data, code } = await serviceActions(postData);
if(code === 1000) {
history.push('/content/columnsManagement')
}
} finally {
setSubmitLoading(false);
}
}
const handleCancel = () => {
history.push('/content/columnsManagement')
}
return (
<div>
<PageHeaderWrapper
onBack={() => history.goBack()}
backIcon={<ReutrnEle description="返回" />}
title={!id ? '新建栏目' : '编辑栏目'}
>
<Card>
<SchemaForm
initialValues={initialValues?.data}
onSubmit={handleSubmit}
editable={isAdd || isEdit}
schema={schema}
components={{ Input, Select, Submit }}
>
{
isAdd || isEdit
? (
<FormButtonGroup offset={3}>
<Submit loading={submitLoading}>提交</Submit>
<Button onClick={handleCancel}>取消</Button>
</FormButtonGroup>
)
: <></>
}
</SchemaForm>
</Card>
</PageHeaderWrapper>
<Prompt when={unsaved && (isAdd || isEdit)} message="您还有未保存的内容,是否确定要离开?" />
</div>
)
}
export default ColumnInfo
import React, { useEffect, useState } from 'react';
import { FilterTable, SchemaFlexRowLayout } from '../components/FilterTable';
import { Card, Input, Button, Table, Space, Popconfirm } from 'antd';
import { createVirtualBox, createFormActions, FormEffectHooks, createEffectHook, registerFormFields, connect } from '@formily/antd';
import { history, Link } from 'umi';
import { PublicApi } from '@/services/api';
import StatusSwitch from '@/components/StatusSwitch';
import columnManagementSchemat from './schema';
import { getTableDataSource } from '../utils/utils';
import TablePagination from '../components/TablePagination';
import CustomSearch from '../components/CustomSearch';
const { onFieldValueChange$ } = FormEffectHooks
// const { Search } = Input;
const SchemaButton = createVirtualBox('button', Button);
const SchemaTable = createVirtualBox('SchemaTable', Table);
// registerFormFields({ FlexRowLayout: connect()(FlexRowLayout) })
const actions = createFormActions();
const getData = async (params) => {
const res = await PublicApi.getManageContentColumnPage(params)
return res.data
}
const columnList: React.FC<{}> = () => {
const columnEffects = () => () => {
onFieldValueChange$('pagination').subscribe((state) => {
handleSearch({...state.value})
})
}
useEffect(() => {
const params = {
current: 1,
pageSize: 10
}
getTableDataSource(actions, params, getData);
}, [])
const handleModify = (value) => {
const { id, status } = value;
const postData = {
id: id,
enableStatus: (status ^ 1),
}
//@ts-ignore
PublicApi.postManageContentColumnUpdateStatus(postData).
then((data) => {
const paginationValue = actions.getFieldValue('pagination');
handleSearch({...paginationValue})
})
}
// 栏目删除
const handleRemove = (id: number) => {
///manage/contentColumn/delete
PublicApi.postManageContentColumnDelete({id: id})
.then((data) => {
const paginationValue = actions.getFieldValue('pagination');
handleSearch({...paginationValue})
})
}
const handleSearch = async (params) => {
const title = actions.getFieldValue('search');
const postData = {
name: title || '',
current: 1,
pageSize: 10,
...params,
}
getTableDataSource(actions, postData, getData);
}
// 重设页码
const resetPagination = (params) => {
const paginationValue = actions.getFieldValue('pagination');
actions.setFieldValue('pagination', {
...paginationValue,
...params
})
}
return (
<Card>
<FilterTable
schema={columnManagementSchemat}
components={{CustomSearch, TablePagination, SchemaButton, Table}}
actions={actions}
expressionScope={{
goToCreate: () => {
// console.log("goToCreate")
history.push(`/content/columnsManagement/add`)
},
search: (value) => {
resetPagination({current: 1})
handleSearch({title: value, current: 1 });
},
renderOperation: (val, record) => {
return (
// 无效 status == 0 可以删除修改该
<Space>
{
record.status === 0
? <>
<Link to={`/content/columnsManagement/detail?id=${record.id}`}>编辑</Link>
<Popconfirm
title="确定要执行这个操作?"
onConfirm={() => handleRemove(record.id)}
okText="是"
cancelText="否"
>
<a>删除</a>
</Popconfirm>
</>
: null
}
</Space>
)
},
renderStatus: (text, record) => {
return (
<StatusSwitch
handleConfirm={() => handleModify(record)}
record={record}
fieldNames="status"
/>
)
},
reset: () => {
actions.setFieldValue('search');
resetPagination({current: 1})
handleSearch({current: 1})
},
// paginationChange: (page: number, pageSize: number) => {
// paginationChange(page, pageSize)
// }
}}
effects={columnEffects()}
/>
</Card>
)
}
export default columnList
\ No newline at end of file
import React from 'react';
import EyePreview from '@/components/EyePreview';
const columns = [
{title: 'ID', dataIndex: 'id'},
{
title: '栏目名称', dataIndex: 'name',
render: (text: string, record: any) => (
<EyePreview
url={`/content/columnsManagement/detail?id=${record.id}&preview=1`}
>
{text}
</EyePreview>
)
},
{title: '栏目分类', dataIndex: 'type', render: (text) => text === 1 ? '市场行情' : '资讯'},
{title: '栏目排序', dataIndex: 'sort'},
{
title: '状态', dataIndex: 'status',
filters: [
{
text: '有效',
value: 1,
},
{
text: '无效',
value: 0,
},
],
onFilter: (value, record) => record.status === value,
render: "{{renderStatus}}"
},
{title: '操作', render: "{{renderOperation}}"}
];
const schema = {
type: 'object',
properties: {
layout: {
type: 'object',
// 'x-component': 'mega-layout',
'x-component': 'CustomFlexRowLayout',
'x-component-props': {
justify: 'space-between'
},
properties: {
createBtn: {
type: "object",
name: "createBtn",
"x-component": "button",
"x-component-props": {
"onClick": "{{goToCreate}}",
"children": "新建",
"type": 'primary'
}
},
'right-layout': {
type: 'object',
name: 'rigth-layout',
"x-component": 'CustomFlexRowLayout',
"x-component-props": {
justify: 'center'
},
properties: {
search: {
type: 'string',
name: 'search',
'x-component': 'CustomSearch',
'x-component-props': {
placeholder: "请填写栏目名称",
"onSearch": "{{search}}",
}
},
searchBtn: {
type: 'string',
name: 'searchBtn',
"x-component": "button",
"x-component-props": {
"onClick": "{{reset}}",
"children": "重置",
style: {
marginLeft: '15px'
}
}
},
}
}
}
},
"table": {
"key": "table",
"type": "object",
"name": "table",
"x-component": "SchemaTable",
"x-component-props": {
"columns": columns,
"rowKey": "id",
"pagination": false
}
},
pagination: {
type: 'object',
'x-component': "TablePagination",
'x-style': {
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-end'
},
'x-component-props': {
showQuickJumper: true,
pageSize: 10,
size: 'small'
}
}
}
}
export default schema
import React from 'react';
import { Checkbox } from 'antd';
const CustomCheckbox = (props) => {
console.log(props);
const editable = props.editable
const handleChange = (e) => {
props.mutators.change(e.target.checked)
}
return (
<Checkbox disabled={!editable} checked={props.value} onChange={handleChange}>置顶</Checkbox>
)
}
CustomCheckbox.isFieldComponent = true
export default CustomCheckbox
\ No newline at end of file
import React from 'react';
import 'braft-editor/dist/index.css';
import BraftEditor from 'braft-editor';
// const SchemaEditor = createVirtualBox('SchemaEditor', BraftEditor);
const CustomEditor = (props) => {
const editorProps = props.props['x-component-props'];
const parentProps = props.props['x-component-parent-props'];
const handleChange = (editorState) => {
const isEmpty = editorState.isEmpty();
const value = isEmpty ? null : editorState
props.mutators.change(value)
}
return (
<div {...parentProps}>
<BraftEditor {...editorProps} value={props.value || ''} onChange={handleChange}/>
</div>
)
}
CustomEditor.isFieldComponent = true;
export default CustomEditor
import React from 'react';
import { Input } from 'antd';
import styles from './styles.less'
const { Search } = Input;
const CustomSearch = (props) => {
const editable = props.editable
const componentProps = props.props['x-component-props'];
const handleChange = (e) => {
props.mutators.change(e.target.value)
}
return (
<div className={styles.container}>
<Search
disabled={!editable}
value={props.value}
onChange={handleChange}
{...componentProps}
// onSearch={value => console.log(value)}
/>
</div>
)
}
CustomSearch.isFieldComponent = true
export default CustomSearch
.container {
:global {
.input-search {
// background-color: red;
.ant-input-wrapper {
.ant-input {
height: 32px !important;
}
}
}
}
}
import React, { useEffect } from 'react';
import {
SchemaForm,
registerVirtualBox,
SchemaField,
registerValidationRules
} from '@formily/antd';
import { Row, Col } from 'antd';
// import { FormMegaLayout, Input } from '@formily/antd-components';
const renderCol = (schema) => {
const { flexcol = {}, } = schema && schema['x-component-props'] || {};
const flexProps = schema['x-flex-props'] || {}
return <Col {...flexcol} {...flexProps} key={schema.path}>
<SchemaField schema={schema.toJSON()} path={schema.path}/>
</Col>
}
registerVirtualBox('CustomFlexRowLayout', (props) => {
const schemaProps = props;
const childProperties = schemaProps.schema && schemaProps.schema.getOrderProperties() || [];
const { justify = "start", align = "top" } = schemaProps && schemaProps.props && schemaProps.props['x-component-props'] || {}
return (
<Row justify={justify} align={align}>
{
childProperties.map((v, i, arr) => renderCol(v.schema))
}
</Row>
)
})
const FlexColumnLayoutStyle = {
'display': 'flex',
'flexDirection': 'column'
}
registerVirtualBox('CustomFlexColumnLayout', (props) => {
const schemaProps = props;
const childProperties = schemaProps.schema && schemaProps.schema.getOrderProperties() || [];
const { style } = schemaProps && schemaProps.props && schemaProps.props['x-component-props'] || {};
return (
<div style={{...FlexColumnLayoutStyle, ...style}}>
{
childProperties.map((v) => {
const { flexCol = {} } = v.schema && v.schema['x-component-props'] || {};
return (
<div {...flexCol} key={v.schema.path}>
<SchemaField schema={v.schema.toJSON()} path={v.schema.path}/>
</div>
)
})
}
</div>
)
})
const FilterTable = (props) => {
const { actions, schema, components, ...rest } = props;
return (
<div>
<SchemaForm
components={components}
actions={actions}
schema={schema}
{...rest}
>
</SchemaForm>
</div>
)
}
export default FilterTable;
\ No newline at end of file
import React from 'react';
import { Row, Col } from 'antd';
import { SchemaField } from "@formily/antd";
const renderCol = (schema) => {
const { flexcol = {}, } = schema && schema['x-component-props'] || {};
const flexProps = schema['x-flex-props'] || {}
return <Col {...flexcol} {...flexProps} key={schema.path}>
<SchemaField schema={schema.toJSON()} path={schema.path}/>
</Col>
}
const SchemaFlexRowLayout = (props) => {
const schemaProps = props;
console.log("FlexRowLayout", schemaProps);
const childProperties = schemaProps.schema && schemaProps.schema.getOrderProperties() || [];
const { justify = "start", align = "top" } = schemaProps && schemaProps.props && schemaProps.props['x-component-props'] || {}
return (
<Row justify={justify} align={align}>
{
childProperties.map((v, i, arr) => renderCol(v.schema))
}
</Row>
)
}
SchemaFlexRowLayout.isVirtualFieldComponent = true
const FlexColumnLayoutStyle = {
'display': 'flex',
'flexDirection': 'column'
}
const SchemaFlexColumnLayout = (props) => {
const schemaProps = props;
console.log("FlexColumnLayout", schemaProps);
const childProperties = schemaProps.schema && schemaProps.schema.getOrderProperties() || [];
console.log("childProperties", childProperties)
const { style } = schemaProps && schemaProps.props && schemaProps.props['x-component-props'] || {};
return (
<div style={{...FlexColumnLayoutStyle, ...style}}>
{
childProperties.map((v) => {
const { flexCol = {} } = v.schema && v.schema['x-component-props'] || {};
return (
<div {...flexCol} key={v.schema.path}>
<SchemaField schema={v.schema.toJSON()} path={v.schema.path}/>
</div>
)
})
}
</div>
)
}
SchemaFlexColumnLayout.isVirtualFieldComponent = true
export {
SchemaFlexRowLayout,
SchemaFlexColumnLayout
}
\ No newline at end of file
import FilterTable from './FilterTable';
import { SchemaFlexRowLayout, SchemaFlexColumnLayout } from './FlexLayout';
export {
FilterTable,
SchemaFlexRowLayout,
SchemaFlexColumnLayout
}
\ No newline at end of file
import React from 'react';
import { Pagination } from 'antd';
const TablePagination = (props) => {
const componentProps = props.props["x-component-props"];
const total = componentProps.total;
const parentStyle = props.props['x-style'];
const handleChange = (page, pageSize) => {
props.mutators.change({current: page, pageSize});
}
return (
<div style={parentStyle}>
{
total > 0
? <Pagination {...componentProps} current={props.value?.current || 1} onChange={handleChange} ></Pagination>
: null
}
</div>
)
}
TablePagination.isFieldComponent = true
export default TablePagination
\ No newline at end of file
.tagContainer {
.selection {
display: flex;
flex-direction: row;
justify-content: flex-start;
flex-wrap: wrap;
.selectionItem {
// height: 24px;
font-size: 12px;
padding: 0px 7px;
margin: 0 16px 16px 0;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
background-color: @secondary-color;
color: @main-color;
border-radius: 4px;
.icon {
margin-left: 4px;
cursor: pointer;
}
}
}
.tips {
font-size: 12px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #909399;
line-height: 12px;
}
.tags {
display: flex;
flex-direction: row;
flex-wrap: wrap;
cursor: pointer;
user-select: none;
.tagItem {
// height: 28px;
padding: 0 6px;
background: #F4F5F7;
border-radius: 4px;
font-size: 12px;
color: #606266;
display: flex;
justify-content: center;
align-items: center;
margin-right: 16px;
margin-bottom: 16px;
}
}
}
import React, { useState, useEffect } from 'react';
import styles from './Tags.less';
import { CloseCircleOutlined } from '@ant-design/icons';
const Tags = (props) => {
const seletedTag = props.props['x-component-props']['seletedTag'] ;
const editable = props.editable;
const [tags, setTags] = useState<number[]>([]);
useEffect(() => {
if(seletedTag instanceof Array) {
setTags(seletedTag);
}
}, [seletedTag])
const handleItemSelect = (params) => {
const { value } = params;
if(tags.includes(value)) {
return;
}
const onChange = props.props['x-component-props']['onChange'];
setTags((state) => {
const current = state;
const res = [...current, value];
!!onChange && onChange(res);
return res
})
}
const handleCancel = (params) => {
const { value } = params;
const onChange = props.props['x-component-props']['onChange'];
setTags((state) => {
const current = state;
const res = current.filter((item) => item !== value);
!!onChange && onChange(res);
return res;
})
}
const dataSource = props.props['x-component-props']['dataSource'] || [];
const selected = dataSource.filter((item) => tags.includes(item.value) )
return (
<div className={styles.tagContainer}>
<div className={styles.selection}>
{
selected.map((item) => {
return (
<div className={styles.selectionItem} key={item.value}>
<span>{item.label}</span>
{
editable
? <span className={styles.icon} onClick={() => handleCancel(item)}><CloseCircleOutlined /></span>
: null
}
</div>
)
})
}
</div>
{
editable
? <>
<p className={styles.tips}>从下列标签中选择</p>
<div className={styles.tags}>
{
dataSource.map((item) => {
return (
<div className={styles.tagItem} key={item.value} onClick={() => handleItemSelect(item)}>
{item.label}
</div>
)
})
}
</div>
</>
: null
}
</div>
)
}
Tags.isFieldComponent = true
export default Tags;
import CustomTags from './Tags';
export {
CustomTags
}
\ No newline at end of file
import React from 'react';
import CustomUpload from '@/components/NiceForm/components/CustomUpload';
const WrapCustomUpload = (props) => {
const imgUrl = props.value;
const errors = props.errors;
const editable = props.editable;
return (
<>
{
editable
? <div>
<CustomUpload {...props}></CustomUpload>
{
errors.length > 0
? <p style={{color: '#ff4d4f'}}>{errors.join(",")}</p>
: null
}
</div>
: <div>
<img src={imgUrl} style={{width: '104px', height: '104px'}} />
</div>
}
</>
)
}
WrapCustomUpload.isFieldComponent = true
export default WrapCustomUpload
\ No newline at end of file
import { createFormActions, FormEffectHooks, createEffectHook } from '@formily/antd';
const { onFormInit$ } = FormEffectHooks
const customEvent$ = createEffectHook('requestAsyncDataSource');
const useAsyncDataSource = (name: string, service: any) => {
const { dispatch, setFieldState } = createFormActions()
onFormInit$().subscribe(() => {
// 这里需要调用一下loading
service().then(res => {
//请求结束可以dispatch一个自定义事件收尾,方便后续针对该事件做联动
setFieldState(name, state => {
// @ts-ignore
state.props["x-component-props"]["dataSource"] = res
})
//@ts-ignore
dispatch('requestAsyncDataSource', {
name,
payload: res
})
})
})
customEvent$().subscribe(() => {
console.log("requestAsyncDataSource")
})
}
export {
useAsyncDataSource
}
\ No newline at end of file
import React, { useEffect, useState } from 'react';
const useInitialValues = (params, service) => {
const [state, setState] = useState(null)
useEffect(() => {
if(params.id) {
service(params).then((data) => {
setState(data);
})
}
}, [params.id])
return state
}
export {
useInitialValues
}
import React, { useEffect, useState } from 'react';
import {
registerValidationRules
} from '@formily/antd';
const useCustomValidator = () => {
useEffect(() => {
//自定义校验规则
registerValidationRules({
limitByte: (value, desc, rules) => {
const { allowChineseTransform = true, maxByte } = desc;
let str = value;
let message = `不能超过${maxByte}个字符`
if (allowChineseTransform) {
str = str.replace(/[\u4E00-\u9FA5]/g, "AA");
message += `,或者${maxByte / 2}个汉字`
}
return str.length > maxByte ? message : "";
},
isInteger: (value, des, rules) => {
if(typeof value == 'undefined' || value == '') {
return ''
}
const isNumber = /^\d+$/;
const message = des ? "只允许填写正整数" : des
const pattern = /[0-9]+\.[0-9]*/;
return !isNumber.test(value) || pattern.test(value) ? message : ""
}
});
}, [])
}
export default useCustomValidator
import React, { useState } from 'react';
import { PageHeaderWrapper } from "@ant-design/pro-layout";
import ReutrnEle from '@/components/ReturnEle';
import { usePageStatus } from '@/hooks/usePageStatus';
import { history, Prompt } from 'umi';
import { Card, Button } from 'antd';
import { SchemaForm, createFormActions, FormButtonGroup, FormEffectHooks, Submit, Reset } from '@formily/antd'
import imageInfoSchema from './schema/imageInfoSchema';
import { Input, Select } from 'antd';
import CustomUpload from '../components/WrapCustomUpload';
import { PublicApi } from '@/services/api';
import { useInitialValues } from '../hooks/useInitialValues';
import useCustomValidator from '../hooks/useValidator'
import { setFormStatus, SCENE, POSITION, transfer2Options, } from '../utils/utils';
const { onFieldValueChange$ } = FormEffectHooks
const actions = createFormActions();
// 暂时写死, 所在位置跟使用场景相关联,当选择Web时去除所有App
const WEB_OPTION = transfer2Options(POSITION).filter((item) => !item.label.includes("APP"))
const APP_OPTION = transfer2Options(POSITION).filter((item) => item.label.includes("APP"))
const ImageInfo = () => {
useCustomValidator();
const { id, preview } = usePageStatus();
const initialValues: any = useInitialValues({id:id}, PublicApi.getManageContentImageGet);
const [submitLoading, setSubmitLoading ] = useState(false);
const [unsaved, setUnsaved] = useState(true);
const isEdit = id && !preview;
const isAdd = !id && !preview;
const isView = id && preview;
const handleSubmit = (value) => {
const serviceActions = isAdd
? PublicApi.postManageContentImageAdd
: PublicApi.postManageContentImageUpdate
let tempData = value;
const postData = isAdd ? tempData : {...tempData, id};
setSubmitLoading(true)
setUnsaved(false)
serviceActions(postData).then((data) => {
setSubmitLoading(false);
if(data.code === 1000) {
history.push('/content/imagesManagement')
}
})
}
const handleCancel = () => {
history.push('/content/imagesManagement')
}
const ImageInfoEffects = () => () => {
onFieldValueChange$('layout.useScene').subscribe((state) => {
if(state.initialValue != state.value) {
actions.setFieldValue('layout.position', null)
setFormStatus(
actions,
'layout.position',
'options',
state.value == 1 ? WEB_OPTION : APP_OPTION
)
}
})
}
return (
<PageHeaderWrapper
onBack={() => history.goBack()}
backIcon={<ReutrnEle description="返回" />}
title={isAdd ? '新建图片' : isEdit ? '编辑图片' : '查看图片'}
>
<Card>
<SchemaForm
schema={imageInfoSchema}
actions={actions}
components={{
Input, Select, Submit, CustomUpload
}}
initialValues={initialValues?.data}
onSubmit={handleSubmit}
editable={isAdd || isEdit}
expressionScope={{
label: (
<div>
{
isAdd || isEdit
? <span style={{color: '#ff4d4f'}}>* </span>
: null
}
图片
</div>
)
}}
effects={ImageInfoEffects()}
>
{
isAdd || isEdit
? (
<FormButtonGroup offset={3}>
<Submit loading={submitLoading}>提交</Submit>
<Button onClick={handleCancel}>取消</Button>
</FormButtonGroup>
)
: <></>
}
</SchemaForm>
</Card>
<Prompt when={unsaved && (isAdd || isEdit)} message="您还有未保存的内容,是否确定要离开?" />
</PageHeaderWrapper>
)
}
export default ImageInfo
import React, { useEffect, useState } from 'react';
import { FilterTable, SchemaFlexRowLayout, SchemaFlexColumnLayout } from '../components/FilterTable';
import { Card, Input, Button, Table, Dropdown, Select, Space, Popconfirm } from 'antd';
import { createVirtualBox, createFormActions, FormEffectHooks } from '@formily/antd';
import { history, Link } from 'umi';
import { PublicApi } from '@/services/api';
import { getTableDataSource } from '../utils/utils';
import ImagementSchema from './schema';
import StatusSwitch from '@/components/StatusSwitch';
import TablePagination from '../components/TablePagination';
import CustomSearch from '../components/CustomSearch'
// const { Search } = Input;
const { onFieldValueChange$ } = FormEffectHooks
const SchemaButton = createVirtualBox('button', Button);
// const SchemaTable = createVirtualBox('SchemaTable', Table);
const SchemaDropDown = createVirtualBox('SchemaDropDown', Dropdown.Button);
const actions = createFormActions();
const getData = async (params: any) => {
const res = await PublicApi.getManageContentImagePage(params);
return res.data
}
const ImagesManagement = () => {
useEffect(() => {
const params = {
current: 1,
pageSize: 10
}
getTableDataSource(actions, params, getData);
}, [])
const ImagesManagementEffects = () => () => {
onFieldValueChange$('pagination').subscribe((state) => {
handleSearch({...state.value})
})
}
const handleSearch = async (params) => {
const title = actions.getFieldValue('search');
const postData = {
name: title || '',
current: 1,
pageSize: 10,
...params,
}
getTableDataSource(actions, postData, getData);
}
const handleDelete = (id) => {
PublicApi.postManageContentImageDelete({id: id})
.then((data) => {
const paginationValue = actions.getFieldValue('pagination');
handleSearch({...paginationValue})
})
}
// 修改状态
const handleUpdateStatus = (id, status) => {
const postData = {id: id, enableStatus: status, shelfStatus: 0};
PublicApi.postManageContentImageUpdateStatus(postData)
.then((data) => {
const paginationValue = actions.getFieldValue('pagination');
handleSearch({...paginationValue})
});
}
// 重设页码
const resetPagination = (params) => {
const paginationValue = actions.getFieldValue('pagination');
actions.setFieldValue('pagination', {
...paginationValue,
...params
})
}
return (
<Card>
<FilterTable
schema={ImagementSchema}
components={{
CustomSearch,
// SchemaFlexRowLayout,
// SchemaFlexColumnLayout,
SchemaDropDown,
Select,
Table,
TablePagination,
SchemaButton
}}
effects={ImagesManagementEffects()}
actions={actions}
expressionScope={{
goToCreate: () => {
history.push(`/content/imagesManagement/add`)
},
reset: () => {
actions.setFieldValue("search");
resetPagination({current: 1})
handleSearch({name: null, current: 1})
},
search: (value) => {
resetPagination({current: 1})
handleSearch({name: value, current: 1 });
},
renderStatus: (text, record) => {
return (
<StatusSwitch
handleConfirm={() => handleUpdateStatus(record.id, record.status ^ 1)}
record={record}
fieldNames="status"
/>
)
},
renderOperation: (val, record) => {
return (
<Space>
{/* // 只有 无效 才有 修改和删除 */}
{
record.status == 0
? (
<>
<Link to={`/content/imagesManagement/detail?id=${record.id}`}>编辑</Link>
<Popconfirm
title="确定要执行这个操作吗"
onConfirm={() => handleDelete(record.id)}
okText="是"
cancelText="否"
>
<a href="#">删除</a>
</Popconfirm>
</>
)
: null
}
</Space>
)
},
}}
>
</FilterTable>
</Card>
)
}
export default ImagesManagement;
\ No newline at end of file
import { SCENE, POSITION, transfer2Options, sortedList } from '../../utils/utils';
const SCENEOPTIONS = transfer2Options(SCENE);
const POSITIONOPTIONS = transfer2Options(POSITION);
const SORTLISTOPTIONS = sortedList(1, 11);
/**
* 内容管理 - 图片详情
* 下面就是一个mega-layout 布局
*/
const schema = {
type: 'object',
properties: {
layout: {
type: 'object',
'x-component': 'mega-layout',
'x-component-props': {
labelCol: 3,
wrapperCol: 10,
labelAlign: 'left'
},
properties: {
name: {
type: 'string',
title: '图片名称',
'x-component': 'Input',
'x-component-props': {
placeholder: '最长30个字符,15个汉字'
},
"x-rules": [
{
"required": true,
"message": "最长30个字符,15个汉字"
},
{
limitByte: true, // 自定义校验规则
maxByte: 30,
}
]
},
useScene: {
title: '使用场景',
type: 'string',
'x-component': 'Select',
'x-component-props': {
options: SCENEOPTIONS
},
"x-rules": [{
"required": true,
"message": "请选择栏目"
}],
},
position: {
title: '所在位置',
type: 'string',
'x-component': 'Select',
'x-component-props': {
options: POSITIONOPTIONS
},
"x-rules": [{
"required": true,
"message": "请选择广告排序"
}],
},
sort: {
title: '图片排序',
type: 'string',
'x-component': 'Select',
'x-component-props': {
options: SORTLISTOPTIONS
},
"x-rules": [{
"required": true,
"message": "请选择图片排序"
}],
},
imageUrl: {
type: "object",
title: "{{label}}",
name: "imageUrl",
"x-component": "CustomUpload",
"x-component-props": {
size: '无',
fileMaxSize: 300
},
"x-rules": [{
"required": true,
"message": "请上传图片"
}],
},
}
}
}
}
export default schema
\ No newline at end of file
import EyePreview from '@/components/EyePreview';
import React from 'react';
import { SCENE, POSITION } from '../../utils/utils'
const columns = [
{title: 'ID', dataIndex: 'id'},
{
title: '图片名称',
dataIndex: 'name',
render: (text: string, record: any) => (
<EyePreview
url={`/content/imagesManagement/detail?id=${record.id}&preview=1`}
>
{text}
</EyePreview>
)},
{
title: '使用场景',
dataIndex: 'useScene',
render: (text, record) => {
return (
<div>{SCENE[text]}</div>
)
}
},
{
title: '所在位置',
dataIndex: 'position',
render: (text, record) => {
return (
<div>{POSITION[text]}</div>
)
}
},
{title: '状态', dataIndex: 'status', render: "{{renderStatus}}"},
{title: '操作', render: "{{renderOperation}}"}
];
/**
* 图片管理列表页 schema
*/
const schema = {
type: 'object',
properties: {
layout: {
type: 'object',
// 'x-component': 'mega-layout',
'x-component': 'CustomFlexRowLayout',
'x-component-props': {
justify: 'space-between',
align: 'center'
},
properties: {
'left-layout': {
type: 'object',
name: 'left-layout',
'x-component': 'CustomFlexRowLayout',
'x-component-props': {
justify: 'start',
align: 'center'
},
properties: {
createBtn: {
type: "object",
name: "createBtn",
"x-component": "button",
"x-component-props": {
"onClick": "{{goToCreate}}",
"children": "新建",
"type": 'primary',
style: {
width: '112px',
margin: '0 0 15px 0'
}
}
},
}
},
'right-layout': {
type: 'object',
name: 'rigth-layout',
"x-component": 'CustomFlexColumnLayout',
properties: {
controllers: {
type: 'object',
name: 'controllers',
'x-component': 'CustomFlexRowLayout',
'x-component-props': {
justify: 'end',
},
properties: {
search: {
type: 'string',
name: 'name',
'x-component': 'CustomSearch',
'x-component-props': {
placeholder: "请填写图片名称",
"onSearch": "{{search}}",
}
},
reset: {
type: 'string',
name: 'reset',
"x-component": "button",
"x-component-props": {
"onClick": "{{reset}}",
"children": "重置",
style: {
margin: '0 10px'
}
}
},
}
},
}
}
}
},
"table": {
"key": "table",
"type": "object",
"name": "table",
"x-component": "Table",
"x-component-props": {
"columns": columns,
"rowKey": "id",
"pagination": false
// "rowSelection": "{{rowSelection}}"
}
},
pagination: {
type: 'object',
'x-component': "TablePagination",
'x-style': {
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-end'
},
'x-component-props': {
showQuickJumper: true,
pageSize: 10,
size: 'small'
}
}
}
}
export default schema
\ No newline at end of file
This diff is collapsed.
import React, { useEffect, useState } from 'react';
import { SchemaForm,Submit, FormButtonGroup, Reset, createFormActions, registerValidationRules, FormEffectHooks } from '@formily/antd';
import { Card, Select, Input, Checkbox, Button} from 'antd';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import ReutrnEle from '@/components/ReturnEle';
import { usePageStatus } from '@/hooks/usePageStatus';
import { history, Prompt } from 'umi';
// import CustomUpload from '@/components/NiceForm/components/CustomUpload';
import { CustomTags } from '../components/Tags';
import CustomEditor from '../components/CustomEditor';
import BraftEditor from 'braft-editor';
import { useInitialValues } from '../hooks/useInitialValues';
import { PublicApi } from '@/services/api';
import infomationInfoSchema from './schema/infomationInfoSchema';
import CustomUpload from '../components/WrapCustomUpload';
import useCustomValidator from '../hooks/useValidator'
import { GetManageContentCategoryAllResponse } from '@/services/PlatformApi';
import { COLUMN_CATEGORY } from '../constant';
const actions = createFormActions();
const { onFieldValueChange$, onFieldInputChange$ } = FormEffectHooks
const { TextArea } = Input;
/**
* 栏目分类
* 1.市场行情;2.资讯
*/
type ColumnType = 0 | 1 | 2;
type ColumnListType = {
label: string,
value: number,
type: 1 | 2 | number & {}
}
const InfomationInfo = () => {
useCustomValidator();
const { id, preview } = usePageStatus();
// const [isTop, setIsTop] = useState(1);
const [labelIds, setLabelIds] = useState<number[]>([]);
const [submitLoading, setSubmitLoading ] = useState(false);
const [unsaved, setUnsaved] = useState(true);
const [type, setType] = useState<ColumnType>(0);
const initialValues = useInitialValues({id:id}, PublicApi.getManageContentInformationGet);
const [category, setCategory] = useState<GetManageContentCategoryAllResponse>([]);
const [column, setColumn] = useState<ColumnListType[]>([])
const isEdit = id && !preview;
const isAdd = !id && !preview;
const isView = id && preview;
// 设置form 的某字段的值
const setFormStatus = (name: string, key: string, value: any) => {
actions.setFieldState(name, state => {
// @ts-ignore
state.props['x-component-props'][key] = value
})
}
useEffect(() => {
async function getColumn() {
const res = await PublicApi.getManageContentColumnAll();
const list = res.data.map((item) => ({label: item.name, value: item.id, type: item.type}));
setFormStatus("layout.columnId", "options", list)
setColumn(list)
}
getColumn()
}, [])
useEffect(() => {
async function getLabels() {
const res = await PublicApi.getManageContentLabelAll();
const labels = res.data.map((item) => ({label: item.name, value: item.id}));
setFormStatus("layout.labelIds", "dataSource", labels)
}
getLabels()
}, [])
/**
* 获取行情分类
*/
useEffect(() => {
async function getCategory() {
const { data, code, message } = await PublicApi.getManageContentCategoryAll();
if (code !== 1000) return;
const list = data.map((_item) => ({ label: _item.name, value: _item.id, children: _item.list }) );
setFormStatus("layout.categoryLayout.firstCategoryId", "options", list);
// sethasGetCategory(true)
setCategory(data);
}
getCategory();
}, [])
useEffect(() => {
if(initialValues === null) {
return;
}
const data = initialValues!.data || {}
const content = data.content;
if(content) {
const editorState = BraftEditor.createEditorState(content);
actions.setFieldValue('layout.contentLayout.content', editorState);
}
setFormStatus('layout.contentLayout.content', 'readOnly', isView);
/** @tofix 这里写的不是很好, 这里分类联动应该单独一个组件抽离 */
if(category) {
actions.setFieldValue('layout.categoryLayout.firstCategoryName', data.firstCategoryName);
const secondCategory = category.filter((_row) => _row.id === data.firstCategoryId)[0]?.list || [];
const options = secondCategory.map((_row: any) => ({label: _row.name, value: _row.id, children: _row.list }))
setFormStatus('layout.categoryLayout.secondCategoryId', 'options', options);
// actions.setFieldValue('layout.categoryLayout.secondCategoryId', data.secondCategoryId);
const thirdCategory = options.filter((_row) => _row.value === data.secondCategoryId)[0]?.children || [];
const thirdOptions = thirdCategory.map((_row: any) => ({label: _row.name, value: _row.id, children: _row.list }))
setFormStatus('layout.categoryLayout.thirdlyCategoryId', 'options', thirdOptions);
// actions.setFieldValue('layout.categoryLayout.thirdlyCategoryId', data.thirdlyCategoryId);
}
if (column) {
const targetColumn = column.filter((_item) => _item.value === data.columnId)[0];
if(targetColumn) {
actions.setFieldState('layout.columnId', state => {
state.props['description'] = `栏目分类:${COLUMN_CATEGORY[targetColumn?.type] || ''}`
})
setType(targetColumn?.type)
}
}
setFormStatus('layout.imageUpload', 'imgUrl', data.imageUrl)
// setIsTop(data.top);
setLabelIds(data.labelIds)
setFormStatus('layout.labelIds', 'seletedTag', data.labelIds)
}, [initialValues, category, column])
const handleSubmit = (value) => {
const content = value.content.toHTML();
const tempPostData = {
...value,
type: type,
labelIds: labelIds || [],
content: content,
}
const serviceActions = isAdd
? PublicApi.postManageContentInformationAdd
: PublicApi.postManageContentInformationUpdate
const postData = isAdd ? tempPostData : {...tempPostData, id: id};
setSubmitLoading(true)
setUnsaved(false)
serviceActions(postData).then((data) => {
setSubmitLoading(false);
if(data.code === 1000) {
history.push('/content/infomations')
}
})
}
const handleCancel = () => {
history.push('/content/infomations')
}
return (
<div>
<PageHeaderWrapper
onBack={() => history.goBack()}
backIcon={<ReutrnEle description="返回" />}
title={isAdd ? '新建资讯' : isEdit ? '编辑资讯' : '查看资讯'}
>
<Card>
<SchemaForm
value={initialValues?.data}
onSubmit={handleSubmit}
schema={infomationInfoSchema}
editable={isAdd || isEdit}
actions={actions}
components={{
Input, Select, Submit,
TextArea, Checkbox,
CustomUpload, CustomTags, CustomEditor,
}}
effects={($) => {
onFieldValueChange$('layout.columnId').subscribe(({ value }) => {
actions.setFieldState('layout.columnId', state => {
const currentType = state.props["x-component-props"]!.options?.filter((_row) => _row.value === value)[0];
state["props"]["description"] = `栏目分类:${currentType && (COLUMN_CATEGORY[currentType.type])}`;
setType(currentType ? currentType.type : 0)
})
})
onFieldInputChange$('layout.categoryLayout.*(firstCategoryId,secondCategoryId)').subscribe(({ value, props, name }) => {
const matchName = name.match(/(.*?)Id/);
if(matchName?.length !== 2) {
return
}
const target = props["x-component-props"]!.options.filter((_row) => _row.value === value)[0];
actions.setFieldValue(`layout.categoryLayout.${matchName[1]}Name`, target?.label)
const childrenName = name === 'firstCategoryId' ? 'secondCategoryId' : 'thirdlyCategoryId';
const optionChildren = target?.children || [];
const list = optionChildren?.map((_item) => ({ label: _item.name, value: _item.id, children: _item.list }) );
actions.setFieldState(`layout.categoryLayout.${childrenName}`, state => {
state.value = "",
state.props['x-component-props']!["options"] = list;
})
})
}}
expressionScope={{
tagOnChange: (value) => {
setLabelIds(value);
},
label: (
<div>
{
isAdd || isEdit
? <span style={{color: '#ff4d4f'}}>* </span>
: null
}
图片
</div>
)
}}
>
{
isAdd || isEdit
? (
<FormButtonGroup offset={3}>
<Submit loading={submitLoading}>提交</Submit>
<Button onClick={handleCancel}>取消</Button>
</FormButtonGroup>
)
: <></>
}
</SchemaForm>
</Card>
<Prompt when={(isAdd || isEdit) && unsaved} message="您还有未保存的内容,是否确定要离开?" />
</PageHeaderWrapper>
</div>
)
}
export default InfomationInfo
import EyePreview from '@/components/EyePreview';
import { DownOutlined, DeleteOutlined, UpOutlined } from '@ant-design/icons';
import { timeRange } from '@/utils/index';
import { TimeList } from '../../statusList';
import { COLUMN_CATEGORY } from '../../constant';
import moment from 'moment';
import React from 'react';
const CustomTimeList = [{label: '全部', value: 0}].concat(TimeList.slice(1));
const columns = [
{ title: 'ID', dataIndex: 'id' },
{
title: '栏目分类',
dataIndex: 'type',
render: (text) => COLUMN_CATEGORY[text],
},
{ title: '栏目', dataIndex: 'columnName' },
{
title: '标题',
dataIndex: 'title',
render: (text: string, record: any) => (
<EyePreview
url={`/content/infomations/detail?id=${record.id}&preview=1`}
>
{text}
</EyePreview>
)
},
{ title: '分类', dataIndex: 'categoryName' },
{ title: '推荐标签', dataIndex: 'labelNames' },
{
title: '排序',
dataIndex: 'sort',
sorter: (a, b) => a.sort - b.sort,
},
{
title: '发布时间',
dataIndex: 'createTime',
sorter: (a, b) => a.createTime - b.createTime,
render: (text) => (
moment(text).format('YYYY-MM-DD HH:mm:ss')
)
},
{
title: '状态', dataIndex: 'status',
filters: [
{ text: '待上架', value: '1' },
{ text: '已上架', value: '2' },
{ text: '已下架', value: '3' },
],
onFilter: (value, record) => {
return record.status.toString().includes(value)
},
render: "{{renderStatus}}"
},
{title: '操作', render: "{{renderOperation}}"}
];
/**
* 这等于是一个flex 布局
* flexRow, FlewColumn 布局的高级搜索
* 咨询管理 schema
*/
const infomationSchema = {
type: 'object',
properties: {
layout: {
type: 'object',
// 'x-component': 'mega-layout',
'x-component': 'CustomFlexRowLayout',
'x-component-props': {
justify: 'space-between',
align: 'center'
},
properties: {
'left-layout': {
type: 'object',
name: 'left-layout',
'x-component': 'CustomFlexRowLayout',
'x-component-props': {
justify: 'start',
align: 'center'
},
properties: {
createBtn: {
type: "object",
name: "createBtn",
"x-component": "button",
"x-component-props": {
"onClick": "{{goToCreate}}",
"children": "新建",
"type": 'primary',
style: {
width: '112px',
margin: '0 0 15px 0'
}
}
},
batchGrounding: {
type: 'object',
name: 'batchUpdata',
'x-component': 'button',
'x-component-props': {
"onClick": "{{batchGrounding}}",
"children": "批量上架",
style: {
margin: '0 0 0 15px'
}
}
},
undercarriage: {
type: 'object',
name: 'undercarriage',
'x-component': 'button',
'x-component-props': {
"onClick": "{{undercarriage}}",
"children": "批量下架",
style: {
margin: '0 15px'
}
}
},
more: {
type: 'object',
name: 'more',
'x-component': 'SchemaDropDown',
'x-component-props': {
overlay: "{{menu}}",
children: '更多',
icon: <DownOutlined />
}
}
}
},
'right-layout': {
type: 'object',
name: 'rigth-layout',
"x-component": 'CustomFlexColumnLayout',
properties: {
controllers: {
type: 'object',
name: 'controllers',
'x-component': 'CustomFlexRowLayout',
'x-component-props': {
justify: 'end',
},
properties: {
search: {
type: 'string',
name: 'name',
'x-component': 'CustomSearch',
'x-component-props': {
placeholder: "请填写标题名称",
style: {
marginRight: '12px'
},
"onSearch": "{{search}}",
}
},
'HIGHT_FILTER_BTN': {
type: 'string',
name: 'HIGHT_FILTER_BTN',
'x-component': 'button',
'x-component-props': {
"children": (
<div>高级搜索 <DownOutlined /></div>
),
"onClick": "{{toggleFilters}}",
style: {
margin: '0 15px'
}
}
},
reset: {
type: 'string',
name: 'reset',
"x-component": "button",
"x-component-props": {
"onClick": "{{reset}}",
"children": "重置",
}
},
}
},
'FILTERS': {
type: 'object',
name: 'FILTERS',
'x-component': 'CustomFlexRowLayout',
'x-component-props': {
justify: 'end'
},
properties: {
columns: {
name: 'columns',
type: 'string',
'x-component': 'Select',
'x-component-props': {
placeholder: '请选择栏目',
style: {
width: '160px'
}
}
},
status: {
name: 'status',
type: 'string',
'x-component': 'Select',
'x-component-props': {
options: [
{label: '全部', value: '0'},
{label: '待上架', value: '1'},
{label: '已上架',value: '2'},
{label: '已下架',value: '3'},
],
placeholder: '请选择状态',
style: {
width: '160px',
margin: '0 15px'
}
}
},
time: {
name: 'time',
type: 'string',
'x-component': 'Select',
'x-component-props': {
placeholder: '发布时间(全部)',
options: CustomTimeList,
style: {
width: '160px',
}
}
}
}
}
}
}
}
},
"table": {
"key": "table",
"type": "object",
"name": "table",
"x-component": "Table",
"x-component-props": {
"columns": columns,
"rowKey": "id",
"pagination":false,
"rowSelection": "{{rowSelection}}"
}
},
pagination: {
type: 'object',
'x-component': "TablePagination",
'x-style': {
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-end'
},
'x-component-props': {
showQuickJumper: true,
pageSize: 10,
size: 'small'
}
}
}
}
export {
infomationSchema
}
interface IOption {
value: number|string,
label: number|string
}
const sortedList = (() => {
let res: IOption[] = []
for(let i = 1; i <= 10; i++ ) {
let data: IOption = {
label: i,
value: i
}
res.push(data);
}
return res
})()
const schema = {
type: 'object',
properties: {
layout: {
name: 'layout',
type: 'object',
'x-component': 'mega-layout',
'x-component-props': {
"labelCol": 3,
"wrapperCol": 10,
"labelAlign": "left",
},
properties: {
title : {
name: 'title',
title: '标题',
'x-component': 'Input',
'x-component-props': {
placeholder: '最长30个汉字,60个字符',
},
"x-rules": [
{
"required": true,
"message": "请填写标题"
},
{
limitByte: true,
maxByte: 60
}
]
},
columnId: {
name: 'columnId',
title: '栏目',
'x-component': 'Select',
"x-component-props": {
style: {
marginBottom: '8px'
},
},
description: "栏目分类",
"x-rules": {
"required": true,
"message": "请选择咨询说明"
},
},
recommendLabel: {
name: 'recommendLabel',
title: '推荐标签',
type: 'string',
'x-component': 'Select',
"x-component-props": {
style: {
marginTop: '12px'
},
allowClear: true,
options: [
{label: '头条文章', value: 1},
{label: '轮播新闻', value: 2},
{label: '图片新闻', value: 3},
{label: '推荐阅读', value: 4},
{label: '行情推荐', value: 5},
{label: '本栏推荐', value: 6}
],
},
},
// sortLayout: {
// type: 'object',
// 'x-component': 'mega-layout',
// "x-component-props": {
// "label": "推荐排序",
// // wrapperCol: 23,
// // layoutProps: {
// // "wrapperCol": 12,
// // },
// style: {
// marginBottom: 0
// },
// // addonAfter: "{{isTop}}"
// },
// properties: {
// sort: {
// name: 'sort',
// type: 'string',
// 'x-component': 'Select',
// 'x-component-props': {
// // style: {
// // width: '100%'
// // },
// options: sortedList,
// allowClear: true,
// }
// },
// }
// },
sort: {
name: 'sort',
type: 'string',
title: '推荐排序',
'x-component': 'Select',
'x-component-props': {
options: sortedList,
allowClear: true,
}
},
readCount: {
name: 'readCount',
title: '浏览数',
type: 'string',
'x-component': 'Input',
'x-component-props': {
style: {
width: '100%'
}
},
'x-rules': [
{
isInteger: true,
}
]
},
categoryLayout: {
type: 'object',
'x-component': 'mega-layout',
'x-component-props': {
grid: true,
full: true,
label: "行情资讯分类:",
"wrapperCol": 24,
},
properties: {
firstCategoryId: {
type: 'string',
"x-component": "Select",
'x-component-props': {
options: [],
},
'x-linkages': [
{
type: 'value:visible',
target: '*(secondCategoryId)',
condition: `{{!!$value}}`,
},
]
},
firstCategoryName: {
type: 'string',
display: false,
},
secondCategoryId: {
type: 'string',
'x-component': "Select",
'x-component-props': {
options: [],
},
'x-linkages': [
{
type: 'value:visible',
target: '*(thirdlyCategoryId)',
condition: '{{!!$value}}'
},
]
},
secondCategoryName: {
type: 'string',
display: false,
},
thirdlyCategoryId: {
type: 'string',
'x-component': "Select",
'x-component-props': {
options: [],
},
},
thirdlyCategoryName: {
type: 'string',
display: false
}
}
},
labelIds: {
name: 'labelIds',
title: '咨询标签',
"x-component": 'CustomTags',
"x-component-props": {
layoutProps: {
"wrapperCol": 12,
},
"onChange": "{{tagOnChange}}",
}
},
imageUrl: {
type: "object",
title: "{{label}}",
name: "imageUrl",
"x-component": "CustomUpload",
"x-component-props": {
size: '无',
// onChange: "{{uploadImage}}",
fileMaxSize: 300
},
"x-rules": {
"required": true,
"message": "请上传图片"
},
},
digest: {
type: 'string',
name: 'digest',
title: '摘要',
"x-component": 'TextArea',
"x-component-props": {
placeholder: "最长300个字符,150个汉字",
rows: 5,
},
"x-rules": [
{
"required": true,
"message": "最长300个字符,150个汉字"
},
{
limitByte: true,
maxByte: 300
}
],
},
contentLayout: {
'x-component': 'mega-layout',
"x-component-props": {
layoutProps: {
"wrapperCol": 21,
},
wrapperCol: 23,
},
properties: {
content: {
type: "string",
name: 'content',
title: '内容',
"x-component": 'CustomEditor',
"x-component-parent-props": {
style: {
border: '1px solid #DCDFE6'
}
},
"x-rules": {
"required": true,
"message": "请输入内容"
},
"x-component-props": {
contentStyle: {
height: 256,
},
excludeControls: [
'letter-spacing',
'line-height',
'clear',
'headings',
'list-ol',
'list-ul',
'remove-styles',
'superscript',
'subscript',
'hr',
],
media: {
// 如果要允许上传视频的话,需要重写uploadFn, https://www.yuque.com/braft-editor/be/gz44tn
accepts: {
video: false,
audio: false,
}
}
},
}
}
}
}
}
}
}
export default schema;
/*
* @Author: LeeJiancong
* @Date: 2020-08-01 11:06:09
* @LastEditors: LeeJiancong
* @LastEditTime: 2020-08-01 11:07:49
*/
export const TimeList = [
{
label: '发布时间(全部)', value: 0
},
{
label: '今天', value: 1
},
{
label: '一周内', value: 2
},
{
label: '一个月内', value: 3
},
{
label: '三个月内', value: 4
},
{
label: '六个月内', value: 5
},
{
label: '一年内', value: 6
},
{
label: '一年前', value: 7
}
]
export const outSideStatusList = [
{
label: '外部状态(全部)', value: ''
},
{
label: '待提交', value: 1
},
{
label: '待确认', value: 2
},
{
label: '接受物流单', value: 3
},
{
label: '不接受物流单', value: 4
}
]
export const statusList = [
{
text: '待提交', value: 1
},
{
text: '待确认', value: 2
},
{
text: '接受物流单', value: 3
},
{
text: '不接受物流单', value: 4
}
]
import React, {useState, useEffect} from 'react';
import { Card, Input, Button, Table, Space, Popconfirm } from 'antd';
import { FilterTable, SchemaFlexRowLayout } from '../components/FilterTable';
import { createVirtualBox, createFormActions, FormEffectHooks, createEffectHook } from '@formily/antd';
import { history, Link } from 'umi';
import { PublicApi } from '@/services/api';
import StatusSwitch from '@/components/StatusSwitch';
import tagsManagementSchema from './schema';
import TablePagination from '../components/TablePagination';
import { getTableDataSource } from '../utils/utils';
import CustomSearch from '../components/CustomSearch';
const { onFieldValueChange$ } = FormEffectHooks
const { Search } = Input;
const SchemaButton = createVirtualBox('button', Button);
const SchemaTable = createVirtualBox('SchemaTable', Table);
const actions = createFormActions();
const getData = async (params) => {
const res = await PublicApi.getManageContentLabelPage(params)
return res.data
}
const Tags = () => {
const tagEffects = () => () => {
onFieldValueChange$('pagination').subscribe((state) => {
handleSearch({...state.value})
})
}
useEffect(() => {
const params = {
current: 1,
pageSize: 10
}
getTableDataSource(actions, params, getData);
}, [])
// 修改状态
const handleModify = (value) => {
const { id, status } = value;
const postData = {
id: id,
enableStatus: (status ^ 1),
}
//@ts-ignore
PublicApi.postManageContentLabelUpdateStatus(postData).
then((data) => {
const paginationValue = actions.getFieldValue('pagination');
handleSearch({...paginationValue})
})
}
// 栏目删除
const handleRemove = (id: number) => {
///manage/contentColumn/delete
PublicApi.postManageContentLabelDelete({id: id})
.then(async (data) => {
const paginationValue = actions.getFieldValue('pagination');
handleSearch({...paginationValue})
})
}
const handleSearch = async (params) => {
const title = actions.getFieldValue('search');
const postData = {
name: title || '',
current: 1,
pageSize: 10,
...params,
}
getTableDataSource(actions, postData, getData);
}
// 重设页码
const resetPagination = (params) => {
const paginationValue = actions.getFieldValue('pagination');
actions.setFieldValue('pagination', {
...paginationValue,
...params
})
}
return (
<div>
<Card>
<FilterTable
effects={tagEffects()}
schema={tagsManagementSchema}
components={{CustomSearch, TablePagination}}
actions={actions}
expressionScope={{
goToCreate: () => {
history.push(`/content/tagsManagement/add`)
},
search: (value) => {
resetPagination({current: 1})
handleSearch({title: value, current: 1 });
},
renderOperation: (val, record) => {
return (
<Space>
{
record.status === 0
? <>
<Link to={`/content/tagsManagement/detail?id=${record.id}`}>编辑</Link>
<Popconfirm
title="确定要执行这个操作?"
onConfirm={() => handleRemove(record.id)}
okText="是"
cancelText="否"
>
<a>删除</a>
</Popconfirm>
</>
: null
}
</Space>
)
},
renderStatus: (text, record) => {
return (
<StatusSwitch
handleConfirm={() => handleModify(record)}
record={record}
fieldNames="status"
/>
)
},
reset: () => {
actions.setFieldValue('search');
resetPagination({current: 1})
handleSearch({current: 1})
},
}}
/>
</Card>
</div>
)
}
export default Tags
\ No newline at end of file
import React from 'react';
import EyePreview from '@/components/EyePreview';
const columns = [
{title: 'ID', dataIndex: 'id'},
{
title: '标签名称', dataIndex: 'name',
render: (text: string, record: any) => (
<EyePreview
url={`/content/tagsManagement/detail?id=${record.id}&preview=1`}
>
{text}
</EyePreview>
)
},
{title: '标签说明', dataIndex: 'explain'},
{title: '状态', dataIndex: 'status', render: "{{renderStatus}}", width: 120},
{title: '操作', render: "{{renderOperation}}", width: 150}
];
const schema = {
type: 'object',
properties: {
layout: {
type: 'object',
// 'x-component': 'mega-layout',
'x-component': 'CustomFlexRowLayout',
'x-component-props': {
justify: 'space-between'
},
properties: {
createBtn: {
type: "object",
name: "createBtn",
"x-component": "button",
"x-component-props": {
"onClick": "{{goToCreate}}",
"children": "新建",
"type": 'primary'
}
},
'right-layout': {
type: 'object',
name: 'rigth-layout',
"x-component": 'CustomFlexRowLayout',
"x-component-props": {
justify: 'center'
},
properties: {
search: {
type: 'string',
name: 'search',
'x-component': 'CustomSearch',
'x-component-props': {
placeholder: "请填写栏目名称",
"onSearch": "{{search}}",
}
},
searchBtn: {
type: 'string',
name: 'searchBtn',
"x-component": "button",
"x-component-props": {
"onClick": "{{reset}}",
"children": "重置",
style: {
marginLeft: '15px'
}
}
},
}
}
}
},
"table": {
"key": "table",
"type": "object",
"name": "table",
"x-component": "SchemaTable",
"x-component-props": {
"dataSource": [],
"columns": columns,
"rowKey": "id",
"pagination": false
}
},
pagination: {
type: 'object',
'x-component': "TablePagination",
'x-style': {
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-end'
},
'x-component-props': {
showQuickJumper: true,
pageSize: 10,
size: 'small'
}
}
}
}
export default schema;
\ No newline at end of file
import React, { useState, useEffect } from 'react';
import { SchemaForm,Submit, FormButtonGroup, Reset } from '@formily/antd';
import { Card, Select, Input, Button } from 'antd';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import ReutrnEle from '@/components/ReturnEle';
import { usePageStatus } from '@/hooks/usePageStatus';
import { history, Prompt } from 'umi';
import { useInitialValues } from '../hooks/useInitialValues';
import { PublicApi } from '@/services/api';
import useCustomValidator from '../hooks/useValidator'
const { TextArea } = Input;
const schema = {
type: 'object',
properties: {
layout: {
name: 'layout',
type: 'boject',
'x-component': 'mega-layout',
'x-component-props': {
"labelCol": 3,
"wrapperCol": 10,
"labelAlign": "left"
},
properties: {
name: {
name: 'name',
title: '标签名称',
'x-component': 'Input',
"required": true,
"x-rules": [
{
"required": true,
"message": "请填写标签名称"
},
{
limitByte: true, // 自定义校验规则
maxByte: 20,
}
],
},
explain: {
name: 'explain',
title: '标签说明',
'x-component': 'TextArea',
'x-component-props': {
rows: 5
},
"x-rules": [
{
limitByte: true, // 自定义校验规则
maxByte: 80,
}
],
},
}
}
}
}
const TagInfo = () => {
useCustomValidator();
const { id, preview } = usePageStatus();
const [ submitLoading, setSubmitLoading ] = useState(false);
const initialValues = useInitialValues({id:id}, PublicApi.getManageContentLabelGet);
const isEdit = id && !preview;
const isAdd = !id && !preview;
const [unsaved, setUnsaved] = useState(true);
const handleSubmit = (value) => {
console.log(value)
const { id, name, explain} = value;
const serviceActions = isAdd
? PublicApi.postManageContentLabelAdd
: PublicApi.postManageContentLabelUpdate
let tempData = {name, explain};
const postData = isAdd ? {...tempData, id: 0} : {...tempData, id};
setSubmitLoading(true)
setUnsaved(false);
serviceActions(postData).then((data) => {
setSubmitLoading(false);
if(data.code === 1000) {
history.push('/content/tagsManagement')
}
})
}
const handleCancel = () => {
history.push('/content/tagsManagement')
}
return (
<div>
<PageHeaderWrapper
onBack={() => history.goBack()}
backIcon={<ReutrnEle description="返回" />}
title={!id ? '新建标签' : '编辑标签'}
>
<Card>
<SchemaForm
initialValues={initialValues?.data}
onSubmit={handleSubmit}
editable={isAdd || isEdit}
schema={schema}
components={{ Input, Select, Submit, TextArea }}
>
{
isAdd || isEdit
? (
<FormButtonGroup offset={3}>
<Submit loading={submitLoading}>提交</Submit>
<Button onClick={handleCancel}>取消</Button>
</FormButtonGroup>
)
: <></>
}
</SchemaForm>
</Card>
<Prompt when={unsaved} message="您还有未保存的内容,是否确定要离开?" />
</PageHeaderWrapper>
</div>
)
}
export default TagInfo
\ No newline at end of file
const tagColorStyle = {
"1": {color: '#606266', background: '#F4F5F7'},
"2": {color: '#00B37A', background: '#EBF7F2'},
"3": {color: '#E63F3B', background: '#FFEBE6'},
}
// 设置Table 状态
const setFormStatus = (ctx, name: string, key: string, value: any) => {
ctx.setFieldState(name, state => {
// @ts-ignore
state.props['x-component-props'][key] = value
})
}
// 设置table DataSource
const setTableDataSource = (ctx, { dataSource, total }) => {
ctx.setFieldState("table", state => {
//@ts-ignore
state.props["x-component-props"]["loading"] = false;
//@ts-ignore
state.props["x-component-props"]["dataSource"] = dataSource;
if(state.props["x-component-props"]["pagination"]) {
state.props["x-component-props"]["pagination"]["total"] = total
}
})
ctx.setFieldState('pagination', state => {
//@ts-ignore
state.props["x-component-props"]["total"] = total;
})
}
// 获取 table DataSource,只是把loading 跟获取数据集合在一起
const getTableDataSource = async (ctx, params, service) => {
setFormStatus(ctx, "table", "loading", true);
const res = await service(params);
setTableDataSource(ctx, {dataSource: res.data, total: res.totalCount})
}
interface IOption {
value: number|string,
label: number|string
}
const sortedList = (start, end) => {
let res: IOption[] = []
for(let i = start; i < end; i++ ) {
let data: IOption = {
label: i,
value: i
}
res.push(data);
}
return res
}
// 内容管理 - 广告栏目
const ADVERTISE_WEB_COLUMN_TYPE = {
"1": "会员首页-活动广告1",
"2": "会员首页-活动广告2",
"3": "会员首页-活动广告3",
"4": "企业采购首页--轮播广告",
"5": "企业采购首页--顶部广告栏",
"6": "企业门户首页--轮播广告",
"7": "采购商机页--右侧广告栏",
"8": "渠道服务首页--轮播广告",
"9": "物流服务首页--轮播广告",
"10": "加工服务首页--轮播广告"
}
const ADVERTISE_APP_COLUMN_TYPE = {
"51": "找店铺--广告",
"52": "人气店铺--广告",
"53": "商品询价--广告",
"54": "未开通电子签章推广页--广告",
"55": "授信申请推广页广告"
}
// 内容管理 - 公告栏目
const ANNOUNCE_COLUMN_TYPE = {
'1': '会员首页公告',
'2': '注册须知',
'3': '入库须知'
}
// 内容管理 - 图片管理 - 使用场景
const SCENE = {
"1": 'WEB',
"2": 'APP'
}
// 内容管理 - 图片管理 - 所在位置
const POSITION = {
"1": "WEB登录页面",
"2": "平台后台登录页面",
"3": "APP引导页",
"4": "APP启动页"
}
/**
* 将字典转换成 {label: 'xx', value: 1}
* @param maps
*/
const transfer2Options = (maps) => {
const res = Object.keys(maps).map((item) => {
return {
label: maps[item],
value: parseInt(item)
}
})
return res
}
export {
tagColorStyle,
setFormStatus,
setTableDataSource,
getTableDataSource,
sortedList,
ADVERTISE_WEB_COLUMN_TYPE,
ADVERTISE_APP_COLUMN_TYPE,
// ADVERTISE_COLUMN_TYPE,
ANNOUNCE_COLUMN_TYPE,
transfer2Options,
SCENE,
POSITION
}
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