项目初始化

This commit is contained in:
2026-03-19 10:57:24 +08:00
commit ee94d420ad
3822 changed files with 582614 additions and 0 deletions

View File

@@ -0,0 +1,290 @@
<!--
* Copyright (c) 2013-Now https://jeesite.com All rights reserved.
* No deletion without permission, or be held responsible to law.
* @author ThinkGem
-->
<template>
<BasicDrawer
v-bind="$attrs"
:showFooter="true"
:okText="t('发布')"
:okAuth="'msg:msgInner:edit'"
:showOkBtn="!record.status || record.status == '9'"
@register="registerDrawer"
@ok="handleSubmit('0')"
width="70%"
>
<template #title>
<Icon :icon="getTitle.icon" class="m-1 pr-1" />
<span> {{ getTitle.value }} </span>
</template>
<template #centerFooter>
<a-button
color="success"
v-if="!record.status || record.status == '9'"
:loading="confirmLoading"
@click="handleSubmit('9')"
>
<Icon icon="i-ant-design:save-outlined" />
{{ t('草稿') }}
</a-button>
</template>
<BasicForm @register="registerForm">
<template #msgContent="{ model, field }">
<WangEditor v-model:value="model[field]" :bizKey="record.id" :bizType="'msgInner_' + field" :height="300" />
</template>
<template #receiveCodes="{ model }">
<Form.ItemRest>
<TreeSelect
v-show="model.receiveType === '1'"
:value="receivers['c' + model.receiveType]"
:labelValue="receivers['n' + model.receiveType]"
:labelInValue="true"
:api="officeTreeData"
:params="userApiParams"
:canSelectParent="true"
:treeCheckable="true"
@change="(a, b) => onReceiversChange(model.receiveType, a, b)"
/>
<TreeSelect
v-show="model.receiveType === '2'"
:value="receivers['c' + model.receiveType]"
:labelValue="receivers['n' + model.receiveType]"
:labelInValue="true"
:api="officeTreeData"
:params="officeApiParams"
:canSelectParent="true"
:treeCheckable="true"
@change="(a, b) => onReceiversChange(model.receiveType, a, b)"
/>
<TreeSelect
v-show="model.receiveType === '3'"
:value="receivers['c' + model.receiveType]"
:labelValue="receivers['n' + model.receiveType]"
:labelInValue="true"
:api="roleTreeData"
:params="officeApiParams"
:canSelectParent="true"
:treeCheckable="true"
@change="(a, b) => onReceiversChange(model.receiveType, a, b)"
/>
<TreeSelect
v-show="model.receiveType === '4'"
:value="receivers['c' + model.receiveType]"
:labelValue="receivers['n' + model.receiveType]"
:labelInValue="true"
:api="postTreeData"
:params="officeApiParams"
:canSelectParent="true"
:treeCheckable="true"
@change="(a, b) => onReceiversChange(model.receiveType, a, b)"
/>
</Form.ItemRest>
</template>
</BasicForm>
</BasicDrawer>
</template>
<script lang="ts" setup name="ViewsMsgMsgInnerForm">
import { ref, unref, computed } from 'vue';
import { Form } from 'ant-design-vue';
import { useI18n } from '@jeesite/core/hooks/web/useI18n';
import { useMessage } from '@jeesite/core/hooks/web/useMessage';
import { router } from '@jeesite/core/router';
import { Icon } from '@jeesite/core/components/Icon';
import { BasicForm, FormSchema, useForm } from '@jeesite/core/components/Form';
import { BasicDrawer, useDrawerInner } from '@jeesite/core/components/Drawer';
import { MsgInner, msgInnerSave, msgInnerForm } from '@jeesite/core/api/msg/msgInner';
import { WangEditor } from '@jeesite/core/components/WangEditor';
import { TreeSelect } from '@jeesite/core/components/Form';
import { officeTreeData } from '@jeesite/core/api/sys/office';
import { roleTreeData } from '@jeesite/core/api/sys/role';
import { postTreeData } from '@jeesite/core/api/sys/post';
const emit = defineEmits(['success', 'register']);
const { t } = useI18n('msg.msgInner');
const { showMessage } = useMessage();
const { meta } = unref(router.currentRoute);
const record = ref<MsgInner>({} as MsgInner);
const getTitle = computed(() => ({
icon: meta.icon || 'ant-design:book-outlined',
value: record.value.isNewRecord ? t('新增消息') : t('编辑消息'),
}));
const receivers = ref({});
const userApiParams = ref<Recordable>({ isLoadUser: true, userIdPrefix: '', isAll: true });
const officeApiParams = ref<Recordable>({ isAll: true });
const inputFormSchemas: FormSchema[] = [
{
label: t('标题'),
field: 'msgTitle',
component: 'Input',
componentProps: {
maxlength: 200,
},
required: true,
colProps: { md: 24, lg: 24 },
},
{
label: t('等级'),
field: 'contentLevel',
component: 'RadioGroup',
componentProps: {
dictType: 'msg_inner_content_level',
},
required: true,
},
{
label: t('类型'),
field: 'contentType',
component: 'RadioGroup',
componentProps: {
dictType: 'msg_inner_content_type',
},
},
{
label: t('内容'),
field: 'msgContent',
component: 'InputTextArea',
required: true,
colProps: { md: 24, lg: 24 },
slot: 'msgContent',
},
{
label: t('附件'),
field: 'dataMap',
component: 'Upload',
componentProps: {
loadTime: computed(() => record.value.__t),
bizKey: computed(() => record.value.id),
bizType: 'msgInner_file',
uploadType: 'all',
},
// rules: [
// { required: true, message: t('请上传附件') },
// {
// validator(_rule, value) {
// return new Promise<void>((resolve, reject) => {
// const bizType = 'msgInner_file';
// if (!value || (value[bizType + '__len'] as number) > 0) resolve();
// else reject(t('请上传附件'));
// });
// },
// },
// ],
colProps: { md: 24, lg: 24 },
},
{
label: t('接受者信息'),
field: 'receiveInfo',
component: 'FormGroup',
colProps: { md: 24, lg: 24 },
},
{
label: t('接受者'),
field: 'receiveType',
component: 'RadioGroup',
componentProps: {
dictType: 'msg_inner_receiver_type',
},
required: true,
colProps: { md: 24, lg: 24 },
},
{
label: ' ',
field: 'receiveCodes',
fieldLabel: 'receiveNames',
component: 'Input',
colProps: { md: 24, lg: 24 },
slot: 'receiveCodes',
show: ({ values }) => values.receiveType != '0',
},
{
label: t('通知'),
field: 'notifyTypes',
component: 'CheckboxGroup',
componentProps: {
dictType: 'sys_msg_type',
},
colProps: { md: 24, lg: 24 },
},
{
label: t('发送者信息'),
field: 'receiveInfo',
component: 'FormGroup',
colProps: { md: 24, lg: 24 },
},
{
label: t('发送者'),
field: 'sendUserName',
component: 'Input',
componentProps: {
maxlength: 100,
disabled: true,
},
},
{
label: t('发送时间'),
field: 'sendDate',
component: 'DatePicker',
componentProps: {
format: 'YYYY-MM-DD HH:mm',
showTime: { format: 'HH:mm' },
disabled: true,
},
},
];
const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({
labelWidth: 120,
schemas: inputFormSchemas,
baseColProps: { md: 24, lg: 12 },
});
const [registerDrawer, { setDrawerProps, closeDrawer, getDrawerProps }] = useDrawerInner(async (data) => {
setDrawerProps({ loading: true });
await resetFields();
const res = await msgInnerForm(data);
record.value = (res.msgInner || {}) as MsgInner;
record.value.__t = new Date().getTime();
onReceiversChange(record.value.receiveType, record.value.receiveCodes, record.value.receiveNames);
setFieldsValue(record.value);
setDrawerProps({ loading: false });
});
const confirmLoading = computed(() => {
return getDrawerProps().confirmLoading || false;
});
function onReceiversChange(receiveType, value, labelValue) {
receivers.value = {};
receivers.value['c' + receiveType] = value;
receivers.value['n' + receiveType] = labelValue;
}
async function handleSubmit(status: string) {
try {
const data = await validate();
data.status = status;
data.receiveCodes = receivers.value['c' + data.receiveType];
data.receiveNames = receivers.value['n' + data.receiveType];
setDrawerProps({ confirmLoading: true });
const params: any = {
isNewRecord: record.value.isNewRecord,
id: record.value.id,
};
// console.log('submit', params, data, record);
const res = await msgInnerSave(params, data);
showMessage(res.message);
setTimeout(closeDrawer);
emit('success', data);
} catch (error: any) {
if (error && error.errorFields) {
showMessage(error.message || t('common.validateError'));
}
console.log('error', error);
} finally {
setDrawerProps({ confirmLoading: false });
}
}
</script>

View File

@@ -0,0 +1,217 @@
<!--
* Copyright (c) 2013-Now https://jeesite.com All rights reserved.
* No deletion without permission, or be held responsible to law.
* @author ThinkGem
-->
<template>
<div>
<BasicTable @register="registerTable">
<template #tableTitle>
<Icon :icon="getTitle.icon" class="m-1 pr-1" />
<span> {{ getTitle.value }} </span>
</template>
<template #toolbar>
<a-button type="primary" @click="handleForm({ status: '9' })" v-auth="'msg:msgInner:edit'">
<Icon icon="i-fluent:add-12-filled" /> {{ t('新增') }}
</a-button>
</template>
<template #firstColumn="{ record }">
<a @click="handleForm({ id: record.id, status: record.status })">
{{ record.msgTitle }}
<Icon v-if="record.isAttac == '1'" icon="i-fa:paperclip" />
</a>
</template>
</BasicTable>
<InputForm @register="registerDrawer" @success="handleSuccess" />
</div>
</template>
<script lang="ts" setup name="ViewsMsgMsgInnerList">
import { unref } from 'vue';
import { useI18n } from '@jeesite/core/hooks/web/useI18n';
import { useMessage } from '@jeesite/core/hooks/web/useMessage';
import { router } from '@jeesite/core/router';
import { Icon } from '@jeesite/core/components/Icon';
import { BasicTable, BasicColumn, useTable } from '@jeesite/core/components/Table';
import { msgInnerDelete, msgInnerListData } from '@jeesite/core/api/msg/msgInner';
import { useDrawer } from '@jeesite/core/components/Drawer';
import { FormProps } from '@jeesite/core/components/Form';
import InputForm from './form.vue';
import { useGo } from '@jeesite/core/hooks/web/usePage';
const { t } = useI18n('msg.msgInner');
const { showMessage } = useMessage();
const { meta } = unref(router.currentRoute);
const getTitle = {
icon: meta.icon || 'ant-design:book-outlined',
value: meta.title || t('站内消息'),
};
const go = useGo();
const searchForm: FormProps = {
baseColProps: { md: 8, lg: 3 },
labelWidth: 50,
schemas: [
{
label: t('标题'),
field: 'msgTitle',
component: 'Input',
},
{
label: t('等级'),
field: 'contentLevel',
component: 'Select',
componentProps: {
dictType: 'msg_inner_content_level',
allowClear: true,
},
},
{
label: t('类型'),
field: 'contentType',
component: 'Select',
componentProps: {
dictType: 'msg_inner_content_type',
allowClear: true,
},
},
{
label: t('发送时间'),
field: 'dateRange',
component: 'RangePicker',
componentProps: {},
colProps: { lg: 6 },
labelWidth: 80,
},
{
label: t('状态'),
field: 'status',
component: 'Select',
componentProps: {
dictType: 'msg_inner_msg_status',
allowClear: true,
onChange: handleSuccess,
},
},
],
fieldMapToTime: [['dateRange', ['sendDate_gte', 'sendDate_lte']]],
};
const tableColumns: BasicColumn[] = [
{
title: t('标题'),
dataIndex: 'msgTitle',
key: 'a.msg_title',
sorter: true,
width: 280,
align: 'left',
slot: 'firstColumn',
},
{
title: t('等级'),
dataIndex: 'contentLevel',
key: 'a.content_level',
sorter: true,
width: 80,
align: 'center',
dictType: 'msg_inner_content_level',
},
{
title: t('类型'),
dataIndex: 'contentType',
key: 'a.content_type',
sorter: true,
width: 80,
align: 'center',
dictType: 'msg_inner_content_type',
},
{
title: t('发送者'),
dataIndex: 'sendUserName',
key: 'a.send_user_name',
sorter: true,
width: 100,
align: 'center',
},
{
title: t('发送时间'),
dataIndex: 'sendDate',
key: 'a.send_date',
sorter: true,
width: 150,
align: 'center',
},
{
title: t('状态'),
dataIndex: 'status',
key: 'a.status',
sorter: true,
width: 80,
align: 'center',
dictType: 'msg_inner_msg_status',
},
];
const actionColumn: BasicColumn = {
width: 160,
actions: (record: Recordable) => [
{
icon: 'i-ant-design:file-text-outlined',
title: t('查看消息'),
onClick: handleForm.bind(this, { id: record.id, status: record.status }),
auth: 'msg:msgInner:view',
ifShow: () => record.status !== '9',
enable: true,
},
{
icon: 'i-clarity:note-edit-line',
title: t('编辑消息'),
onClick: handleForm.bind(this, { id: record.id, status: record.status }),
auth: 'msg:msgInner:edit',
ifShow: () => record.status === '9',
},
{
icon: 'i-ant-design:delete-outlined',
color: 'error',
title: t('删除消息'),
popConfirm: {
title: t('是否确认删除消息'),
confirm: handleDelete.bind(this, { id: record.id }),
},
auth: 'msg:msgInner:edit',
ifShow: () => record.status === '9',
},
],
};
const [registerDrawer, { openDrawer }] = useDrawer();
const [registerTable, { reload }] = useTable({
api: msgInnerListData,
beforeFetch: (params) => {
return params;
},
columns: tableColumns,
actionColumn: actionColumn,
formConfig: searchForm,
showTableSetting: true,
useSearchForm: true,
canResize: true,
});
function handleForm(record: Recordable) {
if (record.status !== '9') {
go('/msg/msgInner/view?id=' + record.id);
return;
}
openDrawer(true, record);
}
async function handleDelete(record: Recordable) {
const res = await msgInnerDelete(record);
showMessage(res.message);
handleSuccess();
}
function handleSuccess() {
reload();
}
</script>

