feat(ui): 新增系统管理员

This commit is contained in:
廖彦棋 2024-03-11 15:59:15 +08:00
parent e91f54e79e
commit 32368caf1b
12 changed files with 233 additions and 115 deletions

View File

@ -3,7 +3,7 @@ import tokenHandler from "./token";
const { _tokenData, refreshToken, setCurRequest } = tokenHandler();
const createInstance = (baseURL: string = (import.meta as any).env.VITE_PROXY_BASE_URL) => {
const createInstance = (baseURL: string) => {
const instance = axios.create({
baseURL,

View File

@ -127,110 +127,6 @@ importers:
specifier: ^1.8.27
version: 1.8.27(typescript@5.3.3)
projects/vue-mobile:
dependencies:
pinia:
specifier: ^2.1.7
version: 2.1.7(typescript@5.3.3)(vue@3.4.21)
vue:
specifier: ^3.4.15
version: 3.4.21(typescript@5.3.3)
vue-router:
specifier: ^4.2.5
version: 4.3.0(vue@3.4.21)
devDependencies:
'@rushstack/eslint-patch':
specifier: ^1.3.3
version: 1.7.2
'@tsconfig/node20':
specifier: ^20.1.2
version: 20.1.2
'@types/node':
specifier: ^20.11.10
version: 20.11.24
'@vitejs/plugin-vue':
specifier: ^5.0.3
version: 5.0.4(vite@5.1.5)(vue@3.4.21)
'@vitejs/plugin-vue-jsx':
specifier: ^3.1.0
version: 3.1.0(vite@5.1.5)(vue@3.4.21)
'@vue/eslint-config-typescript':
specifier: ^12.0.0
version: 12.0.0(eslint-plugin-vue@9.22.0)(eslint@8.57.0)(typescript@5.3.3)
'@vue/tsconfig':
specifier: ^0.5.1
version: 0.5.1
eslint:
specifier: ^8.49.0
version: 8.57.0
eslint-plugin-vue:
specifier: ^9.17.0
version: 9.22.0(eslint@8.57.0)
npm-run-all2:
specifier: ^6.1.1
version: 6.1.2
typescript:
specifier: ~5.3.0
version: 5.3.3
vite:
specifier: ^5.0.11
version: 5.1.5(@types/node@20.11.24)(less@4.2.0)
vue-tsc:
specifier: ^1.8.27
version: 1.8.27(typescript@5.3.3)
projects/vue-web:
dependencies:
pinia:
specifier: ^2.1.7
version: 2.1.7(typescript@5.3.3)(vue@3.4.21)
vue:
specifier: ^3.4.15
version: 3.4.21(typescript@5.3.3)
vue-router:
specifier: ^4.2.5
version: 4.3.0(vue@3.4.21)
devDependencies:
'@rushstack/eslint-patch':
specifier: ^1.3.3
version: 1.7.2
'@tsconfig/node20':
specifier: ^20.1.2
version: 20.1.2
'@types/node':
specifier: ^20.11.10
version: 20.11.24
'@vitejs/plugin-vue':
specifier: ^5.0.3
version: 5.0.4(vite@5.1.5)(vue@3.4.21)
'@vitejs/plugin-vue-jsx':
specifier: ^3.1.0
version: 3.1.0(vite@5.1.5)(vue@3.4.21)
'@vue/eslint-config-typescript':
specifier: ^12.0.0
version: 12.0.0(eslint-plugin-vue@9.22.0)(eslint@8.57.0)(typescript@5.3.3)
'@vue/tsconfig':
specifier: ^0.5.1
version: 0.5.1
eslint:
specifier: ^8.49.0
version: 8.57.0
eslint-plugin-vue:
specifier: ^9.17.0
version: 9.22.0(eslint@8.57.0)
npm-run-all2:
specifier: ^6.1.1
version: 6.1.2
typescript:
specifier: ~5.3.0
version: 5.3.3
vite:
specifier: ^5.0.11
version: 5.1.5(@types/node@20.11.24)(less@4.2.0)
vue-tsc:
specifier: ^1.8.27
version: 1.8.27(typescript@5.3.3)
packages:
/@aashutoshrathi/word-wrap@1.2.6:

View File

@ -1,3 +1,2 @@
VITE_PROXY_BASE_URL="/api"
VITE_TARGET_URL="http://172.22.11.2:5678"
VITE_SOCKET_IO_URL="http://172.28.1.3:8899"

View File

@ -1,3 +1,2 @@
VITE_PROXY_BASE_URL=""
VITE_TARGET_URL="/"
VITE_SOCKET_IO_URL="/"

View File

@ -8,6 +8,7 @@ type OriginProps = SwitchInstance["$props"];
interface Props extends /* @vue-ignore */ OriginProps {
modelValue: boolean | string | number;
api: (params?: any) => Promise<BaseResponse<any>>;
onSuccess?: (res?: any) => void;
}
const props = defineProps<Props>();
@ -23,8 +24,9 @@ const _value = computed({
const onBeforeChange = async (params) => {
try {
await props.api({ ...params, value: !_value.value });
const res = await props.api({ ...params, value: !_value.value });
Message.success("操作成功");
props?.onSuccess?.(res);
return true;
} catch (err) {
console.log(err);

View File

@ -5,7 +5,7 @@ import type { BaseResponse } from "@gpt-vue/packages/type";
export const uploadUrl = import.meta.env.VITE_PROXY_BASE_URL + "/api/admin/upload";
export const instance = createInstance()
export const instance = createInstance(import.meta.env.VITE_PROXY_BASE_URL)
instance.interceptors.request.use((config) => {
config.headers[__AUTH_KEY] = localStorage.getItem(__AUTH_KEY);

View File

@ -11,6 +11,7 @@ import {
IconLock,
IconCodepen,
IconWechatpay,
IconRobot,
} from "@arco-design/web-vue/es/icon";
const menu = [
@ -123,6 +124,15 @@ const menu = [
},
component: () => import("@/views/LoginLog.vue"),
},
{
path: "/sysAdmin",
name: "SysAdmin",
meta: {
title: "系统管理员",
icon: IconRobot,
},
component: () => import("@/views/SysAdmin/SysAdminContainer.vue"),
},
];
export default menu;

View File

@ -0,0 +1,115 @@
<script lang="ts" setup>
import { Message } from "@arco-design/web-vue";
import { dateFormat } from "@gpt-vue/packages/utils";
import SearchTable from "@/components/SearchTable/SearchTable.vue";
import type { SearchTableColumns } from "@/components/SearchTable/type";
import ConfirmSwitch from "@/components/ConfirmSwitch.vue";
import usePopup from "@/composables/usePopup";
import SysAdminForm from "./SysAdminForm.vue";
import SysAdminResetPWD from "./SysAdminResetPWD.vue";
import { getList, save, remove, resetPass } from "./api";
const columns: SearchTableColumns[] = [
{
dataIndex: "username",
title: "账号",
search: {
valueType: "input",
},
},
{
dataIndex: "last_login_ip",
title: "最后登录IP",
},
{
dataIndex: "last_login_at",
title: "最后登录时间",
render: ({ record }) => dateFormat(record.last_login_at),
},
{
dataIndex: "created_at",
title: "创建时间",
render: ({ record }) => dateFormat(record.created_at),
},
{
dataIndex: "updated_at",
title: "更新时间",
render: ({ record }) => dateFormat(record.updated_at),
},
{
dataIndex: "status",
title: "状态",
slotName: "switch",
width: 80,
},
{
title: "操作",
slotName: "actions",
fixed: "right",
width: 180,
},
];
const openFormModal = usePopup(SysAdminForm, {
nodeProps: ([_, record]) => ({ record }),
popupProps: ([reload, record], exposed) => ({
title: `${record?.id ? "编辑" : "新增"}系统管理员`,
onBeforeOk: async (done) => {
await exposed()?.handleSubmit(save, {
id: record?.id,
});
await reload();
done(true);
},
}),
});
const openResetPWDModal = usePopup(SysAdminResetPWD, {
popupProps: ([reload, record], exposed) => ({
title: `修改密码`,
onBeforeOk: async (done) => {
await exposed()?.handleSubmit(resetPass, {
id: record?.id,
});
Message.success("修改成功");
await reload();
done(true);
},
}),
});
const handleRemove = async (id, reload) => {
await remove({ id });
Message.success("删除成功");
await reload();
return true;
};
</script>
<template>
<SearchTable :request="getList" :columns="columns">
<template #header-option="{ reload }">
<a-button type="primary" @click="openFormModal(reload, {})">
<template #icon> <icon-plus /> </template>
新增
</a-button>
</template>
<template #switch="{ record, column }">
<ConfirmSwitch
v-model="record[column.dataIndex]"
:api="async () => save({ ...record, status: !record.status })"
/>
</template>
<template #actions="{ record, reload }">
<a-link @click="openFormModal(reload, record)">编辑</a-link>
<a-link @click="openResetPWDModal(reload, record)">修改密码</a-link>
<a-popconfirm
content="是否删除?"
position="left"
type="warning"
:on-before-ok="() => handleRemove(record.id, reload)"
>
<a-link status="danger">删除</a-link>
</a-popconfirm>
</template>
</SearchTable>
</template>

View File

@ -0,0 +1,39 @@
<template>
<a-form ref="formRef" :model="formData" auto-label-width>
<a-form-item field="username" label="账号" :rules="[{ required: true, message: '请输入账号' }]">
<a-input v-model="formData.username" placeholder="请输入账号" />
</a-form-item>
<a-form-item
v-if="!props.record?.id"
field="password"
label="密码"
:rules="[{ required: true, message: '请输入密码' }]"
>
<a-input-password
v-model="formData.password"
placeholder="请输入密码"
autocomplete="new-password"
/>
</a-form-item>
<a-form-item field="status" label="启用状态">
<a-switch v-model="formData.status" />
</a-form-item>
</a-form>
</template>
<script lang="ts" setup>
import useSubmit from "@/composables/useSubmit";
const props = defineProps({
record: Object,
});
const { formRef, formData, handleSubmit } = useSubmit({
username: "",
password: "",
status: true,
});
defineExpose({
handleSubmit,
});
</script>

View File

@ -0,0 +1,28 @@
<template>
<a-form ref="formRef" :model="formData" auto-label-width>
<a-form-item
field="password"
label="密码"
:rules="[{ required: true, message: '请输入密码' }]"
showable
>
<a-input-password
v-model="formData.password"
placeholder="请输入密码"
autocomplete="new-password"
/>
</a-form-item>
</a-form>
</template>
<script lang="ts" setup>
import useSubmit from "@/composables/useSubmit";
const { formRef, formData, handleSubmit } = useSubmit({
password: "",
});
defineExpose({
handleSubmit,
});
</script>

View File

@ -0,0 +1,33 @@
import http from "@/http/config";
export const getList = (params) => {
return http({
url: "/api/admin/sysUser/list",
method: "get",
params
})
}
export const save = (data) => {
return http({
url: "/api/admin/sysUser/save",
method: "post",
data
})
}
export const remove = (data) => {
return http({
url: "/api/admin/sysUser/remove",
method: "post",
data
})
}
export const resetPass = (data) => {
return http({
url: "/api/admin/sysUser/resetPass",
method: "post",
data
})
}

View File

@ -36,12 +36,9 @@ export default defineConfig(({ mode }) => {
output: {
manualChunks: (id) => {
if (id.includes("node_modules")) {
if (id.includes("arco")) {
return `arco`;
}
if (id.includes("vue") && !id.includes("arco")) {
return `vue`;
}
if (id.includes("echats")) return `echats`;
if (id.includes("arco")) return `arco`;
if (id.includes("vue") && !id.includes("arco")) return `vue`;
return `vendor`;
}
},