diff --git a/package.json b/package.json
index c37eda2..0fc3692 100644
--- a/package.json
+++ b/package.json
@@ -26,6 +26,14 @@
"@electron/remote": "^2.1.2",
"@element-plus/icons-vue": "^2.3.1",
"@vitejs/plugin-vue-jsx": "^4.0.0",
+ "@antv/x6": "^2.18.1",
+ "@antv/x6-plugin-clipboard": "^2.1.6",
+ "@antv/x6-plugin-dnd": "^2.1.1",
+ "@antv/x6-plugin-export": "^2.1.6",
+ "@antv/x6-plugin-keyboard": "^2.2.3",
+ "@antv/x6-plugin-selection": "^2.2.2",
+ "@antv/x6-plugin-snapline": "^2.1.7",
+ "@antv/x6-plugin-transform": "^2.1.8",
"@vue-office/docx": "^1.6.2",
"@vue-office/excel": "^1.7.11",
"@vue-office/pdf": "^2.0.2",
@@ -39,7 +47,7 @@
"electron-store": "8.0.0",
"electron-updater": "^6.1.7",
"element-china-area-data": "^6.1.0",
- "element-plus": "^2.7.6",
+ "element-plus": "^2.8.0",
"fabric": "^5.3.0",
"im_electron_sdk": "^8.0.5904",
"js-cookie": "^3.0.5",
@@ -55,7 +63,10 @@
"vue-qr": "^4.0.9",
"vue-router": "^4.4.0",
"xgplayer": "^3.0.19",
- "xlsx": "^0.18.5"
+ "xlsx": "^0.18.5",
+ "less": "^4.2.0",
+ "less-loader": "^7.3.0",
+ "whiteboard_lyc": "^0.0.8"
},
"devDependencies": {
"@electron-toolkit/eslint-config": "^1.0.2",
diff --git a/src/main/store.js b/src/main/store.js
index c37f8f5..0607e19 100644
--- a/src/main/store.js
+++ b/src/main/store.js
@@ -16,6 +16,12 @@ const defaultData = {
curSubjectNode: {
data: {}, // 当前教材节点 (包含当前教材 单元)
querySearch: {} // 查询资源所需参数
+ },
+ subject: {
+ bookList: null, // 教材列表
+ curBook: null, // 当前选中的教材
+ curNode: null, // 当前选中的节点
+ defaultExpandedKeys: [], //展开的节点
}
},
local: { // 本地(永久localStorage)
diff --git a/src/renderer/src/api/classTask/index.js b/src/renderer/src/api/classTask/index.js
index 61154ff..8f8672b 100644
--- a/src/renderer/src/api/classTask/index.js
+++ b/src/renderer/src/api/classTask/index.js
@@ -62,3 +62,108 @@ export function updateClassworkdata(data) {
data: data
})
}
+
+// 修改classwork
+export function updateClasswork(data) {
+ return request({
+ url: '/education/classwork',
+ method: 'put',
+ data: data
+ })
+}
+
+
+
+// 查询evaluationclue列表
+export function listEvaluationclue(query) {
+ return request({
+ url: '/education/evaluationclue/list',
+ method: 'get',
+ params: query
+ })
+}
+
+// 查询evaluationclue详细
+export function getEvaluationclue(id) {
+ return request({
+ url: '/education/evaluationclue/' + id,
+ method: 'get'
+ })
+}
+
+// 新增evaluationclue
+export function addEvaluationclueReturnId(data) {
+ return request({
+ url: '/education/evaluationclue/addReturnId',
+ method: 'post',
+ data: data
+ })
+}
+
+// 新增evaluationclue
+export function addEvaluationclue(data) {
+ return request({
+ url: '/education/evaluationclue',
+ method: 'post',
+ data: data
+ })
+}
+
+// 修改evaluationclue
+export function updateEvaluationclue(data) {
+ return request({
+ url: '/education/evaluationclue',
+ method: 'put',
+ data: data
+ })
+}
+
+// 删除evaluationclue
+export function delEvaluationclue(id) {
+ return request({
+ url: '/education/evaluationclue/' + id,
+ method: 'delete'
+ })
+}
+
+// 新增evaluationclue,保存base64图片
+export function saveBase64File(data) {
+ return request({
+ url: '/education/evaluationclue/saveBase64File',
+ method: 'post',
+ data: data
+ })
+}
+
+// 新增evaluationclue,上传
+export function saveEvaluationClueUploadFile(data) {
+ return request({
+ url: '/education/evaluationclue/saveUploadFile',
+ method: 'post',
+ data: data
+ })
+}
+
+// 读取文件内容
+export function readFile(data) {
+ return fetch(import.meta.env.VITE_APP_RES_FILE_PATH + data.cluelink, {
+ method: "get",
+ headers: {
+ 'Content-Type': 'text/plain', // 请求头设置为纯文本
+ 'Accept': 'text/plain' // 接受头设置为纯文本
+ },
+ })
+ .then(response => response.text())
+ .then(text => {
+ return Promise.resolve(text);
+ })
+ .catch(error => {
+ console.error('读取文件出错:', error);
+ return Promise.reject();
+ });
+ /*return request({
+ url: '/education/evaluationclue/readFile',
+ method: 'post',
+ data: data
+ })*/
+}
diff --git a/src/renderer/src/api/knowledge/knowledgePoint.js b/src/renderer/src/api/knowledge/knowledgePoint.js
new file mode 100644
index 0000000..2b867c4
--- /dev/null
+++ b/src/renderer/src/api/knowledge/knowledgePoint.js
@@ -0,0 +1,53 @@
+import request from '@/utils/request'
+
+// 查询KnowledgePoint列表
+export function listKnowledgePoint(query) {
+ return request({
+ url: '/point/list',
+ method: 'get',
+ params: query
+ })
+}
+
+// 查询KnowledgePoint详细
+export function getKnowledgePoint(id) {
+ return request({
+ url: '/point/' + id,
+ method: 'get'
+ })
+}
+
+// 新增KnowledgePoint
+export function addKnowledgePointBase(data) {
+ return request({
+ url: '/point/addBase',
+ method: 'post',
+ data: data
+ })
+}
+
+// 新增KnowledgePoint
+export function addKnowledgePoint(data) {
+ return request({
+ url: '/point/add',
+ method: 'post',
+ data: data
+ })
+}
+
+// 修改KnowledgePoint
+export function updateKnowledgePoint(data) {
+ return request({
+ url: '/point/update',
+ method: 'put',
+ data: data
+ })
+}
+
+// 删除KnowledgePoint
+export function delKnowledgePoint(id) {
+ return request({
+ url: '/point/' + id,
+ method: 'delete'
+ })
+}
\ No newline at end of file
diff --git a/src/renderer/src/components/FileUpload/index.vue b/src/renderer/src/components/FileUpload/index.vue
new file mode 100644
index 0000000..e1ce984
--- /dev/null
+++ b/src/renderer/src/components/FileUpload/index.vue
@@ -0,0 +1,208 @@
+
+
+
+
+ 选取文件
+
+
+
+ 请上传
+ 大小不超过 {{ fileSize }}MB
+ 格式为 {{ fileType.join("/") }}
+ 的文件
+
+
+
+
+
+ {{ file.name }}
+
+
+ 删除
+
+
+
+
+
+
+
+
+
diff --git a/src/renderer/src/components/Flowchart/FlowContentMenu.vue b/src/renderer/src/components/Flowchart/FlowContentMenu.vue
new file mode 100644
index 0000000..f2b6da8
--- /dev/null
+++ b/src/renderer/src/components/Flowchart/FlowContentMenu.vue
@@ -0,0 +1,100 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/renderer/src/components/Flowchart/FlowDrawer.vue b/src/renderer/src/components/Flowchart/FlowDrawer.vue
new file mode 100644
index 0000000..ff0f67e
--- /dev/null
+++ b/src/renderer/src/components/Flowchart/FlowDrawer.vue
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 保存
+ 关闭
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/renderer/src/components/Flowchart/FlowLibrary.vue b/src/renderer/src/components/Flowchart/FlowLibrary.vue
new file mode 100644
index 0000000..5359e6b
--- /dev/null
+++ b/src/renderer/src/components/Flowchart/FlowLibrary.vue
@@ -0,0 +1,235 @@
+
+
+
节点库
+
+
+
+
+ {{ item.name }}
+
+
+ {{ item.name }}
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/renderer/src/components/Flowchart/config/index.js b/src/renderer/src/components/Flowchart/config/index.js
new file mode 100644
index 0000000..cb4f29a
--- /dev/null
+++ b/src/renderer/src/components/Flowchart/config/index.js
@@ -0,0 +1,333 @@
+
+import { Shape } from '@antv/x6'
+/**
+ * @desc 初始化面板配置
+ * @param check 查看模式
+ */
+export const graphOptions = (check = false) => {
+ return {
+ container: document.getElementById('flow-container'),
+ // 定制节点和边的交互行为 ==> boolean 节点或边是否可交互
+ interacting: check
+ ? {
+ nodeMovable: false,
+ edgeMovable: false,
+ magnetConnectable: false,
+ vertexDeletable: false
+ }
+ : true,
+ // 对齐线
+ snapline: true,
+ // 显示网格 // 'dot' | 'fixedDot' | 'mesh'
+ grid: {
+ visible: true,
+ size: 20, // 网格大小
+ type: 'mesh',
+ args: {
+ color: '#e9e9e9',
+ thickness: 2 // 网格线宽度/网格点大小
+ }
+ },
+ // 平移
+ panning: true,
+ // 滚轮缩放 MouseWheel
+ mousewheel: {
+ enabled: true,
+ zoomAtMousePosition: true,
+ modifiers: ['ctrl', 'meta'],
+ maxScale: 3,
+ minScale: 0.3
+ },
+ // 连线规则
+ connecting: {
+ // 路由类型
+ router: {
+ // 连线类型在此修改
+ // 曼哈顿路由 'manhattan' 路由是正交路由 'orth' 的智能版本,该路由由水平或垂直的正交线段组成,并自动避开路径上的其他节点(障碍)。
+ name: 'manhattan',
+ args: {
+ padding: 1
+ }
+ },
+ // 圆角连接器,将起点、路由点、终点通过直线按顺序连接,并在线段连接处通过圆弧连接(倒圆角)。
+ connector: {
+ name: 'rounded',
+ args: {
+ radius: 8
+ }
+ },
+ anchor: 'center',
+ connectionPoint: 'anchor',
+ // 是否允许连接到画布空白位置的点,默认为 true。
+ allowBlank: false,
+ // 距离节点或者连接桩 20px 时会触发自动吸附
+ snap: {
+ radius: 20
+ },
+ // 拽出新的边
+ createEdge() {
+ return new Shape.Edge({
+ // markup: [
+ // {
+ // tagName: 'path',
+ // selector: 'stroke'
+ // }
+ // ],
+ // connector: { name: 'rounded' },
+ // attrs: {
+ // stroke: {
+ // fill: 'none',
+ // connection: true,
+ // strokeWidth: 4,
+ // strokeLinecap: 'round',
+ // stroke: '#666'
+ // }
+ // },
+ attrs: {
+ line: {
+ stroke: '#A2B1C3',
+ strokeWidth: 3,
+ targetMarker: {
+ name: 'block',
+ width: 12,
+ height: 8,
+ },
+ },
+ },
+ zIndex: 0
+ })
+ },
+ validateConnection({ targetMagnet }) {
+ return !!targetMagnet
+ }
+ },
+ // 连线高亮
+ highlighting: {
+ // 连线过程中,自动吸附到链接桩时被使用。
+ magnetAdsorbed: {
+ name: 'stroke',
+ args: {
+ attrs: {
+ width: 12,
+ r: 6,
+ magnet: true,
+ stroke: '#008CFF',
+ strokeWidth: 2,
+ fill: '#0F67FF'
+ }
+ }
+ }
+ },
+ rotating: false, // 不能旋转
+ keyboard: !check, // 按键操作
+ clipboard: true, // 剪切板
+ autoResize: true,
+ onToolItemCreated({ tool }) {
+ const options = tool.options
+ if (options && options.index % 2 === 1) {
+ tool.setAttrs({ fill: 'red' })
+ }
+ }
+ }
+}
+
+// 链接桩样式
+export const portStyle = {
+ // width: 12,
+ // r: 6, // 半径
+ // // 当 magnet 属性为 true 时,表示该元素可以被链接,即在连线过程中可以被当做连线的起点或终点,与链接桩类似。
+ // magnet: true,
+ // stroke: '#008CFF',
+ // strokeWidth: 2,
+ // fill: '#fff',
+ // zIndex: 1,
+ // style: {
+ // visibility: 'hidden',
+ // },
+ r: 6,
+ magnet: true,
+ stroke: '#5F95FF',
+ strokeWidth: 2,
+ fill: '#fff',
+ style: {
+ visibility: 'hidden',
+ },
+}
+
+// 链接桩配置
+export const ports = {
+ // 设置链接桩分组
+ groups: {
+ top: {
+ // 定义连接柱的位置,如果不配置,将显示为默认样式
+ position: 'top',
+ // 定义连接柱的样式
+ attrs: {
+ circle: {
+ ...portStyle
+ }
+ }
+ },
+ right: {
+ position: 'right',
+ attrs: {
+ circle: {
+ ...portStyle
+ }
+ }
+ },
+ bottom: {
+ position: 'bottom',
+ attrs: {
+ circle: {
+ ...portStyle
+ }
+ }
+ },
+ left: {
+ position: 'left',
+ attrs: {
+ circle: {
+ ...portStyle
+ }
+ }
+ },
+ absolute: {
+ position: 'absolute',
+ attrs: {
+ circle: {
+ r: 6,
+ magnet: true,
+ stroke: '#008CFF',
+ strokeWidth: 2,
+ fill: '#fff'
+ }
+ }
+ }
+ },
+ // 链接桩
+ items: [
+ {
+ group: 'top'
+ },
+ {
+ group: 'right'
+ },
+ {
+ group: 'bottom'
+ },
+ {
+ group: 'left'
+ }
+ ]
+}
+
+// 动态计算宽高比
+export const transformToPercent = (target, sum, font) => {
+ // https://x6.antv.vision/zh/docs/tutorial/intermediate/attrs
+ // 相对节点的大小
+ const percent = (target / sum).toFixed(2) * 100
+ return `${percent}${font ? 'px' : '%'}`
+}
+
+// 注册节点配置信息 注册以后就可以像使用内置节点那样使用该节点
+export const registerNodeOpeions = {
+ 'custom-rect': {
+ inherit: 'rect',
+ width: 70,
+ height: 40,
+ attrs: {
+ body: {
+ strokeWidth: 1,
+ stroke: '#5F95FF',
+ fill: '#EFF4FF',
+ },
+ text: {
+ fontSize: 12,
+ fill: '#262626',
+ },
+ },
+ ports: { ...ports },
+ },
+ 'custom-polygon' : {
+ inherit: 'polygon',
+ width: 70,
+ height: 40,
+ attrs: {
+ body: {
+ strokeWidth: 1,
+ stroke: '#5F95FF',
+ fill: '#EFF4FF',
+ },
+ text: {
+ fontSize: 12,
+ fill: '#262626',
+ },
+ },
+ ports: {
+ ...ports,
+ items: [
+ {
+ group: 'top',
+ },
+ {
+ group: 'bottom',
+ },
+ ],
+ },
+ },
+ 'custom-circle' : {
+ inherit: 'circle',
+ width: 50,
+ height: 50,
+ attrs: {
+ body: {
+ strokeWidth: 1,
+ stroke: '#5F95FF',
+ fill: '#EFF4FF',
+ },
+ text: {
+ fontSize: 12,
+ fill: '#262626',
+ },
+ },
+ ports: { ...ports },
+ },
+}
+
+// 图形变换配置
+export const transFormOptions = {
+ // 调整尺寸
+ resizing: {
+ enabled: true,
+ minWidth: 1,
+ maxWidth: 200,
+ minHeight: 1,
+ maxHeight: 150,
+ restrict: false,
+ preserveAspectRatio: false,
+ },
+ // 调整角度---旋转
+ rotating: {
+ enabled: true,
+ }
+}
+
+// 拖动添加节点样式配置
+export const addNodeAttrStyle = {
+ '可选过程': {
+ rx: 6,
+ ry: 6,
+ },
+ '开始': {
+ rx: 20,
+ ry: 26,
+ },
+ '决策': {
+ refPoints: '0,10 10,0 20,10 10,20',
+ },
+ '数据': {
+ refPoints: '10,0 40,0 30,20 0,20',
+ }
+
+}
\ No newline at end of file
diff --git a/src/renderer/src/components/Flowchart/index.vue b/src/renderer/src/components/Flowchart/index.vue
new file mode 100644
index 0000000..d808786
--- /dev/null
+++ b/src/renderer/src/components/Flowchart/index.vue
@@ -0,0 +1,391 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/renderer/src/components/choose-textbook/index.vue b/src/renderer/src/components/choose-textbook/index.vue
index a79d68b..aa50586 100644
--- a/src/renderer/src/components/choose-textbook/index.vue
+++ b/src/renderer/src/components/choose-textbook/index.vue
@@ -44,11 +44,12 @@
+
+
diff --git a/src/renderer/src/components/whiteboard/components/Contextmenu.vue b/src/renderer/src/components/whiteboard/components/Contextmenu.vue
new file mode 100644
index 0000000..691f0bd
--- /dev/null
+++ b/src/renderer/src/components/whiteboard/components/Contextmenu.vue
@@ -0,0 +1,213 @@
+
+
+
+
+
+
+
diff --git a/src/renderer/src/components/whiteboard/constants.js b/src/renderer/src/components/whiteboard/constants.js
new file mode 100644
index 0000000..5209a6e
--- /dev/null
+++ b/src/renderer/src/components/whiteboard/constants.js
@@ -0,0 +1,120 @@
+// 描边颜色
+export const strokeColorList = [
+ '#000000',
+ '#343a40',
+ '#495057',
+ '#c92a2a',
+ '#a61e4d',
+ '#862e9c',
+ '#5f3dc4',
+ '#364fc7',
+ '#1864ab',
+ '#0b7285',
+ '#087f5b',
+ '#2b8a3e',
+ '#5c940d',
+ '#e67700',
+ '#d9480f'
+]
+
+// 填充颜色
+export const fillColorList = [
+ 'transparent',
+ '#ced4da',
+ '#868e96',
+ '#fa5252',
+ '#e64980',
+ '#be4bdb',
+ '#7950f2',
+ '#4c6ef5',
+ '#228be6',
+ '#15aabf',
+ '#12b886',
+ '#40c057',
+ '#82c91e',
+ '#fab005',
+ '#fd7e14'
+]
+
+// 背景颜色
+export const backgroundColorList = [
+ '#ffffff',
+ '#f8f9fa',
+ '#f1f3f5',
+ '#fff5f5',
+ '#fff0f6',
+ '#f8f0fc',
+ '#f3f0ff',
+ '#edf2ff',
+ '#e7f5ff',
+ '#e3fafc',
+ '#e6fcf5',
+ '#ebfbee',
+ '#f4fce3',
+ '#fff9db',
+ '#fff4e6'
+]
+
+// 字体列表
+export const fontFamilyList = [
+ {
+ name: '微软雅黑',
+ value: '微软雅黑, Microsoft YaHei'
+ },
+ {
+ name: '宋体',
+ value: '宋体, SimSun, Songti SC'
+ },
+ {
+ name: '楷体',
+ value: '楷体, 楷体_GB2312, SimKai, STKaiti'
+ },
+ {
+ name: '黑体',
+ value: '黑体, SimHei, Heiti SC'
+ },
+ {
+ name: '隶书',
+ value: '隶书, SimLi'
+ },
+ {
+ name: 'Andale Mono',
+ value: 'andale mono'
+ },
+ {
+ name: 'Arial',
+ value: 'arial, helvetica, sans-serif'
+ },
+ {
+ name: 'arialBlack',
+ value: 'arial black, avant garde'
+ },
+ {
+ name: 'Comic Sans Ms',
+ value: 'comic sans ms'
+ },
+ {
+ name: 'Impact',
+ value: 'impact, chicago'
+ },
+ {
+ name: 'Times New Roman',
+ value: 'times new roman'
+ },
+ {
+ name: 'Sans-Serif',
+ value: 'sans-serif'
+ },
+ {
+ name: 'serif',
+ value: 'serif'
+ }
+]
+
+// 字号
+export const fontSizeList = [10, 12, 16, 18, 24, 32, 48].map(item => {
+ return {
+ name: item,
+ value: item
+ }
+})
diff --git a/src/renderer/src/components/whiteboard/libs/icons.svg b/src/renderer/src/components/whiteboard/libs/icons.svg
new file mode 100644
index 0000000..cc8298a
--- /dev/null
+++ b/src/renderer/src/components/whiteboard/libs/icons.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/renderer/src/components/whiteboard/libs/jsonTree.css b/src/renderer/src/components/whiteboard/libs/jsonTree.css
new file mode 100644
index 0000000..3812440
--- /dev/null
+++ b/src/renderer/src/components/whiteboard/libs/jsonTree.css
@@ -0,0 +1,107 @@
+/*
+ * JSON Tree Viewer
+ * http://github.com/summerstyle/jsonTreeViewer
+ *
+ * Copyright 2017 Vera Lobacheva (http://iamvera.com)
+ * Released under the MIT license (LICENSE.txt)
+ */
+
+/* Background for the tree. May use for
element */
+.jsontree_bg {
+ background: #FFF;
+}
+
+/* Styles for the container of the tree (e.g. fonts, margins etc.) */
+.jsontree_tree {
+ margin-left: 30px;
+ font-family: 'PT Mono', monospace;
+ font-size: 14px;
+}
+
+/* Styles for a list of child nodes */
+.jsontree_child-nodes {
+ display: none;
+ margin-left: 35px;
+ margin-bottom: 5px;
+ line-height: 2;
+}
+.jsontree_node_expanded > .jsontree_value-wrapper > .jsontree_value > .jsontree_child-nodes {
+ display: block;
+}
+
+/* Styles for labels */
+.jsontree_label-wrapper {
+ float: left;
+ margin-right: 8px;
+}
+.jsontree_label {
+ font-weight: normal;
+ vertical-align: top;
+ color: #000;
+ position: relative;
+ padding: 1px;
+ border-radius: 4px;
+ cursor: default;
+}
+.jsontree_node_marked > .jsontree_label-wrapper > .jsontree_label {
+ background: #fff2aa;
+}
+
+/* Styles for values */
+.jsontree_value-wrapper {
+ display: block;
+ overflow: hidden;
+}
+.jsontree_node_complex > .jsontree_value-wrapper {
+ overflow: inherit;
+}
+.jsontree_value {
+ vertical-align: top;
+ display: inline;
+}
+.jsontree_value_null {
+ color: #777;
+ font-weight: bold;
+}
+.jsontree_value_string {
+ color: #025900;
+ font-weight: bold;
+}
+.jsontree_value_number {
+ color: #000E59;
+ font-weight: bold;
+}
+.jsontree_value_boolean {
+ color: #600100;
+ font-weight: bold;
+}
+
+/* Styles for active elements */
+.jsontree_expand-button {
+ position: absolute;
+ top: 3px;
+ left: -15px;
+ display: block;
+ width: 11px;
+ height: 11px;
+ background-image: url('icons.svg');
+}
+.jsontree_node_expanded > .jsontree_label-wrapper > .jsontree_label > .jsontree_expand-button {
+ background-position: 0 -11px;
+}
+.jsontree_show-more {
+ cursor: pointer;
+}
+.jsontree_node_expanded > .jsontree_value-wrapper > .jsontree_value > .jsontree_show-more {
+ display: none;
+}
+.jsontree_node_empty > .jsontree_label-wrapper > .jsontree_label > .jsontree_expand-button,
+.jsontree_node_empty > .jsontree_value-wrapper > .jsontree_value > .jsontree_show-more {
+ display: none !important;
+}
+.jsontree_node_complex > .jsontree_label-wrapper > .jsontree_label {
+ cursor: pointer;
+}
+.jsontree_node_empty > .jsontree_label-wrapper > .jsontree_label {
+ cursor: default !important;
+}
diff --git a/src/renderer/src/components/whiteboard/libs/jsonTree.js b/src/renderer/src/components/whiteboard/libs/jsonTree.js
new file mode 100644
index 0000000..b2de406
--- /dev/null
+++ b/src/renderer/src/components/whiteboard/libs/jsonTree.js
@@ -0,0 +1,822 @@
+/**
+ * JSON Tree library (a part of jsonTreeViewer)
+ * http://github.com/summerstyle/jsonTreeViewer
+ *
+ * Copyright 2017 Vera Lobacheva (http://iamvera.com)
+ * Released under the MIT license (LICENSE.txt)
+ */
+
+var jsonTree = (function() {
+
+ /* ---------- Utilities ---------- */
+ var utils = {
+
+ /*
+ * Returns js-"class" of value
+ *
+ * @param val {any type} - value
+ * @returns {string} - for example, "[object Function]"
+ */
+ getClass : function(val) {
+ return Object.prototype.toString.call(val);
+ },
+
+ /**
+ * Checks for a type of value (for valid JSON data types).
+ * In other cases - throws an exception
+ *
+ * @param val {any type} - the value for new node
+ * @returns {string} ("object" | "array" | "null" | "boolean" | "number" | "string")
+ */
+ getType : function(val) {
+ if (val === null) {
+ return 'null';
+ }
+
+ switch (typeof val) {
+ case 'number':
+ return 'number';
+
+ case 'string':
+ return 'string';
+
+ case 'boolean':
+ return 'boolean';
+ }
+
+ switch(utils.getClass(val)) {
+ case '[object Array]':
+ return 'array';
+
+ case '[object Object]':
+ return 'object';
+ }
+
+ throw new Error('Bad type: ' + utils.getClass(val));
+ },
+
+ /**
+ * Applies for each item of list some function
+ * and checks for last element of the list
+ *
+ * @param obj {Object | Array} - a list or a dict with child nodes
+ * @param func {Function} - the function for each item
+ */
+ forEachNode : function(obj, func) {
+ var type = utils.getType(obj),
+ isLast;
+
+ switch (type) {
+ case 'array':
+ isLast = obj.length - 1;
+
+ obj.forEach(function(item, i) {
+ func(i, item, i === isLast);
+ });
+
+ break;
+
+ case 'object':
+ var keys = Object.keys(obj).sort();
+
+ isLast = keys.length - 1;
+
+ keys.forEach(function(item, i) {
+ func(item, obj[item], i === isLast);
+ });
+
+ break;
+ }
+
+ },
+
+ /**
+ * Implements the kind of an inheritance by
+ * using parent prototype and
+ * creating intermediate constructor
+ *
+ * @param Child {Function} - a child constructor
+ * @param Parent {Function} - a parent constructor
+ */
+ inherits : (function() {
+ var F = function() {};
+
+ return function(Child, Parent) {
+ F.prototype = Parent.prototype;
+ Child.prototype = new F();
+ Child.prototype.constructor = Child;
+ };
+ })(),
+
+ /*
+ * Checks for a valid type of root node*
+ *
+ * @param {any type} jsonObj - a value for root node
+ * @returns {boolean} - true for an object or an array, false otherwise
+ */
+ isValidRoot : function(jsonObj) {
+ switch (utils.getType(jsonObj)) {
+ case 'object':
+ case 'array':
+ return true;
+ default:
+ return false;
+ }
+ },
+
+ /**
+ * Extends some object
+ */
+ extend : function(targetObj, sourceObj) {
+ for (var prop in sourceObj) {
+ if (sourceObj.hasOwnProperty(prop)) {
+ targetObj[prop] = sourceObj[prop];
+ }
+ }
+ }
+ };
+
+
+ /* ---------- Node constructors ---------- */
+
+ /**
+ * The factory for creating nodes of defined type.
+ *
+ * ~~~ Node ~~~ is a structure element of an onject or an array
+ * with own label (a key of an object or an index of an array)
+ * and value of any json data type. The root object or array
+ * is a node without label.
+ * {...
+ * [+] "label": value,
+ * ...}
+ *
+ * Markup:
+ *
+ *
+ *
+ *
+ * "label"
+ *
+ * :
+ *
+ * <(div|span) class="jsontree_value jsontree_value_(object|array|boolean|null|number|string)">
+ * ...
+ * (div|span)>
+ *
+ *
+ * @param label {string} - key name
+ * @param val {Object | Array | string | number | boolean | null} - a value of node
+ * @param isLast {boolean} - true if node is last in list of siblings
+ *
+ * @return {Node}
+ */
+ function Node(label, val, isLast) {
+ var nodeType = utils.getType(val);
+
+ if (nodeType in Node.CONSTRUCTORS) {
+ return new Node.CONSTRUCTORS[nodeType](label, val, isLast);
+ } else {
+ throw new Error('Bad type: ' + utils.getClass(val));
+ }
+ }
+
+ Node.CONSTRUCTORS = {
+ 'boolean' : NodeBoolean,
+ 'number' : NodeNumber,
+ 'string' : NodeString,
+ 'null' : NodeNull,
+ 'object' : NodeObject,
+ 'array' : NodeArray
+ };
+
+
+ /*
+ * The constructor for simple types (string, number, boolean, null)
+ * {...
+ * [+] "label": value,
+ * ...}
+ * value = string || number || boolean || null
+ *
+ * Markup:
+ *
+ *
+ * "age"
+ * :
+ *
+ * 25
+ * ,
+ *
+ *
+ * @abstract
+ * @param label {string} - key name
+ * @param val {string | number | boolean | null} - a value of simple types
+ * @param isLast {boolean} - true if node is last in list of parent childNodes
+ */
+ function _NodeSimple(label, val, isLast) {
+ if (this.constructor === _NodeSimple) {
+ throw new Error('This is abstract class');
+ }
+
+ var self = this,
+ el = document.createElement('li'),
+ labelEl,
+ template = function(label, val) {
+ var str = '\
+ \
+ "' +
+ label +
+ '" : \
+ \
+ \
+ ' +
+ val +
+ '' +
+ (!isLast ? ',' : '') +
+ '';
+
+ return str;
+ };
+
+ self.label = label;
+ self.isComplex = false;
+
+ el.classList.add('jsontree_node');
+ el.innerHTML = template(label, val);
+
+ self.el = el;
+
+ labelEl = el.querySelector('.jsontree_label');
+
+ labelEl.addEventListener('click', function(e) {
+ if (e.altKey) {
+ self.toggleMarked();
+ return;
+ }
+
+ if (e.shiftKey) {
+ document.getSelection().removeAllRanges();
+ alert(self.getJSONPath());
+ return;
+ }
+ }, false);
+ }
+
+ _NodeSimple.prototype = {
+ constructor : _NodeSimple,
+
+ /**
+ * Mark node
+ */
+ mark : function() {
+ this.el.classList.add('jsontree_node_marked');
+ },
+
+ /**
+ * Unmark node
+ */
+ unmark : function() {
+ this.el.classList.remove('jsontree_node_marked');
+ },
+
+ /**
+ * Mark or unmark node
+ */
+ toggleMarked : function() {
+ this.el.classList.toggle('jsontree_node_marked');
+ },
+
+ /**
+ * Expands parent node of this node
+ *
+ * @param isRecursive {boolean} - if true, expands all parent nodes
+ * (from node to root)
+ */
+ expandParent : function(isRecursive) {
+ if (!this.parent) {
+ return;
+ }
+
+ this.parent.expand();
+ this.parent.expandParent(isRecursive);
+ },
+
+ /**
+ * Returns JSON-path of this
+ *
+ * @param isInDotNotation {boolean} - kind of notation for returned json-path
+ * (by default, in bracket notation)
+ * @returns {string}
+ */
+ getJSONPath : function(isInDotNotation) {
+ if (this.isRoot) {
+ return "$";
+ }
+
+ var currentPath;
+
+ if (this.parent.type === 'array') {
+ currentPath = "[" + this.label + "]";
+ } else {
+ currentPath = isInDotNotation ? "." + this.label : "['" + this.label + "']";
+ }
+
+ return this.parent.getJSONPath(isInDotNotation) + currentPath;
+ }
+ };
+
+
+ /*
+ * The constructor for boolean values
+ * {...
+ * [+] "label": boolean,
+ * ...}
+ * boolean = true || false
+ *
+ * @constructor
+ * @param label {string} - key name
+ * @param val {boolean} - value of boolean type, true or false
+ * @param isLast {boolean} - true if node is last in list of parent childNodes
+ */
+ function NodeBoolean(label, val, isLast) {
+ this.type = "boolean";
+
+ _NodeSimple.call(this, label, val, isLast);
+ }
+ utils.inherits(NodeBoolean,_NodeSimple);
+
+
+ /*
+ * The constructor for number values
+ * {...
+ * [+] "label": number,
+ * ...}
+ * number = 123
+ *
+ * @constructor
+ * @param label {string} - key name
+ * @param val {number} - value of number type, for example 123
+ * @param isLast {boolean} - true if node is last in list of parent childNodes
+ */
+ function NodeNumber(label, val, isLast) {
+ this.type = "number";
+
+ _NodeSimple.call(this, label, val, isLast);
+ }
+ utils.inherits(NodeNumber,_NodeSimple);
+
+
+ /*
+ * The constructor for string values
+ * {...
+ * [+] "label": string,
+ * ...}
+ * string = "abc"
+ *
+ * @constructor
+ * @param label {string} - key name
+ * @param val {string} - value of string type, for example "abc"
+ * @param isLast {boolean} - true if node is last in list of parent childNodes
+ */
+ function NodeString(label, val, isLast) {
+ this.type = "string";
+
+ _NodeSimple.call(this, label, '"' + val + '"', isLast);
+ }
+ utils.inherits(NodeString,_NodeSimple);
+
+
+ /*
+ * The constructor for null values
+ * {...
+ * [+] "label": null,
+ * ...}
+ *
+ * @constructor
+ * @param label {string} - key name
+ * @param val {null} - value (only null)
+ * @param isLast {boolean} - true if node is last in list of parent childNodes
+ */
+ function NodeNull(label, val, isLast) {
+ this.type = "null";
+
+ _NodeSimple.call(this, label, val, isLast);
+ }
+ utils.inherits(NodeNull,_NodeSimple);
+
+
+ /*
+ * The constructor for complex types (object, array)
+ * {...
+ * [+] "label": value,
+ * ...}
+ * value = object || array
+ *
+ * Markup:
+ *
+ *
+ *
+ *
+ * "label"
+ *
+ * :
+ *
+ *
+ *
{
+ *
+ *
}
+ * ,
+ *
+ *
+ *
+ * @abstract
+ * @param label {string} - key name
+ * @param val {Object | Array} - a value of complex types, object or array
+ * @param isLast {boolean} - true if node is last in list of parent childNodes
+ */
+ function _NodeComplex(label, val, isLast) {
+ if (this.constructor === _NodeComplex) {
+ throw new Error('This is abstract class');
+ }
+
+ var self = this,
+ el = document.createElement('li'),
+ template = function(label, sym) {
+ var comma = (!isLast) ? ',' : '',
+ str = '\
+ \
+
\
+
' + sym[0] + '\
+
…\
+
\
+
' + sym[1] + '' +
+ '
' + comma +
+ '
';
+
+ if (label !== null) {
+ str = '\
+ \
+ ' +
+ '' +
+ '"' + label +
+ '" : \
+ ' + str;
+ }
+
+ return str;
+ },
+ childNodesUl,
+ labelEl,
+ moreContentEl,
+ childNodes = [];
+
+ self.label = label;
+ self.isComplex = true;
+
+ el.classList.add('jsontree_node');
+ el.classList.add('jsontree_node_complex');
+ el.innerHTML = template(label, self.sym);
+
+ childNodesUl = el.querySelector('.jsontree_child-nodes');
+
+ if (label !== null) {
+ labelEl = el.querySelector('.jsontree_label');
+ moreContentEl = el.querySelector('.jsontree_show-more');
+
+ labelEl.addEventListener('click', function(e) {
+ if (e.altKey) {
+ self.toggleMarked();
+ return;
+ }
+
+ if (e.shiftKey) {
+ document.getSelection().removeAllRanges();
+ alert(self.getJSONPath());
+ return;
+ }
+
+ self.toggle(e.ctrlKey || e.metaKey);
+ }, false);
+
+ moreContentEl.addEventListener('click', function(e) {
+ self.toggle(e.ctrlKey || e.metaKey);
+ }, false);
+
+ self.isRoot = false;
+ } else {
+ self.isRoot = true;
+ self.parent = null;
+
+ el.classList.add('jsontree_node_expanded');
+ }
+
+ self.el = el;
+ self.childNodes = childNodes;
+ self.childNodesUl = childNodesUl;
+
+ utils.forEachNode(val, function(label, node, isLast) {
+ self.addChild(new Node(label, node, isLast));
+ });
+
+ self.isEmpty = !Boolean(childNodes.length);
+ if (self.isEmpty) {
+ el.classList.add('jsontree_node_empty');
+ }
+ }
+
+ utils.inherits(_NodeComplex, _NodeSimple);
+
+ utils.extend(_NodeComplex.prototype, {
+ constructor : _NodeComplex,
+
+ /*
+ * Add child node to list of child nodes
+ *
+ * @param child {Node} - child node
+ */
+ addChild : function(child) {
+ this.childNodes.push(child);
+ this.childNodesUl.appendChild(child.el);
+ child.parent = this;
+ },
+
+ /*
+ * Expands this list of node child nodes
+ *
+ * @param isRecursive {boolean} - if true, expands all child nodes
+ */
+ expand : function(isRecursive){
+ if (this.isEmpty) {
+ return;
+ }
+
+ if (!this.isRoot) {
+ this.el.classList.add('jsontree_node_expanded');
+ }
+
+ if (isRecursive) {
+ this.childNodes.forEach(function(item, i) {
+ if (item.isComplex) {
+ item.expand(isRecursive);
+ }
+ });
+ }
+ },
+
+ /*
+ * Collapses this list of node child nodes
+ *
+ * @param isRecursive {boolean} - if true, collapses all child nodes
+ */
+ collapse : function(isRecursive) {
+ if (this.isEmpty) {
+ return;
+ }
+
+ if (!this.isRoot) {
+ this.el.classList.remove('jsontree_node_expanded');
+ }
+
+ if (isRecursive) {
+ this.childNodes.forEach(function(item, i) {
+ if (item.isComplex) {
+ item.collapse(isRecursive);
+ }
+ });
+ }
+ },
+
+ /*
+ * Expands collapsed or collapses expanded node
+ *
+ * @param {boolean} isRecursive - Expand all child nodes if this node is expanded
+ * and collapse it otherwise
+ */
+ toggle : function(isRecursive) {
+ if (this.isEmpty) {
+ return;
+ }
+
+ this.el.classList.toggle('jsontree_node_expanded');
+
+ if (isRecursive) {
+ var isExpanded = this.el.classList.contains('jsontree_node_expanded');
+
+ this.childNodes.forEach(function(item, i) {
+ if (item.isComplex) {
+ item[isExpanded ? 'expand' : 'collapse'](isRecursive);
+ }
+ });
+ }
+ },
+
+ /**
+ * Find child nodes that match some conditions and handle it
+ *
+ * @param {Function} matcher
+ * @param {Function} handler
+ * @param {boolean} isRecursive
+ */
+ findChildren : function(matcher, handler, isRecursive) {
+ if (this.isEmpty) {
+ return;
+ }
+
+ this.childNodes.forEach(function(item, i) {
+ if (matcher(item)) {
+ handler(item);
+ }
+
+ if (item.isComplex && isRecursive) {
+ item.findChildren(matcher, handler, isRecursive);
+ }
+ });
+ }
+ });
+
+
+ /*
+ * The constructor for object values
+ * {...
+ * [+] "label": object,
+ * ...}
+ * object = {"abc": "def"}
+ *
+ * @constructor
+ * @param label {string} - key name
+ * @param val {Object} - value of object type, {"abc": "def"}
+ * @param isLast {boolean} - true if node is last in list of siblings
+ */
+ function NodeObject(label, val, isLast) {
+ this.sym = ['{', '}'];
+ this.type = "object";
+
+ _NodeComplex.call(this, label, val, isLast);
+ }
+ utils.inherits(NodeObject,_NodeComplex);
+
+
+ /*
+ * The constructor for array values
+ * {...
+ * [+] "label": array,
+ * ...}
+ * array = [1,2,3]
+ *
+ * @constructor
+ * @param label {string} - key name
+ * @param val {Array} - value of array type, [1,2,3]
+ * @param isLast {boolean} - true if node is last in list of siblings
+ */
+ function NodeArray(label, val, isLast) {
+ this.sym = ['[', ']'];
+ this.type = "array";
+
+ _NodeComplex.call(this, label, val, isLast);
+ }
+ utils.inherits(NodeArray, _NodeComplex);
+
+
+ /* ---------- The tree constructor ---------- */
+
+ /*
+ * The constructor for json tree.
+ * It contains only one Node (Array or Object), without property name.
+ * CSS-styles of .tree define main tree styles like font-family,
+ * font-size and own margins.
+ *
+ * Markup:
+ *
+ *
+ * @constructor
+ * @param jsonObj {Object | Array} - data for tree
+ * @param domEl {DOMElement} - DOM-element, wrapper for tree
+ */
+ function Tree(jsonObj, domEl) {
+ this.wrapper = document.createElement('ul');
+ this.wrapper.className = 'jsontree_tree clearfix';
+
+ this.rootNode = null;
+
+ this.sourceJSONObj = jsonObj;
+
+ this.loadData(jsonObj);
+ this.appendTo(domEl);
+ }
+
+ Tree.prototype = {
+ constructor : Tree,
+
+ /**
+ * Fill new data in current json tree
+ *
+ * @param {Object | Array} jsonObj - json-data
+ */
+ loadData : function(jsonObj) {
+ if (!utils.isValidRoot(jsonObj)) {
+ alert('The root should be an object or an array');
+ return;
+ }
+
+ this.sourceJSONObj = jsonObj;
+
+ this.rootNode = new Node(null, jsonObj, 'last');
+ this.wrapper.innerHTML = '';
+ this.wrapper.appendChild(this.rootNode.el);
+ },
+
+ /**
+ * Appends tree to DOM-element (or move it to new place)
+ *
+ * @param {DOMElement} domEl
+ */
+ appendTo : function(domEl) {
+ domEl.appendChild(this.wrapper);
+ },
+
+ /**
+ * Expands all tree nodes (objects or arrays) recursively
+ *
+ * @param {Function} filterFunc - 'true' if this node should be expanded
+ */
+ expand : function(filterFunc) {
+ if (this.rootNode.isComplex) {
+ if (typeof filterFunc == 'function') {
+ this.rootNode.childNodes.forEach(function(item, i) {
+ if (item.isComplex && filterFunc(item)) {
+ item.expand();
+ }
+ });
+ } else {
+ this.rootNode.expand('recursive');
+ }
+ }
+ },
+
+ /**
+ * Collapses all tree nodes (objects or arrays) recursively
+ */
+ collapse : function() {
+ if (typeof this.rootNode.collapse === 'function') {
+ this.rootNode.collapse('recursive');
+ }
+ },
+
+ /**
+ * Returns the source json-string (pretty-printed)
+ *
+ * @param {boolean} isPrettyPrinted - 'true' for pretty-printed string
+ * @returns {string} - for exemple, '{"a":2,"b":3}'
+ */
+ toSourceJSON : function(isPrettyPrinted) {
+ if (!isPrettyPrinted) {
+ return JSON.stringify(this.sourceJSONObj);
+ }
+
+ var DELIMETER = "[%^$#$%^%]",
+ jsonStr = JSON.stringify(this.sourceJSONObj, null, DELIMETER);
+
+ jsonStr = jsonStr.split("\n").join("
");
+ jsonStr = jsonStr.split(DELIMETER).join(" ");
+
+ return jsonStr;
+ },
+
+ /**
+ * Find all nodes that match some conditions and handle it
+ */
+ findAndHandle : function(matcher, handler) {
+ this.rootNode.findChildren(matcher, handler, 'isRecursive');
+ },
+
+ /**
+ * Unmark all nodes
+ */
+ unmarkAll : function() {
+ this.rootNode.findChildren(function(node) {
+ return true;
+ }, function(node) {
+ node.unmark();
+ }, 'isRecursive');
+ }
+ };
+
+
+ /* ---------- Public methods ---------- */
+ return {
+ /**
+ * Creates new tree by data and appends it to the DOM-element
+ *
+ * @param jsonObj {Object | Array} - json-data
+ * @param domEl {DOMElement} - the wrapper element
+ * @returns {Tree}
+ */
+ create : function(jsonObj, domEl) {
+ return new Tree(jsonObj, domEl);
+ }
+ };
+})();
+export default {
+ jsonTree
+}
\ No newline at end of file
diff --git a/src/renderer/src/components/whiteboard/whiteboard.vue b/src/renderer/src/components/whiteboard/whiteboard.vue
new file mode 100644
index 0000000..16dfc93
--- /dev/null
+++ b/src/renderer/src/components/whiteboard/whiteboard.vue
@@ -0,0 +1,913 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 下载
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/renderer/src/hooks/useGetSubject.js b/src/renderer/src/hooks/useGetSubject.js
index e164e3e..07bbb5e 100644
--- a/src/renderer/src/hooks/useGetSubject.js
+++ b/src/renderer/src/hooks/useGetSubject.js
@@ -1,13 +1,13 @@
import { ref } from 'vue'
import useUserStore from '@/store/modules/user'
import { listEvaluation } from '@/api/subject'
+import { sessionStore } from '@/utils/store'
export const useGetSubject = async () =>{
// user store
const userStore = useUserStore()
- const { edustage, edusubject, userId } = userStore.user
- const BaseUrl = import.meta.env.VITE_APP_BUILD_BASE_PATH
+ const { edustage, edusubject } = userStore.user
// 章节List
const unitList = ref([])
// 教材List
@@ -15,12 +15,10 @@ export const useGetSubject = async () =>{
// 单元章节树结构
let treeData = null
-
- // 根据 学科 + 学段 获取所有单元章节
+ // 根据学科 + 学段 获取所有单元章节
const getSubjectUnit = async () =>{
- let strUnit = localStorage.getItem('unitList')
- if(strUnit){
- unitList.value = JSON.parse(strUnit)
+ if(sessionStore.get('subject.unitList')){
+ unitList.value = sessionStore.get('subject.unitList')
}
else{
const unitParams = {
@@ -32,18 +30,16 @@ export const useGetSubject = async () =>{
}
const { rows } = await listEvaluation(unitParams)
unitList.value = rows
- localStorage.setItem('unitList', JSON.stringify(rows))
+ sessionStore.set('subject.unitList', rows)
}
-
await getSubject()
}
- // 获取 学科 + 学段 获取教材
+ // 根据学科 + 学段 获取教材
const getSubject = async () =>{
- let strSubject = localStorage.getItem('subjectList')
- if(strSubject){
- subjectList = JSON.parse(strSubject)
+ if(sessionStore.get('subject.bookList')){
+ subjectList = sessionStore.get('subject.bookList')
}
else{
const subjectParams = {
@@ -55,13 +51,19 @@ export const useGetSubject = async () =>{
}
const { rows } = await listEvaluation(subjectParams)
subjectList = rows
- localStorage.setItem('subjectList', JSON.stringify(rows))
+ sessionStore.set('subject.bookList', rows)
+ treeData = getTreeData(subjectList[0].id)
+ // 设置一个默认的curNode
+ let curNode
+ if(treeData[0].children){
+ curNode = treeData[0].children[0]
+ }
+ else{
+ curNode = treeData[0]
+ }
+ sessionStore.set('subject.curNode', curNode)
}
- // 默认选中第一个教材
- if(subjectList && subjectList.length){
- treeData = getTreeData(subjectList[0].id)
- }
}
// 单元章节数据转为“树”结构
@@ -71,6 +73,7 @@ export const useGetSubject = async () =>{
data.forEach( item => {
item.children = unitList.value.filter( item2 => item2.parentid == item.id && item2.level == 2)
})
+ sessionStore.set('subject.subjectTree', data)
return data
}
diff --git a/src/renderer/src/layout/components/Header.vue b/src/renderer/src/layout/components/Header.vue
index b92f4b8..710c338 100644
--- a/src/renderer/src/layout/components/Header.vue
+++ b/src/renderer/src/layout/components/Header.vue
@@ -86,30 +86,6 @@ const currentRoute = ref('')
const dev_api = ref(import.meta.env.VITE_APP_BASE_API)
const userSubjectList = ref([])
-const handleOutLink = (path, type, name) => {
- if (!path) return
- if (type === 'hash') {
- router.push(path)
- } else {
-
- // key 对应的 linkConfig.js 外部链接配置
- let configObj = outLink().getBaseData()
- let fullPath = configObj.fullPath + path
- fullPath = fullPath.replaceAll('//', '/')
- const { levelFirstId, levelSecondId } = JSON.parse(localStorage.getItem('unitId'))
- let unitId = levelSecondId ? levelSecondId : levelFirstId
- if (name == '教材分析' || name == '考试分析') {
- fullPath += `?unitId=${unitId}`
- }
- // 通知主进程
- ipcRenderer.send('openWindow', {
- key: path,
- fullPath: fullPath,
- cookieData: { ...configObj.data }
- })
- }
-}
-
const activeId = ref('/home')
const headerMenus = [
{
diff --git a/src/renderer/src/main.js b/src/renderer/src/main.js
index be69725..395ec45 100644
--- a/src/renderer/src/main.js
+++ b/src/renderer/src/main.js
@@ -15,6 +15,7 @@ import App from './App.vue'
import router from './router'
import log from 'electron-log/renderer' // 渲染进程日志-文件记录
import customComponent from '@/components/common' // 自定义组件
+import plugins from './plugins' // plugins插件
if(process.env.NODE_ENV != 'development') { // 非开发环境,将日志打印到日志文件
Object.assign(console, log.functions) // 渲染进程日志-控制台替换
@@ -40,4 +41,5 @@ app.use(router)
.use(store)
.use(ElementPlus, { locale: zhLocale })
.use(customComponent) // 自定义组件
+ .use(plugins)
.mount('#app')
\ No newline at end of file
diff --git a/src/renderer/src/plugins/index.js b/src/renderer/src/plugins/index.js
new file mode 100644
index 0000000..8b5c431
--- /dev/null
+++ b/src/renderer/src/plugins/index.js
@@ -0,0 +1,18 @@
+// import tab from './tab'
+// import auth from './auth'
+// import cache from './cache'
+import modal from './modal'
+// import download from './download'
+
+export default function installPlugins(app){
+ // 页签操作
+ // app.config.globalProperties.$tab = tab
+ // // 认证对象
+ // app.config.globalProperties.$auth = auth
+ // // 缓存对象
+ // app.config.globalProperties.$cache = cache
+ // 模态框对象
+ app.config.globalProperties.$modal = modal
+ // 下载文件
+ // app.config.globalProperties.$download = download
+}
diff --git a/src/renderer/src/plugins/modal.js b/src/renderer/src/plugins/modal.js
new file mode 100644
index 0000000..b59e14d
--- /dev/null
+++ b/src/renderer/src/plugins/modal.js
@@ -0,0 +1,82 @@
+import { ElMessage, ElMessageBox, ElNotification, ElLoading } from 'element-plus'
+
+let loadingInstance;
+
+export default {
+ // 消息提示
+ msg(content) {
+ ElMessage.info(content)
+ },
+ // 错误消息
+ msgError(content) {
+ ElMessage.error(content)
+ },
+ // 成功消息
+ msgSuccess(content) {
+ ElMessage.success(content)
+ },
+ // 警告消息
+ msgWarning(content) {
+ ElMessage.warning(content)
+ },
+ // 弹出提示
+ alert(content) {
+ ElMessageBox.alert(content, "系统提示")
+ },
+ // 错误提示
+ alertError(content) {
+ ElMessageBox.alert(content, "系统提示", { type: 'error' })
+ },
+ // 成功提示
+ alertSuccess(content) {
+ ElMessageBox.alert(content, "系统提示", { type: 'success' })
+ },
+ // 警告提示
+ alertWarning(content) {
+ ElMessageBox.alert(content, "系统提示", { type: 'warning' })
+ },
+ // 通知提示
+ notify(content) {
+ ElNotification.info(content)
+ },
+ // 错误通知
+ notifyError(content) {
+ ElNotification.error(content);
+ },
+ // 成功通知
+ notifySuccess(content) {
+ ElNotification.success(content)
+ },
+ // 警告通知
+ notifyWarning(content) {
+ ElNotification.warning(content)
+ },
+ // 确认窗体
+ confirm(content) {
+ return ElMessageBox.confirm(content, "系统提示", {
+ confirmButtonText: '确定',
+ cancelButtonText: '取消',
+ type: "warning",
+ })
+ },
+ // 提交内容
+ prompt(content) {
+ return ElMessageBox.prompt(content, "系统提示", {
+ confirmButtonText: '确定',
+ cancelButtonText: '取消',
+ type: "warning",
+ })
+ },
+ // 打开遮罩层
+ loading(content) {
+ loadingInstance = ElLoading.service({
+ lock: true,
+ text: content,
+ background: "rgba(0, 0, 0, 0.7)",
+ })
+ },
+ // 关闭遮罩层
+ closeLoading() {
+ loadingInstance.close();
+ }
+}
diff --git a/src/renderer/src/router/index.js b/src/renderer/src/router/index.js
index 9f1fc0e..2fa6890 100644
--- a/src/renderer/src/router/index.js
+++ b/src/renderer/src/router/index.js
@@ -82,9 +82,9 @@ export const constantRoutes = [
},
{
path: '/classTaskAssign',
- component: () => import('@/views/classTaskAssign/index.vue'),
+ component: () => import('@/views/classTask/classTaskAssign.vue'),
name: 'classTaskAssign',
- meta: {title: '作业设计'},
+ meta: {title: '作业布置'},
},
{
path: '/classTask',
diff --git a/src/renderer/src/views/classTask/classTask.vue b/src/renderer/src/views/classTask/classTask.vue
index 2c23d0a..4254de3 100644
--- a/src/renderer/src/views/classTask/classTask.vue
+++ b/src/renderer/src/views/classTask/classTask.vue
@@ -142,8 +142,9 @@ const getClassWorkList = () => {
edusubject: userStore.edusubject,//学科
deaddate: tabActive.value === '进行中'? getTomorrow() : EndDate.value,// 进行中:明天,已结束:选择的日期
status: '1', // 作业状态:1-已发布
- orderby: 'concat(deaddate,uniquekey) DESC',
- pageSize: 100
+ // orderby: 'concat(deaddate,uniquekey) DESC',
+ orderby: 'uniquekey DESC',
+ pageSize: 100,
}).then((response) => {
for (var i = 0; i < response.rows.length; i++) {
// 初始化部分新增字段值
@@ -325,7 +326,7 @@ const escapeHtmlQuotes = (str) => {
// 后端已replace双引号, 故前端不用在处理
const regex1 = /\\+/g; // 匹配多个反斜杠
let result = str.replace(regex1, '\\');
-
+ result = str.replace(/(?'); //替换\n而不替换\\n 为 \\n
return result;
}
const pollingST = ref(null) //轮询定时器标识
@@ -373,7 +374,8 @@ const getStudentVisible = async () => {
edusubject: userStore.edusubject,//学科
deaddate: tabActive.value === '进行中'? getTomorrow() : EndDate.value,// 进行中:明天,已结束:选择的日期
status: '1', // 作业状态:1-已发布
- orderby: 'concat(deaddate,uniquekey) DESC',
+ // orderby: 'concat(deaddate,uniquekey) DESC',
+ orderby: 'uniquekey DESC',
pageSize: 100
})
const curWorkList = response.rows
diff --git a/src/renderer/src/views/classTask/classTaskAssign.vue b/src/renderer/src/views/classTask/classTaskAssign.vue
new file mode 100644
index 0000000..9ae1560
--- /dev/null
+++ b/src/renderer/src/views/classTask/classTaskAssign.vue
@@ -0,0 +1,603 @@
+
+
+
+
+
+
+
+
+
+
+ 课程目录
+
+
+
+
+
+
+ 设计新作业
+ 一键推送
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+ {{ scope.row.worktype }}
+
+
+
+
+
+
+ {{ scope.row.entpcourseworklistarray.length }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ scope.row.timestamp }}
+
+
+
+
+
+ {{scope.row.status == '10'? '推送' : '已推送'}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/renderer/src/views/classTask/container/item-dialog-score.vue b/src/renderer/src/views/classTask/container/item-dialog-score.vue
index df31dd6..4c33627 100644
--- a/src/renderer/src/views/classTask/container/item-dialog-score.vue
+++ b/src/renderer/src/views/classTask/container/item-dialog-score.vue
@@ -1,7 +1,7 @@
-
+
{{ classWorkFormScore.name }} 答题详情
@@ -88,8 +88,11 @@
学生答案:
-
- {{ formatFeedContent(stuItem, quItem) }}
+
@@ -385,7 +388,7 @@
v-model="fileReadopen"
title="文件预览"
width="80%"
- :style="{ height: '75vh' }"
+ :style="{ height: '72vh' }"
append-to-body
>
diff --git a/src/renderer/src/views/classTask/container/item-dialog.vue b/src/renderer/src/views/classTask/container/item-dialog.vue
index c5691ab..2d6b3a5 100644
--- a/src/renderer/src/views/classTask/container/item-dialog.vue
+++ b/src/renderer/src/views/classTask/container/item-dialog.vue
@@ -58,7 +58,7 @@
@@ -443,6 +443,9 @@ const getStudentClassWorkDataDetail = (row) => {
//
+ wevalres.rows[w].feedcontent = wevalres.rows[w].feedcontent.replace(/(?'); //替换\n而不替换\\n 为 \\n
}
}
classWorkAnalysis.activeStudentQuizlist = wevalres.rows
@@ -523,6 +526,7 @@ const escapeHtmlQuotes = (str) => {
// 后端已replace双引号, 故前端不用在处理
const regex1 = /\\+/g; // 匹配多个反斜杠
let result = str.replace(regex1, '\\');
+ result = str.replace(/(?'); //替换\n而不替换\\n 为 \\n
return result;
}
@@ -643,30 +647,32 @@ defineExpose({
})
+
+
\ No newline at end of file
diff --git a/src/renderer/src/views/desktop/index.vue b/src/renderer/src/views/desktop/index.vue
index 9d92677..493cf57 100644
--- a/src/renderer/src/views/desktop/index.vue
+++ b/src/renderer/src/views/desktop/index.vue
@@ -45,7 +45,8 @@ import { useRouter } from 'vue-router'
import workTrend from './container/work-trend.vue'
import outLink from '@/utils/linkConfig'
import * as echarts from 'echarts'
-import { useGetClassWork } from '@/hooks/useGetClassWork'
+import { useGetSubject } from '@/hooks/useGetSubject'
+import { sessionStore } from '@/utils/store'
const router = useRouter()
const { ipcRenderer } = window.electron || {}
@@ -105,6 +106,7 @@ const menuList = [{
icon: 'icon-jiaoxuefansi',
isOuter: true,
path: '/teaching/classtaskassign?titleName=作业布置&openDialog=newClassTask',
+ // path: '/newClassTask',
id: '2-1'
},
{
@@ -112,6 +114,7 @@ const menuList = [{
icon: 'icon-xiezuo1',
isOuter: true,
path: '/teaching/classtaskassign?titleName=作业布置',
+ // path: '/classTaskAssign',
id: '2-2'
},
{
@@ -169,13 +172,12 @@ const clickMenu = ({isOuter, path, disabled, id}) =>{
let fullPath = configObj.fullPath + path
if(id == '1-2' || id == '2-1' || id == '2-2' ){
// 头部 教材分析打开外部链接需要当前章节ID
- const { levelFirstId, levelSecondId, bookeId } = JSON.parse(localStorage.getItem('unitId'))
- let unitId = levelSecondId ? levelSecondId : levelFirstId
+ const { id, rootid } = sessionStore.get('subject.curNode')
if(fullPath.indexOf('?') == -1){
- fullPath += `?unitId=${unitId}&bookeId=${bookeId}`
+ fullPath += `?unitId=${id}&bookeId=${rootid}`
}
else{
- fullPath += `&unitId=${unitId}&bookeId=${bookeId}`
+ fullPath += `&unitId=${id}&bookeId=${rootid}`
}
}
fullPath = fullPath.replaceAll('//', '/')
@@ -191,6 +193,8 @@ const clickMenu = ({isOuter, path, disabled, id}) =>{
}
onMounted(async ()=>{
+
+ await useGetSubject()
// 确保DOM 渲染完成
await nextTick()
chartInstance = echarts.init(chartDom.value)
@@ -236,21 +240,10 @@ onMounted(async ()=>{
]
}
chartInstance.setOption(option);
- // 初始化 获取教材下面的单元 + 章节
- getSubjectInit();
+
})
-const getSubjectInit = async () => {
- //查看本地是否有缓存
- let unitId = localStorage.getItem('unitId')
- if(unitId){
- // 有缓存不用管
- } else{
- // 没有 就获取 获取教材下面的单元 + 章节
- useGetClassWork();
- }
-}
diff --git a/src/renderer/src/views/examReport/index.vue b/src/renderer/src/views/examReport/index.vue
index 8548fa9..37d73e2 100644
--- a/src/renderer/src/views/examReport/index.vue
+++ b/src/renderer/src/views/examReport/index.vue
@@ -51,11 +51,11 @@
-
+
diff --git a/src/renderer/src/views/resource/index.vue b/src/renderer/src/views/resource/index.vue
index d078cad..35a5350 100644
--- a/src/renderer/src/views/resource/index.vue
+++ b/src/renderer/src/views/resource/index.vue
@@ -2,7 +2,7 @@
-
+
diff --git a/src/renderer/src/views/teach/standardAnalysis/index.vue b/src/renderer/src/views/teach/standardAnalysis/index.vue
index 5eca765..e2e2cbe 100644
--- a/src/renderer/src/views/teach/standardAnalysis/index.vue
+++ b/src/renderer/src/views/teach/standardAnalysis/index.vue
@@ -4,14 +4,14 @@
-
-
+
+
+
{{item.edustage + item.edusubject}}
+
@@ -82,7 +84,7 @@ import { listEvaluationclue } from '@/api/teaching/classwork'
import { uploadServer, getJSONFile } from '@/utils/common'
import { ElNotification } from 'element-plus'
import ChooseTextbook from "@/components/choose-textbook/index.vue";
-
+import { listEvaluation } from '@/api/classManage/index'
import useUserStore from '@/store/modules/user'
const userStore = useUserStore()
const sourceStore = useResoureStore()
@@ -101,6 +103,10 @@ const searchref = ref(null);
const listHeight = ref(0);
const dialogVisible = ref(false);
const booktitle = ref('');
+const bookInfo = ref(null);
+const booksel = ref(0);
+const bookList = ref([])
+
const searchOptions = [{
value: '0',
@@ -109,7 +115,7 @@ const searchOptions = [{
value: '1',
label: '按内容量',
}];
-const standList = ref([])
+const standList = ref([]);
const showData = ref([]);
//查询课标分析列表
@@ -234,16 +240,41 @@ const getData = (data) => {
// 头部 教材分析打开外部链接需要当前章节ID
localStorage.setItem('unitId', JSON.stringify({ levelFirstId, levelSecondId}))
}
-onMounted(() => {
-
+// 获取学科
+const getAllSubject = async () => {
+ const { rows } = await listEvaluation({ itemkey: "subject", pageSize: 500 })
+ const { edustage, edusubject } = userStore.user;
+ rows && rows.map(item => {
+ if(item.edustage === edustage && item.edusubject === edusubject){
+ bookInfo.value = {...item,avartar: import.meta.env.VITE_APP_BUILD_BASE_PATH + item.avartar}
+ }
+ if(item.fileurl !== ''){
+ bookList.value.push({...item,avartar: import.meta.env.VITE_APP_BUILD_BASE_PATH + item.avartar})
+ }
+ })
+ booksel.value = bookList.value.findIndex(item => item.edustage === edustage && item.edusubject === edusubject)
+}
+const bookChange = async (item, idx) => {
+ booksel.value = idx
+ bookInfo.value = {...item}
+ booktitle.value = `${item.edustage + item.edusubject}课程标准`
+ pdfUrl.value = '';
+ const filepath = import.meta.env.VITE_APP_RES_FILE_PATH + item.fileurl.replace('.txt','.pdf')
+ await loadPdfAnimation(filepath)
+ dialogVisible.value = false
+}
+const loadPdfAnimation = (path) => {
+ const timer = setTimeout(() => {
+ pdfUrl.value = path
+ clearTimeout(timer);
+ },2000)
+}
+onMounted(async () => {
+ await getAllSubject();
const { edustage, edusubject } = userStore.user;
booktitle.value = `${edustage + edusubject}课程标准`
const filePath = `${import.meta.env.VITE_APP_RES_FILE_PATH}${edustage}-${edusubject}-课标.pdf`
- const timer = setTimeout(() => {
- pdfUrl.value = filePath
- clearTimeout(timer);
- },2000)
-
+ await loadPdfAnimation(filePath)
if(cardref.value && headref.value){
const cardH = cardref.value.offsetHeight;
const headh = headref.value.offsetHeight;
@@ -492,4 +523,23 @@ onMounted(() => {
}
}
}
+.booklist{
+ width: 100%;
+ height: 500px;
+ padding-top: 1px;
+ overflow: auto;
+ .item{
+ width: 100%;
+ height: auto;
+ padding: 8px 16px;
+ background-color: #ffffff;
+ border-top: 1px solid #f1f1f1;
+ font-size: 14px;
+ color: #3b3b3b;
+ }
+ .active{
+ background-color: #409eff;
+ color: #ffffff;
+ }
+}
\ No newline at end of file