View File

@@ -0,0 +1,149 @@
<!--
* Copyright (c) 2013-Now https://jeesite.com All rights reserved.
* No deletion without permission, or be held responsible to law.
* @author ThinkGem
-->
<template>
<PageWrapper :sidebarWidth="200" title="false">
<div class="jeesite-msg-title mb-5 ml-2 mr-2 pb-6 pt-4 text-center text-xl">
{{ record.msgTitle }}
</div>
<BasicForm @register="registerForm">
<template #readList="{ model, field }">
<span v-for="(e, i) in model[field]" :key="i">
<Tag color="blue" :title="e.receiveUserCode">{{ e.receiveUserName }}</Tag>
</span>
<span v-if="model[field] == 0">
<Tag>{{ t('还没有人阅读') }}</Tag>
</span>
</template>
<template #unReadList="{ model, field }">
<span v-for="(e, i) in model[field]" :key="i">
<Tag color="purple" :title="e.receiveUserCode">{{ e.receiveUserName }}</Tag>
</span>
<span v-if="model[field] == 0">
<Tag>{{ t('没有了') }}</Tag>
</span>
</template>
</BasicForm>
<div class="flex justify-center">
<a-button type="primary" @click="closeCurrent">
<Icon icon="i-ant-design:close-outlined" /> {{ t('关闭') }}
</a-button>
</div>
</PageWrapper>
</template>
<script lang="ts" setup name="ViewsMsgMsgInnerView">
import { ref, computed, onMounted } from 'vue';
import { Tag } from 'ant-design-vue';
import { useI18n } from '@jeesite/core/hooks/web/useI18n';
import { PageWrapper } from '@jeesite/core/components/Page';
import { Icon } from '@jeesite/core/components/Icon';
import { BasicForm, FormSchema, useForm } from '@jeesite/core/components/Form';
import { MsgInner, msgInnerView } from '@jeesite/core/api/msg/msgInner';
import { useTabs } from '@jeesite/core/hooks/web/useTabs';
import { useQuery } from '@jeesite/core/hooks/web/usePage';
const { t } = useI18n('msg.msgInner');
const { closeCurrent } = useTabs();
const getQuery = useQuery();
const record = ref<MsgInner>({ id: getQuery.value.id } as MsgInner);
const inputFormSchemas: FormSchema[] = [
{
label: t('等级'),
field: 'contentLevel',
component: 'Text',
componentProps: {
dictType: 'msg_inner_content_level',
},
},
{
label: t('类型'),
field: 'contentType',
component: 'Text',
componentProps: {
dictType: 'msg_inner_content_type',
},
},
{
label: t('内容'),
field: 'msgContent',
component: 'Text',
componentProps: {
isHtml: true,
},
colProps: { md: 24, lg: 24 },
},
{
label: t('附件'),
field: 'dataMap',
component: 'Upload',
componentProps: {
loadTime: computed(() => record.value.__t),
bizKey: computed(() => record.value.id),
bizType: 'msgInner_file',
uploadType: 'all',
readonly: true,
},
colProps: { md: 24, lg: 24 },
},
{
label: t('发送者'),
field: 'sendUserName',
component: 'Text',
},
{
label: t('发送时间'),
field: 'sendDate',
component: 'Text',
},
{
label: t('阅读状态'),
field: 'receiveInfo',
component: 'FormGroup',
colProps: { md: 24, lg: 24 },
},
{
label: t('已读用户'),
field: 'readList',
component: 'Text',
colProps: { md: 24, lg: 24 },
slot: 'readList',
},
{
label: t('未读用户'),
field: 'unReadList',
component: 'Text',
colProps: { md: 24, lg: 24 },
slot: 'unReadList',
},
];
const [registerForm, { resetFields, setFieldsValue }] = useForm({
labelWidth: 120,
schemas: inputFormSchemas,
baseColProps: { md: 24, lg: 12 },
});
onMounted(async () => {
await resetFields();
const res = await msgInnerView(record.value);
record.value = (res.msgInner || {}) as MsgInner;
record.value.__t = new Date().getTime();
record.value.readList = res.readList;
record.value.unReadList = res.unReadList;
await setFieldsValue(record.value);
});
</script>
<style lang="less">
.jeesite-msg-title {
border-bottom: 1px solid #ddd;
}
[data-theme='dark'] {
.jeesite-msg-title {
border-bottom: 1px solid #303030;
}
}
</style>