Răsfoiți Sursa

:construction: create

visuddhinanda 1 an în urmă
părinte
comite
8e7697d157

Fișier diff suprimat deoarece este prea mare
+ 34 - 0
dashboard-v4/dashboard/src/assets/icon/wikipali stamp2.ai


+ 185 - 0
dashboard-v4/dashboard/src/components/api/task.ts

@@ -0,0 +1,185 @@
+/**
+ *             $table->text('description',512)->nullable();
+            $table->jsonb('assignees')->index()->nullable();
+            $table->jsonb('roles')->index()->nullable();
+            $table->uuid('executor')->index()->nullable();
+            $table->uuid('executor_relation_task')->index()->nullable();
+            $table->uuid('parent')->index()->nullable();
+            $table->jsonb('pre_task')->index()->nullable();
+            $table->uuid('owner')->index();
+            $table->uuid('editor')->index();
+            $table->string('status',32)->index()->default('pending');
+            $table->timestamps();
+ */
+
+import { IStudio } from "../auth/Studio";
+import { IUser } from "../auth/User";
+
+export type TTaskStatus =
+  | "pending"
+  | "published"
+  | "running"
+  | "done"
+  | "restarted"
+  | "closed"
+  | "canceled"
+  | "expired";
+
+export interface IProject {
+  id: string;
+  title: string;
+  description: string | null;
+}
+export interface ITaskData {
+  id: string;
+  title: string;
+  description?: string | null;
+  html?: string | null;
+  type?: "task" | "group";
+  order?: number;
+  assignees?: IUser[] | null;
+  assignees_id?: string[] | null;
+  parent?: ITaskData | null;
+  parent_id?: string | null;
+  roles?: string[] | null;
+  executor?: IUser | null;
+  executor_id?: string | null;
+  executor_relation_task?: ITaskData | null;
+  executor_relation_task_id?: string | null;
+  pre_task?: ITaskData | null;
+  pre_task_id?: string | null;
+  next_task?: ITaskData | null;
+  next_task_id?: string | null;
+  is_milestone: boolean;
+  project?: IProject | null;
+  project_id?: string | null;
+  owner?: IStudio;
+  owner_id?: string | null;
+  editor?: IUser;
+  editor_id?: string | null;
+  status?: TTaskStatus;
+  created_at?: string;
+  updated_at?: string;
+  started_at?: string | null;
+  finished_at?: string | null;
+  children?: ITaskData[];
+}
+
+export interface ITaskUpdateRequest {
+  id: string;
+  studio_name: string;
+  title?: string;
+  description?: string | null;
+  assignees_id?: string[] | null;
+  parent_id?: string | null;
+  project_id?: string | null;
+  roles?: string[] | null;
+  executor_id?: string | null;
+  executor_relation_task_id?: string | null;
+  pre_task_id?: string | null;
+  next_task_id?: string | null;
+  is_milestone?: boolean;
+  status?: string;
+}
+
+export interface ITaskListResponse {
+  ok: boolean;
+  message: string;
+  data: {
+    rows: ITaskData[];
+    count: number;
+  };
+}
+
+export interface ITaskCreateRequest {
+  title: string;
+  studio: string;
+}
+
+export interface ITaskResponse {
+  ok: boolean;
+  message: string;
+  data: ITaskData;
+}
+
+/**
+ *            $table->uuid('id')->primary()->default(DB::raw('uuid_generate_v1mc()'));
+            $table->string('title',512)->index();
+            $table->boolean('is_template')->index()->default(false);
+            $table->text('description')->nullable();
+            $table->jsonb('executors')->index()->nullable();
+            $table->uuid('parent')->index()->nullable();
+            $table->jsonb('milestone')->index()->nullable();
+            $table->uuid('owner')->index();
+            $table->uuid('editor')->index();
+            $table->jsonb('status')->index();
+            $table->timestamps();
+ */
+
+export interface IProjectData {
+  id: string;
+  title: string;
+  type: TProjectType;
+  description: string | null;
+  parent?: IProjectData | null;
+  parent_id?: string | null;
+  path?: IProjectData[] | null;
+  executors?: IUser[] | null;
+  milestone?: IMilestoneInProject[] | null;
+  owner: IStudio;
+  editor: IUser;
+  status: ITaskStatusInProject[];
+  created_at: string;
+  updated_at: string;
+  deleted_at?: string | null;
+  started_at?: string | null;
+  finished_at?: string | null;
+  children?: IProjectData[];
+}
+
+export interface IProjectUpdateRequest {
+  id?: string;
+  studio_name: string;
+  title: string;
+  type: TProjectType;
+  description?: string | null;
+  parent_id?: string | null;
+}
+
+export interface IProjectListResponse {
+  data: { rows: IProjectData[]; count: number };
+  message: string;
+  ok: boolean;
+}
+export interface IProjectResponse {
+  data: IProjectData;
+  message: string;
+  ok: boolean;
+}
+export type TProjectType = "normal" | "workflow" | "endpoint";
+export interface IProjectCreateRequest {
+  title: string;
+  type: TProjectType;
+  studio_name: string;
+}
+
+export interface IMilestoneData {
+  id: string;
+  title: string;
+}
+
+export interface IMilestoneCount {
+  value: number;
+  total: number;
+}
+export interface IMilestoneInProject {
+  milestone: IMilestoneData;
+  projects: IMilestoneCount;
+  chars: IMilestoneCount;
+}
+
+export interface ITaskStatusInProject {
+  status: string;
+  count: number;
+  percent: number;
+}

