fix(staff)

This commit is contained in:
wang_yp 2025-03-24 18:13:33 +08:00
parent 6fecf2b553
commit a2f288073e
27 changed files with 926 additions and 213 deletions

View File

@ -9,7 +9,7 @@
<meta name="description" content="Web site created using create-react-app" /> <meta name="description" content="Web site created using create-react-app" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /> <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>黄水武装</title> <title>农浓深情</title>
</head> </head>
<body> <body>

View File

@ -2,22 +2,7 @@ import Modal from "antd/lib/modal";
import { PlusOutlined } from "@ant-design/icons"; import { PlusOutlined } from "@ant-design/icons";
import Upload, { RcFile, UploadFile, UploadProps } from "antd/lib/upload"; import Upload, { RcFile, UploadFile, UploadProps } from "antd/lib/upload";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import Config from "@/util/config"; import { UploadFileProps } from "./form/interface";
interface UploadFileProps {
imgList: Array<UploadFileEx>;
onChnage: Function;
maxCount?: number;
}
interface UploadFileEx extends UploadFile {
systemImageId?: number;
bannerName?: string;
file_name?: string;
file_url?: string;
redictUrl?: string;
id?: number;
}
const getBase64 = (file: RcFile): Promise<string> => const getBase64 = (file: RcFile): Promise<string> =>
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
@ -45,16 +30,17 @@ const AliUpload = (props: UploadFileProps) => {
}; };
useEffect(() => { useEffect(() => {
setFileList(props.imgList); setFileList(props.value ?? []);
}, [props.imgList]); }, [props.value]);
const handleChange: UploadProps["onChange"] = ({ fileList: newFileList }) => { const handleChange: UploadProps["onChange"] = ({ fileList: newFileList }) => {
newFileList.forEach((i) => { newFileList.forEach((i) => {
i.url = `${Config.baseUrl}/uploads/` + i.name; if (i.status === "done") {
i.fileName = i.name; i.url = i.response.data.record.url;
i.fileName = i.response.data.record.file_name;
}
}); });
setFileList(newFileList); props.onChange!(newFileList);
props.onChnage(newFileList);
}; };
const uploadButton = ( const uploadButton = (
<div> <div>
@ -64,16 +50,17 @@ const AliUpload = (props: UploadFileProps) => {
); );
return ( return (
<div style={{ margin: 10 }}> <div style={{ margin: 10 }} id={props.id}>
<Upload <Upload
{...props}
listType="picture-card" listType="picture-card"
fileList={files} fileList={files}
action={`${Config.baseUrl}/v1/public/fts/upload`} action={`/v1/public/fts/upload`}
onPreview={handlePreview} onPreview={handlePreview}
maxCount={props.maxCount ?? 4} maxCount={props.maxCount ?? 4}
onChange={handleChange} onChange={handleChange}
> >
{files.length >= (props.maxCount??4) ? null : uploadButton} {files.length >= (props.maxCount ?? 4) ? null : uploadButton}
</Upload> </Upload>
<Modal <Modal
open={previewOpen} open={previewOpen}

View File

@ -1,4 +1,4 @@
import { FormInstance } from "antd" import { FormInstance, UploadFile } from "antd"
import React from "react" import React from "react"
export enum FormType { export enum FormType {
input = "input", input = "input",
@ -23,10 +23,12 @@ export interface FormDatas {
name: string, name: string,
value: any, value: any,
selectUrl?: string, selectUrl?: string,
key?: string,
selectList?: Array<selectItem> selectList?: Array<selectItem>
checkboxData?: Array<any>, checkboxData?: Array<any>,
radioData?: Array<any>, radioData?: Array<any>,
rules: Array<rules> rules: Array<rules>,
selectModel?: "multiple" | "tags" | undefined,
} }
export interface SimpleFormData { export interface SimpleFormData {
@ -54,4 +56,20 @@ export interface rules {
export interface selectItem { export interface selectItem {
name: string, name: string,
id: number, id: number,
} }
export interface UploadFileProps {
value?: Array<UploadFileEx>;
onChange?: (value: Array<UploadFileEx>) => void;
maxCount?: number;
id?: string;
}
export interface UploadFileEx extends UploadFile {
systemImageId?: number;
bannerName?: string;
file_name?: string;
file_url?: string;
redictUrl?: string;
id?: number;
}

View File

@ -3,24 +3,24 @@ import { FormDatas } from "./interface";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { base } from "@/service/base"; import { base } from "@/service/base";
const { Option } = Select; const { Option } = Select;
export const FormSelect = (v: FormDatas) => { export const FormSelect = (props: FormDatas) => {
const [list, setList] = useState<any>([]); const [list, setList] = useState<any>([]);
useEffect(() => { useEffect(() => {
if (v.selectList && v.selectList.length > 0) { if (props.selectList && props.selectList.length > 0) {
setList(v.selectList); setList(props.selectList);
} else { } else {
base.get(`${v.selectUrl}/?size=50&offset=1`, "").then((res) => { base.get(`${props.selectUrl}`, {size:50,offset:1}).then((res) => {
setList(res.data.record ?? []); setList(res.data.record ?? []);
}); });
} }
}, [v.selectUrl, v.selectList]); }, [props.selectUrl, props.selectList]);
return ( return (
<Form.Item key={v.label} label={v.label} name={v.name} rules={v.rules}> <Form.Item key={props.label} label={props.label} name={props.name} rules={props.rules}>
<Select placeholder=""> <Select placeholder="" mode={props.selectModel}>
{list?.map((v: any) => { {list?.map((v: any) => {
return ( return (
<Option key={v.id} value={v.id}> <Option key={v.identity} value={v.identity}>
{v.name} {v[props.key ?? "name"]}
</Option> </Option>
); );
})} })}

View File

@ -101,23 +101,11 @@ const SimpleForm = (props: SimpleFormData) => {
<Form.Item <Form.Item
key={v.label} key={v.label}
label={v.label} label={v.label}
valuePropName="fileList"
name={v.name} name={v.name}
rules={v.rules} rules={v.rules}
initialValue={v.value} initialValue={v.value}
getValueFromEvent={(e) => {
if (Array.isArray(e)) {
return e;
}
return e && e.fileList;
}}
> >
<AliUpload <AliUpload />
imgList={form.getFieldValue(v.name) ?? []}
onChnage={(res) => {
form.setFieldValue(v.name, res);
}}
/>
</Form.Item> </Form.Item>
); );
case FormType.textarea: case FormType.textarea:

View File

@ -31,13 +31,14 @@ const LayOut = (props: Store) => {
alignItems: "center", alignItems: "center",
justifyContent: "space-between", justifyContent: "space-between",
}; };
const logoStyle = { width: 60, color: "white" }; const logoStyle = { width: 140, color: "white",display:"flex" };
const contentstyle = { const contentstyle = {
padding: 12, padding: 12,
margin: 0, margin: 0,
minHeight: 280, minHeight: 280,
background: colorBgContainer, background: colorBgContainer,
borderRadius: borderRadiusLG, borderRadius: borderRadiusLG,
}; };
const breadItem = [ const breadItem = [
{ title: "首页" }, { title: "首页" },
@ -55,7 +56,8 @@ const LayOut = (props: Store) => {
<Layout> <Layout>
<Header style={headStyle}> <Header style={headStyle}>
<div style={logoStyle}> <div style={logoStyle}>
{/* <Image src={logo} /> */} <Image src={logo} style={{ width: 40, height: 40,marginRight:10 ,borderRadius:10 }} />
<span></span>
</div> </div>
<Dropdown menu={{ items }}> <Dropdown menu={{ items }}>
<Avatar icon={<UserOutlined />} /> <Avatar icon={<UserOutlined />} />
@ -83,7 +85,7 @@ const LayOut = (props: Store) => {
</Sider> </Sider>
<Layout style={{ padding: "0 15px 15px" }}> <Layout style={{ padding: "0 15px 15px" }}>
<Breadcrumb items={breadItem} style={{ margin: "10px 0" }} /> <Breadcrumb items={breadItem} style={{ margin: "10px 0" }} />
<Content style={contentstyle}> <Content style={{...contentstyle,overflowX:"auto"}}>
<Outlet /> <Outlet />
</Content> </Content>
<Footer style={{ textAlign: "center" }}> <Footer style={{ textAlign: "center" }}>

View File

@ -23,7 +23,12 @@ export const items: ItemType<MenuItemType>[] = [
key: "/city", key: "/city",
label: `城市管理`, label: `城市管理`,
icon: <UserOutlined />, icon: <UserOutlined />,
children: [{ key: "/city/list", label: `城市管理` }], children: [
{ key: "/city/list", label: `城市管理` },
{ key: "/city/hum", label: `人文管理` },
{ key: "/city/history", label: `历史底蕴` },
{ key: "/city/food", label: `地方美食` },
],
}, },
{ {
key: "/sku", key: "/sku",

View File

@ -0,0 +1,69 @@
import { FormType } from "@/components/form/interface";
import { UserDataType } from "@/model/userModel";
import { CityConfig } from "@/service/config";
import { ColumnsType } from "antd/lib/table";
import { Image } from "antd";
export const formConfig = [
{
type: FormType.input,
label: "标题",
name: "title",
value: "",
rules: [{ required: true, message: "城市名称不能为空!" }],
},
{
type: FormType.input,
label: "副标题",
name: "sub_title",
value: "",
rules: [{ required: true, message: "城市编码不能为空" }],
},
{
type: FormType.upload,
label: "缩略图",
name: "cover",
value: [],
maxCount: 1,
rules: [{ required: true, message: "缩略图不能为空" }],
},
{
type: FormType.select,
label: "城市",
name: "city_identity",
value: "",
key: "city_name",
selectUrl: CityConfig.LIST,
rules: [{ required: true, message: "城市不能为空" }],
},
{
type: FormType.editor,
label: "内容",
name: "content",
value: "",
rules: [{ required: true, message: "城市不能为空" }],
},
];
export const columns: ColumnsType<UserDataType> = [
{
title: "美食名称",
dataIndex: "title",
fixed: "left",
},
{
title: "美食副标题",
dataIndex: "sub_title",
},
{
title: "首图",
dataIndex: "cover",
render: (text, record) => {
return <Image width={50} src={text} />;
},
},
{
title: "城市",
dataIndex: "city_identity",
},
];

View File

@ -0,0 +1,79 @@
import React from "react";
import { Button, Space, Modal, FormInstance } from "antd";
import { Store } from "antd/lib/form/interface";
import { inject, observer } from "mobx-react";
import { useEffect, useState } from "react";
import BTable from "@/components/b_table";
import SimpleForm from "@/components/form/simple_form";
import { columns, formConfig } from "./config";
const Food = (props: Store) => {
const { cityLocalStore } = props;
const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
const formRef = React.useRef<FormInstance>(null);
const [userId, setId] = useState<Number | null>(null);
const [record, setRecord] = useState<any>(null);
// 获取列表数据
useEffect(() => {
cityLocalStore.getlist();
}, [cityLocalStore]);
return (
<div className="contentBox">
<Space direction="vertical" size="middle" style={{ display: "flex" }}>
<Button type="default" onClick={() => setIsModalOpen(true)}>
</Button>
<BTable
store={cityLocalStore}
scroll={{ x: "max-content" }}
columns={columns}
dataSource={cityLocalStore.list}
deleteCallback={(record) => {
cityLocalStore.deleteItem(record);
}}
editCallback={(record) => {
setIsModalOpen(true);
let data = {...record}
data.cover = [{url:record.cover}]
formRef.current?.setFieldsValue(data);
setRecord(data);
setId(data.id);
}}
/>
<Modal
title={!userId ? "添加" : "编辑"}
width="80%"
open={isModalOpen}
afterClose={() => formRef.current?.resetFields()}
onOk={() => formRef.current?.submit()}
okText="确定"
cancelText="取消"
onCancel={() => {
setId(null);
setRecord(null);
setIsModalOpen(false);
}}
>
<SimpleForm
formName={"city_form"}
formRef={formRef}
colProps={25}
onFinish={() => {
let data = formRef.current?.getFieldsValue()
data.cover = data.cover[0].url
cityLocalStore.add(data)
setIsModalOpen(false);
}}
createCallback={() => {
formRef.current?.setFieldsValue(record);
}}
formDatas={formConfig as any}
></SimpleForm>
</Modal>
</Space>
</div>
);
};
export default inject("cityLocalStore")(observer(Food));

View File

@ -0,0 +1,69 @@
import { FormType } from "@/components/form/interface";
import { UserDataType } from "@/model/userModel";
import { CityConfig } from "@/service/config";
import { ColumnsType } from "antd/lib/table";
import { Image } from "antd";
export const formConfig = [
{
type: FormType.input,
label: "标题",
name: "title",
value: "",
rules: [{ required: true, message: "城市名称不能为空!" }],
},
{
type: FormType.input,
label: "副标题",
name: "sub_title",
value: "",
rules: [{ required: true, message: "城市编码不能为空" }],
},
{
type: FormType.upload,
label: "缩略图",
name: "cover",
value: [],
maxCount: 1,
rules: [{ required: true, message: "缩略图不能为空" }],
},
{
type: FormType.select,
label: "城市",
name: "city_identity",
value: "",
key: "city_name",
selectUrl: CityConfig.LIST,
rules: [{ required: true, message: "城市不能为空" }],
},
{
type: FormType.editor,
label: "内容",
name: "content",
value: "",
rules: [{ required: true, message: "城市不能为空" }],
},
];
export const columns: ColumnsType<UserDataType> = [
{
title: "标题",
dataIndex: "title",
fixed: "left",
},
{
title: "副标题",
dataIndex: "sub_title",
},
{
title: "首图",
dataIndex: "cover",
render: (text, record) => {
return <Image width={50} src={text} />;
},
},
{
title: "城市",
dataIndex: "city_identity",
},
];

View File

@ -0,0 +1,78 @@
import React from "react";
import { Button, Space, Modal, FormInstance } from "antd";
import { Store } from "antd/lib/form/interface";
import { inject, observer } from "mobx-react";
import { useEffect, useState } from "react";
import BTable from "@/components/b_table";
import SimpleForm from "@/components/form/simple_form";
import { columns, formConfig } from "./config";
const History = (props: Store) => {
const { cityhisStore } = props;
const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
const formRef = React.useRef<FormInstance>(null);
const [userId, setId] = useState<Number | null>(null);
const [record, setRecord] = useState<any>(null);
// 获取列表数据
useEffect(() => {
cityhisStore.getlist();
}, [cityhisStore]);
return (
<div className="contentBox">
<Space direction="vertical" size="middle" style={{ display: "flex" }}>
<Button type="default" onClick={() => setIsModalOpen(true)}>
</Button>
<BTable
store={cityhisStore}
scroll={{ x: "max-content" }}
columns={columns}
dataSource={cityhisStore.list}
deleteCallback={(record) => {
cityhisStore.deleteItem(record);
}}
editCallback={(record) => {
let data = {...record}
data.cover = [{url:record.cover}]
formRef.current?.setFieldsValue(data);
setRecord(record);
setId(record.id);
}}
/>
<Modal
title={!userId ? "添加" : "编辑"}
width={800}
open={isModalOpen}
afterClose={() => formRef.current?.resetFields()}
onOk={() => formRef.current?.submit()}
okText="确定"
cancelText="取消"
onCancel={() => {
setId(null);
setRecord(null);
setIsModalOpen(false);
}}
>
<SimpleForm
formName={"city_form"}
formRef={formRef}
colProps={25}
onFinish={() => {
let data = formRef.current?.getFieldsValue();
data.cover = data.cover[0].url;
cityhisStore.add(data);
setIsModalOpen(false);
}}
createCallback={() => {
formRef.current?.setFieldsValue(record);
}}
formDatas={formConfig as any}
></SimpleForm>
</Modal>
</Space>
</div>
);
};
export default inject("cityhisStore")(observer(History));

View File

@ -0,0 +1,69 @@
import { FormType } from "@/components/form/interface";
import { UserDataType } from "@/model/userModel";
import { CityConfig } from "@/service/config";
import { ColumnsType } from "antd/lib/table";
import { Image } from "antd";
export const formConfig = [
{
type: FormType.input,
label: "标题",
name: "title",
value: "",
rules: [{ required: true, message: "城市名称不能为空!" }],
},
{
type: FormType.input,
label: "副标题",
name: "sub_title",
value: "",
rules: [{ required: true, message: "城市编码不能为空" }],
},
{
type: FormType.upload,
label: "缩略图",
name: "cover",
value: [],
maxCount: 1,
rules: [{ required: true, message: "缩略图不能为空" }],
},
{
type: FormType.select,
label: "城市",
name: "city_identity",
value: "",
key: "city_name",
selectUrl: CityConfig.LIST,
rules: [{ required: true, message: "城市不能为空" }],
},
{
type: FormType.editor,
label: "内容",
name: "content",
value: "",
rules: [{ required: true, message: "城市不能为空" }],
},
];
export const columns: ColumnsType<UserDataType> = [
{
title: "标题",
dataIndex: "title",
fixed: "left",
},
{
title: "副标题",
dataIndex: "sub_title",
},
{
title: "首图",
dataIndex: "cover",
render: (text, record) => {
return <Image width={50} src={text} />;
},
},
{
title: "城市",
dataIndex: "city_identity",
},
];

View File

@ -0,0 +1,79 @@
import React from "react";
import { Button, Space, Modal, FormInstance } from "antd";
import { Store } from "antd/lib/form/interface";
import { inject, observer } from "mobx-react";
import { useEffect, useState } from "react";
import BTable from "@/components/b_table";
import SimpleForm from "@/components/form/simple_form";
import { columns, formConfig } from "./config";
const HumIntro = (props: Store) => {
const { cityhumStore } = props;
const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
const formRef = React.useRef<FormInstance>(null);
const [userId, setId] = useState<Number | null>(null);
const [record, setRecord] = useState<any>(null);
// 获取列表数据
useEffect(() => {
cityhumStore.getlist();
}, [cityhumStore]);
return (
<div className="contentBox">
<Space direction="vertical" size="middle" style={{ display: "flex" }}>
<Button type="default" onClick={() => setIsModalOpen(true)}>
</Button>
<BTable
store={cityhumStore}
scroll={{ x: "max-content" }}
columns={columns}
dataSource={cityhumStore.list}
deleteCallback={(record) => {
cityhumStore.deleteItem(record);
}}
editCallback={(record) => {
setIsModalOpen(true);
let data = {...record}
data.cover = [{url:record.cover}]
formRef.current?.setFieldsValue(data);
setRecord(record);
setId(record.id);
}}
/>
<Modal
title={!userId ? "添加" : "编辑"}
width={800}
open={isModalOpen}
afterClose={() => formRef.current?.resetFields()}
onOk={() => formRef.current?.submit()}
okText="确定"
cancelText="取消"
onCancel={() => {
setId(null);
setRecord(null);
setIsModalOpen(false);
}}
>
<SimpleForm
formName={"city_form"}
formRef={formRef}
colProps={25}
onFinish={() => {
let data = formRef.current?.getFieldsValue();
data.cover = data.cover[0].url;
cityhumStore.add(data);
setIsModalOpen(false);
}}
createCallback={() => {
formRef.current?.setFieldsValue(record);
}}
formDatas={formConfig as any}
></SimpleForm>
</Modal>
</Space>
</div>
);
};
export default inject("cityhumStore")(observer(HumIntro));

View File

@ -1,76 +1,11 @@
import React from "react"; import { Outlet } from "react-router";
import { Button, Space, Modal, FormInstance } from "antd";
import { Store } from "antd/lib/form/interface";
import { inject, observer } from "mobx-react";
import { useEffect, useState } from "react";
import { columns, formConfig } from "./config";
import BTable from "@/components/b_table";
import SimpleForm from "@/components/form/simple_form";
const City = (props: Store) => {
const { cityStore } = props;
const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
const formRef = React.useRef<FormInstance>(null);
const [userId, setId] = useState<Number | null>(null);
const [record, setRecord] = useState<any>(null);
// 获取列表数据
useEffect(() => {
cityStore.getlist();
}, [cityStore]);
const City = () => {
return ( return (
<div className="contentBox"> <div>
<Space direction="vertical" size="middle" style={{ display: "flex" }}> <Outlet />
<Button type="default" onClick={() => setIsModalOpen(true)}>
</Button>
<BTable
store={cityStore}
scroll={{ x: "max-content" }}
columns={columns}
dataSource={cityStore.list}
deleteCallback={(record) => {
cityStore.deleteItem(record);
}}
editCallback={(record) => {
setIsModalOpen(true);
formRef.current?.setFieldsValue(record);
setRecord(record);
setId(record.id);
}}
/>
<Modal
title={!userId ? "添加" : "编辑"}
width={800}
open={isModalOpen}
afterClose={() => formRef.current?.resetFields()}
onOk={() => formRef.current?.submit()}
okText="确定"
cancelText="取消"
onCancel={() => {
setId(null);
setRecord(null);
setIsModalOpen(false);
}}
>
<SimpleForm
formName={"city_form"}
formRef={formRef}
colProps={25}
onFinish={() => {
cityStore.add(formRef.current?.getFieldsValue())
setIsModalOpen(false);
}}
createCallback={() => {
formRef.current?.setFieldsValue(record);
}}
formDatas={formConfig as any}
></SimpleForm>
</Modal>
</Space>
</div> </div>
); );
}; };
export default inject("cityStore")(observer(City)); export default City;

75
src/pages/city/list.tsx Normal file
View File

@ -0,0 +1,75 @@
import React from "react";
import { Button, Space, Modal, FormInstance } from "antd";
import { Store } from "antd/lib/form/interface";
import { inject, observer } from "mobx-react";
import { useEffect, useState } from "react";
import { columns, formConfig } from "./config";
import BTable from "@/components/b_table";
import SimpleForm from "@/components/form/simple_form";
const City = (props: Store) => {
const { cityStore } = props;
const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
const formRef = React.useRef<FormInstance>(null);
const [userId, setId] = useState<Number | null>(null);
const [record, setRecord] = useState<any>(null);
// 获取列表数据
useEffect(() => {
cityStore.getlist();
}, [cityStore]);
return (
<div className="contentBox">
<Space direction="vertical" size="middle" style={{ display: "flex" }}>
<Button type="default" onClick={() => setIsModalOpen(true)}>
</Button>
<BTable
store={cityStore}
scroll={{ x: "max-content" }}
columns={columns}
dataSource={cityStore.list}
deleteCallback={(record) => {
cityStore.deleteItem(record);
}}
editCallback={(record) => {
setIsModalOpen(true);
formRef.current?.setFieldsValue(record);
setRecord(record);
setId(record.id);
}}
/>
<Modal
title={!userId ? "添加" : "编辑"}
width={800}
open={isModalOpen}
afterClose={() => formRef.current?.resetFields()}
onOk={() => formRef.current?.submit()}
okText="确定"
cancelText="取消"
onCancel={() => {
setId(null);
setRecord(null);
setIsModalOpen(false);
}}
>
<SimpleForm
formName={"city_form"}
formRef={formRef}
colProps={25}
onFinish={() => {
cityStore.add(formRef.current?.getFieldsValue())
setIsModalOpen(false);
}}
createCallback={() => {
formRef.current?.setFieldsValue(record);
}}
formDatas={formConfig as any}
></SimpleForm>
</Modal>
</Space>
</div>
);
};
export default inject("cityStore")(observer(City));

View File

@ -1,29 +1,6 @@
import { FormType } from "@/components/form/interface";
import { UserDataType } from "@/model/userModel"; import { UserDataType } from "@/model/userModel";
import { ColumnsType } from "antd/lib/table"; import { ColumnsType } from "antd/lib/table";
export const formConfig = [ import { Image } from "antd";
{
type: FormType.input,
label: "商品名称",
name: "tag_name",
value: "",
rules: [{ required: true, message: "标签名称不能为空!" }],
},
{
type: FormType.input,
label: "标签描述",
name: "tag_desc",
value: "",
rules: [{ required: true, message: "城市编码不能为空" }],
},
{
type: FormType.input,
label: "标签排序",
name: "tag_sort",
value: "1",
rules: [{ required: true, message: "标签排序不能为空" }],
},
];
export const columns: ColumnsType<UserDataType> = [ export const columns: ColumnsType<UserDataType> = [
{ {
@ -31,13 +8,12 @@ export const columns: ColumnsType<UserDataType> = [
dataIndex: "sku_name", dataIndex: "sku_name",
fixed: "left", fixed: "left",
}, },
{
title: "库存",
dataIndex: "sku_stock",
},
{ {
title: "商品分类", title: "商品分类",
dataIndex: "sku_cat_identity", dataIndex: "sku_cat",
render: (text, record) => {
return <span>{text.name}</span>;
},
}, },
{ {
title: "品牌", title: "品牌",
@ -46,6 +22,9 @@ export const columns: ColumnsType<UserDataType> = [
{ {
title: "缩略图", title: "缩略图",
dataIndex: "sku_thumb", dataIndex: "sku_thumb",
render: (text, record) => {
return <Image width={50} src={text} />;
},
}, },
{ {
title: "商品介绍", title: "商品介绍",
@ -53,6 +32,9 @@ export const columns: ColumnsType<UserDataType> = [
}, },
{ {
title: "所属城市", title: "所属城市",
dataIndex: "city_identity", dataIndex: "city",
render: (text, record) => {
return <span>{text.city_name}</span>;
},
}, },
]; ];

View File

@ -1,17 +1,15 @@
import React from "react"; import React from "react";
import { Button, Space, Modal, FormInstance } from "antd"; import { Button, Space } from "antd";
import { Store } from "antd/lib/form/interface"; import { Store } from "antd/lib/form/interface";
import { inject, observer } from "mobx-react"; import { inject, observer } from "mobx-react";
import { useEffect, useState } from "react"; import { useEffect } from "react";
import { columns, formConfig } from "./config"; import { columns } from "./config";
import BTable from "@/components/b_table"; import BTable from "@/components/b_table";
import SimpleForm from "@/components/form/simple_form"; import { useNavigate } from "react-router";
const Sku = (props: Store) => { const Sku = (props: Store) => {
const { skuStore } = props; const { skuStore } = props;
const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
const formRef = React.useRef<FormInstance>(null); const nav = useNavigate();
const [userId, setId] = useState<Number | null>(null);
const [record, setRecord] = useState<any>(null);
// 获取列表数据 // 获取列表数据
useEffect(() => { useEffect(() => {
@ -21,7 +19,7 @@ const Sku = (props: Store) => {
return ( return (
<div className="contentBox"> <div className="contentBox">
<Space direction="vertical" size="middle" style={{ display: "flex" }}> <Space direction="vertical" size="middle" style={{ display: "flex" }}>
<Button type="default" onClick={() => setIsModalOpen(true)}> <Button type="default" onClick={() => nav("/sku/add")}>
</Button> </Button>
<BTable <BTable
@ -33,41 +31,9 @@ const Sku = (props: Store) => {
skuStore.deleteItem(record); skuStore.deleteItem(record);
}} }}
editCallback={(record) => { editCallback={(record) => {
setIsModalOpen(true);
formRef.current?.setFieldsValue(record);
setRecord(record);
setId(record.id);
}} }}
/> />
<Modal
title={!userId ? "添加" : "编辑"}
width={800}
open={isModalOpen}
afterClose={() => formRef.current?.resetFields()}
onOk={() => formRef.current?.submit()}
okText="确定"
cancelText="取消"
onCancel={() => {
setId(null);
setRecord(null);
setIsModalOpen(false);
}}
>
<SimpleForm
formName={"tag_form"}
formRef={formRef}
colProps={25}
onFinish={() => {
skuStore.add(formRef.current?.getFieldsValue())
setIsModalOpen(false);
}}
createCallback={() => {
formRef.current?.setFieldsValue(record);
}}
formDatas={formConfig as any}
></SimpleForm>
</Modal>
</Space> </Space>
</div> </div>
); );

View File

@ -0,0 +1,147 @@
import SimpleForm from "@/components/form/simple_form";
import { Button, Form, FormInstance, InputNumber, Select, Space } from "antd";
import React, { useEffect } from "react";
import { formConfig } from "./sku_add_config";
import { inject, observer } from "mobx-react";
import { MinusCircleOutlined } from "@ant-design/icons";
import { Store } from "antd/es/form/interface";
const { Option } = Select;
const SkuAdd = (props: Store) => {
const formRef = React.useRef<FormInstance>(null);
const { skuStore } = props;
useEffect(() => {
skuStore.getSkuSpecCat();
});
const create = () => {
let data = formRef.current?.getFieldsValue();
let obj = {...data}
let files:any = []
obj.sku_thumb = data.sku_thumb[0].url;
if (data.files && data.files.length > 0) {
data.files.forEach((v)=>{
if(v.url){
files.push({
file_name:v.fileName,
file_url:v.url,
file_type:1,
})
}
})
}
obj.files = files
skuStore.add(obj);
};
return (
<div style={{ margin: "0 auto", overflowX: "auto", height: "80%" }}>
<div
style={{
padding: "10px",
marginLeft: 18,
marginBottom: 10,
fontSize: "1.2rem",
display: "flex",
justifyContent: "space-between",
}}
>
<span></span>
<Button onClick={() => formRef.current?.submit()}></Button>
</div>
<SimpleForm
formName={"tag_form"}
formRef={formRef}
colProps={20}
span={2}
childrenPosi={true}
onFinish={() => create()}
createCallback={() => {
// formRef.current?.setFieldsValue(record);
}}
formDatas={formConfig as any}
>
<Form.List name="sku_price" initialValue={[{}]}>
{(fields, { add, remove }) => {
return (
<>
{fields.map(({ key, name }) => (
<Space
key={key}
style={{
display: "flex",
border: "1px solid #f3f3f3",
padding: "10px",
margin: "10px",
alignItems: "center",
}}
>
<Form.Item
label="规格"
style={{ margin: "0px" }}
labelCol={{ span: 10, offset: 0 }}
name={[name, "sku_spec_id"]}
rules={[{ required: true, message: "规格" }]}
>
<Select placeholder="" style={{ width: "100px" }}>
{skuStore.skuSpec?.map((v: any) => {
return (
<Option key={v.identity} value={v.identity}>
{v.spec_name}
</Option>
);
})}
</Select>
</Form.Item>
<Form.Item
label="零售价"
style={{ margin: "0px" }}
labelCol={{ span: 12, offset: 0 }}
name={[name, "sku_price"]}
rules={[{ required: true, message: "零售价" }]}
>
<InputNumber placeholder="零售价" />
</Form.Item>
<Form.Item
label="优惠价"
name={[name, "sku_sale_price"]}
style={{ margin: "0px" }}
labelCol={{ span: 12, offset: 0 }}
rules={[{ required: true, message: "优惠价" }]}
>
<InputNumber placeholder="优惠价" />
</Form.Item>
<Form.Item
name={[name, "sku_stock"]}
style={{ margin: "0px" }}
labelCol={{ span: 12, offset: 0 }}
label="库存"
rules={[{ required: true, message: "库存" }]}
>
<InputNumber placeholder="库存" />
</Form.Item>
<MinusCircleOutlined
onClick={() => remove(name)}
style={{ marginLeft: "20px" }}
/>
</Space>
))}
<Form.Item style={{ marginTop: "10px" }}>
<Button
type="dashed"
onClick={() => {
add();
}}
>
</Button>
</Form.Item>
</>
);
}}
</Form.List>
</SimpleForm>
</div>
);
};
export default inject("skuStore")(observer(SkuAdd));

View File

@ -0,0 +1,62 @@
import { FormType } from "@/components/form/interface";
import { CityConfig, SkuCatConfig, TagConfig } from "@/service/config";
export const formConfig = [
{
type: FormType.input,
label: "商品名称",
name: "sku_name",
value: "",
rules: [{ required: true, message: "商品名称不能为空!" }],
},
{
type: FormType.select,
label: "标签",
name: "tags_id",
value: "",
key: "tag_name",
selectModel: "tags",
selectUrl: TagConfig.LIST,
rules: [{ required: true, message: "不能为空" }],
},
{
type: FormType.select,
label: "分类",
name: "sku_cat_identity",
value: "1",
selectUrl: SkuCatConfig.LIST,
rules: [{ required: true, message: "分类不能为空" }],
},
{
type: FormType.upload,
label: "缩略图",
name: "sku_thumb",
value: [],
maxCount: 1,
rules: [{ required: true, message: "缩略图不能为空" }],
},
{
type: FormType.select,
label: "城市",
name: "city_identity",
value: "",
key: "city_name",
selectUrl: CityConfig.LIST,
rules: [{ required: true, message: "城市不能为空" }],
},
{
type: FormType.upload,
label: "附件图",
name: "files",
value: [],
maxCount: 10,
rules: [{ required: true, message: "附件图不能为空" }],
},
{
type: FormType.textarea,
label: "描述",
name: "sku_desc",
rules: [{ required: true, message: "描述不能为空" }],
},
];

View File

@ -3,6 +3,7 @@ import LayOut from "@/components/layout/layout";
import { rbac } from "./routers/rbac_router"; import { rbac } from "./routers/rbac_router";
import { sku } from "./routers/sku_router"; import { sku } from "./routers/sku_router";
import { sys } from "./routers/sys_router"; import { sys } from "./routers/sys_router";
import { city } from "./routers/city_router";
const routers = createHashRouter([ const routers = createHashRouter([
{ {
@ -26,6 +27,7 @@ const routers = createHashRouter([
...rbac, ...rbac,
...sku, ...sku,
...sys, ...sys,
...city,
{ {
path: "/order/list", path: "/order/list",
index: true, index: true,
@ -33,13 +35,7 @@ const routers = createHashRouter([
Component:(await import("@/pages/order")).default, Component:(await import("@/pages/order")).default,
}) })
}, },
{
path: "/city/list",
index: true,
lazy: async() => ({
Component:(await import("@/pages/city")).default,
})
},
], ],
}, },
{ {

View File

@ -0,0 +1,38 @@
export const city = [
{
path: "/city",
lazy: async () => ({
Component: (await import("@/pages/city")).default,
}),
children: [
{
path: "/city/list",
index: true,
lazy: async () => ({
Component: (await import("@/pages/city/list")).default,
}),
},
{
path: "/city/history",
index: true,
lazy: async () => ({
Component: (await import("@/pages/city/history")).default,
}),
},
{
path: "/city/hum",
index: true,
lazy: async () => ({
Component: (await import("@/pages/city/hum_intro")).default,
}),
},
{
path: "/city/food",
index: true,
lazy: async () => ({
Component: (await import("@/pages/city/food")).default,
}),
},
],
},
];

View File

@ -12,6 +12,13 @@ export const sku = [
Component: (await import("@/pages/sku/sku")).default, Component: (await import("@/pages/sku/sku")).default,
}), }),
}, },
{
path: "/sku/add",
index: true,
lazy: async () => ({
Component: (await import("@/pages/sku/sku/sku_add")).default,
}),
},
{ {
path: "/sku/cat", path: "/sku/cat",
index: true, index: true,

View File

@ -14,6 +14,26 @@ class CityConfig {
static DELETE: string = "/city"; static DELETE: string = "/city";
} }
class CityLocalFoodConfig {
static ADD: string = "/cityFood";
static EDIT: string = "/cityFood";
static LIST: string = "/cityFood/list";
static DELETE: string = "/cityFood";
}
class CityHistoryConfig {
static ADD: string = "/cityHistory";
static EDIT: string = "/cityHistory";
static LIST: string = "/cityHistory/list";
static DELETE: string = "/cityHistory";
}
class CityHumIntroConfig {
static ADD: string = "/cityHumIntro";
static EDIT: string = "/cityHumIntro";
static LIST: string = "/cityHumIntro/list";
static DELETE: string = "/cityHumIntro";
}
class SkuConfig { class SkuConfig {
static ADD: string = "/sku"; static ADD: string = "/sku";
@ -49,4 +69,4 @@ class SpecConfig {
static LIST: string = "/skuSpec/list"; static LIST: string = "/skuSpec/list";
static DELETE: string = "/skuSpec"; static DELETE: string = "/skuSpec";
} }
export { UserConfig, CityConfig, SkuCatConfig, SkuConfig, Orderconfig, TagConfig, SpecConfig }; export { UserConfig, CityConfig, SkuCatConfig, SkuConfig, Orderconfig, TagConfig, SpecConfig, CityHistoryConfig, CityHumIntroConfig, CityLocalFoodConfig };

View File

@ -1,7 +1,7 @@
import { makeObservable } from "mobx"; import { makeObservable } from "mobx";
import BaseStore from "./baseStore"; import BaseStore from "./baseStore";
import { UserDataType } from "@/model/userModel"; import { UserDataType } from "@/model/userModel";
import { CityConfig } from "@/service/config"; import { CityConfig, CityHistoryConfig, CityHumIntroConfig, CityLocalFoodConfig } from "@/service/config";
class CityStore extends BaseStore<UserDataType> { class CityStore extends BaseStore<UserDataType> {
constructor() { constructor() {
@ -10,4 +10,28 @@ class CityStore extends BaseStore<UserDataType> {
} }
} }
const cityStore = new CityStore(); const cityStore = new CityStore();
export default cityStore;
class CityHisStore extends BaseStore<UserDataType> {
constructor() {
super(CityHistoryConfig)
makeObservable(this, {})
}
}
const cityhisStore = new CityHisStore();
class CityHumStore extends BaseStore<UserDataType> {
constructor() {
super(CityHumIntroConfig)
makeObservable(this, {})
}
}
const cityhumStore = new CityHumStore();
class CityLocalStore extends BaseStore<UserDataType> {
constructor() {
super(CityLocalFoodConfig)
makeObservable(this, {})
}
}
const cityLocalStore = new CityLocalStore();
export { cityStore, cityLocalStore, cityhisStore, cityhumStore };

View File

@ -1,5 +1,5 @@
import usrStore from '@/store/user' import usrStore from '@/store/user'
import cityStore from '@/store/city' import { cityStore, cityLocalStore, cityhisStore, cityhumStore } from '@/store/city'
import orderStore from './order'; import orderStore from './order';
import tagStore from './tag'; import tagStore from './tag';
import skuStore from './sku'; import skuStore from './sku';
@ -13,7 +13,10 @@ const store = {
tagStore, tagStore,
skuStore, skuStore,
skuCatStore, skuCatStore,
skuSpecStore skuSpecStore,
cityLocalStore,
cityhisStore,
cityhumStore
}; };
export default store; export default store;

View File

@ -1,13 +1,28 @@
import { makeObservable } from "mobx"; import { action, makeObservable, observable } from "mobx";
import BaseStore from "./baseStore"; import BaseStore from "./baseStore";
import { UserDataType } from "@/model/userModel"; import { UserDataType } from "@/model/userModel";
import { SkuConfig } from "@/service/config"; import { SkuConfig, SpecConfig } from "@/service/config";
import { base } from "@/service/base";
import { message } from "antd";
class SkuStore extends BaseStore<UserDataType> { class SkuStore extends BaseStore<UserDataType> {
skuSpec = []
constructor() { constructor() {
super(SkuConfig) super(SkuConfig)
makeObservable(this, {}) makeObservable(this, {
skuSpec: observable,
getSkuSpecCat: action,
})
} }
// 获取规格
async getSkuSpecCat() {
let data = await base.get(SpecConfig.LIST, { size: 30, offset: 1 })
if (data.code !== 200) {
message.error(data.msg)
return false
}
this.skuSpec = data.data.record
};
} }
const skuStore = new SkuStore(); const skuStore = new SkuStore();
export default skuStore; export default skuStore;

View File

@ -1,5 +1,5 @@
class Config { class Config {
static baseUrl = "/v1"; static baseUrl = "/v1";
// static baseUrl = "http://127.0.0.1:12215/v1"; static baseUrl1 = "http://127.0.0.1:12215/v1";
} }
export default Config; export default Config;