v2.0 代码提交

This commit is contained in:
zhuoda
2022-10-27 22:14:48 +08:00
parent 207b949484
commit f7e5f6d539
1851 changed files with 108157 additions and 2231 deletions

View File

@@ -1,112 +0,0 @@
<!--
* 缓存
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-06-08 21:50:41
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
-->
<template>
<a-card size="small" :bordered="false" :hoverable="true">
<a-alert>
<template v-slot:message>
<h4>缓存 介绍</h4>
</template>
<template v-slot:description>
<pre>
简介SmartAdmin使用的是SpringCache进行管理缓存SpringCache有多种实现方式本项目默认采用的是caffeine
Caffeine
- Caffeine是一个进程内部缓存框架使用了Java 8最新的[StampedLock]乐观锁技术极大提高缓存并发吞吐量一个高性能的 Java 缓存库被称为最快缓存
其他
· 对于分布式集群等应用实现方式可以改为 RedisCouchBase等
</pre
>
</template>
</a-alert>
<a-table size="small" bordered class="smart-margin-top10" :dataSource="tableData" :columns="columns" rowKey="tag" :pagination="false" >
<template #bodyCell="{ record, column }">
<template v-if="column.dataIndex === 'action'">
<div class="smart-table-operate">
<a-button @click="remove(record.key)" v-privilege="'support:cache:delete'" type="link">清除</a-button>
<a-button @click="getAllKeys(record.key)" v-privilege="'support:cache:keys'" type="link">获取所有key</a-button>
</div>
</template>
</template>
</a-table>
</a-card>
</template>
<script setup>
import { message } from 'ant-design-vue';
import { onMounted, reactive, ref, h } from 'vue';
import { cacheApi } from '/@/api/support/cache/cache-api';
import { SmartLoading } from '/@/components/framework/smart-loading';
import { Modal } from 'ant-design-vue';
import _ from 'lodash';
import { smartSentry } from '/@/lib/smart-sentry';
//------------------------ 删除 ---------------------
async function remove(key) {
try {
await cacheApi.remove(key);
message.success('删除成功');
ajaxQuery();
} catch (e) {
smartSentry.captureError(e);
}
}
//------------------------ 获取所有key ---------------------
async function getAllKeys(cacheName) {
SmartLoading.show();
try {
let res = await cacheApi.getKeys(cacheName);
SmartLoading.hide();
Modal.info({
title: '所有Key:' + cacheName,
content: h('div', {}, [h('p', _.join(res.data, ' , '))]),
onOk() {
ajaxQuery();
},
});
} catch (e) {
smartSentry.captureError(e);
} finally {
SmartLoading.hide();
}
}
//------------------------ 表格渲染 ---------------------
const columns = reactive([
{
title: 'Key',
dataIndex: 'key',
},
{
title: '操作',
dataIndex: 'action',
fixed: 'right',
width: 160,
},
]);
const tableLoading = ref(false);
const tableData = ref([]);
async function ajaxQuery() {
try {
tableLoading.value = true;
let res = await cacheApi.getAllCacheNames();
tableData.value = res.data.map((e) => Object.assign({}, { key: e }));
} catch (e) {
smartSentry.captureError(e);
} finally {
tableLoading.value = false;
}
}
onMounted(ajaxQuery);
</script>

View File

@@ -1,138 +0,0 @@
<!--
* 系统更新日志
*
* @Author: 卓大
* @Date: 2022-09-26 14:53:50
* @Copyright 1024创新实验室
-->
<template>
<a-modal
:title="form.changeLogId ? '编辑' : '添加'"
width="600px"
:closable="true"
:visible="visibleFlag"
@close="onClose"
:onCancel="onClose"
:maskClosable="false"
:destroyOnClose="true"
>
<a-form ref="formRef" :model="form" :rules="rules" :label-col="{ span: 5 }">
<a-form-item label="版本" name="version">
<a-input style="width: 100%" v-model:value="form.version" placeholder="版本" />
</a-form-item>
<a-form-item label="更新类型" name="type">
<SmartEnumSelect width="100%" v-model:value="form.type" enumName="CHANGE_LOG_TYPE_ENUM" placeholder="更新类型" />
</a-form-item>
<a-form-item label="发布人" name="publishAuthor">
<a-input style="width: 100%" v-model:value="form.publishAuthor" placeholder="发布人" />
</a-form-item>
<a-form-item label="发布日期" name="publicDate">
<a-date-picker valueFormat="YYYY-MM-DD" v-model:value="form.publicDate" style="width: 100%" placeholder="发布日期" />
</a-form-item>
<a-form-item label="跳转链接" name="link">
<a-input style="width: 100%" v-model:value="form.link" placeholder="跳转链接" />
</a-form-item>
<a-form-item label="更新内容" name="content">
<a-textarea style="width: 100%" :rows="15" v-model:value="form.content" placeholder="更新内容" />
</a-form-item>
</a-form>
<template #footer>
<a-space>
<a-button @click="onClose">取消</a-button>
<a-button type="primary" @click="onSubmit">保存</a-button>
</a-space>
</template>
</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 { changeLogApi } from '/@/api/support/change-log/change-log-api';
import { smartSentry } from '/@/lib/smart-sentry';
import SmartEnumSelect from '/@/components/framework/smart-enum-select/index.vue';
// ------------------------ 事件 ------------------------
const emits = defineEmits(['reloadList']);
// ------------------------ 显示与隐藏 ------------------------
// 是否显示
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 = {
changeLogId: undefined,
version: undefined, //版本
type: undefined, //更新类型:[1:特大版本功能更新;2:功能更新;3:bug修复]
publishAuthor: undefined, //发布人
publicDate: undefined, //发布日期
content: undefined, //更新内容
link: undefined, //跳转链接
};
let form = reactive({ ...formDefault });
const rules = {
version: [{ required: true, message: '版本 必填' }],
type: [{ required: true, message: '更新类型:[1:特大版本功能更新;2:功能更新;3:bug修复] 必填' }],
publishAuthor: [{ required: true, message: '发布人 必填' }],
publicDate: [{ required: true, message: '发布日期 必填' }],
content: [{ 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.changeLogId) {
await changeLogApi.update(form);
} else {
await changeLogApi.add(form);
}
message.success('操作成功');
emits('reloadList');
onClose();
} catch (err) {
smartSentry.captureError(err);
} finally {
SmartLoading.hide();
}
}
defineExpose({
show,
});
</script>

View File

@@ -1,317 +0,0 @@
<!--
* 系统更新日志
*
* @Author: 卓大
* @Date: 2022-09-26 14:53:50
* @Copyright 1024创新实验室
-->
<template>
<!---------- 查询表单form begin ----------->
<a-form class="smart-query-form" v-privilege="'changeLog:query'">
<a-row class="smart-query-form-row">
<a-form-item label="更新类型" class="smart-query-form-item">
<SmartEnumSelect width="200px" v-model:value="queryForm.type" enumName="CHANGE_LOG_TYPE_ENUM" placeholder="更新类型" />
</a-form-item>
<a-form-item label="关键字" class="smart-query-form-item">
<a-input style="width: 200px" v-model:value="queryForm.keyword" placeholder="关键字" />
</a-form-item>
<a-form-item label="发布日期" class="smart-query-form-item">
<a-range-picker v-model:value="queryForm.publicDate" :ranges="defaultTimeRanges" style="width: 240px" @change="onChangePublicDate" />
</a-form-item>
<a-form-item label="创建时间" class="smart-query-form-item">
<a-date-picker valueFormat="YYYY-MM-DD" v-model:value="queryForm.createTime" style="width: 150px" />
</a-form-item>
<a-form-item class="smart-query-form-item">
<a-button type="primary" @click="queryData">
<template #icon>
<ReloadOutlined />
</template>
查询
</a-button>
<a-button @click="resetQuery" class="smart-margin-left10">
<template #icon>
<SearchOutlined />
</template>
重置
</a-button>
</a-form-item>
</a-row>
</a-form>
<!---------- 查询表单form end ----------->
<a-card size="small" :bordered="false" :hoverable="true">
<!---------- 表格操作行 begin ----------->
<a-row class="smart-table-btn-block">
<div class="smart-table-operate-block">
<a-button @click="showForm" type="primary" size="small" v-privilege="'changeLog:add'">
<template #icon>
<PlusOutlined />
</template>
新建
</a-button>
<a-button @click="confirmBatchDelete" type="danger" size="small" :disabled="selectedRowKeyList.length == 0" v-privilege="'changeLog:batchDelete'">
<template #icon>
<DeleteOutlined />
</template>
批量删除
</a-button>
</div>
<div class="smart-table-setting-block">
<TableOperator v-model="columns" :tableId="null" :refresh="queryData" />
</div>
</a-row>
<!---------- 表格操作行 end ----------->
<!---------- 表格 begin ----------->
<a-table
size="small"
:dataSource="tableData"
:columns="columns"
rowKey="changeLogId"
bordered
:pagination="false"
:row-selection="{ selectedRowKeys: selectedRowKeyList, onChange: onSelectChange }"
>
<template #bodyCell="{ text, record, column }">
<template v-if="column.dataIndex === 'version'">
<a-button @click="showModal(record)" type="link">{{text}}</a-button>
</template>
<template v-if="column.dataIndex === 'type'">
<a-tag color="success">
<template #icon>
<check-circle-outlined />
</template>
{{ $smartEnumPlugin.getDescByValue('CHANGE_LOG_TYPE_ENUM', text) }}
</a-tag>
</template>
<template v-if="column.dataIndex === 'action'">
<div class="smart-table-operate">
<a-button @click="showForm(record)" type="link" v-privilege="'changeLog:update'">编辑</a-button>
<a-button @click="onDelete(record)" danger type="link" v-privilege="'changeLog:delete'">删除</a-button>
</div>
</template>
</template>
</a-table>
<!---------- 表格 end ----------->
<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="queryData"
@showSizeChange="queryData"
:show-total="(total) => `${total}`"
/>
</div>
<ChangeLogForm ref="formRef" @reloadList="queryData" />
<ChangeLogModal ref="modalRef" />
</a-card>
</template>
<script setup>
import { reactive, ref, onMounted } from 'vue';
import { message, Modal } from 'ant-design-vue';
import { SmartLoading } from '/@/components/framework/smart-loading';
import { changeLogApi } from '/@/api/support/change-log/change-log-api';
import { PAGE_SIZE_OPTIONS } from '/@/constants/common-const';
import { smartSentry } from '/@/lib/smart-sentry';
import TableOperator from '/@/components/support/table-operator/index.vue';
import DictSelect from '/@/components/support/dict-select/index.vue';
import SmartEnumSelect from '/@/components/framework/smart-enum-select/index.vue';
import { defaultTimeRanges } from '/@/lib/default-time-ranges';
import ChangeLogModal from './change-log-modal.vue';
import ChangeLogForm from './change-log-form.vue';
// ---------------------------- 表格列 ----------------------------
const columns = ref([
{
title: '版本',
dataIndex: 'version',
ellipsis: true,
},
{
title: '更新类型',
dataIndex: 'type',
ellipsis: true,
},
{
title: '发布人',
dataIndex: 'publishAuthor',
ellipsis: true,
},
{
title: '发布日期',
dataIndex: 'publicDate',
ellipsis: true,
},
{
title: '更新内容',
dataIndex: 'content',
ellipsis: true,
},
{
title: '跳转链接',
dataIndex: 'link',
ellipsis: true,
},
{
title: '创建时间',
dataIndex: 'createTime',
ellipsis: true,
},
{
title: '更新时间',
dataIndex: 'updateTime',
ellipsis: true,
},
{
title: '操作',
dataIndex: 'action',
fixed: 'right',
width: 90,
},
]);
// ---------------------------- 查询数据表单和方法 ----------------------------
const queryFormState = {
type: undefined, //更新类型:[1:特大版本功能更新;2:功能更新;3:bug修复]
keyword: undefined, //关键字
publicDate: [], //发布日期
publicDateBegin: undefined, //发布日期 开始
publicDateEnd: undefined, //发布日期 结束
createTime: undefined, //创建时间
link: undefined, //跳转链接
pageNum: 1,
pageSize: 10,
};
// 查询表单form
const queryForm = reactive({ ...queryFormState });
// 表格加载loading
const tableLoading = ref(false);
// 表格数据
const tableData = ref([]);
// 总数
const total = ref(0);
// 重置查询条件
function resetQuery() {
let pageSize = queryForm.pageSize;
Object.assign(queryForm, queryFormState);
queryForm.pageSize = pageSize;
queryData();
}
// 查询数据
async function queryData() {
tableLoading.value = true;
try {
let queryResult = await changeLogApi.queryPage(queryForm);
tableData.value = queryResult.data.list;
total.value = queryResult.data.total;
} catch (e) {
smartSentry.captureError(e);
} finally {
tableLoading.value = false;
}
}
function onChangePublicDate(dates, dateStrings) {
queryForm.publicDateBegin = dateStrings[0];
queryForm.publicDateEnd = dateStrings[1];
}
onMounted(queryData);
// ---------------------------- 查看 ----------------------------
const modalRef = ref();
function showModal(data) {
modalRef.value.show(data);
}
// ---------------------------- 添加/修改 ----------------------------
const formRef = ref();
function showForm(data) {
formRef.value.show(data);
}
// ---------------------------- 单个删除 ----------------------------
//确认删除
function onDelete(data) {
Modal.confirm({
title: '提示',
content: '确定要删除选吗?',
okText: '删除',
okType: 'danger',
onOk() {
requestDelete(data);
},
cancelText: '取消',
onCancel() {},
});
}
//请求删除
async function requestDelete(data) {
SmartLoading.show();
try {
let deleteForm = {
goodsIdList: selectedRowKeyList.value,
};
await changeLogApi.delete(data.changeLogId);
message.success('删除成功');
queryData();
} catch (e) {
smartSentry.captureError(e);
} finally {
SmartLoading.hide();
}
}
// ---------------------------- 批量删除 ----------------------------
// 选择表格行
const selectedRowKeyList = ref([]);
function onSelectChange(selectedRowKeys) {
selectedRowKeyList.value = selectedRowKeys;
}
// 批量删除
function confirmBatchDelete() {
Modal.confirm({
title: '提示',
content: '确定要批量删除这些数据吗?',
okText: '删除',
okType: 'danger',
onOk() {
requestBatchDelete();
},
cancelText: '取消',
onCancel() {},
});
}
//请求批量删除
async function requestBatchDelete() {
try {
SmartLoading.show();
await changeLogApi.batchDelete(selectedRowKeyList.value);
message.success('删除成功');
queryData();
} catch (e) {
smartSentry.captureError(e);
} finally {
SmartLoading.hide();
}
}
</script>

View File

@@ -1,44 +0,0 @@
<!--
* 系统更新日志 查看
*
* @Author: 卓大
* @Date: 2022-09-26 14:53:50
* @Copyright 1024创新实验室
-->
<template>
<a-modal title="更新日志" width="700px" :visible="visibleFlag" @close="onClose" >
<div>
<pre>{{ content }}</pre>
<div v-if="link">链接:<a :href="link" target="_blank">{{ link }}</a></div>
</div>
<template #footer>
<a-space>
<a-button type="primary" @click="onClose">关闭</a-button>
</a-space>
</template>
</a-modal>
</template>
<script setup>
import { ref } from 'vue';
const visibleFlag = ref(false);
const content = ref('');
const link = ref('');
function show(changeLog) {
content.value = changeLog.content;
link.value = changeLog.link;
visibleFlag.value = true;
}
function onClose() {
visibleFlag.value = false;
}
defineExpose({
show,
});
</script>

View File

@@ -1,178 +0,0 @@
<!--
* 代码生成 列表
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-06-08 21:50:41
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
-->
<template>
<div>
<a-form class="smart-query-form">
<a-row class="smart-query-form-row">
<a-form-item label="表名" class="smart-query-form-item">
<a-input style="width: 300px" v-model:value="queryForm.tableNameKeywords" placeholder="请输入表名关键字" />
</a-form-item>
<a-form-item class="smart-query-form-item smart-margin-left10">
<a-button type="primary" @click="ajaxQuery">
<template #icon>
<ReloadOutlined />
</template>
查询
</a-button>
<a-button @click="resetQuery">
<template #icon>
<SearchOutlined />
</template>
重置
</a-button>
</a-form-item>
</a-row>
</a-form>
<a-card size="small" :bordered="false" :hoverable="true">
<a-row justify="end">
<TableOperator class="smart-margin-bottom5" v-model="columns" :tableId="TABLE_ID_CONST.SUPPORT.CONFIG" :refresh="ajaxQuery" />
</a-row>
<a-table size="small" :loading="tableLoading" bordered :dataSource="tableData" :columns="columns" rowKey="configId" :pagination="false">
<template #bodyCell="{ record, index, column }">
<template v-if="column.dataIndex === 'seq'">
{{ index + 1 }}
</template>
<template v-if="column.dataIndex === 'action'">
<div class="smart-table-operate">
<a-button @click="showConfig(record)" type="link">代码配置</a-button>
<a-button @click="showPreview(record)" type="link">代码预览</a-button>
<a-button @click="download(record)" 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="ajaxQuery"
@showSizeChange="ajaxQuery"
:show-total="(total) => `${total}`"
/>
</div>
</a-card>
<CodeGeneratorTableConfigForm ref="codeGeneratorTableConfigFormRef" />
<CodeGeneratorPreviewModal ref="codeGeneratorPreviewModalRef" />
</div>
</template>
<script setup>
import { onMounted, reactive, ref, nextTick } from 'vue';
import { codeGeneratorApi } from '/@/api/support/code-generator/code-generator-api';
import { PAGE_SIZE_OPTIONS } from '/@/constants/common-const';
import { smartSentry } from '/@/lib/smart-sentry';
import CodeGeneratorTableConfigForm from './components/form/code-generator-table-config-form.vue';
import TableOperator from '/@/components/support/table-operator/index.vue';
import { TABLE_ID_CONST } from '/@/constants/support/table-id-const';
import CodeGeneratorPreviewModal from './components/preview/code-generator-preview-modal.vue';
const columns = ref([
{
title: '序号',
width: 50,
dataIndex: 'seq',
},
{
title: '表名',
dataIndex: 'tableName',
},
{
title: '备注',
dataIndex: 'tableComment',
ellipsis: true,
},
{
title: '代码配置',
dataIndex: 'configTime',
width: 150,
},
{
title: '表创建时间',
dataIndex: 'createTime',
width: 150,
},
{
title: '表修改时间',
dataIndex: 'updateTime',
width: 150,
},
{
title: '操作',
dataIndex: 'action',
fixed: 'right',
width: 210,
},
]);
// ---------------- 查询数据 -----------------------
const queryFormState = {
configKey: '',
pageNum: 1,
pageSize: 10,
tableNameKeywords: undefined,
};
const queryForm = reactive({ ...queryFormState });
const tableLoading = ref(false);
const tableData = ref([]);
const total = ref(0);
function resetQuery() {
Object.assign(queryForm, queryFormState);
ajaxQuery();
}
async function ajaxQuery() {
try {
tableLoading.value = true;
let responseModel = await codeGeneratorApi.queryTableList(queryForm);
const list = responseModel.data.list;
total.value = responseModel.data.total;
tableData.value = list;
} catch (e) {
smartSentry.captureError(e);
} finally {
tableLoading.value = false;
}
}
// ------------------------- 表单操作 弹窗 ------------------------------
const codeGeneratorTableConfigFormRef = ref();
function showConfig(rowData) {
codeGeneratorTableConfigFormRef.value.showModal(rowData);
}
// ------------------------- 预览 弹窗 ------------------------------
const codeGeneratorPreviewModalRef = ref();
function showPreview(rowData) {
codeGeneratorPreviewModalRef.value.showModal(rowData);
}
// ------------------------- 下载 ------------------------------
function download(rowData) {
codeGeneratorApi.downloadCode(rowData.tableName);
}
onMounted(ajaxQuery);
</script>

View File

@@ -1,171 +0,0 @@
import { convertUpperCamel } from '/@/utils/str-util';
// -------------------------------- java 类型 --------------------------------
export const JavaTypeMap = new Map();
JavaTypeMap.set('int', 'Integer');
JavaTypeMap.set('tinyint', 'Integer');
JavaTypeMap.set('smallint', 'Integer');
JavaTypeMap.set('integer', 'Integer');
JavaTypeMap.set('year', 'Integer');
JavaTypeMap.set('bigint', 'Long');
JavaTypeMap.set('float', 'BigDecimal');
JavaTypeMap.set('double', 'BigDecimal');
JavaTypeMap.set('decimal', 'BigDecimal');
JavaTypeMap.set('char', 'String');
JavaTypeMap.set('varchar', 'String');
JavaTypeMap.set('tinytext', 'String');
JavaTypeMap.set('text', 'String');
JavaTypeMap.set('longtext', 'String');
JavaTypeMap.set('blob', 'String');
JavaTypeMap.set('date', 'LocalDate');
JavaTypeMap.set('datetime', 'LocalDateTime');
export const JavaTypeList = [
'Boolean', //
'Integer', //
'Long', //
'Double', //
'String', //
'BigDecimal', //
'LocalDate', //
'LocalDateTime', //
];
export function getJavaType(dataType) {
return JavaTypeMap.get(dataType);
}
// -------------------------------- js 类型 --------------------------------
export const JsTypeMap = new Map();
JsTypeMap.set('int', 'Number');
JsTypeMap.set('tinyint', 'Number');
JsTypeMap.set('smallint', 'Number');
JsTypeMap.set('integer', 'Number');
JsTypeMap.set('year', 'Number');
JsTypeMap.set('bigint', 'Number');
JsTypeMap.set('float', 'Number');
JsTypeMap.set('double', 'Number');
JsTypeMap.set('decimal', 'Number');
JsTypeMap.set('char', 'String');
JsTypeMap.set('varchar', 'String');
JsTypeMap.set('tinytext', 'String');
JsTypeMap.set('text', 'String');
JsTypeMap.set('longtext', 'String');
JsTypeMap.set('blob', 'String');
JsTypeMap.set('date', 'Date');
JsTypeMap.set('datetime', 'Date');
export const JsTypeList = [
'Number', //
'String', //
'Date', //
'Boolean', //
'String', //
];
export function getJsType(dataType) {
return JsTypeMap.get(dataType);
}
// -------------------------------- 前端组件 --------------------------------
export const FrontComponentMap = new Map();
FrontComponentMap.set('int', 'InputNumber');
FrontComponentMap.set('tinyint', 'BooleanSelect');
FrontComponentMap.set('smallint', 'InputNumber');
FrontComponentMap.set('integer', 'InputNumber');
FrontComponentMap.set('year', 'Date');
FrontComponentMap.set('bigint', 'InputNumber');
FrontComponentMap.set('float', 'InputNumber');
FrontComponentMap.set('double', 'InputNumber');
FrontComponentMap.set('decimal', 'InputNumber');
FrontComponentMap.set('char', 'Input');
FrontComponentMap.set('varchar', 'Input');
FrontComponentMap.set('tinytext', 'Input');
FrontComponentMap.set('text', 'Textarea');
FrontComponentMap.set('longtext', 'Textarea');
FrontComponentMap.set('blob', 'Upload');
FrontComponentMap.set('date', 'Date');
FrontComponentMap.set('datetime', 'DateTime');
export function getFrontComponent(dataType) {
return FrontComponentMap.get(dataType);
}
// -------------------------------- 前端文件 --------------------------------
export const LANGUAGE_LIST = [
'js', //
'ts', //
'java', //
];
export const JS_FILE_LIST = [
'js/list.vue', //
'js/form.vue', //
'js/api.js', //
'js/const.js', //
];
export const TS_FILE_LIST = [
'ts/list.vue', //
'ts/form.vue', //
'ts/api.js', //
'ts/const.js', //
];
// -------------------------------- 后端文件 --------------------------------
export const JAVA_DOMAIN_FILE_LIST = [
'Entity.java', //
'AddForm.java', //
'UpdateForm.java', //
'QueryForm.java', //
'VO.java', //
];
export const JAVA_FILE_LIST = [
'Controller.java', //
'Service.java', //
'Manager.java', //
'Dao.java', //
'Mapper.xml', //
...JAVA_DOMAIN_FILE_LIST,
];
// -------------------------------- 枚举enum --------------------------------
export function convertJavaEnumName(moduleName, columnName) {
return moduleName + convertUpperCamel(columnName) + 'Enum';
}
/**
* 检测是否有枚举
*/
export function checkExistEnum(comment) {
if (!comment) {
return false;
}
// 检测是否存在 [ ] 或者 【 】
let leftBracketIndex = comment.indexOf('[');
if (leftBracketIndex === -1) {
leftBracketIndex = comment.indexOf('【');
}
let rightBracketIndex = comment.indexOf(']');
if (rightBracketIndex === -1) {
leftBracketIndex = comment.indexOf('】');
}
if (leftBracketIndex === -1 || rightBracketIndex === -1) {
return false;
}
if (comment.indexOf(':') === -1) {
return false;
}
return true;
}

