hotgo2.1.3版本发布

This commit is contained in:
孟帅 2023-02-09 13:53:19 +08:00
parent 255b411eb7
commit 1efbf698e2
30 changed files with 340 additions and 850 deletions

View File

@ -20,7 +20,7 @@
<a href="https://vitejs.dev/" target="_blank">
<img src="https://img.shields.io/badge/vite-%3E2.0.0-yellow" alt="vite">
</a>
<a href="https://raw.githubusercontent.com/tusen-ai/naive-ui/main/LICENSE" target="_blank">
<a href="https://github.com/bufanyun/hotgo/blob/v2.0/LICENSE" target="_blank">
<img src="https://img.shields.io/badge/license-MIT-success" alt="license">
</a>
</p>
@ -47,20 +47,20 @@
2. 部门管理:配置系统组织机构(公司、部门、岗位),树结构展现支持数据权限。
3. 岗位管理:配置系统用户所属担任职务。
4. 菜单管理:配置系统菜单,操作权限,按钮权限标识等。
5. 角色管理:角色菜单权限分配、设置角色按机构进行数据范围权限划分。
5. 角色管理:角色菜单权限分配、设置角色按机构或按上下级关系进行数据范围权限划分。
6. 字典管理:对系统中经常使用的一些较为固定的数据进行维护。
7. 配置管理:对系统动态配置常用参数。
8. 操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。
9. 登录日志:系统登录日志记录查询包含登录异常。
10. 调度日志:服务端运行所产生的警告、异常、崩溃日志的详细数据和堆栈信息。
10. 服务日志:服务端运行所产生的警告、异常、崩溃日志的详细数据和堆栈信息。
11. 在线用户:当前系统中活跃用户状态监控。
12. 定时任务:在线(添加、修改、删除)任务调度包含执行结果日志。
13. 代码生成支持自动化生成前后端代码。CURD关联表、树表、消息队列、定时任务一键生成等。
14. 服务监控监视当前系统CPU、内存、磁盘、网络、堆栈等相关信息。
15. 附件管理:文件上传,多种上传方式适配。
16. 消息队列:同时兼容 kafka、redis、rocketmq,一键配置切换到自己想用的MQ。
17. 通知公告采用websocket及时推送在线用户最新公告
18. 地区编码:整合国内通用省市区编码,运用于项目于一身。
16. 消息队列:同时兼容 kafka、redis、rocketmq、磁盘队列,一键配置切换到场景适用的MQ。
17. 通知公告采用websocket实时推送在线用户最新通知、公告、私信消息
18. 地区编码:整合国内通用省市区编码,运用于项目于一身,支持动态省市区选项
19. 常用工具:集成常用的工具包和命令行工具,可以快速开发自定义命令行,多种启动入口。
@ -201,7 +201,7 @@ web端
* 本项目包含的第三方源码和二进制文件之版权信息另行标注。
* 版权所有Copyright © 2020-2024 by Ms (https://github.com/bufanyun/hotgo)
* 版权所有Copyright © 2020-2023 by Ms (https://github.com/bufanyun/hotgo)
* All rights reserved。
@ -223,7 +223,7 @@ web端
## License
[MIT © HotGo-2021](./LICENSE)
[MIT © HotGo-2023](./LICENSE)

View File

@ -49,36 +49,11 @@ func TableColumns(ctx context.Context, in sysin.GenCodesColumnListInp) (fields [
func TableSelects(ctx context.Context, in sysin.GenCodesSelectsInp) (res *sysin.GenCodesSelectsModel, err error) {
res = new(sysin.GenCodesSelectsModel)
for k, v := range consts.GenCodesTypeNameMap {
row := &sysin.GenTypeSelect{
Value: k,
Name: v,
Label: v,
Templates: make(form.Selects, 0),
}
confName, ok := consts.GenCodesTypeConfMap[k]
if ok {
var temps []*model.GenerateAppCrudTemplate
err = g.Cfg().MustGet(ctx, "hggen.application."+confName+".templates").Scan(&temps)
if err != nil {
return
}
if len(temps) > 0 {
for index, temp := range temps {
row.Templates = append(row.Templates, &form.Select{
Value: index,
Label: temp.Group,
Name: temp.Group,
})
}
sort.Sort(row.Templates)
}
}
res.GenType = append(res.GenType, row)
res.GenType, err = GenTypeSelect(ctx)
if err != nil {
return
}
sort.Sort(res.GenType)
res.Db = DbSelect(ctx)
for k, v := range consts.GenCodesStatusNameMap {
@ -126,9 +101,12 @@ func TableSelects(ctx context.Context, in sysin.GenCodesSelectsInp) (res *sysin.
}
sort.Sort(res.FormRole)
dictMode, _ := service.SysDictType().TreeSelect(ctx, sysin.DictTreeSelectInp{})
dictMode, err := service.SysDictType().TreeSelect(ctx, sysin.DictTreeSelectInp{})
if err != nil {
return
}
res.DictMode = dictMode
for _, v := range views.WhereModes {
res.WhereMode = append(res.WhereMode, &form.Select{
Value: v,
@ -140,6 +118,42 @@ func TableSelects(ctx context.Context, in sysin.GenCodesSelectsInp) (res *sysin.
return
}
// GenTypeSelect 获取生成类型选项
func GenTypeSelect(ctx context.Context) (res sysin.GenTypeSelects, err error) {
for k, v := range consts.GenCodesTypeNameMap {
row := &sysin.GenTypeSelect{
Value: k,
Name: v,
Label: v,
Templates: make(form.Selects, 0),
}
confName, ok := consts.GenCodesTypeConfMap[k]
if ok {
var temps []*model.GenerateAppCrudTemplate
err = g.Cfg().MustGet(ctx, "hggen.application."+confName+".templates").Scan(&temps)
if err != nil {
return
}
if len(temps) > 0 {
for index, temp := range temps {
row.Templates = append(row.Templates, &form.Select{
Value: index,
Label: temp.Group,
Name: temp.Group,
})
}
sort.Sort(row.Templates)
}
}
res = append(res, row)
}
sort.Sort(res)
return
}
// DbSelect db选项
func DbSelect(ctx context.Context) (res form.Selects) {
dbs := g.Cfg().MustGet(ctx, "hggen.selectDbs")

View File

@ -146,14 +146,17 @@ func (s *sAdminNotice) Status(ctx context.Context, in adminin.NoticeStatusInp) (
}
// MaxSort 最大排序
func (s *sAdminNotice) MaxSort(ctx context.Context, in adminin.NoticeMaxSortInp) (*adminin.NoticeMaxSortModel, error) {
var res adminin.NoticeMaxSortModel
if err := s.Model(ctx).Order("sort desc").Scan(&res); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return nil, err
func (s *sAdminNotice) MaxSort(ctx context.Context, in adminin.NoticeMaxSortInp) (res *adminin.NoticeMaxSortModel, err error) {
if err = dao.AdminNotice.Ctx(ctx).Order("sort desc").Scan(&res); err != nil {
return
}
if res == nil {
res = new(adminin.NoticeMaxSortModel)
}
res.Sort = form.DefaultMaxSort(ctx, res.Sort)
return &res, nil
return
}
// View 获取指定字典类型信息

View File

@ -13,6 +13,7 @@ import (
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
"hotgo/internal/consts"
"hotgo/internal/dao"
@ -22,6 +23,13 @@ import (
"hotgo/internal/service"
)
var MaskDemoField = []string{
"smtpUser", "smtpPass", // 邮箱
"uploadUCloudPublicKey", "uploadUCloudPrivateKey", // 云存储
"geoAmapWebKey", // 地图
"smsAliyunAccessKeyID", "smsAliyunAccessKeySecret", // 短信
}
type sSysConfig struct{}
func NewSysConfig() *sSysConfig {
@ -156,7 +164,12 @@ func (s *sSysConfig) GetConfigByGroup(ctx context.Context, in sysin.GetConfigInp
return nil, err
}
res.List[v.Key] = val
if isDemo.Bool() && (v.Key == "smtpUser" || v.Key == "smtpPass") {
//if isDemo.Bool() && (v.Key == "smtpUser" || v.Key == "smtpPass") {
// res.List[v.Key] = consts.DemoTips
// res.List[v.Key] = consts.DemoTips
//}
if isDemo.Bool() && gstr.InArray(MaskDemoField, v.Key) {
res.List[v.Key] = consts.DemoTips
res.List[v.Key] = consts.DemoTips
}

View File

@ -162,6 +162,34 @@ func (s *sSysGenCodes) List(ctx context.Context, in sysin.GenCodesListInp) (list
return list, totalCount, err
}
typeSelect, err := hggen.GenTypeSelect(ctx)
if err != nil {
return
}
getTemplateGroup := func(row *sysin.GenCodesListModel) string {
if row == nil {
return ""
}
for _, v := range typeSelect {
if v.Value == int(row.GenType) {
for index, template := range v.Templates {
if index == row.GenTemplate {
return template.Label
}
}
}
}
return ""
}
if len(list) > 0 {
for _, v := range list {
v.GenTemplateGroup = getTemplateGroup(v)
}
}
return list, totalCount, err
}

View File

@ -55,6 +55,7 @@ type GenCodesListInp struct {
type GenCodesListModel struct {
entity.SysGenCodes
GenTemplateGroup string `json:"genTemplateGroup" dc:"生成模板组名"`
}
// GenCodesStatusInp 更新状态

File diff suppressed because one or more lines are too long

2
web/.gitignore vendored
View File

@ -4,6 +4,8 @@ node_modules
/dist
dist.zip
dist_electron
package-lock.json
yarn.lock
# local env files
.env.local

View File

@ -91,7 +91,11 @@ export const notificationStore = defineStore({
});
},
getUnreadCount() {
return this.notifyUnread + this.noticeUnread + this.letterUnread;
const count = this.notifyUnread + this.noticeUnread + this.letterUnread;
if (count > 0) {
return count;
}
return 0;
},
},
});

View File

@ -127,7 +127,6 @@ export function encodeParams(obj) {
* @param obj1
*/
export function copyObj(obj2: any, obj1: any) {
console.log('obj1:' + JSON.stringify(obj1));
for (const key in obj1) {
if (obj2[key] !== undefined) {
obj2[key] = obj1[key];

View File

@ -1,10 +1,17 @@
import { h, unref } from 'vue';
import type { App, Plugin } from 'vue';
import { NIcon, NTag } from 'naive-ui';
import { NIcon, NTag, NTooltip } from 'naive-ui';
import { PageEnum } from '@/enums/pageEnum';
import { isObject } from './is/index';
import { cloneDeep } from 'lodash-es';
export const renderTooltip = (trigger, content) => {
return h(NTooltip, null, {
trigger: () => trigger,
default: () => content,
});
};
/**
* render
* */

View File

@ -134,6 +134,7 @@
<template v-if="formParams.type === 1">
<n-input
type="textarea"
:autosize="{ minRows: 3, maxRows: 30 }"
placeholder="请输入通知内容"
v-model:value="formParams.content"
/>

View File

@ -174,6 +174,26 @@
},
width: 180,
},
{
title: '生成模板',
key: 'genTemplate',
render(row) {
return h(
NTag,
{
style: {
marginRight: '6px',
},
type: 'default',
bordered: false,
},
{
default: () => row.genTemplateGroup,
}
);
},
width: 120,
},
{
title: '实体命名',
key: 'varName',

View File

@ -1,6 +1,10 @@
<template>
<n-spin :show="loading">
<n-empty v-show="dataSource.list?.length === 0" description="无数据" />
<n-empty
v-show="dataSource.list?.length === undefined || dataSource.list?.length === 0"
description="无数据"
/>
<n-list hoverable clickable class="list-item">
<n-list-item v-for="item in dataSource.list" :key="item.id" @click="UnRead(item)">
<n-thing
@ -30,7 +34,7 @@
</template>
<template #footer>
<span v-html="item.content"></span>
<span v-html="filters(item.content)"></span>
</template>
</n-thing>
</n-list-item>
@ -139,6 +143,10 @@
onMounted(() => {
loadDataSource();
});
function filters(data) {
return data.replace(/\n/g, '<br>');
}
</script>
<style lang="less" scoped>

View File

@ -1,5 +1,5 @@
import { h, ref } from 'vue';
import { NAvatar, NImage, NTag, NSwitch, NRate } from 'naive-ui';
import { NAvatar, NImage, NTag, NSwitch, NRate, NButton } from 'naive-ui';
import { cloneDeep } from 'lodash-es';
import { FormSchema } from '@/components/Form';
import { Dicts } from '@/api/dict/dict';
@ -9,6 +9,8 @@ import { getFileExt } from '@/utils/urlUtils';
import { defRangeShortcuts, defShortcuts, formatToDate } from '@/utils/dateUtil';
import { format } from 'date-fns';
import { getOptionLabel, getOptionTag, Options, errorImg } from '@/utils/hotgo';
import { renderIcon, renderTooltip } from '@/utils';
import { HelpCircleOutline } from '@vicons/ionicons5';
export interface State {
id: number;
@ -99,7 +101,23 @@ export const columns = [
width: 80,
},
{
title: '链路ID',
title(_column) {
return renderTooltip(
h(
NButton,
{
ghost: true,
strong: true,
tertiary: true,
size: 'small',
text: true,
iconPlacement: 'right',
},
{ default: () => '链路ID', icon: renderIcon(HelpCircleOutline) }
),
'hotgo默认支持链路追踪如果是web请求产生的日志则还可以关联对应的访问日志'
);
},
key: 'traceId',
width: 280,
render(row) {

View File

@ -117,6 +117,8 @@
import { statusActions, statusOptions } from '@/enums/optionsiEnum';
import { Delete, Edit, getDeptList, Status } from '@/api/org/dept';
import { cloneDeep } from 'lodash-es';
import { renderIcon, renderTooltip } from '@/utils';
import { HelpCircleOutline } from '@vicons/ionicons5';
const rules = {
name: {
@ -199,7 +201,23 @@
const data = ref([]);
const columns: DataTableColumns<RowData> = [
{
title: '部门',
title(_column) {
return renderTooltip(
h(
NButton,
{
ghost: true,
strong: true,
tertiary: true,
size: 'small',
text: true,
iconPlacement: 'right',
},
{ default: () => '部门', icon: renderIcon(HelpCircleOutline) }
),
'支持上下级部门,点击列表中左侧 > 按钮可展开下级部门列表'
);
},
key: 'name',
render(row) {
return h(

View File

@ -290,7 +290,6 @@
negativeText: '取消',
onPositiveClick: () => {
Delete({ id: checkedIds.value }).then((_res) => {
console.log('_res:' + JSON.stringify(_res));
message.success('操作成功');
reloadTable();
});

View File

@ -1,13 +1,30 @@
import { h } from 'vue';
import { NTag } from 'naive-ui';
import { NTag, NButton } from 'naive-ui';
import { HelpCircleOutline } from '@vicons/ionicons5';
import { renderTooltip, renderIcon } from '@/utils';
export const columns = [
// {
// title: '角色ID',
// key: 'id',
// },
{
title: '角色',
title(_column) {
return renderTooltip(
h(
NButton,
{
ghost: true,
strong: true,
tertiary: true,
size: 'small',
text: true,
iconPlacement: 'right',
},
{ default: () => '角色', icon: renderIcon(HelpCircleOutline) }
),
'支持上下级角色,点击列表中左侧 > 按钮可展开下级角色列表'
);
},
key: 'name',
render(row) {
return h(

View File

@ -1,133 +0,0 @@
<template>
<n-grid cols="2 s:2 m:2 l:3 xl:3 2xl:3" responsive="screen">
<n-grid-item>
<n-form :label-width="80" :model="formValue" :rules="rules" ref="formRef">
<n-form-item label="网站名称" path="name">
<n-input v-model:value="formValue.name" placeholder="请输入网站名称" />
</n-form-item>
<n-form-item label="备案编号" path="icpCode">
<n-input placeholder="请输入备案编号" v-model:value="formValue.icpCode" />
</n-form-item>
<n-form-item label="联系电话" path="mobile">
<n-input placeholder="请输入联系电话" v-model:value="formValue.mobile" />
</n-form-item>
<n-form-item label="联系地址" path="address">
<n-input v-model:value="formValue.address" type="textarea" placeholder="请输入联系地址" />
</n-form-item>
<n-form-item label="登录验证码" path="loginCode">
<n-radio-group v-model:value="formValue.loginCode" name="loginCode">
<n-space>
<n-radio :value="1">开启</n-radio>
<n-radio :value="0">关闭</n-radio>
</n-space>
</n-radio-group>
</n-form-item>
<n-form-item label="网站开启访问" path="systemOpen">
<n-switch
size="large"
v-model:value="formValue.systemOpen"
@update:value="systemOpenChange"
/>
</n-form-item>
<n-form-item label="网站关闭提示" path="closeText">
<n-input
v-model:value="formValue.closeText"
type="textarea"
placeholder="请输入网站关闭提示"
/>
</n-form-item>
<div>
<n-space>
<n-button type="primary" @click="formSubmit">更新基本信息</n-button>
</n-space>
</div>
</n-form>
</n-grid-item>
</n-grid>
</template>
<script lang="ts">
import { defineComponent, reactive, ref, toRefs } from 'vue';
import { useDialog, useMessage } from 'naive-ui';
const rules = {
name: {
required: true,
message: '请输入网站名称',
trigger: 'blur',
},
mobile: {
required: true,
message: '请输入联系电话',
trigger: 'input',
},
};
export default defineComponent({
setup() {
const formRef: any = ref(null);
const message = useMessage();
const dialog = useDialog();
const state = reactive({
formValue: {
name: '',
mobile: '',
icpCode: '',
address: '',
loginCode: 0,
closeText:
'网站维护中,暂时无法访问!本网站正在进行系统维护和技术升级,网站暂时无法访问,敬请谅解!',
systemOpen: true,
},
});
function systemOpenChange(value) {
if (!value) {
dialog.warning({
title: '提示',
content: '您确定要关闭系统访问吗?该操作立马生效,请慎重操作!',
positiveText: '确定',
negativeText: '取消',
onPositiveClick: () => {
message.success('操作成功');
},
onNegativeClick: () => {
state.formValue.systemOpen = true;
},
});
}
}
function formSubmit() {
formRef.value.validate((errors) => {
if (!errors) {
message.success('验证成功');
} else {
message.error('验证失败,请填写完整信息');
}
});
}
function resetForm() {
formRef.value.restoreValidation();
}
return {
formRef,
...toRefs(state),
rules,
formSubmit,
resetForm,
systemOpenChange,
};
},
});
</script>

View File

@ -1,79 +0,0 @@
<template>
<n-grid cols="2 s:2 m:2 l:3 xl:3 2xl:3" responsive="screen">
<n-grid-item>
<n-form :label-width="120" :model="formValue" :rules="rules" ref="formRef">
<n-form-item label="发件人邮箱" path="originator">
<n-input v-model:value="formValue.originator" placeholder="请输入发件人邮箱" />
</n-form-item>
<n-form-item label="SMTP服务器地址">
<n-input placeholder="请输入SMTP服务器地址" />
</n-form-item>
<n-form-item label="SMTP服务器端口">
<n-input placeholder="请输入SMTP服务器端口" />
</n-form-item>
<n-form-item label="SMTP用户名">
<n-input placeholder="请输入SMTP用户名" />
</n-form-item>
<n-form-item label="SMTP密码">
<n-input type="password" placeholder="请输入SMTP密码" />
</n-form-item>
<n-form-item label="邮件测试">
<n-button>邮件测试</n-button>
</n-form-item>
<div>
<n-space>
<n-button type="primary" @click="formSubmit">更新邮件信息</n-button>
</n-space>
</div>
</n-form>
</n-grid-item>
</n-grid>
</template>
<script lang="ts">
import { defineComponent, reactive, ref, toRefs } from 'vue';
import { useMessage } from 'naive-ui';
const rules = {
originator: {
required: true,
message: '请输入发件人邮箱',
trigger: 'blur',
},
};
export default defineComponent({
setup() {
const formRef: any = ref(null);
const message = useMessage();
const state = reactive({
formValue: {
originator: '',
},
});
function formSubmit() {
formRef.value.validate((errors) => {
if (!errors) {
message.success('验证成功');
} else {
message.error('验证失败,请填写完整信息');
}
});
}
return {
formRef,
...toRefs(state),
rules,
formSubmit,
};
},
});
</script>

View File

@ -1,198 +0,0 @@
<template>
<n-grid cols="2 s:2 m:2 l:3 xl:3 2xl:3" responsive="screen">
<n-grid-item>
<n-form :label-width="120" :model="formValue" :rules="rules" ref="formRef">
<n-form-item label="商品图片(大)">
<n-space align="center">
<span>宽度</span>
<n-input
v-model:value="formValue.bigWidth"
style="width: 80px"
placeholder="宽度像素"
/>
<span>高度</span>
<n-input
v-model:value="formValue.bigHeight"
style="width: 80px"
placeholder="高度像素"
/>
</n-space>
</n-form-item>
<n-form-item label="商品图片(小)">
<n-space align="center">
<span>宽度</span>
<n-input
v-model:value="formValue.smallWidth"
style="width: 80px"
placeholder="宽度像素"
/>
<span>高度</span>
<n-input
v-model:value="formValue.smallHeight"
style="width: 80px"
placeholder="高度像素"
/>
</n-space>
</n-form-item>
<n-form-item label="水印透明度" path="watermarkClarity">
<n-input-number
v-model:value="formValue.watermarkClarity"
:show-button="false"
placeholder="请输入水印透明度"
/>
</n-form-item>
<n-form-item label="水印图片" path="watermarkClarity">
<n-upload action="http://www.mocky.io/v2/5e4bafc63100007100d8b70f">
<n-button>上传文件</n-button>
</n-upload>
</n-form-item>
<n-form-item label="水印位置" path="watermarkPlace">
<n-select
placeholder="请选择价格精确方式"
:options="watermarkPlaceList"
v-model:value="formValue.watermarkPlace"
/>
</n-form-item>
<n-form-item label="价格精确位数" path="pricePreciseNum">
<n-select
placeholder="请选择价格精确位数"
:options="pricePreciseNumList"
v-model:value="formValue.pricePreciseNum"
/>
</n-form-item>
<n-form-item label="价格精确方式" path="pricePrecise">
<n-select
placeholder="请选择价格精确方式"
:options="pricePreciseList"
v-model:value="formValue.pricePrecise"
/>
</n-form-item>
<n-form-item label="前台显示市场价" path="isMarketPrice">
<n-switch size="large" v-model:value="formValue.isMarketPrice" />
</n-form-item>
<div>
<n-space>
<n-button type="primary" @click="formSubmit">更新显示信息</n-button>
</n-space>
</div>
</n-form>
</n-grid-item>
</n-grid>
</template>
<script lang="ts">
import { defineComponent, reactive, ref, toRefs } from 'vue';
import { useMessage } from 'naive-ui';
const rules = {
name: {
required: true,
message: '请输入网站名称',
trigger: 'blur',
},
mobile: {
required: true,
message: '请输入联系电话',
trigger: 'input',
},
};
const watermarkPlaceList = [
{
label: '左上',
value: 1,
},
{
label: '右上',
value: 2,
},
{
label: '居中',
value: 3,
},
{
label: '右下',
value: 4,
},
];
const pricePreciseNumList = [
{
label: '2位',
value: 1,
},
{
label: '3位',
value: 2,
},
{
label: '4位',
value: 3,
},
];
const pricePreciseList = [
{
label: '四舍五入',
value: 1,
},
{
label: '向上取整',
value: 2,
},
{
label: '向下取整',
value: 3,
},
];
export default defineComponent({
setup() {
const formRef: any = ref(null);
const message = useMessage();
const state = reactive({
formValue: {
bigWidth: '',
bigHeight: '',
smallWidth: '',
smallHeight: '',
watermarkClarity: null,
pricePrecise: 1,
isMarketPrice: true,
pricePreciseNum: null,
},
});
function formSubmit() {
formRef.value.validate((errors) => {
if (!errors) {
message.success('验证成功');
} else {
message.error('验证失败,请填写完整信息');
}
});
}
function resetForm() {
formRef.value.restoreValidation();
}
return {
formRef,
...toRefs(state),
pricePreciseList,
watermarkPlaceList,
pricePreciseNumList,
rules,
formSubmit,
resetForm,
};
},
});
</script>

View File

@ -1,95 +0,0 @@
<template>
<div>
<n-grid :x-gap="24">
<n-grid-item span="6">
<n-card :bordered="false" size="small" class="proCard">
<n-thing
class="thing-cell"
v-for="item in typeTabList"
:key="item.key"
:class="{ 'thing-cell-on': type === item.key }"
@click="switchType(item)"
>
<template #header>{{ item.name }}</template>
<template #description>{{ item.desc }}</template>
</n-thing>
</n-card>
</n-grid-item>
<n-grid-item span="18">
<n-card :bordered="false" size="small" :title="typeTitle" class="proCard">
<BasicSetting v-if="type === 1" />
<RevealSetting v-if="type === 2" />
<EmailSetting v-if="type === 3" />
</n-card>
</n-grid-item>
</n-grid>
</div>
</template>
<script lang="ts">
import { defineComponent, reactive, toRefs } from 'vue';
import BasicSetting from './BasicSetting.vue';
import RevealSetting from './RevealSetting.vue';
import EmailSetting from './EmailSetting.vue';
const typeTabList = [
{
name: '基本设置',
desc: '系统常规设置',
key: 1,
},
{
name: '显示设置',
desc: '系统显示设置',
key: 2,
},
{
name: '邮件设置',
desc: '系统邮件设置',
key: 3,
},
];
export default defineComponent({
components: { BasicSetting, RevealSetting, EmailSetting },
setup() {
const state = reactive({
type: 1,
typeTitle: '基本设置',
});
function switchType(e) {
state.type = e.key;
state.typeTitle = e.name;
}
return {
...toRefs(state),
switchType,
typeTabList,
};
},
});
</script>
<style lang="less" scoped>
.thing-cell {
margin: 0 -16px 10px;
padding: 5px 16px;
&:hover {
background: #f3f3f3;
cursor: pointer;
}
}
.thing-cell-on {
background: #f0faff;
color: #2d8cf0;
::v-deep(.n-thing-main .n-thing-header .n-thing-header__title) {
color: #2d8cf0;
}
&:hover {
background: #f0faff;
}
}
</style>

View File

@ -157,17 +157,10 @@
function formSubmit() {
formRef.value.validate((errors) => {
if (!errors) {
console.log('formValue.value:' + JSON.stringify(formValue.value));
updateConfig({ group: group.value, list: formValue.value })
.then((res) => {
console.log('res:' + JSON.stringify(res));
message.success('更新成功');
load();
})
.catch((error) => {
message.error(error.toString());
});
updateConfig({ group: group.value, list: formValue.value }).then((_res) => {
message.success('更新成功');
load();
});
} else {
message.error('验证失败,请填写完整信息');
}
@ -192,14 +185,10 @@
new Promise((_resolve, _reject) => {
getConfig({ group: group.value })
.then((res) => {
show.value = false;
// state.formValue.watermarkClarity = res;
formValue.value = res.list;
console.log('res:' + JSON.stringify(res));
})
.catch((error) => {
.finally(() => {
show.value = false;
message.error(error.toString());
});
});
}

View File

@ -23,7 +23,19 @@
</n-form-item>
<n-form-item label="SMTP密码" path="smtpPass">
<n-input v-model:value="formValue.smtpPass" placeholder="" type="password" />
<n-input
v-model:value="formValue.smtpPass"
placeholder=""
type="password"
show-password-on="click"
>
<template #password-visible-icon>
<n-icon :size="16" :component="GlassesOutline" />
</template>
<template #password-invisible-icon>
<n-icon :size="16" :component="Glasses" />
</template>
</n-input>
<template #feedback>填写您的密码</template>
</n-form-item>
@ -120,6 +132,7 @@
import { onMounted, ref } from 'vue';
import { useMessage } from 'naive-ui';
import { getConfig, sendTestEmail, updateConfig } from '@/api/sys/config';
import { GlassesOutline, Glasses } from '@vicons/ionicons5';
const group = ref('smtp');
const show = ref(false);
@ -194,7 +207,6 @@
new Promise((_resolve, _reject) => {
getConfig({ group: group.value })
.then((res) => {
show.value = false;
res.list.smtpTemplate = JSON.parse(res.list.smtpTemplate);
formValue.value = res.list;
})

View File

@ -5,7 +5,19 @@
<n-grid-item>
<n-form :label-width="100" :model="formValue" :rules="rules" ref="formRef">
<n-form-item label="高德Web服务key" path="geoAmapWebKey">
<n-input v-model:value="formValue.geoAmapWebKey" placeholder="" type="password" />
<n-input
v-model:value="formValue.geoAmapWebKey"
placeholder=""
type="password"
show-password-on="click"
>
<template #password-visible-icon>
<n-icon :size="16" :component="GlassesOutline" />
</template>
<template #password-invisible-icon>
<n-icon :size="16" :component="Glasses" />
</template>
</n-input>
<template #feedback> 申请地址https://console.amap.com/dev/key/app</template>
</n-form-item>
@ -25,6 +37,7 @@
import { ref, onMounted } from 'vue';
import { useMessage } from 'naive-ui';
import { getConfig, updateConfig } from '@/api/sys/config';
import { GlassesOutline, Glasses } from '@vicons/ionicons5';
const group = ref('geo');
const show = ref(false);
@ -47,17 +60,10 @@
function formSubmit() {
formRef.value.validate((errors) => {
if (!errors) {
console.log('formValue.value:' + JSON.stringify(formValue.value));
updateConfig({ group: group.value, list: formValue.value })
.then((res) => {
console.log('res:' + JSON.stringify(res));
message.success('更新成功');
load();
})
.catch((error) => {
message.error(error.toString());
});
updateConfig({ group: group.value, list: formValue.value }).then((_res) => {
message.success('更新成功');
load();
});
} else {
message.error('验证失败,请填写完整信息');
}
@ -73,14 +79,10 @@
new Promise((_resolve, _reject) => {
getConfig({ group: group.value })
.then((res) => {
show.value = false;
// state.formValue.watermarkClarity = res;
formValue.value = res.list;
console.log('res:' + JSON.stringify(res));
})
.catch((error) => {
.finally(() => {
show.value = false;
message.error(error.toString());
});
});
}

View File

@ -179,10 +179,6 @@
});
}
function resetForm() {
formRef.value.restoreValidation();
}
return {
formRef,
...toRefs(state),
@ -191,7 +187,6 @@
pricePreciseNumList,
rules,
formSubmit,
resetForm,
};
},
});

View File

@ -184,15 +184,10 @@
function formSubmit() {
formRef.value.validate((errors) => {
if (!errors) {
updateConfig({ group: group.value, list: formValue.value })
.then((res) => {
console.log('res:' + JSON.stringify(res));
message.success('更新成功');
load();
})
.catch((error) => {
message.error(error.toString());
});
updateConfig({ group: group.value, list: formValue.value }).then((_res) => {
message.success('更新成功');
load();
});
} else {
message.error('验证失败,请填写完整信息');
}
@ -209,13 +204,11 @@
new Promise((_resolve, _reject) => {
getConfig({ group: group.value })
.then((res) => {
show.value = false;
res.list.smsAliyunTemplate = JSON.parse(res.list.smsAliyunTemplate);
formValue.value = res.list;
})
.catch((error) => {
.finally(() => {
show.value = false;
message.error(error.toString());
});
});
}

View File

@ -58,17 +58,10 @@
function formSubmit() {
formRef.value.validate((errors) => {
if (!errors) {
console.log('formValue.value:' + JSON.stringify(formValue.value));
updateConfig({ group: group.value, list: formValue.value })
.then((res) => {
console.log('res:' + JSON.stringify(res));
message.success('更新成功');
load();
})
.catch((error) => {
message.error(error.toString());
});
updateConfig({ group: group.value, list: formValue.value }).then((_res) => {
message.success('更新成功');
load();
});
} else {
message.error('验证失败,请填写完整信息');
}
@ -84,14 +77,10 @@
new Promise((_resolve, _reject) => {
getConfig({ group: group.value })
.then((res) => {
show.value = false;
// state.formValue.watermarkClarity = res;
formValue.value = res.list;
console.log('res:' + JSON.stringify(res));
})
.catch((error) => {
.finally(() => {
show.value = false;
message.error(error.toString());
});
});
}

View File

@ -19,7 +19,7 @@
placeholder="请输入"
v-model:value="formValue.uploadImageSize"
>
<template #suffix> MB </template>
<template #suffix> MB</template>
</n-input-number>
</n-form-item>
<n-form-item label="图片类型限制" path="uploadImageType">
@ -32,7 +32,7 @@
placeholder="请输入"
v-model:value="formValue.uploadFileSize"
>
<template #suffix> MB </template>
<template #suffix> MB</template>
</n-input-number>
</n-form-item>
<n-form-item label="文件类型限制" path="uploadFileType">
@ -48,19 +48,33 @@
<n-divider title-placement="left">UCloud存储</n-divider>
<n-form-item label="公钥" path="uploadUCloudPublicKey">
<n-input
v-model:value="formValue.uploadUCloudPublicKey"
placeholder=""
type="password"
/>
v-model:value="formValue.uploadUCloudPublicKey"
show-password-on="click"
>
<template #password-visible-icon>
<n-icon :size="16" :component="GlassesOutline" />
</template>
<template #password-invisible-icon>
<n-icon :size="16" :component="Glasses" />
</template>
</n-input>
<template #feedback>获取地址https://console.ucloud.cn/ufile/token</template>
</n-form-item>
<n-form-item label="私钥" path="uploadUCloudPrivateKey">
<n-input
v-model:value="formValue.uploadUCloudPrivateKey"
placeholder=""
type="password"
/>
v-model:value="formValue.uploadUCloudPrivateKey"
show-password-on="click"
>
<template #password-visible-icon>
<n-icon :size="16" :component="GlassesOutline" />
</template>
<template #password-invisible-icon>
<n-icon :size="16" :component="Glasses" />
</template>
</n-input>
</n-form-item>
<n-form-item label="存储路径" path="uploadUCloudPath">
<n-input v-model:value="formValue.uploadUCloudPath" placeholder="" />
@ -94,9 +108,10 @@
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue';
import { onMounted, ref } from 'vue';
import { useMessage } from 'naive-ui';
import { getConfig, updateConfig } from '@/api/sys/config';
import { Glasses, GlassesOutline } from '@vicons/ionicons5';
const group = ref('upload');
const show = ref(false);
@ -142,10 +157,7 @@
function formSubmit() {
formRef.value.validate((errors) => {
if (!errors) {
console.log('formValue.value:' + JSON.stringify(formValue.value));
updateConfig({ group: group.value, list: formValue.value }).then((res) => {
console.log('res:' + JSON.stringify(res));
updateConfig({ group: group.value, list: formValue.value }).then((_res) => {
message.success('更新成功');
load();
});
@ -164,13 +176,10 @@
new Promise((_resolve, _reject) => {
getConfig({ group: group.value })
.then((res) => {
show.value = false;
formValue.value = res.list;
console.log('res:' + JSON.stringify(res));
})
.catch((error) => {
.finally(() => {
show.value = false;
message.error(error.toString());
});
});
}

View File

@ -420,7 +420,6 @@ export const columns = [
checked: '开启',
unchecked: '关闭',
onUpdateValue: function (e) {
console.log('onUpdateValue e:' + JSON.stringify(e));
row.switch = e ? 1 : 2;
Switch({ id: row.id, key: 'switch', value: row.switch }).then((_res) => {
$message.success('操作成功');