|
| 1 | +import React, { |
| 2 | + useContext, |
| 3 | + useEffect, |
| 4 | + useImperativeHandle, |
| 5 | + useMemo, |
| 6 | + useRef, |
| 7 | +} from "react"; |
| 8 | +import { Button, Popconfirm, Space, Table, App, Tag } from "antd"; |
| 9 | +import { red, green } from "@ant-design/colors"; |
| 10 | +import { getRESTfulApi } from "../../api"; |
| 11 | +import JsonForm from "../Forms/Json"; |
| 12 | +import { jsonFormatValue, jsonParse } from "../../utils"; |
| 13 | +import { |
| 14 | + CheckCircleOutlined, |
| 15 | + CopyOutlined, |
| 16 | + DeleteOutlined, |
| 17 | + EditOutlined, |
| 18 | + StopOutlined, |
| 19 | +} from "@ant-design/icons"; |
| 20 | +import { GostCommit } from "../../api/local"; |
| 21 | +import Ctx, { CardCtx } from "../../utils/ctx"; |
| 22 | +import { useListData1, UseTemplates } from "../ListCard/hooks"; |
| 23 | +import { configEvent } from "../../utils/events"; |
| 24 | +import { useTranslation } from "react-i18next"; |
| 25 | +import { PublicListProps } from "./Public"; |
| 26 | +const showJsonForm = JsonForm.show; |
| 27 | + |
| 28 | +const statusColors: Record<string, string> = { |
| 29 | + running: "gold", |
| 30 | + ready: "green", |
| 31 | + failed: "red", |
| 32 | +}; |
| 33 | + |
| 34 | +// export type PublicListProps = { |
| 35 | +// name: string; |
| 36 | +// title: string; |
| 37 | +// api: ReturnType<typeof getRESTfulApi>; |
| 38 | +// localApi?: GostCommit; |
| 39 | +// keyName: string; |
| 40 | +// rowKey?: string; |
| 41 | +// keyword?: string; |
| 42 | +// renderConfig?: (v: any, r: any, i: number) => React.ReactNode; |
| 43 | +// filter?: (item: any, keyord: string) => boolean; |
| 44 | +// }; |
| 45 | + |
| 46 | +export const UpdateCtx = React.createContext<{ update?: (v?: any) => any }>({}); |
| 47 | + |
| 48 | +const defaultRenderConfig = (value: any, record: any, index: number) => { |
| 49 | + return JSON.stringify(record); |
| 50 | +}; |
| 51 | + |
| 52 | +const defFilter = (item: any, keyord: string) => { |
| 53 | + return item?.name?.toLowerCase()?.indexOf(keyord) !== -1; |
| 54 | +}; |
| 55 | + |
| 56 | +const ServiceList: React.FC<PublicListProps> = (props) => { |
| 57 | + const { |
| 58 | + name, |
| 59 | + title, |
| 60 | + api, |
| 61 | + localApi, |
| 62 | + keyName, |
| 63 | + rowKey = "name", |
| 64 | + keyword, |
| 65 | + renderConfig = defaultRenderConfig, |
| 66 | + filter = defFilter, |
| 67 | + } = props; |
| 68 | + const { t } = useTranslation(); |
| 69 | + const { localList, comm } = useContext(CardCtx); |
| 70 | + const { gostConfig, localConfig } = useContext(Ctx); |
| 71 | + const { dataList, dataSource } = useListData1({ |
| 72 | + localApi, |
| 73 | + name: keyName, |
| 74 | + gostConfig, |
| 75 | + localConfig, |
| 76 | + }); |
| 77 | + const templates = UseTemplates({ name: keyName }); |
| 78 | + const { modal, message, notification } = App.useApp(); |
| 79 | + const { |
| 80 | + deleteValue, |
| 81 | + updateValue, |
| 82 | + disable, |
| 83 | + enable, |
| 84 | + updateLocal, |
| 85 | + deleteLocal, |
| 86 | + addValue, |
| 87 | + // } = comm.current; |
| 88 | + } = comm!; |
| 89 | + const ref = useRef<any>({ dataList, dataSource }); |
| 90 | + useImperativeHandle( |
| 91 | + ref, |
| 92 | + () => { |
| 93 | + return { |
| 94 | + dataList, |
| 95 | + dataSource, |
| 96 | + }; |
| 97 | + }, |
| 98 | + [dataList, dataSource] |
| 99 | + ); |
| 100 | + |
| 101 | + const showList = useMemo(() => { |
| 102 | + if (keyword) { |
| 103 | + return dataSource.filter((item) => { |
| 104 | + return filter(item, keyword); |
| 105 | + }); |
| 106 | + } |
| 107 | + return dataSource; |
| 108 | + }, [dataSource, filter, keyword]); |
| 109 | + useEffect(() => { |
| 110 | + function onEdit({ path, record }: { path: string; record: any }) { |
| 111 | + const { dataList, dataSource } = ref.current; |
| 112 | + const isEnable = dataList.includes(record); |
| 113 | + const key = record.name; |
| 114 | + const attrs = path.split(","); |
| 115 | + const v = attrs.reduce((a, b) => { |
| 116 | + return a?.[b]; |
| 117 | + }, record); |
| 118 | + const onup = (value: any) => { |
| 119 | + let _record = record; |
| 120 | + attrs.forEach((attr, i) => { |
| 121 | + if (i === attrs.length - 1) { |
| 122 | + _record[attr] = value; |
| 123 | + } else { |
| 124 | + _record = _record[attr]; |
| 125 | + } |
| 126 | + }); |
| 127 | + }; |
| 128 | + showJsonForm({ |
| 129 | + title: t("base.cmd.edit"), |
| 130 | + initialValues: { value: jsonFormatValue(v) }, |
| 131 | + onFinish: async (values: any) => { |
| 132 | + onup(jsonParse(values.value)); |
| 133 | + if (isEnable) { |
| 134 | + await updateValue(key, record); |
| 135 | + } else { |
| 136 | + await updateLocal(key, record); |
| 137 | + } |
| 138 | + return true; |
| 139 | + }, |
| 140 | + }); |
| 141 | + } |
| 142 | + configEvent.on(`edit:${name}`, onEdit); |
| 143 | + return () => { |
| 144 | + configEvent.off(`edit:${name}`, onEdit); |
| 145 | + }; |
| 146 | + }, [name, t, updateLocal, updateValue]); |
| 147 | + |
| 148 | + return ( |
| 149 | + <div style={{ height: 348, overflow: "auto" }}> |
| 150 | + <Table |
| 151 | + rowKey={(obj) => obj._id_ || obj.name} |
| 152 | + scroll={{ y: 290 }} |
| 153 | + size="small" |
| 154 | + dataSource={showList} |
| 155 | + columns={[ |
| 156 | + { |
| 157 | + title: t("base.form.name"), |
| 158 | + dataIndex: rowKey, |
| 159 | + ellipsis: true, |
| 160 | + width: 150, |
| 161 | + }, |
| 162 | + { |
| 163 | + title: t("base.form.details"), |
| 164 | + ellipsis: true, |
| 165 | + render: (value, record, index) => { |
| 166 | + const isEnable = dataList.includes(record); |
| 167 | + const update = isEnable |
| 168 | + ? (value?: any) => updateValue(record.name, value || record) |
| 169 | + : (value?: any) => updateLocal(record.name, value || record); |
| 170 | + let render: React.ReactNode; |
| 171 | + try { |
| 172 | + render = renderConfig(value, record, index); |
| 173 | + } catch (e) { |
| 174 | + render = defaultRenderConfig(value, record, index); |
| 175 | + } |
| 176 | + return ( |
| 177 | + <UpdateCtx.Provider value={{ update }}> |
| 178 | + {render} |
| 179 | + </UpdateCtx.Provider> |
| 180 | + ); |
| 181 | + }, |
| 182 | + }, |
| 183 | + { |
| 184 | + title: t("base.form.status"), |
| 185 | + width: 80, |
| 186 | + // ellipsis: true, |
| 187 | + render: (value, record, index) => { |
| 188 | + const isEnable = dataList.includes(record); |
| 189 | + const status = isEnable ? record?.status?.state : 'disable'; |
| 190 | + if (status) { |
| 191 | + // const color = status.state === "running" ? "green" : "red"; |
| 192 | + return ( |
| 193 | + <Tag color={statusColors[status as string]}> |
| 194 | + {status} |
| 195 | + </Tag> |
| 196 | + ); |
| 197 | + } |
| 198 | + return "-"; |
| 199 | + }, |
| 200 | + }, |
| 201 | + { |
| 202 | + title: t("base.cmd.controls"), |
| 203 | + width: localApi ? 120 : 90, |
| 204 | + align: "right", |
| 205 | + dataIndex: rowKey, |
| 206 | + render: (value, record, index) => { |
| 207 | + // console.log("render", record); |
| 208 | + const isEnable = dataList.includes(record); |
| 209 | + const content = { ...record }; |
| 210 | + delete content.status; |
| 211 | + return ( |
| 212 | + <Space size={2}> |
| 213 | + {localApi ? ( |
| 214 | + isEnable ? ( |
| 215 | + <Button |
| 216 | + title={t("base.cmd.disable")} |
| 217 | + icon={ |
| 218 | + <CheckCircleOutlined |
| 219 | + style={{ color: green.primary }} |
| 220 | + /> |
| 221 | + } |
| 222 | + type="link" |
| 223 | + size={"small"} |
| 224 | + onClick={async () => { |
| 225 | + await disable(record); |
| 226 | + }} |
| 227 | + > |
| 228 | + {/* 禁用 */} |
| 229 | + </Button> |
| 230 | + ) : ( |
| 231 | + <Button |
| 232 | + title={t("base.cmd.enabled")} |
| 233 | + type="link" |
| 234 | + icon={<StopOutlined style={{ color: red.primary }} />} |
| 235 | + size={"small"} |
| 236 | + onClick={async () => { |
| 237 | + await enable(record); |
| 238 | + }} |
| 239 | + > |
| 240 | + {/* 启用 */} |
| 241 | + </Button> |
| 242 | + ) |
| 243 | + ) : null} |
| 244 | + <Button |
| 245 | + title={t("base.cmd.edit")} |
| 246 | + icon={<EditOutlined />} |
| 247 | + type="link" |
| 248 | + size={"small"} |
| 249 | + onClick={() => { |
| 250 | + showJsonForm({ |
| 251 | + title: t("title.edit", { name: value || "" }), |
| 252 | + templates: templates, |
| 253 | + initialValues: { value: jsonFormatValue(content) }, |
| 254 | + onFinish: async (values: any) => { |
| 255 | + const { value } = values; |
| 256 | + const json = jsonParse(value); |
| 257 | + if (isEnable) { |
| 258 | + await updateValue(record.name, json); |
| 259 | + } else { |
| 260 | + await updateLocal(record.name, { |
| 261 | + ...content, |
| 262 | + ...json, |
| 263 | + }); |
| 264 | + } |
| 265 | + return true; |
| 266 | + }, |
| 267 | + }); |
| 268 | + }} |
| 269 | + /> |
| 270 | + <Button |
| 271 | + title={t("base.cmd.copy")} |
| 272 | + icon={<CopyOutlined />} |
| 273 | + type="link" |
| 274 | + size={"small"} |
| 275 | + onClick={() => { |
| 276 | + showJsonForm({ |
| 277 | + title: t("title.copied", { name: value || "" }), |
| 278 | + templates: templates, |
| 279 | + initialValues: { value: jsonFormatValue(content) }, |
| 280 | + onFinish: async (values: any) => { |
| 281 | + const { value } = values; |
| 282 | + const json = jsonParse(value); |
| 283 | + await comm!.addValue(json); |
| 284 | + return true; |
| 285 | + }, |
| 286 | + }); |
| 287 | + }} |
| 288 | + /> |
| 289 | + <Popconfirm |
| 290 | + title={t("text.warn")} |
| 291 | + description={t("text.deleteing")} |
| 292 | + onConfirm={() => { |
| 293 | + if (isEnable) { |
| 294 | + deleteValue(record); |
| 295 | + } else { |
| 296 | + deleteLocal(record); |
| 297 | + } |
| 298 | + }} |
| 299 | + > |
| 300 | + <Button |
| 301 | + title={t("base.cmd.del")} |
| 302 | + icon={<DeleteOutlined />} |
| 303 | + type="link" |
| 304 | + size={"small"} |
| 305 | + /> |
| 306 | + </Popconfirm> |
| 307 | + </Space> |
| 308 | + ); |
| 309 | + }, |
| 310 | + }, |
| 311 | + ]} |
| 312 | + pagination={false} |
| 313 | + ></Table> |
| 314 | + </div> |
| 315 | + ); |
| 316 | +}; |
| 317 | +export default ServiceList; |
0 commit comments