+ 38 - 0
dashboard-v4/dashboard/src/components/article/TypeTask.tsx

@@ -0,0 +1,38 @@
+import { useState } from "react";
+import { Modal } from "antd";
+import { ExclamationCircleOutlined } from "@ant-design/icons";
+import { IArticleDataResponse } from "../api/Article";
+import { ArticleMode, ArticleType } from "./Article";
+import TypeArticleReader from "./TypeArticleReader";
+import ArticleEdit from "./ArticleEdit";
+import TaskEdit from "../task/TaskEdit";
+import { ITaskData } from "../api/task";
+import TaskReader from "../task/TaskReader";
+import Task from "../task/Task";
+
+interface IWidget {
+  type?: ArticleType;
+  articleId?: string;
+  mode?: ArticleMode | null;
+  channelId?: string | null;
+  onArticleChange?: (data: ITaskData) => void;
+  onArticleEdit?: Function;
+  onLoad?: (data: ITaskData) => void;
+}
+const TypeTask = ({
+  type,
+  channelId,
+  articleId,
+  mode = "read",
+  onArticleChange,
+  onLoad,
+  onArticleEdit,
+}: IWidget) => {
+  return (
+    <div>
+      <Task taskId={articleId} />
+    </div>
+  );
+};
+
+export default TypeTask;

+ 241 - 0
dashboard-v4/dashboard/src/components/dict/DictPreferenceEditor.tsx