View File

@@ -1,275 +0,0 @@
<!--
* 代码生成 配置信息
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-09-22 21:50:41
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
-->
<template>
<a-row type="flex">
<a-col flex="350px">
<a-form ref="formRef" :model="formData" :rules="formRules" :label-col="{ span: 5 }" :wrapper-col="{ span: 16 }">
<a-form-item label="表"> {{ tableInfo.tableName }} </a-form-item>
<a-form-item label="表备注"> {{ tableInfo.tableComment }} </a-form-item>
<a-form-item label="单词命名" name="moduleName">
<a-input v-model:value="formData.moduleName" placeholder="请输入 单词命名 " />
</a-form-item>
<a-form-item label="Java包名" name="javaPackageName">
<a-input v-model:value="formData.javaPackageName" placeholder="请输入 Java包名 " />
</a-form-item>
<a-form-item label="注释信息" name="description">
<a-input v-model:value="formData.description" placeholder="请输入 注释信息 " />
</a-form-item>
<a-form-item label="前端作者" name="frontAuthor">
<a-input v-model:value="formData.frontAuthor" placeholder="请输入 前端作者" />
</a-form-item>
<a-form-item label="前端时间" name="frontDate">
<a-date-picker
style="width: 100%"
show-time
format="YYYY-MM-DD HH:mm:ss"
v-model:value="formData.frontDate"
placeholder="请输入 前端时间"
/>
</a-form-item>
<a-form-item label="后端作者" name="backendAuthor">
<a-input v-model:value="formData.backendAuthor" placeholder="请输入 后端作者" />
</a-form-item>
<a-form-item label="后端时间" name="backendDate">
<a-date-picker
style="width: 100%"
show-time
format="YYYY-MM-DD HH:mm:ss"
v-model:value="formData.backendDate"
placeholder="请输入 后端时间"
/>
</a-form-item>
<a-form-item label="版权信息" name="copyright">
<a-input v-model:value="formData.copyright" placeholder="请输入 版权信息" />
</a-form-item>
</a-form>
</a-col>
<a-col flex="auto" style="height: 100vh; overflow-y: scroll">
<a-tabs v-model:activeKey="activeKey" size="small">
<a-tab-pane key="1" tab="前端文件命名">
<div class="preview-title">前端文件名</div>
<div class="preview-block">
<div v-for="item in frontNameList" :key="item">
{{ item }}
</div>
</div>
<div class="preview-title">前端Vue文件注释</div>
<div>
<pre class="preview-block">
&lt;!--
* {{ formData.description }}
*
* @Author: {{ formData.frontAuthor }}
* @Date: {{ formData.frontDate }}
* @Copyright {{ formData.copyright }}
--&gt;</pre
>
</div>
<div class="preview-title">前端Js文件注释</div>
<div>
<pre class="preview-block">
/*
* {{ formData.description }}
*
* @Author: {{ formData.frontAuthor }}
* @Date: {{ formData.frontDate }}
* @Copyright {{ formData.copyright }}
*/
</pre
>
</div>
</a-tab-pane>
<a-tab-pane key="2" tab="后端文件命名">
<div>
<div class="preview-title">后端-四层代码</div>
<div class="preview-block">
<div v-for="item in backendMvcNameList" :key="item">
{{ item }}
</div>
</div>
<div class="preview-title">JavaBean代码</div>
<div class="preview-block">
<div v-for="item in backendJavaBeanNameList" :key="item">
{{ item }}
</div>
</div>
<div class="preview-title">常量代码</div>
<div class="preview-block">
<div v-for="item in backendConstNameList" :key="item">
{{ item }}
</div>
</div>
<div class="preview-title">注释</div>
<pre class="preview-block">
/**
* {{ formData.description }}
*
* @Author: {{ formData.backendAuthor }}
* @Date: {{ formData.backendDate }}
* @Copyright {{ formData.copyright }}
*/
</pre
>
</div>
</a-tab-pane>
</a-tabs>
</a-col>
</a-row>
</template>
<script setup>
import { message } from 'ant-design-vue';
import dayjs from 'dayjs';
import lodash from 'lodash';
import { computed, inject, reactive, ref } from 'vue';
import { convertLowerHyphen, convertUpperCamel } from '/@/utils/str-util';
const tableInfo = inject('tableInfo');
const activeKey = ref('1');
// ------------- 表单 -------------
const formRef = ref();
const defaultFormData = {
moduleName: undefined, // 单词命名
javaPackageName: undefined, // java包名
description: undefined, // 注释信息
frontAuthor: undefined, // 前端作者
frontDate: undefined, // 前端时间
backendAuthor: undefined, // 后端作者
backendDate: undefined, // 后端时间
copyright: undefined, //版权
};
const formData = reactive({ ...defaultFormData });
const formRules = {
moduleName: [{ required: true, message: '请输入 单词命名' }],
javaPackageName: [{ required: true, message: '请输入 java包名' }],
frontAuthor: [{ required: true, message: '请输入 前端作者' }],
frontDate: [{ required: true, message: '请输入 前端时间' }],
backendAuthor: [{ required: true, message: '请输入 后端作者' }],
backendDate: [{ required: true, message: '请输入 后端时间' }],
copyright: [{ required: true, message: '请输入 版权' }],
};
//初始化设置数据
function setData(config) {
//基础信息
let basic = config.basic;
//命名
let removePrefixTableName = tableInfo.tableName;
if (lodash.startsWith(tableInfo.tableName, 't_')) {
removePrefixTableName = lodash.trim(removePrefixTableName, '_t');
}
formData.moduleName = basic && basic.moduleName ? basic.moduleName : removePrefixTableName;
formData.moduleName = convertUpperCamel(formData.moduleName);
//注释
formData.description = basic && basic.description ? basic.description : tableInfo.tableComment;
//时间
formData.frontDate = basic && basic.frontDate ? basic.frontDate : tableInfo.createTime;
formData.frontDate = dayjs(formData.frontDate);
formData.backendDate = basic && basic.backendDate ? basic.backendDate : tableInfo.createTime;
formData.backendDate = dayjs(formData.backendDate);
//其他字段
formData.frontAuthor = basic && basic.frontAuthor ? basic.frontAuthor : null;
formData.javaPackageName = basic && basic.javaPackageName ? basic.javaPackageName : null;
formData.backendAuthor = basic && basic.backendAuthor ? basic.backendAuthor : null;
formData.copyright = basic && basic.copyright ? basic.copyright : null;
}
// 获取表单数据
const timeFormat = 'YYYY-MM-DD HH:mm:ss';
function getBasicForm() {
return Object.assign({}, formData, {
frontDate: dayjs(formData.frontDate).format(timeFormat),
backendDate: dayjs(formData.backendDate).format(timeFormat),
});
}
// 校验表单
function validateForm() {
return new Promise((resolve, reject) => {
formRef.value
.validate()
.then(() => {
resolve(true);
})
.catch((error) => {
message.error('基础命名表单 参数验证错误!');
reject(error);
});
});
}
defineExpose({
setData,
getBasicForm,
validateForm,
});
// ------------- 预览 -------------
const frontName = computed(() => convertLowerHyphen(formData.moduleName));
const frontNameList = computed(() => {
return [
'请求:' + frontName.value + '-api.js', //
'常量:' + frontName.value + '-const.js', //
'列表:' + frontName.value + '-list.vue', //
'表单:' + frontName.value + '-form-modal.vue', //
'详情:' + frontName.value + '-detail.vue', //
];
});
const backendMvcNameList = computed(() => {
return [
'控制层:' + formData.moduleName + 'Controller.java', //
'业务层:' + formData.moduleName + 'Service.java', //
'中间层:' + formData.moduleName + 'Manager.java', //
'持久层:' + formData.moduleName + 'Dao.java', //
'SQL层 ' + formData.moduleName + 'Mapper.xml', //
];
});
const backendJavaBeanNameList = computed(() => {
return [
'实体类:' + formData.moduleName + 'Entity.java', //
'表现类:' + formData.moduleName + 'VO.java', //
'新建类:' + formData.moduleName + 'AddForm.java', //
'更新类:' + formData.moduleName + 'UpdateForm.java', //
'查询类:' + formData.moduleName + 'QueryForm.java', //
];
});
const backendConstNameList = computed(() => {
return [
//
'枚举类:' + formData.moduleName + 'Enum.java', //
'常量类:' + formData.moduleName + 'Const.java', //
];
});
</script>
<style lang="less" scoped>
.preview-title {
font-weight: 600;
margin: 5px 0px;
}
.preview-block {
font-size: 14px;
background-color: #f9f9f9;
padding: 10px 5px;
}
</style>

View File

@@ -1,128 +0,0 @@
<!--
* 代码生成 删除
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-09-22 21:50:41
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
-->
<template>
<a-form ref="formRef" :model="formData" :rules="formRules" :label-col="{ span: 6 }" style="width: 600px">
<a-form-item label="数据库表名词"> {{ tableInfo.tableName }} </a-form-item>
<a-form-item label="数据库表备注"> {{ tableInfo.tableComment }} </a-form-item>
<a-form-item label="是否允许删除" name="isSupportDelete">
<a-radio-group v-model:value="formData.isSupportDelete" button-style="solid">
<a-radio-button :value="true">支持删除</a-radio-button>
<a-radio-button :value="false">不允许删除</a-radio-button>
</a-radio-group>
</a-form-item>
<a-form-item label="是否为物理删除" name="isPhysicallyDeleted" v-if="formData.isSupportDelete">
<a-radio-group v-model:value="formData.isPhysicallyDeleted" button-style="solid">
<a-radio-button :value="true">物理删除</a-radio-button>
<a-radio-button :value="false">假删</a-radio-button>
</a-radio-group>
<div class="smart-margin-top10" v-if="!formData.isPhysicallyDeleted">
<span v-if="deleteFlagColumnName"> 假删字段为{{ deleteFlagColumnName }} </span>
<span stlye="color:red" v-else> 系统未检测出假删字段假删字段名词应该为 <strong>deleted_flag</strong> </span>
</div>
</a-form-item>
<a-form-item label="删除类型" name="deleteEnum" v-if="formData.isSupportDelete">
<SmartEnumSelect enumName="CODE_DELETE_ENUM" v-model:value="formData.deleteEnum" width="200px" />
</a-form-item>
</a-form>
</template>
<script setup>
import { message } from 'ant-design-vue';
import lodash from 'lodash';
import { inject, reactive, ref } from 'vue';
import SmartEnumSelect from '/@/components/framework/smart-enum-select/index.vue';
import { CODE_DELETE_ENUM } from '/@/constants/support/code-generator-const';
const tableInfo = inject('tableInfo');
// ------------- 表单 -------------
const deleteFlagColumnName = ref('');
const formRef = ref();
const defaultFormData = {
isSupportDelete: true, // 是否支持删除
isPhysicallyDeleted: undefined, // 是否为物理删除
deleteEnum: undefined, // 删除类型
};
const formData = reactive({ ...defaultFormData });
const formRules = {
isSupportDelete: [{ required: true, message: '请输入 isSupportDelete' }],
isPhysicallyDeleted: [{ required: true, message: '请输入 是否为物理删除' }],
deleteEnum: [{ required: true, message: '请输入 删除类型' }],
};
//初始化设置数据
function setData(tableColumns, config) {
//删除字段
let deletedFlagColumn = getDeleteFlagColumn(tableColumns);
if (deletedFlagColumn) {
deleteFlagColumnName.value = deletedFlagColumn.columnName;
}
//表单
let deleteInfo = config.delete;
formData.isSupportDelete = deleteInfo && deleteInfo.isSupportDelete ? deleteInfo.isSupportDelete : true;
formData.isPhysicallyDeleted = deleteInfo && deleteInfo.isPhysicallyDeleted ? deleteInfo.isPhysicallyDeleted : deletedFlagColumn ? false : true;
formData.deleteEnum = deleteInfo && deleteInfo.deleteEnum ? deleteInfo.deleteEnum : CODE_DELETE_ENUM.SINGLE_AND_BATCH.value;
}
// 获取配置过的字段信息
function getDeleteFlagColumn(configFields) {
if (!configFields) {
return null;
}
let result = configFields.filter((e) => lodash.startsWith(e.columnName, 'deleted_flag' || lodash.startsWith(e.columnName, 'delete_flag')));
return result && result.length > 0 ? result[0] : null;
}
// 获取表单数据
function getForm() {
return Object.assign({}, formData);
}
// 校验表单
function validateForm() {
return new Promise((resolve, reject) => {
formRef.value
.validate()
.then(() => {
resolve(true);
})
.catch((error) => {
message.error('删除表单 参数验证错误!');
reject(error);
});
});
}
defineExpose({
setData,
getForm,
validateForm,
});
</script>
<style lang="less" scoped>
.preview-title {
font-weight: 600;
margin: 5px 0px;
}
.preview-block {
font-size: 14px;
background-color: #f9f9f9;
padding: 10px 5px;
}
</style>

View File

@@ -1,222 +0,0 @@
<!--
* 代码生成 配置信息
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-09-22 21:50:41
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
-->
<template>
<a-table
:scroll="{ x: 1300 }"
size="small"
bordered
class="smart-margin-top10"
:dataSource="tableData"
:columns="columns"
rowKey="columnName"
:pagination="false"
>
<template #bodyCell="{ text, record, index, column }">
<template v-if="column.dataIndex === 'no'">
{{ index + 1 }}
</template>
<template v-if="column.dataIndex === 'columnName'">
<span>
<template v-if="record.primaryKeyFlag">
<a-tag color="#f50" style="line-height: 12px">主键</a-tag>
</template>
<template v-if="record.autoIncreaseFlag">
<a-tag color="#f50" style="line-height: 12px">自增</a-tag>
</template>
<br />
{{ text }}
</span>
</template>
<template v-if="column.dataIndex === 'nullableFlag'">
<a-tag color="error" v-if="text">非空</a-tag>
</template>
<template v-if="column.dataIndex === 'fieldName'">
<a-input v-model:value="record.fieldName" />
</template>
<template v-if="column.dataIndex === 'label'">
<a-input v-model:value="record.label" />
</template>
<template v-if="column.dataIndex === 'javaType'">
<a-select v-model:value="record.javaType" style="width: 100%">
<a-select-option v-for="item in JavaTypeList" :value="item" :key="item">{{ item }}</a-select-option>
</a-select>
</template>
<template v-if="column.dataIndex === 'jsType'">
<a-select v-model:value="record.jsType" style="width: 100%">
<a-select-option v-for="item in JsTypeList" :value="item" :key="item">{{ item }}</a-select-option>
</a-select>
</template>
<template v-if="column.dataIndex === 'dict'">
<DictKeySelect v-model:value="record.dict" />
</template>
<template v-if="column.dataIndex === 'enumName'">
<a-input v-model:value="record.enumName" />
</template>
</template>
</a-table>
</template>
<script setup>
import { inject, ref } from 'vue';
import { checkExistEnum, convertJavaEnumName, getJavaType, getJsType, JavaTypeList, JsTypeList } from '../../code-generator-util';
import DictKeySelect from '/@/components/support/dict-key-select/index.vue';
import { convertUpperCamel, convertLowerCamel } from '/@/utils/str-util';
import lodash from 'lodash';
//------------------------ 全局数据 ---------------------
const tableInfo = inject('tableInfo');
//------------------------ 表格渲染 ---------------------
const columns = ref([
{
title: '列名',
dataIndex: 'columnName',
width: 120,
ellipsis: true,
},
{
title: '列描述',
dataIndex: 'columnComment',
width: 120,
ellipsis: true,
},
{
title: '列类型',
dataIndex: 'dataType',
width: 100,
ellipsis: true,
},
{
title: '非空',
dataIndex: 'nullableFlag',
width: 60,
},
{
title: '字段命名',
dataIndex: 'fieldName',
width: 150,
},
{
title: '字段名词',
dataIndex: 'label',
width: 150,
},
{
title: 'Java类型',
dataIndex: 'javaType',
width: 150,
},
{
title: '前端类型',
dataIndex: 'jsType',
width: 130,
},
{
title: '字典',
dataIndex: 'dict',
width: 150,
},
{
title: '枚举',
dataIndex: 'enumName',
width: 150,
},
]);
const tableData = ref([]);
// ------------------- 表格数据 -------------------
//初始化设置数据
function setData(tableColumns, config) {
let fields = [];
//基础信息
let basic = config.basic;
//命名
let removePrefixTableName = tableInfo.tableName;
if (lodash.startsWith(tableInfo.tableName, 't_')) {
removePrefixTableName = lodash.trim(removePrefixTableName, '_t');
}
let moduleName = basic && basic.moduleName ? basic.moduleName : removePrefixTableName;
moduleName = convertUpperCamel(moduleName);
for (let column of tableColumns) {
let configField = getConfigField(config.fields, column.columnName);
let field = {
columnName: column.columnName,
columnComment: column.columnComment,
dataType: column.dataType,
nullableFlag: column.isNullable === 'NO' ? true : false,
primaryKeyFlag: column.columnKey === 'PRI' ? true : false,
autoIncreaseFlag: column.extra === 'auto_increment' ? true : false,
//表单
fieldName: configField ? configField.fieldName : convertLowerCamel(column.columnName),
label: configField ? configField.label : column.columnComment,
javaType: configField ? configField.javaType : getJavaType(column.dataType),
jsType: configField ? configField.jsType : getJsType(column.dataType),
dict: configField ? configField.dict : null,
enumName: configField
? configField.enumName
: checkExistEnum(column.columnComment)
? convertJavaEnumName(moduleName, column.columnName)
: null,
};
fields.push(field);
}
tableData.value = fields;
}
// 获取配置过的字段信息
function getConfigField(configFields, columnName) {
if (!configFields) {
return null;
}
let result = configFields.filter((e) => e.columnName === columnName);
return result && result.length > 0 ? result[0] : null;
}
// 获取表单数据
function getFieldsForm() {
return tableData.value.map((e) => {
return {
columnName: e.columnName,
columnComment:e.columnComment,
label: e.label,
fieldName: e.fieldName,
javaType: e.javaType,
jsType: e.jsType,
dict: e.dict,
enumName: e.enumName,
primaryKeyFlag: e.primaryKeyFlag,
autoIncreaseFlag: e.autoIncreaseFlag,
};
});
}
defineExpose({
setData,
getFieldsForm,
});
</script>
<style lang="less" scoped></style>

View File

