v3.8.2 版本前端代码

This commit is contained in:
JEECG
2025-07-30 18:25:58 +08:00
parent e6edde963a
commit 219869f4c0
84 changed files with 3587 additions and 1964 deletions

View File

@ -65,7 +65,7 @@
<a @click="handleDetail(record)">详情</a>
</a-menu-item>
<a-menu-item>
<Popconfirm title="确定删除吗?" @confirm="() => handleDelete(record.id)">
<Popconfirm title="确定删除吗?" @confirm="() => handleDelete(record.id)" placement="left">
<a>删除</a>
</Popconfirm>
</a-menu-item>

View File

@ -1,230 +1,234 @@
<template>
<a-spin :spinning="confirmLoading">
<a-form class="antd-modal-form" ref="formRef" :model="formState" :rules="validatorRules">
<a-row>
<a-col :span="24">
<a-form-item label="文本" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.name">
<a-input v-model:value="formState.name" placeholder="请输入文本"></a-input>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="密码" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.miMa">
<a-input-password v-model:value="formState.miMa" placeholder="请输入密码" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="字典下拉" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.xiala">
<JDictSelectTag type="select" v-model:value="formState.xiala" dictCode="sex" placeholder="请选择字典下拉" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="字典单选" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.danxuan">
<JDictSelectTag type="radio" v-model:value="formState.danxuan" dictCode="sex" placeholder="请选择字典单选" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="字典多选" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.duoxuan">
<JCheckbox v-model:value="formState.duoxuan" dictCode="urgent_level" placeholder="请选择字典多选" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="开关" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.kaiguan">
<JSwitch v-model:value="formState.kaiguan" :options="['1', '0']"></JSwitch>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="日期" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.riqi">
<a-date-picker placeholder="请选择日期" format="YYYY-MM-DD" valueFormat="YYYY-MM-DD" v-model:value="formState.riqi" style="width: 100%" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="年月日时分秒" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.nyrsfm">
<a-date-picker show-time v-model:value="formState.nyrsfm" style="width: 100%" valueFormat="YYYY-MM-DD HH:mm:ss" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="时间" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.shijian">
<TimePicker placeholder="请选择时间" v-model:value="formState.shijian" style="width: 100%" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="文件" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.wenjian">
<JUpload v-model:value="formState.wenjian"></JUpload>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="图片" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.tupian">
<JImageUpload :fileMax="2" v-model:value="formState.tupian"></JImageUpload>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="多行文本框" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.dhwb">
<a-textarea v-model:value="formState.dhwb" rows="4" placeholder="请输入多行文本框" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="字典表下拉搜索框" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.xlss">
<JSearchSelect v-model:value="formState.xlss" dict="sys_user,realname,username" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="popup弹窗" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.popup">
<JPopup
v-model:value="formState.popup"
:fieldConfig="[
{ source: 'name', target: 'popup' },
{ source: 'id', target: 'popback' },
]"
code="report_user"
:multi="true"
:setFieldsValue="setFieldsValue"
/>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="popback" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.popback">
<a-input v-model:value="formState.popback" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="分类字典树" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.flzds">
<JCategorySelect
@change="(value) => handleFormChange('flzds', value)"
v-model:value="formState.flzds"
pcode="B02"
placeholder="请选择分类字典树"
/>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="部门选择" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.bmxz">
<JSelectDept v-model:value="formState.bmxz" :multi="true" type="array" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="用户选择" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.yhxz">
<JSelectUserByDept v-model:value="formState.yhxz" :multi="true" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="富文本" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.fwb">
<JEditor v-model:value="formState.fwb" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="markdown" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.markdownString">
<JMarkdownEditor v-model:value="formState.markdownString"></JMarkdownEditor>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="省市区JAreaSelect" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.shq">
<JAreaSelect v-model:value="formState.shq" placeholder="请输入省市区" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="省市区JAreaLinkage" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.jssq">
<JAreaLinkage v-model:value="formState.jssq" placeholder="请输入省市区" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="JInputPop" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.ldzje">
<JInputPop
v-model:value="formState.ldzje"
placeholder="请输入JInputPop"
@change="(value) => handleFormChange('ldzje', value)"
></JInputPop>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="JSelectInput" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.ldzjs">
<JSelectInput
v-model:value="formState.ldzjs"
placeholder="请选择JSelectInput"
:options="ldzjsOptions"
@change="(value) => handleFormChange('ldzjs', value)"
></JSelectInput>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="下拉多选" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.zddtjxl">
<JSelectMultiple v-model:value="formState.zddtjxl" placeholder="请选择下拉多选" dictCode="sex"></JSelectMultiple>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="用户" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.yongHu">
<JSelectUser v-model:value="formState.yongHu" placeholder="请选择用户"></JSelectUser>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="职务" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.zhiWu">
<JSelectPosition
v-model:value="formState.zhiWu"
placeholder="请选择职务"
@change="(value) => handleFormChange('zhiWu', value)"
></JSelectPosition>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="角色" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.jueSe">
<JSelectRole v-model:value="formState.jueSe" placeholder="请选择角色" @change="(value) => handleFormChange('jueSe', value)"></JSelectRole>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="自定义树" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.zdys">
<JTreeSelect
ref="treeSelect"
placeholder="请选择自定义树"
v-model:value="formState.zdys"
dict="sys_category,name,id"
pidValue="0"
loadTriggleChange
>
</JTreeSelect>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="数值" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.yuanjia">
<a-input-number v-model:value="formState.yuanjia" placeholder="请输入double类型" style="width: 100%" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="输入2到10位的字母" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.ywzz">
<a-input v-model:value="formState.ywzz" placeholder="请输入2到10位的字母"></a-input>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="JTreeDict" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.zdbxl">
<JTreeDict
v-model:value="formState.zdbxl"
placeholder="请选择JTreeDict"
@change="(value) => handleFormChange('zdbxl', value)"
></JTreeDict>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="JCodeEditor" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.zdmrz">
<JCodeEditor
v-model:value="formState.zdmrz"
placeholder="请输入JCodeEditor"
@change="(value) => handleFormChange('zdmrz', value)"
></JCodeEditor>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="参数" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.jsonParam">
<JAddInput v-model:value="formState.jsonParam" placeholder="参数"></JAddInput>
</a-form-item>
</a-col>
</a-row>
</a-form>
<JFormContainer :disabled="disabled">
<template #detail>
<a-form class="antd-modal-form" ref="formRef" :model="formState" :rules="validatorRules">
<a-row>
<a-col :span="24">
<a-form-item label="文本" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.name">
<a-input v-model:value="formState.name" placeholder="请输入文本"></a-input>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="密码" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.miMa">
<a-input-password v-model:value="formState.miMa" placeholder="请输入密码" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="字典下拉" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.xiala">
<JDictSelectTag type="select" v-model:value="formState.xiala" dictCode="sex" placeholder="请选择字典下拉" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="字典单选" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.danxuan">
<JDictSelectTag type="radio" v-model:value="formState.danxuan" dictCode="sex" placeholder="请选择字典单选" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="字典多选" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.duoxuan">
<JCheckbox v-model:value="formState.duoxuan" dictCode="urgent_level" placeholder="请选择字典多选" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="开关" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.kaiguan">
<JSwitch v-model:value="formState.kaiguan" :options="['1', '0']"></JSwitch>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="日期" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.riqi">
<a-date-picker placeholder="请选择日期" format="YYYY-MM-DD" valueFormat="YYYY-MM-DD" v-model:value="formState.riqi" style="width: 100%" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="年月日时分秒" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.nyrsfm">
<a-date-picker show-time v-model:value="formState.nyrsfm" style="width: 100%" valueFormat="YYYY-MM-DD HH:mm:ss" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="时间" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.shijian">
<TimePicker placeholder="请选择时间" v-model:value="formState.shijian" style="width: 100%" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="文件" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.wenjian">
<JUpload v-model:value="formState.wenjian"></JUpload>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="图片" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.tupian">
<JImageUpload :fileMax="2" v-model:value="formState.tupian"></JImageUpload>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="多行文本框" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.dhwb">
<a-textarea v-model:value="formState.dhwb" rows="4" placeholder="请输入多行文本框" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="字典表下拉搜索框" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.xlss">
<JSearchSelect v-model:value="formState.xlss" dict="sys_user,realname,username" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="popup弹窗" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.popup">
<JPopup
v-model:value="formState.popup"
:fieldConfig="[
{ source: 'name', target: 'popup' },
{ source: 'id', target: 'popback' },
]"
code="report_user"
:multi="true"
:setFieldsValue="setFieldsValue"
/>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="popback" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.popback">
<a-input v-model:value="formState.popback" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="分类字典树" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.flzds">
<JCategorySelect
@change="(value) => handleFormChange('flzds', value)"
v-model:value="formState.flzds"
pcode="B02"
placeholder="请选择分类字典树"
/>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="部门选择" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.bmxz">
<JSelectDept v-model:value="formState.bmxz" :multi="true" type="array" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="用户选择" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.yhxz">
<JSelectUserByDept v-model:value="formState.yhxz" :multi="true" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="富文本" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.fwb">
<JEditor v-model:value="formState.fwb" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="markdown" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.markdownString">
<JMarkdownEditor v-model:value="formState.markdownString"></JMarkdownEditor>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="省市区JAreaSelect" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.shq">
<JAreaSelect v-model:value="formState.shq" placeholder="请输入省市区" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="省市区JAreaLinkage" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.jssq">
<JAreaLinkage v-model:value="formState.jssq" placeholder="请输入省市区" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="JInputPop" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.ldzje">
<JInputPop
v-model:value="formState.ldzje"
placeholder="请输入JInputPop"
@change="(value) => handleFormChange('ldzje', value)"
></JInputPop>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="JSelectInput" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.ldzjs">
<JSelectInput
v-model:value="formState.ldzjs"
placeholder="请选择JSelectInput"
:options="ldzjsOptions"
@change="(value) => handleFormChange('ldzjs', value)"
></JSelectInput>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="下拉多选" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.zddtjxl">
<JSelectMultiple v-model:value="formState.zddtjxl" placeholder="请选择下拉多选" dictCode="sex"></JSelectMultiple>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="用户" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.yongHu">
<JSelectUser v-model:value="formState.yongHu" placeholder="请选择用户"></JSelectUser>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="职务" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.zhiWu">
<JSelectPosition
v-model:value="formState.zhiWu"
placeholder="请选择职务"
@change="(value) => handleFormChange('zhiWu', value)"
></JSelectPosition>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="角色" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.jueSe">
<JSelectRole v-model:value="formState.jueSe" placeholder="请选择角色" @change="(value) => handleFormChange('jueSe', value)"></JSelectRole>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="自定义树" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.zdys">
<JTreeSelect
ref="treeSelect"
placeholder="请选择自定义树"
v-model:value="formState.zdys"
dict="sys_category,name,id"
pidValue="0"
loadTriggleChange
>
</JTreeSelect>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="数值" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.yuanjia">
<a-input-number v-model:value="formState.yuanjia" placeholder="请输入double类型" style="width: 100%" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="输入2到10位的字母" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.ywzz">
<a-input v-model:value="formState.ywzz" placeholder="请输入2到10位的字母"></a-input>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="JTreeDict" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.zdbxl">
<JTreeDict
v-model:value="formState.zdbxl"
placeholder="请选择JTreeDict"
@change="(value) => handleFormChange('zdbxl', value)"
></JTreeDict>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="JCodeEditor" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.zdmrz">
<JCodeEditor
v-model:value="formState.zdmrz"
placeholder="请输入JCodeEditor"
@change="(value) => handleFormChange('zdmrz', value)"
></JCodeEditor>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="参数" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.jsonParam">
<JAddInput v-model:value="formState.jsonParam" placeholder="参数"></JAddInput>
</a-form-item>
</a-col>
</a-row>
</a-form>
</template>
</JFormContainer>
</a-spin>
</template>
<script lang="ts" setup>
import { ref, reactive, nextTick } from 'vue';
import { ref, reactive, nextTick, computed } from 'vue';
import { defHttp } from '/@/utils/http/axios';
import { useMessage } from '/@/hooks/web/useMessage';
import dayjs from 'dayjs';
@ -255,7 +259,15 @@
import JCodeEditor from '/@/components/Form/src/jeecg/components/JCodeEditor.vue';
import JAddInput from '/@/components/Form/src/jeecg/components/JAddInput.vue';
import { getValueType } from '/@/utils';
import JFormContainer from '/@/components/Form/src/container/JFormContainer.vue';
const props = defineProps({
formDisabled: { type: Boolean, default: false },
});
// 表单禁用
const disabled = computed(()=>{
return props.formDisabled;
});
const emit = defineEmits(['register', 'ok']);
//update-begin---author:wangshuai ---date:20220616 for报表示例验证修改--------------
const formState = reactive<Record<string, any>>({

View File

@ -9,7 +9,7 @@
@cancel="handleCancel"
cancelText="关闭"
>
<OneNativeForm ref="realForm" @ok="submitCallback" :disabled="disableSubmit"></OneNativeForm>
<OneNativeForm ref="realForm" @ok="submitCallback" :formDisabled="disableSubmit"></OneNativeForm>
</BasicModal>
</template>
@ -61,5 +61,9 @@
});
</script>
<style lang="less" scoped>
<style lang="less">
/**隐藏样式-modal确定按钮 */
.jee-hidden {
display: none !important;
}
</style>