@@ -0,0 +1,241 @@
+import { EditableProTable, ProColumns } from "@ant-design/pro-components";
+import { get, post, put } from "../../request";
+import WbwFactors from "../template/Wbw/WbwFactors";
+import { IWbw } from "../template/Wbw/WbwWord";
+import WbwLookup from "../template/Wbw/WbwLookup";
+import { useState } from "react";
+import { Progress } from "antd";
+import WbwFactorsEditor from "../template/Wbw/WbwFactorsEditor";
+
+interface IPreferenceListResponse {
+  ok: boolean;
+  message: string;
+  data: { rows: DataSourceType[]; count: number };
+}
+interface IPreferenceRequest {
+  id?: string;
+  word?: string;
+  factors?: string;
+  parent?: string;
+  confidence?: number;
+}
+export interface IPreferenceResponse {
+  ok: boolean;
+  message: string;
+  data: string;
+}
+
+type DataSourceType = {
+  sn?: number;
+  id: string;
+  count?: number;
+  word: string;
+  factors?: string;
+  parent?: string;
+  confidence?: number;
+  note?: string;
+  created_at?: number;
+  update_at?: number;
+};
+const DictPreferenceEditor = () => {
+  const [lookupWords, setLookupWords] = useState<string[]>([]);
+  const [lookupRun, setLookupRun] = useState(false);
+  const [editableKeys, setEditableRowKeys] = useState<React.Key[]>([]);
+
+  const columns: ProColumns<DataSourceType>[] = [
+    {
+      title: "序号",
+      dataIndex: "sn",
+      readonly: true,
+    },
+    {
+      title: "数量",
+      dataIndex: "count",
+      tooltip: "在三藏中出现的次数",
+      readonly: true,
+    },
+    {
+      title: "词头",
+      dataIndex: "word",
+      readonly: true,
+      width: "15%",
+    },
+    {
+      title: "factors",
+      key: "factors",
+      dataIndex: "factors",
+      render(dom, entity, index, action, schema) {
+        const wbw: IWbw = {
+          book: 1,
+          para: 1,
+          sn: [1],
+          word: { value: entity.word, status: 5 },
+          real: { value: entity.word, status: 5 },
+          factors: { value: entity.factors ?? "", status: 5 },
+          parent: { value: entity.parent ?? "", status: 5 },
+          confidence: entity.confidence ?? 0,
+        };
+        return (
+          <WbwFactorsEditor
+            key="factors"
+            initValue={wbw}
+            display={"block"}
+            onChange={async (e: string): Promise<IPreferenceResponse> => {
+              console.log("factor change", e);
+              const url = `/v2/dict-preference/${entity.id}`;
+              const data: IPreferenceRequest = {
+                factors: e,
+                confidence: 100,
+              };
+              console.log("api request", url, data);
+              return await put<IPreferenceRequest, IPreferenceResponse>(
+                url,
+                data
+              );
+            }}
+          />
+        );
+      },
+    },
+    {
+      title: "parent",
+      dataIndex: "parent",
+      fieldProps: (form, { rowKey, rowIndex }) => {
+        if (form.getFieldValue([rowKey || "", "title"]) === "不好玩") {
+          return {
+            disabled: true,
+          };
+        }
+        if (rowIndex > 9) {
+          return {
+            disabled: true,
+          };
+        }
+        return {};
+      },
+    },
+    {
+      title: "note",
+      dataIndex: "note",
+      valueType: "textarea",
+    },
+    {
+      title: "信心指数",
+      dataIndex: "confidence",
+      valueType: "digit",
+      render(dom, entity, index, action, schema) {
+        return (
+          <Progress
+            size="small"
+            percent={Math.round(entity.confidence ?? 0)}
+            status={
+              entity.confidence !== undefined && entity.confidence < 50
+                ? "exception"
+                : undefined
+            }
+          />
+        );
+      },
+    },
+    {
+      title: "操作",
+      valueType: "option",
+      width: 200,
+      render: (text, record, _, action) => [
+        <a
+          key="editable"
+          onClick={() => {
+            action?.startEditable?.(record.id);
+          }}
+        >
+          编辑
+        </a>,
+      ],
+    },
+  ];
+
+  return (
+    <div>
+      <WbwLookup words={lookupWords} run={lookupRun} />
+      <EditableProTable<DataSourceType>
+        rowKey="id"
+        headerTitle="词典默认值"
+        maxLength={5}
+        scroll={{
+          x: 960,
+        }}
+        pagination={{
+          showQuickJumper: true,
+          showSizeChanger: true,
+          pageSize: 100,
+        }}
+        search={false}
+        options={{
+          search: true,
+        }}
+        recordCreatorProps={false}
+        columns={columns}
+        request={async (params = {}, sorter, filter) => {
+          let url = `/v2/dict-preference`;
+          const offset =
+            ((params.current ? params.current : 1) - 1) *
+            (params.pageSize ? params.pageSize : 20);
+          url += `?limit=${params.pageSize}&offset=${offset}`;
+          url += params.keyword ? "&keyword=" + params.keyword : "";
+          console.info("api request", url);
+          const res = await get<IPreferenceListResponse>(url);
+          console.info("api response", res);
+          if (res.ok === false) {
+          }
+          return {
+            data: res.data.rows.map((item, id) => {
+              return {
+                sn: id + offset + 1,
+                id: item.id,
+                count: item.count,
+                word: item.word,
+                factors: item.factors,
+                parent: item.parent,
+                confidence: item.confidence,
+                note: item.note,
+                created_at: item.created_at,
+                update_at: item.update_at,
+              };
+            }),
+            total: res.data.count,
+            success: true,
+          };
+        }}
+        onRow={(record) => {
+          return {
+            onMouseEnter: () => {
+              console.info(`点击了行:${record.word}`);
+              setLookupWords([record.word]);
+              setLookupRun(true);
+            },
+            onMouseLeave: () => {
+              setLookupRun(false);
+            },
+          };
+        }}
+        editable={{
+          type: "single",
+          editableKeys,
+          onSave: async (rowKey, data, row) => {
+            console.log(rowKey, data, row);
+            const url = `/v2/dict-preference/${data.id}`;
+            console.log("api request", url, data);
+            await put<IPreferenceRequest, IPreferenceResponse>(url, {
+              factors: data.factors,
+              parent: data.parent,
+              confidence: 100,
+            });
+          },
+          onChange: setEditableRowKeys,
+        }}
+      />
+      ;
+    </div>
+  );
+};
+export default DictPreferenceEditor;