@@ -1,293 +0,0 @@
<!--
* 代码生成 新增和更新
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-09-22 21:50:41
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
-->
<template>
<a-row class="smart-margin-top10">
<a-col flex="350px">
<a-form ref="formRef" :model="formData" style="width: 350px" :rules="formRules" :label-col="{ span: 5 }" :wrapper-col="{ span: 16 }">
<a-form-item label="是否支持" name="isSupportInsertAndUpdate">
<a-radio-group v-model:value="formData.isSupportInsertAndUpdate" button-style="solid">
<a-radio-button :value="true">支持</a-radio-button>
<a-radio-button :value="false">不支持添加修改</a-radio-button>
</a-radio-group>
</a-form-item>
<a-form-item label="页面方式" name="pageType" v-if="formData.isSupportInsertAndUpdate">
<a-radio-group v-model:value="formData.pageType" button-style="solid">
<a-radio-button :value="CODE_INSERT_AND_UPDATE_PAGE_ENUM.MODAL.value">{{CODE_INSERT_AND_UPDATE_PAGE_ENUM.MODAL.desc}}</a-radio-button>
<a-radio-button :value="CODE_INSERT_AND_UPDATE_PAGE_ENUM.DRAWER.value">{{CODE_INSERT_AND_UPDATE_PAGE_ENUM.DRAWER.desc}}</a-radio-button>
</a-radio-group>
</a-form-item>
<a-form-item label="页面宽度" v-show="formData.pageType !== CODE_INSERT_AND_UPDATE_PAGE_ENUM.PAGE.value" name="width" v-if="formData.isSupportInsertAndUpdate">
<a-input v-model:value="formData.width" placeholder="Modal或者Drawer的width属性 " />
</a-form-item>
<a-form-item label="每行数量" name="countPerLine" v-if="formData.isSupportInsertAndUpdate">
<a-input-number style="width: 100%" :max="24" v-model:value="formData.countPerLine" placeholder="请输入 每行数量 " />
</a-form-item>
</a-form>
</a-col>
<a-col flex="auto" style="height: auto; width: 500px" v-if="formData.isSupportInsertAndUpdate">
<div class="form-preview">
<a-row :gutter="20" justify="space-around">
<a-col class="form-item" :span="spanPerLine" v-for="i of formData.countPerLine" :key="i">
<div class="gutter-box">字段</div>
</a-col>
</a-row>
<a-row :gutter="20" class="smart-margin-top10" justify="space-around">
<a-col class="form-item" :span="spanPerLine" v-for="i of formData.countPerLine" :key="i">
<div class="gutter-box">字段</div>
</a-col>
</a-row>
<a-row :gutter="20" class="smart-margin-top10" justify="space-around">
<a-col class="form-item" :span="spanPerLine" v-for="i of formData.countPerLine" :key="i">
<div class="gutter-box">字段</div>
</a-col>
</a-row>
</div>
</a-col>
</a-row>
<a-table size="small" bordered class="smart-margin-top10" :dataSource="tableData" :columns="columns" rowKey="columnName" :pagination="false" v-if="formData.isSupportInsertAndUpdate">
<template #bodyCell="{ text, record, index, column }">
<template v-if="column.dataIndex === 'no'">
{{ index + 1 }}
</template>
<template v-if="column.dataIndex === 'columnName'">
<span>
<template v-if="record.primaryKeyFlag">
<a-tag color="#f50" style="line-height: 12px">主键</a-tag>
</template>
<template v-if="record.autoIncreaseFlag">
<a-tag color="#f50" style="line-height: 12px">自增</a-tag>
</template>
<br />
{{ text }}
</span>
</template>
<template v-if="column.dataIndex === 'nullableFlag'">
<a-tag color="error" v-if="text">非空</a-tag>
</template>
<template v-if="column.dataIndex === 'required'">
<a-checkbox v-model:checked="record.requiredFlag" />
</template>
<template v-if="column.dataIndex === 'insertFlag'">
<a-checkbox v-model:checked="record.insertFlag" />
</template>
<template v-if="column.dataIndex === 'updateFlag'">
<a-checkbox v-model:checked="record.updateFlag" />
</template>
<template v-if="column.dataIndex === 'frontComponent'">
<SmartEnumSelect width="100%" enum-name="CODE_FRONT_COMPONENT_ENUM" v-model:value="record.frontComponent" />
</template>
</template>
</a-table>
</template>
<script setup>
import { inject, ref, reactive, computed } from 'vue';
import { checkExistEnum, getFrontComponent } from '../../code-generator-util';
import SmartEnumRadio from '/@/components/framework/smart-enum-radio/index.vue';
import { CODE_INSERT_AND_UPDATE_PAGE_ENUM } from '/@/constants/support/code-generator-const';
import SmartEnumSelect from '/@/components/framework/smart-enum-select/index.vue';
import { message } from 'ant-design-vue';
import { CODE_FRONT_COMPONENT_ENUM } from '/@/constants/support/code-generator-const';
//------------------------ 全局数据 ---------------------
const tableInfo = inject('tableInfo');
// ------------- 表单 -------------
const formRef = ref();
const defaultFormData = {
isSupportInsertAndUpdate: true, // 是否允许增加、删除
pageType: CODE_INSERT_AND_UPDATE_PAGE_ENUM.MODAL.value, // 类型
width: undefined, // 宽度
countPerLine: 1, // 每行数量
};
const formData = reactive({ ...defaultFormData });
const formRules = {
isSupportInsertAndUpdate: [{ required: true, message: '请输入 是否允许增加、删除' }],
pageType: [{ required: true, message: '请输入 页面方式' }],
width: [{ required: true, message: '请输入 宽度' }],
countPerLine: [{ required: true, message: '请输入 每行数量' }],
};
// ------------- 预览 -------------
const spanPerLine = computed(() => {
return parseInt(20 / formData.countPerLine);
});
//------------------------ 表格渲染 ---------------------
const columns = ref([
{
title: '列名',
dataIndex: 'columnName',
width: 120,
ellipsis: true,
},
{
title: '列描述',
dataIndex: 'columnComment',
width: 120,
ellipsis: true,
},
{
title: '列类型',
dataIndex: 'dataType',
width: 100,
ellipsis: true,
},
{
title: '非空',
dataIndex: 'nullableFlag',
width: 60,
},
{
title: '必填',
dataIndex: 'required',
width: 35,
},
{
title: '新增',
dataIndex: 'insertFlag',
width: 35,
},
{
title: '更新',
dataIndex: 'updateFlag',
width: 35,
},
{
title: '前端组件',
dataIndex: 'frontComponent',
width: 100,
},
]);
const tableData = ref([]);
// ------------------- 表格数据 -------------------
//初始化设置数据
function setData(tableColumns, config) {
//------------- 更新基础信息 -----------------
if (config.insertAndUpdate) {
formData.isSupportInsertAndUpdate = config.insertAndUpdate.isSupportInsertAndUpdate ? config.insertAndUpdate.isSupportInsertAndUpdate : true;
formData.pageType = config.insertAndUpdate.pageType;
formData.width = config.insertAndUpdate.width;
formData.countPerLine = config.insertAndUpdate.countPerLine;
}
//------------- 更新字段信息 -----------------
let insertAndUpdateFields = config.insertAndUpdate && config.insertAndUpdate.fieldList ? config.insertAndUpdate.fieldList : null;
let fields = [];
for (let column of tableColumns) {
let configField = getConfigField(insertAndUpdateFields, column.columnName);
let field = {
columnName: column.columnName,
columnComment: column.columnComment,
dataType: column.dataType,
nullableFlag: column.isNullable === 'NO' ? true : false,
primaryKeyFlag: column.columnKey === 'PRI' ? true : false,
autoIncreaseFlag: column.extra === 'auto_increment' ? true : false,
};
//表单
field.requiredFlag = configField ? configField.requiredFlag : field.nullableFlag;
field.insertFlag = configField ? configField.insertFlag : field.nullableFlag;
field.updateFlag = configField ? configField.updateFlag : false;
if (configField && configField.frontComponent) {
field.frontComponent = configField.frontComponent;
} else {
field.frontComponent = checkExistEnum(column.columnComment)
? CODE_FRONT_COMPONENT_ENUM.ENUM_SELECT.value
: getFrontComponent(column.dataType);
}
fields.push(field);
}
tableData.value = fields;
}
// 获取配置过的字段信息
function getConfigField(configFields, columnName) {
if (!configFields) {
return null;
}
let result = configFields.filter((e) => e.columnName === columnName);
return result && result.length > 0 ? result[0] : null;
}
// 获取表单数据
function getFieldsForm() {
let fieldList = tableData.value.map((e) => {
return {
columnName: e.columnName,
requiredFlag: e.requiredFlag,
insertFlag: e.insertFlag,
updateFlag: e.updateFlag,
frontComponent: e.frontComponent,
};
});
return {
isSupportInsertAndUpdate: formData.isSupportInsertAndUpdate,
pageType: formData.pageType,
width: formData.width,
countPerLine: formData.countPerLine,
fieldList,
};
}
// 校验表单
function validateForm() {
return new Promise((resolve, reject) => {
formRef.value
.validate()
.then(() => {
resolve(true);
})
.catch((error) => {
message.error('基础命名表单 参数验证错误!');
reject(error);
});
});
}
defineExpose({
setData,
getFieldsForm,
validateForm,
});
</script>
<style scoped lang="less">
.form-preview {
background-color: #efefef;
padding: 15px 20px;
border: 0;
}
.form-item {
background: #00a0e9;
padding: 5px 0;
text-align: center;
color: white;
}
</style>

View File

@@ -1,257 +0,0 @@
<!--
* 代码生成 查询条件
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-09-22 21:50:41
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
-->
<template>
<a-row>
<a-button type="primary" @click="addQuery">添加查询条件</a-button>
</a-row>
<a-table
size="small"
bordered
id="smartCodeQueryFieldsTable"
class="smart-margin-top10"
:dataSource="tableData"
row-class-name="column-row"
:columns="columns"
rowKey="rowKey"
:pagination="false"
>
<template #bodyCell="{ text, record, index, column }">
<template v-if="column.dataIndex === 'drag'">
<a-button type="text" class="handle" size="small" style="width: 100%; text-align: left">
<template #icon> <drag-outlined /> </template>
</a-button>
</template>
<template v-if="column.dataIndex === 'label'">
<a-input v-model:value="record.label" placeholder="关键字查询"/>
</template>
<template v-if="column.dataIndex === 'fieldName'">
<a-input v-model:value="record.fieldName" placeholder="keywords"/>
</template>
<template v-if="column.dataIndex === 'width'">
<a-input v-model:value="record.width" placeholder="150px"/>
</template>
<template v-if="column.dataIndex === 'queryTypeEnum'">
<SmartEnumSelect
@change="(value) => onChangeQueryType(value, record)"
enumName="CODE_QUERY_FIELD_QUERY_TYPE_ENUM"
v-model:value="record.queryTypeEnum"
width="100%"
/>
</template>
<template v-if="column.dataIndex === 'columnNameList'">
<a-select
show-search
:mode="record.queryTypeEnum && record.queryTypeEnum === CODE_QUERY_FIELD_QUERY_TYPE_ENUM.LIKE.value ? 'multiple' : ''"
v-model:value="record.columnNameList"
@change="onSelectColumn(record)"
style="width: 100%"
>
<a-select-option v-for="item in tableColumns" :value="item.columnName" :key="item.columnName">
{{ item.columnName }}
<span v-show="item.columnComment"> {{ item.columnComment }} </span>
</a-select-option>
</a-select>
</template>
<template v-if="column.dataIndex === 'operate'">
<div class="smart-table-operate">
<a-button type="link" @click="onDelete(index)" danger>删除</a-button>
</div>
</template>
</template>
</a-table>
</template>
<script setup>
import lodash from 'lodash';
import Sortable from 'sortablejs';
import { inject, nextTick, ref } from 'vue';
import SmartEnumSelect from '/@/components/framework/smart-enum-select/index.vue';
import { SmartLoading } from '/@/components/framework/smart-loading';
import { CODE_QUERY_FIELD_QUERY_TYPE_ENUM } from '/@/constants/support/code-generator-const';
import { convertLowerCamel } from '/@/utils/str-util';
//------------------------ 全局数据 ---------------------
const tableInfo = inject('tableInfo');
//------------------------ 表格渲染 ---------------------
const columns = ref([
{
title: '拖拽',
dataIndex: 'drag',
width: 60,
},
{
title: '列',
dataIndex: 'columnNameList',
},
{
title: '查询类型',
dataIndex: 'queryTypeEnum',
width: 130,
},
{
title: '条件名称',
dataIndex: 'label',
width: 150,
},
{
title: '字段命名',
dataIndex: 'fieldName',
width: 150,
},
{
title: '宽度',
dataIndex: 'width',
width: 150,
},
{
title: '操作',
dataIndex: 'operate',
width: 60,
},
]);
const tableData = ref([]);
const tableColumns = ref([]);
//初始化设置数据
function setData(tableColumnInfos, config) {
rowKeyCounter = 1;
let data = config && config.queryFields ? config.queryFields : [];
for (let index = 0; index < data.length; index++) {
data[index].rowKey = 'rowKey' + (index + 1);
rowKeyCounter++;
}
tableData.value = data;
tableColumns.value = tableColumnInfos;
nextTick(() => {
initDrag();
});
}
// ------------------- 增加、删除 -------------------
let rowKeyCounter = 1;
function addQuery() {
tableData.value.push({
rowKey: 'rowKey' + rowKeyCounter,
label: '',
fieldName: '',
queryTypeEnum: '',
columnNameList: null,
width: '',
});
rowKeyCounter++;
}
function onDelete(index) {
lodash.pullAt(tableData.value, index);
}
//初始化拖拽
function initDrag() {
let tbody = document.querySelector('#smartCodeQueryFieldsTable tbody');
Sortable.create(tbody, {
animation: 300,
dragClass: 'smart-ghost-class', //设置拖拽样式类名
ghostClass: 'smart-ghost-class', //设置拖拽停靠样式类名
chosenClass: 'smart-ghost-class', //设置选中样式类名
handle: '.handle',
});
}
// ------------------- 监听数据变化 -------------------
function onChangeQueryType(queryType, record) {
if (queryType === CODE_QUERY_FIELD_QUERY_TYPE_ENUM.LIKE.value) {
record.columnNameList = [];
} else {
record.columnNameList = null;
}
}
function onSelectColumn(record) {
if (Array.isArray(record.columnNameList)) {
return;
}
let columnName = record.columnNameList;
let column = getConfigField(tableColumns.value, columnName);
//表单
if (!record.fieldName) {
record.fieldName = column && column.columnName ? convertLowerCamel(column.columnName) : '';
}
if (!record.label) {
record.label = column && column.columnComment ? convertLowerCamel(column.columnComment) : '';
}
}
// 获取配置过的字段信息
function getConfigField(configFields, columnName) {
if (!configFields) {
return null;
}
let result = configFields.filter((e) => e.columnName === columnName);
return result && result.length > 0 ? result[0] : null;
}
// ------------------- 获取表单数据 -------------------
// 获取表单数据
function getFieldsForm() {
let result = [];
let trList = document.querySelectorAll('#smartCodeQueryFieldsTable tbody .column-row');
if (trList && trList.length === 0) {
return result;
}
for (let tr of trList) {
let rowKey = tr.getAttribute('data-row-key');
if (!rowKey) {
continue;
}
if (rowKey && rowKey.indexOf('rowKey') === -1) {
continue;
}
let index = parseInt(rowKey.substring(6));
let tableItem = tableData.value[index - 1];
let obj = {
queryTypeEnum: tableItem.queryTypeEnum,
label: tableItem.label,
fieldName: tableItem.fieldName,
columnNameList: tableItem.columnNameList,
width: tableItem.width,
};
// 字符串转为数组
if (obj.columnNameList && typeof obj.columnNameList === 'string') {
obj.columnNameList = [obj.columnNameList];
}
result.push(obj);
}
return result;
}
defineExpose({
setData,
getFieldsForm,
});
</script>
<style lang="less" scoped></style>

View File

@@ -1,150 +0,0 @@
<!--
* 代码生成 列表
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-09-22 21:50:41
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
-->
<template>
<a-table size="small" bordered :scroll="{ x: 1000 }" class="smart-margin-top10" :dataSource="tableData" :columns="columns" rowKey="columnName" :pagination="false">
<template #bodyCell="{ text, record, index, column }">
<template v-if="column.dataIndex === 'no'">
{{ index + 1 }}
</template>
<template v-if="column.dataIndex === 'showFlag'">
<a-checkbox v-model:checked="record.showFlag" />
</template>
<template v-if="column.dataIndex === 'fieldName'">
<a-input v-model:value="record.fieldName" />
</template>
<template v-if="column.dataIndex === 'label'">
<a-input v-model:value="record.label" />
</template>
<template v-if="column.dataIndex === 'width'">
<a-input-number v-model:value="record.width" />
</template>
<template v-if="column.dataIndex === 'ellipsisFlag'">
<a-switch v-model:checked="record.ellipsisFlag" checked-children="自动省略" un-checked-children="换行显示" />
</template>
</template>
</a-table>
</template>
<script setup>
import { inject, ref } from 'vue';
import { convertLowerCamel } from '/@/utils/str-util';
//------------------------ 全局数据 ---------------------
const tableInfo = inject('tableInfo');
//------------------------ 表格渲染 ---------------------
const columns = ref([
{
title: '序号',
dataIndex: 'no',
width: 60,
},
{
title: '列名',
dataIndex: 'columnName',
width: 120,
ellipsis: true,
},
{
title: '列描述',
dataIndex: 'columnComment',
width: 120,
ellipsis: true,
},
{
title: '显示',
dataIndex: 'showFlag',
width: 50,
},
{
title: '字段名词',
dataIndex: 'label',
width: 120,
},
{
title: '字段命名',
dataIndex: 'fieldName',
width: 120,
},
{
title: '宽度',
dataIndex: 'width',
width: 80,
},
{
title: 'ellipsis',
dataIndex: 'ellipsisFlag',
width: 100,
},
]);
const tableData = ref([]);
// ------------------- 表格数据 -------------------
//初始化设置数据
function setData(tableColumns, config) {
let fields = [];
for (let column of tableColumns) {
let configField = getConfigField(config.tableFields, column.columnName);
let field = {
columnName: column.columnName,
columnComment: column.columnComment,
dataType: column.dataType,
//表单
showFlag: configField ? configField.showFlag : true,
label: configField ? configField.label : column.columnComment,
fieldName: configField ? configField.fieldName : convertLowerCamel(column.columnName),
width: configField ? configField.width : null,
ellipsisFlag: configField ? configField.ellipsisFlag : true,
};
fields.push(field);
}
tableData.value = fields;
}
// 获取配置过的字段信息
function getConfigField(configFields, columnName) {
if (!configFields) {
return null;
}
let result = configFields.filter((e) => e.columnName === columnName);
return result && result.length > 0 ? result[0] : null;
}
// 获取表单数据
function getTaleFieldsForm() {
return tableData.value.map((e) => {
return {
columnName: e.columnName,
label: e.label,
fieldName: e.fieldName,
showFlag: e.showFlag,
width: e.width,
ellipsisFlag: e.ellipsisFlag,
};
});
}
defineExpose({
setData,
getTaleFieldsForm,
});
</script>

View File

@@ -1,225 +0,0 @@
<!--
* 代码生成 配置信息
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-09-22 21:50:41
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
-->
<template>
<a-drawer
title="代码配置"
style=""
:visible="visibleFlag"
:width="1000"
:footerStyle="{ textAlign: 'right' }"
@close="onClose"
:maskClosable="false"
:destroyOnClose="true"
>
<a-tabs v-model:activeKey="activeKey">
<a-tab-pane key="1" :forceRender="true">
<template #tab>
<span>
<info-circle-outlined />
1.基础命名
</span>
</template>
<CodeGeneratorTableConfigFormBasic ref="basicRef" />
</a-tab-pane>
<a-tab-pane key="2" :forceRender="true">
<template #tab>
<span>
<unordered-list-outlined />
2.字段列表
</span>
</template>
<CodeGeneratorTableConfigFormField ref="fieldRef" />
</a-tab-pane>
<a-tab-pane key="3" :forceRender="true">
<template #tab>
<span>
<save-outlined />
3.增加修改
</span>
</template>
<CodeGeneratorTableConfigFormInsertAndUpdate ref="insertAndUpdateRef" />
</a-tab-pane>
<a-tab-pane key="4" :forceRender="true">
<template #tab>
<span>
<delete-outlined />
4.删除
</span>
</template>
<CodeGeneratorTableConfigFormDelete ref="deleteRef" />
</a-tab-pane>
<a-tab-pane key="5" :forceRender="true">
<template #tab>
<span>
<file-search-outlined />
5查询条件
</span>
</template>
<CodeGeneratorTableConfigFormQueryField ref="queryRef" />
</a-tab-pane>
<a-tab-pane key="6" :forceRender="true">
<template #tab>
<span>
<table-outlined />
6列表
</span>
</template>
<CodeGeneratorTableConfigFormTableField ref="tableFieldRef" />
</a-tab-pane>
</a-tabs>
<template #footer>
<a-space>
<a-button @click="onClose">取消</a-button>
<a-button type="primary" @click="save">保存</a-button>
</a-space>
</template>
</a-drawer>
</template>
<script setup>
import { reactive, ref, provide, nextTick } from 'vue';
import { SmartLoading } from '/@/components/framework/smart-loading';
import { smartSentry } from '/@/lib/smart-sentry';
import CodeGeneratorTableConfigFormBasic from './code-generator-table-config-form-basic.vue';
import { codeGeneratorApi } from '/@/api/support/code-generator/code-generator-api';
import CodeGeneratorTableConfigFormField from './code-generator-table-config-form-field.vue';
import CodeGeneratorTableConfigFormInsertAndUpdate from './code-generator-table-config-form-insert-and-update.vue';
import CodeGeneratorTableConfigFormDelete from './code-generator-table-config-form-delete.vue';
import CodeGeneratorTableConfigFormQueryField from './code-generator-table-config-form-query-field.vue';
import CodeGeneratorTableConfigFormTableField from './code-generator-table-config-form-table-field.vue';
import { message } from 'ant-design-vue';
// ------------------ 显示,关闭 ------------------
// 显示
const visibleFlag = ref(false);
function showModal(table) {
Object.assign(tableInfo, table);
activeKey.value = '1';
visibleFlag.value = true;
nextTick(() => {
getTableColumns();
});
}
// 关闭
function onClose() {
visibleFlag.value = false;
}
// ------------------ 组件------------------
const basicRef = ref();
const fieldRef = ref();
const insertAndUpdateRef = ref();
const deleteRef = ref();
const queryRef = ref();
const tableFieldRef = ref();
// ------------------ 表的列信息 、配置信息------------------
const tableColumns = ref([]);
const tableConfig = ref({});
// 查询表的列
async function getTableColumns() {
try {
SmartLoading.show();
let columnResult = await codeGeneratorApi.getTableColumns(tableInfo.tableName);
tableColumns.value = columnResult.data;
let configResult = await codeGeneratorApi.getConfig(tableInfo.tableName);
tableConfig.value = configResult.data;
//基础命名
basicRef.value.setData(tableConfig.value);
//字段列表
fieldRef.value.setData(tableColumns.value, tableConfig.value);
//新增和修改
insertAndUpdateRef.value.setData(tableColumns.value, tableConfig.value);
//删除
deleteRef.value.setData(tableColumns.value, tableConfig.value);
//查询
queryRef.value.setData(tableColumns.value, tableConfig.value);
//表格列表
tableFieldRef.value.setData(tableColumns.value, tableConfig.value);
} catch (e) {
smartSentry.captureError(e);
} finally {
SmartLoading.hide();
}
}
// ------------------ 表信息 ------------------
const tableInfo = reactive({
tableName: '', //表名
tableComment: '', //备注
createTime: '', //表创建时间
updateTime: '', //表修改时间
});
// ------------------ 标签页 ------------------
const activeKey = ref('1');
// ------------------ 提交表单 ------------------
const emits = defineEmits(['reloadList']);
async function save() {
try {
let basicValidated = await basicRef.value.validateForm();
let insertAndUpdateValidated = await insertAndUpdateRef.value.validateForm();
let deleteValidated = await deleteRef.value.validateForm();
if (!basicValidated || !insertAndUpdateValidated || !deleteValidated ) {
return;
}
let fields = fieldRef.value.getFieldsForm();
let basic = basicRef.value.getBasicForm();
let insertAndUpdate = insertAndUpdateRef.value.getFieldsForm();
let deleteInfo = deleteRef.value.getForm();
let queryFields = queryRef.value.getFieldsForm();
let tableFields = tableFieldRef.value.getTaleFieldsForm();
await codeGeneratorApi.updateConfig({
tableName: tableInfo.tableName,
basic,
fields,
insertAndUpdate,
deleteInfo: deleteInfo,
queryFields,
tableFields,
});
message.success('保存成功');
emits('reloadList');
onClose();
} catch (e) {
smartSentry.captureError(e);
}
}
defineExpose({
showModal,
});
// ------------------ provide ------------------
provide('tableInfo', tableInfo);
provide('tableColumns', tableColumns);
provide('tableConfig', tableConfig);
</script>
<style lang="less" scoped>
.visible-list {
display: flex;
flex-wrap: wrap;
.visible-item {
padding-top: 8px;
}
}
</style>