View File

@ -1,5 +1,5 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" :title="getTitle" @ok="handleSubmit" :width="500" :minHeight="20" :maxHeight="20">
<BasicModal v-bind="$attrs" @register="registerModal" :title="getTitle" @ok="handleSubmit" :width="500" :minHeight="20" :maxHeight="100">
<BasicForm @register="registerForm" />
</BasicModal>
</template>
@ -16,7 +16,7 @@
const isUpdate = ref(true);
//表单配置
const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({
labelWidth: 150,
// labelWidth: 150,
schemas: ticketFormSchema,
showActionButtonGroup: false,
});

View File

@ -227,6 +227,7 @@ export const ticketFormSchema: FormSchema[] = [
component: 'DatePicker',
componentProps: {
valueFormat: 'YYYY-MM-DD',
getPopupContainer:()=>document.body,
},
},
{

View File

@ -1,92 +1,340 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" title="查看详情" :minHeight="600" :showCancelBtn="false" :showOkBtn="false" :height="88" :destroyOnClose="true">
<BasicModal
v-bind="$attrs"
@register="registerModal"
title="查看详情"
:width="800"
:minHeight="600"
:showCancelBtn="false"
:showOkBtn="false"
:height="88"
:destroyOnClose="true"
@visible-change="handleVisibleChange"
>
<div class="print-btn" @click="onPrinter">
<Icon icon="ant-design:printer-filled" />
<span class="print-text">打印</span>
</div>
<a-card class="daily-article">
<a-card-meta :title="content.titile" :description="'发布人:' + content.sender + ' 发布时间: ' + content.sendTime"> </a-card-meta>
<a-card-meta :title="content.titile">
<template #description>
<div class="article-desc">
<span>发布人{{ content.sender }}</span>
<span>发布时间{{ content.sendTime }}</span>
<span v-if="content.visitsNum">
<a-tooltip placement="top" title="访问次数" :autoAdjustOverflow="true">
<eye-outlined class="item-icon" /> {{ content.visitsNum }}
</a-tooltip>
</span>
</div>
</template>
</a-card-meta>
<a-divider />
<div v-html="content.msgContent" class="article-content"></div>
<div>
<a-button v-if="hasHref" @click="jumpToHandlePage">前往办理<ArrowRightOutlined /></a-button>
</div>
</a-card>
<template v-if="noticeFiles && noticeFiles.length > 0">
<div class="files-title">相关附件</div>
<template v-for="(file, index) in noticeFiles" :key="index">
<div class="files-area">
<div class="files-area-text">
<span>
<paper-clip-outlined />
<a
target="_blank"
rel="noopener noreferrer"
:title="file.fileName"
:href="getFileAccessHttpUrl(file.filePath)"
class="ant-upload-list-item-name"
>{{ file.fileName }}</a
>
</span>
</div>
<div class="files-area-operate">
<download-outlined class="item-icon" @click="handleDownloadFile(file.filePath)" />
<eye-outlined class="item-icon" @click="handleViewFile(file.filePath)" />
</div>
</div>
</template>
</template>
</BasicModal>
</template>
<script lang="ts" setup>
import { BasicModal, useModalInner } from '/@/components/Modal';
import { propTypes } from '/@/utils/propTypes';
import { ArrowRightOutlined } from '@ant-design/icons-vue';
import { useRouter } from 'vue-router'
import xss from 'xss'
import { options } from './XssWhiteList'
const router = useRouter()
import { ArrowRightOutlined, PaperClipOutlined, DownloadOutlined, EyeOutlined } from '@ant-design/icons-vue';
import { addVisitsNum } from '@/views/system/notice/notice.api';
import { useRouter } from 'vue-router';
import xss from 'xss';
import { options } from './XssWhiteList';
import { ref, unref } from 'vue';
import { getFileAccessHttpUrl } from '@/utils/common/compUtils';
import { useGlobSetting } from '@/hooks/setting';
import { encryptByBase64 } from '@/utils/cipher';
const router = useRouter();
const glob = useGlobSetting();
const isUpdate = ref(true);
const content = ref({});
const content = ref<any>({});
const noticeFiles = ref([]);
const emit = defineEmits(['close', 'register']);
//表单赋值
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
isUpdate.value = !!data?.isUpdate;
if (unref(isUpdate)) {
//data.record.msgContent = '<p>2323</p><input onmouseover=alert(1)>xss test';
//update-begin-author:taoyan date:2022-7-14 for: VUEN-1702 【禁止问题】sql注入漏洞
if(data.record.msgContent){
if (data.record.msgContent) {
//update-begin---author:wangshuai---date:2023-11-15---for:【QQYUN-7049】3.6.0版本 通知公告中发布的富文本消息,在我的消息中查看没有样式---
data.record.msgContent = xss(data.record.msgContent,options);
data.record.msgContent = xss(data.record.msgContent, options);
//update-end---author:wangshuai---date:2023-11-15---for:【QQYUN-7049】3.6.0版本 通知公告中发布的富文本消息,在我的消息中查看没有样式---
}
//update-end-author:taoyan date:2022-7-14 for: VUEN-1702 【禁止问题】sql注入漏洞
//update-begin-author:liusq---date:2025-06-17--for: [QQYUN-12521]通知公告消息增加访问量
if (!data.record?.busId) {
await addVisitsNum({ id: data.record.id });
}
//update-end-author:liusq---date:2025-06-17--for: [QQYUN-12521]通知公告消息增加访问量
content.value = data.record;
console.log('data---------->>>', data);
if (data.record?.files && data.record?.files.length > 0) {
noticeFiles.value = data.record.files.split(',').map((item) => {
return {
fileName: item.split('/').pop(),
filePath: item,
};
});
}
showHrefButton();
}
});
const hasHref = ref(false)
const hasHref = ref(false);
//查看消息详情可以跳转
function showHrefButton(){
if(content.value.busId){
function showHrefButton() {
if (content.value.busId) {
hasHref.value = true;
}
}
//跳转至办理页面
function jumpToHandlePage(){
let temp:any = content.value
if(temp.busId){
//这个busId是 任务ID
function jumpToHandlePage() {
let temp: any = content.value;
if (temp.busId) {
//这个busId是 任务ID
let jsonStr = temp.msgAbstract;
let query = {};
try {
if(jsonStr){
let temp = JSON.parse(jsonStr)
if(temp){
Object.keys(temp).map(k=>{
query[k] = temp[k]
if (jsonStr) {
let temp = JSON.parse(jsonStr);
if (temp) {
Object.keys(temp).map((k) => {
query[k] = temp[k];
});
}
}
}catch(e){
console.log('参数解析异常', e)
} catch (e) {
console.log('参数解析异常', e);
}
console.log('query', query, jsonStr)
console.log('busId', temp.busId)
if(Object.keys(query).length>0){
console.log('query', query, jsonStr);
console.log('busId', temp.busId);
if (Object.keys(query).length > 0) {
// taskId taskDefKey procInsId
router.push({ path: '/task/handle/' + temp.busId, query: query })
}else{
router.push({ path: '/task/handle/' + temp.busId })
router.push({ path: '/task/handle/' + temp.busId, query: query });
} else {
router.push({ path: '/task/handle/' + temp.busId });
}
}
closeModal();
}
//打印
function onPrinter() {
// 获取要打印的内容
const printContent = document.querySelector('.daily-article');
if (!printContent) return;
// 创建一个iframe来处理打印
const printFrame = document.createElement('iframe');
printFrame.style.position = 'absolute';
printFrame.style.width = '0';
printFrame.style.height = '0';
printFrame.style.border = 'none';
printFrame.style.left = '-9999px';
printFrame.onload = function () {
const frameDoc = printFrame.contentDocument || printFrame.contentWindow?.document;
if (!frameDoc) return;
// 复制内容到iframe
const clone = printContent.cloneNode(true);
frameDoc.body.appendChild(clone);
// 添加打印样式
const style = frameDoc.createElement('style');
style.innerHTML = `
body {
margin: 0;
padding: 15px;
font-family: Arial, sans-serif;
}
img {
max-width: 100%;
height: auto;
}
@page {
size: auto;
margin: 15mm;
}
@media print {
body {
padding: 0;
}
}
`;
frameDoc.head.appendChild(style);
// 确保图片加载完成
const images = frameDoc.getElementsByTagName('img');
let imagesToLoad = images.length;
const printWhenReady = () => {
if (imagesToLoad === 0) {
setTimeout(() => {
printFrame.contentWindow?.focus();
printFrame.contentWindow?.print();
document.body.removeChild(printFrame);
}, 300);
}
};
if (imagesToLoad === 0) {
printWhenReady();
} else {
Array.from(images).forEach((img) => {
img.onload = () => {
imagesToLoad--;
printWhenReady();
};
// 处理可能已经缓存的图片
if (img.complete && img.naturalWidth !== 0) {
imagesToLoad--;
printWhenReady();
}
});
}
};
document.body.appendChild(printFrame);
}
/**
* 下载文件
* @param filePath
*/
function handleDownloadFile(filePath) {
window.open(getFileAccessHttpUrl(filePath), '_blank');
}
/**
* 预览文件
* @param filePath
*/
function handleViewFile(filePath) {
if (filePath) {
console.log('glob.onlineUrl', glob.viewUrl);
let url = encodeURIComponent(encryptByBase64(filePath));
let previewUrl = `${glob.viewUrl}?url=` + url;
window.open(previewUrl, '_blank');
}
}
function handleVisibleChange(visible: boolean) {
if (!visible) {
emit('close');
}
}
</script>
<style scoped lang="less">
.daily-article {
:deep(.ant-card-meta-detail) {
display: flex !important;
justify-content: center !important;
align-items: center !important;
flex-direction: column !important;
}
:deep(.ant-card-meta-detail .ant-card-meta-title) {
font-size: 22px !important;
}
}
.print-btn {
position: absolute;
top: 20px;
right: 20px;
cursor: pointer;
color: #a3a3a5;
z-index: 999;
.print-text {
margin-left: 5px;
}
&:hover {
color: #40a9ff;
}
}
.detail-iframe {
border: 0;
width: 100%;
height: 100%;
min-height: 600px;
}
.files-title {
font-size: 16px;
margin: 10px;
font-weight: 600;
color: #333;
}
.files-area {
display: flex;
align-items: center;
justify-content: flex-start;
margin: 6px;
&:hover {
background-color: #f5f5f5;
}
.files-area-text {
display: flex;
.ant-upload-list-item-name {
margin: 0 6px;
color: #56befa;
}
}
.files-area-operate {
display: flex;
margin-left: 10px;
.item-icon {
cursor: pointer;
margin: 0 6px;
&:hover {
color: #56befa;
}
}
}
}
.article-desc {
display: flex;
align-items: center;
span:not(:first-child) {
margin-left: 5px;
}
}
/* 确保打印内容中的图片有最大宽度限制 */
.article-content img {
max-width: 100%;
height: auto;
}
</style>

View File

@ -33,9 +33,20 @@
import { useAppStore } from '/@/store/modules/app';
import { useMessageHref } from '/@/views/system/message/components/useSysMessage';
const appStore = useAppStore();
const {goPage} = useMessageHref()
const router = useRouter();
const { currentRoute } = useRouter();
const { goPage } = useMessageHref();
// update-begin--author:liaozhiyang---date:20250709---for【QQYUN-13058】我的消息区分类型且支持根据url参数查询类型
const querystring = currentRoute.value.query;
const findItem: any = searchFormSchema.find((item: any) => item.field === 'msgCategory');
if (findItem) {
if (querystring?.msgCategory) {
findItem.componentProps.defaultValue = querystring.msgCategory
} else {
findItem.componentProps.defaultValue = null
}
}
// update-end--author:liaozhiyang---date:20250709---for【QQYUN-13058】我的消息区分类型且支持根据url参数查询类型
const { prefixCls, tableContext } = useListPage({
designScope: 'mynews-list',
tableProps: {
@ -48,6 +59,14 @@
fieldMapToTime: [['sendTime', ['sendTimeBegin', 'sendTimeEnd'], 'YYYY-MM-DD']],
//update-end---author:wangshuai---date:2024-06-11---for:【TV360X-545】我的消息列表不能通过时间范围查询---
},
beforeFetch: (params) => {
// update-begin--author:liaozhiyang---date:20250709---for【QQYUN-13058】我的消息区分类型且支持根据url参数查询类型
if (querystring?.msgCategory) {
params.msgCategory = querystring.msgCategory;
}
return params;
// update-end--author:liaozhiyang---date:20250709---for【QQYUN-13058】我的消息区分类型且支持根据url参数查询类型
},
},
});
const [registerTable, { reload }] = tableContext;

View File

@ -81,4 +81,16 @@ export const searchFormSchema: FormSchema[] = [
},
colProps: { span: 6 },
},
{
field: 'msgCategory',
label: '消息类型',
component: 'Select',
componentProps: {
options: [
{ label: '通知公告', value: '1' },
{ label: '系统消息', value: '2' },
],
},
colProps: { span: 6 },
},
];

View File

@ -34,7 +34,7 @@
></chat>
</div>
</template>
<Spin v-else :spinning="true"></Spin>
<Loading :loading="loading" tip="加载中,请稍后"></Loading>
</div>
</template>
@ -48,6 +48,7 @@
import { defHttp } from '/@/utils/http/axios';
import { useRouter } from 'vue-router';
import { useAppInject } from "@/hooks/web/useAppInject";
import Loading from '@/components/Loading/src/Loading.vue';
const router = useRouter();
const userId = useUserStore().getUserInfo?.id;
@ -67,6 +68,8 @@
const chatActiveKey = ref<number>(0);
//预置开场白
const presetQuestion = ref<string>('');
//加载
const loading = ref<any>(true);
const handleToggle = () => {
expand.value = !expand.value;
@ -179,10 +182,13 @@
})
.catch(() => {
priming();
});
}).finally(()=>{
loading.value = false
});
}
onMounted(() => {
loading.value = true;
let params: any = router.currentRoute.value.params;
if (params.appId) {
appId.value = params.appId;

View File

@ -84,6 +84,8 @@
}
//倒计时执行前的函数
function sendCodeApi() {
return getCaptcha({ mobile: formData.mobile, smsmode: SmsEnum.FORGET_PASSWORD });
//update-begin---author:wangshuai---date:2025-07-15---for:【issues/8567】严重修改密码存在水平越权问题登录应该用登录模板不应该用忘记密码的模板---
return getCaptcha({ mobile: formData.mobile, smsmode: SmsEnum.LOGIN });
//update-end---author:wangshuai---date:2025-07-15---for:【issues/8567】严重修改密码存在水平越权问题登录应该用登录模板不应该用忘记密码的模板---
}
</script>

View File

@ -0,0 +1,197 @@
<template>
<PageWrapper contentFullHeight>
<a-card :bordered="false" title="版本管理">
<!--编辑模式-->
<a-spin v-if="active" :spinning="confirmLoading">
<a-form ref="formRef" :model="model" :labelCol="labelCol" :wrapperCol="wrapperCol" :rules="validatorRules">
<a-row>
<a-col :span="24">
<a-form-item label="版本" name="appVersion">
<a-input v-model:value="model.appVersion" placeholder="请输入版本" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="APP安装apk" name="downloadUrl">
<a-input placeholder="设置APP安装apk" v-model:value="model.downloadUrl">
<template #addonAfter>
<Icon icon="ant-design:upload-outlined" style="cursor: pointer" @click="showUploadModal('apk')" />
</template>
</a-input>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="APP热更新文件" name="wgtUrl">
<a-input placeholder="设置APP热更新文件" v-model:value="model.wgtUrl">
<template #addonAfter>
<Icon icon="ant-design:upload-outlined" style="cursor: pointer" @click="showUploadModal('wgt')" />
</template>
</a-input>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="更新内容">
<a-textarea :rows="4" v-model:value="model.updateNote" placeholder="请输入更新内容" />
</a-form-item>
</a-col>
</a-row>
</a-form>
<JUploadModal :value="modalValue" :bizPath="filePath" :maxCount="1" @register="registerModel" @change="uploadBack" />
</a-spin>
<!--详情模式-->
<Description v-else class="desc" :column="1" :data="model" :schema="schema" />
<!--底部按钮-->
<div class="anty-form-btn" v-if="hasPermission('app:edit:version')">
<a-button v-if="active" @click="handleSubmit" type="primary" preIcon="ant-design:save-outlined">保存</a-button>
<a-button v-else @click="active = true" type="primary" preIcon="ant-design:edit-outlined">开启编辑模式</a-button>
</div>
</a-card>
</PageWrapper>
</template>
<script lang="ts" setup name="portalapp-sysAppVersion">
import { useMessage } from '/@/hooks/web/useMessage';
import { usePermission } from '@/hooks/web/usePermission';
import { JUploadModal } from '@/components/Form/src/jeecg/components/JUpload';
import { useModal } from '@/components/Modal';
import { reactive, ref, toRaw, unref, onMounted } from 'vue';
import { PageWrapper } from '@/components/Page';
import { queryAppVersion, saveAppVersion } from './appVersion.api';
import { Description, DescItem } from '/@/components/Description/index';
const { hasPermission } = usePermission();
const { createMessage } = useMessage();
const [registerModel, { openModal }] = useModal();
const confirmLoading = ref(false);
const active = ref(false);
const formRef = ref<any>(null);
const appKey = 'E0CC280';
const filePath = 'appVersion';
const uploadType = ref('');
const modalValue = ref('');
const labelCol = {
xs: { span: 24 },
sm: { span: 5 },
};
const wrapperCol = {
xs: { span: 24 },
sm: { span: 16 },
};
const model = reactive({
id: 'E0CC280',
appVersion: '',
versionNum: 0,
updateNote: '',
downloadUrl: '',
wgtUrl: '',
});
/**
* 初始化表单数据
* @param record
*/
async function initFormData() {
const appVersion = await queryAppVersion({ key: appKey });
if (appVersion) {
Object.assign(model, appVersion);
}
}
/**
* 提交保存版本信息
*/
function handleSubmit() {
const form = unref(formRef);
form.validate().then(async () => {
let obj = toRaw(model);
if (obj.appVersion.indexOf('.') != -1) {
obj.versionNum = Number(obj.appVersion.replace(/\./g, ''));
}
obj.id = appKey;
confirmLoading.value = true;
await saveAppVersion(obj);
createMessage.success('保存成功');
confirmLoading.value = false;
active.value = false;
});
}
/**
* 显示设置弹窗
* @param type
*/
function showUploadModal(type) {
uploadType.value = type;
modalValue.value = type == 'apk' ? model.downloadUrl : model.wgtUrl;
openModal(true, {
maxCount: 1,
bizPath: filePath,
});
}
/**
*上传返回
*/
function uploadBack(value) {
if (unref(uploadType) == 'apk') {
model.downloadUrl = value;
} else {
model.wgtUrl = value;
}
}
//表单校验规则
const validatorRules = {
appVersion: [{ required: true, message: '版本不能为空', trigger: 'blur' }],
downloadUrl: [{ required: true, message: 'APP安装apk不能为空', trigger: 'change' }],
wgtUrl: [{ required: true, message: 'APP热更新文件不能为空', trigger: 'change' }],
};
// 显示字段
const schema: DescItem[] = [
{
field: 'appVersion',
label: '版本',
},
{
field: 'downloadUrl',
label: 'APP安装apk',
},
{
field: 'wgtUrl',
label: 'APP热更新文件',
},
{
field: 'updateNote',
label: '更新内容',
},
];
onMounted(() => {
initFormData();
});
</script>
<style scoped>
.anty-form-btn {
width: 100%;
text-align: center;
}
.anty-form-btn button {
margin: 20px;
}
.approveDiv span {
margin: 0 20px;
}
.desc {
width: 80%;
margin: 0 auto;
}
:deep(.ant-descriptions-item-label) {
width: 30% !important;
min-width: 150px !important;
}
:deep(.ant-descriptions-item-content) {
padding: 16px !important;
width: 60% !important;
}
</style>

View File

@ -0,0 +1,20 @@
import { defHttp } from '/@/utils/http/axios';
enum Api {
//查询app版本
queryAppVersion = '/sys/version/app3version',
//保存app版本
saveAppVersion = '/sys/version/saveVersion',
}
/**
* 查询APP版本
* @param params
*/
export const queryAppVersion = (params) => defHttp.get({ url: Api.queryAppVersion, params });
/**
* 保存APP版本
* @param params
*/
export const saveAppVersion = (params) => {
return defHttp.post({ url: Api.saveAppVersion, params });
};

View File

@ -10,6 +10,7 @@ enum Api {
wechatEnterpriseToLocal = '/sys/thirdApp/sync/wechatEnterprise/departAndUser/toLocal',
getThirdUserBindByWechat = '/sys/thirdApp/getThirdUserBindByWechat',
deleteThirdAccount = '/sys/thirdApp/deleteThirdAccount',
deleteThirdAppConfig = '/sys/thirdApp/deleteThirdAppConfig',
}
/**
@ -66,4 +67,15 @@ export const getThirdUserBindByWechat = () => {
*/
export const deleteThirdAccount = (params) => {
return defHttp.delete({ url: Api.deleteThirdAccount, params }, { isTransformResponse:false, joinParamsToUrl: true });
};
};
/**
* 根据配置表的id删除第三方配置
* @param params
* @param handleSuccess
*/
export const deleteThirdAppConfig = (params, handleSuccess) => {
return defHttp.delete({ url: Api.deleteThirdAppConfig, params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
};

View File

@ -17,7 +17,7 @@
<a-collapse-panel key="2">
<template #header>
<div style="width: 100%; justify-content: space-between; display: flex">
<div style="font-size: 16px"> 2.对接信息录入</div>
<div style="font-size: 16px"> 2.对接信息录入及解绑</div>
</div>
</template>
<div class="base-desc">完成步骤1后填入Agentld AppKeyAppSecret后 可对接应用与同步通讯录</div>
@ -47,6 +47,7 @@
</div>
<div style="margin-top: 20px; width: 100%; text-align: right">
<a-button @click="dingEditClick">编辑</a-button>
<a-button v-if="appConfigData.id" @click="cancelBindClick" danger style="margin-left: 10px">取消绑定</a-button>
</div>
</a-collapse-panel>
</a-collapse>
@ -76,7 +77,7 @@
<script lang="ts">
import { defineComponent, h, inject, onMounted, reactive, ref, watch } from 'vue';
import { getThirdConfigByTenantId, syncDingTalkDepartUserToLocal } from './ThirdApp.api';
import { getThirdConfigByTenantId, syncDingTalkDepartUserToLocal, deleteThirdAppConfig } from './ThirdApp.api';
import { useModal } from '/@/components/Modal';
import ThirdAppConfigModal from './ThirdAppConfigModal.vue';
import { Modal } from 'ant-design-vue';
@ -122,6 +123,8 @@
let values = await getThirdConfigByTenantId(params);
if (values) {
appConfigData.value = values;
} else {
appConfigData.value = "";
}
}
@ -214,6 +217,25 @@
function handleIconClick(){
window.open("https://help.qiaoqiaoyun.com/expand/dingdingsyn.html","_target")
}
/**
* 取消绑定
*/
function cancelBindClick() {
if(!appConfigData.value.id){
createMessage.warning("请先绑定钉钉应用!");
return;
}
Modal.confirm({
title: '取消绑定',
content: '是否要解除当前组织的钉钉应用配置绑定?',
okText: '确认',
cancelText: '取消',
onOk: () => {
deleteThirdAppConfig({ id: appConfigData.value.id }, handleSuccess);
},
});
}
onMounted(() => {
let tenantId = getTenantId();
@ -229,6 +251,7 @@
syncDingTalk,
btnLoading,
handleIconClick,
cancelBindClick,
};
},
});

View File

@ -17,7 +17,7 @@
<a-collapse-panel key="2">
<template #header>
<div style="width: 100%; justify-content: space-between; display: flex">
<div style="font-size: 16px"> 2.对接信息录入</div>
<div style="font-size: 16px"> 2.对接信息录入及解绑</div>
</div>
</template>
<div class="flex-flow">
@ -40,6 +40,7 @@
</div>
<div style="margin-top: 20px; width: 100%; text-align: right">
<a-button @click="weEnterpriseEditClick">编辑</a-button>
<a-button v-if="appConfigData.id" @click="cancelBindClick" danger style="margin-left: 10px">取消绑定</a-button>
</div>
</a-collapse-panel>
</a-collapse>
@ -61,7 +62,7 @@
<script lang="ts">
import { defineComponent, onMounted, ref } from 'vue';
import { getThirdConfigByTenantId } from './ThirdApp.api';
import { getThirdConfigByTenantId, deleteThirdAppConfig } from './ThirdApp.api';
import ThirdAppConfigModal from './ThirdAppConfigModal.vue';
import { useModal } from '/@/components/Modal';
import { useMessage } from '/@/hooks/web/useMessage';
@ -97,6 +98,8 @@
let values = await getThirdConfigByTenantId(params);
if (values) {
appConfigData.value = values;
} else {
appConfigData.value = "";
}
}
@ -159,6 +162,25 @@
function seeBindWeChat() {
openBindModal(true,{ izBind: true })
}
/**
* 取消绑定
*/
function cancelBindClick() {
if(!appConfigData.value.id){
createMessage.warning("请先绑定企业微信应用!");
return;
}
Modal.confirm({
title: '取消绑定',
content: '是否要解除当前组织的企业微信应用配置绑定?',
okText: '确认',
cancelText: '取消',
onOk: () => {
deleteThirdAppConfig({ id: appConfigData.value.id }, handleSuccess);
},
});
}
onMounted(() => {
let tenantId = getTenantId();
@ -175,6 +197,7 @@
thirdUserByWechat,
handleBindSuccess,
seeBindWeChat,
cancelBindClick,
};
},
});

View File

@ -0,0 +1,60 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" title="首页配置" @ok="handleSubmit" :width="600">
<BasicForm @register="registerForm" />
</BasicModal>
</template>
<script lang="ts" setup>
import { ref, unref } from 'vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { BasicForm, useForm } from '/@/components/Form/index';
import { formSchema } from '../home.data';
import { saveOrUpdate } from '../home.api';
// Emits声明
const emit = defineEmits(['register', 'success']);
const isUpdate = ref(false);
//表单配置
const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({
labelWidth: 100,
baseRowStyle: { marginTop: '10px' },
schemas: formSchema,
showActionButtonGroup: false,
});
//表单赋值
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
//重置表单
await resetFields();
setModalProps({ confirmLoading: false });
isUpdate.value = !!data?.isUpdate;
if (unref(isUpdate)) {
//表单赋值
if (data.values.relationType == 'USER') {
data.values.userCode = data.values.roleCode;
}
await setFieldsValue({
...data.values,
});
}
});
//表单提交事件
async function handleSubmit() {
try {
let values = await validate();
setModalProps({ confirmLoading: true });
//提交表单
if(values.relationType == 'USER'){
values.roleCode = values.userCode;
}
await saveOrUpdate(values, isUpdate.value);
//关闭弹窗
closeModal();
//刷新列表
emit('success');
} finally {
setModalProps({ confirmLoading: false });
}
}
</script>
<style lang="less" scoped></style>

View File

@ -0,0 +1,55 @@
import { defHttp } from '/@/utils/http/axios';
import { Modal } from 'ant-design-vue';
enum Api {
list = '/sys/sysRoleIndex/list',
save = '/sys/sysRoleIndex/add',
edit = '/sys/sysRoleIndex/edit',
deleteIndex = '/sys/sysRoleIndex/delete',
deleteBatch = '/sys/sysRoleIndex/deleteBatch',
queryIndexByCode = '/sys/sysRoleIndex/queryByCode',
}
/**
* 系统角色列表
* @param params
*/
export const list = (params) => defHttp.get({ url: Api.list, params });
/**
* 删除角色
*/
export const deleteIndex = (params, handleSuccess) => {
return defHttp.delete({ url: Api.deleteIndex, params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
};
/**
* 批量删除角色
* @param params
*/
export const batchDelete = (params, handleSuccess) => {
Modal.confirm({
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.delete({ url: Api.deleteBatch, data: params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
},
});
};
/**
* 保存或者更新首页配置
* @param params
*/
export const saveOrUpdate = (params, isUpdate) => {
const url = isUpdate ? Api.edit : Api.save;
return defHttp.post({ url: url, params });
};
/**
* 查询首页配置
* @param params
*/
export const queryIndexByCode = (params) => defHttp.get({ url: Api.queryIndexByCode, params }, { isTransformResponse: false });

View File

@ -0,0 +1,129 @@
import { FormSchema } from '/@/components/Table';
//列配置
export const columns = [
{
title: '关联类型(用户/角色)',
dataIndex: 'relationType_dictText',
width: 80,
slots: { customRender: 'relationType' },
},
{
title: '用户/角色编码',
dataIndex: 'roleCode',
width: 80,
slots: { customRender: 'roleCode' },
},
{
title: '首页路由',
dataIndex: 'url',
width: 100,
},
{
title: '组件地址',
dataIndex: 'component',
width: 100,
},
{
title: '是否开启',
dataIndex: 'status',
slots: { customRender: 'status' },
width: 60,
},
];
//查询配置
export const searchFormSchema: FormSchema[] = [
{
field: 'relationType',
label: '关联类型',
component: 'JDictSelectTag',
componentProps: {
dictCode: 'relation_type',
},
},
{
field: 'route',
label: '是否路由菜单',
helpMessage: '非路由菜单设置成首页,需开启',
component: 'Switch',
show: false,
},
];
export const formSchema: FormSchema[] = [
{
field: 'id',
label: '',
component: 'Input',
show: false,
},
{
field: 'relationType',
label: '关联类型',
component: 'JDictSelectTag',
required: true,
defaultValue: 'ROLE',
componentProps: {
dictCode: 'relation_type',
type: 'radioButton',
},
},
{
label: '角色编码',
field: 'roleCode',
component: 'JSelectRole',
required: true,
componentProps: {
rowKey: 'roleCode',
isRadioSelection: true,
},
ifShow: ({ values }) => values.relationType == 'ROLE',
},
{
label: '用户编码',
field: 'userCode',
component: 'JSelectUser',
required: true,
componentProps: {
isRadioSelection: true,
},
ifShow: ({ values }) => values.relationType == 'USER',
},
{
label: '首页路由',
field: 'url',
component: 'Input',
required: true,
},
{
label: '组件地址',
field: 'component',
component: 'Input',
componentProps: {
placeholder: '请输入前端组件',
},
required: true,
},
{
label: '优先级',
field: 'priority',
component: 'InputNumber',
},
{
field: 'route',
label: '是否路由菜单',
helpMessage: '非路由菜单设置成首页,需开启',
component: 'Switch',
defaultValue: true,
show: false,
},
{
label: '是否开启',
field: 'status',
component: 'JSwitch',
defaultValue: '1',
componentProps: {
options: ['1', '0'],
},
},
];

View File

@ -0,0 +1,126 @@
<template>
<div>
<BasicTable @register="registerTable" :rowSelection="rowSelection">
<template #tableTitle>
<a-button type="primary" preIcon="ant-design:plus-outlined" @click="handleCreate">新增</a-button>
<a-dropdown v-if="selectedRowKeys.length > 0">
<template #overlay>
<a-menu>
<a-menu-item key="1" @click="batchHandleDelete">
<Icon icon="ant-design:delete-outlined" /> 删除
</a-menu-item>
</a-menu>
</template>
<a-button>批量操作<Icon icon="mdi:chevron-down" /></a-button>
</a-dropdown>
</template>
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" />
</template>
<template #status="{ text }">
<a-tag color="pink" v-if="text == 0">禁用</a-tag>
<a-tag color="#87d068" v-if="text == 1">启用</a-tag>
</template>
<template #relationType="{ text, record }">
<span>{{ record.roleCode == 'DEF_INDEX_ALL' ? '--' : text }}</span>
</template>
<template #roleCode="{ text, record }">
<span>{{ record.roleCode == 'DEF_INDEX_ALL' ? '菜单默认首页' : text }}</span>
</template>
</BasicTable>
<!--角色首页配置-->
<HomeConfigModal @register="register" @success="reload" />
</div>
</template>
<script lang="ts" name="home-config" setup>
import { BasicTable, TableAction } from '/@/components/Table';
import { useModal } from '/@/components/Modal';
import HomeConfigModal from './components/HomeConfigModal.vue';
import { columns, searchFormSchema } from './home.data';
import { useListPage } from '/@/hooks/system/useListPage';
import { list, deleteIndex, batchDelete } from './home.api';
//弹窗配置
const [register, { openModal }] = useModal();
// 列表页面公共参数、方法
const { tableContext } = useListPage({
designScope: 'home-config',
tableProps: {
title: '首页配置',
api: list,
columns: columns,
formConfig: {
labelAlign: 'left',
labelWidth: 80,
schemas: searchFormSchema,
baseRowStyle: {
marginLeft: '2px',
},
},
actionColumn: {
width: 80,
},
//自定义默认排序
defSort: {
column: 'id',
order: 'desc',
},
},
});
const [registerTable, { reload, clearSelectedRowKeys }, { rowSelection, selectedRowKeys }] = tableContext;
/**
* 新增事件
*/
async function handleCreate() {
openModal(true, {
isUpdate: false,
});
}
/**
* 编辑事件
*/
async function handleEdit(record) {
openModal(true, {
isUpdate: true,
values: record,
});
}
/**
* 删除事件
*/
async function handleDelete(record) {
await deleteIndex({ id: record.id }, () => {
reload();
});
}
/**
* 批量删除事件
*/
async function batchHandleDelete() {
await batchDelete({ ids: selectedRowKeys.value }, () => {
clearSelectedRowKeys();
reload();
});
}
/**
* 操作栏
*/
function getTableAction(record) {
return [
{
label: '编辑',
onClick: handleEdit.bind(null, record),
},
{
label: '删除',
popConfirm: {
title: '是否确认删除',
confirm: handleDelete.bind(null, record),
},
},
];
}
</script>

View File

@ -344,7 +344,9 @@
return;
}
//update-begin---author:wangshuai---date:2024-04-18---for:【QQYUN-9005】同一个IP1分钟超过5次短信则提示需要验证码---
const result = await getCaptcha({ mobile: phoneFormData.mobile, smsmode: SmsEnum.FORGET_PASSWORD }).catch((res) =>{
//update-begin---author:wangshuai---date:2025-07-15---for:【issues/8567】严重修改密码存在水平越权问题登录应该用登录模板不应该用忘记密码的模板---
const result = await getCaptcha({ mobile: phoneFormData.mobile, smsmode: SmsEnum.LOGIN }).catch((res) =>{
//update-end---author:wangshuai---date:2025-07-15---for:【issues/8567】严重修改密码存在水平越权问题登录应该用登录模板不应该用忘记密码的模板---
if(res.code === ExceptionEnum.PHONE_SMS_FAIL_CODE){
openCaptchaModal(true, {});
}

View File

@ -1,5 +1,5 @@
<template>
<BasicModal @register="registerModal" :title="title" :width="800" v-bind="$attrs" @ok="onSubmit">
<BasicModal @register="registerModal" :title="title" :width="600" v-bind="$attrs" @ok="onSubmit">
<BasicForm @register="registerForm" />
</BasicModal>
</template>
@ -21,6 +21,15 @@
//update-end---author:wangshuai ---date:20221123 for[VUEN-2807]消息模板加一个查看功能--------------z
schemas: formSchemas,
showActionButtonGroup: false,
baseRowStyle: {
marginTop: '10px',
},
labelCol: {
span: 5,
},
wrapperCol: {
span: 17,
},
});
// 注册 modal
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {

View File

@ -86,12 +86,23 @@ export const formSchemas: FormSchema[] = [
label: '模板类型',
field: 'templateType',
component: 'JDictSelectTag',
defaultValue: '1',
componentProps: {
dictCode: 'msgType',
type: 'radio',
placeholder: '请选择模板类型',
},
required: true,
},
{
label: '模板分类',
field: 'templateCategory',
component: 'JDictSelectTag',
componentProps: {
dictCode: 'msgCategory',
placeholder: '请选择模板分类',
}
},
{
label: '是否应用',
field: 'useStatus',

View File

@ -1,20 +1,113 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" title="查看详情" :showCancelBtn="false" :showOkBtn="false" :maxHeight="500">
<iframe :src="frameSrc" class="detail-iframe" />
<BasicModal v-bind="$attrs" @register="registerModal" :width="800" title="查看详情" :showCancelBtn="false" :showOkBtn="false" :maxHeight="500">
<div class="print-btn" @click="onPrinter">
<Icon icon="ant-design:printer-filled" />
<span class="print-text">打印</span>
</div>
<iframe ref="iframeRef" :src="frameSrc" class="detail-iframe" @load="onIframeLoad"></iframe>
<template v-if="noticeFiles && noticeFiles.length > 0">
<div class="files-title">相关附件</div>
<template v-for="(file, index) in noticeFiles" :key="index">
<div class="files-area">
<div class="files-area-text">
<span>
<paper-clip-outlined />
<a
target="_blank"
rel="noopener noreferrer"
:title="file.fileName"
:href="getFileAccessHttpUrl(file.filePath)"
class="ant-upload-list-item-name"
>{{ file.fileName }}</a
>
</span>
</div>
<div class="files-area-operate">
<download-outlined class="item-icon" @click="handleDownloadFile(file.filePath)" />
<eye-outlined class="item-icon" @click="handleViewFile(file.filePath)" />
</div>
</div>
</template>
</template>
</BasicModal>
</template>
<script lang="ts" setup>
import { BasicModal, useModalInner } from '/@/components/Modal';
import { propTypes } from '/@/utils/propTypes';
import { ref } from 'vue';
import { buildUUID } from '@/utils/uuid';
import { getFileAccessHttpUrl } from '@/utils/common/compUtils';
import { DownloadOutlined, EyeOutlined, PaperClipOutlined } from '@ant-design/icons-vue';
import { encryptByBase64 } from '@/utils/cipher';
import { useGlobSetting } from '@/hooks/setting';
const glob = useGlobSetting();
// 获取props
defineProps({
frameSrc: propTypes.string.def(''),
});
//附件内容
const noticeFiles = ref([]);
//表单赋值
const [registerModal] = useModalInner();
const [registerModal] = useModalInner((data) => {
noticeFiles.value = [];
if (data.record?.files && data.record?.files.length > 0) {
noticeFiles.value = data.record.files.split(',').map((item) => {
return {
fileName: item.split('/').pop(),
filePath: item,
};
});
}
});
// iframe引用
const iframeRef = ref<HTMLIFrameElement>();
// 存储当前打印会话ID
const printSessionId = ref<string>('');
// iframe加载完成后初始化通信
const onIframeLoad = () => {
printSessionId.value = buildUUID(); // 每次加载生成新的会话ID
};
//打印
function onPrinter() {
if (!iframeRef.value) return;
console.log('onPrinter', iframeRef.value);
iframeRef.value?.contentWindow?.postMessage({ printSessionId: printSessionId.value, type: 'action:print' }, '*');
}
/**
* 下载文件
* @param filePath
*/
function handleDownloadFile(filePath) {
window.open(getFileAccessHttpUrl(filePath), '_blank');
}
/**
* 预览文件
* @param filePath
*/
function handleViewFile(filePath) {
if (filePath) {
let url = encodeURIComponent(encryptByBase64(filePath));
let previewUrl = `${glob.viewUrl}?url=` + url;
window.open(previewUrl, '_blank');
}
}
</script>
<style scoped lang="less">
.print-btn {
position: absolute;
top: 20px;
right: 20px;
cursor: pointer;
color: #a3a3a5;
z-index: 999;
.print-text {
margin-left: 5px;
}
&:hover {
color: #40a9ff;
}
}
.detail-iframe {
border: 0;
width: 100%;
@ -24,4 +117,37 @@
display: block;
// -update-end--author:liaozhiyang---date:20240702---for【TV360X-1685】通知公告查看出现两个滚动条
}
.files-title {
font-size: 16px;
margin: 10px;
font-weight: 600;
color: #333;
}
.files-area {
display: flex;
align-items: center;
justify-content: flex-start;
margin: 6px;
&:hover {
background-color: #f5f5f5;
}
.files-area-text {
display: flex;
.ant-upload-list-item-name {
margin: 0 6px;
color: #56befa;
}
}
.files-area-operate {
display: flex;
margin-left: 10px;
.item-icon {
cursor: pointer;
margin: 0 6px;
&:hover {
color: #56befa;
}
}
}
}
</style>

View File

@ -0,0 +1,116 @@
<template>
<div style="min-height: 400px">
<BasicForm @register="registerForm">
<template #msgTemplate="{ model, field }">
<a-select v-model:value="model[field]" placeholder="请选择消息模版" :options="templateOption" @change="handleChange" />
</template>
<template #msgContent="{ model, field }">
<div v-html="model[field]" class="article-content"></div>
</template>
</BasicForm>
<div class="footer-btn" v-if="!formDisabled">
<a-button @click="submitForm" pre-icon="ant-design:check" type="primary"> </a-button>
</div>
</div>
</template>
<script lang="ts" setup>
import { BasicForm, useForm } from '/@/components/Form/index';
import { getBpmFormSchema } from './notice.data';
import { getTempList, queryById, saveOrUpdate } from './notice.api';
import { computed, ref } from 'vue';
// 定义属性
const props = defineProps({
formData: {
type: Object,
default: () => ({}),
},
});
//表单禁用
const formDisabled = computed(() => {
if (props.formData.disabled === false) {
return false;
}
return true;
});
const templateOption = ref([]);
//表单配置
const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({
schemas: getBpmFormSchema(props.formData),
showActionButtonGroup: false,
disabled: formDisabled.value,
labelWidth: 100,
baseRowStyle: { marginTop: '10px' },
baseColProps: { xs: 24, sm: 12, md: 12, lg: 12, xl: 12, xxl: 12 },
});
//表单提交
async function submitForm() {
let values = await validate();
if (values.msgType === 'ALL') {
values.userIds = '';
} else {
values.userIds += ',';
}
console.log('表单数据', values);
await saveOrUpdate(values, true);
}
//初始化模板
async function initTemplate() {
const res = await getTempList({ templateCategory: 'notice', pageSize: 100 });
console.log('res', res);
if (res.records && res.records.length > 0) {
templateOption.value = res.records.map((item) => {
return {
label: item.templateName,
value: item.templateCode,
content: item.templateContent,
};
});
}
}
/**
* 模版修改
* @param val
*/
function handleChange(val) {
const content = templateOption.value.find((item: any) => item.value === val)?.content;
if (content) {
setFieldsValue({
msgContent: content,
});
}
}
/**
* 加载数据
*/
async function initFormData() {
let res = await queryById({ id: props.formData.dataId });
if (res.success) {
//重置表单
await resetFields();
const record = res.result;
if (record.userIds) {
record.userIds = record.userIds.substring(0, record.userIds.length - 1);
}
//表单赋值
await setFieldsValue({
...record,
});
}
}
//加载模版
initTemplate();
//加载数据
initFormData();
</script>
<style lang="less" scoped>
.footer-btn {
width: 100%;
text-align: center;
}
.article-content {
max-width: 100%;
max-height: 500px;
overflow-y: auto;
}
</style>

View File

@ -1,6 +1,19 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" :title="title" @ok="handleSubmit" width="900px" destroyOnClose>
<BasicForm @register="registerForm" />
<BasicModal
v-bind="$attrs"
@register="registerModal"
@ok="handleSubmit"
:title="title"
width="900px"
wrapClassName="notice-cls-modal"
:maxHeight="800"
destroyOnClose
>
<BasicForm @register="registerForm">
<template #msgTemplate="{ model, field }">
<a-select v-model:value="model[field]" placeholder="请选择消息模版" :options="templateOption" @change="handleChange" />
</template>
</BasicForm>
</BasicModal>
</template>
<script lang="ts" setup>
@ -8,17 +21,24 @@
import { BasicModal, useModalInner } from '/@/components/Modal';
import { BasicForm, useForm } from '/@/components/Form/index';
import { formSchema } from './notice.data';
import { saveOrUpdate } from './notice.api';
import { getTempList, saveOrUpdate } from './notice.api';
// 声明Emits
const emit = defineEmits(['register', 'success']);
const isUpdate = ref(true);
const record = ref<any>({});
const templateOption = ref([]);
//表单配置
const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({
schemas: formSchema,
showActionButtonGroup: false,
labelWidth: 100,
baseRowStyle: { marginTop: '10px' },
baseColProps: { xs: 24, sm: 12, md: 12, lg: 12, xl: 12, xxl: 12 },
});
//表单赋值
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
//加载模版
await initTemplate();
//重置表单
await resetFields();
setModalProps({ confirmLoading: false });
@ -31,12 +51,13 @@
await setFieldsValue({
...data.record,
});
record.value = data.record;
}
});
//设置标题
const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
//表单提交事件
async function handleSubmit(v) {
async function handleSubmit() {
try {
let values = await validate();
setModalProps({ confirmLoading: true });
@ -48,7 +69,7 @@
values.userIds += ',';
}
//update-end-author:liusq---date:20230404--for: [issue#429]新增通知公告提交指定用户参数有undefined ---
if (isUpdate.value) {
if (isUpdate.value && record.value.sendStatus != '2') {
values.sendStatus = '0';
}
await saveOrUpdate(values, isUpdate.value);
@ -60,4 +81,36 @@
setModalProps({ confirmLoading: false });
}
}
//初始化模板
async function initTemplate() {
const res = await getTempList({ templateCategory: 'notice', pageSize: 100 });
console.log('res', res);
if (res.records && res.records.length > 0) {
templateOption.value = res.records.map((item) => {
return {
label: item.templateName,
value: item.templateCode,
content: item.templateContent,
};
});
}
}
/**
* 模版修改
* @param val
*/
function handleChange(val) {
const content = templateOption.value.find((item: any) => item.value === val)?.content;
if (content) {
setFieldsValue({
msgContent: content,
});
}
}
</script>
<style scoped>
.notice-cls-modal {
top: 20px !important;
}
</style>

View File

@ -34,17 +34,17 @@
import { useModal } from '/@/components/Modal';
import NoticeModal from './NoticeModal.vue';
import DetailModal from './DetailModal.vue';
import { useMethods } from '/@/hooks/system/useMethods';
import { useMessage } from '/@/hooks/web/useMessage';
import { useGlobSetting } from '/@/hooks/setting';
import { getToken } from '/@/utils/auth';
import { columns, searchFormSchema } from './notice.data';
import { getList, deleteNotice, batchDeleteNotice, getExportUrl, getImportUrl, doReleaseData, doReovkeData } from './notice.api';
import { getList, deleteNotice, batchDeleteNotice,editIzTop, getExportUrl, getImportUrl, doReleaseData, doReovkeData } from './notice.api';
import { useListPage } from '/@/hooks/system/useListPage';
const glob = useGlobSetting();
const [registerModal, { openModal }] = useModal();
const [register, { openModal: openDetail }] = useModal();
const iframeUrl = ref('');
const { createMessage, createConfirm } = useMessage();
// 列表页面公共参数、方法
const { prefixCls, onExportXls, onImportXls, tableContext, doRequest } = useListPage({
designScope: 'notice-template',
@ -66,7 +66,8 @@
});
const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
//流程编码
const flowCode = 'dev_sys_announcement_001';
/**
* 新增事件
*/
@ -92,6 +93,12 @@
async function handleDelete(record) {
await deleteNotice({ id: record.id }, reload);
}
/**
* 置顶操作
*/
async function handleTop(record, izTop) {
await editIzTop({ id: record.id, izTop }, reload);
}
/**
* 批量删除事件
@ -118,8 +125,9 @@
*/
function handleDetail(record) {
iframeUrl.value = `${glob.uploadUrl}/sys/annountCement/show/${record.id}?token=${getToken()}`;
openDetail(true);
openDetail(true, { record });
}
/**
* 操作列定义
* @param record
@ -131,6 +139,11 @@
onClick: handleEdit.bind(null, record),
ifShow: record.sendStatus == 0 || record.sendStatus == '2',
},
{
label: '查看',
onClick: handleDetail.bind(null, record),
ifShow: record.sendStatus == 1,
},
];
}
/**
@ -148,7 +161,7 @@
},
{
label: '发布',
ifShow: record.sendStatus == 0,
ifShow: (!record?.izApproval || record.izApproval == '0') && record.sendStatus == 0,
onClick: handleRelease.bind(null, record.id),
},
{
@ -160,8 +173,22 @@
},
},
{
label: '查看',
onClick: handleDetail.bind(null, record),
label: '发布',
ifShow: record.sendStatus == '2',
popConfirm: {
title: '确定要再次发布吗?',
confirm: handleRelease.bind(null, record.id),
},
},
{
label: '置顶',
onClick: handleTop.bind(null, record, 1),
ifShow: record.sendStatus == 1 && record.izTop == 0,
},
{
label: '取消置顶',
onClick: handleTop.bind(null, record, 0),
ifShow: record.sendStatus == 1 && record.izTop == 1,
},
];
}

View File

@ -5,11 +5,17 @@ enum Api {
save = '/sys/annountCement/add',
edit = '/sys/annountCement/edit',
delete = '/sys/annountCement/delete',
queryById = '/sys/annountCement/queryById',
deleteBatch = '/sys/annountCement/deleteBatch',
exportXls = '/sys/annountCement/exportXls',
importExcel = '/sys/annountCement/importExcel',
releaseData = '/sys/annountCement/doReleaseData',
reovkeData = '/sys/annountCement/doReovkeData',
editIzTop = '/sys/annountCement/editIzTop',
addVisitsNum = '/sys/annountCement/addVisitsNumber',
tempList = '/sys/message/sysMessageTemplate/list',
}
/**
@ -21,7 +27,7 @@ export const getExportUrl = Api.exportXls;
*/
export const getImportUrl = Api.importExcel;
/**
* 查询租户列表
* 查询消息列表
* @param params
*/
export const getList = (params) => {
@ -33,7 +39,7 @@ export const getList = (params) => {
* @param params
*/
export const saveOrUpdate = (params, isUpdate) => {
let url = isUpdate ? Api.edit : Api.save;
const url = isUpdate ? Api.edit : Api.save;
return defHttp.post({ url: url, params });
};
@ -46,6 +52,15 @@ export const deleteNotice = (params, handleSuccess) => {
handleSuccess();
});
};
/**
* 置顶编辑
* @param params
*/
export const editIzTop = (params, handleSuccess) => {
return defHttp.post({ url: Api.editIzTop, data: params }).then(() => {
handleSuccess();
});
};
/**
* 批量消息公告
@ -63,3 +78,26 @@ export const doReleaseData = (params) => defHttp.get({ url: Api.releaseData, par
* @param id
*/
export const doReovkeData = (params) => defHttp.get({ url: Api.reovkeData, params });
/**
* 新增访问量
* @param id
*/
export const addVisitsNum = (params) => defHttp.get({ url: Api.addVisitsNum, params }, { successMessageMode: 'none' });
/**
* 根据ID查询数据
* @param id
*/
export const queryById = (params) => defHttp.get({ url: Api.queryById, params }, { isTransformResponse: false });
/**
* 发起流程
* import { startProcess } from '/@/api/common/api';
* @param params
*/
export const startProcess = (params) => defHttp.post({ url: Api.startProcess, params }, { isTransformResponse: false });
/**
* 查询模板列表
* @param params
*/
export const getTempList = (params) => {
return defHttp.get({ url: Api.tempList, params });
};

View File

@ -1,6 +1,7 @@
import { BasicColumn, FormSchema } from '/@/components/Table';
import { rules } from '/@/utils/helper/validator';
import { render } from '/@/utils/common/renderUtils';
import { h } from 'vue';
import { Tinymce } from '@/components/Tinymce';
export const columns: BasicColumn[] = [
{
@ -87,9 +88,24 @@ export const formSchema: FormSchema[] = [
placeholder: '请选择类型',
},
},
{
field: 'izTop',
label: '是否置顶',
defaultValue: '0',
component: 'JSwitch',
componentProps: {
//取值 options
options: ['1', '0'],
//文本option
labelOptions: ['是', '否'],
placeholder: '是否置顶',
checkedChildren: '是',
unCheckedChildren: '否',
},
},
{
field: 'titile',
label: '标题',
label: '通告标题',
component: 'Input',
required: true,
componentProps: {
@ -114,8 +130,15 @@ export const formSchema: FormSchema[] = [
},
{
field: 'msgAbstract',
label: '摘要',
label: '通告摘要',
component: 'InputTextArea',
componentProps: {
allowClear: true,
autoSize: {
minRows: 2,
maxRows: 5,
},
},
required: true,
},
// {
@ -154,9 +177,18 @@ export const formSchema: FormSchema[] = [
},
ifShow: ({ values }) => values.msgType == 'USER',
},
{
field: 'msgClassify',
label: '公告分类',
component: 'JDictSelectTag',
componentProps: {
dictCode: 'notice_type',
placeholder: '请选择公告分类',
},
},
{
field: 'priority',
label: '优先级',
label: '优先级',
defaultValue: 'H',
component: 'JDictSelectTag',
componentProps: {
@ -166,9 +198,211 @@ export const formSchema: FormSchema[] = [
},
},
{
field: 'msgContent',
label: '内容',
field: 'izApproval',
label: '是否审批',
component: 'RadioGroup',
defaultValue: '0',
componentProps: {
options: [
{
label: '是',
value: '1',
},
{
label: '否',
value: '0',
},
],
},
},
{
field: 'msgTemplate',
label: '公告模版',
component: 'Input',
slot: 'msgTemplate',
},
{
field: 'files',
label: '通告附件',
component: 'JUpload',
componentProps: {
//是否显示选择按钮
text: '文件上传',
//最大上传数
maxCount: 20,
//是否显示下载按钮
download: true,
},
},
{
field: 'msgContent',
label: '通告内容',
component: 'Input',
colProps: { span: 24 },
render: render.renderTinymce,
},
];
/**
* 流程表单调用这个方法获取formSchema
* @param param
*/
export function getBpmFormSchema(_formData): FormSchema[] {
// 默认和原始表单保持一致 如果流程中配置了权限数据这里需要单独处理formSchema
return [
{
field: 'id',
label: 'id',
component: 'Input',
show: false,
},
{
field: 'msgCategory',
label: '消息类型',
required: true,
component: 'JDictSelectTag',
defaultValue: '1',
componentProps: {
type: 'radio',
dictCode: 'msg_category',
placeholder: '请选择类型',
},
},
{
field: 'izTop',
label: '是否置顶',
defaultValue: '0',
component: 'JSwitch',
componentProps: {
//取值 options
options: ['1', '0'],
//文本option
labelOptions: ['是', '否'],
placeholder: '是否置顶',
checkedChildren: '是',
unCheckedChildren: '否',
},
},
{
field: 'titile',
label: '通告标题',
component: 'Input',
required: true,
componentProps: {
placeholder: '请输入标题',
},
// update-begin--author:liaozhiyang---date:20240701---for【TV360X-1632】标题过长保存报错长度校验
dynamicRules() {
return [
{
validator: (_, value) => {
return new Promise<void>((resolve, reject) => {
if (value.length > 100) {
reject('最长100个字符');
}
resolve();
});
},
},
];
},
// update-end--author:liaozhiyang---date:20240701---for【TV360X-1632】标题过长保存报错长度校验
},
{
field: 'msgAbstract',
label: '通告摘要',
component: 'InputTextArea',
required: true,
},
{
field: 'msgType',
label: '接收用户',
defaultValue: 'ALL',
component: 'JDictSelectTag',
required: true,
componentProps: {
type: 'radio',
dictCode: 'msg_type',
placeholder: '请选择发布范围',
},
},
{
field: 'userIds',
label: '指定用户',
component: 'JSelectUserByDepartment',
required: true,
componentProps: {
rowKey: 'id',
// update-begin--author:liaozhiyang---date:20240701---for【TV360X-1627】通知公告用户选择组件没翻译
labelKey: 'realname',
// update-end--author:liaozhiyang---date:20240701---for【TV360X-1627】通知公告用户选择组件没翻译
},
ifShow: ({ values }) => values.msgType == 'USER',
},
{
field: 'msgClassify',
label: '公告分类',
component: 'JDictSelectTag',
componentProps: {
dictCode: 'notice_type',
placeholder: '请选择公告分类',
},
},
{
field: 'priority',
label: '优先级别',
defaultValue: 'H',
component: 'JDictSelectTag',
componentProps: {
dictCode: 'priority',
type: 'radio',
placeholder: '请选择优先级',
},
},
{
field: 'msgTemplate',
label: '公告模版',
component: 'Input',
slot: 'msgTemplate',
},
{
field: 'files',
label: '通告附件',
component: 'JUpload',
componentProps: {
//是否显示选择按钮
text: '文件上传',
//最大上传数
maxCount: 2,
//是否显示下载按钮
download: true,
},
},
{
field: 'msgContent',
label: '通告内容',
component: 'Input',
colProps: { span: 24 },
ifShow: ({}) => _formData.disabled == false,
render: ({ model, field }) => {
return h(Tinymce, {
showImageUpload: false,
disabled: _formData.disabled !== false,
height: 300,
value: model[field],
onChange: (value: string) => {
model[field] = value;
},
});
},
},
{
field: 'msgContent',
label: '通告内容',
component: 'Input',
colProps: { span: 24 },
ifShow: ({}) => _formData.disabled !== false,
slot: 'msgContent',
},
];
}

View File

@ -184,10 +184,6 @@
confirm: handleDelete.bind(null, record),
},
},
{
label: '首页配置',
onClick: handleIndexConfig.bind(null, record.roleCode),
},
];
}
</script>

View File

@ -1,6 +1,11 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" :width="800" title="用户代理" @ok="handleSubmit" destroyOnClose>
<BasicForm @register="registerForm" />
<template #insertFooter>
<Popconfirm title="确定删除当前配置的代理吗?" @confirm="handleDel">
<a-button v-if="agentData.id"><Icon icon="ant-design:clear-outlined" />删除代理</a-button>
</Popconfirm>
</template>
</BasicModal>
</template>
<script lang="ts" setup>
@ -8,7 +13,8 @@
import { BasicModal, useModalInner } from '/@/components/Modal';
import { BasicForm, useForm } from '/@/components/Form/index';
import { formAgentSchema } from './user.data';
import { getUserAgent, saveOrUpdateAgent } from './user.api';
import { deleteAgent, getUserAgent, saveOrUpdateAgent } from './user.api';
import { Popconfirm } from 'ant-design-vue';
// 声明Emits
const emit = defineEmits(['success', 'register']);
//表单配置
@ -16,6 +22,8 @@
schemas: formAgentSchema,
showActionButtonGroup: false,
});
//表单数据
const agentData = ref<any>({});
//表单赋值
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
//重置表单
@ -24,6 +32,8 @@
//查询获取表单数据
const res = await getUserAgent({ userName: data.userName });
data = res.result ? res.result : data;
//代理数据赋值
agentData.value = { ...data };
//表单赋值
await setFieldsValue({ ...data });
});
@ -42,4 +52,20 @@
setModalProps({ confirmLoading: false });
}
}
/**
* 删除代理
*/
async function handleDel() {
const reload = async () => {
await resetFields();
await setFieldsValue({ userName: agentData.value.userName });
//关闭弹窗
closeModal();
emit('success');
};
if (agentData.value.id) {
await deleteAgent({ id: agentData.value.id }, reload);
}
}
</script>

View File

@ -8,6 +8,7 @@ enum Api {
edit = '/sys/user/edit',
agentSave = '/sys/sysUserAgent/add',
agentEdit = '/sys/sysUserAgent/edit',
deleteAgent = '/sys/sysUserAgent/delete',
getUserRole = '/sys/user/queryUserRole',
duplicateCheck = '/sys/duplicate/check',
deleteUser = '/sys/user/delete',
@ -208,6 +209,15 @@ export const saveOrUpdateAgent = (params) => {
let url = params.id ? Api.agentEdit : Api.agentSave;
return defHttp.post({ url: url, params });
};
/**
* 代理删除
* @param params
*/
export const deleteAgent = (params, handleSuccess) => {
return defHttp.delete({ url: Api.deleteAgent, params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
};
/**
* 用户离职(新增代理人和用户状态变更操作)