import {Button, Drawer, Input, Modal, Popover, Space, Switch, Table} from "antd"; import {ColumnsType} from "antd/es/table"; import { AdvanceInputConfigs, AutoForm, AutoFormInstance, AutoTypeInputProps, isNull, Message, uniqueKey } from "planning-tools"; import {useEffect, useRef, useState} from "react"; import { useTranslation } from 'react-i18next' import { ArrowDownOutlined, ArrowUpOutlined, CopyOutlined, DeleteOutlined, EditOutlined, PlusOutlined } from "@ant-design/icons"; import {INginxLocation} from "../../../../models/nginx.ts"; import {cloneDeep} from "lodash"; import FormConfig from './config.json' import {SiteInput} from "../site"; import {renderLocation} from "./utils.ts"; import './index.less' /** * 部分的重要信息 * @param data * @param onChange * @constructor */ const LocationInfo = ({data, onChange}:{ data: INginxLocation, onChange?: (data: INginxLocation) => void})=>{ const rootDir = ()=>{ if (data.alias){ return `alias: ${data.alias}` } return `root: ${data.root || '--'}` } return (
{ data.proxy_type === 'proxy' ?
{`proxy: ${data.proxy_pass}`}
: null } { data.proxy_type === 'static' ?
{rootDir()}
:null }
{ data.rewrite?.regex && data.rewrite?.replacement ? `${data.rewrite.regex} ${data.rewrite.replacement}` : '' }
) } /** * 路由,站点,规则编辑 * @param value * @param onChange * @param column * @constructor */ export const LocationInput = ({value, onChange }: AutoTypeInputProps) => { const [locations, setLocations] = useState([]) const [editData, setEditData] = useState() const isAddRef = useRef(false) const [modal,contextHolder] = Modal.useModal() const {t} = useTranslation() const formRef = useRef() useEffect(() => { if (Array.isArray(value)) { setLocations(value.map((item: INginxLocation) => { if (!item.id) { item.id = uniqueKey(20) } if (!item.lines){ renderLocation(item) } return item })) } }, [value]) const onEditRow = (data: INginxLocation) => { isAddRef.current = false setEditData(cloneDeep(data)) } const onAddData = (data?: INginxLocation, index?: number)=>{ isAddRef.current = true setEditData({ ...data,id: uniqueKey(20),__index__: index} as never) } const moveUp = (data: INginxLocation, index: number) => { if (index == 0){ Message.warning(t('location.is_first')) return } const current = locations[index-1] locations[index-1] = data locations[index] = current setLocations([...locations]) onChange?.(locations) } const moveDown = (data: INginxLocation,index: number)=>{ if (index == locations.length-1){ Message.warning(t('location.is_last')) return } const current = locations[index+1] locations[index+1] = data locations[index] = current setLocations([...locations]) onChange?.(locations) } const onRemoveData = (data: INginxLocation)=>{ const onOk = ()=>{ const list = locations.filter(item=>item.id !== data.id); onChange?.(list) } modal.confirm({ title: '提示', type: 'warning', content: '您确定要删除该代理/站点吗?删除操作不可恢复,请谨慎操作!', okType: 'danger', okText: '仍要删除', cancelText: '先不了', onOk, }) } const onQuickChangeStatus = (data: INginxLocation, enable: boolean) => { const list = locations.map(item=>{ if (item.id === data.id){ return { ...item, enable } } return item }) onChange?.(list) } const onSubmitData = async () => { const values = await formRef.current?.onSyncSubmit(true); const newData = {...editData, ...values} as INginxLocation; console.log('newLocation', newData); if (!editData) { console.warn('editData is null ,skip '); return } renderLocation(newData) let list: INginxLocation[] if (isAddRef.current){ const index = newData.__index__ || locations.length; delete newData.__index__; let exist = locations.find(item=>item.name == newData.name); if (exist){ Message.warning('名称不能相同,请修改后再保存!'); return } exist = locations.find(item=> { if (item.match && newData.match){ return item.match.regex === newData.match.regex && item.match.path === newData.match.path } return false }); if (exist){ Message.warning('匹配规则不能完全一样,请修改后重新添加!'); return } renderLocation(newData); if (isNull(index) || index < 0 || index >= locations.length-1){ list = locations.concat([newData]) }else { list = [] locations.forEach((item,idx)=>{ if (idx === index){ list.push(item) list.push(newData) }else { list.push(item) } }) } }else { renderLocation(newData); list = locations.map(item => { if (item.id === newData.id) { return {...item, ...newData} } return item }) } onChange?.(list) setEditData(undefined) } /** * 部署数据变化,不重新渲染 * @param data */ const onDeployDataChange = (data: INginxLocation) => { const newList = locations.map(item=>{ if (item.id === data.id){ return { ...item, ...data} } return item; }); onChange?.(newList) } const renderPreview = (data: INginxLocation)=>{ let content =''; let rows = 0; if (data.http?.length){ content = data.http.join('\n') + '\n'; rows = data.http.length; } if (data.lines){ content = content+ data.lines.join('\n') rows +=data.lines.length } return (
) } const renderOps = (_: never, data: INginxLocation, index: number) => { return (
moveUp(data, index)}/> moveDown(data,index)}/>
) } const columns: ColumnsType = [ { dataIndex: 'name', title: '路由名称', width: 120 }, { dataIndex: 'match', title: "规则", render: (value) => {`${value.regex || ''} ${value.path}`} }, { dataIndex: 'enable', title: '状态', render: (value,record) => onQuickChangeStatus(record as never,c)} checked={value}/> }, { dataIndex: 'proxy_pass', title: '代理或路径', render: (_,record: any)=>{ return () } }, { dataIndex: 'remark', title:"备注", }, { title: '操作', render: renderOps as never, width: 200, fixed: 'right' } ] return ( <> { locations.length ? (
Empty
) : ( <> } > {contextHolder} ) } AdvanceInputConfigs['locations'] = LocationInput