View File

@@ -1,202 +0,0 @@
<!--
* 代码生成 预览代码
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-09-22 21:50:41
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
-->
<template>
<a-drawer
title="代码预览"
:visible="visibleFlag"
:width="1200"
:footerStyle="{ textAlign: 'right' }"
:bodyStyle="{ padding: '8px 24px' }"
@close="onClose"
:maskClosable="false"
:destroyOnClose="true"
>
<a-row justify="space-between" class="smart-margin-bottom10">
<a-radio-group v-model:value="languageType" button-style="solid" @change="onChangeLanguageType">
<a-radio-button :value="LANGUAGE_LIST[0]">JavaScript代码</a-radio-button>
<a-radio-button :value="LANGUAGE_LIST[1]">TypeScript代码</a-radio-button>
<a-radio-button :value="LANGUAGE_LIST[2]">Java代码</a-radio-button>
</a-radio-group>
<a-button type="link" @click="download" danger size="small"><strong>下载代码</strong></a-button>
</a-row>
<a-tabs v-model:activeKey="fileKey" size="small" @change="onChangeTab">
<a-tab-pane v-for="item in tabList" :key="item" :tab="item" />
</a-tabs>
<div>
<pre><code :class="codeClass">{{resultCode}}</code></pre>
</div>
</a-drawer>
</template>
<script setup>
import { computed, nextTick, ref, watch } from 'vue';
import { codeGeneratorApi } from '/@/api/support/code-generator/code-generator-api';
import { JAVA_FILE_LIST, LANGUAGE_LIST, JS_FILE_LIST,TS_FILE_LIST, JAVA_DOMAIN_FILE_LIST } from '../../code-generator-util';
import { smartSentry } from '/@/lib/smart-sentry';
import { lineNumbersBlock } from '/@/lib/highlight-line-number';
import hljs from 'highlight.js';
import 'highlight.js/styles/github-dark.css';
import javascript from 'highlight.js/lib/languages/javascript';
import typescript from 'highlight.js/lib/languages/typescript';
import java from 'highlight.js/lib/languages/java';
import { message } from 'ant-design-vue';
// ------------------ 显示,关闭 ------------------
// 显示
const visibleFlag = ref(false);
function showModal(tableInfo) {
tableName.value = tableInfo.tableName;
tableComment.value = tableInfo.tableComment;
visibleFlag.value = true;
nextTick(() => {
onChangeTab(fileKey.value);
});
}
// 关闭
function onClose() {
visibleFlag.value = false;
}
// ------------------ 表------------------
const tableName = ref('');
const tableComment = ref('');
// ------------------ 标签页 ------------------
const languageType = ref(LANGUAGE_LIST[0]);
const tabList = computed(() => {
if(languageType.value === LANGUAGE_LIST[0]){
return JS_FILE_LIST;
}else if(languageType.value === LANGUAGE_LIST[1]){
return TS_FILE_LIST;
}else{
return JAVA_FILE_LIST;
}
});
const fileKey = ref(tabList.value[0]);
function getLanguage() {
if(languageType.value === LANGUAGE_LIST[0]){
return 'javascript';
}else if(languageType.value === LANGUAGE_LIST[1]){
return 'typescript';
}else{
return 'java';
}
}
function onChangeLanguageType(e){
if(e.target.value === LANGUAGE_LIST[0]){
fileKey.value = JS_FILE_LIST[0];
}else if(e.target.value === LANGUAGE_LIST[1]){
fileKey.value = TS_FILE_LIST[0];
}else{
fileKey.value = JAVA_FILE_LIST[0];
}
onChangeTab(fileKey.value);
}
// ------------------ 下载代码 ------------------
function download(rowData) {
codeGeneratorApi.downloadCode(tableName.value);
}
// ------------------ 查询代码 ------------------
const codeClass = ref('language-javascript');
function onChangeTab(tab) {
let templateFile = tab;
let language = getLanguage();
hljs.registerLanguage(language, language == 'java' ? java : javascript);
codeClass.value = 'language-' + language;
console.log(templateFile);
nextTick(() => {
generateCode(templateFile, tableName.value);
});
}
const resultCode = ref('');
async function generateCode(templateFile, tableName) {
try {
let result = await codeGeneratorApi.preview({
templateFile,
tableName,
});
resultCode.value = result.data;
nextTick(() => {
document.querySelectorAll('pre code').forEach((block) => {
block.setAttribute('highlighted', 'true');
hljs.highlightElement(block);
lineNumbersBlock(block);
block.innerHTML =
"<div><div style='padding: 5px 0px 10px 20px;float:right'><span style='margin-right: 10px;padding: 5px;border: white solid 1px;color:white;border-radius: 2px'>" +
block.className.match(/(?<=language-).*(?= hljs)/).toString() +
"</span><button class='ant-btn ant-btn-sm' >复制代码</button></div>" +
block.innerHTML +
'</div>';
let copyButton = block.querySelector('button');
if (copyButton != null) {
copyButton.onclick = function () {
copy(resultCode.value);
message.success('复制成功!');
};
}
});
});
} catch (e) {
smartSentry.captureError(e);
}
}
function copy(value) {
let textarea = document.createElement('textarea');
document.body.appendChild(textarea);
textarea.value = value;
textarea.select();
document.execCommand('Copy');
document.body.removeChild(textarea);
}
defineExpose({
showModal,
});
</script>
<style lang="less" scoped>
.preview-title {
font-weight: 600;
margin: 5px 0px;
}
.preview-block {
font-size: 12px;
background-color: #f9f9f9;
padding: 10px 5px;
}
:deep(.hljs) {
.hljs-ln-numbers {
text-align: center;
color: #ccc;
border-right: 1px solid #ccc;
vertical-align: top;
padding-right: 5px !important;
}
.hljs-ln-code {
padding-left: 5px !important;
}
}
</style>

View File

@@ -1,99 +0,0 @@
<!--
* 系统设置表单
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-06-08 21:50:41
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
-->
<template>
<a-modal :visible="visible" :title="form.configId ? '编辑' : '添加'" ok-text="确认" cancel-text="取消" @ok="onSubmit" @cancel="onClose">
<a-form ref="formRef" :model="form" :rules="rules" :label-col="{ span: 5 }">
<a-form-item label="参数Key" name="configKey">
<a-input v-model:value="form.configKey" placeholder="请输入参数Key" />
</a-form-item>
<a-form-item label="参数名称" name="configName">
<a-input v-model:value="form.configName" placeholder="请输入参数名称" />
</a-form-item>
<a-form-item label="参数值" name="configValue">
<a-input v-model:value="form.configValue" placeholder="请输入参数值" />
</a-form-item>
<a-form-item label="备注" name="remark">
<textarea v-model="form.remark" style="width: 100%; height: 100px; outline: none"></textarea>
</a-form-item>
</a-form>
</a-modal>
</template>
<script setup>
import { message } from 'ant-design-vue';
import { reactive, ref } from 'vue';
import { configApi } from '/@/api/support/config/config-api';
import { smartSentry } from '/@/lib/smart-sentry';
import { SmartLoading } from '/@/components/framework/smart-loading';
// emit
const emit = defineEmits('reloadList');
// 组件
const formRef = ref();
const formDefault = {
configId: undefined,
configKey: '',
configName: '',
configValue: '',
remark: '',
};
let form = reactive({ ...formDefault });
const rules = {
configKey: [{ required: true, message: '请输入参数key' }],
configName: [{ required: true, message: '请输入参数名称' }],
configValue: [{ required: true, message: '请输入参数值' }],
};
// 是否展示
const visible = ref(false);
function showModal(rowData) {
Object.assign(form, formDefault);
if (rowData) {
Object.assign(form, rowData);
}
visible.value = true;
}
function onClose() {
Object.assign(form, formDefault);
visible.value = false;
}
function onSubmit() {
formRef.value
.validate()
.then(async () => {
SmartLoading.show();
try {
if (form.configId) {
await configApi.updateConfig(form);
} else {
await configApi.addConfig(form);
}
message.success(`${form.configId ? '修改' : '添加'}成功`);
emit('reloadList');
onClose();
} catch (error) {
smartSentry.captureError(error);
} finally {
SmartLoading.hide();
}
})
.catch((error) => {
console.log('error', error);
message.error('参数验证错误,请仔细填写表单数据!');
});
}
defineExpose({
showModal,
});
</script>

View File

@@ -1,170 +0,0 @@
<!--
* 系统设置 列表
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-06-08 21:50:41
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
-->
<template>
<div>
<a-form class="smart-query-form">
<a-row class="smart-query-form-row">
<a-form-item label="参数Key" class="smart-query-form-item">
<a-input style="width: 300px" v-model:value="queryForm.configKey" placeholder="请输入key" />
</a-form-item>
<a-form-item class="smart-query-form-item smart-margin-left10">
<a-button type="primary" @click="ajaxQuery" v-privilege="'support:config:query'">
<template #icon>
<ReloadOutlined />
</template>
查询
</a-button>
<a-button @click="resetQuery" v-privilege="'support:config:query'">
<template #icon>
<SearchOutlined />
</template>
重置
</a-button>
<a-button @click="toEditOrAdd()" v-privilege="'support:config:add'" type="primary" class="smart-margin-left20">
<template #icon>
<PlusOutlined />
</template>
新建
</a-button>
</a-form-item>
</a-row>
</a-form>
<a-card size="small" :bordered="false" :hoverable="true">
<a-row justify="end" >
<TableOperator class="smart-margin-bottom5" v-model="columns" :tableId="TABLE_ID_CONST.SUPPORT.CONFIG" :refresh="ajaxQuery" />
</a-row>
<a-table size="small" :loading="tableLoading" bordered :dataSource="tableData" :columns="columns" rowKey="configId" :pagination="false">
<template #bodyCell="{ record, column }">
<template v-if="column.dataIndex === 'action'">
<div class="smart-table-operate">
<a-button @click="toEditOrAdd(record)" v-privilege="'support:config:update'" 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="ajaxQuery"
@showSizeChange="ajaxQuery"
:show-total="(total) => `${total}`"
/>
</div>
</a-card>
<ConfigFormModal ref="configFormModal" @reloadList="resetQuery" />
</div>
</template>
<script setup>
import { onMounted, reactive, ref } from 'vue';
import { configApi } from '/@/api/support/config/config-api';
import ConfigFormModal from './config-form-modal.vue';
import { PAGE_SIZE_OPTIONS } from '/@/constants/common-const';
import { smartSentry } from '/@/lib/smart-sentry';
import TableOperator from '/@/components/support/table-operator/index.vue';
import { TABLE_ID_CONST } from '/@/constants/support/table-id-const';
const columns = ref([
{
title: 'id',
width: 50,
dataIndex: 'configId',
},
{
title: '参数key',
dataIndex: 'configKey',
ellipsis: true,
},
{
title: '参数名称',
dataIndex: 'configName',
ellipsis: true,
},
{
title: '参数值',
dataIndex: 'configValue',
ellipsis: true,
},
{
title: '备注',
dataIndex: 'remark',
ellipsis: true,
width: 150,
},
{
title: '创建时间',
dataIndex: 'createTime',
width: 150,
},
{
title: '修改时间',
dataIndex: 'updateTime',
width: 150,
},
{
title: '操作',
dataIndex: 'action',
fixed: 'right',
width: 60,
},
]);
// ---------------- 查询数据 -----------------------
const queryFormState = {
configKey: '',
pageNum: 1,
pageSize: 10,
};
const queryForm = reactive({ ...queryFormState });
const tableLoading = ref(false);
const tableData = ref([]);
const total = ref(0);
function resetQuery() {
Object.assign(queryForm, queryFormState);
ajaxQuery();
}
async function ajaxQuery() {
try {
tableLoading.value = true;
let responseModel = await configApi.queryList(queryForm);
const list = responseModel.data.list;
total.value = responseModel.data.total;
tableData.value = list;
} catch (e) {
smartSentry.captureError(e);
} finally {
tableLoading.value = false;
}
}
// ------------------------- 表单操作 弹窗 ------------------------------
const configFormModal = ref();
function toEditOrAdd(rowData) {
configFormModal.value.showModal(rowData);
}
onMounted(ajaxQuery);
</script>

View File

@@ -1,95 +0,0 @@
<!--
* 字典key 弹窗
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-06-08 21:50:41
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
-->
<template>
<a-modal :visible="visible" :title="form.dictKeyId ? '编辑' : '添加'" ok-text="确认" cancel-text="取消" @ok="onSubmit" @cancel="onClose">
<a-form ref="formRef" :model="form" :rules="rules" :label-col="{ span: 5 }" :wrapper-col="{ span: 12 }">
<a-form-item label="编码" name="keyCode">
<a-input v-model:value="form.keyCode" placeholder="请输入编码" />
</a-form-item>
<a-form-item label="名称" name="keyName">
<a-input v-model:value="form.keyName" placeholder="请输入名称" />
</a-form-item>
<a-form-item label="备注" name="remark">
<textarea v-model="form.remark" style="width: 100%; height: 100px; outline: none"></textarea>
</a-form-item>
</a-form>
</a-modal>
</template>
<script setup>
import { ref, reactive } from 'vue';
import { message } from 'ant-design-vue';
import { SmartLoading } from '/@/components/framework/smart-loading';
import { dictApi } from '/@/api/support/dict/dict-api';
import { smartSentry } from '/@/lib/smart-sentry';
// emit
const emit = defineEmits(['reloadList']);
// 组件
const formRef = ref();
const formDefault = {
dictKeyId: undefined,
keyCode: '',
keyName: '',
remark: '',
};
let form = reactive({ ...formDefault });
const rules = {
keyCode: [{ required: true, message: '请输入编码' }],
keyName: [{ required: true, message: '请输入名称' }],
};
// 是否展示
const visible = ref(false);
function showModal(rowData) {
Object.assign(form, formDefault);
if (rowData) {
Object.assign(form, rowData);
}
visible.value = true;
}
function onClose() {
Object.assign(form, formDefault);
visible.value = false;
}
function onSubmit() {
formRef.value
.validate()
.then(async () => {
SmartLoading.show();
try {
if (form.dictKeyId) {
await dictApi.keyEdit(form);
} else {
await dictApi.keyAdd(form);
}
message.success(`${form.dictKeyId ? '修改' : '添加'}成功`);
emit('reloadList');
onClose();
} catch (error) {
smartSentry.captureError(error);
} finally {
SmartLoading.hide();
}
})
.catch((error) => {
message.error('参数验证错误,请仔细填写表单数据!');
});
}
// ----------------------- 以下是暴露的方法内容 ------------------------
defineExpose({
showModal,
});
</script>

View File

@@ -1,221 +0,0 @@
<!--
* 字典 value 弹窗
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-06-08 21:50:41
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
-->
<template>
<a-drawer :width="800" :visible="visible" :body-style="{ paddingBottom: '80px' }" title="字典值" @close="onClose">
<a-form class="smart-query-form">
<a-row class="smart-query-form-row">
<a-form-item label="关键字" class="smart-query-form-item">
<a-input style="width: 300px" v-model:value="queryForm.searchWord" placeholder="关键字" />
</a-form-item>
<a-form-item class="smart-query-form-item smart-margin-left10">
<a-button type="primary" @click="ajaxQuery">
<template #icon>
<ReloadOutlined />
</template>
查询
</a-button>
<a-button @click="resetQuery">
<template #icon>
<SearchOutlined />
</template>
重置
</a-button>
</a-form-item>
</a-row>
</a-form>
<a-card size="small" :bordered="false">
<a-row class="smart-table-btn-block">
<div class="smart-table-operate-block">
<a-button @click="addOrUpdateValue" type="primary" size="small">
<template #icon>
<PlusOutlined />
</template>
新建
</a-button>
<a-button @click="confirmBatchDelete" type="danger" size="small" :disabled="selectedRowKeyList.length == 0">
<template #icon>
<DeleteOutlined />
</template>
批量删除
</a-button>
</div>
<div class="smart-table-setting-block"></div>
</a-row>
<a-table
size="small"
:dataSource="tableData"
:columns="columns"
rowKey="dictValueId"
:pagination="false"
:row-selection="{ selectedRowKeys: selectedRowKeyList, onChange: onSelectChange }"
bordered
>
<template #bodyCell="{ record, column }">
<template v-if="column.dataIndex === 'action'">
<a-button @click="addOrUpdateValue(record)" type="link">编辑</a-button>
</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="ajaxQuery"
@showSizeChange="ajaxQuery"
:show-total="(total) => `${total}`"
/>
</div>
</a-card>
<DictValueOperateModal ref="operateModal" @reloadList="ajaxQuery" />
</a-drawer>
</template>
<script setup>
import { reactive, ref } from 'vue';
import DictValueOperateModal from './dict-value-operate-modal.vue';
import { PAGE_SIZE_OPTIONS } from '/@/constants/common-const';
import { dictApi } from '/@/api/support/dict/dict-api';
import { SmartLoading } from '/@/components/framework/smart-loading';
import { Modal } from 'ant-design-vue';
import { message } from 'ant-design-vue';
import { smartSentry } from '/@/lib/smart-sentry';
// 是否展示抽屉
const visible = ref(false);
const dictKeyId = ref(undefined);
function showModal(keyId) {
dictKeyId.value = keyId;
visible.value = true;
ajaxQuery();
}
function onClose() {
visible.value = false;
dictKeyId.value = undefined;
}
const columns = reactive([
{
title: 'ID',
width: 80,
dataIndex: 'dictValueId',
},
{
title: '编码',
dataIndex: 'valueCode',
},
{
title: '名称',
dataIndex: 'valueName',
},
{
title: '排序',
width: 80,
dataIndex: 'sort',
},
{
title: '备注',
dataIndex: 'remark',
},
{
title: '操作',
dataIndex: 'action',
fixed: 'right',
},
]);
// ----------------------- 表格 查询 ------------------------
const queryFormState = {
dictKeyId: undefined,
searchWord: '',
pageNum: 1,
pageSize: 10,
};
const queryForm = reactive({ ...queryFormState });
const selectedRowKeyList = ref([]);
const tableLoading = ref(false);
const tableData = ref([]);
const total = ref(0);
function onSelectChange(selectedRowKeys) {
selectedRowKeyList.value = selectedRowKeys;
}
function resetQuery() {
Object.assign(queryForm, queryFormState);
ajaxQuery();
}
async function ajaxQuery() {
try {
tableLoading.value = true;
queryForm.dictKeyId = dictKeyId.value;
let responseModel = await dictApi.valueQuery(queryForm);
const list = responseModel.data.list;
total.value = responseModel.data.total;
tableData.value = list;
} catch (e) {
smartSentry.captureError(e);
} finally {
tableLoading.value = false;
}
}
// ----------------------- 批量 删除 ------------------------
function confirmBatchDelete() {
Modal.confirm({
title: '提示',
content: '确定要删除选中值吗?',
okText: '删除',
okType: 'danger',
onOk() {
batchDelete();
},
cancelText: '取消',
onCancel() {},
});
}
const batchDelete = async () => {
try {
SmartLoading.show();
await dictApi.valueDelete(selectedRowKeyList.value);
message.success('删除成功');
ajaxQuery();
} catch (e) {
smartSentry.captureError(e);
} finally {
SmartLoading.hide();
}
};
// ----------------------- 弹窗表单操作 ------------------------
const operateModal = ref();
function addOrUpdateValue(rowData) {
operateModal.value.showModal(rowData, dictKeyId.value);
}
defineExpose({
showModal,
});
</script>

View File

@@ -1,101 +0,0 @@
<!--
* 字典 value 弹窗
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-06-08 21:50:41
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
-->
<template>
<a-modal :visible="visible" :title="form.dictValueId ? '编辑' : '添加'" ok-text="确认" cancel-text="取消" @ok="onSubmit" @cancel="onClose">
<a-form ref="formRef" :model="form" :rules="rules" :label-col="{ span: 5 }" :wrapper-col="{ span: 12 }">
<a-form-item label="编码" name="valueCode">
<a-input v-model:value="form.valueCode" placeholder="请输入编码" />
</a-form-item>
<a-form-item label="名称" name="valueName">
<a-input v-model:value="form.valueName" placeholder="请输入名称" />
</a-form-item>
<a-form-item label="排序" name="sort">
<a-input-number v-model:value="form.sort" :min="0" :max="1000" />
</a-form-item>
<a-form-item label="备注" name="remark">
<textarea v-model="form.remark" style="width: 100%; height: 100px; outline: none"></textarea>
</a-form-item>
</a-form>
</a-modal>
</template>
<script setup>
import { ref, reactive } from 'vue';
import { message } from 'ant-design-vue';
import { SmartLoading } from '/@/components/framework/smart-loading';
import { dictApi } from '/@/api/support/dict/dict-api';
import { smartSentry } from '/@/lib/smart-sentry';
// emit
const emit = defineEmits(['reloadList']);
// 组件
const formRef = ref();
const formDefault = {
dictValueId: undefined,
dictKeyId: undefined,
sort: 1,
valueCode: '',
valueName: '',
remark: '',
};
let form = reactive({ ...formDefault });
const rules = {
valueCode: [{ required: true, message: '请输入编码' }],
valueName: [{ required: true, message: '请输入名称' }],
sort: [{ required: true, message: '请输入排序' }],
};
// 是否展示
const visible = ref(false);
function showModal(rowData, dictKeyId) {
Object.assign(form, formDefault);
if (rowData) {
Object.assign(form, rowData);
}
form.dictKeyId = dictKeyId;
visible.value = true;
}
function onClose() {
Object.assign(form, formDefault);
visible.value = false;
}
function onSubmit() {
formRef.value
.validate()
.then(async () => {
SmartLoading.show();
try {
if (form.dictValueId) {
await dictApi.valueEdit(form);
} else {
await dictApi.valueAdd(form);
}
message.success(`${form.dictKeyId ? '修改' : '添加'}成功`);
emit('reloadList');
onClose();
} catch (error) {
smartSentry.captureError(error);
} finally {
SmartLoading.hide();
}
})
.catch((error) => {
console.log('error', error);
message.error('参数验证错误,请仔细填写表单数据!');
});
}
defineExpose({
showModal,
});
</script>