+ 42 - 0
dashboard-v4/dashboard/src/components/template/Wbw/WbwFactorsEditor.tsx

@@ -0,0 +1,42 @@
+import { useState } from "react";
+import { LoadingOutlined, WarningOutlined } from "@ant-design/icons";
+
+import WbwFactors from "./WbwFactors";
+import { IWbw, TWbwDisplayMode } from "./WbwWord";
+import { IPreferenceResponse } from "../../dict/DictPreferenceEditor";
+
+interface IWidget {
+  initValue: IWbw;
+  display?: TWbwDisplayMode;
+  onChange?: (key: string) => Promise<IPreferenceResponse>;
+}
+const WbwFactorsEditor = ({ initValue, display, onChange }: IWidget) => {
+  const [value, setValue] = useState(initValue);
+  const [loading, setLoading] = useState(false);
+  const [error, setError] = useState(false);
+  return (
+    <>
+      {loading ? <LoadingOutlined /> : error ? <WarningOutlined /> : <></>}
+      <WbwFactors
+        key="factors"
+        data={value}
+        display={display}
+        onChange={async (e: string) => {
+          console.log("factor change", e);
+          setValue({ ...value, factors: { value: e, status: 5 } });
+          if (onChange) {
+            setLoading(true);
+            setError(false);
+            const response = await onChange(e);
+            setLoading(false);
+            if (!response.ok) {
+              setError(true);
+            }
+          }
+        }}
+      />
+    </>
+  );
+};
+
+export default WbwFactorsEditor;

+ 76 - 0
dashboard-v4/dashboard/src/components/template/Wbw/WbwLookup.tsx

@@ -0,0 +1,76 @@
+import { useEffect, useRef } from "react";
+import { useAppSelector } from "../../../hooks";
+import { add, updateIndex, wordIndex } from "../../../reducers/inline-dict";
+import { get } from "../../../request";
+import { IApiResponseDictList } from "../../api/Dict";
+import store from "../../../store";
+
+interface IWidget {
+  run?: boolean;
+  words?: string[];
+  delay?: number;
+}
+const WbwLookup = ({ words, run = false, delay = 300 }: IWidget) => {
+  const inlineWordIndex = useAppSelector(wordIndex);
+
+  const intervalRef = useRef<number | null>(null); //防抖计时器句柄
+
+  useEffect(() => {
+    // 监听store中的words变化
+    if (run && words && words.length > 0) {
+      // 开始查字典
+      intervalRef.current = window.setInterval(lookup, delay, words);
+    } else {
+      stopLookup();
+    }
+    return () => {
+      // 组件销毁时清除计时器
+      clearInterval(intervalRef.current!);
+    };
+  }, [run, words]);
+  /**
+   * 停止查字典计时
+   * 在两种情况下停止计时
+   * 1. 开始查字典
+   * 2. 防抖时间内鼠标移出单词区
+   */
+  const stopLookup = () => {
+    if (intervalRef.current) {
+      window.clearInterval(intervalRef.current);
+      intervalRef.current = null;
+    }
+  };
+  /**
+   * 查字典
+   * @param word 要查的单词
+   */
+  const lookup = (words: string[]) => {
+    stopLookup();
+
+    //查询这个词在内存字典里是否有
+    const searchWord = words.filter((value) => {
+      if (inlineWordIndex.includes(value)) {
+        //已经有了
+        return false;
+      } else {
+        return true;
+      }
+    });
+    if (searchWord.length === 0) {
+      return;
+    }
+    const url = `/v2/wbwlookup?word=${searchWord.join()}`;
+    console.info("api request", url);
+    get<IApiResponseDictList>(url).then((json) => {
+      console.debug("api response", json);
+      //存储到redux
+      store.dispatch(add(json.data.rows));
+      store.dispatch(updateIndex(searchWord));
+    });
+
+    console.log("lookup", searchWord);
+  };
+  return <></>;
+};
+
+export default WbwLookup;

+ 7 - 0
dashboard-v4/dashboard/src/pages/admin/dictionary/preference.tsx

@@ -0,0 +1,7 @@
+import DictPreferenceEditor from "../../../components/dict/DictPreferenceEditor";
+
+const Widget = () => {
+  return <DictPreferenceEditor />;
+};
+
+export default Widget;

+ 27 - 0
dashboard-v4/dashboard/src/pages/studio/task/hall.tsx

