协同办公界面优化

This commit is contained in:
z9130 2024-09-24 20:04:11 +08:00
parent 3b1197f9ec
commit c8b61880b1
10 changed files with 807 additions and 771 deletions

View File

@ -281,6 +281,9 @@ export default {
/** 协同办公/督查督办/执行反馈 执行反馈已办查询 */
get_pageDone: (data?: QueryOptions) =>
http.get('/app/feedback/pageDone', data),
/** 协同办公/督查督办/立项发起 立项删除 */
post_deletes: (data?: BodyOptions) =>
http.post('/app/feedback/deletes', data),
},
file: {
/** 协同办公/文件上传/下载 多文件上传 */

View File

@ -1,8 +1,9 @@
import { h } from 'vue';
import { EllipsisText } from '@vben/common-ui';
import { Button, Tag } from 'ant-design-vue';
// import { Icon } from '@/components/Icon';
import { Tooltip } from 'ant-design-vue';
import dayjs from 'dayjs';
import { DictTag } from '#/components/dict-tag';
@ -55,16 +56,13 @@ export const useRender = {
if (text) {
params = params || {};
const classArr: string[] = params.class || [];
if (params?.maxLine && params?.maxLine > 0) {
classArr.push(`line-clamp-${params?.maxLine}`);
}
// if (params?.maxLine && params?.maxLine > 0) {
// classArr.push(`line-clamp-${params?.maxLine}`);
// }
return h(
Tooltip,
{ trigger: 'hover' },
{
trigger: () => h('span', { class: classArr.join(' ') }, text),
default: () => text,
},
EllipsisText,
{ class: classArr.join(' '), line: params?.maxLine || 6 },
text,
);
}
return '';
@ -113,7 +111,6 @@ export const useRender = {
* @returns
*/
renderDict: (text: string, dictType: string, params?: any) => {
debugger;
if (!dictType && !params.options) {
console.warn('请传入字典类型');
return '';

View File

@ -1,75 +1,19 @@
<template>
<div class="mx-auto flex h-screen flex-col bg-gray-100 px-8">
<p class="my-6 text-center text-3xl font-bold text-gray-800">值班信息栏</p>
<div class="mb-6 flex items-center justify-center">
<button
@click="prevMonth"
class="bg-primary hover:bg-primary/90 rounded-l-lg px-4 py-2 text-white"
>
上一月
</button>
<span class="mx-4 text-xl font-semibold">{{ currentMonth }}</span>
<button
@click="nextMonth"
class="bg-primary hover:bg-primary/90 rounded-l-lg px-4 py-2 text-white"
>
下一月
</button>
</div>
<div class="mb-8 flex-1 rounded-lg bg-white p-4 shadow-md">
<vxe-grid
ref="xGridRef"
v-bind="gridOptions"
@cell-click="handleCellClick"
>
<template #toolbar_buttons>
<!-- 可添加工具栏按钮 -->
</template>
<template #radio_cell="{ row, checked }">
<span
class="cursor-pointer text-base"
@click.stop="setSelectRow(row)"
>
<MdiRadioChecked v-if="checked" />
<MdiRadioUnchecked v-else />
</span>
</template>
</vxe-grid>
</div>
</div>
</template>
<script setup lang="ts">
import { defineComponent, ref, computed, reactive, onMounted } from 'vue';
import { FsCrud } from '@fast-crud/fast-crud';
import { type VxeGridProps } from 'vxe-table';
import { Page, useVbenModal } from '@vben/common-ui';
import { useVxeTable } from '#/hooks/vxeTable';
import { computed, onMounted, reactive, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { MdiRadioChecked, MdiRadioUnchecked } from '@vben/icons';
import {
MdiAdd,
MdiUpdate,
MdiDelete,
MdiImport,
MdiExport,
MdiRadioUnchecked,
MdiRadioChecked,
} from '@vben/icons';
import { getColumns } from './crud.tsx';
import { dict } from '@fast-crud/fast-crud';
import { getMonthStartAndEnd } from '#/utils/time';
import Apis from '#/api';
import dayjs from 'dayjs';
import { message } from 'ant-design-vue';
import { Modal } from 'ant-design-vue';
import dayjs from 'dayjs';
import { useRouter } from 'vue-router';
import { SolarDay } from 'tyme4ts';
import Apis from '#/api';
import { useVxeTable } from '#/hooks/vxeTable';
import { getMonthStartAndEnd } from '#/utils/time';
const router = useRouter();
import { getColumns } from './crud.tsx';
const { xGridRef, triggerProxy, gridProps } = useVxeTable({ ref: 'xGridRef' });
const currentDate = ref(dayjs());
@ -91,7 +35,7 @@ const exportSearchParams = ref<any>({
});
const searchRef = ref();
let isConfirmLoading = ref(false);
const isConfirmLoading = ref(false);
const [_Modal, modalApi] = useVbenModal({
async onConfirm() {
isConfirmLoading.value = true;
@ -104,9 +48,9 @@ const [_Modal, modalApi] = useVbenModal({
endDate: exportSearchParams.value.daterange[1],
};
}
let res = await Apis.zbgl
const res = await Apis.zbgl
.post_export({
params: params,
params,
config: {
meta: {
responseType: 'blob',
@ -127,8 +71,6 @@ const [_Modal, modalApi] = useVbenModal({
},
});
const { xGridRef, triggerProxy, gridProps } = useVxeTable({ ref: 'xGridRef' });
const tableData = ref([]);
/** Hooks - 表格 */
@ -139,7 +81,7 @@ const gridOptions = reactive(
autoLoad: false,
ajax: {
query: async ({ page }) => {
let data = await Apis.zbgl.get_queryZbInfo({
const data = await Apis.zbgl.get_queryZbInfo({
params: {
pageNum: 1,
pageSize: 100,
@ -147,9 +89,9 @@ const gridOptions = reactive(
},
});
for (const element of data.rows || []) {
var datas = dayjs(element.dutyDate).day();
var week = ['日', '一', '二', '三', '四', '五', '六'];
element.week = '星期' + week[datas];
const datas = dayjs(element.dutyDate).day();
const week = ['日', '一', '二', '三', '四', '五', '六'];
element.week = `星期${week[datas]}`;
}
tableData.value = data.rows;
return data;
@ -177,7 +119,7 @@ const gridOptions = reactive(
headerCellStyle({ column }) {
let style: any = {};
if (['zzb', 'ddy', 'zb'].includes(column.field)) {
if (['ddy', 'zb', 'zzb'].includes(column.field)) {
style = {
borderTop: '2px solid #000',
borderLeft: '1px solid #000',
@ -201,7 +143,7 @@ const gridOptions = reactive(
}
if (
['dutyAllPeople', 'dispatchPeople', 'dutyPeople', 'driver'].includes(
['dispatchPeople', 'driver', 'dutyAllPeople', 'dutyPeople'].includes(
column.field,
)
) {
@ -213,10 +155,10 @@ const gridOptions = reactive(
if (
[
'dutyAllPhone',
'dutyAllTelphone',
'dispatchPhone',
'dispatchTelphone',
'dutyAllPhone',
'dutyAllTelphone',
'dutyPhone',
'dutyTelphone',
].includes(column.field)
@ -241,14 +183,14 @@ const gridOptions = reactive(
};
}
style['fontSize'] = '16px';
style.fontSize = '16px';
return style;
},
cellStyle({ row, column, rowIndex }) {
let style: any = {};
if (
['dutyAllPeople', 'dispatchPeople', 'dutyPeople', 'driver'].includes(
['dispatchPeople', 'driver', 'dutyAllPeople', 'dutyPeople'].includes(
column.field,
)
) {
@ -275,8 +217,7 @@ const gridOptions = reactive(
};
}
style['fontSize'] = '16px';
style.fontSize = '16px';
return style;
},
@ -290,7 +231,7 @@ const selectRow: Recordable = computed(() => {
/** 单选框选中事件 */
const setSelectRow = (row: Recordable) => {
if (selectRow.value && selectRow.value['guid'] === row['guid']) {
if (selectRow.value && selectRow.value.guid === row.guid) {
xGridRef.value?.clearRadioRow();
} else {
xGridRef.value?.setRadioRow(row);
@ -307,4 +248,62 @@ onMounted(() => {
});
</script>
<template>
<div class="mx-auto flex h-screen flex-col bg-gray-100 px-8">
<p class="my-6 text-center text-3xl font-bold text-gray-800">值班信息栏</p>
<div class="mb-6 flex items-center justify-center">
<button
class="bg-primary hover:bg-primary/90 rounded-l-xl px-4 py-2 text-white"
@click="prevMonth"
>
上一月
</button>
<span class="mx-4 text-xl font-semibold">{{ currentMonth }}</span>
<button
class="bg-primary hover:bg-primary/90 rounded-r-xl px-4 py-2 text-white"
@click="nextMonth"
>
下一月
</button>
<!-- <div class="w-[40vw]">
<p>
1.公司机关值班人员包括值班司机都必须严格遵守值班管理要求值班期间保持值班电话畅通
</p>
<p>
2.值班干部值班期间离岗外出开会现场指挥等特殊情况应落实人员接替同时落实值班职责
</p>
<p>
3.值班司机值班地点为公司小车班休息室值班车辆必须停放在公司机关院内确保遇到突发事件值班领导能迅速赶赴现场协调处置
</p>
<p>4.值班时间当日1000次日1000</p>
<p>5.市应急管理办公室信息科62243276236233</p>
</div> -->
</div>
<div class="mb-8 flex-1 rounded-lg bg-white p-4 shadow-md">
<vxe-grid
ref="xGridRef"
v-bind="gridOptions"
@cell-click="handleCellClick"
>
<template #toolbar_buttons>
<!-- 可添加工具栏按钮 -->
</template>
<template #radio_cell="{ row, checked }">
<span
class="cursor-pointer text-base"
@click.stop="setSelectRow(row)"
>
<MdiRadioChecked v-if="checked" />
<MdiRadioUnchecked v-else />
</span>
</template>
</vxe-grid>
</div>
</div>
</template>
<style></style>

View File

@ -1,26 +1,27 @@
<script setup lang="ts">
import { ref, computed, reactive, onMounted, nextTick, onUnmounted } from "vue";
import { MdiAdd } from '@vben/icons';
import { nextTick, onMounted, onUnmounted, reactive, ref } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { Page, useVbenModal } from "@vben/common-ui";
import { compute, dict, type FormScopeContext } from "@fast-crud/fast-crud";
import { MdiUpload } from "@vben/icons";
import { useRouter } from 'vue-router'
import dayjs, { Dayjs } from "dayjs";
import Apis from "#/api";
import { message, Modal, type UploadChangeParam, type UploadProps } from 'ant-design-vue';
import { useUserStore } from "@vben/stores";
import { useRoute } from "vue-router";
import Sortable from "sortablejs";
import spokenPersonEditModal from "./spoken-person-edit-modal.vue";
import chooseUserModal from "#/views/system/user/choose-user-modal.vue";
import { FileUploader } from "#/utils/file";
import { DICT_TYPE, getDictObj, getDictOptions } from "#/utils/dict";
import { useVxeTable } from "#/hooks/vxeTable";
import { Page, useVbenModal } from '@vben/common-ui';
import { MdiAdd, MdiUpload } from '@vben/icons';
import { useUserStore } from '@vben/stores';
const { xGridRef, gridProps, triggerProxy } = useVxeTable({ ref: "xGridRef" });
import { dict } from '@fast-crud/fast-crud';
import { message, Modal, type UploadChangeParam } from 'ant-design-vue';
import Sortable from 'sortablejs';
const fileUploader = new FileUploader({})
import Apis from '#/api';
import { useRender } from '#/hooks/useRender';
import { useVxeTable } from '#/hooks/vxeTable';
import { DICT_TYPE, getDictObj, getDictOptions } from '#/utils/dict';
import { FileUploader } from '#/utils/file';
import chooseUserModal from '#/views/system/user/choose-user-modal.vue';
import spokenPersonEditModal from './spoken-person-edit-modal.vue';
const { xGridRef, gridProps, triggerProxy } = useVxeTable({ ref: 'xGridRef' });
const fileUploader = new FileUploader({});
const [ChooseUserModal, chooseUserModalApi] = useVbenModal({
connectedComponent: chooseUserModal,
@ -35,115 +36,92 @@ const route = useRoute();
const id = route.params.id;
let sortable2: any;
let showHelpTip = ref(false);
const showHelpTip = ref(false);
const pageRef = ref();
const formRef = ref();
const form2Ref = ref();
const selectField = ref("");
const selectField = ref('');
let isLoading = ref(false)
const isLoading = ref(false);
const formBinding = ref({
col: { span: 24 },
initialForm: {},
labelCol: { style: { width: "120px" } },
labelCol: { style: { width: '120px' } },
columns: {
meetingTheme: {
title: "会议主题",
key: "meetingTheme",
col: { span: 16 },
component: {
name: "a-input",
vModel: "value",
allowClear: false,
},
rules: [{ required: true, message: "请输入会议主题" }],
},
isEmployeeRepresentatives: {
title: "职工代表会",
key: "isEmployeeRepresentatives",
title: '会议主题',
key: 'meetingTheme',
col: { span: 8 },
component: {
name: "a-checkbox",
vModel: "value",
dict: dict({
data: [
{ label: "开启", value: true, },
]
}),
name: 'a-input',
vModel: 'value',
allowClear: false,
},
rules: [{ required: true, message: '请输入会议主题' }],
},
meetingType: {
title: "会议类型",
key: "meetingType",
col: { span: 12 },
title: '会议类型',
key: 'meetingType',
col: { span: 8 },
component: {
name: "fs-dict-select",
vModel: "value",
name: 'fs-dict-select',
vModel: 'value',
allowClear: false,
dict: dict({
data: getDictOptions(DICT_TYPE.meeting_type)
data: getDictOptions(DICT_TYPE.meeting_type),
}),
},
rules: [{ required: true, message: '请选择会议类型' }],
},
isEmployeeRepresentatives: {
title: '职工代表会',
key: 'isEmployeeRepresentatives',
col: { span: 8 },
component: {
name: 'a-checkbox',
vModel: 'value',
dict: dict({
data: [{ label: '开启', value: true }],
}),
},
rules: [{ required: true, message: "请选择会议类型" }],
},
meetingId: {
title: "会议室",
key: "meetingId",
col: { span: 12 },
title: '会议室',
key: 'meetingId',
col: { span: 8 },
component: {
name: "fs-dict-select",
vModel: "value",
name: 'fs-dict-select',
vModel: 'value',
allowClear: false,
dict: dict({
data: getDictOptions(DICT_TYPE.meeting_room)
data: getDictOptions(DICT_TYPE.meeting_room),
}),
},
rules: [{ required: true, message: "请选择会议类型" }],
rules: [{ required: true, message: '请选择会议类型' }],
},
meetingDate: {
title: "会议时间",
key: "meetingDate",
col: { span: 24 },
title: '会议时间',
key: 'meetingDate',
col: { span: 8 },
component: {
name: "a-date-picker",
vModel: "value",
name: 'a-date-picker',
vModel: 'value',
allowClear: false,
showTime: { format: "HH:mm" },
format: "YYYY-MM-DD HH:mm",
valueFormat: "YYYY-MM-DD HH:mm",
showTime: { format: 'HH:mm' },
format: 'YYYY-MM-DD HH:mm',
valueFormat: 'YYYY-MM-DD HH:mm',
},
rules: [{ required: true, message: "请选择会议时间" }],
rules: [{ required: true, message: '请选择会议时间' }],
},
// compere: {
// title: "",
// key: "compere",
// col: { span: 24 },
// component: {
// name: "a-input",
// readOnly: true,
// vModel: "value",
// onClick: () => {
// selectField.value = "zcr";
// chooseUserModalApi.open();
// },
// },
// show: compute(({ form }) => {
// if (form.meetingType === '') {
// return false
// }
// return true
// })
// },
compere: {
title: "主持人",
key: "compere",
col: { span: 24 },
title: '主持人',
key: 'compere',
col: { span: 8 },
component: {
name: "a-input",
vModel: "value",
name: 'a-input',
vModel: 'value',
allowClear: true,
},
// show: compute(({ form }) => {
@ -154,17 +132,14 @@ const formBinding = ref({
// })
},
otherEquipment: {
title: "涉及设备",
key: "otherEquipment",
title: '备注',
key: 'otherEquipment',
col: { span: 24 },
component: {
name: "fs-dict-select",
vModel: "value",
name: 'a-textarea',
vModel: 'value',
allowClear: true,
dict: dict({
data: getDictOptions(DICT_TYPE.meeting_facilities)
}),
mode: "multiple"
autoSize: { minRows: 4, maxRows: 6 },
},
// show: compute(({ form }) => {
// if (form.meetingType === '') {
@ -174,84 +149,93 @@ const formBinding = ref({
// })
},
fileList: {
title: "相关附件",
key: "fileList",
title: '相关附件',
key: 'fileList',
},
},
});
const form2Binding = ref({
col: { span: 24 },
initialForm: {
},
labelCol: { style: { width: "120px" } },
initialForm: {},
labelCol: { style: { width: '120px' } },
columns: {
meetingContent: {
title: "会议内容",
key: "meetingContent",
title: '会议内容',
key: 'meetingContent',
component: {
name: "a-textarea",
vModel: "value",
}
name: 'a-textarea',
vModel: 'value',
autoSize: { minRows: 4, maxRows: 6 },
},
},
conferee: {
title: "参会人员",
key: "conferee",
title: '参会人员',
key: 'conferee',
component: {
name: "a-textarea",
vModel: "value",
}
name: 'a-textarea',
vModel: 'value',
autoSize: { minRows: 4, maxRows: 6 },
},
},
fileList: {
title: "相关附件",
key: "fileList",
title: '相关附件',
key: 'fileList',
},
}
},
});
function getColumns(): any {
return [
{ width: 80, title: "", slots: { default: "dragBtn", header: "dragTip" } },
{ type: "seq", title: "发言顺序", width: 80 },
{ field: "addressor", title: "发言人员", width: 150 },
{ field: "abstracts", title: "发言内容", minWidth: 200 },
{ field: "unit", title: "所属组织", width: 150 },
{ width: 80, title: '', slots: { default: 'dragBtn', header: 'dragTip' } },
{ type: 'seq', title: '发言顺序', width: 80 },
{ field: 'addressor', title: '发言人员', width: 150 },
{
field: 'abstracts',
title: '发言内容',
minWidth: 200,
slots: {
default: ({ row }) => {
return useRender.renderMultiLineText(row.abstracts, {
maxLine: 5,
});
},
},
},
{ field: 'unit', title: '所属组织', width: 150 },
// { field: "file", title: "", width: 150 },
{
title: "操作",
title: '操作',
width: 120,
slots: { default: "operate" },
fixed: "right",
slots: { default: 'operate' },
fixed: 'right',
},
];
}
/** Hooks - 表格 */
const gridOptions = reactive(gridProps({
minHeight: "300px",
columns: getColumns(),
data: [],
toolbarConfig: {
enabled: false,
},
pagerConfig: {
enabled: false,
},
rowConfig: {
useKey: true,
isCurrent: false,
},
class: "sortable-tree-demo",
})
const gridOptions = reactive(
gridProps({
height: '800px',
columns: getColumns(),
data: [],
toolbarConfig: {
enabled: false,
},
pagerConfig: {
enabled: false,
},
rowConfig: {
useKey: true,
isCurrent: false,
},
class: 'sortable-tree-demo',
}),
);
const beforeUpload: UploadProps["beforeUpload"] = (file) => {
return false;
};
function handleBack() {
Modal.confirm({
title: "提示",
title: '提示',
content: `是否确认返回上一页面?`,
onOk: async () => {
back();
@ -263,35 +247,33 @@ function handleBack() {
* 页面返回并关闭tab
*/
function back() {
router.replace('/meeting/list')
router.replace('/meeting/list');
}
const handleChange = (info: UploadChangeParam) => {
formRef.value.setFormData({
fileList: info.fileList.length ? info.fileList : [],
fileList: info.fileList.length > 0 ? info.fileList : [],
});
};
const handleChange2 = (info: UploadChangeParam) => {
form2Ref.value.setFormData({
fileList: info.fileList.length ? info.fileList : [],
fileList: info.fileList.length > 0 ? info.fileList : [],
});
};
async function handleChooseUserConfirm(e) {
console.log(e);
chooseUserModalApi.close();
if (selectField.value == "zcr") {
if (selectField.value == 'zcr') {
formRef.value.setFormData({
compere: e.label,
compereId: e.value
})
compereId: e.value,
});
}
if (selectField.value == "fyr") {
if (selectField.value == 'fyr') {
}
}
const selectRow = ref(null);
@ -312,13 +294,13 @@ function handleOpenSpokespersonDetailModal(record) {
* 打开选择用户弹窗
*/
function handleOpenSpokespersonModal(record?) {
let data = { ...record, ...{ meetingGuid: currData.value.guid } };
const data = { ...record, meetingGuid: currData.value.guid };
spokenPersonEditModalApi.setData({
isUpdate: Boolean(record && record['guid']),
isUpdate: Boolean(record && record.guid),
record: JSON.parse(JSON.stringify(data || {})),
})
spokenPersonEditModalApi.open()
});
spokenPersonEditModalApi.open();
// console.log("[ currData.value ] >", currData.value);
// let data = { ...record, ...{ meetingGuid: currData.value.guid } };
// spokespersonModalRef.value?.open({
@ -330,45 +312,52 @@ function handleOpenSpokespersonModal(record?) {
function removeRow(row) {
const $grid = xGridRef.value;
if ($grid) {
let hideLoading = message.loading("移除中")
Apis.addressor.post_deletes({ params: { ids: row['guid'] } }).then((data) => {
$grid.remove(row);
message.success('移除成功')
}).catch((err) => {
console.log(err)
message.error('移除失败,请稍候再试')
}).finally(() => {
hideLoading()
})
const hideLoading = message.loading('移除中');
Apis.addressor
.post_deletes({ params: { ids: row.guid } })
.then((data) => {
$grid.remove(row);
message.success('移除成功');
})
.catch((error) => {
console.log(error);
message.error('移除失败,请稍候再试');
})
.finally(() => {
hideLoading();
});
}
}
function handleDelete() {
Modal.confirm({
title: '提示',
content: "是否确认删除该条记录?",
content: '是否确认删除该条记录?',
okType: 'danger',
onOk: async () => {
await Apis.meeting.post_deletes({ params: { ids: currData.value['guid'] } })
message.success("删除成功");
back()
await Apis.meeting.post_deletes({ params: { ids: currData.value.guid } });
message.success('删除成功');
back();
},
});
}
function loadDataByAddressor() {
Apis.addressor.get_list({ params: { meetingId: currData.value['guid'] } }).then((data) => {
xGridRef.value?.loadData(data.rows || []);
})
Apis.addressor
.get_list({ params: { meetingId: currData.value.guid } })
.then((data) => {
xGridRef.value?.loadData(data.rows || []);
});
}
const rowDrop = () => {
const $grid = xGridRef.value;
console.log("[ $grid ] >", $grid);
console.log('[ $grid ] >', $grid);
sortable2 = Sortable.create(
$grid.$el.querySelector(".body--wrapper>.vxe-table--body tbody") as HTMLElement,
$grid.$el.querySelector(
'.body--wrapper>.vxe-table--body tbody',
) as HTMLElement,
{
handle: ".drag-btn",
handle: '.drag-btn',
onEnd: async (sortableEvent) => {
const newIndex = sortableEvent.newIndex as number;
const oldIndex = sortableEvent.oldIndex as number;
@ -381,14 +370,14 @@ const rowDrop = () => {
tableData.splice(newIndex, 0, currRow);
//
let hideLoading = message.loading("提交中");
const hideLoading = message.loading('提交中');
try {
let data = JSON.parse(JSON.stringify(tableData));
const data = JSON.parse(JSON.stringify(tableData));
data.forEach((item, index) => {
item.sort = index + 1;
});
await Apis.addressor.post_saveBatch({ data: data });
await Apis.addressor.post_saveBatch({ data });
xGridRef.value?.loadData(tableData);
} catch (error) {
console.error(error);
@ -396,66 +385,72 @@ const rowDrop = () => {
hideLoading();
}
},
}
},
);
};
async function handleSubmit() {
isLoading.value = true
isLoading.value = true;
try {
let form = formRef.value.form;
await formRef.value.submit()
console.log(formRef.value)
const userStore = useUserStore()
const form = formRef.value.form;
await formRef.value.submit();
console.log(formRef.value);
const userStore = useUserStore();
console.log(form2Ref.value.form)
console.log(form2Ref.value.form);
let newForm = {}
let newForm = {};
//
let fileList = formRef.value.form.fileList
let files: any = []
if (fileList && fileList.length) {
files = await fileUploader.upload(fileList, { source: 'erp' })
const fileList = formRef.value.form.fileList;
let files: any = [];
if (fileList && fileList.length > 0) {
files = await fileUploader.upload(fileList, { source: 'erp' });
}
console.log(files)
console.log(files);
if (files) {
newForm.fileUuid = (files.map((item) => item.fileUuid) || []).join(',')
newForm.fileUuid = (files.map((item) => item.fileUuid) || []).join(',');
}
//
let fileList2 = form2Ref.value.form.fileList
let files2: any = []
if (fileList2 && fileList2.length) {
files2 = await fileUploader.upload(fileList2, { source: 'erp' })
const fileList2 = form2Ref.value.form.fileList;
let files2: any = [];
if (fileList2 && fileList2.length > 0) {
files2 = await fileUploader.upload(fileList2, { source: 'erp' });
}
if (files2) {
newForm.fileUuidAfter = (files2.map((item) => item.fileUuid) || []).join(',')
newForm.fileUuidAfter = (files2.map((item) => item.fileUuid) || []).join(
',',
);
}
newForm = Object.assign({}, formRef.value.form, form2Ref.value.form, newForm)
newForm = Object.assign(
{},
formRef.value.form,
form2Ref.value.form,
newForm,
);
if (newForm.otherEquipment) {
newForm.otherEquipment = newForm.otherEquipment.join(',')
}
delete newForm.fileList
delete newForm.fileList;
console.log(newForm)
console.log(newForm);
newForm.meeting = getDictObj(DICT_TYPE.meeting_room, newForm.meetingId)?.label;
newForm.meeting = getDictObj(
DICT_TYPE.meeting_room,
newForm.meetingId,
)?.label;
await Apis.meeting.post_save({ data: newForm }).then((data) => {
message.success("提交成功");
message.success('提交成功');
back();
});
} catch (error) {
message.error("提交失败,请稍候再试");
console.log(error)
message.error('提交失败,请稍候再试');
console.log(error);
} finally {
isLoading.value = false
isLoading.value = false;
}
}
// function handleSubmit() {
@ -465,70 +460,65 @@ async function handleSubmit() {
// })
// chooseUserModalApi.open()
// }
const currData = ref({})
const currData = ref({});
onMounted(async () => {
isLoading.value = true
console.log(id)
isLoading.value = true;
console.log(id);
try {
if (id) {
let data = await Apis.meeting.get_page({ params: { guid: id } })
data = data.rows[0]
let data = await Apis.meeting.get_page({ params: { guid: id } });
data = data.rows[0];
data.otherEquipment = (data.otherEquipment || '').split(',')
console.log(data)
console.log(data);
currData.value = data;
nextTick(() => {
formRef.value.setFormData(data)
formRef.value.setFormData(data);
form2Ref.value.setFormData({
meetingContent: data.meetingContent,
conferee: data.conferee,
})
})
});
});
if (data.fileUuid) {
let files = await fileUploader.select(data.fileUuid)
console.log(files)
const files = await fileUploader.select(data.fileUuid);
console.log(files);
nextTick(() => {
formRef.value.setFormData({
fileList: files
})
})
fileList: files,
});
});
}
if (data.fileUuidAfter) {
let files = await fileUploader.select(data.fileUuidAfter)
console.log(files)
const files = await fileUploader.select(data.fileUuidAfter);
console.log(files);
nextTick(() => {
form2Ref.value.setFormData({
fileList: files
})
})
fileList: files,
});
});
}
loadDataByAddressor()
loadDataByAddressor();
}
} catch (error) {
console.log(error);
Modal.error({
title: "提示",
content: "当前会议不存在",
title: '提示',
content: '当前会议不存在',
onOk() {
back();
},
});
} finally {
isLoading.value = false
isLoading.value = false;
}
})
});
let initTime: any;
nextTick(() => {
console.log(pageRef.value)
//
if (id) {
initTime = setTimeout(() => {
@ -551,36 +541,44 @@ onUnmounted(() => {
</script>
<template>
<Page ref="pageRef" contentClass="h-full flex flex-col ">
<Page ref="pageRef" content-class="h-full flex flex-col ">
<ChooseUserModal class="w-[950px]" @row-click="handleChooseUserConfirm" />
<SpokenPersonEditModal class="w-[600px]" @success="loadDataByAddressor()" />
<SpokenPersonEditModal class="w-[950px]" @success="loadDataByAddressor()" />
<a-affix
:target="() => pageRef.bodyRef"
:offset-top="0"
:style="{ zIndex: 50 }"
:target="() => pageRef.bodyRef"
>
<div class="bg-white w-full pt-1 pl-1">
<div class="w-full bg-white pl-1 pt-1">
<a-space>
<vben-button variant="primary" @click="handleSubmit()">提交</vben-button>
<vben-button variant="primary" @click="handleSubmit()">
提交
</vben-button>
<vben-button v-if="id" variant="destructive" @click="handleDelete()">
删除
</vben-button>
<vben-button variant="secondary" @click="handleBack()">返回</vben-button>
<vben-button variant="secondary" @click="handleBack()">
返回
</vben-button>
</a-space>
</div>
</a-affix>
<a-spin :spinning="isLoading">
<div class="mx-auto w-[800px] overflow-auto">
<div class="mx-auto w-[80vw] overflow-auto">
<a-space direction="vertical">
<a-card title="会议信息" size="small" class="w-full">
<a-card class="w-full" size="small" title="会议信息">
<fs-form ref="formRef" class="w-full" v-bind="formBinding">
<template #form_fileList="scope">
<a-upload v-model:fileList="scope.form.fileList" accept=".pdf,.ppt,.pptx" :max-count="3" name="file"
:before-upload="beforeUpload" @change="handleChange">
<a-upload
v-model:file-list="scope.form.fileList"
:before-upload="() => false"
:max-count="3"
accept=".pdf,.ppt,.pptx"
name="file"
@change="handleChange"
>
<a-button>
<MdiUpload />
点击上传
@ -590,11 +588,17 @@ onUnmounted(() => {
</fs-form>
</a-card>
<a-card title="会议内容" size="small">
<a-card size="small" title="会议内容">
<fs-form ref="form2Ref" v-bind="form2Binding">
<template #form_fileList="scope">
<a-upload v-model:fileList="scope.form.fileList" accept=".pdf,.ppt,.pptx" :max-count="3" name="file"
:before-upload="beforeUpload" @change="handleChange2">
<a-upload
v-model:file-list="scope.form.fileList"
:before-upload="() => false"
:max-count="3"
accept=".pdf,.ppt,.pptx"
name="file"
@change="handleChange2"
>
<a-button>
<MdiUpload />
点击上传
@ -604,15 +608,24 @@ onUnmounted(() => {
</fs-form>
</a-card>
<a-card title="发言人员" size="small" class="w-[800px]">
<a-card size="small" title="发言人员">
<template #extra>
<a-button v-if="id" size="small" type="primary" @click="handleOpenSpokespersonModal()">
<MdiAdd class="text-lg mr-0.5" />
<a-button
v-if="id"
size="small"
type="primary"
@click="handleOpenSpokespersonModal()"
>
<MdiAdd class="mr-0.5 text-lg" />
新增发言人员
</a-button>
</template>
<VxeGrid v-if="id" ref="xGridRef" v-bind="gridOptions" class="sortable-row-demo w-full">
<VxeGrid
v-if="id"
ref="xGridRef"
v-bind="gridOptions"
class="sortable-row-demo w-full"
>
<template #dragBtn>
<span class="drag-btn">
<span class="icon-[mdi--drag-variant]"></span>
@ -620,34 +633,49 @@ onUnmounted(() => {
</span>
</template>
<template #dragTip>
<vxe-tooltip v-model="showHelpTip" content="按住后可以上下拖动排序" enterable>
<i class="vxe-icon-question-circle-fill" @click="showHelpTip = !showHelpTip"></i>
<vxe-tooltip
v-model="showHelpTip"
content="按住后可以上下拖动排序"
enterable
>
<i
class="vxe-icon-question-circle-fill"
@click="showHelpTip = !showHelpTip"
></i>
</vxe-tooltip>
</template>
<template #toolbar_buttons></template>
<template #operate="{ row }">
<a-space>
<a-button type="text" class="text-yellow-500" size="small" @click="handleOpenSpokespersonModal(row)">
<a-button
class="text-yellow-500"
size="small"
type="text"
@click="handleOpenSpokespersonModal(row)"
>
编辑
</a-button type="text">
</a-button>
<a-button type="text" class="text-red-500" size="small" @click="removeRow(row)">
<a-button
class="text-red-500"
size="small"
type="text"
@click="removeRow(row)"
>
移除
</a-button type="text">
</a-button>
</a-space>
</template>
</VxeGrid>
<div v-else>
<a-empty description="请完成填报提交后,点击查询页面的编辑按钮添加会议发言人">
<template #extra> </template>
</a-empty>
<a-empty
description="请完成填报提交后,点击查询页面的编辑按钮添加会议发言人"
/>
</div>
</a-card>
</a-space>
</div>
</a-spin>
</Page>
</template>

View File

@ -1,31 +1,35 @@
<script lang="ts" setup>
import { ref, nextTick } from "vue";
import { useVbenModal } from "@vben/common-ui";
import { MdiCloudUpload } from "@vben/icons";
import type { UploadChangeParam, UploadProps } from "ant-design-vue";
import { message } from "ant-design-vue";
import Apis from "#/api";
import chooseUserModal from "#/views/system/user/choose-user-modal.vue";
import { FileUploader } from "#/utils/file";
import type { UploadChangeParam } from 'ant-design-vue';
const fileUploader = new FileUploader({})
import { nextTick, ref } from 'vue';
const [messageApi] = message.useMessage();
import { useVbenModal } from '@vben/common-ui';
import { MdiCloudUpload } from '@vben/icons';
import { message } from 'ant-design-vue';
import { logger } from 'common-utils';
import Apis from '#/api';
import { FileUploader } from '#/utils/file';
import chooseUserModal from '#/views/system/user/choose-user-modal.vue';
const emit = defineEmits<{
(e: "success"): void;
(e: 'success'): void;
}>();
const fileUploader = new FileUploader({});
const [messageApi] = message.useMessage();
const [ChooseUserModal, chooseUserModalApi] = useVbenModal({
connectedComponent: chooseUserModal,
});
let isConfirmLoading = ref(false);
const isConfirmLoading = ref(false);
const data = ref({
isUpdate: false,
record: {}
record: {},
});
const formRef = ref();
const uploading = ref<boolean>(false);
@ -33,33 +37,34 @@ const uploading = ref<boolean>(false);
const formBinding = ref({
col: { span: 24 },
initialForm: {},
labelCol: { style: { width: "120px" } },
labelCol: { style: { width: '120px' } },
columns: {
addressor: {
title: "发言人员",
key: "addressor",
title: '发言人员',
key: 'addressor',
component: {
name: "a-input",
name: 'a-input',
readOnly: true,
vModel: "value",
vModel: 'value',
onClick: () => {
chooseUserModalApi.open();
},
},
rules: [{ required: true, message: "请选择发言人员" }],
rules: [{ required: true, message: '请选择发言人员' }],
},
abstracts: {
title: "会议内容",
key: "abstracts",
title: '会议内容',
key: 'abstracts',
component: {
name: "a-textarea",
vModel: "value",
name: 'a-textarea',
vModel: 'value',
autoSize: { minRows: 6, maxRows: 8 },
},
rules: [{ required: true, message: "请输入发言内容" }],
rules: [{ required: true, message: '请输入发言内容' }],
},
fileList: {
title: "附件上传",
key: "fileList",
title: '附件上传',
key: 'fileList',
// rules: [{ required: true, message: "" }],
},
},
@ -67,14 +72,10 @@ const formBinding = ref({
const handleChange = (info: UploadChangeParam) => {
formRef.value.setFormData({
fileList: info.fileList.length ? info.fileList : [],
fileList: info.fileList.length > 0 ? info.fileList : [],
});
};
const beforeUpload: UploadProps["beforeUpload"] = (file) => {
return false;
};
const currUser = ref({});
function handleChooseUserRowClick(row) {
@ -82,49 +83,46 @@ function handleChooseUserRowClick(row) {
addressor: row.label,
});
currUser.value = row;
chooseUserModalApi.close()
chooseUserModalApi.close();
}
const [Modal, modalApi] = useVbenModal({
async onOpenChange(isOpen: boolean) {
if (isOpen) {
// fileList.value = [];
let fileList: any[] = []
let fileList: any[] = [];
data.value = modalApi.getData<Record<string, any>>();
if (data.value.record.fileUuid) {
let files = await fileUploader.select(data.value.record.fileUuid)
fileList = files
const files = await fileUploader.select(data.value.record.fileUuid);
fileList = files;
}
nextTick(() => {
formRef.value.setFormData({
...data.value.record,
fileList: fileList,
fileList,
});
});
}
isConfirmLoading.value = false;
},
async onConfirm() {
isConfirmLoading.value = true
isConfirmLoading.value = true;
try {
// console.info("onConfirm");
formRef.value?.submit();
let form = formRef.value.form;
const form = formRef.value.form;
let fileList = formRef.value.form.fileList
let files: any = []
if (fileList && fileList.length) {
files = await fileUploader.upload(fileList, { source: 'erp' })
const fileList = formRef.value.form.fileList;
let files: any = [];
if (fileList && fileList.length > 0) {
files = await fileUploader.upload(fileList, { source: 'erp' });
}
console.log(files)
if (files) {
form.fileUuid = (files.map((item) => item.fileUuid) || []).join(',')
form.fileUuid = (files.map((item) => item.fileUuid) || []).join(',');
}
form.addressorId = currUser.value?.ACCOUNT_ID;
@ -132,29 +130,38 @@ const [Modal, modalApi] = useVbenModal({
form.unitId = currUser.value?.ORG_ID;
form.meetingGuid = data.value.record.meetingGuid;
await Apis.addressor.post_save({ data: form })
message.success("操作成功")
modalApi.close()
emit('success')
await Apis.addressor.post_save({ data: form });
message.success('操作成功');
modalApi.close();
emit('success');
} catch (error) {
console.log(error)
logger.error('', error);
} finally {
isConfirmLoading.value = false
isConfirmLoading.value = false;
}
},
});
</script>
<template>
<Modal :title="data.isUpdate ? '修改会议发言人' : '新增会议发言人'" :loading="isConfirmLoading" :confirmLoading="isConfirmLoading">
<Modal
:confirm-loading="isConfirmLoading"
:loading="isConfirmLoading"
:title="data.isUpdate ? '修改会议发言人' : '新增会议发言人'"
>
<ChooseUserModal class="w-[950px]" @row-click="handleChooseUserRowClick" />
<fs-form ref="formRef" v-bind="formBinding">
<template #form_fileList="scope">
<a-upload-dragger v-model:fileList="scope.form.fileList" accept=".pdf,.ppt,.pptx" :max-count="1" name="file"
:before-upload="beforeUpload" @change="handleChange">
<a-upload-dragger
v-model:file-list="scope.form.fileList"
:before-upload="() => false"
:max-count="1"
accept=".pdf,.ppt,.pptx"
name="file"
@change="handleChange"
>
<div class="flex flex-col items-center">
<MdiCloudUpload class="text-[50px] text-center text-gray-600" />
<MdiCloudUpload class="text-center text-[50px] text-gray-600" />
<p class="ant-upload-text">点击或拖动文件到该区域来上传</p>
<p class="ant-upload-hint">仅支持 pdfppt 类型的文件</p>
</div>

View File

@ -1,16 +1,110 @@
<script setup lang="ts">
import { computed, onMounted, reactive, ref } from 'vue';
import { MdiExport, MdiRadioChecked, MdiRadioUnchecked } from '@vben/icons';
import { message } from 'ant-design-vue';
import Apis from '#/api';
import { useVxeTable } from '#/hooks/vxeTable';
import { getFormSchema } from '../list/crud.tsx';
import { getColumns } from './crud.tsx';
const searchRef = ref();
const { xGridRef, triggerProxy, gridProps } = useVxeTable({ ref: 'xGridRef' });
const treeData = ref([]);
/** Hooks - 表格 */
const gridOptions = reactive(
gridProps({
columns: getColumns({ type: 'taizhang' }),
proxyConfig: {
autoLoad: false,
ajax: {
query: ({ page }) => {
return Apis.meeting.get_page({
params: {
pageNum: page.currentPage,
pageSize: page.pageSize,
...searchRef.value?.formData,
},
});
},
},
},
pagerConfig: {
enabled: true,
},
toolbarConfig: {
enabled: true,
},
}),
);
function handleExport() {
const $grid = xGridRef.value;
if ($grid) {
$grid.exportData({
type: 'xlsx',
});
message.success('导出成功');
}
}
/** 选中数据 */
const selectRow: any = computed(() => {
return xGridRef.value?.getRadioRecord() || null;
});
/** 单选框选中事件 */
const setSelectRow = (row: any) => {
if (selectRow.value && selectRow.value.guid === row.guid) {
xGridRef.value?.clearRadioRow();
} else {
xGridRef.value?.setRadioRow(row);
}
};
/** 表格单元格单击事件 */
function handleCellClick({ row }) {
setSelectRow(row);
}
function toDetail(row) {
window.open(`/iframe/meeting/start/${row.guid}`, '_blank');
}
onMounted(() => {
triggerProxy('reload');
});
const searchForm = ref({
...getFormSchema(),
onSearch(_context: any) {
triggerProxy('reload');
},
});
</script>
<template>
<div class="mx-auto h-[100vh] w-[1200px] flex flex-col">
<p class="text-2xl font-bold my-4 text-center" style="color: #001428">
<div class="mx-auto flex h-[100vh] w-[90vw] flex-col">
<p class="my-4 text-center text-2xl font-bold" style="color: #001428">
供热公司网络生产会议系统 - 会议查询篇
</p>
<fs-search ref="searchRef" v-bind="searchForm"> </fs-search>
<div class="flex-1 min-h-300px pb-8">
<vxe-grid ref="xGridRef" v-bind="gridOptions" @cell-click="handleCellClick">
<fs-search ref="searchRef" v-bind="searchForm" />
<div class="min-h-300px flex-1 pb-8">
<vxe-grid
ref="xGridRef"
v-bind="gridOptions"
@cell-click="handleCellClick"
>
<template #toolbar_buttons>
<a-space>
<a-button type="primary" @click="handleExport()">
<MdiExport class="text-lg mr-0.5" />
<MdiExport class="mr-0.5 text-lg" />
导出
</a-button>
</a-space>
@ -29,166 +123,16 @@
</template>
<template #meetingThemeSlot="{ row }">
<span class="cursor-pointer text-blue-500 hover:underline" @click="toDetail(row)">
<span
class="cursor-pointer text-blue-500 hover:underline"
@click="toDetail(row)"
>
{{ row.meetingTheme }}
</span>
</template>
</vxe-grid>
</div>
</div>
</template>
<script setup lang="ts">
import { defineComponent, ref, computed, reactive, onMounted } from 'vue';
import { FsCrud } from '@fast-crud/fast-crud';
import { type VxeGridProps } from 'vxe-table'
import { Page, useVbenModal } from '@vben/common-ui';
import { useVxeTable } from '#/hooks/vxeTable';
import {
type CreateCrudOptionsProps,
useColumns,
useFormWrapper,
useFs,
utils,
} from '@fast-crud/fast-crud';
import { MdiAdd, MdiUpdate, MdiDelete, MdiImport, MdiExport, MdiRadioUnchecked, MdiRadioChecked } from '@vben/icons';
import { getColumns } from './crud.tsx';
import { dict } from "@fast-crud/fast-crud";
import { getMonthStartAndEnd } from '#/utils/time'
import Apis from '#/api'
import dayjs from 'dayjs';
import { message } from "ant-design-vue";
import { Modal } from 'ant-design-vue';
import { useRouter } from 'vue-router'
import { getFormSchema } from '../list/crud.tsx';
const router = useRouter();
const checkedValue = ref('all')
const exportSearchParams = ref<any>({
daterange: getMonthStartAndEnd(),
});
const radioStyle = reactive({
display: 'flex',
height: '30px',
lineHeight: '30px',
});
const searchRef = ref()
let isConfirmLoading = ref(false)
const [_Modal, modalApi] = useVbenModal({
async onConfirm() {
isConfirmLoading.value = true
try {
let params = {};
if (checkedValue.value == "daterange") {
params = {
startDate: exportSearchParams.value.daterange[0],
endDate: exportSearchParams.value.daterange[1],
};
}
let res = await Apis.zbgl.post_export({
params: params, config: {
meta: {
responseType: 'blob'
}
}
}).send();
message.success("导出成功");
modalApi.close()
showExportModal.value = false;
} catch (error) {
console.error(error);
} finally {
isConfirmLoading.value = false
}
console.info("onConfirm");
},
});
const { xGridRef, triggerProxy, gridProps } = useVxeTable({ ref: 'xGridRef' });
const treeData = ref([]);
/** Hooks - 表格 */
const gridOptions = reactive(gridProps({
columns: getColumns({ type: "taizhang" }),
proxyConfig: {
autoLoad: false,
ajax: {
query: ({ page }) => {
return Apis.meeting.get_page({ params: { pageNum: page.currentPage, pageSize: page.pageSize, ...searchRef.value?.formData } })
}
},
},
pagerConfig: {
enabled: true
},
toolbarConfig: {
enabled: true
},
}));
function handleExport() {
const $grid = xGridRef.value;
if ($grid) {
$grid.exportData({
type: "xlsx",
});
message.success("导出成功");
}
}
/** 选中数据 */
const selectRow: Recordable = computed(() => {
return xGridRef.value?.getRadioRecord() || null;
});
/** 单选框选中事件 */
const setSelectRow = (row: Recordable) => {
if (selectRow.value && selectRow.value['guid'] === row['guid']) {
xGridRef.value?.clearRadioRow();
} else {
xGridRef.value?.setRadioRow(row);
}
};
/** 表格单元格单击事件 */
function handleCellClick({ row }) {
setSelectRow(row);
}
function toDetail(row){
window.open("/iframe/meeting/start/" + row.guid, "_blank");
}
onMounted(() => {
triggerProxy('reload')
})
let searchParams = reactive({})
const searchForm = ref({
...getFormSchema(),
onSearch(context: any) {
console.log(searchRef.value)
triggerProxy('reload')
},
onReset(context: any) {
searchParams = context.form
},
});
function toPage() {
window.open("/iframe/meeting/standing-book", "_blank");
}
//
</script>
<style></style>

View File

@ -1,15 +1,16 @@
<script setup lang="ts">
import { ref, computed, onMounted } from "vue";
import { computed, onMounted, ref } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { useRoute, useRouter } from "vue-router";
import dayjs from "dayjs";
import Apis from "#/api";
import { Modal, message } from "ant-design-vue";
import { FileUploader } from "#/utils/file";
import { message, Modal } from 'ant-design-vue';
import dayjs from 'dayjs';
import Apis from '#/api';
import { FileUploader } from '#/utils/file';
const router = useRouter();
const fileUploader = new FileUploader({})
const fileUploader = new FileUploader({});
/**
* 会议开始页面
@ -26,54 +27,50 @@ const currentIndex = ref(0);
const addressorData = ref([]);
const currentSpeaker = computed(() => addressorData.value[currentIndex.value]);
const currData = ref({
guid: "",
meetingTheme: "",
guid: '',
meetingTheme: '',
meetingDate: null,
custom_otherEquipment: [],
otherEquipment: "",
fileName: "",
fileUuid: "",
fileNameAfter: "",
fileUuidAfter: "",
otherEquipment: '',
fileName: '',
fileUuid: '',
fileNameAfter: '',
fileUuidAfter: '',
});
async function loadDataByMeeting() {
try {
let data = await Apis.meeting.get_page({ params: { guid: id } })
console.log(data)
const data = await Apis.meeting.get_page({ params: { guid: id } });
console.log(data);
currData.value = data?.rows[0] || {};
loadAddressorData();
} catch (error) {
console.log(error);
Modal.error({
title: "提示",
content: "当前会议不存在",
title: '提示',
content: '当前会议不存在',
onOk() {
back();
},
});
} finally {
}
}
const fileInfo = ref({});
let isAddressLoading = ref(false);
const isAddressLoading = ref(false);
async function loadAddressorData() {
isAddressLoading.value = true;
Apis.addressor.get_list({ params: { meetingId: currData.value.guid } })
Apis.addressor
.get_list({ params: { meetingId: currData.value.guid } })
.then(async (res: any) => {
for (const item of res.rows) {
if(item.fileUuid){
item.fileUuidArr = item.fileUuid.split(",");
if (item.fileUuid) {
item.fileUuidArr = item.fileUuid.split(',');
}
}
@ -81,11 +78,11 @@ async function loadAddressorData() {
console.log(addressorData.value);
})
.catch((err) => {
console.log(err);
.catch((error) => {
console.log(error);
})
.finally(() => {
loadDataByFiles()
loadDataByFiles();
isAddressLoading.value = false;
});
}
@ -93,24 +90,24 @@ async function loadAddressorData() {
async function loadDataByFiles() {
//
let meetingFileUuid = (currData.value.fileUuid || '').split(',')
let meetingFileUuidAfter = (currData.value.fileUuidAfter || '').split(',')
const meetingFileUuid = (currData.value.fileUuid || '').split(',');
const meetingFileUuidAfter = (currData.value.fileUuidAfter || '').split(',');
let fileArr = [];
const fileArr = [];
for (const addressor of addressorData.value) {
let arr = (addressor.fileUuid || '').split(',')
const arr = (addressor.fileUuid || '').split(',');
for (const fileUuid of arr) {
fileArr.push(fileUuid)
fileArr.push(fileUuid);
}
}
let combinedArray = [
...new Set([...meetingFileUuid, ...meetingFileUuidAfter, ...fileArr])
].filter(item => item !== null && item !== undefined && item !== '');
const combinedArray = [
...new Set([...fileArr, ...meetingFileUuid, ...meetingFileUuidAfter]),
].filter((item) => item !== null && item !== undefined && item !== '');
if (combinedArray.length) {
let files = await fileUploader.select(combinedArray.join(','))
if (combinedArray.length > 0) {
const files = await fileUploader.select(combinedArray.join(','));
for (const file of files) {
fileInfo.value[file.uid] = file;
@ -130,8 +127,8 @@ onMounted(async () => {
await loadDataByMeeting();
} else {
Modal.confirm({
title: "提示",
content: "当前会议不存在",
title: '提示',
content: '当前会议不存在',
onOk() {
back();
},
@ -141,10 +138,10 @@ onMounted(async () => {
const startMeeting = () => {
//
if (addressorData.value.length) {
if (addressorData.value.length > 0) {
meetingStarted.value = true;
} else {
message.warning("暂无发言人员,请先添加发言人员后再试")
message.warning('暂无发言人员,请先添加发言人员后再试');
}
};
@ -171,97 +168,127 @@ function downloadFile(fileUrl) {
const link = document.createElement('a');
link.href = fileUrl;
link.download = 'file.pdf'; //
document.body.appendChild(link);
document.body.append(link);
link.click();
document.body.removeChild(link);
link.remove();
}
</script>
<template>
<div class="container max-w-1200px mx-auto p-4 bg-gray-100 rounded-lg shadow-lg my-8 relative overflow-hidden">
<div
class="max-w-1200px container relative mx-auto overflow-hidden rounded-lg bg-gray-100 p-4 shadow-lg"
>
<div
class="absolute top-0 left-0 w-full h-full bg-gradient-to-r from-blue-500 to-purple-500 opacity-30 -z-10 animate-gradient-shift">
</div>
<h1 class="text-3xl font-bold text-center mb-6 relative z-10">
class="animate-gradient-shift absolute left-0 top-0 -z-10 h-full w-full bg-gradient-to-r from-blue-500 to-purple-500 opacity-30"
></div>
<h1 class="relative z-10 mb-6 text-center text-3xl font-bold">
{{ currData.meetingTheme }}
<span v-if="currData.isEmployeeRepresentatives == 1"
class="absolute -top-2 -right-2 px-2 py-1 bg-red-500 text-white text-xs rounded-full animate-pulse">
<span
v-if="currData.isEmployeeRepresentatives == 1"
class="absolute -right-2 -top-2 animate-pulse rounded-full bg-red-500 px-2 py-1 text-xs text-white"
>
职工代表会
</span>
</h1>
<div class="flex justify-between items-center mb-4 relative z-10">
<div
v-show="!meetingStarted"
class="relative z-10 mb-4 flex items-center justify-between"
>
<div class="flex items-center">
<i class="ri-calendar-line text-gray-500 mr-2"></i>
<i class="ri-calendar-line mr-2 text-gray-500"></i>
<span v-if="currData.meetingDate" class="text-gray-700">
{{ dayjs(currData.meetingDate).format("YYYY年MM月DD日 HH时mm分") }}
{{ dayjs(currData.meetingDate).format('YYYY年MM月DD日 HH时mm分') }}
</span>
</div>
<div class="flex items-center">
<i class="ri-map-pin-line text-gray-500 mr-2"></i>
<i class="ri-map-pin-line mr-2 text-gray-500"></i>
<span class="text-gray-700">{{ currData.meeting }}</span>
</div>
</div>
<div class="bg-white p-4 rounded-lg shadow-md mb-4 relative z-10">
<h2 class="text-lg font-bold mb-2">会议信息</h2>
<div
v-show="!meetingStarted"
class="relative z-10 mb-4 rounded-lg bg-white p-4 shadow-md"
>
<h2 class="mb-2 text-lg font-bold">会议信息</h2>
<div class="flex space-x-4">
<div class="flex items-center">
<span class="font-bold mr-2">会议类型:</span>
<span class="mr-2 font-bold">会议类型:</span>
<span class="text-gray-700">{{ currData.meetingType }}</span>
</div>
<div class="flex items-center" v-if="isRepresentativesMeeting">
<span class="font-bold mr-2">职工代表会:</span>
<div v-if="isRepresentativesMeeting" class="flex items-center">
<span class="mr-2 font-bold">职工代表会:</span>
<span class="text-green-500">{{
currData.isEmployeeRepresentatives == 1 ? "是" : "否"
}}</span>
currData.isEmployeeRepresentatives == 1 ? '是' : '否'
}}</span>
</div>
</div>
<div class="flex space-x-4">
<div class="flex items-center">
<span class="font-bold mr-2">会议内容:</span>
<span class="mr-2 font-bold">会议内容:</span>
<span class="text-gray-700">{{ currData.meetingContent }}</span>
</div>
</div>
<div class="flex space-x-4">
<div class="flex items-center">
<span class="font-bold mr-2">会议附件:</span>
<span class="mr-2 font-bold">会议附件:</span>
</div>
</div>
<div class="flex space-x-4">
<div v-if="currData.fileUuid" class="flex items-center">
<ul class="list-none">
<li v-for="fileUuid in currData.fileUuid.split(',')" :key="fileUuid" class="mb-2">
<a class="text-gray-700 cursor-pointer hover:underline">{{ fileInfo[fileUuid]?.name
}}</a>
<button class="ml-2 px-2 py-1 bg-green-500 text-white rounded hover:bg-green-600 transition"
@click="downloadFile(fileInfo[fileUuid]?.url)">
<li
v-for="fileUuid in currData.fileUuid.split(',')"
:key="fileUuid"
class="mb-2"
>
<a class="cursor-pointer text-gray-700 hover:underline">{{
fileInfo[fileUuid]?.name
}}</a>
<button
class="ml-2 rounded bg-green-500 px-2 py-1 text-white transition hover:bg-green-600"
@click="downloadFile(fileInfo[fileUuid]?.url)"
>
下载
</button>
</li>
</ul>
</div>
<div v-else class="flex items-center">
<span class="text-gray-700">无附件</span>
</div>
</div>
</div>
<div v-if="!meetingStarted" class="text-center text-gray-600 my-4 relative z-10">
<div
v-if="!meetingStarted"
class="relative z-10 my-4 text-center text-gray-600"
>
会议状态: 待开始
</div>
<div v-else class="text-center text-gray-600 my-4 relative z-10">
<div v-else class="relative z-10 my-4 text-center text-gray-600">
会议状态: 已开始
</div>
<div v-if="!meetingStarted" class="text-center text-gray-600 my-4 relative z-10">
<div
v-if="!meetingStarted"
class="relative z-10 my-4 text-center text-gray-600"
>
预计总发言人数: {{ addressorData.length }}
</div>
<template v-if="!meetingStarted && addressorData.length > 0">
<h2 class="text-xl font-bold mb-2 relative z-10">会议发言:</h2>
<div class="text-gray-600 my-4 relative z-10">
<h2 class="relative z-10 mb-2 text-xl font-bold">会议发言:</h2>
<div class="relative z-10 my-4 text-gray-600">
预计发言 {{ addressorData.length }} 个单位
</div>
<ul class="list-disc list-inside space-y-2 relative z-10 h-[30vh] overflow-auto">
<li v-for="(speaker, index) in addressorData" :key="index" class="flex items-center">
<span class="w-8 h-8 rounded-full bg-blue-500 text-white flex items-center justify-center mr-3">
<ul
class="relative z-10 h-[30vh] list-inside list-disc space-y-2 overflow-auto"
>
<li
v-for="(speaker, index) in addressorData"
:key="index"
class="flex items-center"
>
<span
class="mr-3 flex h-8 w-8 items-center justify-center rounded-full bg-blue-500 text-white"
>
{{ speaker.addressor.substring(0, 1) }}
</span>
<span> {{ speaker.unit }} {{ speaker.addressor }} </span>
@ -269,32 +296,46 @@ function downloadFile(fileUrl) {
</ul>
</template>
<div v-if="meetingStarted" class="bg-white p-4 rounded-lg shadow-md mb-4 relative z-10">
<div class="speaker-info bg-white p-6 rounded-lg shadow-md mb-4">
<div class="flex justify-between items-center mb-4">
<div
v-if="meetingStarted"
class="relative z-10 mb-4 rounded-lg bg-white p-4 shadow-md"
>
<div class="speaker-info mb-4 rounded-lg bg-white p-6 shadow-md">
<div class="mb-4 flex items-center justify-between">
<div class="text-lg font-bold">
发言单位: <span class="text-blue-500">{{ currentSpeaker.unit }}</span>
发言单位:
<span class="text-blue-500">{{ currentSpeaker.unit }}</span>
</div>
<div class="text-lg font-bold">
发言人: <span class="text-blue-500">{{ currentSpeaker.addressor }}</span>
发言人:
<span class="text-blue-500">{{ currentSpeaker.addressor }}</span>
</div>
</div>
<div class="text-center mb-4 text-gray-600">
<div class="mb-4 text-center text-gray-600">
预计发言 {{ addressorData.length }} 个单位
{{ currentIndex + 1 }} 个发言人正在进行中......
</div>
<div class="content-area overflow-y-auto bg-gray-50 p-4 rounded-lg mb-4 h-64 flex items-center justify-center">
<p class="text-gray-700 text-center">{{ currentSpeaker.abstracts }}</p>
<div
class="content-area mb-4 flex h-[50vh] items-center justify-center overflow-y-auto rounded-lg bg-gray-50 p-4"
>
<p class="text-center text-gray-700">
{{ currentSpeaker.abstracts }}
</p>
</div>
<div class="attachments">
<h3 class="text-lg font-bold mb-2">附件:</h3>
<h3 class="mb-2 text-lg font-bold">附件:</h3>
<ul v-if="currentSpeaker.fileUuid" class="list-none">
<li v-for="fileUuid in currentSpeaker.fileUuidArr" :key="fileUuid" class="mb-2">
<a :href="fileInfo[fileUuid]?.url"
class="text-gray-700 hover:text-blue-500 cursor-pointer hover:underline">{{
fileInfo[fileUuid]?.name
}}</a>
<li
v-for="fileUuid in currentSpeaker.fileUuidArr"
:key="fileUuid"
class="mb-2"
>
<a
:href="fileInfo[fileUuid]?.url"
class="cursor-pointer text-gray-700 hover:text-blue-500 hover:underline"
>{{ fileInfo[fileUuid]?.name }}</a
>
<!-- <button
class="ml-4 px-2 py-1 bg-blue-500 text-white rounded hover:bg-blue-600 transition"
>
@ -310,34 +351,48 @@ function downloadFile(fileUrl) {
<span v-else class="text-gray-700">无附件</span>
</div>
</div>
<div class="flex justify-between mt-4">
<button v-if="currentIndex > 0" @click="prevSpeaker"
class="py-2 px-4 bg-blue-500 text-white font-bold rounded-lg hover:bg-blue-600 transition">
上一个发言单位<span class="text-gray-200 text-sm">{{
<div class="mt-4 flex justify-between">
<button
v-if="currentIndex > 0"
class="rounded-lg bg-blue-500 px-4 py-2 font-bold text-white transition hover:bg-blue-600"
@click="prevSpeaker"
>
上一个发言单位<span class="text-sm text-gray-200">{{
addressorData[currentIndex - 1].unit
}}</span>-
<span class="text-gray-200 text-sm">{{
}}</span
>-
<span class="text-sm text-gray-200">{{
addressorData[currentIndex - 1].addressor
}}</span>
}}</span>
</button>
<view v-if="currentIndex == 0"> </view>
<button v-if="currentIndex < addressorData.length - 1" @click="nextSpeaker"
class="py-2 px-4 bg-blue-500 text-white font-bold rounded-lg hover:bg-blue-600 transition">
下一个发言单位<span class="text-gray-200 text-sm">{{
<view v-if="currentIndex == 0" />
<button
v-if="currentIndex < addressorData.length - 1"
class="rounded-lg bg-blue-500 px-4 py-2 font-bold text-white transition hover:bg-blue-600"
@click="nextSpeaker"
>
下一个发言单位<span class="text-sm text-gray-200">{{
addressorData[currentIndex + 1].unit
}}</span>-
<span class="text-gray-200 text-sm">{{
}}</span
>-
<span class="text-sm text-gray-200">{{
addressorData[currentIndex + 1].addressor
}}</span>
}}</span>
</button>
<button v-if="currentIndex === addressorData.length - 1" @click="endMeeting"
class="py-2 px-4 bg-red-500 text-white font-bold rounded-lg hover:bg-red-600 transition">
<button
v-if="currentIndex === addressorData.length - 1"
class="rounded-lg bg-red-500 px-4 py-2 font-bold text-white transition hover:bg-red-600"
@click="endMeeting"
>
会议结束
</button>
</div>
</div>
<button v-if="!meetingStarted" @click="startMeeting"
class="mt-6 w-full py-2 bg-green-500 text-white font-bold rounded-lg hover:bg-green-600 transition duration-300 transform">
<button
v-if="!meetingStarted"
class="mt-6 w-full transform rounded-lg bg-green-500 py-2 font-bold text-white transition duration-300 hover:bg-green-600"
@click="startMeeting"
>
开始会议
</button>
</div>

View File

@ -9,14 +9,12 @@ export const PrimaryKey = 'guid';
export function getColumns(_params: any = {}): VxeGridPropTypes.Columns {
return [
{ type: 'seq', width: 50 },
{ field: 'taskName', title: '任务名称', width: 120 },
{ field: 'taskContent', title: '任务内容', minWidth: 200 },
{ field: 'level', title: '紧急程度', width: 120 },
{ field: 'feedbackContent', title: '反馈内容', minWidth: 200 },
{ field: 'finishStatus', title: '完成情况', width: 120 },
{ field: 'ip', title: '负责部门', width: 120 },
{ field: 'starttime', title: '发起时间', width: 120 },
{ field: 'zhushi', title: '预计完成时间', width: 120 },
{ field: 'endtime', title: '截止时间', width: 120 },
{ field: 'principal', title: '负责人', width: 120 },
{ field: 'feedbackTime', title: '反馈时间', width: 150 },
{ field: 'lasttime', title: '修改时间', width: 150 },
// { field: 'principalDepartment', title: '负责部门', width: 120 },
{ field: 'executeDepartmentName', title: '执行部门', width: 120 },
// { title: '操作', width: 120, fixed: 'right', slots: { default: 'operate' } }
];

View File

@ -3,15 +3,13 @@ import { computed, onMounted, reactive, ref } from 'vue';
import { Page } from '@vben/common-ui';
import {
MdiAdd,
MdiDelete,
MdiExport,
MdiRadioChecked,
MdiRadioUnchecked,
MdiUpdate,
} from '@vben/icons';
import { message } from 'ant-design-vue';
import { message, Modal } from 'ant-design-vue';
import Apis from '#/api';
import { useVxeTable } from '#/hooks/vxeTable';
@ -60,13 +58,26 @@ function handleExport() {
}
}
function handleDelete(row) {
Modal.confirm({
title: '提示',
content: '是否确认删除该条记录?',
okType: 'danger',
onOk: async () => {
await Apis.feedback.post_deletes({ params: { ids: row.guid } });
message.success('删除成功');
triggerProxy('reload');
},
});
}
/** 选中数据 */
const selectRow: Recordable = computed(() => {
const selectRow: any = computed(() => {
return xGridRef.value?.getRadioRecord() || null;
});
/** 单选框选中事件 */
const setSelectRow = (row: Recordable) => {
const setSelectRow = (row: any) => {
if (selectRow.value && selectRow.value.guid === row.guid) {
xGridRef.value?.clearRadioRow();
} else {
@ -83,16 +94,11 @@ onMounted(() => {
triggerProxy('reload');
});
let searchParams = reactive({});
const searchForm = ref({
...getFormSchema(),
onSearch(context: any) {
console.log(searchRef.value);
onSearch(_context: any) {
triggerProxy('reload');
},
onReset(context: any) {
searchParams = context.form;
},
});
</script>
@ -107,30 +113,27 @@ const searchForm = ref({
>
<template #toolbar_buttons>
<a-space>
<a-button type="primary" @click="handleAdd()">
<MdiAdd class="mr-0.5 text-lg" />
新增
</a-button>
<a-button
<!-- <a-button
:disabled="!selectRow || !selectRow.guid"
type="primary"
@click="handleUpdate(selectRow)"
>
<MdiUpdate class="mr-0.5 text-lg" />
修改
</a-button>
<a-button
</a-button> -->
<vben-button
:disabled="!selectRow || !selectRow.guid"
type="primary"
variant="destructive"
@click="handleDelete(selectRow)"
>
<MdiDelete class="mr-0.5 text-lg" />
删除
</a-button>
<a-button type="primary" @click="handleExport()">
</vben-button>
<vben-button type="primary" @click="handleExport()">
<MdiExport class="mr-0.5 text-lg" />
导出
</a-button>
</vben-button>
</a-space>
</template>

View File

@ -3,7 +3,7 @@ import type { VxeGridPropTypes } from 'vxe-table';
import { dict } from '@fast-crud/fast-crud';
import { useRender } from '#/hooks/useRender';
import { DICT_TYPE, getDictOptions } from '#/utils/dict';
import { DICT_TYPE } from '#/utils/dict';
export const PrimaryKey = 'guid';
@ -17,14 +17,6 @@ export function getColumns(params: any = {}): VxeGridPropTypes.Columns {
fixed: 'left',
},
// { type: 'expand', width: 60, slots: { content: 'expand_content' } },
{
field: 'TASK_NAME',
title: '任务标题',
width: 200,
slots: {
default: 'task-name-slot',
},
},
{
field: 'status',
title: '任务状态',
@ -33,19 +25,19 @@ export function getColumns(params: any = {}): VxeGridPropTypes.Columns {
default: 'statusSlot',
},
},
{
field: 'taskType',
title: '任务类型',
width: 100,
slots: {
default: ({ row }) => {
return useRender.renderDict(
row.taskType,
DICT_TYPE.supervise_task_type,
);
},
},
},
// {
// field: 'taskType',
// title: '任务类型',
// width: 100,
// slots: {
// default: ({ row }) => {
// return useRender.renderDict(
// row.taskType,
// DICT_TYPE.supervise_task_type,
// );
// },
// },
// },
{
field: 'TASK_CONTENT',
title: '任务内容',
@ -57,7 +49,7 @@ export function getColumns(params: any = {}): VxeGridPropTypes.Columns {
},
},
{
field: 'taskProgress',
field: 'PROGRESS',
title: '任务进度',
minWidth: 200,
slots: {
@ -66,6 +58,16 @@ export function getColumns(params: any = {}): VxeGridPropTypes.Columns {
},
},
},
{
field: 'CONTENT',
title: '反馈内容',
width: 300,
slots: {
default: ({ row }) => {
return useRender.renderMultiLineText(row.CONTENT, {});
},
},
},
{
field: 'urgentDegree',
title: '紧急程度',
@ -73,7 +75,7 @@ export function getColumns(params: any = {}): VxeGridPropTypes.Columns {
slots: {
default: ({ row }) => {
return useRender.renderDict(
row.urgentDegree,
row.urgentDegree || '',
DICT_TYPE.supervise_emergency_level,
);
},
@ -100,12 +102,12 @@ export function getColumns(params: any = {}): VxeGridPropTypes.Columns {
},
},
{
field: 'planFinishTime',
title: '计完成日期',
field: 'PLAN_FINISH_TIME',
title: '完成日期',
width: 120,
slots: {
default: ({ row }) => {
return useRender.renderDate(row.planFinishTime, 'YYYY-MM-DD');
return useRender.renderDate(row.PLAN_FINISH_TIME, 'YYYY-MM-DD');
},
},
},
@ -161,21 +163,21 @@ export function getFormSchema(_params: any = {}) {
autoSearchTrigger: 'enter',
show: true,
},
taskType: {
title: '任务类型',
key: 'taskType',
component: {
name: 'fs-dict-select',
vModel: 'value',
class: 'min-w-[180px]',
allowClear: true,
dict: dict({
data: getDictOptions(DICT_TYPE.supervise_task_type),
}),
},
autoSearchTrigger: 'enter',
show: true,
},
// taskType: {
// title: '任务类型',
// key: 'taskType',
// component: {
// name: 'fs-dict-select',
// vModel: 'value',
// class: 'min-w-[180px]',
// allowClear: true,
// dict: dict({
// data: getDictOptions(DICT_TYPE.supervise_task_type),
// }),
// },
// autoSearchTrigger: 'enter',
// show: true,
// },
// department: {
// title: '负责部门',
// key: 'department',