View File

@@ -1,237 +0,0 @@
<!--
* 数据 字典
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-06-08 21:50:41
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
-->
<template>
<a-form class="smart-query-form">
<a-row class="smart-query-form-row">
<a-form-item label="关键字" class="smart-query-form-item">
<a-input style="width: 300px" v-model:value="queryForm.searchWord" placeholder="关键字" />
</a-form-item>
<a-form-item class="smart-query-form-item smart-margin-left10">
<a-button type="primary" @click="ajaxQuery">
<template #icon>
<ReloadOutlined />
</template>
查询
</a-button>
<a-button @click="resetQuery">
<template #icon>
<SearchOutlined />
</template>
重置
</a-button>
</a-form-item>
</a-row>
</a-form>
<a-card size="small" :bordered="false" :hoverable="true">
<a-row class="smart-table-btn-block">
<div class="smart-table-operate-block">
<a-button @click="addOrUpdateKey" v-privilege="'support:dict:add'" type="primary" size="small">
<template #icon>
<PlusOutlined />
</template>
新建
</a-button>
<a-button @click="confirmBatchDelete" v-privilege="'support:dict:batch:delete'" type="danger" size="small" :disabled="selectedRowKeyList.length == 0">
<template #icon>
<DeleteOutlined />
</template>
批量删除
</a-button>
<a-button @click="cacheRefresh" v-privilege="'support:dict:refresh'" type="primary" size="small">
<template #icon>
<cloud-sync-outlined />
</template>
缓存刷新
</a-button>
</div>
<div class="smart-table-setting-block">
<TableOperator class="smart-margin-bottom5" v-model="columns" :tableId="TABLE_ID_CONST.SUPPORT.DICT" :refresh="ajaxQuery" />
</div>
</a-row>
<a-table
size="small"
:dataSource="tableData"
:columns="columns"
:loading="tableLoading"
rowKey="dictKeyId"
:pagination="false"
bordered
:row-selection="{ selectedRowKeys: selectedRowKeyList, onChange: onSelectChange }"
>
<template #bodyCell="{ record, column }">
<template v-if="column.dataIndex === 'keyCode'">
<a @click="showValueList(record.dictKeyId)">{{ record.keyCode }}</a>
</template>
<template v-else-if="column.dataIndex === 'action'">
<div class="smart-table-operate">
<a-button @click="addOrUpdateKey(record)" v-privilege="'support:dict:update'" 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="ajaxQuery"
@showSizeChange="ajaxQuery"
:show-total="(total) => `${total}`"
/>
</div>
<DictKeyOperateModal ref="operateModal" @reloadList="ajaxQuery" />
<!-- 值列表 -->
<DictValueModal ref="dictValueModal" />
</a-card>
</template>
<script setup>
import DictKeyOperateModal from './components/dict-key-operate-modal.vue';
import DictValueModal from './components/dict-value-modal.vue';
import { reactive, ref, onMounted } from 'vue';
import { message, Modal } from 'ant-design-vue';
import { SmartLoading } from '/@/components/framework/smart-loading';
import { dictApi } from '/@/api/support/dict/dict-api';
import { PAGE_SIZE_OPTIONS } from '/@/constants/common-const';
import { smartSentry } from '/@/lib/smart-sentry';
import TableOperator from '/@/components/support/table-operator/index.vue';
import { TABLE_ID_CONST } from '/@/constants/support/table-id-const';
const columns = ref([
{
title: 'ID',
width: 90,
dataIndex: 'dictKeyId',
},
{
title: '编码',
dataIndex: 'keyCode',
},
{
title: '名称',
dataIndex: 'keyName',
},
{
title: '备注',
dataIndex: 'remark',
},
{
title: '操作',
dataIndex: 'action',
fixed: 'right',
width: 50,
},
]);
// ---------------- 查询数据 -----------------
const queryFormState = {
searchWord: '',
pageNum: 1,
pageSize: 10,
};
const queryForm = reactive({ ...queryFormState });
const tableLoading = ref(false);
const selectedRowKeyList = ref([]);
const tableData = ref([]);
const total = ref(0);
const operateModal = ref();
const dictValueModal = ref();
// 显示操作记录弹窗
function showValueList(dictKeyId) {
dictValueModal.value.showModal(dictKeyId);
}
function onSelectChange(selectedRowKeys) {
selectedRowKeyList.value = selectedRowKeys;
}
function resetQuery() {
Object.assign(queryForm, queryFormState);
ajaxQuery();
}
async function ajaxQuery() {
try {
tableLoading.value = true;
let responseModel = await dictApi.keyQuery(queryForm);
const list = responseModel.data.list;
total.value = responseModel.data.total;
tableData.value = list;
} catch (e) {
smartSentry.captureError(e);
} finally {
tableLoading.value = false;
}
}
// ---------------- 刷新缓存 -----------------
async function cacheRefresh() {
try {
SmartLoading.show();
await dictApi.cacheRefresh();
message.success('缓存刷新成功');
ajaxQuery();
} catch (e) {
smartSentry.captureError(e);
} finally {
SmartLoading.hide();
}
}
// ---------------- 批量 删除 -----------------
function confirmBatchDelete() {
Modal.confirm({
title: '提示',
content: '确定要删除选中Key吗?',
okText: '删除',
okType: 'danger',
onOk() {
batchDelete();
},
cancelText: '取消',
onCancel() {},
});
}
const batchDelete = async () => {
try {
SmartLoading.show();
await dictApi.keyDelete(selectedRowKeyList.value);
message.success('删除成功');
ajaxQuery();
} catch (e) {
smartSentry.captureError(e);
} finally {
SmartLoading.hide();
}
};
// ---------------- 添加/更新 -----------------
function addOrUpdateKey(rowData) {
operateModal.value.showModal(rowData);
}
onMounted(ajaxQuery);
</script>

View File

@@ -1,166 +0,0 @@
<!--
* 意见反馈
*
* @Author: 1024创新实验室开云
* @Date: 2022-07-21 21:55:12
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
-->
<template>
<a-form class="smart-query-form">
<a-row class="smart-query-form-row">
<a-form-item label="关键字" class="smart-query-form-item" style="margin-right: 20px">
<a-input style="width: 240px" v-model:value.trim="queryForm.searchWord" placeholder="反馈内容/创建人" />
</a-form-item>
<a-form-item label="创建日期" class="smart-query-form-item" style="margin-right: 20px">
<a-range-picker
v-model:value="chooseTimeRange"
@change="changeCreateDate"
:ranges="defaultTimeRanges"
format="YYYY-MM-DD"
style="width: 240px"
/>
</a-form-item>
<a-form-item class="smart-query-form-item">
<a-button-group v-privilege="'feedback:query'">
<a-button type="primary" @click="onSearch">
<template #icon>
<SearchOutlined />
</template>
查询
</a-button>
<a-button @click="onReset">
<template #icon>
<ReloadOutlined />
</template>
重置
</a-button>
</a-button-group>
</a-form-item>
</a-row>
</a-form>
<a-card size="small">
<a-table rowKey="feedbackId" :dataSource="tableData" :columns="tableColumns" :pagination="false" :loading="tableLoading" size="small" bordered>
<template #bodyCell="{ text, record, column }">
<template v-if="column.dataIndex === 'feedbackAttachment'">
<FilePreview :fileList="text" />
</template>
<template v-if="column.dataIndex === 'userType'">
<span>{{ $smartEnumPlugin.getDescByValue('USER_TYPE_ENUM', text) }}</span>
</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="queryList"
@showSizeChange="queryList"
:show-total="(total) => `${total}`"
/>
</div>
</a-card>
</template>
<script setup>
import { onMounted, reactive, ref } from 'vue';
import { PAGE_SIZE, PAGE_SIZE_OPTIONS } from '/@/constants/common-const';
import { defaultTimeRanges } from '/@/lib/default-time-ranges';
import { feedbackApi } from '/@/api/support/feedback/feedback-api';
import FilePreview from '/@/components/support/file-preview/index.vue';
import { smartSentry } from '/@/lib/smart-sentry';
// ----------------------- 表格列 --------------------------------------
const tableColumns = reactive([
{
title: '编号',
dataIndex: 'feedbackId',
width: 80,
},
{
title: '反馈内容',
dataIndex: 'feedbackContent',
},
{
title: '反馈图片',
dataIndex: 'feedbackAttachment',
},
{
title: '反馈人',
dataIndex: 'userName',
width: 100,
},
{
title: '反馈人类型',
dataIndex: 'userType',
width: 100,
},
{
title: '反馈时间',
dataIndex: 'createTime',
width: 150,
},
]);
// ----------------------- 查询参数 ------------------------------------
const defaultQueryForm = {
startDate: undefined,
endDate: undefined,
searchWord: undefined,
pageNum: 1,
pageSize: PAGE_SIZE,
};
const queryForm = reactive({ ...defaultQueryForm });
const tableLoading = ref(false);
const tableData = ref([]);
const total = ref(0);
onMounted(() => {
queryList();
});
async function queryList() {
try {
tableLoading.value = true;
const result = await feedbackApi.queryFeedback(queryForm);
tableData.value = result.data.list;
total.value = result.data.total;
} catch (e) {
smartSentry.captureError(e);
} finally {
tableLoading.value = false;
}
}
// 处理选择日期范围
const chooseTimeRange = ref([]);
function changeCreateDate(value, dateString) {
queryForm.startDate = dateString[0];
queryForm.endDate = dateString[1];
}
// 点击查询
function onSearch() {
queryForm.pageNum = 1;
queryList();
}
// 点击重置
function onReset() {
Object.assign(queryForm, defaultQueryForm);
chooseTimeRange.value = [];
queryList();
}
// ----------------------- 分页方法 ------------------------------------
</script>

View File

@@ -1,280 +0,0 @@
<!--
* 文件
*
* @Author: 1024创新实验室-主任-卓大
* @Date: 2020-10-10 22:13:18
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
-->
<template>
<!---------- 查询表单form begin ----------->
<a-form class="smart-query-form" v-privilege="'support:file:query'">
<a-row class="smart-query-form-row">
<a-form-item label="文件夹类型" class="smart-query-form-item">
<SmartEnumSelect width="150px" v-model:value="queryForm.folderType" enumName="FILE_FOLDER_TYPE_ENUM" placeholder="文件夹类型" />
</a-form-item>
<a-form-item label="文件名" class="smart-query-form-item">
<a-input style="width: 150px" v-model:value="queryForm.fileName" placeholder="文件名" />
</a-form-item>
<a-form-item label="文件Key" class="smart-query-form-item">
<a-input style="width: 150px" v-model:value="queryForm.fileKey" placeholder="文件Key" />
</a-form-item>
<a-form-item label="文件类型" class="smart-query-form-item">
<a-input style="width: 150px" v-model:value="queryForm.fileType" placeholder="文件类型" />
</a-form-item>
<a-form-item label="创建人" class="smart-query-form-item">
<a-input style="width: 150px" v-model:value="queryForm.creatorName" placeholder="创建人" />
</a-form-item>
<a-form-item label="创建时间" class="smart-query-form-item">
<a-range-picker v-model:value="queryForm.createTime" :ranges="defaultTimeRanges" style="width: 220px" @change="onChangeCreateTime" />
</a-form-item>
<a-form-item class="smart-query-form-item">
<a-button type="primary" @click="queryData">
<template #icon>
<ReloadOutlined />
</template>
查询
</a-button>
<a-button @click="resetQuery" class="smart-margin-left10">
<template #icon>
<SearchOutlined />
</template>
重置
</a-button>
</a-form-item>
</a-row>
</a-form>
<!---------- 查询表单form end ----------->
<a-card size="small" :bordered="false" :hoverable="true">
<!---------- 表格操作行 begin ----------->
<a-row class="smart-table-btn-block">
<div class="smart-table-operate-block">
<a-button type="primary" @click="showUploadModal" size="small">
<template #icon>
<cloud-upload-outlined />
</template>
上传文件
</a-button>
</div>
<div class="smart-table-setting-block">
<TableOperator v-model="columns" :tableId="null" :refresh="queryData" />
</div>
</a-row>
<!---------- 表格操作行 end ----------->
<!---------- 表格 begin ----------->
<a-table size="small" :dataSource="tableData" :columns="columns" rowKey="fileId" bordered :loading="tableLoading" :pagination="false">
<template #bodyCell="{ text, record, column }">
<template v-if="column.dataIndex === 'folderType'">
<span>{{ $smartEnumPlugin.getDescByValue('FILE_FOLDER_TYPE_ENUM', text) }}</span>
</template>
<template v-if="column.dataIndex === 'creatorUserType'">
<span>{{ $smartEnumPlugin.getDescByValue('USER_TYPE_ENUM', text) }}</span>
</template>
<template v-if="column.dataIndex === 'action'">
<div class="smart-table-operate">
<a-button @click="view(record)" type="link">查看</a-button>
<a-button @click="download(record)" type="link">下载</a-button>
</div>
</template>
</template>
</a-table>
<!---------- 表格 end ----------->
<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="queryData"
@showSizeChange="queryData"
:show-total="(total) => `${total}`"
/>
</div>
<FilePreviewModal ref="filePreviewModalRef" />
<a-modal v-model:visible="uploadModalFlag" title="上传文件" @onCancel="hideUploadModal" @ok="hideUploadModal">
<FileUpload
list-type="text"
:maxUploadSize="5"
buttonText="点击上传文件"
:defaultFileList="[]"
:multiple="true"
:folder="FILE_FOLDER_TYPE_ENUM.COMMON.value"
/>
</a-modal>
</a-card>
</template>
<script setup>
import { onMounted, reactive, ref } from 'vue';
import { fileApi } from '/@/api/support/file/file-api';
import SmartEnumSelect from '/@/components/framework/smart-enum-select/index.vue';
import TableOperator from '/@/components/support/table-operator/index.vue';
import { PAGE_SIZE_OPTIONS } from '/@/constants/common-const';
import { defaultTimeRanges } from '/@/lib/default-time-ranges';
import { smartSentry } from '/@/lib/smart-sentry';
import FilePreviewModal from '/@/components/support/file-preview-modal/index.vue';
import FileUpload from '/@/components/support/file-upload/index.vue';
import { FILE_FOLDER_TYPE_ENUM } from '/@/constants/support/file-const';
// ---------------------------- 表格列 ----------------------------
const columns = ref([
{
title: '主键ID',
dataIndex: 'fileId',
ellipsis: true,
width: 70,
},
{
title: '文件夹',
dataIndex: 'folderType',
ellipsis: true,
width: 100,
},
{
title: '文件名称',
dataIndex: 'fileName',
ellipsis: true,
width: 200,
},
{
title: '文件大小',
dataIndex: 'fileSize',
ellipsis: true,
width: 100,
},
{
title: '文件key',
dataIndex: 'fileKey',
ellipsis: true,
},
{
title: '文件类型',
dataIndex: 'fileType',
ellipsis: true,
width: 80,
},
{
title: '上传人',
dataIndex: 'creatorName',
ellipsis: true,
width: 100,
},
{
title: '人类型',
dataIndex: 'creatorUserType',
ellipsis: true,
width: 100,
},
{
title: '上传时间',
dataIndex: 'createTime',
ellipsis: true,
width: 150,
},
{
title: '操作',
dataIndex: 'action',
width: 120,
},
]);
// ---------------------------- 查询数据表单和方法 ----------------------------
const queryFormState = {
folderType: undefined, //文件夹类型
fileName: undefined, //文件名词
fileKey: undefined, //文件Key
fileType: undefined, //文件类型
creatorName: undefined, //创建人
createTime: [], //创建时间
createTimeBegin: undefined, //创建时间 开始
createTimeEnd: undefined, //创建时间 结束
pageNum: 1,
pageSize: 10,
};
// 查询表单form
const queryForm = reactive({ ...queryFormState });
// 表格加载loading
const tableLoading = ref(false);
// 表格数据
const tableData = ref([]);
// 总数
const total = ref(0);
// 重置查询条件
function resetQuery() {
let pageSize = queryForm.pageSize;
Object.assign(queryForm, queryFormState);
queryForm.pageSize = pageSize;
queryData();
}
// 查询数据
async function queryData() {
tableLoading.value = true;
try {
let queryResult = await fileApi.queryPage(queryForm);
for (const file of queryResult.data.list) {
file.fileSize = getFileSize(file.fileSize);
}
tableData.value = queryResult.data.list;
total.value = queryResult.data.total;
} catch (e) {
smartSentry.captureError(e);
} finally {
tableLoading.value = false;
}
}
function onChangeCreateTime(dates, dateStrings) {
queryForm.createTimeBegin = dateStrings[0];
queryForm.createTimeEnd = dateStrings[1];
}
function getFileSize(size) {
//把字节转换成正常文件大小
if (!size) return '';
var num = 1024.0; //byte
if (size < num) return size + 'B';
if (size < Math.pow(num, 2)) return (size / num).toFixed(2) + 'KB'; //kb
if (size < Math.pow(num, 3)) return (size / Math.pow(num, 2)).toFixed(2) + 'MB'; //M
if (size < Math.pow(num, 4)) return (size / Math.pow(num, 3)).toFixed(2) + 'G'; //G
return (size / Math.pow(num, 4)).toFixed(2) + 'T'; //T
}
// 查看文件
const filePreviewModalRef = ref();
function view(file) {
filePreviewModalRef.value.showPreview(file);
}
// 下载文件
async function download(file) {
try {
await fileApi.downLoadFile(file.fileName, file.fileKey);
} catch (e) {
smartSentry.captureError(e);
}
}
onMounted(queryData);
// ------------- 上传文件 --------------------
const uploadModalFlag = ref(false);
function showUploadModal() {
uploadModalFlag.value = true;
}
function hideUploadModal() {
uploadModalFlag.value = false;
queryData();
}
</script>

View File

@@ -1,166 +0,0 @@
<!--
* 心跳记录
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-06-02 20:23:08
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
-->
<template>
<a-card size="small" :bordered="false" :hoverable="true">
<a-alert>
<template v-slot:message>
<h4>Smart-Heart-Beat 心跳服务介绍</h4>
</template>
<template v-slot:description>
<pre>
简介Smart-Heart-Beat 是心跳服务用于监测Java应用的状态等其他信息
原理Java后端会在项目启动的时候开启一个线程每隔一段时间将该应用的IP进程号更新到数据库t_heart_beat_record表中
用途
1在各个环境无论开发测试生产能统一看到所有启动的服务列表
2检测Java应用是否存活
3当某些业务只允许有一个服务启动的时候用于排查是否别人也启动的服务
4 强烈推荐</pre
>
</template>
</a-alert>
<a-form class="smart-query-form">
<a-row class="smart-query-form-row">
<a-form-item label="关键字" class="smart-query-form-item">
<a-input style="width: 300px" v-model:value="queryForm.keywords" placeholder="关键字" />
</a-form-item>
<a-form-item label="心跳时间" class="smart-query-form-item">
<a-range-picker @change="changeCreateDate" v-model:value="createDateRange" :ranges="defaultChooseTimeRange" style="width: 240px" />
</a-form-item>
<a-form-item class="smart-query-form-item smart-margin-left10">
<a-button type="primary" @click="ajaxQuery">
<template #icon>
<ReloadOutlined />
</template>
查询
</a-button>
<a-button @click="resetQuery">
<template #icon>
<SearchOutlined />
</template>
重置
</a-button>
</a-form-item>
</a-row>
</a-form>
<a-row justify="end">
<TableOperator class="smart-margin-bottom5" v-model="columns" :tableId="TABLE_ID_CONST.SUPPORT.HEART_BEAT" :refresh="ajaxQuery" />
</a-row>
<a-table
size="small"
bordered
:loading="tableLoading"
class="smart-margin-top10"
:dataSource="tableData"
:columns="columns"
rowKey="goodsId"
:pagination="false"
/>
<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="ajaxQuery"
@showSizeChange="ajaxQuery"
:show-total="(total) => `${total}`"
/>
</div>
</a-card>
</template>
<script setup>
import { onMounted, reactive, ref } from 'vue';
import { heartBeatApi } from '/@/api/support/heart-beat/heart-beat-api';
import { PAGE_SIZE_OPTIONS } from '/@/constants/common-const';
import { defaultTimeRanges } from '/@/lib/default-time-ranges';
import { smartSentry } from '/@/lib/smart-sentry';
import TableOperator from '/@/components/support/table-operator/index.vue';
import { TABLE_ID_CONST } from '/@/constants/support/table-id-const';
//------------------------ 时间选择 ---------------------
const defaultChooseTimeRange = defaultTimeRanges;
const createDateRange = ref([]);
// 时间变动
function changeCreateDate(dates, dateStrings) {
queryForm.startDate = dateStrings[0];
queryForm.endDate = dateStrings[1];
}
//------------------------ 表格渲染 ---------------------
const columns = ref([
{
title: '项目路径',
dataIndex: 'projectPath',
ellipsis: true,
},
{
title: '服务器ip',
dataIndex: 'serverIp',
ellipsis: true,
},
{
title: '进程号',
dataIndex: 'processNo',
width:100
},
{
title: '进程开启时间',
dataIndex: 'processStartTime',
width:150
},
{
title: '心跳当前时间',
dataIndex: 'heartBeatTime',
width:150
},
]);
const queryFormState = {
pageNum: 1,
pageSize: 10,
keywords: '',
startDate: undefined,
endDate: undefined,
};
const queryForm = reactive({ ...queryFormState });
const tableLoading = ref(false);
const tableData = ref([]);
const total = ref(0);
function resetQuery() {
Object.assign(queryForm, queryFormState);
createDateRange.value = [];
ajaxQuery();
}
async function ajaxQuery() {
try {
tableLoading.value = true;
let responseModel = await heartBeatApi.queryList(queryForm);
const list = responseModel.data.list;
total.value = responseModel.data.total;
tableData.value = list;
} catch (e) {
smartSentry.captureError(e);
} finally {
tableLoading.value = false;
}
}
onMounted(ajaxQuery);
</script>

View File

@@ -1,132 +0,0 @@
<!--
* 目录表单
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-07-21 21:55:12
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
-->
<template>
<a-modal v-model:visible="visible" :title="formState.helpDocCatalogId ? '编辑目录' : '添加目录'" @ok="handleOk" destroyOnClose>
<a-form ref="formRef" :model="formState" :rules="rules" layout="vertical">
<a-form-item label="上级目录" name="parentId" v-if="formState.parentId != 0">
<HelpDocCatalogTreeSelect ref="helpDocCatalogTreeSelect" 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="sort">
<a-input-number style="width: 100%" v-model:value="formState.sort" :min="0" placeholder="请输入目录名称" />
</a-form-item>
</a-form>
</a-modal>
</template>
<script setup lang="ts">
import message from 'ant-design-vue/lib/message';
import { reactive, ref } from 'vue';
import { helpDocCatalogApi } from '/@/api/support/help-doc/help-doc-catalog-api';
import HelpDocCatalogTreeSelect from './help-doc-catalog-tree-select.vue';
import { SmartLoading } from '/@/components/framework/smart-loading';
import { smartSentry } from '/@/lib/smart-sentry';
// ----------------------- 对外暴漏 ---------------------
defineExpose({
showModal,
});
// ----------------------- modal 的显示与隐藏 ---------------------
const emits = defineEmits(['refresh']);
const visible = ref(false);
function showModal(data) {
visible.value = true;
updateFormData(data);
}
function closeModal() {
visible.value = false;
resetFormData();
}
// ----------------------- form 表单操作 ---------------------
const formRef = ref();
const helpDocCatalogTreeSelect = ref();
const defaultHelpDocCatalogForm = {
helpDocCatalogId: undefined,
name: undefined,
parentId: undefined,
sort: 0,
};
const employeeSelect = ref();
let formState = reactive({
...defaultHelpDocCatalogForm,
});
// 表单校验规则
const rules = {
parentId: [{ required: true, message: '上级目录不能为空' }],
name: [
{ required: true, message: '目录名称不能为空' },
{ max: 50, message: '目录名称不能大于20个字符', trigger: 'blur' },
],
};
// 更新表单数据
function updateFormData(data) {
Object.assign(formState, defaultHelpDocCatalogForm);
if (data) {
Object.assign(formState, data);
}
visible.value = true;
}
// 重置表单数据
function resetFormData() {
Object.assign(formState, defaultHelpDocCatalogForm);
}
async function handleOk() {
try {
await formRef.value.validate();
if (formState.helpDocCatalogId) {
updateHelpDocCatalog();
} else {
addHelpDocCatalog();
}
} catch (error) {
message.error('参数验证错误,请仔细填写表单数据!');
}
}
// ----------------------- form 表单 ajax 操作 ---------------------
//添加目录ajax请求
async function addHelpDocCatalog() {
SmartLoading.show();
try {
await helpDocCatalogApi.add(formState);
emits('refresh');
closeModal();
} catch (error) {
smartSentry.captureError(error);
} finally {
SmartLoading.hide();
}
}
//更新目录ajax请求
async function updateHelpDocCatalog() {
SmartLoading.show();
try {
if (formState.parentId == formState.helpDocCatalogId) {
message.warning('上级菜单不能为自己');
return;
}
await helpDocCatalogApi.update(formState);
emits('refresh');
closeModal();
} catch (error) {
smartSentry.captureError(error);
} finally {
SmartLoading.hide();
}
}
</script>

View File

@@ -1,89 +0,0 @@
<!--
* 目录下拉框
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-07-21 21:55:12
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
-->
<template>
<a-tree-select
:value="props.value"
:treeData="treeData"
:fieldNames="{ label: 'name', key: 'helpDocCatalogId', value: 'helpDocCatalogId' }"
show-search
style="width: 100%"
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
placeholder="请选择目录"
allow-clear
tree-default-expand-all
:multiple="props.multiple"
@change="treeSelectChange"
/>
</template>
<script setup>
import { onMounted, ref } from 'vue';
import _ from 'lodash';
import { helpDocCatalogApi } from '/@/api/support/help-doc/help-doc-catalog-api';
const props = defineProps({
// 绑定值
value: Number,
// 单选多选
multiple: {
type: Boolean,
default: false,
},
});
const emit = defineEmits(['update:value']);
let treeData = ref([]);
onMounted(queryCatalogTree);
// 外部调用初始化
async function queryCatalogTree() {
let res = await helpDocCatalogApi.getAll();
let children = buildHelpDocCatalogTree(res.data, 0);
treeData.value = children;
}
// 构建目录树
function buildHelpDocCatalogTree(data, parentId) {
let children = data.filter((e) => e.parentId === parentId) || [];
children = _.sortBy(children, (e) => e.sort);
children.forEach((e) => {
e.children = buildHelpDocCatalogTree(data, e.helpDocCatalogId);
});
updateHelpDocCatalogPreIdAndNextId(children);
return children;
}
// 更新树的前置id和后置id
function updateHelpDocCatalogPreIdAndNextId(data) {
for (let index = 0; index < data.length; index++) {
if (index === 0) {
data[index].nextId = data.length > 1 ? data[1].helpDocCatalogId : undefined;
continue;
}
if (index === data.length - 1) {
data[index].preId = data[index - 1].helpDocCatalogId;
data[index].nextId = undefined;
continue;
}
data[index].preId = data[index - 1].helpDocCatalogId;
data[index].nextId = data[index + 1].helpDocCatalogId;
}
}
function treeSelectChange(e) {
emit('update:value', e);
}
// ----------------------- 以下是暴露的方法内容 ------------------------
defineExpose({
queryCatalogTree,
});
</script>

View File

@@ -1,354 +0,0 @@
<!--
* 目录树
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-07-21 21:55:12
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
-->
<template>
<a-card class="tree-container" size="small">
<a-row>
<a-input v-model:value.trim="keywords" placeholder="请输入目录名称" />
</a-row>
<a-row class="sort-flag-row" v-if="props.showMenu">
<span>
排序
<template v-if="showSortFlag"> 越小越靠前 </template>
<a-switch v-model:checked="showSortFlag" />
</span>
<a-button type="primary" @click="addTop" size="small" v-privilege="'helpDocCatalog:addCategory'">新建</a-button>
</a-row>
<a-tree
v-if="!_.isEmpty(helpDocCatalogTreeData)"
v-model:selectedKeys="selectedKeys"
v-model:checkedKeys="checkedKeys"
class="tree"
:treeData="helpDocCatalogTreeData"
:fieldNames="{ title: 'name', key: 'helpDocCatalogId', value: 'helpDocCatalogId' }"
style="width: 100%; overflow-x: auto"
:style="[!height ? '' : { height: `${height}px`, overflowY: 'auto' }]"
:showLine="!props.checkable"
:checkable="props.checkable"
:checkStrictly="props.checkStrictly"
:selectable="!props.checkable"
:defaultExpandAll="true"
@select="treeSelectChange"
>
<template #title="item">
<a-popover placement="right" v-if="props.showMenu">
<template #content>
<div style="display: flex; flex-direction: column">
<a-button type="text" @click="addHelpDocCatalog(item.dataRef)" v-privilege="'helpDocCatalog:addCategory'">添加下级</a-button>
<a-button type="text" @click="updateHelpDocCatalog(item.dataRef)" v-privilege="'helpDocCatalog:edit'">修改</a-button>
<a-button
type="text"
v-if="item.helpDocCatalogId != topHelpDocCatalogId"
@click="deleteHelpDocCatalog(item.helpDocCatalogId)"
v-privilege="'helpDocCatalog:delete'"
>删除</a-button
>
</div>
</template>
{{ item.name }}
<!--显示排序字段-->
<template v-if="showSortFlag">
<span class="sort-span">({{ item.sort }})</span>
</template>
</a-popover>
<div v-else>{{ item.name }}</div>
</template>
</a-tree>
<div class="no-data" v-else>暂无结果</div>
<!-- 添加编辑目录弹窗 -->
<HelpDocCatalogFormModal ref="helpDocCatalogFormModal" @refresh="refresh" />
</a-card>
</template>
<script setup lang="ts">
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
import { ref } from 'vue';
import { onUnmounted, watch } from 'vue';
import { Modal } from 'ant-design-vue';
import _ from 'lodash';
import { createVNode, onMounted } from 'vue';
import HelpDocCatalogFormModal from './help-doc-catalog-form-modal.vue';
import { helpDocCatalogApi } from '/@/api/support/help-doc/help-doc-catalog-api';
import { SmartLoading } from '/@/components/framework/smart-loading';
import helpDocCatalogEmitter from '../help-doc-mitt';
import { smartSentry } from '/@/lib/smart-sentry';
const HELP_DOC_CATALOG_PARENT_ID = 0;
// ----------------------- 组件参数 ---------------------
const props = defineProps({
// 是否可以选中
checkable: {
type: Boolean,
default: false,
},
// 父子节点选中状态不再关联
checkStrictly: {
type: Boolean,
default: false,
},
// 树高度 超出出滚动条
height: Number,
// 显示菜单
showMenu: {
type: Boolean,
default: true,
},
});
// ----------------------- 目录树的展示 ---------------------
const topHelpDocCatalogId = ref();
// 所有目录列表
const helpDocCatalogList = ref([]);
// 目录树形数据
const helpDocCatalogTreeData = ref([]);
// 存放目录id和目录用于查找
const idInfoMap = ref(new Map());
// 是否显示排序字段
const showSortFlag = ref(false);
onMounted(() => {
queryHelpDocCatalogTree();
});
// 刷新
async function refresh() {
await queryHelpDocCatalogTree();
if (currentSelectedHelpDocCatalogId.value) {
selectTree(currentSelectedHelpDocCatalogId.value);
}
}
// 查询目录列表并构建 目录树
async function queryHelpDocCatalogTree() {
let res = await helpDocCatalogApi.getAll();
let data = res.data;
helpDocCatalogList.value = data;
helpDocCatalogTreeData.value = buildHelpDocCatalogTree(data, HELP_DOC_CATALOG_PARENT_ID);
data.forEach((e) => {
idInfoMap.value.set(e.helpDocCatalogId, e);
});
// 默认显示 最顶级ID为列表中返回的第一条数据的ID
if (!_.isEmpty(helpDocCatalogTreeData.value) && helpDocCatalogTreeData.value.length > 0) {
topHelpDocCatalogId.value = helpDocCatalogTreeData.value[0].helpDocCatalogId;
selectTree(helpDocCatalogTreeData.value[0].helpDocCatalogId);
}
}
// 构建目录树
function buildHelpDocCatalogTree(data, parentId) {
let children = data.filter((e) => e.parentId === parentId) || [];
children = _.sortBy(children, (e) => e.sort);
children.forEach((e) => {
e.children = buildHelpDocCatalogTree(data, e.helpDocCatalogId);
});
updateHelpDocCatalogPreIdAndNextId(children);
return children;
}
// 更新树的前置id和后置id
function updateHelpDocCatalogPreIdAndNextId(data) {
for (let index = 0; index < data.length; index++) {
if (index === 0) {
data[index].nextId = data.length > 1 ? data[1].helpDocCatalogId : undefined;
continue;
}
if (index === data.length - 1) {
data[index].preId = data[index - 1].helpDocCatalogId;
data[index].nextId = undefined;
continue;
}
data[index].preId = data[index - 1].helpDocCatalogId;
data[index].nextId = data[index + 1].helpDocCatalogId;
}
}
// ----------------------- 树的选中 ---------------------
const selectedKeys = ref([]);
const checkedKeys = ref([]);
const breadcrumb = ref([]);
const currentSelectedHelpDocCatalogId = ref();
const selectedHelpDocCatalogChildren = ref([]);
helpDocCatalogEmitter.on('selectTree', selectTree);
function selectTree(id) {
selectedKeys.value = [id];
treeSelectChange(selectedKeys.value);
}
function treeSelectChange(idList) {
if (_.isEmpty(idList)) {
breadcrumb.value = [];
selectedHelpDocCatalogChildren.value = [];
return;
}
let id = idList[0];
selectedHelpDocCatalogChildren.value = helpDocCatalogList.value.filter((e) => e.parentId == id);
let filterHelpDocCatalogList = [];
recursionFilterHelpDocCatalog(filterHelpDocCatalogList, id, true);
breadcrumb.value = filterHelpDocCatalogList.map((e) => e.name);
}
// ----------------------- 筛选 ---------------------
const keywords = ref('');
watch(
() => keywords.value,
() => {
onSearch();
}
);
// 筛选
function onSearch() {
if (!keywords.value) {
helpDocCatalogTreeData.value = buildHelpDocCatalogTree(helpDocCatalogList.value, HELP_DOC_CATALOG_PARENT_ID);
return;
}
let originData = helpDocCatalogList.value.concat();
if (!originData) {
return;
}
// 筛选出名称符合的目录
let filterDepartmenet = originData.filter((e) => e.name.indexOf(keywords.value) > -1);
let filterHelpDocCatalogList = [];
// 循环筛选出的目录 构建目录树
filterDepartmenet.forEach((e) => {
recursionFilterHelpDocCatalog(filterHelpDocCatalogList, e.helpDocCatalogId, false);
});
helpDocCatalogTreeData.value = buildHelpDocCatalogTree(filterHelpDocCatalogList, HELP_DOC_CATALOG_PARENT_ID);
}
// 根据ID递归筛选目录
function recursionFilterHelpDocCatalog(resList, id, unshift) {
let info = idInfoMap.value.get(id);
if (!info || resList.some((e) => e.helpDocCatalogId == id)) {
return;
}
if (unshift) {
resList.unshift(info);
} else {
resList.push(info);
}
if (info.parentId && info.parentId != 0) {
recursionFilterHelpDocCatalog(resList, info.parentId, unshift);
}
}
// ----------------------- 表单操作:添加目录/修改目录/删除目录/上下移动 ---------------------
const helpDocCatalogFormModal = ref();
// 添加
function addHelpDocCatalog(e) {
let data = {
helpDocCatalogId: 0,
name: '',
parentId: e.helpDocCatalogId,
};
currentSelectedHelpDocCatalogId.value = e.helpDocCatalogId;
helpDocCatalogFormModal.value.showModal(data);
}
// 添加
function addTop() {
let data = {
helpDocCatalogId: 0,
name: '',
parentId: 0,
};
helpDocCatalogFormModal.value.showModal(data);
}
// 编辑
function updateHelpDocCatalog(e) {
currentSelectedHelpDocCatalogId.value = e.helpDocCatalogId;
helpDocCatalogFormModal.value.showModal(e);
}
// 删除
function deleteHelpDocCatalog(id) {
Modal.confirm({
title: '提醒',
icon: createVNode(ExclamationCircleOutlined),
content: '确定要删除该目录吗?',
okText: '删除',
okType: 'danger',
async onOk() {
SmartLoading.show();
try {
// 若删除的是当前的目录 先找到上级目录
let selectedKey = null;
if (!_.isEmpty(selectedKeys.value)) {
selectedKey = selectedKeys.value[0];
if (selectedKey == id) {
let selectInfo = helpDocCatalogList.value.find((e) => e.helpDocCatalogId == id);
if (selectInfo && selectInfo.parentId) {
selectedKey = selectInfo.parentId;
}
}
}
await helpDocCatalogApi.delete(id);
await queryHelpDocCatalogTree();
// 刷新选中目录
if (selectedKey) {
selectTree(selectedKey);
}
} catch (error) {
smartSentry.captureError(error);
} finally {
SmartLoading.hide();
}
},
cancelText: '取消',
onCancel() {},
});
}
onUnmounted(() => {
helpDocCatalogEmitter.all.clear();
});
// ----------------------- 以下是暴露的方法内容 ----------------------------
defineExpose({
queryHelpDocCatalogTree,
selectedHelpDocCatalogChildren,
breadcrumb,
selectedKeys,
checkedKeys,
keywords,
});
</script>
<style scoped lang="less">
.tree-container {
height: 100%;
.tree {
height: 618px;
margin-top: 10px;
overflow-x: hidden;
}
.sort-flag-row {
display: flex;
justify-content: space-between;
margin-top: 10px;
margin-bottom: 10px;
}
.sort-span {
margin-left: 5px;
color: @success-color;
}
.no-data {
margin: 10px;
}
}
</style>

View File

@@ -1,204 +0,0 @@
<!--
* 帮助文档表单
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-07-21 21:55:12
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
-->
<template>
<a-drawer
:title="formData.helpDocId ? '编辑' : '新建'"
:visible="visibleFlag"
:width="1000"
:footerStyle="{ textAlign: 'right' }"
@close="onClose"
:destroyOnClose="true"
>
<a-form ref="formRef" :model="formData" :rules="formRules" :label-col="{ span: 3 }" :wrapper-col="{ span: 20 }">
<a-form-item label="标题" name="title">
<a-input v-model:value="formData.title" placeholder="请输入标题" />
</a-form-item>
<a-form-item label="目录" name="helpDocCatalogId">
<HelpDocCatalogTreeSelect v-model:value="formData.helpDocCatalogId" style="width: 100%" />
</a-form-item>
<a-form-item label="作者" name="author">
<a-input v-model:value="formData.author" placeholder="请输入作者" />
</a-form-item>
<a-form-item label="排序" name="sort">
<a-input-number v-model:value="formData.sort" placeholder="值越小越靠前" />值越小越靠前
</a-form-item>
<a-form-item label="关联菜单">
<MenuTreeSelect v-model:value="formData.relationIdList" ref="menuTreeSelect" />
</a-form-item>
<a-form-item label="公告内容" name="contentHtml">
<Wangeditor ref="contentRef" :modelValue="formData.contentHtml" :height="300" />
</a-form-item>
<a-form-item label="附件">
<Upload
:defaultFileList="defaultFileList"
:maxUploadSize="10"
:folder="FILE_FOLDER_TYPE_ENUM.HELP_DOC.value"
buttonText="上传附件"
listType="text"
extraMsg="最多上传10个附件"
@change="changeAttachment"
/>
</a-form-item>
</a-form>
<template #footer>
<a-space>
<a-button @click="onClose">取消</a-button>
<a-button type="primary" @click="onSubmit">保存</a-button>
</a-space>
</template>
</a-drawer>
</template>
<script setup>
import { reactive, ref, nextTick } from 'vue';
import { message, Modal } from 'ant-design-vue';
import lodash from 'lodash';
import { SmartLoading } from '/@/components/framework/smart-loading';
import { FILE_FOLDER_TYPE_ENUM } from '/@/constants/support/file-const';
import { helpDocApi } from '/@/api/support/help-doc/help-doc-api';
import Wangeditor from '/@/components/framework/wangeditor/index.vue';
import Upload from '/@/components/support/file-upload/index.vue';
import HelpDocCatalogTreeSelect from './help-doc-catalog-tree-select.vue';
import MenuTreeSelect from '/@/components/system/menu-tree-select/index.vue';
import _ from 'lodash';
import { smartSentry } from '/@/lib/smart-sentry';
const emits = defineEmits(['reloadList']);
// ------------------ 显示,关闭 ------------------
// 显示
const visibleFlag = ref(false);
function showModal(helpDocId) {
Object.assign(formData, defaultFormData);
defaultFileList.value = [];
if (helpDocId) {
getDetail(helpDocId);
}
visibleFlag.value = true;
nextTick(() => {
formRef.value.clearValidate();
});
}
// 关闭
function onClose() {
visibleFlag.value = false;
}
// ------------------ 表单 ------------------
const formRef = ref();
const contentRef = ref();
const noticeFormVisibleModal = ref();
const defaultFormData = {
helpDocId: undefined,
helpDocCatalogId: undefined,
title: undefined, // 标题
author: undefined, // 作者
sort: 0, // 排序
attachment: [], // 附件
relationIdList: [], //关联id集合
contentHtml: '', // html内容
contentText: '', // 纯文本内容
};
const formData = reactive({ ...defaultFormData });
const formRules = {
title: [{ required: true, message: '请输入' }],
helpDocCatalogId: [{ required: true, message: '请选择目录' }],
author: [{ required: true, message: '请输入作者' }],
sort: [{ required: true, message: '请输入排序' }],
contentHtml: [{ required: true, message: '请输入内容' }],
};
// 查询详情
async function getDetail(helpDocId) {
try {
SmartLoading.show();
const result = await helpDocApi.getDetail(helpDocId);
const attachment = result.data.attachment;
if (!lodash.isEmpty(attachment)) {
defaultFileList.value = attachment;
} else {
defaultFileList.value = [];
}
Object.assign(formData, result.data);
formData.relationIdList = result.data.relationList ? result.data.relationList.map((e) => e.relationId) : [];
} catch (err) {
smartSentry.captureError(err);
} finally {
SmartLoading.hide();
}
}
// 点击确定,验证表单
async function onSubmit() {
try {
formData.contentHtml = contentRef.value.getHtml();
formData.contentText = contentRef.value.getText();
await formRef.value.validateFields();
save();
} catch (err) {
message.error('参数验证错误,请仔细填写表单数据!');
}
}
// 新建、编辑API
const menuTreeSelect = ref();
async function save() {
try {
SmartLoading.show();
let param = _.cloneDeep(formData);
let relationList = menuTreeSelect.value.getMenuListByIdList(formData.relationIdList);
param.relationList = relationList.map((e) => Object.assign({}, { relationId: e.menuId, relationName: e.menuName }));
if (param.helpDocId) {
await helpDocApi.update(param);
} else {
await helpDocApi.add(param);
}
message.success('保存成功');
emits('reloadList');
onClose();
} catch (err) {
smartSentry.captureError(err);
} finally {
SmartLoading.hide();
}
}
// ----------------------- 上传附件 ----------------------------
// 已上传的附件列表
const defaultFileList = ref([]);
function changeAttachment(fileList) {
defaultFileList.value = fileList;
formData.attachment = lodash.isEmpty(fileList) ? [] : fileList;
}
// ----------------------- 以下是暴露的方法内容 ------------------------
defineExpose({
showModal,
});
</script>
<style lang="less" scoped>
.visible-list {
display: flex;
flex-wrap: wrap;
.visible-item {
padding-top: 8px;
}
}
</style>

View File

@@ -1,267 +0,0 @@
<!--
* 帮助文档 列表
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-07-21 21:55:12
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
-->
<template>
<a-form class="smart-query-form" v-privilege="'helpDoc:query'">
<a-row class="smart-query-form-row">
<a-form-item label="关键字" class="smart-query-form-item">
<a-input style="width: 300px" v-model:value="queryForm.keywords" placeholder="标题、作者" />
</a-form-item>
<a-form-item label="创建时间" class="smart-query-form-item">
<a-range-picker v-model:value="createDate" @change="createDateChange" style="width: 220px" />
</a-form-item>
<a-form-item class="smart-query-form-item smart-margin-left10">
<a-button-group>
<a-button type="primary" @click="onSearch">
<template #icon>
<SearchOutlined />
</template>
查询
</a-button>
<a-button @click="onReload">
<template #icon>
<ReloadOutlined />
</template>
重置
</a-button>
</a-button-group>
</a-form-item>
</a-row>
</a-form>
<a-card size="small" :bordered="false">
<a-row class="smart-table-btn-block">
<div class="smart-table-operate-block">
<a-button type="primary" size="small" @click="addOrUpdate()" v-privilege="'helpDoc:add'">
<template #icon>
<PlusOutlined />
</template>
新建
</a-button>
</div>
<div class="smart-table-setting-block">
<TableOperator v-model="tableColumns" :tableId="TABLE_ID_CONST.SUPPORT.HELP_DOC" :refresh="queryHelpDocList" />
</div>
</a-row>
<a-table
rowKey="helpDocId"
:columns="tableColumns"
:scroll="{ x: 1000 }"
:dataSource="tableData"
:pagination="false"
:loading="tableLoading"
size="small"
bordered
>
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'title'">
<router-link tag="a" target="_blank" :to="{ path: '/help-doc/detail', query: { helpDocId: record.helpDocId } }">{{
record.title
}}</router-link>
</template>
<template v-else-if="column.dataIndex === 'action'">
<div class="smart-table-operate">
<a-button type="link" @click="addOrUpdate(record.helpDocId)" v-privilege="'helpDoc:update'">编辑</a-button>
<a-button type="link" danger @click="onDelete(record.helpDocId)" v-privilege="'helpDoc:delete'">删除</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="queryHelpDocList"
@showSizeChange="queryHelpDocList"
:show-total="(total) => `${total}`"
/>
</div>
</a-card>
<HelpDocFormDrawer ref="helpDocFormDrawerRef" @reloadList="queryHelpDocList" />
</template>
<script setup>
import { message, Modal } from 'ant-design-vue';
import { onMounted, reactive, ref, watch } from 'vue';
import HelpDocFormDrawer from './help-doc-form-drawer.vue';
import { helpDocApi } from '/@/api/support/help-doc/help-doc-api';
import { PAGE_SIZE, PAGE_SIZE_OPTIONS } from '/@/constants/common-const';
import { smartSentry } from '/@/lib/smart-sentry';
import TableOperator from '/@/components/support/table-operator/index.vue';
import { TABLE_ID_CONST } from '/@/constants/support/table-id-const';
const props = defineProps({
// 目录id
helpDocCatalogId: Number,
});
const queryFormState = {
helpDocCatalogId: props.helpDocCatalogId, //目录
keywords: '', //标题、作者
createTimeBegin: null, //创建-开始时间
createTimeEnd: null, //创建-截止时间
pageNum: 1,
pageSize: PAGE_SIZE,
};
const queryForm = reactive({ ...queryFormState });
const tableColumns = ref([
{
title: `标题`,
dataIndex: 'title',
ellipsis: true,
},
{
title: '目录',
dataIndex: 'helpDocCatalogName',
width: 120,
ellipsis: true,
},
{
title: `作者`,
dataIndex: 'author',
width: 110,
ellipsis: true,
},
{
title: '排序',
dataIndex: 'sort',
width: 90,
},
{
title: '页面浏览量',
dataIndex: 'pageViewCount',
width: 90,
},
{
title: '用户浏览量',
dataIndex: 'userViewCount',
width: 90,
},
{
title: '创建时间',
dataIndex: 'createTime',
width: 150,
},
{
title: '操作',
dataIndex: 'action',
fixed: 'right',
width: 90,
},
]);
// ------------------ 查询相关 ------------------
const tableData = ref([]);
const total = ref(0);
const tableLoading = ref(false);
onMounted(() => {
queryHelpDocList();
});
// 查询列表
async function queryHelpDocList() {
try {
tableLoading.value = true;
const result = await helpDocApi.query(queryForm);
tableData.value = result.data.list;
total.value = result.data.total;
} catch (err) {
smartSentry.captureError(err);
} finally {
tableLoading.value = false;
}
}
// 点击查询
function onSearch() {
queryForm.pageNum = 1;
queryHelpDocList();
}
// 点击重置
function onReload() {
Object.assign(queryForm, queryFormState);
publishDate.value = [];
createDate.value = [];
queryHelpDocList();
}
// 发布日期选择
const publishDate = ref([]);
function publishDateChange(dates, dateStrings) {
queryForm.publishTimeBegin = dateStrings[0];
queryForm.publishTimeEnd = dateStrings[1];
}
// 创建日期选择
const createDate = ref([]);
function createDateChange(dates, dateStrings) {
queryForm.createTimeBegin = dateStrings[0];
queryForm.createTimeEnd = dateStrings[1];
}
// ------------------ 新建、编辑 ------------------
// 新建、编辑
const helpDocFormDrawerRef = ref();
function addOrUpdate(helpDocId) {
helpDocFormDrawerRef.value.showModal(helpDocId);
}
// ------------------ 删除 ------------------
// 删除
function onDelete(helpDocId) {
Modal.confirm({
title: '提示',
content: '确认删除此数据吗?',
onOk() {
deleteHelpDoc(helpDocId);
},
});
}
// 删除API
async function deleteHelpDoc(helpDocId) {
try {
tableLoading.value = true;
await helpDocApi.delete(helpDocId);
message.success('删除成功');
queryHelpDocList();
} catch (err) {
smartSentry.captureError(err);
} finally {
tableLoading.value = false;
}
}
watch(
() => props.helpDocCatalogId,
() => {
queryForm.helpDocCatalogId = props.helpDocCatalogId;
onSearch();
},
{ immediate: true }
);
</script>
<style lang="less" scoped></style>

View File

@@ -1,55 +0,0 @@
<!--
* 帮助文档 管理
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-07-21 21:55:12
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
-->
<template>
<div class="height100">
<a-row :gutter="16" class="height100">
<a-col :span="6">
<HelpDocCatalogTree ref="helpDocCatalogTreeRef" />
</a-col>
<a-col :span="18" class="height100">
<div class="help-doc-box height100">
<HelpDocList :helpDocCatalogId="selectedHelpDocCatalogId" />
</div>
</a-col>
</a-row>
</div>
</template>
<script setup>
import _ from 'lodash';
import { computed, ref } from 'vue';
import HelpDocCatalogTree from './components/help-doc-catalog-tree.vue';
import HelpDocList from './components/help-doc-list.vue';
const helpDocCatalogTreeRef = ref();
// 当前选中的目录id
const selectedHelpDocCatalogId = computed(() => {
if (helpDocCatalogTreeRef.value) {
let selectedKeys = helpDocCatalogTreeRef.value.selectedKeys;
return _.isEmpty(selectedKeys) ? null : selectedKeys[0];
}
return null;
});
</script>
<style scoped lang="less">
.height100 {
height: 100%;
}
.help-doc-box {
display: flex;
flex-direction: column;
.employee {
flex-grow: 2;
margin-top: 10px;
}
}
</style>

View File

@@ -1,11 +0,0 @@
/*
* 帮助文档 event bus
*
* @Author: 1024创新实验室-主任:卓大
* @Date: 2022-09-12 18:06:41
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
*/
import mitt from 'mitt';
export default mitt();

View File

@@ -1,168 +0,0 @@
<!--
* 查看记录
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-07-21 21:55:12
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
-->
<template>
<div>
<a-form class="smart-query-form">
<a-row class="smart-query-form-row">
<a-form-item label="关键字" class="smart-query-form-item" style="width: 280px">
<a-input v-model:value="queryForm.keywords" placeholder="姓名/IP/设备" />
</a-form-item>
<a-form-item class="smart-query-form-item smart-margin-left10">
<a-button-group>
<a-button type="primary" @click="onSearch">
<template #icon>
<SearchOutlined />
</template>
查询
</a-button>
<a-button @click="resetQuery">
<template #icon>
<ReloadOutlined />
</template>
重置
</a-button>
</a-button-group>
</a-form-item>
</a-row>
</a-form>
<a-table rowKey="employeeId" :columns="tableColumns" :dataSource="tableData" :pagination="false" :loading="tableLoading" size="small" bordered>
<template #bodyCell="{ column, record, text }">
<template v-if="column.dataIndex === 'firstIp'"> {{ text }} ({{ record.firstDevice }}) </template>
<template v-if="column.dataIndex === 'lastIp'"> {{ text }} ({{ record.lastDevice }}) </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="queryViewRecord"
@showSizeChange="queryViewRecord"
:show-total="(total) => `${total}`"
/>
</div>
</div>
</template>
<script setup>
import { reactive, ref } from 'vue';
import { helpDocApi } from '/@/api/support/help-doc/help-doc-api';
import { PAGE_SIZE, PAGE_SIZE_OPTIONS } from '/@/constants/common-const';
import uaparser from 'ua-parser-js';
import { smartSentry } from '/@/lib/smart-sentry';
const props = defineProps({
helpDocId: {
type: [Number, String],
},
});
defineExpose({
onSearch,
});
const tableColumns = [
{
title: '用户名',
dataIndex: 'userName',
},
{
title: '查看次数',
dataIndex: 'pageViewCount',
with: 100,
},
{
title: '首次查看设备',
dataIndex: 'firstIp',
},
{
title: '首次查看时间',
dataIndex: 'createTime',
with: 120,
},
{
title: '最后一次查看设备',
dataIndex: 'lastIp',
},
{
title: '最后一次查看时间',
dataIndex: 'updateTime',
with: 120,
},
];
const tableData = ref([]);
const total = ref(0);
const tableLoading = ref(false);
const defaultQueryForm = {
helpDocId: props.helpDocId,
keywords: '',
pageNum: 1,
pageSize: PAGE_SIZE,
};
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 helpDocApi.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) {
smartSentry.captureError(err);
} finally {
tableLoading.value = false;
}
}
// 点击查询
function onSearch() {
queryForm.pageNum = 1;
queryViewRecord();
}
// 点击重置
function resetQuery() {
Object.assign(queryForm, defaultQueryForm);
queryViewRecord();
}
</script>
<style lang="less" scoped>
.ant-table.ant-table-small .ant-table-title,
.ant-table.ant-table-small .ant-table-footer,
.ant-table.ant-table-small .ant-table-tbody > tr > td,
.ant-table.ant-table-small tfoot > tr > th,
.ant-table.ant-table-small tfoot > tr > td {
padding: 0px 3px !important;
line-height: 28px;
}
</style>

View File

@@ -1,150 +0,0 @@
<!--
* 帮助文档详情
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-07-21 21:55:12
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
-->
<template>
<a-card size="small" :bordered="false">
<div v-if="helpDocDetail">
<div class="content-header">
<!--startprint-->
<div class="content-header-title">
{{ helpDocDetail.title }}
</div>
<div class="content-header-info">
<span>阅读量{{ helpDocDetail.pageViewCount }}</span>
<span v-show="helpDocDetail.author">作者{{ helpDocDetail.author }}</span>
<span>发布于{{ helpDocDetail.createTime }}</span>
<span>修改于{{ helpDocDetail.updateTime }}</span>
<span @click="print">打印本页</span>
</div>
</div>
<div class="content-html" v-html="helpDocDetail.contentHtml"></div>
<!--endprint-->
</div>
<a-divider v-if="helpDocDetail.attachment && helpDocDetail.attachment.length > 0" />
<div v-if="helpDocDetail.attachment && helpDocDetail.attachment.length > 0">附件:<FilePreview :fileList="helpDocDetail.attachment" /></div>
</a-card>
<a-card title="阅读记录" size="small" class="smart-margin-top10" :bordered="false">
<HelpDocViewRecordList ref="helpDocViewRecordListRef" :helpDocId="route.query.helpDocId" />
</a-card>
<!-- 预览附件 -->
<FilePreview ref="filePreviewRef" />
</template>
<script setup>
import { onMounted, ref } from 'vue';
import { useRoute } from 'vue-router';
import HelpDocViewRecordList from './components/help-doc-view-record-list.vue';
import { helpDocApi } from '/@/api/support/help-doc/help-doc-api';
import { SmartLoading } from '/@/components/framework/smart-loading';
import FilePreview from '/@/components/support/file-preview/index.vue';
import { smartSentry } from '/@/lib/smart-sentry';
const route = useRoute();
const activeKey = ref(1);
const helpDocDetail = ref({});
onMounted(() => {
if (route.query.helpDocId) {
queryHelpDocDetail();
}
});
const helpDocViewRecordListRef = ref();
// 查询详情
async function queryHelpDocDetail() {
try {
SmartLoading.show();
const result = await helpDocApi.view(route.query.helpDocId);
helpDocDetail.value = result.data;
helpDocViewRecordListRef.value.onSearch();
} catch (err) {
smartSentry.captureError(err);
} finally {
SmartLoading.hide();
}
}
// 预览附件
const filePreviewRef = ref();
function onPrevFile(fileItem) {
filePreviewRef.value.showPreview(fileItem);
}
// 打印
function print() {
let bdhtml = window.document.body.innerHTML;
let sprnstr = '<!--startprint-->'; //必须在页面添加<!--startprint-->和<!--endprint-->而且需要打印的内容必须在它们之间
let eprnstr = '<!--endprint-->';
let prnhtml = bdhtml.substr(bdhtml.indexOf(sprnstr));
prnhtml = prnhtml.substring(0, prnhtml.indexOf(eprnstr));
let newWin = window.open(''); //新打开一个空窗口
newWin.document.body.innerHTML = prnhtml;
newWin.document.close(); //在IE浏览器中使用必须添加这一句
newWin.focus(); //在IE浏览器中使用必须添加这一句
newWin.print(); //打印
newWin.close(); //关闭窗口
}
</script>
<style lang="less" scoped>
:deep(.ant-descriptions-item-content) {
flex: 1;
overflow: hidden;
}
.file-list {
width: 100%;
display: flex;
flex-wrap: wrap;
.file-item {
display: block;
margin-right: 10px;
}
}
.visible-list {
display: flex;
flex-wrap: wrap;
.visible-item {
margin-right: 10px;
color: #666;
}
}
.content-header {
.content-header-title {
margin: 10px 0px;
font-size: 20px;
font-weight: bold;
text-align: center;
}
.content-header-info {
margin: 10px 0px;
font-size: 14px;
color: #888;
text-align: center;
span {
margin: 0 10px;
cursor: pointer;
}
}
}
.content-html {
margin-top: 30px;
padding: 0 8px;
line-height: 28px;
font-size: 14px;
img {
max-width: 100%;
}
}
</style>

View File

@@ -1,194 +0,0 @@
<!--
* 登录登出 日志
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-06-02 20:23:08
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
-->
<template>
<a-form class="smart-query-form" v-privilege="'loginLog:query'">
<a-row class="smart-query-form-row">
<a-form-item label="用户名称" class="smart-query-form-item">
<a-input style="width: 300px" v-model:value="queryForm.userName" placeholder="用户名称" />
</a-form-item>
<a-form-item label="用户IP" class="smart-query-form-item">
<a-input style="width: 120px" v-model:value="queryForm.ip" placeholder="IP" />
</a-form-item>
<a-form-item label="时间" class="smart-query-form-item">
<a-range-picker @change="changeCreateDate" v-model:value="createDateRange" :ranges="defaultChooseTimeRange" style="width: 240px" />
</a-form-item>
<a-form-item class="smart-query-form-item smart-margin-left10">
<a-button type="primary" @click="ajaxQuery">
<template #icon>
<ReloadOutlined />
</template>
查询
</a-button>
<a-button @click="resetQuery">
<template #icon>
<SearchOutlined />
</template>
重置
</a-button>
</a-form-item>
</a-row>
</a-form>
<a-card size="small" :bordered="false" :hoverable="true">
<a-row justify="end">
<TableOperator class="smart-margin-bottom5" v-model="columns" :tableId="TABLE_ID_CONST.SUPPORT.LOGIN_LOG" :refresh="ajaxQuery" />
</a-row>
<a-table size="small" :dataSource="tableData" :columns="columns" bordered rowKey="loginLogId" :pagination="false" :loading="tableLoading">
<template #bodyCell="{ text, record, column }">
<template v-if="column.dataIndex === 'loginResult'">
<template v-if="text === LOGIN_RESULT_ENUM.LOGIN_SUCCESS.value">
<a-tag color="success">登录成功</a-tag>
</template>
<template v-if="text === LOGIN_RESULT_ENUM.LOGIN_FAIL.value">
<a-tag color="error">登录失败</a-tag>
</template>
<template v-if="text === LOGIN_RESULT_ENUM.LOGIN_OUT.value">
<a-tag color="processing">退出登录</a-tag>
</template>
</template>
<template v-if="column.dataIndex === 'userAgent'">
<div>{{ record.browser }} / {{ record.os }} / {{ record.device }}</div>
</template>
<template v-if="column.dataIndex === 'userType'">
<span>{{ $smartEnumPlugin.getDescByValue('USER_TYPE_ENUM', text) }}</span>
</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="ajaxQuery"
@showSizeChange="ajaxQuery"
:show-total="(total) => `${total}`"
/>
</div>
</a-card>
</template>
<script setup>
import { onMounted, reactive, ref } from 'vue';
import { PAGE_SIZE_OPTIONS } from '/@/constants/common-const';
import { defaultTimeRanges } from '/@/lib/default-time-ranges';
import uaparser from 'ua-parser-js';
import { LOGIN_RESULT_ENUM } from '/@/constants/support/login-log-const';
import { loginLogApi } from '/@/api/support/login-log/login-log-api';
import { smartSentry } from '/@/lib/smart-sentry';
import TableOperator from '/@/components/support/table-operator/index.vue';
import { TABLE_ID_CONST } from '/@/constants/support/table-id-const';
const columns = ref([
{
title: '用户ID',
dataIndex: 'userId',
width: 70,
},
{
title: '用户名',
dataIndex: 'userName',
ellipsis: true,
},
{
title: '类型',
dataIndex: 'userType',
width: 50,
ellipsis: true,
},
{
title: 'IP',
dataIndex: 'loginIp',
ellipsis: true,
},
{
title: '设备信息',
dataIndex: 'userAgent',
ellipsis: true,
},
{
title: '结果',
dataIndex: 'loginResult',
ellipsis: true,
},
{
title: '备注',
dataIndex: 'remark',
ellipsis: true,
},
{
title: '时间',
dataIndex: 'createTime',
width: 150,
},
]);
const queryFormState = {
userName: '',
ip: '',
startDate: undefined,
endDate: undefined,
pageNum: 1,
pageSize: 10,
};
const queryForm = reactive({ ...queryFormState });
const createDateRange = ref([]);
const defaultChooseTimeRange = defaultTimeRanges;
// 时间变动
function changeCreateDate(dates, dateStrings) {
queryForm.startDate = dateStrings[0];
queryForm.endDate = dateStrings[1];
}
const tableLoading = ref(false);
const tableData = ref([]);
const total = ref(0);
function resetQuery() {
Object.assign(queryForm, queryFormState);
createDateRange.value = [];
ajaxQuery();
}
async function ajaxQuery() {
try {
tableLoading.value = true;
let responseModel = await loginLogApi.queryList(queryForm);
for (const e of responseModel.data.list) {
if (!e.userAgent) {
continue;
}
let ua = uaparser(e.userAgent);
e.browser = ua.browser.name;
e.os = ua.os.name;
e.device = ua.device.vendor ? ua.device.vendor + ua.device.model : '';
}
const list = responseModel.data.list;
total.value = responseModel.data.total;
tableData.value = list;
} catch (e) {
smartSentry.captureError(e);
} finally {
tableLoading.value = false;
}
}
onMounted(ajaxQuery);
</script>

View File

@@ -1,139 +0,0 @@
<!--
* 操作记录 详情
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-06-02 20:23:08
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
-->
<template>
<a-modal :visible="visible" title="请求详情" width="60%" :footer="null" @cancel="close">
<div class="info-box">
<a-row class="smart-margin-top10">
<a-col :span="16">
<a-row class="detail-info">
<a-col :span="12"> 请求地址 {{ detail.url }}</a-col>
<a-col :span="12"> 请求日期 {{ detail.createTime }}</a-col>
</a-row>
<a-row class="detail-info">
<a-col> 请求方法 {{ detail.method }}</a-col>
</a-row>
<a-row class="detail-info">
<a-col :span="12"> 用户id{{ detail.operateUserId }}</a-col>
<a-col :span="12"> 用户名称 {{ detail.operateUserName }}</a-col>
<a-col :span="12"> 请求内容 {{ detail.module }} - {{ detail.content }}</a-col>
</a-row>
</a-col>
<a-col :span="8">
<p class="detail-right-title">请求状态</p>
<p :class="['detail-right', detail.successFlag ? 'success' : 'error']">
{{ detail.successFlag ? '成功' : '失败' }}
</p>
</a-col>
</a-row>
</div>
<div class="info-box">
<h4>请求参数</h4>
<JsonViewer :value="detail.param ? JSON.parse(detail.param) : ''" theme="jv-dark" copyable boxed sort />
</div>
<div class="info-box">
<h4>请求参数</h4>
<div>
{{ detail.failReason }}
</div>
</div>
</a-modal>
</template>
<script setup>
import { reactive, ref } from 'vue';
import { JsonViewer } from 'vue3-json-viewer';
import { operateLogApi } from '/@/api/support/operate-log/operate-log-api';
import { smartSentry } from '/@/lib/smart-sentry';
import { SmartLoading } from '/@/components/framework/smart-loading';
defineExpose({
show,
});
const visible = ref(false);
function show(operateLogId) {
visible.value = true;
clear(detail);
getDetail(operateLogId);
}
const clear = (info) => {
const keys = Object.keys(info);
let obj = {};
keys.forEach((item) => {
obj[item] = '';
});
Object.assign(info, obj);
};
function close() {
visible.value = false;
}
let detail = reactive({
param: '',
url: '',
});
async function getDetail(operateLogId) {
try {
SmartLoading.show();
let res = await operateLogApi.detail(operateLogId);
detail = Object.assign(detail, res.data);
} catch (e) {
smartSentry.captureError(e);
} finally {
SmartLoading.hide();
}
}
</script>
<style scoped lang="less">
.detail-title {
display: flex;
align-items: center;
font-size: 20px;
font-weight: bold;
}
.info-box {
border-bottom: 1px solid #f0f0f0;
padding: 10px 8px;
}
.detail-info {
.ant-col {
line-height: 1.46;
margin-bottom: 12px;
padding-right: 5px;
}
}
.detail-right-title {
text-align: right;
color: grey;
}
:deep(.ant-modal-body) {
padding: 10px !important;
}
.detail-right {
padding-left: 5px;
font-size: 20px;
font-weight: bold;
text-align: right;
}
.success {
color: @success-color;
}
.error {
color: @error-color;
}
</style>

View File

@@ -1,220 +0,0 @@
<!--
* 操作记录 列表
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-06-02 20:23:08
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
-->
<template>
<a-form class="smart-query-form" v-privilege="'operateLog:query'">
<a-row class="smart-query-form-row">
<a-form-item label="用户名称" class="smart-query-form-item">
<a-input style="width: 300px" v-model:value="queryForm.userName" placeholder="用户名称" />
</a-form-item>
<a-form-item label="请求时间" class="smart-query-form-item">
<a-range-picker @change="changeCreateDate" v-model:value="createDateRange" :ranges="defaultChooseTimeRange" style="width: 240px" />
</a-form-item>
<a-form-item label="快速筛选" class="smart-query-form-item">
<a-radio-group v-model:value="queryForm.successFlag" @change="ajaxQuery">
<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-group>
</a-form-item>
<a-form-item class="smart-query-form-item smart-margin-left10">
<a-button type="primary" @click="ajaxQuery">
<template #icon>
<ReloadOutlined />
</template>
查询
</a-button>
<a-button @click="resetQuery">
<template #icon>
<SearchOutlined />
</template>
重置
</a-button>
</a-form-item>
</a-row>
</a-form>
<a-card size="small" :bordered="false" :hoverable="true" style="height:100%">
<a-row justify="end">
<TableOperator class="smart-margin-bottom5" v-model="columns" :tableId="TABLE_ID_CONST.SUPPORT.CONFIG" :refresh="ajaxQuery" />
</a-row>
<a-table size="small" :loading="tableLoading" :dataSource="tableData" :columns="columns" bordered rowKey="operateLogId" :pagination="false" >
<template #bodyCell="{ text, record, column }">
<template v-if="column.dataIndex === 'successFlag'">
<a-tag :color="text ? 'success' : 'error'">{{ text ? '成功' : '失败' }}</a-tag>
</template>
<template v-if="column.dataIndex === 'userAgent'">
<div>{{ record.browser }} / {{ record.os }} / {{ record.device }}</div>
</template>
<template v-if="column.dataIndex === 'operateUserType'">
<div>{{ $smartEnumPlugin.getDescByValue('USER_TYPE_ENUM', text) }}</div>
</template>
<template v-else-if="column.dataIndex === 'action'">
<div class="smart-table-operate">
<a-button @click="showDetail(record.operateLogId)" type="link" v-privilege="'operateLog:detail'">详情</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="ajaxQuery"
@showSizeChange="ajaxQuery"
:show-total="(total) => `${total}`"
/>
</div>
<OperateLogDetailModal ref="detailModal" />
</a-card>
</template>
<script setup>
import { onMounted, reactive, ref } from 'vue';
import OperateLogDetailModal from './operate-log-detail-modal.vue';
import { operateLogApi } from '/@/api/support/operate-log/operate-log-api';
import { PAGE_SIZE_OPTIONS } from '/@/constants/common-const';
import { defaultTimeRanges } from '/@/lib/default-time-ranges';
import uaparser from 'ua-parser-js';
import { smartSentry } from '/@/lib/smart-sentry';
import TableOperator from '/@/components/support/table-operator/index.vue';
import { TABLE_ID_CONST } from '/@/constants/support/table-id-const';
const columns = ref([
{
title: '用户',
dataIndex: 'operateUserName',
width: 70,
},
{
title: '类型',
dataIndex: 'operateUserType',
width: 50,
ellipsis: true,
},
{
title: '操作模块',
dataIndex: 'module',
ellipsis: true,
},
{
title: '操作内容',
dataIndex: 'content',
ellipsis: true,
},
{
title: '请求路径',
dataIndex: 'url',
ellipsis: true,
},
{
title: 'IP',
dataIndex: 'ip',
ellipsis: true,
},
{
title: '客户端',
dataIndex: 'userAgent',
ellipsis: true,
},
{
title: '请求方法',
dataIndex: 'method',
ellipsis: true,
},
{
title: '请求结果',
dataIndex: 'successFlag',
width: 80,
},
{
title: '时间',
dataIndex: 'createTime',
width: 150,
},
{
title: '操作',
dataIndex: 'action',
fixed: 'right',
width: 60,
},
]);
const queryFormState = {
userName: '',
successFlag: undefined,
startDate: undefined,
endDate: undefined,
pageNum: 1,
pageSize: 10,
};
const queryForm = reactive({ ...queryFormState });
const createDateRange = ref([]);
const defaultChooseTimeRange = defaultTimeRanges;
// 时间变动
function changeCreateDate(dates, dateStrings) {
queryForm.startDate = dateStrings[0];
queryForm.endDate = dateStrings[1];
}
const tableLoading = ref(false);
const tableData = ref([]);
const total = ref(0);
function resetQuery() {
Object.assign(queryForm, queryFormState);
createDateRange.value = [];
ajaxQuery();
}
async function ajaxQuery() {
try {
tableLoading.value = true;
let responseModel = await operateLogApi.queryList(queryForm);
for (const e of responseModel.data.list) {
if (!e.userAgent) {
continue;
}
let ua = uaparser(e.userAgent);
e.browser = ua.browser.name;
e.os = ua.os.name;
e.device = ua.device.vendor ? ua.device.vendor + ua.device.model : '';
}
const list = responseModel.data.list;
total.value = responseModel.data.total;
tableData.value = list;
} catch (e) {
smartSentry.captureError(e);
} finally {
tableLoading.value = false;
}
}
onMounted(ajaxQuery);
// ---------------------- 详情 ----------------------
const detailModal = ref();
function showDetail(operateLogId) {
detailModal.value.show(operateLogId);
}
</script>

View File

@@ -1,90 +0,0 @@
<!--
* reload 表单
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-07-21 21:55:12
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
-->
<template>
<a-modal :visible="visible" title="执行Reload" ok-text="确认" cancel-text="取消" @ok="onSubmit" @cancel="onClose">
<a-form ref="formRef" :model="form" :rules="rules" :label-col="{ span: 5 }">
<a-form-item label="标签">
<a-input v-model:value="form.tag" :disabled="true" />
</a-form-item>
<a-form-item label="运行标识" name="identification">
<a-input v-model:value="form.identification" placeholder="请输入运行标识" />
</a-form-item>
<a-form-item label="参数" name="args">
<a-input v-model:value="form.args" placeholder="请输入参数" />
</a-form-item>
</a-form>
</a-modal>
</template>
<script setup>
import { message } from 'ant-design-vue';
import { reactive, ref } from 'vue';
import { reloadApi } from '/@/api/support/reload/reload-api';
import { smartSentry } from '/@/lib/smart-sentry';
import { SmartLoading } from '/@/components/framework/smart-loading';
// emit
const emit = defineEmits(['refresh']);
defineExpose({
showModal,
});
// ----------------------- 表单 隐藏 与 显示 ------------------------
// 是否展示
const visible = ref(false);
function showModal(tag) {
form.tag = tag;
form.identification = '';
form.args = '';
visible.value = true;
}
function onClose() {
Object.assign(form, formDefault);
visible.value = false;
}
// 组件
const formRef = ref();
const formDefault = {
tag: '',
identification: '',
args: '',
};
let form = reactive({ ...formDefault });
const rules = {
identification: [{ required: true, message: '请输入运行标识' }],
args: [{ required: true, message: '请输入参数值' }],
};
// ----------------------- 提交 ------------------------
function onSubmit() {
formRef.value
.validate()
.then(async () => {
SmartLoading.show();
try {
await reloadApi.reload(form);
message.success('reload成功');
emit('refresh');
onClose();
} catch (error) {
smartSentry.captureError(error);
} finally {
SmartLoading.hide();
}
})
.catch((error) => {
console.log('error', error);
message.error('参数验证错误,请仔细填写表单数据!');
});
}
</script>

View File

@@ -1,133 +0,0 @@
<!--
* reload
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-07-21 21:55:12
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
-->
<template>
<a-card size="small" :bordered="false" :hoverable="true">
<a-alert>
<template v-slot:message>
<h4>Smart-Reload 心跳服务介绍</h4>
</template>
<template v-slot:description>
<pre>
简介SmartReload是一个可以在不重启进程的情况下动态重新加载配置或者执行某些预先设置的代码
原理
- Java后端会在项目启动的时候开启一个Daemon线程这个Daemon线程会每隔几秒轮询t_smart_item表的状态
- 如果状态标识上次状态标识比较发生变化会将参数传入SmartReload实现类进行自定义操作
用途
· 用于刷新内存中的缓存
· 用于执行某些后门代码
· 用于进行Java热加载前提是类结构不发生变化
· 其他不能重启服务的应用
</pre
>
</template>
</a-alert>
<a-row justify="end">
<TableOperator class="smart-margin-bottom5 smart-margin-top5" v-model="columns" :tableId="TABLE_ID_CONST.SUPPORT.RELOAD" :refresh="ajaxQuery" />
</a-row>
<a-table
size="small"
bordered
class="smart-margin-top10"
:dataSource="tableData"
:loading="tableLoading"
:columns="columns"
rowKey="tag"
:pagination="false"
>
<template #bodyCell="{ text, record, index, column }">
<template v-if="column.dataIndex === 'action'">
<div class="smart-table-operate">
<a-button @click="doReload(record.tag)" v-privilege="'reload:execute'" type="link">执行</a-button>
<a-button @click="showResultList(record.tag)" v-privilege="'reload:result'" type="link">查看结果</a-button>
</div>
</template>
</template>
</a-table>
<DoReloadForm @refresh="ajaxQuery" ref="doReloadForm" />
<ReloadResultList ref="reloadResultList" />
</a-card>
</template>
<script setup>
import { onMounted, reactive, ref } from 'vue';
import DoReloadForm from './do-reload-form-modal.vue';
import ReloadResultList from './reload-result-list.vue';
import { reloadApi } from '/@/api/support/reload/reload-api';
import { smartSentry } from '/@/lib/smart-sentry';
import TableOperator from '/@/components/support/table-operator/index.vue';
import { TABLE_ID_CONST } from '/@/constants/support/table-id-const';
//------------------------ 表格渲染 ---------------------
const columns = ref([
{
title: '标签',
dataIndex: 'tag',
width: 200,
},
{
title: '运行标识',
dataIndex: 'identification',
},
{
title: '参数',
dataIndex: 'args',
},
{
title: '更新时间',
dataIndex: 'updateTime',
width: 150,
},
{
title: '创建时间',
dataIndex: 'createTime',
width: 150,
},
{
title: '操作',
dataIndex: 'action',
fixed: 'right',
width: 150,
},
]);
const tableLoading = ref(false);
const tableData = ref([]);
async function ajaxQuery() {
try {
tableLoading.value = true;
let res = await reloadApi.queryList();
tableData.value = res.data;
} catch (e) {
smartSentry.captureError(e);
} finally {
tableLoading.value = false;
}
}
onMounted(ajaxQuery);
// ------------------------------ 表格操作列: 执行 reload ------------------------------
const doReloadForm = ref();
function doReload(tag) {
doReloadForm.value.showModal(tag);
}
// ------------------------------ 表格操作列: 查看执行结果 ------------------------------
const reloadResultList = ref();
function showResultList(tag) {
reloadResultList.value.showModal(tag);
}
</script>

View File

@@ -1,100 +0,0 @@
<!--
* reload 结果
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-07-21 21:55:12
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
-->
<template>
<a-modal :visible="visible" title="reload结果列表" width="60%" :footer="null" @cancel="onClose">
<a-button type="primary" @click="ajaxQuery" size="small">
<template #icon>
<ReloadOutlined />
</template>
刷新
</a-button>
<a-table :scroll="{ y: 350 }" size="small" bordered rowKey="id" class="smart-margin-top10" :dataSource="tableData" :columns="columns">
<template #bodyCell="{ text, column }">
<template v-if="column.dataIndex === 'result'">
<a-tag :color="text ? 'success' : 'error'">{{ text ? '成功' : '失败' }}</a-tag>
</template>
</template>
<template #expandedRowRender="{ record }">
<pre style="margin: 0; font-size: 12px">
{{ record.exception }}
</pre>
</template>
</a-table>
</a-modal>
</template>
<script setup>
import { reactive, ref } from 'vue';
import { reloadApi } from '/@/api/support/reload/reload-api';
import { smartSentry } from '/@/lib/smart-sentry';
defineExpose({
showModal,
});
// ----------------------- 表单 隐藏 与 显示 ------------------------
// 是否展示
const visible = ref(false);
function showModal(tag) {
queryTag = tag;
ajaxQuery();
visible.value = true;
}
function onClose() {
visible.value = false;
}
//------------------------ 表格查询 ---------------------
let queryTag = '';
const tableLoading = ref(false);
const tableData = ref([]);
async function ajaxQuery() {
try {
tableLoading.value = true;
let res = await reloadApi.queryReloadResult(queryTag);
let count = 1;
for (const item of res.data) {
item.id = count++;
}
tableData.value = res.data;
} catch (e) {
smartSentry.captureError(e);
} finally {
tableLoading.value = false;
}
}
//------------------------ 表格列 ---------------------
const columns = reactive([
{
title: '标签',
dataIndex: 'tag',
},
{
title: '参数',
dataIndex: 'args',
},
{
title: '运行结果',
dataIndex: 'result',
},
{
title: '异常',
dataIndex: 'exception',
ellipsis: true,
},
{
title: '创建时间',
dataIndex: 'createTime',
},
]);
</script>

View File

@@ -1,107 +0,0 @@
<!--
* 生成
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-07-21 21:55:12
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
-->
<template>
<a-modal :visible="visible" title="生成单号" ok-text="生成" cancel-text="关闭" @ok="onSubmit" @cancel="onClose">
<a-form ref="formRef" :model="form" :rules="rules" :label-col="{ span: 5 }">
<a-form-item label="业务">
<a-input v-model:value="form.businessName" :disabled="true" />
</a-form-item>
<a-form-item label="格式">
<a-input v-model:value="form.format" :disabled="true" />
</a-form-item>
<a-form-item label="循环周期">
<a-input v-model:value="form.ruleType" :disabled="true" />
</a-form-item>
<a-form-item label="上次产生单号">
<a-input v-model:value="form.lastNumber" :disabled="true" />
</a-form-item>
<a-form-item label="生成数量" name="count">
<a-input-number v-model:value="form.count" />
</a-form-item>
<a-form-item label="生成结果">
<a-textarea v-model:value="generateResult" :rows="2" />
</a-form-item>
</a-form>
</a-modal>
</template>
<script setup>
import { message } from 'ant-design-vue';
import { reactive, ref } from 'vue';
import { serialNumberApi } from '/@/api/support/serial-number/serial-number-api';
import { SmartLoading } from '/@/components/framework/smart-loading';
import _ from 'lodash';
import { smartSentry } from '/@/lib/smart-sentry';
// emit
const emit = defineEmits(['refresh']);
defineExpose({
showModal,
});
// ----------------------- 表单 隐藏 与 显示 ------------------------
// 是否展示
const visible = ref(false);
function showModal(data) {
form.serialNumberId = data.serialNumberId;
form.businessName = data.businessName;
form.format = data.format;
form.ruleType = data.ruleType;
form.lastNumber = data.lastNumber;
form.count = 1;
generateResult.value = '';
visible.value = true;
}
function onClose() {
visible.value = false;
emit('refresh');
}
// ----------------------- 表单 ------------------------
const rules = {
count: [{ required: true, message: '请输入数量' }],
};
//生成结果
const generateResult = ref('');
// 组件
const formRef = ref();
const form = reactive({
serialNumberId: -1,
businessName: '',
format: '',
ruleType: '',
lastNumber: -1,
count: 1,
});
function onSubmit() {
formRef.value
.validate()
.then(async () => {
SmartLoading.show();
try {
let res = await serialNumberApi.generate(form);
message.success('生成成功');
generateResult.value = _.join(res.data, ', ');
} catch (error) {
smartSentry.captureError(error);
} finally {
SmartLoading.hide();
}
})
.catch((error) => {
console.log('error', error);
message.error('参数验证错误,请仔细填写表单数据!');
});
}
</script>

View File

@@ -1,143 +0,0 @@
<!--
* 单号
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-07-21 21:55:12
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
-->
<template>
<a-card size="small" :bordered="false" :hoverable="true">
<a-alert>
<template v-slot:message>
<h4>SerialNumber 单号生成器介绍</h4>
</template>
<template v-slot:description>
<pre>
简介SerialNumber是一个可以根据不同的日期规则生成一系列特别单号的功能比如订单号合同号采购单号等等
原理内部有三种实现方式 1) 基于内存锁实现 不支持分布式和集群 2) 基于redis锁实现 3) 基于Mysql 锁for update 实现
- 支持随机生成和查询生成记录
- 支持动态配置
</pre
>
</template>
</a-alert>
<a-row justify="end">
<TableOperator class="smart-margin-bottom5 smart-margin-top5" v-model="columns" :tableId="TABLE_ID_CONST.SUPPORT.SERIAL_NUMBER" :refresh="ajaxQuery" />
</a-row>
<a-table
size="small"
:loading="tableLoading"
bordered
class="smart-margin-top10"
:dataSource="tableData"
:columns="columns"
rowKey="tag"
:pagination="false"
>
<template #bodyCell="{ record, column }">
<template v-if="column.dataIndex === 'action'">
<div class="smart-table-operate">
<a-button @click="generate(record)" v-privilege="'support:serial:number:generate'" type="link">生成</a-button>
<a-button @click="showRecord(record.serialNumberId)" v-privilege="'support:serial:number:record'" type="link">查看记录</a-button>
</div>
</template>
</template>
</a-table>
</a-card>
<!---生成表单--->
<SerialNumberGenerateFormModal ref="generateForm" @refresh="ajaxQuery" />
<!---生成记录--->
<SerialNumberRecordList ref="recordList" />
</template>
<script setup>
import { onMounted, reactive, ref } from 'vue';
import SerialNumberGenerateFormModal from './serial-number-generate-form-modal.vue';
import SerialNumberRecordList from './serial-number-record-list.vue';
import { serialNumberApi } from '/@/api/support/serial-number/serial-number-api';
import TableOperator from '/@/components/support/table-operator/index.vue';
import { TABLE_ID_CONST } from '/@/constants/support/table-id-const';
import { smartSentry } from '/@/lib/smart-sentry';
//------------------------ 表格渲染 ---------------------
const columns = ref([
{
title: 'ID',
dataIndex: 'serialNumberId',
},
{
title: '业务',
dataIndex: 'businessName',
},
{
title: '格式',
dataIndex: 'format',
},
{
title: '循环周期',
dataIndex: 'ruleType',
},
{
title: '初始值',
dataIndex: 'initNumber',
},
{
title: '随机增量',
dataIndex: 'stepRandomRange',
},
{
title: '备注',
dataIndex: 'remark',
},
{
title: '上次产生单号',
dataIndex: 'lastNumber',
},
{
title: '上次产生时间',
dataIndex: 'lastTime',
},
{
title: '操作',
dataIndex: 'action',
fixed: 'right',
width: 140,
},
]);
const tableLoading = ref(false);
const tableData = ref([]);
async function ajaxQuery() {
try {
tableLoading.value = true;
let res = await serialNumberApi.getAll();
tableData.value = res.data;
} catch (e) {
smartSentry.captureError(e);
} finally {
tableLoading.value = false;
}
}
onMounted(ajaxQuery);
// ------------------------------ 表格操作列: 生成 ------------------------------
const generateForm = ref();
function generate(record) {
generateForm.value.showModal(record);
}
// ------------------------------ 表格操作列: 查看结果 ------------------------------
const recordList = ref();
function showRecord(serialNumberId) {
recordList.value.showModal(serialNumberId);
}
</script>

View File

@@ -1,115 +0,0 @@
<!--
* 单号 记录
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-07-21 21:55:12
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
-->
<template>
<a-modal :visible="visible" title="每日生成结果记录" width="60%" :footer="null" @cancel="onClose">
<a-table size="small" :dataSource="tableData" :columns="columns" bordered :pagination="false">
<template #bodyCell="{ text, record, column }">
<template v-if="column.dataIndex === 'successFlag'">
<a-tag :color="text ? 'success' : 'error'">{{ text ? '成功' : '失败' }}</a-tag>
</template>
<template v-else-if="column.dataIndex === 'action'">
<a-button @click="showDetail(record.operateLogId)" type="link">详情</a-button>
</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="ajaxQuery"
@showSizeChange="ajaxQuery"
:show-total="(total) => `${total}`"
/>
</div>
</a-modal>
</template>
<script setup>
import { reactive, ref } from 'vue';
import { serialNumberApi } from '/@/api/support/serial-number/serial-number-api';
import { PAGE_SIZE_OPTIONS } from '/@/constants/common-const';
import { smartSentry } from '/@/lib/smart-sentry';
defineExpose({
showModal,
});
// ----------------------- 表单 隐藏 与 显示 ------------------------
// 是否展示
const visible = ref(false);
function showModal(id) {
queryForm.serialNumberId = id;
queryForm.pageNum = 1;
queryForm.pageSize = 10;
ajaxQuery();
visible.value = true;
}
function onClose() {
visible.value = false;
}
// ----------------------- 表格 ------------------------
const columns = reactive([
{
title: '单号ID',
dataIndex: 'serialNumberId',
width: 70,
},
{
title: '日期',
dataIndex: 'recordDate',
},
{
title: '生成数量',
dataIndex: 'count',
},
{
title: '最后更新值',
dataIndex: 'lastNumber',
},
{
title: '上次生成时间',
dataIndex: 'lastTime',
},
]);
const queryForm = reactive({
serialNumberId: -1,
pageNum: 1,
pageSize: 10,
});
const tableLoading = ref(false);
const tableData = ref([]);
const total = ref(0);
async function ajaxQuery() {
try {
tableLoading.value = true;
let responseModel = await serialNumberApi.queryRecord(queryForm);
const list = responseModel.data.list;
total.value = responseModel.data.total;
tableData.value = list;
} catch (e) {
smartSentry.captureError(e);
} finally {
tableLoading.value = false;
}
}
</script>