mirror of
https://github.com/yangjian102621/geekai.git
synced 2025-09-18 17:26:38 +08:00
feat(ui):用户
This commit is contained in:
parent
8e7413da97
commit
cb0e7d64ff
@ -84,7 +84,7 @@ const optionsEvent = {
|
|||||||
</AFormItem>
|
</AFormItem>
|
||||||
</AGridItem>
|
</AGridItem>
|
||||||
<AGridItem suffix>
|
<AGridItem suffix>
|
||||||
<ASpace>
|
<ASpace class="flex-end">
|
||||||
<slot name="search-options" :option="optionsEvent">
|
<slot name="search-options" :option="optionsEvent">
|
||||||
<AButton
|
<AButton
|
||||||
type="primary"
|
type="primary"
|
||||||
@ -114,4 +114,8 @@ const optionsEvent = {
|
|||||||
.search-form-conteiner {
|
.search-form-conteiner {
|
||||||
padding: 16px 0;
|
padding: 16px 0;
|
||||||
}
|
}
|
||||||
|
.flex-end {
|
||||||
|
display: flex;
|
||||||
|
justify-content: end;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -33,8 +33,9 @@ const handleSearch = async (tips?: boolean) => {
|
|||||||
onActivated(handleSearch);
|
onActivated(handleSearch);
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="search-table">
|
<slot name="header" v-bind="{ reload: handleSearch }" />
|
||||||
<div ref="tableContainerRef" class="search-table-container">
|
<div class="simple-table">
|
||||||
|
<div ref="tableContainerRef" class="simple-table-container">
|
||||||
<ATable
|
<ATable
|
||||||
v-bind="{
|
v-bind="{
|
||||||
...$attrs,
|
...$attrs,
|
||||||
@ -52,15 +53,15 @@ onActivated(handleSearch);
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.search-table {
|
.simple-table {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
.search-table-container {
|
.simple-table-container {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
.search-table-header {
|
.simple-table-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
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>
|
<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>
|
</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