mirror of
https://github.com/yangjian102621/geekai.git
synced 2025-09-18 09:16:39 +08:00
feat(ui):用户
This commit is contained in:
parent
8e7413da97
commit
cb0e7d64ff
@ -84,7 +84,7 @@ const optionsEvent = {
|
||||
</AFormItem>
|
||||
</AGridItem>
|
||||
<AGridItem suffix>
|
||||
<ASpace>
|
||||
<ASpace class="flex-end">
|
||||
<slot name="search-options" :option="optionsEvent">
|
||||
<AButton
|
||||
type="primary"
|
||||
@ -114,4 +114,8 @@ const optionsEvent = {
|
||||
.search-form-conteiner {
|
||||
padding: 16px 0;
|
||||
}
|
||||
.flex-end {
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
}
|
||||
</style>
|
||||
|
@ -33,8 +33,9 @@ const handleSearch = async (tips?: boolean) => {
|
||||
onActivated(handleSearch);
|
||||
</script>
|
||||
<template>
|
||||
<div class="search-table">
|
||||
<div ref="tableContainerRef" class="search-table-container">
|
||||
<slot name="header" v-bind="{ reload: handleSearch }" />
|
||||
<div class="simple-table">
|
||||
<div ref="tableContainerRef" class="simple-table-container">
|
||||
<ATable
|
||||
v-bind="{
|
||||
...$attrs,
|
||||
@ -52,15 +53,15 @@ onActivated(handleSearch);
|
||||
</div>
|
||||
</template>
|
||||
<style scoped>
|
||||
.search-table {
|
||||
.simple-table {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
.search-table-container {
|
||||
.simple-table-container {
|
||||
flex: 1;
|
||||
}
|
||||
.search-table-header {
|
||||
.simple-table-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
@ -1,4 +1,115 @@
|
||||
<script lang="ts" setup></script>
|
||||
<script lang="ts" setup>
|
||||
import SearchTable from "@/components/SearchTable/SearchTable.vue";
|
||||
import type { SearchTableColumns } from "@/components/SearchTable/type";
|
||||
import { getList, save as saveApi, deletApi, resetPassword } from "./api";
|
||||
import UserForm from "./UserForm.vue";
|
||||
import { ref } from "vue";
|
||||
import { Message } from "@arco-design/web-vue";
|
||||
import { dateFormat } from "@gpt-vue/packages/utils";
|
||||
import usePopup from "@/composables/usePopup";
|
||||
import UserPassword from "./UserPassword.vue";
|
||||
const columns: SearchTableColumns[] = [
|
||||
{
|
||||
title: "账号",
|
||||
dataIndex: "username",
|
||||
search: {
|
||||
valueType: "input",
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "剩余对话次数",
|
||||
dataIndex: "calls",
|
||||
},
|
||||
{
|
||||
title: "剩余绘图次数",
|
||||
dataIndex: "img_calls",
|
||||
},
|
||||
{
|
||||
title: "累计消耗tokens",
|
||||
dataIndex: "total_tokens",
|
||||
},
|
||||
{
|
||||
title: "状态",
|
||||
dataIndex: "status",
|
||||
render: ({ record }) => {
|
||||
return record.status ? "正常" : "停用";
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "过期时间",
|
||||
dataIndex: "expired_time",
|
||||
render: ({ record }) => {
|
||||
return dateFormat(record.expired_time);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "注册时间",
|
||||
dataIndex: "created_at",
|
||||
render: ({ record }) => {
|
||||
return dateFormat(record.created_at);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "操作",
|
||||
slotName: "actions",
|
||||
},
|
||||
];
|
||||
|
||||
//弹窗
|
||||
|
||||
const popup = (node, api) => {
|
||||
const nodeProps = (arg) => {
|
||||
return {
|
||||
data: arg[0].record,
|
||||
};
|
||||
};
|
||||
|
||||
const popupProps = (arg, getExposed) => {
|
||||
return {
|
||||
width: 700,
|
||||
onBeforeOk: async () => {
|
||||
const exposed = getExposed();
|
||||
const validateRes = await exposed?.formRef.value.validate();
|
||||
if (validateRes) {
|
||||
return false;
|
||||
}
|
||||
const res = await api(exposed?.form.value);
|
||||
if (res.code === 0) {
|
||||
Message.success("操作成功");
|
||||
}
|
||||
arg[0].reload();
|
||||
return res.code === 0;
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
return usePopup(node, { nodeProps, popupProps });
|
||||
};
|
||||
|
||||
const editModal = popup(UserForm, saveApi);
|
||||
const password = popup(UserPassword, resetPassword);
|
||||
|
||||
const handleDelete = async ({ id }: { id: string }, reload) => {
|
||||
const res = await deletApi(id);
|
||||
if (res.code === 0) {
|
||||
Message.success("操作成功");
|
||||
reload();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<div></div>
|
||||
<SearchTable :request="getList" :columns="columns">
|
||||
<template #actions="{ record, reload }">
|
||||
<a-link @click="editModal({ record, reload })">编辑</a-link>
|
||||
<a-popconfirm content="确定删除?" @ok="handleDelete(record, reload)">
|
||||
<a-link>删除</a-link>
|
||||
</a-popconfirm>
|
||||
<a-link @click="password({ record, reload })">重置密码</a-link>
|
||||
</template>
|
||||
<template #search-extra="{ reload }">
|
||||
<a-button @click="editModal({ reload })" status="success" size="small"
|
||||
><icon-plus />新增用户</a-button
|
||||
>
|
||||
</template>
|
||||
</SearchTable>
|
||||
</template>
|
||||
|
117
gpt-vue/projects/vue-admin/src/views/User/UserForm.vue
Normal file
117
gpt-vue/projects/vue-admin/src/views/User/UserForm.vue
Normal file
@ -0,0 +1,117 @@
|
||||
<template>
|
||||
<a-form ref="formRef" :model="form" :style="{ width: '600px' }" @submit="handleSubmit">
|
||||
<a-form-item
|
||||
field="username"
|
||||
label="账号"
|
||||
:rules="[{ required: true, message: 'name is required' }]"
|
||||
:validate-trigger="['change', 'input']"
|
||||
>
|
||||
<a-input v-model="form.username" placeholder="请输入账号" />
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
v-if="!props.data.id"
|
||||
field="password"
|
||||
label="密码"
|
||||
:rules="[{ required: true, message: 'password is required' }]"
|
||||
:validate-trigger="['change', 'input']"
|
||||
showable
|
||||
>
|
||||
<a-input v-model="form.password" placeholder="请输入密码" />
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
field="calls"
|
||||
label="对话次数"
|
||||
:rules="[
|
||||
{ required: true, message: 'count is required' },
|
||||
{ type: 'number', message: 'age is max than 200' },
|
||||
]"
|
||||
>
|
||||
<a-input-number v-model="form.calls" placeholder="请输入对话次数" />
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
field="img_calls"
|
||||
label="绘图次数"
|
||||
:rules="[
|
||||
{ required: true, message: 'count is required' },
|
||||
{ type: 'number', message: 'age is max than 200' },
|
||||
]"
|
||||
>
|
||||
<a-input-number v-model="form.img_calls" placeholder="请输入绘图次数" />
|
||||
</a-form-item>
|
||||
<a-form-item field="expired_time" label="有效期">
|
||||
<a-date-picker v-model="form.expired_time" placeholder="请选择有效期" />
|
||||
</a-form-item>
|
||||
<a-form-item field="chat_roles" label="聊天角色">
|
||||
<a-select
|
||||
:field-names="{ value: 'key', label: 'name' }"
|
||||
v-model="form.chat_roles"
|
||||
placeholder="请选择聊天角色"
|
||||
multiple
|
||||
:options="roleOption"
|
||||
>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item field="chat_models" label="模型角色">
|
||||
<a-select
|
||||
:field-names="{ value: 'value', label: 'name' }"
|
||||
v-model="form.chat_models"
|
||||
placeholder="请选择模型角色"
|
||||
multiple
|
||||
:options="modalOption"
|
||||
>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item field="status" label="启用状态">
|
||||
<a-switch v-model="form.status" />
|
||||
</a-form-item>
|
||||
<a-form-item field="vip" label="开通VIP">
|
||||
<a-switch v-model="form.vip" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, defineExpose, defineProps } from "vue";
|
||||
import { getModel, getRole } from "./api";
|
||||
const props = defineProps({
|
||||
data: {},
|
||||
});
|
||||
|
||||
const formRef = ref();
|
||||
const form = ref({
|
||||
id: "",
|
||||
username: "",
|
||||
password: "",
|
||||
calls: "",
|
||||
img_calls: "",
|
||||
expired_time: "",
|
||||
chat_roles: [],
|
||||
chat_models: [],
|
||||
status: false,
|
||||
vip: false,
|
||||
});
|
||||
if (props.data?.id) {
|
||||
form.value = Object.assign({}, props.data);
|
||||
if (form.value.expired_time === 0) {
|
||||
form.value.expired_time = "";
|
||||
}
|
||||
}
|
||||
|
||||
//拿选项
|
||||
const modalOption = ref([]);
|
||||
const roleOption = ref([]);
|
||||
const getOption = (api, container) => {
|
||||
api().then(({ code, data }) => {
|
||||
if (code === 0) {
|
||||
container.value = data;
|
||||
}
|
||||
});
|
||||
};
|
||||
getOption(getModel, modalOption);
|
||||
getOption(getRole, roleOption);
|
||||
|
||||
defineExpose({
|
||||
formRef,
|
||||
form,
|
||||
});
|
||||
</script>
|
44
gpt-vue/projects/vue-admin/src/views/User/UserPassword.vue
Normal file
44
gpt-vue/projects/vue-admin/src/views/User/UserPassword.vue
Normal file
@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<a-form ref="formRef" :model="form" :style="{ width: '600px' }" @submit="handleSubmit">
|
||||
<a-form-item
|
||||
field="username"
|
||||
label="账号"
|
||||
:rules="[{ required: true, message: 'name is required' }]"
|
||||
:validate-trigger="['change', 'input']"
|
||||
:disabled="true"
|
||||
>
|
||||
<a-input v-model="form.username" placeholder="请输入账号" />
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
field="password"
|
||||
label="新密码"
|
||||
:rules="[{ required: true, message: 'password is required' }]"
|
||||
:validate-trigger="['change', 'input']"
|
||||
showable
|
||||
>
|
||||
<a-input v-model="form.password" placeholder="请输入密码" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, defineExpose, defineProps } from "vue";
|
||||
import { getModel, getRole } from "./api";
|
||||
const props = defineProps({
|
||||
data: {},
|
||||
});
|
||||
|
||||
const formRef = ref();
|
||||
const form = ref({
|
||||
id: "",
|
||||
username: "",
|
||||
password: "",
|
||||
});
|
||||
form.value.id = props.data.id;
|
||||
form.value.username = props.data.username;
|
||||
|
||||
defineExpose({
|
||||
formRef,
|
||||
form,
|
||||
});
|
||||
</script>
|
46
gpt-vue/projects/vue-admin/src/views/User/api.ts
Normal file
46
gpt-vue/projects/vue-admin/src/views/User/api.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import http from "@/http/config";
|
||||
|
||||
export const getList = (params?: Record<string, unknown>) => {
|
||||
return http({
|
||||
url: "/api/admin/user/list",
|
||||
method: "get",
|
||||
params,
|
||||
});
|
||||
};
|
||||
|
||||
export const save = (data?: Record<string, unknown>) => {
|
||||
return http({
|
||||
url: "/api/admin/user/save",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
};
|
||||
|
||||
export const deletApi = (id: string | number) => {
|
||||
return http({
|
||||
url: `/api/admin/user/remove?id=${id}`,
|
||||
method: "get",
|
||||
});
|
||||
};
|
||||
|
||||
export const getRole = () => {
|
||||
return http({
|
||||
url: `/api/admin/role/list`,
|
||||
method: "get",
|
||||
});
|
||||
};
|
||||
|
||||
export const getModel = () => {
|
||||
return http({
|
||||
url: `/api/admin/model/list`,
|
||||
method: "get",
|
||||
});
|
||||
};
|
||||
|
||||
export const resetPassword = (data) => {
|
||||
return http({
|
||||
url: `/api/admin/user/resetPass`,
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
};
|
Loading…
Reference in New Issue
Block a user