| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631 |
- import { Button, Dropdown, message, Tree } from "antd";
- import { useEffect, useState } from "react";
- import { MoreOutlined } from "@ant-design/icons";
- import { useAppSelector } from "../../hooks";
- import { mode as _mode } from "../../reducers/article-mode";
- import { post } from "../../request";
- import { ArticleMode } from "../article/Article";
- import WbwWord, {
- IWbw,
- IWbwFields,
- TWbwDisplayMode,
- WbwElement,
- WbwStatus,
- } from "./Wbw/WbwWord";
- import { TChannelType } from "../api/Channel";
- import { IDictRequest, IDictResponse, IUserDictCreate } from "../api/Dict";
- import { useIntl } from "react-intl";
- import { add } from "../../reducers/sent-word";
- import store from "../../store";
- import { settingInfo } from "../../reducers/setting";
- import { GetUserSetting } from "../auth/setting/default";
- interface IMagicDictRequest {
- book: number;
- para: number;
- word_start: number;
- word_end: number;
- data: IWbw[];
- channel_id: string;
- lang?: string[];
- }
- interface IMagicDictResponse {
- ok: boolean;
- message: string;
- data: IWbw[];
- }
- interface IWbwXml {
- id: string;
- pali: WbwElement<string>;
- real?: WbwElement<string | null>;
- type?: WbwElement<string | null>;
- gramma?: WbwElement<string | null>;
- mean?: WbwElement<string | null>;
- org?: WbwElement<string | null>;
- om?: WbwElement<string | null>;
- case?: WbwElement<string | null>;
- parent?: WbwElement<string | null>;
- pg?: WbwElement<string | null>;
- parent2?: WbwElement<string | null>;
- rela?: WbwElement<string | null>;
- lock?: boolean;
- bmt?: WbwElement<string | null>;
- bmc?: WbwElement<number | null>;
- cf: number;
- }
- interface IWbwUpdateResponse {
- ok: boolean;
- message: string;
- data: { rows?: IWbwXml[]; count: number };
- }
- interface IWbwWord {
- words: IWbwXml[];
- sn: number;
- }
- interface IWbwRequest {
- book: number;
- para: number;
- sn: number;
- channel_id: string;
- data: IWbwWord[];
- }
- interface IWidget {
- data: IWbw[];
- book: number;
- para: number;
- wordStart: number;
- wordEnd: number;
- channelId: string;
- channelType?: TChannelType;
- display?: TWbwDisplayMode;
- fields?: IWbwFields;
- layoutDirection?: "h" | "v";
- magicDict?: string;
- refreshable?: boolean;
- mode?: ArticleMode;
- onMagicDictDone?: Function;
- onChange?: Function;
- }
- export const WbwSentCtl = ({
- data,
- channelId,
- channelType,
- book,
- para,
- wordStart,
- wordEnd,
- display = "block",
- fields,
- layoutDirection = "h",
- mode,
- magicDict,
- refreshable = false,
- onChange,
- onMagicDictDone,
- }: IWidget) => {
- const intl = useIntl();
- const [wordData, setWordData] = useState<IWbw[]>(data);
- const [wbwMode, setWbwMode] = useState(display);
- const [fieldDisplay, setFieldDisplay] = useState(fields);
- const [displayMode, setDisplayMode] = useState<ArticleMode>();
- const [magic, setMagic] = useState<string>();
- const [loading, setLoading] = useState(false);
- const settings = useAppSelector(settingInfo);
- useEffect(() => {
- setMagic(magicDict);
- }, [magicDict]);
- const newMode = useAppSelector(_mode);
- const update = (data: IWbw[]) => {
- setWordData(data);
- if (typeof onChange !== "undefined") {
- onChange(data);
- }
- };
- useEffect(() => {
- if (refreshable) {
- setWordData(data);
- }
- }, [data, refreshable]);
- useEffect(() => {
- //发布句子里面的单词的变更。给术语输入菜单用。
- let words = new Map<string, number>();
- wordData
- .filter(
- (value) =>
- value.type?.value !== null &&
- value.type?.value !== ".ctl." &&
- value.real.value &&
- value.real.value.length > 0
- )
- .forEach((value) => {
- words.set(value.real.value ? value.real.value : "", 1);
- if (value.parent?.value) {
- words.set(value.parent.value, 1);
- }
- });
- let pubWords: string[] = [];
- words.forEach((value, key) => pubWords.push(key));
- const sentId = `${book}-${para}-${wordStart}-${wordEnd}`;
- store.dispatch(add({ sentId: sentId, words: pubWords }));
- }, [book, para, wordData, wordEnd, wordStart]);
- useEffect(() => {
- console.log("mode", mode);
- let currMode: ArticleMode | undefined;
- if (typeof mode !== "undefined") {
- currMode = mode;
- } else if (typeof newMode !== "undefined") {
- currMode = newMode;
- } else {
- currMode = undefined;
- }
- setDisplayMode(currMode);
- switch (currMode) {
- case "edit":
- if (typeof display === "undefined") {
- setWbwMode("block");
- }
- if (typeof fields === "undefined") {
- setFieldDisplay({
- meaning: true,
- factors: false,
- factorMeaning: false,
- case: false,
- });
- }
- break;
- case "wbw":
- if (typeof display === "undefined") {
- setWbwMode("block");
- }
- if (typeof fields === "undefined") {
- setFieldDisplay({
- meaning: true,
- factors: true,
- factorMeaning: true,
- case: true,
- });
- }
- break;
- }
- }, [newMode, mode]);
- useEffect(() => {
- if (typeof magic === "undefined") {
- return;
- }
- let _lang = GetUserSetting("setting.dict.lang", settings);
- const url = `/v2/wbwlookup`;
- console.log("magic dict url", url);
- if (typeof _lang === "object") {
- }
- post<IMagicDictRequest, IMagicDictResponse>(url, {
- book: book,
- para: para,
- word_start: wordStart,
- word_end: wordEnd,
- data: wordData,
- channel_id: channelId,
- lang: _lang?.toString().split(","),
- })
- .then((json) => {
- if (json.ok) {
- console.log("magic dict result", json.data);
- update(json.data);
- updateWbwAll(json.data);
- } else {
- console.error(json.message);
- }
- })
- .finally(() => {
- setMagic(undefined);
- setLoading(false);
- if (typeof onMagicDictDone !== "undefined") {
- onMagicDictDone();
- }
- });
- }, [magic]);
- const wbwToXml = (item: IWbw) => {
- return {
- pali: item.word,
- real: item.real,
- id: `${book}-${para}-` + item.sn.join("-"),
- type: item.type,
- gramma: item.grammar,
- mean: item.meaning
- ? {
- value: item.meaning.value,
- status: item.meaning?.status,
- }
- : undefined,
- org: item.factors,
- om: item.factorMeaning,
- case: item.case,
- parent: item.parent,
- pg: item.grammar2,
- parent2: item.parent2,
- rela: item.relation,
- lock: item.locked,
- note: item.note,
- bmt: item.bookMarkText,
- bmc: item.bookMarkColor,
- attachments: JSON.stringify(item.attachments),
- cf: item.confidence,
- };
- };
- const updateWbwAll = (wbwData: IWbw[]) => {
- let arrSn: number[] = [];
- wbwData.forEach((value) => {
- if (!arrSn.includes(value.sn[0])) {
- arrSn.push(value.sn[0]);
- }
- });
- const postParam: IWbwRequest = {
- book: book,
- para: para,
- channel_id: channelId,
- sn: wbwData[0].sn[0],
- data: arrSn.map((item) => {
- return {
- sn: item,
- words: wbwData.filter((value) => value.sn[0] === item).map(wbwToXml),
- };
- }),
- };
- post<IWbwRequest, IWbwUpdateResponse>(`/v2/wbw`, postParam).then((json) => {
- if (json.ok) {
- message.info(json.data.count + " updated");
- } else {
- message.error(json.message);
- }
- });
- };
- const saveWord = (wbwData: IWbw[], sn: number) => {
- if (channelType === "nissaya") {
- } else {
- const data = wbwData.filter((value) => value.sn[0] === sn);
- const postParam: IWbwRequest = {
- book: book,
- para: para,
- channel_id: channelId,
- sn: sn,
- data: [
- {
- sn: sn,
- words: data.map(wbwToXml),
- },
- ],
- };
- post<IWbwRequest, IWbwUpdateResponse>(`/v2/wbw`, postParam).then(
- (json) => {
- if (json.ok) {
- message.info(json.data.count + " updated");
- } else {
- message.error(json.message);
- }
- }
- );
- }
- };
- const wordSplit = (id: number, hyphen = "-") => {
- let factors = wordData[id]?.factors?.value;
- if (typeof factors === "string") {
- let sFm = wordData[id]?.factorMeaning?.value;
- if (typeof sFm === "undefined" || sFm === null) {
- sFm = new Array(factors.split("+")).fill([""]).join("+");
- }
- if (wordData[id].case?.value?.split("#")[0] === ".un.") {
- factors = `[+${factors}+]`;
- sFm = `+${sFm}+`;
- } else if (hyphen !== "") {
- factors = factors.replaceAll("+", `+${hyphen}+`);
- sFm = sFm.replaceAll("+", `+${hyphen}+`);
- }
- let fm = sFm.split("+");
- const children: IWbw[] | undefined = factors
- .split("+")
- .map((item, index) => {
- return {
- word: { value: item, status: 5 },
- real: {
- value: item
- .replaceAll("-", "")
- .replaceAll("[", "")
- .replaceAll("]", ""),
- status: 5,
- },
- meaning: { value: fm[index], status: 5 },
- book: wordData[id].book,
- para: wordData[id].para,
- sn: [...wordData[id].sn, index],
- confidence: 1,
- };
- });
- if (typeof children !== "undefined") {
- console.log("children", children);
- const newData: IWbw[] = [...wordData];
- newData.splice(id + 1, 0, ...children);
- console.log("new-data", newData);
- update(newData);
- saveWord(newData, wordData[id].sn[0]);
- }
- }
- };
- const wbwPublish = (wbwData: IWbw[]) => {
- let wordData: IDictRequest[] = [];
- wbwData.forEach((data) => {
- const [wordType, wordGrammar] = data.case?.value
- ? data.case?.value?.split("#")
- : ["", ""];
- wordData.push({
- word: data.real.value ? data.real.value : "",
- type: wordType,
- grammar: wordGrammar,
- mean: data.meaning?.value,
- parent: data.parent?.value,
- factors: data.factors?.value,
- factormean: data.factorMeaning?.value,
- confidence: data.confidence,
- });
- if (data.parent?.value && wordType !== "") {
- if (!wordType.includes("base") && wordType !== ".ind.") {
- wordData.push({
- word: data.parent.value,
- type: "." + wordType.replaceAll(".", "") + ":base.",
- grammar: wordGrammar,
- mean: data.meaning?.value,
- parent: data.parent2?.value ? data.parent2?.value : undefined,
- factors: data.factors?.value,
- factormean: data.factorMeaning?.value,
- confidence: data.confidence,
- });
- }
- }
- });
- post<IUserDictCreate, IDictResponse>("/v2/userdict", {
- view: "wbw",
- data: JSON.stringify(wordData),
- })
- .finally(() => {
- setLoading(false);
- })
- .then((json) => {
- if (json.ok) {
- message.success(
- "wbw " + intl.formatMessage({ id: "flashes.success" })
- );
- } else {
- message.error(json.message);
- }
- });
- };
- const wbwRender = (item: IWbw, id: number) => {
- return (
- <WbwWord
- data={item}
- key={id}
- mode={displayMode}
- display={wbwMode}
- fields={fieldDisplay}
- onChange={(e: IWbw, isPublish?: boolean) => {
- let newData = [...wordData];
- newData.forEach((value, index, array) => {
- //把新的数据更新到数组
- if (value.sn.join() === e.sn.join()) {
- console.log("found", e.sn);
- array[index] = e;
- }
- });
- if (e.sn.length > 1) {
- //把meaning 数据更新到 拆分前单词的factor meaning
- const factorMeaning = newData
- .filter((value) => {
- if (
- value.sn.length === e.sn.length &&
- e.sn.slice(0, e.sn.length - 1).join() ===
- value.sn.slice(0, e.sn.length - 1).join() &&
- value.real.value &&
- value.real.value.length > 0
- ) {
- return true;
- } else {
- return false;
- }
- })
- .map((item) => item.meaning?.value)
- .join("+");
- console.log("fm", factorMeaning);
- newData.forEach((value, index, array) => {
- //把新的数据更新到数组
- if (value.sn.join() === e.sn.slice(0, e.sn.length - 1).join()) {
- console.log("found", value.sn);
- array[index].factorMeaning = {
- value: factorMeaning,
- status: 5,
- };
- if (array[index].meaning?.status !== WbwStatus.manual) {
- array[index].meaning = {
- value: factorMeaning.replaceAll("+", " "),
- status: 5,
- };
- }
- }
- });
- }
- update(newData);
- saveWord(newData, e.sn[0]);
- if (isPublish) {
- wbwPublish([e]);
- }
- }}
- onSplit={() => {
- const newData: IWbw[] = JSON.parse(JSON.stringify(wordData));
- if (
- id < wordData.length - 1 &&
- wordData[id + 1].sn.join("-").indexOf(wordData[id].sn.join("-")) ===
- 0
- ) {
- //合并
- console.log("合并");
- const compactData = newData.filter((value, index) => {
- if (
- index !== id &&
- value.sn.join("-").indexOf(wordData[id].sn.join("-")) === 0
- ) {
- return false;
- } else {
- return true;
- }
- });
- update(compactData);
- saveWord(compactData, wordData[id].sn[0]);
- } else {
- //拆开
- console.log("拆开");
- wordSplit(id);
- }
- }}
- />
- );
- };
- return (
- <div className={`layout-${layoutDirection}`}>
- <Dropdown
- menu={{
- items: [
- {
- key: "magic-dict-current",
- label: "神奇字典",
- },
- {
- key: "wbw-dict-publish-all",
- label: "发布全部单词",
- },
- {
- type: "divider",
- },
- {
- key: "copy-text",
- label: intl.formatMessage({
- id: "buttons.copy.pali.text",
- }),
- },
- ],
- onClick: ({ key }) => {
- console.log(`Click on item ${key}`);
- switch (key) {
- case "magic-dict-current":
- setLoading(true);
- setMagic("curr");
- break;
- case "wbw-dict-publish-all":
- wbwPublish(wordData);
- break;
- case "copy-text":
- const paliText = wordData
- .filter((value) => value.type?.value !== ".ctl.")
- .map((item) => item.word.value)
- .join(" ");
- navigator.clipboard.writeText(paliText).then(() => {
- message.success("已经拷贝到剪贴板");
- });
- break;
- }
- },
- }}
- placement="bottomLeft"
- >
- <Button
- loading={loading}
- onClick={(e) => e.preventDefault()}
- icon={<MoreOutlined />}
- size="small"
- type="text"
- style={{ backgroundColor: "lightblue", opacity: 0.3 }}
- />
- </Dropdown>
- {layoutDirection === "h" ? (
- wordData.map((item, id) => {
- return wbwRender(item, id);
- })
- ) : (
- <Tree
- selectable={true}
- blockNode
- treeData={wordData
- .filter((value) => value.sn.length === 1)
- .map((item, id) => {
- const children = wordData.filter(
- (value) =>
- value.sn.length === 2 &&
- value.sn.slice(0, 1).join() === wordData[id].sn.join()
- );
- return {
- title: wbwRender(item, id),
- key: item.sn.join(),
- isLeaf: !item.factors?.value?.includes("+"),
- children:
- children.length > 0
- ? children.map((item, id) => {
- return {
- title: wbwRender(item, id),
- key: item.sn.join(),
- isLeaf: true,
- };
- })
- : undefined,
- };
- })}
- loadData={({ key, children }: any) =>
- new Promise<void>((resolve) => {
- console.log("key", key, children);
- if (children) {
- resolve();
- return;
- }
- wordSplit(key, "");
- resolve();
- })
- }
- />
- )}
- </div>
- );
- };
- interface IWidgetWbwSent {
- props: string;
- }
- const WbwSentWidget = ({ props }: IWidgetWbwSent) => {
- const prop = JSON.parse(atob(props)) as IWidget;
- return <WbwSentCtl {...prop} />;
- };
- export default WbwSentWidget;
|