签入版本
This commit is contained in:
commit
82df58edd5
19
.editorconfig
Normal file
19
.editorconfig
Normal file
@ -0,0 +1,19 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset=utf-8
|
||||
end_of_line=lf
|
||||
insert_final_newline=true
|
||||
indent_style=space
|
||||
indent_size=2
|
||||
max_line_length = 100
|
||||
|
||||
[*.{yml,yaml,json}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
11
.env
Normal file
11
.env
Normal file
@ -0,0 +1,11 @@
|
||||
# port
|
||||
VITE_PORT = 8002
|
||||
|
||||
# spa-title
|
||||
VITE_GLOB_APP_TITLE = 云恒WMS
|
||||
|
||||
# spa shortname
|
||||
VITE_GLOB_APP_SHORT_NAME = 云恒WMS
|
||||
|
||||
# 生产环境 开启mock
|
||||
VITE_GLOB_PROD_MOCK = false
|
30
.env.development
Normal file
30
.env.development
Normal file
@ -0,0 +1,30 @@
|
||||
# 只在开发模式中被载入
|
||||
VITE_PORT = 8002
|
||||
|
||||
# 网站根目录
|
||||
VITE_PUBLIC_PATH = /
|
||||
|
||||
# 是否开启mock
|
||||
VITE_USE_MOCK = false
|
||||
|
||||
# 网站前缀
|
||||
VITE_BASE_URL = /
|
||||
|
||||
# 是否删除console
|
||||
VITE_DROP_CONSOLE = true
|
||||
|
||||
# 跨域代理,可以配置多个,请注意不要换行
|
||||
#VITE_PROXY = [["/appApi","http://localhost:8001"],["/upload","http://localhost:8001/upload"]]
|
||||
VITE_PROXY=[["/api","http://192.168.124.203:8081/api"]]
|
||||
|
||||
# API 接口地址
|
||||
VITE_GLOB_API_URL =
|
||||
|
||||
# 图片上传地址
|
||||
VITE_GLOB_UPLOAD_URL=
|
||||
|
||||
# 图片前缀地址
|
||||
VITE_GLOB_IMG_URL=
|
||||
|
||||
# 接口前缀
|
||||
VITE_GLOB_API_URL_PREFIX = /api
|
31
.env.production
Normal file
31
.env.production
Normal file
@ -0,0 +1,31 @@
|
||||
# 是否开启mock
|
||||
VITE_USE_MOCK = true
|
||||
|
||||
# 网站根目录
|
||||
VITE_PUBLIC_PATH = /
|
||||
|
||||
# 网站前缀
|
||||
VITE_BASE_URL = /
|
||||
|
||||
# 是否删除console
|
||||
VITE_DROP_CONSOLE = true
|
||||
|
||||
# API
|
||||
VITE_GLOB_API_URL =
|
||||
|
||||
# 图片上传地址
|
||||
VITE_GLOB_UPLOAD_URL=
|
||||
|
||||
# 图片前缀地址
|
||||
VITE_GLOB_IMG_URL=
|
||||
|
||||
# 接口前缀
|
||||
VITE_GLOB_API_URL_PREFIX = /api
|
||||
|
||||
# 是否启用gzip压缩或brotli压缩
|
||||
# 可选: gzip | brotli | none
|
||||
# 如果你需要多种形式,你可以用','来分隔
|
||||
VITE_BUILD_COMPRESS = 'none'
|
||||
|
||||
# 使用压缩时是否删除原始文件,默认为false
|
||||
VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE = false
|
23
.env.test
Normal file
23
.env.test
Normal file
@ -0,0 +1,23 @@
|
||||
NODE_ENV = production
|
||||
|
||||
# 是否开启mock
|
||||
VITE_USE_MOCK = true
|
||||
|
||||
# 网站根目录
|
||||
VITE_PUBLIC_PATH = /
|
||||
|
||||
# 是否删除console
|
||||
VITE_DROP_CONSOLE = true
|
||||
|
||||
# API 接口地址
|
||||
VITE_GLOB_API_URL =
|
||||
|
||||
# 接口前缀
|
||||
VITE_GLOB_API_URL_PREFIX =
|
||||
|
||||
# 图片上传地址
|
||||
VITE_GLOB_UPLOAD_URL = /upload
|
||||
|
||||
# 图片前缀地址
|
||||
VITE_GLOB_IMG_URL =
|
||||
|
15
.eslintignore
Normal file
15
.eslintignore
Normal file
@ -0,0 +1,15 @@
|
||||
|
||||
*.sh
|
||||
node_modules
|
||||
*.md
|
||||
*.woff
|
||||
*.ttf
|
||||
.vscode
|
||||
.idea
|
||||
dist
|
||||
/public
|
||||
/docs
|
||||
.husky
|
||||
.local
|
||||
/bin
|
||||
Dockerfile
|
74
.eslintrc.js
Normal file
74
.eslintrc.js
Normal file
@ -0,0 +1,74 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
browser: true,
|
||||
node: true,
|
||||
es6: true,
|
||||
},
|
||||
parser: 'vue-eslint-parser',
|
||||
parserOptions: {
|
||||
parser: '@typescript-eslint/parser',
|
||||
ecmaVersion: 2020,
|
||||
sourceType: 'module',
|
||||
jsxPragma: 'React',
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
},
|
||||
extends: [
|
||||
'plugin:vue/vue3-recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:prettier/recommended',
|
||||
],
|
||||
rules: {
|
||||
'vue/script-setup-uses-vars': 'error',
|
||||
'@typescript-eslint/ban-ts-ignore': 'off',
|
||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/no-var-requires': 'off',
|
||||
'@typescript-eslint/no-empty-function': 'off',
|
||||
'vue/custom-event-name-casing': 'off',
|
||||
'no-use-before-define': 'off',
|
||||
'@typescript-eslint/no-use-before-define': 'off',
|
||||
'@typescript-eslint/ban-ts-comment': 'off',
|
||||
'@typescript-eslint/ban-types': 'off',
|
||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'error',
|
||||
{
|
||||
argsIgnorePattern: '^_',
|
||||
varsIgnorePattern: '^_',
|
||||
},
|
||||
],
|
||||
'no-unused-vars': [
|
||||
'error',
|
||||
{
|
||||
argsIgnorePattern: '^_',
|
||||
varsIgnorePattern: '^_',
|
||||
},
|
||||
],
|
||||
'space-before-function-paren': 'off',
|
||||
|
||||
'vue/attributes-order': 'off',
|
||||
'vue/one-component-per-file': 'off',
|
||||
'vue/html-closing-bracket-newline': 'off',
|
||||
'vue/max-attributes-per-line': 'off',
|
||||
'vue/multiline-html-element-content-newline': 'off',
|
||||
'vue/singleline-html-element-content-newline': 'off',
|
||||
'vue/attribute-hyphenation': 'off',
|
||||
'vue/require-default-prop': 'off',
|
||||
'vue/html-self-closing': [
|
||||
'error',
|
||||
{
|
||||
html: {
|
||||
void: 'always',
|
||||
normal: 'never',
|
||||
component: 'always',
|
||||
},
|
||||
svg: 'always',
|
||||
math: 'always',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
25
.gitignore
vendored
Normal file
25
.gitignore
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
/screenshots
|
||||
/dist
|
||||
dist.zip
|
||||
dist_electron
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Log files
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
9
.prettierignore
Normal file
9
.prettierignore
Normal file
@ -0,0 +1,9 @@
|
||||
/dist/*
|
||||
.local
|
||||
.output.js
|
||||
/node_modules/**
|
||||
|
||||
**/*.svg
|
||||
**/*.sh
|
||||
|
||||
/public/*
|
3
.stylelintignore
Normal file
3
.stylelintignore
Normal file
@ -0,0 +1,3 @@
|
||||
/dist/*
|
||||
/public/*
|
||||
public/*
|
1
.svn/entries
Normal file
1
.svn/entries
Normal file
@ -0,0 +1 @@
|
||||
12
|
1
.svn/format
Normal file
1
.svn/format
Normal file
@ -0,0 +1 @@
|
||||
12
|
@ -0,0 +1,14 @@
|
||||
import { App } from 'vue';
|
||||
|
||||
import { perm,perms } from '@/directives/permission';
|
||||
import { scrollBar } from '@/directives/scrollBar';
|
||||
|
||||
/**
|
||||
* 注册全局自定义指令
|
||||
* @param app
|
||||
*/
|
||||
export function setupDirectives(app: App) {
|
||||
app.directive('perm', perm); // 权限控制指令 (是否包含其中某个权限)
|
||||
app.directive('perms', perms); // 权限控制指令 (是否包含所有权限)
|
||||
app.directive('scrollBar', scrollBar); // 滚动条
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
import type { FunctionalComponent, defineComponent } from 'vue';
|
||||
import type { ComponentType } from '../../types/componentType';
|
||||
import { componentMap } from '@/components/Table/src/componentMap';
|
||||
|
||||
import { h } from 'vue';
|
||||
|
||||
import { Popover } from 'ant-design-vue';
|
||||
|
||||
export interface ComponentProps {
|
||||
component: ComponentType;
|
||||
rule: boolean;
|
||||
popoverVisible: boolean;
|
||||
ruleMessage: string;
|
||||
}
|
||||
|
||||
export const CellComponent: FunctionalComponent = (
|
||||
{ component = 'Input', rule = true, ruleMessage, popoverVisible }: ComponentProps,
|
||||
{ attrs },
|
||||
) => {
|
||||
const Comp = componentMap.get(component) as typeof defineComponent;
|
||||
const DefaultComp = h(Comp, attrs);
|
||||
if (!rule) {
|
||||
return DefaultComp;
|
||||
}
|
||||
return h(
|
||||
Popover,
|
||||
{
|
||||
overlayClassName: 'edit-cell-rule-popover',
|
||||
visible: !!popoverVisible,
|
||||
},
|
||||
{
|
||||
default: () => DefaultComp,
|
||||
content: () => ruleMessage,
|
||||
},
|
||||
);
|
||||
};
|
@ -0,0 +1,38 @@
|
||||
.fullscreen-modal {
|
||||
overflow: hidden;
|
||||
|
||||
.ant-modal {
|
||||
top: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100%;
|
||||
|
||||
&-content {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-modal {
|
||||
width: 520px;
|
||||
padding-bottom: 0;
|
||||
|
||||
&-title {
|
||||
font-size: 16px;
|
||||
line-height: 16px;
|
||||
|
||||
.base-title {
|
||||
cursor: move !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
&-close-x {
|
||||
display: inline-block;
|
||||
width: 96px;
|
||||
height: 56px;
|
||||
line-height: 56px;
|
||||
}
|
||||
}
|
@ -0,0 +1,202 @@
|
||||
<template>
|
||||
<PageWrapper>
|
||||
<a-card :bordered="false" class="pt-3 mb-3 proCard">
|
||||
<BasicForm @register="register" @submit="handleSubmit" @reset="handleReset">
|
||||
<template #statusSlot="{ model, field }">
|
||||
<a-input v-model="model[field]" />
|
||||
</template>
|
||||
</BasicForm>
|
||||
</a-card>
|
||||
<a-card :bordered="false" class="proCard">
|
||||
<BasicTable
|
||||
:columns="columns"
|
||||
:request="loadDataTable"
|
||||
:row-key="(row) => row.id"
|
||||
ref="tableRef"
|
||||
scroll-x="1200"
|
||||
:row-selection="{ onChange: onSelectionChange }"
|
||||
virtual-scroll
|
||||
>
|
||||
<template #tableTitle>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="handleAdd" v-perm="['sys:ad:add']">
|
||||
<template #icon>
|
||||
<PlusOutlined />
|
||||
</template>
|
||||
新建
|
||||
</a-button>
|
||||
|
||||
<a-button
|
||||
type="danger"
|
||||
@click="handleDelete()"
|
||||
:disabled="!selectionData.length"
|
||||
v-perm="['sys:ad:batchDelete']"
|
||||
>
|
||||
<template #icon>
|
||||
<DeleteOutlined />
|
||||
</template>
|
||||
删除
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'action'">
|
||||
<a-space>
|
||||
<a-button type="primary" @click="handleEdit(record.id)" v-perm="['sys:ad:update']">
|
||||
<template #icon><EditOutlined /></template>
|
||||
编辑
|
||||
</a-button>
|
||||
<a-button
|
||||
type="primary"
|
||||
danger
|
||||
@click="handleDelete(record.id)"
|
||||
v-perm="['sys:ad:delete']"
|
||||
>
|
||||
<template #icon><DeleteOutlined /></template>
|
||||
删除
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</a-card>
|
||||
<editDialog
|
||||
v-if="editVisible"
|
||||
:adId="adId"
|
||||
v-model:visible="editVisible"
|
||||
@success="reloadTable('noRefresh')"
|
||||
/>
|
||||
</PageWrapper>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { h, nextTick, reactive, ref, defineAsyncComponent } from 'vue';
|
||||
import { PlusOutlined, EditOutlined, DeleteOutlined } from '@ant-design/icons-vue';
|
||||
import { useForm } from '@/components/Form/index';
|
||||
import { getAdList, adDelete, adBatchDelete } from '@/api/content/ad';
|
||||
import { columns } from './columns';
|
||||
import { schemas } from './querySchemas';
|
||||
import { Modal, message } from 'ant-design-vue';
|
||||
|
||||
/**
|
||||
* 定义参数变量
|
||||
*/
|
||||
const adId = ref(0);
|
||||
const tableRef = ref();
|
||||
const editVisible = ref(false);
|
||||
const selectionData = ref([]);
|
||||
|
||||
/**
|
||||
* 导入组件
|
||||
*/
|
||||
const editDialog = defineAsyncComponent(() => import('./edit.vue'));
|
||||
|
||||
/**
|
||||
* 定义查询参数
|
||||
*/
|
||||
const formParams = reactive({
|
||||
title: '',
|
||||
status: '',
|
||||
adSortId: '',
|
||||
});
|
||||
|
||||
/**
|
||||
* 加载数据列表
|
||||
* @param res 参数
|
||||
*/
|
||||
const loadDataTable = async (res) => {
|
||||
const result = await getAdList({ ...formParams, ...res });
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* 刷新数据列表
|
||||
* @param noRefresh 参数
|
||||
*/
|
||||
function reloadTable(noRefresh = '') {
|
||||
tableRef.value.reload(noRefresh ? {} : { pageNo: 1 });
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行编辑
|
||||
* @param id 参数
|
||||
*/
|
||||
async function handleEdit(id) {
|
||||
adId.value = id;
|
||||
await nextTick();
|
||||
editVisible.value = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行删除
|
||||
* @param id 参数
|
||||
*/
|
||||
async function handleDelete(id) {
|
||||
Modal.confirm({
|
||||
title: '提示',
|
||||
content: '确定要删除?',
|
||||
onOk: async () => {
|
||||
id ? await adDelete(id) : await adSortBatchDelete(selectionData.value);
|
||||
message.success('删除成功');
|
||||
reloadTable();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行提交表单
|
||||
* @param values 参数
|
||||
*/
|
||||
function handleSubmit(values) {
|
||||
for (const key in formParams) {
|
||||
formParams[key] = '';
|
||||
}
|
||||
for (const key in values) {
|
||||
formParams[key] = values[key];
|
||||
}
|
||||
reloadTable();
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行重置
|
||||
*/
|
||||
function handleReset() {
|
||||
for (const key in formParams) {
|
||||
formParams[key] = '';
|
||||
}
|
||||
reloadTable();
|
||||
}
|
||||
|
||||
/**
|
||||
* 选项发生变化
|
||||
* @param value 参数
|
||||
*/
|
||||
function onSelectionChange(value) {
|
||||
selectionData.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册
|
||||
*/
|
||||
const [register, {}] = useForm({
|
||||
rowProps: { gutter: [16, 0] },
|
||||
colProps: {
|
||||
xs: 24,
|
||||
sm: 24,
|
||||
md: 12,
|
||||
lg: 8,
|
||||
xl: 6,
|
||||
},
|
||||
labelCol: { span: 6, offset: 0 },
|
||||
schemas,
|
||||
});
|
||||
|
||||
/**
|
||||
* 执行添加
|
||||
*/
|
||||
const handleAdd = async () => {
|
||||
adId.value = 0;
|
||||
await nextTick();
|
||||
editVisible.value = true;
|
||||
};
|
||||
</script>
|
@ -0,0 +1,76 @@
|
||||
import { h } from 'vue';
|
||||
import { Tag } from 'ant-design-vue';
|
||||
|
||||
export const columns = [
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'id',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '参数名称',
|
||||
dataIndex: 'name',
|
||||
},
|
||||
{
|
||||
title: '参数编码',
|
||||
dataIndex: 'code',
|
||||
},
|
||||
{
|
||||
title: '参数值',
|
||||
dataIndex: 'value',
|
||||
},
|
||||
{
|
||||
title: '参数类型',
|
||||
dataIndex: 'type',
|
||||
customRender({ record }) {
|
||||
return h(
|
||||
Tag,
|
||||
{
|
||||
color: record.type ==0 ? 'processing' : 'warning',
|
||||
},
|
||||
{
|
||||
default: () => (record.type ==0 ? '系统' : '业务'),
|
||||
},
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '参数状态',
|
||||
dataIndex: 'status',
|
||||
customRender({ record }) {
|
||||
return h(
|
||||
Tag,
|
||||
{
|
||||
color: record.status ==1 ? 'success' : 'error',
|
||||
},
|
||||
{
|
||||
default: () => (record.status ==1 ? '正常' : '禁用'),
|
||||
},
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '排序',
|
||||
dataIndex: 'sort',
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
dataIndex: 'note',
|
||||
},
|
||||
{
|
||||
title: '创建人',
|
||||
dataIndex: 'createUser',
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createTime',
|
||||
width: 180,
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
fixed:'right',
|
||||
dataIndex: 'action',
|
||||
key: 'action',
|
||||
width: 200,
|
||||
},
|
||||
];
|
@ -0,0 +1,31 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { IS_LOCKSCREEN } from '@/store/mutation-types';
|
||||
import { storage } from '@/utils/Storage';
|
||||
|
||||
// 长时间不操作默认锁屏时间
|
||||
const initTime = 60 * 60;
|
||||
|
||||
const isLock = storage.get(IS_LOCKSCREEN, false);
|
||||
|
||||
export type ILockscreenState = {
|
||||
isLock: boolean; // 是否锁屏
|
||||
lockTime: number;
|
||||
};
|
||||
|
||||
export const useLockscreenStore = defineStore({
|
||||
id: 'app-lockscreen',
|
||||
state: (): ILockscreenState => ({
|
||||
isLock: isLock === true, // 是否锁屏
|
||||
lockTime: isLock == 'true' ? initTime : 0,
|
||||
}),
|
||||
getters: {},
|
||||
actions: {
|
||||
setLock(payload) {
|
||||
this.isLock = payload;
|
||||
storage.set(IS_LOCKSCREEN, this.isLock);
|
||||
},
|
||||
setLockTime(payload = initTime) {
|
||||
this.lockTime = payload;
|
||||
},
|
||||
},
|
||||
});
|
Binary file not shown.
After Width: | Height: | Size: 2.6 KiB |
@ -0,0 +1,92 @@
|
||||
import { computed, onMounted, reactive, toRefs } from 'vue';
|
||||
|
||||
interface Battery {
|
||||
charging: boolean; // 当前电池是否正在充电
|
||||
chargingTime: number; // 距离充电完毕还需多少秒,如果为0则充电完毕
|
||||
dischargingTime: number; // 代表距离电池耗电至空且挂起需要多少秒
|
||||
level: number; // 代表电量的放大等级,这个值在 0.0 至 1.0 之间
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export const useBattery = () => {
|
||||
const state = reactive({
|
||||
battery: {
|
||||
charging: false,
|
||||
chargingTime: 0,
|
||||
dischargingTime: 0,
|
||||
level: 100,
|
||||
},
|
||||
});
|
||||
|
||||
// 更新电池使用状态
|
||||
const updateBattery = (target) => {
|
||||
for (const key in state.battery) {
|
||||
state.battery[key] = target[key];
|
||||
}
|
||||
state.battery.level = state.battery.level * 100;
|
||||
};
|
||||
|
||||
// 计算电池剩余可用时间
|
||||
const calcDischargingTime = computed(() => {
|
||||
const hour = state.battery.dischargingTime / 3600;
|
||||
const minute = (state.battery.dischargingTime / 60) % 60;
|
||||
return `${~~hour}小时${~~minute}分钟`;
|
||||
});
|
||||
|
||||
// 计算电池充满剩余时间
|
||||
const calcChargingTime = computed(() => {
|
||||
const hour = state.battery.chargingTime / 3600;
|
||||
const minute = (state.battery.chargingTime / 60) % 60;
|
||||
return `${~~hour}小时${~~minute}分钟`;
|
||||
});
|
||||
|
||||
// 电池状态
|
||||
const batteryStatus = computed(() => {
|
||||
if (state.battery.charging && state.battery.level >= 100) {
|
||||
return '已充满';
|
||||
} else if (state.battery.charging) {
|
||||
return '充电中';
|
||||
} else {
|
||||
return '已断开电源';
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
const BatteryManager: Battery = await (window.navigator as any).getBattery();
|
||||
updateBattery(BatteryManager);
|
||||
|
||||
// 电池充电状态更新时被调用
|
||||
BatteryManager.onchargingchange = ({ target }) => {
|
||||
updateBattery(target);
|
||||
};
|
||||
// 电池充电时间更新时被调用
|
||||
BatteryManager.onchargingtimechange = ({ target }) => {
|
||||
updateBattery(target);
|
||||
};
|
||||
// 电池断开充电时间更新时被调用
|
||||
BatteryManager.ondischargingtimechange = ({ target }) => {
|
||||
updateBattery(target);
|
||||
};
|
||||
// 电池电量更新时被调用
|
||||
BatteryManager.onlevelchange = ({ target }) => {
|
||||
updateBattery(target);
|
||||
};
|
||||
|
||||
// new Intl.DateTimeFormat('zh', {
|
||||
// year: 'numeric',
|
||||
// month: '2-digit',
|
||||
// day: '2-digit',
|
||||
// hour: '2-digit',
|
||||
// minute: '2-digit',
|
||||
// second: '2-digit',
|
||||
// hour12: false
|
||||
// }).format(new Date())
|
||||
});
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
batteryStatus,
|
||||
calcDischargingTime,
|
||||
calcChargingTime,
|
||||
};
|
||||
};
|
@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<a-card :bordered="false" class="proCard">
|
||||
<div class="result-box">
|
||||
<a-result
|
||||
status="success"
|
||||
title="操作成功"
|
||||
description="提交结果页用于反馈一系列操作任务的处理结果,如果仅是简单操作,灰色区域可以显示一些补充的信息。"
|
||||
>
|
||||
<div class="result-box-extra">
|
||||
<p>已提交申请,等待财务部门审核。</p>
|
||||
</div>
|
||||
<template #extra>
|
||||
<a-space align="center" class="mb-4">
|
||||
<a-button type="primary" @click="goHome">回到首页</a-button>
|
||||
<a-button>查看详情</a-button>
|
||||
<a-button>打印</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-result>
|
||||
</div>
|
||||
</a-card>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
function goHome() {
|
||||
router.push('/');
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.result-box {
|
||||
width: 72%;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
padding-top: 5px;
|
||||
|
||||
&-extra {
|
||||
padding: 0px 40px;
|
||||
background: var(--border-color);
|
||||
border-radius: 4px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,152 @@
|
||||
<template>
|
||||
<a-modal
|
||||
v-model:visible="props.visible"
|
||||
:title="props.adSortId ? '编辑' : '新增'"
|
||||
width="500px"
|
||||
@cancel="dialogClose"
|
||||
>
|
||||
<a-form
|
||||
class="ls-form"
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:label-col="{ style: { width: '100px' } }"
|
||||
>
|
||||
<a-form-item
|
||||
label="广告位名称"
|
||||
name="name"
|
||||
:rules="{ required: true, message: '请输入广告位名称', trigger: 'blur' }"
|
||||
>
|
||||
<a-input v-model:value="formData.name" placeholder="请输入广告位名称" />
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="页面位置"
|
||||
name="location"
|
||||
class="flex-1"
|
||||
:rules="{ required: true, message: '请输入页面位置', trigger: 'blur' }"
|
||||
>
|
||||
<number-input v-model:value="formData.location" placeholder="请输入页面位置" />
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="广告位类型"
|
||||
name="type"
|
||||
:rules="{ required: true, message: '请选择广告位类型', trigger: 'change' }"
|
||||
>
|
||||
<a-select v-model:value="formData.type" placeholder="请选择广告位类型">
|
||||
<a-select-option :value="1">网站</a-select-option>
|
||||
<a-select-option :value="2">手机站</a-select-option>
|
||||
<a-select-option :value="3">移动端</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="排序" name="sort">
|
||||
<a-input-number v-model:value="formData.sort" :max="9999" placeholder="请输入排序" />
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="备注"
|
||||
name="note"
|
||||
:rules="{ required: true, message: '请输入备注', trigger: 'blur' }"
|
||||
>
|
||||
<a-textarea v-model:value="formData.note" placeholder="请输入备注" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<a-button @click="dialogClose">取消</a-button>
|
||||
<a-button :loading="subLoading" type="primary" @click="submit"> 确定 </a-button>
|
||||
</span>
|
||||
</template>
|
||||
</a-modal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import type { FormInstance } from 'ant-design-vue';
|
||||
import { getAdSortDetail, adSortAdd, adSortUpdate } from '@/api/content/adSort';
|
||||
import { getLayoutAllList } from '@/api/content/layout';
|
||||
import { onMounted, reactive, shallowRef, ref } from 'vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { useLockFn } from '@/utils/useLockFn';
|
||||
|
||||
/**
|
||||
* 定义参数变量
|
||||
*/
|
||||
const emit = defineEmits(['success', 'update:visible']);
|
||||
const formRef = shallowRef<FormInstance>();
|
||||
|
||||
/**
|
||||
* 定义表单参数
|
||||
*/
|
||||
const formData = reactive({
|
||||
id: '',
|
||||
name: '',
|
||||
location: '',
|
||||
note: '',
|
||||
type: undefined,
|
||||
sort: 0,
|
||||
});
|
||||
|
||||
/**
|
||||
* 定义接收的参数
|
||||
*/
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
default: false,
|
||||
},
|
||||
adSortId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
default: 0,
|
||||
},
|
||||
});
|
||||
const locationList = ref([]);
|
||||
|
||||
/**
|
||||
* 执行提交表单
|
||||
*/
|
||||
const handleSubmit = async () => {
|
||||
await formRef.value?.validate();
|
||||
props.adSortId ? await adSortUpdate(formData) : await adSortAdd(formData);
|
||||
message.success('操作成功');
|
||||
emit('update:visible', false);
|
||||
emit('success');
|
||||
};
|
||||
|
||||
/**
|
||||
* 关闭窗体
|
||||
*/
|
||||
const dialogClose = () => {
|
||||
emit('update:visible', false);
|
||||
};
|
||||
|
||||
const { isLock: subLoading, lockFn: submit } = useLockFn(handleSubmit);
|
||||
|
||||
/**
|
||||
* 设置表单数据
|
||||
*/
|
||||
const setFormData = async () => {
|
||||
const data = await getAdSortDetail(props.adSortId);
|
||||
for (const key in formData) {
|
||||
if (data[key] != null && data[key] != undefined) {
|
||||
//@ts-ignore
|
||||
formData[key] = data[key];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取全部字典数据
|
||||
*/
|
||||
const getAllDict = async () => {
|
||||
let list = await getLayoutAllList();
|
||||
locationList.value = list ? list : [];
|
||||
};
|
||||
|
||||
/**
|
||||
* 钩子函数
|
||||
*/
|
||||
onMounted(() => {
|
||||
getAllDict();
|
||||
if (props.adSortId) {
|
||||
setFormData();
|
||||
}
|
||||
});
|
||||
</script>
|
@ -0,0 +1,194 @@
|
||||
<template>
|
||||
<div class="menu-index">
|
||||
<a-card :bordered="false" class="pt-3 mb-3 proCard">
|
||||
<div>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="handleAdd()" v-perm="['sys:dept:add']">
|
||||
<template #icon><PlusOutlined /></template>
|
||||
新增
|
||||
</a-button>
|
||||
<a-button @click="handleExpand"> 展开/折叠</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
</a-card>
|
||||
<a-card :bordered="false" class="pt-3 mb-3 proCard">
|
||||
<a-spin :spinning="loading">
|
||||
<a-table
|
||||
border
|
||||
ref="tableRef"
|
||||
:dataSource="lists"
|
||||
:columns="columns"
|
||||
v-model:expandedRowKeys="expandKeys"
|
||||
:style="{ minHeight: fwbHeight+'px' }"
|
||||
:scroll="{x:'100%',y:fwbHeight}"
|
||||
>
|
||||
<template #bodyCell="{ column, record, index }">
|
||||
<template v-if="column.key == 'action'">
|
||||
<a-space>
|
||||
<a-button
|
||||
type="primary"
|
||||
@click="handleAdd(record.id)"
|
||||
v-perm="['sys:category:add']"
|
||||
>
|
||||
<template #icon><PlusOutlined /></template>
|
||||
新增</a-button
|
||||
>
|
||||
<a-button
|
||||
type="primary"
|
||||
@click="handleEdit(record)"
|
||||
v-perm="['sys:category:update']"
|
||||
>
|
||||
<template #icon><EditOutlined /></template>
|
||||
编辑
|
||||
</a-button>
|
||||
<a-button
|
||||
type="primary"
|
||||
danger
|
||||
@click="handleDelete(record.id)"
|
||||
v-perm="['sys:category:delete']"
|
||||
>
|
||||
<template #icon><DeleteOutlined /></template>
|
||||
删除
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-spin>
|
||||
</a-card>
|
||||
<editDialog
|
||||
ref="editRef"
|
||||
v-if="editVisible"
|
||||
:categoryId="categoryId"
|
||||
:pid="pid"
|
||||
v-model:visible="editVisible"
|
||||
@success="getLists"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="menu">
|
||||
import { defineAsyncComponent, nextTick, onMounted, ref } from 'vue';
|
||||
import { PlusOutlined, EditOutlined, DeleteOutlined } from '@ant-design/icons-vue';
|
||||
import { getCategoryList, categoryDelete } from '@/api/content/category';
|
||||
import { Modal, message } from 'ant-design-vue';
|
||||
import { columns } from './columns';
|
||||
import { buildTree } from '@/utils/auth';
|
||||
|
||||
/**
|
||||
* 导入组件
|
||||
*/
|
||||
const editDialog = defineAsyncComponent(() => import('./edit.vue'));
|
||||
|
||||
/**
|
||||
* 定义参数变量
|
||||
*/
|
||||
const fwbHeight = document.body.clientHeight - 400;
|
||||
const loading = ref(false);
|
||||
const editVisible = ref(false);
|
||||
const expandAllRows = ref(false);
|
||||
const arrayList = ref([]);
|
||||
const expandKeys = ref([]);
|
||||
const categoryId = ref(0);
|
||||
const pid = ref(0);
|
||||
const lists = ref([]);
|
||||
|
||||
/**
|
||||
* 获取分类数据
|
||||
*/
|
||||
const getLists = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const data = await getCategoryList();
|
||||
data.map((item) => {
|
||||
item.key = item.id;
|
||||
});
|
||||
arrayList.value = data;
|
||||
lists.value = buildTree(data);
|
||||
loading.value = false;
|
||||
} catch (error) {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 执行添加
|
||||
* @param parentId 上级ID
|
||||
*/
|
||||
const handleAdd = async (parentId: any) => {
|
||||
categoryId.value = 0;
|
||||
pid.value = parentId ? parentId : 0;
|
||||
await nextTick();
|
||||
editVisible.value = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* 执行编辑
|
||||
* @param data 参数
|
||||
*/
|
||||
const handleEdit = async (data: any) => {
|
||||
categoryId.value = data.id;
|
||||
await nextTick();
|
||||
editVisible.value = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* 执行删除
|
||||
* @param categoryId 分类ID
|
||||
*/
|
||||
const handleDelete = (categoryId: number) => {
|
||||
Modal.confirm({
|
||||
title: '提示',
|
||||
content: '确定要删除?',
|
||||
onOk: async () => {
|
||||
loading.value = true;
|
||||
await categoryDelete(categoryId);
|
||||
message.success('删除成功');
|
||||
getLists();
|
||||
loading.value = false;
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 执行扩展、收缩
|
||||
*/
|
||||
const handleExpand = () => {
|
||||
expandAllRows.value = !expandAllRows.value;
|
||||
toggleExpand();
|
||||
};
|
||||
|
||||
/**
|
||||
* 扩展、收缩实现
|
||||
*/
|
||||
const toggleExpand = () => {
|
||||
expandKeys.value = [];
|
||||
if (expandAllRows.value) {
|
||||
arrayList.value.map((item) => {
|
||||
expandKeys.value.push(item.id);
|
||||
});
|
||||
} else {
|
||||
expandKeys.value = [];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取全部数据
|
||||
*/
|
||||
const getAll = () => {
|
||||
getLists();
|
||||
};
|
||||
|
||||
/**
|
||||
* 钩子函数
|
||||
*/
|
||||
onMounted(() => {
|
||||
getAll();
|
||||
});
|
||||
|
||||
// onActivated(() => {
|
||||
// getAll()
|
||||
// });
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
@ -0,0 +1,63 @@
|
||||
export const basicProps = {
|
||||
//密码框内容
|
||||
value: {
|
||||
type: [Number, String],
|
||||
default: '',
|
||||
},
|
||||
//是否必填
|
||||
required: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
//是否为复杂密码
|
||||
// 数字、字母和字符两种或以上组合
|
||||
complexity: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
complexityTip: {
|
||||
type: String,
|
||||
default: '需包含字母、数字及特殊字符两种或以上组合',
|
||||
},
|
||||
//密码最短长度要求
|
||||
minLength: {
|
||||
type: Number,
|
||||
default: 6,
|
||||
},
|
||||
//密码最大长度要求
|
||||
maxLength: {
|
||||
type: Number,
|
||||
default: 32,
|
||||
},
|
||||
//密码等级提示语
|
||||
// 自定义时 1 - 4 级需要对应
|
||||
level: {
|
||||
type: Object,
|
||||
default: {
|
||||
1: '弱不禁风',
|
||||
2: '平淡无奇',
|
||||
3: '出神入化',
|
||||
4: '登峰造极',
|
||||
},
|
||||
},
|
||||
//验证规格
|
||||
rules: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
//是否显示 [ 再次确认密码 ] 输入框
|
||||
repeat: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
//尺寸
|
||||
size: {
|
||||
type: String,
|
||||
default: 'medium',
|
||||
},
|
||||
//block属性将使按钮适合其父宽度
|
||||
block: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
};
|
@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<PageWrapper>
|
||||
<a-card shadow="never" size="small" class="proCard tabsCard">
|
||||
<a-tabs v-model="activeName">
|
||||
<a-tab-pane name="basic" label="基本设置">
|
||||
<BasicSetting />
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</a-card>
|
||||
</PageWrapper>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import { PageWrapper } from '@/components/Page';
|
||||
import BasicSetting from './BasicSetting.vue';
|
||||
const activeName = ref('basic');
|
||||
</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: #165DFF;
|
||||
|
||||
:deep(.n-thing-main .n-thing-header .n-thing-header__title) {
|
||||
color: #165DFF;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: #f0faff;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,60 @@
|
||||
import { http } from '@/utils/http/axios';
|
||||
|
||||
/**
|
||||
* @description: 列表
|
||||
*/
|
||||
export function getEmailTemplateList(params?) {
|
||||
return http.request({
|
||||
url: '/email/template/page',
|
||||
method: 'GET',
|
||||
params,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 根据ID获取详情
|
||||
*/
|
||||
export function getEmailTemplateDetail(id) {
|
||||
return http.request({
|
||||
url: '/email/template/detail/' + id,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 添加
|
||||
*/
|
||||
export function emailTemplateAdd(data: any) {
|
||||
return http.request({
|
||||
url: '/email/template/add',
|
||||
method: 'POST',
|
||||
data,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 更新
|
||||
*/
|
||||
export function emailTemplateUpdate(data: any) {
|
||||
return http.request({
|
||||
url: '/email/template/update',
|
||||
method: 'PUT',
|
||||
data,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 删除
|
||||
*/
|
||||
export function emailTemplateDelete(id) {
|
||||
return http.request({
|
||||
url: '/email/template/delete/' + id,
|
||||
method: 'DELETE',
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 批量删除
|
||||
*/
|
||||
export function emailTemplateBatchDelete(data: any) {
|
||||
return http.request({
|
||||
url: '/email/template/batchDelete',
|
||||
method: 'DELETE',
|
||||
data,
|
||||
});
|
||||
}
|
@ -0,0 +1,233 @@
|
||||
<template>
|
||||
<div class="account">
|
||||
<div class="account-container">
|
||||
<div class="account-wrap-login">
|
||||
<div class="login-pic">
|
||||
<h1 class="login-title">WMS </h1>
|
||||
<h4 class="login-subtitle">界面美观组件丰富的中后台前端解决方案 </h4>
|
||||
</div>
|
||||
<div class="login-form">
|
||||
<div class="login-form-container">
|
||||
<div class="account-top">
|
||||
<div class="account-top-desc">{{loginFlag?'用户登录':'用户注册'}}</div>
|
||||
</div>
|
||||
<template v-if="loginFlag">
|
||||
<div class="account-tab-box">
|
||||
<div :class="activeIndex==index?'active':''" v-for="(item,index) in tabData" @click="handleClick(index)" :key="index">{{item}}</div>
|
||||
</div>
|
||||
<LoginForm v-if="activeIndex === 0" @backLogin="goLogin"/>
|
||||
<PhoneForm v-else-if="activeIndex === 1"/>
|
||||
<QrcodeForm v-else-if="activeIndex === 2"></QrcodeForm>
|
||||
</template>
|
||||
<template v-else>
|
||||
<RegisterForm @backLogin="goLogin"></RegisterForm>
|
||||
</template>
|
||||
</div>
|
||||
<div class="corner-box" @click="handleCornerClick">
|
||||
<UserAddOutlined v-if="loginFlag" style="color: #FFFFFF; font-size: 30px"/>
|
||||
|
||||
<RollbackOutlined v-else style="color: #FFFFFF; font-size: 30px"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import LoginForm from './LoginForm2.vue';
|
||||
import PhoneForm from './PhoneForm.vue';
|
||||
import QrcodeForm from './QrcodeForm.vue';
|
||||
import RegisterForm from './RegisterForm2.vue';
|
||||
import {
|
||||
UserAddOutlined,
|
||||
RollbackOutlined
|
||||
} from '@ant-design/icons-vue';
|
||||
const loginFlag = ref(true)
|
||||
const activeIndex = ref(0)
|
||||
const tabData =ref(['账号登录','手机号登录','扫码登录'])
|
||||
const handleClick =(index)=>{
|
||||
activeIndex.value = index
|
||||
}
|
||||
const handleCornerClick = ()=>{
|
||||
loginFlag.value = !loginFlag.value
|
||||
}
|
||||
const goLogin = (type)=>{
|
||||
loginFlag.value = type
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.account {
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
|
||||
&-container {
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
background-image:url('@/assets/images/login-bg.png');
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100% 100%;
|
||||
|
||||
}
|
||||
|
||||
&-wrap-login {
|
||||
width: 920px;
|
||||
height: 510px;
|
||||
background: #fff;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
.login-pic {
|
||||
flex: 1;
|
||||
padding: 32px 8px;
|
||||
box-sizing: border-box;
|
||||
background-color: #1681fd;
|
||||
background-image: url('@/assets/images/login-img.png');
|
||||
background-repeat: no-repeat;
|
||||
background-position: bottom;
|
||||
background-size: contain;
|
||||
text-align: center;
|
||||
.login-title {
|
||||
color: #ffffff;
|
||||
font-size: 28px;
|
||||
margin: 0 0 6px;
|
||||
font-weight: 400;
|
||||
letter-spacing: 1.2px;
|
||||
}
|
||||
.login-subtitle {
|
||||
color: #fffc;
|
||||
font-size: 16px;
|
||||
margin: 0;
|
||||
font-weight: 400;
|
||||
letter-spacing: 4px;
|
||||
letter-spacing: 1.2px;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.login-form {
|
||||
width: 400px;
|
||||
position: relative;
|
||||
&-container {
|
||||
margin: auto;
|
||||
width: 100%;
|
||||
padding:32px 48px 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
&-title {
|
||||
padding-bottom: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
.corner-box {
|
||||
position:absolute;
|
||||
top:0;
|
||||
right:0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border-radius: 0 0 0 150%;
|
||||
background: #1890ff;
|
||||
cursor: pointer;
|
||||
>i {
|
||||
margin-right: -20px;
|
||||
margin-top: -10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 680px) {
|
||||
.login-pic {
|
||||
padding: 20px 12px 100px;
|
||||
background-size: auto 100px;
|
||||
}
|
||||
|
||||
.login-form {
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
.account-top {
|
||||
&-desc {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
color: #000000;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
}
|
||||
.account-tab-box {
|
||||
display: flex;
|
||||
height:34px;
|
||||
margin-bottom: 18px;
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 4px;
|
||||
padding:3px;
|
||||
>div {
|
||||
flex:1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
color:rgba(0, 0, 0, .6);
|
||||
&.active {
|
||||
background-color:#ffffff;
|
||||
color:rgba(0, 0, 0, .92);
|
||||
}
|
||||
&:hover {
|
||||
color:rgba(0, 0, 0, .92);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 680px) {
|
||||
&-container {
|
||||
padding: 0;
|
||||
display: block;
|
||||
background: #fff;
|
||||
}
|
||||
&-wrap-login {
|
||||
display: block;
|
||||
height: auto;
|
||||
width: 100%;
|
||||
background: none;
|
||||
box-shadow: none;
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style lang="less">
|
||||
// .account {
|
||||
// .el-button--primary {
|
||||
// background-color:blue
|
||||
// }
|
||||
// .el-button--text{
|
||||
// padding:0;
|
||||
// }
|
||||
// .el-checkbox__input.is-checked+.el-checkbox__label,.el-button--text {
|
||||
// color: blue;
|
||||
// }
|
||||
// .el-checkbox__input.is-checked .el-checkbox__inner {
|
||||
// background-color: blue;
|
||||
// color: blue;
|
||||
// }
|
||||
// }
|
||||
</style>
|
@ -0,0 +1,60 @@
|
||||
import { http } from '@/utils/http/axios';
|
||||
|
||||
/**
|
||||
* @description: 列表
|
||||
*/
|
||||
export function getMessageTemplateList(params?) {
|
||||
return http.request({
|
||||
url: '/message/template/page',
|
||||
method: 'GET',
|
||||
params,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 根据ID获取详情
|
||||
*/
|
||||
export function getMessageTemplateDetail(id) {
|
||||
return http.request({
|
||||
url: '/message/template/detail/'+id,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 添加
|
||||
*/
|
||||
export function messageTemplateAdd(data:any) {
|
||||
return http.request({
|
||||
url: '/message/template/add',
|
||||
method: 'POST',
|
||||
data,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 更新
|
||||
*/
|
||||
export function messageTemplateUpdate(data:any) {
|
||||
return http.request({
|
||||
url: '/message/template/update',
|
||||
method: 'PUT',
|
||||
data
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 删除
|
||||
*/
|
||||
export function messageTemplateDelete(id) {
|
||||
return http.request({
|
||||
url: '/message/template/delete/'+id,
|
||||
method: 'DELETE',
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 批量删除
|
||||
*/
|
||||
export function messageTemplateBatchDelete(data:any) {
|
||||
return http.request({
|
||||
url: '/message/template/batchDelete',
|
||||
method: 'DELETE',
|
||||
data
|
||||
});
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
import { http } from '@/utils/http/axios';
|
||||
|
||||
/**
|
||||
* @description: 列表
|
||||
*/
|
||||
export function getTableList(params?) {
|
||||
return http.request({
|
||||
url: '/admin/generator/page',
|
||||
method: 'GET',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 一键生成
|
||||
*/
|
||||
export function generator(data: any) {
|
||||
return http.request({
|
||||
url: '/admin/generator/generator',
|
||||
method: 'POST',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 批量删除
|
||||
*/
|
||||
export function batchGenerator(data: any) {
|
||||
return http.request({
|
||||
url: '/admin/generator/batchGenerate',
|
||||
method: 'POST',
|
||||
data,
|
||||
});
|
||||
}
|
@ -0,0 +1,214 @@
|
||||
<template>
|
||||
<a-modal v-model:visible="props.visible" :title="props.tenantId ? '编辑' : '新增'" :width="800"
|
||||
style="top:20px;" @cancel="dialogClose">
|
||||
<a-form ref="formRef" :model="formData" :label-col="{ style: { width: '140px' }}">
|
||||
<div class="flex">
|
||||
<a-form-item
|
||||
label="租户图片"
|
||||
name="image"
|
||||
:rules="{ required: true,message: '请上传租户图片', trigger: 'change' }"
|
||||
>
|
||||
<UploadImg @changeFileName="(name)=>formData.imageImgName=name"
|
||||
:fileType=" ['image/jpeg', 'image/png', 'image/jpg', 'image/gif']"
|
||||
name="tenant"
|
||||
:fileSize="200"
|
||||
v-model:image-url="formData.image" :cropper="false">
|
||||
<template v-slot:tip>支持扩展名: jpg png jpeg;文件大小不超过200M</template>
|
||||
</UploadImg>
|
||||
</a-form-item>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<a-form-item label="名称" name="name" class="flex-1"
|
||||
:rules="{ required: true, message: '请输入名称', trigger: 'blur' }">
|
||||
<a-input v-model:value="formData.name" placeholder="请输入名称" allow-clear />
|
||||
</a-form-item>
|
||||
<a-form-item label="编号" name="code" class="flex-1"
|
||||
:rules="{ required: true, message: '请输入编号', trigger: 'blur' }">
|
||||
<a-input v-model:value="formData.code" allow-clear
|
||||
placeholder="请输入编号" />
|
||||
</a-form-item>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<a-form-item label="统一社会信用代码" name="license" class="flex-1"
|
||||
:rules="{ required: true, message: '请输入统一社会信用代码', trigger: 'blur' }">
|
||||
<a-input v-model:value="formData.license" placeholder="请输入统一社会信用代码" allow-clear />
|
||||
</a-form-item>
|
||||
<a-form-item label="联系人" name="contactUser" class="flex-1"
|
||||
:rules="{ required: true, message: '请输入联系人', trigger: 'blur' }">
|
||||
<a-input v-model:value="formData.contactUser" placeholder="请输入联系人" allow-clear />
|
||||
</a-form-item>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<a-form-item label="联系电话" name="contactMobile" class="flex-1"
|
||||
:rules="{ required: true, message: '请输入联系电话', trigger: 'blur' }">
|
||||
<a-input v-model:value="formData.contactMobile" placeholder="请输入联系电话" allow-clear />
|
||||
</a-form-item>
|
||||
<a-form-item label="邮箱地址" name="contactEmail" class="flex-1"
|
||||
:rules="[{ required: true, message: '请输入邮箱地址', trigger: 'blur' },{ type: 'email', message: '请输入正确邮箱地址', trigger: 'blur'}]">
|
||||
<a-input v-model:value="formData.contactEmail" placeholder="请输入邮箱地址" allow-clear />
|
||||
</a-form-item>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<a-form-item label="官网地址" name="contactSite" class="flex-1"
|
||||
:rules="{ required: true, message: '请输入官网地址', trigger: 'blur'}">
|
||||
<a-input v-model:value="formData.contactSite" placeholder="请输入官网地址" allow-clear />
|
||||
</a-form-item>
|
||||
<a-form-item label="用户限额" name="number" class="flex-1"
|
||||
:rules="{ required: true, message: '请输入用户限额', trigger: 'blur'}">
|
||||
<number-input v-model="formData.number" placeholder="请输入用户限额" allow-clear />
|
||||
</a-form-item>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<a-form-item label="过期时间" name="expireTime" class="flex-1"
|
||||
:rules="{ required: true, message: '请选择过期时间', trigger: 'change'}">
|
||||
<a-date-picker show-time placeholder="请选择过期时间" v-model:value="formData.expireTime" format="YYYY-MM-DD HH:mm:ss" valueFormat="YYYY-MM-DD HH:mm:ss"/>
|
||||
</a-form-item>
|
||||
<a-form-item label="租户地址" name="contactAddress" class="flex-1"
|
||||
:rules="{ required: true, message: '请输入租户地址', trigger: 'blur'}">
|
||||
<a-input v-model:value="formData.contactAddress" placeholder="请输入租户地址" allow-clear />
|
||||
</a-form-item>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<a-form-item label="简介" name="contactIntro" class="flex-1"
|
||||
:rules="{ required: true, message: '请输入简介', trigger: 'blur'}">
|
||||
<a-input v-model:value="formData.contactIntro" type="textarea" placeholder="请输入简介" allow-clear />
|
||||
</a-form-item>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<a-form-item label="备注" name="contactNote" class="flex-1">
|
||||
<a-input v-model:value="formData.contactNote" type="textarea" placeholder="请输入备注" allow-clear />
|
||||
</a-form-item>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<a-form-item label="租户状态" name="status" class="flex-1">
|
||||
<a-radio-group v-model:value="formData.status" name="status">
|
||||
<a-radio :value="0">正常</a-radio>
|
||||
<a-radio :value="1">禁用</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
</div>
|
||||
|
||||
</a-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<a-button @click="dialogClose">取消</a-button>
|
||||
<a-button :loading="subLoading" type="primary" @click="submit">
|
||||
确定
|
||||
</a-button>
|
||||
</span>
|
||||
</template>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { getTenantDetail, tenantAdd, tenantUpdate } from '@/api/system/tenant';
|
||||
import { onMounted, reactive, shallowRef } from "vue";
|
||||
import { getRoleAllList } from '@/api/system/role';
|
||||
import { getDeptList } from '@/api/system/dept';
|
||||
import { getLevelAllList } from '@/api/system/level';
|
||||
import { getPositionAllList } from '@/api/system/position';
|
||||
import UploadImg from "@/components/Upload/Image.vue";
|
||||
import {buildTree } from "@/utils/auth";
|
||||
import type { FormInstance } from 'ant-design-vue';
|
||||
|
||||
const formRef = shallowRef<FormInstance>();
|
||||
import { useLockFn } from "@/utils/useLockFn";
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
default: false
|
||||
},
|
||||
tenantId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
default: 0
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(["success", "update:visible"]);
|
||||
const formData = reactive({
|
||||
id: 0,
|
||||
code:'',
|
||||
name: '',
|
||||
image:'',
|
||||
license: "",
|
||||
contactUser: "",
|
||||
contactMobile: '',
|
||||
contactEmail: '',
|
||||
contactAddress:'',
|
||||
contactIntro: '',
|
||||
contactSite: '',
|
||||
contactNote: '',
|
||||
expireTime: '',
|
||||
status: 0,
|
||||
number: ''
|
||||
});
|
||||
const passwordConfirmValidator = (
|
||||
rule: object,
|
||||
value: string,
|
||||
callback: any
|
||||
) => {
|
||||
if (formData.password) {
|
||||
if (!value) callback(new Error("请再次输入密码"));
|
||||
if (value !== formData.password) callback(new Error("两次输入密码不一致!"));
|
||||
}
|
||||
callback();
|
||||
};
|
||||
|
||||
const dialogClose = () => {
|
||||
emit("update:visible", false);
|
||||
};
|
||||
|
||||
const setFormData = async (row: any) => {
|
||||
const data = await getTenantDetail(row.tenantId);
|
||||
for (const key in formData) {
|
||||
if (data[key] != null && data[key] != undefined) {
|
||||
formData[key] = data[key];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
console.log(formData)
|
||||
await formRef.value?.validate();
|
||||
props.tenantId ? await tenantUpdate(formData) : await tenantAdd(formData);
|
||||
emit("update:visible", false);
|
||||
emit("success");
|
||||
};
|
||||
|
||||
const { isLock: subLoading, lockFn: submit } = useLockFn(handleSubmit);
|
||||
|
||||
const optionData = reactive({
|
||||
roleList: [],
|
||||
deptList: [],
|
||||
levelList: [],
|
||||
positionList: []
|
||||
});
|
||||
|
||||
function uploadChange(data: string[]) {
|
||||
formData.avatar = data.fileUrl;
|
||||
formData.avatarName =data.fileName
|
||||
}
|
||||
const handleDelete =async(file)=>{
|
||||
console.log(file)
|
||||
}
|
||||
|
||||
const getAllDict = async () => {
|
||||
let list = await getRoleAllList();
|
||||
optionData.roleList = list ? list : [];
|
||||
list = await getDeptList();
|
||||
optionData.deptList = list ? buildTree(list) : [];
|
||||
list = await getLevelAllList();
|
||||
optionData.levelList = list ? list : [];
|
||||
list = await getPositionAllList();
|
||||
optionData.positionList = list ? list : [];
|
||||
};
|
||||
onMounted(() => {
|
||||
getAllDict()
|
||||
if (props.tenantId) {
|
||||
setFormData({ tenantId: props.tenantId });
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<div class="page-wrapper" :class="{ 'footer-space': showFooter && getShowFooter }">
|
||||
<div class="mb-4 n-layout-page-header" v-if="title || content || $slots.headerContent">
|
||||
<a-card :bordered="false" :title="title">
|
||||
{{ content }}
|
||||
<slot name="headerContent"></slot>
|
||||
</a-card>
|
||||
</div>
|
||||
<div class="page-wrapper-content" :style="contentStyle" :class="contentClass">
|
||||
<slot></slot>
|
||||
</div>
|
||||
<page-footer v-if="showFooter && getShowFooter" :style="getFooterWidth">
|
||||
<template #left>
|
||||
<slot name="leftFooter"></slot>
|
||||
</template>
|
||||
<template #right>
|
||||
<slot name="rightFooter"></slot>
|
||||
</template>
|
||||
</page-footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, useSlots } from 'vue';
|
||||
import { basicProps } from './wrapperProps';
|
||||
import PageFooter from './PageFooter.vue';
|
||||
import { useProjectSetting } from '@/hooks/setting/useProjectSetting';
|
||||
|
||||
const slots = useSlots();
|
||||
defineProps({ ...basicProps });
|
||||
|
||||
const { getMenuCollapsed, getMenuMinWidth, getMenuWidth } = useProjectSetting();
|
||||
|
||||
const getShowFooter = computed(() => slots?.leftFooter || slots?.rightFooter);
|
||||
|
||||
const getFooterWidth = computed(() => {
|
||||
const wh = getMenuCollapsed.value ? getMenuMinWidth.value : getMenuWidth.value;
|
||||
return {
|
||||
width: `calc(100% - ${wh}px)`,
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.page-wrapper {
|
||||
.mb-4 {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.footer-space {
|
||||
padding-bottom: 64px;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,142 @@
|
||||
<template>
|
||||
<a-modal
|
||||
v-model:visible="props.visible"
|
||||
:title="props.noticeId ? '编辑' : '新增'"
|
||||
:width="`${size}px`"
|
||||
@cancel="dialogClose"
|
||||
style="top: 20px"
|
||||
>
|
||||
<a-form
|
||||
class="ls-form"
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:label-col="{ style: { width: '80px' } }"
|
||||
>
|
||||
<a-form-item
|
||||
label="通知标题"
|
||||
name="title"
|
||||
:rules="{ required: true, message: '请输入通知标题', trigger: 'blur' }"
|
||||
>
|
||||
<a-input class="ls-input" v-model:value="formData.title" placeholder="通知标题" clearable />
|
||||
</a-form-item>
|
||||
<a-form-item label="通知类型">
|
||||
<a-select v-model:value="formData.type" placeholder="请选择通知类型">
|
||||
<a-select-option :value="1">通知</a-select-option>
|
||||
<a-select-option :value="2">公告</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="通知状态" name="status">
|
||||
<a-radio-group v-model:value="formData.status" name="status">
|
||||
<a-radio :value="1">正常</a-radio>
|
||||
<a-radio :value="2">关闭</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<div class="flex">
|
||||
<a-form-item
|
||||
class="flex-1"
|
||||
label="通知内容"
|
||||
name="content"
|
||||
:rules="{ required: true, message: '请输入通知内容', trigger: 'blur' }"
|
||||
>
|
||||
<Editor ref="editorRef" :height="fwbHeight" class="flex-1" name="data" />
|
||||
</a-form-item>
|
||||
</div>
|
||||
</a-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<a-button @click="dialogClose">取消</a-button>
|
||||
<a-button :loading="subLoading" type="primary" @click="submit"> 确定 </a-button>
|
||||
</span>
|
||||
</template>
|
||||
</a-modal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import type { FormInstance } from 'element-plus';
|
||||
import { getNoticeDetail, noticeAdd, noticeUpdate } from '@/api/data/notice';
|
||||
import { onMounted, reactive, shallowRef, ref } from 'vue';
|
||||
import Editor from '@/components/Editor/tinymce.vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { useLockFn } from '@/utils/useLockFn';
|
||||
|
||||
/**
|
||||
* 定义参数变量
|
||||
*/
|
||||
const size = document.body.clientWidth - 500;
|
||||
const emit = defineEmits(['success', 'update:visible']);
|
||||
const formRef = shallowRef<FormInstance>();
|
||||
const editorRef = ref();
|
||||
|
||||
/**
|
||||
* 定义表单参数
|
||||
*/
|
||||
const formData = reactive({
|
||||
id: '',
|
||||
title: '',
|
||||
status: 1,
|
||||
type: 1,
|
||||
content: '',
|
||||
});
|
||||
|
||||
/**
|
||||
* 定义接收的参数
|
||||
*/
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
default: false,
|
||||
},
|
||||
noticeId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
default: 0,
|
||||
},
|
||||
});
|
||||
const fwbHeight = document.body.clientHeight - 400;
|
||||
|
||||
/**
|
||||
* 执行提交表单
|
||||
*/
|
||||
const handleSubmit = async () => {
|
||||
formData.content = editorRef.value.myValue;
|
||||
await formRef.value?.validate();
|
||||
let ruleForm = JSON.parse(JSON.stringify(formData));
|
||||
ruleForm.content = editorRef.value.myValue;
|
||||
props.noticeId ? await noticeUpdate(ruleForm) : await noticeAdd(ruleForm);
|
||||
message.success('操作成功');
|
||||
emit('update:visible', false);
|
||||
emit('success');
|
||||
};
|
||||
|
||||
/**
|
||||
* 关闭窗体
|
||||
*/
|
||||
const dialogClose = () => {
|
||||
emit('update:visible', false);
|
||||
};
|
||||
|
||||
const { isLock: subLoading, lockFn: submit } = useLockFn(handleSubmit);
|
||||
|
||||
/**
|
||||
* 设置表单数据
|
||||
*/
|
||||
const setFormData = async () => {
|
||||
const data = await getNoticeDetail(props.noticeId);
|
||||
for (const key in formData) {
|
||||
if (data[key] != null && data[key] != undefined) {
|
||||
//@ts-ignore
|
||||
formData[key] = data[key];
|
||||
}
|
||||
}
|
||||
editorRef.value.myValue = formData.content;
|
||||
};
|
||||
|
||||
/**
|
||||
* 钩子函数
|
||||
*/
|
||||
onMounted(() => {
|
||||
if (props.noticeId) {
|
||||
setFormData();
|
||||
}
|
||||
});
|
||||
</script>
|
@ -0,0 +1,108 @@
|
||||
<template>
|
||||
<div ref="chartRef" :style="{ height, width }"></div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref, Ref } from 'vue';
|
||||
|
||||
import { useECharts } from '@/hooks/web/useECharts';
|
||||
|
||||
import { basicProps } from './props';
|
||||
|
||||
defineProps({
|
||||
...basicProps,
|
||||
});
|
||||
const chartRef = ref<HTMLDivElement | null>(null);
|
||||
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
|
||||
|
||||
onMounted(() => {
|
||||
setOptions({
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
lineStyle: {
|
||||
width: 1,
|
||||
color: '#019680',
|
||||
},
|
||||
},
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: [
|
||||
'6:00',
|
||||
'7:00',
|
||||
'8:00',
|
||||
'9:00',
|
||||
'10:00',
|
||||
'11:00',
|
||||
'12:00',
|
||||
'13:00',
|
||||
'14:00',
|
||||
'15:00',
|
||||
'16:00',
|
||||
'17:00',
|
||||
'18:00',
|
||||
'19:00',
|
||||
'20:00',
|
||||
'21:00',
|
||||
'22:00',
|
||||
'23:00',
|
||||
],
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
width: 1,
|
||||
type: 'solid',
|
||||
color: 'rgba(226,226,226,0.5)',
|
||||
},
|
||||
},
|
||||
axisTick: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
max: 80000,
|
||||
splitNumber: 4,
|
||||
axisTick: {
|
||||
show: false,
|
||||
},
|
||||
splitArea: {
|
||||
show: true,
|
||||
areaStyle: {
|
||||
color: ['rgba(255,255,255,0.2)', 'rgba(226,226,226,0.2)'],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
grid: { left: '1%', right: '1%', top: '2 %', bottom: 0, containLabel: true },
|
||||
series: [
|
||||
{
|
||||
smooth: true,
|
||||
data: [
|
||||
111, 222, 4000, 18000, 33333, 55555, 66666, 33333, 14000, 36000, 66666, 44444, 22222,
|
||||
11111, 4000, 2000, 500, 333, 222, 111,
|
||||
],
|
||||
type: 'line',
|
||||
areaStyle: {},
|
||||
itemStyle: {
|
||||
color: '#5ab1ef',
|
||||
},
|
||||
},
|
||||
{
|
||||
smooth: true,
|
||||
data: [
|
||||
33, 66, 88, 333, 3333, 5000, 18000, 3000, 1200, 13000, 22000, 11000, 2221, 1201, 390,
|
||||
198, 60, 30, 22, 11,
|
||||
],
|
||||
type: 'line',
|
||||
areaStyle: {},
|
||||
itemStyle: {
|
||||
color: '#019680',
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
</script>
|
@ -0,0 +1,60 @@
|
||||
import { http } from '@/utils/http/axios';
|
||||
|
||||
/**
|
||||
* @description: 列表
|
||||
*/
|
||||
export function getArticleList(params?) {
|
||||
return http.request({
|
||||
url: '/article/page',
|
||||
method: 'GET',
|
||||
params,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 根据ID获取详情
|
||||
*/
|
||||
export function getArticleDetail(articleId) {
|
||||
return http.request({
|
||||
url: '/article/detail/'+articleId,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 添加
|
||||
*/
|
||||
export function articleAdd(data:any) {
|
||||
return http.request({
|
||||
url: '/article/add',
|
||||
method: 'POST',
|
||||
data,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 更新
|
||||
*/
|
||||
export function articleUpdate(data:any) {
|
||||
return http.request({
|
||||
url: '/article/update',
|
||||
method: 'PUT',
|
||||
data
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 删除
|
||||
*/
|
||||
export function articleDelete(articleId) {
|
||||
return http.request({
|
||||
url: '/article/delete/'+articleId,
|
||||
method: 'DELETE',
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 批量删除
|
||||
*/
|
||||
export function articleBatchDelete(data:any) {
|
||||
return http.request({
|
||||
url: '/article/batchDelete',
|
||||
method: 'DELETE',
|
||||
data
|
||||
});
|
||||
}
|
@ -0,0 +1,307 @@
|
||||
<template>
|
||||
<PageWrapper>
|
||||
<a-card :bordered="false" class="pt-3 mb-3 proCard">
|
||||
<BasicForm @register="register" @submit="handleSubmit" @reset="handleReset" />
|
||||
</a-card>
|
||||
<a-card :bordered="false" class="proCard">
|
||||
<BasicTable
|
||||
:columns="columns"
|
||||
:request="loadDataTable"
|
||||
:row-key="(row) => row.id"
|
||||
ref="tableRef"
|
||||
:row-selection="{ onChange: onSelectionChange }"
|
||||
>
|
||||
<template #tableTitle>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="handleAdd" v-perm="['sys:job:add']">
|
||||
<template #icon>
|
||||
<PlusOutlined />
|
||||
</template>
|
||||
添加任务
|
||||
</a-button>
|
||||
<a-button
|
||||
type="danger"
|
||||
@click="handleDelete()"
|
||||
:disabled="!selectionData.length"
|
||||
v-perm="['sys:job:batchDelete']"
|
||||
>
|
||||
<template #icon>
|
||||
<DeleteOutlined />
|
||||
</template>
|
||||
删除
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'status'">
|
||||
<a-switch v-model:checked="record.realStatus" @change="handelSetStatus(record)" />
|
||||
</template>
|
||||
<template v-else-if="column.key === 'action'">
|
||||
<a-space>
|
||||
<a-button
|
||||
type="primary"
|
||||
@click="handelChangeStatus(record)"
|
||||
v-perm="['sys:job:resume']"
|
||||
v-if="record.status == 0 || record.status == 2"
|
||||
>
|
||||
<template #icon><FieldTimeOutlined /></template>
|
||||
启动
|
||||
</a-button>
|
||||
<a-button
|
||||
type="primary"
|
||||
@click="handelChangeStatus(record)"
|
||||
v-perm="['sys:job:pause']"
|
||||
v-if="record.status == 1"
|
||||
>
|
||||
<template #icon><FieldTimeOutlined /></template>
|
||||
暂停
|
||||
</a-button>
|
||||
<a-button type="primary" @click="handleEdit(record.id)" v-perm="['sys:job:update']">
|
||||
<template #icon><EditOutlined /></template>
|
||||
编辑
|
||||
</a-button>
|
||||
<a-button
|
||||
type="primary"
|
||||
danger
|
||||
@click="handleDelete(record.id)"
|
||||
v-perm="['sys:job:delete']"
|
||||
>
|
||||
<template #icon><DeleteOutlined /></template>
|
||||
删除
|
||||
</a-button>
|
||||
<a-dropdown>
|
||||
<a-button class="ant-dropdown-link" @click.prevent>
|
||||
更多<DownOutlined />
|
||||
</a-button>
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item>
|
||||
<a href="javascript:;" @click="handleJobLog(record.id)">调度日志</a>
|
||||
</a-menu-item>
|
||||
<a-menu-item v-if="record.status == 2">
|
||||
<a href="javascript:;" @click="handelRunOnce(record.id)">执行一次</a>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</a-card>
|
||||
|
||||
<editDialog
|
||||
v-if="editVisible"
|
||||
:jobId="jobId"
|
||||
v-model:visible="editVisible"
|
||||
@success="reloadTable('noRefresh')"
|
||||
/>
|
||||
<jobLog v-if="editLogVisible" :jobId="jobId" v-model:visible="editLogVisible" />
|
||||
</PageWrapper>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref, h, nextTick, defineAsyncComponent } from 'vue';
|
||||
import { schemas } from './querySchemas';
|
||||
import { useForm } from '@/components/Form/index';
|
||||
import { TableAction } from '@/components/Table';
|
||||
import {
|
||||
getJobList,
|
||||
jobDelete,
|
||||
jobBatchDelete,
|
||||
getJobRunOnce,
|
||||
setJobStatus,
|
||||
getJobPause,
|
||||
getJobResume,
|
||||
} from '@/api/monitor/job';
|
||||
import { columns } from './columns';
|
||||
import {
|
||||
PlusOutlined,
|
||||
EditOutlined,
|
||||
DeleteOutlined,
|
||||
FieldTimeOutlined,
|
||||
DownOutlined,
|
||||
} from '@ant-design/icons-vue';
|
||||
import { Modal, message } from 'ant-design-vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
/**
|
||||
* 导入组件
|
||||
*/
|
||||
const editDialog = defineAsyncComponent(() => import('./edit.vue'));
|
||||
const jobLog = defineAsyncComponent(() => import('./log/index.vue'));
|
||||
|
||||
/**
|
||||
* 定义参数变量
|
||||
*/
|
||||
const router = useRouter();
|
||||
const jobId = ref(0);
|
||||
const editVisible = ref(false);
|
||||
const editLogVisible = ref(false);
|
||||
const selectionData = ref([]);
|
||||
const tableRef = ref();
|
||||
|
||||
/**
|
||||
* 定义查询参数
|
||||
*/
|
||||
const formParams = reactive({
|
||||
jobName: '',
|
||||
status: '',
|
||||
});
|
||||
|
||||
/**
|
||||
* 加载数据列表
|
||||
* @param res 参数
|
||||
*/
|
||||
const loadDataTable = async (res: any) => {
|
||||
const result = await getJobList({ ...formParams, ...res });
|
||||
result.records.map((item) => {
|
||||
if (item.status == 0) {
|
||||
item.realStatus = false;
|
||||
} else {
|
||||
item.realStatus = true;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* 刷新数据列表
|
||||
* @param noRefresh 参数
|
||||
*/
|
||||
function reloadTable(noRefresh = '') {
|
||||
tableRef.value.reload(noRefresh ? {} : { pageNo: 1 });
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册
|
||||
*/
|
||||
const [register, {}] = useForm({
|
||||
rowProps: { gutter: [16, 0] },
|
||||
colProps: {
|
||||
xs: 24,
|
||||
sm: 24,
|
||||
md: 12,
|
||||
lg: 8,
|
||||
xl: 6,
|
||||
},
|
||||
labelCol: { span: 6, offset: 0 },
|
||||
schemas,
|
||||
});
|
||||
|
||||
/**
|
||||
* 执行提交表单
|
||||
*/
|
||||
function handleSubmit(values) {
|
||||
for (const key in formParams) {
|
||||
formParams[key] = '';
|
||||
}
|
||||
for (const key in values) {
|
||||
formParams[key] = values[key];
|
||||
}
|
||||
reloadTable();
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行重置
|
||||
*/
|
||||
function handleReset() {
|
||||
for (const key in formParams) {
|
||||
formParams[key] = '';
|
||||
}
|
||||
reloadTable();
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行添加
|
||||
*/
|
||||
const handleAdd = async () => {
|
||||
jobId.value = 0;
|
||||
await nextTick();
|
||||
editVisible.value = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* 执行设置状态
|
||||
*/
|
||||
const handelSetStatus = async (row) => {
|
||||
await setJobStatus({ id: row.id, status: row.realStatus ? 1 : 0 });
|
||||
message.success('设置成功');
|
||||
reloadTable('noRefresh');
|
||||
};
|
||||
|
||||
/**
|
||||
* 执行编辑
|
||||
* @param id 参数
|
||||
*/
|
||||
const handleEdit = async (id) => {
|
||||
jobId.value = id;
|
||||
await nextTick();
|
||||
editVisible.value = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* 执行状态发生变化
|
||||
* @param record 参数
|
||||
*/
|
||||
const handelChangeStatus = async (record) => {
|
||||
Modal.confirm({
|
||||
title: '提示',
|
||||
content: `确定要${record.status == 1 ? '暂停' : '启动'}`,
|
||||
onOk: async () => {
|
||||
record.status == 1 ? await getJobPause(record.id) : await getJobResume(record.id);
|
||||
reloadTable('noRefresh');
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 执行一次
|
||||
*/
|
||||
const handelRunOnce = async (id) => {
|
||||
Modal.confirm({
|
||||
title: '提示',
|
||||
content: '确定要执行一次?',
|
||||
onOk: async () => {
|
||||
await getJobRunOnce(id);
|
||||
message.success('执行成功');
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 执行删除
|
||||
* @param id 参数
|
||||
*/
|
||||
async function handleDelete(id) {
|
||||
Modal.confirm({
|
||||
title: '提示',
|
||||
content: '确定要删除?',
|
||||
onOk: async () => {
|
||||
id ? await jobDelete(id) : await jobBatchDelete(selectionData.value);
|
||||
message.success('删除成功');
|
||||
reloadTable();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行查看调度日志
|
||||
* @param id 参数
|
||||
*/
|
||||
const handleJobLog = (id) => {
|
||||
// router.push({path:'/monitorJob/log'})
|
||||
editLogVisible.value = true;
|
||||
jobId.value = id;
|
||||
};
|
||||
|
||||
/**
|
||||
* 选项发生变化
|
||||
* @param value 参数
|
||||
*/
|
||||
function onSelectionChange(value) {
|
||||
selectionData.value = value;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
@ -0,0 +1,318 @@
|
||||
<template>
|
||||
<a-modal
|
||||
v-model:visible="props.visible"
|
||||
:title="props.tenantId ? '编辑' : '新增'"
|
||||
:width="800"
|
||||
style="top: 20px"
|
||||
@cancel="dialogClose"
|
||||
>
|
||||
<a-form ref="formRef" :model="formData" :label-col="{ style: { width: '140px' } }">
|
||||
<div class="flex">
|
||||
<a-form-item
|
||||
label="租户图片"
|
||||
name="image"
|
||||
:rules="{ required: true, message: '请上传租户图片', trigger: 'change' }"
|
||||
>
|
||||
<UploadImg
|
||||
@change-file-name="(name) => (formData.imageImgName = name)"
|
||||
:fileType="['image/jpeg', 'image/png', 'image/jpg', 'image/gif']"
|
||||
name="tenant"
|
||||
:fileSize="200"
|
||||
v-model:image-url="formData.image"
|
||||
:cropper="false"
|
||||
>
|
||||
<template #tip>支持扩展名: jpg png jpeg;文件大小不超过200M</template>
|
||||
</UploadImg>
|
||||
</a-form-item>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<a-form-item
|
||||
label="名称"
|
||||
name="name"
|
||||
class="flex-1"
|
||||
:rules="{ required: true, message: '请输入名称', trigger: 'blur' }"
|
||||
>
|
||||
<a-input v-model:value="formData.name" placeholder="请输入名称" allow-clear />
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="编号"
|
||||
name="code"
|
||||
class="flex-1"
|
||||
:rules="{ required: true, message: '请输入编号', trigger: 'blur' }"
|
||||
>
|
||||
<a-input v-model:value="formData.code" allow-clear placeholder="请输入编号" />
|
||||
</a-form-item>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<a-form-item
|
||||
label="统一社会信用代码"
|
||||
name="license"
|
||||
class="flex-1"
|
||||
:rules="{ required: true, message: '请输入统一社会信用代码', trigger: 'blur' }"
|
||||
>
|
||||
<a-input
|
||||
v-model:value="formData.license"
|
||||
placeholder="请输入统一社会信用代码"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="联系人"
|
||||
name="contactUser"
|
||||
class="flex-1"
|
||||
:rules="{ required: true, message: '请输入联系人', trigger: 'blur' }"
|
||||
>
|
||||
<a-input v-model:value="formData.contactUser" placeholder="请输入联系人" allow-clear />
|
||||
</a-form-item>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<a-form-item
|
||||
label="联系电话"
|
||||
name="contactMobile"
|
||||
class="flex-1"
|
||||
:rules="{ required: true, message: '请输入联系电话', trigger: 'blur' }"
|
||||
>
|
||||
<a-input
|
||||
v-model:value="formData.contactMobile"
|
||||
placeholder="请输入联系电话"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="邮箱地址"
|
||||
name="contactEmail"
|
||||
class="flex-1"
|
||||
:rules="[
|
||||
{ required: true, message: '请输入邮箱地址', trigger: 'blur' },
|
||||
{ type: 'email', message: '请输入正确邮箱地址', trigger: 'blur' },
|
||||
]"
|
||||
>
|
||||
<a-input v-model:value="formData.contactEmail" placeholder="请输入邮箱地址" allow-clear />
|
||||
</a-form-item>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<a-form-item
|
||||
label="官网地址"
|
||||
name="contactSite"
|
||||
class="flex-1"
|
||||
:rules="{ required: true, message: '请输入官网地址', trigger: 'blur' }"
|
||||
>
|
||||
<a-input v-model:value="formData.contactSite" placeholder="请输入官网地址" allow-clear />
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="用户限额"
|
||||
name="number"
|
||||
class="flex-1"
|
||||
:rules="{ required: true, message: '请输入用户限额', trigger: 'blur' }"
|
||||
>
|
||||
<number-input v-model="formData.number" placeholder="请输入用户限额" allow-clear />
|
||||
</a-form-item>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<a-form-item
|
||||
label="过期时间"
|
||||
name="expireTime"
|
||||
class="flex-1"
|
||||
:rules="{ required: true, message: '请选择过期时间', trigger: 'change' }"
|
||||
>
|
||||
<a-date-picker
|
||||
show-time
|
||||
placeholder="请选择过期时间"
|
||||
v-model:value="formData.expireTime"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
valueFormat="YYYY-MM-DD HH:mm:ss"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="租户地址"
|
||||
name="contactAddress"
|
||||
class="flex-1"
|
||||
:rules="{ required: true, message: '请输入租户地址', trigger: 'blur' }"
|
||||
>
|
||||
<a-input
|
||||
v-model:value="formData.contactAddress"
|
||||
placeholder="请输入租户地址"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<a-form-item
|
||||
label="简介"
|
||||
name="contactIntro"
|
||||
class="flex-1"
|
||||
:rules="{ required: true, message: '请输入简介', trigger: 'blur' }"
|
||||
>
|
||||
<a-input
|
||||
v-model:value="formData.contactIntro"
|
||||
type="textarea"
|
||||
placeholder="请输入简介"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<a-form-item label="备注" name="contactNote" class="flex-1">
|
||||
<a-input
|
||||
v-model:value="formData.contactNote"
|
||||
type="textarea"
|
||||
placeholder="请输入备注"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<a-form-item label="租户状态" name="status" class="flex-1">
|
||||
<a-radio-group v-model:value="formData.status" name="status">
|
||||
<a-radio :value="0">正常</a-radio>
|
||||
<a-radio :value="1">禁用</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
</div>
|
||||
</a-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<a-button @click="dialogClose">取消</a-button>
|
||||
<a-button :loading="subLoading" type="primary" @click="submit"> 确定 </a-button>
|
||||
</span>
|
||||
</template>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { getTenantDetail, tenantAdd, tenantUpdate } from '@/api/system/tenant';
|
||||
import { onMounted, reactive, shallowRef } from 'vue';
|
||||
import { getRoleAllList } from '@/api/system/role';
|
||||
import { getDeptList } from '@/api/system/dept';
|
||||
import { getLevelAllList } from '@/api/system/level';
|
||||
import { getPositionAllList } from '@/api/system/position';
|
||||
import UploadImg from '@/components/Upload/Image.vue';
|
||||
import { buildTree } from '@/utils/auth';
|
||||
import type { FormInstance } from 'ant-design-vue';
|
||||
import { useLockFn } from '@/utils/useLockFn';
|
||||
|
||||
/**
|
||||
* 定义参数变量
|
||||
*/
|
||||
const formRef = shallowRef<FormInstance>();
|
||||
const emit = defineEmits(['success', 'update:visible']);
|
||||
|
||||
/**
|
||||
* 定义接收的参数
|
||||
*/
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
default: false,
|
||||
},
|
||||
tenantId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
default: 0,
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* 定义接收的参数
|
||||
*/
|
||||
const formData = reactive({
|
||||
id: 0,
|
||||
code: '',
|
||||
name: '',
|
||||
image: '',
|
||||
license: '',
|
||||
contactUser: '',
|
||||
contactMobile: '',
|
||||
contactEmail: '',
|
||||
contactAddress: '',
|
||||
contactIntro: '',
|
||||
contactSite: '',
|
||||
contactNote: '',
|
||||
expireTime: '',
|
||||
status: 0,
|
||||
number: '',
|
||||
});
|
||||
const passwordConfirmValidator = (rule: object, value: string, callback: any) => {
|
||||
if (formData.password) {
|
||||
if (!value) callback(new Error('请再次输入密码'));
|
||||
if (value !== formData.password) callback(new Error('两次输入密码不一致!'));
|
||||
}
|
||||
callback();
|
||||
};
|
||||
|
||||
/**
|
||||
* 关闭窗体
|
||||
*/
|
||||
const dialogClose = () => {
|
||||
emit('update:visible', false);
|
||||
};
|
||||
|
||||
/**
|
||||
* 设置表单数据
|
||||
* @param row 参数
|
||||
*/
|
||||
const setFormData = async (row: any) => {
|
||||
const data = await getTenantDetail(row.tenantId);
|
||||
for (const key in formData) {
|
||||
if (data[key] != null && data[key] != undefined) {
|
||||
formData[key] = data[key];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 执行提交表单
|
||||
*/
|
||||
const handleSubmit = async () => {
|
||||
console.log(formData);
|
||||
await formRef.value?.validate();
|
||||
props.tenantId ? await tenantUpdate(formData) : await tenantAdd(formData);
|
||||
emit('update:visible', false);
|
||||
emit('success');
|
||||
};
|
||||
|
||||
const { isLock: subLoading, lockFn: submit } = useLockFn(handleSubmit);
|
||||
|
||||
/**
|
||||
* 定义选项数据
|
||||
*/
|
||||
const optionData = reactive({
|
||||
roleList: [],
|
||||
deptList: [],
|
||||
levelList: [],
|
||||
positionList: [],
|
||||
});
|
||||
|
||||
function uploadChange(data: string[]) {
|
||||
formData.avatar = data.fileUrl;
|
||||
formData.avatarName = data.fileName;
|
||||
}
|
||||
const handleDelete = async (file) => {
|
||||
console.log(file);
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取全部字典数据
|
||||
*/
|
||||
const getAllDict = async () => {
|
||||
let list = await getRoleAllList();
|
||||
optionData.roleList = list ? list : [];
|
||||
list = await getDeptList();
|
||||
optionData.deptList = list ? buildTree(list) : [];
|
||||
list = await getLevelAllList();
|
||||
optionData.levelList = list ? list : [];
|
||||
list = await getPositionAllList();
|
||||
optionData.positionList = list ? list : [];
|
||||
};
|
||||
|
||||
/**
|
||||
* 钩子函数
|
||||
*/
|
||||
onMounted(() => {
|
||||
getAllDict();
|
||||
if (props.tenantId) {
|
||||
setFormData({ tenantId: props.tenantId });
|
||||
}
|
||||
});
|
||||
</script>
|
@ -0,0 +1,119 @@
|
||||
<template>
|
||||
<a-modal v-model:visible="props.visible" title="文件日志详情" width="800px" @cancel="dialogClose">
|
||||
<a-descriptions class="margin-top" :column="2" bordered :labelStyle="{ width: '125px' }">
|
||||
<a-descriptions-item label="文件名称:"
|
||||
><a :href="formData.filePath" target="_blank">{{
|
||||
formData.originalName
|
||||
}}</a></a-descriptions-item
|
||||
>
|
||||
<a-descriptions-item label="文件类型:">{{ formData.fileType }}</a-descriptions-item>
|
||||
<a-descriptions-item label="文件大小:">{{ formData.fileSize }}B</a-descriptions-item>
|
||||
<a-descriptions-item label="请求耗时:">{{ formData.consumeTime }}s</a-descriptions-item>
|
||||
<a-descriptions-item label="业务类型:">{{
|
||||
formData.bizType ? (formData.bizType == 1 ? '订单' : '其他') : ''
|
||||
}}</a-descriptions-item>
|
||||
<a-descriptions-item label="业务ID:">{{ formData.bizId }}</a-descriptions-item>
|
||||
<a-descriptions-item label="业务内容" :span="3">
|
||||
{{ formData.bizContent }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="请求状态" :span="3">
|
||||
{{ formData.status ? '失败' : '成功' }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="请求参数" :span="3">
|
||||
{{ formData.param }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="返回结果" :span="3">
|
||||
{{ formData.result }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="错误描述" :span="3">
|
||||
{{ formData.error }}
|
||||
</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<a-button @click="dialogClose">取消</a-button>
|
||||
</span>
|
||||
</template>
|
||||
</a-modal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { getFileLogDetail } from '@/api/logger/fileLog';
|
||||
import { onMounted, ref } from 'vue';
|
||||
|
||||
const emit = defineEmits(['success', 'update:visible']);
|
||||
const formData = ref({});
|
||||
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
default: false,
|
||||
},
|
||||
fileId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
default: 0,
|
||||
},
|
||||
});
|
||||
|
||||
const dialogClose = () => {
|
||||
emit('update:visible', false);
|
||||
};
|
||||
|
||||
const setFormData = async () => {
|
||||
const data = await getFileLogDetail(props.fileId);
|
||||
formData.value = data;
|
||||
};
|
||||
const getReviceType = (type) => {
|
||||
let typeText = '';
|
||||
switch (type) {
|
||||
case 1:
|
||||
typeText = '系统用户';
|
||||
break;
|
||||
case 2:
|
||||
typeText = '会员用户';
|
||||
break;
|
||||
case 3:
|
||||
typeText = '其他';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return typeText;
|
||||
};
|
||||
// 此处作为经典案例,禁止删除
|
||||
const getTyepText = (type) => {
|
||||
let typeText = '';
|
||||
switch (type) {
|
||||
case 1:
|
||||
typeText = '登录';
|
||||
break;
|
||||
case 2:
|
||||
typeText = '注册';
|
||||
break;
|
||||
case 3:
|
||||
typeText = '找回密码';
|
||||
break;
|
||||
case 4:
|
||||
typeText = '业务';
|
||||
break;
|
||||
case 5:
|
||||
typeText = '其他';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return typeText;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
if (props.fileId) {
|
||||
setFormData();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
:deep(.ant-descriptions__body .ant-descriptions__table .ant-descriptions__cell) {
|
||||
word-break: break-all;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,182 @@
|
||||
<template>
|
||||
<a-modal
|
||||
v-model:visible="props.visible"
|
||||
:title="dialogTitle"
|
||||
width="1350px"
|
||||
style="top:15px;"
|
||||
@cancel="dialogClose"
|
||||
>
|
||||
<a-form style="display: flex" ref="formRef" :model="formData" :label-col="{ style: { width: '80px' }}">
|
||||
<div style="width: 880px;flex: none" :style="{ height: fwbHeight + 'px' }">
|
||||
<a-form-item label-width="0px" name="content">
|
||||
<Editor ref="editorRef" :height="fwbHeight" name="content" />
|
||||
</a-form-item>
|
||||
</div>
|
||||
<div style="flex:1;margin-left: 10px">
|
||||
<a-form-item label="广告标题" name="title" :rules="{ required: true, message: '请输入广告标题', trigger: 'blur' }">
|
||||
<a-input placeholder="请输入广告标题" :maxlength="50" v-model:value="formData.title"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="广告位" name="adSortId" class="flex-1"
|
||||
:rules="{ required: true, message: '请选择广告位', trigger: 'change' }">
|
||||
<a-select v-model:value="formData.adSortId" class="flex-1" allow-clear placeholder="请选择广告位">
|
||||
<a-select-option v-for="(item, index) in adSortList" :key="index" :value="item.id">{{ item.name }}</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="广告格式" name="type" :rules="{ required: true, message: '请选择广告格式', trigger: 'change' }">
|
||||
<a-select v-model:value="formData.type" placeholder="请选择广告格式">
|
||||
<a-select-option :value="1">图片</a-select-option>
|
||||
<a-select-option :value="2">文字</a-select-option>
|
||||
<a-select-option :value="3">视频</a-select-option>
|
||||
<a-select-option :value="4">推荐</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="广告封面" name="cover" :rules="{ required: true, message: '请上传广告封面', trigger: 'change' }">
|
||||
<UploadImg
|
||||
:fileType="['image/jpeg', 'image/png', 'image/jpg', 'image/gif']" name="ad" :fileSize="200"
|
||||
v-model:image-url="formData.cover">
|
||||
<template v-slot:tip>支持扩展名: jpg png jpeg;文件大小不超过200M</template>
|
||||
</UploadImg>
|
||||
</a-form-item>
|
||||
<a-form-item label="广告链接" name="url" :rules="{ required: true, message: '请输入广告链接', trigger: 'blur' }">
|
||||
<a-input v-model:value="formData.url" type="textarea" placeholder="请输入广告链接" />
|
||||
</a-form-item>
|
||||
<a-form-item label="广告宽度" name="width"
|
||||
:rules="{ required: true, message: '请输入广告宽度', trigger: 'change' }">
|
||||
<number-input v-model:value="formData.width" placeholder="请输入广告宽度" allow-clear />
|
||||
</a-form-item>
|
||||
<a-form-item label="广告高度" name="height"
|
||||
:rules="{ required: true, message: '请输入广告高度', trigger: 'blur' }">
|
||||
<number-input v-model:value="formData.height" placeholder="请输入广告高度" allow-clear />
|
||||
</a-form-item>
|
||||
<a-form-item label="广告时间" name="adTime" class="flex-1" :rules="{ required: true, message: '请选择广告时间', trigger: 'change' }">
|
||||
<a-range-picker v-model:value="formData.adTime" value-format="YYYY-MM-DD HH:mm:ss" style="width:100%"
|
||||
:show-time="{ format: 'HH:mm' }" :placeholder="['开始时间', '结束时间']">
|
||||
</a-range-picker>
|
||||
</a-form-item>
|
||||
<a-form-item label="点击率" name="click" :rules="{ required: true, message: '请输入点击率', trigger: 'blur' }">
|
||||
<number-input v-model:value="formData.click" placeholder="请输入点击率" allow-clear />
|
||||
</a-form-item>
|
||||
<a-form-item label="广告描述" name="note" :rules="{ required: true, message: '请输入备注', trigger: 'blur' }">
|
||||
<a-input v-model:value="formData.note" type="textarea" placeholder="请输入广告描述" />
|
||||
</a-form-item>
|
||||
<a-form-item label="排序" name="sort">
|
||||
<a-input-number v-model:value="formData.sort" :max="9999" placeholder="请输入排序" />
|
||||
</a-form-item>
|
||||
<a-form-item label="状态">
|
||||
<a-radio-group v-model:value="formData.status" name="status">
|
||||
<a-radio :value="1">在用</a-radio>
|
||||
<a-radio :value="2">停用</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
</div>
|
||||
</a-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<a-button @click="dialogClose">取消</a-button>
|
||||
<a-button :loading="subLoading" type="primary" @click="submit">确认</a-button>
|
||||
</span>
|
||||
</template>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { adAdd, adUpdate, getAdDetail } from '@/api/content/ad';
|
||||
import { getAdSortAllList } from '@/api/content/adSort';
|
||||
import { computed, onMounted, ref, shallowRef, reactive } from "vue";
|
||||
import type { FormInstance } from 'ant-design-vue';
|
||||
const formRef = shallowRef<FormInstance>();
|
||||
import UploadImg from "@/components/Upload/Image.vue";
|
||||
import { useLockFn } from "@/utils/useLockFn";
|
||||
import Editor from '@/components/Editor/tinymce.vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
default: false
|
||||
},
|
||||
adId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
default: 0
|
||||
},
|
||||
searchParamData: {
|
||||
type: Object,
|
||||
default: {}
|
||||
}
|
||||
});
|
||||
const adSortList = ref([])
|
||||
const adTime = ref([])
|
||||
const emit = defineEmits(["success", "update:visible"]);
|
||||
|
||||
const dialogTitle = computed(() => {
|
||||
return props.adId ? "编辑内容" : "新增内容";
|
||||
});
|
||||
|
||||
const fwbHeight = document.body.clientHeight - 180
|
||||
|
||||
const formData = reactive({
|
||||
id: "",
|
||||
title: "",
|
||||
adSortId: undefined,
|
||||
cover: '',
|
||||
type: undefined,
|
||||
note: '',
|
||||
content: '',
|
||||
url: '',
|
||||
width: "",
|
||||
height: "",
|
||||
adTime:[],
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
click: '',
|
||||
status: 1,
|
||||
sort: 0,
|
||||
});
|
||||
|
||||
const dialogClose = () => {
|
||||
emit("update:visible", false);
|
||||
};
|
||||
|
||||
const setFormData = async () => {
|
||||
const data = await getAdDetail(props.adId);
|
||||
for (const key in formData) {
|
||||
if (data[key] != null && data[key] != undefined) {
|
||||
formData[key] = data[key];
|
||||
}
|
||||
}
|
||||
formData.adTime = [formData.startTime,formData.endTime]
|
||||
editorRef.value.myValue = formData.content
|
||||
};
|
||||
|
||||
const editorRef = ref()
|
||||
const handleSubmit = async () => {
|
||||
formData.startTime = formData.adTime.length>0?formData.adTime[0]:''
|
||||
formData.endTime = formData.adTime.length>0?formData.adTime[1]:''
|
||||
await formRef.value?.validate();
|
||||
let ruleForm = JSON.parse(JSON.stringify(formData));
|
||||
ruleForm.content = editorRef.value.myValue
|
||||
if (!ruleForm.content) {
|
||||
message.error("请添加文章内容");
|
||||
return;
|
||||
}
|
||||
props.adId ? await adUpdate(ruleForm) : await adAdd(ruleForm);
|
||||
message.success("操作成功");
|
||||
emit("update:visible", false);
|
||||
emit("success");
|
||||
};
|
||||
|
||||
const { isLock: subLoading, lockFn: submit } = useLockFn(handleSubmit);
|
||||
const getAllDict = async () => {
|
||||
let list = await getAdSortAllList();
|
||||
adSortList.value = list ? list : [];
|
||||
};
|
||||
onMounted(() => {
|
||||
getAllDict()
|
||||
if (props.adId) {
|
||||
setFormData();
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
@ -0,0 +1,164 @@
|
||||
<template>
|
||||
<div ref="chartRef" :style="{ height, width }"></div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, PropType, ref, Ref } from 'vue';
|
||||
import { useECharts } from '@/hooks/web/useECharts';
|
||||
|
||||
defineProps({
|
||||
width: {
|
||||
type: String as PropType<string>,
|
||||
default: '100%',
|
||||
},
|
||||
height: {
|
||||
type: String as PropType<string>,
|
||||
default: '350px',
|
||||
},
|
||||
});
|
||||
const chartRef = ref<HTMLDivElement | null>(null);
|
||||
const { setOptions, echarts } = useECharts(chartRef as Ref<HTMLDivElement>);
|
||||
|
||||
const option = {
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
},
|
||||
tooltip: {
|
||||
show: true,
|
||||
},
|
||||
series: [
|
||||
{
|
||||
data: [
|
||||
{
|
||||
value: 8765,
|
||||
itemStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: '#2378f7' },
|
||||
{ offset: 0.7, color: '#2378f7' },
|
||||
{ offset: 1, color: '#83bff6' },
|
||||
]),
|
||||
},
|
||||
},
|
||||
{
|
||||
value: 4598,
|
||||
itemStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: '#2378f7' },
|
||||
{ offset: 0.7, color: '#2378f7' },
|
||||
{ offset: 1, color: '#83bff6' },
|
||||
]),
|
||||
},
|
||||
},
|
||||
{
|
||||
value: 13567,
|
||||
itemStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: '#2378f7' },
|
||||
{ offset: 0.7, color: '#2378f7' },
|
||||
{ offset: 1, color: '#83bff6' },
|
||||
]),
|
||||
},
|
||||
},
|
||||
{
|
||||
value: 4789,
|
||||
itemStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: '#2378f7' },
|
||||
{ offset: 0.7, color: '#2378f7' },
|
||||
{ offset: 1, color: '#83bff6' },
|
||||
]),
|
||||
},
|
||||
},
|
||||
{
|
||||
value: 9876,
|
||||
itemStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: '#2378f7' },
|
||||
{ offset: 0.7, color: '#2378f7' },
|
||||
{ offset: 1, color: '#83bff6' },
|
||||
]),
|
||||
},
|
||||
},
|
||||
{
|
||||
value: 5478,
|
||||
itemStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: '#2378f7' },
|
||||
{ offset: 0.7, color: '#2378f7' },
|
||||
{ offset: 1, color: '#83bff6' },
|
||||
]),
|
||||
},
|
||||
},
|
||||
{
|
||||
value: 3289,
|
||||
itemStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: '#2378f7' },
|
||||
{ offset: 0.7, color: '#2378f7' },
|
||||
{ offset: 1, color: '#83bff6' },
|
||||
]),
|
||||
},
|
||||
},
|
||||
{
|
||||
value: 13423,
|
||||
itemStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: '#2378f7' },
|
||||
{ offset: 0.7, color: '#2378f7' },
|
||||
{ offset: 1, color: '#83bff6' },
|
||||
]),
|
||||
},
|
||||
},
|
||||
{
|
||||
value: 6543,
|
||||
itemStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: '#2378f7' },
|
||||
{ offset: 0.7, color: '#2378f7' },
|
||||
{ offset: 1, color: '#83bff6' },
|
||||
]),
|
||||
},
|
||||
},
|
||||
{
|
||||
value: 7689,
|
||||
itemStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: '#2378f7' },
|
||||
{ offset: 0.7, color: '#2378f7' },
|
||||
{ offset: 1, color: '#83bff6' },
|
||||
]),
|
||||
},
|
||||
},
|
||||
{
|
||||
value: 8235,
|
||||
itemStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: '#2378f7' },
|
||||
{ offset: 0.7, color: '#2378f7' },
|
||||
{ offset: 1, color: '#83bff6' },
|
||||
]),
|
||||
},
|
||||
},
|
||||
{
|
||||
value: 6578,
|
||||
itemStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: '#2378f7' },
|
||||
{ offset: 0.7, color: '#2378f7' },
|
||||
{ offset: 1, color: '#83bff6' },
|
||||
]),
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'bar',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
setOptions(option as any);
|
||||
});
|
||||
</script>
|
@ -0,0 +1,43 @@
|
||||
import { http } from '@/utils/http/axios';
|
||||
|
||||
/**
|
||||
* @description: 短信日志列表
|
||||
*/
|
||||
export function getSmsLogList(params?) {
|
||||
return http.request({
|
||||
url: '/sms/log/page',
|
||||
method: 'GET',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 根据ID获取详情
|
||||
*/
|
||||
export function getSmsLogDetail(id) {
|
||||
return http.request({
|
||||
url: '/sms/log/detail/' + id,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 删除登录日志
|
||||
*/
|
||||
export function smsLogDelete(id) {
|
||||
return http.request({
|
||||
url: '/sms/log/delete/' + id,
|
||||
method: 'DELETE',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 批量删除登录日志
|
||||
*/
|
||||
export function smsLogBatchDelete(data: any) {
|
||||
return http.request({
|
||||
url: '/sms/log/batchDelete',
|
||||
method: 'DELETE',
|
||||
data,
|
||||
});
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<a-card
|
||||
:bordered="false"
|
||||
bodyStyle="padding: 0;width:100%"
|
||||
class="page-wrapper-footer"
|
||||
:class="{ 'dark-bg': getDarkTheme }"
|
||||
>
|
||||
<div class="flex items-center w-full">
|
||||
<div class="flex-1 page-wrapper-footer-left"><slot name="left"></slot></div>
|
||||
<div class="page-wrapper-footer-right"><slot name="right"></slot></div>
|
||||
</div>
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useProjectSetting } from '@/hooks/setting/useProjectSetting';
|
||||
const { getDarkTheme } = useProjectSetting();
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.page-wrapper-footer {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 999;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
padding: 15px 24px;
|
||||
box-shadow: -3px 1px 2px -2px rgba(0, 0, 0, 0.08), 0 3px 6px 0 rgba(0, 0, 0, 0.08),
|
||||
-3px 5px 12px 4px rgba(0, 0, 0, 0.08);
|
||||
transition: width 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.dark-bg {
|
||||
background: rgb(24, 24, 28);
|
||||
}
|
||||
</style>
|
@ -0,0 +1,129 @@
|
||||
<template>
|
||||
<PageWrapper>
|
||||
<a-card :bordered="false" class="pt-3 mb-3 proCard">
|
||||
<BasicForm @register="register" @submit="handleSubmit" @reset="handleReset"></BasicForm>
|
||||
</a-card>
|
||||
<a-card :bordered="false" class="proCard">
|
||||
<BasicTable
|
||||
:columns="columns"
|
||||
:request="loadDataTable"
|
||||
:row-key="(row) => row.id"
|
||||
ref="tableRef"
|
||||
:row-selection="{onChange: onSelectionChange}"
|
||||
>
|
||||
<template #tableTitle>
|
||||
<a-button type="danger" @click="handleDelete()" :disabled="!selectionData.length" v-perm="['sys:loginLog:batchDelete']">
|
||||
<template #icon>
|
||||
<DeleteOutlined />
|
||||
</template>
|
||||
删除
|
||||
</a-button>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'action'">
|
||||
<a-space>
|
||||
<a-button type="primary" @click="handleInfo(record.id)" v-perm="['sys:loginLog:detail']">
|
||||
<template #icon><EditOutlined /></template>
|
||||
详情
|
||||
</a-button>
|
||||
<a-button type="primary" danger @click="handleDelete(record.id)" v-perm="['sys:loginLog:delete']">
|
||||
<template #icon><DeleteOutlined /></template>
|
||||
删除
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</a-card>
|
||||
|
||||
<editDialog
|
||||
v-if="editVisible"
|
||||
:loginlogId="loginlogId"
|
||||
v-model:visible="editVisible"
|
||||
@success="reloadTable('noRefresh')"
|
||||
>
|
||||
</editDialog>
|
||||
</PageWrapper>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref, h,nextTick,defineAsyncComponent } from 'vue';
|
||||
import { EditOutlined,DeleteOutlined} from '@ant-design/icons-vue';
|
||||
import { schemas } from './loginLog/querySchemas';
|
||||
import { useForm } from '@/components/Form/index';
|
||||
import { TableAction } from '@/components/Table';
|
||||
import { getLoginLogList,loginLogDelete,loginLogBatchDelete } from '@/api/system/loginLog';
|
||||
import { columns } from './loginLog/columns';
|
||||
import { Modal,message } from 'ant-design-vue';
|
||||
const editDialog = defineAsyncComponent(() =>
|
||||
import('./loginLog/edit.vue')
|
||||
)
|
||||
const loginlogId =ref(0)
|
||||
const editVisible=ref(false)
|
||||
const selectionData = ref([])
|
||||
const tableRef = ref();
|
||||
const formParams = reactive({
|
||||
username:'',
|
||||
type:'',
|
||||
status:''
|
||||
});
|
||||
|
||||
const loadDataTable = async (res: any) => {
|
||||
const result = await getLoginLogList({ ...formParams, ...res });
|
||||
return result;
|
||||
};
|
||||
|
||||
function reloadTable(noRefresh='') {
|
||||
tableRef.value.reload(noRefresh?{}:{pageNo:1});
|
||||
}
|
||||
const [register, {}] = useForm({
|
||||
rowProps: { gutter: [16, 0] },
|
||||
colProps: {
|
||||
xs: 24,
|
||||
sm: 24,
|
||||
md: 12,
|
||||
lg: 8,
|
||||
xl: 6,
|
||||
},
|
||||
labelCol: { span: 6, offset: 0 },
|
||||
schemas
|
||||
});
|
||||
function handleSubmit(values) {
|
||||
for (const key in formParams) {
|
||||
formParams[key] ='';
|
||||
}
|
||||
for (const key in values) {
|
||||
formParams[key] = values[key]
|
||||
}
|
||||
reloadTable();
|
||||
}
|
||||
|
||||
function handleReset() {
|
||||
for (const key in formParams) {
|
||||
formParams[key] ='';
|
||||
}
|
||||
reloadTable();
|
||||
}
|
||||
const handleInfo = async (id) => {
|
||||
loginlogId.value=id
|
||||
await nextTick();
|
||||
editVisible.value=true
|
||||
};
|
||||
|
||||
async function handleDelete(id) {
|
||||
Modal.confirm({
|
||||
title: '提示',
|
||||
content: "确定要删除?",
|
||||
onOk: async () => {
|
||||
id? await loginLogDelete(id):await loginLogBatchDelete(selectionData.value);
|
||||
message.success("删除成功");
|
||||
reloadTable()
|
||||
}
|
||||
});
|
||||
}
|
||||
function onSelectionChange(value){
|
||||
selectionData.value = value
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
@ -0,0 +1,81 @@
|
||||
import { h } from 'vue';
|
||||
import { Tag } from 'ant-design-vue';
|
||||
|
||||
export const columns = [
|
||||
{
|
||||
title: '部门名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
title: '部门类型',
|
||||
dataIndex: 'type',
|
||||
key: 'type',
|
||||
width: 100,
|
||||
customRender({ record }) {
|
||||
let typeText = '';
|
||||
let color = '';
|
||||
switch (record.type) {
|
||||
case 1:
|
||||
typeText = '公司';
|
||||
color = 'processing';
|
||||
break;
|
||||
case 2:
|
||||
typeText = '子公司';
|
||||
color = 'success';
|
||||
break;
|
||||
case 3:
|
||||
typeText = '部门';
|
||||
color = 'warning';
|
||||
break;
|
||||
case 4:
|
||||
typeText = '小组';
|
||||
color = 'default';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return h(
|
||||
Tag,
|
||||
{
|
||||
color: color,
|
||||
},
|
||||
{
|
||||
default: () => typeText,
|
||||
},
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '部门排序',
|
||||
dataIndex: 'sort',
|
||||
key: 'sort',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '部门备注',
|
||||
dataIndex: 'note',
|
||||
key: 'note',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '创建人',
|
||||
dataIndex: 'createUser',
|
||||
key: 'createUser',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createTime',
|
||||
key: 'createTime',
|
||||
width: 180,
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
fixed: 'right',
|
||||
dataIndex: 'action',
|
||||
key: 'action',
|
||||
width: 200,
|
||||
},
|
||||
];
|
@ -0,0 +1,60 @@
|
||||
import { http } from '@/utils/http/axios';
|
||||
|
||||
/**
|
||||
* @description: 列表
|
||||
*/
|
||||
export function getFileTemplateList(params?) {
|
||||
return http.request({
|
||||
url: '/file/template/page',
|
||||
method: 'GET',
|
||||
params,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 根据ID获取详情
|
||||
*/
|
||||
export function getFileTemplateDetail(id) {
|
||||
return http.request({
|
||||
url: '/file/template/detail/' + id,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 添加
|
||||
*/
|
||||
export function fileTemplateAdd(data: any) {
|
||||
return http.request({
|
||||
url: '/file/template/add',
|
||||
method: 'POST',
|
||||
data,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 更新
|
||||
*/
|
||||
export function fileTemplateUpdate(data: any) {
|
||||
return http.request({
|
||||
url: '/file/template/update',
|
||||
method: 'PUT',
|
||||
data,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 删除
|
||||
*/
|
||||
export function fileTemplateDelete(id) {
|
||||
return http.request({
|
||||
url: '/file/template/delete/' + id,
|
||||
method: 'DELETE',
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 批量删除
|
||||
*/
|
||||
export function fileTemplateBatchDelete(data: any) {
|
||||
return http.request({
|
||||
url: '/file/template/batchDelete',
|
||||
method: 'DELETE',
|
||||
data,
|
||||
});
|
||||
}
|
@ -0,0 +1,158 @@
|
||||
<template>
|
||||
<PageWrapper>
|
||||
<a-card :bordered="false" class="pt-3 mb-3 proCard">
|
||||
<BasicForm @register="register" @submit="handleSubmit" @reset="handleReset">
|
||||
<template #statusSlot="{ model, field }">
|
||||
<a-input v-model="model[field]" />
|
||||
</template>
|
||||
</BasicForm>
|
||||
</a-card>
|
||||
<a-card :bordered="false" class="proCard">
|
||||
<BasicTable
|
||||
:columns="columns"
|
||||
:request="loadDataTable"
|
||||
:row-key="(row) => row.id"
|
||||
ref="tableRef"
|
||||
scroll-x="1200"
|
||||
:row-selection="{ onChange: onSelectionChange }"
|
||||
virtual-scroll
|
||||
>
|
||||
<template #tableTitle>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="handleAdd" v-perm="['sys:article:add']">
|
||||
<template #icon>
|
||||
<PlusOutlined />
|
||||
</template>
|
||||
添加文章
|
||||
</a-button>
|
||||
|
||||
<a-button
|
||||
type="danger"
|
||||
@click="handleDelete()"
|
||||
:disabled="!selectionData.length"
|
||||
v-perm="['sys:article:batchDelete']"
|
||||
>
|
||||
<template #icon>
|
||||
<DeleteOutlined />
|
||||
</template>
|
||||
删除
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'action'">
|
||||
<a-space>
|
||||
<a-button
|
||||
type="primary"
|
||||
@click="handleEdit(record.id)"
|
||||
v-perm="['sys:article:update']"
|
||||
>
|
||||
<template #icon><EditOutlined /></template>
|
||||
编辑
|
||||
</a-button>
|
||||
<a-button
|
||||
type="primary"
|
||||
danger
|
||||
@click="handleDelete(record.id)"
|
||||
v-perm="['sys:article:delete']"
|
||||
>
|
||||
<template #icon><DeleteOutlined /></template>
|
||||
删除
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</a-card>
|
||||
<editDialog
|
||||
v-if="editVisible"
|
||||
:articleId="articleId"
|
||||
v-model:visible="editVisible"
|
||||
@success="reloadTable('noRefresh')"
|
||||
/>
|
||||
</PageWrapper>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { nextTick, reactive, ref, defineAsyncComponent } from 'vue';
|
||||
import { PlusOutlined, EditOutlined, DeleteOutlined } from '@ant-design/icons-vue';
|
||||
import { useForm } from '@/components/Form/index';
|
||||
import { getArticleList, articleDelete, articleBatchDelete } from '@/api/content/article';
|
||||
import { columns } from './columns';
|
||||
import { schemas } from './querySchemas';
|
||||
import { Modal, message } from 'ant-design-vue';
|
||||
const articleId = ref(0);
|
||||
const tableRef = ref();
|
||||
const editVisible = ref(false);
|
||||
const selectionData = ref([]);
|
||||
const editDialog = defineAsyncComponent(() => import('./edit.vue'));
|
||||
const formParams = reactive({
|
||||
title: '',
|
||||
status: '',
|
||||
});
|
||||
|
||||
const loadDataTable = async (res) => {
|
||||
const result = await getArticleList({ ...formParams, ...res });
|
||||
return result;
|
||||
};
|
||||
|
||||
function reloadTable(noRefresh = '') {
|
||||
tableRef.value.reload(noRefresh ? {} : { pageNo: 1 });
|
||||
}
|
||||
|
||||
async function handleEdit(id) {
|
||||
articleId.value = id;
|
||||
await nextTick();
|
||||
editVisible.value = true;
|
||||
}
|
||||
|
||||
async function handleDelete(id) {
|
||||
Modal.confirm({
|
||||
title: '提示',
|
||||
content: '确定要删除?',
|
||||
onOk: async () => {
|
||||
id ? await articleDelete(id) : await articleBatchDelete(selectionData.value);
|
||||
message.success('删除成功');
|
||||
reloadTable();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function handleSubmit(values: Recordable) {
|
||||
for (const key in formParams) {
|
||||
formParams[key] = '';
|
||||
}
|
||||
for (const key in values) {
|
||||
formParams[key] = values[key];
|
||||
}
|
||||
reloadTable();
|
||||
}
|
||||
|
||||
function handleReset(values) {
|
||||
for (const key in formParams) {
|
||||
formParams[key] = '';
|
||||
}
|
||||
reloadTable();
|
||||
}
|
||||
function onSelectionChange(value) {
|
||||
selectionData.value = value;
|
||||
}
|
||||
const [register, {}] = useForm({
|
||||
rowProps: { gutter: [16, 0] },
|
||||
colProps: {
|
||||
xs: 24,
|
||||
sm: 24,
|
||||
md: 12,
|
||||
lg: 8,
|
||||
xl: 6,
|
||||
},
|
||||
labelCol: { span: 6, offset: 0 },
|
||||
schemas,
|
||||
});
|
||||
//添加
|
||||
const handleAdd = async () => {
|
||||
articleId.value = 0;
|
||||
await nextTick();
|
||||
editVisible.value = true;
|
||||
};
|
||||
</script>
|
@ -0,0 +1,76 @@
|
||||
import { http } from '@/utils/http/axios';
|
||||
|
||||
/**
|
||||
* @description: 获取租户列表
|
||||
*/
|
||||
export function getTenantList(params) {
|
||||
return http.request({
|
||||
url: '/admin/tenant/page',
|
||||
method: 'get',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 根据ID获取详情
|
||||
*/
|
||||
export function getTenantDetail(tenantId) {
|
||||
return http.request({
|
||||
url: '/admin/tenant/detail/' + tenantId,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 添加租户
|
||||
*/
|
||||
export function tenantAdd(data: any) {
|
||||
return http.request({
|
||||
url: '/admin/tenant/add',
|
||||
method: 'POST',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 更新租户
|
||||
*/
|
||||
export function tenantUpdate(data: any) {
|
||||
return http.request({
|
||||
url: '/admin/tenant/update',
|
||||
method: 'PUT',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 删除租户
|
||||
*/
|
||||
export function tenantDelete(tenantId) {
|
||||
return http.request({
|
||||
url: '/admin/tenant/delete/' + tenantId,
|
||||
method: 'DELETE',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 批量删除租户
|
||||
*/
|
||||
export function tenantBatchDelete(data: any) {
|
||||
return http.request({
|
||||
url: '/admin/tenant/batchDelete',
|
||||
method: 'DELETE',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 创建租户账号
|
||||
*/
|
||||
export function tenantAccount(data: any) {
|
||||
return http.request({
|
||||
url: '/admin/tenant/account',
|
||||
method: 'POST',
|
||||
data,
|
||||
});
|
||||
}
|
@ -0,0 +1,241 @@
|
||||
<template>
|
||||
<PageWrapper>
|
||||
<a-row :gutter="10" class="mt-3">
|
||||
<a-col :xs="24" :sm="24" :md="6" :lg="6" :xl="6">
|
||||
<a-card shadow="hover" class="border-0">
|
||||
<template #title>
|
||||
<a-row>
|
||||
<a-col :span="10">
|
||||
<a-input
|
||||
type="text"
|
||||
v-model:value="params.name"
|
||||
placeholder="请输入字典名称"
|
||||
allow-clear
|
||||
/>
|
||||
</a-col>
|
||||
<a-col :span="14" style="text-align: right">
|
||||
<a-button
|
||||
type="primary"
|
||||
@click="
|
||||
pager.page = 1;
|
||||
loadDataTable();
|
||||
"
|
||||
>
|
||||
<template #icon> <SearchOutlined /> </template>查询
|
||||
</a-button>
|
||||
<a-button
|
||||
type="primary"
|
||||
@click="dictRefresh"
|
||||
style="margin-left: 8px"
|
||||
v-perm="['sys:dict:cache']"
|
||||
>
|
||||
<template #icon> <RedoOutlined /> </template>刷新缓存</a-button
|
||||
>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<div style="margin-top: 15px">
|
||||
<a-space>
|
||||
<a-button type="primary" @click="handleAdd" v-perm="['sys:dict:add']">
|
||||
<template #icon>
|
||||
<PlusOutlined />
|
||||
</template>
|
||||
新建
|
||||
</a-button>
|
||||
<a-button type="warning" @click="handleEdit" v-perm="['sys:dict:edit']">
|
||||
<template #icon> <EditOutlined /> </template>编辑
|
||||
</a-button>
|
||||
<a-button type="danger" @click="handleDelete()" v-perm="['sys:dict:delete']">
|
||||
<template #icon> <DeleteOutlined /> </template>删除
|
||||
</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
</template>
|
||||
<div :style="{ height: fwbHeight + 'px' }" class="dict-list-box">
|
||||
<div
|
||||
v-for="(item, index) in dictDataList"
|
||||
:key="index"
|
||||
@click="onCheckedRow(item)"
|
||||
class="dict-item"
|
||||
:class="item.id == dictId ? 'active' : ''"
|
||||
>
|
||||
<span class="t1"
|
||||
>{{ item.name }}<span class="t2">({{ item.code }})</span></span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<pagination
|
||||
style="justify-content: flex-end"
|
||||
class="mt-10 flex"
|
||||
@change="loadDataTable"
|
||||
v-model="pager"
|
||||
/>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :xs="24" :sm="24" :md="18" :lg="18" :xl="18">
|
||||
<a-card shadow="hover" class="mb-4 border-0 proCard">
|
||||
<dictItem :dictId="dictId" v-if="dictItemShow" />
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<editDialog
|
||||
v-if="editVisible"
|
||||
:dictId="dictId"
|
||||
v-model:visible="editVisible"
|
||||
@success="loadDataTable()"
|
||||
/>
|
||||
</PageWrapper>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, nextTick, defineAsyncComponent, onMounted } from 'vue';
|
||||
import { SearchOutlined } from '@ant-design/icons-vue';
|
||||
import { getDictList, refreshCache, dictDelete } from '@/api/data/dictionary';
|
||||
import { PlusOutlined, EditOutlined, DeleteOutlined, RedoOutlined } from '@ant-design/icons-vue';
|
||||
import dictItem from './dictItem.vue';
|
||||
import { Modal, message } from 'ant-design-vue';
|
||||
|
||||
/**
|
||||
* 导入组件
|
||||
*/
|
||||
const editDialog = defineAsyncComponent(() => import('./edit.vue'));
|
||||
|
||||
/**
|
||||
* 定义参数变量
|
||||
*/
|
||||
const dictId = ref(0);
|
||||
const dictItemShow = ref(false);
|
||||
const editVisible = ref(false);
|
||||
|
||||
/**
|
||||
* 定义查询参数
|
||||
*/
|
||||
const params = ref({
|
||||
name: '',
|
||||
});
|
||||
const dictDataList = ref([]);
|
||||
|
||||
/**
|
||||
* 定义分页参数
|
||||
*/
|
||||
const pager = ref({
|
||||
page: 1,
|
||||
size: 20,
|
||||
count: dictDataList.value.length,
|
||||
});
|
||||
const fwbHeight = document.body.clientHeight - 390;
|
||||
|
||||
/**
|
||||
* 添加字典
|
||||
*/
|
||||
const handleAdd = async () => {
|
||||
await nextTick();
|
||||
editVisible.value = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* 刷新缓存
|
||||
*/
|
||||
async function dictRefresh() {
|
||||
await refreshCache();
|
||||
message.success('刷新成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行编辑
|
||||
*/
|
||||
const handleEdit = () => {
|
||||
editVisible.value = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* 数据行选中事件
|
||||
* @param row 参数
|
||||
*/
|
||||
function onCheckedRow(row) {
|
||||
dictId.value = row.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载数据列表
|
||||
*/
|
||||
const loadDataTable = async () => {
|
||||
const result = await getDictList({
|
||||
...params.value,
|
||||
pageNo: pager.value.page,
|
||||
pageSize: pager.value.size,
|
||||
});
|
||||
dictId.value = result?.records[0]?.id;
|
||||
dictItemShow.value = true;
|
||||
dictDataList.value = result.records;
|
||||
pager.value.count = result.total;
|
||||
};
|
||||
|
||||
/**
|
||||
* 执行删除
|
||||
*/
|
||||
async function handleDelete() {
|
||||
Modal.confirm({
|
||||
title: '提示',
|
||||
content: '确定要删除?',
|
||||
onOk: async () => {
|
||||
dictDelete(dictId.value);
|
||||
message.success('删除成功');
|
||||
pager.value.page = 1;
|
||||
loadDataTable();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 钩子函数
|
||||
*/
|
||||
onMounted(() => {
|
||||
loadDataTable();
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.dict-list-box {
|
||||
overflow: auto;
|
||||
.dict-item {
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
padding: 0 6px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.t1 {
|
||||
font-size: 14px;
|
||||
display: block;
|
||||
font-weight: 700;
|
||||
.t2 {
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: #e8f1ff;
|
||||
border-radius: 3px;
|
||||
|
||||
.t1 {
|
||||
color: #1677ff;
|
||||
}
|
||||
.t2 {
|
||||
color: rgb(22, 119, 255, 0.8);
|
||||
}
|
||||
}
|
||||
|
||||
.ant-badge__content.is-fixed {
|
||||
top: 20px;
|
||||
right: calc(-10px + var(--ant-badge-size) / 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,142 @@
|
||||
<template>
|
||||
<a-modal
|
||||
v-model:visible="props.visible"
|
||||
:title="props.noticeId ? '编辑' : '新增'"
|
||||
:width="`${size}px`"
|
||||
@cancel="dialogClose"
|
||||
style="top: 20px"
|
||||
>
|
||||
<a-form
|
||||
class="ls-form"
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:label-col="{ style: { width: '80px' } }"
|
||||
>
|
||||
<a-form-item
|
||||
label="通知标题"
|
||||
name="title"
|
||||
:rules="{ required: true, message: '请输入通知标题', trigger: 'blur' }"
|
||||
>
|
||||
<a-input class="ls-input" v-model:value="formData.title" placeholder="通知标题" clearable />
|
||||
</a-form-item>
|
||||
<a-form-item label="通知类型">
|
||||
<a-select v-model:value="formData.type" placeholder="请选择通知类型">
|
||||
<a-select-option :value="1">通知</a-select-option>
|
||||
<a-select-option :value="2">公告</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="通知状态" name="status">
|
||||
<a-radio-group v-model:value="formData.status" name="status">
|
||||
<a-radio :value="1">正常</a-radio>
|
||||
<a-radio :value="2">关闭</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<div class="flex">
|
||||
<a-form-item
|
||||
class="flex-1"
|
||||
label="通知内容"
|
||||
name="content"
|
||||
:rules="{ required: true, message: '请输入通知内容', trigger: 'blur' }"
|
||||
>
|
||||
<Editor ref="editorRef" :height="fwbHeight" class="flex-1" name="notice" />
|
||||
</a-form-item>
|
||||
</div>
|
||||
</a-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<a-button @click="dialogClose">取消</a-button>
|
||||
<a-button :loading="subLoading" type="primary" @click="submit"> 确定 </a-button>
|
||||
</span>
|
||||
</template>
|
||||
</a-modal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import type { FormInstance } from 'element-plus';
|
||||
import { getNoticeDetail, noticeAdd, noticeUpdate } from '@/api/data/notice';
|
||||
import { onMounted, reactive, shallowRef, ref } from 'vue';
|
||||
import Editor from '@/components/Editor/tinymce.vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { useLockFn } from '@/utils/useLockFn';
|
||||
|
||||
/**
|
||||
* 定义参数变量
|
||||
*/
|
||||
const size = document.body.clientWidth - 500;
|
||||
const emit = defineEmits(['success', 'update:visible']);
|
||||
const formRef = shallowRef<FormInstance>();
|
||||
const editorRef = ref();
|
||||
|
||||
/**
|
||||
* 定义表单参数
|
||||
*/
|
||||
const formData = reactive({
|
||||
id: '',
|
||||
title: '',
|
||||
status: 1,
|
||||
type: 1,
|
||||
content: '',
|
||||
});
|
||||
|
||||
/**
|
||||
* 定义接收的参数
|
||||
*/
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
default: false,
|
||||
},
|
||||
noticeId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
default: 0,
|
||||
},
|
||||
});
|
||||
const fwbHeight = document.body.clientHeight - 400;
|
||||
|
||||
/**
|
||||
* 执行提交表单
|
||||
*/
|
||||
const handleSubmit = async () => {
|
||||
formData.content = editorRef.value.myValue;
|
||||
await formRef.value?.validate();
|
||||
let ruleForm = JSON.parse(JSON.stringify(formData));
|
||||
ruleForm.content = editorRef.value.myValue;
|
||||
props.noticeId ? await noticeUpdate(ruleForm) : await noticeAdd(ruleForm);
|
||||
message.success('操作成功');
|
||||
emit('update:visible', false);
|
||||
emit('success');
|
||||
};
|
||||
|
||||
/**
|
||||
* 关闭窗体
|
||||
*/
|
||||
const dialogClose = () => {
|
||||
emit('update:visible', false);
|
||||
};
|
||||
|
||||
const { isLock: subLoading, lockFn: submit } = useLockFn(handleSubmit);
|
||||
|
||||
/**
|
||||
* 设置表单数据
|
||||
*/
|
||||
const setFormData = async () => {
|
||||
const data = await getNoticeDetail(props.noticeId);
|
||||
for (const key in formData) {
|
||||
if (data[key] != null && data[key] != undefined) {
|
||||
//@ts-ignore
|
||||
formData[key] = data[key];
|
||||
}
|
||||
}
|
||||
editorRef.value.myValue = formData.content;
|
||||
};
|
||||
|
||||
/**
|
||||
* 钩子函数
|
||||
*/
|
||||
onMounted(() => {
|
||||
if (props.noticeId) {
|
||||
setFormData();
|
||||
}
|
||||
});
|
||||
</script>
|
@ -0,0 +1,71 @@
|
||||
import { h } from 'vue';
|
||||
import { Tag } from 'ant-design-vue';
|
||||
|
||||
export const columns = [
|
||||
{
|
||||
title: '城市名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '城市拼音',
|
||||
dataIndex: 'pinyin',
|
||||
key: 'pinyin',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '城市级别',
|
||||
dataIndex: 'level',
|
||||
key: 'level',
|
||||
width: 100,
|
||||
customRender({ record }) {
|
||||
let levelText = '';
|
||||
switch (record.level) {
|
||||
case 0:
|
||||
levelText = '省份';
|
||||
break;
|
||||
case 1:
|
||||
levelText = '城市';
|
||||
break;
|
||||
case 2:
|
||||
levelText = '县区';
|
||||
break;
|
||||
case 3:
|
||||
levelText = '街道';
|
||||
break;
|
||||
case 4:
|
||||
levelText = '居委会';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return h('span', levelText || '-');
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '城市区号',
|
||||
dataIndex: 'cityCode',
|
||||
key: 'cityCode',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '行政编码',
|
||||
dataIndex: 'areaCode',
|
||||
key: 'areaCode',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '城市邮编',
|
||||
dataIndex: 'zipCode',
|
||||
key: 'zipCode',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 200,
|
||||
fixed: 'right',
|
||||
key: 'action',
|
||||
dataIndex: 'action',
|
||||
},
|
||||
];
|
@ -0,0 +1,103 @@
|
||||
<template>
|
||||
<PageWrapper>
|
||||
<a-card :bordered="false" class="pt-3 mb-3 proCard">
|
||||
<BasicForm @register="register" @submit="handleSubmit" @reset="handleReset" />
|
||||
</a-card>
|
||||
<a-card :bordered="false" class="proCard">
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:style="{ minHeight: fwbHeight + 'px' }"
|
||||
:scroll="{ x: '100%', y: fwbHeight + 'px' }"
|
||||
:pagination="{ hideOnSinglePage: true }"
|
||||
:dataSource="onlineTableData.slice((pager.page - 1) * pager.size, pager.page * pager.size)"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'action'">
|
||||
<a-space>
|
||||
<a-button type="danger" @click="handleDelete(record.tokenId)">
|
||||
<template #icon><EditOutlined /></template>
|
||||
强退
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
<pagination
|
||||
style="justify-content: flex-end"
|
||||
class="mt-10 flex"
|
||||
v-model="pager"
|
||||
@change="loadDataTable"
|
||||
/>
|
||||
</a-card>
|
||||
</PageWrapper>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref, onMounted } from 'vue';
|
||||
import { columns } from './columns';
|
||||
import { schemas } from './querySchemas';
|
||||
import { useForm } from '@/components/Form/index';
|
||||
import { getOnlineList, onlineOut } from '@/api/monitor/online';
|
||||
import { Modal, message } from 'ant-design-vue';
|
||||
const onlineTableData = ref([]);
|
||||
const formParams = reactive({
|
||||
ipAddr: '',
|
||||
username: '',
|
||||
});
|
||||
const pager = ref({
|
||||
page: 1,
|
||||
size: 10,
|
||||
count: onlineTableData.value.length,
|
||||
});
|
||||
const fwbHeight = document.body.clientHeight - 420;
|
||||
const loadTable = async () => {
|
||||
onlineTableData.value = await getOnlineList({ ...formParams });
|
||||
pager.value.count = onlineTableData.value.length;
|
||||
};
|
||||
const [register, {}] = useForm({
|
||||
rowProps: { gutter: [16, 0] },
|
||||
colProps: {
|
||||
xs: 24,
|
||||
sm: 24,
|
||||
md: 12,
|
||||
lg: 8,
|
||||
xl: 6,
|
||||
},
|
||||
labelCol: { span: 6, offset: 0 },
|
||||
schemas,
|
||||
});
|
||||
function handleSubmit(values) {
|
||||
for (const key in formParams) {
|
||||
formParams[key] = '';
|
||||
}
|
||||
for (const key in values) {
|
||||
formParams[key] = values[key];
|
||||
}
|
||||
loadTable();
|
||||
}
|
||||
|
||||
function handleReset() {
|
||||
for (const key in formParams) {
|
||||
formParams[key] = '';
|
||||
}
|
||||
pager.value.page = 1;
|
||||
loadTable();
|
||||
}
|
||||
|
||||
async function handleDelete(id) {
|
||||
Modal.confirm({
|
||||
title: '提示',
|
||||
content: '确定要强退?',
|
||||
onOk: async () => {
|
||||
await onlineOut(id);
|
||||
message.success('强退成功');
|
||||
loadTable();
|
||||
},
|
||||
});
|
||||
}
|
||||
onMounted(() => {
|
||||
loadTable();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
@ -0,0 +1,68 @@
|
||||
/**
|
||||
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
|
||||
* Licensed under the LGPL or a commercial license.
|
||||
* For LGPL see License.txt in the project root for license information.
|
||||
* For commercial licenses see https://www.tiny.cloud/
|
||||
*/
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
line-height: 1.4;
|
||||
margin: 1rem auto;
|
||||
max-width: 900px;
|
||||
}
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
/* Apply a default padding if legacy cellpadding attribute is missing */
|
||||
table:not([cellpadding]) th,
|
||||
table:not([cellpadding]) td {
|
||||
padding: 0.4rem;
|
||||
}
|
||||
/* Set default table styles if a table has a positive border attribute
|
||||
and no inline css */
|
||||
table[border]:not([border="0"]):not([style*="border-width"]) th,
|
||||
table[border]:not([border="0"]):not([style*="border-width"]) td {
|
||||
border-width: 1px;
|
||||
}
|
||||
/* Set default table styles if a table has a positive border attribute
|
||||
and no inline css */
|
||||
table[border]:not([border="0"]):not([style*="border-style"]) th,
|
||||
table[border]:not([border="0"]):not([style*="border-style"]) td {
|
||||
border-style: solid;
|
||||
}
|
||||
/* Set default table styles if a table has a positive border attribute
|
||||
and no inline css */
|
||||
table[border]:not([border="0"]):not([style*="border-color"]) th,
|
||||
table[border]:not([border="0"]):not([style*="border-color"]) td {
|
||||
border-color: #ccc;
|
||||
}
|
||||
figure {
|
||||
display: table;
|
||||
margin: 1rem auto;
|
||||
}
|
||||
figure figcaption {
|
||||
color: #999;
|
||||
display: block;
|
||||
margin-top: 0.25rem;
|
||||
text-align: center;
|
||||
}
|
||||
hr {
|
||||
border-color: #ccc;
|
||||
border-style: solid;
|
||||
border-width: 1px 0 0 0;
|
||||
}
|
||||
code {
|
||||
background-color: #e8e8e8;
|
||||
border-radius: 3px;
|
||||
padding: 0.1rem 0.2rem;
|
||||
}
|
||||
.mce-content-body:not([dir=rtl]) blockquote {
|
||||
border-left: 2px solid #ccc;
|
||||
margin-left: 1.5rem;
|
||||
padding-left: 1rem;
|
||||
}
|
||||
.mce-content-body[dir=rtl] blockquote {
|
||||
border-right: 2px solid #ccc;
|
||||
margin-right: 1.5rem;
|
||||
padding-right: 1rem;
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
import { FormSchema } from '@/components/Form/index';
|
||||
export const schemas: FormSchema[] = [
|
||||
{
|
||||
name: 'name',
|
||||
component: 'Input',
|
||||
label: '岗位名称',
|
||||
componentProps: {
|
||||
placeholder: '请输入岗位名称',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'status',
|
||||
component: 'Select',
|
||||
label: '状态',
|
||||
componentProps: {
|
||||
placeholder: '请选择状态',
|
||||
clearable: true,
|
||||
options: [
|
||||
{
|
||||
label: '正常',
|
||||
value: '1',
|
||||
},
|
||||
{
|
||||
label: '停用',
|
||||
value: '2',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
@ -0,0 +1,66 @@
|
||||
import { h } from 'vue';
|
||||
|
||||
export const columns = [
|
||||
{
|
||||
title:'ID',
|
||||
dataIndex: 'id',
|
||||
},
|
||||
{
|
||||
title: '模板名称',
|
||||
dataIndex: 'title',
|
||||
},
|
||||
{
|
||||
title: '模板编码',
|
||||
dataIndex: 'code',
|
||||
},
|
||||
{
|
||||
title: '模板类型',
|
||||
dataIndex: 'type',
|
||||
customRender({ record }) {
|
||||
let typeText = ''
|
||||
switch (record.type) {
|
||||
case 1:
|
||||
typeText='普通邮件'
|
||||
break;
|
||||
case 2:
|
||||
typeText='图文邮件'
|
||||
break;
|
||||
case 3:
|
||||
typeText='模板文件'
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return h('span', typeText || '-');
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '文件名称',
|
||||
dataIndex: 'fileName',
|
||||
},
|
||||
{
|
||||
title: '文件路径',
|
||||
dataIndex: 'filePath',
|
||||
customRender({ record }) {
|
||||
return h('a', {
|
||||
href: record.filePath,
|
||||
target:"_blank"
|
||||
}, "点击查看文件");
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '创建人',
|
||||
dataIndex: 'createUser',
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createTime',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
fixed:'right',
|
||||
dataIndex: 'action',
|
||||
key: 'action',
|
||||
width: 200,
|
||||
},
|
||||
];
|
@ -0,0 +1,78 @@
|
||||
import { http } from '@/utils/http/axios';
|
||||
|
||||
/**
|
||||
* @description: 列表
|
||||
*/
|
||||
export function getAdSortList(params?) {
|
||||
return http.request({
|
||||
url: '/admin/ad/sort/page',
|
||||
method: 'GET',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取全部推荐位列表
|
||||
* @param params 参数
|
||||
* @returns 返回结果
|
||||
*/
|
||||
export function getAdSortAllList(params?) {
|
||||
return http.request({
|
||||
url: '/admin/ad/sort/list',
|
||||
method: 'GET',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 根据ID获取详情
|
||||
*/
|
||||
export function getAdSortDetail(adSortId) {
|
||||
return http.request({
|
||||
url: '/admin/ad/sort/detail/' + adSortId,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 添加
|
||||
*/
|
||||
export function adSortAdd(data: any) {
|
||||
return http.request({
|
||||
url: '/admin/ad/sort/add',
|
||||
method: 'POST',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 更新
|
||||
*/
|
||||
export function adSortUpdate(data: any) {
|
||||
return http.request({
|
||||
url: '/admin/ad/sort/update',
|
||||
method: 'PUT',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 删除
|
||||
*/
|
||||
export function adSortDelete(adSortId) {
|
||||
return http.request({
|
||||
url: '/admin/ad/sort/delete/' + adSortId,
|
||||
method: 'DELETE',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 批量删除
|
||||
*/
|
||||
export function adSortBatchDelete(data: any) {
|
||||
return http.request({
|
||||
url: '/admin/ad/sort/batchDelete',
|
||||
method: 'DELETE',
|
||||
data,
|
||||
});
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
import { http } from '@/utils/http/axios';
|
||||
|
||||
/**
|
||||
* @description: 缓存名称列表
|
||||
*/
|
||||
export function getCacheNames(params?) {
|
||||
return http.request({
|
||||
url: '/cache/getNames',
|
||||
method: 'GET',
|
||||
params,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 缓存key列表
|
||||
*/
|
||||
export function getCacheKeys(cacheName) {
|
||||
return http.request({
|
||||
url: '/cache/getKeys/'+cacheName,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 缓存value列表
|
||||
*/
|
||||
export function getCacheValue(params?) {
|
||||
return http.request({
|
||||
url: '/cache/getValue',
|
||||
method: 'get',
|
||||
params
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 删除缓存名称
|
||||
*/
|
||||
export function deleteCacheName(cacheName) {
|
||||
return http.request({
|
||||
url: '/cache/deleteCacheName/'+cacheName,
|
||||
method: 'DELETE',
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 删除缓存key
|
||||
*/
|
||||
export function deleteCacheKey(cacheKey) {
|
||||
return http.request({
|
||||
url: '/cache/deleteCacheKey/'+cacheKey,
|
||||
method: 'DELETE',
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 清理全部
|
||||
*/
|
||||
export function deleteCacheAll() {
|
||||
return http.request({
|
||||
url: '/cache/deleteCacheAll',
|
||||
method: 'DELETE',
|
||||
});
|
||||
}
|
@ -0,0 +1,119 @@
|
||||
<template>
|
||||
<a-modal
|
||||
v-model:visible="props.visible"
|
||||
:title="props.positionId ? '编辑' : '新增'"
|
||||
width="500px"
|
||||
@cancel="dialogClose"
|
||||
>
|
||||
<a-form
|
||||
class="ls-form"
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:label-col="{ style: { width: '85px' } }"
|
||||
>
|
||||
<a-form-item
|
||||
label="岗位名称"
|
||||
name="name"
|
||||
:rules="{ required: true, message: '请输入岗位名称', trigger: 'blur' }"
|
||||
>
|
||||
<a-input v-model:value="formData.name" placeholder="请输入岗位名称" allow-clear />
|
||||
</a-form-item>
|
||||
<a-form-item label="岗位状态" name="code">
|
||||
<a-radio-group v-model:value="formData.status" name="status">
|
||||
<a-radio :value="1">正常</a-radio>
|
||||
<a-radio :value="2">停用</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item label="排序" name="sort">
|
||||
<a-input-number v-model:value="formData.sort" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<a-button @click="dialogClose">取消</a-button>
|
||||
<a-button :loading="subLoading" type="primary" @click="submit"> 确定 </a-button>
|
||||
</span>
|
||||
</template>
|
||||
</a-modal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import type { FormInstance } from 'ant-design-vue';
|
||||
import { getPositionDetail, positionAdd, positionUpdate } from '@/api/system/position';
|
||||
import { onMounted, reactive, shallowRef } from 'vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { useLockFn } from '@/utils/useLockFn';
|
||||
|
||||
/**
|
||||
* 定义参数变量
|
||||
*/
|
||||
const emit = defineEmits(['success', 'update:visible']);
|
||||
const formRef = shallowRef<FormInstance>();
|
||||
|
||||
/**
|
||||
* 定义表单参数
|
||||
*/
|
||||
const formData = reactive({
|
||||
id: '',
|
||||
name: '',
|
||||
status: 1,
|
||||
sort: 0,
|
||||
});
|
||||
|
||||
/**
|
||||
* 定义接收的参数
|
||||
*/
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
default: false,
|
||||
},
|
||||
positionId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
default: 0,
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* 执行提交表单
|
||||
*/
|
||||
const handleSubmit = async () => {
|
||||
await formRef.value?.validate();
|
||||
props.positionId ? await positionUpdate(formData) : await positionAdd(formData);
|
||||
message.success('操作成功');
|
||||
emit('update:visible', false);
|
||||
emit('success');
|
||||
};
|
||||
|
||||
/**
|
||||
* 关闭窗体
|
||||
*/
|
||||
const dialogClose = () => {
|
||||
emit('update:visible', false);
|
||||
};
|
||||
|
||||
const { isLock: subLoading, lockFn: submit } = useLockFn(handleSubmit);
|
||||
|
||||
/**
|
||||
* 设置表单数据
|
||||
*/
|
||||
const setFormData = async () => {
|
||||
const data = await getPositionDetail(props.positionId);
|
||||
for (const key in formData) {
|
||||
if (data[key] != null && data[key] != undefined) {
|
||||
//@ts-ignore
|
||||
formData[key] = data[key];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 钩子函数
|
||||
*/
|
||||
onMounted(() => {
|
||||
if (props.positionId) {
|
||||
setFormData();
|
||||
}
|
||||
});
|
||||
</script>
|
@ -0,0 +1,67 @@
|
||||
import { http } from '@/utils/http/axios';
|
||||
|
||||
/**
|
||||
* @description: 参数列表
|
||||
*/
|
||||
export function getParamList(params?) {
|
||||
return http.request({
|
||||
url: '/param/page',
|
||||
method: 'GET',
|
||||
params,
|
||||
});
|
||||
}
|
||||
export function getParamAllList(params?) {
|
||||
return http.request({
|
||||
url: '/param/list',
|
||||
method: 'GET',
|
||||
params,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 根据ID获取详情
|
||||
*/
|
||||
export function getParamDetail(paramId) {
|
||||
return http.request({
|
||||
url: '/param/detail/'+paramId,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 添加参数
|
||||
*/
|
||||
export function paramAdd(data:any) {
|
||||
return http.request({
|
||||
url: '/param/add',
|
||||
method: 'POST',
|
||||
data,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 更新参数
|
||||
*/
|
||||
export function paramUpdate(data:any) {
|
||||
return http.request({
|
||||
url: '/param/update',
|
||||
method: 'PUT',
|
||||
data
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 删除参数
|
||||
*/
|
||||
export function paramDelete(paramId) {
|
||||
return http.request({
|
||||
url: '/param/delete/'+paramId,
|
||||
method: 'DELETE',
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 批量删除参数
|
||||
*/
|
||||
export function paramBatchDelete(data:any) {
|
||||
return http.request({
|
||||
url: '/param/batchDelete',
|
||||
method: 'DELETE',
|
||||
data
|
||||
});
|
||||
}
|
@ -0,0 +1,172 @@
|
||||
<template>
|
||||
<a-modal
|
||||
v-model:visible="props.visible"
|
||||
:title="props.linkId ? '编辑' : '新增'"
|
||||
width="500px"
|
||||
@cancel="dialogClose"
|
||||
>
|
||||
<a-form
|
||||
class="ls-form"
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:label-col="{ style: { width: '85px' } }"
|
||||
>
|
||||
<a-form-item
|
||||
label="名称"
|
||||
name="name"
|
||||
:rules="{ required: true, message: '请输入名称', trigger: 'blur' }"
|
||||
>
|
||||
<a-input
|
||||
class="ls-input"
|
||||
v-model:value="formData.name"
|
||||
placeholder="请输入名称"
|
||||
clearable
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="类型"
|
||||
name="type"
|
||||
:rules="{ required: true, message: '请选择类型', trigger: 'change' }"
|
||||
>
|
||||
<a-select v-model:value="formData.type" placeholder="请选择类型">
|
||||
<a-select-option :value="1">友情链接</a-select-option>
|
||||
<a-select-option :value="2">合作伙伴</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="地址"
|
||||
name="url"
|
||||
:rules="{ required: true, message: '请输入地址', trigger: 'blur' }"
|
||||
>
|
||||
<a-input v-model:value="formData.url" placeholder="请输入地址" />
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="形式"
|
||||
name="form"
|
||||
:rules="{ required: true, message: '请选择形式', trigger: 'change' }"
|
||||
>
|
||||
<a-select v-model:value="formData.form" placeholder="请选择形式">
|
||||
<a-select-option :value="1">文字链接</a-select-option>
|
||||
<a-select-option :value="2">图片链接</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="图片"
|
||||
name="image"
|
||||
:rules="{ required: true, message: '请上传图片', trigger: 'change' }"
|
||||
v-if="formData.form == 2"
|
||||
>
|
||||
<UploadImg
|
||||
@change-file-name="(name) => (formData.coverImgName = name)"
|
||||
:fileType="['image/jpeg', 'image/png', 'image/jpg', 'image/gif']"
|
||||
name="article"
|
||||
:fileSize="200"
|
||||
v-model:image-url="formData.image"
|
||||
>
|
||||
<template #tip>支持扩展名: jpg png jpeg;文件大小不超过200M</template>
|
||||
</UploadImg>
|
||||
</a-form-item>
|
||||
<a-form-item label="排序" name="sort">
|
||||
<a-input-number v-model:value="formData.sort" />
|
||||
</a-form-item>
|
||||
<a-form-item label="状态">
|
||||
<a-radio-group v-model:value="formData.status" name="status">
|
||||
<a-radio :value="1">在用</a-radio>
|
||||
<a-radio :value="2">停用</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<a-button @click="dialogClose">取消</a-button>
|
||||
<a-button :loading="subLoading" type="primary" @click="submit"> 确定 </a-button>
|
||||
</span>
|
||||
</template>
|
||||
</a-modal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import type { FormInstance } from 'ant-design-vue';
|
||||
import { getLinkDetail, linkAdd, linkUpdate } from '@/api/content/link';
|
||||
import { onMounted, reactive, shallowRef } from 'vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { useLockFn } from '@/utils/useLockFn';
|
||||
import UploadImg from '@/components/Upload/Image.vue';
|
||||
|
||||
/**
|
||||
* 定义参数变量
|
||||
*/
|
||||
const emit = defineEmits(['success', 'update:visible']);
|
||||
const formRef = shallowRef<FormInstance>();
|
||||
|
||||
/**
|
||||
* 定义表单参数
|
||||
*/
|
||||
const formData = reactive({
|
||||
id: '',
|
||||
name: '',
|
||||
type: undefined,
|
||||
url: '',
|
||||
form: undefined,
|
||||
image: '',
|
||||
status: 1,
|
||||
sort: 0,
|
||||
});
|
||||
|
||||
/**
|
||||
* 定义接收的参数
|
||||
*/
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
default: false,
|
||||
},
|
||||
linkId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
default: 0,
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* 执行提交
|
||||
*/
|
||||
const handleSubmit = async () => {
|
||||
await formRef.value?.validate();
|
||||
props.linkId ? await linkUpdate(formData) : await linkAdd(formData);
|
||||
message.success('操作成功');
|
||||
emit('update:visible', false);
|
||||
emit('success');
|
||||
};
|
||||
|
||||
/**
|
||||
* 关闭窗体
|
||||
*/
|
||||
const dialogClose = () => {
|
||||
emit('update:visible', false);
|
||||
};
|
||||
|
||||
const { isLock: subLoading, lockFn: submit } = useLockFn(handleSubmit);
|
||||
|
||||
/**
|
||||
* 设置表单数据
|
||||
*/
|
||||
const setFormData = async () => {
|
||||
const data = await getLinkDetail(props.linkId);
|
||||
for (const key in formData) {
|
||||
if (data[key] != null && data[key] != undefined) {
|
||||
//@ts-ignore
|
||||
formData[key] = data[key];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 钩子函数
|
||||
*/
|
||||
onMounted(() => {
|
||||
if (props.linkId) {
|
||||
setFormData();
|
||||
}
|
||||
});
|
||||
</script>
|
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1631453917190" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1531" width="32" height="32" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><style type="text/css"></style></defs><path d="M300.032 558.08V427.52c0-19.968 13.312-35.84 29.696-35.84s29.696 15.872 29.696 35.84V558.08c0 19.968-13.312 35.84-29.696 35.84s-29.696-15.872-29.696-35.84zM480.256 558.08V363.52c0-19.968 13.312-35.84 29.696-35.84 16.384 0 29.696 15.872 29.696 35.84v194.56c0 19.968-13.312 35.84-29.696 35.84-16.384 0-29.696-15.872-29.696-35.84zM660.48 558.08V299.52c0-19.968 13.312-35.84 29.696-35.84s29.696 15.872 29.696 35.84V558.08c0 19.968-13.312 35.84-29.696 35.84s-29.696-15.872-29.696-35.84z" p-id="1532" fill="#2d8cf0"></path><path d="M861.696 781.312H568.32v117.248h146.944c16.384 0 29.184 13.312 29.184 29.184 0 16.384-13.312 29.184-29.184 29.184H362.496c-16.384-1.024-28.672-14.848-27.648-30.72 1.024-14.848 12.8-27.136 27.648-27.648h146.944v-117.248H157.184c-65.024 0-117.248-52.736-117.248-117.248V194.048C39.936 129.024 92.16 76.8 157.184 76.8h704.512c65.024 0 117.248 52.736 117.248 117.248v470.016c0.512 64.512-52.224 117.248-117.248 117.248z m58.88-587.264c0-32.256-26.112-58.88-58.88-58.88H157.184c-32.256 0-58.88 26.112-58.88 58.88v470.016c0 32.256 26.112 58.88 58.88 58.88h704.512c32.256 0 58.88-26.112 58.88-58.88V194.048z" p-id="1533" fill="#2d8cf0"></path></svg>
|
After Width: | Height: | Size: 1.5 KiB |
@ -0,0 +1,241 @@
|
||||
import { http } from '@/utils/http/axios';
|
||||
|
||||
export interface BasicResponseModel<T = any> {
|
||||
code: number;
|
||||
msg: string;
|
||||
data: T;
|
||||
}
|
||||
|
||||
export interface BasicPageParams {
|
||||
pageNo: number;
|
||||
pageSize: number;
|
||||
total: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 获取用户信息
|
||||
*/
|
||||
export function getUserInfo() {
|
||||
return http.request({
|
||||
url: '/index/getUser',
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 用户登录
|
||||
*/
|
||||
export function login(params) {
|
||||
return http.request<BasicResponseModel>(
|
||||
{
|
||||
url: '/login',
|
||||
method: 'POST',
|
||||
params,
|
||||
},
|
||||
{
|
||||
isTransformResponse: false,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一认证登录
|
||||
* @param data 参数
|
||||
* @returns 返回结果
|
||||
*/
|
||||
export function login2(data) {
|
||||
const formData = new FormData();
|
||||
formData.append('username', data.username);
|
||||
formData.append('password', data.password);
|
||||
formData.append('code', data.code);
|
||||
formData.append('grant_type', data.grant_type);
|
||||
return http.request<BasicResponseModel>(
|
||||
{
|
||||
url: '/auth/oauth2/token',
|
||||
method: 'POST',
|
||||
data: formData,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
Authorization: 'Basic YWRtaW46MTIzNDU2',
|
||||
},
|
||||
},
|
||||
{
|
||||
isTransformResponse: false,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 用户登录
|
||||
*/
|
||||
export function getInfoCaptcha() {
|
||||
return http.request({
|
||||
url: '/captcha',
|
||||
method: 'GET',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 用户修改密码
|
||||
*/
|
||||
export function changePassword(data) {
|
||||
return http.request({
|
||||
url: `/index/updatePassword`,
|
||||
method: 'PUT',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 获取短信验证码
|
||||
*/
|
||||
export function sendSms(data) {
|
||||
return http.request({
|
||||
url: `/sms/sendSms`,
|
||||
method: 'POST',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 用户登出
|
||||
*/
|
||||
export function logout(params) {
|
||||
return http.request({
|
||||
url: '/login/logout',
|
||||
method: 'POST',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 获取用户列表
|
||||
*/
|
||||
export function getUserList(params) {
|
||||
return http.request({
|
||||
url: '/user/page',
|
||||
method: 'get',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 根据ID获取详情
|
||||
*/
|
||||
export function getUserDetail(userId) {
|
||||
return http.request({
|
||||
url: '/user/detail/' + userId,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 添加用户
|
||||
*/
|
||||
export function userAdd(data: any) {
|
||||
return http.request({
|
||||
url: '/user/add',
|
||||
method: 'POST',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 更新用户
|
||||
*/
|
||||
export function userUpdate(data: any) {
|
||||
return http.request({
|
||||
url: '/user/update',
|
||||
method: 'PUT',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 重置密码
|
||||
*/
|
||||
export function resetPwd(data: any) {
|
||||
return http.request({
|
||||
url: '/user/resetPwd',
|
||||
method: 'PUT',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 删除用户
|
||||
*/
|
||||
export function userDelete(userId) {
|
||||
return http.request({
|
||||
url: '/user/delete/' + userId,
|
||||
method: 'DELETE',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 批量删除用户
|
||||
*/
|
||||
export function userBatchDelete(data: any) {
|
||||
return http.request({
|
||||
url: '/user/batchDelete',
|
||||
method: 'DELETE',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 导入用户
|
||||
*/
|
||||
export function userImport(data) {
|
||||
return http.request({
|
||||
url: '/user/import',
|
||||
method: 'POST',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 导出用户
|
||||
*/
|
||||
export function userExport() {
|
||||
return http.request(
|
||||
{
|
||||
url: '/user/export',
|
||||
method: 'GET',
|
||||
responseType: 'blob',
|
||||
},
|
||||
{
|
||||
isTransformResponse: false,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 下载模板
|
||||
*/
|
||||
export function getTemplateByCode(code: any) {
|
||||
return http.request({
|
||||
url: '/file/template/getTemplateByCode/' + code,
|
||||
method: 'GET',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 获取打印地址
|
||||
*/
|
||||
export function getUserDocument(userId: any) {
|
||||
return http.request({
|
||||
url: '/user/document/' + userId,
|
||||
method: 'GET',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 城市列表
|
||||
*/
|
||||
export function getCityByList(pid: any) {
|
||||
return http.request({
|
||||
url: '/city/getCityList/' + pid,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
import { FormSchema } from '@/components/Form/index';
|
||||
export const schemas: FormSchema[] = [
|
||||
{
|
||||
name: 'title',
|
||||
component: 'Input',
|
||||
label: '模板名称',
|
||||
componentProps: {
|
||||
placeholder: '请输入模板名称',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'type',
|
||||
component: 'Select',
|
||||
label: '邮件类型',
|
||||
componentProps: {
|
||||
placeholder: '请选择邮件类型',
|
||||
clearable: true,
|
||||
options: [
|
||||
{
|
||||
label: '普通邮件',
|
||||
value: '1',
|
||||
},
|
||||
{
|
||||
label: '图文邮件',
|
||||
value: '2',
|
||||
},
|
||||
{
|
||||
label: '模板文件',
|
||||
value: '3',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
@ -0,0 +1,17 @@
|
||||
import { h } from 'vue';
|
||||
import { ElTag } from 'element-plus';
|
||||
|
||||
export const columns = [
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'id',
|
||||
},
|
||||
{
|
||||
title: '字典名称',
|
||||
dataIndex: 'name',
|
||||
},
|
||||
{
|
||||
title: '字典编码',
|
||||
dataIndex: 'code',
|
||||
},
|
||||
];
|
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1631454216260" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2219" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32"><defs><style type="text/css"></style></defs><path d="M697.652 256c-23.565 0-42.667-19.103-42.667-42.667s19.102-42.666 42.667-42.666h124.651c40.288 0 73.697 31.956 73.697 72.347v85.783c0 23.564-19.103 42.667-42.667 42.667s-42.666-19.103-42.666-42.667V256H697.652z m113.015 597.333V496.565c0-23.564 19.102-42.666 42.666-42.666C876.897 453.899 896 473 896 496.565V866.32c0 40.391-33.41 72.348-73.697 72.348H201.697c-40.288 0-73.697-31.957-73.697-72.348V243.014c0-40.39 33.41-72.347 73.697-72.347h124.727c23.564 0 42.667 19.102 42.667 42.666 0 23.564-19.103 42.667-42.667 42.667h-113.09v597.333h597.333zM368.485 541.418c-23.564 0-42.667-19.102-42.667-42.666 0-23.564 19.103-42.667 42.667-42.667h320c23.564 0 42.667 19.103 42.667 42.667s-19.103 42.666-42.667 42.666h-320z m58.182-370.751c-23.564 0-42.667 19.102-42.667 42.666C384 236.897 403.103 256 426.667 256h170.666C620.897 256 640 236.897 640 213.333s-19.103-42.666-42.667-42.666H426.667z m0-85.334h170.666c70.693 0 128 57.308 128 128 0 70.693-57.307 128-128 128H426.667c-70.693 0-128-57.307-128-128 0-70.692 57.307-128 128-128zM368.485 696.57c-23.564 0-42.667-19.103-42.667-42.667s19.103-42.666 42.667-42.666h320c23.564 0 42.667 19.102 42.667 42.666 0 23.564-19.103 42.667-42.667 42.667h-320z" p-id="2220" fill="#f49776"></path></svg>
|
After Width: | Height: | Size: 1.6 KiB |
@ -0,0 +1,39 @@
|
||||
import type { QRCodeSegment, QRCodeRenderersOptions } from 'qrcode';
|
||||
import { Fn } from '/#/index';
|
||||
|
||||
export type ContentType = string | QRCodeSegment[];
|
||||
|
||||
export type { QRCodeRenderersOptions };
|
||||
|
||||
export type LogoType = {
|
||||
src: string;
|
||||
logoSize: number;
|
||||
borderColor: string;
|
||||
bgColor: string;
|
||||
borderSize: number;
|
||||
crossOrigin: string;
|
||||
borderRadius: number;
|
||||
logoRadius: number;
|
||||
};
|
||||
|
||||
export interface RenderQrCodeParams {
|
||||
canvas: any;
|
||||
content: ContentType;
|
||||
width?: number;
|
||||
options?: QRCodeRenderersOptions;
|
||||
logo?: LogoType | string;
|
||||
image?: HTMLImageElement;
|
||||
downloadName?: string;
|
||||
download?: boolean | Fn;
|
||||
}
|
||||
|
||||
export type ToCanvasFn = (options: RenderQrCodeParams) => Promise<unknown>;
|
||||
|
||||
export interface QrCodeActionType {
|
||||
download: (fileName?: string) => void;
|
||||
}
|
||||
|
||||
export interface QrcodeDoneEventParams {
|
||||
url: string;
|
||||
ctx?: CanvasRenderingContext2D | null;
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 528 KiB |
@ -0,0 +1,165 @@
|
||||
import { upperFirst } from 'lodash-es';
|
||||
|
||||
export interface ViewportOffsetResult {
|
||||
left: number;
|
||||
top: number;
|
||||
right: number;
|
||||
bottom: number;
|
||||
rightIncludeBody: number;
|
||||
bottomIncludeBody: number;
|
||||
}
|
||||
|
||||
export function getBoundingClientRect(element: Element): DOMRect | number {
|
||||
if (!element || !element.getBoundingClientRect) {
|
||||
return 0;
|
||||
}
|
||||
return element.getBoundingClientRect();
|
||||
}
|
||||
|
||||
function trim(string: string) {
|
||||
return (string || '').replace(/^[\s\uFEFF]+|[\s\uFEFF]+$/g, '');
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
export function hasClass(el: Element, cls: string) {
|
||||
if (!el || !cls) return false;
|
||||
if (cls.indexOf(' ') !== -1) throw new Error('className should not contain space.');
|
||||
if (el.classList) {
|
||||
return el.classList.contains(cls);
|
||||
} else {
|
||||
return (' ' + el.className + ' ').indexOf(' ' + cls + ' ') > -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
export function addClass(el: Element, cls: string) {
|
||||
if (!el) return;
|
||||
let curClass = el.className;
|
||||
const classes = (cls || '').split(' ');
|
||||
|
||||
for (let i = 0, j = classes.length; i < j; i++) {
|
||||
const clsName = classes[i];
|
||||
if (!clsName) continue;
|
||||
|
||||
if (el.classList) {
|
||||
el.classList.add(clsName);
|
||||
} else if (!hasClass(el, clsName)) {
|
||||
curClass += ' ' + clsName;
|
||||
}
|
||||
}
|
||||
if (!el.classList) {
|
||||
el.className = curClass;
|
||||
}
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
export function removeClass(el: Element, cls: string) {
|
||||
if (!el || !cls) return;
|
||||
const classes = cls.split(' ');
|
||||
let curClass = ' ' + el.className + ' ';
|
||||
|
||||
for (let i = 0, j = classes.length; i < j; i++) {
|
||||
const clsName = classes[i];
|
||||
if (!clsName) continue;
|
||||
|
||||
if (el.classList) {
|
||||
el.classList.remove(clsName);
|
||||
} else if (hasClass(el, clsName)) {
|
||||
curClass = curClass.replace(' ' + clsName + ' ', ' ');
|
||||
}
|
||||
}
|
||||
if (!el.classList) {
|
||||
el.className = trim(curClass);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get the left and top offset of the current element
|
||||
* left: the distance between the leftmost element and the left side of the document
|
||||
* top: the distance from the top of the element to the top of the document
|
||||
* right: the distance from the far right of the element to the right of the document
|
||||
* bottom: the distance from the bottom of the element to the bottom of the document
|
||||
* rightIncludeBody: the distance between the leftmost element and the right side of the document
|
||||
* bottomIncludeBody: the distance from the bottom of the element to the bottom of the document
|
||||
*
|
||||
* @description:
|
||||
*/
|
||||
export function getViewportOffset(element: Element): ViewportOffsetResult {
|
||||
const doc = document.documentElement;
|
||||
|
||||
const docScrollLeft = doc.scrollLeft;
|
||||
const docScrollTop = doc.scrollTop;
|
||||
const docClientLeft = doc.clientLeft;
|
||||
const docClientTop = doc.clientTop;
|
||||
|
||||
const pageXOffset = window.pageXOffset;
|
||||
const pageYOffset = window.pageYOffset;
|
||||
|
||||
const box = getBoundingClientRect(element);
|
||||
|
||||
const { left: retLeft, top: rectTop, width: rectWidth, height: rectHeight } = box as DOMRect;
|
||||
|
||||
const scrollLeft = (pageXOffset || docScrollLeft) - (docClientLeft || 0);
|
||||
const scrollTop = (pageYOffset || docScrollTop) - (docClientTop || 0);
|
||||
const offsetLeft = retLeft + pageXOffset;
|
||||
const offsetTop = rectTop + pageYOffset;
|
||||
|
||||
const left = offsetLeft - scrollLeft;
|
||||
const top = offsetTop - scrollTop;
|
||||
|
||||
const clientWidth = window.document.documentElement.clientWidth;
|
||||
const clientHeight = window.document.documentElement.clientHeight;
|
||||
return {
|
||||
left: left,
|
||||
top: top,
|
||||
right: clientWidth - rectWidth - left,
|
||||
bottom: clientHeight - rectHeight - top,
|
||||
rightIncludeBody: clientWidth - left,
|
||||
bottomIncludeBody: clientHeight - top,
|
||||
};
|
||||
}
|
||||
|
||||
export function hackCss(attr: string, value: string) {
|
||||
const prefix: string[] = ['webkit', 'Moz', 'ms', 'OT'];
|
||||
|
||||
const styleObj: any = {};
|
||||
prefix.forEach((item) => {
|
||||
styleObj[`${item}${upperFirst(attr)}`] = value;
|
||||
});
|
||||
return {
|
||||
...styleObj,
|
||||
[attr]: value,
|
||||
};
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
export function on(
|
||||
element: Element | HTMLElement | Document | Window,
|
||||
event: string,
|
||||
handler: EventListenerOrEventListenerObject,
|
||||
): void {
|
||||
if (element && event && handler) {
|
||||
element.addEventListener(event, handler, false);
|
||||
}
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
export function off(
|
||||
element: Element | HTMLElement | Document | Window,
|
||||
event: string,
|
||||
handler: Fn,
|
||||
): void {
|
||||
if (element && event && handler) {
|
||||
element.removeEventListener(event, handler, false);
|
||||
}
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
export function once(el: HTMLElement, event: string, fn: EventListener): void {
|
||||
const listener = function (this: any, ...args: unknown[]) {
|
||||
if (fn) {
|
||||
fn.apply(this, args);
|
||||
}
|
||||
off(el, event, listener);
|
||||
};
|
||||
on(el, event, listener);
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
import { http } from '@/utils/http/axios';
|
||||
|
||||
/**
|
||||
* @description: 消息列表
|
||||
*/
|
||||
export function getMessageList(params?) {
|
||||
return http.request({
|
||||
url: '/admin/message/page',
|
||||
method: 'GET',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 已读消息
|
||||
*/
|
||||
export function messageRead(id) {
|
||||
return http.request({
|
||||
url: '/admin/message/read/' + id,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 根据ID获取详情
|
||||
*/
|
||||
export function getMessageDetail(id) {
|
||||
return http.request({
|
||||
url: '/admin/message/detail/' + id,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 删除消息
|
||||
*/
|
||||
export function messageDelete(id) {
|
||||
return http.request({
|
||||
url: '/admin/message/delete/' + id,
|
||||
method: 'DELETE',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 批量删除消息
|
||||
*/
|
||||
export function messageBatchDelete(data: any) {
|
||||
return http.request({
|
||||
url: '/admin/message/batchDelete',
|
||||
method: 'DELETE',
|
||||
data,
|
||||
});
|
||||
}
|
@ -0,0 +1,193 @@
|
||||
<template>
|
||||
<a-modal
|
||||
v-model:visible="props.visible"
|
||||
:title="props.jobId ? '编辑' : '新增'"
|
||||
width="600px"
|
||||
@cancel="dialogClose"
|
||||
>
|
||||
<a-form
|
||||
class="ls-form"
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:label-col="{ style: { width: '110px' } }"
|
||||
>
|
||||
<a-form-item
|
||||
label="任务名称"
|
||||
name="jobName"
|
||||
:rules="{ required: true, message: '请输入任务名称', trigger: 'blur' }"
|
||||
>
|
||||
<a-input
|
||||
class="ls-input"
|
||||
v-model:value="formData.jobName"
|
||||
placeholder="请输入任务名称"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="任务别名"
|
||||
name="jobAlias"
|
||||
:rules="{ required: true, message: '请输入任务别名', trigger: 'blur' }"
|
||||
>
|
||||
<a-input
|
||||
class="ls-input"
|
||||
v-model:value="formData.jobAlias"
|
||||
placeholder="请输入任务别名"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="任务分组"
|
||||
name="jobGroup"
|
||||
:rules="{ required: true, message: '请输入任务分组', trigger: 'blur' }"
|
||||
>
|
||||
<a-select v-model:value="formData.jobGroup" placeholder="请选择任务分组">
|
||||
<a-select-option value="DEFAULT">DEFAULT</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="任务触发器"
|
||||
name="jobTrigger"
|
||||
:rules="{ required: true, message: '请输入任务触发器', trigger: 'blur' }"
|
||||
>
|
||||
<a-input
|
||||
class="ls-input"
|
||||
v-model:value="formData.jobTrigger"
|
||||
placeholder="请输入任务触发器"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="任务状态"
|
||||
name="status"
|
||||
:rules="{ required: true, message: '请选择任务状态', trigger: 'blur' }"
|
||||
>
|
||||
<a-select v-model:value="formData.status" placeholder="请选择任务状态">
|
||||
<a-select-option :value="0">未发布</a-select-option>
|
||||
<a-select-option :value="1">运行中</a-select-option>
|
||||
<a-select-option :value="2">暂停</a-select-option>
|
||||
<a-select-option :value="3">删除</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="正则表达式"
|
||||
name="cronExpression"
|
||||
:rules="{ required: true, message: '请输入正则表达式', trigger: 'blur' }"
|
||||
>
|
||||
<a-input
|
||||
class="ls-input"
|
||||
v-model:value="formData.cronExpression"
|
||||
placeholder="请输入正则表达式"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="执行策略"
|
||||
name="executePolicy"
|
||||
:rules="{ required: true, message: '请选择执行策略', trigger: 'blur' }"
|
||||
>
|
||||
<a-select v-model:value="formData.executePolicy" placeholder="请选择执行策略">
|
||||
<a-select-option :value="1">立即执行</a-select-option>
|
||||
<a-select-option :value="2">执行一次</a-select-option>
|
||||
<a-select-option :value="3">放弃执行</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="是否同步任务" name="isSync" class="flex-1">
|
||||
<a-radio-group v-model:value="formData.isSync" name="isSync">
|
||||
<a-radio :value="1">是</a-radio>
|
||||
<a-radio :value="0">否</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="任务URL"
|
||||
name="url"
|
||||
:rules="{ required: true, message: '请输入任务URL', trigger: 'blur' }"
|
||||
>
|
||||
<a-input
|
||||
class="ls-input"
|
||||
v-model:value="formData.url"
|
||||
placeholder="请输入任务URL"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="任务备注"
|
||||
name="note"
|
||||
:rules="{ required: true, message: '请输入任务备注', trigger: 'blur' }"
|
||||
>
|
||||
<a-input v-model:value="formData.note" type="textarea" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<a-button @click="dialogClose">取消</a-button>
|
||||
<a-button :loading="subLoading" type="primary" @click="submit"> 确定 </a-button>
|
||||
</span>
|
||||
</template>
|
||||
</a-modal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import type { FormInstance } from 'element-plus';
|
||||
import { getJobDetail, jobAdd, jobUpdate } from '@/api/monitor/job';
|
||||
import { onMounted, reactive, shallowRef } from 'vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { useLockFn } from '@/utils/useLockFn';
|
||||
|
||||
const emit = defineEmits(['success', 'update:visible']);
|
||||
const formRef = shallowRef<FormInstance>();
|
||||
const formData = reactive({
|
||||
id: '',
|
||||
jobName: '',
|
||||
jobAlias: '',
|
||||
jobGroup: '',
|
||||
jobTrigger: '',
|
||||
status: '',
|
||||
cronExpression: '',
|
||||
executePolicy: '',
|
||||
isSync: 0,
|
||||
url: '',
|
||||
note: '',
|
||||
});
|
||||
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
default: false,
|
||||
},
|
||||
jobId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
default: 0,
|
||||
},
|
||||
});
|
||||
|
||||
const handleSubmit = async () => {
|
||||
await formRef.value?.validate();
|
||||
props.jobId ? await jobUpdate(formData) : await jobAdd(formData);
|
||||
message.success('操作成功');
|
||||
emit('update:visible', false);
|
||||
emit('success');
|
||||
};
|
||||
|
||||
const dialogClose = () => {
|
||||
emit('update:visible', false);
|
||||
};
|
||||
|
||||
const { isLock: subLoading, lockFn: submit } = useLockFn(handleSubmit);
|
||||
|
||||
const setFormData = async () => {
|
||||
const data = await getJobDetail(props.jobId);
|
||||
for (const key in formData) {
|
||||
if (data[key] != null && data[key] != undefined) {
|
||||
//@ts-ignore
|
||||
formData[key] = data[key];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
if (props.jobId) {
|
||||
setFormData();
|
||||
}
|
||||
});
|
||||
</script>
|
@ -0,0 +1,88 @@
|
||||
import { http } from '@/utils/http/axios';
|
||||
|
||||
/**
|
||||
* @description: 职级列表
|
||||
*/
|
||||
export function getLevelList(params?) {
|
||||
return http.request({
|
||||
url: '/admin/level/page',
|
||||
method: 'GET',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取全部职级列表
|
||||
* @param params 参数
|
||||
* @returns 返回结果
|
||||
*/
|
||||
export function getLevelAllList(params?) {
|
||||
return http.request({
|
||||
url: '/admin/level/list',
|
||||
method: 'GET',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 根据ID获取详情
|
||||
*/
|
||||
export function getLevelDetail(levelId) {
|
||||
return http.request({
|
||||
url: '/admin/level/detail/' + levelId,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 添加职级
|
||||
*/
|
||||
export function levelAdd(data: any) {
|
||||
return http.request({
|
||||
url: '/admin/level/add',
|
||||
method: 'POST',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 更新职级
|
||||
*/
|
||||
export function levelUpdate(data: any) {
|
||||
return http.request({
|
||||
url: '/admin/level/update',
|
||||
method: 'PUT',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 删除职级
|
||||
*/
|
||||
export function levelDelete(levelId) {
|
||||
return http.request({
|
||||
url: '/admin/level/delete/' + levelId,
|
||||
method: 'DELETE',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 批量删除职级
|
||||
*/
|
||||
export function levelBatchDelete(data: any) {
|
||||
return http.request({
|
||||
url: '/admin/level/batchDelete',
|
||||
method: 'DELETE',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 导出职级
|
||||
*/
|
||||
export function levelExport() {
|
||||
return http.request({
|
||||
url: '/admin/level/export',
|
||||
method: 'GET',
|
||||
});
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<a-list item-layout="horizontal" :data-source="listData" class="px-4">
|
||||
<template #renderItem="{ item }">
|
||||
<a-list-item>
|
||||
<a-list-item-meta>
|
||||
<template #title>
|
||||
{{ item.title }}
|
||||
</template>
|
||||
<template #description>
|
||||
{{ item.description }}<a-button type="link" class="ml-4">修改</a-button>
|
||||
</template>
|
||||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
</template>
|
||||
</a-list>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const listData = [
|
||||
{
|
||||
title: '账户密码',
|
||||
description: '绑定手机和邮箱,并设置密码,帐号更安全',
|
||||
},
|
||||
{
|
||||
title: '绑定手机',
|
||||
description: '已绑定手机号:+8618000000001',
|
||||
},
|
||||
{
|
||||
title: '密保问题',
|
||||
description: '未设置密保问题,密保问题可有效保护账户安全',
|
||||
},
|
||||
{
|
||||
title: '个性域名',
|
||||
description: '已绑定域名:https://www.baidu.com',
|
||||
},
|
||||
];
|
||||
</script>
|
@ -0,0 +1,15 @@
|
||||
import { isReactive, isRef } from 'vue';
|
||||
|
||||
function setLoading(loading, val) {
|
||||
if (loading != undefined && isRef(loading)) {
|
||||
loading.value = val;
|
||||
} else if (loading != undefined && isReactive(loading)) {
|
||||
loading.loading = val;
|
||||
}
|
||||
}
|
||||
|
||||
export const useAsync = async (func: Promise<any>, loading: any): Promise<any> => {
|
||||
setLoading(loading, true);
|
||||
|
||||
return await func.finally(() => setLoading(loading, false));
|
||||
};
|
@ -0,0 +1,224 @@
|
||||
<template>
|
||||
<div class="menu-index">
|
||||
<a-card :bordered="false" class="pt-3 mb-3 proCard">
|
||||
<div>
|
||||
<a-button type="primary" @click="handleAdd()" v-perm="['sys:city:add']">
|
||||
<template #icon><PlusOutlined /></template>
|
||||
新增
|
||||
</a-button>
|
||||
</div>
|
||||
</a-card>
|
||||
<a-card :bordered="false" class="pt-3 mb-3 proCard">
|
||||
<a-table
|
||||
border
|
||||
v-loading="loading"
|
||||
ref="tableRef"
|
||||
:columns="columns"
|
||||
:dataSource="lists"
|
||||
@expand="getChild"
|
||||
rowKey="areaCode"
|
||||
>
|
||||
<template #bodyCell="{ column, record, index }">
|
||||
<template v-if="column.key == 'action'">
|
||||
<a-space>
|
||||
<a-button type="primary" @click="handleAdd(record)" v-perm="['sys:city:add']">
|
||||
<template #icon><PlusOutlined /></template>
|
||||
新增</a-button
|
||||
>
|
||||
<a-button type="primary" @click="handleEdit(record)" v-perm="['sys:city:update']">
|
||||
<template #icon><EditOutlined /></template>
|
||||
编辑
|
||||
</a-button>
|
||||
<a-button
|
||||
type="primary"
|
||||
danger
|
||||
@click="handleDelete(record)"
|
||||
v-perm="['sys:city:delete']"
|
||||
>
|
||||
<template #icon><DeleteOutlined /></template>
|
||||
删除
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-card>
|
||||
<editDialog
|
||||
ref="editRef"
|
||||
v-if="editVisible"
|
||||
:cityId="cityId"
|
||||
:parentData="parentData"
|
||||
:itemData="itemData"
|
||||
v-model:visible="editVisible"
|
||||
@success="refreshDataList"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="menu">
|
||||
import { defineAsyncComponent, nextTick, onMounted, ref, shallowRef } from 'vue';
|
||||
import { PlusOutlined, EditOutlined, DeleteOutlined } from '@ant-design/icons-vue';
|
||||
import { cityDelete } from '@/api/data/city';
|
||||
import { getCityByList } from '@/api/system/user';
|
||||
import { columns } from './columns';
|
||||
import { Modal, message } from 'ant-design-vue';
|
||||
|
||||
/**
|
||||
* 导入组件
|
||||
*/
|
||||
const editDialog = defineAsyncComponent(() => import('./edit.vue'));
|
||||
|
||||
/**
|
||||
* 定义参数变量
|
||||
*/
|
||||
const tableRef = ref();
|
||||
const loading = ref(false);
|
||||
const editVisible = ref(false);
|
||||
const cityId = ref(0);
|
||||
const parentData = ref();
|
||||
const itemData = ref({});
|
||||
const lists = ref([]);
|
||||
const maps = ref(new Map());
|
||||
|
||||
/**
|
||||
* 获取行政区划列表
|
||||
* @param code 参数
|
||||
*/
|
||||
const getDataList = async (code: any) => {
|
||||
try {
|
||||
loading.value = true;
|
||||
let res = await getCityByList(code);
|
||||
let data = res.length > 0 ? res : [];
|
||||
data.map((item) => {
|
||||
item.children = [];
|
||||
});
|
||||
lists.value = data;
|
||||
} catch (e) {
|
||||
lists.value = [];
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 刷新数据列表
|
||||
*/
|
||||
const refreshDataList = (code) => {
|
||||
console.log(code);
|
||||
if (code == 0 || Object.keys(code).length === 0) {
|
||||
getDataList(0);
|
||||
} else {
|
||||
console.log(code);
|
||||
getChild(true, code);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 执行添加
|
||||
* @param data 参数
|
||||
*/
|
||||
const handleAdd = async (data: any) => {
|
||||
cityId.value = 0;
|
||||
if (data) {
|
||||
itemData.value = data;
|
||||
let datas = findParentNode(lists.value, data.pid);
|
||||
if (datas) {
|
||||
parentData.value = datas;
|
||||
} else {
|
||||
parentData.value = data;
|
||||
}
|
||||
}
|
||||
await nextTick();
|
||||
editVisible.value = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* 执行编辑
|
||||
* @param data 参数
|
||||
*/
|
||||
const handleEdit = async (data: any) => {
|
||||
cityId.value = data.id;
|
||||
parentData.value = findParentNode(lists.value, data.pid);
|
||||
await nextTick();
|
||||
editVisible.value = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* 执行删除
|
||||
* @param row 参数
|
||||
*/
|
||||
const handleDelete = async (row: any) => {
|
||||
console.log(row);
|
||||
Modal.confirm({
|
||||
title: '提示',
|
||||
content: '确定要删除?',
|
||||
onOk: async () => {
|
||||
loading.value = true;
|
||||
await cityDelete(row.id);
|
||||
message.success('删除成功');
|
||||
refreshDataList({ areaCode: row.parentCode });
|
||||
loading.value = false;
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 表格点击获取子级
|
||||
* @param expanded 是否展开
|
||||
* @param record 记录
|
||||
*/
|
||||
const getChild = async (expanded, record) => {
|
||||
console.log(record);
|
||||
if (expanded) {
|
||||
let res = await getCityByList(record.areaCode);
|
||||
const data = lists.value;
|
||||
const children = res;
|
||||
children.forEach((item) => {
|
||||
if (item.level < 4) {
|
||||
item.children = [];
|
||||
}
|
||||
});
|
||||
const dataSourceMap = (items) => {
|
||||
items.find((item) => {
|
||||
if (item.areaCode === record.areaCode) {
|
||||
item.children = children;
|
||||
return items;
|
||||
}
|
||||
if (item.children && item.children.length > 0) {
|
||||
dataSourceMap(item.children);
|
||||
}
|
||||
});
|
||||
};
|
||||
dataSourceMap(data);
|
||||
lists.value = [...lists.value];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据子节点找到父节点
|
||||
* @param nodes 节点
|
||||
* @param targetId 目标节点ID
|
||||
*/
|
||||
const findParentNode = (nodes, targetId) => {
|
||||
for (let node of nodes) {
|
||||
if (node.id == targetId) {
|
||||
return node;
|
||||
}
|
||||
if (node.children && node.children.length > 0) {
|
||||
const result = findParentNode(node.children, targetId);
|
||||
if (result) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 钩子函数
|
||||
*/
|
||||
onMounted(() => {
|
||||
getDataList(0);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
@ -0,0 +1,109 @@
|
||||
<template>
|
||||
<a-modal v-model:visible="props.visible" :title="props.dictId ? '编辑' : '新增'" width="500px" @cancel="dialogClose">
|
||||
<a-form
|
||||
class="ls-form"
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:label-col="{ style: { width: '80px' }}">
|
||||
<a-form-item
|
||||
label="字典名称"
|
||||
name="name"
|
||||
:rules="{ required: true, message: '请输入字典名称', trigger: 'blur' }"
|
||||
>
|
||||
<a-input
|
||||
v-model:value="formData.name"
|
||||
placeholder="请输入字典名称"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="字典编码"
|
||||
name="code"
|
||||
:rules="{ required: true, message: '请输入字典编码', trigger: 'blur' }"
|
||||
>
|
||||
<a-input
|
||||
v-model:value="formData.code"
|
||||
:disabled="props.dictId"
|
||||
placeholder="请输入字典编码"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="排序" name="sort">
|
||||
<a-input-number v-model:value="formData.sort"/>
|
||||
</a-form-item>
|
||||
<a-form-item label="备注" name="note">
|
||||
<a-input v-model:value="formData.note" type="textarea" placeholder="请输入备注" allow-clear />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<a-button @click="dialogClose">取消</a-button>
|
||||
<a-button :loading="subLoading" type="primary" @click="submit">
|
||||
确定
|
||||
</a-button>
|
||||
</span>
|
||||
</template>
|
||||
</a-modal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import type { FormInstance } from 'ant-design-vue';
|
||||
import {dictAdd,dictUpdate,getDictDetail} from "@/api/data/dictionary";
|
||||
import {onMounted, reactive, shallowRef} from "vue";
|
||||
import { message } from 'ant-design-vue'
|
||||
import {useLockFn} from "@/utils/useLockFn";
|
||||
|
||||
const emit = defineEmits(["success", "update:visible"]);
|
||||
const formRef = shallowRef<FormInstance>();
|
||||
const formData = reactive({
|
||||
id: "",
|
||||
name: "",
|
||||
code: "",
|
||||
sort: 0,
|
||||
note:'',
|
||||
});
|
||||
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
default: false
|
||||
},
|
||||
dictId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
default: 0
|
||||
}
|
||||
});
|
||||
|
||||
const handleSubmit = async () => {
|
||||
await formRef.value?.validate();
|
||||
props.dictId ? await dictUpdate(formData) : await dictAdd(formData);
|
||||
message.success("操作成功");
|
||||
emit("update:visible", false);
|
||||
emit("success");
|
||||
};
|
||||
|
||||
const dialogClose = () => {
|
||||
emit("update:visible", false);
|
||||
};
|
||||
|
||||
const { isLock:subLoading,lockFn: submit } = useLockFn(handleSubmit);
|
||||
|
||||
|
||||
const setFormData = async () => {
|
||||
const data = await getDictDetail(props.dictId);
|
||||
for (const key in formData) {
|
||||
if (data[key] != null && data[key] != undefined) {
|
||||
//@ts-ignore
|
||||
formData[key] = data[key];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
if (props.dictId) {
|
||||
setFormData();
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
@ -0,0 +1,145 @@
|
||||
{
|
||||
"name": "naive-admin-antd",
|
||||
"version": "1.4.1",
|
||||
"author": {
|
||||
"name": "Ahjung",
|
||||
"email": "735878602@qq.com"
|
||||
},
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"bootstrap": "pnpm install",
|
||||
"serve": "pnpm run dev",
|
||||
"dev": "vite",
|
||||
"build": "cross-env NODE_ENV=production vite build && esno ./build/script/postBuild.ts",
|
||||
"build:test": "cross-env vite build --mode test && esno ./build/script/postBuild.ts",
|
||||
"build:no-cache": "pnpm clean:cache && pnpm run build",
|
||||
"report": "cross-env REPORT=true pnpm run build",
|
||||
"type:check": "vue-tsc --noEmit --skipLibCheck",
|
||||
"preview": "pnpm run build && vite preview",
|
||||
"preview:dist": "vite preview",
|
||||
"clean:cache": "rimraf node_modules/.cache/ && rimraf node_modules/.vite",
|
||||
"clean:lib": "rimraf node_modules",
|
||||
"lint:eslint": "eslint \"{src,mock}/**/*.{vue,ts,tsx}\" --fix",
|
||||
"lint:prettier": "prettier --write --loglevel warn \"src/**/*.{js,json,tsx,css,less,scss,vue,html,md}\"",
|
||||
"lint:stylelint": "stylelint --fix \"**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/",
|
||||
"lint:lint-staged": "lint-staged -c ./.husky/lintstagedrc.js",
|
||||
"lint:pretty": "pretty-quick --staged",
|
||||
"test prod gzip": "http-server dist --cors --gzip -c-1",
|
||||
"reinstall": "rimraf pnpm-lock.yaml && rimraf package.lock.json && rimraf node_modules && pnpm run bootstrap",
|
||||
"build typecheck": "vuedx-typecheck . && vite build",
|
||||
"deploy": "gh-pages -d dist"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ant-design-vue/pro-layout": "^3.2.5",
|
||||
"@ant-design/icons-vue": "^6.1.0",
|
||||
"@tinymce/tinymce-vue": "^5.1.1",
|
||||
"tinymce": "5.10.3",
|
||||
"@vueup/vue-quill": "1.0.0-beta.8",
|
||||
"@vueuse/core": "^8.9.4",
|
||||
"ant-design-vue": "3.2.20",
|
||||
"axios": "^0.27.2",
|
||||
"cropperjs": "^1.6.2",
|
||||
"dayjs": "^1.11.11",
|
||||
"echarts": "^5.5.1",
|
||||
"lodash-es": "^4.17.21",
|
||||
"mockjs": "^1.1.0",
|
||||
"moment": "^2.30.1",
|
||||
"nprogress": "^0.2.0",
|
||||
"perfect-scrollbar": "^1.5.5",
|
||||
"pinia": "^2.1.7",
|
||||
"print-js": "^1.6.0",
|
||||
"qrcode": "^1.5.3",
|
||||
"qs": "^6.12.2",
|
||||
"vue": "3.3.4",
|
||||
"vue-router": "^4.4.0",
|
||||
"vue-types": "^4.2.1",
|
||||
"vue-cropper": "0.5.8",
|
||||
"vuedraggable": "^4.1.0",
|
||||
"xlsx": "^0.18.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^17.8.1",
|
||||
"@commitlint/config-conventional": "^17.8.1",
|
||||
"@types/element-resize-detector": "^1.1.6",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/node": "^17.0.45",
|
||||
"@types/qrcode": "^1.5.5",
|
||||
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
||||
"@typescript-eslint/parser": "^5.62.0",
|
||||
"@vitejs/plugin-vue": "^2.3.4",
|
||||
"@vitejs/plugin-vue-jsx": "^1.3.10",
|
||||
"@vue/compiler-sfc": "^3.4.31",
|
||||
"@zougt/some-loader-utils": "^1.4.3",
|
||||
"@zougt/vite-plugin-theme-preprocessor": "^1.4.8",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"color": "^4.2.3",
|
||||
"colors": "^1.4.0",
|
||||
"commitizen": "^4.3.0",
|
||||
"core-js": "^3.37.1",
|
||||
"cross-env": "^7.0.3",
|
||||
"dotenv": "^16.4.5",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-prettier": "^8.10.0",
|
||||
"eslint-define-config": "^1.24.1",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-vue": "^9.27.0",
|
||||
"esno": "^0.16.3",
|
||||
"fs-extra": "^10.1.0",
|
||||
"gh-pages": "^4.0.0",
|
||||
"husky": "^8.0.3",
|
||||
"install": "^0.13.0",
|
||||
"less": "^4.2.0",
|
||||
"less-loader": "^11.1.4",
|
||||
"lint-staged": "^12.5.0",
|
||||
"pnpm": "^7.33.7",
|
||||
"postcss": "^8.4.39",
|
||||
"prettier": "^2.8.8",
|
||||
"pretty-quick": "^3.3.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"rollup-plugin-visualizer": "^5.12.0",
|
||||
"stylelint": "^14.16.1",
|
||||
"stylelint-config-prettier": "^9.0.5",
|
||||
"stylelint-config-standard": "^25.0.0",
|
||||
"stylelint-order": "^5.0.0",
|
||||
"stylelint-scss": "^4.7.0",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^4.9.5",
|
||||
"unplugin-vue-components": "^0.19.9",
|
||||
"vite": "^5.3.3",
|
||||
"vite-plugin-compression": "^0.5.1",
|
||||
"vite-plugin-html": "^3.2.2",
|
||||
"vite-plugin-mock": "^2.9.8",
|
||||
"vite-plugin-windicss": "^1.9.3",
|
||||
"vue-eslint-parser": "^9.4.3",
|
||||
"vue-tsc": "^0.34.17"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{vue,js,ts,tsx}": "eslint --fix"
|
||||
},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
"path": "./node_modules/cz-customizable"
|
||||
}
|
||||
},
|
||||
"keywords": [
|
||||
"vue",
|
||||
"antd-admin",
|
||||
"antd-admin-pro",
|
||||
"vue3",
|
||||
"ts",
|
||||
"tsx",
|
||||
"admin",
|
||||
"typescript"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 16.9.0"
|
||||
},
|
||||
"pnpm": {
|
||||
"peerDependencyRules": {
|
||||
"ignoreMissing": [
|
||||
"rollup",
|
||||
"webpack"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
import { http } from '@/utils/http/axios';
|
||||
|
||||
/**
|
||||
* @description: 文件日志列表
|
||||
*/
|
||||
export function getFileLogList(params?) {
|
||||
return http.request({
|
||||
url: '/file/log/page',
|
||||
method: 'GET',
|
||||
params,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 根据ID获取详情
|
||||
*/
|
||||
export function getFileLogDetail(id) {
|
||||
return http.request({
|
||||
url: '/file/log/detail/' + id,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 删除文件日志
|
||||
*/
|
||||
export function fileLogDelete(id) {
|
||||
return http.request({
|
||||
url: '/file/log/delete/' + id,
|
||||
method: 'DELETE',
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 批量删除文件日志
|
||||
*/
|
||||
export function fileLogBatchDelete(data: any) {
|
||||
return http.request({
|
||||
url: '/file/log/batchDelete',
|
||||
method: 'DELETE',
|
||||
data,
|
||||
});
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,23 @@
|
||||
import { http } from '@/utils/http/axios';
|
||||
|
||||
/**
|
||||
* @description: 获取字典列表
|
||||
*/
|
||||
export function getDictionary(params?) {
|
||||
return http.request({
|
||||
url: '/dictionary/list',
|
||||
method: 'get',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 获取字典详情
|
||||
*/
|
||||
export function getDictionaryInfo(params) {
|
||||
return http.request({
|
||||
url: '/dictionary/info',
|
||||
method: 'get',
|
||||
params,
|
||||
});
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
import { http } from '@/utils/http/axios';
|
||||
|
||||
/**
|
||||
* @description: 操作日志列表
|
||||
*/
|
||||
export function getOperLogList(params?) {
|
||||
return http.request({
|
||||
url: '/oper/log/page',
|
||||
method: 'GET',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 根据ID获取详情
|
||||
*/
|
||||
export function getOperLogDetail(id) {
|
||||
return http.request({
|
||||
url: '/oper/log/detail/' + id,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 删除操作日志
|
||||
*/
|
||||
export function operLogDelete(id) {
|
||||
return http.request({
|
||||
url: '/oper/log/delete/' + id,
|
||||
method: 'DELETE',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 批量删除操作日志
|
||||
*/
|
||||
export function operLogBatchDelete(data: any) {
|
||||
return http.request({
|
||||
url: '/oper/log/batchDelete',
|
||||
method: 'DELETE',
|
||||
data,
|
||||
});
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
import { App } from 'vue';
|
||||
|
||||
/**
|
||||
* 注册全局方法 待完善
|
||||
* @param app
|
||||
*/
|
||||
export function setupGlobalMethods(app: App) {}
|
@ -0,0 +1,67 @@
|
||||
import { http } from '@/utils/http/axios';
|
||||
|
||||
/**
|
||||
* @description: 岗位列表
|
||||
*/
|
||||
export function getPositionList(params?) {
|
||||
return http.request({
|
||||
url: '/position/page',
|
||||
method: 'GET',
|
||||
params,
|
||||
});
|
||||
}
|
||||
export function getPositionAllList(params?) {
|
||||
return http.request({
|
||||
url: '/position/list',
|
||||
method: 'GET',
|
||||
params,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 根据ID获取详情
|
||||
*/
|
||||
export function getPositionDetail(positionId) {
|
||||
return http.request({
|
||||
url: '/position/detail/'+positionId,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 添加岗位
|
||||
*/
|
||||
export function positionAdd(data:any) {
|
||||
return http.request({
|
||||
url: '/position/add',
|
||||
method: 'POST',
|
||||
data,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 更新岗位
|
||||
*/
|
||||
export function positionUpdate(data:any) {
|
||||
return http.request({
|
||||
url: '/position/update',
|
||||
method: 'PUT',
|
||||
data
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 删除岗位
|
||||
*/
|
||||
export function positionDelete(positionId) {
|
||||
return http.request({
|
||||
url: '/position/delete/'+positionId,
|
||||
method: 'DELETE',
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 批量删除岗位
|
||||
*/
|
||||
export function positionBatchDelete(data:any) {
|
||||
return http.request({
|
||||
url: '/position/batchDelete',
|
||||
method: 'DELETE',
|
||||
data
|
||||
});
|
||||
}
|
@ -0,0 +1,253 @@
|
||||
<template>
|
||||
<PageWrapper>
|
||||
<a-card :bordered="false" class="pt-3 mb-3 proCard">
|
||||
<BasicForm @register="register" @submit="handleSubmit" @reset="handleReset">
|
||||
<template #statusSlot="{ model, field }">
|
||||
<a-input v-model="model[field]" />
|
||||
</template>
|
||||
</BasicForm>
|
||||
</a-card>
|
||||
<a-card :bordered="false" class="proCard">
|
||||
<BasicTable
|
||||
:columns="columns"
|
||||
:request="loadDataTable"
|
||||
:row-key="(row) => row.id"
|
||||
ref="tableRef"
|
||||
scroll-x="1200"
|
||||
:row-selection="{ onChange: onSelectionChange }"
|
||||
virtual-scroll
|
||||
>
|
||||
<template #tableTitle>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="addUser" v-perm="['sys:user:add']">
|
||||
<template #icon>
|
||||
<PlusOutlined />
|
||||
</template>
|
||||
新建
|
||||
</a-button>
|
||||
|
||||
<a-button
|
||||
type="danger"
|
||||
@click="handleDelete()"
|
||||
:disabled="!selectionData.length"
|
||||
v-perm="['sys:user:batchDelete']"
|
||||
>
|
||||
<template #icon>
|
||||
<DeleteOutlined />
|
||||
</template>
|
||||
删除
|
||||
</a-button>
|
||||
<a-button type="primary" @click="importVisible = true" v-perm="['sys:user:import']">
|
||||
<template #icon>
|
||||
<UploadOutlined />
|
||||
</template>
|
||||
导入
|
||||
</a-button>
|
||||
<!-- <a-upload
|
||||
ref="upload"
|
||||
action="/api/user/import"
|
||||
:headers="uploadHeaders"
|
||||
:on-error="onError"
|
||||
:on-success="onSuccess"
|
||||
:before-upload="beforeUpload"
|
||||
:show-file-list="false"
|
||||
:limit="1"
|
||||
v-perm="['sys:user:import']"
|
||||
>
|
||||
<a-button type="primary">
|
||||
<template #icon>
|
||||
<a-icon class="a-input__icon">
|
||||
<Upload />
|
||||
</a-icon>
|
||||
</template>
|
||||
导入
|
||||
</a-button>
|
||||
</a-upload> -->
|
||||
<a-button
|
||||
type="primary"
|
||||
@click="handleExport"
|
||||
:loading="exportLoading"
|
||||
:disabled="exportLoading"
|
||||
v-perm="['sys:user:export']"
|
||||
>
|
||||
<template #icon>
|
||||
<DownloadOutlined />
|
||||
</template>
|
||||
导出
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'action'">
|
||||
<a-space>
|
||||
<a-button type="primary" @click="handleEdit(record.id)" v-perm="['sys:user:update']">
|
||||
<template #icon><EditOutlined /></template>
|
||||
编辑
|
||||
</a-button>
|
||||
<a-button
|
||||
type="primary"
|
||||
danger
|
||||
@click="handleDelete(record.id)"
|
||||
v-perm="['sys:user:delete']"
|
||||
>
|
||||
<template #icon><DeleteOutlined /></template>
|
||||
删除
|
||||
</a-button>
|
||||
<a-button
|
||||
type="primary"
|
||||
v-if="record.type !== 1"
|
||||
@click="handleResetPassWord(record.id)"
|
||||
v-perm="['sys:user:resetPwd']"
|
||||
>
|
||||
<template #icon><PlusOutlined /></template>
|
||||
重置密码</a-button
|
||||
>
|
||||
<a-button type="primary" @click="handlePrint(record.id)">
|
||||
<template #icon><PrinterOutlined /></template>
|
||||
打印</a-button
|
||||
>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</a-card>
|
||||
<editDialog
|
||||
v-if="editVisible"
|
||||
:userId="userId"
|
||||
v-model:visible="editVisible"
|
||||
@success="reloadTable('noRefresh')"
|
||||
/>
|
||||
<userUpload v-if="importVisible" v-model:visible="importVisible" @success="reloadTable()" />
|
||||
</PageWrapper>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { nextTick, reactive, ref, defineAsyncComponent } from 'vue';
|
||||
import {
|
||||
PlusOutlined,
|
||||
EditOutlined,
|
||||
DeleteOutlined,
|
||||
DownloadOutlined,
|
||||
UploadOutlined,
|
||||
PrinterOutlined,
|
||||
} from '@ant-design/icons-vue';
|
||||
import { useForm } from '@/components/Form/index';
|
||||
import {
|
||||
getUserList,
|
||||
userDelete,
|
||||
userBatchDelete,
|
||||
userExport,
|
||||
resetPwd,
|
||||
getUserDocument,
|
||||
} from '@/api/system/user';
|
||||
import { columns } from './columns';
|
||||
import { schemas } from './querySchemas';
|
||||
import { downloadByData } from '@/utils/file/download';
|
||||
import { Modal, message } from 'ant-design-vue';
|
||||
import printJS from 'print-js';
|
||||
const userId = ref(0);
|
||||
const tableRef = ref();
|
||||
const editVisible = ref(false);
|
||||
const importVisible = ref(false);
|
||||
const selectionData = ref([]);
|
||||
const exportLoading = ref(false);
|
||||
const editDialog = defineAsyncComponent(() => import('./edit.vue'));
|
||||
const userUpload = defineAsyncComponent(() => import('./userUpload.vue'));
|
||||
|
||||
const formParams = reactive({
|
||||
realname: '',
|
||||
role: '',
|
||||
status: '',
|
||||
});
|
||||
|
||||
const loadDataTable = async (res) => {
|
||||
const result = await getUserList({ ...formParams, ...res });
|
||||
return result;
|
||||
};
|
||||
|
||||
function reloadTable(noRefresh = '') {
|
||||
tableRef.value.reload(noRefresh ? {} : { pageNo: 1 });
|
||||
}
|
||||
|
||||
async function handleEdit(id) {
|
||||
userId.value = id;
|
||||
await nextTick();
|
||||
editVisible.value = true;
|
||||
}
|
||||
async function handleResetPassWord(id) {
|
||||
Modal.confirm({
|
||||
title: '提示',
|
||||
content: '确定重置密码?',
|
||||
onOk: async () => {
|
||||
await resetPwd({ userId: id });
|
||||
message.success('重置成功');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function handleDelete(id) {
|
||||
Modal.confirm({
|
||||
title: '提示',
|
||||
content: '确定要删除?',
|
||||
onOk: async () => {
|
||||
id ? await userDelete(id) : await userBatchDelete(selectionData.value);
|
||||
message.success('删除成功');
|
||||
reloadTable();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function handleSubmit(values) {
|
||||
for (const key in formParams) {
|
||||
formParams[key] = '';
|
||||
}
|
||||
for (const key in values) {
|
||||
formParams[key] = values[key];
|
||||
}
|
||||
reloadTable();
|
||||
}
|
||||
|
||||
function handleReset() {
|
||||
for (const key in formParams) {
|
||||
formParams[key] = '';
|
||||
}
|
||||
reloadTable();
|
||||
}
|
||||
function onSelectionChange(value) {
|
||||
selectionData.value = value;
|
||||
}
|
||||
const [register, {}] = useForm({
|
||||
rowProps: { gutter: [16, 0] },
|
||||
colProps: {
|
||||
xs: 24,
|
||||
sm: 24,
|
||||
md: 12,
|
||||
lg: 8,
|
||||
xl: 6,
|
||||
},
|
||||
labelCol: { span: 6, offset: 0 },
|
||||
schemas,
|
||||
});
|
||||
//添加
|
||||
const addUser = async () => {
|
||||
userId.value = 0;
|
||||
await nextTick();
|
||||
editVisible.value = true;
|
||||
};
|
||||
//导出
|
||||
const handleExport = async () => {
|
||||
exportLoading.value = true;
|
||||
const data = await userExport();
|
||||
downloadByData(data, '用户信息.xlsx');
|
||||
exportLoading.value = false;
|
||||
message.success('导出成功');
|
||||
};
|
||||
const handlePrint = async (id) => {
|
||||
const res = await getUserDocument(id);
|
||||
printJS({
|
||||
printable: res.fileUrl,
|
||||
type: 'pdf',
|
||||
showModal: true,
|
||||
});
|
||||
};
|
||||
</script>
|
@ -0,0 +1,19 @@
|
||||
import { h } from 'vue';
|
||||
|
||||
export const cacheNameColumns = [
|
||||
{
|
||||
title: '缓存名称',
|
||||
dataIndex: 'cacheName',
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
dataIndex: 'message',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
fixed:'right',
|
||||
dataIndex: 'action',
|
||||
key: 'action',
|
||||
width: 100,
|
||||
},
|
||||
];
|
@ -0,0 +1,241 @@
|
||||
<template>
|
||||
<PageWrapper>
|
||||
<a-row :gutter="10" class="mt-3">
|
||||
<a-col :xs="24" :sm="24" :md="6" :lg="6" :xl="6">
|
||||
<a-card shadow="hover" class="border-0">
|
||||
<template #title>
|
||||
<a-row>
|
||||
<a-col :span="10">
|
||||
<a-input
|
||||
type="text"
|
||||
v-model:value="params.name"
|
||||
placeholder="请输入字典名称"
|
||||
allow-clear
|
||||
/>
|
||||
</a-col>
|
||||
<a-col :span="14" style="text-align: right">
|
||||
<a-button
|
||||
type="primary"
|
||||
@click="
|
||||
pager.page = 1;
|
||||
loadDataTable();
|
||||
"
|
||||
>
|
||||
<template #icon> <SearchOutlined /> </template>查询
|
||||
</a-button>
|
||||
<a-button
|
||||
type="primary"
|
||||
@click="dictRefresh"
|
||||
style="margin-left: 8px"
|
||||
v-perm="['sys:dict:cache']"
|
||||
>
|
||||
<template #icon> <RedoOutlined /> </template>刷新缓存</a-button
|
||||
>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<div style="margin-top: 15px">
|
||||
<a-space>
|
||||
<a-button type="primary" @click="handleAdd" v-perm="['sys:dict:add']">
|
||||
<template #icon>
|
||||
<PlusOutlined />
|
||||
</template>
|
||||
新建
|
||||
</a-button>
|
||||
<a-button type="warning" @click="handleEdit" v-perm="['sys:dict:edit']">
|
||||
<template #icon> <EditOutlined /> </template>编辑
|
||||
</a-button>
|
||||
<a-button type="danger" @click="handleDelete()" v-perm="['sys:dict:delete']">
|
||||
<template #icon> <DeleteOutlined /> </template>删除
|
||||
</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
</template>
|
||||
<div :style="{ height: fwbHeight + 'px' }" class="dict-list-box">
|
||||
<div
|
||||
v-for="(item, index) in dictDataList"
|
||||
:key="index"
|
||||
@click="onCheckedRow(item)"
|
||||
class="dict-item"
|
||||
:class="item.id == dictId ? 'active' : ''"
|
||||
>
|
||||
<span class="t1"
|
||||
>{{ item.name }}<span class="t2">({{ item.code }})</span></span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<pagination
|
||||
style="justify-content: flex-end"
|
||||
class="mt-10 flex"
|
||||
@change="loadDataTable"
|
||||
v-model="pager"
|
||||
/>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :xs="24" :sm="24" :md="18" :lg="18" :xl="18">
|
||||
<a-card shadow="hover" class="mb-4 border-0 proCard">
|
||||
<dictItem :dictId="dictId" v-if="dictItemShow" />
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<editDialog
|
||||
v-if="editVisible"
|
||||
:dictId="dictId"
|
||||
v-model:visible="editVisible"
|
||||
@success="loadDataTable()"
|
||||
/>
|
||||
</PageWrapper>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, nextTick, defineAsyncComponent, onMounted } from 'vue';
|
||||
import { SearchOutlined } from '@ant-design/icons-vue';
|
||||
import { getDictList, refreshCache, dictDelete } from '@/api/data/dictionary';
|
||||
import { PlusOutlined, EditOutlined, DeleteOutlined, RedoOutlined } from '@ant-design/icons-vue';
|
||||
import dictItem from './dictItem.vue';
|
||||
import { Modal, message } from 'ant-design-vue';
|
||||
|
||||
/**
|
||||
* 导入组件
|
||||
*/
|
||||
const editDialog = defineAsyncComponent(() => import('./edit.vue'));
|
||||
|
||||
/**
|
||||
* 定义参数变量
|
||||
*/
|
||||
const dictId = ref(0);
|
||||
const dictItemShow = ref(false);
|
||||
const editVisible = ref(false);
|
||||
|
||||
/**
|
||||
* 定义查询参数
|
||||
*/
|
||||
const params = ref({
|
||||
name: '',
|
||||
});
|
||||
const dictDataList = ref([]);
|
||||
|
||||
/**
|
||||
* 定义分页参数
|
||||
*/
|
||||
const pager = ref({
|
||||
page: 1,
|
||||
size: 20,
|
||||
count: dictDataList.value.length,
|
||||
});
|
||||
const fwbHeight = document.body.clientHeight - 370;
|
||||
|
||||
/**
|
||||
* 添加字典
|
||||
*/
|
||||
const handleAdd = async () => {
|
||||
await nextTick();
|
||||
editVisible.value = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* 刷新缓存
|
||||
*/
|
||||
async function dictRefresh() {
|
||||
await refreshCache();
|
||||
message.success('刷新成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行编辑
|
||||
*/
|
||||
const handleEdit = () => {
|
||||
editVisible.value = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* 数据行选中事件
|
||||
* @param row 参数
|
||||
*/
|
||||
function onCheckedRow(row) {
|
||||
dictId.value = row.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载数据列表
|
||||
*/
|
||||
const loadDataTable = async () => {
|
||||
const result = await getDictList({
|
||||
...params.value,
|
||||
pageNo: pager.value.page,
|
||||
pageSize: pager.value.size,
|
||||
});
|
||||
dictId.value = result?.records[0]?.id;
|
||||
dictItemShow.value = true;
|
||||
dictDataList.value = result.records;
|
||||
pager.value.count = result.total;
|
||||
};
|
||||
|
||||
/**
|
||||
* 执行删除
|
||||
*/
|
||||
async function handleDelete() {
|
||||
Modal.confirm({
|
||||
title: '提示',
|
||||
content: '确定要删除?',
|
||||
onOk: async () => {
|
||||
dictDelete(dictId.value);
|
||||
message.success('删除成功');
|
||||
pager.value.page = 1;
|
||||
loadDataTable();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 钩子函数
|
||||
*/
|
||||
onMounted(() => {
|
||||
loadDataTable();
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.dict-list-box {
|
||||
overflow: auto;
|
||||
.dict-item {
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
padding: 0 6px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.t1 {
|
||||
font-size: 14px;
|
||||
display: block;
|
||||
font-weight: 700;
|
||||
.t2 {
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: #e8f1ff;
|
||||
border-radius: 3px;
|
||||
|
||||
.t1 {
|
||||
color: #1677ff;
|
||||
}
|
||||
.t2 {
|
||||
color: rgb(22, 119, 255, 0.8);
|
||||
}
|
||||
}
|
||||
|
||||
.ant-badge__content.is-fixed {
|
||||
top: 20px;
|
||||
right: calc(-10px + var(--ant-badge-size) / 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,19 @@
|
||||
const allModules = import.meta.globEager('./*/index.ts');
|
||||
const modules = {} as any;
|
||||
Object.keys(allModules).forEach((path) => {
|
||||
const fileName = path.split('/')[1];
|
||||
modules[fileName] = allModules[path][fileName] || allModules[path].default || allModules[path];
|
||||
});
|
||||
|
||||
// export default modules
|
||||
import asyncRoute from './async-route';
|
||||
import user from './user';
|
||||
import tabsView from './tabs-view';
|
||||
import lockscreen from './lockscreen';
|
||||
|
||||
export default {
|
||||
asyncRoute,
|
||||
user,
|
||||
tabsView,
|
||||
lockscreen,
|
||||
};
|
@ -0,0 +1,60 @@
|
||||
import { http } from '@/utils/http/axios';
|
||||
|
||||
/**
|
||||
* @description: 列表
|
||||
*/
|
||||
export function getAdList(params?) {
|
||||
return http.request({
|
||||
url: '/ad/page',
|
||||
method: 'GET',
|
||||
params,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 根据ID获取详情
|
||||
*/
|
||||
export function getAdDetail(adId) {
|
||||
return http.request({
|
||||
url: '/ad/detail/' + adId,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 添加
|
||||
*/
|
||||
export function adAdd(data: any) {
|
||||
return http.request({
|
||||
url: '/ad/add',
|
||||
method: 'POST',
|
||||
data,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 更新
|
||||
*/
|
||||
export function adUpdate(data: any) {
|
||||
return http.request({
|
||||
url: '/ad/update',
|
||||
method: 'PUT',
|
||||
data,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 删除
|
||||
*/
|
||||
export function adDelete(adId) {
|
||||
return http.request({
|
||||
url: '/ad/delete/' + adId,
|
||||
method: 'DELETE',
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 批量删除
|
||||
*/
|
||||
export function adBatchDelete(data: any) {
|
||||
return http.request({
|
||||
url: '/ad/batchDelete',
|
||||
method: 'DELETE',
|
||||
data,
|
||||
});
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
import { http } from '@/utils/http/axios';
|
||||
|
||||
/**
|
||||
* @description: 消息列表
|
||||
*/
|
||||
export function getMessageProfile(params?) {
|
||||
return http.request({
|
||||
url: '/message/profile',
|
||||
method: 'GET',
|
||||
params,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 设置消息已读
|
||||
*/
|
||||
export function setRead(data?) {
|
||||
return http.request({
|
||||
url: '/message/setRead',
|
||||
method: 'POST',
|
||||
data,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 根据ID获取详情
|
||||
*/
|
||||
export function getMessageDetail(id) {
|
||||
return http.request({
|
||||
url: '/message/detail/' + id,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 删除消息
|
||||
*/
|
||||
export function messageDelete(id) {
|
||||
return http.request({
|
||||
url: '/message/delete/' + id,
|
||||
method: 'DELETE',
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 批量删除消息
|
||||
*/
|
||||
export function messageBatchDelete(data: any) {
|
||||
return http.request({
|
||||
url: '/message/batchDelete',
|
||||
method: 'DELETE',
|
||||
data,
|
||||
});
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
import { generatorMenu } from '@/utils/index';
|
||||
import { useAsyncRouteStore } from '@/store/modules/asyncRoute';
|
||||
import { Menu } from '../types';
|
||||
|
||||
export const getMenus = async (): Promise<Menu[]> => {
|
||||
const asyncRouteStore = useAsyncRouteStore();
|
||||
return generatorMenu(asyncRouteStore.getMenus);
|
||||
};
|
@ -0,0 +1,86 @@
|
||||
import { http } from '@/utils/http/axios';
|
||||
|
||||
/**
|
||||
* @description: 角色列表
|
||||
*/
|
||||
export function getRoleList(params?) {
|
||||
return http.request({
|
||||
url: '/role/page',
|
||||
method: 'GET',
|
||||
params,
|
||||
});
|
||||
}
|
||||
export function getRoleAllList(params?) {
|
||||
return http.request({
|
||||
url: '/role/list',
|
||||
method: 'GET',
|
||||
params,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 根据ID获取详情
|
||||
*/
|
||||
export function getRoleDetail(roleId) {
|
||||
return http.request({
|
||||
url: '/role/detail/'+roleId,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 添加角色
|
||||
*/
|
||||
export function roleAdd(data:any) {
|
||||
return http.request({
|
||||
url: '/role/add',
|
||||
method: 'POST',
|
||||
data,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 更新角色
|
||||
*/
|
||||
export function roleUpdate(data:any) {
|
||||
return http.request({
|
||||
url: '/role/update',
|
||||
method: 'PUT',
|
||||
data
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 删除角色
|
||||
*/
|
||||
export function roleDelete(roleId) {
|
||||
return http.request({
|
||||
url: '/role/delete/'+roleId,
|
||||
method: 'DELETE',
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 批量删除角色
|
||||
*/
|
||||
export function roleBatchDelete(data) {
|
||||
return http.request({
|
||||
url: '/role/batchDelete',
|
||||
method: 'DELETE',
|
||||
data
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 获取角色菜单
|
||||
*/
|
||||
export function getRoleMenuList(roleId) {
|
||||
return http.request({
|
||||
url: '/role/menu/list/'+roleId,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description: 保存角色菜单
|
||||
*/
|
||||
export function roleMenuSave(data:any) {
|
||||
return http.request({
|
||||
url: '/role/menu/save',
|
||||
method: 'POST',
|
||||
data,
|
||||
});
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
import type { PropType } from 'vue';
|
||||
|
||||
export const basicProps = {
|
||||
value: {
|
||||
type: [Array, Object, String, Number],
|
||||
default: undefined,
|
||||
},
|
||||
request: {
|
||||
type: Function as PropType<(...arg: any[]) => Promise<any>>,
|
||||
default: null,
|
||||
required: true,
|
||||
},
|
||||
//是否缓存数据
|
||||
cache: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
//开启缓存,必传缓存key,否则不生效
|
||||
cacheKey: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
width: {
|
||||
type: Number as PropType<number>,
|
||||
default: 150,
|
||||
},
|
||||
//block属性将使按钮适合其父宽度
|
||||
block: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '请选择',
|
||||
},
|
||||
};
|
@ -0,0 +1,140 @@
|
||||
<template>
|
||||
<PageWrapper>
|
||||
<a-card :bordered="false" class="pt-3 mb-3 proCard">
|
||||
<BasicForm @register="register" @submit="handleSubmit" @reset="handleReset" />
|
||||
</a-card>
|
||||
<a-card :bordered="false" class="proCard">
|
||||
<BasicTable
|
||||
:columns="columns"
|
||||
:request="loadDataTable"
|
||||
:row-key="(row) => row.id"
|
||||
ref="tableRef"
|
||||
:row-selection="{ onChange: onSelectionChange }"
|
||||
>
|
||||
<template #tableTitle>
|
||||
<a-button
|
||||
type="danger"
|
||||
@click="handleDelete()"
|
||||
:disabled="!selectionData.length"
|
||||
v-perm="['sys:emailLog:batchDelete']"
|
||||
>
|
||||
<template #icon>
|
||||
<DeleteOutlined />
|
||||
</template>
|
||||
删除
|
||||
</a-button>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'action'">
|
||||
<a-space>
|
||||
<a-button
|
||||
type="primary"
|
||||
@click="handleInfo(record.id)"
|
||||
v-perm="['sys:emailLog:detail']"
|
||||
>
|
||||
<template #icon><EditOutlined /></template>
|
||||
详情
|
||||
</a-button>
|
||||
<a-button
|
||||
type="primary"
|
||||
danger
|
||||
@click="handleDelete(record.id)"
|
||||
v-perm="['sys:emailLog:delete']"
|
||||
>
|
||||
<template #icon><DeleteOutlined /></template>
|
||||
删除
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</a-card>
|
||||
|
||||
<editDialog
|
||||
v-if="editVisible"
|
||||
:emailId="emailId"
|
||||
v-model:visible="editVisible"
|
||||
@success="reloadTable('noRefresh')"
|
||||
/>
|
||||
</PageWrapper>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref, h, nextTick, defineAsyncComponent } from 'vue';
|
||||
import { EditOutlined, DeleteOutlined } from '@ant-design/icons-vue';
|
||||
import { schemas } from './querySchemas';
|
||||
import { useForm } from '@/components/Form/index';
|
||||
import { getEmailLogList, emailLogDelete, emailLogBatchDelete } from '@/api/logger/emailLog';
|
||||
import { columns } from './columns';
|
||||
import { Modal, message } from 'ant-design-vue';
|
||||
const editDialog = defineAsyncComponent(() => import('./edit.vue'));
|
||||
const emailId = ref(0);
|
||||
const editVisible = ref(false);
|
||||
const selectionData = ref([]);
|
||||
const tableRef = ref();
|
||||
const formParams = reactive({
|
||||
title: '',
|
||||
receiveType: '',
|
||||
bizType: '',
|
||||
status: '',
|
||||
});
|
||||
|
||||
const loadDataTable = async (res: any) => {
|
||||
const result = await getEmailLogList({ ...formParams, ...res });
|
||||
return result;
|
||||
};
|
||||
|
||||
function reloadTable(noRefresh = '') {
|
||||
tableRef.value.reload(noRefresh ? {} : { pageNo: 1 });
|
||||
}
|
||||
const [register, {}] = useForm({
|
||||
rowProps: { gutter: [16, 0] },
|
||||
colProps: {
|
||||
xs: 24,
|
||||
sm: 24,
|
||||
md: 12,
|
||||
lg: 8,
|
||||
xl: 6,
|
||||
},
|
||||
labelCol: { span: 6, offset: 0 },
|
||||
schemas,
|
||||
});
|
||||
function handleSubmit(values) {
|
||||
for (const key in formParams) {
|
||||
formParams[key] = '';
|
||||
}
|
||||
for (const key in values) {
|
||||
formParams[key] = values[key];
|
||||
}
|
||||
reloadTable();
|
||||
}
|
||||
|
||||
function handleReset() {
|
||||
for (const key in formParams) {
|
||||
formParams[key] = '';
|
||||
}
|
||||
reloadTable();
|
||||
}
|
||||
const handleInfo = async (id) => {
|
||||
emailId.value = id;
|
||||
await nextTick();
|
||||
editVisible.value = true;
|
||||
};
|
||||
|
||||
async function handleDelete(id) {
|
||||
Modal.confirm({
|
||||
title: '提示',
|
||||
content: '确定要删除?',
|
||||
onOk: async () => {
|
||||
id ? await emailLogDelete(id) : await emailLogBatchDelete(selectionData.value);
|
||||
message.success('删除成功');
|
||||
reloadTable();
|
||||
},
|
||||
});
|
||||
}
|
||||
function onSelectionChange(value) {
|
||||
selectionData.value = value;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
@ -0,0 +1,9 @@
|
||||
/dist/*
|
||||
.local
|
||||
.output.js
|
||||
/node_modules/**
|
||||
|
||||
**/*.svg
|
||||
**/*.sh
|
||||
|
||||
/public/*
|
@ -0,0 +1,99 @@
|
||||
import { http } from '@/utils/http/axios';
|
||||
|
||||
/**
|
||||
* @description: 角色列表
|
||||
*/
|
||||
export function getRoleList(params?) {
|
||||
return http.request({
|
||||
url: '/role/page',
|
||||
method: 'GET',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取全部角色列表
|
||||
* @param params 参数
|
||||
* @returns 返回结果
|
||||
*/
|
||||
export function getRoleAllList(params?) {
|
||||
return http.request({
|
||||
url: '/role/list',
|
||||
method: 'GET',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 根据ID获取详情
|
||||
*/
|
||||
export function getRoleDetail(roleId) {
|
||||
return http.request({
|
||||
url: '/role/detail/' + roleId,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 添加角色
|
||||
*/
|
||||
export function roleAdd(data: any) {
|
||||
return http.request({
|
||||
url: '/role/add',
|
||||
method: 'POST',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 更新角色
|
||||
*/
|
||||
export function roleUpdate(data: any) {
|
||||
return http.request({
|
||||
url: '/role/update',
|
||||
method: 'PUT',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 删除角色
|
||||
*/
|
||||
export function roleDelete(roleId) {
|
||||
return http.request({
|
||||
url: '/role/delete/' + roleId,
|
||||
method: 'DELETE',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 批量删除角色
|
||||
*/
|
||||
export function roleBatchDelete(data) {
|
||||
return http.request({
|
||||
url: '/role/batchDelete',
|
||||
method: 'DELETE',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 获取角色菜单
|
||||
*/
|
||||
export function getRoleMenuList(roleId) {
|
||||
return http.request({
|
||||
url: '/role/menu/list/' + roleId,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 保存角色菜单
|
||||
*/
|
||||
export function roleMenuSave(data: any) {
|
||||
return http.request({
|
||||
url: '/role/menu/save',
|
||||
method: 'POST',
|
||||
data,
|
||||
});
|
||||
}
|
@ -0,0 +1,115 @@
|
||||
import { h } from 'vue';
|
||||
import { Tag,Avatar } from 'ant-design-vue';
|
||||
export const columns = [
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'id',
|
||||
fixed:'left',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '广告标题',
|
||||
dataIndex: 'title',
|
||||
width: 250,
|
||||
customRender({ record }) {
|
||||
return h('a', {
|
||||
href: record.url,
|
||||
target:"_blank"
|
||||
}, record.title);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '广告封面',
|
||||
dataIndex: 'cover',
|
||||
customRender({ record }) {
|
||||
return h(Avatar, {
|
||||
size: 35,
|
||||
src: record.cover,
|
||||
shape: 'square',
|
||||
fit: 'fill',
|
||||
});
|
||||
},
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '广告类型',
|
||||
dataIndex: 'type',
|
||||
customRender({ record }) {
|
||||
let typeText = ''
|
||||
switch (record.type) {
|
||||
case 1:
|
||||
typeText='图片'
|
||||
break;
|
||||
case 2:
|
||||
typeText='文字'
|
||||
break;
|
||||
case 3:
|
||||
typeText='视频'
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return h('span', typeText || '-');
|
||||
},
|
||||
width:100
|
||||
},
|
||||
{
|
||||
title: '广告状态',
|
||||
dataIndex: 'status',
|
||||
customRender({ record }) {
|
||||
return h(
|
||||
Tag,
|
||||
{
|
||||
color: record.status ==1 ? 'success' : 'error',
|
||||
},
|
||||
{
|
||||
default: () => (record.status ==1 ? '正常' : '停用'),
|
||||
},
|
||||
);
|
||||
},
|
||||
width:100
|
||||
},
|
||||
{
|
||||
title: '广告尺寸',
|
||||
dataIndex: 'size',
|
||||
customRender({ record }) {
|
||||
return record.width + 'x' + record.height;
|
||||
},
|
||||
width:100
|
||||
},
|
||||
{
|
||||
title: '投放时间',
|
||||
dataIndex: 'time',
|
||||
customRender({ record }) {
|
||||
return record.startTime + '-' + record.endTime;
|
||||
},
|
||||
width:300
|
||||
},
|
||||
{
|
||||
title: '点击量',
|
||||
dataIndex: 'click',
|
||||
width:100
|
||||
},
|
||||
{
|
||||
title: '排序',
|
||||
dataIndex: 'sort',
|
||||
width:100
|
||||
},
|
||||
{
|
||||
title: '创建人',
|
||||
dataIndex: 'createUser',
|
||||
width:100
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createTime',
|
||||
width: 180,
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
fixed:'right',
|
||||
dataIndex: 'action',
|
||||
key: 'action',
|
||||
width: 200,
|
||||
},
|
||||
];
|
@ -0,0 +1,76 @@
|
||||
import { h } from 'vue';
|
||||
import { Tag,Avatar } from 'ant-design-vue';
|
||||
|
||||
export const columns = [
|
||||
{
|
||||
title:'ID',
|
||||
dataIndex: 'id',
|
||||
width:100
|
||||
},
|
||||
{
|
||||
title: '文章标题',
|
||||
dataIndex: 'title',
|
||||
customRender({ record }) {
|
||||
return h('a', {
|
||||
href: 'http://www.baidu.com',
|
||||
target:"_blank"
|
||||
},record.title);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '文章分类',
|
||||
dataIndex: 'categoryName',
|
||||
},
|
||||
{
|
||||
title: '文章封面',
|
||||
dataIndex: 'cover',
|
||||
key: 'cover',
|
||||
customRender({ record }) {
|
||||
return h(Avatar, {
|
||||
size: 48,
|
||||
src: record.cover,
|
||||
shape: 'square',
|
||||
fit: 'fill',
|
||||
});
|
||||
},
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '文章分类',
|
||||
dataIndex: 'categoryName',
|
||||
},
|
||||
{
|
||||
title: '文章作者',
|
||||
dataIndex: 'author',
|
||||
},
|
||||
{
|
||||
title: '文章状态',
|
||||
dataIndex: 'status',
|
||||
customRender({ record }) {
|
||||
return h(
|
||||
Tag,
|
||||
{
|
||||
color: record.status ==1 ? 'error' : 'success',
|
||||
},
|
||||
{
|
||||
default: () => (record.status ==1 ? '下架' : '正常'),
|
||||
},
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '创建人',
|
||||
dataIndex: 'createUser',
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createTime',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
fixed:'right',
|
||||
dataIndex: 'action',
|
||||
key: 'action',
|
||||
width: 200,
|
||||
},
|
||||
];
|
@ -0,0 +1,186 @@
|
||||
<template>
|
||||
<PageWrapper>
|
||||
<a-card :bordered="false" class="pt-3 mb-3 proCard">
|
||||
<BasicForm @register="register" @submit="handleSubmit" @reset="handleReset" />
|
||||
</a-card>
|
||||
<a-card :bordered="false" class="proCard">
|
||||
<BasicTable
|
||||
:columns="columns"
|
||||
:request="loadDataTable"
|
||||
:row-key="(row) => row.id"
|
||||
ref="tableRef"
|
||||
:row-selection="{ onChange: onSelectionChange }"
|
||||
>
|
||||
<template #tableTitle>
|
||||
<a-button
|
||||
type="danger"
|
||||
@click="handleDelete()"
|
||||
:disabled="!selectionData.length"
|
||||
v-perm="['sys:loginLog:batchDelete']"
|
||||
>
|
||||
<template #icon>
|
||||
<DeleteOutlined />
|
||||
</template>
|
||||
删除
|
||||
</a-button>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'action'">
|
||||
<a-space>
|
||||
<a-button
|
||||
type="primary"
|
||||
@click="handleDetail(record.id)"
|
||||
v-perm="['sys:loginLog:detail']"
|
||||
>
|
||||
<template #icon><EyeOutlined /></template>
|
||||
详情
|
||||
</a-button>
|
||||
<a-button
|
||||
type="primary"
|
||||
danger
|
||||
@click="handleDelete(record.id)"
|
||||
v-perm="['sys:loginLog:delete']"
|
||||
>
|
||||
<template #icon><DeleteOutlined /></template>
|
||||
删除
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</a-card>
|
||||
|
||||
<editDialog
|
||||
v-if="editVisible"
|
||||
:loginlogId="loginlogId"
|
||||
v-model:visible="editVisible"
|
||||
@success="reloadTable('noRefresh')"
|
||||
/>
|
||||
</PageWrapper>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref, h, nextTick, defineAsyncComponent } from 'vue';
|
||||
import { EyeOutlined, DeleteOutlined } from '@ant-design/icons-vue';
|
||||
import { schemas } from './loginLog/querySchemas';
|
||||
import { useForm } from '@/components/Form/index';
|
||||
import { TableAction } from '@/components/Table';
|
||||
import { getLoginLogList, loginLogDelete, loginLogBatchDelete } from '@/api/system/loginLog';
|
||||
import { columns } from './loginLog/columns';
|
||||
import { Modal, message } from 'ant-design-vue';
|
||||
|
||||
/**
|
||||
* 导入组件
|
||||
*/
|
||||
const editDialog = defineAsyncComponent(() => import('./loginLog/edit.vue'));
|
||||
|
||||
/**
|
||||
* 定义参数变量
|
||||
*/
|
||||
const loginlogId = ref(0);
|
||||
const editVisible = ref(false);
|
||||
const selectionData = ref([]);
|
||||
const tableRef = ref();
|
||||
|
||||
/**
|
||||
* 定义查询参数
|
||||
*/
|
||||
const formParams = reactive({
|
||||
username: '',
|
||||
type: '',
|
||||
status: '',
|
||||
});
|
||||
|
||||
/**
|
||||
* 加载数据列表
|
||||
* @param res 参数
|
||||
*/
|
||||
const loadDataTable = async (res: any) => {
|
||||
const result = await getLoginLogList({ ...formParams, ...res });
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* 刷新数据列表
|
||||
* @param noRefresh 参数
|
||||
*/
|
||||
function reloadTable(noRefresh = '') {
|
||||
tableRef.value.reload(noRefresh ? {} : { pageNo: 1 });
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册
|
||||
*/
|
||||
const [register, {}] = useForm({
|
||||
rowProps: { gutter: [16, 0] },
|
||||
colProps: {
|
||||
xs: 24,
|
||||
sm: 24,
|
||||
md: 12,
|
||||
lg: 8,
|
||||
xl: 6,
|
||||
},
|
||||
labelCol: { span: 6, offset: 0 },
|
||||
schemas,
|
||||
});
|
||||
|
||||
/**
|
||||
* 执行提交表单
|
||||
* @param values 参数
|
||||
*/
|
||||
function handleSubmit(values) {
|
||||
for (const key in formParams) {
|
||||
formParams[key] = '';
|
||||
}
|
||||
for (const key in values) {
|
||||
formParams[key] = values[key];
|
||||
}
|
||||
reloadTable();
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行重置
|
||||
*/
|
||||
function handleReset() {
|
||||
for (const key in formParams) {
|
||||
formParams[key] = '';
|
||||
}
|
||||
reloadTable();
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行查询详情
|
||||
* @param id 参数
|
||||
*/
|
||||
const handleDetail = async (id) => {
|
||||
loginlogId.value = id;
|
||||
await nextTick();
|
||||
editVisible.value = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* 执行删除
|
||||
* @param id 参数
|
||||
*/
|
||||
async function handleDelete(id) {
|
||||
Modal.confirm({
|
||||
title: '提示',
|
||||
content: '确定要删除?',
|
||||
onOk: async () => {
|
||||
id ? await loginLogDelete(id) : await loginLogBatchDelete(selectionData.value);
|
||||
message.success('删除成功');
|
||||
reloadTable();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 选项发生变化
|
||||
* @param value 参数
|
||||
*/
|
||||
function onSelectionChange(value) {
|
||||
selectionData.value = value;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
@ -0,0 +1,64 @@
|
||||
import { h } from 'vue';
|
||||
import { Tag,Avatar } from 'ant-design-vue';
|
||||
|
||||
export const columns = [
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'id',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '位置描述',
|
||||
dataIndex: 'description',
|
||||
width: 250,
|
||||
customRender({ record }) {
|
||||
return record.description + ">>" + record.location;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '推荐类型',
|
||||
dataIndex: 'typeText',
|
||||
},
|
||||
{
|
||||
title: '推荐ID',
|
||||
dataIndex: 'typeId',
|
||||
},
|
||||
{
|
||||
title: '推荐图片',
|
||||
dataIndex: 'image',
|
||||
customRender({ record }) {
|
||||
return h(Avatar, {
|
||||
size: 48,
|
||||
src: record.image,
|
||||
shape: 'square',
|
||||
fit: 'fill',
|
||||
});
|
||||
},
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: '推荐标题',
|
||||
dataIndex: 'typeTitle',
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
title: '排序',
|
||||
dataIndex: 'sort',
|
||||
},
|
||||
{
|
||||
title: '创建人',
|
||||
dataIndex: 'createUser',
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createTime',
|
||||
width: 180,
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
fixed:'right',
|
||||
dataIndex: 'action',
|
||||
key: 'action',
|
||||
width: 200,
|
||||
},
|
||||
];
|
@ -0,0 +1,52 @@
|
||||
import { h } from 'vue';
|
||||
import { ElTag } from 'element-plus';
|
||||
|
||||
export const columns = [
|
||||
{
|
||||
title: '数据表名称',
|
||||
dataIndex: 'tableName',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: '数据表描述',
|
||||
dataIndex: 'tableComment',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: '数据表引擎',
|
||||
dataIndex: 'engine',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '数据表行数',
|
||||
dataIndex: 'tableRows',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '数据表长度',
|
||||
dataIndex: 'dataLength',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '数据表自增索引',
|
||||
dataIndex: 'autoIncrement',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '数据表编码',
|
||||
dataIndex: 'tableCollation',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createTime',
|
||||
width: 180,
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
fixed: 'right',
|
||||
dataIndex: 'action',
|
||||
key: 'action',
|
||||
width: 130,
|
||||
},
|
||||
];
|
@ -0,0 +1,85 @@
|
||||
<template>
|
||||
<a-modal v-model:visible="props.visible" title="日志详情" width="800px" @cancel="dialogClose">
|
||||
<a-descriptions :column="2" bordered :labelStyle="{ width: '160px' }">
|
||||
<a-descriptions-item label="任务名称:">{{ formData.jobName }}</a-descriptions-item>
|
||||
<a-descriptions-item label="任务组名:">{{ formData.jobGroup }}</a-descriptions-item>
|
||||
<a-descriptions-item label="任务触发器:">{{ formData.jobTrigger }}</a-descriptions-item>
|
||||
<a-descriptions-item label="任务信息:">{{ formData.jobMessage }}</a-descriptions-item>
|
||||
<a-descriptions-item label="cron执行表达式:">{{
|
||||
formData.cronExpression
|
||||
}}</a-descriptions-item>
|
||||
<a-descriptions-item label="任务开始时间:">{{ formData.startTime }}</a-descriptions-item>
|
||||
<a-descriptions-item label="任务结束时间:">{{ formData.endTime }}</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<a-button @click="dialogClose">关闭</a-button>
|
||||
</span>
|
||||
</template>
|
||||
</a-modal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { getJobLogDetail } from '@/api/monitor/job';
|
||||
import { onMounted, reactive, shallowRef } from 'vue';
|
||||
|
||||
/**
|
||||
* 定义表单参数
|
||||
*/
|
||||
const formData = reactive({
|
||||
id: '',
|
||||
jobName: '',
|
||||
jobGroup: '',
|
||||
jobTrigger: '',
|
||||
jobMessage: '',
|
||||
cronExpression: '',
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:visible']);
|
||||
|
||||
/**
|
||||
* 定义接收的参数
|
||||
*/
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
default: false,
|
||||
},
|
||||
logId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
default: 0,
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* 关闭窗体
|
||||
*/
|
||||
const dialogClose = () => {
|
||||
emit('update:visible', false);
|
||||
};
|
||||
|
||||
/**
|
||||
* 设置表单数据
|
||||
*/
|
||||
const setFormData = async () => {
|
||||
const data = await getJobLogDetail(props.logId);
|
||||
for (const key in formData) {
|
||||
if (data[key] != null && data[key] != undefined) {
|
||||
//@ts-ignore
|
||||
formData[key] = data[key];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 钩子函数
|
||||
*/
|
||||
onMounted(() => {
|
||||
if (props.logId) {
|
||||
setFormData();
|
||||
}
|
||||
});
|
||||
</script>
|
@ -0,0 +1,34 @@
|
||||
import { FormSchema } from '@/components/Form/index';
|
||||
export const schemas: FormSchema[] = [
|
||||
{
|
||||
name: 'title',
|
||||
component: 'Input',
|
||||
label: '模板名称',
|
||||
componentProps: {
|
||||
placeholder: '请输入模板名称',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'type',
|
||||
component: 'Select',
|
||||
label: '模板类型',
|
||||
componentProps: {
|
||||
placeholder: '请选择模板类型',
|
||||
clearable: true,
|
||||
options: [
|
||||
{
|
||||
label: '阿里云',
|
||||
value: '1',
|
||||
},
|
||||
{
|
||||
label: '腾讯云',
|
||||
value: '2',
|
||||
},
|
||||
{
|
||||
label: '华为云',
|
||||
value: '3',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
@ -0,0 +1,115 @@
|
||||
import { h } from 'vue';
|
||||
import { Tag } from 'ant-design-vue';
|
||||
export const columns = [
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'id',
|
||||
fixed: 'left',
|
||||
width: 50,
|
||||
},
|
||||
{
|
||||
title: '配置项名称',
|
||||
dataIndex: 'name',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '配置项编码',
|
||||
dataIndex: 'code',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '配置项值',
|
||||
dataIndex: 'value',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '配置项类型',
|
||||
dataIndex: 'type',
|
||||
width: 100,
|
||||
customRender({ record }) {
|
||||
let typeText = '';
|
||||
switch (record.type) {
|
||||
case 'hidden':
|
||||
typeText = '隐藏';
|
||||
break;
|
||||
case 'readonly':
|
||||
typeText = '只读文本';
|
||||
break;
|
||||
case 'number':
|
||||
typeText = '数字';
|
||||
break;
|
||||
case 'text':
|
||||
typeText = '单行文本';
|
||||
break;
|
||||
case 'textarea':
|
||||
typeText = '多行文本';
|
||||
break;
|
||||
case 'password':
|
||||
typeText = '密码';
|
||||
break;
|
||||
case 'radio':
|
||||
typeText = '单选框';
|
||||
break;
|
||||
case 'checkbox':
|
||||
typeText = '复选框';
|
||||
break;
|
||||
case 'select':
|
||||
typeText = '下拉框(单选)';
|
||||
break;
|
||||
case 'selects':
|
||||
typeText = '下拉框(多选)';
|
||||
break;
|
||||
case 'icon':
|
||||
typeText = '字体图标';
|
||||
break;
|
||||
case 'date':
|
||||
typeText = '日期';
|
||||
break;
|
||||
case 'datetime':
|
||||
typeText = '时间';
|
||||
break;
|
||||
case 'image':
|
||||
typeText = '单张图片';
|
||||
break;
|
||||
case 'images':
|
||||
typeText = '多张图片';
|
||||
break;
|
||||
case 'file':
|
||||
typeText = '单个文件';
|
||||
break;
|
||||
case 'files':
|
||||
typeText = '多个文件';
|
||||
break;
|
||||
case 'ueditor':
|
||||
typeText = '富文本编辑器';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return h('span', typeText || '-');
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '配置项状态',
|
||||
dataIndex: 'status',
|
||||
width: 100,
|
||||
customRender({ record }) {
|
||||
return h(
|
||||
Tag,
|
||||
{
|
||||
color: record.status == 1 ? 'success' : 'error',
|
||||
},
|
||||
{
|
||||
default: () => (record.status == 1 ? '正常' : '停用'),
|
||||
},
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
fixed: 'right',
|
||||
dataIndex: 'action',
|
||||
key: 'action',
|
||||
width: 200,
|
||||
},
|
||||
];
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user