diff --git a/package.json b/package.json index eac52df..493bb7d 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "@vueuse/core": "^10.1.2", "@wangeditor/editor": "^5.1.23", "@wangeditor/editor-for-vue": "^5.1.10", + "@wangeditor/plugin-upload-attachment": "^1.1.0", "@zxcvbn-ts/core": "^3.0.1", "animate.css": "^4.1.1", "axios": "^1.4.0", diff --git a/src/api/okr/meeting.js b/src/api/okr/meeting.js new file mode 100644 index 0000000..6cdaad4 --- /dev/null +++ b/src/api/okr/meeting.js @@ -0,0 +1,45 @@ +import request from '@/config/axios' + +export const createMeeting = (data) => { + return request.post({ + url: '/admin-api/okr/meeting/add', + data, + isSubmitForm: true + // headers: { 'instance-id': 1016 } + }) +} + +// 修改 +export const updateMeeting = (data) => { + return request.put({ + url: '/admin-api/okr/meeting/update', + data + // headers: { 'instance-id': 1016 } + }) +} +// 查询详情 +export const getMeetingDetail = (params) => { + return request.get({ + url: '/admin-api/okr/meeting/get', + params + // headers: { 'instance-id': 1016 } + }) +} + +// 取消会议 +export const cancelMeeting = (data) => { + return request.put({ + url: '/admin-api/okr/meeting/cancel', + data + // headers: { 'instance-id': 1016 } + }) +} + +// 分页查询 +export const getMeetingPage = (params) => { + return request.get({ + url: '/admin-api/okr/meeting/page', + params + // headers: { 'instance-id': 1016 } + }) +} diff --git a/src/api/okr/okr.js b/src/api/okr/okr.js index 09edcd4..46b2619 100644 --- a/src/api/okr/okr.js +++ b/src/api/okr/okr.js @@ -143,3 +143,12 @@ export const getChannelOptions = () => { // headers: { 'instance-id': 1016 } }) } + +// 获取统计表中的合计信息 +export const getOkrStatisticsTotal = (params) => { + return request.get({ + url: '/admin-api/okr/node/data/count', + params + // headers: { 'instance-id': 1016 } + }) +} diff --git a/src/components/Editor/src/Editor.vue b/src/components/Editor/src/Editor.vue index 33affc4..22df80b 100644 --- a/src/components/Editor/src/Editor.vue +++ b/src/components/Editor/src/Editor.vue @@ -42,7 +42,11 @@ const props = defineProps({ 'undo', // 撤销 'redo', // 重做 'fullScreen' - ] + ], + insertKeys: { + index: 20, // 自定义插入的位置 + keys: ['uploadAttachment'] // “上传附件”菜单 + } }) } }) @@ -104,6 +108,12 @@ const editorConfig = computed((): IEditorConfig => { }, autoFocus: false, scroll: true, + // 在编辑器中,点击选中“附件”节点时,要弹出的菜单 + hoverbarKeys: { + attachment: { + menuKeys: ['downloadAttachment'] // “下载附件”菜单 + } + }, MENU_CONF: { ['uploadImage']: { server: import.meta.env.VITE_UPLOAD_URL, @@ -218,6 +228,52 @@ const editorConfig = computed((): IEditorConfig => { customInsert(res: any, insertFn: InsertFnType) { insertFn(res.data, 'video', res.data) } + }, + uploadAttachment: { + server: import.meta.env.VITE_UPLOAD_URL, + timeout: 20 * 1000, // 2s + + fieldName: 'file', + // meta: { token: 'xxx', a: 100 }, // 请求时附加的数据 + // metaWithUrl: true, // meta 拼接到 url 上 + // headers: { Accept: 'text/x-json' }, + // 自定义增加 http header + headers: { + Accept: '*', + Authorization: 'Bearer ' + getAccessToken(), + 'tenant-id': getTenantId(), + 'instance-id': getAppId() + }, + + maxFileSize: 20 * 1024 * 1024, // 20M + + onBeforeUpload(file: File) { + console.log('onBeforeUpload', file) + return file // 上传 file 文件 + // return false // 会阻止上传 + }, + onProgress(progress: number) { + console.log('onProgress', progress) + }, + onSuccess(file: File, res: any) { + console.log('onSuccess', file, res) + }, + onFailed(file: File, res: any) { + alert(res.message) + console.log('onFailed', file, res) + }, + onError(file: File, err: Error, res: any) { + alert(err.message) + console.error('onError', file, err, res) + }, + // 上传成功后,用户自定义插入文件 + customInsert(res: any, file: File, insertFn: Function) { + console.log('customInsert', res) + + // 插入附件到编辑器 + insertFn(file.name, res.data) + // insertFn(res.data, `customInsert-${file.name}`, res.data) + } } }, uploadImgShowBase64: true diff --git a/src/main.js b/src/main.js index bb6458c..438270f 100644 --- a/src/main.js +++ b/src/main.js @@ -41,9 +41,14 @@ import '@/plugins/tongji' // 百度统计 import Logger from '@/utils/Logger' import VueDOMPurifyHTML from 'vue-dompurify-html' +import { Boot } from '@wangeditor/editor' +import attachmentModule from '@wangeditor/plugin-upload-attachment' // 创建实例 const setupAll = async () => { + // 注册。要在创建编辑器之前注册,且只能注册一次,不可重复注册。 + Boot.registerModule(attachmentModule) + const app = createApp(App) await setupI18n(app) diff --git a/src/permission.js b/src/permission.js index 2eac4ef..5ded0f7 100644 --- a/src/permission.js +++ b/src/permission.js @@ -64,7 +64,9 @@ router.beforeEach(async (to, from, next) => { if (tenantId && appId) { next(`/login?tenantId=${tenantId}&appId=${appId}&redirect=${to.fullPath}`) // 否则全部重定向到登录页 } else { - next(`/login?redirect=${to.fullPath}`) // 否则全部重定向到登录页 + // next(`/login?redirect=${to.fullPath}`) + // 否则全部重定向到平台登陆页 + window.location.href = 'https://cloud.ahduima.com/ss/login' } } } diff --git a/src/router/modules/static.ts b/src/router/modules/static.ts index e33943f..4e897c7 100644 --- a/src/router/modules/static.ts +++ b/src/router/modules/static.ts @@ -35,6 +35,46 @@ const staticRouter: AppCustomRouteRecordRaw[] = [ visible: true, alwaysShow: true, redirect: '' + }, + { + icon: 'ep:data-line', + path: 'okr-analysis', + name: 'OKR统计', + componentName: 'OkrAnalysis', + component: 'OKR/Analysis/index', + meta: { + title: 'OKR统计' + }, + visible: true, + alwaysShow: true, + redirect: '' + }, + { + icon: 'ep:data-board', + path: 'okr-meeting', + name: '会议管理', + componentName: 'OkrMeeting', + component: 'OKR/Meeting/index', + meta: { + title: '会议管理' + }, + visible: true, + alwaysShow: true, + redirect: '' + }, + { + icon: 'ep:data-board', + path: 'okr-meeting-info/:id', + name: '会议详情', + componentName: 'MeetingInfo', + component: 'OKR/Meeting/MeetingInfo', + meta: { + title: '会议详情' + }, + visible: false, + alwaysShow: true, + redirect: '', + keepAlive: true } ], meta: { diff --git a/src/styles/index.scss b/src/styles/index.scss index f4e9f17..318e27f 100644 --- a/src/styles/index.scss +++ b/src/styles/index.scss @@ -97,4 +97,13 @@ /* 去除 Firefox 中的指示器 */ .el-input__inner[type='number'] { -moz-appearance: textfield; +} +.el-drawer__header { + padding: 16px 16px 8px 16px !important; + margin: 0 !important; + line-height: 24px !important; + font-size: 18px !important; + color: #303133 !important; + box-sizing: border-box !important; + // border-bottom: 1px solid #e8e8e8 !important; } \ No newline at end of file diff --git a/src/utils/index.ts b/src/utils/index.ts index 9b02f25..9636493 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -220,3 +220,30 @@ export const removeNullField = (obj: Object) => { } return obj } + +import * as XLSX from 'xlsx' +import * as FileSaver from 'file-saver' +export const exportTableWithVue = (domId: any, fileName: String) => { + // const XLSX = require('xlsx') + // 使用 this.$nextTick 是在dom元素都渲染完成之后再执行 + // this.$nextTick(function () { + // 设置导出的内容是否只做解析,不进行格式转换 false:要解析, true:不解析 + const xlsxParam = { raw: true } + const wb = XLSX.utils.table_to_book(document.querySelector(domId), xlsxParam) + + const wbout = XLSX.write(wb, { + bookType: 'xlsx', + bookSST: true, + type: 'array' + }) + try { + // 下载保存文件 + FileSaver.saveAs(new Blob([wbout], { type: 'application/octet-stream' }), `${fileName}.xlsx`) + } catch (e) { + if (typeof console !== 'undefined') { + console.log(e, wbout) + } + } + return wbout + // }); +} diff --git a/src/utils/tableObjectSpanMethod.js b/src/utils/tableObjectSpanMethod.js deleted file mode 100644 index 2dabd28..0000000 --- a/src/utils/tableObjectSpanMethod.js +++ /dev/null @@ -1,80 +0,0 @@ -/** - * @Author: River_qiu - * @Date: 2021/11/18 - */ -// 表格单元格合并多列 -let [spanObj, pos] = [{}, {}] -//spanObj 存储每个key 对应的合并值 -//pos 存储的是 key合并值得索引 大概吧 -export const dataMethod = (data, isH, allColumns = []) => { - //循环数据(行) - for (let i in data) { - let dataI = data[i] - //循环数据内对象,查看有多少key - if (allColumns.length > 0) { - let preProp = undefined - // dataI.historyValue = - // 循环列数据 - for (let index = 0; index < allColumns.length; index++) { - let j = allColumns[index] - if (i == 0) { - // 第一行,每列至少展示1行 - spanObj[j] = [1] - pos[j] = 0 - } else { - if (index == 0) { - data[i].historyValue = '' - data[i - 1].historyValue = '' - } else { - data[i].historyValue += data[i][preProp] - data[i - 1].historyValue += data[i - 1][preProp] - } - // e: 当前行数据,k:上一行数据 - let [e, k] = [dataI, data[i - 1]] - // 判断上一行数据是否存在 - // 空数据不合并 - // 前一列值相同并且不为空或者为第一列 - // 存在当前的列的值与上一行是否一样 - // 判断是否有数组规定只允许那几列需要合并单元格的 - if ( - k && - e[j] && - (!preProp || (e.historyValue && e.historyValue == k.historyValue)) && - e[j] == k[j] && - (!isH || isH.length == 0 || isH.includes(j)) - ) { - //如果上一级和当前一级相当,数组就加1 数组后面就添加一个0 - spanObj[j][pos[j]] += 1 - spanObj[j].push(0) - } else { - spanObj[j].push(1) - pos[j] = i - } - preProp = j - } - } - } else { - for (let j in dataI) { - //如果只有一条数据时默认为1即可,无需合并 - if (i == 0) { - spanObj[j] = [1] - pos[j] = 0 - } else { - let [e, k] = [dataI, data[i - 1]] - //判断上一级别是否存在 , - //存在当前的key是否和上级别的key是否一样 - //判断是否有数组规定只允许那几列需要合并单元格的 - if (k && e[j] && k[j] && e[j] == k[j] && (!isH || isH.length == 0 || isH.includes(j))) { - //如果上一级和当前一级相当,数组就加1 数组后面就添加一个0 - spanObj[j][pos[j]] += 1 - spanObj[j].push(0) - } else { - spanObj[j].push(1) - pos[j] = i - } - } - } - } - } - return spanObj -} diff --git a/src/views/Home/Index.vue b/src/views/Home/Index.vue index 7beecf8..65b9205 100644 --- a/src/views/Home/Index.vue +++ b/src/views/Home/Index.vue @@ -1,7 +1,96 @@ - 首页 + + + + + + + + + {{ t('workplace.welcome') }} {{ username }} {{ t('workplace.happyDay') }} + + + + + + + 今日待办 + + + + + 我的待办 + + + + + + + + + +const { t } = useI18n() +const userStore = useUserStore() +const router = useRouter() // 路由对象 +const loading = ref(false) +const avatar = userStore.getUser.avatar ? userStore.getUser.avatar : avatarImg +const username = userStore.getUser.nickname - +function getWaitTargetCount() { + getWaitCount({}).then((res) => { + waitCount.value = res + }) +} + +const waitCount = ref({ + dayEndAgentWorkNum: 0, + myAgentWorkNum: 0, + urgeAgentWorkNum: 0, + notifyNum: 0 +}) + +const getAllApi = async () => { + await getWaitTargetCount() + loading.value = false +} + +onMounted(() => { + getAllApi() +}) + + + diff --git a/src/views/OKR/Analysis/index.vue b/src/views/OKR/Analysis/index.vue index 3b8d285..037e300 100644 --- a/src/views/OKR/Analysis/index.vue +++ b/src/views/OKR/Analysis/index.vue @@ -1,6 +1,6 @@ - + + + 导出 + + 数据汇总 + + + + + + + + + + + + + 节点笔谈 + + - + {{ row.objectInfo.objectiveName }} @@ -22,7 +58,26 @@ - + + + + + + 关键成果 + + + {{ showTableSearch ? '取消' : '筛选' }} + + + {{ row.sourceName ? `【${row.sourceName}】` : '' }} {{ row.keyResultShowName }} @@ -69,13 +124,179 @@ + + + + + + + + + 取消 + + 发布 + + + + + + + + + 新增 + + + + + + {{ it.creatorName.slice(-2) }} + + {{ it.creatorName }} + + + + + + + + + + {{ it.likeCount }} + + + + + + {{ it.commentCount }} + + + + {{ formatDate(it.createTime, 'YYYY-MM-DD HH:mm') }} + + + + + + {{ subComment.creatorName }}: + + {{ subComment.content }} + + + + + + + + {{ scope.item.name }} + {{ scope.item.dept }} + + + + + 发布 + + + + + + + + - + diff --git a/src/views/OKR/Management/Components/DialogOkr.vue b/src/views/OKR/Management/Components/DialogOkr.vue index cabb65c..05a9d0a 100644 --- a/src/views/OKR/Management/Components/DialogOkr.vue +++ b/src/views/OKR/Management/Components/DialogOkr.vue @@ -143,7 +143,7 @@ - + @@ -178,7 +178,7 @@ - 新增评论 + 新增笔谈 @@ -492,19 +492,20 @@ function handleSaveComment() { businessType: 1, businessId: nodeInfo.value.nodeId, contentType: 1, + commentType: form.value.commentType, content: form.value.commentValue, mentionedUserIdList: form.value.mentionedUserIdList } createComment(data) .then(() => { - message.success('评论成功') + message.success('笔谈成功') searchCommentList() }) .finally(() => { form.value.commentValue = '' }) } catch (error) { - message.error('评论失败') + message.error('笔谈失败') } } const commentList = ref([]) @@ -552,20 +553,22 @@ function handleSendCommnet(idx) { businessType: 1, businessId: nodeInfo.value.nodeId, contentType: 1, + commentType: form.value.commentType, content: form.value.commentValue, mentionedUserIdList: arr, parentId: commentList.value[idx].commentId } createComment(data) .then(() => { - message.success('评论成功') + message.success('创建成功') searchCommentList() }) .finally(() => { form.value.commentValue = '' }) } catch (error) { - message.error('评论失败') + console.log(error) + message.error('创建失败') } } diff --git a/src/views/OKR/Management/Components/DialogOkrInfo.vue b/src/views/OKR/Management/Components/DialogOkrInfo.vue index 2dc6422..acc4ecc 100644 --- a/src/views/OKR/Management/Components/DialogOkrInfo.vue +++ b/src/views/OKR/Management/Components/DialogOkrInfo.vue @@ -1,7 +1,7 @@ + + 参与统计 + 不参与统计 + @@ -445,7 +449,7 @@ function resetForm() { startTime: undefined, endTime: undefined, executor: [], - dataScope: 1 + dataScope: 2 } } @@ -458,9 +462,9 @@ const emit = defineEmits(['success', 'close']) // 定义 success 事件,用于 function addObjective() { objectList.value.push({ objectiveName: '', - executor: [], + executor: form.value.executor || [], keyResults: [], - dataScope: 1 + dataScope: form.value.dataScope || 2 }) } @@ -477,7 +481,8 @@ function AddKR(idx) { process: undefined, currentValue: undefined, executor: obj.executor, - dataScope: obj.dataScope + dataScope: obj.dataScope, + isCount: false }) } @@ -490,7 +495,7 @@ function removeKR(oIdx, krIdx) { } function addChildNode() { - childNodeList.value.push({ dataScope: 1 }) + childNodeList.value.push({ dataScope: 2 }) } function removeChildNode(idx) { @@ -538,8 +543,8 @@ async function handleSave() { .confirm('是否按照当前节点所选的多个执行人自动新增对应的员工节点?', { type: 'warning', showCancelButton: true, - cancelButtonText: '取消', - confirmButtonText: '确定' + cancelButtonText: '不新增员工节点', + confirmButtonText: '新增员工节点' }) .then(() => { saveOkrData(true) @@ -572,24 +577,34 @@ async function saveOkrData(isAutoAddChild = false) { 2, '0' )}-${getLastDayOfMonth(defaultTime.getFullYear(), month)}`, - children: [ - { - nodeName: `${month + 1}月第1周`, - children: [] - }, - { - nodeName: `${month + 1}月第2周`, - children: [] - }, - { - nodeName: `${month + 1}月第3周`, - children: [] - }, - { - nodeName: `${month + 1}月第4周`, - children: [] - } - ] + dataScope: form.value.dataScope, + executor: form.value.executor + // children: [ + // { + // nodeName: `${month + 1}月第1周`, + // dataScope: form.value.dataScope, + // executor: form.value.executor, + // children: [] + // }, + // { + // nodeName: `${month + 1}月第2周`, + // dataScope: form.value.dataScope, + // executor: form.value.executor, + // children: [] + // }, + // { + // nodeName: `${month + 1}月第3周`, + // dataScope: form.value.dataScope, + // executor: form.value.executor, + // children: [] + // }, + // { + // nodeName: `${month + 1}月第4周`, + // dataScope: form.value.dataScope, + // executor: form.value.executor, + // children: [] + // } + // ] }) } } diff --git a/src/views/OKR/Meeting/MeetingInfo.vue b/src/views/OKR/Meeting/MeetingInfo.vue new file mode 100644 index 0000000..e98b981 --- /dev/null +++ b/src/views/OKR/Meeting/MeetingInfo.vue @@ -0,0 +1,305 @@ + + + + + {{ form.meetingId ? '修改会议' : '新增会议' }} + 保存 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{ item.name }} + + + + + + + + + + + + + + + + + + + + + {{ form.meetingSummary }} + + + + + + + + + + + diff --git a/src/views/OKR/Meeting/index.vue b/src/views/OKR/Meeting/index.vue new file mode 100644 index 0000000..9034ba5 --- /dev/null +++ b/src/views/OKR/Meeting/index.vue @@ -0,0 +1,219 @@ + + + + + + + + + + + + + + + + + + + + + + 查询 + 预约会议 + + + + + + + + + + + + + + + {{ ['', '未开始', '已结束', '已取消'][row.status] }} + + + + + + + + 修改 + + + 取消 + + + + 详情 + + + + + + + + + + + diff --git a/src/views/OKR/Wait/index.vue b/src/views/OKR/Wait/index.vue index 44e1545..188fe12 100644 --- a/src/views/OKR/Wait/index.vue +++ b/src/views/OKR/Wait/index.vue @@ -63,7 +63,7 @@ - + @@ -169,6 +169,7 @@ import DialogWait from './Components/DialogWait.vue' import { getWaitPage, deleteWait, getWaitCount, urgeWait } from '@/api/okr/wait' +const route = useRoute() const userStore = useUserStore() const currentUserId = userStore.getUser.id @@ -212,6 +213,11 @@ const priorityNameFilter = (priority) => { const tabIndex = ref(1) onMounted(() => { + if (route?.query?.type) { + tabIndex.value = Number(route.query.type) + } else { + tabIndex.value = 1 + } searchList() }) @@ -237,7 +243,7 @@ function getTabCount() { const loading = ref(false) const tableList = ref([]) -const mentionedList = ref([]) +// const mentionedList = ref([]) const total = ref(0) function getList() { loading.value = true @@ -282,10 +288,10 @@ function handleNotice(row) { }) } -function handleShow(row) { - console.log(row) - message.success('打开okr详情页') -} +// function handleShow(row) { +// console.log(row) +// message.success('打开okr详情页') +// } diff --git a/yarn.lock b/yarn.lock index 4402301..2834158 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2502,6 +2502,13 @@ resolved "https://registry.yarnpkg.com/@wangeditor/list-module/-/list-module-1.0.5.tgz#3fc0b167acddf885536b45fa0c127f9c6adaea33" integrity sha512-uDuYTP6DVhcYf7mF1pTlmNn5jOb4QtcVhYwSSAkyg09zqxI1qBqsfUnveeDeDqIuptSJhkh81cyxi+MF8sEPOQ== +"@wangeditor/plugin-upload-attachment@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@wangeditor/plugin-upload-attachment/-/plugin-upload-attachment-1.1.0.tgz#a014de72703a9f3d5ae44a428ac01406640ac80a" + integrity sha512-K6SsV3Cv1g+Ob1xjRRQ13Sh3lcj3yAa/aXMaKKbaPI76rNZiOpyAGH/iVv5i9enmwbZql01IXpvhK+HtrikVyQ== + dependencies: + dom7 "^4.0.4" + "@wangeditor/table-module@^1.1.4": version "1.1.4" resolved "https://registry.yarnpkg.com/@wangeditor/table-module/-/table-module-1.1.4.tgz#757d4a5868b2b658041cd323854a4d707c8347e9" @@ -3733,6 +3740,13 @@ dom7@^3.0.0: dependencies: ssr-window "^3.0.0-alpha.1" +dom7@^4.0.4: + version "4.0.6" + resolved "https://registry.yarnpkg.com/dom7/-/dom7-4.0.6.tgz#091a51621d7a19ce0fb86045cafb3c10035e97ed" + integrity sha512-emjdpPLhpNubapLFdjNL9tP06Sr+GZkrIHEXLWvOGsytACUrkbeIdjO5g77m00BrHTznnlcNqgmn7pCN192TBA== + dependencies: + ssr-window "^4.0.0" + domelementtype@1, domelementtype@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" @@ -7245,6 +7259,11 @@ ssr-window@^3.0.0-alpha.1: resolved "https://registry.yarnpkg.com/ssr-window/-/ssr-window-3.0.0.tgz#fd5b82801638943e0cc704c4691801435af7ac37" integrity sha512-q+8UfWDg9Itrg0yWK7oe5p/XRCJpJF9OBtXfOPgSJl+u3Xd5KI328RUEvUqSMVM9CiQUEf1QdBzJMkYGErj9QA== +ssr-window@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/ssr-window/-/ssr-window-4.0.2.tgz#dc6b3ee37be86ac0e3ddc60030f7b3bc9b8553be" + integrity sha512-ISv/Ch+ig7SOtw7G2+qkwfVASzazUnvlDTwypdLoPoySv+6MqlOV10VwPSE6EWkGjhW50lUmghPmpYZXMu/+AQ== + stable@^0.1.8: version "0.1.8" resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf"