v3.14.0 更新;【新增】EasyExcel重磅升级为FastExcel;【新增】使用最强Argon2算法作为密码存储;【新增】大家吐槽的数据字典改为可重复;【新增】前端布局再增加多种样式;

This commit is contained in:
zhuoda
2025-03-12 21:30:24 +08:00
parent e74f179a91
commit fecb3a9d81
207 changed files with 4260 additions and 2019 deletions

View File

@@ -17,7 +17,7 @@
</a-modal>
</template>
<script setup>
import { ref, reactive } from 'vue';
import { ref, reactive, nextTick } from 'vue';
import { message } from 'ant-design-vue';
import { SmartLoading } from '/@/components/framework/smart-loading';
import _ from 'lodash';
@@ -43,6 +43,21 @@
Object.assign(form, rowData);
}
visible.value = true;
nextTick(() => {
// 解决弹窗错误信息显示,没有可忽略
const domArr = document.getElementsByClassName('ant-modal');
if (domArr && domArr.length > 0) {
Array.from(domArr).forEach((item) => {
if (item.childNodes && item.childNodes.length > 0) {
Array.from(item.childNodes).forEach((child) => {
if (child.setAttribute) {
child.setAttribute('aria-hidden', 'false');
}
});
}
});
}
});
}
function onClose() {

View File

@@ -20,7 +20,7 @@
<SmartEnumSelect enum-name="GOODS_STATUS_ENUM" v-model:value="form.goodsStatus" />
</a-form-item>
<a-form-item label="产地" name="place">
<DictSelect width="100%" key-code="GODOS_PLACE" v-model:value="form.place" mode="tags" />
<DictSelect width="100%" key-code="GOODS_PLACE" v-model:value="form.place" mode="tags" />
</a-form-item>
<a-form-item label="上架状态" name="shelvesFlag">
<a-radio-group v-model:value="form.shelvesFlag">
@@ -107,7 +107,7 @@
Object.assign(form, rowData);
}
if (form.place && form.place.length > 0) {
form.place = form.place.map((e) => e.valueCode);
form.place = form.place.split(',');
}
visible.value = true;
nextTick(() => {

View File

@@ -25,7 +25,7 @@
</a-form-item>
<a-form-item label="产地" name="place" class="smart-query-form-item">
<DictSelect key-code="GODOS_PLACE" v-model:value="queryForm.place" width="120px" />
<DictSelect key-code="GOODS_PLACE" v-model:value="queryForm.place" width="120px" />
</a-form-item>
<a-form-item label="商品状态" name="goodsStatus" class="smart-query-form-item">
@@ -35,8 +35,8 @@
<a-form-item label="快速筛选" class="smart-query-form-item">
<a-radio-group v-model:value="queryForm.shelvesFlag" @change="onSearch">
<a-radio-button :value="undefined">全部</a-radio-button>
<a-radio-button :value="true">上架</a-radio-button>
<a-radio-button :value="false">下架</a-radio-button>
<a-radio-button :value="'true'">上架</a-radio-button>
<a-radio-button :value="'false'">下架</a-radio-button>
</a-radio-group>
</a-form-item>
@@ -102,15 +102,28 @@
:dataSource="tableData"
:columns="columns"
rowKey="goodsId"
:scroll="{ x: 1000 }"
bordered
:pagination="false"
:showSorterTooltip="false"
:row-selection="{ selectedRowKeys: selectedRowKeyList, onChange: onSelectChange }"
@change="onChange"
@resizeColumn="handleResizeColumn"
>
<!-- main.js中有全局表格高度配置也可单独配置 -->
<!-- :scroll="{ y: 300 }" -->
<template #headerCell="{ column }">
<SmartHeaderCell v-model:value="queryForm[column.filterOptions?.key || column.dataIndex]" :column="column" @change="queryData" />
</template>
<template #bodyCell="{ text, record, column }">
<template v-if="column.dataIndex === 'goodsName'">
{{ text }}
</template>
<template v-if="column.dataIndex === 'place'">
<span>{{ text && text.length > 0 ? text.map((e) => e.valueName).join(',') : '' }}</span>
<DictPreview :options="descList.GOODS_PLACE" :value="text" />
</template>
<template v-if="column.dataIndex === 'remark'">
<span>{{ text ? text : '' }}</span>
</template>
<template v-if="column.dataIndex === 'goodsStatus'">
<span>{{ $smartEnumPlugin.getDescByValue('GOODS_STATUS_ENUM', text) }}</span>
@@ -176,7 +189,7 @@
</template>
<script setup>
import GoodsFormModal from './components/goods-form-modal.vue';
import { reactive, ref, onMounted } from 'vue';
import { onMounted, reactive, ref } from 'vue';
import { message, Modal } from 'ant-design-vue';
import { SmartLoading } from '/@/components/framework/smart-loading';
import { goodsApi } from '/@/api/business/goods/goods-api';
@@ -189,52 +202,88 @@
import { GOODS_STATUS_ENUM } from '/@/constants/business/erp/goods-const';
import DictSelect from '/@/components/support/dict-select/index.vue';
import SmartEnumSelect from '/@/components/framework/smart-enum-select/index.vue';
import { FILE_FOLDER_TYPE_ENUM } from '/@/constants/support/file-const.js';
import FileUpload from '/@/components/support/file-upload/index.vue';
import _ from 'lodash';
import SmartHeaderCell from '/@/components/smart-table-header-cell/index.vue';
import DictPreview from '/@/components/dict-preview/index.vue';
import { useDict } from '/@/utils/dict';
const descList = useDict('GOODS_PLACE');
// ---------------------------- 表格列 ----------------------------
const columns = ref([
{
title: '商品分类',
dataIndex: 'categoryName',
resizable: true,
filterOptions: {
type: 'category-tree',
key: 'categoryId',
categoryType: CATEGORY_TYPE_ENUM.GOODS.value,
},
width: 150,
},
{
title: '商品名称',
dataIndex: 'goodsName',
resizable: true,
filterOptions: {
type: 'input',
key: 'searchWord',
},
width: 150,
},
{
title: '商品状态',
dataIndex: 'goodsStatus',
sorter: true
resizable: true,
sorter: true,
filterOptions: {
type: 'enum-select',
enumName: 'GOODS_STATUS_ENUM',
},
width: 150,
},
{
title: '产地',
dataIndex: 'place',
resizable: true,
filterOptions: {
type: 'dict-select',
keyCode: 'GOODS_PLACE',
},
width: 150,
},
{
title: '商品价格',
title: '价格',
dataIndex: 'price',
sorter: true
resizable: true,
sorter: true,
width: 100,
},
{
title: '上架状态',
title: '状态',
dataIndex: 'shelvesFlag',
sorter: true
resizable: true,
sorter: true,
width: 80,
},
{
title: '备注',
dataIndex: 'remark',
ellipsis: true,
resizable: true,
width: 150,
},
{
title: '创建时间',
dataIndex: 'createTime',
resizable: true,
width: 150,
},
{
title: '操作',
dataIndex: 'action',
resizable: true,
fixed: 'right',
width: 100,
},
@@ -251,7 +300,7 @@
goodsType: undefined,
pageNum: 1,
pageSize: 10,
sortItemList: []
sortItemList: [],
};
// 查询表单form
const queryForm = reactive(_.cloneDeep(queryFormState));
@@ -261,7 +310,15 @@
const tableData = ref([]);
// 总数
const total = ref(0);
function handleResizeColumn(w, col) {
columns.value.forEach((item) => {
if (item.dataIndex === col.dataIndex) {
item.width = Math.floor(w);
// 拖动宽度标识
item.dragAndDropFlag = true;
}
});
}
// 重置查询条件
function resetQuery() {
let pageSize = queryForm.pageSize;
@@ -421,11 +478,11 @@
await goodsApi.exportGoods();
}
function onChange(pagination, filters, sorter, { action }){
function onChange(pagination, filters, sorter, { action }) {
if (action === 'sort') {
const { order, field } = sorter;
let column = camelToUnderscore(field);
let findIndex = queryForm.sortItemList.findIndex(e => e.column === column);
let findIndex = queryForm.sortItemList.findIndex((e) => e.column === column);
if (findIndex !== -1) {
queryForm.sortItemList.splice(findIndex, 1);
}
@@ -433,7 +490,7 @@
let isAsc = order !== 'ascend';
queryForm.sortItemList.push({
column,
isAsc
isAsc,
});
}
queryData();
@@ -441,6 +498,6 @@
}
function camelToUnderscore(str) {
return str.replace(/([A-Z])/g, "_$1").toLowerCase();
return str.replace(/([A-Z])/g, '_$1').toLowerCase();
}
</script>

View File

@@ -85,6 +85,21 @@
detail(enterpriseId);
}
visible.value = true;
nextTick(() => {
// 解决弹窗错误信息显示,没有可忽略
const domArr = document.getElementsByClassName('ant-modal');
if (domArr && domArr.length > 0) {
Array.from(domArr).forEach((item) => {
if (item.childNodes && item.childNodes.length > 0) {
Array.from(item.childNodes).forEach((child) => {
if (child.setAttribute) {
child.setAttribute('aria-hidden', 'false');
}
});
}
});
}
});
}
function onClose() {

View File

@@ -60,102 +60,102 @@
</div>
</template>
<script setup>
import { reactive, ref } from 'vue';
import { noticeApi } from '/@/api/business/oa/notice-api';
import { PAGE_SIZE, PAGE_SIZE_OPTIONS } from '/@/constants/common-const';
import DepartmentTreeSelect from '/@/components/system/department-tree-select/index.vue';
import uaparser from 'ua-parser-js';
import { reactive, ref } from 'vue';
import { noticeApi } from '/@/api/business/oa/notice-api';
import { PAGE_SIZE, PAGE_SIZE_OPTIONS } from '/@/constants/common-const';
import DepartmentTreeSelect from '/@/components/system/department-tree-select/index.vue';
import uaparser from 'ua-parser-js';
const props = defineProps({
noticeId: {
type: [Number, String],
},
});
const props = defineProps({
noticeId: {
type: [Number, String],
},
});
defineExpose({
onSearch,
});
defineExpose({
onSearch,
});
const tableColumns = [
{
title: '姓名',
dataIndex: 'employeeName',
},
{
title: '查看次数',
dataIndex: 'pageViewCount',
},
{
title: '首次查看设备',
dataIndex: 'firstIp',
},
{
title: '首次查看时间',
dataIndex: 'createTime',
},
{
title: '最后一次查看设备',
dataIndex: 'lastIp',
},
{
title: '最后一次查看时间',
dataIndex: 'updateTime',
with: 80,
},
];
const tableColumns = [
{
title: '姓名',
dataIndex: 'employeeName',
},
{
title: '查看次数',
dataIndex: 'pageViewCount',
},
{
title: '首次查看设备',
dataIndex: 'firstIp',
},
{
title: '首次查看时间',
dataIndex: 'createTime',
},
{
title: '最后一次查看设备',
dataIndex: 'lastIp',
},
{
title: '最后一次查看时间',
dataIndex: 'updateTime',
with: 80,
},
];
const tableData = ref([]);
const total = ref(0);
const tableLoading = ref(false);
const tableData = ref([]);
const total = ref(0);
const tableLoading = ref(false);
const defaultQueryForm = {
noticeId: props.noticeId,
departmentId: null,
keywords: '',
pageNum: 1,
pageSize: PAGE_SIZE,
};
const defaultQueryForm = {
noticeId: props.noticeId,
departmentId: null,
keywords: '',
pageNum: 1,
pageSize: PAGE_SIZE,
};
const queryForm = reactive({ ...defaultQueryForm });
const queryForm = reactive({ ...defaultQueryForm });
function buildDeviceInfo(userAgent) {
if (!userAgent) {
return '';
}
let ua = uaparser(userAgent);
let browser = ua.browser.name;
let os = ua.os.name;
return browser + '/' + os + '/' + (ua.device.vendor ? ua.device.vendor + ua.device.model : '');
}
async function queryViewRecord() {
try {
tableLoading.value = true;
const result = await noticeApi.queryViewRecord(queryForm);
for (const e of result.data.list) {
e.firstDevice = buildDeviceInfo(e.firstUserAgent);
e.lastDevice = buildDeviceInfo(e.lastUserAgent);
function buildDeviceInfo(userAgent) {
if (!userAgent) {
return '';
}
tableData.value = result.data.list;
total.value = result.data.total;
} catch (err) {
console.log(err);
} finally {
tableLoading.value = false;
let ua = uaparser(userAgent);
let browser = ua.browser.name;
let os = ua.os.name;
return browser + '/' + os + '/' + (ua.device.vendor ? ua.device.vendor + ua.device.model : '');
}
}
// 点击查询
function onSearch() {
queryForm.pageNum = 1;
queryViewRecord();
}
// 点击重置
function resetQuery() {
Object.assign(queryForm, defaultQueryForm);
queryViewRecord();
}
async function queryViewRecord() {
try {
tableLoading.value = true;
const result = await noticeApi.queryViewRecord(queryForm);
for (const e of result.data.list) {
e.firstDevice = buildDeviceInfo(e.firstUserAgent);
e.lastDevice = buildDeviceInfo(e.lastUserAgent);
}
tableData.value = result.data.list;
total.value = result.data.total;
} catch (err) {
console.log(err);
} finally {
tableLoading.value = false;
}
}
// 点击查询
function onSearch() {
queryForm.pageNum = 1;
queryViewRecord();
}
// 点击重置
function resetQuery() {
Object.assign(queryForm, defaultQueryForm);
queryViewRecord();
}
</script>

View File

@@ -11,17 +11,17 @@
<a-card style="margin-bottom: 15px" size="small">
<a-descriptions :title="noticeDetail.title" :column="4" size="small">
<template #extra>
<a-button v-if="!noticeDetail.publishFlag" type="primary" size="small" @click="onEdit">编辑</a-button>
<a-button type="primary" size="small" @click="onEdit">编辑</a-button>
</template>
<a-descriptions-item label="分类">{{ noticeDetail.noticeTypeName }}</a-descriptions-item>
<a-descriptions-item label="文号">{{ noticeDetail.documentNumber }}</a-descriptions-item>
<a-descriptions-item label="文号">{{ noticeDetail.documentNumber ? noticeDetail.documentNumber : '无' }}</a-descriptions-item>
<a-descriptions-item label="来源">{{ noticeDetail.source }}</a-descriptions-item>
<a-descriptions-item label="作者">{{ noticeDetail.author }}</a-descriptions-item>
<a-descriptions-item label="页面浏览量">{{ noticeDetail.pageViewCount }}</a-descriptions-item>
<a-descriptions-item label="用户浏览量">{{ noticeDetail.userViewCount }}</a-descriptions-item>
<a-descriptions-item label="创建时间">{{ noticeDetail.createTime }}</a-descriptions-item>
<a-descriptions-item label="发布时间">{{ noticeDetail.publishTime }}</a-descriptions-item>
<a-descriptions-item label="定时发布">{{ noticeDetail.publishFlag ? '已发布' : '待发布' }}</a-descriptions-item>
<a-descriptions-item label="发布状态">{{ noticeDetail.publishFlag ? '已发布' : '待发布' }}</a-descriptions-item>
<a-descriptions-item label="删除状态">{{ noticeDetail.deletedFlag ? '已删除' : '未删除' }}</a-descriptions-item>
<a-descriptions-item v-if="!$lodash.isEmpty(noticeDetail.attachment)" label="附件">
<div class="file-list">

View File

@@ -35,7 +35,7 @@
</a-button-group>
</a-form-item>
</a-row>
<a-row class="smart-query-form-row"> </a-row>
<a-row class="smart-query-form-row" />
</a-form>
<a-card size="small" :bordered="false">

View File

@@ -28,7 +28,7 @@
</a-form-item>
<a-form-item label="创建人" class="smart-query-form-item">
<a-input style="width: 100px" v-model:value="queryForm.createUserId" placeholder="创建人" />
<a-input style="width: 100px" v-model:value="queryForm.createUserName" placeholder="创建人" />
</a-form-item>
<a-form-item label="是否删除" class="smart-query-form-item">
@@ -91,6 +91,9 @@
<template v-if="column.dataIndex === 'title'">
<a @click="toDetail(record.noticeId)">{{ text }}</a>
</template>
<template v-if="column.dataIndex === 'documentNumber'">
{{ text ? text : '' }}
</template>
<template v-else-if="column.dataIndex === 'allVisibleFlag'"> {{ text ? '全部可见' : '部分可见' }} </template>
<template v-else-if="column.dataIndex === 'publishFlag'">
{{ text ? '已发布' : '待发布' }}
@@ -145,7 +148,7 @@
noticeTypeId: undefined, //分类
keywords: '', //标题、作者、来源
documentNumber: '', //文号
createUserId: undefined, //创建人
createUserName: undefined, //创建人
deletedFlag: undefined, //删除标识
createTimeBegin: null, //创建-开始时间
createTimeEnd: null, //创建-截止时间
@@ -195,7 +198,7 @@
ellipsis: true,
},
{
title: '发布',
title: '发布状态',
dataIndex: 'publishFlag',
width: 80,
},

View File

@@ -199,7 +199,7 @@
function setData(tableColumns, config) {
//------------- 更新基础信息 -----------------
if (config.insertAndUpdate) {
formData.isSupportInsertAndUpdate = config.insertAndUpdate.isSupportInsertAndUpdate ? config.insertAndUpdate.isSupportInsertAndUpdate : true;
formData.isSupportInsertAndUpdate = config.insertAndUpdate.isSupportInsertAndUpdate;
formData.pageType = config.insertAndUpdate.pageType;
formData.width = config.insertAndUpdate.width;
formData.countPerLine = config.insertAndUpdate.countPerLine;

View File

@@ -43,7 +43,7 @@
新建
</a-button>
<a-button @click="confirmBatchDelete" v-privilege="'support:dict:batchDelete'" type="text" danger :disabled="selectedRowKeyList.length === 0">
<a-button @click="confirmBatchDelete" v-privilege="'support:dict:batchDelete'" type="primary" danger :disabled="selectedRowKeyList.length === 0">
<template #icon>
<DeleteOutlined />
</template>

View File

@@ -107,7 +107,8 @@
<template v-if="column.dataIndex === 'enabledFlag'">
<a-switch
v-model:checked="record.enabledFlag"
checked-children="已启用" un-checked-children="已禁用"
checked-children="已启用"
un-checked-children="已禁用"
@change="(checked) => handleEnabledUpdate(checked, record)"
:loading="record.enabledLoading"
/>
@@ -117,31 +118,31 @@
<a-button v-privilege="'support:job:update'" @click="openUpdateModal(record)" type="link">编辑</a-button>
<a-button v-privilege="'support:job:execute'" type="link" @click="openExecuteModal(record)">执行</a-button>
<a-button v-privilege="'support:job:log:query'" @click="openJobLogModal(record.jobId, record.jobName)" type="link">记录</a-button>
<a-button danger v-privilege="'support:job:log:delete'" @click="confirmDelete(record.jobId, record.jobName)" type="link">删除</a-button>
<a-button danger v-privilege="'support:job:log:delete'" @click="confirmDelete(record.jobId, record.jobName)" type="link"
>删除</a-button
>
</div>
</template>
</template>
</a-table>
<div class="smart-query-table-page">
<a-pagination
showSizeChanger
showQuickJumper
show-less-items
:pageSizeOptions="PAGE_SIZE_OPTIONS"
:defaultPageSize="queryForm.pageSize"
v-model:current="queryForm.pageNum"
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="queryJobList"
@showSizeChange="queryJobList"
:show-total="(total) => `${total}`"
showSizeChanger
showQuickJumper
show-less-items
:pageSizeOptions="PAGE_SIZE_OPTIONS"
:defaultPageSize="queryForm.pageSize"
v-model:current="queryForm.pageNum"
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="queryJobList"
@showSizeChange="queryJobList"
:show-total="(total) => `${total}`"
/>
</div>
</a-tab-pane>
<a-tab-pane key="2" tab="已删除任务"><DeletedJobList /></a-tab-pane>
</a-tabs>
</a-card>
<!-- 表单操作 -->
@@ -152,7 +153,7 @@
</div>
</template>
<script setup>
import {message, Modal} from 'ant-design-vue';
import { message, Modal } from 'ant-design-vue';
import { onMounted, reactive, ref } from 'vue';
import { jobApi } from '/@/api/support/job-api';
import { PAGE_SIZE_OPTIONS } from '/@/constants/common-const';
@@ -163,8 +164,8 @@
import { TRIGGER_TYPE_ENUM } from '/@/constants/support/job-const.js';
import JobFormModal from './components/job-form-modal.vue';
import JobLogListModal from './components/job-log-list-modal.vue';
import {SmartLoading} from "/@/components/framework/smart-loading/index.js";
import { SmartLoading } from '/@/components/framework/smart-loading/index.js';
const activeKey = ref('1');
const columns = ref([
{
title: 'id',
@@ -333,7 +334,7 @@
// ------------------------------------ 删除操作 -------------------------------------
function confirmDelete(jobId, jobName){
function confirmDelete(jobId, jobName) {
Modal.confirm({
title: '警告',
content: `确定要删除【${jobName}】任务吗?`,
@@ -347,15 +348,15 @@
});
}
async function deleteJob(jobId){
try{
async function deleteJob(jobId) {
try {
SmartLoading.show();
await jobApi.deleteJob(jobId);
message.success('删除成功!');
queryJobList();
}catch (e){
} catch (e) {
smartSentry.captureError(e);
}finally {
} finally {
SmartLoading.hide();
}
}

View File

@@ -11,6 +11,16 @@
<a-form-item label="登录账号" name="loginName">
<a-input class="form-item" v-model:value.trim="form.loginName" placeholder="请输入登录账号" disabled />
</a-form-item>
<a-form-item label="部门" name="departmentId">
<DepartmentTreeSelect
class="form-item"
ref="departmentTreeSelect"
width="100%"
:init="false"
v-model:value="form.departmentId"
disabled
/>
</a-form-item>
<a-form-item label="员工名称" name="actualName">
<a-input class="form-item" v-model:value.trim="form.actualName" placeholder="请输入员工名称" />
</a-form-item>
@@ -20,8 +30,11 @@
<a-form-item label="手机号码" name="phone">
<a-input class="form-item" v-model:value.trim="form.phone" placeholder="请输入手机号码" />
</a-form-item>
<a-form-item label="部门" name="departmentId">
<DepartmentTreeSelect class="form-item" ref="departmentTreeSelect" width="100%" :init="false" v-model:value="form.departmentId" />
<a-form-item label="邮箱" name="email">
<a-input v-model:value.trim="form.email" placeholder="请输入邮箱" />
</a-form-item>
<a-form-item label="职务" name="positionId">
<PositionSelect v-model:value="form.positionId" placeholder="请选择职务" />
</a-form-item>
<a-form-item label="备注" name="remark">
<a-textarea class="form-item" v-model:value="form.remark" placeholder="请输入备注" :rows="4" />
@@ -38,7 +51,7 @@
list-type="picture-card"
class="avatar-uploader"
:show-upload-list="false"
:headers="{ 'x-access-token': useUserStore().getToken }"
:headers="{ Authorization: 'Bearer ' + useUserStore().getToken }"
:customRequest="customRequest"
:before-upload="beforeUpload"
>
@@ -65,6 +78,7 @@
import { onMounted, reactive, ref } from 'vue';
import { regular } from '/@/constants/regular-const.js';
import DepartmentTreeSelect from '/@/components/system/department-tree-select/index.vue';
import PositionSelect from '/@/components/system/position-select/index.vue';
import SmartEnumSelect from '/@/components/framework/smart-enum-select/index.vue';
import { loginApi } from '/@/api/system/login-api.js';
import { useUserStore } from '/@/store/modules/system/user.js';
@@ -93,10 +107,12 @@
phone: '',
// 部门id
departmentId: undefined,
// 是否启用
disabledFlag: undefined,
// 邮箱
email: undefined,
// 职务级别
positionId: undefined,
// 是否禁用
disabledFlag: undefined,
// 备注
remark: '',
};
@@ -111,7 +127,7 @@
{ pattern: regular.phone, message: '请输入正确的手机号码', trigger: 'blur' },
],
gender: [{ required: true, message: '性别不能为空' }],
departmentId: [{ required: true, message: '部门不能为空' }],
email: [{ required: true, message: '请输入邮箱' }],
};
// 头像地址
let avatarUrl = ref();
@@ -128,12 +144,13 @@
form.employeeId = data.employeeId;
form.loginName = data.loginName;
form.actualName = data.actualName;
form.email = data.email;
form.gender = data.gender;
form.phone = data.phone;
form.departmentId = data.departmentId;
form.disabledFlag = data.disabledFlag;
form.email = data.email;
form.positionId = data.positionId;
form.remark = data.remark;
form.disabledFlag = data.disabledFlag;
// 头像展示
avatarUrl.value = data.avatar;
} catch (e) {
@@ -187,7 +204,7 @@
async function updateEmployee() {
SmartLoading.show();
try {
await employeeApi.updateByLogin(form);
await employeeApi.updateCenter(form);
message.success('更新成功');
// 重新获取详情,刷新整体缓存
await getLoginInfo();

View File

@@ -4,13 +4,13 @@
<div class="password-form-area">
<a-form ref="formRef" :model="form" :rules="rules" layout="vertical">
<a-form-item label="原密码" name="oldPassword">
<a-input-password class="form-item" v-model:value.trim="form.oldPassword" type="password" placeholder="请输入原密码" />
<a-input-password class="form-item" v-model:value.trim="form.oldPassword" type="password" placeholder="请输入原密码" autocomplete="off" />
</a-form-item>
<a-form-item label="新密码" name="newPassword" :help="tips">
<a-input-password class="form-item" v-model:value.trim="form.newPassword" type="password" placeholder="请输入新密码" />
<a-input-password class="form-item" v-model:value.trim="form.newPassword" type="password" placeholder="请输入新密码" autocomplete="off" />
</a-form-item>
<a-form-item label="确认密码" name="confirmPwd" :help="tips">
<a-input-password class="form-item" v-model:value.trim="form.confirmPwd" type="password" placeholder="请输入确认密码" />
<a-input-password class="form-item" v-model:value.trim="form.confirmPwd" type="password" placeholder="请输入确认密码" autocomplete="off" />
</a-form-item>
</a-form>
<a-button type="primary" style="margin: 20px 0 0 250px" @click="onSubmit">修改密码</a-button>

View File

@@ -31,9 +31,13 @@
});
// 选中的菜单
let selectedMenu = ref({ menuId: 0 });
let selectedKeys = computed(() => {
return _.isEmpty(selectedMenu.value) ? [] : [selectedMenu.value.menuId];
});
let selectedKeys = ref([]);
watch(
() => selectedMenu.value,
(newQuery, oldQuery) => {
selectedKeys.value = _.isEmpty(selectedMenu.value) ? [] : [selectedMenu.value.menuId];
}
);
function selectMenu(menuId) {
selectedMenu.value = menuList.value.find((e) => e.menuId === menuId);

View File

@@ -11,42 +11,59 @@
<a-modal v-model:open="visible" :title="formState.departmentId ? '编辑部门' : '添加部门'" @ok="handleOk" destroyOnClose>
<a-form ref="formRef" :model="formState" :rules="rules" layout="vertical">
<a-form-item label="上级部门" name="parentId" v-if="formState.parentId != 0">
<DepartmentTreeSelect ref="departmentTreeSelect" v-model:value="formState.parentId" :defaultValueFlag="false" width="100%" />
<DepartmentTreeSelect ref="departmentTreeSelect" v-model:value="formState.parentId" :defaultValueFlag="false"
width="100%" />
</a-form-item>
<a-form-item label="部门名称" name="name">
<a-input v-model:value.trim="formState.name" placeholder="请输入部门名称" />
</a-form-item>
<a-form-item label="部门负责人" name="managerId">
<EmployeeSelect ref="employeeSelect" placeholder="请选择部门负责人" width="100%" v-model:value="formState.managerId" :leaveFlag="false" />
<EmployeeSelect ref="employeeSelect" placeholder="请选择部门负责人" width="100%" v-model:value="formState.managerId"
:leaveFlag="false" />
</a-form-item>
<a-form-item label="部门排序 (值越大越靠前!)" name="sort">
<a-input-number style="width: 100%" v-model:value="formState.sort" :min="0" placeholder="请输入部门名称" />
<a-input-number style="width: 100%" v-model:value="formState.sort" :min="0" placeholder="请输入部门排序" />
</a-form-item>
</a-form>
</a-modal>
</template>
<script setup>
import message from 'ant-design-vue/lib/message';
import { reactive, ref } from 'vue';
import { nextTick, reactive, ref } from 'vue';
import { departmentApi } from '/@/api/system/department-api';
import DepartmentTreeSelect from '/@/components/system/department-tree-select/index.vue';
import EmployeeSelect from '/@/components/system/employee-select/index.vue';
import { smartSentry } from '/@/lib/smart-sentry';
import { SmartLoading } from '/@/components/framework/smart-loading';
// ----------------------- 对外暴漏 ---------------------
// ----------------------- 对外暴漏 ---------------------
defineExpose({
showModal,
});
defineExpose({
showModal,
});
// ----------------------- modal 的显示与隐藏 ---------------------
const emits = defineEmits(['refresh']);
// ----------------------- modal 的显示与隐藏 ---------------------
const emits = defineEmits(['refresh']);
const visible = ref(false);
function showModal(data) {
visible.value = true;
updateFormData(data);
nextTick(() => {
// 解决弹窗错误信息显示,没有可忽略
const domArr = document.getElementsByClassName('ant-modal');
if (domArr && domArr.length > 0) {
Array.from(domArr).forEach((item) => {
if (item.childNodes && item.childNodes.length > 0) {
Array.from(item.childNodes).forEach((child) => {
if (child.setAttribute) {
child.setAttribute('aria-hidden', 'false');
}
});
}
});
}
});
}
function closeModal() {
visible.value = false;
@@ -65,30 +82,29 @@
};
const employeeSelect = ref();
let formState = reactive({
...defaultDepartmentForm,
});
// 表单校验规则
const rules = {
parentId: [{ required: true, message: '上级部门不能为空' }],
name: [
{ required: true, message: '部门名称不能为空' },
{ max: 50, message: '部门名称不能大于20个字符', trigger: 'blur' },
],
managerId: [{ required: true, message: '部门负责人不能为空' }],
};
// 更新表单数据
function updateFormData(data) {
Object.assign(formState, defaultDepartmentForm);
if (data) {
Object.assign(formState, data);
}
visible.value = true;
}
// 重置表单数据
function resetFormData() {
Object.assign(formState, defaultDepartmentForm);
let formState = reactive({
...defaultDepartmentForm,
});
// 表单校验规则
const rules = {
parentId: [{ required: true, message: '上级部门不能为空' }],
name: [
{ required: true, message: '部门名称不能为空' },
{ max: 50, message: '部门名称不能大于20个字符', trigger: 'blur' },
],
};
// 更新表单数据
function updateFormData(data) {
Object.assign(formState, defaultDepartmentForm);
if (data) {
Object.assign(formState, data);
}
visible.value = true;
}
// 重置表单数据
function resetFormData() {
Object.assign(formState, defaultDepartmentForm);
}
async function handleOk() {
try {

View File

@@ -119,6 +119,7 @@
phone: undefined,
roleIdList: undefined,
positionId: undefined,
email: undefined,
};
let form = reactive(_.cloneDeep(formDefault));

View File

@@ -17,16 +17,16 @@
>
<a-form ref="formRef" :model="form" :rules="rules" :label-col="{ span: 6 }">
<a-form-item label="职务名称" name="positionName">
<a-input style="width: 100%" v-model:value="form.positionName" placeholder="职务名称"/>
<a-input style="width: 100%" v-model:value="form.positionName" placeholder="职务名称" />
</a-form-item>
<a-form-item label="职级" name="level">
<a-input style="width: 100%" v-model:value="form.level" placeholder="职级"/>
<a-input style="width: 100%" v-model:value="form.level" placeholder="职级" />
</a-form-item>
<a-form-item label="排序" name="sort">
<a-input-number :min="0" :step="1" :precision="0" style="width: 100%" v-model:value="form.sort" placeholder="排序"/>
<a-input-number :min="0" :step="1" :precision="0" style="width: 100%" v-model:value="form.sort" placeholder="排序" />
</a-form-item>
<a-form-item label="备注" name="remark">
<a-input style="width: 100%" v-model:value="form.remark" placeholder="备注"/>
<a-input style="width: 100%" v-model:value="form.remark" placeholder="备注" />
</a-form-item>
</a-form>
@@ -39,86 +39,100 @@
</a-modal>
</template>
<script setup>
import { reactive, ref, nextTick } from 'vue';
import _ from 'lodash';
import { message } from 'ant-design-vue';
import { SmartLoading } from '/@/components/framework/smart-loading';
import { positionApi } from '/@/api/system/position-api';
import { smartSentry } from '/@/lib/smart-sentry';
import { reactive, ref, nextTick } from 'vue';
import _ from 'lodash';
import { message } from 'ant-design-vue';
import { SmartLoading } from '/@/components/framework/smart-loading';
import { positionApi } from '/@/api/system/position-api';
import { smartSentry } from '/@/lib/smart-sentry';
// ------------------------ 事件 ------------------------
// ------------------------ 事件 ------------------------
const emits = defineEmits(['reloadList']);
const emits = defineEmits(['reloadList']);
// ------------------------ 显示与隐藏 ------------------------
// 是否显示
const visibleFlag = ref(false);
// ------------------------ 显示与隐藏 ------------------------
// 是否显示
const visibleFlag = ref(false);
function show (rowData) {
Object.assign(form, formDefault);
if (rowData && !_.isEmpty(rowData)) {
Object.assign(form, rowData);
}
visibleFlag.value = true;
nextTick(() => {
formRef.value.clearValidate();
});
}
function onClose () {
Object.assign(form, formDefault);
visibleFlag.value = false;
}
// ------------------------ 表单 ------------------------
// 组件ref
const formRef = ref();
const formDefault = {
positionId: undefined,
positionName: undefined, //职务名称
level: undefined,//职纪
sort: 0,
remark: undefined, //备注
};
let form = reactive({ ...formDefault });
const rules = {
positionName: [{ required: true, message: '请输入职务名称' }],
};
// 点击确定,验证表单
async function onSubmit () {
try {
await formRef.value.validateFields();
save();
} catch (err) {
message.error('参数验证错误,请仔细填写表单数据!');
}
}
// 新建、编辑API
async function save () {
SmartLoading.show();
try {
if (form.positionId) {
await positionApi.update(form);
} else {
await positionApi.add(form);
function show(rowData) {
Object.assign(form, formDefault);
if (rowData && !_.isEmpty(rowData)) {
Object.assign(form, rowData);
}
message.success('操作成功');
emits('reloadList');
onClose();
} catch (err) {
smartSentry.captureError(err);
} finally {
SmartLoading.hide();
}
}
visibleFlag.value = true;
nextTick(() => {
formRef.value.clearValidate();
defineExpose({
show,
});
// 解决弹窗错误信息显示,没有可忽略
const domArr = document.getElementsByClassName('ant-modal');
if (domArr && domArr.length > 0) {
Array.from(domArr).forEach((item) => {
if (item.childNodes && item.childNodes.length > 0) {
Array.from(item.childNodes).forEach((child) => {
if (child.setAttribute) {
child.setAttribute('aria-hidden', 'false');
}
});
}
});
}
});
}
function onClose() {
Object.assign(form, formDefault);
visibleFlag.value = false;
}
// ------------------------ 表单 ------------------------
// 组件ref
const formRef = ref();
const formDefault = {
positionId: undefined,
positionName: undefined, //职务名称
level: undefined, //职纪
sort: 0,
remark: undefined, //备注
};
let form = reactive({ ...formDefault });
const rules = {
positionName: [{ required: true, message: '请输入职务名称' }],
};
// 点击确定,验证表单
async function onSubmit() {
try {
await formRef.value.validateFields();
save();
} catch (err) {
message.error('参数验证错误,请仔细填写表单数据!');
}
}
// 新建、编辑API
async function save() {
SmartLoading.show();
try {
if (form.positionId) {
await positionApi.update(form);
} else {
await positionApi.add(form);
}
message.success('操作成功');
emits('reloadList');
onClose();
} catch (err) {
smartSentry.captureError(err);
} finally {
SmartLoading.hide();
}
}
defineExpose({
show,
});
</script>

View File

@@ -29,7 +29,7 @@
let props = defineProps({
tree: {
type: Array,
default: [],
default: () => [],
},
});
defineEmits(['update:value']);

View File

@@ -30,7 +30,7 @@
const props = defineProps({
tree: {
type: Array,
default: [],
default: () => [],
},
index: {
type: Number,

View File

@@ -21,7 +21,7 @@
const props = defineProps({
tree: {
type: Array,
default: [],
default: () => [],
},
index: {
type: Number,