@@ -0,0 +1,27 @@
+import { useParams } from "react-router-dom";
+
+import TaskList from "../../../components/task/TaskList";
+import { useAppSelector } from "../../../hooks";
+import { currentUser } from "../../../reducers/current-user";
+
+const Widget = () => {
+  const { studioname } = useParams();
+  const currUser = useAppSelector(currentUser);
+  return currUser ? (
+    <TaskList
+      studioName={studioname}
+      status={["published"]}
+      filters={[
+        {
+          field: "sign_up",
+          operator: "equals",
+          value: "true",
+        },
+      ]}
+    />
+  ) : (
+    <>未登录</>
+  );
+};
+
+export default Widget;

+ 20 - 0
dashboard-v4/dashboard/src/pages/studio/task/index.tsx

@@ -0,0 +1,20 @@
+import { Outlet } from "react-router-dom";
+import { Layout } from "antd";
+
+import LeftSider from "../../../components/studio/LeftSider";
+import { styleStudioContent } from "../style";
+
+const { Content } = Layout;
+
+const Widget = () => {
+  return (
+    <Layout>
+      <LeftSider selectedKeys="task" />
+      <Content style={{ ...styleStudioContent }}>
+        <Outlet />
+      </Content>
+    </Layout>
+  );
+};
+
+export default Widget;

+ 83 - 0
dashboard-v4/dashboard/src/pages/studio/task/project.tsx

@@ -0,0 +1,83 @@
+import { useNavigate, useParams } from "react-router-dom";
+
+import Project from "../../../components/task/Project";
+import TaskList from "../../../components/task/TaskList";
+import { Tabs } from "antd";
+import TaskTable from "../../../components/task/TaskTable";
+import TaskRelation from "../../../components/task/TaskRelation";
+import { useState } from "react";
+import { ITaskData } from "../../../components/api/task";
+import TaskLoader from "../../../components/task/TaskLoader";
+
+const Widget = () => {
+  const { studioname } = useParams();
+  const { projectId } = useParams();
+  const navigate = useNavigate();
+  const [tasks, setTasks] = useState<ITaskData[]>();
+  return (
+    <>
+      <Project
+        studioName={studioname}
+        projectId={projectId}
+        onSelect={(id: string) => {
+          navigate(`/studio/${studioname}/task/project/${id}`);
+        }}
+      />
+      <Tabs
+        type="card"
+        items={[
+          {
+            label: "列表",
+            key: "list",
+            children: (
+              <TaskList
+                editable
+                studioName={studioname}
+                projectId={projectId}
+                onLoad={(data) => setTasks(data)}
+                onChange={(data) =>
+                  setTasks((origin) => {
+                    const old = origin?.find((value) => value.id === data.id);
+                    let newData: ITaskData[] = [];
+                    if (old) {
+                      origin?.forEach(
+                        (
+                          value: ITaskData,
+                          index: number,
+                          array: ITaskData[]
+                        ) => {
+                          if (value.id === data.id) {
+                            array[index] = data;
+                          }
+                        }
+                      );
+                    } else {
+                      if (origin) {
+                        newData = [...origin, data];
+                      } else {
+                        newData = [data];
+                      }
+                    }
+                    return origin;
+                  })
+                }
+              />
+            ),
+          },
+          {
+            label: "表格",
+            key: "table",
+            children: <TaskTable tasks={tasks} />,
+          },
+          {
+            label: "关系图",
+            key: "relation",
+            children: <TaskRelation tasks={tasks} />,
+          },
+        ]}
+      ></Tabs>
+    </>
+  );
+};
+
+export default Widget;

+ 10 - 0
dashboard-v4/dashboard/src/pages/studio/task/projects.tsx

@@ -0,0 +1,10 @@
+import { useParams } from "react-router-dom";
+
+import TaskProjects from "../../../components/task/TaskProjects";
+
+const Widget = () => {
+  const { studioname } = useParams();
+  return <TaskProjects studioName={studioname} />;
+};
+
+export default Widget;

+ 15 - 0
dashboard-v4/dashboard/src/pages/studio/task/tasks.tsx

@@ -0,0 +1,15 @@
+import { useParams } from "react-router-dom";
+
+import TaskList from "../../../components/task/TaskList";
+import TaskList2 from "../../../components/task/TaskList";
+import { useAppSelector } from "../../../hooks";
+import { currentUser } from "../../../reducers/current-user";
+import MyTasks from "../../../components/task/MyTasks";
+
+const Widget = () => {
+  const { studioname } = useParams();
+
+  return <MyTasks studioName={studioname} />;
+};
+
+export default Widget;

Unele fișiere nu au fost afișate deoarece prea multe fișiere au fost modificate în acest diff